Merge r283,r423 from libarchive.googlecode.com: Use libarchive's new

archive_read_disk API to pull metadata off of disk.  This
removes a lot of platform-specific knowledge of things like
ACLs, file flags, and extended attributes from bsdtar.
This commit is contained in:
Tim Kientzle 2009-03-08 05:22:50 +00:00
parent e8f0b45249
commit 3ae673ed49
3 changed files with 128 additions and 531 deletions

View File

@ -92,7 +92,8 @@ struct bsdtar {
* Data for various subsystems. Full definitions are located in
* the file where they are used.
*/
struct archive_entry_linkresolver *resolver;
struct archive *diskreader; /* for write.c */
struct archive_entry_linkresolver *resolver; /* for write.c */
struct archive_dir *archive_dir; /* for write.c */
struct name_cache *gname_cache; /* for write.c */
char *buff; /* for write.c */

View File

@ -188,7 +188,7 @@ excluded(struct bsdtar *bsdtar, const char *pathname)
* gtar. In particular, 'a*b' will match 'foo/a1111/222b/bar'
*
*/
int
static int
match_exclusion(struct match *match, const char *pathname)
{
const char *p;

View File

@ -44,7 +44,8 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_EXT2FS_EXT2_FS_H
#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
/* This header exists but is broken on Cygwin. */
#include <ext2fs/ext2_fs.h>
#endif
#ifdef HAVE_FCNTL_H
@ -120,25 +121,10 @@ static int archive_names_from_file_helper(struct bsdtar *bsdtar,
const char *line);
static int copy_file_data(struct bsdtar *bsdtar,
struct archive *a, struct archive *ina);
static void create_cleanup(struct bsdtar *);
static void free_cache(struct name_cache *cache);
static const char * lookup_gname(struct bsdtar *bsdtar, gid_t gid);
static int lookup_gname_helper(struct bsdtar *bsdtar,
const char **name, id_t gid);
static const char * lookup_uname(struct bsdtar *bsdtar, uid_t uid);
static int lookup_uname_helper(struct bsdtar *bsdtar,
const char **name, id_t uid);
static int new_enough(struct bsdtar *, const char *path,
const struct stat *);
static void setup_acls(struct bsdtar *, struct archive_entry *,
const char *path);
static void setup_xattrs(struct bsdtar *, struct archive_entry *,
const char *path);
static void test_for_append(struct bsdtar *);
static void write_archive(struct archive *, struct bsdtar *);
static void write_entry(struct bsdtar *, struct archive *,
const struct stat *, const char *pathname,
const char *accpath);
static void write_entry_backend(struct bsdtar *, struct archive *,
struct archive_entry *);
static int write_file_data(struct bsdtar *, struct archive *,
@ -417,6 +403,9 @@ write_archive(struct archive *a, struct bsdtar *bsdtar)
bsdtar_errc(bsdtar, 1, 0, "cannot create link resolver");
archive_entry_linkresolver_set_strategy(bsdtar->resolver,
archive_format(a));
if ((bsdtar->diskreader = archive_read_disk_new()) == NULL)
bsdtar_errc(bsdtar, 1, 0, "Cannot create read_disk object");
archive_read_disk_set_standard_lookup(bsdtar->diskreader);
if (bsdtar->names_from_file != NULL)
archive_names_from_file(bsdtar, a);
@ -458,7 +447,6 @@ write_archive(struct archive *a, struct bsdtar *bsdtar)
archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry);
}
create_cleanup(bsdtar);
if (archive_write_close(a)) {
bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a));
bsdtar->return_value = 1;
@ -467,6 +455,10 @@ write_archive(struct archive *a, struct bsdtar *bsdtar)
cleanup:
/* Free file data buffer. */
free(bsdtar->buff);
archive_entry_linkresolver_free(bsdtar->resolver);
bsdtar->resolver = NULL;
archive_read_finish(bsdtar->diskreader);
bsdtar->diskreader = NULL;
if (bsdtar->option_totals) {
fprintf(stderr, "Total bytes written: " BSDTAR_FILESIZE_PRINTF "\n",
@ -636,6 +628,7 @@ copy_file_data(struct bsdtar *bsdtar, struct archive *a, struct archive *ina)
static void
write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
{
struct archive_entry *entry = NULL, *spare_entry = NULL;
struct tree *tree;
char symlink_mode = bsdtar->symlink_mode;
dev_t first_dev = 0;
@ -651,8 +644,10 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
}
while ((tree_ret = tree_next(tree))) {
int r;
const char *name = tree_current_path(tree);
const struct stat *st = NULL, *lst = NULL;
const struct stat *st = NULL; /* info to use for this entry */
const struct stat *lst = NULL; /* lstat() information */
int descend;
if (tree_ret == TREE_ERROR_FATAL)
@ -666,31 +661,56 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
}
if (tree_ret != TREE_REGULAR)
continue;
/*
* If this file/dir is excluded by a filename
* pattern, skip it.
*/
if (excluded(bsdtar, name))
continue;
/*
* Get lstat() info from the tree library.
*/
lst = tree_current_lstat(tree);
if (lst == NULL) {
/* Couldn't lstat(); must not exist. */
bsdtar_warnc(bsdtar, errno, "%s: Cannot stat", name);
/*
* Report an error via the exit code if the failed
* path is a prefix of what the user provided via
* the command line. (Testing for string equality
* here won't work due to trailing '/' characters.)
*/
if (memcmp(name, path, strlen(name)) == 0)
bsdtar->return_value = 1;
/* Return error if files disappear during traverse. */
bsdtar->return_value = 1;
continue;
}
if (S_ISLNK(lst->st_mode))
/*
* Distinguish 'L'/'P'/'H' symlink following.
*/
switch(symlink_mode) {
case 'H':
/* 'H': After the first item, rest like 'P'. */
symlink_mode = 'P';
/* 'H': First item (from command line) like 'L'. */
/* FALLTHROUGH */
case 'L':
/* 'L': Do descend through a symlink to dir. */
descend = tree_current_is_dir(tree);
/* 'L': Follow symlinks to files. */
archive_read_disk_set_symlink_logical(bsdtar->diskreader);
/* 'L': Archive symlinks as targets, if we can. */
st = tree_current_stat(tree);
/* Default: descend into any dir or symlink to dir. */
/* We'll adjust this later on. */
descend = 0;
if ((st != NULL) && S_ISDIR(st->st_mode))
descend = 1;
if ((lst != NULL) && S_ISDIR(lst->st_mode))
descend = 1;
if (st != NULL)
break;
/* If stat fails, we have a broken symlink;
* in that case, don't follow the link. */
/* FALLTHROUGH */
default:
/* 'P': Don't descend through a symlink to dir. */
descend = tree_current_is_physical_dir(tree);
/* 'P': Don't follow symlinks to files. */
archive_read_disk_set_symlink_physical(bsdtar->diskreader);
/* 'P': Archive symlinks as symlinks. */
st = lst;
break;
}
/*
* If user has asked us not to cross mount points,
@ -702,10 +722,40 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
dev_recorded = 1;
}
if (bsdtar->option_dont_traverse_mounts) {
if (lst != NULL && lst->st_dev != first_dev)
if (lst->st_dev != first_dev)
descend = 0;
}
/*
* In -u mode, check that the file is newer than what's
* already in the archive; in all modes, obey --newerXXX flags.
*/
if (!new_enough(bsdtar, name, st))
continue;
archive_entry_free(entry);
entry = archive_entry_new();
archive_entry_set_pathname(entry, name);
archive_entry_copy_sourcepath(entry,
tree_current_access_path(tree));
/* Populate the archive_entry with metadata from the disk. */
/* XXX TODO: Arrange to open a regular file before
* calling this so we can pass in an fd and shorten
* the race to query metadata. The linkify dance
* makes this more complex than it might sound. */
r = archive_read_disk_entry_from_file(bsdtar->diskreader,
entry, -1, st);
if (r != ARCHIVE_OK)
bsdtar_warnc(bsdtar, archive_errno(bsdtar->diskreader),
archive_error_string(bsdtar->diskreader));
if (r < ARCHIVE_WARN)
continue;
/* XXX TODO: Just use flag data from entry; avoid the
* duplicate check here. */
/*
* If this file/dir is flagged "nodump" and we're
* honoring such flags, skip this file/dir.
@ -731,63 +781,56 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path)
}
#endif
/*
* If this file/dir is excluded by a filename
* pattern, skip it.
*/
if (excluded(bsdtar, name))
continue;
/*
* If the user vetoes this file/directory, skip it.
* We want this to be fairly late; if some other
* check would veto this file, we shouldn't bother
* the user with it.
*/
if (bsdtar->option_interactive &&
!yes("add '%s'", name))
continue;
/*
* If this is a dir, decide whether or not to recurse.
*/
if (bsdtar->option_no_subdirs)
descend = 0;
/*
* Distinguish 'L'/'P'/'H' symlink following.
*/
switch(symlink_mode) {
case 'H':
/* 'H': After the first item, rest like 'P'. */
symlink_mode = 'P';
/* 'H': First item (from command line) like 'L'. */
/* FALLTHROUGH */
case 'L':
/* 'L': Do descend through a symlink to dir. */
/* 'L': Archive symlink to file as file. */
lst = tree_current_stat(tree);
/* If stat fails, we have a broken symlink;
* in that case, archive the link as such. */
if (lst == NULL)
lst = tree_current_lstat(tree);
break;
default:
/* 'P': Don't descend through a symlink to dir. */
if (!S_ISDIR(lst->st_mode))
descend = 0;
/* 'P': Archive symlink to file as symlink. */
/* lst = tree_current_lstat(tree); */
break;
}
if (descend)
/* Note: if user vetoes, we won't descend. */
if (descend && !bsdtar->option_no_subdirs)
tree_descend(tree);
/*
* Write the entry. Note that write_entry() handles
* pathname editing and newness testing.
* Rewrite the pathname to be archived. If rewrite
* fails, skip the entry.
*/
write_entry(bsdtar, a, lst, name,
tree_current_access_path(tree));
if (edit_pathname(bsdtar, entry))
continue;
/* Display entry as we process it.
* This format is required by SUSv2. */
if (bsdtar->verbose)
safe_fprintf(stderr, "a %s",
archive_entry_pathname(entry));
/* Non-regular files get archived with zero size. */
if (!S_ISREG(st->st_mode))
archive_entry_set_size(entry, 0);
/* Record what we're doing, for SIGINFO / SIGUSR1. */
siginfo_setinfo(bsdtar, "adding",
archive_entry_pathname(entry), archive_entry_size(entry));
archive_entry_linkify(bsdtar->resolver, &entry, &spare_entry);
/* Handle SIGINFO / SIGUSR1 request if one was made. */
siginfo_printinfo(bsdtar, 0);
while (entry != NULL) {
write_entry_backend(bsdtar, a, entry);
archive_entry_free(entry);
entry = spare_entry;
spare_entry = NULL;
}
if (bsdtar->verbose)
fprintf(stderr, "\n");
}
archive_entry_free(entry);
tree_close(tree);
}
@ -846,117 +889,6 @@ write_entry_backend(struct bsdtar *bsdtar, struct archive *a,
close(fd);
}
/*
* Add a single filesystem object to the archive.
*/
static void
write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st,
const char *pathname, const char *accpath)
{
struct archive_entry *entry, *sparse_entry;
static char linkbuffer[PATH_MAX+1];
entry = archive_entry_new();
archive_entry_set_pathname(entry, pathname);
archive_entry_copy_sourcepath(entry, accpath);
/*
* Rewrite the pathname to be archived. If rewrite
* fails, skip the entry.
*/
if (edit_pathname(bsdtar, entry))
goto abort;
/*
* In -u mode, check that the file is newer than what's
* already in the archive; in all modes, obey --newerXXX flags.
*/
if (!new_enough(bsdtar, archive_entry_pathname(entry), st))
goto abort;
/* Display entry as we process it. This format is required by SUSv2. */
if (bsdtar->verbose)
safe_fprintf(stderr, "a %s", archive_entry_pathname(entry));
/* Read symbolic link information. */
if ((st->st_mode & S_IFMT) == S_IFLNK) {
int lnklen;
lnklen = readlink(accpath, linkbuffer, PATH_MAX);
if (lnklen < 0) {
if (!bsdtar->verbose)
bsdtar_warnc(bsdtar, errno,
"%s: Couldn't read symbolic link",
pathname);
else
safe_fprintf(stderr,
": Couldn't read symbolic link: %s",
strerror(errno));
goto cleanup;
}
linkbuffer[lnklen] = 0;
archive_entry_set_symlink(entry, linkbuffer);
}
/* Look up username and group name. */
archive_entry_set_uname(entry, lookup_uname(bsdtar, st->st_uid));
archive_entry_set_gname(entry, lookup_gname(bsdtar, st->st_gid));
#ifdef HAVE_STRUCT_STAT_ST_FLAGS
/* XXX TODO: archive_entry_copy_stat() should copy st_flags
* on platforms that support it. This should go away then. */
if (st->st_flags != 0)
archive_entry_set_fflags(entry, st->st_flags, 0);
#endif
#ifdef EXT2_IOC_GETFLAGS
/* XXX TODO: Find a way to merge this with the
* flags fetch for nodump support earlier. */
if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
int fd = open(accpath, O_RDONLY | O_NONBLOCK);
if (fd >= 0) {
unsigned long stflags;
int r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags);
close(fd);
if (r == 0 && stflags != 0)
archive_entry_set_fflags(entry, stflags, 0);
}
}
#endif
archive_entry_copy_stat(entry, st);
setup_acls(bsdtar, entry, accpath);
setup_xattrs(bsdtar, entry, accpath);
/* Non-regular files get archived with zero size. */
if (!S_ISREG(st->st_mode))
archive_entry_set_size(entry, 0);
/* Record what we're doing, for the benefit of SIGINFO / SIGUSR1. */
siginfo_setinfo(bsdtar, "adding", archive_entry_pathname(entry),
archive_entry_size(entry));
archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry);
/* Handle SIGINFO / SIGUSR1 request if one was made. */
siginfo_printinfo(bsdtar, 0);
while (entry != NULL) {
write_entry_backend(bsdtar, a, entry);
archive_entry_free(entry);
entry = sparse_entry;
sparse_entry = NULL;
}
cleanup:
if (bsdtar->verbose)
fprintf(stderr, "\n");
abort:
if (entry != NULL)
archive_entry_free(entry);
}
/* Helper function to copy file to archive. */
static int
@ -991,342 +923,6 @@ write_file_data(struct bsdtar *bsdtar, struct archive *a,
return 0;
}
static void
create_cleanup(struct bsdtar *bsdtar)
{
free_cache(bsdtar->uname_cache);
bsdtar->uname_cache = NULL;
free_cache(bsdtar->gname_cache);
bsdtar->gname_cache = NULL;
}
#ifdef HAVE_POSIX_ACL
static void setup_acl(struct bsdtar *bsdtar,
struct archive_entry *entry, const char *accpath,
int acl_type, int archive_entry_acl_type);
static void
setup_acls(struct bsdtar *bsdtar, struct archive_entry *entry,
const char *accpath)
{
archive_entry_acl_clear(entry);
setup_acl(bsdtar, entry, accpath,
ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
/* Only directories can have default ACLs. */
if (S_ISDIR(archive_entry_mode(entry)))
setup_acl(bsdtar, entry, accpath,
ACL_TYPE_DEFAULT, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
}
static void
setup_acl(struct bsdtar *bsdtar, struct archive_entry *entry,
const char *accpath, int acl_type, int archive_entry_acl_type)
{
acl_t acl;
acl_tag_t acl_tag;
acl_entry_t acl_entry;
acl_permset_t acl_permset;
int s, ae_id, ae_tag, ae_perm;
const char *ae_name;
/* Retrieve access ACL from file. */
acl = acl_get_file(accpath, acl_type);
if (acl != NULL) {
s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
while (s == 1) {
ae_id = -1;
ae_name = NULL;
acl_get_tag_type(acl_entry, &acl_tag);
if (acl_tag == ACL_USER) {
ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry);
ae_name = lookup_uname(bsdtar, ae_id);
ae_tag = ARCHIVE_ENTRY_ACL_USER;
} else if (acl_tag == ACL_GROUP) {
ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry);
ae_name = lookup_gname(bsdtar, ae_id);
ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
} else if (acl_tag == ACL_MASK) {
ae_tag = ARCHIVE_ENTRY_ACL_MASK;
} else if (acl_tag == ACL_USER_OBJ) {
ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
} else if (acl_tag == ACL_GROUP_OBJ) {
ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
} else if (acl_tag == ACL_OTHER) {
ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
} else {
/* Skip types that libarchive can't support. */
continue;
}
acl_get_permset(acl_entry, &acl_permset);
ae_perm = 0;
/*
* acl_get_perm() is spelled differently on different
* platforms; see bsdtar_platform.h for details.
*/
if (ACL_GET_PERM(acl_permset, ACL_EXECUTE))
ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE;
if (ACL_GET_PERM(acl_permset, ACL_READ))
ae_perm |= ARCHIVE_ENTRY_ACL_READ;
if (ACL_GET_PERM(acl_permset, ACL_WRITE))
ae_perm |= ARCHIVE_ENTRY_ACL_WRITE;
archive_entry_acl_add_entry(entry,
archive_entry_acl_type, ae_perm, ae_tag,
ae_id, ae_name);
s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
}
acl_free(acl);
}
}
#else
static void
setup_acls(struct bsdtar *bsdtar, struct archive_entry *entry,
const char *accpath)
{
(void)bsdtar;
(void)entry;
(void)accpath;
}
#endif
#if HAVE_LISTXATTR && HAVE_LLISTXATTR && HAVE_GETXATTR && HAVE_LGETXATTR
static void
setup_xattr(struct bsdtar *bsdtar, struct archive_entry *entry,
const char *accpath, const char *name)
{
size_t size;
void *value = NULL;
char symlink_mode = bsdtar->symlink_mode;
if (symlink_mode == 'H')
size = getxattr(accpath, name, NULL, 0);
else
size = lgetxattr(accpath, name, NULL, 0);
if (size == -1) {
bsdtar_warnc(bsdtar, errno, "Couldn't get extended attribute");
return;
}
if (size > 0 && (value = malloc(size)) == NULL) {
bsdtar_errc(bsdtar, 1, errno, "Out of memory");
return;
}
if (symlink_mode == 'H')
size = getxattr(accpath, name, value, size);
else
size = lgetxattr(accpath, name, value, size);
if (size == -1) {
bsdtar_warnc(bsdtar, errno, "Couldn't get extended attribute");
return;
}
archive_entry_xattr_add_entry(entry, name, value, size);
free(value);
}
/*
* Linux extended attribute support
*/
static void
setup_xattrs(struct bsdtar *bsdtar, struct archive_entry *entry,
const char *accpath)
{
char *list, *p;
size_t list_size;
char symlink_mode = bsdtar->symlink_mode;
if (symlink_mode == 'H')
list_size = listxattr(accpath, NULL, 0);
else
list_size = llistxattr(accpath, NULL, 0);
if (list_size == -1) {
bsdtar_warnc(bsdtar, errno,
"Couldn't list extended attributes");
return;
} else if (list_size == 0)
return;
if ((list = malloc(list_size)) == NULL) {
bsdtar_errc(bsdtar, 1, errno, "Out of memory");
return;
}
if (symlink_mode == 'H')
list_size = listxattr(accpath, list, list_size);
else
list_size = llistxattr(accpath, list, list_size);
if (list_size == -1) {
bsdtar_warnc(bsdtar, errno,
"Couldn't list extended attributes");
free(list);
return;
}
for (p = list; (p - list) < list_size; p += strlen(p) + 1) {
if (strncmp(p, "system.", 7) == 0 ||
strncmp(p, "xfsroot.", 8) == 0)
continue;
setup_xattr(bsdtar, entry, accpath, p);
}
free(list);
}
#else
/*
* Generic (stub) extended attribute support.
*/
static void
setup_xattrs(struct bsdtar *bsdtar, struct archive_entry *entry,
const char *accpath)
{
(void)bsdtar; /* UNUSED */
(void)entry; /* UNUSED */
(void)accpath; /* UNUSED */
}
#endif
static void
free_cache(struct name_cache *cache)
{
size_t i;
if (cache != NULL) {
for (i = 0; i < cache->size; i++) {
if (cache->cache[i].name != NULL &&
cache->cache[i].name != NO_NAME)
free((void *)(uintptr_t)cache->cache[i].name);
}
free(cache);
}
}
/*
* Lookup uid/gid from uname/gname, return NULL if no match.
*/
static const char *
lookup_name(struct bsdtar *bsdtar, struct name_cache **name_cache_variable,
int (*lookup_fn)(struct bsdtar *, const char **, id_t), id_t id)
{
struct name_cache *cache;
const char *name;
int slot;
if (*name_cache_variable == NULL) {
*name_cache_variable = malloc(sizeof(struct name_cache));
if (*name_cache_variable == NULL)
bsdtar_errc(bsdtar, 1, ENOMEM, "No more memory");
memset(*name_cache_variable, 0, sizeof(struct name_cache));
(*name_cache_variable)->size = name_cache_size;
}
cache = *name_cache_variable;
cache->probes++;
slot = id % cache->size;
if (cache->cache[slot].name != NULL) {
if (cache->cache[slot].id == id) {
cache->hits++;
if (cache->cache[slot].name == NO_NAME)
return (NULL);
return (cache->cache[slot].name);
}
if (cache->cache[slot].name != NO_NAME)
free((void *)(uintptr_t)cache->cache[slot].name);
cache->cache[slot].name = NULL;
}
if (lookup_fn(bsdtar, &name, id) == 0) {
if (name == NULL || name[0] == '\0') {
/* Cache the negative response. */
cache->cache[slot].name = NO_NAME;
cache->cache[slot].id = id;
} else {
cache->cache[slot].name = strdup(name);
if (cache->cache[slot].name != NULL) {
cache->cache[slot].id = id;
return (cache->cache[slot].name);
}
/*
* Conveniently, NULL marks an empty slot, so
* if the strdup() fails, we've just failed to
* cache it. No recovery necessary.
*/
}
}
return (NULL);
}
static const char *
lookup_uname(struct bsdtar *bsdtar, uid_t uid)
{
return (lookup_name(bsdtar, &bsdtar->uname_cache,
&lookup_uname_helper, (id_t)uid));
}
static int
lookup_uname_helper(struct bsdtar *bsdtar, const char **name, id_t id)
{
struct passwd *pwent;
(void)bsdtar; /* UNUSED */
errno = 0;
pwent = getpwuid((uid_t)id);
if (pwent == NULL) {
*name = NULL;
if (errno != 0)
bsdtar_warnc(bsdtar, errno, "getpwuid(%d) failed", id);
return (errno);
}
*name = pwent->pw_name;
return (0);
}
static const char *
lookup_gname(struct bsdtar *bsdtar, gid_t gid)
{
return (lookup_name(bsdtar, &bsdtar->gname_cache,
&lookup_gname_helper, (id_t)gid));
}
static int
lookup_gname_helper(struct bsdtar *bsdtar, const char **name, id_t id)
{
struct group *grent;
(void)bsdtar; /* UNUSED */
errno = 0;
grent = getgrgid((gid_t)id);
if (grent == NULL) {
*name = NULL;
if (errno != 0)
bsdtar_warnc(bsdtar, errno, "getgrgid(%d) failed", id);
return (errno);
}
*name = grent->gr_name;
return (0);
}
/*
* Test if the specified file is new enough to include in the archive.
*/