bug-tar
[Top][All Lists]
Advanced

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

[Bug-tar] time stamps and out-of-range values in incremental dumps


From: Paul Eggert
Subject: [Bug-tar] time stamps and out-of-range values in incremental dumps
Date: Mon, 07 Aug 2006 14:33:03 -0700
User-agent: Gnus/5.1008 (Gnus v5.10.8) Emacs/21.4 (gnu/linux)

I went through incremen.c looking for problems with unchecked
integer overflow and the like, and installed the following patch.
In particular, this adds support for negative time_t values,
which are represented with a leading '-' in text strings.

The main part of this message lists the patch in "diff -bwu" format,
since my Emacs trimmed trailing spaces from many of the lines.
Attached is a full patch, including the trailing-space changes.

2006-08-07  Paul Eggert  <address@hidden>

        * src/incremen.c (read_incr_db_01): Check input strings more
        carefully.  Do not pass arbitrary char values to isspace, as
        this has undefined behavior.  Likewise for assigning arbitrary
        uintmax_t values to other types.
        (read_negative_num, read_unsigned_num, read_timespec):
        New functions, to check input values a bit more carefuly.
        (read_num): Use read_unsigned_num.  New arg MAX_VAL;
        all callers changed.
        (read_incr_db_2): Use these new functions.
        Use a consistent diagnostic for unexpected EOF.
        (read_directory_file): Do not assign arbitrary uintmax_t value
        to int.
        (read_timespec, write_directory_file_entry, write_directory_file):
        Handle negative time_t values correctly.  We don't bother to do
        this with pre-2 formats, since presumably the time stamps were
        output incorrectly.

--- src/incremen.c      24 Jun 2006 16:48:24 -0000      1.49
+++ src/incremen.c      7 Aug 2006 21:22:42 -0000
@@ -661,7 +661,8 @@ read_incr_db_01 (int version, const char
 {
   int n;
   uintmax_t u;
-  time_t t = u;
+  time_t sec;
+  long int nsec;
   char *buf = 0;
   size_t bufsize;
   char *ebuf;
@@ -683,40 +684,48 @@ read_incr_db_01 (int version, const char
       bufsize = strlen (buf) + 1;
     }
   
-  t = u = (errno = 0, strtoumax (buf, &ebuf, 10));
-  if (buf == ebuf || (u == 0 && errno == EINVAL))
-    ERROR ((0, 0, "%s:%ld: %s",
+  sec = TYPE_MINIMUM (time_t);
+  nsec = -1;
+  errno = 0;
+  u = strtoumax (buf, &ebuf, 10);
+  if (!errno && TYPE_MAXIMUM (time_t) < u)
+    errno = ERANGE;
+  if (errno || buf == ebuf)
+    ERROR ((0, errno, "%s:%ld: %s",
            quotearg_colon (listed_incremental_option),
            lineno,
            _("Invalid time stamp")));
-  else if (t != u)
-    ERROR ((0, 0, "%s:%ld: %s",
-           quotearg_colon (listed_incremental_option),
-           lineno,
-           _("Time stamp out of range")));
-  else if (version == 1)
+  else
     {
-      newer_mtime_option.tv_sec = t;
+      sec = u;
 
-      t = u = (errno = 0, strtoumax (buf, &ebuf, 10));
-      if (buf == ebuf || (u == 0 && errno == EINVAL))
-       ERROR ((0, 0, "%s:%ld: %s",
+      if (version == 1 && *ebuf)
+       {
+         char const *buf_ns = ebuf + 1;
+         errno = 0;
+         u = strtoumax (buf_ns, &ebuf, 10);
+         if (!errno && BILLION <= u)
+           errno = ERANGE;
+         if (errno || buf_ns == ebuf)
+           {
+             ERROR ((0, errno, "%s:%ld: %s",
                quotearg_colon (listed_incremental_option),
                lineno,
                _("Invalid time stamp")));
-      else if (t != u)
-       ERROR ((0, 0, "%s:%ld: %s",
-               quotearg_colon (listed_incremental_option),
-               lineno,
-               _("Time stamp out of range")));
-      newer_mtime_option.tv_nsec = t;
+             sec = TYPE_MINIMUM (time_t);
+           }
+         else
+           nsec = u;
     }
   else
     {
       /* pre-1 incremental format does not contain nanoseconds */
-      newer_mtime_option.tv_sec = t;
-      newer_mtime_option.tv_nsec = 0;
+         nsec = 0;
     }
+    }
+  newer_mtime_option.tv_sec = sec;
+  newer_mtime_option.tv_nsec = nsec;
+
 
   while (0 < (n = getline (&buf, &bufsize, listed_incremental_stream)))
     {
@@ -734,54 +743,68 @@ read_incr_db_01 (int version, const char
       if (version == 1)
        {
          errno = 0;
-         mtime.tv_sec = u = strtoumax (strp, &ebuf, 10);
-         if (!isspace (*ebuf))
-           ERROR ((0, 0, "%s:%ld: %s",
+         u = strtoumax (strp, &ebuf, 10);
+         if (!errno && TYPE_MAXIMUM (time_t) < u)
+           errno = ERANGE;
+         if (errno || strp == ebuf || *ebuf != ' ')
+           {
+             ERROR ((0, errno, "%s:%ld: %s",
                    quotearg_colon (listed_incremental_option), lineno,
                    _("Invalid modification time (seconds)")));
-         else if (mtime.tv_sec != u)
-           ERROR ((0, 0, "%s:%ld: %s",
-                   quotearg_colon (listed_incremental_option), lineno,
-                   _("Modification time (seconds) out of range")));
+             sec = (time_t) -1;
+           }
+         else
+           sec = u;
          strp = ebuf;
          
          errno = 0;
-         mtime.tv_nsec = u = strtoumax (strp, &ebuf, 10);
-         if (!isspace (*ebuf))
-           ERROR ((0, 0, "%s:%ld: %s",
+         u = strtoumax (strp, &ebuf, 10);
+         if (!errno && BILLION <= u)
+           errno = ERANGE;
+         if (errno || strp == ebuf || *ebuf != ' ')
+           {
+             ERROR ((0, errno, "%s:%ld: %s",
                    quotearg_colon (listed_incremental_option), lineno,
                    _("Invalid modification time (nanoseconds)")));
-         else if (mtime.tv_nsec != u)
-           ERROR ((0, 0, "%s:%ld: %s",
-                   quotearg_colon (listed_incremental_option), lineno,
-                   _("Modification time (nanoseconds) out of range")));
+             nsec = -1;
+           }
+         else
+           nsec = u;
+         mtime.tv_sec = sec;
+         mtime.tv_nsec = nsec;
          strp = ebuf;
        }
       else
        memset (&mtime, 0, sizeof mtime);
       
       errno = 0;
-      dev = u = strtoumax (strp, &ebuf, 10);
-      if (!isspace (*ebuf))
-       ERROR ((0, 0, "%s:%ld: %s",
+      u = strtoumax (strp, &ebuf, 10);
+      if (!errno && TYPE_MAXIMUM (dev_t) < u)
+       errno = ERANGE;
+      if (errno || strp == ebuf || *ebuf != ' ')
+       {
+         ERROR ((0, errno, "%s:%ld: %s",
                quotearg_colon (listed_incremental_option), lineno,
                _("Invalid device number")));
-      else if (dev != u)
-       ERROR ((0, 0, "%s:%ld: %s",
-               quotearg_colon (listed_incremental_option), lineno,
-               _("Device number out of range")));
+         dev = (dev_t) -1;
+       }
+      else
+       dev = u;
       strp = ebuf;
 
       errno = 0;
-      ino = u = strtoumax (strp, &ebuf, 10);
-      if (!isspace (*ebuf))
-       ERROR ((0, 0, "%s:%ld: %s",
+      u = strtoumax (strp, &ebuf, 10);
+      if (!errno && TYPE_MAXIMUM (ino_t) < u)
+       errno = ERANGE;
+      if (errno || strp == ebuf || *ebuf != ' ')
+       {
+         ERROR ((0, errno, "%s:%ld: %s",
                quotearg_colon (listed_incremental_option), lineno,
                _("Invalid inode number")));
-      else if (ino != u)
-       ERROR ((0, 0, "%s:%ld: %s",
-               quotearg_colon (listed_incremental_option), lineno,
-               _("Inode number out of range")));
+         ino = (ino_t) -1;
+       }
+      else
+       ino = u;
       strp = ebuf;
       
       strp++;
@@ -810,32 +833,129 @@ read_obstack (FILE *fp, struct obstack *
 }
 
 /* Read from file FP a nul-terminated string and convert it to
-   uintmax_t. Return the resulting value in PVAL.
+   intmax_t.  Return the resulting value in PVAL.  Assume '-' has
+   already been read.
+
+   Throw a fatal error if the string cannot be converted or if the
+   converted value is less than MIN_VAL.  */
+
+static void
+read_negative_num (FILE *fp, intmax_t min_val, intmax_t *pval)
+{
+  int c;
+  size_t i;
+  char buf[INT_BUFSIZE_BOUND (intmax_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")));
+      buf[i] = c;
+    }
+
+  if (c < 0)
+    {
+      if (ferror (fp))
+       FATAL_ERROR ((0, errno, _("Read error in snapshot file")));
+      else
+       FATAL_ERROR ((0, 0, _("Unexpected EOF in snapshot file")));
+    }
 
-   Throw fatal error if the string cannot be converted.
+  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")));
+}
+
+/* Read from file FP a nul-terminated string and convert it to
+   uintmax_t.  Return the resulting 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.
    
    Return the last character read or EOF on end of file. */
 
 static int
-read_num (FILE *fp, uintmax_t *pval)
+read_unsigned_num (int c, FILE *fp, uintmax_t max_val, uintmax_t *pval)
 {
-  int c;
   size_t i;
   char buf[UINTMAX_STRSIZE_BOUND], *ep;
 
-  for (i = 0, c = getc (fp); c != EOF && c != 0; c = getc (fp), i++)
+  for (i = 0; ISDIGIT (c); i++)
     {
       if (i == sizeof buf - 1)
        FATAL_ERROR ((0, 0, _("Field too long while reading snapshot file")));
       buf[i] = c;
+      c = getc (fp);
+    }
+
+  if (c < 0)
+    {
+      if (ferror (fp))
+       FATAL_ERROR ((0, errno, _("Read error in snapshot file")));
+      else if (i == 0)
+       return c;
+      else
+       FATAL_ERROR ((0, 0, _("Unexpected EOF in snapshot file")));
     }
+
   buf[i] = 0;
+  errno = 0;
   *pval = strtoumax (buf, &ep, 10);
-  if (*ep)
-    FATAL_ERROR ((0, 0, _("Unexpected field value in snapshot file")));
+  if (c || errno || max_val < *pval)
+    FATAL_ERROR ((0, errno, _("Unexpected field value in snapshot file")));
   return c;
 }  
 
+/* Read from file FP a nul-terminated string and convert it to
+   uintmax_t.  Return the resulting value in PVAL.
+
+   Throw a fatal error if the string cannot be converted or if the
+   converted value exceeds MAX_VAL.
+
+   Return the last character read or EOF on end of file. */
+
+static int
+read_num (FILE *fp, uintmax_t max_val, uintmax_t *pval)
+{
+  return read_unsigned_num (getc (fp), fp, max_val, pval);
+}
+
+/* Read from FP two NUL-terminated strings representing a struct
+   timespec.  Return the resulting value in PVAL.
+
+   Throw a fatal error if the string cannot be converted.  */
+
+static void
+read_timespec (FILE *fp, struct timespec *pval)
+{
+  int c = getc (fp);
+  intmax_t i;
+  uintmax_t u;
+
+  if (c == '-')
+    {
+      read_negative_num (fp, TYPE_MINIMUM (time_t), &i);
+      c = 0;
+      pval->tv_sec = i;
+    }
+  else
+    {
+      c = read_unsigned_num (c, fp, TYPE_MAXIMUM (time_t), &u);
+      pval->tv_sec = u;
+    }
+
+  if (c || read_num (fp, BILLION - 1, &u))
+    FATAL_ERROR ((0, 0, "%s: %s",
+                 quotearg_colon (listed_incremental_option),
+                 _("Unexpected EOF in snapshot file")));
+  pval->tv_nsec = u;
+}
+
 /* Read incremental snapshot format 2 */
 static void
 read_incr_db_2 ()
@@ -845,25 +965,7 @@ read_incr_db_2 ()
   
   obstack_init (&stk);
 
-  if (read_num (listed_incremental_stream, &u))
-    FATAL_ERROR ((0, 0, "%s: %s",
-                 quotearg_colon (listed_incremental_option), 
-                 _("Error reading time stamp")));
-  newer_mtime_option.tv_sec = u;
-  if (newer_mtime_option.tv_sec != u)
-    FATAL_ERROR ((0, 0, "%s: %s",
-                 quotearg_colon (listed_incremental_option),
-                 _("Time stamp out of range")));
-
-  if (read_num (listed_incremental_stream, &u))
-    FATAL_ERROR ((0, 0, "%s: %s",
-                 quotearg_colon (listed_incremental_option), 
-                 _("Error reading time stamp")));
-  newer_mtime_option.tv_nsec = u;
-  if (newer_mtime_option.tv_nsec != u)
-    FATAL_ERROR ((0, 0, "%s: %s",
-                 quotearg_colon (listed_incremental_option),
-                 _("Time stamp out of range")));
+  read_timespec (listed_incremental_stream, &newer_mtime_option);
 
   for (;;)
     {
@@ -875,42 +977,20 @@ read_incr_db_2 ()
       char *content;
       size_t s;
       
-      if (read_num (listed_incremental_stream, &u))
+      if (read_num (listed_incremental_stream, 1, &u))
        return; /* Normal return */
 
       nfs = u;
       
-      if (read_num (listed_incremental_stream, &u))
-       break;
-      mtime.tv_sec = u;
-      if (mtime.tv_sec != u)
-       FATAL_ERROR ((0, 0, "%s: %s",
-                     quotearg_colon (listed_incremental_option), 
-                     _("Modification time (seconds) out of range")));
+      read_timespec (listed_incremental_stream, &mtime);
       
-      if (read_num (listed_incremental_stream, &u))
-       break;
-      mtime.tv_nsec = u;
-      if (mtime.tv_nsec != u)
-       FATAL_ERROR ((0, 0, "%s: %s",
-                     quotearg_colon (listed_incremental_option), 
-                     _("Modification time (nanoseconds) out of range")));
-
-      if (read_num (listed_incremental_stream, &u))
+      if (read_num (listed_incremental_stream, TYPE_MAXIMUM (dev_t), &u))
        break;
       dev = u;
-      if (dev != u)
-       FATAL_ERROR ((0, 0, "%s: %s",
-                     quotearg_colon (listed_incremental_option), 
-                     _("Device number out of range")));
 
-      if (read_num (listed_incremental_stream, &u))
+      if (read_num (listed_incremental_stream, TYPE_MAXIMUM (ino_t), &u))
        break;
       ino = u;
-      if (ino != u)
-       FATAL_ERROR ((0, 0, "%s: %s",
-                     quotearg_colon (listed_incremental_option), 
-                     _("Inode number out of range")));
 
       if (read_obstack (listed_incremental_stream, &stk, &s))
        break;
@@ -930,7 +1010,7 @@ read_incr_db_2 ()
     }
   FATAL_ERROR ((0, 0, "%s: %s",
                quotearg_colon (listed_incremental_option), 
-               _("Unexpected EOF")));
+               _("Unexpected EOF in snapshot file")));
 }
 
 /* Read incremental snapshot file (directory file).
@@ -967,7 +1047,7 @@ read_directory_file (void)
   if (0 < getline (&buf, &bufsize, listed_incremental_stream))
     {
       char *ebuf;
-      int incremental_version;
+      uintmax_t incremental_version;
 
       if (strncmp (buf, PACKAGE_NAME, sizeof PACKAGE_NAME - 1) == 0)
        {
@@ -978,7 +1058,7 @@ read_directory_file (void)
            if (!*ebuf)
              ERROR((1, 0, _("Bad incremental file format")));
 
-         incremental_version = (errno = 0, strtoumax (ebuf+1, &ebuf, 10));
+         incremental_version = strtoumax (ebuf + 1, NULL, 10);
        }
       else
        incremental_version = 0;
@@ -995,7 +1075,7 @@ read_directory_file (void)
          break;
          
        default:
-         ERROR ((1, 0, _("Unsupported incremental format version: %d"),
+         ERROR ((1, 0, _("Unsupported incremental format version: %"PRIuMAX),
                  incremental_version));
        }
       
@@ -1022,7 +1102,9 @@ write_directory_file_entry (void *entry,
 
       s = DIR_IS_NFS (directory) ? "1" : "0";
       fwrite (s, 2, 1, fp);
-      s = umaxtostr (directory->mtime.tv_sec, buf);
+      s = (TYPE_SIGNED (time_t)
+          ? imaxtostr (directory->mtime.tv_sec, buf)
+          : umaxtostr (directory->mtime.tv_sec, buf));
       fwrite (s, strlen (s) + 1, 1, fp);
       s = umaxtostr (directory->mtime.tv_nsec, buf);
       fwrite (s, strlen (s) + 1, 1, fp);
@@ -1065,7 +1147,9 @@ write_directory_file (void)
   fprintf (fp, "%s-%s-%d\n", PACKAGE_NAME, PACKAGE_VERSION,
           TAR_INCREMENTAL_VERSION);
 
-  s = umaxtostr (start_time.tv_sec, buf);
+  s = (TYPE_SIGNED (time_t)
+       ? imaxtostr (start_time.tv_sec, buf)
+       : umaxtostr (start_time.tv_sec, buf));
   fwrite (s, strlen (s) + 1, 1, fp);
   s = umaxtostr (start_time.tv_nsec, buf);
   fwrite (s, strlen (s) + 1, 1, fp);
Index: src/incremen.c
===================================================================
RCS file: /cvsroot/tar/tar/src/incremen.c,v
retrieving revision 1.49
diff -p -u -r1.49 incremen.c
--- src/incremen.c      24 Jun 2006 16:48:24 -0000      1.49
+++ src/incremen.c      7 Aug 2006 21:22:35 -0000
@@ -46,7 +46,7 @@ enum children
 #define DIR_IS_FOUND(d) ((d)->flags & DIRF_FOUND)
 #define DIR_IS_NEW(d) ((d)->flags & DIRF_NEW)
 #define DIR_IS_RENAMED(d) ((d)->flags & DIRF_RENAMED)
-                      
+
 #define DIR_SET_FLAG(d,f) (d)->flags |= (f)
 #define DIR_CLEAR_FLAG(d,f) (d)->flags &= ~(f)
 
@@ -127,7 +127,7 @@ make_directory (const char *name)
     directory->name[namelen-1] = 0;
   return directory;
 }
-  
+
 /* Create and link a new directory entry for directory NAME, having a
    device number DEV and an inode number INO, with NFS indicating
    whether it is an NFS device and FOUND indicating whether we have
@@ -154,7 +154,7 @@ note_directory (char const *name, struct
     }
   else
     directory->contents = NULL;
-  
+
   if (! ((directory_table
          || (directory_table = hash_initialize (0, 0,
                                                 hash_directory_name,
@@ -239,7 +239,7 @@ procdir (char *name_buffer, struct stat 
     {
       if (DIR_IS_INITED (directory))
        return directory;
-      
+
       /* With NFS, the same file can have two different devices
         if an NFS directory is mounted in multiple locations,
         which is relatively common when automounting.
@@ -278,7 +278,7 @@ procdir (char *name_buffer, struct stat 
        }
       else
        directory->children = CHANGED_CHILDREN;
-      
+
       DIR_SET_FLAG (directory, DIRF_FOUND);
     }
   else
@@ -310,7 +310,7 @@ procdir (char *name_buffer, struct stat 
          if (verbose)
            WARN ((0, 0, _("%s: Directory is new"),
                   quotearg_colon (name_buffer)));
-         directory->children = 
+         directory->children =
            (listed_incremental_option
             || (OLDER_STAT_TIME (*stat_data, m)
                 || (after_date_option
@@ -326,11 +326,11 @@ procdir (char *name_buffer, struct stat 
       /* ... except if it was explicitely given in the command line */
       && !is_individual_file (name_buffer))
     directory->children = NO_CHILDREN;
-  else if (children == ALL_CHILDREN) 
+  else if (children == ALL_CHILDREN)
     directory->children = ALL_CHILDREN;
 
   DIR_SET_FLAG (directory, DIRF_INIT);
-  
+
   return directory;
 }
 
@@ -343,7 +343,7 @@ dumpdir_locate (const char *dump, const 
     while (*dump)
       {
        /* Ignore 'R' (rename) and 'X' (tempname) entries, since they break
-          alphabetical ordering. 
+          alphabetical ordering.
           They normally do not occur in dumpdirs from the snapshot files,
           but this function is also used by purge_directory, which operates
           on a dumpdir from the archive, hence the need for this test. */
@@ -387,7 +387,7 @@ compare_dirnames (const void *first, con
    DIR must be returned by a previous call to savedir().
 
    File names in DIRECTORY->contents must be sorted
-   alphabetically. 
+   alphabetically.
 
    DIRECTORY->contents is replaced with the created template. Each entry is
    prefixed with ' ' if it was present in DUMP and with 'Y' otherwise. */
@@ -461,7 +461,7 @@ scan_directory (char *dir_name, dev_t de
   size_t name_length;          /* used length in name_buffer */
   struct stat stat_data;
   struct directory *directory;
-  
+
   if (! dirp)
     savedir_error (dir_name);
 
@@ -475,7 +475,7 @@ scan_directory (char *dir_name, dev_t de
   if (deref_stat (dereference_option, name_buffer, &stat_data))
     {
       stat_diag (name_buffer);
-      /* FIXME: used to be 
+      /* FIXME: used to be
            children = CHANGED_CHILDREN;
         but changed to: */
       free (name_buffer);
@@ -484,7 +484,7 @@ scan_directory (char *dir_name, dev_t de
     }
 
   directory = procdir (name_buffer, &stat_data, device, NO_CHILDREN, false);
-    
+
   if (dirp && directory->children != NO_CHILDREN)
     {
       char  *entry;    /* directory entry being scanned */
@@ -504,7 +504,7 @@ scan_directory (char *dir_name, dev_t de
              name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
            }
          strcpy (name_buffer + name_length, entry + 1);
-         
+
          if (excluded_name (name_buffer))
            *entry = 'N';
          else
@@ -515,7 +515,7 @@ scan_directory (char *dir_name, dev_t de
                  *entry = 'N';
                  continue;
                }
-             
+
              if (S_ISDIR (stat_data.st_mode))
                {
                  procdir (name_buffer, &stat_data, device,
@@ -523,15 +523,15 @@ scan_directory (char *dir_name, dev_t de
                           verbose_option);
                  *entry = 'D';
                }
-             
+
              else if (one_file_system_option && device != stat_data.st_dev)
                *entry = 'N';
 
              else if (*entry == 'Y')
                /* New entry, skip further checks */;
-             
+
              /* FIXME: if (S_ISHIDDEN (stat_data.st_mode))?? */
-             
+
              else if (OLDER_STAT_TIME (stat_data, m)
                       && (!after_date_option
                           || OLDER_STAT_TIME (stat_data, c)))
@@ -541,7 +541,7 @@ scan_directory (char *dir_name, dev_t de
            }
        }
     }
-  
+
   free (name_buffer);
   if (dirp)
     free (dirp);
@@ -570,7 +570,7 @@ rename_handler (void *data, void *proc_d
 {
   struct directory *dir = data;
   struct obstack *stk = proc_data;
-  
+
   if (DIR_IS_RENAMED (dir))
     {
       struct directory *prev, *p;
@@ -620,7 +620,7 @@ append_incremental_renames (const char *
 
   if (directory_table == NULL)
     return dump;
-  
+
   obstack_init (&stk);
   if (dump)
     {
@@ -629,7 +629,7 @@ append_incremental_renames (const char *
     }
   else
     size = 0;
-  
+
   hash_do_for_each (directory_table, rename_handler, &stk);
   if (obstack_object_size (&stk) != size)
     {
@@ -661,7 +661,8 @@ read_incr_db_01 (int version, const char
 {
   int n;
   uintmax_t u;
-  time_t t = u;
+  time_t sec;
+  long int nsec;
   char *buf = 0;
   size_t bufsize;
   char *ebuf;
@@ -682,41 +683,49 @@ read_incr_db_01 (int version, const char
       buf = strdup (initbuf);
       bufsize = strlen (buf) + 1;
     }
-  
-  t = u = (errno = 0, strtoumax (buf, &ebuf, 10));
-  if (buf == ebuf || (u == 0 && errno == EINVAL))
-    ERROR ((0, 0, "%s:%ld: %s",
+
+  sec = TYPE_MINIMUM (time_t);
+  nsec = -1;
+  errno = 0;
+  u = strtoumax (buf, &ebuf, 10);
+  if (!errno && TYPE_MAXIMUM (time_t) < u)
+    errno = ERANGE;
+  if (errno || buf == ebuf)
+    ERROR ((0, errno, "%s:%ld: %s",
            quotearg_colon (listed_incremental_option),
            lineno,
            _("Invalid time stamp")));
-  else if (t != u)
-    ERROR ((0, 0, "%s:%ld: %s",
-           quotearg_colon (listed_incremental_option),
-           lineno,
-           _("Time stamp out of range")));
-  else if (version == 1)
-    {
-      newer_mtime_option.tv_sec = t;
-
-      t = u = (errno = 0, strtoumax (buf, &ebuf, 10));
-      if (buf == ebuf || (u == 0 && errno == EINVAL))
-       ERROR ((0, 0, "%s:%ld: %s",
-               quotearg_colon (listed_incremental_option),
-               lineno,
-               _("Invalid time stamp")));
-      else if (t != u)
-       ERROR ((0, 0, "%s:%ld: %s",
-               quotearg_colon (listed_incremental_option),
-               lineno,
-               _("Time stamp out of range")));
-      newer_mtime_option.tv_nsec = t;
-    }
   else
     {
-      /* pre-1 incremental format does not contain nanoseconds */
-      newer_mtime_option.tv_sec = t;
-      newer_mtime_option.tv_nsec = 0;
+      sec = u;
+
+      if (version == 1 && *ebuf)
+       {
+         char const *buf_ns = ebuf + 1;
+         errno = 0;
+         u = strtoumax (buf_ns, &ebuf, 10);
+         if (!errno && BILLION <= u)
+           errno = ERANGE;
+         if (errno || buf_ns == ebuf)
+           {
+             ERROR ((0, errno, "%s:%ld: %s",
+                     quotearg_colon (listed_incremental_option),
+                     lineno,
+                     _("Invalid time stamp")));
+             sec = TYPE_MINIMUM (time_t);
+           }
+         else
+           nsec = u;
+       }
+      else
+       {
+         /* pre-1 incremental format does not contain nanoseconds */
+         nsec = 0;
+       }
     }
+  newer_mtime_option.tv_sec = sec;
+  newer_mtime_option.tv_nsec = nsec;
+
 
   while (0 < (n = getline (&buf, &bufsize, listed_incremental_stream)))
     {
@@ -725,65 +734,79 @@ read_incr_db_01 (int version, const char
       bool nfs = buf[0] == '+';
       char *strp = buf + nfs;
       struct timespec mtime;
-      
+
       lineno++;
-      
+
       if (buf[n - 1] == '\n')
        buf[n - 1] = '\0';
-      
+
       if (version == 1)
        {
          errno = 0;
-         mtime.tv_sec = u = strtoumax (strp, &ebuf, 10);
-         if (!isspace (*ebuf))
-           ERROR ((0, 0, "%s:%ld: %s",
-                   quotearg_colon (listed_incremental_option), lineno,
-                   _("Invalid modification time (seconds)")));
-         else if (mtime.tv_sec != u)
-           ERROR ((0, 0, "%s:%ld: %s",
-                   quotearg_colon (listed_incremental_option), lineno,
-                   _("Modification time (seconds) out of range")));
+         u = strtoumax (strp, &ebuf, 10);
+         if (!errno && TYPE_MAXIMUM (time_t) < u)
+           errno = ERANGE;
+         if (errno || strp == ebuf || *ebuf != ' ')
+           {
+             ERROR ((0, errno, "%s:%ld: %s",
+                     quotearg_colon (listed_incremental_option), lineno,
+                     _("Invalid modification time (seconds)")));
+             sec = (time_t) -1;
+           }
+         else
+           sec = u;
          strp = ebuf;
-         
+
          errno = 0;
-         mtime.tv_nsec = u = strtoumax (strp, &ebuf, 10);
-         if (!isspace (*ebuf))
-           ERROR ((0, 0, "%s:%ld: %s",
-                   quotearg_colon (listed_incremental_option), lineno,
-                   _("Invalid modification time (nanoseconds)")));
-         else if (mtime.tv_nsec != u)
-           ERROR ((0, 0, "%s:%ld: %s",
-                   quotearg_colon (listed_incremental_option), lineno,
-                   _("Modification time (nanoseconds) out of range")));
+         u = strtoumax (strp, &ebuf, 10);
+         if (!errno && BILLION <= u)
+           errno = ERANGE;
+         if (errno || strp == ebuf || *ebuf != ' ')
+           {
+             ERROR ((0, errno, "%s:%ld: %s",
+                     quotearg_colon (listed_incremental_option), lineno,
+                     _("Invalid modification time (nanoseconds)")));
+             nsec = -1;
+           }
+         else
+           nsec = u;
+         mtime.tv_sec = sec;
+         mtime.tv_nsec = nsec;
          strp = ebuf;
        }
       else
        memset (&mtime, 0, sizeof mtime);
-      
+
       errno = 0;
-      dev = u = strtoumax (strp, &ebuf, 10);
-      if (!isspace (*ebuf))
-       ERROR ((0, 0, "%s:%ld: %s",
-               quotearg_colon (listed_incremental_option), lineno,
-               _("Invalid device number")));
-      else if (dev != u)
-       ERROR ((0, 0, "%s:%ld: %s",
-               quotearg_colon (listed_incremental_option), lineno,
-               _("Device number out of range")));
+      u = strtoumax (strp, &ebuf, 10);
+      if (!errno && TYPE_MAXIMUM (dev_t) < u)
+       errno = ERANGE;
+      if (errno || strp == ebuf || *ebuf != ' ')
+       {
+         ERROR ((0, errno, "%s:%ld: %s",
+                 quotearg_colon (listed_incremental_option), lineno,
+                 _("Invalid device number")));
+         dev = (dev_t) -1;
+       }
+      else
+       dev = u;
       strp = ebuf;
 
       errno = 0;
-      ino = u = strtoumax (strp, &ebuf, 10);
-      if (!isspace (*ebuf))
-       ERROR ((0, 0, "%s:%ld: %s",
-               quotearg_colon (listed_incremental_option), lineno,
-               _("Invalid inode number")));
-      else if (ino != u)
-       ERROR ((0, 0, "%s:%ld: %s",
-               quotearg_colon (listed_incremental_option), lineno,
-               _("Inode number out of range")));
+      u = strtoumax (strp, &ebuf, 10);
+      if (!errno && TYPE_MAXIMUM (ino_t) < u)
+       errno = ERANGE;
+      if (errno || strp == ebuf || *ebuf != ' ')
+       {
+         ERROR ((0, errno, "%s:%ld: %s",
+                 quotearg_colon (listed_incremental_option), lineno,
+                 _("Invalid inode number")));
+         ino = (ino_t) -1;
+       }
+      else
+       ino = u;
       strp = ebuf;
-      
+
       strp++;
       unquote_string (strp);
       note_directory (strp, mtime, dev, ino, nfs, false, NULL);
@@ -810,31 +833,128 @@ read_obstack (FILE *fp, struct obstack *
 }
 
 /* Read from file FP a nul-terminated string and convert it to
-   uintmax_t. Return the resulting value in PVAL.
+   intmax_t.  Return the resulting value in PVAL.  Assume '-' has
+   already been read.
+
+   Throw a fatal error if the string cannot be converted or if the
+   converted value is less than MIN_VAL.  */
+
+static void
+read_negative_num (FILE *fp, intmax_t min_val, intmax_t *pval)
+{
+  int c;
+  size_t i;
+  char buf[INT_BUFSIZE_BOUND (intmax_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")));
+      buf[i] = c;
+    }
+
+  if (c < 0)
+    {
+      if (ferror (fp))
+       FATAL_ERROR ((0, errno, _("Read error in snapshot file")));
+      else
+       FATAL_ERROR ((0, 0, _("Unexpected EOF in snapshot file")));
+    }
+
+  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")));
+}
+
+/* Read from file FP a nul-terminated string and convert it to
+   uintmax_t.  Return the resulting 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.
 
-   Throw fatal error if the string cannot be converted.
-   
    Return the last character read or EOF on end of file. */
 
 static int
-read_num (FILE *fp, uintmax_t *pval)
+read_unsigned_num (int c, FILE *fp, uintmax_t max_val, uintmax_t *pval)
 {
-  int c;
   size_t i;
   char buf[UINTMAX_STRSIZE_BOUND], *ep;
 
-  for (i = 0, c = getc (fp); c != EOF && c != 0; c = getc (fp), i++)
+  for (i = 0; ISDIGIT (c); i++)
     {
       if (i == sizeof buf - 1)
        FATAL_ERROR ((0, 0, _("Field too long while reading snapshot file")));
       buf[i] = c;
+      c = getc (fp);
+    }
+
+  if (c < 0)
+    {
+      if (ferror (fp))
+       FATAL_ERROR ((0, errno, _("Read error in snapshot file")));
+      else if (i == 0)
+       return c;
+      else
+       FATAL_ERROR ((0, 0, _("Unexpected EOF in snapshot file")));
     }
+
   buf[i] = 0;
+  errno = 0;
   *pval = strtoumax (buf, &ep, 10);
-  if (*ep)
-    FATAL_ERROR ((0, 0, _("Unexpected field value in snapshot file")));
+  if (c || errno || max_val < *pval)
+    FATAL_ERROR ((0, errno, _("Unexpected field value in snapshot file")));
   return c;
-}  
+}
+
+/* Read from file FP a nul-terminated string and convert it to
+   uintmax_t.  Return the resulting value in PVAL.
+
+   Throw a fatal error if the string cannot be converted or if the
+   converted value exceeds MAX_VAL.
+
+   Return the last character read or EOF on end of file. */
+
+static int
+read_num (FILE *fp, uintmax_t max_val, uintmax_t *pval)
+{
+  return read_unsigned_num (getc (fp), fp, max_val, pval);
+}
+
+/* Read from FP two NUL-terminated strings representing a struct
+   timespec.  Return the resulting value in PVAL.
+
+   Throw a fatal error if the string cannot be converted.  */
+
+static void
+read_timespec (FILE *fp, struct timespec *pval)
+{
+  int c = getc (fp);
+  intmax_t i;
+  uintmax_t u;
+
+  if (c == '-')
+    {
+      read_negative_num (fp, TYPE_MINIMUM (time_t), &i);
+      c = 0;
+      pval->tv_sec = i;
+    }
+  else
+    {
+      c = read_unsigned_num (c, fp, TYPE_MAXIMUM (time_t), &u);
+      pval->tv_sec = u;
+    }
+
+  if (c || read_num (fp, BILLION - 1, &u))
+    FATAL_ERROR ((0, 0, "%s: %s",
+                 quotearg_colon (listed_incremental_option),
+                 _("Unexpected EOF in snapshot file")));
+  pval->tv_nsec = u;
+}
 
 /* Read incremental snapshot format 2 */
 static void
@@ -842,28 +962,10 @@ read_incr_db_2 ()
 {
   uintmax_t u;
   struct obstack stk;
-  
-  obstack_init (&stk);
 
-  if (read_num (listed_incremental_stream, &u))
-    FATAL_ERROR ((0, 0, "%s: %s",
-                 quotearg_colon (listed_incremental_option), 
-                 _("Error reading time stamp")));
-  newer_mtime_option.tv_sec = u;
-  if (newer_mtime_option.tv_sec != u)
-    FATAL_ERROR ((0, 0, "%s: %s",
-                 quotearg_colon (listed_incremental_option),
-                 _("Time stamp out of range")));
+  obstack_init (&stk);
 
-  if (read_num (listed_incremental_stream, &u))
-    FATAL_ERROR ((0, 0, "%s: %s",
-                 quotearg_colon (listed_incremental_option), 
-                 _("Error reading time stamp")));
-  newer_mtime_option.tv_nsec = u;
-  if (newer_mtime_option.tv_nsec != u)
-    FATAL_ERROR ((0, 0, "%s: %s",
-                 quotearg_colon (listed_incremental_option),
-                 _("Time stamp out of range")));
+  read_timespec (listed_incremental_stream, &newer_mtime_option);
 
   for (;;)
     {
@@ -874,43 +976,21 @@ read_incr_db_2 ()
       char *name;
       char *content;
       size_t s;
-      
-      if (read_num (listed_incremental_stream, &u))
+
+      if (read_num (listed_incremental_stream, 1, &u))
        return; /* Normal return */
 
       nfs = u;
-      
-      if (read_num (listed_incremental_stream, &u))
-       break;
-      mtime.tv_sec = u;
-      if (mtime.tv_sec != u)
-       FATAL_ERROR ((0, 0, "%s: %s",
-                     quotearg_colon (listed_incremental_option), 
-                     _("Modification time (seconds) out of range")));
-      
-      if (read_num (listed_incremental_stream, &u))
-       break;
-      mtime.tv_nsec = u;
-      if (mtime.tv_nsec != u)
-       FATAL_ERROR ((0, 0, "%s: %s",
-                     quotearg_colon (listed_incremental_option), 
-                     _("Modification time (nanoseconds) out of range")));
 
-      if (read_num (listed_incremental_stream, &u))
+      read_timespec (listed_incremental_stream, &mtime);
+
+      if (read_num (listed_incremental_stream, TYPE_MAXIMUM (dev_t), &u))
        break;
       dev = u;
-      if (dev != u)
-       FATAL_ERROR ((0, 0, "%s: %s",
-                     quotearg_colon (listed_incremental_option), 
-                     _("Device number out of range")));
 
-      if (read_num (listed_incremental_stream, &u))
+      if (read_num (listed_incremental_stream, TYPE_MAXIMUM (ino_t), &u))
        break;
       ino = u;
-      if (ino != u)
-       FATAL_ERROR ((0, 0, "%s: %s",
-                     quotearg_colon (listed_incremental_option), 
-                     _("Inode number out of range")));
 
       if (read_obstack (listed_incremental_stream, &stk, &s))
        break;
@@ -921,16 +1001,16 @@ read_incr_db_2 ()
        ;
       if (getc (listed_incremental_stream) != 0)
        FATAL_ERROR ((0, 0, "%s: %s",
-                     quotearg_colon (listed_incremental_option), 
+                     quotearg_colon (listed_incremental_option),
                      _("Missing record terminator")));
-      
+
       content = obstack_finish (&stk);
       note_directory (name, mtime, dev, ino, nfs, false, content);
       obstack_free (&stk, content);
     }
   FATAL_ERROR ((0, 0, "%s: %s",
-               quotearg_colon (listed_incremental_option), 
-               _("Unexpected EOF")));
+               quotearg_colon (listed_incremental_option),
+               _("Unexpected EOF in snapshot file")));
 }
 
 /* Read incremental snapshot file (directory file).
@@ -967,7 +1047,7 @@ read_directory_file (void)
   if (0 < getline (&buf, &bufsize, listed_incremental_stream))
     {
       char *ebuf;
-      int incremental_version;
+      uintmax_t incremental_version;
 
       if (strncmp (buf, PACKAGE_NAME, sizeof PACKAGE_NAME - 1) == 0)
        {
@@ -978,7 +1058,7 @@ read_directory_file (void)
            if (!*ebuf)
              ERROR((1, 0, _("Bad incremental file format")));
 
-         incremental_version = (errno = 0, strtoumax (ebuf+1, &ebuf, 10));
+         incremental_version = strtoumax (ebuf + 1, NULL, 10);
        }
       else
        incremental_version = 0;
@@ -993,12 +1073,12 @@ read_directory_file (void)
        case TAR_INCREMENTAL_VERSION:
          read_incr_db_2 ();
          break;
-         
+
        default:
-         ERROR ((1, 0, _("Unsupported incremental format version: %d"),
+         ERROR ((1, 0, _("Unsupported incremental format version: %"PRIuMAX),
                  incremental_version));
        }
-      
+
     }
 
   if (ferror (listed_incremental_stream))
@@ -1022,7 +1102,9 @@ write_directory_file_entry (void *entry,
 
       s = DIR_IS_NFS (directory) ? "1" : "0";
       fwrite (s, 2, 1, fp);
-      s = umaxtostr (directory->mtime.tv_sec, buf);
+      s = (TYPE_SIGNED (time_t)
+          ? imaxtostr (directory->mtime.tv_sec, buf)
+          : umaxtostr (directory->mtime.tv_sec, buf));
       fwrite (s, strlen (s) + 1, 1, fp);
       s = umaxtostr (directory->mtime.tv_nsec, buf);
       fwrite (s, strlen (s) + 1, 1, fp);
@@ -1065,7 +1147,9 @@ write_directory_file (void)
   fprintf (fp, "%s-%s-%d\n", PACKAGE_NAME, PACKAGE_VERSION,
           TAR_INCREMENTAL_VERSION);
 
-  s = umaxtostr (start_time.tv_sec, buf);
+  s = (TYPE_SIGNED (time_t)
+       ? imaxtostr (start_time.tv_sec, buf)
+       : umaxtostr (start_time.tv_sec, buf));
   fwrite (s, strlen (s) + 1, 1, fp);
   s = umaxtostr (start_time.tv_nsec, buf);
   fwrite (s, strlen (s) + 1, 1, fp);
@@ -1138,7 +1222,7 @@ dumpdir_ok (char *dumpdir)
   char *p;
   int has_tempdir = 0;
   int expect = 0;
-  
+
   for (p = dumpdir; *p; p += strlen (p) + 1)
     {
       if (expect && *p != expect)
@@ -1160,7 +1244,7 @@ dumpdir_ok (char *dumpdir)
          else
            has_tempdir = 1;
          break;
-         
+
        case 'R':
          if (p[1] == 0)
            {
@@ -1175,14 +1259,14 @@ dumpdir_ok (char *dumpdir)
            }
          expect = 'T';
          break;
-           
+
        case 'T':
          if (expect != 'T')
            {
              ERROR ((0, 0,
                      _("Malformed dumpdir: 'T' not preceeded by 'R'")));
              return false;
-           }  
+           }
          if (p[1] == 0 && !has_tempdir)
            {
              ERROR ((0, 0,
@@ -1191,7 +1275,7 @@ dumpdir_ok (char *dumpdir)
            }
          expect = 0;
          break;
-         
+
        case 'N':
        case 'Y':
        case 'D':
@@ -1216,7 +1300,7 @@ dumpdir_ok (char *dumpdir)
 
   return true;
 }
-      
+
 /* Examine the directories under directory_name and delete any
    files that were not there at the time of the back-up. */
 static bool
@@ -1225,7 +1309,7 @@ try_purge_directory (char const *directo
   char *current_dir;
   char *cur, *arc, *p;
   char *temp_stub = NULL;
-  
+
   if (!is_dumpdir (&current_stat_info))
     return false;
 
@@ -1239,13 +1323,13 @@ try_purge_directory (char const *directo
   /* Verify if dump directory is sane */
   if (!dumpdir_ok (current_stat_info.dumpdir))
     return false;
-       
+
   /* Process renames */
   for (arc = current_stat_info.dumpdir; *arc; arc += strlen (arc) + 1)
     {
       if (*arc == 'X')
        {
-#define TEMP_DIR_TEMPLATE "tar.XXXXXX"   
+#define TEMP_DIR_TEMPLATE "tar.XXXXXX"
          size_t len = strlen (arc + 1);
          temp_stub = xrealloc (temp_stub, len + 1 + sizeof TEMP_DIR_TEMPLATE);
          memcpy (temp_stub, arc + 1, len);
@@ -1273,7 +1357,7 @@ try_purge_directory (char const *directo
            src = temp_stub;
          else if (*dst == 0)
            dst = temp_stub;
-           
+
          if (!rename_directory (src, dst))
            {
              free (temp_stub);
@@ -1286,7 +1370,7 @@ try_purge_directory (char const *directo
     }
 
   free (temp_stub);
-  
+
   /* Process deletes */
   p = NULL;
   for (cur = current_dir; *cur; cur += strlen (cur) + 1)
@@ -1334,7 +1418,7 @@ try_purge_directory (char const *directo
        }
     }
   free (p);
-  
+
   free (current_dir);
   return true;
 }
@@ -1345,7 +1429,7 @@ purge_directory (char const *directory_n
   if (!try_purge_directory (directory_name))
     skip_member ();
 }
-     
+
 void
 list_dumpdir (char *buffer, size_t size)
 {

reply via email to

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