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