VirtualBox

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

Last change on this file since 99937 was 99775, checked in by vboxsync, 20 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

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