]> git.proxmox.com Git - libgit2.git/blame - src/odb_loose.c
refs: Rename git_referece_listcb to _foreach
[libgit2.git] / src / odb_loose.c
CommitLineData
7d7cd885
VM
1/*
2 * This file is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2,
4 * as published by the Free Software Foundation.
5 *
6 * In addition to the permissions in the GNU General Public License,
7 * the authors give you unlimited permission to link the compiled
8 * version of this file into combinations with other programs,
9 * and to distribute those combinations without any restriction
10 * coming from the use of this file. (The General Public License
11 * restrictions do apply in other respects; for example, they cover
12 * modification of the file, and distribution when not linked into
13 * a combined executable.)
14 *
15 * This file is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; see the file COPYING. If not, write to
22 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
26#include "common.h"
44908fe7
VM
27#include "git2/zlib.h"
28#include "git2/object.h"
7d7cd885
VM
29#include "fileops.h"
30#include "hash.h"
31#include "odb.h"
d0323a5f 32#include "oid.h"
7d7cd885 33#include "delta-apply.h"
72a3fe42 34#include "filebuf.h"
7d7cd885 35
44908fe7 36#include "git2/odb_backend.h"
72a3fe42 37#include "git2/types.h"
7d7cd885
VM
38
39typedef struct { /* object header data */
40 git_otype type; /* object type */
41 size_t size; /* object size */
42} obj_hdr;
43
72a3fe42
VM
44typedef struct {
45 git_odb_stream stream;
46 git_filebuf fbuf;
47 int finished;
48} loose_writestream;
49
7d7cd885
VM
50typedef struct loose_backend {
51 git_odb_backend parent;
52
53 int object_zlib_level; /** loose object zlib compression level. */
54 int fsync_object_files; /** loose object file fsync flag. */
55 char *objects_dir;
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 */
64 unsigned int short_oid_len;
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/***********************************************************
74 *
75 * MISCELANEOUS HELPER FUNCTIONS
76 *
77 ***********************************************************/
78
7d7cd885
VM
79static size_t object_file_name(char *name, size_t n, char *dir, const git_oid *id)
80{
81 size_t len = strlen(dir);
82
83 /* check length: 43 = 40 hex sha1 chars + 2 * '/' + '\0' */
84 if (len+43 > n)
85 return len+43;
86
87 /* the object dir: eg $GIT_DIR/objects */
88 strcpy(name, dir);
89 if (name[len-1] != '/')
90 name[len++] = '/';
91
92 /* loose object filename: aa/aaa... (41 bytes) */
93 git_oid_pathfmt(&name[len], id);
94 name[len+41] = '\0';
95
96 return 0;
97}
98
99
100static size_t get_binary_object_header(obj_hdr *hdr, gitfo_buf *obj)
101{
102 unsigned char c;
103 unsigned char *data = obj->data;
104 size_t shift, size, used = 0;
105
106 if (obj->len == 0)
107 return 0;
108
109 c = data[used++];
110 hdr->type = (c >> 4) & 7;
111
112 size = c & 15;
113 shift = 4;
114 while (c & 0x80) {
115 if (obj->len <= used)
116 return 0;
117 if (sizeof(size_t) * 8 <= shift)
118 return 0;
119 c = data[used++];
120 size += (c & 0x7f) << shift;
121 shift += 7;
122 }
123 hdr->size = size;
124
125 return used;
126}
127
128static size_t get_object_header(obj_hdr *hdr, unsigned char *data)
129{
130 char c, typename[10];
131 size_t size, used = 0;
132
133 /*
134 * type name string followed by space.
135 */
136 while ((c = data[used]) != ' ') {
137 typename[used++] = c;
138 if (used >= sizeof(typename))
139 return 0;
140 }
141 typename[used] = 0;
142 if (used == 0)
143 return 0;
d12299fe 144 hdr->type = git_object_string2type(typename);
7d7cd885
VM
145 used++; /* consume the space */
146
147 /*
148 * length follows immediately in decimal (without
149 * leading zeros).
150 */
151 size = data[used++] - '0';
152 if (size > 9)
153 return 0;
154 if (size) {
155 while ((c = data[used]) != '\0') {
156 size_t d = c - '0';
157 if (d > 9)
158 break;
159 used++;
160 size = size * 10 + d;
161 }
162 }
163 hdr->size = size;
164
165 /*
166 * the length must be followed by a zero byte
167 */
168 if (data[used++] != '\0')
169 return 0;
170
171 return used;
172}
173
174
175
176/***********************************************************
177 *
178 * ZLIB RELATED FUNCTIONS
179 *
180 ***********************************************************/
181
182static void init_stream(z_stream *s, void *out, size_t len)
183{
184 memset(s, 0, sizeof(*s));
185 s->next_out = out;
186 s->avail_out = len;
187}
188
189static void set_stream_input(z_stream *s, void *in, size_t len)
190{
191 s->next_in = in;
192 s->avail_in = len;
193}
194
195static void set_stream_output(z_stream *s, void *out, size_t len)
196{
197 s->next_out = out;
198 s->avail_out = len;
199}
200
201
202static int start_inflate(z_stream *s, gitfo_buf *obj, void *out, size_t len)
203{
204 int status;
205
206 init_stream(s, out, len);
207 set_stream_input(s, obj->data, obj->len);
208
209 if ((status = inflateInit(s)) < Z_OK)
210 return status;
211
212 return inflate(s, 0);
213}
214
215static int finish_inflate(z_stream *s)
216{
217 int status = Z_OK;
218
219 while (status == Z_OK)
220 status = inflate(s, Z_FINISH);
221
222 inflateEnd(s);
223
224 if ((status != Z_STREAM_END) || (s->avail_in != 0))
dfb12cd5 225 return git__throw(GIT_ERROR, "Failed to finish inflation. Stream aborted prematurely");
7d7cd885
VM
226
227 return GIT_SUCCESS;
228}
229
72a3fe42 230static int is_zlib_compressed_data(unsigned char *data)
7d7cd885 231{
72a3fe42 232 unsigned int w;
7d7cd885 233
72a3fe42
VM
234 w = ((unsigned int)(data[0]) << 8) + data[1];
235 return data[0] == 0x78 && !(w % 31);
7d7cd885
VM
236}
237
72a3fe42 238static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen)
7d7cd885
VM
239{
240 z_stream zs;
72a3fe42 241 int status = Z_OK;
7d7cd885 242
72a3fe42 243 memset(&zs, 0x0, sizeof(zs));
7d7cd885 244
72a3fe42
VM
245 zs.next_out = out;
246 zs.avail_out = outlen;
7d7cd885 247
72a3fe42
VM
248 zs.next_in = in;
249 zs.avail_in = inlen;
7d7cd885 250
72a3fe42 251 if (inflateInit(&zs) < Z_OK)
dfb12cd5 252 return git__throw(GIT_ERROR, "Failed to inflate buffer");
7d7cd885 253
72a3fe42
VM
254 while (status == Z_OK)
255 status = inflate(&zs, Z_FINISH);
7d7cd885 256
72a3fe42 257 inflateEnd(&zs);
7d7cd885 258
72a3fe42 259 if ((status != Z_STREAM_END) /*|| (zs.avail_in != 0) */)
dfb12cd5 260 return git__throw(GIT_ERROR, "Failed to inflate buffer. Stream aborted prematurely");
7d7cd885 261
72a3fe42 262 if (zs.total_out != outlen)
dfb12cd5 263 return git__throw(GIT_ERROR, "Failed to inflate buffer. Stream aborted prematurely");
7d7cd885
VM
264
265 return GIT_SUCCESS;
266}
267
7d7cd885
VM
268static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
269{
270 unsigned char *buf, *head = hb;
271 size_t tail;
272
273 /*
274 * allocate a buffer to hold the inflated data and copy the
275 * initial sequence of inflated data from the tail of the
276 * head buffer, if any.
277 */
278 if ((buf = git__malloc(hdr->size + 1)) == NULL) {
279 inflateEnd(s);
280 return NULL;
281 }
282 tail = s->total_out - used;
283 if (used > 0 && tail > 0) {
284 if (tail > hdr->size)
285 tail = hdr->size;
286 memcpy(buf, head + used, tail);
287 }
288 used = tail;
289
290 /*
291 * inflate the remainder of the object data, if any
292 */
293 if (hdr->size < used)
294 inflateEnd(s);
295 else {
296 set_stream_output(s, buf + used, hdr->size - used);
297 if (finish_inflate(s)) {
298 free(buf);
299 return NULL;
300 }
301 }
302
303 return buf;
304}
305
306/*
307 * At one point, there was a loose object format that was intended to
308 * mimic the format used in pack-files. This was to allow easy copying
309 * of loose object data into packs. This format is no longer used, but
310 * we must still read it.
311 */
312static int inflate_packlike_loose_disk_obj(git_rawobj *out, gitfo_buf *obj)
313{
314 unsigned char *in, *buf;
315 obj_hdr hdr;
316 size_t len, used;
317
318 /*
319 * read the object header, which is an (uncompressed)
320 * binary encoding of the object type and size.
321 */
322 if ((used = get_binary_object_header(&hdr, obj)) == 0)
dfb12cd5 323 return git__throw(GIT_ERROR, "Failed to inflate loose object. Object has no header");
7d7cd885 324
d12299fe 325 if (!git_object_typeisloose(hdr.type))
dfb12cd5 326 return git__throw(GIT_ERROR, "Failed to inflate loose object. Wrong object type");
7d7cd885
VM
327
328 /*
329 * allocate a buffer and inflate the data into it
330 */
331 buf = git__malloc(hdr.size + 1);
332 if (!buf)
f93f8ec5 333 return GIT_ENOMEM;
7d7cd885
VM
334
335 in = ((unsigned char *)obj->data) + used;
336 len = obj->len - used;
72a3fe42 337 if (inflate_buffer(in, len, buf, hdr.size)) {
7d7cd885 338 free(buf);
dfb12cd5 339 return git__throw(GIT_ERROR, "Failed to inflate loose object. Could not inflate buffer");
7d7cd885
VM
340 }
341 buf[hdr.size] = '\0';
342
343 out->data = buf;
344 out->len = hdr.size;
345 out->type = hdr.type;
346
347 return GIT_SUCCESS;
348}
349
350static int inflate_disk_obj(git_rawobj *out, gitfo_buf *obj)
351{
352 unsigned char head[64], *buf;
353 z_stream zs;
7d7cd885
VM
354 obj_hdr hdr;
355 size_t used;
356
357 /*
358 * check for a pack-like loose object
359 */
360 if (!is_zlib_compressed_data(obj->data))
361 return inflate_packlike_loose_disk_obj(out, obj);
362
363 /*
364 * inflate the initial part of the io buffer in order
365 * to parse the object header (type and size).
366 */
d8e1d038 367 if (start_inflate(&zs, obj, head, sizeof(head)) < Z_OK)
dfb12cd5 368 return git__throw(GIT_ERROR, "Failed to inflate disk object. Could not inflate buffer");
7d7cd885
VM
369
370 if ((used = get_object_header(&hdr, head)) == 0)
dfb12cd5 371 return git__throw(GIT_ERROR, "Failed to inflate disk object. Object has no header");
7d7cd885 372
d12299fe 373 if (!git_object_typeisloose(hdr.type))
dfb12cd5 374 return git__throw(GIT_ERROR, "Failed to inflate disk object. Wrong object type");
7d7cd885
VM
375
376 /*
377 * allocate a buffer and inflate the object data into it
378 * (including the initial sequence in the head buffer).
379 */
380 if ((buf = inflate_tail(&zs, head, used, &hdr)) == NULL)
f93f8ec5 381 return GIT_ENOMEM;
7d7cd885
VM
382 buf[hdr.size] = '\0';
383
384 out->data = buf;
385 out->len = hdr.size;
386 out->type = hdr.type;
387
388 return GIT_SUCCESS;
389}
390
391
392
393
394
395
396/***********************************************************
397 *
398 * ODB OBJECT READING & WRITING
399 *
400 * Backend for the public API; read headers and full objects
401 * from the ODB. Write raw data to the ODB.
402 *
403 ***********************************************************/
404
405static int read_loose(git_rawobj *out, const char *loc)
406{
407 int error;
408 gitfo_buf obj = GITFO_BUF_INIT;
409
410 assert(out && loc);
411
412 out->data = NULL;
413 out->len = 0;
414 out->type = GIT_OBJ_BAD;
415
416 if (gitfo_read_file(&obj, loc) < 0)
dfb12cd5 417 return git__throw(GIT_ENOTFOUND, "Failed to read loose object. File not found");
7d7cd885
VM
418
419 error = inflate_disk_obj(out, &obj);
420 gitfo_free_buf(&obj);
421
dfb12cd5 422 return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to read loose object");
7d7cd885
VM
423}
424
425static int read_header_loose(git_rawobj *out, const char *loc)
426{
427 int error = GIT_SUCCESS, z_return = Z_ERRNO, read_bytes;
428 git_file fd;
429 z_stream zs;
430 obj_hdr header_obj;
431 unsigned char raw_buffer[16], inflated_buffer[64];
432
433 assert(out && loc);
434
435 out->data = NULL;
436
437 if ((fd = gitfo_open(loc, O_RDONLY)) < 0)
dfb12cd5 438 return git__throw(GIT_ENOTFOUND, "Failed to read loose object header. File not found");
7d7cd885
VM
439
440 init_stream(&zs, inflated_buffer, sizeof(inflated_buffer));
441
442 if (inflateInit(&zs) < Z_OK) {
443 error = GIT_EZLIB;
444 goto cleanup;
445 }
446
447 do {
448 if ((read_bytes = read(fd, raw_buffer, sizeof(raw_buffer))) > 0) {
449 set_stream_input(&zs, raw_buffer, read_bytes);
450 z_return = inflate(&zs, 0);
a3ced637
SN
451 } else {
452 z_return = Z_STREAM_END;
453 break;
7d7cd885
VM
454 }
455 } while (z_return == Z_OK);
456
457 if ((z_return != Z_STREAM_END && z_return != Z_BUF_ERROR)
458 || get_object_header(&header_obj, inflated_buffer) == 0
d12299fe 459 || git_object_typeisloose(header_obj.type) == 0) {
7d7cd885
VM
460 error = GIT_EOBJCORRUPTED;
461 goto cleanup;
462 }
463
464 out->len = header_obj.size;
465 out->type = header_obj.type;
466
467cleanup:
468 finish_inflate(&zs);
469 gitfo_close(fd);
60e1b49a
VM
470
471 if (error < GIT_SUCCESS)
472 return git__throw(error, "Failed to read loose object header. Header is corrupted");
473
474 return GIT_SUCCESS;
7d7cd885
VM
475}
476
7d7cd885
VM
477static int locate_object(char *object_location, loose_backend *backend, const git_oid *oid)
478{
479 object_file_name(object_location, GIT_PATH_MAX, backend->objects_dir, oid);
480 return gitfo_exists(object_location);
481}
482
aea8a638
MP
483/* Explore an entry of a directory and see if it matches a short oid */
484int fn_locate_object_short_oid(void *state, char *pathbuf) {
485 loose_locate_object_state *sstate = (loose_locate_object_state *)state;
486
487 size_t pathbuf_len = strlen(pathbuf);
488 if (pathbuf_len - sstate->dir_len != GIT_OID_HEXSZ - 2) {
489 /* Entry cannot be an object. Continue to next entry */
490 return GIT_SUCCESS;
491 }
492
493 if (!gitfo_exists(pathbuf) && gitfo_isdir(pathbuf)) {
494 /* We are already in the directory matching the 2 first hex characters */
c09093cc 495 if (!git_oid_ncmp_hex(sstate->short_oid_len-2, sstate->short_oid+2, (unsigned char *)pathbuf + sstate->dir_len)) {
aea8a638
MP
496 if (!sstate->found) {
497 sstate->res_oid[0] = sstate->short_oid[0];
498 sstate->res_oid[1] = sstate->short_oid[1];
499 memcpy(sstate->res_oid+2, pathbuf+sstate->dir_len, GIT_OID_HEXSZ-2);
500 }
501 sstate->found++;
502 }
503 }
d0323a5f 504 if (sstate->found > 1)
aea8a638 505 return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Ambiguous sha1 prefix within loose objects");
d0323a5f
VM
506
507 return GIT_SUCCESS;
aea8a638
MP
508}
509
510/* Locate an object matching a given short oid */
511static int locate_object_short_oid(char *object_location, git_oid *res_oid, loose_backend *backend, const git_oid *short_oid, unsigned int len)
512{
513 char *objects_dir = backend->objects_dir;
514 size_t dir_len = strlen(objects_dir);
515 loose_locate_object_state state;
516 int error;
517
518 if (dir_len+43 > GIT_PATH_MAX)
519 return git__throw(GIT_ERROR, "Failed to locate object from short oid. Object path too long");
520
521 strcpy(object_location, objects_dir);
522
523 /* Add a separator if not already there */
524 if (object_location[dir_len-1] != '/')
525 object_location[dir_len++] = '/';
526
527 /* Convert raw oid to hex formatted oid */
d0323a5f 528 git_oid_fmt((char *)state.short_oid, short_oid);
aea8a638
MP
529 /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */
530 sprintf(object_location+dir_len, "%.2s/", state.short_oid);
531
532 /* Check that directory exists */
533 if (gitfo_exists(object_location) || gitfo_isdir(object_location))
534 return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found");
535
536 state.dir_len = dir_len+3;
537 state.short_oid_len = len;
538 state.found = 0;
539 /* Explore directory to find a unique object matching short_oid */
540 error = gitfo_dirent(object_location, GIT_PATH_MAX, fn_locate_object_short_oid, &state);
541 if (error) {
542 return git__rethrow(error, "Failed to locate object from short oid");
543 }
544 if (!state.found) {
545 return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found");
546 }
547
548 /* Convert obtained hex formatted oid to raw */
d0323a5f 549 error = git_oid_mkstr(res_oid, (char *)state.res_oid);
aea8a638
MP
550 if (error) {
551 return git__rethrow(error, "Failed to locate object from short oid");
552 }
553
554 /* Update the location according to the oid obtained */
555 git_oid_pathfmt(object_location+dir_len, res_oid);
556
557 return GIT_SUCCESS;
558}
559
7d7cd885
VM
560
561
562
563
564
565
566
567
568/***********************************************************
569 *
570 * LOOSE BACKEND PUBLIC API
571 *
572 * Implement the git_odb_backend API calls
573 *
574 ***********************************************************/
575
72a3fe42 576int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
7d7cd885
VM
577{
578 char object_path[GIT_PATH_MAX];
72a3fe42
VM
579 git_rawobj raw;
580 int error;
7d7cd885 581
72a3fe42 582 assert(backend && oid);
7d7cd885 583
60e1b49a
VM
584 raw.len = 0;
585 raw.type = GIT_OBJ_BAD;
586
7d7cd885 587 if (locate_object(object_path, (loose_backend *)backend, oid) < 0)
dfb12cd5 588 return git__throw(GIT_ENOTFOUND, "Failed to read loose backend header. Object not found");
7d7cd885 589
72a3fe42 590 if ((error = read_header_loose(&raw, object_path)) < GIT_SUCCESS)
60e1b49a 591 return error;
7d7cd885 592
72a3fe42
VM
593 *len_p = raw.len;
594 *type_p = raw.type;
595 return GIT_SUCCESS;
596}
7d7cd885 597
72a3fe42 598int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
7d7cd885
VM
599{
600 char object_path[GIT_PATH_MAX];
72a3fe42
VM
601 git_rawobj raw;
602 int error;
7d7cd885 603
72a3fe42 604 assert(backend && oid);
7d7cd885
VM
605
606 if (locate_object(object_path, (loose_backend *)backend, oid) < 0)
dfb12cd5 607 return git__throw(GIT_ENOTFOUND, "Failed to read loose backend. Object not found");
7d7cd885 608
72a3fe42 609 if ((error = read_loose(&raw, object_path)) < GIT_SUCCESS)
dfb12cd5 610 return git__rethrow(error, "Failed to read loose backend");
72a3fe42
VM
611
612 *buffer_p = raw.data;
613 *len_p = raw.len;
614 *type_p = raw.type;
615
616 return GIT_SUCCESS;
7d7cd885
VM
617}
618
d0323a5f
VM
619int loose_backend__read_prefix(
620 git_oid *out_oid,
621 void **buffer_p,
622 size_t *len_p,
623 git_otype *type_p,
624 git_odb_backend *backend,
625 const git_oid *short_oid,
626 unsigned int len)
ecd6fdf1 627{
aea8a638
MP
628 if (len < GIT_OID_MINPREFIXLEN)
629 return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read loose backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
630
ecd6fdf1 631 if (len >= GIT_OID_HEXSZ) {
aea8a638 632 /* We can fall back to regular read method */
ecd6fdf1
MP
633 int error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
634 if (error == GIT_SUCCESS)
635 git_oid_cpy(out_oid, short_oid);
636
637 return error;
aea8a638
MP
638 } else {
639 char object_path[GIT_PATH_MAX];
640 git_rawobj raw;
641 int error;
642
643 assert(backend && short_oid);
644
645 if ((error = locate_object_short_oid(object_path, out_oid, (loose_backend *)backend, short_oid, len)) < 0) {
646 return git__rethrow(error, "Failed to read loose backend");
647 }
648
649 if ((error = read_loose(&raw, object_path)) < GIT_SUCCESS)
650 return git__rethrow(error, "Failed to read loose backend");
651
652 *buffer_p = raw.data;
653 *len_p = raw.len;
654 *type_p = raw.type;
ecd6fdf1 655 }
aea8a638
MP
656
657 return GIT_SUCCESS;
ecd6fdf1
MP
658}
659
7d7cd885
VM
660int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
661{
662 char object_path[GIT_PATH_MAX];
663
664 assert(backend && oid);
665
666 return locate_object(object_path, (loose_backend *)backend, oid) == GIT_SUCCESS;
667}
668
72a3fe42
VM
669int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream)
670{
671 loose_writestream *stream = (loose_writestream *)_stream;
672 loose_backend *backend = (loose_backend *)_stream->backend;
673
674 int error;
675 char final_path[GIT_PATH_MAX];
676
677 if ((error = git_filebuf_hash(oid, &stream->fbuf)) < GIT_SUCCESS)
dfb12cd5 678 return git__rethrow(error, "Failed to write loose backend");
72a3fe42
VM
679
680 if (object_file_name(final_path, sizeof(final_path), backend->objects_dir, oid))
681 return GIT_ENOMEM;
7d7cd885 682
72a3fe42 683 if ((error = gitfo_mkdir_2file(final_path)) < GIT_SUCCESS)
dfb12cd5 684 return git__rethrow(error, "Failed to write loose backend");
72a3fe42
VM
685
686 stream->finished = 1;
687 return git_filebuf_commit_at(&stream->fbuf, final_path);
688}
689
690int loose_backend__stream_write(git_odb_stream *_stream, const char *data, size_t len)
7d7cd885 691{
72a3fe42
VM
692 loose_writestream *stream = (loose_writestream *)_stream;
693 return git_filebuf_write(&stream->fbuf, data, len);
694}
695
696void loose_backend__stream_free(git_odb_stream *_stream)
697{
698 loose_writestream *stream = (loose_writestream *)_stream;
699
700 if (!stream->finished)
701 git_filebuf_cleanup(&stream->fbuf);
702
703 free(stream);
704}
705
706static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type)
707{
708 const char *type_str = git_object_type2string(obj_type);
709 int len = snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len);
710
711 assert(len > 0); /* otherwise snprintf() is broken */
712 assert(((size_t) len) < n); /* otherwise the caller is broken! */
713
714 if (len < 0 || ((size_t) len) >= n)
dfb12cd5 715 return git__throw(GIT_ERROR, "Failed to format object header. Length is out of bounds");
72a3fe42
VM
716 return len+1;
717}
718
719int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, size_t length, git_otype type)
720{
721 loose_backend *backend;
722 loose_writestream *stream;
723
f6f72d7e 724 char hdr[64], tmp_path[GIT_PATH_MAX];
7d7cd885 725 int hdrlen;
7d7cd885 726 int error;
7d7cd885 727
72a3fe42 728 assert(_backend);
7d7cd885
VM
729
730 backend = (loose_backend *)_backend;
72a3fe42 731 *stream_out = NULL;
7d7cd885 732
72a3fe42
VM
733 hdrlen = format_object_header(hdr, sizeof(hdr), length, type);
734 if (hdrlen < GIT_SUCCESS)
dfb12cd5 735 return git__throw(GIT_EOBJCORRUPTED, "Failed to create loose backend stream. Object is corrupted");
72a3fe42
VM
736
737 stream = git__calloc(1, sizeof(loose_writestream));
738 if (stream == NULL)
739 return GIT_ENOMEM;
7d7cd885 740
72a3fe42
VM
741 stream->stream.backend = _backend;
742 stream->stream.read = NULL; /* read only */
743 stream->stream.write = &loose_backend__stream_write;
744 stream->stream.finalize_write = &loose_backend__stream_fwrite;
745 stream->stream.free = &loose_backend__stream_free;
746 stream->stream.mode = GIT_STREAM_WRONLY;
7d7cd885 747
f6f72d7e
VM
748 git__joinpath(tmp_path, backend->objects_dir, "tmp_object");
749
750 error = git_filebuf_open(&stream->fbuf, tmp_path,
72a3fe42
VM
751 GIT_FILEBUF_HASH_CONTENTS |
752 GIT_FILEBUF_DEFLATE_CONTENTS |
753 GIT_FILEBUF_TEMPORARY);
754
755 if (error < GIT_SUCCESS) {
756 free(stream);
dfb12cd5 757 return git__rethrow(error, "Failed to create loose backend stream");
72a3fe42 758 }
7d7cd885 759
72a3fe42
VM
760 error = stream->stream.write((git_odb_stream *)stream, hdr, hdrlen);
761 if (error < GIT_SUCCESS) {
762 git_filebuf_cleanup(&stream->fbuf);
763 free(stream);
dfb12cd5 764 return git__rethrow(error, "Failed to create loose backend stream");
72a3fe42 765 }
7d7cd885 766
72a3fe42
VM
767 *stream_out = (git_odb_stream *)stream;
768 return GIT_SUCCESS;
7d7cd885
VM
769}
770
771void loose_backend__free(git_odb_backend *_backend)
772{
773 loose_backend *backend;
774 assert(_backend);
775 backend = (loose_backend *)_backend;
776
777 free(backend->objects_dir);
778 free(backend);
779}
780
781int git_odb_backend_loose(git_odb_backend **backend_out, const char *objects_dir)
782{
783 loose_backend *backend;
784
785 backend = git__calloc(1, sizeof(loose_backend));
786 if (backend == NULL)
787 return GIT_ENOMEM;
788
789 backend->objects_dir = git__strdup(objects_dir);
790 if (backend->objects_dir == NULL) {
791 free(backend);
792 return GIT_ENOMEM;
793 }
794
795 backend->object_zlib_level = Z_BEST_SPEED;
796 backend->fsync_object_files = 0;
797
798 backend->parent.read = &loose_backend__read;
d0323a5f 799 backend->parent.read_prefix = &loose_backend__read_prefix;
7d7cd885 800 backend->parent.read_header = &loose_backend__read_header;
72a3fe42 801 backend->parent.writestream = &loose_backend__stream;
7d7cd885
VM
802 backend->parent.exists = &loose_backend__exists;
803 backend->parent.free = &loose_backend__free;
804
7d7cd885
VM
805 *backend_out = (git_odb_backend *)backend;
806 return GIT_SUCCESS;
807}