You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
#include <system.h>
#include <quotearg.h>
#include "common.h"
-bool we_are_root; /* true if our effective uid == 0 */
+static bool 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) */
FIXME: Should the same be done after handling -C option ? */
if (one_file_system_option)
{
- struct stat st;
+ struct stat st;
char *dir = xgetcwd ();
if (deref_stat (true, dir, &st))
else
root_device = st.st_dev;
}
-
+
/* Option -p clears the kernel umask, so it does not affect proper
restoration of file permissions. New intermediate directories will
comply with umask at start of program. */
if (stat (file_name, &st))
{
stat_warn (file_name);
- return true; /* Be on the safe side */
+ /* Be on the safe side: if the file does exist assume it is newer */
+ return errno != ENOENT;
}
if (!S_ISDIR (st.st_mode)
&& st.st_mtime >= tar_stat->stat.st_mtime)
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_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);
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;
+ 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 (errno != EEXIST)
- {
- mkdir_error (file_name);
- if (backup_option)
- undo_last_backup ();
- return;
- }
- break;
- }
+ if (maybe_recoverable (file_name, &interdir_made))
+ continue;
+
+ if (errno != EEXIST)
+ {
+ mkdir_error (file_name);
+ return 1;
+ }
+ break;
+ }
if (status == 0
|| old_files_option == DEFAULT_OLD_FILES
(status == 0
? ARCHIVED_PERMSTATUS
: UNKNOWN_PERMSTATUS));
+
+ return status;
}
fd = open (file_name, openflag, mode);
#endif /* not O_CTG */
-
+
return fd;
}
-static void
+static int
extract_file (char *file_name, int typeflag)
{
int fd;
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 (to_command_option)
{
- if (to_stdout_option)
- fd = STDOUT_FILENO;
- else
+ fd = sys_exec_command (file_name, 'f', ¤t_stat_info);
+ 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);
+ skip_member ();
+ return 0;
}
}
- while (fd < 0 && maybe_recoverable (file_name, &interdir_made));
-
- if (fd < 0)
+ else
{
- open_error (file_name);
- skip_member ();
- if (backup_option)
- undo_last_backup ();
- return;
+ do
+ fd = open_output_file (file_name, typeflag);
+ while (fd < 0 && maybe_recoverable (file_name, &interdir_made));
+
+ if (fd < 0)
+ {
+ open_error (file_name);
+ return 1;
+ }
}
-
+
if (current_stat_info.is_sparse)
sparse_extract_file (fd, ¤t_stat_info, &size);
else
{
if (multi_volume_option)
{
- assign_string (&save_name, current_stat_info.file_name);
+ assign_string (&save_name, current_stat_info.orig_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)
{
}
written = available_space_after (data_block);
-
+
if (written > size)
written = size;
errno = 0;
count = full_write (fd, data_block->buffer, written);
- size -= count;
-
+ size -= written;
+
set_next_block_after ((union block *)
(data_block->buffer + written - 1));
if (count != written)
{
- write_error_details (file_name, count, written);
+ if (!to_command_option)
+ write_error_details (file_name, count, written);
+ /* FIXME: shouldn't we restore from backup? */
break;
}
}
-
+
skip_file (size);
if (multi_volume_option)
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),
- typeflag);
-}
+ if (to_command_option)
+ sys_wait_command ();
+ else
+ 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)
+ return status;
+}
+
+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;
}
while (maybe_recoverable (file_name, &interdir_made));
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;
do
{
h->after_symlinks = 1;
-
+
if (stat (h->file_name, &st) != 0)
stat_error (h->file_name);
else
}
while ((h = h->next) && ! h->after_symlinks);
}
-
+
status = 0;
}
}
- 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))
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. */
+ extract_mangle ();
+ return 0;
+}
- 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;
- }
+static int
+extract_failure (char *file_name, int typeflag)
+{
+ return 1;
+}
- apply_nonancestor_delayed_set_stat (file_name, 0);
+typedef int (*tar_extractor_t) (char *file_name, int typeflag);
- /* Take a safety backup of a previously existing file. */
+\f
- 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;
- }
+/* Prepare to extract a file. Find extractor function.
+ Return zero if extraction should not proceed. */
- /* Extract the archive entry according to its type. */
+static int
+prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun)
+{
+ int rc = 1;
- /* KLUDGE */
- typeflag = sparse_member_p (¤t_stat_info) ?
- GNUTYPE_SPARSE : current_header->header.typeflag;
+ if (EXTRACT_OVER_PIPE)
+ 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"),
+ _("%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 or same age"),
+ 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)
+ 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. */