aboutsummaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorKyle Evans <kevans@FreeBSD.org>2026-02-11 19:55:55 +0000
committerKyle Evans <kevans@FreeBSD.org>2026-02-11 19:56:37 +0000
commit7bf81e39d83087dc7f984077b5eed5a48df794d4 (patch)
treee54a9a6ac96b1dcc4ab6f825a1679b92308dfb1c /bin
parente89454417b2bfecce9daee10dece2f49632640d3 (diff)
Diffstat (limited to 'bin')
-rw-r--r--bin/ls/ls.c17
-rwxr-xr-xbin/ls/tests/ls_tests.sh30
2 files changed, 47 insertions, 0 deletions
diff --git a/bin/ls/ls.c b/bin/ls/ls.c
index b3d0a508d714..c33d4d38c359 100644
--- a/bin/ls/ls.c
+++ b/bin/ls/ls.c
@@ -707,6 +707,23 @@ traverse(int argc, char *argv[], int options)
output = 1;
}
chp = fts_children(ftsp, ch_options);
+ if (chp == NULL && errno != 0) {
+ warn("%s", p->fts_path);
+ rval = 1;
+
+ /*
+ * Avoid further errors on this entry. We won't
+ * always get an FTS_ERR/FTS_DNR for errors
+ * in fts_children(), because opendir could
+ * have failed early on and that only flags an
+ * error for fts_read() when we try to recurse
+ * into it. We catch both the non-recursive and
+ * the recursive case here.
+ */
+ (void)fts_set(ftsp, p, FTS_SKIP);
+ break;
+ }
+
display(p, chp, options);
if (!f_recursive && chp != NULL)
diff --git a/bin/ls/tests/ls_tests.sh b/bin/ls/tests/ls_tests.sh
index c732b60b21a4..be662b75695d 100755
--- a/bin/ls/tests/ls_tests.sh
+++ b/bin/ls/tests/ls_tests.sh
@@ -476,6 +476,35 @@ b_flag_body()
atf_check -e empty -o match:'y\\vz' -s exit:0 ls -b
}
+atf_test_case childerr
+childerr_head()
+{
+ atf_set "descr" "Verify that fts_children() in pre-order errors are checked"
+ atf_set "require.user" "unprivileged"
+}
+
+childerr_body()
+{
+ atf_check mkdir -p root/dir root/edir
+ atf_check touch root/c
+
+ # Check that listing an empty directory hasn't regressed into being
+ # called an error.
+ atf_check -o match:"total 0" -e empty ls -l root/dir
+
+ atf_check chmod 0 root/dir
+
+ # If we did not abort after fts_children() properly, then stdout would
+ # have an output of the total files enumerated (0). Thus, assert that
+ # it's empty and that we see the correct error on stderr.
+ atf_check -s not-exit:0 -e match:"Permission denied" ls -l root/dir
+
+ # Now ensure that we didn't just stop there, we printed out a directory
+ # that would've been enumerated later.
+ atf_check -s not-exit:0 -o match:"^root/edir" \
+ -e match:"Permission denied" ls -lR root
+}
+
atf_test_case d_flag
d_flag_head()
{
@@ -971,6 +1000,7 @@ atf_init_test_cases()
#atf_add_test_case Z_flag
atf_add_test_case a_flag
atf_add_test_case b_flag
+ atf_add_test_case childerr
#atf_add_test_case c_flag
atf_add_test_case d_flag
atf_add_test_case f_flag