[gvfs] MTP: Fix error semantics for do_pull/do_push/do_make_directory
- From: Philip Langdale <philipl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gvfs] MTP: Fix error semantics for do_pull/do_push/do_make_directory
- Date: Mon, 11 Aug 2014 03:30:37 +0000 (UTC)
commit 32983ccd7e3d15e6071464236376abf0b855b904
Author: Philip Langdale <philipl overt org>
Date: Thu Aug 7 22:28:10 2014 -0700
MTP: Fix error semantics for do_pull/do_push/do_make_directory
There are a bunch of semantic inconsistencies in the existing
implementations of these functions, when compared with the GIO
docs for making dirs and copying files.
This change fixes those problems, and also ensures that in the
pull/push cases, the destination is deleted at the last possible
moment, in the case of overwrites.
Also, certain other methods had their error code changed from
NOT_REGULAR_FILE to PERMISSION_DENIED when an operation is
attempted on a storage. This leads to less surprising errors
for users.
https://bugzilla.gnome.org/show_bug.cgi?id=734305
daemon/gvfsbackendmtp.c | 252 ++++++++++++++++++++++++++++++-----------------
1 files changed, 162 insertions(+), 90 deletions(-)
---
diff --git a/daemon/gvfsbackendmtp.c b/daemon/gvfsbackendmtp.c
index 92c6d87..5abe7f9 100644
--- a/daemon/gvfsbackendmtp.c
+++ b/daemon/gvfsbackendmtp.c
@@ -1072,11 +1072,11 @@ get_device_info (GVfsBackendMtp *backend, GFileInfo *info)
g_object_unref (icon);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE);
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, TRUE);
+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, TRUE);
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_RENAME, TRUE);
+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, FALSE);
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "mtpfs");
@@ -1149,7 +1149,7 @@ get_storage_info (LIBMTP_devicestorage_t *storage, GFileInfo *info) {
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, TRUE);
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_RENAME, FALSE);
+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, FALSE);
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, storage->FreeSpaceInBytes);
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, storage->MaxCapacity);
@@ -1499,7 +1499,7 @@ do_make_directory (GVfsBackend *backend,
if (ne < 3) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_FAILED,
+ G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
_("Cannot make directory in this location"));
goto exit;
}
@@ -1507,7 +1507,15 @@ do_make_directory (GVfsBackend *backend,
LIBMTP_mtpdevice_t *device;
device = G_VFS_BACKEND_MTP (backend)->device;
- CacheEntry *entry = get_cache_entry (G_VFS_BACKEND_MTP (backend), dir_name);
+ CacheEntry *entry = get_cache_entry (G_VFS_BACKEND_MTP (backend), filename);
+ if (entry != NULL && entry->id != -1) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_EXISTS,
+ _("Target file already exists"));
+ goto exit;
+ }
+
+ entry = get_cache_entry (G_VFS_BACKEND_MTP (backend), dir_name);
if (!entry) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
@@ -1549,6 +1557,7 @@ do_pull (GVfsBackend *backend,
g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
GFile *local_file = NULL;
+ GFileInfo *local_info = NULL;
GFileInfo *info = NULL;
FAIL_DURING_UNMOUNT();
@@ -1566,9 +1575,62 @@ do_pull (GVfsBackend *backend,
goto exit;
}
+ LIBMTP_mtpdevice_t *device;
+ device = G_VFS_BACKEND_MTP (backend)->device;
+
+ LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, entry->id);
+ if (file == NULL) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ _("File does not exist"));
+ goto exit;
+ }
+
+ info = g_file_info_new ();
+ get_file_info (backend, device, info, file);
+ LIBMTP_destroy_file_t (file);
+
local_file = g_file_new_for_path (local_path);
- if (g_file_query_exists (local_file, G_VFS_JOB (job)->cancellable)) {
+
+ gboolean source_is_dir =
+ g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY;
+
+ GError *error = NULL;
+ local_info =
+ g_file_query_info (local_file, G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ 0, G_VFS_JOB (job)->cancellable, &error);
+ gboolean dest_exists = local_info || error->code != G_IO_ERROR_NOT_FOUND;
+ if (!local_info && error->code != G_IO_ERROR_NOT_FOUND) {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ goto exit;
+ } else if (error != NULL) {
+ g_error_free (error);
+ }
+
+ gboolean dest_is_dir =
+ g_file_info_get_file_type (local_info) == G_FILE_TYPE_DIRECTORY;
+
+ /* Test all the GIO defined failure conditions */
+ if (dest_exists) {
if (flags & G_FILE_COPY_OVERWRITE) {
+ if (!source_is_dir && dest_is_dir) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
+ _("Target is a directory"));
+ goto exit;
+ } else if (source_is_dir && dest_is_dir) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_WOULD_MERGE,
+ _("Can't merge directories"));
+ goto exit;
+ } else if (source_is_dir && !dest_is_dir) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE,
+ _("Can't recursively copy directory"));
+ goto exit;
+ }
+ /* Source and Dest are files */
DEBUG ("(I) Removing destination.\n");
GError *error = NULL;
gboolean ret = g_file_delete (local_file,
@@ -1585,56 +1647,40 @@ do_pull (GVfsBackend *backend,
_("Target file already exists"));
goto exit;
}
- }
-
-
- LIBMTP_mtpdevice_t *device;
- device = G_VFS_BACKEND_MTP (backend)->device;
-
- LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, entry->id);
- if (file == NULL) {
+ } else if (source_is_dir) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
- _("File does not exist"));
+ G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE,
+ _("Can't recursively copy directory"));
goto exit;
}
- info = g_file_info_new ();
- get_file_info (backend, device, info, file);
- LIBMTP_destroy_file_t (file);
- if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
- g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE,
- _("Can't recursively copy directory"));
+ MtpProgressData mtp_progress_data;
+ mtp_progress_data.progress_callback = progress_callback;
+ mtp_progress_data.progress_callback_data = progress_callback_data;
+ mtp_progress_data.job = G_VFS_JOB (job);
+ int ret = LIBMTP_Get_File_To_File (device,
+ entry->id,
+ local_path,
+ (LIBMTP_progressfunc_t)mtp_progress,
+ &mtp_progress_data);
+ if (ret != 0) {
+ fail_job (G_VFS_JOB (job), device);
goto exit;
- } else {
- MtpProgressData mtp_progress_data;
- mtp_progress_data.progress_callback = progress_callback;
- mtp_progress_data.progress_callback_data = progress_callback_data;
- mtp_progress_data.job = G_VFS_JOB (job);
- int ret = LIBMTP_Get_File_To_File (device,
- entry->id,
- local_path,
- (LIBMTP_progressfunc_t)mtp_progress,
- &mtp_progress_data);
- if (ret != 0) {
- fail_job (G_VFS_JOB (job), device);
- goto exit;
- }
- /* Attempt to delete object if requested but don't fail it it fails. */
- if (remove_source) {
- DEBUG ("(I) Removing source.\n");
- LIBMTP_Delete_Object (device, entry->id);
- g_hash_table_foreach (G_VFS_BACKEND_MTP (backend)->monitors,
- emit_delete_event,
- (char *)source);
- remove_cache_entry (G_VFS_BACKEND_MTP (backend),
- source);
- }
- g_vfs_job_succeeded (G_VFS_JOB (job));
}
+ /* Attempt to delete object if requested but don't fail it it fails. */
+ if (remove_source) {
+ DEBUG ("(I) Removing source.\n");
+ LIBMTP_Delete_Object (device, entry->id);
+ g_hash_table_foreach (G_VFS_BACKEND_MTP (backend)->monitors,
+ emit_delete_event,
+ (char *)source);
+ remove_cache_entry (G_VFS_BACKEND_MTP (backend),
+ source);
+ }
+ g_vfs_job_succeeded (G_VFS_JOB (job));
exit:
+ g_clear_object (&local_info);
g_clear_object (&local_file);
g_clear_object (&info);
g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
@@ -1659,7 +1705,7 @@ do_push (GVfsBackend *backend,
char *dir_name = g_path_get_dirname (destination);
char *filename = g_path_get_basename (destination);
- GFile *file = NULL;
+ GFile *local_file = NULL;
GFileInfo *info = NULL;
gchar **elements = g_strsplit_set (destination, "/", -1);
unsigned int ne = g_strv_length (elements);
@@ -1668,7 +1714,7 @@ do_push (GVfsBackend *backend,
if (ne < 3) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
+ G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
_("Cannot write to this location"));
goto exit;
}
@@ -1677,8 +1723,60 @@ do_push (GVfsBackend *backend,
device = G_VFS_BACKEND_MTP (backend)->device;
CacheEntry *entry = get_cache_entry (G_VFS_BACKEND_MTP (backend), destination);
- if (entry != NULL && entry->id != -1) {
+ gboolean dest_exists = entry != NULL && entry->id != -1;
+ gboolean dest_is_dir = FALSE;
+
+ if (dest_exists) {
+ LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, entry->id);
+ if (file != NULL) {
+ dest_is_dir = file->filetype == LIBMTP_FILETYPE_FOLDER;
+ LIBMTP_destroy_file_t (file);
+ }
+ }
+
+ CacheEntry *parent = get_cache_entry (G_VFS_BACKEND_MTP (backend), dir_name);
+ if (!parent) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ _("Directory doesn't exist"));
+ goto exit;
+ }
+
+ GError *error = NULL;
+ local_file = g_file_new_for_path (local_path);
+ info =
+ g_file_query_info (local_file,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE","G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ 0, G_VFS_JOB (job)->cancellable, &error);
+ if (!info) {
+ g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
+ g_error_free (error);
+ goto exit;
+ }
+
+ gboolean source_is_dir =
+ g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY;
+
+ /* Test all the GIO defined failure conditions */
+ if (dest_exists) {
if (flags & G_FILE_COPY_OVERWRITE) {
+ if (!source_is_dir && dest_is_dir) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
+ _("Target is a directory"));
+ goto exit;
+ } else if (source_is_dir && dest_is_dir) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_WOULD_MERGE,
+ _("Can't merge directories"));
+ goto exit;
+ } else if (source_is_dir && !dest_is_dir) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE,
+ _("Can't recursively copy directory"));
+ goto exit;
+ }
+ /* Source and Dest are files */
DEBUG ("(I) Removing destination.\n");
int ret = LIBMTP_Delete_Object (device, entry->id);
if (ret != 0) {
@@ -1696,44 +1794,18 @@ do_push (GVfsBackend *backend,
_("Target file already exists"));
goto exit;
}
- }
-
- entry = get_cache_entry (G_VFS_BACKEND_MTP (backend), dir_name);
- if (!entry) {
- g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
- _("Directory doesn't exist"));
- goto exit;
- }
-
- file = g_file_new_for_path (local_path);
- g_assert(file);
-
- if (g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE,
- G_VFS_JOB (job)->cancellable) ==
- G_FILE_TYPE_DIRECTORY) {
+ } else if (source_is_dir) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE,
_("Can't recursively copy directory"));
goto exit;
}
- GError *error = NULL;
- info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE,
- G_FILE_QUERY_INFO_NONE,
- G_VFS_JOB (job)->cancellable,
- &error);
- if (!info) {
- g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
- g_error_free (error);
- goto exit;
- }
-
LIBMTP_file_t *mtpfile = LIBMTP_new_file_t ();
mtpfile->filename = strdup (filename);
- mtpfile->parent_id = entry->id;
- mtpfile->storage_id = entry->storage;
- mtpfile->filetype = LIBMTP_FILETYPE_UNKNOWN;
+ mtpfile->parent_id = parent->id;
+ mtpfile->storage_id = parent->storage;
+ mtpfile->filetype = LIBMTP_FILETYPE_UNKNOWN;
mtpfile->filesize = g_file_info_get_size (info);
MtpProgressData mtp_progress_data;
@@ -1752,7 +1824,7 @@ do_push (GVfsBackend *backend,
/* Attempt to delete object if requested but don't fail it it fails. */
if (remove_source) {
DEBUG ("(I) Removing source.\n");
- g_file_delete (file, G_VFS_JOB (job)->cancellable, NULL);
+ g_file_delete (local_file, G_VFS_JOB (job)->cancellable, NULL);
}
g_vfs_job_succeeded (G_VFS_JOB (job));
@@ -1762,7 +1834,7 @@ do_push (GVfsBackend *backend,
(char *)destination);
exit:
- g_clear_object (&file);
+ g_clear_object (&local_file);
g_clear_object (&info);
g_strfreev (elements);
g_free (dir_name);
@@ -1791,7 +1863,7 @@ do_delete (GVfsBackend *backend,
goto exit;
} else if (entry->id == -1) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
+ G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
_("Not a regular file"));
goto exit;
}
@@ -1838,7 +1910,7 @@ do_set_display_name (GVfsBackend *backend,
goto exit;
} else if (entry->id == -1) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
+ G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
_("Not a regular file"));
goto exit;
}
@@ -1907,7 +1979,7 @@ do_open_for_read (GVfsBackend *backend,
goto exit;
} else if (entry->id == -1) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
+ G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
_("Not a regular file"));
goto exit;
}
@@ -2173,8 +2245,8 @@ do_create (GVfsBackend *backend,
if (ne < 3) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Invalid filename"));
+ G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+ _("Cannot write to this location"));
goto exit;
}
@@ -2263,7 +2335,7 @@ do_append_to (GVfsBackend *backend,
goto exit;
} else if (entry->id == -1) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
+ G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
_("Not a regular file"));
goto exit;
}
@@ -2331,7 +2403,7 @@ do_replace (GVfsBackend *backend,
return do_create(backend, job, filename, flags);
} else if (entry->id == -1) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
+ G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
_("Not a regular file"));
goto exit;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]