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