summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Langdale <philipl@overt.org>2013-02-24 15:42:02 -0800
committerPhilip Langdale <philipl@overt.org>2013-03-30 16:25:13 -0700
commit79f94867d39bab750fb437ffe552f943cfda0add (patch)
treee0fee467fc4b7062aa37c2a3f0c6d665e71b7563
parente88aa8dad358a23c164a95267f7dd8415d377b21 (diff)
downloadgvfs-79f94867d39bab750fb437ffe552f943cfda0add.tar.gz
MTP: Use normal filenames in paths.
It's becoming too fragile to use item IDs as path elements. Beyond the problems with clients that don't use copy names, there are a bunch of situtations where a path sent by a client could be an id or a filename and it's not 100% possible to tell them apart. When the initial feature set of very limited, this wasn't much of an issue, but with direct read/write support, we are now exposed to a huge set of clients (as opposed to just file managers) with no ability to recognise copy names, and which generate a lot of mixed form paths, so we're past the tipping point where it's now less of a hassle to maintain a name to id mapping. https://bugzilla.gnome.org/show_bug.cgi?id=696016
-rw-r--r--daemon/gvfsbackendmtp.c550
-rw-r--r--daemon/gvfsbackendmtp.h2
2 files changed, 354 insertions, 198 deletions
diff --git a/daemon/gvfsbackendmtp.c b/daemon/gvfsbackendmtp.c
index b047e0b6..9e4f3f18 100644
--- a/daemon/gvfsbackendmtp.c
+++ b/daemon/gvfsbackendmtp.c
@@ -124,6 +124,154 @@ typedef struct {
GByteArray *bytes;
} RWHandle;
+typedef struct {
+ uint32_t storage;
+ uint32_t id;
+} CacheEntry;
+
+
+/************************************************
+ * Cache Helpers
+ ************************************************/
+
+static CacheEntry *
+add_cache_entry (GVfsBackendMtp *backend,
+ char *path,
+ uint32_t storage,
+ uint32_t id)
+{
+ CacheEntry *entry = g_new0 (CacheEntry, 1);
+ entry->storage = storage;
+ entry->id = id;
+ DEBUG ("(II) add_cache_entry: %s: %u, %u",
+ path, entry->storage, entry->id);
+ g_hash_table_replace (backend->file_cache,
+ path, entry);
+}
+
+
+static char *
+build_partial_path (char **elements,
+ unsigned int ne)
+{
+ char **pe = g_new0 (char *, ne + 2);
+ int i;
+ pe[0] = g_strdup("/");
+ for (i = 0; i < ne; i++) {
+ pe[i + 1] = elements[i];
+ }
+ char *path = g_build_filenamev(pe);
+ g_free (pe);
+ return path;
+}
+
+/**
+ * get_file_for_filename:
+ *
+ * Get the entity ID for an element given its filename and
+ * the IDs of its parents.
+ *
+ * Called with backend mutex lock held.
+ */
+static void
+add_cache_entries_for_filename (GVfsBackendMtp *backend,
+ const char *path)
+{
+ LIBMTP_file_t *file = NULL;
+ LIBMTP_mtpdevice_t *device = backend->device;
+
+ gchar **elements = g_strsplit_set (path, "/", -1);
+ unsigned int ne = g_strv_length (elements);
+
+ DEBUG ("(III) add_cache_entries_for_filename: %s, %u", path, ne);
+
+ if (ne < 2) {
+ DEBUG ("(III) Ignoring query on invalid path");
+ goto exit;
+ }
+
+ int i;
+
+ /* Identify Storage */
+ LIBMTP_devicestorage_t *storage;
+
+ int ret = LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED);
+ if (ret != 0) {
+ LIBMTP_Dump_Errorstack (device);
+ LIBMTP_Clear_Errorstack (device);
+ goto exit;
+ }
+ for (storage = device->storage; storage != 0; storage = storage->next) {
+ if (g_strcmp0 (elements[1], storage->StorageDescription) == 0) {
+ char *partial = build_partial_path (elements, 2);
+ add_cache_entry (backend, partial, storage->id, -1);
+ break;
+ }
+ }
+ if (!storage) {
+ DEBUG ("(III) Ignoring query on invalid storage");
+ goto exit;
+ }
+
+ long parent_id = -1;
+ for (i = 2; i < ne; i++) {
+ LIBMTP_file_t *f =
+ LIBMTP_Get_Files_And_Folders (device, storage->id, parent_id);
+ while (f != NULL) {
+ DEBUG_ENUMERATE ("(III) query (entity = %s, name = %s) ", f->filename, elements[i]);
+ if (strcmp (f->filename, elements[i]) == 0) {
+ file = f;
+ f = f->next;
+ char *partial = build_partial_path (elements, i + 1);
+ add_cache_entry (backend, partial, file->storage_id, file->item_id);
+ break;
+ } else {
+ LIBMTP_file_t *tmp = f;
+ f = f->next;
+ LIBMTP_destroy_file_t (tmp);
+ }
+ }
+ while (f != NULL) {
+ LIBMTP_file_t *tmp = f;
+ f = f->next;
+ LIBMTP_destroy_file_t (tmp);
+ }
+ if (!file) {
+ DEBUG ("(III) Ignoring query for non-existent file");
+ goto exit;
+ }
+ parent_id = file->item_id;
+ }
+
+ exit:
+ g_strfreev (elements);
+
+ DEBUG ("(III) add_cache_entries_for_filename done");
+}
+
+
+static CacheEntry *get_cache_entry (GVfsBackendMtp *backend,
+ const char *path)
+{
+ DEBUG ("(III) get_cache_entry: %s", path);
+ CacheEntry *entry = g_hash_table_lookup (backend->file_cache, path);
+ if (!entry) {
+ add_cache_entries_for_filename (backend, path);
+ entry = g_hash_table_lookup (backend->file_cache, path);
+ }
+ DEBUG ("(III) get_cache_entry done: %p", entry);
+ return entry;
+}
+
+
+static void
+remove_cache_entry (GVfsBackendMtp *backend,
+ const char *path)
+{
+ DEBUG ("(III) remove_cache_entry: %s", path);
+ g_hash_table_remove (backend->file_cache, path);
+ DEBUG ("(III) remove_cache_entry done");
+}
/************************************************
* Initialization
@@ -381,11 +529,27 @@ check_event (gpointer user_data)
case LIBMTP_EVENT_STORE_ADDED:
backend = g_weak_ref_get (event_ref);
if (backend && !g_atomic_int_get (&backend->unmount_started)) {
- path = g_strdup_printf ("/%u", param1);
+ LIBMTP_mtpdevice_t *device = backend->device;
+ LIBMTP_devicestorage_t *storage;
+
+ int ret = LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED);
+ if (ret != 0) {
+ LIBMTP_Dump_Errorstack (device);
+ LIBMTP_Clear_Errorstack (device);
+ break;
+ }
g_mutex_lock (&backend->mutex);
- g_hash_table_foreach (backend->monitors, emit_create_event, path);
+ for (storage = device->storage; storage != 0; storage = storage->next) {
+ if (storage->id == param1) {
+ path = g_build_filename ("/", storage->StorageDescription, NULL);
+ add_cache_entry (G_VFS_BACKEND_MTP (backend),
+ path,
+ storage->id,
+ -1);
+ g_hash_table_foreach (backend->monitors, emit_create_event, path);
+ }
+ }
g_mutex_unlock (&backend->mutex);
- g_free (path);
g_object_unref (backend);
break;
} else {
@@ -488,6 +652,8 @@ do_mount (GVfsBackend *backend,
g_signal_connect_object (op_backend->gudev_client, "uevent",
G_CALLBACK (on_uevent), op_backend, 0);
+ op_backend->file_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
LIBMTP_Init ();
get_device (backend, host, G_VFS_JOB (job));
@@ -533,6 +699,8 @@ do_unmount (GVfsBackend *backend, GVfsJobUnmount *job,
g_atomic_int_set (&op_backend->unmount_started, TRUE);
+ g_hash_table_unref (op_backend->file_cache);
+
g_source_remove (op_backend->hb_id);
g_signal_handler_disconnect (op_backend->gudev_client,
op_backend->on_uevent_id);
@@ -715,12 +883,9 @@ get_device_info (GVfsBackendMtp *backend, GFileInfo *info)
static void
get_storage_info (LIBMTP_devicestorage_t *storage, GFileInfo *info) {
- char *id = g_strdup_printf ("%u", storage->id);
- g_file_info_set_name (info, id);
- g_free (id);
-
DEBUG_ENUMERATE ("(II) get_storage_info: %s", storage->id);
+ g_file_info_set_name (info, storage->StorageDescription);
g_file_info_set_display_name (info, storage->StorageDescription);
g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
g_file_info_set_content_type (info, "inode/directory");
@@ -777,12 +942,9 @@ get_file_info (GVfsBackend *backend,
GIcon *icon = NULL;
char *content_type = NULL;
- char *id = g_strdup_printf ("%u", file->item_id);
- g_file_info_set_name (info, id);
- g_free (id);
-
DEBUG_ENUMERATE ("(II) get_file_info: %u", file->item_id);
+ g_file_info_set_name (info, file->filename);
g_file_info_set_display_name (info, file->filename);
switch (file->filetype) {
@@ -833,7 +995,6 @@ get_file_info (GVfsBackend *backend,
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, 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_string (info, G_FILE_ATTRIBUTE_STANDARD_COPY_NAME, file->filename);
if (icon != NULL) {
@@ -880,14 +1041,26 @@ do_enumerate (GVfsBackend *backend,
get_storage_info (storage, info);
g_vfs_job_enumerate_add_info (job, info);
g_object_unref (info);
+ add_cache_entry (G_VFS_BACKEND_MTP (backend),
+ g_build_filename (filename, storage->StorageDescription, NULL),
+ storage->id,
+ -1);
}
} else {
- LIBMTP_file_t *files;
-
- int pid = (ne == 2 ? -1 : strtol (elements[ne-1], NULL, 10));
+ CacheEntry *entry = get_cache_entry (G_VFS_BACKEND_MTP (backend),
+ filename);
+ if (entry == NULL) {
+ LIBMTP_Dump_Errorstack (device);
+ LIBMTP_Clear_Errorstack (device);
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ _("File not found"));
+ goto exit;
+ }
+ LIBMTP_file_t *files;
LIBMTP_Clear_Errorstack (device);
- files = LIBMTP_Get_Files_And_Folders (device, strtol (elements[1], NULL, 10), pid);
+ files = LIBMTP_Get_Files_And_Folders (device, entry->storage, entry->id);
if (files == NULL && LIBMTP_Get_Errorstack (device) != NULL) {
fail_job (G_VFS_JOB (job), device);
goto exit;
@@ -901,6 +1074,11 @@ do_enumerate (GVfsBackend *backend,
g_vfs_job_enumerate_add_info (job, info);
g_object_unref (info);
+ add_cache_entry (G_VFS_BACKEND_MTP (backend),
+ g_build_filename (filename, file->filename, NULL),
+ file->storage_id,
+ file->item_id);
+
LIBMTP_destroy_file_t (file);
}
}
@@ -960,53 +1138,6 @@ get_file_for_filename (LIBMTP_mtpdevice_t *device,
}
-/**
- * normalize_elements:
- *
- * Take a set of path elements and turn any file/directory names into
- * MTP entity IDs.
- *
- * Called with backend mutex lock held.
- */
-static void
-normalize_elements (LIBMTP_mtpdevice_t *device,
- gchar **elements,
- unsigned int ne)
-{
- DEBUG ("(II) normalize_elements (ne = %d)", ne);
- if (ne < 3) {
- /* In these cases, elements are always normal. */
- return;
- }
-
- unsigned int i;
- for (i = 2; i < ne; i++) {
- LIBMTP_file_t *file = NULL;
- char *endptr;
- long file_id = strtol (elements[i], &endptr, 10);
-
- if (file_id == 0 || *endptr != '\0') {
- file = get_file_for_filename(device, elements, i);
- if (file == NULL) {
- /* Missing entity. Cannot normalize. */
- DEBUG ("(II) Cannot normalize missing entity '%s'", elements[i]);
- continue;
- } else {
- char *item_id = g_strdup_printf ("%d", file->item_id);
- DEBUG ("(II) %s = %s", elements[i], item_id);
- g_free (elements[i]);
- elements[i] = item_id;
- LIBMTP_destroy_file_t (file);
- }
- } else {
- /* Already normal. */
- DEBUG ("(II) normal entity '%s'", elements[i]);
- continue;
- }
- }
- DEBUG ("(II) normalize_elements done");
-}
-
static void
do_query_info (GVfsBackend *backend,
GVfsJobQueryInfo *job,
@@ -1027,42 +1158,40 @@ do_query_info (GVfsBackend *backend,
if (ne == 2 && elements[1][0] == '\0') {
get_device_info (G_VFS_BACKEND_MTP (backend), info);
} else if (ne < 3) {
- LIBMTP_devicestorage_t *storage;
- int ret = LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED);
- if (ret != 0) {
+ CacheEntry *entry = get_cache_entry (G_VFS_BACKEND_MTP (backend),
+ filename);
+ if (!entry) {
LIBMTP_Dump_Errorstack (device);
LIBMTP_Clear_Errorstack (device);
g_vfs_job_failed_literal (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
- _("No storage volumes found"));
+ _("Storage not found"));
goto exit;
}
+
+ LIBMTP_devicestorage_t *storage;
for (storage = device->storage; storage != 0; storage = storage->next) {
- if (storage->id == strtol (elements[ne-1], NULL, 10)) {
+ if (storage->id == entry->storage) {
DEBUG ("(I) found storage %u", storage->id);
get_storage_info (storage, info);
}
}
} else {
- LIBMTP_file_t *file = NULL;
- char *endptr;
- long file_id = strtol (elements[ne - 1], &endptr, 10);
-
- if (file_id == 0 || *endptr != '\0') {
- file = get_file_for_filename (device, elements, ne - 1);
- if (file == NULL) {
- /* The backup query might have found nothing. */
- DEBUG ("(I) get_file_for_filename could not find '%s'",
- elements[ne - 1]);
- g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
- _("File not found"));
- goto exit;
- }
- } else {
- file = LIBMTP_Get_Filemetadata (device, file_id);
+ CacheEntry *entry = get_cache_entry (G_VFS_BACKEND_MTP (backend),
+ filename);
+ if (!entry) {
+ LIBMTP_Dump_Errorstack (device);
+ LIBMTP_Clear_Errorstack (device);
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ _("File not found"));
+ goto exit;
}
+
+ LIBMTP_file_t *file = NULL;
+ file = LIBMTP_Get_Filemetadata (device, entry->id);
+
if (file != NULL) {
get_file_info (backend, device, info, file);
LIBMTP_destroy_file_t (file);
@@ -1100,18 +1229,19 @@ do_query_fs_info (GVfsBackend *backend,
if (ne == 2 && elements[1][0] == '\0') {
get_device_info (G_VFS_BACKEND_MTP (backend), info);
} else {
- LIBMTP_devicestorage_t *storage;
- int ret = LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED);
- if (ret != 0) {
+ CacheEntry *entry = get_cache_entry (G_VFS_BACKEND_MTP (backend),
+ filename);
+ if (entry == NULL) {
LIBMTP_Dump_Errorstack (device);
LIBMTP_Clear_Errorstack (device);
g_vfs_job_failed_literal (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
- _("No storage volumes found"));
- goto exit;
+ _("File not found"));
}
+
+ LIBMTP_devicestorage_t *storage;
for (storage = device->storage; storage != 0; storage = storage->next) {
- if (storage->id == strtol (elements[1], NULL, 10)) {
+ if (storage->id == entry->storage) {
get_storage_info (storage, info);
}
}
@@ -1158,6 +1288,9 @@ do_make_directory (GVfsBackend *backend,
DEBUG ("(I) do_make_directory (filename = %s) ", filename);
g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
+ char *dir_name = g_path_get_dirname (filename);
+ char *base_name = g_path_get_basename (filename);
+
gchar **elements = g_strsplit_set (filename, "/", -1);
unsigned int ne = g_strv_length (elements);
@@ -1171,18 +1304,15 @@ do_make_directory (GVfsBackend *backend,
LIBMTP_mtpdevice_t *device;
device = G_VFS_BACKEND_MTP (backend)->device;
- /*
- * Might be called as part of a batch copy of a nested directory hierarchy.
- * New directories would then be referred to by name and not id.
- */
- normalize_elements(device, elements, ne - 1);
-
- int parent_id = 0;
- if (ne > 3) {
- parent_id = strtol (elements[ne-2], NULL, 10);
+ CacheEntry *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,
+ _("Destination directory not found"));
+ goto exit;
}
- int ret = LIBMTP_Create_Folder (device, elements[ne-1], parent_id, strtol (elements[1], NULL, 10));
+ int ret = LIBMTP_Create_Folder (device, base_name, entry->id, entry->storage);
if (ret == 0) {
fail_job (G_VFS_JOB (job), device);
goto exit;
@@ -1194,6 +1324,8 @@ do_make_directory (GVfsBackend *backend,
exit:
g_strfreev (elements);
+ g_free (dir_name);
+ g_free (base_name);
g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
DEBUG ("(I) do_make_directory done.");
@@ -1214,10 +1346,13 @@ do_pull (GVfsBackend *backend,
g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
GFileInfo *info = NULL;
- gchar **elements = g_strsplit_set (source, "/", -1);
- unsigned int ne = g_strv_length (elements);
-
- if (ne < 3) {
+ CacheEntry *entry = get_cache_entry (G_VFS_BACKEND_MTP (backend), source);
+ if (entry == NULL) {
+ g_vfs_job_failed_literal (G_VFS_JOB (job),
+ G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ _("File does not exist"));
+ 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,
_("Not a regular file"));
@@ -1227,7 +1362,7 @@ do_pull (GVfsBackend *backend,
LIBMTP_mtpdevice_t *device;
device = G_VFS_BACKEND_MTP (backend)->device;
- LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, strtol (elements[ne-1], NULL, 10));
+ 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,
@@ -1249,7 +1384,7 @@ do_pull (GVfsBackend *backend,
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,
- strtol (elements[ne-1], NULL, 10),
+ entry->id,
local_path,
(LIBMTP_progressfunc_t)mtp_progress,
&mtp_progress_data);
@@ -1260,14 +1395,18 @@ do_pull (GVfsBackend *backend,
/* Attempt to delete object if requested but don't fail it it fails. */
if (remove_source) {
DEBUG ("(I) Removing source.");
- LIBMTP_Delete_Object (device, strtol (elements[ne-1], NULL, 10));
+ 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 (&info);
- g_strfreev (elements);
g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
DEBUG ("(I) do_pull done.");
@@ -1287,6 +1426,9 @@ do_push (GVfsBackend *backend,
DEBUG ("(I) do_push (filename = %s, local_path = %s) ", destination, local_path);
g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
+ char *dir_name = g_path_get_dirname (destination);
+ char *filename = g_path_get_basename (destination);
+
GFile *file = NULL;
GFileInfo *info = NULL;
gchar **elements = g_strsplit_set (destination, "/", -1);
@@ -1302,16 +1444,12 @@ do_push (GVfsBackend *backend,
LIBMTP_mtpdevice_t *device;
device = G_VFS_BACKEND_MTP (backend)->device;
- /*
- * Might be called as part of a batch copy of a nested directory hierarchy.
- * New files would then be referred to by name and not id.
- */
- normalize_elements(device, elements, ne - 1);
-
- int parent_id = 0;
-
- if (ne > 3) {
- parent_id = strtol (elements[ne-2], NULL, 10);
+ CacheEntry *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,
+ _("Destination directory not found"));
+ goto exit;
}
file = g_file_new_for_path (local_path);
@@ -1338,9 +1476,9 @@ do_push (GVfsBackend *backend,
}
LIBMTP_file_t *mtpfile = LIBMTP_new_file_t ();
- mtpfile->filename = strdup (elements[ne-1]);
- mtpfile->parent_id = parent_id;
- mtpfile->storage_id = strtol (elements[1], NULL, 10);
+ mtpfile->filename = strdup (filename);
+ mtpfile->parent_id = entry->id;
+ mtpfile->storage_id = entry->storage;
mtpfile->filetype = LIBMTP_FILETYPE_UNKNOWN;
mtpfile->filesize = g_file_info_get_size (info);
@@ -1373,6 +1511,8 @@ do_push (GVfsBackend *backend,
g_clear_object (&file);
g_clear_object (&info);
g_strfreev (elements);
+ g_free (dir_name);
+ g_free (filename);
g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
DEBUG ("(I) do_push done.");
@@ -1387,20 +1527,23 @@ do_delete (GVfsBackend *backend,
DEBUG ("(I) do_delete (filename = %s) ", filename);
g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
- gchar **elements = g_strsplit_set (filename, "/", -1);
- unsigned int ne = g_strv_length (elements);
-
- if (ne < 3) {
+ CacheEntry *entry = get_cache_entry (G_VFS_BACKEND_MTP (backend), filename);
+ if (entry == NULL) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Cannot delete this entity"));
+ G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ _("File does not exist"));
+ 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,
+ _("Not a regular file"));
goto exit;
}
LIBMTP_mtpdevice_t *device;
device = G_VFS_BACKEND_MTP (backend)->device;
- int ret = LIBMTP_Delete_Object (device, strtol (elements[ne-1], NULL, 10));
+ int ret = LIBMTP_Delete_Object (device, entry->id);
if (ret != 0) {
fail_job (G_VFS_JOB (job), device);
goto exit;
@@ -1410,9 +1553,10 @@ do_delete (GVfsBackend *backend,
g_hash_table_foreach (G_VFS_BACKEND_MTP (backend)->monitors,
emit_delete_event,
(char *)filename);
+ remove_cache_entry (G_VFS_BACKEND_MTP (backend),
+ filename);
exit:
- g_strfreev (elements);
g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
DEBUG ("(I) do_delete done.");
@@ -1428,36 +1572,52 @@ do_set_display_name (GVfsBackend *backend,
DEBUG ("(I) do_set_display_name '%s' --> '%s' ", filename, display_name);
g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
- gchar **elements = g_strsplit_set (filename, "/", -1);
- unsigned int ne = g_strv_length (elements);
-
- if (ne < 3) {
+ CacheEntry *entry = get_cache_entry (G_VFS_BACKEND_MTP (backend), filename);
+ if (entry == NULL) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- _("Can't rename volume"));
+ G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ _("File does not exist"));
+ 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,
+ _("Not a regular file"));
goto exit;
}
LIBMTP_mtpdevice_t *device;
device = G_VFS_BACKEND_MTP (backend)->device;
- LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, strtol (elements[ne-1], NULL, 10));
+ LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, entry->id);
int ret = LIBMTP_Set_File_Name (device, file, display_name);
if (ret != 0) {
fail_job (G_VFS_JOB (job), device);
goto exit;
}
+
+ char *dir_name = g_path_get_dirname (filename);
+ char *new_name = g_build_filename (dir_name, display_name, NULL);
+
+ remove_cache_entry (G_VFS_BACKEND_MTP (backend),
+ filename);
+ add_cache_entry (G_VFS_BACKEND_MTP (backend), new_name, file->storage_id, file->item_id);
+
LIBMTP_destroy_file_t (file);
file = NULL;
- g_vfs_job_set_display_name_set_new_path (job, filename);
+
+ g_vfs_job_set_display_name_set_new_path (job, new_name);
g_vfs_job_succeeded (G_VFS_JOB (job));
g_hash_table_foreach (G_VFS_BACKEND_MTP (backend)->monitors,
- emit_change_event,
+ emit_create_event,
+ (char *)new_name);
+ g_hash_table_foreach (G_VFS_BACKEND_MTP (backend)->monitors,
+ emit_delete_event,
(char *)filename);
+ g_free (dir_name);
+ g_free (new_name);
exit:
- g_strfreev (elements);
g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
DEBUG ("(I) do_set_display_name done.");
@@ -1480,22 +1640,23 @@ do_open_for_read (GVfsBackend *backend,
DEBUG ("(I) do_open_for_read (%s)", filename);
g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
- gchar **elements = g_strsplit_set (filename, "/", -1);
- unsigned int ne = g_strv_length (elements);
-
- if (ne < 3) {
+ CacheEntry *entry = get_cache_entry (G_VFS_BACKEND_MTP (backend), filename);
+ if (entry == NULL) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Cannot open this entity"));
+ G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ _("File does not exist"));
+ 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,
+ _("Not a regular file"));
goto exit;
}
- unsigned int id = strtol (elements[ne-1], NULL, 10);
-
LIBMTP_mtpdevice_t *device;
device = G_VFS_BACKEND_MTP (backend)->device;
- LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, id);
+ LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, entry->id);
if (file == NULL) {
fail_job (G_VFS_JOB (job), device);
goto exit;
@@ -1503,7 +1664,7 @@ do_open_for_read (GVfsBackend *backend,
RWHandle *handle = g_new0(RWHandle, 1);
handle->handle_type = HANDLE_FILE;
- handle->id = id;
+ handle->id = entry->id;
handle->offset = 0;
handle->size = file->filesize;
@@ -1514,7 +1675,6 @@ do_open_for_read (GVfsBackend *backend,
g_vfs_job_succeeded (G_VFS_JOB (job));
exit:
- g_strfreev (elements);
g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
DEBUG ("(I) do_open_for_read done.");
@@ -1716,6 +1876,9 @@ do_create (GVfsBackend *backend,
DEBUG ("(I) do_create (%s)", filename);
g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
+ char *dir_name = g_path_get_dirname (filename);
+ char *basename = g_path_get_basename (filename);
+
gchar **elements = g_strsplit_set (filename, "/", -1);
unsigned int ne = g_strv_length (elements);
@@ -1726,35 +1889,27 @@ do_create (GVfsBackend *backend,
goto exit;
}
- int parent_id = 0;
-
- if (ne > 3) {
- parent_id = strtol (elements[ne-2], NULL, 10);
+ CacheEntry *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,
+ _("Destination directory not found"));
+ goto exit;
}
LIBMTP_mtpdevice_t *device;
device = G_VFS_BACKEND_MTP (backend)->device;
- unsigned int id = strtol (elements[ne-1], NULL, 10);
- char *existing_filename;
- LIBMTP_file_t *existing_file = LIBMTP_Get_Filemetadata (device, id);
- if (existing_file != NULL) {
- existing_filename = strdup (existing_file->filename);
- LIBMTP_destroy_file_t (existing_file);
- } else {
- existing_filename = strdup (elements[ne-1]);
- }
-
LIBMTP_file_t *mtpfile = LIBMTP_new_file_t ();
- mtpfile->filename = existing_filename;
- mtpfile->parent_id = parent_id;
- mtpfile->storage_id = strtol (elements[1], NULL, 10);
+ mtpfile->filename = strdup (basename);
+ mtpfile->parent_id = entry->id;
+ mtpfile->storage_id = entry->storage;
mtpfile->filetype = LIBMTP_FILETYPE_UNKNOWN;
mtpfile->filesize = 0;
int ret = LIBMTP_Send_File_From_Handler (device, zero_get_func, NULL,
mtpfile, NULL, NULL);
- id = mtpfile->item_id;
+ uint32_t id = mtpfile->item_id;
LIBMTP_destroy_file_t (mtpfile);
if (ret != 0) {
fail_job (G_VFS_JOB (job), device);
@@ -1779,8 +1934,13 @@ do_create (GVfsBackend *backend,
g_vfs_job_open_for_write_set_handle (G_VFS_JOB_OPEN_FOR_WRITE (job), handle);
g_vfs_job_succeeded (G_VFS_JOB (job));
+ g_hash_table_foreach (G_VFS_BACKEND_MTP (backend)->monitors,
+ emit_create_event,
+ (char *)filename);
exit:
g_strfreev (elements);
+ g_free (basename);
+ g_free (dir_name);
g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
DEBUG ("(I) do_create done.");
@@ -1803,29 +1963,30 @@ do_append_to (GVfsBackend *backend,
DEBUG ("(I) do_append_to (%s)", filename);
g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
- gchar **elements = g_strsplit_set (filename, "/", -1);
- unsigned int ne = g_strv_length (elements);
-
- if (ne < 3) {
+ CacheEntry *entry = get_cache_entry (G_VFS_BACKEND_MTP (backend), filename);
+ if (entry == NULL) {
g_vfs_job_failed_literal (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Cannot open this entity"));
+ G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ _("File does not exist"));
+ 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,
+ _("Not a regular file"));
goto exit;
}
- unsigned int id = strtol (elements[ne-1], NULL, 10);
-
LIBMTP_mtpdevice_t *device;
device = G_VFS_BACKEND_MTP (backend)->device;
- LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, id);
+ LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, entry->id);
if (file == NULL) {
fail_job (G_VFS_JOB (job), device);
DEBUG ("(I) Failed to get metadata.");
goto exit;
}
- int ret = LIBMTP_BeginEditObject (device, id);
+ int ret = LIBMTP_BeginEditObject (device, entry->id);
if (ret != 0) {
fail_job (G_VFS_JOB (job), device);
DEBUG ("(I) Failed to begin edit.");
@@ -1834,7 +1995,7 @@ do_append_to (GVfsBackend *backend,
RWHandle *handle = g_new0(RWHandle, 1);
handle->handle_type = HANDLE_FILE;
- handle->id = id;
+ handle->id = entry->id;
handle->offset = file->filesize;
handle->size = file->filesize;
@@ -1845,7 +2006,6 @@ do_append_to (GVfsBackend *backend,
g_vfs_job_succeeded (G_VFS_JOB (job));
exit:
- g_strfreev (elements);
g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
DEBUG ("(I) do_append_to done.");
@@ -1870,40 +2030,35 @@ do_replace (GVfsBackend *backend,
DEBUG ("(I) do_replace (%s)", filename);
g_mutex_lock (&G_VFS_BACKEND_MTP (backend)->mutex);
- gchar **elements = g_strsplit_set (filename, "/", -1);
- unsigned int ne = g_strv_length (elements);
-
- if (ne < 3) {
+ CacheEntry *entry = get_cache_entry (G_VFS_BACKEND_MTP (backend), filename);
+ if (entry == NULL) {
+ g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
+ 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_FAILED,
- _("Cannot open this entity"));
+ G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
+ _("Not a regular file"));
goto exit;
}
- unsigned int id = strtol (elements[ne-1], NULL, 10);
-
LIBMTP_mtpdevice_t *device;
device = G_VFS_BACKEND_MTP (backend)->device;
- LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, id);
+ LIBMTP_file_t *file = LIBMTP_Get_Filemetadata (device, entry->id);
if (file == NULL) {
- g_strfreev (elements);
- g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
- return do_create(backend, job, filename, flags);
-
fail_job (G_VFS_JOB (job), device);
DEBUG ("(I) Failed to get metadata.");
goto exit;
}
- int ret = LIBMTP_BeginEditObject (device, id);
+ int ret = LIBMTP_BeginEditObject (device, entry->id);
if (ret != 0) {
fail_job (G_VFS_JOB (job), device);
DEBUG ("(I) Failed to begin edit.");
goto exit;
}
- ret = LIBMTP_TruncateObject (device, id, 0);
+ ret = LIBMTP_TruncateObject (device, entry->id, 0);
if (ret != 0) {
fail_job (G_VFS_JOB (job), device);
DEBUG ("(I) Failed to truncate.");
@@ -1912,7 +2067,7 @@ do_replace (GVfsBackend *backend,
RWHandle *handle = g_new0(RWHandle, 1);
handle->handle_type = HANDLE_FILE;
- handle->id = id;
+ handle->id = entry->id;
handle->offset = 0;
handle->size = file->filesize;
@@ -1923,7 +2078,6 @@ do_replace (GVfsBackend *backend,
g_vfs_job_succeeded (G_VFS_JOB (job));
exit:
- g_strfreev (elements);
g_mutex_unlock (&G_VFS_BACKEND_MTP (backend)->mutex);
DEBUG ("(I) do_replace done.");
diff --git a/daemon/gvfsbackendmtp.h b/daemon/gvfsbackendmtp.h
index 0fe1decb..d7a7fe3c 100644
--- a/daemon/gvfsbackendmtp.h
+++ b/daemon/gvfsbackendmtp.h
@@ -54,6 +54,8 @@ struct _GVfsBackendMtp
LIBMTP_mtpdevice_t *device;
char *dev_path;
+ GHashTable *file_cache;
+
GHashTable *monitors;
guint hb_id;
gint unmount_started;