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