@@ -1129,14 +1129,32 @@ static int safe_directory_cb(const char *key, const char *value, void *d)
11291129 return 0 ;
11301130}
11311131
1132- static int ensure_valid_ownership (const char * path )
1132+ /*
1133+ * Check if a repository is safe, by verifying the ownership of the
1134+ * worktree (if any), the git directory, and the gitfile (if any).
1135+ *
1136+ * Exemptions for known-safe repositories can be added via `safe.directory`
1137+ * config settings; for non-bare repositories, their worktree needs to be
1138+ * added, for bare ones their git directory.
1139+ */
1140+ static int ensure_valid_ownership (const char * gitfile ,
1141+ const char * worktree , const char * gitdir )
11331142{
1134- struct safe_directory_data data = { .path = path };
1143+ struct safe_directory_data data = {
1144+ .path = worktree ? worktree : gitdir
1145+ };
11351146
11361147 if (!git_env_bool ("GIT_TEST_ASSUME_DIFFERENT_OWNER" , 0 ) &&
1137- is_path_owned_by_current_user (path ))
1148+ (!gitfile || is_path_owned_by_current_user (gitfile )) &&
1149+ (!worktree || is_path_owned_by_current_user (worktree )) &&
1150+ (!gitdir || is_path_owned_by_current_user (gitdir )))
11381151 return 1 ;
11391152
1153+ /*
1154+ * data.path is the "path" that identifies the repository and it is
1155+ * constant regardless of what failed above. data.is_safe should be
1156+ * initialized to false, and might be changed by the callback.
1157+ */
11401158 read_very_early_config (safe_directory_cb , & data );
11411159
11421160 return data .is_safe ;
@@ -1224,6 +1242,8 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
12241242 current_device = get_device_or_die (dir -> buf , NULL , 0 );
12251243 for (;;) {
12261244 int offset = dir -> len , error_code = 0 ;
1245+ char * gitdir_path = NULL ;
1246+ char * gitfile = NULL ;
12271247
12281248 if (offset > min_offset )
12291249 strbuf_addch (dir , '/' );
@@ -1234,21 +1254,50 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
12341254 if (die_on_error ||
12351255 error_code == READ_GITFILE_ERR_NOT_A_FILE ) {
12361256 /* NEEDSWORK: fail if .git is not file nor dir */
1237- if (is_git_directory (dir -> buf ))
1257+ if (is_git_directory (dir -> buf )) {
12381258 gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT ;
1259+ gitdir_path = xstrdup (dir -> buf );
1260+ }
12391261 } else if (error_code != READ_GITFILE_ERR_STAT_FAILED )
12401262 return GIT_DIR_INVALID_GITFILE ;
1241- }
1263+ } else
1264+ gitfile = xstrdup (dir -> buf );
1265+ /*
1266+ * Earlier, we tentatively added DEFAULT_GIT_DIR_ENVIRONMENT
1267+ * to check that directory for a repository.
1268+ * Now trim that tentative addition away, because we want to
1269+ * focus on the real directory we are in.
1270+ */
12421271 strbuf_setlen (dir , offset );
12431272 if (gitdirenv ) {
1244- if (!ensure_valid_ownership (dir -> buf ))
1245- return GIT_DIR_INVALID_OWNERSHIP ;
1246- strbuf_addstr (gitdir , gitdirenv );
1247- return GIT_DIR_DISCOVERED ;
1273+ enum discovery_result ret ;
1274+
1275+ if (ensure_valid_ownership (gitfile ,
1276+ dir -> buf ,
1277+ (gitdir_path ? gitdir_path : gitdirenv ))) {
1278+ strbuf_addstr (gitdir , gitdirenv );
1279+ ret = GIT_DIR_DISCOVERED ;
1280+ } else
1281+ ret = GIT_DIR_INVALID_OWNERSHIP ;
1282+
1283+ /*
1284+ * Earlier, during discovery, we might have allocated
1285+ * string copies for gitdir_path or gitfile so make
1286+ * sure we don't leak by freeing them now, before
1287+ * leaving the loop and function.
1288+ *
1289+ * Note: gitdirenv will be non-NULL whenever these are
1290+ * allocated, therefore we need not take care of releasing
1291+ * them outside of this conditional block.
1292+ */
1293+ free (gitdir_path );
1294+ free (gitfile );
1295+
1296+ return ret ;
12481297 }
12491298
12501299 if (is_git_directory (dir -> buf )) {
1251- if (!ensure_valid_ownership (dir -> buf ))
1300+ if (!ensure_valid_ownership (NULL , NULL , dir -> buf ))
12521301 return GIT_DIR_INVALID_OWNERSHIP ;
12531302 strbuf_addstr (gitdir , "." );
12541303 return GIT_DIR_BARE ;
@@ -1386,7 +1435,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
13861435 struct strbuf quoted = STRBUF_INIT ;
13871436
13881437 sq_quote_buf_pretty (& quoted , dir .buf );
1389- die (_ ("unsafe repository ( '%s' is owned by someone else) \n"
1438+ die (_ ("detected dubious ownership in repository at '%s'\n"
13901439 "To add an exception for this directory, call:\n"
13911440 "\n"
13921441 "\tgit config --global --add safe.directory %s" ),
0 commit comments