Skip to content

Commit 93670d2

Browse files
committed
Merge branch 'ty/setup-error-tightening' into jch
While discovering a ".git" directory, the code treats any stat() failure as a sign that a filesystem entity .git does not exist there, and ignores ".git" that is not a "gitdir" file or a directory. The code has been tightened to notice and report filesystem corruption better. * ty/setup-error-tightening: setup: improve error diagnosis for invalid .git files
2 parents 499ca87 + 994379e commit 93670d2

6 files changed

Lines changed: 118 additions & 15 deletions

File tree

setup.c

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -920,10 +920,14 @@ int verify_repository_format(const struct repository_format *format,
920920
void read_gitfile_error_die(int error_code, const char *path, const char *dir)
921921
{
922922
switch (error_code) {
923-
case READ_GITFILE_ERR_STAT_FAILED:
924-
case READ_GITFILE_ERR_NOT_A_FILE:
923+
case READ_GITFILE_ERR_MISSING:
924+
case READ_GITFILE_ERR_IS_A_DIR:
925925
/* non-fatal; follow return path */
926926
break;
927+
case READ_GITFILE_ERR_STAT_FAILED:
928+
die(_("error reading '%s'"), path);
929+
case READ_GITFILE_ERR_NOT_A_FILE:
930+
die(_("not a regular file: '%s'"), path);
927931
case READ_GITFILE_ERR_OPEN_FAILED:
928932
die_errno(_("error opening '%s'"), path);
929933
case READ_GITFILE_ERR_TOO_LARGE:
@@ -964,8 +968,14 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
964968
static struct strbuf realpath = STRBUF_INIT;
965969

966970
if (stat(path, &st)) {
967-
/* NEEDSWORK: discern between ENOENT vs other errors */
968-
error_code = READ_GITFILE_ERR_STAT_FAILED;
971+
if (errno == ENOENT || errno == ENOTDIR)
972+
error_code = READ_GITFILE_ERR_MISSING;
973+
else
974+
error_code = READ_GITFILE_ERR_STAT_FAILED;
975+
goto cleanup_return;
976+
}
977+
if (S_ISDIR(st.st_mode)) {
978+
error_code = READ_GITFILE_ERR_IS_A_DIR;
969979
goto cleanup_return;
970980
}
971981
if (!S_ISREG(st.st_mode)) {
@@ -1601,20 +1611,31 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
16011611
if (offset > min_offset)
16021612
strbuf_addch(dir, '/');
16031613
strbuf_addstr(dir, DEFAULT_GIT_DIR_ENVIRONMENT);
1604-
gitdirenv = read_gitfile_gently(dir->buf, die_on_error ?
1605-
NULL : &error_code);
1614+
gitdirenv = read_gitfile_gently(dir->buf, &error_code);
16061615
if (!gitdirenv) {
1607-
if (die_on_error ||
1608-
error_code == READ_GITFILE_ERR_NOT_A_FILE) {
1609-
/* NEEDSWORK: fail if .git is not file nor dir */
1616+
switch (error_code) {
1617+
case READ_GITFILE_ERR_MISSING:
1618+
/* no .git in this directory, move on */
1619+
break;
1620+
case READ_GITFILE_ERR_IS_A_DIR:
16101621
if (is_git_directory(dir->buf)) {
16111622
gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
16121623
gitdir_path = xstrdup(dir->buf);
16131624
}
1614-
} else if (error_code != READ_GITFILE_ERR_STAT_FAILED)
1615-
return GIT_DIR_INVALID_GITFILE;
1616-
} else
1625+
/*
1626+
* NEEDSWORK: should we catch a directory .git
1627+
* that is not a git directory here?
1628+
*/
1629+
break;
1630+
default:
1631+
if (die_on_error || error_code == READ_GITFILE_ERR_NOT_A_FILE)
1632+
read_gitfile_error_die(error_code, dir->buf, NULL);
1633+
else
1634+
return GIT_DIR_INVALID_GITFILE;
1635+
}
1636+
} else {
16171637
gitfile = xstrdup(dir->buf);
1638+
}
16181639
/*
16191640
* Earlier, we tentatively added DEFAULT_GIT_DIR_ENVIRONMENT
16201641
* to check that directory for a repository.

setup.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ int is_nonbare_repository_dir(struct strbuf *path);
3636
#define READ_GITFILE_ERR_NO_PATH 6
3737
#define READ_GITFILE_ERR_NOT_A_REPO 7
3838
#define READ_GITFILE_ERR_TOO_LARGE 8
39+
#define READ_GITFILE_ERR_MISSING 9
40+
#define READ_GITFILE_ERR_IS_A_DIR 10
3941
void read_gitfile_error_die(int error_code, const char *path, const char *dir);
4042
const char *read_gitfile_gently(const char *path, int *return_error_code);
4143
#define read_gitfile(path) read_gitfile_gently((path), NULL)

submodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2559,7 +2559,7 @@ void absorb_git_dir_into_superproject(const char *path,
25592559
const struct submodule *sub;
25602560
struct strbuf sub_gitdir = STRBUF_INIT;
25612561

2562-
if (err_code == READ_GITFILE_ERR_STAT_FAILED) {
2562+
if (err_code == READ_GITFILE_ERR_MISSING) {
25632563
/* unpopulated as expected */
25642564
strbuf_release(&gitdir);
25652565
return;

t/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ integration_tests = [
8181
't0006-date.sh',
8282
't0007-git-var.sh',
8383
't0008-ignores.sh',
84+
't0009-git-dir-validation.sh',
8485
't0010-racy-git.sh',
8586
't0012-help.sh',
8687
't0013-sha1dc.sh',

t/t0009-git-dir-validation.sh

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#!/bin/sh
2+
3+
test_description='setup: validation of .git file/directory types
4+
5+
Verify that setup_git_directory() correctly handles:
6+
1. Valid .git directories (including symlinks to them).
7+
2. Invalid .git files (FIFOs, sockets) by erroring out.
8+
3. Invalid .git files (garbage) by erroring out.
9+
'
10+
11+
. ./test-lib.sh
12+
13+
test_expect_success 'setup: create parent git repository' '
14+
git init parent &&
15+
test_commit -C parent "root-commit"
16+
'
17+
18+
test_expect_success SYMLINKS 'setup: .git as a symlink to a directory is valid' '
19+
test_when_finished "rm -rf parent/link-to-dir" &&
20+
mkdir -p parent/link-to-dir &&
21+
(
22+
cd parent/link-to-dir &&
23+
git init real-repo &&
24+
ln -s real-repo/.git .git &&
25+
git rev-parse --git-dir >actual &&
26+
echo .git >expect &&
27+
test_cmp expect actual
28+
)
29+
'
30+
31+
test_expect_success PIPE 'setup: .git as a FIFO (named pipe) is rejected' '
32+
test_when_finished "rm -rf parent/fifo-trap" &&
33+
mkdir -p parent/fifo-trap &&
34+
(
35+
cd parent/fifo-trap &&
36+
mkfifo .git &&
37+
test_must_fail git rev-parse --git-dir 2>stderr &&
38+
grep "not a regular file" stderr
39+
)
40+
'
41+
42+
test_expect_success SYMLINKS,PIPE 'setup: .git as a symlink to a FIFO is rejected' '
43+
test_when_finished "rm -rf parent/symlink-fifo-trap" &&
44+
mkdir -p parent/symlink-fifo-trap &&
45+
(
46+
cd parent/symlink-fifo-trap &&
47+
mkfifo target-fifo &&
48+
ln -s target-fifo .git &&
49+
test_must_fail git rev-parse --git-dir 2>stderr &&
50+
grep "not a regular file" stderr
51+
)
52+
'
53+
54+
test_expect_success 'setup: .git with garbage content is rejected' '
55+
test_when_finished "rm -rf parent/garbage-trap" &&
56+
mkdir -p parent/garbage-trap &&
57+
(
58+
cd parent/garbage-trap &&
59+
echo "garbage" >.git &&
60+
test_must_fail git rev-parse --git-dir 2>stderr &&
61+
grep "invalid gitfile format" stderr
62+
)
63+
'
64+
65+
test_expect_success 'setup: .git as an empty directory is ignored' '
66+
test_when_finished "rm -rf parent/empty-dir" &&
67+
mkdir -p parent/empty-dir &&
68+
(
69+
cd parent/empty-dir &&
70+
mkdir .git &&
71+
git rev-parse --git-dir >actual &&
72+
echo "$TRASH_DIRECTORY/parent/.git" >expect &&
73+
test_cmp expect actual
74+
)
75+
'
76+
77+
test_done

worktree.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,8 @@ static void repair_gitfile(struct worktree *wt,
673673
}
674674
}
675675

676-
if (err == READ_GITFILE_ERR_NOT_A_FILE)
676+
if (err == READ_GITFILE_ERR_NOT_A_FILE ||
677+
err == READ_GITFILE_ERR_IS_A_DIR)
677678
fn(1, wt->path, _(".git is not a file"), cb_data);
678679
else if (err)
679680
repair = _(".git file broken");
@@ -853,7 +854,8 @@ void repair_worktree_at_path(const char *path,
853854
strbuf_addstr(&backlink, dotgit_contents);
854855
strbuf_realpath_forgiving(&backlink, backlink.buf, 0);
855856
}
856-
} else if (err == READ_GITFILE_ERR_NOT_A_FILE) {
857+
} else if (err == READ_GITFILE_ERR_NOT_A_FILE ||
858+
err == READ_GITFILE_ERR_IS_A_DIR) {
857859
fn(1, dotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
858860
goto done;
859861
} else if (err == READ_GITFILE_ERR_NOT_A_REPO) {

0 commit comments

Comments
 (0)