VirtualBox

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

Last change on this file since 105745 was 105635, checked in by vboxsync, 4 months ago

Shared Clipboard: Cleanup: Made a lot more functions static where possible and renamed the non-static ones to indicate its usage.

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