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/queue.h>
18 #include <sys/tree.h>
19 #include <sys/stat.h>
21 #include <errno.h>
22 #include <dirent.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sha1.h>
27 #include <endian.h>
28 #include <limits.h>
29 #include <uuid.h>
31 #include "got_error.h"
32 #include "got_object.h"
33 #include "got_path.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 0x00003000
41 #define GOT_FILEIDX_F_EXTENDED 0x00004000
42 #define GOT_FILEIDX_F_ASSUME_VALID 0x00008000
43 #define GOT_FILEIDX_F_NOT_FLUSHED 0x00010000
44 #define GOT_FILEIDX_F_NO_BLOB 0x00020000
45 #define GOT_FILEIDX_F_NO_COMMIT 0x00040000
46 #define GOT_FILEIDX_F_NO_FILE_ON_DISK 0x00080000
48 struct got_fileindex {
49 struct got_fileindex_tree entries;
50 int nentries;
51 #define GOT_FILEIDX_MAX_ENTRIES INT_MAX
52 };
54 const struct got_error *
55 got_fileindex_entry_update(struct got_fileindex_entry *entry,
56 const char *ondisk_path, uint8_t *blob_sha1, uint8_t *commit_sha1,
57 int update_timestamps)
58 {
59 struct stat sb;
61 if (lstat(ondisk_path, &sb) != 0) {
62 if ((entry->flags & GOT_FILEIDX_F_NO_FILE_ON_DISK) == 0)
63 return got_error_from_errno2("lstat", ondisk_path);
64 } else {
65 if (sb.st_mode & S_IFDIR)
66 return got_error_set_errno(EISDIR, ondisk_path);
67 entry->flags &= ~GOT_FILEIDX_F_NO_FILE_ON_DISK;
68 }
71 if ((entry->flags & GOT_FILEIDX_F_NO_FILE_ON_DISK) == 0) {
72 if (update_timestamps) {
73 entry->ctime_sec = sb.st_ctime;
74 entry->ctime_nsec = sb.st_ctimensec;
75 entry->mtime_sec = sb.st_mtime;
76 entry->mtime_nsec = sb.st_mtimensec;
77 }
78 entry->uid = sb.st_uid;
79 entry->gid = sb.st_gid;
80 entry->size = (sb.st_size & 0xffffffff);
81 if (sb.st_mode & S_IFLNK)
82 entry->mode = GOT_FILEIDX_MODE_SYMLINK;
83 else
84 entry->mode = GOT_FILEIDX_MODE_REGULAR_FILE;
85 entry->mode |= ((sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) <<
86 GOT_FILEIDX_MODE_PERMS_SHIFT);
87 }
89 if (blob_sha1) {
90 memcpy(entry->blob_sha1, blob_sha1, SHA1_DIGEST_LENGTH);
91 entry->flags &= ~GOT_FILEIDX_F_NO_BLOB;
92 } else
93 entry->flags |= GOT_FILEIDX_F_NO_BLOB;
95 if (commit_sha1) {
96 memcpy(entry->commit_sha1, commit_sha1, SHA1_DIGEST_LENGTH);
97 entry->flags &= ~GOT_FILEIDX_F_NO_COMMIT;
98 } else
99 entry->flags |= GOT_FILEIDX_F_NO_COMMIT;
101 return NULL;
104 void
105 got_fileindex_entry_mark_deleted_from_disk(struct got_fileindex_entry *entry)
107 entry->flags |= GOT_FILEIDX_F_NO_FILE_ON_DISK;
110 const struct got_error *
111 got_fileindex_entry_alloc(struct got_fileindex_entry **entry,
112 const char *ondisk_path, const char *relpath, uint8_t *blob_sha1,
113 uint8_t *commit_sha1)
115 size_t len;
117 *entry = calloc(1, sizeof(**entry));
118 if (*entry == NULL)
119 return got_error_from_errno("calloc");
121 (*entry)->path = strdup(relpath);
122 if ((*entry)->path == NULL) {
123 const struct got_error *err = got_error_from_errno("strdup");
124 free(*entry);
125 *entry = NULL;
126 return err;
129 len = strlen(relpath);
130 if (len > GOT_FILEIDX_F_PATH_LEN)
131 len = GOT_FILEIDX_F_PATH_LEN;
132 (*entry)->flags |= len;
134 return got_fileindex_entry_update(*entry, ondisk_path, blob_sha1,
135 commit_sha1, 1);
138 void
139 got_fileindex_entry_free(struct got_fileindex_entry *entry)
141 free(entry->path);
142 free(entry);
145 int
146 got_fileindex_entry_has_blob(struct got_fileindex_entry *ie)
148 return (ie->flags & GOT_FILEIDX_F_NO_BLOB) == 0;
151 int
152 got_fileindex_entry_has_commit(struct got_fileindex_entry *ie)
154 return (ie->flags & GOT_FILEIDX_F_NO_COMMIT) == 0;
157 int
158 got_fileindex_entry_has_file_on_disk(struct got_fileindex_entry *ie)
160 return (ie->flags & GOT_FILEIDX_F_NO_FILE_ON_DISK) == 0;
163 static const struct got_error *
164 add_entry(struct got_fileindex *fileindex, struct got_fileindex_entry *entry)
166 if (fileindex->nentries >= GOT_FILEIDX_MAX_ENTRIES)
167 return got_error(GOT_ERR_NO_SPACE);
169 RB_INSERT(got_fileindex_tree, &fileindex->entries, entry);
170 fileindex->nentries++;
171 return NULL;
174 const struct got_error *
175 got_fileindex_entry_add(struct got_fileindex *fileindex,
176 struct got_fileindex_entry *entry)
178 /* Flag this entry until it gets written out to disk. */
179 entry->flags |= GOT_FILEIDX_F_NOT_FLUSHED;
181 return add_entry(fileindex, entry);
184 void
185 got_fileindex_entry_remove(struct got_fileindex *fileindex,
186 struct got_fileindex_entry *entry)
188 RB_REMOVE(got_fileindex_tree, &fileindex->entries, entry);
189 fileindex->nentries--;
192 struct got_fileindex_entry *
193 got_fileindex_entry_get(struct got_fileindex *fileindex, const char *path)
195 struct got_fileindex_entry key;
196 memset(&key, 0, sizeof(key));
197 key.path = (char *)path;
198 return RB_FIND(got_fileindex_tree, &fileindex->entries, &key);
201 const struct got_error *
202 got_fileindex_for_each_entry_safe(struct got_fileindex *fileindex,
203 got_fileindex_cb cb, void *cb_arg)
205 const struct got_error *err;
206 struct got_fileindex_entry *entry, *tmp;
208 RB_FOREACH_SAFE(entry, got_fileindex_tree, &fileindex->entries, tmp) {
209 err = (*cb)(cb_arg, entry);
210 if (err)
211 return err;
213 return NULL;
216 struct got_fileindex *
217 got_fileindex_alloc(void)
219 struct got_fileindex *fileindex;
221 fileindex = calloc(1, sizeof(*fileindex));
222 if (fileindex == NULL)
223 return NULL;
225 RB_INIT(&fileindex->entries);
226 return fileindex;
229 void
230 got_fileindex_free(struct got_fileindex *fileindex)
232 struct got_fileindex_entry *entry;
234 while ((entry = RB_MIN(got_fileindex_tree, &fileindex->entries))) {
235 RB_REMOVE(got_fileindex_tree, &fileindex->entries, entry);
236 got_fileindex_entry_free(entry);
238 free(fileindex);
241 static const struct got_error *
242 write_fileindex_val64(SHA1_CTX *ctx, uint64_t val, FILE *outfile)
244 size_t n;
246 val = htobe64(val);
247 SHA1Update(ctx, (uint8_t *)&val, sizeof(val));
248 n = fwrite(&val, 1, sizeof(val), outfile);
249 if (n != sizeof(val))
250 return got_ferror(outfile, GOT_ERR_IO);
251 return NULL;
254 static const struct got_error *
255 write_fileindex_val32(SHA1_CTX *ctx, uint32_t val, FILE *outfile)
257 size_t n;
259 val = htobe32(val);
260 SHA1Update(ctx, (uint8_t *)&val, sizeof(val));
261 n = fwrite(&val, 1, sizeof(val), outfile);
262 if (n != sizeof(val))
263 return got_ferror(outfile, GOT_ERR_IO);
264 return NULL;
267 static const struct got_error *
268 write_fileindex_val16(SHA1_CTX *ctx, uint16_t val, FILE *outfile)
270 size_t n;
272 val = htobe16(val);
273 SHA1Update(ctx, (uint8_t *)&val, sizeof(val));
274 n = fwrite(&val, 1, sizeof(val), outfile);
275 if (n != sizeof(val))
276 return got_ferror(outfile, GOT_ERR_IO);
277 return NULL;
280 static const struct got_error *
281 write_fileindex_path(SHA1_CTX *ctx, const char *path, FILE *outfile)
283 size_t n, len, pad = 0;
284 static const uint8_t zero[8] = { 0 };
286 len = strlen(path);
287 while ((len + pad) % 8 != 0)
288 pad++;
289 if (pad == 0)
290 pad = 8; /* NUL-terminate */
292 SHA1Update(ctx, path, len);
293 n = fwrite(path, 1, len, outfile);
294 if (n != len)
295 return got_ferror(outfile, GOT_ERR_IO);
296 SHA1Update(ctx, zero, pad);
297 n = fwrite(zero, 1, pad, outfile);
298 if (n != pad)
299 return got_ferror(outfile, GOT_ERR_IO);
300 return NULL;
303 static const struct got_error *
304 write_fileindex_entry(SHA1_CTX *ctx, struct got_fileindex_entry *entry,
305 FILE *outfile)
307 const struct got_error *err;
308 size_t n;
310 err = write_fileindex_val64(ctx, entry->ctime_sec, outfile);
311 if (err)
312 return err;
313 err = write_fileindex_val64(ctx, entry->ctime_nsec, outfile);
314 if (err)
315 return err;
316 err = write_fileindex_val64(ctx, entry->mtime_sec, outfile);
317 if (err)
318 return err;
319 err = write_fileindex_val64(ctx, entry->mtime_nsec, outfile);
320 if (err)
321 return err;
323 err = write_fileindex_val32(ctx, entry->uid, outfile);
324 if (err)
325 return err;
326 err = write_fileindex_val32(ctx, entry->gid, outfile);
327 if (err)
328 return err;
329 err = write_fileindex_val32(ctx, entry->size, outfile);
330 if (err)
331 return err;
333 err = write_fileindex_val16(ctx, entry->mode, outfile);
334 if (err)
335 return err;
337 SHA1Update(ctx, entry->blob_sha1, SHA1_DIGEST_LENGTH);
338 n = fwrite(entry->blob_sha1, 1, SHA1_DIGEST_LENGTH, outfile);
339 if (n != SHA1_DIGEST_LENGTH)
340 return got_ferror(outfile, GOT_ERR_IO);
342 SHA1Update(ctx, entry->commit_sha1, SHA1_DIGEST_LENGTH);
343 n = fwrite(entry->commit_sha1, 1, SHA1_DIGEST_LENGTH, outfile);
344 if (n != SHA1_DIGEST_LENGTH)
345 return got_ferror(outfile, GOT_ERR_IO);
347 err = write_fileindex_val32(ctx, entry->flags, outfile);
348 if (err)
349 return err;
351 err = write_fileindex_path(ctx, entry->path, outfile);
352 return err;
355 const struct got_error *
356 got_fileindex_write(struct got_fileindex *fileindex, FILE *outfile)
358 const struct got_error *err = NULL;
359 struct got_fileindex_hdr hdr;
360 SHA1_CTX ctx;
361 uint8_t sha1[SHA1_DIGEST_LENGTH];
362 size_t n;
363 struct got_fileindex_entry *entry;
365 SHA1Init(&ctx);
367 hdr.signature = htobe32(GOT_FILE_INDEX_SIGNATURE);
368 hdr.version = htobe32(GOT_FILE_INDEX_VERSION);
369 hdr.nentries = htobe32(fileindex->nentries);
371 SHA1Update(&ctx, (uint8_t *)&hdr.signature, sizeof(hdr.signature));
372 SHA1Update(&ctx, (uint8_t *)&hdr.version, sizeof(hdr.version));
373 SHA1Update(&ctx, (uint8_t *)&hdr.nentries, sizeof(hdr.nentries));
374 n = fwrite(&hdr.signature, 1, sizeof(hdr.signature), outfile);
375 if (n != sizeof(hdr.signature))
376 return got_ferror(outfile, GOT_ERR_IO);
377 n = fwrite(&hdr.version, 1, sizeof(hdr.version), outfile);
378 if (n != sizeof(hdr.version))
379 return got_ferror(outfile, GOT_ERR_IO);
380 n = fwrite(&hdr.nentries, 1, sizeof(hdr.nentries), outfile);
381 if (n != sizeof(hdr.nentries))
382 return got_ferror(outfile, GOT_ERR_IO);
384 RB_FOREACH(entry, got_fileindex_tree, &fileindex->entries) {
385 entry->flags &= ~GOT_FILEIDX_F_NOT_FLUSHED;
386 err = write_fileindex_entry(&ctx, entry, outfile);
387 if (err)
388 return err;
391 SHA1Final(sha1, &ctx);
392 n = fwrite(sha1, 1, sizeof(sha1), outfile);
393 if (n != sizeof(sha1))
394 return got_ferror(outfile, GOT_ERR_IO);
396 if (fflush(outfile) != 0)
397 return got_error_from_errno("fflush");
399 return NULL;
402 static const struct got_error *
403 read_fileindex_val64(uint64_t *val, SHA1_CTX *ctx, FILE *infile)
405 size_t n;
407 n = fread(val, 1, sizeof(*val), infile);
408 if (n != sizeof(*val))
409 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
410 SHA1Update(ctx, (uint8_t *)val, sizeof(*val));
411 *val = be64toh(*val);
412 return NULL;
415 static const struct got_error *
416 read_fileindex_val32(uint32_t *val, SHA1_CTX *ctx, FILE *infile)
418 size_t n;
420 n = fread(val, 1, sizeof(*val), infile);
421 if (n != sizeof(*val))
422 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
423 SHA1Update(ctx, (uint8_t *)val, sizeof(*val));
424 *val = be32toh(*val);
425 return NULL;
428 static const struct got_error *
429 read_fileindex_val16(uint16_t *val, SHA1_CTX *ctx, FILE *infile)
431 size_t n;
433 n = fread(val, 1, sizeof(*val), infile);
434 if (n != sizeof(*val))
435 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
436 SHA1Update(ctx, (uint8_t *)val, sizeof(*val));
437 *val = be16toh(*val);
438 return NULL;
441 static const struct got_error *
442 read_fileindex_path(char **path, SHA1_CTX *ctx, FILE *infile)
444 const struct got_error *err = NULL;
445 uint8_t buf[8];
446 size_t n, len = 0, totlen = sizeof(buf);
448 *path = malloc(totlen);
449 if (*path == NULL)
450 return got_error_from_errno("malloc");
452 do {
453 n = fread(buf, 1, sizeof(buf), infile);
454 if (n != sizeof(buf))
455 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
456 if (len + sizeof(buf) > totlen) {
457 char *p = reallocarray(*path, totlen + sizeof(buf), 1);
458 if (p == NULL) {
459 err = got_error_from_errno("reallocarray");
460 break;
462 totlen += sizeof(buf);
463 *path = p;
465 SHA1Update(ctx, buf, sizeof(buf));
466 memcpy(*path + len, buf, sizeof(buf));
467 len += sizeof(buf);
468 } while (memchr(buf, '\0', sizeof(buf)) == NULL);
470 if (err) {
471 free(*path);
472 *path = NULL;
474 return err;
477 static const struct got_error *
478 read_fileindex_entry(struct got_fileindex_entry **entryp, SHA1_CTX *ctx,
479 FILE *infile)
481 const struct got_error *err;
482 struct got_fileindex_entry *entry;
483 size_t n;
485 *entryp = NULL;
487 entry = calloc(1, sizeof(*entry));
488 if (entry == NULL)
489 return got_error_from_errno("calloc");
491 err = read_fileindex_val64(&entry->ctime_sec, ctx, infile);
492 if (err)
493 goto done;
494 err = read_fileindex_val64(&entry->ctime_nsec, ctx, infile);
495 if (err)
496 goto done;
497 err = read_fileindex_val64(&entry->mtime_sec, ctx, infile);
498 if (err)
499 goto done;
500 err = read_fileindex_val64(&entry->mtime_nsec, ctx, infile);
501 if (err)
502 goto done;
504 err = read_fileindex_val32(&entry->uid, ctx, infile);
505 if (err)
506 goto done;
507 err = read_fileindex_val32(&entry->gid, ctx, infile);
508 if (err)
509 goto done;
510 err = read_fileindex_val32(&entry->size, ctx, infile);
511 if (err)
512 goto done;
514 err = read_fileindex_val16(&entry->mode, ctx, infile);
515 if (err)
516 goto done;
518 n = fread(entry->blob_sha1, 1, SHA1_DIGEST_LENGTH, infile);
519 if (n != SHA1_DIGEST_LENGTH) {
520 err = got_ferror(infile, GOT_ERR_FILEIDX_BAD);
521 goto done;
523 SHA1Update(ctx, entry->blob_sha1, SHA1_DIGEST_LENGTH);
525 n = fread(entry->commit_sha1, 1, SHA1_DIGEST_LENGTH, infile);
526 if (n != SHA1_DIGEST_LENGTH) {
527 err = got_ferror(infile, GOT_ERR_FILEIDX_BAD);
528 goto done;
530 SHA1Update(ctx, entry->commit_sha1, SHA1_DIGEST_LENGTH);
532 err = read_fileindex_val32(&entry->flags, ctx, infile);
533 if (err)
534 goto done;
536 err = read_fileindex_path(&entry->path, ctx, infile);
537 done:
538 if (err)
539 free(entry);
540 else
541 *entryp = entry;
542 return err;
545 const struct got_error *
546 got_fileindex_read(struct got_fileindex *fileindex, FILE *infile)
548 const struct got_error *err = NULL;
549 struct got_fileindex_hdr hdr;
550 SHA1_CTX ctx;
551 struct got_fileindex_entry *entry;
552 uint8_t sha1_expected[SHA1_DIGEST_LENGTH];
553 uint8_t sha1[SHA1_DIGEST_LENGTH];
554 size_t n;
555 int i;
557 SHA1Init(&ctx);
559 n = fread(&hdr.signature, 1, sizeof(hdr.signature), infile);
560 if (n != sizeof(hdr.signature)) {
561 if (n == 0) /* EOF */
562 return NULL;
563 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
565 n = fread(&hdr.version, 1, sizeof(hdr.version), infile);
566 if (n != sizeof(hdr.version)) {
567 if (n == 0) /* EOF */
568 return NULL;
569 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
571 n = fread(&hdr.nentries, 1, sizeof(hdr.nentries), infile);
572 if (n != sizeof(hdr.nentries)) {
573 if (n == 0) /* EOF */
574 return NULL;
575 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
578 SHA1Update(&ctx, (uint8_t *)&hdr.signature, sizeof(hdr.signature));
579 SHA1Update(&ctx, (uint8_t *)&hdr.version, sizeof(hdr.version));
580 SHA1Update(&ctx, (uint8_t *)&hdr.nentries, sizeof(hdr.nentries));
582 hdr.signature = be32toh(hdr.signature);
583 hdr.version = be32toh(hdr.version);
584 hdr.nentries = be32toh(hdr.nentries);
586 if (hdr.signature != GOT_FILE_INDEX_SIGNATURE)
587 return got_error(GOT_ERR_FILEIDX_SIG);
588 if (hdr.version != GOT_FILE_INDEX_VERSION)
589 return got_error(GOT_ERR_FILEIDX_VER);
591 for (i = 0; i < hdr.nentries; i++) {
592 err = read_fileindex_entry(&entry, &ctx, infile);
593 if (err)
594 return err;
595 err = add_entry(fileindex, entry);
596 if (err)
597 return err;
600 n = fread(sha1_expected, 1, sizeof(sha1_expected), infile);
601 if (n != sizeof(sha1_expected))
602 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
603 SHA1Final(sha1, &ctx);
604 if (memcmp(sha1, sha1_expected, SHA1_DIGEST_LENGTH) != 0)
605 return got_error(GOT_ERR_FILEIDX_CSUM);
607 return NULL;
610 static struct got_fileindex_entry *
611 walk_fileindex(struct got_fileindex *fileindex, struct got_fileindex_entry *ie)
613 struct got_fileindex_entry *next;
615 next = RB_NEXT(got_fileindex_tree, &fileindex->entries, ie);
617 /* Skip entries which were newly added by diff callbacks. */
618 while (next && (next->flags & GOT_FILEIDX_F_NOT_FLUSHED))
619 next = RB_NEXT(got_fileindex_tree, &fileindex->entries, next);
621 return next;
624 static const struct got_error *
625 diff_fileindex_tree(struct got_fileindex *, struct got_fileindex_entry **,
626 struct got_tree_object *, const char *, const char *,
627 struct got_repository *, struct got_fileindex_diff_tree_cb *, void *);
629 static const struct got_error *
630 walk_tree(struct got_tree_entry **next, struct got_fileindex *fileindex,
631 struct got_fileindex_entry **ie, struct got_tree_entry *te,
632 const char *path, const char *entry_name, struct got_repository *repo,
633 struct got_fileindex_diff_tree_cb *cb, void *cb_arg)
635 const struct got_error *err = NULL;
637 if (S_ISDIR(te->mode)) {
638 char *subpath;
639 struct got_tree_object *subtree;
641 if (asprintf(&subpath, "%s%s%s", path,
642 path[0] == '\0' ? "" : "/", te->name) == -1)
643 return got_error_from_errno("asprintf");
645 err = got_object_open_as_tree(&subtree, repo, te->id);
646 if (err) {
647 free(subpath);
648 return err;
651 err = diff_fileindex_tree(fileindex, ie, subtree,
652 subpath, entry_name, repo, cb, cb_arg);
653 free(subpath);
654 got_object_tree_close(subtree);
655 if (err)
656 return err;
659 *next = SIMPLEQ_NEXT(te, entry);
660 return NULL;
663 static const struct got_error *
664 diff_fileindex_tree(struct got_fileindex *fileindex,
665 struct got_fileindex_entry **ie, struct got_tree_object *tree,
666 const char *path, const char *entry_name, struct got_repository *repo,
667 struct got_fileindex_diff_tree_cb *cb, void *cb_arg)
669 const struct got_error *err = NULL;
670 struct got_tree_entry *te = NULL;
671 size_t path_len = strlen(path);
672 const struct got_tree_entries *entries;
673 struct got_fileindex_entry *next;
675 entries = got_object_tree_get_entries(tree);
676 te = SIMPLEQ_FIRST(&entries->head);
677 while ((*ie && got_path_is_child((*ie)->path, path, path_len)) || te) {
678 if (te && *ie) {
679 char *te_path;
680 int cmp;
681 if (asprintf(&te_path, "%s/%s", path, te->name) == -1) {
682 err = got_error_from_errno("asprintf");
683 break;
685 cmp = got_path_cmp((*ie)->path, te_path);
686 free(te_path);
687 if (cmp == 0) {
688 if (got_path_is_child((*ie)->path, path,
689 path_len) && (entry_name == NULL ||
690 strcmp(te->name, entry_name) == 0)) {
691 err = cb->diff_old_new(cb_arg, *ie, te,
692 path);
693 if (err || entry_name)
694 break;
696 *ie = walk_fileindex(fileindex, *ie);
697 err = walk_tree(&te, fileindex, ie, te,
698 path, entry_name, repo, cb, cb_arg);
699 } else if (cmp < 0) {
700 next = walk_fileindex(fileindex, *ie);
701 if (got_path_is_child((*ie)->path, path,
702 path_len) && (entry_name == NULL ||
703 strcmp(te->name, entry_name) == 0)) {
704 err = cb->diff_old(cb_arg, *ie, path);
705 if (err || entry_name)
706 break;
708 *ie = next;
709 } else {
710 if ((entry_name == NULL ||
711 strcmp(te->name, entry_name) == 0)) {
712 err = cb->diff_new(cb_arg, te, path);
713 if (err || entry_name)
714 break;
716 err = walk_tree(&te, fileindex, ie, te,
717 path, entry_name, repo, cb, cb_arg);
719 if (err)
720 break;
721 } else if (*ie) {
722 next = walk_fileindex(fileindex, *ie);
723 if (got_path_is_child((*ie)->path, path, path_len) &&
724 (entry_name == NULL ||
725 strcmp(te->name, entry_name) == 0)) {
726 err = cb->diff_old(cb_arg, *ie, path);
727 if (err || entry_name)
728 break;
730 *ie = next;
731 } else if (te) {
732 if (entry_name == NULL ||
733 strcmp(te->name, entry_name) == 0) {
734 err = cb->diff_new(cb_arg, te, path);
735 if (err || entry_name)
736 break;
738 err = walk_tree(&te, fileindex, ie, te, path,
739 entry_name, repo, cb, cb_arg);
740 if (err)
741 break;
745 return err;
748 const struct got_error *
749 got_fileindex_diff_tree(struct got_fileindex *fileindex,
750 struct got_tree_object *tree, const char *path, const char *entry_name,
751 struct got_repository *repo,
752 struct got_fileindex_diff_tree_cb *cb, void *cb_arg)
754 struct got_fileindex_entry *min;
755 min = RB_MIN(got_fileindex_tree, &fileindex->entries);
756 return diff_fileindex_tree(fileindex, &min, tree, path, entry_name,
757 repo, cb, cb_arg);
760 static const struct got_error *
761 diff_fileindex_dir(struct got_fileindex *, struct got_fileindex_entry **, DIR *,
762 const char *, const char *, struct got_repository *,
763 struct got_fileindex_diff_dir_cb *, void *);
765 static const struct got_error *
766 walk_dir(struct got_pathlist_entry **next, struct got_fileindex *fileindex,
767 struct got_fileindex_entry **ie, struct got_pathlist_entry *dle,
768 const char *path, DIR *dir, const char *rootpath,
769 struct got_repository *repo, struct got_fileindex_diff_dir_cb *cb,
770 void *cb_arg)
772 const struct got_error *err = NULL;
773 struct dirent *de = dle->data;
775 *next = NULL;
777 if (de->d_type == DT_DIR) {
778 char *subpath;
779 char *subdirpath;
780 DIR *subdir;
782 if (asprintf(&subpath, "%s%s%s", path,
783 path[0] == '\0' ? "" : "/", de->d_name) == -1)
784 return got_error_from_errno("asprintf");
786 if (asprintf(&subdirpath, "%s/%s", rootpath, subpath) == -1) {
787 free(subpath);
788 return got_error_from_errno("asprintf");
791 subdir = opendir(subdirpath);
792 if (subdir == NULL) {
793 free(subpath);
794 free(subdirpath);
795 return got_error_from_errno2("opendir", subdirpath);
798 err = diff_fileindex_dir(fileindex, ie, subdir, rootpath,
799 subpath, repo, cb, cb_arg);
800 free(subpath);
801 free(subdirpath);
802 closedir(subdir);
803 if (err)
804 return err;
807 *next = TAILQ_NEXT(dle, entry);
808 return NULL;
811 static const struct got_error *
812 diff_fileindex_dir(struct got_fileindex *fileindex,
813 struct got_fileindex_entry **ie, DIR *dir, const char *rootpath,
814 const char *path, struct got_repository *repo,
815 struct got_fileindex_diff_dir_cb *cb, void *cb_arg)
817 const struct got_error *err = NULL;
818 struct dirent *de = NULL;
819 size_t path_len = strlen(path);
820 struct got_fileindex_entry *next;
821 struct got_pathlist_head dirlist;
822 struct got_pathlist_entry *dle;
824 TAILQ_INIT(&dirlist);
826 for (;;) {
827 struct got_pathlist_entry *new = NULL;
828 struct dirent *dep = NULL;
830 de = malloc(sizeof(struct dirent) + NAME_MAX + 1);
831 if (de == NULL) {
832 err = got_error_from_errno("malloc");
833 goto done;
836 if (readdir_r(dir, de, &dep) != 0) {
837 err = got_error_from_errno("readdir_r");
838 free(de);
839 goto done;
841 if (dep == NULL) {
842 free(de);
843 break;
846 if (strcmp(de->d_name, ".") == 0 ||
847 strcmp(de->d_name, "..") == 0 ||
848 (path[0] == '\0' &&
849 strcmp(de->d_name, GOT_WORKTREE_GOT_DIR) == 0)) {
850 free(de);
851 continue;
854 err = got_pathlist_insert(&new, &dirlist, de->d_name, de);
855 if (err) {
856 free(de);
857 goto done;
859 if (new == NULL) {
860 err = got_error(GOT_ERR_DIR_DUP_ENTRY);
861 free(de);
862 goto done;
866 dle = TAILQ_FIRST(&dirlist);
867 while ((*ie && got_path_is_child((*ie)->path, path, path_len)) || dle) {
868 if (dle && *ie) {
869 char *de_path;
870 int cmp;
871 de = dle->data;
872 if (asprintf(&de_path, "%s/%s", path,
873 de->d_name) == -1) {
874 err = got_error_from_errno("asprintf");
875 break;
877 cmp = got_path_cmp((*ie)->path, de_path);
878 free(de_path);
879 if (cmp == 0) {
880 err = cb->diff_old_new(cb_arg, *ie, de, path);
881 if (err)
882 break;
883 *ie = walk_fileindex(fileindex, *ie);
884 err = walk_dir(&dle, fileindex, ie, dle, path,
885 dir, rootpath, repo, cb, cb_arg);
886 } else if (cmp < 0 ) {
887 next = walk_fileindex(fileindex, *ie);
888 err = cb->diff_old(cb_arg, *ie, path);
889 if (err)
890 break;
891 *ie = next;
892 } else {
893 err = cb->diff_new(cb_arg, de, path);
894 if (err)
895 break;
896 err = walk_dir(&dle, fileindex, ie, dle, path,
897 dir, rootpath, repo, cb, cb_arg);
899 if (err)
900 break;
901 } else if (*ie) {
902 next = walk_fileindex(fileindex, *ie);
903 err = cb->diff_old(cb_arg, *ie, path);
904 if (err)
905 break;
906 *ie = next;
907 } else if (dle) {
908 de = dle->data;
909 err = cb->diff_new(cb_arg, de, path);
910 if (err)
911 break;
912 err = walk_dir(&dle, fileindex, ie, dle, path, dir,
913 rootpath, repo, cb, cb_arg);
914 if (err)
915 break;
918 done:
919 TAILQ_FOREACH(dle, &dirlist, entry)
920 free(dle->data);
921 got_pathlist_free(&dirlist);
922 return err;
925 const struct got_error *
926 got_fileindex_diff_dir(struct got_fileindex *fileindex, DIR *rootdir,
927 const char *rootpath, const char *path, struct got_repository *repo,
928 struct got_fileindex_diff_dir_cb *cb, void *cb_arg)
930 struct got_fileindex_entry *min;
931 min = RB_MIN(got_fileindex_tree, &fileindex->entries);
932 return diff_fileindex_dir(fileindex, &min, rootdir, rootpath, path,
933 repo, cb, cb_arg);
936 RB_GENERATE(got_fileindex_tree, got_fileindex_entry, entry, got_fileindex_cmp);