Skip to content

Commit 645a29c

Browse files
pcloudsgitster
authored andcommitted
parse_pathspec: make sure the prefix part is wildcard-free
Prepending prefix to pathspec is a trick to workaround the fact that commands can be executed in a subdirectory, but all git commands run at worktree's root. The prefix part should always be treated as literal string. Make it so. Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b3920bb commit 645a29c

File tree

5 files changed

+54
-10
lines changed

5 files changed

+54
-10
lines changed

cache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ extern void setup_work_tree(void);
414414
extern const char *setup_git_directory_gently(int *);
415415
extern const char *setup_git_directory(void);
416416
extern char *prefix_path(const char *prefix, int len, const char *path);
417+
extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
417418
extern const char *prefix_filename(const char *prefix, int len, const char *path);
418419
extern int check_filename(const char *prefix, const char *name);
419420
extern void verify_filename(const char *prefix,
@@ -741,6 +742,7 @@ const char *real_path(const char *path);
741742
const char *real_path_if_valid(const char *path);
742743
const char *absolute_path(const char *path);
743744
const char *relative_path(const char *abs, const char *base);
745+
int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
744746
int normalize_path_copy(char *dst, const char *src);
745747
int longest_ancestor_length(const char *path, struct string_list *prefixes);
746748
char *strip_path_suffix(const char *path, const char *suffix);

path.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,8 +492,14 @@ const char *relative_path(const char *abs, const char *base)
492492
*
493493
* Note that this function is purely textual. It does not follow symlinks,
494494
* verify the existence of the path, or make any system calls.
495+
*
496+
* prefix_len != NULL is for a specific case of prefix_pathspec():
497+
* assume that src == dst and src[0..prefix_len-1] is already
498+
* normalized, any time "../" eats up to the prefix_len part,
499+
* prefix_len is reduced. In the end prefix_len is the remaining
500+
* prefix that has not been overridden by user pathspec.
495501
*/
496-
int normalize_path_copy(char *dst, const char *src)
502+
int normalize_path_copy_len(char *dst, const char *src, int *prefix_len)
497503
{
498504
char *dst0;
499505

@@ -568,11 +574,18 @@ int normalize_path_copy(char *dst, const char *src)
568574
/* Windows: dst[-1] cannot be backslash anymore */
569575
while (dst0 < dst && dst[-1] != '/')
570576
dst--;
577+
if (prefix_len && *prefix_len > dst - dst0)
578+
*prefix_len = dst - dst0;
571579
}
572580
*dst = '\0';
573581
return 0;
574582
}
575583

584+
int normalize_path_copy(char *dst, const char *src)
585+
{
586+
return normalize_path_copy_len(dst, src, NULL);
587+
}
588+
576589
/*
577590
* path = Canonical absolute path
578591
* prefixes = string_list containing normalized, absolute paths without

pathspec.c

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,14 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
150150
magic |= short_magic;
151151
*p_short_magic = short_magic;
152152

153-
if (magic & PATHSPEC_FROMTOP)
153+
if (magic & PATHSPEC_FROMTOP) {
154154
match = xstrdup(copyfrom);
155-
else
156-
match = prefix_path(prefix, prefixlen, copyfrom);
155+
prefixlen = 0;
156+
} else {
157+
match = prefix_path_gently(prefix, prefixlen, &prefixlen, copyfrom);
158+
if (!match)
159+
die(_("%s: '%s' is outside repository"), elt, copyfrom);
160+
}
157161
*raw = item->match = match;
158162
/*
159163
* Prefix the pathspec (keep all magic) and assign to
@@ -167,6 +171,7 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
167171
} else
168172
item->original = elt;
169173
item->len = strlen(item->match);
174+
item->prefix = prefixlen;
170175

171176
if ((flags & PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP) &&
172177
(item->len >= 1 && item->match[item->len - 1] == '/') &&
@@ -198,13 +203,20 @@ static unsigned prefix_pathspec(struct pathspec_item *item,
198203

199204
if (limit_pathspec_to_literal())
200205
item->nowildcard_len = item->len;
201-
else
206+
else {
202207
item->nowildcard_len = simple_length(item->match);
208+
if (item->nowildcard_len < prefixlen)
209+
item->nowildcard_len = prefixlen;
210+
}
203211
item->flags = 0;
204212
if (item->nowildcard_len < item->len &&
205213
item->match[item->nowildcard_len] == '*' &&
206214
no_wildcard(item->match + item->nowildcard_len + 1))
207215
item->flags |= PATHSPEC_ONESTAR;
216+
217+
/* sanity checks, pathspec matchers assume these are sane */
218+
assert(item->nowildcard_len <= item->len &&
219+
item->prefix <= item->len);
208220
return magic;
209221
}
210222

@@ -284,6 +296,7 @@ void parse_pathspec(struct pathspec *pathspec,
284296
item->match = prefix;
285297
item->original = prefix;
286298
item->nowildcard_len = item->len = strlen(prefix);
299+
item->prefix = item->len;
287300
raw[0] = prefix;
288301
raw[1] = NULL;
289302
pathspec->nr = 1;

pathspec.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct pathspec {
2121
const char *match;
2222
const char *original;
2323
unsigned magic;
24-
int len;
24+
int len, prefix;
2525
int nowildcard_len;
2626
int flags;
2727
} *items;

setup.c

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,37 @@
55
static int inside_git_dir = -1;
66
static int inside_work_tree = -1;
77

8-
static char *prefix_path_gently(const char *prefix, int len, const char *path)
8+
/*
9+
* Normalize "path", prepending the "prefix" for relative paths. If
10+
* remaining_prefix is not NULL, return the actual prefix still
11+
* remains in the path. For example, prefix = sub1/sub2/ and path is
12+
*
13+
* foo -> sub1/sub2/foo (full prefix)
14+
* ../foo -> sub1/foo (remaining prefix is sub1/)
15+
* ../../bar -> bar (no remaining prefix)
16+
* ../../sub1/sub2/foo -> sub1/sub2/foo (but no remaining prefix)
17+
* `pwd`/../bar -> sub1/bar (no remaining prefix)
18+
*/
19+
char *prefix_path_gently(const char *prefix, int len,
20+
int *remaining_prefix, const char *path)
921
{
1022
const char *orig = path;
1123
char *sanitized;
1224
if (is_absolute_path(orig)) {
1325
const char *temp = real_path(path);
1426
sanitized = xmalloc(len + strlen(temp) + 1);
1527
strcpy(sanitized, temp);
28+
if (remaining_prefix)
29+
*remaining_prefix = 0;
1630
} else {
1731
sanitized = xmalloc(len + strlen(path) + 1);
1832
if (len)
1933
memcpy(sanitized, prefix, len);
2034
strcpy(sanitized + len, path);
35+
if (remaining_prefix)
36+
*remaining_prefix = len;
2137
}
22-
if (normalize_path_copy(sanitized, sanitized))
38+
if (normalize_path_copy_len(sanitized, sanitized, remaining_prefix))
2339
goto error_out;
2440
if (is_absolute_path(orig)) {
2541
size_t root_len, len, total;
@@ -44,7 +60,7 @@ static char *prefix_path_gently(const char *prefix, int len, const char *path)
4460

4561
char *prefix_path(const char *prefix, int len, const char *path)
4662
{
47-
char *r = prefix_path_gently(prefix, len, path);
63+
char *r = prefix_path_gently(prefix, len, NULL, path);
4864
if (!r)
4965
die("'%s' is outside repository", path);
5066
return r;
@@ -53,7 +69,7 @@ char *prefix_path(const char *prefix, int len, const char *path)
5369
int path_inside_repo(const char *prefix, const char *path)
5470
{
5571
int len = prefix ? strlen(prefix) : 0;
56-
char *r = prefix_path_gently(prefix, len, path);
72+
char *r = prefix_path_gently(prefix, len, NULL, path);
5773
if (r) {
5874
free(r);
5975
return 1;

0 commit comments

Comments
 (0)