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.
29 #include "got_compat.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
46 #define GOT_FILEIDX_F_REMOVE_ON_FLUSH 0x00100000
47 #define GOT_FILEIDX_F_SKIPPED 0x00200000
49 struct got_fileindex
{
50 struct got_fileindex_tree entries
;
51 int nentries
; /* Does not include entries marked for removal. */
52 #define GOT_FILEIDX_MAX_ENTRIES INT_MAX
56 got_fileindex_entry_perms_get(struct got_fileindex_entry
*ie
)
58 return ((ie
->mode
& GOT_FILEIDX_MODE_PERMS
) >>
59 GOT_FILEIDX_MODE_PERMS_SHIFT
);
63 fileindex_entry_perms_set(struct got_fileindex_entry
*ie
, mode_t mode
)
65 ie
->mode
&= ~GOT_FILEIDX_MODE_PERMS
;
66 ie
->mode
|= ((mode
<< GOT_FILEIDX_MODE_PERMS_SHIFT
) &
67 GOT_FILEIDX_MODE_PERMS
);
71 got_fileindex_perms_to_st(struct got_fileindex_entry
*ie
)
73 mode_t perms
= got_fileindex_entry_perms_get(ie
);
74 int type
= got_fileindex_entry_filetype_get(ie
);
77 if (type
== GOT_FILEIDX_MODE_REGULAR_FILE
||
78 type
== GOT_FILEIDX_MODE_BAD_SYMLINK
)
83 return (ftype
| (perms
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)));
86 const struct got_error
*
87 got_fileindex_entry_update(struct got_fileindex_entry
*ie
,
88 int wt_fd
, const char *ondisk_path
, uint8_t *blob_sha1
,
89 uint8_t *commit_sha1
, int update_timestamps
)
93 if (fstatat(wt_fd
, ondisk_path
, &sb
, AT_SYMLINK_NOFOLLOW
) != 0) {
94 if (!((ie
->flags
& GOT_FILEIDX_F_NO_FILE_ON_DISK
) &&
96 return got_error_from_errno2("fstatat", ondisk_path
);
97 sb
.st_mode
= GOT_DEFAULT_FILE_MODE
;
99 if (sb
.st_mode
& S_IFDIR
)
100 return got_error_set_errno(EISDIR
, ondisk_path
);
101 ie
->flags
&= ~GOT_FILEIDX_F_NO_FILE_ON_DISK
;
105 if ((ie
->flags
& GOT_FILEIDX_F_NO_FILE_ON_DISK
) == 0) {
106 if (update_timestamps
) {
107 ie
->ctime_sec
= sb
.st_ctim
.tv_sec
;
108 ie
->ctime_nsec
= sb
.st_ctim
.tv_nsec
;
109 ie
->mtime_sec
= sb
.st_mtim
.tv_sec
;
110 ie
->mtime_nsec
= sb
.st_mtim
.tv_nsec
;
114 ie
->size
= (sb
.st_size
& 0xffffffff);
115 if (S_ISLNK(sb
.st_mode
)) {
116 got_fileindex_entry_filetype_set(ie
,
117 GOT_FILEIDX_MODE_SYMLINK
);
118 fileindex_entry_perms_set(ie
, 0);
120 got_fileindex_entry_filetype_set(ie
,
121 GOT_FILEIDX_MODE_REGULAR_FILE
);
122 fileindex_entry_perms_set(ie
,
123 sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
));
128 memcpy(ie
->blob_sha1
, blob_sha1
, SHA1_DIGEST_LENGTH
);
129 ie
->flags
&= ~GOT_FILEIDX_F_NO_BLOB
;
131 ie
->flags
|= GOT_FILEIDX_F_NO_BLOB
;
134 memcpy(ie
->commit_sha1
, commit_sha1
, SHA1_DIGEST_LENGTH
);
135 ie
->flags
&= ~GOT_FILEIDX_F_NO_COMMIT
;
137 ie
->flags
|= GOT_FILEIDX_F_NO_COMMIT
;
143 got_fileindex_entry_mark_deleted_from_disk(struct got_fileindex_entry
*ie
)
145 ie
->flags
|= GOT_FILEIDX_F_NO_FILE_ON_DISK
;
149 got_fileindex_entry_mark_skipped(struct got_fileindex_entry
*ie
)
151 ie
->flags
|= GOT_FILEIDX_F_SKIPPED
;
154 const struct got_error
*
155 got_fileindex_entry_alloc(struct got_fileindex_entry
**ie
,
160 *ie
= calloc(1, sizeof(**ie
));
162 return got_error_from_errno("calloc");
164 (*ie
)->path
= strdup(relpath
);
165 if ((*ie
)->path
== NULL
) {
166 const struct got_error
*err
= got_error_from_errno("strdup");
172 len
= strlen(relpath
);
173 if (len
> GOT_FILEIDX_F_PATH_LEN
)
174 len
= GOT_FILEIDX_F_PATH_LEN
;
181 got_fileindex_entry_free(struct got_fileindex_entry
*ie
)
188 got_fileindex_entry_path_len(const struct got_fileindex_entry
*ie
)
190 return (size_t)(ie
->flags
& GOT_FILEIDX_F_PATH_LEN
);
194 got_fileindex_entry_stage_get(const struct got_fileindex_entry
*ie
)
196 return ((ie
->flags
& GOT_FILEIDX_F_STAGE
) >> GOT_FILEIDX_F_STAGE_SHIFT
);
200 got_fileindex_entry_stage_set(struct got_fileindex_entry
*ie
, uint32_t stage
)
202 ie
->flags
&= ~GOT_FILEIDX_F_STAGE
;
203 ie
->flags
|= ((stage
<< GOT_FILEIDX_F_STAGE_SHIFT
) &
204 GOT_FILEIDX_F_STAGE
);
208 got_fileindex_entry_filetype_get(struct got_fileindex_entry
*ie
)
210 return (ie
->mode
& GOT_FILEIDX_MODE_FILE_TYPE_ONDISK
);
214 got_fileindex_entry_filetype_set(struct got_fileindex_entry
*ie
, int type
)
216 ie
->mode
&= ~GOT_FILEIDX_MODE_FILE_TYPE_ONDISK
;
217 ie
->mode
|= (type
& GOT_FILEIDX_MODE_FILE_TYPE_ONDISK
);
221 got_fileindex_entry_staged_filetype_set(struct got_fileindex_entry
*ie
, int type
)
223 ie
->mode
&= ~GOT_FILEIDX_MODE_FILE_TYPE_STAGED
;
224 ie
->mode
|= ((type
<< GOT_FILEIDX_MODE_FILE_TYPE_STAGED_SHIFT
) &
225 GOT_FILEIDX_MODE_FILE_TYPE_STAGED
);
229 got_fileindex_entry_staged_filetype_get(struct got_fileindex_entry
*ie
)
231 return (ie
->mode
& GOT_FILEIDX_MODE_FILE_TYPE_STAGED
) >>
232 GOT_FILEIDX_MODE_FILE_TYPE_STAGED_SHIFT
;
236 got_fileindex_entry_has_blob(struct got_fileindex_entry
*ie
)
238 return (ie
->flags
& GOT_FILEIDX_F_NO_BLOB
) == 0;
242 got_fileindex_entry_has_commit(struct got_fileindex_entry
*ie
)
244 return (ie
->flags
& GOT_FILEIDX_F_NO_COMMIT
) == 0;
248 got_fileindex_entry_has_file_on_disk(struct got_fileindex_entry
*ie
)
250 return (ie
->flags
& GOT_FILEIDX_F_NO_FILE_ON_DISK
) == 0;
254 got_fileindex_entry_was_skipped(struct got_fileindex_entry
*ie
)
256 return (ie
->flags
& GOT_FILEIDX_F_SKIPPED
) != 0;
259 static const struct got_error
*
260 add_entry(struct got_fileindex
*fileindex
, struct got_fileindex_entry
*ie
)
262 if (fileindex
->nentries
>= GOT_FILEIDX_MAX_ENTRIES
)
263 return got_error(GOT_ERR_NO_SPACE
);
265 RB_INSERT(got_fileindex_tree
, &fileindex
->entries
, ie
);
266 fileindex
->nentries
++;
270 const struct got_error
*
271 got_fileindex_entry_add(struct got_fileindex
*fileindex
,
272 struct got_fileindex_entry
*ie
)
274 /* Flag this entry until it gets written out to disk. */
275 ie
->flags
|= GOT_FILEIDX_F_NOT_FLUSHED
;
277 return add_entry(fileindex
, ie
);
281 got_fileindex_entry_remove(struct got_fileindex
*fileindex
,
282 struct got_fileindex_entry
*ie
)
285 * Removing an entry from the RB tree immediately breaks
286 * in-progress iterations over file index entries.
287 * So flag this entry for removal and remove it once the index
288 * is written out to disk. Meanwhile, pretend this entry no longer
289 * exists if we get queried for it again before then.
291 ie
->flags
|= GOT_FILEIDX_F_REMOVE_ON_FLUSH
;
292 fileindex
->nentries
--;
295 struct got_fileindex_entry
*
296 got_fileindex_entry_get(struct got_fileindex
*fileindex
, const char *path
,
299 struct got_fileindex_entry
*ie
;
300 struct got_fileindex_entry key
;
301 memset(&key
, 0, sizeof(key
));
302 key
.path
= (char *)path
;
303 key
.flags
= (path_len
& GOT_FILEIDX_F_PATH_LEN
);
304 ie
= RB_FIND(got_fileindex_tree
, &fileindex
->entries
, &key
);
305 if (ie
&& (ie
->flags
& GOT_FILEIDX_F_REMOVE_ON_FLUSH
))
310 const struct got_error
*
311 got_fileindex_for_each_entry_safe(struct got_fileindex
*fileindex
,
312 got_fileindex_cb cb
, void *cb_arg
)
314 const struct got_error
*err
;
315 struct got_fileindex_entry
*ie
, *tmp
;
317 RB_FOREACH_SAFE(ie
, got_fileindex_tree
, &fileindex
->entries
, tmp
) {
318 if (ie
->flags
& GOT_FILEIDX_F_REMOVE_ON_FLUSH
)
320 err
= (*cb
)(cb_arg
, ie
);
327 struct got_fileindex
*
328 got_fileindex_alloc(void)
330 struct got_fileindex
*fileindex
;
332 fileindex
= calloc(1, sizeof(*fileindex
));
333 if (fileindex
== NULL
)
336 RB_INIT(&fileindex
->entries
);
341 got_fileindex_free(struct got_fileindex
*fileindex
)
343 struct got_fileindex_entry
*ie
;
345 while ((ie
= RB_MIN(got_fileindex_tree
, &fileindex
->entries
))) {
346 RB_REMOVE(got_fileindex_tree
, &fileindex
->entries
, ie
);
347 got_fileindex_entry_free(ie
);
352 static const struct got_error
*
353 write_fileindex_val64(SHA1_CTX
*ctx
, uint64_t val
, FILE *outfile
)
358 SHA1Update(ctx
, (uint8_t *)&val
, sizeof(val
));
359 n
= fwrite(&val
, 1, sizeof(val
), outfile
);
360 if (n
!= sizeof(val
))
361 return got_ferror(outfile
, GOT_ERR_IO
);
365 static const struct got_error
*
366 write_fileindex_val32(SHA1_CTX
*ctx
, uint32_t val
, FILE *outfile
)
371 SHA1Update(ctx
, (uint8_t *)&val
, sizeof(val
));
372 n
= fwrite(&val
, 1, sizeof(val
), outfile
);
373 if (n
!= sizeof(val
))
374 return got_ferror(outfile
, GOT_ERR_IO
);
378 static const struct got_error
*
379 write_fileindex_val16(SHA1_CTX
*ctx
, uint16_t val
, FILE *outfile
)
384 SHA1Update(ctx
, (uint8_t *)&val
, sizeof(val
));
385 n
= fwrite(&val
, 1, sizeof(val
), outfile
);
386 if (n
!= sizeof(val
))
387 return got_ferror(outfile
, GOT_ERR_IO
);
391 static const struct got_error
*
392 write_fileindex_path(SHA1_CTX
*ctx
, const char *path
, FILE *outfile
)
394 size_t n
, len
, pad
= 0;
395 static const uint8_t zero
[8] = { 0 };
398 while ((len
+ pad
) % 8 != 0)
401 pad
= 8; /* NUL-terminate */
403 SHA1Update(ctx
, path
, len
);
404 n
= fwrite(path
, 1, len
, outfile
);
406 return got_ferror(outfile
, GOT_ERR_IO
);
407 SHA1Update(ctx
, zero
, pad
);
408 n
= fwrite(zero
, 1, pad
, outfile
);
410 return got_ferror(outfile
, GOT_ERR_IO
);
414 static const struct got_error
*
415 write_fileindex_entry(SHA1_CTX
*ctx
, struct got_fileindex_entry
*ie
,
418 const struct got_error
*err
;
422 err
= write_fileindex_val64(ctx
, ie
->ctime_sec
, outfile
);
425 err
= write_fileindex_val64(ctx
, ie
->ctime_nsec
, outfile
);
428 err
= write_fileindex_val64(ctx
, ie
->mtime_sec
, outfile
);
431 err
= write_fileindex_val64(ctx
, ie
->mtime_nsec
, outfile
);
435 err
= write_fileindex_val32(ctx
, ie
->uid
, outfile
);
438 err
= write_fileindex_val32(ctx
, ie
->gid
, outfile
);
441 err
= write_fileindex_val32(ctx
, ie
->size
, outfile
);
445 err
= write_fileindex_val16(ctx
, ie
->mode
, outfile
);
449 SHA1Update(ctx
, ie
->blob_sha1
, SHA1_DIGEST_LENGTH
);
450 n
= fwrite(ie
->blob_sha1
, 1, SHA1_DIGEST_LENGTH
, outfile
);
451 if (n
!= SHA1_DIGEST_LENGTH
)
452 return got_ferror(outfile
, GOT_ERR_IO
);
454 SHA1Update(ctx
, ie
->commit_sha1
, SHA1_DIGEST_LENGTH
);
455 n
= fwrite(ie
->commit_sha1
, 1, SHA1_DIGEST_LENGTH
, outfile
);
456 if (n
!= SHA1_DIGEST_LENGTH
)
457 return got_ferror(outfile
, GOT_ERR_IO
);
459 err
= write_fileindex_val32(ctx
, ie
->flags
, outfile
);
463 err
= write_fileindex_path(ctx
, ie
->path
, outfile
);
467 stage
= got_fileindex_entry_stage_get(ie
);
468 if (stage
== GOT_FILEIDX_STAGE_MODIFY
||
469 stage
== GOT_FILEIDX_STAGE_ADD
) {
470 SHA1Update(ctx
, ie
->staged_blob_sha1
, SHA1_DIGEST_LENGTH
);
471 n
= fwrite(ie
->staged_blob_sha1
, 1, SHA1_DIGEST_LENGTH
,
473 if (n
!= SHA1_DIGEST_LENGTH
)
474 return got_ferror(outfile
, GOT_ERR_IO
);
480 const struct got_error
*
481 got_fileindex_write(struct got_fileindex
*fileindex
, FILE *outfile
)
483 const struct got_error
*err
= NULL
;
484 struct got_fileindex_hdr hdr
;
486 uint8_t sha1
[SHA1_DIGEST_LENGTH
];
488 struct got_fileindex_entry
*ie
, *tmp
;
492 hdr
.signature
= htobe32(GOT_FILE_INDEX_SIGNATURE
);
493 hdr
.version
= htobe32(GOT_FILE_INDEX_VERSION
);
494 hdr
.nentries
= htobe32(fileindex
->nentries
);
496 SHA1Update(&ctx
, (uint8_t *)&hdr
.signature
, sizeof(hdr
.signature
));
497 SHA1Update(&ctx
, (uint8_t *)&hdr
.version
, sizeof(hdr
.version
));
498 SHA1Update(&ctx
, (uint8_t *)&hdr
.nentries
, sizeof(hdr
.nentries
));
499 n
= fwrite(&hdr
.signature
, 1, sizeof(hdr
.signature
), outfile
);
500 if (n
!= sizeof(hdr
.signature
))
501 return got_ferror(outfile
, GOT_ERR_IO
);
502 n
= fwrite(&hdr
.version
, 1, sizeof(hdr
.version
), outfile
);
503 if (n
!= sizeof(hdr
.version
))
504 return got_ferror(outfile
, GOT_ERR_IO
);
505 n
= fwrite(&hdr
.nentries
, 1, sizeof(hdr
.nentries
), outfile
);
506 if (n
!= sizeof(hdr
.nentries
))
507 return got_ferror(outfile
, GOT_ERR_IO
);
509 RB_FOREACH_SAFE(ie
, got_fileindex_tree
, &fileindex
->entries
, tmp
) {
510 ie
->flags
&= ~GOT_FILEIDX_F_NOT_FLUSHED
;
511 ie
->flags
&= ~GOT_FILEIDX_F_SKIPPED
;
512 if (ie
->flags
& GOT_FILEIDX_F_REMOVE_ON_FLUSH
) {
513 RB_REMOVE(got_fileindex_tree
, &fileindex
->entries
, ie
);
514 got_fileindex_entry_free(ie
);
517 err
= write_fileindex_entry(&ctx
, ie
, outfile
);
522 SHA1Final(sha1
, &ctx
);
523 n
= fwrite(sha1
, 1, sizeof(sha1
), outfile
);
524 if (n
!= sizeof(sha1
))
525 return got_ferror(outfile
, GOT_ERR_IO
);
527 if (fflush(outfile
) != 0)
528 return got_error_from_errno("fflush");
533 static const struct got_error
*
534 read_fileindex_val64(uint64_t *val
, SHA1_CTX
*ctx
, FILE *infile
)
538 n
= fread(val
, 1, sizeof(*val
), infile
);
539 if (n
!= sizeof(*val
))
540 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
541 SHA1Update(ctx
, (uint8_t *)val
, sizeof(*val
));
542 *val
= be64toh(*val
);
546 static const struct got_error
*
547 read_fileindex_val32(uint32_t *val
, SHA1_CTX
*ctx
, FILE *infile
)
551 n
= fread(val
, 1, sizeof(*val
), infile
);
552 if (n
!= sizeof(*val
))
553 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
554 SHA1Update(ctx
, (uint8_t *)val
, sizeof(*val
));
555 *val
= be32toh(*val
);
559 static const struct got_error
*
560 read_fileindex_val16(uint16_t *val
, SHA1_CTX
*ctx
, FILE *infile
)
564 n
= fread(val
, 1, sizeof(*val
), infile
);
565 if (n
!= sizeof(*val
))
566 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
567 SHA1Update(ctx
, (uint8_t *)val
, sizeof(*val
));
568 *val
= be16toh(*val
);
572 static const struct got_error
*
573 read_fileindex_path(char **path
, SHA1_CTX
*ctx
, FILE *infile
)
575 const struct got_error
*err
= NULL
;
576 const size_t chunk_size
= 8;
577 size_t n
, len
= 0, totlen
= chunk_size
;
579 *path
= malloc(totlen
);
581 return got_error_from_errno("malloc");
584 if (len
+ chunk_size
> totlen
) {
585 char *p
= reallocarray(*path
, totlen
+ chunk_size
, 1);
587 err
= got_error_from_errno("reallocarray");
590 totlen
+= chunk_size
;
593 n
= fread(*path
+ len
, 1, chunk_size
, infile
);
594 if (n
!= chunk_size
) {
595 err
= got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
598 SHA1Update(ctx
, *path
+ len
, chunk_size
);
600 } while (memchr(*path
+ len
- chunk_size
, '\0', chunk_size
) == NULL
);
609 static const struct got_error
*
610 read_fileindex_entry(struct got_fileindex_entry
**iep
, SHA1_CTX
*ctx
,
611 FILE *infile
, uint32_t version
)
613 const struct got_error
*err
;
614 struct got_fileindex_entry
*ie
;
619 ie
= calloc(1, sizeof(*ie
));
621 return got_error_from_errno("calloc");
623 err
= read_fileindex_val64(&ie
->ctime_sec
, ctx
, infile
);
626 err
= read_fileindex_val64(&ie
->ctime_nsec
, ctx
, infile
);
629 err
= read_fileindex_val64(&ie
->mtime_sec
, ctx
, infile
);
632 err
= read_fileindex_val64(&ie
->mtime_nsec
, ctx
, infile
);
636 err
= read_fileindex_val32(&ie
->uid
, ctx
, infile
);
639 err
= read_fileindex_val32(&ie
->gid
, ctx
, infile
);
642 err
= read_fileindex_val32(&ie
->size
, ctx
, infile
);
646 err
= read_fileindex_val16(&ie
->mode
, ctx
, infile
);
650 n
= fread(ie
->blob_sha1
, 1, SHA1_DIGEST_LENGTH
, infile
);
651 if (n
!= SHA1_DIGEST_LENGTH
) {
652 err
= got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
655 SHA1Update(ctx
, ie
->blob_sha1
, SHA1_DIGEST_LENGTH
);
657 n
= fread(ie
->commit_sha1
, 1, SHA1_DIGEST_LENGTH
, infile
);
658 if (n
!= SHA1_DIGEST_LENGTH
) {
659 err
= got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
662 SHA1Update(ctx
, ie
->commit_sha1
, SHA1_DIGEST_LENGTH
);
664 err
= read_fileindex_val32(&ie
->flags
, ctx
, infile
);
668 err
= read_fileindex_path(&ie
->path
, ctx
, infile
);
673 uint32_t stage
= got_fileindex_entry_stage_get(ie
);
674 if (stage
== GOT_FILEIDX_STAGE_MODIFY
||
675 stage
== GOT_FILEIDX_STAGE_ADD
) {
676 n
= fread(ie
->staged_blob_sha1
, 1, SHA1_DIGEST_LENGTH
,
678 if (n
!= SHA1_DIGEST_LENGTH
) {
679 err
= got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
682 SHA1Update(ctx
, ie
->staged_blob_sha1
, SHA1_DIGEST_LENGTH
);
685 /* GOT_FILE_INDEX_VERSION 1 does not support staging. */
686 ie
->flags
&= ~GOT_FILEIDX_F_STAGE
;
691 got_fileindex_entry_free(ie
);
697 const struct got_error
*
698 got_fileindex_read(struct got_fileindex
*fileindex
, FILE *infile
)
700 const struct got_error
*err
= NULL
;
701 struct got_fileindex_hdr hdr
;
703 struct got_fileindex_entry
*ie
;
704 uint8_t sha1_expected
[SHA1_DIGEST_LENGTH
];
705 uint8_t sha1
[SHA1_DIGEST_LENGTH
];
711 n
= fread(&hdr
.signature
, 1, sizeof(hdr
.signature
), infile
);
712 if (n
!= sizeof(hdr
.signature
)) {
713 if (n
== 0) /* EOF */
715 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
717 n
= fread(&hdr
.version
, 1, sizeof(hdr
.version
), infile
);
718 if (n
!= sizeof(hdr
.version
)) {
719 if (n
== 0) /* EOF */
721 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
723 n
= fread(&hdr
.nentries
, 1, sizeof(hdr
.nentries
), infile
);
724 if (n
!= sizeof(hdr
.nentries
)) {
725 if (n
== 0) /* EOF */
727 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
730 SHA1Update(&ctx
, (uint8_t *)&hdr
.signature
, sizeof(hdr
.signature
));
731 SHA1Update(&ctx
, (uint8_t *)&hdr
.version
, sizeof(hdr
.version
));
732 SHA1Update(&ctx
, (uint8_t *)&hdr
.nentries
, sizeof(hdr
.nentries
));
734 hdr
.signature
= be32toh(hdr
.signature
);
735 hdr
.version
= be32toh(hdr
.version
);
736 hdr
.nentries
= be32toh(hdr
.nentries
);
738 if (hdr
.signature
!= GOT_FILE_INDEX_SIGNATURE
)
739 return got_error(GOT_ERR_FILEIDX_SIG
);
740 if (hdr
.version
> GOT_FILE_INDEX_VERSION
)
741 return got_error(GOT_ERR_FILEIDX_VER
);
743 for (i
= 0; i
< hdr
.nentries
; i
++) {
744 err
= read_fileindex_entry(&ie
, &ctx
, infile
, hdr
.version
);
747 err
= add_entry(fileindex
, ie
);
752 n
= fread(sha1_expected
, 1, sizeof(sha1_expected
), infile
);
753 if (n
!= sizeof(sha1_expected
))
754 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
755 SHA1Final(sha1
, &ctx
);
756 if (memcmp(sha1
, sha1_expected
, SHA1_DIGEST_LENGTH
) != 0)
757 return got_error(GOT_ERR_FILEIDX_CSUM
);
762 static struct got_fileindex_entry
*
763 walk_fileindex(struct got_fileindex
*fileindex
, struct got_fileindex_entry
*ie
)
765 struct got_fileindex_entry
*next
;
767 next
= RB_NEXT(got_fileindex_tree
, &fileindex
->entries
, ie
);
769 /* Skip entries which were added or removed by diff callbacks. */
770 while (next
&& (next
->flags
& (GOT_FILEIDX_F_NOT_FLUSHED
|
771 GOT_FILEIDX_F_REMOVE_ON_FLUSH
)))
772 next
= RB_NEXT(got_fileindex_tree
, &fileindex
->entries
, next
);
777 static const struct got_error
*
778 diff_fileindex_tree(struct got_fileindex
*, struct got_fileindex_entry
**ie
,
779 struct got_tree_object
*tree
, const char *, const char *,
780 struct got_repository
*, struct got_fileindex_diff_tree_cb
*, void *);
782 static const struct got_error
*
783 walk_tree(struct got_tree_entry
**next
, struct got_fileindex
*fileindex
,
784 struct got_fileindex_entry
**ie
, struct got_tree_object
*tree
, int *tidx
,
785 const char *path
, const char *entry_name
, struct got_repository
*repo
,
786 struct got_fileindex_diff_tree_cb
*cb
, void *cb_arg
)
788 const struct got_error
*err
= NULL
;
789 struct got_tree_entry
*te
= got_object_tree_get_entry(tree
, *tidx
);
791 if (!got_object_tree_entry_is_submodule(te
) &&
792 S_ISDIR(got_tree_entry_get_mode(te
))) {
794 struct got_tree_object
*subtree
;
796 if (asprintf(&subpath
, "%s%s%s", path
,
797 path
[0] == '\0' ? "" : "/",
798 got_tree_entry_get_name(te
)) == -1)
799 return got_error_from_errno("asprintf");
801 err
= got_object_open_as_tree(&subtree
, repo
,
802 got_tree_entry_get_id(te
));
808 err
= diff_fileindex_tree(fileindex
, ie
, subtree
, subpath
,
809 entry_name
, repo
, cb
, cb_arg
);
811 got_object_tree_close(subtree
);
817 *next
= got_object_tree_get_entry(tree
, *tidx
);
821 static const struct got_error
*
822 diff_fileindex_tree(struct got_fileindex
*fileindex
,
823 struct got_fileindex_entry
**ie
, struct got_tree_object
*tree
,
824 const char *path
, const char *entry_name
, struct got_repository
*repo
,
825 struct got_fileindex_diff_tree_cb
*cb
, void *cb_arg
)
827 const struct got_error
*err
= NULL
;
828 struct got_tree_entry
*te
= NULL
;
829 size_t path_len
= strlen(path
);
830 struct got_fileindex_entry
*next
;
833 te
= got_object_tree_get_entry(tree
, tidx
);
834 while ((*ie
&& got_path_is_child((*ie
)->path
, path
, path_len
)) || te
) {
837 const char *te_name
= got_tree_entry_get_name(te
);
839 if (asprintf(&te_path
, "%s/%s", path
, te_name
) == -1) {
840 err
= got_error_from_errno("asprintf");
843 cmp
= got_path_cmp((*ie
)->path
, te_path
,
844 got_fileindex_entry_path_len(*ie
), strlen(te_path
));
847 if (got_path_is_child((*ie
)->path
, path
,
849 !got_object_tree_entry_is_submodule(te
) &&
850 (entry_name
== NULL
||
851 strcmp(te_name
, entry_name
) == 0)) {
852 err
= cb
->diff_old_new(cb_arg
, *ie
, te
,
854 if (err
|| entry_name
)
857 *ie
= walk_fileindex(fileindex
, *ie
);
858 err
= walk_tree(&te
, fileindex
, ie
, tree
, &tidx
,
859 path
, entry_name
, repo
, cb
, cb_arg
);
860 } else if (cmp
< 0) {
861 next
= walk_fileindex(fileindex
, *ie
);
862 if (got_path_is_child((*ie
)->path
, path
,
863 path_len
) && entry_name
== NULL
) {
864 err
= cb
->diff_old(cb_arg
, *ie
, path
);
865 if (err
|| entry_name
)
870 if ((entry_name
== NULL
||
871 strcmp(te_name
, entry_name
) == 0)) {
872 err
= cb
->diff_new(cb_arg
, te
, path
);
873 if (err
|| entry_name
)
876 err
= walk_tree(&te
, fileindex
, ie
, tree
, &tidx
,
877 path
, entry_name
, repo
, cb
, cb_arg
);
882 next
= walk_fileindex(fileindex
, *ie
);
883 if (got_path_is_child((*ie
)->path
, path
, path_len
) &&
884 (entry_name
== NULL
||
885 (te
&& strcmp(got_tree_entry_get_name(te
),
886 entry_name
) == 0))) {
887 err
= cb
->diff_old(cb_arg
, *ie
, path
);
888 if (err
|| entry_name
)
893 if (!got_object_tree_entry_is_submodule(te
) &&
894 (entry_name
== NULL
||
895 strcmp(got_tree_entry_get_name(te
), entry_name
)
897 err
= cb
->diff_new(cb_arg
, te
, path
);
898 if (err
|| entry_name
)
901 err
= walk_tree(&te
, fileindex
, ie
, tree
, &tidx
, path
,
902 entry_name
, repo
, cb
, cb_arg
);
911 const struct got_error
*
912 got_fileindex_diff_tree(struct got_fileindex
*fileindex
,
913 struct got_tree_object
*tree
, const char *path
, const char *entry_name
,
914 struct got_repository
*repo
,
915 struct got_fileindex_diff_tree_cb
*cb
, void *cb_arg
)
917 struct got_fileindex_entry
*ie
;
918 ie
= RB_MIN(got_fileindex_tree
, &fileindex
->entries
);
919 while (ie
&& !got_path_is_child(ie
->path
, path
, strlen(path
)))
920 ie
= walk_fileindex(fileindex
, ie
);
921 return diff_fileindex_tree(fileindex
, &ie
, tree
, path
, entry_name
, repo
,
925 static const struct got_error
*
926 diff_fileindex_dir(struct got_fileindex
*, struct got_fileindex_entry
**,
927 struct got_pathlist_head
*, int, const char *, const char *,
928 struct got_repository
*, struct got_fileindex_diff_dir_cb
*, void *);
930 static const struct got_error
*
931 read_dirlist(struct got_pathlist_head
*dirlist
, DIR *dir
, const char *path
)
933 const struct got_error
*err
= NULL
;
934 struct got_pathlist_entry
*new = NULL
;
935 struct dirent
*dep
= NULL
;
936 struct dirent
*de
= NULL
;
939 de
= malloc(sizeof(struct dirent
) + NAME_MAX
+ 1);
941 err
= got_error_from_errno("malloc");
945 if (readdir_r(dir
, de
, &dep
) != 0) {
946 err
= got_error_from_errno("readdir_r");
955 if (strcmp(de
->d_name
, ".") == 0 ||
956 strcmp(de
->d_name
, "..") == 0 ||
958 strcmp(de
->d_name
, GOT_WORKTREE_GOT_DIR
) == 0)) {
963 err
= got_pathlist_insert(&new, dirlist
, de
->d_name
, de
);
969 err
= got_error(GOT_ERR_DIR_DUP_ENTRY
);
979 free_dirlist(struct got_pathlist_head
*dirlist
)
981 struct got_pathlist_entry
*dle
;
983 TAILQ_FOREACH(dle
, dirlist
, entry
)
985 got_pathlist_free(dirlist
);
988 static const struct got_error
*
989 walk_dir(struct got_pathlist_entry
**next
, struct got_fileindex
*fileindex
,
990 struct got_fileindex_entry
**ie
, struct got_pathlist_entry
*dle
, int fd
,
991 const char *path
, const char *rootpath
, struct got_repository
*repo
,
992 struct got_fileindex_diff_dir_cb
*cb
, void *cb_arg
)
994 const struct got_error
*err
= NULL
;
995 struct dirent
*de
= dle
->data
;
1002 if (de
->d_type
== DT_UNKNOWN
) {
1003 /* Occurs on NFS mounts without "readdir plus" RPC. */
1005 if (asprintf(&dir_path
, "%s/%s", rootpath
, path
) == -1)
1006 return got_error_from_errno("asprintf");
1007 err
= got_path_dirent_type(&type
, dir_path
, de
);
1014 if (type
== DT_DIR
) {
1017 struct got_pathlist_head subdirlist
;
1019 TAILQ_INIT(&subdirlist
);
1021 if (asprintf(&subpath
, "%s%s%s", path
,
1022 path
[0] == '\0' ? "" : "/", de
->d_name
) == -1)
1023 return got_error_from_errno("asprintf");
1025 if (asprintf(&subdirpath
, "%s/%s", rootpath
, subpath
) == -1) {
1027 return got_error_from_errno("asprintf");
1030 subdirfd
= openat(fd
, de
->d_name
,
1031 O_RDONLY
| O_NOFOLLOW
| O_DIRECTORY
);
1032 if (subdirfd
== -1) {
1033 if (errno
== EACCES
) {
1034 *next
= TAILQ_NEXT(dle
, entry
);
1037 err
= got_error_from_errno2("openat", subdirpath
);
1043 subdir
= fdopendir(subdirfd
);
1045 return got_error_from_errno2("fdopendir", path
);
1047 err
= read_dirlist(&subdirlist
, subdir
, subdirpath
);
1054 err
= diff_fileindex_dir(fileindex
, ie
, &subdirlist
,
1055 dirfd(subdir
), rootpath
, subpath
, repo
, cb
, cb_arg
);
1056 if (subdir
&& closedir(subdir
) == -1 && err
== NULL
)
1057 err
= got_error_from_errno2("closedir", subdirpath
);
1060 free_dirlist(&subdirlist
);
1065 *next
= TAILQ_NEXT(dle
, entry
);
1069 static const struct got_error
*
1070 diff_fileindex_dir(struct got_fileindex
*fileindex
,
1071 struct got_fileindex_entry
**ie
, struct got_pathlist_head
*dirlist
,
1072 int dirfd
, const char *rootpath
, const char *path
,
1073 struct got_repository
*repo
,
1074 struct got_fileindex_diff_dir_cb
*cb
, void *cb_arg
)
1076 const struct got_error
*err
= NULL
;
1077 struct dirent
*de
= NULL
;
1078 size_t path_len
= strlen(path
);
1079 struct got_pathlist_entry
*dle
;
1081 if (cb
->diff_traverse
) {
1082 err
= cb
->diff_traverse(cb_arg
, path
, dirfd
);
1087 dle
= TAILQ_FIRST(dirlist
);
1088 while ((*ie
&& got_path_is_child((*ie
)->path
, path
, path_len
)) || dle
) {
1093 if (asprintf(&de_path
, "%s/%s", path
,
1094 de
->d_name
) == -1) {
1095 err
= got_error_from_errno("asprintf");
1098 cmp
= got_path_cmp((*ie
)->path
, de_path
,
1099 got_fileindex_entry_path_len(*ie
),
1100 strlen(path
) + 1 + strlen(de
->d_name
));
1103 err
= cb
->diff_old_new(cb_arg
, *ie
, de
, path
,
1107 *ie
= walk_fileindex(fileindex
, *ie
);
1108 err
= walk_dir(&dle
, fileindex
, ie
, dle
, dirfd
,
1109 path
, rootpath
, repo
, cb
, cb_arg
);
1110 } else if (cmp
< 0 ) {
1111 err
= cb
->diff_old(cb_arg
, *ie
, path
);
1114 *ie
= walk_fileindex(fileindex
, *ie
);
1116 err
= cb
->diff_new(cb_arg
, de
, path
, dirfd
);
1119 err
= walk_dir(&dle
, fileindex
, ie
, dle
, dirfd
,
1120 path
, rootpath
, repo
, cb
, cb_arg
);
1125 err
= cb
->diff_old(cb_arg
, *ie
, path
);
1128 *ie
= walk_fileindex(fileindex
, *ie
);
1131 err
= cb
->diff_new(cb_arg
, de
, path
, dirfd
);
1134 err
= walk_dir(&dle
, fileindex
, ie
, dle
, dirfd
, path
,
1135 rootpath
, repo
, cb
, cb_arg
);
1144 const struct got_error
*
1145 got_fileindex_diff_dir(struct got_fileindex
*fileindex
, int fd
,
1146 const char *rootpath
, const char *path
, struct got_repository
*repo
,
1147 struct got_fileindex_diff_dir_cb
*cb
, void *cb_arg
)
1149 const struct got_error
*err
;
1150 struct got_fileindex_entry
*ie
;
1151 struct got_pathlist_head dirlist
;
1155 TAILQ_INIT(&dirlist
);
1158 * Duplicate the file descriptor so we can call closedir() below
1159 * without closing the file descriptor passed in by our caller.
1163 return got_error_from_errno2("dup", path
);
1164 if (lseek(fd2
, 0, SEEK_SET
) == -1) {
1165 err
= got_error_from_errno2("lseek", path
);
1169 dir
= fdopendir(fd2
);
1171 err
= got_error_from_errno2("fdopendir", path
);
1175 err
= read_dirlist(&dirlist
, dir
, path
);
1181 ie
= RB_MIN(got_fileindex_tree
, &fileindex
->entries
);
1182 while (ie
&& !got_path_is_child(ie
->path
, path
, strlen(path
)))
1183 ie
= walk_fileindex(fileindex
, ie
);
1184 err
= diff_fileindex_dir(fileindex
, &ie
, &dirlist
, dirfd(dir
),
1185 rootpath
, path
, repo
, cb
, cb_arg
);
1187 if (closedir(dir
) == -1 && err
== NULL
)
1188 err
= got_error_from_errno2("closedir", path
);
1189 free_dirlist(&dirlist
);
1193 RB_GENERATE(got_fileindex_tree
, got_fileindex_entry
, entry
, got_fileindex_cmp
);