1#include "cache.h"
2#include "csum-file.h"
3#include "lockfile.h"
4#include "object-store.h"
5#include "midx.h"
6
7#define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
8#define MIDX_VERSION 1
9#define MIDX_BYTE_FILE_VERSION 4
10#define MIDX_BYTE_HASH_VERSION 5
11#define MIDX_BYTE_NUM_CHUNKS 6
12#define MIDX_BYTE_NUM_PACKS 8
13#define MIDX_HASH_VERSION 1
14#define MIDX_HEADER_SIZE 12
15#define MIDX_HASH_LEN 20
16#define MIDX_MIN_SIZE (MIDX_HEADER_SIZE + MIDX_HASH_LEN)
17
18static char *get_midx_filename(const char *object_dir)
19{
20 return xstrfmt("%s/pack/multi-pack-index", object_dir);
21}
22
23struct multi_pack_index *load_multi_pack_index(const char *object_dir)
24{
25 struct multi_pack_index *m = NULL;
26 int fd;
27 struct stat st;
28 size_t midx_size;
29 void *midx_map = NULL;
30 uint32_t hash_version;
31 char *midx_name = get_midx_filename(object_dir);
32
33 fd = git_open(midx_name);
34
35 if (fd < 0)
36 goto cleanup_fail;
37 if (fstat(fd, &st)) {
38 error_errno(_("failed to read %s"), midx_name);
39 goto cleanup_fail;
40 }
41
42 midx_size = xsize_t(st.st_size);
43
44 if (midx_size < MIDX_MIN_SIZE) {
45 error(_("multi-pack-index file %s is too small"), midx_name);
46 goto cleanup_fail;
47 }
48
49 FREE_AND_NULL(midx_name);
50
51 midx_map = xmmap(NULL, midx_size, PROT_READ, MAP_PRIVATE, fd, 0);
52
53 FLEX_ALLOC_MEM(m, object_dir, object_dir, strlen(object_dir));
54 m->fd = fd;
55 m->data = midx_map;
56 m->data_len = midx_size;
57
58 m->signature = get_be32(m->data);
59 if (m->signature != MIDX_SIGNATURE) {
60 error(_("multi-pack-index signature 0x%08x does not match signature 0x%08x"),
61 m->signature, MIDX_SIGNATURE);
62 goto cleanup_fail;
63 }
64
65 m->version = m->data[MIDX_BYTE_FILE_VERSION];
66 if (m->version != MIDX_VERSION) {
67 error(_("multi-pack-index version %d not recognized"),
68 m->version);
69 goto cleanup_fail;
70 }
71
72 hash_version = m->data[MIDX_BYTE_HASH_VERSION];
73 if (hash_version != MIDX_HASH_VERSION) {
74 error(_("hash version %u does not match"), hash_version);
75 goto cleanup_fail;
76 }
77 m->hash_len = MIDX_HASH_LEN;
78
79 m->num_chunks = m->data[MIDX_BYTE_NUM_CHUNKS];
80
81 m->num_packs = get_be32(m->data + MIDX_BYTE_NUM_PACKS);
82
83 return m;
84
85cleanup_fail:
86 free(m);
87 free(midx_name);
88 if (midx_map)
89 munmap(midx_map, midx_size);
90 if (0 <= fd)
91 close(fd);
92 return NULL;
93}
94
95static size_t write_midx_header(struct hashfile *f,
96 unsigned char num_chunks,
97 uint32_t num_packs)
98{
99 unsigned char byte_values[4];
100
101 hashwrite_be32(f, MIDX_SIGNATURE);
102 byte_values[0] = MIDX_VERSION;
103 byte_values[1] = MIDX_HASH_VERSION;
104 byte_values[2] = num_chunks;
105 byte_values[3] = 0; /* unused */
106 hashwrite(f, byte_values, sizeof(byte_values));
107 hashwrite_be32(f, num_packs);
108
109 return MIDX_HEADER_SIZE;
110}
111
112int write_midx_file(const char *object_dir)
113{
114 unsigned char num_chunks = 0;
115 char *midx_name;
116 struct hashfile *f = NULL;
117 struct lock_file lk;
118
119 midx_name = get_midx_filename(object_dir);
120 if (safe_create_leading_directories(midx_name)) {
121 UNLEAK(midx_name);
122 die_errno(_("unable to create leading directories of %s"),
123 midx_name);
124 }
125
126 hold_lock_file_for_update(&lk, midx_name, LOCK_DIE_ON_ERROR);
127 f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
128 FREE_AND_NULL(midx_name);
129
130 write_midx_header(f, num_chunks, 0);
131
132 finalize_hashfile(f, NULL, CSUM_FSYNC | CSUM_HASH_IN_STREAM);
133 commit_lock_file(&lk);
134
135 return 0;
136}