Improve one-top-level functionality

Make sure the changes become visible with --show-transformed-names.

* src/common.h (strip_compression_suffix): New function.
(one_top_level): Rename to one_top_level_dir. All uses changed.
* src/extract.c (extr_init): Use strip_compression_suffix.
Bail out if unable to determine top-level directory.
(maybe_prepend_name): Remove. All uses removed.
* src/tar.c (options): --one-top-level takes optional argument.
(parse_opt): Handle it.
* src/list.c (enforce_one_top_level): New function.
(transform_stat_info): Call enforce_one_top_level if required.
* src/suffix.c (compression_suffixes): List "tar" (no compression);
terminate with NULL entry.
(find_compression_suffix): New static.
(strip_compression_suffix): New function.

* doc/tar.1: Update.
* doc/tar.texi: Update.

* tests/onetop01.at: New testcase.
* tests/onetop02.at: New testcase.
* tests/onetop03.at: New testcase.
* tests/Makefile.am: Add new testcases.
* tests/testsuite.at: Likewise.
This commit is contained in:
Sergey Poznyakoff
2014-01-28 11:04:20 +02:00
parent 2af87fa277
commit 1689ed4388
12 changed files with 232 additions and 73 deletions

View File

@@ -13,7 +13,7 @@
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program. If not, see <http://www.gnu.org/licenses/>.
.TH TAR 1 "January 27, 2014" "TAR" "GNU TAR Manual"
.TH TAR 1 "January 28, 2014" "TAR" "GNU TAR Manual"
.SH NAME
tar \- an archiving utility
.SH SYNOPSIS
@@ -330,6 +330,11 @@ Don't replace existing files that are newer than their archive copies.
\fB\-\-no\-overwrite\-dir\fR
Preserve metadata of existing directories.
.TP
\fB\-\-one\-top\-level\fR[\fB=\fIDIR\fR]
Extract all files into \fIDIR\fR, or, if used without argument, into a
subdirectory named by the base name of the archive (minus standard
compression suffixes recognizable by \fB\-\-auto\-compress).
.TP
\fB\-\-overwrite\fR
Overwrite existing files when extracting.
.TP

View File

@@ -3087,15 +3087,17 @@ directories that are on different file systems from the current
directory.
@opsummary{one-top-level}
@item --one-top-level
@item --one-top-level[=@var{dir}]
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}.
(or the one passed to @option{-C}) and use it to guard against
tarbombs. In the absence of @var{dir} argument, the name of the new directory
will be equal to the base name of the archive (file name minus the
archive suffix, if recognized). Any member names that do not begin
with that directory name (after
transformations from @option{--transform} and
@option{--strip-components}) will be prefixed with it. Recognized
file name suffixes are @samp{.tar}, and any compression suffixes
recognizable by @xref{--auto-compress}.
@opsummary{overwrite}
@item --overwrite

View File

@@ -237,7 +237,7 @@ GLOBAL bool one_file_system_option;
/* Create a top-level directory for extracting based on the archive name. */
GLOBAL bool one_top_level_option;
GLOBAL char *one_top_level;
GLOBAL char *one_top_level_dir;
/* Specified value to be put into tar file in place of stat () results, or
just null and -1 if such an override should not take place. */
@@ -860,6 +860,7 @@ bool transform_program_p (void);
/* Module suffix.c */
void set_compression_program_by_suffix (const char *name, const char *defprog);
char *strip_compression_suffix (const char *name);
/* Module checkpoint.c */
void checkpoint_compile_action (const char *str);

View File

@@ -194,31 +194,15 @@ extr_init (void)
/* 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)
if (one_top_level_option && !one_top_level_dir)
{
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';
one_top_level_dir = strip_compression_suffix (base);
free (base);
if (!one_top_level_dir)
USAGE_ERROR ((0, 0, _("Cannot deduce top-level directory name; please set it explicitly with --one-top-level=DIR")));
}
}
@@ -1607,33 +1591,6 @@ prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun)
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)
@@ -1684,9 +1641,6 @@ extract_archive (void)
typeflag = sparse_member_p (&current_stat_info) ?
GNUTYPE_SPARSE : current_header->header.typeflag;
if (one_top_level_option)
maybe_prepend_name (&current_stat_info.file_name);
if (prepare_to_extract (current_stat_info.file_name, typeflag, &fun))
{
if (fun && (*fun) (current_stat_info.file_name, typeflag)

View File

@@ -115,6 +115,30 @@ transform_member_name (char **pinput, int type)
return transform_name_fp (pinput, type, decode_xform, &type);
}
static void
enforce_one_top_level (char **pfile_name)
{
char *file_name = *pfile_name;
char *p;
for (p = file_name; *p && (ISSLASH (*p) || *p == '.'); p++)
;
if (!*p)
return;
if (strncmp (p, one_top_level_dir, strlen (one_top_level_dir)) == 0)
{
int pos = strlen (one_top_level_dir);
if (ISSLASH (p[pos]) || p[pos] == 0)
return;
}
*pfile_name = new_name (one_top_level_dir, file_name);
normalize_filename_x (*pfile_name);
free (file_name);
}
void
transform_stat_info (int typeflag, struct tar_stat_info *stat_info)
{
@@ -132,6 +156,9 @@ transform_stat_info (int typeflag, struct tar_stat_info *stat_info)
case LNKTYPE:
transform_member_name (&stat_info->link_name, XFORM_LINK);
}
if (one_top_level_option)
enforce_one_top_level (&current_stat_info.file_name);
}
/* Main loop for reading an archive. */
@@ -194,6 +221,7 @@ read_and (void (*do_something) (void))
continue;
}
}
transform_stat_info (current_header->header.typeflag,
&current_stat_info);
(*do_something) ();

View File

@@ -29,6 +29,7 @@ struct compression_suffix
static struct compression_suffix compression_suffixes[] = {
#define __CAT2__(a,b) a ## b
#define S(s,p) #s, sizeof (#s) - 1, __CAT2__(p,_PROGRAM)
{ "tar", 3, NULL },
{ S(gz, GZIP) },
{ S(tgz, GZIP) },
{ S(taz, GZIP) },
@@ -44,33 +45,43 @@ static struct compression_suffix compression_suffixes[] = {
{ S(lzo, LZOP) },
{ S(xz, XZ) },
{ S(txz, XZ) }, /* Slackware */
{ NULL }
#undef S
#undef __CAT2__
};
static int nsuffixes = sizeof (compression_suffixes) /
sizeof (compression_suffixes[0]);
static const char *
find_compression_program (const char *name, const char *defprog)
static struct compression_suffix const *
find_compression_suffix (const char *name, size_t *base_len)
{
char *suf = strrchr (name, '.');
if (suf)
{
int i;
size_t len;
struct compression_suffix *p;
suf++;
len = strlen (suf);
for (i = 0; i < nsuffixes; i++)
for (p = compression_suffixes; p->suffix; p++)
{
if (compression_suffixes[i].length == len
&& memcmp (compression_suffixes[i].suffix, suf, len) == 0)
return compression_suffixes[i].program;
if (p->length == len && memcmp (p->suffix, suf, len) == 0)
{
if (*base_len)
*base_len = strlen (name) - len - 1;
return p;
}
}
}
return NULL;
}
static const char *
find_compression_program (const char *name, const char *defprog)
{
struct compression_suffix const *p = find_compression_suffix (name, NULL);
if (p)
return p->program;
return defprog;
}
@@ -81,3 +92,23 @@ set_compression_program_by_suffix (const char *name, const char *defprog)
if (program)
use_compress_program_option = program;
}
char *
strip_compression_suffix (const char *name)
{
char *s = NULL;
size_t len;
if (find_compression_suffix (name, &len))
{
if (strncmp (name + len - 4, ".tar", 4) == 0)
len -= 4;
if (len == 0)
return NULL;
s = xmalloc (len + 1);
memcpy (s, name, len);
s[len] = 0;
}
return s;
}

View File

@@ -490,7 +490,7 @@ static struct argp_option options[] = {
{"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,
{"one-top-level", ONE_TOP_LEVEL_OPTION, N_("DIR"), OPTION_ARG_OPTIONAL,
N_("create a subdirectory to avoid having loose files extracted"),
GRID+1 },
#undef GRID
@@ -1447,6 +1447,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
case ONE_TOP_LEVEL_OPTION:
one_top_level_option = true;
one_top_level_dir = arg;
break;
case 'l':

View File

@@ -144,6 +144,9 @@ TESTSUITE_AT = \
multiv07.at\
multiv08.at\
old.at\
onetop01.at\
onetop02.at\
onetop03.at\
opcomp01.at\
opcomp02.at\
opcomp03.at\

42
tests/onetop01.at Normal file
View File

@@ -0,0 +1,42 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright 2014 Free Software Foundation, Inc.
#
# This file is part of GNU tar.
#
# GNU tar is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# GNU tar is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
AT_SETUP([tar --one-top-level])
AT_KEYWORDS([extract onetop onetop01])
AT_TAR_CHECK([
AT_SORT_PREREQ
mkdir a
genfile --file a/b
genfile --file c
tar cf a.tar a c
mkdir out
cd out
tar --one-top-level -x -f ../a.tar
find . | sort
],
[0],
[.
./a
./a/b
./a/c
])
AT_CLEANUP

45
tests/onetop02.at Normal file
View File

@@ -0,0 +1,45 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright 2014 Free Software Foundation, Inc.
#
# This file is part of GNU tar.
#
# GNU tar is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# GNU tar is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
AT_SETUP([tar --one-top-level --show-transformed])
AT_KEYWORDS([extract onetop onetop02])
AT_TAR_CHECK([
AT_SORT_PREREQ
mkdir a
genfile --file a/b
genfile --file c
tar cf a.tar a c
mkdir out
cd out
tar --one-top-level --show-transformed -v -x -f ../a.tar
find . | sort
],
[0],
[a/
a/b
a/c
.
./a
./a/b
./a/c
])
AT_CLEANUP

42
tests/onetop03.at Normal file
View File

@@ -0,0 +1,42 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright 2014 Free Software Foundation, Inc.
#
# This file is part of GNU tar.
#
# GNU tar is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# GNU tar is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
AT_SETUP([tar --one-top-level --transform])
AT_KEYWORDS([extract onetop onetop02])
AT_TAR_CHECK([
AT_SORT_PREREQ
mkdir a
genfile --file a/b
genfile --file c
tar cf a.tar a c
mkdir out
cd out
tar --one-top-level --transform 's/c/d/' -x -f ../a.tar
find . | sort
],
[0],
[.
./a
./a/b
./a/d
])
AT_CLEANUP

View File

@@ -413,6 +413,11 @@ m4_include([selacl01.at])
m4_include([capabs_raw01.at])
AT_BANNER([One top level])
m4_include([onetop01.at])
m4_include([onetop02.at])
m4_include([onetop03.at])
AT_BANNER([Star tests])
m4_include([star/gtarfail.at])
m4_include([star/gtarfail2.at])