]> git.proxmox.com Git - libgit2.git/blob - src/odb_loose.c
New upstream version 1.1.0+dfsg.1
[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 "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 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__calloc(1, 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 = {0};
390 unsigned char inflated[MAX_HEADER_LEN] = {0};
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 ssize_t obj_len;
412 int fd, error;
413
414 assert(out && loc);
415
416 if (git_buf_oom(loc))
417 return -1;
418
419 out->data = NULL;
420
421 if ((error = fd = git_futils_open_ro(loc->ptr)) < 0)
422 goto done;
423
424 if ((obj_len = p_read(fd, obj, sizeof(obj))) < 0) {
425 error = (int)obj_len;
426 goto done;
427 }
428
429 if (!is_zlib_compressed_data(obj, (size_t)obj_len))
430 error = read_header_loose_packlike(out, obj, (size_t)obj_len);
431 else
432 error = read_header_loose_standard(out, obj, (size_t)obj_len);
433
434 if (!error && !git_object_typeisloose(out->type)) {
435 git_error_set(GIT_ERROR_ZLIB, "failed to read loose object header");
436 error = -1;
437 goto done;
438 }
439
440 done:
441 if (fd >= 0)
442 p_close(fd);
443 return error;
444 }
445
446 static int locate_object(
447 git_buf *object_location,
448 loose_backend *backend,
449 const git_oid *oid)
450 {
451 int error = object_file_name(object_location, backend, oid);
452
453 if (!error && !git_path_exists(object_location->ptr))
454 return GIT_ENOTFOUND;
455
456 return error;
457 }
458
459 /* Explore an entry of a directory and see if it matches a short oid */
460 static int fn_locate_object_short_oid(void *state, git_buf *pathbuf) {
461 loose_locate_object_state *sstate = (loose_locate_object_state *)state;
462
463 if (git_buf_len(pathbuf) - sstate->dir_len != GIT_OID_HEXSZ - 2) {
464 /* Entry cannot be an object. Continue to next entry */
465 return 0;
466 }
467
468 if (git_path_isdir(pathbuf->ptr) == false) {
469 /* We are already in the directory matching the 2 first hex characters,
470 * compare the first ncmp characters of the oids */
471 if (!memcmp(sstate->short_oid + 2,
472 (unsigned char *)pathbuf->ptr + sstate->dir_len,
473 sstate->short_oid_len - 2)) {
474
475 if (!sstate->found) {
476 sstate->res_oid[0] = sstate->short_oid[0];
477 sstate->res_oid[1] = sstate->short_oid[1];
478 memcpy(sstate->res_oid+2, pathbuf->ptr+sstate->dir_len, GIT_OID_HEXSZ-2);
479 }
480 sstate->found++;
481 }
482 }
483
484 if (sstate->found > 1)
485 return GIT_EAMBIGUOUS;
486
487 return 0;
488 }
489
490 /* Locate an object matching a given short oid */
491 static int locate_object_short_oid(
492 git_buf *object_location,
493 git_oid *res_oid,
494 loose_backend *backend,
495 const git_oid *short_oid,
496 size_t len)
497 {
498 char *objects_dir = backend->objects_dir;
499 size_t dir_len = strlen(objects_dir), alloc_len;
500 loose_locate_object_state state;
501 int error;
502
503 /* prealloc memory for OBJ_DIR/xx/xx..38x..xx */
504 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ);
505 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 3);
506 if (git_buf_grow(object_location, alloc_len) < 0)
507 return -1;
508
509 git_buf_set(object_location, objects_dir, dir_len);
510 git_path_to_dir(object_location);
511
512 /* save adjusted position at end of dir so it can be restored later */
513 dir_len = git_buf_len(object_location);
514
515 /* Convert raw oid to hex formatted oid */
516 git_oid_fmt((char *)state.short_oid, short_oid);
517
518 /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */
519 if (git_buf_put(object_location, (char *)state.short_oid, 3) < 0)
520 return -1;
521 object_location->ptr[object_location->size - 1] = '/';
522
523 /* Check that directory exists */
524 if (git_path_isdir(object_location->ptr) == false)
525 return git_odb__error_notfound("no matching loose object for prefix",
526 short_oid, len);
527
528 state.dir_len = git_buf_len(object_location);
529 state.short_oid_len = len;
530 state.found = 0;
531
532 /* Explore directory to find a unique object matching short_oid */
533 error = git_path_direach(
534 object_location, 0, fn_locate_object_short_oid, &state);
535 if (error < 0 && error != GIT_EAMBIGUOUS)
536 return error;
537
538 if (!state.found)
539 return git_odb__error_notfound("no matching loose object for prefix",
540 short_oid, len);
541
542 if (state.found > 1)
543 return git_odb__error_ambiguous("multiple matches in loose objects");
544
545 /* Convert obtained hex formatted oid to raw */
546 error = git_oid_fromstr(res_oid, (char *)state.res_oid);
547 if (error)
548 return error;
549
550 /* Update the location according to the oid obtained */
551 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ);
552 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
553
554 git_buf_truncate(object_location, dir_len);
555 if (git_buf_grow(object_location, alloc_len) < 0)
556 return -1;
557
558 git_oid_pathfmt(object_location->ptr + dir_len, res_oid);
559
560 object_location->size += GIT_OID_HEXSZ + 1;
561 object_location->ptr[object_location->size] = '\0';
562
563 return 0;
564 }
565
566
567
568
569
570
571
572
573
574 /***********************************************************
575 *
576 * LOOSE BACKEND PUBLIC API
577 *
578 * Implement the git_odb_backend API calls
579 *
580 ***********************************************************/
581
582 static int loose_backend__read_header(size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *oid)
583 {
584 git_buf object_path = GIT_BUF_INIT;
585 git_rawobj raw;
586 int error;
587
588 assert(backend && oid);
589
590 raw.len = 0;
591 raw.type = GIT_OBJECT_INVALID;
592
593 if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
594 error = git_odb__error_notfound("no matching loose object",
595 oid, GIT_OID_HEXSZ);
596 } else if ((error = read_header_loose(&raw, &object_path)) == 0) {
597 *len_p = raw.len;
598 *type_p = raw.type;
599 }
600
601 git_buf_dispose(&object_path);
602
603 return error;
604 }
605
606 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)
607 {
608 git_buf object_path = GIT_BUF_INIT;
609 git_rawobj raw;
610 int error = 0;
611
612 assert(backend && oid);
613
614 if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
615 error = git_odb__error_notfound("no matching loose object",
616 oid, GIT_OID_HEXSZ);
617 } else if ((error = read_loose(&raw, &object_path)) == 0) {
618 *buffer_p = raw.data;
619 *len_p = raw.len;
620 *type_p = raw.type;
621 }
622
623 git_buf_dispose(&object_path);
624
625 return error;
626 }
627
628 static int loose_backend__read_prefix(
629 git_oid *out_oid,
630 void **buffer_p,
631 size_t *len_p,
632 git_object_t *type_p,
633 git_odb_backend *backend,
634 const git_oid *short_oid,
635 size_t len)
636 {
637 int error = 0;
638
639 assert(len >= GIT_OID_MINPREFIXLEN && len <= GIT_OID_HEXSZ);
640
641 if (len == GIT_OID_HEXSZ) {
642 /* We can fall back to regular read method */
643 error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
644 if (!error)
645 git_oid_cpy(out_oid, short_oid);
646 } else {
647 git_buf object_path = GIT_BUF_INIT;
648 git_rawobj raw;
649
650 assert(backend && short_oid);
651
652 if ((error = locate_object_short_oid(&object_path, out_oid,
653 (loose_backend *)backend, short_oid, len)) == 0 &&
654 (error = read_loose(&raw, &object_path)) == 0)
655 {
656 *buffer_p = raw.data;
657 *len_p = raw.len;
658 *type_p = raw.type;
659 }
660
661 git_buf_dispose(&object_path);
662 }
663
664 return error;
665 }
666
667 static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
668 {
669 git_buf object_path = GIT_BUF_INIT;
670 int error;
671
672 assert(backend && oid);
673
674 error = locate_object(&object_path, (loose_backend *)backend, oid);
675
676 git_buf_dispose(&object_path);
677
678 return !error;
679 }
680
681 static int loose_backend__exists_prefix(
682 git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len)
683 {
684 git_buf object_path = GIT_BUF_INIT;
685 int error;
686
687 assert(backend && out && short_id && len >= GIT_OID_MINPREFIXLEN);
688
689 error = locate_object_short_oid(
690 &object_path, out, (loose_backend *)backend, short_id, len);
691
692 git_buf_dispose(&object_path);
693
694 return error;
695 }
696
697 struct foreach_state {
698 size_t dir_len;
699 git_odb_foreach_cb cb;
700 void *data;
701 };
702
703 GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr)
704 {
705 int v, i = 0;
706 if (strlen(ptr) != GIT_OID_HEXSZ+1)
707 return -1;
708
709 if (ptr[2] != '/') {
710 return -1;
711 }
712
713 v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i+1]);
714 if (v < 0)
715 return -1;
716
717 oid->id[0] = (unsigned char) v;
718
719 ptr += 3;
720 for (i = 0; i < 38; i += 2) {
721 v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i + 1]);
722 if (v < 0)
723 return -1;
724
725 oid->id[1 + i/2] = (unsigned char) v;
726 }
727
728 return 0;
729 }
730
731 static int foreach_object_dir_cb(void *_state, git_buf *path)
732 {
733 git_oid oid;
734 struct foreach_state *state = (struct foreach_state *) _state;
735
736 if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0)
737 return 0;
738
739 return git_error_set_after_callback_function(
740 state->cb(&oid, state->data), "git_odb_foreach");
741 }
742
743 static int foreach_cb(void *_state, git_buf *path)
744 {
745 struct foreach_state *state = (struct foreach_state *) _state;
746
747 /* non-dir is some stray file, ignore it */
748 if (!git_path_isdir(git_buf_cstr(path)))
749 return 0;
750
751 return git_path_direach(path, 0, foreach_object_dir_cb, state);
752 }
753
754 static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
755 {
756 char *objects_dir;
757 int error;
758 git_buf buf = GIT_BUF_INIT;
759 struct foreach_state state;
760 loose_backend *backend = (loose_backend *) _backend;
761
762 assert(backend && cb);
763
764 objects_dir = backend->objects_dir;
765
766 git_buf_sets(&buf, objects_dir);
767 git_path_to_dir(&buf);
768 if (git_buf_oom(&buf))
769 return -1;
770
771 memset(&state, 0, sizeof(state));
772 state.cb = cb;
773 state.data = data;
774 state.dir_len = git_buf_len(&buf);
775
776 error = git_path_direach(&buf, 0, foreach_cb, &state);
777
778 git_buf_dispose(&buf);
779
780 return error;
781 }
782
783 static int loose_backend__writestream_finalize(git_odb_stream *_stream, const git_oid *oid)
784 {
785 loose_writestream *stream = (loose_writestream *)_stream;
786 loose_backend *backend = (loose_backend *)_stream->backend;
787 git_buf final_path = GIT_BUF_INIT;
788 int error = 0;
789
790 if (object_file_name(&final_path, backend, oid) < 0 ||
791 object_mkdir(&final_path, backend) < 0)
792 error = -1;
793 else
794 error = git_filebuf_commit_at(
795 &stream->fbuf, final_path.ptr);
796
797 git_buf_dispose(&final_path);
798
799 return error;
800 }
801
802 static int loose_backend__writestream_write(git_odb_stream *_stream, const char *data, size_t len)
803 {
804 loose_writestream *stream = (loose_writestream *)_stream;
805 return git_filebuf_write(&stream->fbuf, data, len);
806 }
807
808 static void loose_backend__writestream_free(git_odb_stream *_stream)
809 {
810 loose_writestream *stream = (loose_writestream *)_stream;
811
812 git_filebuf_cleanup(&stream->fbuf);
813 git__free(stream);
814 }
815
816 static int filebuf_flags(loose_backend *backend)
817 {
818 int flags = GIT_FILEBUF_TEMPORARY |
819 (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT);
820
821 if (backend->fsync_object_files || git_repository__fsync_gitdir)
822 flags |= GIT_FILEBUF_FSYNC;
823
824 return flags;
825 }
826
827 static int loose_backend__writestream(git_odb_stream **stream_out, git_odb_backend *_backend, git_object_size_t length, git_object_t type)
828 {
829 loose_backend *backend;
830 loose_writestream *stream = NULL;
831 char hdr[MAX_HEADER_LEN];
832 git_buf tmp_path = GIT_BUF_INIT;
833 size_t hdrlen;
834 int error;
835
836 assert(_backend);
837
838 backend = (loose_backend *)_backend;
839 *stream_out = NULL;
840
841 if ((error = git_odb__format_object_header(&hdrlen,
842 hdr, sizeof(hdr), length, type)) < 0)
843 return error;
844
845 stream = git__calloc(1, sizeof(loose_writestream));
846 GIT_ERROR_CHECK_ALLOC(stream);
847
848 stream->stream.backend = _backend;
849 stream->stream.read = NULL; /* read only */
850 stream->stream.write = &loose_backend__writestream_write;
851 stream->stream.finalize_write = &loose_backend__writestream_finalize;
852 stream->stream.free = &loose_backend__writestream_free;
853 stream->stream.mode = GIT_STREAM_WRONLY;
854
855 if (git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||
856 git_filebuf_open(&stream->fbuf, tmp_path.ptr, filebuf_flags(backend),
857 backend->object_file_mode) < 0 ||
858 stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0)
859 {
860 git_filebuf_cleanup(&stream->fbuf);
861 git__free(stream);
862 stream = NULL;
863 }
864 git_buf_dispose(&tmp_path);
865 *stream_out = (git_odb_stream *)stream;
866
867 return !stream ? -1 : 0;
868 }
869
870 static int loose_backend__readstream_read(
871 git_odb_stream *_stream,
872 char *buffer,
873 size_t buffer_len)
874 {
875 loose_readstream *stream = (loose_readstream *)_stream;
876 size_t start_remain = stream->start_len - stream->start_read;
877 int total = 0, error;
878
879 buffer_len = min(buffer_len, INT_MAX);
880
881 /*
882 * if we read more than just the header in the initial read, play
883 * that back for the caller.
884 */
885 if (start_remain && buffer_len) {
886 size_t chunk = min(start_remain, buffer_len);
887 memcpy(buffer, stream->start + stream->start_read, chunk);
888
889 buffer += chunk;
890 stream->start_read += chunk;
891
892 total += (int)chunk;
893 buffer_len -= chunk;
894 }
895
896 if (buffer_len) {
897 size_t chunk = buffer_len;
898
899 if ((error = git_zstream_get_output(buffer, &chunk, &stream->zstream)) < 0)
900 return error;
901
902 total += (int)chunk;
903 }
904
905 return (int)total;
906 }
907
908 static void loose_backend__readstream_free(git_odb_stream *_stream)
909 {
910 loose_readstream *stream = (loose_readstream *)_stream;
911
912 git_futils_mmap_free(&stream->map);
913 git_zstream_free(&stream->zstream);
914 git__free(stream);
915 }
916
917 static int loose_backend__readstream_packlike(
918 obj_hdr *hdr,
919 loose_readstream *stream)
920 {
921 const unsigned char *data;
922 size_t data_len, head_len;
923 int error;
924
925 data = stream->map.data;
926 data_len = stream->map.len;
927
928 /*
929 * read the object header, which is an (uncompressed)
930 * binary encoding of the object type and size.
931 */
932 if ((error = parse_header_packlike(hdr, &head_len, data, data_len)) < 0)
933 return error;
934
935 if (!git_object_typeisloose(hdr->type)) {
936 git_error_set(GIT_ERROR_ODB, "failed to inflate loose object");
937 return -1;
938 }
939
940 return git_zstream_set_input(&stream->zstream,
941 data + head_len, data_len - head_len);
942 }
943
944 static int loose_backend__readstream_standard(
945 obj_hdr *hdr,
946 loose_readstream *stream)
947 {
948 unsigned char head[MAX_HEADER_LEN];
949 size_t init, head_len;
950 int error;
951
952 if ((error = git_zstream_set_input(&stream->zstream,
953 stream->map.data, stream->map.len)) < 0)
954 return error;
955
956 init = sizeof(head);
957
958 /*
959 * inflate the initial part of the compressed buffer in order to
960 * parse the header; read the largest header possible, then store
961 * it in the `start` field of the stream object.
962 */
963 if ((error = git_zstream_get_output(head, &init, &stream->zstream)) < 0 ||
964 (error = parse_header(hdr, &head_len, head, init)) < 0)
965 return error;
966
967 if (!git_object_typeisloose(hdr->type)) {
968 git_error_set(GIT_ERROR_ODB, "failed to inflate disk object");
969 return -1;
970 }
971
972 if (init > head_len) {
973 stream->start_len = init - head_len;
974 memcpy(stream->start, head + head_len, init - head_len);
975 }
976
977 return 0;
978 }
979
980 static int loose_backend__readstream(
981 git_odb_stream **stream_out,
982 size_t *len_out,
983 git_object_t *type_out,
984 git_odb_backend *_backend,
985 const git_oid *oid)
986 {
987 loose_backend *backend;
988 loose_readstream *stream = NULL;
989 git_hash_ctx *hash_ctx = NULL;
990 git_buf object_path = GIT_BUF_INIT;
991 obj_hdr hdr;
992 int error = 0;
993
994 assert(stream_out && len_out && type_out && _backend && oid);
995
996 backend = (loose_backend *)_backend;
997 *stream_out = NULL;
998 *len_out = 0;
999 *type_out = GIT_OBJECT_INVALID;
1000
1001 if (locate_object(&object_path, backend, oid) < 0) {
1002 error = git_odb__error_notfound("no matching loose object",
1003 oid, GIT_OID_HEXSZ);
1004 goto done;
1005 }
1006
1007 stream = git__calloc(1, sizeof(loose_readstream));
1008 GIT_ERROR_CHECK_ALLOC(stream);
1009
1010 hash_ctx = git__malloc(sizeof(git_hash_ctx));
1011 GIT_ERROR_CHECK_ALLOC(hash_ctx);
1012
1013 if ((error = git_hash_ctx_init(hash_ctx)) < 0 ||
1014 (error = git_futils_mmap_ro_file(&stream->map, object_path.ptr)) < 0 ||
1015 (error = git_zstream_init(&stream->zstream, GIT_ZSTREAM_INFLATE)) < 0)
1016 goto done;
1017
1018 /* check for a packlike loose object */
1019 if (!is_zlib_compressed_data(stream->map.data, stream->map.len))
1020 error = loose_backend__readstream_packlike(&hdr, stream);
1021 else
1022 error = loose_backend__readstream_standard(&hdr, stream);
1023
1024 if (error < 0)
1025 goto done;
1026
1027 stream->stream.backend = _backend;
1028 stream->stream.hash_ctx = hash_ctx;
1029 stream->stream.read = &loose_backend__readstream_read;
1030 stream->stream.free = &loose_backend__readstream_free;
1031
1032 *stream_out = (git_odb_stream *)stream;
1033 *len_out = hdr.size;
1034 *type_out = hdr.type;
1035
1036 done:
1037 if (error < 0) {
1038 if (stream) {
1039 git_futils_mmap_free(&stream->map);
1040 git_zstream_free(&stream->zstream);
1041 git__free(stream);
1042 }
1043 if (hash_ctx) {
1044 git_hash_ctx_cleanup(hash_ctx);
1045 git__free(hash_ctx);
1046 }
1047 }
1048
1049 git_buf_dispose(&object_path);
1050 return error;
1051 }
1052
1053 static int loose_backend__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_object_t type)
1054 {
1055 int error = 0;
1056 git_buf final_path = GIT_BUF_INIT;
1057 char header[MAX_HEADER_LEN];
1058 size_t header_len;
1059 git_filebuf fbuf = GIT_FILEBUF_INIT;
1060 loose_backend *backend;
1061
1062 backend = (loose_backend *)_backend;
1063
1064 /* prepare the header for the file */
1065 if ((error = git_odb__format_object_header(&header_len,
1066 header, sizeof(header), len, type)) < 0)
1067 goto cleanup;
1068
1069 if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||
1070 git_filebuf_open(&fbuf, final_path.ptr, filebuf_flags(backend),
1071 backend->object_file_mode) < 0)
1072 {
1073 error = -1;
1074 goto cleanup;
1075 }
1076
1077 git_filebuf_write(&fbuf, header, header_len);
1078 git_filebuf_write(&fbuf, data, len);
1079
1080 if (object_file_name(&final_path, backend, oid) < 0 ||
1081 object_mkdir(&final_path, backend) < 0 ||
1082 git_filebuf_commit_at(&fbuf, final_path.ptr) < 0)
1083 error = -1;
1084
1085 cleanup:
1086 if (error < 0)
1087 git_filebuf_cleanup(&fbuf);
1088 git_buf_dispose(&final_path);
1089 return error;
1090 }
1091
1092 static int loose_backend__freshen(
1093 git_odb_backend *_backend,
1094 const git_oid *oid)
1095 {
1096 loose_backend *backend = (loose_backend *)_backend;
1097 git_buf path = GIT_BUF_INIT;
1098 int error;
1099
1100 if (object_file_name(&path, backend, oid) < 0)
1101 return -1;
1102
1103 error = git_futils_touch(path.ptr, NULL);
1104 git_buf_dispose(&path);
1105
1106 return error;
1107 }
1108
1109 static void loose_backend__free(git_odb_backend *_backend)
1110 {
1111 loose_backend *backend;
1112 assert(_backend);
1113 backend = (loose_backend *)_backend;
1114
1115 git__free(backend);
1116 }
1117
1118 int git_odb_backend_loose(
1119 git_odb_backend **backend_out,
1120 const char *objects_dir,
1121 int compression_level,
1122 int do_fsync,
1123 unsigned int dir_mode,
1124 unsigned int file_mode)
1125 {
1126 loose_backend *backend;
1127 size_t objects_dirlen, alloclen;
1128
1129 assert(backend_out && objects_dir);
1130
1131 objects_dirlen = strlen(objects_dir);
1132
1133 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(loose_backend), objects_dirlen);
1134 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 2);
1135 backend = git__calloc(1, alloclen);
1136 GIT_ERROR_CHECK_ALLOC(backend);
1137
1138 backend->parent.version = GIT_ODB_BACKEND_VERSION;
1139 backend->objects_dirlen = objects_dirlen;
1140 memcpy(backend->objects_dir, objects_dir, objects_dirlen);
1141 if (backend->objects_dir[backend->objects_dirlen - 1] != '/')
1142 backend->objects_dir[backend->objects_dirlen++] = '/';
1143
1144 if (compression_level < 0)
1145 compression_level = Z_BEST_SPEED;
1146
1147 if (dir_mode == 0)
1148 dir_mode = GIT_OBJECT_DIR_MODE;
1149
1150 if (file_mode == 0)
1151 file_mode = GIT_OBJECT_FILE_MODE;
1152
1153 backend->object_zlib_level = compression_level;
1154 backend->fsync_object_files = do_fsync;
1155 backend->object_dir_mode = dir_mode;
1156 backend->object_file_mode = file_mode;
1157
1158 backend->parent.read = &loose_backend__read;
1159 backend->parent.write = &loose_backend__write;
1160 backend->parent.read_prefix = &loose_backend__read_prefix;
1161 backend->parent.read_header = &loose_backend__read_header;
1162 backend->parent.writestream = &loose_backend__writestream;
1163 backend->parent.readstream = &loose_backend__readstream;
1164 backend->parent.exists = &loose_backend__exists;
1165 backend->parent.exists_prefix = &loose_backend__exists_prefix;
1166 backend->parent.foreach = &loose_backend__foreach;
1167 backend->parent.freshen = &loose_backend__freshen;
1168 backend->parent.free = &loose_backend__free;
1169
1170 *backend_out = (git_odb_backend *)backend;
1171 return 0;
1172 }