commit 2e1fa22206f9dbaca888c89bb9938782cbb4c5a1 from: Stefan Sperling date: Thu Jul 23 14:22:34 2020 UTC give bad symlinks a dedicated file type in the file index commit - b88d214a31981ae2610d930c34d956143b03c523 commit + 2e1fa22206f9dbaca888c89bb9938782cbb4c5a1 blob - f15a1b26fc74d03f67098f4b87ea6be4be5328d6 blob + 13820ca28ed7e43a9699728547d5b98015959174 --- lib/fileindex.c +++ lib/fileindex.c @@ -178,6 +178,29 @@ got_fileindex_entry_stage_set(struct got_fileindex_ent } int +got_fileindex_entry_filetype_get(struct got_fileindex_entry *ie) +{ + return (ie->mode & GOT_FILEIDX_MODE_FILE_TYPE); +} + +const struct got_error * +got_fileindex_entry_filetype_set(struct got_fileindex_entry *ie, int type) +{ + switch (type) { + case GOT_FILEIDX_MODE_REGULAR_FILE: + case GOT_FILEIDX_MODE_SYMLINK: + case GOT_FILEIDX_MODE_BAD_SYMLINK: + break; + default: + return got_error(GOT_ERR_BAD_FILETYPE); + } + + ie->mode &= ~GOT_FILEIDX_MODE_FILE_TYPE; + ie->mode |= type; + return NULL; +} + +int got_fileindex_entry_has_blob(struct got_fileindex_entry *ie) { return (ie->flags & GOT_FILEIDX_F_NO_BLOB) == 0; blob - af6efe8eca81db02b8067b47cf97720194c03e52 blob + 31f1e0475396f6c86cdec1586d990c4ac6a7f374 --- lib/got_lib_fileindex.h +++ lib/got_lib_fileindex.h @@ -39,6 +39,7 @@ struct got_fileindex_entry { #define GOT_FILEIDX_MODE_FILE_TYPE 0x000f #define GOT_FILEIDX_MODE_REGULAR_FILE 1 #define GOT_FILEIDX_MODE_SYMLINK 2 +#define GOT_FILEIDX_MODE_BAD_SYMLINK 3 #define GOT_FILEIDX_MODE_PERMS 0xfff0 #define GOT_FILEIDX_MODE_PERMS_SHIFT 4 @@ -161,5 +162,8 @@ int got_fileindex_entry_has_commit(struct got_fileinde int got_fileindex_entry_has_file_on_disk(struct got_fileindex_entry *); uint32_t got_fileindex_entry_stage_get(const struct got_fileindex_entry *); void got_fileindex_entry_stage_set(struct got_fileindex_entry *ie, uint32_t); +int got_fileindex_entry_filetype_get(struct got_fileindex_entry *); +const struct got_error *got_fileindex_entry_filetype_set( + struct got_fileindex_entry *, int); void got_fileindex_entry_mark_deleted_from_disk(struct got_fileindex_entry *); blob - 7bb6476174e9f1c13193dab9b4a12f2fecb8d3a7 blob + 5abfa0d3ed7655b5c44750c9987fe51452d18035 --- lib/worktree.c +++ lib/worktree.c @@ -1197,10 +1197,10 @@ replace_existing_symlink(const char *ondisk_path, cons } static const struct got_error * -install_symlink(struct got_worktree *worktree, const char *ondisk_path, - const char *path, mode_t te_mode, mode_t st_mode, - struct got_blob_object *blob, int restoring_missing_file, - int reverting_versioned_file, struct got_repository *repo, +install_symlink(int *is_bad_symlink, struct got_worktree *worktree, + const char *ondisk_path, const char *path, struct got_blob_object *blob, + int restoring_missing_file, int reverting_versioned_file, + struct got_repository *repo, got_worktree_checkout_cb progress_cb, void *progress_arg) { const struct got_error *err = NULL; @@ -1211,6 +1211,8 @@ install_symlink(struct got_worktree *worktree, const c const uint8_t *buf = got_object_blob_get_read_buf(blob); size_t hdrlen = got_object_blob_get_hdrlen(blob); + *is_bad_symlink = 0; + /* * Blob object content specifies the target path of the link. * If a symbolic link cannot be installed we instead create @@ -1221,6 +1223,7 @@ install_symlink(struct got_worktree *worktree, const c err = got_object_blob_read_block(&len, blob); if (len + target_len >= sizeof(target_path)) { /* Path too long; install as a regular file. */ + *is_bad_symlink = 1; got_object_blob_rewind(blob); return install_blob(worktree, ondisk_path, path, GOT_DEFAULT_FILE_MODE, GOT_DEFAULT_FILE_MODE, blob, @@ -1271,6 +1274,7 @@ install_symlink(struct got_worktree *worktree, const c abspath : target_path), worktree->root_path, strlen(worktree->root_path))) { /* install as a regular file */ + *is_bad_symlink = 1; got_object_blob_rewind(blob); err = install_blob(worktree, ondisk_path, path, GOT_DEFAULT_FILE_MODE, GOT_DEFAULT_FILE_MODE, blob, @@ -1288,6 +1292,7 @@ install_symlink(struct got_worktree *worktree, const c if (got_path_is_child(resolved_path ? resolved_path : (abspath ? abspath : target_path), path_got, strlen(path_got))) { /* install as a regular file */ + *is_bad_symlink = 1; got_object_blob_rewind(blob); err = install_blob(worktree, ondisk_path, path, GOT_DEFAULT_FILE_MODE, GOT_DEFAULT_FILE_MODE, blob, @@ -1332,6 +1337,7 @@ install_symlink(struct got_worktree *worktree, const c /* Handle errors from first or second creation attempt. */ if (errno == ENAMETOOLONG) { /* bad target path; install as a regular file */ + *is_bad_symlink = 1; got_object_blob_rewind(blob); err = install_blob(worktree, ondisk_path, path, GOT_DEFAULT_FILE_MODE, GOT_DEFAULT_FILE_MODE, blob, @@ -1367,11 +1373,6 @@ install_blob(struct got_worktree *worktree, const char int update = 0; char *tmppath = NULL; - if (S_ISLNK(te_mode)) - return install_symlink(worktree, ondisk_path, path, te_mode, - st_mode, blob, restoring_missing_file, - reverting_versioned_file, repo, progress_cb, progress_arg); - fd = open(ondisk_path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, GOT_DEFAULT_FILE_MODE); if (fd == -1) { @@ -1883,9 +1884,18 @@ update_blob(struct got_worktree *worktree, if (err) goto done; } else { - err = install_blob(worktree, ondisk_path, path, te->mode, - sb.st_mode, blob, status == GOT_STATUS_MISSING, 0, 0, - repo, progress_cb, progress_arg); + int is_bad_symlink = 0; + if (S_ISLNK(te->mode)) { + err = install_symlink(&is_bad_symlink, worktree, + ondisk_path, path, blob, + status == GOT_STATUS_MISSING, 0, repo, + progress_cb, progress_arg); + } else { + err = install_blob(worktree, ondisk_path, path, + te->mode, sb.st_mode, blob, + status == GOT_STATUS_MISSING, 0, 0, repo, + progress_cb, progress_arg); + } if (err) goto done; if (ie) { @@ -1898,6 +1908,15 @@ update_blob(struct got_worktree *worktree, } if (err) goto done; + if (is_bad_symlink) { + if (ie == NULL) + ie = got_fileindex_entry_get(fileindex, path, + strlen(path)); + err = got_fileindex_entry_filetype_set(ie, + GOT_FILEIDX_MODE_BAD_SYMLINK); + if (err) + goto done; + } } got_object_blob_close(blob); done: @@ -2742,10 +2761,17 @@ merge_file_cb(void *arg, struct got_blob_object *blob1 goto done; } } else { + int is_bad_symlink = 0; sb.st_mode = GOT_DEFAULT_FILE_MODE; - err = install_blob(a->worktree, ondisk_path, path2, - mode2, sb.st_mode, blob2, 0, 0, 0, repo, - a->progress_cb, a->progress_arg); + if (S_ISLNK(mode2)) { + err = install_symlink(&is_bad_symlink, + a->worktree, ondisk_path, path2, blob2, 0, + 0, repo, a->progress_cb, a->progress_arg); + } else { + err = install_blob(a->worktree, ondisk_path, path2, + mode2, sb.st_mode, blob2, 0, 0, 0, repo, + a->progress_cb, a->progress_arg); + } if (err) goto done; err = got_fileindex_entry_alloc(&ie, path2); @@ -2762,6 +2788,12 @@ merge_file_cb(void *arg, struct got_blob_object *blob1 got_fileindex_entry_free(ie); goto done; } + if (is_bad_symlink) { + err = got_fileindex_entry_filetype_set(ie, + GOT_FILEIDX_MODE_BAD_SYMLINK); + if (err) + goto done; + } } } done: @@ -4256,10 +4288,19 @@ revert_file(void *arg, unsigned char status, unsigned goto done; } } else { - err = install_blob(a->worktree, ondisk_path, ie->path, - te ? te->mode : GOT_DEFAULT_FILE_MODE, - got_fileindex_perms_to_st(ie), blob, 0, 1, 0, - a->repo, a->progress_cb, a->progress_arg); + int is_bad_symlink = 0; + if (te && S_ISLNK(te->mode)) { + err = install_symlink(&is_bad_symlink, + a->worktree, ondisk_path, ie->path, + blob, 0, 1, a->repo, + a->progress_cb, a->progress_arg); + } else { + err = install_blob(a->worktree, ondisk_path, + ie->path, + te ? te->mode : GOT_DEFAULT_FILE_MODE, + got_fileindex_perms_to_st(ie), blob, 0, 1, 0, + a->repo, a->progress_cb, a->progress_arg); + } if (err) goto done; if (status == GOT_STATUS_DELETE || @@ -4267,6 +4308,12 @@ revert_file(void *arg, unsigned char status, unsigned err = got_fileindex_entry_update(ie, ondisk_path, blob->id.sha1, a->worktree->base_commit_id->sha1, 1); + if (err) + goto done; + } + if (is_bad_symlink) { + err = got_fileindex_entry_filetype_set(ie, + GOT_FILEIDX_MODE_BAD_SYMLINK); if (err) goto done; } @@ -4919,6 +4966,106 @@ done: } static const struct got_error * +reinstall_symlink_after_commit(int *is_bad_symlink, struct got_commitable *ct, + struct got_object_id *new_base_commit_id, struct got_worktree *worktree, + struct got_repository *repo) +{ + const struct got_error *err = NULL; + struct got_blob_object *blob = NULL; + struct got_object_id *tree_id = NULL; + char *tree_path = NULL; + struct got_tree_object *tree = NULL; + struct got_tree_entry *te; + char *entry_name; + + err = got_object_open_as_blob(&blob, repo, ct->blob_id, PATH_MAX); + if (err) + return err; + + err = got_path_dirname(&tree_path, ct->in_repo_path); + if (err) { + if (err->code != GOT_ERR_BAD_PATH) + goto done; + err = got_object_id_by_path(&tree_id, repo, + new_base_commit_id, ""); + if (err) + goto done; + } else { + err = got_object_id_by_path(&tree_id, repo, + new_base_commit_id, tree_path); + if (err) + goto done; + } + + err = got_object_open_as_tree(&tree, repo, tree_id); + if (err) + goto done; + + entry_name = basename(ct->path); + if (entry_name == NULL) { + err = got_error_from_errno2("basename", ct->path); + goto done; + } + + te = got_object_tree_find_entry(tree, entry_name); + if (te == NULL) { + err = got_error_path(ct->path, GOT_ERR_NO_TREE_ENTRY); + goto done; + } + + err = install_symlink(is_bad_symlink, worktree, ct->ondisk_path, + ct->path, blob, 0, 0, repo, NULL, NULL); +done: + if (blob) + got_object_blob_close(blob); + if (tree) + got_object_tree_close(tree); + free(tree_id); + free(tree_path); + return err; +} + +/* + * After comitting a symlink we have a chance to convert "bad" symlinks + * (those which point outside the work tree or into .got) to regular files. + * This way, the post-commit work tree state matches a fresh checkout of + * the tree which was just committed. We also mark such newly committed + * symlinks as "bad" in the work tree's fileindex. + */ +static const struct got_error * +reinstall_symlinks_after_commit(struct got_pathlist_head *commitable_paths, + struct got_object_id *new_base_commit_id, struct got_fileindex *fileindex, + struct got_worktree *worktree, struct got_repository *repo) +{ + const struct got_error *err = NULL; + struct got_pathlist_entry *pe; + + TAILQ_FOREACH(pe, commitable_paths, entry) { + struct got_commitable *ct = pe->data; + struct got_fileindex_entry *ie; + int is_bad_symlink = 0; + + if (!S_ISLNK(get_ct_file_mode(ct))) + continue; + + err = reinstall_symlink_after_commit(&is_bad_symlink, + ct, new_base_commit_id, worktree, repo); + if (err) + break; + ie = got_fileindex_entry_get(fileindex, ct->path, + strlen(ct->path)); + if (ie && is_bad_symlink) { + err = got_fileindex_entry_filetype_set(ie, + GOT_FILEIDX_MODE_BAD_SYMLINK); + if (err) + break; + } + } + + return err; +} + +static const struct got_error * update_fileindex_after_commit(struct got_pathlist_head *commitable_paths, struct got_object_id *new_base_commit_id, struct got_fileindex *fileindex, int have_staged_files) @@ -5074,35 +5221,9 @@ commit_worktree(struct got_object_id **new_commit_id, goto done; } err = got_object_blob_create(&ct->blob_id, ondisk_path, repo); - if (err) { - free(ondisk_path); + free(ondisk_path); + if (err) goto done; - } - - /* - * When comitting a symlink we convert "bad" symlinks (those - * which point outside the work tree or into .got) to regular - * files. This way, the post-commit work tree state matches - * a fresh checkout of the tree which was committed. - */ - if (S_ISLNK(get_ct_file_mode(ct))) { - struct got_blob_object *blob; - err = got_object_open_as_blob(&blob, repo, ct->blob_id, - PATH_MAX); - if (err) { - free(ondisk_path); - goto done; - } - err = install_symlink(worktree, ondisk_path, ct->path, - get_ct_file_mode(ct), GOT_DEFAULT_FILE_MODE, blob, - 0, 0, repo, NULL, NULL); - got_object_blob_close(blob); - if (err) { - free(ondisk_path); - goto done; - } - } - free(ondisk_path); } /* Recursively write new tree objects. */ @@ -5321,6 +5442,10 @@ got_worktree_commit(struct got_object_id **new_commit_ err = update_fileindex_after_commit(&commitable_paths, *new_commit_id, fileindex, have_staged_files); + if (err == NULL) { + err = reinstall_symlinks_after_commit(&commitable_paths, + *new_commit_id, fileindex, worktree, repo); + } sync_err = sync_fileindex(fileindex, fileindex_path); if (sync_err && err == NULL) err = sync_err;