setup: add discover_git_directory_reason()

There are many reasons why discovering a Git directory may fail. In particular, 8959555cee7 (setup_git_directory(): add an owner check for the top-level directory, 2022-03-02) added ownership checks as a security precaution. Callers attempting to set up a Git directory may want to inform the user about the reason for the failure. For that, expose the enum discovery_result from within setup.c and move it into cache.h where discover_git_directory() is defined. I initially wanted to change the return type of discover_git_directory() to be this enum, but several callers rely upon the "zero means success". The two problems with this are: 1. The zero value of the enum is actually GIT_DIR_NONE, so nonpositive results are errors. 2. There are multiple successful states; positive results are successful. It is worth noting that GIT_DIR_NONE is not returned, so we remove this option from the enum. We must be careful to keep the successful reasons as positive values, so they are given explicit positive values. Instead of updating all callers immediately, add a new method, discover_git_directory_reason(), and convert discover_git_directory() to be a thin shim on top of it. One thing that is important to note is that discover_git_directory() previously returned -1 on error, so let's continue that into the future. There is only one caller (in scalar.c) that depends on that signedness instead of a non-zero check, so clean that up, too. Because there are extra checks that discover_git_directory_reason() does after setup_git_directory_gently_1(), there are other modes that can be returned for failure states. Add these modes to the enum, but be sure to explicitly add them as BUG() states in the switch of setup_git_directory_gently(). Signed-off-by: Derrick Stolee <derrickstolee@github.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Derrick Stolee committed Aug 28, 2023 at 13:52 UTC 26ae8da683bd2a054c5807ff676336df43b40b03
2 files changed +44 -25
setup.c
+12 -22
@@ -1212,19 +1212,6 @@ static const char *allowed_bare_repo_to_string(
1212 return NULL;
1213 }
1214
1215 -enum discovery_result {
1216 - GIT_DIR_NONE = 0,
1217 - GIT_DIR_EXPLICIT,
1218 - GIT_DIR_DISCOVERED,
1219 - GIT_DIR_BARE,
1220 - /* these are errors */
1221 - GIT_DIR_HIT_CEILING = -1,
1222 - GIT_DIR_HIT_MOUNT_POINT = -2,
1223 - GIT_DIR_INVALID_GITFILE = -3,
1224 - GIT_DIR_INVALID_OWNERSHIP = -4,
1225 - GIT_DIR_DISALLOWED_BARE = -5,
1226 -};
1227 -
1215 /*
1216 * We cannot decide in this function whether we are in the work tree or
1217 * not, since the config can only be read _after_ this function was called.
@@ -1376,21 +1363,23 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
1363 }
1364 }
1365
1379 -int discover_git_directory(struct strbuf *commondir,
1380 - struct strbuf *gitdir)
1366 +enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
1367 + struct strbuf *gitdir)
1368 {
1369 struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT;
1370 size_t gitdir_offset = gitdir->len, cwd_len;
1371 size_t commondir_offset = commondir->len;
1372 struct repository_format candidate = REPOSITORY_FORMAT_INIT;
1373 + enum discovery_result result;
1374
1375 if (strbuf_getcwd(&dir))
1388 - return -1;
1376 + return GIT_DIR_CWD_FAILURE;
1377
1378 cwd_len = dir.len;
1391 - if (setup_git_directory_gently_1(&dir, gitdir, NULL, 0) <= 0) {
1379 + result = setup_git_directory_gently_1(&dir, gitdir, NULL, 0);
1380 + if (result <= 0) {
1381 strbuf_release(&dir);
1393 - return -1;
1382 + return result;
1383 }
1384
1385 /*
@@ -1420,7 +1409,7 @@ int discover_git_directory(struct strbuf *commondir,
1409 strbuf_setlen(commondir, commondir_offset);
1410 strbuf_setlen(gitdir, gitdir_offset);
1411 clear_repository_format(&candidate);
1423 - return -1;
1412 + return GIT_DIR_INVALID_FORMAT;
1413 }
1414
1415 /* take ownership of candidate.partial_clone */
@@ -1429,7 +1418,7 @@ int discover_git_directory(struct strbuf *commondir,
1418 candidate.partial_clone = NULL;
1419
1420 clear_repository_format(&candidate);
1432 - return 0;
1421 + return result;
1422 }
1423
1424 const char *setup_git_directory_gently(int *nongit_ok)
@@ -1511,10 +1500,11 @@ const char *setup_git_directory_gently(int *nongit_ok)
1500 }
1501 *nongit_ok = 1;
1502 break;
1514 - case GIT_DIR_NONE:
1503 + case GIT_DIR_CWD_FAILURE:
1504 + case GIT_DIR_INVALID_FORMAT:
1505 /*
1506 * As a safeguard against setup_git_directory_gently_1 returning
1517 - * this value, fallthrough to BUG. Otherwise it is possible to
1507 + * these values, fallthrough to BUG. Otherwise it is possible to
1508 * set startup_info->have_repository to 1 when we did nothing to
1509 * find a repository.
1510 */
setup.h
+32 -3
@@ -42,16 +42,45 @@ const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
42 #define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
43
44 void setup_work_tree(void);
45 +
46 +/*
47 + * discover_git_directory_reason() is similar to discover_git_directory(),
48 + * except it returns an enum value instead. It is important to note that
49 + * a zero-valued return here is actually GIT_DIR_NONE, which is different
50 + * from discover_git_directory.
51 + */
52 +enum discovery_result {
53 + GIT_DIR_EXPLICIT = 1,
54 + GIT_DIR_DISCOVERED = 2,
55 + GIT_DIR_BARE = 3,
56 + /* these are errors */
57 + GIT_DIR_HIT_CEILING = -1,
58 + GIT_DIR_HIT_MOUNT_POINT = -2,
59 + GIT_DIR_INVALID_GITFILE = -3,
60 + GIT_DIR_INVALID_OWNERSHIP = -4,
61 + GIT_DIR_DISALLOWED_BARE = -5,
62 + GIT_DIR_INVALID_FORMAT = -6,
63 + GIT_DIR_CWD_FAILURE = -7,
64 +};
65 +enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
66 + struct strbuf *gitdir);
67 +
68 /*
69 * Find the commondir and gitdir of the repository that contains the current
70 * working directory, without changing the working directory or other global
71 * state. The result is appended to commondir and gitdir. If the discovered
72 * gitdir does not correspond to a worktree, then 'commondir' and 'gitdir' will
73 * both have the same result appended to the buffer. The return value is
51 - * either 0 upon success and non-zero if no repository was found.
74 + * either 0 upon success and -1 if no repository was found.
75 */
53 -int discover_git_directory(struct strbuf *commondir,
54 - struct strbuf *gitdir);
76 +static inline int discover_git_directory(struct strbuf *commondir,
77 + struct strbuf *gitdir)
78 +{
79 + if (discover_git_directory_reason(commondir, gitdir) <= 0)
80 + return -1;
81 + return 0;
82 +}
83 +
84 const char *setup_git_directory_gently(int *);
85 const char *setup_git_directory(void);
86 char *prefix_path(const char *prefix, int len, const char *path);