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 "got_compat.h"
19 #include <sys/queue.h>
31 #include "got_error.h"
32 #include "got_object.h"
35 #include "got_lib_hash.h"
36 #include "got_lib_fileindex.h"
37 #include "got_lib_worktree.h"
39 /* got_fileindex_entry flags */
40 #define GOT_FILEIDX_F_PATH_LEN 0x00000fff
41 #define GOT_FILEIDX_F_STAGE 0x0000f000
42 #define GOT_FILEIDX_F_STAGE_SHIFT 12
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
47 #define GOT_FILEIDX_F_REMOVE_ON_FLUSH 0x00100000
48 #define GOT_FILEIDX_F_SKIPPED 0x00200000
50 struct got_fileindex
{
51 struct got_fileindex_tree entries
;
52 int nentries
; /* Does not include entries marked for removal. */
53 #define GOT_FILEIDX_MAX_ENTRIES INT_MAX
57 got_fileindex_entry_perms_get(struct got_fileindex_entry
*ie
)
59 return ((ie
->mode
& GOT_FILEIDX_MODE_PERMS
) >>
60 GOT_FILEIDX_MODE_PERMS_SHIFT
);
64 fileindex_entry_perms_set(struct got_fileindex_entry
*ie
, mode_t mode
)
66 ie
->mode
&= ~GOT_FILEIDX_MODE_PERMS
;
67 ie
->mode
|= ((mode
<< GOT_FILEIDX_MODE_PERMS_SHIFT
) &
68 GOT_FILEIDX_MODE_PERMS
);
72 got_fileindex_perms_to_st(struct got_fileindex_entry
*ie
)
74 mode_t perms
= got_fileindex_entry_perms_get(ie
);
75 int type
= got_fileindex_entry_filetype_get(ie
);
78 if (type
== GOT_FILEIDX_MODE_REGULAR_FILE
||
79 type
== GOT_FILEIDX_MODE_BAD_SYMLINK
)
84 return (ftype
| (perms
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)));
87 const struct got_error
*
88 got_fileindex_entry_update(struct got_fileindex_entry
*ie
,
89 int wt_fd
, const char *ondisk_path
, uint8_t *blob_sha1
,
90 uint8_t *commit_sha1
, int update_timestamps
)
94 if (fstatat(wt_fd
, ondisk_path
, &sb
, AT_SYMLINK_NOFOLLOW
) != 0) {
95 if (!((ie
->flags
& GOT_FILEIDX_F_NO_FILE_ON_DISK
) &&
97 return got_error_from_errno2("fstatat", ondisk_path
);
98 sb
.st_mode
= GOT_DEFAULT_FILE_MODE
;
100 if (sb
.st_mode
& S_IFDIR
)
101 return got_error_set_errno(EISDIR
, ondisk_path
);
102 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 memmove(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
,
224 ie
->mode
&= ~GOT_FILEIDX_MODE_FILE_TYPE_STAGED
;
225 ie
->mode
|= ((type
<< GOT_FILEIDX_MODE_FILE_TYPE_STAGED_SHIFT
) &
226 GOT_FILEIDX_MODE_FILE_TYPE_STAGED
);
230 got_fileindex_entry_staged_filetype_get(struct got_fileindex_entry
*ie
)
232 return (ie
->mode
& GOT_FILEIDX_MODE_FILE_TYPE_STAGED
) >>
233 GOT_FILEIDX_MODE_FILE_TYPE_STAGED_SHIFT
;
237 got_fileindex_entry_has_blob(struct got_fileindex_entry
*ie
)
239 return (ie
->flags
& GOT_FILEIDX_F_NO_BLOB
) == 0;
243 got_fileindex_entry_has_commit(struct got_fileindex_entry
*ie
)
245 return (ie
->flags
& GOT_FILEIDX_F_NO_COMMIT
) == 0;
249 got_fileindex_entry_has_file_on_disk(struct got_fileindex_entry
*ie
)
251 return (ie
->flags
& GOT_FILEIDX_F_NO_FILE_ON_DISK
) == 0;
255 got_fileindex_entry_was_skipped(struct got_fileindex_entry
*ie
)
257 return (ie
->flags
& GOT_FILEIDX_F_SKIPPED
) != 0;
260 static const struct got_error
*
261 add_entry(struct got_fileindex
*fileindex
, struct got_fileindex_entry
*ie
)
263 if (fileindex
->nentries
>= GOT_FILEIDX_MAX_ENTRIES
)
264 return got_error(GOT_ERR_NO_SPACE
);
266 if (RB_INSERT(got_fileindex_tree
, &fileindex
->entries
, ie
) != NULL
)
267 return got_error_path(ie
->path
, GOT_ERR_FILEIDX_DUP_ENTRY
);
269 fileindex
->nentries
++;
273 const struct got_error
*
274 got_fileindex_entry_add(struct got_fileindex
*fileindex
,
275 struct got_fileindex_entry
*ie
)
277 /* Flag this entry until it gets written out to disk. */
278 ie
->flags
|= GOT_FILEIDX_F_NOT_FLUSHED
;
280 return add_entry(fileindex
, ie
);
284 got_fileindex_entry_remove(struct got_fileindex
*fileindex
,
285 struct got_fileindex_entry
*ie
)
288 * Removing an entry from the RB tree immediately breaks
289 * in-progress iterations over file index entries.
290 * So flag this entry for removal and remove it once the index
291 * is written out to disk. Meanwhile, pretend this entry no longer
292 * exists if we get queried for it again before then.
294 ie
->flags
|= GOT_FILEIDX_F_REMOVE_ON_FLUSH
;
295 fileindex
->nentries
--;
298 struct got_fileindex_entry
*
299 got_fileindex_entry_get(struct got_fileindex
*fileindex
, const char *path
,
302 struct got_fileindex_entry
*ie
;
303 struct got_fileindex_entry key
;
304 memset(&key
, 0, sizeof(key
));
305 key
.path
= (char *)path
;
306 key
.flags
= (path_len
& GOT_FILEIDX_F_PATH_LEN
);
307 ie
= RB_FIND(got_fileindex_tree
, &fileindex
->entries
, &key
);
308 if (ie
&& (ie
->flags
& GOT_FILEIDX_F_REMOVE_ON_FLUSH
))
313 const struct got_error
*
314 got_fileindex_for_each_entry_safe(struct got_fileindex
*fileindex
,
315 got_fileindex_cb cb
, void *cb_arg
)
317 const struct got_error
*err
;
318 struct got_fileindex_entry
*ie
, *tmp
;
320 RB_FOREACH_SAFE(ie
, got_fileindex_tree
, &fileindex
->entries
, tmp
) {
321 if (ie
->flags
& GOT_FILEIDX_F_REMOVE_ON_FLUSH
)
323 err
= (*cb
)(cb_arg
, ie
);
330 struct got_fileindex
*
331 got_fileindex_alloc(void)
333 struct got_fileindex
*fileindex
;
335 fileindex
= calloc(1, sizeof(*fileindex
));
336 if (fileindex
== NULL
)
339 RB_INIT(&fileindex
->entries
);
344 got_fileindex_free(struct got_fileindex
*fileindex
)
346 struct got_fileindex_entry
*ie
;
348 while ((ie
= RB_MIN(got_fileindex_tree
, &fileindex
->entries
))) {
349 RB_REMOVE(got_fileindex_tree
, &fileindex
->entries
, ie
);
350 got_fileindex_entry_free(ie
);
355 static const struct got_error
*
356 write_fileindex_val64(struct got_hash
*ctx
, uint64_t val
, FILE *outfile
)
361 got_hash_update(ctx
, &val
, sizeof(val
));
362 n
= fwrite(&val
, 1, sizeof(val
), outfile
);
363 if (n
!= sizeof(val
))
364 return got_ferror(outfile
, GOT_ERR_IO
);
368 static const struct got_error
*
369 write_fileindex_val32(struct got_hash
*ctx
, uint32_t val
, FILE *outfile
)
374 got_hash_update(ctx
, &val
, sizeof(val
));
375 n
= fwrite(&val
, 1, sizeof(val
), outfile
);
376 if (n
!= sizeof(val
))
377 return got_ferror(outfile
, GOT_ERR_IO
);
381 static const struct got_error
*
382 write_fileindex_val16(struct got_hash
*ctx
, uint16_t val
, FILE *outfile
)
387 got_hash_update(ctx
, &val
, sizeof(val
));
388 n
= fwrite(&val
, 1, sizeof(val
), outfile
);
389 if (n
!= sizeof(val
))
390 return got_ferror(outfile
, GOT_ERR_IO
);
394 static const struct got_error
*
395 write_fileindex_path(struct got_hash
*ctx
, const char *path
, FILE *outfile
)
397 size_t n
, len
, pad
= 0;
398 static const uint8_t zero
[8] = { 0 };
401 while ((len
+ pad
) % 8 != 0)
404 pad
= 8; /* NUL-terminate */
406 got_hash_update(ctx
, path
, len
);
407 n
= fwrite(path
, 1, len
, outfile
);
409 return got_ferror(outfile
, GOT_ERR_IO
);
410 got_hash_update(ctx
, zero
, pad
);
411 n
= fwrite(zero
, 1, pad
, outfile
);
413 return got_ferror(outfile
, GOT_ERR_IO
);
417 static const struct got_error
*
418 write_fileindex_entry(struct got_hash
*ctx
, struct got_fileindex_entry
*ie
,
421 const struct got_error
*err
;
425 err
= write_fileindex_val64(ctx
, ie
->ctime_sec
, outfile
);
428 err
= write_fileindex_val64(ctx
, ie
->ctime_nsec
, outfile
);
431 err
= write_fileindex_val64(ctx
, ie
->mtime_sec
, outfile
);
434 err
= write_fileindex_val64(ctx
, ie
->mtime_nsec
, outfile
);
438 err
= write_fileindex_val32(ctx
, ie
->uid
, outfile
);
441 err
= write_fileindex_val32(ctx
, ie
->gid
, outfile
);
444 err
= write_fileindex_val32(ctx
, ie
->size
, outfile
);
448 err
= write_fileindex_val16(ctx
, ie
->mode
, outfile
);
452 got_hash_update(ctx
, ie
->blob_sha1
, SHA1_DIGEST_LENGTH
);
453 n
= fwrite(ie
->blob_sha1
, 1, SHA1_DIGEST_LENGTH
, outfile
);
454 if (n
!= SHA1_DIGEST_LENGTH
)
455 return got_ferror(outfile
, GOT_ERR_IO
);
457 got_hash_update(ctx
, ie
->commit_sha1
, SHA1_DIGEST_LENGTH
);
458 n
= fwrite(ie
->commit_sha1
, 1, SHA1_DIGEST_LENGTH
, outfile
);
459 if (n
!= SHA1_DIGEST_LENGTH
)
460 return got_ferror(outfile
, GOT_ERR_IO
);
462 err
= write_fileindex_val32(ctx
, ie
->flags
, outfile
);
466 err
= write_fileindex_path(ctx
, ie
->path
, outfile
);
470 stage
= got_fileindex_entry_stage_get(ie
);
471 if (stage
== GOT_FILEIDX_STAGE_MODIFY
||
472 stage
== GOT_FILEIDX_STAGE_ADD
) {
473 got_hash_update(ctx
, ie
->staged_blob_sha1
, SHA1_DIGEST_LENGTH
);
474 n
= fwrite(ie
->staged_blob_sha1
, 1, SHA1_DIGEST_LENGTH
,
476 if (n
!= SHA1_DIGEST_LENGTH
)
477 return got_ferror(outfile
, GOT_ERR_IO
);
483 const struct got_error
*
484 got_fileindex_write(struct got_fileindex
*fileindex
, FILE *outfile
)
486 const struct got_error
*err
= NULL
;
487 struct got_fileindex_hdr hdr
;
489 uint8_t hash
[GOT_HASH_DIGEST_MAXLEN
];
491 struct got_fileindex_entry
*ie
, *tmp
;
493 got_hash_init(&ctx
, GOT_HASH_SHA1
);
495 hdr
.signature
= htobe32(GOT_FILE_INDEX_SIGNATURE
);
496 hdr
.version
= htobe32(GOT_FILE_INDEX_VERSION
);
497 hdr
.nentries
= htobe32(fileindex
->nentries
);
499 got_hash_update(&ctx
, &hdr
.signature
, sizeof(hdr
.signature
));
500 got_hash_update(&ctx
, &hdr
.version
, sizeof(hdr
.version
));
501 got_hash_update(&ctx
, &hdr
.nentries
, sizeof(hdr
.nentries
));
502 n
= fwrite(&hdr
.signature
, 1, sizeof(hdr
.signature
), outfile
);
503 if (n
!= sizeof(hdr
.signature
))
504 return got_ferror(outfile
, GOT_ERR_IO
);
505 n
= fwrite(&hdr
.version
, 1, sizeof(hdr
.version
), outfile
);
506 if (n
!= sizeof(hdr
.version
))
507 return got_ferror(outfile
, GOT_ERR_IO
);
508 n
= fwrite(&hdr
.nentries
, 1, sizeof(hdr
.nentries
), outfile
);
509 if (n
!= sizeof(hdr
.nentries
))
510 return got_ferror(outfile
, GOT_ERR_IO
);
512 RB_FOREACH_SAFE(ie
, got_fileindex_tree
, &fileindex
->entries
, tmp
) {
513 ie
->flags
&= ~GOT_FILEIDX_F_NOT_FLUSHED
;
514 ie
->flags
&= ~GOT_FILEIDX_F_SKIPPED
;
515 if (ie
->flags
& GOT_FILEIDX_F_REMOVE_ON_FLUSH
) {
516 RB_REMOVE(got_fileindex_tree
, &fileindex
->entries
, ie
);
517 got_fileindex_entry_free(ie
);
520 err
= write_fileindex_entry(&ctx
, ie
, outfile
);
525 got_hash_final(&ctx
, hash
);
526 n
= fwrite(hash
, 1, SHA1_DIGEST_LENGTH
, outfile
);
527 if (n
!= SHA1_DIGEST_LENGTH
)
528 return got_ferror(outfile
, GOT_ERR_IO
);
530 if (fflush(outfile
) != 0)
531 return got_error_from_errno("fflush");
536 static const struct got_error
*
537 read_fileindex_val64(uint64_t *val
, struct got_hash
*ctx
, FILE *infile
)
541 n
= fread(val
, 1, sizeof(*val
), infile
);
542 if (n
!= sizeof(*val
))
543 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
544 got_hash_update(ctx
, val
, sizeof(*val
));
545 *val
= be64toh(*val
);
549 static const struct got_error
*
550 read_fileindex_val32(uint32_t *val
, struct got_hash
*ctx
, FILE *infile
)
554 n
= fread(val
, 1, sizeof(*val
), infile
);
555 if (n
!= sizeof(*val
))
556 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
557 got_hash_update(ctx
, val
, sizeof(*val
));
558 *val
= be32toh(*val
);
562 static const struct got_error
*
563 read_fileindex_val16(uint16_t *val
, struct got_hash
*ctx
, FILE *infile
)
567 n
= fread(val
, 1, sizeof(*val
), infile
);
568 if (n
!= sizeof(*val
))
569 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
570 got_hash_update(ctx
, val
, sizeof(*val
));
571 *val
= be16toh(*val
);
575 static const struct got_error
*
576 read_fileindex_path(char **path
, struct got_hash
*ctx
, FILE *infile
)
578 const struct got_error
*err
= NULL
;
579 const size_t chunk_size
= 8;
580 size_t n
, len
= 0, totlen
= chunk_size
;
582 *path
= malloc(totlen
);
584 return got_error_from_errno("malloc");
587 if (len
+ chunk_size
> totlen
) {
588 char *p
= reallocarray(*path
, totlen
+ chunk_size
, 1);
590 err
= got_error_from_errno("reallocarray");
593 totlen
+= chunk_size
;
596 n
= fread(*path
+ len
, 1, chunk_size
, infile
);
597 if (n
!= chunk_size
) {
598 err
= got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
601 got_hash_update(ctx
, *path
+ len
, chunk_size
);
603 } while (memchr(*path
+ len
- chunk_size
, '\0', chunk_size
) == NULL
);
612 static const struct got_error
*
613 read_fileindex_entry(struct got_fileindex_entry
**iep
, struct got_hash
*ctx
,
614 FILE *infile
, uint32_t version
)
616 const struct got_error
*err
;
617 struct got_fileindex_entry
*ie
;
622 ie
= calloc(1, sizeof(*ie
));
624 return got_error_from_errno("calloc");
626 err
= read_fileindex_val64(&ie
->ctime_sec
, ctx
, infile
);
629 err
= read_fileindex_val64(&ie
->ctime_nsec
, ctx
, infile
);
632 err
= read_fileindex_val64(&ie
->mtime_sec
, ctx
, infile
);
635 err
= read_fileindex_val64(&ie
->mtime_nsec
, ctx
, infile
);
639 err
= read_fileindex_val32(&ie
->uid
, ctx
, infile
);
642 err
= read_fileindex_val32(&ie
->gid
, ctx
, infile
);
645 err
= read_fileindex_val32(&ie
->size
, ctx
, infile
);
649 err
= read_fileindex_val16(&ie
->mode
, ctx
, infile
);
653 n
= fread(ie
->blob_sha1
, 1, SHA1_DIGEST_LENGTH
, infile
);
654 if (n
!= SHA1_DIGEST_LENGTH
) {
655 err
= got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
658 got_hash_update(ctx
, ie
->blob_sha1
, SHA1_DIGEST_LENGTH
);
660 n
= fread(ie
->commit_sha1
, 1, SHA1_DIGEST_LENGTH
, infile
);
661 if (n
!= SHA1_DIGEST_LENGTH
) {
662 err
= got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
665 got_hash_update(ctx
, ie
->commit_sha1
, SHA1_DIGEST_LENGTH
);
667 err
= read_fileindex_val32(&ie
->flags
, ctx
, infile
);
671 err
= read_fileindex_path(&ie
->path
, ctx
, infile
);
676 uint32_t stage
= got_fileindex_entry_stage_get(ie
);
677 if (stage
== GOT_FILEIDX_STAGE_MODIFY
||
678 stage
== GOT_FILEIDX_STAGE_ADD
) {
679 n
= fread(ie
->staged_blob_sha1
, 1, SHA1_DIGEST_LENGTH
,
681 if (n
!= SHA1_DIGEST_LENGTH
) {
682 err
= got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
685 got_hash_update(ctx
, ie
->staged_blob_sha1
,
689 /* GOT_FILE_INDEX_VERSION 1 does not support staging. */
690 ie
->flags
&= ~GOT_FILEIDX_F_STAGE
;
695 got_fileindex_entry_free(ie
);
701 const struct got_error
*
702 got_fileindex_read(struct got_fileindex
*fileindex
, FILE *infile
)
704 const struct got_error
*err
= NULL
;
705 struct got_fileindex_hdr hdr
;
707 struct got_fileindex_entry
*ie
;
708 uint8_t sha1_expected
[SHA1_DIGEST_LENGTH
];
709 uint8_t sha1
[SHA1_DIGEST_LENGTH
];
713 got_hash_init(&ctx
, GOT_HASH_SHA1
);
715 n
= fread(&hdr
.signature
, 1, sizeof(hdr
.signature
), infile
);
716 if (n
!= sizeof(hdr
.signature
)) {
717 if (n
== 0) /* EOF */
719 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
721 n
= fread(&hdr
.version
, 1, sizeof(hdr
.version
), infile
);
722 if (n
!= sizeof(hdr
.version
)) {
723 if (n
== 0) /* EOF */
725 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
727 n
= fread(&hdr
.nentries
, 1, sizeof(hdr
.nentries
), infile
);
728 if (n
!= sizeof(hdr
.nentries
)) {
729 if (n
== 0) /* EOF */
731 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
734 got_hash_update(&ctx
, &hdr
.signature
, sizeof(hdr
.signature
));
735 got_hash_update(&ctx
, &hdr
.version
, sizeof(hdr
.version
));
736 got_hash_update(&ctx
, &hdr
.nentries
, sizeof(hdr
.nentries
));
738 hdr
.signature
= be32toh(hdr
.signature
);
739 hdr
.version
= be32toh(hdr
.version
);
740 hdr
.nentries
= be32toh(hdr
.nentries
);
742 if (hdr
.signature
!= GOT_FILE_INDEX_SIGNATURE
)
743 return got_error(GOT_ERR_FILEIDX_SIG
);
744 if (hdr
.version
> GOT_FILE_INDEX_VERSION
)
745 return got_error(GOT_ERR_FILEIDX_VER
);
747 for (i
= 0; i
< hdr
.nentries
; i
++) {
748 err
= read_fileindex_entry(&ie
, &ctx
, infile
, hdr
.version
);
751 err
= add_entry(fileindex
, ie
);
756 n
= fread(sha1_expected
, 1, sizeof(sha1_expected
), infile
);
757 if (n
!= sizeof(sha1_expected
))
758 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
759 got_hash_final(&ctx
, sha1
);
760 if (memcmp(sha1
, sha1_expected
, SHA1_DIGEST_LENGTH
) != 0)
761 return got_error(GOT_ERR_FILEIDX_CSUM
);
766 static struct got_fileindex_entry
*
767 walk_fileindex(struct got_fileindex
*fileindex
, struct got_fileindex_entry
*ie
)
769 struct got_fileindex_entry
*next
;
771 next
= RB_NEXT(got_fileindex_tree
, &fileindex
->entries
, ie
);
773 /* Skip entries which were added or removed by diff callbacks. */
774 while (next
&& (next
->flags
& (GOT_FILEIDX_F_NOT_FLUSHED
|
775 GOT_FILEIDX_F_REMOVE_ON_FLUSH
)))
776 next
= RB_NEXT(got_fileindex_tree
, &fileindex
->entries
, next
);
781 static const struct got_error
*
782 diff_fileindex_tree(struct got_fileindex
*, struct got_fileindex_entry
**ie
,
783 struct got_tree_object
*tree
, const char *, const char *,
784 struct got_repository
*, struct got_fileindex_diff_tree_cb
*, void *);
786 static const struct got_error
*
787 walk_tree(struct got_tree_entry
**next
, struct got_fileindex
*fileindex
,
788 struct got_fileindex_entry
**ie
, struct got_tree_object
*tree
, int *tidx
,
789 const char *path
, const char *entry_name
, struct got_repository
*repo
,
790 struct got_fileindex_diff_tree_cb
*cb
, void *cb_arg
)
792 const struct got_error
*err
= NULL
;
793 struct got_tree_entry
*te
= got_object_tree_get_entry(tree
, *tidx
);
795 if (!got_object_tree_entry_is_submodule(te
) &&
796 S_ISDIR(got_tree_entry_get_mode(te
))) {
798 struct got_tree_object
*subtree
;
800 if (asprintf(&subpath
, "%s%s%s", path
,
801 path
[0] == '\0' ? "" : "/",
802 got_tree_entry_get_name(te
)) == -1)
803 return got_error_from_errno("asprintf");
805 err
= got_object_open_as_tree(&subtree
, repo
,
806 got_tree_entry_get_id(te
));
812 err
= diff_fileindex_tree(fileindex
, ie
, subtree
, subpath
,
813 entry_name
, repo
, cb
, cb_arg
);
815 got_object_tree_close(subtree
);
821 *next
= got_object_tree_get_entry(tree
, *tidx
);
825 static const struct got_error
*
826 diff_fileindex_tree(struct got_fileindex
*fileindex
,
827 struct got_fileindex_entry
**ie
, struct got_tree_object
*tree
,
828 const char *path
, const char *entry_name
, struct got_repository
*repo
,
829 struct got_fileindex_diff_tree_cb
*cb
, void *cb_arg
)
831 const struct got_error
*err
= NULL
;
832 struct got_tree_entry
*te
= NULL
;
833 size_t path_len
= strlen(path
);
834 struct got_fileindex_entry
*next
;
837 te
= got_object_tree_get_entry(tree
, tidx
);
838 while ((*ie
&& got_path_is_child((*ie
)->path
, path
, path_len
)) || te
) {
841 const char *te_name
= got_tree_entry_get_name(te
);
843 if (asprintf(&te_path
, "%s/%s", path
, te_name
) == -1) {
844 err
= got_error_from_errno("asprintf");
847 cmp
= got_path_cmp((*ie
)->path
, te_path
,
848 got_fileindex_entry_path_len(*ie
), strlen(te_path
));
851 if (got_path_is_child((*ie
)->path
, path
,
853 !got_object_tree_entry_is_submodule(te
) &&
854 (entry_name
== NULL
||
855 strcmp(te_name
, entry_name
) == 0)) {
856 err
= cb
->diff_old_new(cb_arg
, *ie
, te
,
858 if (err
|| entry_name
)
861 *ie
= walk_fileindex(fileindex
, *ie
);
862 err
= walk_tree(&te
, fileindex
, ie
, tree
, &tidx
,
863 path
, entry_name
, repo
, cb
, cb_arg
);
864 } else if (cmp
< 0) {
865 next
= walk_fileindex(fileindex
, *ie
);
866 if (got_path_is_child((*ie
)->path
, path
,
867 path_len
) && entry_name
== NULL
) {
868 err
= cb
->diff_old(cb_arg
, *ie
, path
);
869 if (err
|| entry_name
)
874 if ((entry_name
== NULL
||
875 strcmp(te_name
, entry_name
) == 0)) {
876 err
= cb
->diff_new(cb_arg
, te
, path
);
877 if (err
|| entry_name
)
880 err
= walk_tree(&te
, fileindex
, ie
, tree
, &tidx
,
881 path
, entry_name
, repo
, cb
, cb_arg
);
886 next
= walk_fileindex(fileindex
, *ie
);
887 if (got_path_is_child((*ie
)->path
, path
, path_len
) &&
888 (entry_name
== NULL
||
889 (te
&& strcmp(got_tree_entry_get_name(te
),
890 entry_name
) == 0))) {
891 err
= cb
->diff_old(cb_arg
, *ie
, path
);
892 if (err
|| entry_name
)
897 if (!got_object_tree_entry_is_submodule(te
) &&
898 (entry_name
== NULL
||
899 strcmp(got_tree_entry_get_name(te
), entry_name
)
901 err
= cb
->diff_new(cb_arg
, te
, path
);
902 if (err
|| entry_name
)
905 err
= walk_tree(&te
, fileindex
, ie
, tree
, &tidx
, path
,
906 entry_name
, repo
, cb
, cb_arg
);
915 const struct got_error
*
916 got_fileindex_diff_tree(struct got_fileindex
*fileindex
,
917 struct got_tree_object
*tree
, const char *path
, const char *entry_name
,
918 struct got_repository
*repo
,
919 struct got_fileindex_diff_tree_cb
*cb
, void *cb_arg
)
921 struct got_fileindex_entry
*ie
;
922 ie
= RB_MIN(got_fileindex_tree
, &fileindex
->entries
);
923 while (ie
&& !got_path_is_child(ie
->path
, path
, strlen(path
)))
924 ie
= walk_fileindex(fileindex
, ie
);
925 return diff_fileindex_tree(fileindex
, &ie
, tree
, path
, entry_name
, repo
,
929 static const struct got_error
*
930 diff_fileindex_dir(struct got_fileindex
*, struct got_fileindex_entry
**,
931 struct got_pathlist_head
*, int, const char *, const char *,
932 struct got_repository
*, struct got_fileindex_diff_dir_cb
*, void *);
934 static const struct got_error
*
935 read_dirlist(struct got_pathlist_head
*dirlist
, DIR *dir
, const char *path
)
937 const struct got_error
*err
= NULL
;
938 struct got_pathlist_entry
*new = NULL
;
939 struct dirent
*dep
= NULL
;
940 struct dirent
*de
= NULL
;
943 de
= malloc(sizeof(struct dirent
) + NAME_MAX
+ 1);
945 err
= got_error_from_errno("malloc");
949 if (readdir_r(dir
, de
, &dep
) != 0) {
950 err
= got_error_from_errno("readdir_r");
959 if (strcmp(de
->d_name
, ".") == 0 ||
960 strcmp(de
->d_name
, "..") == 0 ||
962 strcmp(de
->d_name
, GOT_WORKTREE_GOT_DIR
) == 0)) {
967 err
= got_pathlist_insert(&new, dirlist
, de
->d_name
, de
);
973 err
= got_error(GOT_ERR_DIR_DUP_ENTRY
);
983 have_tracked_file_in_dir(struct got_fileindex
*fileindex
, const char *path
)
985 struct got_fileindex_entry
*ie
;
986 size_t path_len
= strlen(path
);
989 ie
= RB_ROOT(&fileindex
->entries
);
991 if (got_path_is_child(ie
->path
, path
, path_len
))
993 cmp
= got_path_cmp(path
, ie
->path
, path_len
,
994 got_fileindex_entry_path_len(ie
));
996 ie
= RB_LEFT(ie
, entry
);
998 ie
= RB_RIGHT(ie
, entry
);
1006 static const struct got_error
*
1007 walk_dir(struct got_pathlist_entry
**next
, struct got_fileindex
*fileindex
,
1008 struct got_fileindex_entry
**ie
, struct got_pathlist_entry
*dle
, int fd
,
1009 const char *path
, const char *rootpath
, struct got_repository
*repo
,
1010 int ignore
, struct got_fileindex_diff_dir_cb
*cb
, void *cb_arg
)
1012 const struct got_error
*err
= NULL
;
1013 struct dirent
*de
= dle
->data
;
1019 /* Must traverse ignored directories if they contain tracked files. */
1020 if (de
->d_type
== DT_DIR
&& ignore
&&
1021 have_tracked_file_in_dir(fileindex
, path
))
1024 if (de
->d_type
== DT_DIR
&& !ignore
) {
1027 struct got_pathlist_head subdirlist
;
1029 TAILQ_INIT(&subdirlist
);
1031 if (asprintf(&subpath
, "%s%s%s", path
,
1032 path
[0] == '\0' ? "" : "/", de
->d_name
) == -1)
1033 return got_error_from_errno("asprintf");
1035 if (asprintf(&subdirpath
, "%s/%s", rootpath
, subpath
) == -1) {
1037 return got_error_from_errno("asprintf");
1040 subdirfd
= openat(fd
, de
->d_name
,
1041 O_RDONLY
| O_NOFOLLOW
| O_DIRECTORY
| O_CLOEXEC
);
1042 if (subdirfd
== -1) {
1043 if (errno
== EACCES
) {
1044 *next
= TAILQ_NEXT(dle
, entry
);
1047 err
= got_error_from_errno2("openat", subdirpath
);
1053 subdir
= fdopendir(subdirfd
);
1055 return got_error_from_errno2("fdopendir", path
);
1057 err
= read_dirlist(&subdirlist
, subdir
, subdirpath
);
1064 err
= diff_fileindex_dir(fileindex
, ie
, &subdirlist
,
1065 dirfd(subdir
), rootpath
, subpath
, repo
, cb
, cb_arg
);
1066 if (subdir
&& closedir(subdir
) == -1 && err
== NULL
)
1067 err
= got_error_from_errno2("closedir", subdirpath
);
1070 got_pathlist_free(&subdirlist
, GOT_PATHLIST_FREE_DATA
);
1075 *next
= TAILQ_NEXT(dle
, entry
);
1079 static const struct got_error
*
1080 dirent_type_fixup(struct dirent
*de
, const char *rootpath
, const char *path
)
1082 const struct got_error
*err
;
1086 if (de
->d_type
!= DT_UNKNOWN
)
1089 /* DT_UNKNOWN occurs on NFS mounts without "readdir plus" RPC. */
1090 if (asprintf(&dir_path
, "%s/%s", rootpath
, path
) == -1)
1091 return got_error_from_errno("asprintf");
1092 err
= got_path_dirent_type(&type
, dir_path
, de
);
1101 static const struct got_error
*
1102 diff_fileindex_dir(struct got_fileindex
*fileindex
,
1103 struct got_fileindex_entry
**ie
, struct got_pathlist_head
*dirlist
,
1104 int dirfd
, const char *rootpath
, const char *path
,
1105 struct got_repository
*repo
,
1106 struct got_fileindex_diff_dir_cb
*cb
, void *cb_arg
)
1108 const struct got_error
*err
= NULL
;
1109 struct dirent
*de
= NULL
;
1110 size_t path_len
= strlen(path
);
1111 struct got_pathlist_entry
*dle
;
1114 if (cb
->diff_traverse
) {
1115 err
= cb
->diff_traverse(cb_arg
, path
, dirfd
);
1120 dle
= TAILQ_FIRST(dirlist
);
1121 while ((*ie
&& got_path_is_child((*ie
)->path
, path
, path_len
)) || dle
) {
1126 err
= dirent_type_fixup(de
, rootpath
, path
);
1129 if (asprintf(&de_path
, "%s/%s", path
,
1130 de
->d_name
) == -1) {
1131 err
= got_error_from_errno("asprintf");
1134 cmp
= got_path_cmp((*ie
)->path
, de_path
,
1135 got_fileindex_entry_path_len(*ie
),
1136 strlen(path
) + 1 + strlen(de
->d_name
));
1139 err
= cb
->diff_old_new(cb_arg
, *ie
, de
, path
,
1143 *ie
= walk_fileindex(fileindex
, *ie
);
1144 err
= walk_dir(&dle
, fileindex
, ie
, dle
, dirfd
,
1145 path
, rootpath
, repo
, 0, cb
, cb_arg
);
1146 } else if (cmp
< 0 ) {
1147 err
= cb
->diff_old(cb_arg
, *ie
, path
);
1150 *ie
= walk_fileindex(fileindex
, *ie
);
1152 err
= cb
->diff_new(&ignore
, cb_arg
, de
, path
,
1156 err
= walk_dir(&dle
, fileindex
, ie
, dle
, dirfd
,
1157 path
, rootpath
, repo
, ignore
, cb
, cb_arg
);
1162 err
= cb
->diff_old(cb_arg
, *ie
, path
);
1165 *ie
= walk_fileindex(fileindex
, *ie
);
1168 err
= dirent_type_fixup(de
, rootpath
, path
);
1171 err
= cb
->diff_new(&ignore
, cb_arg
, de
, path
, dirfd
);
1174 err
= walk_dir(&dle
, fileindex
, ie
, dle
, dirfd
, path
,
1175 rootpath
, repo
, ignore
, cb
, cb_arg
);
1184 const struct got_error
*
1185 got_fileindex_diff_dir(struct got_fileindex
*fileindex
, int fd
,
1186 const char *rootpath
, const char *path
, struct got_repository
*repo
,
1187 struct got_fileindex_diff_dir_cb
*cb
, void *cb_arg
)
1189 const struct got_error
*err
;
1190 struct got_fileindex_entry
*ie
;
1191 struct got_pathlist_head dirlist
;
1195 TAILQ_INIT(&dirlist
);
1198 * Duplicate the file descriptor so we can call closedir() below
1199 * without closing the file descriptor passed in by our caller.
1203 return got_error_from_errno2("dup", path
);
1204 if (lseek(fd2
, 0, SEEK_SET
) == -1) {
1205 err
= got_error_from_errno2("lseek", path
);
1209 dir
= fdopendir(fd2
);
1211 err
= got_error_from_errno2("fdopendir", path
);
1215 err
= read_dirlist(&dirlist
, dir
, path
);
1221 ie
= RB_MIN(got_fileindex_tree
, &fileindex
->entries
);
1222 while (ie
&& !got_path_is_child(ie
->path
, path
, strlen(path
)))
1223 ie
= walk_fileindex(fileindex
, ie
);
1224 err
= diff_fileindex_dir(fileindex
, &ie
, &dirlist
, dirfd(dir
),
1225 rootpath
, path
, repo
, cb
, cb_arg
);
1227 if (closedir(dir
) == -1 && err
== NULL
)
1228 err
= got_error_from_errno2("closedir", path
);
1229 got_pathlist_free(&dirlist
, GOT_PATHLIST_FREE_DATA
);
1233 struct got_object_id
*
1234 got_fileindex_entry_get_staged_blob_id(struct got_object_id
*id
,
1235 struct got_fileindex_entry
*ie
)
1237 memset(id
, 0, sizeof(*id
));
1238 memcpy(id
->sha1
, ie
->staged_blob_sha1
, sizeof(ie
->staged_blob_sha1
));
1242 struct got_object_id
*
1243 got_fileindex_entry_get_blob_id(struct got_object_id
*id
,
1244 struct got_fileindex_entry
*ie
)
1246 memset(id
, 0, sizeof(*id
));
1247 memcpy(id
->sha1
, ie
->blob_sha1
, sizeof(ie
->blob_sha1
));
1251 struct got_object_id
*
1252 got_fileindex_entry_get_commit_id(struct got_object_id
*id
,
1253 struct got_fileindex_entry
*ie
)
1255 memset(id
, 0, sizeof(*id
));
1256 memcpy(id
->sha1
, ie
->commit_sha1
, sizeof(ie
->commit_sha1
));
1260 RB_GENERATE(got_fileindex_tree
, got_fileindex_entry
, entry
, got_fileindex_cmp
);