(root)/
tar-1.35/
src/
map.c
       1  /* Owner/group mapping for tar
       2  
       3     Copyright 2015-2023 Free Software Foundation, Inc.
       4  
       5     This file is part of GNU tar.
       6  
       7     GNU tar is free software; you can redistribute it and/or modify
       8     it under the terms of the GNU General Public License as published by
       9     the Free Software Foundation; either version 3 of the License, or
      10     (at your option) any later version.
      11  
      12     GNU tar is distributed in the hope that it will be useful,
      13     but WITHOUT ANY WARRANTY; without even the implied warranty of
      14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15     GNU General Public License for more details.
      16  
      17     You should have received a copy of the GNU General Public License
      18     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      19  
      20  #include <system.h>
      21  #include "common.h"
      22  #include "wordsplit.h"
      23  #include <hash.h>
      24  #include <pwd.h>
      25  
      26  struct mapentry
      27  {
      28    uintmax_t orig_id;
      29    uintmax_t new_id;
      30    char *new_name;
      31  };
      32  
      33  static size_t
      34  map_hash (void const *entry, size_t nbuckets)
      35  {
      36    struct mapentry const *map = entry;
      37    return map->orig_id % nbuckets;
      38  }
      39  
      40  static bool
      41  map_compare (void const *entry1, void const *entry2)
      42  {
      43    struct mapentry const *map1 = entry1;
      44    struct mapentry const *map2 = entry2;
      45    return map1->orig_id == map2->orig_id;
      46  }
      47  
      48  static int
      49  parse_id (uintmax_t *retval,
      50  	  char const *arg, char const *what, uintmax_t maxval,
      51  	  char const *file, unsigned line)
      52  {
      53    uintmax_t v;
      54    char *p;
      55    
      56    errno = 0;
      57    v = strtoumax (arg, &p, 10);
      58    if (*p || errno)
      59      {
      60        error (0, 0, _("%s:%u: invalid %s: %s"),  file, line, what, arg);
      61        return -1;
      62      }
      63    if (v > maxval)
      64      {
      65        error (0, 0, _("%s:%u: %s out of range: %s"), file, line, what, arg);
      66        return -1;
      67      }
      68    *retval = v;
      69    return 0;
      70  }
      71  
      72  static void
      73  map_read (Hash_table **ptab, char const *file,
      74  	  uintmax_t (*name_to_id) (char const *), char const *what,
      75  	  uintmax_t maxval)
      76  {
      77    FILE *fp;
      78    char *buf = NULL;
      79    size_t bufsize = 0;
      80    ssize_t n;
      81    struct wordsplit ws;
      82    int wsopt;
      83    unsigned line;
      84    int err = 0;
      85    
      86    fp = fopen (file, "r");
      87    if (!fp)
      88      open_fatal (file);
      89  
      90    ws.ws_comment = "#";
      91    wsopt = WRDSF_COMMENT | WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_SQUEEZE_DELIMS
      92            | WRDSF_QUOTE;
      93    line = 0;
      94    while ((n = getline (&buf, &bufsize, fp)) > 0)
      95      {
      96        struct mapentry *ent;
      97        uintmax_t orig_id, new_id;
      98        char *name = NULL;
      99        char *colon;
     100        
     101        ++line;
     102        if (wordsplit (buf, &ws, wsopt))
     103  	FATAL_ERROR ((0, 0, _("%s:%u: cannot split line: %s"),
     104  		      file, line, wordsplit_strerror (&ws)));
     105        wsopt |= WRDSF_REUSE;
     106        if (ws.ws_wordc == 0)
     107  	continue;
     108        if (ws.ws_wordc != 2)
     109  	{
     110  	  error (0, 0, _("%s:%u: malformed line"), file, line);
     111  	  err = 1;
     112  	  continue;
     113  	}
     114  
     115        if (ws.ws_wordv[0][0] == '+')
     116  	{
     117  	  if (parse_id (&orig_id, ws.ws_wordv[0]+1, what, maxval, file, line)) 
     118  	    {
     119  	      err = 1;
     120  	      continue;
     121  	    }
     122  	}
     123        else if (name_to_id)
     124  	{
     125  	  orig_id = name_to_id (ws.ws_wordv[0]);
     126  	  if (orig_id == UINTMAX_MAX)
     127  	    {
     128  	      error (0, 0, _("%s:%u: can't obtain %s of %s"),
     129  		     file, line, what, ws.ws_wordv[0]);
     130  	      err = 1;
     131  	      continue;
     132  	    }
     133  	}
     134  
     135        colon = strchr (ws.ws_wordv[1], ':');
     136        if (colon)
     137  	{
     138  	  if (colon > ws.ws_wordv[1])
     139  	    name = ws.ws_wordv[1];
     140  	  *colon++ = 0;
     141  	  if (parse_id (&new_id, colon, what, maxval, file, line)) 
     142  	    {
     143  	      err = 1;
     144  	      continue;
     145  	    }
     146  	}
     147        else if (ws.ws_wordv[1][0] == '+')
     148  	{
     149  	  if (parse_id (&new_id, ws.ws_wordv[1], what, maxval, file, line)) 
     150  	    {
     151  	      err = 1;
     152  	      continue;
     153  	    }
     154  	}
     155        else
     156  	{
     157  	  name = ws.ws_wordv[1];
     158  	  new_id = name_to_id (ws.ws_wordv[1]);
     159  	  if (new_id == UINTMAX_MAX)
     160  	    {
     161  	      error (0, 0, _("%s:%u: can't obtain %s of %s"),
     162  		     file, line, what, ws.ws_wordv[1]);
     163  	      err = 1;
     164  	      continue;
     165  	    }
     166  	}
     167  
     168        ent = xmalloc (sizeof (*ent));
     169        ent->orig_id = orig_id;
     170        ent->new_id = new_id;
     171        ent->new_name = name ? xstrdup (name) : NULL;
     172        
     173        if (!((*ptab
     174  	     || (*ptab = hash_initialize (0, 0, map_hash, map_compare, 0)))
     175  	    && hash_insert (*ptab, ent)))
     176  	xalloc_die ();
     177      }
     178    if (wsopt & WRDSF_REUSE)
     179      wordsplit_free (&ws);
     180    fclose (fp);
     181    if (err)
     182      FATAL_ERROR ((0, 0, _("errors reading map file")));
     183  }
     184  
     185  /* UID translation */
     186  
     187  static Hash_table *owner_map;
     188  
     189  static uintmax_t
     190  name_to_uid (char const *name)
     191  {
     192    struct passwd *pw = getpwnam (name);
     193    return pw ? pw->pw_uid : UINTMAX_MAX;
     194  }
     195  
     196  void
     197  owner_map_read (char const *file)
     198  {
     199    map_read (&owner_map, file, name_to_uid, "UID", TYPE_MAXIMUM (uid_t));
     200  }
     201  
     202  int
     203  owner_map_translate (uid_t uid, uid_t *new_uid, char const **new_name)
     204  {
     205    int rc = 1;
     206    
     207    if (owner_map)
     208      {
     209        struct mapentry ent, *res;
     210    
     211        ent.orig_id = uid;
     212        res = hash_lookup (owner_map, &ent);
     213        if (res)
     214  	{
     215  	  *new_uid = res->new_id;
     216  	  *new_name = res->new_name;
     217  	  return 0;
     218  	}
     219      }
     220  
     221    if (owner_option != (uid_t) -1)
     222      {
     223        *new_uid = owner_option;
     224        rc = 0;
     225      }
     226    if (owner_name_option)
     227      {
     228        *new_name = owner_name_option;
     229        rc = 0;
     230      }
     231  
     232    return rc;
     233  }
     234  
     235  /* GID translation */
     236  
     237  static Hash_table *group_map;
     238  
     239  static uintmax_t
     240  name_to_gid (char const *name)
     241  {
     242    struct group *gr = getgrnam (name);
     243    return gr ? gr->gr_gid : UINTMAX_MAX;
     244  }
     245  
     246  void
     247  group_map_read (char const *file)
     248  {
     249    map_read (&group_map, file, name_to_gid, "GID", TYPE_MAXIMUM (gid_t));
     250  }
     251  
     252  int
     253  group_map_translate (gid_t gid, gid_t *new_gid, char const **new_name)
     254  {
     255    int rc = 1;
     256    
     257    if (group_map)
     258      {
     259        struct mapentry ent, *res;
     260    
     261        ent.orig_id = gid;
     262        res = hash_lookup (group_map, &ent);
     263        if (res)
     264  	{
     265  	  *new_gid = res->new_id;
     266  	  *new_name = res->new_name;
     267  	  return 0;
     268  	}
     269      }
     270  
     271    if (group_option != (uid_t) -1)
     272      {
     273        *new_gid = group_option;
     274        rc = 0;
     275      }
     276    if (group_name_option)
     277      {
     278        *new_name = group_name_option;
     279        rc = 0;
     280      }
     281    
     282    return rc;
     283  }