attr: validate workdir paths for attribute files We should allow attribute files - inside working directories - to have names longer than MAX_PATH when core.longpaths is set. `git_attr_path__init` takes a repository to validate the path with.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
diff --git a/src/attr.c b/src/attr.c
index e85e271..f9cd930 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -67,7 +67,7 @@ int git_attr_get(
if (git_repository_is_bare(repo))
dir_flag = GIT_DIR_FLAG_FALSE;
- if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
+ if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1;
if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0)
@@ -133,7 +133,7 @@ int git_attr_get_many_with_session(
if (git_repository_is_bare(repo))
dir_flag = GIT_DIR_FLAG_FALSE;
- if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
+ if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1;
if ((error = collect_attr_files(repo, attr_session, flags, pathname, &files)) < 0)
@@ -217,7 +217,7 @@ int git_attr_foreach(
if (git_repository_is_bare(repo))
dir_flag = GIT_DIR_FLAG_FALSE;
- if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
+ if (git_attr_path__init(&path, repo, pathname, git_repository_workdir(repo), dir_flag) < 0)
return -1;
if ((error = collect_attr_files(repo, NULL, flags, pathname, &files)) < 0 ||
diff --git a/src/attr_file.c b/src/attr_file.c
index adc56d5..d1b90c5 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -403,7 +403,7 @@ int git_attr_file__load_standalone(git_attr_file **out, const char *path)
if ((error = git_attr_file__new(&file, NULL, GIT_ATTR_FILE__FROM_FILE)) < 0 ||
(error = git_attr_file__parse_buffer(NULL, file, content.ptr, true)) < 0 ||
- (error = git_attr_cache__alloc_file_entry(&file->entry, NULL, path, &file->pool)) < 0)
+ (error = git_attr_cache__alloc_file_entry(&file->entry, NULL, NULL, path, &file->pool)) < 0)
goto out;
*out = file;
@@ -503,14 +503,19 @@ git_attr_assignment *git_attr_rule__lookup_assignment(
}
int git_attr_path__init(
- git_attr_path *info, const char *path, const char *base, git_dir_flag dir_flag)
+ git_attr_path *info,
+ git_repository *repo,
+ const char *path,
+ const char *base,
+ git_dir_flag dir_flag)
{
ssize_t root;
/* build full path as best we can */
git_buf_init(&info->full, 0);
- if (git_path_join_unrooted(&info->full, path, base, &root) < 0)
+ if (git_path_join_unrooted(&info->full, path, base, &root) < 0 ||
+ git_path_validate_workdir_buf(repo, &info->full) < 0)
return -1;
info->path = info->full.ptr + root;
diff --git a/src/attr_file.h b/src/attr_file.h
index 2b6b1d6..617436b 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -207,8 +207,11 @@ extern git_attr_assignment *git_attr_rule__lookup_assignment(
typedef enum { GIT_DIR_FLAG_TRUE = 1, GIT_DIR_FLAG_FALSE = 0, GIT_DIR_FLAG_UNKNOWN = -1 } git_dir_flag;
extern int git_attr_path__init(
- git_attr_path *info, const char *path, const char *base, git_dir_flag is_dir);
-
+ git_attr_path *out,
+ git_repository *repo,
+ const char *path,
+ const char *base,
+ git_dir_flag is_dir);
extern void git_attr_path__free(git_attr_path *info);
extern int git_attr_assignment__parse(
diff --git a/src/attrcache.c b/src/attrcache.c
index 2485b05..32513da 100644
--- a/src/attrcache.c
+++ b/src/attrcache.c
@@ -38,6 +38,7 @@ GIT_INLINE(git_attr_file_entry *) attr_cache_lookup_entry(
int git_attr_cache__alloc_file_entry(
git_attr_file_entry **out,
+ git_repository *repo,
const char *base,
const char *path,
git_pool *pool)
@@ -65,6 +66,9 @@ int git_attr_cache__alloc_file_entry(
}
memcpy(&ce->fullpath[baselen], path, pathlen);
+ if (git_path_validate_workdir_with_len(repo, ce->fullpath, pathlen + baselen) < 0)
+ return -1;
+
ce->path = &ce->fullpath[baselen];
*out = ce;
@@ -79,8 +83,8 @@ static int attr_cache_make_entry(
git_attr_file_entry *entry = NULL;
int error;
- if ((error = git_attr_cache__alloc_file_entry(&entry, git_repository_workdir(repo),
- path, &cache->pool)) < 0)
+ if ((error = git_attr_cache__alloc_file_entry(&entry, repo,
+ git_repository_workdir(repo), path, &cache->pool)) < 0)
return error;
if ((error = git_strmap_set(cache->files, entry->path, entry)) < 0)
@@ -169,7 +173,8 @@ static int attr_cache_lookup(
if (base != NULL && git_path_root(filename) < 0) {
git_buf *p = attr_session ? &attr_session->tmp : &path;
- if (git_buf_joinpath(p, base, filename) < 0)
+ if (git_buf_joinpath(p, base, filename) < 0 ||
+ git_path_validate_workdir_buf(repo, p) < 0)
return -1;
filename = p->ptr;
diff --git a/src/attrcache.h b/src/attrcache.h
index 4b1d5ce..5e2fb29 100644
--- a/src/attrcache.h
+++ b/src/attrcache.h
@@ -44,6 +44,7 @@ extern bool git_attr_cache__is_cached(
extern int git_attr_cache__alloc_file_entry(
git_attr_file_entry **out,
+ git_repository *repo,
const char *base,
const char *path,
git_pool *pool);
diff --git a/src/ignore.c b/src/ignore.c
index 27a650b..085b0e9 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -453,7 +453,7 @@ int git_ignore__lookup(
*out = GIT_IGNORE_NOTFOUND;
if (git_attr_path__init(
- &path, pathname, git_repository_workdir(ignores->repo), dir_flag) < 0)
+ &path, ignores->repo, pathname, git_repository_workdir(ignores->repo), dir_flag) < 0)
return -1;
/* first process builtins - success means path was found */
@@ -537,7 +537,7 @@ int git_ignore_path_is_ignored(
else if (git_repository_is_bare(repo))
dir_flag = GIT_DIR_FLAG_FALSE;
- if ((error = git_attr_path__init(&path, pathname, workdir, dir_flag)) < 0 ||
+ if ((error = git_attr_path__init(&path, repo, pathname, workdir, dir_flag)) < 0 ||
(error = git_ignore__for_path(repo, path.path, &ignores)) < 0)
goto cleanup;
diff --git a/tests/attr/lookup.c b/tests/attr/lookup.c
index 6063468..29a66b0 100644
--- a/tests/attr/lookup.c
+++ b/tests/attr/lookup.c
@@ -13,7 +13,7 @@ void test_attr_lookup__simple(void)
cl_assert_equal_s(cl_fixture("attr/attr0"), file->entry->path);
cl_assert(file->rules.length == 1);
- cl_git_pass(git_attr_path__init(&path, "test", NULL, GIT_DIR_FLAG_UNKNOWN));
+ cl_git_pass(git_attr_path__init(&path, NULL, "test", NULL, GIT_DIR_FLAG_UNKNOWN));
cl_assert_equal_s("test", path.path);
cl_assert_equal_s("test", path.basename);
cl_assert(!path.is_dir);
@@ -36,7 +36,7 @@ static void run_test_cases(git_attr_file *file, struct attr_expected *cases, int
int error;
for (c = cases; c->path != NULL; c++) {
- cl_git_pass(git_attr_path__init(&path, c->path, NULL, GIT_DIR_FLAG_UNKNOWN));
+ cl_git_pass(git_attr_path__init(&path, NULL, c->path, NULL, GIT_DIR_FLAG_UNKNOWN));
if (force_dir)
path.is_dir = 1;
@@ -133,7 +133,7 @@ void test_attr_lookup__match_variants(void)
cl_assert_equal_s(cl_fixture("attr/attr1"), file->entry->path);
cl_assert(file->rules.length == 10);
- cl_git_pass(git_attr_path__init(&path, "/testing/for/pat0", NULL, GIT_DIR_FLAG_UNKNOWN));
+ cl_git_pass(git_attr_path__init(&path, NULL, "/testing/for/pat0", NULL, GIT_DIR_FLAG_UNKNOWN));
cl_assert_equal_s("pat0", path.basename);
run_test_cases(file, cases, 0);