Commit 0f4b2f028ab3a70b76b9bd67f073747df7078559

Edward Thomson 2018-07-01T15:13:50

reader: optionally validate index matches workdir When using a workdir reader, optionally validate that the index contents match the working directory contents.

diff --git a/src/apply.c b/src/apply.c
index 0810c02..d4c7dd0 100644
--- a/src/apply.c
+++ b/src/apply.c
@@ -661,7 +661,7 @@ int git_apply(
 	 * in the index.
 	 */
 	if (opts.location == GIT_APPLY_LOCATION_WORKDIR)
-		error = git_reader_for_workdir(&pre_reader, repo);
+		error = git_reader_for_workdir(&pre_reader, repo, false);
 	else
 		error = git_reader_for_index(&pre_reader, repo, NULL);
 
diff --git a/src/reader.c b/src/reader.c
index 32900df..94faff7 100644
--- a/src/reader.c
+++ b/src/reader.c
@@ -74,6 +74,7 @@ int git_reader_for_tree(git_reader **out, git_tree *tree)
 typedef struct {
 	git_reader reader;
 	git_repository *repo;
+	git_index *index;
 } workdir_reader;
 
 static int workdir_reader_read(
@@ -84,6 +85,8 @@ static int workdir_reader_read(
 {
 	workdir_reader *reader = (workdir_reader *)_reader;
 	git_buf path = GIT_BUF_INIT;
+	const git_index_entry *idx_entry;
+	git_oid id;
 	int error;
 
 	if ((error = git_buf_joinpath(&path,
@@ -94,8 +97,21 @@ static int workdir_reader_read(
 	if ((error = git_futils_readbuffer(out, path.ptr)) < 0)
 		goto done;
 
+	if (out_id || reader->index) {
+		if ((error = git_odb_hash(&id, out->ptr, out->size, GIT_OBJ_BLOB)) < 0)
+			goto done;
+	}
+
+	if (reader->index) {
+		if (!(idx_entry = git_index_get_bypath(reader->index, filename, 0)) ||
+		    !git_oid_equal(&id, &idx_entry->id)) {
+			error = GIT_READER_MISMATCH;
+			goto done;
+		}
+	}
+
 	if (out_id)
-		error = git_odb_hash(out_id, out->ptr, out->size, GIT_OBJ_BLOB);
+		git_oid_cpy(out_id, &id);
 
 done:
 	git_buf_dispose(&path);
@@ -107,9 +123,13 @@ static void workdir_reader_free(git_reader *_reader)
 	GIT_UNUSED(_reader);
 }
 
-int git_reader_for_workdir(git_reader **out, git_repository *repo)
+int git_reader_for_workdir(
+	git_reader **out,
+	git_repository *repo,
+	bool validate_index)
 {
 	workdir_reader *reader;
+	int error;
 
 	assert(out && repo);
 
@@ -120,6 +140,12 @@ int git_reader_for_workdir(git_reader **out, git_repository *repo)
 	reader->reader.free = workdir_reader_free;
 	reader->repo = repo;
 
+	if (validate_index &&
+	    (error = git_repository_index__weakptr(&reader->index, repo)) < 0) {
+		git__free(reader);
+		return error;
+	}
+
 	*out = (git_reader *)reader;
 	return 0;
 }
@@ -183,13 +209,9 @@ int git_reader_for_index(
 
 	if (index) {
 		reader->index = index;
-	} else {
-		error = git_repository_index__weakptr(&reader->index, repo);
-
-		if (error < 0) {
-			git__free(reader);
-			return error;
-		}
+	} else if ((error = git_repository_index__weakptr(&reader->index, repo)) < 0) {
+		git__free(reader);
+		return error;
 	}
 
 	*out = (git_reader *)reader;
diff --git a/src/reader.h b/src/reader.h
index 7bb60e1..e1d9068 100644
--- a/src/reader.h
+++ b/src/reader.h
@@ -9,6 +9,9 @@
 
 #include "common.h"
 
+/* Returned when the workdir does not match the index */
+#define GIT_READER_MISMATCH	1
+
 typedef struct git_reader git_reader;
 
 /*
@@ -76,7 +79,8 @@ extern int git_reader_for_index(
  */
 extern int git_reader_for_workdir(
 	git_reader **out,
-	git_repository *repo);
+	git_repository *repo,
+	bool validate_index);
 
 /**
  * Read the given filename from the reader and populate the given buffer