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 0x0000f000
41 #define GOT_FILEIDX_F_STAGE_SHIFT 24
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;
49 int nentries;
50 #define GOT_FILEIDX_MAX_ENTRIES INT_MAX
51 };
53 uint16_t
54 got_fileindex_perms_from_st(struct stat *sb)
55 {
56 uint16_t perms = (sb->st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
57 return (perms << GOT_FILEIDX_MODE_PERMS_SHIFT);
58 }
60 mode_t
61 got_fileindex_perms_to_st(struct got_fileindex_entry *ie)
62 {
63 mode_t perms = (ie->mode >> GOT_FILEIDX_MODE_PERMS_SHIFT);
64 return (perms & (S_IRWXU | S_IRWXG | S_IRWXO));
65 }
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)
71 {
72 struct stat sb;
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);
77 } else {
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;
81 }
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;
90 }
91 ie->uid = sb.st_uid;
92 ie->gid = sb.st_gid;
93 ie->size = (sb.st_size & 0xffffffff);
94 if (sb.st_mode & S_IFLNK)
95 ie->mode = GOT_FILEIDX_MODE_SYMLINK;
96 else
97 ie->mode = GOT_FILEIDX_MODE_REGULAR_FILE;
98 ie->mode |= got_fileindex_perms_from_st(&sb);
99 }
101 if (blob_sha1) {
102 memcpy(ie->blob_sha1, blob_sha1, SHA1_DIGEST_LENGTH);
103 ie->flags &= ~GOT_FILEIDX_F_NO_BLOB;
104 } else
105 ie->flags |= GOT_FILEIDX_F_NO_BLOB;
107 if (commit_sha1) {
108 memcpy(ie->commit_sha1, commit_sha1, SHA1_DIGEST_LENGTH);
109 ie->flags &= ~GOT_FILEIDX_F_NO_COMMIT;
110 } else
111 ie->flags |= GOT_FILEIDX_F_NO_COMMIT;
113 return NULL;
116 void
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)
127 size_t len;
129 *ie = calloc(1, sizeof(**ie));
130 if (*ie == NULL)
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");
136 free(*ie);
137 *ie = NULL;
138 return err;
141 len = strlen(relpath);
142 if (len > GOT_FILEIDX_F_PATH_LEN)
143 len = GOT_FILEIDX_F_PATH_LEN;
144 (*ie)->flags |= len;
146 return got_fileindex_entry_update(*ie, ondisk_path, blob_sha1,
147 commit_sha1, 1);
150 void
151 got_fileindex_entry_free(struct got_fileindex_entry *ie)
153 free(ie->path);
154 free(ie);
157 size_t
158 got_fileindex_entry_path_len(const struct got_fileindex_entry *ie)
160 return (size_t)(ie->flags & GOT_FILEIDX_F_PATH_LEN);
163 uint32_t
164 got_fileindex_entry_stage(const struct got_fileindex_entry *ie)
166 return ((ie->flags & GOT_FILEIDX_F_STAGE) >> GOT_FILEIDX_F_STAGE_SHIFT);
169 int
170 got_fileindex_entry_has_blob(struct got_fileindex_entry *ie)
172 return (ie->flags & GOT_FILEIDX_F_NO_BLOB) == 0;
175 int
176 got_fileindex_entry_has_commit(struct got_fileindex_entry *ie)
178 return (ie->flags & GOT_FILEIDX_F_NO_COMMIT) == 0;
181 int
182 got_fileindex_entry_has_file_on_disk(struct got_fileindex_entry *ie)
184 return (ie->flags & GOT_FILEIDX_F_NO_FILE_ON_DISK) == 0;
187 static const struct got_error *
188 add_entry(struct got_fileindex *fileindex, struct got_fileindex_entry *ie)
190 if (fileindex->nentries >= GOT_FILEIDX_MAX_ENTRIES)
191 return got_error(GOT_ERR_NO_SPACE);
193 RB_INSERT(got_fileindex_tree, &fileindex->entries, ie);
194 fileindex->nentries++;
195 return NULL;
198 const struct got_error *
199 got_fileindex_entry_add(struct got_fileindex *fileindex,
200 struct got_fileindex_entry *ie)
202 /* Flag this entry until it gets written out to disk. */
203 ie->flags |= GOT_FILEIDX_F_NOT_FLUSHED;
205 return add_entry(fileindex, ie);
208 void
209 got_fileindex_entry_remove(struct got_fileindex *fileindex,
210 struct got_fileindex_entry *ie)
212 RB_REMOVE(got_fileindex_tree, &fileindex->entries, ie);
213 fileindex->nentries--;
216 struct got_fileindex_entry *
217 got_fileindex_entry_get(struct got_fileindex *fileindex, const char *path,
218 size_t path_len)
220 struct got_fileindex_entry key;
221 memset(&key, 0, sizeof(key));
222 key.path = (char *)path;
223 key.flags = (path_len & GOT_FILEIDX_F_PATH_LEN);
224 return RB_FIND(got_fileindex_tree, &fileindex->entries, &key);
227 const struct got_error *
228 got_fileindex_for_each_entry_safe(struct got_fileindex *fileindex,
229 got_fileindex_cb cb, void *cb_arg)
231 const struct got_error *err;
232 struct got_fileindex_entry *ie, *tmp;
234 RB_FOREACH_SAFE(ie, got_fileindex_tree, &fileindex->entries, tmp) {
235 err = (*cb)(cb_arg, ie);
236 if (err)
237 return err;
239 return NULL;
242 struct got_fileindex *
243 got_fileindex_alloc(void)
245 struct got_fileindex *fileindex;
247 fileindex = calloc(1, sizeof(*fileindex));
248 if (fileindex == NULL)
249 return NULL;
251 RB_INIT(&fileindex->entries);
252 return fileindex;
255 void
256 got_fileindex_free(struct got_fileindex *fileindex)
258 struct got_fileindex_entry *ie;
260 while ((ie = RB_MIN(got_fileindex_tree, &fileindex->entries))) {
261 RB_REMOVE(got_fileindex_tree, &fileindex->entries, ie);
262 got_fileindex_entry_free(ie);
264 free(fileindex);
267 static const struct got_error *
268 write_fileindex_val64(SHA1_CTX *ctx, uint64_t val, FILE *outfile)
270 size_t n;
272 val = htobe64(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_val32(SHA1_CTX *ctx, uint32_t val, FILE *outfile)
283 size_t n;
285 val = htobe32(val);
286 SHA1Update(ctx, (uint8_t *)&val, sizeof(val));
287 n = fwrite(&val, 1, sizeof(val), outfile);
288 if (n != sizeof(val))
289 return got_ferror(outfile, GOT_ERR_IO);
290 return NULL;
293 static const struct got_error *
294 write_fileindex_val16(SHA1_CTX *ctx, uint16_t val, FILE *outfile)
296 size_t n;
298 val = htobe16(val);
299 SHA1Update(ctx, (uint8_t *)&val, sizeof(val));
300 n = fwrite(&val, 1, sizeof(val), outfile);
301 if (n != sizeof(val))
302 return got_ferror(outfile, GOT_ERR_IO);
303 return NULL;
306 static const struct got_error *
307 write_fileindex_path(SHA1_CTX *ctx, const char *path, FILE *outfile)
309 size_t n, len, pad = 0;
310 static const uint8_t zero[8] = { 0 };
312 len = strlen(path);
313 while ((len + pad) % 8 != 0)
314 pad++;
315 if (pad == 0)
316 pad = 8; /* NUL-terminate */
318 SHA1Update(ctx, path, len);
319 n = fwrite(path, 1, len, outfile);
320 if (n != len)
321 return got_ferror(outfile, GOT_ERR_IO);
322 SHA1Update(ctx, zero, pad);
323 n = fwrite(zero, 1, pad, outfile);
324 if (n != pad)
325 return got_ferror(outfile, GOT_ERR_IO);
326 return NULL;
329 static const struct got_error *
330 write_fileindex_entry(SHA1_CTX *ctx, struct got_fileindex_entry *ie,
331 FILE *outfile)
333 const struct got_error *err;
334 size_t n;
335 uint32_t stage;
337 err = write_fileindex_val64(ctx, ie->ctime_sec, outfile);
338 if (err)
339 return err;
340 err = write_fileindex_val64(ctx, ie->ctime_nsec, outfile);
341 if (err)
342 return err;
343 err = write_fileindex_val64(ctx, ie->mtime_sec, outfile);
344 if (err)
345 return err;
346 err = write_fileindex_val64(ctx, ie->mtime_nsec, outfile);
347 if (err)
348 return err;
350 err = write_fileindex_val32(ctx, ie->uid, outfile);
351 if (err)
352 return err;
353 err = write_fileindex_val32(ctx, ie->gid, outfile);
354 if (err)
355 return err;
356 err = write_fileindex_val32(ctx, ie->size, outfile);
357 if (err)
358 return err;
360 err = write_fileindex_val16(ctx, ie->mode, outfile);
361 if (err)
362 return err;
364 SHA1Update(ctx, ie->blob_sha1, SHA1_DIGEST_LENGTH);
365 n = fwrite(ie->blob_sha1, 1, SHA1_DIGEST_LENGTH, outfile);
366 if (n != SHA1_DIGEST_LENGTH)
367 return got_ferror(outfile, GOT_ERR_IO);
369 SHA1Update(ctx, ie->commit_sha1, SHA1_DIGEST_LENGTH);
370 n = fwrite(ie->commit_sha1, 1, SHA1_DIGEST_LENGTH, outfile);
371 if (n != SHA1_DIGEST_LENGTH)
372 return got_ferror(outfile, GOT_ERR_IO);
374 err = write_fileindex_val32(ctx, ie->flags, outfile);
375 if (err)
376 return err;
378 err = write_fileindex_path(ctx, ie->path, outfile);
379 if (err)
380 return err;
382 stage = got_fileindex_entry_stage(ie);
383 if (stage == GOT_FILEIDX_STAGE_MODIFY ||
384 stage == GOT_FILEIDX_STAGE_ADD) {
385 SHA1Update(ctx, ie->staged_blob_sha1, SHA1_DIGEST_LENGTH);
386 n = fwrite(ie->staged_blob_sha1, 1, SHA1_DIGEST_LENGTH,
387 outfile);
388 if (n != SHA1_DIGEST_LENGTH)
389 return got_ferror(outfile, GOT_ERR_IO);
392 return NULL;
395 const struct got_error *
396 got_fileindex_write(struct got_fileindex *fileindex, FILE *outfile)
398 const struct got_error *err = NULL;
399 struct got_fileindex_hdr hdr;
400 SHA1_CTX ctx;
401 uint8_t sha1[SHA1_DIGEST_LENGTH];
402 size_t n;
403 struct got_fileindex_entry *ie;
405 SHA1Init(&ctx);
407 hdr.signature = htobe32(GOT_FILE_INDEX_SIGNATURE);
408 hdr.version = htobe32(GOT_FILE_INDEX_VERSION);
409 hdr.nentries = htobe32(fileindex->nentries);
411 SHA1Update(&ctx, (uint8_t *)&hdr.signature, sizeof(hdr.signature));
412 SHA1Update(&ctx, (uint8_t *)&hdr.version, sizeof(hdr.version));
413 SHA1Update(&ctx, (uint8_t *)&hdr.nentries, sizeof(hdr.nentries));
414 n = fwrite(&hdr.signature, 1, sizeof(hdr.signature), outfile);
415 if (n != sizeof(hdr.signature))
416 return got_ferror(outfile, GOT_ERR_IO);
417 n = fwrite(&hdr.version, 1, sizeof(hdr.version), outfile);
418 if (n != sizeof(hdr.version))
419 return got_ferror(outfile, GOT_ERR_IO);
420 n = fwrite(&hdr.nentries, 1, sizeof(hdr.nentries), outfile);
421 if (n != sizeof(hdr.nentries))
422 return got_ferror(outfile, GOT_ERR_IO);
424 RB_FOREACH(ie, got_fileindex_tree, &fileindex->entries) {
425 ie->flags &= ~GOT_FILEIDX_F_NOT_FLUSHED;
426 err = write_fileindex_entry(&ctx, ie, outfile);
427 if (err)
428 return err;
431 SHA1Final(sha1, &ctx);
432 n = fwrite(sha1, 1, sizeof(sha1), outfile);
433 if (n != sizeof(sha1))
434 return got_ferror(outfile, GOT_ERR_IO);
436 if (fflush(outfile) != 0)
437 return got_error_from_errno("fflush");
439 return NULL;
442 static const struct got_error *
443 read_fileindex_val64(uint64_t *val, SHA1_CTX *ctx, FILE *infile)
445 size_t n;
447 n = fread(val, 1, sizeof(*val), infile);
448 if (n != sizeof(*val))
449 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
450 SHA1Update(ctx, (uint8_t *)val, sizeof(*val));
451 *val = be64toh(*val);
452 return NULL;
455 static const struct got_error *
456 read_fileindex_val32(uint32_t *val, SHA1_CTX *ctx, FILE *infile)
458 size_t n;
460 n = fread(val, 1, sizeof(*val), infile);
461 if (n != sizeof(*val))
462 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
463 SHA1Update(ctx, (uint8_t *)val, sizeof(*val));
464 *val = be32toh(*val);
465 return NULL;
468 static const struct got_error *
469 read_fileindex_val16(uint16_t *val, SHA1_CTX *ctx, FILE *infile)
471 size_t n;
473 n = fread(val, 1, sizeof(*val), infile);
474 if (n != sizeof(*val))
475 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
476 SHA1Update(ctx, (uint8_t *)val, sizeof(*val));
477 *val = be16toh(*val);
478 return NULL;
481 static const struct got_error *
482 read_fileindex_path(char **path, SHA1_CTX *ctx, FILE *infile)
484 const struct got_error *err = NULL;
485 const size_t chunk_size = 8;
486 size_t n, len = 0, totlen = chunk_size;
488 *path = malloc(totlen);
489 if (*path == NULL)
490 return got_error_from_errno("malloc");
492 do {
493 if (len + chunk_size > totlen) {
494 char *p = reallocarray(*path, totlen + chunk_size, 1);
495 if (p == NULL) {
496 err = got_error_from_errno("reallocarray");
497 break;
499 totlen += chunk_size;
500 *path = p;
502 n = fread(*path + len, 1, chunk_size, infile);
503 if (n != chunk_size) {
504 err = got_ferror(infile, GOT_ERR_FILEIDX_BAD);
505 break;
507 SHA1Update(ctx, *path + len, chunk_size);
508 len += chunk_size;
509 } while (memchr(*path + len - chunk_size, '\0', chunk_size) == NULL);
511 if (err) {
512 free(*path);
513 *path = NULL;
515 return err;
518 static const struct got_error *
519 read_fileindex_entry(struct got_fileindex_entry **iep, SHA1_CTX *ctx,
520 FILE *infile, uint32_t version)
522 const struct got_error *err;
523 struct got_fileindex_entry *ie;
524 size_t n;
526 *iep = NULL;
528 ie = calloc(1, sizeof(*ie));
529 if (ie == NULL)
530 return got_error_from_errno("calloc");
532 err = read_fileindex_val64(&ie->ctime_sec, ctx, infile);
533 if (err)
534 goto done;
535 err = read_fileindex_val64(&ie->ctime_nsec, ctx, infile);
536 if (err)
537 goto done;
538 err = read_fileindex_val64(&ie->mtime_sec, ctx, infile);
539 if (err)
540 goto done;
541 err = read_fileindex_val64(&ie->mtime_nsec, ctx, infile);
542 if (err)
543 goto done;
545 err = read_fileindex_val32(&ie->uid, ctx, infile);
546 if (err)
547 goto done;
548 err = read_fileindex_val32(&ie->gid, ctx, infile);
549 if (err)
550 goto done;
551 err = read_fileindex_val32(&ie->size, ctx, infile);
552 if (err)
553 goto done;
555 err = read_fileindex_val16(&ie->mode, ctx, infile);
556 if (err)
557 goto done;
559 n = fread(ie->blob_sha1, 1, SHA1_DIGEST_LENGTH, infile);
560 if (n != SHA1_DIGEST_LENGTH) {
561 err = got_ferror(infile, GOT_ERR_FILEIDX_BAD);
562 goto done;
564 SHA1Update(ctx, ie->blob_sha1, SHA1_DIGEST_LENGTH);
566 n = fread(ie->commit_sha1, 1, SHA1_DIGEST_LENGTH, infile);
567 if (n != SHA1_DIGEST_LENGTH) {
568 err = got_ferror(infile, GOT_ERR_FILEIDX_BAD);
569 goto done;
571 SHA1Update(ctx, ie->commit_sha1, SHA1_DIGEST_LENGTH);
573 err = read_fileindex_val32(&ie->flags, ctx, infile);
574 if (err)
575 goto done;
577 err = read_fileindex_path(&ie->path, ctx, infile);
578 if (err)
579 goto done;
581 if (version >= 2) {
582 uint32_t stage = got_fileindex_entry_stage(ie);
583 if (stage == GOT_FILEIDX_STAGE_MODIFY ||
584 stage == GOT_FILEIDX_STAGE_ADD) {
585 n = fread(ie->staged_blob_sha1, 1, SHA1_DIGEST_LENGTH,
586 infile);
587 if (n != SHA1_DIGEST_LENGTH) {
588 err = got_ferror(infile, GOT_ERR_FILEIDX_BAD);
589 goto done;
591 SHA1Update(ctx, ie->staged_blob_sha1, SHA1_DIGEST_LENGTH);
593 } else {
594 /* GOT_FILE_INDEX_VERSION 1 does not support staging. */
595 ie->flags &= ~GOT_FILEIDX_F_STAGE;
598 done:
599 if (err)
600 got_fileindex_entry_free(ie);
601 else
602 *iep = ie;
603 return err;
606 const struct got_error *
607 got_fileindex_read(struct got_fileindex *fileindex, FILE *infile)
609 const struct got_error *err = NULL;
610 struct got_fileindex_hdr hdr;
611 SHA1_CTX ctx;
612 struct got_fileindex_entry *ie;
613 uint8_t sha1_expected[SHA1_DIGEST_LENGTH];
614 uint8_t sha1[SHA1_DIGEST_LENGTH];
615 size_t n;
616 int i;
618 SHA1Init(&ctx);
620 n = fread(&hdr.signature, 1, sizeof(hdr.signature), infile);
621 if (n != sizeof(hdr.signature)) {
622 if (n == 0) /* EOF */
623 return NULL;
624 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
626 n = fread(&hdr.version, 1, sizeof(hdr.version), infile);
627 if (n != sizeof(hdr.version)) {
628 if (n == 0) /* EOF */
629 return NULL;
630 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
632 n = fread(&hdr.nentries, 1, sizeof(hdr.nentries), infile);
633 if (n != sizeof(hdr.nentries)) {
634 if (n == 0) /* EOF */
635 return NULL;
636 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
639 SHA1Update(&ctx, (uint8_t *)&hdr.signature, sizeof(hdr.signature));
640 SHA1Update(&ctx, (uint8_t *)&hdr.version, sizeof(hdr.version));
641 SHA1Update(&ctx, (uint8_t *)&hdr.nentries, sizeof(hdr.nentries));
643 hdr.signature = be32toh(hdr.signature);
644 hdr.version = be32toh(hdr.version);
645 hdr.nentries = be32toh(hdr.nentries);
647 if (hdr.signature != GOT_FILE_INDEX_SIGNATURE)
648 return got_error(GOT_ERR_FILEIDX_SIG);
649 if (hdr.version > GOT_FILE_INDEX_VERSION)
650 return got_error(GOT_ERR_FILEIDX_VER);
652 for (i = 0; i < hdr.nentries; i++) {
653 err = read_fileindex_entry(&ie, &ctx, infile, hdr.version);
654 if (err)
655 return err;
656 err = add_entry(fileindex, ie);
657 if (err)
658 return err;
661 n = fread(sha1_expected, 1, sizeof(sha1_expected), infile);
662 if (n != sizeof(sha1_expected))
663 return got_ferror(infile, GOT_ERR_FILEIDX_BAD);
664 SHA1Final(sha1, &ctx);
665 if (memcmp(sha1, sha1_expected, SHA1_DIGEST_LENGTH) != 0)
666 return got_error(GOT_ERR_FILEIDX_CSUM);
668 return NULL;
671 static struct got_fileindex_entry *
672 walk_fileindex(struct got_fileindex *fileindex, struct got_fileindex_entry *ie)
674 struct got_fileindex_entry *next;
676 next = RB_NEXT(got_fileindex_tree, &fileindex->entries, ie);
678 /* Skip entries which were newly added by diff callbacks. */
679 while (next && (next->flags & GOT_FILEIDX_F_NOT_FLUSHED))
680 next = RB_NEXT(got_fileindex_tree, &fileindex->entries, next);
682 return next;
685 static const struct got_error *
686 diff_fileindex_tree(struct got_fileindex *, struct got_fileindex_entry **,
687 const struct got_tree_entries *, const char *, const char *,
688 struct got_repository *, struct got_fileindex_diff_tree_cb *, void *);
690 static const struct got_error *
691 walk_tree(struct got_tree_entry **next, struct got_fileindex *fileindex,
692 struct got_fileindex_entry **ie, struct got_tree_entry *te,
693 const char *path, const char *entry_name, struct got_repository *repo,
694 struct got_fileindex_diff_tree_cb *cb, void *cb_arg)
696 const struct got_error *err = NULL;
698 if (S_ISDIR(te->mode)) {
699 char *subpath;
700 struct got_tree_object *subtree;
702 if (asprintf(&subpath, "%s%s%s", path,
703 path[0] == '\0' ? "" : "/", te->name) == -1)
704 return got_error_from_errno("asprintf");
706 err = got_object_open_as_tree(&subtree, repo, te->id);
707 if (err) {
708 free(subpath);
709 return err;
712 err = diff_fileindex_tree(fileindex, ie,
713 got_object_tree_get_entries(subtree), subpath, entry_name,
714 repo, cb, cb_arg);
715 free(subpath);
716 got_object_tree_close(subtree);
717 if (err)
718 return err;
721 *next = SIMPLEQ_NEXT(te, entry);
722 return NULL;
725 static const struct got_error *
726 diff_fileindex_tree(struct got_fileindex *fileindex,
727 struct got_fileindex_entry **ie, const struct got_tree_entries *entries,
728 const char *path, const char *entry_name, struct got_repository *repo,
729 struct got_fileindex_diff_tree_cb *cb, void *cb_arg)
731 const struct got_error *err = NULL;
732 struct got_tree_entry *te = NULL;
733 size_t path_len = strlen(path);
734 struct got_fileindex_entry *next;
736 te = SIMPLEQ_FIRST(&entries->head);
737 while ((*ie && got_path_is_child((*ie)->path, path, path_len)) || te) {
738 if (te && *ie) {
739 char *te_path;
740 int cmp;
741 if (asprintf(&te_path, "%s/%s", path, te->name) == -1) {
742 err = got_error_from_errno("asprintf");
743 break;
745 cmp = got_path_cmp((*ie)->path, te_path,
746 got_fileindex_entry_path_len(*ie), strlen(te_path));
747 free(te_path);
748 if (cmp == 0) {
749 if (got_path_is_child((*ie)->path, path,
750 path_len) && (entry_name == NULL ||
751 strcmp(te->name, entry_name) == 0)) {
752 err = cb->diff_old_new(cb_arg, *ie, te,
753 path);
754 if (err || entry_name)
755 break;
757 *ie = walk_fileindex(fileindex, *ie);
758 err = walk_tree(&te, fileindex, ie, te,
759 path, entry_name, repo, cb, cb_arg);
760 } else if (cmp < 0) {
761 next = walk_fileindex(fileindex, *ie);
762 if (got_path_is_child((*ie)->path, path,
763 path_len) && (entry_name == NULL ||
764 strcmp(te->name, entry_name) == 0)) {
765 err = cb->diff_old(cb_arg, *ie, path);
766 if (err || entry_name)
767 break;
769 *ie = next;
770 } else {
771 if ((entry_name == NULL ||
772 strcmp(te->name, entry_name) == 0)) {
773 err = cb->diff_new(cb_arg, te, path);
774 if (err || entry_name)
775 break;
777 err = walk_tree(&te, fileindex, ie, te,
778 path, entry_name, repo, cb, cb_arg);
780 if (err)
781 break;
782 } else if (*ie) {
783 next = walk_fileindex(fileindex, *ie);
784 if (got_path_is_child((*ie)->path, path, path_len) &&
785 (entry_name == NULL ||
786 strcmp(te->name, entry_name) == 0)) {
787 err = cb->diff_old(cb_arg, *ie, path);
788 if (err || entry_name)
789 break;
791 *ie = next;
792 } else if (te) {
793 if (entry_name == NULL ||
794 strcmp(te->name, entry_name) == 0) {
795 err = cb->diff_new(cb_arg, te, path);
796 if (err || entry_name)
797 break;
799 err = walk_tree(&te, fileindex, ie, te, path,
800 entry_name, repo, cb, cb_arg);
801 if (err)
802 break;
806 return err;
809 const struct got_error *
810 got_fileindex_diff_tree(struct got_fileindex *fileindex,
811 struct got_tree_object *tree, const char *path, const char *entry_name,
812 struct got_repository *repo,
813 struct got_fileindex_diff_tree_cb *cb, void *cb_arg)
815 struct got_fileindex_entry *ie;
816 ie = RB_MIN(got_fileindex_tree, &fileindex->entries);
817 while (ie && !got_path_is_child(ie->path, path, strlen(path)))
818 ie = walk_fileindex(fileindex, ie);
819 return diff_fileindex_tree(fileindex, &ie,
820 got_object_tree_get_entries(tree), path, entry_name, repo,
821 cb, cb_arg);
824 static const struct got_error *
825 diff_fileindex_dir(struct got_fileindex *, struct got_fileindex_entry **,
826 struct got_pathlist_head *, const char *, const char *,
827 struct got_repository *, struct got_fileindex_diff_dir_cb *, void *);
829 static const struct got_error *
830 read_dirlist(struct got_pathlist_head *dirlist, DIR *dir, const char *path)
832 const struct got_error *err = NULL;
833 struct got_pathlist_entry *new = NULL;
834 struct dirent *dep = NULL;
835 struct dirent *de = NULL;
837 for (;;) {
838 de = malloc(sizeof(struct dirent) + NAME_MAX + 1);
839 if (de == NULL) {
840 err = got_error_from_errno("malloc");
841 break;
844 if (readdir_r(dir, de, &dep) != 0) {
845 err = got_error_from_errno("readdir_r");
846 free(de);
847 break;
849 if (dep == NULL) {
850 free(de);
851 break;
854 if (strcmp(de->d_name, ".") == 0 ||
855 strcmp(de->d_name, "..") == 0 ||
856 (path[0] == '\0' &&
857 strcmp(de->d_name, GOT_WORKTREE_GOT_DIR) == 0)) {
858 free(de);
859 continue;
862 err = got_pathlist_insert(&new, dirlist, de->d_name, de);
863 if (err) {
864 free(de);
865 break;
867 if (new == NULL) {
868 err = got_error(GOT_ERR_DIR_DUP_ENTRY);
869 free(de);
870 break;
874 return err;
877 void
878 free_dirlist(struct got_pathlist_head *dirlist)
880 struct got_pathlist_entry *dle;
882 TAILQ_FOREACH(dle, dirlist, entry)
883 free(dle->data);
884 got_pathlist_free(dirlist);
887 static const struct got_error *
888 walk_dir(struct got_pathlist_entry **next, struct got_fileindex *fileindex,
889 struct got_fileindex_entry **ie, struct got_pathlist_entry *dle,
890 const char *path, const char *rootpath, struct got_repository *repo,
891 struct got_fileindex_diff_dir_cb *cb, void *cb_arg)
893 const struct got_error *err = NULL;
894 struct dirent *de = dle->data;
896 *next = NULL;
898 if (de->d_type == DT_DIR) {
899 char *subpath;
900 char *subdirpath;
901 DIR *subdir;
902 struct got_pathlist_head subdirlist;
904 TAILQ_INIT(&subdirlist);
906 if (asprintf(&subpath, "%s%s%s", path,
907 path[0] == '\0' ? "" : "/", de->d_name) == -1)
908 return got_error_from_errno("asprintf");
910 if (asprintf(&subdirpath, "%s/%s", rootpath, subpath) == -1) {
911 free(subpath);
912 return got_error_from_errno("asprintf");
915 subdir = opendir(subdirpath);
916 if (subdir == NULL) {
917 free(subpath);
918 free(subdirpath);
919 return got_error_from_errno2("opendir", subdirpath);
922 err = read_dirlist(&subdirlist, subdir, subdirpath);
923 if (err) {
924 free(subpath);
925 free(subdirpath);
926 closedir(subdir);
927 return err;
929 err = diff_fileindex_dir(fileindex, ie, &subdirlist, rootpath,
930 subpath, repo, cb, cb_arg);
931 free(subpath);
932 free(subdirpath);
933 closedir(subdir);
934 free_dirlist(&subdirlist);
935 if (err)
936 return err;
939 *next = TAILQ_NEXT(dle, entry);
940 return NULL;
943 static const struct got_error *
944 diff_fileindex_dir(struct got_fileindex *fileindex,
945 struct got_fileindex_entry **ie, struct got_pathlist_head *dirlist,
946 const char *rootpath, const char *path, struct got_repository *repo,
947 struct got_fileindex_diff_dir_cb *cb, void *cb_arg)
949 const struct got_error *err = NULL;
950 struct dirent *de = NULL;
951 size_t path_len = strlen(path);
952 struct got_pathlist_entry *dle;
954 dle = TAILQ_FIRST(dirlist);
955 while ((*ie && got_path_is_child((*ie)->path, path, path_len)) || dle) {
956 if (dle && *ie) {
957 char *de_path;
958 int cmp;
959 de = dle->data;
960 if (asprintf(&de_path, "%s/%s", path,
961 de->d_name) == -1) {
962 err = got_error_from_errno("asprintf");
963 break;
965 cmp = got_path_cmp((*ie)->path, de_path,
966 got_fileindex_entry_path_len(*ie),
967 strlen(path) + 1 + de->d_namlen);
968 free(de_path);
969 if (cmp == 0) {
970 err = cb->diff_old_new(cb_arg, *ie, de, path);
971 if (err)
972 break;
973 *ie = walk_fileindex(fileindex, *ie);
974 err = walk_dir(&dle, fileindex, ie, dle, path,
975 rootpath, repo, cb, cb_arg);
976 } else if (cmp < 0 ) {
977 err = cb->diff_old(cb_arg, *ie, path);
978 if (err)
979 break;
980 *ie = walk_fileindex(fileindex, *ie);
981 } else {
982 err = cb->diff_new(cb_arg, de, path);
983 if (err)
984 break;
985 err = walk_dir(&dle, fileindex, ie, dle, path,
986 rootpath, repo, cb, cb_arg);
988 if (err)
989 break;
990 } else if (*ie) {
991 err = cb->diff_old(cb_arg, *ie, path);
992 if (err)
993 break;
994 *ie = walk_fileindex(fileindex, *ie);
995 } else if (dle) {
996 de = dle->data;
997 err = cb->diff_new(cb_arg, de, path);
998 if (err)
999 break;
1000 err = walk_dir(&dle, fileindex, ie, dle, path,
1001 rootpath, repo, cb, cb_arg);
1002 if (err)
1003 break;
1007 return err;
1010 const struct got_error *
1011 got_fileindex_diff_dir(struct got_fileindex *fileindex, DIR *rootdir,
1012 const char *rootpath, const char *path, struct got_repository *repo,
1013 struct got_fileindex_diff_dir_cb *cb, void *cb_arg)
1015 const struct got_error *err;
1016 struct got_fileindex_entry *ie;
1017 struct got_pathlist_head dirlist;
1019 TAILQ_INIT(&dirlist);
1020 err = read_dirlist(&dirlist, rootdir, path);
1021 if (err)
1022 return err;
1023 ie = RB_MIN(got_fileindex_tree, &fileindex->entries);
1024 while (ie && !got_path_is_child(ie->path, path, strlen(path)))
1025 ie = walk_fileindex(fileindex, ie);
1026 err = diff_fileindex_dir(fileindex, &ie, &dirlist, rootpath, path,
1027 repo, cb, cb_arg);
1028 free_dirlist(&dirlist);
1029 return err;
1032 RB_GENERATE(got_fileindex_tree, got_fileindex_entry, entry, got_fileindex_cmp);