git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 5/5] rename_ref(): fix a mkdir()/rmdir() race

From
Michael Haggerty <mhagger@alum.mit.edu>
Date
Dec 22, 2013, 07:14 UTC
Message-ID
<1387696451-32224-6-git-send-email-mhagger@alum.mit.edu>
In-Reply-To
<1387696451-32224-1-git-send-email-mhagger@alum.mit.edu>

When renaming a reflog file, it was possible that an empty directory that we just created using safe_create_leading_directories() might get deleted by another process before we have a chance to move the new file into it.

So if the rename fails with ENOENT, then retry from the beginning. Make up to three attempts before giving up.

It could theoretically happen that the ENOENT comes from the disappearance of TMP_RENAMED_LOG. In that case three pointless attempts will be made to move the nonexistent file, but no other harm should come of it.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/refs.c b/refs.c
index 3926136..3ab1491 100644
--- a/refs.c
+++ b/refs.c
@@ -2516,6 +2516,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
 	struct stat loginfo;
 	int log = !lstat(git_path("logs/%s", oldrefname), &loginfo);
 	const char *symref = NULL;
+	int attempts = 3;
 
 	if (log && S_ISLNK(loginfo.st_mode))
 		return error("reflog for %s is a symlink", oldrefname);
@@ -2555,12 +2556,12 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
 		}
 	}
 
+ retry:
 	if (log && safe_create_leading_directories(git_path("logs/%s", newrefname))) {
 		error("unable to create directory for %s", newrefname);
 		goto rollback;
 	}
 
- retry:
 	if (log && rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", newrefname))) {
 		if (errno==EISDIR || errno==ENOTDIR) {
 			/*
@@ -2574,6 +2575,13 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
 			}
 			goto retry;
 		} else {
+			if (errno == ENOENT && --attempts)
+				/*
+				 * Perhaps somebody just pruned the empty
+				 * directory into which we wanted to move the
+				 * file.
+				 */
+				goto retry;
 			error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",
 				newrefname, strerror(errno));
 			goto rollback;
-- 
1.8.5.1
Previous: Michael HaggertyNext: Jonathan Nieder
Message 14 of 15 in “Fix two mkdir/rmdir races”
  1. 0/5 Fix two mkdir/rmdir racesMichael Haggerty, Dec 22, 2013
  2. 1/5 safe_create_leading_directories(): modernize format of "if" chainingMichael Haggerty, Dec 22, 2013
  3. Jonathan NiederDec 26, 2013
  4. 2/5 safe_create_leading_directories(): reduce scope of local variableMichael Haggerty, Dec 22, 2013
  5. Jonathan NiederDec 26, 2013
  6. Michael HaggertyJan 1, 2014
  7. 3/5 safe_create_leading_directories(): add "slash" pointerMichael Haggerty, Dec 22, 2013
  8. Jonathan NiederDec 26, 2013
  9. Michael HaggertyJan 1, 2014
  10. 4/5 safe_create_leading_directories(): fix a mkdir/rmdir raceMichael Haggerty, Dec 22, 2013
  11. Ramsay JonesDec 22, 2013
  12. Jonathan NiederDec 26, 2013
  13. Michael HaggertyJan 2, 2014
  14. 5/5 rename_ref(): fix a mkdir()/rmdir() raceMichael Haggerty, Dec 22, 2013
  15. Jonathan NiederDec 26, 2013

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.