/* Extract files from a tar archive.
Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000,
- 2001, 2003 Free Software Foundation, Inc.
+ 2001, 2003, 2004 Free Software Foundation, Inc.
Written by John Gilmore, on 1985-11-19.
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 <system.h>
#include <quotearg.h>
+#include <errno.h>
+#include <xgetcwd.h>
#if HAVE_UTIME_H
# include <utime.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) */
we_are_root = geteuid () == 0;
same_permissions_option += we_are_root;
same_owner_option += we_are_root;
- xalloc_fail_func = extract_finish;
+
+ /* Save 'root device' to avoid purging mount points.
+ FIXME: Should the same be done after handling -C option ? */
+ if (one_file_system_option)
+ {
+ struct stat st;
+ char *dir = xgetcwd ();
+
+ if (deref_stat (true, dir, &st))
+ stat_diag (dir);
+ 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
}
/* Update the delayed_set_stat info for an intermediate directory
- created on the path to DIR_NAME. The intermediate directory turned
+ created within the file name of DIR. The intermediate directory turned
out to be the same as this directory, e.g. due to ".." or symbolic
links. *DIR_STAT_INFO is the status of the directory. */
static void
-repair_delayed_set_stat (char const *dir_name,
+repair_delayed_set_stat (char const *dir,
struct stat const *dir_stat_info)
{
struct delayed_set_stat *data;
}
ERROR ((0, 0, _("%s: Unexpected inconsistency when making directory"),
- quotearg_colon (dir_name)));
+ quotearg_colon (dir)));
}
/* After a file/link/symlink/directory creation has failed, see if
static int
make_directories (char *file_name)
{
- char *cursor0 = file_name + FILESYSTEM_PREFIX_LEN (file_name);
- char *cursor; /* points into path */
+ char *cursor0 = file_name + FILE_SYSTEM_PREFIX_LEN (file_name);
+ char *cursor; /* points into the file name */
int did_something = 0; /* did we do anything yet? */
int mode;
int invert_permissions;
int status;
-
+
for (cursor = cursor0; *cursor; cursor++)
{
if (! ISSLASH (*cursor))
if (cursor == cursor0 || ISSLASH (cursor[-1]))
continue;
- /* Avoid mkdir where last part of path is "." or "..". */
+ /* Avoid mkdir where last part of file name is "." or "..". */
if (cursor[-1] == '.'
&& (cursor == cursor0 + 1 || ISSLASH (cursor[-2])
&& (cursor == cursor0 + 2 || ISSLASH (cursor[-3])))))
continue;
- *cursor = '\0'; /* truncate the path there */
+ *cursor = '\0'; /* truncate the name there */
mode = MODE_RWX & ~ newdir_umask;
invert_permissions = we_are_root ? 0 : MODE_WXUSR & ~ mode;
status = mkdir (file_name, mode ^ invert_permissions);
else if ((errno == ENOSYS /* Automounted dirs on Solaris return
this. Reported by Warren Hyde
<Warren.Hyde@motorola.com> */
-#if MSDOS
- || errno == EACCES /* Turbo C mkdir gives a funny errno. */
-#endif
- )
+ || ERRNO_IS_EACCES) /* Turbo C mkdir gives a funny errno. */
&& access (file_name, W_OK) == 0)
continue;
return did_something; /* tell them to retry if we made one */
}
-/* Prepare to extract a file.
- Return zero if extraction should not proceed. */
-
-static int
-prepare_to_extract (char const *file_name, bool directory)
+static bool
+file_newer_p (const char *file_name, struct tar_stat_info *tar_stat)
{
- if (to_stdout_option)
- return 0;
+ struct stat st;
- if (old_files_option == UNLINK_FIRST_OLD_FILES
- && !remove_any_file (file_name, recursive_unlink_option)
- && errno && errno != ENOENT)
+ if (stat (file_name, &st))
{
- unlink_error (file_name);
- return 0;
+ stat_warn (file_name);
+ /* Be on the safe side: if the file does exist assume it is newer */
+ return errno != ENOENT;
}
-
- return 1;
+ if (!S_ISDIR (st.st_mode)
+ && st.st_mtime >= tar_stat->stat.st_mtime)
+ {
+ return true;
+ }
+ return false;
}
/* Attempt repairing what went wrong with the extraction. Delete an
static int
maybe_recoverable (char *file_name, int *interdir_made)
{
+ int e = errno;
+
if (*interdir_made)
return 0;
switch (old_files_option)
{
- default:
+ case KEEP_OLD_FILES:
return 0;
+ case KEEP_NEWER_FILES:
+ if (file_newer_p (file_name, ¤t_stat_info))
+ {
+ errno = e;
+ return 0;
+ }
+ /* FALL THROUGH */
+
case DEFAULT_OLD_FILES:
case NO_OVERWRITE_DIR_OLD_FILES:
case OVERWRITE_OLD_FILES:
{
- int r = remove_any_file (file_name, 0);
+ int r = remove_any_file (file_name, ORDINARY_REMOVE_OPTION);
errno = EEXIST;
return r;
}
+
+ case UNLINK_FIRST_OLD_FILES:
+ break;
}
case ENOENT:
}
}
-/* Translate the sparse information on the header, and in any
- subsequent extended headers, into an array of structures with true
- numbers, as opposed to character strings. Return nonzero if
- successful.
-
- This function invalidates current_header. */
-
-bool
-fill_in_sparse_array (void)
-{
- off_t sparse_data_size = current_stat_info.stat.st_size;
- off_t file_size = OFF_FROM_HEADER (current_header->oldgnu_header.realsize);
- int sparses;
- int counter;
- union block *h = current_header;
-
- init_sparsearray ();
-
- for (sparses = 0; sparses < SPARSES_IN_OLDGNU_HEADER; sparses++)
- {
- struct sparse const *s = &h->oldgnu_header.sp[sparses];
- off_t offset;
- size_t numbytes;
- if (s->numbytes[0] == '\0')
- break;
- sparsearray[sparses].offset = offset = OFF_FROM_HEADER (s->offset);
- sparsearray[sparses].numbytes = numbytes =
- SIZE_FROM_HEADER (s->numbytes);
- sparse_data_size -= numbytes;
- if (offset < 0 || file_size < offset + numbytes || sparse_data_size < 0)
- goto invalid_member;
- }
-
- if (h->oldgnu_header.isextended)
- do
- {
- h = find_next_block ();
- if (! h)
- {
- ERROR ((0, 0, _("Unexpected EOF in archive")));
- return 0;
- }
-
- for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++)
- {
- struct sparse const *s = &h->sparse_header.sp[counter];
- off_t offset;
- size_t numbytes;
- if (s->numbytes[0] == '\0')
- break;
-
- if (sparses == sp_array_size)
- {
- sp_array_size *= 2;
- sparsearray =
- xrealloc (sparsearray,
- sp_array_size * sizeof *sparsearray);
- }
-
- sparsearray[sparses].offset = offset =
- OFF_FROM_HEADER (s->offset);
- sparsearray[sparses].numbytes = numbytes =
- SIZE_FROM_HEADER (s->numbytes);
- sparse_data_size -= numbytes;
- if (offset < 0 || file_size < offset + numbytes
- || sparse_data_size < 0)
- goto invalid_member;
- sparses++;
- }
-
- set_next_block_after (h);
-
- } while (h->sparse_header.isextended);
-
- return 1;
-
- invalid_member:
- ERROR ((0, 0, _("%s: invalid sparse archive member"),
- current_stat_info.file_name));
- return 0;
-}
-
-
-static off_t
-extract_sparse_file (int fd, char const *name,
- off_t sizeleft, off_t file_size)
-{
- int sparse_ind = 0;
-
- while (sizeleft != 0)
- {
- size_t written;
- size_t count;
- union block *data_block = find_next_block ();
- if (! data_block)
- {
- ERROR ((0, 0, _("Unexpected EOF in archive")));
- return sizeleft;
- }
- if (lseek (fd, sparsearray[sparse_ind].offset, SEEK_SET) < 0)
- {
- seek_error_details (name, sparsearray[sparse_ind].offset);
- return sizeleft;
- }
- written = sparsearray[sparse_ind++].numbytes;
- while (written > BLOCKSIZE)
- {
- count = full_write (fd, data_block->buffer, BLOCKSIZE);
- written -= count;
- sizeleft -= count;
- if (count != BLOCKSIZE)
- {
- write_error_details (name, count, BLOCKSIZE);
- return sizeleft;
- }
- set_next_block_after (data_block);
- data_block = find_next_block ();
- if (! data_block)
- {
- ERROR ((0, 0, _("Unexpected EOF in archive")));
- return sizeleft;
- }
- }
-
- count = full_write (fd, data_block->buffer, written);
- sizeleft -= count;
-
- if (count != written)
- {
- write_error_details (name, count, written);
- return sizeleft;
- }
-
- set_next_block_after (data_block);
- }
-
- if (ftruncate (fd, file_size) != 0)
- truncate_error (name);
-
- return 0;
-}
-
/* Fix the statuses of all directories whose statuses need fixing, and
which are not ancestors of FILE_NAME. If AFTER_SYMLINKS is
nonzero, do this for all such directories; otherwise, stop at the
}
}
-/* Extract a file from the archive. */
-void
-extract_archive (void)
+\f
+
+/* Extractor functions for various member types */
+
+static int
+extract_dir (char *file_name, int typeflag)
{
- union block *data_block;
- int fd;
int status;
- size_t count;
- size_t written;
- int openflag;
mode_t mode;
- off_t size;
- off_t file_size;
int interdir_made = 0;
- 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 (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 ();
- if (verbose_option)
- print_header (-1);
+ mode = (current_stat_info.stat.st_mode | (we_are_root ? 0 : MODE_WXUSR)) & MODE_RWX;
- file_name = safer_name_suffix (current_stat_info.file_name, 0);
- if (strip_path_elements)
+ while ((status = mkdir (file_name, mode)))
{
- size_t prefix_len = stripped_prefix_len (file_name, strip_path_elements);
- if (prefix_len == (size_t) -1)
+ if (errno == EEXIST
+ && (interdir_made
+ || old_files_option == DEFAULT_OLD_FILES
+ || old_files_option == OVERWRITE_OLD_FILES))
{
- skip_member ();
- return;
+ 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;
}
- 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. */
-
- typeflag = current_header->header.typeflag;
- switch (typeflag)
- {
- case GNUTYPE_SPARSE:
- file_size = OFF_FROM_HEADER (current_header->oldgnu_header.realsize);
- if (! fill_in_sparse_array ())
- return;
- /* Fall through. */
+ if (maybe_recoverable (file_name, &interdir_made))
+ continue;
- case AREGTYPE:
- case REGTYPE:
- case CONTTYPE:
+ if (errno != EEXIST)
+ {
+ mkdir_error (file_name);
+ return 1;
+ }
+ break;
+ }
- /* Appears to be a file. But BSD tar uses the convention that a slash
- suffix means a directory. */
+ 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));
- if (current_stat_info.had_trailing_slash)
- goto really_dir;
+ return status;
+}
- /* FIXME: deal with protection issues. */
- again_file:
- openflag = (O_WRONLY | O_BINARY | O_CREAT
+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 = 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, 0))
- {
- skip_member ();
- if (backup_option)
- undo_last_backup ();
- break;
- }
+ 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. */
+ /* 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);
+ 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 (typeflag == CONTTYPE)
+ {
+ static int conttype_diagnosed;
- if (!conttype_diagnosed)
- {
- conttype_diagnosed = 1;
- WARN ((0, 0, _("Extracting contiguous files as regular files")));
- }
+ if (!conttype_diagnosed)
+ {
+ conttype_diagnosed = 1;
+ WARN ((0, 0, _("Extracting contiguous files as regular files")));
}
- fd = open (file_name, openflag, mode);
+ }
+ fd = open (file_name, openflag, mode);
#endif /* not O_CTG */
+ return fd;
+}
+
+static int
+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. */
+
+ if (to_stdout_option)
+ fd = STDOUT_FILENO;
+ else if (to_command_option)
+ {
+ fd = sys_exec_command (file_name, 'f', ¤t_stat_info);
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;
+ return 0;
}
+ }
+ else
+ {
+ do
+ fd = open_output_file (file_name, typeflag);
+ while (fd < 0 && maybe_recoverable (file_name, &interdir_made));
- extract_file:
- if (typeflag == GNUTYPE_SPARSE)
+ if (fd < 0)
{
- char *name;
- size_t name_length_bis;
-
- /* Kludge alert. NAME is assigned to header.name because
- during the extraction, the space that contains the header
- will get scribbled on, and the name will get munged, so any
- error messages that happen to contain the filename will look
- REAL interesting unless we do this. */
-
- name_length_bis = strlen (file_name) + 1;
- name = xmalloc (name_length_bis);
- memcpy (name, file_name, name_length_bis);
- size = extract_sparse_file (fd, name,
- current_stat_info.stat.st_size, file_size);
- free (sparsearray);
+ open_error (file_name);
+ return 1;
}
- else
- for (size = current_stat_info.stat.st_size; size > 0; )
+ }
+
+ 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)
{
- 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;
- }
+ assign_string (&save_name, current_stat_info.orig_file_name);
+ save_totsize = current_stat_info.stat.st_size;
+ save_sizeleft = size;
}
- skip_file (size);
+ /* Locate data, determine max length writeable, write it,
+ block that we have used the data, then check if the write
+ worked. */
- if (multi_volume_option)
- assign_string (&save_name, 0);
+ data_block = find_next_block ();
+ if (! data_block)
+ {
+ ERROR ((0, 0, _("Unexpected EOF in archive")));
+ break; /* FIXME: What happens, then? */
+ }
- /* 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. */
+ written = available_space_after (data_block);
- if (to_stdout_option)
- break;
+ if (written > size)
+ written = size;
+ errno = 0;
+ count = full_write (fd, data_block->buffer, written);
+ size -= written;
+
+ set_next_block_after ((union block *)
+ (data_block->buffer + written - 1));
+ if (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)
+ 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. */
- status = close (fd);
- if (status < 0)
+ if (to_stdout_option)
+ return 0;
+
+ status = close (fd);
+ if (status < 0)
+ close_error (file_name);
+
+ 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);
+
+ return status;
+}
+
+static int
+extract_link (char *file_name, int typeflag)
+{
+ char const *link_name = safer_name_suffix (current_stat_info.link_name, true);
+ int interdir_made = 0;
+
+ do
+ {
+ struct stat st1, st2;
+ int e;
+ int status = link (link_name, file_name);
+ e = errno;
+
+ if (status == 0)
{
- close_error (file_name);
- if (backup_option)
- undo_last_backup ();
+ 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 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 0;
+
+ errno = e;
+ }
+ while (maybe_recoverable (file_name, &interdir_made));
- set_stat (file_name, ¤t_stat_info.stat, 0, 0,
- (old_files_option == OVERWRITE_OLD_FILES
- ? UNKNOWN_PERMSTATUS
- : ARCHIVED_PERMSTATUS),
- typeflag);
- break;
+ if (!(incremental_option && errno == EEXIST))
+ {
+ link_error (link_name, file_name);
+ return 1;
+ }
+ return 0;
+}
- case SYMTYPE:
+static int
+extract_symlink (char *file_name, int typeflag)
+{
#ifdef HAVE_SYMLINK
- if (! prepare_to_extract (file_name, 0))
- break;
+ int status, fd;
+ int interdir_made = 0;
- if (absolute_names_option
- || ! (ISSLASH (current_stat_info.link_name
- [FILESYSTEM_PREFIX_LEN (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 (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)))
+ 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);
- }
+ if (status == 0)
+ set_stat (file_name, ¤t_stat_info.stat, 0, 0, 0, SYMTYPE);
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;
+ 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;
+ 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
+ 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))
{
- 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
{
- do
+ h->after_symlinks = 1;
+
+ if (stat (h->file_name, &st) != 0)
+ stat_error (h->file_name);
+ else
{
- 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;
- }
+ 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;
+ while ((h = h->next) && ! h->after_symlinks);
}
+
+ status = 0;
}
-
- if (status != 0 && backup_option)
- undo_last_backup ();
- break;
+ }
+
+ return status;
#else
- {
- static int warned_once;
+ 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. */
+ if (!warned_once)
+ {
+ warned_once = 1;
+ WARN ((0, 0, _("Attempting extraction of symbolic links as hard links")));
+ }
+ return extract_link (file_name, typeflag);
#endif
+}
- case LNKTYPE:
- if (! prepare_to_extract (file_name, 0))
- break;
+#if S_IFCHR || S_IFBLK
+static int
+extract_node (char *file_name, int typeflag)
+{
+ int status;
+ int interdir_made = 0;
- again_link:
- {
- char const *link_name = safer_name_suffix (current_stat_info.link_name, 1);
- struct stat st1, st2;
- int e;
+ do
+ status = mknod (file_name, current_stat_info.stat.st_mode,
+ current_stat_info.stat.st_rdev);
+ while (status && maybe_recoverable (file_name, &interdir_made));
- /* MSDOS does not implement links. However, djgpp's link() actually
- copies the file. */
- status = link (link_name, file_name);
+ if (status != 0)
+ mknod_error (file_name);
+ else
+ set_stat (file_name, ¤t_stat_info.stat, 0, 0, ARCHIVED_PERMSTATUS, typeflag);
+ return status;
+}
+#endif
- if (status == 0)
- {
- struct delayed_symlink *ds = delayed_symlink_head;
- if (ds && stat (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 (maybe_recoverable (file_name, &interdir_made))
- goto again_link;
+#if HAVE_MKFIFO || defined mkfifo
+static int
+extract_fifo (char *file_name, int typeflag)
+{
+ int status;
+ int interdir_made = 0;
- if (incremental_option && errno == EEXIST)
- break;
- e = errno;
- if (stat (link_name, &st1) == 0
- && stat (file_name, &st2) == 0
- && st1.st_dev == st2.st_dev
- && st1.st_ino == st2.st_ino)
- break;
+ while ((status = mkfifo (file_name, current_stat_info.stat.st_mode)))
+ if (!maybe_recoverable (file_name, &interdir_made))
+ break;
- link_error (link_name, file_name);
- if (backup_option)
- undo_last_backup ();
- }
+ if (status == 0)
+ set_stat (file_name, ¤t_stat_info.stat, NULL, 0,
+ ARCHIVED_PERMSTATUS, typeflag);
+ else
+ mkfifo_error (file_name);
+ return status;
+}
+#endif
+
+static int
+extract_mangle_wrapper (char *file_name, int typeflag)
+{
+ extract_mangle ();
+ return 0;
+}
+
+
+static int
+extract_failure (char *file_name, int typeflag)
+{
+ return 1;
+}
+
+typedef int (*tar_extractor_t) (char *file_name, int typeflag);
+
+\f
+
+/* Prepare to extract a file. Find extractor function.
+ Return zero if extraction should not proceed. */
+
+static int
+prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun)
+{
+ int rc = 1;
+
+ if (EXTRACT_OVER_PIPE)
+ rc = 0;
+
+ /* Select the extractor */
+ switch (typeflag)
+ {
+ 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)
+ *fun = extract_dir;
+ else
+ {
+ *fun = extract_file;
+ rc = 1;
+ }
+ break;
+
+ case SYMTYPE:
+ *fun = extract_symlink;
+ break;
+
+ case LNKTYPE:
+ *fun = extract_link;
break;
#if S_IFCHR
case CHRTYPE:
current_stat_info.stat.st_mode |= S_IFCHR;
- goto make_node;
+ *fun = extract_node;
+ 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, 0))
- 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);
+ *fun = extract_node;
break;
#endif
#if HAVE_MKFIFO || defined mkfifo
case FIFOTYPE:
- if (! prepare_to_extract (file_name, 0))
- 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 ();
- }
+ *fun = extract_fifo;
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. */
-
- gnu_restore (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, 1);
- 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));
+ *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, _("Visible long name error")));
- skip_member ();
- if (backup_option)
- undo_last_backup ();
+ ERROR ((0, 0, _("Unexpected long name header")));
+ *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));
- goto again_file;
+ *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. */
error (TAREXIT_FAILURE, 0, _("Error is not recoverable: exiting now"));
abort ();
}
+
+void
+xalloc_die (void)
+{
+ error (0, 0, "%s", _("memory exhausted"));
+ fatal_exit ();
+}