Skip to content

Commit 9db2291

Browse files
committed
Merge branch 'kg/packed-ref-cache-fix'
Avoid mmapping small files while using packed refs (especially ones with zero size, which would cause later munmap() to fail). * kg/packed-ref-cache-fix: packed_ref_cache: don't use mmap() for small files load_contents(): don't try to mmap an empty file packed_ref_iterator_begin(): make optimization more general find_reference_location(): make function safe for empty snapshots create_snapshot(): use `xmemdupz()` rather than a strbuf struct snapshot: store `start` rather than `header_len`
2 parents 52b7ab3 + ba41a8b commit 9db2291

File tree

1 file changed

+55
-51
lines changed

1 file changed

+55
-51
lines changed

refs/packed-backend.c

Lines changed: 55 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,21 @@ struct snapshot {
6868
int mmapped;
6969

7070
/*
71-
* The contents of the `packed-refs` file. If the file was
72-
* already sorted, this points at the mmapped contents of the
73-
* file. If not, this points at heap-allocated memory
74-
* containing the contents, sorted. If there were no contents
75-
* (e.g., because the file didn't exist), `buf` and `eof` are
76-
* both NULL.
71+
* The contents of the `packed-refs` file:
72+
*
73+
* - buf -- a pointer to the start of the memory
74+
* - start -- a pointer to the first byte of actual references
75+
* (i.e., after the header line, if one is present)
76+
* - eof -- a pointer just past the end of the reference
77+
* contents
78+
*
79+
* If the `packed-refs` file was already sorted, `buf` points
80+
* at the mmapped contents of the file. If not, it points at
81+
* heap-allocated memory containing the contents, sorted. If
82+
* there were no contents (e.g., because the file didn't
83+
* exist), `buf`, `start`, and `eof` are all NULL.
7784
*/
78-
char *buf, *eof;
79-
80-
/* The size of the header line, if any; otherwise, 0: */
81-
size_t header_len;
85+
char *buf, *start, *eof;
8286

8387
/*
8488
* What is the peeled state of the `packed-refs` file that
@@ -169,8 +173,7 @@ static void clear_snapshot_buffer(struct snapshot *snapshot)
169173
} else {
170174
free(snapshot->buf);
171175
}
172-
snapshot->buf = snapshot->eof = NULL;
173-
snapshot->header_len = 0;
176+
snapshot->buf = snapshot->start = snapshot->eof = NULL;
174177
}
175178

176179
/*
@@ -319,13 +322,14 @@ static void sort_snapshot(struct snapshot *snapshot)
319322
size_t len, i;
320323
char *new_buffer, *dst;
321324

322-
pos = snapshot->buf + snapshot->header_len;
325+
pos = snapshot->start;
323326
eof = snapshot->eof;
324-
len = eof - pos;
325327

326-
if (!len)
328+
if (pos == eof)
327329
return;
328330

331+
len = eof - pos;
332+
329333
/*
330334
* Initialize records based on a crude estimate of the number
331335
* of references in the file (we'll grow it below if needed):
@@ -391,9 +395,8 @@ static void sort_snapshot(struct snapshot *snapshot)
391395
* place:
392396
*/
393397
clear_snapshot_buffer(snapshot);
394-
snapshot->buf = new_buffer;
398+
snapshot->buf = snapshot->start = new_buffer;
395399
snapshot->eof = new_buffer + len;
396-
snapshot->header_len = 0;
397400

398401
cleanup:
399402
free(records);
@@ -442,23 +445,26 @@ static const char *find_end_of_record(const char *p, const char *end)
442445
*/
443446
static void verify_buffer_safe(struct snapshot *snapshot)
444447
{
445-
const char *buf = snapshot->buf + snapshot->header_len;
448+
const char *start = snapshot->start;
446449
const char *eof = snapshot->eof;
447450
const char *last_line;
448451

449-
if (buf == eof)
452+
if (start == eof)
450453
return;
451454

452-
last_line = find_start_of_record(buf, eof - 1);
455+
last_line = find_start_of_record(start, eof - 1);
453456
if (*(eof - 1) != '\n' || eof - last_line < GIT_SHA1_HEXSZ + 2)
454457
die_invalid_line(snapshot->refs->path,
455458
last_line, eof - last_line);
456459
}
457460

461+
#define SMALL_FILE_SIZE (32*1024)
462+
458463
/*
459464
* Depending on `mmap_strategy`, either mmap or read the contents of
460465
* the `packed-refs` file into the snapshot. Return 1 if the file
461-
* existed and was read, or 0 if the file was absent. Die on errors.
466+
* existed and was read, or 0 if the file was absent or empty. Die on
467+
* errors.
462468
*/
463469
static int load_contents(struct snapshot *snapshot)
464470
{
@@ -489,24 +495,23 @@ static int load_contents(struct snapshot *snapshot)
489495
die_errno("couldn't stat %s", snapshot->refs->path);
490496
size = xsize_t(st.st_size);
491497

492-
switch (mmap_strategy) {
493-
case MMAP_NONE:
498+
if (!size) {
499+
return 0;
500+
} else if (mmap_strategy == MMAP_NONE || size <= SMALL_FILE_SIZE) {
494501
snapshot->buf = xmalloc(size);
495502
bytes_read = read_in_full(fd, snapshot->buf, size);
496503
if (bytes_read < 0 || bytes_read != size)
497504
die_errno("couldn't read %s", snapshot->refs->path);
498-
snapshot->eof = snapshot->buf + size;
499505
snapshot->mmapped = 0;
500-
break;
501-
case MMAP_TEMPORARY:
502-
case MMAP_OK:
506+
} else {
503507
snapshot->buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
504-
snapshot->eof = snapshot->buf + size;
505508
snapshot->mmapped = 1;
506-
break;
507509
}
508510
close(fd);
509511

512+
snapshot->start = snapshot->buf;
513+
snapshot->eof = snapshot->buf + size;
514+
510515
return 1;
511516
}
512517

@@ -515,9 +520,11 @@ static int load_contents(struct snapshot *snapshot)
515520
* `refname` starts. If `mustexist` is true and the reference doesn't
516521
* exist, then return NULL. If `mustexist` is false and the reference
517522
* doesn't exist, then return the point where that reference would be
518-
* inserted. In the latter mode, `refname` doesn't have to be a proper
519-
* reference name; for example, one could search for "refs/replace/"
520-
* to find the start of any replace references.
523+
* inserted, or `snapshot->eof` (which might be NULL) if it would be
524+
* inserted at the end of the file. In the latter mode, `refname`
525+
* doesn't have to be a proper reference name; for example, one could
526+
* search for "refs/replace/" to find the start of any replace
527+
* references.
521528
*
522529
* The record is sought using a binary search, so `snapshot->buf` must
523530
* be sorted.
@@ -539,15 +546,15 @@ static const char *find_reference_location(struct snapshot *snapshot,
539546
* preceding records all have reference names that come
540547
* *before* `refname`.
541548
*/
542-
const char *lo = snapshot->buf + snapshot->header_len;
549+
const char *lo = snapshot->start;
543550

544551
/*
545552
* A pointer to a the first character of a record whose
546553
* reference name comes *after* `refname`.
547554
*/
548555
const char *hi = snapshot->eof;
549556

550-
while (lo < hi) {
557+
while (lo != hi) {
551558
const char *mid, *rec;
552559
int cmp;
553560

@@ -616,9 +623,7 @@ static struct snapshot *create_snapshot(struct packed_ref_store *refs)
616623

617624
/* If the file has a header line, process it: */
618625
if (snapshot->buf < snapshot->eof && *snapshot->buf == '#') {
619-
struct strbuf tmp = STRBUF_INIT;
620-
char *p;
621-
const char *eol;
626+
char *tmp, *p, *eol;
622627
struct string_list traits = STRING_LIST_INIT_NODUP;
623628

624629
eol = memchr(snapshot->buf, '\n',
@@ -628,9 +633,9 @@ static struct snapshot *create_snapshot(struct packed_ref_store *refs)
628633
snapshot->buf,
629634
snapshot->eof - snapshot->buf);
630635

631-
strbuf_add(&tmp, snapshot->buf, eol - snapshot->buf);
636+
tmp = xmemdupz(snapshot->buf, eol - snapshot->buf);
632637

633-
if (!skip_prefix(tmp.buf, "# pack-refs with:", (const char **)&p))
638+
if (!skip_prefix(tmp, "# pack-refs with:", (const char **)&p))
634639
die_invalid_line(refs->path,
635640
snapshot->buf,
636641
snapshot->eof - snapshot->buf);
@@ -647,10 +652,10 @@ static struct snapshot *create_snapshot(struct packed_ref_store *refs)
647652
/* perhaps other traits later as well */
648653

649654
/* The "+ 1" is for the LF character. */
650-
snapshot->header_len = eol + 1 - snapshot->buf;
655+
snapshot->start = eol + 1;
651656

652657
string_list_clear(&traits, 0);
653-
strbuf_release(&tmp);
658+
free(tmp);
654659
}
655660

656661
verify_buffer_safe(snapshot);
@@ -671,13 +676,12 @@ static struct snapshot *create_snapshot(struct packed_ref_store *refs)
671676
* We don't want to leave the file mmapped, so we are
672677
* forced to make a copy now:
673678
*/
674-
size_t size = snapshot->eof -
675-
(snapshot->buf + snapshot->header_len);
679+
size_t size = snapshot->eof - snapshot->start;
676680
char *buf_copy = xmalloc(size);
677681

678-
memcpy(buf_copy, snapshot->buf + snapshot->header_len, size);
682+
memcpy(buf_copy, snapshot->start, size);
679683
clear_snapshot_buffer(snapshot);
680-
snapshot->buf = buf_copy;
684+
snapshot->buf = snapshot->start = buf_copy;
681685
snapshot->eof = buf_copy + size;
682686
}
683687

@@ -924,7 +928,12 @@ static struct ref_iterator *packed_ref_iterator_begin(
924928
*/
925929
snapshot = get_snapshot(refs);
926930

927-
if (!snapshot->buf)
931+
if (prefix && *prefix)
932+
start = find_reference_location(snapshot, prefix, 0);
933+
else
934+
start = snapshot->start;
935+
936+
if (start == snapshot->eof)
928937
return empty_ref_iterator_begin();
929938

930939
iter = xcalloc(1, sizeof(*iter));
@@ -934,11 +943,6 @@ static struct ref_iterator *packed_ref_iterator_begin(
934943
iter->snapshot = snapshot;
935944
acquire_snapshot(snapshot);
936945

937-
if (prefix && *prefix)
938-
start = find_reference_location(snapshot, prefix, 0);
939-
else
940-
start = snapshot->buf + snapshot->header_len;
941-
942946
iter->pos = start;
943947
iter->eof = snapshot->eof;
944948
strbuf_init(&iter->refname_buf, 0);

0 commit comments

Comments
 (0)