]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Start of archive_read_disk API.
authorTim Kientzle <kientzle@gmail.com>
Wed, 7 Jan 2009 06:38:55 +0000 (01:38 -0500)
committerTim Kientzle <kientzle@gmail.com>
Wed, 7 Jan 2009 06:38:55 +0000 (01:38 -0500)
Right now, this is just a handle for username/group name lookups
and the associated caches.  Soon, this will hold machinery to
populate archive_entry objects from files on disk (which relies
on efficient uname/gname lookups).

SVN-Revision: 399

libarchive/Makefile
libarchive/archive.h
libarchive/archive_read_disk.c [new file with mode: 0644]
libarchive/archive_read_disk_private.h [new file with mode: 0644]
libarchive/archive_read_disk_set_standard_lookup.c [new file with mode: 0644]
libarchive/test/Makefile
libarchive/test/test_read_disk.c [new file with mode: 0644]

index 8e6e340fb5d6997fabe8367207c5eb316137ca8f..eecf3c33961ec95221608a0117a56e92a4b5450a 100644 (file)
@@ -25,6 +25,8 @@ SRCS= archive_check_magic.c                           \
        archive_entry_link_resolver.c                   \
        archive_read.c                                  \
        archive_read_data_into_fd.c                     \
+       archive_read_disk.c                             \
+       archive_read_disk_set_standard_lookup.c         \
        archive_read_extract.c                          \
        archive_read_open_fd.c                          \
        archive_read_open_file.c                        \
index a45cc09c70e40c82bd503e66475d5205a2586278..d9fb40711343ba94a13598c6702c03f8fd0905bd 100644 (file)
@@ -605,6 +605,22 @@ __LA_DECL int       archive_write_disk_set_user_lookup(struct archive *,
                            __LA_UID_T (*)(void *, const char *, __LA_UID_T),
                            void (* /* cleanup */)(void *));
 
+/*
+ * ARCHIVE_READ_DISK API
+ */
+__LA_DECL struct archive *archive_read_disk_new(void);
+__LA_DECL int  archive_read_disk_set_standard_lookup(struct archive *);
+__LA_DECL const char *archive_read_disk_gname(struct archive *, __LA_GID_T);
+__LA_DECL const char *archive_read_disk_uname(struct archive *, __LA_UID_T);
+__LA_DECL int  archive_read_disk_set_gname_lookup(struct archive *,
+    void * /* private_data */,
+    const char *(* /* lookup_fn */)(void *, __LA_GID_T),
+    void (* /* cleanup_fn */)(void *));
+__LA_DECL int  archive_read_disk_set_uname_lookup(struct archive *,
+    void * /* private_data */,
+    const char *(* /* lookup_fn */)(void *, __LA_UID_T),
+    void (* /* cleanup_fn */)(void *));
+
 /*
  * Accessor functions to read/set various information in
  * the struct archive object:
diff --git a/libarchive/archive_read_disk.c b/libarchive/archive_read_disk.c
new file mode 100644 (file)
index 0000000..010865c
--- /dev/null
@@ -0,0 +1,162 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+
+static int     _archive_read_finish(struct archive *);
+static const char *trivial_lookup_gname(void *, gid_t gid);
+static const char *trivial_lookup_uname(void *, uid_t uid);
+
+static struct archive_vtable *
+archive_read_disk_vtable(void)
+{
+       static struct archive_vtable av;
+       static int inited = 0;
+
+       if (!inited) {
+               av.archive_finish = _archive_read_finish;
+       }
+       return (&av);
+}
+
+const char *
+archive_read_disk_gname(struct archive *_a, gid_t gid)
+{
+       struct archive_read_disk *a = (struct archive_read_disk *)_a;
+       if (a->lookup_gname != NULL)
+               return ((*a->lookup_gname)(a->lookup_gname_data, gid));
+       return (NULL);
+}
+
+const char *
+archive_read_disk_uname(struct archive *_a, uid_t uid)
+{
+       struct archive_read_disk *a = (struct archive_read_disk *)_a;
+       if (a->lookup_uname != NULL)
+               return ((*a->lookup_uname)(a->lookup_uname_data, uid));
+       return (NULL);
+}
+
+int
+archive_read_disk_set_gname_lookup(struct archive *_a,
+    void *private_data,
+    const char * (*lookup_gname)(void *private, gid_t gid),
+    void (*cleanup_gname)(void *private))
+{
+       struct archive_read_disk *a = (struct archive_read_disk *)_a;
+       __archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
+           ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup");
+
+       if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
+               (a->cleanup_gname)(a->lookup_gname_data);
+
+       a->lookup_gname = lookup_gname;
+       a->cleanup_gname = cleanup_gname;
+       a->lookup_gname_data = private_data;
+       return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_uname_lookup(struct archive *_a,
+    void *private_data,
+    const char * (*lookup_uname)(void *private, uid_t uid),
+    void (*cleanup_uname)(void *private))
+{
+       struct archive_read_disk *a = (struct archive_read_disk *)_a;
+       __archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
+           ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup");
+
+       if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
+               (a->cleanup_uname)(a->lookup_uname_data);
+
+       a->lookup_uname = lookup_uname;
+       a->cleanup_uname = cleanup_uname;
+       a->lookup_uname_data = private_data;
+       return (ARCHIVE_OK);
+}
+
+/*
+ * Create a new archive_read_disk object and initialize it with global state.
+ */
+struct archive *
+archive_read_disk_new(void)
+{
+       struct archive_read_disk *a;
+
+       a = (struct archive_read_disk *)malloc(sizeof(*a));
+       if (a == NULL)
+               return (NULL);
+       memset(a, 0, sizeof(*a));
+       a->archive.magic = ARCHIVE_READ_DISK_MAGIC;
+       /* We're ready to write a header immediately. */
+       a->archive.state = ARCHIVE_STATE_HEADER;
+       a->archive.vtable = archive_read_disk_vtable();
+       a->lookup_uname = trivial_lookup_uname;
+       a->lookup_gname = trivial_lookup_gname;
+       return (&a->archive);
+}
+
+static int
+_archive_read_finish(struct archive *_a)
+{
+       struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+       if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
+               (a->cleanup_gname)(a->lookup_gname_data);
+       if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
+               (a->cleanup_uname)(a->lookup_uname_data);
+       archive_string_free(&a->archive.error_string);
+       free(a);
+       return (ARCHIVE_OK);
+}
+
+/*
+ * Trivial implementations of gname/uname lookup functions.
+ * These are normally overridden by the client, but these stub
+ * versions ensure that we always have something that works.
+ */
+static const char *
+trivial_lookup_gname(void *private_data, gid_t gid)
+{
+       (void)private_data; /* UNUSED */
+       (void)gid; /* UNUSED */
+       return (NULL);
+}
+
+static const char *
+trivial_lookup_uname(void *private_data, uid_t uid)
+{
+       (void)private_data; /* UNUSED */
+       (void)uid; /* UNUSED */
+       return (NULL);
+}
diff --git a/libarchive/archive_read_disk_private.h b/libarchive/archive_read_disk_private.h
new file mode 100644 (file)
index 0000000..758e66b
--- /dev/null
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
+#define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
+
+struct archive_read_disk {
+       struct archive  archive;
+
+       const char * (*lookup_gname)(void *private, gid_t gid);
+       void    (*cleanup_gname)(void *private);
+       void     *lookup_gname_data;
+       const char * (*lookup_uname)(void *private, gid_t gid);
+       void    (*cleanup_uname)(void *private);
+       void     *lookup_uname_data;
+};
+
+#endif
diff --git a/libarchive/archive_read_disk_set_standard_lookup.c b/libarchive/archive_read_disk_set_standard_lookup.c
new file mode 100644 (file)
index 0000000..8af42b0
--- /dev/null
@@ -0,0 +1,220 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+
+#define        name_cache_size 127
+
+static const char * const NO_NAME = "(noname)";
+
+struct name_cache {
+       struct archive *archive;
+       int     probes;
+       int     hits;
+       size_t  size;
+       struct {
+               id_t id;
+               const char *name;
+       } cache[name_cache_size];
+};
+
+static const char *    lookup_gname(void *, gid_t);
+static const char *    lookup_uname(void *, uid_t);
+static void    cleanup(void *);
+static const char *    lookup_gname_helper(struct archive *, id_t gid);
+static const char *    lookup_uname_helper(struct archive *, id_t uid);
+
+/*
+ * Installs functions that use getpwuid()/getgrgid()---along with
+ * a simple cache to accelerate such lookups---into the archive_read_disk
+ * object.  This is in a separate file because getpwuid()/getgrgid()
+ * can pull in a LOT of library code (including NIS/LDAP functions, which
+ * pull in DNS resolveers, etc).  This can easily top 500kB, which makes
+ * it inappropriate for some space-constrained applications.
+ *
+ * Applications that are size-sensitive may want to just use the
+ * real default functions (defined in archive_read_disk.c) that just
+ * use the uid/gid without the lookup.  Or define your own custom functions
+ * if you prefer.
+ */
+int
+archive_read_disk_set_standard_lookup(struct archive *a)
+{
+       struct name_cache *ucache = malloc(sizeof(struct name_cache));
+       struct name_cache *gcache = malloc(sizeof(struct name_cache));
+
+       if (ucache == NULL || gcache == NULL) {
+               archive_set_error(a, ENOMEM,
+                   "Can't allocate uname/gname lookup cache");
+               free(ucache);
+               free(gcache);
+               return (ARCHIVE_FATAL);
+       }
+
+       memset(ucache, 0, sizeof(*ucache));
+       ucache->archive = a;
+       ucache->size = name_cache_size;
+       memset(gcache, 0, sizeof(*gcache));
+       gcache->archive = a;
+       gcache->size = name_cache_size;
+
+       archive_read_disk_set_gname_lookup(a, gcache, lookup_gname, cleanup);
+       archive_read_disk_set_uname_lookup(a, ucache, lookup_uname, cleanup);
+
+       return (ARCHIVE_OK);
+}
+
+static void
+cleanup(void *data)
+{
+       struct name_cache *cache = (struct name_cache *)data;
+       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 name_cache *cache,
+    const char * (*lookup_fn)(struct archive *, id_t), id_t id)
+{
+       const char *name;
+       int slot;
+
+
+       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;
+       }
+
+       name = (lookup_fn)(cache->archive, id);
+       if (name == NULL) {
+               /* Cache and return the negative response. */
+               cache->cache[slot].name = NO_NAME;
+               cache->cache[slot].id = id;
+               return (NULL);
+       }
+
+       cache->cache[slot].name = name;
+       cache->cache[slot].id = id;
+       return (cache->cache[slot].name);
+}
+
+static const char *
+lookup_uname(void *data, uid_t uid)
+{
+       struct name_cache *uname_cache = (struct name_cache *)data;
+       return (lookup_name(uname_cache,
+                   &lookup_uname_helper, (id_t)uid));
+}
+
+static const char *
+lookup_uname_helper(struct archive *a, id_t id)
+{
+       char buffer[64];
+       struct passwd   pwent, *result;
+       int r;
+
+       errno = 0;
+       r = getpwuid_r((uid_t)id, &pwent, buffer, sizeof(buffer), &result);
+       if (r != 0) {
+               archive_set_error(a, errno,
+                   "Can't lookup user for id %d", (int)id);
+               return (NULL);
+       }
+       if (result == NULL)
+               return (NULL);
+
+       return strdup(pwent.pw_name);
+}
+
+static const char *
+lookup_gname(void *data, gid_t gid)
+{
+       struct name_cache *gname_cache = (struct name_cache *)data;
+       return (lookup_name(gname_cache,
+                   &lookup_gname_helper, (id_t)gid));
+}
+
+static const char *
+lookup_gname_helper(struct archive *a, id_t id)
+{
+       char buffer[64];
+       struct group    grent, *result;
+       int r;
+
+       errno = 0;
+       r = getgrgid_r((gid_t)id, &grent, buffer, sizeof(buffer), &result);
+       if (r != 0) {
+               archive_set_error(a, errno,
+                   "Can't lookup group for id %d", (int)id);
+               return (NULL);
+       }
+       if (result == NULL)
+               return (NULL);
+
+       return strdup(grent.gr_name);
+}
index 669f448aa16f39b4d8c8917f184e2205e69c0939..f744a3401a5eca250a94c2a00bac2a7601f50206 100644 (file)
@@ -26,6 +26,7 @@ TESTS= \
        test_pax_filename_encoding.c            \
        test_read_compress_program.c            \
        test_read_data_large.c                  \
+       test_read_disk.c                        \
        test_read_extract.c                     \
        test_read_format_ar.c                   \
        test_read_format_cpio_bin.c             \
diff --git a/libarchive/test/test_read_disk.c b/libarchive/test/test_read_disk.c
new file mode 100644 (file)
index 0000000..efd232d
--- /dev/null
@@ -0,0 +1,125 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD$");
+
+static void
+gname_cleanup(void *d)
+{
+       int *mp = d;
+       assertEqualInt(*mp, 0x13579);
+       *mp = 0x2468;
+}
+
+static const char *
+gname_lookup(void *d, gid_t g)
+{
+       int *mp = d;
+       assertEqualInt(*mp, 0x13579);
+       if (g == 1)
+               return ("FOOGROUP");
+       return ("NOTFOOGROUP");
+}
+
+static void
+uname_cleanup(void *d)
+{
+       int *mp = d;
+       assertEqualInt(*mp, 0x1234);
+       *mp = 0x2345;
+}
+
+static const char *
+uname_lookup(void *d, uid_t u)
+{
+       int *mp = d;
+       assertEqualInt(*mp, 0x1234);
+       if (u == 1)
+               return ("FOO");
+       return ("NOTFOO");
+}
+
+DEFINE_TEST(test_read_disk)
+{
+       struct archive *a;
+       int gmagic = 0x13579, umagic = 0x1234;
+
+       assert((a = archive_read_disk_new()) != NULL);
+
+       /* Default uname/gname lookups always return NULL. */
+       assert(archive_read_disk_gname(a, 0) == NULL);
+       assert(archive_read_disk_uname(a, 0) == NULL);
+
+       /* Register some weird lookup functions. */
+       assertEqualInt(ARCHIVE_OK, archive_read_disk_set_gname_lookup(a,
+                          &gmagic, &gname_lookup, &gname_cleanup));
+       /* Verify that our new function got called. */
+       assertEqualString(archive_read_disk_gname(a, 0), "NOTFOOGROUP");
+       assertEqualString(archive_read_disk_gname(a, 1), "FOOGROUP");
+
+       /* De-register. */
+       assertEqualInt(ARCHIVE_OK,
+           archive_read_disk_set_gname_lookup(a, NULL, NULL, NULL));
+       /* Ensure our cleanup function got called. */
+       assertEqualInt(gmagic, 0x2468);
+
+       /* Same thing with uname lookup.... */
+       assertEqualInt(ARCHIVE_OK, archive_read_disk_set_uname_lookup(a,
+                          &umagic, &uname_lookup, &uname_cleanup));
+       assertEqualString(archive_read_disk_uname(a, 0), "NOTFOO");
+       assertEqualString(archive_read_disk_uname(a, 1), "FOO");
+       assertEqualInt(ARCHIVE_OK,
+           archive_read_disk_set_uname_lookup(a, NULL, NULL, NULL));
+       assertEqualInt(umagic, 0x2345);
+
+       /* Try the standard lookup functions. */
+       assertEqualInt(ARCHIVE_OK,
+           archive_read_disk_set_standard_lookup(a));
+       assertEqualString(archive_read_disk_uname(a, 0), "root");
+       assertEqualString(archive_read_disk_gname(a, 0), "wheel");
+
+       /* Deregister again and verify the default lookups again. */
+       assertEqualInt(ARCHIVE_OK,
+           archive_read_disk_set_gname_lookup(a, NULL, NULL, NULL));
+       assertEqualInt(ARCHIVE_OK,
+           archive_read_disk_set_uname_lookup(a, NULL, NULL, NULL));
+       assert(archive_read_disk_gname(a, 0) == NULL);
+       assert(archive_read_disk_uname(a, 0) == NULL);
+
+       /* Re-register our custom handlers. */
+       gmagic = 0x13579;
+       umagic = 0x1234;
+       assertEqualInt(ARCHIVE_OK, archive_read_disk_set_gname_lookup(a,
+                          &gmagic, &gname_lookup, &gname_cleanup));
+       assertEqualInt(ARCHIVE_OK, archive_read_disk_set_uname_lookup(a,
+                          &umagic, &uname_lookup, &uname_cleanup));
+
+       /* Destroy the archive. */
+       assertEqualInt(ARCHIVE_OK, archive_read_finish(a));
+
+       /* Verify our cleanup functions got called. */
+       assertEqualInt(gmagic, 0x2468);
+       assertEqualInt(umagic, 0x2345);
+}