Merge branch 'bc/racy-4gb-files' into next

The index file has room only for lower 32-bit of the file size in
the cached stat information, which means cached stat information
will have 0 in its sd_size member for a file whose size is multiple
of 4GiB.  This is mistaken for a racily clean path.  Avoid it by
storing a bogus sd_size value instead for such files.

* bc/racy-4gb-files:
  Prevent git from rehashing 4GiB files
  t: add a test helper to truncate files
This commit is contained in:
Junio C Hamano
2023-10-16 10:25:30 -07:00
6 changed files with 62 additions and 2 deletions

View File

@ -852,6 +852,7 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
TEST_BUILTINS_OBJS += test-submodule.o
TEST_BUILTINS_OBJS += test-subprocess.o
TEST_BUILTINS_OBJS += test-trace2.o
TEST_BUILTINS_OBJS += test-truncate.o
TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
TEST_BUILTINS_OBJS += test-userdiff.o
TEST_BUILTINS_OBJS += test-wildmatch.o

View File

@ -2,6 +2,22 @@
#include "environment.h"
#include "statinfo.h"
/*
* Munge st_size into an unsigned int.
*/
static unsigned int munge_st_size(off_t st_size) {
unsigned int sd_size = st_size;
/*
* If the file is an exact multiple of 4 GiB, modify the value so it
* doesn't get marked as racily clean (zero).
*/
if (!sd_size && st_size)
return 0x80000000;
else
return sd_size;
}
void fill_stat_data(struct stat_data *sd, struct stat *st)
{
sd->sd_ctime.sec = (unsigned int)st->st_ctime;
@ -12,7 +28,7 @@ void fill_stat_data(struct stat_data *sd, struct stat *st)
sd->sd_ino = st->st_ino;
sd->sd_uid = st->st_uid;
sd->sd_gid = st->st_gid;
sd->sd_size = st->st_size;
sd->sd_size = munge_st_size(st->st_size);
}
int match_stat_data(const struct stat_data *sd, struct stat *st)
@ -51,7 +67,7 @@ int match_stat_data(const struct stat_data *sd, struct stat *st)
changed |= INODE_CHANGED;
#endif
if (sd->sd_size != (unsigned int) st->st_size)
if (sd->sd_size != munge_st_size(st->st_size))
changed |= DATA_CHANGED;
return changed;

View File

@ -86,6 +86,7 @@ static struct test_cmd cmds[] = {
{ "submodule-nested-repo-config", cmd__submodule_nested_repo_config },
{ "subprocess", cmd__subprocess },
{ "trace2", cmd__trace2 },
{ "truncate", cmd__truncate },
{ "userdiff", cmd__userdiff },
{ "urlmatch-normalization", cmd__urlmatch_normalization },
{ "xml-encode", cmd__xml_encode },

View File

@ -79,6 +79,7 @@ int cmd__submodule_config(int argc, const char **argv);
int cmd__submodule_nested_repo_config(int argc, const char **argv);
int cmd__subprocess(int argc, const char **argv);
int cmd__trace2(int argc, const char **argv);
int cmd__truncate(int argc, const char **argv);
int cmd__userdiff(int argc, const char **argv);
int cmd__urlmatch_normalization(int argc, const char **argv);
int cmd__xml_encode(int argc, const char **argv);

25
t/helper/test-truncate.c Normal file
View File

@ -0,0 +1,25 @@
#include "test-tool.h"
#include "git-compat-util.h"
/*
* Truncate a file to the given size.
*/
int cmd__truncate(int argc, const char **argv)
{
char *p = NULL;
uintmax_t sz = 0;
int fd = -1;
if (argc != 3)
die("expected filename and size");
sz = strtoumax(argv[2], &p, 0);
if (*p)
die("invalid size");
fd = xopen(argv[1], O_WRONLY | O_CREAT, 0600);
if (ftruncate(fd, (off_t) sz) < 0)
die_errno("failed to truncate file");
return 0;
}

View File

@ -1745,4 +1745,20 @@ test_expect_success 'slow status advice when core.untrackedCache true, and fsmon
)
'
test_expect_success EXPENSIVE 'status does not re-read unchanged 4 or 8 GiB file' '
(
mkdir large-file &&
cd large-file &&
# Files are 2 GiB, 4 GiB, and 8 GiB sparse files.
test-tool truncate file-a 0x080000000 &&
test-tool truncate file-b 0x100000000 &&
test-tool truncate file-c 0x200000000 &&
# This will be slow.
git add file-a file-b file-c &&
git commit -m "add large files" &&
git diff-index HEAD file-a file-b file-c >actual &&
test_must_be_empty actual
)
'
test_done