VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc.cpp@ 82506

Last change on this file since 82506 was 82506, checked in by vboxsync, 5 years ago

SharedClipboardSvc,Vbgl: Reviewed and adjusted the handling of the VBOX_SHCL_GUEST_FN_DATA_WRITE message, paddling back two thirds of the parameter changes from the 6.1 dev cycle and fixing a couple of bugs introduced (buggy code commented out and marked with @todos). Fixing the broadcast approach of VBOX_SHCL_GUEST_FN_FORMATS_REPORT as the host doesn't want to know about clipboard stuff when VRDE clients are using the clipboard. Also documented the message. bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 89.3 KB
Line 
1/* $Id: VBoxSharedClipboardSvc.cpp 82506 2019-12-09 03:18:31Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Host service entry points.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/** @page pg_hostclip The Shared Clipboard Host Service
20 *
21 * The shared clipboard host service is the host half of the clibpoard proxying
22 * between the host and the guest. The guest parts live in VBoxClient, VBoxTray
23 * and VBoxService depending on the OS, with code shared between host and guest
24 * under src/VBox/GuestHost/SharedClipboard/.
25 *
26 * The service is split into a platform-independent core and platform-specific
27 * backends. The service defines two communication protocols - one to
28 * communicate with the clipboard service running on the guest, and one to
29 * communicate with the backend. These will be described in a very skeletal
30 * fashion here.
31 *
32 * r=bird: The "two communication protocols" does not seems to be factual, there
33 * is only one protocol, the first one mentioned. It cannot be backend
34 * specific, because the guest/host protocol is platform and backend agnostic in
35 * nature. You may call it versions, but I take a great dislike to "protocol
36 * versions" here, as you've just extended the existing protocol with a feature
37 * that allows to transfer files and directories too. See @bugref{9437#c39}.
38 *
39 *
40 * @section sec_hostclip_guest_proto The guest communication protocol
41 *
42 * The guest clipboard service communicates with the host service over HGCM
43 * (the host is a HGCM service). HGCM is connection based, so the guest side
44 * has to connect before anything else can be done. (Windows hosts currently
45 * only support one simultaneous connection.) Once it has connected, it can
46 * send messages to the host services, some of which will receive immediate
47 * replies from the host, others which will block till a reply becomes
48 * available. The latter is because HGCM does't allow the host to initiate
49 * communication, it must be guest triggered. The HGCM service is single
50 * threaded, so it doesn't matter if the guest tries to send lots of requests in
51 * parallel, the service will process them one at the time.
52 *
53 * There are currently four messages defined. The first is
54 * VBOX_SHCL_GUEST_FN_MSG_GET / VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD, which waits
55 * for a message from the host. If a host message is sent while the guest is
56 * not waiting, it will be queued until the guest requests it. The host code
57 * only supports a single simultaneous GET call from one client guest.
58 *
59 * The second guest message is VBOX_SHCL_GUEST_FN_FORMATS_REPORT, which tells
60 * the host that the guest has new clipboard data available. The third is
61 * VBOX_SHCL_GUEST_FN_DATA_READ, which asks the host to send its clipboard data
62 * and waits until it arrives. The host supports at most one simultaneous
63 * VBOX_SHCL_GUEST_FN_DATA_READ call from a guest - if a second call is made
64 * before the first has returned, the first will be aborted.
65 *
66 * The last guest message is VBOX_SHCL_GUEST_FN_DATA_WRITE, which is used to
67 * send the contents of the guest clipboard to the host. This call should be
68 * used after the host has requested data from the guest.
69 *
70 *
71 * @section sec_hostclip_backend_proto The communication protocol with the
72 * platform-specific backend
73 *
74 * The initial protocol implementation (called protocol v0) was very simple,
75 * and could only handle simple data (like copied text and so on). It also
76 * was limited to two (2) fixed parameters at all times.
77 *
78 * Since VBox 6.1 a newer protocol (v1) has been established to also support
79 * file transfers. This protocol uses a (per-client) message queue instead
80 * (see VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD vs. VBOX_SHCL_GUEST_FN_GET_HOST_MSG).
81 *
82 * To distinguish the old (legacy) or new(er) protocol, the VBOX_SHCL_GUEST_FN_CONNECT
83 * message has been introduced. If an older guest does not send this message,
84 * an appropriate translation will be done to serve older Guest Additions (< 6.1).
85 *
86 * The protocol also support out-of-order messages by using so-called "context IDs",
87 * which are generated by the host. A context ID consists of a so-called "source event ID"
88 * and a so-called "event ID". Each HGCM client has an own, random, source event ID and
89 * generates non-deterministic event IDs so that the guest side does not known what
90 * comes next; the guest side has to reply with the same conext ID which was sent by
91 * the host request.
92 *
93 * Also see the protocol changelog at VBoxShClSvc.h.
94 *
95 *
96 * @section sec_uri_intro Transferring files
97 *
98 * Since VBox x.x.x transferring files via Shared Clipboard is supported.
99 * See the VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS define for supported / enabled
100 * platforms. This is called "Shared Clipboard transfers".
101 *
102 * Copying files / directories from guest A to guest B requires the host
103 * service to act as a proxy and cache, as we don't allow direct VM-to-VM
104 * communication. Copying from / to the host also is taken into account.
105 *
106 * At the moment a transfer is a all-or-nothing operation, e.g. it either
107 * completes or fails completely. There might be callbacks in the future
108 * to e.g. skip failing entries.
109 *
110 * Known limitations:
111 *
112 * - Support for VRDE (VRDP) is not implemented yet (see #9498).
113 * - Unicode support on Windows hosts / guests is not enabled (yet).
114 * - Symbolic links / Windows junctions are not allowed.
115 * - Windows alternate data streams (ADS) are not allowed.
116 * - No support for ACLs yet.
117 * - No (maybe never) support for NT4.
118 *
119 * @section sec_transfers_areas Clipboard areas.
120 *
121 * For larger / longer transfers there might be file data
122 * temporarily cached on the host, which has not been transferred to the
123 * destination yet. Such a cache is called a "Shared Clipboard Area", which
124 * in turn is identified by a unique ID across all VMs running on the same
125 * host. To control the access (and needed cleanup) of such clipboard areas,
126 * VBoxSVC (Main) is used for this task. A Shared Clipboard client can register,
127 * unregister, attach to and detach from a clipboard area. If all references
128 * to a clipboard area are released, a clipboard area gets detroyed automatically
129 * by VBoxSVC.
130 *
131 * By default a clipboard area lives in the user's temporary directory in the
132 * sub folder "VirtualBox Shared Clipboards/clipboard-<ID>". VBoxSVC does not
133 * do any file locking in a clipboard area, but keeps the clipboard areas's
134 * directory open to prevent deletion by third party processes.
135 *
136 * @todo We might use some VFS / container (IPRT?) for this instead of the
137 * host's file system directly?
138 * bird> Yes, but may take some work as we don't have the pick and choose
139 * kind of VFS containers implemented yet.
140 *
141 * @section sec_transfer_structure Transfer handling structure
142 *
143 * All structures / classes are designed for running on both, on the guest
144 * (via VBoxTray / VBoxClient) or on the host (host service) to avoid code
145 * duplication where applicable.
146 *
147 * Per HGCM client there is a so-called "transfer context", which in turn can
148 * have one or mulitple so-called "Shared Clipboard transfer" objects. At the
149 * moment we only support on concurrent Shared Clipboard transfer per transfer
150 * context. It's being used for reading from a source or writing to destination,
151 * depening on its direction. An Shared Clipboard transfer can have optional
152 * callbacks which might be needed by various implementations. Also, transfers
153 * optionally can run in an asynchronous thread to prevent blocking the UI while
154 * running.
155 *
156 * A Shared Clipboard transfer can maintain its own clipboard area; for the host
157 * service such a clipboard area is coupled to a clipboard area registered or
158 * attached with VBoxSVC. This is needed because multiple transfers from
159 * multiple VMs (n:n) can rely on the same clipboard area, so there needs a
160 * master keeping tracking of a clipboard area. To minimize IPC traffic only the
161 * minimum de/attaching is done at the moment. A clipboard area gets cleaned up
162 * (i.e. physically deleted) if no references are held to it anymore, or if
163 * VBoxSVC goes down.
164 *
165 * @section sec_transfer_providers Transfer providers
166 *
167 * For certain implementations (for example on Windows guests / hosts, using
168 * IDataObject and IStream objects) a more flexible approach reqarding reading /
169 * writing is needed. For this so-called transfer providers abstract the way of how
170 * data is being read / written in the current context (host / guest), while
171 * the rest of the code stays the same.
172 *
173 * @section sec_transfer_protocol Transfer protocol
174 *
175 * The host service issues commands which the guest has to respond with an own
176 * message to. The protocol itself is designed so that it has primitives to list
177 * directories and open/close/read/write file system objects.
178 *
179 * Note that this is different from the DnD approach, as Shared Clipboard transfers
180 * need to be deeper integrated within the host / guest OS (i.e. for progress UI),
181 * and this might require non-monolithic / random access APIs to achieve.
182 *
183 * As there can be multiple file system objects (fs objects) selected for transfer,
184 * a transfer can be queried for its root entries, which then contains the top-level
185 * elements. Based on these elements, (a) (recursive) listing(s) can be performed
186 * to (partially) walk down into directories and query fs object information. The
187 * provider provides appropriate interface for this, even if not all implementations
188 * might need this mechanism.
189 *
190 * An Shared Clipboard transfer has three stages:
191 * - 1. Announcement: An Shared Clipboard transfer-compatible format (currently only one format available)
192 * has been announced, the destination side creates a transfer object, which then,
193 * depending on the actual implementation, can be used to tell the OS that
194 * there is transfer (file) data available.
195 * At this point this just acts as a (kind-of) promise to the OS that we
196 * can provide (file) data at some later point in time.
197 *
198 * - 2. Initialization: As soon as the OS requests the (file) data, mostly triggered
199 * by the user starting a paste operation (CTRL + V), the transfer get initialized
200 * on the destination side, which in turn lets the source know that a transfer
201 * is going to happen.
202 *
203 * - 3. Transfer: At this stage the actual transfer from source to the destination takes
204 * place. How the actual transfer is structurized (e.g. which files / directories
205 * are transferred in which order) depends on the destination implementation. This
206 * is necessary in order to fulfill requirements on the destination side with
207 * regards to ETA calculation or other dependencies.
208 * Both sides can abort or cancel the transfer at any time.
209 */
210
211
212/*********************************************************************************************************************************
213* Header Files *
214*********************************************************************************************************************************/
215#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
216#include <VBox/log.h>
217
218#include <VBox/GuestHost/clipboard-helper.h>
219#include <VBox/HostServices/Service.h>
220#include <VBox/HostServices/VBoxClipboardSvc.h>
221#include <VBox/HostServices/VBoxClipboardExt.h>
222
223#include <VBox/AssertGuest.h>
224#include <VBox/err.h>
225#include <VBox/VMMDev.h>
226#include <VBox/vmm/ssm.h>
227
228#include <iprt/mem.h>
229#include <iprt/string.h>
230#include <iprt/assert.h>
231#include <iprt/critsect.h>
232#include <iprt/rand.h>
233
234#include "VBoxSharedClipboardSvc-internal.h"
235#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
236# include "VBoxSharedClipboardSvc-transfers.h"
237#endif
238
239using namespace HGCM;
240
241
242/*********************************************************************************************************************************
243* Defined Constants And Macros *
244*********************************************************************************************************************************/
245/** @name The saved state versions for the shared clipboard service.
246 *
247 * @note We set bit 31 because prior to version 0x80000002 there would be a
248 * structure size rather than a version number. Setting bit 31 dispells
249 * any possible ambiguity.
250 *
251 * @{ */
252/** The current saved state version. */
253#define VBOX_SHCL_SAVED_STATE_VER_CURRENT VBOX_SHCL_SAVED_STATE_VER_6_1RC1
254/** Adds the client's POD state and client state flags.
255 * @since 6.1 RC1 */
256#define VBOX_SHCL_SAVED_STATE_VER_6_1RC1 UINT32_C(0x80000004)
257/** First attempt saving state during @bugref{9437} development.
258 * @since 6.1 BETA 2 */
259#define VBOX_SHCL_SAVED_STATE_VER_6_1B2 UINT32_C(0x80000003)
260/** First structured version.
261 * @since 3.1 / r53668 */
262#define VBOX_SHCL_SAVED_STATE_VER_3_1 UINT32_C(0x80000002)
263/** This was just a state memory dump, including pointers and everything.
264 * @note This is not supported any more. Sorry. */
265#define VBOX_SHCL_SAVED_STATE_VER_NOT_SUPP (ARCH_BITS == 64 ? UINT32_C(72) : UINT32_C(48))
266/** @} */
267
268
269/*********************************************************************************************************************************
270* Global Variables *
271*********************************************************************************************************************************/
272PVBOXHGCMSVCHELPERS g_pHelpers;
273
274static RTCRITSECT g_CritSect;
275/** Global Shared Clipboard mode. */
276static uint32_t g_uMode = VBOX_SHCL_MODE_OFF;
277#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
278/** Global Shared Clipboard (file) transfer mode. */
279uint32_t g_fTransferMode = VBOX_SHCL_TRANSFER_MODE_DISABLED;
280#endif
281
282/** Is the clipboard running in headless mode? */
283static bool g_fHeadless = false;
284
285/** Holds the service extension state. */
286SHCLEXTSTATE g_ExtState = { 0 };
287
288/** Global map of all connected clients. */
289ClipboardClientMap g_mapClients;
290
291/** Global list of all clients which are queued up (deferred return) and ready
292 * to process new commands. The key is the (unique) client ID. */
293ClipboardClientQueue g_listClientsDeferred;
294
295/** Host feature mask for VBOX_SHCL_GUEST_FN_REPORT_FEATURES/VBOX_SHCL_GUEST_FN_QUERY_FEATURES. */
296static uint64_t const g_fHostFeatures0 = VBOX_SHCL_HF_NONE;
297
298
299/**
300 * Returns the current Shared Clipboard service mode.
301 *
302 * @returns Current Shared Clipboard service mode.
303 */
304uint32_t ShClSvcGetMode(void)
305{
306 return g_uMode;
307}
308
309/**
310 * Getter for headless setting. Also needed by testcase.
311 *
312 * @returns Whether service currently running in headless mode or not.
313 */
314bool ShClSvcGetHeadless(void)
315{
316 return g_fHeadless;
317}
318
319static int shClSvcModeSet(uint32_t uMode)
320{
321 int rc = VERR_NOT_SUPPORTED;
322
323 switch (uMode)
324 {
325 case VBOX_SHCL_MODE_OFF:
326 RT_FALL_THROUGH();
327 case VBOX_SHCL_MODE_HOST_TO_GUEST:
328 RT_FALL_THROUGH();
329 case VBOX_SHCL_MODE_GUEST_TO_HOST:
330 RT_FALL_THROUGH();
331 case VBOX_SHCL_MODE_BIDIRECTIONAL:
332 {
333 g_uMode = uMode;
334
335 rc = VINF_SUCCESS;
336 break;
337 }
338
339 default:
340 {
341 g_uMode = VBOX_SHCL_MODE_OFF;
342 break;
343 }
344 }
345
346 LogFlowFuncLeaveRC(rc);
347 return rc;
348}
349
350bool ShClSvcLock(void)
351{
352 return RT_SUCCESS(RTCritSectEnter(&g_CritSect));
353}
354
355void ShClSvcUnlock(void)
356{
357 int rc2 = RTCritSectLeave(&g_CritSect);
358 AssertRC(rc2);
359}
360
361/**
362 * Resets a client's state message queue.
363 *
364 * @param pClient Pointer to the client data structure to reset message queue for.
365 */
366void shClSvcMsgQueueReset(PSHCLCLIENT pClient)
367{
368 LogFlowFuncEnter();
369
370 while (!pClient->queueMsg.isEmpty())
371 {
372 RTMemFree(pClient->queueMsg.last());
373 pClient->queueMsg.removeLast();
374 }
375}
376
377/**
378 * Allocates a new clipboard message.
379 *
380 * @returns Allocated clipboard message, or NULL on failure.
381 * @param uMsg Message type of message to allocate.
382 * @param cParms Number of HGCM parameters to allocate.
383 */
384PSHCLCLIENTMSG shClSvcMsgAlloc(uint32_t uMsg, uint32_t cParms)
385{
386 PSHCLCLIENTMSG pMsg = (PSHCLCLIENTMSG)RTMemAlloc(sizeof(SHCLCLIENTMSG));
387 if (pMsg)
388 {
389 pMsg->paParms = (PVBOXHGCMSVCPARM)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * cParms);
390 if (pMsg->paParms)
391 {
392 pMsg->cParms = cParms;
393 pMsg->uMsg = uMsg;
394
395 return pMsg;
396 }
397 }
398
399 RTMemFree(pMsg);
400 return NULL;
401}
402
403/**
404 * Frees a formerly allocated clipboard message.
405 *
406 * @param pMsg Clipboard message to free.
407 * The pointer will be invalid after calling this function.
408 */
409void shClSvcMsgFree(PSHCLCLIENTMSG pMsg)
410{
411 if (!pMsg)
412 return;
413
414 if (pMsg->paParms)
415 RTMemFree(pMsg->paParms);
416
417 RTMemFree(pMsg);
418 pMsg = NULL;
419}
420
421/**
422 * Sets the VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT and VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT
423 * return parameters.
424 *
425 * @param pMsg Message to set return parameters to.
426 * @param paDstParms The peek parameter vector.
427 * @param cDstParms The number of peek parameters (at least two).
428 * @remarks ASSUMES the parameters has been cleared by clientMsgPeek.
429 */
430void shClSvcMsgSetPeekReturn(PSHCLCLIENTMSG pMsg, PVBOXHGCMSVCPARM paDstParms, uint32_t cDstParms)
431{
432 Assert(cDstParms >= 2);
433 if (paDstParms[0].type == VBOX_HGCM_SVC_PARM_32BIT)
434 paDstParms[0].u.uint32 = pMsg->uMsg;
435 else
436 paDstParms[0].u.uint64 = pMsg->uMsg;
437 paDstParms[1].u.uint32 = pMsg->cParms;
438
439 uint32_t i = RT_MIN(cDstParms, pMsg->cParms + 2);
440 while (i-- > 2)
441 switch (pMsg->paParms[i - 2].type)
442 {
443 case VBOX_HGCM_SVC_PARM_32BIT: paDstParms[i].u.uint32 = ~(uint32_t)sizeof(uint32_t); break;
444 case VBOX_HGCM_SVC_PARM_64BIT: paDstParms[i].u.uint32 = ~(uint32_t)sizeof(uint64_t); break;
445 case VBOX_HGCM_SVC_PARM_PTR: paDstParms[i].u.uint32 = pMsg->paParms[i - 2].u.pointer.size; break;
446 }
447}
448
449/**
450 * Sets the VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD return parameters.
451 *
452 * This function does the necessary translation between the legacy protocol (<= VBox 6.0) and the new protocols (>= VBox 6.1),
453 * as messages are always stored as >= v1 messages in the message queue.
454 *
455 * @returns VBox status code.
456 * @param pMsg Message to set return parameters to.
457 * @param paDstParms The peek parameter vector.
458 * @param cDstParms The number of peek parameters (at least two).
459 * @param pfRemove Returns whether the message can be removed from the queue or not.
460 * This is needed for certain messages which need to stay around for more than one (guest) call.
461 */
462int shClSvcMsgSetGetHostMsgOldReturn(PSHCLCLIENTMSG pMsg, PVBOXHGCMSVCPARM paDstParms, uint32_t cDstParms,
463 bool *pfRemove)
464{
465 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
466 AssertPtrReturn(paDstParms, VERR_INVALID_POINTER);
467 AssertReturn (cDstParms >= 2, VERR_INVALID_PARAMETER);
468 AssertPtrReturn(pfRemove, VERR_INVALID_POINTER);
469
470 int rc = VINF_SUCCESS;
471
472 bool fRemove = true;
473
474 LogFlowFunc(("uMsg=%RU32 (%s), cParms=%RU32\n",
475 pMsg->uMsg, ShClHostMsgToStr(pMsg->uMsg), pMsg->cParms));
476
477 switch (pMsg->uMsg)
478 {
479 case VBOX_SHCL_HOST_MSG_QUIT:
480 {
481 HGCMSvcSetU32(&paDstParms[0], VBOX_SHCL_HOST_MSG_QUIT);
482 HGCMSvcSetU32(&paDstParms[1], 0 /* Not used */);
483 break;
484 }
485
486 case VBOX_SHCL_HOST_MSG_READ_DATA:
487 {
488 HGCMSvcSetU32(&paDstParms[0], VBOX_SHCL_HOST_MSG_READ_DATA);
489 AssertBreakStmt(pMsg->cParms >= 2, rc = VERR_INVALID_PARAMETER); /* Paranoia. */
490 uint32_t uSrcFmt = VBOX_SHCL_FMT_NONE;
491 uint32_t uDstFmt = VBOX_SHCL_FMT_NONE;
492 rc = HGCMSvcGetU32(&pMsg->paParms[2] /* uFormat */, &uSrcFmt);
493 if (RT_SUCCESS(rc))
494 {
495 if (uSrcFmt & VBOX_SHCL_FMT_UNICODETEXT)
496 uDstFmt = VBOX_SHCL_FMT_UNICODETEXT;
497 else if (uSrcFmt & VBOX_SHCL_FMT_BITMAP)
498 uDstFmt = VBOX_SHCL_FMT_BITMAP;
499 else if (uSrcFmt & VBOX_SHCL_FMT_HTML)
500 uDstFmt = VBOX_SHCL_FMT_HTML;
501 else
502 AssertStmt(uSrcFmt == VBOX_SHCL_FMT_NONE, uSrcFmt = VBOX_SHCL_FMT_NONE);
503
504 LogFlowFunc(("uSrcFmt=0x%x, uDstFmt=0x%x\n", uSrcFmt, uDstFmt));
505
506 /* Remove format we're going to return from the queued message. */
507 uSrcFmt &= ~uDstFmt;
508 HGCMSvcSetU32(&pMsg->paParms[2], uSrcFmt);
509
510 /* Only report back one format at a time. */
511 HGCMSvcSetU32(&paDstParms[1], uDstFmt);
512
513 /* If no more formats are left, the message can be removed finally. */
514 fRemove = uSrcFmt == VBOX_SHCL_FMT_NONE;
515
516 LogFlowFunc(("uSrcFmt=0x%x, fRemove=%RTbool\n", uSrcFmt, fRemove));
517 }
518 break;
519 }
520
521 case VBOX_SHCL_HOST_MSG_FORMATS_REPORT:
522 {
523 HGCMSvcSetU32(&paDstParms[0], VBOX_SHCL_HOST_MSG_FORMATS_REPORT);
524 AssertBreakStmt(pMsg->cParms >= 2, rc = VERR_INVALID_PARAMETER); /* Paranoia. */
525 uint32_t uFmts;
526 rc = HGCMSvcGetU32(&pMsg->paParms[1] /* uFormats */, &uFmts);
527 if (RT_SUCCESS(rc))
528 HGCMSvcSetU32(&paDstParms[1], uFmts);
529 break;
530 }
531
532 default:
533 AssertFailed(); /* Not supported by legacy protocol. */
534 rc = VERR_NOT_SUPPORTED;
535 break;
536 }
537
538 *pfRemove = fRemove;
539
540 LogFlowFuncLeaveRC(rc);
541 return rc;
542}
543
544/**
545 * Adds a new message to a client'S message queue.
546 *
547 * @returns IPRT status code.
548 * @param pClient Pointer to the client data structure to add new message to.
549 * @param pMsg Pointer to message to add. The queue then owns the pointer.
550 * @param fAppend Whether to append or prepend the message to the queue.
551 */
552int shClSvcMsgAdd(PSHCLCLIENT pClient, PSHCLCLIENTMSG pMsg, bool fAppend)
553{
554 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
555
556 LogFlowFunc(("uMsg=%RU32 (%s), cParms=%RU32, fAppend=%RTbool\n",
557 pMsg->uMsg, ShClHostMsgToStr(pMsg->uMsg), pMsg->cParms, fAppend));
558
559 if (fAppend)
560 pClient->queueMsg.append(pMsg);
561 else
562 pClient->queueMsg.prepend(pMsg);
563
564 /** @todo Catch / handle OOM? */
565
566 return VINF_SUCCESS;
567}
568
569/**
570 * Initializes a Shared Clipboard client.
571 *
572 * @param pClient Client to initialize.
573 * @param uClientID HGCM client ID to assign client to.
574 */
575int shClSvcClientInit(PSHCLCLIENT pClient, uint32_t uClientID)
576{
577 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
578
579 /* Assign the client ID. */
580 pClient->State.uClientID = uClientID;
581
582 LogFlowFunc(("[Client %RU32]\n", pClient->State.uClientID));
583
584 int rc = RTCritSectInit(&pClient->CritSect);
585 if (RT_SUCCESS(rc))
586 {
587 /* Create the client's own event source. */
588 rc = ShClEventSourceCreate(&pClient->Events, 0 /* ID, ignored */);
589 if (RT_SUCCESS(rc))
590 {
591 LogFlowFunc(("[Client %RU32] Using event source %RU32\n", uClientID, pClient->Events.uID));
592
593 /* Reset the client state. */
594 shclSvcClientStateReset(&pClient->State);
595
596 /* (Re-)initialize the client state. */
597 rc = shClSvcClientStateInit(&pClient->State, uClientID);
598
599#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
600 if (RT_SUCCESS(rc))
601 rc = ShClTransferCtxInit(&pClient->TransferCtx);
602#endif
603 }
604 }
605
606 LogFlowFuncLeaveRC(rc);
607 return rc;
608}
609
610/**
611 * Destroys a Shared Clipboard client.
612 *
613 * @param pClient Client to destroy.
614 */
615void shClSvcClientDestroy(PSHCLCLIENT pClient)
616{
617 AssertPtrReturnVoid(pClient);
618
619 LogFlowFunc(("[Client %RU32]\n", pClient->State.uClientID));
620
621 /* Make sure to send a quit message to the guest so that it can terminate gracefully. */
622 if (pClient->Pending.uType)
623 {
624 if (pClient->Pending.cParms >= 2)
625 {
626 HGCMSvcSetU32(&pClient->Pending.paParms[0], VBOX_SHCL_HOST_MSG_QUIT);
627 HGCMSvcSetU32(&pClient->Pending.paParms[1], 0);
628 }
629 g_pHelpers->pfnCallComplete(pClient->Pending.hHandle, VINF_SUCCESS);
630 pClient->Pending.uType = 0;
631 }
632
633 ShClEventSourceDestroy(&pClient->Events);
634
635 shClSvcClientStateDestroy(&pClient->State);
636
637 int rc2 = RTCritSectDelete(&pClient->CritSect);
638 AssertRC(rc2);
639
640 ClipboardClientMap::iterator itClient = g_mapClients.find(pClient->State.uClientID);
641 if (itClient != g_mapClients.end())
642 {
643 g_mapClients.erase(itClient);
644 }
645 else
646 AssertFailed();
647
648 LogFlowFuncLeave();
649}
650
651/**
652 * Resets a Shared Clipboard client.
653 *
654 * @param pClient Client to reset.
655 */
656void shClSvcClientReset(PSHCLCLIENT pClient)
657{
658 if (!pClient)
659 return;
660
661 LogFlowFunc(("[Client %RU32]\n", pClient->State.uClientID));
662
663 /* Reset message queue. */
664 shClSvcMsgQueueReset(pClient);
665
666 /* Reset event source. */
667 ShClEventSourceReset(&pClient->Events);
668
669 /* Reset pending state. */
670 RT_ZERO(pClient->Pending);
671
672#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
673 shClSvcClientTransfersReset(pClient);
674#endif
675
676 shclSvcClientStateReset(&pClient->State);
677}
678
679/**
680 * Implements VBOX_SHCL_GUEST_FN_REPORT_FEATURES.
681 *
682 * @returns VBox status code.
683 * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
684 * @retval VERR_ACCESS_DENIED if not master
685 * @retval VERR_INVALID_PARAMETER if bit 63 in the 2nd parameter isn't set.
686 * @retval VERR_WRONG_PARAMETER_COUNT
687 *
688 * @param pClient The client state.
689 * @param hCall The client's call handle.
690 * @param cParms Number of parameters.
691 * @param paParms Array of parameters.
692 */
693int shClSvcClientReportFeatures(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall,
694 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
695{
696 /*
697 * Validate the request.
698 */
699 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
700 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
701 uint64_t const fFeatures0 = paParms[0].u.uint64;
702 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
703 uint64_t const fFeatures1 = paParms[1].u.uint64;
704 ASSERT_GUEST_RETURN(fFeatures1 & VBOX_SHCL_GF_1_MUST_BE_ONE, VERR_INVALID_PARAMETER);
705
706 /*
707 * Do the work.
708 */
709 paParms[0].u.uint64 = g_fHostFeatures0;
710 paParms[1].u.uint64 = 0;
711
712 int rc = g_pHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
713 if (RT_SUCCESS(rc))
714 {
715 pClient->State.fGuestFeatures0 = fFeatures0;
716 pClient->State.fGuestFeatures1 = fFeatures1;
717 Log(("[Client %RU32] features: %#RX64 %#RX64\n", pClient->State.uClientID, fFeatures0, fFeatures1));
718 }
719 else
720 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
721
722 return VINF_HGCM_ASYNC_EXECUTE;
723}
724
725/**
726 * Implements VBOX_SHCL_GUEST_FN_QUERY_FEATURES.
727 *
728 * @returns VBox status code.
729 * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
730 * @retval VERR_WRONG_PARAMETER_COUNT
731 *
732 * @param hCall The client's call handle.
733 * @param cParms Number of parameters.
734 * @param paParms Array of parameters.
735 */
736int shClSvcClientQueryFeatures(VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
737{
738 /*
739 * Validate the request.
740 */
741 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
742 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
743 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
744 ASSERT_GUEST(paParms[1].u.uint64 & RT_BIT_64(63));
745
746 /*
747 * Do the work.
748 */
749 paParms[0].u.uint64 = g_fHostFeatures0;
750 paParms[1].u.uint64 = 0;
751 int rc = g_pHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
752 if (RT_FAILURE(rc))
753 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
754
755 return VINF_HGCM_ASYNC_EXECUTE;
756}
757
758/**
759 * Implements VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT and VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT.
760 *
761 * @returns VBox status code.
762 * @retval VINF_SUCCESS if a message was pending and is being returned.
763 * @retval VERR_TRY_AGAIN if no message pending and not blocking.
764 * @retval VERR_RESOURCE_BUSY if another read already made a waiting call.
765 * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending.
766 *
767 * @param pClient The client state.
768 * @param hCall The client's call handle.
769 * @param cParms Number of parameters.
770 * @param paParms Array of parameters.
771 * @param fWait Set if we should wait for a message, clear if to return
772 * immediately.
773 */
774int shClSvcMsgPeek(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[],
775 bool fWait)
776{
777 /*
778 * Validate the request.
779 */
780 ASSERT_GUEST_MSG_RETURN(cParms >= 2, ("cParms=%u!\n", cParms), VERR_WRONG_PARAMETER_COUNT);
781
782 uint64_t idRestoreCheck = 0;
783 uint32_t i = 0;
784 if (paParms[i].type == VBOX_HGCM_SVC_PARM_64BIT)
785 {
786 idRestoreCheck = paParms[0].u.uint64;
787 paParms[0].u.uint64 = 0;
788 i++;
789 }
790 for (; i < cParms; i++)
791 {
792 ASSERT_GUEST_MSG_RETURN(paParms[i].type == VBOX_HGCM_SVC_PARM_32BIT, ("#%u type=%u\n", i, paParms[i].type),
793 VERR_WRONG_PARAMETER_TYPE);
794 paParms[i].u.uint32 = 0;
795 }
796
797 /*
798 * Check restore session ID.
799 */
800 if (idRestoreCheck != 0)
801 {
802 uint64_t idRestore = g_pHelpers->pfnGetVMMDevSessionId(g_pHelpers);
803 if (idRestoreCheck != idRestore)
804 {
805 paParms[0].u.uint64 = idRestore;
806 LogFlowFunc(("[Client %RU32] VBOX_SHCL_GUEST_FN_MSG_PEEK_XXX -> VERR_VM_RESTORED (%#RX64 -> %#RX64)\n",
807 pClient->State.uClientID, idRestoreCheck, idRestore));
808 return VERR_VM_RESTORED;
809 }
810 Assert(!g_pHelpers->pfnIsCallRestored(hCall));
811 }
812
813 /*
814 * Return information about the first message if one is pending in the list.
815 */
816 if (!pClient->queueMsg.isEmpty())
817 {
818 PSHCLCLIENTMSG pFirstMsg = pClient->queueMsg.first();
819 if (pFirstMsg)
820 {
821 shClSvcMsgSetPeekReturn(pFirstMsg, paParms, cParms);
822 LogFlowFunc(("[Client %RU32] VBOX_SHCL_GUEST_FN_MSG_PEEK_XXX -> VINF_SUCCESS (idMsg=%u (%s), cParms=%u)\n",
823 pClient->State.uClientID, pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg),
824 pFirstMsg->cParms));
825 return VINF_SUCCESS;
826 }
827 }
828
829 /*
830 * If we cannot wait, fail the call.
831 */
832 if (!fWait)
833 {
834 LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_NOWAIT -> VERR_TRY_AGAIN\n", pClient->State.uClientID));
835 return VERR_TRY_AGAIN;
836 }
837
838 /*
839 * Wait for the host to queue a message for this client.
840 */
841 ASSERT_GUEST_MSG_RETURN(pClient->Pending.uType == 0, ("Already pending! (idClient=%RU32)\n",
842 pClient->State.uClientID), VERR_RESOURCE_BUSY);
843 pClient->Pending.hHandle = hCall;
844 pClient->Pending.cParms = cParms;
845 pClient->Pending.paParms = paParms;
846 pClient->Pending.uType = VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT;
847 LogFlowFunc(("[Client %RU32] Is now in pending mode...\n", pClient->State.uClientID));
848 return VINF_HGCM_ASYNC_EXECUTE;
849}
850
851/**
852 * Implements VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD.
853 *
854 * @returns VBox status code.
855 * @retval VINF_SUCCESS if a message was pending and is being returned.
856 * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending.
857 *
858 * @param pClient The client state.
859 * @param hCall The client's call handle.
860 * @param cParms Number of parameters.
861 * @param paParms Array of parameters.
862 */
863int shClSvcMsgGetOld(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
864{
865 int rc;
866
867 if (cParms != VBOX_SHCL_CPARMS_GET_HOST_MSG_OLD)
868 {
869 rc = VERR_INVALID_PARAMETER;
870 }
871 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* msg */
872 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT) /* formats */
873 {
874 rc = VERR_INVALID_PARAMETER;
875 }
876 else
877 {
878 if (!pClient->queueMsg.isEmpty())
879 {
880 PSHCLCLIENTMSG pFirstMsg = pClient->queueMsg.first();
881 AssertPtr(pFirstMsg);
882
883 LogFlowFunc(("[Client %RU32] uMsg=%RU32 (%s), cParms=%RU32\n",
884 pClient->State.uClientID, pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg),
885 pFirstMsg->cParms));
886
887 bool fRemove;
888 rc = shClSvcMsgSetGetHostMsgOldReturn(pFirstMsg, paParms, cParms, &fRemove);
889 if (RT_SUCCESS(rc))
890 {
891 AssertPtr(g_pHelpers);
892 rc = g_pHelpers->pfnCallComplete(hCall, rc);
893 if ( rc != VERR_CANCELLED
894 && fRemove)
895 {
896 pClient->queueMsg.removeFirst();
897 shClSvcMsgFree(pFirstMsg);
898
899 rc = VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
900 }
901 }
902 }
903 else
904 {
905 ASSERT_GUEST_MSG_RETURN(pClient->Pending.uType == 0, ("Already pending! (idClient=%RU32)\n",
906 pClient->State.uClientID), VERR_RESOURCE_BUSY);
907
908 pClient->Pending.hHandle = hCall;
909 pClient->Pending.cParms = cParms;
910 pClient->Pending.paParms = paParms;
911 pClient->Pending.uType = VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD;
912
913 rc = VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
914
915 LogFlowFunc(("[Client %RU32] Is now in pending mode...\n", pClient->State.uClientID));
916 }
917 }
918
919 LogFlowFunc(("[Client %RU32] rc=%Rrc\n", pClient->State.uClientID, rc));
920 return rc;
921}
922
923/**
924 * Implements VBOX_SHCL_GUEST_FN_MSG_GET.
925 *
926 * @returns VBox status code.
927 * @retval VINF_SUCCESS if message retrieved and removed from the pending queue.
928 * @retval VERR_TRY_AGAIN if no message pending.
929 * @retval VERR_BUFFER_OVERFLOW if a parmeter buffer is too small. The buffer
930 * size was updated to reflect the required size, though this isn't yet
931 * forwarded to the guest. (The guest is better of using peek with
932 * parameter count + 2 parameters to get the sizes.)
933 * @retval VERR_MISMATCH if the incoming message ID does not match the pending.
934 * @retval VINF_HGCM_ASYNC_EXECUTE if message was completed already.
935 *
936 * @param pClient The client state.
937 * @param hCall The client's call handle.
938 * @param cParms Number of parameters.
939 * @param paParms Array of parameters.
940 */
941int shClSvcMsgGet(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
942{
943 /*
944 * Validate the request.
945 */
946 uint32_t const idMsgExpected = cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT ? paParms[0].u.uint32
947 : cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT ? paParms[0].u.uint64
948 : UINT32_MAX;
949
950 /*
951 * Return information about the first message if one is pending in the list.
952 */
953 if (!pClient->queueMsg.isEmpty())
954 {
955 PSHCLCLIENTMSG pFirstMsg = pClient->queueMsg.first();
956 if (pFirstMsg)
957 {
958 LogFlowFunc(("First message is: %RU32 (%s), cParms=%RU32\n",
959 pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg), pFirstMsg->cParms));
960
961 ASSERT_GUEST_MSG_RETURN(pFirstMsg->uMsg == idMsgExpected || idMsgExpected == UINT32_MAX,
962 ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n",
963 pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg), pFirstMsg->cParms,
964 idMsgExpected, ShClHostMsgToStr(idMsgExpected), cParms),
965 VERR_MISMATCH);
966 ASSERT_GUEST_MSG_RETURN(pFirstMsg->cParms == cParms,
967 ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n",
968 pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg), pFirstMsg->cParms,
969 idMsgExpected, ShClHostMsgToStr(idMsgExpected), cParms),
970 VERR_WRONG_PARAMETER_COUNT);
971
972 /* Check the parameter types. */
973 for (uint32_t i = 0; i < cParms; i++)
974 ASSERT_GUEST_MSG_RETURN(pFirstMsg->paParms[i].type == paParms[i].type,
975 ("param #%u: type %u, caller expected %u (idMsg=%u %s)\n", i, pFirstMsg->paParms[i].type,
976 paParms[i].type, pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg)),
977 VERR_WRONG_PARAMETER_TYPE);
978 /*
979 * Copy out the parameters.
980 *
981 * No assertions on buffer overflows, and keep going till the end so we can
982 * communicate all the required buffer sizes.
983 */
984 int rc = VINF_SUCCESS;
985 for (uint32_t i = 0; i < cParms; i++)
986 switch (pFirstMsg->paParms[i].type)
987 {
988 case VBOX_HGCM_SVC_PARM_32BIT:
989 paParms[i].u.uint32 = pFirstMsg->paParms[i].u.uint32;
990 break;
991
992 case VBOX_HGCM_SVC_PARM_64BIT:
993 paParms[i].u.uint64 = pFirstMsg->paParms[i].u.uint64;
994 break;
995
996 case VBOX_HGCM_SVC_PARM_PTR:
997 {
998 uint32_t const cbSrc = pFirstMsg->paParms[i].u.pointer.size;
999 uint32_t const cbDst = paParms[i].u.pointer.size;
1000 paParms[i].u.pointer.size = cbSrc; /** @todo Check if this is safe in other layers...
1001 * Update: Safe, yes, but VMMDevHGCM doesn't pass it along. */
1002 if (cbSrc <= cbDst)
1003 memcpy(paParms[i].u.pointer.addr, pFirstMsg->paParms[i].u.pointer.addr, cbSrc);
1004 else
1005 {
1006 AssertMsgFailed(("#%u: cbSrc=%RU32 is bigger than cbDst=%RU32\n", i, cbSrc, cbDst));
1007 rc = VERR_BUFFER_OVERFLOW;
1008 }
1009 break;
1010 }
1011
1012 default:
1013 AssertMsgFailed(("#%u: %u\n", i, pFirstMsg->paParms[i].type));
1014 rc = VERR_INTERNAL_ERROR;
1015 break;
1016 }
1017 if (RT_SUCCESS(rc))
1018 {
1019 /*
1020 * Complete the message and remove the pending message unless the
1021 * guest raced us and cancelled this call in the meantime.
1022 */
1023 AssertPtr(g_pHelpers);
1024 rc = g_pHelpers->pfnCallComplete(hCall, rc);
1025
1026 LogFlowFunc(("[Client %RU32] pfnCallComplete -> %Rrc\n", pClient->State.uClientID, rc));
1027
1028 if (rc != VERR_CANCELLED)
1029 {
1030 pClient->queueMsg.removeFirst();
1031 shClSvcMsgFree(pFirstMsg);
1032 }
1033
1034 return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
1035 }
1036
1037 LogFlowFunc(("[Client %RU32] Returning %Rrc\n", pClient->State.uClientID, rc));
1038 return rc;
1039 }
1040 }
1041
1042 paParms[0].u.uint32 = 0;
1043 paParms[1].u.uint32 = 0;
1044 LogFlowFunc(("[Client %RU32] -> VERR_TRY_AGAIN\n", pClient->State.uClientID));
1045 return VERR_TRY_AGAIN;
1046}
1047
1048/**
1049 * Wakes up a pending client (i.e. waiting for new messages).
1050 *
1051 * @returns VBox status code.
1052 * @retval VINF_NO_CHANGE if the client is not in pending mode.
1053 *
1054 * @param pClient Client to wake up.
1055 */
1056int shClSvcClientWakeup(PSHCLCLIENT pClient)
1057{
1058 int rc = VINF_NO_CHANGE;
1059
1060 if (pClient->Pending.uType)
1061 {
1062 LogFunc(("[Client %RU32] Waking up ...\n", pClient->State.uClientID));
1063
1064 rc = VINF_SUCCESS;
1065
1066 if (!pClient->queueMsg.isEmpty())
1067 {
1068 PSHCLCLIENTMSG pFirstMsg = pClient->queueMsg.first();
1069 if (pFirstMsg)
1070 {
1071 LogFunc(("[Client %RU32] Current host message is %RU32 (%s), cParms=%RU32\n",
1072 pClient->State.uClientID, pFirstMsg->uMsg, ShClHostMsgToStr(pFirstMsg->uMsg),
1073 pFirstMsg->cParms));
1074
1075 bool fDonePending = false;
1076
1077 if (pClient->Pending.uType == VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT)
1078 {
1079 shClSvcMsgSetPeekReturn(pFirstMsg, pClient->Pending.paParms, pClient->Pending.cParms);
1080 fDonePending = true;
1081 }
1082 else if (pClient->Pending.uType == VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD) /* Legacy, Guest Additions < 6.1. */
1083 {
1084 bool fRemove;
1085 rc = shClSvcMsgSetGetHostMsgOldReturn(pFirstMsg, pClient->Pending.paParms, pClient->Pending.cParms,
1086 &fRemove);
1087 if (RT_SUCCESS(rc))
1088 {
1089 if (fRemove)
1090 {
1091 /* The old (legacy) protocol gets the message right when returning from peeking, so
1092 * remove the actual message from our queue right now. */
1093 pClient->queueMsg.removeFirst();
1094 shClSvcMsgFree(pFirstMsg);
1095 }
1096
1097 fDonePending = true;
1098 }
1099 }
1100
1101 if (fDonePending)
1102 {
1103 rc = g_pHelpers->pfnCallComplete(pClient->Pending.hHandle, VINF_SUCCESS);
1104
1105 pClient->Pending.hHandle = NULL;
1106 pClient->Pending.paParms = NULL;
1107 pClient->Pending.cParms = 0;
1108 pClient->Pending.uType = 0;
1109 }
1110 }
1111 else
1112 AssertFailed();
1113 }
1114 else
1115 AssertMsgFailed(("Waking up client ID=%RU32 with no host message in queue is a bad idea\n", pClient->State.uClientID));
1116
1117 return rc;
1118 }
1119 else
1120 LogFunc(("[Client %RU32] Not in pending state, skipping wakeup\n", pClient->State.uClientID));
1121
1122 return VINF_NO_CHANGE;
1123}
1124
1125/**
1126 * Requests to read clipboard data from the guest.
1127 *
1128 * @returns VBox status code.
1129 * @param pClient Client to request to read data form.
1130 * @param pDataReq Data request to send to the guest.
1131 * @param puEvent Event ID for waiting for new data. Optional.
1132 */
1133int ShClSvcDataReadRequest(PSHCLCLIENT pClient, PSHCLDATAREQ pDataReq,
1134 PSHCLEVENTID puEvent)
1135{
1136 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
1137 AssertPtrReturn(pDataReq, VERR_INVALID_POINTER);
1138 /* puEvent is optional. */
1139
1140 LogFlowFuncEnter();
1141
1142 int rc;
1143
1144 PSHCLCLIENTMSG pMsgReadData = shClSvcMsgAlloc(VBOX_SHCL_HOST_MSG_READ_DATA,
1145 VBOX_SHCL_CPARMS_READ_DATA_REQ);
1146 if (pMsgReadData)
1147 {
1148 const SHCLEVENTID uEvent = ShClEventIDGenerate(&pClient->Events);
1149
1150 LogFlowFunc(("uFmt=0x%x\n", pDataReq->uFmt));
1151
1152 HGCMSvcSetU64(&pMsgReadData->paParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
1153 pClient->Events.uID, uEvent));
1154 HGCMSvcSetU32(&pMsgReadData->paParms[1], 0 /* fFlags */);
1155 HGCMSvcSetU32(&pMsgReadData->paParms[2], pDataReq->uFmt);
1156 HGCMSvcSetU32(&pMsgReadData->paParms[3], pClient->State.cbChunkSize);
1157
1158 rc = shClSvcMsgAdd(pClient, pMsgReadData, true /* fAppend */);
1159 if (RT_SUCCESS(rc))
1160 {
1161 rc = ShClEventRegister(&pClient->Events, uEvent);
1162 if (RT_SUCCESS(rc))
1163 {
1164 rc = shClSvcClientWakeup(pClient);
1165 if (RT_SUCCESS(rc))
1166 {
1167 if (puEvent)
1168 *puEvent = uEvent;
1169 }
1170 else
1171 ShClEventUnregister(&pClient->Events, uEvent);
1172 }
1173 }
1174 }
1175 else
1176 rc = VERR_NO_MEMORY;
1177
1178 LogFlowFuncLeaveRC(rc);
1179 return rc;
1180}
1181
1182int ShClSvcDataReadSignal(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx,
1183 PSHCLDATABLOCK pData)
1184{
1185 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
1186 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
1187 AssertPtrReturn(pData, VERR_INVALID_POINTER);
1188
1189 LogFlowFuncEnter();
1190
1191 SHCLEVENTID uEvent;
1192 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID)) /* Legacy, Guest Additions < 6.1. */
1193 {
1194 /* Older Guest Additions (<= VBox 6.0) did not have any context ID handling, so we ASSUME that the last event registered
1195 * is the one we want to handle (as this all was a synchronous protocol anyway). */
1196 uEvent = ShClEventGetLast(&pClient->Events);
1197 }
1198 else
1199 uEvent = VBOX_SHCL_CONTEXTID_GET_EVENT(pCmdCtx->uContextID);
1200
1201 int rc = VINF_SUCCESS;
1202
1203 PSHCLEVENTPAYLOAD pPayload = NULL;
1204 if (pData->cbData)
1205 rc = ShClPayloadAlloc(uEvent, pData->pvData, pData->cbData, &pPayload);
1206
1207 if (RT_SUCCESS(rc))
1208 {
1209 rc = ShClEventSignal(&pClient->Events, uEvent, pPayload);
1210 if (RT_FAILURE(rc))
1211 ShClPayloadFree(pPayload);
1212 }
1213
1214 LogFlowFuncLeaveRC(rc);
1215 return rc;
1216}
1217
1218/**
1219 * Reports available VBox clipboard formats to the guest.
1220 *
1221 * @returns VBox status code.
1222 * @param pClient Client to request to read data form.
1223 * @param pFormats Formats to report.
1224 */
1225int ShClSvcFormatsReport(PSHCLCLIENT pClient, PSHCLFORMATDATA pFormats)
1226{
1227 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
1228 AssertPtrReturn(pFormats, VERR_INVALID_POINTER);
1229
1230 LogFlowFuncEnter();
1231
1232 uint32_t fFormats = pFormats->Formats;
1233 uint32_t fFlags = pFormats->fFlags;
1234
1235#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1236 /* If transfer mode is set to disabled, don't report the URI list format to the guest. */
1237 if (!(g_fTransferMode & VBOX_SHCL_TRANSFER_MODE_ENABLED))
1238 fFormats &= ~VBOX_SHCL_FMT_URI_LIST;
1239#endif
1240
1241 LogFlowFunc(("fFormats=0x%x -> 0x%x\n", pFormats->Formats, fFormats));
1242
1243 /* Nothing to report? Bail out early. */
1244 if (fFormats == VBOX_SHCL_FMT_NONE)
1245 return VINF_SUCCESS;
1246
1247 LogRel2(("Shared Clipboard: Reporting formats 0x%x to guest\n", fFormats));
1248
1249 int rc;
1250
1251 PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(VBOX_SHCL_HOST_MSG_FORMATS_REPORT, 3);
1252 if (pMsg)
1253 {
1254 const SHCLEVENTID uEvent = ShClEventIDGenerate(&pClient->Events);
1255
1256 HGCMSvcSetU64(&pMsg->paParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
1257 pClient->Events.uID, uEvent));
1258 HGCMSvcSetU32(&pMsg->paParms[1], fFormats);
1259 HGCMSvcSetU32(&pMsg->paParms[2], fFlags);
1260
1261 rc = shClSvcMsgAdd(pClient, pMsg, true /* fAppend */);
1262 if (RT_SUCCESS(rc))
1263 {
1264#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1265 /* If we announce an URI list, create a transfer locally and also tell the guest to create
1266 * a transfer on the guest side. */
1267 if (fFormats & VBOX_SHCL_FMT_URI_LIST)
1268 {
1269 rc = shClSvcTransferStart(pClient, SHCLTRANSFERDIR_TO_REMOTE, SHCLSOURCE_LOCAL,
1270 NULL /* pTransfer */);
1271 if (RT_FAILURE(rc))
1272 LogRel(("Shared Clipboard: Initializing host write transfer failed with %Rrc\n", rc));
1273 }
1274 else
1275 {
1276#endif
1277 pClient->State.fFlags |= SHCLCLIENTSTATE_FLAGS_READ_ACTIVE;
1278
1279 rc = shClSvcClientWakeup(pClient);
1280#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1281 }
1282#endif
1283 }
1284 }
1285 else
1286 rc = VERR_NO_MEMORY;
1287
1288 LogFlowFuncLeaveRC(rc);
1289 return rc;
1290}
1291
1292/**
1293 * Handles the VBOX_SHCL_GUEST_FN_DATA_READ message from the guest.
1294 */
1295static int shClSvcGetDataRead(PSHCLCLIENT pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1296{
1297 LogFlowFuncEnter();
1298
1299 /*
1300 * Check if the service mode allows this operation and whether the guest is
1301 * supposed to be reading from the host.
1302 */
1303 uint32_t uMode = ShClSvcGetMode();
1304 if ( uMode == VBOX_SHCL_MODE_BIDIRECTIONAL
1305 || uMode == VBOX_SHCL_MODE_HOST_TO_GUEST)
1306 { /* likely */ }
1307 else
1308 return VERR_ACCESS_DENIED;
1309
1310 /// @todo r=bird: The management of the SHCLCLIENTSTATE_FLAGS_READ_ACTIVE
1311 /// makes it impossible for the guest to retrieve more than one format from
1312 /// the clipboard. I.e. it can either get the TEXT or the HTML rendering,
1313 /// but not both. So, I've disable the check. */
1314 //ASSERT_GUEST_RETURN(pClient->State.fFlags & SHCLCLIENTSTATE_FLAGS_READ_ACTIVE, VERR_WRONG_ORDER);
1315
1316 /*
1317 * Digest parameters.
1318 *
1319 * We are dragging some legacy here from the 6.1 dev cycle, a 5 parameter
1320 * variant which prepends a 64-bit context ID (RAZ as meaning not defined),
1321 * a 32-bit flag (MBZ, no defined meaning) and switches the last two parameters.
1322 */
1323 ASSERT_GUEST_RETURN( cParms == VBOX_SHCL_CPARMS_DATA_READ
1324 || ( cParms == VBOX_SHCL_CPARMS_DATA_READ_61B
1325 && (pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID)),
1326 VERR_WRONG_PARAMETER_COUNT);
1327
1328 uintptr_t iParm = 0;
1329 SHCLCLIENTCMDCTX cmdCtx;
1330 RT_ZERO(cmdCtx);
1331 if (cParms == VBOX_SHCL_CPARMS_DATA_READ_61B)
1332 {
1333 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
1334 /* This has no defined meaning and was never used, however the guest passed stuff, so ignore it and leave idContext=0. */
1335 iParm++;
1336 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1337 ASSERT_GUEST_RETURN(paParms[iParm].u.uint32 == 0, VERR_INVALID_FLAGS);
1338 iParm++;
1339 }
1340
1341 SHCLDATABLOCK dataBlock;
1342 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1343 dataBlock.uFormat = paParms[iParm].u.uint32;
1344 iParm++;
1345 if (cParms != VBOX_SHCL_CPARMS_DATA_READ_61B)
1346 {
1347 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE); /* Data buffer */
1348 dataBlock.pvData = paParms[iParm].u.pointer.addr;
1349 dataBlock.cbData = paParms[iParm].u.pointer.size;
1350 iParm++;
1351 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /*cbDataReturned*/
1352 iParm++;
1353 }
1354 else
1355 {
1356 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /*cbDataReturned*/
1357 iParm++;
1358 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE); /* Data buffer */
1359 dataBlock.pvData = paParms[iParm].u.pointer.addr;
1360 dataBlock.cbData = paParms[iParm].u.pointer.size;
1361 iParm++;
1362 }
1363 Assert(iParm == cParms);
1364
1365 /*
1366 * For some reason we need to do this (makes absolutely no sense to bird).
1367 */
1368 /** @todo r=bird: I really don't get why you need the State.POD.uFormat
1369 * member. I'm sure there is a reason. Incomplete code? */
1370 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID))
1371 {
1372 if (pClient->State.POD.uFormat == VBOX_SHCL_FMT_NONE)
1373 pClient->State.POD.uFormat = dataBlock.uFormat;
1374 /// @todo r=bird: This actively breaks copying different types of data into the
1375 /// guest (first copy a text snippet, then you cannot copy any bitmaps), so I've
1376 /// disabled it.
1377 //ASSERT_GUEST_MSG_RETURN(pClient->State.POD.uFormat == dataBlock.uFormat,
1378 // ("Requested %#x, POD.uFormat=%#x\n", dataBlock.uFormat, pClient->State.POD.uFormat),
1379 // VERR_BAD_EXE_FORMAT /*VERR_INTERNAL_ERROR*/);
1380 }
1381
1382 /*
1383 * Do the reading.
1384 */
1385 int rc;
1386 uint32_t cbActual = 0;
1387
1388 /* If there is a service extension active, try reading data from it first. */
1389 if (g_ExtState.pfnExtension)
1390 {
1391 SHCLEXTPARMS parms;
1392 RT_ZERO(parms);
1393
1394 parms.uFormat = dataBlock.uFormat;
1395 parms.u.pvData = dataBlock.pvData;
1396 parms.cbData = dataBlock.cbData;
1397
1398 g_ExtState.fReadingData = true;
1399
1400 /* Read clipboard data from the extension. */
1401 rc = g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_READ, &parms, sizeof(parms));
1402 LogRelFlowFunc(("DATA/Ext: fDelayedAnnouncement=%RTbool uDelayedFormats=%#x cbData=%RU32->%RU32 rc=%Rrc\n",
1403 g_ExtState.fDelayedAnnouncement, g_ExtState.uDelayedFormats, dataBlock.cbData, parms.cbData, rc));
1404
1405 /* Did the extension send the clipboard formats yet?
1406 * Otherwise, do this now. */
1407 if (g_ExtState.fDelayedAnnouncement)
1408 {
1409 SHCLFORMATDATA FormatData;
1410 FormatData.fFlags = 0;
1411 FormatData.Formats = g_ExtState.uDelayedFormats;
1412 Assert(FormatData.Formats != VBOX_SHCL_FMT_NONE); /* There better is *any* format here now. */
1413
1414 int rc2 = ShClSvcFormatsReport(pClient, &FormatData);
1415 AssertRC(rc2);
1416
1417 g_ExtState.fDelayedAnnouncement = false;
1418 g_ExtState.uDelayedFormats = 0;
1419 }
1420
1421 g_ExtState.fReadingData = false;
1422
1423 if (RT_SUCCESS(rc))
1424 cbActual = parms.cbData;
1425 }
1426 else
1427 {
1428 rc = ShClSvcImplReadData(pClient, &cmdCtx, &dataBlock, &cbActual);
1429 LogRelFlowFunc(("DATA/Host: cbData=%RU32->%RU32 rc=%Rrc\n", dataBlock.cbData, cbActual, rc));
1430 }
1431
1432 if (RT_SUCCESS(rc))
1433 {
1434 /* Return the actual size required to fullfil the request. */
1435 if (cParms != VBOX_SHCL_CPARMS_DATA_READ_61B)
1436 HGCMSvcSetU32(&paParms[2], cbActual);
1437 else
1438 HGCMSvcSetU32(&paParms[3], cbActual);
1439
1440 /* If the data to return exceeds the buffer the guest supplies, tell it (and let it try again). */
1441 if (cbActual >= dataBlock.cbData)
1442 rc = VINF_BUFFER_OVERFLOW;
1443
1444 if (rc == VINF_SUCCESS)
1445 {
1446 /* Only remove "read active" flag after successful read again. */
1447 /** @todo r=bird: This doesn't make any effing sense. What if the guest
1448 * wants to read another format??? */
1449 pClient->State.fFlags &= ~SHCLCLIENTSTATE_FLAGS_READ_ACTIVE;
1450 }
1451 }
1452
1453 LogFlowFuncLeaveRC(rc);
1454 return rc;
1455}
1456
1457int shClSvcGetDataWrite(PSHCLCLIENT pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1458{
1459 LogFlowFuncEnter();
1460
1461 /*
1462 * Check if the service mode allows this operation and whether the guest is
1463 * supposed to be reading from the host.
1464 */
1465 uint32_t uMode = ShClSvcGetMode();
1466 if ( uMode == VBOX_SHCL_MODE_BIDIRECTIONAL
1467 || uMode == VBOX_SHCL_MODE_GUEST_TO_HOST)
1468 { /* likely */ }
1469 else
1470 return VERR_ACCESS_DENIED;
1471
1472 /** @todo r=bird: This whole active flag stuff is broken, so disabling for now. */
1473 //if (pClient->State.fFlags & SHCLCLIENTSTATE_FLAGS_WRITE_ACTIVE)
1474 //{ /* likely */ }
1475 //else
1476 // return VERR_WRONG_ORDER;
1477
1478 /*
1479 * Digest parameters.
1480 *
1481 * There are 3 different format here, formatunately no parameters have been
1482 * switch around so it's plain sailing compared to the DATA_READ message.
1483 */
1484 ASSERT_GUEST_RETURN(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID
1485 ? cParms == VBOX_SHCL_CPARMS_DATA_WRITE || cParms == VBOX_SHCL_CPARMS_DATA_WRITE_61B
1486 : cParms == VBOX_SHCL_CPARMS_DATA_WRITE_OLD,
1487 VERR_WRONG_PARAMETER_COUNT);
1488
1489 uintptr_t iParm = 0;
1490 SHCLCLIENTCMDCTX cmdCtx;
1491 RT_ZERO(cmdCtx);
1492 if (cParms > VBOX_SHCL_CPARMS_DATA_WRITE_OLD)
1493 {
1494 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
1495 cmdCtx.uContextID = paParms[iParm].u.uint64;
1496 uint64_t const idCtxExpected = VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, pClient->Events.uID,
1497 VBOX_SHCL_CONTEXTID_GET_EVENT(cmdCtx.uContextID));
1498 ASSERT_GUEST_MSG_RETURN(cmdCtx.uContextID == idCtxExpected,
1499 ("Wrong context ID: %#RX64, expected %#RX64\n", cmdCtx.uContextID, idCtxExpected),
1500 VERR_INVALID_CONTEXT);
1501 iParm++;
1502 }
1503 else
1504 {
1505 /** @todo supply CID from client state? Setting it in ShClSvcDataReadRequest? */
1506 }
1507 if (cParms == VBOX_SHCL_CPARMS_DATA_WRITE_61B)
1508 {
1509 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1510 ASSERT_GUEST_RETURN(paParms[iParm].u.uint32 == 0, VERR_INVALID_FLAGS);
1511 iParm++;
1512 }
1513 SHCLDATABLOCK dataBlock;
1514 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* Format bit. */
1515 dataBlock.uFormat = paParms[iParm].u.uint32;
1516 iParm++;
1517 if (cParms == VBOX_SHCL_CPARMS_DATA_WRITE_61B)
1518 {
1519 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* "cbData" - duplicates buffer size. */
1520 iParm++;
1521 }
1522 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE); /* Data buffer */
1523 dataBlock.pvData = paParms[iParm].u.pointer.addr;
1524 dataBlock.cbData = paParms[iParm].u.pointer.size;
1525 iParm++;
1526 Assert(iParm == cParms);
1527
1528 /*
1529 * For some reason we need to do this (makes absolutely no sense to bird).
1530 */
1531 /** @todo r=bird: I really don't get why you need the State.POD.uFormat
1532 * member. I'm sure there is a reason. Incomplete code? */
1533 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID))
1534 {
1535 if (pClient->State.POD.uFormat == VBOX_SHCL_FMT_NONE)
1536 pClient->State.POD.uFormat = dataBlock.uFormat;
1537 /** @todo r=bird: this must be buggy to, I've disabled it without testing
1538 * though. */
1539 //ASSERT_GUEST_MSG_RETURN(pClient->State.POD.uFormat == dataBlock.uFormat,
1540 // ("Requested %#x, POD.uFormat=%#x\n", dataBlock.uFormat, pClient->State.POD.uFormat),
1541 // VERR_BAD_EXE_FORMAT /*VERR_INTERNAL_ERROR*/);
1542 }
1543
1544 /*
1545 * Write the data to the active host side clipboard.
1546 */
1547 int rc;
1548 if (g_ExtState.pfnExtension)
1549 {
1550 SHCLEXTPARMS parms;
1551 RT_ZERO(parms);
1552 parms.uFormat = dataBlock.uFormat;
1553 parms.u.pvData = dataBlock.pvData;
1554 parms.cbData = dataBlock.cbData;
1555
1556 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_WRITE, &parms, sizeof(parms));
1557 rc = VINF_SUCCESS;
1558 }
1559 else
1560 rc = ShClSvcImplWriteData(pClient, &cmdCtx, &dataBlock);
1561 if (RT_SUCCESS(rc))
1562 {
1563 /* Remove "write active" flag after successful read again. */
1564 /** @todo r=bird: This doesn't make any effing sense. What if the host
1565 * wants to have the guest write it another format??? */
1566 pClient->State.fFlags &= ~SHCLCLIENTSTATE_FLAGS_WRITE_ACTIVE;
1567 }
1568
1569 LogFlowFuncLeaveRC(rc);
1570 return rc;
1571}
1572
1573/**
1574 * Gets an error from HGCM service parameters.
1575 *
1576 * @returns VBox status code.
1577 * @param cParms Number of HGCM parameters supplied in \a paParms.
1578 * @param paParms Array of HGCM parameters.
1579 * @param pRc Where to store the received error code.
1580 */
1581static int shClSvcGetError(uint32_t cParms, VBOXHGCMSVCPARM paParms[], int *pRc)
1582{
1583 AssertPtrReturn(paParms, VERR_INVALID_PARAMETER);
1584 AssertPtrReturn(pRc, VERR_INVALID_PARAMETER);
1585
1586 int rc;
1587
1588 if (cParms == VBOX_SHCL_CPARMS_ERROR)
1589 {
1590 rc = HGCMSvcGetU32(&paParms[1], (uint32_t *)pRc); /** @todo int vs. uint32_t !!! */
1591 }
1592 else
1593 rc = VERR_INVALID_PARAMETER;
1594
1595 LogFlowFuncLeaveRC(rc);
1596 return rc;
1597}
1598
1599int shClSvcSetSource(PSHCLCLIENT pClient, SHCLSOURCE enmSource)
1600{
1601 if (!pClient) /* If no client connected (anymore), bail out. */
1602 return VINF_SUCCESS;
1603
1604 int rc = VINF_SUCCESS;
1605
1606 if (ShClSvcLock())
1607 {
1608 pClient->State.enmSource = enmSource;
1609
1610 LogFlowFunc(("Source of client %RU32 is now %RU32\n", pClient->State.uClientID, pClient->State.enmSource));
1611
1612 ShClSvcUnlock();
1613 }
1614
1615 LogFlowFuncLeaveRC(rc);
1616 return rc;
1617}
1618
1619static int svcInit(void)
1620{
1621 int rc = RTCritSectInit(&g_CritSect);
1622
1623 if (RT_SUCCESS(rc))
1624 {
1625 shClSvcModeSet(VBOX_SHCL_MODE_OFF);
1626
1627 rc = ShClSvcImplInit();
1628
1629 /* Clean up on failure, because 'svnUnload' will not be called
1630 * if the 'svcInit' returns an error.
1631 */
1632 if (RT_FAILURE(rc))
1633 {
1634 RTCritSectDelete(&g_CritSect);
1635 }
1636 }
1637
1638 return rc;
1639}
1640
1641static DECLCALLBACK(int) svcUnload(void *)
1642{
1643 LogFlowFuncEnter();
1644
1645 ShClSvcImplDestroy();
1646
1647 RTCritSectDelete(&g_CritSect);
1648
1649 return VINF_SUCCESS;
1650}
1651
1652static DECLCALLBACK(int) svcDisconnect(void *, uint32_t u32ClientID, void *pvClient)
1653{
1654 RT_NOREF(u32ClientID);
1655
1656 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
1657
1658 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
1659 AssertPtr(pClient);
1660
1661#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1662 shClSvcClientTransfersReset(pClient);
1663#endif
1664
1665 ShClSvcImplDisconnect(pClient);
1666
1667 shClSvcClientDestroy(pClient);
1668
1669 return VINF_SUCCESS;
1670}
1671
1672static DECLCALLBACK(int) svcConnect(void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring)
1673{
1674 RT_NOREF(fRequestor, fRestoring);
1675
1676 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
1677 AssertPtr(pvClient);
1678
1679 int rc = shClSvcClientInit(pClient, u32ClientID);
1680 if (RT_SUCCESS(rc))
1681 {
1682 rc = ShClSvcImplConnect(pClient, ShClSvcGetHeadless());
1683 if (RT_SUCCESS(rc))
1684 {
1685 /* Sync the host clipboard content with the client. */
1686 rc = ShClSvcImplSync(pClient);
1687 if (rc == VINF_NO_CHANGE)
1688 {
1689 /*
1690 * The sync could return VINF_NO_CHANGE if nothing has changed on the host, but older
1691 * Guest Additions rely on the fact that only VINF_SUCCESS indicates a successful connect
1692 * to the host service (instead of using RT_SUCCESS()).
1693 *
1694 * So implicitly set VINF_SUCCESS here to not break older Guest Additions.
1695 */
1696 rc = VINF_SUCCESS;
1697 }
1698
1699 if (RT_SUCCESS(rc))
1700 {
1701 /* Assign weak pointer to client map .*/
1702 g_mapClients[u32ClientID] = pClient; /** @todo Handle OOM / collisions? */
1703
1704 /* For now we ASSUME that the first client ever connected is in charge for
1705 * communicating withe the service extension.
1706 *
1707 ** @todo This needs to be fixed ASAP w/o breaking older guest / host combos. */
1708 if (g_ExtState.uClientID == 0)
1709 g_ExtState.uClientID = u32ClientID;
1710 }
1711 }
1712 }
1713
1714 LogFlowFuncLeaveRC(rc);
1715 return rc;
1716}
1717
1718static DECLCALLBACK(void) svcCall(void *,
1719 VBOXHGCMCALLHANDLE callHandle,
1720 uint32_t u32ClientID,
1721 void *pvClient,
1722 uint32_t u32Function,
1723 uint32_t cParms,
1724 VBOXHGCMSVCPARM paParms[],
1725 uint64_t tsArrival)
1726{
1727 RT_NOREF(u32ClientID, pvClient, tsArrival);
1728
1729 int rc = VINF_SUCCESS;
1730
1731 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
1732 AssertPtr(pClient);
1733
1734 LogFunc(("u32ClientID=%RU32, fn=%RU32 (%s), cParms=%RU32, paParms=%p\n",
1735 u32ClientID, u32Function, ShClGuestMsgToStr(u32Function), cParms, paParms));
1736
1737#ifdef LOG_ENABLED
1738 for (uint32_t i = 0; i < cParms; i++)
1739 {
1740 /** @todo parameters other than 32 bit */
1741 LogFunc((" paParms[%d]: type %RU32 - value %RU32\n", i, paParms[i].type, paParms[i].u.uint32));
1742 }
1743#endif
1744
1745 LogFunc(("Client state: fFlags=0x%x, fGuestFeatures0=0x%x, fGuestFeatures1=0x%x\n",
1746 pClient->State.fFlags, pClient->State.fGuestFeatures0, pClient->State.fGuestFeatures1));
1747
1748 switch (u32Function)
1749 {
1750 case VBOX_SHCL_GUEST_FN_GET_HOST_MSG_OLD:
1751 {
1752 rc = shClSvcMsgGetOld(pClient, callHandle, cParms, paParms);
1753 break;
1754 }
1755
1756 case VBOX_SHCL_GUEST_FN_CONNECT:
1757 {
1758 if (cParms != VBOX_SHCL_CPARMS_CONNECT)
1759 {
1760 rc = VERR_INVALID_PARAMETER;
1761 }
1762 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* cbChunkSize */
1763 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* enmCompression */
1764 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT) /* enmChecksumType */
1765 {
1766 rc = VERR_INVALID_PARAMETER;
1767 }
1768 else if (ShClSvcGetMode() == VBOX_SHCL_MODE_OFF)
1769 {
1770 rc = VERR_ACCESS_DENIED;
1771 }
1772 else
1773 {
1774 /* Report back supported chunk size to the guest. */
1775 HGCMSvcSetU32(&paParms[0], _64K); /* Chunk size */ /** @todo Make chunk size dynamic. */
1776
1777 rc = VINF_SUCCESS;
1778 }
1779
1780 break;
1781 }
1782
1783 case VBOX_SHCL_GUEST_FN_REPORT_FEATURES:
1784 {
1785 rc = shClSvcClientReportFeatures(pClient, callHandle, cParms, paParms);
1786 break;
1787 }
1788
1789 case VBOX_SHCL_GUEST_FN_QUERY_FEATURES:
1790 {
1791 rc = shClSvcClientQueryFeatures(callHandle, cParms, paParms);
1792 break;
1793 }
1794
1795 case VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT:
1796 {
1797 rc = shClSvcMsgPeek(pClient, callHandle, cParms, paParms, false /*fWait*/);
1798 break;
1799 }
1800
1801 case VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT:
1802 {
1803 rc = shClSvcMsgPeek(pClient, callHandle, cParms, paParms, true /*fWait*/);
1804 break;
1805 }
1806
1807 case VBOX_SHCL_GUEST_FN_MSG_GET:
1808 {
1809 rc = shClSvcMsgGet(pClient, callHandle, cParms, paParms);
1810 break;
1811 }
1812
1813 case VBOX_SHCL_GUEST_FN_FORMATS_REPORT:
1814 {
1815 uint32_t uFormats = 0;
1816
1817 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID)) /* Legacy, Guest Additions < 6.1. */
1818 {
1819 if (cParms != 1)
1820 {
1821 rc = VERR_INVALID_PARAMETER;
1822 }
1823 else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT) /* uFormats */
1824 {
1825 rc = VERR_INVALID_PARAMETER;
1826 }
1827 else
1828 {
1829 rc = HGCMSvcGetU32(&paParms[0], &uFormats);
1830 }
1831 }
1832 else
1833 {
1834 if (cParms != 3)
1835 {
1836 rc = VERR_INVALID_PARAMETER;
1837 }
1838 else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_64BIT /* uContextID */
1839 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* uFormats */
1840 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT) /* fFlags */
1841 {
1842 rc = VERR_INVALID_PARAMETER;
1843 }
1844 else
1845 {
1846 rc = HGCMSvcGetU32(&paParms[1], &uFormats);
1847
1848 /** @todo Handle rest. */
1849 }
1850 }
1851
1852 if (RT_SUCCESS(rc))
1853 {
1854 if ( ShClSvcGetMode() != VBOX_SHCL_MODE_GUEST_TO_HOST
1855 && ShClSvcGetMode() != VBOX_SHCL_MODE_BIDIRECTIONAL)
1856 {
1857 rc = VERR_ACCESS_DENIED;
1858 }
1859 else if (uFormats != VBOX_SHCL_FMT_NONE) /* Only announce formats if we actually *have* formats to announce! */
1860 {
1861 rc = shClSvcSetSource(pClient, SHCLSOURCE_REMOTE);
1862 if (RT_SUCCESS(rc))
1863 {
1864 if (g_ExtState.pfnExtension)
1865 {
1866 SHCLEXTPARMS parms;
1867 RT_ZERO(parms);
1868
1869 parms.uFormat = uFormats;
1870
1871 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE,
1872 &parms, sizeof(parms));
1873 }
1874 else
1875 {
1876
1877 SHCLCLIENTCMDCTX cmdCtx;
1878 RT_ZERO(cmdCtx);
1879
1880 SHCLFORMATDATA formatData;
1881 RT_ZERO(formatData);
1882
1883 formatData.Formats = uFormats;
1884 Assert(formatData.Formats != VBOX_SHCL_FMT_NONE); /* Sanity. */
1885
1886 rc = ShClSvcImplFormatAnnounce(pClient, &cmdCtx, &formatData);
1887 }
1888
1889 /** @todo r=bird: I'm not sure if the guest should be automatically allowed
1890 * to write the host clipboard now. It would make more sense to disallow
1891 * host clipboard reads until the host reports formats.
1892 *
1893 * The writes should only really be allowed upon request from the host,
1894 * shouldn't they? (Though, I'm not sure, maybe there are situations
1895 * where the guest side will just want to push the content over
1896 * immediately while it's still available, I don't quite recall now...
1897 */
1898 if (RT_SUCCESS(rc))
1899 pClient->State.fFlags |= SHCLCLIENTSTATE_FLAGS_WRITE_ACTIVE;
1900 }
1901 }
1902 }
1903
1904 break;
1905 }
1906
1907 case VBOX_SHCL_GUEST_FN_DATA_READ:
1908 rc = shClSvcGetDataRead(pClient, cParms, paParms);
1909 break;
1910
1911 case VBOX_SHCL_GUEST_FN_DATA_WRITE:
1912 rc = shClSvcGetDataWrite(pClient, cParms, paParms);
1913 break;
1914
1915 case VBOX_SHCL_GUEST_FN_CANCEL:
1916 {
1917 LogRel2(("Shared Clipboard: Operation canceled by guest side\n"));
1918
1919 /** @todo r=bird: What on earth is this? The only user of this message
1920 * (VBOX_SHCL_GUEST_FN_CANCEL) is VbglR3ClipboardMsgPeekWait(), where it was
1921 * copied over from guest control. What happens here is _nothing_ like what it
1922 * expects to happen. See GstCtrlService::clientMsgCancel for a reference.
1923 */
1924
1925 /* Reset client state and start over. */
1926 shclSvcClientStateReset(&pClient->State);
1927#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1928 shClSvcClientTransfersReset(pClient);
1929#endif
1930 /** @todo Do we need to do anything else here? */
1931 break;
1932 }
1933
1934 case VBOX_SHCL_GUEST_FN_ERROR:
1935 {
1936 int rcGuest;
1937 rc = shClSvcGetError(cParms,paParms, &rcGuest);
1938 if (RT_SUCCESS(rc))
1939 {
1940 LogRel(("Shared Clipboard: Error from guest side: %Rrc\n", rcGuest));
1941
1942 /* Reset client state and start over. */
1943 shclSvcClientStateReset(&pClient->State);
1944#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1945 shClSvcClientTransfersReset(pClient);
1946#endif
1947 }
1948 break;
1949 }
1950
1951 default:
1952 {
1953#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1954 if (u32Function <= VBOX_SHCL_GUEST_FN_LAST)
1955 {
1956 if (g_fTransferMode & VBOX_SHCL_TRANSFER_MODE_ENABLED)
1957 rc = shClSvcTransferHandler(pClient, callHandle, u32Function, cParms, paParms, tsArrival);
1958 else
1959 {
1960 LogRel2(("Shared Clipboard: File transfers are disabled for this VM\n"));
1961 rc = VERR_ACCESS_DENIED;
1962 }
1963 }
1964 else
1965#endif
1966 {
1967 LogRel2(("Shared Clipboard: Unknown guest function: %u (%#x)\n", u32Function, u32Function));
1968 rc = VERR_NOT_IMPLEMENTED;
1969 }
1970 break;
1971 }
1972 }
1973
1974 LogFlowFunc(("[Client %RU32] rc=%Rrc\n", pClient->State.uClientID, rc));
1975
1976 if (rc != VINF_HGCM_ASYNC_EXECUTE)
1977 g_pHelpers->pfnCallComplete(callHandle, rc);
1978}
1979
1980/**
1981 * Initializes a Shared Clipboard service's client state.
1982 *
1983 * @returns VBox status code.
1984 * @param pClientState Client state to initialize.
1985 * @param uClientID Client ID (HGCM) to use for this client state.
1986 */
1987int shClSvcClientStateInit(PSHCLCLIENTSTATE pClientState, uint32_t uClientID)
1988{
1989 LogFlowFuncEnter();
1990
1991 shclSvcClientStateReset(pClientState);
1992
1993 /* Register the client. */
1994 pClientState->uClientID = uClientID;
1995
1996 return VINF_SUCCESS;
1997}
1998
1999/**
2000 * Destroys a Shared Clipboard service's client state.
2001 *
2002 * @returns VBox status code.
2003 * @param pClientState Client state to destroy.
2004 */
2005int shClSvcClientStateDestroy(PSHCLCLIENTSTATE pClientState)
2006{
2007 RT_NOREF(pClientState);
2008
2009 LogFlowFuncEnter();
2010
2011 return VINF_SUCCESS;
2012}
2013
2014/**
2015 * Resets a Shared Clipboard service's client state.
2016 *
2017 * @param pClientState Client state to reset.
2018 */
2019void shclSvcClientStateReset(PSHCLCLIENTSTATE pClientState)
2020{
2021 LogFlowFuncEnter();
2022
2023 pClientState->fGuestFeatures0 = VBOX_SHCL_GF_NONE;
2024 pClientState->fGuestFeatures1 = VBOX_SHCL_GF_NONE;
2025
2026 pClientState->cbChunkSize = _64K; /** Make this configurable. */
2027 pClientState->enmSource = SHCLSOURCE_INVALID;
2028 pClientState->fFlags = SHCLCLIENTSTATE_FLAGS_NONE;
2029
2030 pClientState->POD.enmDir = SHCLTRANSFERDIR_UNKNOWN;
2031 pClientState->POD.uFormat = VBOX_SHCL_FMT_NONE;
2032 pClientState->POD.cbToReadWriteTotal = 0;
2033 pClientState->POD.cbReadWritten = 0;
2034
2035#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2036 pClientState->Transfers.enmTransferDir = SHCLTRANSFERDIR_UNKNOWN;
2037#endif
2038
2039
2040}
2041
2042/*
2043 * We differentiate between a function handler for the guest and one for the host.
2044 */
2045static DECLCALLBACK(int) svcHostCall(void *,
2046 uint32_t u32Function,
2047 uint32_t cParms,
2048 VBOXHGCMSVCPARM paParms[])
2049{
2050 int rc = VINF_SUCCESS;
2051
2052 LogFlowFunc(("u32Function=%RU32 (%s), cParms=%RU32, paParms=%p\n",
2053 u32Function, ShClHostFunctionToStr(u32Function), cParms, paParms));
2054
2055 switch (u32Function)
2056 {
2057 case VBOX_SHCL_HOST_FN_SET_MODE:
2058 {
2059 if (cParms != 1)
2060 {
2061 rc = VERR_INVALID_PARAMETER;
2062 }
2063 else
2064 {
2065 uint32_t u32Mode = VBOX_SHCL_MODE_OFF;
2066
2067 rc = HGCMSvcGetU32(&paParms[0], &u32Mode);
2068 if (RT_SUCCESS(rc))
2069 rc = shClSvcModeSet(u32Mode);
2070 }
2071
2072 break;
2073 }
2074
2075#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2076 case VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE:
2077 {
2078 if (cParms != 1)
2079 {
2080 rc = VERR_INVALID_PARAMETER;
2081 }
2082 else
2083 {
2084 uint32_t fTransferMode;
2085 rc = HGCMSvcGetU32(&paParms[0], &fTransferMode);
2086 if (RT_SUCCESS(rc))
2087 rc = shClSvcTransferModeSet(fTransferMode);
2088 }
2089 break;
2090 }
2091#endif
2092 case VBOX_SHCL_HOST_FN_SET_HEADLESS:
2093 {
2094 if (cParms != 1)
2095 {
2096 rc = VERR_INVALID_PARAMETER;
2097 }
2098 else
2099 {
2100 uint32_t uHeadless;
2101 rc = HGCMSvcGetU32(&paParms[0], &uHeadless);
2102 if (RT_SUCCESS(rc))
2103 {
2104 g_fHeadless = RT_BOOL(uHeadless);
2105 LogRel(("Shared Clipboard: Service running in %s mode\n", g_fHeadless ? "headless" : "normal"));
2106 }
2107 }
2108 break;
2109 }
2110
2111 default:
2112 {
2113#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2114 rc = shClSvcTransferHostHandler(u32Function, cParms, paParms);
2115#else
2116 rc = VERR_NOT_IMPLEMENTED;
2117#endif
2118 break;
2119 }
2120 }
2121
2122 LogFlowFuncLeaveRC(rc);
2123 return rc;
2124}
2125
2126#ifndef UNIT_TEST
2127/**
2128 * SSM descriptor table for the SHCLCLIENTSTATE structure.
2129 *
2130 * @note Saving the session ID not necessary, as they're not persistent across
2131 * state save/restore.
2132 */
2133static SSMFIELD const s_aShClSSMClientState[] =
2134{
2135 SSMFIELD_ENTRY(SHCLCLIENTSTATE, fGuestFeatures0),
2136 SSMFIELD_ENTRY(SHCLCLIENTSTATE, fGuestFeatures1),
2137 SSMFIELD_ENTRY(SHCLCLIENTSTATE, cbChunkSize),
2138 SSMFIELD_ENTRY(SHCLCLIENTSTATE, enmSource),
2139 SSMFIELD_ENTRY(SHCLCLIENTSTATE, fFlags),
2140 SSMFIELD_ENTRY_TERM()
2141};
2142
2143/**
2144 * VBox 6.1 Beta 1 version of s_aShClSSMClientState (no flags).
2145 */
2146static SSMFIELD const s_aShClSSMClientState61B1[] =
2147{
2148 SSMFIELD_ENTRY(SHCLCLIENTSTATE, fGuestFeatures0),
2149 SSMFIELD_ENTRY(SHCLCLIENTSTATE, fGuestFeatures1),
2150 SSMFIELD_ENTRY(SHCLCLIENTSTATE, cbChunkSize),
2151 SSMFIELD_ENTRY(SHCLCLIENTSTATE, enmSource),
2152 SSMFIELD_ENTRY_TERM()
2153};
2154
2155/**
2156 * SSM descriptor table for the SHCLCLIENTPODSTATE structure.
2157 */
2158static SSMFIELD const s_aShClSSMClientPODState[] =
2159{
2160 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, enmDir),
2161 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, uFormat),
2162 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, cbToReadWriteTotal),
2163 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, cbReadWritten),
2164 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, tsLastReadWrittenMs),
2165 SSMFIELD_ENTRY_TERM()
2166};
2167
2168/**
2169 * SSM descriptor table for the SHCLCLIENTURISTATE structure.
2170 */
2171static SSMFIELD const s_aShClSSMClientTransferState[] =
2172{
2173 SSMFIELD_ENTRY(SHCLCLIENTTRANSFERSTATE, enmTransferDir),
2174 SSMFIELD_ENTRY_TERM()
2175};
2176
2177/**
2178 * SSM descriptor table for the header of the SHCLCLIENTMSG structure.
2179 * The actual message parameters will be serialized separately.
2180 */
2181static SSMFIELD const s_aShClSSMClientMsgHdr[] =
2182{
2183 SSMFIELD_ENTRY(SHCLCLIENTMSG, uMsg),
2184 SSMFIELD_ENTRY(SHCLCLIENTMSG, cParms),
2185 SSMFIELD_ENTRY_TERM()
2186};
2187
2188/**
2189 * SSM descriptor table for the VBOXSHCLMSGCTX structure.
2190 */
2191static SSMFIELD const s_aShClSSMClientMsgCtx[] =
2192{
2193 SSMFIELD_ENTRY(SHCLMSGCTX, uContextID),
2194 SSMFIELD_ENTRY_TERM()
2195};
2196#endif /* !UNIT_TEST */
2197
2198static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
2199{
2200 LogFlowFuncEnter();
2201
2202#ifndef UNIT_TEST
2203 /*
2204 * When the state will be restored, pending requests will be reissued
2205 * by VMMDev. The service therefore must save state as if there were no
2206 * pending request.
2207 * Pending requests, if any, will be completed in svcDisconnect.
2208 */
2209 RT_NOREF(u32ClientID);
2210 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
2211
2212 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
2213 AssertPtr(pClient);
2214
2215 /* Write Shared Clipboard saved state version. */
2216 SSMR3PutU32(pSSM, VBOX_SHCL_SAVED_STATE_VER_CURRENT);
2217
2218 int rc = SSMR3PutStructEx(pSSM, &pClient->State, sizeof(pClient->State), 0 /*fFlags*/, &s_aShClSSMClientState[0], NULL);
2219 AssertRCReturn(rc, rc);
2220
2221 rc = SSMR3PutStructEx(pSSM, &pClient->State.POD, sizeof(pClient->State.POD), 0 /*fFlags*/, &s_aShClSSMClientPODState[0], NULL);
2222 AssertRCReturn(rc, rc);
2223
2224 rc = SSMR3PutStructEx(pSSM, &pClient->State.Transfers, sizeof(pClient->State.Transfers), 0 /*fFlags*/, &s_aShClSSMClientTransferState[0], NULL);
2225 AssertRCReturn(rc, rc);
2226
2227 /* Serialize the client's internal message queue. */
2228 rc = SSMR3PutU64(pSSM, (uint64_t)pClient->queueMsg.size());
2229 AssertRCReturn(rc, rc);
2230
2231 for (size_t i = 0; i < pClient->queueMsg.size(); i++)
2232 {
2233 PSHCLCLIENTMSG pMsg = pClient->queueMsg.at(i);
2234 AssertPtr(pMsg);
2235
2236 rc = SSMR3PutStructEx(pSSM, pMsg, sizeof(SHCLCLIENTMSG), 0 /*fFlags*/, &s_aShClSSMClientMsgHdr[0], NULL);
2237 AssertRCReturn(rc, rc);
2238
2239 rc = SSMR3PutStructEx(pSSM, &pMsg->Ctx, sizeof(SHCLMSGCTX), 0 /*fFlags*/, &s_aShClSSMClientMsgCtx[0], NULL);
2240 AssertRCReturn(rc, rc);
2241
2242 for (uint32_t p = 0; p < pMsg->cParms; p++)
2243 {
2244 rc = HGCMSvcSSMR3Put(&pMsg->paParms[p], pSSM);
2245 AssertRCReturn(rc, rc);
2246 }
2247 }
2248
2249#else /* UNIT_TEST */
2250 RT_NOREF3(u32ClientID, pvClient, pSSM);
2251#endif /* UNIT_TEST */
2252 return VINF_SUCCESS;
2253}
2254
2255#ifndef UNIT_TEST
2256static int svcLoadStateV0(uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
2257{
2258 RT_NOREF(u32ClientID, pvClient, pSSM, uVersion);
2259
2260 uint32_t uMarker;
2261 int rc = SSMR3GetU32(pSSM, &uMarker); /* Begin marker. */
2262 AssertRC(rc);
2263 Assert(uMarker == UINT32_C(0x19200102) /* SSMR3STRUCT_BEGIN */);
2264
2265 rc = SSMR3Skip(pSSM, sizeof(uint32_t)); /* Client ID */
2266 AssertRCReturn(rc, rc);
2267
2268 bool fValue;
2269 rc = SSMR3GetBool(pSSM, &fValue); /* fHostMsgQuit */
2270 AssertRCReturn(rc, rc);
2271
2272 rc = SSMR3GetBool(pSSM, &fValue); /* fHostMsgReadData */
2273 AssertRCReturn(rc, rc);
2274
2275 rc = SSMR3GetBool(pSSM, &fValue); /* fHostMsgFormats */
2276 AssertRCReturn(rc, rc);
2277
2278 uint32_t fFormats;
2279 rc = SSMR3GetU32(pSSM, &fFormats); /* u32RequestedFormat */
2280 AssertRCReturn(rc, rc);
2281
2282 rc = SSMR3GetU32(pSSM, &uMarker); /* End marker. */
2283 AssertRCReturn(rc, rc);
2284 Assert(uMarker == UINT32_C(0x19920406) /* SSMR3STRUCT_END */);
2285
2286 return VINF_SUCCESS;
2287}
2288#endif /* UNIT_TEST */
2289
2290static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
2291{
2292 LogFlowFuncEnter();
2293
2294#ifndef UNIT_TEST
2295
2296 RT_NOREF(u32ClientID, uVersion);
2297
2298 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
2299 AssertPtr(pClient);
2300
2301 /* Restore the client data. */
2302 uint32_t lenOrVer;
2303 int rc = SSMR3GetU32(pSSM, &lenOrVer);
2304 AssertRCReturn(rc, rc);
2305
2306 LogFunc(("u32ClientID=%RU32, lenOrVer=%#RX64\n", u32ClientID, lenOrVer));
2307
2308 if (lenOrVer == VBOX_SHCL_SAVED_STATE_VER_3_1)
2309 return svcLoadStateV0(u32ClientID, pvClient, pSSM, uVersion);
2310
2311 if ( lenOrVer >= VBOX_SHCL_SAVED_STATE_VER_6_1B2
2312 && lenOrVer <= VBOX_SHCL_SAVED_STATE_VER_CURRENT)
2313 {
2314 if (lenOrVer >= VBOX_SHCL_SAVED_STATE_VER_6_1RC1)
2315 {
2316 SSMR3GetStructEx(pSSM, &pClient->State, sizeof(pClient->State), 0 /* fFlags */, &s_aShClSSMClientState[0], NULL);
2317 SSMR3GetStructEx(pSSM, &pClient->State.POD, sizeof(pClient->State.POD), 0 /* fFlags */,
2318 &s_aShClSSMClientPODState[0], NULL);
2319 }
2320 else
2321 SSMR3GetStructEx(pSSM, &pClient->State, sizeof(pClient->State), 0 /* fFlags */, &s_aShClSSMClientState61B1[0], NULL);
2322 rc = SSMR3GetStructEx(pSSM, &pClient->State.Transfers, sizeof(pClient->State.Transfers), 0 /* fFlags */,
2323 &s_aShClSSMClientTransferState[0], NULL);
2324 AssertRCReturn(rc, rc);
2325
2326 /* Load the client's internal message queue. */
2327 uint64_t cMsgs;
2328 rc = SSMR3GetU64(pSSM, &cMsgs);
2329 AssertRCReturn(rc, rc);
2330 AssertLogRelMsgReturn(cMsgs < _16K, ("Too many messages: %u (%x)\n", cMsgs, cMsgs), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
2331
2332 for (uint64_t i = 0; i < cMsgs; i++)
2333 {
2334 PSHCLCLIENTMSG pMsg = (PSHCLCLIENTMSG)RTMemAlloc(sizeof(SHCLCLIENTMSG));
2335 AssertReturn(pMsg, VERR_NO_MEMORY);
2336
2337 SSMR3GetStructEx(pSSM, pMsg, sizeof(SHCLCLIENTMSG), 0 /*fFlags*/, &s_aShClSSMClientMsgHdr[0], NULL);
2338 rc = SSMR3GetStructEx(pSSM, &pMsg->Ctx, sizeof(SHCLMSGCTX), 0 /*fFlags*/, &s_aShClSSMClientMsgCtx[0], NULL);
2339 AssertRCReturnStmt(rc, RTMemFree(pMsg), rc);
2340
2341 AssertLogRelMsgReturnStmt(pMsg->cParms <= VMMDEV_MAX_HGCM_PARMS,
2342 ("Too many HGCM message parameters: %u (%#x)\n", pMsg->cParms, pMsg->cParms),
2343 RTMemFree(pMsg),
2344 VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
2345
2346 pMsg->paParms = (PVBOXHGCMSVCPARM)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * pMsg->cParms);
2347 AssertReturnStmt(pMsg->paParms, RTMemFree(pMsg), VERR_NO_MEMORY);
2348
2349 for (uint32_t p = 0; p < pMsg->cParms; p++)
2350 {
2351 rc = HGCMSvcSSMR3Get(&pMsg->paParms[p], pSSM);
2352 AssertRCReturn(rc, rc); /* we'll leak stuff here, oh well... */
2353 }
2354
2355 rc = shClSvcMsgAdd(pClient, pMsg, true /* fAppend */);
2356 AssertRCReturn(rc, rc);
2357 }
2358 }
2359 else
2360 {
2361 LogRel(("Shared Clipboard: Unsupported saved state version (%#x)\n", lenOrVer));
2362 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2363 }
2364
2365 /* Actual host data are to be reported to guest (SYNC). */
2366 ShClSvcImplSync(pClient);
2367
2368#else /* UNIT_TEST */
2369 RT_NOREF(u32ClientID, pvClient, pSSM, uVersion);
2370#endif /* UNIT_TEST */
2371 return VINF_SUCCESS;
2372}
2373
2374static DECLCALLBACK(int) extCallback(uint32_t u32Function, uint32_t u32Format, void *pvData, uint32_t cbData)
2375{
2376 RT_NOREF(pvData, cbData);
2377
2378 LogFlowFunc(("u32Function=%RU32\n", u32Function));
2379
2380 int rc = VINF_SUCCESS;
2381
2382 /* Figure out if the client in charge for the service extension still is connected. */
2383 ClipboardClientMap::const_iterator itClient = g_mapClients.find(g_ExtState.uClientID);
2384 if (itClient != g_mapClients.end())
2385 {
2386 PSHCLCLIENT pClient = itClient->second;
2387 AssertPtr(pClient);
2388
2389 switch (u32Function)
2390 {
2391 /* The service extension announces formats to the guest. */
2392 case VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE:
2393 {
2394 LogFlowFunc(("VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE: g_ExtState.fReadingData=%RTbool\n", g_ExtState.fReadingData));
2395 if (g_ExtState.fReadingData)
2396 {
2397 g_ExtState.fDelayedAnnouncement = true;
2398 g_ExtState.uDelayedFormats = u32Format;
2399 }
2400 else if (u32Format != VBOX_SHCL_FMT_NONE)
2401 {
2402 SHCLFORMATDATA formatData;
2403 RT_ZERO(formatData);
2404
2405 formatData.Formats = u32Format;
2406
2407 rc = ShClSvcFormatsReport(pClient, &formatData);
2408 }
2409
2410 break;
2411 }
2412
2413 /* The service extension wants read data from the guest. */
2414 case VBOX_CLIPBOARD_EXT_FN_DATA_READ:
2415 {
2416 SHCLDATAREQ dataReq;
2417 RT_ZERO(dataReq);
2418
2419 dataReq.uFmt = u32Format;
2420 dataReq.cbSize = _64K; /** @todo Make this more dynamic. */
2421
2422 rc = ShClSvcDataReadRequest(pClient, &dataReq, NULL /* puEvent */);
2423 break;
2424 }
2425
2426 default:
2427 /* Just skip other messages. */
2428 break;
2429 }
2430 }
2431 else
2432 rc = VERR_NOT_FOUND;
2433
2434 LogFlowFuncLeaveRC(rc);
2435 return rc;
2436}
2437
2438static DECLCALLBACK(int) svcRegisterExtension(void *, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
2439{
2440 LogFlowFunc(("pfnExtension=%p\n", pfnExtension));
2441
2442 SHCLEXTPARMS parms;
2443 RT_ZERO(parms);
2444
2445 if (pfnExtension)
2446 {
2447 /* Install extension. */
2448 g_ExtState.pfnExtension = pfnExtension;
2449 g_ExtState.pvExtension = pvExtension;
2450
2451 parms.u.pfnCallback = extCallback;
2452
2453 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
2454 }
2455 else
2456 {
2457 if (g_ExtState.pfnExtension)
2458 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
2459
2460 /* Uninstall extension. */
2461 g_ExtState.pfnExtension = NULL;
2462 g_ExtState.pvExtension = NULL;
2463 }
2464
2465 return VINF_SUCCESS;
2466}
2467
2468extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
2469{
2470 int rc = VINF_SUCCESS;
2471
2472 LogFlowFunc(("pTable=%p\n", pTable));
2473
2474 if (!VALID_PTR(pTable))
2475 {
2476 rc = VERR_INVALID_PARAMETER;
2477 }
2478 else
2479 {
2480 LogFunc(("pTable->cbSize = %d, ptable->u32Version = 0x%08X\n", pTable->cbSize, pTable->u32Version));
2481
2482 if ( pTable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
2483 || pTable->u32Version != VBOX_HGCM_SVC_VERSION)
2484 {
2485 rc = VERR_VERSION_MISMATCH;
2486 }
2487 else
2488 {
2489 g_pHelpers = pTable->pHelpers;
2490
2491 pTable->cbClient = sizeof(SHCLCLIENT);
2492
2493 pTable->pfnUnload = svcUnload;
2494 pTable->pfnConnect = svcConnect;
2495 pTable->pfnDisconnect = svcDisconnect;
2496 pTable->pfnCall = svcCall;
2497 pTable->pfnHostCall = svcHostCall;
2498 pTable->pfnSaveState = svcSaveState;
2499 pTable->pfnLoadState = svcLoadState;
2500 pTable->pfnRegisterExtension = svcRegisterExtension;
2501 pTable->pfnNotify = NULL;
2502 pTable->pvService = NULL;
2503
2504 /* Service specific initialization. */
2505 rc = svcInit();
2506 }
2507 }
2508
2509 LogFlowFunc(("Returning %Rrc\n", rc));
2510 return rc;
2511}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette