VirtualBox

source: vbox/trunk/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.cpp@ 100619

Last change on this file since 100619 was 100616, checked in by vboxsync, 17 months ago

Shared Clipboard: More code for host service error propagation. bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 88.0 KB
Line 
1/* $Id: VBoxSharedClipboardSvc-transfers.cpp 100616 2023-07-17 18:22:58Z vboxsync $ */
2/** @file
3 * Shared Clipboard Service - Internal code for transfer (list) handling.
4 */
5
6/*
7 * Copyright (C) 2019-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
33#include <VBox/log.h>
34
35#include <VBox/err.h>
36
37#include <VBox/GuestHost/clipboard-helper.h>
38#include <VBox/HostServices/VBoxClipboardSvc.h>
39#include <VBox/HostServices/VBoxClipboardExt.h>
40
41#include <VBox/AssertGuest.h>
42#include <iprt/dir.h>
43#include <iprt/file.h>
44#include <iprt/path.h>
45
46#include "VBoxSharedClipboardSvc-internal.h"
47#include "VBoxSharedClipboardSvc-transfers.h"
48
49
50/*********************************************************************************************************************************
51* Externals *
52*********************************************************************************************************************************/
53extern uint32_t g_fTransferMode;
54extern SHCLEXTSTATE g_ExtState;
55extern PVBOXHGCMSVCHELPERS g_pHelpers;
56extern ClipboardClientMap g_mapClients;
57extern ClipboardClientQueue g_listClientsDeferred;
58
59
60/*********************************************************************************************************************************
61* Prototypes *
62*********************************************************************************************************************************/
63static int shClSvcTransferSendStatusAsync(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, SHCLTRANSFERSTATUS uStatus, int rcTransfer, PSHCLEVENT *ppEvent);
64static int shClSvcTransferSetListOpen(uint32_t cParms, VBOXHGCMSVCPARM aParms[], uint64_t idCtx, PSHCLLISTOPENPARMS pOpenParms);
65static int shClSvcTransferSetListClose(uint32_t cParms, VBOXHGCMSVCPARM aParms[], uint64_t idCtx, SHCLLISTHANDLE hList);
66
67
68/**
69 * Destroys all transfers of a Shared Clipboard client.
70 *
71 * @param pClient Client to destroy transfers for.
72 */
73void shClSvcTransferDestroyAll(PSHCLCLIENT pClient)
74{
75 if (!pClient)
76 return;
77
78 LogFlowFuncEnter();
79
80 /* Unregister and destroy all transfers.
81 * Also make sure to let the backend know that all transfers are getting destroyed.
82 *
83 * Note: The index always will be 0, as the transfer gets unregistered. */
84 PSHCLTRANSFER pTransfer;
85 while ((pTransfer = ShClTransferCtxGetTransferByIndex(&pClient->Transfers.Ctx, 0 /* Index */)))
86 ShClSvcTransferDestroy(pClient, pTransfer);
87}
88
89/**
90 * Reads a root list header from the guest, asynchronous version.
91 *
92 * @returns VBox status code.
93 * @param pClient Client to read from.
94 * @param pTransfer Transfer to read root list header for.
95 * @param ppEvent Where to return the event to wait for.
96 * Must be released by the caller with ShClEventRelease().
97 */
98int ShClSvcTransferGHRootListReadHdrAsync(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, PSHCLEVENT *ppEvent)
99{
100 LogFlowFuncEnter();
101
102 int rc;
103
104 PSHCLCLIENTMSG pMsgHdr = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_READ,
105 VBOX_SHCL_CPARMS_ROOT_LIST_HDR_READ_REQ);
106 if (pMsgHdr)
107 {
108 PSHCLEVENT pEvent;
109 rc = ShClEventSourceGenerateAndRegisterEvent(&pTransfer->Events, &pEvent);
110 if (RT_SUCCESS(rc))
111 {
112 HGCMSvcSetU64(&pMsgHdr->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
113 ShClTransferGetID(pTransfer), pEvent->idEvent));
114 HGCMSvcSetU32(&pMsgHdr->aParms[1], 0 /* fRoots */);
115
116 shClSvcClientLock(pClient);
117
118 shClSvcMsgAdd(pClient, pMsgHdr, true /* fAppend */);
119 rc = shClSvcClientWakeup(pClient);
120
121 shClSvcClientUnlock(pClient);
122
123 /* Remove event from list if caller did not request event handle or in case
124 * of failure (in this case caller should not release event). */
125 if ( RT_FAILURE(rc)
126 || !ppEvent)
127 {
128 ShClEventRelease(pEvent);
129 pEvent = NULL;
130 }
131 else if (ppEvent)
132 *ppEvent = pEvent;
133 }
134 else
135 {
136 shClSvcMsgFree(pClient, pMsgHdr);
137 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
138 }
139 }
140 else
141 rc = VERR_NO_MEMORY;
142
143 LogFlowFuncLeaveRC(rc);
144 return rc;
145}
146
147/**
148 * Reads a root list header from the guest.
149 *
150 * @returns VBox status code.
151 * @param pClient Client to read from.
152 * @param pTransfer Transfer to read root list header for.
153 * @param pHdr Where to store the root list header on succeess.
154 */
155int ShClSvcTransferGHRootListReadHdr(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, PSHCLLISTHDR pHdr)
156{
157 PSHCLEVENT pEvent;
158 int rc = ShClSvcTransferGHRootListReadHdrAsync(pClient, pTransfer, &pEvent);
159 if (RT_SUCCESS(rc))
160 {
161 PSHCLEVENTPAYLOAD pPayload;
162 rc = ShClEventWait(pEvent, SHCL_TIMEOUT_DEFAULT_MS, &pPayload);
163 if (RT_SUCCESS(rc))
164 {
165 Assert(pPayload->cbData == sizeof(SHCLLISTHDR));
166
167 memcpy(pHdr, (PSHCLLISTHDR)pPayload->pvData, sizeof(SHCLLISTHDR));
168
169 LogFlowFunc(("cRoots=%RU32, fFeatures=0x%x\n", pHdr->cEntries, pHdr->fFeatures));
170
171 ShClPayloadFree(pPayload);
172 }
173
174 ShClEventRelease(pEvent);
175 pEvent = NULL;
176 }
177
178 LogFlowFuncLeaveRC(rc);
179 return rc;
180}
181
182/**
183 * Reads a root list entry from the guest, asynchronous version.
184 *
185 * @returns VBox status code.
186 * @param pClient Client to read from.
187 * @param pTransfer Transfer to read root list header for.
188 * @param idxEntry Index of entry to read.
189 * @param ppEvent Where to return the event to wait for.
190 * Must be released by the caller with ShClEventRelease().
191 */
192int ShClSvcTransferGHRootListReadEntryAsync(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, uint64_t idxEntry,
193 PSHCLEVENT *ppEvent)
194{
195 LogFlowFuncEnter();
196
197 PSHCLCLIENTMSG pMsgEntry = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_READ,
198 VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_READ_REQ);
199
200 PSHCLEVENT pEvent;
201 int rc = ShClEventSourceGenerateAndRegisterEvent(&pTransfer->Events, &pEvent);
202 if (RT_SUCCESS(rc))
203 {
204 HGCMSvcSetU64(&pMsgEntry->aParms[0],
205 VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uClientID, ShClTransferGetID(pTransfer), pEvent->idEvent));
206 HGCMSvcSetU32(&pMsgEntry->aParms[1], 0 /* fFeatures */);
207 HGCMSvcSetU64(&pMsgEntry->aParms[2], idxEntry /* uIndex */);
208
209 shClSvcClientLock(pClient);
210
211 shClSvcMsgAdd(pClient, pMsgEntry, true /* fAppend */);
212 rc = shClSvcClientWakeup(pClient);
213
214 shClSvcClientUnlock(pClient);
215
216 /* Remove event from list if caller did not request event handle or in case
217 * of failure (in this case caller should not release event). */
218 if ( RT_FAILURE(rc)
219 || !ppEvent)
220 {
221 ShClEventRelease(pEvent);
222 pEvent = NULL;
223 }
224 else if (ppEvent)
225 *ppEvent = pEvent;
226 }
227 else
228 rc = VERR_NO_MEMORY;
229
230 LogFlowFuncLeave();
231 return rc;
232}
233
234/**
235 * Reads a root list entry from the guest.
236 *
237 * @returns VBox status code.
238 * @param pClient Client to read from.
239 * @param pTransfer Transfer to read root list header for.
240 * @param idxEntry Index of entry to read.
241 * @param ppListEntry Where to return the allocated root list entry.
242 */
243int ShClSvcTransferGHRootListReadEntry(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, uint64_t idxEntry,
244 PSHCLLISTENTRY *ppListEntry)
245{
246 AssertPtrReturn(ppListEntry, VERR_INVALID_POINTER);
247
248 PSHCLEVENT pEvent;
249 int rc = ShClSvcTransferGHRootListReadEntryAsync(pClient, pTransfer, idxEntry, &pEvent);
250 if (RT_SUCCESS(rc))
251 {
252 PSHCLEVENTPAYLOAD pPayload;
253 rc = ShClEventWait(pEvent, SHCL_TIMEOUT_DEFAULT_MS, &pPayload);
254 if (RT_SUCCESS(rc))
255 {
256 *ppListEntry = (PSHCLLISTENTRY)pPayload->pvData; /* ppLisEntry own pPayload-pvData now. */
257
258 }
259
260 ShClEventRelease(pEvent);
261 pEvent = NULL;
262 }
263
264 LogFlowFuncLeaveRC(rc);
265 return rc;
266}
267
268
269/*********************************************************************************************************************************
270* Provider interface implementation *
271*********************************************************************************************************************************/
272
273/** @copydoc SHCLTXPROVIDERIFACE::pfnRootListRead */
274DECLCALLBACK(int) shClSvcTransferIfaceGHRootListRead(PSHCLTXPROVIDERCTX pCtx)
275{
276 LogFlowFuncEnter();
277
278 PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser;
279 AssertPtr(pClient);
280
281 SHCLLISTHDR Hdr;
282 int rc = ShClSvcTransferGHRootListReadHdr(pClient, pCtx->pTransfer, &Hdr);
283 if (RT_SUCCESS(rc))
284 {
285 for (uint64_t i = 0; i < Hdr.cEntries; i++)
286 {
287 PSHCLLISTENTRY pEntry;
288 rc = ShClSvcTransferGHRootListReadEntry(pClient, pCtx->pTransfer, i, &pEntry);
289 if (RT_SUCCESS(rc))
290 rc = ShClTransferListAddEntry(&pCtx->pTransfer->lstRoots, pEntry, true /* fAppend */);
291
292 if (RT_FAILURE(rc))
293 break;
294 }
295 }
296
297 LogFlowFuncLeave();
298 return rc;
299}
300
301/** @copydoc SHCLTXPROVIDERIFACE::pfnListOpen */
302DECLCALLBACK(int) shClSvcTransferIfaceGHListOpen(PSHCLTXPROVIDERCTX pCtx,
303 PSHCLLISTOPENPARMS pOpenParms, PSHCLLISTHANDLE phList)
304{
305 LogFlowFuncEnter();
306
307 PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser;
308 AssertPtr(pClient);
309
310 int rc;
311
312 PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_LIST_OPEN,
313 VBOX_SHCL_CPARMS_LIST_OPEN);
314 if (pMsg)
315 {
316 PSHCLEVENT pEvent;
317 rc = ShClEventSourceGenerateAndRegisterEvent(&pCtx->pTransfer->Events, &pEvent);
318 if (RT_SUCCESS(rc))
319 {
320 pMsg->idCtx = VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, pCtx->pTransfer->State.uID,
321 pEvent->idEvent);
322
323 rc = shClSvcTransferSetListOpen(pMsg->cParms, pMsg->aParms, pMsg->idCtx, pOpenParms);
324 if (RT_SUCCESS(rc))
325 {
326 shClSvcClientLock(pClient);
327
328 shClSvcMsgAdd(pClient, pMsg, true /* fAppend */);
329 rc = shClSvcClientWakeup(pClient);
330
331 shClSvcClientUnlock(pClient);
332
333 if (RT_SUCCESS(rc))
334 {
335 PSHCLEVENTPAYLOAD pPayload;
336 rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
337 if (RT_SUCCESS(rc))
338 {
339 Assert(pPayload->cbData == sizeof(SHCLREPLY));
340
341 PSHCLREPLY pReply = (PSHCLREPLY)pPayload->pvData;
342 AssertPtr(pReply);
343
344 Assert(pReply->uType == VBOX_SHCL_TX_REPLYMSGTYPE_LIST_OPEN);
345
346 LogFlowFunc(("hList=%RU64\n", pReply->u.ListOpen.uHandle));
347
348 *phList = pReply->u.ListOpen.uHandle;
349
350 ShClPayloadFree(pPayload);
351 }
352 }
353 }
354
355 ShClEventRelease(pEvent);
356 }
357 else
358 {
359 shClSvcMsgFree(pClient, pMsg);
360 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
361 }
362 }
363 else
364 rc = VERR_NO_MEMORY;
365
366 LogFlowFuncLeaveRC(rc);
367 return rc;
368}
369
370/** @copydoc SHCLTXPROVIDERIFACE::pfnListClose */
371DECLCALLBACK(int) shClSvcTransferIfaceGHListClose(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList)
372{
373 LogFlowFuncEnter();
374
375 PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser;
376 AssertPtr(pClient);
377
378 int rc;
379
380 PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_LIST_CLOSE,
381 VBOX_SHCL_CPARMS_LIST_CLOSE);
382 if (pMsg)
383 {
384 PSHCLEVENT pEvent;
385 rc = ShClEventSourceGenerateAndRegisterEvent(&pCtx->pTransfer->Events, &pEvent);
386 if (RT_SUCCESS(rc))
387 {
388 pMsg->idCtx = VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, pCtx->pTransfer->State.uID,
389 pEvent->idEvent);
390
391 rc = shClSvcTransferSetListClose(pMsg->cParms, pMsg->aParms, pMsg->idCtx, hList);
392 if (RT_SUCCESS(rc))
393 {
394 shClSvcClientLock(pClient);
395
396 shClSvcMsgAdd(pClient, pMsg, true /* fAppend */);
397 rc = shClSvcClientWakeup(pClient);
398
399 shClSvcClientUnlock(pClient);
400
401 if (RT_SUCCESS(rc))
402 {
403 PSHCLEVENTPAYLOAD pPayload;
404 rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
405 if (RT_SUCCESS(rc))
406 ShClPayloadFree(pPayload);
407 }
408 }
409
410 ShClEventRelease(pEvent);
411 }
412 else
413 {
414 shClSvcMsgFree(pClient, pMsg);
415 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
416 }
417 }
418 else
419 rc = VERR_NO_MEMORY;
420
421 LogFlowFuncLeaveRC(rc);
422 return rc;
423}
424
425/** @copydoc SHCLTXPROVIDERIFACE::pfnListHdrRead */
426DECLCALLBACK(int) shClSvcTransferIfaceGHListHdrRead(PSHCLTXPROVIDERCTX pCtx,
427 SHCLLISTHANDLE hList, PSHCLLISTHDR pListHdr)
428{
429 LogFlowFuncEnter();
430
431 PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser;
432 AssertPtr(pClient);
433
434 int rc;
435
436 PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_LIST_HDR_READ,
437 VBOX_SHCL_CPARMS_LIST_HDR_READ_REQ);
438 if (pMsg)
439 {
440 PSHCLEVENT pEvent;
441 rc = ShClEventSourceGenerateAndRegisterEvent(&pCtx->pTransfer->Events, &pEvent);
442 if (RT_SUCCESS(rc))
443 {
444 HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
445 pCtx->pTransfer->State.uID, pEvent->idEvent));
446 HGCMSvcSetU64(&pMsg->aParms[1], hList);
447 HGCMSvcSetU32(&pMsg->aParms[2], 0 /* fFlags */);
448
449 shClSvcClientLock(pClient);
450
451 shClSvcMsgAdd(pClient, pMsg, true /* fAppend */);
452 rc = shClSvcClientWakeup(pClient);
453
454 shClSvcClientUnlock(pClient);
455
456 if (RT_SUCCESS(rc))
457 {
458 PSHCLEVENTPAYLOAD pPayload;
459 rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
460 if (RT_SUCCESS(rc))
461 {
462 Assert(pPayload->cbData == sizeof(SHCLLISTHDR));
463
464 *pListHdr = *(PSHCLLISTHDR)pPayload->pvData;
465
466 ShClPayloadFree(pPayload);
467 }
468 }
469
470 ShClEventRelease(pEvent);
471 }
472 else
473 {
474 shClSvcMsgFree(pClient, pMsg);
475 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
476 }
477 }
478 else
479 rc = VERR_NO_MEMORY;
480
481 LogFlowFuncLeaveRC(rc);
482 return rc;
483}
484
485/** @copydoc SHCLTXPROVIDERIFACE::pfnListHdrWrite */
486DECLCALLBACK(int) shClSvcTransferIfaceHGListHdrWrite(PSHCLTXPROVIDERCTX pCtx,
487 SHCLLISTHANDLE hList, PSHCLLISTHDR pListHdr)
488{
489 RT_NOREF(pCtx, hList, pListHdr);
490
491 LogFlowFuncEnter();
492
493 return VERR_NOT_IMPLEMENTED;
494}
495
496/** @copydoc SHCLTXPROVIDERIFACE::pfnListEntryRead */
497DECLCALLBACK(int) shClSvcTransferIfaceGHListEntryRead(PSHCLTXPROVIDERCTX pCtx,
498 SHCLLISTHANDLE hList, PSHCLLISTENTRY pListEntry)
499{
500 LogFlowFuncEnter();
501
502 PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser;
503 AssertPtr(pClient);
504
505 int rc;
506
507 PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ,
508 VBOX_SHCL_CPARMS_LIST_ENTRY_READ);
509 if (pMsg)
510 {
511 PSHCLEVENT pEvent;
512 rc = ShClEventSourceGenerateAndRegisterEvent(&pCtx->pTransfer->Events, &pEvent);
513 if (RT_SUCCESS(rc))
514 {
515 HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
516 pCtx->pTransfer->State.uID, pEvent->idEvent));
517 HGCMSvcSetU64(&pMsg->aParms[1], hList);
518 HGCMSvcSetU32(&pMsg->aParms[2], 0 /* fInfo */);
519
520 shClSvcClientLock(pClient);
521
522 shClSvcMsgAdd(pClient, pMsg, true /* fAppend */);
523 rc = shClSvcClientWakeup(pClient);
524
525 shClSvcClientUnlock(pClient);
526
527 if (RT_SUCCESS(rc))
528 {
529 PSHCLEVENTPAYLOAD pPayload;
530 rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
531 if (RT_SUCCESS(rc))
532 {
533 Assert(pPayload->cbData == sizeof(SHCLLISTENTRY));
534
535 rc = ShClTransferListEntryCopy(pListEntry, (PSHCLLISTENTRY)pPayload->pvData);
536
537 ShClPayloadFree(pPayload);
538 }
539 }
540
541 ShClEventRelease(pEvent);
542 }
543 else
544 {
545 shClSvcMsgFree(pClient, pMsg);
546 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
547 }
548 }
549 else
550 rc = VERR_NO_MEMORY;
551
552 LogFlowFuncLeaveRC(rc);
553 return rc;
554}
555
556/** @copydoc SHCLTXPROVIDERIFACE::pfnListEntryWrite */
557DECLCALLBACK(int) shClSvcTransferIfaceHGListEntryWrite(PSHCLTXPROVIDERCTX pCtx,
558 SHCLLISTHANDLE hList, PSHCLLISTENTRY pListEntry)
559{
560 RT_NOREF(pCtx, hList, pListEntry);
561
562 LogFlowFuncEnter();
563
564 return VERR_NOT_IMPLEMENTED;
565}
566
567/** @copydoc SHCLTXPROVIDERIFACE::pfnObjOpen */
568DECLCALLBACK(int) shClSvcTransferIfaceGHObjOpen(PSHCLTXPROVIDERCTX pCtx, PSHCLOBJOPENCREATEPARMS pCreateParms, PSHCLOBJHANDLE phObj)
569{
570 LogFlowFuncEnter();
571
572 PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser;
573 AssertPtr(pClient);
574
575 int rc;
576
577 PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_OPEN,
578 VBOX_SHCL_CPARMS_OBJ_OPEN);
579 if (pMsg)
580 {
581 PSHCLEVENT pEvent;
582 rc = ShClEventSourceGenerateAndRegisterEvent(&pCtx->pTransfer->Events, &pEvent);
583 if (RT_SUCCESS(rc))
584 {
585 LogFlowFunc(("pszPath=%s, fCreate=0x%x\n", pCreateParms->pszPath, pCreateParms->fCreate));
586
587 const uint32_t cbPath = (uint32_t)strlen(pCreateParms->pszPath) + 1; /* Include terminating zero */
588
589 HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
590 pCtx->pTransfer->State.uID, pEvent->idEvent));
591 HGCMSvcSetU64(&pMsg->aParms[1], 0); /* uHandle */
592 HGCMSvcSetPv (&pMsg->aParms[2], pCreateParms->pszPath, cbPath);
593 HGCMSvcSetU32(&pMsg->aParms[3], pCreateParms->fCreate);
594
595 shClSvcClientLock(pClient);
596
597 shClSvcMsgAdd(pClient, pMsg, true /* fAppend */);
598 rc = shClSvcClientWakeup(pClient);
599
600 shClSvcClientUnlock(pClient);
601
602 if (RT_SUCCESS(rc))
603 {
604 PSHCLEVENTPAYLOAD pPayload;
605 rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
606 if (RT_SUCCESS(rc))
607 {
608 Assert(pPayload->cbData == sizeof(SHCLREPLY));
609
610 PSHCLREPLY pReply = (PSHCLREPLY)pPayload->pvData;
611 AssertPtr(pReply);
612
613 Assert(pReply->uType == VBOX_SHCL_TX_REPLYMSGTYPE_OBJ_OPEN);
614
615 LogFlowFunc(("hObj=%RU64\n", pReply->u.ObjOpen.uHandle));
616
617 *phObj = pReply->u.ObjOpen.uHandle;
618
619 ShClPayloadFree(pPayload);
620 }
621 }
622
623 ShClEventRelease(pEvent);
624 }
625 else
626 {
627 shClSvcMsgFree(pClient, pMsg);
628 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
629 }
630 }
631 else
632 rc = VERR_NO_MEMORY;
633
634 LogFlowFuncLeaveRC(rc);
635 return rc;
636}
637
638/** @copydoc SHCLTXPROVIDERIFACE::pfnObjClose */
639DECLCALLBACK(int) shClSvcTransferIfaceGHObjClose(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj)
640{
641 LogFlowFuncEnter();
642
643 PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser;
644 AssertPtr(pClient);
645
646 int rc;
647
648 PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_CLOSE,
649 VBOX_SHCL_CPARMS_OBJ_CLOSE);
650 if (pMsg)
651 {
652 PSHCLEVENT pEvent;
653 rc = ShClEventSourceGenerateAndRegisterEvent(&pCtx->pTransfer->Events, &pEvent);
654 if (RT_SUCCESS(rc))
655 {
656 HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
657 pCtx->pTransfer->State.uID, pEvent->idEvent));
658 HGCMSvcSetU64(&pMsg->aParms[1], hObj);
659
660 shClSvcClientLock(pClient);
661
662 shClSvcMsgAdd(pClient, pMsg, true /* fAppend */);
663 rc = shClSvcClientWakeup(pClient);
664
665 shClSvcClientUnlock(pClient);
666
667 if (RT_SUCCESS(rc))
668 {
669 PSHCLEVENTPAYLOAD pPayload;
670 rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
671 if (RT_SUCCESS(rc))
672 {
673 Assert(pPayload->cbData == sizeof(SHCLREPLY));
674#ifdef VBOX_STRICT
675 PSHCLREPLY pReply = (PSHCLREPLY)pPayload->pvData;
676 AssertPtr(pReply);
677
678 Assert(pReply->uType == VBOX_SHCL_TX_REPLYMSGTYPE_OBJ_CLOSE);
679
680 LogFlowFunc(("hObj=%RU64\n", pReply->u.ObjClose.uHandle));
681#endif
682 ShClPayloadFree(pPayload);
683 }
684 }
685
686 ShClEventRelease(pEvent);
687 }
688 else
689 {
690 shClSvcMsgFree(pClient, pMsg);
691 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
692 }
693 }
694 else
695 rc = VERR_NO_MEMORY;
696
697 LogFlowFuncLeaveRC(rc);
698 return rc;
699}
700
701/** @copydoc SHCLTXPROVIDERIFACE::pfnObjRead */
702DECLCALLBACK(int) shClSvcTransferIfaceGHObjRead(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj,
703 void *pvData, uint32_t cbData, uint32_t fFlags, uint32_t *pcbRead)
704{
705 LogFlowFuncEnter();
706
707 PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser;
708 AssertPtr(pClient);
709
710 int rc;
711
712 PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_READ,
713 VBOX_SHCL_CPARMS_OBJ_READ_REQ);
714 if (pMsg)
715 {
716 PSHCLEVENT pEvent;
717 rc = ShClEventSourceGenerateAndRegisterEvent(&pCtx->pTransfer->Events, &pEvent);
718 if (RT_SUCCESS(rc))
719 {
720 HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
721 pCtx->pTransfer->State.uID, pEvent->idEvent));
722 HGCMSvcSetU64(&pMsg->aParms[1], hObj);
723 HGCMSvcSetU32(&pMsg->aParms[2], cbData);
724 HGCMSvcSetU32(&pMsg->aParms[3], fFlags);
725
726 shClSvcClientLock(pClient);
727
728 shClSvcMsgAdd(pClient, pMsg, true /* fAppend */);
729 rc = shClSvcClientWakeup(pClient);
730
731 shClSvcClientUnlock(pClient);
732
733 if (RT_SUCCESS(rc))
734 {
735 PSHCLEVENTPAYLOAD pPayload;
736 rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
737 if (RT_SUCCESS(rc))
738 {
739 Assert(pPayload->cbData == sizeof(SHCLOBJDATACHUNK));
740
741 PSHCLOBJDATACHUNK pDataChunk = (PSHCLOBJDATACHUNK)pPayload->pvData;
742 AssertPtr(pDataChunk);
743
744 const uint32_t cbRead = RT_MIN(cbData, pDataChunk->cbData);
745
746 memcpy(pvData, pDataChunk->pvData, cbRead);
747
748 if (pcbRead)
749 *pcbRead = cbRead;
750
751 ShClPayloadFree(pPayload);
752 }
753 }
754
755 ShClEventRelease(pEvent);
756 }
757 else
758 {
759 shClSvcMsgFree(pClient, pMsg);
760 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
761 }
762 }
763 else
764 rc = VERR_NO_MEMORY;
765
766 LogFlowFuncLeaveRC(rc);
767 return rc;
768}
769
770/** @copydoc SHCLTXPROVIDERIFACE::pfnObjWrite */
771DECLCALLBACK(int) shClSvcTransferIfaceHGObjWrite(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj,
772 void *pvData, uint32_t cbData, uint32_t fFlags, uint32_t *pcbWritten)
773{
774 LogFlowFuncEnter();
775
776 PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser;
777 AssertPtr(pClient);
778
779 int rc;
780
781 PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_WRITE,
782 VBOX_SHCL_CPARMS_OBJ_WRITE);
783 if (pMsg)
784 {
785 PSHCLEVENT pEvent;
786 rc = ShClEventSourceGenerateAndRegisterEvent(&pCtx->pTransfer->Events, &pEvent);
787 if (RT_SUCCESS(rc))
788 {
789 HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID,
790 pCtx->pTransfer->State.uID, pEvent->idEvent));
791 HGCMSvcSetU64(&pMsg->aParms[1], hObj);
792 HGCMSvcSetU64(&pMsg->aParms[2], cbData);
793 HGCMSvcSetU64(&pMsg->aParms[3], fFlags);
794
795 shClSvcClientLock(pClient);
796
797 shClSvcMsgAdd(pClient, pMsg, true /* fAppend */);
798 rc = shClSvcClientWakeup(pClient);
799
800 shClSvcClientUnlock(pClient);
801
802 if (RT_SUCCESS(rc))
803 {
804 PSHCLEVENTPAYLOAD pPayload;
805 rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload);
806 if (RT_SUCCESS(rc))
807 {
808 const uint32_t cbRead = RT_MIN(cbData, pPayload->cbData);
809
810 memcpy(pvData, pPayload->pvData, cbRead);
811
812 if (pcbWritten)
813 *pcbWritten = cbRead;
814
815 ShClPayloadFree(pPayload);
816 }
817 }
818
819 ShClEventRelease(pEvent);
820 }
821 else
822 {
823 shClSvcMsgFree(pClient, pMsg);
824 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
825 }
826 }
827 else
828 rc = VERR_NO_MEMORY;
829
830 LogFlowFuncLeaveRC(rc);
831 return rc;
832}
833
834
835/*********************************************************************************************************************************
836* HGCM getters / setters *
837*********************************************************************************************************************************/
838
839/**
840 * Returns whether a HGCM message is allowed in a certain service mode or not.
841 *
842 * @returns \c true if message is allowed, \c false if not.
843 * @param uMode Service mode to check allowance for.
844 * @param uMsg HGCM message to check allowance for.
845 */
846bool shClSvcTransferMsgIsAllowed(uint32_t uMode, uint32_t uMsg)
847{
848 const bool fHostToGuest = uMode == VBOX_SHCL_MODE_HOST_TO_GUEST
849 || uMode == VBOX_SHCL_MODE_BIDIRECTIONAL;
850
851 const bool fGuestToHost = uMode == VBOX_SHCL_MODE_GUEST_TO_HOST
852 || uMode == VBOX_SHCL_MODE_BIDIRECTIONAL;
853
854 bool fAllowed = false; /* If in doubt, don't allow. */
855
856 switch (uMsg)
857 {
858 case VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_WRITE:
859 RT_FALL_THROUGH();
860 case VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_WRITE:
861 RT_FALL_THROUGH();
862 case VBOX_SHCL_GUEST_FN_LIST_HDR_WRITE:
863 RT_FALL_THROUGH();
864 case VBOX_SHCL_GUEST_FN_LIST_ENTRY_WRITE:
865 RT_FALL_THROUGH();
866 case VBOX_SHCL_GUEST_FN_OBJ_WRITE:
867 fAllowed = fGuestToHost;
868 break;
869
870 case VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_READ:
871 RT_FALL_THROUGH();
872 case VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_READ:
873 RT_FALL_THROUGH();
874 case VBOX_SHCL_GUEST_FN_LIST_HDR_READ:
875 RT_FALL_THROUGH();
876 case VBOX_SHCL_GUEST_FN_LIST_ENTRY_READ:
877 RT_FALL_THROUGH();
878 case VBOX_SHCL_GUEST_FN_OBJ_READ:
879 fAllowed = fHostToGuest;
880 break;
881
882 case VBOX_SHCL_GUEST_FN_CONNECT:
883 RT_FALL_THROUGH();
884 case VBOX_SHCL_GUEST_FN_NEGOTIATE_CHUNK_SIZE:
885 RT_FALL_THROUGH();
886 case VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT:
887 RT_FALL_THROUGH();
888 case VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT:
889 RT_FALL_THROUGH();
890 case VBOX_SHCL_GUEST_FN_REPORT_FEATURES:
891 RT_FALL_THROUGH();
892 case VBOX_SHCL_GUEST_FN_QUERY_FEATURES:
893 RT_FALL_THROUGH();
894 case VBOX_SHCL_GUEST_FN_MSG_GET:
895 RT_FALL_THROUGH();
896 case VBOX_SHCL_GUEST_FN_REPLY:
897 RT_FALL_THROUGH();
898 case VBOX_SHCL_GUEST_FN_MSG_CANCEL:
899 RT_FALL_THROUGH();
900 case VBOX_SHCL_GUEST_FN_ERROR:
901 RT_FALL_THROUGH();
902 case VBOX_SHCL_GUEST_FN_LIST_OPEN:
903 RT_FALL_THROUGH();
904 case VBOX_SHCL_GUEST_FN_LIST_CLOSE:
905 RT_FALL_THROUGH();
906 case VBOX_SHCL_GUEST_FN_OBJ_OPEN:
907 RT_FALL_THROUGH();
908 case VBOX_SHCL_GUEST_FN_OBJ_CLOSE:
909 fAllowed = fHostToGuest || fGuestToHost;
910 break;
911
912 default:
913 break;
914 }
915
916 LogFlowFunc(("uMsg=%RU32 (%s), uMode=%RU32 -> fAllowed=%RTbool\n", uMsg, ShClGuestMsgToStr(uMsg), uMode, fAllowed));
917 return fAllowed;
918}
919
920/**
921 * Gets a transfer message reply from HGCM service parameters.
922 *
923 * @returns VBox status code.
924 * @param cParms Number of HGCM parameters supplied in \a aParms.
925 * @param aParms Array of HGCM parameters.
926 * @param pReply Where to store the reply.
927 */
928static int shClSvcTransferGetReply(uint32_t cParms, VBOXHGCMSVCPARM aParms[],
929 PSHCLREPLY pReply)
930{
931 int rc;
932
933 if (cParms >= VBOX_SHCL_CPARMS_REPLY_MIN)
934 {
935 /* aParms[0] has the context ID. */
936 rc = HGCMSvcGetU32(&aParms[1], &pReply->uType);
937 if (RT_SUCCESS(rc))
938 rc = HGCMSvcGetU32(&aParms[2], &pReply->rc);
939 if (RT_SUCCESS(rc))
940 rc = HGCMSvcGetPv(&aParms[3], &pReply->pvPayload, &pReply->cbPayload);
941
942 if (RT_SUCCESS(rc))
943 {
944 rc = VERR_INVALID_PARAMETER; /* Play safe. */
945
946 const unsigned idxParm = VBOX_SHCL_CPARMS_REPLY_MIN;
947
948 switch (pReply->uType)
949 {
950 case VBOX_SHCL_TX_REPLYMSGTYPE_TRANSFER_STATUS:
951 {
952 if (cParms > idxParm)
953 rc = HGCMSvcGetU32(&aParms[idxParm], &pReply->u.TransferStatus.uStatus);
954
955 LogFlowFunc(("uTransferStatus=%RU32\n", pReply->u.TransferStatus.uStatus));
956 break;
957 }
958
959 case VBOX_SHCL_TX_REPLYMSGTYPE_LIST_OPEN:
960 {
961 if (cParms > idxParm)
962 rc = HGCMSvcGetU64(&aParms[idxParm], &pReply->u.ListOpen.uHandle);
963
964 LogFlowFunc(("hListOpen=%RU64\n", pReply->u.ListOpen.uHandle));
965 break;
966 }
967
968 case VBOX_SHCL_TX_REPLYMSGTYPE_LIST_CLOSE:
969 {
970 if (cParms > idxParm)
971 rc = HGCMSvcGetU64(&aParms[idxParm], &pReply->u.ListClose.uHandle);
972
973 LogFlowFunc(("hListClose=%RU64\n", pReply->u.ListClose.uHandle));
974 break;
975 }
976
977 case VBOX_SHCL_TX_REPLYMSGTYPE_OBJ_OPEN:
978 {
979 if (cParms > idxParm)
980 rc = HGCMSvcGetU64(&aParms[idxParm], &pReply->u.ObjOpen.uHandle);
981
982 LogFlowFunc(("hObjOpen=%RU64\n", pReply->u.ObjOpen.uHandle));
983 break;
984 }
985
986 case VBOX_SHCL_TX_REPLYMSGTYPE_OBJ_CLOSE:
987 {
988 if (cParms > idxParm)
989 rc = HGCMSvcGetU64(&aParms[idxParm], &pReply->u.ObjClose.uHandle);
990
991 LogFlowFunc(("hObjClose=%RU64\n", pReply->u.ObjClose.uHandle));
992 break;
993 }
994
995 default:
996 rc = VERR_NOT_SUPPORTED;
997 break;
998 }
999 }
1000 }
1001 else
1002 rc = VERR_INVALID_PARAMETER;
1003
1004 LogFlowFuncLeaveRC(rc);
1005 return rc;
1006}
1007
1008/**
1009 * Gets a transfer root list header from HGCM service parameters.
1010 *
1011 * @returns VBox status code.
1012 * @param cParms Number of HGCM parameters supplied in \a aParms.
1013 * @param aParms Array of HGCM parameters.
1014 * @param pRootLstHdr Where to store the transfer root list header on success.
1015 */
1016static int shClSvcTransferGetRootListHdr(uint32_t cParms, VBOXHGCMSVCPARM aParms[],
1017 PSHCLLISTHDR pRootLstHdr)
1018{
1019 int rc;
1020
1021 if (cParms == VBOX_SHCL_CPARMS_ROOT_LIST_HDR_WRITE)
1022 {
1023 rc = HGCMSvcGetU32(&aParms[1], &pRootLstHdr->fFeatures);
1024 if (RT_SUCCESS(rc))
1025 rc = HGCMSvcGetU64(&aParms[2], &pRootLstHdr->cEntries);
1026 }
1027 else
1028 rc = VERR_INVALID_PARAMETER;
1029
1030 LogFlowFuncLeaveRC(rc);
1031 return rc;
1032}
1033
1034/**
1035 * Gets a transfer root list entry from HGCM service parameters.
1036 *
1037 * @returns VBox status code.
1038 * @param cParms Number of HGCM parameters supplied in \a aParms.
1039 * @param aParms Array of HGCM parameters.
1040 * @param pListEntry Where to store the root list entry.
1041 */
1042static int shClSvcTransferGetRootListEntry(uint32_t cParms, VBOXHGCMSVCPARM aParms[],
1043 PSHCLLISTENTRY pListEntry)
1044{
1045 int rc;
1046
1047 if (cParms == VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_WRITE)
1048 {
1049 rc = HGCMSvcGetU32(&aParms[1], &pListEntry->fInfo);
1050 /* Note: aParms[2] contains the entry index, currently being ignored. */
1051 if (RT_SUCCESS(rc))
1052 rc = HGCMSvcGetPv(&aParms[3], (void **)&pListEntry->pszName, &pListEntry->cbName);
1053 if (RT_SUCCESS(rc))
1054 {
1055 uint32_t cbInfo;
1056 rc = HGCMSvcGetU32(&aParms[4], &cbInfo);
1057 if (RT_SUCCESS(rc))
1058 {
1059 rc = HGCMSvcGetPv(&aParms[5], &pListEntry->pvInfo, &pListEntry->cbInfo);
1060 AssertReturn(cbInfo == pListEntry->cbInfo, VERR_INVALID_PARAMETER);
1061 }
1062 }
1063 }
1064 else
1065 rc = VERR_INVALID_PARAMETER;
1066
1067 LogFlowFuncLeaveRC(rc);
1068 return rc;
1069}
1070
1071/**
1072 * Gets a transfer list open request from HGCM service parameters.
1073 *
1074 * @returns VBox status code.
1075 * @param cParms Number of HGCM parameters supplied in \a aParms.
1076 * @param aParms Array of HGCM parameters.
1077 * @param pOpenParms Where to store the open parameters of the request.
1078 */
1079static int shClSvcTransferGetListOpen(uint32_t cParms, VBOXHGCMSVCPARM aParms[],
1080 PSHCLLISTOPENPARMS pOpenParms)
1081{
1082 int rc;
1083
1084 if (cParms == VBOX_SHCL_CPARMS_LIST_OPEN)
1085 {
1086 rc = HGCMSvcGetU32(&aParms[1], &pOpenParms->fList);
1087 if (RT_SUCCESS(rc))
1088 rc = HGCMSvcGetStr(&aParms[2], &pOpenParms->pszFilter, &pOpenParms->cbFilter);
1089 if (RT_SUCCESS(rc))
1090 rc = HGCMSvcGetStr(&aParms[3], &pOpenParms->pszPath, &pOpenParms->cbPath);
1091
1092 /** @todo Some more validation. */
1093 }
1094 else
1095 rc = VERR_INVALID_PARAMETER;
1096
1097 LogFlowFuncLeaveRC(rc);
1098 return rc;
1099}
1100
1101/**
1102 * Sets a transfer list open request to HGCM service parameters.
1103 *
1104 * @returns VBox status code.
1105 * @param cParms Number of HGCM parameters supplied in \a aParms.
1106 * @param aParms Array of HGCM parameters.
1107 * @param idCtx Context ID to use.
1108 * @param pOpenParms List open parameters to set.
1109 */
1110static int shClSvcTransferSetListOpen(uint32_t cParms, VBOXHGCMSVCPARM aParms[],
1111 uint64_t idCtx, PSHCLLISTOPENPARMS pOpenParms)
1112{
1113 int rc;
1114
1115 if (cParms == VBOX_SHCL_CPARMS_LIST_OPEN)
1116 {
1117 HGCMSvcSetU64(&aParms[0], idCtx);
1118 HGCMSvcSetU32(&aParms[1], pOpenParms->fList);
1119 HGCMSvcSetPv (&aParms[2], pOpenParms->pszFilter, pOpenParms->cbFilter);
1120 HGCMSvcSetPv (&aParms[3], pOpenParms->pszPath, pOpenParms->cbPath);
1121 HGCMSvcSetU64(&aParms[4], 0); /* OUT: uHandle */
1122
1123 rc = VINF_SUCCESS;
1124 }
1125 else
1126 rc = VERR_INVALID_PARAMETER;
1127
1128 LogFlowFuncLeaveRC(rc);
1129 return rc;
1130}
1131
1132/**
1133 * Sets a transfer list close request to HGCM service parameters.
1134 *
1135 * @returns VBox status code.
1136 * @param cParms Number of HGCM parameters supplied in \a aParms.
1137 * @param aParms Array of HGCM parameters.
1138 * @param idCtx Context ID to use.
1139 * @param hList Handle of list to close.
1140 */
1141static int shClSvcTransferSetListClose(uint32_t cParms, VBOXHGCMSVCPARM aParms[],
1142 uint64_t idCtx, SHCLLISTHANDLE hList)
1143{
1144 int rc;
1145
1146 if (cParms == VBOX_SHCL_CPARMS_LIST_CLOSE)
1147 {
1148 HGCMSvcSetU64(&aParms[0], idCtx);
1149 HGCMSvcSetU64(&aParms[1], hList);
1150
1151 rc = VINF_SUCCESS;
1152 }
1153 else
1154 rc = VERR_INVALID_PARAMETER;
1155
1156 LogFlowFuncLeaveRC(rc);
1157 return rc;
1158}
1159
1160/**
1161 * Gets a transfer list header from HGCM service parameters.
1162 *
1163 * @returns VBox status code.
1164 * @param cParms Number of HGCM parameters supplied in \a aParms.
1165 * @param aParms Array of HGCM parameters.
1166 * @param phList Where to store the list handle.
1167 * @param pListHdr Where to store the list header.
1168 */
1169static int shClSvcTransferGetListHdr(uint32_t cParms, VBOXHGCMSVCPARM aParms[],
1170 PSHCLLISTHANDLE phList, PSHCLLISTHDR pListHdr)
1171{
1172 int rc;
1173
1174 if (cParms == VBOX_SHCL_CPARMS_LIST_HDR)
1175 {
1176 rc = HGCMSvcGetU64(&aParms[1], phList);
1177 /* Note: Flags (aParms[2]) not used here. */
1178 if (RT_SUCCESS(rc))
1179 rc = HGCMSvcGetU32(&aParms[3], &pListHdr->fFeatures);
1180 if (RT_SUCCESS(rc))
1181 rc = HGCMSvcGetU64(&aParms[4], &pListHdr->cEntries);
1182 if (RT_SUCCESS(rc))
1183 rc = HGCMSvcGetU64(&aParms[5], &pListHdr->cbTotalSize);
1184
1185 if (RT_SUCCESS(rc))
1186 {
1187 /** @todo Validate pvMetaFmt + cbMetaFmt. */
1188 /** @todo Validate header checksum. */
1189 }
1190 }
1191 else
1192 rc = VERR_INVALID_PARAMETER;
1193
1194 LogFlowFuncLeaveRC(rc);
1195 return rc;
1196}
1197
1198/**
1199 * Sets a transfer list header to HGCM service parameters.
1200 *
1201 * @returns VBox status code.
1202 * @param cParms Number of HGCM parameters supplied in \a aParms.
1203 * @param aParms Array of HGCM parameters.
1204 * @param pListHdr Pointer to list header to set.
1205 */
1206static int shClSvcTransferSetListHdr(uint32_t cParms, VBOXHGCMSVCPARM aParms[], PSHCLLISTHDR pListHdr)
1207{
1208 int rc;
1209
1210 if (cParms == VBOX_SHCL_CPARMS_LIST_HDR)
1211 {
1212 /** @todo Set pvMetaFmt + cbMetaFmt. */
1213 /** @todo Calculate header checksum. */
1214
1215 HGCMSvcSetU32(&aParms[3], pListHdr->fFeatures);
1216 HGCMSvcSetU64(&aParms[4], pListHdr->cEntries);
1217 HGCMSvcSetU64(&aParms[5], pListHdr->cbTotalSize);
1218
1219 rc = VINF_SUCCESS;
1220 }
1221 else
1222 rc = VERR_INVALID_PARAMETER;
1223
1224 LogFlowFuncLeaveRC(rc);
1225 return rc;
1226}
1227
1228/**
1229 * Gets a transfer list entry from HGCM service parameters.
1230 *
1231 * @returns VBox status code.
1232 * @param cParms Number of HGCM parameters supplied in \a aParms.
1233 * @param aParms Array of HGCM parameters.
1234 * @param phList Where to store the list handle.
1235 * @param pListEntry Where to store the list entry.
1236 */
1237static int shClSvcTransferGetListEntry(uint32_t cParms, VBOXHGCMSVCPARM aParms[],
1238 PSHCLLISTHANDLE phList, PSHCLLISTENTRY pListEntry)
1239{
1240 int rc;
1241
1242 if (cParms == VBOX_SHCL_CPARMS_LIST_ENTRY)
1243 {
1244 rc = HGCMSvcGetU64(&aParms[1], phList);
1245 if (RT_SUCCESS(rc))
1246 rc = HGCMSvcGetU32(&aParms[2], &pListEntry->fInfo);
1247 if (RT_SUCCESS(rc))
1248 rc = HGCMSvcGetPv(&aParms[3], (void **)&pListEntry->pszName, &pListEntry->cbName);
1249 if (RT_SUCCESS(rc))
1250 {
1251 uint32_t cbInfo;
1252 rc = HGCMSvcGetU32(&aParms[4], &cbInfo);
1253 if (RT_SUCCESS(rc))
1254 {
1255 rc = HGCMSvcGetPv(&aParms[5], &pListEntry->pvInfo, &pListEntry->cbInfo);
1256 AssertReturn(cbInfo == pListEntry->cbInfo, VERR_INVALID_PARAMETER);
1257 }
1258 }
1259
1260 if (RT_SUCCESS(rc))
1261 {
1262 if (!ShClTransferListEntryIsValid(pListEntry))
1263 rc = VERR_INVALID_PARAMETER;
1264 }
1265 }
1266 else
1267 rc = VERR_INVALID_PARAMETER;
1268
1269 LogFlowFuncLeaveRC(rc);
1270 return rc;
1271}
1272
1273/**
1274 * Sets a Shared Clipboard list entry to HGCM service parameters.
1275 *
1276 * @returns VBox status code.
1277 * @param cParms Number of HGCM parameters supplied in \a aParms.
1278 * @param aParms Array of HGCM parameters.
1279 * @param pEntry Pointer list entry to set.
1280 */
1281static int shClSvcTransferSetListEntry(uint32_t cParms, VBOXHGCMSVCPARM aParms[],
1282 PSHCLLISTENTRY pEntry)
1283{
1284 int rc;
1285
1286 /* Sanity. */
1287 AssertReturn(ShClTransferListEntryIsValid(pEntry), VERR_INVALID_PARAMETER);
1288
1289 if (cParms == VBOX_SHCL_CPARMS_LIST_ENTRY)
1290 {
1291 /* Entry name */
1292 void *pvDst = aParms[3].u.pointer.addr;
1293 size_t cbDst = aParms[3].u.pointer.size;
1294 memcpy(pvDst, pEntry->pszName, RT_MIN(pEntry->cbName, cbDst));
1295
1296 /* Info size */
1297 HGCMSvcSetU32(&aParms[4], pEntry->cbInfo);
1298
1299 /* Info data */
1300 pvDst = aParms[5].u.pointer.addr;
1301 cbDst = aParms[5].u.pointer.size;
1302 memcpy(pvDst, pEntry->pvInfo, RT_MIN(pEntry->cbInfo, cbDst));
1303
1304 rc = VINF_SUCCESS;
1305 }
1306 else
1307 rc = VERR_INVALID_PARAMETER;
1308
1309 LogFlowFuncLeaveRC(rc);
1310 return rc;
1311}
1312
1313/**
1314 * Gets a transfer object data chunk from HGCM service parameters.
1315 *
1316 * @returns VBox status code.
1317 * @param cParms Number of HGCM parameters supplied in \a aParms.
1318 * @param aParms Array of HGCM parameters.
1319 * @param pDataChunk Where to store the object data chunk data.
1320 */
1321static int shClSvcTransferGetObjDataChunk(uint32_t cParms, VBOXHGCMSVCPARM aParms[], PSHCLOBJDATACHUNK pDataChunk)
1322{
1323 AssertPtrReturn(aParms, VERR_INVALID_PARAMETER);
1324 AssertPtrReturn(pDataChunk, VERR_INVALID_PARAMETER);
1325
1326 int rc;
1327
1328 if (cParms == VBOX_SHCL_CPARMS_OBJ_WRITE)
1329 {
1330 rc = HGCMSvcGetU64(&aParms[1], &pDataChunk->uHandle);
1331 if (RT_SUCCESS(rc))
1332 {
1333 uint32_t cbToRead;
1334 rc = HGCMSvcGetU32(&aParms[2], &cbToRead);
1335 if (RT_SUCCESS(rc))
1336 {
1337 rc = HGCMSvcGetPv(&aParms[3], &pDataChunk->pvData, &pDataChunk->cbData);
1338 if (RT_SUCCESS(rc))
1339 rc = cbToRead == pDataChunk->cbData ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
1340 }
1341
1342 /** @todo Implement checksum handling. */
1343 }
1344 }
1345 else
1346 rc = VERR_INVALID_PARAMETER;
1347
1348 LogFlowFuncLeaveRC(rc);
1349 return rc;
1350}
1351
1352/**
1353 * Handles a guest reply (VBOX_SHCL_GUEST_FN_REPLY) message.
1354 *
1355 * @returns VBox status code.
1356 * @param pClient Pointer to associated client.
1357 * @param idTransfer Transfer ID supplied from the guest.
1358 * @param cParms Number of function parameters supplied.
1359 * @param aParms Array function parameters supplied.
1360 */
1361static int shClSvcTransferHandleReply(PSHCLCLIENT pClient, SHCLTRANSFERID idTransfer, uint32_t cParms, VBOXHGCMSVCPARM aParms[])
1362{
1363 int rc;
1364
1365 PSHCLTRANSFER pTransfer = NULL;
1366
1367 uint32_t cbReply = sizeof(SHCLREPLY);
1368 PSHCLREPLY pReply = (PSHCLREPLY)RTMemAlloc(cbReply);
1369 if (pReply)
1370 {
1371 rc = shClSvcTransferGetReply(cParms, aParms, pReply);
1372 if (RT_SUCCESS(rc))
1373 {
1374 if ( pReply->uType == VBOX_SHCL_TX_REPLYMSGTYPE_TRANSFER_STATUS
1375 && pReply->u.TransferStatus.uStatus == SHCLTRANSFERSTATUS_REQUESTED)
1376 {
1377 /* SHCLTRANSFERSTATUS_REQUESTED is special, as it doesn't provide a transfer ID. */
1378 }
1379 else /* Everything else needs a valid transfer ID. */
1380 {
1381 pTransfer = ShClTransferCtxGetTransferById(&pClient->Transfers.Ctx, idTransfer);
1382 if (!pTransfer)
1383 {
1384 LogRel2(("Shared Clipboard: Transfer with ID %RU16 not found\n", idTransfer));
1385 rc = VERR_SHCLPB_TRANSFER_ID_NOT_FOUND;
1386 }
1387 }
1388
1389 if (RT_FAILURE(rc))
1390 {
1391 RTMemFree(pReply);
1392 pReply = NULL;
1393
1394 return rc;
1395 }
1396
1397 PSHCLEVENTPAYLOAD pPayload
1398 = (PSHCLEVENTPAYLOAD)RTMemAlloc(sizeof(SHCLEVENTPAYLOAD));
1399 if (pPayload)
1400 {
1401 pPayload->pvData = pReply;
1402 pPayload->cbData = cbReply;
1403
1404 switch (pReply->uType)
1405 {
1406 case VBOX_SHCL_TX_REPLYMSGTYPE_TRANSFER_STATUS:
1407 {
1408 /* SHCLTRANSFERSTATUS_REQUESTED is special, as it doesn't provide a transfer ID. */
1409 if (SHCLTRANSFERSTATUS_REQUESTED == pReply->u.TransferStatus.uStatus)
1410 {
1411 LogRel2(("Shared Clipboard: Guest requested a new host -> guest transfer\n"));
1412 }
1413 else
1414 LogRel2(("Shared Clipboard: Guest reported status %s for transfer %RU32\n",
1415 ShClTransferStatusToStr(pReply->u.TransferStatus.uStatus), idTransfer));
1416
1417 switch (pReply->u.TransferStatus.uStatus)
1418 {
1419 case SHCLTRANSFERSTATUS_REQUESTED: /* Guest requests a H->G transfer. */
1420 {
1421 uint32_t const uMode = ShClSvcGetMode();
1422 if ( uMode == VBOX_SHCL_MODE_HOST_TO_GUEST
1423 || uMode == VBOX_SHCL_MODE_BIDIRECTIONAL)
1424 {
1425 /* We only create (but not initialize) the transfer here. This is the most lightweight form of
1426 * having a pending transfer around. Report back the new transfer ID to the guest then. */
1427 if (pTransfer == NULL) /* Must not exist yet. */
1428 {
1429 rc = ShClSvcTransferCreate(pClient, SHCLTRANSFERDIR_TO_REMOTE, SHCLSOURCE_LOCAL,
1430 NIL_SHCLTRANSFERID /* Creates a new transfer ID */,
1431 &pTransfer);
1432 if (RT_SUCCESS(rc))
1433 {
1434 shClSvcClientLock(pClient);
1435
1436 rc = shClSvcTransferSendStatusAsync(pClient, pTransfer,
1437 SHCLTRANSFERSTATUS_REQUESTED, VINF_SUCCESS,
1438 NULL);
1439 shClSvcClientUnlock(pClient);
1440 }
1441 }
1442 else
1443 rc = VERR_WRONG_ORDER;
1444 }
1445 else
1446 rc = VERR_INVALID_PARAMETER;
1447
1448 break;
1449 }
1450
1451 case SHCLTRANSFERSTATUS_INITIALIZED: /* Guest reports the transfer as being initialized. */
1452 {
1453 switch (ShClTransferGetDir(pTransfer))
1454 {
1455 case SHCLTRANSFERDIR_FROM_REMOTE: /* G->H */
1456 /* Already done locally when creating the transfer. */
1457 break;
1458
1459 case SHCLTRANSFERDIR_TO_REMOTE: /* H->G */
1460 {
1461 /* Initialize the transfer on the host side. */
1462 rc = ShClSvcTransferInit(pClient, pTransfer);
1463 break;
1464 }
1465
1466 default:
1467 AssertFailed();
1468 break;
1469 }
1470
1471 break;
1472 }
1473 case SHCLTRANSFERSTATUS_STARTED: /* Guest has started the transfer on its side. */
1474 {
1475 /* We only need to start for H->G transfers here.
1476 * For G->H transfers we start this as soon as the host clipboard requests data. */
1477 if (ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_TO_REMOTE)
1478 {
1479 /* Start the transfer on the host side. */
1480 rc = ShClSvcTransferStart(pClient, pTransfer);
1481 }
1482 break;
1483 }
1484
1485 case SHCLTRANSFERSTATUS_CANCELED:
1486 RT_FALL_THROUGH();
1487 case SHCLTRANSFERSTATUS_KILLED:
1488 {
1489 LogRel(("Shared Clipboard: Guest has %s transfer %RU32\n",
1490 pReply->u.TransferStatus.uStatus == SHCLTRANSFERSTATUS_CANCELED ? "canceled" : "killed", pTransfer->State.uID));
1491
1492 rc = ShClSvcTransferStop(pClient, pTransfer, false /* fWaitForGuest */);
1493
1494 /* Regardless of whether the guest was able to report back and/or stop the transfer, remove the transfer on the host
1495 * so that we don't risk of having stale transfers here. */
1496 ShClSvcTransferDestroy(pClient, pTransfer);
1497 pTransfer = NULL;
1498 break;
1499 }
1500
1501 case SHCLTRANSFERSTATUS_COMPLETED:
1502 {
1503 LogRel(("Shared Clipboard: Guest has stopped transfer %RU32\n", pTransfer->State.uID));
1504
1505 rc = ShClSvcTransferStop(pClient, pTransfer, false /* fWaitForGuest */);
1506 break;
1507 }
1508
1509 case SHCLTRANSFERSTATUS_ERROR:
1510 {
1511 LogRel(("Shared Clipboard: Guest reported error %Rrc for transfer %RU32\n",
1512 pReply->rc, pTransfer->State.uID));
1513
1514 if (g_ExtState.pfnExtension)
1515 {
1516 SHCLEXTPARMS parms;
1517 RT_ZERO(parms);
1518
1519 parms.u.Error.rc = pReply->rc;
1520 parms.u.Error.pszMsg = RTStrAPrintf2("Guest reported error %Rrc for transfer %RU32", /** @todo Make the error messages more fine-grained based on rc. */
1521 pReply->rc, pTransfer->State.uID);
1522 AssertPtrBreakStmt(parms.u.Error.pszMsg, rc = VERR_NO_MEMORY);
1523
1524 g_ExtState.pfnExtension(g_ExtState.pvExtension, VBOX_CLIPBOARD_EXT_FN_ERROR, &parms, sizeof(parms));
1525
1526 RTStrFree(parms.u.Error.pszMsg);
1527 parms.u.Error.pszMsg = NULL;
1528 }
1529
1530 RT_FALL_THROUGH();
1531 }
1532
1533 default:
1534 {
1535 /* Regardless of whether the guest was able to report back and/or stop the transfer, remove the transfer on the host
1536 * so that we don't risk of having stale transfers here. */
1537 ShClSvcTransferDestroy(pClient, pTransfer);
1538 pTransfer = NULL;
1539 break;
1540 }
1541 }
1542
1543 /* Tell the backend. */
1544 int rc2 = ShClBackendTransferHandleStatusReply(pClient->pBackend, pClient, pTransfer,
1545 SHCLSOURCE_REMOTE, pReply->u.TransferStatus.uStatus,
1546 pReply->rc);
1547 if (RT_SUCCESS(rc))
1548 rc = rc2;
1549
1550 RT_FALL_THROUGH();
1551 }
1552 case VBOX_SHCL_TX_REPLYMSGTYPE_LIST_OPEN:
1553 RT_FALL_THROUGH();
1554 case VBOX_SHCL_TX_REPLYMSGTYPE_LIST_CLOSE:
1555 RT_FALL_THROUGH();
1556 case VBOX_SHCL_TX_REPLYMSGTYPE_OBJ_OPEN:
1557 RT_FALL_THROUGH();
1558 case VBOX_SHCL_TX_REPLYMSGTYPE_OBJ_CLOSE:
1559 {
1560 uint64_t uCID;
1561 rc = HGCMSvcGetU64(&aParms[0], &uCID);
1562 if (RT_SUCCESS(rc))
1563 {
1564 const PSHCLEVENT pEvent
1565 = ShClEventSourceGetFromId(&pClient->EventSrc, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID));
1566 if (pEvent)
1567 {
1568 LogFlowFunc(("uCID=%RU64 -> idEvent=%RU32\n", uCID, pEvent->idEvent));
1569
1570 rc = ShClEventSignal(pEvent, pPayload);
1571 }
1572 /** @todo Silently skip? */
1573 }
1574 break;
1575 }
1576
1577 default:
1578 rc = VERR_NOT_FOUND;
1579 break;
1580 }
1581
1582 if (RT_FAILURE(rc))
1583 {
1584 if (pPayload)
1585 RTMemFree(pPayload);
1586 }
1587 }
1588 else
1589 rc = VERR_NO_MEMORY;
1590 }
1591 }
1592 else
1593 rc = VERR_NO_MEMORY;
1594
1595 if (RT_FAILURE(rc))
1596 {
1597 if (pReply)
1598 RTMemFree(pReply);
1599 }
1600
1601 LogFlowFuncLeaveRC(rc);
1602 return rc;
1603}
1604
1605/**
1606 * Transfer client (guest) handler for the Shared Clipboard host service.
1607 *
1608 * @returns VBox status code, or VINF_HGCM_ASYNC_EXECUTE if returning to the client will be deferred.
1609 * @param pClient Pointer to associated client.
1610 * @param callHandle The client's call handle of this call.
1611 * @param u32Function Function number being called.
1612 * @param cParms Number of function parameters supplied.
1613 * @param aParms Array function parameters supplied.
1614 * @param tsArrival Timestamp of arrival.
1615 */
1616int shClSvcTransferHandler(PSHCLCLIENT pClient,
1617 VBOXHGCMCALLHANDLE callHandle,
1618 uint32_t u32Function,
1619 uint32_t cParms,
1620 VBOXHGCMSVCPARM aParms[],
1621 uint64_t tsArrival)
1622{
1623 RT_NOREF(callHandle, aParms, tsArrival);
1624
1625 LogFlowFunc(("uClient=%RU32, u32Function=%RU32 (%s), cParms=%RU32, g_ExtState.pfnExtension=%p\n",
1626 pClient->State.uClientID, u32Function, ShClGuestMsgToStr(u32Function), cParms, g_ExtState.pfnExtension));
1627
1628 /* Check if we've the right mode set. */
1629 if (!shClSvcTransferMsgIsAllowed(ShClSvcGetMode(), u32Function))
1630 {
1631 LogFunc(("Wrong clipboard mode, denying access\n"));
1632 return VERR_ACCESS_DENIED;
1633 }
1634
1635 int rc = VERR_INVALID_PARAMETER; /* Play safe by default. */
1636
1637 if (cParms < 1)
1638 return rc;
1639 ASSERT_GUEST_RETURN(aParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
1640
1641 uint64_t uCID = 0; /* Context ID */
1642 rc = HGCMSvcGetU64(&aParms[0], &uCID);
1643 if (RT_FAILURE(rc))
1644 return rc;
1645
1646 const SHCLTRANSFERID idTransfer = VBOX_SHCL_CONTEXTID_GET_TRANSFER(uCID);
1647
1648 /*
1649 * Pre-check: For certain messages we need to make sure that a (right) transfer is present.
1650 */
1651 PSHCLTRANSFER pTransfer = NULL;
1652 switch (u32Function)
1653 {
1654 case VBOX_SHCL_GUEST_FN_REPLY:
1655 /* Function does its own lookup. */
1656 break;
1657
1658 default:
1659 {
1660 pTransfer = ShClTransferCtxGetTransferById(&pClient->Transfers.Ctx, idTransfer);
1661 if (!pTransfer)
1662 {
1663 LogRel(("Shared Clipboard: Transfer with ID %RU16 not found\n", idTransfer));
1664 rc = VERR_SHCLPB_TRANSFER_ID_NOT_FOUND;
1665 }
1666 break;
1667 }
1668 }
1669
1670 if (RT_FAILURE(rc))
1671 return rc;
1672
1673 rc = VERR_INVALID_PARAMETER; /* Play safe. */
1674
1675 switch (u32Function)
1676 {
1677 case VBOX_SHCL_GUEST_FN_REPLY:
1678 {
1679 rc = shClSvcTransferHandleReply(pClient, idTransfer, cParms, aParms);
1680 break;
1681 }
1682
1683 case VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_READ:
1684 {
1685 if (cParms != VBOX_SHCL_CPARMS_ROOT_LIST_HDR_READ)
1686 break;
1687
1688 ASSERT_GUEST_RETURN(aParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* Features */
1689 ASSERT_GUEST_RETURN(aParms[2].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE); /* # Entries */
1690
1691 SHCLLISTHDR rootListHdr;
1692 RT_ZERO(rootListHdr);
1693
1694 rootListHdr.cEntries = ShClTransferRootsCount(pTransfer);
1695 /** @todo BUGBUG What about the features? */
1696
1697 HGCMSvcSetU64(&aParms[0], 0 /* Context ID */);
1698 HGCMSvcSetU32(&aParms[1], rootListHdr.fFeatures);
1699 HGCMSvcSetU64(&aParms[2], rootListHdr.cEntries);
1700
1701 rc = VINF_SUCCESS;
1702 break;
1703 }
1704
1705 case VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_WRITE:
1706 {
1707 SHCLLISTHDR lstHdr;
1708 rc = shClSvcTransferGetRootListHdr(cParms, aParms, &lstHdr);
1709 if (RT_SUCCESS(rc))
1710 {
1711 void *pvData = ShClTransferListHdrDup(&lstHdr);
1712 uint32_t cbData = sizeof(SHCLLISTHDR);
1713
1714 const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pTransfer->Events, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID));
1715 if (pEvent)
1716 {
1717 PSHCLEVENTPAYLOAD pPayload;
1718 rc = ShClPayloadAlloc(pEvent->idEvent, pvData, cbData, &pPayload);
1719 if (RT_SUCCESS(rc))
1720 {
1721 rc = ShClEventSignal(pEvent, pPayload);
1722 if (RT_FAILURE(rc))
1723 ShClPayloadFree(pPayload);
1724 }
1725 }
1726 else
1727 rc = VERR_SHCLPB_EVENT_ID_NOT_FOUND;
1728 }
1729 break;
1730 }
1731
1732 case VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_READ:
1733 {
1734 if (cParms != VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_READ)
1735 break;
1736
1737 ASSERT_GUEST_RETURN(aParms[1].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* Info flags */
1738 ASSERT_GUEST_RETURN(aParms[2].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE); /* Entry index # */
1739 ASSERT_GUEST_RETURN(aParms[3].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE); /* Entry name */
1740 ASSERT_GUEST_RETURN(aParms[4].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* Info size */
1741 ASSERT_GUEST_RETURN(aParms[5].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE); /* Info data */
1742
1743 uint32_t fInfo;
1744 rc = HGCMSvcGetU32(&aParms[1], &fInfo);
1745 AssertRCBreak(rc);
1746
1747 ASSERT_GUEST_RETURN(fInfo & VBOX_SHCL_INFO_F_FSOBJINFO, VERR_WRONG_PARAMETER_TYPE); /* Validate info flags. */
1748
1749 uint64_t uIdx;
1750 rc = HGCMSvcGetU64(&aParms[2], &uIdx);
1751 AssertRCBreak(rc);
1752
1753 PCSHCLLISTENTRY pEntry = ShClTransferRootsEntryGet(pTransfer, uIdx);
1754 if (pEntry)
1755 {
1756 /* Entry name */
1757 void *pvDst = aParms[3].u.pointer.addr;
1758 size_t cbDst = aParms[3].u.pointer.size;
1759 memcpy(pvDst, pEntry->pszName, RT_MIN(pEntry->cbName, cbDst));
1760
1761 /* Info size */
1762 HGCMSvcSetU32(&aParms[4], pEntry->cbInfo);
1763
1764 /* Info data */
1765 pvDst = aParms[5].u.pointer.addr;
1766 cbDst = aParms[5].u.pointer.size;
1767 memcpy(pvDst, pEntry->pvInfo, RT_MIN(pEntry->cbInfo, cbDst));
1768 }
1769 else
1770 rc = VERR_NOT_FOUND;
1771
1772 break;
1773 }
1774
1775 case VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_WRITE:
1776 {
1777 SHCLLISTENTRY lstEntry;
1778 rc = shClSvcTransferGetRootListEntry(cParms, aParms, &lstEntry);
1779 if (RT_SUCCESS(rc))
1780 {
1781 void *pvData = ShClTransferListEntryDup(&lstEntry);
1782 uint32_t cbData = sizeof(SHCLLISTENTRY);
1783
1784 const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pTransfer->Events, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID));
1785 if (pEvent)
1786 {
1787 PSHCLEVENTPAYLOAD pPayload;
1788 rc = ShClPayloadAlloc(pEvent->idEvent, pvData, cbData, &pPayload);
1789 if (RT_SUCCESS(rc))
1790 {
1791 rc = ShClEventSignal(pEvent, pPayload);
1792 if (RT_FAILURE(rc))
1793 ShClPayloadFree(pPayload);
1794 }
1795 }
1796 else
1797 rc = VERR_SHCLPB_EVENT_ID_NOT_FOUND;
1798 }
1799 break;
1800 }
1801
1802 case VBOX_SHCL_GUEST_FN_LIST_OPEN:
1803 {
1804 if (cParms != VBOX_SHCL_CPARMS_LIST_OPEN)
1805 break;
1806
1807 SHCLLISTOPENPARMS listOpenParms;
1808 rc = shClSvcTransferGetListOpen(cParms, aParms, &listOpenParms);
1809 if (RT_SUCCESS(rc))
1810 {
1811 SHCLLISTHANDLE hList;
1812 rc = ShClTransferListOpen(pTransfer, &listOpenParms, &hList);
1813 if (RT_SUCCESS(rc))
1814 {
1815 /* Return list handle. */
1816 HGCMSvcSetU64(&aParms[4], hList);
1817 }
1818 }
1819 break;
1820 }
1821
1822 case VBOX_SHCL_GUEST_FN_LIST_CLOSE:
1823 {
1824 if (cParms != VBOX_SHCL_CPARMS_LIST_CLOSE)
1825 break;
1826
1827 SHCLLISTHANDLE hList;
1828 rc = HGCMSvcGetU64(&aParms[1], &hList);
1829 if (RT_SUCCESS(rc))
1830 {
1831 rc = ShClTransferListClose(pTransfer, hList);
1832 }
1833 break;
1834 }
1835
1836 case VBOX_SHCL_GUEST_FN_LIST_HDR_READ:
1837 {
1838 if (cParms != VBOX_SHCL_CPARMS_LIST_HDR)
1839 break;
1840
1841 SHCLLISTHANDLE hList;
1842 rc = HGCMSvcGetU64(&aParms[1], &hList); /* Get list handle. */
1843 if (RT_SUCCESS(rc))
1844 {
1845 SHCLLISTHDR hdrList;
1846 rc = ShClTransferListGetHeader(pTransfer, hList, &hdrList);
1847 if (RT_SUCCESS(rc))
1848 rc = shClSvcTransferSetListHdr(cParms, aParms, &hdrList);
1849 }
1850 break;
1851 }
1852
1853 case VBOX_SHCL_GUEST_FN_LIST_HDR_WRITE:
1854 {
1855 SHCLLISTHDR hdrList;
1856 rc = ShClTransferListHdrInit(&hdrList);
1857 if (RT_SUCCESS(rc))
1858 {
1859 SHCLLISTHANDLE hList;
1860 rc = shClSvcTransferGetListHdr(cParms, aParms, &hList, &hdrList);
1861 if (RT_SUCCESS(rc))
1862 {
1863 void *pvData = ShClTransferListHdrDup(&hdrList);
1864 uint32_t cbData = sizeof(SHCLLISTHDR);
1865
1866 const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pTransfer->Events, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID));
1867 if (pEvent)
1868 {
1869 PSHCLEVENTPAYLOAD pPayload;
1870 rc = ShClPayloadAlloc(pEvent->idEvent, pvData, cbData, &pPayload);
1871 if (RT_SUCCESS(rc))
1872 {
1873 rc = ShClEventSignal(pEvent, pPayload);
1874 if (RT_FAILURE(rc))
1875 ShClPayloadFree(pPayload);
1876 }
1877 }
1878 else
1879 rc = VERR_SHCLPB_EVENT_ID_NOT_FOUND;
1880 }
1881 }
1882 break;
1883 }
1884
1885 case VBOX_SHCL_GUEST_FN_LIST_ENTRY_READ:
1886 {
1887 if (cParms != VBOX_SHCL_CPARMS_LIST_ENTRY)
1888 break;
1889
1890 SHCLLISTHANDLE hList;
1891 rc = HGCMSvcGetU64(&aParms[1], &hList); /* Get list handle. */
1892 if (RT_SUCCESS(rc))
1893 {
1894 SHCLLISTENTRY entryList;
1895 rc = ShClTransferListEntryInit(&entryList);
1896 if (RT_SUCCESS(rc))
1897 {
1898 rc = ShClTransferListRead(pTransfer, hList, &entryList);
1899 if (RT_SUCCESS(rc))
1900 rc = shClSvcTransferSetListEntry(cParms, aParms, &entryList);
1901 }
1902 }
1903 break;
1904 }
1905
1906 case VBOX_SHCL_GUEST_FN_LIST_ENTRY_WRITE:
1907 {
1908 SHCLLISTENTRY entryList;
1909 rc = ShClTransferListEntryInit(&entryList);
1910 if (RT_SUCCESS(rc))
1911 {
1912 SHCLLISTHANDLE hList;
1913 rc = shClSvcTransferGetListEntry(cParms, aParms, &hList, &entryList);
1914 if (RT_SUCCESS(rc))
1915 {
1916 void *pvData = ShClTransferListEntryDup(&entryList);
1917 uint32_t cbData = sizeof(SHCLLISTENTRY);
1918
1919 const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pTransfer->Events, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID));
1920 if (pEvent)
1921 {
1922 PSHCLEVENTPAYLOAD pPayload;
1923 rc = ShClPayloadAlloc(pEvent->idEvent, pvData, cbData, &pPayload);
1924 if (RT_SUCCESS(rc))
1925 {
1926 rc = ShClEventSignal(pEvent, pPayload);
1927 if (RT_FAILURE(rc))
1928 ShClPayloadFree(pPayload);
1929 }
1930 }
1931 else
1932 rc = VERR_SHCLPB_EVENT_ID_NOT_FOUND;
1933 }
1934 }
1935 break;
1936 }
1937
1938 case VBOX_SHCL_GUEST_FN_OBJ_OPEN:
1939 {
1940 ASSERT_GUEST_STMT_BREAK(cParms == VBOX_SHCL_CPARMS_OBJ_OPEN, VERR_WRONG_PARAMETER_COUNT);
1941
1942 SHCLOBJOPENCREATEPARMS openCreateParms;
1943 RT_ZERO(openCreateParms);
1944
1945 /* aParms[1] will return the object handle on success; see below. */
1946 rc = HGCMSvcGetStr(&aParms[2], &openCreateParms.pszPath, &openCreateParms.cbPath);
1947 if (RT_SUCCESS(rc))
1948 rc = HGCMSvcGetU32(&aParms[3], &openCreateParms.fCreate);
1949
1950 if (RT_SUCCESS(rc))
1951 {
1952 SHCLOBJHANDLE hObj;
1953 rc = ShClTransferObjOpen(pTransfer, &openCreateParms, &hObj);
1954 if (RT_SUCCESS(rc))
1955 {
1956 LogFlowFunc(("hObj=%RU64\n", hObj));
1957
1958 HGCMSvcSetU64(&aParms[1], hObj);
1959 }
1960 }
1961 break;
1962 }
1963
1964 case VBOX_SHCL_GUEST_FN_OBJ_CLOSE:
1965 {
1966 if (cParms != VBOX_SHCL_CPARMS_OBJ_CLOSE)
1967 break;
1968
1969 SHCLOBJHANDLE hObj;
1970 rc = HGCMSvcGetU64(&aParms[1], &hObj); /* Get object handle. */
1971 if (RT_SUCCESS(rc))
1972 rc = ShClTransferObjClose(pTransfer, hObj);
1973 break;
1974 }
1975
1976 case VBOX_SHCL_GUEST_FN_OBJ_READ:
1977 {
1978 if (cParms != VBOX_SHCL_CPARMS_OBJ_READ)
1979 break;
1980
1981 ASSERT_GUEST_RETURN(aParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE); /* Object handle */
1982 ASSERT_GUEST_RETURN(aParms[2].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* Bytes to read */
1983 ASSERT_GUEST_RETURN(aParms[3].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE); /* Data buffer */
1984 ASSERT_GUEST_RETURN(aParms[4].type == VBOX_HGCM_SVC_PARM_32BIT, VERR_WRONG_PARAMETER_TYPE); /* Checksum data size */
1985 ASSERT_GUEST_RETURN(aParms[5].type == VBOX_HGCM_SVC_PARM_PTR, VERR_WRONG_PARAMETER_TYPE); /* Checksum data buffer*/
1986
1987 SHCLOBJHANDLE hObj;
1988 rc = HGCMSvcGetU64(&aParms[1], &hObj); /* Get object handle. */
1989 AssertRCBreak(rc);
1990
1991 uint32_t cbToRead = 0;
1992 rc = HGCMSvcGetU32(&aParms[2], &cbToRead);
1993 AssertRCBreak(rc);
1994
1995 void *pvBuf = NULL;
1996 uint32_t cbBuf = 0;
1997 rc = HGCMSvcGetPv(&aParms[3], &pvBuf, &cbBuf);
1998 AssertRCBreak(rc);
1999
2000 LogFlowFunc(("hObj=%RU64, cbBuf=%RU32, cbToRead=%RU32, rc=%Rrc\n", hObj, cbBuf, cbToRead, rc));
2001
2002 if ( RT_SUCCESS(rc)
2003 && ( !cbBuf
2004 || !cbToRead
2005 || cbBuf < cbToRead
2006 )
2007 )
2008 {
2009 rc = VERR_INVALID_PARAMETER;
2010 }
2011
2012 if (RT_SUCCESS(rc))
2013 {
2014 uint32_t cbRead;
2015 rc = ShClTransferObjRead(pTransfer, hObj, pvBuf, cbToRead, 0 /* fFlags */, &cbRead);
2016 if (RT_SUCCESS(rc))
2017 {
2018 HGCMSvcSetU32(&aParms[2], cbRead);
2019
2020 /** @todo Implement checksum support. */
2021 }
2022 }
2023 break;
2024 }
2025
2026 case VBOX_SHCL_GUEST_FN_OBJ_WRITE:
2027 {
2028 SHCLOBJDATACHUNK dataChunk;
2029
2030 rc = shClSvcTransferGetObjDataChunk(cParms, aParms, &dataChunk);
2031 if (RT_SUCCESS(rc))
2032 {
2033 void *pvData = ShClTransferObjDataChunkDup(&dataChunk);
2034 uint32_t cbData = sizeof(SHCLOBJDATACHUNK);
2035
2036 const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pTransfer->Events, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID));
2037 if (pEvent)
2038 {
2039 PSHCLEVENTPAYLOAD pPayload;
2040 rc = ShClPayloadAlloc(pEvent->idEvent, pvData, cbData, &pPayload);
2041 if (RT_SUCCESS(rc))
2042 {
2043 rc = ShClEventSignal(pEvent, pPayload);
2044 if (RT_FAILURE(rc))
2045 ShClPayloadFree(pPayload);
2046 }
2047 }
2048 else
2049 rc = VERR_SHCLPB_EVENT_ID_NOT_FOUND;
2050 }
2051
2052 break;
2053 }
2054
2055 default:
2056 rc = VERR_NOT_IMPLEMENTED;
2057 break;
2058 }
2059
2060 /* If anything wrong has happened, make sure to unregister the transfer again (if not done already) and tell the guest. */
2061 if ( RT_FAILURE(rc)
2062 && pTransfer)
2063 {
2064 shClSvcClientLock(pClient);
2065
2066 /* Let the guest know. */
2067 int rc2 = shClSvcTransferSendStatusAsync(pClient, pTransfer,
2068 SHCLTRANSFERSTATUS_ERROR, rc, NULL /* ppEvent */);
2069 AssertRC(rc2);
2070
2071 shClSvcClientUnlock(pClient);
2072
2073 ShClSvcTransferDestroy(pClient, pTransfer);
2074 }
2075
2076 LogFlowFunc(("[Client %RU32] Returning rc=%Rrc\n", pClient->State.uClientID, rc));
2077 return rc;
2078}
2079
2080/**
2081 * Transfer host handler for the Shared Clipboard host service.
2082 *
2083 * @returns VBox status code.
2084 * @param u32Function Function number being called.
2085 * @param cParms Number of function parameters supplied.
2086 * @param aParms Array function parameters supplied.
2087 */
2088int shClSvcTransferHostHandler(uint32_t u32Function,
2089 uint32_t cParms,
2090 VBOXHGCMSVCPARM aParms[])
2091{
2092 RT_NOREF(cParms, aParms);
2093
2094 int rc = VERR_NOT_IMPLEMENTED; /* Play safe. */
2095
2096 switch (u32Function)
2097 {
2098 case VBOX_SHCL_HOST_FN_CANCEL: /** @todo BUGBUG Implement this. */
2099 break;
2100
2101 case VBOX_SHCL_HOST_FN_ERROR: /** @todo BUGBUG Implement this. */
2102 break;
2103
2104 default:
2105 break;
2106
2107 }
2108
2109 LogFlowFuncLeaveRC(rc);
2110 return rc;
2111}
2112
2113int shClSvcTransferHostMsgHandler(PSHCLCLIENT pClient, PSHCLCLIENTMSG pMsg)
2114{
2115 RT_NOREF(pClient);
2116
2117 int rc;
2118
2119 switch (pMsg->idMsg)
2120 {
2121 default:
2122 rc = VINF_SUCCESS;
2123 break;
2124 }
2125
2126 LogFlowFuncLeaveRC(rc);
2127 return rc;
2128}
2129
2130/**
2131 * Reports a transfer status to the guest.
2132 *
2133 * @returns VBox status code.
2134 * @param pClient Client that owns the transfer.
2135 * @param idTransfer Transfer ID to report status for.
2136 * @param enmDir Transfer direction to report status for.
2137 * @param uStatus Status to report.
2138 * @param rcTransfer Result code to report. Optional and depending on status.
2139 * @param ppEvent Where to return the wait event on success. Optional.
2140 * Must be released by the caller with ShClEventRelease().
2141 *
2142 * @note Caller must enter the client's critical section.
2143 */
2144static int shClSvcTransferSendStatusExAsync(PSHCLCLIENT pClient, SHCLTRANSFERID idTransfer, SHCLTRANSFERDIR enmDir, SHCLTRANSFERSTATUS uStatus,
2145 int rcTransfer, PSHCLEVENT *ppEvent)
2146{
2147 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
2148 AssertReturn(idTransfer != NIL_SHCLTRANSFERID, VERR_INVALID_PARAMETER);
2149 /* ppEvent is optional. */
2150
2151 Assert(RTCritSectIsOwner(&pClient->CritSect));
2152
2153 PSHCLCLIENTMSG pMsgReadData = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_STATUS,
2154 VBOX_SHCL_CPARMS_TRANSFER_STATUS);
2155 if (!pMsgReadData)
2156 return VERR_NO_MEMORY;
2157
2158 PSHCLEVENT pEvent;
2159 int rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent);
2160 if (RT_SUCCESS(rc))
2161 {
2162 HGCMSvcSetU64(&pMsgReadData->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, idTransfer, pEvent->idEvent));
2163 HGCMSvcSetU32(&pMsgReadData->aParms[1], enmDir);
2164 HGCMSvcSetU32(&pMsgReadData->aParms[2], uStatus);
2165 HGCMSvcSetU32(&pMsgReadData->aParms[3], (uint32_t)rcTransfer); /** @todo uint32_t vs. int. */
2166 HGCMSvcSetU32(&pMsgReadData->aParms[4], 0 /* fFlags, unused */);
2167
2168 shClSvcMsgAdd(pClient, pMsgReadData, true /* fAppend */);
2169
2170 rc = shClSvcClientWakeup(pClient);
2171 if (RT_SUCCESS(rc))
2172 {
2173 LogRel2(("Shared Clipboard: Reported status %s (rc=%Rrc) of transfer %RU32 to guest\n",
2174 ShClTransferStatusToStr(uStatus), rcTransfer, idTransfer));
2175
2176 if (ppEvent)
2177 {
2178 *ppEvent = pEvent; /* Takes ownership. */
2179 }
2180 else /* If event is not consumed by the caller, release the event again. */
2181 ShClEventRelease(pEvent);
2182 }
2183 else
2184 ShClEventRelease(pEvent);
2185 }
2186 else
2187 rc = VERR_SHCLPB_MAX_EVENTS_REACHED;
2188
2189 if (RT_FAILURE(rc))
2190 LogRel(("Shared Clipboard: Reporting status %s (%Rrc) for transfer %RU32 to guest failed with %Rrc\n",
2191 ShClTransferStatusToStr(uStatus), rcTransfer, idTransfer, rc));
2192
2193 LogFlowFuncLeaveRC(rc);
2194 return rc;
2195}
2196
2197/**
2198 * Reports a transfer status to the guest, internal version.
2199 *
2200 * @returns VBox status code.
2201 * @param pClient Client that owns the transfer.
2202 * @param pTransfer Transfer to report status for.
2203 * @param uStatus Status to report.
2204 * @param rcTransfer Result code to report. Optional and depending on status.
2205 * @param ppEvent Where to return the wait event on success. Optional.
2206 * Must be released by the caller with ShClEventRelease().
2207 *
2208 * @note Caller must enter the client's critical section.
2209 */
2210static int shClSvcTransferSendStatusAsync(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, SHCLTRANSFERSTATUS uStatus,
2211 int rcTransfer, PSHCLEVENT *ppEvent)
2212{
2213 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
2214 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2215 /* ppEvent is optional. */
2216
2217 return shClSvcTransferSendStatusExAsync(pClient, ShClTransferGetID(pTransfer), ShClTransferGetDir(pTransfer),
2218 uStatus, rcTransfer, ppEvent);
2219}
2220
2221/**
2222 * Reports a transfer status to the guest.
2223 *
2224 * @returns VBox status code.
2225 * @param pClient Client that owns the transfer.
2226 * @param pTransfer Transfer to report status for.
2227 * @param uStatus Status to report.
2228 * @param rcTransfer Result code to report. Optional and depending on status.
2229 * @param ppEvent Where to return the wait event on success. Optional.
2230 * Must be released by the caller with ShClEventRelease().
2231 */
2232int ShClSvcTransferSendStatusAsync(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, SHCLTRANSFERSTATUS uStatus,
2233 int rcTransfer, PSHCLEVENT *ppEvent)
2234{
2235 return shClSvcTransferSendStatusAsync(pClient, pTransfer, uStatus, rcTransfer, ppEvent);
2236}
2237
2238/**
2239 * Cleans up (unregisters and destroys) all transfers not in started state (anymore).
2240 *
2241 * @param pClient Client to clean up transfers for.
2242 *
2243 * @note Caller needs to take the critical section.
2244 */
2245static void shClSvcTransferCleanupAllUnused(PSHCLCLIENT pClient)
2246{
2247 Assert(RTCritSectIsOwner(&pClient->CritSect));
2248
2249 LogFlowFuncEnter();
2250
2251 PSHCLTRANSFERCTX pTxCtx = &pClient->Transfers.Ctx;
2252
2253 PSHCLTRANSFER pTransfer, pTransferNext;
2254 RTListForEachSafe(&pTxCtx->List, pTransfer, pTransferNext, SHCLTRANSFER, Node)
2255 {
2256 SHCLTRANSFERSTATUS const enmSts = ShClTransferGetStatus(pTransfer);
2257 if (enmSts != SHCLTRANSFERSTATUS_STARTED)
2258 {
2259 /* Let the guest know. */
2260 int rc2 = shClSvcTransferSendStatusAsync(pClient, pTransfer,
2261 SHCLTRANSFERSTATUS_UNINITIALIZED, VINF_SUCCESS, NULL /* ppEvent */);
2262 AssertRC(rc2);
2263
2264 ShClTransferCtxUnregisterById(pTxCtx, pTransfer->State.uID);
2265
2266 ShClTransferDestroy(pTransfer);
2267
2268 RTMemFree(pTransfer);
2269 pTransfer = NULL;
2270 }
2271 }
2272}
2273
2274/**
2275 * Creates a new transfer on the host.
2276 *
2277 * @returns VBox status code.
2278 * @param pClient Client that owns the transfer.
2279 * @param enmDir Transfer direction to create.
2280 * @param enmSource Transfer source to create.
2281 * @param idTransfer Transfer ID to use for creation.
2282 * If set to NIL_SHCLTRANSFERID, a new transfer ID will be created.
2283 * @param ppTransfer Where to return the created transfer on success. Optional and can be NULL.
2284 */
2285int ShClSvcTransferCreate(PSHCLCLIENT pClient, SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource, SHCLTRANSFERID idTransfer, PSHCLTRANSFER *ppTransfer)
2286{
2287 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
2288 /* ppTransfer is optional. */
2289
2290 LogFlowFuncEnter();
2291
2292 shClSvcClientLock(pClient);
2293
2294 /* When creating a new transfer, this is a good time to clean up old stuff we don't need anymore. */
2295 shClSvcTransferCleanupAllUnused(pClient);
2296
2297 PSHCLTRANSFER pTransfer;
2298 int rc = ShClTransferCreate(enmDir, enmSource, &pClient->Transfers.Callbacks, &pTransfer);
2299 if (RT_SUCCESS(rc))
2300 {
2301 if (idTransfer == NIL_SHCLTRANSFERID)
2302 rc = ShClTransferCtxRegister(&pClient->Transfers.Ctx, pTransfer, &idTransfer);
2303 else
2304 rc = ShClTransferCtxRegisterById(&pClient->Transfers.Ctx, pTransfer, idTransfer);
2305 if (RT_SUCCESS(rc))
2306 {
2307 if (ppTransfer)
2308 *ppTransfer = pTransfer;
2309 }
2310 }
2311
2312 shClSvcClientUnlock(pClient);
2313
2314 if (RT_FAILURE(rc))
2315 {
2316 ShClTransferDestroy(pTransfer);
2317
2318 RTMemFree(pTransfer);
2319 pTransfer = NULL;
2320 }
2321
2322 if (RT_FAILURE(rc))
2323 LogRel(("Shared Clipboard: Creating transfer failed with %Rrc\n", rc));
2324
2325 LogFlowFuncLeaveRC(rc);
2326 return rc;
2327}
2328
2329/**
2330 * Destroys a transfer on the host.
2331 *
2332 * @param pClient Client to destroy transfer for.
2333 * @param pTransfer Transfer to destroy.
2334 * The pointer will be invalid after return.
2335 */
2336void ShClSvcTransferDestroy(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
2337{
2338 if (!pTransfer)
2339 return;
2340
2341 LogFlowFuncEnter();
2342
2343 shClSvcClientLock(pClient);
2344
2345 PSHCLTRANSFERCTX pTxCtx = &pClient->Transfers.Ctx;
2346
2347 ShClTransferCtxUnregisterById(pTxCtx, pTransfer->State.uID);
2348
2349 /* Make sure to let the guest know. */
2350 int rc = shClSvcTransferSendStatusAsync(pClient, pTransfer,
2351 SHCLTRANSFERSTATUS_UNINITIALIZED, VINF_SUCCESS, NULL /* ppEvent */);
2352 AssertRC(rc);
2353
2354 ShClTransferDestroy(pTransfer);
2355
2356 RTMemFree(pTransfer);
2357 pTransfer = NULL;
2358
2359 shClSvcClientUnlock(pClient);
2360
2361 LogFlowFuncLeave();
2362}
2363
2364/**
2365 * Initializes a (created) transfer on the host.
2366 *
2367 * @returns VBox status code.
2368 * @param pClient Client that owns the transfer.
2369 * @param pTransfer Transfer to initialize.
2370 */
2371int ShClSvcTransferInit(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
2372{
2373 AssertPtrReturn(pClient, VERR_INVALID_POINTER);
2374
2375 LogFlowFuncEnter();
2376
2377 shClSvcClientLock(pClient);
2378
2379 Assert(ShClTransferGetStatus(pTransfer) == SHCLTRANSFERSTATUS_NONE);
2380
2381 PSHCLTRANSFERCTX pTxCtx = &pClient->Transfers.Ctx;
2382
2383 int rc;
2384
2385 if (!ShClTransferCtxIsMaximumReached(pTxCtx))
2386 {
2387 SHCLTRANSFERDIR const enmDir = ShClTransferGetDir(pTransfer);
2388
2389 LogRel2(("Shared Clipboard: Initializing %s transfer ...\n",
2390 enmDir == SHCLTRANSFERDIR_FROM_REMOTE ? "guest -> host" : "host -> guest"));
2391
2392 rc = ShClTransferInit(pTransfer);
2393 if (RT_SUCCESS(rc))
2394 {
2395 /* Sanity: Make sure that the transfer we're gonna report as INITIALIZED to the guest
2396 * actually has some root entries set, as the guest can query for those at any time then. */
2397 if (enmDir == SHCLTRANSFERDIR_TO_REMOTE)
2398 AssertMsgStmt(ShClTransferRootsCount(pTransfer), ("Transfer has no root entries set\n"), rc = VERR_WRONG_ORDER);
2399 }
2400 }
2401 else
2402 rc = VERR_SHCLPB_MAX_TRANSFERS_REACHED;
2403
2404 /* Tell the guest the outcome. */
2405 int rc2 = shClSvcTransferSendStatusAsync(pClient, pTransfer,
2406 RT_SUCCESS(rc)
2407 ? SHCLTRANSFERSTATUS_INITIALIZED : SHCLTRANSFERSTATUS_ERROR, rc,
2408 NULL /* ppEvent */);
2409 if (RT_SUCCESS(rc))
2410 rc2 = rc;
2411
2412 if (RT_FAILURE(rc))
2413 LogRel(("Shared Clipboard: Initializing transfer failed with %Rrc\n", rc));
2414
2415 shClSvcClientUnlock(pClient);
2416
2417 LogFlowFuncLeaveRC(rc);
2418 return rc;
2419}
2420
2421/**
2422 * Starts a transfer, communicating the status to the guest side.
2423 *
2424 * @returns VBox status code.
2425 * @param pClient Client that owns the transfer.
2426 * @param pTransfer Transfer to start.
2427 */
2428int ShClSvcTransferStart(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer)
2429{
2430 LogRel2(("Shared Clipboard: Starting transfer %RU32 ...\n", pTransfer->State.uID));
2431
2432 shClSvcClientLock(pClient);
2433
2434 int rc = ShClTransferStart(pTransfer);
2435
2436 /* Let the guest know in any case
2437 * (so that it can tear down the transfer on error as well). */
2438 int rc2 = shClSvcTransferSendStatusAsync(pClient, pTransfer,
2439 RT_SUCCESS(rc)
2440 ? SHCLTRANSFERSTATUS_STARTED : SHCLTRANSFERSTATUS_ERROR, rc,
2441 NULL /* ppEvent */);
2442 if (RT_SUCCESS(rc))
2443 rc = rc2;
2444
2445 shClSvcClientUnlock(pClient);
2446 return rc;
2447}
2448
2449/**
2450 * Stops (and destroys) a transfer, communicating the status to the guest side.
2451 *
2452 * @returns VBox status code.
2453 * @param pClient Client that owns the transfer.
2454 * @param pTransfer Transfer to stop. The pointer will be invalid on success.
2455 * @param fWaitForGuest Set to \c true to wait for acknowledgement from guest, or \c false to skip waiting.
2456 */
2457int ShClSvcTransferStop(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, bool fWaitForGuest)
2458{
2459 LogRel2(("Shared Clipboard: Stopping transfer %RU32 ...\n", pTransfer->State.uID));
2460
2461 shClSvcClientLock(pClient);
2462
2463 PSHCLEVENT pEvent;
2464 int rc = shClSvcTransferSendStatusAsync(pClient, pTransfer,
2465 SHCLTRANSFERSTATUS_COMPLETED, VINF_SUCCESS, &pEvent);
2466 if ( RT_SUCCESS(rc)
2467 && fWaitForGuest)
2468 {
2469 LogRel2(("Shared Clipboard: Waiting for stop of transfer %RU32 on guest ...\n", pTransfer->State.uID));
2470
2471 shClSvcClientUnlock(pClient);
2472
2473 rc = ShClEventWait(pEvent, pTransfer->uTimeoutMs, NULL /* ppPayload */);
2474 if (RT_SUCCESS(rc))
2475 LogRel2(("Shared Clipboard: Stopped transfer %RU32 on guest\n", pTransfer->State.uID));
2476
2477 ShClEventRelease(pEvent);
2478
2479 shClSvcClientLock(pClient);
2480 }
2481
2482 if (RT_FAILURE(rc))
2483 LogRel(("Shared Clipboard: Unable to stop transfer %RU32 on guest, rc=%Rrc\n",
2484 pTransfer->State.uID, rc));
2485
2486 shClSvcClientUnlock(pClient);
2487
2488 LogFlowFuncLeaveRC(rc);
2489 return rc;
2490}
2491
2492/**
2493 * Sets the host service's (file) transfer mode.
2494 *
2495 * @returns VBox status code.
2496 * @param fMode Transfer mode to set.
2497 */
2498int shClSvcTransferModeSet(uint32_t fMode)
2499{
2500 if (fMode & ~VBOX_SHCL_TRANSFER_MODE_F_VALID_MASK)
2501 return VERR_INVALID_FLAGS;
2502
2503 g_fTransferMode = fMode;
2504
2505 LogRel2(("Shared Clipboard: File transfers are now %s\n",
2506 g_fTransferMode & VBOX_SHCL_TRANSFER_MODE_F_ENABLED ? "enabled" : "disabled"));
2507
2508 /* If file transfers are being disabled, make sure to also reset (destroy) all pending transfers. */
2509 if (!(g_fTransferMode & VBOX_SHCL_TRANSFER_MODE_F_ENABLED))
2510 {
2511 ClipboardClientMap::const_iterator itClient = g_mapClients.begin();
2512 while (itClient != g_mapClients.end())
2513 {
2514 PSHCLCLIENT pClient = itClient->second;
2515 AssertPtr(pClient);
2516
2517 shClSvcTransferDestroyAll(pClient);
2518
2519 ++itClient;
2520 }
2521 }
2522
2523 LogFlowFuncLeaveRC(VINF_SUCCESS);
2524 return VINF_SUCCESS;
2525}
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