summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cache.h10
-rw-r--r--sha1_file.c11
2 files changed, 20 insertions, 1 deletions
diff --git a/cache.h b/cache.h
index c6a4157..f34c0a7 100644
--- a/cache.h
+++ b/cache.h
@@ -741,12 +741,20 @@ int adjust_shared_perm(const char *path);
* Create the directory containing the named path, using care to be
* somewhat safe against races. Return one of the scld_error values
* to indicate success/failure.
+ *
+ * SCLD_VANISHED indicates that one of the ancestor directories of the
+ * path existed at one point during the function call and then
+ * suddenly vanished, probably because another process pruned the
+ * directory while we were working. To be robust against this kind of
+ * race, callers might want to try invoking the function again when it
+ * returns SCLD_VANISHED.
*/
enum scld_error {
SCLD_OK = 0,
SCLD_FAILED = -1,
SCLD_PERMS = -2,
- SCLD_EXISTS = -3
+ SCLD_EXISTS = -3,
+ SCLD_VANISHED = -4
};
enum scld_error safe_create_leading_directories(char *path);
enum scld_error safe_create_leading_directories_const(const char *path);
diff --git a/sha1_file.c b/sha1_file.c
index 2a86912..ed814e5 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -132,6 +132,17 @@ enum scld_error safe_create_leading_directories(char *path)
if (errno == EEXIST &&
!stat(path, &st) && S_ISDIR(st.st_mode))
; /* somebody created it since we checked */
+ else if (errno == ENOENT)
+ /*
+ * Either mkdir() failed because
+ * somebody just pruned the containing
+ * directory, or stat() failed because
+ * the file that was in our way was
+ * just removed. Either way, inform
+ * the caller that it might be worth
+ * trying again:
+ */
+ ret = SCLD_VANISHED;
else
ret = SCLD_FAILED;
} else if (adjust_shared_perm(path)) {