mirror of
https://gitlab.gnome.org/GNOME/libsecret.git
synced 2024-12-22 04:38:55 +00:00
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
This commit is contained in:
parent
ae7c52bd9c
commit
1f44c81ca4
@ -54,6 +54,7 @@ struct _SecretFileCollection
|
|||||||
guint64 usage_count;
|
guint64 usage_count;
|
||||||
GBytes *key;
|
GBytes *key;
|
||||||
GVariant *items;
|
GVariant *items;
|
||||||
|
guint64 file_last_modified;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void secret_file_collection_async_initable_iface (GAsyncInitableIface *iface);
|
static void secret_file_collection_async_initable_iface (GAsyncInitableIface *iface);
|
||||||
@ -68,6 +69,22 @@ enum {
|
|||||||
PROP_PASSWORD
|
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
|
static gboolean
|
||||||
do_derive_key (SecretFileCollection *self)
|
do_derive_key (SecretFileCollection *self)
|
||||||
{
|
{
|
||||||
@ -296,17 +313,13 @@ secret_file_collection_class_init (SecretFileCollectionClass *klass)
|
|||||||
egg_libgcrypt_initialize ();
|
egg_libgcrypt_initialize ();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static gboolean
|
||||||
on_load_contents (GObject *source_object,
|
load_contents (SecretFileCollection *self,
|
||||||
GAsyncResult *result,
|
gchar *contents, /* takes ownership */
|
||||||
gpointer user_data)
|
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;
|
gchar *p;
|
||||||
gsize length;
|
|
||||||
GVariant *variant;
|
GVariant *variant;
|
||||||
GVariant *salt_array;
|
GVariant *salt_array;
|
||||||
guint32 salt_size;
|
guint32 salt_size;
|
||||||
@ -315,70 +328,25 @@ on_load_contents (GObject *source_object,
|
|||||||
guint64 usage_count;
|
guint64 usage_count;
|
||||||
gconstpointer data;
|
gconstpointer data;
|
||||||
gsize n_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;
|
p = contents;
|
||||||
if (length < KEYRING_FILE_HEADER_LEN ||
|
if (length < KEYRING_FILE_HEADER_LEN ||
|
||||||
memcmp (p, KEYRING_FILE_HEADER, KEYRING_FILE_HEADER_LEN) != 0) {
|
memcmp (p, KEYRING_FILE_HEADER, KEYRING_FILE_HEADER_LEN) != 0) {
|
||||||
g_task_return_new_error (task,
|
g_set_error_literal (error,
|
||||||
SECRET_ERROR,
|
SECRET_ERROR,
|
||||||
SECRET_ERROR_INVALID_FILE_FORMAT,
|
SECRET_ERROR_INVALID_FILE_FORMAT,
|
||||||
"file header mismatch");
|
"file header mismatch");
|
||||||
g_object_unref (task);
|
return FALSE;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
p += KEYRING_FILE_HEADER_LEN;
|
p += KEYRING_FILE_HEADER_LEN;
|
||||||
length -= KEYRING_FILE_HEADER_LEN;
|
length -= KEYRING_FILE_HEADER_LEN;
|
||||||
|
|
||||||
if (length < 2 || *p != MAJOR_VERSION || *(p + 1) != MINOR_VERSION) {
|
if (length < 2 || *p != MAJOR_VERSION || *(p + 1) != MINOR_VERSION) {
|
||||||
g_task_return_new_error (task,
|
g_set_error_literal (error,
|
||||||
SECRET_ERROR,
|
SECRET_ERROR,
|
||||||
SECRET_ERROR_INVALID_FILE_FORMAT,
|
SECRET_ERROR_INVALID_FILE_FORMAT,
|
||||||
"version mismatch");
|
"version mismatch");
|
||||||
g_object_unref (task);
|
return FALSE;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
p += 2;
|
p += 2;
|
||||||
length -= 2;
|
length -= 2;
|
||||||
@ -407,19 +375,125 @@ on_load_contents (GObject *source_object,
|
|||||||
g_assert (n_data == salt_size);
|
g_assert (n_data == salt_size);
|
||||||
|
|
||||||
self->salt = g_bytes_new (data, n_data);
|
self->salt = g_bytes_new (data, n_data);
|
||||||
if (!do_derive_key (self)) {
|
|
||||||
g_task_return_new_error (task,
|
|
||||||
SECRET_ERROR,
|
|
||||||
SECRET_ERROR_PROTOCOL,
|
|
||||||
"couldn't derive key");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_task_return_boolean (task, TRUE);
|
|
||||||
|
|
||||||
out:
|
|
||||||
g_variant_unref (salt_array);
|
g_variant_unref (salt_array);
|
||||||
g_variant_unref (variant);
|
g_variant_unref (variant);
|
||||||
|
|
||||||
|
if (!do_derive_key (self)) {
|
||||||
|
g_set_error_literal (error,
|
||||||
|
SECRET_ERROR,
|
||||||
|
SECRET_ERROR_PROTOCOL,
|
||||||
|
"couldn't derive key");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
g_object_unref (task);
|
g_object_unref (task);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,6 +620,8 @@ secret_file_collection_replace (SecretFileCollection *self,
|
|||||||
GDateTime *created = NULL;
|
GDateTime *created = NULL;
|
||||||
GDateTime *modified;
|
GDateTime *modified;
|
||||||
|
|
||||||
|
ensure_up_to_date (self);
|
||||||
|
|
||||||
hashed_attributes = hash_attributes (self, attributes);
|
hashed_attributes = hash_attributes (self, attributes);
|
||||||
if (!hashed_attributes) {
|
if (!hashed_attributes) {
|
||||||
g_set_error (error,
|
g_set_error (error,
|
||||||
@ -657,6 +733,8 @@ secret_file_collection_search (SecretFileCollection *self,
|
|||||||
GVariant *child;
|
GVariant *child;
|
||||||
GList *result = NULL;
|
GList *result = NULL;
|
||||||
|
|
||||||
|
ensure_up_to_date (self);
|
||||||
|
|
||||||
g_variant_iter_init (&iter, self->items);
|
g_variant_iter_init (&iter, self->items);
|
||||||
while ((child = g_variant_iter_next_value (&iter)) != NULL) {
|
while ((child = g_variant_iter_next_value (&iter)) != NULL) {
|
||||||
GVariant *hashed_attributes;
|
GVariant *hashed_attributes;
|
||||||
@ -750,6 +828,8 @@ secret_file_collection_clear (SecretFileCollection *self,
|
|||||||
GVariant *child;
|
GVariant *child;
|
||||||
gboolean removed = FALSE;
|
gboolean removed = FALSE;
|
||||||
|
|
||||||
|
ensure_up_to_date (self);
|
||||||
|
|
||||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(a{say}ay)"));
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(a{say}ay)"));
|
||||||
g_variant_iter_init (&items, self->items);
|
g_variant_iter_init (&items, self->items);
|
||||||
while ((child = g_variant_iter_next_value (&items)) != NULL) {
|
while ((child = g_variant_iter_next_value (&items)) != NULL) {
|
||||||
@ -791,6 +871,8 @@ on_replace_contents (GObject *source_object,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self->file_last_modified = get_file_last_modified (self);
|
||||||
|
|
||||||
g_task_return_boolean (task, TRUE);
|
g_task_return_boolean (task, TRUE);
|
||||||
g_object_unref (task);
|
g_object_unref (task);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user