diff options
author | Jim Meyering <jim@meyering.net> | 2004-06-07 15:27:42 +0000 |
---|---|---|
committer | Jim Meyering <jim@meyering.net> | 2004-06-07 15:27:42 +0000 |
commit | 16155336a095fc4e749d42608e747bce4082230f (patch) | |
tree | 033f67752e21673342b4d5e4da8f742829fe4dad /src/shred.c | |
parent | 8f3e7ea8136036a29b2289e81c7906deb11bc944 (diff) | |
download | coreutils-16155336a095fc4e749d42608e747bce4082230f.tar.xz |
Enable direct-mode I/O (bypassing the buffer cache), if possible.
Prompted by a suggestion from Kalle Olavi Niemitalo
in http://bugs.debian.org/207035.
(direct_mode): New function.
(do_wipefd): Turn on direct-mode I/O.
(dopass): If a file's first write fails with EINVAL,
turn off direct-mode I/O and retry the write.
Diffstat (limited to 'src/shred.c')
-rw-r--r-- | src/shred.c | 44 |
1 files changed, 43 insertions, 1 deletions
diff --git a/src/shred.c b/src/shred.c index 0d66c1cd2..44be403f5 100644 --- a/src/shred.c +++ b/src/shred.c @@ -810,6 +810,31 @@ dosync (int fd, char const *qname) return 0; } +/* Turn on or off direct I/O mode for file descriptor FD, if possible. + Try to turn it on if ENABLE is true. Otherwise, try to turn it off. */ +static void +direct_mode (int fd, bool enable) +{ + if (O_DIRECT) + { + int fd_flags = fcntl (fd, F_GETFL); + if (0 < fd_flags) + { + int new_flags = (enable + ? (fd_flags | O_DIRECT) + : (fd_flags & ~O_DIRECT)); + if (new_flags != fd_flags) + fcntl (fd, F_SETFL, new_flags); + } + } + +#if HAVE_DIRECTIO && defined DIRECTIO_ON + /* This is Solaris-specific. See the following for details: + http://docs.sun.com/db/doc/816-0213/6m6ne37so?q=directio&a=view */ + directio (fd, DIRECTIO_ON); +#endif +} + /* * 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 @@ -836,6 +861,7 @@ dopass (int fd, char const *qname, off_t *sizep, int type, size_t ralign = lcm (getpagesize (), sizeof *r); /* Fill alignment. */ char pass_string[PASS_NAME_SIZE]; /* Name of current pass */ bool write_error = false; + bool first_write = true; /* Printable previous offset into the file */ char previous_offset_buf[LONGEST_HUMAN_READABLE + 1]; @@ -890,7 +916,7 @@ dopass (int fd, char const *qname, off_t *sizep, int type, if (type < 0) fillrand (s, r, rsize, lim); /* Loop to retry partial writes. */ - for (soff = 0; soff < lim; soff += ssize) + for (soff = 0; soff < lim; soff += ssize, first_write = false) { ssize = write (fd, (char *) r + soff, lim - soff); if (ssize <= 0) @@ -906,6 +932,20 @@ 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-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) + { + direct_mode (fd, false); + ssize = 0; + continue; + } error (0, errnum, _("%s: error writing at offset %s"), qname, umaxtostr ((uintmax_t) offset + soff, buf)); /* @@ -1243,6 +1283,8 @@ do_wipefd (int fd, char const *qname, struct isaac_state *s, return false; } + direct_mode (fd, true); + /* Allocate pass array */ passarray = xnmalloc (flags->n_iterations, sizeof *passarray); |