Skip to content

Commit

Permalink
Merge branch 'bc/racy-4gb-files'
Browse files Browse the repository at this point in the history
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
  • Loading branch information
gitster committed Oct 23, 2023
2 parents 626f689 + 5143ac0 commit 5edbcea
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 2 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 18 additions & 2 deletions statinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions t/helper/test-tool.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
1 change: 1 addition & 0 deletions t/helper/test-tool.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
25 changes: 25 additions & 0 deletions t/helper/test-truncate.c
Original file line number Diff line number Diff line change
@@ -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;
}
16 changes: 16 additions & 0 deletions t/t7508-status.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 5edbcea

Please sign in to comment.