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_str
*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_str_grow(name
, alloclen
) < 0)
89 git_str_set(name
, be
->objects_dir
, be
->objects_dirlen
);
90 git_fs_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_str
*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_str
*obj
)
227 git_str body
= GIT_STR_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_str_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_str_detach(&body
);
269 git_str_dispose(&body
);
273 static int read_loose_standard(git_rawobj
*out
, git_str
*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_str_cstr(obj
), git_str_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__calloc(1, alloc_size
)) == NULL
) {
312 GIT_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_str
*loc
)
345 git_str obj
= GIT_STR_INIT
;
350 if (git_str_oom(loc
))
355 out
->type
= GIT_OBJECT_INVALID
;
357 if ((error
= git_futils_readbuffer(&obj
, loc
->ptr
)) < 0)
360 if (!is_zlib_compressed_data((unsigned char *)obj
.ptr
, obj
.size
))
361 error
= read_loose_packlike(out
, &obj
);
363 error
= read_loose_standard(out
, &obj
);
366 git_str_dispose(&obj
);
370 static int read_header_loose_packlike(
371 git_rawobj
*out
, const unsigned char *data
, size_t len
)
377 if ((error
= parse_header_packlike(&hdr
, &header_len
, data
, len
)) < 0)
381 out
->type
= hdr
.type
;
386 static int read_header_loose_standard(
387 git_rawobj
*out
, const unsigned char *data
, size_t len
)
389 git_zstream zs
= GIT_ZSTREAM_INIT
;
391 unsigned char inflated
[MAX_HEADER_LEN
] = {0};
392 size_t header_len
, inflated_len
= sizeof(inflated
);
395 if ((error
= git_zstream_init(&zs
, GIT_ZSTREAM_INFLATE
)) < 0 ||
396 (error
= git_zstream_set_input(&zs
, data
, len
)) < 0 ||
397 (error
= git_zstream_get_output_chunk(inflated
, &inflated_len
, &zs
)) < 0 ||
398 (error
= parse_header(&hdr
, &header_len
, inflated
, inflated_len
)) < 0)
402 out
->type
= hdr
.type
;
405 git_zstream_free(&zs
);
409 static int read_header_loose(git_rawobj
*out
, git_str
*loc
)
411 unsigned char obj
[1024];
418 if (git_str_oom(loc
))
423 if ((error
= fd
= git_futils_open_ro(loc
->ptr
)) < 0)
426 if ((obj_len
= p_read(fd
, obj
, sizeof(obj
))) < 0) {
427 error
= (int)obj_len
;
431 if (!is_zlib_compressed_data(obj
, (size_t)obj_len
))
432 error
= read_header_loose_packlike(out
, obj
, (size_t)obj_len
);
434 error
= read_header_loose_standard(out
, obj
, (size_t)obj_len
);
436 if (!error
&& !git_object_typeisloose(out
->type
)) {
437 git_error_set(GIT_ERROR_ZLIB
, "failed to read loose object header");
448 static int locate_object(
449 git_str
*object_location
,
450 loose_backend
*backend
,
453 int error
= object_file_name(object_location
, backend
, oid
);
455 if (!error
&& !git_fs_path_exists(object_location
->ptr
))
456 return GIT_ENOTFOUND
;
461 /* Explore an entry of a directory and see if it matches a short oid */
462 static int fn_locate_object_short_oid(void *state
, git_str
*pathbuf
) {
463 loose_locate_object_state
*sstate
= (loose_locate_object_state
*)state
;
465 if (git_str_len(pathbuf
) - sstate
->dir_len
!= GIT_OID_HEXSZ
- 2) {
466 /* Entry cannot be an object. Continue to next entry */
470 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 if (!memcmp(sstate
->short_oid
+ 2,
474 (unsigned char *)pathbuf
->ptr
+ sstate
->dir_len
,
475 sstate
->short_oid_len
- 2)) {
477 if (!sstate
->found
) {
478 sstate
->res_oid
[0] = sstate
->short_oid
[0];
479 sstate
->res_oid
[1] = sstate
->short_oid
[1];
480 memcpy(sstate
->res_oid
+2, pathbuf
->ptr
+sstate
->dir_len
, GIT_OID_HEXSZ
-2);
486 if (sstate
->found
> 1)
487 return GIT_EAMBIGUOUS
;
492 /* Locate an object matching a given short oid */
493 static int locate_object_short_oid(
494 git_str
*object_location
,
496 loose_backend
*backend
,
497 const git_oid
*short_oid
,
500 char *objects_dir
= backend
->objects_dir
;
501 size_t dir_len
= strlen(objects_dir
), alloc_len
;
502 loose_locate_object_state state
;
505 /* prealloc memory for OBJ_DIR/xx/xx..38x..xx */
506 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len
, dir_len
, GIT_OID_HEXSZ
);
507 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len
, alloc_len
, 3);
508 if (git_str_grow(object_location
, alloc_len
) < 0)
511 git_str_set(object_location
, objects_dir
, dir_len
);
512 git_fs_path_to_dir(object_location
);
514 /* save adjusted position at end of dir so it can be restored later */
515 dir_len
= git_str_len(object_location
);
517 /* Convert raw oid to hex formatted oid */
518 git_oid_fmt((char *)state
.short_oid
, short_oid
);
520 /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */
521 if (git_str_put(object_location
, (char *)state
.short_oid
, 3) < 0)
523 object_location
->ptr
[object_location
->size
- 1] = '/';
525 /* Check that directory exists */
526 if (git_fs_path_isdir(object_location
->ptr
) == false)
527 return git_odb__error_notfound("no matching loose object for prefix",
530 state
.dir_len
= git_str_len(object_location
);
531 state
.short_oid_len
= len
;
534 /* Explore directory to find a unique object matching short_oid */
535 error
= git_fs_path_direach(
536 object_location
, 0, fn_locate_object_short_oid
, &state
);
537 if (error
< 0 && error
!= GIT_EAMBIGUOUS
)
541 return git_odb__error_notfound("no matching loose object for prefix",
545 return git_odb__error_ambiguous("multiple matches in loose objects");
547 /* Convert obtained hex formatted oid to raw */
548 error
= git_oid_fromstr(res_oid
, (char *)state
.res_oid
);
552 /* Update the location according to the oid obtained */
553 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len
, dir_len
, GIT_OID_HEXSZ
);
554 GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len
, alloc_len
, 2);
556 git_str_truncate(object_location
, dir_len
);
557 if (git_str_grow(object_location
, alloc_len
) < 0)
560 git_oid_pathfmt(object_location
->ptr
+ dir_len
, res_oid
);
562 object_location
->size
+= GIT_OID_HEXSZ
+ 1;
563 object_location
->ptr
[object_location
->size
] = '\0';
576 /***********************************************************
578 * LOOSE BACKEND PUBLIC API
580 * Implement the git_odb_backend API calls
582 ***********************************************************/
584 static int loose_backend__read_header(size_t *len_p
, git_object_t
*type_p
, git_odb_backend
*backend
, const git_oid
*oid
)
586 git_str object_path
= GIT_STR_INIT
;
590 GIT_ASSERT_ARG(backend
);
594 raw
.type
= GIT_OBJECT_INVALID
;
596 if (locate_object(&object_path
, (loose_backend
*)backend
, oid
) < 0) {
597 error
= git_odb__error_notfound("no matching loose object",
599 } else if ((error
= read_header_loose(&raw
, &object_path
)) == 0) {
604 git_str_dispose(&object_path
);
609 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
)
611 git_str object_path
= GIT_STR_INIT
;
615 GIT_ASSERT_ARG(backend
);
618 if (locate_object(&object_path
, (loose_backend
*)backend
, oid
) < 0) {
619 error
= git_odb__error_notfound("no matching loose object",
621 } else if ((error
= read_loose(&raw
, &object_path
)) == 0) {
622 *buffer_p
= raw
.data
;
627 git_str_dispose(&object_path
);
632 static int loose_backend__read_prefix(
636 git_object_t
*type_p
,
637 git_odb_backend
*backend
,
638 const git_oid
*short_oid
,
643 GIT_ASSERT_ARG(len
>= GIT_OID_MINPREFIXLEN
&& len
<= GIT_OID_HEXSZ
);
645 if (len
== GIT_OID_HEXSZ
) {
646 /* We can fall back to regular read method */
647 error
= loose_backend__read(buffer_p
, len_p
, type_p
, backend
, short_oid
);
649 git_oid_cpy(out_oid
, short_oid
);
651 git_str object_path
= GIT_STR_INIT
;
654 GIT_ASSERT_ARG(backend
&& short_oid
);
656 if ((error
= locate_object_short_oid(&object_path
, out_oid
,
657 (loose_backend
*)backend
, short_oid
, len
)) == 0 &&
658 (error
= read_loose(&raw
, &object_path
)) == 0)
660 *buffer_p
= raw
.data
;
665 git_str_dispose(&object_path
);
671 static int loose_backend__exists(git_odb_backend
*backend
, const git_oid
*oid
)
673 git_str object_path
= GIT_STR_INIT
;
676 GIT_ASSERT_ARG(backend
);
679 error
= locate_object(&object_path
, (loose_backend
*)backend
, oid
);
681 git_str_dispose(&object_path
);
686 static int loose_backend__exists_prefix(
687 git_oid
*out
, git_odb_backend
*backend
, const git_oid
*short_id
, size_t len
)
689 git_str object_path
= GIT_STR_INIT
;
692 GIT_ASSERT_ARG(backend
);
694 GIT_ASSERT_ARG(short_id
);
695 GIT_ASSERT_ARG(len
>= GIT_OID_MINPREFIXLEN
);
697 error
= locate_object_short_oid(
698 &object_path
, out
, (loose_backend
*)backend
, short_id
, len
);
700 git_str_dispose(&object_path
);
705 struct foreach_state
{
707 git_odb_foreach_cb cb
;
711 GIT_INLINE(int) filename_to_oid(git_oid
*oid
, const char *ptr
)
714 if (strlen(ptr
) != GIT_OID_HEXSZ
+1)
721 v
= (git__fromhex(ptr
[i
]) << 4) | git__fromhex(ptr
[i
+1]);
725 oid
->id
[0] = (unsigned char) v
;
728 for (i
= 0; i
< 38; i
+= 2) {
729 v
= (git__fromhex(ptr
[i
]) << 4) | git__fromhex(ptr
[i
+ 1]);
733 oid
->id
[1 + i
/2] = (unsigned char) v
;
739 static int foreach_object_dir_cb(void *_state
, git_str
*path
)
742 struct foreach_state
*state
= (struct foreach_state
*) _state
;
744 if (filename_to_oid(&oid
, path
->ptr
+ state
->dir_len
) < 0)
747 return git_error_set_after_callback_function(
748 state
->cb(&oid
, state
->data
), "git_odb_foreach");
751 static int foreach_cb(void *_state
, git_str
*path
)
753 struct foreach_state
*state
= (struct foreach_state
*) _state
;
755 /* non-dir is some stray file, ignore it */
756 if (!git_fs_path_isdir(git_str_cstr(path
)))
759 return git_fs_path_direach(path
, 0, foreach_object_dir_cb
, state
);
762 static int loose_backend__foreach(git_odb_backend
*_backend
, git_odb_foreach_cb cb
, void *data
)
766 git_str buf
= GIT_STR_INIT
;
767 struct foreach_state state
;
768 loose_backend
*backend
= (loose_backend
*) _backend
;
770 GIT_ASSERT_ARG(backend
);
773 objects_dir
= backend
->objects_dir
;
775 git_str_sets(&buf
, objects_dir
);
776 git_fs_path_to_dir(&buf
);
777 if (git_str_oom(&buf
))
780 memset(&state
, 0, sizeof(state
));
783 state
.dir_len
= git_str_len(&buf
);
785 error
= git_fs_path_direach(&buf
, 0, foreach_cb
, &state
);
787 git_str_dispose(&buf
);
792 static int loose_backend__writestream_finalize(git_odb_stream
*_stream
, const git_oid
*oid
)
794 loose_writestream
*stream
= (loose_writestream
*)_stream
;
795 loose_backend
*backend
= (loose_backend
*)_stream
->backend
;
796 git_str final_path
= GIT_STR_INIT
;
799 if (object_file_name(&final_path
, backend
, oid
) < 0 ||
800 object_mkdir(&final_path
, backend
) < 0)
803 error
= git_filebuf_commit_at(
804 &stream
->fbuf
, final_path
.ptr
);
806 git_str_dispose(&final_path
);
811 static int loose_backend__writestream_write(git_odb_stream
*_stream
, const char *data
, size_t len
)
813 loose_writestream
*stream
= (loose_writestream
*)_stream
;
814 return git_filebuf_write(&stream
->fbuf
, data
, len
);
817 static void loose_backend__writestream_free(git_odb_stream
*_stream
)
819 loose_writestream
*stream
= (loose_writestream
*)_stream
;
821 git_filebuf_cleanup(&stream
->fbuf
);
825 static int filebuf_flags(loose_backend
*backend
)
827 int flags
= GIT_FILEBUF_TEMPORARY
|
828 (backend
->object_zlib_level
<< GIT_FILEBUF_DEFLATE_SHIFT
);
830 if (backend
->fsync_object_files
|| git_repository__fsync_gitdir
)
831 flags
|= GIT_FILEBUF_FSYNC
;
836 static int loose_backend__writestream(git_odb_stream
**stream_out
, git_odb_backend
*_backend
, git_object_size_t length
, git_object_t type
)
838 loose_backend
*backend
;
839 loose_writestream
*stream
= NULL
;
840 char hdr
[MAX_HEADER_LEN
];
841 git_str tmp_path
= GIT_STR_INIT
;
845 GIT_ASSERT_ARG(_backend
);
847 backend
= (loose_backend
*)_backend
;
850 if ((error
= git_odb__format_object_header(&hdrlen
,
851 hdr
, sizeof(hdr
), length
, type
)) < 0)
854 stream
= git__calloc(1, sizeof(loose_writestream
));
855 GIT_ERROR_CHECK_ALLOC(stream
);
857 stream
->stream
.backend
= _backend
;
858 stream
->stream
.read
= NULL
; /* read only */
859 stream
->stream
.write
= &loose_backend__writestream_write
;
860 stream
->stream
.finalize_write
= &loose_backend__writestream_finalize
;
861 stream
->stream
.free
= &loose_backend__writestream_free
;
862 stream
->stream
.mode
= GIT_STREAM_WRONLY
;
864 if (git_str_joinpath(&tmp_path
, backend
->objects_dir
, "tmp_object") < 0 ||
865 git_filebuf_open(&stream
->fbuf
, tmp_path
.ptr
, filebuf_flags(backend
),
866 backend
->object_file_mode
) < 0 ||
867 stream
->stream
.write((git_odb_stream
*)stream
, hdr
, hdrlen
) < 0)
869 git_filebuf_cleanup(&stream
->fbuf
);
873 git_str_dispose(&tmp_path
);
874 *stream_out
= (git_odb_stream
*)stream
;
876 return !stream
? -1 : 0;
879 static int loose_backend__readstream_read(
880 git_odb_stream
*_stream
,
884 loose_readstream
*stream
= (loose_readstream
*)_stream
;
885 size_t start_remain
= stream
->start_len
- stream
->start_read
;
886 int total
= 0, error
;
888 buffer_len
= min(buffer_len
, INT_MAX
);
891 * if we read more than just the header in the initial read, play
892 * that back for the caller.
894 if (start_remain
&& buffer_len
) {
895 size_t chunk
= min(start_remain
, buffer_len
);
896 memcpy(buffer
, stream
->start
+ stream
->start_read
, chunk
);
899 stream
->start_read
+= chunk
;
906 size_t chunk
= buffer_len
;
908 if ((error
= git_zstream_get_output(buffer
, &chunk
, &stream
->zstream
)) < 0)
917 static void loose_backend__readstream_free(git_odb_stream
*_stream
)
919 loose_readstream
*stream
= (loose_readstream
*)_stream
;
921 git_futils_mmap_free(&stream
->map
);
922 git_zstream_free(&stream
->zstream
);
926 static int loose_backend__readstream_packlike(
928 loose_readstream
*stream
)
930 const unsigned char *data
;
931 size_t data_len
, head_len
;
934 data
= stream
->map
.data
;
935 data_len
= stream
->map
.len
;
938 * read the object header, which is an (uncompressed)
939 * binary encoding of the object type and size.
941 if ((error
= parse_header_packlike(hdr
, &head_len
, data
, data_len
)) < 0)
944 if (!git_object_typeisloose(hdr
->type
)) {
945 git_error_set(GIT_ERROR_ODB
, "failed to inflate loose object");
949 return git_zstream_set_input(&stream
->zstream
,
950 data
+ head_len
, data_len
- head_len
);
953 static int loose_backend__readstream_standard(
955 loose_readstream
*stream
)
957 unsigned char head
[MAX_HEADER_LEN
];
958 size_t init
, head_len
;
961 if ((error
= git_zstream_set_input(&stream
->zstream
,
962 stream
->map
.data
, stream
->map
.len
)) < 0)
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.
972 if ((error
= git_zstream_get_output(head
, &init
, &stream
->zstream
)) < 0 ||
973 (error
= parse_header(hdr
, &head_len
, head
, init
)) < 0)
976 if (!git_object_typeisloose(hdr
->type
)) {
977 git_error_set(GIT_ERROR_ODB
, "failed to inflate disk object");
981 if (init
> head_len
) {
982 stream
->start_len
= init
- head_len
;
983 memcpy(stream
->start
, head
+ head_len
, init
- head_len
);
989 static int loose_backend__readstream(
990 git_odb_stream
**stream_out
,
992 git_object_t
*type_out
,
993 git_odb_backend
*_backend
,
996 loose_backend
*backend
;
997 loose_readstream
*stream
= NULL
;
998 git_hash_ctx
*hash_ctx
= NULL
;
999 git_str object_path
= GIT_STR_INIT
;
1003 GIT_ASSERT_ARG(stream_out
);
1004 GIT_ASSERT_ARG(len_out
);
1005 GIT_ASSERT_ARG(type_out
);
1006 GIT_ASSERT_ARG(_backend
);
1007 GIT_ASSERT_ARG(oid
);
1009 backend
= (loose_backend
*)_backend
;
1012 *type_out
= GIT_OBJECT_INVALID
;
1014 if (locate_object(&object_path
, backend
, oid
) < 0) {
1015 error
= git_odb__error_notfound("no matching loose object",
1016 oid
, GIT_OID_HEXSZ
);
1020 stream
= git__calloc(1, sizeof(loose_readstream
));
1021 GIT_ERROR_CHECK_ALLOC(stream
);
1023 hash_ctx
= git__malloc(sizeof(git_hash_ctx
));
1024 GIT_ERROR_CHECK_ALLOC(hash_ctx
);
1026 if ((error
= git_hash_ctx_init(hash_ctx
, GIT_HASH_ALGORITHM_SHA1
)) < 0 ||
1027 (error
= git_futils_mmap_ro_file(&stream
->map
, object_path
.ptr
)) < 0 ||
1028 (error
= git_zstream_init(&stream
->zstream
, GIT_ZSTREAM_INFLATE
)) < 0)
1031 /* check for a packlike loose object */
1032 if (!is_zlib_compressed_data(stream
->map
.data
, stream
->map
.len
))
1033 error
= loose_backend__readstream_packlike(&hdr
, stream
);
1035 error
= loose_backend__readstream_standard(&hdr
, stream
);
1040 stream
->stream
.backend
= _backend
;
1041 stream
->stream
.hash_ctx
= hash_ctx
;
1042 stream
->stream
.read
= &loose_backend__readstream_read
;
1043 stream
->stream
.free
= &loose_backend__readstream_free
;
1045 *stream_out
= (git_odb_stream
*)stream
;
1046 *len_out
= hdr
.size
;
1047 *type_out
= hdr
.type
;
1052 git_futils_mmap_free(&stream
->map
);
1053 git_zstream_free(&stream
->zstream
);
1057 git_hash_ctx_cleanup(hash_ctx
);
1058 git__free(hash_ctx
);
1062 git_str_dispose(&object_path
);
1066 static int loose_backend__write(git_odb_backend
*_backend
, const git_oid
*oid
, const void *data
, size_t len
, git_object_t type
)
1069 git_str final_path
= GIT_STR_INIT
;
1070 char header
[MAX_HEADER_LEN
];
1072 git_filebuf fbuf
= GIT_FILEBUF_INIT
;
1073 loose_backend
*backend
;
1075 backend
= (loose_backend
*)_backend
;
1077 /* prepare the header for the file */
1078 if ((error
= git_odb__format_object_header(&header_len
,
1079 header
, sizeof(header
), len
, type
)) < 0)
1082 if (git_str_joinpath(&final_path
, backend
->objects_dir
, "tmp_object") < 0 ||
1083 git_filebuf_open(&fbuf
, final_path
.ptr
, filebuf_flags(backend
),
1084 backend
->object_file_mode
) < 0)
1090 git_filebuf_write(&fbuf
, header
, header_len
);
1091 git_filebuf_write(&fbuf
, data
, len
);
1093 if (object_file_name(&final_path
, backend
, oid
) < 0 ||
1094 object_mkdir(&final_path
, backend
) < 0 ||
1095 git_filebuf_commit_at(&fbuf
, final_path
.ptr
) < 0)
1100 git_filebuf_cleanup(&fbuf
);
1101 git_str_dispose(&final_path
);
1105 static int loose_backend__freshen(
1106 git_odb_backend
*_backend
,
1109 loose_backend
*backend
= (loose_backend
*)_backend
;
1110 git_str path
= GIT_STR_INIT
;
1113 if (object_file_name(&path
, backend
, oid
) < 0)
1116 error
= git_futils_touch(path
.ptr
, NULL
);
1117 git_str_dispose(&path
);
1122 static void loose_backend__free(git_odb_backend
*_backend
)
1124 git__free(_backend
);
1127 int git_odb_backend_loose(
1128 git_odb_backend
**backend_out
,
1129 const char *objects_dir
,
1130 int compression_level
,
1132 unsigned int dir_mode
,
1133 unsigned int file_mode
)
1135 loose_backend
*backend
;
1136 size_t objects_dirlen
, alloclen
;
1138 GIT_ASSERT_ARG(backend_out
);
1139 GIT_ASSERT_ARG(objects_dir
);
1141 objects_dirlen
= strlen(objects_dir
);
1143 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, sizeof(loose_backend
), objects_dirlen
);
1144 GIT_ERROR_CHECK_ALLOC_ADD(&alloclen
, alloclen
, 2);
1145 backend
= git__calloc(1, alloclen
);
1146 GIT_ERROR_CHECK_ALLOC(backend
);
1148 backend
->parent
.version
= GIT_ODB_BACKEND_VERSION
;
1149 backend
->objects_dirlen
= objects_dirlen
;
1150 memcpy(backend
->objects_dir
, objects_dir
, objects_dirlen
);
1151 if (backend
->objects_dir
[backend
->objects_dirlen
- 1] != '/')
1152 backend
->objects_dir
[backend
->objects_dirlen
++] = '/';
1154 if (compression_level
< 0)
1155 compression_level
= Z_BEST_SPEED
;
1158 dir_mode
= GIT_OBJECT_DIR_MODE
;
1161 file_mode
= GIT_OBJECT_FILE_MODE
;
1163 backend
->object_zlib_level
= compression_level
;
1164 backend
->fsync_object_files
= do_fsync
;
1165 backend
->object_dir_mode
= dir_mode
;
1166 backend
->object_file_mode
= file_mode
;
1168 backend
->parent
.read
= &loose_backend__read
;
1169 backend
->parent
.write
= &loose_backend__write
;
1170 backend
->parent
.read_prefix
= &loose_backend__read_prefix
;
1171 backend
->parent
.read_header
= &loose_backend__read_header
;
1172 backend
->parent
.writestream
= &loose_backend__writestream
;
1173 backend
->parent
.readstream
= &loose_backend__readstream
;
1174 backend
->parent
.exists
= &loose_backend__exists
;
1175 backend
->parent
.exists_prefix
= &loose_backend__exists_prefix
;
1176 backend
->parent
.foreach
= &loose_backend__foreach
;
1177 backend
->parent
.freshen
= &loose_backend__freshen
;
1178 backend
->parent
.free
= &loose_backend__free
;
1180 *backend_out
= (git_odb_backend
*)backend
;