From 53c78143c411170cef79f75938ac0051c59cafa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1draig=20Brady?= 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