Skip to content

Commit 94cd775

Browse files
ttaylorrgitster
authored andcommitted
pack-mtimes: support reading .mtimes files
To store the individual mtimes of objects in a cruft pack, introduce a new `.mtimes` format that can optionally accompany a single pack in the repository. The format is defined in Documentation/technical/pack-format.txt, and stores a 4-byte network order timestamp for each object in name (index) order. This patch prepares for cruft packs by defining the `.mtimes` format, and introducing a basic API that callers can use to read out individual mtimes. Signed-off-by: Taylor Blau <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 3d89a8c commit 94cd775

File tree

7 files changed

+202
-3
lines changed

7 files changed

+202
-3
lines changed

Documentation/technical/pack-format.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,25 @@ Pack file entry: <+
294294

295295
All 4-byte numbers are in network order.
296296

297+
== pack-*.mtimes files have the format:
298+
299+
All 4-byte numbers are in network byte order.
300+
301+
- A 4-byte magic number '0x4d544d45' ('MTME').
302+
303+
- A 4-byte version identifier (= 1).
304+
305+
- A 4-byte hash function identifier (= 1 for SHA-1, 2 for SHA-256).
306+
307+
- A table of 4-byte unsigned integers. The ith value is the
308+
modification time (mtime) of the ith object in the corresponding
309+
pack by lexicographic (index) order. The mtimes count standard
310+
epoch seconds.
311+
312+
- A trailer, containing a checksum of the corresponding packfile,
313+
and a checksum of all of the above (each having length according
314+
to the specified hash function).
315+
297316
== multi-pack-index (MIDX) files have the following format:
298317

299318
The multi-pack-index files refer to multiple pack-files and loose objects.

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,7 @@ LIB_OBJS += oidtree.o
993993
LIB_OBJS += pack-bitmap-write.o
994994
LIB_OBJS += pack-bitmap.o
995995
LIB_OBJS += pack-check.o
996+
LIB_OBJS += pack-mtimes.o
996997
LIB_OBJS += pack-objects.o
997998
LIB_OBJS += pack-revindex.o
998999
LIB_OBJS += pack-write.o

builtin/repack.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ static struct {
217217
} exts[] = {
218218
{".pack"},
219219
{".rev", 1},
220+
{".mtimes", 1},
220221
{".bitmap", 1},
221222
{".promisor", 1},
222223
{".idx"},

object-store.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,20 @@ struct packed_git {
115115
freshened:1,
116116
do_not_close:1,
117117
pack_promisor:1,
118-
multi_pack_index:1;
118+
multi_pack_index:1,
119+
is_cruft:1;
119120
unsigned char hash[GIT_MAX_RAWSZ];
120121
struct revindex_entry *revindex;
121122
const uint32_t *revindex_data;
122123
const uint32_t *revindex_map;
123124
size_t revindex_size;
125+
/*
126+
* mtimes_map points at the beginning of the memory mapped region of
127+
* this pack's corresponding .mtimes file, and mtimes_size is the size
128+
* of that .mtimes file
129+
*/
130+
const uint32_t *mtimes_map;
131+
size_t mtimes_size;
124132
/* something like ".git/objects/pack/xxxxx.pack" */
125133
char pack_name[FLEX_ARRAY]; /* more */
126134
};

pack-mtimes.c

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#include "git-compat-util.h"
2+
#include "pack-mtimes.h"
3+
#include "object-store.h"
4+
#include "packfile.h"
5+
6+
static char *pack_mtimes_filename(struct packed_git *p)
7+
{
8+
size_t len;
9+
if (!strip_suffix(p->pack_name, ".pack", &len))
10+
BUG("pack_name does not end in .pack");
11+
return xstrfmt("%.*s.mtimes", (int)len, p->pack_name);
12+
}
13+
14+
#define MTIMES_HEADER_SIZE (12)
15+
16+
struct mtimes_header {
17+
uint32_t signature;
18+
uint32_t version;
19+
uint32_t hash_id;
20+
};
21+
22+
static int load_pack_mtimes_file(char *mtimes_file,
23+
uint32_t num_objects,
24+
const uint32_t **data_p, size_t *len_p)
25+
{
26+
int fd, ret = 0;
27+
struct stat st;
28+
uint32_t *data = NULL;
29+
size_t mtimes_size, expected_size;
30+
struct mtimes_header header;
31+
32+
fd = git_open(mtimes_file);
33+
34+
if (fd < 0) {
35+
ret = -1;
36+
goto cleanup;
37+
}
38+
if (fstat(fd, &st)) {
39+
ret = error_errno(_("failed to read %s"), mtimes_file);
40+
goto cleanup;
41+
}
42+
43+
mtimes_size = xsize_t(st.st_size);
44+
45+
if (mtimes_size < MTIMES_HEADER_SIZE) {
46+
ret = error(_("mtimes file %s is too small"), mtimes_file);
47+
goto cleanup;
48+
}
49+
50+
data = xmmap(NULL, mtimes_size, PROT_READ, MAP_PRIVATE, fd, 0);
51+
52+
header.signature = ntohl(data[0]);
53+
header.version = ntohl(data[1]);
54+
header.hash_id = ntohl(data[2]);
55+
56+
if (header.signature != MTIMES_SIGNATURE) {
57+
ret = error(_("mtimes file %s has unknown signature"), mtimes_file);
58+
goto cleanup;
59+
}
60+
61+
if (header.version != 1) {
62+
ret = error(_("mtimes file %s has unsupported version %"PRIu32),
63+
mtimes_file, header.version);
64+
goto cleanup;
65+
}
66+
67+
if (!(header.hash_id == 1 || header.hash_id == 2)) {
68+
ret = error(_("mtimes file %s has unsupported hash id %"PRIu32),
69+
mtimes_file, header.hash_id);
70+
goto cleanup;
71+
}
72+
73+
74+
expected_size = MTIMES_HEADER_SIZE;
75+
expected_size = st_add(expected_size, st_mult(sizeof(uint32_t), num_objects));
76+
expected_size = st_add(expected_size, 2 * (header.hash_id == 1 ? GIT_SHA1_RAWSZ : GIT_SHA256_RAWSZ));
77+
78+
if (mtimes_size != expected_size) {
79+
ret = error(_("mtimes file %s is corrupt"), mtimes_file);
80+
goto cleanup;
81+
}
82+
83+
cleanup:
84+
if (ret) {
85+
if (data)
86+
munmap(data, mtimes_size);
87+
} else {
88+
*len_p = mtimes_size;
89+
*data_p = data;
90+
}
91+
92+
close(fd);
93+
return ret;
94+
}
95+
96+
int load_pack_mtimes(struct packed_git *p)
97+
{
98+
char *mtimes_name = NULL;
99+
int ret = 0;
100+
101+
if (!p->is_cruft)
102+
return ret; /* not a cruft pack */
103+
if (p->mtimes_map)
104+
return ret; /* already loaded */
105+
106+
ret = open_pack_index(p);
107+
if (ret < 0)
108+
goto cleanup;
109+
110+
mtimes_name = pack_mtimes_filename(p);
111+
ret = load_pack_mtimes_file(mtimes_name,
112+
p->num_objects,
113+
&p->mtimes_map,
114+
&p->mtimes_size);
115+
cleanup:
116+
free(mtimes_name);
117+
return ret;
118+
}
119+
120+
uint32_t nth_packed_mtime(struct packed_git *p, uint32_t pos)
121+
{
122+
if (!p->mtimes_map)
123+
BUG("pack .mtimes file not loaded for %s", p->pack_name);
124+
if (p->num_objects <= pos)
125+
BUG("pack .mtimes out-of-bounds (%"PRIu32" vs %"PRIu32")",
126+
pos, p->num_objects);
127+
128+
return get_be32(p->mtimes_map + pos + 3);
129+
}

pack-mtimes.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#ifndef PACK_MTIMES_H
2+
#define PACK_MTIMES_H
3+
4+
#include "git-compat-util.h"
5+
6+
#define MTIMES_SIGNATURE 0x4d544d45 /* "MTME" */
7+
#define MTIMES_VERSION 1
8+
9+
struct packed_git;
10+
11+
/*
12+
* Loads the .mtimes file corresponding to "p", if any, returning zero
13+
* on success.
14+
*/
15+
int load_pack_mtimes(struct packed_git *p);
16+
17+
/* Returns the mtime associated with the object at position "pos" (in
18+
* lexicographic/index order) in pack "p".
19+
*
20+
* Note that it is a BUG() to call this function if either (a) "p" does
21+
* not have a corresponding .mtimes file, or (b) it does, but it hasn't
22+
* been loaded
23+
*/
24+
uint32_t nth_packed_mtime(struct packed_git *p, uint32_t pos);
25+
26+
#endif

packfile.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,12 +334,22 @@ static void close_pack_revindex(struct packed_git *p)
334334
p->revindex_data = NULL;
335335
}
336336

337+
static void close_pack_mtimes(struct packed_git *p)
338+
{
339+
if (!p->mtimes_map)
340+
return;
341+
342+
munmap((void *)p->mtimes_map, p->mtimes_size);
343+
p->mtimes_map = NULL;
344+
}
345+
337346
void close_pack(struct packed_git *p)
338347
{
339348
close_pack_windows(p);
340349
close_pack_fd(p);
341350
close_pack_index(p);
342351
close_pack_revindex(p);
352+
close_pack_mtimes(p);
343353
oidset_clear(&p->bad_objects);
344354
}
345355

@@ -363,7 +373,7 @@ void close_object_store(struct raw_object_store *o)
363373

364374
void unlink_pack_path(const char *pack_name, int force_delete)
365375
{
366-
static const char *exts[] = {".pack", ".idx", ".rev", ".keep", ".bitmap", ".promisor"};
376+
static const char *exts[] = {".pack", ".idx", ".rev", ".keep", ".bitmap", ".promisor", ".mtimes"};
367377
int i;
368378
struct strbuf buf = STRBUF_INIT;
369379
size_t plen;
@@ -718,6 +728,10 @@ struct packed_git *add_packed_git(const char *path, size_t path_len, int local)
718728
if (!access(p->pack_name, F_OK))
719729
p->pack_promisor = 1;
720730

731+
xsnprintf(p->pack_name + path_len, alloc - path_len, ".mtimes");
732+
if (!access(p->pack_name, F_OK))
733+
p->is_cruft = 1;
734+
721735
xsnprintf(p->pack_name + path_len, alloc - path_len, ".pack");
722736
if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {
723737
free(p);
@@ -869,7 +883,8 @@ static void prepare_pack(const char *full_name, size_t full_name_len,
869883
ends_with(file_name, ".pack") ||
870884
ends_with(file_name, ".bitmap") ||
871885
ends_with(file_name, ".keep") ||
872-
ends_with(file_name, ".promisor"))
886+
ends_with(file_name, ".promisor") ||
887+
ends_with(file_name, ".mtimes"))
873888
string_list_append(data->garbage, full_name);
874889
else
875890
report_garbage(PACKDIR_FILE_GARBAGE, full_name);

0 commit comments

Comments
 (0)