瀏覽代碼

Fix extraction of symbolic links hardlinked to another symbolic links

* src/extract.c (create_placeholder_file): Take additional argument:
the delayed_link entry after which to the newly created one.
(extract_link): Create a placeholder file if the target link name
exists in the delayed_link list.
Sergey Poznyakoff 5 年之前
父節點
當前提交
2d3396c3ea
共有 1 個文件被更改,包括 65 次插入12 次删除
  1. 65 12
      src/extract.c

+ 65 - 12
src/extract.c

@@ -1283,10 +1283,15 @@ extract_file (char *file_name, int typeflag)
    replaced after other extraction is done by a symbolic link if
    IS_SYMLINK is true, and by a hard link otherwise.  Set
    *INTERDIR_MADE if an intermediate directory is made in the
-   process.  */
+   process.
+   Install the created struct delayed_link after PREV, unless the
+   latter is NULL, in which case insert it at the head of the delayed
+   link list.
+*/
 
 static int
-create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made)
+create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made,
+			 struct delayed_link *prev)
 {
   int fd;
   struct stat st;
@@ -1321,8 +1326,16 @@ create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made)
 	xmalloc (offsetof (struct delayed_link, target)
 		 + strlen (current_stat_info.link_name)
 		 + 1);
-      p->next = delayed_link_head;
-      delayed_link_head = p;
+      if (prev)
+	{
+	  p->next = prev->next;
+	  prev->next = p;
+	}
+      else
+	{
+	  p->next = delayed_link_head;
+	  delayed_link_head = p;
+	}
       p->dev = st.st_dev;
       p->ino = st.st_ino;
       p->birthtime = get_stat_birthtime (&st);
@@ -1337,7 +1350,7 @@ create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made)
 	}
       p->change_dir = chdir_current;
       p->sources = xmalloc (offsetof (struct string_list, string)
-			    + strlen (file_name) + 1);
+			     + strlen (file_name) + 1);
       p->sources->next = 0;
       strcpy (p->sources->string, file_name);
       p->cntx_name = NULL;
@@ -1346,7 +1359,8 @@ create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made)
       p->acls_a_len = 0;
       p->acls_d_ptr = NULL;
       p->acls_d_len = 0;
-      xheader_xattr_copy (&current_stat_info, &p->xattr_map, &p->xattr_map_size);
+      xheader_xattr_copy (&current_stat_info, &p->xattr_map,
+			  &p->xattr_map_size);
       strcpy (p->target, current_stat_info.link_name);
 
       if ((h = find_direct_ancestor (file_name)) != NULL)
@@ -1358,18 +1372,57 @@ create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made)
   return -1;
 }
 
+/* Find a delayed_link structure corresponding to the source NAME.
+   Such a structure exists in the delayed link list only if the link
+   placeholder file has been created. Therefore, try to stat the NAME
+   first. If it doesn't exist, there is no matching entry in the list.
+   Otherwise, look for the entry in list which has the matching dev
+   and ino numbers.
+   
+   This approach avoids scanning the singly-linked list in obvious cases
+   and does not rely on comparing file names, which may differ for
+   various reasons (e.g. relative vs. absolute file names).
+ */
+static struct delayed_link *
+find_delayed_link_source (char const *name)
+{
+  struct delayed_link *dl;
+  struct stat st;
+
+  if (!delayed_link_head)
+    return NULL;
+  
+  if (fstatat (chdir_fd, name, &st, AT_SYMLINK_NOFOLLOW))
+    {
+      if (errno != ENOENT)
+	stat_error (name);
+      return NULL;
+    }
+  
+  for (dl = delayed_link_head; dl; dl = dl->next)
+    {
+      if (dl->dev == st.st_dev && dl->ino == st.st_ino)
+	break;
+    }
+  return dl;
+}
+  
 static int
 extract_link (char *file_name, int typeflag)
 {
   bool interdir_made = false;
   char const *link_name;
   int rc;
-
+  struct delayed_link *dl;
+  
   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);
-
+    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);
+  
   do
     {
       struct stat st1, st2;
@@ -1431,7 +1484,7 @@ extract_symlink (char *file_name, int typeflag)
   if (! absolute_names_option
       && (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name)
 	  || contains_dot_dot (current_stat_info.link_name)))
-    return create_placeholder_file (file_name, true, &interdir_made);
+    return create_placeholder_file (file_name, true, &interdir_made, NULL);
 
   while (symlinkat (current_stat_info.link_name, chdir_fd, file_name) != 0)
     switch (maybe_recoverable (file_name, false, &interdir_made))
@@ -1814,8 +1867,8 @@ apply_delayed_links (void)
 	  sources = next;
 	}
 
-   xheader_xattr_free (ds->xattr_map, ds->xattr_map_size);
-   free (ds->cntx_name);
+      xheader_xattr_free (ds->xattr_map, ds->xattr_map_size);
+      free (ds->cntx_name);
 
       {
 	struct delayed_link *next = ds->next;