new feature: rm accepts new option: --one-file-system

Suggested by Steve McIntyre in <http://bugs.debian.org/392925>.
* src/remove.h (struct rm_options) [one_file_system]: New member.
* src/rm.c (rm_option_init): Initialize it.
(usage): Document the option.
* src/mv.c (rm_option_init): Likewise.
* src/remove.c (remove_dir): With --one-file-system and --recursive,
for each directory command line argument, do not affect a file system
different from that of the starting directory.  And give a diagnostic.
* src/rm.c (ONE_FILE_SYSTEM): New enum.
(main): Handle new option.
* tests/rm/one-file-system: Test the above.
* tests/rm/Makefile.am (TESTS): Add one-file-system.
* tests/Makefile.am (check-root): Add the rm/one-file-system
test to the list.
(EXTRA_DIST): Add other-fs-tmpdir.

* tests/mv/setup: Removed.  Renamed to...
* tests/other-fs-tmpdir: ...this new file.
* tests/mv/Makefile.am (EXTRA_DIST): Remove setup.
* tests/mv/acl: Reflect renaming: use ../other-fs-tmpdir.
* tests/mv/backup-is-src: Likewise.
* tests/mv/hard-link-1: Likewise.
* tests/mv/leak-fd: Likewise.
* tests/mv/mv-special-1: Likewise.
* tests/mv/part-fail: Likewise.
* tests/mv/part-hardlink: Likewise.
* tests/mv/part-rename: Likewise.
* tests/mv/part-symlink: Likewise.
* tests/mv/partition-perm: Likewise.
* tests/mv/to-symlink: Likewise.
* tests/mv/into-self-2: Likewise.

[doc/ChangeLog]
* coreutils.texi (rm invocation): Describe --one-file-system.
This commit is contained in:
Jim Meyering 2006-10-25 00:01:33 +02:00
parent ba6b1acefd
commit 5e42576c01
25 changed files with 189 additions and 20 deletions

View File

@ -1,5 +1,38 @@
2006-10-24 Jim Meyering <jim@meyering.net>
* NEWS: new feature: rm accepts new option: --one-file-system
Suggested by Steve McIntyre in <http://bugs.debian.org/392925>.
* src/remove.h (struct rm_options) [one_file_system]: New member.
* src/rm.c (rm_option_init): Initialize it.
(usage): Document the option.
* src/mv.c (rm_option_init): Likewise.
* src/remove.c (remove_dir): With --one-file-system and --recursive,
for each directory command line argument, do not affect a file system
different from that of the starting directory. And give a diagnostic.
* src/rm.c (ONE_FILE_SYSTEM): New enum.
(main): Handle new option.
* tests/rm/one-file-system: Test the above.
* tests/rm/Makefile.am (TESTS): Add one-file-system.
* tests/Makefile.am (check-root): Add the rm/one-file-system
test to the list.
(EXTRA_DIST): Add other-fs-tmpdir.
* tests/mv/setup: Removed. Renamed to...
* tests/other-fs-tmpdir: ...this new file.
* tests/mv/Makefile.am (EXTRA_DIST): Remove setup.
* tests/mv/acl: Reflect renaming: use ../other-fs-tmpdir.
* tests/mv/backup-is-src: Likewise.
* tests/mv/hard-link-1: Likewise.
* tests/mv/leak-fd: Likewise.
* tests/mv/mv-special-1: Likewise.
* tests/mv/part-fail: Likewise.
* tests/mv/part-hardlink: Likewise.
* tests/mv/part-rename: Likewise.
* tests/mv/part-symlink: Likewise.
* tests/mv/partition-perm: Likewise.
* tests/mv/to-symlink: Likewise.
* tests/mv/into-self-2: Likewise.
Don't let a failure in one test stop "make -k" from running the others.
* tests/Makefile.am (t1 t2 t3 t4 t5): New targets.
(check-root): Depend on them, rather than executing the five

4
NEWS
View File

@ -2,6 +2,10 @@ GNU coreutils NEWS -*- outline -*-
* Major changes in release 6.5-cvs (2006-??-??)
** New features
rm accepts a new option: --one-file-system
* Major changes in release 6.4 (2006-10-22) [stable]

View File

@ -1,3 +1,7 @@
2006-10-23 Jim Meyering <jim@meyering.net>
* coreutils.texi (rm invocation): Describe --one-file-system.
2006-09-26 Paul Eggert <eggert@cs.ucla.edu>
* coreutils.texi (groups invocation): "groups" no longer prefixes

View File

@ -7772,6 +7772,24 @@ removal is requested. Equivalent to @option{-I}.
Specifying @option{--interactive} and no @var{when} is equivalent to
@option{--interactive=always}.
@itemx --one-file-system
@opindex --one-file-system
@cindex one file system, restricting @command{rm} to
When removing a hierarchy recursively, skip any directory that is on a
file system different from that of the corresponding command line argument.
This option is useful when removing a build ``chroot'' hierarchy,
which normally contains no valuable data. However, it is not uncommon
to bind-mount @file{/home} into such a hierarchy, to make it easier to
use one's start-up file. The catch is that it's easy to forget to
unmount @file{/home}. Then, when you use @command{rm -rf} to remove
your normally throw-away chroot, that command will remove everything
under @file{/home}, too.
Use the @option{--one-file-system} option, and it will
warn about and skip directories on other file systems.
Of course, this will not save your @file{/home} if it and your
chroot happen to be on the same file system.
@itemx --preserve-root
@opindex --preserve-root
@cindex root directory, disallow recursive destruction

View File

@ -94,6 +94,7 @@ rm_option_init (struct rm_options *x)
x->ignore_missing_files = false;
x->root_dev_ino = NULL;
x->recursive = true;
x->one_file_system = false;
/* Should we prompt for removal, too? No. Prompting for the `move'
part is enough. It implies removal. */

View File

@ -1298,6 +1298,7 @@ remove_dir (int fd_cwd, Dirstack_state *ds, char const *dir,
struct rm_options const *x, int *cwd_errno)
{
enum RM_status status;
dev_t current_dev = dir_st->st_dev;
/* There is a race condition in that an attacker could replace the nonempty
directory, DIR, with a symlink between the preceding call to rmdir
@ -1359,15 +1360,31 @@ remove_dir (int fd_cwd, Dirstack_state *ds, char const *dir,
}
if (subdir)
{
AD_push (dirfd (dirp), ds, subdir, &subdir_sb);
AD_INIT_OTHER_MEMBERS ();
if ( ! x->one_file_system
|| subdir_sb.st_dev == current_dev)
{
AD_push (dirfd (dirp), ds, subdir, &subdir_sb);
AD_INIT_OTHER_MEMBERS ();
free (subdir);
continue;
}
/* Here, --one-file-system is in effect, and with remove_cwd_entries'
traversal into the current directory, (known as SUBDIR, from ..),
DIRP's device number is different from CURRENT_DEV. Arrange not
to do anything more with this hierarchy. */
error (0, errno, _("skipping %s, since it's on a different device"),
quote (full_filename (subdir)));
free (subdir);
continue;
AD_mark_current_as_unremovable (ds);
tmp_status = RM_ERROR;
UPDATE_STATUS (status, tmp_status);
}
/* Execution reaches this point when we've removed the last
removable entry from the current directory. */
removable entry from the current directory -- or, with
--one-file-system, when the current directory is on a
different file system. */
{
/* The name of the directory that we have just processed,
nominally removing all of its contents. */

View File

@ -30,6 +30,14 @@ struct rm_options
/* If true, query the user about whether to remove each file. */
bool interactive;
/* If true, do not traverse into (or remove) any directory that is
on a file system (i.e., that has a different device number) other
than that of the corresponding command line argument. Note that
even without this option, rm will fail in the end, due to its
probable inability to remove the mount point. But there, the
diagnostic comes too late -- after removing all contents. */
bool one_file_system;
/* If true, recursively remove directories. */
bool recursive;

View File

@ -72,6 +72,7 @@ char *program_name;
enum
{
INTERACTIVE_OPTION = CHAR_MAX + 1,
ONE_FILE_SYSTEM,
NO_PRESERVE_ROOT,
PRESERVE_ROOT,
PRESUME_INPUT_TTY_OPTION
@ -90,6 +91,7 @@ static struct option const long_opts[] =
{"force", no_argument, NULL, 'f'},
{"interactive", optional_argument, NULL, INTERACTIVE_OPTION},
{"one-file-system", no_argument, NULL, ONE_FILE_SYSTEM},
{"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT},
{"preserve-root", no_argument, NULL, PRESERVE_ROOT},
@ -168,6 +170,11 @@ Remove (unlink) the FILE(s).\n\
while still giving protection against most mistakes\n\
--interactive[=WHEN] prompt according to WHEN: never, once (-I), or\n\
always (-i). Without WHEN, prompt always\n\
"), stdout);
fputs (_("\
--one-file-system when removing a hierarchy recursively, skip any\n\
directory that is on a file system different from\n\
that of the corresponding command line argument\n\
"), stdout);
fputs (_("\
--no-preserve-root do not treat `/' specially\n\
@ -207,6 +214,7 @@ rm_option_init (struct rm_options *x)
{
x->ignore_missing_files = false;
x->interactive = false;
x->one_file_system = false;
x->recursive = false;
x->root_dev_ino = NULL;
x->stdin_tty = isatty (STDIN_FILENO);
@ -299,6 +307,10 @@ main (int argc, char **argv)
break;
}
case ONE_FILE_SYSTEM:
x.one_file_system = true;
break;
case NO_PRESERVE_ROOT:
preserve_root = false;
break;

View File

@ -15,7 +15,8 @@ TESTS_ENVIRONMENT = \
EXTRA_DIST = \
$(TESTS) Coreutils.pm Makefile.am.in README acl envvar-check \
expensive group-names input-tty lang-default mk-script priv-check \
expensive group-names input-tty lang-default mk-script \
other-fs-tmpdir priv-check \
rwx-to-mode sample-test setgid-check sparse-file \
umask-check very-expensive
@ -28,8 +29,9 @@ SUBDIRS = \
tsort unexpand uniq wc
## N O T E :: Please do not add new directories.
.PHONY: check-root t1 t2 t3 t4 t5
check-root: t1 t2 t3 t4 t5
all_t = t1 t2 t3 t4 t5 t6
.PHONY: check-root $(all_t)
check-root: $(all_t)
t1:
cd chown && $(MAKE) check TESTS=basic
@ -41,6 +43,8 @@ t4:
cd rm && $(MAKE) check TESTS=fail-2eperm
t5:
cd tail-2 && $(MAKE) check TESTS=append-only
t6:
cd rm && $(MAKE) check TESTS=one-file-system
check-recursive: root-hint

View File

@ -43,7 +43,7 @@ TESTS = \
i-1 hard-link-1 force partition-perm to-symlink dir-file diag \
part-symlink part-rename trailing-slash
EXTRA_DIST = $(TESTS) setup vfat
EXTRA_DIST = $(TESTS) vfat
TESTS_ENVIRONMENT = \
PERL="$(PERL)" \
PATH="$(VG_PATH_PREFIX)`pwd`/../../src$(PATH_SEPARATOR)$$PATH" \

View File

@ -20,7 +20,7 @@
# 02110-1301, USA.
. $srcdir/../acl
. $srcdir/setup
. $srcdir/../other-fs-tmpdir
# Make sure we get English translations.
. $srcdir/../lang-default

View File

@ -23,7 +23,7 @@ if test "$VERBOSE" = yes; then
mv --version
fi
. $srcdir/setup
. $srcdir/../other-fs-tmpdir
. $srcdir/../envvar-check
if test -z "$other_partition_tmpdir"; then

View File

@ -24,7 +24,7 @@ if test "$VERBOSE" = yes; then
mv --version
fi
. $srcdir/setup
. $srcdir/../other-fs-tmpdir
# Make sure we get English translations.
. $srcdir/../lang-default

View File

@ -25,7 +25,7 @@ if test "$VERBOSE" = yes; then
mv --version
fi
. $srcdir/setup
. $srcdir/../other-fs-tmpdir
. $srcdir/../envvar-check
if test -z "$other_partition_tmpdir"; then

View File

@ -30,7 +30,7 @@ if test "$VERBOSE" = yes; then
mv --version
fi
. $srcdir/setup
. $srcdir/../other-fs-tmpdir
. $srcdir/../envvar-check
PRIV_CHECK_ARG=require-non-root . $srcdir/../priv-check

View File

@ -29,7 +29,7 @@ tmp=mv-spec.$$
trap 'status=$?; cd "$pwd" && exec 1>&2; rm -rf $tmp $other_partition_tmpdir && exit $status' 0
trap '(exit $?); exit' 1 2 13 15
. $srcdir/setup
. $srcdir/../other-fs-tmpdir
. $srcdir/../envvar-check
# Make sure we get English translations.
. $srcdir/../lang-default

View File

@ -26,7 +26,7 @@ if test "$VERBOSE" = yes; then
mv --version
fi
. $srcdir/setup
. $srcdir/../other-fs-tmpdir
. $srcdir/../envvar-check
. $srcdir/../lang-default
PRIV_CHECK_ARG=require-non-root . $srcdir/../priv-check

View File

@ -26,7 +26,7 @@ if test "$VERBOSE" = yes; then
mv --version
fi
. $srcdir/setup
. $srcdir/../other-fs-tmpdir
. $srcdir/../envvar-check
pwd=`pwd`

View File

@ -25,7 +25,7 @@ if test "$VERBOSE" = yes; then
mv --version
fi
. $srcdir/setup
. $srcdir/../other-fs-tmpdir
. $srcdir/../envvar-check
pwd=`pwd`

View File

@ -32,7 +32,7 @@ trap '(exit $?); exit' 1 2 13 15
pwd_tmp=$pwd/$tmp
. $srcdir/setup
. $srcdir/../other-fs-tmpdir
. $srcdir/../envvar-check
# Make sure the programs use C-locale formats/translations.
. $srcdir/../lang-default

View File

@ -23,7 +23,7 @@ if test "$VERBOSE" = yes; then
mv --version
fi
. $srcdir/setup
. $srcdir/../other-fs-tmpdir
. $srcdir/../envvar-check
. $srcdir/../lang-default

View File

@ -24,7 +24,7 @@ if test "$VERBOSE" = yes; then
mv --version
fi
. $srcdir/setup
. $srcdir/../other-fs-tmpdir
. $srcdir/../envvar-check
if test -z "$other_partition_tmpdir"; then

View File

@ -21,6 +21,7 @@
AUTOMAKE_OPTIONS = 1.1 gnits
TESTS = \
one-file-system \
ignorable \
readdir-bug \
empty-inacc \

67
tests/rm/one-file-system Executable file
View File

@ -0,0 +1,67 @@
#!/bin/sh
# Demonstrate rm's new --one-file-system option.
# Copyright (C) 2006 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 2 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
if test "$VERBOSE" = yes; then
set -x
rm --version
fi
PRIV_CHECK_ARG=require-root . $srcdir/../priv-check
. $srcdir/../lang-default
. $srcdir/../other-fs-tmpdir
if test -z "$other_partition_tmpdir"; then
(exit 77); exit 77
fi
pwd=`pwd`
t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$
trap 'status=$?; cd $pwd; chmod -R u+rwx $t0; rm -rf $t0 && exit $status' 0
trap '(exit $?); exit $?' 1 2 13 15
t0="$t0 $other_partition_tmpdir"
framework_failure=0
mkdir -p $tmp || framework_failure=1
cd $tmp || framework_failure=1
t=$other_partition_tmpdir
mkdir -p a/b $t/y
mount --bind $t a/b || framework_failure=1
cat <<\EOF > exp || framework_failure=1
rm: skipping `a/b', since it's on a different device
EOF
if test $framework_failure = 1; then
echo "$0: failure in testing framework" 1>&2
(exit 1); exit 1
fi
fail=0
rm --one-file-system -rf a 2> out && fail=1
test -d $t/y || fail=1
umount $t
cmp out exp || fail=1
test $fail = 1 && diff out exp 2> /dev/null
(exit $fail); exit $fail