[Spice-devel] [phodav PATCH 1/3 v4] spice-webdavd-windows: Automount shared folder

Lukas Venhoda lvenhoda at redhat.com
Thu Mar 24 15:39:40 UTC 2016


Try to connect to shared folder automatically on Windows.

On each loop of run_service(), run a GTask, that waits for 0.5s.
If sharing is disabled, read_thread will immediately return and cause the main_loop to stop.
This in turn causes the cancellable object to be canceled and the loop stops.

If nothing happens for 0.5s (no cancel is called), it means, that sharing is enabled,
because read_thread (and main loop) is blocked, and we can call map_drive.

map_drive then enumerates free drive letters, and maps Spice Folder to highest possible
(from Z: to A:).

If all drive letters are already assigned, show a warning, but
don't stop the service. The folder can still be accessed trough other
means.
---
Changes since v3:
 - Added init_netresource and clear_netresource functions
    - Should clean up the map_drive function
    - Better handeling of adress to map/unmap
       - Not hardcoded port
 - Syntax cleanup
 - Changed criticals to warnings
 - Better TODO

Changes since v2:
 - Changed for loop to gpoll
 - split map_drive into 2 functions
 - added enum for return values of map_drive
 - added TODO for renaming the drive
 - added explanation for the 0.5 delay into commit log

Changes since v1:
 - Changed GThread to a GTask
 - Only wait half a second, instead of 5

Changes since RFC:
 - Calling WNetAddConnection2() blindly was slow and caause many threads to spawn.
    - Now only call it once, when it is sure, that it will connect.
 - Now connects to a * drive, instead of always Z:
 - Thread is now spawned and joined every time run_service() is called.
---
 Makefile.am           |   4 ++
 spice/spice-webdavd.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 123 insertions(+)

diff --git a/Makefile.am b/Makefile.am
index 6127f93..d8e2d53 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -87,6 +87,10 @@ spice_webdavd_LDADD =		\
 	$(PIE_LDFLAGS)		\
 	$(NULL)

+if OS_WIN32
+spice_webdavd_LDADD += -lnetapi32 -lmpr
+endif
+
 deps.txt:
 	$(AM_V_GEN)rpm -qa | grep $(host_os) | sort | unix2dos > $@

diff --git a/spice/spice-webdavd.c b/spice/spice-webdavd.c
index f9c5cf1..ee49469 100644
--- a/spice/spice-webdavd.c
+++ b/spice/spice-webdavd.c
@@ -737,11 +737,125 @@ open_mux_path (const char *path)
   mux_queue = output_queue_new (G_OUTPUT_STREAM (mux_ostream));
 }

+#ifdef G_OS_WIN32
+typedef enum TMapDriveEnum
+{
+  MAP_DRIVE_OK,
+  MAP_DRIVE_TRY_AGAIN,
+  MAP_DRIVE_ERROR
+} MapDriveEnum;
+
+static gchar
+get_free_drive_letter(void)
+{
+  const guint32 max_mask = 1 << 25;
+  guint32 drives;
+  guint32 mask;
+  gint i;
+
+  drives = GetLogicalDrives ();
+
+  for (i = 0; i < 26; i++)
+  {
+      mask = max_mask >> i;
+      if ((drives & mask) == 0)
+        return 'z' - i;
+  }
+
+  return 0;
+}
+
+/* User is required to call clear_netresource, when no longer needed. */
+static void
+init_netresource(NETRESOURCE * net_resource, const gchar drive_letter)
+{
+  net_resource->dwType = RESOURCETYPE_DISK;
+  net_resource->lpLocalName = g_strdup_printf("%c:", drive_letter);
+  net_resource->lpRemoteName = g_strdup_printf("http://localhost:%d/", port);
+  net_resource->lpProvider = NULL;
+}
+
+static void
+clear_netresource(NETRESOURCE * net_resource)
+{
+  g_free(net_resource->lpLocalName);
+  g_free(net_resource->lpRemoteName);
+}
+
+static MapDriveEnum
+map_drive(const gchar drive_letter)
+{
+  NETRESOURCE net_resource;
+  guint32 errn;
+
+  init_netresource(&net_resource, drive_letter);
+  errn = WNetAddConnection2 (&net_resource, NULL, NULL, CONNECT_TEMPORARY);
+  clear_netresource(&net_resource);
+
+  if (errn == NO_ERROR) {
+    g_debug ("map_drive ok");
+    return MAP_DRIVE_OK;
+  } else if (errn == ERROR_ALREADY_ASSIGNED) {
+    g_debug ("map_drive already asigned");
+    return MAP_DRIVE_TRY_AGAIN;
+  } else {
+    g_warning ("map_drive error %d", errn);
+    return MAP_DRIVE_ERROR;
+  }
+}
+
+static void
+map_drive_cb(GTask *task,
+             gpointer source_object,
+             gpointer task_data,
+             GCancellable *cancellable)
+{
+  guint32 timeout = 500; //half a second
+  GCancellable * cancel_map = task_data;
+  gchar drive_letter;
+  GPollFD cancel_pollfd;
+
+  if (!g_cancellable_make_pollfd (cancel_map, &cancel_pollfd)) {
+    g_critical ("GPollFD failed to create.");
+    return;
+  }
+
+  if (g_poll (&cancel_pollfd, 1, timeout) <= 0)
+  {
+    while (TRUE)
+    {
+      drive_letter = get_free_drive_letter ();
+      if (drive_letter == 0) {
+        g_warning ("all drive letters already assigned.");
+        break;
+      }
+
+      if (map_drive (drive_letter) != MAP_DRIVE_TRY_AGAIN) {
+        break;
+      }
+    }
+
+    //TODO: After mapping, rename network drive from \\localhost at PORT\DavWWWRoot to something like SPICE Shared Folder
+  }
+
+  g_cancellable_release_fd (cancel_map);
+  return;
+}
+#endif
+
 static void
 run_service (void)
 {
   g_debug ("Run service");

+#ifdef G_OS_WIN32
+  GCancellable * cancel_map = g_cancellable_new ();
+  GTask * map_drive_task = g_task_new (NULL, NULL, NULL, NULL);
+  g_task_set_task_data (map_drive_task, cancel_map, NULL);
+  g_task_run_in_thread (map_drive_task, map_drive_cb);
+  g_object_unref (map_drive_task);
+#endif
+
   g_socket_service_start (socket_service);

   cancel = g_cancellable_new ();
@@ -775,6 +889,11 @@ run_service (void)
   g_main_loop_run (loop);
   g_main_loop_unref (loop);

+#ifdef G_OS_WIN32
+  g_cancellable_cancel (cancel_map);
+  g_object_unref (cancel_map);
+#endif
+
   g_cancellable_cancel (cancel);

   g_clear_object (&mux_istream);
--
2.5.5



More information about the Spice-devel mailing list