Browse Source

Fix incremental archiving of renamed directories.

* 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.
Sergey Poznyakoff 16 years ago
parent
commit
dbbffde583
9 changed files with 276 additions and 40 deletions
  1. 9 1
      ChangeLog
  2. 88 30
      src/incremen.c
  3. 2 0
      tests/Makefile.am
  4. 4 0
      tests/atlocal.in
  5. 4 1
      tests/incremental.at
  6. 3 8
      tests/multiv06.at
  7. 83 0
      tests/rename04.at
  8. 81 0
      tests/rename05.at
  9. 2 0
      tests/testsuite.at

+ 9 - 1
ChangeLog

@@ -1,8 +1,16 @@
+2008-07-31  Sergey Poznyakoff  <[email protected]>
+
+	* 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  <[email protected]>
 2008-07-24  Sergey Poznyakoff  <[email protected]>
 
 
 	* src/tar.c (decode_options): Do not allow volume length less
 	* src/tar.c (decode_options): Do not allow volume length less
 	than record size.
 	than record size.
-
 	* src/buffer.c (_gnu_flush_write): Compensate for the effect
 	* src/buffer.c (_gnu_flush_write): Compensate for the effect
 	of eventual flush_archive occurring in the middle of buffer
 	of eventual flush_archive occurring in the middle of buffer
 	move.
 	move.

+ 88 - 30
src/incremen.c

@@ -60,6 +60,7 @@ struct dumpdir                 /* Dump directory listing */
 /* Directory attributes.  */
 /* Directory attributes.  */
 struct directory
 struct directory
   {
   {
+    struct directory *next;
     struct timespec mtime;      /* Modification time */
     struct timespec mtime;      /* Modification time */
     dev_t device_number;	/* device number for directory */
     dev_t device_number;	/* device number for directory */
     ino_t inode_number;		/* inode number for directory */
     ino_t inode_number;		/* inode number for directory */
@@ -72,7 +73,7 @@ struct directory
 				   the original directory structure */
 				   the original directory structure */
     const char *tagfile;        /* Tag file, if the directory falls under
     const char *tagfile;        /* Tag file, if the directory falls under
 				   exclusion_tag_under */
 				   exclusion_tag_under */
-    char name[1];		/* file name of directory */
+    char *name;	     	        /* file name of directory */
   };
   };
 
 
 struct dumpdir *
 struct dumpdir *
@@ -196,6 +197,7 @@ dumpdir_size (const char *p)
 }
 }
 
 
 
 
+static struct directory *dirhead, *dirtail;
 static Hash_table *directory_table;
 static Hash_table *directory_table;
 static Hash_table *directory_meta_table;
 static Hash_table *directory_meta_table;
 
 
@@ -247,18 +249,67 @@ static struct directory *
 make_directory (const char *name)
 make_directory (const char *name)
 {
 {
   size_t namelen = strlen (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->dump = directory->idump = NULL;
   directory->orig = NULL;
   directory->orig = NULL;
   directory->flags = false;
   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;
   directory->tagfile = NULL;
   return directory;
   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;
+}
+		 
+
+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
 /* Create and link a new directory entry for directory NAME, having a
    device number DEV and an inode number INO, with NFS indicating
    device number DEV and an inode number INO, with NFS indicating
    whether it is an NFS device and FOUND indicating whether we have
    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,
 		dev_t dev, ino_t ino, bool nfs, bool found,
 		const char *contents)
 		const char *contents)
 {
 {
-  struct directory *directory = make_directory (name);
+  struct directory *directory = attach_directory (name);
 
 
   directory->mtime = mtime;
   directory->mtime = mtime;
   directory->device_number = dev;
   directory->device_number = dev;
@@ -311,7 +362,7 @@ find_directory (const char *name)
     {
     {
       struct directory *dir = make_directory (name);
       struct directory *dir = make_directory (name);
       struct directory *ret = hash_lookup (directory_table, dir);
       struct directory *ret = hash_lookup (directory_table, dir);
-      free (dir);
+      free_directory (dir);
       return ret;
       return ret;
     }
     }
 }
 }
@@ -330,7 +381,7 @@ find_directory_meta (dev_t dev, ino_t ino)
       dir->device_number = dev;
       dir->device_number = dev;
       dir->inode_number = ino;
       dir->inode_number = ino;
       ret = hash_lookup (directory_meta_table, dir);
       ret = hash_lookup (directory_meta_table, dir);
-      free (dir);
+      free_directory (dir);
       return ret;
       return ret;
     }
     }
 }
 }
@@ -386,12 +437,16 @@ procdir (char *name_buffer, struct stat *stat_data,
 						     stat_data->st_ino);
 						     stat_data->st_ino);
 	  if (d)
 	  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;
 	      directory->children = CHANGED_CHILDREN;
 	    }
 	    }
 	  else
 	  else
@@ -426,12 +481,16 @@ procdir (char *name_buffer, struct stat *stat_data,
 
 
       if (d)
       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;
 	  directory->children = CHANGED_CHILDREN;
 	}
 	}
       else
       else
@@ -701,12 +760,9 @@ obstack_code_rename (struct obstack *stk, char *from, char *to)
   obstack_grow (stk, s, strlen (s) + 1);
   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))
   if (DIR_IS_RENAMED (dir))
     {
     {
       struct directory *prev, *p;
       struct directory *prev, *p;
@@ -745,7 +801,6 @@ rename_handler (void *data, void *proc_data)
 	  obstack_code_rename (stk, "", prev->name);
 	  obstack_code_rename (stk, "", prev->name);
 	}
 	}
     }
     }
-  return true;
 }
 }
 
 
 const char *
 const char *
@@ -753,8 +808,9 @@ append_incremental_renames (const char *dump)
 {
 {
   struct obstack stk;
   struct obstack stk;
   size_t size;
   size_t size;
-
-  if (directory_table == NULL)
+  struct directory *dp;
+  
+  if (dirhead == NULL)
     return dump;
     return dump;
 
 
   obstack_init (&stk);
   obstack_init (&stk);
@@ -766,7 +822,9 @@ append_incremental_renames (const char *dump)
   else
   else
     size = 0;
     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)
   if (obstack_object_size (&stk) != size)
     {
     {
       obstack_1grow (&stk, 0);
       obstack_1grow (&stk, 0);

+ 2 - 0
tests/Makefile.am

@@ -98,6 +98,8 @@ TESTSUITE_AT = \
  rename01.at\
  rename01.at\
  rename02.at\
  rename02.at\
  rename03.at\
  rename03.at\
+ rename04.at\
+ rename05.at\
  same-order01.at\
  same-order01.at\
  same-order02.at\
  same-order02.at\
  shortfile.at\
  shortfile.at\

+ 4 - 0
tests/atlocal.in

@@ -30,4 +30,8 @@ tarball_prereq() {
   echo "$2  $3/$1" | md5sum --status --check - >/dev/null 2>&1
   echo "$2  $3/$1" | md5sum --status --check - >/dev/null 2>&1
 }
 }
 
 
+decho() {
+  echo $*
+  echo >&2 $*
+}
 
 

+ 4 - 1
tests/incremental.at

@@ -44,7 +44,10 @@ sleep 1
 tar cf archive --listed=list structure
 tar cf archive --listed=list structure
 tar cfv archive --listed=list structure
 tar cfv archive --listed=list structure
 echo separator
 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
 echo y >structure/file
 tar cfv archive --listed=list structure
 tar cfv archive --listed=list structure
 ],
 ],

+ 3 - 8
tests/multiv06.at

@@ -27,18 +27,13 @@
 AT_SETUP([Multivolumes with L=record_size])
 AT_SETUP([Multivolumes with L=record_size])
 AT_KEYWORDS([multivolume multiv multiv06])
 AT_KEYWORDS([multivolume multiv multiv06])
 
 
-m4_define([echo2],[
-echo $*
-echo >&2 $*
-])
-
 AT_TAR_CHECK([
 AT_TAR_CHECK([
 exec <&-
 exec <&-
-echo2("Creating file")
+decho Creating file
 genfile --length 20139 --file file
 genfile --length 20139 --file file
-echo2("Creating archive") 
+decho Creating archive
 tar -c -M -L10 -b20 -farc.1 -farc.2 -farc.3 file
 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],
 tar -t -M -farc.1 -farc.2 -farc.3],
 [0],
 [0],
 [Creating file
 [Creating file

+ 83 - 0
tests/rename04.at

@@ -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: <[email protected]>
+# Reported by: Enric Hernandez <[email protected]>
+
+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
+
+

+ 81 - 0
tests/rename05.at

@@ -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
+
+

+ 2 - 0
tests/testsuite.at

@@ -138,6 +138,8 @@ m4_include([incr04.at])
 m4_include([rename01.at])
 m4_include([rename01.at])
 m4_include([rename02.at])
 m4_include([rename02.at])
 m4_include([rename03.at])
 m4_include([rename03.at])
+m4_include([rename04.at])
+m4_include([rename05.at])
 m4_include([chtype.at])
 m4_include([chtype.at])
 
 
 m4_include([ignfail.at])
 m4_include([ignfail.at])