(we_are_root): Now global.

(struct delayed_symlink): New type.
(delayed_symlink_head): New var.
(extr_init, fatal_exit):
Invoke extract_finish on fatal errors, not apply_delayed_set_stat.
(set_mode, set_stat): Pointer args are now const pointers.
(check_time): New function.
(set_stat): Warn if setting a file's timestamp to be the future.
(make_directories): Do not save and restore errno.
(maybe_recoverable): Set errno to ENOENT if we cannot make missing
intermediate directories.
(extract_archive):
Invoke apply_nonancestor_delayed_set_stat here, not in caller.
Extract potentially dangerous symbolic links more carefully, deferring their
creation until the end, and using a regular file placeholder in the meantime.
Do not remove trailing / and /. from file names.
Do not bother checking for ".." when checking whether a directory
loops back on itself, as loopbacks can occur with symlinks too.
Also, in that case, do not bother saving and restoring errno; just
set it to EEXIST.
(apply_nonancestor_delayed_set_stat): A prefix is a potential ancestor if
it ends in slash too (as well as ending in a char just before slash).
(apply_delayed_set_stat): Remove.
(apply_delayed_symlinks, extract_finish): New functions.
This commit is contained in:
Paul Eggert
2000-10-24 06:18:37 +00:00
parent 2f04b655d8
commit e89bcb7fb3

View File

@@ -31,7 +31,7 @@ struct utimbuf
#include "common.h"
static int we_are_root; /* true if our effective uid == 0 */
int we_are_root; /* true if our effective uid == 0 */
static mode_t newdir_umask; /* umask when creating new directories */
static mode_t current_umask; /* current umask (which is set to 0 if -p) */
@@ -65,17 +65,37 @@ struct delayed_set_stat
static struct delayed_set_stat *delayed_set_stat_head;
/*--------------------------.
| Set up to extract files. |
`--------------------------*/
/* List of symbolic links whose creation we have delayed. */
struct delayed_symlink
{
/* The next delayed symbolic link in the list. */
struct delayed_symlink *next;
/* The device, inode number and last-modified time of the
placeholder symbolic link. */
dev_t dev;
ino_t ino;
time_t mtime;
/* The desired owner and group of the symbolic link. */
uid_t uid;
gid_t gid;
/* The location and desired target of the desired link, as two
adjacent character strings, both null-terminated. */
char names[1];
};
static struct delayed_symlink *delayed_symlink_head;
/* Set up to extract files. */
void
extr_init (void)
{
we_are_root = geteuid () == 0;
same_permissions_option += we_are_root;
same_owner_option += we_are_root;
xalloc_fail_func = apply_delayed_set_stat;
xalloc_fail_func = extract_finish;
/* Option -p clears the kernel umask, so it does not affect proper
restoration of file permissions. New intermediate directories will
@@ -97,7 +117,7 @@ extr_init (void)
PERMSTATUS specifies the status of the file's permissions.
TYPEFLAG specifies the type of the file. */
static void
set_mode (char *file_name, struct stat *stat_info,
set_mode (char const *file_name, struct stat const *stat_info,
mode_t invert_permissions, enum permstatus permstatus,
char typeflag)
{
@@ -141,6 +161,16 @@ set_mode (char *file_name, struct stat *stat_info,
chmod_error_details (file_name, mode);
}
/* Check time after successfully setting FILE_NAME's time stamp to T. */
static void
check_time (char const *file_name, time_t t)
{
time_t now;
if (start_time < t && (now = time (0)) < t)
WARN ((0, 0, _("%s: time stamp %s is %lu s in the future"),
file_name, tartime (t), (unsigned long) (t - now)));
}
/* Restore stat attributes (owner, group, mode and times) for
FILE_NAME, using information given in *STAT_INFO.
If not restoring permissions, invert the
@@ -154,7 +184,7 @@ set_mode (char *file_name, struct stat *stat_info,
punt for the rest. Sigh! */
static void
set_stat (char *file_name, struct stat *stat_info,
set_stat (char const *file_name, struct stat const *stat_info,
mode_t invert_permissions, enum permstatus permstatus,
char typeflag)
{
@@ -182,6 +212,11 @@ set_stat (char *file_name, struct stat *stat_info,
if (utime (file_name, &utimbuf) < 0)
utime_error (file_name);
else
{
check_time (file_name, stat_info->st_atime);
check_time (file_name, stat_info->st_mtime);
}
}
/* Some systems allow non-root users to give files away. Once this
@@ -245,9 +280,10 @@ delay_set_stat (char const *file_name, struct stat const *stat_info,
}
/* Update the delayed_set_stat info for an intermediate directory
created on the path to DIR_NAME. The intermediate directory
turned out to be the same as this directory, due to ".." or
symbolic links. *DIR_STAT_INFO is the status of the directory. */
created on the path to DIR_NAME. The intermediate directory turned
out to be the same as this directory, e.g. due trailing slash or
".." or symbolic links. *DIR_STAT_INFO is the status of the
directory. */
static void
repair_delayed_set_stat (char const *dir_name,
struct stat const *dir_stat_info)
@@ -277,18 +313,15 @@ repair_delayed_set_stat (char const *dir_name,
quotearg_colon (dir_name)));
}
/*-----------------------------------------------------------------------.
| After a file/link/symlink/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. |
`-----------------------------------------------------------------------*/
/* After a file/link/symlink/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. */
static int
make_directories (char *file_name)
{
char *cursor; /* points into path */
int did_something = 0; /* did we do anything yet? */
int saved_errno = errno; /* remember caller's errno */
int mode;
int invert_permissions;
int status;
@@ -346,7 +379,6 @@ make_directories (char *file_name)
break;
}
errno = saved_errno;
return did_something; /* tell them to retry if we made one */
}
@@ -370,13 +402,10 @@ prepare_to_extract (char const *file_name)
return 1;
}
/*--------------------------------------------------------------------.
| Attempt repairing what went wrong with the extraction. Delete an |
| already existing file or create missing intermediate directories. |
| Return nonzero if we somewhat increased our chances at a successful |
| extraction. errno is properly restored on zero return. |
`--------------------------------------------------------------------*/
/* Attempt repairing what went wrong with the extraction. Delete an
already existing file or create missing intermediate directories.
Return nonzero if we somewhat increased our chances at a successful
extraction. errno is properly restored on zero return. */
static int
maybe_recoverable (char *file_name, int *interdir_made)
{
@@ -405,7 +434,10 @@ maybe_recoverable (char *file_name, int *interdir_made)
case ENOENT:
/* Attempt creating missing intermediate directories. */
if (! make_directories (file_name))
return 0;
{
errno = ENOENT;
return 0;
}
*interdir_made = 1;
return 1;
@@ -416,10 +448,6 @@ maybe_recoverable (char *file_name, int *interdir_made)
}
}
/*---.
| ? |
`---*/
static void
extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name)
{
@@ -477,10 +505,30 @@ extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name)
free (sparsearray);
}
/*----------------------------------.
| Extract a file from the archive. |
`----------------------------------*/
/* Fix the statuses of all directories whose statuses need fixing, and
which are not ancestors of FILE_NAME. */
static void
apply_nonancestor_delayed_set_stat (char const *file_name)
{
size_t file_name_len = strlen (file_name);
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]
&& (file_name[data->file_name_len] == '/'
|| file_name[data->file_name_len - 1] == '/')
&& memcmp (file_name, data->file_name, data->file_name_len) == 0)
break;
delayed_set_stat_head = data->next;
set_stat (data->file_name, &data->stat_info,
data->invert_permissions, data->permstatus, DIRTYPE);
free (data);
}
}
/* Extract a file from the archive. */
void
extract_archive (void)
{
@@ -497,9 +545,6 @@ extract_archive (void)
int counter;
int interdir_made = 0;
char typeflag;
#if 0
int sparse_ind = 0;
#endif
union block *exhdr;
#define CURRENT_FILE_NAME (skipcrud + current_file_name)
@@ -509,13 +554,11 @@ extract_archive (void)
if (interactive_option && !confirm ("extract", current_file_name))
{
if (current_header->oldgnu_header.isextended)
skip_extended_headers ();
skip_file (current_stat.st_size);
skip_member ();
return;
}
/* Print the block from `current_header' and `current_stat'. */
/* Print the block from current_header and current_stat. */
if (verbose_option)
print_header ();
@@ -541,13 +584,13 @@ extract_archive (void)
{
ERROR ((0, 0, _("%s: Member name contains `..'"),
quotearg_colon (CURRENT_FILE_NAME)));
if (current_header->oldgnu_header.isextended)
skip_extended_headers ();
skip_file (current_stat.st_size);
skip_member ();
return;
}
}
apply_nonancestor_delayed_set_stat (CURRENT_FILE_NAME);
/* Take a safety backup of a previously existing file. */
if (backup_option && !to_stdout_option)
@@ -556,9 +599,7 @@ extract_archive (void)
int e = errno;
ERROR ((0, e, _("%s: Was unable to backup this file"),
quotearg_colon (CURRENT_FILE_NAME)));
if (current_header->oldgnu_header.isextended)
skip_extended_headers ();
skip_file (current_stat.st_size);
skip_member ();
return;
}
@@ -671,9 +712,7 @@ extract_archive (void)
if (! prepare_to_extract (CURRENT_FILE_NAME))
{
if (current_header->oldgnu_header.isextended)
skip_extended_headers ();
skip_file (current_stat.st_size);
skip_member ();
if (backup_option)
undo_last_backup ();
break;
@@ -710,9 +749,7 @@ extract_archive (void)
goto again_file;
open_error (CURRENT_FILE_NAME);
if (current_header->oldgnu_header.isextended)
skip_extended_headers ();
skip_file (current_stat.st_size);
skip_member ();
if (backup_option)
undo_last_backup ();
break;
@@ -808,30 +845,63 @@ extract_archive (void)
if (! prepare_to_extract (CURRENT_FILE_NAME))
break;
while (status = symlink (current_link_name, CURRENT_FILE_NAME),
status != 0)
if (!maybe_recoverable (CURRENT_FILE_NAME, &interdir_made))
break;
if (status == 0)
/* Setting the attributes of symbolic links might, on some systems,
change the pointed to file, instead of the symbolic link itself.
At least some of these systems have a lchown call, and the
set_stat routine knows about this. */
set_stat (CURRENT_FILE_NAME, &current_stat, 0,
ARCHIVED_PERMSTATUS, typeflag);
if (absolute_names_option
|| (current_link_name[0] != '/'
&& ! contains_dot_dot (current_link_name)))
{
while (status = symlink (current_link_name, CURRENT_FILE_NAME),
status != 0)
if (!maybe_recoverable (CURRENT_FILE_NAME, &interdir_made))
break;
if (status == 0)
set_stat (CURRENT_FILE_NAME, &current_stat, 0, 0, SYMTYPE);
else
symlink_error (current_link_name, CURRENT_FILE_NAME);
}
else
{
int e = errno;
ERROR ((0, e, _("%s: Cannot create symlink to %s"),
quotearg_colon (CURRENT_FILE_NAME),
quote (current_link_name)));
if (backup_option)
undo_last_backup ();
/* 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 (CURRENT_FILE_NAME, O_WRONLY | O_CREAT | O_EXCL, 0),
fd < 0)
if (! maybe_recoverable (CURRENT_FILE_NAME, &interdir_made))
break;
status = -1;
if (fd < 0)
open_error (CURRENT_FILE_NAME);
else if (fstat (fd, &st) != 0)
{
stat_error (CURRENT_FILE_NAME);
close (fd);
}
else if (close (fd) != 0)
close_error (CURRENT_FILE_NAME);
else
{
size_t filelen = strlen (CURRENT_FILE_NAME);
size_t linklen = strlen (current_link_name);
struct delayed_symlink *p =
xmalloc (sizeof *p + filelen + linklen + 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.st_uid;
p->gid = current_stat.st_gid;
memcpy (p->names, CURRENT_FILE_NAME, filelen + 1);
memcpy (p->names + filelen + 1, current_link_name, linklen + 1);
status = 0;
}
}
if (status != 0 && backup_option)
undo_last_backup ();
break;
#else
@@ -943,20 +1013,6 @@ extract_archive (void)
name_length = strlen (CURRENT_FILE_NAME);
really_dir:
/* Remove trailing "/" and "/.", unless that would result in the
empty string. */
for (;;)
{
if (1 < name_length && CURRENT_FILE_NAME[name_length - 1] == '/')
CURRENT_FILE_NAME[--name_length] = '\0';
else if (2 < name_length
&& CURRENT_FILE_NAME[name_length - 1] == '.'
&& CURRENT_FILE_NAME[name_length - 2] == '/')
CURRENT_FILE_NAME[name_length -= 2] = '\0';
else
break;
}
if (incremental_option)
{
/* Read the entry and delete files that aren't listed in the
@@ -965,7 +1021,7 @@ extract_archive (void)
gnu_restore (skipcrud);
}
else if (typeflag == GNUTYPE_DUMPDIR)
skip_file (current_stat.st_size);
skip_member ();
if (! prepare_to_extract (CURRENT_FILE_NAME))
break;
@@ -978,17 +1034,15 @@ extract_archive (void)
status = mkdir (CURRENT_FILE_NAME, mode);
if (status != 0)
{
if (errno == EEXIST && interdir_made
&& contains_dot_dot (CURRENT_FILE_NAME))
if (errno == EEXIST && interdir_made)
{
int e = errno;
struct stat st;
if (stat (CURRENT_FILE_NAME, &st) == 0)
{
repair_delayed_set_stat (CURRENT_FILE_NAME, &st);
break;
}
e = errno;
errno = EEXIST;
}
if (maybe_recoverable (CURRENT_FILE_NAME, &interdir_made))
@@ -1025,7 +1079,7 @@ extract_archive (void)
ERROR ((0, 0,
_("%s: Cannot extract -- file is continued from another volume"),
quotearg_colon (current_file_name)));
skip_file (current_stat.st_size);
skip_member ();
if (backup_option)
undo_last_backup ();
break;
@@ -1033,7 +1087,7 @@ extract_archive (void)
case GNUTYPE_LONGNAME:
case GNUTYPE_LONGLINK:
ERROR ((0, 0, _("Visible long name error")));
skip_file (current_stat.st_size);
skip_member ();
if (backup_option)
undo_last_backup ();
break;
@@ -1048,38 +1102,63 @@ extract_archive (void)
#undef CURRENT_FILE_NAME
}
/* Fix the status of all directories whose statuses need fixing. */
void
apply_delayed_set_stat (void)
/* Extract the symbolic links whose final extraction were delayed. */
static void
apply_delayed_symlinks (void)
{
apply_nonancestor_delayed_set_stat ("");
struct delayed_symlink *p;
struct delayed_symlink *next;
for (p = delayed_symlink_head; p; p = next)
{
char const *file = p->names;
struct stat st;
/* Before doing anything, make sure the placeholder file is still
there. If the placeholder isn't there, don't worry about it, as
it may have been removed by a later extraction. */
if (lstat (file, &st) == 0
&& st.st_dev == p->dev
&& st.st_ino == p->ino
&& st.st_mtime == p->mtime)
{
if (unlink (file) != 0)
unlink_error (file);
else
{
char const *contents = file + strlen (file) + 1;
if (symlink (contents, file) != 0)
symlink_error (contents, file);
else
{
st.st_uid = p->uid;
st.st_gid = p->gid;
set_stat (file, &st, 0, 0, SYMTYPE);
}
}
}
next = p->next;
free (p);
}
delayed_symlink_head = 0;
}
/* Fix the statuses of all directories whose statuses need fixing, and
which are not ancestors of FILE_NAME. */
/* Finish the extraction of an archive. */
void
apply_nonancestor_delayed_set_stat (char const *file_name)
extract_finish (void)
{
size_t file_name_len = strlen (file_name);
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] == '/'
&& memcmp (file_name, data->file_name, data->file_name_len) == 0)
break;
delayed_set_stat_head = data->next;
set_stat (data->file_name, &data->stat_info,
data->invert_permissions, data->permstatus, DIRTYPE);
free (data);
}
/* Apply delayed symlinks last, so that they don't affect
delayed directory status-setting. */
apply_nonancestor_delayed_set_stat ("");
apply_delayed_symlinks ();
}
void
fatal_exit (void)
{
apply_delayed_set_stat ();
extract_finish ();
error (TAREXIT_FAILURE, 0, _("Error is not recoverable: exiting now"));
abort ();
}