re-mark 1.29b-2 as not yet uploaded (merge madness!)
[debian/tar] / src / map.c
1 /* Owner/group mapping for tar
2
3    Copyright 2015-2016 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 \f
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 \f
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 }