Make "dna rhizome add" work for more test cases

- adding a bundle using an existing manifest with an incorrect payload should
   fail with an error
 - adding a bundle using an existing manifest to update the payload to a new
   version should succeed
 - improve format of "dna rhizome list" output to one bundle per line
This commit is contained in:
Andrew Bettison 2012-04-13 17:47:20 +09:30
parent 45369b0891
commit de88d3db21
7 changed files with 250 additions and 130 deletions

View File

@ -23,6 +23,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <math.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include "serval.h"
#include "rhizome.h"
@ -627,27 +628,36 @@ int app_rhizome_add_file(int argc, char **argv, struct command_line_option *o)
/* Create a new manifest that will represent the file. If a manifest file was supplied, then read
* it, otherwise create a blank manifest. */
rhizome_manifest *m = NULL;
if (manifestpath[0]) {
int manifest_file_supplied = 0;
if (manifestpath[0] && access(manifestpath, R_OK) == 0) {
m = rhizome_read_manifest_file(manifestpath, 0, 0); // no verify
if (!m)
return WHY("Manifest file could not be loaded -- not added to rhizome");
manifest_file_supplied = 1;
} else {
m = rhizome_new_manifest();
if (!m)
return WHY("Manifest struct could not be allocated -- not added to rhizome");
}
/* Use the file's basename to fill in a missing "name". */
/* Fill in a few missing manifest fields, to make it easier to use when adding new files:
- the payload file's basename for "name"
- current time for "date"
*/
if (rhizome_manifest_get(m, "name", NULL, 0) == NULL) {
const char *name = strrchr(filepath, '/');
name = name ? name + 1 : filepath;
rhizome_manifest_set(m, "name", name);
}
/* Use current time to fill in a missing "date". */
if (rhizome_manifest_get(m, "date", NULL, 0) == NULL) {
rhizome_manifest_set_ll(m, "date", overlay_gettime_ms());
}
/* Add the manifest and its associated file to the Rhizome database, generating an "id" in the
* process */
int ret = rhizome_add_manifest(m, filepath,
rhizome_manifest *mout = NULL;
int ret = rhizome_add_manifest(m, &mout, filepath,
NULL, // no groups - XXX should allow them
255, // ttl - XXX should read from somewhere
manifestpath[0] != 0, // int verifyP
manifest_file_supplied, // int verifyP
1, // int checkFileP
1 // int signP
);
@ -656,11 +666,13 @@ int app_rhizome_add_file(int argc, char **argv, struct command_line_option *o)
} else {
/* If successfully added, overwrite the manifest file so that the Java component that is
* invoking this command can read it to obtain feedback on the result. */
if (manifestpath[0] && rhizome_write_manifest_file(m, manifestpath) == -1) {
if (manifestpath[0] && rhizome_write_manifest_file(mout, manifestpath) == -1) {
ret = WHY("Could not overwrite manifest file.");
}
}
rhizome_manifest_free(m);
if (mout != m)
rhizome_manifest_free(mout);
return ret;
}

2
dna.c
View File

@ -581,7 +581,7 @@ int main(int argc,char **argv)
manifest.
A different calling would be required to import an existing pre-signed
manifest */
return rhizome_bundle_import(NULL,optarg,
return rhizome_bundle_import(NULL, NULL, optarg,
NULL /* no groups - XXX should allow them */,
255 /* ttl - XXX should read from somewhere,
e.g., bar if being imported */,

132
rhizome.c
View File

@ -28,12 +28,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
file and object buffers and lifetimes.
*/
int rhizome_bundle_import(rhizome_manifest *m_in,char *bundle,char *groups[], int ttl,
int rhizome_bundle_import(rhizome_manifest *m_in, rhizome_manifest **m_out, char *bundle,
char *groups[], int ttl,
int verifyP, int checkFileP, int signP)
{
if (m_out) *m_out = NULL;
char filename[1024];
char manifestname[1024];
if (snprintf(filename, sizeof(filename), "%s/import/file.%s", rhizome_datastore_path, bundle) >= sizeof(filename)
|| snprintf(manifestname, sizeof(manifestname), "%s/import/manifest.%s", rhizome_datastore_path, bundle) >= sizeof(manifestname)) {
return WHY("Manifest bundle name too long");
@ -51,7 +53,8 @@ int rhizome_bundle_import(rhizome_manifest *m_in,char *bundle,char *groups[], in
}
/* Add the manifest and its associated file to the Rhizome database. */
int ret = rhizome_add_manifest(m, filename, groups, ttl, verifyP, checkFileP, signP);
rhizome_manifest *dupm;
int ret = rhizome_add_manifest(m, &dupm, filename, groups, ttl, verifyP, checkFileP, signP);
unlink(filename);
if (ret == -1) {
unlink(manifestname);
@ -62,9 +65,11 @@ int rhizome_bundle_import(rhizome_manifest *m_in,char *bundle,char *groups[], in
ret = WHY("Could not write manifest file.");
}
/* If the manifest structure was allocated in this function, then this function is responsible for
freeing it */
if (!m_in)
/* If the manifest structure was allocated in this function, and it is not being returned to the
caller, then this function is responsible for freeing it */
if (m_out)
*m_out = m;
else if (!m_in)
rhizome_manifest_free(m);
return ret;
@ -121,7 +126,8 @@ int rhizome_bundle_import(rhizome_manifest *m_in,char *bundle,char *groups[], in
*/
int rhizome_add_manifest(rhizome_manifest *m,
int rhizome_add_manifest(rhizome_manifest *m_in,
rhizome_manifest **m_out,
const char *filename,
char *groups[],
int ttl,
@ -130,22 +136,20 @@ int rhizome_add_manifest(rhizome_manifest *m,
int signP
)
{
char *id = NULL;
char hexhash[SHA512_DIGEST_STRING_LENGTH];
int verifyErrors = 0;
if (m_out) *m_out = NULL;
/* Ensure manifest meets basic sanity checks. */
const char *name = rhizome_manifest_get(m, "name", NULL, 0);
const char *name = rhizome_manifest_get(m_in, "name", NULL, 0);
if (name == NULL || !name[0])
return WHY("Manifest missing 'name' field");
if (rhizome_manifest_get_ll(m, "date") == -1)
if (rhizome_manifest_get_ll(m_in, "date") == -1)
return WHY("Manifest missing 'date' field");
/* Keep payload file name handy for later */
m->dataFileName = strdup(filename);
m_in->dataFileName = strdup(filename);
/* Store time to live, clamped to within legal range */
m->ttl = ttl < 0 ? 0 : ttl > 254 ? 254 : ttl;
m_in->ttl = ttl < 0 ? 0 : ttl > 254 ? 254 : ttl;
/* Check payload file is accessible and discover its length, then check that it matches
the file size stored in the manifest */
@ -153,115 +157,121 @@ int rhizome_add_manifest(rhizome_manifest *m,
struct stat stat;
if (lstat(filename,&stat))
return WHY("Could not stat() payload file");
m->fileLength = stat.st_size;
long long mfilesize = rhizome_manifest_get_ll(m, "filesize");
if (mfilesize != -1 && mfilesize != m->fileLength) {
WHYF("Manifest.filesize (%lld) != actual file size (%lld)", mfilesize, m->fileLength);
++verifyErrors;
m_in->fileLength = stat.st_size;
long long mfilesize = rhizome_manifest_get_ll(m_in, "filesize");
if (mfilesize != -1 && mfilesize != m_in->fileLength) {
WHYF("Manifest.filesize (%lld) != actual file size (%lld)", mfilesize, m_in->fileLength);
if (verifyP)
return -1;
}
}
/* Bail out now if errors occurred loading the manifest file, eg signature failed to validate */
if (verifyP && m_in->errors)
return WHYF("Manifest.errors (%d) is non-zero", m_in->errors);
/* Compute hash of payload unless we know verification has already failed */
if (checkFileP ? !(verifyP && (m->errors || verifyErrors)) : signP) {
if (checkFileP || signP) {
char hexhash[SHA512_DIGEST_STRING_LENGTH];
if (rhizome_hash_file(filename, hexhash))
return WHY("Could not hash file.");
memcpy(&m->fileHexHash[0], &hexhash[0], SHA512_DIGEST_STRING_LENGTH);
m->fileHashedP = 1;
memcpy(&m_in->fileHexHash[0], &hexhash[0], SHA512_DIGEST_STRING_LENGTH);
m_in->fileHashedP = 1;
}
/* Check that paylod hash matches manifest */
/* Check that payload hash matches manifest */
if (checkFileP) {
const char *mhexhash = rhizome_manifest_get(m, "filehash", NULL, 0);
if (mhexhash && strcmp(hexhash, mhexhash)) {
WHYF("Manifest.filehash (%s) != actual file hash (%s)", mhexhash, hexhash);
++verifyErrors;
const char *mhexhash = rhizome_manifest_get(m_in, "filehash", NULL, 0);
if (mhexhash && strcmp(m_in->fileHexHash, mhexhash)) {
WHYF("Manifest.filehash (%s) does not match payload hash (%s)", mhexhash, m_in->fileHexHash);
if (verifyP)
return -1;
}
}
/* If any signature errors were encountered on loading, or manifest is inconsistent with payload,
then bail out now. */
if (verifyP) {
if (m->errors)
WHYF("Manifest.errors (%d) is non-zero", m->errors);
if (verifyErrors || m->errors)
return WHY("Errors encountered verifying bundle manifest");
}
/* Fill in the manifest so that duplicate detection can be performed, and to avoid redundant work
by rhizome_manifest_finalise() below. */
if (checkFileP) {
rhizome_manifest_set(m, "filehash", hexhash);
rhizome_manifest_set_ll(m, "first_byte", 0);
rhizome_manifest_set_ll(m, "last_byte", m->fileLength);
rhizome_manifest_set(m_in, "filehash", m_in->fileHexHash);
rhizome_manifest_set_ll(m_in, "first_byte", 0);
rhizome_manifest_set_ll(m_in, "last_byte", m_in->fileLength);
}
/* Check if a manifest is already stored for the same payload with the same details.
This catches the case of "dna rhizome add file <filename>" on the same file more than once.
(Debounce!) */
rhizome_manifest *dupm = NULL;
if (rhizome_find_duplicate(m, &dupm) == -1)
if (rhizome_find_duplicate(m_in, &dupm) == -1)
return WHY("Errors encountered searching for duplicate manifest");
if (dupm) {
if (debug & DEBUG_RHIZOME) fprintf(stderr, "Not adding manifest for payload name=\"%s\" hexhash=%s - duplicate found in rhizome store\n", name, hexhash);
if (debug & DEBUG_RHIZOME)
fprintf(stderr, "Not adding manifest for payload name=\"%s\" hexhash=%s - duplicate found in rhizome store\n", name, m_in->fileHexHash);
#if 0
/* TODO Upgrade the version of the duplicate? */
long long version = rhizome_manifest_get_ll(m, "version");
long long version = rhizome_manifest_get_ll(m_in, "version");
long long dupversion = rhizome_manifest_get_ll(dupm, "version");
if (version > dupversion) {
rhizome_manifest_set_ll(dupm, "version", version);
...
}
#endif
rhizome_manifest_free(dupm);
if (m_out) {
/* Finish completing the manifest */
if (rhizome_manifest_finalise(dupm, 0))
return WHY("Failed to finalise manifest.\n");
*m_out = dupm;
}
else
rhizome_manifest_free(dupm);
return 0;
}
/* Supply manifest version number if missing, so we can do the version check below */
if (rhizome_manifest_get(m, "version", NULL, 0) == NULL) {
rhizome_manifest_set_ll(m, "version", overlay_gettime_ms());
if (rhizome_manifest_get(m_in, "version", NULL, 0) == NULL) {
rhizome_manifest_set_ll(m_in, "version", overlay_gettime_ms());
}
/* If the manifest already has an ID, look to see if we possess its private key */
if ((id = rhizome_manifest_get(m, "id", NULL, 0))) {
rhizome_hex_to_bytes(id, m->cryptoSignPublic, crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES*2);
if (!rhizome_find_keypair_bytes(m->cryptoSignPublic, m->cryptoSignSecret))
m->haveSecret=1;
}
/* Discard the new manifest it is older than the most recent known version with the same ID */
if (id) {
/* If the manifest already has an ID */
char *id = NULL;
if ((id = rhizome_manifest_get(m_in, "id", NULL, 0))) {
/* Discard the new manifest it is older than the most recent known version with the same ID */
long long storedversion = sqlite_exec_int64("SELECT version from manifests where id='%s';", id);
if (storedversion > rhizome_manifest_get_ll(m, "version")) {
if (storedversion > rhizome_manifest_get_ll(m_in, "version")) {
return WHY("Newer version exists");
}
/* Check if we know its private key */
rhizome_hex_to_bytes(id, m_in->cryptoSignPublic, crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES*2);
if (!rhizome_find_keypair_bytes(m_in->cryptoSignPublic, m_in->cryptoSignSecret))
m_in->haveSecret=1;
} else {
/* The manifest had no ID (256 bit random string being a public key in the NaCl CryptoSign
crypto system), so create one. */
printf("manifest does not have an id\n");
rhizome_manifest_createid(m);
rhizome_manifest_createid(m_in);
/* The ID is implicit in transit, but we need to store it in the file, so that reimporting
manifests on receiver nodes works easily. We might implement something that strips the id
variable out of the manifest when sending it, or some other scheme to avoid sending all the
extra bytes. */
id = rhizome_bytes_to_hex(m->cryptoSignPublic, crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES);
rhizome_manifest_set(m, "id", id);
id = rhizome_bytes_to_hex(m_in->cryptoSignPublic, crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES);
rhizome_manifest_set(m_in, "id", id);
}
/* Add group memberships */
if (groups) {
int i;
for(i = 0; groups[i]; i++)
rhizome_manifest_add_group(m, groups[i]);
rhizome_manifest_add_group(m_in, groups[i]);
}
/* Finish completing the manifest */
if (rhizome_manifest_finalise(m,signP))
if (rhizome_manifest_finalise(m_in, signP))
return WHY("Failed to finalise manifest.\n");
/* Okay, it is written, and can be put directly into the rhizome database now */
if (rhizome_store_bundle(m, filename) == -1)
if (rhizome_store_bundle(m_in, filename) == -1)
return WHY("rhizome_store_bundle() failed.");
if (m_out) *m_out = m_in;
return 0;
}

View File

@ -182,9 +182,11 @@ int rhizome_manifest_add_group(rhizome_manifest *m,char *groupid);
int rhizome_store_file(const char *file,char *hash,int priortity);
char *rhizome_safe_encode(unsigned char *in,int len);
int rhizome_finish_sqlstatement(sqlite3_stmt *statement);
int rhizome_bundle_import(rhizome_manifest *m_in,char *bundle,char *groups[], int ttl,
int rhizome_bundle_import(rhizome_manifest *m_in, rhizome_manifest **m_out, char *bundle,
char *groups[], int ttl,
int verifyP, int checkFileP, int signP);
int rhizome_add_manifest(rhizome_manifest *m, const char *filename, char *groups[], int ttl,
int rhizome_add_manifest(rhizome_manifest *m_in, rhizome_manifest **m_out, const char *filename,
char *groups[], int ttl,
int verifyP, int checkFileP, int signP);
int rhizome_manifest_finalise(rhizome_manifest *m,int signP);
char *rhizome_bytes_to_hex(unsigned char *in,int byteCount);

View File

@ -504,7 +504,11 @@ char *rhizome_safe_encode(unsigned char *in,int len)
int rhizome_list_manifests(int limit, int offset)
{
char sqlcmd[1024];
int n = snprintf(sqlcmd, sizeof(sqlcmd), "SELECT files.id, files.length, files.datavalid, manifests.id, manifests.manifest, manifests.version, manifests.inserttime FROM files, filemanifests, manifests WHERE files.id = filemanifests.fileid AND filemanifests.manifestid = manifests.id");
int n = snprintf(sqlcmd, sizeof(sqlcmd),
"SELECT files.id, files.length, files.datavalid, manifests.id, manifests.manifest, manifests.version, manifests.inserttime"
" FROM files, filemanifests, manifests WHERE files.id = filemanifests.fileid AND filemanifests.manifestid = manifests.id"
" ORDER BY files.id ASC"
);
if (n >= sizeof(sqlcmd))
return WHY("SQL command too long");
if (limit) {
@ -539,21 +543,23 @@ int rhizome_list_manifests(int limit, int offset)
ret = WHY("Incorrect statement column");
break;
}
size_t filesize = sqlite3_column_int(statement, 1);
const char *manifestblob = (char *) sqlite3_column_blob(statement, 4);
size_t manifestblobsize = sqlite3_column_bytes(statement, 4); // must call after sqlite3_column_blob()
//printf("manifest blob = %s\n", manifestblob);
rhizome_manifest *m = rhizome_read_manifest_file(manifestblob, manifestblobsize, 0);
const char *name = rhizome_manifest_get(m, "name", NULL, 0);
printf("file id = %s\nfile length = %u\nfile datavalid = %u\nfile name = \"%s\"\n\n",
long long date = rhizome_manifest_get_ll(m, "date");
printf("fileid=%s:manifestid=%s:version=%d:inserttime=%lld:length=%u:datavalid=%u:date=%lld:name=%s\n",
sqlite3_column_text(statement, 0),
filesize,
sqlite3_column_text(statement, 3),
sqlite3_column_int(statement, 5),
(long long) sqlite3_column_int64(statement, 6),
sqlite3_column_int(statement, 1),
sqlite3_column_int(statement, 2),
date,
name
);
rhizome_manifest_free(m);
}
printf("Found %lu rows\n", (unsigned long) rows);
}
sqlite3_finalize(statement);
return ret;
@ -758,6 +764,9 @@ int rhizome_find_duplicate(const rhizome_manifest *m, rhizome_manifest **found)
{
if (!m->fileHashedP)
return WHY("Manifest payload is not hashed");
const char *name = rhizome_manifest_get(m, "name", NULL, 0);
if (!name)
return WHY("Manifest has no name");
char sqlcmd[1024];
int n = snprintf(sqlcmd, sizeof(sqlcmd), "SELECT manifests.id, manifests.manifest FROM filemanifests, manifests WHERE filemanifests.fileid = ? AND filemanifests.manifestid = manifests.id");
if (n >= sizeof(sqlcmd))
@ -766,12 +775,10 @@ int rhizome_find_duplicate(const rhizome_manifest *m, rhizome_manifest **found)
sqlite3_stmt *statement;
const char *cmdtail;
if (sqlite3_prepare_v2(rhizome_db, sqlcmd, strlen(sqlcmd) + 1, &statement, &cmdtail) != SQLITE_OK) {
sqlite3_finalize(statement);
ret = WHY(sqlite3_errmsg(rhizome_db));
} else {
if (debug & DEBUG_RHIZOME) fprintf(stderr, "fileHaxHash = \"%s\"\n", m->fileHexHash);
if (debug & DEBUG_RHIZOME) fprintf(stderr, "fileHexHash = \"%s\"\n", m->fileHexHash);
sqlite3_bind_text(statement, 1, m->fileHexHash, -1, SQLITE_STATIC);
const char *name = rhizome_manifest_get(m, "name", NULL, 0);
size_t rows = 0;
while (sqlite3_step(statement) == SQLITE_ROW) {
++rows;
@ -793,12 +800,20 @@ int rhizome_find_duplicate(const rhizome_manifest *m, rhizome_manifest **found)
size_t manifestblobsize = sqlite3_column_bytes(statement, 1); // must call after sqlite3_column_blob()
rhizome_manifest *mq = rhizome_read_manifest_file(manifestblob, manifestblobsize, 0);
const char *nameq = rhizome_manifest_get(mq, "name", NULL, 0);
/* No need to compare "filehash" here, but we do so as a precaution. */
const char *filehashq = rhizome_manifest_get(mq, "filehash", NULL, 0);
long long lengthq = rhizome_manifest_get_ll(mq, "filesize");
if (debug & DEBUG_RHIZOME) fprintf(stderr, "Consider manifest.id=%s manifest.name=\"%s\"\n", manifestid, nameq);
if ( !strcmp(nameq, name)
&& !strncmp(rhizome_manifest_get(mq, "filehash", NULL, 0), m->fileHexHash, SHA512_DIGEST_STRING_LENGTH)) {
/* No need to compare "filehash" or "filesize" here, but we do so as a precaution if present */
if ( nameq && !strcmp(nameq, name)
&& (!filehashq || strncmp(filehashq, m->fileHexHash, SHA512_DIGEST_STRING_LENGTH) == 0)
&& (lengthq == -1 || lengthq == m->fileLength)
) {
memcpy(mq->fileHexHash, m->fileHexHash, SHA512_DIGEST_STRING_LENGTH);
mq->fileHashedP = 1;
mq->fileLength = m->fileLength;
*found = mq;
ret = 1;
if (debug & DEBUG_RHIZOME) fprintf(stderr, "found\n");
break;
}
rhizome_manifest_free(mq);

View File

@ -393,7 +393,7 @@ int rhizome_queue_manifest_import(rhizome_manifest *m,
rhizome_datastore_path,
rhizome_manifest_get(m,"id",NULL,0));
if (!rhizome_write_manifest_file(m,filename)) {
rhizome_bundle_import(m,rhizome_manifest_get(m,"id",NULL,0),
rhizome_bundle_import(m, NULL, rhizome_manifest_get(m,"id",NULL,0),
NULL /* no additional groups */,
m->ttl-1 /* TTL */,
1 /* do verify */,
@ -522,17 +522,15 @@ int rhizome_fetch_poll()
q->manifest->finalised=1;
q->manifest->manifest_bytes=q->manifest->manifest_all_bytes;
if (!rhizome_write_manifest_file(q->manifest,filename)) {
rhizome_bundle_import(q->manifest,
rhizome_manifest_get(q->manifest,
"id",NULL,0),
NULL /* no additional groups */,
q->manifest->ttl-1 /* TTL */,
1 /* do verify */,
1 /* do check hash of file */,
0 /* do not sign it, just keep existing
signatures */);
rhizome_bundle_import(q->manifest, NULL,
rhizome_manifest_get(q->manifest, "id", NULL, 0),
NULL /* no additional groups */,
q->manifest->ttl - 1 /* TTL */,
1 /* do verify */,
1 /* do check hash of file */,
0 /* do not sign it, just keep existing signatures */);
q->manifest=NULL;
}
}
}
}
break;

View File

@ -26,9 +26,29 @@ setup_dna_rhizome() {
executeOk $dna config set debug rhizome
}
assert_rhizome_list_empty() {
assert_rhizome_list() {
executeOk $dna rhizome list
assertStdoutIs -e "Found 0 rows\n"
assertStdoutLineCount '==' $#
assertStdoutGrep --matches=$# '^fileid=.*:name='
local filename
for filename; do
local filehash='[^:]\+'
# If there is a manifest file that looks like it matches this payload
# file, then use its file hash to check the rhizome list output.
if [ -r "$filename.manifest" ]; then
local name=$(sed -n -e '/^name=/s///p' "$filename.manifest")
if [ "$name" == "$filename" ]; then
filehash=$(sed -n -e '/^filehash=/s///p' "$filename.manifest")
fi
fi
assertStdoutGrep --matches=1 "^\(.*:\)\?fileid=$filehash:.*:name=$filename\$"
done
}
strip_signatures() {
for file; do
cat -v "$file" | sed -e '/^^@/,$d' >"tmp.$file" && mv -f "tmp.$file" "$file"
done
}
doc_InitialEmptyList="Initial rhizome list is empty"
@ -36,15 +56,47 @@ setup_InitialEmptyList() {
setup_dna_rhizome
}
test_InitialEmptyList() {
assert_rhizome_list_empty
assert_rhizome_list
}
doc_AddNoManifest="Rhizome add with no manifest file"
setup_AddNoManifest() {
setup_dna_rhizome
assert_rhizome_list
echo "A test file" >file1
echo "Another test file" >file2
}
test_AddNoManifest() {
executeOk $dna rhizome add file file1
}
doc_AddNonExistManifest="Rhizome add with non-existent manifest file"
setup_AddNonExistManifest() {
setup_dna_rhizome
assert_rhizome_list
echo "A test file" >file1
echo "Another test file" >file2
}
test_AddNonExistManifest() {
assert --error-on-fail [ ! -e file1.manifest ]
executeOk $dna rhizome add file file1 file1.manifest
assert [ -r file1.manifest ]
tfw_cat -v file1.manifest
assertGrep file1.manifest '^name=file1$'
assertGrep file1.manifest '^date=[0-9]\+$'
assertGrep file1.manifest '^version=[0-9]\+$'
assertGrep file1.manifest '^id=[0-9a-fA-F]\+$'
assertGrep file1.manifest "^filesize=$(cat file1 | wc --bytes)\$"
assertGrep file1.manifest "^first_byte=0$"
assertGrep file1.manifest "^last_byte=$(cat file1 | wc --bytes)\$"
}
doc_AddManifest="Rhizome add with manifest"
setup_AddManifest() {
setup_dna_rhizome
assert_rhizome_list_empty
assert_rhizome_list
echo "A test file" >file1
echo 'name=wah' >file1.manifest
echo -e 'name=wah\ndate=12345' >file1.manifest
echo "Another test file" >file2
}
test_AddManifest() {
@ -53,7 +105,7 @@ test_AddManifest() {
tfw_cat --stderr
tfw_cat -v file1.manifest
assertGrep file1.manifest '^name=wah$'
assertGrep file1.manifest '^date=[0-9]\+$'
assertGrep file1.manifest '^date=12345$'
assertGrep file1.manifest '^version=[0-9]\+$'
assertGrep file1.manifest '^id=[0-9a-fA-F]\+$'
assertGrep file1.manifest "^filesize=$(cat file1 | wc --bytes)\$"
@ -64,55 +116,86 @@ test_AddManifest() {
doc_AddThenList="Rhizome list contains one file after one add"
setup_AddThenList() {
setup_dna_rhizome
assert_rhizome_list_empty
assert_rhizome_list
echo "A test file" >file1
echo "Another test file" >file2
}
test_AddThenList() {
# Add first file
executeOk $dna rhizome add file file1
executeOk $dna rhizome list
assertStdoutGrep "^Found 1 rows$"
assertStdoutLineCount '==' 6
assertStdoutGrep --matches=1 '^file name = "file1"$'
assertStdoutGrep --matches=0 '^file name = "file2"$'
replayStdout >add1.stdout
executeOk $dna rhizome add file file1 file1.manifest
assert_rhizome_list file1
# Add second file
executeOk $dna rhizome add file file2
executeOk $dna rhizome list
assertStdoutGrep "^Found 2 rows$"
assertStdoutLineCount '==' 11
assertStdoutGrep --matches=1 '^file name = "file1"$'
assertStdoutGrep --matches=1 '^file name = "file2"$'
executeOk $dna rhizome add file file2 file2.manifest
assert_rhizome_list file1 file2
}
doc_AddDuplicate="Rhizome add of same file"
setup_AddDuplicate() {
setup_dna_rhizome
assert_rhizome_list_empty
assert_rhizome_list
echo "A test file" >file1
echo "Another test file" >file2
echo "A test file, second version" >file1_2
# Add first file
executeOk $dna rhizome add file file1
executeOk $dna rhizome add file file1 file1.manifest
# Add second file
executeOk $dna rhizome add file file2
executeOk $dna rhizome add file file2 file2.manifest
# Make sure they are both in the list.
executeOk $dna rhizome list
assertStdoutGrep "^Found 2 rows$"
assertStdoutGrep --matches=1 '^file name = "file1"$'
assertStdoutGrep --matches=1 '^file name = "file2"$'
assert_rhizome_list file1 file2
}
test_AddDuplicate() {
# Add first file again (nothing should change except its date).
executeOk $dna config get debug
tfw_cat --stdout
executeOk $dna rhizome add file file1
tfw_cat --stderr
executeOk $dna rhizome list
assertStdoutGrep "^Found 2 rows$"
assertStdoutLineCount '==' 11
assertStdoutGrep --matches=1 '^file name = "file1"$'
assertStdoutGrep --matches=1 '^file name = "file2"$'
# Add first file again - nothing should change in its manifests, and it
# should appear that the add command succeeded (with perhaps some grumbling
# on stderr).
executeOk $dna rhizome add file file1 file1.manifestA
assert [ -s file1.manifestA ]
assert_rhizome_list file1 file2
strip_signatures file1.manifest file1.manifestA
assert diff file1.manifest file1.manifestA
# Repeat for second file.
executeOk $dna rhizome add file file2 file2.manifestA
assert [ -s file2.manifestA ]
assert_rhizome_list file1 file2
strip_signatures file2.manifest file2.manifestA
assert diff file2.manifest file2.manifestA
}
doc_AddMismatched="Rhizome add fails on mismatched manifest and payload"
setup_AddMismatched() {
setup_AddDuplicate
}
test_AddMismatched() {
# Try to add another file using an existing manifest, should fail and leave
# the manifest file unchanged.
cp file1.manifest file1_2.manifest
execute $dna rhizome add file file1_2 file1_2.manifest
assertExitStatus '!=' 0
assert cmp file1.manifest file1_2.manifest
# And rhizome store should be unchanged.
assert_rhizome_list file1 file2
}
doc_AddUpdate="Rhizome add new version of existing file"
setup_AddUpdate() {
setup_AddDuplicate
}
test_AddUpdate() {
# Change payload associated with first manifest.
cp file1.manifest file1_2.manifest
strip_signatures file1_2.manifest
sed -i -e '/^date=/d' -e '/^filehash=/d' -e '/^filesize=/d' file1_2.manifest
tfw_cat file1_2.manifest
assertGrep --matches=0 --error-on-fail file1_2.manifest '^filehash='
executeOk $dna rhizome add file file1_2 file1_2.manifest
# The new manifest must now have a different filehash from the original.
filehash1=$(sed -n -e '/^filehash=/s///p' file1.manifest)
filehash1_2=$(sed -n -e '/^filehash=/s///p' file1_2.manifest)
assert [ -n "$filehash1" ]
assert [ -n "$filehash1_2" ]
assert [ "$filehash1" != "$filehash1_2" ]
# Rhizome store contents reflect new payload.
mv -f file1_2.manifest file1.manifest
assert_rhizome_list file1 file2
}
runTests "$@"