[gvfs] MTP: Use normal filenames in paths.



commit 79f94867d39bab750fb437ffe552f943cfda0add
Author: Philip Langdale <philipl overt org>
Date:   Sun Feb 24 15:42:02 2013 -0800

    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

 daemon/gvfsbackendmtp.c |  550 ++++++++++++++++++++++++++++++-----------------
 daemon/gvfsbackendmtp.h |    2 +
 2 files changed, 354 insertions(+), 198 deletions(-)
---
diff --git a/daemon/gvfsbackendmtp.c b/daemon/gvfsbackendmtp.c
index b047e0b..9e4f3f1 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 0fe1dec..d7a7fe3 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;


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