diff --git a/SOURCES/google-performance-fixes.patch b/SOURCES/google-performance-fixes.patch
new file mode 100644
index 0000000..3225c59
--- /dev/null
+++ b/SOURCES/google-performance-fixes.patch
@@ -0,0 +1,2724 @@
+From ea26e6ddefca9b5f75f3d7485e420729bc038852 Mon Sep 17 00:00:00 2001
+From: Ondrej Holy <oholy@redhat.com>
+Date: Tue, 5 Jun 2018 13:04:15 +0200
+Subject: [PATCH 01/14] google: Do not create .desktop files for native files
+
+Currently, .desktop file with a link to the original location is provided,
+when a native file is opened because Google doesn't provide its proprietary
+formats and GLocalFile doesn't know G_FILE_TYPE_SHORTCUT. It is a bit
+tricky and confusing, among other because the .desktop files aren't
+automatically converted back to native files when writing.
+
+What is worse, Nautilus has dropped support for .desktop files recently,
+so you see "There was an error launching the application." instead of the
+requested file. It doesn't make more sense to provide this .desktop files,
+so let's remove this fallback and return error instead.
+
+https://gitlab.gnome.org/GNOME/nautilus/issues/448
+---
+ daemon/gvfsbackendgoogle.c | 68 ++++++++------------------------------
+ 1 file changed, 13 insertions(+), 55 deletions(-)
+
+diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
+index 23bd8f58..7bea4bff 100644
+--- a/daemon/gvfsbackendgoogle.c
++++ b/daemon/gvfsbackendgoogle.c
+@@ -907,7 +907,6 @@ build_file_info (GVfsBackendGoogle      *self,
+   gchar *escaped_name = NULL;
+   gchar *content_type = NULL;
+   gchar *copy_name = NULL;
+-  gchar *generated_copy_name = NULL;
+   gint64 atime;
+   gint64 ctime;
+   gint64 mtime;
+@@ -1025,21 +1024,7 @@ build_file_info (GVfsBackendGoogle      *self,
+   g_file_info_set_display_name (info, title);
+   g_file_info_set_edit_name (info, title);
+ 
+-  generated_copy_name = generate_copy_name (self, entry);
+-
+-  /* While copying remote Drive content to local storage, we want to
+-   * create Link-type desktop files because GLocalFile doesn't know
+-   * about shortcuts. That might change in future.
+-   */
+-  if (file_type == G_FILE_TYPE_SHORTCUT)
+-    {
+-      copy_name = g_strconcat (generated_copy_name, ".desktop", NULL);
+-    }
+-  else
+-    {
+-      copy_name = generated_copy_name;
+-      generated_copy_name = NULL;
+-    }
++  copy_name = generate_copy_name (self, entry);
+ 
+   /* Sanitize copy-name by replacing slashes with dashes. This is
+    * what nautilus does (for desktop files).
+@@ -1097,7 +1082,6 @@ build_file_info (GVfsBackendGoogle      *self,
+ 
+  out:
+   g_free (copy_name);
+-  g_free (generated_copy_name);
+   g_free (escaped_name);
+   g_free (content_type);
+   g_list_free (links);
+@@ -2249,6 +2233,8 @@ g_vfs_backend_google_open_for_read (GVfsBackend        *_self,
+   GError *error;
+   gchar *content_type = NULL;
+   gchar *entry_path = NULL;
++  GDataAuthorizationDomain *auth_domain;
++  const gchar *uri;
+ 
+   g_rec_mutex_lock (&self->mutex);
+   g_debug ("+ open_for_read: %s\n", filename);
+@@ -2278,47 +2264,19 @@ g_vfs_backend_google_open_for_read (GVfsBackend        *_self,
+       goto out;
+     }
+ 
+-  /* While copying remote Drive content to local storage, we want to
+-   * create Link-type desktop files because GLocalFile doesn't know
+-   * about shortcuts. That might change in future.
+-   */
+-  if (g_str_has_prefix (content_type, CONTENT_TYPE_PREFIX_GOOGLE))
++  if (is_native_file (entry))
+     {
+-      GDataLink *alternate;
+-      GKeyFile *file;
+-      const gchar *title;
+-      const gchar *uri;
+-      gchar *data;
+-      gsize length;
+-
+-      file = g_key_file_new ();
+-
+-      title = gdata_entry_get_title (entry);
+-      g_key_file_set_string (file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, title);
+-      g_key_file_set_string (file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TYPE, "Link");
+-
+-      alternate = gdata_entry_look_up_link (entry, GDATA_LINK_ALTERNATE);
+-      uri = gdata_link_get_uri (alternate);
+-      g_key_file_set_string (file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_URL, uri);
+-
+-      data = g_key_file_to_data (file, &length, NULL);
+-      stream = g_memory_input_stream_new_from_data (data, (gssize) length, g_free);
+-
+-      g_key_file_free (file);
++      g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE, _("File is not a regular file"));
++      goto out;
+     }
+-  else
+-    {
+-      GDataAuthorizationDomain *auth_domain;
+-      const gchar *uri;
+ 
+-      auth_domain = gdata_documents_service_get_primary_authorization_domain ();
+-      uri = gdata_entry_get_content_uri (entry);
+-      stream = gdata_download_stream_new (GDATA_SERVICE (self->service), auth_domain, uri, cancellable);
+-      if (stream == NULL)
+-        {
+-          g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_FAILED, _("Error getting data from file"));
+-          goto out;
+-        }
++  auth_domain = gdata_documents_service_get_primary_authorization_domain ();
++  uri = gdata_entry_get_content_uri (entry);
++  stream = gdata_download_stream_new (GDATA_SERVICE (self->service), auth_domain, uri, cancellable);
++  if (stream == NULL)
++    {
++      g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_FAILED, _("Error getting data from file"));
++      goto out;
+     }
+ 
+   g_object_set_data_full (G_OBJECT (stream), "g-vfs-backend-google-entry", g_object_ref (entry), g_object_unref);
+-- 
+2.36.1
+
+
+From fec7d2162966c3574226564b62c6dd252bf4706f Mon Sep 17 00:00:00 2001
+From: Ondrej Holy <oholy@redhat.com>
+Date: Mon, 23 Jul 2018 13:39:33 +0200
+Subject: [PATCH 02/14] google: Drop the (GDestroyNotify) cast
+
+"warning: function called through a non-compatible type" is printed
+by GCC 8 because of (GDestroyNotfiy) cast in g_clear_pointer, see for
+more info: https://gitlab.gnome.org/GNOME/glib/issues/1425. Let's
+drop the (GDestroyNotify) cast in order to prevent those warnings.
+The cast was not needed anyway.
+---
+ daemon/gvfsbackendgoogle.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
+index 7bea4bff..2e96cca4 100644
+--- a/daemon/gvfsbackendgoogle.c
++++ b/daemon/gvfsbackendgoogle.c
+@@ -2866,8 +2866,8 @@ g_vfs_backend_google_dispose (GObject *_self)
+   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_clear_pointer (&self->entries, g_hash_table_unref);
++  g_clear_pointer (&self->dir_entries, g_hash_table_unref);
+ 
+   G_OBJECT_CLASS (g_vfs_backend_google_parent_class)->dispose (_self);
+ }
+-- 
+2.36.1
+
+
+From 2d5fc426acb1bff385258d860ec97ff30242285d Mon Sep 17 00:00:00 2001
+From: Ondrej Holy <oholy@redhat.com>
+Date: Wed, 1 Aug 2018 13:43:16 +0200
+Subject: [PATCH 03/14] google: Move debug prints before releasing entry
+
+Debug output contains mess because id and title are const gchar *
+and are released together with GDataEntry on previous line. Let's
+just swap the lines.
+---
+ daemon/gvfsbackendgoogle.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
+index 2e96cca4..e157458b 100644
+--- a/daemon/gvfsbackendgoogle.c
++++ b/daemon/gvfsbackendgoogle.c
+@@ -510,13 +510,13 @@ remove_entry (GVfsBackendGoogle *self,
+   parent_id = get_parent_id (self, 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);
++  g_hash_table_remove (self->dir_entries, k);
+   dir_entries_key_free (k);
+ 
+   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);
++  g_hash_table_remove (self->dir_entries, k);
+   dir_entries_key_free (k);
+ 
+   for (l = self->dir_collisions; l != NULL; l = l->next)
+-- 
+2.36.1
+
+
+From b3f8db69522fdbdc965219e403da6e8e60997907 Mon Sep 17 00:00:00 2001
+From: Ondrej Holy <oholy@redhat.com>
+Date: Wed, 1 Aug 2018 13:44:59 +0200
+Subject: [PATCH 04/14] google: Remove also dir_collisions entries
+
+dir_collisions are not properly invalidated if removed entry is on this list.
+Let's remove the entry also from this list.
+---
+ daemon/gvfsbackendgoogle.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
+index e157458b..e9eaec1f 100644
+--- a/daemon/gvfsbackendgoogle.c
++++ b/daemon/gvfsbackendgoogle.c
+@@ -519,6 +519,13 @@ remove_entry (GVfsBackendGoogle *self,
+   g_hash_table_remove (self->dir_entries, k);
+   dir_entries_key_free (k);
+ 
++  l = g_list_find (self->dir_collisions, entry);
++  if (l != NULL)
++    {
++      self->dir_collisions = g_list_remove_link (self->dir_collisions, l);
++      g_object_unref (entry);
++    }
++
+   for (l = self->dir_collisions; l != NULL; l = l->next)
+     {
+       GDataEntry *colliding_entry = GDATA_ENTRY (l->data);
+-- 
+2.36.1
+
+
+From ab007b1f3215a30c3ef49492cf22e6ef1383b0fd Mon Sep 17 00:00:00 2001
+From: Ondrej Holy <oholy@redhat.com>
+Date: Tue, 31 Jul 2018 18:43:44 +0200
+Subject: [PATCH 05/14] google: Ignore entries without parents
+
+Entries without parents are not shown on the web and there isn't any
+reason to list them here. Such entries belongs to some web services
+and we have no control over them.
+---
+ daemon/gvfsbackendgoogle.c | 18 +-----------------
+ 1 file changed, 1 insertion(+), 17 deletions(-)
+
+diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
+index e9eaec1f..897df61f 100644
+--- a/daemon/gvfsbackendgoogle.c
++++ b/daemon/gvfsbackendgoogle.c
+@@ -347,14 +347,6 @@ get_parent_id (GVfsBackendGoogle *self,
+         }
+     }
+ 
+-  if (ret_val == NULL)
+-    {
+-      const gchar *root_id;
+-
+-      root_id = gdata_entry_get_id (self->root);
+-      ret_val = g_strdup (root_id);
+-    }
+-
+   g_list_free (links);
+   return ret_val;
+ }
+@@ -903,10 +895,8 @@ build_file_info (GVfsBackendGoogle      *self,
+ {
+   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;
+@@ -925,9 +915,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);
+@@ -936,9 +923,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)
+     {
+@@ -1091,7 +1076,6 @@ build_file_info (GVfsBackendGoogle      *self,
+   g_free (copy_name);
+   g_free (escaped_name);
+   g_free (content_type);
+-  g_list_free (links);
+ }
+ 
+ /* ---------------------------------------------------------------------------------------------------- */
+-- 
+2.36.1
+
+
+From 631f794a4a660f49be9d30744e5c66a1f60da4de Mon Sep 17 00:00:00 2001
+From: Ondrej Holy <oholy@redhat.com>
+Date: Tue, 31 Jul 2018 19:08:39 +0200
+Subject: [PATCH 06/14] google: Add support for files with multiple parents
+
+One entry can have multiple parents. You can create such entry on
+the web in a pretty simple way, e.g. Ctrl + Drag&Drop. Such entries
+are currently shown only on one place in the backend. Also the backend
+rely on get_parent_id() and get_entry_path() functions which are tottaly
+wrong. Let's introduce get_parent_ids() and resolve entry_path only
+from given filename.
+---
+ daemon/gvfsbackendgoogle.c | 388 ++++++++++++++++---------------------
+ 1 file changed, 168 insertions(+), 220 deletions(-)
+
+diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
+index 897df61f..7f812448 100644
+--- a/daemon/gvfsbackendgoogle.c
++++ b/daemon/gvfsbackendgoogle.c
+@@ -99,11 +99,13 @@ 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,
+                                 GError            **error);
+ 
+ /* ---------------------------------------------------------------------------------------------------- */
+@@ -160,7 +162,7 @@ entries_in_folder_equal (gconstpointer a, gconstpointer b)
+ /* ---------------------------------------------------------------------------------------------------- */
+ 
+ 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 +179,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 +195,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 +317,13 @@ 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 *ids = NULL;
+ 
+   links = gdata_entry_look_up_links (entry, GDATA_LINK_PARENT);
+   for (l = links; l != NULL; l = l->next)
+@@ -341,68 +345,13 @@ get_parent_id (GVfsBackendGoogle *self,
+           id = uri + uri_prefix_len;
+           if (id[0] != '\0')
+             {
+-              ret_val = g_strdup (uri + uri_prefix_len);
+-              break;
++              ids = g_list_prepend (ids, g_strdup (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 ids;
+ }
+ 
+ /* ---------------------------------------------------------------------------------------------------- */
+@@ -418,62 +367,66 @@ insert_entry_full (GVfsBackendGoogle *self,
+   const gchar *id;
+   const gchar *old_id;
+   const gchar *title;
+-  gchar *parent_id;
++  GList *parent_ids, *l;
+ 
+   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 = get_parent_id (self, entry);
++  parent_ids = get_parent_ids (self, entry);
++  for (l = parent_ids; l != NULL; l = l->next)
++    {
++      gchar *parent_id = l->data;
+ 
+-  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);
++      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);
+ 
+-  k = dir_entries_key_new (title, parent_id);
+-  old_entry = g_hash_table_lookup (self->dir_entries, k);
+-  if (old_entry != NULL)
+-    {
+-      old_id = gdata_entry_get_id (old_entry);
+-      if (g_strcmp0 (old_id, title) == 0)
+-        {
+-          insert_title = FALSE;
+-        }
+-      else
++      k = dir_entries_key_new (title, parent_id);
++      old_entry = g_hash_table_lookup (self->dir_entries, k);
++      if (old_entry != NULL)
+         {
+-          /* 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;
++          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;
++            }
+         }
+-    }
+ 
+-  if (insert_title)
+-    {
+-      if (old_entry != NULL && track_dir_collisions)
++      if (insert_title)
+         {
+-          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);
+-        }
++          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);
+-    }
+-  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);
++          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);
+         }
++      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);
++          dir_entries_key_free (k);
++        }
+     }
++  g_list_free_full (parent_ids, g_free);
+ 
+-  g_free (parent_id);
+   return insert_title;
+ }
+ 
+@@ -492,47 +445,50 @@ remove_entry (GVfsBackendGoogle *self,
+   GList *l;
+   const gchar *id;
+   const gchar *title;
+-  gchar *parent_id;
++  GList *parent_ids, *ll;
+ 
+   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);
+-
+-  k = dir_entries_key_new (id, parent_id);
+-  g_debug ("  remove_entry: Removed (%s, %s) -> %p\n", id, parent_id, entry);
+-  g_hash_table_remove (self->dir_entries, k);
+-  dir_entries_key_free (k);
+-
+-  k = dir_entries_key_new (title, parent_id);
+-  g_debug ("  remove_entry: Removed (%s, %s) -> %p\n", title, parent_id, entry);
+-  g_hash_table_remove (self->dir_entries, k);
+-  dir_entries_key_free (k);
+-
+-  l = g_list_find (self->dir_collisions, entry);
+-  if (l != NULL)
++  parent_ids = get_parent_ids (self, entry);
++  for (ll = parent_ids; ll != NULL; ll = ll->next)
+     {
+-      self->dir_collisions = g_list_remove_link (self->dir_collisions, l);
+-      g_object_unref (entry);
+-    }
++      gchar *parent_id = ll->data;
+ 
+-  for (l = self->dir_collisions; l != NULL; l = l->next)
+-    {
+-      GDataEntry *colliding_entry = GDATA_ENTRY (l->data);
++      k = dir_entries_key_new (id, parent_id);
++      g_debug ("  remove_entry: Removed (%s, %s) -> %p\n", id, parent_id, entry);
++      g_hash_table_remove (self->dir_entries, k);
++      dir_entries_key_free (k);
++
++      k = dir_entries_key_new (title, parent_id);
++      g_debug ("  remove_entry: Removed (%s, %s) -> %p\n", title, parent_id, entry);
++      g_hash_table_remove (self->dir_entries, k);
++      dir_entries_key_free (k);
+ 
+-      if (insert_entry_full (self, colliding_entry, FALSE))
++      l = g_list_find (self->dir_collisions, entry);
++      if (l != NULL)
+         {
+           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;
++          g_object_unref (entry);
+         }
+-    }
+ 
+-  g_free (parent_id);
++      for (l = self->dir_collisions; l != NULL; l = l->next)
++        {
++          GDataEntry *colliding_entry = GDATA_ENTRY (l->data);
++
++          if (insert_entry_full (self, colliding_entry, FALSE))
++            {
++              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;
++            }
++        }
++    }
++  g_list_free_full (parent_ids, g_free);
+ }
+ 
+ static void
+@@ -617,6 +573,7 @@ resolve_child (GVfsBackendGoogle *self,
+ static GDataEntry *
+ resolve (GVfsBackendGoogle  *self,
+          const gchar        *filename,
++         gchar             **out_path,
+          GError            **error)
+ {
+   GDataEntry *parent;
+@@ -627,11 +584,15 @@ resolve (GVfsBackendGoogle  *self,
+   if (g_strcmp0 (filename, "/") == 0)
+     {
+       ret_val = self->root;
++
++      if (out_path != NULL)
++        *out_path = g_strdup ("/");
++
+       goto out;
+     }
+ 
+   local_error = NULL;
+-  parent = resolve_dir (self, filename, &basename, &local_error);
++  parent = resolve_dir (self, filename, &basename, out_path, &local_error);
+   if (local_error != NULL)
+     {
+       g_propagate_error (error, local_error);
+@@ -645,6 +606,14 @@ resolve (GVfsBackendGoogle  *self,
+       goto out;
+     }
+ 
++  if (out_path != NULL)
++    {
++      gchar *tmp;
++      tmp = g_build_path ("/", *out_path, gdata_entry_get_id (ret_val), NULL);
++      g_free (*out_path);
++      *out_path = tmp;
++    }
++
+  out:
+   g_free (basename);
+   return ret_val;
+@@ -654,6 +623,7 @@ static GDataEntry *
+ resolve_dir (GVfsBackendGoogle  *self,
+              const gchar        *filename,
+              gchar             **out_basename,
++             gchar             **out_path,
+              GError            **error)
+ {
+   GDataEntry *parent;
+@@ -666,7 +636,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, &local_error);
+   if (local_error != NULL)
+     {
+       g_propagate_error (error, local_error);
+@@ -699,12 +669,13 @@ static GDataEntry *
+ resolve_and_rebuild (GVfsBackendGoogle  *self,
+                      const gchar        *filename,
+                      GCancellable       *cancellable,
++                     gchar             **out_path,
+                      GError            **error)
+ {
+   GDataEntry *entry;
+   GDataEntry *ret_val = NULL;
+ 
+-  entry = resolve (self, filename, NULL);
++  entry = resolve (self, filename, out_path, NULL);
+   if (entry == NULL)
+     {
+       GError *local_error;
+@@ -718,7 +689,7 @@ resolve_and_rebuild (GVfsBackendGoogle  *self,
+         }
+ 
+       local_error = NULL;
+-      entry = resolve (self, filename, &local_error);
++      entry = resolve (self, filename, out_path, &local_error);
+       if (local_error != NULL)
+         {
+           g_propagate_error (error, local_error);
+@@ -737,6 +708,7 @@ resolve_dir_and_rebuild (GVfsBackendGoogle  *self,
+                          const gchar        *filename,
+                          GCancellable       *cancellable,
+                          gchar             **out_basename,
++                         gchar             **out_path,
+                          GError            **error)
+ {
+   GDataEntry *parent;
+@@ -745,7 +717,7 @@ resolve_dir_and_rebuild (GVfsBackendGoogle  *self,
+   gchar *basename = NULL;
+ 
+   local_error = NULL;
+-  parent = resolve_dir (self, filename, &basename, &local_error);
++  parent = resolve_dir (self, filename, &basename, out_path, &local_error);
+   if (local_error != NULL)
+     {
+       if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY))
+@@ -767,7 +739,7 @@ resolve_dir_and_rebuild (GVfsBackendGoogle  *self,
+         }
+ 
+       local_error = NULL;
+-      parent = resolve_dir (self, filename, &basename, &local_error);
++      parent = resolve_dir (self, filename, &basename, out_path, &local_error);
+       if (local_error != NULL)
+         {
+           g_propagate_error (error, local_error);
+@@ -819,13 +791,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;
+   const gchar *id;
+   const gchar *title;
+-  gchar *entry_path = NULL;
+   gchar *extension = NULL;
+   gchar *extension_offset;
+   gchar *ret_val = NULL;
+@@ -833,11 +804,7 @@ 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);
+   if (parent == NULL)
+     goto out;
+ 
+@@ -859,7 +826,6 @@ generate_copy_name (GVfsBackendGoogle *self, GDataEntry *entry)
+  out:
+   if (ret_val == NULL)
+     ret_val = g_strdup (title);
+-  g_free (entry_path);
+   g_free (extension);
+   g_free (title_without_extension);
+   return ret_val;
+@@ -890,7 +856,7 @@ build_file_info (GVfsBackendGoogle      *self,
+                  GFileAttributeMatcher  *matcher,
+                  gboolean                is_symlink,
+                  const gchar            *symlink_name,
+-                 const gchar            *symlink_target,
++                 const gchar            *entry_path,
+                  GError                **error)
+ {
+   GFileType file_type;
+@@ -968,7 +934,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)
+@@ -1016,7 +982,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).
+@@ -1116,6 +1082,7 @@ g_vfs_backend_google_copy (GVfsBackend           *_self,
+   gchar *destination_basename = NULL;
+   gchar *entry_path = NULL;
+   goffset size;
++  gchar *parent_path = NULL;
+ 
+   g_rec_mutex_lock (&self->mutex);
+   g_debug ("+ copy: %s -> %s, %d\n", source, destination, flags);
+@@ -1130,12 +1097,12 @@ g_vfs_backend_google_copy (GVfsBackend           *_self,
+       goto out;
+     }
+ 
+-  source_entry = resolve (self, source, NULL);
++  source_entry = resolve (self, source, NULL, NULL);
+   if (source_entry == NULL)
+     needs_rebuild = TRUE;
+ 
+   error = NULL;
+-  destination_parent = resolve_dir (self, destination, &destination_basename, &error);
++  destination_parent = resolve_dir (self, destination, &destination_basename, &parent_path, &error);
+   if (error != NULL)
+     {
+       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY))
+@@ -1158,7 +1125,7 @@ g_vfs_backend_google_copy (GVfsBackend           *_self,
+         }
+ 
+       error = NULL;
+-      source_entry = resolve (self, source, &error);
++      source_entry = resolve (self, source, NULL, &error);
+       if (error != NULL)
+         {
+           g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -1172,7 +1139,7 @@ g_vfs_backend_google_copy (GVfsBackend           *_self,
+           destination_basename = NULL;
+ 
+           error = NULL;
+-          destination_parent = resolve_dir (self, destination, &destination_basename, &error);
++          destination_parent = resolve_dir (self, destination, &destination_basename, &parent_path, &error);
+           if (error != NULL)
+             {
+               g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -1258,7 +1225,7 @@ 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));
+@@ -1277,6 +1244,7 @@ g_vfs_backend_google_copy (GVfsBackend           *_self,
+   g_clear_object (&new_entry);
+   g_free (destination_basename);
+   g_free (entry_path);
++  g_free (parent_path);
+   g_debug ("- copy\n");
+   g_rec_mutex_unlock (&self->mutex);
+ }
+@@ -1306,7 +1274,7 @@ g_vfs_backend_google_create_dir_monitor (GVfsBackend          *_self,
+     }
+ 
+   error = NULL;
+-  entry = resolve_and_rebuild (self, filename, cancellable, &error);
++  entry = resolve_and_rebuild (self, filename, cancellable, &entry_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -1314,7 +1282,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))
+@@ -1356,7 +1323,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_and_rebuild (self, filename, cancellable, &entry_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -1364,7 +1331,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)
+@@ -1420,7 +1386,8 @@ g_vfs_backend_google_enumerate (GVfsBackend           *_self,
+   GDataEntry *entry;
+   GError *error;
+   GHashTableIter iter;
+-  gchar *entry_path = NULL;
++  char *parent_path;
++  char *id;
+ 
+   g_rec_mutex_lock (&self->mutex);
+   g_debug ("+ enumerate: %s\n", filename);
+@@ -1447,7 +1414,7 @@ g_vfs_backend_google_enumerate (GVfsBackend           *_self,
+     }
+ 
+   error = NULL;
+-  entry = resolve (self, filename, &error);
++  entry = resolve (self, filename, &parent_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -1455,9 +1422,6 @@ 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))
+     {
+       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,_("The file is not a directory"));
+@@ -1466,39 +1430,37 @@ g_vfs_backend_google_enumerate (GVfsBackend           *_self,
+ 
+   g_vfs_job_succeeded (G_VFS_JOB (job));
+ 
++  /* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
++  id = g_strdup (gdata_entry_get_id (entry));
++
+   g_hash_table_iter_init (&iter, self->entries);
+   while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &entry))
+     {
+-      gchar *path;
++      DirEntriesKey *k;
+ 
+-      path = get_entry_path (self, entry);
+-      g_debug ("  found entry: %s\n", path);
+-      if (path != NULL)
++      k = dir_entries_key_new (gdata_entry_get_id (entry), id);
++      if (g_hash_table_contains (self->dir_entries, k))
+         {
+-          gchar *parent_path;
+-
+-          parent_path = g_path_get_dirname (path);
+-          if (g_strcmp0 (entry_path, parent_path) == 0)
+-            {
+-              GFileInfo *info;
+-
+-              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_free (parent_path);
++          GFileInfo *info;
++          gchar *entry_path;
++
++          info = g_file_info_new ();
++          entry_path = g_build_path ("/", parent_path, gdata_entry_get_id (GDATA_ENTRY (entry)), NULL);
++          build_file_info (self, entry, flags, info, matcher, FALSE, NULL, entry_path, NULL);
++          g_vfs_job_enumerate_add_info (job, info);
++          g_object_unref (info);
++          g_free (entry_path);
+         }
+ 
+-      g_free (path);
++      dir_entries_key_free (k);
+     }
+ 
+   g_vfs_job_enumerate_done (job);
+ 
+  out:
+-  g_free (entry_path);
+   g_debug ("- enumerate\n");
++  g_free (parent_path);
++  g_free (id);
+   g_rec_mutex_unlock (&self->mutex);
+ }
+ 
+@@ -1532,7 +1494,7 @@ g_vfs_backend_google_make_directory (GVfsBackend          *_self,
+     }
+ 
+   error = NULL;
+-  parent = resolve_dir_and_rebuild (self, filename, cancellable, &basename, &error);
++  parent = resolve_dir_and_rebuild (self, filename, cancellable, &basename, &parent_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -1540,7 +1502,6 @@ 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);
+@@ -1574,7 +1535,7 @@ 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));
+@@ -1744,6 +1705,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);
+@@ -1777,7 +1739,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_and_rebuild (self, destination, cancellable, &destination_basename, &parent_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -1922,7 +1884,7 @@ 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)
+@@ -1960,6 +1922,7 @@ g_vfs_backend_google_push (GVfsBackend           *_self,
+   g_clear_object (&ostream);
+   g_free (destination_basename);
+   g_free (entry_path);
++  g_free (parent_path);
+   g_debug ("- push\n");
+   g_rec_mutex_unlock (&self->mutex);
+ }
+@@ -2065,7 +2028,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_and_rebuild (self, filename, cancellable, &entry_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -2073,7 +2036,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;
+@@ -2122,8 +2084,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;
+@@ -2152,7 +2114,6 @@ g_vfs_backend_google_query_info_on_read (GVfsBackend           *_self,
+   g_vfs_job_succeeded (G_VFS_JOB (job));
+ 
+  out:
+-  g_free (entry_path);
+   g_free (symlink_name);
+   g_debug ("- query_info_on_read\n");
+ }
+@@ -2170,19 +2131,17 @@ g_vfs_backend_google_query_info_on_write (GVfsBackend           *_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,
+@@ -2192,7 +2151,7 @@ g_vfs_backend_google_query_info_on_write (GVfsBackend           *_self,
+                    matcher,
+                    is_symlink,
+                    symlink_name,
+-                   entry_path,
++                   wh->entry_path,
+                    &error);
+   if (error != NULL)
+     {
+@@ -2204,7 +2163,6 @@ g_vfs_backend_google_query_info_on_write (GVfsBackend           *_self,
+   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;
+@@ -2231,7 +2189,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_and_rebuild (self, filename, cancellable, &entry_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -2239,7 +2197,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))
+@@ -2272,6 +2229,7 @@ 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));
+@@ -2419,7 +2377,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_and_rebuild (self, filename, cancellable, &entry_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -2427,7 +2385,6 @@ g_vfs_backend_google_set_display_name (GVfsBackend           *_self,
+       goto out;
+     }
+ 
+-  entry_path = get_entry_path (self, entry);
+   g_debug ("  entry path: %s\n", entry_path);
+ 
+   if (entry == self->root)
+@@ -2492,7 +2449,7 @@ g_vfs_backend_google_create (GVfsBackend         *_self,
+     }
+ 
+   error = NULL;
+-  parent = resolve_dir_and_rebuild (self, filename, cancellable, &basename, &error);
++  parent = resolve_dir_and_rebuild (self, filename, cancellable, &basename, &parent_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -2500,7 +2457,6 @@ 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);
+@@ -2538,13 +2494,13 @@ 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));
+   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));
+ 
+@@ -2602,7 +2558,7 @@ g_vfs_backend_google_replace (GVfsBackend         *_self,
+     }
+ 
+   error = NULL;
+-  parent = resolve_dir_and_rebuild (self, filename, cancellable, &basename, &error);
++  parent = resolve_dir_and_rebuild (self, filename, cancellable, &basename, &parent_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -2610,7 +2566,6 @@ 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);
+@@ -2639,7 +2594,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);
+@@ -2660,7 +2615,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
+     {
+@@ -2681,13 +2636,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));
+       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);
+@@ -2718,7 +2673,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);
+@@ -2751,9 +2705,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),
+@@ -2768,12 +2720,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");
+ }
+ 
+@@ -2789,7 +2740,6 @@ g_vfs_backend_google_close_write (GVfsBackend       *_self,
+   GDataDocumentsDocument *new_document = NULL;
+   GError *error;
+   WriteHandle *wh = (WriteHandle *) handle;
+-  gchar *entry_path = NULL;
+ 
+   g_debug ("+ close_write: %p\n", handle);
+ 
+@@ -2820,18 +2770,16 @@ 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);
++  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);
+   write_handle_free (wh);
+-  g_free (entry_path);
+   g_debug ("- close_write\n");
+ }
+ 
+-- 
+2.36.1
+
+
+From 77610a76ae671ec340d100791043f7b3de4bbaf4 Mon Sep 17 00:00:00 2001
+From: Ondrej Holy <oholy@redhat.com>
+Date: Wed, 1 Aug 2018 11:21:45 +0200
+Subject: [PATCH 07/14] google: Remove file just from concrete parent
+
+Files with multiple parents are currently removed from all parents.
+This is unexpected and may cause data loss, because it is not obvious
+that it is one file. Let's remove the file just from requested folder.
+---
+ daemon/gvfsbackendgoogle.c | 37 +++++++++++++++++++++++++++++++++----
+ 1 file changed, 33 insertions(+), 4 deletions(-)
+
+diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
+index 7f812448..45a48079 100644
+--- a/daemon/gvfsbackendgoogle.c
++++ b/daemon/gvfsbackendgoogle.c
+@@ -1314,10 +1314,12 @@ 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 *parent;
++  GDataDocumentsEntry *new_entry = NULL;
+   GError *error;
+   gchar *entry_path = NULL;
++  GList *parent_ids;
+ 
+   g_rec_mutex_lock (&self->mutex);
+   g_debug ("+ delete: %s\n", filename);
+@@ -1331,6 +1333,14 @@ g_vfs_backend_google_delete (GVfsBackend   *_self,
+       goto out;
+     }
+ 
++  parent = resolve_dir (self, filename, cancellable, NULL, NULL, &error);
++  if (error != NULL)
++    {
++      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++      g_error_free (error);
++      goto out;
++    }
++
+   g_debug ("  entry path: %s\n", entry_path);
+ 
+   if (entry == self->root)
+@@ -1339,10 +1349,26 @@ g_vfs_backend_google_delete (GVfsBackend   *_self,
+       goto out;
+     }
+ 
+-  auth_domain = gdata_documents_service_get_primary_authorization_domain ();
++  /* It has to be removed before the actual call to properly invalidate dir entries. */
++  g_object_ref (entry);
++  remove_entry (self, entry);
+ 
+   error = NULL;
+-  gdata_service_delete_entry (GDATA_SERVICE (self->service), auth_domain, entry, cancellable, &error);
++
++  /* gdata_documents_service_remove_entry_from_folder seems doesn't work for one parent. */
++  parent_ids = get_parent_ids (self, entry);
++  if (g_list_length (parent_ids) > 1)
++    {
++      new_entry = gdata_documents_service_remove_entry_from_folder (self->service, GDATA_DOCUMENTS_ENTRY (entry), GDATA_DOCUMENTS_FOLDER (parent), cancellable, &error);
++    }
++  else
++    {
++      GDataAuthorizationDomain *auth_domain;
++
++      auth_domain = gdata_documents_service_get_primary_authorization_domain ();
++      gdata_service_delete_entry (GDATA_SERVICE (self->service), auth_domain, entry, cancellable, &error);
++    }
++
+   if (error != NULL)
+     {
+       sanitize_error (&error);
+@@ -1351,11 +1377,14 @@ g_vfs_backend_google_delete (GVfsBackend   *_self,
+       goto out;
+     }
+ 
+-  remove_entry (self, entry);
++  if (new_entry)
++    insert_entry (self, GDATA_ENTRY (new_entry));
+   g_hash_table_foreach (self->monitors, emit_delete_event, entry_path);
+   g_vfs_job_succeeded (G_VFS_JOB (job));
+ 
+  out:
++  g_object_unref (entry);
++  g_clear_object (&new_entry);
+   g_free (entry_path);
+   g_debug ("- delete\n");
+   g_rec_mutex_unlock (&self->mutex);
+-- 
+2.36.1
+
+
+From 454e38f9a96505ea5ecaa6a95a8658d58caf24a1 Mon Sep 17 00:00:00 2001
+From: Ondrej Holy <oholy@redhat.com>
+Date: Mon, 30 Jul 2018 16:42:31 +0200
+Subject: [PATCH 08/14] google: Rework cache for better performance
+
+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. Let's build the cache incrementaly per folders.
+
+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. On the other hand, more requests is done
+to Drive, but the Drive quotas seem big enough.
+---
+ daemon/gvfsbackendgoogle.c | 404 +++++++++++++++----------------------
+ 1 file changed, 166 insertions(+), 238 deletions(-)
+
+diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
+index 45a48079..bf50fef6 100644
+--- a/daemon/gvfsbackendgoogle.c
++++ b/daemon/gvfsbackendgoogle.c
+@@ -55,15 +55,13 @@ struct _GVfsBackendGoogle
+   GVfsBackend parent;
+   GDataDocumentsService *service;
+   GDataEntry *root;
+-  GHashTable *entries;
+-  GHashTable *dir_entries;
++  GHashTable *entries; /* gchar *entry_id -> GDataEntry */
++  GHashTable *dir_entries; /* DirEntriesKey -> GDataEntry */
+   GHashTable *monitors;
+   GList *dir_collisions;
+   GRecMutex mutex; /* guards cache */
+   GoaClient *client;
+-  gboolean entries_stale;
+   gchar *account_identity;
+-  guint entries_stale_timeout;
+ };
+ 
+ struct _GVfsBackendGoogleClass
+@@ -104,6 +102,7 @@ typedef struct
+ 
+ static GDataEntry *resolve_dir (GVfsBackendGoogle  *self,
+                                 const gchar        *filename,
++                                GCancellable       *cancellable,
+                                 gchar             **out_basename,
+                                 gchar             **out_path,
+                                 GError            **error);
+@@ -434,12 +433,19 @@ static void
+ insert_entry (GVfsBackendGoogle *self,
+               GDataEntry        *entry)
+ {
++  gint64 *timestamp;
++
++  timestamp = g_new (gint64, 1);
++  *timestamp =  g_get_real_time ();
++  g_object_set_data_full (G_OBJECT (entry), "timestamp", timestamp, g_free);
++
+   insert_entry_full (self, entry, TRUE);
+ }
+ 
+ static void
+-remove_entry (GVfsBackendGoogle *self,
+-              GDataEntry        *entry)
++remove_entry_full (GVfsBackendGoogle *self,
++                   GDataEntry        *entry,
++                   gboolean           restore_dir_collisions)
+ {
+   DirEntriesKey *k;
+   GList *l;
+@@ -474,17 +480,20 @@ remove_entry (GVfsBackendGoogle *self,
+           g_object_unref (entry);
+         }
+ 
+-      for (l = self->dir_collisions; l != NULL; l = l->next)
++      if (restore_dir_collisions)
+         {
+-          GDataEntry *colliding_entry = GDATA_ENTRY (l->data);
+-
+-          if (insert_entry_full (self, colliding_entry, FALSE))
++          for (l = self->dir_collisions; 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;
++              GDataEntry *colliding_entry = GDATA_ENTRY (l->data);
++
++              if (insert_entry_full (self, colliding_entry, FALSE))
++                {
++                  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;
++                }
+             }
+         }
+     }
+@@ -492,16 +501,85 @@ remove_entry (GVfsBackendGoogle *self,
+ }
+ 
+ static void
+-rebuild_entries (GVfsBackendGoogle  *self,
+-                 GCancellable       *cancellable,
+-                 GError            **error)
++remove_entry (GVfsBackendGoogle *self,
++              GDataEntry        *entry)
++{
++  remove_entry_full (self, entry, TRUE);
++}
++
++static void
++remove_dir (GVfsBackendGoogle *self,
++            GDataEntry        *parent)
++{
++  GHashTableIter iter;
++  GDataEntry *entry;
++  gchar *parent_id;
++  GList *l;
++
++  /* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
++  parent_id = g_strdup (gdata_entry_get_id (parent));
++
++  g_hash_table_iter_init (&iter, self->entries);
++  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &entry))
++    {
++      DirEntriesKey *k;
++
++      k = dir_entries_key_new (gdata_entry_get_id (entry), parent_id);
++      if (g_hash_table_contains (self->dir_entries, k))
++        {
++          g_object_ref (entry);
++          g_hash_table_iter_remove (&iter);
++          remove_entry_full (self, entry, FALSE);
++          g_object_unref (entry);
++        }
++
++      dir_entries_key_free (k);
++    }
++
++  for (l = self->dir_collisions; l != NULL; l = l->next)
++  {
++    GDataEntry *colliding_entry = GDATA_ENTRY (l->data);
++
++    if (insert_entry_full (self, colliding_entry, FALSE))
++      {
++        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;
++      }
++  }
++
++  g_free (parent_id);
++}
++
++static gboolean
++is_entry_valid (GDataEntry *entry)
++{
++  gint64 *timestamp;
++
++  timestamp = g_object_get_data (G_OBJECT (entry), "timestamp");
++  return (g_get_real_time () - *timestamp < REBUILD_ENTRIES_TIMEOUT * G_USEC_PER_SEC);
++}
++
++static void
++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;
++
++  parent_id = gdata_entry_get_id (parent);
+ 
+-  query = gdata_documents_query_new_with_limits (NULL, 1, MAX_RESULTS);
++  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)
+     {
+@@ -515,18 +593,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;
++          remove_dir (self, parent);
+ 
+           succeeded_once = TRUE;
+         }
+@@ -545,8 +618,6 @@ rebuild_entries (GVfsBackendGoogle  *self,
+       g_clear_object (&feed);
+     }
+ 
+-  self->entries_stale = FALSE;
+-
+  out:
+   g_clear_object (&feed);
+   g_clear_object (&query);
+@@ -555,24 +626,48 @@ 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;
++  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);
++  // TODO: Rebuild only if dir listing is not valid
++  if (entry == NULL || !is_entry_valid (entry))
++    {
++      rebuild_dir (self, parent, cancellable, &local_error);
++      if (local_error != NULL)
++        {
++          g_propagate_error (error, local_error);
++          goto out;
++        }
++
++      entry = g_hash_table_lookup (self->dir_entries, k);
++      if (entry == NULL)
++        {
++          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("No such file or directory"));
++          goto out;
++        }
++    }
++
++ out:
+   dir_entries_key_free (k);
++
+   return entry;
+ }
+ 
+ static GDataEntry *
+ resolve (GVfsBackendGoogle  *self,
+          const gchar        *filename,
++         GCancellable       *cancellable,
+          gchar             **out_path,
+          GError            **error)
+ {
+@@ -581,6 +676,8 @@ resolve (GVfsBackendGoogle  *self,
+   GError *local_error;
+   gchar *basename = NULL;
+ 
++  g_assert (filename && filename[0] == '/');
++
+   if (g_strcmp0 (filename, "/") == 0)
+     {
+       ret_val = self->root;
+@@ -592,17 +689,17 @@ resolve (GVfsBackendGoogle  *self,
+     }
+ 
+   local_error = NULL;
+-  parent = resolve_dir (self, filename, &basename, out_path, &local_error);
++  parent = resolve_dir (self, filename, cancellable, &basename, out_path, &local_error);
+   if (local_error != NULL)
+     {
+       g_propagate_error (error, local_error);
+       goto out;
+     }
+ 
+-  ret_val = resolve_child (self, parent, basename);
++  ret_val = resolve_child (self, parent, basename, cancellable, &local_error);
+   if (ret_val == 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;
+     }
+ 
+@@ -622,6 +719,7 @@ resolve (GVfsBackendGoogle  *self,
+ static GDataEntry *
+ resolve_dir (GVfsBackendGoogle  *self,
+              const gchar        *filename,
++             GCancellable       *cancellable,
+              gchar             **out_basename,
+              gchar             **out_path,
+              GError            **error)
+@@ -636,7 +734,7 @@ resolve_dir (GVfsBackendGoogle  *self,
+   parent_path = g_path_get_dirname (filename);
+ 
+   local_error = NULL;
+-  parent = resolve (self, parent_path, out_path, &local_error);
++  parent = resolve (self, parent_path, cancellable, out_path, &local_error);
+   if (local_error != NULL)
+     {
+       g_propagate_error (error, local_error);
+@@ -665,103 +763,6 @@ resolve_dir (GVfsBackendGoogle  *self,
+ 
+ /* ---------------------------------------------------------------------------------------------------- */
+ 
+-static GDataEntry *
+-resolve_and_rebuild (GVfsBackendGoogle  *self,
+-                     const gchar        *filename,
+-                     GCancellable       *cancellable,
+-                     gchar             **out_path,
+-                     GError            **error)
+-{
+-  GDataEntry *entry;
+-  GDataEntry *ret_val = NULL;
+-
+-  entry = resolve (self, filename, out_path, 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, out_path, &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,
+-                         gchar             **out_path,
+-                         GError            **error)
+-{
+-  GDataEntry *parent;
+-  GDataEntry *ret_val = NULL;
+-  GError *local_error;
+-  gchar *basename = NULL;
+-
+-  local_error = NULL;
+-  parent = resolve_dir (self, filename, &basename, out_path, &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, out_path, &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)
+ {
+@@ -804,11 +805,11 @@ generate_copy_name (GVfsBackendGoogle *self, GDataEntry *entry, const gchar *ent
+ 
+   title = gdata_entry_get_title (entry);
+ 
+-  parent = resolve_dir (self, entry_path, NULL, 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;
+ 
+@@ -1074,8 +1075,6 @@ g_vfs_backend_google_copy (GVfsBackend           *_self,
+   GDataEntry *source_entry;
+   GError *error;
+   GType source_entry_type;
+-  gboolean needs_rebuild = FALSE;
+-  gboolean destination_not_directory = FALSE;
+   const gchar *etag;
+   const gchar *id;
+   const gchar *summary;
+@@ -1097,56 +1096,21 @@ g_vfs_backend_google_copy (GVfsBackend           *_self,
+       goto out;
+     }
+ 
+-  source_entry = resolve (self, source, NULL, NULL);
+-  if (source_entry == NULL)
+-    needs_rebuild = TRUE;
+-
+   error = NULL;
+-  destination_parent = resolve_dir (self, destination, &destination_basename, &parent_path, &error);
++  source_entry = resolve (self, source, cancellable, NULL, &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)
++  destination_parent = resolve_dir (self, destination, cancellable, &destination_basename, &parent_path, &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, NULL, &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, &parent_path, &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);
+@@ -1165,13 +1129,7 @@ g_vfs_backend_google_copy (GVfsBackend           *_self,
+       goto out;
+     }
+ 
+-  if (destination_not_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;
+-    }
+-
+-  existing_entry = resolve_child (self, destination_parent, destination_basename);
++  existing_entry = resolve_child (self, destination_parent, destination_basename, cancellable, NULL);
+   if (existing_entry != NULL)
+     {
+       if (flags & G_FILE_COPY_OVERWRITE)
+@@ -1274,7 +1232,7 @@ g_vfs_backend_google_create_dir_monitor (GVfsBackend          *_self,
+     }
+ 
+   error = NULL;
+-  entry = resolve_and_rebuild (self, filename, cancellable, &entry_path, &error);
++  entry = resolve (self, filename, cancellable, &entry_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -1325,7 +1283,7 @@ g_vfs_backend_google_delete (GVfsBackend   *_self,
+   g_debug ("+ delete: %s\n", filename);
+ 
+   error = NULL;
+-  entry = resolve_and_rebuild (self, filename, cancellable, &entry_path, &error);
++  entry = resolve (self, filename, cancellable, &entry_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -1392,17 +1350,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,
+@@ -1421,29 +1368,8 @@ g_vfs_backend_google_enumerate (GVfsBackend           *_self,
+   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, &parent_path, &error);
++  entry = resolve (self, filename, cancellable, &parent_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -1457,6 +1383,15 @@ g_vfs_backend_google_enumerate (GVfsBackend           *_self,
+       goto out;
+     }
+ 
++  // TODO: Rebuild only if dir listing is not valid
++  rebuild_dir (self, entry, cancellable, &error);
++  if (error != NULL)
++    {
++      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++      g_error_free (error);
++      goto out;
++    }
++
+   g_vfs_job_succeeded (G_VFS_JOB (job));
+ 
+   /* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
+@@ -1523,7 +1458,7 @@ g_vfs_backend_google_make_directory (GVfsBackend          *_self,
+     }
+ 
+   error = NULL;
+-  parent = resolve_dir_and_rebuild (self, filename, cancellable, &basename, &parent_path, &error);
++  parent = resolve_dir (self, filename, cancellable, &basename, &parent_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -1539,7 +1474,7 @@ g_vfs_backend_google_make_directory (GVfsBackend          *_self,
+   else
+     summary = gdata_entry_get_summary (summary_entry);
+ 
+-  existing_entry = resolve_child (self, parent, basename);
++  existing_entry = resolve_child (self, parent, basename, cancellable, NULL);
+   if (existing_entry != NULL)
+     {
+       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_EXISTS, _("Target file already exists"));
+@@ -1768,7 +1703,7 @@ g_vfs_backend_google_push (GVfsBackend           *_self,
+     }
+ 
+   error = NULL;
+-  destination_parent = resolve_dir_and_rebuild (self, destination, cancellable, &destination_basename, &parent_path, &error);
++  destination_parent = resolve_dir (self, destination, cancellable, &destination_basename, &parent_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -1776,7 +1711,7 @@ 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, NULL);
+   if (existing_entry != NULL)
+     {
+       if (flags & G_FILE_COPY_OVERWRITE)
+@@ -2057,7 +1992,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, &entry_path, &error);
++  entry = resolve (self, filename, cancellable, &entry_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -2218,7 +2153,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, &entry_path, &error);
++  entry = resolve (self, filename, cancellable, &entry_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -2406,7 +2341,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, &entry_path, &error);
++  entry = resolve (self, filename, cancellable, &entry_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -2478,7 +2413,7 @@ g_vfs_backend_google_create (GVfsBackend         *_self,
+     }
+ 
+   error = NULL;
+-  parent = resolve_dir_and_rebuild (self, filename, cancellable, &basename, &parent_path, &error);
++  parent = resolve_dir (self, filename, cancellable, &basename, &parent_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -2488,7 +2423,7 @@ g_vfs_backend_google_create (GVfsBackend         *_self,
+ 
+   g_debug ("  parent path: %s\n", parent_path);
+ 
+-  existing_entry = resolve_child (self, parent, basename);
++  existing_entry = resolve_child (self, parent, basename, cancellable, NULL);
+   if (existing_entry != NULL)
+     {
+       if (flags & G_FILE_CREATE_REPLACE_DESTINATION)
+@@ -2587,7 +2522,7 @@ g_vfs_backend_google_replace (GVfsBackend         *_self,
+     }
+ 
+   error = NULL;
+-  parent = resolve_dir_and_rebuild (self, filename, cancellable, &basename, &parent_path, &error);
++  parent = resolve_dir (self, filename, cancellable, &basename, &parent_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -2597,7 +2532,7 @@ g_vfs_backend_google_replace (GVfsBackend         *_self,
+ 
+   g_debug ("  parent path: %s\n", parent_path);
+ 
+-  existing_entry = resolve_child (self, parent, basename);
++  existing_entry = resolve_child (self, parent, basename, cancellable, NULL);
+   if (existing_entry != NULL)
+     {
+       if (GDATA_IS_DOCUMENTS_FOLDER (existing_entry))
+@@ -2819,12 +2754,6 @@ 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);
+@@ -2896,5 +2825,4 @@ g_vfs_backend_google_init (GVfsBackendGoogle *self)
+                                              g_object_unref);
+   self->monitors = g_hash_table_new (NULL, NULL);
+   g_rec_mutex_init (&self->mutex);
+-  self->entries_stale = TRUE;
+ }
+-- 
+2.36.1
+
+
+From 8c8bc7a8c37bbf1493b14cd76b21b077136e3260 Mon Sep 17 00:00:00 2001
+From: Ondrej Holy <oholy@redhat.com>
+Date: Thu, 16 Aug 2018 15:00:52 +0200
+Subject: [PATCH 09/14] google: Use cache for enumeration also
+
+The reworked cache hasn't been used for enumeration results and also
+for missing files checks, which always caused rebuilding cache. Let's
+save timestamps also for enumerations and use it to prevent redundant
+cache rebuilds.
+---
+ daemon/gvfsbackendgoogle.c | 62 ++++++++++++++++++++++++++++----------
+ 1 file changed, 46 insertions(+), 16 deletions(-)
+
+diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
+index bf50fef6..4eb8dbfe 100644
+--- a/daemon/gvfsbackendgoogle.c
++++ b/daemon/gvfsbackendgoogle.c
+@@ -57,6 +57,7 @@ struct _GVfsBackendGoogle
+   GDataEntry *root;
+   GHashTable *entries; /* gchar *entry_id -> GDataEntry */
+   GHashTable *dir_entries; /* DirEntriesKey -> GDataEntry */
++  GHashTable *dir_timestamps; /* gchar *entry_id -> gint64 *timestamp */
+   GHashTable *monitors;
+   GList *dir_collisions;
+   GRecMutex mutex; /* guards cache */
+@@ -463,6 +464,8 @@ remove_entry_full (GVfsBackendGoogle *self,
+     {
+       gchar *parent_id = ll->data;
+ 
++      g_hash_table_remove (self->dir_timestamps, parent_id);
++
+       k = dir_entries_key_new (id, parent_id);
+       g_debug ("  remove_entry: Removed (%s, %s) -> %p\n", id, parent_id, entry);
+       g_hash_table_remove (self->dir_entries, k);
+@@ -519,6 +522,8 @@ remove_dir (GVfsBackendGoogle *self,
+   /* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
+   parent_id = g_strdup (gdata_entry_get_id (parent));
+ 
++  g_hash_table_remove (self->dir_timestamps, parent_id);
++
+   g_hash_table_iter_init (&iter, self->entries);
+   while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &entry))
+     {
+@@ -562,6 +567,18 @@ is_entry_valid (GDataEntry *entry)
+   return (g_get_real_time () - *timestamp < REBUILD_ENTRIES_TIMEOUT * G_USEC_PER_SEC);
+ }
+ 
++static gboolean
++is_dir_listing_valid (GVfsBackendGoogle *self, GDataEntry *parent)
++{
++  gint64 *timestamp;
++
++  timestamp = g_hash_table_lookup (self->dir_timestamps, gdata_entry_get_id (parent));
++  if (timestamp != NULL)
++    return (g_get_real_time () - *timestamp < REBUILD_ENTRIES_TIMEOUT * G_USEC_PER_SEC);
++
++  return FALSE;
++}
++
+ static void
+ rebuild_dir (GVfsBackendGoogle  *self,
+              GDataEntry         *parent,
+@@ -572,9 +589,10 @@ rebuild_dir (GVfsBackendGoogle  *self,
+   GDataDocumentsQuery *query = NULL;
+   gboolean succeeded_once = FALSE;
+   gchar *search;
+-  const gchar *parent_id;
++  gchar *parent_id;
+ 
+-  parent_id = gdata_entry_get_id (parent);
++  /* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
++  parent_id = g_strdup (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);
+@@ -599,8 +617,14 @@ rebuild_dir (GVfsBackendGoogle  *self,
+ 
+       if (!succeeded_once)
+         {
++          gint64 *timestamp;
++
+           remove_dir (self, parent);
+ 
++          timestamp = g_new (gint64, 1);
++          *timestamp = g_get_real_time ();
++          g_hash_table_insert (self->dir_timestamps, g_strdup (parent_id), timestamp);
++
+           succeeded_once = TRUE;
+         }
+ 
+@@ -621,6 +645,7 @@ rebuild_dir (GVfsBackendGoogle  *self,
+  out:
+   g_clear_object (&feed);
+   g_clear_object (&query);
++  g_free (parent_id);
+ }
+ 
+ /* ---------------------------------------------------------------------------------------------------- */
+@@ -640,8 +665,8 @@ resolve_child (GVfsBackendGoogle  *self,
+   parent_id = gdata_entry_get_id (parent);
+   k = dir_entries_key_new (basename, parent_id);
+   entry = g_hash_table_lookup (self->dir_entries, k);
+-  // TODO: Rebuild only if dir listing is not valid
+-  if (entry == NULL || !is_entry_valid (entry))
++  if ((entry == NULL && !is_dir_listing_valid (self, parent)) ||
++      (entry != NULL && !is_entry_valid (entry)))
+     {
+       rebuild_dir (self, parent, cancellable, &local_error);
+       if (local_error != NULL)
+@@ -651,11 +676,12 @@ resolve_child (GVfsBackendGoogle  *self,
+         }
+ 
+       entry = g_hash_table_lookup (self->dir_entries, k);
+-      if (entry == NULL)
+-        {
+-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("No such file or directory"));
+-          goto out;
+-        }
++    }
++
++  if (entry == NULL)
++    {
++      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("No such file or directory"));
++      goto out;
+     }
+ 
+  out:
+@@ -1363,7 +1389,7 @@ g_vfs_backend_google_enumerate (GVfsBackend           *_self,
+   GError *error;
+   GHashTableIter iter;
+   char *parent_path;
+-  char *id;
++  char *id = NULL;
+ 
+   g_rec_mutex_lock (&self->mutex);
+   g_debug ("+ enumerate: %s\n", filename);
+@@ -1383,13 +1409,15 @@ g_vfs_backend_google_enumerate (GVfsBackend           *_self,
+       goto out;
+     }
+ 
+-  // TODO: Rebuild only if dir listing is not valid
+-  rebuild_dir (self, entry, cancellable, &error);
+-  if (error != NULL)
++  if (!is_dir_listing_valid (self, entry))
+     {
+-      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+-      g_error_free (error);
+-      goto out;
++      rebuild_dir (self, entry, cancellable, &error);
++      if (error != NULL)
++        {
++          g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
++          g_error_free (error);
++          goto out;
++        }
+     }
+ 
+   g_vfs_job_succeeded (G_VFS_JOB (job));
+@@ -2765,6 +2793,7 @@ g_vfs_backend_google_dispose (GObject *_self)
+   g_clear_object (&self->client);
+   g_clear_pointer (&self->entries, g_hash_table_unref);
+   g_clear_pointer (&self->dir_entries, g_hash_table_unref);
++  g_clear_pointer (&self->dir_timestamps, g_hash_table_unref);
+ 
+   G_OBJECT_CLASS (g_vfs_backend_google_parent_class)->dispose (_self);
+ }
+@@ -2823,6 +2852,7 @@ g_vfs_backend_google_init (GVfsBackendGoogle *self)
+                                              entries_in_folder_equal,
+                                              dir_entries_key_free,
+                                              g_object_unref);
++  self->dir_timestamps = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+   self->monitors = g_hash_table_new (NULL, NULL);
+   g_rec_mutex_init (&self->mutex);
+ }
+-- 
+2.36.1
+
+
+From 79e1878e1a43e32c0dbce585ea377d0222f85f27 Mon Sep 17 00:00:00 2001
+From: Ondrej Holy <oholy@redhat.com>
+Date: Wed, 1 Aug 2018 16:17:42 +0200
+Subject: [PATCH 10/14] google: Handle child of volatile also as volatile
+
+Files in volatile folder should be also marked as volatile.
+
+Volatile handling is a bit simplified as a part of this patch also.
+---
+ daemon/gvfsbackendgoogle.c | 58 +++++++++++++-------------------------
+ 1 file changed, 19 insertions(+), 39 deletions(-)
+
+diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
+index 4eb8dbfe..3823cfe4 100644
+--- a/daemon/gvfsbackendgoogle.c
++++ b/daemon/gvfsbackendgoogle.c
+@@ -881,8 +881,7 @@ build_file_info (GVfsBackendGoogle      *self,
+                  GFileQueryInfoFlags     flags,
+                  GFileInfo              *info,
+                  GFileAttributeMatcher  *matcher,
+-                 gboolean                is_symlink,
+-                 const gchar            *symlink_name,
++                 const gchar            *filename,
+                  const gchar            *entry_path,
+                  GError                **error)
+ {
+@@ -890,6 +889,7 @@ build_file_info (GVfsBackendGoogle      *self,
+   GList *authors;
+   gboolean is_folder = FALSE;
+   gboolean is_root = FALSE;
++  gboolean is_symlink = FALSE;
+   const gchar *etag;
+   const gchar *id;
+   const gchar *name;
+@@ -897,6 +897,7 @@ build_file_info (GVfsBackendGoogle      *self,
+   gchar *escaped_name = NULL;
+   gchar *content_type = NULL;
+   gchar *copy_name = NULL;
++  gchar *symlink_name = NULL;
+   gint64 atime;
+   gint64 ctime;
+   gint64 mtime;
+@@ -908,6 +909,12 @@ build_file_info (GVfsBackendGoogle      *self,
+   if (entry == self->root)
+     is_root = TRUE;
+ 
++  if (filename != NULL && g_strcmp0 (entry_path, filename) != 0) /* volatile */
++    {
++      is_symlink = TRUE;
++      symlink_name = g_path_get_basename (filename);
++    }
++
+   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);
+@@ -1066,6 +1073,7 @@ build_file_info (GVfsBackendGoogle      *self,
+     }
+ 
+  out:
++  g_free (symlink_name);
+   g_free (copy_name);
+   g_free (escaped_name);
+   g_free (content_type);
+@@ -1435,10 +1443,12 @@ g_vfs_backend_google_enumerate (GVfsBackend           *_self,
+         {
+           GFileInfo *info;
+           gchar *entry_path;
++          gchar *child_filename;
+ 
+           info = g_file_info_new ();
+           entry_path = g_build_path ("/", parent_path, gdata_entry_get_id (GDATA_ENTRY (entry)), NULL);
+-          build_file_info (self, entry, flags, info, matcher, FALSE, NULL, entry_path, NULL);
++          child_filename = g_build_filename (filename, gdata_entry_get_id (GDATA_ENTRY (entry)), NULL);
++          build_file_info (self, entry, flags, info, matcher, child_filename, entry_path, NULL);
+           g_vfs_job_enumerate_add_info (job, info);
+           g_object_unref (info);
+           g_free (entry_path);
+@@ -2012,9 +2022,7 @@ g_vfs_backend_google_query_info (GVfsBackend           *_self,
+   GCancellable *cancellable = G_VFS_JOB (job)->cancellable;
+   GDataEntry *entry;
+   GError *error;
+-  gboolean is_symlink = FALSE;
+   gchar *entry_path = NULL;
+-  gchar *symlink_name = NULL;
+ 
+   g_rec_mutex_lock (&self->mutex);
+   g_debug ("+ query_info: %s, %d\n", filename, flags);
+@@ -2028,16 +2036,10 @@ g_vfs_backend_google_query_info (GVfsBackend           *_self,
+       goto out;
+     }
+ 
+-  if (g_strcmp0 (entry_path, filename) != 0) /* volatile */
+-    {
+-      is_symlink = TRUE;
+-      symlink_name = g_path_get_basename (filename);
+-    }
+-
+-  g_debug ("  entry path: %s (%d)\n", entry_path, is_symlink);
++  g_debug ("  entry path: %s\n", entry_path);
+ 
+   error = NULL;
+-  build_file_info (self, entry, flags, info, matcher, is_symlink, symlink_name, entry_path, &error);
++  build_file_info (self, entry, flags, info, matcher, filename, entry_path, &error);
+   if (error != NULL)
+     {
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+@@ -2049,7 +2051,6 @@ g_vfs_backend_google_query_info (GVfsBackend           *_self,
+ 
+  out:
+   g_free (entry_path);
+-  g_free (symlink_name);
+   g_debug ("- query_info\n");
+   g_rec_mutex_unlock (&self->mutex);
+ }
+@@ -2067,10 +2068,8 @@ g_vfs_backend_google_query_info_on_read (GVfsBackend           *_self,
+   GDataEntry *entry;
+   GError *error;
+   GInputStream *stream = G_INPUT_STREAM (handle);
+-  gboolean is_symlink = FALSE;
+   const gchar *filename;
+   gchar *entry_path = NULL;
+-  gchar *symlink_name = NULL;
+ 
+   g_debug ("+ query_info_on_read: %p\n", handle);
+ 
+@@ -2078,13 +2077,7 @@ g_vfs_backend_google_query_info_on_read (GVfsBackend           *_self,
+   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");
+ 
+-  if (g_strcmp0 (entry_path, filename) != 0) /* volatile */
+-    {
+-      is_symlink = TRUE;
+-      symlink_name = g_path_get_basename (filename);
+-    }
+-
+-  g_debug ("  entry path: %s (%d)\n", entry_path, is_symlink);
++  g_debug ("  entry path: %s\n", entry_path);
+ 
+   error = NULL;
+   build_file_info (self,
+@@ -2092,8 +2085,7 @@ g_vfs_backend_google_query_info_on_read (GVfsBackend           *_self,
+                    G_FILE_QUERY_INFO_NONE,
+                    info,
+                    matcher,
+-                   is_symlink,
+-                   symlink_name,
++                   filename,
+                    entry_path,
+                    &error);
+   if (error != NULL)
+@@ -2106,7 +2098,6 @@ g_vfs_backend_google_query_info_on_read (GVfsBackend           *_self,
+   g_vfs_job_succeeded (G_VFS_JOB (job));
+ 
+  out:
+-  g_free (symlink_name);
+   g_debug ("- query_info_on_read\n");
+ }
+ 
+@@ -2122,18 +2113,9 @@ g_vfs_backend_google_query_info_on_write (GVfsBackend           *_self,
+   GVfsBackendGoogle *self = G_VFS_BACKEND_GOOGLE (_self);
+   GError *error;
+   WriteHandle *wh = (WriteHandle *) handle;
+-  gboolean is_symlink = FALSE;
+-  gchar *symlink_name = NULL;
+ 
+   g_debug ("+ query_info_on_write: %p\n", handle);
+-
+-  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", wh->entry_path, is_symlink);
++  g_debug ("  entry path: %s\n", wh->entry_path);
+ 
+   error = NULL;
+   build_file_info (self,
+@@ -2141,8 +2123,7 @@ g_vfs_backend_google_query_info_on_write (GVfsBackend           *_self,
+                    G_FILE_QUERY_INFO_NONE,
+                    info,
+                    matcher,
+-                   is_symlink,
+-                   symlink_name,
++                   wh->filename,
+                    wh->entry_path,
+                    &error);
+   if (error != NULL)
+@@ -2155,7 +2136,6 @@ g_vfs_backend_google_query_info_on_write (GVfsBackend           *_self,
+   g_vfs_job_succeeded (G_VFS_JOB (job));
+ 
+  out:
+-  g_free (symlink_name);
+   g_debug ("- query_info_on_write\n");
+   return TRUE;
+ }
+-- 
+2.36.1
+
+
+From 46444e0aacd07a152e3d7958dcc263195cb69433 Mon Sep 17 00:00:00 2001
+From: Ondrej Holy <oholy@redhat.com>
+Date: Fri, 12 Jul 2019 10:35:47 +0200
+Subject: [PATCH 11/14] google: Do not enumerate volatile entries if title
+ matches id
+
+Currently, the volatile entry is enumerated if its title matches id
+of another entry. But we don't want to enumerate volatile entries
+to not confuse our clients. Let's add simple check to prevent this.
+---
+ daemon/gvfsbackendgoogle.c | 23 +++++++++++++++++++----
+ 1 file changed, 19 insertions(+), 4 deletions(-)
+
+diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
+index 3823cfe4..60d6142f 100644
+--- a/daemon/gvfsbackendgoogle.c
++++ b/daemon/gvfsbackendgoogle.c
+@@ -1437,23 +1437,38 @@ g_vfs_backend_google_enumerate (GVfsBackend           *_self,
+   while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &entry))
+     {
+       DirEntriesKey *k;
++      GDataEntry *child;
++      gchar *child_id;
+ 
+-      k = dir_entries_key_new (gdata_entry_get_id (entry), id);
+-      if (g_hash_table_contains (self->dir_entries, k))
++      /* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
++      child_id = g_strdup (gdata_entry_get_id (entry));
++
++      k = dir_entries_key_new (child_id, id);
++      if ((child = g_hash_table_lookup (self->dir_entries, k)) != NULL)
+         {
+           GFileInfo *info;
+           gchar *entry_path;
+           gchar *child_filename;
+ 
++          /* Be sure that we are not matching title of volatile file */
++          if (g_strcmp0 (child_id, gdata_entry_get_id (child)) != 0)
++            {
++              g_debug ("Skipping %s as it is volatile path for %s\n", child_id, gdata_entry_get_id (child));
++              g_free (child_id);
++              dir_entries_key_free (k);
++              continue;
++            }
++
+           info = g_file_info_new ();
+-          entry_path = g_build_path ("/", parent_path, gdata_entry_get_id (GDATA_ENTRY (entry)), NULL);
+-          child_filename = g_build_filename (filename, gdata_entry_get_id (GDATA_ENTRY (entry)), NULL);
++          entry_path = g_build_path ("/", parent_path, child_id, NULL);
++          child_filename = g_build_filename (filename, child_id, NULL);
+           build_file_info (self, entry, flags, info, matcher, child_filename, entry_path, NULL);
+           g_vfs_job_enumerate_add_info (job, info);
+           g_object_unref (info);
+           g_free (entry_path);
+         }
+ 
++      g_free (child_id);
+       dir_entries_key_free (k);
+     }
+ 
+-- 
+2.36.1
+
+
+From 7a6419a1a8702516f82002c4a003eb6468ac5e7b Mon Sep 17 00:00:00 2001
+From: Mayank Sharma <mayank8019@gmail.com>
+Date: Thu, 18 Jul 2019 01:18:43 +0530
+Subject: [PATCH 12/14] google: Fix issue with stale entries remaining after
+ rename operation
+
+Currently, whenever we perform a rename operation, we set the `entry`'s
+title to new display name, but at the time of removal of this entry from
+cache, we still use the newer title. Hence, each time rename is done, a
+single stale entry remains. This commit fixes the issue by reverting
+back the title to the original display name of `entry` and then
+performing a removal from cache.
+
+Fixes: https://gitlab.gnome.org/GNOME/gvfs/issues/410
+---
+ daemon/gvfsbackendgoogle.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
+index 60d6142f..1147e8f1 100644
+--- a/daemon/gvfsbackendgoogle.c
++++ b/daemon/gvfsbackendgoogle.c
+@@ -2380,6 +2380,11 @@ g_vfs_backend_google_set_display_name (GVfsBackend           *_self,
+       goto out;
+     }
+ 
++  /* The internal ref count has to be increased before removing the entry since
++   * remove_entry_full calls g_object_unref(). */
++  g_object_ref (entry);
++  remove_entry (self, entry);
++
+   gdata_entry_set_title (entry, display_name);
+   auth_domain = gdata_documents_service_get_primary_authorization_domain ();
+ 
+@@ -2390,14 +2395,15 @@ g_vfs_backend_google_set_display_name (GVfsBackend           *_self,
+       sanitize_error (&error);
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+       g_error_free (error);
++      g_object_unref (entry);
+       goto out;
+     }
+ 
+-  remove_entry (self, entry);
+   insert_entry (self, new_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));
++  g_object_unref (entry);
+ 
+  out:
+   g_clear_object (&new_entry);
+-- 
+2.36.1
+
+
+From 00d1e161f47adedf2f05d3574beb05d06c76b4c0 Mon Sep 17 00:00:00 2001
+From: Mayank Sharma <mayank8019@gmail.com>
+Date: Tue, 23 Jul 2019 09:51:41 +0530
+Subject: [PATCH 13/14] google: Fix crashes when deleting if the file isn't
+ found
+
+Currently in delete operation, if the entry gets resolved but parent
+resolution fails, the jump to `out` label (while handling error) will
+cause the existing entry's ref_count to decrease by 1 (since `out`
+label calls g_object_unref on entry).
+
+We fix the issue by removing g_object_unref from `out` label and
+suitably unreffing the entry.
+---
+ daemon/gvfsbackendgoogle.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
+index 1147e8f1..230b89eb 100644
+--- a/daemon/gvfsbackendgoogle.c
++++ b/daemon/gvfsbackendgoogle.c
+@@ -1366,6 +1366,7 @@ g_vfs_backend_google_delete (GVfsBackend   *_self,
+       sanitize_error (&error);
+       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+       g_error_free (error);
++      g_object_unref (entry);
+       goto out;
+     }
+ 
+@@ -1373,9 +1374,9 @@ g_vfs_backend_google_delete (GVfsBackend   *_self,
+     insert_entry (self, GDATA_ENTRY (new_entry));
+   g_hash_table_foreach (self->monitors, emit_delete_event, entry_path);
+   g_vfs_job_succeeded (G_VFS_JOB (job));
++  g_object_unref (entry);
+ 
+  out:
+-  g_object_unref (entry);
+   g_clear_object (&new_entry);
+   g_free (entry_path);
+   g_debug ("- delete\n");
+-- 
+2.36.1
+
+
+From d842aa6c729866343d7a3b0514a6574c01be30ed Mon Sep 17 00:00:00 2001
+From: Ondrej Holy <oholy@redhat.com>
+Date: Fri, 29 Jan 2021 14:06:16 +0100
+Subject: [PATCH 14/14] google: Increase number of results in batches for
+ better performance
+
+Currently, only 50 results are returned in one batch, which means that
+multiple network requests are needed for folders with a lot files, which
+makes it slow. I am not sure there was some reason for this limitation
+earlier (e.g. before the cache rework), however, I don't see any
+rationals for it currently. Let's increase this to its maximum for
+better performance.
+---
+ daemon/gvfsbackendgoogle.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
+index 230b89eb..f97c038c 100644
+--- a/daemon/gvfsbackendgoogle.c
++++ b/daemon/gvfsbackendgoogle.c
+@@ -79,8 +79,6 @@ G_DEFINE_TYPE(GVfsBackendGoogle, g_vfs_backend_google, G_VFS_TYPE_BACKEND)
+ 
+ #define CONTENT_TYPE_PREFIX_GOOGLE "application/vnd.google-apps"
+ 
+-#define MAX_RESULTS 50
+-
+ #define REBUILD_ENTRIES_TIMEOUT 60 /* s */
+ 
+ #define URI_PREFIX "https://www.googleapis.com/drive/v2/files/"
+@@ -595,7 +593,7 @@ rebuild_dir (GVfsBackendGoogle  *self,
+   parent_id = g_strdup (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);
++  query = gdata_documents_query_new_with_limits (search, 1, G_MAXUINT);
+   gdata_documents_query_set_show_folders (query, TRUE);
+   g_free (search);
+ 
+-- 
+2.36.1
+
diff --git a/SOURCES/smb-Ignore-EINVAL-for-kerberos-login.patch b/SOURCES/smb-Ignore-EINVAL-for-kerberos-login.patch
new file mode 100644
index 0000000..aa42bd9
--- /dev/null
+++ b/SOURCES/smb-Ignore-EINVAL-for-kerberos-login.patch
@@ -0,0 +1,43 @@
+diff --git a/daemon/gvfsbackendsmb.c b/daemon/gvfsbackendsmb.c
+index 33d1a209..776b67bc 100644
+--- a/daemon/gvfsbackendsmb.c
++++ b/daemon/gvfsbackendsmb.c
+@@ -513,7 +513,13 @@ do_mount (GVfsBackend *backend,
+       if (res == 0)
+         break;
+ 
+-      if (op_backend->mount_cancelled || (errsv != EACCES && errsv != EPERM))
++      if (errsv == EINVAL && op_backend->mount_try == 0 && op_backend->user == NULL)
++        {
++          /* EINVAL is "expected" when kerberos/ccache is misconfigured, see:
++           * https://gitlab.gnome.org/GNOME/gvfs/-/issues/611
++           */
++        }
++      else if (op_backend->mount_cancelled || (errsv != EACCES && errsv != EPERM))
+         {
+           g_debug ("do_mount - (errno != EPERM && errno != EACCES), cancelled = %d, breaking\n", op_backend->mount_cancelled);
+           break;
+diff --git a/daemon/gvfsbackendsmbbrowse.c b/daemon/gvfsbackendsmbbrowse.c
+index 57bae9db..7e8facfb 100644
+--- a/daemon/gvfsbackendsmbbrowse.c
++++ b/daemon/gvfsbackendsmbbrowse.c
+@@ -967,8 +967,14 @@ do_mount (GVfsBackend *backend,
+              uri, op_backend->mount_try, dir, op_backend->mount_cancelled,
+              errsv, g_strerror (errsv));
+ 
+-      if (dir == NULL && 
+-          (op_backend->mount_cancelled || (errsv != EPERM && errsv != EACCES)))
++      if (errsv == EINVAL && op_backend->mount_try == 0 && op_backend->user == NULL)
++        {
++          /* EINVAL is "expected" when kerberos is misconfigured, see:
++           * https://gitlab.gnome.org/GNOME/gvfs/-/issues/611
++           */
++        }
++      else if (dir == NULL &&
++               (op_backend->mount_cancelled || (errsv != EPERM && errsv != EACCES)))
+         {
+           g_debug ("do_mount - (errno != EPERM && errno != EACCES), cancelled = %d, breaking\n", op_backend->mount_cancelled);
+ 	  break;
+-- 
+2.35.1
+
diff --git a/SOURCES/smb-Rework-anonymous-handling-to-avoid-EINVAL.patch b/SOURCES/smb-Rework-anonymous-handling-to-avoid-EINVAL.patch
new file mode 100644
index 0000000..3486aa7
--- /dev/null
+++ b/SOURCES/smb-Rework-anonymous-handling-to-avoid-EINVAL.patch
@@ -0,0 +1,57 @@
+diff --git a/daemon/gvfsbackendsmb.c b/daemon/gvfsbackendsmb.c
+index 776b67bc..a1e3eacd 100644
+--- a/daemon/gvfsbackendsmb.c
++++ b/daemon/gvfsbackendsmb.c
+@@ -80,7 +80,6 @@ struct _GVfsBackendSmb
+   int mount_try;
+   gboolean mount_try_again;
+   gboolean mount_cancelled;
+-  gboolean use_anonymous;
+ 	
+   gboolean password_in_keyring;
+   GPasswordSave password_save;
+@@ -215,13 +214,6 @@ auth_callback (SMBCCTX *context,
+       backend->mount_try_again = TRUE;
+       g_debug ("auth_callback - kerberos pass\n");
+     }
+-  else if (backend->use_anonymous)
+-    {
+-      /* Try again if anonymous login fails */
+-      backend->use_anonymous = FALSE;
+-      backend->mount_try_again = TRUE;
+-      g_debug ("auth_callback - anonymous login pass\n");
+-    }
+   else
+     {
+       gboolean in_keyring = FALSE;
+@@ -304,10 +296,13 @@ auth_callback (SMBCCTX *context,
+       /* Try again if this fails */
+       backend->mount_try_again = TRUE;
+ 
++      smbc_setOptionNoAutoAnonymousLogin (backend->smb_context,
++                                          !anonymous);
++
+       if (anonymous)
+         {
+-          backend->use_anonymous = TRUE;
+           backend->password_save = FALSE;
++          g_debug ("auth_callback - anonymous enabled\n");
+         }
+       else
+         {
+@@ -535,12 +530,6 @@ do_mount (GVfsBackend *backend,
+           smbc_setOptionFallbackAfterKerberos (op_backend->smb_context, 1);
+         }
+ 
+-      /* If the AskPassword reply requested anonymous login, enable the
+-       * anonymous fallback and try again.
+-       */
+-      smbc_setOptionNoAutoAnonymousLogin (op_backend->smb_context,
+-                                          !op_backend->use_anonymous);
+-
+       op_backend->mount_try ++;
+     }
+   while (op_backend->mount_try_again);
+-- 
+2.36.0
+
diff --git a/SOURCES/smb-Use-O_RDWR-to-fix-fstat-when-writing.patch b/SOURCES/smb-Use-O_RDWR-to-fix-fstat-when-writing.patch
new file mode 100644
index 0000000..2142250
--- /dev/null
+++ b/SOURCES/smb-Use-O_RDWR-to-fix-fstat-when-writing.patch
@@ -0,0 +1,67 @@
+From 3f6f906c7c7b28dc30edb98200b6e13e1a513bb4 Mon Sep 17 00:00:00 2001
+From: Ondrej Holy <oholy@redhat.com>
+Date: Wed, 9 May 2018 12:54:59 +0200
+Subject: [PATCH] smb: Use O_RDWR to fix fstat when writing
+
+fstat fails with EINVAL on Windows servers if O_WRONLY is used to open
+(though it works properly on SAMBA servers). O_RDWR is needed to make
+it work. This causes issues when copying files over gvfsd-fuse among
+others.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=795805
+---
+ daemon/gvfsbackendsmb.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/daemon/gvfsbackendsmb.c b/daemon/gvfsbackendsmb.c
+index d4944197..9571fa0d 100644
+--- a/daemon/gvfsbackendsmb.c
++++ b/daemon/gvfsbackendsmb.c
+@@ -808,7 +808,7 @@ do_create (GVfsBackend *backend,
+   smbc_open = smbc_getFunctionOpen (op_backend->smb_context);
+   errno = 0;
+   file = smbc_open (op_backend->smb_context, uri,
+-		    O_CREAT|O_WRONLY|O_EXCL, 0666);
++                    O_CREAT|O_RDWR|O_EXCL, 0666);
+   g_free (uri);
+ 
+   if (file == NULL)
+@@ -850,7 +850,7 @@ do_append_to (GVfsBackend *backend,
+   smbc_open = smbc_getFunctionOpen (op_backend->smb_context);
+   errno = 0;
+   file = smbc_open (op_backend->smb_context, uri,
+-					O_CREAT|O_WRONLY|O_APPEND, 0666);
++                    O_CREAT|O_RDWR|O_APPEND, 0666);
+   g_free (uri);
+ 
+   if (file == NULL)
+@@ -916,7 +916,7 @@ open_tmpfile (GVfsBackendSmb *backend,
+     smbc_open = smbc_getFunctionOpen (backend->smb_context);
+     errno = 0;
+     file = smbc_open (backend->smb_context, tmp_uri,
+-		      O_CREAT|O_WRONLY|O_EXCL, 0666);
++                      O_CREAT|O_RDWR|O_EXCL, 0666);
+   } while (file == NULL && errno == EEXIST);
+ 
+   g_free (dir_uri);
+@@ -1040,7 +1040,7 @@ do_replace (GVfsBackend *backend,
+   
+   errno = 0;
+   file = smbc_open (op_backend->smb_context, uri,
+-		    O_CREAT|O_WRONLY|O_EXCL, 0);
++                    O_CREAT|O_RDWR|O_EXCL, 0);
+   if (file == NULL && errno != EEXIST)
+     {
+       int errsv = fixup_open_errno (errno);
+@@ -1110,7 +1110,7 @@ do_replace (GVfsBackend *backend,
+ 	  
+ 	  errno = 0;
+ 	  file = smbc_open (op_backend->smb_context, uri,
+-			    O_CREAT|O_WRONLY|O_TRUNC, 0);
++                            O_CREAT|O_RDWR|O_TRUNC, 0);
+ 	  if (file == NULL)
+ 	    {
+               int errsv = fixup_open_errno (errno);
+-- 
+2.35.3
+
diff --git a/SPECS/gvfs.spec b/SPECS/gvfs.spec
index d80d0ce..c727337 100644
--- a/SPECS/gvfs.spec
+++ b/SPECS/gvfs.spec
@@ -25,7 +25,7 @@
 
 Name: gvfs
 Version: 1.36.2
-Release: 11%{?dist}
+Release: 14%{?dist}
 Summary: Backends for the gio framework in GLib
 
 License: GPLv3 and LGPLv2+ and BSD and MPLv2.0
@@ -66,6 +66,16 @@ Patch10: smb-Improve-enumeration-performance.patch
 # https://bugzilla.redhat.com/show_bug.cgi?id=1889411
 Patch11: goa-Add-support-for-certificate-prompts.patch
 
+# https://bugzilla.redhat.com/show_bug.cgi?id=2095712
+Patch12: smb-Ignore-EINVAL-for-kerberos-login.patch
+Patch13: smb-Rework-anonymous-handling-to-avoid-EINVAL.patch
+
+# https://bugzilla.redhat.com/show_bug.cgi?id=2080478
+Patch14: smb-Use-O_RDWR-to-fix-fstat-when-writing.patch
+
+# https://bugzilla.redhat.com/show_bug.cgi?id=2083481
+Patch15: google-performance-fixes.patch
+
 BuildRequires: pkgconfig
 BuildRequires: pkgconfig(glib-2.0) >= %{glib2_version}
 BuildRequires: pkgconfig(dbus-glib-1)
@@ -352,9 +362,11 @@ killall -USR1 gvfsd >&/dev/null || :
 %{_libexecdir}/gvfsd-recent
 %{_mandir}/man1/gvfsd.1*
 %{_mandir}/man1/gvfsd-metadata.1*
+%if ! 0%{?flatpak}
 %{_userunitdir}/gvfs-daemon.service
 %{_userunitdir}/gvfs-metadata.service
 %{_userunitdir}/gvfs-udisks2-volume-monitor.service
+%endif
 
 %files client -f gvfs.lang
 %{!?_licensedir:%global license %%doc}
@@ -376,7 +388,9 @@ killall -USR1 gvfsd >&/dev/null || :
 %files fuse
 %{_libexecdir}/gvfsd-fuse
 %{_mandir}/man1/gvfsd-fuse.1*
+%if ! 0%{?flatpak}
 %{_tmpfilesdir}/gvfsd-fuse-tmpfiles.conf
+%endif
 
 %files smb
 %{_libexecdir}/gvfsd-smb
@@ -396,7 +410,9 @@ killall -USR1 gvfsd >&/dev/null || :
 %{_libexecdir}/gvfs-gphoto2-volume-monitor
 %{_datadir}/dbus-1/services/org.gtk.vfs.GPhoto2VolumeMonitor.service
 %{_datadir}/gvfs/remote-volume-monitors/gphoto2.monitor
+%if ! 0%{?flatpak}
 %{_userunitdir}/gvfs-gphoto2-volume-monitor.service
+%endif
 
 %ifnarch s390 s390x
 %files afc
@@ -405,8 +421,10 @@ killall -USR1 gvfsd >&/dev/null || :
 %{_libexecdir}/gvfs-afc-volume-monitor
 %{_datadir}/dbus-1/services/org.gtk.vfs.AfcVolumeMonitor.service
 %{_datadir}/gvfs/remote-volume-monitors/afc.monitor
+%if ! 0%{?flatpak}
 %{_userunitdir}/gvfs-afc-volume-monitor.service
 %endif
+%endif
 
 %files afp
 %{_libexecdir}/gvfsd-afp
@@ -420,7 +438,9 @@ killall -USR1 gvfsd >&/dev/null || :
 %{_libexecdir}/gvfs-mtp-volume-monitor
 %{_datadir}/dbus-1/services/org.gtk.vfs.MTPVolumeMonitor.service
 %{_datadir}/gvfs/remote-volume-monitors/mtp.monitor
+%if ! 0%{?flatpak}
 %{_userunitdir}/gvfs-mtp-volume-monitor.service
+%endif
 
 %if ! 0%{?rhel}
 %files nfs
@@ -436,7 +456,9 @@ killall -USR1 gvfsd >&/dev/null || :
 %{_datadir}/gvfs/remote-volume-monitors/goa.monitor
 %{_datadir}/gvfs/mounts/google.mount
 %{_libexecdir}/gvfsd-google
+%if ! 0%{?flatpak}
 %{_userunitdir}/gvfs-goa-volume-monitor.service
+%endif
 
 %files tests
 %dir %{_libexecdir}/installed-tests
@@ -444,6 +466,15 @@ killall -USR1 gvfsd >&/dev/null || :
 %{_datadir}/installed-tests
 
 %changelog
+* Thu Jun 16 2022 Ondrej Holy <oholy@redhat.com> - 1.36.2-14
+- Backport performance fixes for Google backend (#2083481)
+
+* Tue Jun 14 2022 Ondrej Holy <oholy@redhat.com> - 1.36.2-13
+- Use O_RDWR to fix fstat when writing on SMB share (#2080478)
+
+* Tue Jun 14 2022 Ondrej Holy <oholy@redhat.com> - 1.36.2-12
+- Ignore EINVAL for kerberos login to fix SMB mounting (#2095712)
+
 * Tue Nov 03 2020 Ondrej Holy <oholy@redhat.com> - 1.36.2-11
 - Add support for certificates prompts for GOA mounts (rhbz#1889411)