]> Dogcows Code - chaz/tar/blobdiff - src/incremen.c
Relicense under GPLv3
[chaz/tar] / src / incremen.c
index eadd80fb056065e8baa4116d7af33c52c3094f8f..4400008a87787eb172c958e35abcb6ecd411d563 100644 (file)
@@ -1,11 +1,11 @@
 /* GNU dump extensions to tar.
 
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
 /* GNU dump extensions to tar.
 
    Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
-   2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+   2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
 
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
-   Free Software Foundation; either version 2, or (at your option) any later
+   Free Software Foundation; either version 3, or (at your option) any later
    version.
 
    This program is distributed in the hope that it will be useful, but
    version.
 
    This program is distributed in the hope that it will be useful, but
@@ -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_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)
 
 #define DIR_SET_FLAG(d,f) (d)->flags |= (f)
 #define DIR_CLEAR_FLAG(d,f) (d)->flags &= ~(f)
 
@@ -119,13 +119,15 @@ make_directory (const char *name)
   size_t namelen = strlen (name);
   size_t size = offsetof (struct directory, name) + namelen + 1;
   struct directory *directory = xmalloc (size);
   size_t namelen = strlen (name);
   size_t size = offsetof (struct directory, name) + namelen + 1;
   struct directory *directory = xmalloc (size);
+  directory->contents = directory->icontents = NULL;
+  directory->orig = NULL;
+  directory->flags = false;
   strcpy (directory->name, name);
   if (ISSLASH (directory->name[namelen-1]))
     directory->name[namelen-1] = 0;
   strcpy (directory->name, name);
   if (ISSLASH (directory->name[namelen-1]))
     directory->name[namelen-1] = 0;
-  directory->flags = false;
   return directory;
 }
   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
 /* 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
@@ -152,7 +154,7 @@ note_directory (char const *name, struct timespec mtime,
     }
   else
     directory->contents = NULL;
     }
   else
     directory->contents = NULL;
-  
+
   if (! ((directory_table
          || (directory_table = hash_initialize (0, 0,
                                                 hash_directory_name,
   if (! ((directory_table
          || (directory_table = hash_initialize (0, 0,
                                                 hash_directory_name,
@@ -209,7 +211,7 @@ void
 update_parent_directory (const char *name)
 {
   struct directory *directory;
 update_parent_directory (const char *name)
 {
   struct directory *directory;
-  char *p, *name_buffer;
+  char *p;
 
   p = dir_name (name);
   directory = find_directory (p);
 
   p = dir_name (name);
   directory = find_directory (p);
@@ -232,13 +234,12 @@ procdir (char *name_buffer, struct stat *stat_data,
 {
   struct directory *directory;
   bool nfs = NFS_FILE_STAT (*stat_data);
 {
   struct directory *directory;
   bool nfs = NFS_FILE_STAT (*stat_data);
-  struct name *np;
 
   if ((directory = find_directory (name_buffer)) != NULL)
     {
       if (DIR_IS_INITED (directory))
        return directory;
 
   if ((directory = find_directory (name_buffer)) != NULL)
     {
       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.
       /* 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.
@@ -277,7 +278,7 @@ procdir (char *name_buffer, struct stat *stat_data,
        }
       else
        directory->children = CHANGED_CHILDREN;
        }
       else
        directory->children = CHANGED_CHILDREN;
-      
+
       DIR_SET_FLAG (directory, DIRF_FOUND);
     }
   else
       DIR_SET_FLAG (directory, DIRF_FOUND);
     }
   else
@@ -309,7 +310,7 @@ procdir (char *name_buffer, struct stat *stat_data,
          if (verbose)
            WARN ((0, 0, _("%s: Directory is new"),
                   quotearg_colon (name_buffer)));
          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
            (listed_incremental_option
             || (OLDER_STAT_TIME (*stat_data, m)
                 || (after_date_option
@@ -323,13 +324,13 @@ procdir (char *name_buffer, struct stat *stat_data,
      omit it... */
   if (one_file_system_option && device != stat_data->st_dev
       /* ... except if it was explicitely given in the command line */
      omit it... */
   if (one_file_system_option && device != stat_data->st_dev
       /* ... except if it was explicitely given in the command line */
-      && !((np = name_scan (name_buffer, true)) && np->explicit))
+      && !is_individual_file (name_buffer))
     directory->children = NO_CHILDREN;
     directory->children = NO_CHILDREN;
-  else if (children == ALL_CHILDREN) 
+  else if (children == ALL_CHILDREN)
     directory->children = ALL_CHILDREN;
 
   DIR_SET_FLAG (directory, DIRF_INIT);
     directory->children = ALL_CHILDREN;
 
   DIR_SET_FLAG (directory, DIRF_INIT);
-  
+
   return directory;
 }
 
   return directory;
 }
 
@@ -341,11 +342,12 @@ dumpdir_locate (const char *dump, const char *name)
   if (dump)
     while (*dump)
       {
   if (dump)
     while (*dump)
       {
-       /* Ignore 'R' (rename) entries, since they break alphabetical ordering.
+       /* Ignore 'R' (rename) and 'X' (tempname) entries, since they break
+          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. */
           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. */
-       if (*dump != 'R')
+       if (!strchr ("RX", *dump))
          {
            int rc = strcmp (dump + 1, name);
            if (rc == 0)
          {
            int rc = strcmp (dump + 1, name);
            if (rc == 0)
@@ -376,7 +378,9 @@ dumpdir_size (const char *p)
 static int
 compare_dirnames (const void *first, const void *second)
 {
 static int
 compare_dirnames (const void *first, const void *second)
 {
-  return strcmp (*(const char**)first, *(const char**)second);
+  char const *const *name1 = first;
+  char const *const *name2 = second;
+  return strcmp (*name1, *name2);
 }
 
 /* Compare dumpdir array from DIRECTORY with directory listing DIR and
 }
 
 /* Compare dumpdir array from DIRECTORY with directory listing DIR and
@@ -385,7 +389,7 @@ compare_dirnames (const void *first, const void *second)
    DIR must be returned by a previous call to savedir().
 
    File names in DIRECTORY->contents must be sorted
    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. */
 
    DIRECTORY->contents is replaced with the created template. Each entry is
    prefixed with ' ' if it was present in DUMP and with 'Y' otherwise. */
@@ -459,7 +463,7 @@ scan_directory (char *dir_name, dev_t device)
   size_t name_length;          /* used length in name_buffer */
   struct stat stat_data;
   struct directory *directory;
   size_t name_length;          /* used length in name_buffer */
   struct stat stat_data;
   struct directory *directory;
-  
+
   if (! dirp)
     savedir_error (dir_name);
 
   if (! dirp)
     savedir_error (dir_name);
 
@@ -473,7 +477,7 @@ scan_directory (char *dir_name, dev_t device)
   if (deref_stat (dereference_option, name_buffer, &stat_data))
     {
       stat_diag (name_buffer);
   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);
            children = CHANGED_CHILDREN;
         but changed to: */
       free (name_buffer);
@@ -482,7 +486,7 @@ scan_directory (char *dir_name, dev_t device)
     }
 
   directory = procdir (name_buffer, &stat_data, device, NO_CHILDREN, false);
     }
 
   directory = procdir (name_buffer, &stat_data, device, NO_CHILDREN, false);
-    
+
   if (dirp && directory->children != NO_CHILDREN)
     {
       char  *entry;    /* directory entry being scanned */
   if (dirp && directory->children != NO_CHILDREN)
     {
       char  *entry;    /* directory entry being scanned */
@@ -502,7 +506,7 @@ scan_directory (char *dir_name, dev_t device)
              name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
            }
          strcpy (name_buffer + name_length, entry + 1);
              name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
            }
          strcpy (name_buffer + name_length, entry + 1);
-         
+
          if (excluded_name (name_buffer))
            *entry = 'N';
          else
          if (excluded_name (name_buffer))
            *entry = 'N';
          else
@@ -513,7 +517,7 @@ scan_directory (char *dir_name, dev_t device)
                  *entry = 'N';
                  continue;
                }
                  *entry = 'N';
                  continue;
                }
-             
+
              if (S_ISDIR (stat_data.st_mode))
                {
                  procdir (name_buffer, &stat_data, device,
              if (S_ISDIR (stat_data.st_mode))
                {
                  procdir (name_buffer, &stat_data, device,
@@ -521,15 +525,15 @@ scan_directory (char *dir_name, dev_t device)
                           verbose_option);
                  *entry = 'D';
                }
                           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 */;
              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))?? */
              /* 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)))
              else if (OLDER_STAT_TIME (stat_data, m)
                       && (!after_date_option
                           || OLDER_STAT_TIME (stat_data, c)))
@@ -539,7 +543,7 @@ scan_directory (char *dir_name, dev_t device)
            }
        }
     }
            }
        }
     }
-  
+
   free (name_buffer);
   if (dirp)
     free (dirp);
   free (name_buffer);
   if (dirp)
     free (dirp);
@@ -554,49 +558,6 @@ get_directory_contents (char *dir_name, dev_t device)
 }
 
 \f
 }
 
 \f
-static bool
-try_pos (char *name, int pos, const char *dumpdir)
-{
-  int i;
-  static char namechars[] =
-    "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-
-  if (pos > 0)
-    for (i = 0; i < sizeof namechars; i++)
-      {
-       name[pos] = namechars[i];
-       if (!dumpdir_locate (dumpdir, name)
-           || try_pos (name, pos-1, dumpdir))
-         return true;
-      }
-  
-  return false;
-}
-  
-static bool
-create_temp_name (char *name, const char *dumpdir)
-{
-  size_t pos = strlen (name) - 6;
-  return try_pos (name + pos, 5, dumpdir);
-}
-
-char *
-make_tmp_dir_name (const char *name)
-{
-  char *dirname = dir_name (name);
-  char *tmp_name = NULL;
-  struct directory *dir = find_directory (dirname);
-      
-  tmp_name = new_name (dirname, "000000");
-  if (!create_temp_name (tmp_name, dir ? dir->contents : NULL))
-    {
-      free (tmp_name);
-      tmp_name = NULL;
-    }
-  free (dirname);
-  return tmp_name;
-}
-
 static void
 obstack_code_rename (struct obstack *stk, char *from, char *to)
 {
 static void
 obstack_code_rename (struct obstack *stk, char *from, char *to)
 {
@@ -611,12 +572,12 @@ rename_handler (void *data, void *proc_data)
 {
   struct directory *dir = data;
   struct obstack *stk = proc_data;
 {
   struct directory *dir = data;
   struct obstack *stk = proc_data;
-  
+
   if (DIR_IS_RENAMED (dir))
     {
       struct directory *prev, *p;
 
   if (DIR_IS_RENAMED (dir))
     {
       struct directory *prev, *p;
 
-      /* Detect eventual cycles and clear DIRF_RENAMED flag, so this entries
+      /* Detect eventual cycles and clear DIRF_RENAMED flag, so these entries
         are ignored when hit by this function next time.
         If the chain forms a cycle, prev points to the entry DIR is renamed
         from. In this case it still retains DIRF_RENAMED flag, which will be
         are ignored when hit by this function next time.
         If the chain forms a cycle, prev points to the entry DIR is renamed
         from. In this case it still retains DIRF_RENAMED flag, which will be
@@ -637,14 +598,17 @@ rename_handler (void *data, void *proc_data)
 
          /* Break the cycle by using a temporary name for one of its
             elements.
 
          /* Break the cycle by using a temporary name for one of its
             elements.
-            FIXME: Leave the choice of the name to the extractor. */
-         temp_name = make_tmp_dir_name (dir->name);
-         obstack_code_rename (stk, dir->name, temp_name);
+            First, create a temp name stub entry. */
+         temp_name = dir_name (dir->name);
+         obstack_1grow (stk, 'X');
+         obstack_grow (stk, temp_name, strlen (temp_name) + 1);
+
+         obstack_code_rename (stk, dir->name, "");
 
          for (p = dir; p != prev; p = p->orig)
            obstack_code_rename (stk, p->orig->name, p->name);
 
 
          for (p = dir; p != prev; p = p->orig)
            obstack_code_rename (stk, p->orig->name, p->name);
 
-         obstack_code_rename (stk, temp_name, prev->name);
+         obstack_code_rename (stk, "", prev->name);
        }
     }
   return true;
        }
     }
   return true;
@@ -658,7 +622,7 @@ append_incremental_renames (const char *dump)
 
   if (directory_table == NULL)
     return dump;
 
   if (directory_table == NULL)
     return dump;
-  
+
   obstack_init (&stk);
   if (dump)
     {
   obstack_init (&stk);
   if (dump)
     {
@@ -667,7 +631,7 @@ append_incremental_renames (const char *dump)
     }
   else
     size = 0;
     }
   else
     size = 0;
-  
+
   hash_do_for_each (directory_table, rename_handler, &stk);
   if (obstack_object_size (&stk) != size)
     {
   hash_do_for_each (directory_table, rename_handler, &stk);
   if (obstack_object_size (&stk) != size)
     {
@@ -699,7 +663,8 @@ read_incr_db_01 (int version, const char *initbuf)
 {
   int n;
   uintmax_t u;
 {
   int n;
   uintmax_t u;
-  time_t t = u;
+  time_t sec;
+  long int nsec;
   char *buf = 0;
   size_t bufsize;
   char *ebuf;
   char *buf = 0;
   size_t bufsize;
   char *ebuf;
@@ -720,41 +685,49 @@ read_incr_db_01 (int version, const char *initbuf)
       buf = strdup (initbuf);
       bufsize = strlen (buf) + 1;
     }
       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")));
            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
     {
   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)))
     {
 
   while (0 < (n = getline (&buf, &bufsize, listed_incremental_stream)))
     {
@@ -763,65 +736,79 @@ read_incr_db_01 (int version, const char *initbuf)
       bool nfs = buf[0] == '+';
       char *strp = buf + nfs;
       struct timespec mtime;
       bool nfs = buf[0] == '+';
       char *strp = buf + nfs;
       struct timespec mtime;
-      
+
       lineno++;
       lineno++;
-      
+
       if (buf[n - 1] == '\n')
        buf[n - 1] = '\0';
       if (buf[n - 1] == '\n')
        buf[n - 1] = '\0';
-      
+
       if (version == 1)
        {
          errno = 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;
          strp = ebuf;
-         
+
          errno = 0;
          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);
          strp = ebuf;
        }
       else
        memset (&mtime, 0, sizeof mtime);
-      
+
       errno = 0;
       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;
       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 = ebuf;
-      
+
       strp++;
       unquote_string (strp);
       note_directory (strp, mtime, dev, ino, nfs, false, NULL);
       strp++;
       unquote_string (strp);
       note_directory (strp, mtime, dev, ino, nfs, false, NULL);
@@ -848,31 +835,128 @@ read_obstack (FILE *fp, struct obstack *stk, size_t *pcount)
 }
 
 /* Read from file FP a nul-terminated string and convert it to
 }
 
 /* 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
    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;
 
   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;
     {
       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;
   buf[i] = 0;
+  errno = 0;
   *pval = strtoumax (buf, &ep, 10);
   *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;
   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 incremental snapshot format 2 */
 static void
@@ -880,28 +964,10 @@ read_incr_db_2 ()
 {
   uintmax_t u;
   struct obstack stk;
 {
   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 (;;)
     {
 
   for (;;)
     {
@@ -912,43 +978,21 @@ read_incr_db_2 ()
       char *name;
       char *content;
       size_t s;
       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;
        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;
        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;
        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;
 
       if (read_obstack (listed_incremental_stream, &stk, &s))
        break;
@@ -959,16 +1003,16 @@ read_incr_db_2 ()
        ;
       if (getc (listed_incremental_stream) != 0)
        FATAL_ERROR ((0, 0, "%s: %s",
        ;
       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")));
                      _("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",
       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).
 }
 
 /* Read incremental snapshot file (directory file).
@@ -983,7 +1027,6 @@ read_directory_file (void)
   int fd;
   char *buf = 0;
   size_t bufsize;
   int fd;
   char *buf = 0;
   size_t bufsize;
-  long lineno = 1;
 
   /* Open the file for both read and write.  That way, we can write
      it later without having to reopen it, and don't have to worry if
 
   /* Open the file for both read and write.  That way, we can write
      it later without having to reopen it, and don't have to worry if
@@ -1006,7 +1049,7 @@ read_directory_file (void)
   if (0 < getline (&buf, &bufsize, listed_incremental_stream))
     {
       char *ebuf;
   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)
        {
 
       if (strncmp (buf, PACKAGE_NAME, sizeof PACKAGE_NAME - 1) == 0)
        {
@@ -1017,7 +1060,7 @@ read_directory_file (void)
            if (!*ebuf)
              ERROR((1, 0, _("Bad incremental file format")));
 
            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;
        }
       else
        incremental_version = 0;
@@ -1032,12 +1075,12 @@ read_directory_file (void)
        case TAR_INCREMENTAL_VERSION:
          read_incr_db_2 ();
          break;
        case TAR_INCREMENTAL_VERSION:
          read_incr_db_2 ();
          break;
-         
+
        default:
        default:
-         ERROR ((1, 0, _("Unsupported incremental format version: %d"),
+         ERROR ((1, 0, _("Unsupported incremental format version: %"PRIuMAX),
                  incremental_version));
        }
                  incremental_version));
        }
-      
+
     }
 
   if (ferror (listed_incremental_stream))
     }
 
   if (ferror (listed_incremental_stream))
@@ -1061,7 +1104,9 @@ write_directory_file_entry (void *entry, void *data)
 
       s = DIR_IS_NFS (directory) ? "1" : "0";
       fwrite (s, 2, 1, fp);
 
       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);
       fwrite (s, strlen (s) + 1, 1, fp);
       s = umaxtostr (directory->mtime.tv_nsec, buf);
       fwrite (s, strlen (s) + 1, 1, fp);
@@ -1104,7 +1149,9 @@ write_directory_file (void)
   fprintf (fp, "%s-%s-%d\n", PACKAGE_NAME, PACKAGE_VERSION,
           TAR_INCREMENTAL_VERSION);
 
   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);
   fwrite (s, strlen (s) + 1, 1, fp);
   s = umaxtostr (start_time.tv_nsec, buf);
   fwrite (s, strlen (s) + 1, 1, fp);
@@ -1171,51 +1218,161 @@ is_dumpdir (struct tar_stat_info *stat_info)
   return stat_info->is_dumpdir;
 }
 
   return stat_info->is_dumpdir;
 }
 
+static bool
+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)
+       {
+         ERROR ((0, 0,
+                 _("Malformed dumpdir: expected '%c' but found %#3o"),
+                 expect, *p));
+         return false;
+       }
+      switch (*p)
+       {
+       case 'X':
+         if (has_tempdir)
+           {
+             ERROR ((0, 0,
+                     _("Malformed dumpdir: 'X' duplicated")));
+             return false;
+           }
+         else
+           has_tempdir = 1;
+         break;
+
+       case 'R':
+         if (p[1] == 0)
+           {
+             if (!has_tempdir)
+               {
+                 ERROR ((0, 0,
+                         _("Malformed dumpdir: empty name in 'R'")));
+                 return false;
+               }
+             else
+               has_tempdir = 0;
+           }
+         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,
+                     _("Malformed dumpdir: empty name in 'T'")));
+             return false;
+           }
+         expect = 0;
+         break;
+
+       case 'N':
+       case 'Y':
+       case 'D':
+         break;
+
+       default:
+         /* FIXME: bail out? */
+         break;
+       }
+    }
+
+  if (expect)
+    {
+      ERROR ((0, 0,
+             _("Malformed dumpdir: expected '%c' but found end of data"),
+             expect));
+      return false;
+    }
+
+  if (has_tempdir)
+    WARN ((0, 0, _("Malformed dumpdir: 'X' never used")));
+
+  return true;
+}
+
 /* Examine the directories under directory_name and delete any
    files that were not there at the time of the back-up. */
 /* Examine the directories under directory_name and delete any
    files that were not there at the time of the back-up. */
-void
-purge_directory (char const *directory_name)
+static bool
+try_purge_directory (char const *directory_name)
 {
   char *current_dir;
   char *cur, *arc, *p;
 {
   char *current_dir;
   char *cur, *arc, *p;
+  char *temp_stub = NULL;
 
   if (!is_dumpdir (&current_stat_info))
 
   if (!is_dumpdir (&current_stat_info))
-    {
-      skip_member ();
-      return;
-    }
+    return false;
 
   current_dir = savedir (directory_name);
 
   if (!current_dir)
 
   current_dir = savedir (directory_name);
 
   if (!current_dir)
-    {
-      /* The directory doesn't exist now.  It'll be created.  In any
-        case, we don't have to delete any files out of it.  */
+    /* The directory doesn't exist now.  It'll be created.  In any
+       case, we don't have to delete any files out of it.  */
+    return false;
 
 
-      skip_member ();
-      return;
-    }
+  /* 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)
     {
 
   /* Process renames */
   for (arc = current_stat_info.dumpdir; *arc; arc += strlen (arc) + 1)
     {
-      if (*arc == 'R')
+      if (*arc == 'X')
+       {
+#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);
+         temp_stub[len] = '/';
+         memcpy (temp_stub + len + 1, TEMP_DIR_TEMPLATE,
+                 sizeof TEMP_DIR_TEMPLATE);
+         if (!mkdtemp (temp_stub))
+           {
+             ERROR ((0, errno,
+                     _("Cannot create temporary directory using template %s"),
+                     quote (temp_stub)));
+             free (temp_stub);
+             free (current_dir);
+             return false;
+           }
+       }
+      else if (*arc == 'R')
        {
          char *src, *dst;
          src = arc + 1;
          arc += strlen (arc) + 1;
          dst = arc + 1;
 
        {
          char *src, *dst;
          src = arc + 1;
          arc += strlen (arc) + 1;
          dst = arc + 1;
 
+         if (*src == 0)
+           src = temp_stub;
+         else if (*dst == 0)
+           dst = temp_stub;
+
          if (!rename_directory (src, dst))
            {
          if (!rename_directory (src, dst))
            {
+             free (temp_stub);
              free (current_dir);
              /* FIXME: Make sure purge_directory(dst) will return
                 immediately */
              free (current_dir);
              /* FIXME: Make sure purge_directory(dst) will return
                 immediately */
-             return;
+             return false;
            }
        }
     }
            }
        }
     }
-  
+
+  free (temp_stub);
+
   /* Process deletes */
   p = NULL;
   for (cur = current_dir; *cur; cur += strlen (cur) + 1)
   /* Process deletes */
   p = NULL;
   for (cur = current_dir; *cur; cur += strlen (cur) + 1)
@@ -1226,22 +1383,23 @@ purge_directory (char const *directory_name)
        free (p);
       p = new_name (directory_name, cur);
 
        free (p);
       p = new_name (directory_name, cur);
 
-      if (!(entry = dumpdir_locate (current_stat_info.dumpdir, cur))
-         || (*entry == 'D' && S_ISDIR (st.st_mode))
-         || (*entry == 'Y' && !S_ISDIR (st.st_mode)))
+      if (deref_stat (false, p, &st))
        {
        {
-         if (deref_stat (false, p, &st))
+         if (errno != ENOENT) /* FIXME: Maybe keep a list of renamed
+                                 dirs and check it here? */
            {
            {
-             if (errno != ENOENT) /* FIXME: Maybe keep a list of renamed
-                                     dirs and check it here? */
-               {
-                 stat_diag (p);
-                 WARN ((0, 0, _("%s: Not purging directory: unable to stat"),
-                        quotearg_colon (p)));
-               }
-             continue;
+             stat_diag (p);
+             WARN ((0, 0, _("%s: Not purging directory: unable to stat"),
+                    quotearg_colon (p)));
            }
            }
-         else if (one_file_system_option && st.st_dev != root_device)
+         continue;
+       }
+
+      if (!(entry = dumpdir_locate (current_stat_info.dumpdir, cur))
+         || (*entry == 'D' && !S_ISDIR (st.st_mode))
+         || (*entry == 'Y' && S_ISDIR (st.st_mode)))
+       {
+         if (one_file_system_option && st.st_dev != root_device)
            {
              WARN ((0, 0,
                     _("%s: directory is on a different device: not purging"),
            {
              WARN ((0, 0,
                     _("%s: directory is on a different device: not purging"),
@@ -1263,13 +1421,22 @@ purge_directory (char const *directory_name)
        }
     }
   free (p);
        }
     }
   free (p);
-  
+
   free (current_dir);
   free (current_dir);
+  return true;
+}
+
+void
+purge_directory (char const *directory_name)
+{
+  if (!try_purge_directory (directory_name))
+    skip_member ();
 }
 
 void
 list_dumpdir (char *buffer, size_t size)
 {
 }
 
 void
 list_dumpdir (char *buffer, size_t size)
 {
+  int state = 0;
   while (size)
     {
       switch (*buffer)
   while (size)
     {
       switch (*buffer)
@@ -1279,7 +1446,13 @@ list_dumpdir (char *buffer, size_t size)
        case 'D':
        case 'R':
        case 'T':
        case 'D':
        case 'R':
        case 'T':
-         fprintf (stdlis, "%c ", *buffer);
+       case 'X':
+         fprintf (stdlis, "%c", *buffer);
+         if (state == 0)
+           {
+             fprintf (stdlis, " ");
+             state = 1;
+           }
          buffer++;
          size--;
          break;
          buffer++;
          size--;
          break;
@@ -1288,6 +1461,7 @@ list_dumpdir (char *buffer, size_t size)
          fputc ('\n', stdlis);
          buffer++;
          size--;
          fputc ('\n', stdlis);
          buffer++;
          size--;
+         state = 0;
          break;
 
        default:
          break;
 
        default:
This page took 0.046005 seconds and 4 git commands to generate.