1
0
mirror of git://git.sv.gnu.org/coreutils.git synced 2026-04-19 18:26:32 +02:00

back out Paul's changes

This commit is contained in:
Jim Meyering
1999-04-04 02:34:49 +00:00
parent c21cecaea8
commit 8fe009ed7c

View File

@@ -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 <colin@nyx.net>. 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)
s->mm[0] = getpid ();
s->mm[1] = getppid ();
/* 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. */
{
#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,54 +393,59 @@ 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);
}
{
uid_t t = getuid ();
MIXIN (t);
}
{
gid_t t = getgid ();
MIXIN (t);
}
isaac_init (s, s->mm, sizeof (s->mm));
}
/*
* 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];
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));
/* Final mix, as in isaac_init */
isaac_mix (s, s->mm);
return size - sizeleft;
}
/* Single-word RNG built on top of ISAAC */
struct irand_state
{
@@ -580,6 +568,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.
*
@@ -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;
}
if (0 < size && 0 < st.st_blksize && !(flags->exact))
/* 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;
}
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)
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);
/* Do the work */
for (i = 0; i < passes; i++)
{
if (dopass (fd, name, size, passarray[i], s, i + 1, n) < 0)
{
error (0, 0, _("virtual memory exhausted"));
memset (passarray, 0, passes * sizeof (int));
free (passarray);
return -1;
}
/* Schedule the passes in random order. */
genpattern (passarray, passes, s);
/* Do the work */
for (i = 0; i < passes; i++)
{
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);
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 (fd < 0)
{
error (0, 0, _("Unable to open `%s'"), name);
return -1;
}
if (flags->contents == ALL_CONTENTS
|| (flags->contents == FREED_CONTENTS && remove_link
&& !S_ISLNK (st.st_mode) && st.st_nlink <= 1))
{
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;
}
}
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 ();