From 9a21e6ddf9eb7bb0f13f01cddba9dedd7a6e43b3 Mon Sep 17 00:00:00 2001
From: Stef Walter <stefw@gnome.org>
Date: Wed, 23 Jan 2013 17:35:58 +0100
Subject: Support for sane writing to files extracted

 * Implement atomic writes of files
 * Writing with checks that not overwriting anything unless desired
 * Writing and overwriting of directory contents in a robust way
---
 tools/Makefile.am             |   3 +
 tools/save.c                  | 462 +++++++++++++++++++++++++++++++++++++++
 tools/save.h                  |  79 +++++++
 tools/tests/Makefile.am       |  52 +++++
 tools/tests/files/cacert3.der | Bin 0 -> 1885 bytes
 tools/tests/test-save.c       | 494 ++++++++++++++++++++++++++++++++++++++++++
 tools/tests/test.c            | 200 +++++++++++++++++
 tools/tests/test.h            | 211 ++++++++++++++++++
 8 files changed, 1501 insertions(+)
 create mode 100644 tools/save.c
 create mode 100644 tools/save.h
 create mode 100644 tools/tests/Makefile.am
 create mode 100644 tools/tests/files/cacert3.der
 create mode 100644 tools/tests/test-save.c
 create mode 100644 tools/tests/test.c
 create mode 100644 tools/tests/test.h

(limited to 'tools')

diff --git a/tools/Makefile.am b/tools/Makefile.am
index 43adf56..fab1bd9 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -1,5 +1,7 @@
 NULL =
 
+SUBDIRS = . tests
+
 COMMON = $(top_srcdir)/common
 
 INCLUDES = \
@@ -16,6 +18,7 @@ bin_PROGRAMS = \
 
 p11_kit_SOURCES = \
 	list.c \
+	save.c save.h \
 	tool.c tool.h \
 	$(NULL)
 
diff --git a/tools/save.c b/tools/save.c
new file mode 100644
index 0000000..695c1fc
--- /dev/null
+++ b/tools/save.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright (c) 2013, Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above
+ *       copyright notice, this list of conditions and the
+ *       following disclaimer.
+ *     * 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.
+ *     * The names of contributors to this software may not be
+ *       used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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
+ * COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ *
+ * Author: Stef Walter <stefw@redhat.com>
+ */
+
+#include "config.h"
+
+#include "buffer.h"
+#include "debug.h"
+#include "dict.h"
+#include "library.h"
+#include "save.h"
+
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct _p11_save_file {
+	char *path;
+	char *temp;
+	int fd;
+	int flags;
+};
+
+struct _p11_save_dir {
+	p11_dict *cache;
+	char *path;
+	int flags;
+};
+
+bool
+p11_save_write_and_finish (p11_save_file *file,
+                           const void *data,
+                           size_t length)
+{
+	bool ret;
+
+	if (!file)
+		return false;
+
+	ret = p11_save_write (file, data, length);
+	if (!p11_save_finish_file (file, ret))
+		ret = false;
+
+	return ret;
+}
+
+p11_save_file *
+p11_save_open_file (const char *path,
+                    int flags)
+{
+	struct stat st;
+	p11_save_file *file;
+	char *temp;
+	int fd;
+
+	return_val_if_fail (path != NULL, NULL);
+
+	/*
+	 * This is just an early convenience check. We check again
+	 * later when committing, in a non-racy fashion.
+	 */
+
+	if (!(flags & P11_SAVE_OVERWRITE)) {
+		if (stat (path, &st) >= 0) {
+			p11_message ("file already exists: %s", path);
+			return NULL;
+		}
+	}
+
+	if (asprintf (&temp, "%s.XXXXXX", path) < 0)
+		return_val_if_reached (NULL);
+
+	fd = mkstemp (temp);
+	if (fd < 0) {
+		p11_message ("couldn't create file: %s: %s",
+		             path, strerror (errno));
+		free (temp);
+		return NULL;
+	}
+
+	file = calloc (1, sizeof (p11_save_file));
+	return_val_if_fail (file != NULL, NULL);
+	file->temp = temp;
+	file->path = strdup (path);
+	return_val_if_fail (file->path != NULL, NULL);
+	file->flags = flags;
+	file->fd = fd;
+
+	return file;
+}
+
+bool
+p11_save_write (p11_save_file *file,
+                const void *data,
+                size_t length)
+{
+	const unsigned char *buf = data;
+	ssize_t written = 0;
+	ssize_t res;
+
+	if (!file)
+		return false;
+
+	while (written < length) {
+		res = write (file->fd, buf + written, length - written);
+		if (res <= 0) {
+			if (errno == EAGAIN && errno == EINTR)
+				continue;
+			p11_message ("couldn't write to file: %s: %s",
+			             file->temp, strerror (errno));
+			return false;
+		} else {
+			written += res;
+		}
+	}
+
+	return true;
+}
+
+static void
+filo_free (p11_save_file *file)
+{
+	free (file->temp);
+	free (file->path);
+	free (file);
+}
+
+bool
+p11_save_finish_file (p11_save_file *file,
+                      bool commit)
+{
+	bool ret = true;
+
+	if (!file)
+		return false;
+
+	if (!commit) {
+		unlink (file->temp);
+		close (file->fd);
+		filo_free (file);
+		return true;
+	}
+
+	/* Set the mode of the file */
+	if (chmod (file->temp, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
+		p11_message ("couldn't set file permissions: %s: %s",
+		             file->temp, strerror (errno));
+		close (file->fd);
+		ret = false;
+
+	} else if (close (file->fd) < 0) {
+		p11_message ("couldn't write file: %s: %s",
+		             file->temp, strerror (errno));
+		ret = false;
+
+	/* Atomically rename the tempfile over the filename */
+	} else if (file->flags & P11_SAVE_OVERWRITE) {
+		if (rename (file->temp, file->path) < 0) {
+			p11_message ("couldn't complete writing file: %s: %s",
+			             file->path, strerror (errno));
+			ret = false;
+		} else {
+			unlink (file->temp);
+		}
+
+	/* When not overwriting, link will fail if filename exists. */
+	} else {
+		if (link (file->temp, file->path) < 0) {
+			p11_message ("couldn't complete writing of file: %s: %s",
+			             file->path, strerror (errno));
+			ret = false;
+		}
+		unlink (file->temp);
+	}
+
+	filo_free (file);
+	return ret;
+}
+
+p11_save_dir *
+p11_save_open_directory (const char *path,
+                         int flags)
+{
+	mode_t mode = S_IRWXU | S_IXGRP | S_IRGRP | S_IROTH | S_IXOTH;
+	p11_save_dir *dir;
+
+	return_val_if_fail (path != NULL, NULL);
+
+	if (mkdir (path, mode) < 0) {
+		if (!(flags & P11_SAVE_OVERWRITE) || errno != EEXIST) {
+			p11_message ("directory already exists: %s", path);
+			return NULL;
+		}
+	}
+
+	dir = calloc (1, sizeof (p11_save_dir));
+	return_val_if_fail (dir != NULL, NULL);
+
+	dir->path = strdup (path);
+	return_val_if_fail (dir->path != NULL, NULL);
+
+	dir->cache = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal, free, NULL);
+	return_val_if_fail (dir->cache != NULL, NULL);
+
+	dir->flags = flags;
+	return dir;
+}
+
+static char *
+make_unique_name (p11_save_dir *dir,
+                  const char *filename,
+                  const char *extension)
+{
+	char unique[16];
+	p11_buffer buf;
+	int i;
+
+	p11_buffer_init_null (&buf, 0);
+
+	for (i = 0; true; i++) {
+
+		p11_buffer_reset (&buf, 64);
+
+		switch (i) {
+
+		/*
+		 * For the first iteration, just build the filename as
+		 * provided by the caller.
+		 */
+		case 0:
+			p11_buffer_add (&buf, filename, -1);
+			break;
+
+		/*
+		 * On later iterations we try to add a numeric .N suffix
+		 * before the extension, so the resulting file might look
+		 * like filename.1.ext.
+		 *
+		 * As a special case if the extension is already '.0' then
+		 * just just keep incerementing that.
+		 */
+		case 1:
+			if (extension && strcmp (extension, ".0") == 0)
+				extension = NULL;
+			/* fall through */
+
+		default:
+			p11_buffer_add (&buf, filename, -1);
+			snprintf (unique, sizeof (unique), ".%d", i);
+			p11_buffer_add (&buf, unique, -1);
+			break;
+		}
+
+		if (extension)
+			p11_buffer_add (&buf, extension, -1);
+
+		return_val_if_fail (p11_buffer_ok (&buf), NULL);
+
+		if (!p11_dict_get (dir->cache, buf.data))
+			return p11_buffer_steal (&buf, NULL);
+	}
+
+	assert_not_reached ();
+}
+
+p11_save_file *
+p11_save_open_file_in (p11_save_dir *dir,
+                       const char *basename,
+                       const char *extension,
+                       const char **ret_name)
+{
+	p11_save_file *file = NULL;
+	char *name;
+	char *path;
+
+	return_val_if_fail (dir != NULL, NULL);
+	return_val_if_fail (basename != NULL, NULL);
+
+	name = make_unique_name (dir, basename, extension);
+	return_val_if_fail (name != NULL, NULL);
+
+	if (asprintf (&path, "%s/%s", dir->path, name) < 0)
+		return_val_if_reached (NULL);
+
+	file = p11_save_open_file (path, dir->flags);
+
+	if (file) {
+		if (!p11_dict_set (dir->cache, name, name))
+			return_val_if_reached (NULL);
+		if (ret_name)
+			*ret_name = name;
+		name = NULL;
+	}
+
+	free (name);
+	free (path);
+
+	return file;
+}
+
+bool
+p11_save_symlink_in (p11_save_dir *dir,
+                     const char *linkname,
+                     const char *extension,
+                     const char *destination)
+{
+	char *name;
+	char *path;
+	bool ret;
+
+	return_val_if_fail (dir != NULL, false);
+	return_val_if_fail (linkname != NULL, false);
+	return_val_if_fail (destination != NULL, false);
+
+	name = make_unique_name (dir, linkname, extension);
+	return_val_if_fail (name != NULL, false);
+
+	if (asprintf (&path, "%s/%s", dir->path, name) < 0)
+		return_val_if_reached (false);
+
+	unlink (path);
+
+	if (symlink (destination, path) < 0) {
+		p11_message ("couldn't create symlink: %s: %s",
+		             path, strerror (errno));
+		ret = false;
+	} else {
+		if (!p11_dict_set (dir->cache, name, name))
+			return_val_if_reached (false);
+		name = NULL;
+		ret = true;
+	}
+
+	free (path);
+	free (name);
+
+	return ret;
+}
+
+static bool
+cleanup_directory (const char *directory,
+                   p11_dict *cache)
+{
+	struct dirent *dp;
+	p11_dict *remove;
+	p11_dictiter iter;
+	char *path;
+	DIR *dir;
+	int skip;
+	bool ret;
+
+	/* First we load all the modules */
+	dir = opendir (directory);
+	if (!dir) {
+		p11_message ("couldn't list directory: %s: %s",
+		             directory, strerror (errno));
+		return false;
+	}
+
+	remove = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal, free, NULL);
+
+	/* We're within a global mutex, so readdir is safe */
+	while ((dp = readdir (dir)) != NULL) {
+		if (p11_dict_get (cache, dp->d_name))
+			continue;
+
+		if (asprintf (&path, "%s/%s", directory, dp->d_name) < 0)
+			return_val_if_reached (false);
+
+#ifdef HAVE_STRUCT_DIRENT_D_TYPE
+		if(dp->d_type != DT_UNKNOWN) {
+			skip = (dp->d_type == DT_DIR);
+		} else
+#endif
+		{
+			struct stat st;
+
+			skip = (stat (path, &st) < 0) || S_ISDIR (st.st_mode);
+		}
+
+		if (!skip) {
+			if (!p11_dict_set (remove, path, path))
+				return_val_if_reached (false);
+		} else {
+			free (path);
+		}
+	}
+
+	closedir (dir);
+
+	ret = true;
+
+	/* Remove all the files still in the cache */
+	p11_dict_iterate (remove, &iter);
+	while (p11_dict_next (&iter, (void **)&path, NULL)) {
+		if (unlink (path) < 0 && errno != ENOENT) {
+			p11_message ("couldn't remove file: %s: %s",
+			             path, strerror (errno));
+			ret = false;
+			break;
+		}
+	}
+
+	p11_dict_free (remove);
+
+	return ret;
+}
+
+bool
+p11_save_finish_directory (p11_save_dir *dir,
+                           bool commit)
+{
+	bool ret = true;
+
+	if (!dir)
+		return false;
+
+	if (commit && (dir->flags & P11_SAVE_OVERWRITE))
+		ret = cleanup_directory (dir->path, dir->cache);
+
+	p11_dict_free (dir->cache);
+	free (dir->path);
+	free (dir);
+
+	return ret;
+}
diff --git a/tools/save.h b/tools/save.h
new file mode 100644
index 0000000..3e877ae
--- /dev/null
+++ b/tools/save.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2013, Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above
+ *       copyright notice, this list of conditions and the
+ *       following disclaimer.
+ *     * 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.
+ *     * The names of contributors to this software may not be
+ *       used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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
+ * COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ *
+ * Author: Stef Walter <stefw@redhat.com>
+ */
+
+#ifndef P11_SAVE_H_
+#define P11_SAVE_H_
+
+#include "compat.h"
+
+enum {
+	P11_SAVE_OVERWRITE = 1 << 0,
+};
+
+typedef struct _p11_save_file p11_save_file;
+typedef struct _p11_save_dir p11_save_dir;
+
+p11_save_file *  p11_save_open_file         (const char *path,
+                                             int flags);
+
+bool             p11_save_write             (p11_save_file *file,
+                                             const void *data,
+                                             size_t length);
+
+bool             p11_save_write_and_finish  (p11_save_file *file,
+                                             const void *data,
+                                             size_t length);
+
+bool             p11_save_finish_file       (p11_save_file *file,
+                                             bool commit);
+
+const char *     p11_save_file_name         (p11_save_file *file);
+
+p11_save_dir *   p11_save_open_directory    (const char *path,
+                                             int flags);
+
+p11_save_file *  p11_save_open_file_in      (p11_save_dir *directory,
+                                             const char *basename,
+                                             const char *extension,
+                                             const char **filename);
+
+bool             p11_save_symlink_in        (p11_save_dir *dir,
+                                             const char *linkname,
+                                             const char *extension,
+                                             const char *destination);
+
+bool             p11_save_finish_directory  (p11_save_dir *dir,
+                                             bool commit);
+
+#endif /* P11_SAVE_H_ */
diff --git a/tools/tests/Makefile.am b/tools/tests/Makefile.am
new file mode 100644
index 0000000..e4dd7ff
--- /dev/null
+++ b/tools/tests/Makefile.am
@@ -0,0 +1,52 @@
+
+include $(top_srcdir)/build/Makefile.tests
+
+NULL =
+
+EXTRA_DIST = files
+
+if WITH_ASN1
+
+COMMON = $(top_srcdir)/common
+TOOLS = $(top_srcdir)/tools
+
+INCLUDES = \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/p11-kit \
+	-I$(srcdir)/.. \
+	-I$(COMMON) \
+	-DP11_KIT_FUTURE_UNSTABLE_API \
+	$(CUTEST_CFLAGS)
+
+LDADD = \
+	$(top_builddir)/p11-kit/libp11-kit.la \
+	$(top_builddir)/common/libp11-data.la \
+	$(top_builddir)/common/libp11-library.la \
+	$(top_builddir)/common/libp11-compat.la \
+	$(builddir)/libtestcommon.la \
+	$(LIBTASN1_LIBS) \
+	$(LTLIBINTL) \
+	$(CUTEST_LIBS) \
+	$(NULL)
+
+noinst_LTLIBRARIES = \
+	libtestcommon.la
+
+libtestcommon_la_SOURCES = \
+	test.c test.h
+
+CHECK_PROGS = \
+	test-save \
+	$(NULL)
+
+noinst_PROGRAMS = \
+	$(CHECK_PROGS)
+
+TESTS = $(CHECK_PROGS:=$(EXEEXT))
+
+test_save_SOURCES = \
+	test-save.c \
+	$(TOOLS)/save.c \
+	$(NULL)
+
+endif # WITH_ASN1
diff --git a/tools/tests/files/cacert3.der b/tools/tests/files/cacert3.der
new file mode 100644
index 0000000..56f8c88
Binary files /dev/null and b/tools/tests/files/cacert3.der differ
diff --git a/tools/tests/test-save.c b/tools/tests/test-save.c
new file mode 100644
index 0000000..743ba88
--- /dev/null
+++ b/tools/tests/test-save.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright (c) 2013, Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above
+ *       copyright notice, this list of conditions and the
+ *       following disclaimer.
+ *     * 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.
+ *     * The names of contributors to this software may not be
+ *       used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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
+ * COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include "config.h"
+#include "CuTest.h"
+
+#include "attrs.h"
+#include "compat.h"
+#include "debug.h"
+#include "dict.h"
+#include "library.h"
+#include "save.h"
+#include "test.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+struct {
+	char *directory;
+} test;
+
+static void
+setup (CuTest *tc)
+{
+	test.directory = strdup ("/tmp/test-extract.XXXXXX");
+	if (!mkdtemp (test.directory))
+		CuFail (tc, "mkdtemp() failed");
+}
+
+static void
+teardown (CuTest *tc)
+{
+	if (rmdir (test.directory) < 0)
+		CuFail (tc, "rmdir() failed");
+	free (test.directory);
+}
+
+static void
+write_zero_file (CuTest *tc,
+                 const char *directory,
+                 const char *name)
+{
+	char *filename;
+	int res;
+	int fd;
+
+	if (asprintf (&filename, "%s/%s", directory, name) < 0)
+		CuFail (tc, "asprintf() failed");
+
+	fd = open (filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+	CuAssertTrue (tc, fd != -1);
+	res = close (fd);
+	CuAssertTrue (tc, res >= 0);
+}
+
+static void
+test_file_write (CuTest *tc)
+{
+	p11_save_file *file;
+	char *filename;
+	bool ret;
+
+	setup (tc);
+
+	if (asprintf (&filename, "%s/%s", test.directory, "extract-file") < 0)
+		CuFail (tc, "asprintf() failed");
+
+	file = p11_save_open_file (filename, 0);
+	CuAssertPtrNotNull (tc, file);
+
+	ret = p11_save_write_and_finish (file, test_cacert3_ca_der, sizeof (test_cacert3_ca_der));
+	CuAssertIntEquals (tc, true, ret);
+	free (filename);
+
+	test_check_file (tc, test.directory, "extract-file", SRCDIR "/files/cacert3.der");
+
+	teardown (tc);
+}
+
+static void
+test_file_exists (CuTest *tc)
+{
+	p11_save_file *file;
+	char *filename;
+
+	setup (tc);
+
+	if (asprintf (&filename, "%s/%s", test.directory, "extract-file") < 0)
+		CuFail (tc, "asprintf() failed");
+
+	write_zero_file (tc, test.directory, "extract-file");
+
+	p11_message_quiet ();
+
+	file = p11_save_open_file (filename, 0);
+	CuAssertTrue (tc, file == NULL);
+
+	p11_message_loud ();
+
+	unlink (filename);
+	free (filename);
+	teardown (tc);
+}
+
+static void
+test_file_bad_directory (CuTest *tc)
+{
+	p11_save_file *file;
+	char *filename;
+
+	setup (tc);
+
+	if (asprintf (&filename, "/non-existent/%s/%s", test.directory, "extract-file") < 0)
+		CuFail (tc, "asprintf() failed");
+
+	p11_message_quiet ();
+
+	file = p11_save_open_file (filename, 0);
+	CuAssertTrue (tc, file == NULL);
+
+	p11_message_loud ();
+
+	free (filename);
+	teardown (tc);
+}
+
+static void
+test_file_overwrite (CuTest *tc)
+{
+	p11_save_file *file;
+	char *filename;
+	bool ret;
+
+	setup (tc);
+
+	if (asprintf (&filename, "%s/%s", test.directory, "extract-file") < 0)
+		CuFail (tc, "asprintf() failed");
+
+	write_zero_file (tc, test.directory, "extract-file");
+
+	file = p11_save_open_file (filename, P11_SAVE_OVERWRITE);
+	CuAssertPtrNotNull (tc, file);
+
+	ret = p11_save_write_and_finish (file, test_cacert3_ca_der, sizeof (test_cacert3_ca_der));
+	CuAssertIntEquals (tc, true, ret);
+	free (filename);
+
+	test_check_file (tc, test.directory, "extract-file", SRCDIR "/files/cacert3.der");
+
+	teardown (tc);
+}
+
+static void
+test_write_with_null (CuTest *tc)
+{
+	bool ret;
+
+	ret = p11_save_write (NULL, "test", 4);
+	CuAssertIntEquals (tc, false, ret);
+}
+
+static void
+test_write_and_finish_with_null (CuTest *tc)
+{
+	bool ret;
+
+	ret = p11_save_write_and_finish (NULL, "test", 4);
+	CuAssertIntEquals (tc, false, ret);
+}
+
+static void
+test_file_abort (CuTest *tc)
+{
+	struct stat st;
+	p11_save_file *file;
+	char *filename;
+	bool ret;
+
+	setup (tc);
+
+	if (asprintf (&filename, "%s/%s", test.directory, "extract-file") < 0)
+		CuFail (tc, "asprintf() failed");
+
+	file = p11_save_open_file (filename, 0);
+	CuAssertPtrNotNull (tc, file);
+
+	ret = p11_save_finish_file (file, false);
+	CuAssertIntEquals (tc, true, ret);
+
+	if (stat (filename, &st) >= 0 || errno != ENOENT)
+		CuFail (tc, "file should not exist");
+
+	free (filename);
+
+	teardown (tc);
+}
+
+
+static void
+test_directory_empty (CuTest *tc)
+{
+	p11_save_dir *dir;
+	char *subdir;
+	bool ret;
+
+	setup (tc);
+
+	if (asprintf (&subdir, "%s/%s", test.directory, "extract-dir") < 0)
+		CuFail (tc, "asprintf() failed");
+
+	dir = p11_save_open_directory (subdir, 0);
+	CuAssertPtrNotNull (tc, dir);
+
+	ret = p11_save_finish_directory (dir, true);
+	CuAssertIntEquals (tc, true, ret);
+
+	test_check_directory (tc, subdir, (NULL, NULL));
+
+	rmdir (subdir);
+	free (subdir);
+
+	teardown (tc);
+}
+
+static void
+test_directory_files (CuTest *tc)
+{
+	const char *filename;
+	p11_save_dir *dir;
+	char *subdir;
+	bool ret;
+
+	setup (tc);
+
+	if (asprintf (&subdir, "%s/%s", test.directory, "extract-dir") < 0)
+		CuFail (tc, "asprintf() failed");
+
+	dir = p11_save_open_directory (subdir, 0);
+	CuAssertPtrNotNull (tc, dir);
+
+	ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "blah", ".cer", &filename),
+	                                 test_cacert3_ca_der, sizeof (test_cacert3_ca_der));
+	CuAssertIntEquals (tc, true, ret);
+	CuAssertStrEquals (tc, "blah.cer", filename);
+
+	ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "file", ".txt", &filename),
+	                                 test_text, strlen (test_text));
+	CuAssertIntEquals (tc, true, ret);
+	CuAssertStrEquals (tc, "file.txt", filename);
+
+	ret = p11_save_symlink_in (dir, "link", ".ext", "/the/destination");
+	CuAssertIntEquals (tc, true, ret);
+
+	ret = p11_save_finish_directory (dir, true);
+	CuAssertIntEquals (tc, true, ret);
+
+	test_check_directory (tc, subdir, ("blah.cer", "file.txt", "link.ext", NULL));
+	test_check_file (tc, subdir, "blah.cer", SRCDIR "/files/cacert3.der");
+	test_check_data (tc, subdir, "file.txt", test_text, strlen (test_text));
+	test_check_symlink (tc, subdir, "link.ext", "/the/destination");
+
+	rmdir (subdir);
+	free (subdir);
+
+	teardown (tc);
+}
+
+static void
+test_directory_dups (CuTest *tc)
+{
+	const char *filename;
+	p11_save_dir *dir;
+	char *subdir;
+	bool ret;
+
+	setup (tc);
+
+	if (asprintf (&subdir, "%s/%s", test.directory, "extract-dir") < 0)
+		CuFail (tc, "asprintf() failed");
+
+	dir = p11_save_open_directory (subdir, 0);
+	CuAssertPtrNotNull (tc, dir);
+
+	ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "file", ".txt", &filename),
+	                                 test_text, 5);
+	CuAssertIntEquals (tc, true, ret);
+	CuAssertStrEquals (tc, "file.txt", filename);
+
+	ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "file", ".txt", &filename),
+	                                 test_text, 10);
+	CuAssertIntEquals (tc, true, ret);
+	CuAssertStrEquals (tc, "file.1.txt", filename);
+
+	ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "file", ".txt", NULL),
+	                                 test_text, 15);
+	CuAssertIntEquals (tc, true, ret);
+
+	ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "no-ext", NULL, NULL),
+	                                 test_text, 8);
+	CuAssertIntEquals (tc, true, ret);
+
+	ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "no-ext", NULL, NULL),
+	                                 test_text, 16);
+	CuAssertIntEquals (tc, true, ret);
+
+	ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "with-num", ".0", NULL),
+	                                 test_text, 14);
+	CuAssertIntEquals (tc, true, ret);
+
+	ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "with-num", ".0", NULL),
+	                                 test_text, 15);
+	CuAssertIntEquals (tc, true, ret);
+
+	ret = p11_save_symlink_in (dir, "link", ".0", "/destination1");
+	CuAssertIntEquals (tc, true, ret);
+
+	ret = p11_save_symlink_in (dir, "link", ".0", "/destination2");
+	CuAssertIntEquals (tc, true, ret);
+
+	ret = p11_save_finish_directory (dir, true);
+	CuAssertIntEquals (tc, true, ret);
+
+	test_check_directory (tc, subdir, ("file.txt", "file.1.txt", "file.2.txt",
+	                                   "no-ext", "no-ext.1",
+	                                   "with-num.0", "with-num.1",
+	                                   "link.0", "link.1",
+	                                   NULL));
+	test_check_data (tc, subdir, "file.txt", test_text, 5);
+	test_check_data (tc, subdir, "file.1.txt", test_text, 10);
+	test_check_data (tc, subdir, "file.2.txt", test_text, 15);
+	test_check_data (tc, subdir, "no-ext", test_text, 8);
+	test_check_data (tc, subdir, "no-ext.1", test_text, 16);
+	test_check_data (tc, subdir, "with-num.0", test_text, 14);
+	test_check_data (tc, subdir, "with-num.1", test_text, 15);
+	test_check_symlink (tc, subdir, "link.0", "/destination1");
+	test_check_symlink (tc, subdir, "link.1", "/destination2");
+
+	rmdir (subdir);
+	free (subdir);
+
+	teardown (tc);
+}
+
+static void
+test_directory_exists (CuTest *tc)
+{
+	p11_save_dir *dir;
+	char *subdir;
+
+	setup (tc);
+
+	if (asprintf (&subdir, "%s/%s", test.directory, "extract-dir") < 0)
+		CuFail (tc, "asprintf() failed");
+
+	if (mkdir (subdir, S_IRWXU) < 0)
+		CuFail (tc, "mkdir() failed");
+
+	p11_message_quiet ();
+
+	dir = p11_save_open_directory (subdir, 0);
+	CuAssertPtrEquals (tc, NULL, dir);
+
+	p11_message_loud ();
+
+	rmdir (subdir);
+	free (subdir);
+
+	teardown (tc);
+}
+
+static void
+test_directory_overwrite (CuTest *tc)
+{
+	const char *filename;
+	p11_save_dir *dir;
+	char *subdir;
+	bool ret;
+
+	setup (tc);
+
+	if (asprintf (&subdir, "%s/%s", test.directory, "extract-dir") < 0)
+		CuFail (tc, "asprintf() failed");
+
+	if (mkdir (subdir, S_IRWXU) < 0)
+		CuFail (tc, "mkdir() failed");
+
+	/* Some initial files into this directory, which get overwritten */
+	write_zero_file (tc, subdir, "file.txt");
+	write_zero_file (tc, subdir, "another-file");
+	write_zero_file (tc, subdir, "third-file");
+
+	dir = p11_save_open_directory (subdir, P11_SAVE_OVERWRITE);
+	CuAssertPtrNotNull (tc, dir);
+
+	ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "blah", ".cer", &filename),
+	                                 test_cacert3_ca_der, sizeof (test_cacert3_ca_der));
+	CuAssertIntEquals (tc, true, ret);
+	CuAssertStrEquals (tc, "blah.cer", filename);
+
+	ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "file", ".txt", &filename),
+	                                 test_text, strlen (test_text));
+	CuAssertIntEquals (tc, true, ret);
+	CuAssertStrEquals (tc, "file.txt", filename);
+
+	ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "file", ".txt", &filename),
+	                                 test_text, 10);
+	CuAssertIntEquals (tc, true, ret);
+	CuAssertStrEquals (tc, "file.1.txt", filename);
+
+	ret = p11_save_finish_directory (dir, true);
+	CuAssertIntEquals (tc, true, ret);
+
+	test_check_directory (tc, subdir, ("blah.cer", "file.txt", "file.1.txt", NULL));
+	test_check_data (tc, subdir, "blah.cer", test_cacert3_ca_der, sizeof (test_cacert3_ca_der));
+	test_check_data (tc, subdir, "file.txt", test_text, strlen (test_text));
+	test_check_data (tc, subdir, "file.1.txt", test_text, 10);
+
+	rmdir (subdir);
+	free (subdir);
+
+	teardown (tc);
+}
+
+int
+main (void)
+{
+	CuString *output = CuStringNew ();
+	CuSuite* suite = CuSuiteNew ();
+	int ret;
+
+	SUITE_ADD_TEST (suite, test_file_write);
+	SUITE_ADD_TEST (suite, test_file_exists);
+	SUITE_ADD_TEST (suite, test_file_bad_directory);
+	SUITE_ADD_TEST (suite, test_file_overwrite);
+	SUITE_ADD_TEST (suite, test_write_with_null);
+	SUITE_ADD_TEST (suite, test_write_and_finish_with_null);
+	SUITE_ADD_TEST (suite, test_file_abort);
+
+	SUITE_ADD_TEST (suite, test_directory_empty);
+	SUITE_ADD_TEST (suite, test_directory_files);
+	SUITE_ADD_TEST (suite, test_directory_dups);
+	SUITE_ADD_TEST (suite, test_directory_exists);
+	SUITE_ADD_TEST (suite, test_directory_overwrite);
+
+	CuSuiteRun (suite);
+	CuSuiteSummary (suite, output);
+	CuSuiteDetails (suite, output);
+	printf ("%s\n", output->buffer);
+	ret = suite->failCount;
+	CuSuiteDelete (suite);
+	CuStringDelete (output);
+
+	return ret;
+}
diff --git a/tools/tests/test.c b/tools/tests/test.c
new file mode 100644
index 0000000..ecc0410
--- /dev/null
+++ b/tools/tests/test.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2013, Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above
+ *       copyright notice, this list of conditions and the
+ *       following disclaimer.
+ *     * 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.
+ *     * The names of contributors to this software may not be
+ *       used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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
+ * COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include "config.h"
+#include "CuTest.h"
+
+#include "debug.h"
+#include "test.h"
+
+#include <assert.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+static char *
+read_file (CuTest *tc,
+           const char *file,
+           int line,
+           const char *filename,
+           long *len)
+{
+	FILE *f = NULL;
+	char *data;
+
+	f = fopen (filename, "r");
+	if (f == NULL)
+		CuFail_Line (tc, file, line, "Couldn't open file", filename);
+
+	/* Figure out size */
+	if (fseek (f, 0, SEEK_END) == -1 ||
+	    (*len = ftell (f)) == -1 ||
+	    fseek (f, 0, SEEK_SET) == -1) {
+		CuFail_Line (tc, file, line, "Couldn't get file length", filename);
+	}
+
+	data = malloc (*len ? *len : 1);
+	assert (data != NULL);
+
+	/* And read in one block */
+	if (fread (data, 1, *len, f) != *len)
+		CuFail_Line (tc, file, line, "Couldn't read file", filename);
+
+	fclose (f);
+
+	return data;
+}
+
+void
+test_check_file_msg (CuTest *tc,
+                     const char *file,
+                     int line,
+                     const char *directory,
+                     const char *name,
+                     const char *reference)
+{
+	char *refdata;
+	long reflen;
+
+	refdata = read_file (tc, file, line, reference, &reflen);
+	test_check_data_msg (tc, file, line, directory, name, refdata, reflen);
+	free (refdata);
+}
+
+void
+test_check_data_msg (CuTest *tc,
+                     const char *file,
+                     int line,
+                     const char *directory,
+                     const char *name,
+                     const void *refdata,
+                     long reflen)
+{
+	char *filedata;
+	char *filename;
+	long filelen;
+
+	if (asprintf (&filename, "%s/%s", directory, name) < 0)
+		CuFail_Line (tc, file, line, "asprintf() failed", NULL);
+
+	filedata = read_file (tc, file, line, filename, &filelen);
+
+	if (filelen != reflen || memcmp (filedata, refdata, reflen) != 0)
+		CuFail_Line (tc, file, line, "File contents not as expected", filename);
+
+	unlink (filename);
+	free (filename);
+	free (filedata);
+}
+
+void
+test_check_symlink_msg (CuTest *tc,
+                        const char *file,
+                        int line,
+                        const char *directory,
+                        const char *name,
+                        const char *destination)
+{
+	char buf[1024] = { 0, };
+	char *filename;
+
+	if (asprintf (&filename, "%s/%s", directory, name) < 0)
+		CuFail_Line (tc, file, line, "asprintf() failed", NULL);
+
+	if (readlink (filename, buf, sizeof (buf)) < 0)
+		CuFail_Line (tc, file, line, "Couldn't read symlink", filename);
+
+	CuAssertStrEquals_LineMsg (tc, file, line, "symlink contents wrong", destination, buf);
+
+	unlink (filename);
+	free (filename);
+}
+
+p11_dict *
+test_check_directory_files (const char *file,
+                            ...)
+{
+	p11_dict *files;
+	va_list va;
+
+	files = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal, NULL, NULL);
+
+	va_start (va, file);
+
+	while (file != NULL) {
+		if (!p11_dict_set (files, (void *)file, (void *)file))
+			return_val_if_reached (NULL);
+		file = va_arg (va, const char *);
+	}
+
+	va_end (va);
+
+	return files;
+}
+
+void
+test_check_directory_msg (CuTest *tc,
+                          const char *file,
+                          int line,
+                          const char *directory,
+                          p11_dict *files)
+{
+	p11_dictiter iter;
+	struct dirent *dp;
+	const char *name;
+	DIR *dir;
+
+	dir = opendir (directory);
+	if (dir == NULL)
+		CuFail_Line (tc, file ,line, "Couldn't open directory", directory);
+
+	while ((dp = readdir (dir)) != NULL) {
+		if (strcmp (dp->d_name, ".") == 0 ||
+		    strcmp (dp->d_name, "..") == 0)
+			continue;
+
+		if (!p11_dict_remove (files, dp->d_name))
+			CuFail_Line (tc, file, line, "Unexpected file in directory", dp->d_name);
+	}
+
+	closedir (dir);
+
+	p11_dict_iterate (files, &iter);
+	while (p11_dict_next (&iter, (void **)&name, NULL))
+		CuFail_Line (tc, file, line, "Couldn't find file in directory", name);
+
+	p11_dict_free (files);
+}
diff --git a/tools/tests/test.h b/tools/tests/test.h
new file mode 100644
index 0000000..c3e0d08
--- /dev/null
+++ b/tools/tests/test.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2013, Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     * Redistributions of source code must retain the above
+ *       copyright notice, this list of conditions and the
+ *       following disclaimer.
+ *     * 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.
+ *     * The names of contributors to this software may not be
+ *       used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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
+ * COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#ifndef TEST_COMMON_H_
+#define TEST_COMMON_H_
+
+#include "CuTest.h"
+
+#include "dict.h"
+
+#include <stdlib.h>
+
+static const char test_text[] = "This is the file text";
+
+static const unsigned char test_cacert3_ca_der[] = {
+	0x30, 0x82, 0x07, 0x59, 0x30, 0x82, 0x05, 0x41, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x0a,
+	0x41, 0x8a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
+	0x00, 0x30, 0x79, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x52, 0x6f,
+	0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x15,
+	0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x63, 0x61, 0x63, 0x65, 0x72,
+	0x74, 0x2e, 0x6f, 0x72, 0x67, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19,
+	0x43, 0x41, 0x20, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20,
+	0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a,
+	0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x12, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72,
+	0x74, 0x40, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x1e, 0x17, 0x0d,
+	0x31, 0x31, 0x30, 0x35, 0x32, 0x33, 0x31, 0x37, 0x34, 0x38, 0x30, 0x32, 0x5a, 0x17, 0x0d, 0x32,
+	0x31, 0x30, 0x35, 0x32, 0x30, 0x31, 0x37, 0x34, 0x38, 0x30, 0x32, 0x5a, 0x30, 0x54, 0x31, 0x14,
+	0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b, 0x43, 0x41, 0x63, 0x65, 0x72, 0x74, 0x20,
+	0x49, 0x6e, 0x63, 0x2e, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x15, 0x68,
+	0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x43, 0x41, 0x63, 0x65, 0x72, 0x74,
+	0x2e, 0x6f, 0x72, 0x67, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x13, 0x43,
+	0x41, 0x63, 0x65, 0x72, 0x74, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x33, 0x20, 0x52, 0x6f,
+	0x6f, 0x74, 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+	0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a, 0x02, 0x82,
+	0x02, 0x01, 0x00, 0xab, 0x49, 0x35, 0x11, 0x48, 0x7c, 0xd2, 0x26, 0x7e, 0x53, 0x94, 0xcf, 0x43,
+	0xa9, 0xdd, 0x28, 0xd7, 0x42, 0x2a, 0x8b, 0xf3, 0x87, 0x78, 0x19, 0x58, 0x7c, 0x0f, 0x9e, 0xda,
+	0x89, 0x7d, 0xe1, 0xfb, 0xeb, 0x72, 0x90, 0x0d, 0x74, 0xa1, 0x96, 0x64, 0xab, 0x9f, 0xa0, 0x24,
+	0x99, 0x73, 0xda, 0xe2, 0x55, 0x76, 0xc7, 0x17, 0x7b, 0xf5, 0x04, 0xac, 0x46, 0xb8, 0xc3, 0xbe,
+	0x7f, 0x64, 0x8d, 0x10, 0x6c, 0x24, 0xf3, 0x61, 0x9c, 0xc0, 0xf2, 0x90, 0xfa, 0x51, 0xe6, 0xf5,
+	0x69, 0x01, 0x63, 0xc3, 0x0f, 0x56, 0xe2, 0x4a, 0x42, 0xcf, 0xe2, 0x44, 0x8c, 0x25, 0x28, 0xa8,
+	0xc5, 0x79, 0x09, 0x7d, 0x46, 0xb9, 0x8a, 0xf3, 0xe9, 0xf3, 0x34, 0x29, 0x08, 0x45, 0xe4, 0x1c,
+	0x9f, 0xcb, 0x94, 0x04, 0x1c, 0x81, 0xa8, 0x14, 0xb3, 0x98, 0x65, 0xc4, 0x43, 0xec, 0x4e, 0x82,
+	0x8d, 0x09, 0xd1, 0xbd, 0xaa, 0x5b, 0x8d, 0x92, 0xd0, 0xec, 0xde, 0x90, 0xc5, 0x7f, 0x0a, 0xc2,
+	0xe3, 0xeb, 0xe6, 0x31, 0x5a, 0x5e, 0x74, 0x3e, 0x97, 0x33, 0x59, 0xe8, 0xc3, 0x03, 0x3d, 0x60,
+	0x33, 0xbf, 0xf7, 0xd1, 0x6f, 0x47, 0xc4, 0xcd, 0xee, 0x62, 0x83, 0x52, 0x6e, 0x2e, 0x08, 0x9a,
+	0xa4, 0xd9, 0x15, 0x18, 0x91, 0xa6, 0x85, 0x92, 0x47, 0xb0, 0xae, 0x48, 0xeb, 0x6d, 0xb7, 0x21,
+	0xec, 0x85, 0x1a, 0x68, 0x72, 0x35, 0xab, 0xff, 0xf0, 0x10, 0x5d, 0xc0, 0xf4, 0x94, 0xa7, 0x6a,
+	0xd5, 0x3b, 0x92, 0x7e, 0x4c, 0x90, 0x05, 0x7e, 0x93, 0xc1, 0x2c, 0x8b, 0xa4, 0x8e, 0x62, 0x74,
+	0x15, 0x71, 0x6e, 0x0b, 0x71, 0x03, 0xea, 0xaf, 0x15, 0x38, 0x9a, 0xd4, 0xd2, 0x05, 0x72, 0x6f,
+	0x8c, 0xf9, 0x2b, 0xeb, 0x5a, 0x72, 0x25, 0xf9, 0x39, 0x46, 0xe3, 0x72, 0x1b, 0x3e, 0x04, 0xc3,
+	0x64, 0x27, 0x22, 0x10, 0x2a, 0x8a, 0x4f, 0x58, 0xa7, 0x03, 0xad, 0xbe, 0xb4, 0x2e, 0x13, 0xed,
+	0x5d, 0xaa, 0x48, 0xd7, 0xd5, 0x7d, 0xd4, 0x2a, 0x7b, 0x5c, 0xfa, 0x46, 0x04, 0x50, 0xe4, 0xcc,
+	0x0e, 0x42, 0x5b, 0x8c, 0xed, 0xdb, 0xf2, 0xcf, 0xfc, 0x96, 0x93, 0xe0, 0xdb, 0x11, 0x36, 0x54,
+	0x62, 0x34, 0x38, 0x8f, 0x0c, 0x60, 0x9b, 0x3b, 0x97, 0x56, 0x38, 0xad, 0xf3, 0xd2, 0x5b, 0x8b,
+	0xa0, 0x5b, 0xea, 0x4e, 0x96, 0xb8, 0x7c, 0xd7, 0xd5, 0xa0, 0x86, 0x70, 0x40, 0xd3, 0x91, 0x29,
+	0xb7, 0xa2, 0x3c, 0xad, 0xf5, 0x8c, 0xbb, 0xcf, 0x1a, 0x92, 0x8a, 0xe4, 0x34, 0x7b, 0xc0, 0xd8,
+	0x6c, 0x5f, 0xe9, 0x0a, 0xc2, 0xc3, 0xa7, 0x20, 0x9a, 0x5a, 0xdf, 0x2c, 0x5d, 0x52, 0x5c, 0xba,
+	0x47, 0xd5, 0x9b, 0xef, 0x24, 0x28, 0x70, 0x38, 0x20, 0x2f, 0xd5, 0x7f, 0x29, 0xc0, 0xb2, 0x41,
+	0x03, 0x68, 0x92, 0xcc, 0xe0, 0x9c, 0xcc, 0x97, 0x4b, 0x45, 0xef, 0x3a, 0x10, 0x0a, 0xab, 0x70,
+	0x3a, 0x98, 0x95, 0x70, 0xad, 0x35, 0xb1, 0xea, 0x85, 0x2b, 0xa4, 0x1c, 0x80, 0x21, 0x31, 0xa9,
+	0xae, 0x60, 0x7a, 0x80, 0x26, 0x48, 0x00, 0xb8, 0x01, 0xc0, 0x93, 0x63, 0x55, 0x22, 0x91, 0x3c,
+	0x56, 0xe7, 0xaf, 0xdb, 0x3a, 0x25, 0xf3, 0x8f, 0x31, 0x54, 0xea, 0x26, 0x8b, 0x81, 0x59, 0xf9,
+	0xa1, 0xd1, 0x53, 0x11, 0xc5, 0x7b, 0x9d, 0x03, 0xf6, 0x74, 0x11, 0xe0, 0x6d, 0xb1, 0x2c, 0x3f,
+	0x2c, 0x86, 0x91, 0x99, 0x71, 0x9a, 0xa6, 0x77, 0x8b, 0x34, 0x60, 0xd1, 0x14, 0xb4, 0x2c, 0xac,
+	0x9d, 0xaf, 0x8c, 0x10, 0xd3, 0x9f, 0xc4, 0x6a, 0xf8, 0x6f, 0x13, 0xfc, 0x73, 0x59, 0xf7, 0x66,
+	0x42, 0x74, 0x1e, 0x8a, 0xe3, 0xf8, 0xdc, 0xd2, 0x6f, 0x98, 0x9c, 0xcb, 0x47, 0x98, 0x95, 0x40,
+	0x05, 0xfb, 0xe9, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x02, 0x0d, 0x30, 0x82, 0x02, 0x09,
+	0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x75, 0xa8, 0x71, 0x60, 0x4c,
+	0x88, 0x13, 0xf0, 0x78, 0xd9, 0x89, 0x77, 0xb5, 0x6d, 0xc5, 0x89, 0xdf, 0xbc, 0xb1, 0x7a, 0x30,
+	0x81, 0xa3, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81, 0x9b, 0x30, 0x81, 0x98, 0x80, 0x14, 0x16,
+	0xb5, 0x32, 0x1b, 0xd4, 0xc7, 0xf3, 0xe0, 0xe6, 0x8e, 0xf3, 0xbd, 0xd2, 0xb0, 0x3a, 0xee, 0xb2,
+	0x39, 0x18, 0xd1, 0xa1, 0x7d, 0xa4, 0x7b, 0x30, 0x79, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55,
+	0x04, 0x0a, 0x13, 0x07, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1e, 0x30, 0x1c, 0x06,
+	0x03, 0x55, 0x04, 0x0b, 0x13, 0x15, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77,
+	0x2e, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x31, 0x22, 0x30, 0x20, 0x06,
+	0x03, 0x55, 0x04, 0x03, 0x13, 0x19, 0x43, 0x41, 0x20, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x69,
+	0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31,
+	0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x12,
+	0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x40, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f,
+	0x72, 0x67, 0x82, 0x01, 0x00, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04,
+	0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x5d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+	0x01, 0x01, 0x04, 0x51, 0x30, 0x4f, 0x30, 0x23, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+	0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e,
+	0x43, 0x41, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x30, 0x28, 0x06, 0x08, 0x2b,
+	0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
+	0x77, 0x77, 0x77, 0x2e, 0x43, 0x41, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x63,
+	0x61, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x4a, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x43, 0x30, 0x41,
+	0x30, 0x3f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x81, 0x90, 0x4a, 0x30, 0x33, 0x30, 0x31,
+	0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x25, 0x68, 0x74, 0x74, 0x70,
+	0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x43, 0x41, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f, 0x72,
+	0x67, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e, 0x70, 0x68, 0x70, 0x3f, 0x69, 0x64, 0x3d, 0x31,
+	0x30, 0x30, 0x34, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x08, 0x04, 0x27,
+	0x16, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x43, 0x41, 0x63,
+	0x65, 0x72, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2e, 0x70, 0x68,
+	0x70, 0x3f, 0x69, 0x64, 0x3d, 0x31, 0x30, 0x30, 0x50, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86,
+	0xf8, 0x42, 0x01, 0x0d, 0x04, 0x43, 0x16, 0x41, 0x54, 0x6f, 0x20, 0x67, 0x65, 0x74, 0x20, 0x79,
+	0x6f, 0x75, 0x72, 0x20, 0x6f, 0x77, 0x6e, 0x20, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+	0x61, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x46, 0x52, 0x45, 0x45, 0x2c, 0x20, 0x67, 0x6f,
+	0x20, 0x74, 0x6f, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x43,
+	0x41, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+	0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x29, 0x28, 0x85,
+	0xae, 0x44, 0xa9, 0xb9, 0xaf, 0xa4, 0x79, 0x13, 0xf0, 0xa8, 0xa3, 0x2b, 0x97, 0x60, 0xf3, 0x5c,
+	0xee, 0xe3, 0x2f, 0xc1, 0xf6, 0xe2, 0x66, 0xa0, 0x11, 0xae, 0x36, 0x37, 0x3a, 0x76, 0x15, 0x04,
+	0x53, 0xea, 0x42, 0xf5, 0xf9, 0xea, 0xc0, 0x15, 0xd8, 0xa6, 0x82, 0xd9, 0xe4, 0x61, 0xae, 0x72,
+	0x0b, 0x29, 0x5c, 0x90, 0x43, 0xe8, 0x41, 0xb2, 0xe1, 0x77, 0xdb, 0x02, 0x13, 0x44, 0x78, 0x47,
+	0x55, 0xaf, 0x58, 0xfc, 0xcc, 0x98, 0xf6, 0x45, 0xb9, 0xd1, 0x20, 0xf8, 0xd8, 0x21, 0x07, 0xfe,
+	0x6d, 0xaa, 0x73, 0xd4, 0xb3, 0xc6, 0x07, 0xe9, 0x09, 0x85, 0xcc, 0x3b, 0xf2, 0xb6, 0xbe, 0x2c,
+	0x1c, 0x25, 0xd5, 0x71, 0x8c, 0x39, 0xb5, 0x2e, 0xea, 0xbe, 0x18, 0x81, 0xba, 0xb0, 0x93, 0xb8,
+	0x0f, 0xe3, 0xe6, 0xd7, 0x26, 0x8c, 0x31, 0x5a, 0x72, 0x03, 0x84, 0x52, 0xe6, 0xa6, 0xf5, 0x33,
+	0x22, 0x45, 0x0a, 0xc8, 0x0b, 0x0d, 0x8a, 0xb8, 0x36, 0x6f, 0x90, 0x09, 0xa1, 0xab, 0xbd, 0xd7,
+	0xd5, 0x4e, 0x2e, 0x71, 0xa2, 0xd4, 0xae, 0xfa, 0xa7, 0x54, 0x2b, 0xeb, 0x35, 0x8d, 0x5a, 0xb7,
+	0x54, 0x88, 0x2f, 0xee, 0x74, 0x9f, 0xed, 0x48, 0x16, 0xca, 0x0d, 0x48, 0xd0, 0x94, 0xd3, 0xac,
+	0xa4, 0xa2, 0xf6, 0x24, 0xdf, 0x92, 0xe3, 0xbd, 0xeb, 0x43, 0x40, 0x91, 0x6e, 0x1c, 0x18, 0x8e,
+	0x56, 0xb4, 0x82, 0x12, 0xf3, 0xa9, 0x93, 0x9f, 0xd4, 0xbc, 0x9c, 0xad, 0x9c, 0x75, 0xee, 0x5a,
+	0x97, 0x1b, 0x95, 0xe7, 0x74, 0x2d, 0x1c, 0x0f, 0xb0, 0x2c, 0x97, 0x9f, 0xfb, 0xa9, 0x33, 0x39,
+	0x7a, 0xe7, 0x03, 0x3a, 0x92, 0x8e, 0x22, 0xf6, 0x8c, 0x0d, 0xe4, 0xd9, 0x7e, 0x0d, 0x76, 0x18,
+	0xf7, 0x01, 0xf9, 0xef, 0x96, 0x96, 0xa2, 0x55, 0x73, 0xc0, 0x3c, 0x71, 0xb4, 0x1d, 0x1a, 0x56,
+	0x43, 0xb7, 0xc3, 0x0a, 0x8d, 0x72, 0xfc, 0xe2, 0x10, 0x09, 0x0b, 0x41, 0xce, 0x8c, 0x94, 0xa0,
+	0xf9, 0x03, 0xfd, 0x71, 0x73, 0x4b, 0x8a, 0x57, 0x33, 0xe5, 0x8e, 0x74, 0x7e, 0x15, 0x01, 0x00,
+	0xe6, 0xcc, 0x4a, 0x1c, 0xe7, 0x7f, 0x95, 0x19, 0x2d, 0xc5, 0xa5, 0x0c, 0x8b, 0xbb, 0xb5, 0xed,
+	0x85, 0xb3, 0x5c, 0xd3, 0xdf, 0xb8, 0xb9, 0xf2, 0xca, 0xc7, 0x0d, 0x01, 0x14, 0xac, 0x70, 0x58,
+	0xc5, 0x8c, 0x8d, 0x33, 0xd4, 0x9d, 0x66, 0xa3, 0x1a, 0x50, 0x95, 0x23, 0xfc, 0x48, 0xe0, 0x06,
+	0x43, 0x12, 0xd9, 0xcd, 0xa7, 0x86, 0x39, 0x2f, 0x36, 0x72, 0xa3, 0x80, 0x10, 0xe4, 0xe1, 0xf3,
+	0xd1, 0xcb, 0x5b, 0x1a, 0xc0, 0xe4, 0x80, 0x9a, 0x7c, 0x13, 0x73, 0x06, 0x4f, 0xdb, 0xa3, 0x6b,
+	0x24, 0x0a, 0xba, 0xb3, 0x1c, 0xbc, 0x4a, 0x78, 0xbb, 0xe5, 0xe3, 0x75, 0x38, 0xa5, 0x48, 0xa7,
+	0xa2, 0x1e, 0xaf, 0x76, 0xd4, 0x5e, 0xf7, 0x38, 0x86, 0x56, 0x5a, 0x89, 0xce, 0xd6, 0xc3, 0xa7,
+	0x79, 0xb2, 0x52, 0xa0, 0xc6, 0xf1, 0x85, 0xb4, 0x25, 0x8c, 0xf2, 0x3f, 0x96, 0xb3, 0x10, 0xd9,
+	0x8d, 0x6c, 0x57, 0x3b, 0x9f, 0x6f, 0x86, 0x3a, 0x18, 0x82, 0x22, 0x36, 0xc8, 0xb0, 0x91, 0x38,
+	0xdb, 0x2a, 0xa1, 0x93, 0xaa, 0x84, 0x3f, 0xf5, 0x27, 0x65, 0xae, 0x73, 0xd5, 0xc8, 0xd5, 0xd3,
+	0x77, 0xea, 0x4b, 0x9d, 0xc7, 0x41, 0xbb, 0xc7, 0xc0, 0xe3, 0xa0, 0x3f, 0xe4, 0x7d, 0xa4, 0x8d,
+	0x73, 0xe6, 0x12, 0x4b, 0xdf, 0xa1, 0x73, 0x73, 0x73, 0x3a, 0x80, 0xe8, 0xd5, 0xcb, 0x8e, 0x2f,
+	0xcb, 0xea, 0x13, 0xa7, 0xd6, 0x41, 0x8b, 0xac, 0xfa, 0x3c, 0x89, 0xd7, 0x24, 0xf5, 0x4e, 0xb4,
+	0xe0, 0x61, 0x92, 0xb7, 0xf3, 0x37, 0x98, 0xc4, 0xbe, 0x96, 0xa3, 0xb7, 0x8a,
+};
+
+void       test_check_file_msg          (CuTest *tc,
+                                         const char *file,
+                                         int line,
+                                         const char *directory,
+                                         const char *filename,
+                                         const char *reference);
+
+void       test_check_data_msg          (CuTest *tc,
+                                         const char *file,
+                                         int line,
+                                         const char *directory,
+                                         const char *filename,
+                                         const void *refdata,
+                                         long reflen);
+
+void       test_check_symlink_msg       (CuTest *tc,
+                                         const char *file,
+                                         int line,
+                                         const char *directory,
+                                         const char *name,
+                                         const char *destination);
+
+p11_dict * test_check_directory_files   (const char *file,
+                                         ...) GNUC_NULL_TERMINATED;
+
+void       test_check_directory_msg     (CuTest *tc,
+                                         const char *file,
+                                         int line,
+                                         const char *directory,
+                                         p11_dict *files);
+
+#define test_check_file(tc, directory, name, reference) \
+	(test_check_file_msg (tc, __FILE__, __LINE__, directory, name, reference))
+
+#define test_check_data(tc, directory, name, data, length) \
+	(test_check_data_msg (tc, __FILE__, __LINE__, directory, name, data, length))
+
+#define test_check_symlink(tc, directory, name, destination) \
+	(test_check_symlink_msg (tc, __FILE__, __LINE__, directory, name, destination))
+
+#define test_check_directory(tc, directory, files) \
+	(test_check_directory_msg (tc, __FILE__, __LINE__, directory, \
+	                           test_check_directory_files files))
+
+#endif /* TEST_COMMON_H_ */
-- 
cgit v1.1