2 * Copyright (c) 2018, 2019 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 "got_compat.h"
19 #include <sys/types.h>
21 #include <sys/queue.h>
36 #include "got_error.h"
37 #include "got_object.h"
38 #include "got_repository.h"
39 #include "got_opentemp.h"
42 #include "got_lib_hash.h"
43 #include "got_lib_delta.h"
44 #include "got_lib_inflate.h"
45 #include "got_lib_object.h"
46 #include "got_lib_object_idcache.h"
47 #include "got_lib_object_cache.h"
48 #include "got_lib_object_parse.h"
49 #include "got_lib_pack.h"
50 #include "got_lib_repository.h"
53 #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
57 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
60 struct got_object_id
*
61 got_object_get_id(struct got_object
*obj
)
66 const struct got_error
*
67 got_object_get_id_str(char **outbuf
, struct got_object
*obj
)
69 return got_object_id_str(outbuf
, &obj
->id
);
72 const struct got_error
*
73 got_object_get_type(int *type
, struct got_repository
*repo
,
74 struct got_object_id
*id
)
76 const struct got_error
*err
= NULL
;
77 struct got_object
*obj
;
79 err
= got_object_open(&obj
, repo
, id
);
84 case GOT_OBJ_TYPE_COMMIT
:
85 case GOT_OBJ_TYPE_TREE
:
86 case GOT_OBJ_TYPE_BLOB
:
87 case GOT_OBJ_TYPE_TAG
:
91 err
= got_error(GOT_ERR_OBJ_TYPE
);
95 got_object_close(obj
);
99 const struct got_error
*
100 got_object_get_path(char **path
, struct got_object_id
*id
,
101 struct got_repository
*repo
)
103 const struct got_error
*err
= NULL
;
109 path_objects
= got_repo_get_path_objects(repo
);
110 if (path_objects
== NULL
)
111 return got_error_from_errno("got_repo_get_path_objects");
113 err
= got_object_id_str(&hex
, id
);
117 if (asprintf(path
, "%s/%.2x/%s", path_objects
,
118 id
->sha1
[0], hex
+ 2) == -1)
119 err
= got_error_from_errno("asprintf");
127 const struct got_error
*
128 got_object_open_loose_fd(int *fd
, struct got_object_id
*id
,
129 struct got_repository
*repo
)
131 const struct got_error
*err
= NULL
;
134 err
= got_object_get_path(&path
, id
, repo
);
137 *fd
= open(path
, O_RDONLY
| O_NOFOLLOW
| O_CLOEXEC
);
139 err
= got_error_from_errno2("open", path
);
147 const struct got_error
*
148 got_object_open_by_id_str(struct got_object
**obj
, struct got_repository
*repo
,
151 struct got_object_id id
;
153 if (!got_parse_object_id(&id
, id_str
, GOT_HASH_SHA1
))
154 return got_error_path(id_str
, GOT_ERR_BAD_OBJ_ID_STR
);
156 return got_object_open(obj
, repo
, &id
);
159 const struct got_error
*
160 got_object_resolve_id_str(struct got_object_id
**id
,
161 struct got_repository
*repo
, const char *id_str
)
163 const struct got_error
*err
= NULL
;
164 struct got_object
*obj
;
166 err
= got_object_open_by_id_str(&obj
, repo
, id_str
);
170 *id
= got_object_id_dup(got_object_get_id(obj
));
171 got_object_close(obj
);
173 return got_error_from_errno("got_object_id_dup");
179 got_object_tree_get_nentries(struct got_tree_object
*tree
)
181 return tree
->nentries
;
184 struct got_tree_entry
*
185 got_object_tree_get_first_entry(struct got_tree_object
*tree
)
187 return got_object_tree_get_entry(tree
, 0);
190 struct got_tree_entry
*
191 got_object_tree_get_last_entry(struct got_tree_object
*tree
)
193 return got_object_tree_get_entry(tree
, tree
->nentries
- 1);
196 struct got_tree_entry
*
197 got_object_tree_get_entry(struct got_tree_object
*tree
, int i
)
199 if (i
< 0 || i
>= tree
->nentries
)
201 return &tree
->entries
[i
];
205 got_tree_entry_get_mode(struct got_tree_entry
*te
)
211 got_tree_entry_get_name(struct got_tree_entry
*te
)
216 struct got_object_id
*
217 got_tree_entry_get_id(struct got_tree_entry
*te
)
222 const struct got_error
*
223 got_object_blob_read_to_str(char **s
, struct got_blob_object
*blob
)
225 const struct got_error
*err
= NULL
;
226 size_t len
, totlen
, hdrlen
, offset
;
230 hdrlen
= got_object_blob_get_hdrlen(blob
);
236 err
= got_object_blob_read_block(&len
, blob
);
243 totlen
+= len
- hdrlen
;
244 p
= realloc(*s
, totlen
+ 1);
246 err
= got_error_from_errno("realloc");
252 /* Skip blob object header first time around. */
254 got_object_blob_get_read_buf(blob
) + hdrlen
, len
- hdrlen
);
263 const struct got_error
*
264 got_tree_entry_get_symlink_target(char **link_target
, struct got_tree_entry
*te
,
265 struct got_repository
*repo
)
267 const struct got_error
*err
= NULL
;
268 struct got_blob_object
*blob
= NULL
;
273 if (!got_object_tree_entry_is_symlink(te
))
274 return got_error(GOT_ERR_TREE_ENTRY_TYPE
);
276 fd
= got_opentempfd();
278 err
= got_error_from_errno("got_opentempfd");
282 err
= got_object_open_as_blob(&blob
, repo
,
283 got_tree_entry_get_id(te
), PATH_MAX
, fd
);
287 err
= got_object_blob_read_to_str(link_target
, blob
);
289 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
290 err
= got_error_from_errno("close");
292 got_object_blob_close(blob
);
301 got_tree_entry_get_index(struct got_tree_entry
*te
)
306 struct got_tree_entry
*
307 got_tree_entry_get_next(struct got_tree_object
*tree
,
308 struct got_tree_entry
*te
)
310 return got_object_tree_get_entry(tree
, te
->idx
+ 1);
313 struct got_tree_entry
*
314 got_tree_entry_get_prev(struct got_tree_object
*tree
,
315 struct got_tree_entry
*te
)
317 return got_object_tree_get_entry(tree
, te
->idx
- 1);
320 const struct got_error
*
321 got_object_blob_close(struct got_blob_object
*blob
)
323 const struct got_error
*err
= NULL
;
324 free(blob
->read_buf
);
325 if (blob
->f
&& fclose(blob
->f
) == EOF
)
326 err
= got_error_from_errno("fclose");
333 got_object_blob_rewind(struct got_blob_object
*blob
)
340 got_object_blob_id_str(struct got_blob_object
*blob
, char *buf
, size_t size
)
342 return got_object_id_hex(&blob
->id
, buf
, size
);
346 got_object_blob_get_hdrlen(struct got_blob_object
*blob
)
352 got_object_blob_get_read_buf(struct got_blob_object
*blob
)
354 return blob
->read_buf
;
357 const struct got_error
*
358 got_object_blob_read_block(size_t *outlenp
, struct got_blob_object
*blob
)
362 n
= fread(blob
->read_buf
, 1, blob
->blocksize
, blob
->f
);
363 if (n
== 0 && ferror(blob
->f
))
364 return got_ferror(blob
->f
, GOT_ERR_IO
);
369 const struct got_error
*
370 got_object_blob_is_binary(int *binary
, struct got_blob_object
*blob
)
372 const struct got_error
*err
;
376 hdrlen
= got_object_blob_get_hdrlen(blob
);
378 if (fseeko(blob
->f
, hdrlen
, SEEK_SET
) == -1)
379 return got_error_from_errno("fseeko");
381 err
= got_object_blob_read_block(&len
, blob
);
385 *binary
= memchr(blob
->read_buf
, '\0', len
) != NULL
;
387 if (fseeko(blob
->f
, hdrlen
, SEEK_SET
) == -1)
388 return got_error_from_errno("fseeko");
392 const struct got_error
*
393 got_object_blob_getline(char **line
, ssize_t
*linelen
, size_t *linesize
,
394 struct got_blob_object
*blob
)
396 *linelen
= getline(line
, linesize
, blob
->f
);
397 if (*linelen
== -1 && !feof(blob
->f
))
398 return got_error_from_errno("getline");
402 const struct got_error
*
403 got_object_blob_dump_to_file(off_t
*filesize
, int *nlines
,
404 off_t
**line_offsets
, FILE *outfile
, struct got_blob_object
*blob
)
406 const struct got_error
*err
= NULL
;
407 size_t n
, len
, hdrlen
;
410 const int alloc_chunksz
= 512;
412 off_t off
= 0, total_len
= 0;
415 *line_offsets
= NULL
;
421 hdrlen
= got_object_blob_get_hdrlen(blob
);
423 err
= got_object_blob_read_block(&len
, blob
);
428 buf
= got_object_blob_get_read_buf(blob
);
431 if (line_offsets
&& *line_offsets
== NULL
) {
432 /* Have some data but perhaps no '\n'. */
434 nalloc
= alloc_chunksz
;
435 *line_offsets
= calloc(nalloc
,
436 sizeof(**line_offsets
));
437 if (*line_offsets
== NULL
)
438 return got_error_from_errno("calloc");
440 /* Skip forward over end of first line. */
447 /* Scan '\n' offsets in remaining chunk of data. */
449 if (buf
[i
] != '\n') {
454 if (line_offsets
&& nalloc
< *nlines
) {
455 size_t n
= *nlines
+ alloc_chunksz
;
456 off_t
*o
= recallocarray(*line_offsets
,
457 nalloc
, n
, sizeof(**line_offsets
));
460 *line_offsets
= NULL
;
461 return got_error_from_errno(
468 off
= total_len
+ i
- hdrlen
+ 1;
469 (*line_offsets
)[*nlines
- 1] = off
;
474 /* Skip blob object header first time around. */
475 n
= fwrite(buf
+ hdrlen
, 1, len
- hdrlen
, outfile
);
476 if (n
!= len
- hdrlen
)
477 return got_ferror(outfile
, GOT_ERR_IO
);
478 total_len
+= len
- hdrlen
;
482 if (fflush(outfile
) != 0)
483 return got_error_from_errno("fflush");
487 *filesize
= total_len
;
493 got_object_tag_get_name(struct got_tag_object
*tag
)
499 got_object_tag_get_object_type(struct got_tag_object
*tag
)
501 return tag
->obj_type
;
504 struct got_object_id
*
505 got_object_tag_get_object_id(struct got_tag_object
*tag
)
511 got_object_tag_get_tagger_time(struct got_tag_object
*tag
)
513 return tag
->tagger_time
;
517 got_object_tag_get_tagger_gmtoff(struct got_tag_object
*tag
)
519 return tag
->tagger_gmtoff
;
523 got_object_tag_get_tagger(struct got_tag_object
*tag
)
529 got_object_tag_get_message(struct got_tag_object
*tag
)
534 static struct got_tree_entry
*
535 find_entry_by_name(struct got_tree_object
*tree
, const char *name
, size_t len
)
539 /* Note that tree entries are sorted in strncmp() order. */
540 for (i
= 0; i
< tree
->nentries
; i
++) {
541 struct got_tree_entry
*te
= &tree
->entries
[i
];
542 int cmp
= strncmp(te
->name
, name
, len
);
547 if (te
->name
[len
] == '\0')
553 struct got_tree_entry
*
554 got_object_tree_find_entry(struct got_tree_object
*tree
, const char *name
)
556 return find_entry_by_name(tree
, name
, strlen(name
));
559 const struct got_error
*
560 got_object_tree_find_path(struct got_object_id
**id
, mode_t
*mode
,
561 struct got_repository
*repo
, struct got_tree_object
*tree
,
564 const struct got_error
*err
= NULL
;
565 struct got_tree_object
*subtree
= NULL
;
566 struct got_tree_entry
*te
= NULL
;
579 struct got_tree_object
*next_tree
;
588 te
= find_entry_by_name(subtree
, seg
, seglen
);
590 err
= got_error_path(path
, GOT_ERR_NO_TREE_ENTRY
);
601 err
= got_object_open_as_tree(&next_tree
, repo
,
607 got_object_tree_close(subtree
);
613 *id
= got_object_id_dup(&te
->id
);
615 return got_error_from_errno("got_object_id_dup");
619 err
= got_error_path(path
, GOT_ERR_NO_TREE_ENTRY
);
621 if (subtree
&& subtree
!= tree
)
622 got_object_tree_close(subtree
);
626 const struct got_error
*
627 got_object_id_by_path(struct got_object_id
**id
, struct got_repository
*repo
,
628 struct got_commit_object
*commit
, const char *path
)
630 const struct got_error
*err
= NULL
;
631 struct got_tree_object
*tree
= NULL
;
635 /* Handle opening of root of commit's tree. */
636 if (got_path_is_root_dir(path
)) {
637 *id
= got_object_id_dup(commit
->tree_id
);
639 err
= got_error_from_errno("got_object_id_dup");
641 err
= got_object_open_as_tree(&tree
, repo
, commit
->tree_id
);
644 err
= got_object_tree_find_path(id
, NULL
, repo
, tree
, path
);
648 got_object_tree_close(tree
);
653 * Normalize file mode bits to avoid false positive tree entry differences
654 * in case tree entries have unexpected mode bits set.
657 normalize_mode_for_comparison(mode_t mode
)
660 * For directories, the only relevant bit is the IFDIR bit.
661 * This allows us to detect paths changing from a directory
662 * to a file and vice versa.
665 return mode
& S_IFDIR
;
668 * For symlinks, the only relevant bit is the IFLNK bit.
669 * This allows us to detect paths changing from a symlinks
670 * to a file or directory and vice versa.
673 return mode
& S_IFLNK
;
675 /* For files, the only change we care about is the executable bit. */
676 return mode
& S_IXUSR
;
679 const struct got_error
*
680 got_object_tree_path_changed(int *changed
,
681 struct got_tree_object
*tree01
, struct got_tree_object
*tree02
,
682 const char *path
, struct got_repository
*repo
)
684 const struct got_error
*err
= NULL
;
685 struct got_tree_object
*tree1
= NULL
, *tree2
= NULL
;
686 struct got_tree_entry
*te1
= NULL
, *te2
= NULL
;
692 /* We not do support comparing the root path. */
693 if (got_path_is_root_dir(path
))
694 return got_error_path(path
, GOT_ERR_BAD_PATH
);
704 struct got_tree_object
*next_tree1
, *next_tree2
;
714 te1
= find_entry_by_name(tree1
, seg
, seglen
);
716 err
= got_error(GOT_ERR_NO_OBJ
);
721 te2
= find_entry_by_name(tree2
, seg
, seglen
);
724 mode1
= normalize_mode_for_comparison(te1
->mode
);
725 mode2
= normalize_mode_for_comparison(te2
->mode
);
726 if (mode1
!= mode2
) {
731 if (got_object_id_cmp(&te1
->id
, &te2
->id
) == 0) {
737 if (*s
== '\0') { /* final path element */
746 err
= got_object_open_as_tree(&next_tree1
, repo
,
752 got_object_tree_close(tree1
);
756 err
= got_object_open_as_tree(&next_tree2
, repo
,
762 got_object_tree_close(tree2
);
766 got_object_tree_close(tree2
);
772 if (tree1
&& tree1
!= tree01
)
773 got_object_tree_close(tree1
);
774 if (tree2
&& tree2
!= tree02
)
775 got_object_tree_close(tree2
);
779 const struct got_error
*
780 got_object_tree_entry_dup(struct got_tree_entry
**new_te
,
781 struct got_tree_entry
*te
)
783 const struct got_error
*err
= NULL
;
785 *new_te
= calloc(1, sizeof(**new_te
));
787 return got_error_from_errno("calloc");
789 (*new_te
)->mode
= te
->mode
;
790 memcpy((*new_te
)->name
, te
->name
, sizeof((*new_te
)->name
));
791 memcpy(&(*new_te
)->id
, &te
->id
, sizeof((*new_te
)->id
));
796 got_object_tree_entry_is_submodule(struct got_tree_entry
*te
)
798 return (te
->mode
& S_IFMT
) == (S_IFDIR
| S_IFLNK
);
802 got_object_tree_entry_is_symlink(struct got_tree_entry
*te
)
804 /* S_IFDIR check avoids confusing symlinks with submodules. */
805 return ((te
->mode
& (S_IFDIR
| S_IFLNK
)) == S_IFLNK
);
808 static const struct got_error
*
809 resolve_symlink(char **link_target
, const char *path
,
810 struct got_commit_object
*commit
, struct got_repository
*repo
)
812 const struct got_error
*err
= NULL
;
814 char *name
, *parent_path
= NULL
;
815 struct got_object_id
*tree_obj_id
= NULL
;
816 struct got_tree_object
*tree
= NULL
;
817 struct got_tree_entry
*te
= NULL
;
821 if (strlcpy(buf
, path
, sizeof(buf
)) >= sizeof(buf
))
822 return got_error(GOT_ERR_NO_SPACE
);
824 name
= basename(buf
);
826 return got_error_from_errno2("basename", path
);
828 err
= got_path_dirname(&parent_path
, path
);
832 err
= got_object_id_by_path(&tree_obj_id
, repo
, commit
,
835 if (err
->code
== GOT_ERR_NO_TREE_ENTRY
) {
836 /* Display the complete path in error message. */
837 err
= got_error_path(path
, err
->code
);
842 err
= got_object_open_as_tree(&tree
, repo
, tree_obj_id
);
846 te
= got_object_tree_find_entry(tree
, name
);
848 err
= got_error_path(path
, GOT_ERR_NO_TREE_ENTRY
);
852 if (got_object_tree_entry_is_symlink(te
)) {
853 err
= got_tree_entry_get_symlink_target(link_target
, te
, repo
);
856 if (!got_path_is_absolute(*link_target
)) {
858 if (asprintf(&abspath
, "%s/%s", parent_path
,
859 *link_target
) == -1) {
860 err
= got_error_from_errno("asprintf");
864 *link_target
= malloc(PATH_MAX
);
865 if (*link_target
== NULL
) {
866 err
= got_error_from_errno("malloc");
869 err
= got_canonpath(abspath
, *link_target
, PATH_MAX
);
879 got_object_tree_close(tree
);
887 const struct got_error
*
888 got_object_resolve_symlinks(char **link_target
, const char *path
,
889 struct got_commit_object
*commit
, struct got_repository
*repo
)
891 const struct got_error
*err
= NULL
;
892 char *next_target
= NULL
;
893 int max_recursion
= 40; /* matches Git */
898 err
= resolve_symlink(&next_target
,
899 *link_target
? *link_target
: path
, commit
, repo
);
904 if (--max_recursion
== 0) {
905 err
= got_error_path(path
, GOT_ERR_RECURSION
);
909 *link_target
= next_target
;
911 } while (next_target
);
917 got_object_commit_retain(struct got_commit_object
*commit
)
922 const struct got_error
*
923 got_object_raw_alloc(struct got_raw_object
**obj
, uint8_t *outbuf
, int *outfd
,
924 size_t max_in_mem_size
, size_t hdrlen
, off_t size
)
926 const struct got_error
*err
= NULL
;
931 *obj
= calloc(1, sizeof(**obj
));
933 err
= got_error_from_errno("calloc");
937 (*obj
)->tempfile_idx
= -1;
940 (*obj
)->data
= outbuf
;
943 if (fstat(*outfd
, &sb
) == -1) {
944 err
= got_error_from_errno("fstat");
948 if (sb
.st_size
!= tot
) {
949 err
= got_error_msg(GOT_ERR_BAD_OBJ_HDR
,
950 "raw object has unexpected size");
953 #ifndef GOT_PACK_NO_MMAP
954 if (tot
> 0 && tot
<= max_in_mem_size
) {
955 (*obj
)->data
= mmap(NULL
, tot
, PROT_READ
,
956 MAP_PRIVATE
, *outfd
, 0);
957 if ((*obj
)->data
== MAP_FAILED
) {
958 if (errno
!= ENOMEM
) {
959 err
= got_error_from_errno("mmap");
970 (*obj
)->f
= fdopen(*outfd
, "r");
971 if ((*obj
)->f
== NULL
) {
972 err
= got_error_from_errno("fdopen");
978 (*obj
)->hdrlen
= hdrlen
;
983 got_object_raw_close(*obj
);