* Fix extracting sparse members from star archives.
+* The --one-top-level option.
+
+This new command line option tells tar that the working directory
+(or the one passed to -C) should not be populated with more than one
+name directly under it. Instead, a newly created subdirectory is
+used whose name is equal to the archive name without the extension.
+For example, foo.tar.gz would be extracted to foo.
+
\f
version 1.27 - Sergey Poznyakoff, 2013-10-05
directories that are on different file systems from the current
directory.
+@opsummary{one-top-level}
+@item --one-top-level
+Tells @command{tar} to create a new directory beneath the extraction directory
+(or the one passed to @option{-C}) and use it to guard against tarbombs. The
+name of the new directory will be equal to the name of the archive with the
+extension stripped off. If any archive names (after transformations from
+@option{--transform} and @option{--strip-components}) do not already begin with
+it, the new directory will be prepended to the names immediately before
+extraction. Recognized extensions are @samp{.tar}, @samp{.taz}, @samp{.tbz},
+@samp{.tb2}, @samp{.tgz}, @samp{.tlz} and @samp{.txz}.
+
@opsummary{overwrite}
@item --overwrite
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. */
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)
OCCURRENCE_OPTION,
OLD_ARCHIVE_OPTION,
ONE_FILE_SYSTEM_OPTION,
+ ONE_TOP_LEVEL_OPTION,
OVERWRITE_DIR_OPTION,
OVERWRITE_OPTION,
OWNER_OPTION,
{"keep-directory-symlink", KEEP_DIRECTORY_SYMLINK_OPTION, 0, 0,
N_("preserve existing symlinks to directories when extracting"),
GRID+1 },
+ {"one-top-level", ONE_TOP_LEVEL_OPTION, 0, 0,
+ N_("create a subdirectory to avoid having loose files extracted"),
+ GRID+1 },
#undef GRID
#define GRID 40
one_file_system_option = true;
break;
+ case ONE_TOP_LEVEL_OPTION:
+ one_top_level_option = true;
+ break;
+
case 'l':
check_links_option = 1;
break;
subcommand_string (subcommand_option)));
}
+ if (one_top_level_option && absolute_names_option)
+ USAGE_ERROR ((0, 0, _("--one-top-level cannot be used with --absolute-names")));
+
if (archive_names == 0)
{
/* If no archive file name given, try TAPE from the environment, or