Hi, I'm not sure if this was discussed before, but I've got tired from having to work around the lack of native recursion within `shred`. So, attached is a patch to add recursion, including documentation.
Let me know if there's anything that needs to be modified further. I've compiled and tested shred, so far everything works perfectly. Have a great day, -- Amr Ali
From 481932b48ffd4e9c0e97f934a64ea54b039666f1 Mon Sep 17 00:00:00 2001 From: Amr Ali <[email protected]> Date: Fri, 9 Dec 2011 18:48:40 +0200 Subject: [PATCH 1/2] shred: add recursion operations --- src/shred.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 files changed, 136 insertions(+), 19 deletions(-) diff --git a/src/shred.c b/src/shred.c index a2365b0..820b86d 100644 --- a/src/shred.c +++ b/src/shred.c @@ -22,7 +22,6 @@ - use consistent non-capitalization in error messages - add standard GNU copyleft comment - - Add -r/-R/--recursive - Add -i/--interactive - Reserve -d - Add -L @@ -97,10 +96,12 @@ #include "error.h" #include "fcntl--.h" #include "human.h" +#include "quote.h" #include "quotearg.h" /* For quotearg_colon */ #include "randint.h" #include "randread.h" #include "stat-size.h" +#include "xfts.h" /* Default number of times to overwrite. */ enum { DEFAULT_PASSES = 3 }; @@ -121,6 +122,7 @@ struct Options size_t n_iterations; /* -n flag: Number of iterations */ off_t size; /* -s flag: size of file */ bool remove_file; /* -u flag: remove file after shredding */ + bool recursive; /* -r flag: go through directories recursively */ bool verbose; /* -v flag: Print progress */ bool exact; /* -x flag: Do not round up file size */ bool zero_fill; /* -z flag: Add a final zero pass */ @@ -141,6 +143,7 @@ static struct option const long_opts[] = {"size", required_argument, NULL, 's'}, {"random-source", required_argument, NULL, RANDOM_SOURCE_OPTION}, {"remove", no_argument, NULL, 'u'}, + {"recursive", no_argument, NULL, 'r'}, {"verbose", no_argument, NULL, 'v'}, {"zero", no_argument, NULL, 'z'}, {GETOPT_HELP_OPTION_DECL}, @@ -173,6 +176,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\ "), DEFAULT_PASSES); fputs (_("\ -u, --remove truncate and remove file after overwriting\n\ + -r, --recursive go through directories recursively\n\ -v, --verbose show progress\n\ -x, --exact do not round file sizes up to the next full block;\n\ this is the default for non-regular files\n\ @@ -1068,6 +1072,8 @@ wipefile (char *name, char const *qname, && (errno == EACCES && flags->force) && chmod (name, S_IWUSR) == 0) fd = open (name, O_WRONLY | O_NOCTTY | O_BINARY); + if (fd < 0 && (errno == EISDIR)) /* Directories should be transparent */ + return true; if (fd < 0) { error (0, errno, _("%s: failed to open for writing"), qname); @@ -1098,6 +1104,126 @@ clear_random_data (void) randint_all_free (randint_source); } +/* Wipe FILE. + Return true if successful. This function is called + once for every file system object that fts encounters. */ +static bool +process_file (FTS *fts, FTSENT *ent, struct randint_source *s, + struct Options const *flags) +{ + char const *file_full_name = ent->fts_path; + char const *file = ent->fts_accpath; + const struct stat *file_stats = ent->fts_statp; + bool ok = true; + + switch (ent->fts_info) + { + case FTS_DP: + return true; + + case FTS_NS: + if (! flags->verbose) + error (0, ent->fts_errno, _("cannot access %s"), + quote (file_full_name)); + ok = false; + break; + + case FTS_ERR: + if (! flags->verbose) + error (0, ent->fts_errno, "%s", quote (file_full_name)); + ok = false; + break; + + case FTS_DNR: + if (! flags->verbose) + error (0, ent->fts_errno, _("cannot read directory %s"), + quote (file_full_name)); + ok = false; + break; + + case FTS_SLNONE: + if (! flags->verbose) + error (0, 0, _("cannot operate on dangling symlink %s"), + quote (file_full_name)); + ok = false; + break; + + case FTS_DC: /* directory that causes cycles */ + if (cycle_warning_required (fts, ent)) + { + emit_cycle_warning (file_full_name); + return false; + } + break; + + default: + break; + } + + if (ok) + { + char *qname = xstrdup (quotearg_colon (file_full_name)); + + if (STREQ (file, "-")) + { + ok = wipefd (STDOUT_FILENO, qname, s, flags); + } + else + { + /* Plain filename */ + char *fname = xstrdup (file_full_name); + ok = wipefile (fname, qname, s, flags); + free (fname); + } + + free (qname); + } + + if (! flags->recursive) + fts_set (fts, ent, FTS_SKIP); + + return ok; +} + +/* Recursively wipe the specified FILES (the last entry + of which is NULL). BIT_FLAGS controls how fts works. + Return true if successful. */ +static bool +process_files (char **files, int bit_flags, struct randint_source *s, + struct Options const *flags) +{ + bool ok = true; + + FTS *fts = xfts_open (files, bit_flags, NULL); + + while (1) + { + FTSENT *ent; + + ent = fts_read (fts); + if (ent == NULL) + { + if (errno != 0) + { + /* XXX: try to give a better message */ + if (! flags->verbose) + error (0, errno, _("fts_read failed")); + ok = false; + } + break; + } + + ok &= process_file (fts, ent, s, flags); + } + + if (fts_close (fts) != 0) + { + error (0, errno, _("fts_close failed")); + ok = false; + } + + return ok; +} int main (int argc, char **argv) @@ -1105,7 +1231,6 @@ main (int argc, char **argv) bool ok = true; struct Options flags = { 0, }; char **file; - int n_files; int c; int i; char const *random_source = NULL; @@ -1121,7 +1246,7 @@ main (int argc, char **argv) flags.n_iterations = DEFAULT_PASSES; flags.size = -1; - while ((c = getopt_long (argc, argv, "fn:s:uvxz", long_opts, NULL)) != -1) + while ((c = getopt_long (argc, argv, "fn:s:urvxz", long_opts, NULL)) != -1) { switch (c) { @@ -1152,6 +1277,10 @@ main (int argc, char **argv) flags.remove_file = true; break; + case 'r': + flags.recursive = true; + break; + case 's': { uintmax_t tmp; @@ -1187,9 +1316,8 @@ main (int argc, char **argv) } file = argv + optind; - n_files = argc - optind; - if (n_files == 0) + if (argc - optind == 0) { error (0, 0, _("missing file operand")); usage (EXIT_FAILURE); @@ -1200,20 +1328,9 @@ main (int argc, char **argv) error (EXIT_FAILURE, errno, "%s", quotearg_colon (random_source)); atexit (clear_random_data); - for (i = 0; i < n_files; i++) - { - char *qname = xstrdup (quotearg_colon (file[i])); - if (STREQ (file[i], "-")) - { - ok &= wipefd (STDOUT_FILENO, qname, randint_source, &flags); - } - else - { - /* Plain filename - Note that this overwrites *argv! */ - ok &= wipefile (file[i], qname, randint_source, &flags); - } - free (qname); - } + ok &= process_files (file, + FTS_COMFOLLOW | FTS_PHYSICAL | FTS_DEFER_STAT, + randint_source, &flags); exit (ok ? EXIT_SUCCESS : EXIT_FAILURE); } -- 1.7.5.4 From 4bb9ba4a92adea5d77885445c82a17b967fb6f31 Mon Sep 17 00:00:00 2001 From: Amr Ali <[email protected]> Date: Fri, 9 Dec 2011 18:49:03 +0200 Subject: [PATCH 2/2] doc: add entries for shred's -r/--recursive --- doc/coreutils.texi | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 369fad2..edeba0a 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -9001,6 +9001,13 @@ the whole file. @var{bytes} can be followed by a size specification like After shredding a file, truncate it (if possible) and then remove it. If a file has multiple links, only the named links will be removed. +@item -r +@item --recursive +@opindex -r +@opindex --recursive +@cindex shred files recursively +Recurse through directories and shred files within. + @item -v @itemx --verbose @opindex -v -- 1.7.5.4
signature.asc
Description: OpenPGP digital signature
