Only trust the results of a stat if two separate backups agree.
authorMichael Vrable <mvrable@cs.ucsd.edu>
Thu, 28 Jun 2007 20:30:23 +0000 (13:30 -0700)
committerMichael Vrable <mvrable@turin.ucsd.edu>
Thu, 28 Jun 2007 20:30:23 +0000 (13:30 -0700)
This should eliminate races where stat information might not change if
there are two changes to a file within the same second--as long as two
backups are not taken less than a second apart.

statcache.cc
statcache.h

index a0bee44..db07792 100644 (file)
@@ -117,13 +117,14 @@ void StatCache::ReadNext()
     std::istream &cache = *oldcache;
     map<string, string> fields;
 
+    old_is_validated = false;
     old_mtime = -1;
     old_ctime = -1;
     old_inode = -1;
     old_checksum = "";
     old_contents.clear();
 
-    /* First, read in the filename. */
+    /* First, read in the filename.  TODO: Unescaping. */
     getline(cache, old_name);
     if (!cache) {
         end_of_cache = true;
@@ -162,6 +163,8 @@ void StatCache::ReadNext()
     }
 
     /* Parse the easy fields: mtime, ctime, inode, checksum, ... */
+    if (fields.count("validated"))
+        old_is_validated = true;
     if (fields.count("mtime"))
         old_mtime = parse_int(fields["mtime"]);
     if (fields.count("ctime"))
@@ -211,6 +214,10 @@ bool StatCache::Find(const string &path, const struct stat *stat_buf)
     if (old_name != path)
         return false;
 
+    /* Do we trust cached stat information? */
+    if (!old_is_validated)
+        return false;
+
     /* Check to see if the file is unchanged. */
     if (stat_buf->st_mtime != old_mtime)
         return false;
@@ -227,6 +234,22 @@ bool StatCache::Find(const string &path, const struct stat *stat_buf)
 void StatCache::Save(const string &path, struct stat *stat_buf,
                      const string &checksum, const list<string> &blocks)
 {
+    /* Was this file in the old stat cache, and is the information unchanged?
+     * If so, mark the information "validated", which means we are confident
+     * that we can use it to accurately detect changes.  (Stat information may
+     * not be updated if, for example, there are two writes within a single
+     * second and we happen to make the first stat call between them.  However,
+     * if two stat calls separated in time agree, then we will trust the
+     * values.) */
+    bool validated = false;
+    if (!end_of_cache && path == old_name) {
+        if (stat_buf->st_mtime == old_mtime
+            && stat_buf->st_ctime == old_ctime
+            && (long long)stat_buf->st_ino == old_inode
+            && old_checksum == checksum)
+            validated = true;
+    }
+
     *newcache << uri_encode(path) << "\n";
     *newcache << "mtime: " << encode_int(stat_buf->st_mtime) << "\n"
               << "ctime: " << encode_int(stat_buf->st_ctime) << "\n"
@@ -241,5 +264,8 @@ void StatCache::Save(const string &path, struct stat *stat_buf,
         *newcache << " " << *i << "\n";
     }
 
+    if (validated)
+        *newcache << "validated: true\n";
+
     *newcache << "\n";
 }
index 8b13b7e..e0e2fbb 100644 (file)
@@ -55,6 +55,7 @@ private:
 
     /* Information about one file read from the old cache. */
     bool end_of_cache;
+    bool old_is_validated;
     int64_t old_mtime, old_ctime, old_inode;
     std::string old_name, old_checksum;
     std::list<ObjectReference> old_contents;