return false;
}
-/* Prepare to extract a file.
- Return zero if extraction should not proceed. */
-
-static int
-prepare_to_extract (char const *file_name)
-{
- if (to_stdout_option)
- return 0;
-
- switch (old_files_option)
- {
- case UNLINK_FIRST_OLD_FILES:
- if (!remove_any_file (file_name,
- recursive_unlink_option ? RECURSIVE_REMOVE_OPTION
- : ORDINARY_REMOVE_OPTION)
- && errno && errno != ENOENT)
- {
- unlink_error (file_name);
- return 0;
- }
- break;
-
- case KEEP_NEWER_FILES:
- if (file_newer_p (file_name, ¤t_stat_info))
- {
- WARN ((0, 0, _("Current %s is newer"), quote (file_name)));
- return 0;
- }
- break;
-
- default:
- break;
- }
-
- 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
}
}
+\f
-void
+/* Extractor functions for various member types */
+
+static int
extract_dir (char *file_name, int typeflag)
{
int status;
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;
+ 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 0;
+ }
+ 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);
+ return 1;
+ }
+ break;
+ }
- 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)
(status == 0
? ARCHIVED_PERMSTATUS
: UNKNOWN_PERMSTATUS));
+
+ return status;
}
return fd;
}
-static void
+static int
extract_file (char *file_name, int typeflag)
{
int fd;
/* FIXME: deal with protection issues. */
- do
+ if (to_stdout_option)
+ fd = STDOUT_FILENO;
+ else
{
- if (to_stdout_option)
- fd = STDOUT_FILENO;
- else
+ do
+ fd = open_output_file (file_name, typeflag);
+ while (fd < 0 && maybe_recoverable (file_name, &interdir_made));
+
+ if (fd < 0)
{
- if (! prepare_to_extract (file_name))
- {
- skip_member ();
- if (backup_option)
- undo_last_backup ();
- return;
- }
- fd = open_output_file (file_name, typeflag);
+ open_error (file_name);
+ return 1;
}
}
- 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);
(data_block->buffer + written - 1));
if (count != written)
{
- write_error_details (file_name, count, written);
+ write_error_details (file_name, count, written); /* FIXME: shouldn't we
+ restore from backup? */
break;
}
}
it doesn't exist, or we don't want to touch it anyway. */
if (to_stdout_option)
- return;
+ return 0;
status = close (fd);
if (status < 0)
- {
- close_error (file_name);
- if (backup_option)
- undo_last_backup ();
- }
+ close_error (file_name);
set_stat (file_name, ¤t_stat_info.stat, 0, 0,
- (old_files_option == OVERWRITE_OLD_FILES
- ? UNKNOWN_PERMSTATUS
- : ARCHIVED_PERMSTATUS),
+ (old_files_option == OVERWRITE_OLD_FILES ?
+ UNKNOWN_PERMSTATUS : ARCHIVED_PERMSTATUS),
typeflag);
+
+ return status;
}
-static void
-extract_link (char *file_name)
+static int
+extract_link (char *file_name, int typeflag)
{
- char const *link_name;
+ char const *link_name = safer_name_suffix (current_stat_info.link_name, true);
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;
ds->sources = p;
break;
}
- return;
+ return 0;
}
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;
+ return 0;
errno = e;
}
if (!(incremental_option && errno == EEXIST))
{
link_error (link_name, file_name);
- if (backup_option)
- undo_last_backup ();
+ return 1;
}
+ return 0;
}
-static void
-extract_symlink (char *file_name)
+static int
+extract_symlink (char *file_name, int typeflag)
{
#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)
+ while ((status = symlink (current_stat_info.link_name, file_name)))
if (!maybe_recoverable (file_name, &interdir_made))
break;
}
}
- if (status != 0 && backup_option)
- undo_last_backup ();
+ return status;
#else
static int warned_once;
warned_once = 1;
WARN ((0, 0, _("Attempting extraction of symbolic links as hard links")));
}
- extract_link (file_name);
+ return extract_link (file_name, typeflag);
#endif
}
#if S_IFCHR || S_IFBLK
-static void
+static int
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);
- }
+ 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 ();
- }
+ mknod_error (file_name);
else
set_stat (file_name, ¤t_stat_info.stat, 0, 0, ARCHIVED_PERMSTATUS, typeflag);
+ return status;
}
#endif
#if HAVE_MKFIFO || defined mkfifo
-static void
+static int
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;
set_stat (file_name, ¤t_stat_info.stat, NULL, 0,
ARCHIVED_PERMSTATUS, typeflag);
else
- {
- mkfifo_error (file_name);
- if (backup_option)
- undo_last_backup ();
- }
+ mkfifo_error (file_name);
+ return status;
}
#endif
-/* Extract a file from the archive. */
-void
-extract_archive (void)
+static int
+extract_mangle_wrapper (char *file_name, int typeflag)
{
- char typeflag;
- char *file_name;
-
- set_next_block_after (current_header);
- decode_header (current_header, ¤t_stat_info, ¤t_format, 1);
-
- if (interactive_option && !confirm ("extract", current_stat_info.file_name))
- {
- skip_member ();
- return;
- }
-
- /* Print the block from current_header and current_stat. */
-
- if (verbose_option)
- print_header (¤t_stat_info, -1);
-
- file_name = safer_name_suffix (current_stat_info.file_name, false);
- if (strip_name_components)
- {
- size_t prefix_len = stripped_prefix_len (file_name, strip_name_components);
- if (prefix_len == (size_t) -1)
- {
- skip_member ();
- return;
- }
- file_name += prefix_len;
- }
+ extract_mangle ();
+ return 0;
+}
- apply_nonancestor_delayed_set_stat (file_name, 0);
+
+static int
+extract_failure (char *file_name, int typeflag)
+{
+ return 1;
+}
- /* Take a safety backup of a previously existing file. */
+typedef int (*tar_extractor_t) (char *file_name, int typeflag);
- if (backup_option && !to_stdout_option)
- if (!maybe_backup_file (file_name, 0))
- {
- int e = errno;
- ERROR ((0, e, _("%s: Was unable to backup this file"),
- quotearg_colon (file_name)));
- skip_member ();
- return;
- }
+\f
- /* Extract the archive entry according to its type. */
+/* Prepare to extract a file. Find extractor function.
+ Return zero if extraction should not proceed. */
- /* KLUDGE */
- typeflag = sparse_member_p (¤t_stat_info) ?
- GNUTYPE_SPARSE : current_header->header.typeflag;
+static int
+prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun)
+{
+ int rc = 1;
+
+ if (to_stdout_option)
+ rc = 0;
+ /* Select the extractor */
switch (typeflag)
{
- case GNUTYPE_SPARSE: /* FIXME: Shouldn't we call extract_file at once? */
+ case GNUTYPE_SPARSE:
+ *fun = extract_file;
+ rc = 1;
+ break;
+
case AREGTYPE:
case REGTYPE:
case CONTTYPE:
-
/* Appears to be a file. But BSD tar uses the convention that a slash
suffix means a directory. */
-
if (current_stat_info.had_trailing_slash)
- extract_dir (file_name, typeflag);
+ *fun = extract_dir;
else
- extract_file (file_name, typeflag);
+ {
+ *fun = extract_file;
+ rc = 1;
+ }
break;
case SYMTYPE:
- extract_symlink (file_name);
+ *fun = extract_symlink;
break;
-
+
case LNKTYPE:
- extract_link (file_name);
+ *fun = extract_link;
break;
#if S_IFCHR
case CHRTYPE:
current_stat_info.stat.st_mode |= S_IFCHR;
- extract_node (file_name, typeflag);
+ *fun = extract_node;
break;
#endif
#if S_IFBLK
case BLKTYPE:
current_stat_info.stat.st_mode |= S_IFBLK;
- extract_node (file_name, typeflag);
+ *fun = extract_node;
break;
#endif
#if HAVE_MKFIFO || defined mkfifo
case FIFOTYPE:
- extract_fifo (file_name, typeflag);
+ *fun = extract_fifo;
break;
#endif
case DIRTYPE:
case GNUTYPE_DUMPDIR:
- extract_dir (file_name, typeflag);
+ *fun = extract_dir;
break;
case GNUTYPE_VOLHDR:
if (verbose_option)
fprintf (stdlis, _("Reading %s\n"), quote (current_stat_info.file_name));
+ *fun = NULL;
break;
case GNUTYPE_NAMES:
- extract_mangle ();
+ *fun = extract_mangle_wrapper;
break;
case GNUTYPE_MULTIVOL:
ERROR ((0, 0,
_("%s: Cannot extract -- file is continued from another volume"),
quotearg_colon (current_stat_info.file_name)));
- skip_member ();
- if (backup_option)
- undo_last_backup ();
+ *fun = extract_failure;
break;
case GNUTYPE_LONGNAME:
case GNUTYPE_LONGLINK:
ERROR ((0, 0, _("Unexpected long name header")));
- skip_member ();
- if (backup_option)
- undo_last_backup ();
+ *fun = extract_failure;
break;
default:
WARN ((0, 0,
_("%s: Unknown file type '%c', extracted as normal file"),
quotearg_colon (file_name), typeflag));
- extract_file (file_name, typeflag);
+ *fun = extract_file;
}
+
+ /* Determine whether the extraction should proceed */
+ if (rc == 0)
+ return 0;
+
+ switch (old_files_option)
+ {
+ case UNLINK_FIRST_OLD_FILES:
+ if (!remove_any_file (file_name,
+ recursive_unlink_option ? RECURSIVE_REMOVE_OPTION
+ : ORDINARY_REMOVE_OPTION)
+ && errno && errno != ENOENT)
+ {
+ unlink_error (file_name);
+ return 0;
+ }
+ break;
+
+ case KEEP_NEWER_FILES:
+ if (file_newer_p (file_name, ¤t_stat_info))
+ {
+ WARN ((0, 0, _("Current %s is newer"), quote (file_name)));
+ return 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 1;
+}
+
+/* Extract a file from the archive. */
+void
+extract_archive (void)
+{
+ char typeflag;
+ char *file_name;
+ tar_extractor_t fun;
+
+ set_next_block_after (current_header);
+ decode_header (current_header, ¤t_stat_info, ¤t_format, 1);
+
+ if (interactive_option && !confirm ("extract", current_stat_info.file_name))
+ {
+ skip_member ();
+ return;
+ }
+
+ /* Print the block from current_header and current_stat. */
+
+ if (verbose_option)
+ print_header (¤t_stat_info, -1);
+
+ file_name = safer_name_suffix (current_stat_info.file_name, false);
+ if (strip_name_components)
+ {
+ size_t prefix_len = stripped_prefix_len (file_name, strip_name_components);
+ if (prefix_len == (size_t) -1)
+ {
+ skip_member ();
+ return;
+ }
+ file_name += prefix_len;
+ }
+
+ apply_nonancestor_delayed_set_stat (file_name, 0);
+
+ /* Take a safety backup of a previously existing file. */
+
+ if (backup_option && !to_stdout_option)
+ if (!maybe_backup_file (file_name, 0))
+ {
+ int e = errno;
+ ERROR ((0, e, _("%s: Was unable to backup this file"),
+ quotearg_colon (file_name)));
+ skip_member ();
+ return;
+ }
+
+ /* Extract the archive entry according to its type. */
+
+ /* KLUDGE */
+ typeflag = sparse_member_p (¤t_stat_info) ?
+ GNUTYPE_SPARSE : current_header->header.typeflag;
+
+ if (prepare_to_extract (file_name, typeflag, &fun))
+ {
+ if (fun && (*fun) (file_name, typeflag) && backup_option)
+ undo_last_backup ();
+ }
+ else
+ skip_member ();
+
}
/* Extract the symbolic links whose final extraction were delayed. */