VirtualBox

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

Last change on this file since 92017 was 92017, checked in by vboxsync, 3 years ago

SharedClipboard: Corrected another pvData assertion preventing zero byte replies from getting thru in the VBOX_SHCL_GUEST_FN_DATA_WRITE path, causing waiting host clipboard code to get stuck waiting/timeout. Introduced in r135860, possibly by accident and probably not help by zero documentation around ShClSvcGuestDataSignal at that time. Made the code signal the waiter even if we fail to duplicate the data. bugref:10094

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 100.1 KB
Line 
1/* $Id: VBoxSharedClipboardSvc.cpp 92017 2021-10-24 12:42:46Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Host service entry points.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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_MSG_OLD_GET_WAIT, 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_REPORT_FORMATS, 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_MSG_OLD_GET_WAIT 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_transfer_structure Transfer handling structure
120 *
121 * All structures / classes are designed for running on both, on the guest
122 * (via VBoxTray / VBoxClient) or on the host (host service) to avoid code
123 * duplication where applicable.
124 *
125 * Per HGCM client there is a so-called "transfer context", which in turn can
126 * have one or mulitple so-called "Shared Clipboard transfer" objects. At the
127 * moment we only support on concurrent Shared Clipboard transfer per transfer
128 * context. It's being used for reading from a source or writing to destination,
129 * depening on its direction. An Shared Clipboard transfer can have optional
130 * callbacks which might be needed by various implementations. Also, transfers
131 * optionally can run in an asynchronous thread to prevent blocking the UI while
132 * running.
133 *
134 * @section sec_transfer_providers Transfer providers
135 *
136 * For certain implementations (for example on Windows guests / hosts, using
137 * IDataObject and IStream objects) a more flexible approach reqarding reading /
138 * writing is needed. For this so-called transfer providers abstract the way of how
139 * data is being read / written in the current context (host / guest), while
140 * the rest of the code stays the same.
141 *
142 * @section sec_transfer_protocol Transfer protocol
143 *
144 * The host service issues commands which the guest has to respond with an own
145 * message to. The protocol itself is designed so that it has primitives to list
146 * directories and open/close/read/write file system objects.
147 *
148 * Note that this is different from the DnD approach, as Shared Clipboard transfers
149 * need to be deeper integrated within the host / guest OS (i.e. for progress UI),
150 * and this might require non-monolithic / random access APIs to achieve.
151 *
152 * As there can be multiple file system objects (fs objects) selected for transfer,
153 * a transfer can be queried for its root entries, which then contains the top-level
154 * elements. Based on these elements, (a) (recursive) listing(s) can be performed
155 * to (partially) walk down into directories and query fs object information. The
156 * provider provides appropriate interface for this, even if not all implementations
157 * might need this mechanism.
158 *
159 * An Shared Clipboard transfer has three stages:
160 * - 1. Announcement: An Shared Clipboard transfer-compatible format (currently only one format available)
161 * has been announced, the destination side creates a transfer object, which then,
162 * depending on the actual implementation, can be used to tell the OS that
163 * there is transfer (file) data available.
164 * At this point this just acts as a (kind-of) promise to the OS that we
165 * can provide (file) data at some later point in time.
166 *
167 * - 2. Initialization: As soon as the OS requests the (file) data, mostly triggered
168 * by the user starting a paste operation (CTRL + V), the transfer get initialized
169 * on the destination side, which in turn lets the source know that a transfer
170 * is going to happen.
171 *
172 * - 3. Transfer: At this stage the actual transfer from source to the destination takes
173 * place. How the actual transfer is structurized (e.g. which files / directories
174 * are transferred in which order) depends on the destination implementation. This
175 * is necessary in order to fulfill requirements on the destination side with
176 * regards to ETA calculation or other dependencies.
177 * Both sides can abort or cancel the transfer at any time.
178 */
179
180
181/*********************************************************************************************************************************
182* Header Files *
183*********************************************************************************************************************************/
184#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
185#include <VBox/log.h>
186
187#include <VBox/GuestHost/clipboard-helper.h>
188#include <VBox/HostServices/Service.h>
189#include <VBox/HostServices/VBoxClipboardSvc.h>
190#include <VBox/HostServices/VBoxClipboardExt.h>
191
192#include <VBox/AssertGuest.h>
193#include <VBox/err.h>
194#include <VBox/VMMDev.h>
195#include <VBox/vmm/ssm.h>
196
197#include <iprt/mem.h>
198#include <iprt/string.h>
199#include <iprt/assert.h>
200#include <iprt/critsect.h>
201#include <iprt/rand.h>
202
203#include "VBoxSharedClipboardSvc-internal.h"
204#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
205# include "VBoxSharedClipboardSvc-transfers.h"
206#endif
207
208using namespace HGCM;
209
210
211/*********************************************************************************************************************************
212* Defined Constants And Macros *
213*********************************************************************************************************************************/
214/** @name The saved state versions for the shared clipboard service.
215 *
216 * @note We set bit 31 because prior to version 0x80000002 there would be a
217 * structure size rather than a version number. Setting bit 31 dispells
218 * any possible ambiguity.
219 *
220 * @{ */
221/** The current saved state version. */
222#define VBOX_SHCL_SAVED_STATE_VER_CURRENT VBOX_SHCL_SAVED_STATE_LEGACY_CID
223/** Adds the legacy context ID list. */
224#define VBOX_SHCL_SAVED_STATE_LEGACY_CID UINT32_C(0x80000005)
225/** Adds the client's POD state and client state flags.
226 * @since 6.1 RC1 */
227#define VBOX_SHCL_SAVED_STATE_VER_6_1RC1 UINT32_C(0x80000004)
228/** First attempt saving state during @bugref{9437} development.
229 * @since 6.1 BETA 2 */
230#define VBOX_SHCL_SAVED_STATE_VER_6_1B2 UINT32_C(0x80000003)
231/** First structured version.
232 * @since 3.1 / r53668 */
233#define VBOX_SHCL_SAVED_STATE_VER_3_1 UINT32_C(0x80000002)
234/** This was just a state memory dump, including pointers and everything.
235 * @note This is not supported any more. Sorry. */
236#define VBOX_SHCL_SAVED_STATE_VER_NOT_SUPP (ARCH_BITS == 64 ? UINT32_C(72) : UINT32_C(48))
237/** @} */
238
239
240/*********************************************************************************************************************************
241* Global Variables *
242*********************************************************************************************************************************/
243PVBOXHGCMSVCHELPERS g_pHelpers;
244
245static RTCRITSECT g_CritSect; /** @todo r=andy Put this into some instance struct, avoid globals. */
246/** Global Shared Clipboard mode. */
247static uint32_t g_uMode = VBOX_SHCL_MODE_OFF;
248#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
249/** Global Shared Clipboard (file) transfer mode. */
250uint32_t g_fTransferMode = VBOX_SHCL_TRANSFER_MODE_DISABLED;
251#endif
252
253/** Is the clipboard running in headless mode? */
254static bool g_fHeadless = false;
255
256/** Holds the service extension state. */
257SHCLEXTSTATE g_ExtState = { 0 };
258
259/** Global map of all connected clients. */
260ClipboardClientMap g_mapClients;
261
262/** Global list of all clients which are queued up (deferred return) and ready
263 * to process new commands. The key is the (unique) client ID. */
264ClipboardClientQueue g_listClientsDeferred;
265
266/** Host feature mask (VBOX_SHCL_HF_0_XXX) for VBOX_SHCL_GUEST_FN_REPORT_FEATURES
267 * and VBOX_SHCL_GUEST_FN_QUERY_FEATURES. */
268static uint64_t const g_fHostFeatures0 = VBOX_SHCL_HF_0_CONTEXT_ID
269#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
270 | VBOX_SHCL_HF_0_TRANSFERS
271#endif
272 ;
273
274
275/**
276 * Returns the current Shared Clipboard service mode.
277 *
278 * @returns Current Shared Clipboard service mode.
279 */
280uint32_t ShClSvcGetMode(void)
281{
282 return g_uMode;
283}
284
285/**
286 * Getter for headless setting. Also needed by testcase.
287 *
288 * @returns Whether service currently running in headless mode or not.
289 */
290bool ShClSvcGetHeadless(void)
291{
292 return g_fHeadless;
293}
294
295static int shClSvcModeSet(uint32_t uMode)
296{
297 int rc = VERR_NOT_SUPPORTED;
298
299 switch (uMode)
300 {
301 case VBOX_SHCL_MODE_OFF:
302 RT_FALL_THROUGH();
303 case VBOX_SHCL_MODE_HOST_TO_GUEST:
304 RT_FALL_THROUGH();
305 case VBOX_SHCL_MODE_GUEST_TO_HOST:
306 RT_FALL_THROUGH();
307 case VBOX_SHCL_MODE_BIDIRECTIONAL:
308 {
309 g_uMode = uMode;
310
311 rc = VINF_SUCCESS;
312 break;
313 }
314
315 default:
316 {
317 g_uMode = VBOX_SHCL_MODE_OFF;
318 break;
319 }
320 }
321
322 LogFlowFuncLeaveRC(rc);
323 return rc;
324}
325
326/**
327 * Takes the global Shared Clipboard service lock.
328 *
329 * @returns \c true if locking was successful, or \c false if not.
330 */
331bool ShClSvcLock(void)
332{
333 return RT_SUCCESS(RTCritSectEnter(&g_CritSect));
334}
335
336/**
337 * Unlocks the formerly locked global Shared Clipboard service lock.
338 */
339void ShClSvcUnlock(void)
340{
341 int rc2 = RTCritSectLeave(&g_CritSect);
342 AssertRC(rc2);
343}
344
345/**
346 * Resets a client's state message queue.
347 *
348 * @param pClient Pointer to the client data structure to reset message queue for.
349 * @note Caller enters pClient->CritSect.
350 */
351void shClSvcMsgQueueReset(PSHCLCLIENT pClient)
352{
353 Assert(RTCritSectIsOwner(&pClient->CritSect));
354 LogFlowFuncEnter();
355
356 while (!RTListIsEmpty(&pClient->MsgQueue))
357 {
358 PSHCLCLIENTMSG pMsg = RTListRemoveFirst(&pClient->MsgQueue, SHCLCLIENTMSG, ListEntry);
359 shClSvcMsgFree(pClient, pMsg);
360 }
361 pClient->cMsgAllocated = 0;
362
363 while (!RTListIsEmpty(&pClient->Legacy.lstCID))
364 {
365 PSHCLCLIENTLEGACYCID pCID = RTListRemoveFirst(&pClient->Legacy.lstCID, SHCLCLIENTLEGACYCID, Node);
366 RTMemFree(pCID);
367 }
368 pClient->Legacy.cCID = 0;
369}
370
371/**
372 * Allocates a new clipboard message.
373 *
374 * @returns Allocated clipboard message, or NULL on failure.
375 * @param pClient The client which is target of this message.
376 * @param idMsg The message ID (VBOX_SHCL_HOST_MSG_XXX) to use
377 * @param cParms The number of parameters the message takes.
378 */
379PSHCLCLIENTMSG shClSvcMsgAlloc(PSHCLCLIENT pClient, uint32_t idMsg, uint32_t cParms)
380{
381 RT_NOREF(pClient);
382 PSHCLCLIENTMSG pMsg = (PSHCLCLIENTMSG)RTMemAllocZ(RT_UOFFSETOF_DYN(SHCLCLIENTMSG, aParms[cParms]));
383 if (pMsg)
384 {
385 uint32_t cAllocated = ASMAtomicIncU32(&pClient->cMsgAllocated);
386 if (cAllocated <= 4096)
387 {
388 RTListInit(&pMsg->ListEntry);
389 pMsg->cParms = cParms;
390 pMsg->idMsg = idMsg;
391 return pMsg;
392 }
393 AssertMsgFailed(("Too many messages allocated for client %u! (%u)\n", pClient->State.uClientID, cAllocated));
394 ASMAtomicDecU32(&pClient->cMsgAllocated);
395 RTMemFree(pMsg);
396 }
397 return NULL;
398}
399
400/**
401 * Frees a formerly allocated clipboard message.
402 *
403 * @param pClient The client which was the target of this message.
404 * @param pMsg Clipboard message to free.
405 */
406void shClSvcMsgFree(PSHCLCLIENT pClient, PSHCLCLIENTMSG pMsg)
407{
408 RT_NOREF(pClient);
409 /** @todo r=bird: Do accounting. */
410 if (pMsg)
411 {
412 pMsg->idMsg = UINT32_C(0xdeadface);
413 RTMemFree(pMsg);
414
415 uint32_t cAllocated = ASMAtomicDecU32(&pClient->cMsgAllocated);
416 Assert(cAllocated < UINT32_MAX / 2);
417 RT_NOREF(cAllocated);
418 }
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 */
430static void 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->idMsg;
435 else
436 paDstParms[0].u.uint64 = pMsg->idMsg;
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->aParms[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->aParms[i - 2].u.pointer.size; break;
446 }
447}
448
449/**
450 * Sets the VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT return parameters.
451 *
452 * @returns VBox status code.
453 * @param pMsg The message which parameters to return to the guest.
454 * @param paDstParms The peek parameter vector.
455 * @param cDstParms The number of peek parameters should be exactly two
456 */
457static int shClSvcMsgSetOldWaitReturn(PSHCLCLIENTMSG pMsg, PVBOXHGCMSVCPARM paDstParms, uint32_t cDstParms)
458{
459 /*
460 * Assert sanity.
461 */
462 AssertPtr(pMsg);
463 AssertPtrReturn(paDstParms, VERR_INVALID_POINTER);
464 AssertReturn(cDstParms >= 2, VERR_INVALID_PARAMETER);
465
466 Assert(pMsg->cParms == 2);
467 Assert(pMsg->aParms[0].u.uint32 == pMsg->idMsg);
468 switch (pMsg->idMsg)
469 {
470 case VBOX_SHCL_HOST_MSG_READ_DATA:
471 case VBOX_SHCL_HOST_MSG_FORMATS_REPORT:
472 break;
473 default:
474 AssertFailed();
475 }
476
477 /*
478 * Set the parameters.
479 */
480 if (pMsg->cParms > 0)
481 paDstParms[0] = pMsg->aParms[0];
482 if (pMsg->cParms > 1)
483 paDstParms[1] = pMsg->aParms[1];
484 return VINF_SUCCESS;
485}
486
487/**
488 * Adds a new message to a client'S message queue.
489 *
490 * @param pClient Pointer to the client data structure to add new message to.
491 * @param pMsg Pointer to message to add. The queue then owns the pointer.
492 * @param fAppend Whether to append or prepend the message to the queue.
493 *
494 * @note Caller must enter critical section.
495 */
496void shClSvcMsgAdd(PSHCLCLIENT pClient, PSHCLCLIENTMSG pMsg, bool fAppend)
497{
498 Assert(RTCritSectIsOwned(&pClient->CritSect));
499 AssertPtr(pMsg);
500
501 LogFlowFunc(("idMsg=%s (%RU32) cParms=%RU32 fAppend=%RTbool\n",
502 ShClHostMsgToStr(pMsg->idMsg), pMsg->idMsg, pMsg->cParms, fAppend));
503
504 if (fAppend)
505 RTListAppend(&pClient->MsgQueue, &pMsg->ListEntry);
506 else
507 RTListPrepend(&pClient->MsgQueue, &pMsg->ListEntry);
508}
509
510
511/**
512 * Appends a message to the client's queue and wake it up.
513 *
514 * @returns VBox status code, though the message is consumed regardless of what
515 * is returned.
516 * @param pClient The client to queue the message on.
517 * @param pMsg The message to queue. Ownership is always
518 * transfered to the queue.
519 *
520 * @note Caller must enter critical section.
521 */
522int shClSvcMsgAddAndWakeupClient(PSHCLCLIENT pClient, PSHCLCLIENTMSG pMsg)
523{
524 Assert(RTCritSectIsOwned(&pClient->CritSect));
525 AssertPtr(pMsg);
526 AssertPtr(pClient);
527 LogFlowFunc(("idMsg=%s (%u) cParms=%u\n", ShClHostMsgToStr(pMsg->idMsg), pMsg->idMsg, pMsg->cParms));
528
529 RTListAppend(&pClient->MsgQueue, &pMsg->ListEntry);
530 return shClSvcClientWakeup(pClient);
531}
532
533/**
534 * Initializes a Shared Clipboard client.
535 *
536 * @param pClient Client to initialize.
537 * @param uClientID HGCM client ID to assign client to.
538 */
539int shClSvcClientInit(PSHCLCLIENT pClient, uint32_t uClientID)
540{
541 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
542
543 /* Assign the client ID. */
544 pClient->State.uClientID = uClientID;
545
546 RTListInit(&pClient->MsgQueue);
547 pClient->cMsgAllocated = 0;
548
549 RTListInit(&pClient->Legacy.lstCID);
550 pClient->Legacy.cCID = 0;
551
552 LogFlowFunc(("[Client %RU32]\n", pClient->State.uClientID));
553
554 int rc = RTCritSectInit(&pClient->CritSect);
555 if (RT_SUCCESS(rc))
556 {
557 /* Create the client's own event source. */
558 rc = ShClEventSourceCreate(&pClient->EventSrc, 0 /* ID, ignored */);
559 if (RT_SUCCESS(rc))
560 {
561 LogFlowFunc(("[Client %RU32] Using event source %RU32\n", uClientID, pClient->EventSrc.uID));
562
563 /* Reset the client state. */
564 shclSvcClientStateReset(&pClient->State);
565
566 /* (Re-)initialize the client state. */
567 rc = shClSvcClientStateInit(&pClient->State, uClientID);
568
569#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
570 if (RT_SUCCESS(rc))
571 rc = ShClTransferCtxInit(&pClient->Transfers.Ctx);
572#endif
573 }
574 }
575
576 LogFlowFuncLeaveRC(rc);
577 return rc;
578}
579
580/**
581 * Destroys a Shared Clipboard client.
582 *
583 * @param pClient Client to destroy.
584 */
585void shClSvcClientDestroy(PSHCLCLIENT pClient)
586{
587 AssertPtrReturnVoid(pClient);
588
589 LogFlowFunc(("[Client %RU32]\n", pClient->State.uClientID));
590
591 /* Make sure to send a quit message to the guest so that it can terminate gracefully. */
592 RTCritSectEnter(&pClient->CritSect);
593 if (pClient->Pending.uType)
594 {
595 if (pClient->Pending.cParms > 1)
596 HGCMSvcSetU32(&pClient->Pending.paParms[0], VBOX_SHCL_HOST_MSG_QUIT);
597 if (pClient->Pending.cParms > 2)
598 HGCMSvcSetU32(&pClient->Pending.paParms[1], 0);
599 g_pHelpers->pfnCallComplete(pClient->Pending.hHandle, VINF_SUCCESS);
600 pClient->Pending.uType = 0;
601 pClient->Pending.cParms = 0;
602 pClient->Pending.hHandle = NULL;
603 pClient->Pending.paParms = NULL;
604 }
605 RTCritSectLeave(&pClient->CritSect);
606
607 ShClEventSourceDestroy(&pClient->EventSrc);
608
609 shClSvcClientStateDestroy(&pClient->State);
610
611 PSHCLCLIENTLEGACYCID pCidIter, pCidIterNext;
612 RTListForEachSafe(&pClient->Legacy.lstCID, pCidIter, pCidIterNext, SHCLCLIENTLEGACYCID, Node)
613 {
614 RTMemFree(pCidIter);
615 }
616
617 int rc2 = RTCritSectDelete(&pClient->CritSect);
618 AssertRC(rc2);
619
620 ClipboardClientMap::iterator itClient = g_mapClients.find(pClient->State.uClientID);
621 if (itClient != g_mapClients.end())
622 g_mapClients.erase(itClient);
623 else
624 AssertFailed();
625
626 LogFlowFuncLeave();
627}
628
629void shClSvcClientLock(PSHCLCLIENT pClient)
630{
631 int rc2 = RTCritSectEnter(&pClient->CritSect);
632 AssertRC(rc2);
633}
634
635void shClSvcClientUnlock(PSHCLCLIENT pClient)
636{
637 int rc2 = RTCritSectLeave(&pClient->CritSect);
638 AssertRC(rc2);
639}
640
641/**
642 * Resets a Shared Clipboard client.
643 *
644 * @param pClient Client to reset.
645 */
646void shClSvcClientReset(PSHCLCLIENT pClient)
647{
648 if (!pClient)
649 return;
650
651 LogFlowFunc(("[Client %RU32]\n", pClient->State.uClientID));
652 RTCritSectEnter(&pClient->CritSect);
653
654 /* Reset message queue. */
655 shClSvcMsgQueueReset(pClient);
656
657 /* Reset event source. */
658 ShClEventSourceReset(&pClient->EventSrc);
659
660 /* Reset pending state. */
661 RT_ZERO(pClient->Pending);
662
663#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
664 shClSvcClientTransfersReset(pClient);
665#endif
666
667 shclSvcClientStateReset(&pClient->State);
668
669 RTCritSectLeave(&pClient->CritSect);
670}
671
672static int shClSvcClientNegogiateChunkSize(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall,
673 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
674{
675 /*
676 * Validate the request.
677 */
678 ASSERT_GUEST_RETURN(cParms == VBOX_SHCL_CPARMS_NEGOTIATE_CHUNK_SIZE, VERR_WRONG_PARAMETER_COUNT);
679 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
680 uint32_t const cbClientMaxChunkSize = paParms[0].u.uint32;
681 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
682 uint32_t const cbClientChunkSize = paParms[1].u.uint32;
683
684 uint32_t const cbHostMaxChunkSize = VBOX_SHCL_MAX_CHUNK_SIZE; /** @todo Make this configurable. */
685
686 /*
687 * Do the work.
688 */
689 if (cbClientChunkSize == 0) /* Does the client want us to choose? */
690 {
691 paParms[0].u.uint32 = cbHostMaxChunkSize; /* Maximum */
692 paParms[1].u.uint32 = RT_MIN(pClient->State.cbChunkSize, cbHostMaxChunkSize); /* Preferred */
693
694 }
695 else /* The client told us what it supports, so update and report back. */
696 {
697 paParms[0].u.uint32 = RT_MIN(cbClientMaxChunkSize, cbHostMaxChunkSize); /* Maximum */
698 paParms[1].u.uint32 = RT_MIN(cbClientMaxChunkSize, pClient->State.cbChunkSize); /* Preferred */
699 }
700
701 int rc = g_pHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
702 if (RT_SUCCESS(rc))
703 {
704 Log(("[Client %RU32] chunk size: %#RU32, max: %#RU32\n",
705 pClient->State.uClientID, paParms[1].u.uint32, paParms[0].u.uint32));
706 }
707 else
708 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
709
710 return VINF_HGCM_ASYNC_EXECUTE;
711}
712
713/**
714 * Implements VBOX_SHCL_GUEST_FN_REPORT_FEATURES.
715 *
716 * @returns VBox status code.
717 * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
718 * @retval VERR_ACCESS_DENIED if not master
719 * @retval VERR_INVALID_PARAMETER if bit 63 in the 2nd parameter isn't set.
720 * @retval VERR_WRONG_PARAMETER_COUNT
721 *
722 * @param pClient The client state.
723 * @param hCall The client's call handle.
724 * @param cParms Number of parameters.
725 * @param paParms Array of parameters.
726 */
727static int shClSvcClientReportFeatures(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall,
728 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
729{
730 /*
731 * Validate the request.
732 */
733 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
734 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
735 uint64_t const fFeatures0 = paParms[0].u.uint64;
736 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
737 uint64_t const fFeatures1 = paParms[1].u.uint64;
738 ASSERT_GUEST_RETURN(fFeatures1 & VBOX_SHCL_GF_1_MUST_BE_ONE, VERR_INVALID_PARAMETER);
739
740 /*
741 * Do the work.
742 */
743 paParms[0].u.uint64 = g_fHostFeatures0;
744 paParms[1].u.uint64 = 0;
745
746 int rc = g_pHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
747 if (RT_SUCCESS(rc))
748 {
749 pClient->State.fGuestFeatures0 = fFeatures0;
750 pClient->State.fGuestFeatures1 = fFeatures1;
751 LogRel2(("Shared Clipboard: Guest reported the following features: %#RX64\n",
752 pClient->State.fGuestFeatures0)); /* Note: fFeatures1 not used yet. */
753 if (pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_TRANSFERS)
754 LogRel2(("Shared Clipboard: Guest supports file transfers\n"));
755 }
756 else
757 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
758
759 return VINF_HGCM_ASYNC_EXECUTE;
760}
761
762/**
763 * Implements VBOX_SHCL_GUEST_FN_QUERY_FEATURES.
764 *
765 * @returns VBox status code.
766 * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
767 * @retval VERR_WRONG_PARAMETER_COUNT
768 *
769 * @param hCall The client's call handle.
770 * @param cParms Number of parameters.
771 * @param paParms Array of parameters.
772 */
773static int shClSvcClientQueryFeatures(VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
774{
775 /*
776 * Validate the request.
777 */
778 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
779 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
780 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
781 ASSERT_GUEST(paParms[1].u.uint64 & RT_BIT_64(63));
782
783 /*
784 * Do the work.
785 */
786 paParms[0].u.uint64 = g_fHostFeatures0;
787 paParms[1].u.uint64 = 0;
788 int rc = g_pHelpers->pfnCallComplete(hCall, VINF_SUCCESS);
789 if (RT_FAILURE(rc))
790 LogFunc(("pfnCallComplete -> %Rrc\n", rc));
791
792 return VINF_HGCM_ASYNC_EXECUTE;
793}
794
795/**
796 * Implements VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT and VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT.
797 *
798 * @returns VBox status code.
799 * @retval VINF_SUCCESS if a message was pending and is being returned.
800 * @retval VERR_TRY_AGAIN if no message pending and not blocking.
801 * @retval VERR_RESOURCE_BUSY if another read already made a waiting call.
802 * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending.
803 *
804 * @param pClient The client state.
805 * @param hCall The client's call handle.
806 * @param cParms Number of parameters.
807 * @param paParms Array of parameters.
808 * @param fWait Set if we should wait for a message, clear if to return
809 * immediately.
810 *
811 * @note Caller takes and leave the client's critical section.
812 */
813static int shClSvcClientMsgPeek(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[], bool fWait)
814{
815 /*
816 * Validate the request.
817 */
818 ASSERT_GUEST_MSG_RETURN(cParms >= 2, ("cParms=%u!\n", cParms), VERR_WRONG_PARAMETER_COUNT);
819
820 uint64_t idRestoreCheck = 0;
821 uint32_t i = 0;
822 if (paParms[i].type == VBOX_HGCM_SVC_PARM_64BIT)
823 {
824 idRestoreCheck = paParms[0].u.uint64;
825 paParms[0].u.uint64 = 0;
826 i++;
827 }
828 for (; i < cParms; i++)
829 {
830 ASSERT_GUEST_MSG_RETURN(paParms[i].type == VBOX_HGCM_SVC_PARM_32BIT, ("#%u type=%u\n", i, paParms[i].type),
831 VERR_WRONG_PARAMETER_TYPE);
832 paParms[i].u.uint32 = 0;
833 }
834
835 /*
836 * Check restore session ID.
837 */
838 if (idRestoreCheck != 0)
839 {
840 uint64_t idRestore = g_pHelpers->pfnGetVMMDevSessionId(g_pHelpers);
841 if (idRestoreCheck != idRestore)
842 {
843 paParms[0].u.uint64 = idRestore;
844 LogFlowFunc(("[Client %RU32] VBOX_SHCL_GUEST_FN_MSG_PEEK_XXX -> VERR_VM_RESTORED (%#RX64 -> %#RX64)\n",
845 pClient->State.uClientID, idRestoreCheck, idRestore));
846 return VERR_VM_RESTORED;
847 }
848 Assert(!g_pHelpers->pfnIsCallRestored(hCall));
849 }
850
851 /*
852 * Return information about the first message if one is pending in the list.
853 */
854 PSHCLCLIENTMSG pFirstMsg = RTListGetFirst(&pClient->MsgQueue, SHCLCLIENTMSG, ListEntry);
855 if (pFirstMsg)
856 {
857 shClSvcMsgSetPeekReturn(pFirstMsg, paParms, cParms);
858 LogFlowFunc(("[Client %RU32] VBOX_SHCL_GUEST_FN_MSG_PEEK_XXX -> VINF_SUCCESS (idMsg=%s (%u), cParms=%u)\n",
859 pClient->State.uClientID, ShClHostMsgToStr(pFirstMsg->idMsg), pFirstMsg->idMsg, pFirstMsg->cParms));
860 return VINF_SUCCESS;
861 }
862
863 /*
864 * If we cannot wait, fail the call.
865 */
866 if (!fWait)
867 {
868 LogFlowFunc(("[Client %RU32] GUEST_MSG_PEEK_NOWAIT -> VERR_TRY_AGAIN\n", pClient->State.uClientID));
869 return VERR_TRY_AGAIN;
870 }
871
872 /*
873 * Wait for the host to queue a message for this client.
874 */
875 ASSERT_GUEST_MSG_RETURN(pClient->Pending.uType == 0, ("Already pending! (idClient=%RU32)\n",
876 pClient->State.uClientID), VERR_RESOURCE_BUSY);
877 pClient->Pending.hHandle = hCall;
878 pClient->Pending.cParms = cParms;
879 pClient->Pending.paParms = paParms;
880 pClient->Pending.uType = VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT;
881 LogFlowFunc(("[Client %RU32] Is now in pending mode...\n", pClient->State.uClientID));
882 return VINF_HGCM_ASYNC_EXECUTE;
883}
884
885/**
886 * Implements VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT.
887 *
888 * @returns VBox status code.
889 * @retval VINF_SUCCESS if a message was pending and is being returned.
890 * @retval VINF_HGCM_ASYNC_EXECUTE if message wait is pending.
891 *
892 * @param pClient The client state.
893 * @param hCall The client's call handle.
894 * @param cParms Number of parameters.
895 * @param paParms Array of parameters.
896 *
897 * @note Caller takes and leave the client's critical section.
898 */
899static int shClSvcClientMsgOldGet(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
900{
901 /*
902 * Validate input.
903 */
904 ASSERT_GUEST_RETURN(cParms == VBOX_SHCL_CPARMS_GET_HOST_MSG_OLD, VERR_WRONG_PARAMETER_COUNT);
905 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* id32Msg */
906 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* f32Formats */
907
908 paParms[0].u.uint32 = 0;
909 paParms[1].u.uint32 = 0;
910
911 /*
912 * If there is a message pending we can return immediately.
913 */
914 int rc;
915 PSHCLCLIENTMSG pFirstMsg = RTListGetFirst(&pClient->MsgQueue, SHCLCLIENTMSG, ListEntry);
916 if (pFirstMsg)
917 {
918 LogFlowFunc(("[Client %RU32] uMsg=%s (%RU32), cParms=%RU32\n", pClient->State.uClientID,
919 ShClHostMsgToStr(pFirstMsg->idMsg), pFirstMsg->idMsg, pFirstMsg->cParms));
920
921 rc = shClSvcMsgSetOldWaitReturn(pFirstMsg, paParms, cParms);
922 AssertPtr(g_pHelpers);
923 rc = g_pHelpers->pfnCallComplete(hCall, rc);
924 if (rc != VERR_CANCELLED)
925 {
926 RTListNodeRemove(&pFirstMsg->ListEntry);
927 shClSvcMsgFree(pClient, pFirstMsg);
928
929 rc = VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
930 }
931 }
932 /*
933 * Otherwise we must wait.
934 */
935 else
936 {
937 ASSERT_GUEST_MSG_RETURN(pClient->Pending.uType == 0, ("Already pending! (idClient=%RU32)\n", pClient->State.uClientID),
938 VERR_RESOURCE_BUSY);
939
940 pClient->Pending.hHandle = hCall;
941 pClient->Pending.cParms = cParms;
942 pClient->Pending.paParms = paParms;
943 pClient->Pending.uType = VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT;
944
945 rc = VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
946
947 LogFlowFunc(("[Client %RU32] Is now in pending mode...\n", pClient->State.uClientID));
948 }
949
950 LogFlowFunc(("[Client %RU32] rc=%Rrc\n", pClient->State.uClientID, rc));
951 return rc;
952}
953
954/**
955 * Implements VBOX_SHCL_GUEST_FN_MSG_GET.
956 *
957 * @returns VBox status code.
958 * @retval VINF_SUCCESS if message retrieved and removed from the pending queue.
959 * @retval VERR_TRY_AGAIN if no message pending.
960 * @retval VERR_BUFFER_OVERFLOW if a parmeter buffer is too small. The buffer
961 * size was updated to reflect the required size, though this isn't yet
962 * forwarded to the guest. (The guest is better of using peek with
963 * parameter count + 2 parameters to get the sizes.)
964 * @retval VERR_MISMATCH if the incoming message ID does not match the pending.
965 * @retval VINF_HGCM_ASYNC_EXECUTE if message was completed already.
966 *
967 * @param pClient The client state.
968 * @param hCall The client's call handle.
969 * @param cParms Number of parameters.
970 * @param paParms Array of parameters.
971 *
972 * @note Called from within pClient->CritSect.
973 */
974static int shClSvcClientMsgGet(PSHCLCLIENT pClient, VBOXHGCMCALLHANDLE hCall, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
975{
976 /*
977 * Validate the request.
978 */
979 uint32_t const idMsgExpected = cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT ? paParms[0].u.uint32
980 : cParms > 0 && paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT ? paParms[0].u.uint64
981 : UINT32_MAX;
982
983 /*
984 * Return information about the first message if one is pending in the list.
985 */
986 PSHCLCLIENTMSG pFirstMsg = RTListGetFirst(&pClient->MsgQueue, SHCLCLIENTMSG, ListEntry);
987 if (pFirstMsg)
988 {
989 LogFlowFunc(("First message is: %s (%u), cParms=%RU32\n", ShClHostMsgToStr(pFirstMsg->idMsg), pFirstMsg->idMsg, pFirstMsg->cParms));
990
991 ASSERT_GUEST_MSG_RETURN(pFirstMsg->idMsg == idMsgExpected || idMsgExpected == UINT32_MAX,
992 ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n",
993 pFirstMsg->idMsg, ShClHostMsgToStr(pFirstMsg->idMsg), pFirstMsg->cParms,
994 idMsgExpected, ShClHostMsgToStr(idMsgExpected), cParms),
995 VERR_MISMATCH);
996 ASSERT_GUEST_MSG_RETURN(pFirstMsg->cParms == cParms,
997 ("idMsg=%u (%s) cParms=%u, caller expected %u (%s) and %u\n",
998 pFirstMsg->idMsg, ShClHostMsgToStr(pFirstMsg->idMsg), pFirstMsg->cParms,
999 idMsgExpected, ShClHostMsgToStr(idMsgExpected), cParms),
1000 VERR_WRONG_PARAMETER_COUNT);
1001
1002 /* Check the parameter types. */
1003 for (uint32_t i = 0; i < cParms; i++)
1004 ASSERT_GUEST_MSG_RETURN(pFirstMsg->aParms[i].type == paParms[i].type,
1005 ("param #%u: type %u, caller expected %u (idMsg=%u %s)\n", i, pFirstMsg->aParms[i].type,
1006 paParms[i].type, pFirstMsg->idMsg, ShClHostMsgToStr(pFirstMsg->idMsg)),
1007 VERR_WRONG_PARAMETER_TYPE);
1008 /*
1009 * Copy out the parameters.
1010 *
1011 * No assertions on buffer overflows, and keep going till the end so we can
1012 * communicate all the required buffer sizes.
1013 */
1014 int rc = VINF_SUCCESS;
1015 for (uint32_t i = 0; i < cParms; i++)
1016 switch (pFirstMsg->aParms[i].type)
1017 {
1018 case VBOX_HGCM_SVC_PARM_32BIT:
1019 paParms[i].u.uint32 = pFirstMsg->aParms[i].u.uint32;
1020 break;
1021
1022 case VBOX_HGCM_SVC_PARM_64BIT:
1023 paParms[i].u.uint64 = pFirstMsg->aParms[i].u.uint64;
1024 break;
1025
1026 case VBOX_HGCM_SVC_PARM_PTR:
1027 {
1028 uint32_t const cbSrc = pFirstMsg->aParms[i].u.pointer.size;
1029 uint32_t const cbDst = paParms[i].u.pointer.size;
1030 paParms[i].u.pointer.size = cbSrc; /** @todo Check if this is safe in other layers...
1031 * Update: Safe, yes, but VMMDevHGCM doesn't pass it along. */
1032 if (cbSrc <= cbDst)
1033 memcpy(paParms[i].u.pointer.addr, pFirstMsg->aParms[i].u.pointer.addr, cbSrc);
1034 else
1035 {
1036 AssertMsgFailed(("#%u: cbSrc=%RU32 is bigger than cbDst=%RU32\n", i, cbSrc, cbDst));
1037 rc = VERR_BUFFER_OVERFLOW;
1038 }
1039 break;
1040 }
1041
1042 default:
1043 AssertMsgFailed(("#%u: %u\n", i, pFirstMsg->aParms[i].type));
1044 rc = VERR_INTERNAL_ERROR;
1045 break;
1046 }
1047 if (RT_SUCCESS(rc))
1048 {
1049 /*
1050 * Complete the message and remove the pending message unless the
1051 * guest raced us and cancelled this call in the meantime.
1052 */
1053 AssertPtr(g_pHelpers);
1054 rc = g_pHelpers->pfnCallComplete(hCall, rc);
1055
1056 LogFlowFunc(("[Client %RU32] pfnCallComplete -> %Rrc\n", pClient->State.uClientID, rc));
1057
1058 if (rc != VERR_CANCELLED)
1059 {
1060 RTListNodeRemove(&pFirstMsg->ListEntry);
1061 shClSvcMsgFree(pClient, pFirstMsg);
1062 }
1063
1064 return VINF_HGCM_ASYNC_EXECUTE; /* The caller must not complete it. */
1065 }
1066
1067 LogFlowFunc(("[Client %RU32] Returning %Rrc\n", pClient->State.uClientID, rc));
1068 return rc;
1069 }
1070
1071 paParms[0].u.uint32 = 0;
1072 paParms[1].u.uint32 = 0;
1073 LogFlowFunc(("[Client %RU32] -> VERR_TRY_AGAIN\n", pClient->State.uClientID));
1074 return VERR_TRY_AGAIN;
1075}
1076
1077/**
1078 * Implements VBOX_SHCL_GUEST_FN_MSG_GET.
1079 *
1080 * @returns VBox status code.
1081 * @retval VINF_SUCCESS if message retrieved and removed from the pending queue.
1082 * @retval VERR_TRY_AGAIN if no message pending.
1083 * @retval VERR_MISMATCH if the incoming message ID does not match the pending.
1084 * @retval VINF_HGCM_ASYNC_EXECUTE if message was completed already.
1085 *
1086 * @param pClient The client state.
1087 * @param cParms Number of parameters.
1088 *
1089 * @note Called from within pClient->CritSect.
1090 */
1091static int shClSvcClientMsgCancel(PSHCLCLIENT pClient, uint32_t cParms)
1092{
1093 /*
1094 * Validate the request.
1095 */
1096 ASSERT_GUEST_MSG_RETURN(cParms == 0, ("cParms=%u!\n", cParms), VERR_WRONG_PARAMETER_COUNT);
1097
1098 /*
1099 * Execute.
1100 */
1101 if (pClient->Pending.uType != 0)
1102 {
1103 LogFlowFunc(("[Client %RU32] Cancelling waiting thread, isPending=%d, pendingNumParms=%RU32, m_idSession=%x\n",
1104 pClient->State.uClientID, pClient->Pending.uType, pClient->Pending.cParms, pClient->State.uSessionID));
1105
1106 /*
1107 * The PEEK call is simple: At least two parameters, all set to zero before sleeping.
1108 */
1109 int rcComplete;
1110 if (pClient->Pending.uType == VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT)
1111 {
1112 Assert(pClient->Pending.cParms >= 2);
1113 if (pClient->Pending.paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT)
1114 HGCMSvcSetU64(&pClient->Pending.paParms[0], VBOX_SHCL_HOST_MSG_CANCELED);
1115 else
1116 HGCMSvcSetU32(&pClient->Pending.paParms[0], VBOX_SHCL_HOST_MSG_CANCELED);
1117 rcComplete = VINF_TRY_AGAIN;
1118 }
1119 /*
1120 * The MSG_OLD call is complicated, though we're
1121 * generally here to wake up someone who is peeking and have two parameters.
1122 * If there aren't two parameters, fail the call.
1123 */
1124 else
1125 {
1126 Assert(pClient->Pending.uType == VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT);
1127 if (pClient->Pending.cParms > 0)
1128 HGCMSvcSetU32(&pClient->Pending.paParms[0], VBOX_SHCL_HOST_MSG_CANCELED);
1129 if (pClient->Pending.cParms > 1)
1130 HGCMSvcSetU32(&pClient->Pending.paParms[1], 0);
1131 rcComplete = pClient->Pending.cParms == 2 ? VINF_SUCCESS : VERR_TRY_AGAIN;
1132 }
1133
1134 g_pHelpers->pfnCallComplete(pClient->Pending.hHandle, rcComplete);
1135
1136 pClient->Pending.hHandle = NULL;
1137 pClient->Pending.paParms = NULL;
1138 pClient->Pending.cParms = 0;
1139 pClient->Pending.uType = 0;
1140 return VINF_SUCCESS;
1141 }
1142 return VWRN_NOT_FOUND;
1143}
1144
1145
1146/**
1147 * Wakes up a pending client (i.e. waiting for new messages).
1148 *
1149 * @returns VBox status code.
1150 * @retval VINF_NO_CHANGE if the client is not in pending mode.
1151 *
1152 * @param pClient Client to wake up.
1153 * @note Caller must enter pClient->CritSect.
1154 */
1155int shClSvcClientWakeup(PSHCLCLIENT pClient)
1156{
1157 Assert(RTCritSectIsOwner(&pClient->CritSect));
1158 int rc = VINF_NO_CHANGE;
1159
1160 if (pClient->Pending.uType != 0)
1161 {
1162 LogFunc(("[Client %RU32] Waking up ...\n", pClient->State.uClientID));
1163
1164 PSHCLCLIENTMSG pFirstMsg = RTListGetFirst(&pClient->MsgQueue, SHCLCLIENTMSG, ListEntry);
1165 AssertReturn(pFirstMsg, VERR_INTERNAL_ERROR);
1166
1167 LogFunc(("[Client %RU32] Current host message is %s (%RU32), cParms=%RU32\n",
1168 pClient->State.uClientID, ShClHostMsgToStr(pFirstMsg->idMsg), pFirstMsg->idMsg, pFirstMsg->cParms));
1169
1170 if (pClient->Pending.uType == VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT)
1171 shClSvcMsgSetPeekReturn(pFirstMsg, pClient->Pending.paParms, pClient->Pending.cParms);
1172 else if (pClient->Pending.uType == VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT) /* Legacy, Guest Additions < 6.1. */
1173 shClSvcMsgSetOldWaitReturn(pFirstMsg, pClient->Pending.paParms, pClient->Pending.cParms);
1174 else
1175 AssertMsgFailedReturn(("pClient->Pending.uType=%u\n", pClient->Pending.uType), VERR_INTERNAL_ERROR_3);
1176
1177 rc = g_pHelpers->pfnCallComplete(pClient->Pending.hHandle, VINF_SUCCESS);
1178
1179 if ( rc != VERR_CANCELLED
1180 && pClient->Pending.uType == VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT)
1181 {
1182 RTListNodeRemove(&pFirstMsg->ListEntry);
1183 shClSvcMsgFree(pClient, pFirstMsg);
1184 }
1185
1186 pClient->Pending.hHandle = NULL;
1187 pClient->Pending.paParms = NULL;
1188 pClient->Pending.cParms = 0;
1189 pClient->Pending.uType = 0;
1190 }
1191 else
1192 LogFunc(("[Client %RU32] Not in pending state, skipping wakeup\n", pClient->State.uClientID));
1193
1194 return rc;
1195}
1196
1197/**
1198 * Requests to read clipboard data from the guest.
1199 *
1200 * @returns VBox status code.
1201 * @param pClient Client to request to read data form.
1202 * @param fFormats The formats being requested, OR'ed together (VBOX_SHCL_FMT_XXX).
1203 * @param pidEvent Event ID for waiting for new data. Optional.
1204 * Must be released by the caller with ShClEventRelease() before unregistering then.
1205 */
1206int ShClSvcGuestDataRequest(PSHCLCLIENT pClient, SHCLFORMATS fFormats, PSHCLEVENTID pidEvent)
1207{
1208 LogFlowFuncEnter();
1209 if (pidEvent)
1210 *pidEvent = NIL_SHCLEVENTID;
1211 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
1212
1213 LogFlowFunc(("fFormats=%#x\n", fFormats));
1214
1215 int rc = VERR_NOT_SUPPORTED;
1216
1217 SHCLEVENTID idEvent = NIL_SHCLEVENTID;
1218
1219 /* Generate a separate message for every (valid) format we support. */
1220 while (fFormats)
1221 {
1222 /* Pick the next format to get from the mask: */
1223 /** @todo Make format reporting precedence configurable? */
1224 SHCLFORMAT fFormat;
1225 if (fFormats & VBOX_SHCL_FMT_UNICODETEXT)
1226 fFormat = VBOX_SHCL_FMT_UNICODETEXT;
1227 else if (fFormats & VBOX_SHCL_FMT_BITMAP)
1228 fFormat = VBOX_SHCL_FMT_BITMAP;
1229 else if (fFormats & VBOX_SHCL_FMT_HTML)
1230 fFormat = VBOX_SHCL_FMT_HTML;
1231 else
1232 AssertMsgFailedBreak(("%#x\n", fFormats));
1233
1234 /* Remove it from the mask. */
1235 fFormats &= ~fFormat;
1236
1237#ifdef LOG_ENABLED
1238 char *pszFmt = ShClFormatsToStrA(fFormat);
1239 AssertPtrReturn(pszFmt, VERR_NO_MEMORY);
1240 LogRel2(("Shared Clipboard: Requesting guest clipboard data in format '%s'\n", pszFmt));
1241 RTStrFree(pszFmt);
1242#endif
1243 /*
1244 * Allocate messages, one for each format.
1245 */
1246 PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient,
1247 pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID
1248 ? VBOX_SHCL_HOST_MSG_READ_DATA_CID : VBOX_SHCL_HOST_MSG_READ_DATA,
1249 2);
1250 if (pMsg)
1251 {
1252 /*
1253 * Enter the critical section and generate an event.
1254 */
1255 RTCritSectEnter(&pClient->CritSect);
1256
1257 idEvent = ShClEventIdGenerateAndRegister(&pClient->EventSrc);
1258 if (idEvent != NIL_SHCLEVENTID)
1259 {
1260 LogFlowFunc(("fFormats=%#x -> fFormat=%#x, idEvent=%#x\n", fFormats, fFormat, idEvent));
1261
1262 const uint64_t uCID = VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, pClient->EventSrc.uID, idEvent);
1263
1264 rc = VINF_SUCCESS;
1265
1266 /* Save the context ID in our legacy cruft if we have to deal with old(er) Guest Additions (< 6.1). */
1267 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID))
1268 {
1269 AssertStmt(pClient->Legacy.cCID < 4096, rc = VERR_TOO_MUCH_DATA);
1270 if (RT_SUCCESS(rc))
1271 {
1272 PSHCLCLIENTLEGACYCID pCID = (PSHCLCLIENTLEGACYCID)RTMemAlloc(sizeof(SHCLCLIENTLEGACYCID));
1273 if (pCID)
1274 {
1275 pCID->uCID = uCID;
1276 pCID->enmType = 0; /* Not used yet. */
1277 pCID->uFormat = fFormat;
1278 RTListAppend(&pClient->Legacy.lstCID, &pCID->Node);
1279 pClient->Legacy.cCID++;
1280 }
1281 else
1282 rc = VERR_NO_MEMORY;
1283 }
1284 }
1285
1286 if (RT_SUCCESS(rc))
1287 {
1288 /*
1289 * Format the message.
1290 */
1291 if (pMsg->idMsg == VBOX_SHCL_HOST_MSG_READ_DATA_CID)
1292 HGCMSvcSetU64(&pMsg->aParms[0], uCID);
1293 else
1294 HGCMSvcSetU32(&pMsg->aParms[0], VBOX_SHCL_HOST_MSG_READ_DATA);
1295 HGCMSvcSetU32(&pMsg->aParms[1], fFormat);
1296
1297 shClSvcMsgAdd(pClient, pMsg, true /* fAppend */);
1298 }
1299 }
1300 else
1301 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
1302
1303 RTCritSectLeave(&pClient->CritSect);
1304
1305 if (RT_FAILURE(rc))
1306 shClSvcMsgFree(pClient, pMsg);
1307 }
1308 else
1309 rc = VERR_NO_MEMORY;
1310
1311 if (RT_FAILURE(rc))
1312 break;
1313 }
1314
1315 if (RT_SUCCESS(rc))
1316 {
1317 RTCritSectEnter(&pClient->CritSect);
1318
1319 /* Retain the last event generated (in case there were multiple clipboard formats)
1320 * if we need to return the event ID to the caller. */
1321 if (pidEvent)
1322 {
1323 ShClEventRetain(&pClient->EventSrc, idEvent);
1324 *pidEvent = idEvent;
1325 }
1326
1327 shClSvcClientWakeup(pClient);
1328
1329 RTCritSectLeave(&pClient->CritSect);
1330 }
1331
1332 if (RT_FAILURE(rc))
1333 LogRel(("Shared Clipboard: Requesting data in formats %#x from guest failed with %Rrc\n", fFormats, rc));
1334
1335 LogFlowFuncLeaveRC(rc);
1336 return rc;
1337}
1338
1339/**
1340 * Signals the host that clipboard data from the guest has been received.
1341 *
1342 * @returns VBox status code. Returns VERR_NOT_FOUND when related event ID was not found.
1343 * @param pClient Client the guest clipboard data was received from.
1344 * @param pCmdCtx Client command context.
1345 * @param uFormat Clipboard format of data received.
1346 * @param pvData Pointer to clipboard data received. This can be
1347 * NULL if @a cbData is zero.
1348 * @param cbData Size (in bytes) of clipboard data received.
1349 * This can be zero.
1350 */
1351int ShClSvcGuestDataSignal(PSHCLCLIENT pClient, PSHCLCLIENTCMDCTX pCmdCtx, SHCLFORMAT uFormat, void *pvData, uint32_t cbData)
1352{
1353 LogFlowFuncEnter();
1354 RT_NOREF(uFormat);
1355
1356 /*
1357 * Validate intput.
1358 */
1359 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
1360 AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER);
1361 if (cbData > 0)
1362 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1363
1364 const SHCLEVENTID idEvent = VBOX_SHCL_CONTEXTID_GET_EVENT(pCmdCtx->uContextID);
1365 AssertMsgReturn(idEvent != NIL_SHCLEVENTID, ("NIL event in context ID %#RX64\n", pCmdCtx->uContextID), VERR_WRONG_ORDER);
1366 AssertMsg(ShClEventGet(&pClient->EventSrc, idEvent) != NULL, ("Event %#x not found\n", idEvent));
1367
1368 /*
1369 * Make a copy of the data so we can attach it to the signal.
1370 *
1371 * Note! We still signal the waiter should we run out of memory,
1372 * because otherwise it will be stuck waiting.
1373 */
1374 int rc = VINF_SUCCESS;
1375 PSHCLEVENTPAYLOAD pPayload = NULL;
1376 if (cbData > 0)
1377 rc = ShClPayloadAlloc(idEvent, pvData, cbData, &pPayload);
1378
1379 /*
1380 * Signal the event.
1381 */
1382 RTCritSectEnter(&pClient->CritSect);
1383 int rc2 = ShClEventSignal(&pClient->EventSrc, idEvent, pPayload);
1384 RTCritSectLeave(&pClient->CritSect);
1385 if (RT_FAILURE(rc2))
1386 {
1387 rc = rc2;
1388 ShClPayloadFree(pPayload);
1389 LogRel(("Shared Clipboard: Signalling of guest clipboard data to the host failed: %Rrc\n", rc));
1390 }
1391
1392 /*
1393 * No one holding a reference to the event anymore? Unregister it.
1394 */
1395 /** @todo r=bird: This isn't how reference counting is supposed to be
1396 * implemented, is it now? */
1397 if (ShClEventGetRefs(&pClient->EventSrc, idEvent) == 0)
1398 {
1399 rc2 = ShClEventUnregister(&pClient->EventSrc, idEvent);
1400 if (RT_SUCCESS(rc))
1401 rc = rc2;
1402 }
1403
1404 LogFlowFuncLeaveRC(rc);
1405 return rc;
1406}
1407
1408/**
1409 * Reports available VBox clipboard formats to the guest.
1410 *
1411 * @note Host backend callers must check if it's active (use
1412 * ShClSvcIsBackendActive) before calling to prevent mixing up the
1413 * VRDE clipboard.
1414 *
1415 * @returns VBox status code.
1416 * @param pClient Client to report clipboard formats to.
1417 * @param fFormats The formats to report (VBOX_SHCL_FMT_XXX), zero
1418 * is okay (empty the clipboard).
1419 */
1420int ShClSvcHostReportFormats(PSHCLCLIENT pClient, SHCLFORMATS fFormats)
1421{
1422 /*
1423 * Check if the service mode allows this operation and whether the guest is
1424 * supposed to be reading from the host. Otherwise, silently ignore reporting
1425 * formats and return VINF_SUCCESS in order to do not trigger client
1426 * termination in svcConnect().
1427 */
1428 uint32_t uMode = ShClSvcGetMode();
1429 if ( uMode == VBOX_SHCL_MODE_BIDIRECTIONAL
1430 || uMode == VBOX_SHCL_MODE_HOST_TO_GUEST)
1431 { /* likely */ }
1432 else
1433 return VINF_SUCCESS;
1434
1435 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
1436
1437 LogFlowFunc(("fFormats=%#x\n", fFormats));
1438
1439#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1440 /*
1441 * If transfer mode is set to disabled, don't report the URI list format to the guest.
1442 */
1443 if (!(g_fTransferMode & VBOX_SHCL_TRANSFER_MODE_ENABLED))
1444 {
1445 fFormats &= ~VBOX_SHCL_FMT_URI_LIST;
1446 LogRel2(("Shared Clipboard: File transfers are disabled, skipping reporting those to the guest\n"));
1447 }
1448#endif
1449
1450#ifdef LOG_ENABLED
1451 char *pszFmts = ShClFormatsToStrA(fFormats);
1452 AssertPtrReturn(pszFmts, VERR_NO_MEMORY);
1453 LogRel2(("Shared Clipboard: Reporting formats '%s' to guest\n", pszFmts));
1454 RTStrFree(pszFmts);
1455#endif
1456
1457 /*
1458 * Allocate a message, populate parameters and post it to the client.
1459 */
1460 int rc;
1461 PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_FORMATS_REPORT, 2);
1462 if (pMsg)
1463 {
1464 HGCMSvcSetU32(&pMsg->aParms[0], VBOX_SHCL_HOST_MSG_FORMATS_REPORT);
1465 HGCMSvcSetU32(&pMsg->aParms[1], fFormats);
1466
1467 RTCritSectEnter(&pClient->CritSect);
1468 shClSvcMsgAddAndWakeupClient(pClient, pMsg);
1469 RTCritSectLeave(&pClient->CritSect);
1470
1471#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1472 /* If we announce an URI list, create a transfer locally and also tell the guest to create
1473 * a transfer on the guest side. */
1474 if (fFormats & VBOX_SHCL_FMT_URI_LIST)
1475 {
1476 rc = shClSvcTransferStart(pClient, SHCLTRANSFERDIR_TO_REMOTE, SHCLSOURCE_LOCAL,
1477 NULL /* pTransfer */);
1478 if (RT_SUCCESS(rc))
1479 rc = shClSvcSetSource(pClient, SHCLSOURCE_LOCAL);
1480
1481 if (RT_FAILURE(rc))
1482 LogRel(("Shared Clipboard: Initializing host write transfer failed with %Rrc\n", rc));
1483 }
1484 else
1485#endif
1486 {
1487 rc = VINF_SUCCESS;
1488 }
1489 }
1490 else
1491 rc = VERR_NO_MEMORY;
1492
1493 if (RT_FAILURE(rc))
1494 LogRel(("Shared Clipboard: Reporting formats %#x to guest failed with %Rrc\n", fFormats, rc));
1495
1496 LogFlowFuncLeaveRC(rc);
1497 return rc;
1498}
1499
1500
1501/**
1502 * Handles the VBOX_SHCL_GUEST_FN_REPORT_FORMATS message from the guest.
1503 */
1504static int shClSvcClientReportFormats(PSHCLCLIENT pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1505{
1506 /*
1507 * Check if the service mode allows this operation and whether the guest is
1508 * supposed to be reading from the host.
1509 */
1510 uint32_t uMode = ShClSvcGetMode();
1511 if ( uMode == VBOX_SHCL_MODE_BIDIRECTIONAL
1512 || uMode == VBOX_SHCL_MODE_GUEST_TO_HOST)
1513 { /* likely */ }
1514 else
1515 return VERR_ACCESS_DENIED;
1516
1517 /*
1518 * Digest parameters.
1519 */
1520 ASSERT_GUEST_RETURN( cParms == VBOX_SHCL_CPARMS_REPORT_FORMATS
1521 || ( cParms == VBOX_SHCL_CPARMS_REPORT_FORMATS_61B
1522 && (pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID)),
1523 VERR_WRONG_PARAMETER_COUNT);
1524
1525 uintptr_t iParm = 0;
1526 if (cParms == VBOX_SHCL_CPARMS_REPORT_FORMATS_61B)
1527 {
1528 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
1529 /* no defined value, so just ignore it */
1530 iParm++;
1531 }
1532 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1533 uint32_t const fFormats = paParms[iParm].u.uint32;
1534 iParm++;
1535 if (cParms == VBOX_SHCL_CPARMS_REPORT_FORMATS_61B)
1536 {
1537 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1538 ASSERT_GUEST_RETURN(paParms[iParm].u.uint32 == 0, VERR_INVALID_FLAGS);
1539 iParm++;
1540 }
1541 Assert(iParm == cParms);
1542
1543 /*
1544 * Report the formats.
1545 *
1546 * We ignore empty reports if the guest isn't the clipboard owner, this
1547 * prevents a freshly booted guest with an empty clibpoard from clearing
1548 * the host clipboard on startup. Likewise, when a guest shutdown it will
1549 * typically issue an empty report in case it's the owner, we don't want
1550 * that to clear host content either.
1551 */
1552 int rc;
1553 if (!fFormats && pClient->State.enmSource != SHCLSOURCE_REMOTE)
1554 rc = VINF_SUCCESS;
1555 else
1556 {
1557 rc = shClSvcSetSource(pClient, SHCLSOURCE_REMOTE);
1558 if (RT_SUCCESS(rc))
1559 {
1560 rc = RTCritSectEnter(&g_CritSect);
1561 if (RT_SUCCESS(rc))
1562 {
1563 if (g_ExtState.pfnExtension)
1564 {
1565 SHCLEXTPARMS parms;
1566 RT_ZERO(parms);
1567 parms.uFormat = fFormats;
1568
1569 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE, &parms, sizeof(parms));
1570 }
1571 else
1572 {
1573#ifdef LOG_ENABLED
1574 char *pszFmts = ShClFormatsToStrA(fFormats);
1575 if (pszFmts)
1576 {
1577 LogRel2(("Shared Clipboard: Guest reported formats '%s' to host\n", pszFmts));
1578 RTStrFree(pszFmts);
1579 }
1580#endif
1581 rc = ShClBackendFormatAnnounce(pClient, fFormats);
1582 if (RT_FAILURE(rc))
1583 LogRel(("Shared Clipboard: Reporting guest clipboard formats to the host failed with %Rrc\n", rc));
1584 }
1585
1586 RTCritSectLeave(&g_CritSect);
1587 }
1588 else
1589 LogRel2(("Shared Clipboard: Unable to take internal lock while receiving guest clipboard announcement: %Rrc\n", rc));
1590 }
1591 }
1592
1593 return rc;
1594}
1595
1596/**
1597 * Called when the guest wants to read host clipboard data.
1598 * Handles the VBOX_SHCL_GUEST_FN_DATA_READ message.
1599 *
1600 * @returns VBox status code.
1601 * @retval VINF_BUFFER_OVERFLOW if the guest supplied a smaller buffer than needed in order to read the host clipboard data.
1602 * @param pClient Client that wants to read host clipboard data.
1603 * @param cParms Number of HGCM parameters supplied in \a paParms.
1604 * @param paParms Array of HGCM parameters.
1605 */
1606static int shClSvcClientReadData(PSHCLCLIENT pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1607{
1608 LogFlowFuncEnter();
1609
1610 /*
1611 * Check if the service mode allows this operation and whether the guest is
1612 * supposed to be reading from the host.
1613 */
1614 uint32_t uMode = ShClSvcGetMode();
1615 if ( uMode == VBOX_SHCL_MODE_BIDIRECTIONAL
1616 || uMode == VBOX_SHCL_MODE_HOST_TO_GUEST)
1617 { /* likely */ }
1618 else
1619 return VERR_ACCESS_DENIED;
1620
1621 /*
1622 * Digest parameters.
1623 *
1624 * We are dragging some legacy here from the 6.1 dev cycle, a 5 parameter
1625 * variant which prepends a 64-bit context ID (RAZ as meaning not defined),
1626 * a 32-bit flag (MBZ, no defined meaning) and switches the last two parameters.
1627 */
1628 ASSERT_GUEST_RETURN( cParms == VBOX_SHCL_CPARMS_DATA_READ
1629 || ( cParms == VBOX_SHCL_CPARMS_DATA_READ_61B
1630 && (pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID)),
1631 VERR_WRONG_PARAMETER_COUNT);
1632
1633 uintptr_t iParm = 0;
1634 SHCLCLIENTCMDCTX cmdCtx;
1635 RT_ZERO(cmdCtx);
1636 if (cParms == VBOX_SHCL_CPARMS_DATA_READ_61B)
1637 {
1638 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
1639 /* This has no defined meaning and was never used, however the guest passed stuff, so ignore it and leave idContext=0. */
1640 iParm++;
1641 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1642 ASSERT_GUEST_RETURN(paParms[iParm].u.uint32 == 0, VERR_INVALID_FLAGS);
1643 iParm++;
1644 }
1645
1646 SHCLFORMAT uFormat = VBOX_SHCL_FMT_NONE;
1647 uint32_t cbData = 0;
1648 void *pvData = NULL;
1649
1650 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1651 uFormat = paParms[iParm].u.uint32;
1652 iParm++;
1653 if (cParms != VBOX_SHCL_CPARMS_DATA_READ_61B)
1654 {
1655 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE); /* Data buffer */
1656 pvData = paParms[iParm].u.pointer.addr;
1657 cbData = paParms[iParm].u.pointer.size;
1658 iParm++;
1659 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /*cbDataReturned*/
1660 iParm++;
1661 }
1662 else
1663 {
1664 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /*cbDataReturned*/
1665 iParm++;
1666 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE); /* Data buffer */
1667 pvData = paParms[iParm].u.pointer.addr;
1668 cbData = paParms[iParm].u.pointer.size;
1669 iParm++;
1670 }
1671 Assert(iParm == cParms);
1672
1673 /*
1674 * For some reason we need to do this (makes absolutely no sense to bird).
1675 */
1676 /** @todo r=bird: I really don't get why you need the State.POD.uFormat
1677 * member. I'm sure there is a reason. Incomplete code? */
1678 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID))
1679 {
1680 if (pClient->State.POD.uFormat == VBOX_SHCL_FMT_NONE)
1681 pClient->State.POD.uFormat = uFormat;
1682 }
1683
1684#ifdef LOG_ENABLED
1685 char *pszFmt = ShClFormatsToStrA(uFormat);
1686 AssertPtrReturn(pszFmt, VERR_NO_MEMORY);
1687 LogRel2(("Shared Clipboard: Guest wants to read %RU32 bytes host clipboard data in format '%s'\n", cbData, pszFmt));
1688 RTStrFree(pszFmt);
1689#endif
1690
1691 /*
1692 * Do the reading.
1693 */
1694 uint32_t cbActual = 0;
1695
1696 int rc = RTCritSectEnter(&g_CritSect);
1697 AssertRCReturn(rc, rc);
1698
1699 /* If there is a service extension active, try reading data from it first. */
1700 if (g_ExtState.pfnExtension)
1701 {
1702 SHCLEXTPARMS parms;
1703 RT_ZERO(parms);
1704
1705 parms.uFormat = uFormat;
1706 parms.u.pvData = pvData;
1707 parms.cbData = cbData;
1708
1709 g_ExtState.fReadingData = true;
1710
1711 /* Read clipboard data from the extension. */
1712 rc = g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_READ, &parms, sizeof(parms));
1713
1714 LogRel2(("Shared Clipboard: Read extension clipboard data (fDelayedAnnouncement=%RTbool, fDelayedFormats=%#x, max %RU32 bytes), got %RU32 bytes: rc=%Rrc\n",
1715 g_ExtState.fDelayedAnnouncement, g_ExtState.fDelayedFormats, cbData, parms.cbData, rc));
1716
1717 /* Did the extension send the clipboard formats yet?
1718 * Otherwise, do this now. */
1719 if (g_ExtState.fDelayedAnnouncement)
1720 {
1721 int rc2 = ShClSvcHostReportFormats(pClient, g_ExtState.fDelayedFormats);
1722 AssertRC(rc2);
1723
1724 g_ExtState.fDelayedAnnouncement = false;
1725 g_ExtState.fDelayedFormats = 0;
1726 }
1727
1728 g_ExtState.fReadingData = false;
1729
1730 if (RT_SUCCESS(rc))
1731 cbActual = parms.cbData;
1732 }
1733 else
1734 {
1735 rc = ShClBackendReadData(pClient, &cmdCtx, uFormat, pvData, cbData, &cbActual);
1736 if (RT_SUCCESS(rc))
1737 LogRel2(("Shared Clipboard: Read host clipboard data (max %RU32 bytes), got %RU32 bytes\n", cbData, cbActual));
1738 else
1739 LogRel(("Shared Clipboard: Reading host clipboard data failed with %Rrc\n", rc));
1740 }
1741
1742 if (RT_SUCCESS(rc))
1743 {
1744 /* Return the actual size required to fullfil the request. */
1745 if (cParms != VBOX_SHCL_CPARMS_DATA_READ_61B)
1746 HGCMSvcSetU32(&paParms[2], cbActual);
1747 else
1748 HGCMSvcSetU32(&paParms[3], cbActual);
1749
1750 /* If the data to return exceeds the buffer the guest supplies, tell it (and let it try again). */
1751 if (cbActual >= cbData)
1752 rc = VINF_BUFFER_OVERFLOW;
1753 }
1754
1755 RTCritSectLeave(&g_CritSect);
1756
1757 LogFlowFuncLeaveRC(rc);
1758 return rc;
1759}
1760
1761/**
1762 * Called when the guest writes clipboard data to the host.
1763 * Handles the VBOX_SHCL_GUEST_FN_DATA_WRITE message.
1764 *
1765 * @returns VBox status code.
1766 * @param pClient Client that wants to read host clipboard data.
1767 * @param cParms Number of HGCM parameters supplied in \a paParms.
1768 * @param paParms Array of HGCM parameters.
1769 */
1770int shClSvcClientWriteData(PSHCLCLIENT pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
1771{
1772 LogFlowFuncEnter();
1773
1774 /*
1775 * Check if the service mode allows this operation and whether the guest is
1776 * supposed to be reading from the host.
1777 */
1778 uint32_t uMode = ShClSvcGetMode();
1779 if ( uMode == VBOX_SHCL_MODE_BIDIRECTIONAL
1780 || uMode == VBOX_SHCL_MODE_GUEST_TO_HOST)
1781 { /* likely */ }
1782 else
1783 return VERR_ACCESS_DENIED;
1784
1785 const bool fReportsContextID = RT_BOOL(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID);
1786
1787 /*
1788 * Digest parameters.
1789 *
1790 * There are 3 different format here, formatunately no parameters have been
1791 * switch around so it's plain sailing compared to the DATA_READ message.
1792 */
1793 ASSERT_GUEST_RETURN(fReportsContextID
1794 ? cParms == VBOX_SHCL_CPARMS_DATA_WRITE || cParms == VBOX_SHCL_CPARMS_DATA_WRITE_61B
1795 : cParms == VBOX_SHCL_CPARMS_DATA_WRITE_OLD,
1796 VERR_WRONG_PARAMETER_COUNT);
1797
1798 uintptr_t iParm = 0;
1799 SHCLCLIENTCMDCTX cmdCtx;
1800 RT_ZERO(cmdCtx);
1801 if (cParms > VBOX_SHCL_CPARMS_DATA_WRITE_OLD)
1802 {
1803 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
1804 cmdCtx.uContextID = paParms[iParm].u.uint64;
1805 iParm++;
1806 }
1807 else
1808 {
1809 /* Older Guest Additions (< 6.1) did not supply a context ID.
1810 * We dig it out from our saved context ID list then a bit down below. */
1811 }
1812
1813 if (cParms == VBOX_SHCL_CPARMS_DATA_WRITE_61B)
1814 {
1815 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE);
1816 ASSERT_GUEST_RETURN(paParms[iParm].u.uint32 == 0, VERR_INVALID_FLAGS);
1817 iParm++;
1818 }
1819
1820 SHCLFORMAT uFormat = VBOX_SHCL_FMT_NONE;
1821 uint32_t cbData = 0;
1822 void *pvData = NULL;
1823
1824 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* Format bit. */
1825 uFormat = paParms[iParm].u.uint32;
1826 iParm++;
1827 if (cParms == VBOX_SHCL_CPARMS_DATA_WRITE_61B)
1828 {
1829 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* "cbData" - duplicates buffer size. */
1830 iParm++;
1831 }
1832 ASSERT_GUEST_RETURN(paParms[iParm].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE); /* Data buffer */
1833 pvData = paParms[iParm].u.pointer.addr;
1834 cbData = paParms[iParm].u.pointer.size;
1835 iParm++;
1836 Assert(iParm == cParms);
1837
1838 /*
1839 * Handle / check context ID.
1840 */
1841 if (!fReportsContextID) /* Do we have to deal with old(er) GAs (< 6.1) which don't support context IDs? Dig out the context ID then. */
1842 {
1843 PSHCLCLIENTLEGACYCID pCID = NULL;
1844 PSHCLCLIENTLEGACYCID pCIDIter;
1845 RTListForEach(&pClient->Legacy.lstCID, pCIDIter, SHCLCLIENTLEGACYCID, Node) /* Slow, but does the job for now. */
1846 {
1847 if (pCIDIter->uFormat == uFormat)
1848 {
1849 pCID = pCIDIter;
1850 break;
1851 }
1852 }
1853
1854 ASSERT_GUEST_MSG_RETURN(pCID != NULL, ("Context ID for format %#x not found\n", uFormat), VERR_INVALID_CONTEXT);
1855 cmdCtx.uContextID = pCID->uCID;
1856
1857 /* Not needed anymore; clean up. */
1858 Assert(pClient->Legacy.cCID);
1859 pClient->Legacy.cCID--;
1860 RTListNodeRemove(&pCID->Node);
1861 RTMemFree(pCID);
1862 }
1863
1864 uint64_t const idCtxExpected = VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, pClient->EventSrc.uID,
1865 VBOX_SHCL_CONTEXTID_GET_EVENT(cmdCtx.uContextID));
1866 ASSERT_GUEST_MSG_RETURN(cmdCtx.uContextID == idCtxExpected,
1867 ("Wrong context ID: %#RX64, expected %#RX64\n", cmdCtx.uContextID, idCtxExpected),
1868 VERR_INVALID_CONTEXT);
1869
1870 /*
1871 * For some reason we need to do this (makes absolutely no sense to bird).
1872 */
1873 /** @todo r=bird: I really don't get why you need the State.POD.uFormat
1874 * member. I'm sure there is a reason. Incomplete code? */
1875 if (!(pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID))
1876 {
1877 if (pClient->State.POD.uFormat == VBOX_SHCL_FMT_NONE)
1878 pClient->State.POD.uFormat = uFormat;
1879 }
1880
1881#ifdef LOG_ENABLED
1882 char *pszFmt = ShClFormatsToStrA(uFormat);
1883 if (pszFmt)
1884 {
1885 LogRel2(("Shared Clipboard: Guest writes %RU32 bytes clipboard data in format '%s' to host\n", cbData, pszFmt));
1886 RTStrFree(pszFmt);
1887 }
1888#endif
1889
1890 /*
1891 * Write the data to the active host side clipboard.
1892 */
1893 int rc = RTCritSectEnter(&g_CritSect);
1894 AssertRCReturn(rc, rc);
1895
1896 if (g_ExtState.pfnExtension)
1897 {
1898 SHCLEXTPARMS parms;
1899 RT_ZERO(parms);
1900 parms.uFormat = uFormat;
1901 parms.u.pvData = pvData;
1902 parms.cbData = cbData;
1903
1904 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_DATA_WRITE, &parms, sizeof(parms));
1905 rc = VINF_SUCCESS;
1906 }
1907 else
1908 {
1909 /* Let the backend implementation know. */
1910 rc = ShClBackendWriteData(pClient, &cmdCtx, uFormat, pvData, cbData);
1911 if (RT_FAILURE(rc))
1912 LogRel(("Shared Clipboard: Writing guest clipboard data to the host failed with %Rrc\n", rc));
1913
1914 int rc2; /* Don't return internals back to the guest. */
1915 rc2 = ShClSvcGuestDataSignal(pClient, &cmdCtx, uFormat, pvData, cbData); /* To complete pending events, if any. */
1916 if (RT_FAILURE(rc2))
1917 LogRel(("Shared Clipboard: Signalling host about guest clipboard data failed with %Rrc\n", rc2));
1918 AssertRC(rc2);
1919 }
1920
1921 RTCritSectLeave(&g_CritSect);
1922
1923 LogFlowFuncLeaveRC(rc);
1924 return rc;
1925}
1926
1927/**
1928 * Gets an error from HGCM service parameters.
1929 *
1930 * @returns VBox status code.
1931 * @param cParms Number of HGCM parameters supplied in \a paParms.
1932 * @param paParms Array of HGCM parameters.
1933 * @param pRc Where to store the received error code.
1934 */
1935static int shClSvcClientError(uint32_t cParms, VBOXHGCMSVCPARM paParms[], int *pRc)
1936{
1937 AssertPtrReturn(paParms, VERR_INVALID_PARAMETER);
1938 AssertPtrReturn(pRc, VERR_INVALID_PARAMETER);
1939
1940 int rc;
1941
1942 if (cParms == VBOX_SHCL_CPARMS_ERROR)
1943 {
1944 rc = HGCMSvcGetU32(&paParms[1], (uint32_t *)pRc); /** @todo int vs. uint32_t !!! */
1945 }
1946 else
1947 rc = VERR_INVALID_PARAMETER;
1948
1949 LogFlowFuncLeaveRC(rc);
1950 return rc;
1951}
1952
1953/**
1954 * Sets the transfer source type of a Shared Clipboard client.
1955 *
1956 * @returns VBox status code.
1957 * @param pClient Client to set transfer source type for.
1958 * @param enmSource Source type to set.
1959 */
1960int shClSvcSetSource(PSHCLCLIENT pClient, SHCLSOURCE enmSource)
1961{
1962 if (!pClient) /* If no client connected (anymore), bail out. */
1963 return VINF_SUCCESS;
1964
1965 int rc = VINF_SUCCESS;
1966
1967 if (ShClSvcLock())
1968 {
1969 pClient->State.enmSource = enmSource;
1970
1971 LogFlowFunc(("Source of client %RU32 is now %RU32\n", pClient->State.uClientID, pClient->State.enmSource));
1972
1973 ShClSvcUnlock();
1974 }
1975
1976 LogFlowFuncLeaveRC(rc);
1977 return rc;
1978}
1979
1980static int svcInit(VBOXHGCMSVCFNTABLE *pTable)
1981{
1982 int rc = RTCritSectInit(&g_CritSect);
1983
1984 if (RT_SUCCESS(rc))
1985 {
1986 shClSvcModeSet(VBOX_SHCL_MODE_OFF);
1987
1988 rc = ShClBackendInit(pTable);
1989
1990 /* Clean up on failure, because 'svnUnload' will not be called
1991 * if the 'svcInit' returns an error.
1992 */
1993 if (RT_FAILURE(rc))
1994 {
1995 RTCritSectDelete(&g_CritSect);
1996 }
1997 }
1998
1999 return rc;
2000}
2001
2002static DECLCALLBACK(int) svcUnload(void *)
2003{
2004 LogFlowFuncEnter();
2005
2006 ShClBackendDestroy();
2007
2008 RTCritSectDelete(&g_CritSect);
2009
2010 return VINF_SUCCESS;
2011}
2012
2013static DECLCALLBACK(int) svcDisconnect(void *, uint32_t u32ClientID, void *pvClient)
2014{
2015 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
2016
2017 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
2018 AssertPtr(pClient);
2019
2020 /* In order to communicate with guest service, HGCM VRDP clipboard extension
2021 * needs to know its connection client ID. Currently, in svcConnect() we always
2022 * cache ID of the first ever connected client. When client disconnects,
2023 * we need to forget its ID and let svcConnect() to pick up the next ID when a new
2024 * connection will be requested by guest service (see #10115). */
2025 if (g_ExtState.uClientID == u32ClientID)
2026 {
2027 g_ExtState.uClientID = 0;
2028 }
2029
2030#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2031 shClSvcClientTransfersReset(pClient);
2032#endif
2033
2034 ShClBackendDisconnect(pClient);
2035
2036 shClSvcClientDestroy(pClient);
2037
2038 return VINF_SUCCESS;
2039}
2040
2041static DECLCALLBACK(int) svcConnect(void *, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring)
2042{
2043 RT_NOREF(fRequestor, fRestoring);
2044
2045 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
2046 AssertPtr(pvClient);
2047
2048 int rc = shClSvcClientInit(pClient, u32ClientID);
2049 if (RT_SUCCESS(rc))
2050 {
2051 /* Assign weak pointer to client map .*/
2052 g_mapClients[u32ClientID] = pClient; /** @todo Handle OOM / collisions? */
2053
2054 rc = ShClBackendConnect(pClient, ShClSvcGetHeadless());
2055 if (RT_SUCCESS(rc))
2056 {
2057 /* Sync the host clipboard content with the client. */
2058 rc = ShClBackendSync(pClient);
2059 if (rc == VINF_NO_CHANGE)
2060 {
2061 /*
2062 * The sync could return VINF_NO_CHANGE if nothing has changed on the host, but older
2063 * Guest Additions rely on the fact that only VINF_SUCCESS indicates a successful connect
2064 * to the host service (instead of using RT_SUCCESS()).
2065 *
2066 * So implicitly set VINF_SUCCESS here to not break older Guest Additions.
2067 */
2068 rc = VINF_SUCCESS;
2069 }
2070
2071 if (RT_SUCCESS(rc))
2072 {
2073 /* For now we ASSUME that the first client ever connected is in charge for
2074 * communicating withe the service extension.
2075 *
2076 ** @todo This needs to be fixed ASAP w/o breaking older guest / host combos. */
2077 if (g_ExtState.uClientID == 0)
2078 g_ExtState.uClientID = u32ClientID;
2079 }
2080 }
2081
2082 if (RT_FAILURE(rc))
2083 {
2084 shClSvcClientDestroy(pClient);
2085 }
2086
2087 }
2088
2089 LogFlowFuncLeaveRC(rc);
2090 return rc;
2091}
2092
2093static DECLCALLBACK(void) svcCall(void *,
2094 VBOXHGCMCALLHANDLE callHandle,
2095 uint32_t u32ClientID,
2096 void *pvClient,
2097 uint32_t u32Function,
2098 uint32_t cParms,
2099 VBOXHGCMSVCPARM paParms[],
2100 uint64_t tsArrival)
2101{
2102 RT_NOREF(u32ClientID, pvClient, tsArrival);
2103 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
2104 AssertPtr(pClient);
2105
2106#ifdef LOG_ENABLED
2107 Log2Func(("u32ClientID=%RU32, fn=%RU32 (%s), cParms=%RU32, paParms=%p\n",
2108 u32ClientID, u32Function, ShClGuestMsgToStr(u32Function), cParms, paParms));
2109 for (uint32_t i = 0; i < cParms; i++)
2110 {
2111 switch (paParms[i].type)
2112 {
2113 case VBOX_HGCM_SVC_PARM_32BIT:
2114 Log3Func((" paParms[%RU32]: type uint32_t - value %RU32\n", i, paParms[i].u.uint32));
2115 break;
2116 case VBOX_HGCM_SVC_PARM_64BIT:
2117 Log3Func((" paParms[%RU32]: type uint64_t - value %RU64\n", i, paParms[i].u.uint64));
2118 break;
2119 case VBOX_HGCM_SVC_PARM_PTR:
2120 Log3Func((" paParms[%RU32]: type ptr - value 0x%p (%RU32 bytes)\n",
2121 i, paParms[i].u.pointer.addr, paParms[i].u.pointer.size));
2122 break;
2123 case VBOX_HGCM_SVC_PARM_PAGES:
2124 Log3Func((" paParms[%RU32]: type pages - cb=%RU32, cPages=%RU16\n",
2125 i, paParms[i].u.Pages.cb, paParms[i].u.Pages.cPages));
2126 break;
2127 default:
2128 AssertFailed();
2129 }
2130 }
2131 Log2Func(("Client state: fFlags=0x%x, fGuestFeatures0=0x%x, fGuestFeatures1=0x%x\n",
2132 pClient->State.fFlags, pClient->State.fGuestFeatures0, pClient->State.fGuestFeatures1));
2133#endif
2134
2135 int rc;
2136 switch (u32Function)
2137 {
2138 case VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT:
2139 RTCritSectEnter(&pClient->CritSect);
2140 rc = shClSvcClientMsgOldGet(pClient, callHandle, cParms, paParms);
2141 RTCritSectLeave(&pClient->CritSect);
2142 break;
2143
2144 case VBOX_SHCL_GUEST_FN_CONNECT:
2145 LogRel(("Shared Clipboard: 6.1.0 beta or rc Guest Additions detected. Please upgrade!\n"));
2146 rc = VERR_NOT_IMPLEMENTED;
2147 break;
2148
2149 case VBOX_SHCL_GUEST_FN_NEGOTIATE_CHUNK_SIZE:
2150 rc = shClSvcClientNegogiateChunkSize(pClient, callHandle, cParms, paParms);
2151 break;
2152
2153 case VBOX_SHCL_GUEST_FN_REPORT_FEATURES:
2154 rc = shClSvcClientReportFeatures(pClient, callHandle, cParms, paParms);
2155 break;
2156
2157 case VBOX_SHCL_GUEST_FN_QUERY_FEATURES:
2158 rc = shClSvcClientQueryFeatures(callHandle, cParms, paParms);
2159 break;
2160
2161 case VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT:
2162 RTCritSectEnter(&pClient->CritSect);
2163 rc = shClSvcClientMsgPeek(pClient, callHandle, cParms, paParms, false /*fWait*/);
2164 RTCritSectLeave(&pClient->CritSect);
2165 break;
2166
2167 case VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT:
2168 RTCritSectEnter(&pClient->CritSect);
2169 rc = shClSvcClientMsgPeek(pClient, callHandle, cParms, paParms, true /*fWait*/);
2170 RTCritSectLeave(&pClient->CritSect);
2171 break;
2172
2173 case VBOX_SHCL_GUEST_FN_MSG_GET:
2174 RTCritSectEnter(&pClient->CritSect);
2175 rc = shClSvcClientMsgGet(pClient, callHandle, cParms, paParms);
2176 RTCritSectLeave(&pClient->CritSect);
2177 break;
2178
2179 case VBOX_SHCL_GUEST_FN_MSG_CANCEL:
2180 RTCritSectEnter(&pClient->CritSect);
2181 rc = shClSvcClientMsgCancel(pClient, cParms);
2182 RTCritSectLeave(&pClient->CritSect);
2183 break;
2184
2185 case VBOX_SHCL_GUEST_FN_REPORT_FORMATS:
2186 rc = shClSvcClientReportFormats(pClient, cParms, paParms);
2187 break;
2188
2189 case VBOX_SHCL_GUEST_FN_DATA_READ:
2190 rc = shClSvcClientReadData(pClient, cParms, paParms);
2191 break;
2192
2193 case VBOX_SHCL_GUEST_FN_DATA_WRITE:
2194 rc = shClSvcClientWriteData(pClient, cParms, paParms);
2195 break;
2196
2197 case VBOX_SHCL_GUEST_FN_ERROR:
2198 {
2199 int rcGuest;
2200 rc = shClSvcClientError(cParms,paParms, &rcGuest);
2201 if (RT_SUCCESS(rc))
2202 {
2203 LogRel(("Shared Clipboard: Error reported from guest side: %Rrc\n", rcGuest));
2204
2205 shClSvcClientLock(pClient);
2206
2207#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2208 shClSvcClientTransfersReset(pClient);
2209#endif
2210 shClSvcClientUnlock(pClient);
2211 }
2212 break;
2213 }
2214
2215 default:
2216 {
2217#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2218 if ( u32Function <= VBOX_SHCL_GUEST_FN_LAST
2219 && (pClient->State.fGuestFeatures0 & VBOX_SHCL_GF_0_CONTEXT_ID) )
2220 {
2221 if (g_fTransferMode & VBOX_SHCL_TRANSFER_MODE_ENABLED)
2222 rc = shClSvcTransferHandler(pClient, callHandle, u32Function, cParms, paParms, tsArrival);
2223 else
2224 {
2225 LogRel2(("Shared Clipboard: File transfers are disabled for this VM\n"));
2226 rc = VERR_ACCESS_DENIED;
2227 }
2228 }
2229 else
2230#endif
2231 {
2232 LogRel2(("Shared Clipboard: Unknown guest function: %u (%#x)\n", u32Function, u32Function));
2233 rc = VERR_NOT_IMPLEMENTED;
2234 }
2235 break;
2236 }
2237 }
2238
2239 LogFlowFunc(("[Client %RU32] rc=%Rrc\n", pClient->State.uClientID, rc));
2240
2241 if (rc != VINF_HGCM_ASYNC_EXECUTE)
2242 g_pHelpers->pfnCallComplete(callHandle, rc);
2243}
2244
2245/**
2246 * Initializes a Shared Clipboard service's client state.
2247 *
2248 * @returns VBox status code.
2249 * @param pClientState Client state to initialize.
2250 * @param uClientID Client ID (HGCM) to use for this client state.
2251 */
2252int shClSvcClientStateInit(PSHCLCLIENTSTATE pClientState, uint32_t uClientID)
2253{
2254 LogFlowFuncEnter();
2255
2256 shclSvcClientStateReset(pClientState);
2257
2258 /* Register the client. */
2259 pClientState->uClientID = uClientID;
2260
2261 return VINF_SUCCESS;
2262}
2263
2264/**
2265 * Destroys a Shared Clipboard service's client state.
2266 *
2267 * @returns VBox status code.
2268 * @param pClientState Client state to destroy.
2269 */
2270int shClSvcClientStateDestroy(PSHCLCLIENTSTATE pClientState)
2271{
2272 RT_NOREF(pClientState);
2273
2274 LogFlowFuncEnter();
2275
2276 return VINF_SUCCESS;
2277}
2278
2279/**
2280 * Resets a Shared Clipboard service's client state.
2281 *
2282 * @param pClientState Client state to reset.
2283 */
2284void shclSvcClientStateReset(PSHCLCLIENTSTATE pClientState)
2285{
2286 LogFlowFuncEnter();
2287
2288 pClientState->fGuestFeatures0 = VBOX_SHCL_GF_NONE;
2289 pClientState->fGuestFeatures1 = VBOX_SHCL_GF_NONE;
2290
2291 pClientState->cbChunkSize = VBOX_SHCL_DEFAULT_CHUNK_SIZE; /** @todo Make this configurable. */
2292 pClientState->enmSource = SHCLSOURCE_INVALID;
2293 pClientState->fFlags = SHCLCLIENTSTATE_FLAGS_NONE;
2294
2295 pClientState->POD.enmDir = SHCLTRANSFERDIR_UNKNOWN;
2296 pClientState->POD.uFormat = VBOX_SHCL_FMT_NONE;
2297 pClientState->POD.cbToReadWriteTotal = 0;
2298 pClientState->POD.cbReadWritten = 0;
2299
2300#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2301 pClientState->Transfers.enmTransferDir = SHCLTRANSFERDIR_UNKNOWN;
2302#endif
2303}
2304
2305/*
2306 * We differentiate between a function handler for the guest and one for the host.
2307 */
2308static DECLCALLBACK(int) svcHostCall(void *,
2309 uint32_t u32Function,
2310 uint32_t cParms,
2311 VBOXHGCMSVCPARM paParms[])
2312{
2313 int rc = VINF_SUCCESS;
2314
2315 LogFlowFunc(("u32Function=%RU32 (%s), cParms=%RU32, paParms=%p\n",
2316 u32Function, ShClHostFunctionToStr(u32Function), cParms, paParms));
2317
2318 switch (u32Function)
2319 {
2320 case VBOX_SHCL_HOST_FN_SET_MODE:
2321 {
2322 if (cParms != 1)
2323 {
2324 rc = VERR_INVALID_PARAMETER;
2325 }
2326 else
2327 {
2328 uint32_t u32Mode = VBOX_SHCL_MODE_OFF;
2329
2330 rc = HGCMSvcGetU32(&paParms[0], &u32Mode);
2331 if (RT_SUCCESS(rc))
2332 rc = shClSvcModeSet(u32Mode);
2333 }
2334
2335 break;
2336 }
2337
2338#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2339 case VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE:
2340 {
2341 if (cParms != 1)
2342 {
2343 rc = VERR_INVALID_PARAMETER;
2344 }
2345 else
2346 {
2347 uint32_t fTransferMode;
2348 rc = HGCMSvcGetU32(&paParms[0], &fTransferMode);
2349 if (RT_SUCCESS(rc))
2350 rc = shClSvcTransferModeSet(fTransferMode);
2351 }
2352 break;
2353 }
2354#endif
2355 case VBOX_SHCL_HOST_FN_SET_HEADLESS:
2356 {
2357 if (cParms != 1)
2358 {
2359 rc = VERR_INVALID_PARAMETER;
2360 }
2361 else
2362 {
2363 uint32_t uHeadless;
2364 rc = HGCMSvcGetU32(&paParms[0], &uHeadless);
2365 if (RT_SUCCESS(rc))
2366 {
2367 g_fHeadless = RT_BOOL(uHeadless);
2368 LogRel(("Shared Clipboard: Service running in %s mode\n", g_fHeadless ? "headless" : "normal"));
2369 }
2370 }
2371 break;
2372 }
2373
2374 default:
2375 {
2376#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
2377 rc = shClSvcTransferHostHandler(u32Function, cParms, paParms);
2378#else
2379 rc = VERR_NOT_IMPLEMENTED;
2380#endif
2381 break;
2382 }
2383 }
2384
2385 LogFlowFuncLeaveRC(rc);
2386 return rc;
2387}
2388
2389#ifndef UNIT_TEST
2390
2391/**
2392 * SSM descriptor table for the SHCLCLIENTLEGACYCID structure.
2393 *
2394 * @note Saving the ListEntry attribute is not necessary, as this gets used on runtime only.
2395 */
2396static SSMFIELD const s_aShClSSMClientLegacyCID[] =
2397{
2398 SSMFIELD_ENTRY(SHCLCLIENTLEGACYCID, uCID),
2399 SSMFIELD_ENTRY(SHCLCLIENTLEGACYCID, enmType),
2400 SSMFIELD_ENTRY(SHCLCLIENTLEGACYCID, uFormat),
2401 SSMFIELD_ENTRY_TERM()
2402};
2403
2404/**
2405 * SSM descriptor table for the SHCLCLIENTSTATE structure.
2406 *
2407 * @note Saving the session ID not necessary, as they're not persistent across
2408 * state save/restore.
2409 */
2410static SSMFIELD const s_aShClSSMClientState[] =
2411{
2412 SSMFIELD_ENTRY(SHCLCLIENTSTATE, fGuestFeatures0),
2413 SSMFIELD_ENTRY(SHCLCLIENTSTATE, fGuestFeatures1),
2414 SSMFIELD_ENTRY(SHCLCLIENTSTATE, cbChunkSize),
2415 SSMFIELD_ENTRY(SHCLCLIENTSTATE, enmSource),
2416 SSMFIELD_ENTRY(SHCLCLIENTSTATE, fFlags),
2417 SSMFIELD_ENTRY_TERM()
2418};
2419
2420/**
2421 * VBox 6.1 Beta 1 version of s_aShClSSMClientState (no flags).
2422 */
2423static SSMFIELD const s_aShClSSMClientState61B1[] =
2424{
2425 SSMFIELD_ENTRY(SHCLCLIENTSTATE, fGuestFeatures0),
2426 SSMFIELD_ENTRY(SHCLCLIENTSTATE, fGuestFeatures1),
2427 SSMFIELD_ENTRY(SHCLCLIENTSTATE, cbChunkSize),
2428 SSMFIELD_ENTRY(SHCLCLIENTSTATE, enmSource),
2429 SSMFIELD_ENTRY_TERM()
2430};
2431
2432/**
2433 * SSM descriptor table for the SHCLCLIENTPODSTATE structure.
2434 */
2435static SSMFIELD const s_aShClSSMClientPODState[] =
2436{
2437 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, enmDir),
2438 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, uFormat),
2439 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, cbToReadWriteTotal),
2440 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, cbReadWritten),
2441 SSMFIELD_ENTRY(SHCLCLIENTPODSTATE, tsLastReadWrittenMs),
2442 SSMFIELD_ENTRY_TERM()
2443};
2444
2445/**
2446 * SSM descriptor table for the SHCLCLIENTURISTATE structure.
2447 */
2448static SSMFIELD const s_aShClSSMClientTransferState[] =
2449{
2450 SSMFIELD_ENTRY(SHCLCLIENTTRANSFERSTATE, enmTransferDir),
2451 SSMFIELD_ENTRY_TERM()
2452};
2453
2454/**
2455 * SSM descriptor table for the header of the SHCLCLIENTMSG structure.
2456 * The actual message parameters will be serialized separately.
2457 */
2458static SSMFIELD const s_aShClSSMClientMsgHdr[] =
2459{
2460 SSMFIELD_ENTRY(SHCLCLIENTMSG, idMsg),
2461 SSMFIELD_ENTRY(SHCLCLIENTMSG, cParms),
2462 SSMFIELD_ENTRY_TERM()
2463};
2464
2465/**
2466 * SSM descriptor table for what used to be the VBOXSHCLMSGCTX structure but is
2467 * now part of SHCLCLIENTMSG.
2468 */
2469static SSMFIELD const s_aShClSSMClientMsgCtx[] =
2470{
2471 SSMFIELD_ENTRY(SHCLCLIENTMSG, idCtx),
2472 SSMFIELD_ENTRY_TERM()
2473};
2474#endif /* !UNIT_TEST */
2475
2476static DECLCALLBACK(int) svcSaveState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
2477{
2478 LogFlowFuncEnter();
2479
2480#ifndef UNIT_TEST
2481 /*
2482 * When the state will be restored, pending requests will be reissued
2483 * by VMMDev. The service therefore must save state as if there were no
2484 * pending request.
2485 * Pending requests, if any, will be completed in svcDisconnect.
2486 */
2487 RT_NOREF(u32ClientID);
2488 LogFunc(("u32ClientID=%RU32\n", u32ClientID));
2489
2490 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
2491 AssertPtr(pClient);
2492
2493 /* Write Shared Clipboard saved state version. */
2494 SSMR3PutU32(pSSM, VBOX_SHCL_SAVED_STATE_VER_CURRENT);
2495
2496 int rc = SSMR3PutStructEx(pSSM, &pClient->State, sizeof(pClient->State), 0 /*fFlags*/, &s_aShClSSMClientState[0], NULL);
2497 AssertRCReturn(rc, rc);
2498
2499 rc = SSMR3PutStructEx(pSSM, &pClient->State.POD, sizeof(pClient->State.POD), 0 /*fFlags*/, &s_aShClSSMClientPODState[0], NULL);
2500 AssertRCReturn(rc, rc);
2501
2502 rc = SSMR3PutStructEx(pSSM, &pClient->State.Transfers, sizeof(pClient->State.Transfers), 0 /*fFlags*/, &s_aShClSSMClientTransferState[0], NULL);
2503 AssertRCReturn(rc, rc);
2504
2505 /* Serialize the client's internal message queue. */
2506 rc = SSMR3PutU64(pSSM, pClient->cMsgAllocated);
2507 AssertRCReturn(rc, rc);
2508
2509 PSHCLCLIENTMSG pMsg;
2510 RTListForEach(&pClient->MsgQueue, pMsg, SHCLCLIENTMSG, ListEntry)
2511 {
2512 SSMR3PutStructEx(pSSM, pMsg, sizeof(SHCLCLIENTMSG), 0 /*fFlags*/, &s_aShClSSMClientMsgHdr[0], NULL);
2513 SSMR3PutStructEx(pSSM, pMsg, sizeof(SHCLCLIENTMSG), 0 /*fFlags*/, &s_aShClSSMClientMsgCtx[0], NULL);
2514
2515 for (uint32_t iParm = 0; iParm < pMsg->cParms; iParm++)
2516 HGCMSvcSSMR3Put(&pMsg->aParms[iParm], pSSM);
2517 }
2518
2519 rc = SSMR3PutU64(pSSM, pClient->Legacy.cCID);
2520 AssertRCReturn(rc, rc);
2521
2522 PSHCLCLIENTLEGACYCID pCID;
2523 RTListForEach(&pClient->Legacy.lstCID, pCID, SHCLCLIENTLEGACYCID, Node)
2524 {
2525 rc = SSMR3PutStructEx(pSSM, pCID, sizeof(SHCLCLIENTLEGACYCID), 0 /*fFlags*/, &s_aShClSSMClientLegacyCID[0], NULL);
2526 AssertRCReturn(rc, rc);
2527 }
2528#else /* UNIT_TEST */
2529 RT_NOREF3(u32ClientID, pvClient, pSSM);
2530#endif /* UNIT_TEST */
2531 return VINF_SUCCESS;
2532}
2533
2534#ifndef UNIT_TEST
2535static int svcLoadStateV0(uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
2536{
2537 RT_NOREF(u32ClientID, pvClient, pSSM, uVersion);
2538
2539 uint32_t uMarker;
2540 int rc = SSMR3GetU32(pSSM, &uMarker); /* Begin marker. */
2541 AssertRC(rc);
2542 Assert(uMarker == UINT32_C(0x19200102) /* SSMR3STRUCT_BEGIN */);
2543
2544 rc = SSMR3Skip(pSSM, sizeof(uint32_t)); /* Client ID */
2545 AssertRCReturn(rc, rc);
2546
2547 bool fValue;
2548 rc = SSMR3GetBool(pSSM, &fValue); /* fHostMsgQuit */
2549 AssertRCReturn(rc, rc);
2550
2551 rc = SSMR3GetBool(pSSM, &fValue); /* fHostMsgReadData */
2552 AssertRCReturn(rc, rc);
2553
2554 rc = SSMR3GetBool(pSSM, &fValue); /* fHostMsgFormats */
2555 AssertRCReturn(rc, rc);
2556
2557 uint32_t fFormats;
2558 rc = SSMR3GetU32(pSSM, &fFormats); /* u32RequestedFormat */
2559 AssertRCReturn(rc, rc);
2560
2561 rc = SSMR3GetU32(pSSM, &uMarker); /* End marker. */
2562 AssertRCReturn(rc, rc);
2563 Assert(uMarker == UINT32_C(0x19920406) /* SSMR3STRUCT_END */);
2564
2565 return VINF_SUCCESS;
2566}
2567#endif /* UNIT_TEST */
2568
2569static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM, uint32_t uVersion)
2570{
2571 LogFlowFuncEnter();
2572
2573#ifndef UNIT_TEST
2574
2575 RT_NOREF(u32ClientID, uVersion);
2576
2577 PSHCLCLIENT pClient = (PSHCLCLIENT)pvClient;
2578 AssertPtr(pClient);
2579
2580 /* Restore the client data. */
2581 uint32_t lenOrVer;
2582 int rc = SSMR3GetU32(pSSM, &lenOrVer);
2583 AssertRCReturn(rc, rc);
2584
2585 LogFunc(("u32ClientID=%RU32, lenOrVer=%#RX64\n", u32ClientID, lenOrVer));
2586
2587 if (lenOrVer == VBOX_SHCL_SAVED_STATE_VER_3_1)
2588 return svcLoadStateV0(u32ClientID, pvClient, pSSM, uVersion);
2589
2590 if ( lenOrVer >= VBOX_SHCL_SAVED_STATE_VER_6_1B2
2591 && lenOrVer <= VBOX_SHCL_SAVED_STATE_VER_CURRENT)
2592 {
2593 if (lenOrVer >= VBOX_SHCL_SAVED_STATE_VER_6_1RC1)
2594 {
2595 SSMR3GetStructEx(pSSM, &pClient->State, sizeof(pClient->State), 0 /* fFlags */, &s_aShClSSMClientState[0], NULL);
2596 SSMR3GetStructEx(pSSM, &pClient->State.POD, sizeof(pClient->State.POD), 0 /* fFlags */,
2597 &s_aShClSSMClientPODState[0], NULL);
2598 }
2599 else
2600 SSMR3GetStructEx(pSSM, &pClient->State, sizeof(pClient->State), 0 /* fFlags */, &s_aShClSSMClientState61B1[0], NULL);
2601 rc = SSMR3GetStructEx(pSSM, &pClient->State.Transfers, sizeof(pClient->State.Transfers), 0 /* fFlags */,
2602 &s_aShClSSMClientTransferState[0], NULL);
2603 AssertRCReturn(rc, rc);
2604
2605 /* Load the client's internal message queue. */
2606 uint64_t cMsgs;
2607 rc = SSMR3GetU64(pSSM, &cMsgs);
2608 AssertRCReturn(rc, rc);
2609 AssertLogRelMsgReturn(cMsgs < _16K, ("Too many messages: %u (%x)\n", cMsgs, cMsgs), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
2610
2611 for (uint64_t i = 0; i < cMsgs; i++)
2612 {
2613 union
2614 {
2615 SHCLCLIENTMSG Msg;
2616 uint8_t abPadding[RT_UOFFSETOF(SHCLCLIENTMSG, aParms) + sizeof(VBOXHGCMSVCPARM) * 2];
2617 } u;
2618
2619 SSMR3GetStructEx(pSSM, &u.Msg, RT_UOFFSETOF(SHCLCLIENTMSG, aParms), 0 /*fFlags*/, &s_aShClSSMClientMsgHdr[0], NULL);
2620 rc = SSMR3GetStructEx(pSSM, &u.Msg, RT_UOFFSETOF(SHCLCLIENTMSG, aParms), 0 /*fFlags*/, &s_aShClSSMClientMsgCtx[0], NULL);
2621 AssertRCReturn(rc, rc);
2622
2623 AssertLogRelMsgReturn(u.Msg.cParms <= VMMDEV_MAX_HGCM_PARMS,
2624 ("Too many HGCM message parameters: %u (%#x)\n", u.Msg.cParms, u.Msg.cParms),
2625 VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
2626
2627 PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, u.Msg.idMsg, u.Msg.cParms);
2628 AssertReturn(pMsg, VERR_NO_MEMORY);
2629 pMsg->idCtx = u.Msg.idCtx;
2630
2631 for (uint32_t p = 0; p < pMsg->cParms; p++)
2632 {
2633 rc = HGCMSvcSSMR3Get(&pMsg->aParms[p], pSSM);
2634 AssertRCReturnStmt(rc, shClSvcMsgFree(pClient, pMsg), rc);
2635 }
2636
2637 RTCritSectEnter(&pClient->CritSect);
2638 shClSvcMsgAdd(pClient, pMsg, true /* fAppend */);
2639 RTCritSectLeave(&pClient->CritSect);
2640 }
2641
2642 if (lenOrVer >= VBOX_SHCL_SAVED_STATE_LEGACY_CID)
2643 {
2644 uint64_t cCID;
2645 rc = SSMR3GetU64(pSSM, &cCID);
2646 AssertRCReturn(rc, rc);
2647 AssertLogRelMsgReturn(cCID < _16K, ("Too many context IDs: %u (%x)\n", cCID, cCID), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
2648
2649 for (uint64_t i = 0; i < cCID; i++)
2650 {
2651 PSHCLCLIENTLEGACYCID pCID = (PSHCLCLIENTLEGACYCID)RTMemAlloc(sizeof(SHCLCLIENTLEGACYCID));
2652 AssertPtrReturn(pCID, VERR_NO_MEMORY);
2653
2654 SSMR3GetStructEx(pSSM, pCID, sizeof(SHCLCLIENTLEGACYCID), 0 /* fFlags */, &s_aShClSSMClientLegacyCID[0], NULL);
2655 RTListAppend(&pClient->Legacy.lstCID, &pCID->Node);
2656 }
2657 }
2658 }
2659 else
2660 {
2661 LogRel(("Shared Clipboard: Unsupported saved state version (%#x)\n", lenOrVer));
2662 return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
2663 }
2664
2665 /* Actual host data are to be reported to guest (SYNC). */
2666 ShClBackendSync(pClient);
2667
2668#else /* UNIT_TEST */
2669 RT_NOREF(u32ClientID, pvClient, pSSM, uVersion);
2670#endif /* UNIT_TEST */
2671 return VINF_SUCCESS;
2672}
2673
2674static DECLCALLBACK(int) extCallback(uint32_t u32Function, uint32_t u32Format, void *pvData, uint32_t cbData)
2675{
2676 RT_NOREF(pvData, cbData);
2677
2678 LogFlowFunc(("u32Function=%RU32\n", u32Function));
2679
2680 int rc = VINF_SUCCESS;
2681
2682 /* Figure out if the client in charge for the service extension still is connected. */
2683 ClipboardClientMap::const_iterator itClient = g_mapClients.find(g_ExtState.uClientID);
2684 if (itClient != g_mapClients.end())
2685 {
2686 PSHCLCLIENT pClient = itClient->second;
2687 AssertPtr(pClient);
2688
2689 switch (u32Function)
2690 {
2691 /* The service extension announces formats to the guest. */
2692 case VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE:
2693 {
2694 LogFlowFunc(("VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE: g_ExtState.fReadingData=%RTbool\n", g_ExtState.fReadingData));
2695 if (!g_ExtState.fReadingData)
2696 rc = ShClSvcHostReportFormats(pClient, u32Format);
2697 else
2698 {
2699 g_ExtState.fDelayedAnnouncement = true;
2700 g_ExtState.fDelayedFormats = u32Format;
2701 rc = VINF_SUCCESS;
2702 }
2703 break;
2704 }
2705
2706 /* The service extension wants read data from the guest. */
2707 case VBOX_CLIPBOARD_EXT_FN_DATA_READ:
2708 rc = ShClSvcGuestDataRequest(pClient, u32Format, NULL /* pidEvent */);
2709 break;
2710
2711 default:
2712 /* Just skip other messages. */
2713 break;
2714 }
2715 }
2716 else
2717 rc = VERR_NOT_FOUND;
2718
2719 LogFlowFuncLeaveRC(rc);
2720 return rc;
2721}
2722
2723static DECLCALLBACK(int) svcRegisterExtension(void *, PFNHGCMSVCEXT pfnExtension, void *pvExtension)
2724{
2725 LogFlowFunc(("pfnExtension=%p\n", pfnExtension));
2726
2727 SHCLEXTPARMS parms;
2728 RT_ZERO(parms);
2729
2730 /*
2731 * Reference counting for service extension registration is done a few
2732 * layers up (in ConsoleVRDPServer::ClipboardCreate()).
2733 */
2734
2735 int rc = RTCritSectEnter(&g_CritSect);
2736 AssertLogRelRCReturn(rc, rc);
2737
2738 if (pfnExtension)
2739 {
2740 /* Install extension. */
2741 g_ExtState.pfnExtension = pfnExtension;
2742 g_ExtState.pvExtension = pvExtension;
2743
2744 parms.u.pfnCallback = extCallback;
2745 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
2746
2747 LogRel2(("Shared Clipboard: registered service extension\n"));
2748 }
2749 else
2750 {
2751 if (g_ExtState.pfnExtension)
2752 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK, &parms, sizeof(parms));
2753
2754 /* Uninstall extension. */
2755 g_ExtState.pvExtension = NULL;
2756 g_ExtState.pfnExtension = NULL;
2757
2758 LogRel2(("Shared Clipboard: de-registered service extension\n"));
2759 }
2760
2761 RTCritSectLeave(&g_CritSect);
2762
2763 return VINF_SUCCESS;
2764}
2765
2766extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
2767{
2768 int rc = VINF_SUCCESS;
2769
2770 LogFlowFunc(("pTable=%p\n", pTable));
2771
2772 if (!RT_VALID_PTR(pTable))
2773 {
2774 rc = VERR_INVALID_PARAMETER;
2775 }
2776 else
2777 {
2778 LogFunc(("pTable->cbSize = %d, ptable->u32Version = 0x%08X\n", pTable->cbSize, pTable->u32Version));
2779
2780 if ( pTable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
2781 || pTable->u32Version != VBOX_HGCM_SVC_VERSION)
2782 {
2783 rc = VERR_VERSION_MISMATCH;
2784 }
2785 else
2786 {
2787 g_pHelpers = pTable->pHelpers;
2788
2789 pTable->cbClient = sizeof(SHCLCLIENT);
2790
2791 /* Map legacy clients to root. */
2792 pTable->idxLegacyClientCategory = HGCM_CLIENT_CATEGORY_ROOT;
2793
2794 /* Limit the number of clients to 128 in each category (should be enough),
2795 but set kernel clients to 1. */
2796 for (uintptr_t i = 0; i < RT_ELEMENTS(pTable->acMaxClients); i++)
2797 pTable->acMaxClients[i] = 128;
2798 pTable->acMaxClients[HGCM_CLIENT_CATEGORY_KERNEL] = 1;
2799
2800 /* Only 16 pending calls per client (1 should be enough). */
2801 for (uintptr_t i = 0; i < RT_ELEMENTS(pTable->acMaxClients); i++)
2802 pTable->acMaxCallsPerClient[i] = 16;
2803
2804 pTable->pfnUnload = svcUnload;
2805 pTable->pfnConnect = svcConnect;
2806 pTable->pfnDisconnect = svcDisconnect;
2807 pTable->pfnCall = svcCall;
2808 pTable->pfnHostCall = svcHostCall;
2809 pTable->pfnSaveState = svcSaveState;
2810 pTable->pfnLoadState = svcLoadState;
2811 pTable->pfnRegisterExtension = svcRegisterExtension;
2812 pTable->pfnNotify = NULL;
2813 pTable->pvService = NULL;
2814
2815 /* Service specific initialization. */
2816 rc = svcInit(pTable);
2817 }
2818 }
2819
2820 LogFlowFunc(("Returning %Rrc\n", rc));
2821 return rc;
2822}
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