summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2020-02-11 10:45:46 +0000
committerPádraig Brady <P@draigBrady.com>2020-02-27 14:11:37 +0000
commit05a99f7d7f8e0999994b760bb6337ca10ea0a14b (patch)
tree792a87deec52eeffff8e72ab1dfba9e966a594bb
parentbuild: update to latest gnulib (diff)
downloadcoreutils-05a99f7d7f8e0999994b760bb6337ca10ea0a14b.tar.gz
coreutils-05a99f7d7f8e0999994b760bb6337ca10ea0a14b.zip
ls: issue error message on removed directory
If the current directory has been removed, then "ls" confusingly produced no output and no error message, indistinguishable from running on an empty directory. * src/ls.c (print_dir): Report ENOENT on GNU/Linux if readdir finds no directory entries at all, not even "." or "..", and a recheck with the getdents syscall returns ENOENT. We recheck with getdents() as POSIX states that "The directory entries for dot and dot-dot are optional". * tests/ls/removed-directory.sh: New file. * tests/local.mk (all_tests): Add new test. * NEWS: Mention the change in behavior. Reported by Owen Thomas.
-rw-r--r--NEWS4
-rw-r--r--src/ls.c22
-rw-r--r--tests/local.mk1
-rwxr-xr-xtests/ls/removed-directory.sh45
4 files changed, 72 insertions, 0 deletions
diff --git a/NEWS b/NEWS
index a03572d01..841f3f854 100644
--- a/NEWS
+++ b/NEWS
@@ -65,6 +65,10 @@ GNU coreutils NEWS -*- outline -*-
[The old behavior was introduced in sh-utils 2.0.15 ca. 1999, predating
coreutils package.]
+ ls issues an error message on a removed directory, on GNU/Linux systems.
+ Previously no error and no entries were output, and so indistinguishable
+ from an empty directory, with default ls options.
+
uniq no longer uses strcoll() to determine string equivalence,
and so will operate more efficiently and consistently.
diff --git a/src/ls.c b/src/ls.c
index 4acf5f44d..24b983287 100644
--- a/src/ls.c
+++ b/src/ls.c
@@ -49,6 +49,10 @@
# include <sys/ptem.h>
#endif
+#ifdef __linux__
+# include <sys/syscall.h>
+#endif
+
#include <stdio.h>
#include <assert.h>
#include <setjmp.h>
@@ -2892,6 +2896,7 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
struct dirent *next;
uintmax_t total_blocks = 0;
static bool first = true;
+ bool found_any_entries = false;
errno = 0;
dirp = opendir (name);
@@ -2967,6 +2972,7 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
next = readdir (dirp);
if (next)
{
+ found_any_entries = true;
if (! file_ignored (next->d_name))
{
enum filetype type = unknown;
@@ -3012,6 +3018,22 @@ print_dir (char const *name, char const *realname, bool command_line_arg)
if (errno != EOVERFLOW)
break;
}
+#ifdef __linux__
+ else if (! found_any_entries)
+ {
+ /* If readdir finds no directory entries at all, not even "." or
+ "..", then double check that the directory exists. */
+ if (syscall (SYS_getdents, dirfd (dirp), NULL, 0) == -1
+ && errno != EINVAL)
+ {
+ /* We exclude EINVAL as that pertains to buffer handling,
+ and we've passed NULL as the buffer for simplicity.
+ ENOENT is returned if appropriate before buffer handling. */
+ file_failure (command_line_arg, _("reading directory %s"), name);
+ }
+ break;
+ }
+#endif
else
break;
diff --git a/tests/local.mk b/tests/local.mk
index 0aabdaacc..7c8196a97 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -615,6 +615,7 @@ all_tests = \
tests/ls/quote-align.sh \
tests/ls/readdir-mountpoint-inode.sh \
tests/ls/recursive.sh \
+ tests/ls/removed-directory.sh \
tests/ls/root-rel-symlink-color.sh \
tests/ls/rt-1.sh \
tests/ls/slink-acl.sh \
diff --git a/tests/ls/removed-directory.sh b/tests/ls/removed-directory.sh
new file mode 100755
index 000000000..e8c835dab
--- /dev/null
+++ b/tests/ls/removed-directory.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+# If ls is asked to list a removed directory (e.g. the parent process's
+# current working directory that has been removed by another process), it
+# emits an error message.
+
+# Copyright (C) 2020 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ ls
+
+case $host_triplet in
+ *linux*) ;;
+ *) skip_ 'non linux kernel' ;;
+esac
+
+LS_FAILURE=2
+
+cat <<\EOF >exp-err || framework_failure_
+ls: reading directory '.': No such file or directory
+EOF
+
+cwd=$(pwd)
+mkdir d || framework_failure_
+cd d || framework_failure_
+rmdir ../d || framework_failure_
+
+returns_ $LS_FAILURE ls >../out 2>../err || fail=1
+cd "$cwd" || framework_failure_
+compare /dev/null out || fail=1
+compare exp-err err || fail=1
+
+Exit $fail