浏览代码

Rewrite update algorithm.

* src/common.h (namebuf_t): New typedef.
(namebuf_create, namebuf_free)
(namebuf_name): New prototypes.
(remname): New prototype.
* src/misc.c (struct namebuf): New structure.
(namebuf_create, namebuf_free)
(namebuf_name): New functions.
* src/create.c (dup_dir0): Remove is_avoided_name
checks. This is taken care of in update_archive.
* src/incremen.c (scan_directory): Use namebuf
to produce full file names.
* src/names.c (nametail): Remove extra level of
indirection. All uses updated.
(avoided_name_table, add_avoided_name)
(is_avoided_name): Remove.
* src/update.c (update_archive): Change algorithm.
Instead of adding unmodified files to the avoided_name
table, create namelist so that it contains only
modified files.

* tests/Makefile.am: Add update01.at, update02.at
* tests/testsuite.at: Likewise.
* tests/update.at (AT_KEYWORDS): Add update00.
Sergey Poznyakoff 15 年之前
父节点
当前提交
cac45fffc5
共有 11 个文件被更改,包括 283 次插入133 次删除
  1. 6 0
      src/common.h
  2. 54 60
      src/create.c
  3. 18 33
      src/incremen.c
  4. 41 0
      src/misc.c
  5. 17 33
      src/names.c
  6. 28 6
      src/update.c
  7. 2 0
      tests/Makefile.am
  8. 2 0
      tests/testsuite.at
  9. 2 1
      tests/update.at
  10. 58 0
      tests/update01.at
  11. 55 0
      tests/update02.at

+ 6 - 0
src/common.h

@@ -596,6 +596,11 @@ char *normalize_filename (const char *name);
 void replace_prefix (char **pname, const char *samp, size_t slen,
 		     const char *repl, size_t rlen);
 
+typedef struct namebuf *namebuf_t;
+namebuf_t namebuf_create (const char *dir);
+void namebuf_free (namebuf_t buf);
+char *namebuf_name (namebuf_t buf, const char *name);
+
 void code_ns_fraction (int ns, char *p);
 char const *code_timespec (struct timespec ts, char *sbuf);
 enum { BILLION = 1000000000, LOG10_BILLION = 9 };
@@ -666,6 +671,7 @@ const char *name_next (int change_dirs);
 void name_gather (void);
 struct name *addname (char const *string, int change_dir,
 		      bool cmdline, struct name *parent);
+void remname (struct name *name);
 bool name_match (const char *name);
 void names_notfound (void);
 void collect_and_sort_names (void);

+ 54 - 60
src/create.c

@@ -1096,73 +1096,70 @@ dump_dir0 (char *directory,
 {
   dev_t our_device = st->stat.st_dev;
   const char *tag_file_name;
-  
-  if (!is_avoided_name (st->orig_file_name))
-    {
-      union block *blk = NULL;
-      off_t block_ordinal = current_block_ordinal ();
-      st->stat.st_size = 0;	/* force 0 size on dir */
+  union block *blk = NULL;
+  off_t block_ordinal = current_block_ordinal ();
 
-      blk = start_header (st);
-      if (!blk)
-	return;
+  st->stat.st_size = 0;	/* force 0 size on dir */
+
+  blk = start_header (st);
+  if (!blk)
+    return;
 
-      if (incremental_option && archive_format != POSIX_FORMAT)
-	blk->header.typeflag = GNUTYPE_DUMPDIR;
-      else /* if (standard_option) */
-	blk->header.typeflag = DIRTYPE;
+  if (incremental_option && archive_format != POSIX_FORMAT)
+    blk->header.typeflag = GNUTYPE_DUMPDIR;
+  else /* if (standard_option) */
+    blk->header.typeflag = DIRTYPE;
 
-      /* If we're gnudumping, we aren't done yet so don't close it.  */
+  /* If we're gnudumping, we aren't done yet so don't close it.  */
 
-      if (!incremental_option)
-	finish_header (st, blk, block_ordinal);
-      else if (gnu_list_name->directory)
+  if (!incremental_option)
+    finish_header (st, blk, block_ordinal);
+  else if (gnu_list_name->directory)
+    {
+      if (archive_format == POSIX_FORMAT)
 	{
-	  if (archive_format == POSIX_FORMAT)
-	    {
-	      xheader_store ("GNU.dumpdir", st,
-			     safe_directory_contents (gnu_list_name->directory));
-	      finish_header (st, blk, block_ordinal);
-	    }
-	  else
+	  xheader_store ("GNU.dumpdir", st,
+			 safe_directory_contents (gnu_list_name->directory));
+	  finish_header (st, blk, block_ordinal);
+	}
+      else
+	{
+	  off_t size_left;
+	  off_t totsize;
+	  size_t bufsize;
+	  ssize_t count;
+	  const char *buffer, *p_buffer;
+	  
+	  block_ordinal = current_block_ordinal ();
+	  buffer = safe_directory_contents (gnu_list_name->directory);
+	  totsize = dumpdir_size (buffer);
+	  OFF_TO_CHARS (totsize, blk->header.size);
+	  finish_header (st, blk, block_ordinal);
+	  p_buffer = buffer;
+	  size_left = totsize;
+	  
+	  mv_begin (st);
+	  mv_total_size (totsize);
+	  while (size_left > 0)
 	    {
-	      off_t size_left;
-	      off_t totsize;
-	      size_t bufsize;
-	      ssize_t count;
-	      const char *buffer, *p_buffer;
-
-	      block_ordinal = current_block_ordinal ();
-	      buffer = safe_directory_contents (gnu_list_name->directory);
-	      totsize = dumpdir_size (buffer);
-	      OFF_TO_CHARS (totsize, blk->header.size);
-	      finish_header (st, blk, block_ordinal);
-	      p_buffer = buffer;
-	      size_left = totsize;
-
-	      mv_begin (st);
-	      mv_total_size (totsize);
-	      while (size_left > 0)
+	      mv_size_left (size_left);
+	      blk = find_next_block ();
+	      bufsize = available_space_after (blk);
+	      if (size_left < bufsize)
 		{
-		  mv_size_left (size_left);
-		  blk = find_next_block ();
-		  bufsize = available_space_after (blk);
-		  if (size_left < bufsize)
-		    {
-		      bufsize = size_left;
-		      count = bufsize % BLOCKSIZE;
-		      if (count)
-			memset (blk->buffer + size_left, 0, BLOCKSIZE - count);
-		    }
-		  memcpy (blk->buffer, p_buffer, bufsize);
-		  size_left -= bufsize;
-		  p_buffer += bufsize;
-		  set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE);
+		  bufsize = size_left;
+		  count = bufsize % BLOCKSIZE;
+		  if (count)
+		    memset (blk->buffer + size_left, 0, BLOCKSIZE - count);
 		}
-	      mv_end ();
+	      memcpy (blk->buffer, p_buffer, bufsize);
+	      size_left -= bufsize;
+	      p_buffer += bufsize;
+	      set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE);
 	    }
-	  return;
+	  mv_end ();
 	}
+      return;
     }
 
   if (!recursion_option)
@@ -1557,9 +1554,6 @@ dump_file0 (struct tar_stat_info *st, const char *p,
       return;
     }
 
-  if (is_avoided_name (p))
-    return;
-
   is_dir = S_ISDIR (st->stat.st_mode) != 0;
 
   if (!is_dir && dump_hard_link (st))

+ 18 - 33
src/incremen.c

@@ -692,9 +692,8 @@ struct directory *
 scan_directory (char *dir, dev_t device, bool cmdline)
 {
   char *dirp = savedir (dir);	/* for scanning directory */
-  char *name_buffer;		/* directory, `/', and directory member */
-  size_t name_buffer_size;	/* allocated size of name_buffer, minus 2 */
-  size_t name_length;		/* used length in name_buffer */
+  namebuf_t nbuf;
+  char *tmp;
   struct stat stat_data;
   struct directory *directory;
   char ch;
@@ -702,35 +701,28 @@ scan_directory (char *dir, dev_t device, bool cmdline)
   if (! dirp)
     savedir_error (dir);
 
-  name_buffer_size = strlen (dir) + NAME_FIELD_SIZE;
-  name_buffer = xmalloc (name_buffer_size + 2);
-  strcpy (name_buffer, dir);
-  zap_slashes (name_buffer);
+  tmp = xstrdup (dir);
+  zap_slashes (tmp);
   
-  if (deref_stat (dereference_option, name_buffer, &stat_data))
+  if (deref_stat (dereference_option, tmp, &stat_data))
     {
-      dir_removed_diag (name_buffer, cmdline, stat_diag);
-      free (name_buffer);
+      dir_removed_diag (tmp, cmdline, stat_diag);
+      free (tmp);
       free (dirp);
       return NULL;
     }
 
-  directory = procdir (name_buffer, &stat_data, device,
+  directory = procdir (tmp, &stat_data, device,
 		       (cmdline ? PD_FORCE_INIT : 0),
 		       &ch);
   
-  name_length = strlen (name_buffer); 
-  if (! ISSLASH (name_buffer[name_length - 1]))
-    {
-      name_buffer[name_length] = DIRECTORY_SEPARATOR;
-      /* name_buffer has been allocated an extra slot */
-      name_buffer[++name_length] = 0;
-    }
+  free (tmp);
+
+  nbuf = namebuf_create (dir);
 
   if (dirp && directory->children != NO_CHILDREN)
     {
       char *entry;	/* directory entry being scanned */
-      size_t entrylen;	/* length of directory entry */
       dumpdir_iter_t itr;
 
       makedumpdir (directory, dirp);
@@ -739,25 +731,17 @@ scan_directory (char *dir, dev_t device, bool cmdline)
 	   entry;
 	   entry = dumpdir_next (itr))
 	{
-	  entrylen = strlen (entry);
-	  if (name_buffer_size <= entrylen - 1 + name_length)
-	    {
-	      do
-		name_buffer_size += NAME_FIELD_SIZE;
-	      while (name_buffer_size <= entrylen - 1 + name_length);
-	      name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
-	    }
-	  strcpy (name_buffer + name_length, entry + 1);
+	  char *full_name = namebuf_name (nbuf, entry + 1);
 
 	  if (*entry == 'I') /* Ignored entry */
 	    *entry = 'N';
-	  else if (excluded_name (name_buffer))
+	  else if (excluded_name (full_name))
 	    *entry = 'N';
 	  else
 	    {
-	      if (deref_stat (dereference_option, name_buffer, &stat_data))
+	      if (deref_stat (dereference_option, full_name, &stat_data))
 		{
-		  file_removed_diag (name_buffer, false, stat_diag);
+		  file_removed_diag (full_name, false, stat_diag);
 		  *entry = 'N';
 		  continue;
 		}
@@ -770,7 +754,7 @@ scan_directory (char *dir, dev_t device, bool cmdline)
 		  else if (directory->children == ALL_CHILDREN)
 		    pd_flag |= PD_FORCE_CHILDREN | ALL_CHILDREN;
 		  *entry = 'D';
-		  procdir (name_buffer, &stat_data, device, pd_flag, entry);
+		  procdir (full_name, &stat_data, device, pd_flag, entry);
 		}
 
 	      else if (one_file_system_option && device != stat_data.st_dev)
@@ -792,7 +776,8 @@ scan_directory (char *dir, dev_t device, bool cmdline)
       free (itr);
     }
 
-  free (name_buffer);
+  namebuf_free (nbuf);
+
   if (dirp)
     free (dirp);
 

+ 41 - 0
src/misc.c

@@ -827,3 +827,44 @@ page_aligned_alloc (void **ptr, size_t size)
   return ptr_align (*ptr, alignment);
 }
 
+
+
+struct namebuf
+{
+  char *buffer;		/* directory, `/', and directory member */
+  size_t buffer_size;	/* allocated size of name_buffer */
+  size_t dir_length;	/* length of directory part in buffer */
+};
+
+namebuf_t
+namebuf_create (const char *dir)
+{
+  namebuf_t buf = xmalloc (sizeof (*buf));
+  buf->buffer_size = strlen (dir) + 2;
+  buf->buffer = xmalloc (buf->buffer_size);
+  strcpy (buf->buffer, dir);
+  buf->dir_length = strlen (buf->buffer);
+  if (!ISSLASH (buf->buffer[buf->dir_length - 1]))
+    buf->buffer[buf->dir_length++] = DIRECTORY_SEPARATOR;
+  return buf;
+}
+
+void
+namebuf_free (namebuf_t buf)
+{
+  free (buf->buffer);
+  free (buf);
+}
+
+char *
+namebuf_name (namebuf_t buf, const char *name)
+{
+  size_t len = strlen (name);
+  while (buf->dir_length + len + 1 >= buf->buffer_size)
+    buf->buffer = x2realloc (buf->buffer, &buf->buffer_size);
+  strcpy (buf->buffer + buf->dir_length, name);
+  return buf->buffer;
+}
+
+
+  

+ 17 - 33
src/names.c

@@ -205,7 +205,7 @@ free_name (struct name *p)
 /* Names from the command call.  */
 
 static struct name *namelist;	/* first name in list, if any */
-static struct name **nametail = &namelist;	/* end of name list */
+static struct name *nametail;	/* end of name list */
 
 /* File name arguments are processed in two stages: first a 
    name_array (see below) is filled, then the names from it
@@ -422,8 +422,7 @@ name_gather (void)
 	  buffer->parent = NULL;
 	  buffer->cmdline = true;
 	  
-	  namelist = buffer;
-	  nametail = &namelist->next;
+	  namelist = nametail = buffer;
 	}
       else if (change_dir)
 	addname (0, change_dir, false, NULL);
@@ -457,7 +456,7 @@ addname (char const *string, int change_dir, bool cmdline, struct name *parent)
 {
   struct name *name = make_name (string);
 
-  name->prev = *nametail;
+  name->prev = nametail;
   name->next = NULL;
   name->found_count = 0;
   name->matching_flags = matching_flags;
@@ -465,9 +464,12 @@ addname (char const *string, int change_dir, bool cmdline, struct name *parent)
   name->directory = NULL;
   name->parent = parent;
   name->cmdline = cmdline;
-  
-  *nametail = name;
-  nametail = &name->next;
+
+  if (nametail)
+    nametail->next = name;
+  else
+    namelist = name;
+  nametail = name;
   return name;
 }
 
@@ -501,7 +503,7 @@ remname (struct name *name)
   if ((p = name->next) != NULL)
     p->prev = name->prev;
   else
-    nametail = &name->prev;
+    nametail = name->prev;
 }
 
 /* Return true if and only if name FILE_NAME (from an archive) matches any
@@ -521,8 +523,8 @@ name_match (const char *file_name)
       if (cursor->name[0] == 0)
 	{
 	  chdir_do (cursor->change_dir);
-	  namelist = 0;
-	  nametail = &namelist;
+	  namelist = NULL;
+	  nametail = NULL;
 	  return true;
 	}
 
@@ -535,8 +537,8 @@ name_match (const char *file_name)
 	  if (starting_file_option)
 	    {
 	      free (namelist);
-	      namelist = 0;
-	      nametail = &namelist;
+	      namelist = NULL;
+	      nametail = NULL;
 	    }
 	  chdir_do (cursor->change_dir);
 
@@ -627,8 +629,8 @@ names_notfound (void)
       }
 
   /* Don't bother freeing the name list; we're about to exit.  */
-  namelist = 0;
-  nametail = &namelist;
+  namelist = NULL;
+  nametail = NULL;
 
   if (same_order_option)
     {
@@ -975,7 +977,7 @@ collect_and_sort_names (void)
       prev_name = name;
       num_names++;
     }
-  nametail = &prev_name;
+  nametail = prev_name;
   hash_free (nametab);
 
   namelist = merge_sort (namelist, num_names, compare_names_found);
@@ -1074,24 +1076,6 @@ excluded_name (char const *name)
 {
   return excluded_file_name (excluded, name + FILE_SYSTEM_PREFIX_LEN (name));
 }
-
-/* Names to avoid dumping.  */
-static Hash_table *avoided_name_table;
-
-/* Remember to not archive NAME.  */
-void
-add_avoided_name (char const *name)
-{
-  hash_string_insert (&avoided_name_table, name);
-}
-
-/* Should NAME be avoided when archiving?  */
-bool
-is_avoided_name (char const *name)
-{
-  return hash_string_lookup (avoided_name_table, name);
-}
-
 
 static Hash_table *individual_file_table;
 

+ 28 - 6
src/update.c

@@ -137,13 +137,35 @@ update_archive (void)
 
 		chdir_do (name->change_dir);
 		if (deref_stat (dereference_option,
-				current_stat_info.file_name, &s) == 0
-		    && (tar_timespec_cmp (get_stat_mtime (&s),
-				          current_stat_info.mtime)
-			<= 0))
-		  add_avoided_name (current_stat_info.file_name);
+				current_stat_info.file_name, &s) == 0)
+		  {
+		    if (S_ISDIR (s.st_mode))
+		      {
+			char *p, *dirp;
+			dirp = savedir (name->name);
+			if (!dirp)
+			  savedir_error (name->name);
+			else
+			  {
+			    namebuf_t nbuf = namebuf_create (name->name);
+			    
+			    for (p = dirp; *p; p += strlen (p) + 1)
+			      addname (namebuf_name (nbuf, p),
+				       0, false, NULL);
+			    
+			    namebuf_free (nbuf);
+			    free (dirp);
+			    
+			    remname (name);
+			  }
+		      }
+		    else if (tar_timespec_cmp (get_stat_mtime (&s),
+					       current_stat_info.mtime)
+			     <= 0)
+		      remname (name);
+		  }
 	      }
-
+	    
 	    skip_member ();
 	    break;
 	  }

+ 2 - 0
tests/Makefile.am

@@ -130,6 +130,8 @@ TESTSUITE_AT = \
  spmvp10.at\
  truncate.at\
  update.at\
+ update01.at\
+ update02.at\
  volsize.at\
  volume.at\
  verbose.at\

+ 2 - 0
tests/testsuite.at

@@ -207,6 +207,8 @@ m4_include([spmvp01.at])
 m4_include([spmvp10.at])
 
 m4_include([update.at])
+m4_include([update01.at])
+m4_include([update02.at])
 
 m4_include([volume.at])
 m4_include([volsize.at])

+ 2 - 1
tests/update.at

@@ -23,9 +23,10 @@
 # References: <[email protected]>
 #             by Martin Lohmeier <[email protected]>
 #             on Sat, 11 Jun 2005 18:11:20 +0200
+#             http://lists.gnu.org/archive/html/bug-tar/2005-06/msg00024.html
 
 AT_SETUP([update unchanged directories])
-AT_KEYWORDS([update])
+AT_KEYWORDS([update update00])
 
 AT_TAR_CHECK([
 AT_SORT_PREREQ

+ 58 - 0
tests/update01.at

@@ -0,0 +1,58 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+
+# Test suite for GNU tar.
+# Copyright (C) 2009 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: If dir is a directory and arc is a tar archive which
+# contains that directory, and dir contains some modifications added
+# after adding it to the archive, then `tar -u dir' would add dir/ to
+# the archive.
+# Last-Affected-Version: 1.22.90
+# References: <[email protected]>
+#             http://lists.gnu.org/archive/html/bug-tar/2009-10/msg00017.html
+
+AT_SETUP([update directories])
+AT_KEYWORDS([update update01])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir a
+genfile --file a/b
+
+tar cf arc a
+
+echo "separator"
+
+sleep 2
+genfile --file a/c
+
+tar ufv arc a
+echo "separator"
+tar tf arc | sort || exit 1
+],
+[0],
+[separator
+a/c
+separator
+a/
+a/b
+a/c
+])
+
+AT_CLEANUP
+

+ 55 - 0
tests/update02.at

@@ -0,0 +1,55 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+
+# Test suite for GNU tar.
+# Copyright (C) 2009 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: See update01.at
+# Last-Affected-Version: 1.22.90
+# References: <[email protected]>
+#             http://lists.gnu.org/archive/html/bug-tar/2009-10/msg00017.html
+
+AT_SETUP([update changed files])
+AT_KEYWORDS([update update02])
+
+AT_TAR_CHECK([
+AT_SORT_PREREQ
+mkdir a
+genfile --file a/b
+
+tar cf arc a
+
+echo "separator"
+
+sleep 2
+touch a/b
+
+tar ufv arc a
+echo "separator"
+tar tf arc | sort || exit 1
+],
+[0],
+[separator
+a/b
+separator
+a/
+a/b
+a/b
+])
+
+AT_CLEANUP
+