[gvfs/wip/rishi/goa: 5/8] push: Implement overwrites and guard against broken filenames



commit d77d303f05f5838c9d42e7589cb1ff586a42dace
Author: Debarshi Ray <debarshir gnome org>
Date:   Tue Aug 25 21:14:56 2015 +0200

    push: Implement overwrites and guard against broken filenames

 daemon/gvfsbackendgoogle.c |  178 ++++++++++++++++++++++++++++++++++---------
 1 files changed, 140 insertions(+), 38 deletions(-)
---
diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
index 29bf3c2..2604f6f 100644
--- a/daemon/gvfsbackendgoogle.c
+++ b/daemon/gvfsbackendgoogle.c
@@ -1594,16 +1594,19 @@ g_vfs_backend_google_push (GVfsBackend           *_self,
   GDataDocumentsDocument *document = NULL;
   GDataDocumentsDocument *new_document = NULL;
   GDataDocumentsFolder *parent = NULL;
+  GDataEntry *entry;
   GDataUploadStream *ostream = NULL;
   GError *error;
   GFile *local_file = NULL;
   GFileInputStream *istream = NULL;
   GFileInfo *info = NULL;
+  gboolean is_display_name;
   gboolean is_root;
+  gboolean needs_overwrite = FALSE;
   const gchar *content_type;
   const gchar *id;
-  const gchar *real_parent_path;
   const gchar *title;
+  gchar *basename = NULL;
   gchar *parent_id = NULL;
   gchar *parent_path = NULL;
   gchar *path = NULL;
@@ -1615,8 +1618,24 @@ g_vfs_backend_google_push (GVfsBackend           *_self,
    */
 
   g_rec_mutex_lock (&self->mutex);
-  unescaped_destination = unescape_basename_and_map_dirname (self, destination, NULL);
-  g_debug ("+ push: %s -> %s (%s, 1)\n", local_path, destination, unescaped_destination);
+  unescaped_destination = unescape_basename_and_map_dirname (self, destination, &is_display_name);
+  g_debug ("+ push: %s -> %s (%s, %d)\n", local_path, destination, unescaped_destination, is_display_name);
+
+  if (unescaped_destination == NULL)
+    {
+      g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("No such file or directory"));
+      goto out;
+    }
+
+  if (flags & G_FILE_COPY_BACKUP)
+    {
+      /* Return G_IO_ERROR_NOT_SUPPORTED instead of
+       * G_IO_ERROR_CANT_CREATE_BACKUP to proceed with the GIO
+       * fallback copy.
+       */
+      g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Operation not 
supported");
+      goto out;
+    }
 
   local_file = g_file_new_for_path (local_path);
 
@@ -1644,6 +1663,74 @@ g_vfs_backend_google_push (GVfsBackend           *_self,
       goto out;
     }
 
+  basename = g_path_get_basename (unescaped_destination);
+
+  if (!is_display_name)
+    {
+      const gchar *destination_id = basename;
+
+      /* This is almost a pathological case. In reality, clients like
+       * file managers won't use an ID as the destination
+       * basename. They will use an escaped version of the source
+       * basename. That means this code path is unlikely to be hit
+       * when recursively pushing a folder.
+       *
+       * Therefore, in order to keep things simple, we unconditionally
+       * refresh the cache.
+       */
+      error = NULL;
+      rebuild_entries (self, cancellable, &error);
+      if (error != NULL)
+        {
+          g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+          goto out;
+        }
+
+      entry = g_hash_table_lookup (self->entries, destination_id);
+      if (entry != NULL)
+        {
+          if (flags & G_FILE_COPY_OVERWRITE)
+            {
+              if (GDATA_IS_DOCUMENTS_FOLDER (entry))
+                {
+                  /* At this point, the source can never be a
+                   * directory. So we can ignore the issue of copying
+                   * a directory over another
+                   * (ie. G_IO_ERROR_WOULD_MERGE).
+                   */
+                  g_vfs_job_failed (G_VFS_JOB (job),
+                                    G_IO_ERROR,
+                                    G_IO_ERROR_IS_DIRECTORY,
+                                    _("Can't copy file over directory"));
+                  goto out;
+                }
+
+              needs_overwrite = TRUE;
+            }
+          else
+            {
+              g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_EXISTS, _("Target file already 
exists"));
+              goto out;
+            }
+        }
+      else
+        {
+          /* The basename looks like an ID but that ID doesn't
+           * exist. This is fishy. Probably caused by not using
+           * g_file_get_child_for_display_name to create the
+           * destination path.
+           */
+          g_vfs_job_failed (G_VFS_JOB (job),
+                            G_IO_ERROR,
+                            G_IO_ERROR_INVALID_FILENAME,
+                            _("Invalid filename %s"),
+                            destination);
+          goto out;
+        }
+    }
+
+  g_debug ("  will overwrite: %d\n", needs_overwrite);
+
   error = NULL;
   istream = g_file_read (local_file, cancellable, &error);
   if (error != NULL)
@@ -1653,54 +1740,68 @@ g_vfs_backend_google_push (GVfsBackend           *_self,
       goto out;
     }
 
-  parent_path = g_path_get_dirname (unescaped_destination);
-  real_parent_path = g_hash_table_lookup (self->lookaside, parent_path);
-  if (real_parent_path != NULL)
+  content_type = g_file_info_get_content_type (info);
+
+  if (needs_overwrite)
     {
-      g_free (parent_path);
-      parent_path = g_strdup (real_parent_path);
-    }
+      document = g_object_ref (entry);
+      title = gdata_entry_get_title (entry);
 
-  is_folder_or_root (parent_path, NULL, NULL, &is_root);
-  if (!is_root)
+      error = NULL;
+      ostream = gdata_documents_service_update_document (self->service,
+                                                         document,
+                                                         title,
+                                                         content_type,
+                                                         cancellable,
+                                                         &error);
+    }
+  else
     {
-      parent_id = g_path_get_basename (parent_path);
-      parent = g_hash_table_lookup (self->entries, parent_id);
+      parent_path = g_path_get_dirname (unescaped_destination);
 
-      if (parent == NULL)
+      is_folder_or_root (parent_path, NULL, NULL, &is_root);
+      if (!is_root)
         {
-          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;
-            }
-
+          parent_id = g_path_get_basename (parent_path);
           parent = g_hash_table_lookup (self->entries, parent_id);
+
           if (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;
+              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;
+                }
+
+              parent = g_hash_table_lookup (self->entries, parent_id);
+              if (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;
+                }
             }
         }
-    }
 
-  document = gdata_documents_document_new (NULL);
-  title = g_file_info_get_display_name (info);
-  gdata_entry_set_title (GDATA_ENTRY (document), title);
+      document = gdata_documents_document_new (NULL);
+      title = basename;
+      gdata_entry_set_title (GDATA_ENTRY (document), title);
 
-  content_type = g_file_info_get_content_type (info);
+      error = NULL;
+      ostream = gdata_documents_service_upload_document (self->service,
+                                                         document,
+                                                         title,
+                                                         content_type,
+                                                         parent,
+                                                         cancellable,
+                                                         &error);
+    }
 
-  error = NULL;
-  ostream = gdata_documents_service_upload_document (self->service,
-                                                     document,
-                                                     title,
-                                                     content_type,
-                                                     parent,
-                                                     cancellable,
-                                                     &error);
   if (error != NULL)
     {
       g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
@@ -1764,6 +1865,7 @@ g_vfs_backend_google_push (GVfsBackend           *_self,
   g_clear_object (&local_file);
   g_clear_object (&new_document);
   g_clear_object (&ostream);
+  g_free (basename);
   g_free (parent_id);
   g_free (parent_path);
   g_free (path);


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