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