On Sat, Dec 22, 2012 at 20:43:16 -0800, Paul Eggert wrote:
> * src/incremen.c (read_incr_db_01, write_directory_file_entry):
> Allow negative time_t, dev_t, and ino_t.

On Sat, Dec 22, 2012 at 22:31:12 -0800, Paul Eggert wrote:
> * src/incremen.c (read_unsigned_num): Last arg is intmax_t *, not
> uintmax_t, for consistency with other readers.  All callers changed.
> Avoid signed integer overflow.
> (read_num):

The attached patch builds on those changes to incremen.c, proposing a
way to extend the error messages generated when reading and parsing the
fields in the snapshot file fails for some reason.

Unfortunately the name of the directory entry associated with an
invalid field isn't known at the time that field is being read, so we
can't give the user that information in the error message.  Instead, we
print what we do have available: the name of the snapshot file, the
exact type of error that was detected, a hint of the context (e.g.
characters read just before the error) when available, and finally the
name of the field being read and the the current byte position in the
input file.

Here's some sample messages generated with this patch in place (covering
most of the different conditions that might be detected):

================
./tar: snaptest0: Field too long while reading snapshot file:
   after reading "12345678901234567890" [field "tv_sec" at byte 36]
./tar: Error is not recoverable: exiting now

./tar: snaptest1: Unexpected EOF in snapshot file:
   after reading "98765432" [field "tv_nsec" at byte 40]
./tar: Error is not recoverable: exiting now

./tar: snaptest2: Unexpected field value in snapshot file:
   non-numeric character 0x78 found after reading "6336301" [field "tv_nsec" at 
byte 160]
./tar: Error is not recoverable: exiting now

./tar: snaptest3: Unexpected field value in snapshot file:
   strtoumax failed on value "99999999999999999999" [field "dev" at byte 142]: 
Numerical result out of range
./tar: Error is not recoverable: exiting now

./tar: snaptest4: Unexpected field value in snapshot file:
   strtoimax failed on value "-9999999999999999999" [field "dev" at byte 142]: 
Numerical result out of range
./tar: Error is not recoverable: exiting now

./tar: snaptest5: Unexpected field value in snapshot file:
   value "-2306" below min 0 [field "dev" at byte 166]
./tar: Error is not recoverable: exiting now

./tar: snaptest6: Unexpected field value in snapshot file:
   value "9223372036854775899" exceeds max 4294967295 [field "tv_sec" at byte 
160]
./tar: Error is not recoverable: exiting now

./tar: snaptest7: Missing record terminator:
   at end of dumpdir data [field "dircontents" at byte 206]
./tar: Error is not recoverable: exiting now

./tar: snaptest8: Unexpected EOF in snapshot file:
   at start of new field [field "?" at byte 173]
./tar: Error is not recoverable: exiting now

./tar: snaptest9: Unexpected EOF in snapshot file:
   at start of field [field "tv_nsec" at byte 46]
./tar: Error is not recoverable: exiting now
================

(I preserved the existing gettext strings, but didn't see any simple way
to factor out the common "[field \"%s\" at byte %s]" substring or
otherwise simplify the newly-created strings, since they are all part of
the the format masks.)

                                                        Nathan

----------------------------------------------------------------------------
Nathan Stratton Treadway  -  natha...@ontko.com  -  Mid-Atlantic region
Ray Ontko & Co.  -  Software consulting services  -   http://www.ontko.com/
 GPG Key: http://www.ontko.com/~nathanst/gpg_key.txt   ID: 1023D/ECFB6239
 Key fingerprint = 6AD8 485E 20B9 5C71 231C  0C32 15F3 ADCD ECFB 6239
diff --git a/src/incremen.c b/src/incremen.c
index 7130bc2..e4ceb98 100644
--- a/src/incremen.c
+++ b/src/incremen.c
@@ -1084,37 +1084,78 @@ read_obstack (FILE *fp, struct obstack *stk, size_t *pcount)
    already been read.
 
    Throw a fatal error if the string cannot be converted or if the
-   converted value is less than MIN_VAL.  */
+   converted value is less than MIN_VAL.  (FIELDNAME is used only as part
+   of these error messages.) */
 
 static void
-read_negative_num (FILE *fp, intmax_t min_val, intmax_t *pval)
+read_negative_num (FILE *fp, const char *fieldname, intmax_t min_val, intmax_t *pval)
 {
   int c;
   size_t i;
   char buf[INT_BUFSIZE_BOUND (intmax_t)];
+  char offbuf[INT_BUFSIZE_BOUND (off_t)];
   char *ep;
   buf[0] = '-';
 
   for (i = 1; ISDIGIT (c = getc (fp)); i++)
     {
-      if (i == sizeof buf - 1)
-	FATAL_ERROR ((0, 0, _("Field too long while reading snapshot file")));
+      if (i == sizeof buf - 1) {
+	buf[i] = 0;
+	FATAL_ERROR ((0, 0, 
+		      _("%s: %s:\n   after reading \"%s\" [field \"%s\" at byte %s]"), 
+		      quotearg_colon (listed_incremental_option),
+		      _("Field too long while reading snapshot file"),
+		      buf,
+		      fieldname, offtostr(ftello(fp),offbuf) ));
+	}
       buf[i] = c;
     }
+  buf[i] = 0;
 
   if (c < 0)
     {
       if (ferror (fp))
-	FATAL_ERROR ((0, errno, _("Read error in snapshot file")));
+	FATAL_ERROR ((0, errno, 
+		      _("%s: %s:\n   after reading \"%s\" [field \"%s\" at byte %s]"), 
+		      quotearg_colon (listed_incremental_option),
+		      _("Read error in snapshot file"),
+		      buf,
+		      fieldname, offtostr(ftello(fp),offbuf) ));
       else
-	FATAL_ERROR ((0, 0, _("Unexpected EOF in snapshot file")));
+	FATAL_ERROR ((0, 0, 
+		      _("%s: %s:\n   after reading \"%s\" [field \"%s\" at byte %s]"), 
+		      quotearg_colon (listed_incremental_option),
+		      _("Unexpected EOF in snapshot file"),
+		      buf,
+		      fieldname, offtostr(ftello(fp),offbuf) ));
     }
+  if (c)
+    FATAL_ERROR ((0, errno, 
+		  _("%s: %s:\n   non-numeric character 0x%x found after reading \"%s\" [field \"%s\" at byte %s]"), 
+		  quotearg_colon (listed_incremental_option),
+		  _("Unexpected field value in snapshot file"),
+		  c, buf,
+		  fieldname, offtostr(ftello(fp),offbuf) ));
 
-  buf[i] = 0;
   errno = 0;
   *pval = strtoimax (buf, &ep, 10);
-  if (c || errno || *pval < min_val)
-    FATAL_ERROR ((0, errno, _("Unexpected field value in snapshot file")));
+  if (errno)
+    FATAL_ERROR ((0, errno, 
+		  _("%s: %s:\n   strtoimax failed on value \"%s\" [field \"%s\" at byte %s]"), 
+		  quotearg_colon (listed_incremental_option),
+		  _("Unexpected field value in snapshot file"),
+		  buf,
+		  fieldname, offtostr(ftello(fp),offbuf) ));
+  if (*pval < min_val) {
+    char minbuf[INT_BUFSIZE_BOUND (intmax_t)];
+     
+    FATAL_ERROR ((0, errno, 
+		  _("%s: %s:\n   value \"%s\" below min %s [field \"%s\" at byte %s]"),
+		  quotearg_colon (listed_incremental_option),
+		  _("Unexpected field value in snapshot file"),
+		  buf, imaxtostr(min_val,minbuf),
+		  fieldname, offtostr(ftello(fp),offbuf) ));
+    }
 }
 
 /* Read from file FP a nul-terminated string and convert it to
@@ -1122,47 +1163,90 @@ read_negative_num (FILE *fp, intmax_t min_val, intmax_t *pval)
    value in PVAL.  Assume C has already been read.
 
    Throw a fatal error if the string cannot be converted or if the
-   converted value exceeds MAX_VAL.
+   converted value exceeds MAX_VAL.  (FIELDNAME is used only as part
+   of these error messages.)
 
    Return the last character read or EOF on end of file. */
 
 static int
-read_unsigned_num (int c, FILE *fp, uintmax_t max_val, intmax_t *pval)
+read_unsigned_num (int c, FILE *fp, const char *fieldname, uintmax_t max_val, intmax_t *pval)
 {
   size_t i;
   uintmax_t u;
   char buf[UINTMAX_STRSIZE_BOUND], *ep;
+  char offbuf[INT_BUFSIZE_BOUND (off_t)];
 
   for (i = 0; ISDIGIT (c); i++)
     {
-      if (i == sizeof buf - 1)
-	FATAL_ERROR ((0, 0, _("Field too long while reading snapshot file")));
+      if (i == sizeof buf - 1) {
+	buf[i] = 0;
+	FATAL_ERROR ((0, 0, 
+		      _("%s: %s:\n   after reading \"%s\" [field \"%s\" at byte %s]"), 
+		      quotearg_colon (listed_incremental_option),
+		      _("Field too long while reading snapshot file"),
+		      buf,
+		      fieldname, offtostr(ftello(fp),offbuf) ));
+	}
       buf[i] = c;
       c = getc (fp);
     }
+  buf[i] = 0;
 
   if (c < 0)
     {
       if (ferror (fp))
-	FATAL_ERROR ((0, errno, _("Read error in snapshot file")));
+	FATAL_ERROR ((0, errno, 
+		      _("%s: %s:\n   after reading \"%s\" [field \"%s\" at byte %s]"), 
+		      quotearg_colon (listed_incremental_option),
+		      _("Read error in snapshot file"),
+		      buf,
+		      fieldname, offtostr(ftello(fp),offbuf) ));
       else if (i == 0)
 	return c;
       else
-	FATAL_ERROR ((0, 0, _("Unexpected EOF in snapshot file")));
+	FATAL_ERROR ((0, 0, 
+		      _("%s: %s:\n   after reading \"%s\" [field \"%s\" at byte %s]"), 
+		      quotearg_colon (listed_incremental_option),
+		      _("Unexpected EOF in snapshot file"),
+		      buf,
+		      fieldname, offtostr(ftello(fp),offbuf) ));
     }
+  if (c)
+    FATAL_ERROR ((0, errno, 
+		  _("%s: %s:\n   non-numeric character 0x%x found after reading \"%s\" [field \"%s\" at byte %s]"), 
+		  quotearg_colon (listed_incremental_option),
+		  _("Unexpected field value in snapshot file"),
+		  c, buf,
+		  fieldname, offtostr(ftello(fp),offbuf) ));
 
-  buf[i] = 0;
   errno = 0;
   u = strtoumax (buf, &ep, 10);
-  if (c || errno || max_val < u)
-    FATAL_ERROR ((0, errno, _("Unexpected field value in snapshot file")));
+  if (errno)
+    FATAL_ERROR ((0, errno, 
+		  _("%s: %s:\n   strtoumax failed on value \"%s\" [field \"%s\" at byte %s]"), 
+		  quotearg_colon (listed_incremental_option),
+		  _("Unexpected field value in snapshot file"),
+		  buf,
+		  fieldname, offtostr(ftello(fp),offbuf) ));
+  if (max_val < u) {
+    char maxbuf[INT_BUFSIZE_BOUND (uintmax_t)];
+
+    FATAL_ERROR ((0, errno, 
+		  _("%s: %s:\n   value \"%s\" exceeds max %s [field \"%s\" at byte %s]"),
+		  quotearg_colon (listed_incremental_option),
+		  _("Unexpected field value in snapshot file"),
+		  buf, uinttostr(max_val,maxbuf),
+		  fieldname, offtostr(ftello(fp),offbuf) ));
+    }
   *pval = represent_uintmax (u);
   return c;
 }
 
 /* Read from file FP a nul-terminated string and convert it to
-   an integer in the range MIN_VAL..MAXVAL.  Return the resulting
-   value, converted to intmax_t, in PVAL.  MINVAL must be nonpositive.
+   an integer in the range MIN_VAL..MAX_VAL.  Return the resulting
+   value, converted to intmax_t, in PVAL.  MIN_VAL must be nonpositive
+   and MAX_VAL positive.  FIELDNAME simply a label for the snapshot-file
+   field currently being read, used only for generating error messages.
 
    Throw a fatal error if the string cannot be converted or if the
    converted value is out of range.
@@ -1170,15 +1254,15 @@ read_unsigned_num (int c, FILE *fp, uintmax_t max_val, intmax_t *pval)
    Return the last character read or EOF on end of file. */
 
 static int
-read_num (FILE *fp, intmax_t min_val, uintmax_t max_val, intmax_t *pval)
+read_num (FILE *fp, const char *fieldname, intmax_t min_val, uintmax_t max_val, intmax_t *pval)
 {
   int c = getc (fp);
   if (c == '-')
     {
-      read_negative_num (fp, min_val, pval);
+      read_negative_num (fp, fieldname, min_val, pval);
       return 0;
     }
-  return read_unsigned_num (c, fp, max_val, pval);
+  return read_unsigned_num (c, fp, fieldname, max_val, pval);
 }
 
 /* Read from FP two NUL-terminated strings representing a struct
@@ -1189,14 +1273,24 @@ read_num (FILE *fp, intmax_t min_val, uintmax_t max_val, intmax_t *pval)
 static void
 read_timespec (FILE *fp, struct timespec *pval)
 {
+  char offbuf[INT_BUFSIZE_BOUND (off_t)];
   intmax_t i;
-  int c = read_num (fp, TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t), &i);
+  int c = read_num (fp, "tv_sec", TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t), &i);
   pval->tv_sec = i;
 
-  if (c || read_num (fp, 0, BILLION - 1, &i))
-    FATAL_ERROR ((0, 0, "%s: %s",
+  if (c)
+    FATAL_ERROR ((0, 0,
+		  _("%s: %s:\n   at start of field [field \"%s\" at byte %s]"), 
 		  quotearg_colon (listed_incremental_option),
-		  _("Unexpected EOF in snapshot file")));
+		  _("Unexpected EOF in snapshot file"),
+		  "tv_sec", offtostr(ftello(fp),offbuf) ));
+
+  if (read_num (fp, "tv_nsec", 0, BILLION - 1, &i))
+    FATAL_ERROR ((0, 0,
+		  _("%s: %s:\n   at start of field [field \"%s\" at byte %s]"), 
+		  quotearg_colon (listed_incremental_option),
+		  _("Unexpected EOF in snapshot file"),
+		  "tv_nsec", offtostr(ftello(fp),offbuf) ));
   pval->tv_nsec = i;
 }
 
@@ -1205,6 +1299,7 @@ static void
 read_incr_db_2 (void)
 {
   struct obstack stk;
+  char offbuf[INT_BUFSIZE_BOUND (off_t)];
 
   obstack_init (&stk);
 
@@ -1221,19 +1316,19 @@ read_incr_db_2 (void)
       char *content;
       size_t s;
 
-      if (read_num (listed_incremental_stream, 0, 1, &i))
+      if (read_num (listed_incremental_stream, "nfs", 0, 1, &i))
 	return; /* Normal return */
 
       nfs = i;
 
       read_timespec (listed_incremental_stream, &mtime);
 
-      if (read_num (listed_incremental_stream,
+      if (read_num (listed_incremental_stream, "dev",
 		    TYPE_MINIMUM (dev_t), TYPE_MAXIMUM (dev_t), &i))
 	break;
       dev = i;
 
-      if (read_num (listed_incremental_stream,
+      if (read_num (listed_incremental_stream, "ino",
 		    TYPE_MINIMUM (ino_t), TYPE_MAXIMUM (ino_t), &i))
 	break;
       ino = i;
@@ -1246,17 +1341,22 @@ read_incr_db_2 (void)
       while (read_obstack (listed_incremental_stream, &stk, &s) == 0 && s > 1)
 	;
       if (getc (listed_incremental_stream) != 0)
-	FATAL_ERROR ((0, 0, "%s: %s",
+	FATAL_ERROR ((0, 0, 
+		      _("%s: %s:\n   at end of dumpdir data [field \"%s\" at byte %s]"),
 		      quotearg_colon (listed_incremental_option),
-		      _("Missing record terminator")));
+		      _("Missing record terminator"),
+		     "dircontents", offtostr(ftello(listed_incremental_stream),offbuf) ));
 
       content = obstack_finish (&stk);
       note_directory (name, mtime, dev, ino, nfs, false, content);
       obstack_free (&stk, content);
     }
-  FATAL_ERROR ((0, 0, "%s: %s",
+  FATAL_ERROR ((0, 0, 
+		_("%s: %s:\n   at start of new field [field \"%s\" at byte %s]"),
 		quotearg_colon (listed_incremental_option),
-		_("Unexpected EOF in snapshot file")));
+		_("Unexpected EOF in snapshot file"),
+		"?", offtostr(ftello(listed_incremental_stream),offbuf) ));
+
 }
 
 /* Read incremental snapshot file (directory file).

Reply via email to