+ chunk_size = 0;
+}
+
+/* Read the next entry from the old statcache file, loading it into
+ * old_metadata. */
+void MetadataWriter::read_statcache()
+{
+ if (statcache_in == NULL) {
+ old_metadata_eof = true;
+ return;
+ }
+
+ old_metadata.clear();
+
+ char *buf = NULL;
+ size_t n = 0;
+ string field = ""; // Last field to be read in
+
+ /* Look for a first line starting with "@@", which tells where the metadata
+ * can be found in the metadata log of an old snapshot. */
+ if (getline(&buf, &n, statcache_in) < 0
+ || buf == NULL || buf[0] != '@' || buf[1] != '@') {
+ old_metadata_eof = true;
+ return;
+ }
+
+ if (strchr(buf, '\n') != NULL)
+ *strchr(buf, '\n') = '\0';
+ old_metadata_loc = buf + 2;
+
+ /* After the initial line follows the metadata, as key-value pairs. */
+ while (!feof(statcache_in)) {
+ if (getline(&buf, &n, statcache_in) < 0)
+ break;
+
+ char *eol = strchr(buf, '\n');
+ if (eol != NULL)
+ *eol = '\0';
+
+ /* Is the line blank? If so, we have reached the end of this entry. */
+ if (buf[0] == '\0')
+ break;
+
+ /* Is this a continuation line? (Does it start with whitespace?) */
+ if (isspace(buf[0]) && field != "") {
+ old_metadata[field] += string("\n") + buf;
+ continue;
+ }
+
+ /* For lines of the form "Key: Value" look for ':' and split the line
+ * apart. */
+ char *value = strchr(buf, ':');
+ if (value == NULL)
+ continue;
+ *value = '\0';
+ field = buf;
+
+ value++;
+ while (isspace(*value))
+ value++;
+
+ old_metadata[field] = value;
+ }
+
+ if (feof(statcache_in) && old_metadata.size() == 0) {
+ old_metadata_eof = true;
+ }
+
+ free(buf);
+}
+
+bool MetadataWriter::find(const string& path)
+{
+ const char *path_str = path.c_str();
+ while (!old_metadata_eof) {
+ string old_path = uri_decode(old_metadata["name"]);
+ int cmp = pathcmp(old_path.c_str(), path_str);
+ if (cmp == 0)
+ return true;
+ else if (cmp > 0)
+ return false;
+ else
+ read_statcache();
+ }
+
+ return false;
+}
+
+/* Does a file appear to be unchanged from the previous time it was backed up,
+ * based on stat information? */
+bool MetadataWriter::is_unchanged(const struct stat *stat_buf)
+{
+ if (old_metadata.find("volatile") != old_metadata.end()
+ && parse_int(old_metadata["volatile"]) != 0)
+ return false;
+
+ if (old_metadata.find("ctime") == old_metadata.end())
+ return false;
+ if (stat_buf->st_ctime != parse_int(old_metadata["ctime"]))
+ return false;
+
+ if (old_metadata.find("mtime") == old_metadata.end())
+ return false;
+ if (stat_buf->st_mtime != parse_int(old_metadata["mtime"]))
+ return false;
+
+ if (old_metadata.find("size") == old_metadata.end())
+ return false;
+ if (stat_buf->st_size != parse_int(old_metadata["size"]))
+ return false;
+
+ if (old_metadata.find("inode") == old_metadata.end())
+ return false;
+ string inode = encode_int(major(stat_buf->st_dev))
+ + "/" + encode_int(minor(stat_buf->st_dev))
+ + "/" + encode_int(stat_buf->st_ino);
+ if (inode != old_metadata["inode"])
+ return false;
+
+ return true;
+}
+
+list<ObjectReference> MetadataWriter::get_blocks()
+{
+ list<ObjectReference> blocks;
+
+ /* Parse the list of blocks. */
+ const char *s = old_metadata["data"].c_str();
+ while (*s != '\0') {
+ if (isspace(*s)) {
+ s++;
+ continue;
+ }
+
+ string ref = "";
+ while (*s != '\0' && !isspace(*s)) {
+ char buf[2];
+ buf[0] = *s;
+ buf[1] = '\0';
+ ref += buf;
+ s++;
+ }
+
+ ObjectReference r = ObjectReference::parse(ref);
+ if (!r.is_null())
+ blocks.push_back(r);
+ }
+
+ return blocks;