VirtualBox

source: vbox/trunk/src/VBox/HostServices/DragAndDrop/VBoxDragAndDropSvc.cpp@ 97797

Last change on this file since 97797 was 97797, checked in by vboxsync, 2 years ago

DnD/HostService: Reset handling fixes: Don't reset (clear) the service's internal message queue when a new (HGCM) client connects. Reconnecting the client will happen on a VM restore (as the VM guest session changed), causing the message queue to be cleared, eventually running into host-side timeouts for the very first drag'n drop action performed after a VM restore.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.2 KB
Line 
1/* $Id: VBoxDragAndDropSvc.cpp 97797 2022-12-13 17:09:09Z vboxsync $ */
2/** @file
3 * Drag and Drop Service.
4 */
5
6/*
7 * Copyright (C) 2011-2022 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/** @page pg_svc_dnd Drag and drop HGCM Service
29 *
30 * @sa See src/VBox/Main/src-client/GuestDnDPrivate.cpp for more information.
31 */
32
33
34/*********************************************************************************************************************************
35* Header Files *
36*********************************************************************************************************************************/
37#define LOG_GROUP LOG_GROUP_GUEST_DND
38#include <VBox/GuestHost/DragAndDrop.h>
39#include <VBox/GuestHost/DragAndDropDefs.h>
40#include <VBox/HostServices/Service.h>
41#include <VBox/HostServices/DragAndDropSvc.h>
42#include <VBox/AssertGuest.h>
43
44#include <VBox/err.h>
45
46#include <algorithm>
47#include <list>
48#include <map>
49
50#include "dndmanager.h"
51
52using namespace DragAndDropSvc;
53
54
55/*********************************************************************************************************************************
56* Service class declaration *
57*********************************************************************************************************************************/
58
59class DragAndDropClient : public HGCM::Client
60{
61public:
62
63 DragAndDropClient(uint32_t idClient)
64 : HGCM::Client(idClient)
65 , uProtocolVerDeprecated(0)
66 , fGuestFeatures0(VBOX_DND_GF_NONE)
67 , fGuestFeatures1(VBOX_DND_GF_NONE)
68 {
69 RT_ZERO(m_SvcCtx);
70 }
71
72 virtual ~DragAndDropClient(void)
73 {
74 disconnect();
75 }
76
77public:
78
79 void disconnect(void) RT_NOEXCEPT;
80
81public:
82
83 /** Protocol version used by this client.
84 * Deprecated; only used for keeping backwards compatibility. */
85 uint32_t uProtocolVerDeprecated;
86 /** Guest feature flags, VBOX_DND_GF_0_XXX. */
87 uint64_t fGuestFeatures0;
88 /** Guest feature flags, VBOX_DND_GF_1_XXX. */
89 uint64_t fGuestFeatures1;
90};
91
92/** Map holding pointers to drag and drop clients. Key is the (unique) HGCM client ID. */
93typedef std::map<uint32_t, DragAndDropClient*> DnDClientMap;
94
95/** Simple queue (list) which holds deferred (waiting) clients. */
96typedef std::list<uint32_t> DnDClientQueue;
97
98/**
99 * Specialized drag & drop service class.
100 */
101class DragAndDropService : public HGCM::AbstractService<DragAndDropService>
102{
103public:
104 explicit DragAndDropService(PVBOXHGCMSVCHELPERS pHelpers)
105 : HGCM::AbstractService<DragAndDropService>(pHelpers)
106 , m_pManager(NULL)
107 , m_u32Mode(VBOX_DRAG_AND_DROP_MODE_OFF)
108 {}
109
110protected:
111 int init(VBOXHGCMSVCFNTABLE *pTable) RT_NOEXCEPT RT_OVERRIDE;
112 int uninit(void) RT_NOEXCEPT RT_OVERRIDE;
113 int clientConnect(uint32_t idClient, void *pvClient) RT_NOEXCEPT RT_OVERRIDE;
114 int clientDisconnect(uint32_t idClient, void *pvClient) RT_NOEXCEPT RT_OVERRIDE;
115 int clientQueryFeatures(uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT;
116 int clientReportFeatures(DragAndDropClient *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT;
117 void guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t idClient, void *pvClient, uint32_t u32Function,
118 uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT RT_OVERRIDE;
119 int hostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT RT_OVERRIDE;
120
121private:
122 int modeSet(uint32_t u32Mode) RT_NOEXCEPT;
123 inline uint32_t modeGet(void) const RT_NOEXCEPT
124 { return m_u32Mode; };
125
126 static DECLCALLBACK(int) progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser);
127
128private:
129 /** Pointer to our DnD manager instance. */
130 DnDManager *m_pManager;
131 /** Map of all connected clients.
132 * The primary key is the (unique) client ID, the secondary value
133 * an allocated pointer to the DragAndDropClient class, managed
134 * by this service class. */
135 DnDClientMap m_clientMap;
136 /** List of all clients which are queued up (deferred return) and ready
137 * to process new commands. The key is the (unique) client ID. */
138 DnDClientQueue m_clientQueue;
139 /** Current drag and drop mode, VBOX_DRAG_AND_DROP_MODE_XXX. */
140 uint32_t m_u32Mode;
141 /** Host feature mask (VBOX_DND_HF_0_XXX) for DND_GUEST_REPORT_FEATURES
142 * and DND_GUEST_QUERY_FEATURES. */
143 uint64_t m_fHostFeatures0;
144};
145
146
147/*********************************************************************************************************************************
148* Client implementation *
149*********************************************************************************************************************************/
150
151/**
152 * Called when the HGCM client disconnected on the guest side.
153 *
154 * This function takes care of the client's data cleanup and also lets the host
155 * know that the client has been disconnected.
156 */
157void DragAndDropClient::disconnect(void) RT_NOEXCEPT
158{
159 LogFlowThisFunc(("uClient=%RU32, fDeferred=%RTbool\n", m_idClient, IsDeferred()));
160
161 /*
162 * If the client still is waiting for a message (i.e in deferred mode),
163 * complete the call with a VERR_CANCELED status so that the client (VBoxTray / VBoxClient) knows
164 * it should bail out.
165 */
166 if (IsDeferred())
167 CompleteDeferred(VERR_CANCELLED);
168
169 /*
170 * Let the host know.
171 */
172 VBOXDNDCBDISCONNECTMSGDATA data;
173 RT_ZERO(data);
174 /** @todo Magic needed? */
175 /** @todo Add context ID. */
176
177 if (m_SvcCtx.pfnHostCallback)
178 {
179 int rc2 = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, GUEST_DND_FN_DISCONNECT, &data, sizeof(data));
180 if (RT_FAILURE(rc2))
181 LogFlowFunc(("Warning: Unable to notify host about client %RU32 disconnect, rc=%Rrc\n", m_idClient, rc2));
182 /* Not fatal. */
183 }
184}
185
186
187/*********************************************************************************************************************************
188* Service class implementation *
189*********************************************************************************************************************************/
190
191int DragAndDropService::init(VBOXHGCMSVCFNTABLE *pTable) RT_NOEXCEPT
192{
193 /* Legacy clients map to the root category. */
194 pTable->idxLegacyClientCategory = HGCM_CLIENT_CATEGORY_ROOT;
195
196 /* Limit to 255 clients (see also DragAndDropService::clientConnect). */
197 for (uintptr_t i = 0; i < RT_ELEMENTS(pTable->acMaxClients); i++)
198 pTable->acMaxClients[i] = UINT8_MAX;
199
200 /* Limit the number of concurrent calls to 256 (playing safe). */
201 /** @todo Properly determin the max number of pending/concurrent calls for DnD. */
202 for (uintptr_t i = 0; i < RT_ELEMENTS(pTable->acMaxClients); i++)
203 pTable->acMaxCallsPerClient[i] = 256;
204
205 /* Register functions. */
206 pTable->pfnHostCall = svcHostCall;
207 pTable->pfnSaveState = NULL; /* The service is stateless, so the normal */
208 pTable->pfnLoadState = NULL; /* construction done before restoring suffices */
209 pTable->pfnRegisterExtension = svcRegisterExtension;
210 pTable->pfnNotify = NULL;
211
212 /* Drag'n drop mode is disabled by default. */
213 modeSet(VBOX_DRAG_AND_DROP_MODE_OFF);
214
215 /* Set host features. */
216 m_fHostFeatures0 = VBOX_DND_HF_NONE;
217
218 int rc = VINF_SUCCESS;
219
220 try
221 {
222 m_pManager = new DnDManager(&DragAndDropService::progressCallback, this);
223 }
224 catch (std::bad_alloc &)
225 {
226 rc = VERR_NO_MEMORY;
227 }
228
229 LogFlowFuncLeaveRC(rc);
230 return rc;
231}
232
233int DragAndDropService::uninit(void) RT_NOEXCEPT
234{
235 LogFlowFuncEnter();
236
237 if (m_pManager)
238 {
239 delete m_pManager;
240 m_pManager = NULL;
241 }
242
243 DnDClientMap::iterator itClient = m_clientMap.begin();
244 while (itClient != m_clientMap.end())
245 {
246 delete itClient->second;
247 m_clientMap.erase(itClient);
248 itClient = m_clientMap.begin();
249 }
250
251 LogFlowFuncLeave();
252 return VINF_SUCCESS;
253}
254
255int DragAndDropService::clientConnect(uint32_t idClient, void *pvClient) RT_NOEXCEPT
256{
257 RT_NOREF1(pvClient);
258 if (m_clientMap.size() >= UINT8_MAX) /* Don't allow too much clients at the same time. */
259 {
260 AssertMsgFailed(("Maximum number of clients reached\n"));
261 return VERR_MAX_PROCS_REACHED;
262 }
263
264 /*
265 * Add client to our client map.
266 */
267 if (m_clientMap.find(idClient) != m_clientMap.end())
268 {
269 LogFunc(("Client %RU32 is already connected!\n", idClient));
270 return VERR_ALREADY_EXISTS;
271 }
272
273 try
274 {
275 DragAndDropClient *pClient = new DragAndDropClient(idClient);
276 pClient->SetSvcContext(m_SvcCtx);
277 m_clientMap[idClient] = pClient;
278 }
279 catch (std::bad_alloc &)
280 {
281 LogFunc(("Client %RU32 - VERR_NO_MEMORY!\n", idClient));
282 return VERR_NO_MEMORY;
283 }
284
285 LogFlowFunc(("Client %RU32 connected (VINF_SUCCESS)\n", idClient));
286 return VINF_SUCCESS;
287}
288
289int DragAndDropService::clientDisconnect(uint32_t idClient, void *pvClient) RT_NOEXCEPT
290{
291 RT_NOREF1(pvClient);
292
293 /* Client not found? Bail out early. */
294 DnDClientMap::iterator itClient = m_clientMap.find(idClient);
295 if (itClient == m_clientMap.end())
296 {
297 LogFunc(("Client %RU32 not found!\n", idClient));
298 return VERR_NOT_FOUND;
299 }
300
301 /*
302 * Remove from waiters queue.
303 */
304 m_clientQueue.remove(idClient);
305
306 /*
307 * Remove from client map and deallocate.
308 */
309 AssertPtr(itClient->second);
310 delete itClient->second;
311
312 m_clientMap.erase(itClient);
313
314 LogFlowFunc(("Client %RU32 disconnected\n", idClient));
315 return VINF_SUCCESS;
316}
317
318/**
319 * Implements GUEST_DND_FN_REPORT_FEATURES.
320 *
321 * @returns VBox status code.
322 * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
323 * @retval VERR_ACCESS_DENIED if not master
324 * @retval VERR_INVALID_PARAMETER if bit 63 in the 2nd parameter isn't set.
325 * @retval VERR_WRONG_PARAMETER_COUNT
326 *
327 * @param pClient The client state.
328 * @param cParms Number of parameters.
329 * @param paParms Array of parameters.
330 */
331int DragAndDropService::clientReportFeatures(DragAndDropClient *pClient, uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT
332{
333 RT_NOREF(pClient);
334
335 /*
336 * Validate the request.
337 */
338 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
339 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
340 uint64_t const fFeatures0 = paParms[0].u.uint64;
341 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
342 uint64_t const fFeatures1 = paParms[1].u.uint64;
343 ASSERT_GUEST_RETURN(fFeatures1 & VBOX_DND_GF_1_MUST_BE_ONE, VERR_INVALID_PARAMETER);
344
345 /*
346 * Report back the host features.
347 */
348 paParms[0].u.uint64 = m_fHostFeatures0;
349 paParms[1].u.uint64 = 0;
350
351 pClient->fGuestFeatures0 = fFeatures0;
352 pClient->fGuestFeatures1 = fFeatures1;
353
354 Log(("[Client %RU32] features: %#RX64 %#RX64\n", pClient->GetClientID(), fFeatures0, fFeatures1));
355
356 return VINF_SUCCESS;
357}
358
359/**
360 * Implements GUEST_DND_FN_QUERY_FEATURES.
361 *
362 * @returns VBox status code.
363 * @retval VINF_HGCM_ASYNC_EXECUTE on success (we complete the message here).
364 * @retval VERR_WRONG_PARAMETER_COUNT
365 *
366 * @param cParms Number of parameters.
367 * @param paParms Array of parameters.
368 */
369int DragAndDropService::clientQueryFeatures(uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT
370{
371 /*
372 * Validate the request.
373 */
374 ASSERT_GUEST_RETURN(cParms == 2, VERR_WRONG_PARAMETER_COUNT);
375 ASSERT_GUEST_RETURN(paParms[0].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
376 ASSERT_GUEST_RETURN(paParms[1].type == VBOX_HGCM_SVC_PARM_64BIT, VERR_WRONG_PARAMETER_TYPE);
377 ASSERT_GUEST(paParms[1].u.uint64 & RT_BIT_64(63));
378
379 /*
380 * Report back the host features.
381 */
382 paParms[0].u.uint64 = m_fHostFeatures0;
383 paParms[1].u.uint64 = 0;
384
385 return VINF_SUCCESS;
386}
387
388int DragAndDropService::modeSet(uint32_t u32Mode) RT_NOEXCEPT
389{
390#ifndef VBOX_WITH_DRAG_AND_DROP_GH
391 if ( u32Mode == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST
392 || u32Mode == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL)
393 {
394 m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
395 return VERR_NOT_SUPPORTED;
396 }
397#endif
398
399 switch (u32Mode)
400 {
401 case VBOX_DRAG_AND_DROP_MODE_OFF:
402 case VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST:
403 case VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST:
404 case VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL:
405 m_u32Mode = u32Mode;
406 break;
407
408 default:
409 m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
410 break;
411 }
412
413 return VINF_SUCCESS;
414}
415
416void DragAndDropService::guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t idClient,
417 void *pvClient, uint32_t u32Function,
418 uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT
419{
420 RT_NOREF1(pvClient);
421 LogFlowFunc(("idClient=%RU32, u32Function=%s (%#x), cParms=%RU32\n",
422 idClient, DnDGuestMsgToStr(u32Function), u32Function, cParms));
423
424 /* Check if we've the right mode set. */
425 int rc = VERR_ACCESS_DENIED; /* Play safe. */
426 switch (u32Function)
427 {
428 case GUEST_DND_FN_GET_NEXT_HOST_MSG:
429 {
430 if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
431 rc = VINF_SUCCESS;
432 else
433 {
434 LogRel(("DnD: Feature is disabled, ignoring request from guest\n"));
435 rc = VINF_HGCM_ASYNC_EXECUTE;
436 }
437 break;
438 }
439
440 /* New since protocol v2. */
441 case GUEST_DND_FN_CONNECT:
442 RT_FALL_THROUGH();
443 /* New since VBox 6.1.x. */
444 case GUEST_DND_FN_REPORT_FEATURES:
445 RT_FALL_THROUGH();
446 /* New since VBox 6.1.x. */
447 case GUEST_DND_FN_QUERY_FEATURES:
448 {
449 /*
450 * Never block these calls, as the clients issues those when
451 * initializing and might get stuck if drag and drop is set to "disabled" at
452 * that time.
453 */
454 rc = VINF_SUCCESS;
455 break;
456 }
457
458 /* New since VBOx 7.0.x. See define for details. */
459 case GUEST_DND_FN_EVT_ERROR:
460 {
461 rc = VINF_SUCCESS;
462 break;
463 }
464
465 case GUEST_DND_FN_HG_ACK_OP:
466 case GUEST_DND_FN_HG_REQ_DATA:
467 case GUEST_DND_FN_HG_EVT_PROGRESS:
468 {
469 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
470 || modeGet() == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST)
471 rc = VINF_SUCCESS;
472 else
473 LogRel(("DnD: Host -> Guest mode disabled, ignoring request from guest\n"));
474 break;
475 }
476
477 case GUEST_DND_FN_GH_ACK_PENDING:
478 case GUEST_DND_FN_GH_SND_DATA_HDR:
479 case GUEST_DND_FN_GH_SND_DATA:
480 case GUEST_DND_FN_GH_SND_DIR:
481 case GUEST_DND_FN_GH_SND_FILE_HDR:
482 case GUEST_DND_FN_GH_SND_FILE_DATA:
483 {
484#ifdef VBOX_WITH_DRAG_AND_DROP_GH
485 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
486 || modeGet() == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
487 rc = VINF_SUCCESS;
488 else
489#endif
490 LogRel(("DnD: Guest -> Host mode disabled, ignoring request from guest\n"));
491 break;
492 }
493
494 default:
495 /* Reach through to DnD manager. */
496 rc = VINF_SUCCESS;
497 break;
498 }
499
500#define DO_HOST_CALLBACK(); \
501 if ( RT_SUCCESS(rc) \
502 && m_SvcCtx.pfnHostCallback) \
503 { \
504 rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data)); \
505 }
506
507 /*
508 * Lookup client.
509 */
510 DragAndDropClient *pClient = NULL;
511
512 DnDClientMap::iterator itClient = m_clientMap.find(idClient);
513 if (itClient != m_clientMap.end())
514 {
515 pClient = itClient->second;
516 AssertPtr(pClient);
517 }
518 else
519 {
520 LogFunc(("Client %RU32 was not found\n", idClient));
521 rc = VERR_NOT_FOUND;
522 }
523
524/* Verifies that an uint32 parameter has the expected buffer size set.
525 * Will set rc to VERR_INVALID_PARAMETER otherwise. See #9777. */
526#define VERIFY_BUFFER_SIZE_UINT32(a_ParmUInt32, a_SizeExpected) \
527do { \
528 uint32_t cbTemp = 0; \
529 rc = HGCMSvcGetU32(&a_ParmUInt32, &cbTemp); \
530 ASSERT_GUEST_BREAK(RT_SUCCESS(rc) && cbTemp == a_SizeExpected); \
531} while (0)
532
533/* Gets the context ID from the first parameter and store it into the data header.
534 * Then increments idxParm by one if more than one parameter is available. */
535#define GET_CONTEXT_ID_PARM0() \
536 if (fHasCtxID) \
537 { \
538 ASSERT_GUEST_BREAK(cParms >= 1); \
539 rc = HGCMSvcGetU32(&paParms[0], &data.hdr.uContextID); \
540 ASSERT_GUEST_BREAK(RT_SUCCESS(rc)); \
541 if (cParms > 1) \
542 idxParm++; \
543 }
544
545 if (rc == VINF_SUCCESS) /* Note: rc might be VINF_HGCM_ASYNC_EXECUTE! */
546 {
547 rc = VERR_INVALID_PARAMETER; /* Play safe by default. */
548
549 /* Whether the client's advertised protocol sends context IDs with commands. */
550 const bool fHasCtxID = pClient->uProtocolVerDeprecated >= 3;
551
552 /* Current parameter index to process. */
553 unsigned idxParm = 0;
554
555 switch (u32Function)
556 {
557 /*
558 * Note: Older VBox versions with enabled DnD guest->host support (< 5.0)
559 * used the same message ID (300) for GUEST_DND_FN_GET_NEXT_HOST_MSG and
560 * HOST_DND_FN_GH_REQ_PENDING, which led this service returning
561 * VERR_INVALID_PARAMETER when the guest wanted to actually
562 * handle HOST_DND_FN_GH_REQ_PENDING.
563 */
564 case GUEST_DND_FN_GET_NEXT_HOST_MSG:
565 {
566 if (cParms == 3)
567 {
568 /* Make sure to increase the reference count so that the next message doesn't get removed between
569 * the guest's GUEST_DND_FN_GET_NEXT_HOST_MSG call and the actual message retrieval call. */
570 rc = m_pManager->GetNextMsgInfo(true /* fAddRef */,
571 &paParms[0].u.uint32 /* uMsg */, &paParms[1].u.uint32 /* cParms */);
572 if (RT_FAILURE(rc)) /* No queued messages available? */
573 {
574 if (m_SvcCtx.pfnHostCallback) /* Try asking the host. */
575 {
576 VBOXDNDCBHGGETNEXTHOSTMSG data;
577 RT_ZERO(data);
578 data.hdr.uMagic = CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG;
579 rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data));
580 if (RT_SUCCESS(rc))
581 {
582 paParms[0].u.uint32 = data.uMsg; /* uMsg */
583 paParms[1].u.uint32 = data.cParms; /* cParms */
584 /* Note: paParms[2] was set by the guest as blocking flag. */
585 }
586
587 LogFlowFunc(("Host callback returned %Rrc\n", rc));
588 }
589 else /* No host callback in place, so drag and drop is not supported by the host. */
590 rc = VERR_NOT_SUPPORTED;
591
592 if (RT_FAILURE(rc))
593 rc = m_pManager->GetNextMsg(u32Function, cParms, paParms);
594
595 /* Some error occurred or no (new) messages available? */
596 if (RT_FAILURE(rc))
597 {
598 uint32_t fFlags = 0;
599 int rc2 = HGCMSvcGetU32(&paParms[2], &fFlags);
600 if ( RT_SUCCESS(rc2)
601 && fFlags) /* Blocking flag set? */
602 {
603 /* Defer client returning. */
604 rc = VINF_HGCM_ASYNC_EXECUTE;
605 }
606 else
607 rc = VERR_INVALID_PARAMETER;
608
609 LogFlowFunc(("Message queue is empty, returning %Rrc to guest\n", rc));
610 }
611 }
612 }
613 break;
614 }
615 case GUEST_DND_FN_CONNECT:
616 {
617 ASSERT_GUEST_BREAK(cParms >= 2);
618
619 VBOXDNDCBCONNECTDATA data;
620 RT_ZERO(data);
621 data.hdr.uMagic = CB_MAGIC_DND_CONNECT;
622
623 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.hdr.uContextID); \
624 ASSERT_GUEST_RC_BREAK(rc);
625 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uProtocolVersion);
626 ASSERT_GUEST_RC_BREAK(rc);
627 rc = HGCMSvcGetU32(&paParms[idxParm], &data.fFlags);
628 ASSERT_GUEST_RC_BREAK(rc);
629
630 unsigned uProtocolVer = 3; /* The protocol version we're going to use. */
631
632 /* Make sure we're only setting a protocl version we're supporting on the host. */
633 if (data.uProtocolVersion > uProtocolVer)
634 data.uProtocolVersion = uProtocolVer;
635
636 pClient->uProtocolVerDeprecated = data.uProtocolVersion;
637
638 /* Return the highest protocol version we're supporting. */
639 AssertBreak(idxParm);
640 ASSERT_GUEST_BREAK(idxParm);
641 paParms[idxParm - 1].u.uint32 = data.uProtocolVersion;
642
643 LogFlowFunc(("Client %RU32 is now using protocol v%RU32\n",
644 pClient->GetClientID(), pClient->uProtocolVerDeprecated));
645
646 DO_HOST_CALLBACK();
647 break;
648 }
649 case GUEST_DND_FN_REPORT_FEATURES:
650 {
651 rc = clientReportFeatures(pClient, cParms, paParms);
652 if (RT_SUCCESS(rc))
653 {
654 VBOXDNDCBREPORTFEATURESDATA data;
655 RT_ZERO(data);
656 data.hdr.uMagic = CB_MAGIC_DND_REPORT_FEATURES;
657
658 data.fGuestFeatures0 = pClient->fGuestFeatures0;
659 /* fGuestFeatures1 is not used yet. */
660
661 /* Don't touch initial rc. */
662 int rc2 = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data));
663 AssertRC(rc2);
664 }
665 break;
666 }
667 case GUEST_DND_FN_QUERY_FEATURES:
668 {
669 rc = clientQueryFeatures(cParms, paParms);
670 break;
671 }
672 case GUEST_DND_FN_HG_ACK_OP:
673 {
674 ASSERT_GUEST_BREAK(cParms >= 2);
675
676 VBOXDNDCBHGACKOPDATA data;
677 RT_ZERO(data);
678 data.hdr.uMagic = CB_MAGIC_DND_HG_ACK_OP;
679
680 GET_CONTEXT_ID_PARM0();
681 rc = HGCMSvcGetU32(&paParms[idxParm], &data.uAction); /* Get drop action. */
682 ASSERT_GUEST_RC_BREAK(rc);
683
684 DO_HOST_CALLBACK();
685 break;
686 }
687 case GUEST_DND_FN_HG_REQ_DATA:
688 {
689 VBOXDNDCBHGREQDATADATA data;
690 RT_ZERO(data);
691 data.hdr.uMagic = CB_MAGIC_DND_HG_REQ_DATA;
692
693 switch (pClient->uProtocolVerDeprecated)
694 {
695 case 3:
696 {
697 ASSERT_GUEST_BREAK(cParms == 3);
698 GET_CONTEXT_ID_PARM0();
699 rc = HGCMSvcGetPv(&paParms[idxParm++], (void **)&data.pszFormat, &data.cbFormat);
700 ASSERT_GUEST_RC_BREAK(rc);
701 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.cbFormat);
702 break;
703 }
704
705 case 2:
706 RT_FALL_THROUGH();
707 default:
708 {
709 ASSERT_GUEST_BREAK(cParms == 1);
710 rc = HGCMSvcGetPv(&paParms[idxParm], (void**)&data.pszFormat, &data.cbFormat);
711 ASSERT_GUEST_RC_BREAK(rc);
712 break;
713 }
714 }
715
716 DO_HOST_CALLBACK();
717 break;
718 }
719 case GUEST_DND_FN_HG_EVT_PROGRESS:
720 {
721 ASSERT_GUEST_BREAK(cParms >= 3);
722
723 VBOXDNDCBHGEVTPROGRESSDATA data;
724 RT_ZERO(data);
725 data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS;
726
727 GET_CONTEXT_ID_PARM0();
728 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uStatus);
729 ASSERT_GUEST_RC_BREAK(rc);
730 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uPercentage);
731 ASSERT_GUEST_RC_BREAK(rc);
732 rc = HGCMSvcGetU32(&paParms[idxParm], &data.rc);
733 ASSERT_GUEST_RC_BREAK(rc);
734
735 DO_HOST_CALLBACK();
736 break;
737 }
738#ifdef VBOX_WITH_DRAG_AND_DROP_GH
739 case GUEST_DND_FN_GH_ACK_PENDING:
740 {
741 VBOXDNDCBGHACKPENDINGDATA data;
742 RT_ZERO(data);
743 data.hdr.uMagic = CB_MAGIC_DND_GH_ACK_PENDING;
744
745 switch (pClient->uProtocolVerDeprecated)
746 {
747 case 3:
748 {
749 ASSERT_GUEST_BREAK(cParms == 5);
750 GET_CONTEXT_ID_PARM0();
751 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uDefAction);
752 ASSERT_GUEST_RC_BREAK(rc);
753 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uAllActions);
754 ASSERT_GUEST_RC_BREAK(rc);
755 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pszFormat, &data.cbFormat);
756 ASSERT_GUEST_RC_BREAK(rc);
757 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.cbFormat);
758 break;
759 }
760
761 case 2:
762 default:
763 {
764 ASSERT_GUEST_BREAK(cParms == 3);
765 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uDefAction);
766 ASSERT_GUEST_RC_BREAK(rc);
767 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.uAllActions);
768 ASSERT_GUEST_RC_BREAK(rc);
769 rc = HGCMSvcGetPv(&paParms[idxParm], (void**)&data.pszFormat, &data.cbFormat);
770 ASSERT_GUEST_RC_BREAK(rc);
771 break;
772 }
773 }
774
775 DO_HOST_CALLBACK();
776 break;
777 }
778 /* New since protocol v3. */
779 case GUEST_DND_FN_GH_SND_DATA_HDR:
780 {
781 ASSERT_GUEST_BREAK(cParms == 12);
782
783 VBOXDNDCBSNDDATAHDRDATA data;
784 RT_ZERO(data);
785 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA_HDR;
786
787 GET_CONTEXT_ID_PARM0();
788 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.data.uFlags);
789 ASSERT_GUEST_RC_BREAK(rc);
790 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.data.uScreenId);
791 ASSERT_GUEST_RC_BREAK(rc);
792 rc = HGCMSvcGetU64(&paParms[idxParm++], &data.data.cbTotal);
793 ASSERT_GUEST_RC_BREAK(rc);
794 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.data.cbMeta);
795 ASSERT_GUEST_RC_BREAK(rc);
796 ASSERT_GUEST_BREAK(data.data.cbMeta <= data.data.cbTotal);
797 rc = HGCMSvcGetPv(&paParms[idxParm++], &data.data.pvMetaFmt, &data.data.cbMetaFmt);
798 ASSERT_GUEST_RC_BREAK(rc);
799 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.data.cbMetaFmt);
800 rc = HGCMSvcGetU64(&paParms[idxParm++], &data.data.cObjects);
801 ASSERT_GUEST_RC_BREAK(rc);
802 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.data.enmCompression);
803 ASSERT_GUEST_RC_BREAK(rc);
804 rc = HGCMSvcGetU32(&paParms[idxParm++], (uint32_t *)&data.data.enmChecksumType);
805 ASSERT_GUEST_RC_BREAK(rc);
806 rc = HGCMSvcGetPv(&paParms[idxParm++], &data.data.pvChecksum, &data.data.cbChecksum);
807 ASSERT_GUEST_RC_BREAK(rc);
808 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.data.cbChecksum);
809
810 DO_HOST_CALLBACK();
811 break;
812 }
813 case GUEST_DND_FN_GH_SND_DATA:
814 {
815 switch (pClient->uProtocolVerDeprecated)
816 {
817 case 3:
818 {
819 ASSERT_GUEST_BREAK(cParms == 5);
820
821 VBOXDNDCBSNDDATADATA data;
822 RT_ZERO(data);
823 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA;
824
825 GET_CONTEXT_ID_PARM0();
826 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.data.u.v3.pvData, &data.data.u.v3.cbData);
827 ASSERT_GUEST_RC_BREAK(rc);
828 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.data.u.v3.cbData);
829 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.data.u.v3.pvChecksum, &data.data.u.v3.cbChecksum);
830 ASSERT_GUEST_RC_BREAK(rc);
831 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.data.u.v3.cbChecksum);
832
833 DO_HOST_CALLBACK();
834 break;
835 }
836
837 case 2:
838 RT_FALL_THROUGH();
839 default:
840 {
841 ASSERT_GUEST_BREAK(cParms == 2);
842
843 VBOXDNDCBSNDDATADATA data;
844 RT_ZERO(data);
845 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DATA;
846
847 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.data.u.v1.pvData, &data.data.u.v1.cbData);
848 ASSERT_GUEST_RC_BREAK(rc);
849 rc = HGCMSvcGetU32(&paParms[idxParm], &data.data.u.v1.cbTotalSize);
850 ASSERT_GUEST_RC_BREAK(rc);
851
852 DO_HOST_CALLBACK();
853 break;
854 }
855 }
856 break;
857 }
858 case GUEST_DND_FN_GH_SND_DIR:
859 {
860 ASSERT_GUEST_BREAK(cParms >= 3);
861
862 VBOXDNDCBSNDDIRDATA data;
863 RT_ZERO(data);
864 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_DIR;
865
866 GET_CONTEXT_ID_PARM0();
867 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pszPath, &data.cbPath);
868 ASSERT_GUEST_RC_BREAK(rc);
869 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.cbPath);
870 rc = HGCMSvcGetU32(&paParms[idxParm], &data.fMode);
871 ASSERT_GUEST_RC_BREAK(rc);
872
873 DO_HOST_CALLBACK();
874 break;
875 }
876 /* New since protocol v2 (>= VBox 5.0). */
877 case GUEST_DND_FN_GH_SND_FILE_HDR:
878 {
879 ASSERT_GUEST_BREAK(cParms == 6);
880
881 VBOXDNDCBSNDFILEHDRDATA data;
882 RT_ZERO(data);
883 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_HDR;
884
885 GET_CONTEXT_ID_PARM0();
886 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pszFilePath, &data.cbFilePath);
887 ASSERT_GUEST_RC_BREAK(rc);
888 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.cbFilePath);
889 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.fFlags);
890 ASSERT_GUEST_RC_BREAK(rc);
891 rc = HGCMSvcGetU32(&paParms[idxParm++], &data.fMode);
892 ASSERT_GUEST_RC_BREAK(rc);
893 rc = HGCMSvcGetU64(&paParms[idxParm], &data.cbSize);
894 ASSERT_GUEST_RC_BREAK(rc);
895
896 DO_HOST_CALLBACK();
897 break;
898 }
899 case GUEST_DND_FN_GH_SND_FILE_DATA:
900 {
901 switch (pClient->uProtocolVerDeprecated)
902 {
903 /* Protocol v3 adds (optional) checksums. */
904 case 3:
905 {
906 ASSERT_GUEST_BREAK(cParms == 5);
907
908 VBOXDNDCBSNDFILEDATADATA data;
909 RT_ZERO(data);
910 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
911
912 GET_CONTEXT_ID_PARM0();
913 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pvData, &data.cbData);
914 ASSERT_GUEST_RC_BREAK(rc);
915 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.cbData);
916 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.u.v3.pvChecksum, &data.u.v3.cbChecksum);
917 ASSERT_GUEST_RC_BREAK(rc);
918 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.u.v3.cbChecksum);
919
920 DO_HOST_CALLBACK();
921 break;
922 }
923 /* Protocol v2 only sends the next data chunks to reduce traffic. */
924 case 2:
925 {
926 ASSERT_GUEST_BREAK(cParms == 3);
927
928 VBOXDNDCBSNDFILEDATADATA data;
929 RT_ZERO(data);
930 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
931
932 GET_CONTEXT_ID_PARM0();
933 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pvData, &data.cbData);
934 ASSERT_GUEST_RC_BREAK(rc);
935 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm], data.cbData);
936
937 DO_HOST_CALLBACK();
938 break;
939 }
940 /* Protocol v1 sends the file path and attributes for every file chunk (!). */
941 default:
942 {
943 ASSERT_GUEST_BREAK(cParms == 5);
944
945 VBOXDNDCBSNDFILEDATADATA data;
946 RT_ZERO(data);
947 data.hdr.uMagic = CB_MAGIC_DND_GH_SND_FILE_DATA;
948
949 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.u.v1.pszFilePath, &data.u.v1.cbFilePath);
950 ASSERT_GUEST_RC_BREAK(rc);
951 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.u.v1.cbFilePath);
952 rc = HGCMSvcGetPv(&paParms[idxParm++], (void**)&data.pvData, &data.cbData);
953 ASSERT_GUEST_RC_BREAK(rc);
954 VERIFY_BUFFER_SIZE_UINT32(paParms[idxParm++], data.cbData);
955 rc = HGCMSvcGetU32(&paParms[idxParm], &data.u.v1.fMode);
956 ASSERT_GUEST_RC_BREAK(rc);
957
958 DO_HOST_CALLBACK();
959 break;
960 }
961 }
962 break;
963 }
964 case GUEST_DND_FN_EVT_ERROR:
965 {
966 ASSERT_GUEST_BREAK(cParms >= 1);
967
968 VBOXDNDCBEVTERRORDATA data;
969 RT_ZERO(data);
970 data.hdr.uMagic = CB_MAGIC_DND_EVT_ERROR;
971
972 GET_CONTEXT_ID_PARM0();
973 rc = HGCMSvcGetU32(&paParms[idxParm], (uint32_t *)&data.rc);
974 ASSERT_GUEST_RC_BREAK(rc);
975
976 DO_HOST_CALLBACK();
977 break;
978 }
979#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
980
981 default:
982 {
983 LogFlowFunc(("u32Function=%s (%#x), cParms=%RU32\n", DnDHostMsgToStr(u32Function), u32Function, cParms));
984
985 /* All other messages are handled by the DnD manager. */
986 rc = m_pManager->GetNextMsg(u32Function, cParms, paParms);
987 if (rc == VERR_NO_DATA) /* Manager has no new messsages? Try asking the host. */
988 {
989 if (m_SvcCtx.pfnHostCallback)
990 {
991 VBOXDNDCBHGGETNEXTHOSTMSGDATA data;
992 RT_ZERO(data);
993
994 data.hdr.uMagic = VBOX_DND_CB_MAGIC_MAKE(0 /* uFn */, 0 /* uVer */);
995
996 data.uMsg = u32Function;
997 data.cParms = cParms;
998 data.paParms = paParms;
999
1000 rc = m_SvcCtx.pfnHostCallback(m_SvcCtx.pvHostData, u32Function, &data, sizeof(data));
1001 if (RT_SUCCESS(rc))
1002 {
1003 cParms = data.cParms;
1004 paParms = data.paParms;
1005 }
1006 else
1007 {
1008 if (rc == VERR_CANCELLED)
1009 {
1010 /* Host indicated that the current operation was cancelled. Tell the guest. */
1011 LogFunc(("Host indicated that operation was cancelled\n"));
1012 }
1013 else
1014 {
1015 /*
1016 * In case the guest is too fast asking for the next message
1017 * and the host did not supply it yet, just defer the client's
1018 * return until a response from the host available.
1019 */
1020 LogFunc(("No new messages from the host (%Rrc), deferring request\n", rc));
1021 rc = VINF_HGCM_ASYNC_EXECUTE;
1022 }
1023 }
1024 }
1025 else /* No host callback in place, so drag and drop is not supported by the host. */
1026 rc = VERR_NOT_SUPPORTED;
1027 }
1028 break;
1029 }
1030 }
1031 }
1032
1033#undef VERIFY_BUFFER_SIZE_UINT32
1034
1035 /*
1036 * If async execution is requested, we didn't notify the guest yet about
1037 * completion. The client is queued into the waiters list and will be
1038 * notified as soon as a new event is available.
1039 */
1040 if (rc == VINF_HGCM_ASYNC_EXECUTE)
1041 {
1042 LogFunc(("Deferring client %RU32\n", idClient));
1043
1044 try
1045 {
1046 AssertPtr(pClient);
1047 pClient->SetDeferred(callHandle, u32Function, cParms, paParms);
1048 m_clientQueue.push_back(idClient);
1049 }
1050 catch (std::bad_alloc &)
1051 {
1052 rc = VERR_NO_MEMORY;
1053 /* Don't report to guest. */
1054 }
1055 }
1056 else if (pClient)
1057 {
1058 /* Complete the call on the guest side. */
1059 pClient->Complete(callHandle, rc);
1060 }
1061 else
1062 {
1063 AssertMsgFailed(("Guest call failed with %Rrc\n", rc));
1064 rc = VERR_NOT_IMPLEMENTED;
1065 }
1066
1067 LogFunc(("Returning %Rrc to guest\n", rc));
1068}
1069
1070int DragAndDropService::hostCall(uint32_t u32Function,
1071 uint32_t cParms, VBOXHGCMSVCPARM paParms[]) RT_NOEXCEPT
1072{
1073 LogFlowFunc(("u32Function=%s (%#x), cParms=%RU32, cClients=%zu, cQueue=%zu\n",
1074 DnDHostMsgToStr(u32Function), u32Function, cParms, m_clientMap.size(), m_clientQueue.size()));
1075
1076 uint32_t const uMode = modeGet();
1077
1078 /* Check if we've the right mode set. */
1079 int rc = VERR_ACCESS_DENIED; /* Play safe. */
1080 switch (u32Function)
1081 {
1082 /*
1083 * Host -> Guest mode
1084 */
1085 case HOST_DND_FN_HG_EVT_ENTER:
1086 RT_FALL_THROUGH();
1087 case HOST_DND_FN_HG_EVT_MOVE:
1088 RT_FALL_THROUGH();
1089 case HOST_DND_FN_HG_EVT_LEAVE:
1090 RT_FALL_THROUGH();
1091 case HOST_DND_FN_HG_EVT_DROPPED:
1092 RT_FALL_THROUGH();
1093 case HOST_DND_FN_HG_SND_DATA_HDR:
1094 RT_FALL_THROUGH();
1095 case HOST_DND_FN_HG_SND_DATA:
1096 RT_FALL_THROUGH();
1097 case HOST_DND_FN_HG_SND_MORE_DATA:
1098 RT_FALL_THROUGH();
1099 case HOST_DND_FN_HG_SND_DIR:
1100 RT_FALL_THROUGH();
1101 case HOST_DND_FN_HG_SND_FILE_DATA:
1102 RT_FALL_THROUGH();
1103 case HOST_DND_FN_HG_SND_FILE_HDR:
1104 {
1105 if ( uMode == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST
1106 || uMode == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL)
1107 rc = VINF_SUCCESS;
1108 else
1109 {
1110 LogRel2(("DnD: Host to guest mode is disabled, ignoring request from host\n"));
1111 }
1112 break;
1113 }
1114
1115 /*
1116 * Guest -> Host mode
1117 */
1118 case HOST_DND_FN_GH_REQ_PENDING:
1119 RT_FALL_THROUGH();
1120 case HOST_DND_FN_GH_EVT_DROPPED:
1121 {
1122 if ( uMode == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST
1123 || uMode == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL)
1124 rc = VINF_SUCCESS;
1125 else
1126 {
1127 LogRel2(("DnD: Guest to host mode is disabled, ignoring request from host\n"));
1128 }
1129 break;
1130 }
1131
1132 /*
1133 * Both modes
1134 */
1135 case HOST_DND_FN_CANCEL:
1136 if (uMode != VBOX_DRAG_AND_DROP_MODE_OFF)
1137 rc = VINF_SUCCESS;
1138 break;
1139
1140 /*
1141 * Functions that always work.
1142 */
1143 case HOST_DND_FN_SET_MODE:
1144 rc = VINF_SUCCESS;
1145 break;
1146
1147 /*
1148 * Forbid everything else not explicitly allowed.
1149 */
1150 default:
1151 break;
1152 }
1153
1154 if (RT_FAILURE(rc))
1155 return rc;
1156
1157 bool fSendToGuest = false; /* Whether to send the message down to the guest side or not. */
1158
1159 switch (u32Function)
1160 {
1161 case HOST_DND_FN_SET_MODE:
1162 {
1163 if (cParms != 1)
1164 rc = VERR_INVALID_PARAMETER;
1165 else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT)
1166 rc = VERR_INVALID_PARAMETER;
1167 else
1168 rc = modeSet(paParms[0].u.uint32);
1169 break;
1170 }
1171
1172 case HOST_DND_FN_CANCEL:
1173 {
1174 LogFlowFunc(("Cancelling all waiting clients ...\n"));
1175
1176 /* Forcefully reset the message queue, as the host has cancelled the current operation. */
1177 m_pManager->Reset(true /* fForce */);
1178
1179 /*
1180 * Wake up all deferred clients and tell them to process
1181 * the cancelling message next.
1182 */
1183 DnDClientQueue::iterator itQueue = m_clientQueue.begin();
1184 while (itQueue != m_clientQueue.end())
1185 {
1186 DnDClientMap::iterator itClient = m_clientMap.find(*itQueue);
1187 Assert(itClient != m_clientMap.end());
1188
1189 DragAndDropClient *pClient = itClient->second;
1190 AssertPtr(pClient);
1191
1192 int rc2 = pClient->SetDeferredMsgInfo(HOST_DND_FN_CANCEL,
1193 /* Protocol v3+ also contains the context ID. */
1194 pClient->uProtocolVerDeprecated >= 3 ? 1 : 0);
1195 AssertRC(rc2);
1196
1197 /* Return VERR_CANCELLED when waking up the guest side. */
1198 pClient->CompleteDeferred(VERR_CANCELLED);
1199
1200 m_clientQueue.erase(itQueue);
1201 itQueue = m_clientQueue.begin();
1202 }
1203
1204 Assert(m_clientQueue.empty());
1205
1206 /* Tell the host that everything went well. */
1207 rc = VINF_SUCCESS;
1208 break;
1209 }
1210
1211 case HOST_DND_FN_HG_EVT_ENTER:
1212 {
1213 /* Reset the message queue as a new DnD operation just began. */
1214 m_pManager->Reset(false /* fForce */);
1215
1216 fSendToGuest = true;
1217 rc = VINF_SUCCESS;
1218 break;
1219 }
1220
1221 default:
1222 {
1223 fSendToGuest = true;
1224 rc = VINF_SUCCESS;
1225 break;
1226 }
1227 }
1228
1229 do /* goto avoidance break-loop. */
1230 {
1231 if (fSendToGuest)
1232 {
1233 if (m_clientMap.empty()) /* At least one client on the guest connected? */
1234 {
1235 /*
1236 * Tell the host that the guest does not support drag'n drop.
1237 * This might happen due to not installed Guest Additions or
1238 * not running VBoxTray/VBoxClient.
1239 */
1240 rc = VERR_NOT_SUPPORTED;
1241 break;
1242 }
1243
1244 rc = m_pManager->AddMsg(u32Function, cParms, paParms, true /* fAppend */);
1245 if (RT_FAILURE(rc))
1246 {
1247 AssertMsgFailed(("Adding new message of type=%RU32 failed with rc=%Rrc\n", u32Function, rc));
1248 break;
1249 }
1250
1251 /* Any clients in our queue ready for processing the next command? */
1252 if (m_clientQueue.empty())
1253 {
1254 LogFlowFunc(("All clients (%zu) busy -- delaying execution\n", m_clientMap.size()));
1255 break;
1256 }
1257
1258 uint32_t uClientNext = m_clientQueue.front();
1259 DnDClientMap::iterator itClientNext = m_clientMap.find(uClientNext);
1260 Assert(itClientNext != m_clientMap.end());
1261
1262 DragAndDropClient *pClient = itClientNext->second;
1263 AssertPtr(pClient);
1264
1265 /*
1266 * Check if this was a request for getting the next host
1267 * message. If so, return the message ID and the parameter
1268 * count. The message itself has to be queued.
1269 */
1270 uint32_t uMsgClient = pClient->GetMsgType();
1271
1272 uint32_t uMsgNext = 0;
1273 uint32_t cParmsNext = 0;
1274 /* Note: We only want to peek for the next message, hence fAddRef is false. */
1275 int rcNext = m_pManager->GetNextMsgInfo(false /* fAddRef */, &uMsgNext, &cParmsNext);
1276
1277 LogFlowFunc(("uMsgClient=%s (%#x), uMsgNext=%s (%#x), cParmsNext=%RU32, rcNext=%Rrc\n",
1278 DnDGuestMsgToStr(uMsgClient), uMsgClient, DnDHostMsgToStr(uMsgNext), uMsgNext, cParmsNext, rcNext));
1279
1280 if (RT_SUCCESS(rcNext))
1281 {
1282 if (uMsgClient == GUEST_DND_FN_GET_NEXT_HOST_MSG)
1283 {
1284 rc = pClient->SetDeferredMsgInfo(uMsgNext, cParmsNext);
1285
1286 /* Note: Report the current rc back to the guest. */
1287 pClient->CompleteDeferred(rc);
1288 }
1289 /*
1290 * Does the message the client is waiting for match the message
1291 * next in the queue? Process it right away then.
1292 */
1293 else if (uMsgClient == uMsgNext)
1294 {
1295 rc = m_pManager->GetNextMsg(u32Function, cParms, paParms);
1296
1297 /* Note: Report the current rc back to the guest. */
1298 pClient->CompleteDeferred(rc);
1299 }
1300 else /* Should not happen; cancel the operation on the guest. */
1301 {
1302 LogFunc(("Client ID=%RU32 in wrong state with uMsg=%RU32 (next message in queue: %RU32), cancelling\n",
1303 pClient->GetClientID(), uMsgClient, uMsgNext));
1304
1305 pClient->CompleteDeferred(VERR_CANCELLED);
1306 }
1307
1308 m_clientQueue.pop_front();
1309 }
1310
1311 } /* fSendToGuest */
1312
1313 } while (0); /* To use breaks. */
1314
1315 LogFlowFuncLeaveRC(rc);
1316 return rc;
1317}
1318
1319DECLCALLBACK(int) DragAndDropService::progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser)
1320{
1321 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
1322
1323 DragAndDropService *pSelf = static_cast<DragAndDropService *>(pvUser);
1324 AssertPtr(pSelf);
1325
1326 if (pSelf->m_SvcCtx.pfnHostCallback)
1327 {
1328 LogFlowFunc(("GUEST_DND_FN_HG_EVT_PROGRESS: uStatus=%RU32, uPercentage=%RU32, rc=%Rrc\n",
1329 uStatus, uPercentage, rc));
1330
1331 VBOXDNDCBHGEVTPROGRESSDATA data;
1332 data.hdr.uMagic = CB_MAGIC_DND_HG_EVT_PROGRESS;
1333 data.uPercentage = RT_MIN(uPercentage, 100);
1334 data.uStatus = uStatus;
1335 data.rc = rc; /** @todo uin32_t vs. int. */
1336
1337 return pSelf->m_SvcCtx.pfnHostCallback(pSelf->m_SvcCtx.pvHostData,
1338 GUEST_DND_FN_HG_EVT_PROGRESS,
1339 &data, sizeof(data));
1340 }
1341
1342 return VINF_SUCCESS;
1343}
1344
1345/**
1346 * @copydoc FNVBOXHGCMSVCLOAD
1347 */
1348extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
1349{
1350 return DragAndDropService::svcLoad(pTable);
1351}
1352
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