]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
new feature: rm accepts new option: --one-file-system
authorJim Meyering <jim@meyering.net>
Tue, 24 Oct 2006 22:01:33 +0000 (00:01 +0200)
committerJim Meyering <jim@meyering.net>
Tue, 24 Oct 2006 22:01:33 +0000 (00:01 +0200)
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.

25 files changed:
ChangeLog
NEWS
doc/ChangeLog
doc/coreutils.texi
src/mv.c
src/remove.c
src/remove.h
src/rm.c
tests/Makefile.am
tests/mv/Makefile.am
tests/mv/acl
tests/mv/backup-is-src
tests/mv/hard-link-1
tests/mv/into-self-2
tests/mv/leak-fd
tests/mv/mv-special-1
tests/mv/part-fail
tests/mv/part-hardlink
tests/mv/part-rename
tests/mv/part-symlink
tests/mv/partition-perm
tests/mv/to-symlink
tests/other-fs-tmpdir [moved from tests/mv/setup with 100% similarity]
tests/rm/Makefile.am
tests/rm/one-file-system [new file with mode: 0755]

index eb2ac2491e40a22393f573d56dc6d2951bc992b0..939e99f431901660af9bedaec189d56376968f22 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/NEWS b/NEWS
index 41a418acb260d336b3961602d9ad1cb315f93668..734d57ee686db9c7195b3849e54541b8ec569a19 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -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]
 
index 2867798b0eb56660d39103507ad7bebff7ed128e..0e0b5c2a0b6febd5edb9ef6b7c05476ecaf7daac 100644 (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
index 3a180f66949be4edc1986d8f1dbd028c81f78256..ee81792462005ed92b0b3d2cfc19df4b32dee6bc 100644 (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
index 299a6acfd08ded75490e1addb152fe6c1dd6bfe1..03e96e5a0aea99d7d10d2e731ca0c0a6e5e40e62 100644 (file)
--- a/src/mv.c
+++ b/src/mv.c
@@ -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.  */
index d362db0eec7c794dd4e59c55fd6df1f2f74a2ec5..add85dd7ba1e7dd873fe0a5b7cd48fe43428fc2d 100644 (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.  */
index d3609d768b09b29b4fd889c6263405d6e27b1877..2dc6176184ccb5f1e9ecee2cc465a1854b4a2710 100644 (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;
 
index 28e09ce1253a500d987ac273d36ef9f4c25f4d3f..0c93a040a0f669153fbb4bc67d0469a6d4d94e9f 100644 (file)
--- a/src/rm.c
+++ b/src/rm.c
@@ -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;
index b13294bf5d7814faf2fb7bcb30c683c700e8d8b3..d173ecda92460578a959cd9d08ba653c731a1e33 100644 (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
 
index d17151de0e3ab9e6ba33f00e94d92c7a244b2fb7..927bac89b45bfe620427c43ad494801e427d91bb 100644 (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" \
index b29f0e8eaed89e9a5c3aa00a2e6c6fc0de91d167..f570656fa9f434405026090e73149f478db517db 100755 (executable)
@@ -20,7 +20,7 @@
 # 02110-1301, USA.
 
 . $srcdir/../acl
-. $srcdir/setup
+. $srcdir/../other-fs-tmpdir
 # Make sure we get English translations.
 . $srcdir/../lang-default
 
index 00ecc10334631fcabc5513d1e88567a09ca2ffc7..8d5c69d6ef426e432b31e119639842d34ac72aa1 100755 (executable)
@@ -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
index dc345f110442d36405f1196a50e7ab7e9b147898..7ce9176e11804d479936965e62813209ba4e5247 100755 (executable)
@@ -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
 
index f5b7f74f8e65894de6c4a8ca4ab6a816d1444880..11fddf2102ed4e0558cc72eabbf55ada67a9472e 100755 (executable)
@@ -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
index 5e5bb1a833c1d709760d0d026aaf05642134a1fd..1592e0c5022ccc3eaca243688f921c9753361d7d 100755 (executable)
@@ -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
 
index dadcd697db3af1da8fd3827a27980c4c446ca10f..4d7c6978cce2a5e24fc9d3ae2e305ec7e7c7789a 100755 (executable)
@@ -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
index 2bbd9d247599fcca20dd6f2c0ec83ee7889d074c..e4c5dc96e02ba7462c913b15460a1cdfbe940229 100755 (executable)
@@ -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
index 9a8eac770e36685c821a45e8f8425d036dfb5c3e..66cfe0d4cdada250d84b0ecc70a757b08962fe51 100755 (executable)
@@ -26,7 +26,7 @@ if test "$VERBOSE" = yes; then
   mv --version
 fi
 
-. $srcdir/setup
+. $srcdir/../other-fs-tmpdir
 . $srcdir/../envvar-check
 
 pwd=`pwd`
index a4b361010dc837010f06df410547d7602592d45d..b8c03e09262fd3e55c37d321ac92ce0d29dd50d4 100755 (executable)
@@ -25,7 +25,7 @@ if test "$VERBOSE" = yes; then
   mv --version
 fi
 
-. $srcdir/setup
+. $srcdir/../other-fs-tmpdir
 . $srcdir/../envvar-check
 
 pwd=`pwd`
index eb66779c6a0735c61e2f6bc00cfe565e52d77443..5cd198cad1722944dd746dd7dc5c7f1fd67a46da 100755 (executable)
@@ -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
index f510edf08731c149b4c820d3abd865565497d89b..969dc74cd7cd764c3b00a5233b1ee03436b827eb 100755 (executable)
@@ -23,7 +23,7 @@ if test "$VERBOSE" = yes; then
   mv --version
 fi
 
-. $srcdir/setup
+. $srcdir/../other-fs-tmpdir
 . $srcdir/../envvar-check
 . $srcdir/../lang-default
 
index 864fef6c7151869248784df0141ba425cefda26e..6473cfba30fd9aebdd51118a23519cb8a45bd843 100755 (executable)
@@ -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
similarity index 100%
rename from tests/mv/setup
rename to tests/other-fs-tmpdir
index ada461bc3cf6d1f584a4e4a3baa96f7c1040489e..6670307e8abf4fb5735210fb39d9f16dd43d3470 100644 (file)
@@ -21,6 +21,7 @@
 AUTOMAKE_OPTIONS = 1.1 gnits
 
 TESTS = \
+  one-file-system \
   ignorable \
   readdir-bug \
   empty-inacc \
diff --git a/tests/rm/one-file-system b/tests/rm/one-file-system
new file mode 100755 (executable)
index 0000000..bb7ffeb
--- /dev/null
@@ -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