]>
Commit | Line | Data |
---|---|---|
7d7cd885 | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
7d7cd885 | 3 | * |
bb742ede VM |
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. | |
7d7cd885 VM |
6 | */ |
7 | ||
8 | #include "common.h" | |
eae0bfdc | 9 | |
0c3bae62 | 10 | #include <zlib.h> |
44908fe7 | 11 | #include "git2/object.h" |
83cc70d9 | 12 | #include "git2/sys/odb_backend.h" |
22a2d3d5 | 13 | #include "futils.h" |
7d7cd885 VM |
14 | #include "hash.h" |
15 | #include "odb.h" | |
6a2d2f8a | 16 | #include "delta.h" |
72a3fe42 | 17 | #include "filebuf.h" |
6d3ad7e0 | 18 | #include "object.h" |
eae0bfdc | 19 | #include "zstream.h" |
7d7cd885 | 20 | |
44908fe7 | 21 | #include "git2/odb_backend.h" |
72a3fe42 | 22 | #include "git2/types.h" |
7d7cd885 | 23 | |
eae0bfdc PP |
24 | /* maximum possible header length */ |
25 | #define MAX_HEADER_LEN 64 | |
26 | ||
87d9869f | 27 | typedef struct { /* object header data */ |
ac3d33df | 28 | git_object_t type; /* object type */ |
87d9869f | 29 | size_t size; /* object size */ |
7d7cd885 VM |
30 | } obj_hdr; |
31 | ||
72a3fe42 VM |
32 | typedef struct { |
33 | git_odb_stream stream; | |
34 | git_filebuf fbuf; | |
72a3fe42 VM |
35 | } loose_writestream; |
36 | ||
eae0bfdc PP |
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 | ||
7d7cd885 VM |
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. */ | |
dd64c71c ET |
51 | mode_t object_file_mode; |
52 | mode_t object_dir_mode; | |
8294e8cf RB |
53 | |
54 | size_t objects_dirlen; | |
55 | char objects_dir[GIT_FLEX_ARRAY]; | |
7d7cd885 VM |
56 | } loose_backend; |
57 | ||
aea8a638 MP |
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 */ | |
b8457baa | 64 | size_t short_oid_len; |
aea8a638 MP |
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 | ||
7d7cd885 VM |
72 | /*********************************************************** |
73 | * | |
b874629b | 74 | * MISCELLANEOUS HELPER FUNCTIONS |
7d7cd885 VM |
75 | * |
76 | ***********************************************************/ | |
77 | ||
8294e8cf | 78 | static int object_file_name( |
e579e0f7 | 79 | git_str *name, const loose_backend *be, const git_oid *id) |
7d7cd885 | 80 | { |
f1453c59 ET |
81 | size_t alloclen; |
82 | ||
8294e8cf | 83 | /* expand length for object root + 40 hex sha1 chars + 2 * '/' + '\0' */ |
ac3d33df JK |
84 | GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, be->objects_dirlen, GIT_OID_HEXSZ); |
85 | GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 3); | |
e579e0f7 | 86 | if (git_str_grow(name, alloclen) < 0) |
e1de726c | 87 | return -1; |
7d7cd885 | 88 | |
e579e0f7 MB |
89 | git_str_set(name, be->objects_dir, be->objects_dirlen); |
90 | git_fs_path_to_dir(name); | |
7d7cd885 VM |
91 | |
92 | /* loose object filename: aa/aaa... (41 bytes) */ | |
8294e8cf | 93 | git_oid_pathfmt(name->ptr + name->size, id); |
97769280 RB |
94 | name->size += GIT_OID_HEXSZ + 1; |
95 | name->ptr[name->size] = '\0'; | |
7d7cd885 | 96 | |
e1de726c | 97 | return 0; |
7d7cd885 VM |
98 | } |
99 | ||
e579e0f7 | 100 | static int object_mkdir(const git_str *name, const loose_backend *be) |
8294e8cf | 101 | { |
ac2fba0e | 102 | return git_futils_mkdir_relative( |
dd64c71c | 103 | name->ptr + be->objects_dirlen, be->objects_dir, be->object_dir_mode, |
ac2fba0e | 104 | GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR, NULL); |
8294e8cf | 105 | } |
7d7cd885 | 106 | |
eae0bfdc PP |
107 | static int parse_header_packlike( |
108 | obj_hdr *out, size_t *out_len, const unsigned char *data, size_t len) | |
7d7cd885 | 109 | { |
7f407710 | 110 | unsigned long c; |
7d7cd885 VM |
111 | size_t shift, size, used = 0; |
112 | ||
eae0bfdc PP |
113 | if (len == 0) |
114 | goto on_error; | |
7d7cd885 VM |
115 | |
116 | c = data[used++]; | |
eae0bfdc | 117 | out->type = (c >> 4) & 7; |
7d7cd885 VM |
118 | |
119 | size = c & 15; | |
120 | shift = 4; | |
121 | while (c & 0x80) { | |
eae0bfdc PP |
122 | if (len <= used) |
123 | goto on_error; | |
124 | ||
7d7cd885 | 125 | if (sizeof(size_t) * 8 <= shift) |
eae0bfdc PP |
126 | goto on_error; |
127 | ||
7d7cd885 VM |
128 | c = data[used++]; |
129 | size += (c & 0x7f) << shift; | |
130 | shift += 7; | |
131 | } | |
7d7cd885 | 132 | |
eae0bfdc | 133 | out->size = size; |
7d7cd885 | 134 | |
eae0bfdc PP |
135 | if (out_len) |
136 | *out_len = used; | |
7d7cd885 | 137 | |
eae0bfdc | 138 | return 0; |
7d7cd885 | 139 | |
eae0bfdc | 140 | on_error: |
ac3d33df | 141 | git_error_set(GIT_ERROR_OBJECT, "failed to parse loose object: invalid header"); |
eae0bfdc | 142 | return -1; |
7d7cd885 VM |
143 | } |
144 | ||
eae0bfdc PP |
145 | static int parse_header( |
146 | obj_hdr *out, | |
147 | size_t *out_len, | |
148 | const unsigned char *_data, | |
149 | size_t data_len) | |
7d7cd885 | 150 | { |
eae0bfdc PP |
151 | const char *data = (char *)_data; |
152 | size_t i, typename_len, size_idx, size_len; | |
153 | int64_t size; | |
7d7cd885 | 154 | |
eae0bfdc | 155 | *out_len = 0; |
7d7cd885 | 156 | |
eae0bfdc PP |
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 | } | |
7d7cd885 | 162 | |
eae0bfdc PP |
163 | if (typename_len == data_len) |
164 | goto on_error; | |
7d7cd885 | 165 | |
eae0bfdc | 166 | out->type = git_object_stringn2type(data, typename_len); |
1dc89aab | 167 | |
eae0bfdc PP |
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 | } | |
7d7cd885 | 173 | |
eae0bfdc PP |
174 | if (i == data_len) |
175 | goto on_error; | |
7d7cd885 | 176 | |
eae0bfdc PP |
177 | if (git__strntol64(&size, &data[size_idx], size_len, NULL, 10) < 0 || |
178 | size < 0) | |
179 | goto on_error; | |
7d7cd885 | 180 | |
eae0bfdc | 181 | if ((uint64_t)size > SIZE_MAX) { |
ac3d33df | 182 | git_error_set(GIT_ERROR_OBJECT, "object is larger than available memory"); |
e1de726c RB |
183 | return -1; |
184 | } | |
7d7cd885 | 185 | |
ac3d33df | 186 | out->size = (size_t)size; |
eae0bfdc PP |
187 | |
188 | if (GIT_ADD_SIZET_OVERFLOW(out_len, i, 1)) | |
189 | goto on_error; | |
190 | ||
e1de726c | 191 | return 0; |
eae0bfdc PP |
192 | |
193 | on_error: | |
ac3d33df | 194 | git_error_set(GIT_ERROR_OBJECT, "failed to parse loose object: invalid header"); |
eae0bfdc | 195 | return -1; |
7d7cd885 VM |
196 | } |
197 | ||
eae0bfdc | 198 | static int is_zlib_compressed_data(unsigned char *data, size_t data_len) |
7d7cd885 | 199 | { |
72a3fe42 | 200 | unsigned int w; |
7d7cd885 | 201 | |
eae0bfdc PP |
202 | if (data_len < 2) |
203 | return 0; | |
204 | ||
72a3fe42 | 205 | w = ((unsigned int)(data[0]) << 8) + data[1]; |
c51065e3 | 206 | return (data[0] & 0x8F) == 0x08 && !(w % 31); |
7d7cd885 VM |
207 | } |
208 | ||
eae0bfdc PP |
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 | ***********************************************************/ | |
7d7cd885 | 217 | |
7d7cd885 VM |
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 | */ | |
e579e0f7 | 225 | static int read_loose_packlike(git_rawobj *out, git_str *obj) |
7d7cd885 | 226 | { |
e579e0f7 | 227 | git_str body = GIT_STR_INIT; |
eae0bfdc | 228 | const unsigned char *obj_data; |
7d7cd885 | 229 | obj_hdr hdr; |
eae0bfdc PP |
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; | |
7d7cd885 VM |
235 | |
236 | /* | |
237 | * read the object header, which is an (uncompressed) | |
238 | * binary encoding of the object type and size. | |
239 | */ | |
eae0bfdc PP |
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) { | |
ac3d33df | 244 | git_error_set(GIT_ERROR_ODB, "failed to inflate loose object"); |
eae0bfdc PP |
245 | error = -1; |
246 | goto done; | |
e1de726c | 247 | } |
7d7cd885 | 248 | |
eae0bfdc PP |
249 | obj_data += head_len; |
250 | obj_len -= head_len; | |
251 | ||
7d7cd885 VM |
252 | /* |
253 | * allocate a buffer and inflate the data into it | |
254 | */ | |
eae0bfdc | 255 | if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr.size, 1) || |
e579e0f7 | 256 | git_str_init(&body, alloc_size) < 0) { |
eae0bfdc PP |
257 | error = -1; |
258 | goto done; | |
7d7cd885 | 259 | } |
7d7cd885 | 260 | |
eae0bfdc PP |
261 | if ((error = git_zstream_inflatebuf(&body, obj_data, obj_len)) < 0) |
262 | goto done; | |
263 | ||
87d9869f | 264 | out->len = hdr.size; |
7d7cd885 | 265 | out->type = hdr.type; |
e579e0f7 | 266 | out->data = git_str_detach(&body); |
7d7cd885 | 267 | |
eae0bfdc | 268 | done: |
e579e0f7 | 269 | git_str_dispose(&body); |
eae0bfdc | 270 | return error; |
7d7cd885 VM |
271 | } |
272 | ||
e579e0f7 | 273 | static int read_loose_standard(git_rawobj *out, git_str *obj) |
7d7cd885 | 274 | { |
eae0bfdc PP |
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; | |
7d7cd885 | 278 | obj_hdr hdr; |
eae0bfdc | 279 | int error; |
7d7cd885 | 280 | |
eae0bfdc | 281 | if ((error = git_zstream_init(&zstream, GIT_ZSTREAM_INFLATE)) < 0 || |
e579e0f7 | 282 | (error = git_zstream_set_input(&zstream, git_str_cstr(obj), git_str_len(obj))) < 0) |
eae0bfdc PP |
283 | goto done; |
284 | ||
285 | decompressed = sizeof(head); | |
7d7cd885 VM |
286 | |
287 | /* | |
eae0bfdc PP |
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. | |
7d7cd885 | 291 | */ |
eae0bfdc PP |
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)) { | |
ac3d33df | 297 | git_error_set(GIT_ERROR_ODB, "failed to inflate disk object"); |
eae0bfdc PP |
298 | error = -1; |
299 | goto done; | |
e1de726c | 300 | } |
7d7cd885 VM |
301 | |
302 | /* | |
303 | * allocate a buffer and inflate the object data into it | |
304 | * (including the initial sequence in the head buffer). | |
305 | */ | |
eae0bfdc | 306 | if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr.size, 1) || |
22a2d3d5 | 307 | (body = git__calloc(1, alloc_size)) == NULL) { |
eae0bfdc PP |
308 | error = -1; |
309 | goto done; | |
310 | } | |
7d7cd885 | 311 | |
c25aa7cd | 312 | GIT_ASSERT(decompressed >= head_len); |
eae0bfdc | 313 | body_len = decompressed - head_len; |
7d7cd885 | 314 | |
eae0bfdc PP |
315 | if (body_len) |
316 | memcpy(body, head + head_len, body_len); | |
7d7cd885 | 317 | |
eae0bfdc PP |
318 | decompressed = hdr.size - body_len; |
319 | if ((error = git_zstream_get_output(body + body_len, &decompressed, &zstream)) < 0) | |
320 | goto done; | |
7d7cd885 | 321 | |
eae0bfdc | 322 | if (!git_zstream_done(&zstream)) { |
ac3d33df | 323 | git_error_set(GIT_ERROR_ZLIB, "failed to finish zlib inflation: stream aborted prematurely"); |
eae0bfdc PP |
324 | error = -1; |
325 | goto done; | |
326 | } | |
7d7cd885 | 327 | |
eae0bfdc | 328 | body[hdr.size] = '\0'; |
7d7cd885 | 329 | |
eae0bfdc PP |
330 | out->data = body; |
331 | out->len = hdr.size; | |
332 | out->type = hdr.type; | |
7d7cd885 | 333 | |
eae0bfdc PP |
334 | done: |
335 | if (error < 0) | |
336 | git__free(body); | |
7d7cd885 | 337 | |
eae0bfdc PP |
338 | git_zstream_free(&zstream); |
339 | return error; | |
340 | } | |
7d7cd885 | 341 | |
e579e0f7 | 342 | static int read_loose(git_rawobj *out, git_str *loc) |
7d7cd885 VM |
343 | { |
344 | int error; | |
e579e0f7 | 345 | git_str obj = GIT_STR_INIT; |
7d7cd885 | 346 | |
c25aa7cd PP |
347 | GIT_ASSERT_ARG(out); |
348 | GIT_ASSERT_ARG(loc); | |
7d7cd885 | 349 | |
e579e0f7 | 350 | if (git_str_oom(loc)) |
e1de726c | 351 | return -1; |
97769280 | 352 | |
7d7cd885 | 353 | out->data = NULL; |
87d9869f | 354 | out->len = 0; |
ac3d33df | 355 | out->type = GIT_OBJECT_INVALID; |
7d7cd885 | 356 | |
eae0bfdc PP |
357 | if ((error = git_futils_readbuffer(&obj, loc->ptr)) < 0) |
358 | goto done; | |
7d7cd885 | 359 | |
eae0bfdc PP |
360 | if (!is_zlib_compressed_data((unsigned char *)obj.ptr, obj.size)) |
361 | error = read_loose_packlike(out, &obj); | |
362 | else | |
363 | error = read_loose_standard(out, &obj); | |
364 | ||
365 | done: | |
e579e0f7 | 366 | git_str_dispose(&obj); |
eae0bfdc PP |
367 | return error; |
368 | } | |
369 | ||
370 | static int read_header_loose_packlike( | |
371 | git_rawobj *out, const unsigned char *data, size_t len) | |
372 | { | |
373 | obj_hdr hdr; | |
374 | size_t header_len; | |
375 | int error; | |
376 | ||
377 | if ((error = parse_header_packlike(&hdr, &header_len, data, len)) < 0) | |
378 | return error; | |
379 | ||
380 | out->len = hdr.size; | |
381 | out->type = hdr.type; | |
382 | ||
383 | return error; | |
384 | } | |
385 | ||
386 | static int read_header_loose_standard( | |
387 | git_rawobj *out, const unsigned char *data, size_t len) | |
388 | { | |
389 | git_zstream zs = GIT_ZSTREAM_INIT; | |
22a2d3d5 UG |
390 | obj_hdr hdr = {0}; |
391 | unsigned char inflated[MAX_HEADER_LEN] = {0}; | |
eae0bfdc PP |
392 | size_t header_len, inflated_len = sizeof(inflated); |
393 | int error; | |
7d7cd885 | 394 | |
eae0bfdc PP |
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) | |
399 | goto done; | |
400 | ||
401 | out->len = hdr.size; | |
402 | out->type = hdr.type; | |
403 | ||
404 | done: | |
405 | git_zstream_free(&zs); | |
e1de726c | 406 | return error; |
7d7cd885 VM |
407 | } |
408 | ||
e579e0f7 | 409 | static int read_header_loose(git_rawobj *out, git_str *loc) |
7d7cd885 | 410 | { |
eae0bfdc | 411 | unsigned char obj[1024]; |
22a2d3d5 UG |
412 | ssize_t obj_len; |
413 | int fd, error; | |
7d7cd885 | 414 | |
c25aa7cd PP |
415 | GIT_ASSERT_ARG(out); |
416 | GIT_ASSERT_ARG(loc); | |
7d7cd885 | 417 | |
e579e0f7 | 418 | if (git_str_oom(loc)) |
e1de726c | 419 | return -1; |
97769280 | 420 | |
7d7cd885 VM |
421 | out->data = NULL; |
422 | ||
22a2d3d5 | 423 | if ((error = fd = git_futils_open_ro(loc->ptr)) < 0) |
eae0bfdc | 424 | goto done; |
7d7cd885 | 425 | |
22a2d3d5 UG |
426 | if ((obj_len = p_read(fd, obj, sizeof(obj))) < 0) { |
427 | error = (int)obj_len; | |
428 | goto done; | |
429 | } | |
430 | ||
eae0bfdc PP |
431 | if (!is_zlib_compressed_data(obj, (size_t)obj_len)) |
432 | error = read_header_loose_packlike(out, obj, (size_t)obj_len); | |
433 | else | |
434 | error = read_header_loose_standard(out, obj, (size_t)obj_len); | |
7d7cd885 | 435 | |
eae0bfdc | 436 | if (!error && !git_object_typeisloose(out->type)) { |
ac3d33df | 437 | git_error_set(GIT_ERROR_ZLIB, "failed to read loose object header"); |
e1de726c | 438 | error = -1; |
eae0bfdc | 439 | goto done; |
7d7cd885 VM |
440 | } |
441 | ||
eae0bfdc PP |
442 | done: |
443 | if (fd >= 0) | |
444 | p_close(fd); | |
e1de726c | 445 | return error; |
7d7cd885 VM |
446 | } |
447 | ||
97769280 | 448 | static int locate_object( |
e579e0f7 | 449 | git_str *object_location, |
97769280 RB |
450 | loose_backend *backend, |
451 | const git_oid *oid) | |
7d7cd885 | 452 | { |
8294e8cf | 453 | int error = object_file_name(object_location, backend, oid); |
97769280 | 454 | |
e579e0f7 | 455 | if (!error && !git_fs_path_exists(object_location->ptr)) |
e1de726c | 456 | return GIT_ENOTFOUND; |
97769280 RB |
457 | |
458 | return error; | |
7d7cd885 VM |
459 | } |
460 | ||
aea8a638 | 461 | /* Explore an entry of a directory and see if it matches a short oid */ |
e579e0f7 | 462 | static int fn_locate_object_short_oid(void *state, git_str *pathbuf) { |
aea8a638 MP |
463 | loose_locate_object_state *sstate = (loose_locate_object_state *)state; |
464 | ||
e579e0f7 | 465 | if (git_str_len(pathbuf) - sstate->dir_len != GIT_OID_HEXSZ - 2) { |
aea8a638 | 466 | /* Entry cannot be an object. Continue to next entry */ |
e1de726c | 467 | return 0; |
aea8a638 MP |
468 | } |
469 | ||
e579e0f7 | 470 | if (git_fs_path_isdir(pathbuf->ptr) == false) { |
f1d01851 VM |
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, | |
97769280 | 474 | (unsigned char *)pathbuf->ptr + sstate->dir_len, |
f1d01851 VM |
475 | sstate->short_oid_len - 2)) { |
476 | ||
aea8a638 MP |
477 | if (!sstate->found) { |
478 | sstate->res_oid[0] = sstate->short_oid[0]; | |
479 | sstate->res_oid[1] = sstate->short_oid[1]; | |
97769280 | 480 | memcpy(sstate->res_oid+2, pathbuf->ptr+sstate->dir_len, GIT_OID_HEXSZ-2); |
aea8a638 MP |
481 | } |
482 | sstate->found++; | |
483 | } | |
484 | } | |
e1de726c | 485 | |
d0323a5f | 486 | if (sstate->found > 1) |
209f9b67 | 487 | return GIT_EAMBIGUOUS; |
d0323a5f | 488 | |
e1de726c | 489 | return 0; |
aea8a638 MP |
490 | } |
491 | ||
492 | /* Locate an object matching a given short oid */ | |
97769280 | 493 | static int locate_object_short_oid( |
e579e0f7 | 494 | git_str *object_location, |
97769280 RB |
495 | git_oid *res_oid, |
496 | loose_backend *backend, | |
497 | const git_oid *short_oid, | |
b8457baa | 498 | size_t len) |
aea8a638 MP |
499 | { |
500 | char *objects_dir = backend->objects_dir; | |
f1453c59 | 501 | size_t dir_len = strlen(objects_dir), alloc_len; |
aea8a638 MP |
502 | loose_locate_object_state state; |
503 | int error; | |
504 | ||
f5753999 | 505 | /* prealloc memory for OBJ_DIR/xx/xx..38x..xx */ |
ac3d33df JK |
506 | GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ); |
507 | GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 3); | |
e579e0f7 | 508 | if (git_str_grow(object_location, alloc_len) < 0) |
e1de726c | 509 | return -1; |
aea8a638 | 510 | |
e579e0f7 MB |
511 | git_str_set(object_location, objects_dir, dir_len); |
512 | git_fs_path_to_dir(object_location); | |
aea8a638 | 513 | |
97769280 | 514 | /* save adjusted position at end of dir so it can be restored later */ |
e579e0f7 | 515 | dir_len = git_str_len(object_location); |
aea8a638 MP |
516 | |
517 | /* Convert raw oid to hex formatted oid */ | |
d0323a5f | 518 | git_oid_fmt((char *)state.short_oid, short_oid); |
97769280 | 519 | |
aea8a638 | 520 | /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */ |
e579e0f7 | 521 | if (git_str_put(object_location, (char *)state.short_oid, 3) < 0) |
e1de726c | 522 | return -1; |
f5753999 | 523 | object_location->ptr[object_location->size - 1] = '/'; |
aea8a638 MP |
524 | |
525 | /* Check that directory exists */ | |
e579e0f7 | 526 | if (git_fs_path_isdir(object_location->ptr) == false) |
e10144ae ET |
527 | return git_odb__error_notfound("no matching loose object for prefix", |
528 | short_oid, len); | |
aea8a638 | 529 | |
e579e0f7 | 530 | state.dir_len = git_str_len(object_location); |
aea8a638 MP |
531 | state.short_oid_len = len; |
532 | state.found = 0; | |
97769280 | 533 | |
aea8a638 | 534 | /* Explore directory to find a unique object matching short_oid */ |
e579e0f7 | 535 | error = git_fs_path_direach( |
219d3457 | 536 | object_location, 0, fn_locate_object_short_oid, &state); |
25e0b157 | 537 | if (error < 0 && error != GIT_EAMBIGUOUS) |
e1de726c | 538 | return error; |
97769280 | 539 | |
e1de726c | 540 | if (!state.found) |
e10144ae ET |
541 | return git_odb__error_notfound("no matching loose object for prefix", |
542 | short_oid, len); | |
aea8a638 | 543 | |
209f9b67 | 544 | if (state.found > 1) |
545 | return git_odb__error_ambiguous("multiple matches in loose objects"); | |
546 | ||
aea8a638 | 547 | /* Convert obtained hex formatted oid to raw */ |
fa48608e | 548 | error = git_oid_fromstr(res_oid, (char *)state.res_oid); |
e1de726c RB |
549 | if (error) |
550 | return error; | |
aea8a638 MP |
551 | |
552 | /* Update the location according to the oid obtained */ | |
ac3d33df JK |
553 | GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, dir_len, GIT_OID_HEXSZ); |
554 | GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2); | |
97769280 | 555 | |
e579e0f7 MB |
556 | git_str_truncate(object_location, dir_len); |
557 | if (git_str_grow(object_location, alloc_len) < 0) | |
e1de726c | 558 | return -1; |
97769280 RB |
559 | |
560 | git_oid_pathfmt(object_location->ptr + dir_len, res_oid); | |
561 | ||
562 | object_location->size += GIT_OID_HEXSZ + 1; | |
563 | object_location->ptr[object_location->size] = '\0'; | |
aea8a638 | 564 | |
e1de726c | 565 | return 0; |
aea8a638 MP |
566 | } |
567 | ||
7d7cd885 VM |
568 | |
569 | ||
570 | ||
571 | ||
572 | ||
573 | ||
574 | ||
575 | ||
576 | /*********************************************************** | |
577 | * | |
578 | * LOOSE BACKEND PUBLIC API | |
579 | * | |
580 | * Implement the git_odb_backend API calls | |
581 | * | |
582 | ***********************************************************/ | |
583 | ||
ac3d33df | 584 | static int loose_backend__read_header(size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *oid) |
7d7cd885 | 585 | { |
e579e0f7 | 586 | git_str object_path = GIT_STR_INIT; |
72a3fe42 | 587 | git_rawobj raw; |
0d0fa7c3 | 588 | int error; |
7d7cd885 | 589 | |
c25aa7cd PP |
590 | GIT_ASSERT_ARG(backend); |
591 | GIT_ASSERT_ARG(oid); | |
7d7cd885 | 592 | |
60e1b49a | 593 | raw.len = 0; |
ac3d33df | 594 | raw.type = GIT_OBJECT_INVALID; |
60e1b49a | 595 | |
e10144ae ET |
596 | if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) { |
597 | error = git_odb__error_notfound("no matching loose object", | |
598 | oid, GIT_OID_HEXSZ); | |
599 | } else if ((error = read_header_loose(&raw, &object_path)) == 0) { | |
97769280 RB |
600 | *len_p = raw.len; |
601 | *type_p = raw.type; | |
602 | } | |
7d7cd885 | 603 | |
e579e0f7 | 604 | git_str_dispose(&object_path); |
7d7cd885 | 605 | |
97769280 | 606 | return error; |
72a3fe42 | 607 | } |
7d7cd885 | 608 | |
ac3d33df | 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) |
7d7cd885 | 610 | { |
e579e0f7 | 611 | git_str object_path = GIT_STR_INIT; |
72a3fe42 | 612 | git_rawobj raw; |
e1de726c | 613 | int error = 0; |
7d7cd885 | 614 | |
c25aa7cd PP |
615 | GIT_ASSERT_ARG(backend); |
616 | GIT_ASSERT_ARG(oid); | |
7d7cd885 | 617 | |
e10144ae ET |
618 | if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) { |
619 | error = git_odb__error_notfound("no matching loose object", | |
620 | oid, GIT_OID_HEXSZ); | |
621 | } else if ((error = read_loose(&raw, &object_path)) == 0) { | |
97769280 RB |
622 | *buffer_p = raw.data; |
623 | *len_p = raw.len; | |
624 | *type_p = raw.type; | |
625 | } | |
72a3fe42 | 626 | |
e579e0f7 | 627 | git_str_dispose(&object_path); |
72a3fe42 | 628 | |
97769280 | 629 | return error; |
7d7cd885 VM |
630 | } |
631 | ||
d568d585 | 632 | static int loose_backend__read_prefix( |
d0323a5f VM |
633 | git_oid *out_oid, |
634 | void **buffer_p, | |
635 | size_t *len_p, | |
ac3d33df | 636 | git_object_t *type_p, |
d0323a5f VM |
637 | git_odb_backend *backend, |
638 | const git_oid *short_oid, | |
b8457baa | 639 | size_t len) |
ecd6fdf1 | 640 | { |
e1de726c | 641 | int error = 0; |
97769280 | 642 | |
c25aa7cd | 643 | GIT_ASSERT_ARG(len >= GIT_OID_MINPREFIXLEN && len <= GIT_OID_HEXSZ); |
d19dd9cf | 644 | |
26875825 | 645 | if (len == GIT_OID_HEXSZ) { |
aea8a638 | 646 | /* We can fall back to regular read method */ |
97769280 | 647 | error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid); |
e1de726c | 648 | if (!error) |
ecd6fdf1 | 649 | git_oid_cpy(out_oid, short_oid); |
aea8a638 | 650 | } else { |
e579e0f7 | 651 | git_str object_path = GIT_STR_INIT; |
aea8a638 | 652 | git_rawobj raw; |
aea8a638 | 653 | |
c25aa7cd | 654 | GIT_ASSERT_ARG(backend && short_oid); |
aea8a638 | 655 | |
97769280 | 656 | if ((error = locate_object_short_oid(&object_path, out_oid, |
e1de726c RB |
657 | (loose_backend *)backend, short_oid, len)) == 0 && |
658 | (error = read_loose(&raw, &object_path)) == 0) | |
659 | { | |
97769280 RB |
660 | *buffer_p = raw.data; |
661 | *len_p = raw.len; | |
662 | *type_p = raw.type; | |
aea8a638 MP |
663 | } |
664 | ||
e579e0f7 | 665 | git_str_dispose(&object_path); |
ecd6fdf1 | 666 | } |
aea8a638 | 667 | |
97769280 | 668 | return error; |
ecd6fdf1 MP |
669 | } |
670 | ||
d568d585 | 671 | static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid) |
7d7cd885 | 672 | { |
e579e0f7 | 673 | git_str object_path = GIT_STR_INIT; |
97769280 | 674 | int error; |
7d7cd885 | 675 | |
c25aa7cd PP |
676 | GIT_ASSERT_ARG(backend); |
677 | GIT_ASSERT_ARG(oid); | |
7d7cd885 | 678 | |
97769280 RB |
679 | error = locate_object(&object_path, (loose_backend *)backend, oid); |
680 | ||
e579e0f7 | 681 | git_str_dispose(&object_path); |
97769280 | 682 | |
e1de726c | 683 | return !error; |
7d7cd885 VM |
684 | } |
685 | ||
f5753999 RB |
686 | static int loose_backend__exists_prefix( |
687 | git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len) | |
688 | { | |
e579e0f7 | 689 | git_str object_path = GIT_STR_INIT; |
f5753999 RB |
690 | int error; |
691 | ||
c25aa7cd PP |
692 | GIT_ASSERT_ARG(backend); |
693 | GIT_ASSERT_ARG(out); | |
694 | GIT_ASSERT_ARG(short_id); | |
695 | GIT_ASSERT_ARG(len >= GIT_OID_MINPREFIXLEN); | |
f5753999 RB |
696 | |
697 | error = locate_object_short_oid( | |
698 | &object_path, out, (loose_backend *)backend, short_id, len); | |
699 | ||
e579e0f7 | 700 | git_str_dispose(&object_path); |
f5753999 RB |
701 | |
702 | return error; | |
703 | } | |
704 | ||
521aedad CMN |
705 | struct foreach_state { |
706 | size_t dir_len; | |
c3fb7d04 | 707 | git_odb_foreach_cb cb; |
521aedad CMN |
708 | void *data; |
709 | }; | |
710 | ||
b7158c53 | 711 | GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr) |
521aedad CMN |
712 | { |
713 | int v, i = 0; | |
3b2cb2c9 | 714 | if (strlen(ptr) != GIT_OID_HEXSZ+1) |
521aedad CMN |
715 | return -1; |
716 | ||
717 | if (ptr[2] != '/') { | |
718 | return -1; | |
719 | } | |
720 | ||
721 | v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i+1]); | |
722 | if (v < 0) | |
723 | return -1; | |
724 | ||
725 | oid->id[0] = (unsigned char) v; | |
726 | ||
727 | ptr += 3; | |
728 | for (i = 0; i < 38; i += 2) { | |
729 | v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i + 1]); | |
730 | if (v < 0) | |
731 | return -1; | |
732 | ||
733 | oid->id[1 + i/2] = (unsigned char) v; | |
734 | } | |
735 | ||
736 | return 0; | |
737 | } | |
738 | ||
e579e0f7 | 739 | static int foreach_object_dir_cb(void *_state, git_str *path) |
521aedad CMN |
740 | { |
741 | git_oid oid; | |
742 | struct foreach_state *state = (struct foreach_state *) _state; | |
743 | ||
744 | if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0) | |
745 | return 0; | |
746 | ||
ac3d33df | 747 | return git_error_set_after_callback_function( |
25e0b157 | 748 | state->cb(&oid, state->data), "git_odb_foreach"); |
521aedad CMN |
749 | } |
750 | ||
e579e0f7 | 751 | static int foreach_cb(void *_state, git_str *path) |
521aedad CMN |
752 | { |
753 | struct foreach_state *state = (struct foreach_state *) _state; | |
754 | ||
ee311907 | 755 | /* non-dir is some stray file, ignore it */ |
e579e0f7 | 756 | if (!git_fs_path_isdir(git_str_cstr(path))) |
ee311907 CMN |
757 | return 0; |
758 | ||
e579e0f7 | 759 | return git_fs_path_direach(path, 0, foreach_object_dir_cb, state); |
521aedad CMN |
760 | } |
761 | ||
c3fb7d04 | 762 | static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data) |
521aedad CMN |
763 | { |
764 | char *objects_dir; | |
765 | int error; | |
e579e0f7 | 766 | git_str buf = GIT_STR_INIT; |
521aedad CMN |
767 | struct foreach_state state; |
768 | loose_backend *backend = (loose_backend *) _backend; | |
769 | ||
c25aa7cd PP |
770 | GIT_ASSERT_ARG(backend); |
771 | GIT_ASSERT_ARG(cb); | |
521aedad CMN |
772 | |
773 | objects_dir = backend->objects_dir; | |
774 | ||
e579e0f7 MB |
775 | git_str_sets(&buf, objects_dir); |
776 | git_fs_path_to_dir(&buf); | |
777 | if (git_str_oom(&buf)) | |
25e0b157 | 778 | return -1; |
521aedad | 779 | |
5dca2010 | 780 | memset(&state, 0, sizeof(state)); |
521aedad CMN |
781 | state.cb = cb; |
782 | state.data = data; | |
e579e0f7 | 783 | state.dir_len = git_str_len(&buf); |
521aedad | 784 | |
e579e0f7 | 785 | error = git_fs_path_direach(&buf, 0, foreach_cb, &state); |
5dca2010 | 786 | |
e579e0f7 | 787 | git_str_dispose(&buf); |
521aedad | 788 | |
96869a4e | 789 | return error; |
521aedad CMN |
790 | } |
791 | ||
eae0bfdc | 792 | static int loose_backend__writestream_finalize(git_odb_stream *_stream, const git_oid *oid) |
72a3fe42 VM |
793 | { |
794 | loose_writestream *stream = (loose_writestream *)_stream; | |
795 | loose_backend *backend = (loose_backend *)_stream->backend; | |
e579e0f7 | 796 | git_str final_path = GIT_STR_INIT; |
e1de726c | 797 | int error = 0; |
72a3fe42 | 798 | |
8380b39a | 799 | if (object_file_name(&final_path, backend, oid) < 0 || |
8294e8cf | 800 | object_mkdir(&final_path, backend) < 0) |
e1de726c | 801 | error = -1; |
e1de726c RB |
802 | else |
803 | error = git_filebuf_commit_at( | |
1d3a8aeb | 804 | &stream->fbuf, final_path.ptr); |
97769280 | 805 | |
e579e0f7 | 806 | git_str_dispose(&final_path); |
97769280 | 807 | |
97769280 | 808 | return error; |
72a3fe42 VM |
809 | } |
810 | ||
eae0bfdc | 811 | static int loose_backend__writestream_write(git_odb_stream *_stream, const char *data, size_t len) |
7d7cd885 | 812 | { |
72a3fe42 VM |
813 | loose_writestream *stream = (loose_writestream *)_stream; |
814 | return git_filebuf_write(&stream->fbuf, data, len); | |
815 | } | |
816 | ||
eae0bfdc | 817 | static void loose_backend__writestream_free(git_odb_stream *_stream) |
72a3fe42 VM |
818 | { |
819 | loose_writestream *stream = (loose_writestream *)_stream; | |
820 | ||
97769280 | 821 | git_filebuf_cleanup(&stream->fbuf); |
3286c408 | 822 | git__free(stream); |
72a3fe42 VM |
823 | } |
824 | ||
fc27fe21 ET |
825 | static int filebuf_flags(loose_backend *backend) |
826 | { | |
827 | int flags = GIT_FILEBUF_TEMPORARY | | |
828 | (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT); | |
829 | ||
6c23704d | 830 | if (backend->fsync_object_files || git_repository__fsync_gitdir) |
fc27fe21 ET |
831 | flags |= GIT_FILEBUF_FSYNC; |
832 | ||
833 | return flags; | |
834 | } | |
835 | ||
22a2d3d5 | 836 | static int loose_backend__writestream(git_odb_stream **stream_out, git_odb_backend *_backend, git_object_size_t length, git_object_t type) |
72a3fe42 VM |
837 | { |
838 | loose_backend *backend; | |
e1de726c | 839 | loose_writestream *stream = NULL; |
eae0bfdc | 840 | char hdr[MAX_HEADER_LEN]; |
e579e0f7 | 841 | git_str tmp_path = GIT_STR_INIT; |
eae0bfdc PP |
842 | size_t hdrlen; |
843 | int error; | |
7d7cd885 | 844 | |
c25aa7cd | 845 | GIT_ASSERT_ARG(_backend); |
7d7cd885 VM |
846 | |
847 | backend = (loose_backend *)_backend; | |
72a3fe42 | 848 | *stream_out = NULL; |
7d7cd885 | 849 | |
eae0bfdc PP |
850 | if ((error = git_odb__format_object_header(&hdrlen, |
851 | hdr, sizeof(hdr), length, type)) < 0) | |
852 | return error; | |
72a3fe42 VM |
853 | |
854 | stream = git__calloc(1, sizeof(loose_writestream)); | |
ac3d33df | 855 | GIT_ERROR_CHECK_ALLOC(stream); |
7d7cd885 | 856 | |
72a3fe42 VM |
857 | stream->stream.backend = _backend; |
858 | stream->stream.read = NULL; /* read only */ | |
eae0bfdc PP |
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; | |
72a3fe42 | 862 | stream->stream.mode = GIT_STREAM_WRONLY; |
7d7cd885 | 863 | |
e579e0f7 | 864 | if (git_str_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 || |
fc27fe21 | 865 | git_filebuf_open(&stream->fbuf, tmp_path.ptr, filebuf_flags(backend), |
1d3a8aeb | 866 | backend->object_file_mode) < 0 || |
e1de726c RB |
867 | stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0) |
868 | { | |
869 | git_filebuf_cleanup(&stream->fbuf); | |
870 | git__free(stream); | |
871 | stream = NULL; | |
872 | } | |
e579e0f7 | 873 | git_str_dispose(&tmp_path); |
72a3fe42 | 874 | *stream_out = (git_odb_stream *)stream; |
97769280 | 875 | |
e1de726c | 876 | return !stream ? -1 : 0; |
7d7cd885 VM |
877 | } |
878 | ||
eae0bfdc PP |
879 | static int loose_backend__readstream_read( |
880 | git_odb_stream *_stream, | |
881 | char *buffer, | |
882 | size_t buffer_len) | |
883 | { | |
884 | loose_readstream *stream = (loose_readstream *)_stream; | |
885 | size_t start_remain = stream->start_len - stream->start_read; | |
886 | int total = 0, error; | |
887 | ||
22a2d3d5 UG |
888 | buffer_len = min(buffer_len, INT_MAX); |
889 | ||
eae0bfdc PP |
890 | /* |
891 | * if we read more than just the header in the initial read, play | |
892 | * that back for the caller. | |
893 | */ | |
894 | if (start_remain && buffer_len) { | |
895 | size_t chunk = min(start_remain, buffer_len); | |
896 | memcpy(buffer, stream->start + stream->start_read, chunk); | |
897 | ||
898 | buffer += chunk; | |
899 | stream->start_read += chunk; | |
900 | ||
22a2d3d5 | 901 | total += (int)chunk; |
eae0bfdc PP |
902 | buffer_len -= chunk; |
903 | } | |
904 | ||
905 | if (buffer_len) { | |
22a2d3d5 | 906 | size_t chunk = buffer_len; |
eae0bfdc PP |
907 | |
908 | if ((error = git_zstream_get_output(buffer, &chunk, &stream->zstream)) < 0) | |
909 | return error; | |
910 | ||
22a2d3d5 | 911 | total += (int)chunk; |
eae0bfdc PP |
912 | } |
913 | ||
22a2d3d5 | 914 | return (int)total; |
eae0bfdc PP |
915 | } |
916 | ||
917 | static void loose_backend__readstream_free(git_odb_stream *_stream) | |
918 | { | |
919 | loose_readstream *stream = (loose_readstream *)_stream; | |
920 | ||
921 | git_futils_mmap_free(&stream->map); | |
922 | git_zstream_free(&stream->zstream); | |
923 | git__free(stream); | |
924 | } | |
925 | ||
926 | static int loose_backend__readstream_packlike( | |
927 | obj_hdr *hdr, | |
928 | loose_readstream *stream) | |
929 | { | |
930 | const unsigned char *data; | |
931 | size_t data_len, head_len; | |
932 | int error; | |
933 | ||
934 | data = stream->map.data; | |
935 | data_len = stream->map.len; | |
936 | ||
937 | /* | |
938 | * read the object header, which is an (uncompressed) | |
939 | * binary encoding of the object type and size. | |
940 | */ | |
941 | if ((error = parse_header_packlike(hdr, &head_len, data, data_len)) < 0) | |
942 | return error; | |
943 | ||
944 | if (!git_object_typeisloose(hdr->type)) { | |
ac3d33df | 945 | git_error_set(GIT_ERROR_ODB, "failed to inflate loose object"); |
eae0bfdc PP |
946 | return -1; |
947 | } | |
948 | ||
949 | return git_zstream_set_input(&stream->zstream, | |
950 | data + head_len, data_len - head_len); | |
951 | } | |
952 | ||
953 | static int loose_backend__readstream_standard( | |
954 | obj_hdr *hdr, | |
955 | loose_readstream *stream) | |
956 | { | |
957 | unsigned char head[MAX_HEADER_LEN]; | |
958 | size_t init, head_len; | |
959 | int error; | |
960 | ||
961 | if ((error = git_zstream_set_input(&stream->zstream, | |
962 | stream->map.data, stream->map.len)) < 0) | |
963 | return error; | |
964 | ||
965 | init = sizeof(head); | |
966 | ||
967 | /* | |
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. | |
971 | */ | |
972 | if ((error = git_zstream_get_output(head, &init, &stream->zstream)) < 0 || | |
973 | (error = parse_header(hdr, &head_len, head, init)) < 0) | |
974 | return error; | |
975 | ||
976 | if (!git_object_typeisloose(hdr->type)) { | |
ac3d33df | 977 | git_error_set(GIT_ERROR_ODB, "failed to inflate disk object"); |
eae0bfdc PP |
978 | return -1; |
979 | } | |
980 | ||
981 | if (init > head_len) { | |
982 | stream->start_len = init - head_len; | |
983 | memcpy(stream->start, head + head_len, init - head_len); | |
984 | } | |
985 | ||
986 | return 0; | |
987 | } | |
988 | ||
989 | static int loose_backend__readstream( | |
990 | git_odb_stream **stream_out, | |
991 | size_t *len_out, | |
ac3d33df | 992 | git_object_t *type_out, |
eae0bfdc PP |
993 | git_odb_backend *_backend, |
994 | const git_oid *oid) | |
995 | { | |
996 | loose_backend *backend; | |
997 | loose_readstream *stream = NULL; | |
998 | git_hash_ctx *hash_ctx = NULL; | |
e579e0f7 | 999 | git_str object_path = GIT_STR_INIT; |
eae0bfdc PP |
1000 | obj_hdr hdr; |
1001 | int error = 0; | |
1002 | ||
c25aa7cd PP |
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); | |
eae0bfdc PP |
1008 | |
1009 | backend = (loose_backend *)_backend; | |
1010 | *stream_out = NULL; | |
1011 | *len_out = 0; | |
ac3d33df | 1012 | *type_out = GIT_OBJECT_INVALID; |
eae0bfdc PP |
1013 | |
1014 | if (locate_object(&object_path, backend, oid) < 0) { | |
1015 | error = git_odb__error_notfound("no matching loose object", | |
1016 | oid, GIT_OID_HEXSZ); | |
1017 | goto done; | |
1018 | } | |
1019 | ||
1020 | stream = git__calloc(1, sizeof(loose_readstream)); | |
ac3d33df | 1021 | GIT_ERROR_CHECK_ALLOC(stream); |
eae0bfdc PP |
1022 | |
1023 | hash_ctx = git__malloc(sizeof(git_hash_ctx)); | |
ac3d33df | 1024 | GIT_ERROR_CHECK_ALLOC(hash_ctx); |
eae0bfdc | 1025 | |
e579e0f7 | 1026 | if ((error = git_hash_ctx_init(hash_ctx, GIT_HASH_ALGORITHM_SHA1)) < 0 || |
eae0bfdc PP |
1027 | (error = git_futils_mmap_ro_file(&stream->map, object_path.ptr)) < 0 || |
1028 | (error = git_zstream_init(&stream->zstream, GIT_ZSTREAM_INFLATE)) < 0) | |
1029 | goto done; | |
1030 | ||
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); | |
1034 | else | |
1035 | error = loose_backend__readstream_standard(&hdr, stream); | |
1036 | ||
1037 | if (error < 0) | |
1038 | goto done; | |
1039 | ||
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; | |
1044 | ||
1045 | *stream_out = (git_odb_stream *)stream; | |
1046 | *len_out = hdr.size; | |
1047 | *type_out = hdr.type; | |
1048 | ||
1049 | done: | |
1050 | if (error < 0) { | |
ac3d33df JK |
1051 | if (stream) { |
1052 | git_futils_mmap_free(&stream->map); | |
1053 | git_zstream_free(&stream->zstream); | |
1054 | git__free(stream); | |
1055 | } | |
1056 | if (hash_ctx) { | |
1057 | git_hash_ctx_cleanup(hash_ctx); | |
1058 | git__free(hash_ctx); | |
1059 | } | |
eae0bfdc PP |
1060 | } |
1061 | ||
e579e0f7 | 1062 | git_str_dispose(&object_path); |
eae0bfdc PP |
1063 | return error; |
1064 | } | |
1065 | ||
ac3d33df | 1066 | static int loose_backend__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_object_t type) |
afeecf4f | 1067 | { |
eae0bfdc | 1068 | int error = 0; |
e579e0f7 | 1069 | git_str final_path = GIT_STR_INIT; |
eae0bfdc PP |
1070 | char header[MAX_HEADER_LEN]; |
1071 | size_t header_len; | |
b762e576 | 1072 | git_filebuf fbuf = GIT_FILEBUF_INIT; |
afeecf4f VM |
1073 | loose_backend *backend; |
1074 | ||
1075 | backend = (loose_backend *)_backend; | |
1076 | ||
1077 | /* prepare the header for the file */ | |
eae0bfdc PP |
1078 | if ((error = git_odb__format_object_header(&header_len, |
1079 | header, sizeof(header), len, type)) < 0) | |
1080 | goto cleanup; | |
afeecf4f | 1081 | |
e579e0f7 | 1082 | if (git_str_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 || |
fc27fe21 | 1083 | git_filebuf_open(&fbuf, final_path.ptr, filebuf_flags(backend), |
1d3a8aeb | 1084 | backend->object_file_mode) < 0) |
e1de726c RB |
1085 | { |
1086 | error = -1; | |
97769280 | 1087 | goto cleanup; |
e1de726c | 1088 | } |
afeecf4f VM |
1089 | |
1090 | git_filebuf_write(&fbuf, header, header_len); | |
1091 | git_filebuf_write(&fbuf, data, len); | |
afeecf4f | 1092 | |
8294e8cf RB |
1093 | if (object_file_name(&final_path, backend, oid) < 0 || |
1094 | object_mkdir(&final_path, backend) < 0 || | |
1d3a8aeb | 1095 | git_filebuf_commit_at(&fbuf, final_path.ptr) < 0) |
e1de726c | 1096 | error = -1; |
afeecf4f VM |
1097 | |
1098 | cleanup: | |
0d0fa7c3 | 1099 | if (error < 0) |
97769280 | 1100 | git_filebuf_cleanup(&fbuf); |
e579e0f7 | 1101 | git_str_dispose(&final_path); |
afeecf4f VM |
1102 | return error; |
1103 | } | |
1104 | ||
8f09a98e ET |
1105 | static int loose_backend__freshen( |
1106 | git_odb_backend *_backend, | |
1107 | const git_oid *oid) | |
1108 | { | |
1109 | loose_backend *backend = (loose_backend *)_backend; | |
e579e0f7 | 1110 | git_str path = GIT_STR_INIT; |
8f09a98e ET |
1111 | int error; |
1112 | ||
1113 | if (object_file_name(&path, backend, oid) < 0) | |
1114 | return -1; | |
1115 | ||
27051d4e | 1116 | error = git_futils_touch(path.ptr, NULL); |
e579e0f7 | 1117 | git_str_dispose(&path); |
8f09a98e ET |
1118 | |
1119 | return error; | |
1120 | } | |
1121 | ||
d568d585 | 1122 | static void loose_backend__free(git_odb_backend *_backend) |
7d7cd885 | 1123 | { |
c25aa7cd | 1124 | git__free(_backend); |
7d7cd885 VM |
1125 | } |
1126 | ||
8af4d074 VM |
1127 | int git_odb_backend_loose( |
1128 | git_odb_backend **backend_out, | |
1129 | const char *objects_dir, | |
1130 | int compression_level, | |
dd64c71c | 1131 | int do_fsync, |
f966acd1 ET |
1132 | unsigned int dir_mode, |
1133 | unsigned int file_mode) | |
7d7cd885 VM |
1134 | { |
1135 | loose_backend *backend; | |
f1453c59 | 1136 | size_t objects_dirlen, alloclen; |
8294e8cf | 1137 | |
c25aa7cd PP |
1138 | GIT_ASSERT_ARG(backend_out); |
1139 | GIT_ASSERT_ARG(objects_dir); | |
8294e8cf RB |
1140 | |
1141 | objects_dirlen = strlen(objects_dir); | |
7d7cd885 | 1142 | |
ac3d33df JK |
1143 | GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(loose_backend), objects_dirlen); |
1144 | GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 2); | |
f1453c59 | 1145 | backend = git__calloc(1, alloclen); |
ac3d33df | 1146 | GIT_ERROR_CHECK_ALLOC(backend); |
7d7cd885 | 1147 | |
55f6f21b | 1148 | backend->parent.version = GIT_ODB_BACKEND_VERSION; |
8294e8cf RB |
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++] = '/'; | |
7d7cd885 | 1153 | |
8af4d074 VM |
1154 | if (compression_level < 0) |
1155 | compression_level = Z_BEST_SPEED; | |
1156 | ||
dd64c71c ET |
1157 | if (dir_mode == 0) |
1158 | dir_mode = GIT_OBJECT_DIR_MODE; | |
1159 | ||
1160 | if (file_mode == 0) | |
1161 | file_mode = GIT_OBJECT_FILE_MODE; | |
1162 | ||
8af4d074 VM |
1163 | backend->object_zlib_level = compression_level; |
1164 | backend->fsync_object_files = do_fsync; | |
dd64c71c ET |
1165 | backend->object_dir_mode = dir_mode; |
1166 | backend->object_file_mode = file_mode; | |
7d7cd885 VM |
1167 | |
1168 | backend->parent.read = &loose_backend__read; | |
afeecf4f | 1169 | backend->parent.write = &loose_backend__write; |
d0323a5f | 1170 | backend->parent.read_prefix = &loose_backend__read_prefix; |
7d7cd885 | 1171 | backend->parent.read_header = &loose_backend__read_header; |
eae0bfdc PP |
1172 | backend->parent.writestream = &loose_backend__writestream; |
1173 | backend->parent.readstream = &loose_backend__readstream; | |
7d7cd885 | 1174 | backend->parent.exists = &loose_backend__exists; |
f5753999 | 1175 | backend->parent.exists_prefix = &loose_backend__exists_prefix; |
521aedad | 1176 | backend->parent.foreach = &loose_backend__foreach; |
8f09a98e | 1177 | backend->parent.freshen = &loose_backend__freshen; |
7d7cd885 VM |
1178 | backend->parent.free = &loose_backend__free; |
1179 | ||
7d7cd885 | 1180 | *backend_out = (git_odb_backend *)backend; |
e1de726c | 1181 | return 0; |
7d7cd885 | 1182 | } |