commit c577a9cecea0c94f30a1e650e6e332fb8b43b9f6 from: Stefan Sperling date: Sat Jul 27 09:02:22 2019 UTC fix 'got status' recursion problems commit - a062651c55aa60f0633437e9851dd80b1d713e86 commit + c577a9cecea0c94f30a1e650e6e332fb8b43b9f6 blob - 8ff467458d80b3a0f400b0b4b668affadbc2a12a blob + 0e62e71ac79879c8427c88f483ad9505a3d27f18 --- lib/fileindex.c +++ lib/fileindex.c @@ -759,9 +759,67 @@ got_fileindex_diff_tree(struct got_fileindex *fileinde static const struct got_error * diff_fileindex_dir(struct got_fileindex *, struct got_fileindex_entry **, DIR *, - const char *, const char *, struct got_repository *, - struct got_fileindex_diff_dir_cb *, void *); + struct got_pathlist_head *, const char *, const char *, + struct got_repository *, struct got_fileindex_diff_dir_cb *, void *); + +static const struct got_error * +read_dirlist(struct got_pathlist_head *dirlist, DIR *dir, const char *path) +{ + const struct got_error *err = NULL; + struct got_pathlist_entry *new = NULL; + struct dirent *dep = NULL; + struct dirent *de = NULL; + + for (;;) { + de = malloc(sizeof(struct dirent) + NAME_MAX + 1); + if (de == NULL) { + err = got_error_from_errno("malloc"); + break; + } + + if (readdir_r(dir, de, &dep) != 0) { + err = got_error_from_errno("readdir_r"); + free(de); + break; + } + if (dep == NULL) { + free(de); + break; + } + + if (strcmp(de->d_name, ".") == 0 || + strcmp(de->d_name, "..") == 0 || + (path[0] == '\0' && + strcmp(de->d_name, GOT_WORKTREE_GOT_DIR) == 0)) { + free(de); + continue; + } + + err = got_pathlist_insert(&new, dirlist, de->d_name, de); + if (err) { + free(de); + break; + } + if (new == NULL) { + err = got_error(GOT_ERR_DIR_DUP_ENTRY); + free(de); + break; + } + } + return err; +} + +void +free_dirlist(struct got_pathlist_head *dirlist) +{ + struct got_pathlist_entry *dle; + + TAILQ_FOREACH(dle, dirlist, entry) + free(dle->data); + got_pathlist_free(dirlist); +} + static const struct got_error * walk_dir(struct got_pathlist_entry **next, struct got_fileindex *fileindex, struct got_fileindex_entry **ie, struct got_pathlist_entry *dle, @@ -778,7 +836,10 @@ walk_dir(struct got_pathlist_entry **next, struct got_ char *subpath; char *subdirpath; DIR *subdir; + struct got_pathlist_head subdirlist; + TAILQ_INIT(&subdirlist); + if (asprintf(&subpath, "%s%s%s", path, path[0] == '\0' ? "" : "/", de->d_name) == -1) return got_error_from_errno("asprintf"); @@ -795,11 +856,19 @@ walk_dir(struct got_pathlist_entry **next, struct got_ return got_error_from_errno2("opendir", subdirpath); } - err = diff_fileindex_dir(fileindex, ie, subdir, rootpath, - subpath, repo, cb, cb_arg); + err = read_dirlist(&subdirlist, subdir, subdirpath); + if (err) { + free(subpath); + free(subdirpath); + closedir(subdir); + return err; + } + err = diff_fileindex_dir(fileindex, ie, subdir, &subdirlist, + rootpath, subpath, repo, cb, cb_arg); free(subpath); free(subdirpath); closedir(subdir); + free_dirlist(&subdirlist); if (err) return err; } @@ -810,60 +879,17 @@ walk_dir(struct got_pathlist_entry **next, struct got_ static const struct got_error * diff_fileindex_dir(struct got_fileindex *fileindex, - struct got_fileindex_entry **ie, DIR *dir, const char *rootpath, - const char *path, struct got_repository *repo, - struct got_fileindex_diff_dir_cb *cb, void *cb_arg) + struct got_fileindex_entry **ie, DIR *dir, + struct got_pathlist_head *dirlist, const char *rootpath, const char *path, + struct got_repository *repo, struct got_fileindex_diff_dir_cb *cb, + void *cb_arg) { const struct got_error *err = NULL; struct dirent *de = NULL; size_t path_len = strlen(path); - struct got_fileindex_entry *next; - struct got_pathlist_head dirlist; struct got_pathlist_entry *dle; - TAILQ_INIT(&dirlist); - - for (;;) { - struct got_pathlist_entry *new = NULL; - struct dirent *dep = NULL; - - de = malloc(sizeof(struct dirent) + NAME_MAX + 1); - if (de == NULL) { - err = got_error_from_errno("malloc"); - goto done; - } - - if (readdir_r(dir, de, &dep) != 0) { - err = got_error_from_errno("readdir_r"); - free(de); - goto done; - } - if (dep == NULL) { - free(de); - break; - } - - if (strcmp(de->d_name, ".") == 0 || - strcmp(de->d_name, "..") == 0 || - (path[0] == '\0' && - strcmp(de->d_name, GOT_WORKTREE_GOT_DIR) == 0)) { - free(de); - continue; - } - - err = got_pathlist_insert(&new, &dirlist, de->d_name, de); - if (err) { - free(de); - goto done; - } - if (new == NULL) { - err = got_error(GOT_ERR_DIR_DUP_ENTRY); - free(de); - goto done; - } - } - - dle = TAILQ_FIRST(&dirlist); + dle = TAILQ_FIRST(dirlist); while ((*ie && got_path_is_child((*ie)->path, path, path_len)) || dle) { if (dle && *ie) { char *de_path; @@ -884,11 +910,10 @@ diff_fileindex_dir(struct got_fileindex *fileindex, err = walk_dir(&dle, fileindex, ie, dle, path, dir, rootpath, repo, cb, cb_arg); } else if (cmp < 0 ) { - next = walk_fileindex(fileindex, *ie); err = cb->diff_old(cb_arg, *ie, path); if (err) break; - *ie = next; + *ie = walk_fileindex(fileindex, *ie); } else { err = cb->diff_new(cb_arg, de, path); if (err) @@ -899,11 +924,10 @@ diff_fileindex_dir(struct got_fileindex *fileindex, if (err) break; } else if (*ie) { - next = walk_fileindex(fileindex, *ie); err = cb->diff_old(cb_arg, *ie, path); if (err) break; - *ie = next; + *ie = walk_fileindex(fileindex, *ie); } else if (dle) { de = dle->data; err = cb->diff_new(cb_arg, de, path); @@ -915,10 +939,7 @@ diff_fileindex_dir(struct got_fileindex *fileindex, break; } } -done: - TAILQ_FOREACH(dle, &dirlist, entry) - free(dle->data); - got_pathlist_free(&dirlist); + return err; } @@ -927,10 +948,21 @@ got_fileindex_diff_dir(struct got_fileindex *fileindex const char *rootpath, const char *path, struct got_repository *repo, struct got_fileindex_diff_dir_cb *cb, void *cb_arg) { - struct got_fileindex_entry *min; - min = RB_MIN(got_fileindex_tree, &fileindex->entries); - return diff_fileindex_dir(fileindex, &min, rootdir, rootpath, path, - repo, cb, cb_arg); + const struct got_error *err; + struct got_fileindex_entry *ie; + struct got_pathlist_head dirlist; + + TAILQ_INIT(&dirlist); + err = read_dirlist(&dirlist, rootdir, path); + if (err) + return err; + ie = RB_MIN(got_fileindex_tree, &fileindex->entries); + while (ie && !got_path_is_child(ie->path, path, strlen(path))) + ie = walk_fileindex(fileindex, ie); + err = diff_fileindex_dir(fileindex, &ie, rootdir, &dirlist, rootpath, + path, repo, cb, cb_arg); + free_dirlist(&dirlist); + return err; } RB_GENERATE(got_fileindex_tree, got_fileindex_entry, entry, got_fileindex_cmp); blob - 80edb4d443d90485004348c8ef3b92fa6d592f6e blob + 686a315559b212e3c9a0e54239e92de274a498b4 --- lib/worktree.c +++ lib/worktree.c @@ -2198,7 +2198,7 @@ status_old(void *arg, struct got_fileindex_entry *ie, if (a->cancel_cb && a->cancel_cb(a->cancel_arg)) return got_error(GOT_ERR_CANCELLED); - if (!got_path_is_child(parent_path, a->status_path, a->status_path_len)) + if (!got_path_is_child(ie->path, a->status_path, a->status_path_len)) return NULL; memcpy(blob_id.sha1, ie->blob_sha1, SHA1_DIGEST_LENGTH); @@ -2226,9 +2226,6 @@ status_new(void *arg, struct dirent *de, const char *p /* XXX ignore symlinks for now */ if (de->d_type == DT_LNK) - return NULL; - - if (!got_path_is_child(parent_path, a->status_path, a->status_path_len)) return NULL; if (parent_path[0]) { @@ -2238,8 +2235,9 @@ status_new(void *arg, struct dirent *de, const char *p path = de->d_name; } - err = (*a->status_cb)(a->status_arg, GOT_STATUS_UNVERSIONED, path, - NULL, NULL); + if (got_path_is_child(path, a->status_path, a->status_path_len)) + err = (*a->status_cb)(a->status_arg, GOT_STATUS_UNVERSIONED, + path, NULL, NULL); if (parent_path[0]) free(path); return err; blob - fa78fd826325bfbc31e69d96bf74d4329add0b6b blob + caf49bac02e4804db8079107c17f7b157837f688 --- regress/cmdline/status.sh +++ regress/cmdline/status.sh @@ -383,7 +383,57 @@ function test_status_shows_conflict { echo 'C numbers' > $testroot/stdout.expected (cd $testroot/wt && got status > $testroot/stdout) + + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + fi + test_done "$testroot" "$ret" +} + +function test_status_empty_dir { + local testroot=`test_init status_empty_dir` + + got checkout $testroot/repo $testroot/wt > /dev/null + ret="$?" + if [ "$ret" != "0" ]; then + test_done "$testroot" "$ret" + return 1 + fi + + rm $testroot/wt/epsilon/zeta + + echo '! epsilon/zeta' > $testroot/stdout.expected + + (cd $testroot/wt && got status epsilon > $testroot/stdout) + + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + fi + test_done "$testroot" "$ret" +} +function test_status_empty_dir_unversioned_file { + local testroot=`test_init status_empty_dir_unversioned_file` + + got checkout $testroot/repo $testroot/wt > /dev/null + ret="$?" + if [ "$ret" != "0" ]; then + test_done "$testroot" "$ret" + return 1 + fi + + rm $testroot/wt/epsilon/zeta + touch $testroot/wt/epsilon/unversioned + + echo '? epsilon/unversioned' > $testroot/stdout.expected + echo '! epsilon/zeta' >> $testroot/stdout.expected + + (cd $testroot/wt && got status epsilon > $testroot/stdout) + cmp -s $testroot/stdout.expected $testroot/stdout ret="$?" if [ "$ret" != "0" ]; then @@ -401,3 +451,5 @@ run_test test_status_unversioned_subdirs run_test test_status_ignores_symlink run_test test_status_shows_no_mods_after_complete_merge run_test test_status_shows_conflict +run_test test_status_empty_dir +run_test test_status_empty_dir_unversioned_file