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.
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 <thayne....@gmail.com>
Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=136231
Forwarded: https://lists.gnu.org/archive/html/bug-tar/2011-08/msg00001.html
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;
 

Reply via email to