#
#
# patch "ChangeLog"
# from [9201a20f06e7485e50b7f2355c57ca3b8e46fda5]
# to [293183871c3cbe2c818712f198d2df2c0428f68b]
#
# patch "unix/inodeprint.cc"
# from [97d9fd62fa12374cde1be2c31269793730532084]
# to [69978997d67e2cc2e60e4fc2c1ba0a6179348525]
#
============================================================
--- ChangeLog 9201a20f06e7485e50b7f2355c57ca3b8e46fda5
+++ ChangeLog 293183871c3cbe2c818712f198d2df2c0428f68b
@@ -1,3 +1,10 @@
+2006-07-06 Nathaniel Smith
+
+ * unix/inodeprint.cc (inodeprint_file): Add some clever logic
+ inspired by Martin Pool, to be somewhat more robust against clock
+ issues. This should be ported to win32, but I don't know how; I
+ also have no earthly idea how to test this new code.
+
2006-07-05 Nathaniel Smith
* tests/checking_a_few_command_specific_options/__driver__.lua
============================================================
--- unix/inodeprint.cc 97d9fd62fa12374cde1be2c31269793730532084
+++ unix/inodeprint.cc 69978997d67e2cc2e60e4fc2c1ba0a6179348525
@@ -1,9 +1,10 @@
// copyright (C) 2005 nathaniel smith
// all rights reserved.
// licensed to the public under the terms of the GNU GPL (>= 2)
// see the file COPYING for details
#include
+#include
#include "botan/botan.h"
#include "botan/sha160.h"
@@ -29,15 +30,61 @@
}
};
+// To make this more robust, there are some tricks:
+// -- we refuse to inodeprint files whose times are within a few seconds of
+// 'now'. This is because, we might memorize the inodeprint, then
+// someone writes to the file, and this write does not update the
+// timestamp -- or rather, it does update the timestamp, but nothing
+// happens, because the new value is the same as the old value. We use
+// "a few seconds" to make sure that it is larger than whatever the
+// filesystem's timekeeping granularity is (rounding to 2 seconds is
+// known to exist in the wild).
+// -- by the same reasoning, we should also refuse to inodeprint files whose
+// time is in the future, because it is possible that someone will write
+// to that file exactly when that future second arrives, and we will
+// never notice. However, this would create persistent and hard to
+// diagnosis slowdowns, whenever a tree accidentally had its times set
+// into the future. Therefore, to handle this case, we include a "is
+// this time in the future?" bit in the hashed information. This bit
+// will change when we pass the future point, and trigger a re-check of
+// the file's contents.
+//
+// This is, of course, still not perfect. There is no way to make our stat
+// atomic with the actual read of the file, so there's always a race condition
+// there. Additionally, this handling means that checkout will never actually
+// inodeprint anything, but rather the first command after checkout will be
+// slow. There doesn't seem to be anything that could be done about this.
+
+inline bool should_abort(time_t now, time_t then)
+{
+ if (now < 0 || then < 0)
+ return false;
+ double difference = difftime(now, then);
+ return (difference >= -3 && difference <= 3);
+}
+
+inline bool is_future(time_t now, time_t then)
+{
+ if (now < 0 || then < 0)
+ return false;
+ return difftime(now, then) > 0;
+}
+
bool inodeprint_file(file_path const & file, hexenc & ip)
{
struct stat st;
if (stat(file.as_external().c_str(), &st) < 0)
return false;
+ time_t now;
+ time(&now);
+
Botan::SHA_160 hash;
+ if (should_abort(now, st.st_ctime))
+ return false;
add_hash(hash, st.st_ctime);
+ add_hash(hash, is_future(now, st.st_ctime));
// aah, portability.
#ifdef HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC
@@ -50,7 +97,10 @@
add_hash(hash, (long)0);
#endif
+ if (should_abort(now, st.st_mtime))
+ return false;
add_hash(hash, st.st_mtime);
+ add_hash(hash, is_future(now, st.st_mtime));
#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
add_hash(hash, st.st_mtim.tv_nsec);