Import upstream version 1.29
[debian/tar] / src / map.c
diff --git a/src/map.c b/src/map.c
new file mode 100644 (file)
index 0000000..0cd9861
--- /dev/null
+++ b/src/map.c
@@ -0,0 +1,283 @@
+/* Owner/group mapping for tar
+
+   Copyright 2015-2016 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/>.  */
+
+#include <system.h>
+#include "common.h"
+#include "wordsplit.h"
+#include <hash.h>
+#include <pwd.h>
+
+struct mapentry
+{
+  uintmax_t orig_id;
+  uintmax_t new_id;
+  char *new_name;
+};
+
+static size_t
+map_hash (void const *entry, size_t nbuckets)
+{
+  struct mapentry const *map = entry;
+  return map->orig_id % nbuckets;
+}
+
+static bool
+map_compare (void const *entry1, void const *entry2)
+{
+  struct mapentry const *map1 = entry1;
+  struct mapentry const *map2 = entry2;
+  return map1->orig_id == map2->orig_id;
+}
+
+static int
+parse_id (uintmax_t *retval,
+         char const *arg, char const *what, uintmax_t maxval,
+         char const *file, unsigned line)
+{
+  uintmax_t v;
+  char *p;
+  
+  errno = 0;
+  v = strtoumax (arg, &p, 10);
+  if (*p || errno)
+    {
+      error (0, 0, _("%s:%u: invalid %s: %s"),  file, line, what, arg);
+      return -1;
+    }
+  if (v > maxval)
+    {
+      error (0, 0, _("%s:%u: %s out of range: %s"), file, line, what, arg);
+      return -1;
+    }
+  *retval = v;
+  return 0;
+}
+
+static void
+map_read (Hash_table **ptab, char const *file,
+         uintmax_t (*name_to_id) (char const *), char const *what,
+         uintmax_t maxval)
+{
+  FILE *fp;
+  char *buf = NULL;
+  size_t bufsize = 0;
+  ssize_t n;
+  struct wordsplit ws;
+  int wsopt;
+  unsigned line;
+  int err = 0;
+  
+  fp = fopen (file, "r");
+  if (!fp)
+    open_fatal (file);
+
+  ws.ws_comment = "#";
+  wsopt = WRDSF_COMMENT | WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_SQUEEZE_DELIMS
+          | WRDSF_QUOTE;
+  line = 0;
+  while ((n = getline (&buf, &bufsize, fp)) > 0)
+    {
+      struct mapentry *ent;
+      uintmax_t orig_id, new_id;
+      char *name = NULL;
+      char *colon;
+      
+      ++line;
+      if (wordsplit (buf, &ws, wsopt))
+       FATAL_ERROR ((0, 0, _("%s:%u: cannot split line: %s"),
+                     file, line, wordsplit_strerror (&ws)));
+      wsopt |= WRDSF_REUSE;
+      if (ws.ws_wordc == 0)
+       continue;
+      if (ws.ws_wordc != 2)
+       {
+         error (0, 0, _("%s:%u: malformed line"), file, line);
+         err = 1;
+         continue;
+       }
+
+      if (ws.ws_wordv[0][0] == '+')
+       {
+         if (parse_id (&orig_id, ws.ws_wordv[0]+1, what, maxval, file, line)) 
+           {
+             err = 1;
+             continue;
+           }
+       }
+      else if (name_to_id)
+       {
+         orig_id = name_to_id (ws.ws_wordv[0]);
+         if (orig_id == UINTMAX_MAX)
+           {
+             error (0, 0, _("%s:%u: can't obtain %s of %s"),
+                    file, line, what, ws.ws_wordv[0]);
+             err = 1;
+             continue;
+           }
+       }
+
+      colon = strchr (ws.ws_wordv[1], ':');
+      if (colon)
+       {
+         if (colon > ws.ws_wordv[1])
+           name = ws.ws_wordv[1];
+         *colon++ = 0;
+         if (parse_id (&new_id, colon, what, maxval, file, line)) 
+           {
+             err = 1;
+             continue;
+           }
+       }
+      else if (ws.ws_wordv[1][0] == '+')
+       {
+         if (parse_id (&new_id, ws.ws_wordv[1], what, maxval, file, line)) 
+           {
+             err = 1;
+             continue;
+           }
+       }
+      else
+       {
+         name = ws.ws_wordv[1];
+         new_id = name_to_id (ws.ws_wordv[1]);
+         if (new_id == UINTMAX_MAX)
+           {
+             error (0, 0, _("%s:%u: can't obtain %s of %s"),
+                    file, line, what, ws.ws_wordv[1]);
+             err = 1;
+             continue;
+           }
+       }
+
+      ent = xmalloc (sizeof (*ent));
+      ent->orig_id = orig_id;
+      ent->new_id = new_id;
+      ent->new_name = name ? xstrdup (name) : NULL;
+      
+      if (!((*ptab
+            || (*ptab = hash_initialize (0, 0, map_hash, map_compare, 0)))
+           && hash_insert (*ptab, ent)))
+       xalloc_die ();
+    }
+  if (wsopt & WRDSF_REUSE)
+    wordsplit_free (&ws);
+  fclose (fp);
+  if (err)
+    FATAL_ERROR ((0, 0, _("errors reading map file")));
+}
+\f
+/* UID translation */
+
+static Hash_table *owner_map;
+
+static uintmax_t
+name_to_uid (char const *name)
+{
+  struct passwd *pw = getpwnam (name);
+  return pw ? pw->pw_uid : UINTMAX_MAX;
+}
+
+void
+owner_map_read (char const *file)
+{
+  map_read (&owner_map, file, name_to_uid, "UID", TYPE_MAXIMUM (uid_t));
+}
+
+int
+owner_map_translate (uid_t uid, uid_t *new_uid, char const **new_name)
+{
+  int rc = 1;
+  
+  if (owner_map)
+    {
+      struct mapentry ent, *res;
+  
+      ent.orig_id = uid;
+      res = hash_lookup (owner_map, &ent);
+      if (res)
+       {
+         *new_uid = res->new_id;
+         *new_name = res->new_name;
+         return 0;
+       }
+    }
+
+  if (owner_option != (uid_t) -1)
+    {
+      *new_uid = owner_option;
+      rc = 0;
+    }
+  if (owner_name_option)
+    {
+      *new_name = owner_name_option;
+      rc = 0;
+    }
+
+  return rc;
+}
+\f
+/* GID translation */
+
+static Hash_table *group_map;
+
+static uintmax_t
+name_to_gid (char const *name)
+{
+  struct group *gr = getgrnam (name);
+  return gr ? gr->gr_gid : UINTMAX_MAX;
+}
+
+void
+group_map_read (char const *file)
+{
+  map_read (&group_map, file, name_to_gid, "GID", TYPE_MAXIMUM (gid_t));
+}
+
+int
+group_map_translate (gid_t gid, gid_t *new_gid, char const **new_name)
+{
+  int rc = 1;
+  
+  if (group_map)
+    {
+      struct mapentry ent, *res;
+  
+      ent.orig_id = gid;
+      res = hash_lookup (group_map, &ent);
+      if (res)
+       {
+         *new_gid = res->new_id;
+         *new_name = res->new_name;
+         return 0;
+       }
+    }
+
+  if (group_option != (uid_t) -1)
+    {
+      *new_gid = group_option;
+      rc = 0;
+    }
+  if (group_name_option)
+    {
+      *new_name = group_name_option;
+      rc = 0;
+    }
+  
+  return rc;
+}