bug-coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

possible bug - output stream handling inconsistency in dd


From: Theodoros V. Kalamatianos
Subject: possible bug - output stream handling inconsistency in dd
Date: Mon, 31 Oct 2005 13:55:45 +0200 (EET)

Hello,

While trying to implement the per-block seek/skip options I suggested I encountered an inconsistency between the handling of regular files and stdout in the following case:

dd if=/dev/zero count=0 seek=1

To reproduce the inconsistency:

# rm -f test; dd if=/dev/zero count=0 seek=1 of=test; ll test
0+0 records in
0+0 records out
0 bytes (0 B) copied, 3,9e-05 seconds, 0,0 kB/s
-rw-r--r-- 1 root root 512 Oct 31 13:37 test

# rm -f test; dd if=/dev/zero count=0 seek=1 > test; ll test
0+0 records in
0+0 records out
0 bytes (0 B) copied, 3,6e-05 seconds, 0,0 kB/s
-rw-r--r-- 1 root root 0 Oct 31 13:37 test

Note the difference in the resulting file sizes.

In detail:

- when the output is a regular file ftruncate() is used to truncate the file. This means that if the file is smaller than obs*seek it is expanded to that size.

- when the output is stdout ftruncate() is not used and no expansion happens.

I think that the proper behaviour would be to ftruncate() the output file only when it is larger than the requested seek size. The following patch corrects this inconsistency:

--- dd.c.orig   2005-10-31 10:21:15.000000000 +0200
+++ dd.c.orig.cmp       2005-10-31 13:49:14.000000000 +0200
@@ -1651,6 +1651,7 @@
 #if HAVE_FTRUNCATE
       if (seek_records != 0 && !(conversions_mask & C_NOTRUNC))
        {
+         struct stat stdout_stat;
          uintmax_t size = seek_records * output_blocksize;
          unsigned long int obs = output_blocksize;

@@ -1661,7 +1662,12 @@
                     " (%lu-byte) blocks"),
                   seek_records, obs);

-         if (ftruncate (STDOUT_FILENO, size) != 0)
+         if (fstat (STDOUT_FILENO, &stdout_stat) != 0)
+           error (EXIT_FAILURE, errno, _("cannot fstat %s"),
+               quote (output_file));
+
+         if ((stdout_stat.st_size > size) &&
+             (ftruncate (STDOUT_FILENO, size) != 0))
            {
              /* Complain only when ftruncate fails on a regular file, a
directory, or a shared memory object, as POSIX 1003.1-2004
@@ -1669,10 +1675,6 @@
                 For example, do not complain when Linux 2.4 ftruncate
                 fails on /dev/fd0.  */
              int ftruncate_errno = errno;
-             struct stat stdout_stat;
-             if (fstat (STDOUT_FILENO, &stdout_stat) != 0)
-               error (EXIT_FAILURE, errno, _("cannot fstat %s"),
-                      quote (output_file));
              if (S_ISREG (stdout_stat.st_mode)
                  || S_ISDIR (stdout_stat.st_mode)
                  || S_TYPEISSHM (&stdout_stat))


Note that this may introduce a race condition if the file size increases between the fstat and ftruncate calls, but I don't think that there is a way to do this atomically. Any comments are welcome...


Regards,

Theodoros Kalamatianos




reply via email to

[Prev in Thread] Current Thread [Next in Thread]