]> git.proxmox.com Git - libgit2.git/blame - src/odb_loose.c
Update Clay script
[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"
f1d01851 29#include "git2/oid.h"
7d7cd885
VM
30#include "fileops.h"
31#include "hash.h"
32#include "odb.h"
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
f79026b4 100static size_t get_binary_object_header(obj_hdr *hdr, git_fbuffer *obj)
7d7cd885
VM
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;
1c3fac4d 186 s->avail_out = (uInt)len;
7d7cd885
VM
187}
188
189static void set_stream_input(z_stream *s, void *in, size_t len)
190{
191 s->next_in = in;
1c3fac4d 192 s->avail_in = (uInt)len;
7d7cd885
VM
193}
194
195static void set_stream_output(z_stream *s, void *out, size_t len)
196{
197 s->next_out = out;
1c3fac4d 198 s->avail_out = (uInt)len;
7d7cd885
VM
199}
200
201
f79026b4 202static int start_inflate(z_stream *s, git_fbuffer *obj, void *out, size_t len)
7d7cd885
VM
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 245 zs.next_out = out;
1c3fac4d 246 zs.avail_out = (uInt)outlen;
7d7cd885 247
72a3fe42 248 zs.next_in = in;
1c3fac4d 249 zs.avail_in = (uInt)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 */
f79026b4 312static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_fbuffer *obj)
7d7cd885
VM
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
f79026b4 350static int inflate_disk_obj(git_rawobj *out, git_fbuffer *obj)
7d7cd885
VM
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;
f79026b4 408 git_fbuffer obj = GIT_FBUFFER_INIT;
7d7cd885
VM
409
410 assert(out && loc);
411
412 out->data = NULL;
413 out->len = 0;
414 out->type = GIT_OBJ_BAD;
415
f79026b4 416 if (git_futils_readbuffer(&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);
f79026b4 420 git_futils_freebuffer(&obj);
7d7cd885 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
f79026b4 437 if ((fd = p_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);
f79026b4 469 p_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);
f79026b4 480 return git_futils_exists(object_location);
7d7cd885
VM
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
f79026b4 493 if (!git_futils_exists(pathbuf) && git_futils_isdir(pathbuf)) {
f1d01851
VM
494 /* We are already in the directory matching the 2 first hex characters,
495 * compare the first ncmp characters of the oids */
496 if (!memcmp(sstate->short_oid + 2,
497 (unsigned char *)pathbuf + sstate->dir_len,
498 sstate->short_oid_len - 2)) {
499
aea8a638
MP
500 if (!sstate->found) {
501 sstate->res_oid[0] = sstate->short_oid[0];
502 sstate->res_oid[1] = sstate->short_oid[1];
503 memcpy(sstate->res_oid+2, pathbuf+sstate->dir_len, GIT_OID_HEXSZ-2);
504 }
505 sstate->found++;
506 }
507 }
d0323a5f 508 if (sstate->found > 1)
aea8a638 509 return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Ambiguous sha1 prefix within loose objects");
d0323a5f
VM
510
511 return GIT_SUCCESS;
aea8a638
MP
512}
513
514/* Locate an object matching a given short oid */
515static int locate_object_short_oid(char *object_location, git_oid *res_oid, loose_backend *backend, const git_oid *short_oid, unsigned int len)
516{
517 char *objects_dir = backend->objects_dir;
518 size_t dir_len = strlen(objects_dir);
519 loose_locate_object_state state;
520 int error;
521
522 if (dir_len+43 > GIT_PATH_MAX)
523 return git__throw(GIT_ERROR, "Failed to locate object from short oid. Object path too long");
524
525 strcpy(object_location, objects_dir);
526
527 /* Add a separator if not already there */
528 if (object_location[dir_len-1] != '/')
529 object_location[dir_len++] = '/';
530
531 /* Convert raw oid to hex formatted oid */
d0323a5f 532 git_oid_fmt((char *)state.short_oid, short_oid);
aea8a638
MP
533 /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */
534 sprintf(object_location+dir_len, "%.2s/", state.short_oid);
535
536 /* Check that directory exists */
f79026b4 537 if (git_futils_exists(object_location) || git_futils_isdir(object_location))
aea8a638
MP
538 return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found");
539
540 state.dir_len = dir_len+3;
541 state.short_oid_len = len;
542 state.found = 0;
543 /* Explore directory to find a unique object matching short_oid */
f79026b4 544 error = git_futils_direach(object_location, GIT_PATH_MAX, fn_locate_object_short_oid, &state);
aea8a638
MP
545 if (error) {
546 return git__rethrow(error, "Failed to locate object from short oid");
547 }
548 if (!state.found) {
549 return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found");
550 }
551
552 /* Convert obtained hex formatted oid to raw */
fa48608e 553 error = git_oid_fromstr(res_oid, (char *)state.res_oid);
aea8a638
MP
554 if (error) {
555 return git__rethrow(error, "Failed to locate object from short oid");
556 }
557
558 /* Update the location according to the oid obtained */
559 git_oid_pathfmt(object_location+dir_len, res_oid);
560
561 return GIT_SUCCESS;
562}
563
7d7cd885
VM
564
565
566
567
568
569
570
571
572/***********************************************************
573 *
574 * LOOSE BACKEND PUBLIC API
575 *
576 * Implement the git_odb_backend API calls
577 *
578 ***********************************************************/
579
72a3fe42 580int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
7d7cd885
VM
581{
582 char object_path[GIT_PATH_MAX];
72a3fe42
VM
583 git_rawobj raw;
584 int error;
7d7cd885 585
72a3fe42 586 assert(backend && oid);
7d7cd885 587
60e1b49a
VM
588 raw.len = 0;
589 raw.type = GIT_OBJ_BAD;
590
7d7cd885 591 if (locate_object(object_path, (loose_backend *)backend, oid) < 0)
dfb12cd5 592 return git__throw(GIT_ENOTFOUND, "Failed to read loose backend header. Object not found");
7d7cd885 593
72a3fe42 594 if ((error = read_header_loose(&raw, object_path)) < GIT_SUCCESS)
60e1b49a 595 return error;
7d7cd885 596
72a3fe42
VM
597 *len_p = raw.len;
598 *type_p = raw.type;
599 return GIT_SUCCESS;
600}
7d7cd885 601
72a3fe42 602int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
7d7cd885
VM
603{
604 char object_path[GIT_PATH_MAX];
72a3fe42
VM
605 git_rawobj raw;
606 int error;
7d7cd885 607
72a3fe42 608 assert(backend && oid);
7d7cd885
VM
609
610 if (locate_object(object_path, (loose_backend *)backend, oid) < 0)
dfb12cd5 611 return git__throw(GIT_ENOTFOUND, "Failed to read loose backend. Object not found");
7d7cd885 612
72a3fe42 613 if ((error = read_loose(&raw, object_path)) < GIT_SUCCESS)
dfb12cd5 614 return git__rethrow(error, "Failed to read loose backend");
72a3fe42
VM
615
616 *buffer_p = raw.data;
617 *len_p = raw.len;
618 *type_p = raw.type;
619
620 return GIT_SUCCESS;
7d7cd885
VM
621}
622
d0323a5f
VM
623int loose_backend__read_prefix(
624 git_oid *out_oid,
625 void **buffer_p,
626 size_t *len_p,
627 git_otype *type_p,
628 git_odb_backend *backend,
629 const git_oid *short_oid,
630 unsigned int len)
ecd6fdf1 631{
aea8a638
MP
632 if (len < GIT_OID_MINPREFIXLEN)
633 return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read loose backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
634
ecd6fdf1 635 if (len >= GIT_OID_HEXSZ) {
aea8a638 636 /* We can fall back to regular read method */
ecd6fdf1
MP
637 int error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
638 if (error == GIT_SUCCESS)
639 git_oid_cpy(out_oid, short_oid);
640
641 return error;
aea8a638
MP
642 } else {
643 char object_path[GIT_PATH_MAX];
644 git_rawobj raw;
645 int error;
646
647 assert(backend && short_oid);
648
649 if ((error = locate_object_short_oid(object_path, out_oid, (loose_backend *)backend, short_oid, len)) < 0) {
650 return git__rethrow(error, "Failed to read loose backend");
651 }
652
653 if ((error = read_loose(&raw, object_path)) < GIT_SUCCESS)
654 return git__rethrow(error, "Failed to read loose backend");
655
656 *buffer_p = raw.data;
657 *len_p = raw.len;
658 *type_p = raw.type;
ecd6fdf1 659 }
aea8a638
MP
660
661 return GIT_SUCCESS;
ecd6fdf1
MP
662}
663
7d7cd885
VM
664int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
665{
666 char object_path[GIT_PATH_MAX];
667
668 assert(backend && oid);
669
670 return locate_object(object_path, (loose_backend *)backend, oid) == GIT_SUCCESS;
671}
672
72a3fe42
VM
673int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream)
674{
675 loose_writestream *stream = (loose_writestream *)_stream;
676 loose_backend *backend = (loose_backend *)_stream->backend;
677
678 int error;
679 char final_path[GIT_PATH_MAX];
680
681 if ((error = git_filebuf_hash(oid, &stream->fbuf)) < GIT_SUCCESS)
dfb12cd5 682 return git__rethrow(error, "Failed to write loose backend");
72a3fe42
VM
683
684 if (object_file_name(final_path, sizeof(final_path), backend->objects_dir, oid))
685 return GIT_ENOMEM;
7d7cd885 686
f79026b4 687 if ((error = git_futils_mkpath2file(final_path)) < GIT_SUCCESS)
dfb12cd5 688 return git__rethrow(error, "Failed to write loose backend");
72a3fe42
VM
689
690 stream->finished = 1;
691 return git_filebuf_commit_at(&stream->fbuf, final_path);
692}
693
694int loose_backend__stream_write(git_odb_stream *_stream, const char *data, size_t len)
7d7cd885 695{
72a3fe42
VM
696 loose_writestream *stream = (loose_writestream *)_stream;
697 return git_filebuf_write(&stream->fbuf, data, len);
698}
699
700void loose_backend__stream_free(git_odb_stream *_stream)
701{
702 loose_writestream *stream = (loose_writestream *)_stream;
703
704 if (!stream->finished)
705 git_filebuf_cleanup(&stream->fbuf);
706
707 free(stream);
708}
709
710static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type)
711{
712 const char *type_str = git_object_type2string(obj_type);
713 int len = snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len);
714
715 assert(len > 0); /* otherwise snprintf() is broken */
716 assert(((size_t) len) < n); /* otherwise the caller is broken! */
717
718 if (len < 0 || ((size_t) len) >= n)
dfb12cd5 719 return git__throw(GIT_ERROR, "Failed to format object header. Length is out of bounds");
72a3fe42
VM
720 return len+1;
721}
722
723int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, size_t length, git_otype type)
724{
725 loose_backend *backend;
726 loose_writestream *stream;
727
f6f72d7e 728 char hdr[64], tmp_path[GIT_PATH_MAX];
7d7cd885 729 int hdrlen;
7d7cd885 730 int error;
7d7cd885 731
72a3fe42 732 assert(_backend);
7d7cd885
VM
733
734 backend = (loose_backend *)_backend;
72a3fe42 735 *stream_out = NULL;
7d7cd885 736
72a3fe42
VM
737 hdrlen = format_object_header(hdr, sizeof(hdr), length, type);
738 if (hdrlen < GIT_SUCCESS)
dfb12cd5 739 return git__throw(GIT_EOBJCORRUPTED, "Failed to create loose backend stream. Object is corrupted");
72a3fe42
VM
740
741 stream = git__calloc(1, sizeof(loose_writestream));
742 if (stream == NULL)
743 return GIT_ENOMEM;
7d7cd885 744
72a3fe42
VM
745 stream->stream.backend = _backend;
746 stream->stream.read = NULL; /* read only */
747 stream->stream.write = &loose_backend__stream_write;
748 stream->stream.finalize_write = &loose_backend__stream_fwrite;
749 stream->stream.free = &loose_backend__stream_free;
750 stream->stream.mode = GIT_STREAM_WRONLY;
7d7cd885 751
f79026b4 752 git_path_join(tmp_path, backend->objects_dir, "tmp_object");
f6f72d7e
VM
753
754 error = git_filebuf_open(&stream->fbuf, tmp_path,
72a3fe42
VM
755 GIT_FILEBUF_HASH_CONTENTS |
756 GIT_FILEBUF_DEFLATE_CONTENTS |
757 GIT_FILEBUF_TEMPORARY);
758
759 if (error < GIT_SUCCESS) {
760 free(stream);
dfb12cd5 761 return git__rethrow(error, "Failed to create loose backend stream");
72a3fe42 762 }
7d7cd885 763
72a3fe42
VM
764 error = stream->stream.write((git_odb_stream *)stream, hdr, hdrlen);
765 if (error < GIT_SUCCESS) {
766 git_filebuf_cleanup(&stream->fbuf);
767 free(stream);
dfb12cd5 768 return git__rethrow(error, "Failed to create loose backend stream");
72a3fe42 769 }
7d7cd885 770
72a3fe42
VM
771 *stream_out = (git_odb_stream *)stream;
772 return GIT_SUCCESS;
7d7cd885
VM
773}
774
afeecf4f
VM
775int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const void *data, size_t len, git_otype type)
776{
777 int error, header_len;
778 char final_path[GIT_PATH_MAX], header[64];
779 git_filebuf fbuf;
780 loose_backend *backend;
781
782 backend = (loose_backend *)_backend;
783
784 /* prepare the header for the file */
785 {
786 header_len = format_object_header(header, sizeof(header), len, type);
787 if (header_len < GIT_SUCCESS)
788 return GIT_EOBJCORRUPTED;
789 }
790
791 git_path_join(final_path, backend->objects_dir, "tmp_object");
792
793 error = git_filebuf_open(&fbuf, final_path,
794 GIT_FILEBUF_HASH_CONTENTS |
795 GIT_FILEBUF_DEFLATE_CONTENTS |
796 GIT_FILEBUF_TEMPORARY);
797
798 if (error < GIT_SUCCESS)
799 return error;
800
801 git_filebuf_write(&fbuf, header, header_len);
802 git_filebuf_write(&fbuf, data, len);
803 git_filebuf_hash(oid, &fbuf);
804
805 if ((error = object_file_name(final_path, sizeof(final_path), backend->objects_dir, oid)) < GIT_SUCCESS)
806 goto cleanup;
807
808 if ((error = git_futils_mkpath2file(final_path)) < GIT_SUCCESS)
809 goto cleanup;
810
811 return git_filebuf_commit_at(&fbuf, final_path);
812
813cleanup:
814 git_filebuf_cleanup(&fbuf);
815 return error;
816}
817
7d7cd885
VM
818void loose_backend__free(git_odb_backend *_backend)
819{
820 loose_backend *backend;
821 assert(_backend);
822 backend = (loose_backend *)_backend;
823
824 free(backend->objects_dir);
825 free(backend);
826}
827
828int git_odb_backend_loose(git_odb_backend **backend_out, const char *objects_dir)
829{
830 loose_backend *backend;
831
832 backend = git__calloc(1, sizeof(loose_backend));
833 if (backend == NULL)
834 return GIT_ENOMEM;
835
836 backend->objects_dir = git__strdup(objects_dir);
837 if (backend->objects_dir == NULL) {
838 free(backend);
839 return GIT_ENOMEM;
840 }
841
842 backend->object_zlib_level = Z_BEST_SPEED;
843 backend->fsync_object_files = 0;
844
845 backend->parent.read = &loose_backend__read;
afeecf4f 846 backend->parent.write = &loose_backend__write;
d0323a5f 847 backend->parent.read_prefix = &loose_backend__read_prefix;
7d7cd885 848 backend->parent.read_header = &loose_backend__read_header;
72a3fe42 849 backend->parent.writestream = &loose_backend__stream;
7d7cd885
VM
850 backend->parent.exists = &loose_backend__exists;
851 backend->parent.free = &loose_backend__free;
852
7d7cd885
VM
853 *backend_out = (git_odb_backend *)backend;
854 return GIT_SUCCESS;
855}