Blob


1 /*
2 * Copyright (c) 2022 Stefan Sperling <stsp@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include <sys/queue.h>
18 #include <sys/tree.h>
19 #include <sys/stat.h>
21 #include <errno.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
28 #include "got_error.h"
29 #include "got_object.h"
30 #include "got_repository.h"
31 #include "got_path.h"
33 #include "got_lib_delta.h"
34 #include "got_lib_object.h"
35 #include "got_lib_object_cache.h"
36 #include "got_lib_object_parse.h"
37 #include "got_lib_pack.h"
38 #include "got_lib_repository.h"
40 const struct got_error *
41 got_object_open_packed(struct got_object **obj, struct got_object_id *id,
42 struct got_repository *repo)
43 {
44 const struct got_error *err = NULL;
45 struct got_pack *pack = NULL;
46 struct got_packidx *packidx = NULL;
47 int idx;
48 char *path_packfile;
50 err = got_repo_search_packidx(&packidx, &idx, repo, id);
51 if (err)
52 return err;
54 err = got_packidx_get_packfile_path(&path_packfile,
55 packidx->path_packidx);
56 if (err)
57 return err;
59 pack = got_repo_get_cached_pack(repo, path_packfile);
60 if (pack == NULL) {
61 err = got_repo_cache_pack(&pack, repo, path_packfile, packidx);
62 if (err)
63 goto done;
64 }
66 err = got_packfile_open_object(obj, pack, packidx, idx, id);
67 if (err)
68 return err;
69 (*obj)->refcnt++;
71 err = got_repo_cache_object(repo, id, *obj);
72 if (err) {
73 if (err->code == GOT_ERR_OBJ_EXISTS ||
74 err->code == GOT_ERR_OBJ_TOO_LARGE)
75 err = NULL;
76 }
77 done:
78 free(path_packfile);
79 return err;
80 }
82 const struct got_error *
83 got_object_open_from_packfile(struct got_object **obj, struct got_object_id *id,
84 struct got_pack *pack, struct got_packidx *packidx, int obj_idx,
85 struct got_repository *repo)
86 {
87 return got_error(GOT_ERR_NOT_IMPL);
88 }
90 const struct got_error *
91 got_object_read_raw_delta(uint64_t *base_size, uint64_t *result_size,
92 off_t *delta_size, off_t *delta_compressed_size, off_t *delta_offset,
93 off_t *delta_out_offset, struct got_object_id **base_id, int delta_cache_fd,
94 struct got_packidx *packidx, int obj_idx, struct got_object_id *id,
95 struct got_repository *repo)
96 {
97 return got_error(GOT_ERR_NOT_IMPL);
98 }
100 const struct got_error *
101 got_object_open(struct got_object **obj, struct got_repository *repo,
102 struct got_object_id *id)
104 const struct got_error *err = NULL;
105 int fd;
107 *obj = got_repo_get_cached_object(repo, id);
108 if (*obj != NULL) {
109 (*obj)->refcnt++;
110 return NULL;
113 err = got_object_open_packed(obj, id, repo);
114 if (err) {
115 if (err->code != GOT_ERR_NO_OBJ)
116 return err;
117 } else
118 return NULL;
120 err = got_object_open_loose_fd(&fd, id, repo);
121 if (err) {
122 if (err->code == GOT_ERR_ERRNO && errno == ENOENT)
123 err = got_error_no_obj(id);
124 return err;
127 err = got_object_read_header(obj, fd);
128 if (err)
129 goto done;
131 memcpy(&(*obj)->id, id, sizeof((*obj)->id));
132 (*obj)->refcnt++;
134 err = got_repo_cache_object(repo, id, *obj);
135 if (err) {
136 if (err->code == GOT_ERR_OBJ_EXISTS ||
137 err->code == GOT_ERR_OBJ_TOO_LARGE)
138 err = NULL;
140 done:
141 if (close(fd) == -1 && err == NULL)
142 err = got_error_from_errno("close");
143 return err;
146 static const struct got_error *
147 wrap_fd(FILE **f, int wrapped_fd)
149 const struct got_error *err = NULL;
150 int fd;
152 if (ftruncate(wrapped_fd, 0L) == -1)
153 return got_error_from_errno("ftruncate");
155 if (lseek(wrapped_fd, 0L, SEEK_SET) == -1)
156 return got_error_from_errno("lseek");
158 fd = dup(wrapped_fd);
159 if (fd == -1)
160 return got_error_from_errno("dup");
162 *f = fdopen(fd, "w+");
163 if (*f == NULL) {
164 err = got_error_from_errno("fdopen");
165 close(fd);
167 return err;
170 static const struct got_error *
171 read_packed_object_raw(uint8_t **outbuf, off_t *size, size_t *hdrlen,
172 int outfd, struct got_pack *pack, struct got_packidx *packidx, int idx,
173 struct got_object_id *id)
175 const struct got_error *err = NULL;
176 uint64_t raw_size = 0;
177 struct got_object *obj;
178 FILE *outfile = NULL, *basefile = NULL, *accumfile = NULL;
180 *outbuf = NULL;
181 *size = 0;
182 *hdrlen = 0;
184 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
185 if (err)
186 return err;
188 if (obj->flags & GOT_OBJ_FLAG_DELTIFIED) {
189 err = got_pack_get_max_delta_object_size(&raw_size, obj, pack);
190 if (err)
191 goto done;
192 } else
193 raw_size = obj->size;
195 if (raw_size <= GOT_DELTA_RESULT_SIZE_CACHED_MAX) {
196 size_t len;
197 err = got_packfile_extract_object_to_mem(outbuf, &len,
198 obj, pack);
199 if (err)
200 goto done;
201 *size = (off_t)len;
202 } else {
203 /*
204 * XXX This uses 3 file extra descriptors for no good reason.
205 * We should have got_packfile_extract_object_to_fd().
206 */
207 err = wrap_fd(&outfile, outfd);
208 if (err)
209 goto done;
210 err = wrap_fd(&basefile, pack->basefd);
211 if (err)
212 goto done;
213 err = wrap_fd(&accumfile, pack->accumfd);
214 if (err)
215 goto done;
216 err = got_packfile_extract_object(pack, obj, outfile, basefile,
217 accumfile);
218 if (err)
219 goto done;
220 *size = obj->size;
223 *hdrlen = obj->hdrlen;
224 done:
225 got_object_close(obj);
226 if (outfile && fclose(outfile) == EOF && err == NULL)
227 err = got_error_from_errno("fclose");
228 if (basefile && fclose(basefile) == EOF && err == NULL)
229 err = got_error_from_errno("fclose");
230 if (accumfile && fclose(accumfile) == EOF && err == NULL)
231 err = got_error_from_errno("fclose");
232 return err;
236 static void
237 put_raw_object_tempfile(struct got_raw_object *obj)
239 struct got_repository *repo = obj->close_arg;
241 if (obj->tempfile_idx != -1)
242 got_repo_temp_fds_put(obj->tempfile_idx, repo);
245 /* *outfd must be initialized to -1 by caller */
246 const struct got_error *
247 got_object_raw_open(struct got_raw_object **obj, int *outfd,
248 struct got_repository *repo, struct got_object_id *id)
250 const struct got_error *err = NULL;
251 struct got_packidx *packidx = NULL;
252 int idx, tempfd, tempfile_idx;
253 uint8_t *outbuf = NULL;
254 off_t size = 0;
255 size_t hdrlen = 0;
256 char *path_packfile = NULL;
258 *obj = got_repo_get_cached_raw_object(repo, id);
259 if (*obj != NULL) {
260 (*obj)->refcnt++;
261 return NULL;
264 err = got_repo_temp_fds_get(&tempfd, &tempfile_idx, repo);
265 if (err)
266 return err;
268 err = got_repo_search_packidx(&packidx, &idx, repo, id);
269 if (err == NULL) {
270 struct got_pack *pack = NULL;
272 err = got_packidx_get_packfile_path(&path_packfile,
273 packidx->path_packidx);
274 if (err)
275 goto done;
277 pack = got_repo_get_cached_pack(repo, path_packfile);
278 if (pack == NULL) {
279 err = got_repo_cache_pack(&pack, repo, path_packfile,
280 packidx);
281 if (err)
282 goto done;
284 err = read_packed_object_raw(&outbuf, &size, &hdrlen,
285 tempfd, pack, packidx, idx, id);
286 if (err)
287 goto done;
288 } else if (err->code == GOT_ERR_NO_OBJ) {
289 int fd;
291 err = got_object_open_loose_fd(&fd, id, repo);
292 if (err)
293 goto done;
294 err = got_object_read_raw(&outbuf, &size, &hdrlen,
295 GOT_DELTA_RESULT_SIZE_CACHED_MAX, tempfd, id, fd);
296 if (close(fd) == -1 && err == NULL)
297 err = got_error_from_errno("close");
298 if (err)
299 goto done;
302 if (outbuf == NULL) {
303 if (*outfd != -1) {
304 err = got_error_msg(GOT_ERR_NOT_IMPL, "bad outfd");
305 goto done;
308 /*
309 * Duplicate tempfile descriptor to allow use of
310 * fdopen(3) inside got_object_raw_alloc().
311 */
312 *outfd = dup(tempfd);
313 if (*outfd == -1) {
314 err = got_error_from_errno("dup");
315 goto done;
319 err = got_object_raw_alloc(obj, outbuf, outfd,
320 GOT_DELTA_RESULT_SIZE_CACHED_MAX, hdrlen, size);
321 if (err)
322 goto done;
324 err = got_repo_cache_raw_object(repo, id, *obj);
325 if (err) {
326 if (err->code == GOT_ERR_OBJ_EXISTS ||
327 err->code == GOT_ERR_OBJ_TOO_LARGE)
328 err = NULL;
330 done:
331 free(path_packfile);
332 if (err) {
333 if (*obj) {
334 got_object_raw_close(*obj);
335 *obj = NULL;
337 free(outbuf);
338 got_repo_temp_fds_put(tempfile_idx, repo);
339 if (*outfd != -1) {
340 close(*outfd);
341 *outfd = -1;
343 } else {
344 if (((*obj)->f == NULL && (*obj)->fd == -1)) {
345 /* This raw object is not backed by a file. */
346 got_repo_temp_fds_put(tempfile_idx, repo);
347 if (*outfd != -1) {
348 close(*outfd);
349 *outfd = -1;
351 } else {
352 (*obj)->tempfile_idx = tempfile_idx;
353 (*obj)->close_cb = put_raw_object_tempfile;
354 (*obj)->close_arg = repo;
357 return err;
360 static const struct got_error *
361 open_commit(struct got_commit_object **commit,
362 struct got_repository *repo, struct got_object_id *id, int check_cache)
364 const struct got_error *err = NULL;
365 struct got_packidx *packidx = NULL;
366 int idx;
367 char *path_packfile = NULL;
369 if (check_cache) {
370 *commit = got_repo_get_cached_commit(repo, id);
371 if (*commit != NULL) {
372 (*commit)->refcnt++;
373 return NULL;
375 } else
376 *commit = NULL;
378 err = got_repo_search_packidx(&packidx, &idx, repo, id);
379 if (err == NULL) {
380 struct got_pack *pack = NULL;
381 struct got_object *obj;
382 uint8_t *buf;
383 size_t len;
385 err = got_packidx_get_packfile_path(&path_packfile,
386 packidx->path_packidx);
387 if (err)
388 return err;
390 pack = got_repo_get_cached_pack(repo, path_packfile);
391 if (pack == NULL) {
392 err = got_repo_cache_pack(&pack, repo, path_packfile,
393 packidx);
394 if (err)
395 goto done;
397 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
398 if (err)
399 goto done;
400 err = got_packfile_extract_object_to_mem(&buf, &len,
401 obj, pack);
402 got_object_close(obj);
403 if (err)
404 goto done;
405 err = got_object_parse_commit(commit, buf, len);
406 free(buf);
407 } else if (err->code == GOT_ERR_NO_OBJ) {
408 int fd;
410 err = got_object_open_loose_fd(&fd, id, repo);
411 if (err)
412 return err;
413 err = got_object_read_commit(commit, fd, id, 0);
414 if (close(fd) == -1 && err == NULL)
415 err = got_error_from_errno("close");
416 if (err)
417 return err;
420 if (err == NULL) {
421 (*commit)->refcnt++;
422 err = got_repo_cache_commit(repo, id, *commit);
423 if (err) {
424 if (err->code == GOT_ERR_OBJ_EXISTS ||
425 err->code == GOT_ERR_OBJ_TOO_LARGE)
426 err = NULL;
429 done:
430 free(path_packfile);
431 return err;
434 const struct got_error *
435 got_object_open_as_commit(struct got_commit_object **commit,
436 struct got_repository *repo, struct got_object_id *id)
438 *commit = got_repo_get_cached_commit(repo, id);
439 if (*commit != NULL) {
440 (*commit)->refcnt++;
441 return NULL;
444 return open_commit(commit, repo, id, 0);
447 const struct got_error *
448 got_object_commit_open(struct got_commit_object **commit,
449 struct got_repository *repo, struct got_object *obj)
451 return open_commit(commit, repo, got_object_get_id(obj), 1);
454 static const struct got_error *
455 open_tree(struct got_tree_object **tree,
456 struct got_repository *repo, struct got_object_id *id, int check_cache)
458 const struct got_error *err = NULL;
459 struct got_packidx *packidx = NULL;
460 int idx;
461 char *path_packfile = NULL;
462 struct got_parsed_tree_entry *entries = NULL;
463 size_t nentries = 0, nentries_alloc = 0, i;
464 uint8_t *buf = NULL;
466 if (check_cache) {
467 *tree = got_repo_get_cached_tree(repo, id);
468 if (*tree != NULL) {
469 (*tree)->refcnt++;
470 return NULL;
472 } else
473 *tree = NULL;
475 err = got_repo_search_packidx(&packidx, &idx, repo, id);
476 if (err == NULL) {
477 struct got_pack *pack = NULL;
478 struct got_object *obj;
479 size_t len;
481 err = got_packidx_get_packfile_path(&path_packfile,
482 packidx->path_packidx);
483 if (err)
484 return err;
486 pack = got_repo_get_cached_pack(repo, path_packfile);
487 if (pack == NULL) {
488 err = got_repo_cache_pack(&pack, repo, path_packfile,
489 packidx);
490 if (err)
491 goto done;
493 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
494 if (err)
495 goto done;
496 err = got_packfile_extract_object_to_mem(&buf, &len,
497 obj, pack);
498 got_object_close(obj);
499 if (err)
500 goto done;
501 err = got_object_parse_tree(&entries, &nentries,
502 &nentries_alloc, buf, len);
503 if (err)
504 goto done;
505 } else if (err->code == GOT_ERR_NO_OBJ) {
506 int fd;
508 err = got_object_open_loose_fd(&fd, id, repo);
509 if (err)
510 return err;
511 err = got_object_read_tree(&entries, &nentries,
512 &nentries_alloc, &buf, fd, id);
513 if (close(fd) == -1 && err == NULL)
514 err = got_error_from_errno("close");
515 if (err)
516 goto done;
517 } else
518 goto done;
520 *tree = malloc(sizeof(**tree));
521 if (*tree == NULL) {
522 err = got_error_from_errno("malloc");
523 goto done;
525 (*tree)->entries = calloc(nentries, sizeof(struct got_tree_entry));
526 if ((*tree)->entries == NULL) {
527 err = got_error_from_errno("malloc");
528 goto done;
530 (*tree)->nentries = nentries;
531 (*tree)->refcnt = 0;
533 for (i = 0; i < nentries; i++) {
534 struct got_parsed_tree_entry *pe = &entries[i];
535 struct got_tree_entry *te = &(*tree)->entries[i];
537 if (strlcpy(te->name, pe->name,
538 sizeof(te->name)) >= sizeof(te->name)) {
539 err = got_error(GOT_ERR_NO_SPACE);
540 goto done;
542 memcpy(te->id.sha1, pe->id, SHA1_DIGEST_LENGTH);
543 te->mode = pe->mode;
544 te->idx = i;
546 done:
547 free(path_packfile);
548 free(entries);
549 free(buf);
550 if (err == NULL) {
551 (*tree)->refcnt++;
552 err = got_repo_cache_tree(repo, id, *tree);
553 if (err) {
554 if (err->code == GOT_ERR_OBJ_EXISTS ||
555 err->code == GOT_ERR_OBJ_TOO_LARGE)
556 err = NULL;
559 if (err) {
560 if (*tree)
561 free((*tree)->entries);
562 free(*tree);
563 *tree = NULL;
565 return err;
568 const struct got_error *
569 got_object_open_as_tree(struct got_tree_object **tree,
570 struct got_repository *repo, struct got_object_id *id)
572 *tree = got_repo_get_cached_tree(repo, id);
573 if (*tree != NULL) {
574 (*tree)->refcnt++;
575 return NULL;
578 return open_tree(tree, repo, id, 0);
581 const struct got_error *
582 got_object_tree_open(struct got_tree_object **tree,
583 struct got_repository *repo, struct got_object *obj)
585 return open_tree(tree, repo, got_object_get_id(obj), 1);
588 const struct got_error *
589 got_object_open_as_blob(struct got_blob_object **blob,
590 struct got_repository *repo, struct got_object_id *id, size_t blocksize,
591 int outfd)
593 return got_error(GOT_ERR_NOT_IMPL);
596 const struct got_error *
597 got_object_blob_open(struct got_blob_object **blob,
598 struct got_repository *repo, struct got_object *obj, size_t blocksize,
599 int outfd)
601 return got_error(GOT_ERR_NOT_IMPL);
604 static const struct got_error *
605 open_tag(struct got_tag_object **tag, struct got_repository *repo,
606 struct got_object_id *id, int check_cache)
608 const struct got_error *err = NULL;
609 struct got_packidx *packidx = NULL;
610 int idx;
611 char *path_packfile = NULL;
612 struct got_object *obj = NULL;
613 int obj_type = GOT_OBJ_TYPE_ANY;
615 if (check_cache) {
616 *tag = got_repo_get_cached_tag(repo, id);
617 if (*tag != NULL) {
618 (*tag)->refcnt++;
619 return NULL;
621 } else
622 *tag = NULL;
624 err = got_repo_search_packidx(&packidx, &idx, repo, id);
625 if (err == NULL) {
626 struct got_pack *pack = NULL;
627 uint8_t *buf = NULL;
628 size_t len;
630 err = got_packidx_get_packfile_path(&path_packfile,
631 packidx->path_packidx);
632 if (err)
633 return err;
635 pack = got_repo_get_cached_pack(repo, path_packfile);
636 if (pack == NULL) {
637 err = got_repo_cache_pack(&pack, repo, path_packfile,
638 packidx);
639 if (err)
640 goto done;
643 /* Beware of "lightweight" tags: Check object type first. */
644 err = got_packfile_open_object(&obj, pack, packidx, idx, id);
645 if (err)
646 goto done;
647 obj_type = obj->type;
648 if (obj_type != GOT_OBJ_TYPE_TAG) {
649 err = got_error(GOT_ERR_OBJ_TYPE);
650 got_object_close(obj);
651 goto done;
653 err = got_packfile_extract_object_to_mem(&buf, &len,
654 obj, pack);
655 got_object_close(obj);
656 if (err)
657 goto done;
658 err = got_object_parse_tag(tag, buf, len);
659 free(buf);
660 } else if (err->code == GOT_ERR_NO_OBJ) {
661 int fd;
663 err = got_object_open_loose_fd(&fd, id, repo);
664 if (err)
665 return err;
666 err = got_object_read_header(&obj, fd);
667 if (close(fd) == -1 && err == NULL)
668 err = got_error_from_errno("close");
669 if (err)
670 return err;
671 obj_type = obj->type;
672 got_object_close(obj);
673 if (obj_type != GOT_OBJ_TYPE_TAG)
674 return got_error(GOT_ERR_OBJ_TYPE);
676 err = got_object_open_loose_fd(&fd, id, repo);
677 if (err)
678 return err;
679 err = got_object_read_tag(tag, fd, id, 0);
680 if (close(fd) == -1 && err == NULL)
681 err = got_error_from_errno("close");
682 if (err)
683 return err;
686 if (err == NULL) {
687 (*tag)->refcnt++;
688 err = got_repo_cache_tag(repo, id, *tag);
689 if (err) {
690 if (err->code == GOT_ERR_OBJ_EXISTS ||
691 err->code == GOT_ERR_OBJ_TOO_LARGE)
692 err = NULL;
695 done:
696 free(path_packfile);
697 return err;
700 const struct got_error *
701 got_object_open_as_tag(struct got_tag_object **tag,
702 struct got_repository *repo, struct got_object_id *id)
704 *tag = got_repo_get_cached_tag(repo, id);
705 if (*tag != NULL) {
706 (*tag)->refcnt++;
707 return NULL;
710 return open_tag(tag, repo, id, 0);
713 const struct got_error *
714 got_object_tag_open(struct got_tag_object **tag,
715 struct got_repository *repo, struct got_object *obj)
717 return open_tag(tag, repo, got_object_get_id(obj), 1);