Subject: bug#7362: dd strangeness

From: Paul Eggert <[email-masked]>
Date: Thu, 03 Mar 2011 23:44:15 -0800

On 03/02/2011 05:12 AM, Pádraig Brady wrote:

> So I'm not sure how to proceed here.

Before worrying about swab, we should at least try to get a warning
out for the short-read case we discussed earlier.  Here's a revision
of my earlier proposal, with a higher-quality warning.  Quite possibly
we can fold a swab warning into this code later, but one thing at
a time.

2011-03-04  Paul Eggert  <[email-masked]>

	dd: avoid or diagnose some problems with short reads
	* src/dd.c (iread): Diagnose short reads when they mess up counts.
	Derived from a suggestion by Pádraig Brady in:
	http://lists.gnu.org/archive/html/bug-coreutils/2011-02/msg00150.html

diff --git a/NEWS b/NEWS
index a367d8d..32cc7b0 100644
--- a/NEWS
+++ b/NEWS
@@ -39,6 +39,10 @@ GNU coreutils NEWS                                    -*- outline -*-
   reproduce them efficiently in the output file.  mv also benefits
   when it resorts to copying, e.g., between file systems.
 
+  dd bs=BYTES, with either count=BLOCKS or skip=BLOCKS and without
+  iflag=fullblock, now warns if a short block is read (not at end of
+  file).  This helps avoid confusion when counting or skipping bytes.
+
   join now supports -o 'auto' which will automatically infer the
   output format from the first line in each file, to ensure
   the same number of fields are output for each line.
diff --git a/src/dd.c b/src/dd.c
index daddc1e..39fa3ab 100644
--- a/src/dd.c
+++ b/src/dd.c
@@ -796,14 +796,43 @@ process_signals (void)
 static ssize_t
 iread (int fd, char *buf, size_t size)
 {
-  while (true)
+  ssize_t nread;
+
+  do
     {
-      ssize_t nread;
       process_signals ();
       nread = read (fd, buf, size);
-      if (! (nread < 0 && errno == EINTR))
-        return nread;
     }
+  while (nread < 0 && errno == EINTR);
+
+  if (0 < nread)
+    {
+      /* If bs=SIZE is given and iflag=fullblock is not, warn if a
+         short block was read (not at EOF), and either count=BLOCKS or
+         skip=BLOCKS is also given.  This helps avoid confusion when
+         counting or skipping bytes.  */
+      static bool warned;
+      static ssize_t prev_nread;
+
+      if (! warned && iread_fnc == iread
+          && 0 < prev_nread && prev_nread < size
+          && (skip_records
+              || (0 < max_records && max_records < (uintmax_t) -1)))
+        {
+          uintmax_t prev = prev_nread;
+          error (0, 0, ngettext (("warning: short read (%"PRIuMAX" byte); "
+                                  "suggest iflag=fullblock"),
+                                 ("warning: short read (%"PRIuMAX" bytes); "
+                                  "suggest iflag=fullblock"),
+                                 select_plural (prev)),
+                 prev);
+          warned = true;
+        }
+
+      prev_nread = nread;
+    }
+
+  return nread;
 }
 
 /* Wrapper around iread function to accumulate full blocks.  */