VirtualBox

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

Last change on this file since 100347 was 100204, checked in by vboxsync, 21 months ago

Shared Clipboard: Unified root list entry code to also use the generic list entry code, a lot of updates for the cross OS transfer handling code, more updates for HTTP server transfer handling.

This also changed the handling of how that transfers are being initiated, as we needed to have this for X11: Before, transfers were initiated as soon as on side announced the URI list format -- now we postpone initiating the transfer until the receiving side requests the data as URI list.

bugref:9437

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

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