/* ** Do a file-by-file comparison of the content of the repository and ** the working check-out on disk. Report any errors. */ void vfile_compare_repository_to_disk(int vid){ int rc; Stmt q; Blob disk, repo; char *zOut; db_must_be_within_tree(); db_prepare(&q, "SELECT %Q || pathname, pathname, rid FROM vfile" " WHERE NOT deleted AND vid=%d AND is_selected(id)" " ORDER BY if_selected(id, pathname, origname) /*scan*/", g.zLocalRoot, vid ); md5sum_init(); while( db_step(&q)==SQLITE_ROW ){ const char *zFullpath = db_column_text(&q, 0); const char *zName = db_column_text(&q, 1); int rid = db_column_int(&q, 2); blob_zero(&disk); if( file_wd_islink(zFullpath) ){ rc = blob_read_link(&disk, zFullpath); }else{ rc = blob_read_from_file(&disk, zFullpath); } if( rc<0 ){ fossil_print("ERROR: cannot read file [%s]\n", zFullpath); blob_reset(&disk); continue; } blob_zero(&repo); content_get(rid, &repo); if( blob_size(&repo)!=blob_size(&disk) ){ fossil_print("ERROR: [%s] is %d bytes on disk but %d in the repository\n", zName, blob_size(&disk), blob_size(&repo)); zOut = write_blob_to_temp_file(&repo); fossil_print("NOTICE: Repository version of [%s] stored in [%s]\n", zName, zOut); sqlite3_free(zOut); blob_reset(&disk); blob_reset(&repo); continue; } if( blob_compare(&repo, &disk) ){ fossil_print( "ERROR: [%s] is different on disk compared to the repository\n", zName); zOut = write_blob_to_temp_file(&repo); fossil_print("NOTICE: Repository version of [%s] stored in [%s]\n", zName, zOut); sqlite3_free(zOut); } blob_reset(&disk); blob_reset(&repo); } db_finalize(&q); }
/* ** Compare a blob to a string. Return TRUE if they are equal. */ int blob_eq_str(Blob *pBlob, const char *z, int n){ Blob t; blob_is_init(pBlob); if( n<=0 ) n = strlen(z); t.aData = (char*)z; t.nUsed = n; t.xRealloc = blobReallocStatic; return blob_compare(pBlob, &t)==0; }
/* ** Extract an item from content from the bundle */ static void bundle_extract_item( int blobid, /* ID of the item to extract */ Blob *pOut /* Write the content into this blob */ ){ Stmt q; Blob x, basis, h1, h2; static Bag busy; db_prepare(&q, "SELECT uuid, delta, data FROM bblob" " WHERE blobid=%d", blobid); if( db_step(&q)!=SQLITE_ROW ){ db_finalize(&q); fossil_fatal("no such item: %d", blobid); } if( bag_find(&busy, blobid) ) fossil_fatal("delta loop"); blob_zero(&x); db_column_blob(&q, 2, &x); blob_uncompress(&x, &x); if( db_column_type(&q,1)==SQLITE_INTEGER ){ bundle_extract_item(db_column_int(&q,1), &basis); blob_delta_apply(&basis, &x, pOut); blob_reset(&basis); blob_reset(&x); }else if( db_column_type(&q,1)==SQLITE_TEXT ){ int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", db_column_text(&q,1)); if( rid==0 ){ fossil_fatal("cannot find delta basis %s", db_column_text(&q,1)); } content_get(rid, &basis); db_column_blob(&q, 2, &x); blob_delta_apply(&basis, &x, pOut); blob_reset(&basis); blob_reset(&x); }else{ *pOut = x; } blob_zero(&h1); db_column_blob(&q, 0, &h1); sha1sum_blob(pOut, &h2); if( blob_compare(&h1, &h2)!=0 ){ fossil_fatal("SHA1 hash mismatch - wanted %s, got %s", blob_str(&h1), blob_str(&h2)); } blob_reset(&h1); blob_reset(&h2); bag_remove(&busy, blobid); db_finalize(&q); }
/* ** COMMAND: test-cycle-compress ** ** Compress and uncompress each file named on the command line. ** Verify that the original content is recovered. */ void test_cycle_compress(void){ int i; Blob b1, b2, b3; for(i=2; i<g.argc; i++){ blob_read_from_file(&b1, g.argv[i]); blob_compress(&b1, &b2); blob_uncompress(&b2, &b3); if( blob_compare(&b1, &b3) ){ fossil_fatal("compress/uncompress cycle failed for %s", g.argv[i]); } blob_reset(&b1); blob_reset(&b2); blob_reset(&b3); } fossil_print("ok\n"); }
/* ** Prompt the user for a password. Store the result in the pPassphrase ** blob. ** ** Behavior is controlled by the verify parameter: ** ** 0 Just ask once. ** ** 1 If the first answer is a non-empty string, ask for ** verification. Repeat if the two strings do not match. ** ** 2 Ask twice, repeat if the strings do not match. */ void prompt_for_password( const char *zPrompt, Blob *pPassphrase, int verify ){ Blob secondTry; blob_zero(pPassphrase); blob_zero(&secondTry); while(1){ prompt_for_passphrase(zPrompt, pPassphrase); if( verify==0 ) break; if( verify==1 && blob_size(pPassphrase)==0 ) break; prompt_for_passphrase("Again: ", &secondTry); if( blob_compare(pPassphrase, &secondTry) ){ printf("Passphrases do not match. Try again...\n"); }else{ break; } } blob_reset(&secondTry); }
/* ** Load the record identify by rid. Make sure we can reproduce it ** without error. ** ** Panic if anything goes wrong. If this procedure returns it means ** that everything is OK. */ static void verify_rid(int rid){ Blob uuid, hash, content; if( content_size(rid, 0)<0 ){ return; /* No way to verify phantoms */ } blob_zero(&uuid); db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", rid); if( blob_size(&uuid)!=UUID_SIZE ){ fossil_fatal("not a valid rid: %d", rid); } if( content_get(rid, &content) ){ sha1sum_blob(&content, &hash); blob_reset(&content); if( blob_compare(&uuid, &hash) ){ fossil_fatal("hash of rid %d (%b) does not match its uuid (%b)", rid, &hash, &uuid); } blob_reset(&hash); } blob_reset(&uuid); }
/* ** Look at every VFILE entry with the given vid and set update ** VFILE.CHNGED field on every file according to whether or not ** the file has changes. 0 means no change. 1 means edited. 2 means ** the file has changed due to a merge. 3 means the file was added ** by a merge. ** ** If VFILE.DELETED is true or if VFILE.RID is zero, then the file was ** either removed from managemented via "vcs rm" or added via ** "vcs add", respectively, and in both cases we always know that ** the file has changed without having the check the size, mtime, ** or on-disk content. ** ** If the size of the file has changed, then we always know that the file ** changed without having to look at the mtime or on-disk content. ** ** The mtime of the file is only a factor if the mtime-changes setting ** is false and the useSha1sum flag is false. If the mtime-changes ** setting is true (or undefined - it defaults to true) or if useSha1sum ** is true, then we do not trust the mtime and will examine the on-disk ** content to determine if a file really is the same. ** ** If the mtime is used, it is used only to determine if files are the same. ** If the mtime of a file has changed, we still examine the on-disk content ** to see whether or not the edit was a null-edit. */ void vfile_check_signature(int vid, int notFileIsFatal, int useSha1sum){ int nErr = 0; Stmt q; Blob fileCksum, origCksum; int useMtime = useSha1sum==0 && db_get_boolean("mtime-changes", 1); db_begin_transaction(); db_prepare(&q, "SELECT id, %Q || pathname," " vfile.mrid, deleted, chnged, uuid, size, mtime" " FROM vfile LEFT JOIN blob ON vfile.mrid=blob.rid" " WHERE vid=%d ", g.zLocalRoot, vid); while( db_step(&q)==SQLITE_ROW ){ int id, rid, isDeleted; const char *zName; int chnged = 0; int oldChnged; i64 oldMtime; i64 currentMtime; i64 origSize; i64 currentSize; id = db_column_int(&q, 0); zName = db_column_text(&q, 1); rid = db_column_int(&q, 2); isDeleted = db_column_int(&q, 3); oldChnged = chnged = db_column_int(&q, 4); oldMtime = db_column_int64(&q, 7); currentSize = file_wd_size(zName); origSize = db_column_int64(&q, 6); currentMtime = file_wd_mtime(0); if( chnged==0 && (isDeleted || rid==0) ){ /* "vcs rm" or "vcs add" always change the file */ chnged = 1; }else if( !file_wd_isfile_or_link(0) && currentSize>=0 ){ if( notFileIsFatal ){ vcs_warning("not an ordinary file: %s", zName); nErr++; } chnged = 1; } if( origSize!=currentSize ){ if( chnged!=1 ){ /* A file size change is definitive - the file has changed. No ** need to check the mtime or sha1sum */ chnged = 1; } }else if( chnged==1 && rid!=0 && !isDeleted ){ /* File is believed to have changed but it is the same size. ** Double check that it really has changed by looking at content. */ assert( origSize==currentSize ); db_ephemeral_blob(&q, 5, &origCksum); if( sha1sum_file(zName, &fileCksum) ){ blob_zero(&fileCksum); } if( blob_compare(&fileCksum, &origCksum)==0 ) chnged = 0; blob_reset(&origCksum); blob_reset(&fileCksum); }else if( chnged==0 && (useMtime==0 || currentMtime!=oldMtime) ){ /* For files that were formerly believed to be unchanged, if their ** mtime changes, or unconditionally if --sha1sum is used, check ** to see if they have been edited by looking at their SHA1 sum */ assert( origSize==currentSize ); db_ephemeral_blob(&q, 5, &origCksum); if( sha1sum_file(zName, &fileCksum) ){ blob_zero(&fileCksum); } if( blob_compare(&fileCksum, &origCksum) ){ chnged = 1; } blob_reset(&origCksum); blob_reset(&fileCksum); } if( currentMtime!=oldMtime || chnged!=oldChnged ){ db_multi_exec("UPDATE vfile SET mtime=%lld, chnged=%d WHERE id=%d", currentMtime, chnged, id); } } db_finalize(&q); if( nErr ) vcs_fatal("abort due to prior errors"); db_end_transaction(0); }
VALUE blob_intersect_files(gzFile* files, int file_count) { VALUE result = rb_ary_new(); if(file_count == 0) return result; int master_idx = 0; int ii = 0; blob* master_blob = blob_make(512); blob* next_blob = blob_make(512); // bootstrap master_blob = blob_read(files[0], master_blob); // until a file runs out of data while(1) { int all_match = 1; int end_of_file = 0; for(ii = 0; ii < file_count; ++ii) { if(ii == master_idx) continue; // read blobs from this file until they aren't less than the // master blob int compare_result = 0; while(1) { next_blob = blob_read(files[ii], next_blob); if(next_blob == NULL) { end_of_file = 1; break; } else { compare_result = blob_compare(&next_blob, &master_blob); if(compare_result >= 0) break; } } // if any file ever reaches the end while we're looking it means // that we've found the entire intersection if(end_of_file) { all_match = 0; break; } // if we ever get a non-zero compare result then that means the // current candidate is a failure and we have a new candidate to // try if(compare_result != 0) { all_match = 0; break; } } // finish bailing out on end of file if(end_of_file) break; // store the match if we had one if(all_match) { rb_ary_push(result, rb_str_new(master_blob->data, master_blob->size)); } else { // if we didn't have a match then whichever blob failed first // becomes the new master and we try again blob* temp = master_blob; master_blob = next_blob; next_blob = temp; master_idx = ii; } } free(master_blob); free(next_blob); return result; }
/* ** There is a TEMP table bix(blobid,delta) containing a set of purgeitems ** that need to be transferred to the BLOB table. This routine does ** all items that have srcid=iSrc. The pBasis blob holds the content ** of the source document if iSrc>0. */ static void bundle_import_elements(int iSrc, Blob *pBasis, int isPriv){ Stmt q; static Bag busy; assert( pBasis!=0 || iSrc==0 ); if( iSrc>0 ){ if( bag_find(&busy, iSrc) ){ fossil_fatal("delta loop while uncompressing bundle artifacts"); } bag_insert(&busy, iSrc); } db_prepare(&q, "SELECT uuid, data, bblob.delta, bix.blobid" " FROM bix, bblob" " WHERE bix.delta=%d" " AND bix.blobid=bblob.blobid;", iSrc ); while( db_step(&q)==SQLITE_ROW ){ Blob h1, h2, c1, c2; int rid; blob_zero(&h1); db_column_blob(&q, 0, &h1); blob_zero(&c1); db_column_blob(&q, 1, &c1); blob_uncompress(&c1, &c1); blob_zero(&c2); if( db_column_type(&q,2)==SQLITE_TEXT && db_column_bytes(&q,2)==40 ){ Blob basis; rid = db_int(0,"SELECT rid FROM blob WHERE uuid=%Q", db_column_text(&q,2)); content_get(rid, &basis); blob_delta_apply(&basis, &c1, &c2); blob_reset(&basis); blob_reset(&c1); }else if( pBasis ){ blob_delta_apply(pBasis, &c1, &c2); blob_reset(&c1); }else{ c2 = c1; } sha1sum_blob(&c2, &h2); if( blob_compare(&h1, &h2)!=0 ){ fossil_fatal("SHA1 hash mismatch - wanted %s, got %s", blob_str(&h1), blob_str(&h2)); } blob_reset(&h2); rid = content_put_ex(&c2, blob_str(&h1), 0, 0, isPriv); if( rid==0 ){ fossil_fatal("%s", g.zErrMsg); }else{ if( !isPriv ) content_make_public(rid); content_get(rid, &c1); manifest_crosslink(rid, &c1, MC_NO_ERRORS); db_multi_exec("INSERT INTO got(rid) VALUES(%d)",rid); } bundle_import_elements(db_column_int(&q,3), &c2, isPriv); blob_reset(&c2); } db_finalize(&q); if( iSrc>0 ) bag_remove(&busy, iSrc); }
static void test_blob_new(void) { int rc; char a[BUF_SIZE]; blob_t *bp; uint32_t ms; blob_t *dupbp; int32_t c; memset(a, 'a', sizeof(a)); bp = NULL; rc = blob_new(a, sizeof(a)-1, &bp); assert(rc == 0 && bp != NULL); assert(blob_size(bp) == sizeof(a) - 1); ms = blob_memory_size(bp); assert(blob_size(bp) == ms); rc = blob_init(bp, a, sizeof(a)); assert(rc != 0); assert(blob_size(bp) == sizeof(a) - 1); assert(blob_size(bp) == ms); assert(ms == blob_memory_size(bp)); rc = blob_init(bp, a, 1); assert(rc == 0); assert(blob_size(bp) == 1); assert(ms == blob_memory_size(bp)); dupbp = NULL; rc = blob_dup(bp, &dupbp); assert(rc == 0 && dupbp != NULL); assert(blob_size(dupbp) == 1); assert(blob_memory_size(dupbp) == 1); c = blob_compare(bp, dupbp); assert(c == 0); c = blob_compare(dupbp, bp); assert(c == 0); rc = blob_init(bp, a, sizeof(a)); assert(rc != 0); assert(blob_size(bp) == 1); assert(ms == blob_memory_size(bp)); rc = blob_init(bp, a, sizeof(a)-1); assert(rc == 0); assert(blob_size(bp) == sizeof(a) - 1); assert(blob_size(bp) == ms); assert(ms == blob_memory_size(bp)); c = blob_compare(bp, dupbp); assert(c > 0); assert(c == blob_size(bp) - blob_size(dupbp)); c = blob_compare(dupbp, bp); assert(c < 0); assert(c == blob_size(dupbp) - blob_size(bp)); blob_free(bp); bp = NULL; blob_free(dupbp); dupbp = NULL; }