]> git.proxmox.com Git - libgit2.git/blame - src/odb_loose.c
odb: wrap the stream reading and writing functions
[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"
0c3bae62 9#include <zlib.h>
44908fe7 10#include "git2/object.h"
83cc70d9 11#include "git2/sys/odb_backend.h"
7d7cd885
VM
12#include "fileops.h"
13#include "hash.h"
14#include "odb.h"
15#include "delta-apply.h"
72a3fe42 16#include "filebuf.h"
7d7cd885 17
44908fe7 18#include "git2/odb_backend.h"
72a3fe42 19#include "git2/types.h"
7d7cd885 20
87d9869f
VM
21typedef struct { /* object header data */
22 git_otype type; /* object type */
23 size_t size; /* object size */
7d7cd885
VM
24} obj_hdr;
25
72a3fe42
VM
26typedef struct {
27 git_odb_stream stream;
28 git_filebuf fbuf;
72a3fe42
VM
29} loose_writestream;
30
7d7cd885
VM
31typedef struct loose_backend {
32 git_odb_backend parent;
33
34 int object_zlib_level; /** loose object zlib compression level. */
35 int fsync_object_files; /** loose object file fsync flag. */
8294e8cf
RB
36
37 size_t objects_dirlen;
38 char objects_dir[GIT_FLEX_ARRAY];
7d7cd885
VM
39} loose_backend;
40
aea8a638
MP
41/* State structure for exploring directories,
42 * in order to locate objects matching a short oid.
43 */
44typedef struct {
45 size_t dir_len;
46 unsigned char short_oid[GIT_OID_HEXSZ]; /* hex formatted oid to match */
b8457baa 47 size_t short_oid_len;
aea8a638
MP
48 int found; /* number of matching
49 * objects already found */
50 unsigned char res_oid[GIT_OID_HEXSZ]; /* hex formatted oid of
51 * the object found */
52} loose_locate_object_state;
53
54
7d7cd885
VM
55/***********************************************************
56 *
57 * MISCELANEOUS HELPER FUNCTIONS
58 *
59 ***********************************************************/
60
8294e8cf
RB
61static int object_file_name(
62 git_buf *name, const loose_backend *be, const git_oid *id)
7d7cd885 63{
8294e8cf
RB
64 /* expand length for object root + 40 hex sha1 chars + 2 * '/' + '\0' */
65 if (git_buf_grow(name, be->objects_dirlen + GIT_OID_HEXSZ + 3) < 0)
e1de726c 66 return -1;
7d7cd885 67
8294e8cf 68 git_buf_set(name, be->objects_dir, be->objects_dirlen);
97769280 69 git_path_to_dir(name);
7d7cd885
VM
70
71 /* loose object filename: aa/aaa... (41 bytes) */
8294e8cf 72 git_oid_pathfmt(name->ptr + name->size, id);
97769280
RB
73 name->size += GIT_OID_HEXSZ + 1;
74 name->ptr[name->size] = '\0';
7d7cd885 75
e1de726c 76 return 0;
7d7cd885
VM
77}
78
8294e8cf
RB
79static int object_mkdir(const git_buf *name, const loose_backend *be)
80{
81 return git_futils_mkdir(
82 name->ptr + be->objects_dirlen, be->objects_dir, GIT_OBJECT_DIR_MODE,
83 GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR);
84}
7d7cd885 85
13224ea4 86static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj)
7d7cd885
VM
87{
88 unsigned char c;
13224ea4 89 unsigned char *data = (unsigned char *)obj->ptr;
7d7cd885
VM
90 size_t shift, size, used = 0;
91
fa6420f7 92 if (git_buf_len(obj) == 0)
7d7cd885
VM
93 return 0;
94
95 c = data[used++];
96 hdr->type = (c >> 4) & 7;
97
98 size = c & 15;
99 shift = 4;
100 while (c & 0x80) {
fa6420f7 101 if (git_buf_len(obj) <= used)
7d7cd885
VM
102 return 0;
103 if (sizeof(size_t) * 8 <= shift)
104 return 0;
105 c = data[used++];
106 size += (c & 0x7f) << shift;
107 shift += 7;
108 }
109 hdr->size = size;
110
111 return used;
112}
113
114static size_t get_object_header(obj_hdr *hdr, unsigned char *data)
115{
116 char c, typename[10];
117 size_t size, used = 0;
118
119 /*
120 * type name string followed by space.
121 */
122 while ((c = data[used]) != ' ') {
123 typename[used++] = c;
124 if (used >= sizeof(typename))
125 return 0;
126 }
127 typename[used] = 0;
128 if (used == 0)
129 return 0;
d12299fe 130 hdr->type = git_object_string2type(typename);
87d9869f 131 used++; /* consume the space */
7d7cd885
VM
132
133 /*
134 * length follows immediately in decimal (without
135 * leading zeros).
136 */
137 size = data[used++] - '0';
138 if (size > 9)
139 return 0;
140 if (size) {
141 while ((c = data[used]) != '\0') {
142 size_t d = c - '0';
143 if (d > 9)
144 break;
145 used++;
146 size = size * 10 + d;
147 }
148 }
149 hdr->size = size;
150
151 /*
152 * the length must be followed by a zero byte
153 */
154 if (data[used++] != '\0')
155 return 0;
156
157 return used;
158}
159
160
161
162/***********************************************************
163 *
164 * ZLIB RELATED FUNCTIONS
165 *
166 ***********************************************************/
167
168static void init_stream(z_stream *s, void *out, size_t len)
169{
170 memset(s, 0, sizeof(*s));
87d9869f 171 s->next_out = out;
1c3fac4d 172 s->avail_out = (uInt)len;
7d7cd885
VM
173}
174
175static void set_stream_input(z_stream *s, void *in, size_t len)
176{
87d9869f 177 s->next_in = in;
1c3fac4d 178 s->avail_in = (uInt)len;
7d7cd885
VM
179}
180
181static void set_stream_output(z_stream *s, void *out, size_t len)
182{
87d9869f 183 s->next_out = out;
1c3fac4d 184 s->avail_out = (uInt)len;
7d7cd885
VM
185}
186
187
13224ea4 188static int start_inflate(z_stream *s, git_buf *obj, void *out, size_t len)
7d7cd885
VM
189{
190 int status;
191
192 init_stream(s, out, len);
fa6420f7 193 set_stream_input(s, obj->ptr, git_buf_len(obj));
7d7cd885
VM
194
195 if ((status = inflateInit(s)) < Z_OK)
196 return status;
197
198 return inflate(s, 0);
199}
200
201static int finish_inflate(z_stream *s)
202{
203 int status = Z_OK;
204
205 while (status == Z_OK)
206 status = inflate(s, Z_FINISH);
207
208 inflateEnd(s);
209
e1de726c
RB
210 if ((status != Z_STREAM_END) || (s->avail_in != 0)) {
211 giterr_set(GITERR_ZLIB, "Failed to finish ZLib inflation. Stream aborted prematurely");
212 return -1;
213 }
7d7cd885 214
e1de726c 215 return 0;
7d7cd885
VM
216}
217
72a3fe42 218static int is_zlib_compressed_data(unsigned char *data)
7d7cd885 219{
72a3fe42 220 unsigned int w;
7d7cd885 221
72a3fe42 222 w = ((unsigned int)(data[0]) << 8) + data[1];
c51065e3 223 return (data[0] & 0x8F) == 0x08 && !(w % 31);
7d7cd885
VM
224}
225
72a3fe42 226static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen)
7d7cd885
VM
227{
228 z_stream zs;
72a3fe42 229 int status = Z_OK;
7d7cd885 230
72a3fe42 231 memset(&zs, 0x0, sizeof(zs));
7d7cd885 232
87d9869f 233 zs.next_out = out;
1c3fac4d 234 zs.avail_out = (uInt)outlen;
7d7cd885 235
87d9869f 236 zs.next_in = in;
1c3fac4d 237 zs.avail_in = (uInt)inlen;
7d7cd885 238
e1de726c
RB
239 if (inflateInit(&zs) < Z_OK) {
240 giterr_set(GITERR_ZLIB, "Failed to inflate buffer");
241 return -1;
242 }
7d7cd885 243
72a3fe42
VM
244 while (status == Z_OK)
245 status = inflate(&zs, Z_FINISH);
7d7cd885 246
72a3fe42 247 inflateEnd(&zs);
7d7cd885 248
e1de726c
RB
249 if (status != Z_STREAM_END /* || zs.avail_in != 0 */ ||
250 zs.total_out != outlen)
251 {
252 giterr_set(GITERR_ZLIB, "Failed to inflate buffer. Stream aborted prematurely");
253 return -1;
254 }
7d7cd885 255
e1de726c 256 return 0;
7d7cd885
VM
257}
258
7d7cd885
VM
259static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
260{
261 unsigned char *buf, *head = hb;
262 size_t tail;
263
264 /*
265 * allocate a buffer to hold the inflated data and copy the
266 * initial sequence of inflated data from the tail of the
267 * head buffer, if any.
268 */
269 if ((buf = git__malloc(hdr->size + 1)) == NULL) {
270 inflateEnd(s);
271 return NULL;
272 }
273 tail = s->total_out - used;
274 if (used > 0 && tail > 0) {
275 if (tail > hdr->size)
276 tail = hdr->size;
277 memcpy(buf, head + used, tail);
278 }
279 used = tail;
280
281 /*
282 * inflate the remainder of the object data, if any
283 */
284 if (hdr->size < used)
285 inflateEnd(s);
286 else {
287 set_stream_output(s, buf + used, hdr->size - used);
288 if (finish_inflate(s)) {
3286c408 289 git__free(buf);
7d7cd885
VM
290 return NULL;
291 }
292 }
293
294 return buf;
295}
296
297/*
298 * At one point, there was a loose object format that was intended to
299 * mimic the format used in pack-files. This was to allow easy copying
300 * of loose object data into packs. This format is no longer used, but
301 * we must still read it.
302 */
13224ea4 303static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj)
7d7cd885
VM
304{
305 unsigned char *in, *buf;
306 obj_hdr hdr;
307 size_t len, used;
308
309 /*
310 * read the object header, which is an (uncompressed)
311 * binary encoding of the object type and size.
312 */
e1de726c
RB
313 if ((used = get_binary_object_header(&hdr, obj)) == 0 ||
314 !git_object_typeisloose(hdr.type)) {
315 giterr_set(GITERR_ODB, "Failed to inflate loose object.");
316 return -1;
317 }
7d7cd885
VM
318
319 /*
320 * allocate a buffer and inflate the data into it
321 */
322 buf = git__malloc(hdr.size + 1);
e1de726c 323 GITERR_CHECK_ALLOC(buf);
7d7cd885 324
13224ea4
VM
325 in = ((unsigned char *)obj->ptr) + used;
326 len = obj->size - used;
e1de726c 327 if (inflate_buffer(in, len, buf, hdr.size) < 0) {
3286c408 328 git__free(buf);
e1de726c 329 return -1;
7d7cd885
VM
330 }
331 buf[hdr.size] = '\0';
332
333 out->data = buf;
87d9869f 334 out->len = hdr.size;
7d7cd885
VM
335 out->type = hdr.type;
336
e1de726c 337 return 0;
7d7cd885
VM
338}
339
13224ea4 340static int inflate_disk_obj(git_rawobj *out, git_buf *obj)
7d7cd885
VM
341{
342 unsigned char head[64], *buf;
343 z_stream zs;
7d7cd885
VM
344 obj_hdr hdr;
345 size_t used;
346
347 /*
348 * check for a pack-like loose object
349 */
13224ea4 350 if (!is_zlib_compressed_data((unsigned char *)obj->ptr))
7d7cd885
VM
351 return inflate_packlike_loose_disk_obj(out, obj);
352
353 /*
354 * inflate the initial part of the io buffer in order
355 * to parse the object header (type and size).
356 */
e1de726c
RB
357 if (start_inflate(&zs, obj, head, sizeof(head)) < Z_OK ||
358 (used = get_object_header(&hdr, head)) == 0 ||
359 !git_object_typeisloose(hdr.type))
360 {
361 giterr_set(GITERR_ODB, "Failed to inflate disk object.");
362 return -1;
363 }
7d7cd885
VM
364
365 /*
366 * allocate a buffer and inflate the object data into it
367 * (including the initial sequence in the head buffer).
368 */
369 if ((buf = inflate_tail(&zs, head, used, &hdr)) == NULL)
e1de726c 370 return -1;
7d7cd885
VM
371 buf[hdr.size] = '\0';
372
373 out->data = buf;
87d9869f 374 out->len = hdr.size;
7d7cd885
VM
375 out->type = hdr.type;
376
e1de726c 377 return 0;
7d7cd885
VM
378}
379
380
381
382
383
384
385/***********************************************************
386 *
387 * ODB OBJECT READING & WRITING
388 *
389 * Backend for the public API; read headers and full objects
390 * from the ODB. Write raw data to the ODB.
391 *
392 ***********************************************************/
393
97769280 394static int read_loose(git_rawobj *out, git_buf *loc)
7d7cd885
VM
395{
396 int error;
13224ea4 397 git_buf obj = GIT_BUF_INIT;
7d7cd885
VM
398
399 assert(out && loc);
400
cb8a7961 401 if (git_buf_oom(loc))
e1de726c 402 return -1;
97769280 403
7d7cd885 404 out->data = NULL;
87d9869f 405 out->len = 0;
7d7cd885
VM
406 out->type = GIT_OBJ_BAD;
407
e1de726c
RB
408 if (!(error = git_futils_readbuffer(&obj, loc->ptr)))
409 error = inflate_disk_obj(out, &obj);
7d7cd885 410
13224ea4 411 git_buf_free(&obj);
7d7cd885 412
e1de726c 413 return error;
7d7cd885
VM
414}
415
97769280 416static int read_header_loose(git_rawobj *out, git_buf *loc)
7d7cd885 417{
e1de726c 418 int error = 0, z_return = Z_ERRNO, read_bytes;
7d7cd885
VM
419 git_file fd;
420 z_stream zs;
421 obj_hdr header_obj;
422 unsigned char raw_buffer[16], inflated_buffer[64];
423
424 assert(out && loc);
425
cb8a7961 426 if (git_buf_oom(loc))
e1de726c 427 return -1;
97769280 428
7d7cd885
VM
429 out->data = NULL;
430
e1de726c
RB
431 if ((fd = git_futils_open_ro(loc->ptr)) < 0)
432 return fd;
7d7cd885
VM
433
434 init_stream(&zs, inflated_buffer, sizeof(inflated_buffer));
435
e1de726c 436 z_return = inflateInit(&zs);
7d7cd885 437
e1de726c
RB
438 while (z_return == Z_OK) {
439 if ((read_bytes = p_read(fd, raw_buffer, sizeof(raw_buffer))) > 0) {
7d7cd885
VM
440 set_stream_input(&zs, raw_buffer, read_bytes);
441 z_return = inflate(&zs, 0);
e1de726c 442 } else
a3ced637 443 z_return = Z_STREAM_END;
e1de726c 444 }
7d7cd885
VM
445
446 if ((z_return != Z_STREAM_END && z_return != Z_BUF_ERROR)
447 || get_object_header(&header_obj, inflated_buffer) == 0
e1de726c
RB
448 || git_object_typeisloose(header_obj.type) == 0)
449 {
450 giterr_set(GITERR_ZLIB, "Failed to read loose object header");
451 error = -1;
452 } else {
453 out->len = header_obj.size;
454 out->type = header_obj.type;
7d7cd885
VM
455 }
456
7d7cd885 457 finish_inflate(&zs);
f79026b4 458 p_close(fd);
60e1b49a 459
e1de726c 460 return error;
7d7cd885
VM
461}
462
97769280
RB
463static int locate_object(
464 git_buf *object_location,
465 loose_backend *backend,
466 const git_oid *oid)
7d7cd885 467{
8294e8cf 468 int error = object_file_name(object_location, backend, oid);
97769280 469
e1de726c
RB
470 if (!error && !git_path_exists(object_location->ptr))
471 return GIT_ENOTFOUND;
97769280
RB
472
473 return error;
7d7cd885
VM
474}
475
aea8a638 476/* Explore an entry of a directory and see if it matches a short oid */
97769280 477static int fn_locate_object_short_oid(void *state, git_buf *pathbuf) {
aea8a638
MP
478 loose_locate_object_state *sstate = (loose_locate_object_state *)state;
479
fa6420f7 480 if (git_buf_len(pathbuf) - sstate->dir_len != GIT_OID_HEXSZ - 2) {
aea8a638 481 /* Entry cannot be an object. Continue to next entry */
e1de726c 482 return 0;
aea8a638
MP
483 }
484
9d160ba8 485 if (git_path_isdir(pathbuf->ptr) == false) {
f1d01851
VM
486 /* We are already in the directory matching the 2 first hex characters,
487 * compare the first ncmp characters of the oids */
488 if (!memcmp(sstate->short_oid + 2,
97769280 489 (unsigned char *)pathbuf->ptr + sstate->dir_len,
f1d01851
VM
490 sstate->short_oid_len - 2)) {
491
aea8a638
MP
492 if (!sstate->found) {
493 sstate->res_oid[0] = sstate->short_oid[0];
494 sstate->res_oid[1] = sstate->short_oid[1];
97769280 495 memcpy(sstate->res_oid+2, pathbuf->ptr+sstate->dir_len, GIT_OID_HEXSZ-2);
aea8a638
MP
496 }
497 sstate->found++;
498 }
499 }
e1de726c 500
d0323a5f 501 if (sstate->found > 1)
e1de726c 502 return git_odb__error_ambiguous("multiple matches in loose objects");
d0323a5f 503
e1de726c 504 return 0;
aea8a638
MP
505}
506
507/* Locate an object matching a given short oid */
97769280
RB
508static int locate_object_short_oid(
509 git_buf *object_location,
510 git_oid *res_oid,
511 loose_backend *backend,
512 const git_oid *short_oid,
b8457baa 513 size_t len)
aea8a638
MP
514{
515 char *objects_dir = backend->objects_dir;
516 size_t dir_len = strlen(objects_dir);
517 loose_locate_object_state state;
518 int error;
519
97769280 520 /* prealloc memory for OBJ_DIR/xx/ */
e1de726c
RB
521 if (git_buf_grow(object_location, dir_len + 5) < 0)
522 return -1;
aea8a638 523
97769280
RB
524 git_buf_sets(object_location, objects_dir);
525 git_path_to_dir(object_location);
aea8a638 526
97769280 527 /* save adjusted position at end of dir so it can be restored later */
fa6420f7 528 dir_len = git_buf_len(object_location);
aea8a638
MP
529
530 /* Convert raw oid to hex formatted oid */
d0323a5f 531 git_oid_fmt((char *)state.short_oid, short_oid);
97769280 532
aea8a638 533 /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */
e1de726c
RB
534 if (git_buf_printf(object_location, "%.2s/", state.short_oid) < 0)
535 return -1;
aea8a638
MP
536
537 /* Check that directory exists */
1a481123 538 if (git_path_isdir(object_location->ptr) == false)
282283ac 539 return git_odb__error_notfound("no matching loose object for prefix", short_oid);
aea8a638 540
fa6420f7 541 state.dir_len = git_buf_len(object_location);
aea8a638
MP
542 state.short_oid_len = len;
543 state.found = 0;
97769280 544
aea8a638 545 /* Explore directory to find a unique object matching short_oid */
e1de726c
RB
546 error = git_path_direach(
547 object_location, fn_locate_object_short_oid, &state);
97769280 548 if (error)
e1de726c 549 return error;
97769280 550
e1de726c 551 if (!state.found)
282283ac 552 return git_odb__error_notfound("no matching loose object for prefix", short_oid);
aea8a638
MP
553
554 /* Convert obtained hex formatted oid to raw */
fa48608e 555 error = git_oid_fromstr(res_oid, (char *)state.res_oid);
e1de726c
RB
556 if (error)
557 return error;
aea8a638
MP
558
559 /* Update the location according to the oid obtained */
97769280
RB
560
561 git_buf_truncate(object_location, dir_len);
e1de726c
RB
562 if (git_buf_grow(object_location, dir_len + GIT_OID_HEXSZ + 2) < 0)
563 return -1;
97769280
RB
564
565 git_oid_pathfmt(object_location->ptr + dir_len, res_oid);
566
567 object_location->size += GIT_OID_HEXSZ + 1;
568 object_location->ptr[object_location->size] = '\0';
aea8a638 569
e1de726c 570 return 0;
aea8a638
MP
571}
572
7d7cd885
VM
573
574
575
576
577
578
579
580
581/***********************************************************
582 *
583 * LOOSE BACKEND PUBLIC API
584 *
585 * Implement the git_odb_backend API calls
586 *
587 ***********************************************************/
588
d568d585 589static int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
7d7cd885 590{
97769280 591 git_buf object_path = GIT_BUF_INIT;
72a3fe42 592 git_rawobj raw;
0d0fa7c3 593 int error;
7d7cd885 594
72a3fe42 595 assert(backend && oid);
7d7cd885 596
60e1b49a
VM
597 raw.len = 0;
598 raw.type = GIT_OBJ_BAD;
599
97769280 600 if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
282283ac 601 error = git_odb__error_notfound("no matching loose object", oid);
e1de726c 602 else if ((error = read_header_loose(&raw, &object_path)) == 0) {
97769280
RB
603 *len_p = raw.len;
604 *type_p = raw.type;
605 }
7d7cd885 606
97769280 607 git_buf_free(&object_path);
7d7cd885 608
97769280 609 return error;
72a3fe42 610}
7d7cd885 611
d568d585 612static int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
7d7cd885 613{
97769280 614 git_buf object_path = GIT_BUF_INIT;
72a3fe42 615 git_rawobj raw;
e1de726c 616 int error = 0;
7d7cd885 617
72a3fe42 618 assert(backend && oid);
7d7cd885 619
97769280 620 if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
282283ac 621 error = git_odb__error_notfound("no matching loose object", oid);
e1de726c 622 else if ((error = read_loose(&raw, &object_path)) == 0) {
97769280
RB
623 *buffer_p = raw.data;
624 *len_p = raw.len;
625 *type_p = raw.type;
626 }
72a3fe42 627
97769280 628 git_buf_free(&object_path);
72a3fe42 629
97769280 630 return error;
7d7cd885
VM
631}
632
d568d585 633static int loose_backend__read_prefix(
d0323a5f
VM
634 git_oid *out_oid,
635 void **buffer_p,
636 size_t *len_p,
637 git_otype *type_p,
638 git_odb_backend *backend,
639 const git_oid *short_oid,
b8457baa 640 size_t len)
ecd6fdf1 641{
e1de726c 642 int error = 0;
97769280 643
aea8a638 644 if (len < GIT_OID_MINPREFIXLEN)
e1de726c 645 error = git_odb__error_ambiguous("prefix length too short");
aea8a638 646
e1de726c 647 else if (len >= GIT_OID_HEXSZ) {
aea8a638 648 /* We can fall back to regular read method */
97769280 649 error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
e1de726c 650 if (!error)
ecd6fdf1 651 git_oid_cpy(out_oid, short_oid);
aea8a638 652 } else {
97769280 653 git_buf object_path = GIT_BUF_INIT;
aea8a638 654 git_rawobj raw;
aea8a638
MP
655
656 assert(backend && short_oid);
657
97769280 658 if ((error = locate_object_short_oid(&object_path, out_oid,
e1de726c
RB
659 (loose_backend *)backend, short_oid, len)) == 0 &&
660 (error = read_loose(&raw, &object_path)) == 0)
661 {
97769280
RB
662 *buffer_p = raw.data;
663 *len_p = raw.len;
664 *type_p = raw.type;
aea8a638
MP
665 }
666
97769280 667 git_buf_free(&object_path);
ecd6fdf1 668 }
aea8a638 669
97769280 670 return error;
ecd6fdf1
MP
671}
672
d568d585 673static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
7d7cd885 674{
97769280
RB
675 git_buf object_path = GIT_BUF_INIT;
676 int error;
7d7cd885
VM
677
678 assert(backend && oid);
679
97769280
RB
680 error = locate_object(&object_path, (loose_backend *)backend, oid);
681
682 git_buf_free(&object_path);
683
e1de726c 684 return !error;
7d7cd885
VM
685}
686
521aedad
CMN
687struct foreach_state {
688 size_t dir_len;
c3fb7d04 689 git_odb_foreach_cb cb;
521aedad 690 void *data;
5dca2010 691 int cb_error;
521aedad
CMN
692};
693
b7158c53 694GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr)
521aedad
CMN
695{
696 int v, i = 0;
697 if (strlen(ptr) != 41)
698 return -1;
699
700 if (ptr[2] != '/') {
701 return -1;
702 }
703
704 v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i+1]);
705 if (v < 0)
706 return -1;
707
708 oid->id[0] = (unsigned char) v;
709
710 ptr += 3;
711 for (i = 0; i < 38; i += 2) {
712 v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i + 1]);
713 if (v < 0)
714 return -1;
715
716 oid->id[1 + i/2] = (unsigned char) v;
717 }
718
719 return 0;
720}
721
722static int foreach_object_dir_cb(void *_state, git_buf *path)
723{
724 git_oid oid;
725 struct foreach_state *state = (struct foreach_state *) _state;
726
727 if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0)
728 return 0;
729
5dca2010
RB
730 if (state->cb(&oid, state->data)) {
731 state->cb_error = GIT_EUSER;
521aedad 732 return -1;
5dca2010 733 }
521aedad
CMN
734
735 return 0;
736}
737
738static int foreach_cb(void *_state, git_buf *path)
739{
740 struct foreach_state *state = (struct foreach_state *) _state;
741
5dca2010 742 return git_path_direach(path, foreach_object_dir_cb, state);
521aedad
CMN
743}
744
c3fb7d04 745static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
521aedad
CMN
746{
747 char *objects_dir;
748 int error;
749 git_buf buf = GIT_BUF_INIT;
750 struct foreach_state state;
751 loose_backend *backend = (loose_backend *) _backend;
752
753 assert(backend && cb);
754
755 objects_dir = backend->objects_dir;
756
757 git_buf_sets(&buf, objects_dir);
758 git_path_to_dir(&buf);
759
5dca2010 760 memset(&state, 0, sizeof(state));
521aedad
CMN
761 state.cb = cb;
762 state.data = data;
763 state.dir_len = git_buf_len(&buf);
764
765 error = git_path_direach(&buf, foreach_cb, &state);
5dca2010 766
521aedad
CMN
767 git_buf_free(&buf);
768
5dca2010 769 return state.cb_error ? state.cb_error : error;
521aedad
CMN
770}
771
d568d585 772static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream)
72a3fe42
VM
773{
774 loose_writestream *stream = (loose_writestream *)_stream;
775 loose_backend *backend = (loose_backend *)_stream->backend;
97769280 776 git_buf final_path = GIT_BUF_INIT;
e1de726c 777 int error = 0;
72a3fe42 778
e1de726c 779 if (git_filebuf_hash(oid, &stream->fbuf) < 0 ||
8294e8cf
RB
780 object_file_name(&final_path, backend, oid) < 0 ||
781 object_mkdir(&final_path, backend) < 0)
e1de726c 782 error = -1;
472d4d85
CMN
783 /*
784 * Don't try to add an existing object to the repository. This
785 * is what git does and allows us to sidestep the fact that
786 * we're not allowed to overwrite a read-only file on Windows.
787 */
e1de726c 788 else if (git_path_exists(final_path.ptr) == true)
472d4d85 789 git_filebuf_cleanup(&stream->fbuf);
e1de726c
RB
790 else
791 error = git_filebuf_commit_at(
792 &stream->fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE);
97769280 793
97769280
RB
794 git_buf_free(&final_path);
795
97769280 796 return error;
72a3fe42
VM
797}
798
d568d585 799static int loose_backend__stream_write(git_odb_stream *_stream, const char *data, size_t len)
7d7cd885 800{
72a3fe42
VM
801 loose_writestream *stream = (loose_writestream *)_stream;
802 return git_filebuf_write(&stream->fbuf, data, len);
803}
804
d568d585 805static void loose_backend__stream_free(git_odb_stream *_stream)
72a3fe42
VM
806{
807 loose_writestream *stream = (loose_writestream *)_stream;
808
97769280 809 git_filebuf_cleanup(&stream->fbuf);
3286c408 810 git__free(stream);
72a3fe42
VM
811}
812
813static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type)
814{
815 const char *type_str = git_object_type2string(obj_type);
816 int len = snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len);
817
87d9869f 818 assert(len > 0); /* otherwise snprintf() is broken */
e1de726c 819 assert(((size_t)len) < n); /* otherwise the caller is broken! */
72a3fe42 820
72a3fe42
VM
821 return len+1;
822}
823
d568d585 824static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, size_t length, git_otype type)
72a3fe42
VM
825{
826 loose_backend *backend;
e1de726c 827 loose_writestream *stream = NULL;
97769280
RB
828 char hdr[64];
829 git_buf tmp_path = GIT_BUF_INIT;
87d9869f 830 int hdrlen;
7d7cd885 831
72a3fe42 832 assert(_backend);
7d7cd885
VM
833
834 backend = (loose_backend *)_backend;
72a3fe42 835 *stream_out = NULL;
7d7cd885 836
72a3fe42 837 hdrlen = format_object_header(hdr, sizeof(hdr), length, type);
72a3fe42
VM
838
839 stream = git__calloc(1, sizeof(loose_writestream));
e1de726c 840 GITERR_CHECK_ALLOC(stream);
7d7cd885 841
72a3fe42
VM
842 stream->stream.backend = _backend;
843 stream->stream.read = NULL; /* read only */
844 stream->stream.write = &loose_backend__stream_write;
845 stream->stream.finalize_write = &loose_backend__stream_fwrite;
846 stream->stream.free = &loose_backend__stream_free;
847 stream->stream.mode = GIT_STREAM_WRONLY;
7d7cd885 848
e1de726c
RB
849 if (git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||
850 git_filebuf_open(&stream->fbuf, tmp_path.ptr,
851 GIT_FILEBUF_HASH_CONTENTS |
852 GIT_FILEBUF_TEMPORARY |
853 (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)) < 0 ||
854 stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0)
855 {
856 git_filebuf_cleanup(&stream->fbuf);
857 git__free(stream);
858 stream = NULL;
859 }
97769280 860 git_buf_free(&tmp_path);
72a3fe42 861 *stream_out = (git_odb_stream *)stream;
97769280 862
e1de726c 863 return !stream ? -1 : 0;
7d7cd885
VM
864}
865
d568d585 866static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const void *data, size_t len, git_otype type)
afeecf4f 867{
8e8b6b01 868 int error = 0, header_len;
97769280
RB
869 git_buf final_path = GIT_BUF_INIT;
870 char header[64];
b762e576 871 git_filebuf fbuf = GIT_FILEBUF_INIT;
afeecf4f
VM
872 loose_backend *backend;
873
874 backend = (loose_backend *)_backend;
875
876 /* prepare the header for the file */
e1de726c 877 header_len = format_object_header(header, sizeof(header), len, type);
afeecf4f 878
e1de726c
RB
879 if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||
880 git_filebuf_open(&fbuf, final_path.ptr,
e1de726c
RB
881 GIT_FILEBUF_TEMPORARY |
882 (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)) < 0)
883 {
884 error = -1;
97769280 885 goto cleanup;
e1de726c 886 }
afeecf4f
VM
887
888 git_filebuf_write(&fbuf, header, header_len);
889 git_filebuf_write(&fbuf, data, len);
afeecf4f 890
8294e8cf
RB
891 if (object_file_name(&final_path, backend, oid) < 0 ||
892 object_mkdir(&final_path, backend) < 0 ||
e1de726c
RB
893 git_filebuf_commit_at(&fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE) < 0)
894 error = -1;
afeecf4f
VM
895
896cleanup:
0d0fa7c3 897 if (error < 0)
97769280
RB
898 git_filebuf_cleanup(&fbuf);
899 git_buf_free(&final_path);
afeecf4f
VM
900 return error;
901}
902
d568d585 903static void loose_backend__free(git_odb_backend *_backend)
7d7cd885
VM
904{
905 loose_backend *backend;
906 assert(_backend);
907 backend = (loose_backend *)_backend;
908
3286c408 909 git__free(backend);
7d7cd885
VM
910}
911
8af4d074
VM
912int git_odb_backend_loose(
913 git_odb_backend **backend_out,
914 const char *objects_dir,
915 int compression_level,
916 int do_fsync)
7d7cd885
VM
917{
918 loose_backend *backend;
8294e8cf
RB
919 size_t objects_dirlen;
920
921 assert(backend_out && objects_dir);
922
923 objects_dirlen = strlen(objects_dir);
7d7cd885 924
8294e8cf 925 backend = git__calloc(1, sizeof(loose_backend) + objects_dirlen + 2);
e1de726c 926 GITERR_CHECK_ALLOC(backend);
7d7cd885 927
55f6f21b 928 backend->parent.version = GIT_ODB_BACKEND_VERSION;
8294e8cf
RB
929 backend->objects_dirlen = objects_dirlen;
930 memcpy(backend->objects_dir, objects_dir, objects_dirlen);
931 if (backend->objects_dir[backend->objects_dirlen - 1] != '/')
932 backend->objects_dir[backend->objects_dirlen++] = '/';
7d7cd885 933
8af4d074
VM
934 if (compression_level < 0)
935 compression_level = Z_BEST_SPEED;
936
937 backend->object_zlib_level = compression_level;
938 backend->fsync_object_files = do_fsync;
7d7cd885
VM
939
940 backend->parent.read = &loose_backend__read;
afeecf4f 941 backend->parent.write = &loose_backend__write;
d0323a5f 942 backend->parent.read_prefix = &loose_backend__read_prefix;
7d7cd885 943 backend->parent.read_header = &loose_backend__read_header;
72a3fe42 944 backend->parent.writestream = &loose_backend__stream;
7d7cd885 945 backend->parent.exists = &loose_backend__exists;
521aedad 946 backend->parent.foreach = &loose_backend__foreach;
7d7cd885
VM
947 backend->parent.free = &loose_backend__free;
948
7d7cd885 949 *backend_out = (git_odb_backend *)backend;
e1de726c 950 return 0;
7d7cd885 951}