2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
11 #include "git2/object.h"
12 #include "git2/sys/odb_backend.h"
21 #include "git2/odb_backend.h"
22 #include "git2/types.h"
24 /* maximum possible header length */
25 #define MAX_HEADER_LEN 64
27 typedef struct { /* object header data */
28 git_object_t type
; /* object type */
29 size_t size
; /* object size */
33 git_odb_stream stream
;
38 git_odb_stream stream
;
40 char start
[MAX_HEADER_LEN
];
46 typedef struct loose_backend
{
47 git_odb_backend parent
;
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
;
54 size_t objects_dirlen
;
55 char objects_dir
[GIT_FLEX_ARRAY
];
58 /* State structure for exploring directories,
59 * in order to locate objects matching a short oid.
63 unsigned char short_oid
[GIT_OID_HEXSZ
]; /* hex formatted oid to match */
65 int found
; /* number of matching
66 * objects already found */
67 unsigned char res_oid
[GIT_OID_HEXSZ
]; /* hex formatted oid of
69 } loose_locate_object_state
;
72 /***********************************************************
74 * MISCELLANEOUS HELPER FUNCTIONS
76 ***********************************************************/
78 static int object_file_name(
79 git_buf
*name
, const loose_backend
*be
, const git_oid
*id
)
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)
89 git_buf_set(name
, be
->objects_dir
, be
->objects_dirlen
);
90 git_path_to_dir(name
);
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';
100 static int object_mkdir(const git_buf
*name
, const loose_backend
*be
)
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
);
107 static int parse_header_packlike(
108 obj_hdr
*out
, size_t *out_len
, const unsigned char *data
, size_t len
)
111 size_t shift
, size
, used
= 0;
117 out
->type
= (c
>> 4) & 7;
125 if (sizeof(size_t) * 8 <= shift
)
129 size
+= (c
& 0x7f) << shift
;
141 git_error_set(GIT_ERROR_OBJECT
, "failed to parse loose object: invalid header");
145 static int parse_header(
148 const unsigned char *_data
,
151 const char *data
= (char *)_data
;
152 size_t i
, typename_len
, size_idx
, size_len
;
157 /* find the object type name */
158 for (i
= 0, typename_len
= 0; i
< data_len
; i
++, typename_len
++) {
163 if (typename_len
== data_len
)
166 out
->type
= git_object_stringn2type(data
, typename_len
);
168 size_idx
= typename_len
+ 1;
169 for (i
= size_idx
, size_len
= 0; i
< data_len
; i
++, size_len
++) {
177 if (git__strntol64(&size
, &data
[size_idx
], size_len
, NULL
, 10) < 0 ||
181 if ((uint64_t)size
> SIZE_MAX
) {
182 git_error_set(GIT_ERROR_OBJECT
, "object is larger than available memory");
186 out
->size
= (size_t)size
;
188 if (GIT_ADD_SIZET_OVERFLOW(out_len
, i
, 1))
194 git_error_set(GIT_ERROR_OBJECT
, "failed to parse loose object: invalid header");
198 static int is_zlib_compressed_data(unsigned char *data
, size_t data_len
)
205 w
= ((unsigned int)(data
[0]) << 8) + data
[1];
206 return (data
[0] & 0x8F) == 0x08 && !(w
% 31);
209 /***********************************************************
211 * ODB OBJECT READING & WRITING
213 * Backend for the public API; read headers and full objects
214 * from the ODB. Write raw data to the ODB.
216 ***********************************************************/
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.
225 static int read_loose_packlike(git_rawobj
*out
, git_buf
*obj
)
227 git_buf body
= GIT_BUF_INIT
;
228 const unsigned char *obj_data
;
230 size_t obj_len
, head_len
, alloc_size
;
233 obj_data
= (unsigned char *)obj
->ptr
;
237 * read the object header, which is an (uncompressed)
238 * binary encoding of the object type and size.
240 if ((error
= parse_header_packlike(&hdr
, &head_len
, obj_data
, obj_len
)) < 0)
243 if (!git_object_typeisloose(hdr
.type
) || head_len
> obj_len
) {
244 git_error_set(GIT_ERROR_ODB
, "failed to inflate loose object");
249 obj_data
+= head_len
;
253 * allocate a buffer and inflate the data into it
255 if (GIT_ADD_SIZET_OVERFLOW(&alloc_size
, hdr
.size
, 1) ||
256 git_buf_init(&body
, alloc_size
) < 0) {
261 if ((error
= git_zstream_inflatebuf(&body
, obj_data
, obj_len
)) < 0)
265 out
->type
= hdr
.type
;
266 out
->data
= git_buf_detach(&body
);
269 git_buf_dispose(&body
);
273 static int read_loose_standard(git_rawobj
*out
, git_buf
*obj
)
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
;
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)
285 decompressed
= sizeof(head
);
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.
292 if ((error
= git_zstream_get_output(head
, &decompressed
, &zstream
)) < 0 ||
293 (error
= parse_header(&hdr
, &head_len
, head
, decompressed
)) < 0)
296 if (!git_object_typeisloose(hdr
.type
)) {
297 git_error_set(GIT_ERROR_ODB
, "failed to inflate disk object");
303 * allocate a buffer and inflate the object data into it
304 * (including the initial sequence in the head buffer).
306 if (GIT_ADD_SIZET_OVERFLOW(&alloc_size
, hdr
.size
, 1) ||
307 (body
= git__malloc(alloc_size
)) == NULL
) {
312 assert(decompressed
>= head_len
);
313 body_len
= decompressed
- head_len
;
316 memcpy(body
, head
+ head_len
, body_len
);
318 decompressed
= hdr
.size
- body_len
;
319 if ((error
= git_zstream_get_output(body
+ body_len
, &decompressed
, &zstream
)) < 0)
322 if (!git_zstream_done(&zstream
)) {
323 git_error_set(GIT_ERROR_ZLIB
, "failed to finish zlib inflation: stream aborted prematurely");
328 body
[hdr
.size
] = '\0';
332 out
->type
= hdr
.type
;
338 git_zstream_free(&zstream
);
342 static int read_loose(git_rawobj
*out
, git_buf
*loc
)
345 git_buf obj
= GIT_BUF_INIT
;
349 if (git_buf_oom(loc
))
354 out
->type
= GIT_OBJECT_INVALID
;
356 if ((error
= git_futils_readbuffer(&obj
, loc
->ptr
)) < 0)
359 if (!is_zlib_compressed_data((unsigned char *)obj
.ptr
, obj
.size
))
360 error
= read_loose_packlike(out
, &obj
);
362 error
= read_loose_standard(out
, &obj
);
365 git_buf_dispose(&obj
);
369 static int read_header_loose_packlike(
370 git_rawobj
*out
, const unsigned char *data
, size_t len
)
376 if ((error
= parse_header_packlike(&hdr
, &header_len
, data
, len
)) < 0)
380 out
->type
= hdr
.type
;
385 static int read_header_loose_standard(
386 git_rawobj
*out
, const unsigned char *data
, size_t len
)
388 git_zstream zs
= GIT_ZSTREAM_INIT
;
390 unsigned char inflated
[MAX_HEADER_LEN
];
391 size_t header_len
, inflated_len
= sizeof(inflated
);
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)
401 out
->type
= hdr
.type
;
404 git_zstream_free(&zs
);
408 static int read_header_loose(git_rawobj
*out
, git_buf
*loc
)
410 unsigned char obj
[1024];
411 int fd
, obj_len
, error
;
415 if (git_buf_oom(loc
))
420 if ((error
= fd
= git_futils_open_ro(loc
->ptr
)) < 0 ||
421 (error
= obj_len
= p_read(fd
, obj
, sizeof(obj
))) < 0)
424 if (!is_zlib_compressed_data(obj
, (size_t)obj_len
))
425 error
= read_header_loose_packlike(out
, obj
, (size_t)obj_len
);
427 error
= read_header_loose_standard(out
, obj
, (size_t)obj_len
);
429 if (!error
&& !git_object_typeisloose(out
->type
)) {
430 git_error_set(GIT_ERROR_ZLIB
, "failed to read loose object header");
441 static int locate_object(
442 git_buf
*object_location
,
443 loose_backend
*backend
,
446 int error
= object_file_name(object_location
, backend
, oid
);
448 if (!error
&& !git_path_exists(object_location
->ptr
))
449 return GIT_ENOTFOUND
;
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
;
458 if (git_buf_len(pathbuf
) - sstate
->dir_len
!= GIT_OID_HEXSZ
- 2) {
459 /* Entry cannot be an object. Continue to next entry */
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)) {
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);
479 if (sstate
->found
> 1)
480 return GIT_EAMBIGUOUS
;
485 /* Locate an object matching a given short oid */
486 static int locate_object_short_oid(
487 git_buf
*object_location
,
489 loose_backend
*backend
,
490 const git_oid
*short_oid
,
493 char *objects_dir
= backend
->objects_dir
;
494 size_t dir_len
= strlen(objects_dir
), alloc_len
;
495 loose_locate_object_state state
;
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)
504 git_buf_set(object_location
, objects_dir
, dir_len
);
505 git_path_to_dir(object_location
);
507 /* save adjusted position at end of dir so it can be restored later */
508 dir_len
= git_buf_len(object_location
);
510 /* Convert raw oid to hex formatted oid */
511 git_oid_fmt((char *)state
.short_oid
, short_oid
);
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)
516 object_location
->ptr
[object_location
->size
- 1] = '/';
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",
523 state
.dir_len
= git_buf_len(object_location
);
524 state
.short_oid_len
= len
;
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
)
534 return git_odb__error_notfound("no matching loose object for prefix",
538 return git_odb__error_ambiguous("multiple matches in loose objects");
540 /* Convert obtained hex formatted oid to raw */
541 error
= git_oid_fromstr(res_oid
, (char *)state
.res_oid
);
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);
549 git_buf_truncate(object_location
, dir_len
);
550 if (git_buf_grow(object_location
, alloc_len
) < 0)
553 git_oid_pathfmt(object_location
->ptr
+ dir_len
, res_oid
);
555 object_location
->size
+= GIT_OID_HEXSZ
+ 1;
556 object_location
->ptr
[object_location
->size
] = '\0';
569 /***********************************************************
571 * LOOSE BACKEND PUBLIC API
573 * Implement the git_odb_backend API calls
575 ***********************************************************/
577 static int loose_backend__read_header(size_t *len_p
, git_object_t
*type_p
, git_odb_backend
*backend
, const git_oid
*oid
)
579 git_buf object_path
= GIT_BUF_INIT
;
583 assert(backend
&& oid
);
586 raw
.type
= GIT_OBJECT_INVALID
;
588 if (locate_object(&object_path
, (loose_backend
*)backend
, oid
) < 0) {
589 error
= git_odb__error_notfound("no matching loose object",
591 } else if ((error
= read_header_loose(&raw
, &object_path
)) == 0) {
596 git_buf_dispose(&object_path
);
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
)
603 git_buf object_path
= GIT_BUF_INIT
;
607 assert(backend
&& oid
);
609 if (locate_object(&object_path
, (loose_backend
*)backend
, oid
) < 0) {
610 error
= git_odb__error_notfound("no matching loose object",
612 } else if ((error
= read_loose(&raw
, &object_path
)) == 0) {
613 *buffer_p
= raw
.data
;
618 git_buf_dispose(&object_path
);
623 static int loose_backend__read_prefix(
627 git_object_t
*type_p
,
628 git_odb_backend
*backend
,
629 const git_oid
*short_oid
,
634 assert(len
>= GIT_OID_MINPREFIXLEN
&& len
<= GIT_OID_HEXSZ
);
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
);
640 git_oid_cpy(out_oid
, short_oid
);
642 git_buf object_path
= GIT_BUF_INIT
;
645 assert(backend
&& short_oid
);
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)
651 *buffer_p
= raw
.data
;
656 git_buf_dispose(&object_path
);
662 static int loose_backend__exists(git_odb_backend
*backend
, const git_oid
*oid
)
664 git_buf object_path
= GIT_BUF_INIT
;
667 assert(backend
&& oid
);
669 error
= locate_object(&object_path
, (loose_backend
*)backend
, oid
);
671 git_buf_dispose(&object_path
);
676 static int loose_backend__exists_prefix(
677 git_oid
*out
, git_odb_backend
*backend
, const git_oid
*short_id
, size_t len
)
679 git_buf object_path
= GIT_BUF_INIT
;
682 assert(backend
&& out
&& short_id
&& len
>= GIT_OID_MINPREFIXLEN
);
684 error
= locate_object_short_oid(
685 &object_path
, out
, (loose_backend
*)backend
, short_id
, len
);
687 git_buf_dispose(&object_path
);
692 struct foreach_state
{
694 git_odb_foreach_cb cb
;
698 GIT_INLINE(int) filename_to_oid(git_oid
*oid
, const char *ptr
)
701 if (strlen(ptr
) != GIT_OID_HEXSZ
+1)
708 v
= (git__fromhex(ptr
[i
]) << 4) | git__fromhex(ptr
[i
+1]);
712 oid
->id
[0] = (unsigned char) v
;
715 for (i
= 0; i
< 38; i
+= 2) {
716 v
= (git__fromhex(ptr
[i
]) << 4) | git__fromhex(ptr
[i
+ 1]);
720 oid
->id
[1 + i
/2] = (unsigned char) v
;
726 static int foreach_object_dir_cb(void *_state
, git_buf
*path
)
729 struct foreach_state
*state
= (struct foreach_state
*) _state
;
731 if (filename_to_oid(&oid
, path
->ptr
+ state
->dir_len
) < 0)
734 return git_error_set_after_callback_function(
735 state
->cb(&oid
, state
->data
), "git_odb_foreach");
738 static int foreach_cb(void *_state
, git_buf
*path
)
740 struct foreach_state
*state
= (struct foreach_state
*) _state
;
742 /* non-dir is some stray file, ignore it */
743 if (!git_path_isdir(git_buf_cstr(path
)))
746 return git_path_direach(path
, 0, foreach_object_dir_cb
, state
);
749 static int loose_backend__foreach(git_odb_backend
*_backend
, git_odb_foreach_cb cb
, void *data
)
753 git_buf buf
= GIT_BUF_INIT
;
754 struct foreach_state state
;
755 loose_backend
*backend
= (loose_backend
*) _backend
;
757 assert(backend
&& cb
);
759 objects_dir
= backend
->objects_dir
;
761 git_buf_sets(&buf
, objects_dir
);
762 git_path_to_dir(&buf
);
763 if (git_buf_oom(&buf
))
766 memset(&state
, 0, sizeof(state
));
769 state
.dir_len
= git_buf_len(&buf
);
771 error
= git_path_direach(&buf
, 0, foreach_cb
, &state
);
773 git_buf_dispose(&buf
);
778 static int loose_backend__writestream_finalize(git_odb_stream
*_stream
, const git_oid
*oid
)
780 loose_writestream
*stream
= (loose_writestream
*)_stream
;
781 loose_backend
*backend
= (loose_backend
*)_stream
->backend
;
782 git_buf final_path
= GIT_BUF_INIT
;
785 if (object_file_name(&final_path
, backend
, oid
) < 0 ||
786 object_mkdir(&final_path
, backend
) < 0)
789 error
= git_filebuf_commit_at(
790 &stream
->fbuf
, final_path
.ptr
);
792 git_buf_dispose(&final_path
);
797 static int loose_backend__writestream_write(git_odb_stream
*_stream
, const char *data
, size_t len
)
799 loose_writestream
*stream
= (loose_writestream
*)_stream
;
800 return git_filebuf_write(&stream
->fbuf
, data
, len
);
803 static void loose_backend__writestream_free(git_odb_stream
*_stream
)
805 loose_writestream
*stream
= (loose_writestream
*)_stream
;
807 git_filebuf_cleanup(&stream
->fbuf
);
811 static int filebuf_flags(loose_backend
*backend
)
813 int flags
= GIT_FILEBUF_TEMPORARY
|
814 (backend
->object_zlib_level
<< GIT_FILEBUF_DEFLATE_SHIFT
);
816 if (backend
->fsync_object_files
|| git_repository__fsync_gitdir
)
817 flags
|= GIT_FILEBUF_FSYNC
;
822 static int loose_backend__writestream(git_odb_stream
**stream_out
, git_odb_backend
*_backend
, git_off_t length
, git_object_t type
)
824 loose_backend
*backend
;
825 loose_writestream
*stream
= NULL
;
826 char hdr
[MAX_HEADER_LEN
];
827 git_buf tmp_path
= GIT_BUF_INIT
;
831 assert(_backend
&& length
>= 0);
833 backend
= (loose_backend
*)_backend
;
836 if ((error
= git_odb__format_object_header(&hdrlen
,
837 hdr
, sizeof(hdr
), length
, type
)) < 0)
840 stream
= git__calloc(1, sizeof(loose_writestream
));
841 GIT_ERROR_CHECK_ALLOC(stream
);
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
;
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)
855 git_filebuf_cleanup(&stream
->fbuf
);
859 git_buf_dispose(&tmp_path
);
860 *stream_out
= (git_odb_stream
*)stream
;
862 return !stream
? -1 : 0;
865 static int loose_backend__readstream_read(
866 git_odb_stream
*_stream
,
870 loose_readstream
*stream
= (loose_readstream
*)_stream
;
871 size_t start_remain
= stream
->start_len
- stream
->start_read
;
872 int total
= 0, error
;
875 * if we read more than just the header in the initial read, play
876 * that back for the caller.
878 if (start_remain
&& buffer_len
) {
879 size_t chunk
= min(start_remain
, buffer_len
);
880 memcpy(buffer
, stream
->start
+ stream
->start_read
, chunk
);
883 stream
->start_read
+= chunk
;
890 size_t chunk
= min(buffer_len
, INT_MAX
);
892 if ((error
= git_zstream_get_output(buffer
, &chunk
, &stream
->zstream
)) < 0)
901 static void loose_backend__readstream_free(git_odb_stream
*_stream
)
903 loose_readstream
*stream
= (loose_readstream
*)_stream
;
905 git_futils_mmap_free(&stream
->map
);
906 git_zstream_free(&stream
->zstream
);
910 static int loose_backend__readstream_packlike(
912 loose_readstream
*stream
)
914 const unsigned char *data
;
915 size_t data_len
, head_len
;
918 data
= stream
->map
.data
;
919 data_len
= stream
->map
.len
;
922 * read the object header, which is an (uncompressed)
923 * binary encoding of the object type and size.
925 if ((error
= parse_header_packlike(hdr
, &head_len
, data
, data_len
)) < 0)
928 if (!git_object_typeisloose(hdr
->type
)) {
929 git_error_set(GIT_ERROR_ODB
, "failed to inflate loose object");
933 return git_zstream_set_input(&stream
->zstream
,
934 data
+ head_len
, data_len
- head_len
);
937 static int loose_backend__readstream_standard(
939 loose_readstream
*stream
)
941 unsigned char head
[MAX_HEADER_LEN
];
942 size_t init
, head_len
;
945 if ((error
= git_zstream_set_input(&stream
->zstream
,
946 stream
->map
.data
, stream
->map
.len
)) < 0)
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.
956 if ((error
= git_zstream_get_output(head
, &init
, &stream
->zstream
)) < 0 ||
957 (error
= parse_header(hdr
, &head_len
, head
, init
)) < 0)
960 if (!git_object_typeisloose(hdr
->type
)) {
961 git_error_set(GIT_ERROR_ODB
, "failed to inflate disk object");
965 if (init
> head_len
) {
966 stream
->start_len
= init
- head_len
;
967 memcpy(stream
->start
, head
+ head_len
, init
- head_len
);
973 static int loose_backend__readstream(
974 git_odb_stream
**stream_out
,
976 git_object_t
*type_out
,
977 git_odb_backend
*_backend
,
980 loose_backend
*backend
;
981 loose_readstream
*stream
= NULL
;
982 git_hash_ctx
*hash_ctx
= NULL
;
983 git_buf object_path
= GIT_BUF_INIT
;
987 assert(stream_out
&& len_out
&& type_out
&& _backend
&& oid
);
989 backend
= (loose_backend
*)_backend
;
992 *type_out
= GIT_OBJECT_INVALID
;
994 if (locate_object(&object_path
, backend
, oid
) < 0) {
995 error
= git_odb__error_notfound("no matching loose object",
1000 stream
= git__calloc(1, sizeof(loose_readstream
));
1001 GIT_ERROR_CHECK_ALLOC(stream
);
1003 hash_ctx
= git__malloc(sizeof(git_hash_ctx
));
1004 GIT_ERROR_CHECK_ALLOC(hash_ctx
);
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)
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
);
1015 error
= loose_backend__readstream_standard(&hdr
, stream
);
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
;
1025 *stream_out
= (git_odb_stream
*)stream
;
1026 *len_out
= hdr
.size
;
1027 *type_out
= hdr
.type
;
1032 git_futils_mmap_free(&stream
->map
);
1033 git_zstream_free(&stream
->zstream
);
1037 git_hash_ctx_cleanup(hash_ctx
);
1038 git__free(hash_ctx
);
1042 git_buf_dispose(&object_path
);
1046 static int loose_backend__write(git_odb_backend
*_backend
, const git_oid
*oid
, const void *data
, size_t len
, git_object_t type
)
1049 git_buf final_path
= GIT_BUF_INIT
;
1050 char header
[MAX_HEADER_LEN
];
1052 git_filebuf fbuf
= GIT_FILEBUF_INIT
;
1053 loose_backend
*backend
;
1055 backend
= (loose_backend
*)_backend
;
1057 /* prepare the header for the file */
1058 if ((error
= git_odb__format_object_header(&header_len
,
1059 header
, sizeof(header
), len
, type
)) < 0)
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)
1070 git_filebuf_write(&fbuf
, header
, header_len
);
1071 git_filebuf_write(&fbuf
, data
, len
);
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)
1080 git_filebuf_cleanup(&fbuf
);
1081 git_buf_dispose(&final_path
);
1085 static int loose_backend__freshen(
1086 git_odb_backend
*_backend
,
1089 loose_backend
*backend
= (loose_backend
*)_backend
;
1090 git_buf path
= GIT_BUF_INIT
;
1093 if (object_file_name(&path
, backend
, oid
) < 0)
1096 error
= git_futils_touch(path
.ptr
, NULL
);
1097 git_buf_dispose(&path
);
1102 static void loose_backend__free(git_odb_backend
*_backend
)
1104 loose_backend
*backend
;
1106 backend
= (loose_backend
*)_backend
;
1111 int git_odb_backend_loose(
1112 git_odb_backend
**backend_out
,
1113 const char *objects_dir
,
1114 int compression_level
,
1116 unsigned int dir_mode
,
1117 unsigned int file_mode
)
1119 loose_backend
*backend
;
1120 size_t objects_dirlen
, alloclen
;
1122 assert(backend_out
&& objects_dir
);
1124 objects_dirlen
= strlen(objects_dir
);
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
);
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
++] = '/';
1137 if (compression_level
< 0)
1138 compression_level
= Z_BEST_SPEED
;
1141 dir_mode
= GIT_OBJECT_DIR_MODE
;
1144 file_mode
= GIT_OBJECT_FILE_MODE
;
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
;
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
;
1163 *backend_out
= (git_odb_backend
*)backend
;