1
0
mirror of git://git.sv.gnu.org/coreutils.git synced 2026-08-03 14:59:53 +02:00

In shred, check for errors from fdatasync more carefully. If

fdatasync fails with errno==EINVAL, it means this implementation
does not support synchronized I/O for this file.  Do not report
this as an error, as (for example) AIX 5.2 fdatasync reports it
for raw disk devices.  Problem reported by Albert Chin in
<http://mail.gnu.org/archive/html/bug-gnu-utils/2004-05/msg00028.html>.

Check for write errors, though: the old code ignored them.
Improve error checking in a few other cases, too (e.g., close of a
directory).

Also, change several 'int' values to 'bool', so that the error
checking is a bit clearer.  Similarly, change unsigned values
to size_t where appropriate.

* src/shred.c: Include "dirname.h".
(datasync) [!HAVE_FDATASYNC]: Remove.
(dosync): New function.
(dopass): Use it.  Return 1 on write error, -1 on other error.
All callers changed.  Report write error if dosync does.
(do_wipefd, wipefd, wipename, wipefile): Return bool (true/false),
not int (0/-1).  All callers changed.  Return false if there's a
write error.
(incname): Return bool (true/false), not int (0/1).  Accept
size_t length, not unsigned.  All callers changed.  Do not
bother checking for non-digits; it can't happen.  Replace
recursion with iteration.
(wipename): Use dir_name, base_name, etc. instead of assuming
Unix file names.  Use size_t for length, not unsigned.
Report error if unlink or close fails.
(wipename, main): Use bool for booleans.

(names): Use only digits and uppercase letters, for greater
portability.
This commit is contained in:
Jim Meyering
2004-05-16 19:32:30 +00:00
parent 1f90c86c41
commit 3bb407ccf3
+148 -113
View File
@@ -104,6 +104,7 @@
#include "system.h"
#include "xstrtol.h"
#include "dirname.h"
#include "error.h"
#include "getpagesize.h"
#include "human.h"
@@ -218,10 +219,6 @@ to be recovered later.\n\
exit (status);
}
#if ! HAVE_FDATASYNC
# define fdatasync(fd) -1
#endif
/*
* --------------------------------------------------------------------
* Bob Jenkins' cryptographic random number generator, ISAAC.
@@ -776,6 +773,42 @@ passname (unsigned char const *data, char name[PASS_NAME_SIZE])
memcpy (name, "random", PASS_NAME_SIZE);
}
/* Request that all data for FD be transferred to the corresponding
storage device. QNAME is the file name (quoted for colons), and
*ST its status. Report any errors found. Return 0 on success, -1
(setting errno) on failure. It is not an error if fdatasync and/or
fsync is not supported for this file. */
static int
dosync (int fd, char const *qname)
{
int err;
#if HAVE_FDATASYNC
if (fdatasync (fd) == 0)
return 0;
err = errno;
if (err != EINVAL)
{
error (0, err, "%s: fdatasync", qname);
errno = err;
return -1;
}
#endif
if (fsync (fd) == 0)
return 0;
err = errno;
if (err != EINVAL)
{
error (0, err, "%s: fsync", qname);
errno = err;
return -1;
}
sync ();
return 0;
}
/*
* Do pass number k of n, writing "size" bytes of the given pattern "type"
* to the file descriptor fd. Qname, k and n are passed in only for verbose
@@ -783,6 +816,8 @@ passname (unsigned char const *data, char name[PASS_NAME_SIZE])
*
* If *sizep == -1, the size is unknown, and it will be filled in as soon
* as writing fails.
*
* Return 1 on write error, -1 on other error, 0 on success.
*/
static int
dopass (int fd, char const *qname, off_t *sizep, int type,
@@ -799,6 +834,7 @@ dopass (int fd, char const *qname, off_t *sizep, int type,
size_t rsize = 3 * MAX (ISAAC_WORDS, 1024) * sizeof *r; /* Fill size. */
size_t ralign = lcm (getpagesize (), sizeof *r); /* Fill alignment. */
char pass_string[PASS_NAME_SIZE]; /* Name of current pass */
bool write_error = false;
/* Printable previous offset into the file */
char previous_offset_buf[LONGEST_HUMAN_READABLE + 1];
@@ -885,6 +921,7 @@ dopass (int fd, char const *qname, off_t *sizep, int type,
!= -1)
{
soff += 512;
write_error = true;
continue;
}
error (0, errno, "%s: lseek", qname);
@@ -953,22 +990,25 @@ dopass (int fd, char const *qname, off_t *sizep, int type,
* It's a common problem with programs that do lots of writes,
* like mkfs.
*/
if (fdatasync (fd) < 0 && fsync (fd) < 0)
if (dosync (fd, qname) != 0)
{
error (0, errno, "%s: fsync", qname);
return -1;
if (errno != EIO)
return -1;
write_error = true;
}
}
}
}
/* Force what we just wrote to hit the media. */
if (fdatasync (fd) < 0 && fsync (fd) < 0)
if (dosync (fd, qname) != 0)
{
error (0, errno, "%s: fsync", qname);
return -1;
if (errno != EIO)
return -1;
write_error = true;
}
return 0;
return write_error;
}
/*
@@ -1167,9 +1207,9 @@ 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. Return true if successful.
*/
static int
static bool
do_wipefd (int fd, char const *qname, struct isaac_state *s,
struct Options const *flags)
{
@@ -1178,6 +1218,7 @@ do_wipefd (int fd, char const *qname, struct isaac_state *s,
off_t size; /* Size to write, size to read */
unsigned long n; /* Number of passes for printing purposes */
int *passarray;
bool ok = true;
n = 0; /* dopass takes n -- 0 to mean "don't print progress" */
if (flags->verbose)
@@ -1186,7 +1227,7 @@ do_wipefd (int fd, char const *qname, struct isaac_state *s,
if (fstat (fd, &st))
{
error (0, errno, "%s: fstat", qname);
return -1;
return false;
}
/* If we know that we can't possibly shred the file, give up now.
@@ -1197,7 +1238,7 @@ do_wipefd (int fd, char const *qname, struct isaac_state *s,
|| S_ISSOCK (st.st_mode))
{
error (0, 0, _("%s: invalid file type"), qname);
return -1;
return false;
}
/* Allocate pass array */
@@ -1214,7 +1255,7 @@ do_wipefd (int fd, char const *qname, struct isaac_state *s,
if (size < 0)
{
error (0, 0, _("%s: file has negative size"), qname);
return -1;
return false;
}
}
else
@@ -1245,11 +1286,16 @@ do_wipefd (int fd, char const *qname, struct isaac_state *s,
/* Do the work */
for (i = 0; i < flags->n_iterations; i++)
{
if (dopass (fd, qname, &size, passarray[i], s, i + 1, n) < 0)
int err = dopass (fd, qname, &size, passarray[i], s, i + 1, n);
if (err)
{
memset (passarray, 0, flags->n_iterations * sizeof (int));
free (passarray);
return -1;
if (err < 0)
{
memset (passarray, 0, flags->n_iterations * sizeof (int));
free (passarray);
return false;
}
ok = false;
}
}
@@ -1257,8 +1303,15 @@ do_wipefd (int fd, char const *qname, struct isaac_state *s,
free (passarray);
if (flags->zero_fill)
if (dopass (fd, qname, &size, 0, s, flags->n_iterations + 1, n) < 0)
return -1;
{
int err = dopass (fd, qname, &size, 0, s, flags->n_iterations + 1, n);
if (err)
{
if (err < 0)
return false;
ok = false;
}
}
/* Okay, now deallocate the data. The effect of ftruncate on
non-regular files is unspecified, so don't worry about any
@@ -1267,14 +1320,14 @@ do_wipefd (int fd, char const *qname, struct isaac_state *s,
&& S_ISREG (st.st_mode))
{
error (0, errno, _("%s: error truncating"), qname);
return -1;
return false;
}
return 0;
return ok;
}
/* A wrapper with a little more checking for fds on the command line */
static int
static bool
wipefd (int fd, char const *qname, struct isaac_state *s,
struct Options const *flags)
{
@@ -1283,12 +1336,12 @@ wipefd (int fd, char const *qname, struct isaac_state *s,
if (fd_flags < 0)
{
error (0, errno, "%s: fcntl", qname);
return -1;
return false;
}
if (fd_flags & O_APPEND)
{
error (0, 0, _("%s: cannot shred append-only file descriptor"), qname);
return -1;
return false;
}
return do_wipefd (fd, qname, s, flags);
}
@@ -1296,43 +1349,32 @@ wipefd (int fd, char const *qname, struct isaac_state *s,
/* --- Name-wiping code --- */
/* Characters allowed in a file name - a safe universal set. */
static char const nameset[] =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_+=%@#.";
static char const nameset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/*
* This increments the name, considering it as a big-endian base-N number
* with the digits taken from nameset. Characters not in the nameset
* are considered to come before nameset[0].
*
* It's not obvious, but this will explode if name[0..len-1] contains
* any 0 bytes.
*
* This returns the carry (1 on overflow).
*/
static int
incname (char *name, unsigned len)
/* Increment NAME (with LEN bytes). NAME must be a big-endian base N
number with the digits taken from nameset. Return true if
successful if not (because NAME already has the greatest possible
value. */
static bool
incname (char *name, size_t len)
{
char const *p;
if (!len)
return 1;
p = strchr (nameset, name[--len]);
/* If the character is not found, replace it with a 0 digit */
if (!p)
while (len--)
{
char const *p = strchr (nameset, name[--len]);
/* If this character has a successor, use it. */
if (p[1])
{
name[len] = p[1];
return true;
}
/* Otherwise, set this digit to 0 and increment the prefix. */
name[len] = nameset[0];
return 0;
}
/* If this character has a successor, use it */
if (p[1])
{
name[len] = p[1];
return 0;
}
/* Otherwise, set this digit to 0 and increment the prefix */
name[len] = nameset[0];
return incname (name, len);
return false;
}
/*
@@ -1361,35 +1403,21 @@ incname (char *name, unsigned len)
* This is fairly significantly Unix-specific. Of course, on any
* filesystem with synchronous metadata updates, this is unnecessary.
*/
static int
static bool
wipename (char *oldname, char const *qoldname, struct Options const *flags)
{
char *newname, *base; /* Base points to filename part of newname */
unsigned len;
int first = 1;
int err;
int dir_fd; /* Try to open directory to sync *it* */
char *newname = xstrdup (oldname);
char *base = base_name (newname);
size_t len = base_len (base);
char *dir = dir_name (newname);
char *qdir = xstrdup (quotearg_colon (dir));
int dir_fd = open (dir, O_RDONLY | O_NOCTTY);
bool first = true;
bool ok = true;
newname = xstrdup (oldname);
if (flags->verbose)
error (0, 0, _("%s: removing"), qoldname);
/* Find the file name portion */
base = strrchr (newname, '/');
/* Temporary hackery to get a directory fd */
if (base)
{
*base = '\0';
dir_fd = open (newname, O_RDONLY | O_NOCTTY);
*base = '/';
}
else
{
dir_fd = open (".", O_RDONLY | O_NOCTTY);
}
base = base ? base + 1 : newname;
len = strlen (base);
while (len)
{
memset (base, nameset[0], len);
@@ -1401,9 +1429,8 @@ wipename (char *oldname, char const *qoldname, struct Options const *flags)
{
if (rename (oldname, newname) == 0)
{
if (dir_fd < 0
|| (fdatasync (dir_fd) < 0 && fsync (dir_fd) < 0))
sync (); /* Force directory out */
if (0 <= dir_fd && dosync (dir_fd, qdir) != 0)
ok = false;
if (flags->verbose)
{
/*
@@ -1414,7 +1441,7 @@ wipename (char *oldname, char const *qoldname, struct Options const *flags)
*/
char const *old = (first ? qoldname : oldname);
error (0, 0, _("%s: renamed to %s"), old, newname);
first = 0;
first = false;
}
memcpy (oldname + (base - newname), base, len + 1);
break;
@@ -1430,17 +1457,30 @@ wipename (char *oldname, char const *qoldname, struct Options const *flags)
/* newname exists, so increment BASE so we use another */
}
}
while (!incname (base, len));
while (incname (base, len));
len--;
}
free (newname);
err = unlink (oldname);
if (dir_fd < 0 || (fdatasync (dir_fd) < 0 && fsync (dir_fd) < 0))
sync ();
close (dir_fd);
if (!err && flags->verbose)
if (unlink (oldname) != 0)
{
error (0, errno, "%s: remove", qoldname);
ok = false;
}
else if (flags->verbose)
error (0, 0, _("%s: removed"), qoldname);
return err;
if (0 <= dir_fd)
{
if (dosync (dir_fd, qdir) != 0)
ok = false;
if (close (dir_fd) != 0)
{
error (0, errno, "%s: close", qdir);
ok = false;
}
}
free (newname);
free (dir);
free (qdir);
return ok;
}
/*
@@ -1455,11 +1495,12 @@ wipename (char *oldname, char const *qoldname, struct Options const *flags)
* again or EPERM, which both give similar error messages.
* Does anyone disagree?
*/
static int
static bool
wipefile (char *name, char const *qname,
struct isaac_state *s, struct Options const *flags)
{
int err, fd;
bool ok;
int fd;
fd = open (name, O_WRONLY | O_NOCTTY);
if (fd < 0)
@@ -1491,29 +1532,25 @@ wipefile (char *name, char const *qname,
if (fd < 0)
{
error (0, errno, "%s", qname);
return -1;
return false;
}
err = do_wipefd (fd, qname, s, flags);
ok = do_wipefd (fd, qname, s, flags);
if (close (fd) != 0)
{
error (0, 0, "%s: close", qname);
err = -1;
error (0, errno, "%s: close", qname);
ok = false;
}
if (err == 0 && flags->remove_file)
{
err = wipename (name, qname, flags);
if (err < 0)
error (0, 0, _("%s: cannot remove"), qname);
}
return err;
if (ok && flags->remove_file)
ok = wipename (name, qname, flags);
return ok;
}
int
main (int argc, char **argv)
{
struct isaac_state s;
int err = 0;
bool ok = true;
struct Options flags;
char **file;
int n_files;
@@ -1612,14 +1649,12 @@ main (int argc, char **argv)
char *qname = xstrdup (quotearg_colon (file[i]));
if (STREQ (file[i], "-"))
{
if (wipefd (STDOUT_FILENO, qname, &s, &flags) < 0)
err = 1;
ok &= wipefd (STDOUT_FILENO, qname, &s, &flags);
}
else
{
/* Plain filename - Note that this overwrites *argv! */
if (wipefile (file[i], qname, &s, &flags) < 0)
err = 1;
ok &= wipefile (file[i], qname, &s, &flags);
}
free (qname);
}
@@ -1627,7 +1662,7 @@ main (int argc, char **argv)
/* Just on general principles, wipe s. */
memset (&s, 0, sizeof s);
exit (err == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
}
/*
* vim:sw=2:sts=2: