[libsecret/62-flatpak-libsecret-fails-to-read-credentials-saved-by-other-process] secret-file-collection: Check for file changes and reload when needed



commit 7268a4111e8242c0d2fc8e5ad8b33190a468ca30
Author: Milan Crha <mcrha redhat com>
Date:   Tue Nov 30 11:48:37 2021 +0100

    secret-file-collection: Check for file changes and reload when needed
    
    When some other process changes the underlying file, the collection
    should reload its in-memory content, to reflect the changes.
    
    Closes https://gitlab.gnome.org/GNOME/libsecret/-/issues/62

 libsecret/secret-file-collection.c | 228 +++++++++++++++++++++++++------------
 1 file changed, 155 insertions(+), 73 deletions(-)
---
diff --git a/libsecret/secret-file-collection.c b/libsecret/secret-file-collection.c
index 62847b9..1819e83 100644
--- a/libsecret/secret-file-collection.c
+++ b/libsecret/secret-file-collection.c
@@ -54,6 +54,7 @@ struct _SecretFileCollection
        guint64 usage_count;
        GBytes *key;
        GVariant *items;
+       guint64 file_last_modified;
 };
 
 static void secret_file_collection_async_initable_iface (GAsyncInitableIface *iface);
@@ -68,6 +69,22 @@ enum {
        PROP_PASSWORD
 };
 
+static guint64
+get_file_last_modified (SecretFileCollection *self)
+{
+       GFileInfo *info;
+       guint64 res;
+
+       info = g_file_query_info (self->file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, 
NULL);
+       if (info == NULL)
+               return 0;
+
+       res = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+       g_object_unref (info);
+
+       return res;
+}
+
 static gboolean
 do_derive_key (SecretFileCollection *self)
 {
@@ -296,17 +313,13 @@ secret_file_collection_class_init (SecretFileCollectionClass *klass)
        egg_libgcrypt_initialize ();
 }
 
-static void
-on_load_contents (GObject *source_object,
-                 GAsyncResult *result,
-                 gpointer user_data)
+static gboolean
+load_contents (SecretFileCollection *self,
+              gchar *contents, /* takes ownership */
+              gsize length,
+              GError **error)
 {
-       GFile *file = G_FILE (source_object);
-       GTask *task = G_TASK (user_data);
-       SecretFileCollection *self = g_task_get_source_object (task);
-       gchar *contents;
        gchar *p;
-       gsize length;
        GVariant *variant;
        GVariant *salt_array;
        guint32 salt_size;
@@ -315,70 +328,25 @@ on_load_contents (GObject *source_object,
        guint64 usage_count;
        gconstpointer data;
        gsize n_data;
-       GError *error = NULL;
-       gboolean ret;
-
-       ret = g_file_load_contents_finish (file, result,
-                                          &contents, &length,
-                                          &self->etag,
-                                          &error);
-
-       if (!ret) {
-               if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
-                       GVariantBuilder builder;
-                       guint8 salt[SALT_SIZE];
-
-                       g_clear_error (&error);
-
-                       gcry_create_nonce (salt, sizeof(salt));
-                       self->salt = g_bytes_new (salt, sizeof(salt));
-                       self->iteration_count = ITERATION_COUNT;
-                       self->modified = g_date_time_new_now_utc ();
-                       self->usage_count = 0;
-
-                       if (!do_derive_key (self)) {
-                               g_task_return_new_error (task,
-                                                        SECRET_ERROR,
-                                                        SECRET_ERROR_PROTOCOL,
-                                                        "couldn't derive key");
-                               g_object_unref (task);
-                               return;
-                       }
-
-                       g_variant_builder_init (&builder,
-                                               G_VARIANT_TYPE ("a(a{say}ay)"));
-                       self->items = g_variant_builder_end (&builder);
-                       g_variant_ref_sink (self->items);
-                       g_task_return_boolean (task, TRUE);
-                       g_object_unref (task);
-                       return;
-               }
-
-               g_task_return_error (task, error);
-               g_object_unref (task);
-               return;
-       }
 
        p = contents;
        if (length < KEYRING_FILE_HEADER_LEN ||
            memcmp (p, KEYRING_FILE_HEADER, KEYRING_FILE_HEADER_LEN) != 0) {
-               g_task_return_new_error (task,
-                                        SECRET_ERROR,
-                                        SECRET_ERROR_INVALID_FILE_FORMAT,
-                                        "file header mismatch");
-               g_object_unref (task);
-               return;
+               g_set_error_literal (error,
+                                    SECRET_ERROR,
+                                    SECRET_ERROR_INVALID_FILE_FORMAT,
+                                    "file header mismatch");
+               return FALSE;
        }
        p += KEYRING_FILE_HEADER_LEN;
        length -= KEYRING_FILE_HEADER_LEN;
 
        if (length < 2 || *p != MAJOR_VERSION || *(p + 1) != MINOR_VERSION) {
-               g_task_return_new_error (task,
-                                        SECRET_ERROR,
-                                        SECRET_ERROR_INVALID_FILE_FORMAT,
-                                        "version mismatch");
-               g_object_unref (task);
-               return;
+               g_set_error_literal (error,
+                                    SECRET_ERROR,
+                                    SECRET_ERROR_INVALID_FILE_FORMAT,
+                                    "version mismatch");
+               return FALSE;
        }
        p += 2;
        length -= 2;
@@ -407,19 +375,125 @@ on_load_contents (GObject *source_object,
        g_assert (n_data == salt_size);
 
        self->salt = g_bytes_new (data, n_data);
+
+       g_variant_unref (salt_array);
+       g_variant_unref (variant);
+
        if (!do_derive_key (self)) {
-               g_task_return_new_error (task,
-                                        SECRET_ERROR,
-                                        SECRET_ERROR_PROTOCOL,
-                                        "couldn't derive key");
-               goto out;
+               g_set_error_literal (error,
+                                    SECRET_ERROR,
+                                    SECRET_ERROR_PROTOCOL,
+                                    "couldn't derive key");
+               return FALSE;
        }
 
-       g_task_return_boolean (task, TRUE);
+       return TRUE;
+}
+
+static gboolean
+init_empty_file (SecretFileCollection *self,
+                GError **error)
+{
+       GVariantBuilder builder;
+       guint8 salt[SALT_SIZE];
+
+       gcry_create_nonce (salt, sizeof(salt));
+       self->salt = g_bytes_new (salt, sizeof(salt));
+       self->iteration_count = ITERATION_COUNT;
+       self->modified = g_date_time_new_now_utc ();
+       self->usage_count = 0;
+
+       if (!do_derive_key (self)) {
+               g_set_error_literal (error,
+                                    SECRET_ERROR,
+                                    SECRET_ERROR_PROTOCOL,
+                                    "couldn't derive key");
+               return FALSE;
+       }
+
+       g_variant_builder_init (&builder,
+                               G_VARIANT_TYPE ("a(a{say}ay)"));
+       self->items = g_variant_builder_end (&builder);
+       g_variant_ref_sink (self->items);
+
+       return TRUE;
+}
+
+static void
+ensure_up_to_date (SecretFileCollection *self)
+{
+       guint64 last_modified;
+
+       last_modified = get_file_last_modified (self);
+       if (last_modified != self->file_last_modified) {
+               gchar *contents = NULL;
+               gsize length = 0;
+               gboolean success;
+               GError *error = NULL;
+
+               self->file_last_modified = last_modified;
+               g_clear_pointer (&self->etag, g_free);
+
+               success = g_file_load_contents (self->file, NULL, &contents, &length, &self->etag, &error);
+
+               if (!success && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
+                       g_clear_error (&error);
+
+                       success = init_empty_file (self, &error);
+               }
+
+               if (success)
+                       success = load_contents (self, contents, length, &error);
+
+               if (!success)
+                       g_debug ("Failed to load file contents: %s", error ? error->message : "Unknown 
error");
+
+               g_clear_error (&error);
+       }
+}
+
+static void
+on_load_contents (GObject *source_object,
+                 GAsyncResult *result,
+                 gpointer user_data)
+{
+       GFile *file = G_FILE (source_object);
+       GTask *task = G_TASK (user_data);
+       SecretFileCollection *self = g_task_get_source_object (task);
+       gchar *contents;
+       gsize length;
+       GError *error = NULL;
+       gboolean ret;
+
+       self->file_last_modified = get_file_last_modified (self);
+
+       ret = g_file_load_contents_finish (file, result,
+                                          &contents, &length,
+                                          &self->etag,
+                                          &error);
+
+       if (!ret) {
+               if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
+                       g_clear_error (&error);
+
+                       if (init_empty_file (self, &error)) {
+                               g_task_return_boolean (task, TRUE);
+                               g_object_unref (task);
+                               return;
+                       }
+               }
+
+               g_task_return_error (task, error);
+               g_object_unref (task);
+               return;
+       }
+
+       ret = load_contents (self, contents, length, &error);
+       if (ret)
+               g_task_return_boolean (task, ret);
+       else
+               g_task_return_error (task, error);
 
- out:
-       g_variant_unref (salt_array);
-       g_variant_unref (variant);
        g_object_unref (task);
 }
 
@@ -546,6 +620,8 @@ secret_file_collection_replace (SecretFileCollection *self,
        GDateTime *created = NULL;
        GDateTime *modified;
 
+       ensure_up_to_date (self);
+
        hashed_attributes = hash_attributes (self, attributes);
        if (!hashed_attributes) {
                g_set_error (error,
@@ -657,6 +733,8 @@ secret_file_collection_search (SecretFileCollection *self,
        GVariant *child;
        GList *result = NULL;
 
+       ensure_up_to_date (self);
+
        g_variant_iter_init (&iter, self->items);
        while ((child = g_variant_iter_next_value (&iter)) != NULL) {
                GVariant *hashed_attributes;
@@ -750,6 +828,8 @@ secret_file_collection_clear (SecretFileCollection *self,
        GVariant *child;
        gboolean removed = FALSE;
 
+       ensure_up_to_date (self);
+
        g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(a{say}ay)"));
        g_variant_iter_init (&items, self->items);
        while ((child = g_variant_iter_next_value (&items)) != NULL) {
@@ -791,6 +871,8 @@ on_replace_contents (GObject *source_object,
                return;
        }
 
+       self->file_last_modified = get_file_last_modified (self);
+
        g_task_return_boolean (task, TRUE);
        g_object_unref (task);
 }


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