diff --git a/NEWS b/NEWS index 12c1dd63..06955f4f 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,18 @@ -GNU tar NEWS - User visible changes. 2011-03-12 +GNU tar NEWS - User visible changes. 2011-08-13 Please send GNU tar bug reports to +version ?.? - ?, 201?-??-?? + +* New features + +** --owner and --group names and numbers + +The --owner and --group options now accept operands of the form +NAME:NUM, so that you can specify both symbolic name and numeric ID +for owner and group. In these options, NAME no longer needs to be +present in the current host's user and group databases. + version 1.26 - Sergey Poznyakoff, 2011-03-12 * Bugfixes diff --git a/doc/tar.texi b/doc/tar.texi index 357c8c1c..48e8c3cc 100644 --- a/doc/tar.texi +++ b/doc/tar.texi @@ -2713,9 +2713,9 @@ tutorial}). @item --group=@var{group} Files added to the @command{tar} archive will have a group @acronym{ID} of @var{group}, -rather than the group from the source file. @var{group} is first decoded -as a group symbolic name, but if this interpretation fails, it has to be -a decimal numeric group @acronym{ID}. @xref{override}. +rather than the group from the source file. @var{group} can specify a +symbolic name, or a numeric @acronym{ID}, or both as +@var{name}:@var{id}. @xref{override}. Also see the comments for the @option{--owner=@var{user}} option. @@ -3082,8 +3082,8 @@ from an archive. @xref{Overwrite Old Files}. Specifies that @command{tar} should use @var{user} as the owner of members when creating archives, instead of the user associated with the source -file. @var{user} is first decoded as a user symbolic name, but if -this interpretation fails, it has to be a decimal numeric user @acronym{ID}. +file. @var{user} can specify a symbolic name, or a numeric +@acronym{ID}, or both as @var{name}:@var{id}. @xref{override}. This option does not affect extraction from archives. @@ -4947,8 +4947,22 @@ tar: Option --mtime: Treating date `yesterday' as 2006-06-20 Specifies that @command{tar} should use @var{user} as the owner of members when creating archives, instead of the user associated with the source -file. The argument @var{user} can be either an existing user symbolic -name, or a decimal numeric user @acronym{ID}. +file. + +If @var{user} contains a colon, it is taken to be of the form +@var{name}:@var{id} where a nonempty @var{name} specifies the user +name and a nonempty @var{id} specifies the decimal numeric user +@acronym{ID}. If @var{user} does not contain a colon, it is taken to +be a user number if it is one or more decimal digits; otherwise it is +taken to be a user name. + +If a name is given but no number, the number is inferred from the +current host's user database if possible, and the file's user number +is used otherwise. If a number is given but no name, the name is +inferred from the number if possible, and an empty name is used +otherwise. If both name and number are given, the user database is +not consulted, and the name and number need not be valid on the +current host. There is no value indicating a missing number, and @samp{0} usually means @code{root}. Some people like to force @samp{0} as the value to offer in @@ -4971,8 +4985,9 @@ $ @kbd{tar -c -f archive.tar --owner=root .} @opindex group Files added to the @command{tar} archive will have a group @acronym{ID} of @var{group}, -rather than the group from the source file. The argument @var{group} -can be either an existing group symbolic name, or a decimal numeric group @acronym{ID}. +rather than the group from the source file. As with @option{--owner}, +the argument @var{group} can be an existing group symbolic name, or a +decimal numeric group @acronym{ID}, or @var{name}:@var{id}. @end table @node Ignore Failed Read diff --git a/src/common.h b/src/common.h index 0b9bd7a1..8c81cad0 100644 --- a/src/common.h +++ b/src/common.h @@ -158,7 +158,8 @@ enum exclusion_tag_type }; /* Specified value to be put into tar file in place of stat () results, or - just -1 if such an override should not take place. */ + just null and -1 if such an override should not take place. */ +GLOBAL char const *group_name_option; GLOBAL gid_t group_option; GLOBAL bool ignore_failed_read_option; @@ -230,7 +231,8 @@ GLOBAL bool numeric_owner_option; GLOBAL bool one_file_system_option; /* Specified value to be put into tar file in place of stat () results, or - just -1 if such an override should not take place. */ + just null and -1 if such an override should not take place. */ +GLOBAL char const *owner_name_option; GLOBAL uid_t owner_option; GLOBAL bool recursive_unlink_option; diff --git a/src/create.c b/src/create.c index 43b5a4c2..9839e1fd 100644 --- a/src/create.c +++ b/src/create.c @@ -920,8 +920,15 @@ start_header (struct tar_stat_info *st) } else { - uid_to_uname (st->stat.st_uid, &st->uname); - gid_to_gname (st->stat.st_gid, &st->gname); + if (owner_name_option) + st->uname = xstrdup (owner_name_option); + else + uid_to_uname (st->stat.st_uid, &st->uname); + + if (group_name_option) + st->gname = xstrdup (group_name_option); + else + gid_to_gname (st->stat.st_gid, &st->gname); if (archive_format == POSIX_FORMAT && (strlen (st->uname) > UNAME_FIELD_SIZE diff --git a/src/tar.c b/src/tar.c index 95781624..6d370441 100644 --- a/src/tar.c +++ b/src/tar.c @@ -1364,6 +1364,58 @@ expand_pax_option (struct tar_args *targs, const char *arg) } +static uintmax_t +parse_owner_group (char *arg, uintmax_t field_max, char const **name_option) +{ + strtol_error err; + uintmax_t u = UINTMAX_MAX; + char *end; + char const *name = 0; + char const *invalid_num = 0; + char *colon = strchr (arg, ':'); + + if (colon) + { + char const *num = colon + 1; + *colon = '\0'; + if (*arg) + name = arg; + if (num && (! (xstrtoumax (num, &end, 10, &u, "") == LONGINT_OK + && u <= field_max))) + invalid_num = num; + } + else + { + uintmax_t u1; + switch ('0' <= *arg && *arg <= '9' + ? xstrtoumax (arg, &end, 10, &u1, "") + : LONGINT_INVALID) + { + default: + name = arg; + break; + + case LONGINT_OK: + if (u1 <= field_max) + { + u = u1; + break; + } + /* Fall through. */ + case LONGINT_OVERFLOW: + invalid_num = arg; + break; + } + } + + if (invalid_num) + FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (invalid_num), + _("Invalid owner or group ID"))); + if (name) + *name_option = name; + return u; +} + #define TAR_SIZE_SUFFIXES "bBcGgkKMmPTtw" static error_t @@ -1836,17 +1888,18 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case GROUP_OPTION: - if (! (strlen (arg) < GNAME_FIELD_SIZE - && gname_to_gid (arg, &group_option))) - { - uintmax_t g; - if (xstrtoumax (arg, 0, 10, &g, "") == LONGINT_OK - && g == (gid_t) g) - group_option = g; - else - FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (arg), - _("Invalid group"))); - } + { + uintmax_t u = parse_owner_group (arg, TYPE_MAXIMUM (gid_t), + &group_name_option); + if (u == UINTMAX_MAX) + { + group_option = -1; + if (group_name_option) + gname_to_gid (group_name_option, &group_option); + } + else + group_option = u; + } break; case MODE_OPTION: @@ -1922,17 +1975,18 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case OWNER_OPTION: - if (! (strlen (arg) < UNAME_FIELD_SIZE - && uname_to_uid (arg, &owner_option))) - { - uintmax_t u; - if (xstrtoumax (arg, 0, 10, &u, "") == LONGINT_OK - && u == (uid_t) u) - owner_option = u; - else - FATAL_ERROR ((0, 0, "%s: %s", quotearg_colon (arg), - _("Invalid owner"))); - } + { + uintmax_t u = parse_owner_group (arg, TYPE_MAXIMUM (uid_t), + &owner_name_option); + if (u == UINTMAX_MAX) + { + owner_option = -1; + if (owner_name_option) + uname_to_uid (owner_name_option, &owner_option); + } + else + owner_option = u; + } break; case QUOTE_CHARS_OPTION: @@ -2241,8 +2295,8 @@ decode_options (int argc, char **argv) tar_sparse_major = 1; tar_sparse_minor = 0; - owner_option = -1; - group_option = -1; + owner_option = -1; owner_name_option = NULL; + group_option = -1; group_name_option = NULL; check_device_option = true; diff --git a/tests/Makefile.am b/tests/Makefile.am index d9c9aae5..7acc9d6d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -128,6 +128,7 @@ TESTSUITE_AT = \ old.at\ options.at\ options02.at\ + owner.at\ pipe.at\ recurse.at\ rename01.at\ diff --git a/tests/owner.at b/tests/owner.at new file mode 100644 index 00000000..799bd43d --- /dev/null +++ b/tests/owner.at @@ -0,0 +1,45 @@ +# Process this file with autom4te to create testsuite. -*- Autotest -*- + +# Test suite for GNU tar. +# Copyright 2011 Free Software Foundation, Inc. + +# This program 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, or (at your option) +# any later version. + +# This program 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +# Check the --owner and --group options. + +AT_SETUP([--owner and --group]) +AT_KEYWORDS([owner]) + +AT_TAR_CHECK([ +export TZ=UTC0 + +genfile --file a + +tar --owner="Joe the Plumber:1234" \ + --group="Plumber's Union:5678" \ + --mtime='@0' \ + -cf arc a + +tar -tvf arc +tar --numeric-owner -tvf arc +], +[0], +[-rw-r--r-- Joe the Plumber/Plumber's Union 0 1970-01-01 00:00 a +-rw-r--r-- 1234/5678 0 1970-01-01 00:00 a +], +[],[],[],[gnu]) + +AT_CLEANUP diff --git a/tests/testsuite.at b/tests/testsuite.at index 9f262ec8..35f8c2f7 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -222,6 +222,7 @@ m4_include([multiv07.at]) m4_include([multiv08.at]) m4_include([old.at]) +m4_include([owner.at]) m4_include([recurse.at])