]> git.proxmox.com Git - libgit2.git/blame - src/odb_loose.c
Merge pull request #3653 from libgit2/cmn/treebuilder-submodule
[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"
0c3bae62 9#include <zlib.h>
44908fe7 10#include "git2/object.h"
83cc70d9 11#include "git2/sys/odb_backend.h"
7d7cd885
VM
12#include "fileops.h"
13#include "hash.h"
14#include "odb.h"
15#include "delta-apply.h"
72a3fe42 16#include "filebuf.h"
7d7cd885 17
44908fe7 18#include "git2/odb_backend.h"
72a3fe42 19#include "git2/types.h"
7d7cd885 20
87d9869f
VM
21typedef struct { /* object header data */
22 git_otype type; /* object type */
23 size_t size; /* object size */
7d7cd885
VM
24} obj_hdr;
25
72a3fe42
VM
26typedef struct {
27 git_odb_stream stream;
28 git_filebuf fbuf;
72a3fe42
VM
29} loose_writestream;
30
7d7cd885
VM
31typedef struct loose_backend {
32 git_odb_backend parent;
33
34 int object_zlib_level; /** loose object zlib compression level. */
35 int fsync_object_files; /** loose object file fsync flag. */
dd64c71c
ET
36 mode_t object_file_mode;
37 mode_t object_dir_mode;
8294e8cf
RB
38
39 size_t objects_dirlen;
40 char objects_dir[GIT_FLEX_ARRAY];
7d7cd885
VM
41} loose_backend;
42
aea8a638
MP
43/* State structure for exploring directories,
44 * in order to locate objects matching a short oid.
45 */
46typedef struct {
47 size_t dir_len;
48 unsigned char short_oid[GIT_OID_HEXSZ]; /* hex formatted oid to match */
b8457baa 49 size_t short_oid_len;
aea8a638
MP
50 int found; /* number of matching
51 * objects already found */
52 unsigned char res_oid[GIT_OID_HEXSZ]; /* hex formatted oid of
53 * the object found */
54} loose_locate_object_state;
55
56
7d7cd885
VM
57/***********************************************************
58 *
b874629b 59 * MISCELLANEOUS HELPER FUNCTIONS
7d7cd885
VM
60 *
61 ***********************************************************/
62
8294e8cf
RB
63static int object_file_name(
64 git_buf *name, const loose_backend *be, const git_oid *id)
7d7cd885 65{
f1453c59
ET
66 size_t alloclen;
67
8294e8cf 68 /* expand length for object root + 40 hex sha1 chars + 2 * '/' + '\0' */
f1453c59
ET
69 GITERR_CHECK_ALLOC_ADD(&alloclen, be->objects_dirlen, GIT_OID_HEXSZ);
70 GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 3);
71 if (git_buf_grow(name, alloclen) < 0)
e1de726c 72 return -1;
7d7cd885 73
8294e8cf 74 git_buf_set(name, be->objects_dir, be->objects_dirlen);
97769280 75 git_path_to_dir(name);
7d7cd885
VM
76
77 /* loose object filename: aa/aaa... (41 bytes) */
8294e8cf 78 git_oid_pathfmt(name->ptr + name->size, id);
97769280
RB
79 name->size += GIT_OID_HEXSZ + 1;
80 name->ptr[name->size] = '\0';
7d7cd885 81
e1de726c 82 return 0;
7d7cd885
VM
83}
84
8294e8cf
RB
85static int object_mkdir(const git_buf *name, const loose_backend *be)
86{
ac2fba0e 87 return git_futils_mkdir_relative(
dd64c71c 88 name->ptr + be->objects_dirlen, be->objects_dir, be->object_dir_mode,
ac2fba0e 89 GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR, NULL);
8294e8cf 90}
7d7cd885 91
13224ea4 92static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj)
7d7cd885
VM
93{
94 unsigned char c;
13224ea4 95 unsigned char *data = (unsigned char *)obj->ptr;
7d7cd885
VM
96 size_t shift, size, used = 0;
97
fa6420f7 98 if (git_buf_len(obj) == 0)
7d7cd885
VM
99 return 0;
100
101 c = data[used++];
102 hdr->type = (c >> 4) & 7;
103
104 size = c & 15;
105 shift = 4;
106 while (c & 0x80) {
fa6420f7 107 if (git_buf_len(obj) <= used)
7d7cd885
VM
108 return 0;
109 if (sizeof(size_t) * 8 <= shift)
110 return 0;
111 c = data[used++];
112 size += (c & 0x7f) << shift;
113 shift += 7;
114 }
115 hdr->size = size;
116
117 return used;
118}
119
120static size_t get_object_header(obj_hdr *hdr, unsigned char *data)
121{
122 char c, typename[10];
123 size_t size, used = 0;
124
125 /*
126 * type name string followed by space.
127 */
128 while ((c = data[used]) != ' ') {
129 typename[used++] = c;
130 if (used >= sizeof(typename))
131 return 0;
132 }
133 typename[used] = 0;
134 if (used == 0)
135 return 0;
d12299fe 136 hdr->type = git_object_string2type(typename);
87d9869f 137 used++; /* consume the space */
7d7cd885
VM
138
139 /*
140 * length follows immediately in decimal (without
141 * leading zeros).
142 */
143 size = data[used++] - '0';
144 if (size > 9)
145 return 0;
146 if (size) {
147 while ((c = data[used]) != '\0') {
148 size_t d = c - '0';
149 if (d > 9)
150 break;
151 used++;
152 size = size * 10 + d;
153 }
154 }
155 hdr->size = size;
156
157 /*
158 * the length must be followed by a zero byte
159 */
160 if (data[used++] != '\0')
161 return 0;
162
163 return used;
164}
165
166
167
168/***********************************************************
169 *
170 * ZLIB RELATED FUNCTIONS
171 *
172 ***********************************************************/
173
174static void init_stream(z_stream *s, void *out, size_t len)
175{
176 memset(s, 0, sizeof(*s));
87d9869f 177 s->next_out = out;
1c3fac4d 178 s->avail_out = (uInt)len;
7d7cd885
VM
179}
180
181static void set_stream_input(z_stream *s, void *in, size_t len)
182{
87d9869f 183 s->next_in = in;
1c3fac4d 184 s->avail_in = (uInt)len;
7d7cd885
VM
185}
186
187static void set_stream_output(z_stream *s, void *out, size_t len)
188{
87d9869f 189 s->next_out = out;
1c3fac4d 190 s->avail_out = (uInt)len;
7d7cd885
VM
191}
192
193
13224ea4 194static int start_inflate(z_stream *s, git_buf *obj, void *out, size_t len)
7d7cd885
VM
195{
196 int status;
197
198 init_stream(s, out, len);
fa6420f7 199 set_stream_input(s, obj->ptr, git_buf_len(obj));
7d7cd885
VM
200
201 if ((status = inflateInit(s)) < Z_OK)
202 return status;
203
204 return inflate(s, 0);
205}
206
207static int finish_inflate(z_stream *s)
208{
209 int status = Z_OK;
210
211 while (status == Z_OK)
212 status = inflate(s, Z_FINISH);
213
214 inflateEnd(s);
215
e1de726c
RB
216 if ((status != Z_STREAM_END) || (s->avail_in != 0)) {
217 giterr_set(GITERR_ZLIB, "Failed to finish ZLib inflation. Stream aborted prematurely");
218 return -1;
219 }
7d7cd885 220
e1de726c 221 return 0;
7d7cd885
VM
222}
223
72a3fe42 224static int is_zlib_compressed_data(unsigned char *data)
7d7cd885 225{
72a3fe42 226 unsigned int w;
7d7cd885 227
72a3fe42 228 w = ((unsigned int)(data[0]) << 8) + data[1];
c51065e3 229 return (data[0] & 0x8F) == 0x08 && !(w % 31);
7d7cd885
VM
230}
231
72a3fe42 232static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen)
7d7cd885
VM
233{
234 z_stream zs;
72a3fe42 235 int status = Z_OK;
7d7cd885 236
72a3fe42 237 memset(&zs, 0x0, sizeof(zs));
7d7cd885 238
87d9869f 239 zs.next_out = out;
1c3fac4d 240 zs.avail_out = (uInt)outlen;
7d7cd885 241
87d9869f 242 zs.next_in = in;
1c3fac4d 243 zs.avail_in = (uInt)inlen;
7d7cd885 244
e1de726c
RB
245 if (inflateInit(&zs) < Z_OK) {
246 giterr_set(GITERR_ZLIB, "Failed to inflate buffer");
247 return -1;
248 }
7d7cd885 249
72a3fe42
VM
250 while (status == Z_OK)
251 status = inflate(&zs, Z_FINISH);
7d7cd885 252
72a3fe42 253 inflateEnd(&zs);
7d7cd885 254
e1de726c
RB
255 if (status != Z_STREAM_END /* || zs.avail_in != 0 */ ||
256 zs.total_out != outlen)
257 {
258 giterr_set(GITERR_ZLIB, "Failed to inflate buffer. Stream aborted prematurely");
259 return -1;
260 }
7d7cd885 261
e1de726c 262 return 0;
7d7cd885
VM
263}
264
7d7cd885
VM
265static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
266{
267 unsigned char *buf, *head = hb;
f1453c59 268 size_t tail, alloc_size;
7d7cd885
VM
269
270 /*
271 * allocate a buffer to hold the inflated data and copy the
272 * initial sequence of inflated data from the tail of the
273 * head buffer, if any.
274 */
f1453c59
ET
275 if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr->size, 1) ||
276 (buf = git__malloc(alloc_size)) == NULL) {
7d7cd885
VM
277 inflateEnd(s);
278 return NULL;
279 }
280 tail = s->total_out - used;
281 if (used > 0 && tail > 0) {
282 if (tail > hdr->size)
283 tail = hdr->size;
284 memcpy(buf, head + used, tail);
285 }
286 used = tail;
287
288 /*
289 * inflate the remainder of the object data, if any
290 */
291 if (hdr->size < used)
292 inflateEnd(s);
293 else {
294 set_stream_output(s, buf + used, hdr->size - used);
295 if (finish_inflate(s)) {
3286c408 296 git__free(buf);
7d7cd885
VM
297 return NULL;
298 }
299 }
300
301 return buf;
302}
303
304/*
305 * At one point, there was a loose object format that was intended to
306 * mimic the format used in pack-files. This was to allow easy copying
307 * of loose object data into packs. This format is no longer used, but
308 * we must still read it.
309 */
13224ea4 310static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj)
7d7cd885
VM
311{
312 unsigned char *in, *buf;
313 obj_hdr hdr;
f1453c59 314 size_t len, used, alloclen;
7d7cd885
VM
315
316 /*
317 * read the object header, which is an (uncompressed)
318 * binary encoding of the object type and size.
319 */
e1de726c
RB
320 if ((used = get_binary_object_header(&hdr, obj)) == 0 ||
321 !git_object_typeisloose(hdr.type)) {
322 giterr_set(GITERR_ODB, "Failed to inflate loose object.");
323 return -1;
324 }
7d7cd885
VM
325
326 /*
327 * allocate a buffer and inflate the data into it
328 */
f1453c59
ET
329 GITERR_CHECK_ALLOC_ADD(&alloclen, hdr.size, 1);
330 buf = git__malloc(alloclen);
e1de726c 331 GITERR_CHECK_ALLOC(buf);
7d7cd885 332
13224ea4
VM
333 in = ((unsigned char *)obj->ptr) + used;
334 len = obj->size - used;
e1de726c 335 if (inflate_buffer(in, len, buf, hdr.size) < 0) {
3286c408 336 git__free(buf);
e1de726c 337 return -1;
7d7cd885
VM
338 }
339 buf[hdr.size] = '\0';
340
341 out->data = buf;
87d9869f 342 out->len = hdr.size;
7d7cd885
VM
343 out->type = hdr.type;
344
e1de726c 345 return 0;
7d7cd885
VM
346}
347
13224ea4 348static int inflate_disk_obj(git_rawobj *out, git_buf *obj)
7d7cd885
VM
349{
350 unsigned char head[64], *buf;
351 z_stream zs;
7d7cd885
VM
352 obj_hdr hdr;
353 size_t used;
354
355 /*
356 * check for a pack-like loose object
357 */
13224ea4 358 if (!is_zlib_compressed_data((unsigned char *)obj->ptr))
7d7cd885
VM
359 return inflate_packlike_loose_disk_obj(out, obj);
360
361 /*
362 * inflate the initial part of the io buffer in order
363 * to parse the object header (type and size).
364 */
e1de726c
RB
365 if (start_inflate(&zs, obj, head, sizeof(head)) < Z_OK ||
366 (used = get_object_header(&hdr, head)) == 0 ||
367 !git_object_typeisloose(hdr.type))
368 {
369 giterr_set(GITERR_ODB, "Failed to inflate disk object.");
370 return -1;
371 }
7d7cd885
VM
372
373 /*
374 * allocate a buffer and inflate the object data into it
375 * (including the initial sequence in the head buffer).
376 */
377 if ((buf = inflate_tail(&zs, head, used, &hdr)) == NULL)
e1de726c 378 return -1;
7d7cd885
VM
379 buf[hdr.size] = '\0';
380
381 out->data = buf;
87d9869f 382 out->len = hdr.size;
7d7cd885
VM
383 out->type = hdr.type;
384
e1de726c 385 return 0;
7d7cd885
VM
386}
387
388
389
390
391
392
393/***********************************************************
394 *
395 * ODB OBJECT READING & WRITING
396 *
397 * Backend for the public API; read headers and full objects
398 * from the ODB. Write raw data to the ODB.
399 *
400 ***********************************************************/
401
97769280 402static int read_loose(git_rawobj *out, git_buf *loc)
7d7cd885
VM
403{
404 int error;
13224ea4 405 git_buf obj = GIT_BUF_INIT;
7d7cd885
VM
406
407 assert(out && loc);
408
cb8a7961 409 if (git_buf_oom(loc))
e1de726c 410 return -1;
97769280 411
7d7cd885 412 out->data = NULL;
87d9869f 413 out->len = 0;
7d7cd885
VM
414 out->type = GIT_OBJ_BAD;
415
e1de726c
RB
416 if (!(error = git_futils_readbuffer(&obj, loc->ptr)))
417 error = inflate_disk_obj(out, &obj);
7d7cd885 418
13224ea4 419 git_buf_free(&obj);
7d7cd885 420
e1de726c 421 return error;
7d7cd885
VM
422}
423
97769280 424static int read_header_loose(git_rawobj *out, git_buf *loc)
7d7cd885 425{
e1de726c 426 int error = 0, z_return = Z_ERRNO, read_bytes;
7d7cd885
VM
427 git_file fd;
428 z_stream zs;
429 obj_hdr header_obj;
430 unsigned char raw_buffer[16], inflated_buffer[64];
431
432 assert(out && loc);
433
cb8a7961 434 if (git_buf_oom(loc))
e1de726c 435 return -1;
97769280 436
7d7cd885
VM
437 out->data = NULL;
438
e1de726c
RB
439 if ((fd = git_futils_open_ro(loc->ptr)) < 0)
440 return fd;
7d7cd885
VM
441
442 init_stream(&zs, inflated_buffer, sizeof(inflated_buffer));
443
e1de726c 444 z_return = inflateInit(&zs);
7d7cd885 445
e1de726c
RB
446 while (z_return == Z_OK) {
447 if ((read_bytes = p_read(fd, raw_buffer, sizeof(raw_buffer))) > 0) {
7d7cd885
VM
448 set_stream_input(&zs, raw_buffer, read_bytes);
449 z_return = inflate(&zs, 0);
e1de726c 450 } else
a3ced637 451 z_return = Z_STREAM_END;
e1de726c 452 }
7d7cd885
VM
453
454 if ((z_return != Z_STREAM_END && z_return != Z_BUF_ERROR)
455 || get_object_header(&header_obj, inflated_buffer) == 0
e1de726c
RB
456 || git_object_typeisloose(header_obj.type) == 0)
457 {
458 giterr_set(GITERR_ZLIB, "Failed to read loose object header");
459 error = -1;
460 } else {
461 out->len = header_obj.size;
462 out->type = header_obj.type;
7d7cd885
VM
463 }
464
7d7cd885 465 finish_inflate(&zs);
f79026b4 466 p_close(fd);
60e1b49a 467
e1de726c 468 return error;
7d7cd885
VM
469}
470
97769280
RB
471static int locate_object(
472 git_buf *object_location,
473 loose_backend *backend,
474 const git_oid *oid)
7d7cd885 475{
8294e8cf 476 int error = object_file_name(object_location, backend, oid);
97769280 477
e1de726c
RB
478 if (!error && !git_path_exists(object_location->ptr))
479 return GIT_ENOTFOUND;
97769280
RB
480
481 return error;
7d7cd885
VM
482}
483
aea8a638 484/* Explore an entry of a directory and see if it matches a short oid */
97769280 485static int fn_locate_object_short_oid(void *state, git_buf *pathbuf) {
aea8a638
MP
486 loose_locate_object_state *sstate = (loose_locate_object_state *)state;
487
fa6420f7 488 if (git_buf_len(pathbuf) - sstate->dir_len != GIT_OID_HEXSZ - 2) {
aea8a638 489 /* Entry cannot be an object. Continue to next entry */
e1de726c 490 return 0;
aea8a638
MP
491 }
492
9d160ba8 493 if (git_path_isdir(pathbuf->ptr) == false) {
f1d01851
VM
494 /* We are already in the directory matching the 2 first hex characters,
495 * compare the first ncmp characters of the oids */
496 if (!memcmp(sstate->short_oid + 2,
97769280 497 (unsigned char *)pathbuf->ptr + sstate->dir_len,
f1d01851
VM
498 sstate->short_oid_len - 2)) {
499
aea8a638
MP
500 if (!sstate->found) {
501 sstate->res_oid[0] = sstate->short_oid[0];
502 sstate->res_oid[1] = sstate->short_oid[1];
97769280 503 memcpy(sstate->res_oid+2, pathbuf->ptr+sstate->dir_len, GIT_OID_HEXSZ-2);
aea8a638
MP
504 }
505 sstate->found++;
506 }
507 }
e1de726c 508
d0323a5f 509 if (sstate->found > 1)
209f9b67 510 return GIT_EAMBIGUOUS;
d0323a5f 511
e1de726c 512 return 0;
aea8a638
MP
513}
514
515/* Locate an object matching a given short oid */
97769280
RB
516static int locate_object_short_oid(
517 git_buf *object_location,
518 git_oid *res_oid,
519 loose_backend *backend,
520 const git_oid *short_oid,
b8457baa 521 size_t len)
aea8a638
MP
522{
523 char *objects_dir = backend->objects_dir;
f1453c59 524 size_t dir_len = strlen(objects_dir), alloc_len;
aea8a638
MP
525 loose_locate_object_state state;
526 int error;
527
f5753999 528 /* prealloc memory for OBJ_DIR/xx/xx..38x..xx */
f1453c59
ET
529 GITERR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ);
530 GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 3);
531 if (git_buf_grow(object_location, alloc_len) < 0)
e1de726c 532 return -1;
aea8a638 533
f5753999 534 git_buf_set(object_location, objects_dir, dir_len);
97769280 535 git_path_to_dir(object_location);
aea8a638 536
97769280 537 /* save adjusted position at end of dir so it can be restored later */
fa6420f7 538 dir_len = git_buf_len(object_location);
aea8a638
MP
539
540 /* Convert raw oid to hex formatted oid */
d0323a5f 541 git_oid_fmt((char *)state.short_oid, short_oid);
97769280 542
aea8a638 543 /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */
f5753999 544 if (git_buf_put(object_location, (char *)state.short_oid, 3) < 0)
e1de726c 545 return -1;
f5753999 546 object_location->ptr[object_location->size - 1] = '/';
aea8a638
MP
547
548 /* Check that directory exists */
1a481123 549 if (git_path_isdir(object_location->ptr) == false)
282283ac 550 return git_odb__error_notfound("no matching loose object for prefix", short_oid);
aea8a638 551
fa6420f7 552 state.dir_len = git_buf_len(object_location);
aea8a638
MP
553 state.short_oid_len = len;
554 state.found = 0;
97769280 555
aea8a638 556 /* Explore directory to find a unique object matching short_oid */
e1de726c 557 error = git_path_direach(
219d3457 558 object_location, 0, fn_locate_object_short_oid, &state);
25e0b157 559 if (error < 0 && error != GIT_EAMBIGUOUS)
e1de726c 560 return error;
97769280 561
e1de726c 562 if (!state.found)
282283ac 563 return git_odb__error_notfound("no matching loose object for prefix", short_oid);
aea8a638 564
209f9b67 565 if (state.found > 1)
566 return git_odb__error_ambiguous("multiple matches in loose objects");
567
aea8a638 568 /* Convert obtained hex formatted oid to raw */
fa48608e 569 error = git_oid_fromstr(res_oid, (char *)state.res_oid);
e1de726c
RB
570 if (error)
571 return error;
aea8a638
MP
572
573 /* Update the location according to the oid obtained */
f1453c59
ET
574 GITERR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ);
575 GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
97769280
RB
576
577 git_buf_truncate(object_location, dir_len);
f1453c59 578 if (git_buf_grow(object_location, alloc_len) < 0)
e1de726c 579 return -1;
97769280
RB
580
581 git_oid_pathfmt(object_location->ptr + dir_len, res_oid);
582
583 object_location->size += GIT_OID_HEXSZ + 1;
584 object_location->ptr[object_location->size] = '\0';
aea8a638 585
e1de726c 586 return 0;
aea8a638
MP
587}
588
7d7cd885
VM
589
590
591
592
593
594
595
596
597/***********************************************************
598 *
599 * LOOSE BACKEND PUBLIC API
600 *
601 * Implement the git_odb_backend API calls
602 *
603 ***********************************************************/
604
d568d585 605static int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
7d7cd885 606{
97769280 607 git_buf object_path = GIT_BUF_INIT;
72a3fe42 608 git_rawobj raw;
0d0fa7c3 609 int error;
7d7cd885 610
72a3fe42 611 assert(backend && oid);
7d7cd885 612
60e1b49a
VM
613 raw.len = 0;
614 raw.type = GIT_OBJ_BAD;
615
97769280 616 if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
282283ac 617 error = git_odb__error_notfound("no matching loose object", oid);
e1de726c 618 else if ((error = read_header_loose(&raw, &object_path)) == 0) {
97769280
RB
619 *len_p = raw.len;
620 *type_p = raw.type;
621 }
7d7cd885 622
97769280 623 git_buf_free(&object_path);
7d7cd885 624
97769280 625 return error;
72a3fe42 626}
7d7cd885 627
d568d585 628static int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
7d7cd885 629{
97769280 630 git_buf object_path = GIT_BUF_INIT;
72a3fe42 631 git_rawobj raw;
e1de726c 632 int error = 0;
7d7cd885 633
72a3fe42 634 assert(backend && oid);
7d7cd885 635
97769280 636 if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
282283ac 637 error = git_odb__error_notfound("no matching loose object", oid);
e1de726c 638 else if ((error = read_loose(&raw, &object_path)) == 0) {
97769280
RB
639 *buffer_p = raw.data;
640 *len_p = raw.len;
641 *type_p = raw.type;
642 }
72a3fe42 643
97769280 644 git_buf_free(&object_path);
72a3fe42 645
97769280 646 return error;
7d7cd885
VM
647}
648
d568d585 649static int loose_backend__read_prefix(
d0323a5f
VM
650 git_oid *out_oid,
651 void **buffer_p,
652 size_t *len_p,
653 git_otype *type_p,
654 git_odb_backend *backend,
655 const git_oid *short_oid,
b8457baa 656 size_t len)
ecd6fdf1 657{
e1de726c 658 int error = 0;
97769280 659
26875825 660 assert(len >= GIT_OID_MINPREFIXLEN && len <= GIT_OID_HEXSZ);
d19dd9cf 661
26875825 662 if (len == GIT_OID_HEXSZ) {
aea8a638 663 /* We can fall back to regular read method */
97769280 664 error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
e1de726c 665 if (!error)
ecd6fdf1 666 git_oid_cpy(out_oid, short_oid);
aea8a638 667 } else {
97769280 668 git_buf object_path = GIT_BUF_INIT;
aea8a638 669 git_rawobj raw;
aea8a638
MP
670
671 assert(backend && short_oid);
672
97769280 673 if ((error = locate_object_short_oid(&object_path, out_oid,
e1de726c
RB
674 (loose_backend *)backend, short_oid, len)) == 0 &&
675 (error = read_loose(&raw, &object_path)) == 0)
676 {
97769280
RB
677 *buffer_p = raw.data;
678 *len_p = raw.len;
679 *type_p = raw.type;
aea8a638
MP
680 }
681
97769280 682 git_buf_free(&object_path);
ecd6fdf1 683 }
aea8a638 684
97769280 685 return error;
ecd6fdf1
MP
686}
687
d568d585 688static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
7d7cd885 689{
97769280
RB
690 git_buf object_path = GIT_BUF_INIT;
691 int error;
7d7cd885
VM
692
693 assert(backend && oid);
694
97769280
RB
695 error = locate_object(&object_path, (loose_backend *)backend, oid);
696
697 git_buf_free(&object_path);
698
e1de726c 699 return !error;
7d7cd885
VM
700}
701
f5753999
RB
702static int loose_backend__exists_prefix(
703 git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len)
704{
705 git_buf object_path = GIT_BUF_INIT;
706 int error;
707
26875825 708 assert(backend && out && short_id && len >= GIT_OID_MINPREFIXLEN);
f5753999
RB
709
710 error = locate_object_short_oid(
711 &object_path, out, (loose_backend *)backend, short_id, len);
712
713 git_buf_free(&object_path);
714
715 return error;
716}
717
521aedad
CMN
718struct foreach_state {
719 size_t dir_len;
c3fb7d04 720 git_odb_foreach_cb cb;
521aedad
CMN
721 void *data;
722};
723
b7158c53 724GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr)
521aedad
CMN
725{
726 int v, i = 0;
3b2cb2c9 727 if (strlen(ptr) != GIT_OID_HEXSZ+1)
521aedad
CMN
728 return -1;
729
730 if (ptr[2] != '/') {
731 return -1;
732 }
733
734 v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i+1]);
735 if (v < 0)
736 return -1;
737
738 oid->id[0] = (unsigned char) v;
739
740 ptr += 3;
741 for (i = 0; i < 38; i += 2) {
742 v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i + 1]);
743 if (v < 0)
744 return -1;
745
746 oid->id[1 + i/2] = (unsigned char) v;
747 }
748
749 return 0;
750}
751
752static int foreach_object_dir_cb(void *_state, git_buf *path)
753{
754 git_oid oid;
755 struct foreach_state *state = (struct foreach_state *) _state;
756
757 if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0)
758 return 0;
759
26c1cb91 760 return giterr_set_after_callback_function(
25e0b157 761 state->cb(&oid, state->data), "git_odb_foreach");
521aedad
CMN
762}
763
764static int foreach_cb(void *_state, git_buf *path)
765{
766 struct foreach_state *state = (struct foreach_state *) _state;
767
ee311907
CMN
768 /* non-dir is some stray file, ignore it */
769 if (!git_path_isdir(git_buf_cstr(path)))
770 return 0;
771
25e0b157 772 return git_path_direach(path, 0, foreach_object_dir_cb, state);
521aedad
CMN
773}
774
c3fb7d04 775static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
521aedad
CMN
776{
777 char *objects_dir;
778 int error;
779 git_buf buf = GIT_BUF_INIT;
780 struct foreach_state state;
781 loose_backend *backend = (loose_backend *) _backend;
782
783 assert(backend && cb);
784
785 objects_dir = backend->objects_dir;
786
25e0b157 787 git_buf_sets(&buf, objects_dir);
521aedad 788 git_path_to_dir(&buf);
25e0b157
RB
789 if (git_buf_oom(&buf))
790 return -1;
521aedad 791
5dca2010 792 memset(&state, 0, sizeof(state));
521aedad
CMN
793 state.cb = cb;
794 state.data = data;
795 state.dir_len = git_buf_len(&buf);
796
219d3457 797 error = git_path_direach(&buf, 0, foreach_cb, &state);
5dca2010 798
521aedad
CMN
799 git_buf_free(&buf);
800
96869a4e 801 return error;
521aedad
CMN
802}
803
fe0c6d4e 804static int loose_backend__stream_fwrite(git_odb_stream *_stream, const git_oid *oid)
72a3fe42
VM
805{
806 loose_writestream *stream = (loose_writestream *)_stream;
807 loose_backend *backend = (loose_backend *)_stream->backend;
97769280 808 git_buf final_path = GIT_BUF_INIT;
e1de726c 809 int error = 0;
72a3fe42 810
8380b39a 811 if (object_file_name(&final_path, backend, oid) < 0 ||
8294e8cf 812 object_mkdir(&final_path, backend) < 0)
e1de726c 813 error = -1;
e1de726c
RB
814 else
815 error = git_filebuf_commit_at(
1d3a8aeb 816 &stream->fbuf, final_path.ptr);
97769280 817
97769280
RB
818 git_buf_free(&final_path);
819
97769280 820 return error;
72a3fe42
VM
821}
822
d568d585 823static int loose_backend__stream_write(git_odb_stream *_stream, const char *data, size_t len)
7d7cd885 824{
72a3fe42
VM
825 loose_writestream *stream = (loose_writestream *)_stream;
826 return git_filebuf_write(&stream->fbuf, data, len);
827}
828
d568d585 829static void loose_backend__stream_free(git_odb_stream *_stream)
72a3fe42
VM
830{
831 loose_writestream *stream = (loose_writestream *)_stream;
832
97769280 833 git_filebuf_cleanup(&stream->fbuf);
3286c408 834 git__free(stream);
72a3fe42
VM
835}
836
77b339f7 837static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, git_off_t length, git_otype type)
72a3fe42
VM
838{
839 loose_backend *backend;
e1de726c 840 loose_writestream *stream = NULL;
97769280
RB
841 char hdr[64];
842 git_buf tmp_path = GIT_BUF_INIT;
87d9869f 843 int hdrlen;
7d7cd885 844
77b339f7 845 assert(_backend && length >= 0);
7d7cd885
VM
846
847 backend = (loose_backend *)_backend;
72a3fe42 848 *stream_out = NULL;
7d7cd885 849
d4e6cf0c 850 hdrlen = git_odb__format_object_header(hdr, sizeof(hdr), length, type);
72a3fe42
VM
851
852 stream = git__calloc(1, sizeof(loose_writestream));
e1de726c 853 GITERR_CHECK_ALLOC(stream);
7d7cd885 854
72a3fe42
VM
855 stream->stream.backend = _backend;
856 stream->stream.read = NULL; /* read only */
857 stream->stream.write = &loose_backend__stream_write;
858 stream->stream.finalize_write = &loose_backend__stream_fwrite;
859 stream->stream.free = &loose_backend__stream_free;
860 stream->stream.mode = GIT_STREAM_WRONLY;
7d7cd885 861
e1de726c
RB
862 if (git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||
863 git_filebuf_open(&stream->fbuf, tmp_path.ptr,
e1de726c 864 GIT_FILEBUF_TEMPORARY |
1d3a8aeb
ET
865 (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT),
866 backend->object_file_mode) < 0 ||
e1de726c
RB
867 stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0)
868 {
869 git_filebuf_cleanup(&stream->fbuf);
870 git__free(stream);
871 stream = NULL;
872 }
97769280 873 git_buf_free(&tmp_path);
72a3fe42 874 *stream_out = (git_odb_stream *)stream;
97769280 875
e1de726c 876 return !stream ? -1 : 0;
7d7cd885
VM
877}
878
fe0c6d4e 879static int loose_backend__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_otype type)
afeecf4f 880{
8e8b6b01 881 int error = 0, header_len;
97769280
RB
882 git_buf final_path = GIT_BUF_INIT;
883 char header[64];
b762e576 884 git_filebuf fbuf = GIT_FILEBUF_INIT;
afeecf4f
VM
885 loose_backend *backend;
886
887 backend = (loose_backend *)_backend;
888
889 /* prepare the header for the file */
d4e6cf0c 890 header_len = git_odb__format_object_header(header, sizeof(header), len, type);
afeecf4f 891
e1de726c
RB
892 if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||
893 git_filebuf_open(&fbuf, final_path.ptr,
e1de726c 894 GIT_FILEBUF_TEMPORARY |
1d3a8aeb
ET
895 (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT),
896 backend->object_file_mode) < 0)
e1de726c
RB
897 {
898 error = -1;
97769280 899 goto cleanup;
e1de726c 900 }
afeecf4f
VM
901
902 git_filebuf_write(&fbuf, header, header_len);
903 git_filebuf_write(&fbuf, data, len);
afeecf4f 904
8294e8cf
RB
905 if (object_file_name(&final_path, backend, oid) < 0 ||
906 object_mkdir(&final_path, backend) < 0 ||
1d3a8aeb 907 git_filebuf_commit_at(&fbuf, final_path.ptr) < 0)
e1de726c 908 error = -1;
afeecf4f
VM
909
910cleanup:
0d0fa7c3 911 if (error < 0)
97769280
RB
912 git_filebuf_cleanup(&fbuf);
913 git_buf_free(&final_path);
afeecf4f
VM
914 return error;
915}
916
d568d585 917static void loose_backend__free(git_odb_backend *_backend)
7d7cd885
VM
918{
919 loose_backend *backend;
920 assert(_backend);
921 backend = (loose_backend *)_backend;
922
3286c408 923 git__free(backend);
7d7cd885
VM
924}
925
8af4d074
VM
926int git_odb_backend_loose(
927 git_odb_backend **backend_out,
928 const char *objects_dir,
929 int compression_level,
dd64c71c 930 int do_fsync,
f966acd1
ET
931 unsigned int dir_mode,
932 unsigned int file_mode)
7d7cd885
VM
933{
934 loose_backend *backend;
f1453c59 935 size_t objects_dirlen, alloclen;
8294e8cf
RB
936
937 assert(backend_out && objects_dir);
938
939 objects_dirlen = strlen(objects_dir);
7d7cd885 940
f1453c59
ET
941 GITERR_CHECK_ALLOC_ADD(&alloclen, sizeof(loose_backend), objects_dirlen);
942 GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 2);
943 backend = git__calloc(1, alloclen);
e1de726c 944 GITERR_CHECK_ALLOC(backend);
7d7cd885 945
55f6f21b 946 backend->parent.version = GIT_ODB_BACKEND_VERSION;
8294e8cf
RB
947 backend->objects_dirlen = objects_dirlen;
948 memcpy(backend->objects_dir, objects_dir, objects_dirlen);
949 if (backend->objects_dir[backend->objects_dirlen - 1] != '/')
950 backend->objects_dir[backend->objects_dirlen++] = '/';
7d7cd885 951
8af4d074
VM
952 if (compression_level < 0)
953 compression_level = Z_BEST_SPEED;
954
dd64c71c
ET
955 if (dir_mode == 0)
956 dir_mode = GIT_OBJECT_DIR_MODE;
957
958 if (file_mode == 0)
959 file_mode = GIT_OBJECT_FILE_MODE;
960
8af4d074
VM
961 backend->object_zlib_level = compression_level;
962 backend->fsync_object_files = do_fsync;
dd64c71c
ET
963 backend->object_dir_mode = dir_mode;
964 backend->object_file_mode = file_mode;
7d7cd885
VM
965
966 backend->parent.read = &loose_backend__read;
afeecf4f 967 backend->parent.write = &loose_backend__write;
d0323a5f 968 backend->parent.read_prefix = &loose_backend__read_prefix;
7d7cd885 969 backend->parent.read_header = &loose_backend__read_header;
72a3fe42 970 backend->parent.writestream = &loose_backend__stream;
7d7cd885 971 backend->parent.exists = &loose_backend__exists;
f5753999 972 backend->parent.exists_prefix = &loose_backend__exists_prefix;
521aedad 973 backend->parent.foreach = &loose_backend__foreach;
7d7cd885
VM
974 backend->parent.free = &loose_backend__free;
975
7d7cd885 976 *backend_out = (git_odb_backend *)backend;
e1de726c 977 return 0;
7d7cd885 978}