]> git.proxmox.com Git - libgit2.git/blame - src/odb_loose.c
New upstream version 1.4.3+dfsg.1
[libgit2.git] / src / odb_loose.c
CommitLineData
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 27typedef 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
32typedef struct {
33 git_odb_stream stream;
34 git_filebuf fbuf;
72a3fe42
VM
35} loose_writestream;
36
eae0bfdc
PP
37typedef 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
46typedef 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 */
61typedef 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 78static 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 100static 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
107static 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 140on_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
145static 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
193on_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 198static 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 225static 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 268done:
e579e0f7 269 git_str_dispose(&body);
eae0bfdc 270 return error;
7d7cd885
VM
271}
272
e579e0f7 273static 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
334done:
335 if (error < 0)
336 git__free(body);
7d7cd885 337
eae0bfdc
PP
338 git_zstream_free(&zstream);
339 return error;
340}
7d7cd885 341
e579e0f7 342static 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
365done:
e579e0f7 366 git_str_dispose(&obj);
eae0bfdc
PP
367 return error;
368}
369
370static 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
386static 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
404done:
405 git_zstream_free(&zs);
e1de726c 406 return error;
7d7cd885
VM
407}
408
e579e0f7 409static 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
442done:
443 if (fd >= 0)
444 p_close(fd);
e1de726c 445 return error;
7d7cd885
VM
446}
447
97769280 448static 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 462static 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 493static 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 584static 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 609static 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 632static 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 671static 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
686static 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
705struct foreach_state {
706 size_t dir_len;
c3fb7d04 707 git_odb_foreach_cb cb;
521aedad
CMN
708 void *data;
709};
710
b7158c53 711GIT_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 739static 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 751static 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 762static 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 792static 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 811static 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 817static 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
825static 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 836static 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
879static 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
917static 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
926static 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
953static 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
989static 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
1049done:
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 1066static 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
1098cleanup:
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
1105static 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 1122static void loose_backend__free(git_odb_backend *_backend)
7d7cd885 1123{
c25aa7cd 1124 git__free(_backend);
7d7cd885
VM
1125}
1126
8af4d074
VM
1127int 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}