Fossil SCM

Detect when the repository associated with a check-out has been swapped out for a clone with different RID values, and give the user a warning. Still to do: automatically recover.

drh 2019-01-16 00:11 trunk merge
Commit 1b114d241ffa496d7ea1d60d6f2bed66fed4d1f0873ea95186731dd015fa334e
+1 -1
--- src/checkin.c
+++ src/checkin.c
@@ -2539,11 +2539,11 @@
25392539
"UPDATE vfile SET vid=%d;"
25402540
"UPDATE vfile SET rid=mrid, chnged=0, deleted=0, origname=NULL"
25412541
" WHERE is_selected(id);"
25422542
, vid, nvid
25432543
);
2544
- db_lset_int("checkout", nvid);
2544
+ db_set_checkout(nvid);
25452545
25462546
/* Update the isexe and islink columns of the vfile table */
25472547
db_prepare(&q,
25482548
"UPDATE vfile SET isexe=:exec, islink=:link"
25492549
" WHERE vid=:vid AND pathname=:path AND (isexe!=:exec OR islink!=:link)"
25502550
--- src/checkin.c
+++ src/checkin.c
@@ -2539,11 +2539,11 @@
2539 "UPDATE vfile SET vid=%d;"
2540 "UPDATE vfile SET rid=mrid, chnged=0, deleted=0, origname=NULL"
2541 " WHERE is_selected(id);"
2542 , vid, nvid
2543 );
2544 db_lset_int("checkout", nvid);
2545
2546 /* Update the isexe and islink columns of the vfile table */
2547 db_prepare(&q,
2548 "UPDATE vfile SET isexe=:exec, islink=:link"
2549 " WHERE vid=:vid AND pathname=:path AND (isexe!=:exec OR islink!=:link)"
2550
--- src/checkin.c
+++ src/checkin.c
@@ -2539,11 +2539,11 @@
2539 "UPDATE vfile SET vid=%d;"
2540 "UPDATE vfile SET rid=mrid, chnged=0, deleted=0, origname=NULL"
2541 " WHERE is_selected(id);"
2542 , vid, nvid
2543 );
2544 db_set_checkout(nvid);
2545
2546 /* Update the isexe and islink columns of the vfile table */
2547 db_prepare(&q,
2548 "UPDATE vfile SET isexe=:exec, islink=:link"
2549 " WHERE vid=:vid AND pathname=:path AND (isexe!=:exec OR islink!=:link)"
2550
+1 -1
--- src/checkout.c
+++ src/checkout.c
@@ -304,11 +304,11 @@
304304
vfile_to_disk(vid, 0, !g.fQuiet, promptFlag);
305305
}
306306
checkout_set_all_exe(vid);
307307
manifest_to_disk(vid);
308308
ensure_empty_dirs_created();
309
- db_lset_int("checkout", vid);
309
+ db_set_checkout(vid);
310310
undo_reset();
311311
db_multi_exec("DELETE FROM vmerge");
312312
if( !keepFlag && db_get_boolean("repo-cksum",1) ){
313313
vfile_aggregate_checksum_manifest(vid, &cksum1, &cksum1b);
314314
vfile_aggregate_checksum_disk(vid, &cksum2);
315315
--- src/checkout.c
+++ src/checkout.c
@@ -304,11 +304,11 @@
304 vfile_to_disk(vid, 0, !g.fQuiet, promptFlag);
305 }
306 checkout_set_all_exe(vid);
307 manifest_to_disk(vid);
308 ensure_empty_dirs_created();
309 db_lset_int("checkout", vid);
310 undo_reset();
311 db_multi_exec("DELETE FROM vmerge");
312 if( !keepFlag && db_get_boolean("repo-cksum",1) ){
313 vfile_aggregate_checksum_manifest(vid, &cksum1, &cksum1b);
314 vfile_aggregate_checksum_disk(vid, &cksum2);
315
--- src/checkout.c
+++ src/checkout.c
@@ -304,11 +304,11 @@
304 vfile_to_disk(vid, 0, !g.fQuiet, promptFlag);
305 }
306 checkout_set_all_exe(vid);
307 manifest_to_disk(vid);
308 ensure_empty_dirs_created();
309 db_set_checkout(vid);
310 undo_reset();
311 db_multi_exec("DELETE FROM vmerge");
312 if( !keepFlag && db_get_boolean("repo-cksum",1) ){
313 vfile_aggregate_checksum_manifest(vid, &cksum1, &cksum1b);
314 vfile_aggregate_checksum_disk(vid, &cksum2);
315
+135 -1
--- src/db.c
+++ src/db.c
@@ -1655,10 +1655,29 @@
16551655
16561656
/* Make a change to the CHECK constraint on the BLOB table for
16571657
** version 2.0 and later.
16581658
*/
16591659
rebuild_schema_update_2_0(); /* Do the Fossil-2.0 schema updates */
1660
+
1661
+ /* If the checkout database was opened first, then check to make
1662
+ ** sure that the repository database that was just opened has not
1663
+ ** be replaced by a clone of the same project, with different RID
1664
+ ** values.
1665
+ */
1666
+ if( g.localOpen && !db_fingerprint_ok() ){
1667
+ fossil_print(
1668
+ "Oops. It looks like the repository database file located at\n"
1669
+ " \"%s\"\n", zDbName
1670
+ );
1671
+ fossil_print(
1672
+ "has been swapped with a clone that may have different\n"
1673
+ "integer keys for the various artifacts. As of 2019-01-11,\n"
1674
+ "we are working on enhancing Fossil to be able to deal with\n"
1675
+ "that automatically, but we are not there yet. Sorry.\n\n"
1676
+ );
1677
+ fossil_fatal("bad fingerprint");
1678
+ }
16601679
}
16611680
16621681
/*
16631682
** Return true if there have been any changes to the repository
16641683
** database since it was opened.
@@ -2872,11 +2891,11 @@
28722891
g.allowSymlinks = db_get_boolean("allow-symlinks",
28732892
db_allow_symlinks_by_default());
28742893
}
28752894
db_lset("repository", g.argv[2]);
28762895
db_record_repository_filename(g.argv[2]);
2877
- db_lset_int("checkout", 0);
2896
+ db_set_checkout(0);
28782897
azNewArgv[0] = g.argv[0];
28792898
g.argv = azNewArgv;
28802899
if( !emptyFlag ){
28812900
g.argc = 3;
28822901
if( g.zOpenRevision ){
@@ -3704,5 +3723,120 @@
37043723
db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
37053724
fossil_print("Repository database: %s\n", g.zRepositoryName);
37063725
fossil_print("Local database: %s\n", g.zLocalDbName);
37073726
fossil_print("Config database: %s\n", g.zConfigDbName);
37083727
}
3728
+
3729
+/*
3730
+** Compute a "fingerprint" on the repository. A fingerprint is used
3731
+** to verify that that the repository has not been replaced by a clone
3732
+** of the same repository. More precisely, a fingerprint are used to
3733
+** verify that the mapping between SHA3 hashes and RID values is unchanged.
3734
+**
3735
+** The checkout database ("localdb") stores RID values. When associating
3736
+** a checkout database against a repository database, it is useful to verify
3737
+** the fingerprint so that we know tha the RID values in the checkout
3738
+** database still correspond to the correct entries in the BLOB table of
3739
+** the repository.
3740
+**
3741
+** The fingerprint is based on the RCVFROM table. When constructing a
3742
+** new fingerprint, use the most recent RCVFROM entry. (Set rcvid==0 to
3743
+** accomplish this.) When verifying an old fingerprint, use the same
3744
+** RCVFROM entry that generated the fingerprint in the first place.
3745
+**
3746
+** The fingerprint consists of the rcvid, a "/", and the MD5 checksum of
3747
+** the remaining fields of the RCVFROM table entry. MD5 is used for this
3748
+** because it is 4x faster than SHA3 and 5x faster than SHA1, and there
3749
+** are no security concerns - this is just a checksum, not a security
3750
+** token.
3751
+*/
3752
+char *db_fingerprint(int rcvid){
3753
+ char *z = 0;
3754
+ Blob sql = BLOB_INITIALIZER;
3755
+ Stmt q;
3756
+ blob_append_sql(&sql,
3757
+ "SELECT rcvid, quote(uid), quote(mtime), quote(nonce), quote(ipaddr)"
3758
+ " FROM rcvfrom"
3759
+ );
3760
+ if( rcvid<=0 ){
3761
+ blob_append_sql(&sql, " ORDER BY rcvid DESC LIMIT 1");
3762
+ }else{
3763
+ blob_append_sql(&sql, " WHERE rcvid=%d", rcvid);
3764
+ }
3765
+ db_prepare_blob(&q, &sql);
3766
+ blob_reset(&sql);
3767
+ if( db_step(&q)==SQLITE_ROW ){
3768
+ int i;
3769
+ md5sum_init();
3770
+ for(i=1; i<=4; i++){
3771
+ md5sum_step_text(db_column_text(&q,i),-1);
3772
+ }
3773
+ z = mprintf("%d/%s",db_column_int(&q,0),md5sum_finish(0));
3774
+ }
3775
+ db_finalize(&q);
3776
+ return z;
3777
+}
3778
+
3779
+/*
3780
+** COMMAND: test-fingerprint
3781
+**
3782
+** Usage: %fossil test-fingerprint ?RCVID? ?--check?
3783
+**
3784
+** Display the repository fingerprint. Or if the --check option
3785
+** is provided and this command is run from a checkout, invoke the
3786
+** db_fingerprint_ok() method and print its result.
3787
+*/
3788
+void test_fingerprint(void){
3789
+ int rcvid = 0;
3790
+ if( find_option("check",0,0)!=0 ){
3791
+ db_must_be_within_tree();
3792
+ fossil_print("db_fingerprint_ok() => %d\n", db_fingerprint_ok());
3793
+ return;
3794
+ }
3795
+ db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
3796
+ if( g.argc==3 ){
3797
+ rcvid = atoi(g.argv[2]);
3798
+ }else if( g.argc!=2 ){
3799
+ fossil_fatal("wrong number of arguments");
3800
+ }
3801
+ fossil_print("%z\n", db_fingerprint(rcvid));
3802
+}
3803
+
3804
+/*
3805
+** Set the value of the "checkout" entry in the VVAR table.
3806
+**
3807
+** Also set "fingerprint" and "checkout-hash".
3808
+*/
3809
+void db_set_checkout(int rid){
3810
+ char *z;
3811
+ db_lset_int("checkout", rid);
3812
+ z = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",rid);
3813
+ db_lset("checkout-hash", z);
3814
+ fossil_free(z);
3815
+ z = db_fingerprint(0);
3816
+ db_lset("fingerprint", z);
3817
+ fossil_free(z);
3818
+}
3819
+
3820
+/*
3821
+** Verify that the fingerprint recorded in the "fingerprint" entry
3822
+** of the VVAR table matches the fingerprint on the currently
3823
+** connected repository. Return true if the fingerprint is ok, and
3824
+** return false if the fingerprint does not match.
3825
+*/
3826
+int db_fingerprint_ok(void){
3827
+ char *zCkout; /* The fingerprint recorded in the checkout database */
3828
+ char *zRepo; /* The fingerprint of the repository */
3829
+ int rc; /* Result */
3830
+
3831
+ zCkout = db_text(0,"SELECT value FROM localdb.vvar WHERE name='fingerprint'");
3832
+ if( zCkout==0 ){
3833
+ /* This is an older checkout that does not record a fingerprint.
3834
+ ** We have to assume everything is ok */
3835
+ return 2;
3836
+ }
3837
+ zRepo = db_fingerprint(atoi(zCkout));
3838
+ rc = fossil_strcmp(zCkout,zRepo)==0;
3839
+ fossil_free(zCkout);
3840
+ fossil_free(zRepo);
3841
+ return rc;
3842
+}
37093843
--- src/db.c
+++ src/db.c
@@ -1655,10 +1655,29 @@
1655
1656 /* Make a change to the CHECK constraint on the BLOB table for
1657 ** version 2.0 and later.
1658 */
1659 rebuild_schema_update_2_0(); /* Do the Fossil-2.0 schema updates */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1660 }
1661
1662 /*
1663 ** Return true if there have been any changes to the repository
1664 ** database since it was opened.
@@ -2872,11 +2891,11 @@
2872 g.allowSymlinks = db_get_boolean("allow-symlinks",
2873 db_allow_symlinks_by_default());
2874 }
2875 db_lset("repository", g.argv[2]);
2876 db_record_repository_filename(g.argv[2]);
2877 db_lset_int("checkout", 0);
2878 azNewArgv[0] = g.argv[0];
2879 g.argv = azNewArgv;
2880 if( !emptyFlag ){
2881 g.argc = 3;
2882 if( g.zOpenRevision ){
@@ -3704,5 +3723,120 @@
3704 db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
3705 fossil_print("Repository database: %s\n", g.zRepositoryName);
3706 fossil_print("Local database: %s\n", g.zLocalDbName);
3707 fossil_print("Config database: %s\n", g.zConfigDbName);
3708 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3709
--- src/db.c
+++ src/db.c
@@ -1655,10 +1655,29 @@
1655
1656 /* Make a change to the CHECK constraint on the BLOB table for
1657 ** version 2.0 and later.
1658 */
1659 rebuild_schema_update_2_0(); /* Do the Fossil-2.0 schema updates */
1660
1661 /* If the checkout database was opened first, then check to make
1662 ** sure that the repository database that was just opened has not
1663 ** be replaced by a clone of the same project, with different RID
1664 ** values.
1665 */
1666 if( g.localOpen && !db_fingerprint_ok() ){
1667 fossil_print(
1668 "Oops. It looks like the repository database file located at\n"
1669 " \"%s\"\n", zDbName
1670 );
1671 fossil_print(
1672 "has been swapped with a clone that may have different\n"
1673 "integer keys for the various artifacts. As of 2019-01-11,\n"
1674 "we are working on enhancing Fossil to be able to deal with\n"
1675 "that automatically, but we are not there yet. Sorry.\n\n"
1676 );
1677 fossil_fatal("bad fingerprint");
1678 }
1679 }
1680
1681 /*
1682 ** Return true if there have been any changes to the repository
1683 ** database since it was opened.
@@ -2872,11 +2891,11 @@
2891 g.allowSymlinks = db_get_boolean("allow-symlinks",
2892 db_allow_symlinks_by_default());
2893 }
2894 db_lset("repository", g.argv[2]);
2895 db_record_repository_filename(g.argv[2]);
2896 db_set_checkout(0);
2897 azNewArgv[0] = g.argv[0];
2898 g.argv = azNewArgv;
2899 if( !emptyFlag ){
2900 g.argc = 3;
2901 if( g.zOpenRevision ){
@@ -3704,5 +3723,120 @@
3723 db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
3724 fossil_print("Repository database: %s\n", g.zRepositoryName);
3725 fossil_print("Local database: %s\n", g.zLocalDbName);
3726 fossil_print("Config database: %s\n", g.zConfigDbName);
3727 }
3728
3729 /*
3730 ** Compute a "fingerprint" on the repository. A fingerprint is used
3731 ** to verify that that the repository has not been replaced by a clone
3732 ** of the same repository. More precisely, a fingerprint are used to
3733 ** verify that the mapping between SHA3 hashes and RID values is unchanged.
3734 **
3735 ** The checkout database ("localdb") stores RID values. When associating
3736 ** a checkout database against a repository database, it is useful to verify
3737 ** the fingerprint so that we know tha the RID values in the checkout
3738 ** database still correspond to the correct entries in the BLOB table of
3739 ** the repository.
3740 **
3741 ** The fingerprint is based on the RCVFROM table. When constructing a
3742 ** new fingerprint, use the most recent RCVFROM entry. (Set rcvid==0 to
3743 ** accomplish this.) When verifying an old fingerprint, use the same
3744 ** RCVFROM entry that generated the fingerprint in the first place.
3745 **
3746 ** The fingerprint consists of the rcvid, a "/", and the MD5 checksum of
3747 ** the remaining fields of the RCVFROM table entry. MD5 is used for this
3748 ** because it is 4x faster than SHA3 and 5x faster than SHA1, and there
3749 ** are no security concerns - this is just a checksum, not a security
3750 ** token.
3751 */
3752 char *db_fingerprint(int rcvid){
3753 char *z = 0;
3754 Blob sql = BLOB_INITIALIZER;
3755 Stmt q;
3756 blob_append_sql(&sql,
3757 "SELECT rcvid, quote(uid), quote(mtime), quote(nonce), quote(ipaddr)"
3758 " FROM rcvfrom"
3759 );
3760 if( rcvid<=0 ){
3761 blob_append_sql(&sql, " ORDER BY rcvid DESC LIMIT 1");
3762 }else{
3763 blob_append_sql(&sql, " WHERE rcvid=%d", rcvid);
3764 }
3765 db_prepare_blob(&q, &sql);
3766 blob_reset(&sql);
3767 if( db_step(&q)==SQLITE_ROW ){
3768 int i;
3769 md5sum_init();
3770 for(i=1; i<=4; i++){
3771 md5sum_step_text(db_column_text(&q,i),-1);
3772 }
3773 z = mprintf("%d/%s",db_column_int(&q,0),md5sum_finish(0));
3774 }
3775 db_finalize(&q);
3776 return z;
3777 }
3778
3779 /*
3780 ** COMMAND: test-fingerprint
3781 **
3782 ** Usage: %fossil test-fingerprint ?RCVID? ?--check?
3783 **
3784 ** Display the repository fingerprint. Or if the --check option
3785 ** is provided and this command is run from a checkout, invoke the
3786 ** db_fingerprint_ok() method and print its result.
3787 */
3788 void test_fingerprint(void){
3789 int rcvid = 0;
3790 if( find_option("check",0,0)!=0 ){
3791 db_must_be_within_tree();
3792 fossil_print("db_fingerprint_ok() => %d\n", db_fingerprint_ok());
3793 return;
3794 }
3795 db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
3796 if( g.argc==3 ){
3797 rcvid = atoi(g.argv[2]);
3798 }else if( g.argc!=2 ){
3799 fossil_fatal("wrong number of arguments");
3800 }
3801 fossil_print("%z\n", db_fingerprint(rcvid));
3802 }
3803
3804 /*
3805 ** Set the value of the "checkout" entry in the VVAR table.
3806 **
3807 ** Also set "fingerprint" and "checkout-hash".
3808 */
3809 void db_set_checkout(int rid){
3810 char *z;
3811 db_lset_int("checkout", rid);
3812 z = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",rid);
3813 db_lset("checkout-hash", z);
3814 fossil_free(z);
3815 z = db_fingerprint(0);
3816 db_lset("fingerprint", z);
3817 fossil_free(z);
3818 }
3819
3820 /*
3821 ** Verify that the fingerprint recorded in the "fingerprint" entry
3822 ** of the VVAR table matches the fingerprint on the currently
3823 ** connected repository. Return true if the fingerprint is ok, and
3824 ** return false if the fingerprint does not match.
3825 */
3826 int db_fingerprint_ok(void){
3827 char *zCkout; /* The fingerprint recorded in the checkout database */
3828 char *zRepo; /* The fingerprint of the repository */
3829 int rc; /* Result */
3830
3831 zCkout = db_text(0,"SELECT value FROM localdb.vvar WHERE name='fingerprint'");
3832 if( zCkout==0 ){
3833 /* This is an older checkout that does not record a fingerprint.
3834 ** We have to assume everything is ok */
3835 return 2;
3836 }
3837 zRepo = db_fingerprint(atoi(zCkout));
3838 rc = fossil_strcmp(zCkout,zRepo)==0;
3839 fossil_free(zCkout);
3840 fossil_free(zRepo);
3841 return rc;
3842 }
3843
+135 -1
--- src/db.c
+++ src/db.c
@@ -1655,10 +1655,29 @@
16551655
16561656
/* Make a change to the CHECK constraint on the BLOB table for
16571657
** version 2.0 and later.
16581658
*/
16591659
rebuild_schema_update_2_0(); /* Do the Fossil-2.0 schema updates */
1660
+
1661
+ /* If the checkout database was opened first, then check to make
1662
+ ** sure that the repository database that was just opened has not
1663
+ ** be replaced by a clone of the same project, with different RID
1664
+ ** values.
1665
+ */
1666
+ if( g.localOpen && !db_fingerprint_ok() ){
1667
+ fossil_print(
1668
+ "Oops. It looks like the repository database file located at\n"
1669
+ " \"%s\"\n", zDbName
1670
+ );
1671
+ fossil_print(
1672
+ "has been swapped with a clone that may have different\n"
1673
+ "integer keys for the various artifacts. As of 2019-01-11,\n"
1674
+ "we are working on enhancing Fossil to be able to deal with\n"
1675
+ "that automatically, but we are not there yet. Sorry.\n\n"
1676
+ );
1677
+ fossil_fatal("bad fingerprint");
1678
+ }
16601679
}
16611680
16621681
/*
16631682
** Return true if there have been any changes to the repository
16641683
** database since it was opened.
@@ -2872,11 +2891,11 @@
28722891
g.allowSymlinks = db_get_boolean("allow-symlinks",
28732892
db_allow_symlinks_by_default());
28742893
}
28752894
db_lset("repository", g.argv[2]);
28762895
db_record_repository_filename(g.argv[2]);
2877
- db_lset_int("checkout", 0);
2896
+ db_set_checkout(0);
28782897
azNewArgv[0] = g.argv[0];
28792898
g.argv = azNewArgv;
28802899
if( !emptyFlag ){
28812900
g.argc = 3;
28822901
if( g.zOpenRevision ){
@@ -3704,5 +3723,120 @@
37043723
db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
37053724
fossil_print("Repository database: %s\n", g.zRepositoryName);
37063725
fossil_print("Local database: %s\n", g.zLocalDbName);
37073726
fossil_print("Config database: %s\n", g.zConfigDbName);
37083727
}
3728
+
3729
+/*
3730
+** Compute a "fingerprint" on the repository. A fingerprint is used
3731
+** to verify that that the repository has not been replaced by a clone
3732
+** of the same repository. More precisely, a fingerprint are used to
3733
+** verify that the mapping between SHA3 hashes and RID values is unchanged.
3734
+**
3735
+** The checkout database ("localdb") stores RID values. When associating
3736
+** a checkout database against a repository database, it is useful to verify
3737
+** the fingerprint so that we know tha the RID values in the checkout
3738
+** database still correspond to the correct entries in the BLOB table of
3739
+** the repository.
3740
+**
3741
+** The fingerprint is based on the RCVFROM table. When constructing a
3742
+** new fingerprint, use the most recent RCVFROM entry. (Set rcvid==0 to
3743
+** accomplish this.) When verifying an old fingerprint, use the same
3744
+** RCVFROM entry that generated the fingerprint in the first place.
3745
+**
3746
+** The fingerprint consists of the rcvid, a "/", and the MD5 checksum of
3747
+** the remaining fields of the RCVFROM table entry. MD5 is used for this
3748
+** because it is 4x faster than SHA3 and 5x faster than SHA1, and there
3749
+** are no security concerns - this is just a checksum, not a security
3750
+** token.
3751
+*/
3752
+char *db_fingerprint(int rcvid){
3753
+ char *z = 0;
3754
+ Blob sql = BLOB_INITIALIZER;
3755
+ Stmt q;
3756
+ blob_append_sql(&sql,
3757
+ "SELECT rcvid, quote(uid), quote(mtime), quote(nonce), quote(ipaddr)"
3758
+ " FROM rcvfrom"
3759
+ );
3760
+ if( rcvid<=0 ){
3761
+ blob_append_sql(&sql, " ORDER BY rcvid DESC LIMIT 1");
3762
+ }else{
3763
+ blob_append_sql(&sql, " WHERE rcvid=%d", rcvid);
3764
+ }
3765
+ db_prepare_blob(&q, &sql);
3766
+ blob_reset(&sql);
3767
+ if( db_step(&q)==SQLITE_ROW ){
3768
+ int i;
3769
+ md5sum_init();
3770
+ for(i=1; i<=4; i++){
3771
+ md5sum_step_text(db_column_text(&q,i),-1);
3772
+ }
3773
+ z = mprintf("%d/%s",db_column_int(&q,0),md5sum_finish(0));
3774
+ }
3775
+ db_finalize(&q);
3776
+ return z;
3777
+}
3778
+
3779
+/*
3780
+** COMMAND: test-fingerprint
3781
+**
3782
+** Usage: %fossil test-fingerprint ?RCVID? ?--check?
3783
+**
3784
+** Display the repository fingerprint. Or if the --check option
3785
+** is provided and this command is run from a checkout, invoke the
3786
+** db_fingerprint_ok() method and print its result.
3787
+*/
3788
+void test_fingerprint(void){
3789
+ int rcvid = 0;
3790
+ if( find_option("check",0,0)!=0 ){
3791
+ db_must_be_within_tree();
3792
+ fossil_print("db_fingerprint_ok() => %d\n", db_fingerprint_ok());
3793
+ return;
3794
+ }
3795
+ db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
3796
+ if( g.argc==3 ){
3797
+ rcvid = atoi(g.argv[2]);
3798
+ }else if( g.argc!=2 ){
3799
+ fossil_fatal("wrong number of arguments");
3800
+ }
3801
+ fossil_print("%z\n", db_fingerprint(rcvid));
3802
+}
3803
+
3804
+/*
3805
+** Set the value of the "checkout" entry in the VVAR table.
3806
+**
3807
+** Also set "fingerprint" and "checkout-hash".
3808
+*/
3809
+void db_set_checkout(int rid){
3810
+ char *z;
3811
+ db_lset_int("checkout", rid);
3812
+ z = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",rid);
3813
+ db_lset("checkout-hash", z);
3814
+ fossil_free(z);
3815
+ z = db_fingerprint(0);
3816
+ db_lset("fingerprint", z);
3817
+ fossil_free(z);
3818
+}
3819
+
3820
+/*
3821
+** Verify that the fingerprint recorded in the "fingerprint" entry
3822
+** of the VVAR table matches the fingerprint on the currently
3823
+** connected repository. Return true if the fingerprint is ok, and
3824
+** return false if the fingerprint does not match.
3825
+*/
3826
+int db_fingerprint_ok(void){
3827
+ char *zCkout; /* The fingerprint recorded in the checkout database */
3828
+ char *zRepo; /* The fingerprint of the repository */
3829
+ int rc; /* Result */
3830
+
3831
+ zCkout = db_text(0,"SELECT value FROM localdb.vvar WHERE name='fingerprint'");
3832
+ if( zCkout==0 ){
3833
+ /* This is an older checkout that does not record a fingerprint.
3834
+ ** We have to assume everything is ok */
3835
+ return 2;
3836
+ }
3837
+ zRepo = db_fingerprint(atoi(zCkout));
3838
+ rc = fossil_strcmp(zCkout,zRepo)==0;
3839
+ fossil_free(zCkout);
3840
+ fossil_free(zRepo);
3841
+ return rc;
3842
+}
37093843
--- src/db.c
+++ src/db.c
@@ -1655,10 +1655,29 @@
1655
1656 /* Make a change to the CHECK constraint on the BLOB table for
1657 ** version 2.0 and later.
1658 */
1659 rebuild_schema_update_2_0(); /* Do the Fossil-2.0 schema updates */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1660 }
1661
1662 /*
1663 ** Return true if there have been any changes to the repository
1664 ** database since it was opened.
@@ -2872,11 +2891,11 @@
2872 g.allowSymlinks = db_get_boolean("allow-symlinks",
2873 db_allow_symlinks_by_default());
2874 }
2875 db_lset("repository", g.argv[2]);
2876 db_record_repository_filename(g.argv[2]);
2877 db_lset_int("checkout", 0);
2878 azNewArgv[0] = g.argv[0];
2879 g.argv = azNewArgv;
2880 if( !emptyFlag ){
2881 g.argc = 3;
2882 if( g.zOpenRevision ){
@@ -3704,5 +3723,120 @@
3704 db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
3705 fossil_print("Repository database: %s\n", g.zRepositoryName);
3706 fossil_print("Local database: %s\n", g.zLocalDbName);
3707 fossil_print("Config database: %s\n", g.zConfigDbName);
3708 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3709
--- src/db.c
+++ src/db.c
@@ -1655,10 +1655,29 @@
1655
1656 /* Make a change to the CHECK constraint on the BLOB table for
1657 ** version 2.0 and later.
1658 */
1659 rebuild_schema_update_2_0(); /* Do the Fossil-2.0 schema updates */
1660
1661 /* If the checkout database was opened first, then check to make
1662 ** sure that the repository database that was just opened has not
1663 ** be replaced by a clone of the same project, with different RID
1664 ** values.
1665 */
1666 if( g.localOpen && !db_fingerprint_ok() ){
1667 fossil_print(
1668 "Oops. It looks like the repository database file located at\n"
1669 " \"%s\"\n", zDbName
1670 );
1671 fossil_print(
1672 "has been swapped with a clone that may have different\n"
1673 "integer keys for the various artifacts. As of 2019-01-11,\n"
1674 "we are working on enhancing Fossil to be able to deal with\n"
1675 "that automatically, but we are not there yet. Sorry.\n\n"
1676 );
1677 fossil_fatal("bad fingerprint");
1678 }
1679 }
1680
1681 /*
1682 ** Return true if there have been any changes to the repository
1683 ** database since it was opened.
@@ -2872,11 +2891,11 @@
2891 g.allowSymlinks = db_get_boolean("allow-symlinks",
2892 db_allow_symlinks_by_default());
2893 }
2894 db_lset("repository", g.argv[2]);
2895 db_record_repository_filename(g.argv[2]);
2896 db_set_checkout(0);
2897 azNewArgv[0] = g.argv[0];
2898 g.argv = azNewArgv;
2899 if( !emptyFlag ){
2900 g.argc = 3;
2901 if( g.zOpenRevision ){
@@ -3704,5 +3723,120 @@
3723 db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
3724 fossil_print("Repository database: %s\n", g.zRepositoryName);
3725 fossil_print("Local database: %s\n", g.zLocalDbName);
3726 fossil_print("Config database: %s\n", g.zConfigDbName);
3727 }
3728
3729 /*
3730 ** Compute a "fingerprint" on the repository. A fingerprint is used
3731 ** to verify that that the repository has not been replaced by a clone
3732 ** of the same repository. More precisely, a fingerprint are used to
3733 ** verify that the mapping between SHA3 hashes and RID values is unchanged.
3734 **
3735 ** The checkout database ("localdb") stores RID values. When associating
3736 ** a checkout database against a repository database, it is useful to verify
3737 ** the fingerprint so that we know tha the RID values in the checkout
3738 ** database still correspond to the correct entries in the BLOB table of
3739 ** the repository.
3740 **
3741 ** The fingerprint is based on the RCVFROM table. When constructing a
3742 ** new fingerprint, use the most recent RCVFROM entry. (Set rcvid==0 to
3743 ** accomplish this.) When verifying an old fingerprint, use the same
3744 ** RCVFROM entry that generated the fingerprint in the first place.
3745 **
3746 ** The fingerprint consists of the rcvid, a "/", and the MD5 checksum of
3747 ** the remaining fields of the RCVFROM table entry. MD5 is used for this
3748 ** because it is 4x faster than SHA3 and 5x faster than SHA1, and there
3749 ** are no security concerns - this is just a checksum, not a security
3750 ** token.
3751 */
3752 char *db_fingerprint(int rcvid){
3753 char *z = 0;
3754 Blob sql = BLOB_INITIALIZER;
3755 Stmt q;
3756 blob_append_sql(&sql,
3757 "SELECT rcvid, quote(uid), quote(mtime), quote(nonce), quote(ipaddr)"
3758 " FROM rcvfrom"
3759 );
3760 if( rcvid<=0 ){
3761 blob_append_sql(&sql, " ORDER BY rcvid DESC LIMIT 1");
3762 }else{
3763 blob_append_sql(&sql, " WHERE rcvid=%d", rcvid);
3764 }
3765 db_prepare_blob(&q, &sql);
3766 blob_reset(&sql);
3767 if( db_step(&q)==SQLITE_ROW ){
3768 int i;
3769 md5sum_init();
3770 for(i=1; i<=4; i++){
3771 md5sum_step_text(db_column_text(&q,i),-1);
3772 }
3773 z = mprintf("%d/%s",db_column_int(&q,0),md5sum_finish(0));
3774 }
3775 db_finalize(&q);
3776 return z;
3777 }
3778
3779 /*
3780 ** COMMAND: test-fingerprint
3781 **
3782 ** Usage: %fossil test-fingerprint ?RCVID? ?--check?
3783 **
3784 ** Display the repository fingerprint. Or if the --check option
3785 ** is provided and this command is run from a checkout, invoke the
3786 ** db_fingerprint_ok() method and print its result.
3787 */
3788 void test_fingerprint(void){
3789 int rcvid = 0;
3790 if( find_option("check",0,0)!=0 ){
3791 db_must_be_within_tree();
3792 fossil_print("db_fingerprint_ok() => %d\n", db_fingerprint_ok());
3793 return;
3794 }
3795 db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
3796 if( g.argc==3 ){
3797 rcvid = atoi(g.argv[2]);
3798 }else if( g.argc!=2 ){
3799 fossil_fatal("wrong number of arguments");
3800 }
3801 fossil_print("%z\n", db_fingerprint(rcvid));
3802 }
3803
3804 /*
3805 ** Set the value of the "checkout" entry in the VVAR table.
3806 **
3807 ** Also set "fingerprint" and "checkout-hash".
3808 */
3809 void db_set_checkout(int rid){
3810 char *z;
3811 db_lset_int("checkout", rid);
3812 z = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",rid);
3813 db_lset("checkout-hash", z);
3814 fossil_free(z);
3815 z = db_fingerprint(0);
3816 db_lset("fingerprint", z);
3817 fossil_free(z);
3818 }
3819
3820 /*
3821 ** Verify that the fingerprint recorded in the "fingerprint" entry
3822 ** of the VVAR table matches the fingerprint on the currently
3823 ** connected repository. Return true if the fingerprint is ok, and
3824 ** return false if the fingerprint does not match.
3825 */
3826 int db_fingerprint_ok(void){
3827 char *zCkout; /* The fingerprint recorded in the checkout database */
3828 char *zRepo; /* The fingerprint of the repository */
3829 int rc; /* Result */
3830
3831 zCkout = db_text(0,"SELECT value FROM localdb.vvar WHERE name='fingerprint'");
3832 if( zCkout==0 ){
3833 /* This is an older checkout that does not record a fingerprint.
3834 ** We have to assume everything is ok */
3835 return 2;
3836 }
3837 zRepo = db_fingerprint(atoi(zCkout));
3838 rc = fossil_strcmp(zCkout,zRepo)==0;
3839 fossil_free(zCkout);
3840 fossil_free(zRepo);
3841 return rc;
3842 }
3843
+1 -1
--- src/undo.c
+++ src/undo.c
@@ -164,11 +164,11 @@
164164
}
165165
}
166166
ncid = db_lget_int("undo_checkout", 0);
167167
ucid = db_lget_int("checkout", 0);
168168
db_lset_int("undo_checkout", ucid);
169
- db_lset_int("checkout", ncid);
169
+ db_set_checkout(ncid);
170170
}
171171
172172
/*
173173
** Reset the undo memory.
174174
*/
175175
--- src/undo.c
+++ src/undo.c
@@ -164,11 +164,11 @@
164 }
165 }
166 ncid = db_lget_int("undo_checkout", 0);
167 ucid = db_lget_int("checkout", 0);
168 db_lset_int("undo_checkout", ucid);
169 db_lset_int("checkout", ncid);
170 }
171
172 /*
173 ** Reset the undo memory.
174 */
175
--- src/undo.c
+++ src/undo.c
@@ -164,11 +164,11 @@
164 }
165 }
166 ncid = db_lget_int("undo_checkout", 0);
167 ucid = db_lget_int("checkout", 0);
168 db_lset_int("undo_checkout", ucid);
169 db_set_checkout(ncid);
170 }
171
172 /*
173 ** Reset the undo memory.
174 */
175
+1 -1
--- src/update.c
+++ src/update.c
@@ -573,11 +573,11 @@
573573
if( g.argc<=3 ){
574574
/* All files updated. Shift the current checkout to the target. */
575575
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
576576
checkout_set_all_exe(tid);
577577
manifest_to_disk(tid);
578
- db_lset_int("checkout", tid);
578
+ db_set_checkout(tid);
579579
}else{
580580
/* A subset of files have been checked out. Keep the current
581581
** checkout unchanged. */
582582
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
583583
}
584584
--- src/update.c
+++ src/update.c
@@ -573,11 +573,11 @@
573 if( g.argc<=3 ){
574 /* All files updated. Shift the current checkout to the target. */
575 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
576 checkout_set_all_exe(tid);
577 manifest_to_disk(tid);
578 db_lset_int("checkout", tid);
579 }else{
580 /* A subset of files have been checked out. Keep the current
581 ** checkout unchanged. */
582 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
583 }
584
--- src/update.c
+++ src/update.c
@@ -573,11 +573,11 @@
573 if( g.argc<=3 ){
574 /* All files updated. Shift the current checkout to the target. */
575 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
576 checkout_set_all_exe(tid);
577 manifest_to_disk(tid);
578 db_set_checkout(tid);
579 }else{
580 /* A subset of files have been checked out. Keep the current
581 ** checkout unchanged. */
582 db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
583 }
584

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button