From: Jim Meyering Date: Sun, 4 Apr 1999 02:34:49 +0000 (+0000) Subject: back out Paul's changes X-Git-Tag: FILEUTILS-4_0e~17 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8fe009ed7cad1b3552f06e119c1e6b2a28c41dbc;p=platform%2Fupstream%2Fcoreutils.git back out Paul's changes --- diff --git a/src/shred.c b/src/shred.c index f747a7b..05ff721 100644 --- a/src/shred.c +++ b/src/shred.c @@ -8,7 +8,7 @@ /* * shred.c - by Colin Plumb. * - * Do a more-secure overwrite of given files or devices, so that not even + * Do a secure overwrite of given files or devices, so that not even * very expensive hardware probing can recover the data. * * Although this process is also known as "wiping", I prefer the longer @@ -34,7 +34,7 @@ * assumption out, and the assumption that you want the data processed * as fast as the hard drive can spin, you can do better. * - * If asked to wipe a file, this also removes it, renaming it to in a + * If asked to wipe a file, this also deletes it, renaming it to in a * clever way to try to leave no trace of the original filename. * * Copyright 1997-1999 Colin Plumb . This program may @@ -64,8 +64,6 @@ #include "system.h" #include "error.h" -#include "human.h" -#include "quotearg.h" #include "xstrtoul.h" /* The official name of this program (e.g., no `g' prefix). */ @@ -81,31 +79,22 @@ /* FIXME: add comments */ struct Options { - enum { NO_CONTENTS, FREED_CONTENTS, ALL_CONTENTS } contents; - enum { NO_LINKS, ORDINARY_LINKS, ALL_LINKS } links; + int allow_devices; int force; - size_t n_iterations; + unsigned int n_iterations; + int remove_file; int verbose; int exact; int zero_fill; }; -/* If positive, the units to use when printing sizes; - if negative, the human-readable base. - For now, this is a constant. */ -static int const output_block_size = -1024; - static struct option const long_opts[] = { - {"no-contents", no_argument, NULL, 'b'}, - {"freed-contents", no_argument, NULL, 'c'}, - {"all-contents", no_argument, NULL, 'C'}, - {"no-links", no_argument, NULL, 'k'}, - {"ordinary-links", no_argument, NULL, 'l'}, - {"all-links", no_argument, NULL, 'L'}, + {"device", no_argument, NULL, 'd'}, {"exact", required_argument, NULL, 'x'}, {"force", no_argument, NULL, 'f'}, {"iterations", required_argument, NULL, 'n'}, + {"preserve", no_argument, NULL, 'p'}, {"verbose", no_argument, NULL, 'v'}, {"zero", required_argument, NULL, 'z'}, {GETOPT_HELP_OPTION_DECL}, @@ -116,8 +105,6 @@ static struct option const long_opts[] = /* Global variable for error printing purposes */ char const *program_name; -void usage (int status) __attribute__ ((__noreturn__)); - void usage (int status) { @@ -128,22 +115,19 @@ usage (int status) { printf (_("Usage: %s [OPTIONS] FILE [...]\n"), program_name); printf (_("\ -Overwrite a file to hide its contents.\n\ +Delete a file securely, first overwriting it to hide its contents.\n\ \n\ - -b, --no-contents do not shred contents\n\ - -c, --freed-contents shred contents that will be freed\n\ - -C, --all-contents shred all contents (default)\n\ + -d, --device allow operation on devices (devices are never deleted)\n\ -f, --force change permissions to allow writing if necessary\n\ - -k, --no-links do not shred links (default)\n\ - -l, --ordinary-links shred links to regular files\n\ - -L, --all-links shred all links\n\ -n, --iterations=N Overwrite N times instead of the default (25)\n\ + -p, --preserve do not delete file after overwriting\n\ -v, --verbose indicate progress (-vv to leave progress on screen)\n\ -x, --exact do not round file sizes up to the next full block\n\ -z, --zero add a final overwrite with zeros to hide shredding\n\ - - shred standard output (but don't remove it)\n\ - --help display this help and exit\n\ - --version print version information and exit\n\ + - shred standard input (but don't delete it);\n\ + this will fail unless you use <>file, a safety feature\n\ + --help display this help and exit\n\ + --version print version information and exit\n\ \n\ FIXME maybe add more discussion here?\n\ ")); @@ -154,7 +138,7 @@ FIXME maybe add more discussion here?\n\ } #if ! HAVE_FDATASYNC -# define fdatasync(fd) (-1) +# define fdatasync(Fd) fsync (Fd) #endif /* @@ -379,29 +363,28 @@ isaac_init (struct isaac_state *s, word32 const *seed, size_t seedsize) static void isaac_seed (struct isaac_state *s) { - char *p = (char *) s->mm; - char *lim = p + sizeof s->mm; -#define MIXIN_BOUND(s) ((s) < lim - p ? (s) : lim - p) -#define MIXIN(o) \ - do \ - { \ - size_t s = MIXIN_BOUND (sizeof (o)); \ - memcpy (p, (char *) &(o), s); \ - p += s; \ - } \ - while (0) - - /* Mix in bits of random information from the environment. - Mix the most random items first, in case lim - p is small - and we have to truncate. */ + s->mm[0] = getpid (); + s->mm[1] = getppid (); + + { +#ifdef HAVE_CLOCK_GETTIME /* POSIX ns-resolution */ + struct timespec ts; + clock_gettime (CLOCK_REALTIME, &ts); + s->mm[2] = ts.tv_sec; + s->mm[3] = ts.tv_nsec; +#else + struct timeval tv; + gettimeofday (&tv, (struct timezone *) 0); + s->mm[2] = tv.tv_sec; + s->mm[3] = tv.tv_usec; +#endif + } { int fd = open ("/dev/urandom", O_RDONLY); if (fd >= 0) { - size_t s = MIXIN_BOUND (32); - read (fd, p, s); - p += s; + read (fd, (char *) (s->mm + 4), 32); close (fd); } else @@ -410,52 +393,57 @@ isaac_seed (struct isaac_state *s) if (fd >= 0) { /* /dev/random is more precious, so use less */ - size_t s = MIXIN_BOUND (16); - read (fd, p, s); - p += s; + read (fd, (char *) (s->mm + 4), 16); close (fd); } } } -#ifdef HAVE_GETHRTIME - { - hrtime_t t = gethrtime (); - MIXIN (t); - } -#endif - -#ifdef HAVE_CLOCK_GETTIME - { - struct timespec t; - clock_gettime (CLOCK_REALTIME, &t); - MIXIN (t); - } -#endif - - { - time_t t = time ((time_t *) 0); - MIXIN (t); - } - - { - pid_t t = getpid (); - MIXIN (t); - t = getppid (); - MIXIN (t); - } + isaac_init (s, s->mm, sizeof (s->mm)); +} - { - uid_t t = getuid (); - MIXIN (t); - } +/* + * Read up to "size" bytes from the given fd and use them as additional + * ISAAC seed material. Returns the number of bytes actually read. + */ +static off_t +isaac_seedfd (struct isaac_state *s, int fd, off_t size) +{ + off_t sizeleft = size; + size_t lim, soff; + ssize_t ssize; + int i; + word32 seed[ISAAC_WORDS]; - { - gid_t t = getgid (); - MIXIN (t); - } + while (sizeleft) + { + lim = sizeof (seed); + if ((off_t) lim > sizeleft) + lim = (size_t) sizeleft; + soff = 0; + do + { + ssize = read (fd, (char *) seed + soff, lim - soff); + } + while (ssize > 0 && (soff += (size_t) ssize) < lim); + /* Mix in what was read */ + if (soff) + { + /* Garbage after the sofff position is harmless */ + for (i = 0; i < ISAAC_WORDS; i++) + s->mm[i] += seed[i]; + isaac_mix (s, s->mm); + sizeleft -= soff; + } + if (ssize <= 0) + break; + } + /* Wipe the copy of the file in "seed" */ + memset (seed, 0, sizeof (seed)); - isaac_init (s, s->mm, sizeof (s->mm)); + /* Final mix, as in isaac_init */ + isaac_mix (s, s->mm); + return size - sizeleft; } /* Single-word RNG built on top of ISAAC */ @@ -581,6 +569,60 @@ flushstatus (void) } /* + * Get the size of a file that doesn't want to cooperate (such as a + * device) by doing a binary search for the last readable byte. The size + * of the file is the least offset at which it is not possible to read + * a byte. + * + * This is also a nice example of using loop invariants to correctly + * implement an algorithm that is potentially full of fencepost errors. + * We assume that if it is possible to read a byte at offset x, it is + * also possible at all offsets <= x. + */ +static off_t +sizefd (int fd) +{ + off_t hi, lo, mid; + char c; /* One-byte buffer for dummy reads */ + + /* Binary doubling upwards to find the right range */ + lo = 0; + hi = 0; /* Any number, preferably 2^x-1, is okay here. */ + + /* + * Loop invariant: we have verified that it is possible to read a + * byte at all offsets < lo. Probe at offset hi >= lo until it + * is not possible to read a byte at that offset, establishing + * the loop invariant for the following loop. + */ + for (;;) + { + if (lseek (fd, hi, SEEK_SET) == (off_t) -1 + || read (fd, &c, 1) < 1) + break; + lo = hi + 1; /* This preserves the loop invariant. */ + hi += lo; /* Exponential doubling. */ + } + /* + * Binary search to find the exact endpoint. + * Loop invariant: it is not possible to read a byte at hi, + * but it is possible at all offsets < lo. Thus, the + * offset we seek is between lo and hi inclusive. + */ + while (hi > lo) + { + mid = (hi + lo) / 2; /* Rounded down, so lo <= mid < hi */ + if (lseek (fd, mid, SEEK_SET) == (off_t) -1 + || read (fd, &c, 1) < 1) + hi = mid; /* mid < hi, so this makes progress */ + else + lo = mid + 1; /* Because mid < hi, lo <= hi */ + } + /* lo == hi, so we have an exact answer */ + return hi; +} + +/* * Fill a buffer with a fixed pattern. * * The buffer must be at least 3 bytes long, even if @@ -638,14 +680,13 @@ passname (unsigned char const *data, char name[PASS_NAME_SIZE]) * Do pass number k of n, writing "size" bytes of the given pattern "type" * to the file descriptor fd. Name, k and n are passed in only for verbose * progress message purposes. If n == 0, no progress messages are printed. - * If size is negative, write until we fall off the end. */ static int dopass (int fd, char const *name, off_t size, int type, struct isaac_state *s, unsigned long k, unsigned long n) { - off_t off; /* Offset into file */ - off_t thresh; /* Offset for next status update */ + off_t cursize; /* Amount of file remaining to wipe (counts down) */ + off_t thresh; /* cursize at which next status update is printed */ size_t lim; /* Amount of data to try writing */ size_t soff; /* Offset into buffer for next write */ ssize_t ssize; /* Return value from write() */ @@ -656,9 +697,9 @@ dopass (int fd, char const *name, off_t size, int type, #endif char pass_string[PASS_NAME_SIZE]; /* Name of current pass */ - if (lseek (fd, 0, SEEK_SET) == (off_t) -1) + if (lseek (fd, 0, SEEK_SET) < 0) { - error (0, errno, "%s: %s", quotearg_colon (name), _("cannot rewind")); + error (0, 0, _("Error seeking `%s'"), name); return -1; } @@ -666,7 +707,7 @@ dopass (int fd, char const *name, off_t size, int type, if (type >= 0) { lim = sizeof (r); - if (0 <= size && (off_t) lim > size) + if ((off_t) lim > size) { lim = (size_t) size; } @@ -682,17 +723,17 @@ dopass (int fd, char const *name, off_t size, int type, thresh = 0; if (n) { - pfstatus (_("%s: pass %lu/%lu (%s)...)"), quotearg_colon (name), - k, n, pass_string); - thresh = VERBOSE_UPDATE; + pfstatus (_("%s: pass %lu/%lu (%s)...)"), name, k, n, pass_string); + if (size > VERBOSE_UPDATE) + thresh = size - VERBOSE_UPDATE; } - for (off = 0; off < size || size < 0; ) + for (cursize = size; cursize;) { /* How much to write this time? */ lim = sizeof (r); - if (0 <= size && size - off < (off_t) lim) - lim = (size_t) (size - off); + if ((off_t) lim > cursize) + lim = (size_t) cursize; if (type < 0) fillrand (s, r, lim); /* Loop to retry partial writes. */ @@ -701,46 +742,38 @@ dopass (int fd, char const *name, off_t size, int type, ssize = write (fd, (char *) r + soff, lim - soff); if (ssize < 0) { - char buf[LONGEST_HUMAN_READABLE + 1]; - if (size < 0 && errno == EIO) - { - /* Now we know the file size, since we fell off the end. */ - thresh = size = off + lim; - break; - } - error (0, errno, _("%s: cannot write at offset %s"), - quotearg_colon (name), - human_readable ((uintmax_t) (off + soff), - buf, 1, 1)); + int e = errno; + error (0, 0, _("Error writing `%s' at %lu"), + name, size - cursize + soff); + /* FIXME: this is slightly fragile in that some systems + may fail with a different errno. */ + /* This error confuses people. */ + if (e == EBADF && fd == 0) + fputs (_("(Did you remember to open stdin read/write with \"<>file\"?)\n"), + stderr); return -1; } } /* Okay, we have written "lim" bytes. */ - off += lim; + cursize -= lim; /* Time to print progress? */ - if (thresh <= off && n) + if (cursize <= thresh && n) { - char offbuf[LONGEST_HUMAN_READABLE + 1]; - char sizebuf[LONGEST_HUMAN_READABLE + 1]; - pfstatus (_("%s: pass %lu/%lu (%s)...%s/%s"), - quotearg_colon (name), k, n, pass_string, - human_readable ((uintmax_t) off, offbuf, 1, - output_block_size), - (size < 0 - ? "?" - : human_readable ((uintmax_t) size, sizebuf, 1, - output_block_size))); - thresh += VERBOSE_UPDATE; - if (! (0 <= thresh && (thresh < size || size < 0))) - thresh = size; + pfstatus (_("%s: pass %lu/%lu (%s)...%lu/%lu K"), + name, k, n, pass_string, + (size - cursize + 1023) / 1024, (size + 1023) / 1024); + if (thresh > VERBOSE_UPDATE) + thresh -= VERBOSE_UPDATE; + else + thresh = 0; } } /* Force what we just wrote to hit the media. */ - if (fdatasync (fd) < 0 && fsync (fd) < 0) + if (fdatasync (fd) < 0) { - error (0, errno, "%s: fsync", quotearg_colon (name)); + error (0, 0, _("Error syncing `%s'"), name); return -1; } return 0; @@ -944,73 +977,103 @@ genpattern (int *dest, size_t num, struct isaac_state *s) /* * The core routine to actually do the work. This overwrites the first - * size bytes of the given fd. Returns -1 on error, 0 on success. + * size bytes of the given fd. Returns -1 on error, 0 on success with + * regular files, and 1 on success with non-regular files. */ static int wipefd (int fd, char const *name, struct isaac_state *s, - struct Options const *flags) + size_t passes, struct Options const *flags) { size_t i; struct stat st; - off_t size; /* Size to write; -1 if not known */ - size_t passes = flags->n_iterations; + off_t size, seedsize; /* Size to write, size to read */ unsigned long n; /* Number of passes for printing purposes */ + int *passarray; + + if (!passes) + passes = DEFAULT_PASSES; n = 0; /* dopass takes n -- 0 to mean "don't print progress" */ if (flags->verbose) n = passes + ((flags->zero_fill) != 0); - if (fstat (fd, &st) != 0) + if (fstat (fd, &st)) { - error (0, errno, "%s: fstat", quotearg_colon (name)); + error (0, 0, _("Can't fstat file `%s'"), name); return -1; } - size = S_ISREG (st.st_mode) ? st.st_size : (off_t) -1; + /* Check for devices */ + if (!S_ISREG (st.st_mode) && !(flags->allow_devices)) + { + error (0, 0, + _("`%s' is not a regular file: use -d to enable operations on devices"), + name); + return -1; + } + + /* Allocate pass array */ + passarray = malloc (passes * sizeof (int)); + if (!passarray) + { + error (0, 0, _("unable to allocate storage for %lu passes"), + (unsigned long) passes); + return -1; + } - if (0 < size && 0 < st.st_blksize && !(flags->exact)) + seedsize = size = st.st_size; + if (!size) + { + /* Reluctant to talk? Apply thumbscrews. */ + seedsize = size = sizefd (fd); + } + else if (st.st_blksize && !(flags->exact)) { /* Round up to the next st_blksize to include "slack" */ size += st.st_blksize - 1 - (size - 1) % st.st_blksize; - if (size < 0) - size = TYPE_MAXIMUM (off_t); } - if (passes) + /* + * Use the file itself as seed material. Avoid wasting "lots" + * of time (>10% of the write time) reading "large" (>16K) + * files for seed material if there aren't many passes. + * + * Note that "seedsize*passes/10" risks overflow, while + * "seedsize/10*passes is slightly inaccurate. The hack + * here manages perfection with no overflow. + */ + if (passes < 10 && seedsize > 16384) { - /* Allocate pass array */ - int *passarray = malloc (passes * sizeof (int)); - if (!passarray) - { - error (0, 0, _("virtual memory exhausted")); - return -1; - } + seedsize -= 16384; + seedsize = seedsize / 10 * passes + seedsize % 10 * passes / 10; + seedsize += 16384; + } + (void) isaac_seedfd (s, fd, seedsize); - /* Schedule the passes in random order. */ - genpattern (passarray, passes, s); + /* Schedule the passes in random order. */ + genpattern (passarray, passes, s); - /* Do the work */ - for (i = 0; i < passes; i++) + /* Do the work */ + for (i = 0; i < passes; i++) + { + if (dopass (fd, name, size, passarray[i], s, i + 1, n) < 0) { - if (dopass (fd, name, size, passarray[i], s, i + 1, n) < 0) - { - memset (passarray, 0, passes * sizeof (int)); - free (passarray); - return -1; - } - if (flags->verbose > 1) - flushstatus (); + memset (passarray, 0, passes * sizeof (int)); + free (passarray); + return -1; } - - memset (passarray, 0, passes * sizeof (int)); - free (passarray); + if (flags->verbose > 1) + flushstatus (); } + memset (passarray, 0, passes * sizeof (int)); + free (passarray); + if (flags->zero_fill) if (dopass (fd, name, size, 0, s, passes + 1, n) < 0) return -1; - return 0; + return !S_ISREG (st.st_mode); } /* Characters allowed in a file name - a safe universal set. */ @@ -1057,9 +1120,9 @@ incname (char *name, unsigned len) * Repeatedly rename a file with shorter and shorter names, * to obliterate all traces of the file name on any system that * adds a trailing delimiter to on-disk file names and reuses - * the same directory slot. Finally, remove it. + * the same directory slot. Finally, delete it. * The passed-in filename is modified in place to the new filename. - * (Which is removed if this function succeeds, but is still present if + * (Which is deleted if this function succeeds, but is still present if * it fails for some reason.) * * The main loop is written carefully to not get stuck if all possible @@ -1067,7 +1130,7 @@ incname (char *name, unsigned len) * the original to 0. While the length is non-zero, it tries to find an * unused file name of the given length. It continues until either the * name is available and the rename succeeds, or it runs out of names - * to try (incname() wraps and returns 1). Finally, it removes the file. + * to try (incname() wraps and returns 1). Finally, it deletes the file. * * Note that rename() and remove() are both in the ANSI C standard, * so that part, at least, is NOT Unix-specific. @@ -1078,7 +1141,7 @@ incname (char *name, unsigned len) * insist that it works, just fall back to a global sync() in that case. * Unfortunately, this code is Unix-specific. */ -static int +int wipename (char *oldname, struct Options const *flags) { char *newname, *origname = 0; @@ -1088,12 +1151,12 @@ wipename (char *oldname, struct Options const *flags) int dir_fd; /* Try to open directory to sync *it* */ if (flags->verbose) - pfstatus (_("%s: removing"), quotearg_colon (oldname)); + pfstatus (_("%s: deleting"), oldname); newname = strdup (oldname); /* This is a malloc */ if (!newname) { - error (0, 0, _("virtual memory exhausted")); + error (0, 0, _("malloc failed")); return -1; } if (flags->verbose) @@ -1101,7 +1164,7 @@ wipename (char *oldname, struct Options const *flags) origname = strdup (oldname); if (!origname) { - error (0, 0, _("virtual memory exhausted")); + error (0, 0, _("malloc failed")); free (newname); return -1; } @@ -1132,7 +1195,7 @@ wipename (char *oldname, struct Options const *flags) if (access (newname, F_OK) < 0 && !rename (oldname, newname)) { - if (dir_fd < 0 || (fdatasync (dir_fd) < 0 && fsync (dir_fd) < 0)) + if (dir_fd < 0 || fdatasync (dir_fd) < 0) sync (); /* Force directory out */ if (origname) { @@ -1140,8 +1203,7 @@ wipename (char *oldname, struct Options const *flags) deliberate. It makes the -v output more intelligible at the expense of making the `renamed to ...' messages use the logical (original) file name. */ - pfstatus (_("%s: renamed to: %s"), - quotearg_colon (origname), newname); + pfstatus (_("%s: renamed to `%s'"), origname, newname); if (flags->verbose > 1) flushstatus (); } @@ -1153,14 +1215,14 @@ wipename (char *oldname, struct Options const *flags) len--; } free (newname); - err = remove (oldname) ? errno : 0; - if (dir_fd < 0 || (fdatasync (dir_fd) < 0 && fsync (dir_fd) < 0)) + err = remove (oldname); + if (dir_fd < 0 || fdatasync (dir_fd) < 0) sync (); close (dir_fd); if (origname) { if (!err && flags->verbose) - pfstatus (_("%s: removed"), quotearg_colon (origname)); + pfstatus (_("%s: deleted"), origname); free (origname); } return err; @@ -1168,65 +1230,43 @@ wipename (char *oldname, struct Options const *flags) /* * Finally, the function that actually takes a filename and grinds - * it into hamburger. Returns nonzero if there was an error. + * it into hamburger. Returns 1 if it was not a regular file. + * + * FIXME + * Detail to note: since we do not restore errno to EACCES after + * a failed chmod, we end up printing the error code from the chmod. + * This is probably either EACCES again or EPERM, which both give + * reasonable error messages. But it might be better to change that. */ static int -wipefile (char *name, struct isaac_state *s, struct Options const *flags) +wipefile (char *name, struct isaac_state *s, size_t passes, + struct Options const *flags) { - int err = 0; - struct stat st; - int remove_link = flags->links == ALL_LINKS; + int err, fd; - if (flags->links == ORDINARY_LINKS - || (flags->contents == FREED_CONTENTS && remove_link)) + fd = open (name, O_RDWR); + if (fd < 0 && errno == EACCES && flags->force) { - if (lstat (name, &st) != 0) - { - error (0, errno, "%s", quotearg_colon (name)); - return -1; - } - if (flags->links == ORDINARY_LINKS) - remove_link = S_ISREG (st.st_mode) || S_ISLNK (st.st_mode); + if (chmod (name, 0600) >= 0) + fd = open (name, O_RDWR); } - - if (flags->contents == ALL_CONTENTS - || (flags->contents == FREED_CONTENTS && remove_link - && !S_ISLNK (st.st_mode) && st.st_nlink <= 1)) + if (fd < 0) { - int fd = open (name, O_WRONLY); - if (fd < 0) - { - if (errno == EACCES && flags->force) - { - if (chmod (name, S_IWUSR) != 0) - { - error (0, errno, "%s: %s", quotearg_colon (name), - _("cannot change permissions")); - return -1; - } - fd = open (name, O_WRONLY); - } - if (fd < 0) - { - error (0, errno, "%s", quotearg_colon (name)); - return -1; - } - } - - err = wipefd (fd, name, s, flags); - if (close (fd) != 0) - { - error (0, errno, "%s: close", quotearg_colon (name)); - return -1; - } + error (0, 0, _("Unable to open `%s'"), name); + return -1; } - if (err == 0 && remove_link) + err = wipefd (fd, name, s, passes, flags); + close (fd); + /* + * Wipe the name and unlink - regular files only, no devices! + * (wipefd returns 1 for non-regular files.) + */ + if (err == 0 && flags->remove_file) { err = wipename (name, flags); - if (err != 0) - error (0, err < 0 ? 0 : err, "%s: %s", quotearg_colon (name), - _("cannot remove")); + if (err < 0) + error (0, 0, _("Unable to delete file `%s'"), name); } return err; } @@ -1237,6 +1277,7 @@ main (int argc, char **argv) struct isaac_state s; int err = 0; struct Options flags; + unsigned long n_passes = 0; char **file; int n_files; int c; @@ -1250,44 +1291,25 @@ main (int argc, char **argv) isaac_seed (&s); memset (&flags, 0, sizeof flags); - flags.contents = ALL_CONTENTS; - flags.n_iterations = DEFAULT_PASSES; - while ((c = getopt_long (argc, argv, "bcCfklLn:pvxz", long_opts, NULL)) != -1) + /* By default, remove each file after sanitization. */ + flags.remove_file = 1; + + while ((c = getopt_long (argc, argv, "dfn:pvxz", long_opts, NULL)) != -1) { switch (c) { case 0: break; - case 'b': - flags.contents = NO_CONTENTS; - break; - - case 'c': - flags.contents = FREED_CONTENTS; - break; - - case 'C': - flags.contents = ALL_CONTENTS; + case 'd': + flags.allow_devices = 1; break; case 'f': flags.force = 1; break; - case 'k': - flags.links = NO_LINKS; - break; - - case 'l': - flags.links = ORDINARY_LINKS; - break; - - case 'L': - flags.links = ALL_LINKS; - break; - case 'n': { unsigned long int tmp_ulong; @@ -1295,12 +1317,17 @@ main (int argc, char **argv) || (word32) tmp_ulong != tmp_ulong || ((size_t) (tmp_ulong * sizeof (int)) / sizeof (int) != tmp_ulong)) - error (1, 0, "%s: %s", quotearg_colon (optarg), - _("invalid number of passes")); - flags.n_iterations = tmp_ulong; + { + error (1, 0, _("invalid number of passes: %s"), optarg); + } + n_passes = tmp_ulong; } break; + case 'p': + flags.remove_file = 0; + break; + case 'v': flags.verbose++; break; @@ -1327,7 +1354,7 @@ main (int argc, char **argv) if (n_files == 0) { - error (0, 0, _("too few arguments")); + error (0, 0, _("missing file argument")); usage (1); } @@ -1335,24 +1362,13 @@ main (int argc, char **argv) { if (STREQ (file[i], "-")) { - int fd_flags = fcntl (STDOUT_FILENO, F_GETFL); - if (fd_flags < 0) - { - error (0, errno, _("standard output")); - err = 1; - } - else if ((fd_flags & O_APPEND) != 0) - { - error (0, 0, _("cannot shred append-only standard output")); - err = 1; - } - else if (wipefd (STDOUT_FILENO, file[i], &s, &flags) < 0) + if (wipefd (0, file[i], &s, (size_t) n_passes, &flags) < 0) err = 1; } else { /* Plain filename - Note that this overwrites *argv! */ - if (wipefile (file[i], &s, &flags) < 0) + if (wipefile (file[i], &s, (size_t) n_passes, &flags) < 0) err = 1; } flushstatus ();