/* 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.
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
comply with umask at start of program. */
}
/* 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
make_directories (char *file_name)
{
char *cursor0 = file_name + FILESYSTEM_PREFIX_LEN (file_name);
- char *cursor; /* points into path */
+ 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);
return did_something; /* tell them to retry if we made one */
}
+static bool
+file_newer_p (const char *file_name, struct tar_stat_info *tar_stat)
+{
+ struct stat st;
+
+ if (stat (file_name, &st))
+ {
+ stat_warn (file_name);
+ return true; /* Be on the safe side */
+ }
+ if (!S_ISDIR (st.st_mode)
+ && st.st_mtime >= tar_stat->stat.st_mtime)
+ {
+ return true;
+ }
+ return false;
+}
+
/* Prepare to extract a file.
Return zero if extraction should not proceed. */
static int
-prepare_to_extract (char const *file_name, bool directory)
+prepare_to_extract (char const *file_name)
{
if (to_stdout_option)
return 0;
- if (old_files_option == UNLINK_FIRST_OLD_FILES
- && !remove_any_file (file_name, recursive_unlink_option)
- && errno && errno != ENOENT)
+ switch (old_files_option)
{
- unlink_error (file_name);
- return 0;
+ case UNLINK_FIRST_OLD_FILES:
+ if (!remove_any_file (file_name, recursive_unlink_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"), file_name));
+ return 0;
+ }
+ break;
+
+ default:
+ break;
}
return 1;
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:
errno = EEXIST;
return r;
}
+
+ case UNLINK_FIRST_OLD_FILES:
+ break;
}
case ENOENT:
int openflag;
mode_t mode;
off_t size;
- off_t file_size;
int interdir_made = 0;
char typeflag;
char *file_name;
if (verbose_option)
print_header (¤t_stat_info, -1);
- file_name = safer_name_suffix (current_stat_info.file_name, 0);
- if (strip_path_elements)
+ 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_path_elements);
+ size_t prefix_len = stripped_prefix_len (file_name, strip_name_components);
if (prefix_len == (size_t) -1)
{
skip_member ();
}
file_name += prefix_len;
}
-
+
apply_nonancestor_delayed_set_stat (file_name, 0);
/* Take a safety backup of a previously existing file. */
/* Extract the archive entry according to its type. */
- typeflag = current_header->header.typeflag;
- /*KLUDGE */
- if (current_format == POSIX_FORMAT
- && current_stat_info.archive_file_size != current_stat_info.stat.st_size)
- typeflag = GNUTYPE_SPARSE;
-
+ /* KLUDGE */
+ typeflag = sparse_member_p (¤t_stat_info) ?
+ GNUTYPE_SPARSE : current_header->header.typeflag;
+
switch (typeflag)
{
case GNUTYPE_SPARSE:
goto extract_file;
}
- if (! prepare_to_extract (file_name, 0))
+ if (! prepare_to_extract (file_name))
{
skip_member ();
if (backup_option)
}
extract_file:
- if (typeflag == GNUTYPE_SPARSE)
+ if (current_stat_info.is_sparse)
{
sparse_extract_file (fd, ¤t_stat_info, &size);
}
case SYMTYPE:
#ifdef HAVE_SYMLINK
- if (! prepare_to_extract (file_name, 0))
+ if (! prepare_to_extract (file_name))
break;
if (absolute_names_option
status = 0;
}
}
-
+
if (status != 0 && backup_option)
undo_last_backup ();
break;
#endif
case LNKTYPE:
- if (! prepare_to_extract (file_name, 0))
+ if (! prepare_to_extract (file_name))
break;
again_link:
{
- char const *link_name = safer_name_suffix (current_stat_info.link_name, 1);
+ 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 && stat (link_name, &st1) == 0)
+ 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 =
+ struct string_list *p =
xmalloc (offsetof (struct string_list, string)
+ strlen (file_name) + 1);
strcpy (p->string, file_name);
}
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;
- 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;
link_error (link_name, file_name);
if (backup_option)
#if S_IFCHR || S_IFBLK
make_node:
- if (! prepare_to_extract (file_name, 0))
+ if (! prepare_to_extract (file_name))
break;
status = mknod (file_name, current_stat_info.stat.st_mode,
#if HAVE_MKFIFO || defined mkfifo
case FIFOTYPE:
- if (! prepare_to_extract (file_name, 0))
+ if (! prepare_to_extract (file_name))
break;
while (status = mkfifo (file_name, current_stat_info.stat.st_mode),
/* Read the entry and delete files that aren't listed in the
archive. */
- gnu_restore (file_name);
+ purge_directory (file_name);
}
else if (typeflag == GNUTYPE_DUMPDIR)
skip_member ();
| (we_are_root ? 0 : MODE_WXUSR))
& MODE_RWX);
- status = prepare_to_extract (file_name, 1);
+ status = prepare_to_extract (file_name);
if (status == 0)
break;
if (status < 0)
}
errno = EEXIST;
}
-
+
if (maybe_recoverable (file_name, &interdir_made))
goto again_dir;