File Coverage

deps/libgit2/src/libgit2/odb_loose.c
Criterion Covered Total %
statement 345 554 62.2
branch 134 330 40.6
condition n/a
subroutine n/a
pod n/a
total 479 884 54.1


line stmt bran cond sub pod time code
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              
10             #include
11             #include "git2/object.h"
12             #include "git2/sys/odb_backend.h"
13             #include "futils.h"
14             #include "hash.h"
15             #include "odb.h"
16             #include "delta.h"
17             #include "filebuf.h"
18             #include "object.h"
19             #include "zstream.h"
20              
21             #include "git2/odb_backend.h"
22             #include "git2/types.h"
23              
24             /* maximum possible header length */
25             #define MAX_HEADER_LEN 64
26              
27             typedef struct { /* object header data */
28             git_object_t type; /* object type */
29             size_t size; /* object size */
30             } obj_hdr;
31              
32             typedef struct {
33             git_odb_stream stream;
34             git_filebuf fbuf;
35             } loose_writestream;
36              
37             typedef struct {
38             git_odb_stream stream;
39             git_map map;
40             char start[MAX_HEADER_LEN];
41             size_t start_len;
42             size_t start_read;
43             git_zstream zstream;
44             } loose_readstream;
45              
46             typedef struct loose_backend {
47             git_odb_backend parent;
48              
49             int object_zlib_level; /** loose object zlib compression level. */
50             int fsync_object_files; /** loose object file fsync flag. */
51             mode_t object_file_mode;
52             mode_t object_dir_mode;
53              
54             size_t objects_dirlen;
55             char objects_dir[GIT_FLEX_ARRAY];
56             } loose_backend;
57              
58             /* State structure for exploring directories,
59             * in order to locate objects matching a short oid.
60             */
61             typedef struct {
62             size_t dir_len;
63             unsigned char short_oid[GIT_OID_HEXSZ]; /* hex formatted oid to match */
64             size_t short_oid_len;
65             int found; /* number of matching
66             * objects already found */
67             unsigned char res_oid[GIT_OID_HEXSZ]; /* hex formatted oid of
68             * the object found */
69             } loose_locate_object_state;
70              
71              
72             /***********************************************************
73             *
74             * MISCELLANEOUS HELPER FUNCTIONS
75             *
76             ***********************************************************/
77              
78 1573           static int object_file_name(
79             git_str *name, const loose_backend *be, const git_oid *id)
80             {
81             size_t alloclen;
82              
83             /* expand length for object root + 40 hex sha1 chars + 2 * '/' + '\0' */
84 1573 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, be->objects_dirlen, GIT_OID_HEXSZ);
    50          
85 1573 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 3);
    50          
86 1573 50         if (git_str_grow(name, alloclen) < 0)
87 0           return -1;
88              
89 1573           git_str_set(name, be->objects_dir, be->objects_dirlen);
90 1573           git_fs_path_to_dir(name);
91              
92             /* loose object filename: aa/aaa... (41 bytes) */
93 1573           git_oid_pathfmt(name->ptr + name->size, id);
94 1573           name->size += GIT_OID_HEXSZ + 1;
95 1573           name->ptr[name->size] = '\0';
96              
97 1573           return 0;
98             }
99              
100 157           static int object_mkdir(const git_str *name, const loose_backend *be)
101             {
102 157           return git_futils_mkdir_relative(
103 157           name->ptr + be->objects_dirlen, be->objects_dir, be->object_dir_mode,
104             GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR, NULL);
105             }
106              
107 0           static int parse_header_packlike(
108             obj_hdr *out, size_t *out_len, const unsigned char *data, size_t len)
109             {
110             unsigned long c;
111 0           size_t shift, size, used = 0;
112              
113 0 0         if (len == 0)
114 0           goto on_error;
115              
116 0           c = data[used++];
117 0           out->type = (c >> 4) & 7;
118              
119 0           size = c & 15;
120 0           shift = 4;
121 0 0         while (c & 0x80) {
122 0 0         if (len <= used)
123 0           goto on_error;
124              
125 0 0         if (sizeof(size_t) * 8 <= shift)
126 0           goto on_error;
127              
128 0           c = data[used++];
129 0           size += (c & 0x7f) << shift;
130 0           shift += 7;
131             }
132              
133 0           out->size = size;
134              
135 0 0         if (out_len)
136 0           *out_len = used;
137              
138 0           return 0;
139              
140             on_error:
141 0           git_error_set(GIT_ERROR_OBJECT, "failed to parse loose object: invalid header");
142 0           return -1;
143             }
144              
145 1128           static int parse_header(
146             obj_hdr *out,
147             size_t *out_len,
148             const unsigned char *_data,
149             size_t data_len)
150             {
151 1128           const char *data = (char *)_data;
152             size_t i, typename_len, size_idx, size_len;
153             int64_t size;
154              
155 1128           *out_len = 0;
156              
157             /* find the object type name */
158 6417 50         for (i = 0, typename_len = 0; i < data_len; i++, typename_len++) {
159 6417 100         if (data[i] == ' ')
160 1128           break;
161             }
162              
163 1128 50         if (typename_len == data_len)
164 0           goto on_error;
165              
166 1128           out->type = git_object_stringn2type(data, typename_len);
167              
168 1128           size_idx = typename_len + 1;
169 3755 50         for (i = size_idx, size_len = 0; i < data_len; i++, size_len++) {
170 3755 100         if (data[i] == '\0')
171 1128           break;
172             }
173              
174 1128 50         if (i == data_len)
175 0           goto on_error;
176              
177 1128 50         if (git__strntol64(&size, &data[size_idx], size_len, NULL, 10) < 0 ||
    50          
178 1128           size < 0)
179             goto on_error;
180              
181             if ((uint64_t)size > SIZE_MAX) {
182             git_error_set(GIT_ERROR_OBJECT, "object is larger than available memory");
183             return -1;
184             }
185              
186 1128           out->size = (size_t)size;
187              
188 1128 50         if (GIT_ADD_SIZET_OVERFLOW(out_len, i, 1))
    50          
189 0           goto on_error;
190              
191 1128           return 0;
192              
193             on_error:
194 0           git_error_set(GIT_ERROR_OBJECT, "failed to parse loose object: invalid header");
195 1128           return -1;
196             }
197              
198 1128           static int is_zlib_compressed_data(unsigned char *data, size_t data_len)
199             {
200             unsigned int w;
201              
202 1128 50         if (data_len < 2)
203 0           return 0;
204              
205 1128           w = ((unsigned int)(data[0]) << 8) + data[1];
206 1128 50         return (data[0] & 0x8F) == 0x08 && !(w % 31);
    50          
207             }
208              
209             /***********************************************************
210             *
211             * ODB OBJECT READING & WRITING
212             *
213             * Backend for the public API; read headers and full objects
214             * from the ODB. Write raw data to the ODB.
215             *
216             ***********************************************************/
217              
218              
219             /*
220             * At one point, there was a loose object format that was intended to
221             * mimic the format used in pack-files. This was to allow easy copying
222             * of loose object data into packs. This format is no longer used, but
223             * we must still read it.
224             */
225 0           static int read_loose_packlike(git_rawobj *out, git_str *obj)
226             {
227 0           git_str body = GIT_STR_INIT;
228             const unsigned char *obj_data;
229             obj_hdr hdr;
230             size_t obj_len, head_len, alloc_size;
231             int error;
232              
233 0           obj_data = (unsigned char *)obj->ptr;
234 0           obj_len = obj->size;
235              
236             /*
237             * read the object header, which is an (uncompressed)
238             * binary encoding of the object type and size.
239             */
240 0 0         if ((error = parse_header_packlike(&hdr, &head_len, obj_data, obj_len)) < 0)
241 0           goto done;
242              
243 0 0         if (!git_object_typeisloose(hdr.type) || head_len > obj_len) {
    0          
244 0           git_error_set(GIT_ERROR_ODB, "failed to inflate loose object");
245 0           error = -1;
246 0           goto done;
247             }
248              
249 0           obj_data += head_len;
250 0           obj_len -= head_len;
251              
252             /*
253             * allocate a buffer and inflate the data into it
254             */
255 0 0         if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr.size, 1) ||
    0          
256 0           git_str_init(&body, alloc_size) < 0) {
257 0           error = -1;
258 0           goto done;
259             }
260              
261 0 0         if ((error = git_zstream_inflatebuf(&body, obj_data, obj_len)) < 0)
262 0           goto done;
263              
264 0           out->len = hdr.size;
265 0           out->type = hdr.type;
266 0           out->data = git_str_detach(&body);
267              
268             done:
269 0           git_str_dispose(&body);
270 0           return error;
271             }
272              
273 757           static int read_loose_standard(git_rawobj *out, git_str *obj)
274             {
275 757           git_zstream zstream = GIT_ZSTREAM_INIT;
276 757           unsigned char head[MAX_HEADER_LEN], *body = NULL;
277             size_t decompressed, head_len, body_len, alloc_size;
278             obj_hdr hdr;
279             int error;
280              
281 1514 50         if ((error = git_zstream_init(&zstream, GIT_ZSTREAM_INFLATE)) < 0 ||
    50          
282 757           (error = git_zstream_set_input(&zstream, git_str_cstr(obj), git_str_len(obj))) < 0)
283             goto done;
284              
285 757           decompressed = sizeof(head);
286              
287             /*
288             * inflate the initial part of the compressed buffer in order to
289             * parse the header; read the largest header possible, then push the
290             * remainder into the body buffer.
291             */
292 757 50         if ((error = git_zstream_get_output(head, &decompressed, &zstream)) < 0 ||
    50          
293 757           (error = parse_header(&hdr, &head_len, head, decompressed)) < 0)
294             goto done;
295              
296 757 50         if (!git_object_typeisloose(hdr.type)) {
297 0           git_error_set(GIT_ERROR_ODB, "failed to inflate disk object");
298 0           error = -1;
299 0           goto done;
300             }
301              
302             /*
303             * allocate a buffer and inflate the object data into it
304             * (including the initial sequence in the head buffer).
305             */
306 757 50         if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr.size, 1) ||
    50          
307 757           (body = git__calloc(1, alloc_size)) == NULL) {
308 0           error = -1;
309 0           goto done;
310             }
311              
312 757 50         GIT_ASSERT(decompressed >= head_len);
313 757           body_len = decompressed - head_len;
314              
315 757 100         if (body_len)
316 742           memcpy(body, head + head_len, body_len);
317              
318 757           decompressed = hdr.size - body_len;
319 757 50         if ((error = git_zstream_get_output(body + body_len, &decompressed, &zstream)) < 0)
320 0           goto done;
321              
322 757 50         if (!git_zstream_done(&zstream)) {
323 0           git_error_set(GIT_ERROR_ZLIB, "failed to finish zlib inflation: stream aborted prematurely");
324 0           error = -1;
325 0           goto done;
326             }
327              
328 757           body[hdr.size] = '\0';
329              
330 757           out->data = body;
331 757           out->len = hdr.size;
332 757           out->type = hdr.type;
333              
334             done:
335 757 50         if (error < 0)
336 0           git__free(body);
337              
338 757           git_zstream_free(&zstream);
339 757           return error;
340             }
341              
342 757           static int read_loose(git_rawobj *out, git_str *loc)
343             {
344             int error;
345 757           git_str obj = GIT_STR_INIT;
346              
347 757 50         GIT_ASSERT_ARG(out);
348 757 50         GIT_ASSERT_ARG(loc);
349              
350 757 50         if (git_str_oom(loc))
351 0           return -1;
352              
353 757           out->data = NULL;
354 757           out->len = 0;
355 757           out->type = GIT_OBJECT_INVALID;
356              
357 757 50         if ((error = git_futils_readbuffer(&obj, loc->ptr)) < 0)
358 0           goto done;
359              
360 757 50         if (!is_zlib_compressed_data((unsigned char *)obj.ptr, obj.size))
361 0           error = read_loose_packlike(out, &obj);
362             else
363 757           error = read_loose_standard(out, &obj);
364              
365             done:
366 757           git_str_dispose(&obj);
367 757           return error;
368             }
369              
370 0           static int read_header_loose_packlike(
371             git_rawobj *out, const unsigned char *data, size_t len)
372             {
373             obj_hdr hdr;
374             size_t header_len;
375             int error;
376              
377 0 0         if ((error = parse_header_packlike(&hdr, &header_len, data, len)) < 0)
378 0           return error;
379              
380 0           out->len = hdr.size;
381 0           out->type = hdr.type;
382              
383 0           return error;
384             }
385              
386 371           static int read_header_loose_standard(
387             git_rawobj *out, const unsigned char *data, size_t len)
388             {
389 371           git_zstream zs = GIT_ZSTREAM_INIT;
390 371           obj_hdr hdr = {0};
391 371           unsigned char inflated[MAX_HEADER_LEN] = {0};
392 371           size_t header_len, inflated_len = sizeof(inflated);
393             int error;
394              
395 371 50         if ((error = git_zstream_init(&zs, GIT_ZSTREAM_INFLATE)) < 0 ||
    50          
396 371 50         (error = git_zstream_set_input(&zs, data, len)) < 0 ||
397 371 50         (error = git_zstream_get_output_chunk(inflated, &inflated_len, &zs)) < 0 ||
398 371           (error = parse_header(&hdr, &header_len, inflated, inflated_len)) < 0)
399             goto done;
400              
401 371           out->len = hdr.size;
402 371           out->type = hdr.type;
403              
404             done:
405 371           git_zstream_free(&zs);
406 371           return error;
407             }
408              
409 371           static int read_header_loose(git_rawobj *out, git_str *loc)
410             {
411             unsigned char obj[1024];
412             ssize_t obj_len;
413             int fd, error;
414              
415 371 50         GIT_ASSERT_ARG(out);
416 371 50         GIT_ASSERT_ARG(loc);
417              
418 371 50         if (git_str_oom(loc))
419 0           return -1;
420              
421 371           out->data = NULL;
422              
423 371 50         if ((error = fd = git_futils_open_ro(loc->ptr)) < 0)
424 0           goto done;
425              
426 371 50         if ((obj_len = p_read(fd, obj, sizeof(obj))) < 0) {
427 0           error = (int)obj_len;
428 0           goto done;
429             }
430              
431 371 50         if (!is_zlib_compressed_data(obj, (size_t)obj_len))
432 0           error = read_header_loose_packlike(out, obj, (size_t)obj_len);
433             else
434 371           error = read_header_loose_standard(out, obj, (size_t)obj_len);
435              
436 371 50         if (!error && !git_object_typeisloose(out->type)) {
    50          
437 0           git_error_set(GIT_ERROR_ZLIB, "failed to read loose object header");
438 0           error = -1;
439 0           goto done;
440             }
441              
442             done:
443 371 50         if (fd >= 0)
444 371           p_close(fd);
445 371           return error;
446             }
447              
448 1127           static int locate_object(
449             git_str *object_location,
450             loose_backend *backend,
451             const git_oid *oid)
452             {
453 1127           int error = object_file_name(object_location, backend, oid);
454              
455 1127 50         if (!error && !git_fs_path_exists(object_location->ptr))
    100          
456 3           return GIT_ENOTFOUND;
457              
458 1124           return error;
459             }
460              
461             /* Explore an entry of a directory and see if it matches a short oid */
462 10           static int fn_locate_object_short_oid(void *state, git_str *pathbuf) {
463 10           loose_locate_object_state *sstate = (loose_locate_object_state *)state;
464              
465 10 50         if (git_str_len(pathbuf) - sstate->dir_len != GIT_OID_HEXSZ - 2) {
466             /* Entry cannot be an object. Continue to next entry */
467 0           return 0;
468             }
469              
470 10 50         if (git_fs_path_isdir(pathbuf->ptr) == false) {
471             /* We are already in the directory matching the 2 first hex characters,
472             * compare the first ncmp characters of the oids */
473 10 100         if (!memcmp(sstate->short_oid + 2,
474 10           (unsigned char *)pathbuf->ptr + sstate->dir_len,
475 10           sstate->short_oid_len - 2)) {
476              
477 8 50         if (!sstate->found) {
478 8           sstate->res_oid[0] = sstate->short_oid[0];
479 8           sstate->res_oid[1] = sstate->short_oid[1];
480 8           memcpy(sstate->res_oid+2, pathbuf->ptr+sstate->dir_len, GIT_OID_HEXSZ-2);
481             }
482 8           sstate->found++;
483             }
484             }
485              
486 10 50         if (sstate->found > 1)
487 0           return GIT_EAMBIGUOUS;
488              
489 10           return 0;
490             }
491              
492             /* Locate an object matching a given short oid */
493 16           static int locate_object_short_oid(
494             git_str *object_location,
495             git_oid *res_oid,
496             loose_backend *backend,
497             const git_oid *short_oid,
498             size_t len)
499             {
500 16           char *objects_dir = backend->objects_dir;
501 16           size_t dir_len = strlen(objects_dir), alloc_len;
502             loose_locate_object_state state;
503             int error;
504              
505             /* prealloc memory for OBJ_DIR/xx/xx..38x..xx */
506 16 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ);
    50          
507 16 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 3);
    50          
508 16 50         if (git_str_grow(object_location, alloc_len) < 0)
509 0           return -1;
510              
511 16           git_str_set(object_location, objects_dir, dir_len);
512 16           git_fs_path_to_dir(object_location);
513              
514             /* save adjusted position at end of dir so it can be restored later */
515 16           dir_len = git_str_len(object_location);
516              
517             /* Convert raw oid to hex formatted oid */
518 16           git_oid_fmt((char *)state.short_oid, short_oid);
519              
520             /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */
521 16 50         if (git_str_put(object_location, (char *)state.short_oid, 3) < 0)
522 0           return -1;
523 16           object_location->ptr[object_location->size - 1] = '/';
524              
525             /* Check that directory exists */
526 16 100         if (git_fs_path_isdir(object_location->ptr) == false)
527 7           return git_odb__error_notfound("no matching loose object for prefix",
528             short_oid, len);
529              
530 9           state.dir_len = git_str_len(object_location);
531 9           state.short_oid_len = len;
532 9           state.found = 0;
533              
534             /* Explore directory to find a unique object matching short_oid */
535 9           error = git_fs_path_direach(
536             object_location, 0, fn_locate_object_short_oid, &state);
537 9 50         if (error < 0 && error != GIT_EAMBIGUOUS)
    0          
538 0           return error;
539              
540 9 100         if (!state.found)
541 1           return git_odb__error_notfound("no matching loose object for prefix",
542             short_oid, len);
543              
544 8 50         if (state.found > 1)
545 0           return git_odb__error_ambiguous("multiple matches in loose objects");
546              
547             /* Convert obtained hex formatted oid to raw */
548 8           error = git_oid_fromstr(res_oid, (char *)state.res_oid);
549 8 50         if (error)
550 0           return error;
551              
552             /* Update the location according to the oid obtained */
553 8 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ);
    50          
554 8 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
    50          
555              
556 8           git_str_truncate(object_location, dir_len);
557 8 50         if (git_str_grow(object_location, alloc_len) < 0)
558 0           return -1;
559              
560 8           git_oid_pathfmt(object_location->ptr + dir_len, res_oid);
561              
562 8           object_location->size += GIT_OID_HEXSZ + 1;
563 8           object_location->ptr[object_location->size] = '\0';
564              
565 16           return 0;
566             }
567              
568              
569              
570              
571              
572              
573              
574              
575              
576             /***********************************************************
577             *
578             * LOOSE BACKEND PUBLIC API
579             *
580             * Implement the git_odb_backend API calls
581             *
582             ***********************************************************/
583              
584 371           static int loose_backend__read_header(size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *oid)
585             {
586 371           git_str object_path = GIT_STR_INIT;
587             git_rawobj raw;
588             int error;
589              
590 371 50         GIT_ASSERT_ARG(backend);
591 371 50         GIT_ASSERT_ARG(oid);
592              
593 371           raw.len = 0;
594 371           raw.type = GIT_OBJECT_INVALID;
595              
596 371 50         if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
597 0           error = git_odb__error_notfound("no matching loose object",
598             oid, GIT_OID_HEXSZ);
599 371 50         } else if ((error = read_header_loose(&raw, &object_path)) == 0) {
600 371           *len_p = raw.len;
601 371           *type_p = raw.type;
602             }
603              
604 371           git_str_dispose(&object_path);
605              
606 371           return error;
607             }
608              
609 752           static int loose_backend__read(void **buffer_p, size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *oid)
610             {
611 752           git_str object_path = GIT_STR_INIT;
612             git_rawobj raw;
613 752           int error = 0;
614              
615 752 50         GIT_ASSERT_ARG(backend);
616 752 50         GIT_ASSERT_ARG(oid);
617              
618 752 100         if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
619 3           error = git_odb__error_notfound("no matching loose object",
620             oid, GIT_OID_HEXSZ);
621 749 50         } else if ((error = read_loose(&raw, &object_path)) == 0) {
622 749           *buffer_p = raw.data;
623 749           *len_p = raw.len;
624 749           *type_p = raw.type;
625             }
626              
627 752           git_str_dispose(&object_path);
628              
629 752           return error;
630             }
631              
632 138           static int loose_backend__read_prefix(
633             git_oid *out_oid,
634             void **buffer_p,
635             size_t *len_p,
636             git_object_t *type_p,
637             git_odb_backend *backend,
638             const git_oid *short_oid,
639             size_t len)
640             {
641 138           int error = 0;
642              
643 138 50         GIT_ASSERT_ARG(len >= GIT_OID_MINPREFIXLEN && len <= GIT_OID_HEXSZ);
    50          
644              
645 138 100         if (len == GIT_OID_HEXSZ) {
646             /* We can fall back to regular read method */
647 122           error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
648 122 50         if (!error)
649 122           git_oid_cpy(out_oid, short_oid);
650             } else {
651 16           git_str object_path = GIT_STR_INIT;
652             git_rawobj raw;
653              
654 16 50         GIT_ASSERT_ARG(backend && short_oid);
    50          
655              
656 16 100         if ((error = locate_object_short_oid(&object_path, out_oid,
657 8 50         (loose_backend *)backend, short_oid, len)) == 0 &&
658             (error = read_loose(&raw, &object_path)) == 0)
659             {
660 8           *buffer_p = raw.data;
661 8           *len_p = raw.len;
662 8           *type_p = raw.type;
663             }
664              
665 16           git_str_dispose(&object_path);
666             }
667              
668 138           return error;
669             }
670              
671 4           static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
672             {
673 4           git_str object_path = GIT_STR_INIT;
674             int error;
675              
676 4 50         GIT_ASSERT_ARG(backend);
677 4 50         GIT_ASSERT_ARG(oid);
678              
679 4           error = locate_object(&object_path, (loose_backend *)backend, oid);
680              
681 4           git_str_dispose(&object_path);
682              
683 4           return !error;
684             }
685              
686 0           static int loose_backend__exists_prefix(
687             git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len)
688             {
689 0           git_str object_path = GIT_STR_INIT;
690             int error;
691              
692 0 0         GIT_ASSERT_ARG(backend);
693 0 0         GIT_ASSERT_ARG(out);
694 0 0         GIT_ASSERT_ARG(short_id);
695 0 0         GIT_ASSERT_ARG(len >= GIT_OID_MINPREFIXLEN);
696              
697 0           error = locate_object_short_oid(
698             &object_path, out, (loose_backend *)backend, short_id, len);
699              
700 0           git_str_dispose(&object_path);
701              
702 0           return error;
703             }
704              
705             struct foreach_state {
706             size_t dir_len;
707             git_odb_foreach_cb cb;
708             void *data;
709             };
710              
711 121           GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr)
712             {
713 121           int v, i = 0;
714 121 50         if (strlen(ptr) != GIT_OID_HEXSZ+1)
715 0           return -1;
716              
717 121 50         if (ptr[2] != '/') {
718 0           return -1;
719             }
720              
721 121           v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i+1]);
722 121 50         if (v < 0)
723 0           return -1;
724              
725 121           oid->id[0] = (unsigned char) v;
726              
727 121           ptr += 3;
728 2420 100         for (i = 0; i < 38; i += 2) {
729 2299           v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i + 1]);
730 2299 50         if (v < 0)
731 0           return -1;
732              
733 2299           oid->id[1 + i/2] = (unsigned char) v;
734             }
735              
736 121           return 0;
737             }
738              
739 121           static int foreach_object_dir_cb(void *_state, git_str *path)
740             {
741             git_oid oid;
742 121           struct foreach_state *state = (struct foreach_state *) _state;
743              
744 121 50         if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0)
745 0           return 0;
746              
747 121           return git_error_set_after_callback_function(
748 121           state->cb(&oid, state->data), "git_odb_foreach");
749             }
750              
751 99           static int foreach_cb(void *_state, git_str *path)
752             {
753 99           struct foreach_state *state = (struct foreach_state *) _state;
754              
755             /* non-dir is some stray file, ignore it */
756 99 50         if (!git_fs_path_isdir(git_str_cstr(path)))
757 0           return 0;
758              
759 99           return git_fs_path_direach(path, 0, foreach_object_dir_cb, state);
760             }
761              
762 1           static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
763             {
764             char *objects_dir;
765             int error;
766 1           git_str buf = GIT_STR_INIT;
767             struct foreach_state state;
768 1           loose_backend *backend = (loose_backend *) _backend;
769              
770 1 50         GIT_ASSERT_ARG(backend);
771 1 50         GIT_ASSERT_ARG(cb);
772              
773 1           objects_dir = backend->objects_dir;
774              
775 1           git_str_sets(&buf, objects_dir);
776 1           git_fs_path_to_dir(&buf);
777 1 50         if (git_str_oom(&buf))
778 0           return -1;
779              
780 1           memset(&state, 0, sizeof(state));
781 1           state.cb = cb;
782 1           state.data = data;
783 1           state.dir_len = git_str_len(&buf);
784              
785 1           error = git_fs_path_direach(&buf, 0, foreach_cb, &state);
786              
787 1           git_str_dispose(&buf);
788              
789 1           return error;
790             }
791              
792 43           static int loose_backend__writestream_finalize(git_odb_stream *_stream, const git_oid *oid)
793             {
794 43           loose_writestream *stream = (loose_writestream *)_stream;
795 43           loose_backend *backend = (loose_backend *)_stream->backend;
796 43           git_str final_path = GIT_STR_INIT;
797 43           int error = 0;
798              
799 86           if (object_file_name(&final_path, backend, oid) < 0 ||
800 43           object_mkdir(&final_path, backend) < 0)
801 0           error = -1;
802             else
803 43           error = git_filebuf_commit_at(
804 43           &stream->fbuf, final_path.ptr);
805              
806 43           git_str_dispose(&final_path);
807              
808 43           return error;
809             }
810              
811 125           static int loose_backend__writestream_write(git_odb_stream *_stream, const char *data, size_t len)
812             {
813 125           loose_writestream *stream = (loose_writestream *)_stream;
814 125           return git_filebuf_write(&stream->fbuf, data, len);
815             }
816              
817 63           static void loose_backend__writestream_free(git_odb_stream *_stream)
818             {
819 63           loose_writestream *stream = (loose_writestream *)_stream;
820              
821 63           git_filebuf_cleanup(&stream->fbuf);
822 63           git__free(stream);
823 63           }
824              
825 177           static int filebuf_flags(loose_backend *backend)
826             {
827 177           int flags = GIT_FILEBUF_TEMPORARY |
828 177           (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT);
829              
830 177 50         if (backend->fsync_object_files || git_repository__fsync_gitdir)
    50          
831 0           flags |= GIT_FILEBUF_FSYNC;
832              
833 177           return flags;
834             }
835              
836 63           static int loose_backend__writestream(git_odb_stream **stream_out, git_odb_backend *_backend, git_object_size_t length, git_object_t type)
837             {
838             loose_backend *backend;
839 63           loose_writestream *stream = NULL;
840             char hdr[MAX_HEADER_LEN];
841 63           git_str tmp_path = GIT_STR_INIT;
842             size_t hdrlen;
843             int error;
844              
845 63 50         GIT_ASSERT_ARG(_backend);
846              
847 63           backend = (loose_backend *)_backend;
848 63           *stream_out = NULL;
849              
850 63 50         if ((error = git_odb__format_object_header(&hdrlen,
851             hdr, sizeof(hdr), length, type)) < 0)
852 0           return error;
853              
854 63           stream = git__calloc(1, sizeof(loose_writestream));
855 63 50         GIT_ERROR_CHECK_ALLOC(stream);
856              
857 63           stream->stream.backend = _backend;
858 63           stream->stream.read = NULL; /* read only */
859 63           stream->stream.write = &loose_backend__writestream_write;
860 63           stream->stream.finalize_write = &loose_backend__writestream_finalize;
861 63           stream->stream.free = &loose_backend__writestream_free;
862 63           stream->stream.mode = GIT_STREAM_WRONLY;
863              
864 126           if (git_str_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||
865 63           git_filebuf_open(&stream->fbuf, tmp_path.ptr, filebuf_flags(backend),
866 63 50         backend->object_file_mode) < 0 ||
867 63           stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0)
868             {
869 0           git_filebuf_cleanup(&stream->fbuf);
870 0           git__free(stream);
871 0           stream = NULL;
872             }
873 63           git_str_dispose(&tmp_path);
874 63           *stream_out = (git_odb_stream *)stream;
875              
876 63 50         return !stream ? -1 : 0;
877             }
878              
879 0           static int loose_backend__readstream_read(
880             git_odb_stream *_stream,
881             char *buffer,
882             size_t buffer_len)
883             {
884 0           loose_readstream *stream = (loose_readstream *)_stream;
885 0           size_t start_remain = stream->start_len - stream->start_read;
886 0           int total = 0, error;
887              
888 0           buffer_len = min(buffer_len, INT_MAX);
889              
890             /*
891             * if we read more than just the header in the initial read, play
892             * that back for the caller.
893             */
894 0 0         if (start_remain && buffer_len) {
    0          
895 0           size_t chunk = min(start_remain, buffer_len);
896 0           memcpy(buffer, stream->start + stream->start_read, chunk);
897              
898 0           buffer += chunk;
899 0           stream->start_read += chunk;
900              
901 0           total += (int)chunk;
902 0           buffer_len -= chunk;
903             }
904              
905 0 0         if (buffer_len) {
906 0           size_t chunk = buffer_len;
907              
908 0 0         if ((error = git_zstream_get_output(buffer, &chunk, &stream->zstream)) < 0)
909 0           return error;
910              
911 0           total += (int)chunk;
912             }
913              
914 0           return (int)total;
915             }
916              
917 0           static void loose_backend__readstream_free(git_odb_stream *_stream)
918             {
919 0           loose_readstream *stream = (loose_readstream *)_stream;
920              
921 0           git_futils_mmap_free(&stream->map);
922 0           git_zstream_free(&stream->zstream);
923 0           git__free(stream);
924 0           }
925              
926 0           static int loose_backend__readstream_packlike(
927             obj_hdr *hdr,
928             loose_readstream *stream)
929             {
930             const unsigned char *data;
931             size_t data_len, head_len;
932             int error;
933              
934 0           data = stream->map.data;
935 0           data_len = stream->map.len;
936              
937             /*
938             * read the object header, which is an (uncompressed)
939             * binary encoding of the object type and size.
940             */
941 0 0         if ((error = parse_header_packlike(hdr, &head_len, data, data_len)) < 0)
942 0           return error;
943              
944 0 0         if (!git_object_typeisloose(hdr->type)) {
945 0           git_error_set(GIT_ERROR_ODB, "failed to inflate loose object");
946 0           return -1;
947             }
948              
949 0           return git_zstream_set_input(&stream->zstream,
950             data + head_len, data_len - head_len);
951             }
952              
953 0           static int loose_backend__readstream_standard(
954             obj_hdr *hdr,
955             loose_readstream *stream)
956             {
957             unsigned char head[MAX_HEADER_LEN];
958             size_t init, head_len;
959             int error;
960              
961 0 0         if ((error = git_zstream_set_input(&stream->zstream,
962 0           stream->map.data, stream->map.len)) < 0)
963 0           return error;
964              
965 0           init = sizeof(head);
966              
967             /*
968             * inflate the initial part of the compressed buffer in order to
969             * parse the header; read the largest header possible, then store
970             * it in the `start` field of the stream object.
971             */
972 0 0         if ((error = git_zstream_get_output(head, &init, &stream->zstream)) < 0 ||
    0          
973 0           (error = parse_header(hdr, &head_len, head, init)) < 0)
974 0           return error;
975              
976 0 0         if (!git_object_typeisloose(hdr->type)) {
977 0           git_error_set(GIT_ERROR_ODB, "failed to inflate disk object");
978 0           return -1;
979             }
980              
981 0 0         if (init > head_len) {
982 0           stream->start_len = init - head_len;
983 0           memcpy(stream->start, head + head_len, init - head_len);
984             }
985              
986 0           return 0;
987             }
988              
989 0           static int loose_backend__readstream(
990             git_odb_stream **stream_out,
991             size_t *len_out,
992             git_object_t *type_out,
993             git_odb_backend *_backend,
994             const git_oid *oid)
995             {
996             loose_backend *backend;
997 0           loose_readstream *stream = NULL;
998 0           git_hash_ctx *hash_ctx = NULL;
999 0           git_str object_path = GIT_STR_INIT;
1000             obj_hdr hdr;
1001 0           int error = 0;
1002              
1003 0 0         GIT_ASSERT_ARG(stream_out);
1004 0 0         GIT_ASSERT_ARG(len_out);
1005 0 0         GIT_ASSERT_ARG(type_out);
1006 0 0         GIT_ASSERT_ARG(_backend);
1007 0 0         GIT_ASSERT_ARG(oid);
1008              
1009 0           backend = (loose_backend *)_backend;
1010 0           *stream_out = NULL;
1011 0           *len_out = 0;
1012 0           *type_out = GIT_OBJECT_INVALID;
1013              
1014 0 0         if (locate_object(&object_path, backend, oid) < 0) {
1015 0           error = git_odb__error_notfound("no matching loose object",
1016             oid, GIT_OID_HEXSZ);
1017 0           goto done;
1018             }
1019              
1020 0           stream = git__calloc(1, sizeof(loose_readstream));
1021 0 0         GIT_ERROR_CHECK_ALLOC(stream);
1022              
1023 0           hash_ctx = git__malloc(sizeof(git_hash_ctx));
1024 0 0         GIT_ERROR_CHECK_ALLOC(hash_ctx);
1025              
1026 0 0         if ((error = git_hash_ctx_init(hash_ctx, GIT_HASH_ALGORITHM_SHA1)) < 0 ||
    0          
1027 0 0         (error = git_futils_mmap_ro_file(&stream->map, object_path.ptr)) < 0 ||
1028 0           (error = git_zstream_init(&stream->zstream, GIT_ZSTREAM_INFLATE)) < 0)
1029             goto done;
1030              
1031             /* check for a packlike loose object */
1032 0 0         if (!is_zlib_compressed_data(stream->map.data, stream->map.len))
1033 0           error = loose_backend__readstream_packlike(&hdr, stream);
1034             else
1035 0           error = loose_backend__readstream_standard(&hdr, stream);
1036              
1037 0 0         if (error < 0)
1038 0           goto done;
1039              
1040 0           stream->stream.backend = _backend;
1041 0           stream->stream.hash_ctx = hash_ctx;
1042 0           stream->stream.read = &loose_backend__readstream_read;
1043 0           stream->stream.free = &loose_backend__readstream_free;
1044              
1045 0           *stream_out = (git_odb_stream *)stream;
1046 0           *len_out = hdr.size;
1047 0           *type_out = hdr.type;
1048              
1049             done:
1050 0 0         if (error < 0) {
1051 0 0         if (stream) {
1052 0           git_futils_mmap_free(&stream->map);
1053 0           git_zstream_free(&stream->zstream);
1054 0           git__free(stream);
1055             }
1056 0 0         if (hash_ctx) {
1057 0           git_hash_ctx_cleanup(hash_ctx);
1058 0           git__free(hash_ctx);
1059             }
1060             }
1061              
1062 0           git_str_dispose(&object_path);
1063 0           return error;
1064             }
1065              
1066 114           static int loose_backend__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_object_t type)
1067             {
1068 114           int error = 0;
1069 114           git_str final_path = GIT_STR_INIT;
1070             char header[MAX_HEADER_LEN];
1071             size_t header_len;
1072 114           git_filebuf fbuf = GIT_FILEBUF_INIT;
1073             loose_backend *backend;
1074              
1075 114           backend = (loose_backend *)_backend;
1076              
1077             /* prepare the header for the file */
1078 114 50         if ((error = git_odb__format_object_header(&header_len,
1079             header, sizeof(header), len, type)) < 0)
1080 0           goto cleanup;
1081              
1082 228           if (git_str_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||
1083 114           git_filebuf_open(&fbuf, final_path.ptr, filebuf_flags(backend),
1084             backend->object_file_mode) < 0)
1085             {
1086 0           error = -1;
1087 0           goto cleanup;
1088             }
1089              
1090 114           git_filebuf_write(&fbuf, header, header_len);
1091 114           git_filebuf_write(&fbuf, data, len);
1092              
1093 228           if (object_file_name(&final_path, backend, oid) < 0 ||
1094 228 50         object_mkdir(&final_path, backend) < 0 ||
1095 114           git_filebuf_commit_at(&fbuf, final_path.ptr) < 0)
1096 0           error = -1;
1097              
1098             cleanup:
1099 114 50         if (error < 0)
1100 0           git_filebuf_cleanup(&fbuf);
1101 114           git_str_dispose(&final_path);
1102 114           return error;
1103             }
1104              
1105 289           static int loose_backend__freshen(
1106             git_odb_backend *_backend,
1107             const git_oid *oid)
1108             {
1109 289           loose_backend *backend = (loose_backend *)_backend;
1110 289           git_str path = GIT_STR_INIT;
1111             int error;
1112              
1113 289 50         if (object_file_name(&path, backend, oid) < 0)
1114 0           return -1;
1115              
1116 289           error = git_futils_touch(path.ptr, NULL);
1117 289           git_str_dispose(&path);
1118              
1119 289           return error;
1120             }
1121              
1122 36           static void loose_backend__free(git_odb_backend *_backend)
1123             {
1124 36           git__free(_backend);
1125 36           }
1126              
1127 36           int git_odb_backend_loose(
1128             git_odb_backend **backend_out,
1129             const char *objects_dir,
1130             int compression_level,
1131             int do_fsync,
1132             unsigned int dir_mode,
1133             unsigned int file_mode)
1134             {
1135             loose_backend *backend;
1136             size_t objects_dirlen, alloclen;
1137              
1138 36 50         GIT_ASSERT_ARG(backend_out);
1139 36 50         GIT_ASSERT_ARG(objects_dir);
1140              
1141 36           objects_dirlen = strlen(objects_dir);
1142              
1143 36 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(loose_backend), objects_dirlen);
    50          
1144 36 50         GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 2);
    50          
1145 36           backend = git__calloc(1, alloclen);
1146 36 50         GIT_ERROR_CHECK_ALLOC(backend);
1147              
1148 36           backend->parent.version = GIT_ODB_BACKEND_VERSION;
1149 36           backend->objects_dirlen = objects_dirlen;
1150 36           memcpy(backend->objects_dir, objects_dir, objects_dirlen);
1151 36 100         if (backend->objects_dir[backend->objects_dirlen - 1] != '/')
1152 3           backend->objects_dir[backend->objects_dirlen++] = '/';
1153              
1154 36 100         if (compression_level < 0)
1155 35           compression_level = Z_BEST_SPEED;
1156              
1157 36 50         if (dir_mode == 0)
1158 36           dir_mode = GIT_OBJECT_DIR_MODE;
1159              
1160 36 50         if (file_mode == 0)
1161 36           file_mode = GIT_OBJECT_FILE_MODE;
1162              
1163 36           backend->object_zlib_level = compression_level;
1164 36           backend->fsync_object_files = do_fsync;
1165 36           backend->object_dir_mode = dir_mode;
1166 36           backend->object_file_mode = file_mode;
1167              
1168 36           backend->parent.read = &loose_backend__read;
1169 36           backend->parent.write = &loose_backend__write;
1170 36           backend->parent.read_prefix = &loose_backend__read_prefix;
1171 36           backend->parent.read_header = &loose_backend__read_header;
1172 36           backend->parent.writestream = &loose_backend__writestream;
1173 36           backend->parent.readstream = &loose_backend__readstream;
1174 36           backend->parent.exists = &loose_backend__exists;
1175 36           backend->parent.exists_prefix = &loose_backend__exists_prefix;
1176 36           backend->parent.foreach = &loose_backend__foreach;
1177 36           backend->parent.freshen = &loose_backend__freshen;
1178 36           backend->parent.free = &loose_backend__free;
1179              
1180 36           *backend_out = (git_odb_backend *)backend;
1181 36           return 0;
1182             }