[gvfs] google: Support deleting shared Google Drive files



commit bc8ac1055355a82bc32819569d048d1495793584
Author: Mayank Sharma <mayank8019 gmail com>
Date:   Mon Apr 29 09:40:54 2019 +0530

    google: Support deleting shared Google Drive files
    
    `g_file_delete ()` currently fails for shared files with
    "Permission denied" error (if they have a different owner and only
    one parent folder). This is because `gdata_service_delete_entry ()`
    can't be used for files, which have a different owner.
    `gdata_documents_service_remove_entry_from_folder ()` has to be used
    instead in this case (as well as for cases when the files have
    multiple parents).
    
    This fix depends on https://gitlab.gnome.org/GNOME/libgdata/merge_requests/5,
    which fixes `gdata_documents_service_remove_entry_from_folder ()`
    implementation to make it work for files with only one parent.
    
    Fixes: https://gitlab.gnome.org/GNOME/gvfs/issues/265

 daemon/gvfsbackendgoogle.c | 80 +++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 75 insertions(+), 5 deletions(-)
---
diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
index dfb35503..e60ad510 100644
--- a/daemon/gvfsbackendgoogle.c
+++ b/daemon/gvfsbackendgoogle.c
@@ -356,6 +356,55 @@ get_parent_ids (GVfsBackendGoogle *self,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static gboolean
+is_owner (GVfsBackendGoogle  *self,
+          GDataEntry         *entry,
+          GCancellable       *cancellable,
+          GError            **error)
+{
+  GDataFeed *acl_feed;
+  GDataAccessRule *rule;
+  GList *l;
+  GError *local_error = NULL;
+  gboolean ret_val = FALSE;
+
+  acl_feed = gdata_access_handler_get_rules (GDATA_ACCESS_HANDLER (GDATA_DOCUMENTS_ENTRY (entry)),
+                                             GDATA_SERVICE (self->service),
+                                             cancellable,
+                                             NULL,
+                                             NULL,
+                                             &local_error);
+
+  if (local_error != NULL)
+    {
+      sanitize_error (&local_error);
+      g_propagate_error (error, local_error);
+
+      goto out;
+    }
+
+  for (l = gdata_feed_get_entries (acl_feed); l != NULL; l = l->next)
+    {
+      const gchar *scope_value, *scope_type, *role;
+      rule = GDATA_ACCESS_RULE (l->data);
+      role = gdata_access_rule_get_role (rule);
+      gdata_access_rule_get_scope (rule, &scope_type, &scope_value);
+
+      if (g_strcmp0 (scope_value, self->account_identity) == 0 &&
+          g_strcmp0 (role, GDATA_DOCUMENTS_ACCESS_ROLE_OWNER) == 0)
+        {
+          ret_val = TRUE;
+          goto out;
+        }
+    }
+
+  out:
+   g_clear_object (&acl_feed);
+   return ret_val;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static gboolean
 insert_entry_full (GVfsBackendGoogle *self,
                    GDataEntry        *entry,
@@ -1304,6 +1353,8 @@ g_vfs_backend_google_delete (GVfsBackend   *_self,
   GError *error;
   gchar *entry_path = NULL;
   GList *parent_ids;
+  gboolean owner = FALSE;
+  guint parent_ids_len;
 
   g_rec_mutex_lock (&self->mutex);
   g_debug ("+ delete: %s\n", filename);
@@ -1339,11 +1390,26 @@ g_vfs_backend_google_delete (GVfsBackend   *_self,
 
   error = NULL;
 
-  /* 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)
+  owner = is_owner (self, GDATA_ENTRY (entry), cancellable, &error);
+  if (error != NULL)
     {
-      new_entry = gdata_documents_service_remove_entry_from_folder (self->service, GDATA_DOCUMENTS_ENTRY 
(entry), GDATA_DOCUMENTS_FOLDER (parent), cancellable, &error);
+      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+      g_error_free (error);
+      goto out;
+    }
+
+  parent_ids = get_parent_ids (self, entry);
+  parent_ids_len = g_list_length (parent_ids);
+  if (parent_ids_len > 1 || !owner)
+    {
+      /* gdata_documents_service_remove_entry_from_folder () returns the
+       * updated entry variable provided as argument with an increased ref.
+       * The ref count after the next line shall be 2. */
+      new_entry = gdata_documents_service_remove_entry_from_folder (self->service,
+                                                                    GDATA_DOCUMENTS_ENTRY (entry),
+                                                                    GDATA_DOCUMENTS_FOLDER (parent),
+                                                                    cancellable,
+                                                                    &error);
     }
   else
     {
@@ -1361,7 +1427,11 @@ g_vfs_backend_google_delete (GVfsBackend   *_self,
       goto out;
     }
 
-  if (new_entry)
+  /* In case of files owned by somebody else, the new entry is returned
+   * even if it had just one parent before the operation. The backend
+   * doesn't care about entries without parents (i.e. entries with
+   * parent_ids_len = 1), so let's ignore it. */
+  if (new_entry && parent_ids_len > 1)
     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));


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