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>
32 #include "got_error.h"
33 #include "got_object.h"
36 #include "got_lib_hash.h"
37 #include "got_lib_fileindex.h"
38 #include "got_lib_worktree.h"
40 /* got_fileindex_entry flags */
41 #define GOT_FILEIDX_F_PATH_LEN 0x00000fff
42 #define GOT_FILEIDX_F_STAGE 0x0000f000
43 #define GOT_FILEIDX_F_STAGE_SHIFT 12
44 #define GOT_FILEIDX_F_NOT_FLUSHED 0x00010000
45 #define GOT_FILEIDX_F_NO_BLOB 0x00020000
46 #define GOT_FILEIDX_F_NO_COMMIT 0x00040000
47 #define GOT_FILEIDX_F_NO_FILE_ON_DISK 0x00080000
48 #define GOT_FILEIDX_F_REMOVE_ON_FLUSH 0x00100000
49 #define GOT_FILEIDX_F_SKIPPED 0x00200000
51 struct got_fileindex
{
52 struct got_fileindex_tree entries
;
53 int nentries
; /* Does not include entries marked for removal. */
54 #define GOT_FILEIDX_MAX_ENTRIES INT32_MAX
58 got_fileindex_entry_perms_get(struct got_fileindex_entry
*ie
)
60 return ((ie
->mode
& GOT_FILEIDX_MODE_PERMS
) >>
61 GOT_FILEIDX_MODE_PERMS_SHIFT
);
65 fileindex_entry_perms_set(struct got_fileindex_entry
*ie
, mode_t mode
)
67 ie
->mode
&= ~GOT_FILEIDX_MODE_PERMS
;
68 ie
->mode
|= ((mode
<< GOT_FILEIDX_MODE_PERMS_SHIFT
) &
69 GOT_FILEIDX_MODE_PERMS
);
73 got_fileindex_perms_to_st(struct got_fileindex_entry
*ie
)
75 mode_t perms
= got_fileindex_entry_perms_get(ie
);
76 int type
= got_fileindex_entry_filetype_get(ie
);
79 if (type
== GOT_FILEIDX_MODE_REGULAR_FILE
||
80 type
== GOT_FILEIDX_MODE_BAD_SYMLINK
)
85 return (ftype
| (perms
& (S_IRWXU
| S_IRWXG
| S_IRWXO
)));
88 const struct got_error
*
89 got_fileindex_entry_update(struct got_fileindex_entry
*ie
,
90 int wt_fd
, const char *ondisk_path
, uint8_t *blob_sha1
,
91 uint8_t *commit_sha1
, int update_timestamps
)
95 if (fstatat(wt_fd
, ondisk_path
, &sb
, AT_SYMLINK_NOFOLLOW
) != 0) {
96 if (!((ie
->flags
& GOT_FILEIDX_F_NO_FILE_ON_DISK
) &&
98 return got_error_from_errno2("fstatat", ondisk_path
);
99 sb
.st_mode
= GOT_DEFAULT_FILE_MODE
;
101 if (sb
.st_mode
& S_IFDIR
)
102 return got_error_set_errno(EISDIR
, ondisk_path
);
103 ie
->flags
&= ~GOT_FILEIDX_F_NO_FILE_ON_DISK
;
106 if ((ie
->flags
& GOT_FILEIDX_F_NO_FILE_ON_DISK
) == 0) {
107 if (update_timestamps
) {
108 ie
->ctime_sec
= sb
.st_ctim
.tv_sec
;
109 ie
->ctime_nsec
= sb
.st_ctim
.tv_nsec
;
110 ie
->mtime_sec
= sb
.st_mtim
.tv_sec
;
111 ie
->mtime_nsec
= sb
.st_mtim
.tv_nsec
;
115 ie
->size
= (sb
.st_size
& 0xffffffff);
116 if (S_ISLNK(sb
.st_mode
)) {
117 got_fileindex_entry_filetype_set(ie
,
118 GOT_FILEIDX_MODE_SYMLINK
);
119 fileindex_entry_perms_set(ie
, 0);
121 got_fileindex_entry_filetype_set(ie
,
122 GOT_FILEIDX_MODE_REGULAR_FILE
);
123 fileindex_entry_perms_set(ie
,
124 sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
));
129 memmove(ie
->blob_sha1
, blob_sha1
, SHA1_DIGEST_LENGTH
);
130 ie
->flags
&= ~GOT_FILEIDX_F_NO_BLOB
;
132 ie
->flags
|= GOT_FILEIDX_F_NO_BLOB
;
135 memcpy(ie
->commit_sha1
, commit_sha1
, SHA1_DIGEST_LENGTH
);
136 ie
->flags
&= ~GOT_FILEIDX_F_NO_COMMIT
;
138 ie
->flags
|= GOT_FILEIDX_F_NO_COMMIT
;
144 got_fileindex_entry_mark_deleted_from_disk(struct got_fileindex_entry
*ie
)
146 ie
->flags
|= GOT_FILEIDX_F_NO_FILE_ON_DISK
;
150 got_fileindex_entry_mark_skipped(struct got_fileindex_entry
*ie
)
152 ie
->flags
|= GOT_FILEIDX_F_SKIPPED
;
155 const struct got_error
*
156 got_fileindex_entry_alloc(struct got_fileindex_entry
**ie
,
161 *ie
= calloc(1, sizeof(**ie
));
163 return got_error_from_errno("calloc");
165 (*ie
)->path
= strdup(relpath
);
166 if ((*ie
)->path
== NULL
) {
167 const struct got_error
*err
= got_error_from_errno("strdup");
173 len
= strlen(relpath
);
174 if (len
> GOT_FILEIDX_F_PATH_LEN
)
175 len
= GOT_FILEIDX_F_PATH_LEN
;
182 got_fileindex_entry_free(struct got_fileindex_entry
*ie
)
189 got_fileindex_entry_path_len(const struct got_fileindex_entry
*ie
)
191 return (size_t)(ie
->flags
& GOT_FILEIDX_F_PATH_LEN
);
195 got_fileindex_entry_stage_get(const struct got_fileindex_entry
*ie
)
197 return ((ie
->flags
& GOT_FILEIDX_F_STAGE
) >> GOT_FILEIDX_F_STAGE_SHIFT
);
201 got_fileindex_entry_stage_set(struct got_fileindex_entry
*ie
, uint32_t stage
)
203 ie
->flags
&= ~GOT_FILEIDX_F_STAGE
;
204 ie
->flags
|= ((stage
<< GOT_FILEIDX_F_STAGE_SHIFT
) &
205 GOT_FILEIDX_F_STAGE
);
209 got_fileindex_entry_filetype_get(struct got_fileindex_entry
*ie
)
211 return (ie
->mode
& GOT_FILEIDX_MODE_FILE_TYPE_ONDISK
);
215 got_fileindex_entry_filetype_set(struct got_fileindex_entry
*ie
, int type
)
217 ie
->mode
&= ~GOT_FILEIDX_MODE_FILE_TYPE_ONDISK
;
218 ie
->mode
|= (type
& GOT_FILEIDX_MODE_FILE_TYPE_ONDISK
);
222 got_fileindex_entry_staged_filetype_set(struct got_fileindex_entry
*ie
,
225 ie
->mode
&= ~GOT_FILEIDX_MODE_FILE_TYPE_STAGED
;
226 ie
->mode
|= ((type
<< GOT_FILEIDX_MODE_FILE_TYPE_STAGED_SHIFT
) &
227 GOT_FILEIDX_MODE_FILE_TYPE_STAGED
);
231 got_fileindex_entry_staged_filetype_get(struct got_fileindex_entry
*ie
)
233 return (ie
->mode
& GOT_FILEIDX_MODE_FILE_TYPE_STAGED
) >>
234 GOT_FILEIDX_MODE_FILE_TYPE_STAGED_SHIFT
;
238 got_fileindex_entry_has_blob(struct got_fileindex_entry
*ie
)
240 return (ie
->flags
& GOT_FILEIDX_F_NO_BLOB
) == 0;
244 got_fileindex_entry_has_commit(struct got_fileindex_entry
*ie
)
246 return (ie
->flags
& GOT_FILEIDX_F_NO_COMMIT
) == 0;
250 got_fileindex_entry_has_file_on_disk(struct got_fileindex_entry
*ie
)
252 return (ie
->flags
& GOT_FILEIDX_F_NO_FILE_ON_DISK
) == 0;
256 got_fileindex_entry_was_skipped(struct got_fileindex_entry
*ie
)
258 return (ie
->flags
& GOT_FILEIDX_F_SKIPPED
) != 0;
261 static const struct got_error
*
262 add_entry(struct got_fileindex
*fileindex
, struct got_fileindex_entry
*ie
)
264 if (fileindex
->nentries
>= GOT_FILEIDX_MAX_ENTRIES
)
265 return got_error(GOT_ERR_NO_SPACE
);
267 if (RB_INSERT(got_fileindex_tree
, &fileindex
->entries
, ie
) != NULL
)
268 return got_error_path(ie
->path
, GOT_ERR_FILEIDX_DUP_ENTRY
);
270 fileindex
->nentries
++;
274 const struct got_error
*
275 got_fileindex_entry_add(struct got_fileindex
*fileindex
,
276 struct got_fileindex_entry
*ie
)
278 /* Flag this entry until it gets written out to disk. */
279 ie
->flags
|= GOT_FILEIDX_F_NOT_FLUSHED
;
281 return add_entry(fileindex
, ie
);
285 got_fileindex_entry_remove(struct got_fileindex
*fileindex
,
286 struct got_fileindex_entry
*ie
)
289 * Removing an entry from the RB tree immediately breaks
290 * in-progress iterations over file index entries.
291 * So flag this entry for removal and remove it once the index
292 * is written out to disk. Meanwhile, pretend this entry no longer
293 * exists if we get queried for it again before then.
295 ie
->flags
|= GOT_FILEIDX_F_REMOVE_ON_FLUSH
;
296 fileindex
->nentries
--;
299 struct got_fileindex_entry
*
300 got_fileindex_entry_get(struct got_fileindex
*fileindex
, const char *path
,
303 struct got_fileindex_entry
*ie
;
304 struct got_fileindex_entry key
;
305 memset(&key
, 0, sizeof(key
));
306 key
.path
= (char *)path
;
307 key
.flags
= (path_len
& GOT_FILEIDX_F_PATH_LEN
);
308 ie
= RB_FIND(got_fileindex_tree
, &fileindex
->entries
, &key
);
309 if (ie
&& (ie
->flags
& GOT_FILEIDX_F_REMOVE_ON_FLUSH
))
314 const struct got_error
*
315 got_fileindex_for_each_entry_safe(struct got_fileindex
*fileindex
,
316 got_fileindex_cb cb
, void *cb_arg
)
318 const struct got_error
*err
;
319 struct got_fileindex_entry
*ie
, *tmp
;
321 RB_FOREACH_SAFE(ie
, got_fileindex_tree
, &fileindex
->entries
, tmp
) {
322 if (ie
->flags
& GOT_FILEIDX_F_REMOVE_ON_FLUSH
)
324 err
= (*cb
)(cb_arg
, ie
);
331 struct got_fileindex
*
332 got_fileindex_alloc(void)
334 struct got_fileindex
*fileindex
;
336 fileindex
= calloc(1, sizeof(*fileindex
));
337 if (fileindex
== NULL
)
340 RB_INIT(&fileindex
->entries
);
345 got_fileindex_free(struct got_fileindex
*fileindex
)
347 struct got_fileindex_entry
*ie
;
349 while ((ie
= RB_MIN(got_fileindex_tree
, &fileindex
->entries
))) {
350 RB_REMOVE(got_fileindex_tree
, &fileindex
->entries
, ie
);
351 got_fileindex_entry_free(ie
);
356 static const struct got_error
*
357 write_fileindex_val64(struct got_hash
*ctx
, uint64_t val
, FILE *outfile
)
362 got_hash_update(ctx
, &val
, sizeof(val
));
363 n
= fwrite(&val
, 1, sizeof(val
), outfile
);
364 if (n
!= sizeof(val
))
365 return got_ferror(outfile
, GOT_ERR_IO
);
369 static const struct got_error
*
370 write_fileindex_val32(struct got_hash
*ctx
, uint32_t val
, FILE *outfile
)
375 got_hash_update(ctx
, &val
, sizeof(val
));
376 n
= fwrite(&val
, 1, sizeof(val
), outfile
);
377 if (n
!= sizeof(val
))
378 return got_ferror(outfile
, GOT_ERR_IO
);
382 static const struct got_error
*
383 write_fileindex_val16(struct got_hash
*ctx
, uint16_t val
, FILE *outfile
)
388 got_hash_update(ctx
, &val
, sizeof(val
));
389 n
= fwrite(&val
, 1, sizeof(val
), outfile
);
390 if (n
!= sizeof(val
))
391 return got_ferror(outfile
, GOT_ERR_IO
);
395 static const struct got_error
*
396 write_fileindex_path(struct got_hash
*ctx
, const char *path
, FILE *outfile
)
398 size_t n
, len
, pad
= 0;
399 static const uint8_t zero
[8] = { 0 };
402 while ((len
+ pad
) % 8 != 0)
405 pad
= 8; /* NUL-terminate */
407 got_hash_update(ctx
, path
, len
);
408 n
= fwrite(path
, 1, len
, outfile
);
410 return got_ferror(outfile
, GOT_ERR_IO
);
411 got_hash_update(ctx
, zero
, pad
);
412 n
= fwrite(zero
, 1, pad
, outfile
);
414 return got_ferror(outfile
, GOT_ERR_IO
);
418 static const struct got_error
*
419 write_fileindex_entry(struct got_hash
*ctx
, struct got_fileindex_entry
*ie
,
422 const struct got_error
*err
;
426 err
= write_fileindex_val64(ctx
, ie
->ctime_sec
, outfile
);
429 err
= write_fileindex_val64(ctx
, ie
->ctime_nsec
, outfile
);
432 err
= write_fileindex_val64(ctx
, ie
->mtime_sec
, outfile
);
435 err
= write_fileindex_val64(ctx
, ie
->mtime_nsec
, outfile
);
439 err
= write_fileindex_val32(ctx
, ie
->uid
, outfile
);
442 err
= write_fileindex_val32(ctx
, ie
->gid
, outfile
);
445 err
= write_fileindex_val32(ctx
, ie
->size
, outfile
);
449 err
= write_fileindex_val16(ctx
, ie
->mode
, outfile
);
453 got_hash_update(ctx
, ie
->blob_sha1
, SHA1_DIGEST_LENGTH
);
454 n
= fwrite(ie
->blob_sha1
, 1, SHA1_DIGEST_LENGTH
, outfile
);
455 if (n
!= SHA1_DIGEST_LENGTH
)
456 return got_ferror(outfile
, GOT_ERR_IO
);
458 got_hash_update(ctx
, ie
->commit_sha1
, SHA1_DIGEST_LENGTH
);
459 n
= fwrite(ie
->commit_sha1
, 1, SHA1_DIGEST_LENGTH
, outfile
);
460 if (n
!= SHA1_DIGEST_LENGTH
)
461 return got_ferror(outfile
, GOT_ERR_IO
);
463 err
= write_fileindex_val32(ctx
, ie
->flags
, outfile
);
467 err
= write_fileindex_path(ctx
, ie
->path
, outfile
);
471 stage
= got_fileindex_entry_stage_get(ie
);
472 if (stage
== GOT_FILEIDX_STAGE_MODIFY
||
473 stage
== GOT_FILEIDX_STAGE_ADD
) {
474 got_hash_update(ctx
, ie
->staged_blob_sha1
, SHA1_DIGEST_LENGTH
);
475 n
= fwrite(ie
->staged_blob_sha1
, 1, SHA1_DIGEST_LENGTH
,
477 if (n
!= SHA1_DIGEST_LENGTH
)
478 return got_ferror(outfile
, GOT_ERR_IO
);
484 const struct got_error
*
485 got_fileindex_write(struct got_fileindex
*fileindex
, FILE *outfile
)
487 const struct got_error
*err
= NULL
;
488 struct got_fileindex_hdr hdr
;
490 uint8_t hash
[GOT_HASH_DIGEST_MAXLEN
];
492 struct got_fileindex_entry
*ie
, *tmp
;
494 got_hash_init(&ctx
, GOT_HASH_SHA1
);
496 hdr
.signature
= htobe32(GOT_FILE_INDEX_SIGNATURE
);
497 hdr
.version
= htobe32(GOT_FILE_INDEX_VERSION
);
498 hdr
.nentries
= htobe32(fileindex
->nentries
);
500 got_hash_update(&ctx
, &hdr
.signature
, sizeof(hdr
.signature
));
501 got_hash_update(&ctx
, &hdr
.version
, sizeof(hdr
.version
));
502 got_hash_update(&ctx
, &hdr
.nentries
, sizeof(hdr
.nentries
));
503 n
= fwrite(&hdr
.signature
, 1, sizeof(hdr
.signature
), outfile
);
504 if (n
!= sizeof(hdr
.signature
))
505 return got_ferror(outfile
, GOT_ERR_IO
);
506 n
= fwrite(&hdr
.version
, 1, sizeof(hdr
.version
), outfile
);
507 if (n
!= sizeof(hdr
.version
))
508 return got_ferror(outfile
, GOT_ERR_IO
);
509 n
= fwrite(&hdr
.nentries
, 1, sizeof(hdr
.nentries
), outfile
);
510 if (n
!= sizeof(hdr
.nentries
))
511 return got_ferror(outfile
, GOT_ERR_IO
);
513 RB_FOREACH_SAFE(ie
, got_fileindex_tree
, &fileindex
->entries
, tmp
) {
514 ie
->flags
&= ~GOT_FILEIDX_F_NOT_FLUSHED
;
515 ie
->flags
&= ~GOT_FILEIDX_F_SKIPPED
;
516 if (ie
->flags
& GOT_FILEIDX_F_REMOVE_ON_FLUSH
) {
517 RB_REMOVE(got_fileindex_tree
, &fileindex
->entries
, ie
);
518 got_fileindex_entry_free(ie
);
521 err
= write_fileindex_entry(&ctx
, ie
, outfile
);
526 got_hash_final(&ctx
, hash
);
527 n
= fwrite(hash
, 1, SHA1_DIGEST_LENGTH
, outfile
);
528 if (n
!= SHA1_DIGEST_LENGTH
)
529 return got_ferror(outfile
, GOT_ERR_IO
);
531 if (fflush(outfile
) != 0)
532 return got_error_from_errno("fflush");
537 static const struct got_error
*
538 read_fileindex_val64(uint64_t *val
, struct got_hash
*ctx
, FILE *infile
)
542 n
= fread(val
, 1, sizeof(*val
), infile
);
543 if (n
!= sizeof(*val
))
544 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
545 got_hash_update(ctx
, val
, sizeof(*val
));
546 *val
= be64toh(*val
);
550 static const struct got_error
*
551 read_fileindex_val32(uint32_t *val
, struct got_hash
*ctx
, FILE *infile
)
555 n
= fread(val
, 1, sizeof(*val
), infile
);
556 if (n
!= sizeof(*val
))
557 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
558 got_hash_update(ctx
, val
, sizeof(*val
));
559 *val
= be32toh(*val
);
563 static const struct got_error
*
564 read_fileindex_val16(uint16_t *val
, struct got_hash
*ctx
, FILE *infile
)
568 n
= fread(val
, 1, sizeof(*val
), infile
);
569 if (n
!= sizeof(*val
))
570 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
571 got_hash_update(ctx
, val
, sizeof(*val
));
572 *val
= be16toh(*val
);
576 static const struct got_error
*
577 read_fileindex_path(char **path
, struct got_hash
*ctx
, FILE *infile
)
579 const size_t chunk_size
= 8;
584 if (len
+ chunk_size
> sizeof(p
))
585 return got_error(GOT_ERR_FILEIDX_BAD
);
587 n
= fread(&p
[len
], 1, chunk_size
, infile
);
589 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
591 got_hash_update(ctx
, &p
[len
], chunk_size
);
593 } while (memchr(&p
[len
- chunk_size
], '\0', chunk_size
) == NULL
);
597 return got_error_from_errno("strdup");
601 static const struct got_error
*
602 read_fileindex_entry(struct got_fileindex_entry
**iep
, struct got_hash
*ctx
,
603 FILE *infile
, uint32_t version
)
605 const struct got_error
*err
;
606 struct got_fileindex_entry
*ie
;
611 ie
= calloc(1, sizeof(*ie
));
613 return got_error_from_errno("calloc");
615 err
= read_fileindex_val64(&ie
->ctime_sec
, ctx
, infile
);
618 err
= read_fileindex_val64(&ie
->ctime_nsec
, ctx
, infile
);
621 err
= read_fileindex_val64(&ie
->mtime_sec
, ctx
, infile
);
624 err
= read_fileindex_val64(&ie
->mtime_nsec
, ctx
, infile
);
628 err
= read_fileindex_val32(&ie
->uid
, ctx
, infile
);
631 err
= read_fileindex_val32(&ie
->gid
, ctx
, infile
);
634 err
= read_fileindex_val32(&ie
->size
, ctx
, infile
);
638 err
= read_fileindex_val16(&ie
->mode
, ctx
, infile
);
642 n
= fread(ie
->blob_sha1
, 1, SHA1_DIGEST_LENGTH
, infile
);
643 if (n
!= SHA1_DIGEST_LENGTH
) {
644 err
= got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
647 got_hash_update(ctx
, ie
->blob_sha1
, SHA1_DIGEST_LENGTH
);
649 n
= fread(ie
->commit_sha1
, 1, SHA1_DIGEST_LENGTH
, infile
);
650 if (n
!= SHA1_DIGEST_LENGTH
) {
651 err
= got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
654 got_hash_update(ctx
, ie
->commit_sha1
, SHA1_DIGEST_LENGTH
);
656 err
= read_fileindex_val32(&ie
->flags
, ctx
, infile
);
660 err
= read_fileindex_path(&ie
->path
, ctx
, infile
);
665 uint32_t stage
= got_fileindex_entry_stage_get(ie
);
666 if (stage
== GOT_FILEIDX_STAGE_MODIFY
||
667 stage
== GOT_FILEIDX_STAGE_ADD
) {
668 n
= fread(ie
->staged_blob_sha1
, 1, SHA1_DIGEST_LENGTH
,
670 if (n
!= SHA1_DIGEST_LENGTH
) {
671 err
= got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
674 got_hash_update(ctx
, ie
->staged_blob_sha1
,
678 /* GOT_FILE_INDEX_VERSION 1 does not support staging. */
679 ie
->flags
&= ~GOT_FILEIDX_F_STAGE
;
684 got_fileindex_entry_free(ie
);
690 const struct got_error
*
691 got_fileindex_read(struct got_fileindex
*fileindex
, FILE *infile
)
693 const struct got_error
*err
= NULL
;
694 struct got_fileindex_hdr hdr
;
696 struct got_fileindex_entry
*ie
;
697 uint8_t sha1_expected
[SHA1_DIGEST_LENGTH
];
698 uint8_t sha1
[SHA1_DIGEST_LENGTH
];
702 got_hash_init(&ctx
, GOT_HASH_SHA1
);
704 n
= fread(&hdr
.signature
, 1, sizeof(hdr
.signature
), infile
);
705 if (n
!= sizeof(hdr
.signature
)) {
706 if (n
== 0) /* EOF */
708 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
710 n
= fread(&hdr
.version
, 1, sizeof(hdr
.version
), infile
);
711 if (n
!= sizeof(hdr
.version
)) {
712 if (n
== 0) /* EOF */
714 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
716 n
= fread(&hdr
.nentries
, 1, sizeof(hdr
.nentries
), infile
);
717 if (n
!= sizeof(hdr
.nentries
)) {
718 if (n
== 0) /* EOF */
720 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
723 got_hash_update(&ctx
, &hdr
.signature
, sizeof(hdr
.signature
));
724 got_hash_update(&ctx
, &hdr
.version
, sizeof(hdr
.version
));
725 got_hash_update(&ctx
, &hdr
.nentries
, sizeof(hdr
.nentries
));
727 hdr
.signature
= be32toh(hdr
.signature
);
728 hdr
.version
= be32toh(hdr
.version
);
729 hdr
.nentries
= be32toh(hdr
.nentries
);
731 if (hdr
.signature
!= GOT_FILE_INDEX_SIGNATURE
)
732 return got_error(GOT_ERR_FILEIDX_SIG
);
733 if (hdr
.version
> GOT_FILE_INDEX_VERSION
)
734 return got_error(GOT_ERR_FILEIDX_VER
);
736 for (i
= 0; i
< hdr
.nentries
; i
++) {
737 err
= read_fileindex_entry(&ie
, &ctx
, infile
, hdr
.version
);
740 err
= add_entry(fileindex
, ie
);
742 got_fileindex_entry_free(ie
);
747 n
= fread(sha1_expected
, 1, sizeof(sha1_expected
), infile
);
748 if (n
!= sizeof(sha1_expected
))
749 return got_ferror(infile
, GOT_ERR_FILEIDX_BAD
);
750 got_hash_final(&ctx
, sha1
);
751 if (memcmp(sha1
, sha1_expected
, SHA1_DIGEST_LENGTH
) != 0)
752 return got_error(GOT_ERR_FILEIDX_CSUM
);
757 static struct got_fileindex_entry
*
758 walk_fileindex(struct got_fileindex
*fileindex
, struct got_fileindex_entry
*ie
)
760 struct got_fileindex_entry
*next
;
762 next
= RB_NEXT(got_fileindex_tree
, &fileindex
->entries
, ie
);
764 /* Skip entries which were added or removed by diff callbacks. */
765 while (next
&& (next
->flags
& (GOT_FILEIDX_F_NOT_FLUSHED
|
766 GOT_FILEIDX_F_REMOVE_ON_FLUSH
)))
767 next
= RB_NEXT(got_fileindex_tree
, &fileindex
->entries
, next
);
772 static const struct got_error
*
773 diff_fileindex_tree(struct got_fileindex
*, struct got_fileindex_entry
**ie
,
774 struct got_tree_object
*tree
, const char *, const char *,
775 struct got_repository
*, struct got_fileindex_diff_tree_cb
*, void *);
777 static const struct got_error
*
778 walk_tree(struct got_tree_entry
**next
, struct got_fileindex
*fileindex
,
779 struct got_fileindex_entry
**ie
, struct got_tree_object
*tree
, int *tidx
,
780 const char *path
, const char *entry_name
, struct got_repository
*repo
,
781 struct got_fileindex_diff_tree_cb
*cb
, void *cb_arg
)
783 const struct got_error
*err
= NULL
;
784 struct got_tree_entry
*te
= got_object_tree_get_entry(tree
, *tidx
);
786 if (!got_object_tree_entry_is_submodule(te
) &&
787 S_ISDIR(got_tree_entry_get_mode(te
))) {
789 struct got_tree_object
*subtree
;
791 if (asprintf(&subpath
, "%s%s%s", path
,
792 path
[0] == '\0' ? "" : "/",
793 got_tree_entry_get_name(te
)) == -1)
794 return got_error_from_errno("asprintf");
796 err
= got_object_open_as_tree(&subtree
, repo
,
797 got_tree_entry_get_id(te
));
803 err
= diff_fileindex_tree(fileindex
, ie
, subtree
, subpath
,
804 entry_name
, repo
, cb
, cb_arg
);
806 got_object_tree_close(subtree
);
812 *next
= got_object_tree_get_entry(tree
, *tidx
);
816 static const struct got_error
*
817 diff_fileindex_tree(struct got_fileindex
*fileindex
,
818 struct got_fileindex_entry
**ie
, struct got_tree_object
*tree
,
819 const char *path
, const char *entry_name
, struct got_repository
*repo
,
820 struct got_fileindex_diff_tree_cb
*cb
, void *cb_arg
)
822 const struct got_error
*err
= NULL
;
823 struct got_tree_entry
*te
= NULL
;
824 size_t path_len
= strlen(path
);
825 struct got_fileindex_entry
*next
;
828 te
= got_object_tree_get_entry(tree
, tidx
);
829 while ((*ie
&& got_path_is_child((*ie
)->path
, path
, path_len
)) || te
) {
832 const char *te_name
= got_tree_entry_get_name(te
);
834 if (asprintf(&te_path
, "%s/%s", path
, te_name
) == -1) {
835 err
= got_error_from_errno("asprintf");
838 cmp
= got_path_cmp((*ie
)->path
, te_path
,
839 got_fileindex_entry_path_len(*ie
), strlen(te_path
));
842 if (got_path_is_child((*ie
)->path
, path
,
844 !got_object_tree_entry_is_submodule(te
) &&
845 (entry_name
== NULL
||
846 strcmp(te_name
, entry_name
) == 0)) {
847 err
= cb
->diff_old_new(cb_arg
, *ie
, te
,
849 if (err
|| entry_name
)
852 *ie
= walk_fileindex(fileindex
, *ie
);
853 err
= walk_tree(&te
, fileindex
, ie
, tree
, &tidx
,
854 path
, entry_name
, repo
, cb
, cb_arg
);
855 } else if (cmp
< 0) {
856 next
= walk_fileindex(fileindex
, *ie
);
857 if (got_path_is_child((*ie
)->path
, path
,
858 path_len
) && entry_name
== NULL
) {
859 err
= cb
->diff_old(cb_arg
, *ie
, path
);
860 if (err
|| entry_name
)
865 if ((entry_name
== NULL
||
866 strcmp(te_name
, entry_name
) == 0)) {
867 err
= cb
->diff_new(cb_arg
, te
, path
);
868 if (err
|| entry_name
)
871 err
= walk_tree(&te
, fileindex
, ie
, tree
, &tidx
,
872 path
, entry_name
, repo
, cb
, cb_arg
);
877 next
= walk_fileindex(fileindex
, *ie
);
878 if (got_path_is_child((*ie
)->path
, path
, path_len
) &&
879 (entry_name
== NULL
||
880 (te
&& strcmp(got_tree_entry_get_name(te
),
881 entry_name
) == 0))) {
882 err
= cb
->diff_old(cb_arg
, *ie
, path
);
883 if (err
|| entry_name
)
888 if (!got_object_tree_entry_is_submodule(te
) &&
889 (entry_name
== NULL
||
890 strcmp(got_tree_entry_get_name(te
), entry_name
)
892 err
= cb
->diff_new(cb_arg
, te
, path
);
893 if (err
|| entry_name
)
896 err
= walk_tree(&te
, fileindex
, ie
, tree
, &tidx
, path
,
897 entry_name
, repo
, cb
, cb_arg
);
906 const struct got_error
*
907 got_fileindex_diff_tree(struct got_fileindex
*fileindex
,
908 struct got_tree_object
*tree
, const char *path
, const char *entry_name
,
909 struct got_repository
*repo
,
910 struct got_fileindex_diff_tree_cb
*cb
, void *cb_arg
)
912 struct got_fileindex_entry
*ie
;
913 ie
= RB_MIN(got_fileindex_tree
, &fileindex
->entries
);
914 while (ie
&& !got_path_is_child(ie
->path
, path
, strlen(path
)))
915 ie
= walk_fileindex(fileindex
, ie
);
916 return diff_fileindex_tree(fileindex
, &ie
, tree
, path
, entry_name
, repo
,
920 static const struct got_error
*
921 diff_fileindex_dir(struct got_fileindex
*, struct got_fileindex_entry
**,
922 struct got_pathlist_head
*, int, const char *, const char *,
923 struct got_repository
*, struct got_fileindex_diff_dir_cb
*, void *);
925 static const struct got_error
*
926 read_dirlist(struct got_pathlist_head
*dirlist
, DIR *dir
, const char *path
)
928 const struct got_error
*err
= NULL
;
929 struct got_pathlist_entry
*new = NULL
;
930 struct dirent
*dep
= NULL
;
931 struct dirent
*de
= NULL
;
934 de
= malloc(sizeof(struct dirent
) + NAME_MAX
+ 1);
936 err
= got_error_from_errno("malloc");
940 if (readdir_r(dir
, de
, &dep
) != 0) {
941 err
= got_error_from_errno("readdir_r");
950 if (strcmp(de
->d_name
, ".") == 0 ||
951 strcmp(de
->d_name
, "..") == 0 ||
953 strcmp(de
->d_name
, GOT_WORKTREE_GOT_DIR
) == 0) ||
955 strcmp(de
->d_name
, GOT_WORKTREE_CVG_DIR
) == 0)) {
960 err
= got_pathlist_insert(&new, dirlist
, de
->d_name
, de
);
966 err
= got_error(GOT_ERR_DIR_DUP_ENTRY
);
976 have_tracked_file_in_dir(struct got_fileindex
*fileindex
, const char *path
)
978 struct got_fileindex_entry
*ie
;
979 size_t path_len
= strlen(path
);
982 ie
= RB_ROOT(&fileindex
->entries
);
984 if (got_path_is_child(ie
->path
, path
, path_len
))
986 cmp
= got_path_cmp(path
, ie
->path
, path_len
,
987 got_fileindex_entry_path_len(ie
));
989 ie
= RB_LEFT(ie
, entry
);
991 ie
= RB_RIGHT(ie
, entry
);
999 static const struct got_error
*
1000 walk_dir(struct got_pathlist_entry
**next
, struct got_fileindex
*fileindex
,
1001 struct got_fileindex_entry
**ie
, struct got_pathlist_entry
*dle
, int fd
,
1002 const char *path
, const char *rootpath
, struct got_repository
*repo
,
1003 int ignore
, struct got_fileindex_diff_dir_cb
*cb
, void *cb_arg
)
1005 const struct got_error
*err
= NULL
;
1006 struct dirent
*de
= dle
->data
;
1012 /* Must traverse ignored directories if they contain tracked files. */
1013 if (de
->d_type
== DT_DIR
&& ignore
&&
1014 have_tracked_file_in_dir(fileindex
, path
))
1017 if (de
->d_type
== DT_DIR
&& !ignore
) {
1020 struct got_pathlist_head subdirlist
;
1022 TAILQ_INIT(&subdirlist
);
1024 if (asprintf(&subpath
, "%s%s%s", path
,
1025 path
[0] == '\0' ? "" : "/", de
->d_name
) == -1)
1026 return got_error_from_errno("asprintf");
1028 if (asprintf(&subdirpath
, "%s/%s", rootpath
, subpath
) == -1) {
1030 return got_error_from_errno("asprintf");
1033 subdirfd
= openat(fd
, de
->d_name
,
1034 O_RDONLY
| O_NOFOLLOW
| O_DIRECTORY
| O_CLOEXEC
);
1035 if (subdirfd
== -1) {
1036 if (errno
== EACCES
) {
1037 *next
= TAILQ_NEXT(dle
, entry
);
1040 err
= got_error_from_errno2("openat", subdirpath
);
1046 subdir
= fdopendir(subdirfd
);
1048 return got_error_from_errno2("fdopendir", path
);
1050 err
= read_dirlist(&subdirlist
, subdir
, subdirpath
);
1057 err
= diff_fileindex_dir(fileindex
, ie
, &subdirlist
,
1058 dirfd(subdir
), rootpath
, subpath
, repo
, cb
, cb_arg
);
1059 if (subdir
&& closedir(subdir
) == -1 && err
== NULL
)
1060 err
= got_error_from_errno2("closedir", subdirpath
);
1063 got_pathlist_free(&subdirlist
, GOT_PATHLIST_FREE_DATA
);
1068 *next
= TAILQ_NEXT(dle
, entry
);
1072 static const struct got_error
*
1073 dirent_type_fixup(struct dirent
*de
, const char *rootpath
, const char *path
)
1075 const struct got_error
*err
;
1079 if (de
->d_type
!= DT_UNKNOWN
)
1082 /* DT_UNKNOWN occurs on NFS mounts without "readdir plus" RPC. */
1083 if (asprintf(&dir_path
, "%s/%s", rootpath
, path
) == -1)
1084 return got_error_from_errno("asprintf");
1085 err
= got_path_dirent_type(&type
, dir_path
, de
);
1094 static const struct got_error
*
1095 diff_fileindex_dir(struct got_fileindex
*fileindex
,
1096 struct got_fileindex_entry
**ie
, struct got_pathlist_head
*dirlist
,
1097 int dirfd
, const char *rootpath
, const char *path
,
1098 struct got_repository
*repo
,
1099 struct got_fileindex_diff_dir_cb
*cb
, void *cb_arg
)
1101 const struct got_error
*err
= NULL
;
1102 struct dirent
*de
= NULL
;
1103 size_t path_len
= strlen(path
);
1104 struct got_pathlist_entry
*dle
;
1107 if (cb
->diff_traverse
) {
1108 err
= cb
->diff_traverse(cb_arg
, path
, dirfd
);
1113 dle
= TAILQ_FIRST(dirlist
);
1114 while ((*ie
&& got_path_is_child((*ie
)->path
, path
, path_len
)) || dle
) {
1119 err
= dirent_type_fixup(de
, rootpath
, path
);
1122 if (asprintf(&de_path
, "%s/%s", path
,
1123 de
->d_name
) == -1) {
1124 err
= got_error_from_errno("asprintf");
1127 cmp
= got_path_cmp((*ie
)->path
, de_path
,
1128 got_fileindex_entry_path_len(*ie
),
1129 strlen(path
) + 1 + strlen(de
->d_name
));
1132 err
= cb
->diff_old_new(cb_arg
, *ie
, de
, path
,
1136 *ie
= walk_fileindex(fileindex
, *ie
);
1137 err
= walk_dir(&dle
, fileindex
, ie
, dle
, dirfd
,
1138 path
, rootpath
, repo
, 0, cb
, cb_arg
);
1139 } else if (cmp
< 0 ) {
1140 err
= cb
->diff_old(cb_arg
, *ie
, path
);
1143 *ie
= walk_fileindex(fileindex
, *ie
);
1145 err
= cb
->diff_new(&ignore
, cb_arg
, de
, path
,
1149 err
= walk_dir(&dle
, fileindex
, ie
, dle
, dirfd
,
1150 path
, rootpath
, repo
, ignore
, cb
, cb_arg
);
1155 err
= cb
->diff_old(cb_arg
, *ie
, path
);
1158 *ie
= walk_fileindex(fileindex
, *ie
);
1161 err
= dirent_type_fixup(de
, rootpath
, path
);
1164 err
= cb
->diff_new(&ignore
, cb_arg
, de
, path
, dirfd
);
1167 err
= walk_dir(&dle
, fileindex
, ie
, dle
, dirfd
, path
,
1168 rootpath
, repo
, ignore
, cb
, cb_arg
);
1177 const struct got_error
*
1178 got_fileindex_diff_dir(struct got_fileindex
*fileindex
, int fd
,
1179 const char *rootpath
, const char *path
, struct got_repository
*repo
,
1180 struct got_fileindex_diff_dir_cb
*cb
, void *cb_arg
)
1182 const struct got_error
*err
;
1183 struct got_fileindex_entry
*ie
;
1184 struct got_pathlist_head dirlist
;
1188 TAILQ_INIT(&dirlist
);
1191 * Duplicate the file descriptor so we can call closedir() below
1192 * without closing the file descriptor passed in by our caller.
1196 return got_error_from_errno2("dup", path
);
1197 if (lseek(fd2
, 0, SEEK_SET
) == -1) {
1198 err
= got_error_from_errno2("lseek", path
);
1202 dir
= fdopendir(fd2
);
1204 err
= got_error_from_errno2("fdopendir", path
);
1208 err
= read_dirlist(&dirlist
, dir
, path
);
1214 ie
= RB_MIN(got_fileindex_tree
, &fileindex
->entries
);
1215 while (ie
&& !got_path_is_child(ie
->path
, path
, strlen(path
)))
1216 ie
= walk_fileindex(fileindex
, ie
);
1217 err
= diff_fileindex_dir(fileindex
, &ie
, &dirlist
, dirfd(dir
),
1218 rootpath
, path
, repo
, cb
, cb_arg
);
1220 if (closedir(dir
) == -1 && err
== NULL
)
1221 err
= got_error_from_errno2("closedir", path
);
1222 got_pathlist_free(&dirlist
, GOT_PATHLIST_FREE_DATA
);
1226 struct got_object_id
*
1227 got_fileindex_entry_get_staged_blob_id(struct got_object_id
*id
,
1228 struct got_fileindex_entry
*ie
)
1230 memset(id
, 0, sizeof(*id
));
1231 memcpy(id
->sha1
, ie
->staged_blob_sha1
, sizeof(ie
->staged_blob_sha1
));
1235 struct got_object_id
*
1236 got_fileindex_entry_get_blob_id(struct got_object_id
*id
,
1237 struct got_fileindex_entry
*ie
)
1239 memset(id
, 0, sizeof(*id
));
1240 memcpy(id
->sha1
, ie
->blob_sha1
, sizeof(ie
->blob_sha1
));
1244 struct got_object_id
*
1245 got_fileindex_entry_get_commit_id(struct got_object_id
*id
,
1246 struct got_fileindex_entry
*ie
)
1248 memset(id
, 0, sizeof(*id
));
1249 memcpy(id
->sha1
, ie
->commit_sha1
, sizeof(ie
->commit_sha1
));
1253 RB_GENERATE(got_fileindex_tree
, got_fileindex_entry
, entry
, got_fileindex_cmp
);