@@ -1120,14 +1120,32 @@ static int safe_directory_cb(const char *key, const char *value, void *d)
11201120 return 0 ;
11211121}
11221122
1123- static int ensure_valid_ownership (const char * path )
1123+ /*
1124+ * Check if a repository is safe, by verifying the ownership of the
1125+ * worktree (if any), the git directory, and the gitfile (if any).
1126+ *
1127+ * Exemptions for known-safe repositories can be added via `safe.directory`
1128+ * config settings; for non-bare repositories, their worktree needs to be
1129+ * added, for bare ones their git directory.
1130+ */
1131+ static int ensure_valid_ownership (const char * gitfile ,
1132+ const char * worktree , const char * gitdir )
11241133{
1125- struct safe_directory_data data = { .path = path };
1134+ struct safe_directory_data data = {
1135+ .path = worktree ? worktree : gitdir
1136+ };
11261137
11271138 if (!git_env_bool ("GIT_TEST_ASSUME_DIFFERENT_OWNER" , 0 ) &&
1128- is_path_owned_by_current_user (path ))
1139+ (!gitfile || is_path_owned_by_current_user (gitfile )) &&
1140+ (!worktree || is_path_owned_by_current_user (worktree )) &&
1141+ (!gitdir || is_path_owned_by_current_user (gitdir )))
11291142 return 1 ;
11301143
1144+ /*
1145+ * data.path is the "path" that identifies the repository and it is
1146+ * constant regardless of what failed above. data.is_safe should be
1147+ * initialized to false, and might be changed by the callback.
1148+ */
11311149 read_very_early_config (safe_directory_cb , & data );
11321150
11331151 return data .is_safe ;
@@ -1215,6 +1233,8 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
12151233 current_device = get_device_or_die (dir -> buf , NULL , 0 );
12161234 for (;;) {
12171235 int offset = dir -> len , error_code = 0 ;
1236+ char * gitdir_path = NULL ;
1237+ char * gitfile = NULL ;
12181238
12191239 if (offset > min_offset )
12201240 strbuf_addch (dir , '/' );
@@ -1225,21 +1245,50 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
12251245 if (die_on_error ||
12261246 error_code == READ_GITFILE_ERR_NOT_A_FILE ) {
12271247 /* NEEDSWORK: fail if .git is not file nor dir */
1228- if (is_git_directory (dir -> buf ))
1248+ if (is_git_directory (dir -> buf )) {
12291249 gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT ;
1250+ gitdir_path = xstrdup (dir -> buf );
1251+ }
12301252 } else if (error_code != READ_GITFILE_ERR_STAT_FAILED )
12311253 return GIT_DIR_INVALID_GITFILE ;
1232- }
1254+ } else
1255+ gitfile = xstrdup (dir -> buf );
1256+ /*
1257+ * Earlier, we tentatively added DEFAULT_GIT_DIR_ENVIRONMENT
1258+ * to check that directory for a repository.
1259+ * Now trim that tentative addition away, because we want to
1260+ * focus on the real directory we are in.
1261+ */
12331262 strbuf_setlen (dir , offset );
12341263 if (gitdirenv ) {
1235- if (!ensure_valid_ownership (dir -> buf ))
1236- return GIT_DIR_INVALID_OWNERSHIP ;
1237- strbuf_addstr (gitdir , gitdirenv );
1238- return GIT_DIR_DISCOVERED ;
1264+ enum discovery_result ret ;
1265+
1266+ if (ensure_valid_ownership (gitfile ,
1267+ dir -> buf ,
1268+ (gitdir_path ? gitdir_path : gitdirenv ))) {
1269+ strbuf_addstr (gitdir , gitdirenv );
1270+ ret = GIT_DIR_DISCOVERED ;
1271+ } else
1272+ ret = GIT_DIR_INVALID_OWNERSHIP ;
1273+
1274+ /*
1275+ * Earlier, during discovery, we might have allocated
1276+ * string copies for gitdir_path or gitfile so make
1277+ * sure we don't leak by freeing them now, before
1278+ * leaving the loop and function.
1279+ *
1280+ * Note: gitdirenv will be non-NULL whenever these are
1281+ * allocated, therefore we need not take care of releasing
1282+ * them outside of this conditional block.
1283+ */
1284+ free (gitdir_path );
1285+ free (gitfile );
1286+
1287+ return ret ;
12391288 }
12401289
12411290 if (is_git_directory (dir -> buf )) {
1242- if (!ensure_valid_ownership (dir -> buf ))
1291+ if (!ensure_valid_ownership (NULL , NULL , dir -> buf ))
12431292 return GIT_DIR_INVALID_OWNERSHIP ;
12441293 strbuf_addstr (gitdir , "." );
12451294 return GIT_DIR_BARE ;
@@ -1377,7 +1426,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
13771426 struct strbuf quoted = STRBUF_INIT ;
13781427
13791428 sq_quote_buf_pretty (& quoted , dir .buf );
1380- die (_ ("unsafe repository ( '%s' is owned by someone else) \n"
1429+ die (_ ("detected dubious ownership in repository at '%s'\n"
13811430 "To add an exception for this directory, call:\n"
13821431 "\n"
13831432 "\tgit config --global --add safe.directory %s" ),
0 commit comments