(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:
Paul Eggert
2001-09-12 06:46:23 +00:00
parent 172dc3e72b
commit 95dda72cc6

View File

@@ -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, &current_stat, 0,
set_stat (CURRENT_FILE_NAME, &current_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, &current_stat, 0, 0, SYMTYPE);
set_stat (CURRENT_FILE_NAME, &current_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, &current_stat, 0,
set_stat (CURRENT_FILE_NAME, &current_stat, 0, 0,
ARCHIVED_PERMSTATUS, typeflag);
break;
#endif
@@ -1031,7 +1098,7 @@ extract_archive (void)
break;
if (status == 0)
set_stat (CURRENT_FILE_NAME, &current_stat, 0,
set_stat (CURRENT_FILE_NAME, &current_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