[gnome-keyring/trust-store] [gkm] Add function for creating unique filename in transaction.



commit be343963893ffd84aff2d2a833c57a66de57542c
Author: Stef Walter <stef memberwebs com>
Date:   Sun Sep 19 02:17:01 2010 +0000

    [gkm] Add function for creating unique filename in transaction.
    
    Add gkm_transaction_unique_file() function.

 pkcs11/gkm/gkm-transaction.c             |  104 +++++++++++++++++++++++++---
 pkcs11/gkm/gkm-transaction.h             |    6 ++-
 pkcs11/gkm/tests/unit-test-transaction.c |  110 ++++++++++++++++++++++++++++--
 3 files changed, 202 insertions(+), 18 deletions(-)
---
diff --git a/pkcs11/gkm/gkm-transaction.c b/pkcs11/gkm/gkm-transaction.c
index ef45a98..ddadefc 100644
--- a/pkcs11/gkm/gkm-transaction.c
+++ b/pkcs11/gkm/gkm-transaction.c
@@ -61,6 +61,8 @@ typedef struct _Complete {
 
 G_DEFINE_TYPE (GkmTransaction, gkm_transaction, G_TYPE_OBJECT);
 
+#define MAX_TRIES 100000
+
 /* -----------------------------------------------------------------------------
  * INTERNAL
  */
@@ -167,15 +169,20 @@ complete_link_temporary (GkmTransaction *self, GObject *unused, gpointer user_da
 }
 
 static gboolean
-begin_link_temporary (GkmTransaction *self, const gchar *filename)
+begin_link_temporary_if_exists (GkmTransaction *self, const gchar *filename, gboolean *exists)
 {
 	gchar *result;
+	guint i = 0;
 
 	g_assert (GKM_IS_TRANSACTION (self));
 	g_assert (!gkm_transaction_get_failed (self));
 	g_assert (filename);
+	g_assert (exists);
+
+	for (i = 0; i < MAX_TRIES; ++i) {
+
+		*exists = TRUE;
 
-	for (;;) {
 		/* Try to link to random temporary file names */
 		result = g_strdup_printf ("%s.temp-%d", filename, g_random_int_range (0, G_MAXINT));
 		if (link (filename, result) == 0) {
@@ -185,6 +192,13 @@ begin_link_temporary (GkmTransaction *self, const gchar *filename)
 
 		g_free (result);
 
+		/* The original file does not exist */
+		if (errno == ENOENT || errno == ENOTDIR) {
+			*exists = FALSE;
+			return TRUE;
+		}
+
+		/* If exists, try again, otherwise fail */
 		if (errno != EEXIST) {
 			g_warning ("couldn't create temporary file for: %s: %s", filename, g_strerror (errno));
 			gkm_transaction_fail (self, CKR_DEVICE_ERROR);
@@ -451,20 +465,21 @@ gkm_transaction_get_result (GkmTransaction *self)
 
 void
 gkm_transaction_write_file (GkmTransaction *self, const gchar *filename,
-                            const guchar *data, gsize n_data)
+                            gconstpointer data, gsize n_data)
 {
+	gboolean exists;
+
 	g_return_if_fail (GKM_IS_TRANSACTION (self));
 	g_return_if_fail (filename);
 	g_return_if_fail (data);
 	g_return_if_fail (!gkm_transaction_get_failed (self));
 
-	/* Prepare file to be reverted */
-	if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+	if (!begin_link_temporary_if_exists (self, filename, &exists))
+		return;
+
+	if (!exists) {
 		if (!begin_new_file (self, filename))
 			return;
-	} else {
-		if (!begin_link_temporary (self, filename))
-			return;
 	}
 
 	/* Put data in the expected place */
@@ -474,18 +489,85 @@ gkm_transaction_write_file (GkmTransaction *self, const gchar *filename,
 	}
 }
 
+gchar*
+gkm_transaction_unique_file (GkmTransaction *self, const gchar *directory,
+                             const gchar *basename)
+{
+	gchar *ext;
+	gchar *filename = NULL;
+	gchar *base = NULL;
+	gchar *result = NULL;
+	gint seed = 1;
+	int fd;
+
+	g_return_val_if_fail (GKM_IS_TRANSACTION (self), NULL);
+	g_return_val_if_fail (directory, NULL);
+	g_return_val_if_fail (basename, NULL);
+	g_return_val_if_fail (!gkm_transaction_get_failed (self), NULL);
+
+	filename = g_build_filename (directory, basename, NULL);
+
+	/* Write a zero byte file */
+	fd = g_open (filename, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+	if (fd != -1) {
+		result = g_strdup (basename);
+
+	/* Try to find a unique filename */
+	} else if (errno == EEXIST) {
+		base = g_strdup (basename);
+		ext = strrchr (base, '.');
+		if (ext != NULL)
+			*(ext++) = '\0';
+
+		do {
+			g_free (result);
+			result = g_strdup_printf ("%s_%d%s%s", base, seed++,
+			                          ext ? "." : "", ext ? ext : "");
+
+			g_free (filename);
+			filename = g_build_filename (directory, result, NULL);
+			fd = g_open (filename, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+
+		} while (seed < MAX_TRIES && fd == -1 && errno == EEXIST);
+	}
+
+	/* Something failed */
+	if (fd == -1){
+		g_warning ("couldn't open file: %s: %s", filename, g_strerror (errno));
+		gkm_transaction_fail (self, CKR_DEVICE_ERROR);
+
+	/* Success, just leave our zero byte file */
+	} else {
+		gkm_transaction_add (self, NULL, complete_new_file, filename);
+		filename = NULL;
+		close (fd);
+	}
+
+	g_free (filename);
+	g_free (base);
+
+	if (gkm_transaction_get_failed (self)) {
+		g_free (result);
+		result = NULL;
+	}
+
+	return result;
+}
+
 void
 gkm_transaction_remove_file (GkmTransaction *self, const gchar *filename)
 {
+	gboolean exists;
+
 	g_return_if_fail (GKM_IS_TRANSACTION (self));
 	g_return_if_fail (filename);
 	g_return_if_fail (!gkm_transaction_get_failed (self));
 
-	/* Already gone? Job accomplished */
-	if (!g_file_test (filename, G_FILE_TEST_EXISTS))
+	if (!begin_link_temporary_if_exists (self, filename, &exists))
 		return;
 
-	if (!begin_link_temporary (self, filename))
+	/* Already gone? Job accomplished */
+	if (!exists)
 		return;
 
 	/* If failure, temporary will automatically be removed */
diff --git a/pkcs11/gkm/gkm-transaction.h b/pkcs11/gkm/gkm-transaction.h
index 9a39bd1..edbdf24 100644
--- a/pkcs11/gkm/gkm-transaction.h
+++ b/pkcs11/gkm/gkm-transaction.h
@@ -69,9 +69,13 @@ CK_RV                       gkm_transaction_get_result             (GkmTransacti
 
 gboolean                    gkm_transaction_get_completed          (GkmTransaction *self);
 
+gchar*                      gkm_transaction_unique_file            (GkmTransaction *self,
+                                                                    const gchar *directory,
+                                                                    const gchar *basename);
+
 void                        gkm_transaction_write_file             (GkmTransaction *self,
                                                                     const gchar *filename,
-                                                                    const guchar *data,
+                                                                    gconstpointer data,
                                                                     gsize n_data);
 
 void                        gkm_transaction_remove_file            (GkmTransaction *self,
diff --git a/pkcs11/gkm/tests/unit-test-transaction.c b/pkcs11/gkm/tests/unit-test-transaction.c
index cc69535..7fc03d2 100644
--- a/pkcs11/gkm/tests/unit-test-transaction.c
+++ b/pkcs11/gkm/tests/unit-test-transaction.c
@@ -25,6 +25,31 @@
 
 #include "gkm/gkm-transaction.h"
 
+DEFINE_SETUP (transaction_setup)
+{
+	GDir *dir;
+	const gchar *directory;
+	const gchar *basename;
+	gchar *filename;
+
+	directory = testing_scratch_directory ();
+	dir = g_dir_open (directory, 0, NULL);
+	g_assert (dir);
+
+	for (;;) {
+		basename = g_dir_read_name (dir);
+		if (basename == NULL)
+			break;
+		if (g_str_has_prefix (basename, "transaction-")) {
+			filename = g_build_filename (directory, basename, NULL);
+			g_unlink (filename);
+			g_free (filename);
+		}
+	}
+
+	g_dir_close (dir);
+}
+
 DEFINE_TEST(transaction_empty)
 {
 	GkmTransaction *transaction;
@@ -190,7 +215,7 @@ DEFINE_TEST(transaction_dispose_completes)
 DEFINE_TEST(remove_file_success)
 {
 	GkmTransaction *transaction = gkm_transaction_new ();
-	gchar *filename = testing_scratch_filename ("remove-file");
+	gchar *filename = testing_scratch_filename ("transaction-remove");
 
 	g_assert (g_file_set_contents (filename, "xxx", 3, NULL));
 	g_assert (g_file_test (filename, G_FILE_TEST_IS_REGULAR));
@@ -210,7 +235,7 @@ DEFINE_TEST(remove_file_success)
 DEFINE_TEST(remove_file_abort)
 {
 	GkmTransaction *transaction = gkm_transaction_new ();
-	gchar *filename = testing_scratch_filename ("remove-file");
+	gchar *filename = testing_scratch_filename ("transaction-remove");
 	gchar *data;
 	gsize n_data;
 
@@ -242,7 +267,7 @@ DEFINE_TEST(remove_file_abort)
 DEFINE_TEST(remove_file_non_exist)
 {
 	GkmTransaction *transaction = gkm_transaction_new ();
-	gchar *filename = testing_scratch_filename ("remove-non-existant");
+	gchar *filename = testing_scratch_filename ("transaction-non-existant");
 
 	g_unlink (filename);
 
@@ -258,7 +283,7 @@ DEFINE_TEST(remove_file_non_exist)
 DEFINE_TEST(write_file)
 {
 	GkmTransaction *transaction = gkm_transaction_new ();
-	gchar *filename = testing_scratch_filename ("write-test");
+	gchar *filename = testing_scratch_filename ("transaction-test");
 	gchar *data;
 	gsize n_data;
 
@@ -284,7 +309,7 @@ DEFINE_TEST(write_file)
 DEFINE_TEST(write_file_abort_gone)
 {
 	GkmTransaction *transaction = gkm_transaction_new ();
-	gchar *filename = testing_scratch_filename ("write-test");
+	gchar *filename = testing_scratch_filename ("transaction-test");
 	gchar *data;
 	gsize n_data;
 
@@ -310,7 +335,7 @@ DEFINE_TEST(write_file_abort_gone)
 DEFINE_TEST(write_file_abort_revert)
 {
 	GkmTransaction *transaction = gkm_transaction_new ();
-	gchar *filename = testing_scratch_filename ("write-test");
+	gchar *filename = testing_scratch_filename ("transaction-test");
 	gchar *data;
 
 	g_assert (g_file_set_contents (filename, "my original", -1, NULL));
@@ -332,3 +357,76 @@ DEFINE_TEST(write_file_abort_revert)
 	g_object_unref (transaction);
 	g_free (filename);
 }
+
+DEFINE_TEST (unique_file_conflict)
+{
+	GkmTransaction *transaction = gkm_transaction_new ();
+	gchar *filename = testing_scratch_filename ("transaction-test");
+	gchar *dirname;
+	gchar *basename;
+	gchar *result;
+
+	dirname = g_path_get_dirname (filename);
+	basename = g_path_get_basename (filename);
+
+	g_assert (g_file_set_contents (filename, "data", -1, NULL));
+
+	result = gkm_transaction_unique_file (transaction, dirname, basename);
+	g_assert (!gkm_transaction_get_failed (transaction));
+
+	g_assert (result);
+	g_assert_cmpstr (result, !=, basename);
+	g_assert_cmpstr (result, ==, "transaction-test_1");
+
+	g_free (dirname);
+	g_free (basename);
+	g_free (result);
+
+	g_object_unref (transaction);
+	g_free (filename);
+}
+
+DEFINE_TEST (unique_file_conflict_with_ext)
+{
+	GkmTransaction *transaction = gkm_transaction_new ();
+	gchar *filename = testing_scratch_filename ("transaction-test.ext");
+	gchar *dirname;
+	gchar *basename;
+	gchar *result;
+
+	dirname = g_path_get_dirname (filename);
+	basename = g_path_get_basename (filename);
+
+	g_assert (g_file_set_contents (filename, "data", -1, NULL));
+
+	result = gkm_transaction_unique_file (transaction, dirname, basename);
+	g_assert (!gkm_transaction_get_failed (transaction));
+
+	g_assert (result);
+	g_assert_cmpstr (result, !=, basename);
+	g_assert_cmpstr (result, ==, "transaction-test_1.ext");
+
+	g_free (dirname);
+	g_free (basename);
+	g_free (result);
+
+	g_object_unref (transaction);
+	g_free (filename);
+}
+
+DEFINE_TEST (unique_file_no_conflict)
+{
+	GkmTransaction *transaction = gkm_transaction_new ();
+	const gchar *dirname = testing_scratch_directory ();
+	gchar *result;
+
+	result = gkm_transaction_unique_file (transaction, dirname, "transaction-another");
+	g_assert (!gkm_transaction_get_failed (transaction));
+
+	g_assert (result);
+	g_assert_cmpstr (result, ==, "transaction-another");
+
+	g_free (result);
+
+	g_object_unref (transaction);
+}



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