From 80f96acfb98a06327a66bc97c81bdc190103340b Mon Sep 17 00:00:00 2001 From: Michael Vrable Date: Thu, 28 Jun 2007 13:30:23 -0700 Subject: [PATCH] Only trust the results of a stat if two separate backups agree. 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 | 28 +++++++++++++++++++++++++++- statcache.h | 1 + 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/statcache.cc b/statcache.cc index a0bee44..db07792 100644 --- a/statcache.cc +++ b/statcache.cc @@ -117,13 +117,14 @@ void StatCache::ReadNext() std::istream &cache = *oldcache; map 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 &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"; } diff --git a/statcache.h b/statcache.h index 8b13b7e..e0e2fbb 100644 --- a/statcache.h +++ b/statcache.h @@ -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 old_contents; -- 2.20.1