I was asked off list to consider adding an option to rm that could be enabled with an alias, and would protect mount points specified on the command line.
I think this is useful functionality as rm will ultimately fail to remove the mount point itself, but only after processing the whole file system. It's unusual to remove a whole file system as it's generally more efficient to recreate and remount them, and in the particular case I was told about, it was a pretty effective DoS to the system to process and unlink the whole tree. Rather than add a new option, I thought it cleaner to extend the existing --preserve-root option to support --preserve-root=all, as that can be interpreted to preserve the root of all specified file systems. Attached is the proposed implementation, to which I'll add tests and docs after comments. An example run on my system is: $ rm -r --preserve-root=all /dev/shm rm: skipping '/dev/shm', since it's a mount point rm: and --preserve-root=all is in effect cheers, Pádraig
From 53c78143c411170cef79f75938ac0051c59cafa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= <p...@draigbrady.com> Date: Sun, 10 Jun 2018 23:02:58 -0700 Subject: [PATCH] rm: add --preserve-root=all to protect mount points * src/remove.c (rm_fts): With the --preserve-root=all extension, skip command line arguments that are mount points. --- src/mv.c | 2 ++ src/remove.c | 35 +++++++++++++++++++++++++++++++++++ src/remove.h | 4 ++++ src/rm.c | 14 +++++++++++++- 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/mv.c b/src/mv.c index edc1e73..b6dd72d 100644 --- a/src/mv.c +++ b/src/mv.c @@ -99,6 +99,8 @@ rm_option_init (struct rm_options *x) die (EXIT_FAILURE, errno, _("failed to get attributes of %s"), quoteaf ("/")); } + + x->preserve_all_root = false; } static void diff --git a/src/remove.c b/src/remove.c index eafb964..6ff837c 100644 --- a/src/remove.c +++ b/src/remove.c @@ -24,6 +24,7 @@ #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" @@ -459,6 +460,40 @@ rm_fts (FTS *fts, FTSENT *ent, struct rm_options const *x) 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 a mount point"), + quoteaf (ent->fts_path)); + error (0, 0, _("and --preserve-root=all is in effect")); + } + fts_skip_tree (fts, ent); + return RM_ERROR; + } + } } { diff --git a/src/remove.h b/src/remove.h index 2ce3a16..8fc6171 100644 --- a/src/remove.h +++ b/src/remove.h @@ -56,6 +56,10 @@ struct rm_options 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 mount point. */ + bool preserve_all_root; + /* If nonzero, stdin is a tty. */ bool stdin_tty; diff --git a/src/rm.c b/src/rm.c index dbc0cb7..82cf132 100644 --- a/src/rm.c +++ b/src/rm.c @@ -67,7 +67,7 @@ static struct option const long_opts[] = {"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. @@ -192,6 +192,7 @@ rm_option_init (struct rm_options *x) 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; @@ -294,6 +295,17 @@ main (int argc, char **argv) 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; -- 2.9.3