Commit eb5977991a75f8d1630348a529805ba40765a616

Carlos Martín Nieto 2015-10-29T21:12:37

filebuf: use a checksum to detect file changes Instead of relying on the size and timestamp, which can hide changes performed in the same second, hash the file content's when we care about detecting changes.

diff --git a/src/config_file.c b/src/config_file.c
index 46f21c0..5f5e309 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -77,8 +77,7 @@ typedef struct git_config_file_iter {
 		 (iter) = (tmp))
 
 struct reader {
-	time_t file_mtime;
-	size_t file_size;
+	git_oid checksum;
 	char *file_path;
 	git_buf buffer;
 	char *read_ptr;
@@ -285,7 +284,7 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
 
 	git_buf_init(&reader->buffer, 0);
 	res = git_futils_readbuffer_updated(
-		&reader->buffer, b->file_path, &reader->file_mtime, &reader->file_size, NULL);
+		&reader->buffer, b->file_path, &reader->checksum, NULL);
 
 	/* It's fine if the file doesn't exist */
 	if (res == GIT_ENOTFOUND)
@@ -345,7 +344,7 @@ static int config_refresh(git_config_backend *cfg)
 		reader = git_array_get(b->readers, i);
 		error = git_futils_readbuffer_updated(
 			&reader->buffer, reader->file_path,
-			&reader->file_mtime, &reader->file_size, &updated);
+			&reader->checksum, &updated);
 
 		if (error < 0 && error != GIT_ENOTFOUND)
 			return error;
@@ -1618,7 +1617,7 @@ static int read_on_variable(
 		git_buf_init(&r->buffer, 0);
 
 		result = git_futils_readbuffer_updated(
-			&r->buffer, r->file_path, &r->file_mtime, &r->file_size, NULL);
+			&r->buffer, r->file_path, &r->checksum, NULL);
 
 		if (result == 0) {
 			result = config_read(parse_data->values, parse_data->cfg_file, r, parse_data->level, parse_data->depth+1);
@@ -1894,7 +1893,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
 	} else {
 		/* Lock the file */
 		if ((result = git_filebuf_open(
-			     &file, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) {
+			     &file, cfg->file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) {
 			git_buf_free(&reader->buffer);
 			return result;
 		}
@@ -1945,10 +1944,6 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
 		git_buf_attach(&cfg->locked_content, git_buf_detach(&buf), len);
 	} else {
 		git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf));
-
-		/* refresh stats - if this errors, then commit will error too */
-		(void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file);
-
 		result = git_filebuf_commit(&file);
 	}
 
diff --git a/src/fileops.c b/src/fileops.c
index 57d2ce9..9bef028 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -153,11 +153,12 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
 }
 
 int git_futils_readbuffer_updated(
-	git_buf *buf, const char *path, time_t *mtime, size_t *size, int *updated)
+	git_buf *buf, const char *path, git_oid *checksum, int *updated)
 {
+	int error;
 	git_file fd;
 	struct stat st;
-	bool changed = false;
+	git_oid checksum_new;
 
 	assert(buf && path && *path);
 
@@ -178,26 +179,6 @@ int git_futils_readbuffer_updated(
 		return -1;
 	}
 
-	/*
-	 * If we were given a time and/or a size, we only want to read the file
-	 * if it has been modified.
-	 */
-	if (size && *size != (size_t)st.st_size)
-		changed = true;
-	if (mtime && *mtime != (time_t)st.st_mtime)
-		changed = true;
-	if (!size && !mtime)
-		changed = true;
-
-	if (!changed) {
-		return 0;
-	}
-
-	if (mtime != NULL)
-		*mtime = st.st_mtime;
-	if (size != NULL)
-		*size = (size_t)st.st_size;
-
 	if ((fd = git_futils_open_ro(path)) < 0)
 		return fd;
 
@@ -208,6 +189,28 @@ int git_futils_readbuffer_updated(
 
 	p_close(fd);
 
+	if ((error = git_hash_buf(&checksum_new, buf->ptr, buf->size)) < 0) {
+		git_buf_free(buf);
+		return error;
+	}
+
+	/*
+	 * If we were given a checksum, we only want to use it if it's different
+	 */
+	if (checksum && !git_oid__cmp(checksum, &checksum_new)) {
+		git_buf_free(buf);
+		if (updated)
+			*updated = 0;
+
+		return 0;
+	}
+
+	/*
+	 * If we're here, the file did change, or the user didn't have an old version
+	 */
+	if (checksum)
+		git_oid_cpy(checksum, &checksum_new);
+
 	if (updated != NULL)
 		*updated = 1;
 
@@ -216,7 +219,7 @@ int git_futils_readbuffer_updated(
 
 int git_futils_readbuffer(git_buf *buf, const char *path)
 {
-	return git_futils_readbuffer_updated(buf, path, NULL, NULL, NULL);
+	return git_futils_readbuffer_updated(buf, path, NULL, NULL);
 }
 
 int git_futils_writebuffer(
diff --git a/src/fileops.h b/src/fileops.h
index 572ff01..c6db195 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -13,6 +13,7 @@
 #include "path.h"
 #include "pool.h"
 #include "strmap.h"
+#include "oid.h"
 
 /**
  * Filebuffer methods
@@ -21,7 +22,7 @@
  */
 extern int git_futils_readbuffer(git_buf *obj, const char *path);
 extern int git_futils_readbuffer_updated(
-	git_buf *obj, const char *path, time_t *mtime, size_t *size, int *updated);
+	git_buf *obj, const char *path, git_oid *checksum, int *updated);
 extern int git_futils_readbuffer_fd(git_buf *obj, git_file fd, size_t len);
 
 extern int git_futils_writebuffer(