Hi Nathan,

> Does anyone know of a reason that tar needs to perform the file-changed
> check on directories that it has skipped due to --one-file-system...?

There's no compelling reason, it's just an oversight. Please try
the attached patch.

Regards,
Sergey

PS: I'm going to release v.1.30 today. It looks a bit risky to apply
a patch of this size to about-to-be-released code, so I'm going to
postpone it until it is well tested.

>From e08d7825df0967edf5ea31fdb2a0269a3368f594 Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <g...@gnu.org>
Date: Sun, 17 Dec 2017 12:04:13 +0200
Subject: [PATCH] Don't do post-dumping checks on files that have been skipped.

* src/common.h (dump_status_skip): New status code.
* src/create.c (dump_dir0): Return enum dump_status.
(dump_dir): Likewise.
(dump_file0): Don't do additional checks (timestamp, etc)
if the file has been skipped.
---
 src/common.h |  3 ++-
 src/create.c | 63 ++++++++++++++++++++++++++++++++++--------------------------
 2 files changed, 38 insertions(+), 28 deletions(-)

diff --git a/src/common.h b/src/common.h
index bbe167e..275252d 100644
--- a/src/common.h
+++ b/src/common.h
@@ -492,7 +492,8 @@ enum dump_status
     dump_status_ok,
     dump_status_short,
     dump_status_fail,
-    dump_status_not_implemented
+    dump_status_not_implemented,
+    dump_status_skip
   };

 void add_exclusion_tag (const char *name, enum exclusion_tag_type type,
diff --git a/src/create.c b/src/create.c
index 35bcf5b..1537fb4 100644
--- a/src/create.c
+++ b/src/create.c
@@ -1119,9 +1119,11 @@ dump_regular_file (int fd, struct tar_stat_info *st)

 
 /* Copy info from the directory identified by ST into the archive.
-   DIRECTORY contains the directory's entries.  */
+   DIRECTORY contains the directory's entries. Return dump_status_ok
+   on success, dump_status_skip if the directory was not dumped, and
+   dump_status_fail on error. */

-static void
+static enum dump_status
 dump_dir0 (struct tar_stat_info *st, char const *directory)
 {
   bool top_level = ! st->parent;
@@ -1133,7 +1135,7 @@ dump_dir0 (struct tar_stat_info *st, char const *directory)

   blk = start_header (st);
   if (!blk)
-    return;
+    return dump_status_fail;

   info_attach_exclist (st);

@@ -1188,11 +1190,11 @@ dump_dir0 (struct tar_stat_info *st, char const *directory)
 	      set_next_block_after (blk + (bufsize - 1) / BLOCKSIZE);
 	    }
 	}
-      return;
+      return dump_status_ok;
     }

   if (!recursion_option)
-    return;
+    return dump_status_skip;

   if (one_file_system_option
       && !top_level
@@ -1203,6 +1205,7 @@ dump_dir0 (struct tar_stat_info *st, char const *directory)
 		 (0, 0,
 		  _("%s: file is on a different filesystem; not dumped"),
 		  quotearg_colon (st->orig_file_name)));
+      return dump_status_skip;
     }
   else
     {
@@ -1259,6 +1262,7 @@ dump_dir0 (struct tar_stat_info *st, char const *directory)
 	  break;
 	}
     }
+  return dump_status_ok;
 }

 /* Ensure exactly one trailing slash.  */
@@ -1315,24 +1319,28 @@ get_directory_entries (struct tar_stat_info *st)
   return streamsavedir (st->dirstream, savedir_sort_order);
 }

-/* Dump the directory ST.  Return true if successful, false (emitting
-   diagnostics) otherwise.  Get ST's entries, recurse through its
-   subdirectories, and clean up file descriptors afterwards.  */
-static bool
+/* Dump the directory ST.  Return dump_status_ok if successful,
+   dump_status_skip if the directory was not eligible for dumping (e.g.
+   located on another file system while --one-file-system is in effect), and
+   dump_status_fail (emitting diagnostics) on error.  Get ST's entries,
+   recurse through its subdirectories, and clean up file descriptors
+   afterwards.  */
+static enum dump_status
 dump_dir (struct tar_stat_info *st)
 {
+  enum dump_status status;
   char *directory = get_directory_entries (st);
   if (! directory)
     {
       savedir_diag (st->orig_file_name);
-      return false;
+      return dump_status_fail;
     }

-  dump_dir0 (st, directory);
+  status = dump_dir0 (st, directory);

   restore_parent_fd (st);
   free (directory);
-  return true;
+  return status;
 }

 
@@ -1755,7 +1763,7 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)

   if (is_dir || S_ISREG (st->stat.st_mode) || S_ISCTG (st->stat.st_mode))
     {
-      bool ok;
+      enum dump_status status;
       struct stat final_stat;

       xattrs_acls_get (parentfd, name, st, 0, !is_dir);
@@ -1775,15 +1783,13 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
 	      return;
 	    }

-	  ok = dump_dir (st);
+	  status = dump_dir (st);

 	  fd = st->fd;
 	  parentfd = top_level ? chdir_fd : parent->fd;
 	}
       else
 	{
-	  enum dump_status status;
-
 	  if (fd && sparse_option && ST_IS_SPARSE (st->stat))
 	    {
 	      status = sparse_dump_file (fd, st);
@@ -1801,40 +1807,42 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
 	      break;

 	    case dump_status_fail:
+	    case dump_status_skip:
 	      break;

 	    case dump_status_not_implemented:
 	      abort ();
 	    }
-
-	  ok = status == dump_status_ok;
 	}

-      if (ok)
+      if (status == dump_status_ok)
 	{
 	  if (fd < 0)
 	    {
 	      errno = - fd;
-	      ok = false;
+	      status = dump_status_fail;
 	    }
 	  else if (fd == 0)
 	    {
 	      if (parentfd < 0 && ! top_level)
 		{
 		  errno = - parentfd;
-		  ok = false;
+		  status = dump_status_fail;
 		}
 	      else
-		ok = fstatat (parentfd, name, &final_stat, fstatat_flags) == 0;
+		status =
+		  fstatat (parentfd, name, &final_stat, fstatat_flags) == 0
+		  ? dump_status_ok : dump_status_fail;
 	    }
 	  else
-	    ok = fstat (fd, &final_stat) == 0;
+	    status = fstat (fd, &final_stat) == 0
+	               ? dump_status_ok : dump_status_fail;

-	  if (! ok)
+	  if (status == dump_status_fail)
 	    file_removed_diag (p, top_level, stat_diag);
 	}

-      if (ok)
+      if (status == dump_status_ok)
 	{
 	  if ((timespec_cmp (get_stat_ctime (&final_stat), original_ctime) != 0
 	       /* Original ctime will change if the file is a directory and
@@ -1853,8 +1861,9 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
 	    utime_error (p);
 	}

-      ok &= tar_stat_close (st);
-      if (ok && remove_files_option)
+      if (! tar_stat_close (st))
+	status = dump_status_fail;
+      if (status == dump_status_ok && remove_files_option)
 	queue_deferred_unlink (p, is_dir);

       return;
--
1.8.4

Reply via email to