umask (newdir_umask); /* restore the kernel umask */
current_umask = newdir_umask;
}
+
+ /* If the user wants to guarantee that everything is under one directory,
+ determine its name now and let it be created later. */
+ if (one_top_level_option)
+ {
+ int i;
+ char *base = base_name (archive_name_array[0]);
+
+ for (i = strlen (base) - 1; i > 2; i--)
+ if (!strncmp (base + i - 3, ".tar", 4) ||
+ !strncmp (base + i - 3, ".taz", 4) ||
+ !strncmp (base + i - 3, ".tbz", 4) ||
+ !strncmp (base + i - 3, ".tb2", 4) ||
+ !strncmp (base + i - 3, ".tgz", 4) ||
+ !strncmp (base + i - 3, ".tlz", 4) ||
+ !strncmp (base + i - 3, ".txz", 4)) break;
+
+ if (i <= 3)
+ {
+ one_top_level_option = false;
+ free (base);
+ return;
+ }
+
+ one_top_level = xmalloc (i - 2);
+ strncpy (one_top_level, base, i - 3);
+ one_top_level[i - 3] = '\0';
+ free (base);
+ }
}
/* Use fchmod if possible, fchmodat otherwise. */
static void
check_time (char const *file_name, struct timespec t)
{
- if (t.tv_sec <= 0)
+ if (t.tv_sec < 0)
WARNOPT (WARN_TIMESTAMP,
(0, 0, _("%s: implausibly old time stamp %s"),
file_name, tartime (t, true)));
}
\f
-
+static bool
+is_directory_link (const char *file_name)
+{
+ struct stat st;
+ int e = errno;
+ int res;
+
+ res = (fstatat (chdir_fd, file_name, &st, AT_SYMLINK_NOFOLLOW) == 0 &&
+ S_ISLNK (st.st_mode) &&
+ fstatat (chdir_fd, file_name, &st, 0) == 0 &&
+ S_ISDIR (st.st_mode));
+ errno = e;
+ return res;
+}
+\f
/* Extractor functions for various member types */
static int
if (errno == EEXIST
&& (interdir_made
+ || keep_directory_symlink_option
|| old_files_option == DEFAULT_OLD_FILES
|| old_files_option == OVERWRITE_OLD_FILES))
{
struct stat st;
+
+ if (keep_directory_symlink_option && is_directory_link (file_name))
+ return 0;
+
if (deref_stat (file_name, &st) == 0)
{
current_mode = st.st_mode;
if (!warned_once)
{
warned_once = 1;
- WARNOPT (WARN_SYMBOLIC_CAST,
+ WARNOPT (WARN_SYMLINK_CAST,
(0, 0,
_("Attempting extraction of symbolic links as hard links")));
}
return 1;
}
+static int
+extract_skip (char *file_name, int typeflag)
+{
+ skip_member ();
+ return 0;
+}
+
typedef int (*tar_extractor_t) (char *file_name, int typeflag);
\f
ERROR ((0, 0,
_("%s: Cannot extract -- file is continued from another volume"),
quotearg_colon (current_stat_info.file_name)));
- *fun = extract_failure;
+ *fun = extract_skip;
break;
case GNUTYPE_LONGNAME:
return 1;
}
+void
+maybe_prepend_name (char **file_name)
+{
+ int i;
+
+ for (i = 0; i < strlen (*file_name); i++)
+ if (!ISSLASH ((*file_name)[i]) && (*file_name)[i] != '.') break;
+
+ if (i == strlen (*file_name))
+ return;
+
+ if (!strncmp (*file_name + i, one_top_level, strlen (one_top_level)))
+ {
+ int pos = i + strlen (one_top_level);
+ if (ISSLASH ((*file_name)[pos]) || (*file_name)[pos] == '\0') return;
+ }
+
+ char *new_name = xmalloc (strlen (one_top_level) + strlen (*file_name) + 2);
+
+ strcpy (new_name, one_top_level);
+ strcat (new_name, "/");
+ strcat (new_name, *file_name);
+
+ free (*file_name);
+ *file_name = new_name;
+}
+
/* Extract a file from the archive. */
void
extract_archive (void)
typeflag = sparse_member_p (¤t_stat_info) ?
GNUTYPE_SPARSE : current_header->header.typeflag;
+ if (one_top_level_option)
+ maybe_prepend_name (¤t_stat_info.file_name);
+
if (prepare_to_extract (current_stat_info.file_name, typeflag, &fun))
{
if (fun && (*fun) (current_stat_info.file_name, typeflag)