diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:11:47 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:11:47 +0000 |
commit | 758f820bcc0f68aeebac1717e537ca13a320b909 (patch) | |
tree | 48111ece75cf4f98316848b37a7e26356e00669e /tests/rm | |
parent | Initial commit. (diff) | |
download | coreutils-upstream.tar.xz coreutils-upstream.zip |
Adding upstream version 9.1.upstream/9.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
54 files changed, 3005 insertions, 0 deletions
diff --git a/tests/rm/cycle.sh b/tests/rm/cycle.sh new file mode 100755 index 0000000..4cb958a --- /dev/null +++ b/tests/rm/cycle.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# rm (coreutils-4.5.4) could be tricked into mistakenly reporting a cycle. + +# Copyright (C) 2003-2022 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_ rm +skip_if_root_ + +mkdir -p a/b +touch a/b/file +chmod ug-w a/b + + +rm -rf a a 2>&1 | sed 's/:[^:]*$//' > out || fail=1 +cat <<\EOF > exp +rm: cannot remove 'a/b/file' +rm: cannot remove 'a/b/file' +EOF + +compare exp out || fail=1 + +Exit $fail diff --git a/tests/rm/d-1.sh b/tests/rm/d-1.sh new file mode 100755 index 0000000..1fbbffd --- /dev/null +++ b/tests/rm/d-1.sh @@ -0,0 +1,38 @@ +#!/bin/sh +# Test "rm --dir --verbose". + +# Copyright (C) 2012-2022 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_ rm + +mkdir a || framework_failure_ +> b || framework_failure_ + +rm --verbose --dir a b > out || fail=1 + +cat <<\EOF > exp || framework_failure_ +removed directory 'a' +removed 'b' +EOF + +test -e a && fail=1 +test -e b && fail=1 + +# Compare expected and actual output. +compare exp out || fail=1 + +Exit $fail diff --git a/tests/rm/d-2.sh b/tests/rm/d-2.sh new file mode 100755 index 0000000..901563e --- /dev/null +++ b/tests/rm/d-2.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# Ensure that 'rm -d dir' (i.e., without --recursive) gives a reasonable +# diagnostic when failing. + +# Copyright (C) 2012-2022 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_ rm + +mkdir d || framework_failure_ +> d/a || framework_failure_ + +rm -d d 2> out && fail=1 + +# Accept any of these: EEXIST, ENOTEMPTY +sed 's/: File exists/: Directory not empty/' out > out2 + +printf "%s\n" \ + "rm: cannot remove 'd': Directory not empty" \ + > exp || framework_failure_ + +compare exp out2 || fail=1 + +Exit $fail diff --git a/tests/rm/d-3.sh b/tests/rm/d-3.sh new file mode 100755 index 0000000..8613a2f --- /dev/null +++ b/tests/rm/d-3.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# Ensure that 'rm -d -i dir' (i.e., without --recursive) gives a prompt and +# then deletes the directory if it is empty + +# Copyright (C) 2012-2022 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_ rm + +mkdir d || framework_failure_ + +echo "y" | rm -i -d --verbose d > out 2> out.err || fail=1 +printf "%s" \ + "rm: remove directory 'd'? " \ + > exp.err || framework_failure_ + +printf "%s\n" \ + "removed directory 'd'" \ + > exp || framework_failure_ + +compare exp out || fail=1 +compare exp.err out.err || fail=1 + +Exit $fail diff --git a/tests/rm/dangling-symlink.sh b/tests/rm/dangling-symlink.sh new file mode 100755 index 0000000..4208080 --- /dev/null +++ b/tests/rm/dangling-symlink.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# rm should not prompt before removing a dangling symlink. +# Likewise for a non-dangling symlink. +# But for fileutils-4.1.9, it would do the former and +# for fileutils-4.1.10 the latter. + +# Copyright (C) 2002-2022 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_ rm + +ln -s no-file dangle +ln -s / symlink + +# Terminate any background processes +cleanup_() { kill $pid 2>/dev/null && wait $pid; } + +rm ---presume-input-tty dangle symlink & pid=$! +# The buggy rm (fileutils-4.1.9) would hang here, waiting for input. + +# Wait up to 6.3s for rm to remove the files +check_files_removed() { + local present=0 + sleep $1 + ls -l dangle > /dev/null 2>&1 && present=1 + ls -l symlink > /dev/null 2>&1 && present=1 + test $present = 0 +} +retry_delay_ check_files_removed .1 6 || fail=1 + +cleanup_ + +Exit $fail diff --git a/tests/rm/deep-1.sh b/tests/rm/deep-1.sh new file mode 100755 index 0000000..41fac84 --- /dev/null +++ b/tests/rm/deep-1.sh @@ -0,0 +1,53 @@ +#!/bin/sh +# Test "rm" with a deep hierarchy. + +# Copyright (C) 1997-2022 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/>. + +# This is a bit of a torture test for mkdir -p, too. +# GNU rm performs *much* better on systems that have a d_type member +# in the directory structure because then it does only one stat per +# command line argument. + +# If this test takes too long on your system, blame the OS. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ rm + +umask 022 + + +k20=/k/k/k/k/k/k/k/k/k/k/k/k/k/k/k/k/k/k/k/k +k200=$k20$k20$k20$k20$k20$k20$k20$k20$k20$k20 + +# Be careful not to exceed max file name length (usu 512?). +# Doing so wouldn't affect GNU mkdir or GNU rm, but any tool that +# operates on the full pathname (like 'test') would choke. +k_deep=$k200$k200 + +t=t +# Create a directory in $t with lots of 'k' components. +deep=$t$k_deep +mkdir -p $deep || fail=1 + +# Make sure the deep dir was created. +test -d $deep || fail=1 + +rm -r $t || fail=1 + +# Make sure all of $t was deleted. +test -d $t && fail=1 + +Exit $fail diff --git a/tests/rm/deep-2.sh b/tests/rm/deep-2.sh new file mode 100755 index 0000000..01c2104 --- /dev/null +++ b/tests/rm/deep-2.sh @@ -0,0 +1,53 @@ +#!/bin/sh +# Ensure rm -r DIR does not prompt for very long full relative names in DIR. + +# Copyright (C) 2008-2022 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_ rm +require_perl_ + +# Root can run this test, but it always succeeds, since for root, all +# files are writable, and write_protected_non_symlink never reaches +# the offending euidaccess_stat call. +skip_if_root_ + +# ecryptfs for example uses some of the file name space +# for encrypting filenames, so we must check dynamically. +name_max=$(stat -f -c %l .) +test "$name_max" -ge '200' || skip_ "NAME_MAX=$name_max is not sufficient" + +mkdir x || framework_failure_ +cd x || framework_failure_ + +# Construct a hierarchy containing a relative file with a long name +$PERL \ + -e 'my $d = "x" x 200; foreach my $i (1..52)' \ + -e ' { mkdir ($d, 0700) && chdir $d or die "$!" }' \ + || framework_failure_ + +cd .. || framework_failure_ +echo n > no || framework_failure_ + +rm ---presume-input-tty -r x < no > out || fail=1 + +# expect empty output +compare /dev/null out || fail=1 + +# the directory must have been removed +test -d x && fail=1 + +Exit $fail diff --git a/tests/rm/dir-no-w.sh b/tests/rm/dir-no-w.sh new file mode 100755 index 0000000..8424d9f --- /dev/null +++ b/tests/rm/dir-no-w.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# rm (without -r) must give a diagnostic for any directory. +# It must not prompt, even if that directory is unwritable. + +# Copyright (C) 2003-2022 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_ rm + +mkdir --mode=0500 unwritable-dir || framework_failure_ + + +# For rm from coreutils-5.0.1, this would prompt. +rm ---presume-input-tty unwritable-dir < /dev/null > out-t 2>&1 && fail=1 +cat <<\EOF > exp || framework_failure_ +rm: cannot remove 'unwritable-dir': Is a directory +EOF + +# When run by a non-privileged user we get this: +# rm: cannot remove directory 'unwritable-dir': Is a directory +# When run by root we get this: +# rm: cannot remove 'unwritable-dir': Is a directory +# Normalize the message. +sed 's/remove directory/remove/' out-t > out +rm -f out-t + +compare exp out || fail=1 + +Exit $fail diff --git a/tests/rm/dir-nonrecur.sh b/tests/rm/dir-nonrecur.sh new file mode 100755 index 0000000..d309828 --- /dev/null +++ b/tests/rm/dir-nonrecur.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# Ensure that 'rm dir' (i.e., without --recursive) gives a reasonable +# diagnostic when failing. + +# Copyright (C) 2005-2022 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_ rm + +mkdir d || framework_failure_ + + +rm d 2> out && fail=1 +cat <<\EOF > exp || framework_failure_ +rm: cannot remove 'd': Is a directory +EOF + +# Before coreutils-5.93 this test would fail on Solaris 9 and newer. +compare exp out || fail=1 + +Exit $fail diff --git a/tests/rm/dot-rel.sh b/tests/rm/dot-rel.sh new file mode 100755 index 0000000..3f603b1 --- /dev/null +++ b/tests/rm/dot-rel.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# Use rm -r to remove two non-empty dot-relative directories. +# This would have failed between 2004-10-18 and 2004-10-21. + +# Copyright (C) 2004-2022 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_ rm + +mkdir a b || framework_failure_ +touch a/f b/f || framework_failure_ + + +rm -r a b || fail=1 + +Exit $fail diff --git a/tests/rm/empty-immutable-skip.sh b/tests/rm/empty-immutable-skip.sh new file mode 100755 index 0000000..a4eae3c --- /dev/null +++ b/tests/rm/empty-immutable-skip.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# Ensure that rm does not skip extra files after hitting an empty immutable dir. +# Requires root access to do chattr +i, as well as an ext[23] or xfs file system + +# Copyright (C) 2020-2022 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_ rm +require_root_ + +# These simple one-file operations are expected to work even in the +# presence of this bug, and we need them to set up the rest of the test. +chattr_i_works=1 +touch f +chattr +i f 2>/dev/null || chattr_i_works=0 +rm f 2>/dev/null +test -f f || chattr_i_works=0 +chattr -i f 2>/dev/null || chattr_i_works=0 +rm f 2>/dev/null || chattr_i_works=0 +test -f f && chattr_i_works=0 + +if test $chattr_i_works = 0; then + skip_ "chattr +i doesn't work on this file system" +fi + +mkdir empty || framework_failure_ +touch x y || framework_failure_ +chattr +i empty || framework_failure_ +rm -rf empty x y +{ test -f x || test -f y; } && fail=1 +chattr -i empty + +Exit $fail diff --git a/tests/rm/empty-inacc.sh b/tests/rm/empty-inacc.sh new file mode 100755 index 0000000..5cab266 --- /dev/null +++ b/tests/rm/empty-inacc.sh @@ -0,0 +1,38 @@ +#!/bin/sh +# Ensure that rm -rf removes an empty-and-inaccessible directory. + +# Copyright (C) 2006-2022 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_ rm +skip_if_root_ + +mkdir -m0 inacc || framework_failure_ + +# Also exercise the different code path that's taken for a directory +# that is empty (hence removable) and unreadable. +mkdir -m a-r -p a/unreadable + + +# This would fail for e.g., coreutils-5.93. +rm -rf inacc || fail=1 +test -d inacc && fail=1 + +# This would fail for e.g., coreutils-5.97. +rm -rf a || fail=1 +test -d a && fail=1 + +Exit $fail diff --git a/tests/rm/empty-name.pl b/tests/rm/empty-name.pl new file mode 100755 index 0000000..e31f68f --- /dev/null +++ b/tests/rm/empty-name.pl @@ -0,0 +1,60 @@ +#!/usr/bin/perl +# Make sure that rm -r '' fails. + +# Copyright (C) 1998-2022 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/>. + +# On SunOS 4.1.3, running rm -r '' in a nonempty directory may +# actually remove files with names of entries in the current directory +# but relative to '/' rather than relative to the current directory. + +use strict; + +(my $program_name = $0) =~ s|.*/||; + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my $prog = 'rm'; + +# FIXME: copied from misc/ls-misc; factor into Coreutils.pm? +sub mk_file(@) +{ + foreach my $f (@_) + { + open (F, '>', $f) && close F + or die "creating $f: $!\n"; + } +} + +my @Tests = + ( + # test-name options input expected-output + # + ['empty-name-1', "''", {EXIT => 1}, + {ERR => "$prog: cannot remove '': No such file or directory\n"}], + + ['empty-name-2', "a '' b", {EXIT => 1}, + {ERR => "$prog: cannot remove '': No such file or directory\n"}, + {PRE => sub { mk_file qw(a b) }}, + {POST => sub {-f 'a' || -f 'b' and die "a or b remain\n" }}, + ], + ); + +my $save_temps = $ENV{SAVE_TEMPS}; +my $verbose = $ENV{VERBOSE}; + +my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); +exit $fail; diff --git a/tests/rm/ext3-perf.sh b/tests/rm/ext3-perf.sh new file mode 100755 index 0000000..744298a --- /dev/null +++ b/tests/rm/ext3-perf.sh @@ -0,0 +1,83 @@ +#!/bin/sh +# ensure that "rm -rf DIR-with-many-entries" is not O(N^2) + +# Copyright (C) 2008-2022 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_ rm + +very_expensive_ + +# In a circa 2008 benchmark, using rm -rf to remove a 400k-entry directory took: +# - 9 seconds with the patch, on a 2-yr-old system +# - 350 seconds without the patch, on a high-end system (disk 20-30% faster) +threshold_seconds=60 + +# The number of entries in our test directory. +n=400000 + +# Choose a value that is large enough to ensure an accidentally +# regressed rm would require much longer than $threshold_seconds to remove +# the directory. With n=400k, pre-patch GNU rm would require about 350 +# seconds even on a fast disk. On a circa 2006 system, the +# patched version of rm requires about 10 seconds, so even if you +# choose to enable very expensive tests with a device that is much slower, +# the test should still succeed. + +# Skip unless "." is on an ext[34] file system. +# FIXME-maybe: try to find a suitable file system or allow +# the user to specify it via an envvar. +df -T -t ext3 -t ext4dev -t ext4 . \ + || skip_ 'this test runs only on an ext3 or ext4 file system' + +# Skip if there are too few inodes free. Require some slack. +free_inodes=$(stat -f --format=%d .) || framework_failure_ +min_free_inodes=$(expr 12 \* $n / 10) +test $min_free_inodes -lt $free_inodes \ + || skip_ "too few free inodes on '.': $free_inodes;" \ + "this test requires at least $min_free_inodes" + +ok=0 +start=$(date +%s) +mkdir d && + cd d && + seq $n | xargs touch && + test -f 1 && + test -f $n && + cd .. && + ok=1 +test $ok = 1 || framework_failure_ +setup_duration=$(expr $(date +%s) - $start) +echo creating a $n-entry directory took $setup_duration seconds + +# If set-up took longer than the default $threshold_seconds, +# use the longer set-up duration as the limit. +test $threshold_seconds -lt $setup_duration \ + && threshold_seconds=$setup_duration + +start=$(date +%s) +timeout ${threshold_seconds}s rm -rf d; err=$? +duration=$(expr $(date +%s) - $start) + +case $err in + 124) fail=1; echo rm took longer than $threshold_seconds seconds;; + 0) ;; + *) fail=1;; +esac + +echo removing a $n-entry directory took $duration seconds + +Exit $fail diff --git a/tests/rm/f-1.sh b/tests/rm/f-1.sh new file mode 100755 index 0000000..3045684 --- /dev/null +++ b/tests/rm/f-1.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# Test "rm -f" with a nonexistent file. + +# Copyright (C) 1997-2022 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_ rm + +mkdir -p d || framework_failure_ + +rm -f d/no-such-file || fail=1 + +Exit $fail diff --git a/tests/rm/fail-2eperm.sh b/tests/rm/fail-2eperm.sh new file mode 100755 index 0000000..dbee69f --- /dev/null +++ b/tests/rm/fail-2eperm.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# Like fail-eperm, but the failure must be for a file encountered +# while trying to remove the containing directory with the sticky bit set. + +# Copyright (C) 2003-2022 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_ rm +require_root_ + +# The containing directory must be owned by the user who eventually runs rm. +chown $NON_ROOT_USERNAME . + +mkdir a || framework_failure_ +chmod 1777 a || framework_failure_ +touch a/b || framework_failure_ + + +# Try to ensure that $NON_ROOT_USERNAME can access +# the required version of rm. +rm_version=$( + chroot --skip-chdir --user=$NON_ROOT_USERNAME / env PATH="$PATH" \ + rm --version | + sed -n '1s/.* //p' +) +case $rm_version in + $PACKAGE_VERSION) ;; + *) skip_ "cannot access just-built rm as user $NON_ROOT_USERNAME";; +esac +chroot --skip-chdir --user=$NON_ROOT_USERNAME / \ + env PATH="$PATH" rm -rf a 2> out-t && fail=1 + +# On some systems, we get 'Not owner'. Convert it. +# On other systems (HPUX), we get 'Permission denied'. Convert it, too. +onp='Operation not permitted' +sed "s/Not owner/$onp/;s/Permission denied/$onp/" out-t > out + +cat <<\EOF > exp +rm: cannot remove 'a/b': Operation not permitted +EOF + +compare exp out || fail=1 + +Exit $fail diff --git a/tests/rm/fail-eacces.sh b/tests/rm/fail-eacces.sh new file mode 100755 index 0000000..94f36c2 --- /dev/null +++ b/tests/rm/fail-eacces.sh @@ -0,0 +1,54 @@ +#!/bin/sh +# Ensure that rm -rf unremovable-non-dir gives a diagnostic. +# Test both a regular file and a symlink -- it makes a difference to rm. +# With the symlink, rm from coreutils-6.9 would fail with a misleading +# ELOOP diagnostic. + +# Copyright (C) 2006-2022 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_ rm +skip_if_root_ + +ok=0 +mkdir d && + touch d/f && + ln -s f d/slink && + chmod a-w d && + ok=1 +test $ok = 1 || framework_failure_ + +mkdir e && + ln -s f e/slink && + chmod a-w e && + ok=1 +test $ok = 1 || framework_failure_ + + +rm -rf d/f 2> out && fail=1 +cat <<\EOF > exp +rm: cannot remove 'd/f': Permission denied +EOF +compare exp out || fail=1 + +# This used to fail with ELOOP. +rm -rf e 2> out && fail=1 +cat <<\EOF > exp +rm: cannot remove 'e/slink': Permission denied +EOF +compare exp out || fail=1 + +Exit $fail diff --git a/tests/rm/fail-eperm.xpl b/tests/rm/fail-eperm.xpl new file mode 100755 index 0000000..09374e1 --- /dev/null +++ b/tests/rm/fail-eperm.xpl @@ -0,0 +1,150 @@ +#!/usr/bin/perl -Tw +# Ensure that rm gives the expected diagnostic when failing to remove a file +# owned by some other user in a directory with the sticky bit set. + +# Copyright (C) 2002-2022 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/>. + +use strict; + +(my $ME = $0) =~ s|.*/||; + +my $uid = $<; +# skip if root +$uid == 0 + and CuSkip::skip "$ME: can't run this test as root: skipping this test"; + +my $verbose = $ENV{VERBOSE} && $ENV{VERBOSE} eq 'yes'; + +# Ensure that the diagnostics are in English. +$ENV{LC_ALL} = 'C'; + +# Set up a safe, well-known environment +$ENV{IFS} = ''; + +# Taint checking requires a sanitized $PATH. This script performs no $PATH +# search, so on most Unix-based systems, it is fine simply to clear $ENV{PATH}. +# However, on Cygwin, it's used to find cygwin1.dll, so set it. +$ENV{PATH} = '/bin:/usr/bin'; + +my @dir_list = qw(/tmp /var/tmp /usr/tmp); +my $rm = "$ENV{abs_top_builddir}/src/rm"; + +# Untaint for upcoming popen. +$rm =~ m!^([-+\@\w./]+)$! + or CuSkip::skip "$ME: unusual absolute builddir name; skipping this test\n"; +$rm = $1; + +# Find a directory with the sticky bit set. +my $found_dir; +my $found_file; +foreach my $dir (@dir_list) + { + if (-d $dir && -k _ && -r _ && -w _ && -x _) + { + $found_dir = 1; + + # Find a non-directory there that is owned by some other user. + opendir DIR_HANDLE, $dir + or die "$ME: couldn't open $dir: $!\n"; + + foreach my $f (readdir DIR_HANDLE) + { + # Consider only names containing "safe" characters. + $f =~ /^([-\@\w.]+)$/ + or next; + $f = $1; # untaint $f + + my $target_file = "$dir/$f"; + $verbose + and warn "$ME: considering $target_file\n"; + + # Skip files owned by self, symlinks, and directories. + # It's not technically necessary to skip symlinks, but it's simpler. + # SVR4-like systems (e.g., Solaris 9) let you unlink files that + # you can write, so skip writable files too. + -l $target_file || -o _ || -d _ || -w _ + and next; + + $found_file = 1; + + # Invoke rm on this file and ensure that we get the + # expected exit code and diagnostic. + my $cmd = "$rm -f -- $target_file"; + open RM, "$cmd 2>&1 |" + or die "$ME: cannot execute '$cmd'\n"; + + my $line = <RM>; + + close RM; + my $rc = $?; + # This test opportunistically looks for files that can't + # be removed but those files may already have been removed + # by their owners by the time we get to them. It is a + # race condition. If so then the rm is successful and our + # test is thwarted. Detect this case and ignore. + if ($rc == 0) + { + next if ! -e $target_file; + die "$ME: unexpected exit status from '$cmd';\n" + . " got 0, expected 1\n"; + } + if (0x80 < $rc) + { + my $status = $rc >> 8; + $status == 1 + or die "$ME: unexpected exit status from '$cmd';\n" + . " got $status, expected 1\n"; + } + else + { + # Terminated by a signal. + my $sig_num = $rc & 0x7F; + die "$ME: command '$cmd' died with signal $sig_num\n"; + } + + my $exp = "rm: cannot remove '$target_file':"; + $line + or die "$ME: no output from '$cmd';\n" + . "expected something like '$exp ...'\n"; + + # Transform the actual diagnostic so that it starts with "rm:". + # Depending on your system, it might be "rm:" already, or + # "../../src/rm:". + $line =~ s,^\Q$rm\E:,rm:,; + + my $regex = quotemeta $exp; + $line =~ /^$regex/ + or die "$ME: unexpected diagnostic from '$cmd';\n" + . " got $line" + . " expected $exp ...\n"; + + last; + } + + closedir DIR_HANDLE; + $found_file + and last; + } + } + +$found_dir + or CuSkip::skip "$ME: couldn't find a directory with the sticky bit set;" + . " skipping this test\n"; + +$found_file + or CuSkip::skip "$ME: couldn't find a file not owned by you\n" + . " in any of the following directories:\n @dir_list\n" + . "...so, skipping this test\n"; diff --git a/tests/rm/hash.sh b/tests/rm/hash.sh new file mode 100755 index 0000000..ce902c9 --- /dev/null +++ b/tests/rm/hash.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# Exercise a bug that was fixed in 4.0s. +# Before then, rm would fail occasionally, sometimes via +# a failed assertion, others with a seg fault. + +# Copyright (C) 2000-2022 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_ rm +expensive_ + +# Create a hierarchy with 3*26 leaf directories, each at depth 153. +echo "$0: creating 78 trees, each of depth 153; this will take a while..." >&2 +y=$(seq 1 150|tr -sc '\n' y|tr '\n' /) +for i in 1 2 3; do + for j in a b c d e f g h i j k l m n o p q r s t u v w x y z; do + mkdir -p t/$i/$j/$y || framework_failure_ + done +done + + +rm -r t || fail=1 + +Exit $fail diff --git a/tests/rm/i-1.sh b/tests/rm/i-1.sh new file mode 100755 index 0000000..00f4200 --- /dev/null +++ b/tests/rm/i-1.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# Test "rm -i". + +# Copyright (C) 1997-2022 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_ rm + +t=t +mkdir -p $t || framework_failure_ +echo > $t/a || framework_failure_ +test -f $t/a || framework_failure_ + +echo y > $t/in-y +echo n > $t/in-n + +rm -i $t/a < $t/in-n > /dev/null 2>&1 || fail=1 +# The file should not have been removed. +test -f $t/a || fail=1 + +rm -i $t/a < $t/in-y > /dev/null 2>&1 || fail=1 +# The file should have been removed this time. +test -f $t/a && fail=1 + +rm -rf $t + +Exit $fail diff --git a/tests/rm/i-never.sh b/tests/rm/i-never.sh new file mode 100755 index 0000000..da988a2 --- /dev/null +++ b/tests/rm/i-never.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# Ensure that rm --interactive=never works does not prompt, even for +# an unwritable file. + +# Copyright (C) 2007-2022 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_ rm +skip_if_root_ + +touch f || framework_failure_ +chmod 0 f || framework_failure_ +touch exp || framework_failure_ + + +rm --interactive=never f > out || fail=1 + +compare exp out || fail=1 + +Exit $fail diff --git a/tests/rm/i-no-r.sh b/tests/rm/i-no-r.sh new file mode 100755 index 0000000..f107b04 --- /dev/null +++ b/tests/rm/i-no-r.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# Since the rewrite for fileutils-4.1.9, 'rm -i DIR' would mistakenly +# recurse into directory DIR. rm -i (without -r) must fail in that case. +# Fixed in coreutils-4.5.2. + +# Copyright (C) 2002-2022 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_ rm + +mkdir dir || framework_failure_ +echo y > y || framework_failure_ + + +# This must fail. +returns_ 1 rm -i dir < y > /dev/null 2>&1 || fail=1 + +# The directory must remain. +test -d dir || fail=1 + +Exit $fail diff --git a/tests/rm/ignorable.sh b/tests/rm/ignorable.sh new file mode 100755 index 0000000..357b94b --- /dev/null +++ b/tests/rm/ignorable.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# Ensure that rm -f existing-non-dir/anything exits successfully + +# Copyright (C) 2006-2022 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_ rm +skip_if_root_ + +touch existing-non-dir || framework_failure_ + + +# With coreutils-6.3, this would exit nonzero. It should not. +# Example from Andreas Schwab. +rm -f existing-non-dir/f > out 2>&1 || fail=1 + +Exit $fail diff --git a/tests/rm/inaccessible.sh b/tests/rm/inaccessible.sh new file mode 100755 index 0000000..d0f5363 --- /dev/null +++ b/tests/rm/inaccessible.sh @@ -0,0 +1,49 @@ +#!/bin/sh +# Ensure that rm works even when run from a directory +# for which the user has no access at all. + +# Copyright (C) 2004-2022 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_ rm + +# Skip this test if your system has neither the openat-style functions +# nor /proc/self/fd support with which to emulate them. +require_openat_support_ +skip_if_root_ + +p=$(pwd) +mkdir abs1 abs2 no-access || framework_failure_ + + +set +x +(cd no-access; chmod 0 . && rm -r "$p/abs1" rel "$p/abs2") 2> out && fail=1 +test -d "$p/abs1" && fail=1 +test -d "$p/abs2" && fail=1 + +cat <<\EOF > exp || framework_failure_ +rm: cannot remove 'rel': Permission denied +EOF + +# AIX 4.3.3 fails with a different diagnostic. +# Transform their diagnostic +# ...: The file access permissions do not allow the specified action. +# to the expected one: +sed 's/: The file access permissions.*/: Permission denied/'<out>o1;mv o1 out + +compare exp out || fail=1 + +Exit $fail diff --git a/tests/rm/interactive-always.sh b/tests/rm/interactive-always.sh new file mode 100755 index 0000000..4ac3145 --- /dev/null +++ b/tests/rm/interactive-always.sh @@ -0,0 +1,87 @@ +#!/bin/sh +# Test the --interactive[=WHEN] changes added to coreutils 6.0 + +# Copyright (C) 2006-2022 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_ rm + +touch file1-1 file1-2 file2-1 file2-2 file3-1 file3-2 file4-1 file4-2 \ + || framework_failure_ +# If asked, answer no to first question, then yes to second. +echo 'n +y' > in || framework_failure_ +rm -f out err || framework_failure_ + + +# The prompt has a trailing space, and no newline, so an extra +# 'echo .' is inserted after each rm to make it obvious what was asked. + +echo 'no WHEN' > err || framework_failure_ +rm -R --interactive file1-* < in >> out 2>> err || fail=1 +echo . >> err || framework_failure_ +test -f file1-1 || fail=1 +test -f file1-2 && fail=1 + +echo 'WHEN=never' >> err || framework_failure_ +rm -R --interactive=never file2-* < in >> out 2>> err || fail=1 +echo . >> err || framework_failure_ +test -f file2-1 && fail=1 +test -f file2-2 && fail=1 + +echo 'WHEN=once' >> err || framework_failure_ +rm -R --interactive=once file3-* < in >> out 2>> err || fail=1 +echo . >> err || framework_failure_ +test -f file3-1 || fail=1 +test -f file3-2 || fail=1 + +echo 'WHEN=always' >> err || framework_failure_ +rm -R --interactive=always file4-* < in >> out 2>> err || fail=1 +echo . >> err || framework_failure_ +test -f file4-1 || fail=1 +test -f file4-2 && fail=1 + +echo '-f overrides --interactive' >> err || framework_failure_ +rm -R --interactive=once -f file1-* < in >> out 2>> err || fail=1 +echo . >> err || framework_failure_ +test -f file1-1 && fail=1 + +echo '--interactive overrides -f' >> err || framework_failure_ +rm -R -f --interactive=once file4-* < in >> out 2>> err || fail=1 +echo . >> err || framework_failure_ +test -f file4-1 || fail=1 + +cat <<\EOF > experr.t || framework_failure_ +no WHEN +@remove_empty 'file1-1'? @remove_empty 'file1-2'? . +WHEN=never +. +WHEN=once +rm: remove 2 arguments recursively? . +WHEN=always +@remove_empty 'file4-1'? @remove_empty 'file4-2'? . +-f overrides --interactive +. +--interactive overrides -f +rm: remove 1 argument recursively? . +EOF +sed 's/@remove_empty/rm: remove regular empty file/g' < experr.t > experr || + framework_failure_ + +compare /dev/null out || fail=1 +compare experr err || fail=1 + +Exit $fail diff --git a/tests/rm/interactive-once.sh b/tests/rm/interactive-once.sh new file mode 100755 index 0000000..16bd4ca --- /dev/null +++ b/tests/rm/interactive-once.sh @@ -0,0 +1,148 @@ +#!/bin/sh +# Test the -I option added to coreutils 6.0 + +# Copyright (C) 2006-2022 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_ rm + +mkdir -p dir1-1 dir2-1 dir2-2 || framework_failure_ +touch file1-1 file2-1 file2-2 file2-3 file3-1 file3-2 file3-3 file3-4 \ + || framework_failure_ +echo y > in-y || framework_failure_ +echo n > in-n || framework_failure_ +rm -f out err || framework_failure_ + + +# The prompt has a trailing space, and no newline, so an extra +# 'echo .' is inserted after each rm to make it obvious what was asked. + +echo 'one file, no recursion' > err || framework_failure_ +rm -I file1-* < in-n >> out 2>> err || fail=1 +echo . >> err || framework_failure_ +test -f file1-1 && fail=1 + +echo 'one file, read only, answer no' >> err || framework_failure_ +if ls /dev/stdin >/dev/null 2>&1; then + touch file1-1 || framework_failure_ + chmod a-w file1-1 || framework_failure_ + if ! test -w file1-1; then + # root won't get prompted + write_prot_msg1="rm: remove write-protected regular empty file 'file1-1'? " + fi + rm ---presume-input-tty -I file1-* < in-n >> out 2>> err || fail=1 + echo . >> err || framework_failure_ + if test "$write_prot_msg1"; then + test -f file1-1 || fail=1 + fi +else + echo '.' >> err || framework_failure_ +fi + +echo 'three files, no recursion' >> err || framework_failure_ +rm -I file2-* < in-n >> out 2>> err || fail=1 +echo . >> err || framework_failure_ +test -f file2-1 && fail=1 +test -f file2-2 && fail=1 +test -f file2-3 && fail=1 + +echo 'four files, no recursion, answer no' >> err || framework_failure_ +rm -I file3-* < in-n >> out 2>> err || fail=1 +echo . >> err || framework_failure_ +test -f file3-1 || fail=1 +test -f file3-2 || fail=1 +test -f file3-3 || fail=1 +test -f file3-4 || fail=1 + +echo 'four files, no recursion, answer yes' >> err || framework_failure_ +rm -I file3-* < in-y >> out 2>> err || fail=1 +echo . >> err || framework_failure_ +test -f file3-1 && fail=1 +test -f file3-2 && fail=1 +test -f file3-3 && fail=1 +test -f file3-4 && fail=1 + +echo 'four files, no recursion, 1 read only, answer yes no' >> err \ + || framework_failure_ +if ls /dev/stdin >/dev/null 2>&1; then + touch file3-1 file3-2 file3-3 file3-4 || framework_failure_ + echo non_empty > file3-4 || framework_failure_ # to shorten diagnostic + chmod a-w file3-4 || framework_failure_ + if ! test -w file3-4; then + # root won't get prompted + write_prot_msg2="rm: remove write-protected regular file 'file3-4'? " + fi + cat in-y in-n | rm ---presume-input-tty -I file3-* >> out 2>> err || fail=1 + echo . >> err || framework_failure_ + test -f file3-1 && fail=1 + test -f file3-2 && fail=1 + test -f file3-3 && fail=1 + if test "$write_prot_msg2"; then + test -f file3-4 || fail=1 + fi +else + echo 'rm: remove 4 arguments? .' >> err || framework_failure_ +fi + +echo 'one file, recursion, answer no' >> err || framework_failure_ +rm -I -R dir1-* < in-n >> out 2>> err || fail=1 +echo . >> err || framework_failure_ +test -d dir1-1 || fail=1 + +echo 'one file, recursion, answer yes' >> err || framework_failure_ +rm -I -R dir1-* < in-y >> out 2>> err || fail=1 +echo . >> err || framework_failure_ +test -d dir1-1 && fail=1 + +echo 'multiple files, recursion, answer no' >> err || framework_failure_ +rm -I -R dir2-* < in-n >> out 2>> err || fail=1 +echo . >> err || framework_failure_ +test -d dir2-1 || fail=1 +test -d dir2-2 || fail=1 + +echo 'multiple files, recursion, answer yes' >> err || framework_failure_ +rm -I -R dir2-* < in-y >> out 2>> err || fail=1 +echo . >> err || framework_failure_ +test -d dir2-1 && fail=1 +test -d dir2-2 && fail=1 + +cat <<EOF > experr || framework_failure_ +one file, no recursion +. +one file, read only, answer no +$write_prot_msg1. +three files, no recursion +. +four files, no recursion, answer no +rm: remove 4 arguments? . +four files, no recursion, answer yes +rm: remove 4 arguments? . +four files, no recursion, 1 read only, answer yes no +rm: remove 4 arguments? $write_prot_msg2. +one file, recursion, answer no +rm: remove 1 argument recursively? . +one file, recursion, answer yes +rm: remove 1 argument recursively? . +multiple files, recursion, answer no +rm: remove 2 arguments recursively? . +multiple files, recursion, answer yes +rm: remove 2 arguments recursively? . +EOF + +compare /dev/null out || fail=1 +compare experr err || fail=1 + +Exit $fail diff --git a/tests/rm/ir-1.sh b/tests/rm/ir-1.sh new file mode 100755 index 0000000..bc12050 --- /dev/null +++ b/tests/rm/ir-1.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# Test "rm -ir". + +# Copyright (C) 1997-2022 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_ rm + +t=t +mkdir -p $t $t/a $t/b $t/c || framework_failure_ +> $t/a/a || framework_failure_ +> $t/b/bb || framework_failure_ +> $t/c/cc || framework_failure_ + +cat <<EOF > in +y +y +y +y +y +y +y +y +n +n +n +EOF + +# Remove all but one of a, b, c -- I doubt that this test can portably +# determine which one was removed based on order of dir entries. +# This is a good argument for switching to a dejagnu-style test suite. +rm --verbose -i -r $t < in > /dev/null 2>&1 || fail=1 + +# $t should not have been removed. +test -d $t || fail=1 + +# There should be only one directory left. +case $(echo $t/*) in + $t/[abc]) ;; + *) fail=1 ;; +esac + +Exit $fail diff --git a/tests/rm/isatty.sh b/tests/rm/isatty.sh new file mode 100755 index 0000000..0f0b663 --- /dev/null +++ b/tests/rm/isatty.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# Make sure 'chown 0 f; rm f' prompts before removing f. + +# Copyright (C) 2001-2022 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_ rm +skip_if_root_ + + +# Skip this test if there is no /dev/stdin file. +ls /dev/stdin >/dev/null 2>&1 \ + || skip_ 'there is no /dev/stdin file' + +# Terminate any background processes +cleanup_() { kill $pid 2>/dev/null && wait $pid; } + +touch f +chmod 0 f +rm ---presume-input-tty f > out 2>&1 & pid=$! + +# Wait a second, to give a buggy rm (as in fileutils-4.0.40) +# enough time to remove the file. +sleep 1 + +# The file must still exist. +test -f f || fail=1 + +cleanup_ + +# Note the trailing 'x' -- so I don't have to have a trailing +# blank in this file :-) +cat > exp <<\EOF +rm: remove write-protected regular empty file 'f'? x +EOF + +# Append an 'x' and a newline. +echo x >> out + +compare exp out || fail=1 + +Exit $fail diff --git a/tests/rm/many-dir-entries-vs-OOM.sh b/tests/rm/many-dir-entries-vs-OOM.sh new file mode 100755 index 0000000..c3ff97f --- /dev/null +++ b/tests/rm/many-dir-entries-vs-OOM.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# In coreutils-8.12, rm,du,chmod, etc. would use too much memory +# when processing a directory with many entries (as in > 100,000). + +# Copyright (C) 2011-2022 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_ rm du chmod +expensive_ + +mkdir d2 \ + && touch d2/f || framework_failure_ + +# Restrict memory. Each of these coreutils-8.12 programs would fail +# with a diagnostic like "rm: fts_read failed: Cannot allocate memory". +vm=$(get_min_ulimit_v_ du -sh d2) \ + || skip_ "this shell lacks ulimit support" + +# With many files in a single directory... +mkdir d || framework_failure_ +seq --format="d/%06g" 200000 | xargs touch || framework_failure_ + +# Allow 35MiB more memory as for the trivial case above. +(ulimit -v $(($vm + 35000)) && du -sh d) || fail=1 + +vm=$(get_min_ulimit_v_ chmod -R 700 d2) \ + || skip_ "this shell lacks ulimit support" +(ulimit -v $(($vm + 35000)) && chmod -R 700 d) || fail=1 + +vm=$(get_min_ulimit_v_ rm -rf d2) \ + || skip_ "this shell lacks ulimit support" +(ulimit -v $(($vm + 35000)) && rm -rf d) || fail=1 + +Exit $fail diff --git a/tests/rm/no-give-up.sh b/tests/rm/no-give-up.sh new file mode 100755 index 0000000..bf81914 --- /dev/null +++ b/tests/rm/no-give-up.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# With rm from coreutils-5.2.1 and earlier, 'rm -r' would mistakenly +# give up too early under some conditions. + +# Copyright (C) 2004-2022 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_ rm +require_root_ + +mkdir d || framework_failure_ +touch d/f || framework_failure_ +chown -R $NON_ROOT_USERNAME d || framework_failure_ + +# Ensure that non-root can access files in root-owned ".". +chmod go=x . || framework_failure_ + + +# This must fail, since '.' is not writable by $NON_ROOT_USERNAME. +returns_ 1 chroot --skip-chdir --user=$NON_ROOT_USERNAME / env PATH="$PATH" \ + rm -rf d 2>/dev/null || fail=1 + +# d must remain. +test -d d || fail=1 + +# f must have been removed. +test -f d/f && fail=1 + +Exit $fail diff --git a/tests/rm/one-file-system.sh b/tests/rm/one-file-system.sh new file mode 100755 index 0000000..c171fab --- /dev/null +++ b/tests/rm/one-file-system.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# Demonstrate rm's new --one-file-system option. + +# Copyright (C) 2006-2022 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_ rm +require_root_ + +cleanup_() +{ + # When you take the undesirable shortcut of making /etc/mtab a link + # to /proc/mounts, unmounting "$other_partition_tmpdir" would fail. + # So, here we unmount a/b instead. + umount a/b + rm -rf "$other_partition_tmpdir" +} +. "$abs_srcdir/tests/other-fs-tmpdir" + +t=$other_partition_tmpdir +mkdir -p a/b $t/y +mount --bind $t a/b \ + || skip_ "This test requires mount with a working --bind option." + +cat <<\EOF > exp || framework_failure_ +rm: skipping 'a/b', since it's on a different device +EOF +returns_ 1 rm --one-file-system -rf a 2> out || fail=1 +test -d $t/y || fail=1 +compare exp out || fail=1 + +cat <<\EOF >> exp || framework_failure_ +rm: and --preserve-root=all is in effect +EOF +returns_ 1 rm --preserve-root=all -rf a/b 2>out || fail=1 +compare exp out || fail=1 + +Exit $fail diff --git a/tests/rm/one-file-system2.sh b/tests/rm/one-file-system2.sh new file mode 100755 index 0000000..acd520d --- /dev/null +++ b/tests/rm/one-file-system2.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# Verify --one-file-system does delete within a file system + +# Copyright (C) 2009-2022 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_ rm + +mkdir -p a/b + +rm --one-file-system -rf a || fail=1 +test -d a && fail=1 + +Exit $fail diff --git a/tests/rm/r-1.sh b/tests/rm/r-1.sh new file mode 100755 index 0000000..8c9cc28 --- /dev/null +++ b/tests/rm/r-1.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# Test "rm -r --verbose". + +# Copyright (C) 1997-2022 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_ rm + +mkdir a a/a || framework_failure_ +> b || framework_failure_ + +cat <<\EOF > exp || framework_failure_ +removed directory 'a/a' +removed directory 'a' +removed 'b' +EOF + +rm --verbose -r a b > out || fail=1 + +for d in $dirs; do + test -d $d && fail=1 +done + +# Compare expected and actual output. +compare exp out || fail=1 + +Exit $fail diff --git a/tests/rm/r-2.sh b/tests/rm/r-2.sh new file mode 100755 index 0000000..8cd46b3 --- /dev/null +++ b/tests/rm/r-2.sh @@ -0,0 +1,45 @@ +#!/bin/sh +# Test "rm -r --verbose". + +# Copyright (C) 1997-2022 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_ rm + +mkdir t t/a t/a/b || framework_failure_ +> t/a/f || framework_failure_ +> t/a/b/g || framework_failure_ + +# FIXME: if this fails, it's a framework failure +cat <<\EOF | sort > t/E || framework_failure_ +removed directory 't/a' +removed directory 't/a/b' +removed 't/a/b/g' +removed 't/a/f' +EOF + +# Note that both the expected output (above) and the actual output lines +# are sorted, because directory entries may be processed in arbitrary order. +rm --verbose -r t/a | sort > t/O || fail=1 + +if test -d t/a; then + fail=1 +fi + +# Compare expected and actual output. +cmp t/E t/O || fail=1 + +Exit $fail diff --git a/tests/rm/r-3.sh b/tests/rm/r-3.sh new file mode 100755 index 0000000..b3feb61 --- /dev/null +++ b/tests/rm/r-3.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# Create and remove a directory with more than 254 files. + +# Copyright (C) 1997-2022 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/>. + + +# An early version of my rewritten rm failed to remove all of +# the files on SunOS4 when there were 254 or more in a directory. + +# And the rm from coreutils-5.0 exposes the same problem when there +# are 338 or more files in a directory on a Darwin-6.5 system + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ rm + +mkdir t || framework_failure_ +cd t || framework_failure_ + +# Create 500 files (20 * 25). +for i in 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j; do + files= + for j in a b c d e f g h i j k l m n o p q r s t u v w x y; do + files="$files $i$j" + done + touch $files || framework_failure_ +done + +test -f 0a || framework_failure_ +test -f by || framework_failure_ +cd .. || framework_failure_ + +rm -rf t || fail=1 +test -d t && fail=1 + +Exit $fail diff --git a/tests/rm/r-4.sh b/tests/rm/r-4.sh new file mode 100755 index 0000000..94859f4 --- /dev/null +++ b/tests/rm/r-4.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# Try to remove '.' and '..' recursively. + +# Copyright (C) 2006-2022 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_ rm + +mkdir d || framework_failure_ +touch d/a || framework_failure_ + +# Expected error diagnostic as grep pattern. +exp="^rm: refusing to remove '\.' or '\.\.' directory: skipping '.*'\$" + +rmtest() +{ + # Try removing - expecting failure. + rm -fr "$1" 2> err && fail=1 + + # Ensure the expected error diagnostic is output. + grep "$exp" err || { cat err; fail=1; } + + return $fail +} + +rmtest 'd/.' || fail=1 +rmtest 'd/./' || fail=1 +rmtest 'd/.////' || fail=1 +rmtest 'd/..' || fail=1 +rmtest 'd/../' || fail=1 + + +# This test is handled more carefully in r-root.sh +# returns_ 1 rm -fr / 2>/dev/null || fail=1 + +test -f d/a || fail=1 + +Exit $fail diff --git a/tests/rm/r-root.sh b/tests/rm/r-root.sh new file mode 100755 index 0000000..742d7a2 --- /dev/null +++ b/tests/rm/r-root.sh @@ -0,0 +1,320 @@ +#!/bin/sh +# Try to remove '/' recursively. + +# Copyright (C) 2013-2022 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_ rm + +# POSIX mandates rm(1) to skip '/' arguments. This test verifies this mandated +# behavior as well as the --preserve-root and --no-preserve-root options. +# Especially the latter case is a live fire exercise as rm(1) is supposed to +# enter the unlinkat() system call. Therefore, limit the risk as much +# as possible -- if there's a bug this test would wipe the system out! + +# Faint-hearted: skip this test for the 'root' user. +skip_if_root_ + +# Pull the teeth from rm(1) by intercepting the unlinkat() system call via the +# LD_PRELOAD environment variable. This requires shared libraries to work. +require_gcc_shared_ + +# Ensure this variable is unset as it's +# used later in the unlinkat() wrapper. +unset CU_TEST_SKIP_EXIT + +# Set this to 0 if you don't have a working gdb but would +# still like to run the test +USE_GDB=1 + +if test $USE_GDB = 1; then + # Use gdb to provide further protection by limiting calls to unlinkat(). + ( timeout 10s gdb --version ) > gdb.out 2>&1 + case $(cat gdb.out) in + *'GNU gdb'*) ;; + *) skip_ "can't run gdb";; + esac +fi + +# Break on a line rather than a symbol, to cater for inline functions +break_src="$abs_top_srcdir/src/remove.c" +break_line=$(grep -n ^excise "$break_src") || framework_failure_ +break_line=$(echo "$break_line" | cut -d: -f1) || framework_failure_ +break_line="$break_src:$break_line" + + +cat > k.c <<'EOF' || framework_failure_ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +int unlinkat (int dirfd, const char *pathname, int flags) +{ + /* Prove that LD_PRELOAD works: create the evidence file "x". */ + fclose (fopen ("x", "w")); + + /* Immediately terminate, unless indicated otherwise. */ + if (! getenv("CU_TEST_SKIP_EXIT")) + _exit (0); + + /* Pretend success. */ + return 0; +} +EOF + +# Then compile/link it: +gcc_shared_ k.c k.so \ + || framework_failure_ 'failed to build shared library' + +# Note breakpoint commands don't work in batch mode +# https://sourceware.org/bugzilla/show_bug.cgi?id=10079 +# So we use python to script behavior upon hitting the breakpoint +cat > bp.py <<'EOF.py' || framework_failure_ +def breakpoint_handler (event): + if not isinstance(event, gdb.BreakpointEvent): + return + hit_count = event.breakpoints[0].hit_count + if hit_count == 1: + gdb.execute('shell touch excise.break') + gdb.execute('continue') + elif hit_count > 2: + gdb.write('breakpoint hit twice already') + gdb.execute('quit 1') + else: + gdb.execute('continue') + +gdb.events.stop.connect(breakpoint_handler) +EOF.py + +# In order of the sed expressions below, this cleans: +# +# 1. gdb uses the full path when running rm, so remove the leading dirs. +# 2. For some of the "/" synonyms, the error diagnostic slightly differs from +# that of the basic "/" case (see gnulib's fts_open' and ROOT_DEV_INO_WARN): +# rm: it is dangerous to operate recursively on 'FILE' (same as '/') +# Strip that part off for the following comparison. +clean_rm_err_() +{ + sed "s/.*rm: /rm: /; \ + s/\(rm: it is dangerous to operate recursively on\).*$/\1 '\/'/" +} + +#------------------------------------------------------------------------------- +# exercise_rm_r_root: shell function to test "rm -r '/'" +# The caller must provide the FILE to remove as well as any options +# which should be passed to 'rm'. +# Paranoia mode on: +# For the worst case where both rm(1) would fail to refuse to process the "/" +# argument (in the cases without the --no-preserve-root option), and +# intercepting the unlinkat(1) system call would fail (which actually already +# has been proven to work above), and the current non root user has +# write access to "/", limit the damage to the current file system via +# the --one-file-system option. +# Furthermore, run rm(1) via gdb that limits the number of unlinkat() calls. +exercise_rm_r_root () +{ + # Remove the evidence files; verify that. + rm -f x excise.break || framework_failure_ + test -f x && framework_failure_ + test -f excise.break && framework_failure_ + + local skip_exit= + if [ "$CU_TEST_SKIP_EXIT" = 1 ]; then + # Pass on this variable into 'rm's environment. + skip_exit='CU_TEST_SKIP_EXIT=1' + fi + + if test $USE_GDB = 1; then + gdb -nx --batch-silent -return-child-result \ + --eval-command="set exec-wrapper \ + env 'LD_PRELOAD=$LD_PRELOAD:./k.so' $skip_exit" \ + --eval-command="break '$break_line'" \ + --eval-command='source bp.py' \ + --eval-command="run -rv --one-file-system $*" \ + --eval-command='quit' \ + rm < /dev/null > out 2> err.t + else + touch excise.break + env LD_PRELOAD=$LD_PRELOAD:./k.so $skip_exit \ + rm -rv --one-file-system $* < /dev/null > out 2> err.t + fi + + ret=$? + + clean_rm_err_ < err.t > err || ret=$? + + return $ret +} + +# Verify that "rm -r dir" basically works. +mkdir dir || framework_failure_ +rm -r dir || framework_failure_ +test -d dir && framework_failure_ + +# Now verify that intercepting unlinkat() works: +# rm(1) must succeed as before, but this time both the evidence file "x" +# and the test file / directory must still exist afterward. +mkdir dir || framework_failure_ +> file || framework_failure_ + +skip= +for file in dir file ; do + exercise_rm_r_root "$file" || skip=1 + test -e "$file" || skip=1 + test -f x || skip=1 + test -f excise.break || skip=1 # gdb works and breakpoint hit + compare /dev/null err || skip=1 + + test "$skip" = 1 \ + && { cat out; cat err; \ + skip_ "internal test failure: maybe LD_PRELOAD or gdb doesn't work?"; } +done + +# "rm -r /" without --no-preserve-root should output the following +# diagnostic error message. +cat <<EOD > exp || framework_failure_ +rm: it is dangerous to operate recursively on '/' +rm: use --no-preserve-root to override this failsafe +EOD + +#------------------------------------------------------------------------------- +# Exercise "rm -r /" without and with the --preserve-root option. +# Exercise various synonyms of "/" including symlinks to it. +# Expect a non-Zero exit status. +# Prepare a few symlinks to "/". +ln -s / rootlink || framework_failure_ +ln -s rootlink rootlink2 || framework_failure_ +ln -sr / rootlink3 || framework_failure_ + +for opts in \ + '/' \ + '--preserve-root /' \ + '//' \ + '///' \ + '////' \ + 'rootlink/' \ + 'rootlink2/' \ + 'rootlink3/' ; do + + returns_ 1 exercise_rm_r_root $opts || fail=1 + + # Expect nothing in 'out' and the above error diagnostic in 'err'. + # As rm(1) should have skipped the "/" argument, it does not call unlinkat(). + # Therefore, the evidence file "x" should not exist. + compare /dev/null out || fail=1 + compare exp err || fail=1 + test -f x && fail=1 + + # Do nothing more if this test failed. + test $fail = 1 && { cat out; cat err; Exit $fail; } +done + +#------------------------------------------------------------------------------- +# Exercise with --no-preserve to ensure shortened equivalent is not allowed. +cat <<EOD > exp_opt || framework_failure_ +rm: you may not abbreviate the --no-preserve-root option +EOD +returns_ 1 exercise_rm_r_root --no-preserve / || fail=1 +compare exp_opt err || fail=1 +test -f x && fail=1 + +#------------------------------------------------------------------------------- +# Exercise "rm -r file1 / file2". +# Expect a non-Zero exit status representing failure to remove "/", +# yet 'file1' and 'file2' should be removed. +> file1 || framework_failure_ +> file2 || framework_failure_ + +# Now that we know that 'rm' won't call the unlinkat() system function for "/", +# we could probably execute it without the LD_PRELOAD'ed safety net. +# Nevertheless, it's still better to use it for this test. +# Tell the unlinkat() replacement function to not _exit(0) immediately +# by setting the following variable. +CU_TEST_SKIP_EXIT=1 + +returns_ 1 exercise_rm_r_root --preserve-root file1 '/' file2 || fail=1 + +unset CU_TEST_SKIP_EXIT + +cat <<EOD > out_removed +removed 'file1' +removed 'file2' +EOD + +# The above error diagnostic should appear in 'err'. +# Both 'file1' and 'file2' should be removed. Simply verify that in the +# "out" file, as the replacement unlinkat() dummy did not remove them. +# Expect the evidence file "x" to exist. +compare out_removed out || fail=1 +compare exp err || fail=1 +test -f x || fail=1 + +# Do nothing more if this test failed. +test $fail = 1 && { cat out; cat err; Exit $fail; } + +#------------------------------------------------------------------------------- +# Exercise various synonyms of "/" having a trailing "." or ".." in the name. +# This triggers another check in the code first and therefore leads to a +# different diagnostic. However, we want to test anyway to protect against +# future reordering of the checks in the code. +# Expect that other error diagnostic in 'err' and nothing in 'out'. +# Expect a non-Zero exit status. The evidence file "x" should not exist. +for file in \ + '//.' \ + '/./' \ + '/.//' \ + '/../' \ + '/.././' \ + '/etc/..' \ + 'rootlink/..' \ + 'rootlink2/.' \ + 'rootlink3/./' ; do + + test -d "$file" || continue # if e.g. /etc does not exist. + + returns_ 1 exercise_rm_r_root --preserve-root "$file" || fail=1 + + grep "rm: refusing to remove '\.' or '\.\.' directory: skipping" err \ + || fail=1 + + compare /dev/null out || fail=1 + test -f x && fail=1 + + # Do nothing more if this test failed. + test $fail = 1 && { cat out; cat err; Exit $fail; } +done + +#------------------------------------------------------------------------------- +# Until now, it was all just fun. +# Now exercise the --no-preserve-root option with which rm(1) should enter +# the intercepted unlinkat() system call. +# As the interception code terminates the process immediately via _exit(0), +# the exit status should be 0. +# Use the option --interactive=never to bypass the following prompt: +# "rm: descend into write-protected directory '/'?" +exercise_rm_r_root --interactive=never --no-preserve-root '/' \ + || fail=1 + +# The 'err' file should not contain the above error diagnostic. +grep "rm: it is dangerous to operate recursively on '/'" err && fail=1 + +# Instead, rm(1) should have called the intercepted unlinkat() function, +# i.e., the evidence file "x" should exist. +test -f x || fail=1 + +test $fail = 1 && { cat out; cat err; } + +Exit $fail diff --git a/tests/rm/read-only.sh b/tests/rm/read-only.sh new file mode 100755 index 0000000..adf5741 --- /dev/null +++ b/tests/rm/read-only.sh @@ -0,0 +1,52 @@ +#!/bin/sh +# Ensure that rm -f nonexistent-file-on-read-only-fs succeeds. + +# Copyright (C) 2009-2022 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_ rm +require_root_ + +cwd=$(pwd) +cleanup_() { cd /; umount "$cwd/mnt"; } + +skip=0 +# Create a file system, then mount it. +dd if=/dev/zero of=blob bs=8192 count=200 > /dev/null 2>&1 \ + || skip=1 +mkdir mnt || skip=1 +mkfs -t ext2 -F blob \ + || skip_ "failed to create ext2 file system" + +mount -oloop blob mnt || skip=1 +echo test > mnt/f || skip=1 +test -s mnt/f || skip=1 +mount -o remount,loop,ro mnt || skip=1 + +test $skip = 1 \ + && skip_ "insufficient mount/ext2 support" + +# Applying rm -f to a nonexistent file on a read-only file system must succeed. +rm -f mnt/no-such > out 2>&1 || fail=1 +# It must produce no diagnostic. +compare /dev/null out || fail=1 + +# However, trying to remove an existing file must fail. +rm -f mnt/f > out 2>&1 && fail=1 +# with a diagnostic. +compare /dev/null out && fail=1 + +Exit $fail diff --git a/tests/rm/readdir-bug.sh b/tests/rm/readdir-bug.sh new file mode 100755 index 0000000..43fdc07 --- /dev/null +++ b/tests/rm/readdir-bug.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# Exercise the Darwin/MacOS bug worked around on 2006-09-29, +# whereby rm would fail to remove all entries in a directory. + +# Copyright (C) 2006-2022 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_ rm + +# Create a directory containing many files. +# What counts is a combination of the number of files and +# the lengths of their names. For details, see +# https://lists.gnu.org/r/bug-coreutils/2006-09/msg00326.html +mkdir b || framework_failure_ +cd b || framework_failure_ +for i in $(seq 1 250); do + touch $(printf %040d $i) || framework_failure_ +done +cd .. || framework_failure_ + + +# On a buggy system, this would fail with the diagnostic, +# "cannot remove directory 'b': Directory not empty" +rm -rf b || fail=1 + +test -d b && fail=1 + +Exit $fail diff --git a/tests/rm/rm-readdir-fail.sh b/tests/rm/rm-readdir-fail.sh new file mode 100755 index 0000000..48d71f7 --- /dev/null +++ b/tests/rm/rm-readdir-fail.sh @@ -0,0 +1,120 @@ +#!/bin/sh +# Test rm's behaviour when the directory cannot be read. +# This test is skipped on systems that lack LD_PRELOAD support. + +# Copyright (C) 2016-2022 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_ rm +require_gcc_shared_ + +mkdir -p dir/notempty || framework_failure_ + +# Simulate "readdir" failure. +cat > k.c <<\EOF || framework_failure_ +#define _GNU_SOURCE + +/* Setup so we don't have to worry about readdir64. */ +#ifndef __LP64__ +# define _FILE_OFFSET_BITS 64 +#endif + +#include <dlfcn.h> +#include <dirent.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +struct dirent *readdir (DIR *dirp) +{ + static int count = 1; + +#ifndef __LP64__ + if (count == 1) + fclose (fopen ("32bit", "w")); + errno = ENOSYS; + return NULL; +#endif + + static struct dirent *(*real_readdir)(DIR *dirp); + if (! real_readdir && ! (real_readdir = dlsym (RTLD_NEXT, "readdir"))) + { + fprintf (stderr, "Failed to find readdir()\n"); + errno = ESRCH; + return NULL; + } + struct dirent* d; + if (! (d = real_readdir (dirp))) + { + fprintf (stderr, "Failed to get dirent\n"); + errno = ENOENT; + return NULL; + } + + /* Flag that LD_PRELOAD and above functions work. */ + if (count == 1) + fclose (fopen ("preloaded", "w")); + + /* Return some entries to trigger partial read failure, + ensuring we don't return ignored '.' or '..' */ + char const *readdir_partial = getenv ("READDIR_PARTIAL"); + if (readdir_partial && *readdir_partial && count <= 3) + { + count++; + d->d_name[0]='0'+count; d->d_name[1]='\0'; +#ifdef _DIRENT_HAVE_D_NAMLEN + d->d_namlen = 2; +#endif + errno = 0; + return d; + }; + + /* Fail. */ + errno = ENOENT; + return NULL; +} +EOF + +# Then compile/link it: +gcc_shared_ k.c k.so \ + || framework_failure_ 'failed to build shared library' + +# Test if LD_PRELOAD works: +export READDIR_PARTIAL +for READDIR_PARTIAL in '' '1'; do + rm -f preloaded + (export LD_PRELOAD=$LD_PRELOAD:./k.so + returns_ 1 rm -Rf dir 2>>errt) || fail=1 + if test -f 32bit; then + skip_ 'This test only supports 64 bit systems' + elif ! test -f preloaded; then + cat errt + skip_ "internal test failure: maybe LD_PRELOAD doesn't work?" + fi +done + +# First case is failure to read any items from dir, then assume empty. +# Generally that will be diagnosed when rm tries to rmdir(). +# Second case is more general error where we fail immediately +# (with ENOENT in this case but it could be anything). +cat <<EOF > exp +rm: cannot remove 'dir' +rm: traversal failed: dir +EOF +sed 's/\(rm:.*\):.*/\1/' errt > err || framework_failure_ +compare exp err || fail=1 + +Exit $fail diff --git a/tests/rm/rm1.sh b/tests/rm/rm1.sh new file mode 100755 index 0000000..f37138b --- /dev/null +++ b/tests/rm/rm1.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# exercise another small part of remove.c + +# Copyright (C) 2002-2022 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_ rm +skip_if_root_ + +mkdir -p b/a/p b/c b/d || framework_failure_ +chmod ug-w b/a || framework_failure_ + + +# This should fail. +rm -rf b > out 2>&1 && fail=1 +cat <<\EOF > exp +rm: cannot remove directory 'b/a/p': Permission denied +EOF + +# On some systems, rm doesn't have enough information to +# say it's a directory. +cat <<\EOF > exp2 +rm: cannot remove 'b/a/p': Permission denied +EOF + +cmp out exp > /dev/null 2>&1 || { + cmp out exp2 || fail=1 + } +test $fail = 1 && compare exp out + +test -d b/a/p || fail=1 +test -d b/c && fail=1 +test -d b/d && fail=1 + +Exit $fail diff --git a/tests/rm/rm2.sh b/tests/rm/rm2.sh new file mode 100755 index 0000000..23a1d06 --- /dev/null +++ b/tests/rm/rm2.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# exercise another small part of remove.c + +# Copyright (C) 2002-2022 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_ rm +skip_if_root_ + +mkdir -p a/0 || framework_failure_ +mkdir -p a/1/2 b/3 || framework_failure_ +mkdir a/2 a/3 || framework_failure_ +chmod u-x a/1 b || framework_failure_ + + +# Exercise two separate code paths -- though both result +# in the same sort of diagnostic. +# Both of these should fail. +rm -rf a b > out 2>&1 && fail=1 +cat <<\EOF > exp +rm: cannot remove 'a/1': Permission denied +rm: cannot remove 'b': Permission denied +EOF + +cat <<\EOF > exp-solaris +rm: cannot remove 'a/1/2': Permission denied +rm: cannot remove 'b/3': Permission denied +EOF + +cmp out exp > /dev/null 2>&1 \ + || { cmp out exp-solaris > /dev/null 2>&1 || fail=1; } +test $fail = 1 && compare exp out + +test -d a/0 && fail=1 +test -d a/1 || fail=1 +test -d a/2 && fail=1 +test -d a/3 && fail=1 + +chmod u+x b +test -d b/3 || fail=1 + +Exit $fail diff --git a/tests/rm/rm3.sh b/tests/rm/rm3.sh new file mode 100755 index 0000000..8eb84bc --- /dev/null +++ b/tests/rm/rm3.sh @@ -0,0 +1,73 @@ +#!/bin/sh +# exercise another small part of remove.c + +# Copyright (C) 2002-2022 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_ rm +skip_if_root_ + +mkdir -p z || framework_failure_ +cd z || framework_failure_ +touch empty empty-u || framework_failure_ +echo not-empty > fu +ln -s empty-f slink +ln -s . slinkdot +mkdir d du || framework_failure_ +chmod u-w fu du empty-u || framework_failure_ +cd .. + + +cat <<EOF > in || framework_failure_ +y +y +y +y +y +y +y +y +y +EOF + +# Both of these should fail. +rm -ir z < in > out 2>&1 || fail=1 + +# Given input like 'rm: ...? rm: ...? ' (no trailing newline), +# the 'head...' part of the pipeline below removes the trailing space, so +# that sed doesn't have to deal with a line lacking a terminating newline. +# This avoids a bug whereby some vendor-provided (Tru64) versions of sed +# would mistakenly tack a newline onto the end of the output. +tr '?' '\n' < out | head --bytes=-1 | sed 's/^ //' |sort > o2 +mv o2 out || framework_failure_ + +sort <<EOF > exp || framework_failure_ +rm: descend into directory 'z' +rm: remove regular empty file 'z/empty' +rm: remove write-protected regular file 'z/fu' +rm: remove write-protected regular empty file 'z/empty-u' +rm: remove symbolic link 'z/slink' +rm: remove symbolic link 'z/slinkdot' +rm: remove directory 'z/d' +rm: remove write-protected directory 'z/du' +rm: remove directory 'z' +EOF + +compare exp out || fail=1 + +test -d z && fail=1 + +Exit $fail diff --git a/tests/rm/rm4.sh b/tests/rm/rm4.sh new file mode 100755 index 0000000..ae08c98 --- /dev/null +++ b/tests/rm/rm4.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# ensure that 'rm dir' fails without --recursive + +# Copyright (C) 2002-2022 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_ rm +skip_if_root_ + +mkdir dir || framework_failure_ + + +# This should fail. +returns_ 1 rm dir > /dev/null 2>&1 || fail=1 + +test -d dir || fail=1 + +Exit $fail diff --git a/tests/rm/rm5.sh b/tests/rm/rm5.sh new file mode 100755 index 0000000..e7fd3ef --- /dev/null +++ b/tests/rm/rm5.sh @@ -0,0 +1,52 @@ +#!/bin/sh +# a basic test of rm -ri + +# Copyright (C) 2002-2022 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_ rm +skip_if_root_ + +mkdir -p d/e || framework_failure_ +cat <<EOF > in || framework_failure_ +y +y +y +EOF + +cat <<\EOF > exp || framework_failure_ +rm: descend into directory 'd' +rm: remove directory 'd/e' +rm: remove directory 'd' +EOF + + +rm -ir d < in > out 2>&1 || fail=1 + +# Given input like 'rm: ...? rm: ...? ' (no trailing newline), +# the 'head...' part of the pipeline below removes the trailing space, so +# that sed doesn't have to deal with a line lacking a terminating newline. +# This avoids a bug whereby some vendor-provided (Tru64) versions of sed +# would mistakenly tack a newline onto the end of the output. +tr '?' '\n' < out | head --bytes=-1 | sed 's/^ //' > o2 +mv o2 out + +# Make sure it's been removed. +test -d d && fail=1 + +compare exp out || fail=1 + +Exit $fail diff --git a/tests/rm/sunos-1.sh b/tests/rm/sunos-1.sh new file mode 100755 index 0000000..15e82e9 --- /dev/null +++ b/tests/rm/sunos-1.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# Make sure that rm -r '' fails. + +# Copyright (C) 1997-2022 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/>. + + +# On SunOS 4.1.3, running rm -r '' in a nonempty directory may +# actually remove files with names of entries in the current directory +# but relative to '/' rather than relative to the current directory. + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ rm + +returns_ 1 rm -r '' > /dev/null 2>&1 || fail=1 + +Exit $fail diff --git a/tests/rm/unread2.sh b/tests/rm/unread2.sh new file mode 100755 index 0000000..50e809d --- /dev/null +++ b/tests/rm/unread2.sh @@ -0,0 +1,35 @@ +#!/bin/sh +# exercise one small part of remove.c + +# Copyright (C) 2002-2022 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_ rm +skip_if_root_ + +mkdir -p a/b || framework_failure_ +chmod u-r a + + +# This should fail. +rm -rf a > out 2>&1 && fail=1 +cat <<\EOF > exp +rm: cannot remove 'a': Permission denied +EOF + +compare exp out || fail=1 + +Exit $fail diff --git a/tests/rm/unread3.sh b/tests/rm/unread3.sh new file mode 100755 index 0000000..c1f2830 --- /dev/null +++ b/tests/rm/unread3.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# Ensure that rm works even from an unreadable working directory. + +# Copyright (C) 2004-2022 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_ rm +skip_if_root_ + +mkdir -p a/1 b c d/2 e/3 || framework_failure_ + + +t=$(pwd) +cd c +chmod u=x,go= . + +# With coreutils-5.2.1, this would get a failed assertion. +rm -r "$t/a" "$t/b" || fail=1 + +# With coreutils-5.2.1, this would get the following: +# rm: cannot get current directory: Permission denied +# rm: failed to return to initial working directory: Bad file descriptor +rm -r "$t/d" "$t/e" || fail=1 + +test -d "$t/a" && fail=1 +test -d "$t/b" && fail=1 +test -d "$t/d" && fail=1 +test -d "$t/e" && fail=1 + +Exit $fail diff --git a/tests/rm/unreadable.pl b/tests/rm/unreadable.pl new file mode 100755 index 0000000..ae79620 --- /dev/null +++ b/tests/rm/unreadable.pl @@ -0,0 +1,50 @@ +#!/usr/bin/perl +# Test "rm" and unreadable directories. + +# Copyright (C) 1998-2022 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/>. + +use strict; + +(my $program_name = $0) =~ s|.*/||; + +# Turn off localization of executable's output. +@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3; + +my $d = "dir-$$"; +my $mkdir = {PRE => sub {mkdir $d,0100 or die "$d: $!\n"}}; +my $prog = 'rm'; +my $uid = $<; + +my @Tests = + ( + # test-name options input expected-output + # + # Removing an empty, unwritable directory succeeds. + ['unreadable-1', '-rf', $d, {EXIT => 0}, $mkdir], + + ['unreadable-2', '-rf', $d, + {EXIT => $uid == 0 ? 0 : 1}, + {ERR => $uid == 0 ? '' + : "$prog: cannot remove '$d': Permission denied\n"}, + {PRE => sub { (mkdir $d,0700 and mkdir "$d/x",0700 and chmod 0100,$d) + or die "$d: $!\n"}} ], + ); + +my $save_temps = $ENV{SAVE_TEMPS}; +my $verbose = $ENV{VERBOSE}; + +my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose); +exit $fail; diff --git a/tests/rm/v-slash.sh b/tests/rm/v-slash.sh new file mode 100755 index 0000000..6b82633 --- /dev/null +++ b/tests/rm/v-slash.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# avoid extra slashes in --verbose output + +# Copyright (C) 2007-2022 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_ rm + +mkdir a || framework_failure_ +touch a/x || framework_failure_ + + +rm --verbose -r a/// > out || fail=1 +cat <<\EOF > exp || framework_failure_ +removed 'a/x' +removed directory 'a/' +EOF + +compare exp out || fail=1 + +Exit $fail diff --git a/tests/rmdir/fail-perm.sh b/tests/rmdir/fail-perm.sh new file mode 100755 index 0000000..6bc29d7 --- /dev/null +++ b/tests/rmdir/fail-perm.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# For unwritable directory 'd', 'rmdir -p d d/e/f' would emit +# diagnostics but would not fail. Fixed in 5.1.2. + +# Copyright (C) 2004-2022 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_ rmdir + +mkdir d d/e d/e/f || framework_failure_ +chmod a-w d || framework_failure_ + + +# This rmdir command outputs two diagnostics. +# Before coreutils-5.1.2, it would mistakenly exit successfully. +# As of coreutils-5.1.2, it fails, as required. +returns_ 1 rmdir -p d d/e/f 2> /dev/null || fail=1 + +Exit $fail diff --git a/tests/rmdir/ignore.sh b/tests/rmdir/ignore.sh new file mode 100755 index 0000000..49cf8a7 --- /dev/null +++ b/tests/rmdir/ignore.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# make sure rmdir's --ignore-fail-on-non-empty option works + +# Copyright (C) 1999-2022 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_ rmdir + +cwd=$(pwd) +mkdir -p "$cwd/a/b/c" "$cwd/a/x" || framework_failure_ + +rmdir -p --ignore-fail-on-non-empty "$cwd/a/b/c" || fail=1 +# $cwd/a/x should remain +test -d "$cwd/a/x" || fail=1 +# $cwd/a/b and $cwd/a/b/c should be gone +test -d "$cwd/a/b" && fail=1 +test -d "$cwd/a/b/c" && fail=1 + +# Ensure that with --ignore-fail-on-non-empty, we still fail, e.g., for EPERM. +# Between 6.11 and 8.31, the following rmdir would mistakenly succeed. +mkdir -p x/y || framework_failure_ +chmod a-w x || framework_failure_ + +if ! uid_is_privileged_; then # root does not get EPERM. + returns_ 1 rmdir --ignore-fail-on-non-empty x/y || fail=1 +fi + +test -d x/y || fail=1 +# Between 6.11 and 8.31, the following rmdir would mistakenly fail, +# and also give a non descript error +touch x/y/z || framework_failure_ +rmdir --ignore-fail-on-non-empty x/y || fail=1 +test -d x/y || fail=1 + +if ! uid_is_privileged_; then # root does not get EPERM. + # assume empty dir if unreadable entries (so failure to remove diagnosed) + rm x/y/z || framework_failure_ + chmod a-r x/y || framework_failure_ + returns_ 1 rmdir --ignore-fail-on-non-empty x/y || fail=1 + test -d x/y || fail=1 +fi + +Exit $fail diff --git a/tests/rmdir/symlink-errors.sh b/tests/rmdir/symlink-errors.sh new file mode 100755 index 0000000..68f4479 --- /dev/null +++ b/tests/rmdir/symlink-errors.sh @@ -0,0 +1,66 @@ +#!/bin/sh +# make sure rmdir outputs clear errors in the presence of symlinks + +# Copyright (C) 2021-2022 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_ rmdir + +mkdir dir || framework_failure_ +ln -s dir sl || framework_failure_ +ln -s missing dl || framework_failure_ +touch file || framework_failure_ +ln -s file fl || framework_failure_ + +# Ensure a we maintain ENOTDIR so that we provide +# accurate errors on systems on which rmdir(2) does following the symlink/ +returns_ 1 rmdir fl/ 2> err || fail=1 +# Ensure we diagnose symlink behavior. +printf '%s\n' "rmdir: failed to remove 'fl/': Not a directory" > exp +compare exp err || fail=1 + +# Also ensure accurate errors from rmdir -p when traversing symlinks +# Up to and including 8.32 rmdir would fail like: +# rmdir: failed to remove directory 'sl': Not a directory +mkdir dir/dir2 || framework_failure_ +returns_ 1 rmdir -p sl/dir2 2> err || fail=1 +# Ensure we diagnose symlink behavior. +printf '%s\n' "rmdir: failed to remove 'sl': Not a directory" > exp +compare exp err || fail=1 + +# Only perform the following on systems that don't follow the symlink +if ! rmdir sl/ 2>/dev/null; then + # Up to and including 8.32 rmdir would fail like: + # rmdir: failed to remove 'sl/': Not a directory + # That's inconsistent though as rm sl/ gives: + # rm: cannot remove 'sl/': Is a directory + # Also this is inconsistent with other systems + # which do follow the symlink and rmdir the target. + + new_error="rmdir: failed to remove '%s': Symbolic link not followed\\n" + + # Ensure we diagnose symlink behavior. + returns_ 1 rmdir sl/ 2> err || fail=1 + printf "$new_error" 'sl/' > exp || framework_failure_ + compare exp err || fail=1 + + # Ensure a consistent diagnosis for dangling symlinks etc. + returns_ 1 rmdir dl/ 2> err || fail=1 + printf "$new_error" 'dl/' > exp || framework_failure_ + compare exp err || fail=1 +fi + +Exit $fail diff --git a/tests/rmdir/t-slash.sh b/tests/rmdir/t-slash.sh new file mode 100755 index 0000000..033fc40 --- /dev/null +++ b/tests/rmdir/t-slash.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# make sure rmdir -p works on a directory specified with a trailing slash + +# Copyright (C) 2002-2022 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_ rmdir + +mkdir dir || framework_failure_ + + +# Prior to coreutils-4.5.2, this would fail with the following: +# rmdir: 'dir': No such file or directory +rmdir -p dir/ || fail=1 + +Exit $fail |