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__calloc(1, 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
] = {0};
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];
416 if (git_buf_oom(loc
))
421 if ((error
= fd
= git_futils_open_ro(loc
->ptr
)) < 0)
424 if ((obj_len
= p_read(fd
, obj
, sizeof(obj
))) < 0) {
425 error
= (int)obj_len
;
429 if (!is_zlib_compressed_data(obj
, (size_t)obj_len
))
430 error
= read_header_loose_packlike(out
, obj
, (size_t)obj_len
);
432 error
= read_header_loose_standard(out
, obj
, (size_t)obj_len
);
434 if (!error
&& !git_object_typeisloose(out
->type
)) {
435 git_error_set(GIT_ERROR_ZLIB
, "failed to read loose object header");
446 static int locate_object(
447 git_buf
*object_location
,
448 loose_backend
*backend
,
451 int error
= object_file_name(object_location
, backend
, oid
);
453 if (!error
&& !git_path_exists(object_location
->ptr
))
454 return GIT_ENOTFOUND
;
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
;
463 if (git_buf_len(pathbuf
) - sstate
->dir_len
!= GIT_OID_HEXSZ
- 2) {
464 /* Entry cannot be an object. Continue to next entry */
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)) {
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);
484 if (sstate
->found
> 1)
485 return GIT_EAMBIGUOUS
;
490 /* Locate an object matching a given short oid */
491 static int locate_object_short_oid(
492 git_buf
*object_location
,
494 loose_backend
*backend
,
495 const git_oid
*short_oid
,
498 char *objects_dir
= backend
->objects_dir
;
499 size_t dir_len
= strlen(objects_dir
), alloc_len
;
500 loose_locate_object_state state
;
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)
509 git_buf_set(object_location
, objects_dir
, dir_len
);
510 git_path_to_dir(object_location
);
512 /* save adjusted position at end of dir so it can be restored later */
513 dir_len
= git_buf_len(object_location
);
515 /* Convert raw oid to hex formatted oid */
516 git_oid_fmt((char *)state
.short_oid
, short_oid
);
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)
521 object_location
->ptr
[object_location
->size
- 1] = '/';
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",
528 state
.dir_len
= git_buf_len(object_location
);
529 state
.short_oid_len
= len
;
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
)
539 return git_odb__error_notfound("no matching loose object for prefix",
543 return git_odb__error_ambiguous("multiple matches in loose objects");
545 /* Convert obtained hex formatted oid to raw */
546 error
= git_oid_fromstr(res_oid
, (char *)state
.res_oid
);
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);
554 git_buf_truncate(object_location
, dir_len
);
555 if (git_buf_grow(object_location
, alloc_len
) < 0)
558 git_oid_pathfmt(object_location
->ptr
+ dir_len
, res_oid
);
560 object_location
->size
+= GIT_OID_HEXSZ
+ 1;
561 object_location
->ptr
[object_location
->size
] = '\0';
574 /***********************************************************
576 * LOOSE BACKEND PUBLIC API
578 * Implement the git_odb_backend API calls
580 ***********************************************************/
582 static int loose_backend__read_header(size_t *len_p
, git_object_t
*type_p
, git_odb_backend
*backend
, const git_oid
*oid
)
584 git_buf object_path
= GIT_BUF_INIT
;
588 assert(backend
&& oid
);
591 raw
.type
= GIT_OBJECT_INVALID
;
593 if (locate_object(&object_path
, (loose_backend
*)backend
, oid
) < 0) {
594 error
= git_odb__error_notfound("no matching loose object",
596 } else if ((error
= read_header_loose(&raw
, &object_path
)) == 0) {
601 git_buf_dispose(&object_path
);
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
)
608 git_buf object_path
= GIT_BUF_INIT
;
612 assert(backend
&& oid
);
614 if (locate_object(&object_path
, (loose_backend
*)backend
, oid
) < 0) {
615 error
= git_odb__error_notfound("no matching loose object",
617 } else if ((error
= read_loose(&raw
, &object_path
)) == 0) {
618 *buffer_p
= raw
.data
;
623 git_buf_dispose(&object_path
);
628 static int loose_backend__read_prefix(
632 git_object_t
*type_p
,
633 git_odb_backend
*backend
,
634 const git_oid
*short_oid
,
639 assert(len
>= GIT_OID_MINPREFIXLEN
&& len
<= GIT_OID_HEXSZ
);
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
);
645 git_oid_cpy(out_oid
, short_oid
);
647 git_buf object_path
= GIT_BUF_INIT
;
650 assert(backend
&& short_oid
);
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)
656 *buffer_p
= raw
.data
;
661 git_buf_dispose(&object_path
);
667 static int loose_backend__exists(git_odb_backend
*backend
, const git_oid
*oid
)
669 git_buf object_path
= GIT_BUF_INIT
;
672 assert(backend
&& oid
);
674 error
= locate_object(&object_path
, (loose_backend
*)backend
, oid
);
676 git_buf_dispose(&object_path
);
681 static int loose_backend__exists_prefix(
682 git_oid
*out
, git_odb_backend
*backend
, const git_oid
*short_id
, size_t len
)
684 git_buf object_path
= GIT_BUF_INIT
;
687 assert(backend
&& out
&& short_id
&& len
>= GIT_OID_MINPREFIXLEN
);
689 error
= locate_object_short_oid(
690 &object_path
, out
, (loose_backend
*)backend
, short_id
, len
);
692 git_buf_dispose(&object_path
);
697 struct foreach_state
{
699 git_odb_foreach_cb cb
;
703 GIT_INLINE(int) filename_to_oid(git_oid
*oid
, const char *ptr
)
706 if (strlen(ptr
) != GIT_OID_HEXSZ
+1)
713 v
= (git__fromhex(ptr
[i
]) << 4) | git__fromhex(ptr
[i
+1]);
717 oid
->id
[0] = (unsigned char) v
;
720 for (i
= 0; i
< 38; i
+= 2) {
721 v
= (git__fromhex(ptr
[i
]) << 4) | git__fromhex(ptr
[i
+ 1]);
725 oid
->id
[1 + i
/2] = (unsigned char) v
;
731 static int foreach_object_dir_cb(void *_state
, git_buf
*path
)
734 struct foreach_state
*state
= (struct foreach_state
*) _state
;
736 if (filename_to_oid(&oid
, path
->ptr
+ state
->dir_len
) < 0)
739 return git_error_set_after_callback_function(
740 state
->cb(&oid
, state
->data
), "git_odb_foreach");
743 static int foreach_cb(void *_state
, git_buf
*path
)
745 struct foreach_state
*state
= (struct foreach_state
*) _state
;
747 /* non-dir is some stray file, ignore it */
748 if (!git_path_isdir(git_buf_cstr(path
)))
751 return git_path_direach(path
, 0, foreach_object_dir_cb
, state
);
754 static int loose_backend__foreach(git_odb_backend
*_backend
, git_odb_foreach_cb cb
, void *data
)
758 git_buf buf
= GIT_BUF_INIT
;
759 struct foreach_state state
;
760 loose_backend
*backend
= (loose_backend
*) _backend
;
762 assert(backend
&& cb
);
764 objects_dir
= backend
->objects_dir
;
766 git_buf_sets(&buf
, objects_dir
);
767 git_path_to_dir(&buf
);
768 if (git_buf_oom(&buf
))
771 memset(&state
, 0, sizeof(state
));
774 state
.dir_len
= git_buf_len(&buf
);
776 error
= git_path_direach(&buf
, 0, foreach_cb
, &state
);
778 git_buf_dispose(&buf
);
783 static int loose_backend__writestream_finalize(git_odb_stream
*_stream
, const git_oid
*oid
)
785 loose_writestream
*stream
= (loose_writestream
*)_stream
;
786 loose_backend
*backend
= (loose_backend
*)_stream
->backend
;
787 git_buf final_path
= GIT_BUF_INIT
;
790 if (object_file_name(&final_path
, backend
, oid
) < 0 ||
791 object_mkdir(&final_path
, backend
) < 0)
794 error
= git_filebuf_commit_at(
795 &stream
->fbuf
, final_path
.ptr
);
797 git_buf_dispose(&final_path
);
802 static int loose_backend__writestream_write(git_odb_stream
*_stream
, const char *data
, size_t len
)
804 loose_writestream
*stream
= (loose_writestream
*)_stream
;
805 return git_filebuf_write(&stream
->fbuf
, data
, len
);
808 static void loose_backend__writestream_free(git_odb_stream
*_stream
)
810 loose_writestream
*stream
= (loose_writestream
*)_stream
;
812 git_filebuf_cleanup(&stream
->fbuf
);
816 static int filebuf_flags(loose_backend
*backend
)
818 int flags
= GIT_FILEBUF_TEMPORARY
|
819 (backend
->object_zlib_level
<< GIT_FILEBUF_DEFLATE_SHIFT
);
821 if (backend
->fsync_object_files
|| git_repository__fsync_gitdir
)
822 flags
|= GIT_FILEBUF_FSYNC
;
827 static int loose_backend__writestream(git_odb_stream
**stream_out
, git_odb_backend
*_backend
, git_object_size_t length
, git_object_t type
)
829 loose_backend
*backend
;
830 loose_writestream
*stream
= NULL
;
831 char hdr
[MAX_HEADER_LEN
];
832 git_buf tmp_path
= GIT_BUF_INIT
;
838 backend
= (loose_backend
*)_backend
;
841 if ((error
= git_odb__format_object_header(&hdrlen
,
842 hdr
, sizeof(hdr
), length
, type
)) < 0)
845 stream
= git__calloc(1, sizeof(loose_writestream
));
846 GIT_ERROR_CHECK_ALLOC(stream
);
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
;
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)
860 git_filebuf_cleanup(&stream
->fbuf
);
864 git_buf_dispose(&tmp_path
);
865 *stream_out
= (git_odb_stream
*)stream
;
867 return !stream
? -1 : 0;
870 static int loose_backend__readstream_read(
871 git_odb_stream
*_stream
,
875 loose_readstream
*stream
= (loose_readstream
*)_stream
;
876 size_t start_remain
= stream
->start_len
- stream
->start_read
;
877 int total
= 0, error
;
879 buffer_len
= min(buffer_len
, INT_MAX
);
882 * if we read more than just the header in the initial read, play
883 * that back for the caller.
885 if (start_remain
&& buffer_len
) {
886 size_t chunk
= min(start_remain
, buffer_len
);
887 memcpy(buffer
, stream
->start
+ stream
->start_read
, chunk
);
890 stream
->start_read
+= chunk
;
897 size_t chunk
= buffer_len
;
899 if ((error
= git_zstream_get_output(buffer
, &chunk
, &stream
->zstream
)) < 0)
908 static void loose_backend__readstream_free(git_odb_stream
*_stream
)
910 loose_readstream
*stream
= (loose_readstream
*)_stream
;
912 git_futils_mmap_free(&stream
->map
);
913 git_zstream_free(&stream
->zstream
);
917 static int loose_backend__readstream_packlike(
919 loose_readstream
*stream
)
921 const unsigned char *data
;
922 size_t data_len
, head_len
;
925 data
= stream
->map
.data
;
926 data_len
= stream
->map
.len
;
929 * read the object header, which is an (uncompressed)
930 * binary encoding of the object type and size.
932 if ((error
= parse_header_packlike(hdr
, &head_len
, data
, data_len
)) < 0)
935 if (!git_object_typeisloose(hdr
->type
)) {
936 git_error_set(GIT_ERROR_ODB
, "failed to inflate loose object");
940 return git_zstream_set_input(&stream
->zstream
,
941 data
+ head_len
, data_len
- head_len
);
944 static int loose_backend__readstream_standard(
946 loose_readstream
*stream
)
948 unsigned char head
[MAX_HEADER_LEN
];
949 size_t init
, head_len
;
952 if ((error
= git_zstream_set_input(&stream
->zstream
,
953 stream
->map
.data
, stream
->map
.len
)) < 0)
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.
963 if ((error
= git_zstream_get_output(head
, &init
, &stream
->zstream
)) < 0 ||
964 (error
= parse_header(hdr
, &head_len
, head
, init
)) < 0)
967 if (!git_object_typeisloose(hdr
->type
)) {
968 git_error_set(GIT_ERROR_ODB
, "failed to inflate disk object");
972 if (init
> head_len
) {
973 stream
->start_len
= init
- head_len
;
974 memcpy(stream
->start
, head
+ head_len
, init
- head_len
);
980 static int loose_backend__readstream(
981 git_odb_stream
**stream_out
,
983 git_object_t
*type_out
,
984 git_odb_backend
*_backend
,
987 loose_backend
*backend
;
988 loose_readstream
*stream
= NULL
;
989 git_hash_ctx
*hash_ctx
= NULL
;
990 git_buf object_path
= GIT_BUF_INIT
;
994 assert(stream_out
&& len_out
&& type_out
&& _backend
&& oid
);
996 backend
= (loose_backend
*)_backend
;
999 *type_out
= GIT_OBJECT_INVALID
;
1001 if (locate_object(&object_path
, backend
, oid
) < 0) {
1002 error
= git_odb__error_notfound("no matching loose object",
1003 oid
, GIT_OID_HEXSZ
);
1007 stream
= git__calloc(1, sizeof(loose_readstream
));
1008 GIT_ERROR_CHECK_ALLOC(stream
);
1010 hash_ctx
= git__malloc(sizeof(git_hash_ctx
));
1011 GIT_ERROR_CHECK_ALLOC(hash_ctx
);
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)
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
);
1022 error
= loose_backend__readstream_standard(&hdr
, stream
);
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
;
1032 *stream_out
= (git_odb_stream
*)stream
;
1033 *len_out
= hdr
.size
;
1034 *type_out
= hdr
.type
;
1039 git_futils_mmap_free(&stream
->map
);
1040 git_zstream_free(&stream
->zstream
);
1044 git_hash_ctx_cleanup(hash_ctx
);
1045 git__free(hash_ctx
);
1049 git_buf_dispose(&object_path
);
1053 static int loose_backend__write(git_odb_backend
*_backend
, const git_oid
*oid
, const void *data
, size_t len
, git_object_t type
)
1056 git_buf final_path
= GIT_BUF_INIT
;
1057 char header
[MAX_HEADER_LEN
];
1059 git_filebuf fbuf
= GIT_FILEBUF_INIT
;
1060 loose_backend
*backend
;
1062 backend
= (loose_backend
*)_backend
;
1064 /* prepare the header for the file */
1065 if ((error
= git_odb__format_object_header(&header_len
,
1066 header
, sizeof(header
), len
, type
)) < 0)
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)
1077 git_filebuf_write(&fbuf
, header
, header_len
);
1078 git_filebuf_write(&fbuf
, data
, len
);
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)
1087 git_filebuf_cleanup(&fbuf
);
1088 git_buf_dispose(&final_path
);
1092 static int loose_backend__freshen(
1093 git_odb_backend
*_backend
,
1096 loose_backend
*backend
= (loose_backend
*)_backend
;
1097 git_buf path
= GIT_BUF_INIT
;
1100 if (object_file_name(&path
, backend
, oid
) < 0)
1103 error
= git_futils_touch(path
.ptr
, NULL
);
1104 git_buf_dispose(&path
);
1109 static void loose_backend__free(git_odb_backend
*_backend
)
1111 loose_backend
*backend
;
1113 backend
= (loose_backend
*)_backend
;
1118 int git_odb_backend_loose(
1119 git_odb_backend
**backend_out
,
1120 const char *objects_dir
,
1121 int compression_level
,
1123 unsigned int dir_mode
,
1124 unsigned int file_mode
)
1126 loose_backend
*backend
;
1127 size_t objects_dirlen
, alloclen
;
1129 assert(backend_out
&& objects_dir
);
1131 objects_dirlen
= strlen(objects_dir
);
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
);
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
++] = '/';
1144 if (compression_level
< 0)
1145 compression_level
= Z_BEST_SPEED
;
1148 dir_mode
= GIT_OBJECT_DIR_MODE
;
1151 file_mode
= GIT_OBJECT_FILE_MODE
;
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
;
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
;
1170 *backend_out
= (git_odb_backend
*)backend
;