VirtualBox

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

Last change on this file since 82526 was 82526, checked in by vboxsync, 5 years ago

SharedClipboardSvc,Vbgl: Started looking over the host message handling, adding missing locking and fixing docs. bugref:9437

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

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