VirtualBox

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

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

Shared Clipboard: Implemented backend callbacks and a dedicated backend context, together with a new testcase which mocks HGCM to also test the guest-side clipboard code (disabled by default for now). Work in progress, only tested on Linux so far.

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