summaryrefslogtreecommitdiff
path: root/src/shred.c
diff options
context:
space:
mode:
authorPádraig Brady <P@draigBrady.com>2013-11-07 13:26:25 +0000
committerPádraig Brady <P@draigBrady.com>2013-11-08 13:33:50 +0000
commitc93d5b4aa7f675dabaebf5e4ee9583162e4c977d (patch)
tree4d2f9263b40382fb0c133cf5a46392595e3acae5 /src/shred.c
parentbf6bf52dce37832b03ecfbe1b3a3b104f532df42 (diff)
downloadcoreutils-c93d5b4aa7f675dabaebf5e4ee9583162e4c977d.tar.xz
shred: fix direct I/O failures for last write to file
Since direct I/O is now enabled with commit v8.21-139-gebaf961 we must handle the case where we write an odd size at the end of a file (with --exact), or we specify an odd --size that is larger than 64KiB, or in the very unlikely case of a device with an odd size. This issue was present since direct I/O support was first added in v5.3.0, but latent since v6.0. Theoretically this could have also been an issue after that on systems which didn't have alignment constraints, but did have size constraints for direct I/O. * src/shred.c (dopass): On the first pass for a file, always retry a write that fails with EINVAL, so we handle direct I/O failure at either the start or end of the file. Adjust the comment as the original case is out of date and implicitly handled by this more general fix. * tests/misc/shred-exact.sh: Add a test case. * NEWS: Add a "bug fix" entry for shred since there are two related issues now fixed.
Diffstat (limited to 'src/shred.c')
-rw-r--r--src/shred.c21
1 files changed, 10 insertions, 11 deletions
diff --git a/src/shred.c b/src/shred.c
index 9ff723865..98dd8726c 100644
--- a/src/shred.c
+++ b/src/shred.c
@@ -394,7 +394,7 @@ dopass (int fd, char const *qname, off_t *sizep, int type,
char pass_string[PASS_NAME_SIZE]; /* Name of current pass */
bool write_error = false;
bool other_error = false;
- bool first_write = true;
+ bool tried_without_directio = false;
/* Printable previous offset into the file */
char previous_offset_buf[LONGEST_HUMAN_READABLE + 1];
@@ -443,7 +443,7 @@ dopass (int fd, char const *qname, off_t *sizep, int type,
if (type < 0)
randread (s, pbuf, lim);
/* Loop to retry partial writes. */
- for (soff = 0; soff < lim; soff += ssize, first_write = false)
+ for (soff = 0; soff < lim; soff += ssize)
{
ssize = write (fd, pbuf + soff, lim - soff);
if (ssize <= 0)
@@ -459,17 +459,15 @@ dopass (int fd, char const *qname, off_t *sizep, int type,
int errnum = errno;
char buf[INT_BUFSIZE_BOUND (uintmax_t)];
- /* If the first write of the first pass for a given file
- has just failed with EINVAL, turn off direct mode I/O
- and try again. This works around a bug in Linux kernel
- 2.4 whereby opening with O_DIRECT would succeed for some
- file system types (e.g., ext3), but any attempt to
- access a file through the resulting descriptor would
- fail with EINVAL. */
- if (k == 1 && first_write && errno == EINVAL)
+ /* Retry without direct I/O since this may not be supported
+ at all on some (file) systems, or with the current size.
+ I.E. a specified --size that is not aligned, or when
+ dealing with slop at the end of a file with --exact. */
+ if (k == 1 && !tried_without_directio && errno == EINVAL)
{
direct_mode (fd, false);
ssize = 0;
+ tried_without_directio = true;
continue;
}
error (0, errnum, _("%s: error writing at offset %s"),
@@ -478,7 +476,8 @@ dopass (int fd, char const *qname, off_t *sizep, int type,
/* 'shred' is often used on bad media, before throwing it
out. Thus, it shouldn't give up on bad blocks. This
code works because lim is always a multiple of
- SECTOR_SIZE, except at the end. */
+ SECTOR_SIZE, except at the end. This size constraint
+ also enables direct I/O on some (file) systems. */
verify (PERIODIC_OUTPUT_SIZE % SECTOR_SIZE == 0);
verify (NONPERIODIC_OUTPUT_SIZE % SECTOR_SIZE == 0);
if (errnum == EIO && 0 <= size && (soff | SECTOR_MASK) < lim)