@@ -1054,14 +1054,32 @@ static int safe_directory_cb(const char *key, const char *value, void *d)
10541054 return 0 ;
10551055}
10561056
1057- static int ensure_valid_ownership (const char * path )
1057+ /*
1058+ * Check if a repository is safe, by verifying the ownership of the
1059+ * worktree (if any), the git directory, and the gitfile (if any).
1060+ *
1061+ * Exemptions for known-safe repositories can be added via `safe.directory`
1062+ * config settings; for non-bare repositories, their worktree needs to be
1063+ * added, for bare ones their git directory.
1064+ */
1065+ static int ensure_valid_ownership (const char * gitfile ,
1066+ const char * worktree , const char * gitdir )
10581067{
1059- struct safe_directory_data data = { .path = path };
1068+ struct safe_directory_data data = {
1069+ .path = worktree ? worktree : gitdir
1070+ };
10601071
10611072 if (!git_env_bool ("GIT_TEST_ASSUME_DIFFERENT_OWNER" , 0 ) &&
1062- is_path_owned_by_current_user (path ))
1073+ (!gitfile || is_path_owned_by_current_user (gitfile )) &&
1074+ (!worktree || is_path_owned_by_current_user (worktree )) &&
1075+ (!gitdir || is_path_owned_by_current_user (gitdir )))
10631076 return 1 ;
10641077
1078+ /*
1079+ * data.path is the "path" that identifies the repository and it is
1080+ * constant regardless of what failed above. data.is_safe should be
1081+ * initialized to false, and might be changed by the callback.
1082+ */
10651083 read_very_early_config (safe_directory_cb , & data );
10661084
10671085 return data .is_safe ;
@@ -1149,6 +1167,8 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
11491167 current_device = get_device_or_die (dir -> buf , NULL , 0 );
11501168 for (;;) {
11511169 int offset = dir -> len , error_code = 0 ;
1170+ char * gitdir_path = NULL ;
1171+ char * gitfile = NULL ;
11521172
11531173 if (offset > min_offset )
11541174 strbuf_addch (dir , '/' );
@@ -1159,21 +1179,50 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
11591179 if (die_on_error ||
11601180 error_code == READ_GITFILE_ERR_NOT_A_FILE ) {
11611181 /* NEEDSWORK: fail if .git is not file nor dir */
1162- if (is_git_directory (dir -> buf ))
1182+ if (is_git_directory (dir -> buf )) {
11631183 gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT ;
1184+ gitdir_path = xstrdup (dir -> buf );
1185+ }
11641186 } else if (error_code != READ_GITFILE_ERR_STAT_FAILED )
11651187 return GIT_DIR_INVALID_GITFILE ;
1166- }
1188+ } else
1189+ gitfile = xstrdup (dir -> buf );
1190+ /*
1191+ * Earlier, we tentatively added DEFAULT_GIT_DIR_ENVIRONMENT
1192+ * to check that directory for a repository.
1193+ * Now trim that tentative addition away, because we want to
1194+ * focus on the real directory we are in.
1195+ */
11671196 strbuf_setlen (dir , offset );
11681197 if (gitdirenv ) {
1169- if (!ensure_valid_ownership (dir -> buf ))
1170- return GIT_DIR_INVALID_OWNERSHIP ;
1171- strbuf_addstr (gitdir , gitdirenv );
1172- return GIT_DIR_DISCOVERED ;
1198+ enum discovery_result ret ;
1199+
1200+ if (ensure_valid_ownership (gitfile ,
1201+ dir -> buf ,
1202+ (gitdir_path ? gitdir_path : gitdirenv ))) {
1203+ strbuf_addstr (gitdir , gitdirenv );
1204+ ret = GIT_DIR_DISCOVERED ;
1205+ } else
1206+ ret = GIT_DIR_INVALID_OWNERSHIP ;
1207+
1208+ /*
1209+ * Earlier, during discovery, we might have allocated
1210+ * string copies for gitdir_path or gitfile so make
1211+ * sure we don't leak by freeing them now, before
1212+ * leaving the loop and function.
1213+ *
1214+ * Note: gitdirenv will be non-NULL whenever these are
1215+ * allocated, therefore we need not take care of releasing
1216+ * them outside of this conditional block.
1217+ */
1218+ free (gitdir_path );
1219+ free (gitfile );
1220+
1221+ return ret ;
11731222 }
11741223
11751224 if (is_git_directory (dir -> buf )) {
1176- if (!ensure_valid_ownership (dir -> buf ))
1225+ if (!ensure_valid_ownership (NULL , NULL , dir -> buf ))
11771226 return GIT_DIR_INVALID_OWNERSHIP ;
11781227 strbuf_addstr (gitdir , "." );
11791228 return GIT_DIR_BARE ;
@@ -1306,7 +1355,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
13061355 struct strbuf quoted = STRBUF_INIT ;
13071356
13081357 sq_quote_buf_pretty (& quoted , dir .buf );
1309- die (_ ("unsafe repository ( '%s' is owned by someone else) \n"
1358+ die (_ ("detected dubious ownership in repository at '%s'\n"
13101359 "To add an exception for this directory, call:\n"
13111360 "\n"
13121361 "\tgit config --global --add safe.directory %s" ),
0 commit comments