multi-pack-index: read packfile list
[git.git] / midx.c
blobf742d7ccd7078333286464fbb6f3a2a633c5db77
1 #include "cache.h"
2 #include "csum-file.h"
3 #include "dir.h"
4 #include "lockfile.h"
5 #include "packfile.h"
6 #include "object-store.h"
7 #include "midx.h"
9 #define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
10 #define MIDX_VERSION 1
11 #define MIDX_BYTE_FILE_VERSION 4
12 #define MIDX_BYTE_HASH_VERSION 5
13 #define MIDX_BYTE_NUM_CHUNKS 6
14 #define MIDX_BYTE_NUM_PACKS 8
15 #define MIDX_HASH_VERSION 1
16 #define MIDX_HEADER_SIZE 12
17 #define MIDX_HASH_LEN 20
18 #define MIDX_MIN_SIZE (MIDX_HEADER_SIZE + MIDX_HASH_LEN)
20 static char *get_midx_filename(const char *object_dir)
22 return xstrfmt("%s/pack/multi-pack-index", object_dir);
25 struct multi_pack_index *load_multi_pack_index(const char *object_dir)
27 struct multi_pack_index *m = NULL;
28 int fd;
29 struct stat st;
30 size_t midx_size;
31 void *midx_map = NULL;
32 uint32_t hash_version;
33 char *midx_name = get_midx_filename(object_dir);
35 fd = git_open(midx_name);
37 if (fd < 0)
38 goto cleanup_fail;
39 if (fstat(fd, &st)) {
40 error_errno(_("failed to read %s"), midx_name);
41 goto cleanup_fail;
44 midx_size = xsize_t(st.st_size);
46 if (midx_size < MIDX_MIN_SIZE) {
47 error(_("multi-pack-index file %s is too small"), midx_name);
48 goto cleanup_fail;
51 FREE_AND_NULL(midx_name);
53 midx_map = xmmap(NULL, midx_size, PROT_READ, MAP_PRIVATE, fd, 0);
55 FLEX_ALLOC_MEM(m, object_dir, object_dir, strlen(object_dir));
56 m->fd = fd;
57 m->data = midx_map;
58 m->data_len = midx_size;
60 m->signature = get_be32(m->data);
61 if (m->signature != MIDX_SIGNATURE) {
62 error(_("multi-pack-index signature 0x%08x does not match signature 0x%08x"),
63 m->signature, MIDX_SIGNATURE);
64 goto cleanup_fail;
67 m->version = m->data[MIDX_BYTE_FILE_VERSION];
68 if (m->version != MIDX_VERSION) {
69 error(_("multi-pack-index version %d not recognized"),
70 m->version);
71 goto cleanup_fail;
74 hash_version = m->data[MIDX_BYTE_HASH_VERSION];
75 if (hash_version != MIDX_HASH_VERSION) {
76 error(_("hash version %u does not match"), hash_version);
77 goto cleanup_fail;
79 m->hash_len = MIDX_HASH_LEN;
81 m->num_chunks = m->data[MIDX_BYTE_NUM_CHUNKS];
83 m->num_packs = get_be32(m->data + MIDX_BYTE_NUM_PACKS);
85 return m;
87 cleanup_fail:
88 free(m);
89 free(midx_name);
90 if (midx_map)
91 munmap(midx_map, midx_size);
92 if (0 <= fd)
93 close(fd);
94 return NULL;
97 static size_t write_midx_header(struct hashfile *f,
98 unsigned char num_chunks,
99 uint32_t num_packs)
101 unsigned char byte_values[4];
103 hashwrite_be32(f, MIDX_SIGNATURE);
104 byte_values[0] = MIDX_VERSION;
105 byte_values[1] = MIDX_HASH_VERSION;
106 byte_values[2] = num_chunks;
107 byte_values[3] = 0; /* unused */
108 hashwrite(f, byte_values, sizeof(byte_values));
109 hashwrite_be32(f, num_packs);
111 return MIDX_HEADER_SIZE;
114 struct pack_list {
115 struct packed_git **list;
116 uint32_t nr;
117 uint32_t alloc_list;
120 static void add_pack_to_midx(const char *full_path, size_t full_path_len,
121 const char *file_name, void *data)
123 struct pack_list *packs = (struct pack_list *)data;
125 if (ends_with(file_name, ".idx")) {
126 ALLOC_GROW(packs->list, packs->nr + 1, packs->alloc_list);
128 packs->list[packs->nr] = add_packed_git(full_path,
129 full_path_len,
131 if (!packs->list[packs->nr]) {
132 warning(_("failed to add packfile '%s'"),
133 full_path);
134 return;
137 packs->nr++;
141 int write_midx_file(const char *object_dir)
143 unsigned char num_chunks = 0;
144 char *midx_name;
145 uint32_t i;
146 struct hashfile *f = NULL;
147 struct lock_file lk;
148 struct pack_list packs;
150 midx_name = get_midx_filename(object_dir);
151 if (safe_create_leading_directories(midx_name)) {
152 UNLEAK(midx_name);
153 die_errno(_("unable to create leading directories of %s"),
154 midx_name);
157 packs.nr = 0;
158 packs.alloc_list = 16;
159 packs.list = NULL;
160 ALLOC_ARRAY(packs.list, packs.alloc_list);
162 for_each_file_in_pack_dir(object_dir, add_pack_to_midx, &packs);
164 hold_lock_file_for_update(&lk, midx_name, LOCK_DIE_ON_ERROR);
165 f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
166 FREE_AND_NULL(midx_name);
168 write_midx_header(f, num_chunks, packs.nr);
170 finalize_hashfile(f, NULL, CSUM_FSYNC | CSUM_HASH_IN_STREAM);
171 commit_lock_file(&lk);
173 for (i = 0; i < packs.nr; i++) {
174 if (packs.list[i]) {
175 close_pack(packs.list[i]);
176 free(packs.list[i]);
180 free(packs.list);
181 return 0;