Blob


1 /*
2 * Copyright (c) 2018, 2019 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/types.h>
18 #include <sys/queue.h>
19 #include <sys/uio.h>
20 #include <sys/stat.h>
21 #include <sys/mman.h>
22 #include <sys/syslimits.h>
24 #include <ctype.h>
25 #include <fcntl.h>
26 #include <limits.h>
27 #include <dirent.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <sha1.h>
31 #include <string.h>
32 #include <zlib.h>
33 #include <errno.h>
34 #include <libgen.h>
35 #include <stdint.h>
36 #include <imsg.h>
37 #include <uuid.h>
39 #include "got_error.h"
40 #include "got_reference.h"
41 #include "got_repository.h"
42 #include "got_path.h"
43 #include "got_worktree.h"
44 #include "got_object.h"
46 #include "got_lib_delta.h"
47 #include "got_lib_inflate.h"
48 #include "got_lib_object.h"
49 #include "got_lib_pack.h"
50 #include "got_lib_privsep.h"
51 #include "got_lib_worktree.h"
52 #include "got_lib_sha1.h"
53 #include "got_lib_object_cache.h"
54 #include "got_lib_repository.h"
56 #ifndef nitems
57 #define nitems(_a) (sizeof(_a) / sizeof((_a)[0]))
58 #endif
60 #define GOT_GIT_DIR ".git"
62 /* Mandatory files and directories inside the git directory. */
63 #define GOT_OBJECTS_DIR "objects"
64 #define GOT_REFS_DIR "refs"
65 #define GOT_HEAD_FILE "HEAD"
67 /* Other files and directories inside the git directory. */
68 #define GOT_FETCH_HEAD_FILE "FETCH_HEAD"
69 #define GOT_ORIG_HEAD_FILE "ORIG_HEAD"
70 #define GOT_OBJECTS_PACK_DIR "objects/pack"
71 #define GOT_PACKED_REFS_FILE "packed-refs"
73 const char *
74 got_repo_get_path(struct got_repository *repo)
75 {
76 return repo->path;
77 }
79 const char *
80 got_repo_get_path_git_dir(struct got_repository *repo)
81 {
82 return repo->path_git_dir;
83 }
85 int
86 got_repo_is_bare(struct got_repository *repo)
87 {
88 return (strcmp(repo->path, repo->path_git_dir) == 0);
89 }
91 static char *
92 get_path_git_child(struct got_repository *repo, const char *basename)
93 {
94 char *path_child;
96 if (asprintf(&path_child, "%s/%s", repo->path_git_dir,
97 basename) == -1)
98 return NULL;
100 return path_child;
103 char *
104 got_repo_get_path_objects(struct got_repository *repo)
106 return get_path_git_child(repo, GOT_OBJECTS_DIR);
109 char *
110 got_repo_get_path_objects_pack(struct got_repository *repo)
112 return get_path_git_child(repo, GOT_OBJECTS_PACK_DIR);
115 char *
116 got_repo_get_path_refs(struct got_repository *repo)
118 return get_path_git_child(repo, GOT_REFS_DIR);
121 char *
122 got_repo_get_path_packed_refs(struct got_repository *repo)
124 return get_path_git_child(repo, GOT_PACKED_REFS_FILE);
127 static char *
128 get_path_head(struct got_repository *repo)
130 return get_path_git_child(repo, GOT_HEAD_FILE);
133 static int
134 is_git_repo(struct got_repository *repo)
136 const char *path_git = got_repo_get_path_git_dir(repo);
137 char *path_objects = got_repo_get_path_objects(repo);
138 char *path_refs = got_repo_get_path_refs(repo);
139 char *path_head = get_path_head(repo);
140 int ret = 0;
141 struct stat sb;
142 struct got_reference *head_ref;
144 if (lstat(path_git, &sb) == -1)
145 goto done;
146 if (!S_ISDIR(sb.st_mode))
147 goto done;
149 if (lstat(path_objects, &sb) == -1)
150 goto done;
151 if (!S_ISDIR(sb.st_mode))
152 goto done;
154 if (lstat(path_refs, &sb) == -1)
155 goto done;
156 if (!S_ISDIR(sb.st_mode))
157 goto done;
159 if (lstat(path_head, &sb) == -1)
160 goto done;
161 if (!S_ISREG(sb.st_mode))
162 goto done;
164 /* Check if the HEAD reference can be opened. */
165 if (got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0) != NULL)
166 goto done;
167 got_ref_close(head_ref);
169 ret = 1;
170 done:
171 free(path_objects);
172 free(path_refs);
173 free(path_head);
174 return ret;
178 const struct got_error *
179 got_repo_cache_object(struct got_repository *repo, struct got_object_id *id,
180 struct got_object *obj)
182 #ifndef GOT_NO_OBJ_CACHE
183 const struct got_error *err = NULL;
184 err = got_object_cache_add(&repo->objcache, id, obj);
185 if (err) {
186 if (err->code == GOT_ERR_OBJ_EXISTS ||
187 err->code == GOT_ERR_OBJ_TOO_LARGE)
188 err = NULL;
189 return err;
191 obj->refcnt++;
192 #endif
193 return NULL;
196 struct got_object *
197 got_repo_get_cached_object(struct got_repository *repo,
198 struct got_object_id *id)
200 return (struct got_object *)got_object_cache_get(&repo->objcache, id);
203 const struct got_error *
204 got_repo_cache_tree(struct got_repository *repo, struct got_object_id *id,
205 struct got_tree_object *tree)
207 #ifndef GOT_NO_OBJ_CACHE
208 const struct got_error *err = NULL;
209 err = got_object_cache_add(&repo->treecache, id, tree);
210 if (err) {
211 if (err->code == GOT_ERR_OBJ_EXISTS ||
212 err->code == GOT_ERR_OBJ_TOO_LARGE)
213 err = NULL;
214 return err;
216 tree->refcnt++;
217 #endif
218 return NULL;
221 struct got_tree_object *
222 got_repo_get_cached_tree(struct got_repository *repo,
223 struct got_object_id *id)
225 return (struct got_tree_object *)got_object_cache_get(
226 &repo->treecache, id);
229 const struct got_error *
230 got_repo_cache_commit(struct got_repository *repo, struct got_object_id *id,
231 struct got_commit_object *commit)
233 #ifndef GOT_NO_OBJ_CACHE
234 const struct got_error *err = NULL;
235 err = got_object_cache_add(&repo->commitcache, id, commit);
236 if (err) {
237 if (err->code == GOT_ERR_OBJ_EXISTS ||
238 err->code == GOT_ERR_OBJ_TOO_LARGE)
239 err = NULL;
240 return err;
242 commit->refcnt++;
243 #endif
244 return NULL;
247 struct got_commit_object *
248 got_repo_get_cached_commit(struct got_repository *repo,
249 struct got_object_id *id)
251 return (struct got_commit_object *)got_object_cache_get(
252 &repo->commitcache, id);
255 const struct got_error *
256 got_repo_cache_tag(struct got_repository *repo, struct got_object_id *id,
257 struct got_tag_object *tag)
259 #ifndef GOT_NO_OBJ_CACHE
260 const struct got_error *err = NULL;
261 err = got_object_cache_add(&repo->tagcache, id, tag);
262 if (err) {
263 if (err->code == GOT_ERR_OBJ_EXISTS ||
264 err->code == GOT_ERR_OBJ_TOO_LARGE)
265 err = NULL;
266 return err;
268 tag->refcnt++;
269 #endif
270 return NULL;
273 struct got_tag_object *
274 got_repo_get_cached_tag(struct got_repository *repo, struct got_object_id *id)
276 return (struct got_tag_object *)got_object_cache_get(
277 &repo->tagcache, id);
280 const struct got_error *
281 open_repo(struct got_repository *repo, const char *path)
283 const struct got_error *err = NULL;
285 /* bare git repository? */
286 repo->path_git_dir = strdup(path);
287 if (repo->path_git_dir == NULL)
288 return got_error_from_errno("strdup");
289 if (is_git_repo(repo)) {
290 repo->path = strdup(repo->path_git_dir);
291 if (repo->path == NULL) {
292 err = got_error_from_errno("strdup");
293 goto done;
295 return NULL;
298 /* git repository with working tree? */
299 free(repo->path_git_dir);
300 if (asprintf(&repo->path_git_dir, "%s/%s", path, GOT_GIT_DIR) == -1) {
301 err = got_error_from_errno("asprintf");
302 goto done;
304 if (is_git_repo(repo)) {
305 repo->path = strdup(path);
306 if (repo->path == NULL) {
307 err = got_error_from_errno("strdup");
308 goto done;
310 return NULL;
313 err = got_error(GOT_ERR_NOT_GIT_REPO);
314 done:
315 if (err) {
316 free(repo->path);
317 repo->path = NULL;
318 free(repo->path_git_dir);
319 repo->path_git_dir = NULL;
321 return err;
324 const struct got_error *
325 got_repo_open(struct got_repository **repop, const char *path)
327 struct got_repository *repo = NULL;
328 const struct got_error *err = NULL;
329 char *abspath, *normpath = NULL;
330 int i, tried_root = 0;
332 *repop = NULL;
334 if (got_path_is_absolute(path))
335 abspath = strdup(path);
336 else
337 abspath = got_path_get_absolute(path);
338 if (abspath == NULL)
339 return got_error(GOT_ERR_BAD_PATH);
341 repo = calloc(1, sizeof(*repo));
342 if (repo == NULL) {
343 err = got_error_from_errno("calloc");
344 goto done;
347 for (i = 0; i < nitems(repo->privsep_children); i++) {
348 memset(&repo->privsep_children[i], 0,
349 sizeof(repo->privsep_children[0]));
350 repo->privsep_children[i].imsg_fd = -1;
353 err = got_object_cache_init(&repo->objcache,
354 GOT_OBJECT_CACHE_TYPE_OBJ);
355 if (err)
356 goto done;
357 err = got_object_cache_init(&repo->treecache,
358 GOT_OBJECT_CACHE_TYPE_TREE);
359 if (err)
360 goto done;
361 err = got_object_cache_init(&repo->commitcache,
362 GOT_OBJECT_CACHE_TYPE_COMMIT);
363 if (err)
364 goto done;
365 err = got_object_cache_init(&repo->tagcache,
366 GOT_OBJECT_CACHE_TYPE_TAG);
367 if (err)
368 goto done;
370 normpath = got_path_normalize(abspath);
371 if (normpath == NULL) {
372 err = got_error(GOT_ERR_BAD_PATH);
373 goto done;
376 path = normpath;
377 do {
378 err = open_repo(repo, path);
379 if (err == NULL)
380 break;
381 if (err->code != GOT_ERR_NOT_GIT_REPO)
382 break;
383 if (path[0] == '/' && path[1] == '\0') {
384 if (tried_root) {
385 err = got_error(GOT_ERR_NOT_GIT_REPO);
386 break;
388 tried_root = 1;
390 path = dirname(path);
391 if (path == NULL)
392 err = got_error_from_errno2("dirname", path);
393 } while (path);
394 done:
395 if (err)
396 got_repo_close(repo);
397 else
398 *repop = repo;
399 free(abspath);
400 free(normpath);
401 return err;
404 const struct got_error *
405 got_repo_close(struct got_repository *repo)
407 const struct got_error *err = NULL, *child_err;
408 int i;
410 for (i = 0; i < nitems(repo->packidx_cache); i++) {
411 if (repo->packidx_cache[i] == NULL)
412 break;
413 got_packidx_close(repo->packidx_cache[i]);
416 for (i = 0; i < nitems(repo->packs); i++) {
417 if (repo->packs[i].path_packfile == NULL)
418 break;
419 got_pack_close(&repo->packs[i]);
422 free(repo->path);
423 free(repo->path_git_dir);
425 got_object_cache_close(&repo->objcache);
426 got_object_cache_close(&repo->treecache);
427 got_object_cache_close(&repo->commitcache);
428 got_object_cache_close(&repo->tagcache);
430 for (i = 0; i < nitems(repo->privsep_children); i++) {
431 if (repo->privsep_children[i].imsg_fd == -1)
432 continue;
433 imsg_clear(repo->privsep_children[i].ibuf);
434 free(repo->privsep_children[i].ibuf);
435 err = got_privsep_send_stop(repo->privsep_children[i].imsg_fd);
436 child_err = got_privsep_wait_for_child(
437 repo->privsep_children[i].pid);
438 if (child_err && err == NULL)
439 err = child_err;
440 if (close(repo->privsep_children[i].imsg_fd) != 0 &&
441 err == NULL)
442 err = got_error_from_errno("close");
444 free(repo);
446 return err;
449 const struct got_error *
450 got_repo_map_path(char **in_repo_path, struct got_repository *repo,
451 const char *input_path, int check_disk)
453 const struct got_error *err = NULL;
454 const char *repo_abspath = NULL;
455 size_t repolen, cwdlen, len;
456 char *cwd, *canonpath, *path = NULL;
458 *in_repo_path = NULL;
460 cwd = getcwd(NULL, 0);
461 if (cwd == NULL)
462 return got_error_from_errno("getcwd");
464 canonpath = strdup(input_path);
465 if (canonpath == NULL) {
466 err = got_error_from_errno("strdup");
467 goto done;
469 err = got_canonpath(input_path, canonpath, strlen(canonpath) + 1);
470 if (err)
471 goto done;
473 repo_abspath = got_repo_get_path(repo);
475 if (!check_disk || canonpath[0] == '\0') {
476 path = strdup(canonpath);
477 if (path == NULL) {
478 err = got_error_from_errno("strdup");
479 goto done;
481 } else {
482 int is_repo_child = 0, is_cwd_child = 0;
484 path = realpath(canonpath, NULL);
485 if (path == NULL) {
486 if (errno != ENOENT) {
487 err = got_error_from_errno2("realpath",
488 canonpath);
489 goto done;
491 /*
492 * Path is not on disk.
493 * Assume it is already relative to repository root.
494 */
495 path = strdup(canonpath);
496 if (path == NULL) {
497 err = got_error_from_errno("strdup");
498 goto done;
502 repolen = strlen(repo_abspath);
503 cwdlen = strlen(cwd);
504 len = strlen(path);
506 if (len > repolen && strncmp(path, repo_abspath, repolen) == 0)
507 is_repo_child = 1;
508 if (len > cwdlen && strncmp(path, cwd, cwdlen) == 0)
509 is_cwd_child = 1;
511 if (strcmp(path, repo_abspath) == 0) {
512 free(path);
513 path = strdup("");
514 if (path == NULL) {
515 err = got_error_from_errno("strdup");
516 goto done;
518 } else if (is_repo_child && is_cwd_child) {
519 char *child;
520 /* Strip common prefix with repository path. */
521 err = got_path_skip_common_ancestor(&child,
522 repo_abspath, path);
523 if (err)
524 goto done;
525 free(path);
526 path = child;
527 } else if (is_repo_child) {
528 /* Matched an on-disk path inside repository. */
529 if (got_repo_is_bare(repo)) {
530 /*
531 * Matched an on-disk path inside repository
532 * database. Treat as repository-relative.
533 */
534 } else {
535 char *child;
536 /* Strip common prefix with repository path. */
537 err = got_path_skip_common_ancestor(&child,
538 repo_abspath, path);
539 if (err)
540 goto done;
541 free(path);
542 path = child;
544 } else if (is_cwd_child) {
545 char *child;
546 /* Strip common prefix with cwd. */
547 err = got_path_skip_common_ancestor(&child, cwd,
548 path);
549 if (err)
550 goto done;
551 free(path);
552 path = child;
553 } else {
554 /*
555 * Matched unrelated on-disk path.
556 * Treat it as repository-relative.
557 */
561 /* Make in-repository path absolute */
562 if (path[0] != '/') {
563 char *abspath;
564 if (asprintf(&abspath, "/%s", path) == -1) {
565 err = got_error_from_errno("asprintf");
566 goto done;
568 free(path);
569 path = abspath;
572 done:
573 free(cwd);
574 free(canonpath);
575 if (err)
576 free(path);
577 else
578 *in_repo_path = path;
579 return err;
582 const struct got_error *
583 got_repo_cache_packidx(struct got_repository *repo, struct got_packidx *packidx)
585 const struct got_error *err = NULL;
586 int i;
588 for (i = 0; i < nitems(repo->packidx_cache); i++) {
589 if (repo->packidx_cache[i] == NULL)
590 break;
592 if (i == nitems(repo->packidx_cache)) {
593 err = got_packidx_close(repo->packidx_cache[i - 1]);
594 if (err)
595 return err;
598 /*
599 * Insert the new pack index at the front so it will
600 * be searched first in the future.
601 */
602 memmove(&repo->packidx_cache[1], &repo->packidx_cache[0],
603 sizeof(repo->packidx_cache) -
604 sizeof(repo->packidx_cache[0]));
605 repo->packidx_cache[0] = packidx;
607 return NULL;
610 static int
611 is_packidx_filename(const char *name, size_t len)
613 if (len != GOT_PACKIDX_NAMELEN)
614 return 0;
616 if (strncmp(name, GOT_PACK_PREFIX, strlen(GOT_PACK_PREFIX)) != 0)
617 return 0;
619 if (strcmp(name + strlen(GOT_PACK_PREFIX) +
620 SHA1_DIGEST_STRING_LENGTH - 1, GOT_PACKIDX_SUFFIX) != 0)
621 return 0;
623 return 1;
626 const struct got_error *
627 got_repo_search_packidx(struct got_packidx **packidx, int *idx,
628 struct got_repository *repo, struct got_object_id *id)
630 const struct got_error *err;
631 char *path_packdir;
632 DIR *packdir;
633 struct dirent *dent;
634 char *path_packidx;
635 int i;
637 /* Search pack index cache. */
638 for (i = 0; i < nitems(repo->packidx_cache); i++) {
639 if (repo->packidx_cache[i] == NULL)
640 break;
641 *idx = got_packidx_get_object_idx(repo->packidx_cache[i], id);
642 if (*idx != -1) {
643 *packidx = repo->packidx_cache[i];
644 return NULL;
647 /* No luck. Search the filesystem. */
649 path_packdir = got_repo_get_path_objects_pack(repo);
650 if (path_packdir == NULL)
651 return got_error_from_errno("got_repo_get_path_objects_pack");
653 packdir = opendir(path_packdir);
654 if (packdir == NULL) {
655 err = got_error_from_errno2("opendir", path_packdir);
656 goto done;
659 while ((dent = readdir(packdir)) != NULL) {
660 if (!is_packidx_filename(dent->d_name, dent->d_namlen))
661 continue;
663 if (asprintf(&path_packidx, "%s/%s", path_packdir,
664 dent->d_name) == -1) {
665 err = got_error_from_errno("asprintf");
666 goto done;
669 err = got_packidx_open(packidx, path_packidx, 0);
670 free(path_packidx);
671 if (err)
672 goto done;
674 *idx = got_packidx_get_object_idx(*packidx, id);
675 if (*idx != -1) {
676 err = NULL; /* found the object */
677 err = got_repo_cache_packidx(repo, *packidx);
678 goto done;
681 err = got_packidx_close(*packidx);
682 *packidx = NULL;
683 if (err)
684 goto done;
687 err = got_error_no_obj(id);
688 done:
689 free(path_packdir);
690 if (packdir && closedir(packdir) != 0 && err == NULL)
691 err = got_error_from_errno("closedir");
692 return err;
695 static const struct got_error *
696 read_packfile_hdr(int fd, struct got_packidx *packidx)
698 const struct got_error *err = NULL;
699 uint32_t totobj = betoh32(packidx->hdr.fanout_table[0xff]);
700 struct got_packfile_hdr hdr;
701 ssize_t n;
703 n = read(fd, &hdr, sizeof(hdr));
704 if (n < 0)
705 return got_error_from_errno("read");
706 if (n != sizeof(hdr))
707 return got_error(GOT_ERR_BAD_PACKFILE);
709 if (betoh32(hdr.signature) != GOT_PACKFILE_SIGNATURE ||
710 betoh32(hdr.version) != GOT_PACKFILE_VERSION ||
711 betoh32(hdr.nobjects) != totobj)
712 err = got_error(GOT_ERR_BAD_PACKFILE);
714 return err;
717 static const struct got_error *
718 open_packfile(int *fd, const char *path_packfile, struct got_packidx *packidx)
720 const struct got_error *err = NULL;
722 *fd = open(path_packfile, O_RDONLY | O_NOFOLLOW);
723 if (*fd == -1)
724 return got_error_from_errno2("open", path_packfile);
726 if (packidx) {
727 err = read_packfile_hdr(*fd, packidx);
728 if (err) {
729 close(*fd);
730 *fd = -1;
734 return err;
737 const struct got_error *
738 got_repo_cache_pack(struct got_pack **packp, struct got_repository *repo,
739 const char *path_packfile, struct got_packidx *packidx)
741 const struct got_error *err = NULL;
742 struct got_pack *pack = NULL;
743 struct stat sb;
744 int i;
746 if (packp)
747 *packp = NULL;
749 for (i = 0; i < nitems(repo->packs); i++) {
750 pack = &repo->packs[i];
751 if (pack->path_packfile == NULL)
752 break;
753 if (strcmp(pack->path_packfile, path_packfile) == 0)
754 return NULL;
757 if (i == nitems(repo->packs) - 1) {
758 err = got_pack_close(&repo->packs[i - 1]);
759 if (err)
760 return err;
761 memmove(&repo->packs[1], &repo->packs[0],
762 sizeof(repo->packs) - sizeof(repo->packs[0]));
763 i = 0;
766 pack = &repo->packs[i];
768 pack->path_packfile = strdup(path_packfile);
769 if (pack->path_packfile == NULL) {
770 err = got_error_from_errno("strdup");
771 goto done;
774 err = open_packfile(&pack->fd, path_packfile, packidx);
775 if (err)
776 goto done;
778 if (fstat(pack->fd, &sb) != 0) {
779 err = got_error_from_errno("fstat");
780 goto done;
782 pack->filesize = sb.st_size;
784 pack->privsep_child = NULL;
786 #ifndef GOT_PACK_NO_MMAP
787 pack->map = mmap(NULL, pack->filesize, PROT_READ, MAP_PRIVATE,
788 pack->fd, 0);
789 if (pack->map == MAP_FAILED) {
790 if (errno != ENOMEM) {
791 err = got_error_from_errno("mmap");
792 goto done;
794 pack->map = NULL; /* fall back to read(2) */
796 #endif
797 done:
798 if (err) {
799 if (pack) {
800 free(pack->path_packfile);
801 memset(pack, 0, sizeof(*pack));
803 } else if (packp)
804 *packp = pack;
805 return err;
808 struct got_pack *
809 got_repo_get_cached_pack(struct got_repository *repo, const char *path_packfile)
811 struct got_pack *pack = NULL;
812 int i;
814 for (i = 0; i < nitems(repo->packs); i++) {
815 pack = &repo->packs[i];
816 if (pack->path_packfile == NULL)
817 break;
818 if (strcmp(pack->path_packfile, path_packfile) == 0)
819 return pack;
822 return NULL;
825 const struct got_error *
826 got_repo_init(const char *repo_path)
828 const struct got_error *err = NULL;
829 const char *dirnames[] = {
830 GOT_OBJECTS_DIR,
831 GOT_OBJECTS_PACK_DIR,
832 GOT_REFS_DIR,
833 };
834 const char *description_str = "Unnamed repository; "
835 "edit this file 'description' to name the repository.";
836 const char *headref_str = "ref: refs/heads/master";
837 const char *gitconfig_str = "[core]\n"
838 "\trepositoryformatversion = 0\n"
839 "\tfilemode = true\n"
840 "\tbare = true\n";
841 char *path;
842 int i;
844 if (!got_path_dir_is_empty(repo_path))
845 return got_error(GOT_ERR_DIR_NOT_EMPTY);
847 for (i = 0; i < nitems(dirnames); i++) {
848 if (asprintf(&path, "%s/%s", repo_path, dirnames[i]) == -1) {
849 return got_error_from_errno("asprintf");
851 err = got_path_mkdir(path);
852 free(path);
853 if (err)
854 return err;
857 if (asprintf(&path, "%s/%s", repo_path, "description") == -1)
858 return got_error_from_errno("asprintf");
859 err = got_path_create_file(path, description_str);
860 free(path);
861 if (err)
862 return err;
864 if (asprintf(&path, "%s/%s", repo_path, GOT_HEAD_FILE) == -1)
865 return got_error_from_errno("asprintf");
866 err = got_path_create_file(path, headref_str);
867 free(path);
868 if (err)
869 return err;
871 if (asprintf(&path, "%s/%s", repo_path, "config") == -1)
872 return got_error_from_errno("asprintf");
873 err = got_path_create_file(path, gitconfig_str);
874 free(path);
875 if (err)
876 return err;
878 return NULL;
881 static const struct got_error *
882 match_packed_object(struct got_object_id **unique_id,
883 struct got_repository *repo, const char *id_str_prefix, int obj_type)
885 const struct got_error *err = NULL;
886 char *path_packdir;
887 DIR *packdir;
888 struct dirent *dent;
889 char *path_packidx;
890 struct got_object_id_queue matched_ids;
892 SIMPLEQ_INIT(&matched_ids);
894 path_packdir = got_repo_get_path_objects_pack(repo);
895 if (path_packdir == NULL)
896 return got_error_from_errno("got_repo_get_path_objects_pack");
898 packdir = opendir(path_packdir);
899 if (packdir == NULL) {
900 err = got_error_from_errno2("opendir", path_packdir);
901 goto done;
904 while ((dent = readdir(packdir)) != NULL) {
905 struct got_packidx *packidx;
906 struct got_object_qid *qid;
909 if (!is_packidx_filename(dent->d_name, dent->d_namlen))
910 continue;
912 if (asprintf(&path_packidx, "%s/%s", path_packdir,
913 dent->d_name) == -1) {
914 err = got_error_from_errno("asprintf");
915 break;
918 err = got_packidx_open(&packidx, path_packidx, 0);
919 free(path_packidx);
920 if (err)
921 break;
923 err = got_packidx_match_id_str_prefix(&matched_ids,
924 packidx, id_str_prefix);
925 if (err) {
926 got_packidx_close(packidx);
927 break;
929 err = got_packidx_close(packidx);
930 if (err)
931 break;
933 SIMPLEQ_FOREACH(qid, &matched_ids, entry) {
934 if (obj_type != GOT_OBJ_TYPE_ANY) {
935 int matched_type;
936 err = got_object_get_type(&matched_type, repo,
937 qid->id);
938 if (err)
939 goto done;
940 if (matched_type != obj_type)
941 continue;
943 if (*unique_id == NULL) {
944 *unique_id = got_object_id_dup(qid->id);
945 if (*unique_id == NULL) {
946 err = got_error_from_errno("malloc");
947 goto done;
949 } else {
950 err = got_error(GOT_ERR_AMBIGUOUS_ID);
951 goto done;
955 done:
956 got_object_id_queue_free(&matched_ids);
957 free(path_packdir);
958 if (packdir && closedir(packdir) != 0 && err == NULL)
959 err = got_error_from_errno("closedir");
960 if (err) {
961 free(*unique_id);
962 *unique_id = NULL;
964 return err;
967 static const struct got_error *
968 match_loose_object(struct got_object_id **unique_id, const char *path_objects,
969 const char *object_dir, const char *id_str_prefix, int obj_type,
970 struct got_repository *repo)
972 const struct got_error *err = NULL;
973 char *path;
974 DIR *dir = NULL;
975 struct dirent *dent;
976 struct got_object_id id;
978 if (asprintf(&path, "%s/%s", path_objects, object_dir) == -1) {
979 err = got_error_from_errno("asprintf");
980 goto done;
983 dir = opendir(path);
984 if (dir == NULL) {
985 if (errno == ENOENT) {
986 err = NULL;
987 goto done;
989 err = got_error_from_errno2("opendir", path);
990 goto done;
992 while ((dent = readdir(dir)) != NULL) {
993 char *id_str;
994 int cmp;
996 if (strcmp(dent->d_name, ".") == 0 ||
997 strcmp(dent->d_name, "..") == 0)
998 continue;
1000 if (asprintf(&id_str, "%s%s", object_dir, dent->d_name) == -1) {
1001 err = got_error_from_errno("asprintf");
1002 goto done;
1005 if (!got_parse_sha1_digest(id.sha1, id_str))
1006 continue;
1009 * Directory entries do not necessarily appear in
1010 * sorted order, so we must iterate over all of them.
1012 cmp = strncmp(id_str, id_str_prefix, strlen(id_str_prefix));
1013 if (cmp != 0) {
1014 free(id_str);
1015 continue;
1018 if (*unique_id == NULL) {
1019 if (obj_type != GOT_OBJ_TYPE_ANY) {
1020 int matched_type;
1021 err = got_object_get_type(&matched_type, repo,
1022 &id);
1023 if (err)
1024 goto done;
1025 if (matched_type != obj_type)
1026 continue;
1028 *unique_id = got_object_id_dup(&id);
1029 if (*unique_id == NULL) {
1030 err = got_error_from_errno("got_object_id_dup");
1031 free(id_str);
1032 goto done;
1034 } else {
1035 err = got_error(GOT_ERR_AMBIGUOUS_ID);
1036 free(id_str);
1037 goto done;
1040 done:
1041 if (dir && closedir(dir) != 0 && err == NULL)
1042 err = got_error_from_errno("closedir");
1043 if (err) {
1044 free(*unique_id);
1045 *unique_id = NULL;
1047 free(path);
1048 return err;
1051 const struct got_error *
1052 got_repo_match_object_id_prefix(struct got_object_id **id,
1053 const char *id_str_prefix, int obj_type, struct got_repository *repo)
1055 const struct got_error *err = NULL;
1056 char *path_objects = got_repo_get_path_objects(repo);
1057 char *object_dir = NULL;
1058 size_t len;
1059 int i;
1061 *id = NULL;
1063 for (i = 0; i < strlen(id_str_prefix); i++) {
1064 if (isxdigit((unsigned char)id_str_prefix[i]))
1065 continue;
1066 return got_error(GOT_ERR_BAD_OBJ_ID_STR);
1069 len = strlen(id_str_prefix);
1070 if (len >= 2) {
1071 err = match_packed_object(id, repo, id_str_prefix, obj_type);
1072 if (err)
1073 goto done;
1074 object_dir = strndup(id_str_prefix, 2);
1075 if (object_dir == NULL) {
1076 err = got_error_from_errno("strdup");
1077 goto done;
1079 err = match_loose_object(id, path_objects, object_dir,
1080 id_str_prefix, obj_type, repo);
1081 } else if (len == 1) {
1082 int i;
1083 for (i = 0; i < 0xf; i++) {
1084 if (asprintf(&object_dir, "%s%.1x", id_str_prefix, i)
1085 == -1) {
1086 err = got_error_from_errno("asprintf");
1087 goto done;
1089 err = match_packed_object(id, repo, object_dir,
1090 obj_type);
1091 if (err)
1092 goto done;
1093 err = match_loose_object(id, path_objects, object_dir,
1094 id_str_prefix, obj_type, repo);
1095 if (err)
1096 goto done;
1098 } else {
1099 err = got_error(GOT_ERR_BAD_OBJ_ID_STR);
1100 goto done;
1102 done:
1103 free(object_dir);
1104 if (err) {
1105 free(*id);
1106 *id = NULL;
1107 } else if (*id == NULL)
1108 err = got_error(GOT_ERR_NO_OBJ);
1110 return err;