[gnome-keyring] gnome2-store: Fix locking and add tests for locking



commit 49d16a7888d37af0cb1d6339351e1268487193d9
Author: Stef Walter <stefw collabora co uk>
Date:   Sat Oct 15 22:37:34 2011 +0200

    gnome2-store: Fix locking and add tests for locking
    
     * Fix locking and add tests for locking between processes
     * Fix bug when writing multiple values, which caused the file
       to be written out twice.

 .gitignore                                         |    3 +-
 pkcs11/gnome2-store/gkm-gnome2-module.c            |    8 +
 pkcs11/gnome2-store/gkm-gnome2-storage.c           |   95 ++++--
 pkcs11/gnome2-store/tests/Makefile.am              |    7 +-
 .../tests/files/Thawte_Personal_Premium_CA.cer     |  Bin 0 -> 813 bytes
 .../gnome2-store/tests/files/test-certificate.cer  |  Bin 0 -> 1857 bytes
 pkcs11/gnome2-store/tests/files/user.keystore      |  Bin 0 -> 226 bytes
 pkcs11/gnome2-store/tests/mock-gnome2-module.c     |  111 ++++++
 pkcs11/gnome2-store/tests/mock-gnome2-module.h     |   43 +++
 pkcs11/gnome2-store/tests/test-gnome2-storage.c    |  374 ++++++++++++++++++++
 10 files changed, 612 insertions(+), 29 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 37da3b0..991b4d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -113,7 +113,8 @@ run-tests
 /pkcs11/gkm/tests/test-transaction
 
 /pkcs11/gnome2-store/tests/frob-gnome2-file
-/pkcs11/gnome2-store/tests/test-gnome2-file
+/pkcs11/gnome2-store/tests/test-*
+!/pkcs11/gnome2-store/tests/test-*.c
 /pkcs11/gnome2-store/tests/check-gnome2-module
 /pkcs11/gnome2-store/tests/check-module
 
diff --git a/pkcs11/gnome2-store/gkm-gnome2-module.c b/pkcs11/gnome2-store/gkm-gnome2-module.c
index 6067b9d..a63e6af 100644
--- a/pkcs11/gnome2-store/gkm-gnome2-module.c
+++ b/pkcs11/gnome2-store/gkm-gnome2-module.c
@@ -77,6 +77,8 @@ static const CK_TOKEN_INFO user_module_token_info = {
 
 G_DEFINE_TYPE (GkmGnome2Module, gkm_gnome2_module, GKM_TYPE_MODULE);
 
+GkmModule *  _gkm_gnome2_store_get_module_for_testing (void);
+
 /* -----------------------------------------------------------------------------
  * ACTUAL PKCS#11 Module Implementation
  */
@@ -348,3 +350,9 @@ gkm_gnome2_store_get_functions (void)
 	gkm_crypto_initialize ();
 	return gkm_gnome2_module_function_list;
 }
+
+GkmModule *
+_gkm_gnome2_store_get_module_for_testing (void)
+{
+	return pkcs11_module;
+}
diff --git a/pkcs11/gnome2-store/gkm-gnome2-storage.c b/pkcs11/gnome2-store/gkm-gnome2-storage.c
index feaf252..cd705c7 100644
--- a/pkcs11/gnome2-store/gkm-gnome2-storage.c
+++ b/pkcs11/gnome2-store/gkm-gnome2-storage.c
@@ -27,6 +27,8 @@
 #include "gkm-gnome2-storage.h"
 
 #include "gkm/gkm-certificate.h"
+#define DEBUG_FLAG GKM_DEBUG_STORAGE
+#include "gkm/gkm-debug.h"
 #include "gkm/gkm-data-asn1.h"
 #include "gkm/gkm-manager.h"
 #include "gkm/gkm-module.h"
@@ -218,20 +220,9 @@ type_from_identifier (const gchar *identifier)
 	return type_from_extension (ext);
 }
 
-static gboolean
-complete_lock_file (GkmTransaction *transaction, GObject *object, gpointer data)
-{
-	int fd = GPOINTER_TO_INT (data);
-
-	/* This also unlocks the file */
-	close (fd);
-
-	/* Completed successfully */
-	return TRUE;
-}
-
 static gint
-begin_lock_file (GkmGnome2Storage *self, GkmTransaction *transaction)
+lock_and_open_file (const gchar *filename,
+                    gint flags)
 {
 	guint tries = 0;
 	gint fd = -1;
@@ -241,42 +232,39 @@ begin_lock_file (GkmGnome2Storage *self, GkmTransaction *transaction)
 	 * that's the callers job if necessary.
 	 */
 
-	g_assert (GKM_IS_GNOME2_STORAGE (self));
-	g_assert (GKM_IS_TRANSACTION (transaction));
-
-	g_return_val_if_fail (!gkm_transaction_get_failed (transaction), -1);
-
 	/* File lock retry loop */
 	for (tries = 0; TRUE; ++tries) {
 		if (tries > MAX_LOCK_TRIES) {
-			g_message ("couldn't write to store file: %s: file is locked", self->filename);
-			gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+			g_message ("couldn't write to store file: %s: file is locked", filename);
 			return -1;
 		}
 
-		fd = open (self->filename, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
+		fd = open (filename, flags, S_IRUSR | S_IWUSR);
 		if (fd == -1) {
-			g_message ("couldn't open store file: %s: %s", self->filename, g_strerror (errno));
-			gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+			if ((errno != ENOENT) || (flags & O_CREAT))
+				g_message ("couldn't open store file: %s: %s",
+				           filename, g_strerror (errno));
 			return -1;
 		}
 
 		if (flock (fd, LOCK_EX | LOCK_NB) < 0) {
 			if (errno != EWOULDBLOCK) {
-				g_message ("couldn't lock store file: %s: %s", self->filename, g_strerror (errno));
+				g_message ("couldn't lock store file: %s: %s",
+				           filename, g_strerror (errno));
 				close (fd);
-				gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED);
 				return -1;
 			}
 
+			gkm_debug ("waiting for locked file: %s", filename);
 			close (fd);
 			fd = -1;
 			g_usleep (200000);
 			continue;
 		}
 
+		gkm_debug ("successfully opened: %s", filename);
+
 		/* Successfully opened file */;
-		gkm_transaction_add (transaction, self, complete_lock_file, GINT_TO_POINTER (fd));
 		return fd;
 	}
 
@@ -284,6 +272,47 @@ begin_lock_file (GkmGnome2Storage *self, GkmTransaction *transaction)
 }
 
 static gboolean
+complete_lock_file (GkmTransaction *transaction, GObject *object, gpointer data)
+{
+	GkmGnome2Storage *self = GKM_GNOME2_STORAGE (object);
+	int fd = GPOINTER_TO_INT (data);
+
+	/* This also unlocks the file */
+	gkm_debug ("closing: %s", self->filename);
+	close (fd);
+
+	/* Completed successfully */
+	return TRUE;
+}
+
+static gint
+begin_lock_file (GkmGnome2Storage *self, GkmTransaction *transaction)
+{
+	gint fd;
+
+	/*
+	 * In this function we don't actually put the object into a 'write' state,
+	 * that's the callers job if necessary.
+	 */
+
+	g_assert (GKM_IS_GNOME2_STORAGE (self));
+	g_assert (GKM_IS_TRANSACTION (transaction));
+
+	g_return_val_if_fail (!gkm_transaction_get_failed (transaction), -1);
+	gkm_debug ("modifying: %s", self->filename);
+
+	fd = lock_and_open_file (self->filename, O_RDONLY | O_CREAT);
+	if (fd == -1) {
+		gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+		return fd;
+	}
+
+	/* Successfully opened file */;
+	gkm_transaction_add (transaction, self, complete_lock_file, GINT_TO_POINTER (fd));
+	return fd;
+}
+
+static gboolean
 complete_write_state (GkmTransaction *transaction, GObject *object, gpointer unused)
 {
 	GkmGnome2Storage *self = GKM_GNOME2_STORAGE (object);
@@ -389,6 +418,12 @@ begin_modification_state (GkmGnome2Storage *self, GkmTransaction *transaction)
 	struct stat sb;
 	CK_RV rv = CKR_OK;
 
+	/* Already in write state for this transaction? */
+	if (self->transaction != NULL) {
+		g_return_val_if_fail (self->transaction == transaction, FALSE);
+		return TRUE;
+	}
+
 	if (!begin_write_state (self, transaction))
 		return FALSE;
 
@@ -713,9 +748,10 @@ refresh_with_login (GkmGnome2Storage *self, GkmSecret *login)
 	int fd;
 
 	g_assert (GKM_GNOME2_STORAGE (self));
+	gkm_debug ("refreshing: %s", self->filename);
 
 	/* Open the file for reading */
-	fd = open (self->filename, O_RDONLY, 0);
+	fd = lock_and_open_file (self->filename, O_RDONLY);
 	if (fd == -1) {
 		/* No file, no worries */
 		if (errno == ENOENT)
@@ -753,7 +789,9 @@ refresh_with_login (GkmGnome2Storage *self, GkmSecret *login)
 	if (rv == CKR_FUNCTION_FAILED)
 		self->last_mtime = 0;
 
+	gkm_debug ("closing: %s", self->filename);
 	close (fd);
+
 	return rv;
 }
 
@@ -823,6 +861,9 @@ gkm_gnome2_storage_real_write_value (GkmStore *base, GkmTransaction *transaction
 		return;
 	}
 
+	if (!begin_modification_state (self, transaction))
+		return;
+
 	if (self->last_mtime == 0) {
 		rv = gkm_gnome2_storage_refresh (self);
 		if (rv != CKR_OK) {
diff --git a/pkcs11/gnome2-store/tests/Makefile.am b/pkcs11/gnome2-store/tests/Makefile.am
index 6dfa12a..1d09dba 100644
--- a/pkcs11/gnome2-store/tests/Makefile.am
+++ b/pkcs11/gnome2-store/tests/Makefile.am
@@ -21,7 +21,12 @@ CHECK_PROGS =
 endif
 
 TEST_PROGS = \
-	test-gnome2-file
+	test-gnome2-file \
+	test-gnome2-storage
+
+test_gnome2_storage_SOURCES = \
+	test-gnome2-storage.c \
+	mock-gnome2-module.c mock-gnome2-module.h
 
 check_PROGRAMS = $(TEST_PROGS)
 
diff --git a/pkcs11/gnome2-store/tests/files/Thawte_Personal_Premium_CA.cer b/pkcs11/gnome2-store/tests/files/Thawte_Personal_Premium_CA.cer
new file mode 100644
index 0000000..1c08437
Binary files /dev/null and b/pkcs11/gnome2-store/tests/files/Thawte_Personal_Premium_CA.cer differ
diff --git a/pkcs11/gnome2-store/tests/files/test-certificate.cer b/pkcs11/gnome2-store/tests/files/test-certificate.cer
new file mode 100644
index 0000000..719b0ff
Binary files /dev/null and b/pkcs11/gnome2-store/tests/files/test-certificate.cer differ
diff --git a/pkcs11/gnome2-store/tests/files/user.keystore b/pkcs11/gnome2-store/tests/files/user.keystore
new file mode 100644
index 0000000..d606f23
Binary files /dev/null and b/pkcs11/gnome2-store/tests/files/user.keystore differ
diff --git a/pkcs11/gnome2-store/tests/mock-gnome2-module.c b/pkcs11/gnome2-store/tests/mock-gnome2-module.c
new file mode 100644
index 0000000..f652fd0
--- /dev/null
+++ b/pkcs11/gnome2-store/tests/mock-gnome2-module.c
@@ -0,0 +1,111 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* mock-gnome2-module.c
+
+   Copyright (C) 2011 Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stef thewalter net>
+*/
+
+#include "config.h"
+
+#include "mock-gnome2-module.h"
+
+#include "egg/egg-secure-memory.h"
+
+#include "gkm/gkm-module.h"
+
+#include "gnome2-store/gkm-gnome2-store.h"
+
+EGG_SECURE_GLIB_DEFINITIONS ();
+
+static GMutex *mutex = NULL;
+
+GkmModule *    _gkm_gnome2_store_get_module_for_testing                 (void);
+
+GMutex    *    _gkm_module_get_scary_mutex_that_you_should_not_touch    (GkmModule *module);
+
+GkmModule *
+mock_gnome2_module_initialize_and_enter (void)
+{
+	CK_FUNCTION_LIST_PTR funcs;
+	GkmModule *module;
+	CK_RV rv;
+
+	funcs = gkm_gnome2_store_get_functions ();
+	rv = (funcs->C_Initialize) (NULL);
+	g_return_val_if_fail (rv == CKR_OK, NULL);
+
+	module = _gkm_gnome2_store_get_module_for_testing ();
+	g_return_val_if_fail (module, NULL);
+
+	mutex = _gkm_module_get_scary_mutex_that_you_should_not_touch (module);
+	mock_gnome2_module_enter ();
+
+	return module;
+}
+
+void
+mock_gnome2_module_leave_and_finalize (void)
+{
+	CK_FUNCTION_LIST_PTR funcs;
+	CK_RV rv;
+
+	mock_gnome2_module_leave ();
+
+	funcs = gkm_gnome2_store_get_functions ();
+	rv = (funcs->C_Finalize) (NULL);
+	g_return_if_fail (rv == CKR_OK);
+}
+
+void
+mock_gnome2_module_leave (void)
+{
+	g_assert (mutex);
+	g_mutex_unlock (mutex);
+}
+
+void
+mock_gnome2_module_enter (void)
+{
+	g_assert (mutex);
+	g_mutex_lock (mutex);
+}
+
+GkmSession *
+mock_gnome2_module_open_session (gboolean writable)
+{
+	CK_ULONG flags = CKF_SERIAL_SESSION;
+	CK_SESSION_HANDLE handle;
+	GkmModule *module;
+	GkmSession *session;
+	CK_RV rv;
+
+	module = _gkm_gnome2_store_get_module_for_testing ();
+	g_return_val_if_fail (module, NULL);
+
+	if (writable)
+		flags |= CKF_RW_SESSION;
+
+	rv = gkm_module_C_OpenSession (module, 1, flags, NULL, NULL, &handle);
+	g_assert (rv == CKR_OK);
+
+	session = gkm_module_lookup_session (module, handle);
+	g_assert (session);
+
+	return session;
+}
diff --git a/pkcs11/gnome2-store/tests/mock-gnome2-module.h b/pkcs11/gnome2-store/tests/mock-gnome2-module.h
new file mode 100644
index 0000000..cbd1e86
--- /dev/null
+++ b/pkcs11/gnome2-store/tests/mock-gnome2-module.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* test-module.h: A test PKCS#11 module implementation
+
+   Copyright (C) 2011 Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stef thewalter net>
+*/
+
+#ifndef MOCK_GNOME2_MODULE_H_
+#define MOCK_GNOME2_MODULE_H_
+
+#include <glib.h>
+
+#include "gkm/gkm-types.h"
+
+#include "pkcs11.h"
+
+void              mock_gnome2_module_leave                 (void);
+
+void              mock_gnome2_module_enter                 (void);
+
+GkmModule *       mock_gnome2_module_initialize_and_enter  (void);
+
+void              mock_gnome2_module_leave_and_finalize    (void);
+
+GkmSession *      mock_gnome2_module_open_session          (gboolean writable);
+
+#endif /* MOCK_GNOME2_MODULE_H_ */
diff --git a/pkcs11/gnome2-store/tests/test-gnome2-storage.c b/pkcs11/gnome2-store/tests/test-gnome2-storage.c
new file mode 100644
index 0000000..5cb4488
--- /dev/null
+++ b/pkcs11/gnome2-store/tests/test-gnome2-storage.c
@@ -0,0 +1,374 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* unit-test-file-store.c: Test file store functionality
+
+   Copyright (C) 2008 Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stef memberwebs com>
+*/
+
+#include "config.h"
+
+#include "mock-gnome2-module.h"
+
+#include "gnome2-store/gkm-gnome2-storage.h"
+
+#include "gkm/gkm-certificate.h"
+#include "gkm/gkm-module.h"
+#include "gkm/gkm-serializable.h"
+#include "gkm/gkm-test.h"
+
+#include "egg/egg-libgcrypt.h"
+#include "egg/egg-mkdtemp.h"
+
+#include <glib/gstdio.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+typedef struct {
+	gchar *directory;
+	GkmModule *module;
+	GkmGnome2Storage *storage;
+	GkmObject *new_object;
+	gchar *new_filename;
+	GkmObject *old_object;
+} Test;
+
+static void
+copy_scratch_file (Test *test,
+                   const gchar *name)
+{
+	GError *error = NULL;
+	gchar *filename;
+	gchar *contents;
+	gsize length;
+
+	g_assert (test->directory);
+
+	filename = g_build_filename (SRCDIR, "files", name, NULL);
+	g_file_get_contents (filename, &contents, &length, &error);
+	g_assert_no_error (error);
+	g_free (filename);
+
+	filename = g_build_filename (test->directory, name, NULL);
+	g_file_set_contents (filename, contents, length, &error);
+	g_assert_no_error (error);
+	g_free (filename);
+}
+
+static void
+setup_directory (Test *test,
+                 gconstpointer unused)
+{
+	test->directory = g_strdup ("/tmp/gkd-test.XXXXXX");
+	if (!egg_mkdtemp (test->directory))
+		g_assert_not_reached ();
+
+	/* Copy in a valid set of storage data */
+	copy_scratch_file (test, "Thawte_Personal_Premium_CA.cer");
+	copy_scratch_file (test, "user.keystore");
+}
+
+static void
+setup_module (Test *test,
+              gconstpointer unused)
+{
+	CK_ATTRIBUTE label = { CKA_LABEL, NULL, 0 };
+	CK_ATTRIBUTE url = { CKA_URL, NULL, 0 };
+	gchar *contents;
+	gsize length;
+	GkmManager *manager;
+	GError *error = NULL;
+	GkmSession *session;
+	CK_ATTRIBUTE attr;
+	CK_OBJECT_CLASS klass;
+	CK_RV rv;
+
+	g_assert (test->directory != NULL);
+
+	test->module = mock_gnome2_module_initialize_and_enter ();
+	manager = gkm_module_get_manager (test->module);
+	session = mock_gnome2_module_open_session (TRUE);
+
+	test->storage = gkm_gnome2_storage_new (test->module, test->directory);
+	rv = gkm_gnome2_storage_refresh (test->storage);
+	gkm_assert_cmprv (rv, ==, CKR_OK);
+
+	gkm_store_register_schema (GKM_STORE (test->storage), &url, NULL, 0);
+	gkm_store_register_schema (GKM_STORE (test->storage), &label, NULL, 0);
+
+	/*
+	 * Create a new object that hasn't yet been stored in the storage.
+	 * It's a certificate because that's easiest.
+	 */
+	test->new_object = g_object_new (GKM_TYPE_CERTIFICATE,
+	                                 "unique", "test.cer",
+	                                 "module", test->module,
+	                                 "manager", manager,
+	                                 NULL);
+	g_file_get_contents (SRCDIR "/files/test-certificate.cer", &contents, &length, &error);
+	g_assert_no_error (error);
+	if (!gkm_serializable_load (GKM_SERIALIZABLE (test->new_object), NULL, contents, length))
+		g_assert_not_reached ();
+	g_free (contents);
+	/* We happen to know this certificate will get named */
+	test->new_filename = g_build_filename (test->directory, "CA_Cert_Signing_Authority.cer", NULL);
+
+	/*
+	 * Find the object stored in the storage, it's a certificate, and we happen to
+	 * know there's only one
+	 */
+	klass = CKO_CERTIFICATE;
+	attr.type = CKA_CLASS;
+	attr.pValue = &klass;
+	attr.ulValueLen = sizeof (klass);
+	test->old_object = gkm_manager_find_one_by_attributes (manager, session, &attr, 1);
+	g_assert (GKM_IS_OBJECT (test->old_object));
+	g_object_ref (test->old_object);
+}
+
+static void
+setup_all (Test *test,
+           gconstpointer unused)
+{
+	setup_directory (test, unused);
+	setup_module (test, unused);
+}
+
+static void
+teardown_directory (Test *test,
+                    gconstpointer unused)
+{
+	GDir *dir;
+	GError *error = NULL;
+	const gchar *name;
+	gchar *filename;
+
+	dir = g_dir_open (test->directory, 0, &error);
+	g_assert_no_error (error);
+
+	while ((name = g_dir_read_name (dir)) != NULL) {
+		filename = g_build_filename (test->directory, name, NULL);
+		if (g_unlink (filename) < 0)
+			g_assert_not_reached ();
+	}
+
+	g_dir_close (dir);
+
+	if (g_rmdir (test->directory) < 0)
+		g_assert_not_reached ();
+
+	g_free (test->directory);
+}
+
+static void
+teardown_module (Test *test,
+                 gconstpointer unused)
+{
+	g_assert (test->directory);
+
+	g_object_unref (test->new_object);
+	g_object_unref (test->old_object);
+	g_object_unref (test->storage);
+	g_assert (!G_IS_OBJECT (test->storage));
+
+	mock_gnome2_module_leave_and_finalize ();
+
+	g_free (test->new_filename);
+}
+
+static void
+teardown_all (Test *test,
+              gconstpointer unused)
+{
+	teardown_module (test, unused);
+	teardown_directory (test, unused);
+}
+
+static void
+test_create (Test *test,
+             gconstpointer unused)
+{
+	GkmTransaction *transaction;
+
+	transaction = gkm_transaction_new ();
+
+	gkm_gnome2_storage_create (test->storage, transaction, test->new_object);
+	gkm_assert_cmprv (gkm_transaction_get_result (transaction), ==, CKR_OK);
+
+	gkm_transaction_complete_and_unref (transaction);
+
+	g_assert (g_file_test (test->new_filename, G_FILE_TEST_EXISTS));
+}
+
+static void
+test_create_and_fail (Test *test,
+                      gconstpointer unused)
+{
+	GkmTransaction *transaction;
+
+	transaction = gkm_transaction_new ();
+
+	gkm_gnome2_storage_create (test->storage, transaction, test->new_object);
+	gkm_assert_cmprv (gkm_transaction_get_result (transaction), ==, CKR_OK);
+
+	gkm_transaction_fail (transaction, CKR_FUNCTION_FAILED);
+	gkm_transaction_complete_and_unref (transaction);
+
+	g_assert (!g_file_test (test->new_filename, G_FILE_TEST_EXISTS));
+}
+
+static void
+test_write_value (Test *test,
+                  gconstpointer unused)
+{
+	CK_ATTRIBUTE label = { CKA_LABEL, "Hello", 5 };
+	CK_ATTRIBUTE url = { CKA_URL, "http://example.com";, 18 };
+	GkmTransaction *transaction;
+	gchar *string;
+
+	transaction = gkm_transaction_new ();
+
+	gkm_store_write_value (GKM_STORE (test->storage), transaction,
+	                       test->old_object, &label);
+	gkm_assert_cmprv (gkm_transaction_get_result (transaction), ==, CKR_OK);
+
+	gkm_store_write_value (GKM_STORE (test->storage), transaction,
+	                       test->old_object, &url);
+	gkm_assert_cmprv (gkm_transaction_get_result (transaction), ==, CKR_OK);
+
+	gkm_transaction_complete_and_unref (transaction);
+
+	string = gkm_store_read_string (GKM_STORE (test->storage), test->old_object, CKA_URL);
+	g_assert_cmpstr (string, ==, "http://example.com";);
+	g_free (string);
+
+	string = gkm_store_read_string (GKM_STORE (test->storage), test->old_object, CKA_LABEL);
+	g_assert_cmpstr (string, ==, "Hello");
+	g_free (string);
+
+}
+
+static void
+test_lock_contention (Test *test,
+                      gconstpointer unused)
+{
+	pid_t pid;
+
+	/* Fork before setting up the model, as it may start threads */
+	pid = fork ();
+	g_assert (pid >= 0);
+
+	/*
+	 * This is the child. It initializes, writes a value, waits 100 ms,
+	 * writes a second value, and then writes another value.
+	 */
+	if (pid == 0) {
+		CK_ATTRIBUTE label = { CKA_LABEL, "Hello", 5 };
+		CK_ATTRIBUTE url = { CKA_URL, "http://example.com";, 18 };
+		GkmTransaction *transaction;
+
+		setup_module (test, unused);
+
+		transaction = gkm_transaction_new ();
+
+		gkm_store_write_value (GKM_STORE (test->storage), transaction,
+		                       test->old_object, &label);
+		gkm_assert_cmprv (gkm_transaction_get_result (transaction), ==, CKR_OK);
+
+		g_usleep (300 * 1000);
+
+		gkm_store_write_value (GKM_STORE (test->storage), transaction,
+		                       test->old_object, &url);
+		gkm_assert_cmprv (gkm_transaction_get_result (transaction), ==, CKR_OK);
+
+		gkm_transaction_complete_and_unref (transaction);
+
+		teardown_module (test, unused);
+		_exit (0);
+		g_assert_not_reached ();
+
+	/*
+	 * This is the parent. it initializes, waits 100 ms, writes a value that
+	 * should override the one from the child, because the file is locked
+	 * when it tries to write, so it waits for the child to finish. The other
+	 * attribute from the child (the label) should come through.
+	 */
+	} else {
+		CK_ATTRIBUTE url = { CKA_URL, "http://parent.example.com";, 25 };
+		GkmTransaction *transaction;
+		gchar *string;
+		pid_t wpid;
+		int status;
+
+		g_assert (pid != -1);
+
+		g_usleep (100 * 1000);
+
+		setup_module (test, unused);
+
+		transaction = gkm_transaction_new ();
+
+		gkm_store_write_value (GKM_STORE (test->storage), transaction,
+		                       test->old_object, &url);
+		gkm_assert_cmprv (gkm_transaction_get_result (transaction), ==, CKR_OK);
+
+		gkm_transaction_complete_and_unref (transaction);
+
+		/* wait for the child to finish */
+		wpid = waitpid (pid, &status, 0);
+		g_assert_cmpint (wpid, ==, pid);
+		g_assert_cmpint (status, ==, 0);
+
+		string = gkm_store_read_string (GKM_STORE (test->storage), test->old_object, CKA_URL);
+		g_assert_cmpstr (string, ==, "http://parent.example.com";);
+		g_free (string);
+
+		string = gkm_store_read_string (GKM_STORE (test->storage), test->old_object, CKA_LABEL);
+		g_assert_cmpstr (string, ==, "Hello");
+		g_free (string);
+
+		teardown_module (test, unused);
+	}
+}
+
+int
+main (int argc, char **argv)
+{
+	g_type_init ();
+	g_test_init (&argc, &argv, NULL);
+
+	egg_libgcrypt_initialize ();
+
+	g_test_add ("/gnome2-store/storage/create", Test, NULL,
+	            setup_all, test_create, teardown_all);
+	g_test_add ("/gnome2-store/storage/create_and_fail", Test, NULL,
+	            setup_all, test_create_and_fail, teardown_all);
+	g_test_add ("/gnome2-store/storage/write_value", Test, NULL,
+	            setup_all, test_write_value, teardown_all);
+
+	if (g_test_thorough ())
+		g_test_add ("/gnome2-store/storage/lock_contention", Test, NULL,
+		            setup_directory, test_lock_contention, teardown_directory);
+
+	return g_test_run ();
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]