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