Browse Source

tar: extract delayed links in order

Extract delayed links in tar file order, rather than
in hash table order with modifications.
This is simpler and more likely to use the kernel’s
cached filesystem data, assuming related delayed links
are nearby in the tar file.
* src/extract.c (struct delayed_link.has_predecessor):
Remove.  All uses removed.
(delayed_link_head, delayed_link_tail): New static vars.
This resurrects delayed_link_head’s old function
except that the linked list is now in forward order, not reverse.
(find_delayed_link_source): Now simply returns bool,
since the callers no longer need the pointer.
(create_placeholder_file):
Put the delayed link at the end of the linked list.
Omit no-longer-needed last arg.  All callers changed.
(apply_delayed_links): Simplify now that we can just iterate
through the delayed_link_head list.
Paul Eggert 1 year ago
parent
commit
d6a60bba76
1 changed files with 25 additions and 55 deletions
  1. 25 55
      src/extract.c

+ 25 - 55
src/extract.c

@@ -137,9 +137,6 @@ struct delayed_link
        this delayed link.  */
        this delayed link.  */
     struct delayed_link *next;
     struct delayed_link *next;
 
 
-    /* Whether this delayed link has a predecessor in the NEXT list.  */
-    bool has_predecessor;
-
     /* The device, inode number and birthtime of the placeholder.
     /* The device, inode number and birthtime of the placeholder.
        birthtime.tv_nsec is negative if the birthtime is not available.
        birthtime.tv_nsec is negative if the birthtime is not available.
        Don't use mtime as this would allow for false matches if some
        Don't use mtime as this would allow for false matches if some
@@ -184,8 +181,14 @@ struct delayed_link
     char target[FLEXIBLE_ARRAY_MEMBER];
     char target[FLEXIBLE_ARRAY_MEMBER];
   };
   };
 
 
+/* Table of delayed links hashed by device and inode; null if none.  */
 static Hash_table *delayed_link_table;
 static Hash_table *delayed_link_table;
 
 
+/* A list of the delayed links in tar file order,
+   and the tail of that list.  */
+static struct delayed_link *delayed_link_head;
+static struct delayed_link **delayed_link_tail = &delayed_link_head;
+
 struct string_list
 struct string_list
   {
   {
     struct string_list *next;
     struct string_list *next;
@@ -1359,35 +1362,34 @@ extract_file (char *file_name, int typeflag)
   return status;
   return status;
 }
 }
 
 
-/* Find a delayed_link structure corresponding to the source NAME.
-   Such a structure exists in the delayed link table only if the link
+/* Return true if NAME is a delayed link.  This can happen only if the link
    placeholder file has been created. Therefore, try to stat the NAME
    placeholder file has been created. Therefore, try to stat the NAME
    first. If it doesn't exist, there is no matching entry in the table.
    first. If it doesn't exist, there is no matching entry in the table.
    Otherwise, look for the entry in the table that has the matching dev
    Otherwise, look for the entry in the table that has the matching dev
-   and ino numbers.  Return a null pointer if not found.
+   and ino numbers.  Return false if not found.
 
 
    Do not rely on comparing file names, which may differ for
    Do not rely on comparing file names, which may differ for
    various reasons (e.g. relative vs. absolute file names).
    various reasons (e.g. relative vs. absolute file names).
  */
  */
-static struct delayed_link *
+static bool
 find_delayed_link_source (char const *name)
 find_delayed_link_source (char const *name)
 {
 {
   struct stat st;
   struct stat st;
 
 
   if (!delayed_link_table)
   if (!delayed_link_table)
-    return NULL;
+    return false;
 
 
   if (fstatat (chdir_fd, name, &st, AT_SYMLINK_NOFOLLOW))
   if (fstatat (chdir_fd, name, &st, AT_SYMLINK_NOFOLLOW))
     {
     {
       if (errno != ENOENT)
       if (errno != ENOENT)
 	stat_error (name);
 	stat_error (name);
-      return NULL;
+      return false;
     }
     }
 
 
   struct delayed_link dl;
   struct delayed_link dl;
   dl.dev = st.st_dev;
   dl.dev = st.st_dev;
   dl.ino = st.st_ino;
   dl.ino = st.st_ino;
-  return hash_lookup (delayed_link_table, &dl);
+  return hash_lookup (delayed_link_table, &dl) != NULL;
 }
 }
 
 
 /* Create a placeholder file with name FILE_NAME, which will be
 /* Create a placeholder file with name FILE_NAME, which will be
@@ -1395,12 +1397,10 @@ find_delayed_link_source (char const *name)
    IS_SYMLINK is true, and by a hard link otherwise.  Set
    IS_SYMLINK is true, and by a hard link otherwise.  Set
    *INTERDIR_MADE if an intermediate directory is made in the
    *INTERDIR_MADE if an intermediate directory is made in the
    process.
    process.
-   If PREV, install the created struct delayed_link after PREV.
 */
 */
 
 
 static int
 static int
-create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made,
-			 MAYBE_UNUSED struct delayed_link *prev)
+create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made)
 {
 {
   int fd;
   int fd;
   struct stat st;
   struct stat st;
@@ -1443,18 +1443,7 @@ create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made,
       struct delayed_link *p =
       struct delayed_link *p =
 	xmalloc (FLEXNSIZEOF (struct delayed_link, target,
 	xmalloc (FLEXNSIZEOF (struct delayed_link, target,
 			      strlen (current_stat_info.link_name) + 1));
 			      strlen (current_stat_info.link_name) + 1));
-      if (prev)
-	{
-	  p->next = prev->next;
-	  prev->next = p;
-	  p->has_predecessor = true;
-	}
-      else
-	{
-	  p->next = NULL;
-	  p->has_predecessor = false;
-	}
-
+      p->next = NULL;
       p->dev = st.st_dev;
       p->dev = st.st_dev;
       p->ino = st.st_ino;
       p->ino = st.st_ino;
 #if HAVE_BIRTHTIME
 #if HAVE_BIRTHTIME
@@ -1484,6 +1473,8 @@ create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made,
       xattr_map_copy (&p->xattr_map, &current_stat_info.xattr_map);
       xattr_map_copy (&p->xattr_map, &current_stat_info.xattr_map);
       strcpy (p->target, current_stat_info.link_name);
       strcpy (p->target, current_stat_info.link_name);
 
 
+      *delayed_link_tail = p;
+      delayed_link_tail = &p->next;
       if (! ((delayed_link_table
       if (! ((delayed_link_table
 	      || (delayed_link_table = hash_initialize (0, 0, dl_hash,
 	      || (delayed_link_table = hash_initialize (0, 0, dl_hash,
 							dl_compare, free)))
 							dl_compare, free)))
@@ -1505,15 +1496,12 @@ extract_link (char *file_name, MAYBE_UNUSED int typeflag)
   bool interdir_made = false;
   bool interdir_made = false;
   char const *link_name;
   char const *link_name;
   int rc;
   int rc;
-  struct delayed_link *dl;
 
 
   link_name = current_stat_info.link_name;
   link_name = current_stat_info.link_name;
 
 
-  if (! absolute_names_option && contains_dot_dot (link_name))
-    return create_placeholder_file (file_name, false, &interdir_made, NULL);
-  dl = find_delayed_link_source (link_name);
-  if (dl)
-    return create_placeholder_file (file_name, false, &interdir_made, dl);
+  if ((! absolute_names_option && contains_dot_dot (link_name))
+      || find_delayed_link_source (link_name))
+    return create_placeholder_file (file_name, false, &interdir_made);
 
 
   do
   do
     {
     {
@@ -1578,7 +1566,7 @@ extract_symlink (char *file_name, MAYBE_UNUSED int typeflag)
   if (! absolute_names_option
   if (! absolute_names_option
       && (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name)
       && (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name)
 	  || contains_dot_dot (current_stat_info.link_name)))
 	  || contains_dot_dot (current_stat_info.link_name)))
-    return create_placeholder_file (file_name, true, &interdir_made, NULL);
+    return create_placeholder_file (file_name, true, &interdir_made);
 
 
   while (symlinkat (current_stat_info.link_name, chdir_fd, file_name) != 0)
   while (symlinkat (current_stat_info.link_name, chdir_fd, file_name) != 0)
     switch (maybe_recoverable (file_name, false, &interdir_made))
     switch (maybe_recoverable (file_name, false, &interdir_made))
@@ -1946,33 +1934,15 @@ apply_delayed_link (struct delayed_link *ds)
 static void
 static void
 apply_delayed_links (void)
 apply_delayed_links (void)
 {
 {
-  if (!delayed_link_table)
-    return;
-
-  for (struct delayed_link *dl = hash_get_first (delayed_link_table); dl;)
-    {
-      struct delayed_link *ds = dl;
-      if (!ds->has_predecessor)
-	{
-	  do
-	    {
-	      apply_delayed_link (ds);
-	      ds = ds->next;
-	    }
-	  while (ds);
-	}
-      else if (dl->next)
-	{
-	  dl = dl->next;
-	  continue;
-	}
-      dl = hash_get_next (delayed_link_table, dl);
-    }
+  for (struct delayed_link *ds = delayed_link_head; ds; ds = ds->next)
+    apply_delayed_link (ds);
 
 
   if (false)
   if (false)
     {
     {
       /* There is little point to freeing, as we are about to exit,
       /* There is little point to freeing, as we are about to exit,
-	 and freeing is more likely to cause than cure trouble.  */
+	 and freeing is more likely to cause than cure trouble.
+	 Also, the above code has not bothered to free the list
+	 in delayed_link_head.  */
       hash_free (delayed_link_table);
       hash_free (delayed_link_table);
       delayed_link_table = NULL;
       delayed_link_table = NULL;
     }
     }