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;
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);
}
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;
p->acls_a_len = 0;
p->acls_d_ptr = NULL;
p->acls_d_len = 0;
- xheader_xattr_copy (¤t_stat_info, &p->xattr_map, &p->xattr_map_size);
+ xheader_xattr_copy (¤t_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)
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;
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))
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;