Fossil SCM
Modify the check-in command to detect clock skew and abort if found.
Commit
8fdac87b688c3ea0cecc970364632b8ba254004b
Parent
ff9efe302608321…
1 file changed
+25
+25
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -432,10 +432,32 @@ | ||
| 432 | 432 | @ WHERE tagid=%d AND rid=plink.cid), 'trunk') |
| 433 | 433 | ; |
| 434 | 434 | rc = db_int(0, zSql, rid, TAG_BRANCH, TAG_BRANCH); |
| 435 | 435 | return rc==0; |
| 436 | 436 | } |
| 437 | + | |
| 438 | +/* | |
| 439 | +** Make sure the current check-in with timestamp zDate is younger than its | |
| 440 | +** ancestor identified rid and zUuid. Throw a fatal error if not. | |
| 441 | +*/ | |
| 442 | +static void checkin_verify_younger( | |
| 443 | + int rid, /* The record ID of the ancestor */ | |
| 444 | + const char *zUuid, /* The artifact ID of the ancestor */ | |
| 445 | + const char *zDate /* Date & time of the current check-in */ | |
| 446 | +){ | |
| 447 | + int b; | |
| 448 | + b = db_exists( | |
| 449 | + "SELECT 1 FROM event" | |
| 450 | + " WHERE datetime(mtime)>=%Q" | |
| 451 | + " AND type='ci' AND objid=%d", | |
| 452 | + zDate, rid | |
| 453 | + ); | |
| 454 | + if( b ){ | |
| 455 | + fossil_fatal("ancestor check-in [%.10s] (%s) is younger (clock skew?)", | |
| 456 | + zUuid, zDate); | |
| 457 | + } | |
| 458 | +} | |
| 437 | 459 | |
| 438 | 460 | /* |
| 439 | 461 | ** COMMAND: ci |
| 440 | 462 | ** COMMAND: commit |
| 441 | 463 | ** |
| @@ -661,10 +683,11 @@ | ||
| 661 | 683 | } |
| 662 | 684 | blob_appendf(&manifest, "C %F\n", blob_str(&comment)); |
| 663 | 685 | zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now"); |
| 664 | 686 | zDate[10] = 'T'; |
| 665 | 687 | blob_appendf(&manifest, "D %s\n", zDate); |
| 688 | + zDate[10] = ' '; | |
| 666 | 689 | db_prepare(&q, |
| 667 | 690 | "SELECT pathname, uuid, origname, blob.rid" |
| 668 | 691 | " FROM vfile JOIN blob ON vfile.mrid=blob.rid" |
| 669 | 692 | " WHERE NOT deleted AND vfile.vid=%d" |
| 670 | 693 | " ORDER BY 1", vid); |
| @@ -694,19 +717,21 @@ | ||
| 694 | 717 | } |
| 695 | 718 | blob_reset(&filename); |
| 696 | 719 | db_finalize(&q); |
| 697 | 720 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); |
| 698 | 721 | blob_appendf(&manifest, "P %s", zUuid); |
| 722 | + checkin_verify_younger(vid, zUuid, zDate); | |
| 699 | 723 | |
| 700 | 724 | db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id"); |
| 701 | 725 | db_bind_int(&q2, ":id", 0); |
| 702 | 726 | while( db_step(&q2)==SQLITE_ROW ){ |
| 703 | 727 | int mid = db_column_int(&q2, 0); |
| 704 | 728 | if( !g.markPrivate && content_is_private(mid) ) continue; |
| 705 | 729 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid); |
| 706 | 730 | if( zUuid ){ |
| 707 | 731 | blob_appendf(&manifest, " %s", zUuid); |
| 732 | + checkin_verify_younger(mid, zUuid, zDate); | |
| 708 | 733 | free(zUuid); |
| 709 | 734 | } |
| 710 | 735 | } |
| 711 | 736 | db_reset(&q2); |
| 712 | 737 | |
| 713 | 738 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -432,10 +432,32 @@ | |
| 432 | @ WHERE tagid=%d AND rid=plink.cid), 'trunk') |
| 433 | ; |
| 434 | rc = db_int(0, zSql, rid, TAG_BRANCH, TAG_BRANCH); |
| 435 | return rc==0; |
| 436 | } |
| 437 | |
| 438 | /* |
| 439 | ** COMMAND: ci |
| 440 | ** COMMAND: commit |
| 441 | ** |
| @@ -661,10 +683,11 @@ | |
| 661 | } |
| 662 | blob_appendf(&manifest, "C %F\n", blob_str(&comment)); |
| 663 | zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now"); |
| 664 | zDate[10] = 'T'; |
| 665 | blob_appendf(&manifest, "D %s\n", zDate); |
| 666 | db_prepare(&q, |
| 667 | "SELECT pathname, uuid, origname, blob.rid" |
| 668 | " FROM vfile JOIN blob ON vfile.mrid=blob.rid" |
| 669 | " WHERE NOT deleted AND vfile.vid=%d" |
| 670 | " ORDER BY 1", vid); |
| @@ -694,19 +717,21 @@ | |
| 694 | } |
| 695 | blob_reset(&filename); |
| 696 | db_finalize(&q); |
| 697 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); |
| 698 | blob_appendf(&manifest, "P %s", zUuid); |
| 699 | |
| 700 | db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id"); |
| 701 | db_bind_int(&q2, ":id", 0); |
| 702 | while( db_step(&q2)==SQLITE_ROW ){ |
| 703 | int mid = db_column_int(&q2, 0); |
| 704 | if( !g.markPrivate && content_is_private(mid) ) continue; |
| 705 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid); |
| 706 | if( zUuid ){ |
| 707 | blob_appendf(&manifest, " %s", zUuid); |
| 708 | free(zUuid); |
| 709 | } |
| 710 | } |
| 711 | db_reset(&q2); |
| 712 | |
| 713 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -432,10 +432,32 @@ | |
| 432 | @ WHERE tagid=%d AND rid=plink.cid), 'trunk') |
| 433 | ; |
| 434 | rc = db_int(0, zSql, rid, TAG_BRANCH, TAG_BRANCH); |
| 435 | return rc==0; |
| 436 | } |
| 437 | |
| 438 | /* |
| 439 | ** Make sure the current check-in with timestamp zDate is younger than its |
| 440 | ** ancestor identified rid and zUuid. Throw a fatal error if not. |
| 441 | */ |
| 442 | static void checkin_verify_younger( |
| 443 | int rid, /* The record ID of the ancestor */ |
| 444 | const char *zUuid, /* The artifact ID of the ancestor */ |
| 445 | const char *zDate /* Date & time of the current check-in */ |
| 446 | ){ |
| 447 | int b; |
| 448 | b = db_exists( |
| 449 | "SELECT 1 FROM event" |
| 450 | " WHERE datetime(mtime)>=%Q" |
| 451 | " AND type='ci' AND objid=%d", |
| 452 | zDate, rid |
| 453 | ); |
| 454 | if( b ){ |
| 455 | fossil_fatal("ancestor check-in [%.10s] (%s) is younger (clock skew?)", |
| 456 | zUuid, zDate); |
| 457 | } |
| 458 | } |
| 459 | |
| 460 | /* |
| 461 | ** COMMAND: ci |
| 462 | ** COMMAND: commit |
| 463 | ** |
| @@ -661,10 +683,11 @@ | |
| 683 | } |
| 684 | blob_appendf(&manifest, "C %F\n", blob_str(&comment)); |
| 685 | zDate = db_text(0, "SELECT datetime('%q')", zDateOvrd ? zDateOvrd : "now"); |
| 686 | zDate[10] = 'T'; |
| 687 | blob_appendf(&manifest, "D %s\n", zDate); |
| 688 | zDate[10] = ' '; |
| 689 | db_prepare(&q, |
| 690 | "SELECT pathname, uuid, origname, blob.rid" |
| 691 | " FROM vfile JOIN blob ON vfile.mrid=blob.rid" |
| 692 | " WHERE NOT deleted AND vfile.vid=%d" |
| 693 | " ORDER BY 1", vid); |
| @@ -694,19 +717,21 @@ | |
| 717 | } |
| 718 | blob_reset(&filename); |
| 719 | db_finalize(&q); |
| 720 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", vid); |
| 721 | blob_appendf(&manifest, "P %s", zUuid); |
| 722 | checkin_verify_younger(vid, zUuid, zDate); |
| 723 | |
| 724 | db_prepare(&q2, "SELECT merge FROM vmerge WHERE id=:id"); |
| 725 | db_bind_int(&q2, ":id", 0); |
| 726 | while( db_step(&q2)==SQLITE_ROW ){ |
| 727 | int mid = db_column_int(&q2, 0); |
| 728 | if( !g.markPrivate && content_is_private(mid) ) continue; |
| 729 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", mid); |
| 730 | if( zUuid ){ |
| 731 | blob_appendf(&manifest, " %s", zUuid); |
| 732 | checkin_verify_younger(mid, zUuid, zDate); |
| 733 | free(zUuid); |
| 734 | } |
| 735 | } |
| 736 | db_reset(&q2); |
| 737 | |
| 738 |