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