If the argument to the --owner or --group option is a name (not an ID) and
it fails look-up on the current system then GNU TAR refuses to create an
archive with the name. This seems like an artificial failure - it is
conceivable that an archive might be created on a system where a particular
owner or group does not exist and then unpack it elsewhere where the owner
or group does exist.
I noticed that someone else has logged this as a bug:
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=136231
Attached is a patch that allows archives to be created with arbitrary owner
or group names. I'm more than happy to rework the patch for it to meet any
requirements for merging.
I am not subscribed to the list so please CC me with any comments.
Description: Fix bug in --owner and --group options
Unless the --owner or --group option specified names that could be
resolved to ID numbers then the --owner or --group option would fail.
.
This patch allows --owner and --group to specify names that are
unknown on the system where the tar is created. It is also careful
to continue to accept actual ID numbers as arguments to --owner
and --group as well as names.
.
This patch has been created by dpkg-source during the package build.
Here's the last changelog entry, hopefully it gives details on why
those changes were made:
.
tar (1.25-3.1) unstable; urgency=low
.
* Non-maintainer upload.
* Make --owner=OWNER and --group=GROUP options accept owners and
groups that do not exist on the system.
.
The person named in the Author field signed this changelog entry.
Author: Thayne Harbaugh <[email protected]>
Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=136231
Last-Updated: 2011-08-08
--- a/src/create.c
+++ b/src/create.c
@@ -760,10 +760,10 @@
/* Override some stat fields, if requested to do so. */
- if (owner_option != (uid_t) -1)
- st->stat.st_uid = owner_option;
- if (group_option != (gid_t) -1)
- st->stat.st_gid = group_option;
+ if (owner_option)
+ st->stat.st_uid = owner_option_id;
+ if (group_option)
+ st->stat.st_gid = group_option_id;
if (mode_option)
st->stat.st_mode =
((st->stat.st_mode & ~MODE_ALL)
@@ -922,8 +922,15 @@
}
else
{
- uid_to_uname (st->stat.st_uid, &st->uname);
- gid_to_gname (st->stat.st_gid, &st->gname);
+ if (owner_option)
+ st->uname = xstrdup (owner_option);
+ else
+ uid_to_uname (st->stat.st_uid, &st->uname);
+
+ if (group_option)
+ st->gname = xstrdup (group_option);
+ else
+ gid_to_gname (st->stat.st_gid, &st->gname);
if (archive_format == POSIX_FORMAT
&& (strlen (st->uname) > UNAME_FIELD_SIZE
--- a/src/common.h
+++ b/src/common.h
@@ -158,8 +158,9 @@
};
/* Specified value to be put into tar file in place of stat () results, or
- just -1 if such an override should not take place. */
-GLOBAL gid_t group_option;
+ just NULL if such an override should not take place. */
+GLOBAL const char *group_option;
+GLOBAL gid_t group_option_id;
GLOBAL bool ignore_failed_read_option;
@@ -230,8 +231,9 @@
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. */
-GLOBAL uid_t owner_option;
+ just NULL if such an override should not take place. */
+GLOBAL const char *owner_option;
+GLOBAL uid_t owner_option_id;
GLOBAL bool recursive_unlink_option;
--- a/src/tar.c
+++ b/src/tar.c
@@ -1836,18 +1836,45 @@
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")));
- }
- break;
+ {
+ char *endptr;
+ unsigned long val;
+
+ group_option = arg;
+ errno = 0;
+ val = strtoul(arg, &endptr, 10);
+ group_option_id = (gid_t)val;
+
+ if (!endptr)
+ {
+ /* strtoul() should never return NULL in endptr */
+ FATAL_ERROR ((0, 0, _("Internal error parsing group option '%s'"),
+ quotearg_colon (arg)));
+ }
+ else if (! *endptr)
+ {
+ /* Full strtoul() conversion occured . . . */
+ if (ERANGE == errno || group_option_id != val)
+ {
+ /* . . . but it overflowed UL. */
+ FATAL_ERROR ((0, 0, _("Conversion overflow for group option '%s'"),
+ quotearg_colon (arg)));
+ }
+ /* . . . and it was good and already stored in group_option_id. */
+ }
+ else if (strlen (arg) >= GNAME_FIELD_SIZE)
+ {
+ /* It is a username that overflows. */
+ FATAL_ERROR ((0, 0, _("Group name '%s' too long (max characters is %d)"),
+ quotearg_colon (arg), GNAME_FIELD_SIZE - 1));
+ }
+ else if (! gname_to_gid (group_option, &group_option_id))
+ {
+ /* It is a groupname that fails look-up. */
+ group_option_id = -1;
+ }
+ }
+ break;
case MODE_OPTION:
mode_option = mode_compile (arg);
@@ -1922,18 +1948,45 @@
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")));
- }
- break;
+ {
+ char *endptr;
+ unsigned long val;
+
+ owner_option = arg;
+ errno = 0;
+ val = strtoul(arg, &endptr, 10);
+ owner_option_id = (uid_t)val;
+
+ if (!endptr)
+ {
+ /* strtoul() should never return NULL in endptr */
+ FATAL_ERROR ((0, 0, _("Internal error parsing owner option '%s'"),
+ quotearg_colon (arg)));
+ }
+ else if (! *endptr)
+ {
+ /* Full strtoul() conversion occurred . . . */
+ if (ERANGE == errno || owner_option_id != val)
+ {
+ /* . . . but it overflowed UL. */
+ FATAL_ERROR ((0, 0, _("Conversion overflow for owner '%s'"),
+ quotearg_colon (arg)));
+ }
+ /* . . . and it was good and already stored in owner_option_id. */
+ }
+ else if (strlen (arg) >= GNAME_FIELD_SIZE)
+ {
+ /* It is a username that overflows. */
+ FATAL_ERROR ((0, 0, _("Owner name '%s' too long (max characters is %d)"),
+ quotearg_colon (arg), UNAME_FIELD_SIZE - 1));
+ }
+ else if (! uname_to_uid (owner_option, &owner_option_id))
+ {
+ /* It is a username that fails look-up. */
+ owner_option_id = -1;
+ }
+ }
+ break;
case QUOTE_CHARS_OPTION:
for (;*arg; arg++)
@@ -2241,8 +2293,10 @@
tar_sparse_major = 1;
tar_sparse_minor = 0;
- owner_option = -1;
- group_option = -1;
+ owner_option = NULL;
+ owner_option_id = -1;
+ group_option = NULL;
+ group_option_id = -1;
check_device_option = true;