Commit dce7b1a4 by Edward Thomson Committed by Edward Thomson

treebuilder: take a repository for path validation

Path validation may be influenced by `core.protectHFS` and
`core.protectNTFS` configuration settings, thus treebuilders
can take a repository to influence their configuration.
parent ec74b40c
......@@ -136,3 +136,7 @@ v0.21 + 1
* git_libgit2_init() and git_libgit2_shutdown() now return the number of
initializations of the library, so consumers may schedule work on the
first initialization.
* git_treebuilder_create now takes a repository so that it can query
repository configuration. Subsequently, git_treebuilder_write no
longer takes a repository.
......@@ -247,11 +247,12 @@ GIT_EXTERN(int) git_tree_entry_to_object(
* entries and will have to be filled manually.
*
* @param out Pointer where to store the tree builder
* @param repo Repository in which to store the object
* @param source Source tree to initialize the builder (optional)
* @return 0 on success; error code otherwise
*/
GIT_EXTERN(int) git_treebuilder_create(
git_treebuilder **out, const git_tree *source);
git_treebuilder **out, git_repository *repo, const git_tree *source);
/**
* Clear all the entires in the builder
......@@ -368,12 +369,11 @@ GIT_EXTERN(void) git_treebuilder_filter(
* identifying SHA1 hash will be stored in the `id` pointer.
*
* @param id Pointer to store the OID of the newly written tree
* @param repo Repository in which to store the object
* @param bld Tree builder to write
* @return 0 or an error code
*/
GIT_EXTERN(int) git_treebuilder_write(
git_oid *id, git_repository *repo, git_treebuilder *bld);
git_oid *id, git_treebuilder *bld);
/** Callback for the tree traversal method */
......
......@@ -107,7 +107,7 @@ static int tree_write(
const git_tree_entry *entry;
git_oid tree_oid;
if ((error = git_treebuilder_create(&tb, source_tree)) < 0)
if ((error = git_treebuilder_create(&tb, repo, source_tree)) < 0)
goto cleanup;
if (object_oid) {
......@@ -119,7 +119,7 @@ static int tree_write(
goto cleanup;
}
if ((error = git_treebuilder_write(&tree_oid, repo, tb)) < 0)
if ((error = git_treebuilder_write(&tree_oid, tb)) < 0)
goto cleanup;
error = git_tree_lookup(out, repo, &tree_oid);
......
......@@ -50,14 +50,11 @@ GIT_INLINE(git_filemode_t) normalize_filemode(git_filemode_t filemode)
return GIT_FILEMODE_BLOB;
}
static int valid_entry_name(const char *filename)
static int valid_entry_name(git_repository *repo, const char *filename)
{
return *filename != '\0' &&
strchr(filename, '/') == NULL &&
(*filename != '.' ||
(strcmp(filename, ".") != 0 &&
strcmp(filename, "..") != 0 &&
strcasecmp(filename, DOT_GIT) != 0));
git_path_isvalid(repo, filename,
GIT_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT | GIT_PATH_REJECT_SLASH);
}
static int entry_sort_cmp(const void *a, const void *b)
......@@ -455,7 +452,7 @@ static int append_entry(
git_tree_entry *entry;
int error = 0;
if (!valid_entry_name(filename))
if (!valid_entry_name(bld->repo, filename))
return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
entry = alloc_entry(filename);
......@@ -493,7 +490,7 @@ static int write_tree(
return (int)find_next_dir(dirname, index, start);
}
if ((error = git_treebuilder_create(&bld, NULL)) < 0 || bld == NULL)
if ((error = git_treebuilder_create(&bld, repo, NULL)) < 0 || bld == NULL)
return -1;
/*
......@@ -564,7 +561,7 @@ static int write_tree(
}
}
if (git_treebuilder_write(oid, repo, bld) < 0)
if (git_treebuilder_write(oid, bld) < 0)
goto on_error;
git_treebuilder_free(bld);
......@@ -627,16 +624,21 @@ int git_tree__write_index(
return ret;
}
int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source)
int git_treebuilder_create(
git_treebuilder **builder_p,
git_repository *repo,
const git_tree *source)
{
git_treebuilder *bld;
size_t i;
assert(builder_p);
assert(builder_p && repo);
bld = git__calloc(1, sizeof(git_treebuilder));
GITERR_CHECK_ALLOC(bld);
bld->repo = repo;
if (git_strmap_alloc(&bld->map) < 0) {
git__free(bld);
return -1;
......@@ -678,7 +680,7 @@ int git_treebuilder_insert(
if (!valid_filemode(filemode))
return tree_error("Failed to insert entry. Invalid filemode for file", filename);
if (!valid_entry_name(filename))
if (!valid_entry_name(bld->repo, filename))
return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
pos = git_strmap_lookup_index(bld->map, filename);
......@@ -738,7 +740,7 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
return 0;
}
int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld)
int git_treebuilder_write(git_oid *oid, git_treebuilder *bld)
{
int error = 0;
size_t i, entrycount;
......@@ -777,7 +779,7 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b
git_vector_free(&entries);
if (!error &&
!(error = git_repository_odb__weakptr(&odb, repo)))
!(error = git_repository_odb__weakptr(&odb, bld->repo)))
error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
git_buf_free(&tree);
......
......@@ -26,6 +26,7 @@ struct git_tree {
};
struct git_treebuilder {
git_repository *repo;
git_strmap *map;
};
......
......@@ -421,6 +421,27 @@ void test_index_tests__write_invalid_filename(void)
cl_fixture_cleanup("invalid");
}
void test_index_tests__honors_protect_filesystems(void)
{
git_repository *repo;
p_mkdir("invalid", 0700);
cl_git_pass(git_repository_init(&repo, "./invalid", 0));
cl_repo_set_bool(repo, "core.protectHFS", true);
cl_repo_set_bool(repo, "core.protectNTFS", true);
write_invalid_filename(repo, ".git./hello");
write_invalid_filename(repo, ".git\xe2\x80\xad/hello");
write_invalid_filename(repo, "git~1/hello");
write_invalid_filename(repo, ".git\xe2\x81\xaf/hello");
git_repository_free(repo);
cl_fixture_cleanup("invalid");
}
void test_index_tests__remove_entry(void)
{
git_repository *repo;
......
#include "clar_libgit2.h"
#include "tree.h"
static git_repository *repo;
static const char *blob_oid = "3d0970ec547fc41ef8a5882dde99c6adce65b021";
static const char *tree_oid = "1b05fdaa881ee45b48cbaa5e9b037d667a47745e";
void test_object_tree_attributes__initialize(void)
{
repo = cl_git_sandbox_init("deprecated-mode.git");
}
void test_object_tree_attributes__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion(void)
{
git_treebuilder *builder;
......@@ -11,7 +23,7 @@ void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion(
cl_git_pass(git_oid_fromstr(&oid, blob_oid));
cl_git_pass(git_treebuilder_create(&builder, NULL));
cl_git_pass(git_treebuilder_create(&builder, repo, NULL));
cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0777777));
cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0100666));
......@@ -22,7 +34,6 @@ void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion(
void test_object_tree_attributes__group_writable_tree_entries_created_with_an_antique_git_version_can_still_be_accessed(void)
{
git_repository *repo;
git_oid tid;
git_tree *tree;
const git_tree_entry *entry;
......@@ -38,7 +49,6 @@ void test_object_tree_attributes__group_writable_tree_entries_created_with_an_an
git_tree_entry_filemode(entry));
git_tree_free(tree);
git_repository_free(repo);
}
void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void)
......@@ -48,7 +58,7 @@ void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void)
const git_tree_entry *entry;
cl_git_pass(git_oid_fromstr(&bid, blob_oid));
cl_git_pass(git_treebuilder_create(&builder, NULL));
cl_git_pass(git_treebuilder_create(&builder, repo, NULL));
cl_git_fail(git_treebuilder_insert(
&entry,
......@@ -62,25 +72,22 @@ void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void)
void test_object_tree_attributes__normalize_attributes_when_creating_a_tree_from_an_existing_one(void)
{
git_repository *repo;
git_treebuilder *builder;
git_oid tid, tid2;
git_tree *tree;
const git_tree_entry *entry;
repo = cl_git_sandbox_init("deprecated-mode.git");
cl_git_pass(git_oid_fromstr(&tid, tree_oid));
cl_git_pass(git_tree_lookup(&tree, repo, &tid));
cl_git_pass(git_treebuilder_create(&builder, tree));
cl_git_pass(git_treebuilder_create(&builder, repo, tree));
entry = git_treebuilder_get(builder, "old_mode.txt");
cl_assert_equal_i(
GIT_FILEMODE_BLOB,
git_tree_entry_filemode(entry));
cl_git_pass(git_treebuilder_write(&tid2, repo, builder));
cl_git_pass(git_treebuilder_write(&tid2, builder));
git_treebuilder_free(builder);
git_tree_free(tree);
......@@ -91,18 +98,14 @@ void test_object_tree_attributes__normalize_attributes_when_creating_a_tree_from
git_tree_entry_filemode(entry));
git_tree_free(tree);
cl_git_sandbox_cleanup();
}
void test_object_tree_attributes__normalize_600(void)
{
git_oid id;
git_tree *tree;
git_repository *repo;
const git_tree_entry *entry;
repo = cl_git_sandbox_init("deprecated-mode.git");
git_oid_fromstr(&id, "0810fb7818088ff5ac41ee49199b51473b1bd6c7");
cl_git_pass(git_tree_lookup(&tree, repo, &id));
......@@ -111,5 +114,4 @@ void test_object_tree_attributes__normalize_600(void)
cl_assert_equal_i(git_tree_entry_filemode_raw(entry), 0100600);
git_tree_free(tree);
cl_git_sandbox_cleanup();
}
......@@ -57,11 +57,11 @@ static void tree_creator(git_oid *out, void (*fn)(git_treebuilder *))
{
git_treebuilder *builder;
cl_git_pass(git_treebuilder_create(&builder, NULL));
cl_git_pass(git_treebuilder_create(&builder, _repo, NULL));
fn(builder);
cl_git_pass(git_treebuilder_write(out, _repo, builder));
cl_git_pass(git_treebuilder_write(out, builder));
git_treebuilder_free(builder);
}
......
......@@ -35,7 +35,7 @@ void test_object_tree_write__from_memory(void)
* on REPOSITORY_FOLDER.
*/
cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
cl_git_pass(git_treebuilder_create(&builder, tree));
cl_git_pass(git_treebuilder_create(&builder, g_repo, tree));
cl_git_fail(git_treebuilder_insert(NULL, builder, "",
&bid, GIT_FILEMODE_BLOB));
......@@ -53,7 +53,7 @@ void test_object_tree_write__from_memory(void)
cl_git_pass(git_treebuilder_insert(
NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB));
cl_git_pass(git_treebuilder_write(&rid, g_repo, builder));
cl_git_pass(git_treebuilder_write(&rid, builder));
cl_assert(git_oid_cmp(&rid, &id2) == 0);
......@@ -75,18 +75,18 @@ void test_object_tree_write__subtree(void)
git_oid_fromstr(&bid, blob_oid);
/* create subtree */
cl_git_pass(git_treebuilder_create(&builder, NULL));
cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
cl_git_pass(git_treebuilder_insert(
NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB)); /* -V536 */
cl_git_pass(git_treebuilder_write(&subtree_id, g_repo, builder));
cl_git_pass(git_treebuilder_write(&subtree_id, builder));
git_treebuilder_free(builder);
/* create parent tree */
cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
cl_git_pass(git_treebuilder_create(&builder, tree));
cl_git_pass(git_treebuilder_create(&builder, g_repo, tree));
cl_git_pass(git_treebuilder_insert(
NULL, builder, "new", &subtree_id, GIT_FILEMODE_TREE)); /* -V536 */
cl_git_pass(git_treebuilder_write(&id_hiearar, g_repo, builder));
cl_git_pass(git_treebuilder_write(&id_hiearar, builder));
git_treebuilder_free(builder);
git_tree_free(tree);
......@@ -135,14 +135,14 @@ void test_object_tree_write__sorted_subtrees(void)
memset(&blank_oid, 0x0, sizeof(blank_oid));
cl_git_pass(git_treebuilder_create(&builder, NULL));
cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
for (i = 0; i < ARRAY_SIZE(entries); ++i) {
cl_git_pass(git_treebuilder_insert(NULL,
builder, entries[i].filename, &blank_oid, entries[i].attr));
}
cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder));
cl_git_pass(git_treebuilder_write(&tree_oid, builder));
cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid));
for (i = 0; i < git_tree_entrycount(tree); i++) {
......@@ -192,7 +192,7 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void)
memset(&blank_oid, 0x0, sizeof(blank_oid));
cl_git_pass(git_treebuilder_create(&builder, NULL));
cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
cl_assert_equal_i(0, (int)git_treebuilder_entrycount(builder));
......@@ -229,7 +229,7 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void)
NULL, builder, "apple_extra", &blank_oid, GIT_FILEMODE_BLOB));
cl_assert_equal_i(7, (int)git_treebuilder_entrycount(builder));
cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder));
cl_git_pass(git_treebuilder_write(&tree_oid, builder));
git_treebuilder_free(builder);
......@@ -283,7 +283,7 @@ void test_object_tree_write__filtering(void)
memset(&blank_oid, 0x0, sizeof(blank_oid));
cl_git_pass(git_treebuilder_create(&builder, NULL));
cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
for (i = 0; _entries[i].filename; ++i)
cl_git_pass(git_treebuilder_insert(NULL,
......@@ -310,7 +310,7 @@ void test_object_tree_write__filtering(void)
cl_assert(git_treebuilder_get(builder, "aardvark") == NULL);
cl_assert(git_treebuilder_get(builder, "last") != NULL);
cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder));
cl_git_pass(git_treebuilder_write(&tree_oid, builder));
git_treebuilder_free(builder);
......@@ -346,13 +346,13 @@ void test_object_tree_write__cruel_paths(void)
git_oid_fromstr(&bid, blob_oid);
/* create tree */
cl_git_pass(git_treebuilder_create(&builder, NULL));
cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
for (scan = the_paths; *scan; ++scan) {
cl_git_pass(git_treebuilder_insert(
NULL, builder, *scan, &bid, GIT_FILEMODE_BLOB));
count++;
}
cl_git_pass(git_treebuilder_write(&id, g_repo, builder));
cl_git_pass(git_treebuilder_write(&id, builder));
git_treebuilder_free(builder);
/* check data is correct */
......@@ -374,12 +374,12 @@ void test_object_tree_write__cruel_paths(void)
git_tree_free(tree);
/* let's try longer paths */
cl_git_pass(git_treebuilder_create(&builder, NULL));
cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
for (scan = the_paths; *scan; ++scan) {
cl_git_pass(git_treebuilder_insert(
NULL, builder, *scan, &id, GIT_FILEMODE_TREE));
}
cl_git_pass(git_treebuilder_write(&subid, g_repo, builder));
cl_git_pass(git_treebuilder_write(&subid, builder));
git_treebuilder_free(builder);
/* check data is correct */
......@@ -400,3 +400,43 @@ void test_object_tree_write__cruel_paths(void)
git_tree_free(tree);
}
void test_object_tree_write__protect_filesystems(void)
{
git_treebuilder *builder;
git_oid bid;
/* Ensure that (by default) we can write objects with funny names on
* platforms that are not affected.
*/
cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
#ifndef GIT_WIN32
cl_git_pass(git_treebuilder_insert(NULL, builder, ".git.", &bid, GIT_FILEMODE_BLOB));
cl_git_pass(git_treebuilder_insert(NULL, builder, "git~1", &bid, GIT_FILEMODE_BLOB));
#endif
#ifndef __APPLE__
cl_git_pass(git_treebuilder_insert(NULL, builder, ".git\xef\xbb\xbf", &bid, GIT_FILEMODE_BLOB));
cl_git_pass(git_treebuilder_insert(NULL, builder, ".git\xe2\x80\xad", &bid, GIT_FILEMODE_BLOB));
#endif
git_treebuilder_free(builder);
/* Now turn on core.protectHFS and core.protectNTFS and validate that these
* paths are rejected.
*/
cl_repo_set_bool(g_repo, "core.protectHFS", true);
cl_repo_set_bool(g_repo, "core.protectNTFS", true);
cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
cl_git_fail(git_treebuilder_insert(NULL, builder, ".git.", &bid, GIT_FILEMODE_BLOB));
cl_git_fail(git_treebuilder_insert(NULL, builder, "git~1", &bid, GIT_FILEMODE_BLOB));
cl_git_fail(git_treebuilder_insert(NULL, builder, ".git\xef\xbb\xbf", &bid, GIT_FILEMODE_BLOB));
cl_git_fail(git_treebuilder_insert(NULL, builder, ".git\xe2\x80\xad", &bid, GIT_FILEMODE_BLOB));
git_treebuilder_free(builder);
}
......@@ -427,7 +427,7 @@ static void build_test_tree(
git_buf name = GIT_BUF_INIT;
va_list arglist;
cl_git_pass(git_treebuilder_create(&builder, NULL)); /* start builder */
cl_git_pass(git_treebuilder_create(&builder, repo, NULL)); /* start builder */
va_start(arglist, fmt);
while (*scan) {
......@@ -451,7 +451,7 @@ static void build_test_tree(
}
va_end(arglist);
cl_git_pass(git_treebuilder_write(out, repo, builder));
cl_git_pass(git_treebuilder_write(out, builder));
git_treebuilder_free(builder);
git_buf_free(&name);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment