Skip to content

Commit

Permalink
object-file: retry linking file into place when occluding file vanishes
Browse files Browse the repository at this point in the history
In a preceding commit, we have adapted `check_collision()` to ignore
the case where either of the colliding files vanishes. This should be
safe in general when we assume that the contents of these two files were
the same. But the check is all about detecting collisions, so that
assumption may be too optimistic.

Adapt the code to retry linking the object into place when we see that
the destination file has racily vanished. This should generally succeed
as we have just observed that the destination file does not exist
anymore, except in the very unlikely event that it gets recreated by
another concurrent process again.

Furthermore, stop treating `ENOENT` specially for the source file. It
shouldn't happen that the source vanishes as we're using a fresh
temporary file for it, so if it does vanish it indicates an actual
error.

Suggested-by: Jeff King <peff@peff.net>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
pks-t authored and gitster committed Jan 3, 2025
1 parent 07471c4 commit 3bf936a
Showing 1 changed file with 17 additions and 6 deletions.
23 changes: 17 additions & 6 deletions object-file.c
Original file line number Diff line number Diff line change
Expand Up @@ -1974,6 +1974,8 @@ static void write_object_file_prepare_literally(const struct git_hash_algo *algo
hash_object_body(algo, &c, buf, len, oid, hdr, hdrlen);
}

#define CHECK_COLLISION_DEST_VANISHED -2

static int check_collision(const char *source, const char *dest)
{
char buf_source[4096], buf_dest[4096];
Expand All @@ -1982,15 +1984,16 @@ static int check_collision(const char *source, const char *dest)

fd_source = open(source, O_RDONLY);
if (fd_source < 0) {
if (errno != ENOENT)
ret = error_errno(_("unable to open %s"), source);
ret = error_errno(_("unable to open %s"), source);
goto out;
}

fd_dest = open(dest, O_RDONLY);
if (fd_dest < 0) {
if (errno != ENOENT)
ret = error_errno(_("unable to open %s"), dest);
else
ret = CHECK_COLLISION_DEST_VANISHED;
goto out;
}

Expand Down Expand Up @@ -2038,8 +2041,10 @@ int finalize_object_file(const char *tmpfile, const char *filename)
int finalize_object_file_flags(const char *tmpfile, const char *filename,
enum finalize_object_file_flags flags)
{
struct stat st;
int ret = 0;
int ret;

retry:
ret = 0;

if (object_creation_mode == OBJECT_CREATION_USES_RENAMES)
goto try_rename;
Expand All @@ -2060,6 +2065,8 @@ int finalize_object_file_flags(const char *tmpfile, const char *filename,
* left to unlink.
*/
if (ret && ret != EEXIST) {
struct stat st;

try_rename:
if (!stat(filename, &st))
ret = EEXIST;
Expand All @@ -2075,9 +2082,13 @@ int finalize_object_file_flags(const char *tmpfile, const char *filename,
errno = saved_errno;
return error_errno(_("unable to write file %s"), filename);
}
if (!(flags & FOF_SKIP_COLLISION_CHECK) &&
check_collision(tmpfile, filename))
if (!(flags & FOF_SKIP_COLLISION_CHECK)) {
ret = check_collision(tmpfile, filename);
if (ret == CHECK_COLLISION_DEST_VANISHED)
goto retry;
else if (ret)
return -1;
}
unlink_or_warn(tmpfile);
}

Expand Down

0 comments on commit 3bf936a

Please sign in to comment.