diff options
| author | Colin Watson <cjwatson@debian.org> | 2020-02-11 10:45:46 +0000 |
|---|---|---|
| committer | Pádraig Brady <P@draigBrady.com> | 2020-02-27 14:11:37 +0000 |
| commit | 05a99f7d7f8e0999994b760bb6337ca10ea0a14b (patch) | |
| tree | 792a87deec52eeffff8e72ab1dfba9e966a594bb | |
| parent | build: update to latest gnulib (diff) | |
| download | coreutils-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-- | NEWS | 4 | ||||
| -rw-r--r-- | src/ls.c | 22 | ||||
| -rw-r--r-- | tests/local.mk | 1 | ||||
| -rwxr-xr-x | tests/ls/removed-directory.sh | 45 |
4 files changed, 72 insertions, 0 deletions
@@ -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. @@ -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 |
