(<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:
123
src/extract.c
123
src/extract.c
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user