VirtualBox

source: vbox/trunk/src/VBox/HostServices/DragAndDrop/service.cpp@ 55784

Last change on this file since 55784 was 55640, checked in by vboxsync, 10 years ago

DnD:

  • Overhauled "dropped files" directory + general file handling: Keep the directories/files open when doing the actual transfers (only protocol >= 2)
  • Unified "dropped files" directory creation/rollback handling for guest/host parts.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.4 KB
Line 
1/* $Id: service.cpp 55640 2015-05-04 12:38:57Z vboxsync $ */
2/** @file
3 * Drag and Drop Service.
4 */
5
6/*
7 * Copyright (C) 2011-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/** @page pg_svc_guest_control Guest Control HGCM Service
19 *
20 * This service acts as a proxy for handling and buffering host command requests
21 * and clients on the guest. It tries to be as transparent as possible to let
22 * the guest (client) and host side do their protocol handling as desired.
23 *
24 * The following terms are used:
25 * - Host: A host process (e.g. VBoxManage or another tool utilizing the Main API)
26 * which wants to control something on the guest.
27 * - Client: A client (e.g. VBoxService) running inside the guest OS waiting for
28 * new host commands to perform. There can be multiple clients connected
29 * to a service. A client is represented by its HGCM client ID.
30 * - Context ID: An (almost) unique ID automatically generated on the host (Main API)
31 * to not only distinguish clients but individual requests. Because
32 * the host does not know anything about connected clients it needs
33 * an indicator which it can refer to later. This context ID gets
34 * internally bound by the service to a client which actually processes
35 * the command in order to have a relationship between client<->context ID(s).
36 *
37 * The host can trigger commands which get buffered by the service (with full HGCM
38 * parameter info). As soon as a client connects (or is ready to do some new work)
39 * it gets a buffered host command to process it. This command then will be immediately
40 * removed from the command list. If there are ready clients but no new commands to be
41 * processed, these clients will be set into a deferred state (that is being blocked
42 * to return until a new command is available).
43 *
44 * If a client needs to inform the host that something happened, it can send a
45 * message to a low level HGCM callback registered in Main. This callback contains
46 * the actual data as well as the context ID to let the host do the next necessary
47 * steps for this context. This context ID makes it possible to wait for an event
48 * inside the host's Main API function (like starting a process on the guest and
49 * wait for getting its PID returned by the client) as well as cancelling blocking
50 * host calls in order the client terminated/crashed (HGCM detects disconnected
51 * clients and reports it to this service's callback).
52 */
53
54/******************************************************************************
55 * Header Files *
56 ******************************************************************************/
57#ifdef LOG_GROUP
58 #undef LOG_GROUP
59#endif
60#define LOG_GROUP LOG_GROUP_GUEST_DND
61
62#include <map>
63
64#include "dndmanager.h"
65
66/******************************************************************************
67 * Service class declaration *
68 ******************************************************************************/
69
70/** Map holding pointers to HGCM clients. Key is the (unique) HGCM client ID. */
71typedef std::map<uint32_t, HGCM::Client*> DnDClientMap;
72
73/**
74 * Specialized drag & drop service class.
75 */
76class DragAndDropService : public HGCM::AbstractService<DragAndDropService>
77{
78public:
79
80 explicit DragAndDropService(PVBOXHGCMSVCHELPERS pHelpers)
81 : HGCM::AbstractService<DragAndDropService>(pHelpers)
82 , m_pManager(NULL) {}
83
84protected:
85
86 int init(VBOXHGCMSVCFNTABLE *pTable);
87 int uninit(void);
88 int clientConnect(uint32_t u32ClientID, void *pvClient);
89 int clientDisconnect(uint32_t u32ClientID, void *pvClient);
90 void guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
91 int hostCall(uint32_t u32Function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
92
93 int modeSet(uint32_t u32Mode);
94 inline uint32_t modeGet() { return m_u32Mode; };
95
96protected:
97
98 static DECLCALLBACK(int) progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser);
99
100protected:
101
102 DnDManager *m_pManager;
103 /** Map of all connected clients. */
104 DnDClientMap m_clientMap;
105 /** List of all clients which are queued up (deferred return) and ready
106 * to process new commands. */
107 RTCList<HGCM::Client*> m_clientQueue;
108 uint32_t m_u32Mode;
109};
110
111/******************************************************************************
112 * Service class implementation *
113 ******************************************************************************/
114
115int DragAndDropService::init(VBOXHGCMSVCFNTABLE *pTable)
116{
117 /* Register functions. */
118 pTable->pfnHostCall = svcHostCall;
119 pTable->pfnSaveState = NULL; /* The service is stateless, so the normal */
120 pTable->pfnLoadState = NULL; /* construction done before restoring suffices */
121 pTable->pfnRegisterExtension = svcRegisterExtension;
122
123 /* Drag'n drop mode is disabled by default. */
124 modeSet(VBOX_DRAG_AND_DROP_MODE_OFF);
125
126 int rc = VINF_SUCCESS;
127
128 try
129 {
130 m_pManager = new DnDManager(&DragAndDropService::progressCallback, this);
131 }
132 catch(std::bad_alloc &)
133 {
134 rc = VERR_NO_MEMORY;
135 }
136
137 LogFlowFuncLeaveRC(rc);
138 return rc;
139}
140
141int DragAndDropService::uninit(void)
142{
143 if (m_pManager)
144 {
145 delete m_pManager;
146 m_pManager = NULL;
147 }
148
149 return VINF_SUCCESS;
150}
151
152int DragAndDropService::clientConnect(uint32_t u32ClientID, void *pvClient)
153{
154 if (m_clientMap.size() >= UINT8_MAX) /* Don't allow too much clients at the same time. */
155 {
156 AssertMsgFailed(("Maximum number of clients reached\n"));
157 return VERR_BUFFER_OVERFLOW;
158 }
159
160 int rc = VINF_SUCCESS;
161
162 /*
163 * Add client to our client map.
164 */
165 if (m_clientMap.find(u32ClientID) != m_clientMap.end())
166 rc = VERR_ALREADY_EXISTS;
167
168 if (RT_SUCCESS(rc))
169 {
170 try
171 {
172 m_clientMap[u32ClientID] = new HGCM::Client(u32ClientID);
173 }
174 catch(std::bad_alloc &)
175 {
176 rc = VERR_NO_MEMORY;
177 }
178
179 if (RT_SUCCESS(rc))
180 {
181 /*
182 * Clear the message queue as soon as a new clients connect
183 * to ensure that every client has the same state.
184 */
185 if (m_pManager)
186 m_pManager->clear();
187 }
188 }
189
190 LogFlowFunc(("Client %RU32 connected, rc=%Rrc\n", u32ClientID, rc));
191 return rc;
192}
193
194int DragAndDropService::clientDisconnect(uint32_t u32ClientID, void *pvClient)
195{
196 /* Client not found? Bail out early. */
197 DnDClientMap::iterator itClient = m_clientMap.find(u32ClientID);
198 if (itClient == m_clientMap.end())
199 return VERR_NOT_FOUND;
200
201 /*
202 * Remove from waiters queue.
203 */
204 for (size_t i = 0; i < m_clientQueue.size(); i++)
205 {
206 HGCM::Client *pClient = m_clientQueue.at(i);
207 if (pClient->clientId() == u32ClientID)
208 {
209 if (m_pHelpers)
210 m_pHelpers->pfnCallComplete(pClient->handle(), VERR_INTERRUPTED);
211
212 m_clientQueue.removeAt(i);
213 delete pClient;
214
215 break;
216 }
217 }
218
219 /*
220 * Remove from client map and deallocate.
221 */
222 AssertPtr(itClient->second);
223 delete itClient->second;
224
225 m_clientMap.erase(itClient);
226
227 LogFlowFunc(("Client %RU32 disconnected\n", u32ClientID));
228 return VINF_SUCCESS;
229}
230
231int DragAndDropService::modeSet(uint32_t u32Mode)
232{
233 /** @todo Validate mode. */
234 switch (u32Mode)
235 {
236 case VBOX_DRAG_AND_DROP_MODE_OFF:
237 case VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST:
238 case VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST:
239 case VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL:
240 m_u32Mode = u32Mode;
241 break;
242
243 default:
244 m_u32Mode = VBOX_DRAG_AND_DROP_MODE_OFF;
245 break;
246 }
247
248 return VINF_SUCCESS;
249}
250
251void DragAndDropService::guestCall(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
252 void *pvClient, uint32_t u32Function,
253 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
254{
255 LogFlowFunc(("u32ClientID=%RU32, u32Function=%RU32, cParms=%RU32\n",
256 u32ClientID, u32Function, cParms));
257
258 /* Check if we've the right mode set. */
259 int rc = VERR_ACCESS_DENIED; /* Play safe. */
260 switch (u32Function)
261 {
262 case DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG:
263 {
264 if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
265 {
266 rc = VINF_SUCCESS;
267 }
268 else
269 {
270 LogFlowFunc(("DnD disabled, deferring request\n"));
271 rc = VINF_HGCM_ASYNC_EXECUTE;
272 }
273 break;
274 }
275
276 /* Note: New since protocol version 2. */
277 case DragAndDropSvc::GUEST_DND_CONNECT:
278 /* Fall through is intentional. */
279 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
280 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
281 case DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS:
282 {
283 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
284 || modeGet() == VBOX_DRAG_AND_DROP_MODE_HOST_TO_GUEST)
285 {
286 rc = VINF_SUCCESS;
287 }
288 else
289 LogFlowFunc(("Host -> Guest DnD mode disabled, ignoring request\n"));
290 break;
291 }
292
293 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
294 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
295 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
296 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR:
297 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA:
298 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
299 {
300#ifdef VBOX_WITH_DRAG_AND_DROP_GH
301 if ( modeGet() == VBOX_DRAG_AND_DROP_MODE_BIDIRECTIONAL
302 || modeGet() == VBOX_DRAG_AND_DROP_MODE_GUEST_TO_HOST)
303 {
304 rc = VINF_SUCCESS;
305 }
306 else
307#endif
308 LogFlowFunc(("Guest -> Host DnD mode disabled, ignoring request\n"));
309 break;
310 }
311
312 default:
313 /* Reach through to DnD manager. */
314 rc = VINF_SUCCESS;
315 break;
316 }
317
318#ifdef DEBUG_andy
319 LogFlowFunc(("Mode (%RU32) check rc=%Rrc\n", modeGet(), rc));
320#endif
321
322#define DO_HOST_CALLBACK(); \
323 if ( RT_SUCCESS(rc) \
324 && m_pfnHostCallback) \
325 { \
326 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data)); \
327 }
328
329 if (rc == VINF_SUCCESS) /* Note: rc might be VINF_HGCM_ASYNC_EXECUTE! */
330 {
331 DnDClientMap::iterator itClient = m_clientMap.find(u32ClientID);
332 Assert(itClient != m_clientMap.end());
333
334 HGCM::Client *pClient = itClient->second;
335 AssertPtr(pClient);
336
337 switch (u32Function)
338 {
339 /*
340 * Note: Older VBox versions with enabled DnD guest->host support (< 5.0)
341 * used the same message ID (300) for GUEST_DND_GET_NEXT_HOST_MSG and
342 * HOST_DND_GH_REQ_PENDING, which led this service returning
343 * VERR_INVALID_PARAMETER when the guest wanted to actually
344 * handle HOST_DND_GH_REQ_PENDING.
345 */
346 case DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG:
347 {
348 LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG\n"));
349 if ( cParms != 3
350 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* message */
351 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* parameter count */
352 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* blocking */)
353 {
354 rc = VERR_INVALID_PARAMETER;
355 }
356 else
357 {
358 rc = m_pManager->nextMessageInfo(&paParms[0].u.uint32 /* uMsg */, &paParms[1].u.uint32 /* cParms */);
359 if (RT_FAILURE(rc)) /* No queued messages available? */
360 {
361 if (m_pfnHostCallback) /* Try asking the host. */
362 {
363 DragAndDropSvc::VBOXDNDCBHGGETNEXTHOSTMSG data;
364 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG;
365 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
366 if (RT_SUCCESS(rc))
367 {
368 paParms[0].u.uint32 = data.uMsg; /* uMsg */
369 paParms[1].u.uint32 = data.cParms; /* cParms */
370 /* Note: paParms[2] was set by the guest as blocking flag. */
371 }
372 }
373 else
374 rc = VERR_NOT_FOUND;
375
376 if (RT_FAILURE(rc))
377 rc = m_pManager->nextMessage(u32Function, cParms, paParms);
378
379 /* Some error occurred? */
380 if ( RT_FAILURE(rc)
381 && paParms[2].u.uint32) /* Blocking flag set? */
382 {
383 /* Defer client returning. */
384 rc = VINF_HGCM_ASYNC_EXECUTE;
385 }
386 }
387 }
388 break;
389 }
390 case DragAndDropSvc::GUEST_DND_CONNECT:
391 {
392 LogFlowFunc(("GUEST_DND_CONNECT\n"));
393 if ( cParms != 2
394 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* protocol version */
395 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* additional connection flags */)
396 rc = VERR_INVALID_PARAMETER;
397 else
398 {
399 uint32_t uProtocol;
400 rc = paParms[0].getUInt32(&uProtocol); /* Get protocol version. */
401 if (RT_SUCCESS(rc))
402 rc = pClient->setProtocol(uProtocol);
403 if (RT_SUCCESS(rc))
404 {
405 /** @todo Handle connection flags (paParms[1]). */
406 }
407
408 /* Note: Does not reach the host; the client's protocol version
409 * is only kept in this service. */
410 }
411 break;
412 }
413 case DragAndDropSvc::GUEST_DND_HG_ACK_OP:
414 {
415 LogFlowFunc(("GUEST_DND_HG_ACK_OP\n"));
416 if ( cParms != 1
417 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* action */)
418 rc = VERR_INVALID_PARAMETER;
419 else
420 {
421 DragAndDropSvc::VBOXDNDCBHGACKOPDATA data;
422 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_ACK_OP;
423 rc = paParms[0].getUInt32(&data.uAction); /* Get drop action. */
424 DO_HOST_CALLBACK();
425 }
426 break;
427 }
428 case DragAndDropSvc::GUEST_DND_HG_REQ_DATA:
429 {
430 LogFlowFunc(("GUEST_DND_HG_REQ_DATA\n"));
431 if ( cParms != 1
432 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* format */)
433 rc = VERR_INVALID_PARAMETER;
434 else
435 {
436 DragAndDropSvc::VBOXDNDCBHGREQDATADATA data;
437 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_REQ_DATA;
438 rc = paParms[0].getPointer((void**)&data.pszFormat, &data.cbFormat);
439 DO_HOST_CALLBACK();
440 }
441 break;
442 }
443 case DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS:
444 {
445 LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS\n"));
446 if ( cParms != 3
447 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* status */
448 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* percent */
449 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* rc */)
450 rc = VERR_INVALID_PARAMETER;
451 else
452 {
453 DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA data;
454 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS;
455 rc = paParms[0].getUInt32(&data.uStatus);
456 if (RT_SUCCESS(rc))
457 rc = paParms[1].getUInt32(&data.uPercentage);
458 if (RT_SUCCESS(rc))
459 rc = paParms[2].getUInt32(&data.rc);
460 DO_HOST_CALLBACK();
461 }
462 break;
463 }
464#ifdef VBOX_WITH_DRAG_AND_DROP_GH
465 case DragAndDropSvc::GUEST_DND_GH_ACK_PENDING:
466 {
467 LogFlowFunc(("GUEST_DND_GH_ACK_PENDING\n"));
468 if ( cParms != 3
469 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* defaction */
470 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* alloctions */
471 || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* format */)
472 rc = VERR_INVALID_PARAMETER;
473 else
474 {
475 DragAndDropSvc::VBOXDNDCBGHACKPENDINGDATA data;
476 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_ACK_PENDING;
477 rc = paParms[0].getUInt32(&data.uDefAction);
478 if (RT_SUCCESS(rc))
479 rc = paParms[1].getUInt32(&data.uAllActions);
480 if (RT_SUCCESS(rc))
481 rc = paParms[2].getPointer((void**)&data.pszFormat, &data.cbFormat);
482 DO_HOST_CALLBACK();
483 }
484 break;
485 }
486 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
487 {
488 LogFlowFunc(("GUEST_DND_GH_SND_DATA\n"));
489 if ( cParms != 2
490 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* data */
491 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* size */)
492 rc = VERR_INVALID_PARAMETER;
493 else
494 {
495 DragAndDropSvc::VBOXDNDCBSNDDATADATA data;
496 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA;
497 rc = paParms[0].getPointer((void**)&data.pvData, &data.cbData);
498 if (RT_SUCCESS(rc))
499 rc = paParms[1].getUInt32(&data.cbTotalSize);
500 DO_HOST_CALLBACK();
501 }
502 break;
503 }
504 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
505 {
506 LogFlowFunc(("GUEST_DND_GH_SND_DIR\n"));
507 if ( cParms != 3
508 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* path */
509 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* path length */
510 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* creation mode */)
511 rc = VERR_INVALID_PARAMETER;
512 else
513 {
514 DragAndDropSvc::VBOXDNDCBSNDDIRDATA data;
515 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR;
516 uint32_t cTmp;
517 rc = paParms[0].getPointer((void**)&data.pszPath, &cTmp);
518 if (RT_SUCCESS(rc))
519 rc = paParms[1].getUInt32(&data.cbPath);
520 if (RT_SUCCESS(rc))
521 rc = paParms[2].getUInt32(&data.fMode);
522
523 LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x\n", data.pszPath, data.cbPath, data.fMode));
524 DO_HOST_CALLBACK();
525 }
526 break;
527 }
528 /* Note: Since protocol v2 (>= VBox 5.0). */
529 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR:
530 {
531 LogFlowFunc(("GUEST_DND_GH_SND_FILE_HDR\n"));
532 if ( cParms != 6
533 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* context ID */
534 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* file path */
535 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* file path length */
536 || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */
537 || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* file mode */
538 || paParms[5].type != VBOX_HGCM_SVC_PARM_64BIT /* file size */)
539 rc = VERR_INVALID_PARAMETER;
540 else
541 {
542 DragAndDropSvc::VBOXDNDCBSNDFILEHDRDATA data;
543 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_HDR;
544 uint32_t cTmp;
545 /* paParms[0] is context ID; unused yet. */
546 rc = paParms[1].getPointer((void**)&data.pszFilePath, &cTmp);
547 if (RT_SUCCESS(rc))
548 rc = paParms[2].getUInt32(&data.cbFilePath);
549 if (RT_SUCCESS(rc))
550 rc = paParms[3].getUInt32(&data.fFlags);
551 if (RT_SUCCESS(rc))
552 rc = paParms[4].getUInt32(&data.fMode);
553 if (RT_SUCCESS(rc))
554 rc = paParms[5].getUInt64(&data.cbSize);
555
556 LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x, cbSize=%RU64\n",
557 data.pszFilePath, data.cbFilePath, data.fMode, data.cbSize));
558 DO_HOST_CALLBACK();
559 }
560 break;
561 }
562 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA:
563 {
564 LogFlowFunc(("GUEST_DND_GH_SND_FILE_DATA\n"));
565
566 switch (pClient->protocol())
567 {
568 case 2: /* Protocol version 2 only sends the next data chunks to reduce traffic. */
569 {
570 if ( cParms != 3
571 /* paParms[0] is context ID; unused yet. */
572 || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* file data */
573 || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* file data length */)
574 {
575 rc = VERR_INVALID_PARAMETER;
576 }
577 else
578 {
579 DragAndDropSvc::VBOXDNDCBSNDFILEDATADATA data;
580 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_DATA;
581 /* paParms[0] is context ID; unused yet. */
582 rc = paParms[1].getPointer((void**)&data.pvData, &data.cbData);
583 if (RT_SUCCESS(rc))
584 rc = paParms[2].getUInt32(&data.cbData);
585
586 LogFlowFunc(("cbData=%RU32, pvData=0x%p\n", data.cbData, data.pvData));
587 DO_HOST_CALLBACK();
588 }
589 break;
590 }
591 default:
592 {
593 if ( cParms != 5
594 || paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* file path */
595 || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* file path length */
596 || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* file data */
597 || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* file data length */
598 || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* creation mode */)
599 {
600 rc = VERR_INVALID_PARAMETER;
601 }
602 else
603 {
604 DragAndDropSvc::VBOXDNDCBSNDFILEDATADATA data;
605 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_DATA;
606 uint32_t cTmp;
607 rc = paParms[0].getPointer((void**)&data.u.v1.pszFilePath, &cTmp);
608 if (RT_SUCCESS(rc))
609 rc = paParms[1].getUInt32(&data.u.v1.cbFilePath);
610 if (RT_SUCCESS(rc))
611 rc = paParms[2].getPointer((void**)&data.pvData, &cTmp);
612 if (RT_SUCCESS(rc))
613 rc = paParms[3].getUInt32(&data.cbData);
614 if (RT_SUCCESS(rc))
615 rc = paParms[4].getUInt32(&data.u.v1.fMode);
616
617 LogFlowFunc(("pszFilePath=%s, cbData=%RU32, pvData=0x%p, fMode=0x%x\n",
618 data.u.v1.pszFilePath, data.cbData, data.pvData, data.u.v1.fMode));
619 DO_HOST_CALLBACK();
620 }
621 break;
622 }
623 }
624 break;
625 }
626 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
627 {
628 LogFlowFunc(("GUEST_DND_GH_EVT_ERROR\n"));
629 if ( cParms != 1
630 || paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* rc */)
631 rc = VERR_INVALID_PARAMETER;
632 else
633 {
634 DragAndDropSvc::VBOXDNDCBEVTERRORDATA data;
635 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR;
636
637 uint32_t rcOp;
638 rc = paParms[0].getUInt32(&rcOp);
639 if (RT_SUCCESS(rc))
640 data.rc = rcOp;
641
642 DO_HOST_CALLBACK();
643 }
644 break;
645 }
646#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
647 default:
648 {
649 /* All other messages are handled by the DnD manager. */
650 rc = m_pManager->nextMessage(u32Function, cParms, paParms);
651 if (rc == VERR_NO_DATA) /* Manager has no new messsages? Try asking the host. */
652 {
653 if (m_pfnHostCallback)
654 {
655 DragAndDropSvc::VBOXDNDCBHGGETNEXTHOSTMSGDATA data;
656 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG_DATA;
657 data.uMsg = u32Function;
658 data.cParms = cParms;
659 data.paParms = paParms;
660
661 rc = m_pfnHostCallback(m_pvHostData, u32Function, &data, sizeof(data));
662 if (RT_SUCCESS(rc))
663 {
664 cParms = data.cParms;
665 paParms = data.paParms;
666 }
667 }
668 }
669 break;
670 }
671 }
672 }
673
674 /*
675 * If async execution is requested, we didn't notify the guest yet about
676 * completion. The client is queued into the waiters list and will be
677 * notified as soon as a new event is available.
678 */
679 if (rc == VINF_HGCM_ASYNC_EXECUTE)
680 {
681 m_clientQueue.append(new HGCM::Client(u32ClientID, callHandle,
682 u32Function, cParms, paParms));
683 }
684
685 if ( rc != VINF_HGCM_ASYNC_EXECUTE
686 && m_pHelpers)
687 {
688 m_pHelpers->pfnCallComplete(callHandle, rc);
689 }
690
691 LogFlowFunc(("Returning rc=%Rrc\n", rc));
692}
693
694int DragAndDropService::hostCall(uint32_t u32Function,
695 uint32_t cParms, VBOXHGCMSVCPARM paParms[])
696{
697 LogFlowFunc(("u32Function=%RU32, cParms=%RU32, cClients=%zu, cQueue=%zu\n",
698 u32Function, cParms, m_clientMap.size(), m_clientQueue.size()));
699
700 int rc;
701 if (u32Function == DragAndDropSvc::HOST_DND_SET_MODE)
702 {
703 if (cParms != 1)
704 rc = VERR_INVALID_PARAMETER;
705 else if (paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT)
706 rc = VERR_INVALID_PARAMETER;
707 else
708 rc = modeSet(paParms[0].u.uint32);
709 }
710 else if (modeGet() != VBOX_DRAG_AND_DROP_MODE_OFF)
711 {
712 if (m_clientMap.size()) /* At least one client on the guest connected? */
713 {
714 rc = m_pManager->addMessage(u32Function, cParms, paParms, true /* fAppend */);
715 if (RT_SUCCESS(rc))
716 {
717 if (m_clientQueue.size()) /* Any clients in our queue ready for processing the next command? */
718 {
719 HGCM::Client *pClient = m_clientQueue.first();
720 AssertPtr(pClient);
721
722 /*
723 * Check if this was a request for getting the next host
724 * message. If so, return the message ID and the parameter
725 * count. The message itself has to be queued.
726 */
727 uint32_t uMsg = pClient->message();
728 if (uMsg == DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG)
729 {
730 LogFlowFunc(("Client %RU32 is waiting for next host msg\n", pClient->clientId()));
731
732 uint32_t uMsg1;
733 uint32_t cParms1;
734 rc = m_pManager->nextMessageInfo(&uMsg1, &cParms1);
735 if (RT_SUCCESS(rc))
736 {
737 pClient->addMessageInfo(uMsg1, cParms1);
738 if ( m_pHelpers
739 && m_pHelpers->pfnCallComplete)
740 {
741 m_pHelpers->pfnCallComplete(pClient->handle(), rc);
742 }
743
744 m_clientQueue.removeFirst();
745
746 delete pClient;
747 pClient = NULL;
748 }
749 else
750 AssertMsgFailed(("m_pManager::nextMessageInfo failed with rc=%Rrc\n", rc));
751 }
752 else
753 AssertMsgFailed(("Client ID=%RU32 in wrong state with uMsg=%RU32\n",
754 pClient->clientId(), uMsg));
755 }
756 else
757 LogFlowFunc(("All clients busy; delaying execution\n"));
758 }
759 else
760 AssertMsgFailed(("Adding new message of type=%RU32 failed with rc=%Rrc\n",
761 u32Function, rc));
762 }
763 else
764 {
765 /*
766 * Tell the host that the guest does not support drag'n drop.
767 * This might happen due to not installed Guest Additions or
768 * not running VBoxTray/VBoxClient.
769 */
770 rc = VERR_NOT_SUPPORTED;
771 }
772 }
773 else
774 {
775 /* Tell the host that a wrong drag'n drop mode is set. */
776 rc = VERR_ACCESS_DENIED;
777 }
778
779 LogFlowFuncLeaveRC(rc);
780 return rc;
781}
782
783DECLCALLBACK(int) DragAndDropService::progressCallback(uint32_t uStatus, uint32_t uPercentage, int rc, void *pvUser)
784{
785 AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
786
787 DragAndDropService *pSelf = static_cast<DragAndDropService *>(pvUser);
788 AssertPtr(pSelf);
789
790 if (pSelf->m_pfnHostCallback)
791 {
792 LogFlowFunc(("GUEST_DND_HG_EVT_PROGRESS: uStatus=%RU32, uPercentage=%RU32, rc=%Rrc\n",
793 uStatus, uPercentage, rc));
794
795 DragAndDropSvc::VBOXDNDCBHGEVTPROGRESSDATA data;
796 data.hdr.u32Magic = DragAndDropSvc::CB_MAGIC_DND_HG_EVT_PROGRESS;
797 data.uPercentage = RT_MIN(uPercentage, 100);
798 data.uStatus = uStatus;
799 data.rc = rc; /** @todo uin32_t vs. int. */
800
801 return pSelf->m_pfnHostCallback(pSelf->m_pvHostData,
802 DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS,
803 &data, sizeof(data));
804 }
805
806 return VINF_SUCCESS;
807}
808
809/**
810 * @copydoc VBOXHGCMSVCLOAD
811 */
812extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
813{
814 return DragAndDropService::svcLoad(pTable);
815}
816
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