VirtualBox

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

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

Shared Clipboard: prevent guest clipboard reset when clipboard sharing is disabled, bugref:10110, ticketref:20487.

This commit prevents host service from notifying guest about new data appearance
in host clipboard buffer when clipboard sharing is disabled. When such notification
was sent in such situation, it triggered guest to ask host for a clipboard content.
Such request was rejected on a host side what, in turn, resulted in guest clipboard
content loss.

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

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