NUL instead of a newline character. This also disables file name escaping.
This also applies to sha*sum and b2sum.
+ rm --preserve-root now supports the --preserve-root=all option to
+ reject any command line argument that is mounted to a separate file system.
+
** Improvements
cut supports line lengths up to the max file size on 32 bit systems.
@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.
-
@cindex bind mount
This option is useful when removing a build ``chroot'' hierarchy,
which normally contains no valuable data. However, it is not uncommon
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.
+See also @option{--preserve-root=all} to protect command line arguments
+themselves.
-@item --preserve-root
+@item --preserve-root [=all]
@opindex --preserve-root
@cindex root directory, disallow recursive destruction
Fail upon any attempt to remove the root directory, @file{/},
when used with the @option{--recursive} option.
This is the default behavior.
@xref{Treating / specially}.
+When @samp{all} is specified, reject any command line argument
+that is not on the same file system as its parent.
@item --no-preserve-root
@opindex --no-preserve-root
die (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
quoteaf ("/"));
}
+
+ x->preserve_all_root = false;
}
static void
#include "system.h"
#include "error.h"
#include "file-type.h"
+#include "filenamecat.h"
#include "ignore-value.h"
#include "remove.h"
#include "root-dev-ino.h"
fts_skip_tree (fts, ent);
return RM_ERROR;
}
+
+ /* If a command line argument is a mount point and
+ --preserve-root=all is in effect, diagnose and skip it.
+ This doesn't handle "/", but that's handled above. */
+ if (x->preserve_all_root)
+ {
+ bool failed = false;
+ char *parent = file_name_concat (ent->fts_accpath, "..", NULL);
+ struct stat statbuf;
+
+ if (!parent || lstat (parent, &statbuf))
+ {
+ error (0, 0,
+ _("failed to stat %s: skipping %s"),
+ quoteaf_n (0, parent),
+ quoteaf_n (1, ent->fts_accpath));
+ failed = true;
+ }
+
+ free (parent);
+
+ if (failed || fts->fts_dev != statbuf.st_dev)
+ {
+ if (! failed)
+ {
+ error (0, 0,
+ _("skipping %s, since it's on a different device"),
+ quoteaf (ent->fts_path));
+ error (0, 0, _("and --preserve-root=all is in effect"));
+ }
+ fts_skip_tree (fts, ent);
+ return RM_ERROR;
+ }
+ }
}
{
and preserving '/'. Otherwise NULL. */
struct dev_ino *root_dev_ino;
+ /* If true, do not traverse into (or remove) any directory that is
+ the root of a file system. I.e., a separate device. */
+ bool preserve_all_root;
+
/* If nonzero, stdin is a tty. */
bool stdin_tty;
{"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},
+ {"preserve-root", optional_argument, NULL, PRESERVE_ROOT},
/* This is solely for testing. Do not document. */
/* It is relatively difficult to ensure that there is a tty on stdin.
"), stdout);
fputs (_("\
--no-preserve-root do not treat '/' specially\n\
- --preserve-root do not remove '/' (default)\n\
+ --preserve-root[=all] do not remove '/' (default);\n\
+ with 'all', reject any command line argument\n\
+ on a separate device from its parent\n\
+"), stdout);
+ fputs (_("\
-r, -R, --recursive remove directories and their contents recursively\n\
-d, --dir remove empty directories\n\
-v, --verbose explain what is being done\n\
x->remove_empty_directories = false;
x->recursive = false;
x->root_dev_ino = NULL;
+ x->preserve_all_root = false;
x->stdin_tty = isatty (STDIN_FILENO);
x->verbose = false;
break;
case PRESERVE_ROOT:
+ if (optarg)
+ {
+ if STREQ (optarg, "all")
+ x.preserve_all_root = true;
+ else
+ {
+ die (EXIT_FAILURE, 0,
+ _("unrecognized --preserve-root argument: %s"),
+ quoteaf (optarg));
+ }
+ }
preserve_root = true;
break;
cat <<\EOF > exp || framework_failure_
rm: skipping 'a/b', since it's on a different device
EOF
-
-
-rm --one-file-system -rf a 2> out && fail=1
+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