/* Owner/group mapping for tar Copyright 2015-2022 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 . */ #include #include "common.h" #include "wordsplit.h" #include #include 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"))); } /* 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; } /* 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; }