(struct delayed_set_stat): New member 'after_symlinks'.
(delay_set_stat): Initialize it to 0. (set_mode): New arg current_stat_info. Use it (if nonnull) to avoid taking an extra stat ourselves. All callers changed. (set_stat): Likewise. (apply_nonancestor_delayed_set_stat): New arg 'after_symlinks'. If false, stop when encountering a struct whose 'after_symlinks' member is true. Otherwise, go through all structures but check them more carefully. All callers changed. (extract_archive): When extracting a deferred symlink, if its parent directory's status needs fixing, then mark the directory as needing to be fixed after symlinks. (extract_finish): Fix status of ordinary directories, then apply delayed symlinks, then fix the status of directories that are ancestors of delayed symlinks.
This commit is contained in:
129
src/extract.c
129
src/extract.c
@@ -53,9 +53,14 @@ enum permstatus
|
||||
};
|
||||
|
||||
/* List of directories whose statuses we need to extract after we've
|
||||
finished extracting their subsidiary files. The head of the list
|
||||
has the longest name; each non-head element in the list is an
|
||||
ancestor (in the directory hierarchy) of the preceding element. */
|
||||
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
|
||||
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. */
|
||||
|
||||
struct delayed_set_stat
|
||||
{
|
||||
struct delayed_set_stat *next;
|
||||
@@ -63,6 +68,7 @@ struct delayed_set_stat
|
||||
size_t file_name_len;
|
||||
mode_t invert_permissions;
|
||||
enum permstatus permstatus;
|
||||
bool after_symlinks;
|
||||
char file_name[1];
|
||||
};
|
||||
|
||||
@@ -123,12 +129,14 @@ extr_init (void)
|
||||
}
|
||||
|
||||
/* If restoring permissions, restore the mode for FILE_NAME from
|
||||
information given in *STAT_INFO; otherwise invert the
|
||||
information given in *STAT_INFO (where *CURRENT_STAT_INFO gives
|
||||
the current status if CURRENT_STAT_INFO is nonzero); otherwise invert the
|
||||
INVERT_PERMISSIONS bits from the file's current permissions.
|
||||
PERMSTATUS specifies the status of the file's permissions.
|
||||
TYPEFLAG specifies the type of the file. */
|
||||
static void
|
||||
set_mode (char const *file_name, struct stat const *stat_info,
|
||||
struct stat const *current_stat_info,
|
||||
mode_t invert_permissions, enum permstatus permstatus,
|
||||
char typeflag)
|
||||
{
|
||||
@@ -160,12 +168,16 @@ set_mode (char const *file_name, struct stat const *stat_info,
|
||||
that we created, so there's no point optimizing this code for
|
||||
other cases. */
|
||||
struct stat st;
|
||||
if (stat (file_name, &st) != 0)
|
||||
if (! current_stat_info)
|
||||
{
|
||||
stat_error (file_name);
|
||||
return;
|
||||
if (stat (file_name, &st) != 0)
|
||||
{
|
||||
stat_error (file_name);
|
||||
return;
|
||||
}
|
||||
current_stat_info = &st;
|
||||
}
|
||||
mode = st.st_mode ^ invert_permissions;
|
||||
mode = current_stat_info->st_mode ^ invert_permissions;
|
||||
}
|
||||
|
||||
if (chmod (file_name, mode) != 0)
|
||||
@@ -184,6 +196,8 @@ check_time (char const *file_name, time_t t)
|
||||
|
||||
/* Restore stat attributes (owner, group, mode and times) for
|
||||
FILE_NAME, using information given in *STAT_INFO.
|
||||
If CURRENT_STAT_INFO is nonzero, *CURRENT_STAT_INFO is the
|
||||
file's currernt status.
|
||||
If not restoring permissions, invert the
|
||||
INVERT_PERMISSIONS bits from the file's current permissions.
|
||||
PERMSTATUS specifies the status of the file's permissions.
|
||||
@@ -196,6 +210,7 @@ check_time (char const *file_name, time_t t)
|
||||
|
||||
static void
|
||||
set_stat (char const *file_name, struct stat const *stat_info,
|
||||
struct stat const *current_stat_info,
|
||||
mode_t invert_permissions, enum permstatus permstatus,
|
||||
char typeflag)
|
||||
{
|
||||
@@ -234,7 +249,7 @@ set_stat (char const *file_name, struct stat const *stat_info,
|
||||
done, it is not possible anymore to change file permissions, so we
|
||||
have to set permissions prior to possibly giving files away. */
|
||||
|
||||
set_mode (file_name, stat_info,
|
||||
set_mode (file_name, stat_info, current_stat_info,
|
||||
invert_permissions, permstatus, typeflag);
|
||||
}
|
||||
|
||||
@@ -263,7 +278,7 @@ set_stat (char const *file_name, struct stat const *stat_info,
|
||||
away, changing the owner or group destroys the suid or sgid bits.
|
||||
So let's attempt setting these bits once more. */
|
||||
if (stat_info->st_mode & (S_ISUID | S_ISGID | S_ISVTX))
|
||||
set_mode (file_name, stat_info,
|
||||
set_mode (file_name, stat_info, 0,
|
||||
invert_permissions, permstatus, typeflag);
|
||||
}
|
||||
}
|
||||
@@ -287,6 +302,7 @@ delay_set_stat (char const *file_name, struct stat const *stat_info,
|
||||
strcpy (data->file_name, file_name);
|
||||
data->invert_permissions = invert_permissions;
|
||||
data->permstatus = permstatus;
|
||||
data->after_symlinks = 0;
|
||||
data->stat_info = *stat_info;
|
||||
data->next = delayed_set_stat_head;
|
||||
delayed_set_stat_head = data;
|
||||
@@ -519,24 +535,55 @@ extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name)
|
||||
}
|
||||
|
||||
/* Fix the statuses of all directories whose statuses need fixing, and
|
||||
which are not ancestors of FILE_NAME. */
|
||||
which are not ancestors of FILE_NAME. If AFTER_SYMLINKS 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. */
|
||||
static void
|
||||
apply_nonancestor_delayed_set_stat (char const *file_name)
|
||||
apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks)
|
||||
{
|
||||
size_t file_name_len = strlen (file_name);
|
||||
bool check_for_renamed_directories = 0;
|
||||
|
||||
while (delayed_set_stat_head)
|
||||
{
|
||||
struct delayed_set_stat *data = delayed_set_stat_head;
|
||||
if (data->file_name_len < file_name_len
|
||||
&& file_name[data->file_name_len]
|
||||
&& (ISSLASH (file_name[data->file_name_len])
|
||||
|| ISSLASH (file_name[data->file_name_len - 1]))
|
||||
&& memcmp (file_name, data->file_name, data->file_name_len) == 0)
|
||||
bool skip_this_one = 0;
|
||||
check_for_renamed_directories |= data->after_symlinks;
|
||||
struct stat st;
|
||||
struct stat const *current_stat_info = 0;
|
||||
|
||||
if (after_symlinks < data->after_symlinks
|
||||
|| (data->file_name_len < file_name_len
|
||||
&& file_name[data->file_name_len]
|
||||
&& (ISSLASH (file_name[data->file_name_len])
|
||||
|| ISSLASH (file_name[data->file_name_len - 1]))
|
||||
&& memcmp (file_name, data->file_name, data->file_name_len) == 0))
|
||||
break;
|
||||
|
||||
if (check_for_renamed_directories)
|
||||
{
|
||||
current_stat_info = &st;
|
||||
if (stat (data->file_name, &st) != 0)
|
||||
{
|
||||
stat_error (data->file_name);
|
||||
skip_this_one = 1;
|
||||
}
|
||||
else if (! (st.st_dev == data->stat_info.st_dev
|
||||
&& (st.st_ino == data->stat_info.st_ino)))
|
||||
{
|
||||
ERROR ((0, 0,
|
||||
_("%s: Directory renamed before its status could be extracted"),
|
||||
quotearg_colon (data->file_name)));
|
||||
skip_this_one = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (! skip_this_one)
|
||||
set_stat (data->file_name, &data->stat_info, current_stat_info,
|
||||
data->invert_permissions, data->permstatus, DIRTYPE);
|
||||
|
||||
delayed_set_stat_head = data->next;
|
||||
set_stat (data->file_name, &data->stat_info,
|
||||
data->invert_permissions, data->permstatus, DIRTYPE);
|
||||
free (data);
|
||||
}
|
||||
}
|
||||
@@ -606,7 +653,7 @@ extract_archive (void)
|
||||
}
|
||||
}
|
||||
|
||||
apply_nonancestor_delayed_set_stat (CURRENT_FILE_NAME);
|
||||
apply_nonancestor_delayed_set_stat (CURRENT_FILE_NAME, 0);
|
||||
|
||||
/* Take a safety backup of a previously existing file. */
|
||||
|
||||
@@ -849,7 +896,7 @@ extract_archive (void)
|
||||
undo_last_backup ();
|
||||
}
|
||||
|
||||
set_stat (CURRENT_FILE_NAME, ¤t_stat, 0,
|
||||
set_stat (CURRENT_FILE_NAME, ¤t_stat, 0, 0,
|
||||
(old_files_option == OVERWRITE_OLD_FILES
|
||||
? UNKNOWN_PERMSTATUS
|
||||
: ARCHIVED_PERMSTATUS),
|
||||
@@ -872,7 +919,7 @@ extract_archive (void)
|
||||
break;
|
||||
|
||||
if (status == 0)
|
||||
set_stat (CURRENT_FILE_NAME, ¤t_stat, 0, 0, SYMTYPE);
|
||||
set_stat (CURRENT_FILE_NAME, ¤t_stat, 0, 0, 0, SYMTYPE);
|
||||
else
|
||||
symlink_error (current_link_name, CURRENT_FILE_NAME);
|
||||
}
|
||||
@@ -900,6 +947,7 @@ extract_archive (void)
|
||||
close_error (CURRENT_FILE_NAME);
|
||||
else
|
||||
{
|
||||
struct delayed_set_stat *h;
|
||||
struct delayed_symlink *p =
|
||||
xmalloc (offsetof (struct delayed_symlink, target)
|
||||
+ strlen (current_link_name) + 1);
|
||||
@@ -915,6 +963,25 @@ extract_archive (void)
|
||||
p->sources->next = 0;
|
||||
strcpy (p->sources->string, CURRENT_FILE_NAME);
|
||||
strcpy (p->target, current_link_name);
|
||||
|
||||
h = delayed_set_stat_head;
|
||||
if (h && ! h->after_symlinks
|
||||
&& strncmp (CURRENT_FILE_NAME, h->file_name, h->file_name_len) == 0
|
||||
&& ISSLASH (CURRENT_FILE_NAME[h->file_name_len])
|
||||
&& (base_name (CURRENT_FILE_NAME)
|
||||
== CURRENT_FILE_NAME + h->file_name_len + 1))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
status = 0;
|
||||
}
|
||||
}
|
||||
@@ -1015,7 +1082,7 @@ extract_archive (void)
|
||||
undo_last_backup ();
|
||||
break;
|
||||
};
|
||||
set_stat (CURRENT_FILE_NAME, ¤t_stat, 0,
|
||||
set_stat (CURRENT_FILE_NAME, ¤t_stat, 0, 0,
|
||||
ARCHIVED_PERMSTATUS, typeflag);
|
||||
break;
|
||||
#endif
|
||||
@@ -1031,7 +1098,7 @@ extract_archive (void)
|
||||
break;
|
||||
|
||||
if (status == 0)
|
||||
set_stat (CURRENT_FILE_NAME, ¤t_stat, 0,
|
||||
set_stat (CURRENT_FILE_NAME, ¤t_stat, 0, 0,
|
||||
ARCHIVED_PERMSTATUS, typeflag);
|
||||
else
|
||||
{
|
||||
@@ -1190,7 +1257,7 @@ apply_delayed_symlinks (void)
|
||||
valid_source = source;
|
||||
st.st_uid = ds->uid;
|
||||
st.st_gid = ds->gid;
|
||||
set_stat (source, &st, 0, 0, SYMTYPE);
|
||||
set_stat (source, &st, 0, 0, 0, SYMTYPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1216,10 +1283,16 @@ apply_delayed_symlinks (void)
|
||||
void
|
||||
extract_finish (void)
|
||||
{
|
||||
/* Apply delayed symlinks last, so that they don't affect
|
||||
delayed directory status-setting. */
|
||||
apply_nonancestor_delayed_set_stat ("");
|
||||
/* 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
|
||||
directory status-setting for ordinary directories. */
|
||||
apply_delayed_symlinks ();
|
||||
|
||||
/* Finally, fix the status of directories that are ancestors
|
||||
of delayed symlinks. */
|
||||
apply_nonancestor_delayed_set_stat ("", 1);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user