summaryrefslogtreecommitdiffstats
path: root/pack-mtimes.c
blob: 0f9785fc5e4ed9c26f7c1f5503395e74da0d74b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include "git-compat-util.h"
#include "pack-mtimes.h"
#include "object-store.h"
#include "packfile.h"

static char *pack_mtimes_filename(struct packed_git *p)
{
	size_t len;
	if (!strip_suffix(p->pack_name, ".pack", &len))
		BUG("pack_name does not end in .pack");
	return xstrfmt("%.*s.mtimes", (int)len, p->pack_name);
}

#define MTIMES_HEADER_SIZE (12)

struct mtimes_header {
	uint32_t signature;
	uint32_t version;
	uint32_t hash_id;
};

static int load_pack_mtimes_file(char *mtimes_file,
				 uint32_t num_objects,
				 const uint32_t **data_p, size_t *len_p)
{
	int fd, ret = 0;
	struct stat st;
	uint32_t *data = NULL;
	size_t mtimes_size, expected_size;
	struct mtimes_header header;

	fd = git_open(mtimes_file);

	if (fd < 0) {
		ret = -1;
		goto cleanup;
	}
	if (fstat(fd, &st)) {
		ret = error_errno(_("failed to read %s"), mtimes_file);
		goto cleanup;
	}

	mtimes_size = xsize_t(st.st_size);

	if (mtimes_size < MTIMES_HEADER_SIZE) {
		ret = error(_("mtimes file %s is too small"), mtimes_file);
		goto cleanup;
	}

	data = xmmap(NULL, mtimes_size, PROT_READ, MAP_PRIVATE, fd, 0);

	header.signature = ntohl(data[0]);
	header.version = ntohl(data[1]);
	header.hash_id = ntohl(data[2]);

	if (header.signature != MTIMES_SIGNATURE) {
		ret = error(_("mtimes file %s has unknown signature"), mtimes_file);
		goto cleanup;
	}

	if (header.version != 1) {
		ret = error(_("mtimes file %s has unsupported version %"PRIu32),
			    mtimes_file, header.version);
		goto cleanup;
	}

	if (!(header.hash_id == 1 || header.hash_id == 2)) {
		ret = error(_("mtimes file %s has unsupported hash id %"PRIu32),
			    mtimes_file, header.hash_id);
		goto cleanup;
	}


	expected_size = MTIMES_HEADER_SIZE;
	expected_size = st_add(expected_size, st_mult(sizeof(uint32_t), num_objects));
	expected_size = st_add(expected_size, 2 * (header.hash_id == 1 ? GIT_SHA1_RAWSZ : GIT_SHA256_RAWSZ));

	if (mtimes_size != expected_size) {
		ret = error(_("mtimes file %s is corrupt"), mtimes_file);
		goto cleanup;
	}

cleanup:
	if (ret) {
		if (data)
			munmap(data, mtimes_size);
	} else {
		*len_p = mtimes_size;
		*data_p = data;
	}

	if (fd >= 0)
		close(fd);
	return ret;
}

int load_pack_mtimes(struct packed_git *p)
{
	char *mtimes_name = NULL;
	int ret = 0;

	if (!p->is_cruft)
		return ret; /* not a cruft pack */
	if (p->mtimes_map)
		return ret; /* already loaded */

	ret = open_pack_index(p);
	if (ret < 0)
		goto cleanup;

	mtimes_name = pack_mtimes_filename(p);
	ret = load_pack_mtimes_file(mtimes_name,
				    p->num_objects,
				    &p->mtimes_map,
				    &p->mtimes_size);
cleanup:
	free(mtimes_name);
	return ret;
}

uint32_t nth_packed_mtime(struct packed_git *p, uint32_t pos)
{
	if (!p->mtimes_map)
		BUG("pack .mtimes file not loaded for %s", p->pack_name);
	if (p->num_objects <= pos)
		BUG("pack .mtimes out-of-bounds (%"PRIu32" vs %"PRIu32")",
		    pos, p->num_objects);

	return get_be32(p->mtimes_map + pos + 3);
}