]> git.proxmox.com Git - libgit2.git/blame - src/odb_loose.c
Update d/ch for 0.28.4+dfsg.1-4 release
[libgit2.git] / src / odb_loose.c
CommitLineData
7d7cd885 1/*
359fc2d2 2 * Copyright (C) the libgit2 contributors. All rights reserved.
7d7cd885 3 *
bb742ede
VM
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
7d7cd885
VM
6 */
7
8#include "common.h"
eae0bfdc 9
0c3bae62 10#include <zlib.h>
44908fe7 11#include "git2/object.h"
83cc70d9 12#include "git2/sys/odb_backend.h"
7d7cd885
VM
13#include "fileops.h"
14#include "hash.h"
15#include "odb.h"
6a2d2f8a 16#include "delta.h"
72a3fe42 17#include "filebuf.h"
6d3ad7e0 18#include "object.h"
eae0bfdc 19#include "zstream.h"
7d7cd885 20
44908fe7 21#include "git2/odb_backend.h"
72a3fe42 22#include "git2/types.h"
7d7cd885 23
eae0bfdc
PP
24/* maximum possible header length */
25#define MAX_HEADER_LEN 64
26
87d9869f 27typedef struct { /* object header data */
ac3d33df 28 git_object_t type; /* object type */
87d9869f 29 size_t size; /* object size */
7d7cd885
VM
30} obj_hdr;
31
72a3fe42
VM
32typedef struct {
33 git_odb_stream stream;
34 git_filebuf fbuf;
72a3fe42
VM
35} loose_writestream;
36
eae0bfdc
PP
37typedef struct {
38 git_odb_stream stream;
39 git_map map;
40 char start[MAX_HEADER_LEN];
41 size_t start_len;
42 size_t start_read;
43 git_zstream zstream;
44} loose_readstream;
45
7d7cd885
VM
46typedef struct loose_backend {
47 git_odb_backend parent;
48
49 int object_zlib_level; /** loose object zlib compression level. */
50 int fsync_object_files; /** loose object file fsync flag. */
dd64c71c
ET
51 mode_t object_file_mode;
52 mode_t object_dir_mode;
8294e8cf
RB
53
54 size_t objects_dirlen;
55 char objects_dir[GIT_FLEX_ARRAY];
7d7cd885
VM
56} loose_backend;
57
aea8a638
MP
58/* State structure for exploring directories,
59 * in order to locate objects matching a short oid.
60 */
61typedef struct {
62 size_t dir_len;
63 unsigned char short_oid[GIT_OID_HEXSZ]; /* hex formatted oid to match */
b8457baa 64 size_t short_oid_len;
aea8a638
MP
65 int found; /* number of matching
66 * objects already found */
67 unsigned char res_oid[GIT_OID_HEXSZ]; /* hex formatted oid of
68 * the object found */
69} loose_locate_object_state;
70
71
7d7cd885
VM
72/***********************************************************
73 *
b874629b 74 * MISCELLANEOUS HELPER FUNCTIONS
7d7cd885
VM
75 *
76 ***********************************************************/
77
8294e8cf
RB
78static int object_file_name(
79 git_buf *name, const loose_backend *be, const git_oid *id)
7d7cd885 80{
f1453c59
ET
81 size_t alloclen;
82
8294e8cf 83 /* expand length for object root + 40 hex sha1 chars + 2 * '/' + '\0' */
ac3d33df
JK
84 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, be->objects_dirlen, GIT_OID_HEXSZ);
85 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 3);
f1453c59 86 if (git_buf_grow(name, alloclen) < 0)
e1de726c 87 return -1;
7d7cd885 88
8294e8cf 89 git_buf_set(name, be->objects_dir, be->objects_dirlen);
97769280 90 git_path_to_dir(name);
7d7cd885
VM
91
92 /* loose object filename: aa/aaa... (41 bytes) */
8294e8cf 93 git_oid_pathfmt(name->ptr + name->size, id);
97769280
RB
94 name->size += GIT_OID_HEXSZ + 1;
95 name->ptr[name->size] = '\0';
7d7cd885 96
e1de726c 97 return 0;
7d7cd885
VM
98}
99
8294e8cf
RB
100static int object_mkdir(const git_buf *name, const loose_backend *be)
101{
ac2fba0e 102 return git_futils_mkdir_relative(
dd64c71c 103 name->ptr + be->objects_dirlen, be->objects_dir, be->object_dir_mode,
ac2fba0e 104 GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR, NULL);
8294e8cf 105}
7d7cd885 106
eae0bfdc
PP
107static int parse_header_packlike(
108 obj_hdr *out, size_t *out_len, const unsigned char *data, size_t len)
7d7cd885 109{
7f407710 110 unsigned long c;
7d7cd885
VM
111 size_t shift, size, used = 0;
112
eae0bfdc
PP
113 if (len == 0)
114 goto on_error;
7d7cd885
VM
115
116 c = data[used++];
eae0bfdc 117 out->type = (c >> 4) & 7;
7d7cd885
VM
118
119 size = c & 15;
120 shift = 4;
121 while (c & 0x80) {
eae0bfdc
PP
122 if (len <= used)
123 goto on_error;
124
7d7cd885 125 if (sizeof(size_t) * 8 <= shift)
eae0bfdc
PP
126 goto on_error;
127
7d7cd885
VM
128 c = data[used++];
129 size += (c & 0x7f) << shift;
130 shift += 7;
131 }
7d7cd885 132
eae0bfdc 133 out->size = size;
7d7cd885 134
eae0bfdc
PP
135 if (out_len)
136 *out_len = used;
7d7cd885 137
eae0bfdc 138 return 0;
7d7cd885 139
eae0bfdc 140on_error:
ac3d33df 141 git_error_set(GIT_ERROR_OBJECT, "failed to parse loose object: invalid header");
eae0bfdc 142 return -1;
7d7cd885
VM
143}
144
eae0bfdc
PP
145static int parse_header(
146 obj_hdr *out,
147 size_t *out_len,
148 const unsigned char *_data,
149 size_t data_len)
7d7cd885 150{
eae0bfdc
PP
151 const char *data = (char *)_data;
152 size_t i, typename_len, size_idx, size_len;
153 int64_t size;
7d7cd885 154
eae0bfdc 155 *out_len = 0;
7d7cd885 156
eae0bfdc
PP
157 /* find the object type name */
158 for (i = 0, typename_len = 0; i < data_len; i++, typename_len++) {
159 if (data[i] == ' ')
160 break;
161 }
7d7cd885 162
eae0bfdc
PP
163 if (typename_len == data_len)
164 goto on_error;
7d7cd885 165
eae0bfdc 166 out->type = git_object_stringn2type(data, typename_len);
1dc89aab 167
eae0bfdc
PP
168 size_idx = typename_len + 1;
169 for (i = size_idx, size_len = 0; i < data_len; i++, size_len++) {
170 if (data[i] == '\0')
171 break;
172 }
7d7cd885 173
eae0bfdc
PP
174 if (i == data_len)
175 goto on_error;
7d7cd885 176
eae0bfdc
PP
177 if (git__strntol64(&size, &data[size_idx], size_len, NULL, 10) < 0 ||
178 size < 0)
179 goto on_error;
7d7cd885 180
eae0bfdc 181 if ((uint64_t)size > SIZE_MAX) {
ac3d33df 182 git_error_set(GIT_ERROR_OBJECT, "object is larger than available memory");
e1de726c
RB
183 return -1;
184 }
7d7cd885 185
ac3d33df 186 out->size = (size_t)size;
eae0bfdc
PP
187
188 if (GIT_ADD_SIZET_OVERFLOW(out_len, i, 1))
189 goto on_error;
190
e1de726c 191 return 0;
eae0bfdc
PP
192
193on_error:
ac3d33df 194 git_error_set(GIT_ERROR_OBJECT, "failed to parse loose object: invalid header");
eae0bfdc 195 return -1;
7d7cd885
VM
196}
197
eae0bfdc 198static int is_zlib_compressed_data(unsigned char *data, size_t data_len)
7d7cd885 199{
72a3fe42 200 unsigned int w;
7d7cd885 201
eae0bfdc
PP
202 if (data_len < 2)
203 return 0;
204
72a3fe42 205 w = ((unsigned int)(data[0]) << 8) + data[1];
c51065e3 206 return (data[0] & 0x8F) == 0x08 && !(w % 31);
7d7cd885
VM
207}
208
eae0bfdc
PP
209/***********************************************************
210 *
211 * ODB OBJECT READING & WRITING
212 *
213 * Backend for the public API; read headers and full objects
214 * from the ODB. Write raw data to the ODB.
215 *
216 ***********************************************************/
7d7cd885 217
7d7cd885
VM
218
219/*
220 * At one point, there was a loose object format that was intended to
221 * mimic the format used in pack-files. This was to allow easy copying
222 * of loose object data into packs. This format is no longer used, but
223 * we must still read it.
224 */
eae0bfdc 225static int read_loose_packlike(git_rawobj *out, git_buf *obj)
7d7cd885 226{
eae0bfdc
PP
227 git_buf body = GIT_BUF_INIT;
228 const unsigned char *obj_data;
7d7cd885 229 obj_hdr hdr;
eae0bfdc
PP
230 size_t obj_len, head_len, alloc_size;
231 int error;
232
233 obj_data = (unsigned char *)obj->ptr;
234 obj_len = obj->size;
7d7cd885
VM
235
236 /*
237 * read the object header, which is an (uncompressed)
238 * binary encoding of the object type and size.
239 */
eae0bfdc
PP
240 if ((error = parse_header_packlike(&hdr, &head_len, obj_data, obj_len)) < 0)
241 goto done;
242
243 if (!git_object_typeisloose(hdr.type) || head_len > obj_len) {
ac3d33df 244 git_error_set(GIT_ERROR_ODB, "failed to inflate loose object");
eae0bfdc
PP
245 error = -1;
246 goto done;
e1de726c 247 }
7d7cd885 248
eae0bfdc
PP
249 obj_data += head_len;
250 obj_len -= head_len;
251
7d7cd885
VM
252 /*
253 * allocate a buffer and inflate the data into it
254 */
eae0bfdc
PP
255 if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr.size, 1) ||
256 git_buf_init(&body, alloc_size) < 0) {
257 error = -1;
258 goto done;
7d7cd885 259 }
7d7cd885 260
eae0bfdc
PP
261 if ((error = git_zstream_inflatebuf(&body, obj_data, obj_len)) < 0)
262 goto done;
263
87d9869f 264 out->len = hdr.size;
7d7cd885 265 out->type = hdr.type;
eae0bfdc 266 out->data = git_buf_detach(&body);
7d7cd885 267
eae0bfdc 268done:
ac3d33df 269 git_buf_dispose(&body);
eae0bfdc 270 return error;
7d7cd885
VM
271}
272
eae0bfdc 273static int read_loose_standard(git_rawobj *out, git_buf *obj)
7d7cd885 274{
eae0bfdc
PP
275 git_zstream zstream = GIT_ZSTREAM_INIT;
276 unsigned char head[MAX_HEADER_LEN], *body = NULL;
277 size_t decompressed, head_len, body_len, alloc_size;
7d7cd885 278 obj_hdr hdr;
eae0bfdc 279 int error;
7d7cd885 280
eae0bfdc
PP
281 if ((error = git_zstream_init(&zstream, GIT_ZSTREAM_INFLATE)) < 0 ||
282 (error = git_zstream_set_input(&zstream, git_buf_cstr(obj), git_buf_len(obj))) < 0)
283 goto done;
284
285 decompressed = sizeof(head);
7d7cd885
VM
286
287 /*
eae0bfdc
PP
288 * inflate the initial part of the compressed buffer in order to
289 * parse the header; read the largest header possible, then push the
290 * remainder into the body buffer.
7d7cd885 291 */
eae0bfdc
PP
292 if ((error = git_zstream_get_output(head, &decompressed, &zstream)) < 0 ||
293 (error = parse_header(&hdr, &head_len, head, decompressed)) < 0)
294 goto done;
295
296 if (!git_object_typeisloose(hdr.type)) {
ac3d33df 297 git_error_set(GIT_ERROR_ODB, "failed to inflate disk object");
eae0bfdc
PP
298 error = -1;
299 goto done;
e1de726c 300 }
7d7cd885
VM
301
302 /*
303 * allocate a buffer and inflate the object data into it
304 * (including the initial sequence in the head buffer).
305 */
eae0bfdc
PP
306 if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr.size, 1) ||
307 (body = git__malloc(alloc_size)) == NULL) {
308 error = -1;
309 goto done;
310 }
7d7cd885 311
eae0bfdc
PP
312 assert(decompressed >= head_len);
313 body_len = decompressed - head_len;
7d7cd885 314
eae0bfdc
PP
315 if (body_len)
316 memcpy(body, head + head_len, body_len);
7d7cd885 317
eae0bfdc
PP
318 decompressed = hdr.size - body_len;
319 if ((error = git_zstream_get_output(body + body_len, &decompressed, &zstream)) < 0)
320 goto done;
7d7cd885 321
eae0bfdc 322 if (!git_zstream_done(&zstream)) {
ac3d33df 323 git_error_set(GIT_ERROR_ZLIB, "failed to finish zlib inflation: stream aborted prematurely");
eae0bfdc
PP
324 error = -1;
325 goto done;
326 }
7d7cd885 327
eae0bfdc 328 body[hdr.size] = '\0';
7d7cd885 329
eae0bfdc
PP
330 out->data = body;
331 out->len = hdr.size;
332 out->type = hdr.type;
7d7cd885 333
eae0bfdc
PP
334done:
335 if (error < 0)
336 git__free(body);
7d7cd885 337
eae0bfdc
PP
338 git_zstream_free(&zstream);
339 return error;
340}
7d7cd885 341
97769280 342static int read_loose(git_rawobj *out, git_buf *loc)
7d7cd885
VM
343{
344 int error;
13224ea4 345 git_buf obj = GIT_BUF_INIT;
7d7cd885
VM
346
347 assert(out && loc);
348
cb8a7961 349 if (git_buf_oom(loc))
e1de726c 350 return -1;
97769280 351
7d7cd885 352 out->data = NULL;
87d9869f 353 out->len = 0;
ac3d33df 354 out->type = GIT_OBJECT_INVALID;
7d7cd885 355
eae0bfdc
PP
356 if ((error = git_futils_readbuffer(&obj, loc->ptr)) < 0)
357 goto done;
7d7cd885 358
eae0bfdc
PP
359 if (!is_zlib_compressed_data((unsigned char *)obj.ptr, obj.size))
360 error = read_loose_packlike(out, &obj);
361 else
362 error = read_loose_standard(out, &obj);
363
364done:
ac3d33df 365 git_buf_dispose(&obj);
eae0bfdc
PP
366 return error;
367}
368
369static int read_header_loose_packlike(
370 git_rawobj *out, const unsigned char *data, size_t len)
371{
372 obj_hdr hdr;
373 size_t header_len;
374 int error;
375
376 if ((error = parse_header_packlike(&hdr, &header_len, data, len)) < 0)
377 return error;
378
379 out->len = hdr.size;
380 out->type = hdr.type;
381
382 return error;
383}
384
385static int read_header_loose_standard(
386 git_rawobj *out, const unsigned char *data, size_t len)
387{
388 git_zstream zs = GIT_ZSTREAM_INIT;
389 obj_hdr hdr;
390 unsigned char inflated[MAX_HEADER_LEN];
391 size_t header_len, inflated_len = sizeof(inflated);
392 int error;
7d7cd885 393
eae0bfdc
PP
394 if ((error = git_zstream_init(&zs, GIT_ZSTREAM_INFLATE)) < 0 ||
395 (error = git_zstream_set_input(&zs, data, len)) < 0 ||
396 (error = git_zstream_get_output_chunk(inflated, &inflated_len, &zs)) < 0 ||
397 (error = parse_header(&hdr, &header_len, inflated, inflated_len)) < 0)
398 goto done;
399
400 out->len = hdr.size;
401 out->type = hdr.type;
402
403done:
404 git_zstream_free(&zs);
e1de726c 405 return error;
7d7cd885
VM
406}
407
97769280 408static int read_header_loose(git_rawobj *out, git_buf *loc)
7d7cd885 409{
eae0bfdc
PP
410 unsigned char obj[1024];
411 int fd, obj_len, error;
7d7cd885
VM
412
413 assert(out && loc);
414
cb8a7961 415 if (git_buf_oom(loc))
e1de726c 416 return -1;
97769280 417
7d7cd885
VM
418 out->data = NULL;
419
eae0bfdc
PP
420 if ((error = fd = git_futils_open_ro(loc->ptr)) < 0 ||
421 (error = obj_len = p_read(fd, obj, sizeof(obj))) < 0)
422 goto done;
7d7cd885 423
eae0bfdc
PP
424 if (!is_zlib_compressed_data(obj, (size_t)obj_len))
425 error = read_header_loose_packlike(out, obj, (size_t)obj_len);
426 else
427 error = read_header_loose_standard(out, obj, (size_t)obj_len);
7d7cd885 428
eae0bfdc 429 if (!error && !git_object_typeisloose(out->type)) {
ac3d33df 430 git_error_set(GIT_ERROR_ZLIB, "failed to read loose object header");
e1de726c 431 error = -1;
eae0bfdc 432 goto done;
7d7cd885
VM
433 }
434
eae0bfdc
PP
435done:
436 if (fd >= 0)
437 p_close(fd);
e1de726c 438 return error;
7d7cd885
VM
439}
440
97769280
RB
441static int locate_object(
442 git_buf *object_location,
443 loose_backend *backend,
444 const git_oid *oid)
7d7cd885 445{
8294e8cf 446 int error = object_file_name(object_location, backend, oid);
97769280 447
e1de726c
RB
448 if (!error && !git_path_exists(object_location->ptr))
449 return GIT_ENOTFOUND;
97769280
RB
450
451 return error;
7d7cd885
VM
452}
453
aea8a638 454/* Explore an entry of a directory and see if it matches a short oid */
97769280 455static int fn_locate_object_short_oid(void *state, git_buf *pathbuf) {
aea8a638
MP
456 loose_locate_object_state *sstate = (loose_locate_object_state *)state;
457
fa6420f7 458 if (git_buf_len(pathbuf) - sstate->dir_len != GIT_OID_HEXSZ - 2) {
aea8a638 459 /* Entry cannot be an object. Continue to next entry */
e1de726c 460 return 0;
aea8a638
MP
461 }
462
9d160ba8 463 if (git_path_isdir(pathbuf->ptr) == false) {
f1d01851
VM
464 /* We are already in the directory matching the 2 first hex characters,
465 * compare the first ncmp characters of the oids */
466 if (!memcmp(sstate->short_oid + 2,
97769280 467 (unsigned char *)pathbuf->ptr + sstate->dir_len,
f1d01851
VM
468 sstate->short_oid_len - 2)) {
469
aea8a638
MP
470 if (!sstate->found) {
471 sstate->res_oid[0] = sstate->short_oid[0];
472 sstate->res_oid[1] = sstate->short_oid[1];
97769280 473 memcpy(sstate->res_oid+2, pathbuf->ptr+sstate->dir_len, GIT_OID_HEXSZ-2);
aea8a638
MP
474 }
475 sstate->found++;
476 }
477 }
e1de726c 478
d0323a5f 479 if (sstate->found > 1)
209f9b67 480 return GIT_EAMBIGUOUS;
d0323a5f 481
e1de726c 482 return 0;
aea8a638
MP
483}
484
485/* Locate an object matching a given short oid */
97769280
RB
486static int locate_object_short_oid(
487 git_buf *object_location,
488 git_oid *res_oid,
489 loose_backend *backend,
490 const git_oid *short_oid,
b8457baa 491 size_t len)
aea8a638
MP
492{
493 char *objects_dir = backend->objects_dir;
f1453c59 494 size_t dir_len = strlen(objects_dir), alloc_len;
aea8a638
MP
495 loose_locate_object_state state;
496 int error;
497
f5753999 498 /* prealloc memory for OBJ_DIR/xx/xx..38x..xx */
ac3d33df
JK
499 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ);
500 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 3);
f1453c59 501 if (git_buf_grow(object_location, alloc_len) < 0)
e1de726c 502 return -1;
aea8a638 503
f5753999 504 git_buf_set(object_location, objects_dir, dir_len);
97769280 505 git_path_to_dir(object_location);
aea8a638 506
97769280 507 /* save adjusted position at end of dir so it can be restored later */
fa6420f7 508 dir_len = git_buf_len(object_location);
aea8a638
MP
509
510 /* Convert raw oid to hex formatted oid */
d0323a5f 511 git_oid_fmt((char *)state.short_oid, short_oid);
97769280 512
aea8a638 513 /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */
f5753999 514 if (git_buf_put(object_location, (char *)state.short_oid, 3) < 0)
e1de726c 515 return -1;
f5753999 516 object_location->ptr[object_location->size - 1] = '/';
aea8a638
MP
517
518 /* Check that directory exists */
1a481123 519 if (git_path_isdir(object_location->ptr) == false)
e10144ae
ET
520 return git_odb__error_notfound("no matching loose object for prefix",
521 short_oid, len);
aea8a638 522
fa6420f7 523 state.dir_len = git_buf_len(object_location);
aea8a638
MP
524 state.short_oid_len = len;
525 state.found = 0;
97769280 526
aea8a638 527 /* Explore directory to find a unique object matching short_oid */
e1de726c 528 error = git_path_direach(
219d3457 529 object_location, 0, fn_locate_object_short_oid, &state);
25e0b157 530 if (error < 0 && error != GIT_EAMBIGUOUS)
e1de726c 531 return error;
97769280 532
e1de726c 533 if (!state.found)
e10144ae
ET
534 return git_odb__error_notfound("no matching loose object for prefix",
535 short_oid, len);
aea8a638 536
209f9b67 537 if (state.found > 1)
538 return git_odb__error_ambiguous("multiple matches in loose objects");
539
aea8a638 540 /* Convert obtained hex formatted oid to raw */
fa48608e 541 error = git_oid_fromstr(res_oid, (char *)state.res_oid);
e1de726c
RB
542 if (error)
543 return error;
aea8a638
MP
544
545 /* Update the location according to the oid obtained */
ac3d33df
JK
546 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ);
547 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
97769280
RB
548
549 git_buf_truncate(object_location, dir_len);
f1453c59 550 if (git_buf_grow(object_location, alloc_len) < 0)
e1de726c 551 return -1;
97769280
RB
552
553 git_oid_pathfmt(object_location->ptr + dir_len, res_oid);
554
555 object_location->size += GIT_OID_HEXSZ + 1;
556 object_location->ptr[object_location->size] = '\0';
aea8a638 557
e1de726c 558 return 0;
aea8a638
MP
559}
560
7d7cd885
VM
561
562
563
564
565
566
567
568
569/***********************************************************
570 *
571 * LOOSE BACKEND PUBLIC API
572 *
573 * Implement the git_odb_backend API calls
574 *
575 ***********************************************************/
576
ac3d33df 577static int loose_backend__read_header(size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *oid)
7d7cd885 578{
97769280 579 git_buf object_path = GIT_BUF_INIT;
72a3fe42 580 git_rawobj raw;
0d0fa7c3 581 int error;
7d7cd885 582
72a3fe42 583 assert(backend && oid);
7d7cd885 584
60e1b49a 585 raw.len = 0;
ac3d33df 586 raw.type = GIT_OBJECT_INVALID;
60e1b49a 587
e10144ae
ET
588 if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
589 error = git_odb__error_notfound("no matching loose object",
590 oid, GIT_OID_HEXSZ);
591 } else if ((error = read_header_loose(&raw, &object_path)) == 0) {
97769280
RB
592 *len_p = raw.len;
593 *type_p = raw.type;
594 }
7d7cd885 595
ac3d33df 596 git_buf_dispose(&object_path);
7d7cd885 597
97769280 598 return error;
72a3fe42 599}
7d7cd885 600
ac3d33df 601static int loose_backend__read(void **buffer_p, size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *oid)
7d7cd885 602{
97769280 603 git_buf object_path = GIT_BUF_INIT;
72a3fe42 604 git_rawobj raw;
e1de726c 605 int error = 0;
7d7cd885 606
72a3fe42 607 assert(backend && oid);
7d7cd885 608
e10144ae
ET
609 if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
610 error = git_odb__error_notfound("no matching loose object",
611 oid, GIT_OID_HEXSZ);
612 } else if ((error = read_loose(&raw, &object_path)) == 0) {
97769280
RB
613 *buffer_p = raw.data;
614 *len_p = raw.len;
615 *type_p = raw.type;
616 }
72a3fe42 617
ac3d33df 618 git_buf_dispose(&object_path);
72a3fe42 619
97769280 620 return error;
7d7cd885
VM
621}
622
d568d585 623static int loose_backend__read_prefix(
d0323a5f
VM
624 git_oid *out_oid,
625 void **buffer_p,
626 size_t *len_p,
ac3d33df 627 git_object_t *type_p,
d0323a5f
VM
628 git_odb_backend *backend,
629 const git_oid *short_oid,
b8457baa 630 size_t len)
ecd6fdf1 631{
e1de726c 632 int error = 0;
97769280 633
26875825 634 assert(len >= GIT_OID_MINPREFIXLEN && len <= GIT_OID_HEXSZ);
d19dd9cf 635
26875825 636 if (len == GIT_OID_HEXSZ) {
aea8a638 637 /* We can fall back to regular read method */
97769280 638 error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
e1de726c 639 if (!error)
ecd6fdf1 640 git_oid_cpy(out_oid, short_oid);
aea8a638 641 } else {
97769280 642 git_buf object_path = GIT_BUF_INIT;
aea8a638 643 git_rawobj raw;
aea8a638
MP
644
645 assert(backend && short_oid);
646
97769280 647 if ((error = locate_object_short_oid(&object_path, out_oid,
e1de726c
RB
648 (loose_backend *)backend, short_oid, len)) == 0 &&
649 (error = read_loose(&raw, &object_path)) == 0)
650 {
97769280
RB
651 *buffer_p = raw.data;
652 *len_p = raw.len;
653 *type_p = raw.type;
aea8a638
MP
654 }
655
ac3d33df 656 git_buf_dispose(&object_path);
ecd6fdf1 657 }
aea8a638 658
97769280 659 return error;
ecd6fdf1
MP
660}
661
d568d585 662static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
7d7cd885 663{
97769280
RB
664 git_buf object_path = GIT_BUF_INIT;
665 int error;
7d7cd885
VM
666
667 assert(backend && oid);
668
97769280
RB
669 error = locate_object(&object_path, (loose_backend *)backend, oid);
670
ac3d33df 671 git_buf_dispose(&object_path);
97769280 672
e1de726c 673 return !error;
7d7cd885
VM
674}
675
f5753999
RB
676static int loose_backend__exists_prefix(
677 git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len)
678{
679 git_buf object_path = GIT_BUF_INIT;
680 int error;
681
26875825 682 assert(backend && out && short_id && len >= GIT_OID_MINPREFIXLEN);
f5753999
RB
683
684 error = locate_object_short_oid(
685 &object_path, out, (loose_backend *)backend, short_id, len);
686
ac3d33df 687 git_buf_dispose(&object_path);
f5753999
RB
688
689 return error;
690}
691
521aedad
CMN
692struct foreach_state {
693 size_t dir_len;
c3fb7d04 694 git_odb_foreach_cb cb;
521aedad
CMN
695 void *data;
696};
697
b7158c53 698GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr)
521aedad
CMN
699{
700 int v, i = 0;
3b2cb2c9 701 if (strlen(ptr) != GIT_OID_HEXSZ+1)
521aedad
CMN
702 return -1;
703
704 if (ptr[2] != '/') {
705 return -1;
706 }
707
708 v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i+1]);
709 if (v < 0)
710 return -1;
711
712 oid->id[0] = (unsigned char) v;
713
714 ptr += 3;
715 for (i = 0; i < 38; i += 2) {
716 v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i + 1]);
717 if (v < 0)
718 return -1;
719
720 oid->id[1 + i/2] = (unsigned char) v;
721 }
722
723 return 0;
724}
725
726static int foreach_object_dir_cb(void *_state, git_buf *path)
727{
728 git_oid oid;
729 struct foreach_state *state = (struct foreach_state *) _state;
730
731 if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0)
732 return 0;
733
ac3d33df 734 return git_error_set_after_callback_function(
25e0b157 735 state->cb(&oid, state->data), "git_odb_foreach");
521aedad
CMN
736}
737
738static int foreach_cb(void *_state, git_buf *path)
739{
740 struct foreach_state *state = (struct foreach_state *) _state;
741
ee311907
CMN
742 /* non-dir is some stray file, ignore it */
743 if (!git_path_isdir(git_buf_cstr(path)))
744 return 0;
745
25e0b157 746 return git_path_direach(path, 0, foreach_object_dir_cb, state);
521aedad
CMN
747}
748
c3fb7d04 749static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
521aedad
CMN
750{
751 char *objects_dir;
752 int error;
753 git_buf buf = GIT_BUF_INIT;
754 struct foreach_state state;
755 loose_backend *backend = (loose_backend *) _backend;
756
757 assert(backend && cb);
758
759 objects_dir = backend->objects_dir;
760
25e0b157 761 git_buf_sets(&buf, objects_dir);
521aedad 762 git_path_to_dir(&buf);
25e0b157
RB
763 if (git_buf_oom(&buf))
764 return -1;
521aedad 765
5dca2010 766 memset(&state, 0, sizeof(state));
521aedad
CMN
767 state.cb = cb;
768 state.data = data;
769 state.dir_len = git_buf_len(&buf);
770
219d3457 771 error = git_path_direach(&buf, 0, foreach_cb, &state);
5dca2010 772
ac3d33df 773 git_buf_dispose(&buf);
521aedad 774
96869a4e 775 return error;
521aedad
CMN
776}
777
eae0bfdc 778static int loose_backend__writestream_finalize(git_odb_stream *_stream, const git_oid *oid)
72a3fe42
VM
779{
780 loose_writestream *stream = (loose_writestream *)_stream;
781 loose_backend *backend = (loose_backend *)_stream->backend;
97769280 782 git_buf final_path = GIT_BUF_INIT;
e1de726c 783 int error = 0;
72a3fe42 784
8380b39a 785 if (object_file_name(&final_path, backend, oid) < 0 ||
8294e8cf 786 object_mkdir(&final_path, backend) < 0)
e1de726c 787 error = -1;
e1de726c
RB
788 else
789 error = git_filebuf_commit_at(
1d3a8aeb 790 &stream->fbuf, final_path.ptr);
97769280 791
ac3d33df 792 git_buf_dispose(&final_path);
97769280 793
97769280 794 return error;
72a3fe42
VM
795}
796
eae0bfdc 797static int loose_backend__writestream_write(git_odb_stream *_stream, const char *data, size_t len)
7d7cd885 798{
72a3fe42
VM
799 loose_writestream *stream = (loose_writestream *)_stream;
800 return git_filebuf_write(&stream->fbuf, data, len);
801}
802
eae0bfdc 803static void loose_backend__writestream_free(git_odb_stream *_stream)
72a3fe42
VM
804{
805 loose_writestream *stream = (loose_writestream *)_stream;
806
97769280 807 git_filebuf_cleanup(&stream->fbuf);
3286c408 808 git__free(stream);
72a3fe42
VM
809}
810
fc27fe21
ET
811static int filebuf_flags(loose_backend *backend)
812{
813 int flags = GIT_FILEBUF_TEMPORARY |
814 (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT);
815
6c23704d 816 if (backend->fsync_object_files || git_repository__fsync_gitdir)
fc27fe21
ET
817 flags |= GIT_FILEBUF_FSYNC;
818
819 return flags;
820}
821
ac3d33df 822static int loose_backend__writestream(git_odb_stream **stream_out, git_odb_backend *_backend, git_off_t length, git_object_t type)
72a3fe42
VM
823{
824 loose_backend *backend;
e1de726c 825 loose_writestream *stream = NULL;
eae0bfdc 826 char hdr[MAX_HEADER_LEN];
97769280 827 git_buf tmp_path = GIT_BUF_INIT;
eae0bfdc
PP
828 size_t hdrlen;
829 int error;
7d7cd885 830
77b339f7 831 assert(_backend && length >= 0);
7d7cd885
VM
832
833 backend = (loose_backend *)_backend;
72a3fe42 834 *stream_out = NULL;
7d7cd885 835
eae0bfdc
PP
836 if ((error = git_odb__format_object_header(&hdrlen,
837 hdr, sizeof(hdr), length, type)) < 0)
838 return error;
72a3fe42
VM
839
840 stream = git__calloc(1, sizeof(loose_writestream));
ac3d33df 841 GIT_ERROR_CHECK_ALLOC(stream);
7d7cd885 842
72a3fe42
VM
843 stream->stream.backend = _backend;
844 stream->stream.read = NULL; /* read only */
eae0bfdc
PP
845 stream->stream.write = &loose_backend__writestream_write;
846 stream->stream.finalize_write = &loose_backend__writestream_finalize;
847 stream->stream.free = &loose_backend__writestream_free;
72a3fe42 848 stream->stream.mode = GIT_STREAM_WRONLY;
7d7cd885 849
e1de726c 850 if (git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||
fc27fe21 851 git_filebuf_open(&stream->fbuf, tmp_path.ptr, filebuf_flags(backend),
1d3a8aeb 852 backend->object_file_mode) < 0 ||
e1de726c
RB
853 stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0)
854 {
855 git_filebuf_cleanup(&stream->fbuf);
856 git__free(stream);
857 stream = NULL;
858 }
ac3d33df 859 git_buf_dispose(&tmp_path);
72a3fe42 860 *stream_out = (git_odb_stream *)stream;
97769280 861
e1de726c 862 return !stream ? -1 : 0;
7d7cd885
VM
863}
864
eae0bfdc
PP
865static int loose_backend__readstream_read(
866 git_odb_stream *_stream,
867 char *buffer,
868 size_t buffer_len)
869{
870 loose_readstream *stream = (loose_readstream *)_stream;
871 size_t start_remain = stream->start_len - stream->start_read;
872 int total = 0, error;
873
874 /*
875 * if we read more than just the header in the initial read, play
876 * that back for the caller.
877 */
878 if (start_remain && buffer_len) {
879 size_t chunk = min(start_remain, buffer_len);
880 memcpy(buffer, stream->start + stream->start_read, chunk);
881
882 buffer += chunk;
883 stream->start_read += chunk;
884
885 total += chunk;
886 buffer_len -= chunk;
887 }
888
889 if (buffer_len) {
890 size_t chunk = min(buffer_len, INT_MAX);
891
892 if ((error = git_zstream_get_output(buffer, &chunk, &stream->zstream)) < 0)
893 return error;
894
895 total += chunk;
896 }
897
898 return total;
899}
900
901static void loose_backend__readstream_free(git_odb_stream *_stream)
902{
903 loose_readstream *stream = (loose_readstream *)_stream;
904
905 git_futils_mmap_free(&stream->map);
906 git_zstream_free(&stream->zstream);
907 git__free(stream);
908}
909
910static int loose_backend__readstream_packlike(
911 obj_hdr *hdr,
912 loose_readstream *stream)
913{
914 const unsigned char *data;
915 size_t data_len, head_len;
916 int error;
917
918 data = stream->map.data;
919 data_len = stream->map.len;
920
921 /*
922 * read the object header, which is an (uncompressed)
923 * binary encoding of the object type and size.
924 */
925 if ((error = parse_header_packlike(hdr, &head_len, data, data_len)) < 0)
926 return error;
927
928 if (!git_object_typeisloose(hdr->type)) {
ac3d33df 929 git_error_set(GIT_ERROR_ODB, "failed to inflate loose object");
eae0bfdc
PP
930 return -1;
931 }
932
933 return git_zstream_set_input(&stream->zstream,
934 data + head_len, data_len - head_len);
935}
936
937static int loose_backend__readstream_standard(
938 obj_hdr *hdr,
939 loose_readstream *stream)
940{
941 unsigned char head[MAX_HEADER_LEN];
942 size_t init, head_len;
943 int error;
944
945 if ((error = git_zstream_set_input(&stream->zstream,
946 stream->map.data, stream->map.len)) < 0)
947 return error;
948
949 init = sizeof(head);
950
951 /*
952 * inflate the initial part of the compressed buffer in order to
953 * parse the header; read the largest header possible, then store
954 * it in the `start` field of the stream object.
955 */
956 if ((error = git_zstream_get_output(head, &init, &stream->zstream)) < 0 ||
957 (error = parse_header(hdr, &head_len, head, init)) < 0)
958 return error;
959
960 if (!git_object_typeisloose(hdr->type)) {
ac3d33df 961 git_error_set(GIT_ERROR_ODB, "failed to inflate disk object");
eae0bfdc
PP
962 return -1;
963 }
964
965 if (init > head_len) {
966 stream->start_len = init - head_len;
967 memcpy(stream->start, head + head_len, init - head_len);
968 }
969
970 return 0;
971}
972
973static int loose_backend__readstream(
974 git_odb_stream **stream_out,
975 size_t *len_out,
ac3d33df 976 git_object_t *type_out,
eae0bfdc
PP
977 git_odb_backend *_backend,
978 const git_oid *oid)
979{
980 loose_backend *backend;
981 loose_readstream *stream = NULL;
982 git_hash_ctx *hash_ctx = NULL;
983 git_buf object_path = GIT_BUF_INIT;
984 obj_hdr hdr;
985 int error = 0;
986
987 assert(stream_out && len_out && type_out && _backend && oid);
988
989 backend = (loose_backend *)_backend;
990 *stream_out = NULL;
991 *len_out = 0;
ac3d33df 992 *type_out = GIT_OBJECT_INVALID;
eae0bfdc
PP
993
994 if (locate_object(&object_path, backend, oid) < 0) {
995 error = git_odb__error_notfound("no matching loose object",
996 oid, GIT_OID_HEXSZ);
997 goto done;
998 }
999
1000 stream = git__calloc(1, sizeof(loose_readstream));
ac3d33df 1001 GIT_ERROR_CHECK_ALLOC(stream);
eae0bfdc
PP
1002
1003 hash_ctx = git__malloc(sizeof(git_hash_ctx));
ac3d33df 1004 GIT_ERROR_CHECK_ALLOC(hash_ctx);
eae0bfdc
PP
1005
1006 if ((error = git_hash_ctx_init(hash_ctx)) < 0 ||
1007 (error = git_futils_mmap_ro_file(&stream->map, object_path.ptr)) < 0 ||
1008 (error = git_zstream_init(&stream->zstream, GIT_ZSTREAM_INFLATE)) < 0)
1009 goto done;
1010
1011 /* check for a packlike loose object */
1012 if (!is_zlib_compressed_data(stream->map.data, stream->map.len))
1013 error = loose_backend__readstream_packlike(&hdr, stream);
1014 else
1015 error = loose_backend__readstream_standard(&hdr, stream);
1016
1017 if (error < 0)
1018 goto done;
1019
1020 stream->stream.backend = _backend;
1021 stream->stream.hash_ctx = hash_ctx;
1022 stream->stream.read = &loose_backend__readstream_read;
1023 stream->stream.free = &loose_backend__readstream_free;
1024
1025 *stream_out = (git_odb_stream *)stream;
1026 *len_out = hdr.size;
1027 *type_out = hdr.type;
1028
1029done:
1030 if (error < 0) {
ac3d33df
JK
1031 if (stream) {
1032 git_futils_mmap_free(&stream->map);
1033 git_zstream_free(&stream->zstream);
1034 git__free(stream);
1035 }
1036 if (hash_ctx) {
1037 git_hash_ctx_cleanup(hash_ctx);
1038 git__free(hash_ctx);
1039 }
eae0bfdc
PP
1040 }
1041
ac3d33df 1042 git_buf_dispose(&object_path);
eae0bfdc
PP
1043 return error;
1044}
1045
ac3d33df 1046static int loose_backend__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_object_t type)
afeecf4f 1047{
eae0bfdc 1048 int error = 0;
97769280 1049 git_buf final_path = GIT_BUF_INIT;
eae0bfdc
PP
1050 char header[MAX_HEADER_LEN];
1051 size_t header_len;
b762e576 1052 git_filebuf fbuf = GIT_FILEBUF_INIT;
afeecf4f
VM
1053 loose_backend *backend;
1054
1055 backend = (loose_backend *)_backend;
1056
1057 /* prepare the header for the file */
eae0bfdc
PP
1058 if ((error = git_odb__format_object_header(&header_len,
1059 header, sizeof(header), len, type)) < 0)
1060 goto cleanup;
afeecf4f 1061
e1de726c 1062 if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||
fc27fe21 1063 git_filebuf_open(&fbuf, final_path.ptr, filebuf_flags(backend),
1d3a8aeb 1064 backend->object_file_mode) < 0)
e1de726c
RB
1065 {
1066 error = -1;
97769280 1067 goto cleanup;
e1de726c 1068 }
afeecf4f
VM
1069
1070 git_filebuf_write(&fbuf, header, header_len);
1071 git_filebuf_write(&fbuf, data, len);
afeecf4f 1072
8294e8cf
RB
1073 if (object_file_name(&final_path, backend, oid) < 0 ||
1074 object_mkdir(&final_path, backend) < 0 ||
1d3a8aeb 1075 git_filebuf_commit_at(&fbuf, final_path.ptr) < 0)
e1de726c 1076 error = -1;
afeecf4f
VM
1077
1078cleanup:
0d0fa7c3 1079 if (error < 0)
97769280 1080 git_filebuf_cleanup(&fbuf);
ac3d33df 1081 git_buf_dispose(&final_path);
afeecf4f
VM
1082 return error;
1083}
1084
8f09a98e
ET
1085static int loose_backend__freshen(
1086 git_odb_backend *_backend,
1087 const git_oid *oid)
1088{
1089 loose_backend *backend = (loose_backend *)_backend;
1090 git_buf path = GIT_BUF_INIT;
1091 int error;
1092
1093 if (object_file_name(&path, backend, oid) < 0)
1094 return -1;
1095
27051d4e 1096 error = git_futils_touch(path.ptr, NULL);
ac3d33df 1097 git_buf_dispose(&path);
8f09a98e
ET
1098
1099 return error;
1100}
1101
d568d585 1102static void loose_backend__free(git_odb_backend *_backend)
7d7cd885
VM
1103{
1104 loose_backend *backend;
1105 assert(_backend);
1106 backend = (loose_backend *)_backend;
1107
3286c408 1108 git__free(backend);
7d7cd885
VM
1109}
1110
8af4d074
VM
1111int git_odb_backend_loose(
1112 git_odb_backend **backend_out,
1113 const char *objects_dir,
1114 int compression_level,
dd64c71c 1115 int do_fsync,
f966acd1
ET
1116 unsigned int dir_mode,
1117 unsigned int file_mode)
7d7cd885
VM
1118{
1119 loose_backend *backend;
f1453c59 1120 size_t objects_dirlen, alloclen;
8294e8cf
RB
1121
1122 assert(backend_out && objects_dir);
1123
1124 objects_dirlen = strlen(objects_dir);
7d7cd885 1125
ac3d33df
JK
1126 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(loose_backend), objects_dirlen);
1127 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 2);
f1453c59 1128 backend = git__calloc(1, alloclen);
ac3d33df 1129 GIT_ERROR_CHECK_ALLOC(backend);
7d7cd885 1130
55f6f21b 1131 backend->parent.version = GIT_ODB_BACKEND_VERSION;
8294e8cf
RB
1132 backend->objects_dirlen = objects_dirlen;
1133 memcpy(backend->objects_dir, objects_dir, objects_dirlen);
1134 if (backend->objects_dir[backend->objects_dirlen - 1] != '/')
1135 backend->objects_dir[backend->objects_dirlen++] = '/';
7d7cd885 1136
8af4d074
VM
1137 if (compression_level < 0)
1138 compression_level = Z_BEST_SPEED;
1139
dd64c71c
ET
1140 if (dir_mode == 0)
1141 dir_mode = GIT_OBJECT_DIR_MODE;
1142
1143 if (file_mode == 0)
1144 file_mode = GIT_OBJECT_FILE_MODE;
1145
8af4d074
VM
1146 backend->object_zlib_level = compression_level;
1147 backend->fsync_object_files = do_fsync;
dd64c71c
ET
1148 backend->object_dir_mode = dir_mode;
1149 backend->object_file_mode = file_mode;
7d7cd885
VM
1150
1151 backend->parent.read = &loose_backend__read;
afeecf4f 1152 backend->parent.write = &loose_backend__write;
d0323a5f 1153 backend->parent.read_prefix = &loose_backend__read_prefix;
7d7cd885 1154 backend->parent.read_header = &loose_backend__read_header;
eae0bfdc
PP
1155 backend->parent.writestream = &loose_backend__writestream;
1156 backend->parent.readstream = &loose_backend__readstream;
7d7cd885 1157 backend->parent.exists = &loose_backend__exists;
f5753999 1158 backend->parent.exists_prefix = &loose_backend__exists_prefix;
521aedad 1159 backend->parent.foreach = &loose_backend__foreach;
8f09a98e 1160 backend->parent.freshen = &loose_backend__freshen;
7d7cd885
VM
1161 backend->parent.free = &loose_backend__free;
1162
7d7cd885 1163 *backend_out = (git_odb_backend *)backend;
e1de726c 1164 return 0;
7d7cd885 1165}