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
->hash
[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
);
140 err
= got_error_no_obj(id
);
142 err
= got_error_from_errno2("open", path
);
150 const struct got_error
*
151 got_object_open_by_id_str(struct got_object
**obj
, struct got_repository
*repo
,
154 struct got_object_id id
;
156 if (!got_parse_object_id(&id
, id_str
, repo
->algo
))
157 return got_error_path(id_str
, GOT_ERR_BAD_OBJ_ID_STR
);
159 return got_object_open(obj
, repo
, &id
);
162 const struct got_error
*
163 got_object_resolve_id_str(struct got_object_id
**id
,
164 struct got_repository
*repo
, const char *id_str
)
166 const struct got_error
*err
= NULL
;
167 struct got_object
*obj
;
169 err
= got_object_open_by_id_str(&obj
, repo
, id_str
);
173 *id
= got_object_id_dup(got_object_get_id(obj
));
174 got_object_close(obj
);
176 return got_error_from_errno("got_object_id_dup");
182 got_object_tree_get_nentries(struct got_tree_object
*tree
)
184 return tree
->nentries
;
187 struct got_tree_entry
*
188 got_object_tree_get_first_entry(struct got_tree_object
*tree
)
190 return got_object_tree_get_entry(tree
, 0);
193 struct got_tree_entry
*
194 got_object_tree_get_last_entry(struct got_tree_object
*tree
)
196 return got_object_tree_get_entry(tree
, tree
->nentries
- 1);
199 struct got_tree_entry
*
200 got_object_tree_get_entry(struct got_tree_object
*tree
, int i
)
202 if (i
< 0 || i
>= tree
->nentries
)
204 return &tree
->entries
[i
];
208 got_tree_entry_get_mode(struct got_tree_entry
*te
)
214 got_tree_entry_get_name(struct got_tree_entry
*te
)
219 struct got_object_id
*
220 got_tree_entry_get_id(struct got_tree_entry
*te
)
225 const struct got_error
*
226 got_object_blob_read_to_str(char **s
, struct got_blob_object
*blob
)
228 const struct got_error
*err
= NULL
;
229 size_t len
, totlen
, hdrlen
, offset
;
233 hdrlen
= got_object_blob_get_hdrlen(blob
);
239 err
= got_object_blob_read_block(&len
, blob
);
246 totlen
+= len
- hdrlen
;
247 p
= realloc(*s
, totlen
+ 1);
249 err
= got_error_from_errno("realloc");
255 /* Skip blob object header first time around. */
257 got_object_blob_get_read_buf(blob
) + hdrlen
, len
- hdrlen
);
266 const struct got_error
*
267 got_tree_entry_get_symlink_target(char **link_target
, struct got_tree_entry
*te
,
268 struct got_repository
*repo
)
270 const struct got_error
*err
= NULL
;
271 struct got_blob_object
*blob
= NULL
;
276 if (!got_object_tree_entry_is_symlink(te
))
277 return got_error(GOT_ERR_TREE_ENTRY_TYPE
);
279 fd
= got_opentempfd();
281 err
= got_error_from_errno("got_opentempfd");
285 err
= got_object_open_as_blob(&blob
, repo
,
286 got_tree_entry_get_id(te
), PATH_MAX
, fd
);
290 err
= got_object_blob_read_to_str(link_target
, blob
);
292 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
293 err
= got_error_from_errno("close");
295 got_object_blob_close(blob
);
304 got_tree_entry_get_index(struct got_tree_entry
*te
)
309 struct got_tree_entry
*
310 got_tree_entry_get_next(struct got_tree_object
*tree
,
311 struct got_tree_entry
*te
)
313 return got_object_tree_get_entry(tree
, te
->idx
+ 1);
316 struct got_tree_entry
*
317 got_tree_entry_get_prev(struct got_tree_object
*tree
,
318 struct got_tree_entry
*te
)
320 return got_object_tree_get_entry(tree
, te
->idx
- 1);
323 const struct got_error
*
324 got_object_blob_close(struct got_blob_object
*blob
)
326 const struct got_error
*err
= NULL
;
327 free(blob
->read_buf
);
328 if (blob
->f
&& fclose(blob
->f
) == EOF
)
329 err
= got_error_from_errno("fclose");
336 got_object_blob_rewind(struct got_blob_object
*blob
)
343 got_object_blob_id_str(struct got_blob_object
*blob
, char *buf
, size_t size
)
345 return got_object_id_hex(&blob
->id
, buf
, size
);
349 got_object_blob_get_hdrlen(struct got_blob_object
*blob
)
355 got_object_blob_get_read_buf(struct got_blob_object
*blob
)
357 return blob
->read_buf
;
360 const struct got_error
*
361 got_object_blob_read_block(size_t *outlenp
, struct got_blob_object
*blob
)
365 n
= fread(blob
->read_buf
, 1, blob
->blocksize
, blob
->f
);
366 if (n
== 0 && ferror(blob
->f
))
367 return got_ferror(blob
->f
, GOT_ERR_IO
);
372 const struct got_error
*
373 got_object_blob_is_binary(int *binary
, struct got_blob_object
*blob
)
375 const struct got_error
*err
;
379 hdrlen
= got_object_blob_get_hdrlen(blob
);
381 if (fseeko(blob
->f
, hdrlen
, SEEK_SET
) == -1)
382 return got_error_from_errno("fseeko");
384 err
= got_object_blob_read_block(&len
, blob
);
388 *binary
= memchr(blob
->read_buf
, '\0', len
) != NULL
;
390 if (fseeko(blob
->f
, hdrlen
, SEEK_SET
) == -1)
391 return got_error_from_errno("fseeko");
395 const struct got_error
*
396 got_object_blob_getline(char **line
, ssize_t
*linelen
, size_t *linesize
,
397 struct got_blob_object
*blob
)
399 *linelen
= getline(line
, linesize
, blob
->f
);
400 if (*linelen
== -1 && !feof(blob
->f
))
401 return got_error_from_errno("getline");
405 const struct got_error
*
406 got_object_blob_dump_to_file(off_t
*filesize
, int *nlines
,
407 off_t
**line_offsets
, FILE *outfile
, struct got_blob_object
*blob
)
409 const struct got_error
*err
= NULL
;
410 size_t n
, len
, hdrlen
;
413 const int alloc_chunksz
= 512;
415 off_t off
= 0, total_len
= 0;
418 *line_offsets
= NULL
;
424 hdrlen
= got_object_blob_get_hdrlen(blob
);
426 err
= got_object_blob_read_block(&len
, blob
);
431 buf
= got_object_blob_get_read_buf(blob
);
434 if (line_offsets
&& *line_offsets
== NULL
) {
435 /* Have some data but perhaps no '\n'. */
437 nalloc
= alloc_chunksz
;
438 *line_offsets
= calloc(nalloc
,
439 sizeof(**line_offsets
));
440 if (*line_offsets
== NULL
)
441 return got_error_from_errno("calloc");
443 /* Skip forward over end of first line. */
450 /* Scan '\n' offsets in remaining chunk of data. */
452 if (buf
[i
] != '\n') {
457 if (line_offsets
&& nalloc
< *nlines
) {
458 size_t n
= *nlines
+ alloc_chunksz
;
459 off_t
*o
= recallocarray(*line_offsets
,
460 nalloc
, n
, sizeof(**line_offsets
));
463 *line_offsets
= NULL
;
464 return got_error_from_errno(
471 off
= total_len
+ i
- hdrlen
+ 1;
472 (*line_offsets
)[*nlines
- 1] = off
;
477 /* Skip blob object header first time around. */
478 n
= fwrite(buf
+ hdrlen
, 1, len
- hdrlen
, outfile
);
479 if (n
!= len
- hdrlen
)
480 return got_ferror(outfile
, GOT_ERR_IO
);
481 total_len
+= len
- hdrlen
;
485 if (fflush(outfile
) != 0)
486 return got_error_from_errno("fflush");
490 *filesize
= total_len
;
496 got_object_tag_get_name(struct got_tag_object
*tag
)
502 got_object_tag_get_object_type(struct got_tag_object
*tag
)
504 return tag
->obj_type
;
507 struct got_object_id
*
508 got_object_tag_get_object_id(struct got_tag_object
*tag
)
514 got_object_tag_get_tagger_time(struct got_tag_object
*tag
)
516 return tag
->tagger_time
;
520 got_object_tag_get_tagger_gmtoff(struct got_tag_object
*tag
)
522 return tag
->tagger_gmtoff
;
526 got_object_tag_get_tagger(struct got_tag_object
*tag
)
532 got_object_tag_get_message(struct got_tag_object
*tag
)
537 static struct got_tree_entry
*
538 find_entry_by_name(struct got_tree_object
*tree
, const char *name
, size_t len
)
542 /* Note that tree entries are sorted in strncmp() order. */
543 for (i
= 0; i
< tree
->nentries
; i
++) {
544 struct got_tree_entry
*te
= &tree
->entries
[i
];
545 int cmp
= strncmp(te
->name
, name
, len
);
550 if (te
->name
[len
] == '\0')
556 struct got_tree_entry
*
557 got_object_tree_find_entry(struct got_tree_object
*tree
, const char *name
)
559 return find_entry_by_name(tree
, name
, strlen(name
));
562 const struct got_error
*
563 got_object_tree_find_path(struct got_object_id
**id
, mode_t
*mode
,
564 struct got_repository
*repo
, struct got_tree_object
*tree
,
567 const struct got_error
*err
= NULL
;
568 struct got_tree_object
*subtree
= NULL
;
569 struct got_tree_entry
*te
= NULL
;
582 struct got_tree_object
*next_tree
;
591 te
= find_entry_by_name(subtree
, seg
, seglen
);
593 err
= got_error_path(path
, GOT_ERR_NO_TREE_ENTRY
);
604 err
= got_object_open_as_tree(&next_tree
, repo
,
610 got_object_tree_close(subtree
);
616 *id
= got_object_id_dup(&te
->id
);
618 return got_error_from_errno("got_object_id_dup");
622 err
= got_error_path(path
, GOT_ERR_NO_TREE_ENTRY
);
624 if (subtree
&& subtree
!= tree
)
625 got_object_tree_close(subtree
);
629 const struct got_error
*
630 got_object_id_by_path(struct got_object_id
**id
, struct got_repository
*repo
,
631 struct got_commit_object
*commit
, const char *path
)
633 const struct got_error
*err
= NULL
;
634 struct got_tree_object
*tree
= NULL
;
638 /* Handle opening of root of commit's tree. */
639 if (got_path_is_root_dir(path
)) {
640 *id
= got_object_id_dup(commit
->tree_id
);
642 err
= got_error_from_errno("got_object_id_dup");
644 err
= got_object_open_as_tree(&tree
, repo
, commit
->tree_id
);
647 err
= got_object_tree_find_path(id
, NULL
, repo
, tree
, path
);
651 got_object_tree_close(tree
);
656 * Normalize file mode bits to avoid false positive tree entry differences
657 * in case tree entries have unexpected mode bits set.
660 normalize_mode_for_comparison(mode_t mode
)
663 * For directories, the only relevant bit is the IFDIR bit.
664 * This allows us to detect paths changing from a directory
665 * to a file and vice versa.
668 return mode
& S_IFDIR
;
671 * For symlinks, the only relevant bit is the IFLNK bit.
672 * This allows us to detect paths changing from a symlinks
673 * to a file or directory and vice versa.
676 return mode
& S_IFLNK
;
678 /* For files, the only change we care about is the executable bit. */
679 return mode
& S_IXUSR
;
682 const struct got_error
*
683 got_object_tree_path_changed(int *changed
,
684 struct got_tree_object
*tree01
, struct got_tree_object
*tree02
,
685 const char *path
, struct got_repository
*repo
)
687 const struct got_error
*err
= NULL
;
688 struct got_tree_object
*tree1
= NULL
, *tree2
= NULL
;
689 struct got_tree_entry
*te1
= NULL
, *te2
= NULL
;
695 /* We not do support comparing the root path. */
696 if (got_path_is_root_dir(path
))
697 return got_error_path(path
, GOT_ERR_BAD_PATH
);
707 struct got_tree_object
*next_tree1
, *next_tree2
;
717 te1
= find_entry_by_name(tree1
, seg
, seglen
);
719 err
= got_error(GOT_ERR_NO_OBJ
);
724 te2
= find_entry_by_name(tree2
, seg
, seglen
);
727 mode1
= normalize_mode_for_comparison(te1
->mode
);
728 mode2
= normalize_mode_for_comparison(te2
->mode
);
729 if (mode1
!= mode2
) {
734 if (got_object_id_cmp(&te1
->id
, &te2
->id
) == 0) {
740 if (*s
== '\0') { /* final path element */
749 err
= got_object_open_as_tree(&next_tree1
, repo
,
755 got_object_tree_close(tree1
);
759 err
= got_object_open_as_tree(&next_tree2
, repo
,
765 got_object_tree_close(tree2
);
769 got_object_tree_close(tree2
);
775 if (tree1
&& tree1
!= tree01
)
776 got_object_tree_close(tree1
);
777 if (tree2
&& tree2
!= tree02
)
778 got_object_tree_close(tree2
);
782 const struct got_error
*
783 got_object_tree_entry_dup(struct got_tree_entry
**new_te
,
784 struct got_tree_entry
*te
)
786 const struct got_error
*err
= NULL
;
788 *new_te
= calloc(1, sizeof(**new_te
));
790 return got_error_from_errno("calloc");
792 (*new_te
)->mode
= te
->mode
;
793 memcpy((*new_te
)->name
, te
->name
, sizeof((*new_te
)->name
));
794 memcpy(&(*new_te
)->id
, &te
->id
, sizeof((*new_te
)->id
));
799 got_object_tree_entry_is_submodule(struct got_tree_entry
*te
)
801 return (te
->mode
& S_IFMT
) == (S_IFDIR
| S_IFLNK
);
805 got_object_tree_entry_is_symlink(struct got_tree_entry
*te
)
807 /* S_IFDIR check avoids confusing symlinks with submodules. */
808 return ((te
->mode
& (S_IFDIR
| S_IFLNK
)) == S_IFLNK
);
811 static const struct got_error
*
812 resolve_symlink(char **link_target
, const char *path
,
813 struct got_commit_object
*commit
, struct got_repository
*repo
)
815 const struct got_error
*err
= NULL
;
817 char *name
, *parent_path
= NULL
;
818 struct got_object_id
*tree_obj_id
= NULL
;
819 struct got_tree_object
*tree
= NULL
;
820 struct got_tree_entry
*te
= NULL
;
824 if (strlcpy(buf
, path
, sizeof(buf
)) >= sizeof(buf
))
825 return got_error(GOT_ERR_NO_SPACE
);
827 name
= basename(buf
);
829 return got_error_from_errno2("basename", path
);
831 err
= got_path_dirname(&parent_path
, path
);
835 err
= got_object_id_by_path(&tree_obj_id
, repo
, commit
,
838 if (err
->code
== GOT_ERR_NO_TREE_ENTRY
) {
839 /* Display the complete path in error message. */
840 err
= got_error_path(path
, err
->code
);
845 err
= got_object_open_as_tree(&tree
, repo
, tree_obj_id
);
849 te
= got_object_tree_find_entry(tree
, name
);
851 err
= got_error_path(path
, GOT_ERR_NO_TREE_ENTRY
);
855 if (got_object_tree_entry_is_symlink(te
)) {
856 err
= got_tree_entry_get_symlink_target(link_target
, te
, repo
);
859 if (!got_path_is_absolute(*link_target
)) {
861 if (asprintf(&abspath
, "%s/%s", parent_path
,
862 *link_target
) == -1) {
863 err
= got_error_from_errno("asprintf");
867 *link_target
= malloc(PATH_MAX
);
868 if (*link_target
== NULL
) {
869 err
= got_error_from_errno("malloc");
872 err
= got_canonpath(abspath
, *link_target
, PATH_MAX
);
882 got_object_tree_close(tree
);
890 const struct got_error
*
891 got_object_resolve_symlinks(char **link_target
, const char *path
,
892 struct got_commit_object
*commit
, struct got_repository
*repo
)
894 const struct got_error
*err
= NULL
;
895 char *next_target
= NULL
;
896 int max_recursion
= 40; /* matches Git */
901 err
= resolve_symlink(&next_target
,
902 *link_target
? *link_target
: path
, commit
, repo
);
907 if (--max_recursion
== 0) {
908 err
= got_error_path(path
, GOT_ERR_RECURSION
);
912 *link_target
= next_target
;
914 } while (next_target
);
920 got_object_commit_retain(struct got_commit_object
*commit
)
925 const struct got_error
*
926 got_object_raw_alloc(struct got_raw_object
**obj
, uint8_t *outbuf
, int *outfd
,
927 size_t max_in_mem_size
, size_t hdrlen
, off_t size
)
929 const struct got_error
*err
= NULL
;
934 *obj
= calloc(1, sizeof(**obj
));
936 err
= got_error_from_errno("calloc");
940 (*obj
)->tempfile_idx
= -1;
943 (*obj
)->data
= outbuf
;
946 if (fstat(*outfd
, &sb
) == -1) {
947 err
= got_error_from_errno("fstat");
951 if (sb
.st_size
!= tot
) {
952 err
= got_error_msg(GOT_ERR_BAD_OBJ_HDR
,
953 "raw object has unexpected size");
956 #ifndef GOT_PACK_NO_MMAP
957 if (tot
> 0 && tot
<= max_in_mem_size
) {
958 (*obj
)->data
= mmap(NULL
, tot
, PROT_READ
,
959 MAP_PRIVATE
, *outfd
, 0);
960 if ((*obj
)->data
== MAP_FAILED
) {
961 if (errno
!= ENOMEM
) {
962 err
= got_error_from_errno("mmap");
973 (*obj
)->f
= fdopen(*outfd
, "r");
974 if ((*obj
)->f
== NULL
) {
975 err
= got_error_from_errno("fdopen");
981 (*obj
)->hdrlen
= hdrlen
;
986 got_object_raw_close(*obj
);