Merge branch 'ps/object-collision-check'

CI jobs gave sporadic failures, which turns out that that the
object finalization code was giving an error when it did not have
to.

* ps/object-collision-check:
  object-file: retry linking file into place when occluding file vanishes
  object-file: don't special-case missing source file in collision check
  object-file: rename variables in `check_collision()`
  object-file: fix race in object collision check
This commit is contained in:
Junio C Hamano 2025-01-16 16:35:13 -08:00
commit 66e01e510a

View File

@ -1970,54 +1970,59 @@ static void write_object_file_prepare_literally(const struct git_hash_algo *algo
hash_object_body(algo, &c, buf, len, oid, hdr, hdrlen);
}
static int check_collision(const char *filename_a, const char *filename_b)
#define CHECK_COLLISION_DEST_VANISHED -2
static int check_collision(const char *source, const char *dest)
{
char buf_a[4096], buf_b[4096];
int fd_a = -1, fd_b = -1;
char buf_source[4096], buf_dest[4096];
int fd_source = -1, fd_dest = -1;
int ret = 0;
fd_a = open(filename_a, O_RDONLY);
if (fd_a < 0) {
ret = error_errno(_("unable to open %s"), filename_a);
fd_source = open(source, O_RDONLY);
if (fd_source < 0) {
ret = error_errno(_("unable to open %s"), source);
goto out;
}
fd_b = open(filename_b, O_RDONLY);
if (fd_b < 0) {
ret = error_errno(_("unable to open %s"), filename_b);
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;
}
while (1) {
ssize_t sz_a, sz_b;
sz_a = read_in_full(fd_a, buf_a, sizeof(buf_a));
sz_a = read_in_full(fd_source, buf_source, sizeof(buf_source));
if (sz_a < 0) {
ret = error_errno(_("unable to read %s"), filename_a);
ret = error_errno(_("unable to read %s"), source);
goto out;
}
sz_b = read_in_full(fd_b, buf_b, sizeof(buf_b));
sz_b = read_in_full(fd_dest, buf_dest, sizeof(buf_dest));
if (sz_b < 0) {
ret = error_errno(_("unable to read %s"), filename_b);
ret = error_errno(_("unable to read %s"), dest);
goto out;
}
if (sz_a != sz_b || memcmp(buf_a, buf_b, sz_a)) {
if (sz_a != sz_b || memcmp(buf_source, buf_dest, sz_a)) {
ret = error(_("files '%s' and '%s' differ in contents"),
filename_a, filename_b);
source, dest);
goto out;
}
if (sz_a < sizeof(buf_a))
if (sz_a < sizeof(buf_source))
break;
}
out:
if (fd_a > -1)
close(fd_a);
if (fd_b > -1)
close(fd_b);
if (fd_source > -1)
close(fd_source);
if (fd_dest > -1)
close(fd_dest);
return ret;
}
@ -2032,8 +2037,11 @@ 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;
unsigned retries = 0;
int ret;
retry:
ret = 0;
if (object_creation_mode == OBJECT_CREATION_USES_RENAMES)
goto try_rename;
@ -2054,6 +2062,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;
@ -2069,9 +2079,17 @@ 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) {
if (retries++ > 5)
return error(_("unable to write repeatedly vanishing file %s"),
filename);
goto retry;
}
else if (ret)
return -1;
}
unlink_or_warn(tmpfile);
}