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:
552
src/shred.c
552
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 <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 ();
|
||||
|
||||
Reference in New Issue
Block a user