[Spice-devel] [phodav PATCH 1/2 v2] spice-webdavd: Automount shared folder on Windows
Marc-André Lureau
marcandre.lureau at gmail.com
Thu Feb 25 15:21:53 UTC 2016
Hi
On Thu, Feb 25, 2016 at 3:19 PM, Lukas Venhoda <lvenhoda at redhat.com> wrote:
> 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 shared folder to highest possible (from Z: to A:).
>
> If all drive letters are already assigned, stop the service.
> ---
> 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 | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 85 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..55b7bbb 100644
> --- a/spice/spice-webdavd.c
> +++ b/spice/spice-webdavd.c
> @@ -737,11 +737,88 @@ open_mux_path (const char *path)
> mux_queue = output_queue_new (G_OUTPUT_STREAM (mux_ostream));
> }
>
> +#ifdef G_OS_WIN32
> +gchar drive_letter;
> +
> +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;
> +}
> +
> +static void
> +map_drive(GTask *task,
> + gpointer source_object,
> + gpointer task_data,
> + GCancellable *cancellable)
> +{
> + static int loop_time = G_USEC_PER_SEC/10;
> + GCancellable * cancel_map = task_data;
> + gchar local_name[3];
> + gchar remote_name[] = "http://localhost:9843/";
> + NETRESOURCE net_resource;
> + guint32 retval;
> + gint i;
> +
> + for (i = 0; i < 5; ++i)
> + {
> + if (g_cancellable_is_cancelled (cancel_map))
> + return;
> + else
> + g_usleep (loop_time);
> + }
> +
I am not sure I see the point of waiting 0.5s here. Add a comment? (a
better way would be to use g_poll with the pollfd cancellable can
creats: no need to loop)
> + if ((drive_letter = get_free_drive_letter ()) == 0)
> + g_error ("all drive letters already assigned.");
This will abort(), I think it's better to have simply a critical. The
webdav folder can be accessed through other means (browser etc)
> + local_name[0] = drive_letter;
> + local_name[1] = ':';
> + local_name[2] = 0;
This is not strictly equivalent to "net use *", it will be racy. It
probably needs to retry if the localname is already used.
> + net_resource.dwType = RESOURCETYPE_DISK;
> + net_resource.lpLocalName = local_name;
> + net_resource.lpRemoteName = remote_name;
> + net_resource.lpProvider = NULL;
> +
> + retval = WNetAddConnection2 (&net_resource, NULL, NULL, CONNECT_TEMPORARY);
> +
> + if (retval == NO_ERROR)
> + g_debug ("map_drive ok");
> + else if (retval == ERROR_ALREADY_ASSIGNED)
> + g_debug ("map_drive already asigned");
here, goto getfreedriver?
> + else
> + g_error ("map_drive error %d", retval);
Please avoid g_error for non-fatal issues.
The map-driver.bat file uses a nifty hack to name the folder "Spice
client" (Spice folder would make more sense). It would be really nice
to name the folder here too.
> +
> + 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);
no unref? you probably should g_task_return() somewhere too.
> + g_task_set_task_data (map_drive_task, cancel_map, NULL);
> + g_task_run_in_thread (map_drive_task, map_drive);
I think you could unref after this call, since run_in_thread() keeps a ref.
Before adding and calling map_drive, I think you should implement the
function to check if there is already a mapping.
> +#endif
> +
> g_socket_service_start (socket_service);
>
> cancel = g_cancellable_new ();
> @@ -775,6 +852,10 @@ run_service (void)
> g_main_loop_run (loop);
> g_main_loop_unref (loop);
>
> +#ifdef G_OS_WIN32
> + g_cancellable_cancel (cancel_map);
no unref either?
> +#endif
> +
> g_cancellable_cancel (cancel);
>
> g_clear_object (&mux_istream);
> --
> 2.5.0
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/spice-devel
--
Marc-André Lureau
More information about the Spice-devel
mailing list