Formalise "rhizome add file" exit status

Formalise add-bundle result in "enum rhizome_bundle_status"

Rewrite rhizome_manifest_finalise(), rhizome_find_duplicate() and
rhizome_add_manifest() to return enum rhizome_bundle_status

New function rhizome_manifest_check_stored() that compares a manifest
with its stored counterpart and returns enum rhizome_bundle_status

Remove redundant rhizome_manifest_check_sanity(), consolidating all
manifest validation rules in rhizome_manifest_validate(), which now
checks the 'id' field is present, and that 'sender' and 'recipient' are
both present for MeshMS

Correct manifest finalisation logic: set the 'finalised' flag in
rhizome_manifest_validate(), not in rhizome_manifest_verify() (which
sets 'selfSigned'), and consistently clear 'finalised' flag in all
attribute setter functions

Remove manifest 'ttl' field and all references thereof (leaving unused
space in Rhizome BAR)

Rename some payload functions for clarity
This commit is contained in:
Andrew Bettison 2013-12-19 19:13:39 +10:30
parent 273c5f2689
commit 9ebef81a49
11 changed files with 420 additions and 280 deletions

View File

@ -1510,48 +1510,60 @@ int app_rhizome_add_file(const struct cli_parsed *parsed, struct cli_context *co
return -1;
}
enum rhizome_bundle_status status = RHIZOME_BUNDLE_STATUS_NEW;
if (journal){
if (rhizome_append_journal_file(m, 0, filepath)){
rhizome_manifest_free(m);
keyring_free(keyring);
return -1;
}
}else{
if (rhizome_stat_file(m, filepath) == -1) {
rhizome_manifest_free(m);
keyring_free(keyring);
return -1;
}
if (m->filesize) {
if (rhizome_add_file(m, filepath) == -1) {
rhizome_manifest_free(m);
keyring_free(keyring);
return -1;
if (rhizome_append_journal_file(m, 0, filepath))
status = -1;
} else {
int n = rhizome_stat_payload_file(m, filepath);
if (n == 0 && m->filesize)
n = rhizome_store_payload_file(m, filepath);
if (n == -1)
status = -1;
else if (n)
status = RHIZOME_BUNDLE_STATUS_INCONSISTENT;
}
rhizome_manifest *mout = m;
if (status == RHIZOME_BUNDLE_STATUS_NEW) {
if (!rhizome_manifest_validate(m) || m->malformed)
status = RHIZOME_BUNDLE_STATUS_INVALID;
else {
status = rhizome_manifest_finalise(m, &mout, !force_new);
if (mout && mout != m && !rhizome_manifest_validate(mout)) {
WHYF("Stored manifest id=%s is invalid -- overwriting", alloca_tohex_rhizome_bid_t(mout->cryptoSignPublic));
status = RHIZOME_BUNDLE_STATUS_NEW;
}
}
}
rhizome_manifest *mout = NULL;
int ret = rhizome_manifest_finalise(m, &mout, !force_new);
if (ret<0){
rhizome_manifest_free(m);
keyring_free(keyring);
return -1;
switch (status) {
case RHIZOME_BUNDLE_STATUS_NEW:
if (mout && mout != m)
rhizome_manifest_free(mout);
mout = m;
// fall through
case RHIZOME_BUNDLE_STATUS_SAME:
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_OLD:
cli_put_manifest(context, mout);
if ( manifestpath && *manifestpath
&& rhizome_write_manifest_file(mout, manifestpath, 0) == -1
)
WHYF("Could not write manifest to %s", alloca_str_toprint(manifestpath));
break;
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
case RHIZOME_BUNDLE_STATUS_ERROR:
case RHIZOME_BUNDLE_STATUS_INVALID:
break;
default:
FATALF("status=%d", status);
}
if (manifestpath && *manifestpath
&& rhizome_write_manifest_file(mout, manifestpath, 0) == -1)
ret = WHY("Could not overwrite manifest file.");
assert(m->haveSecret);
assert(mout->authorship != AUTHOR_LOCAL);
cli_put_manifest(context, mout);
if (mout != m)
if (mout && mout != m) {
rhizome_manifest_free(mout);
m = NULL;
}
rhizome_manifest_free(m);
keyring_free(keyring);
return ret;
return status;
}
int app_slip_test(const struct cli_parsed *parsed, struct cli_context *context)
@ -1606,18 +1618,27 @@ int app_rhizome_import_bundle(const struct cli_parsed *parsed, struct cli_contex
cli_arg(parsed, "manifestpath", &manifestpath, NULL, "");
if (rhizome_opendb() == -1)
return -1;
rhizome_manifest *m = rhizome_new_manifest();
if (!m)
return WHY("Out of manifests.");
int status=rhizome_bundle_import_files(m, manifestpath, filepath);
if (status<0)
goto cleanup;
cli_put_manifest(context, m);
cleanup:
rhizome_manifest *m_out = NULL;
enum rhizome_bundle_status status = rhizome_bundle_import_files(m, &m_out, manifestpath, filepath);
switch (status) {
case RHIZOME_BUNDLE_STATUS_NEW:
case RHIZOME_BUNDLE_STATUS_SAME:
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_OLD:
cli_put_manifest(context, m_out);
break;
case RHIZOME_BUNDLE_STATUS_ERROR:
case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
break;
default:
FATALF("rhizome_bundle_import_files() returned %d", status);
}
if (m_out && m_out != m)
rhizome_manifest_free(m_out);
rhizome_manifest_free(m);
return status;
}
@ -1638,7 +1659,6 @@ int app_rhizome_append_manifest(const struct cli_parsed *parsed, struct cli_cont
&& rhizome_manifest_validate(m)
&& rhizome_manifest_verify(m)
) {
assert(m->finalised);
if (rhizome_write_manifest_file(m, filepath, 1) != -1)
ret = 0;
}
@ -1764,12 +1784,11 @@ int app_rhizome_extract(const struct cli_parsed *parsed, struct cli_context *con
return WHY("Out of manifests");
}
ret = rhizome_retrieve_manifest(&bid, m);
if (ret==0){
assert(m->finalised);
if (bskhex)
rhizome_apply_bundle_secret(m, &bsk);
rhizome_authenticate_author(m);
assert(m->authorship != AUTHOR_LOCAL);
cli_put_manifest(context, m);
}
@ -1801,11 +1820,6 @@ int app_rhizome_extract(const struct cli_parsed *parsed, struct cli_context *con
int append = (strcmp(manifestpath, filepath)==0)?1:0;
// don't write out the manifest if we were asked to append it and writing the file failed.
if ((!append) || retfile==0){
/* If the manifest has been read in from database, the blob is there,
and we can lie and say we are finalised and just want to write it
out. TODO: really should have a dirty/clean flag, so that write
works if clean but not finalised. */
m->finalised=1;
if (rhizome_write_manifest_file(m, manifestpath, append) == -1)
ret = -1;
}

View File

@ -370,7 +370,7 @@ static int append_meshms_buffer(const sid_t *my_sid, struct conversations *conv,
if (rhizome_append_journal_buffer(m, 0, buffer, len))
goto end;
if (rhizome_manifest_finalise(m, &mout, 1))
if (rhizome_manifest_finalise(m, &mout, 1) == -1)
goto end;
ret=0;
@ -641,7 +641,7 @@ static int write_known_conversations(rhizome_manifest *m, struct conversations *
if (rhizome_finish_write(&write))
goto end;
rhizome_manifest_set_filehash(m, &write.id);
if (rhizome_manifest_finalise(m, &mout, 1))
if (rhizome_manifest_finalise(m, &mout, 1) == -1)
goto end;
ret=0;

184
rhizome.c
View File

@ -93,11 +93,10 @@ int rhizome_fetch_delay_ms()
}
/* Import a bundle from a pair of files, one containing the manifest and the optional other
containing the payload. The logic is all in rhizome_bundle_import(). This function just wraps
that function and manages file and object buffers and lifetimes.
*/
int rhizome_bundle_import_files(rhizome_manifest *m, const char *manifest_path, const char *filepath)
* containing the payload. The work is all done by rhizome_bundle_import() and
* rhizome_store_manifest().
*/
enum rhizome_bundle_status rhizome_bundle_import_files(rhizome_manifest *m, rhizome_manifest **mout, const char *manifest_path, const char *filepath)
{
if (config.debug.rhizome)
DEBUGF("(manifest_path=%s, filepath=%s)",
@ -152,55 +151,22 @@ int rhizome_bundle_import_files(rhizome_manifest *m, const char *manifest_path,
if (ret)
return ret;
m->manifest_all_bytes = buffer_len;
if (rhizome_manifest_parse(m) == -1)
return WHY("could not parse manifest file");
if (!rhizome_manifest_validate(m))
return WHY("manifest is invalid");
if (!rhizome_manifest_verify(m))
return WHY("could not verify manifest");
/* Do we already have this manifest or newer? */
uint64_t dbVersion = 0;
if (sqlite_exec_uint64(&dbVersion, "SELECT version FROM MANIFESTS WHERE id = ?;", RHIZOME_BID_T, &m->cryptoSignPublic, END) == -1)
return WHY("Select failure");
if (dbVersion >= m->version)
return 2;
int status = rhizome_import_file(m, filepath);
if (status<0)
return status;
return rhizome_add_manifest(m, 1);
}
int rhizome_manifest_check_sanity(rhizome_manifest *m)
{
/* Ensure manifest meets basic sanity checks. */
int ret = 0;
if (m->version == 0)
ret = WHY("Manifest must have a version number");
if (m->filesize == RHIZOME_SIZE_UNSET)
ret = WHY("Manifest missing 'filesize' field");
else if (m->filesize && rhizome_filehash_t_is_zero(m->filehash))
ret = WHY("Manifest 'filehash' field has not been set");
if (m->service == NULL)
ret = WHY("Manifest missing 'service' field");
else if (strcasecmp(m->service, RHIZOME_SERVICE_FILE) == 0) {
if (m->name == NULL)
ret = WHY("Manifest with service='" RHIZOME_SERVICE_FILE "' missing 'name' field");
} else if (strcasecmp(m->service, RHIZOME_SERVICE_MESHMS) == 0
|| strcasecmp(m->service, RHIZOME_SERVICE_MESHMS2) == 0) {
if (!m->has_sender)
ret = WHYF("Manifest with service='%s' missing 'sender' field", m->service);
if (!m->has_recipient)
ret = WHYF("Manifest with service='%s' missing 'recipient' field", m->service);
if ( rhizome_manifest_parse(m) == -1
|| !rhizome_manifest_validate(m)
|| !rhizome_manifest_verify(m)
)
return RHIZOME_BUNDLE_STATUS_INVALID;
enum rhizome_bundle_status status = rhizome_manifest_check_stored(m, mout);
if (status == RHIZOME_BUNDLE_STATUS_NEW) {
int n = rhizome_import_payload_from_file(m, filepath);
if (n == -1)
return -1;
if (n != 0)
status = RHIZOME_BUNDLE_STATUS_INCONSISTENT;
else if (rhizome_store_manifest(m) == -1)
return -1;
}
else if (!rhizome_str_is_manifest_service(m->service))
ret = WHYF("Manifest invalid 'service' field %s", alloca_str_toprint(m->service));
if (!m->has_date)
ret = WHY("Manifest missing 'date' field");
return ret;
return status;
}
/* Sets the bundle key "BK" field of a manifest. Returns 1 if the field was set, 0 if not.
@ -273,50 +239,90 @@ int rhizome_manifest_add_bundle_key(rhizome_manifest *m)
RETURN(0);
}
int rhizome_add_manifest(rhizome_manifest *m, int ttl)
/* Test the status of a given manifest 'm' (id, version) with respect to the Rhizome store, and
* return a code which indicates whether 'm' should be stored or not, setting *mout to 'm' or
* to point to a newly allocated manifest. The caller is responsible for freeing *mout if *mout !=
* m. If the caller passes mout==NULL then no new manifest is allocated.
*
* - If the store contains no manifest with the given id, sets *mout = m and returns
* RHIZOME_BUNDLE_STATUS_NEW, ie, the manifest 'm' should be stored.
*
* - If the store contains a manifest with the same id and an older version, sets *mout to the
* stored manifest and returns RHIZOME_BUNDLE_STATUS_NEW, ie, the manifest 'm' should be
* stored.
*
* - If the store contains a manifest with the same id and version, sets *mout to the stored
* manifest and returns RHIZOME_BUNDLE_STATUS_SAME. The caller must compare *m and *mout, and
* if they are not identical, must decide what to do.
*
* - If the store contains a manifest with the same id and a later version, sets *mout to the
* stored manifest and returns RHIZOME_BUNDLE_STATUS_OLD, ie, the manifest 'm' should NOT be
* stored.
*
* - If there is an error querying the Rhizome store or allocating a new manifest structure, logs
* an error and returns -1.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
enum rhizome_bundle_status rhizome_manifest_check_stored(rhizome_manifest *m, rhizome_manifest **mout)
{
if (config.debug.rhizome)
DEBUGF("rhizome_add_manifest(m=%p, ttl=%d)",m, ttl);
if (m->finalised==0)
return WHY("Manifest must be finalised before being stored");
/* Store time to live, clamped to within legal range */
m->ttl = ttl < 0 ? 0 : ttl > 254 ? 254 : ttl;
if (rhizome_manifest_check_sanity(m))
assert(m->has_id);
assert(m->version != 0);
rhizome_manifest *stored_m = rhizome_new_manifest();
if (stored_m == NULL)
return -1;
assert(m->filesize != RHIZOME_SIZE_UNSET);
if (m->filesize > 0 && !rhizome_exists(&m->filehash))
return WHY("File has not been imported");
/* If the manifest already has an ID */
if (rhizome_bid_t_is_zero(m->cryptoSignPublic))
return WHY("Manifest does not have an ID");
/* Discard the new manifest unless it is newer than the most recent known version with the same ID */
uint64_t storedversion = -1;
switch (sqlite_exec_uint64(&storedversion, "SELECT version FROM MANIFESTS WHERE id = ?;", RHIZOME_BID_T, &m->cryptoSignPublic, END)) {
int n = rhizome_retrieve_manifest(&m->cryptoSignPublic, stored_m);
switch (n) {
case -1:
return WHY("Select failed");
case 0:
if (config.debug.rhizome) DEBUG("No existing manifest");
break;
rhizome_manifest_free(stored_m);
return -1;
case 1:
if (config.debug.rhizome)
DEBUGF("Found existing version=%"PRIu64", new version=%"PRIu64, storedversion, m->version);
if (m->version < storedversion)
return WHY("Newer version exists");
if (m->version == storedversion)
return WHYF("Already have %s:%"PRIu64", not adding", alloca_tohex_rhizome_bid_t(m->cryptoSignPublic), m->version);
if (config.debug.rhizome)
DEBUGF("No stored manifest with id=%s", alloca_tohex_rhizome_bid_t(m->cryptoSignPublic));
rhizome_manifest_free(stored_m);
if (mout)
*mout = m;
return RHIZOME_BUNDLE_STATUS_NEW;
case 0:
break;
default:
return WHY("Select found too many rows!");
FATALF("rhizome_retrieve_manifest() returned %d", n);
}
if (mout)
*mout = stored_m;
else
rhizome_manifest_free(stored_m);
enum rhizome_bundle_status result = RHIZOME_BUNDLE_STATUS_NEW;
const char *what = "newer than";
if (m->version < stored_m->version) {
result = RHIZOME_BUNDLE_STATUS_OLD;
what = "older than";
}
if (m->version == stored_m->version) {
return RHIZOME_BUNDLE_STATUS_SAME;
what = "same as";
}
if (config.debug.rhizome)
DEBUGF("Bundle %s:%"PRIu64" is %s stored version %"PRIu64, alloca_tohex_rhizome_bid_t(m->cryptoSignPublic), m->version, what, stored_m->version);
return result;
}
/* Okay, it is written, and can be put directly into the rhizome database now */
return rhizome_store_bundle(m);
enum rhizome_bundle_status rhizome_add_manifest(rhizome_manifest *m, rhizome_manifest **mout)
{
if (config.debug.rhizome)
DEBUGF("rhizome_add_manifest(m=manifest[%d](%p), mout=%p)", m->manifest_record_number, m, mout);
if (!m->finalised && !rhizome_manifest_validate(m))
return RHIZOME_BUNDLE_STATUS_INVALID;
assert(m->finalised);
if (!m->selfSigned && !rhizome_manifest_verify(m))
return RHIZOME_BUNDLE_STATUS_FAKE;
assert(m->filesize != RHIZOME_SIZE_UNSET);
if (m->filesize > 0 && !rhizome_exists(&m->filehash))
return WHY("Payload has not been stored");
enum rhizome_bundle_status status = rhizome_manifest_check_stored(m, mout);
if (status == RHIZOME_BUNDLE_STATUS_NEW && rhizome_store_manifest(m) == -1)
return -1;
return status;
}
/* When voice traffic is being carried, we need to throttle Rhizome down

View File

@ -224,6 +224,10 @@ typedef struct rhizome_manifest
*/
bool_t has_id;
/* Set if the filehash field contains a file hash.
*/
bool_t has_filehash;
/* Set if the tail field is valid, ie, the bundle is a journal.
*/
bool_t is_journal;
@ -257,9 +261,6 @@ typedef struct rhizome_manifest
AUTHOR_AUTHENTIC // a local identity is the verified author
} authorship;
/* time-to-live in hops of this manifest. */
int ttl;
int fileHighestPriority;
/* Absolute path of the file associated with the manifest */
@ -451,6 +452,17 @@ struct rhizome_manifest_summary {
int rhizome_manifest_inspect(const char *buf, size_t len, struct rhizome_manifest_summary *summ);
enum rhizome_bundle_status {
RHIZOME_BUNDLE_STATUS_ERROR = -1,
RHIZOME_BUNDLE_STATUS_NEW = 0, // bundle is newer than store
RHIZOME_BUNDLE_STATUS_SAME = 1, // same version already in store
RHIZOME_BUNDLE_STATUS_DUPLICATE = 2, // equivalent bundle already in store
RHIZOME_BUNDLE_STATUS_OLD = 3, // newer version already in store
RHIZOME_BUNDLE_STATUS_INVALID = 4, // manifest is invalid
RHIZOME_BUNDLE_STATUS_FAKE = 5, // manifest signature not valid
RHIZOME_BUNDLE_STATUS_INCONSISTENT = 6, // manifest filesize/filehash does not match supplied payload
};
int rhizome_write_manifest_file(rhizome_manifest *m, const char *filename, char append);
int rhizome_manifest_selfsign(rhizome_manifest *m);
int rhizome_drop_stored_file(const rhizome_filehash_t *hashp, int maximum_priority);
@ -459,7 +471,6 @@ int rhizome_read_manifest_from_file(rhizome_manifest *m, const char *filename);
int rhizome_manifest_validate(rhizome_manifest *m);
int rhizome_manifest_parse(rhizome_manifest *m);
int rhizome_manifest_verify(rhizome_manifest *m);
int rhizome_manifest_check_sanity(rhizome_manifest *m_in);
int rhizome_hash_file(rhizome_manifest *m, const char *path, rhizome_filehash_t *hash_out, uint64_t *size_out);
@ -468,10 +479,10 @@ void _rhizome_manifest_free(struct __sourceloc __whence, rhizome_manifest *m);
rhizome_manifest *_rhizome_new_manifest(struct __sourceloc __whence);
#define rhizome_new_manifest() _rhizome_new_manifest(__WHENCE__)
int rhizome_store_bundle(rhizome_manifest *m);
int rhizome_store_manifest(rhizome_manifest *m);
int rhizome_remove_file_datainvalid(sqlite_retry_state *retry, const rhizome_filehash_t *hashp);
int rhizome_store_file(rhizome_manifest *m,const unsigned char *key);
int rhizome_bundle_import_files(rhizome_manifest *m, const char *manifest_path, const char *filepath);
int rhizome_bundle_import_files(rhizome_manifest *m, rhizome_manifest **m_out, const char *manifest_path, const char *filepath);
int rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, const sid_t *authorSidp);
@ -482,9 +493,9 @@ void rhizome_find_bundle_author_and_secret(rhizome_manifest *m);
int rhizome_lookup_author(rhizome_manifest *m);
void rhizome_authenticate_author(rhizome_manifest *m);
int rhizome_manifest_finalise(rhizome_manifest *m, rhizome_manifest **mout, int deduplicate);
int rhizome_add_manifest(rhizome_manifest *m_in,int ttl);
enum rhizome_bundle_status rhizome_manifest_finalise(rhizome_manifest *m, rhizome_manifest **m_out, int deduplicate);
enum rhizome_bundle_status rhizome_manifest_check_stored(rhizome_manifest *m, rhizome_manifest **m_out);
enum rhizome_bundle_status rhizome_add_manifest(rhizome_manifest *m_in, rhizome_manifest **m_out);
void rhizome_bytes_to_hex_upper(unsigned const char *in, char *out, int byteCount);
int rhizome_find_privatekey(rhizome_manifest *m);
@ -574,7 +585,7 @@ int _sqlite_vexec_strbuf_retry(struct __sourceloc, sqlite_retry_state *retry, st
double rhizome_manifest_get_double(rhizome_manifest *m,char *var,double default_value);
int rhizome_manifest_extract_signature(rhizome_manifest *m, unsigned *ofs);
int rhizome_update_file_priority(const char *fileid);
int rhizome_find_duplicate(const rhizome_manifest *m, rhizome_manifest **found);
enum rhizome_bundle_status rhizome_find_duplicate(const rhizome_manifest *m, rhizome_manifest **found);
int rhizome_manifest_to_bar(rhizome_manifest *m,unsigned char *bar);
uint64_t rhizome_bar_version(const unsigned char *bar);
uint64_t rhizome_bar_bidprefix_ll(unsigned char *bar);
@ -932,10 +943,10 @@ int rhizome_write_open_manifest(struct rhizome_write *write, rhizome_manifest *m
int rhizome_write_file(struct rhizome_write *write, const char *filename);
int rhizome_fail_write(struct rhizome_write *write);
int rhizome_finish_write(struct rhizome_write *write);
int rhizome_import_file(rhizome_manifest *m, const char *filepath);
int rhizome_import_payload_from_file(rhizome_manifest *m, const char *filepath);
int rhizome_import_buffer(rhizome_manifest *m, unsigned char *buffer, size_t length);
int rhizome_stat_file(rhizome_manifest *m, const char *filepath);
int rhizome_add_file(rhizome_manifest *m, const char *filepath);
int rhizome_stat_payload_file(rhizome_manifest *m, const char *filepath);
int rhizome_store_payload_file(rhizome_manifest *m, const char *filepath);
int rhizome_derive_payload_key(rhizome_manifest *m);
int rhizome_append_journal_buffer(rhizome_manifest *m, uint64_t advance_by, unsigned char *buffer, size_t len);

View File

@ -63,7 +63,6 @@ static int _rhizome_manifest_del(struct __sourceloc __whence, rhizome_manifest *
free((char *) m->vars[i]);
free((char *) m->values[i]);
--m->var_count;
m->finalised = 0;
ret = 1;
break;
}
@ -90,7 +89,6 @@ static const char *_rhizome_manifest_set(struct __sourceloc __whence, rhizome_ma
return NULL;
free((char *)m->values[i]);
m->values[i] = ret;
m->finalised = 0;
return ret;
}
if (m->var_count >= NELS(m->vars))
@ -104,7 +102,6 @@ static const char *_rhizome_manifest_set(struct __sourceloc __whence, rhizome_ma
return NULL;
}
m->var_count++;
m->finalised = 0;
return ret;
}
@ -135,6 +132,7 @@ void _rhizome_manifest_set_id(struct __sourceloc __whence, rhizome_manifest *m,
m->authorship = AUTHOR_LOCAL;
}
m->has_id = 1;
m->finalised = 0;
}
void _rhizome_manifest_set_version(struct __sourceloc __whence, rhizome_manifest *m, uint64_t version)
@ -142,6 +140,7 @@ void _rhizome_manifest_set_version(struct __sourceloc __whence, rhizome_manifest
const char *v = rhizome_manifest_set_ui64(m, "version", version);
assert(v); // TODO: remove known manifest fields from vars[]
m->version = version;
m->finalised = 0;
}
void _rhizome_manifest_set_filesize(struct __sourceloc __whence, rhizome_manifest *m, uint64_t size)
@ -149,6 +148,7 @@ void _rhizome_manifest_set_filesize(struct __sourceloc __whence, rhizome_manifes
const char *v = rhizome_manifest_set_ui64(m, "filesize", size);
assert(v); // TODO: remove known manifest fields from vars[]
m->filesize = size;
m->finalised = 0;
if (m->filesize == 0)
rhizome_manifest_set_filehash(m, NULL);
}
@ -163,11 +163,14 @@ void _rhizome_manifest_set_filehash(struct __sourceloc __whence, rhizome_manifes
const char *v = rhizome_manifest_set(m, "filehash", alloca_tohex_rhizome_filehash_t(*hash));
assert(v); // TODO: remove known manifest fields from vars[]
m->filehash = *hash;
m->has_filehash = 1;
} else {
assert(m->filesize == 0);
rhizome_manifest_del(m, "filehash");
m->filehash = RHIZOME_FILEHASH_NONE;
m->has_filehash = 0;
}
m->finalised = 0;
}
void _rhizome_manifest_set_tail(struct __sourceloc __whence, rhizome_manifest *m, uint64_t tail)
@ -176,6 +179,7 @@ void _rhizome_manifest_set_tail(struct __sourceloc __whence, rhizome_manifest *m
assert(v); // TODO: remove known manifest fields from vars[]
m->tail = tail;
m->is_journal = (tail != RHIZOME_SIZE_UNSET);
m->finalised = 0;
}
void _rhizome_manifest_set_bundle_key(struct __sourceloc __whence, rhizome_manifest *m, const rhizome_bk_t *bkp)
@ -185,6 +189,7 @@ void _rhizome_manifest_set_bundle_key(struct __sourceloc __whence, rhizome_manif
assert(v); // TODO: remove known manifest fields from vars[]
m->bundle_key = *bkp;
m->has_bundle_key = 1;
m->finalised = 0;
} else
_rhizome_manifest_del_bundle_key(__whence, m);
}
@ -195,6 +200,7 @@ void _rhizome_manifest_del_bundle_key(struct __sourceloc __whence, rhizome_manif
rhizome_manifest_del(m, "BK");
m->has_bundle_key = 0;
m->bundle_key = RHIZOME_BK_NONE; // not strictly necessary, but aids debugging
m->finalised = 0;
} else
assert(rhizome_manifest_get(m, "BK") == NULL);
// Once there is no BK field, any authenticated authorship is no longer.
@ -209,6 +215,7 @@ void _rhizome_manifest_set_service(struct __sourceloc __whence, rhizome_manifest
const char *v = rhizome_manifest_set(m, "service", service);
assert(v); // TODO: remove known manifest fields from vars[]
m->service = v;
m->finalised = 0;
} else
_rhizome_manifest_del_service(__whence, m);
}
@ -217,6 +224,7 @@ void _rhizome_manifest_del_service(struct __sourceloc __whence, rhizome_manifest
{
if (m->service) {
m->service = NULL;
m->finalised = 0;
rhizome_manifest_del(m, "service");
} else
assert(rhizome_manifest_get(m, "service") == NULL);
@ -224,6 +232,7 @@ void _rhizome_manifest_del_service(struct __sourceloc __whence, rhizome_manifest
void _rhizome_manifest_set_name(struct __sourceloc __whence, rhizome_manifest *m, const char *name)
{
m->finalised = 0;
if (name) {
assert(rhizome_str_is_manifest_name(name));
const char *v = rhizome_manifest_set(m, "name", name);
@ -239,6 +248,7 @@ void _rhizome_manifest_del_name(struct __sourceloc __whence, rhizome_manifest *m
{
if (m->name) {
m->name = NULL;
m->finalised = 0;
rhizome_manifest_del(m, "name");
} else
assert(rhizome_manifest_get(m, "name") == NULL);
@ -250,6 +260,7 @@ void _rhizome_manifest_set_date(struct __sourceloc __whence, rhizome_manifest *m
assert(v); // TODO: remove known manifest fields from vars[]
m->date = date;
m->has_date = 1;
m->finalised = 0;
}
void _rhizome_manifest_set_sender(struct __sourceloc __whence, rhizome_manifest *m, const sid_t *sidp)
@ -259,6 +270,7 @@ void _rhizome_manifest_set_sender(struct __sourceloc __whence, rhizome_manifest
assert(v); // TODO: remove known manifest fields from vars[]
m->sender = *sidp;
m->has_sender = 1;
m->finalised = 0;
} else
_rhizome_manifest_del_sender(__whence, m);
}
@ -269,6 +281,7 @@ void _rhizome_manifest_del_sender(struct __sourceloc __whence, rhizome_manifest
rhizome_manifest_del(m, "sender");
m->sender = SID_ANY;
m->has_sender = 0;
m->finalised = 0;
} else
assert(rhizome_manifest_get(m, "sender") == NULL);
}
@ -280,6 +293,7 @@ void _rhizome_manifest_set_recipient(struct __sourceloc __whence, rhizome_manife
assert(v); // TODO: remove known manifest fields from vars[]
m->recipient = *sidp;
m->has_recipient = 1;
m->finalised = 0;
} else
_rhizome_manifest_del_recipient(__whence, m);
}
@ -290,6 +304,7 @@ void _rhizome_manifest_del_recipient(struct __sourceloc __whence, rhizome_manife
rhizome_manifest_del(m, "recipient");
m->recipient = SID_ANY;
m->has_recipient = 0;
m->finalised = 0;
} else
assert(rhizome_manifest_get(m, "recipient") == NULL);
}
@ -313,6 +328,7 @@ void _rhizome_manifest_set_crypt(struct __sourceloc __whence, rhizome_manifest *
default: abort();
}
m->payloadEncryption = flag;
m->finalised = 0;
}
void _rhizome_manifest_set_rowid(struct __sourceloc __whence, rhizome_manifest *m, uint64_t rowid)
@ -355,15 +371,18 @@ void _rhizome_manifest_del_author(struct __sourceloc __whence, rhizome_manifest
/* Compute the hash of the manifest's body, including the NUL byte that separates the body from
* the signature block, and verify that a signature is present and is correct.
*
* Returns 1 if the manifest signature is valid, ie, the signature is a self-signature using the
* manifest's own private key. Sets the m->finalised flag to 1.
* If the manifest signature is valid, ie, the signature is a self-signature using the
* manifest's own private key, then sets the m->selfSigned flag and returns 1.
*
* Returns 0 if there are no signatures or if the signature block does not verify.
* If there are no signatures or if the signature block does not verify, then clears the
* m->selfSigned flag and returns 0.
*
* Only call this function on manifests for which rhizome_manifest_validate(m) has returned true.
* Only call this function on manifests for which rhizome_manifest_validate(m) has returned true
* (ie, m->finalised is set).
*/
int rhizome_manifest_verify(rhizome_manifest *m)
{
assert(m->finalised);
assert(m->manifest_body_bytes > 0);
assert(m->manifest_all_bytes > 0);
assert(m->manifest_body_bytes <= m->manifest_all_bytes);
@ -378,7 +397,6 @@ int rhizome_manifest_verify(rhizome_manifest *m)
break;
}
assert(ofs <= m->manifest_all_bytes);
// Make sure the first signatory's public key is the bundle ID
assert(m->has_id);
if (m->sig_count == 0) {
@ -396,7 +414,6 @@ int rhizome_manifest_verify(rhizome_manifest *m)
return 0;
}
m->selfSigned = 1;
m->finalised = 1;
return 1;
}
@ -415,6 +432,7 @@ static void rhizome_manifest_clear(rhizome_manifest *m)
}
m->malformed = 0;
m->has_id = 0;
m->has_filehash = 0;
m->is_journal = 0;
m->filesize = RHIZOME_SIZE_UNSET;
m->tail = RHIZOME_SIZE_UNSET;
@ -526,12 +544,18 @@ int rhizome_manifest_parse(rhizome_manifest *m)
assert(m->manifest_all_bytes <= sizeof m->manifestdata);
assert(m->manifest_body_bytes == 0);
assert(m->var_count == 0);
assert(!m->finalised);
assert(!m->malformed);
assert(!m->has_id);
assert(!m->has_filehash);
assert(!m->is_journal);
assert(m->filesize == RHIZOME_SIZE_UNSET);
assert(m->tail == RHIZOME_SIZE_UNSET);
assert(m->version == 0);
assert(!m->has_date);
assert(!m->has_sender);
assert(!m->has_recipient);
assert(m->payloadEncryption == PAYLOAD_CRYPT_UNKNOWN);
unsigned has_invalid_essential = 0;
unsigned has_duplicate = 0;
@ -581,6 +605,7 @@ int rhizome_manifest_parse(rhizome_manifest *m)
status = FIELD_DUPLICATE;
else if (strcasecmp(label, "id") == 0) {
if (str_to_rhizome_bid_t(&m->cryptoSignPublic, value) != -1) {
assert(!m->has_id);
status = FIELD_OK;
m->has_id = 1;
if (config.debug.rhizome_manifest)
@ -591,6 +616,7 @@ int rhizome_manifest_parse(rhizome_manifest *m)
else if (strcasecmp(label, "version") == 0) {
uint64_t version;
if (str_to_uint64(value, 10, &version, NULL) && version != 0) {
assert(m->version == 0);
status = FIELD_OK;
m->version = version;
if (config.debug.rhizome_manifest)
@ -599,8 +625,10 @@ int rhizome_manifest_parse(rhizome_manifest *m)
status = FIELD_INVALID;
}
else if (strcasecmp(label, "filehash") == 0) {
if (str_to_rhizome_filehash_t(&m->filehash, value) != -1 && !rhizome_filehash_t_is_zero(m->filehash)) {
if (str_to_rhizome_filehash_t(&m->filehash, value) != -1) {
assert(!m->has_filehash);
status = FIELD_OK;
m->has_filehash = 1;
if (config.debug.rhizome_manifest)
DEBUGF("PARSE manifest[%d].filehash = %s", m->manifest_record_number, alloca_tohex_rhizome_filehash_t(m->filehash));
} else
@ -609,6 +637,7 @@ int rhizome_manifest_parse(rhizome_manifest *m)
else if (strcasecmp(label, "filesize") == 0) {
uint64_t filesize;
if (str_to_uint64(value, 10, &filesize, NULL) && filesize != RHIZOME_SIZE_UNSET) {
assert(m->filesize == RHIZOME_SIZE_UNSET);
status = FIELD_OK;
m->filesize = filesize;
if (config.debug.rhizome_manifest)
@ -619,6 +648,7 @@ int rhizome_manifest_parse(rhizome_manifest *m)
else if (strcasecmp(label, "tail") == 0) {
uint64_t tail;
if (str_to_uint64(value, 10, &tail, NULL) && tail != RHIZOME_SIZE_UNSET) {
assert(m->tail == RHIZOME_SIZE_UNSET);
status = FIELD_OK;
m->tail = tail;
m->is_journal = 1;
@ -633,6 +663,7 @@ int rhizome_manifest_parse(rhizome_manifest *m)
// layer can refuse to add (or export?) the bundle.
else if (strcasecmp(label, "BK") == 0) {
if (str_to_rhizome_bk_t(&m->bundle_key, value) != -1) {
assert(!m->has_bundle_key);
status = FIELD_OK;
m->has_bundle_key = 1;
if (config.debug.rhizome_manifest)
@ -642,6 +673,7 @@ int rhizome_manifest_parse(rhizome_manifest *m)
}
else if (strcasecmp(label, "service") == 0) {
if (rhizome_str_is_manifest_service(value)) {
assert(m->service == NULL);
status = FIELD_OK;
m->service = value; // will be free()d when vars[] and values[] are free()d
if (config.debug.rhizome_manifest)
@ -652,6 +684,7 @@ int rhizome_manifest_parse(rhizome_manifest *m)
else if (strcasecmp(label, "date") == 0) {
int64_t date;
if (str_to_int64(value, 10, &date, NULL)) {
assert(!m->has_date);
status = FIELD_OK;
m->date = date;
m->has_date = 1;
@ -662,6 +695,7 @@ int rhizome_manifest_parse(rhizome_manifest *m)
}
else if (strcasecmp(label, "sender") == 0) {
if (str_to_sid_t(&m->sender, value) != -1) {
assert(!m->has_sender);
status = FIELD_OK;
m->has_sender = 1;
if (config.debug.rhizome_manifest)
@ -671,6 +705,7 @@ int rhizome_manifest_parse(rhizome_manifest *m)
}
else if (strcasecmp(label, "recipient") == 0) {
if (str_to_sid_t(&m->recipient, value) != -1) {
assert(!m->has_recipient);
status = FIELD_OK;
m->has_recipient = 1;
if (config.debug.rhizome_manifest)
@ -686,6 +721,7 @@ int rhizome_manifest_parse(rhizome_manifest *m)
}
else if (strcasecmp(label, "crypt") == 0) {
if (strcmp(value, "0") == 0 || strcmp(value, "1") == 0) {
assert(m->payloadEncryption == PAYLOAD_CRYPT_UNKNOWN);
status = FIELD_OK;
m->payloadEncryption = (value[0] == '1') ? PAYLOAD_ENCRYPTED : PAYLOAD_CLEAR;
if (config.debug.rhizome_manifest)
@ -742,8 +778,13 @@ int rhizome_manifest_parse(rhizome_manifest *m)
OUT();
}
/* Return 1 if all necessary fields are present, 0 if not. Increment m->malformed if any
* unnecessary fields are missing.
/* If all essential (transport) fields are present and well formed then sets the m->finalised field
* and returns 1, otherwise returns 0.
*
* Increments m->malformed if any non-essential fields are missing or invalid. It is up to the
* caller to check the m->malformed field and decide whether or not to process a malformed manifest.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int rhizome_manifest_validate(rhizome_manifest *m)
{
@ -762,31 +803,52 @@ int rhizome_manifest_validate(rhizome_manifest *m)
if (config.debug.rhizome_manifest)
DEBUG("Missing 'filesize' field");
ret = 0;
} else if (m->filesize == 0 && m->has_filehash) {
if (config.debug.rhizome_manifest)
DEBUG("Spurious 'filehash' field");
ret = 0;
} else if (m->filesize != 0 && !m->has_filehash) {
if (config.debug.rhizome_manifest)
DEBUG("Missing 'filehash' field");
ret = 0;
}
if (rhizome_filehash_t_is_zero(m->filehash)) {
if (m->filesize > 0) {
if (config.debug.rhizome_manifest)
DEBUG("Missing 'filehash' field");
ret = 0;
}
} else {
if (m->filesize == 0) {
if (config.debug.rhizome_manifest)
DEBUG("Spurious 'filehash' field");
ret = 0;
}
}
// warn if expected fields are missing
// Warn if expected fields are missing or invalid
if (m->service == NULL) {
if (config.debug.rhizome_manifest)
DEBUG("Missing 'service' field");
++m->malformed;
}
else if (strcmp(m->service, RHIZOME_SERVICE_FILE) == 0) {
if (m->name == NULL) {
if (config.debug.rhizome_manifest)
DEBUG("Manifest with service='" RHIZOME_SERVICE_FILE "' missing 'name' field");
++m->malformed;
}
} else if (strcmp(m->service, RHIZOME_SERVICE_MESHMS) == 0
|| strcmp(m->service, RHIZOME_SERVICE_MESHMS2) == 0
) {
if (!m->has_sender) {
if (config.debug.rhizome_manifest)
DEBUGF("Manifest with service='%s' missing 'sender' field", m->service);
++m->malformed;
}
if (!m->has_recipient) {
if (config.debug.rhizome_manifest)
DEBUGF("Manifest with service='%s' missing 'recipient' field", m->service);
++m->malformed;
}
}
else if (!rhizome_str_is_manifest_service(m->service)) {
if (config.debug.rhizome_manifest)
DEBUGF("Manifest invalid 'service' field %s", alloca_str_toprint(m->service));
++m->malformed;
}
if (!m->has_date) {
if (config.debug.rhizome_manifest)
DEBUG("Missing 'date' field");
++m->malformed;
}
m->finalised = ret;
return ret;
}
@ -1002,10 +1064,7 @@ int rhizome_write_manifest_file(rhizome_manifest *m, const char *path, char appe
{
if (config.debug.rhizome)
DEBUGF("write manifest (%zd bytes) to %s", m->manifest_all_bytes, path);
if (!m)
return WHY("Manifest is null.");
if (!m->finalised)
return WHY("Manifest must be finalised before it can be written.");
assert(m->finalised);
int fd = open(path, O_WRONLY | O_CREAT | (append ? O_APPEND : 0), 0666);
if (fd == -1)
return WHYF_perror("open(%s,O_WRONLY|O_CREAT%s,0666)", alloca_str_toprint(path), append ? "|O_APPEND" : "");
@ -1039,20 +1098,28 @@ int rhizome_manifest_dump(rhizome_manifest *m, const char *msg)
return 0;
}
int rhizome_manifest_finalise(rhizome_manifest *m, rhizome_manifest **mout, int deduplicate)
enum rhizome_bundle_status rhizome_manifest_finalise(rhizome_manifest *m, rhizome_manifest **mout, int deduplicate)
{
IN();
int ret=0;
if (m->filesize == RHIZOME_SIZE_UNSET)
RETURN(WHY("Manifest filesize unknown"));
if (!m->finalised && !rhizome_manifest_validate(m))
RETURN(RHIZOME_BUNDLE_STATUS_INVALID);
// if a manifest was supplied with an ID, don't bother to check for a duplicate.
// we only want to filter out added files with no existing manifest.
if (deduplicate && m->haveSecret != EXISTING_BUNDLE_ID && rhizome_find_duplicate(m, mout) == 1)
RETURN(2);
*mout=m;
if (deduplicate && m->haveSecret != EXISTING_BUNDLE_ID) {
enum rhizome_bundle_status status = rhizome_find_duplicate(m, mout);
switch (status) {
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_ERROR:
RETURN(status);
case RHIZOME_BUNDLE_STATUS_NEW:
break;
default:
FATALF("rhizome_find_duplicate() returned %d", status);
}
}
*mout = m;
/* Convert to final form for signing and writing to disk */
if (rhizome_manifest_pack_variables(m))
@ -1063,20 +1130,19 @@ int rhizome_manifest_finalise(rhizome_manifest *m, rhizome_manifest **mout, int
RETURN(WHY("Could not sign manifest"));
/* mark manifest as finalised */
m->finalised=1;
ret = rhizome_add_manifest(m, 255 /* TTL */);
RETURN(ret);
enum rhizome_bundle_status status = rhizome_add_manifest(m, mout);
RETURN(status);
OUT();
}
/* Fill in a few missing manifest fields, to make it easier to use when adding new files:
* - use the current time for "date" and "version"
* - use the given author SID, or the 'sender' if present, as the author
* - create an ID if there is none, otherwise authenticate the existing one
* - if service is file, then use the payload file's basename for "name"
*/
int rhizome_fill_manifest(rhizome_manifest *m, const char *filepath, const sid_t *authorSidp)
{
/* Fill in a few missing manifest fields, to make it easier to use when adding new files:
- use the current time for "date" and "version"
- if service is file, then use the payload file's basename for "name"
*/
/* Set version of manifest from current time if not already set. */
if (m->version == 0)
rhizome_manifest_set_version(m, gettime_ms());

View File

@ -187,7 +187,7 @@ void verify_bundles()
) {
assert(m->finalised);
// Store it again, to ensure that MANIFESTS columns are up to date.
ret = rhizome_store_bundle(m);
ret = rhizome_store_manifest(m);
}
if (ret) {
if (config.debug.rhizome)
@ -1351,7 +1351,7 @@ int rhizome_drop_stored_file(const rhizome_filehash_t *hashp, int maximum_priori
We need to also need to create the appropriate row(s) in the MANIFESTS, FILES,
and GROUPMEMBERSHIPS tables, and possibly GROUPLIST as well.
*/
int rhizome_store_bundle(rhizome_manifest *m)
int rhizome_store_manifest(rhizome_manifest *m)
{
if (!m->finalised)
return WHY("Manifest was not finalised");
@ -1704,13 +1704,13 @@ int rhizome_update_file_priority(const char *fileid)
}
/* Search the database for a manifest having the same name and payload content, and if the version
* is known, having the same version. Returns 1 if a duplicate is found (setting *found to point to
* the duplicate's manifest), returns 0 if no duplicate is found (leaving *found unchanged).
* Returns -1 on error (leaving *found undefined).
* is known, having the same version. Returns RHIZOME_BUNDLE_STATUS_DUPLICATE if a duplicate is found
* (setting *found to point to the duplicate's manifest), returns RHIZOME_BUNDLE_STATUS_NEW if no
* duplicate is found (leaving *found unchanged). Returns -1 on error (leaving *found undefined).
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int rhizome_find_duplicate(const rhizome_manifest *m, rhizome_manifest **found)
enum rhizome_bundle_status rhizome_find_duplicate(const rhizome_manifest *m, rhizome_manifest **found)
{
if (m->service == NULL)
return WHY("Manifest has no service");
@ -1728,7 +1728,7 @@ int rhizome_find_duplicate(const rhizome_manifest *m, rhizome_manifest **found)
strbuf_puts(b, " AND recipient = ?");
if (strbuf_overrun(b))
return WHYF("SQL command too long: %s", strbuf_str(b));
int ret = 0;
int ret = RHIZOME_BUNDLE_STATUS_NEW;
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT;
sqlite3_stmt *statement = sqlite_prepare_bind(&retry, strbuf_str(b), INT64, m->filesize, STATIC_TEXT, m->service, END);
if (!statement)
@ -1760,7 +1760,7 @@ int rhizome_find_duplicate(const rhizome_manifest *m, rhizome_manifest **found)
blob_m->manifest_all_bytes = manifestblobsize;
if ( rhizome_manifest_parse(blob_m) == -1
|| !rhizome_manifest_validate(blob_m)
) {
) {
WARNF("MANIFESTS row id=%s has invalid manifest blob -- skipped", q_manifestid);
goto next;
}
@ -1783,7 +1783,7 @@ int rhizome_find_duplicate(const rhizome_manifest *m, rhizome_manifest **found)
*found = blob_m;
if (config.debug.rhizome)
DEBUGF("Found duplicate payload, %s", q_manifestid);
ret = 1;
ret = RHIZOME_BUNDLE_STATUS_DUPLICATE;
break;
next:
if (blob_m)

View File

@ -79,12 +79,12 @@ static int rhizome_direct_import_end(struct http_request *hr)
alloca_str_toprint(manifest_path),
alloca_str_toprint(payload_path)
);
int ret = 0;
enum rhizome_bundle_status status = 0;
rhizome_manifest *m = rhizome_new_manifest();
if (!m)
ret = WHY("Out of manifests");
status = WHY("Out of manifests");
else {
ret = rhizome_bundle_import_files(m, manifest_path, payload_path);
status = rhizome_bundle_import_files(m, NULL, manifest_path, payload_path);
rhizome_manifest_free(m);
}
rhizome_direct_clear_temporary_files(r);
@ -96,13 +96,28 @@ static int rhizome_direct_import_end(struct http_request *hr)
the import fails due to malformed data etc.
(should probably also indicate if we have a newer version if possible)
*/
switch (ret) {
case 0:
switch (status) {
case RHIZOME_BUNDLE_STATUS_NEW:
http_request_simple_response(&r->http, 201, "Bundle succesfully imported");
return 0;
case 2:
case RHIZOME_BUNDLE_STATUS_SAME:
http_request_simple_response(&r->http, 200, "Bundle already imported");
return 0;
case RHIZOME_BUNDLE_STATUS_OLD:
http_request_simple_response(&r->http, 403, "Newer bundle already stored");
return 0;
case RHIZOME_BUNDLE_STATUS_INVALID:
http_request_simple_response(&r->http, 403, "Manifest is invalid");
return 0;
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
http_request_simple_response(&r->http, 403, "Manifest is inconsistent with file");
return 0;
case RHIZOME_BUNDLE_STATUS_FAKE:
http_request_simple_response(&r->http, 403, "Manifest not signed");
return 0;
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_ERROR:
break;
}
http_request_simple_response(&r->http, 500, "Internal Error: Rhizome import failed");
return 0;
@ -181,7 +196,7 @@ static int rhizome_direct_addfile_end(struct http_request *hr)
return 0;
}
if (config.debug.rhizome)
DEBUGF("Call rhizome_add_file(%s)", alloca_str_toprint(payload_path));
DEBUGF("Call rhizome_store_payload_file(%s)", alloca_str_toprint(payload_path));
char manifestTemplate[1024];
manifestTemplate[0] = '\0';
if (config.rhizome.api.addfile.manifest_template_file[0]) {
@ -214,7 +229,7 @@ static int rhizome_direct_addfile_end(struct http_request *hr)
http_request_simple_response(&r->http, 500, "Internal Error: Malformed manifest template");
return 0;
}
if (rhizome_stat_file(m, payload_path)) {
if (rhizome_stat_payload_file(m, payload_path)) {
WHY("Payload file stat failed");
rhizome_manifest_free(m);
rhizome_direct_clear_temporary_files(r);
@ -238,7 +253,7 @@ static int rhizome_direct_addfile_end(struct http_request *hr)
// TODO, stream file into database
assert(m->filesize != RHIZOME_SIZE_UNSET);
if (m->filesize > 0) {
if (rhizome_add_file(m, payload_path)) {
if (rhizome_store_payload_file(m, payload_path)) {
rhizome_manifest_free(m);
rhizome_direct_clear_temporary_files(r);
http_request_simple_response(&r->http, 500, "Internal Error: Could not store file");
@ -246,7 +261,7 @@ static int rhizome_direct_addfile_end(struct http_request *hr)
}
}
rhizome_manifest *mout = NULL;
if (rhizome_manifest_finalise(m, &mout, 1)) {
if (rhizome_manifest_finalise(m, &mout, 1) == -1) {
if (mout && mout != m)
rhizome_manifest_free(mout);
rhizome_manifest_free(m);

View File

@ -475,13 +475,27 @@ int rhizome_queue_ignore_manifest(unsigned char *bid_prefix, int prefix_len, int
static int rhizome_import_received_bundle(struct rhizome_manifest *m)
{
m->finalised = 1;
if (!rhizome_manifest_validate(m))
return 0;
if (config.debug.rhizome_rx) {
DEBUGF("manifest len=%zu has %u signatories. Associated filesize=%"PRIu64" bytes",
m->manifest_all_bytes, m->sig_count, m->filesize);
dump("manifest", m->manifestdata, m->manifest_all_bytes);
}
return rhizome_add_manifest(m, m->ttl - 1 /* TTL */);
enum rhizome_bundle_status status = rhizome_add_manifest(m, NULL);
switch (status) {
case RHIZOME_BUNDLE_STATUS_NEW:
return 0;
case RHIZOME_BUNDLE_STATUS_SAME:
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_OLD:
return 1;
case RHIZOME_BUNDLE_STATUS_ERROR:
case RHIZOME_BUNDLE_STATUS_INVALID:
return -1;
default:
FATALF("rhizome_add_manifest() returned %d", status);
}
}
// begin fetching a bundle
@ -733,7 +747,7 @@ rhizome_fetch(struct rhizome_fetch_slot *slot, rhizome_manifest *m, const struct
if (rhizome_exists(&m->filehash)){
if (config.debug.rhizome_rx)
DEBUGF(" fetch not started - payload already present, so importing instead");
if (rhizome_add_manifest(m, m->ttl-1) == -1)
if (rhizome_add_manifest(m, NULL) == -1)
RETURN(WHY("add manifest failed"));
RETURN(IMPORTED);
}
@ -1294,7 +1308,7 @@ int rhizome_write_complete(struct rhizome_fetch_slot *slot)
RETURN(-1);
}
if (rhizome_import_received_bundle(slot->manifest)){
if (rhizome_import_received_bundle(slot->manifest) == -1){
rhizome_fetch_close(slot);
RETURN(-1);
}
@ -1447,9 +1461,10 @@ int rhizome_received_content(const unsigned char *bidprefix,
}
if (m){
if (rhizome_import_buffer(m, bytes, count) >= 0 && !rhizome_import_received_bundle(m)){
if (rhizome_import_buffer(m, bytes, count) >= 0){
INFOF("Completed MDP transfer in one hit for file %s",
alloca_tohex_rhizome_filehash_t(m->filehash));
rhizome_import_received_bundle(m);
if (c)
candidate_unqueue(c);
}

View File

@ -112,9 +112,7 @@ int rhizome_manifest_to_bar(rhizome_manifest *m,unsigned char *bar)
v=(maxLat+90)*(65535/180); bar[o++]=(v>>8)&0xff; bar[o++]=(v>>0)&0xff;
v=(maxLong+180)*(65535/360); bar[o++]=(v>>8)&0xff; bar[o++]=(v>>0)&0xff;
/* TTL */
if (m->ttl>0) bar[RHIZOME_BAR_TTL_OFFSET]=m->ttl-1;
else bar[RHIZOME_BAR_TTL_OFFSET]=0;
bar[RHIZOME_BAR_TTL_OFFSET]=0;
RETURN(0);
OUT();

View File

@ -467,6 +467,7 @@ int rhizome_fail_write(struct rhizome_write *write)
int rhizome_finish_write(struct rhizome_write *write)
{
int ret = -1;
if (write->blob_rowid==0 && write->blob_fd == -1)
return WHY("Can't finish a write that has already been closed");
if (write->buffer_list){
@ -498,7 +499,8 @@ int rhizome_finish_write(struct rhizome_write *write)
if (write->id_known) {
if (cmp_rhizome_filehash_t(&write->id, &hash_out) != 0) {
WHYF("expected filehash=%s, got %s", alloca_tohex_rhizome_filehash_t(write->id), alloca_tohex_rhizome_filehash_t(hash_out));
WARNF("expected filehash=%s, got %s", alloca_tohex_rhizome_filehash_t(write->id), alloca_tohex_rhizome_filehash_t(hash_out));
ret = 1;
goto failure;
}
} else {
@ -574,11 +576,16 @@ dbfailure:
sqlite_exec_void_retry(&retry, "ROLLBACK;", END);
failure:
rhizome_fail_write(write);
return -1;
return ret;
}
// import a file for an existing bundle with a known file hash
int rhizome_import_file(rhizome_manifest *m, const char *filepath)
/* Import the payload for an existing manifest with a known file size and hash. Compute the hash of
* the payload as it is imported, and when finished, check if the size and hash match the manifest.
* If the import is successful and the size and hash match, return 0. If the size or hash do not
* match, return 1. If there is an error reading the payload file or writing to the database,
* return -1.
*/
int rhizome_import_payload_from_file(rhizome_manifest *m, const char *filepath)
{
assert(m->filesize != RHIZOME_SIZE_UNSET);
if (m->filesize == 0)
@ -598,12 +605,7 @@ int rhizome_import_file(rhizome_manifest *m, const char *filepath)
return -1;
}
if (rhizome_finish_write(&write)){
rhizome_fail_write(&write);
return -1;
}
return 0;
return rhizome_finish_write(&write);
}
// store a whole payload from a single buffer
@ -630,15 +632,17 @@ int rhizome_import_buffer(rhizome_manifest *m, unsigned char *buffer, size_t len
return -1;
}
if (rhizome_finish_write(&write)){
rhizome_fail_write(&write);
return -1;
}
return 0;
return rhizome_finish_write(&write);
}
int rhizome_stat_file(rhizome_manifest *m, const char *filepath)
/* Checks the size of the file with the given path as a candidate payload for an existing manifest.
* An empty path (zero length) is taken to mean empty payload (size = 0). If the manifest's
* 'filesize' is not yet set, then sets the manifest's 'filesize' to the size of the file and
* returns 0. Otherwise, if the file's size equals the 'filesize' in the manifest, return 0. If
* the file size does not match the manifest's 'filesize', returns 1. If there is an error calling
* stat(2) on the payload file (eg, file does not exist), returns -1.
*/
int rhizome_stat_payload_file(rhizome_manifest *m, const char *filepath)
{
uint64_t size = 0;
if (filepath[0]) {
@ -647,14 +651,14 @@ int rhizome_stat_file(rhizome_manifest *m, const char *filepath)
return WHYF_perror("lstat(%s)", alloca_str_toprint(filepath));
size = stat.st_size;
}
// Fail if the file is shorter than already specified by the manifest.
if (m->filesize != RHIZOME_SIZE_UNSET && size < m->filesize)
return WHY("Manifest length is longer than the file");
// If the file is longer than already specified by the manifest, ignore the end of the file.
if (m->filesize == RHIZOME_SIZE_UNSET || size > m->filesize)
if (m->filesize == RHIZOME_SIZE_UNSET)
rhizome_manifest_set_filesize(m, size);
else if (size != m->filesize) {
if (config.debug.rhizome)
DEBUGF("payload file %s (size=%"PRIu64") does not match manifest[%d].filesize=%"PRIu64,
alloca_str_toprint(filepath), size, m->manifest_record_number, m->filesize);
return 1;
}
return 0;
}
@ -681,10 +685,14 @@ static int rhizome_write_derive_key(rhizome_manifest *m, struct rhizome_write *w
int rhizome_write_open_manifest(struct rhizome_write *write, rhizome_manifest *m)
{
assert(m->filesize != RHIZOME_SIZE_UNSET);
if (rhizome_open_write(write, NULL, m->filesize, RHIZOME_PRIORITY_DEFAULT))
if (rhizome_open_write(
write,
m->has_filehash ? &m->filehash : NULL,
m->filesize,
RHIZOME_PRIORITY_DEFAULT
)
)
return -1;
if (rhizome_write_derive_key(m, write))
return -1;
return 0;
@ -692,22 +700,25 @@ int rhizome_write_open_manifest(struct rhizome_write *write, rhizome_manifest *m
// import a file for a new bundle with an unknown file hash
// update the manifest with the details of the file
int rhizome_add_file(rhizome_manifest *m, const char *filepath)
int rhizome_store_payload_file(rhizome_manifest *m, const char *filepath)
{
// Stream the file directly into the database, encrypting & hashing as we go.
struct rhizome_write write;
bzero(&write, sizeof(write));
if (rhizome_write_open_manifest(&write, m))
goto failure;
if (rhizome_write_file(&write, filepath))
goto failure;
if (rhizome_finish_write(&write))
goto failure;
rhizome_manifest_set_filehash(m, &write.id);
return 0;
failure:
rhizome_fail_write(&write);
return -1;
if ( rhizome_write_open_manifest(&write, m)
|| rhizome_write_file(&write, filepath)
) {
rhizome_fail_write(&write);
return -1;
}
int ret = rhizome_finish_write(&write);
if (ret == 0) {
if (m->has_filehash)
assert(cmp_rhizome_filehash_t(&m->filehash, &write.id) == 0);
else
rhizome_manifest_set_filehash(m, &write.id);
}
return ret;
}
/* Return -1 on error, 0 if file blob found, 1 if not found.

View File

@ -566,7 +566,7 @@ setup_AddDeDuplicate() {
test_AddDeDuplicate() {
# Add first file again - should return a "duplicate" status code and nothing
# should change in its manifests.
execute --exit-status=2 --stderr $servald rhizome add file $SIDB1 file1 file1.manifestA
execute --exit-status=2 --stderr --core-backtrace $servald rhizome add file $SIDB1 file1 file1.manifestA
assert [ -s file1.manifestA ]
assert_stdout_add_file file1
extract_stdout_secret file1_dup_secret
@ -620,13 +620,14 @@ setup_AddMismatched() {
setup_AddDeDuplicate
}
test_AddMismatched() {
# Try to add another file using an existing manifest, should fail and leave
# the manifest file unchanged because the manifest's version field has not
# advanced.
# Try to add another file using an existing manifest, should fail with status
# code indicating inconsistency.
cp file1.manifest file1_2.manifest
execute $servald rhizome add file $SIDB1 file1_2 file1_2.manifest
assertExitStatus '!=' 0
assert diff file1.manifest file1_2.manifest
# Exit status 6 means manifest and payload do not match (filesize/filehash).
execute --exit-status=6 --stderr --core-backtrace $servald rhizome add file $SIDB1 file1_2 file1_2.manifest
tfw_cat file1.manifest file1_2.manifest
# Output manifest should be the same as the re-used manigfest
assert --stderr cmp file1.manifest file1_2.manifest
# And rhizome store should be unchanged.
executeOk_servald rhizome list
assert_rhizome_list --fromhere=1 --author=$SIDB1 file1 file2
@ -645,11 +646,13 @@ setup_AddUpdateSameVersion() {
cp file1_2.manifest file1_2.manifest.orig
}
test_AddUpdateSameVersion() {
# Try to add another file using an existing manifest Id and Version, should
# fail and update the manifest file to show existing bundle's manifest.
tfw_cat -v file1_2.manifest
execute $servald rhizome add file $SIDB1 file1_2 file1_2.manifest
execute --exit-status=1 $servald rhizome add file $SIDB1 file1_2 file1_2.manifest
assertExitStatus --stderr '!=' 0
tfw_cat -v file1_2.manifest
assert cmp file1_2.manifest file1_2.manifest.orig
tfw_cat -v file1_2.manifest file1.manifest
assert cmp file1_2.manifest file1.manifest
# And rhizome store should be unchanged.
executeOk_servald rhizome list
assert_rhizome_list --fromhere=1 --author=$SIDB1 file1 file2
@ -929,7 +932,7 @@ setup_MeshMSAddMissingSender() {
}
test_MeshMSAddMissingSender() {
execute $servald rhizome add file $SIDB1 file1 file1.manifest
assertExitStatus '!=' 0
assertExitStatus --stdout --stderr '!=' 0
}
doc_MeshMSAddMissingRecipient="Add MeshMS without recipient fails"
@ -1058,7 +1061,8 @@ setup_ImportForeignBundle() {
test_ImportForeignBundle() {
executeOk_servald rhizome import bundle fileA fileA.manifest
assert_stdout_import_bundle fileA
execute --exit-status=2 --stdout --stderr $servald rhizome import bundle fileA fileA.manifest
# Exit status 1 means bundle is already in store
execute --exit-status=1 --stdout --stderr $servald rhizome import bundle fileA fileA.manifest
assert_stdout_import_bundle fileA
executeOk_servald rhizome list
assert_rhizome_list --fromhere=0 fileA