Skip to content

Commit

Permalink
Formalise "rhizome add file" exit status
Browse files Browse the repository at this point in the history
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
  • Loading branch information
quixotique committed Dec 21, 2013
1 parent 273c5f2 commit 9ebef81
Show file tree
Hide file tree
Showing 11 changed files with 417 additions and 277 deletions.
116 changes: 65 additions & 51 deletions commandline.c
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions meshms.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
184 changes: 95 additions & 89 deletions rhizome.c
Expand Up @@ -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)",
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 9ebef81

Please sign in to comment.