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 <sys/queue.h>
31 #include "got_error.h"
32 #include "got_object.h"
35 #include "got_lib_fileindex.h"
36 #include "got_lib_worktree.h"
38 /* got_fileindex_entry flags */
39 #define GOT_FILEIDX_F_PATH_LEN 0x00000fff
40 #define GOT_FILEIDX_F_STAGE 0x0000f000
41 #define GOT_FILEIDX_F_STAGE_SHIFT 12
42 #define GOT_FILEIDX_F_NOT_FLUSHED 0x00010000
43 #define GOT_FILEIDX_F_NO_BLOB 0x00020000
44 #define GOT_FILEIDX_F_NO_COMMIT 0x00040000
45 #define GOT_FILEIDX_F_NO_FILE_ON_DISK 0x00080000
47 struct got_fileindex {
48 struct got_fileindex_tree entries;
50 #define GOT_FILEIDX_MAX_ENTRIES INT_MAX
54 got_fileindex_perms_from_st(struct stat *sb)
56 uint16_t perms = (sb->st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
57 return (perms << GOT_FILEIDX_MODE_PERMS_SHIFT);
61 got_fileindex_perms_to_st(struct got_fileindex_entry *ie)
63 mode_t perms = (ie->mode >> GOT_FILEIDX_MODE_PERMS_SHIFT);
64 return (S_IFREG | (perms & (S_IRWXU | S_IRWXG | S_IRWXO)));
67 const struct got_error *
68 got_fileindex_entry_update(struct got_fileindex_entry *ie,
69 const char *ondisk_path, uint8_t *blob_sha1, uint8_t *commit_sha1,
70 int update_timestamps)
74 if (lstat(ondisk_path, &sb) != 0) {
75 if ((ie->flags & GOT_FILEIDX_F_NO_FILE_ON_DISK) == 0)
76 return got_error_from_errno2("lstat", ondisk_path);
78 if (sb.st_mode & S_IFDIR)
79 return got_error_set_errno(EISDIR, ondisk_path);
80 ie->flags &= ~GOT_FILEIDX_F_NO_FILE_ON_DISK;
84 if ((ie->flags & GOT_FILEIDX_F_NO_FILE_ON_DISK) == 0) {
85 if (update_timestamps) {
86 ie->ctime_sec = sb.st_ctime;
87 ie->ctime_nsec = sb.st_ctimensec;
88 ie->mtime_sec = sb.st_mtime;
89 ie->mtime_nsec = sb.st_mtimensec;
93 ie->size = (sb.st_size & 0xffffffff);
94 if (sb.st_mode & S_IFLNK)
95 ie->mode = GOT_FILEIDX_MODE_SYMLINK;
97 ie->mode = GOT_FILEIDX_MODE_REGULAR_FILE;
98 ie->mode |= got_fileindex_perms_from_st(&sb);
102 memcpy(ie->blob_sha1, blob_sha1, SHA1_DIGEST_LENGTH);
103 ie->flags &= ~GOT_FILEIDX_F_NO_BLOB;
105 ie->flags |= GOT_FILEIDX_F_NO_BLOB;
108 memcpy(ie->commit_sha1, commit_sha1, SHA1_DIGEST_LENGTH);
109 ie->flags &= ~GOT_FILEIDX_F_NO_COMMIT;
111 ie->flags |= GOT_FILEIDX_F_NO_COMMIT;
117 got_fileindex_entry_mark_deleted_from_disk(struct got_fileindex_entry *ie)
119 ie->flags |= GOT_FILEIDX_F_NO_FILE_ON_DISK;
122 const struct got_error *
123 got_fileindex_entry_alloc(struct got_fileindex_entry **ie,
124 const char *ondisk_path, const char *relpath, uint8_t *blob_sha1,
125 uint8_t *commit_sha1)
129 *ie = calloc(1, sizeof(**ie));
131 return got_error_from_errno("calloc");
133 (*ie)->path = strdup(relpath);
134 if ((*ie)->path == NULL) {
135 const struct got_error *err = got_error_from_errno("strdup");
141 len = strlen(relpath);
142 if (len > GOT_FILEIDX_F_PATH_LEN)
143 len = GOT_FILEIDX_F_PATH_LEN;
146 return got_fileindex_entry_update(*ie, ondisk_path, blob_sha1,
151 got_fileindex_entry_free(struct got_fileindex_entry *ie)
158 got_fileindex_entry_path_len(const struct got_fileindex_entry *ie)
160 return (size_t)(ie->flags & GOT_FILEIDX_F_PATH_LEN);
164 got_fileindex_entry_stage_get(const struct got_fileindex_entry *ie)
166 return ((ie->flags & GOT_FILEIDX_F_STAGE) >> GOT_FILEIDX_F_STAGE_SHIFT);
170 got_fileindex_entry_stage_set(struct got_fileindex_entry *ie, uint32_t stage)
172 ie->flags &= ~GOT_FILEIDX_F_STAGE;
173 ie->flags |= ((stage << GOT_FILEIDX_F_STAGE_SHIFT) &
174 GOT_FILEIDX_F_STAGE);
178 got_fileindex_entry_has_blob(struct got_fileindex_entry *ie)
180 return (ie->flags & GOT_FILEIDX_F_NO_BLOB) == 0;
184 got_fileindex_entry_has_commit(struct got_fileindex_entry *ie)
186 return (ie->flags & GOT_FILEIDX_F_NO_COMMIT) == 0;
190 got_fileindex_entry_has_file_on_disk(struct got_fileindex_entry *ie)
192 return (ie->flags & GOT_FILEIDX_F_NO_FILE_ON_DISK) == 0;
195 static const struct got_error *
196 add_entry(struct got_fileindex *fileindex, struct got_fileindex_entry *ie)
198 if (fileindex->nentries >= GOT_FILEIDX_MAX_ENTRIES)
199 return got_error(GOT_ERR_NO_SPACE);
201 RB_INSERT(got_fileindex_tree, &fileindex->entries, ie);
202 fileindex->nentries++;
206 const struct got_error *
207 got_fileindex_entry_add(struct got_fileindex *fileindex,
208 struct got_fileindex_entry *ie)
210 /* Flag this entry until it gets written out to disk. */
211 ie->flags |= GOT_FILEIDX_F_NOT_FLUSHED;
213 return add_entry(fileindex, ie);
217 got_fileindex_entry_remove(struct got_fileindex *fileindex,
218 struct got_fileindex_entry *ie)
220 RB_REMOVE(got_fileindex_tree, &fileindex->entries, ie);
221 fileindex->nentries--;
224 struct got_fileindex_entry *
225 got_fileindex_entry_get(struct got_fileindex *fileindex, const char *path,
228 struct got_fileindex_entry key;
229 memset(&key, 0, sizeof(key));
230 key.path = (char *)path;
231 key.flags = (path_len & GOT_FILEIDX_F_PATH_LEN);
232 return RB_FIND(got_fileindex_tree, &fileindex->entries, &key);
235 const struct got_error *
236 got_fileindex_for_each_entry_safe(struct got_fileindex *fileindex,
237 got_fileindex_cb cb, void *cb_arg)
239 const struct got_error *err;
240 struct got_fileindex_entry *ie, *tmp;
242 RB_FOREACH_SAFE(ie, got_fileindex_tree, &fileindex->entries, tmp) {
243 err = (*cb)(cb_arg, ie);
250 struct got_fileindex *
251 got_fileindex_alloc(void)
253 struct got_fileindex *fileindex;
255 fileindex = calloc(1, sizeof(*fileindex));
256 if (fileindex == NULL)
259 RB_INIT(&fileindex->entries);
264 got_fileindex_free(struct got_fileindex *fileindex)
266 struct got_fileindex_entry *ie;
268 while ((ie = RB_MIN(got_fileindex_tree, &fileindex->entries))) {
269 RB_REMOVE(got_fileindex_tree, &fileindex->entries, ie);
270 got_fileindex_entry_free(ie);
275 static const struct got_error *
276 write_fileindex_val64(SHA1_CTX *ctx, uint64_t val, FILE *outfile)
281 SHA1Update(ctx, (uint8_t *)&val, sizeof(val));
282 n = fwrite(&val, 1, sizeof(val), outfile);
283 if (n != sizeof(val))
284 return got_ferror(outfile, GOT_ERR_IO);
288 static const struct got_error *
289 write_fileindex_val32(SHA1_CTX *ctx, uint32_t val, FILE *outfile)
294 SHA1Update(ctx, (uint8_t *)&val, sizeof(val));
295 n = fwrite(&val, 1, sizeof(val), outfile);
296 if (n != sizeof(val))
297 return got_ferror(outfile, GOT_ERR_IO);
301 static const struct got_error *
302 write_fileindex_val16(SHA1_CTX *ctx, uint16_t val, FILE *outfile)
307 SHA1Update(ctx, (uint8_t *)&val, sizeof(val));
308 n = fwrite(&val, 1, sizeof(val), outfile);
309 if (n != sizeof(val))
310 return got_ferror(outfile, GOT_ERR_IO);
314 static const struct got_error *
315 write_fileindex_path(SHA1_CTX *ctx, const char *path, FILE *outfile)
317 size_t n, len, pad = 0;
318 static const uint8_t zero[8] = { 0 };
321 while ((len + pad) % 8 != 0)
324 pad = 8; /* NUL-terminate */
326 SHA1Update(ctx, path, len);
327 n = fwrite(path, 1, len, outfile);
329 return got_ferror(outfile, GOT_ERR_IO);
330 SHA1Update(ctx, zero, pad);
331 n = fwrite(zero, 1, pad, outfile);
333 return got_ferror(outfile, GOT_ERR_IO);
337 static const struct got_error *
338 write_fileindex_entry(SHA1_CTX *ctx, struct got_fileindex_entry *ie,
341 const struct got_error *err;
345 err = write_fileindex_val64(ctx, ie->ctime_sec, outfile);
348 err = write_fileindex_val64(ctx, ie->ctime_nsec, outfile);
351 err = write_fileindex_val64(ctx, ie->mtime_sec, outfile);
354 err = write_fileindex_val64(ctx, ie->mtime_nsec, outfile);
358 err = write_fileindex_val32(ctx, ie->uid, outfile);
361 err = write_fileindex_val32(ctx, ie->gid, outfile);
364 err = write_fileindex_val32(ctx, ie->size, outfile);
368 err = write_fileindex_val16(ctx, ie->mode, outfile);
372 SHA1Update(ctx, ie->blob_sha1, SHA1_DIGEST_LENGTH);
373 n = fwrite(ie->blob_sha1, 1, SHA1_DIGEST_LENGTH, outfile);
374 if (n != SHA1_DIGEST_LENGTH)
375 return got_ferror(outfile, GOT_ERR_IO);
377 SHA1Update(ctx, ie->commit_sha1, SHA1_DIGEST_LENGTH);
378 n = fwrite(ie->commit_sha1, 1, SHA1_DIGEST_LENGTH, outfile);
379 if (n != SHA1_DIGEST_LENGTH)
380 return got_ferror(outfile, GOT_ERR_IO);
382 err = write_fileindex_val32(ctx, ie->flags, outfile);
386 err = write_fileindex_path(ctx, ie->path, outfile);
390 stage = got_fileindex_entry_stage_get(ie);
391 if (stage == GOT_FILEIDX_STAGE_MODIFY ||
392 stage == GOT_FILEIDX_STAGE_ADD) {
393 SHA1Update(ctx, ie->staged_blob_sha1, SHA1_DIGEST_LENGTH);
394 n = fwrite(ie->staged_blob_sha1, 1, SHA1_DIGEST_LENGTH,
396 if (n != SHA1_DIGEST_LENGTH)
397 return got_ferror(outfile, GOT_ERR_IO);
403 const struct got_error *
404 got_fileindex_write(struct got_fileindex *fileindex, FILE *outfile)
406 const struct got_error *err = NULL;
407 struct got_fileindex_hdr hdr;
409 uint8_t sha1[SHA1_DIGEST_LENGTH];
411 struct got_fileindex_entry *ie;
415 hdr.signature = htobe32(GOT_FILE_INDEX_SIGNATURE);
416 hdr.version = htobe32(GOT_FILE_INDEX_VERSION);
417 hdr.nentries = htobe32(fileindex->nentries);
419 SHA1Update(&ctx, (uint8_t *)&hdr.signature, sizeof(hdr.signature));
420 SHA1Update(&ctx, (uint8_t *)&hdr.version, sizeof(hdr.version));
421 SHA1Update(&ctx, (uint8_t *)&hdr.nentries, sizeof(hdr.nentries));
422 n = fwrite(&hdr.signature, 1, sizeof(hdr.signature), outfile);
423 if (n != sizeof(hdr.signature))
424 return got_ferror(outfile, GOT_ERR_IO);
425 n = fwrite(&hdr.version, 1, sizeof(hdr.version), outfile);
426 if (n != sizeof(hdr.version))
427 return got_ferror(outfile, GOT_ERR_IO);
428 n = fwrite(&hdr.nentries, 1, sizeof(hdr.nentries), outfile);
429 if (n != sizeof(hdr.nentries))
430 return got_ferror(outfile, GOT_ERR_IO);
432 RB_FOREACH(ie, got_fileindex_tree, &fileindex->entries) {
433 ie->flags &= ~GOT_FILEIDX_F_NOT_FLUSHED;
434 err = write_fileindex_entry(&ctx, ie, outfile);
439 SHA1Final(sha1, &ctx);
440 n = fwrite(sha1, 1, sizeof(sha1), outfile);
441 if (n != sizeof(sha1))
442 return got_ferror(outfile, GOT_ERR_IO);
444 if (fflush(outfile) != 0)
445 return got_error_from_errno("fflush");
450 static const struct got_error *
451 read_fileindex_val64(uint64_t *val, SHA1_CTX *ctx, FILE *infile)
455 n = fread(val, 1, sizeof(*val), infile);
456 if (n != sizeof(*val))
457 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
458 SHA1Update(ctx, (uint8_t *)val, sizeof(*val));
459 *val = be64toh(*val);
463 static const struct got_error *
464 read_fileindex_val32(uint32_t *val, SHA1_CTX *ctx, FILE *infile)
468 n = fread(val, 1, sizeof(*val), infile);
469 if (n != sizeof(*val))
470 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
471 SHA1Update(ctx, (uint8_t *)val, sizeof(*val));
472 *val = be32toh(*val);
476 static const struct got_error *
477 read_fileindex_val16(uint16_t *val, SHA1_CTX *ctx, FILE *infile)
481 n = fread(val, 1, sizeof(*val), infile);
482 if (n != sizeof(*val))
483 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
484 SHA1Update(ctx, (uint8_t *)val, sizeof(*val));
485 *val = be16toh(*val);
489 static const struct got_error *
490 read_fileindex_path(char **path, SHA1_CTX *ctx, FILE *infile)
492 const struct got_error *err = NULL;
493 const size_t chunk_size = 8;
494 size_t n, len = 0, totlen = chunk_size;
496 *path = malloc(totlen);
498 return got_error_from_errno("malloc");
501 if (len + chunk_size > totlen) {
502 char *p = reallocarray(*path, totlen + chunk_size, 1);
504 err = got_error_from_errno("reallocarray");
507 totlen += chunk_size;
510 n = fread(*path + len, 1, chunk_size, infile);
511 if (n != chunk_size) {
512 err = got_ferror(infile, GOT_ERR_FILEIDX_BAD);
515 SHA1Update(ctx, *path + len, chunk_size);
517 } while (memchr(*path + len - chunk_size, '\0', chunk_size) == NULL);
526 static const struct got_error *
527 read_fileindex_entry(struct got_fileindex_entry **iep, SHA1_CTX *ctx,
528 FILE *infile, uint32_t version)
530 const struct got_error *err;
531 struct got_fileindex_entry *ie;
536 ie = calloc(1, sizeof(*ie));
538 return got_error_from_errno("calloc");
540 err = read_fileindex_val64(&ie->ctime_sec, ctx, infile);
543 err = read_fileindex_val64(&ie->ctime_nsec, ctx, infile);
546 err = read_fileindex_val64(&ie->mtime_sec, ctx, infile);
549 err = read_fileindex_val64(&ie->mtime_nsec, ctx, infile);
553 err = read_fileindex_val32(&ie->uid, ctx, infile);
556 err = read_fileindex_val32(&ie->gid, ctx, infile);
559 err = read_fileindex_val32(&ie->size, ctx, infile);
563 err = read_fileindex_val16(&ie->mode, ctx, infile);
567 n = fread(ie->blob_sha1, 1, SHA1_DIGEST_LENGTH, infile);
568 if (n != SHA1_DIGEST_LENGTH) {
569 err = got_ferror(infile, GOT_ERR_FILEIDX_BAD);
572 SHA1Update(ctx, ie->blob_sha1, SHA1_DIGEST_LENGTH);
574 n = fread(ie->commit_sha1, 1, SHA1_DIGEST_LENGTH, infile);
575 if (n != SHA1_DIGEST_LENGTH) {
576 err = got_ferror(infile, GOT_ERR_FILEIDX_BAD);
579 SHA1Update(ctx, ie->commit_sha1, SHA1_DIGEST_LENGTH);
581 err = read_fileindex_val32(&ie->flags, ctx, infile);
585 err = read_fileindex_path(&ie->path, ctx, infile);
590 uint32_t stage = got_fileindex_entry_stage_get(ie);
591 if (stage == GOT_FILEIDX_STAGE_MODIFY ||
592 stage == GOT_FILEIDX_STAGE_ADD) {
593 n = fread(ie->staged_blob_sha1, 1, SHA1_DIGEST_LENGTH,
595 if (n != SHA1_DIGEST_LENGTH) {
596 err = got_ferror(infile, GOT_ERR_FILEIDX_BAD);
599 SHA1Update(ctx, ie->staged_blob_sha1, SHA1_DIGEST_LENGTH);
602 /* GOT_FILE_INDEX_VERSION 1 does not support staging. */
603 ie->flags &= ~GOT_FILEIDX_F_STAGE;
608 got_fileindex_entry_free(ie);
614 const struct got_error *
615 got_fileindex_read(struct got_fileindex *fileindex, FILE *infile)
617 const struct got_error *err = NULL;
618 struct got_fileindex_hdr hdr;
620 struct got_fileindex_entry *ie;
621 uint8_t sha1_expected[SHA1_DIGEST_LENGTH];
622 uint8_t sha1[SHA1_DIGEST_LENGTH];
628 n = fread(&hdr.signature, 1, sizeof(hdr.signature), infile);
629 if (n != sizeof(hdr.signature)) {
630 if (n == 0) /* EOF */
632 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
634 n = fread(&hdr.version, 1, sizeof(hdr.version), infile);
635 if (n != sizeof(hdr.version)) {
636 if (n == 0) /* EOF */
638 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
640 n = fread(&hdr.nentries, 1, sizeof(hdr.nentries), infile);
641 if (n != sizeof(hdr.nentries)) {
642 if (n == 0) /* EOF */
644 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
647 SHA1Update(&ctx, (uint8_t *)&hdr.signature, sizeof(hdr.signature));
648 SHA1Update(&ctx, (uint8_t *)&hdr.version, sizeof(hdr.version));
649 SHA1Update(&ctx, (uint8_t *)&hdr.nentries, sizeof(hdr.nentries));
651 hdr.signature = be32toh(hdr.signature);
652 hdr.version = be32toh(hdr.version);
653 hdr.nentries = be32toh(hdr.nentries);
655 if (hdr.signature != GOT_FILE_INDEX_SIGNATURE)
656 return got_error(GOT_ERR_FILEIDX_SIG);
657 if (hdr.version > GOT_FILE_INDEX_VERSION)
658 return got_error(GOT_ERR_FILEIDX_VER);
660 for (i = 0; i < hdr.nentries; i++) {
661 err = read_fileindex_entry(&ie, &ctx, infile, hdr.version);
664 err = add_entry(fileindex, ie);
669 n = fread(sha1_expected, 1, sizeof(sha1_expected), infile);
670 if (n != sizeof(sha1_expected))
671 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
672 SHA1Final(sha1, &ctx);
673 if (memcmp(sha1, sha1_expected, SHA1_DIGEST_LENGTH) != 0)
674 return got_error(GOT_ERR_FILEIDX_CSUM);
679 static struct got_fileindex_entry *
680 walk_fileindex(struct got_fileindex *fileindex, struct got_fileindex_entry *ie)
682 struct got_fileindex_entry *next;
684 next = RB_NEXT(got_fileindex_tree, &fileindex->entries, ie);
686 /* Skip entries which were newly added by diff callbacks. */
687 while (next && (next->flags & GOT_FILEIDX_F_NOT_FLUSHED))
688 next = RB_NEXT(got_fileindex_tree, &fileindex->entries, next);
693 static const struct got_error *
694 diff_fileindex_tree(struct got_fileindex *, struct got_fileindex_entry **,
695 const struct got_tree_entries *, const char *, const char *,
696 struct got_repository *, struct got_fileindex_diff_tree_cb *, void *);
698 static const struct got_error *
699 walk_tree(struct got_tree_entry **next, struct got_fileindex *fileindex,
700 struct got_fileindex_entry **ie, struct got_tree_entry *te,
701 const char *path, const char *entry_name, struct got_repository *repo,
702 struct got_fileindex_diff_tree_cb *cb, void *cb_arg)
704 const struct got_error *err = NULL;
706 if (S_ISDIR(te->mode)) {
708 struct got_tree_object *subtree;
710 if (asprintf(&subpath, "%s%s%s", path,
711 path[0] == '\0' ? "" : "/", te->name) == -1)
712 return got_error_from_errno("asprintf");
714 err = got_object_open_as_tree(&subtree, repo, te->id);
720 err = diff_fileindex_tree(fileindex, ie,
721 got_object_tree_get_entries(subtree), subpath, entry_name,
724 got_object_tree_close(subtree);
729 *next = SIMPLEQ_NEXT(te, entry);
733 static const struct got_error *
734 diff_fileindex_tree(struct got_fileindex *fileindex,
735 struct got_fileindex_entry **ie, const struct got_tree_entries *entries,
736 const char *path, const char *entry_name, struct got_repository *repo,
737 struct got_fileindex_diff_tree_cb *cb, void *cb_arg)
739 const struct got_error *err = NULL;
740 struct got_tree_entry *te = NULL;
741 size_t path_len = strlen(path);
742 struct got_fileindex_entry *next;
744 te = SIMPLEQ_FIRST(&entries->head);
745 while ((*ie && got_path_is_child((*ie)->path, path, path_len)) || te) {
749 if (asprintf(&te_path, "%s/%s", path, te->name) == -1) {
750 err = got_error_from_errno("asprintf");
753 cmp = got_path_cmp((*ie)->path, te_path,
754 got_fileindex_entry_path_len(*ie), strlen(te_path));
757 if (got_path_is_child((*ie)->path, path,
758 path_len) && (entry_name == NULL ||
759 strcmp(te->name, entry_name) == 0)) {
760 err = cb->diff_old_new(cb_arg, *ie, te,
762 if (err || entry_name)
765 *ie = walk_fileindex(fileindex, *ie);
766 err = walk_tree(&te, fileindex, ie, te,
767 path, entry_name, repo, cb, cb_arg);
768 } else if (cmp < 0) {
769 next = walk_fileindex(fileindex, *ie);
770 if (got_path_is_child((*ie)->path, path,
771 path_len) && (entry_name == NULL ||
772 strcmp(te->name, entry_name) == 0)) {
773 err = cb->diff_old(cb_arg, *ie, path);
774 if (err || entry_name)
779 if ((entry_name == NULL ||
780 strcmp(te->name, entry_name) == 0)) {
781 err = cb->diff_new(cb_arg, te, path);
782 if (err || entry_name)
785 err = walk_tree(&te, fileindex, ie, te,
786 path, entry_name, repo, cb, cb_arg);
791 next = walk_fileindex(fileindex, *ie);
792 if (got_path_is_child((*ie)->path, path, path_len) &&
793 (entry_name == NULL ||
794 strcmp(te->name, entry_name) == 0)) {
795 err = cb->diff_old(cb_arg, *ie, path);
796 if (err || entry_name)
801 if (entry_name == NULL ||
802 strcmp(te->name, entry_name) == 0) {
803 err = cb->diff_new(cb_arg, te, path);
804 if (err || entry_name)
807 err = walk_tree(&te, fileindex, ie, te, path,
808 entry_name, repo, cb, cb_arg);
817 const struct got_error *
818 got_fileindex_diff_tree(struct got_fileindex *fileindex,
819 struct got_tree_object *tree, const char *path, const char *entry_name,
820 struct got_repository *repo,
821 struct got_fileindex_diff_tree_cb *cb, void *cb_arg)
823 struct got_fileindex_entry *ie;
824 ie = RB_MIN(got_fileindex_tree, &fileindex->entries);
825 while (ie && !got_path_is_child(ie->path, path, strlen(path)))
826 ie = walk_fileindex(fileindex, ie);
827 return diff_fileindex_tree(fileindex, &ie,
828 got_object_tree_get_entries(tree), path, entry_name, repo,
832 static const struct got_error *
833 diff_fileindex_dir(struct got_fileindex *, struct got_fileindex_entry **,
834 struct got_pathlist_head *, const char *, const char *,
835 struct got_repository *, struct got_fileindex_diff_dir_cb *, void *);
837 static const struct got_error *
838 read_dirlist(struct got_pathlist_head *dirlist, DIR *dir, const char *path)
840 const struct got_error *err = NULL;
841 struct got_pathlist_entry *new = NULL;
842 struct dirent *dep = NULL;
843 struct dirent *de = NULL;
846 de = malloc(sizeof(struct dirent) + NAME_MAX + 1);
848 err = got_error_from_errno("malloc");
852 if (readdir_r(dir, de, &dep) != 0) {
853 err = got_error_from_errno("readdir_r");
862 if (strcmp(de->d_name, ".") == 0 ||
863 strcmp(de->d_name, "..") == 0 ||
865 strcmp(de->d_name, GOT_WORKTREE_GOT_DIR) == 0)) {
870 err = got_pathlist_insert(&new, dirlist, de->d_name, de);
876 err = got_error(GOT_ERR_DIR_DUP_ENTRY);
886 free_dirlist(struct got_pathlist_head *dirlist)
888 struct got_pathlist_entry *dle;
890 TAILQ_FOREACH(dle, dirlist, entry)
892 got_pathlist_free(dirlist);
895 static const struct got_error *
896 walk_dir(struct got_pathlist_entry **next, struct got_fileindex *fileindex,
897 struct got_fileindex_entry **ie, struct got_pathlist_entry *dle,
898 const char *path, const char *rootpath, struct got_repository *repo,
899 struct got_fileindex_diff_dir_cb *cb, void *cb_arg)
901 const struct got_error *err = NULL;
902 struct dirent *de = dle->data;
906 if (de->d_type == DT_DIR) {
910 struct got_pathlist_head subdirlist;
912 TAILQ_INIT(&subdirlist);
914 if (asprintf(&subpath, "%s%s%s", path,
915 path[0] == '\0' ? "" : "/", de->d_name) == -1)
916 return got_error_from_errno("asprintf");
918 if (asprintf(&subdirpath, "%s/%s", rootpath, subpath) == -1) {
920 return got_error_from_errno("asprintf");
923 subdir = opendir(subdirpath);
924 if (subdir == NULL) {
927 return got_error_from_errno2("opendir", subdirpath);
930 err = read_dirlist(&subdirlist, subdir, subdirpath);
937 err = diff_fileindex_dir(fileindex, ie, &subdirlist, rootpath,
938 subpath, repo, cb, cb_arg);
942 free_dirlist(&subdirlist);
947 *next = TAILQ_NEXT(dle, entry);
951 static const struct got_error *
952 diff_fileindex_dir(struct got_fileindex *fileindex,
953 struct got_fileindex_entry **ie, struct got_pathlist_head *dirlist,
954 const char *rootpath, const char *path, struct got_repository *repo,
955 struct got_fileindex_diff_dir_cb *cb, void *cb_arg)
957 const struct got_error *err = NULL;
958 struct dirent *de = NULL;
959 size_t path_len = strlen(path);
960 struct got_pathlist_entry *dle;
962 dle = TAILQ_FIRST(dirlist);
963 while ((*ie && got_path_is_child((*ie)->path, path, path_len)) || dle) {
968 if (asprintf(&de_path, "%s/%s", path,
970 err = got_error_from_errno("asprintf");
973 cmp = got_path_cmp((*ie)->path, de_path,
974 got_fileindex_entry_path_len(*ie),
975 strlen(path) + 1 + de->d_namlen);
978 err = cb->diff_old_new(cb_arg, *ie, de, path);
981 *ie = walk_fileindex(fileindex, *ie);
982 err = walk_dir(&dle, fileindex, ie, dle, path,
983 rootpath, repo, cb, cb_arg);
984 } else if (cmp < 0 ) {
985 err = cb->diff_old(cb_arg, *ie, path);
988 *ie = walk_fileindex(fileindex, *ie);
990 err = cb->diff_new(cb_arg, de, path);
993 err = walk_dir(&dle, fileindex, ie, dle, path,
994 rootpath, repo, cb, cb_arg);
999 err = cb->diff_old(cb_arg, *ie, path);
1002 *ie = walk_fileindex(fileindex, *ie);
1005 err = cb->diff_new(cb_arg, de, path);
1008 err = walk_dir(&dle, fileindex, ie, dle, path,
1009 rootpath, repo, cb, cb_arg);
1018 const struct got_error *
1019 got_fileindex_diff_dir(struct got_fileindex *fileindex, DIR *rootdir,
1020 const char *rootpath, const char *path, struct got_repository *repo,
1021 struct got_fileindex_diff_dir_cb *cb, void *cb_arg)
1023 const struct got_error *err;
1024 struct got_fileindex_entry *ie;
1025 struct got_pathlist_head dirlist;
1027 TAILQ_INIT(&dirlist);
1028 err = read_dirlist(&dirlist, rootdir, path);
1031 ie = RB_MIN(got_fileindex_tree, &fileindex->entries);
1032 while (ie && !got_path_is_child(ie->path, path, strlen(path)))
1033 ie = walk_fileindex(fileindex, ie);
1034 err = diff_fileindex_dir(fileindex, &ie, &dirlist, rootpath, path,
1036 free_dirlist(&dirlist);
1040 RB_GENERATE(got_fileindex_tree, got_fileindex_entry, entry, got_fileindex_cmp);