Subject: bug#10493: (no subject)

From: Jim Meyering <[email-masked]>
Date: Mon, 16 Jan 2012 09:27:59 +0100

Leontiev Danil wrote:
> Log for  "strace -o LOG shred -uvf /media/usb/test" in attachment.
>
> Operation system info:
>
> [root@localhost]$ uname -a
> Linux localhost 2.6.33.7-desktop586-1mnb #1 SMP Fri Aug 27 20:24:16
> UTC 2010 i686 i686 i386 GNU/Linux
>
> [root@localhost]# LANG=C shred --version
> shred (GNU coreutils) 8.5
...

Thanks for the strace log.
It suggests a problem with your file system or the
underlying device, or maybe a kernel bug.

> execve("/usr/bin/shred", ["shred", "-uvf", "/media/netbook-ia32-x86_64-20110"...], [/* 65 vars */]) = 0
> brk(0)                                  = 0x9385000
...
> open("/media/netbook-ia32-x86_64-201105181448/0", O_WRONLY|O_NOCTTY|O_LARGEFILE) = 3
...
> fcntl64(3, F_SETFL, O_WRONLY|O_DIRECT|O_LARGEFILE) = 0
...
> write(3, "\214~\372\267\214C\230\242,\372\357\333\331\34\257{\327\6{\362\363o\256\212\211\204\242\235\200\265\200\320"..., 4096) = 4096
> time(NULL)                              = 1326713309
> fdatasync(3)                            = 0
> _llseek(3, 0, [0], SEEK_SET)            = 0
> write(2, "shred: ", 7)                  = 7
> write(2, "/media/netbook-ia32-x86_64-20110"..., 71) = 71
> write(2, "\n", 1)                       = 1
> time(NULL)                              = 1326713309
> write(3, "TI\245\352\217)\3541\f\234\260\272h\330\246{\303\200\317A\372\217\374\21\25\17\376\32\34\320\34N"..., 4096) = -1 EINVAL (Invalid argument)

Note how the first 4KiB write succeeds,
yet the second one fails with EINVAL.

shred.c is designed to accommodate (and recover from) an EINVAL failure
when that failure happens on the very first write syscall.  However, when
it happens
after that, the code presumes there really is an error.

For the record, the diagnostic you're seeing is the one from the
end of this excerpt:

  while (true)
    {
      /* How much to write this time? */
      lim = sizeof r;
      if (0 <= size && size - offset < sizeof_r)
        {
          if (size < offset)
            break;
          lim = size - offset;
          if (!lim)
            break;
        }
      if (type < 0)
        randread (s, &r, lim);
      /* Loop to retry partial writes. */
      for (soff = 0; soff < lim; soff += ssize, first_write = false)
        {
          ssize = write (fd, r.c + soff, lim - soff);
          if (ssize <= 0)
            {
              if (size < 0 && (ssize == 0 || errno == ENOSPC))
                {
                  /* Ah, we have found the end of the file */
                  *sizep = size = offset + soff;
                  break;
                }
              else
                {
                  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)
                    {
                      direct_mode (fd, false);
                      ssize = 0;
                      continue;
                    }
                  error (0, errnum, _("%s: error writing at offset %s"),
                         qname, umaxtostr (offset + soff, buf));

If you're ambitious, you can find out if the kernel bug mentioned above
affects 2.6.x and is now triggered only on the *second* write (with 2.4,
it was triggered on the first).  To do that, could change this line in
src/shred.c,

-                 if (k == 1 && first_write && errno == EINVAL)
+                 if (k == 1 && errno == EINVAL)

and build shred from source.

Note however: that induces the risk of an infinite loop.
It will work fine if write fails with EINVAL only on the second
attempt, but then always succeeds after turning off O_DIRECT.