diff --git a/CHANGELOG.md b/CHANGELOG.md index cfb4f0430..5dfc27edf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/include/git2/tree.h b/include/git2/tree.h index 42b68193e..0f7616210 100644 --- a/include/git2/tree.h +++ b/include/git2/tree.h @@ -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 */ diff --git a/src/notes.c b/src/notes.c index 8fdf388ab..a0bc0d355 100644 --- a/src/notes.c +++ b/src/notes.c @@ -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); diff --git a/src/tree.c b/src/tree.c index 6246ff648..57cc95387 100644 --- a/src/tree.c +++ b/src/tree.c @@ -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); diff --git a/src/tree.h b/src/tree.h index 5d27eb7c9..d01b6fd41 100644 --- a/src/tree.h +++ b/src/tree.h @@ -26,6 +26,7 @@ struct git_tree { }; struct git_treebuilder { + git_repository *repo; git_strmap *map; }; diff --git a/tests/index/tests.c b/tests/index/tests.c index 0464e7337..a6c4b895c 100644 --- a/tests/index/tests.c +++ b/tests/index/tests.c @@ -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; diff --git a/tests/object/tree/attributes.c b/tests/object/tree/attributes.c index 85216cd1b..14f3f89f9 100644 --- a/tests/object/tree/attributes.c +++ b/tests/object/tree/attributes.c @@ -1,9 +1,21 @@ #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(); } diff --git a/tests/object/tree/duplicateentries.c b/tests/object/tree/duplicateentries.c index 1b752acbb..11314ec90 100644 --- a/tests/object/tree/duplicateentries.c +++ b/tests/object/tree/duplicateentries.c @@ -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); } diff --git a/tests/object/tree/write.c b/tests/object/tree/write.c index ddb62e278..2947ac362 100644 --- a/tests/object/tree/write.c +++ b/tests/object/tree/write.c @@ -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); +} diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c index fb5561bc2..0e8793d99 100644 --- a/tests/repo/iterator.c +++ b/tests/repo/iterator.c @@ -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);