Commit 6cc14ae3a76586722526eb6cca784b4386bfb2a1

Edward Thomson 2018-10-20T20:22:04

Merge pull request #4840 from libgit2/cmn/validity-tree-from-unowned-index Check object existence when creating a tree from an index

diff --git a/src/tree.c b/src/tree.c
index e4a0547..d628aeb 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -456,6 +456,36 @@ static size_t find_next_dir(const char *dirname, git_index *index, size_t start)
 	return i;
 }
 
+static git_otype otype_from_mode(git_filemode_t filemode)
+{
+	switch (filemode) {
+	case GIT_FILEMODE_TREE:
+		return GIT_OBJ_TREE;
+	case GIT_FILEMODE_COMMIT:
+		return GIT_OBJ_COMMIT;
+	default:
+		return GIT_OBJ_BLOB;
+	}
+}
+
+static int check_entry(git_repository *repo, const char *filename, const git_oid *id, git_filemode_t filemode)
+{
+	if (!valid_filemode(filemode))
+		return tree_error("failed to insert entry: invalid filemode for file", filename);
+
+	if (!valid_entry_name(repo, filename))
+		return tree_error("failed to insert entry: invalid name for a tree entry", filename);
+
+	if (git_oid_iszero(id))
+		return tree_error("failed to insert entry: invalid null OID", filename);
+
+	if (filemode != GIT_FILEMODE_COMMIT &&
+	    !git_object__is_valid(repo, id, otype_from_mode(filemode)))
+		return tree_error("failed to insert entry: invalid object specified", filename);
+
+	return 0;
+}
+
 static int append_entry(
 	git_treebuilder *bld,
 	const char *filename,
@@ -466,11 +496,8 @@ static int append_entry(
 	git_tree_entry *entry;
 	int error = 0;
 
-	if (validate && !valid_entry_name(bld->repo, filename))
-		return tree_error("failed to insert entry: invalid name for a tree entry", filename);
-
-	if (validate && git_oid_iszero(id))
-		return tree_error("failed to insert entry: invalid null OID for a tree entry", filename);
+	if (validate && ((error = check_entry(bld->repo, filename, id, filemode)) < 0))
+		return error;
 
 	entry = alloc_entry(filename, strlen(filename), id);
 	GITERR_CHECK_ALLOC(entry);
@@ -684,18 +711,6 @@ on_error:
 	return -1;
 }
 
-static git_otype otype_from_mode(git_filemode_t filemode)
-{
-	switch (filemode) {
-	case GIT_FILEMODE_TREE:
-		return GIT_OBJ_TREE;
-	case GIT_FILEMODE_COMMIT:
-		return GIT_OBJ_COMMIT;
-	default:
-		return GIT_OBJ_BLOB;
-	}
-}
-
 int git_treebuilder_insert(
 	const git_tree_entry **entry_out,
 	git_treebuilder *bld,
@@ -709,18 +724,8 @@ int git_treebuilder_insert(
 
 	assert(bld && id && filename);
 
-	if (!valid_filemode(filemode))
-		return tree_error("failed to insert entry: invalid filemode for file", filename);
-
-	if (!valid_entry_name(bld->repo, filename))
-		return tree_error("failed to insert entry: invalid name for a tree entry", filename);
-
-	if (git_oid_iszero(id))
-		return tree_error("failed to insert entry: invalid null OID", filename);
-
-	if (filemode != GIT_FILEMODE_COMMIT &&
-	    !git_object__is_valid(bld->repo, id, otype_from_mode(filemode)))
-		return tree_error("failed to insert entry: invalid object specified", filename);
+	if ((error = check_entry(bld->repo, filename, id, filemode)) < 0)
+		return error;
 
 	pos = git_strmap_lookup_index(bld->map, filename);
 	if (git_strmap_valid_index(bld->map, pos)) {
diff --git a/tests/index/tests.c b/tests/index/tests.c
index 3605ac9..a208e2c 100644
--- a/tests/index/tests.c
+++ b/tests/index/tests.c
@@ -639,6 +639,30 @@ static void write_invalid_filename(git_repository *repo, const char *fn_orig)
 	git__free(fn);
 }
 
+void test_index_tests__write_tree_invalid_unowned_index(void)
+{
+	git_index *idx;
+	git_repository *repo;
+	git_index_entry entry = {{0}};
+	git_oid tree_id;
+
+	cl_git_pass(git_index_new(&idx));
+
+	cl_git_pass(git_oid_fromstr(&entry.id, "8312e0a89a9cbab77c732b6bc39b51a783e3a318"));
+	entry.path = "foo";
+	entry.mode = GIT_FILEMODE_BLOB;
+	cl_git_pass(git_index_add(idx, &entry));
+
+	cl_git_pass(git_repository_init(&repo, "./invalid-id", 0));
+
+	cl_git_fail(git_index_write_tree_to(&tree_id, idx, repo));
+
+	git_index_free(idx);
+	git_repository_free(repo);
+
+	cl_fixture_cleanup("invalid-id");
+}
+
 /* Test that writing an invalid filename fails */
 void test_index_tests__write_invalid_filename(void)
 {
diff --git a/tests/resources/testrepo/.gitted/index b/tests/resources/testrepo/.gitted/index
index a27fb9c..963f7ad 100644
Binary files a/tests/resources/testrepo/.gitted/index and b/tests/resources/testrepo/.gitted/index differ
diff --git a/tests/resources/testrepo/.gitted/objects/97/328ac7e3bd0bcd3900cb3e7a624d71dd0df888 b/tests/resources/testrepo/.gitted/objects/97/328ac7e3bd0bcd3900cb3e7a624d71dd0df888
new file mode 100644
index 0000000..2aa37bc
Binary files /dev/null and b/tests/resources/testrepo/.gitted/objects/97/328ac7e3bd0bcd3900cb3e7a624d71dd0df888 differ
diff --git a/tests/resources/testrepo/.gitted/objects/99/1f1b12603e1d78411c1b4042719f964efa7adf b/tests/resources/testrepo/.gitted/objects/99/1f1b12603e1d78411c1b4042719f964efa7adf
new file mode 100644
index 0000000..4173754
Binary files /dev/null and b/tests/resources/testrepo/.gitted/objects/99/1f1b12603e1d78411c1b4042719f964efa7adf differ