[gnome-keyring] gnome2-store: Fix locking and add tests for locking
- From: Stefan Walter <stefw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-keyring] gnome2-store: Fix locking and add tests for locking
- Date: Sat, 15 Oct 2011 20:52:37 +0000 (UTC)
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]