Skip to content

Commit 5edbcea

Browse files
committed
Merge branch 'bc/racy-4gb-files'
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
2 parents 626f689 + 5143ac0 commit 5edbcea

File tree

6 files changed

+62
-2
lines changed

6 files changed

+62
-2
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,7 @@ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
852852
TEST_BUILTINS_OBJS += test-submodule.o
853853
TEST_BUILTINS_OBJS += test-subprocess.o
854854
TEST_BUILTINS_OBJS += test-trace2.o
855+
TEST_BUILTINS_OBJS += test-truncate.o
855856
TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
856857
TEST_BUILTINS_OBJS += test-userdiff.o
857858
TEST_BUILTINS_OBJS += test-wildmatch.o

statinfo.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22
#include "environment.h"
33
#include "statinfo.h"
44

5+
/*
6+
* Munge st_size into an unsigned int.
7+
*/
8+
static unsigned int munge_st_size(off_t st_size) {
9+
unsigned int sd_size = st_size;
10+
11+
/*
12+
* If the file is an exact multiple of 4 GiB, modify the value so it
13+
* doesn't get marked as racily clean (zero).
14+
*/
15+
if (!sd_size && st_size)
16+
return 0x80000000;
17+
else
18+
return sd_size;
19+
}
20+
521
void fill_stat_data(struct stat_data *sd, struct stat *st)
622
{
723
sd->sd_ctime.sec = (unsigned int)st->st_ctime;
@@ -12,7 +28,7 @@ void fill_stat_data(struct stat_data *sd, struct stat *st)
1228
sd->sd_ino = st->st_ino;
1329
sd->sd_uid = st->st_uid;
1430
sd->sd_gid = st->st_gid;
15-
sd->sd_size = st->st_size;
31+
sd->sd_size = munge_st_size(st->st_size);
1632
}
1733

1834
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)
5167
changed |= INODE_CHANGED;
5268
#endif
5369

54-
if (sd->sd_size != (unsigned int) st->st_size)
70+
if (sd->sd_size != munge_st_size(st->st_size))
5571
changed |= DATA_CHANGED;
5672

5773
return changed;

t/helper/test-tool.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ static struct test_cmd cmds[] = {
8686
{ "submodule-nested-repo-config", cmd__submodule_nested_repo_config },
8787
{ "subprocess", cmd__subprocess },
8888
{ "trace2", cmd__trace2 },
89+
{ "truncate", cmd__truncate },
8990
{ "userdiff", cmd__userdiff },
9091
{ "urlmatch-normalization", cmd__urlmatch_normalization },
9192
{ "xml-encode", cmd__xml_encode },

t/helper/test-tool.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ int cmd__submodule_config(int argc, const char **argv);
7979
int cmd__submodule_nested_repo_config(int argc, const char **argv);
8080
int cmd__subprocess(int argc, const char **argv);
8181
int cmd__trace2(int argc, const char **argv);
82+
int cmd__truncate(int argc, const char **argv);
8283
int cmd__userdiff(int argc, const char **argv);
8384
int cmd__urlmatch_normalization(int argc, const char **argv);
8485
int cmd__xml_encode(int argc, const char **argv);

t/helper/test-truncate.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include "test-tool.h"
2+
#include "git-compat-util.h"
3+
4+
/*
5+
* Truncate a file to the given size.
6+
*/
7+
int cmd__truncate(int argc, const char **argv)
8+
{
9+
char *p = NULL;
10+
uintmax_t sz = 0;
11+
int fd = -1;
12+
13+
if (argc != 3)
14+
die("expected filename and size");
15+
16+
sz = strtoumax(argv[2], &p, 0);
17+
if (*p)
18+
die("invalid size");
19+
20+
fd = xopen(argv[1], O_WRONLY | O_CREAT, 0600);
21+
22+
if (ftruncate(fd, (off_t) sz) < 0)
23+
die_errno("failed to truncate file");
24+
return 0;
25+
}

t/t7508-status.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1745,4 +1745,20 @@ test_expect_success 'slow status advice when core.untrackedCache true, and fsmon
17451745
)
17461746
'
17471747

1748+
test_expect_success EXPENSIVE 'status does not re-read unchanged 4 or 8 GiB file' '
1749+
(
1750+
mkdir large-file &&
1751+
cd large-file &&
1752+
# Files are 2 GiB, 4 GiB, and 8 GiB sparse files.
1753+
test-tool truncate file-a 0x080000000 &&
1754+
test-tool truncate file-b 0x100000000 &&
1755+
test-tool truncate file-c 0x200000000 &&
1756+
# This will be slow.
1757+
git add file-a file-b file-c &&
1758+
git commit -m "add large files" &&
1759+
git diff-index HEAD file-a file-b file-c >actual &&
1760+
test_must_be_empty actual
1761+
)
1762+
'
1763+
17481764
test_done

0 commit comments

Comments
 (0)