summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--daemon/gvfsbackendgoogle.c597
1 files changed, 487 insertions, 110 deletions
diff --git a/daemon/gvfsbackendgoogle.c b/daemon/gvfsbackendgoogle.c
index e5989895..d4b074e2 100644
--- a/daemon/gvfsbackendgoogle.c
+++ b/daemon/gvfsbackendgoogle.c
@@ -393,6 +393,30 @@ get_parent_ids (GVfsBackendGoogle *self,
/* ---------------------------------------------------------------------------------------------------- */
+static gint64
+count_files_in_directory_with_title (GVfsBackendGoogle *self,
+ const gchar *entry_title,
+ GDataEntry *directory)
+{
+ gint64 num_same_title_files = 0;
+ GHashTableIter iter;
+ GDataEntry *entry;
+ DirEntriesKey *key;
+
+ g_hash_table_iter_init (&iter, self->dir_entries);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &entry))
+ {
+ if (g_strcmp0 (entry_title, gdata_entry_get_title (entry)) == 0 &&
+ g_strcmp0 (key->parent_id, gdata_entry_get_id (GDATA_ENTRY (directory))) == 0 &&
+ g_strcmp0 (key->title_or_id, gdata_entry_get_id (entry)) == 0)
+ num_same_title_files++;
+ }
+
+ return num_same_title_files;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
static gboolean
is_owner (GVfsBackendGoogle *self, GDataEntry *entry)
{
@@ -1392,6 +1416,7 @@ g_vfs_backend_google_copy (GVfsBackend *_self,
const gchar *summary;
const gchar *title;
const gchar *dummy_entry_filename;
+ gboolean will_overwrite = FALSE;
gchar *destination_basename = NULL;
gchar *entry_path = NULL;
goffset size;
@@ -1436,32 +1461,40 @@ g_vfs_backend_google_copy (GVfsBackend *_self,
goto out;
}
+ id = gdata_entry_get_id (source_entry);
title = gdata_entry_get_title (source_entry);
source_parent_id = gdata_entry_get_id (source_parent);
destination_parent_id = gdata_entry_get_id (destination_parent);
+ g_debug (" wants overwrite: %d\n", flags & G_FILE_COPY_OVERWRITE);
+
existing_entry = resolve_child (self, destination_parent, destination_basename, cancellable, NULL);
if (existing_entry != NULL)
{
- /* We don't support overwrites, so we don't need to care
- * about G_IO_ERROR_IS_DIRECTORY and G_IO_ERROR_WOULD_MERGE. */
+ gint64 num_same_title_files;
+
+ num_same_title_files = count_files_in_directory_with_title (self,
+ destination_basename,
+ destination_parent);
+
+ g_debug (" count of files with title same as destination_basename: %ld\n", num_same_title_files);
+
if (flags & G_FILE_COPY_OVERWRITE)
{
+ if (num_same_title_files > 1)
+ {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Operation not supported"));
+ goto out;
+ }
+
/* If the destination-basename matches that of some other entry, we
* further check if the files have the same ID or not. If the source
* file as well as the destination file have same ID, we fail the
- * operation.
- *
- * Doing the below check allows us to support the following sequence
- * of operations -
- * 1. `gio copy id1 id2/foo`
- * 2. `gio copy id1 id2/foo`
- *
- * Moreover, it disallows operations where foo is the real ID of some
- * other file existing in folder with ID `id2`. Although, in-practive
- * we can allow this operation, but later when query_info will be called,
- * it will resolve to some other entry instead of the expected entry. */
- if (g_strcmp0 (gdata_entry_get_id (existing_entry), destination_basename) == 0)
+ * operation. */
+ if (g_strcmp0 (gdata_entry_get_id (existing_entry), gdata_entry_get_id (source_entry)) == 0)
{
/* We return G_IO_ERROR_FAILED below instead of
* G_IO_ERROR_NOT_SUPPORTED because _NOT_SUPPORTED implies that the
@@ -1479,29 +1512,30 @@ g_vfs_backend_google_copy (GVfsBackend *_self,
_("Operation not supported"));
goto out;
}
- else if (g_strcmp0 (gdata_entry_get_title (existing_entry), destination_basename) == 0)
+ else if (g_strcmp0 (gdata_entry_get_id (existing_entry), destination_basename) == 0 ||
+ g_strcmp0 (gdata_entry_get_title (existing_entry), destination_basename) == 0)
+ will_overwrite = TRUE;
+ else
{
- /* Case occurs when doing `gio copy ./id1 ./$TITLE$` where $TITLE$ is
- * the title of file with ID `id1` */
- g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR,
- G_IO_ERROR_FAILED,
- _("Operation not supported"));
- goto out;
+ /* Neither the ID nor the title of existing_entry matches the destination_basename.
+ * This can only happen when destination_basename corresponds to the volatile entry
+ * being pointed to by some other file. We simply ignore it and let the copy
+ * operation to happen. */
}
}
else
{
- /* If the destination_basename matches the volatile path of some
- * other file (given by existing_entry) and if the titles are
- * different, copy operation should be allowed. In that case, we will
- * be having two different entries (each with different titles), but
- * the stored volatile path (in GDataDocumentsProperty) will be same.
+ /* We just check for two conditions here (out of a total of 3 we checked above when
+ * G_FILE_COPY_OVERWRITE is specified). The first condition "num_same_title_files > 0"
+ * checks if we have multiple files with same destination_name in the destination
+ * directory, whereas the second condition specifically checks for the case when we have a
+ * file whose ID is same as that of source file (i.e. the same file with multiple
+ * parents).
*
- * This isn't an issue since we've already handled those extra
- * entries in insert_entry_full().
- */
- if (g_strcmp0 (gdata_entry_get_title (existing_entry), title) == 0)
+ * In the case our destination_name matches the volatile entry of some other file, we
+ * simply ignore it and let the copy operation to take place. */
+ if (num_same_title_files > 0 ||
+ g_strcmp0 (gdata_entry_get_id (existing_entry), destination_basename) == 0)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_EXISTS, _("Target file already exists"));
goto out;
@@ -1509,6 +1543,86 @@ g_vfs_backend_google_copy (GVfsBackend *_self,
}
}
+ /* For the cases like `gio copy id1 ./folder/`, the operation actually resolves to its longer form
+ * `gio copy id1 ./folder/id1`. We cater such cases differently and check if another file with the
+ * same title as that of file with real ID id1 exists in the destination folder. This check allows
+ * us to be as similar to POSIX filesystem operations as possible. This way, we can make
+ * title-based copy-overwrite operations to work in nautilus. */
+ if (g_strcmp0 (destination_basename, id) == 0)
+ {
+ existing_entry = resolve_child (self, destination_parent, title, cancellable, NULL);
+ if (existing_entry != NULL)
+ {
+ gint64 num_same_title_files;
+
+ num_same_title_files = count_files_in_directory_with_title (self, title, destination_parent);
+ g_debug (" count of same title files: %ld\n", num_same_title_files);
+
+ if (flags & G_FILE_COPY_OVERWRITE)
+ {
+ if (num_same_title_files > 1)
+ {
+ /* We return G_IO_ERROR_FAILED below instead of
+ * G_IO_ERROR_NOT_SUPPORTED because _NOT_SUPPORTED implies that the
+ * operation hasn't been implemented yet, and hence GIO takes a
+ * fallback to perform the operation. So, for copy operation with
+ * overwrite, it uses a (replace + read + write) fallback which in
+ * turn overwrites the file even though we don't support overwrites.
+ *
+ * For a concrete case see this discussion:
+ * https://gitlab.gnome.org/GNOME/gvfs/merge_requests/58#note_584083
+ */
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Operation not supported"));
+ goto out;
+ }
+
+ if (GDATA_IS_DOCUMENTS_FOLDER (existing_entry))
+ {
+ if (GDATA_IS_DOCUMENTS_FOLDER (source_entry))
+ {
+ g_vfs_job_failed (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_WOULD_MERGE,
+ _("Can’t copy directory over directory"));
+ goto out;
+ }
+ else
+ {
+ g_vfs_job_failed (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_IS_DIRECTORY,
+ _("Can’t copy file over directory"));
+ goto out;
+ }
+ }
+
+ will_overwrite = (num_same_title_files == 1);
+ }
+ else
+ {
+ /* If the title matches the volatile path of some
+ * other file (given by existing_entry) and if the titles are
+ * different, move operation should be allowed. In that case, we will
+ * be having two different entries (each with different titles), but
+ * the stored volatile path (in GDataDocumentsProperty) will be same.
+ *
+ * This isn't an issue since we've already handled those extra
+ * entries in insert_entry_full().
+ */
+ if (num_same_title_files > 0)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_EXISTS, _("Target file already exists"));
+ goto out;
+ }
+ }
+ }
+ }
+
+ g_debug (" will overwrite: %d\n", will_overwrite);
+
/* We again resolve the source_entry after checking existing_entry. This is
* because when we try to find existing_entry, resolve_child is called which
* internally calls rebuild_dir function. Now, if between the initial
@@ -1553,11 +1667,19 @@ g_vfs_backend_google_copy (GVfsBackend *_self,
* Moreover, just after copy operation, a query_info operation is performed
* and it needs the ("Foobar (copy).pdf", destination_parent_id) -> Entry mapping
* in the cache. Hence, we set the new entry's filename conditionally. */
- if (g_strcmp0 (source_parent_id, destination_parent_id) != 0 &&
- g_strcmp0 (destination_basename, id) == 0)
+ if (g_strcmp0 (destination_basename, id) == 0)
dummy_entry_filename = gdata_entry_get_title (source_entry);
else
- dummy_entry_filename = destination_basename;
+ {
+ /* Case occurs when trying to overwrite an existing file using destination_basename
+ * set to ID2 in `gio copy ID1 ./folder/ID2`. We need to retain the title of the
+ * source file instead of setting dummy_entry_filename to destination_basename. */
+ if (will_overwrite && existing_entry &&
+ g_strcmp0 (gdata_entry_get_id (existing_entry), destination_basename) == 0)
+ dummy_entry_filename = title;
+ else
+ dummy_entry_filename = destination_basename;
+ }
dummy_source_entry = g_object_new (source_entry_type,
"etag", etag,
@@ -1617,6 +1739,55 @@ g_vfs_backend_google_copy (GVfsBackend *_self,
insert_entry (self, GDATA_ENTRY (new_entry));
g_hash_table_foreach (self->monitors, emit_create_event, entry_path);
+ if (will_overwrite)
+ {
+ GDataDocumentsEntry *entry_to_remove = NULL;
+ guint parent_ids_len;
+ GList *parent_ids;
+ gchar *existing_entry_path = NULL;
+
+ /* The internal ref count has to be increased before removing the
+ * existing_entry since remove_entry_full calls g_object_unref() internally */
+ g_object_ref (existing_entry);
+ remove_entry (self, existing_entry);
+
+ parent_ids = get_parent_ids (self, existing_entry);
+ parent_ids_len = g_list_length (parent_ids);
+ if (parent_ids_len > 1 || !is_owner (self, GDATA_ENTRY (existing_entry)))
+ {
+ /* 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. */
+ entry_to_remove = gdata_documents_service_remove_entry_from_folder (self->service,
+ GDATA_DOCUMENTS_ENTRY (existing_entry),
+ GDATA_DOCUMENTS_FOLDER (destination_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, existing_entry, cancellable, &error);
+ }
+
+ if (error != NULL)
+ {
+ sanitize_error (&error);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ g_clear_object (&entry_to_remove);
+ goto out;
+ }
+
+ existing_entry_path = g_build_path ("/", parent_path, gdata_entry_get_id (existing_entry), NULL);
+ g_hash_table_foreach (self->monitors, emit_delete_event, existing_entry_path);
+
+ g_object_unref (existing_entry);
+ g_clear_object (&entry_to_remove);
+ }
+
size = gdata_documents_entry_get_file_size (new_entry);
g_vfs_job_progress_callback (size, size, job);
g_vfs_job_succeeded (G_VFS_JOB (job));
@@ -1654,8 +1825,8 @@ g_vfs_backend_google_move (GVfsBackend *_self,
GDataLink *source_parent_link = NULL;
GDataLink *destination_parent_link = NULL;
GError *error = NULL;
+ gboolean will_overwrite = FALSE;
const gchar *source_id;
- const gchar *volatile_entry_id;
const gchar *source_parent_id;
const gchar *destination_parent_id;
const gchar *title;
@@ -1708,91 +1879,199 @@ g_vfs_backend_google_move (GVfsBackend *_self,
source_parent_id = gdata_entry_get_id (source_parent);
destination_parent_id = gdata_entry_get_id (destination_parent);
title = gdata_entry_get_title (source_entry);
+ auth_domain = gdata_documents_service_get_primary_authorization_domain ();
+
+ g_debug (" wants overwrite: %d\n", flags & G_FILE_COPY_OVERWRITE);
- /* We disallow over-writes to the same file in the same parent directory
- * since Drive doesn't have any notion of "over-writing" a file. For the
- * cases like renaming a file using `gio move id1 new_title`, we check using
- * the below condition, and later update the title of the file conditionally. */
- if (g_strcmp0 (source_parent_id, destination_parent_id) == 0 &&
- g_strcmp0 (destination_basename, title) == 0)
+ existing_entry = resolve_child (self, destination_parent, destination_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"));
- goto out;
- }
+ const gchar *existing_entry_id;
+ gint64 num_same_title_files;
- auth_domain = gdata_documents_service_get_primary_authorization_domain ();
+ existing_entry_id = gdata_entry_get_id (existing_entry);
+ num_same_title_files = count_files_in_directory_with_title (self,
+ destination_basename,
+ destination_parent);
- existing_entry = resolve_child (self, destination_parent, source_id, cancellable, NULL);
- if (existing_entry != NULL && g_strcmp0 (destination_parent_id, source_parent_id) != 0)
- {
- DirEntriesKey *key = dir_entries_key_new (source_id, destination_parent_id);
+ g_debug (" count of files with title same as destination_basename: %ld\n", num_same_title_files);
- /* We need to check if the real ID of source_entry being moved clashes with some,
- * other entry's volatile ID or not. This happens when we're trying to move an entry
- * but there already exists some tuple (source_id, destination_parent_id) in the
- * dir_entries hash table. Here, the already present key corresponds to the
- * volatile_entry_id of some previously copied file. This can happen for the
- * following operations:
- *
- * 1. `gio copy ./id1 ./id2/` ("id2" is the real ID of a folder)
- * 2. `gio move ./id1 ./id2/`
+ /* We don't support overwrites, so we don't need to care
+ * about G_IO_ERROR_IS_DIRECTORY and G_IO_ERROR_WOULD_MERGE.
*
- * The first copy operation will create a volatile entry in the
- * cache i.e. (id1, id2) -> NewEntry. The subsequent move operation will see that there
- * already exists a tuple (id1, id2), and hence this case will happen. If
- * volatile_entry_id != NULL, we simply ignore the case and allow the operation
- * to happen. */
- if ((volatile_entry_id = get_volatile_entry_id (self, source_entry, key)) == NULL)
+ * Moreover, we don't need to take care of G_IO_ERROR_WOULD_RECURSE
+ * too since changing a Folder's parent on Drive is a matter of
+ * simply changing "parents" property. We don't have to do any
+ * recursive move. */
+ if (flags & G_FILE_COPY_OVERWRITE)
{
- /* This case happens when a file has multiple parents and we're trying to
- * perform a move operation from one parent to another parent (where the
- * file already exists). Such operation simply doesn't makes sense
- * since moving the same file here and there has no consequence.
- * Hence, we return _EXISTS. */
- g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_EXISTS, _("Target file already exists"));
- goto out;
+ if (num_same_title_files > 1)
+ {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Operation not supported"));
+ goto out;
+ }
+
+ if (GDATA_IS_DOCUMENTS_FOLDER (existing_entry))
+ {
+ if (GDATA_IS_DOCUMENTS_FOLDER (source_entry))
+ {
+ g_vfs_job_failed (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_WOULD_MERGE,
+ _("Can’t copy directory over directory"));
+ goto out;
+ }
+ else
+ {
+ g_vfs_job_failed (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_IS_DIRECTORY,
+ _("Can’t copy file over directory"));
+ goto out;
+ }
+ }
+
+ /* If the destination-basename matches that of some other entry, we
+ * further check if the files have the same ID or not. If the source
+ * file as well as the destination file have same ID, we fail the
+ * operation. */
+ if (g_strcmp0 (existing_entry_id, source_id) == 0)
+ {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Operation not supported"));
+ goto out;
+ }
+ else if (g_strcmp0 (existing_entry_id, destination_basename) == 0)
+ {
+ if (g_strcmp0 (source_parent_id, destination_parent_id) == 0)
+ {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Operation not supported"));
+ goto out;
+ }
+ else
+ will_overwrite = TRUE;
+ }
+ else if (g_strcmp0 (gdata_entry_get_title (existing_entry), destination_basename) == 0)
+ will_overwrite = TRUE;
+ else
+ {
+ /* Neither the ID nor the title of existing_entry matches the destination_basename.
+ * This can only happen when destination_basename corresponds to the volatile entry
+ * being pointed to by some other file. We simply ignore it and let the move
+ * operation to happen. */
+ }
+ }
+ else
+ {
+ /* We just check for two conditions here (out of a total of 3 we checked above when
+ * G_FILE_COPY_OVERWRITE is specified). The first condition "num_same_title_files > 0"
+ * checks if we have multiple files with same destination_name in the destination
+ * directory, whereas the second condition specifically checks for the case when we have a
+ * file whose ID is same as that of source file (i.e. the same file with multiple
+ * parents).
+ *
+ * In the case our destination_name matches the volatile entry of some other file, we
+ * simply ignore it and let the move operation take place. */
+ if (num_same_title_files > 0 ||
+ g_strcmp0 (gdata_entry_get_id (existing_entry), destination_basename) == 0)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_EXISTS, _("Target file already exists"));
+ goto out;
+ }
}
}
- existing_entry = resolve_child (self, destination_parent, destination_basename, cancellable, NULL);
- if (existing_entry != NULL)
+ /* For the cases like `gio move id1 ./folder/`, the operation actually resolves to its longer form
+ * `gio move id1 ./folder/id1`. We cater such cases differently and check if another file with the
+ * same title as that of file with real ID id1 exists in the destination folder. This check allows
+ * us to be as similar to POSIX filesystem operations as possible. This way, we can make
+ * title-based move-overwrite operations to work in nautilus. */
+ if (g_strcmp0 (destination_basename, source_id) == 0)
{
- const gchar *existing_entry_id = gdata_entry_get_id (existing_entry);
-
- /* If the destination-basename matches that of some other entry, we
- * further check if the files have the same ID or not. If the source
- * file as well as the destination file have same ID, we fail the
- * operation.
- *
- * Doing the below check allows us to support the following sequence
- * of operations -
- * 1. `gio copy id1 id2/foo`
- * 2. `gio move id1 id2/foo`
- *
- * Moreover, it disallows operations where foo is the real ID of some
- * other file existing in folder with ID `id2`. Although, in-practive
- * we can allow this operation, but later when query_info will be called,
- * it will resolve to some other entry instead of the expected entry. */
- if (g_strcmp0 (existing_entry_id, source_id) == 0 ||
- g_strcmp0 (existing_entry_id, destination_basename) == 0)
+ existing_entry = resolve_child (self, destination_parent, title, cancellable, NULL);
+ if (existing_entry != NULL)
{
- /* The reason why we're returning G_IO_ERROR_FAILED here
- * instead of G_IO_ERROR_NOT_SUPPORTED is because _NOT_SUPPORTED
- * implies that this operation isn't implemented and it tells GIO
- * to use some other fallback for it.
- * So, for move operation, it'll use a copy+delete fallback here.
- * Since, we don't support backups, so the fallback will end up
- * removing the source file completely without making any copy, and
- * we lose the file. Hence, we return G_IO_ERROR_FAILED.
- */
- g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR,
- G_IO_ERROR_FAILED,
- _("Operation not supported"));
- goto out;
+ gint64 num_same_title_files;
+
+ num_same_title_files = count_files_in_directory_with_title (self,
+ title,
+ destination_parent);
+
+ g_debug (" count of same title files: %ld\n", num_same_title_files);
+
+ if (flags & G_FILE_COPY_OVERWRITE)
+ {
+ if (num_same_title_files > 1)
+ {
+ /* We return G_IO_ERROR_FAILED below instead of
+ * G_IO_ERROR_NOT_SUPPORTED because _NOT_SUPPORTED implies that the
+ * operation hasn't been implemented yet, and hence GIO takes a
+ * fallback to perform the operation. So, for copy operation with
+ * overwrite, it uses a (replace + read + write) fallback which in
+ * turn overwrites the file even though we don't support overwrites.
+ *
+ * For a concrete case see this discussion:
+ * https://gitlab.gnome.org/GNOME/gvfs/merge_requests/58#note_584083
+ */
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Operation not supported"));
+ goto out;
+ }
+
+ if (GDATA_IS_DOCUMENTS_FOLDER (existing_entry))
+ {
+ if (GDATA_IS_DOCUMENTS_FOLDER (source_entry))
+ {
+ g_vfs_job_failed (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_WOULD_MERGE,
+ _("Can’t move directory over directory"));
+ goto out;
+ }
+ else
+ {
+ g_vfs_job_failed (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_IS_DIRECTORY,
+ _("Can’t move file over directory"));
+ goto out;
+ }
+ }
+
+ will_overwrite = (num_same_title_files == 1);
+ }
+ else
+ {
+ /* If the title matches the volatile path of some
+ * other file (given by existing_entry) and if the titles are
+ * different, move operation should be allowed. In that case, we will
+ * be having two different entries (each with different titles), but
+ * the stored volatile path (in GDataDocumentsProperty) will be same.
+ *
+ * This isn't an issue since we've already handled those extra
+ * entries in insert_entry_full().
+ */
+ if (num_same_title_files > 0)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_EXISTS, _("Target file already exists"));
+ goto out;
+ }
+ }
}
}
+ g_debug (" will overwrite: %d\n", will_overwrite);
+
/* The internal ref count has to be increased before removing the
* source_entry since remove_entry_full calls g_object_unref() internally */
g_object_ref (source_entry);
@@ -1823,7 +2102,13 @@ g_vfs_backend_google_move (GVfsBackend *_self,
* rare case. If you explicity wish to move the file and update the title of
* the file to its ID, do a move + rename operation. */
if (g_strcmp0 (destination_basename, source_id) != 0)
- gdata_entry_set_title (source_entry, destination_basename);
+ {
+ /* When we're trying to overwrite a file using its ID, the destination_basename will be set to
+ * the ID. Instead, we want to have the title to be same as that of source entry. */
+ if (!(will_overwrite && existing_entry &&
+ g_strcmp0 (gdata_entry_get_id (existing_entry), destination_basename) == 0))
+ gdata_entry_set_title (source_entry, destination_basename);
+ }
error = NULL;
new_entry = gdata_service_update_entry (GDATA_SERVICE (self->service),
@@ -1840,6 +2125,55 @@ g_vfs_backend_google_move (GVfsBackend *_self,
goto out;
}
+ if (will_overwrite)
+ {
+ GDataDocumentsEntry *entry_to_remove = NULL;
+ guint parent_ids_len;
+ GList *parent_ids;
+ gchar *existing_entry_path = NULL;
+
+ /* The internal ref count has to be increased before removing the
+ * existing_entry since remove_entry_full calls g_object_unref() internally */
+ g_object_ref (existing_entry);
+ remove_entry (self, existing_entry);
+
+ parent_ids = get_parent_ids (self, existing_entry);
+ parent_ids_len = g_list_length (parent_ids);
+ if (parent_ids_len > 1 || !is_owner (self, GDATA_ENTRY (existing_entry)))
+ {
+ /* 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. */
+ entry_to_remove = gdata_documents_service_remove_entry_from_folder (self->service,
+ GDATA_DOCUMENTS_ENTRY (existing_entry),
+ GDATA_DOCUMENTS_FOLDER (destination_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, existing_entry, cancellable, &error);
+ }
+
+ if (error != NULL)
+ {
+ sanitize_error (&error);
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ g_clear_object (&entry_to_remove);
+ goto out;
+ }
+
+ existing_entry_path = g_build_path ("/", parent_path, gdata_entry_get_id (existing_entry), NULL);
+ g_hash_table_foreach (self->monitors, emit_delete_event, existing_entry_path);
+
+ g_object_unref (existing_entry);
+ g_clear_object (&entry_to_remove);
+ }
+
entry_path = g_build_path ("/", parent_path, gdata_entry_get_id (new_entry), NULL);
g_debug (" new entry path: %s\n", entry_path);
@@ -2437,6 +2771,7 @@ g_vfs_backend_google_push (GVfsBackend *_self,
gchar *destination_basename = NULL;
gchar *entry_path = NULL;
gchar *parent_path = NULL;
+ gchar *local_file_title = NULL;
goffset size;
g_rec_mutex_lock (&self->mutex);
@@ -2469,6 +2804,8 @@ g_vfs_backend_google_push (GVfsBackend *_self,
goto out;
}
+ local_file_title = g_file_info_get_attribute_as_string (info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
+
error = NULL;
destination_parent = resolve_dir (self, destination, cancellable, &destination_basename, &parent_path, &error);
if (error != NULL)
@@ -2481,8 +2818,19 @@ g_vfs_backend_google_push (GVfsBackend *_self,
existing_entry = resolve_child (self, destination_parent, destination_basename, cancellable, NULL);
if (existing_entry != NULL)
{
+ const gchar *existing_entry_id;
+ gint64 num_same_title_files;
+
+ existing_entry_id = gdata_entry_get_id (existing_entry);
+ num_same_title_files = count_files_in_directory_with_title (self,
+ destination_basename,
+ destination_parent);
+
+ g_debug (" count of files with title same as destination_basename: %ld\n", num_same_title_files);
+
if (flags & G_FILE_COPY_OVERWRITE)
{
+
if (GDATA_IS_DOCUMENTS_FOLDER (existing_entry))
{
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
@@ -2522,12 +2870,40 @@ g_vfs_backend_google_push (GVfsBackend *_self,
}
}
- needs_overwrite = TRUE;
+ if (g_strcmp0 (local_file_title, existing_entry_id) == 0)
+ {
+ /* This corresponds to the operation when a local file has its title set to the
+ * real ID of some file, and that the local file is being pushed with the same title.
+ * We disallow such operation. */
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Operation not supported"));
+ goto out;
+ }
+
+ if (num_same_title_files > 1)
+ {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Operation not supported"));
+ goto out;
+ }
+
+ if (num_same_title_files == 1 || g_strcmp0 (destination_basename, existing_entry_id) == 0)
+ 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;
+ if (num_same_title_files > 0 ||
+ g_strcmp0 (destination_basename, existing_entry_id) == 0 ||
+ g_strcmp0 (local_file_title, existing_entry_id) == 0)
+ {
+
+ g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_EXISTS, _("Target file already exists"));
+ goto out;
+ }
}
}
else
@@ -2558,7 +2934,7 @@ g_vfs_backend_google_push (GVfsBackend *_self,
if (needs_overwrite)
{
document = GDATA_DOCUMENTS_DOCUMENT (g_object_ref (existing_entry));
- title = gdata_entry_get_title (existing_entry);
+ title = local_file_title;
error = NULL;
ostream = gdata_documents_service_update_document (self->service,
@@ -2648,6 +3024,7 @@ g_vfs_backend_google_push (GVfsBackend *_self,
g_clear_object (&new_document);
g_clear_object (&ostream);
g_free (destination_basename);
+ g_free (local_file_title);
g_free (entry_path);
g_free (parent_path);
g_debug ("- push\n");