(<time.h>): Do not include; system.h now does this.

(make_directories): Skip filesystem prefixes.
Don't assume '/' is the only separator.
(extract_sparse_file): Use new full_write semantics.
On write error, return instead of invoking skip_file.
Do not free sparsearray; caller does this now.
(apply_nonancestor_delayed_set_stat): Do not assume '/' is the only
separator.
(extract_archive): Don't assume file name lengths fit in int.
Report what got stripped from member name; it might be more than '/'.
Use new full_write semantics.
Do not pass redundant trailing "/" to mkdir, as POSIX does not allow
mkdir to ignore it.
Do not report mkdir error if old_files_option == KEEP_OLD_FILES.
This commit is contained in:
Paul Eggert
2001-01-13 05:59:29 +00:00
parent 1ff433e49e
commit eca497ef34

View File

@@ -1,5 +1,5 @@
/* Extract files from a tar archive.
Copyright 1988, 92,93,94,96,97,98,99, 2000 Free Software Foundation, Inc.
Copyright 1988,92,93,94,96,97,98,99,2000,2001 Free Software Foundation, Inc.
Written by John Gilmore, on 1985-11-19.
This program is free software; you can redistribute it and/or modify it
@@ -18,7 +18,6 @@
#include "system.h"
#include <quotearg.h>
#include <time.h>
#if HAVE_UTIME_H
# include <utime.h>
@@ -321,27 +320,30 @@ repair_delayed_set_stat (char const *dir_name,
static int
make_directories (char *file_name)
{
char *cursor0 = file_name + FILESYSTEM_PREFIX_LEN (file_name);
char *cursor; /* points into path */
int did_something = 0; /* did we do anything yet? */
int mode;
int invert_permissions;
int status;
for (cursor = strchr (file_name, '/');
cursor;
cursor = strchr (cursor + 1, '/'))
for (cursor = cursor0; *cursor; cursor++)
{
if (! ISSLASH (*cursor))
continue;
/* Avoid mkdir of empty string, if leading or double '/'. */
if (cursor == file_name || cursor[-1] == '/')
if (cursor == cursor0 || ISSLASH (cursor[-1]))
continue;
/* Avoid mkdir where last part of path is "." or "..". */
if (cursor[-1] == '.'
&& (cursor == file_name + 1 || cursor[-2] == '/'
&& (cursor == cursor0 + 1 || ISSLASH (cursor[-2])
|| (cursor[-2] == '.'
&& (cursor == file_name + 2 || cursor[-3] == '/'))))
&& (cursor == cursor0 + 2 || ISSLASH (cursor[-3])))))
continue;
*cursor = '\0'; /* truncate the path there */
@@ -453,13 +455,13 @@ static void
extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name)
{
int sparse_ind = 0;
size_t written;
ssize_t count;
/* assuming sizeleft is initially totalsize */
while (*sizeleft > 0)
{
size_t written;
size_t count;
union block *data_block = find_next_block ();
if (! data_block)
{
@@ -475,10 +477,13 @@ extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name)
while (written > BLOCKSIZE)
{
count = full_write (fd, data_block->buffer, BLOCKSIZE);
if (count < 0)
write_error (name);
written -= count;
*sizeleft -= count;
if (count != BLOCKSIZE)
{
write_error_details (name, count, BLOCKSIZE);
return;
}
set_next_block_after (data_block);
data_block = find_next_block ();
if (! data_block)
@@ -489,21 +494,16 @@ extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name)
}
count = full_write (fd, data_block->buffer, written);
*sizeleft -= count;
if (count < 0)
write_error (name);
else if (count != written)
if (count != written)
{
write_error_details (name, count, written);
skip_file (*sizeleft);
return;
}
written -= count;
*sizeleft -= count;
set_next_block_after (data_block);
}
free (sparsearray);
}
/* Fix the statuses of all directories whose statuses need fixing, and
@@ -518,8 +518,8 @@ apply_nonancestor_delayed_set_stat (char const *file_name)
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] == '/')
&& (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;
delayed_set_stat_head = data->next;
@@ -536,13 +536,13 @@ extract_archive (void)
union block *data_block;
int fd;
int status;
ssize_t sstatus;
size_t count;
size_t name_length;
size_t written;
int openflag;
mode_t mode;
off_t size;
int skipcrud;
size_t skipcrud;
int counter;
int interdir_made = 0;
char typeflag;
@@ -569,18 +569,6 @@ extract_archive (void)
skipcrud = 0;
if (! absolute_names_option)
{
while (CURRENT_FILE_NAME[0] == '/')
{
static int warned_once;
if (!warned_once)
{
warned_once = 1;
WARN ((0, 0, _("Removing leading `/' from member names")));
}
skipcrud++; /* force relative path */
}
if (contains_dot_dot (CURRENT_FILE_NAME))
{
ERROR ((0, 0, _("%s: Member name contains `..'"),
@@ -588,6 +576,22 @@ extract_archive (void)
skip_member ();
return;
}
skipcrud = FILESYSTEM_PREFIX_LEN (current_file_name);
while (ISSLASH (CURRENT_FILE_NAME[0]))
skipcrud++;
if (skipcrud)
{
static int warned_once;
if (!warned_once)
{
warned_once = 1;
WARN ((0, 0, _("Removing leading `%.*s' from member names"),
(int) skipcrud, current_file_name));
}
}
}
apply_nonancestor_delayed_set_stat (CURRENT_FILE_NAME);
@@ -693,7 +697,8 @@ extract_archive (void)
suffix means a directory. */
name_length = strlen (CURRENT_FILE_NAME);
if (name_length && CURRENT_FILE_NAME[name_length - 1] == '/')
if (FILESYSTEM_PREFIX_LEN (CURRENT_FILE_NAME) < name_length
&& CURRENT_FILE_NAME[name_length - 1] == '/')
goto really_dir;
/* FIXME: deal with protection issues. */
@@ -773,11 +778,10 @@ extract_archive (void)
memcpy (name, CURRENT_FILE_NAME, name_length_bis);
size = current_stat.st_size;
extract_sparse_file (fd, &size, current_stat.st_size, name);
free (sparsearray);
}
else
for (size = current_stat.st_size;
size > 0;
size -= written)
for (size = current_stat.st_size; size > 0; )
{
if (multi_volume_option)
{
@@ -802,21 +806,20 @@ extract_archive (void)
if (written > size)
written = size;
errno = 0;
sstatus = full_write (fd, data_block->buffer, written);
count = full_write (fd, data_block->buffer, written);
size -= count;
set_next_block_after ((union block *)
(data_block->buffer + written - 1));
if (sstatus == written)
continue;
/* Error in writing to file. Print it, skip to next file in
archive. */
write_error_details (CURRENT_FILE_NAME, sstatus, written);
skip_file (size - written);
break; /* still do the close, mod time, chmod, etc */
if (count != written)
{
write_error_details (CURRENT_FILE_NAME, count, written);
break;
}
}
skip_file (size);
if (multi_volume_option)
assign_string (&save_name, 0);
@@ -847,7 +850,8 @@ extract_archive (void)
break;
if (absolute_names_option
|| (current_link_name[0] != '/'
|| (ISSLASH (current_link_name
[FILESYSTEM_PREFIX_LEN (current_link_name)])
&& ! contains_dot_dot (current_link_name)))
{
while (status = symlink (current_link_name, CURRENT_FILE_NAME),
@@ -1014,6 +1018,7 @@ extract_archive (void)
name_length = strlen (CURRENT_FILE_NAME);
really_dir:
if (incremental_option)
{
/* Read the entry and delete files that aren't listed in the
@@ -1032,7 +1037,19 @@ extract_archive (void)
& MODE_RWX);
again_dir:
status = mkdir (CURRENT_FILE_NAME, mode);
{
/* Do not pass redundant trailing "/" to mkdir, as POSIX does
not allow mkdir to ignore it. */
size_t len = name_length;
char ch = '\0';
while (FILESYSTEM_PREFIX_LEN (CURRENT_FILE_NAME) < len
&& CURRENT_FILE_NAME[len - 1] == '/')
len--, ch = '/';
CURRENT_FILE_NAME[len] = '\0';
status = mkdir (CURRENT_FILE_NAME, mode);
CURRENT_FILE_NAME[len] = ch;
}
if (status != 0)
{
if (errno == EEXIST && interdir_made)
@@ -1049,7 +1066,7 @@ extract_archive (void)
if (maybe_recoverable (CURRENT_FILE_NAME, &interdir_made))
goto again_dir;
if (errno != EEXIST || old_files_option == KEEP_OLD_FILES)
if (errno != EEXIST)
{
mkdir_error (CURRENT_FILE_NAME);
if (backup_option)