2 * Copyright (c) 2018 Stefan Sperling <stsp@openbsd.org>
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.
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.
17 #include <sys/types.h>
19 #include <sys/queue.h>
21 #include <sys/socket.h>
37 #include "got_error.h"
38 #include "got_object.h"
39 #include "got_repository.h"
40 #include "got_opentemp.h"
42 #include "got_lib_sha1.h"
43 #include "got_lib_delta.h"
44 #include "got_lib_pack.h"
45 #include "got_lib_path.h"
46 #include "got_lib_inflate.h"
47 #include "got_lib_object.h"
48 #include "got_lib_privsep.h"
49 #include "got_lib_object_parse.h"
50 #include "got_lib_repository.h"
53 #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
56 const struct got_error *
57 got_object_id_str(char **outbuf, struct got_object_id *id)
59 static const size_t len = SHA1_DIGEST_STRING_LENGTH;
61 *outbuf = malloc(len);
63 return got_error_from_errno();
65 if (got_sha1_digest_to_str(id->sha1, *outbuf, len) == NULL) {
68 return got_error(GOT_ERR_BAD_OBJ_ID_STR);
75 got_object_id_cmp(struct got_object_id *id1, struct got_object_id *id2)
77 return memcmp(id1->sha1, id2->sha1, SHA1_DIGEST_LENGTH);
80 struct got_object_id *
81 got_object_id_dup(struct got_object_id *id1)
83 struct got_object_id *id2;
85 id2 = malloc(sizeof(*id2));
88 memcpy(id2, id1, sizeof(*id2));
92 struct got_object_id *
93 got_object_get_id(struct got_object *obj)
95 return got_object_id_dup(&obj->id);
98 const struct got_error *
99 got_object_get_id_str(char **outbuf, struct got_object *obj)
101 return got_object_id_str(outbuf, &obj->id);
105 got_object_get_type(struct got_object *obj)
108 case GOT_OBJ_TYPE_COMMIT:
109 case GOT_OBJ_TYPE_TREE:
110 case GOT_OBJ_TYPE_BLOB:
111 case GOT_OBJ_TYPE_TAG:
122 static const struct got_error *
123 object_path(char **path, struct got_object_id *id, struct got_repository *repo)
125 const struct got_error *err = NULL;
127 char *path_objects = got_repo_get_path_objects(repo);
131 if (path_objects == NULL)
132 return got_error_from_errno();
134 err = got_object_id_str(&hex, id);
138 if (asprintf(path, "%s/%.2x/%s", path_objects,
139 id->sha1[0], hex + 2) == -1)
140 err = got_error_from_errno();
148 static const struct got_error *
149 open_loose_object(int *fd, struct got_object *obj, struct got_repository *repo)
151 const struct got_error *err = NULL;
154 err = object_path(&path, &obj->id, repo);
157 *fd = open(path, O_RDONLY | O_NOFOLLOW, GOT_DEFAULT_FILE_MODE);
159 err = got_error_from_errno();
167 const struct got_error *
168 got_object_open(struct got_object **obj, struct got_repository *repo,
169 struct got_object_id *id)
171 const struct got_error *err = NULL;
175 *obj = got_repo_get_cached_object(repo, id);
181 err = object_path(&path, id, repo);
185 fd = open(path, O_RDONLY | O_NOFOLLOW, GOT_DEFAULT_FILE_MODE);
187 if (errno != ENOENT) {
188 err = got_error_from_errno();
191 err = got_packfile_open_object(obj, id, repo);
195 err = got_error(GOT_ERR_NO_OBJ);
197 err = got_object_read_header_privsep(obj, repo, fd);
200 memcpy((*obj)->id.sha1, id->sha1, SHA1_DIGEST_LENGTH);
205 err = got_repo_cache_object(repo, id, *obj);
215 const struct got_error *
216 got_object_open_by_id_str(struct got_object **obj, struct got_repository *repo,
219 struct got_object_id id;
221 if (!got_parse_sha1_digest(id.sha1, id_str))
222 return got_error(GOT_ERR_BAD_OBJ_ID_STR);
224 return got_object_open(obj, repo, &id);
227 const struct got_error *
228 got_object_open_as_commit(struct got_commit_object **commit,
229 struct got_repository *repo, struct got_object_id *id)
231 const struct got_error *err;
232 struct got_object *obj;
236 err = got_object_open(&obj, repo, id);
239 if (got_object_get_type(obj) != GOT_OBJ_TYPE_COMMIT) {
240 err = got_error(GOT_ERR_OBJ_TYPE);
244 err = got_object_commit_open(commit, repo, obj);
246 got_object_close(obj);
250 const struct got_error *
251 got_object_qid_alloc(struct got_object_qid **qid, struct got_object_id *id)
253 const struct got_error *err = NULL;
255 *qid = calloc(1, sizeof(**qid));
257 return got_error_from_errno();
259 (*qid)->id = got_object_id_dup(id);
260 if ((*qid)->id == NULL) {
261 err = got_error_from_errno();
262 got_object_qid_free(*qid);
270 const struct got_error *
271 got_object_commit_open(struct got_commit_object **commit,
272 struct got_repository *repo, struct got_object *obj)
274 const struct got_error *err = NULL;
276 *commit = got_repo_get_cached_commit(repo, &obj->id);
277 if (*commit != NULL) {
282 if (obj->type != GOT_OBJ_TYPE_COMMIT)
283 return got_error(GOT_ERR_OBJ_TYPE);
285 if (obj->flags & GOT_OBJ_FLAG_PACKED) {
288 err = got_packfile_extract_object_to_mem(&buf, &len, obj, repo);
292 err = got_object_parse_commit(commit, buf, len);
296 err = open_loose_object(&fd, obj, repo);
299 err = got_object_read_commit_privsep(commit, obj, fd, repo);
305 err = got_repo_cache_commit(repo, &obj->id, *commit);
311 const struct got_error *
312 got_object_tree_open(struct got_tree_object **tree,
313 struct got_repository *repo, struct got_object *obj)
315 const struct got_error *err = NULL;
317 *tree = got_repo_get_cached_tree(repo, &obj->id);
323 if (obj->type != GOT_OBJ_TYPE_TREE)
324 return got_error(GOT_ERR_OBJ_TYPE);
326 if (obj->flags & GOT_OBJ_FLAG_PACKED) {
329 err = got_packfile_extract_object_to_mem(&buf, &len, obj, repo);
333 err = got_object_parse_tree(tree, buf, len);
337 err = open_loose_object(&fd, obj, repo);
340 err = got_object_read_tree_privsep(tree, obj, fd, repo);
346 err = got_repo_cache_tree(repo, &obj->id, *tree);
352 const struct got_error *
353 got_object_open_as_tree(struct got_tree_object **tree,
354 struct got_repository *repo, struct got_object_id *id)
356 const struct got_error *err;
357 struct got_object *obj;
361 err = got_object_open(&obj, repo, id);
364 if (got_object_get_type(obj) != GOT_OBJ_TYPE_TREE) {
365 err = got_error(GOT_ERR_OBJ_TYPE);
369 err = got_object_tree_open(tree, repo, obj);
371 got_object_close(obj);
375 const struct got_tree_entries *
376 got_object_tree_get_entries(struct got_tree_object *tree)
378 return &tree->entries;
381 const struct got_error *
382 got_object_blob_open(struct got_blob_object **blob,
383 struct got_repository *repo, struct got_object *obj, size_t blocksize)
385 const struct got_error *err = NULL;
387 if (obj->type != GOT_OBJ_TYPE_BLOB)
388 return got_error(GOT_ERR_OBJ_TYPE);
390 if (blocksize < obj->hdrlen)
391 return got_error(GOT_ERR_NO_SPACE);
393 *blob = calloc(1, sizeof(**blob));
395 return got_error_from_errno();
397 (*blob)->read_buf = malloc(blocksize);
398 if ((*blob)->read_buf == NULL) {
399 err = got_error_from_errno();
402 if (obj->flags & GOT_OBJ_FLAG_PACKED) {
403 err = got_packfile_extract_object(&((*blob)->f), obj, repo);
411 err = open_loose_object(&infd, obj, repo);
416 outfd = got_opentempfd();
418 err = got_error_from_errno();
423 err = got_object_read_blob_privsep(&size, outfd, infd, repo);
428 if (size != obj->hdrlen + obj->size) {
429 err = got_error(GOT_ERR_PRIVSEP_LEN);
434 if (fstat(outfd, &sb) == -1) {
435 err = got_error_from_errno();
440 if (sb.st_size != size) {
441 err = got_error(GOT_ERR_PRIVSEP_LEN);
446 (*blob)->f = fdopen(outfd, "rb");
447 if ((*blob)->f == NULL) {
448 err = got_error_from_errno();
454 (*blob)->hdrlen = obj->hdrlen;
455 (*blob)->blocksize = blocksize;
456 memcpy(&(*blob)->id.sha1, obj->id.sha1, SHA1_DIGEST_LENGTH);
462 free((*blob)->read_buf);
469 const struct got_error *
470 got_object_open_as_blob(struct got_blob_object **blob,
471 struct got_repository *repo, struct got_object_id *id,
474 const struct got_error *err;
475 struct got_object *obj;
479 err = got_object_open(&obj, repo, id);
482 if (got_object_get_type(obj) != GOT_OBJ_TYPE_BLOB) {
483 err = got_error(GOT_ERR_OBJ_TYPE);
487 err = got_object_blob_open(blob, repo, obj, blocksize);
489 got_object_close(obj);
494 got_object_blob_close(struct got_blob_object *blob)
496 free(blob->read_buf);
502 got_object_blob_id_str(struct got_blob_object *blob, char *buf, size_t size)
504 return got_sha1_digest_to_str(blob->id.sha1, buf, size);
508 got_object_blob_get_hdrlen(struct got_blob_object *blob)
514 got_object_blob_get_read_buf(struct got_blob_object *blob)
516 return blob->read_buf;
519 const struct got_error *
520 got_object_blob_read_block(size_t *outlenp, struct got_blob_object *blob)
524 n = fread(blob->read_buf, 1, blob->blocksize, blob->f);
525 if (n == 0 && ferror(blob->f))
526 return got_ferror(blob->f, GOT_ERR_IO);
531 const struct got_error *
532 got_object_blob_dump_to_file(size_t *total_len, size_t *nlines,
533 FILE *outfile, struct got_blob_object *blob)
535 const struct got_error *err = NULL;
545 hdrlen = got_object_blob_get_hdrlen(blob);
547 err = got_object_blob_read_block(&len, blob);
554 buf = got_object_blob_get_read_buf(blob);
556 for (i = 0; i < len; i++) {
561 /* Skip blob object header first time around. */
562 fwrite(buf + hdrlen, len - hdrlen, 1, outfile);
572 static struct got_tree_entry *
573 find_entry_by_name(struct got_tree_object *tree, const char *name)
575 struct got_tree_entry *te;
577 SIMPLEQ_FOREACH(te, &tree->entries.head, entry) {
578 if (strcmp(te->name, name) == 0)
584 const struct got_error *
585 got_object_open_by_path(struct got_object **obj, struct got_repository *repo,
586 struct got_object_id *commit_id, const char *path)
588 const struct got_error *err = NULL;
589 struct got_commit_object *commit = NULL;
590 struct got_tree_object *tree = NULL;
591 struct got_tree_entry *te = NULL;
592 char *seg, *s, *s0 = NULL;
593 size_t len = strlen(path);
597 /* We are expecting an absolute in-repository path. */
599 return got_error(GOT_ERR_NOT_ABSPATH);
601 err = got_object_open_as_commit(&commit, repo, commit_id);
605 /* Handle opening of root of commit's tree. */
606 if (path[1] == '\0') {
607 err = got_object_open(obj, repo, commit->tree_id);
611 err = got_object_open_as_tree(&tree, repo, commit->tree_id);
617 err = got_error_from_errno();
620 err = got_canonpath(path, s0, len + 1);
625 s++; /* skip leading '/' */
629 struct got_tree_object *next_tree;
638 /* end of path segment */
641 te = find_entry_by_name(tree, seg);
643 err = got_error(GOT_ERR_NO_OBJ);
654 err = got_object_open_as_tree(&next_tree, repo,
659 got_object_tree_close(tree);
665 err = got_object_open(obj, repo, te->id);
667 err = got_error(GOT_ERR_NO_OBJ);
671 got_object_commit_close(commit);
673 got_object_tree_close(tree);