New options: --owner-map and --group-map.

* NEWS: Update.
* doc/tar.1: Document --owner-map and --group-map
* doc/tar.texi: Likewise.

* src/map.c: New file.
* src/Makefile.am: Add map.c
* src/common.h (owner_map_read, owner_map_translate)
(group_map_read, group_map_translate): New protos.
* src/create.c (start_header): Use owner_map_translate
and group_map_translate to optionally translate user/group names/ids.
* src/tar.c: New options --owner-map and --group-map.

* tests/map.at: New file.
* tests/Makefile.am: Add map.at
* tests/testsuite.at: Include map.at.
This commit is contained in:
Sergey Poznyakoff
2015-11-02 13:00:39 +02:00
parent cdf41c383f
commit 1a615a41f5
11 changed files with 550 additions and 16 deletions
+1
View File
@@ -33,6 +33,7 @@ tar_SOURCES = \
xheader.c\
incremen.c\
list.c\
map.c\
misc.c\
names.c\
sparse.c\
+7
View File
@@ -952,4 +952,11 @@ void info_free_exclist (struct tar_stat_info *dir);
bool excluded_name (char const *name, struct tar_stat_info *st);
void exclude_vcs_ignores (void);
/* Module map.c */
void owner_map_read (char const *name);
int owner_map_translate (uid_t uid, uid_t *new_uid, char const **new_name);
void group_map_read (char const *file);
int group_map_translate (gid_t gid, gid_t *new_gid, char const **new_name);
_GL_INLINE_HEADER_END
+9 -9
View File
@@ -741,17 +741,17 @@ union block *
start_header (struct tar_stat_info *st)
{
union block *header;
char const *uname = NULL;
char const *gname = NULL;
header = write_header_name (st);
if (!header)
return NULL;
/* Override some stat fields, if requested to do so. */
owner_map_translate (st->stat.st_uid, &st->stat.st_uid, &uname);
group_map_translate (st->stat.st_gid, &st->stat.st_gid, &gname);
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 (mode_option)
st->stat.st_mode =
((st->stat.st_mode & ~MODE_ALL)
@@ -910,13 +910,13 @@ start_header (struct tar_stat_info *st)
}
else
{
if (owner_name_option)
st->uname = xstrdup (owner_name_option);
if (uname)
st->uname = xstrdup (uname);
else
uid_to_uname (st->stat.st_uid, &st->uname);
if (group_name_option)
st->gname = xstrdup (group_name_option);
if (gname)
st->gname = xstrdup (gname);
else
gid_to_gname (st->stat.st_gid, &st->gname);
+283
View File
@@ -0,0 +1,283 @@
/* Owner/group mapping for tar
Copyright 2015 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")));
}
/* 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;
}
+14
View File
@@ -298,6 +298,7 @@ enum
FORCE_LOCAL_OPTION,
FULL_TIME_OPTION,
GROUP_OPTION,
GROUP_MAP_OPTION,
IGNORE_CASE_OPTION,
IGNORE_COMMAND_ERROR_OPTION,
IGNORE_FAILED_READ_OPTION,
@@ -340,6 +341,7 @@ enum
OVERWRITE_DIR_OPTION,
OVERWRITE_OPTION,
OWNER_OPTION,
OWNER_MAP_OPTION,
PAX_OPTION,
POSIX_OPTION,
PRESERVE_OPTION,
@@ -534,6 +536,10 @@ static struct argp_option options[] = {
N_("force NAME as owner for added files"), GRID+1 },
{"group", GROUP_OPTION, N_("NAME"), 0,
N_("force NAME as group for added files"), GRID+1 },
{"owner-map", OWNER_MAP_OPTION, N_("FILE"), 0,
N_("use FILE to map file owner UIDs and names"), GRID+1 },
{"group-map", GROUP_MAP_OPTION, N_("FILE"), 0,
N_("use FILE to map file owner GIDs and names"), GRID+1 },
{"mtime", MTIME_OPTION, N_("DATE-OR-FILE"), 0,
N_("set mtime for added files from DATE-OR-FILE"), GRID+1 },
{"mode", MODE_OPTION, N_("CHANGES"), 0,
@@ -1996,6 +2002,10 @@ parse_opt (int key, char *arg, struct argp_state *state)
}
break;
case GROUP_MAP_OPTION:
group_map_read (arg);
break;
case MODE_OPTION:
mode_option = mode_compile (arg);
if (!mode_option)
@@ -2090,6 +2100,10 @@ parse_opt (int key, char *arg, struct argp_state *state)
}
break;
case OWNER_MAP_OPTION:
owner_map_read (arg);
break;
case QUOTE_CHARS_OPTION:
for (;*arg; arg++)
set_char_quoting (NULL, *arg, 1);