/* List of directories whose statuses we need to extract after we've
finished extracting their subsidiary files. If you consider each
contiguous subsequence of elements of the form [D]?[^D]*, where [D]
- represents an element where AFTER_SYMLINKS is nonzero and [^D]
- represents an element where AFTER_SYMLINKS is zero, then the head
+ represents an element where AFTER_LINKS is nonzero and [^D]
+ represents an element where AFTER_LINKS is zero, then the head
of the subsequence has the longest name, and each non-head element
in the prefix is an ancestor (in the directory hierarchy) of the
preceding element. */
size_t file_name_len;
mode_t invert_permissions;
enum permstatus permstatus;
- bool after_symlinks;
+ bool after_links;
char file_name[1];
};
static struct delayed_set_stat *delayed_set_stat_head;
-/* List of symbolic links whose creation we have delayed. */
-struct delayed_symlink
+/* List of links whose creation we have delayed. */
+struct delayed_link
{
- /* The next delayed symbolic link in the list. */
- struct delayed_symlink *next;
+ /* The next delayed link in the list. */
+ struct delayed_link *next;
/* The device, inode number and last-modified time of the placeholder. */
dev_t dev;
ino_t ino;
time_t mtime;
- /* The desired owner and group of the symbolic link. */
+ /* True if the link is symbolic. */
+ bool is_symlink;
+
+ /* The desired owner and group of the link, if it is a symlink. */
uid_t uid;
gid_t gid;
- /* A list of sources for this symlink. The sources are all to be
+ /* A list of sources for this link. The sources are all to be
hard-linked together. */
struct string_list *sources;
char target[1];
};
-static struct delayed_symlink *delayed_symlink_head;
+static struct delayed_link *delayed_link_head;
struct string_list
{
strcpy (data->file_name, file_name);
data->invert_permissions = invert_permissions;
data->permstatus = permstatus;
- data->after_symlinks = 0;
+ data->after_links = 0;
data->stat_info = *stat_info;
data->next = delayed_set_stat_head;
delayed_set_stat_head = data;
quotearg_colon (dir)));
}
-/* After a file/link/symlink/directory creation has failed, see if
+/* After a file/link/directory creation has failed, see if
it's because some required directory was not present, and if so,
create all required directories. Return non-zero if a directory
was created. */
}
/* Fix the statuses of all directories whose statuses need fixing, and
- which are not ancestors of FILE_NAME. If AFTER_SYMLINKS is
+ which are not ancestors of FILE_NAME. If AFTER_LINKS is
nonzero, do this for all such directories; otherwise, stop at the
first directory that is marked to be fixed up only after delayed
- symlinks are applied. */
+ links are applied. */
static void
-apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks)
+apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links)
{
size_t file_name_len = strlen (file_name);
bool check_for_renamed_directories = 0;
struct stat st;
struct stat const *cur_info = 0;
- check_for_renamed_directories |= data->after_symlinks;
+ check_for_renamed_directories |= data->after_links;
- if (after_symlinks < data->after_symlinks
+ if (after_links < data->after_links
|| (data->file_name_len < file_name_len
&& file_name[data->file_name_len]
&& (ISSLASH (file_name[data->file_name_len])
return status;
}
+/* Create a placeholder file with name FILE_NAME, which will be
+ 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. */
+
+static int
+create_placeholder_file (char *file_name, bool is_symlink, int *interdir_made)
+{
+ int fd;
+ struct stat st;
+
+ while ((fd = open (file_name, O_WRONLY | O_CREAT | O_EXCL, 0)) < 0)
+ if (! maybe_recoverable (file_name, interdir_made))
+ break;
+
+ if (fd < 0)
+ open_error (file_name);
+ else if (fstat (fd, &st) != 0)
+ {
+ stat_error (file_name);
+ close (fd);
+ }
+ else if (close (fd) != 0)
+ close_error (file_name);
+ else
+ {
+ struct delayed_set_stat *h;
+ struct delayed_link *p =
+ xmalloc (offsetof (struct delayed_link, target)
+ + strlen (current_stat_info.link_name)
+ + 1);
+ p->next = delayed_link_head;
+ delayed_link_head = p;
+ p->dev = st.st_dev;
+ p->ino = st.st_ino;
+ p->mtime = st.st_mtime;
+ p->is_symlink = is_symlink;
+ if (is_symlink)
+ {
+ p->uid = current_stat_info.stat.st_uid;
+ p->gid = current_stat_info.stat.st_gid;
+ }
+ p->sources = xmalloc (offsetof (struct string_list, string)
+ + strlen (file_name) + 1);
+ p->sources->next = 0;
+ strcpy (p->sources->string, file_name);
+ strcpy (p->target, current_stat_info.link_name);
+
+ h = delayed_set_stat_head;
+ if (h && ! h->after_links
+ && strncmp (file_name, h->file_name, h->file_name_len) == 0
+ && ISSLASH (file_name[h->file_name_len])
+ && (base_name (file_name) == file_name + h->file_name_len + 1))
+ {
+ do
+ {
+ h->after_links = 1;
+
+ if (stat (h->file_name, &st) != 0)
+ stat_error (h->file_name);
+ else
+ {
+ h->stat_info.st_dev = st.st_dev;
+ h->stat_info.st_ino = st.st_ino;
+ }
+ }
+ while ((h = h->next) && ! h->after_links);
+ }
+
+ return 0;
+ }
+
+ return -1;
+}
+
static int
extract_link (char *file_name, int typeflag)
{
true, absolute_names_option);
int interdir_made = 0;
+ if (! absolute_names_option && contains_dot_dot (link_name))
+ return create_placeholder_file (file_name, false, &interdir_made);
+
do
{
struct stat st1, st2;
if (status == 0)
{
- struct delayed_symlink *ds = delayed_symlink_head;
+ struct delayed_link *ds = delayed_link_head;
if (ds && lstat (link_name, &st1) == 0)
for (; ds; ds = ds->next)
if (ds->dev == st1.st_dev
extract_symlink (char *file_name, int typeflag)
{
#ifdef HAVE_SYMLINK
- int status, fd;
+ int status;
int interdir_made = 0;
- if (absolute_names_option
- || ! (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name)
- || contains_dot_dot (current_stat_info.link_name)))
- {
- while ((status = symlink (current_stat_info.link_name, file_name)))
- if (!maybe_recoverable (file_name, &interdir_made))
- break;
-
- if (status == 0)
- set_stat (file_name, ¤t_stat_info.stat, 0, 0, 0, SYMTYPE);
- else
- symlink_error (current_stat_info.link_name, file_name);
- }
- else
- {
- /* This symbolic link is potentially dangerous. Don't
- create it now; instead, create a placeholder file, which
- will be replaced after other extraction is done. */
- struct stat st;
-
- while ((fd = open (file_name, O_WRONLY | O_CREAT | O_EXCL, 0)) < 0)
- if (! maybe_recoverable (file_name, &interdir_made))
- break;
-
- status = -1;
- if (fd < 0)
- open_error (file_name);
- else if (fstat (fd, &st) != 0)
- {
- stat_error (file_name);
- close (fd);
- }
- else if (close (fd) != 0)
- close_error (file_name);
- else
- {
- struct delayed_set_stat *h;
- struct delayed_symlink *p = xmalloc (offsetof (struct delayed_symlink, target)
- + strlen (current_stat_info.link_name)
- + 1);
- p->next = delayed_symlink_head;
- delayed_symlink_head = p;
- p->dev = st.st_dev;
- p->ino = st.st_ino;
- p->mtime = st.st_mtime;
- p->uid = current_stat_info.stat.st_uid;
- p->gid = current_stat_info.stat.st_gid;
- p->sources = xmalloc (offsetof (struct string_list, string)
- + strlen (file_name) + 1);
- p->sources->next = 0;
- strcpy (p->sources->string, file_name);
- strcpy (p->target, current_stat_info.link_name);
-
- h = delayed_set_stat_head;
- if (h && ! h->after_symlinks
- && strncmp (file_name, h->file_name, h->file_name_len) == 0
- && ISSLASH (file_name[h->file_name_len])
- && (base_name (file_name) == file_name + h->file_name_len + 1))
- {
- do
- {
- h->after_symlinks = 1;
-
- if (stat (h->file_name, &st) != 0)
- stat_error (h->file_name);
- else
- {
- h->stat_info.st_dev = st.st_dev;
- h->stat_info.st_ino = st.st_ino;
- }
- }
- while ((h = h->next) && ! h->after_symlinks);
- }
+ 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);
- status = 0;
- }
- }
+ while ((status = symlink (current_stat_info.link_name, file_name)))
+ if (!maybe_recoverable (file_name, &interdir_made))
+ break;
+ if (status == 0)
+ set_stat (file_name, ¤t_stat_info.stat, 0, 0, 0, SYMTYPE);
+ else
+ symlink_error (current_stat_info.link_name, file_name);
return status;
#else
/* Extract the symbolic links whose final extraction were delayed. */
static void
-apply_delayed_symlinks (void)
+apply_delayed_links (void)
{
- struct delayed_symlink *ds;
+ struct delayed_link *ds;
- for (ds = delayed_symlink_head; ds; )
+ for (ds = delayed_link_head; ds; )
{
struct string_list *sources = ds->sources;
char const *valid_source = 0;
struct stat st;
/* Make sure the placeholder file is still there. If not,
- don't create a symlink, as the placeholder was probably
+ don't create a link, as the placeholder was probably
removed by a later extraction. */
if (lstat (source, &st) == 0
&& st.st_dev == ds->dev
unlink_error (source);
else if (valid_source && link (valid_source, source) == 0)
;
+ else if (!ds->is_symlink)
+ {
+ if (link (ds->target, source) != 0)
+ link_error (ds->target, source);
+ }
else if (symlink (ds->target, source) != 0)
symlink_error (ds->target, source);
else
}
{
- struct delayed_symlink *next = ds->next;
+ struct delayed_link *next = ds->next;
free (ds);
ds = next;
}
}
- delayed_symlink_head = 0;
+ delayed_link_head = 0;
}
/* Finish the extraction of an archive. */
/* First, fix the status of ordinary directories that need fixing. */
apply_nonancestor_delayed_set_stat ("", 0);
- /* Then, apply delayed symlinks, so that they don't affect delayed
+ /* Then, apply delayed links, so that they don't affect delayed
directory status-setting for ordinary directories. */
- apply_delayed_symlinks ();
+ apply_delayed_links ();
/* Finally, fix the status of directories that are ancestors
- of delayed symlinks. */
+ of delayed links. */
apply_nonancestor_delayed_set_stat ("", 1);
}