]> Dogcows Code - chaz/tar/commitdiff
Fix incremental archiving of renamed directories.
authorSergey Poznyakoff <gray@gnu.org.ua>
Thu, 31 Jul 2008 07:12:50 +0000 (07:12 +0000)
committerSergey Poznyakoff <gray@gnu.org.ua>
Thu, 31 Jul 2008 07:12:50 +0000 (07:12 +0000)
* src/incremen.c (struct directory): New member `next'.  Change
type of `name'.
(dirhead, dirtail): New statics.
(make_directory): Reflect changes to struct directory.
(free_directory, attach_directory): New functions.
(dirlist_replace_prefix): New function.
(note_directory): Use attach_directory, instead of make_directory,
(find_directory, find_directory_meta): Use free_directory.
(procdir): Replace directory prefixes in directory list to avoid
marking subdirectories as renamed after renaming their parent
directory.
(append_incremental_renames): Iterate over directory list, not
hash table, to preserve logical ordering of renames.
* tests/rename04.at, tests/rename05.at: New test cases.
* tests/Makefile.am, tests/testsuite.at: Add rename04.at and
rename05.at.
* tests/atlocal.in (decho): New function.
* tests/multiv06.at: Use decho instead of echo2.
* tests/incremental.at: Raise wait interval to 2 seconds.

ChangeLog
src/incremen.c
tests/Makefile.am
tests/atlocal.in
tests/incremental.at
tests/multiv06.at
tests/rename04.at [new file with mode: 0644]
tests/rename05.at [new file with mode: 0644]
tests/testsuite.at

index b2e94524cc0b4344b6656c787574369b4d691802..21c972f0db6d007b3af698e8bd0e8e697356321d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,8 +1,16 @@
+2008-07-31  Sergey Poznyakoff  <gray@gnu.org.ua>
+
+       * src/incremen.c (struct directory): New member `next'.  Change
+       type of `name'.
+       (dirhead, dirtail): New statics.
+       (make_directory): Reflect changes to struct directory.
+       (free_directory, attach_directory): New functions.
+       
+
 2008-07-24  Sergey Poznyakoff  <gray@gnu.org.ua>
 
        * src/tar.c (decode_options): Do not allow volume length less
        than record size.
-
        * src/buffer.c (_gnu_flush_write): Compensate for the effect
        of eventual flush_archive occurring in the middle of buffer
        move.
index 372061dd12a3aadb4f51524482e7066107dd6df9..d23e45c7a2787662c16e6bd92f135bf2160d58da 100644 (file)
@@ -60,6 +60,7 @@ struct dumpdir                 /* Dump directory listing */
 /* Directory attributes.  */
 struct directory
   {
+    struct directory *next;
     struct timespec mtime;      /* Modification time */
     dev_t device_number;       /* device number for directory */
     ino_t inode_number;                /* inode number for directory */
@@ -72,7 +73,7 @@ struct directory
                                   the original directory structure */
     const char *tagfile;        /* Tag file, if the directory falls under
                                   exclusion_tag_under */
-    char name[1];              /* file name of directory */
+    char *name;                        /* file name of directory */
   };
 
 struct dumpdir *
@@ -196,6 +197,7 @@ dumpdir_size (const char *p)
 }
 
 \f
+static struct directory *dirhead, *dirtail;
 static Hash_table *directory_table;
 static Hash_table *directory_meta_table;
 
@@ -247,18 +249,67 @@ static struct directory *
 make_directory (const char *name)
 {
   size_t namelen = strlen (name);
-  size_t size = offsetof (struct directory, name) + namelen + 1;
-  struct directory *directory = xmalloc (size);
+  struct directory *directory = xmalloc (sizeof (*directory));
+  directory->next = NULL;
   directory->dump = directory->idump = NULL;
   directory->orig = NULL;
   directory->flags = false;
-  strcpy (directory->name, name);
-  if (namelen && ISSLASH (directory->name[namelen - 1]))
-    directory->name[namelen - 1] = 0;
+  if (namelen && ISSLASH (name[namelen - 1]))
+    namelen--;
+  directory->name = xmalloc (namelen + 1);
+  memcpy (directory->name, name, namelen);
+  directory->name[namelen] = 0;
   directory->tagfile = NULL;
   return directory;
 }
 
+static void
+free_directory (struct directory *dir)
+{
+  free (dir->name);
+  free (dir);
+}
+
+static struct directory *
+attach_directory (const char *name)
+{
+  struct directory *dir = make_directory (name);
+  if (dirtail)
+    dirtail->next = dir;
+  else
+    dirhead = dir;
+  dirtail = dir;
+}
+                
+\f
+static void
+replace_prefix (char **pname, const char *samp, size_t slen,
+               const char *repl, size_t rlen)
+{
+  char *name = *pname;
+  size_t nlen = strlen (name);
+  if (nlen > slen && memcmp (name, samp, slen) == 0 && ISSLASH (name[slen]))
+    {
+      if (rlen > slen)
+       {
+         name = xrealloc (name, nlen - slen + rlen + 1);
+         *pname = name;
+       }
+      memmove (name + rlen, name + slen, nlen - slen + 1);
+      memcpy (name, repl, rlen);
+    }
+}
+
+void
+dirlist_replace_prefix (const char *pref, const char *repl)
+{
+  struct directory *dp;
+  size_t pref_len = strlen (pref);
+  size_t repl_len = strlen (repl);
+  for (dp = dirhead; dp; dp = dp->next)
+    replace_prefix (&dp->name, pref, pref_len, repl, repl_len);
+}
+
 /* 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
@@ -268,7 +319,7 @@ note_directory (char const *name, struct timespec mtime,
                dev_t dev, ino_t ino, bool nfs, bool found,
                const char *contents)
 {
-  struct directory *directory = make_directory (name);
+  struct directory *directory = attach_directory (name);
 
   directory->mtime = mtime;
   directory->device_number = dev;
@@ -311,7 +362,7 @@ find_directory (const char *name)
     {
       struct directory *dir = make_directory (name);
       struct directory *ret = hash_lookup (directory_table, dir);
-      free (dir);
+      free_directory (dir);
       return ret;
     }
 }
@@ -330,7 +381,7 @@ find_directory_meta (dev_t dev, ino_t ino)
       dir->device_number = dev;
       dir->inode_number = ino;
       ret = hash_lookup (directory_meta_table, dir);
-      free (dir);
+      free_directory (dir);
       return ret;
     }
 }
@@ -386,12 +437,16 @@ procdir (char *name_buffer, struct stat *stat_data,
                                                     stat_data->st_ino);
          if (d)
            {
-             if (verbose_option)
-               WARN ((0, 0, _("%s: Directory has been renamed from %s"),
-                      quotearg_colon (name_buffer),
-                      quote_n (1, d->name)));
-             directory->orig = d;
-             DIR_SET_FLAG (directory, DIRF_RENAMED);
+             if (strcmp (d->name, name_buffer))
+               {
+                 if (verbose_option)
+                   WARN ((0, 0, _("%s: Directory has been renamed from %s"),
+                          quotearg_colon (name_buffer),
+                          quote_n (1, d->name)));
+                 directory->orig = d;
+                 DIR_SET_FLAG (directory, DIRF_RENAMED);
+                 dirlist_replace_prefix (d->name, name_buffer);
+               }
              directory->children = CHANGED_CHILDREN;
            }
          else
@@ -426,12 +481,16 @@ procdir (char *name_buffer, struct stat *stat_data,
 
       if (d)
        {
-         if (verbose)
-           WARN ((0, 0, _("%s: Directory has been renamed from %s"),
-                  quotearg_colon (name_buffer),
-                  quote_n (1, d->name)));
-         directory->orig = d;
-         DIR_SET_FLAG (directory, DIRF_RENAMED);
+         if (strcmp (d->name, name_buffer))
+           {
+             if (verbose)
+               WARN ((0, 0, _("%s: Directory has been renamed from %s"),
+                      quotearg_colon (name_buffer),
+                      quote_n (1, d->name)));
+             directory->orig = d;
+             DIR_SET_FLAG (directory, DIRF_RENAMED);
+             dirlist_replace_prefix (d->name, name_buffer);
+           }
          directory->children = CHANGED_CHILDREN;
        }
       else
@@ -701,12 +760,9 @@ obstack_code_rename (struct obstack *stk, char *from, char *to)
   obstack_grow (stk, s, strlen (s) + 1);
 }
 
-static bool
-rename_handler (void *data, void *proc_data)
+static void
+store_rename (struct directory *dir, struct obstack *stk)
 {
-  struct directory *dir = data;
-  struct obstack *stk = proc_data;
-
   if (DIR_IS_RENAMED (dir))
     {
       struct directory *prev, *p;
@@ -745,7 +801,6 @@ rename_handler (void *data, void *proc_data)
          obstack_code_rename (stk, "", prev->name);
        }
     }
-  return true;
 }
 
 const char *
@@ -753,8 +808,9 @@ append_incremental_renames (const char *dump)
 {
   struct obstack stk;
   size_t size;
-
-  if (directory_table == NULL)
+  struct directory *dp;
+  
+  if (dirhead == NULL)
     return dump;
 
   obstack_init (&stk);
@@ -766,7 +822,9 @@ append_incremental_renames (const char *dump)
   else
     size = 0;
 
-  hash_do_for_each (directory_table, rename_handler, &stk);
+  for (dp = dirhead; dp; dp = dp->next)
+    store_rename (dp, &stk);
+
   if (obstack_object_size (&stk) != size)
     {
       obstack_1grow (&stk, 0);
index 263f6eefd02d58eb1170b94cbe24824bb8354564..cad0dfed275f16174671303ac58ce7e0ac6b16cb 100644 (file)
@@ -98,6 +98,8 @@ TESTSUITE_AT = \
  rename01.at\
  rename02.at\
  rename03.at\
+ rename04.at\
+ rename05.at\
  same-order01.at\
  same-order02.at\
  shortfile.at\
index 380cac01aed4882869207bd043635e6f3917cecd..5eaaab68f4b24ef8714fdf4274be86eb0fb3d0ab 100644 (file)
@@ -30,4 +30,8 @@ tarball_prereq() {
   echo "$2  $3/$1" | md5sum --status --check - >/dev/null 2>&1
 }
 
+decho() {
+  echo $*
+  echo >&2 $*
+}
 
index bab0beba144f8da039f646bd8454d9bbc7b15d04..5873d83aa9baee16aec66d04cad318ead7d18b5a 100644 (file)
@@ -44,7 +44,10 @@ sleep 1
 tar cf archive --listed=list structure
 tar cfv archive --listed=list structure
 echo separator
-sleep 1
+# ReiserFS often offsets the timestamps of newly created files
+# 1 second to the past.  Try to compensate for it, until a better
+# solution is found.
+sleep 2
 echo y >structure/file
 tar cfv archive --listed=list structure
 ],
index 4df9cd71dbc3de5c64d77b8500290adf731ab14e..966529952ffd5f8fc3d3afed85d04f85cb6f1f3d 100644 (file)
 AT_SETUP([Multivolumes with L=record_size])
 AT_KEYWORDS([multivolume multiv multiv06])
 
-m4_define([echo2],[
-echo $*
-echo >&2 $*
-])
-
 AT_TAR_CHECK([
 exec <&-
-echo2("Creating file")
+decho Creating file
 genfile --length 20139 --file file
-echo2("Creating archive") 
+decho Creating archive
 tar -c -M -L10 -b20 -farc.1 -farc.2 -farc.3 file
-echo2("Testing archive")
+decho Testing archive
 tar -t -M -farc.1 -farc.2 -farc.3],
 [0],
 [Creating file
diff --git a/tests/rename04.at b/tests/rename04.at
new file mode 100644 (file)
index 0000000..3e9eb1a
--- /dev/null
@@ -0,0 +1,83 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+
+# Test suite for GNU tar.
+# Copyright (C) 2008 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 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 WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+# Description: Up to version 1.20, when storing a record for renamed
+# directory in an incremental archive, tar incorrectly flagged all its
+# subdirectories as renamed, which led to problems at archive extraction.
+# References: <00a401c8ecc0$56b7ef30$6a17a8c0@inti.com>
+# Reported by: Enric Hernandez <ehernandez@notariado.org>
+
+AT_SETUP([renamed directory containing subdirectories])
+AT_KEYWORDS([incremental rename04 rename])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+
+decho Creating directory structure
+mkdir directory
+mkdir directory/subdir
+genfile --file=directory/file
+
+decho Creating initial archive
+tar -cf archive.1 -g db.1 directory
+
+decho Renaming
+mv directory dir
+
+decho Creating incremental archive
+cp db.1 db.2
+tar -cf archive.2 -g db.2 dir
+
+mv dir orig
+
+decho First restore
+tar -xf archive.1 -g db.1
+find directory | sort
+
+decho Second restore
+tar -xf archive.2 -g db.2
+find dir | sort
+],
+[0],
+[Creating directory structure
+Creating initial archive
+Renaming
+Creating incremental archive
+First restore
+directory
+directory/file
+directory/subdir
+Second restore
+dir
+dir/subdir
+],
+[Creating directory structure
+Creating initial archive
+Renaming
+Creating incremental archive
+First restore
+Second restore
+],[],[],[gnu, oldgnu, posix])
+
+AT_CLEANUP
+
+# End of rename04.at
+
+
diff --git a/tests/rename05.at b/tests/rename05.at
new file mode 100644 (file)
index 0000000..3ad93a6
--- /dev/null
@@ -0,0 +1,81 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+
+# Test suite for GNU tar.
+# Copyright (C) 2008 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 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 WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+# Description:  A continuation of rename04.at, that checks additionally if
+# renamed subdirectories are restored correctly.
+
+AT_SETUP([renamed subdirectories])
+AT_KEYWORDS([incremental rename05 rename])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+
+decho Creating directory structure
+mkdir directory
+mkdir directory/subdir
+genfile --file=directory/file
+
+decho Creating initial archive
+tar -cf archive.1 -g db.1 directory
+
+decho Renaming
+mv directory/subdir directory/subdir.0
+mv directory dir
+
+decho Creating incremental archive
+cp db.1 db.2
+tar -cf archive.2 -g db.2 dir
+
+mv dir orig
+
+decho First restore
+tar -xf archive.1 -g db.1
+find directory | sort
+
+decho Second restore
+tar -xf archive.2 -g db.2
+find dir | sort
+],
+[0],
+[Creating directory structure
+Creating initial archive
+Renaming
+Creating incremental archive
+First restore
+directory
+directory/file
+directory/subdir
+Second restore
+dir
+dir/subdir.0
+],
+[Creating directory structure
+Creating initial archive
+Renaming
+Creating incremental archive
+First restore
+Second restore
+],[],[],[gnu, oldgnu, posix])
+
+AT_CLEANUP
+
+# End of rename05.at
+
+
index 11f108bd6c3c58d8b701a9860caf3f89e7fd2e14..2fa53925c66b72d80b548baa8831413e6dd9a43d 100644 (file)
@@ -138,6 +138,8 @@ m4_include([incr04.at])
 m4_include([rename01.at])
 m4_include([rename02.at])
 m4_include([rename03.at])
+m4_include([rename04.at])
+m4_include([rename05.at])
 m4_include([chtype.at])
 
 m4_include([ignfail.at])
This page took 0.041089 seconds and 4 git commands to generate.