diff options
author | Ondrej Holy <oholy@redhat.com> | 2017-02-07 08:10:23 +0100 |
---|---|---|
committer | Ondrej Holy <oholy@redhat.com> | 2018-07-19 16:03:25 +0200 |
commit | 123795fcfbaca32aae64fc3f3f06060592cdcc86 (patch) | |
tree | ba3ae94324759eff3066e40f7f3bca227f5318be | |
parent | 0e89a6f5f249fb34440fae14c78dd479d56ab4c3 (diff) | |
download | gvfs-wip/oholy/google-cache.tar.gz |
google: Improve performance for Drives with many fileswip/oholy/google-cache
The backend is totally unusable if you have too many files on your
Drive. This happens because the backend preloads the whole Drive's
metadata. Various operations require rereading the whole cache,
because the proper invalidation is not possible currently. Let's
change the workflow in order to improve the performance. The summary
of changes done by this patch:
- Do not preload the whole Drive, but cache just requested folders.
Invalidate by time separately for each folder and not for the whole
cache.
- Remove functions get_parent_id and get_entry_path which are totally
wrong because one GDataEntry may have multiple parents.
- Invalidate cache entries for all parents, not just for concrete
folder, because operations affects the files on all places.
- Files without parent folder are not listed anymore, they are not
listed on the web anyway. They used to be shown as hidden in the root
folder.
As a result, the backend works smoothly regardless of the total number
of Drive files, because the total number of transmitted data is
significantly reduced. It is true, that slightly more requests to Drive
have to done, but I think that the Drive quotas are big enough.
https://bugzilla.gnome.org/show_bug.cgi?id=771390
-rw-r--r-- | daemon/gvfsbackendgoogle.c | 1016 |
1 files changed, 424 insertions, 592 deletions
diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c index 7bea4bff..0714bdd8 100644 --- a/daemon/gvfsbackendgoogle.c +++ b/daemon/gvfsbackendgoogle.c @@ -55,15 +55,11 @@ struct _GVfsBackendGoogle GVfsBackend parent; GDataDocumentsService *service; GDataEntry *root; - GHashTable *entries; - GHashTable *dir_entries; + GHashTable *dir_entries; /* gchar *parent_id -> DirEntriesData */ GHashTable *monitors; - GList *dir_collisions; GRecMutex mutex; /* guards cache */ GoaClient *client; - gboolean entries_stale; gchar *account_identity; - guint entries_stale_timeout; }; struct _GVfsBackendGoogleClass @@ -90,77 +86,61 @@ G_DEFINE_TYPE(GVfsBackendGoogle, g_vfs_backend_google, G_VFS_TYPE_BACKEND) typedef struct { - gchar *title_or_id; - gchar *parent_id; -} DirEntriesKey; + GHashTable *entries; /* gchar *child_title_or_id -> GDataEntry */ + gint64 timestamp; +} DirEntriesData; typedef struct { GDataEntry *document; GDataUploadStream *stream; gchar *filename; + gchar *entry_path; } WriteHandle; static GDataEntry *resolve_dir (GVfsBackendGoogle *self, const gchar *filename, gchar **out_basename, + gchar **out_path, + GCancellable *cancellable, GError **error); /* ---------------------------------------------------------------------------------------------------- */ -static DirEntriesKey * -dir_entries_key_new (const gchar *title_or_id, const gchar *parent_id) +static DirEntriesData * +dir_entries_data_new (void) { - DirEntriesKey *k; + DirEntriesData *d; - k = g_slice_new0 (DirEntriesKey); - k->title_or_id = g_strdup (title_or_id); - k->parent_id = g_strdup (parent_id); - return k; -} - -static void -dir_entries_key_free (gpointer data) -{ - DirEntriesKey *k = (DirEntriesKey *) data; + d = g_slice_new0 (DirEntriesData); + d->entries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + d->timestamp = g_get_real_time (); - if (k == NULL) - return; - - g_free (k->title_or_id); - g_free (k->parent_id); - g_slice_free (DirEntriesKey, k); + return d; } -static guint -entries_in_folder_hash (gconstpointer key) +static gboolean +dir_entries_data_is_valid (DirEntriesData *d) { - DirEntriesKey *k = (DirEntriesKey *) key; - guint hash1; - guint hash2; - - hash1 = g_str_hash (k->title_or_id); - hash2 = g_str_hash (k->parent_id); - return hash1 ^ hash2; + return (g_get_real_time () - d->timestamp < REBUILD_ENTRIES_TIMEOUT * G_USEC_PER_SEC); } -static gboolean -entries_in_folder_equal (gconstpointer a, gconstpointer b) +static void +dir_entries_data_free (gpointer data) { - DirEntriesKey *k_a = (DirEntriesKey *) a; - DirEntriesKey *k_b = (DirEntriesKey *) b; + DirEntriesData *d = (DirEntriesData *) data; - if (g_strcmp0 (k_a->title_or_id, k_b->title_or_id) == 0 && - g_strcmp0 (k_a->parent_id, k_b->parent_id) == 0) - return TRUE; + if (d == NULL) + return; - return FALSE; + g_hash_table_unref (d->entries); + g_slice_free (DirEntriesData, d); } /* ---------------------------------------------------------------------------------------------------- */ static WriteHandle * -write_handle_new (GDataEntry *document, GDataUploadStream *stream, const gchar *filename) +write_handle_new (GDataEntry *document, GDataUploadStream *stream, const gchar *filename, const gchar *entry_path) { WriteHandle *handle; @@ -177,6 +157,7 @@ write_handle_new (GDataEntry *document, GDataUploadStream *stream, const gchar * } handle->filename = g_strdup (filename); + handle->entry_path = g_strdup (entry_path); return handle; } @@ -192,6 +173,7 @@ write_handle_free (gpointer data) g_clear_object (&handle->document); g_clear_object (&handle->stream); g_free (handle->filename); + g_free (handle->entry_path); g_slice_free (WriteHandle, handle); } @@ -313,13 +295,11 @@ get_content_type_from_entry (GDataEntry *entry) /* ---------------------------------------------------------------------------------------------------- */ -static gchar * -get_parent_id (GVfsBackendGoogle *self, - GDataEntry *entry) +static GList * +get_parent_ids (GVfsBackendGoogle *self, + GDataEntry *entry) { - GList *l; - GList *links = NULL; - gchar *ret_val = NULL; + GList *l, *links, *parent_ids = NULL; links = gdata_entry_look_up_links (entry, GDATA_LINK_PARENT); for (l = links; l != NULL; l = l->next) @@ -341,212 +321,135 @@ get_parent_id (GVfsBackendGoogle *self, id = uri + uri_prefix_len; if (id[0] != '\0') { - ret_val = g_strdup (uri + uri_prefix_len); + parent_ids = g_list_prepend (parent_ids, g_strdup (id)); break; } } } - if (ret_val == NULL) + if (parent_ids == NULL) { const gchar *root_id; root_id = gdata_entry_get_id (self->root); - ret_val = g_strdup (root_id); + parent_ids = g_list_prepend (parent_ids, g_strdup (root_id)); } g_list_free (links); - return ret_val; -} - -static gchar * -get_entry_path (GVfsBackendGoogle *self, GDataEntry *entry) -{ - GString *path = NULL; - const gchar *base_id; - const gchar *root_id; - gchar *id = NULL; - gchar *ret_val = NULL; - - if (entry == self->root) - { - ret_val = g_strdup ("/"); - goto out; - } - - base_id = gdata_entry_get_id (entry); - path = g_string_new (base_id); - g_string_prepend_c (path, '/'); - - id = get_parent_id (self, entry); - root_id = gdata_entry_get_id (self->root); - while (id != NULL) - { - GDataEntry *parent_entry; - - /* The root folder itself has an ID, so path can become - * /root/folder1/folder2/file. Instead, we want it to be - * /folder1/folder2/file. - */ - - if (g_strcmp0 (id, root_id) == 0) - break; - - parent_entry = g_hash_table_lookup (self->entries, id); - if (parent_entry == NULL) - goto out; - - g_string_prepend (path, id); - g_string_prepend_c (path, '/'); - - g_free (id); - id = get_parent_id (self, parent_entry); - } - - ret_val = g_strdup (path->str); - - out: - g_free (id); - if (path != NULL) - g_string_free (path, TRUE); - return ret_val; + return parent_ids; } /* ---------------------------------------------------------------------------------------------------- */ -static gboolean -insert_entry_full (GVfsBackendGoogle *self, - GDataEntry *entry, - gboolean track_dir_collisions) +static void +insert_entry (GVfsBackendGoogle *self, + GDataEntry *parent, + GDataEntry *entry) { - DirEntriesKey *k; - GDataEntry *old_entry; - gboolean insert_title = TRUE; + DirEntriesData *d; + GDataEntry *existing_entry; const gchar *id; - const gchar *old_id; + const gchar *parent_id; const gchar *title; - gchar *parent_id; id = gdata_entry_get_id (entry); - title = gdata_entry_get_title (entry); - - g_hash_table_insert (self->entries, g_strdup (id), g_object_ref (entry)); + parent_id = gdata_entry_get_id (parent); - parent_id = get_parent_id (self, entry); + d = g_hash_table_lookup (self->dir_entries, parent_id); + g_return_if_fail (d != NULL); - k = dir_entries_key_new (id, parent_id); - g_hash_table_insert (self->dir_entries, k, g_object_ref (entry)); - g_debug (" insert_entry: Inserted (%s, %s) -> %p\n", id, parent_id, entry); + g_hash_table_insert (d->entries, g_strdup (id), g_object_ref (entry)); + g_debug ("insert_entry: Inserted %s in %s\n", id, parent_id); - k = dir_entries_key_new (title, parent_id); - old_entry = g_hash_table_lookup (self->dir_entries, k); - if (old_entry != NULL) + title = gdata_entry_get_title (entry); + existing_entry = g_hash_table_lookup (d->entries, title); + if (existing_entry == NULL) { - old_id = gdata_entry_get_id (old_entry); - if (g_strcmp0 (old_id, title) == 0) - { - insert_title = FALSE; - } - else - { - /* If the collision is not due to the title matching the ID - * of an earlier GDataEntry, then it is due to duplicate - * titles. - */ - if (g_strcmp0 (old_id, id) < 0) - insert_title = FALSE; - } + g_hash_table_insert (d->entries, g_strdup (title), g_object_ref (entry)); + g_debug ("insert_entry: Inserted '%s' in %s\n", title, parent_id); } - - if (insert_title) + /* The last updated entry is probably the one which needs to be there */ + else if (gdata_entry_get_updated (existing_entry) < gdata_entry_get_updated (entry)) { - if (old_entry != NULL && track_dir_collisions) - { - self->dir_collisions = g_list_prepend (self->dir_collisions, g_object_ref (old_entry)); - g_debug (" insert_entry: Ejected (%s, %s, %s) -> %p\n", old_id, title, parent_id, old_entry); - } - - g_hash_table_insert (self->dir_entries, k, g_object_ref (entry)); - g_debug (" insert_entry: Inserted (%s, %s) -> %p\n", title, parent_id, entry); + g_hash_table_insert (d->entries, g_strdup (title), g_object_ref (entry)); + g_debug ("insert_entry: Replaced '%s' in %s\n", title, parent_id); } else { - if (track_dir_collisions) - { - self->dir_collisions = g_list_prepend (self->dir_collisions, g_object_ref (entry)); - g_debug (" insert_entry: Skipped (%s, %s, %s) -> %p\n", id, title, parent_id, entry); - } - - dir_entries_key_free (k); + g_debug ("insert_entry: Skipped '%s' in %s\n", title, parent_id); } - - g_free (parent_id); - return insert_title; -} - -static void -insert_entry (GVfsBackendGoogle *self, - GDataEntry *entry) -{ - insert_entry_full (self, entry, TRUE); } static void remove_entry (GVfsBackendGoogle *self, + GDataEntry *parent, GDataEntry *entry) { - DirEntriesKey *k; - GList *l; - const gchar *id; - const gchar *title; - gchar *parent_id; - - id = gdata_entry_get_id (entry); - title = gdata_entry_get_title (entry); - - g_hash_table_remove (self->entries, id); - - parent_id = get_parent_id (self, entry); + if (parent != NULL) + { + DirEntriesData *d; + const gchar *parent_id, *id, *title; + GDataEntry *current_entry; - k = dir_entries_key_new (id, parent_id); - g_hash_table_remove (self->dir_entries, k); - g_debug (" remove_entry: Removed (%s, %s) -> %p\n", id, parent_id, entry); - dir_entries_key_free (k); + parent_id = gdata_entry_get_id (parent); + id = gdata_entry_get_id (entry); + title = gdata_entry_get_title (entry); - k = dir_entries_key_new (title, parent_id); - g_hash_table_remove (self->dir_entries, k); - g_debug (" remove_entry: Removed (%s, %s) -> %p\n", title, parent_id, entry); - dir_entries_key_free (k); + d = g_hash_table_lookup (self->dir_entries, parent_id); + if (d != NULL) + { + g_hash_table_remove (d->entries, id); + g_debug ("remove_entry: Removed %s from %s\n", id, parent_id); - for (l = self->dir_collisions; l != NULL; l = l->next) + current_entry = g_hash_table_lookup (d->entries, title); + if (g_strcmp0 (gdata_entry_get_id (current_entry), id) == 0) + { + g_hash_table_remove (d->entries, title); + g_debug ("remove_entry: Removed '%s' from %s\n", title, parent_id); + } + else + { + g_hash_table_remove (self->dir_entries, parent_id); + g_debug ("remove_entry: Removed all from %s\n", parent_id); + } + } + } + else { - GDataEntry *colliding_entry = GDATA_ENTRY (l->data); + GList *l, *parent_ids = NULL; - if (insert_entry_full (self, colliding_entry, FALSE)) + parent_ids = get_parent_ids (self, entry); + for (l = parent_ids; l != NULL; l = l->next) { - self->dir_collisions = g_list_remove_link (self->dir_collisions, l); - g_debug (" remove_entry: Restored %p\n", colliding_entry); - g_list_free (l); - g_object_unref (colliding_entry); - break; + const gchar *parent_id = (const gchar *) l->data; + + g_hash_table_remove (self->dir_entries, parent_id); + g_debug ("remove_entry: Removed all from %s\n", parent_id); } - } - g_free (parent_id); + g_list_free_full (parent_ids, g_free); + } } static void -rebuild_entries (GVfsBackendGoogle *self, - GCancellable *cancellable, - GError **error) +rebuild_dir (GVfsBackendGoogle *self, + GDataEntry *parent, + GCancellable *cancellable, + GError **error) { GDataDocumentsFeed *feed = NULL; GDataDocumentsQuery *query = NULL; gboolean succeeded_once = FALSE; + gchar *search; + const gchar *parent_id; - query = gdata_documents_query_new_with_limits (NULL, 1, MAX_RESULTS); + parent_id = gdata_entry_get_id (parent); + + search = g_strdup_printf ("'%s' in parents", parent_id); + query = gdata_documents_query_new_with_limits (search, 1, MAX_RESULTS); gdata_documents_query_set_show_folders (query, TRUE); + g_free (search); while (TRUE) { @@ -560,18 +463,13 @@ rebuild_entries (GVfsBackendGoogle *self, { sanitize_error (&local_error); g_propagate_error (error, local_error); - self->entries_stale = TRUE; - goto out; } if (!succeeded_once) { - g_hash_table_remove_all (self->entries); - g_hash_table_remove_all (self->dir_entries); - - g_list_free_full (self->dir_collisions, g_object_unref); - self->dir_collisions = NULL; + g_hash_table_remove (self->dir_entries, parent_id); + g_hash_table_insert (self->dir_entries, g_strdup (parent_id), dir_entries_data_new ()); succeeded_once = TRUE; } @@ -583,15 +481,13 @@ rebuild_entries (GVfsBackendGoogle *self, for (l = entries; l != NULL; l = l->next) { GDataEntry *entry = GDATA_ENTRY (l->data); - insert_entry (self, entry); + insert_entry (self, parent, entry); } gdata_query_next_page (GDATA_QUERY (query)); g_clear_object (&feed); } - self->entries_stale = FALSE; - out: g_clear_object (&feed); g_clear_object (&query); @@ -600,53 +496,100 @@ rebuild_entries (GVfsBackendGoogle *self, /* ---------------------------------------------------------------------------------------------------- */ static GDataEntry * -resolve_child (GVfsBackendGoogle *self, - GDataEntry *parent, - const gchar *basename) +resolve_child (GVfsBackendGoogle *self, + GDataEntry *parent, + const gchar *basename, + GCancellable *cancellable, + GError **error) { - DirEntriesKey *k; - GDataEntry *entry; - const gchar *parent_id; + DirEntriesData *d; + GDataEntry *entry = NULL; + GError *local_error = NULL; - parent_id = gdata_entry_get_id (parent); - k = dir_entries_key_new (basename, parent_id); - entry = g_hash_table_lookup (self->dir_entries, k); - dir_entries_key_free (k); + d = g_hash_table_lookup (self->dir_entries, gdata_entry_get_id (parent)); + if (d == NULL || !dir_entries_data_is_valid (d)) + { + rebuild_dir (self, parent, cancellable, &local_error); + if (local_error != NULL) + { + g_propagate_error (error, local_error); + goto out; + } + + d = g_hash_table_lookup (self->dir_entries, gdata_entry_get_id (parent)); + } + + if (d != NULL) + { + entry = g_hash_table_lookup (d->entries, basename); + if (entry == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("No such file or directory")); + goto out; + } + + g_object_ref (entry); + } + + out: return entry; } static GDataEntry * resolve (GVfsBackendGoogle *self, const gchar *filename, + gchar **out_path, + GDataEntry **out_parent, + GCancellable *cancellable, GError **error) { - GDataEntry *parent; + GDataEntry *parent = NULL; GDataEntry *ret_val = NULL; GError *local_error; gchar *basename = NULL; + g_assert (filename && filename[0] == '/'); + if (g_strcmp0 (filename, "/") == 0) { - ret_val = self->root; + ret_val = g_object_ref (self->root); + + if (out_path != NULL) + *out_path = g_strdup ("/"); + + if (out_parent != NULL) + *out_parent = NULL; + goto out; } local_error = NULL; - parent = resolve_dir (self, filename, &basename, &local_error); + parent = resolve_dir (self, filename, &basename, out_path, cancellable, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); goto out; } - ret_val = resolve_child (self, parent, basename); - if (ret_val == NULL) + ret_val = resolve_child (self, parent, basename, cancellable, &local_error); + if (local_error != NULL) { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("No such file or directory")); + g_propagate_error (error, local_error); goto out; } + if (out_parent != NULL) + *out_parent = g_object_ref (parent); + + if (out_path != NULL) + { + gchar *temp = *out_path; + *out_path = g_build_path ("/", *out_path, gdata_entry_get_id (ret_val), NULL); + g_free (temp); + } + out: + g_clear_object (&parent); g_free (basename); return ret_val; } @@ -655,6 +598,8 @@ static GDataEntry * resolve_dir (GVfsBackendGoogle *self, const gchar *filename, gchar **out_basename, + gchar **out_path, + GCancellable *cancellable, GError **error) { GDataEntry *parent; @@ -667,7 +612,7 @@ resolve_dir (GVfsBackendGoogle *self, parent_path = g_path_get_dirname (filename); local_error = NULL; - parent = resolve (self, parent_path, &local_error); + parent = resolve (self, parent_path, out_path, NULL, cancellable, &local_error); if (local_error != NULL) { g_propagate_error (error, local_error); @@ -696,101 +641,6 @@ resolve_dir (GVfsBackendGoogle *self, /* ---------------------------------------------------------------------------------------------------- */ -static GDataEntry * -resolve_and_rebuild (GVfsBackendGoogle *self, - const gchar *filename, - GCancellable *cancellable, - GError **error) -{ - GDataEntry *entry; - GDataEntry *ret_val = NULL; - - entry = resolve (self, filename, NULL); - if (entry == NULL) - { - GError *local_error; - - local_error = NULL; - rebuild_entries (self, cancellable, &local_error); - if (local_error != NULL) - { - g_propagate_error (error, local_error); - goto out; - } - - local_error = NULL; - entry = resolve (self, filename, &local_error); - if (local_error != NULL) - { - g_propagate_error (error, local_error); - goto out; - } - } - - ret_val = entry; - - out: - return ret_val; -} - -static GDataEntry * -resolve_dir_and_rebuild (GVfsBackendGoogle *self, - const gchar *filename, - GCancellable *cancellable, - gchar **out_basename, - GError **error) -{ - GDataEntry *parent; - GDataEntry *ret_val = NULL; - GError *local_error; - gchar *basename = NULL; - - local_error = NULL; - parent = resolve_dir (self, filename, &basename, &local_error); - if (local_error != NULL) - { - if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY)) - { - g_propagate_error (error, local_error); - goto out; - } - else - { - g_error_free (local_error); - } - - local_error = NULL; - rebuild_entries (self, cancellable, &local_error); - if (local_error != NULL) - { - g_propagate_error (error, local_error); - goto out; - } - - local_error = NULL; - parent = resolve_dir (self, filename, &basename, &local_error); - if (local_error != NULL) - { - g_propagate_error (error, local_error); - goto out; - } - } - - if (out_basename != NULL) - { - *out_basename = basename; - basename = NULL; - } - - ret_val = parent; - - out: - g_free (basename); - return ret_val; -} - -/* ---------------------------------------------------------------------------------------------------- */ - static char * get_extension_offset (const char *title) { @@ -820,13 +670,12 @@ get_extension_offset (const char *title) } static gchar * -generate_copy_name (GVfsBackendGoogle *self, GDataEntry *entry) +generate_copy_name (GVfsBackendGoogle *self, GDataEntry *entry, const gchar *entry_path) { - GDataEntry *existing_entry; - GDataEntry *parent; + GDataEntry *existing_entry = NULL; + GDataEntry *parent = NULL; const gchar *id; const gchar *title; - gchar *entry_path = NULL; gchar *extension = NULL; gchar *extension_offset; gchar *ret_val = NULL; @@ -834,15 +683,11 @@ generate_copy_name (GVfsBackendGoogle *self, GDataEntry *entry) title = gdata_entry_get_title (entry); - entry_path = get_entry_path (self, entry); - if (entry_path == NULL) - goto out; - - parent = resolve_dir (self, entry_path, NULL, NULL); + parent = resolve_dir (self, entry_path, NULL, NULL, NULL, NULL); if (parent == NULL) goto out; - existing_entry = resolve_child (self, parent, title); + existing_entry = resolve_child (self, parent, title, NULL, NULL); if (existing_entry == entry) goto out; @@ -860,7 +705,9 @@ generate_copy_name (GVfsBackendGoogle *self, GDataEntry *entry) out: if (ret_val == NULL) ret_val = g_strdup (title); - g_free (entry_path); + + g_clear_object (&parent); + g_clear_object (&existing_entry); g_free (extension); g_free (title_without_extension); return ret_val; @@ -891,15 +738,12 @@ build_file_info (GVfsBackendGoogle *self, GFileAttributeMatcher *matcher, gboolean is_symlink, const gchar *symlink_name, - const gchar *symlink_target, - GError **error) + const gchar *entry_path) { GFileType file_type; GList *authors; - GList *links; gboolean is_folder = FALSE; gboolean is_root = FALSE; - gboolean has_parent = FALSE; const gchar *etag; const gchar *id; const gchar *name; @@ -918,9 +762,6 @@ build_file_info (GVfsBackendGoogle *self, if (entry == self->root) is_root = TRUE; - links = gdata_entry_look_up_links (entry, GDATA_LINK_PARENT); - has_parent = (links != NULL); - g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, !is_root); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, is_folder); @@ -929,9 +770,7 @@ build_file_info (GVfsBackendGoogle *self, g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_VOLATILE, is_symlink); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE); - g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, !is_root && has_parent); - - g_file_info_set_is_hidden (info, !has_parent); + g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, !is_root); if (is_folder) { @@ -976,7 +815,7 @@ build_file_info (GVfsBackendGoogle *self, file_type = G_FILE_TYPE_SYMBOLIC_LINK; } - g_file_info_set_symlink_target (info, symlink_target); + g_file_info_set_symlink_target (info, entry_path); } if (content_type != NULL) @@ -1024,7 +863,7 @@ build_file_info (GVfsBackendGoogle *self, g_file_info_set_display_name (info, title); g_file_info_set_edit_name (info, title); - copy_name = generate_copy_name (self, entry); + copy_name = generate_copy_name (self, entry, entry_path); /* Sanitize copy-name by replacing slashes with dashes. This is * what nautilus does (for desktop files). @@ -1084,7 +923,6 @@ build_file_info (GVfsBackendGoogle *self, g_free (copy_name); g_free (escaped_name); g_free (content_type); - g_list_free (links); } /* ---------------------------------------------------------------------------------------------------- */ @@ -1112,18 +950,17 @@ g_vfs_backend_google_copy (GVfsBackend *_self, GCancellable *cancellable = G_VFS_JOB (job)->cancellable; GDataDocumentsEntry *dummy_source_entry = NULL; GDataDocumentsEntry *new_entry = NULL; - GDataEntry *destination_parent; - GDataEntry *existing_entry; - GDataEntry *source_entry; + GDataEntry *destination_parent = NULL; + GDataEntry *existing_entry = NULL; + GDataEntry *source_entry = NULL; GError *error; GType source_entry_type; - gboolean needs_rebuild = FALSE; - gboolean destination_not_directory = FALSE; const gchar *etag; const gchar *id; const gchar *summary; gchar *destination_basename = NULL; gchar *entry_path = NULL; + gchar *parent_path = NULL; goffset size; g_rec_mutex_lock (&self->mutex); @@ -1139,56 +976,22 @@ g_vfs_backend_google_copy (GVfsBackend *_self, goto out; } - source_entry = resolve (self, source, NULL); - if (source_entry == NULL) - needs_rebuild = TRUE; - error = NULL; - destination_parent = resolve_dir (self, destination, &destination_basename, &error); + source_entry = resolve (self, source, NULL, NULL, cancellable, &error); if (error != NULL) { - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY)) - destination_not_directory = TRUE; - else - needs_rebuild = TRUE; - + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); g_error_free (error); + goto out; } - if (needs_rebuild) + error = NULL; + destination_parent = resolve_dir (self, destination, &destination_basename, &parent_path, cancellable, &error); + if (error != NULL) { - error = NULL; - rebuild_entries (self, cancellable, &error); - if (error != NULL) - { - g_vfs_job_failed_from_error (G_VFS_JOB (job), error); - g_error_free (error); - goto out; - } - - error = NULL; - source_entry = resolve (self, source, &error); - if (error != NULL) - { - g_vfs_job_failed_from_error (G_VFS_JOB (job), error); - g_error_free (error); - goto out; - } - - if (!destination_not_directory) - { - g_free (destination_basename); - destination_basename = NULL; - - error = NULL; - destination_parent = resolve_dir (self, destination, &destination_basename, &error); - if (error != NULL) - { - g_vfs_job_failed_from_error (G_VFS_JOB (job), error); - g_error_free (error); - goto out; - } - } + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + goto out; } etag = gdata_entry_get_etag (source_entry); @@ -1207,13 +1010,21 @@ g_vfs_backend_google_copy (GVfsBackend *_self, goto out; } - if (destination_not_directory) + existing_entry = resolve_child (self, destination_parent, destination_basename, cancellable, &error); + if (error != NULL) { - g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, _("The file is not a directory")); - goto out; + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + g_clear_error (&error); + } + else + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + goto out; + } } - existing_entry = resolve_child (self, destination_parent, destination_basename); if (existing_entry != NULL) { if (flags & G_FILE_COPY_OVERWRITE) @@ -1267,10 +1078,10 @@ g_vfs_backend_google_copy (GVfsBackend *_self, goto out; } - entry_path = get_entry_path (self, GDATA_ENTRY (new_entry)); + entry_path = g_build_path ("/", parent_path, gdata_entry_get_id (GDATA_ENTRY (new_entry)), NULL); g_debug (" new entry path: %s\n", entry_path); - insert_entry (self, GDATA_ENTRY (new_entry)); + insert_entry (self, destination_parent, GDATA_ENTRY (new_entry)); g_hash_table_foreach (self->monitors, emit_create_event, entry_path); #if HAVE_LIBGDATA_0_17_7 @@ -1284,8 +1095,12 @@ g_vfs_backend_google_copy (GVfsBackend *_self, out: g_clear_object (&dummy_source_entry); g_clear_object (&new_entry); + g_clear_object (&destination_parent); + g_clear_object (&existing_entry); + g_clear_object (&source_entry); g_free (destination_basename); g_free (entry_path); + g_free (parent_path); g_debug ("- copy\n"); g_rec_mutex_unlock (&self->mutex); } @@ -1300,7 +1115,7 @@ g_vfs_backend_google_create_dir_monitor (GVfsBackend *_self, { GVfsBackendGoogle *self = G_VFS_BACKEND_GOOGLE (_self); GCancellable *cancellable = G_VFS_JOB (job)->cancellable; - GDataEntry *entry; + GDataEntry *entry = NULL; GError *error; GVfsMonitor *monitor = NULL; gchar *entry_path = NULL; @@ -1315,7 +1130,7 @@ g_vfs_backend_google_create_dir_monitor (GVfsBackend *_self, } error = NULL; - entry = resolve_and_rebuild (self, filename, cancellable, &error); + entry = resolve (self, filename, &entry_path, NULL, cancellable, &error); if (error != NULL) { g_vfs_job_failed_from_error (G_VFS_JOB (job), error); @@ -1323,7 +1138,6 @@ g_vfs_backend_google_create_dir_monitor (GVfsBackend *_self, goto out; } - entry_path = get_entry_path (self, entry); g_debug (" entry path: %s\n", entry_path); if (!GDATA_IS_DOCUMENTS_FOLDER (entry)) @@ -1341,6 +1155,7 @@ g_vfs_backend_google_create_dir_monitor (GVfsBackend *_self, out: g_clear_object (&monitor); + g_clear_object (&entry); g_free (entry_path); g_debug ("- create_dir_monitor\n"); g_rec_mutex_unlock (&self->mutex); @@ -1357,7 +1172,8 @@ g_vfs_backend_google_delete (GVfsBackend *_self, GVfsBackendGoogle *self = G_VFS_BACKEND_GOOGLE (_self); GCancellable *cancellable = G_VFS_JOB (job)->cancellable; GDataAuthorizationDomain *auth_domain; - GDataEntry *entry; + GDataEntry *entry = NULL; + GDataEntry *parent = NULL; GError *error; gchar *entry_path = NULL; @@ -1365,7 +1181,7 @@ g_vfs_backend_google_delete (GVfsBackend *_self, g_debug ("+ delete: %s\n", filename); error = NULL; - entry = resolve_and_rebuild (self, filename, cancellable, &error); + entry = resolve (self, filename, &entry_path, &parent, cancellable, &error); if (error != NULL) { g_vfs_job_failed_from_error (G_VFS_JOB (job), error); @@ -1373,7 +1189,6 @@ g_vfs_backend_google_delete (GVfsBackend *_self, goto out; } - entry_path = get_entry_path (self, entry); g_debug (" entry path: %s\n", entry_path); if (entry == self->root) @@ -1394,11 +1209,13 @@ g_vfs_backend_google_delete (GVfsBackend *_self, goto out; } - remove_entry (self, entry); + remove_entry (self, NULL, entry); g_hash_table_foreach (self->monitors, emit_delete_event, entry_path); g_vfs_job_succeeded (G_VFS_JOB (job)); out: + g_clear_object (&parent); + g_clear_object (&entry); g_free (entry_path); g_debug ("- delete\n"); g_rec_mutex_unlock (&self->mutex); @@ -1406,17 +1223,6 @@ g_vfs_backend_google_delete (GVfsBackend *_self, /* ---------------------------------------------------------------------------------------------------- */ -static gboolean -rebuild_entries_timeout_cb (gpointer user_data) -{ - GVfsBackendGoogle *self = G_VFS_BACKEND_GOOGLE (user_data); - - self->entries_stale = TRUE; - self->entries_stale_timeout = 0; - - return G_SOURCE_REMOVE; -} - static void g_vfs_backend_google_enumerate (GVfsBackend *_self, GVfsJobEnumerate *job, @@ -1426,37 +1232,18 @@ g_vfs_backend_google_enumerate (GVfsBackend *_self, { GVfsBackendGoogle *self = G_VFS_BACKEND_GOOGLE (_self); GCancellable *cancellable = G_VFS_JOB (job)->cancellable; - GDataEntry *entry; + GDataEntry *parent, *entry; GError *error; + DirEntriesData *d; GHashTableIter iter; - gchar *entry_path = NULL; + const gchar *basename = NULL; + char *parent_path = NULL; g_rec_mutex_lock (&self->mutex); g_debug ("+ enumerate: %s\n", filename); - if (self->entries_stale_timeout == 0) - { - self->entries_stale_timeout = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, - REBUILD_ENTRIES_TIMEOUT, - rebuild_entries_timeout_cb, - g_object_ref (self), - g_object_unref); - } - - if (self->entries_stale) - { - error = NULL; - rebuild_entries (self, cancellable, &error); - if (error != NULL) - { - g_vfs_job_failed_from_error (G_VFS_JOB (job), error); - g_error_free (error); - goto out; - } - } - error = NULL; - entry = resolve (self, filename, &error); + parent = resolve (self, filename, &parent_path, NULL, cancellable, &error); if (error != NULL) { g_vfs_job_failed_from_error (G_VFS_JOB (job), error); @@ -1464,51 +1251,59 @@ g_vfs_backend_google_enumerate (GVfsBackend *_self, goto out; } - entry_path = get_entry_path (self, entry); - g_debug (" entry path: %s\n", entry_path); - - if (!GDATA_IS_DOCUMENTS_FOLDER (entry)) + if (!GDATA_IS_DOCUMENTS_FOLDER (parent)) { - g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,_("The file is not a directory")); + g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, _("The file is not a directory")); goto out; } - g_vfs_job_succeeded (G_VFS_JOB (job)); - - g_hash_table_iter_init (&iter, self->entries); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &entry)) + d = g_hash_table_lookup (self->dir_entries, gdata_entry_get_id (parent)); + if (d == NULL || !dir_entries_data_is_valid (d)) { - gchar *path; + rebuild_dir (self, parent, cancellable, &error); + if (error != NULL) + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + goto out; + } - path = get_entry_path (self, entry); - g_debug (" found entry: %s\n", path); - if (path != NULL) + d = g_hash_table_lookup (self->dir_entries, gdata_entry_get_id (parent)); + if (d == NULL) { - gchar *parent_path; + g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("No such file or directory")); + goto out; + } + } - parent_path = g_path_get_dirname (path); - if (g_strcmp0 (entry_path, parent_path) == 0) - { - GFileInfo *info; + g_vfs_job_succeeded (G_VFS_JOB (job)); - info = g_file_info_new (); - build_file_info (self, entry, flags, info, matcher, FALSE, NULL, NULL, NULL); - g_vfs_job_enumerate_add_info (job, info); - g_object_unref (info); - } + g_hash_table_iter_init (&iter, d->entries); + while (g_hash_table_iter_next (&iter, (gpointer *) &basename, (gpointer *) &entry)) + { + GFileInfo *info; + char *entry_path; - g_free (parent_path); - } + /* Skip volatile entries */ + if (g_strcmp0 (gdata_entry_get_id (entry), basename) != 0) + continue; + + entry_path = g_build_path ("/", parent_path, gdata_entry_get_id (GDATA_ENTRY (entry)), NULL); + info = g_file_info_new (); + build_file_info (self, entry, flags, info, matcher, FALSE, NULL, entry_path); + g_vfs_job_enumerate_add_info (job, info); - g_free (path); + g_free (entry_path); + g_object_unref (info); } g_vfs_job_enumerate_done (job); out: - g_free (entry_path); + g_clear_object (&parent); g_debug ("- enumerate\n"); g_rec_mutex_unlock (&self->mutex); + g_free (parent_path); } /* ---------------------------------------------------------------------------------------------------- */ @@ -1522,11 +1317,9 @@ g_vfs_backend_google_make_directory (GVfsBackend *_self, GCancellable *cancellable = G_VFS_JOB (job)->cancellable; GDataDocumentsEntry *new_folder = NULL; GDataDocumentsFolder *folder = NULL; - GDataEntry *existing_entry; - GDataEntry *parent; - GDataEntry *summary_entry; + GDataEntry *existing_entry = NULL; + GDataEntry *parent = NULL; GError *error; - const gchar *summary; gchar *entry_path = NULL; gchar *basename = NULL; gchar *parent_path = NULL; @@ -1541,7 +1334,7 @@ g_vfs_backend_google_make_directory (GVfsBackend *_self, } error = NULL; - parent = resolve_dir_and_rebuild (self, filename, cancellable, &basename, &error); + parent = resolve_dir (self, filename, &basename, &parent_path, cancellable, &error); if (error != NULL) { g_vfs_job_failed_from_error (G_VFS_JOB (job), error); @@ -1549,16 +1342,23 @@ g_vfs_backend_google_make_directory (GVfsBackend *_self, goto out; } - parent_path = get_entry_path (self, parent); g_debug (" parent path: %s\n", parent_path); - summary_entry = g_hash_table_lookup (self->entries, basename); - if (summary_entry == NULL) - summary = NULL; - else - summary = gdata_entry_get_summary (summary_entry); + existing_entry = resolve_child (self, parent, basename, cancellable, &error); + if (error != NULL) + { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + g_clear_error (&error); + } + else + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + goto out; + } + } - existing_entry = resolve_child (self, parent, basename); if (existing_entry != NULL) { g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_EXISTS, _("Target file already exists")); @@ -1567,7 +1367,6 @@ g_vfs_backend_google_make_directory (GVfsBackend *_self, folder = gdata_documents_folder_new (NULL); gdata_entry_set_title (GDATA_ENTRY (folder), basename); - gdata_entry_set_summary (GDATA_ENTRY (folder), summary); error = NULL; new_folder = gdata_documents_service_add_entry_to_folder (self->service, @@ -1583,16 +1382,18 @@ g_vfs_backend_google_make_directory (GVfsBackend *_self, goto out; } - entry_path = get_entry_path (self, GDATA_ENTRY (new_folder)); + entry_path = g_build_path ("/", parent_path, gdata_entry_get_id (GDATA_ENTRY (new_folder)), NULL); g_debug (" new entry path: %s\n", entry_path); - insert_entry (self, GDATA_ENTRY (new_folder)); + insert_entry (self, parent, GDATA_ENTRY (new_folder)); g_hash_table_foreach (self->monitors, emit_create_event, entry_path); g_vfs_job_succeeded (G_VFS_JOB (job)); out: g_clear_object (&folder); g_clear_object (&new_folder); + g_clear_object (&parent); + g_clear_object (&existing_entry); g_free (basename); g_free (entry_path); g_free (parent_path); @@ -1741,8 +1542,8 @@ g_vfs_backend_google_push (GVfsBackend *_self, GCancellable *cancellable = G_VFS_JOB (job)->cancellable; GDataDocumentsDocument *document = NULL; GDataDocumentsDocument *new_document = NULL; - GDataEntry *destination_parent; - GDataEntry *existing_entry; + GDataEntry *destination_parent = NULL; + GDataEntry *existing_entry = NULL; GDataUploadStream *ostream = NULL; GError *error; GFile *local_file = NULL; @@ -1753,6 +1554,7 @@ g_vfs_backend_google_push (GVfsBackend *_self, const gchar *title; gchar *destination_basename = NULL; gchar *entry_path = NULL; + gchar *parent_path = NULL; goffset size; g_rec_mutex_lock (&self->mutex); @@ -1786,7 +1588,7 @@ g_vfs_backend_google_push (GVfsBackend *_self, } error = NULL; - destination_parent = resolve_dir_and_rebuild (self, destination, cancellable, &destination_basename, &error); + destination_parent = resolve_dir (self, destination, &destination_basename, &parent_path, cancellable, &error); if (error != NULL) { g_vfs_job_failed_from_error (G_VFS_JOB (job), error); @@ -1794,7 +1596,21 @@ g_vfs_backend_google_push (GVfsBackend *_self, goto out; } - existing_entry = resolve_child (self, destination_parent, destination_basename); + existing_entry = resolve_child (self, destination_parent, destination_basename, cancellable, &error); + if (error != NULL) + { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + g_clear_error (&error); + } + else + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + goto out; + } + } + if (existing_entry != NULL) { if (flags & G_FILE_COPY_OVERWRITE) @@ -1931,14 +1747,18 @@ g_vfs_backend_google_push (GVfsBackend *_self, goto out; } - entry_path = get_entry_path (self, GDATA_ENTRY (new_document)); + entry_path = g_build_path ("/", parent_path, gdata_entry_get_id (GDATA_ENTRY (new_document)), NULL); g_debug (" new entry path: %s\n", entry_path); if (needs_overwrite) - remove_entry (self, existing_entry); + { + remove_entry (self, NULL, existing_entry); + g_hash_table_foreach (self->monitors, emit_changes_done_event, entry_path); + } - insert_entry (self, GDATA_ENTRY (new_document)); - g_hash_table_foreach (self->monitors, emit_create_event, entry_path); + insert_entry (self, destination_parent, GDATA_ENTRY (new_document)); + if (!needs_overwrite) + g_hash_table_foreach (self->monitors, emit_create_event, entry_path); if (remove_source) { @@ -1967,8 +1787,11 @@ g_vfs_backend_google_push (GVfsBackend *_self, g_clear_object (&local_file); g_clear_object (&new_document); g_clear_object (&ostream); + g_clear_object (&destination_parent); + g_clear_object (&existing_entry); g_free (destination_basename); g_free (entry_path); + g_free (parent_path); g_debug ("- push\n"); g_rec_mutex_unlock (&self->mutex); } @@ -2064,7 +1887,7 @@ g_vfs_backend_google_query_info (GVfsBackend *_self, { GVfsBackendGoogle *self = G_VFS_BACKEND_GOOGLE (_self); GCancellable *cancellable = G_VFS_JOB (job)->cancellable; - GDataEntry *entry; + GDataEntry *entry = NULL; GError *error; gboolean is_symlink = FALSE; gchar *entry_path = NULL; @@ -2074,7 +1897,7 @@ g_vfs_backend_google_query_info (GVfsBackend *_self, g_debug ("+ query_info: %s, %d\n", filename, flags); error = NULL; - entry = resolve_and_rebuild (self, filename, cancellable, &error); + entry = resolve (self, filename, &entry_path, NULL, cancellable, &error); if (error != NULL) { g_vfs_job_failed_from_error (G_VFS_JOB (job), error); @@ -2082,7 +1905,6 @@ g_vfs_backend_google_query_info (GVfsBackend *_self, goto out; } - entry_path = get_entry_path (self, entry); if (g_strcmp0 (entry_path, filename) != 0) /* volatile */ { is_symlink = TRUE; @@ -2091,18 +1913,12 @@ g_vfs_backend_google_query_info (GVfsBackend *_self, g_debug (" entry path: %s (%d)\n", entry_path, is_symlink); - error = NULL; - build_file_info (self, entry, flags, info, matcher, is_symlink, symlink_name, entry_path, &error); - if (error != NULL) - { - g_vfs_job_failed_from_error (G_VFS_JOB (job), error); - g_error_free (error); - goto out; - } + build_file_info (self, entry, flags, info, matcher, is_symlink, symlink_name, entry_path); g_vfs_job_succeeded (G_VFS_JOB (job)); out: + g_clear_object (&entry); g_free (entry_path); g_free (symlink_name); g_debug ("- query_info\n"); @@ -2120,7 +1936,6 @@ g_vfs_backend_google_query_info_on_read (GVfsBackend *_self, { GVfsBackendGoogle *self = G_VFS_BACKEND_GOOGLE (_self); GDataEntry *entry; - GError *error; GInputStream *stream = G_INPUT_STREAM (handle); gboolean is_symlink = FALSE; const gchar *filename; @@ -2131,8 +1946,8 @@ g_vfs_backend_google_query_info_on_read (GVfsBackend *_self, entry = g_object_get_data (G_OBJECT (stream), "g-vfs-backend-google-entry"); filename = g_object_get_data (G_OBJECT (stream), "g-vfs-backend-google-filename"); + entry_path = g_object_get_data (G_OBJECT (stream), "g-vfs-backend-google-entry-path"); - entry_path = get_entry_path (self, entry); if (g_strcmp0 (entry_path, filename) != 0) /* volatile */ { is_symlink = TRUE; @@ -2141,7 +1956,6 @@ g_vfs_backend_google_query_info_on_read (GVfsBackend *_self, g_debug (" entry path: %s (%d)\n", entry_path, is_symlink); - error = NULL; build_file_info (self, entry, G_FILE_QUERY_INFO_NONE, @@ -2149,19 +1963,10 @@ g_vfs_backend_google_query_info_on_read (GVfsBackend *_self, matcher, is_symlink, symlink_name, - entry_path, - &error); - if (error != NULL) - { - g_vfs_job_failed_from_error (G_VFS_JOB (job), error); - g_error_free (error); - goto out; - } + entry_path); g_vfs_job_succeeded (G_VFS_JOB (job)); - out: - g_free (entry_path); g_free (symlink_name); g_debug ("- query_info_on_read\n"); } @@ -2176,24 +1981,20 @@ g_vfs_backend_google_query_info_on_write (GVfsBackend *_self, GFileAttributeMatcher *matcher) { GVfsBackendGoogle *self = G_VFS_BACKEND_GOOGLE (_self); - GError *error; WriteHandle *wh = (WriteHandle *) handle; gboolean is_symlink = FALSE; - gchar *entry_path = NULL; gchar *symlink_name = NULL; g_debug ("+ query_info_on_write: %p\n", handle); - entry_path = get_entry_path (self, wh->document); - if (g_strcmp0 (entry_path, wh->filename) != 0) /* volatile */ + if (g_strcmp0 (wh->entry_path, wh->filename) != 0) /* volatile */ { is_symlink = TRUE; symlink_name = g_path_get_basename (wh->filename); } - g_debug (" entry path: %s (%d)\n", entry_path, is_symlink); + g_debug (" entry path: %s (%d)\n", wh->entry_path, is_symlink); - error = NULL; build_file_info (self, wh->document, G_FILE_QUERY_INFO_NONE, @@ -2201,19 +2002,10 @@ g_vfs_backend_google_query_info_on_write (GVfsBackend *_self, matcher, is_symlink, symlink_name, - entry_path, - &error); - if (error != NULL) - { - g_vfs_job_failed_from_error (G_VFS_JOB (job), error); - g_error_free (error); - goto out; - } + wh->entry_path); g_vfs_job_succeeded (G_VFS_JOB (job)); - out: - g_free (entry_path); g_free (symlink_name); g_debug ("- query_info_on_write\n"); return TRUE; @@ -2228,7 +2020,7 @@ g_vfs_backend_google_open_for_read (GVfsBackend *_self, { GVfsBackendGoogle *self = G_VFS_BACKEND_GOOGLE (_self); GCancellable *cancellable = G_VFS_JOB (job)->cancellable; - GDataEntry *entry; + GDataEntry *entry = NULL; GInputStream *stream; GError *error; gchar *content_type = NULL; @@ -2240,7 +2032,7 @@ g_vfs_backend_google_open_for_read (GVfsBackend *_self, g_debug ("+ open_for_read: %s\n", filename); error = NULL; - entry = resolve_and_rebuild (self, filename, cancellable, &error); + entry = resolve (self, filename, &entry_path, NULL, cancellable, &error); if (error != NULL) { g_vfs_job_failed_from_error (G_VFS_JOB (job), error); @@ -2248,7 +2040,6 @@ g_vfs_backend_google_open_for_read (GVfsBackend *_self, goto out; } - entry_path = get_entry_path (self, entry); g_debug (" entry path: %s\n", entry_path); if (GDATA_IS_DOCUMENTS_FOLDER (entry)) @@ -2281,11 +2072,13 @@ g_vfs_backend_google_open_for_read (GVfsBackend *_self, g_object_set_data_full (G_OBJECT (stream), "g-vfs-backend-google-entry", g_object_ref (entry), g_object_unref); g_object_set_data_full (G_OBJECT (stream), "g-vfs-backend-google-filename", g_strdup (filename), g_free); + g_object_set_data_full (G_OBJECT (stream), "g-vfs-backend-google-entry-path", g_strdup (entry_path), g_free); g_vfs_job_open_for_read_set_handle (job, stream); g_vfs_job_open_for_read_set_can_seek (job, TRUE); g_vfs_job_succeeded (G_VFS_JOB (job)); out: + g_clear_object (&entry); g_free (content_type); g_free (entry_path); g_debug ("- open_for_read\n"); @@ -2419,7 +2212,9 @@ g_vfs_backend_google_set_display_name (GVfsBackend *_self, GVfsBackendGoogle *self = G_VFS_BACKEND_GOOGLE (_self); GCancellable *cancellable = G_VFS_JOB (job)->cancellable; GDataAuthorizationDomain *auth_domain; - GDataEntry *entry; + GDataEntry *existing_entry = NULL; + GDataEntry *entry = NULL; + GDataEntry *parent = NULL; GDataEntry *new_entry = NULL; GError *error; gchar *entry_path = NULL; @@ -2428,7 +2223,7 @@ g_vfs_backend_google_set_display_name (GVfsBackend *_self, g_debug ("+ set_display_name: %s, %s\n", filename, display_name); error = NULL; - entry = resolve_and_rebuild (self, filename, cancellable, &error); + entry = resolve (self, filename, &entry_path, &parent, cancellable, &error); if (error != NULL) { g_vfs_job_failed_from_error (G_VFS_JOB (job), error); @@ -2436,7 +2231,27 @@ g_vfs_backend_google_set_display_name (GVfsBackend *_self, goto out; } - entry_path = get_entry_path (self, entry); + existing_entry = resolve_child (self, parent, display_name, cancellable, &error); + if (error != NULL) + { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + g_clear_error (&error); + } + else + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + goto out; + } + } + + if (existing_entry != NULL) + { + g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_EXISTS, _("Target file already exists")); + goto out; + } + g_debug (" entry path: %s\n", entry_path); if (entry == self->root) @@ -2458,14 +2273,16 @@ g_vfs_backend_google_set_display_name (GVfsBackend *_self, goto out; } - remove_entry (self, entry); - insert_entry (self, new_entry); + remove_entry (self, NULL, entry); g_hash_table_foreach (self->monitors, emit_attribute_changed_event, entry_path); g_vfs_job_set_display_name_set_new_path (job, entry_path); g_vfs_job_succeeded (G_VFS_JOB (job)); out: g_clear_object (&new_entry); + g_clear_object (&parent); + g_clear_object (&existing_entry); + g_clear_object (&entry); g_free (entry_path); g_debug ("- set_display_name\n"); g_rec_mutex_unlock (&self->mutex); @@ -2483,8 +2300,8 @@ g_vfs_backend_google_create (GVfsBackend *_self, GCancellable *cancellable = G_VFS_JOB (job)->cancellable; GDataDocumentsDocument *document = NULL; GDataDocumentsEntry *new_document = NULL; - GDataEntry *existing_entry; - GDataEntry *parent; + GDataEntry *existing_entry = NULL; + GDataEntry *parent = NULL; GError *error; WriteHandle *handle; gchar *basename = NULL; @@ -2501,7 +2318,7 @@ g_vfs_backend_google_create (GVfsBackend *_self, } error = NULL; - parent = resolve_dir_and_rebuild (self, filename, cancellable, &basename, &error); + parent = resolve_dir (self, filename, &basename, &parent_path, cancellable, &error); if (error != NULL) { g_vfs_job_failed_from_error (G_VFS_JOB (job), error); @@ -2509,10 +2326,23 @@ g_vfs_backend_google_create (GVfsBackend *_self, goto out; } - parent_path = get_entry_path (self, parent); g_debug (" parent path: %s\n", parent_path); - existing_entry = resolve_child (self, parent, basename); + existing_entry = resolve_child (self, parent, basename, cancellable, &error); + if (error != NULL) + { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + g_clear_error (&error); + } + else + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + goto out; + } + } + if (existing_entry != NULL) { if (flags & G_FILE_CREATE_REPLACE_DESTINATION) @@ -2547,19 +2377,21 @@ g_vfs_backend_google_create (GVfsBackend *_self, goto out; } - entry_path = get_entry_path (self, GDATA_ENTRY (new_document)); + entry_path = g_build_path ("/", parent_path, gdata_entry_get_id (GDATA_ENTRY (new_document)), NULL); g_debug (" new entry path: %s\n", entry_path); - insert_entry (self, GDATA_ENTRY (new_document)); + insert_entry (self, parent, GDATA_ENTRY (new_document)); g_hash_table_foreach (self->monitors, emit_create_event, entry_path); - handle = write_handle_new (GDATA_ENTRY (new_document), NULL, filename); + handle = write_handle_new (GDATA_ENTRY (new_document), NULL, filename, entry_path); g_vfs_job_open_for_write_set_handle (job, handle); g_vfs_job_succeeded (G_VFS_JOB (job)); out: g_clear_object (&document); g_clear_object (&new_document); + g_clear_object (&parent); + g_clear_object (&existing_entry); g_free (basename); g_free (entry_path); g_free (parent_path); @@ -2581,8 +2413,8 @@ g_vfs_backend_google_replace (GVfsBackend *_self, GCancellable *cancellable = G_VFS_JOB (job)->cancellable; GDataDocumentsDocument *document = NULL; GDataDocumentsEntry *new_document = NULL; - GDataEntry *existing_entry; - GDataEntry *parent; + GDataEntry *existing_entry = NULL; + GDataEntry *parent = NULL; GDataUploadStream *stream = NULL; GError *error; WriteHandle *handle; @@ -2611,7 +2443,7 @@ g_vfs_backend_google_replace (GVfsBackend *_self, } error = NULL; - parent = resolve_dir_and_rebuild (self, filename, cancellable, &basename, &error); + parent = resolve_dir (self, filename, &basename, &parent_path, cancellable, &error); if (error != NULL) { g_vfs_job_failed_from_error (G_VFS_JOB (job), error); @@ -2619,10 +2451,23 @@ g_vfs_backend_google_replace (GVfsBackend *_self, goto out; } - parent_path = get_entry_path (self, parent); g_debug (" parent path: %s\n", parent_path); - existing_entry = resolve_child (self, parent, basename); + existing_entry = resolve_child (self, parent, basename, cancellable, &error); + if (error != NULL) + { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + g_clear_error (&error); + } + else + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + goto out; + } + } + if (existing_entry != NULL) { if (GDATA_IS_DOCUMENTS_FOLDER (existing_entry)) @@ -2648,7 +2493,7 @@ g_vfs_backend_google_replace (GVfsBackend *_self, { const gchar *title; - entry_path = get_entry_path (self, existing_entry); + entry_path = g_build_path ("/", parent_path, gdata_entry_get_id (existing_entry), NULL); g_debug (" existing entry path: %s\n", entry_path); title = gdata_entry_get_title (existing_entry); @@ -2669,7 +2514,7 @@ g_vfs_backend_google_replace (GVfsBackend *_self, goto out; } - handle = write_handle_new (NULL, stream, filename); + handle = write_handle_new (NULL, stream, filename, entry_path); } else { @@ -2690,13 +2535,13 @@ g_vfs_backend_google_replace (GVfsBackend *_self, goto out; } - entry_path = get_entry_path (self, GDATA_ENTRY (new_document)); + entry_path = g_build_path ("/", parent_path, gdata_entry_get_id (GDATA_ENTRY (new_document)), NULL); g_debug (" new entry path: %s\n", entry_path); - insert_entry (self, GDATA_ENTRY (new_document)); + insert_entry (self, parent, GDATA_ENTRY (new_document)); g_hash_table_foreach (self->monitors, emit_create_event, entry_path); - handle = write_handle_new (GDATA_ENTRY (new_document), NULL, filename); + handle = write_handle_new (GDATA_ENTRY (new_document), NULL, filename, entry_path); } g_vfs_job_open_for_write_set_handle (job, handle); @@ -2706,6 +2551,8 @@ g_vfs_backend_google_replace (GVfsBackend *_self, g_clear_object (&document); g_clear_object (&new_document); g_clear_object (&stream); + g_clear_object (&parent); + g_clear_object (&existing_entry); g_free (basename); g_free (content_type); g_free (entry_path); @@ -2727,7 +2574,6 @@ g_vfs_backend_google_write (GVfsBackend *_self, GCancellable *cancellable = G_VFS_JOB (job)->cancellable; GError *error; WriteHandle *wh = (WriteHandle *) handle; - gchar *entry_path = NULL; gssize nwrite; g_debug ("+ write: %p\n", handle); @@ -2760,9 +2606,7 @@ g_vfs_backend_google_write (GVfsBackend *_self, } g_debug (" writing to stream: %p\n", wh->stream); - - entry_path = get_entry_path (self, wh->document); - g_debug (" entry path: %s\n", entry_path); + g_debug (" entry path: %s\n", wh->entry_path); error = NULL; nwrite = g_output_stream_write (G_OUTPUT_STREAM (wh->stream), @@ -2777,12 +2621,11 @@ g_vfs_backend_google_write (GVfsBackend *_self, goto out; } - g_hash_table_foreach (self->monitors, emit_changed_event, entry_path); + g_hash_table_foreach (self->monitors, emit_changed_event, wh->entry_path); g_vfs_job_write_set_written_size (job, (gsize) nwrite); g_vfs_job_succeeded (G_VFS_JOB (job)); out: - g_free (entry_path); g_debug ("- write\n"); } @@ -2797,8 +2640,8 @@ g_vfs_backend_google_close_write (GVfsBackend *_self, GCancellable *cancellable = G_VFS_JOB (job)->cancellable; GDataDocumentsDocument *new_document = NULL; GError *error; + GDataEntry *parent = NULL; WriteHandle *wh = (WriteHandle *) handle; - gchar *entry_path = NULL; g_debug ("+ close_write: %p\n", handle); @@ -2829,18 +2672,25 @@ g_vfs_backend_google_close_write (GVfsBackend *_self, goto out; } - entry_path = get_entry_path (self, GDATA_ENTRY (new_document)); - g_debug (" new entry path: %s\n", entry_path); + g_debug (" new entry path: %s\n", wh->entry_path); - remove_entry (self, wh->document); - insert_entry (self, GDATA_ENTRY (new_document)); - g_hash_table_foreach (self->monitors, emit_changes_done_event, entry_path); + parent = resolve_dir (self, wh->entry_path, NULL, NULL, cancellable, &error); + if (error != NULL) + { + sanitize_error (&error); + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + goto out; + } + + remove_entry (self, NULL, wh->document); + g_hash_table_foreach (self->monitors, emit_changes_done_event, wh->entry_path); g_vfs_job_succeeded (G_VFS_JOB (job)); out: g_clear_object (&new_document); + g_clear_object (&parent); write_handle_free (wh); - g_free (entry_path); g_debug ("- close_write\n"); } @@ -2851,22 +2701,9 @@ g_vfs_backend_google_dispose (GObject *_self) { GVfsBackendGoogle *self = G_VFS_BACKEND_GOOGLE (_self); - if (self->entries_stale_timeout != 0) - { - g_source_remove (self->entries_stale_timeout); - self->entries_stale_timeout = 0; - } - - if (self->dir_collisions != NULL) - { - g_list_free_full (self->dir_collisions, g_object_unref); - self->dir_collisions = NULL; - } - g_clear_object (&self->service); g_clear_object (&self->root); g_clear_object (&self->client); - g_clear_pointer (&self->entries, (GDestroyNotify) g_hash_table_unref); g_clear_pointer (&self->dir_entries, (GDestroyNotify) g_hash_table_unref); G_OBJECT_CLASS (g_vfs_backend_google_parent_class)->dispose (_self); @@ -2921,12 +2758,7 @@ g_vfs_backend_google_class_init (GVfsBackendGoogleClass * klass) static void g_vfs_backend_google_init (GVfsBackendGoogle *self) { - self->entries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - self->dir_entries = g_hash_table_new_full (entries_in_folder_hash, - entries_in_folder_equal, - dir_entries_key_free, - g_object_unref); + self->dir_entries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, dir_entries_data_free); self->monitors = g_hash_table_new (NULL, NULL); g_rec_mutex_init (&self->mutex); - self->entries_stale = TRUE; } |