Skip to content

Commit 57edfa3

Browse files
committed
Merge branch 'ty/setup-error-tightening' into seen
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: SQUASH??? read_gitfile(): group ENOENT and ENOTDIR into a single MISSING error setup: improve error diagnosis for invalid .git files
2 parents 15146eb + 043cae6 commit 57edfa3

6 files changed

Lines changed: 110 additions & 15 deletions

File tree

setup.c

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -923,10 +923,14 @@ int verify_repository_format(const struct repository_format *format,
923923
void read_gitfile_error_die(int error_code, const char *path, const char *dir)
924924
{
925925
switch (error_code) {
926-
case READ_GITFILE_ERR_STAT_FAILED:
927-
case READ_GITFILE_ERR_NOT_A_FILE:
926+
case READ_GITFILE_ERR_STAT_MISSING:
927+
case READ_GITFILE_ERR_IS_A_DIR:
928928
/* non-fatal; follow return path */
929929
break;
930+
case READ_GITFILE_ERR_STAT_FAILED:
931+
die(_("error reading %s"), path);
932+
case READ_GITFILE_ERR_NOT_A_FILE:
933+
die(_("not a regular file: %s"), path);
930934
case READ_GITFILE_ERR_OPEN_FAILED:
931935
die_errno(_("error opening '%s'"), path);
932936
case READ_GITFILE_ERR_TOO_LARGE:
@@ -967,8 +971,14 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
967971
static struct strbuf realpath = STRBUF_INIT;
968972

969973
if (stat(path, &st)) {
970-
/* NEEDSWORK: discern between ENOENT vs other errors */
971-
error_code = READ_GITFILE_ERR_STAT_FAILED;
974+
if (errno == ENOENT || errno == ENOTDIR)
975+
error_code = READ_GITFILE_ERR_STAT_MISSING;
976+
else
977+
error_code = READ_GITFILE_ERR_STAT_FAILED;
978+
goto cleanup_return;
979+
}
980+
if (S_ISDIR(st.st_mode)) {
981+
error_code = READ_GITFILE_ERR_IS_A_DIR;
972982
goto cleanup_return;
973983
}
974984
if (!S_ISREG(st.st_mode)) {
@@ -1604,20 +1614,28 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
16041614
if (offset > min_offset)
16051615
strbuf_addch(dir, '/');
16061616
strbuf_addstr(dir, DEFAULT_GIT_DIR_ENVIRONMENT);
1607-
gitdirenv = read_gitfile_gently(dir->buf, die_on_error ?
1608-
NULL : &error_code);
1617+
gitdirenv = read_gitfile_gently(dir->buf, &error_code);
16091618
if (!gitdirenv) {
1610-
if (die_on_error ||
1611-
error_code == READ_GITFILE_ERR_NOT_A_FILE) {
1612-
/* NEEDSWORK: fail if .git is not file nor dir */
1619+
switch (error_code) {
1620+
case READ_GITFILE_ERR_STAT_MISSING:
1621+
/* no .git in this directory, move on */
1622+
break;
1623+
case READ_GITFILE_ERR_IS_A_DIR:
16131624
if (is_git_directory(dir->buf)) {
16141625
gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
16151626
gitdir_path = xstrdup(dir->buf);
16161627
}
1617-
} else if (error_code != READ_GITFILE_ERR_STAT_FAILED)
1618-
return GIT_DIR_INVALID_GITFILE;
1619-
} else
1628+
/* NEEDSWORK: should we catch a directory .git that is not a git directory here? */
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 {
16201637
gitfile = xstrdup(dir->buf);
1638+
}
16211639
/*
16221640
* Earlier, we tentatively added DEFAULT_GIT_DIR_ENVIRONMENT
16231641
* 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_STAT_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
@@ -2572,7 +2572,7 @@ void absorb_git_dir_into_superproject(const char *path,
25722572
const struct submodule *sub;
25732573
struct strbuf sub_gitdir = STRBUF_INIT;
25742574

2575-
if (err_code == READ_GITFILE_ERR_STAT_FAILED) {
2575+
if (err_code == READ_GITFILE_ERR_STAT_MISSING) {
25762576
/* unpopulated as expected */
25772577
strbuf_release(&gitdir);
25782578
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: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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+
mkdir -p parent/link-to-dir &&
20+
(
21+
cd parent/link-to-dir &&
22+
git init real-repo &&
23+
ln -s real-repo/.git .git &&
24+
git rev-parse --git-dir >actual &&
25+
echo .git >expect &&
26+
test_cmp expect actual
27+
)
28+
'
29+
30+
test_expect_success PIPE 'setup: .git as a FIFO (named pipe) is rejected' '
31+
mkdir -p parent/fifo-trap &&
32+
(
33+
cd parent/fifo-trap &&
34+
mkfifo .git &&
35+
test_must_fail git rev-parse --git-dir 2>stderr &&
36+
grep "not a regular file" stderr
37+
)
38+
'
39+
40+
test_expect_success SYMLINKS,PIPE 'setup: .git as a symlink to a FIFO is rejected' '
41+
mkdir -p parent/symlink-fifo-trap &&
42+
(
43+
cd parent/symlink-fifo-trap &&
44+
mkfifo target-fifo &&
45+
ln -s target-fifo .git &&
46+
test_must_fail git rev-parse --git-dir 2>stderr &&
47+
grep "not a regular file" stderr
48+
)
49+
'
50+
51+
test_expect_success 'setup: .git with garbage content is rejected' '
52+
mkdir -p parent/garbage-trap &&
53+
(
54+
cd parent/garbage-trap &&
55+
echo "garbage" >.git &&
56+
test_must_fail git rev-parse --git-dir 2>stderr &&
57+
grep "invalid gitfile format" stderr
58+
)
59+
'
60+
61+
test_expect_success 'setup: .git as an empty directory is ignored' '
62+
mkdir -p parent/empty-dir &&
63+
(
64+
cd parent/empty-dir &&
65+
mkdir .git &&
66+
git rev-parse --git-dir >actual &&
67+
echo "$TRASH_DIRECTORY/parent/.git" >expect &&
68+
test_cmp expect actual
69+
)
70+
'
71+
72+
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)