[gvfs] gphoto2: Switch to a stable device uri



commit efc76d0cd84df8904ea661cb0e04728e3e6205d4
Author: Philip Langdale <philipl overt org>
Date:   Tue Apr 17 20:29:05 2018 -0700

    gphoto2: Switch to a stable device uri
    
    For the same reasons that the equivalent change to the mtp backend
    was desirable, we want to do this in the gphoto2 (ptp) backend.
    
    Stable URIs allow things like bookmarking and external scripting
    to work across plug/unplug events (which is to say, allows them
    to work at all).
    
    See the change description for the mtp backend for more details.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=795311

 daemon/gvfsbackendgphoto2.c             |   57 +++++++++++++++++++++++++----
 monitor/gphoto2/ggphoto2volumemonitor.c |   60 ++++++++++++++++++++++++++++---
 2 files changed, 105 insertions(+), 12 deletions(-)
---
diff --git a/daemon/gvfsbackendgphoto2.c b/daemon/gvfsbackendgphoto2.c
index 240b326..b31bc59 100644
--- a/daemon/gvfsbackendgphoto2.c
+++ b/daemon/gvfsbackendgphoto2.c
@@ -1210,6 +1210,49 @@ ensure_ignore_prefix (GVfsBackendGphoto2 *gphoto2_backend, GVfsJob *job)
 
 /* ------------------------------------------------------------------------------------------------- */
 
+static char *
+get_port_from_host (GVfsJob *job,
+                    GUdevClient *gudev_client,
+                    const char *host)
+{
+  guint32 bus_num = 0, dev_num = 0;
+  GList *devices, *l;
+
+  /* find corresponding GUdevDevice */
+  devices = g_udev_client_query_by_subsystem (gudev_client, "usb");
+  for (l = devices; l != NULL; l = l->next)
+    {
+      const char *id = g_udev_device_get_property (l->data, "ID_SERIAL");
+      if (g_strcmp0 (id, host) == 0)
+        {
+          bus_num = g_ascii_strtoull (g_udev_device_get_property (l->data, "BUSNUM"),
+                                      NULL, 10);
+          dev_num = g_ascii_strtoull (g_udev_device_get_property (l->data, "DEVNUM"),
+                                      NULL, 10);
+          break;
+        }
+    }
+  g_list_free_full (devices, g_object_unref);
+
+  if (bus_num && dev_num)
+    {
+      return g_strdup_printf ("usb:%03u,%03u", bus_num, dev_num);
+    }
+
+  /* For compatibility, handle old style host specifications. */
+  if (g_str_has_prefix (host, "[usb:") && host[strlen (host) - 1] == ']')
+    {
+      char *port = g_strdup (host + 1);
+      port[strlen (port) -1] = '\0';
+      return port;
+    }
+
+  g_vfs_job_failed_literal (G_VFS_JOB (job),
+                            G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                            _("Couldn’t find matching udev device."));
+  return NULL;
+}
+
 static void
 do_mount (GVfsBackend *backend,
          GVfsJobMount *job,
@@ -1251,16 +1294,16 @@ do_mount (GVfsBackend *backend,
 
   host = g_mount_spec_get (mount_spec, "host");
   g_debug ("  host='%s'\n", host);
-  if (host == NULL || strlen (host) < 3 || host[0] != '[' || host[strlen (host) - 1] != ']')
+
+  char *port = get_port_from_host (G_VFS_JOB (job),
+                                   gphoto2_backend->gudev_client,
+                                   host);
+  if (port == NULL)
     {
-      g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, _("No device specified"));
-      g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
-      g_error_free (error);
+      /* Job already set to failed */
       return;
     }
-
-  gphoto2_backend->gphoto2_port = g_strdup (host + 1);
-  gphoto2_backend->gphoto2_port[strlen (gphoto2_backend->gphoto2_port) - 1] = '\0';
+  gphoto2_backend->gphoto2_port = port;
 
   g_debug ("  decoded host='%s'\n", gphoto2_backend->gphoto2_port);
 
diff --git a/monitor/gphoto2/ggphoto2volumemonitor.c b/monitor/gphoto2/ggphoto2volumemonitor.c
index a9f51de..6bd9fc8 100644
--- a/monitor/gphoto2/ggphoto2volumemonitor.c
+++ b/monitor/gphoto2/ggphoto2volumemonitor.c
@@ -139,7 +139,10 @@ gudev_add_camera (GGPhoto2VolumeMonitor *monitor, GUdevDevice *device, gboolean
     GGPhoto2Volume *volume;
     GList *store_heads, *l;
     guint num_store_heads;
-    const char *usb_bus_num, *usb_device_num, *device_path;
+    const char *usb_bus_num, *usb_device_num, *usb_serial_id, *device_path;
+    gchar *prefix;
+    GFile *mount_prefix;
+    gboolean serial_conflict = FALSE;
 
     device_path = g_udev_device_get_device_file (device);
     if (!device_path)
@@ -157,6 +160,17 @@ gudev_add_camera (GGPhoto2VolumeMonitor *monitor, GUdevDevice *device, gboolean
       }
 #endif /* HAVE_LIBMTP */
 
+    /*
+     * We do not use ID_SERIAL_SHORT (the actualy device serial value) as
+     * this field is not populated when an ID_SERIAL has to be synthesized.
+     */
+    usb_serial_id = g_udev_device_get_property (device, "ID_SERIAL");
+    if (usb_serial_id == NULL)
+      {
+        g_warning ("device %s has no ID_SERIAL property, ignoring", device_path);
+        return;
+      }
+
     usb_bus_num = g_udev_device_get_property (device, "BUSNUM");
     if (usb_bus_num == NULL)
       {
@@ -171,9 +185,45 @@ gudev_add_camera (GGPhoto2VolumeMonitor *monitor, GUdevDevice *device, gboolean
         return;
       }
 
-    g_debug ("gudev_add_camera: camera device %s (bus: %s, device: %s)",
+    prefix = g_strdup_printf ("gphoto2://%s", usb_serial_id);
+    mount_prefix = g_file_new_for_uri (prefix);
+    g_free (prefix);
+
+    /*
+     * We do not support plugging in multiple devices that lack proper serial
+     * numbers. Linux will attempt to synthesize an ID based on the device
+     * product information, which will avoid collisions between different
+     * types of device, but two identical, serial-less, devices will still
+     * conflict.
+     */
+    for (l = monitor->camera_volumes; l != NULL; l = l->next)
+      {
+        GGPhoto2Volume *volume = G_GPHOTO2_VOLUME (l->data);
+
+        GFile *existing_root = g_volume_get_activation_root (G_VOLUME (volume));
+        if (g_file_equal (existing_root, mount_prefix) ||
+            g_file_has_prefix (existing_root, mount_prefix))
+          {
+            serial_conflict = TRUE;
+          }
+        g_object_unref (existing_root);
+        if (serial_conflict)
+          {
+            break;
+          }
+      }
+    g_object_unref (mount_prefix);
+    if (serial_conflict)
+      {
+        g_warning ("device %s has an identical ID_SERIAL value to an "
+                   "existing device. Multiple devices are not supported.",
+                   g_udev_device_get_device_file (device));
+        return;
+      }
+
+    g_debug ("gudev_add_camera: camera device %s (id: %s)",
              g_udev_device_get_device_file (device),
-             usb_bus_num, usb_device_num);
+             usb_serial_id);
 
     store_heads = get_stores_for_camera (usb_bus_num, usb_device_num);
     num_store_heads = g_list_length (store_heads);
@@ -189,11 +239,11 @@ gudev_add_camera (GGPhoto2VolumeMonitor *monitor, GUdevDevice *device, gboolean
          */
         if (num_store_heads == 1)
           {
-            uri = g_strdup_printf ("gphoto2://[usb:%s,%s]", usb_bus_num, usb_device_num);
+            uri = g_strdup_printf ("gphoto2://%s", usb_serial_id);
           }
         else
           {
-            uri = g_strdup_printf ("gphoto2://[usb:%s,%s]/%s", usb_bus_num, usb_device_num,
+            uri = g_strdup_printf ("gphoto2://%s/%s", usb_serial_id,
                                    store_path[0] == '/' ? store_path + 1 : store_path);
           }
         g_debug ("gudev_add_camera: ... adding URI for storage head: %s", uri);


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