[gvfs/wip/rishi/goa: 7/8] Introduce dir_entries and fix a bug in copy



commit 1104ade401cf1afe2c4cd1adadb1cfc3b61c25b1
Author: Debarshi Ray <debarshir gnome org>
Date:   Thu Aug 27 20:28:50 2015 +0200

    Introduce dir_entries and fix a bug in copy

 daemon/gvfsbackendgoogle.c |  327 +++++++++++++++++++++++++++++++++++++-------
 1 files changed, 275 insertions(+), 52 deletions(-)
---
diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
index a5ca53e..0e6c89c 100644
--- a/daemon/gvfsbackendgoogle.c
+++ b/daemon/gvfsbackendgoogle.c
@@ -55,6 +55,7 @@ struct _GVfsBackendGoogle
   GDataDocumentsService *service;
   GDataEntry *root;
   GHashTable *entries;
+  GHashTable *dir_entries;
   GHashTable *lookaside;
   GHashTable *monitors;
   GRecMutex mutex;
@@ -86,6 +87,69 @@ G_DEFINE_TYPE(GVfsBackendGoogle, g_vfs_backend_google, G_VFS_TYPE_BACKEND)
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+typedef struct
+{
+  gchar *title_or_id;
+  gchar *parent_id;
+} DirEntriesKey;
+
+static GDataEntry *resolve_dir (GVfsBackendGoogle  *self,
+                                const gchar        *filename,
+                                gchar             **out_basename);
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+DirEntriesKey *
+dir_entries_key_new (const gchar *title_or_id, const gchar *parent_id)
+{
+  DirEntriesKey *k;
+
+  k = g_slice_new (DirEntriesKey);
+  k->title_or_id = g_strdup (title_or_id);
+  k->parent_id = g_strdup (parent_id);
+  return k;
+}
+
+static void
+dir_entries_key_free (gpointer data)
+{
+  DirEntriesKey *k = (DirEntriesKey *) data;
+
+  if (k == NULL)
+    return;
+
+  g_free (k->title_or_id);
+  g_free (k->parent_id);
+  g_slice_free (DirEntriesKey, k);
+}
+
+guint
+entries_in_folder_hash (gconstpointer key)
+{
+  DirEntriesKey *k = (DirEntriesKey *) key;
+  guint hash1;
+  guint hash2;
+
+  hash1 = g_str_hash (k->title_or_id);
+  hash2 = g_str_hash (k->parent_id);
+  return hash1 ^ hash2;
+}
+
+gboolean
+entries_in_folder_equal (gconstpointer a, gconstpointer b)
+{
+  DirEntriesKey *k_a = (DirEntriesKey *) a;
+  DirEntriesKey *k_b = (DirEntriesKey *) b;
+
+  if (g_strcmp0 (k_a->title_or_id, k_b->title_or_id) == 0 &&
+      g_strcmp0 (k_a->parent_id, k_b->parent_id) == 0)
+    return TRUE;
+
+  return FALSE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static void
 insert_lookaside (GVfsBackendGoogle *self,
                   const gchar       *parent_id,
@@ -397,15 +461,16 @@ emit_delete_event (gpointer monitor,
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
-is_folder_or_root (const gchar *filename,
-                   GDataEntry  *entry,
-                   gboolean    *out_is_folder,
-                   gboolean    *out_is_root)
+is_folder_or_root (GVfsBackendGoogle *self,
+                   const gchar       *filename,
+                   GDataEntry        *entry,
+                   gboolean          *out_is_folder,
+                   gboolean          *out_is_root)
 {
   gboolean is_folder = FALSE;
   gboolean is_root = FALSE;
 
-  if (g_strcmp0 (filename, "/") == 0 && entry == NULL)
+  if (g_strcmp0 (filename, "/") == 0 && (entry == NULL || entry == self->root))
     {
       is_folder = TRUE;
       is_root = TRUE;
@@ -475,7 +540,7 @@ build_file_info (GVfsBackendGoogle      *self,
   gint64 mtime;
   gsize i;
 
-  is_folder_or_root (filename, entry, &is_folder, &is_root);
+  is_folder_or_root (self, filename, entry, &is_folder, &is_root);
   if (entry == NULL && !is_root)
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("No such file or directory"));
@@ -745,6 +810,86 @@ get_parent_basename (const gchar *filename)
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
+insert_entry (GVfsBackendGoogle *self,
+              GDataEntry        *entry)
+{
+  DirEntriesKey *k;
+  GDataEntry *old_entry;
+  gboolean insert_title = TRUE;
+  const gchar *id;
+  const gchar *title;
+  gchar *parent_id;
+
+  id = gdata_entry_get_id (entry);
+  g_hash_table_insert (self->entries, g_strdup (id), g_object_ref (entry));
+
+  parent_id = get_parent_id (entry);
+  if (parent_id == NULL)
+    return;
+
+  k = dir_entries_key_new (id, parent_id);
+  g_hash_table_insert (self->dir_entries, k, g_object_ref (entry));
+
+  title = gdata_entry_get_title (entry);
+  k = dir_entries_key_new (title, parent_id);
+  old_entry = g_hash_table_lookup (self->dir_entries, k);
+  if (old_entry != NULL)
+    {
+      const gchar *old_id;
+
+      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)
+    g_hash_table_insert (self->dir_entries, k, g_object_ref (entry));
+  else
+    dir_entries_key_free (k);
+
+  g_free (parent_id);
+}
+
+static void
+remove_entry (GVfsBackendGoogle *self,
+              GDataEntry        *entry)
+{
+  DirEntriesKey *k;
+  const gchar *id;
+  const gchar *title;
+  gchar *parent_id;
+
+  id = gdata_entry_get_id (entry);
+  g_hash_table_remove (self->entries, id);
+
+  parent_id = get_parent_id (entry);
+  if (parent_id == NULL)
+    return;
+
+  k = dir_entries_key_new (id, parent_id);
+  g_hash_table_remove (self->dir_entries, k);
+  dir_entries_key_free (k);
+
+  title = gdata_entry_get_title (entry);
+  k = dir_entries_key_new (title, parent_id);
+  g_hash_table_remove (self->dir_entries, k);
+  dir_entries_key_free (k);
+
+  g_free (parent_id);
+}
+
+static void
 rebuild_entries (GVfsBackendGoogle  *self,
                  GCancellable       *cancellable,
                  GError            **error)
@@ -782,6 +927,7 @@ rebuild_entries (GVfsBackendGoogle  *self,
       if (!succeeded_once)
         {
           g_hash_table_remove_all (self->entries);
+          g_hash_table_remove_all (self->dir_entries);
           succeeded_once = TRUE;
         }
 
@@ -792,10 +938,7 @@ rebuild_entries (GVfsBackendGoogle  *self,
       for (l = entries; l != NULL; l = l->next)
         {
           GDataEntry *entry = GDATA_ENTRY (l->data);
-          const gchar *id;
-
-          id = gdata_entry_get_id (entry);
-          g_hash_table_insert (self->entries, g_strdup (id), g_object_ref (entry));
+          insert_entry (self, entry);
         }
 
       gdata_query_next_page (GDATA_QUERY (query));
@@ -811,6 +954,89 @@ rebuild_entries (GVfsBackendGoogle  *self,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static GDataEntry *
+resolve_child (GVfsBackendGoogle *self,
+               GDataEntry        *parent,
+               const gchar       *basename)
+{
+  DirEntriesKey *k;
+  GDataEntry *entry;
+  const gchar *parent_id;
+
+  parent_id = gdata_entry_get_id (parent);
+  k = dir_entries_key_new (basename, parent_id);
+  entry = g_hash_table_lookup (self->dir_entries, k);
+  dir_entries_key_free (k);
+  return entry;
+}
+
+static GDataEntry *
+resolve (GVfsBackendGoogle  *self,
+         const gchar        *filename,
+         GError            **error)
+{
+  GDataEntry *parent;
+  GDataEntry *ret_val = NULL;
+  gchar *basename = NULL;
+
+  if (g_strcmp0 (filename, "/") == 0)
+    {
+      ret_val = self->root;
+      goto out;
+    }
+
+  parent = resolve_dir (self, filename, &basename);
+  if (parent == NULL)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, _("The file is not a directory"));
+      goto out;
+    }
+
+  ret_val = resolve_child (self, parent, basename);
+  if (ret_val == NULL)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("No such file or directory"));
+      goto out;
+    }
+
+ out:
+  g_free (basename);
+  return ret_val;
+}
+
+static GDataEntry *
+resolve_dir (GVfsBackendGoogle  *self,
+             const gchar        *filename,
+             gchar             **out_basename)
+{
+  GDataEntry *parent;
+  gboolean is_folder;
+  gchar *basename = NULL;
+  gchar *parent_path = NULL;
+
+  basename = g_path_get_basename (filename);
+  if (out_basename != NULL)
+    {
+      *out_basename = basename;
+      basename = NULL;
+    }
+
+  parent_path = g_path_get_dirname (filename);
+  parent = resolve (self, parent_path, NULL);
+  if (parent != NULL)
+    {
+      is_folder_or_root (self, parent_path, parent, &is_folder, NULL);
+      if (!is_folder)
+        parent = NULL;
+    }
+
+  g_free (basename);
+  g_free (parent_path);
+  return parent;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static void
 remove_monitor_weak_ref (gpointer monitor,
                          gpointer unused,
@@ -834,14 +1060,13 @@ create_document (GVfsBackendGoogle  *self,
   GDataDocumentsFolder *parent;
   GError *local_error;
   gboolean is_root;
-  const gchar *id;
   gchar *parent_id = NULL;
   gchar *parent_path = NULL;
   gchar *path = NULL;
   gchar *title = NULL;
 
   parent_path = g_path_get_dirname (unescaped_filename);
-  is_folder_or_root (parent_path, NULL, NULL, &is_root);
+  is_folder_or_root (self, parent_path, NULL, NULL, &is_root);
   if (is_root)
     {
       parent = GDATA_DOCUMENTS_FOLDER (self->root);
@@ -881,8 +1106,7 @@ create_document (GVfsBackendGoogle  *self,
   if (local_error != NULL)
     goto out;
 
-  id = gdata_entry_get_id (GDATA_ENTRY (new_document));
-  g_hash_table_insert (self->entries, g_strdup (id), g_object_ref (new_document));
+  insert_entry (self, GDATA_ENTRY (new_document));
 
   path = get_entry_path (self, GDATA_ENTRY (new_document));
   if (path != NULL)
@@ -924,9 +1148,8 @@ g_vfs_backend_google_copy (GVfsBackend           *_self,
   GDataEntry *source_entry;
   GError *error;
   gboolean is_folder;
-  gboolean is_root;
+  gboolean destination_is_root;
   gboolean is_volatile_source;
-  const gchar *id;
   const gchar *real_destination_parent_path;
   const gchar *real_parent_path;
   const gchar *real_source;
@@ -996,8 +1219,8 @@ g_vfs_backend_google_copy (GVfsBackend           *_self,
       destination_parent_path = g_strdup (real_destination_parent_path);
     }
 
-  is_folder_or_root (destination_parent_path, NULL, NULL, &is_root);
-  if (is_root)
+  is_folder_or_root (self, destination_parent_path, NULL, NULL, &destination_is_root);
+  if (destination_is_root)
     {
       destination_parent = GDATA_DOCUMENTS_FOLDER (self->root);
     }
@@ -1020,7 +1243,17 @@ g_vfs_backend_google_copy (GVfsBackend           *_self,
     }
 
   source_entry = g_hash_table_lookup (self->entries, source_id);
-  is_folder_or_root (source, source_entry, &is_folder, NULL);
+
+  if (!destination_is_root && destination_parent == NULL)
+    destination_parent = g_hash_table_lookup (self->entries, destination_parent_id);
+
+  if (source_entry == NULL || destination_parent == NULL)
+    {
+      g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("No such file or directory"));
+      goto out;
+    }
+
+  is_folder_or_root (self, source, source_entry, &is_folder, NULL);
   if (is_folder)
     {
       g_vfs_job_failed (G_VFS_JOB (job),
@@ -1030,13 +1263,6 @@ g_vfs_backend_google_copy (GVfsBackend           *_self,
       goto out;
     }
 
-  destination_parent = g_hash_table_lookup (self->entries, destination_parent_id);
-  if (source_entry == NULL || destination_parent == NULL)
-    {
-      g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("No such file or directory"));
-      goto out;
-    }
-
   error = NULL;
   new_entry = gdata_documents_service_add_entry_to_folder (self->service,
                                                            GDATA_DOCUMENTS_ENTRY (source_entry),
@@ -1050,8 +1276,7 @@ g_vfs_backend_google_copy (GVfsBackend           *_self,
       goto out;
     }
 
-  id = gdata_entry_get_id (GDATA_ENTRY (new_entry));
-  g_hash_table_insert (self->entries, g_strdup (id), g_object_ref (new_entry));
+  insert_entry (self, GDATA_ENTRY (new_entry));
 
   path = get_entry_path (self, GDATA_ENTRY (new_entry));
   if (path != NULL)
@@ -1123,7 +1348,7 @@ g_vfs_backend_google_create_dir_monitor (GVfsBackend          *_self,
 
   id = g_path_get_basename (filename);
   entry = g_hash_table_lookup (self->entries, id);
-  is_folder_or_root (filename, entry, NULL, &is_root);
+  is_folder_or_root (self, filename, entry, NULL, &is_root);
 
   if (entry == NULL && !is_root)
     {
@@ -1144,7 +1369,7 @@ g_vfs_backend_google_create_dir_monitor (GVfsBackend          *_self,
         }
     }
 
-  is_folder_or_root (filename, entry, &is_folder, NULL);
+  is_folder_or_root (self, filename, entry, &is_folder, NULL);
   if (!is_folder)
     {
       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, _("The file is not a 
directory"));
@@ -1204,7 +1429,7 @@ g_vfs_backend_google_delete (GVfsBackend   *_self,
       filename = real_filename;
     }
 
-  is_folder_or_root (filename, NULL, NULL, &is_root);
+  is_folder_or_root (self, filename, NULL, NULL, &is_root);
   if (is_root)
     {
       g_vfs_job_failed (G_VFS_JOB (job),
@@ -1248,7 +1473,7 @@ g_vfs_backend_google_delete (GVfsBackend   *_self,
       goto out;
     }
 
-  g_hash_table_remove (self->entries, id);
+  remove_entry (self, entry);
   if (path != NULL)
     g_hash_table_foreach (self->monitors, emit_delete_event, (gpointer) path);
 
@@ -1384,7 +1609,6 @@ g_vfs_backend_google_make_directory (GVfsBackend          *_self,
   gboolean is_display_name;
   gboolean is_root;
   gboolean needs_rebuild = FALSE;
-  const gchar *id;
   const gchar *real_parent_path;
   gchar *parent_id = NULL;
   gchar *parent_path = NULL;
@@ -1403,7 +1627,7 @@ g_vfs_backend_google_make_directory (GVfsBackend          *_self,
       goto out;
     }
 
-  is_folder_or_root (unescaped_filename, NULL, NULL, &is_root);
+  is_folder_or_root (self, unescaped_filename, NULL, NULL, &is_root);
   if (is_root)
     {
       g_vfs_job_failed (G_VFS_JOB (job),
@@ -1414,7 +1638,7 @@ g_vfs_backend_google_make_directory (GVfsBackend          *_self,
     }
 
   parent_path = g_path_get_dirname (unescaped_filename);
-  is_folder_or_root (parent_path, NULL, NULL, &is_root);
+  is_folder_or_root (self, parent_path, NULL, NULL, &is_root);
   if (is_root)
     {
       parent = GDATA_DOCUMENTS_FOLDER (self->root);
@@ -1489,8 +1713,7 @@ g_vfs_backend_google_make_directory (GVfsBackend          *_self,
       goto out;
     }
 
-  id = gdata_entry_get_id (GDATA_ENTRY (new_entry));
-  g_hash_table_insert (self->entries, g_strdup (id), g_object_ref (new_entry));
+  insert_entry (self, GDATA_ENTRY (new_entry));
 
   path = get_entry_path (self, GDATA_ENTRY (new_entry));
   if (path != NULL)
@@ -1672,7 +1895,6 @@ g_vfs_backend_google_push (GVfsBackend           *_self,
   gboolean is_root;
   gboolean needs_overwrite = FALSE;
   const gchar *content_type;
-  const gchar *id;
   const gchar *title;
   gchar *basename = NULL;
   gchar *parent_id = NULL;
@@ -1827,7 +2049,7 @@ g_vfs_backend_google_push (GVfsBackend           *_self,
     {
       parent_path = g_path_get_dirname (unescaped_destination);
 
-      is_folder_or_root (parent_path, NULL, NULL, &is_root);
+      is_folder_or_root (self, parent_path, NULL, NULL, &is_root);
       if (!is_root)
         {
           parent_id = g_path_get_basename (parent_path);
@@ -1899,8 +2121,7 @@ g_vfs_backend_google_push (GVfsBackend           *_self,
       goto out;
     }
 
-  id = gdata_entry_get_id (GDATA_ENTRY (new_document));
-  g_hash_table_insert (self->entries, g_strdup (id), g_object_ref (new_document));
+  insert_entry (self, GDATA_ENTRY (new_document));
 
   path = get_entry_path (self, GDATA_ENTRY (new_document));
   if (path != NULL)
@@ -2016,7 +2237,7 @@ g_vfs_backend_google_query_info (GVfsBackend           *_self,
 
   id = g_path_get_basename (filename);
   entry = g_hash_table_lookup (self->entries, id);
-  is_folder_or_root (filename, entry, NULL, &is_root);
+  is_folder_or_root (self, filename, entry, NULL, &is_root);
 
   if (entry == NULL && !is_root)
     {
@@ -2175,7 +2396,7 @@ g_vfs_backend_google_open_for_read (GVfsBackend        *_self,
   id = g_path_get_basename (filename);
   entry = g_hash_table_lookup (self->entries, id);
 
-  is_folder_or_root (filename, entry, NULL, &is_root);
+  is_folder_or_root (self, filename, entry, NULL, &is_root);
   if (is_root)
     {
       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY, _("Can't open directory"));
@@ -2201,7 +2422,7 @@ g_vfs_backend_google_open_for_read (GVfsBackend        *_self,
         }
     }
 
-  is_folder_or_root (filename, entry, &is_folder, NULL);
+  is_folder_or_root (self, filename, entry, &is_folder, NULL);
   if (is_folder)
     {
       g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY, _("Can't open directory"));
@@ -2404,7 +2625,6 @@ g_vfs_backend_google_set_display_name (GVfsBackend           *_self,
   GError *error;
   gboolean is_root;
   gboolean is_volatile;
-  const gchar *new_id;
   gchar *escaped_filename = NULL;
   gchar *id = NULL;
   gchar *unescaped_filename = NULL;
@@ -2428,7 +2648,7 @@ g_vfs_backend_google_set_display_name (GVfsBackend           *_self,
       filename = real_filename;
     }
 
-  is_folder_or_root (filename, NULL, NULL, &is_root);
+  is_folder_or_root (self, filename, NULL, NULL, &is_root);
   if (is_root)
     {
       g_vfs_job_failed (G_VFS_JOB (job),
@@ -2472,10 +2692,8 @@ g_vfs_backend_google_set_display_name (GVfsBackend           *_self,
       goto out;
     }
 
-  g_hash_table_remove (self->entries, id);
-
-  new_id = gdata_entry_get_id (new_entry);
-  g_hash_table_insert (self->entries, g_strdup (new_id), g_object_ref (new_entry));
+  remove_entry (self, entry);
+  insert_entry (self, new_entry);
 
   g_hash_table_foreach (self->monitors, emit_attribute_changed_event, (gpointer) filename);
 
@@ -2522,7 +2740,7 @@ g_vfs_backend_google_create (GVfsBackend         *_self,
       goto out;
     }
 
-  is_folder_or_root (unescaped_filename, NULL, NULL, &is_root);
+  is_folder_or_root (self, unescaped_filename, NULL, NULL, &is_root);
   if (is_root)
     {
       g_vfs_job_failed (G_VFS_JOB (job),
@@ -2581,7 +2799,7 @@ g_vfs_backend_google_replace (GVfsBackend         *_self,
       goto out;
     }
 
-  is_folder_or_root (unescaped_filename, NULL, NULL, &is_root);
+  is_folder_or_root (self, unescaped_filename, NULL, NULL, &is_root);
   if (is_root)
     {
       g_vfs_job_failed (G_VFS_JOB (job),
@@ -2826,6 +3044,7 @@ g_vfs_backend_google_dispose (GObject *_self)
   g_clear_object (&self->root);
   g_clear_object (&self->client);
   g_clear_pointer (&self->entries, (GDestroyNotify) g_hash_table_unref);
+  g_clear_pointer (&self->dir_entries, (GDestroyNotify) g_hash_table_unref);
 
   G_OBJECT_CLASS (g_vfs_backend_google_parent_class)->dispose (_self);
 }
@@ -2884,6 +3103,10 @@ g_vfs_backend_google_init (GVfsBackendGoogle *self)
   g_vfs_backend_set_prefered_filename_encoding (G_VFS_BACKEND (self), "google-drive");
 
   self->entries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+  self->dir_entries = g_hash_table_new_full (entries_in_folder_hash,
+                                             entries_in_folder_equal,
+                                             dir_entries_key_free,
+                                             g_object_unref);
   self->lookaside = 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);


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