Fossil

Check-in [1b114d24]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment: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.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 1b114d241ffa496d7ea1d60d6f2bed66fed4d1f0873ea95186731dd015fa334e
User & Date: drh 2019-01-16 00:11:42
Context
2019-01-16
00:14
For the "file info NAME" command, if NAME is directory, give a sensible error message. check-in: 0ee8c8cd user: drh tags: trunk
00:11
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. check-in: 1b114d24 user: drh tags: trunk
2019-01-15
19:41
Give a fatal error, not a panic, if unable to open the database file. check-in: c0eca1f9 user: drh tags: trunk
2019-01-11
03:31
Verify the repository fingerprint whenever a repository is opened from a checkout database. For now, abort with an error if the fingerprint is incorrect. To do: have Fossil automatically adjust RIDs in the checkout database if the fingerprint is incorrect. Closed-Leaf check-in: 6036bc62 user: drh tags: repo-fingerprint
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/checkin.c.

  2537   2537       "DELETE FROM vfile WHERE (vid!=%d OR deleted) AND is_selected(id);"
  2538   2538       "DELETE FROM vmerge;"
  2539   2539       "UPDATE vfile SET vid=%d;"
  2540   2540       "UPDATE vfile SET rid=mrid, chnged=0, deleted=0, origname=NULL"
  2541   2541       " WHERE is_selected(id);"
  2542   2542       , vid, nvid
  2543   2543     );
  2544         -  db_lset_int("checkout", nvid);
         2544  +  db_set_checkout(nvid);
  2545   2545   
  2546   2546     /* Update the isexe and islink columns of the vfile table */
  2547   2547     db_prepare(&q,
  2548   2548       "UPDATE vfile SET isexe=:exec, islink=:link"
  2549   2549       " WHERE vid=:vid AND pathname=:path AND (isexe!=:exec OR islink!=:link)"
  2550   2550     );
  2551   2551     db_bind_int(&q, ":vid", nvid);

Changes to src/checkout.c.

   302    302     db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
   303    303     if( !keepFlag ){
   304    304       vfile_to_disk(vid, 0, !g.fQuiet, promptFlag);
   305    305     }
   306    306     checkout_set_all_exe(vid);
   307    307     manifest_to_disk(vid);
   308    308     ensure_empty_dirs_created();
   309         -  db_lset_int("checkout", vid);
          309  +  db_set_checkout(vid);
   310    310     undo_reset();
   311    311     db_multi_exec("DELETE FROM vmerge");
   312    312     if( !keepFlag && db_get_boolean("repo-cksum",1) ){
   313    313       vfile_aggregate_checksum_manifest(vid, &cksum1, &cksum1b);
   314    314       vfile_aggregate_checksum_disk(vid, &cksum2);
   315    315       if( blob_compare(&cksum1, &cksum2) ){
   316    316         fossil_print("WARNING: manifest checksum does not agree with disk\n");

Changes to src/db.c.

  1653   1653       db_set_int("hash-policy", g.eHashPolicy, 0);
  1654   1654     }
  1655   1655   
  1656   1656     /* Make a change to the CHECK constraint on the BLOB table for
  1657   1657     ** version 2.0 and later.
  1658   1658     */
  1659   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  +  }
  1660   1679   }
  1661   1680   
  1662   1681   /*
  1663   1682   ** Return true if there have been any changes to the repository
  1664   1683   ** database since it was opened.
  1665   1684   **
  1666   1685   ** Changes to "config" and "localdb" and "temp" do not count.
................................................................................
  2870   2889       ** point, this will probably be the setting value from the
  2871   2890       ** repository or global configuration databases. */
  2872   2891       g.allowSymlinks = db_get_boolean("allow-symlinks",
  2873   2892                                        db_allow_symlinks_by_default());
  2874   2893     }
  2875   2894     db_lset("repository", g.argv[2]);
  2876   2895     db_record_repository_filename(g.argv[2]);
  2877         -  db_lset_int("checkout", 0);
         2896  +  db_set_checkout(0);
  2878   2897     azNewArgv[0] = g.argv[0];
  2879   2898     g.argv = azNewArgv;
  2880   2899     if( !emptyFlag ){
  2881   2900       g.argc = 3;
  2882   2901       if( g.zOpenRevision ){
  2883   2902         azNewArgv[g.argc-1] = g.zOpenRevision;
  2884   2903       }else{
................................................................................
  3702   3721   */
  3703   3722   void test_database_name_cmd(void){
  3704   3723     db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
  3705   3724     fossil_print("Repository database: %s\n", g.zRepositoryName);
  3706   3725     fossil_print("Local database:      %s\n", g.zLocalDbName);
  3707   3726     fossil_print("Config database:     %s\n", g.zConfigDbName);
  3708   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  +}

Changes to src/undo.c.

   162    162           "INSERT OR IGNORE INTO stashfile SELECT * FROM undo_stashfile;"
   163    163         );
   164    164       }
   165    165     }
   166    166     ncid = db_lget_int("undo_checkout", 0);
   167    167     ucid = db_lget_int("checkout", 0);
   168    168     db_lset_int("undo_checkout", ucid);
   169         -  db_lset_int("checkout", ncid);
          169  +  db_set_checkout(ncid);
   170    170   }
   171    171   
   172    172   /*
   173    173   ** Reset the undo memory.
   174    174   */
   175    175   void undo_reset(void){
   176    176     static const char zSql[] =

Changes to src/update.c.

   571    571     }else{
   572    572       ensure_empty_dirs_created();
   573    573       if( g.argc<=3 ){
   574    574         /* All files updated.  Shift the current checkout to the target. */
   575    575         db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
   576    576         checkout_set_all_exe(tid);
   577    577         manifest_to_disk(tid);
   578         -      db_lset_int("checkout", tid);
          578  +      db_set_checkout(tid);
   579    579       }else{
   580    580         /* A subset of files have been checked out.  Keep the current
   581    581         ** checkout unchanged. */
   582    582         db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
   583    583       }
   584    584       if( !internalUpdate ) undo_finish();
   585    585       if( setmtimeFlag ) vfile_check_signature(tid, CKSIG_SETMTIME);