Skip to content

Commit d2189a7

Browse files
committed
Merge branch 'en/fill-directory-fixes'
Assorted fixes to the directory traversal API. * en/fill-directory-fixes: dir.c: use st_add3() for allocation size dir: consolidate similar code in treat_directory() dir: synchronize treat_leading_path() and read_directory_recursive() dir: fix checks on common prefix directory dir: break part of read_directory_recursive() out for reuse dir: exit before wildcard fall-through if there is no wildcard dir: remove stray quote character in comment Revert "dir.c: make 'git-status --ignored' work within leading directories" t3011: demonstrate directory traversal failures
2 parents 8be0a42 + 6836d2f commit d2189a7

File tree

3 files changed

+354
-51
lines changed

3 files changed

+354
-51
lines changed

dir.c

Lines changed: 138 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -371,12 +371,19 @@ static int match_pathspec_item(const struct index_state *istate,
371371
!ps_strncmp(item, match, name, namelen))
372372
return MATCHED_RECURSIVELY_LEADING_PATHSPEC;
373373

374-
/* name" doesn't match up to the first wild character */
374+
/* name doesn't match up to the first wild character */
375375
if (item->nowildcard_len < item->len &&
376376
ps_strncmp(item, match, name,
377377
item->nowildcard_len - prefix))
378378
return 0;
379379

380+
/*
381+
* name has no wildcard, and it didn't match as a leading
382+
* pathspec so return.
383+
*/
384+
if (item->nowildcard_len == item->len)
385+
return 0;
386+
380387
/*
381388
* Here is where we would perform a wildmatch to check if
382389
* "name" can be matched as a directory (or a prefix) against
@@ -1652,6 +1659,8 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
16521659
const char *dirname, int len, int baselen, int exclude,
16531660
const struct pathspec *pathspec)
16541661
{
1662+
int nested_repo = 0;
1663+
16551664
/* The "len-1" is to strip the final '/' */
16561665
switch (directory_exists_in_index(istate, dirname, len-1)) {
16571666
case index_directory:
@@ -1661,15 +1670,16 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
16611670
return path_none;
16621671

16631672
case index_nonexistent:
1664-
if (dir->flags & DIR_SKIP_NESTED_GIT) {
1665-
int nested_repo;
1673+
if ((dir->flags & DIR_SKIP_NESTED_GIT) ||
1674+
!(dir->flags & DIR_NO_GITLINKS)) {
16661675
struct strbuf sb = STRBUF_INIT;
16671676
strbuf_addstr(&sb, dirname);
16681677
nested_repo = is_nonbare_repository_dir(&sb);
16691678
strbuf_release(&sb);
1670-
if (nested_repo)
1671-
return path_none;
16721679
}
1680+
if (nested_repo)
1681+
return ((dir->flags & DIR_SKIP_NESTED_GIT) ? path_none :
1682+
(exclude ? path_excluded : path_untracked));
16731683

16741684
if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
16751685
break;
@@ -1697,13 +1707,6 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
16971707

16981708
return path_none;
16991709
}
1700-
if (!(dir->flags & DIR_NO_GITLINKS)) {
1701-
struct strbuf sb = STRBUF_INIT;
1702-
strbuf_addstr(&sb, dirname);
1703-
if (is_nonbare_repository_dir(&sb))
1704-
return exclude ? path_excluded : path_untracked;
1705-
strbuf_release(&sb);
1706-
}
17071710
return path_recurse;
17081711
}
17091712

@@ -2123,6 +2126,40 @@ static void close_cached_dir(struct cached_dir *cdir)
21232126
}
21242127
}
21252128

2129+
static void add_path_to_appropriate_result_list(struct dir_struct *dir,
2130+
struct untracked_cache_dir *untracked,
2131+
struct cached_dir *cdir,
2132+
struct index_state *istate,
2133+
struct strbuf *path,
2134+
int baselen,
2135+
const struct pathspec *pathspec,
2136+
enum path_treatment state)
2137+
{
2138+
/* add the path to the appropriate result list */
2139+
switch (state) {
2140+
case path_excluded:
2141+
if (dir->flags & DIR_SHOW_IGNORED)
2142+
dir_add_name(dir, istate, path->buf, path->len);
2143+
else if ((dir->flags & DIR_SHOW_IGNORED_TOO) ||
2144+
((dir->flags & DIR_COLLECT_IGNORED) &&
2145+
exclude_matches_pathspec(path->buf, path->len,
2146+
pathspec)))
2147+
dir_add_ignored(dir, istate, path->buf, path->len);
2148+
break;
2149+
2150+
case path_untracked:
2151+
if (dir->flags & DIR_SHOW_IGNORED)
2152+
break;
2153+
dir_add_name(dir, istate, path->buf, path->len);
2154+
if (cdir->fdir)
2155+
add_untracked(untracked, path->buf + baselen);
2156+
break;
2157+
2158+
default:
2159+
break;
2160+
}
2161+
}
2162+
21262163
/*
21272164
* Read a directory tree. We currently ignore anything but
21282165
* directories, regular files and symlinks. That's because git
@@ -2147,6 +2184,15 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
21472184
struct untracked_cache_dir *untracked, int check_only,
21482185
int stop_at_first_file, const struct pathspec *pathspec)
21492186
{
2187+
/*
2188+
* WARNING WARNING WARNING:
2189+
*
2190+
* Any updates to the traversal logic here may need corresponding
2191+
* updates in treat_leading_path(). See the commit message for the
2192+
* commit adding this warning as well as the commit preceding it
2193+
* for details.
2194+
*/
2195+
21502196
struct cached_dir cdir;
21512197
enum path_treatment state, subdir_state, dir_state = path_none;
21522198
struct strbuf path = STRBUF_INIT;
@@ -2226,29 +2272,9 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
22262272
continue;
22272273
}
22282274

2229-
/* add the path to the appropriate result list */
2230-
switch (state) {
2231-
case path_excluded:
2232-
if (dir->flags & DIR_SHOW_IGNORED)
2233-
dir_add_name(dir, istate, path.buf, path.len);
2234-
else if ((dir->flags & DIR_SHOW_IGNORED_TOO) ||
2235-
((dir->flags & DIR_COLLECT_IGNORED) &&
2236-
exclude_matches_pathspec(path.buf, path.len,
2237-
pathspec)))
2238-
dir_add_ignored(dir, istate, path.buf, path.len);
2239-
break;
2240-
2241-
case path_untracked:
2242-
if (dir->flags & DIR_SHOW_IGNORED)
2243-
break;
2244-
dir_add_name(dir, istate, path.buf, path.len);
2245-
if (cdir.fdir)
2246-
add_untracked(untracked, path.buf + baselen);
2247-
break;
2248-
2249-
default:
2250-
break;
2251-
}
2275+
add_path_to_appropriate_result_list(dir, untracked, &cdir,
2276+
istate, &path, baselen,
2277+
pathspec, state);
22522278
}
22532279
close_cached_dir(&cdir);
22542280
out:
@@ -2278,41 +2304,104 @@ static int treat_leading_path(struct dir_struct *dir,
22782304
const char *path, int len,
22792305
const struct pathspec *pathspec)
22802306
{
2307+
/*
2308+
* WARNING WARNING WARNING:
2309+
*
2310+
* Any updates to the traversal logic here may need corresponding
2311+
* updates in treat_leading_path(). See the commit message for the
2312+
* commit adding this warning as well as the commit preceding it
2313+
* for details.
2314+
*/
2315+
22812316
struct strbuf sb = STRBUF_INIT;
2282-
int baselen, rc = 0;
2317+
int prevlen, baselen;
22832318
const char *cp;
2284-
int old_flags = dir->flags;
2319+
struct cached_dir cdir;
2320+
struct dirent *de;
2321+
enum path_treatment state = path_none;
2322+
2323+
/*
2324+
* For each directory component of path, we are going to check whether
2325+
* that path is relevant given the pathspec. For example, if path is
2326+
* foo/bar/baz/
2327+
* then we will ask treat_path() whether we should go into foo, then
2328+
* whether we should go into bar, then whether baz is relevant.
2329+
* Checking each is important because e.g. if path is
2330+
* .git/info/
2331+
* then we need to check .git to know we shouldn't traverse it.
2332+
* If the return from treat_path() is:
2333+
* * path_none, for any path, we return false.
2334+
* * path_recurse, for all path components, we return true
2335+
* * <anything else> for some intermediate component, we make sure
2336+
* to add that path to the relevant list but return false
2337+
* signifying that we shouldn't recurse into it.
2338+
*/
22852339

22862340
while (len && path[len - 1] == '/')
22872341
len--;
22882342
if (!len)
22892343
return 1;
2344+
2345+
/*
2346+
* We need a manufactured dirent with sufficient space to store a
2347+
* leading directory component of path in its d_name. Here, we
2348+
* assume that the dirent's d_name is either declared as
2349+
* char d_name[BIG_ENOUGH]
2350+
* or that it is declared at the end of the struct as
2351+
* char d_name[]
2352+
* For either case, padding with len+1 bytes at the end will ensure
2353+
* sufficient storage space.
2354+
*/
2355+
de = xcalloc(1, st_add3(sizeof(struct dirent), len, 1));
2356+
memset(&cdir, 0, sizeof(cdir));
2357+
cdir.de = de;
2358+
#if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
2359+
de->d_type = DT_DIR;
2360+
#endif
22902361
baselen = 0;
2291-
dir->flags &= ~DIR_SHOW_OTHER_DIRECTORIES;
2362+
prevlen = 0;
22922363
while (1) {
2293-
cp = path + baselen + !!baselen;
2364+
prevlen = baselen + !!baselen;
2365+
cp = path + prevlen;
22942366
cp = memchr(cp, '/', path + len - cp);
22952367
if (!cp)
22962368
baselen = len;
22972369
else
22982370
baselen = cp - path;
2299-
strbuf_setlen(&sb, 0);
2371+
strbuf_reset(&sb);
23002372
strbuf_add(&sb, path, baselen);
23012373
if (!is_directory(sb.buf))
23022374
break;
2303-
if (simplify_away(sb.buf, sb.len, pathspec))
2304-
break;
2305-
if (treat_one_path(dir, NULL, istate, &sb, baselen, pathspec,
2306-
DT_DIR, NULL) == path_none)
2375+
strbuf_reset(&sb);
2376+
strbuf_add(&sb, path, prevlen);
2377+
memcpy(de->d_name, path+prevlen, baselen-prevlen);
2378+
de->d_name[baselen-prevlen] = '\0';
2379+
state = treat_path(dir, NULL, &cdir, istate, &sb, prevlen,
2380+
pathspec);
2381+
if (state == path_untracked &&
2382+
get_dtype(cdir.de, istate, sb.buf, sb.len) == DT_DIR &&
2383+
(dir->flags & DIR_SHOW_IGNORED_TOO ||
2384+
do_match_pathspec(istate, pathspec, sb.buf, sb.len,
2385+
baselen, NULL, DO_MATCH_LEADING_PATHSPEC) == MATCHED_RECURSIVELY_LEADING_PATHSPEC)) {
2386+
add_path_to_appropriate_result_list(dir, NULL, &cdir,
2387+
istate,
2388+
&sb, baselen,
2389+
pathspec, state);
2390+
state = path_recurse;
2391+
}
2392+
2393+
if (state != path_recurse)
23072394
break; /* do not recurse into it */
2308-
if (len <= baselen) {
2309-
rc = 1;
2395+
if (len <= baselen)
23102396
break; /* finished checking */
2311-
}
23122397
}
2398+
add_path_to_appropriate_result_list(dir, NULL, &cdir, istate,
2399+
&sb, baselen, pathspec,
2400+
state);
2401+
2402+
free(de);
23132403
strbuf_release(&sb);
2314-
dir->flags = old_flags;
2315-
return rc;
2404+
return state == path_recurse;
23162405
}
23172406

23182407
static const char *get_ident_string(void)

0 commit comments

Comments
 (0)