(extract_archive): Rewritten
This commit is contained in:
842
src/extract.c
842
src/extract.c
@@ -602,19 +602,435 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
extract_dir (char *file_name, int typeflag)
|
||||
{
|
||||
int status;
|
||||
mode_t mode;
|
||||
int interdir_made = 0;
|
||||
|
||||
if (incremental_option)
|
||||
/* Read the entry and delete files that aren't listed in the archive. */
|
||||
purge_directory (file_name);
|
||||
else if (typeflag == GNUTYPE_DUMPDIR)
|
||||
skip_member ();
|
||||
|
||||
mode = (current_stat_info.stat.st_mode | (we_are_root ? 0 : MODE_WXUSR)) & MODE_RWX;
|
||||
|
||||
status = prepare_to_extract (file_name);
|
||||
if (status == 0)
|
||||
return;
|
||||
|
||||
if (status > 0)
|
||||
while ((status = mkdir (file_name, mode)))
|
||||
{
|
||||
if (errno == EEXIST
|
||||
&& (interdir_made
|
||||
|| old_files_option == DEFAULT_OLD_FILES
|
||||
|| old_files_option == OVERWRITE_OLD_FILES))
|
||||
{
|
||||
struct stat st;
|
||||
if (stat (file_name, &st) == 0)
|
||||
{
|
||||
if (interdir_made)
|
||||
{
|
||||
repair_delayed_set_stat (file_name, &st);
|
||||
return;
|
||||
}
|
||||
if (S_ISDIR (st.st_mode))
|
||||
{
|
||||
mode = st.st_mode & ~ current_umask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
errno = EEXIST;
|
||||
}
|
||||
|
||||
if (maybe_recoverable (file_name, &interdir_made))
|
||||
continue;
|
||||
|
||||
if (errno != EEXIST)
|
||||
{
|
||||
mkdir_error (file_name);
|
||||
if (backup_option)
|
||||
undo_last_backup ();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == 0
|
||||
|| old_files_option == DEFAULT_OLD_FILES
|
||||
|| old_files_option == OVERWRITE_OLD_FILES)
|
||||
delay_set_stat (file_name, ¤t_stat_info.stat,
|
||||
MODE_RWX & (mode ^ current_stat_info.stat.st_mode),
|
||||
(status == 0
|
||||
? ARCHIVED_PERMSTATUS
|
||||
: UNKNOWN_PERMSTATUS));
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
open_output_file (char *file_name, int typeflag)
|
||||
{
|
||||
int fd;
|
||||
int openflag = (O_WRONLY | O_BINARY | O_CREAT
|
||||
| (old_files_option == OVERWRITE_OLD_FILES
|
||||
? O_TRUNC
|
||||
: O_EXCL));
|
||||
mode_t mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask;
|
||||
|
||||
#if O_CTG
|
||||
/* Contiguous files (on the Masscomp) have to specify the size in
|
||||
the open call that creates them. */
|
||||
|
||||
if (typeflag == CONTTYPE)
|
||||
fd = open (file_name, openflag | O_CTG, mode, current_stat_info.stat.st_size);
|
||||
else
|
||||
fd = open (file_name, openflag, mode);
|
||||
|
||||
#else /* not O_CTG */
|
||||
if (typeflag == CONTTYPE)
|
||||
{
|
||||
static int conttype_diagnosed;
|
||||
|
||||
if (!conttype_diagnosed)
|
||||
{
|
||||
conttype_diagnosed = 1;
|
||||
WARN ((0, 0, _("Extracting contiguous files as regular files")));
|
||||
}
|
||||
}
|
||||
fd = open (file_name, openflag, mode);
|
||||
|
||||
#endif /* not O_CTG */
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void
|
||||
extract_file (char *file_name, int typeflag)
|
||||
{
|
||||
int fd;
|
||||
off_t size;
|
||||
union block *data_block;
|
||||
int status;
|
||||
size_t count;
|
||||
size_t written;
|
||||
int interdir_made = 0;
|
||||
|
||||
/* FIXME: deal with protection issues. */
|
||||
|
||||
do
|
||||
{
|
||||
if (to_stdout_option)
|
||||
fd = STDOUT_FILENO;
|
||||
else
|
||||
{
|
||||
if (! prepare_to_extract (file_name))
|
||||
{
|
||||
skip_member ();
|
||||
if (backup_option)
|
||||
undo_last_backup ();
|
||||
return;
|
||||
}
|
||||
fd = open_output_file (file_name, typeflag);
|
||||
}
|
||||
}
|
||||
while (fd < 0 && maybe_recoverable (file_name, &interdir_made));
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
open_error (file_name);
|
||||
skip_member ();
|
||||
if (backup_option)
|
||||
undo_last_backup ();
|
||||
return;
|
||||
}
|
||||
|
||||
if (current_stat_info.is_sparse)
|
||||
sparse_extract_file (fd, ¤t_stat_info, &size);
|
||||
else
|
||||
for (size = current_stat_info.stat.st_size; size > 0; )
|
||||
{
|
||||
if (multi_volume_option)
|
||||
{
|
||||
assign_string (&save_name, current_stat_info.file_name);
|
||||
save_totsize = current_stat_info.stat.st_size;
|
||||
save_sizeleft = size;
|
||||
}
|
||||
|
||||
/* Locate data, determine max length writeable, write it,
|
||||
block that we have used the data, then check if the write
|
||||
worked. */
|
||||
|
||||
data_block = find_next_block ();
|
||||
if (! data_block)
|
||||
{
|
||||
ERROR ((0, 0, _("Unexpected EOF in archive")));
|
||||
break; /* FIXME: What happens, then? */
|
||||
}
|
||||
|
||||
written = available_space_after (data_block);
|
||||
|
||||
if (written > size)
|
||||
written = size;
|
||||
errno = 0;
|
||||
count = full_write (fd, data_block->buffer, written);
|
||||
size -= count;
|
||||
|
||||
set_next_block_after ((union block *)
|
||||
(data_block->buffer + written - 1));
|
||||
if (count != written)
|
||||
{
|
||||
write_error_details (file_name, count, written);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
skip_file (size);
|
||||
|
||||
if (multi_volume_option)
|
||||
assign_string (&save_name, 0);
|
||||
|
||||
/* If writing to stdout, don't try to do anything to the filename;
|
||||
it doesn't exist, or we don't want to touch it anyway. */
|
||||
|
||||
if (to_stdout_option)
|
||||
return;
|
||||
|
||||
status = close (fd);
|
||||
if (status < 0)
|
||||
{
|
||||
close_error (file_name);
|
||||
if (backup_option)
|
||||
undo_last_backup ();
|
||||
}
|
||||
|
||||
set_stat (file_name, ¤t_stat_info.stat, 0, 0,
|
||||
(old_files_option == OVERWRITE_OLD_FILES
|
||||
? UNKNOWN_PERMSTATUS
|
||||
: ARCHIVED_PERMSTATUS),
|
||||
typeflag);
|
||||
}
|
||||
|
||||
static void
|
||||
extract_link (char *file_name)
|
||||
{
|
||||
char const *link_name;
|
||||
int interdir_made = 0;
|
||||
|
||||
if (!prepare_to_extract (file_name))
|
||||
return;
|
||||
|
||||
do
|
||||
{
|
||||
struct stat st1, st2;
|
||||
int e;
|
||||
link_name = safer_name_suffix (current_stat_info.link_name, true);
|
||||
|
||||
int status = link (link_name, file_name);
|
||||
e = errno;
|
||||
|
||||
if (status == 0)
|
||||
{
|
||||
struct delayed_symlink *ds = delayed_symlink_head;
|
||||
if (ds && lstat (link_name, &st1) == 0)
|
||||
for (; ds; ds = ds->next)
|
||||
if (ds->dev == st1.st_dev
|
||||
&& ds->ino == st1.st_ino
|
||||
&& ds->mtime == st1.st_mtime)
|
||||
{
|
||||
struct string_list *p = xmalloc (offsetof (struct string_list, string)
|
||||
+ strlen (file_name) + 1);
|
||||
strcpy (p->string, file_name);
|
||||
p->next = ds->sources;
|
||||
ds->sources = p;
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if ((e == EEXIST && strcmp (link_name, file_name) == 0)
|
||||
|| (lstat (link_name, &st1) == 0
|
||||
&& lstat (file_name, &st2) == 0
|
||||
&& st1.st_dev == st2.st_dev
|
||||
&& st1.st_ino == st2.st_ino))
|
||||
return;
|
||||
|
||||
errno = e;
|
||||
}
|
||||
while (maybe_recoverable (file_name, &interdir_made));
|
||||
|
||||
if (!(incremental_option && errno == EEXIST))
|
||||
{
|
||||
link_error (link_name, file_name);
|
||||
if (backup_option)
|
||||
undo_last_backup ();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
extract_symlink (char *file_name)
|
||||
{
|
||||
#ifdef HAVE_SYMLINK
|
||||
int status, fd;
|
||||
int interdir_made = 0;
|
||||
|
||||
if (! prepare_to_extract (file_name))
|
||||
return;
|
||||
|
||||
if (absolute_names_option
|
||||
|| ! (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name)
|
||||
|| contains_dot_dot (current_stat_info.link_name)))
|
||||
{
|
||||
while (status = symlink (current_stat_info.link_name, file_name),
|
||||
status != 0)
|
||||
if (!maybe_recoverable (file_name, &interdir_made))
|
||||
break;
|
||||
|
||||
if (status == 0)
|
||||
set_stat (file_name, ¤t_stat_info.stat, 0, 0, 0, SYMTYPE);
|
||||
else
|
||||
symlink_error (current_stat_info.link_name, file_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 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 (file_name, O_WRONLY | O_CREAT | O_EXCL, 0)) < 0)
|
||||
if (! maybe_recoverable (file_name, &interdir_made))
|
||||
break;
|
||||
|
||||
status = -1;
|
||||
if (fd < 0)
|
||||
open_error (file_name);
|
||||
else if (fstat (fd, &st) != 0)
|
||||
{
|
||||
stat_error (file_name);
|
||||
close (fd);
|
||||
}
|
||||
else if (close (fd) != 0)
|
||||
close_error (file_name);
|
||||
else
|
||||
{
|
||||
struct delayed_set_stat *h;
|
||||
struct delayed_symlink *p = xmalloc (offsetof (struct delayed_symlink, target)
|
||||
+ strlen (current_stat_info.link_name)
|
||||
+ 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_info.stat.st_uid;
|
||||
p->gid = current_stat_info.stat.st_gid;
|
||||
p->sources = xmalloc (offsetof (struct string_list, string)
|
||||
+ strlen (file_name) + 1);
|
||||
p->sources->next = 0;
|
||||
strcpy (p->sources->string, file_name);
|
||||
strcpy (p->target, current_stat_info.link_name);
|
||||
|
||||
h = delayed_set_stat_head;
|
||||
if (h && ! h->after_symlinks
|
||||
&& strncmp (file_name, h->file_name, h->file_name_len) == 0
|
||||
&& ISSLASH (file_name[h->file_name_len])
|
||||
&& (base_name (file_name) == file_name + h->file_name_len + 1))
|
||||
{
|
||||
do
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
while ((h = h->next) && ! h->after_symlinks);
|
||||
}
|
||||
|
||||
status = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (status != 0 && backup_option)
|
||||
undo_last_backup ();
|
||||
|
||||
#else
|
||||
static int warned_once;
|
||||
|
||||
if (!warned_once)
|
||||
{
|
||||
warned_once = 1;
|
||||
WARN ((0, 0, _("Attempting extraction of symbolic links as hard links")));
|
||||
}
|
||||
extract_link (file_name);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if S_IFCHR || S_IFBLK
|
||||
static void
|
||||
extract_node (char *file_name, int typeflag)
|
||||
{
|
||||
int status;
|
||||
int interdir_made = 0;
|
||||
|
||||
do
|
||||
{
|
||||
if (! prepare_to_extract (file_name))
|
||||
return;
|
||||
|
||||
status = mknod (file_name, current_stat_info.stat.st_mode,
|
||||
current_stat_info.stat.st_rdev);
|
||||
}
|
||||
while (status && maybe_recoverable (file_name, &interdir_made));
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
mknod_error (file_name);
|
||||
if (backup_option)
|
||||
undo_last_backup ();
|
||||
}
|
||||
else
|
||||
set_stat (file_name, ¤t_stat_info.stat, 0, 0, ARCHIVED_PERMSTATUS, typeflag);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if HAVE_MKFIFO || defined mkfifo
|
||||
static void
|
||||
extract_fifo (char *file_name, int typeflag)
|
||||
{
|
||||
int status;
|
||||
int interdir_made = 0;
|
||||
|
||||
if (! prepare_to_extract (file_name))
|
||||
return;
|
||||
|
||||
while ((status = mkfifo (file_name, current_stat_info.stat.st_mode)))
|
||||
if (!maybe_recoverable (file_name, &interdir_made))
|
||||
break;
|
||||
|
||||
if (status == 0)
|
||||
set_stat (file_name, ¤t_stat_info.stat, NULL, 0,
|
||||
ARCHIVED_PERMSTATUS, typeflag);
|
||||
else
|
||||
{
|
||||
mkfifo_error (file_name);
|
||||
if (backup_option)
|
||||
undo_last_backup ();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Extract a file from the archive. */
|
||||
void
|
||||
extract_archive (void)
|
||||
{
|
||||
union block *data_block;
|
||||
int fd;
|
||||
int status;
|
||||
size_t count;
|
||||
size_t written;
|
||||
int openflag;
|
||||
mode_t mode;
|
||||
off_t size;
|
||||
int interdir_made = 0;
|
||||
char typeflag;
|
||||
char *file_name;
|
||||
|
||||
@@ -666,9 +1082,7 @@ extract_archive (void)
|
||||
|
||||
switch (typeflag)
|
||||
{
|
||||
case GNUTYPE_SPARSE:
|
||||
/* Fall through. */
|
||||
|
||||
case GNUTYPE_SPARSE: /* FIXME: Shouldn't we call extract_file at once? */
|
||||
case AREGTYPE:
|
||||
case REGTYPE:
|
||||
case CONTTYPE:
|
||||
@@ -677,422 +1091,42 @@ extract_archive (void)
|
||||
suffix means a directory. */
|
||||
|
||||
if (current_stat_info.had_trailing_slash)
|
||||
goto really_dir;
|
||||
|
||||
/* FIXME: deal with protection issues. */
|
||||
|
||||
again_file:
|
||||
openflag = (O_WRONLY | O_BINARY | O_CREAT
|
||||
| (old_files_option == OVERWRITE_OLD_FILES
|
||||
? O_TRUNC
|
||||
: O_EXCL));
|
||||
mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask;
|
||||
|
||||
if (to_stdout_option)
|
||||
{
|
||||
fd = STDOUT_FILENO;
|
||||
goto extract_file;
|
||||
}
|
||||
|
||||
if (! prepare_to_extract (file_name))
|
||||
{
|
||||
skip_member ();
|
||||
if (backup_option)
|
||||
undo_last_backup ();
|
||||
break;
|
||||
}
|
||||
|
||||
#if O_CTG
|
||||
/* Contiguous files (on the Masscomp) have to specify the size in
|
||||
the open call that creates them. */
|
||||
|
||||
if (typeflag == CONTTYPE)
|
||||
fd = open (file_name, openflag | O_CTG, mode, current_stat_info.stat.st_size);
|
||||
extract_dir (file_name, typeflag);
|
||||
else
|
||||
fd = open (file_name, openflag, mode);
|
||||
|
||||
#else /* not O_CTG */
|
||||
if (typeflag == CONTTYPE)
|
||||
{
|
||||
static int conttype_diagnosed;
|
||||
|
||||
if (!conttype_diagnosed)
|
||||
{
|
||||
conttype_diagnosed = 1;
|
||||
WARN ((0, 0, _("Extracting contiguous files as regular files")));
|
||||
}
|
||||
}
|
||||
fd = open (file_name, openflag, mode);
|
||||
|
||||
#endif /* not O_CTG */
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
if (maybe_recoverable (file_name, &interdir_made))
|
||||
goto again_file;
|
||||
|
||||
open_error (file_name);
|
||||
skip_member ();
|
||||
if (backup_option)
|
||||
undo_last_backup ();
|
||||
break;
|
||||
}
|
||||
|
||||
extract_file:
|
||||
if (current_stat_info.is_sparse)
|
||||
{
|
||||
sparse_extract_file (fd, ¤t_stat_info, &size);
|
||||
}
|
||||
else
|
||||
for (size = current_stat_info.stat.st_size; size > 0; )
|
||||
{
|
||||
if (multi_volume_option)
|
||||
{
|
||||
assign_string (&save_name, current_stat_info.file_name);
|
||||
save_totsize = current_stat_info.stat.st_size;
|
||||
save_sizeleft = size;
|
||||
}
|
||||
|
||||
/* Locate data, determine max length writeable, write it,
|
||||
block that we have used the data, then check if the write
|
||||
worked. */
|
||||
|
||||
data_block = find_next_block ();
|
||||
if (! data_block)
|
||||
{
|
||||
ERROR ((0, 0, _("Unexpected EOF in archive")));
|
||||
break; /* FIXME: What happens, then? */
|
||||
}
|
||||
|
||||
written = available_space_after (data_block);
|
||||
|
||||
if (written > size)
|
||||
written = size;
|
||||
errno = 0;
|
||||
count = full_write (fd, data_block->buffer, written);
|
||||
size -= count;
|
||||
|
||||
set_next_block_after ((union block *)
|
||||
(data_block->buffer + written - 1));
|
||||
if (count != written)
|
||||
{
|
||||
write_error_details (file_name, count, written);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
skip_file (size);
|
||||
|
||||
if (multi_volume_option)
|
||||
assign_string (&save_name, 0);
|
||||
|
||||
/* If writing to stdout, don't try to do anything to the filename;
|
||||
it doesn't exist, or we don't want to touch it anyway. */
|
||||
|
||||
if (to_stdout_option)
|
||||
break;
|
||||
|
||||
status = close (fd);
|
||||
if (status < 0)
|
||||
{
|
||||
close_error (file_name);
|
||||
if (backup_option)
|
||||
undo_last_backup ();
|
||||
}
|
||||
|
||||
set_stat (file_name, ¤t_stat_info.stat, 0, 0,
|
||||
(old_files_option == OVERWRITE_OLD_FILES
|
||||
? UNKNOWN_PERMSTATUS
|
||||
: ARCHIVED_PERMSTATUS),
|
||||
typeflag);
|
||||
extract_file (file_name, typeflag);
|
||||
break;
|
||||
|
||||
case SYMTYPE:
|
||||
#ifdef HAVE_SYMLINK
|
||||
if (! prepare_to_extract (file_name))
|
||||
break;
|
||||
|
||||
if (absolute_names_option
|
||||
|| ! (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name)
|
||||
|| contains_dot_dot (current_stat_info.link_name)))
|
||||
{
|
||||
while (status = symlink (current_stat_info.link_name, file_name),
|
||||
status != 0)
|
||||
if (!maybe_recoverable (file_name, &interdir_made))
|
||||
break;
|
||||
|
||||
if (status == 0)
|
||||
set_stat (file_name, ¤t_stat_info.stat, 0, 0, 0, SYMTYPE);
|
||||
else
|
||||
symlink_error (current_stat_info.link_name, file_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 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 (file_name, O_WRONLY | O_CREAT | O_EXCL, 0),
|
||||
fd < 0)
|
||||
if (! maybe_recoverable (file_name, &interdir_made))
|
||||
break;
|
||||
|
||||
status = -1;
|
||||
if (fd < 0)
|
||||
open_error (file_name);
|
||||
else if (fstat (fd, &st) != 0)
|
||||
{
|
||||
stat_error (file_name);
|
||||
close (fd);
|
||||
}
|
||||
else if (close (fd) != 0)
|
||||
close_error (file_name);
|
||||
else
|
||||
{
|
||||
struct delayed_set_stat *h;
|
||||
struct delayed_symlink *p =
|
||||
xmalloc (offsetof (struct delayed_symlink, target)
|
||||
+ strlen (current_stat_info.link_name) + 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_info.stat.st_uid;
|
||||
p->gid = current_stat_info.stat.st_gid;
|
||||
p->sources = xmalloc (offsetof (struct string_list, string)
|
||||
+ strlen (file_name) + 1);
|
||||
p->sources->next = 0;
|
||||
strcpy (p->sources->string, file_name);
|
||||
strcpy (p->target, current_stat_info.link_name);
|
||||
|
||||
h = delayed_set_stat_head;
|
||||
if (h && ! h->after_symlinks
|
||||
&& strncmp (file_name, h->file_name, h->file_name_len) == 0
|
||||
&& ISSLASH (file_name[h->file_name_len])
|
||||
&& (base_name (file_name)
|
||||
== file_name + h->file_name_len + 1))
|
||||
{
|
||||
do
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
while ((h = h->next) && ! h->after_symlinks);
|
||||
}
|
||||
|
||||
status = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (status != 0 && backup_option)
|
||||
undo_last_backup ();
|
||||
extract_symlink (file_name);
|
||||
break;
|
||||
|
||||
#else
|
||||
{
|
||||
static int warned_once;
|
||||
|
||||
if (!warned_once)
|
||||
{
|
||||
warned_once = 1;
|
||||
WARN ((0, 0,
|
||||
_("Attempting extraction of symbolic links as hard links")));
|
||||
}
|
||||
}
|
||||
typeflag = LNKTYPE;
|
||||
/* Fall through. */
|
||||
#endif
|
||||
|
||||
|
||||
case LNKTYPE:
|
||||
if (! prepare_to_extract (file_name))
|
||||
break;
|
||||
|
||||
again_link:
|
||||
{
|
||||
char const *link_name = safer_name_suffix (current_stat_info.link_name,
|
||||
true);
|
||||
struct stat st1, st2;
|
||||
int e;
|
||||
|
||||
/* MSDOS does not implement links. However, djgpp's link() actually
|
||||
copies the file. */
|
||||
status = link (link_name, file_name);
|
||||
e = errno;
|
||||
|
||||
if (status == 0)
|
||||
{
|
||||
struct delayed_symlink *ds = delayed_symlink_head;
|
||||
if (ds && lstat (link_name, &st1) == 0)
|
||||
for (; ds; ds = ds->next)
|
||||
if (ds->dev == st1.st_dev
|
||||
&& ds->ino == st1.st_ino
|
||||
&& ds->mtime == st1.st_mtime)
|
||||
{
|
||||
struct string_list *p =
|
||||
xmalloc (offsetof (struct string_list, string)
|
||||
+ strlen (file_name) + 1);
|
||||
strcpy (p->string, file_name);
|
||||
p->next = ds->sources;
|
||||
ds->sources = p;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ((e == EEXIST && strcmp (link_name, file_name) == 0)
|
||||
|| (lstat (link_name, &st1) == 0
|
||||
&& lstat (file_name, &st2) == 0
|
||||
&& st1.st_dev == st2.st_dev
|
||||
&& st1.st_ino == st2.st_ino))
|
||||
break;
|
||||
|
||||
errno = e;
|
||||
if (maybe_recoverable (file_name, &interdir_made))
|
||||
goto again_link;
|
||||
|
||||
if (incremental_option && errno == EEXIST)
|
||||
break;
|
||||
|
||||
link_error (link_name, file_name);
|
||||
if (backup_option)
|
||||
undo_last_backup ();
|
||||
}
|
||||
extract_link (file_name);
|
||||
break;
|
||||
|
||||
#if S_IFCHR
|
||||
case CHRTYPE:
|
||||
current_stat_info.stat.st_mode |= S_IFCHR;
|
||||
goto make_node;
|
||||
extract_node (file_name, typeflag);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if S_IFBLK
|
||||
case BLKTYPE:
|
||||
current_stat_info.stat.st_mode |= S_IFBLK;
|
||||
#endif
|
||||
|
||||
#if S_IFCHR || S_IFBLK
|
||||
make_node:
|
||||
if (! prepare_to_extract (file_name))
|
||||
break;
|
||||
|
||||
status = mknod (file_name, current_stat_info.stat.st_mode,
|
||||
current_stat_info.stat.st_rdev);
|
||||
if (status != 0)
|
||||
{
|
||||
if (maybe_recoverable (file_name, &interdir_made))
|
||||
goto make_node;
|
||||
mknod_error (file_name);
|
||||
if (backup_option)
|
||||
undo_last_backup ();
|
||||
break;
|
||||
};
|
||||
set_stat (file_name, ¤t_stat_info.stat, 0, 0,
|
||||
ARCHIVED_PERMSTATUS, typeflag);
|
||||
extract_node (file_name, typeflag);
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if HAVE_MKFIFO || defined mkfifo
|
||||
case FIFOTYPE:
|
||||
if (! prepare_to_extract (file_name))
|
||||
break;
|
||||
|
||||
while (status = mkfifo (file_name, current_stat_info.stat.st_mode),
|
||||
status != 0)
|
||||
if (!maybe_recoverable (file_name, &interdir_made))
|
||||
break;
|
||||
|
||||
if (status == 0)
|
||||
set_stat (file_name, ¤t_stat_info.stat, NULL, 0,
|
||||
ARCHIVED_PERMSTATUS, typeflag);
|
||||
else
|
||||
{
|
||||
mkfifo_error (file_name);
|
||||
if (backup_option)
|
||||
undo_last_backup ();
|
||||
}
|
||||
extract_fifo (file_name, typeflag);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case DIRTYPE:
|
||||
case GNUTYPE_DUMPDIR:
|
||||
really_dir:
|
||||
if (incremental_option)
|
||||
{
|
||||
/* Read the entry and delete files that aren't listed in the
|
||||
archive. */
|
||||
|
||||
purge_directory (file_name);
|
||||
}
|
||||
else if (typeflag == GNUTYPE_DUMPDIR)
|
||||
skip_member ();
|
||||
|
||||
mode = ((current_stat_info.stat.st_mode
|
||||
| (we_are_root ? 0 : MODE_WXUSR))
|
||||
& MODE_RWX);
|
||||
|
||||
status = prepare_to_extract (file_name);
|
||||
if (status == 0)
|
||||
break;
|
||||
if (status < 0)
|
||||
goto directory_exists;
|
||||
|
||||
again_dir:
|
||||
status = mkdir (file_name, mode);
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
if (errno == EEXIST
|
||||
&& (interdir_made
|
||||
|| old_files_option == DEFAULT_OLD_FILES
|
||||
|| old_files_option == OVERWRITE_OLD_FILES))
|
||||
{
|
||||
struct stat st;
|
||||
if (stat (file_name, &st) == 0)
|
||||
{
|
||||
if (interdir_made)
|
||||
{
|
||||
repair_delayed_set_stat (file_name, &st);
|
||||
break;
|
||||
}
|
||||
if (S_ISDIR (st.st_mode))
|
||||
{
|
||||
mode = st.st_mode & ~ current_umask;
|
||||
goto directory_exists;
|
||||
}
|
||||
}
|
||||
errno = EEXIST;
|
||||
}
|
||||
|
||||
if (maybe_recoverable (file_name, &interdir_made))
|
||||
goto again_dir;
|
||||
|
||||
if (errno != EEXIST)
|
||||
{
|
||||
mkdir_error (file_name);
|
||||
if (backup_option)
|
||||
undo_last_backup ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
directory_exists:
|
||||
if (status == 0
|
||||
|| old_files_option == DEFAULT_OLD_FILES
|
||||
|| old_files_option == OVERWRITE_OLD_FILES)
|
||||
delay_set_stat (file_name, ¤t_stat_info.stat,
|
||||
MODE_RWX & (mode ^ current_stat_info.stat.st_mode),
|
||||
(status == 0
|
||||
? ARCHIVED_PERMSTATUS
|
||||
: UNKNOWN_PERMSTATUS));
|
||||
extract_dir (file_name, typeflag);
|
||||
break;
|
||||
|
||||
case GNUTYPE_VOLHDR:
|
||||
@@ -1115,7 +1149,7 @@ extract_archive (void)
|
||||
|
||||
case GNUTYPE_LONGNAME:
|
||||
case GNUTYPE_LONGLINK:
|
||||
ERROR ((0, 0, _("Visible long name error")));
|
||||
ERROR ((0, 0, _("Unexpected long name header")));
|
||||
skip_member ();
|
||||
if (backup_option)
|
||||
undo_last_backup ();
|
||||
@@ -1125,7 +1159,7 @@ extract_archive (void)
|
||||
WARN ((0, 0,
|
||||
_("%s: Unknown file type '%c', extracted as normal file"),
|
||||
quotearg_colon (file_name), typeflag));
|
||||
goto again_file;
|
||||
extract_file (file_name, typeflag);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user