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

Lukáš Venhoda lvenhoda at redhat.com
Thu Apr 21 15:28:07 UTC 2016


From: Lukas Venhoda <lvenhoda at redhat.com>

Try to connect to shared folder automatically on Windows.

On each loop of run_service(), run a GTask, that waits for half a second.
If read_thread() returns, it means, that sharing is not yet connected,
and the map_drive task is cancelled.

If the map_drive task is NOT cancelled, it enumerates free drive letters,
and maps Spice Folder to highest possible (from Z: to A:).

If all drive letters are already assigned, show a critical error, but
don't stop the service. The folder can still be accessed trough other
means.
---
Changes since v5:
 - fixed indentation
 - fixed * indentaion
 - removed return from void functions

Changes since v4:
 - Changed cancel_map to MapDriveData structure
 - Added error checking for GetLogicalDrives()
 - Moved defines and variables around
 - Renamed some functions and variables
 - Better debug messages

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 | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 146 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..88c29d0 100644
--- a/spice/spice-webdavd.c
+++ b/spice/spice-webdavd.c
@@ -737,11 +737,148 @@ open_mux_path (const char *path)
   mux_queue = output_queue_new (G_OUTPUT_STREAM (mux_ostream));
 }

+#ifdef G_OS_WIN32
+#define MAX_SHARED_FOLDER_NAME_SIZE 64
+#define MAX_DRIVE_LETTER_SIZE 3
+typedef enum _MapDriveEnum
+{
+  MAP_DRIVE_OK,
+  MAP_DRIVE_TRY_AGAIN,
+  MAP_DRIVE_ERROR
+} MapDriveEnum;
+
+typedef struct _MapDriveData
+{
+  GCancellable *cancel_map;
+} MapDriveData;
+
+static gchar
+get_free_drive_letter(void)
+{
+  const guint32 max_mask = 1 << 25;
+  guint32 drives;
+  gint i;
+
+  drives = GetLogicalDrives ();
+  if (drives == 0)
+    {
+      g_warning ("%s", g_win32_error_message (GetLastError ()));
+      return 0;
+    }
+
+  for (i = 0; i < 26; i++)
+    {
+      guint32 mask = max_mask >> i;
+      if ((drives & mask) == 0)
+        return 'z' - i;
+    }
+
+  return 0;
+}
+
+/* User is required to call netresource_free, when no longer needed. */
+static void
+netresource_init(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
+netresource_free(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;
+
+  netresource_init(&net_resource, drive_letter);
+  errn = WNetAddConnection2 (&net_resource, NULL, NULL, CONNECT_TEMPORARY);
+  netresource_free(&net_resource);
+
+  if (errn == NO_ERROR)
+    {
+      g_debug ("Shared folder mapped to %c succesfully", drive_letter);
+      return MAP_DRIVE_OK;
+    }
+  else if (errn == ERROR_ALREADY_ASSIGNED)
+    {
+      g_debug ("Drive letter %c is already assigned", drive_letter);
+      return MAP_DRIVE_TRY_AGAIN;
+    }
+
+  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)
+{
+  const guint32 delay = 500; //half a second
+  MapDriveData *map_drive_data = task_data;
+  gchar drive_letter;
+  GPollFD cancel_pollfd;
+  guint32 ret = 0;
+
+  if (!g_cancellable_make_pollfd (map_drive_data->cancel_map, &cancel_pollfd))
+    {
+      g_critical ("GPollFD failed to create.");
+      return;
+    }
+
+  ret = g_poll (&cancel_pollfd, 1, delay);
+  g_cancellable_release_fd (map_drive_data->cancel_map);
+
+  if (ret != 0)
+    {
+      return;
+    }
+
+  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
+    }
+
+  *(map_drive_data->drive_letter) = drive_letter;
+}
+#endif
+
 static void
 run_service (void)
 {
   g_debug ("Run service");

+#ifdef G_OS_WIN32
+  MapDriveData map_drive_data;
+  map_drive_data.cancel_map = g_cancellable_new ();
+  GTask *map_drive_task = g_task_new (NULL, NULL, NULL, NULL);
+  g_task_set_task_data (map_drive_task, &map_drive_data, 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 +912,11 @@ run_service (void)
   g_main_loop_run (loop);
   g_main_loop_unref (loop);

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

   g_clear_object (&mux_istream);
--
2.5.5



More information about the Spice-devel mailing list