Raw
1 #define USE_THE_REPOSITORY_VARIABLE
2
3 #include "test-tool.h"
4 #include "git-compat-util.h"
5 #include "git-zlib.h"
6 #include "hash.h"
7 #include "hex.h"
8 #include "object-file.h"
9 #include "object.h"
10 #include "pack.h"
11 #include "parse-options.h"
12 #include "parse.h"
13 #include "repository.h"
14 #include "setup.h"
15 #include "strbuf.h"
16 #include "write-or-die.h"
17
18 #define BLOCK_SIZE 0xffff
19 static const unsigned char zeros[BLOCK_SIZE];
20
21 /*
22 * Write data as an uncompressed zlib stream.
23 * For data larger than 64KB, writes multiple uncompressed blocks.
24 * If data is NULL, writes zeros.
25 * Updates the pack checksum context.
26 */
27 static void write_uncompressed_zlib(FILE *f, struct git_hash_ctx *pack_ctx,
28 const void *data, size_t len,
29 const struct git_hash_algo *algo)
30 {
31 unsigned char zlib_header[2] = { 0x78, 0x01 }; /* CMF, FLG */
32 unsigned char block_header[5];
33 const unsigned char *p = data;
34 size_t remaining = len;
35 uint32_t adler = 1L; /* adler32 initial value */
36 unsigned char adler_buf[4];
37
38 /* Write zlib header */
39 fwrite_or_die(f, zlib_header, sizeof(zlib_header));
40 algo->update_fn(pack_ctx, zlib_header, 2);
41
42 /* Write uncompressed blocks (max 64KB each) */
43 do {
44 size_t block_len = remaining > BLOCK_SIZE ? BLOCK_SIZE : remaining;
45 int is_final = (block_len == remaining);
46 const unsigned char *block_data = data ? p : zeros;
47
48 block_header[0] = is_final ? 0x01 : 0x00;
49 block_header[1] = block_len & 0xff;
50 block_header[2] = (block_len >> 8) & 0xff;
51 block_header[3] = block_header[1] ^ 0xff;
52 block_header[4] = block_header[2] ^ 0xff;
53
54 fwrite_or_die(f, block_header, sizeof(block_header));
55 algo->update_fn(pack_ctx, block_header, 5);
56
57 if (block_len) {
58 fwrite_or_die(f, block_data, block_len);
59 algo->update_fn(pack_ctx, block_data, block_len);
60 adler = adler32(adler, block_data, block_len);
61 }
62
63 if (data)
64 p += block_len;
65 remaining -= block_len;
66 } while (remaining > 0);
67
68 /* Write adler32 checksum */
69 put_be32(adler_buf, adler);
70 fwrite_or_die(f, adler_buf, sizeof(adler_buf));
71 algo->update_fn(pack_ctx, adler_buf, 4);
72 }
73
74 /*
75 * Write an uncompressed object to the pack file.
76 * If `data == NULL`, it is treated like a buffer to NUL bytes.
77 * Updates the pack checksum context.
78 */
79 static void write_pack_object(FILE *f, struct git_hash_ctx *pack_ctx,
80 enum object_type type,
81 const void *data, size_t len,
82 struct object_id *oid,
83 const struct git_hash_algo *algo)
84 {
85 unsigned char pack_header[MAX_PACK_OBJECT_HEADER];
86 char object_header[32];
87 int pack_header_len, object_header_len;
88 struct git_hash_ctx ctx;
89
90 /* Write pack object header */
91 pack_header_len = encode_in_pack_object_header(pack_header,
92 sizeof(pack_header),
93 type, len);
94 fwrite_or_die(f, pack_header, pack_header_len);
95 algo->update_fn(pack_ctx, pack_header, pack_header_len);
96
97 /* Write the data as uncompressed zlib */
98 write_uncompressed_zlib(f, pack_ctx, data, len, algo);
99
100 algo->init_fn(&ctx);
101 object_header_len = format_object_header(object_header,
102 sizeof(object_header),
103 type, len);
104 algo->update_fn(&ctx, object_header, object_header_len);
105 if (data)
106 algo->update_fn(&ctx, data, len);
107 else {
108 for (size_t i = len / BLOCK_SIZE; i; i--)
109 algo->update_fn(&ctx, zeros, BLOCK_SIZE);
110 algo->update_fn(&ctx, zeros, len % BLOCK_SIZE);
111 }
112 algo->final_oid_fn(oid, &ctx);
113 }
114
115 /*
116 * Fast path: precomputed pack data for a 4 GiB + 1 all-NUL blob.
117 *
118 * The generated pack is almost entirely zeros with a small constant
119 * prefix, periodic deflate block headers, and a constant suffix
120 * containing the tree, two commits, and the pack checksum. Because
121 * every byte is deterministic for a given blob size and hash algorithm,
122 * we can write the pack without computing any hashes at all, reducing
123 * runtime from minutes of hash computation to seconds of pure I/O.
124 *
125 * The blob is stored as an uncompressed deflate stream: a two-byte
126 * zlib header, then 65538 blocks of up to 0xffff bytes each, followed
127 * by an adler32 checksum. The pack header and deflate framing are
128 * shared across hash algorithms; only the suffix (which contains OIDs
129 * and the pack checksum) differs.
130 *
131 * Constants were generated by running the generic path and extracting
132 * the non-zero bytes from the resulting pack file.
133 */
134
135 #define FAST_PACK_4G1_BLOB_SIZE ((size_t)4 * 1024 * 1024 * 1024 + 1)
136 #define FAST_PACK_4G1_N_FULL_BLOCKS 65537
137
138 /*
139 * Per-hash-algorithm constants for the fast path. The prefix and
140 * deflate block structure are identical across algorithms; only the
141 * suffix (tree, commits, pack checksum) and the commit OID differ.
142 */
143 struct fast_pack {
144 uint32_t format_id;
145 const unsigned char *suffix;
146 size_t suffix_len;
147 const char *commit_oid;
148 };
149
150 /* Pack header + pack object header + zlib header + first block header */
151 static const unsigned char fast_pack_prefix[] = {
152 /* PACK header: signature, version 2, 5 objects */
153 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02,
154 0x00, 0x00, 0x00, 0x05,
155 /* pack object header: blob, size = 4294967297 */
156 0xb1, 0x80, 0x80, 0x80, 0x80, 0x01,
157 /* zlib header: CMF=0x78, FLG=0x01 */
158 0x78, 0x01,
159 /* first non-final block header: BFINAL=0, LEN=0xffff, NLEN=0x0000 */
160 0x00, 0xff, 0xff, 0x00, 0x00
161 };
162
163 /* Every non-final deflate block header is identical */
164 static const unsigned char fast_pack_block_header[] = {
165 0x00, 0xff, 0xff, 0x00, 0x00
166 };
167
168 /* Final block (2 data bytes) + adler32 of 4294967297 NUL bytes */
169 static const unsigned char fast_pack_final_block[] = {
170 /* BFINAL=1, LEN=2, NLEN=0xfffd */
171 0x01, 0x02, 0x00, 0xfd, 0xff,
172 /* 2 NUL data bytes */
173 0x00, 0x00,
174 /* adler32 */
175 0x00, 0xe2, 0x00, 0x01
176 };
177
178 /*
179 * SHA-1 suffix: tree, commit, empty tree, final commit, pack checksum.
180 */
181 static const unsigned char fast_pack_sha1_suffix[] = {
182 0xa0, 0x02, 0x78, 0x01, 0x01, 0x20, 0x00, 0xdf,
183 0xff, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
184 0x66, 0x69, 0x6c, 0x65, 0x00, 0x3e, 0xb7, 0xfe,
185 0xb1, 0x41, 0x3c, 0x75, 0x7f, 0x0d, 0x81, 0x81,
186 0xde, 0xb2, 0x8d, 0x1d, 0xab, 0x03, 0xd6, 0x48,
187 0x46, 0xb4, 0xb4, 0x0c, 0x60, 0x95, 0x0b, 0x78,
188 0x01, 0x01, 0xb5, 0x00, 0x4a, 0xff, 0x74, 0x72,
189 0x65, 0x65, 0x20, 0x63, 0x36, 0x38, 0x33, 0x66,
190 0x63, 0x63, 0x37, 0x64, 0x31, 0x64, 0x38, 0x33,
191 0x65, 0x66, 0x32, 0x66, 0x65, 0x31, 0x61, 0x66,
192 0x35, 0x35, 0x32, 0x31, 0x35, 0x64, 0x30, 0x31,
193 0x36, 0x38, 0x64, 0x62, 0x35, 0x32, 0x61, 0x33,
194 0x61, 0x33, 0x62, 0x0a, 0x61, 0x75, 0x74, 0x68,
195 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55, 0x20, 0x54,
196 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, 0x75, 0x74,
197 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
198 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
199 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
200 0x38, 0x39, 0x30, 0x20, 0x2b, 0x30, 0x30, 0x30,
201 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
202 0x74, 0x65, 0x72, 0x20, 0x43, 0x20, 0x4f, 0x20,
203 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x3c,
204 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65,
205 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
206 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e, 0x20, 0x31,
207 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
208 0x30, 0x20, 0x2b, 0x30, 0x30, 0x30, 0x30, 0x0a,
209 0x0a, 0x4c, 0x61, 0x72, 0x67, 0x65, 0x20, 0x62,
210 0x6c, 0x6f, 0x62, 0x20, 0x63, 0x6f, 0x6d, 0x6d,
211 0x69, 0x74, 0x0a, 0xc6, 0x55, 0x37, 0x6b, 0x20,
212 0x78, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00,
213 0x00, 0x00, 0x01, 0x95, 0x0e, 0x78, 0x01, 0x01,
214 0xe5, 0x00, 0x1a, 0xff, 0x74, 0x72, 0x65, 0x65,
215 0x20, 0x34, 0x62, 0x38, 0x32, 0x35, 0x64, 0x63,
216 0x36, 0x34, 0x32, 0x63, 0x62, 0x36, 0x65, 0x62,
217 0x39, 0x61, 0x30, 0x36, 0x30, 0x65, 0x35, 0x34,
218 0x62, 0x66, 0x38, 0x64, 0x36, 0x39, 0x32, 0x38,
219 0x38, 0x66, 0x62, 0x65, 0x65, 0x34, 0x39, 0x30,
220 0x34, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74,
221 0x20, 0x63, 0x35, 0x62, 0x32, 0x31, 0x63, 0x36,
222 0x31, 0x31, 0x61, 0x61, 0x35, 0x39, 0x34, 0x65,
223 0x63, 0x39, 0x66, 0x64, 0x37, 0x65, 0x39, 0x32,
224 0x63, 0x66, 0x39, 0x36, 0x34, 0x38, 0x39, 0x31,
225 0x34, 0x63, 0x61, 0x34, 0x63, 0x32, 0x34, 0x31,
226 0x32, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72,
227 0x20, 0x41, 0x20, 0x55, 0x20, 0x54, 0x68, 0x6f,
228 0x72, 0x20, 0x3c, 0x61, 0x75, 0x74, 0x68, 0x6f,
229 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
230 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e, 0x20, 0x31,
231 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
232 0x30, 0x20, 0x2b, 0x30, 0x30, 0x30, 0x30, 0x0a,
233 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65,
234 0x72, 0x20, 0x43, 0x20, 0x4f, 0x20, 0x4d, 0x69,
235 0x74, 0x74, 0x65, 0x72, 0x20, 0x3c, 0x63, 0x6f,
236 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x40,
237 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
238 0x63, 0x6f, 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x33,
239 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x20,
240 0x2b, 0x30, 0x30, 0x30, 0x30, 0x0a, 0x0a, 0x45,
241 0x6d, 0x70, 0x74, 0x79, 0x20, 0x74, 0x72, 0x65,
242 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
243 0x0a, 0xaa, 0xb8, 0x45, 0x01, 0x8e, 0xfc, 0xf0,
244 0x2f, 0x9c, 0xc5, 0xcc, 0x4f, 0x6a, 0x1a, 0xc9,
245 0x2b, 0x23, 0xa9, 0xff, 0x91, 0x06, 0xc2, 0x70,
246 0xe3
247 };
248
249 /*
250 * SHA-256 suffix: same structure, but with 32-byte OIDs and SHA-256
251 * pack checksum (609 bytes vs 513 for SHA-1).
252 */
253 static const unsigned char fast_pack_sha256_suffix[] = {
254 0xac, 0x02, 0x78, 0x01, 0x01, 0x2c, 0x00, 0xd3,
255 0xff, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
256 0x66, 0x69, 0x6c, 0x65, 0x00, 0x42, 0x53, 0xc1,
257 0x8a, 0x9f, 0x5e, 0xc3, 0xbb, 0x47, 0xb0, 0x83,
258 0x8a, 0x19, 0xdb, 0x31, 0xbb, 0x7b, 0x0f, 0x3b,
259 0x80, 0xa4, 0xbc, 0x2f, 0xaf, 0x72, 0x6b, 0xdb,
260 0x62, 0xaa, 0xba, 0xdd, 0xde, 0x77, 0xc6, 0x13,
261 0xeb, 0x9d, 0x0c, 0x78, 0x01, 0x01, 0xcd, 0x00,
262 0x32, 0xff, 0x74, 0x72, 0x65, 0x65, 0x20, 0x62,
263 0x36, 0x30, 0x39, 0x37, 0x37, 0x64, 0x37, 0x63,
264 0x34, 0x63, 0x32, 0x64, 0x31, 0x65, 0x63, 0x63,
265 0x33, 0x66, 0x62, 0x61, 0x31, 0x64, 0x39, 0x38,
266 0x65, 0x65, 0x31, 0x32, 0x30, 0x61, 0x64, 0x63,
267 0x32, 0x34, 0x38, 0x33, 0x34, 0x39, 0x35, 0x30,
268 0x62, 0x65, 0x34, 0x31, 0x32, 0x64, 0x39, 0x34,
269 0x63, 0x38, 0x30, 0x39, 0x34, 0x38, 0x30, 0x66,
270 0x35, 0x38, 0x62, 0x61, 0x39, 0x64, 0x61, 0x0a,
271 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41,
272 0x20, 0x55, 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20,
273 0x3c, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40,
274 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
275 0x63, 0x6f, 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x33,
276 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x20,
277 0x2b, 0x30, 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f,
278 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x72, 0x20,
279 0x43, 0x20, 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74,
280 0x65, 0x72, 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d,
281 0x69, 0x74, 0x74, 0x65, 0x72, 0x40, 0x65, 0x78,
282 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
283 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35,
284 0x36, 0x37, 0x38, 0x39, 0x30, 0x20, 0x2b, 0x30,
285 0x30, 0x30, 0x30, 0x0a, 0x0a, 0x4c, 0x61, 0x72,
286 0x67, 0x65, 0x20, 0x62, 0x6c, 0x6f, 0x62, 0x20,
287 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x0a, 0xb7,
288 0x80, 0x3d, 0xd7, 0x20, 0x78, 0x01, 0x01, 0x00,
289 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x95,
290 0x11, 0x78, 0x01, 0x01, 0x15, 0x01, 0xea, 0xfe,
291 0x74, 0x72, 0x65, 0x65, 0x20, 0x36, 0x65, 0x66,
292 0x31, 0x39, 0x62, 0x34, 0x31, 0x32, 0x32, 0x35,
293 0x63, 0x35, 0x33, 0x36, 0x39, 0x66, 0x31, 0x63,
294 0x31, 0x30, 0x34, 0x64, 0x34, 0x35, 0x64, 0x38,
295 0x64, 0x38, 0x35, 0x65, 0x66, 0x61, 0x39, 0x62,
296 0x30, 0x35, 0x37, 0x62, 0x35, 0x33, 0x62, 0x31,
297 0x34, 0x62, 0x34, 0x62, 0x39, 0x62, 0x39, 0x33,
298 0x39, 0x64, 0x64, 0x37, 0x34, 0x64, 0x65, 0x63,
299 0x63, 0x35, 0x33, 0x32, 0x31, 0x0a, 0x70, 0x61,
300 0x72, 0x65, 0x6e, 0x74, 0x20, 0x37, 0x35, 0x62,
301 0x66, 0x30, 0x63, 0x34, 0x37, 0x61, 0x65, 0x34,
302 0x62, 0x62, 0x33, 0x30, 0x38, 0x65, 0x37, 0x63,
303 0x63, 0x32, 0x34, 0x38, 0x32, 0x65, 0x32, 0x32,
304 0x65, 0x66, 0x61, 0x65, 0x33, 0x37, 0x38, 0x37,
305 0x61, 0x39, 0x36, 0x38, 0x34, 0x38, 0x62, 0x64,
306 0x31, 0x37, 0x34, 0x39, 0x35, 0x36, 0x37, 0x31,
307 0x34, 0x37, 0x31, 0x35, 0x32, 0x34, 0x36, 0x64,
308 0x64, 0x62, 0x64, 0x35, 0x34, 0x0a, 0x61, 0x75,
309 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55,
310 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61,
311 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78,
312 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
313 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x33, 0x34, 0x35,
314 0x36, 0x37, 0x38, 0x39, 0x30, 0x20, 0x2b, 0x30,
315 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d,
316 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x20,
317 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
318 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
319 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
320 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
321 0x20, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
322 0x38, 0x39, 0x30, 0x20, 0x2b, 0x30, 0x30, 0x30,
323 0x30, 0x0a, 0x0a, 0x45, 0x6d, 0x70, 0x74, 0x79,
324 0x20, 0x74, 0x72, 0x65, 0x65, 0x20, 0x63, 0x6f,
325 0x6d, 0x6d, 0x69, 0x74, 0x0a, 0x6d, 0x6d, 0x51,
326 0x9a, 0xc9, 0x11, 0x76, 0x61, 0xa3, 0x89, 0x49,
327 0xb7, 0xa1, 0x58, 0xc6, 0x1d, 0x8c, 0x33, 0x75,
328 0x8d, 0x7e, 0x4d, 0x8e, 0x58, 0x91, 0xf8, 0x5c,
329 0x57, 0xd9, 0x89, 0x9e, 0xb8, 0xd2, 0x9a, 0xd8,
330 0xc9
331 };
332
333 static const struct fast_pack fast_packs[] = {
334 {
335 .format_id = GIT_SHA1_FORMAT_ID,
336 .suffix = fast_pack_sha1_suffix,
337 .suffix_len = sizeof(fast_pack_sha1_suffix),
338 .commit_oid = "aac43daf40d0377af31aa9c798a4ae8a31b55c1d",
339 },
340 {
341 .format_id = GIT_SHA256_FORMAT_ID,
342 .suffix = fast_pack_sha256_suffix,
343 .suffix_len = sizeof(fast_pack_sha256_suffix),
344 .commit_oid = "63c46ca51267b1d45be69a044bb84b4bf0559f09"
345 "d727f861d2ae94ddebdddbc9",
346 },
347 };
348
349 /*
350 * Try the fast path for known blob sizes. Returns 1 if the pack was
351 * written from precomputed constants, 0 if the caller should fall
352 * through to the generic path.
353 */
354 static int generate_fast_pack(const char *path, size_t blob_size,
355 const struct git_hash_algo *algo)
356 {
357 const struct fast_pack *fp = NULL;
358 FILE *f;
359 size_t i;
360
361 if (blob_size != FAST_PACK_4G1_BLOB_SIZE)
362 return 0;
363
364 for (i = 0; i < ARRAY_SIZE(fast_packs); i++) {
365 if (fast_packs[i].format_id == algo->format_id) {
366 fp = &fast_packs[i];
367 break;
368 }
369 }
370 if (!fp)
371 return 0;
372
373 f = xfopen(path, "wb");
374
375 fwrite_or_die(f, fast_pack_prefix, sizeof(fast_pack_prefix));
376
377 /* First full block: 0xffff zero bytes (header already in prefix) */
378 fwrite_or_die(f, zeros, BLOCK_SIZE);
379
380 /* Remaining non-final full blocks */
381 for (i = 1; i < FAST_PACK_4G1_N_FULL_BLOCKS; i++) {
382 fwrite_or_die(f, fast_pack_block_header,
383 sizeof(fast_pack_block_header));
384 fwrite_or_die(f, zeros, BLOCK_SIZE);
385 }
386
387 /* Final block (2 data bytes) + adler32 */
388 fwrite_or_die(f, fast_pack_final_block,
389 sizeof(fast_pack_final_block));
390
391 /* Tree, commits, and pack checksum */
392 fwrite_or_die(f, fp->suffix, fp->suffix_len);
393
394 if (fclose(f))
395 die_errno(_("could not close '%s'"), path);
396
397 printf("%s\n", fp->commit_oid);
398 return 1;
399 }
400
401 /*
402 * Generate a pack file with a single large (>4GB) reachable object.
403 *
404 * Creates:
405 * 1. A large blob (all NUL bytes)
406 * 2. A tree containing that blob as "file"
407 * 3. A commit using that tree
408 * 4. The empty tree
409 * 5. A child commit using the empty tree
410 *
411 * This is useful for testing that Git can handle objects larger than 4GB.
412 */
413 static int generate_pack_with_large_object(const char *path, size_t blob_size,
414 const struct git_hash_algo *algo)
415 {
416 FILE *f;
417 struct git_hash_ctx pack_ctx;
418 unsigned char pack_hash[GIT_MAX_RAWSZ];
419 struct object_id blob_oid, tree_oid, commit_oid, empty_tree_oid, final_commit_oid;
420 struct strbuf buf = STRBUF_INIT;
421 const uint32_t object_count = 5;
422 struct pack_header pack_header = {
423 .hdr_signature = htonl(PACK_SIGNATURE),
424 .hdr_version = htonl(PACK_VERSION),
425 .hdr_entries = htonl(object_count),
426 };
427
428 if (generate_fast_pack(path, blob_size, algo))
429 return 0;
430
431 f = xfopen(path, "wb");
432
433 algo->init_fn(&pack_ctx);
434
435 /* Write pack header */
436 fwrite_or_die(f, &pack_header, sizeof(pack_header));
437 algo->update_fn(&pack_ctx, &pack_header, sizeof(pack_header));
438
439 /* 1. Write the large blob */
440 write_pack_object(f, &pack_ctx, OBJ_BLOB, NULL, blob_size, &blob_oid, algo);
441
442 /* 2. Write tree containing the blob as "file" */
443 strbuf_addf(&buf, "100644 file%c", '\0');
444 strbuf_add(&buf, blob_oid.hash, algo->rawsz);
445 write_pack_object(f, &pack_ctx, OBJ_TREE, buf.buf, buf.len, &tree_oid, algo);
446
447 /* 3. Write commit using that tree */
448 strbuf_reset(&buf);
449 strbuf_addf(&buf,
450 "tree %s\n"
451 "author A U Thor <author@example.com> 1234567890 +0000\n"
452 "committer C O Mitter <committer@example.com> 1234567890 +0000\n"
453 "\n"
454 "Large blob commit\n",
455 oid_to_hex(&tree_oid));
456 write_pack_object(f, &pack_ctx, OBJ_COMMIT, buf.buf, buf.len, &commit_oid, algo);
457
458 /* 4. Write the empty tree */
459 write_pack_object(f, &pack_ctx, OBJ_TREE, "", 0, &empty_tree_oid, algo);
460
461 /* 5. Write final commit using empty tree, with previous commit as parent */
462 strbuf_reset(&buf);
463 strbuf_addf(&buf,
464 "tree %s\n"
465 "parent %s\n"
466 "author A U Thor <author@example.com> 1234567890 +0000\n"
467 "committer C O Mitter <committer@example.com> 1234567890 +0000\n"
468 "\n"
469 "Empty tree commit\n",
470 oid_to_hex(&empty_tree_oid),
471 oid_to_hex(&commit_oid));
472 write_pack_object(f, &pack_ctx, OBJ_COMMIT, buf.buf, buf.len, &final_commit_oid, algo);
473
474 /* Write pack trailer (checksum) */
475 algo->final_fn(pack_hash, &pack_ctx);
476 fwrite_or_die(f, pack_hash, algo->rawsz);
477 if (fclose(f))
478 die_errno(_("could not close '%s'"), path);
479
480 strbuf_release(&buf);
481
482 /* Print the final commit OID so caller can set up refs */
483 printf("%s\n", oid_to_hex(&final_commit_oid));
484
485 return 0;
486 }
487
488 static int cmd__synthesize__pack(int argc, const char **argv,
489 const char *prefix UNUSED,
490 struct repository *repo)
491 {
492 int non_git;
493 int reachable_large = 0;
494 const struct git_hash_algo *algo;
495 size_t blob_size;
496 uintmax_t blob_size_u;
497 const char *path;
498 const char * const usage[] = {
499 "test-tool synthesize pack "
500 "--reachable-large <blob-size> <filename>",
501 NULL
502 };
503 struct option options[] = {
504 OPT_BOOL(0, "reachable-large", &reachable_large,
505 N_("write a pack with a single reachable large blob")),
506 OPT_END()
507 };
508
509 setup_git_directory_gently(the_repository, &non_git);
510 repo = the_repository;
511 algo = unsafe_hash_algo(repo->hash_algo);
512
513 argc = parse_options(argc, argv, NULL, options, usage,
514 PARSE_OPT_KEEP_ARGV0);
515 if (argc != 3 || !reachable_large)
516 usage_with_options(usage, options);
517
518 if (!git_parse_unsigned(argv[1], &blob_size_u,
519 maximum_unsigned_value_of_type(size_t)))
520 die(_("'%s' is not a valid blob size"), argv[1]);
521 blob_size = blob_size_u;
522 path = argv[2];
523
524 return !!generate_pack_with_large_object(path, blob_size, algo);
525 }
526
527 int cmd__synthesize(int argc, const char **argv)
528 {
529 const char *prefix = NULL;
530 char const * const synthesize_usage[] = {
531 "test-tool synthesize pack <options>",
532 NULL,
533 };
534 parse_opt_subcommand_fn *fn = NULL;
535 struct option options[] = {
536 OPT_SUBCOMMAND("pack", &fn, cmd__synthesize__pack),
537 OPT_END()
538 };
539 argc = parse_options(argc, argv, prefix, options, synthesize_usage, 0);
540 return !!fn(argc, argv, prefix, NULL);
541 }