VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibDragAndDrop.cpp@ 64226

Last change on this file since 64226 was 62845, checked in by vboxsync, 8 years ago

vbglR3DnDHGRecvDataRaw: Added suggestion about dropping buggy pre-v3 protocol code, spotted while fixing complaints about variables (cbFormatRecv & cbDataRecv, only the former being spot on) being used uninitialized. Again, it looks like MSC 2010 isn't very clever wrt determining uninitialized variables, so no do-while-false-break loops of flat if (RT_SUCCESS(rc)) statements - sideways if pyramids only.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 77.6 KB
Line 
1/* $Id: VBoxGuestR3LibDragAndDrop.cpp 62845 2016-08-01 20:18:42Z vboxsync $ */
2/** @file
3 * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Drag & Drop.
4 */
5
6/*
7 * Copyright (C) 2011-2016 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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/path.h>
32#include <iprt/dir.h>
33#include <iprt/file.h>
34#include <iprt/uri.h>
35#include <iprt/thread.h>
36
37#include <iprt/cpp/list.h>
38#include <iprt/cpp/ministring.h>
39
40#ifdef LOG_GROUP
41 #undef LOG_GROUP
42#endif
43#define LOG_GROUP LOG_GROUP_GUEST_DND
44#include <VBox/log.h>
45
46#include <VBox/VBoxGuestLib.h>
47#include <VBox/GuestHost/DragAndDrop.h>
48#include <VBox/HostServices/DragAndDropSvc.h>
49
50using namespace DragAndDropSvc;
51
52#include "VBGLR3Internal.h"
53
54
55/*********************************************************************************************************************************
56* Forward declarations *
57*********************************************************************************************************************************/
58
59VBGLR3DECL(int) VbglR3DnDHGSendProgress(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uStatus, uint8_t uPercent, int rcErr);
60
61
62/*********************************************************************************************************************************
63* Private internal functions *
64*********************************************************************************************************************************/
65
66static int vbglR3DnDGetNextMsgType(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puMsg, uint32_t *pcParms, bool fWait)
67{
68 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
69 AssertPtrReturn(puMsg, VERR_INVALID_POINTER);
70 AssertPtrReturn(pcParms, VERR_INVALID_POINTER);
71
72 VBOXDNDNEXTMSGMSG Msg;
73 RT_ZERO(Msg);
74 Msg.hdr.result = VERR_WRONG_ORDER;
75 Msg.hdr.u32ClientID = pCtx->uClientID;
76 Msg.hdr.u32Function = GUEST_DND_GET_NEXT_HOST_MSG;
77 Msg.hdr.cParms = 3;
78
79 Msg.uMsg.SetUInt32(0);
80 Msg.cParms.SetUInt32(0);
81 Msg.fBlock.SetUInt32(fWait ? 1 : 0);
82
83 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
84 if (RT_SUCCESS(rc))
85 {
86 rc = Msg.hdr.result;
87 if (RT_SUCCESS(rc))
88 {
89 rc = Msg.uMsg.GetUInt32(puMsg); AssertRC(rc);
90 rc = Msg.cParms.GetUInt32(pcParms); AssertRC(rc);
91 }
92 }
93
94 return rc;
95}
96
97/** @todo r=andy Clean up the parameter list. */
98static int vbglR3DnDHGRecvAction(PVBGLR3GUESTDNDCMDCTX pCtx,
99 uint32_t uMsg,
100 uint32_t *puScreenId,
101 uint32_t *puX,
102 uint32_t *puY,
103 uint32_t *puDefAction,
104 uint32_t *puAllActions,
105 char *pszFormats,
106 uint32_t cbFormats,
107 uint32_t *pcbFormatsRecv)
108{
109 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
110 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
111 AssertPtrReturn(puX, VERR_INVALID_POINTER);
112 AssertPtrReturn(puY, VERR_INVALID_POINTER);
113 AssertPtrReturn(puDefAction, VERR_INVALID_POINTER);
114 AssertPtrReturn(puAllActions, VERR_INVALID_POINTER);
115 AssertPtrReturn(pszFormats, VERR_INVALID_POINTER);
116 AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
117 AssertPtrReturn(pcbFormatsRecv, VERR_INVALID_POINTER);
118
119 VBOXDNDHGACTIONMSG Msg;
120 RT_ZERO(Msg);
121 Msg.hdr.result = VERR_WRONG_ORDER;
122 Msg.hdr.u32ClientID = pCtx->uClientID;
123 Msg.hdr.u32Function = uMsg;
124
125 if (pCtx->uProtocol < 3)
126 {
127 Msg.hdr.cParms = 7;
128
129 Msg.u.v1.uScreenId.SetUInt32(0);
130 Msg.u.v1.uX.SetUInt32(0);
131 Msg.u.v1.uY.SetUInt32(0);
132 Msg.u.v1.uDefAction.SetUInt32(0);
133 Msg.u.v1.uAllActions.SetUInt32(0);
134 Msg.u.v1.pvFormats.SetPtr(pszFormats, cbFormats);
135 Msg.u.v1.cbFormats.SetUInt32(0);
136 }
137 else
138 {
139 Msg.hdr.cParms = 8;
140
141 Msg.u.v3.uContext.SetUInt32(0);
142 Msg.u.v3.uScreenId.SetUInt32(0);
143 Msg.u.v3.uX.SetUInt32(0);
144 Msg.u.v3.uY.SetUInt32(0);
145 Msg.u.v3.uDefAction.SetUInt32(0);
146 Msg.u.v3.uAllActions.SetUInt32(0);
147 Msg.u.v3.pvFormats.SetPtr(pszFormats, cbFormats);
148 Msg.u.v3.cbFormats.SetUInt32(0);
149 }
150
151 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
152 if (RT_SUCCESS(rc))
153 {
154 rc = Msg.hdr.result;
155 if (RT_SUCCESS(rc))
156 {
157 if (pCtx->uProtocol < 3)
158 {
159 rc = Msg.u.v1.uScreenId.GetUInt32(puScreenId); AssertRC(rc);
160 rc = Msg.u.v1.uX.GetUInt32(puX); AssertRC(rc);
161 rc = Msg.u.v1.uY.GetUInt32(puY); AssertRC(rc);
162 rc = Msg.u.v1.uDefAction.GetUInt32(puDefAction); AssertRC(rc);
163 rc = Msg.u.v1.uAllActions.GetUInt32(puAllActions); AssertRC(rc);
164 rc = Msg.u.v1.cbFormats.GetUInt32(pcbFormatsRecv); AssertRC(rc);
165 }
166 else
167 {
168 /** @todo Context ID not used yet. */
169 rc = Msg.u.v3.uScreenId.GetUInt32(puScreenId); AssertRC(rc);
170 rc = Msg.u.v3.uX.GetUInt32(puX); AssertRC(rc);
171 rc = Msg.u.v3.uY.GetUInt32(puY); AssertRC(rc);
172 rc = Msg.u.v3.uDefAction.GetUInt32(puDefAction); AssertRC(rc);
173 rc = Msg.u.v3.uAllActions.GetUInt32(puAllActions); AssertRC(rc);
174 rc = Msg.u.v3.cbFormats.GetUInt32(pcbFormatsRecv); AssertRC(rc);
175 }
176
177 AssertReturn(cbFormats >= *pcbFormatsRecv, VERR_TOO_MUCH_DATA);
178 }
179 }
180
181 return rc;
182}
183
184static int vbglR3DnDHGRecvLeave(PVBGLR3GUESTDNDCMDCTX pCtx)
185{
186 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
187
188 VBOXDNDHGLEAVEMSG Msg;
189 RT_ZERO(Msg);
190 Msg.hdr.u32ClientID = pCtx->uClientID;
191 Msg.hdr.u32Function = HOST_DND_HG_EVT_LEAVE;
192
193 if (pCtx->uProtocol < 3)
194 {
195 Msg.hdr.cParms = 0;
196 }
197 else
198 {
199 Msg.hdr.cParms = 1;
200
201 /** @todo Context ID not used yet. */
202 Msg.u.v3.uContext.SetUInt32(0);
203 }
204
205 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
206 if (RT_SUCCESS(rc))
207 rc = Msg.hdr.result;
208
209 return rc;
210}
211
212static int vbglR3DnDHGRecvCancel(PVBGLR3GUESTDNDCMDCTX pCtx)
213{
214 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
215
216 VBOXDNDHGCANCELMSG Msg;
217 RT_ZERO(Msg);
218 Msg.hdr.result = VERR_WRONG_ORDER;
219 Msg.hdr.u32ClientID = pCtx->uClientID;
220 Msg.hdr.u32Function = HOST_DND_HG_EVT_CANCEL;
221
222 if (pCtx->uProtocol < 3)
223 {
224 Msg.hdr.cParms = 0;
225 }
226 else
227 {
228 Msg.hdr.cParms = 1;
229
230 /** @todo Context ID not used yet. */
231 Msg.u.v3.uContext.SetUInt32(0);
232 }
233
234 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
235 if (RT_SUCCESS(rc))
236 rc = Msg.hdr.result;
237
238 return rc;
239}
240
241static int vbglR3DnDHGRecvDir(PVBGLR3GUESTDNDCMDCTX pCtx,
242 char *pszDirname,
243 uint32_t cbDirname,
244 uint32_t *pcbDirnameRecv,
245 uint32_t *pfMode)
246{
247 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
248 AssertPtrReturn(pszDirname, VERR_INVALID_POINTER);
249 AssertReturn(cbDirname, VERR_INVALID_PARAMETER);
250 AssertPtrReturn(pcbDirnameRecv, VERR_INVALID_POINTER);
251 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
252
253 VBOXDNDHGSENDDIRMSG Msg;
254 RT_ZERO(Msg);
255 Msg.hdr.result = VERR_WRONG_ORDER;
256 Msg.hdr.u32ClientID = pCtx->uClientID;
257 Msg.hdr.u32Function = HOST_DND_HG_SND_DIR;
258
259 if (pCtx->uProtocol < 3)
260 {
261 Msg.hdr.cParms = 3;
262
263 Msg.u.v1.pvName.SetPtr(pszDirname, cbDirname);
264 Msg.u.v1.cbName.SetUInt32(cbDirname);
265 Msg.u.v1.fMode.SetUInt32(0);
266 }
267 else
268 {
269 Msg.hdr.cParms = 4;
270
271 /** @todo Context ID not used yet. */
272 Msg.u.v3.uContext.SetUInt32(0);
273 Msg.u.v3.pvName.SetPtr(pszDirname, cbDirname);
274 Msg.u.v3.cbName.SetUInt32(cbDirname);
275 Msg.u.v3.fMode.SetUInt32(0);
276 }
277
278 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
279 if (RT_SUCCESS(rc))
280 {
281 rc = Msg.hdr.result;
282 if (RT_SUCCESS(Msg.hdr.result))
283 {
284 if (pCtx->uProtocol < 3)
285 {
286 rc = Msg.u.v1.cbName.GetUInt32(pcbDirnameRecv); AssertRC(rc);
287 rc = Msg.u.v1.fMode.GetUInt32(pfMode); AssertRC(rc);
288 }
289 else
290 {
291 /** @todo Context ID not used yet. */
292 rc = Msg.u.v3.cbName.GetUInt32(pcbDirnameRecv); AssertRC(rc);
293 rc = Msg.u.v3.fMode.GetUInt32(pfMode); AssertRC(rc);
294 }
295
296 AssertReturn(cbDirname >= *pcbDirnameRecv, VERR_TOO_MUCH_DATA);
297 }
298 }
299
300 return rc;
301}
302
303static int vbglR3DnDHGRecvFileData(PVBGLR3GUESTDNDCMDCTX pCtx,
304 char *pszFilename,
305 uint32_t cbFilename,
306 uint32_t *pcbFilenameRecv,
307 void *pvData,
308 uint32_t cbData,
309 uint32_t *pcbDataRecv,
310 uint32_t *pfMode)
311{
312 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
313 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
314 AssertReturn(cbFilename, VERR_INVALID_PARAMETER);
315 AssertPtrReturn(pcbFilenameRecv, VERR_INVALID_POINTER);
316 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
317 AssertReturn(cbData, VERR_INVALID_PARAMETER);
318 AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER);
319 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
320
321 VBOXDNDHGSENDFILEDATAMSG Msg;
322 RT_ZERO(Msg);
323 Msg.hdr.result = VERR_WRONG_ORDER;
324 Msg.hdr.u32ClientID = pCtx->uClientID;
325 Msg.hdr.u32Function = HOST_DND_HG_SND_FILE_DATA;
326
327 if (pCtx->uProtocol <= 1)
328 {
329 Msg.u.v1.pvName.SetPtr(pszFilename, cbFilename);
330 Msg.u.v1.cbName.SetUInt32(0);
331 Msg.u.v1.pvData.SetPtr(pvData, cbData);
332 Msg.u.v1.cbData.SetUInt32(0);
333 Msg.u.v1.fMode.SetUInt32(0);
334
335 Msg.hdr.cParms = 5;
336 }
337 else if (pCtx->uProtocol == 2)
338 {
339 Msg.u.v2.uContext.SetUInt32(0);
340 Msg.u.v2.pvData.SetPtr(pvData, cbData);
341 Msg.u.v2.cbData.SetUInt32(cbData);
342
343 Msg.hdr.cParms = 3;
344 }
345 else if (pCtx->uProtocol >= 3)
346 {
347 Msg.u.v3.uContext.SetUInt32(0);
348 Msg.u.v3.pvData.SetPtr(pvData, cbData);
349 Msg.u.v3.cbData.SetUInt32(0);
350 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
351 Msg.u.v3.cbChecksum.SetUInt32(0);
352
353 Msg.hdr.cParms = 5;
354 }
355 else
356 AssertMsgFailed(("Protocol %RU32 not implemented\n", pCtx->uProtocol));
357
358 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
359 if (RT_SUCCESS(rc))
360 {
361 rc = Msg.hdr.result;
362 if (RT_SUCCESS(rc))
363 {
364 if (pCtx->uProtocol <= 1)
365 {
366 rc = Msg.u.v1.cbName.GetUInt32(pcbFilenameRecv); AssertRC(rc);
367 rc = Msg.u.v1.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
368 rc = Msg.u.v1.fMode.GetUInt32(pfMode); AssertRC(rc);
369
370 AssertReturn(cbFilename >= *pcbFilenameRecv, VERR_TOO_MUCH_DATA);
371 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
372 }
373 else if (pCtx->uProtocol == 2)
374 {
375 /** @todo Context ID not used yet. */
376 rc = Msg.u.v2.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
377 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
378 }
379 else if (pCtx->uProtocol >= 3)
380 {
381 /** @todo Context ID not used yet. */
382 rc = Msg.u.v3.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
383 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
384 /** @todo Add checksum support. */
385 }
386 else
387 AssertMsgFailed(("Protocol %RU32 not implemented\n", pCtx->uProtocol));
388 }
389 }
390
391 return rc;
392}
393
394static int vbglR3DnDHGRecvFileHdr(PVBGLR3GUESTDNDCMDCTX pCtx,
395 char *pszFilename,
396 uint32_t cbFilename,
397 uint32_t *puFlags,
398 uint32_t *pfMode,
399 uint64_t *pcbTotal)
400{
401 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
402 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
403 AssertReturn(cbFilename, VERR_INVALID_PARAMETER);
404 AssertPtrReturn(puFlags, VERR_INVALID_POINTER);
405 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
406 AssertReturn(pcbTotal, VERR_INVALID_POINTER);
407
408 VBOXDNDHGSENDFILEHDRMSG Msg;
409 RT_ZERO(Msg);
410 Msg.hdr.result = VERR_WRONG_ORDER;
411 Msg.hdr.u32ClientID = pCtx->uClientID;
412 Msg.hdr.u32Function = HOST_DND_HG_SND_FILE_HDR;
413
414 int rc;
415
416 if (pCtx->uProtocol <= 1)
417 {
418 rc = VERR_NOT_SUPPORTED;
419 }
420 else
421 {
422 Msg.hdr.cParms = 6;
423
424 Msg.uContext.SetUInt32(0); /** @todo Not used yet. */
425 Msg.pvName.SetPtr(pszFilename, cbFilename);
426 Msg.cbName.SetUInt32(cbFilename);
427 Msg.uFlags.SetUInt32(0);
428 Msg.fMode.SetUInt32(0);
429 Msg.cbTotal.SetUInt64(0);
430
431 rc = VINF_SUCCESS;
432 }
433
434 if (RT_SUCCESS(rc))
435 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
436 if (RT_SUCCESS(rc))
437 {
438 rc = Msg.hdr.result;
439 if (RT_SUCCESS(rc))
440 {
441 /** @todo Get context ID. */
442 rc = Msg.uFlags.GetUInt32(puFlags); AssertRC(rc);
443 rc = Msg.fMode.GetUInt32(pfMode); AssertRC(rc);
444 rc = Msg.cbTotal.GetUInt64(pcbTotal); AssertRC(rc);
445 }
446 }
447
448 return rc;
449}
450
451static int vbglR3DnDHGRecvURIData(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr, DnDDroppedFiles *pDroppedFiles)
452{
453 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
454 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
455 AssertPtrReturn(pDroppedFiles, VERR_INVALID_POINTER);
456
457 /* Only count the raw data minus the already received meta data. */
458 Assert(pDataHdr->cbTotal >= pDataHdr->cbMeta);
459 uint64_t cbToRecvBytes = pDataHdr->cbTotal - pDataHdr->cbMeta;
460 uint64_t cToRecvObjs = pDataHdr->cObjects;
461
462 LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64, (cbTotal=%RU64, cbMeta=%RU32)\n",
463 cbToRecvBytes, cToRecvObjs, pDataHdr->cbTotal, pDataHdr->cbMeta));
464
465 /*
466 * Only do accounting for protocol v3 and up.
467 * The older protocols did not have any data accounting available, so
468 * we simply tried to receive as much data as available and bail out.
469 */
470 const bool fDoAccounting = pCtx->uProtocol >= 3;
471
472 /* Anything to do at all? */
473 if (fDoAccounting)
474 {
475 if ( !cbToRecvBytes
476 && !cToRecvObjs)
477 {
478 return VINF_SUCCESS;
479 }
480 }
481
482 /*
483 * Allocate temporary chunk buffer.
484 */
485 uint32_t cbChunkMax = pCtx->cbMaxChunkSize;
486 void *pvChunk = RTMemAlloc(cbChunkMax);
487 if (!pvChunk)
488 return VERR_NO_MEMORY;
489 uint32_t cbChunkRead = 0;
490
491 uint64_t cbFileSize = 0; /* Total file size (in bytes). */
492 uint64_t cbFileWritten = 0; /* Written bytes. */
493
494 /*
495 * Create and query the (unique) drop target directory in the user's temporary directory.
496 */
497 int rc = pDroppedFiles->OpenTemp(0 /* fFlags */);
498 if (RT_FAILURE(rc))
499 {
500 RTMemFree(pvChunk);
501 return VERR_NO_MEMORY;
502 }
503
504 const char *pszDropDir = pDroppedFiles->GetDirAbs();
505 AssertPtr(pszDropDir);
506
507 /*
508 * Enter the main loop of retieving files + directories.
509 */
510 DnDURIObject objFile(DnDURIObject::File);
511
512 char szPathName[RTPATH_MAX] = { 0 };
513 uint32_t cbPathName = 0;
514 uint32_t fFlags = 0;
515 uint32_t fMode = 0;
516
517 /*
518 * Only wait for new incoming commands for protocol v3 and up.
519 * The older protocols did not have any data accounting available, so
520 * we simply tried to receive as much data as available and bail out.
521 */
522 const bool fWait = pCtx->uProtocol >= 3;
523
524 do
525 {
526 LogFlowFunc(("Wating for new message ...\n"));
527
528 uint32_t uNextMsg;
529 uint32_t cNextParms;
530 rc = vbglR3DnDGetNextMsgType(pCtx, &uNextMsg, &cNextParms, fWait);
531 if (RT_SUCCESS(rc))
532 {
533 LogFlowFunc(("uNextMsg=%RU32, cNextParms=%RU32\n", uNextMsg, cNextParms));
534
535 switch (uNextMsg)
536 {
537 case HOST_DND_HG_SND_DIR:
538 {
539 rc = vbglR3DnDHGRecvDir(pCtx,
540 szPathName,
541 sizeof(szPathName),
542 &cbPathName,
543 &fMode);
544 LogFlowFunc(("HOST_DND_HG_SND_DIR pszPathName=%s, cbPathName=%RU32, fMode=0x%x, rc=%Rrc\n",
545 szPathName, cbPathName, fMode, rc));
546
547 char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName);
548 if (pszPathAbs)
549 {
550#ifdef RT_OS_WINDOWS
551 uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL;
552#else
553 uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRWXU;
554#endif
555 rc = RTDirCreate(pszPathAbs, fCreationMode, 0);
556 if (RT_SUCCESS(rc))
557 rc = pDroppedFiles->AddDir(pszPathAbs);
558
559 if ( RT_SUCCESS(rc)
560 && fDoAccounting)
561 {
562 Assert(cToRecvObjs);
563 cToRecvObjs--;
564 }
565
566 RTStrFree(pszPathAbs);
567 }
568 else
569 rc = VERR_NO_MEMORY;
570 break;
571 }
572 case HOST_DND_HG_SND_FILE_HDR:
573 case HOST_DND_HG_SND_FILE_DATA:
574 {
575 if (uNextMsg == HOST_DND_HG_SND_FILE_HDR)
576 {
577 rc = vbglR3DnDHGRecvFileHdr(pCtx,
578 szPathName,
579 sizeof(szPathName),
580 &fFlags,
581 &fMode,
582 &cbFileSize);
583 LogFlowFunc(("HOST_DND_HG_SND_FILE_HDR: "
584 "szPathName=%s, fFlags=0x%x, fMode=0x%x, cbFileSize=%RU64, rc=%Rrc\n",
585 szPathName, fFlags, fMode, cbFileSize, rc));
586 }
587 else
588 {
589 rc = vbglR3DnDHGRecvFileData(pCtx,
590 szPathName,
591 sizeof(szPathName),
592 &cbPathName,
593 pvChunk,
594 cbChunkMax,
595 &cbChunkRead,
596 &fMode);
597
598 LogFlowFunc(("HOST_DND_HG_SND_FILE_DATA: "
599 "szPathName=%s, cbPathName=%RU32, cbChunkRead=%RU32, fMode=0x%x, rc=%Rrc\n",
600 szPathName, cbPathName, cbChunkRead, fMode, rc));
601 }
602
603 if ( RT_SUCCESS(rc)
604 && ( uNextMsg == HOST_DND_HG_SND_FILE_HDR
605 /* Protocol v1 always sends the file name, so opening the file every time. */
606 || pCtx->uProtocol <= 1)
607 )
608 {
609 char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName);
610 if (pszPathAbs)
611 {
612 LogFlowFunc(("Opening pszPathName=%s, cbPathName=%RU32, fMode=0x%x, cbFileSize=%zu\n",
613 szPathName, cbPathName, fMode, cbFileSize));
614
615 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
616 if (pCtx->uProtocol <= 1)
617 fOpen |= RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND;
618 else
619 fOpen |= RTFILE_O_CREATE_REPLACE;
620
621 /* Is there already a file open, e.g. in transfer? */
622 if (!objFile.IsOpen())
623 {
624 RTCString strPathAbs(pszPathAbs);
625#ifdef RT_OS_WINDOWS
626 uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL;
627#else
628 uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR;
629#endif
630 rc = objFile.OpenEx(strPathAbs, DnDURIObject::File, DnDURIObject::Target, fOpen, fCreationMode);
631 if (RT_SUCCESS(rc))
632 {
633 rc = pDroppedFiles->AddFile(strPathAbs.c_str());
634 if (RT_SUCCESS(rc))
635 {
636 cbFileWritten = 0;
637
638 if (pCtx->uProtocol >= 2) /* Set the expected file size. */
639 objFile.SetSize(cbFileSize);
640 }
641 }
642 }
643 else
644 {
645 AssertMsgFailed(("ObjType=%RU32, Proto=%RU32\n", objFile.GetType(), pCtx->uProtocol));
646 rc = VERR_WRONG_ORDER;
647 }
648
649 RTStrFree(pszPathAbs);
650 }
651 else
652 rc = VERR_NO_MEMORY;
653 }
654
655 if ( RT_SUCCESS(rc)
656 && uNextMsg == HOST_DND_HG_SND_FILE_DATA)
657 {
658 uint32_t cbChunkWritten;
659 rc = objFile.Write(pvChunk, cbChunkRead, &cbChunkWritten);
660 if (RT_SUCCESS(rc))
661 {
662 LogFlowFunc(("HOST_DND_HG_SND_FILE_DATA "
663 "cbChunkRead=%RU32, cbChunkWritten=%RU32, cbFileWritten=%RU64 cbFileSize=%RU64\n",
664 cbChunkRead, cbChunkWritten, cbFileWritten + cbChunkWritten, cbFileSize));
665
666 cbFileWritten += cbChunkWritten;
667
668 if (fDoAccounting)
669 {
670 Assert(cbChunkRead <= cbToRecvBytes);
671 cbToRecvBytes -= cbChunkRead;
672 }
673 }
674 }
675
676 bool fClose = false;
677 if (pCtx->uProtocol >= 2)
678 {
679 /* Data transfer complete? Close the file. */
680 fClose = objFile.IsComplete();
681 if ( fClose
682 && fDoAccounting)
683 {
684 Assert(cToRecvObjs);
685 cToRecvObjs--;
686 }
687
688 /* Only since protocol v2 we know the file size upfront. */
689 Assert(cbFileWritten <= cbFileSize);
690 }
691 else
692 fClose = true; /* Always close the file after each chunk. */
693
694 if (fClose)
695 {
696 LogFlowFunc(("Closing file\n"));
697 objFile.Close();
698 }
699
700 break;
701 }
702 case HOST_DND_HG_EVT_CANCEL:
703 {
704 rc = vbglR3DnDHGRecvCancel(pCtx);
705 if (RT_SUCCESS(rc))
706 rc = VERR_CANCELLED;
707 break;
708 }
709 default:
710 {
711 LogFlowFunc(("Message %RU32 not supported\n", uNextMsg));
712 rc = VERR_NOT_SUPPORTED;
713 break;
714 }
715 }
716 }
717
718 if (RT_FAILURE(rc))
719 break;
720
721 if (fDoAccounting)
722 {
723 LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64\n", cbToRecvBytes, cToRecvObjs));
724 if ( !cbToRecvBytes
725 && !cToRecvObjs)
726 {
727 break;
728 }
729 }
730
731 } while (RT_SUCCESS(rc));
732
733 LogFlowFunc(("Loop ended with %Rrc\n", rc));
734
735 /* All URI data processed? */
736 if (rc == VERR_NO_DATA)
737 rc = VINF_SUCCESS;
738
739 /* Delete temp buffer again. */
740 if (pvChunk)
741 RTMemFree(pvChunk);
742
743 /* Cleanup on failure or if the user has canceled the operation or
744 * something else went wrong. */
745 if (RT_FAILURE(rc))
746 {
747 objFile.Close();
748 pDroppedFiles->Rollback();
749 }
750 else
751 {
752 /** @todo Compare the URI list with the dirs/files we really transferred. */
753 /** @todo Implement checksum verification, if any. */
754 }
755
756 /*
757 * Close the dropped files directory.
758 * Don't try to remove it here, however, as the files are being needed
759 * by the client's drag'n drop operation lateron.
760 */
761 int rc2 = pDroppedFiles->Reset(false /* fRemoveDropDir */);
762 if (RT_FAILURE(rc2)) /* Not fatal, don't report back to host. */
763 LogFlowFunc(("Closing dropped files directory failed with %Rrc\n", rc2));
764
765 LogFlowFuncLeaveRC(rc);
766 return rc;
767}
768
769static int vbglR3DnDHGRecvDataRaw(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr,
770 void *pvData, uint32_t cbData, uint32_t *pcbDataRecv)
771{
772 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
773 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
774 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
775 AssertReturn(cbData, VERR_INVALID_PARAMETER);
776 AssertPtrNullReturn(pcbDataRecv, VERR_INVALID_POINTER);
777
778 LogFlowFunc(("pvDate=%p, cbData=%RU32\n", pvData, cbData));
779
780 VBOXDNDHGSENDDATAMSG Msg;
781 RT_ZERO(Msg);
782 Msg.hdr.result = VERR_WRONG_ORDER;
783 Msg.hdr.u32ClientID = pCtx->uClientID;
784 Msg.hdr.u32Function = HOST_DND_HG_SND_DATA;
785
786 int rc;
787 if (pCtx->uProtocol < 3)
788 {
789 Msg.hdr.cParms = 5;
790
791 Msg.u.v1.uScreenId.SetUInt32(0);
792 Msg.u.v1.pvFormat.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
793 Msg.u.v1.cbFormat.SetUInt32(0);
794 Msg.u.v1.pvData.SetPtr(pvData, cbData);
795 Msg.u.v1.cbData.SetUInt32(0);
796
797 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
798 if (RT_SUCCESS(rc))
799 {
800 rc = Msg.hdr.result;
801 if ( RT_SUCCESS(rc)
802 || rc == VERR_BUFFER_OVERFLOW)
803 {
804 /** @todo r=bird: The VERR_BUFFER_OVERFLOW case is probably broken as the
805 * status isn't returned to the caller (vbglR3DnDHGRecvDataLoop).
806 * This was the case before fixing the uninitalized variable. As
807 * other V0-2 protocol functions have been marked deprecated, it's
808 * probably a good idea to just remove this code and tell the 1-2 users
809 * to upgrade the host instead. Unused and untested weird code like this
810 * is just hard+costly to maintain and liability.
811 * (VERR_BUFFER_OVERFLOW == weird, no disrespect intended) */
812
813 /* Unmarshal the whole message first. */
814 rc = Msg.u.v1.uScreenId.GetUInt32(&pDataHdr->uScreenId);
815 AssertRC(rc);
816 if (RT_SUCCESS(rc))
817 {
818 uint32_t cbFormatRecv;
819 rc = Msg.u.v1.cbFormat.GetUInt32(&cbFormatRecv);
820 AssertRC(rc);
821 if (RT_SUCCESS(rc))
822 {
823 uint32_t cbDataRecv;
824 rc = Msg.u.v1.cbData.GetUInt32(&cbDataRecv);
825 AssertRC(rc);
826 if (RT_SUCCESS(rc))
827 {
828 /*
829 * In case of VERR_BUFFER_OVERFLOW get the data sizes required
830 * for the format + data blocks.
831 */
832 if ( cbFormatRecv >= pDataHdr->cbMetaFmt
833 || cbDataRecv >= pDataHdr->cbMeta)
834 rc = VERR_TOO_MUCH_DATA;
835 else
836 {
837 pDataHdr->cbMetaFmt = cbFormatRecv;
838 if (pcbDataRecv)
839 *pcbDataRecv = cbDataRecv;
840 LogFlowFuncLeaveRC(rc);
841 return rc;
842 }
843 }
844 }
845 }
846 }
847 }
848 }
849 else /* Protocol v3 and up. */
850 {
851 Msg.hdr.cParms = 5;
852
853 Msg.u.v3.uContext.SetUInt32(0);
854 Msg.u.v3.pvData.SetPtr(pvData, cbData);
855 Msg.u.v3.cbData.SetUInt32(0);
856 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
857 Msg.u.v3.cbChecksum.SetUInt32(0);
858
859 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
860 if (RT_SUCCESS(rc))
861 {
862 rc = Msg.hdr.result;
863 if (RT_SUCCESS(rc))
864 {
865 uint32_t cbDataRecv;
866 rc = Msg.u.v3.cbData.GetUInt32(&cbDataRecv);
867 AssertRC(rc);
868 if (RT_SUCCESS(rc))
869 {
870 /** @todo Use checksum for validating the received data. */
871 if (pcbDataRecv)
872 *pcbDataRecv = cbDataRecv;
873 LogFlowFuncLeaveRC(rc);
874 return rc;
875 }
876 }
877
878 }
879 }
880
881 /* failure */
882 LogFlowFuncLeaveRC(rc);
883 return rc;
884}
885
886static int vbglR3DnDHGRecvDataHdr(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr)
887{
888 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
889 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
890
891 Assert(pCtx->uProtocol >= 3); /* Only for protocol v3 and up. */
892
893 VBOXDNDHGSENDDATAHDRMSG Msg;
894 RT_ZERO(Msg);
895 Msg.hdr.result = VERR_WRONG_ORDER;
896 Msg.hdr.u32ClientID = pCtx->uClientID;
897 Msg.hdr.u32Function = HOST_DND_HG_SND_DATA_HDR;
898 Msg.hdr.cParms = 12;
899
900 Msg.uContext.SetUInt32(0);
901 Msg.uFlags.SetUInt32(0);
902 Msg.uScreenId.SetUInt32(0);
903 Msg.cbTotal.SetUInt64(0);
904 Msg.cbMeta.SetUInt32(0);
905 Msg.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
906 Msg.cbMetaFmt.SetUInt32(0);
907 Msg.cObjects.SetUInt64(0);
908 Msg.enmCompression.SetUInt32(0);
909 Msg.enmChecksumType.SetUInt32(0);
910 Msg.pvChecksum.SetPtr(pDataHdr->pvChecksum, pDataHdr->cbChecksum);
911 Msg.cbChecksum.SetUInt32(0);
912
913 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
914 if (RT_SUCCESS(rc))
915 {
916 rc = Msg.hdr.result;
917 if (RT_SUCCESS(rc))
918 {
919 /* Msg.uContext not needed here. */
920 Msg.uFlags.GetUInt32(&pDataHdr->uFlags);
921 Msg.uScreenId.GetUInt32(&pDataHdr->uScreenId);
922 Msg.cbTotal.GetUInt64(&pDataHdr->cbTotal);
923 Msg.cbMeta.GetUInt32(&pDataHdr->cbMeta);
924 Msg.cbMetaFmt.GetUInt32(&pDataHdr->cbMetaFmt);
925 Msg.cObjects.GetUInt64(&pDataHdr->cObjects);
926 Msg.enmCompression.GetUInt32(&pDataHdr->enmCompression);
927 Msg.enmChecksumType.GetUInt32((uint32_t *)&pDataHdr->enmChecksumType);
928 Msg.cbChecksum.GetUInt32(&pDataHdr->cbChecksum);
929 }
930 }
931
932 LogFlowFuncLeaveRC(rc);
933 return rc;
934}
935
936/** @todo Deprecated function; will be removed. */
937static int vbglR3DnDHGRecvMoreData(PVBGLR3GUESTDNDCMDCTX pCtx, void *pvData, uint32_t cbData, uint32_t *pcbDataRecv)
938{
939 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
940 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
941 AssertReturn(cbData, VERR_INVALID_PARAMETER);
942 AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER);
943
944 VBOXDNDHGSENDMOREDATAMSG Msg;
945 RT_ZERO(Msg);
946 Msg.hdr.result = VERR_WRONG_ORDER;
947 Msg.hdr.u32ClientID = pCtx->uClientID;
948 Msg.hdr.u32Function = HOST_DND_HG_SND_MORE_DATA;
949 Msg.hdr.cParms = 2;
950
951 Msg.pvData.SetPtr(pvData, cbData);
952 Msg.cbData.SetUInt32(0);
953
954 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
955 if (RT_SUCCESS(rc))
956 {
957 rc = Msg.hdr.result;
958 if ( RT_SUCCESS(rc)
959 || rc == VERR_BUFFER_OVERFLOW)
960 {
961 rc = Msg.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
962 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
963 }
964 }
965 return rc;
966}
967
968static int vbglR3DnDHGRecvDataLoop(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr,
969 void **ppvData, uint64_t *pcbData)
970{
971 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
972 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
973 AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
974 AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
975
976 int rc;
977 uint32_t cbDataRecv;
978
979 LogFlowFuncEnter();
980
981 if (pCtx->uProtocol < 3)
982 {
983 uint64_t cbDataTmp = pCtx->cbMaxChunkSize;
984 void *pvDataTmp = RTMemAlloc(cbDataTmp);
985
986 if (!cbDataTmp)
987 return VERR_NO_MEMORY;
988
989 /*
990 * Protocols < v3 contain the header information in every HOST_DND_HG_SND_DATA
991 * message, so do the actual retrieving immediately.
992 *
993 * Also, the initial implementation used VERR_BUFFER_OVERFLOW as a return code to
994 * indicate that there will be more data coming in after the initial data chunk. There
995 * was no way of telling the total data size upfront (in form of a header or some such),
996 * so also handle this case to not break backwards compatibility.
997 */
998 rc = vbglR3DnDHGRecvDataRaw(pCtx, pDataHdr, pvDataTmp, pCtx->cbMaxChunkSize, &cbDataRecv);
999
1000 /* See comment above. */
1001 while (rc == VERR_BUFFER_OVERFLOW)
1002 {
1003 uint32_t uNextMsg;
1004 uint32_t cNextParms;
1005 rc = vbglR3DnDGetNextMsgType(pCtx, &uNextMsg, &cNextParms, false /* fBlock */);
1006 if (RT_SUCCESS(rc))
1007 {
1008 switch(uNextMsg)
1009 {
1010 case HOST_DND_HG_SND_MORE_DATA:
1011 {
1012 /** @todo r=andy Don't use reallocate here; can go wrong with *really* big URI lists.
1013 * Instead send as many URI entries as possible per chunk and add those entries
1014 * to our to-process list for immediata processing. Repeat the step after processing then. */
1015 LogFlowFunc(("HOST_DND_HG_SND_MORE_DATA cbDataTotal: %RU64 -> %RU64\n",
1016 cbDataTmp, cbDataTmp + pCtx->cbMaxChunkSize));
1017 void *pvDataNew = RTMemRealloc(pvDataTmp, cbDataTmp + pCtx->cbMaxChunkSize);
1018 if (!pvDataNew)
1019 {
1020 rc = VERR_NO_MEMORY;
1021 break;
1022 }
1023
1024 pvDataTmp = pvDataNew;
1025
1026 uint8_t *pvDataOff = (uint8_t *)pvDataTmp + cbDataTmp;
1027 rc = vbglR3DnDHGRecvMoreData(pCtx, pvDataOff, pCtx->cbMaxChunkSize, &cbDataRecv);
1028 if ( RT_SUCCESS(rc)
1029 || rc == VERR_BUFFER_OVERFLOW) /* Still can return VERR_BUFFER_OVERFLOW. */
1030 {
1031 cbDataTmp += cbDataRecv;
1032 }
1033 break;
1034 }
1035 case HOST_DND_HG_EVT_CANCEL:
1036 default:
1037 {
1038 rc = vbglR3DnDHGRecvCancel(pCtx);
1039 if (RT_SUCCESS(rc))
1040 rc = VERR_CANCELLED;
1041 break;
1042 }
1043 }
1044 }
1045 }
1046
1047 if (RT_SUCCESS(rc))
1048 {
1049 /* There was no way of telling the total data size upfront
1050 * (in form of a header or some such), so set the total data size here. */
1051 pDataHdr->cbTotal = cbDataTmp;
1052
1053 *ppvData = pvDataTmp;
1054 *pcbData = cbDataTmp;
1055 }
1056 else
1057 RTMemFree(pvDataTmp);
1058 }
1059 else /* Protocol v3 and up. */
1060 {
1061 rc = vbglR3DnDHGRecvDataHdr(pCtx, pDataHdr);
1062 if (RT_SUCCESS(rc))
1063 {
1064 LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32\n", pDataHdr->cbTotal, pDataHdr->cbMeta));
1065 if (pDataHdr->cbMeta)
1066 {
1067 uint64_t cbDataTmp = 0;
1068 void *pvDataTmp = RTMemAlloc(pDataHdr->cbMeta);
1069 if (!pvDataTmp)
1070 rc = VERR_NO_MEMORY;
1071
1072 if (RT_SUCCESS(rc))
1073 {
1074 uint8_t *pvDataOff = (uint8_t *)pvDataTmp;
1075 while (cbDataTmp < pDataHdr->cbMeta)
1076 {
1077 rc = vbglR3DnDHGRecvDataRaw(pCtx, pDataHdr,
1078 pvDataOff, RT_MIN(pDataHdr->cbMeta - cbDataTmp, pCtx->cbMaxChunkSize),
1079 &cbDataRecv);
1080 if (RT_SUCCESS(rc))
1081 {
1082 LogFlowFunc(("cbDataRecv=%RU32, cbDataTmp=%RU64\n", cbDataRecv, cbDataTmp));
1083 Assert(cbDataTmp + cbDataRecv <= pDataHdr->cbMeta);
1084 cbDataTmp += cbDataRecv;
1085 pvDataOff += cbDataRecv;
1086 }
1087 else
1088 break;
1089 }
1090
1091 if (RT_SUCCESS(rc))
1092 {
1093 Assert(cbDataTmp == pDataHdr->cbMeta);
1094
1095 LogFlowFunc(("Received %RU64 bytes of data\n", cbDataTmp));
1096
1097 *ppvData = pvDataTmp;
1098 *pcbData = cbDataTmp;
1099 }
1100 else
1101 RTMemFree(pvDataTmp);
1102 }
1103 }
1104 else
1105 {
1106 *ppvData = NULL;
1107 *pcbData = 0;
1108 }
1109 }
1110 }
1111
1112 LogFlowFuncLeaveRC(rc);
1113 return rc;
1114}
1115
1116/** @todo Replace the parameters (except pCtx) with PVBOXDNDSNDDATAHDR. Later. */
1117/** @todo Hand in the DnDURIList + DnDDroppedFiles objects so that this function
1118 * can fill it directly instead of passing huge blobs of data around. */
1119static int vbglR3DnDHGRecvDataMain(PVBGLR3GUESTDNDCMDCTX pCtx,
1120 uint32_t *puScreenId,
1121 char **ppszFormat,
1122 uint32_t *pcbFormat,
1123 void **ppvData,
1124 uint32_t *pcbData)
1125{
1126 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1127 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
1128 AssertPtrReturn(ppszFormat, VERR_INVALID_POINTER);
1129 AssertPtrReturn(pcbFormat, VERR_INVALID_POINTER);
1130 AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
1131 AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
1132
1133 VBOXDNDDATAHDR dataHdr; /** @todo See todo above. */
1134 RT_ZERO(dataHdr);
1135
1136 dataHdr.cbMetaFmt = _64K; /** @todo Make this configurable? */
1137 dataHdr.pvMetaFmt = RTMemAlloc(dataHdr.cbMetaFmt);
1138 if (!dataHdr.pvMetaFmt)
1139 return VERR_NO_MEMORY;
1140
1141 DnDURIList lstURI;
1142 DnDDroppedFiles droppedFiles;
1143
1144 void *pvData; /** @todo See todo above. */
1145 uint64_t cbData; /** @todo See todo above. */
1146
1147 int rc = vbglR3DnDHGRecvDataLoop(pCtx, &dataHdr, &pvData, &cbData);
1148 if (RT_SUCCESS(rc))
1149 {
1150 /**
1151 * Check if this is an URI event. If so, let VbglR3 do all the actual
1152 * data transfer + file/directory creation internally without letting
1153 * the caller know.
1154 *
1155 * This keeps the actual (guest OS-)dependent client (like VBoxClient /
1156 * VBoxTray) small by not having too much redundant code.
1157 */
1158 Assert(dataHdr.cbMetaFmt);
1159 AssertPtr(dataHdr.pvMetaFmt);
1160 if (DnDMIMEHasFileURLs((char *)dataHdr.pvMetaFmt, dataHdr.cbMetaFmt))
1161 {
1162 AssertPtr(pvData);
1163 Assert(cbData);
1164 rc = lstURI.RootFromURIData(pvData, cbData, 0 /* fFlags */);
1165 if (RT_SUCCESS(rc))
1166 rc = vbglR3DnDHGRecvURIData(pCtx, &dataHdr, &droppedFiles);
1167
1168 if (RT_SUCCESS(rc)) /** @todo Remove this block as soon as we hand in DnDURIList. */
1169 {
1170 if (pvData)
1171 {
1172 /* Reuse data buffer to fill in the transformed URI file list. */
1173 RTMemFree(pvData);
1174 pvData = NULL;
1175 }
1176
1177 RTCString strData = lstURI.RootToString(droppedFiles.GetDirAbs());
1178 Assert(!strData.isEmpty());
1179
1180 cbData = strData.length() + 1;
1181 LogFlowFunc(("URI list has %zu bytes\n", cbData));
1182
1183 pvData = RTMemAlloc(cbData);
1184 if (pvData)
1185 {
1186 memcpy(pvData, strData.c_str(), cbData);
1187 }
1188 else
1189 rc = VERR_NO_MEMORY;
1190 }
1191 }
1192 else /* Raw data. */
1193 {
1194 const uint32_t cbDataRaw = dataHdr.cbMetaFmt;
1195 if (cbData >= cbDataRaw)
1196 {
1197 if (cbDataRaw)
1198 memcpy(pvData, dataHdr.pvMetaFmt, cbDataRaw);
1199 cbData = cbDataRaw;
1200 }
1201 else
1202 rc = VERR_BUFFER_OVERFLOW;
1203 }
1204 }
1205
1206 if ( RT_FAILURE(rc)
1207 && rc != VERR_CANCELLED)
1208 {
1209 if (dataHdr.pvMetaFmt)
1210 RTMemFree(dataHdr.pvMetaFmt);
1211 if (pvData)
1212 RTMemFree(pvData);
1213
1214 int rc2 = VbglR3DnDHGSendProgress(pCtx, DND_PROGRESS_ERROR, 100 /* Percent */, rc);
1215 if (RT_FAILURE(rc2))
1216 LogFlowFunc(("Unable to send progress error %Rrc to host: %Rrc\n", rc, rc2));
1217 }
1218 else if (RT_SUCCESS(rc))
1219 {
1220 *ppszFormat = (char *)dataHdr.pvMetaFmt;
1221 *pcbFormat = dataHdr.cbMetaFmt;
1222 *ppvData = pvData;
1223 *pcbData = cbData;
1224 }
1225
1226 LogFlowFuncLeaveRC(rc);
1227 return rc;
1228}
1229
1230static int vbglR3DnDGHRecvPending(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puScreenId)
1231{
1232 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1233 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
1234
1235 VBOXDNDGHREQPENDINGMSG Msg;
1236 RT_ZERO(Msg);
1237 Msg.hdr.result = VERR_WRONG_ORDER;
1238 Msg.hdr.u32ClientID = pCtx->uClientID;
1239 Msg.hdr.u32Function = HOST_DND_GH_REQ_PENDING;
1240
1241 if (pCtx->uProtocol < 3)
1242 {
1243 Msg.hdr.cParms = 1;
1244
1245 Msg.u.v1.uScreenId.SetUInt32(0);
1246 }
1247 else
1248 {
1249 Msg.hdr.cParms = 2;
1250
1251 /** @todo Context ID not used yet. */
1252 Msg.u.v3.uContext.SetUInt32(0);
1253 Msg.u.v3.uScreenId.SetUInt32(0);
1254 }
1255
1256 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1257 if (RT_SUCCESS(rc))
1258 {
1259 rc = Msg.hdr.result;
1260 if (RT_SUCCESS(rc))
1261 {
1262 if (pCtx->uProtocol < 3)
1263 {
1264 rc = Msg.u.v1.uScreenId.GetUInt32(puScreenId); AssertRC(rc);
1265 }
1266 else
1267 {
1268 /** @todo Context ID not used yet. */
1269 rc = Msg.u.v3.uContext.GetUInt32(puScreenId); AssertRC(rc);
1270 }
1271 }
1272 }
1273
1274 return rc;
1275}
1276
1277static int vbglR3DnDGHRecvDropped(PVBGLR3GUESTDNDCMDCTX pCtx,
1278 char *pszFormat,
1279 uint32_t cbFormat,
1280 uint32_t *pcbFormatRecv,
1281 uint32_t *puAction)
1282{
1283 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1284 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
1285 AssertReturn(cbFormat, VERR_INVALID_PARAMETER);
1286 AssertPtrReturn(pcbFormatRecv, VERR_INVALID_POINTER);
1287 AssertPtrReturn(puAction, VERR_INVALID_POINTER);
1288
1289 VBOXDNDGHDROPPEDMSG Msg;
1290 RT_ZERO(Msg);
1291 Msg.hdr.result = VERR_WRONG_ORDER;
1292 Msg.hdr.u32ClientID = pCtx->uClientID;
1293 Msg.hdr.u32Function = HOST_DND_GH_EVT_DROPPED;
1294
1295 if (pCtx->uProtocol < 3)
1296 {
1297 Msg.hdr.cParms = 3;
1298
1299 Msg.u.v1.pvFormat.SetPtr(pszFormat, cbFormat);
1300 Msg.u.v1.cbFormat.SetUInt32(0);
1301 Msg.u.v1.uAction.SetUInt32(0);
1302 }
1303 else
1304 {
1305 Msg.hdr.cParms = 4;
1306
1307 Msg.u.v3.uContext.SetUInt32(0);
1308 Msg.u.v3.pvFormat.SetPtr(pszFormat, cbFormat);
1309 Msg.u.v3.cbFormat.SetUInt32(0);
1310 Msg.u.v3.uAction.SetUInt32(0);
1311 }
1312
1313 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1314 if (RT_SUCCESS(rc))
1315 {
1316 rc = Msg.hdr.result;
1317 if (RT_SUCCESS(rc))
1318 {
1319 if (pCtx->uProtocol < 3)
1320 {
1321 rc = Msg.u.v1.cbFormat.GetUInt32(pcbFormatRecv); AssertRC(rc);
1322 rc = Msg.u.v1.uAction.GetUInt32(puAction); AssertRC(rc);
1323 }
1324 else
1325 {
1326 /** @todo Context ID not used yet. */
1327 rc = Msg.u.v3.cbFormat.GetUInt32(pcbFormatRecv); AssertRC(rc);
1328 rc = Msg.u.v3.uAction.GetUInt32(puAction); AssertRC(rc);
1329 }
1330
1331 AssertReturn(cbFormat >= *pcbFormatRecv, VERR_TOO_MUCH_DATA);
1332 }
1333 }
1334
1335 return rc;
1336}
1337
1338
1339/*********************************************************************************************************************************
1340* Public functions *
1341*********************************************************************************************************************************/
1342
1343VBGLR3DECL(int) VbglR3DnDConnect(PVBGLR3GUESTDNDCMDCTX pCtx)
1344{
1345 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1346
1347 /* Initialize header */
1348 VBoxGuestHGCMConnectInfo Info;
1349 RT_ZERO(Info.Loc.u);
1350 Info.result = VERR_WRONG_ORDER;
1351 Info.u32ClientID = UINT32_MAX; /* try make valgrind shut up. */
1352 Info.Loc.type = VMMDevHGCMLoc_LocalHost_Existing;
1353
1354 int rc = RTStrCopy(Info.Loc.u.host.achName, sizeof(Info.Loc.u.host.achName), "VBoxDragAndDropSvc");
1355 if (RT_FAILURE(rc))
1356 return rc;
1357
1358 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CONNECT, &Info, sizeof(Info));
1359 if (RT_SUCCESS(rc))
1360 {
1361 rc = Info.result;
1362 if (RT_SUCCESS(rc))
1363 {
1364 /* Set the default protocol version to use. */
1365 pCtx->uProtocol = 3;
1366
1367 Assert(Info.u32ClientID);
1368 pCtx->uClientID = Info.u32ClientID;
1369 }
1370 }
1371
1372 if (RT_FAILURE(rc))
1373 return rc;
1374
1375 /*
1376 * Get the VM's session ID.
1377 * This is not fatal in case we're running with an ancient VBox version.
1378 */
1379 pCtx->uSessionID = 0;
1380 int rc2 = VbglR3GetSessionId(&pCtx->uSessionID);
1381 LogFlowFunc(("uSessionID=%RU64, rc=%Rrc\n", pCtx->uSessionID, rc2));
1382
1383 /*
1384 * Check if the host is >= VBox 5.0 which in case supports GUEST_DND_CONNECT.
1385 */
1386 bool fSupportsConnectReq = false;
1387 if (RT_SUCCESS(rc))
1388 {
1389 /* The guest property service might not be available. Not fatal. */
1390 uint32_t uGuestPropSvcClientID;
1391 rc2 = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
1392 if (RT_SUCCESS(rc2))
1393 {
1394 char *pszHostVersion;
1395 rc2 = VbglR3GuestPropReadValueAlloc(uGuestPropSvcClientID, "/VirtualBox/HostInfo/VBoxVer", &pszHostVersion);
1396 if (RT_SUCCESS(rc2))
1397 {
1398 fSupportsConnectReq = RTStrVersionCompare(pszHostVersion, "5.0") >= 0;
1399 LogFlowFunc(("pszHostVersion=%s, fSupportsConnectReq=%RTbool\n", pszHostVersion, fSupportsConnectReq));
1400 VbglR3GuestPropReadValueFree(pszHostVersion);
1401 }
1402
1403 VbglR3GuestPropDisconnect(uGuestPropSvcClientID);
1404 }
1405
1406 if (RT_FAILURE(rc2))
1407 LogFlowFunc(("Retrieving host version failed with rc=%Rrc\n", rc2));
1408 }
1409
1410 if (fSupportsConnectReq)
1411 {
1412 /*
1413 * Try sending the connect message to tell the protocol version to use.
1414 * Note: This might fail when the Guest Additions run on an older VBox host (< VBox 5.0) which
1415 * does not implement this command.
1416 */
1417 VBOXDNDCONNECTMSG Msg;
1418 RT_ZERO(Msg);
1419 Msg.hdr.result = VERR_WRONG_ORDER;
1420 Msg.hdr.u32ClientID = pCtx->uClientID;
1421 Msg.hdr.u32Function = GUEST_DND_CONNECT;
1422
1423 if (pCtx->uProtocol < 3)
1424 {
1425 Msg.hdr.cParms = 2;
1426
1427 Msg.u.v2.uProtocol.SetUInt32(pCtx->uProtocol);
1428 Msg.u.v2.uFlags.SetUInt32(0); /* Unused at the moment. */
1429 }
1430 else
1431 {
1432 Msg.hdr.cParms = 3;
1433
1434 /** @todo Context ID not used yet. */
1435 Msg.u.v3.uContext.SetUInt32(0);
1436 Msg.u.v3.uProtocol.SetUInt32(pCtx->uProtocol);
1437 Msg.u.v3.uFlags.SetUInt32(0); /* Unused at the moment. */
1438 }
1439
1440 rc2 = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1441 if (RT_SUCCESS(rc2))
1442 rc2 = Msg.hdr.result; /* Not fatal. */
1443
1444 if (RT_FAILURE(rc2))
1445 fSupportsConnectReq = false;
1446
1447 LogFlowFunc(("Connection request ended with rc=%Rrc\n", rc2));
1448 }
1449
1450 /* GUEST_DND_CONNECT not supported; play safe here and just use protocol v1. */
1451 if (!fSupportsConnectReq)
1452 pCtx->uProtocol = 1; /* Fall back to protocol version 1 (< VBox 5.0). */
1453
1454 pCtx->cbMaxChunkSize = _64K; /** @todo Use a scratch buffer on the heap? */
1455
1456 LogFlowFunc(("uClient=%RU32, uProtocol=%RU32, rc=%Rrc\n", pCtx->uClientID, pCtx->uProtocol, rc));
1457 return rc;
1458}
1459
1460VBGLR3DECL(int) VbglR3DnDDisconnect(PVBGLR3GUESTDNDCMDCTX pCtx)
1461{
1462 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1463
1464 VBoxGuestHGCMDisconnectInfo Info;
1465 Info.result = VERR_WRONG_ORDER;
1466 Info.u32ClientID = pCtx->uClientID;
1467
1468 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_DISCONNECT, &Info, sizeof(Info));
1469 if (RT_SUCCESS(rc))
1470 rc = Info.result;
1471
1472 return rc;
1473}
1474
1475VBGLR3DECL(int) VbglR3DnDRecvNextMsg(PVBGLR3GUESTDNDCMDCTX pCtx, CPVBGLR3DNDHGCMEVENT pEvent)
1476{
1477 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1478 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1479
1480 const uint32_t cbFormatMax = pCtx->cbMaxChunkSize;
1481
1482 uint32_t uMsg = 0;
1483 uint32_t cParms = 0;
1484 int rc = vbglR3DnDGetNextMsgType(pCtx, &uMsg, &cParms, true /* fWait */);
1485 if (RT_SUCCESS(rc))
1486 {
1487 /* Check for VM session change. */
1488 uint64_t uSessionID;
1489 int rc2 = VbglR3GetSessionId(&uSessionID);
1490 if ( RT_SUCCESS(rc2)
1491 && (uSessionID != pCtx->uSessionID))
1492 {
1493 LogFlowFunc(("VM session ID changed to %RU64, doing reconnect\n", uSessionID));
1494
1495 /* Try a reconnect to the DnD service. */
1496 rc2 = VbglR3DnDDisconnect(pCtx);
1497 AssertRC(rc2);
1498 rc2 = VbglR3DnDConnect(pCtx);
1499 AssertRC(rc2);
1500
1501 /* At this point we continue processing the messsages with the new client ID. */
1502 }
1503 }
1504
1505 if (RT_SUCCESS(rc))
1506 {
1507 pEvent->uType = uMsg;
1508
1509 switch(uMsg)
1510 {
1511 case HOST_DND_HG_EVT_ENTER:
1512 case HOST_DND_HG_EVT_MOVE:
1513 case HOST_DND_HG_EVT_DROPPED:
1514 {
1515 pEvent->pszFormats = static_cast<char*>(RTMemAlloc(cbFormatMax));
1516 if (!pEvent->pszFormats)
1517 rc = VERR_NO_MEMORY;
1518
1519 if (RT_SUCCESS(rc))
1520 rc = vbglR3DnDHGRecvAction(pCtx,
1521 uMsg,
1522 &pEvent->uScreenId,
1523 &pEvent->u.a.uXpos,
1524 &pEvent->u.a.uYpos,
1525 &pEvent->u.a.uDefAction,
1526 &pEvent->u.a.uAllActions,
1527 pEvent->pszFormats,
1528 cbFormatMax,
1529 &pEvent->cbFormats);
1530 break;
1531 }
1532 case HOST_DND_HG_EVT_LEAVE:
1533 {
1534 rc = vbglR3DnDHGRecvLeave(pCtx);
1535 break;
1536 }
1537 case HOST_DND_HG_SND_DATA:
1538 /* Protocol v1 + v2: Also contains the header data.
1539 * Note: Fall through is intentional. */
1540 case HOST_DND_HG_SND_DATA_HDR:
1541 {
1542 rc = vbglR3DnDHGRecvDataMain(pCtx,
1543 /* Screen ID */
1544 &pEvent->uScreenId,
1545 /* Format */
1546 &pEvent->pszFormats,
1547 &pEvent->cbFormats,
1548 /* Data */
1549 &pEvent->u.b.pvData,
1550 &pEvent->u.b.cbData);
1551 break;
1552 }
1553 case HOST_DND_HG_SND_MORE_DATA:
1554 case HOST_DND_HG_SND_DIR:
1555 case HOST_DND_HG_SND_FILE_DATA:
1556 {
1557 /*
1558 * All messages in this case are handled internally
1559 * by vbglR3DnDHGRecvDataMain() and must be specified
1560 * by a preceding HOST_DND_HG_SND_DATA or HOST_DND_HG_SND_DATA_HDR
1561 * calls.
1562 */
1563 rc = VERR_WRONG_ORDER;
1564 break;
1565 }
1566 case HOST_DND_HG_EVT_CANCEL:
1567 {
1568 rc = vbglR3DnDHGRecvCancel(pCtx);
1569 break;
1570 }
1571#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1572 case HOST_DND_GH_REQ_PENDING:
1573 {
1574 rc = vbglR3DnDGHRecvPending(pCtx, &pEvent->uScreenId);
1575 break;
1576 }
1577 case HOST_DND_GH_EVT_DROPPED:
1578 {
1579 pEvent->pszFormats = static_cast<char*>(RTMemAlloc(cbFormatMax));
1580 if (!pEvent->pszFormats)
1581 rc = VERR_NO_MEMORY;
1582
1583 if (RT_SUCCESS(rc))
1584 rc = vbglR3DnDGHRecvDropped(pCtx,
1585 pEvent->pszFormats,
1586 cbFormatMax,
1587 &pEvent->cbFormats,
1588 &pEvent->u.a.uDefAction);
1589 break;
1590 }
1591#endif
1592 default:
1593 {
1594 rc = VERR_NOT_SUPPORTED;
1595 break;
1596 }
1597 }
1598 }
1599
1600 if (RT_FAILURE(rc))
1601 LogFlowFunc(("Returning error %Rrc\n", rc));
1602 return rc;
1603}
1604
1605VBGLR3DECL(int) VbglR3DnDHGSendAckOp(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uAction)
1606{
1607 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1608
1609 VBOXDNDHGACKOPMSG Msg;
1610 RT_ZERO(Msg);
1611 Msg.hdr.result = VERR_WRONG_ORDER;
1612 Msg.hdr.u32ClientID = pCtx->uClientID;
1613 Msg.hdr.u32Function = GUEST_DND_HG_ACK_OP;
1614
1615 LogFlowFunc(("uProto=%RU32\n", pCtx->uProtocol));
1616
1617 if (pCtx->uProtocol < 3)
1618 {
1619 Msg.hdr.cParms = 1;
1620
1621 Msg.u.v1.uAction.SetUInt32(uAction);
1622 }
1623 else
1624 {
1625 Msg.hdr.cParms = 2;
1626
1627 /** @todo Context ID not used yet. */
1628 Msg.u.v3.uContext.SetUInt32(0);
1629 Msg.u.v3.uAction.SetUInt32(uAction);
1630 }
1631
1632 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1633 if (RT_SUCCESS(rc))
1634 rc = Msg.hdr.result;
1635
1636 return rc;
1637}
1638
1639VBGLR3DECL(int) VbglR3DnDHGSendReqData(PVBGLR3GUESTDNDCMDCTX pCtx, const char* pcszFormat)
1640{
1641 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1642 AssertPtrReturn(pcszFormat, VERR_INVALID_POINTER);
1643
1644 VBOXDNDHGREQDATAMSG Msg;
1645 RT_ZERO(Msg);
1646 Msg.hdr.result = VERR_WRONG_ORDER;
1647 Msg.hdr.u32ClientID = pCtx->uClientID;
1648 Msg.hdr.u32Function = GUEST_DND_HG_REQ_DATA;
1649
1650 if (!RTStrIsValidEncoding(pcszFormat))
1651 return VERR_INVALID_PARAMETER;
1652
1653 const uint32_t cbFormat = (uint32_t)strlen(pcszFormat) + 1; /* Include termination */
1654
1655 if (pCtx->uProtocol < 3)
1656 {
1657 Msg.hdr.cParms = 1;
1658
1659 Msg.u.v1.pvFormat.SetPtr((void*)pcszFormat, cbFormat);
1660 }
1661 else
1662 {
1663 Msg.hdr.cParms = 3;
1664
1665 /** @todo Context ID not used yet. */
1666 Msg.u.v3.uContext.SetUInt32(0);
1667 Msg.u.v3.pvFormat.SetPtr((void*)pcszFormat, cbFormat);
1668 Msg.u.v3.cbFormat.SetUInt32(cbFormat);
1669 }
1670
1671 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1672 if (RT_SUCCESS(rc))
1673 rc = Msg.hdr.result;
1674
1675 return rc;
1676}
1677
1678VBGLR3DECL(int) VbglR3DnDHGSendProgress(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uStatus, uint8_t uPercent, int rcErr)
1679{
1680 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1681 AssertReturn(uStatus > DND_PROGRESS_UNKNOWN, VERR_INVALID_PARAMETER);
1682
1683 VBOXDNDHGEVTPROGRESSMSG Msg;
1684 RT_ZERO(Msg);
1685 Msg.hdr.result = VERR_WRONG_ORDER;
1686 Msg.hdr.u32ClientID = pCtx->uClientID;
1687 Msg.hdr.u32Function = uStatus;
1688
1689 if (pCtx->uProtocol < 3)
1690 {
1691 Msg.hdr.cParms = 3;
1692
1693 Msg.u.v1.uStatus.SetUInt32(uStatus);
1694 Msg.u.v1.uPercent.SetUInt32(uPercent);
1695 Msg.u.v1.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1696 }
1697 else
1698 {
1699 Msg.hdr.cParms = 4;
1700
1701 /** @todo Context ID not used yet. */
1702 Msg.u.v3.uContext.SetUInt32(0);
1703 Msg.u.v3.uStatus.SetUInt32(uStatus);
1704 Msg.u.v3.uPercent.SetUInt32(uPercent);
1705 Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1706 }
1707
1708 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1709 if (RT_SUCCESS(rc))
1710 rc = Msg.hdr.result;
1711
1712 return rc;
1713}
1714
1715#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1716VBGLR3DECL(int) VbglR3DnDGHSendAckPending(PVBGLR3GUESTDNDCMDCTX pCtx,
1717 uint32_t uDefAction, uint32_t uAllActions, const char* pcszFormats, uint32_t cbFormats)
1718{
1719 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1720 AssertPtrReturn(pcszFormats, VERR_INVALID_POINTER);
1721 AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
1722
1723 if (!RTStrIsValidEncoding(pcszFormats))
1724 return VERR_INVALID_UTF8_ENCODING;
1725
1726 VBOXDNDGHACKPENDINGMSG Msg;
1727 RT_ZERO(Msg);
1728 Msg.hdr.result = VERR_WRONG_ORDER;
1729 Msg.hdr.u32ClientID = pCtx->uClientID;
1730 Msg.hdr.u32Function = GUEST_DND_GH_ACK_PENDING;
1731
1732 if (pCtx->uProtocol < 3)
1733 {
1734 Msg.hdr.cParms = 3;
1735
1736 Msg.u.v1.uDefAction.SetUInt32(uDefAction);
1737 Msg.u.v1.uAllActions.SetUInt32(uAllActions);
1738 Msg.u.v1.pvFormats.SetPtr((void*)pcszFormats, cbFormats);
1739 }
1740 else
1741 {
1742 Msg.hdr.cParms = 5;
1743
1744 /** @todo Context ID not used yet. */
1745 Msg.u.v3.uContext.SetUInt32(0);
1746 Msg.u.v3.uDefAction.SetUInt32(uDefAction);
1747 Msg.u.v3.uAllActions.SetUInt32(uAllActions);
1748 Msg.u.v3.pvFormats.SetPtr((void*)pcszFormats, cbFormats);
1749 Msg.u.v3.cbFormats.SetUInt32(cbFormats);
1750 }
1751
1752 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1753 if (RT_SUCCESS(rc))
1754 rc = Msg.hdr.result;
1755
1756 return rc;
1757}
1758
1759static int vbglR3DnDGHSendDataInternal(PVBGLR3GUESTDNDCMDCTX pCtx,
1760 void *pvData, uint64_t cbData, PVBOXDNDSNDDATAHDR pDataHdr)
1761{
1762 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1763 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1764 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1765 /* cbAdditionalData is optional. */
1766 /* pDataHdr is optional in protocols < v3. */
1767
1768 int rc = VINF_SUCCESS;
1769
1770 /* For protocol v3 and up we need to send the data header first. */
1771 if (pCtx->uProtocol >= 3)
1772 {
1773 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
1774
1775 VBOXDNDGHSENDDATAHDRMSG Msg;
1776 RT_ZERO(Msg);
1777 Msg.hdr.result = VERR_WRONG_ORDER;
1778 Msg.hdr.u32ClientID = pCtx->uClientID;
1779 Msg.hdr.u32Function = GUEST_DND_GH_SND_DATA_HDR;
1780 Msg.hdr.cParms = 12;
1781
1782 Msg.uContext.SetUInt32(0); /** @todo Not used yet. */
1783 Msg.uFlags.SetUInt32(0); /** @todo Not used yet. */
1784 Msg.uScreenId.SetUInt32(0); /** @todo Not used for guest->host (yet). */
1785 Msg.cbTotal.SetUInt64(pDataHdr->cbTotal);
1786 Msg.cbMeta.SetUInt32(pDataHdr->cbMeta);
1787 Msg.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
1788 Msg.cbMetaFmt.SetUInt32(pDataHdr->cbMetaFmt);
1789 Msg.cObjects.SetUInt64(pDataHdr->cObjects);
1790 Msg.enmCompression.SetUInt32(0); /** @todo Not used yet. */
1791 Msg.enmChecksumType.SetUInt32(RTDIGESTTYPE_INVALID); /** @todo Not used yet. */
1792 Msg.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
1793 Msg.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
1794
1795 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1796 if (RT_SUCCESS(rc))
1797 rc = Msg.hdr.result;
1798
1799 LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU64, rc=%Rrc\n",
1800 pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects, rc));
1801 }
1802
1803 if (RT_SUCCESS(rc))
1804 {
1805 VBOXDNDGHSENDDATAMSG Msg;
1806 RT_ZERO(Msg);
1807 Msg.hdr.result = VERR_WRONG_ORDER;
1808 Msg.hdr.u32ClientID = pCtx->uClientID;
1809 Msg.hdr.u32Function = GUEST_DND_GH_SND_DATA;
1810
1811 if (pCtx->uProtocol >= 3)
1812 {
1813 Msg.hdr.cParms = 5;
1814
1815 Msg.u.v3.uContext.SetUInt32(0); /** @todo Not used yet. */
1816 Msg.u.v3.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
1817 Msg.u.v3.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
1818 }
1819 else
1820 {
1821 Msg.hdr.cParms = 2;
1822
1823 /* Total amount of bytes to send (meta data + all directory/file objects). */
1824 /* Note: Only supports uint32_t, so this is *not* a typo. */
1825 Msg.u.v1.cbTotalBytes.SetUInt32((uint32_t)pDataHdr->cbTotal);
1826 }
1827
1828 uint32_t cbCurChunk;
1829 const uint32_t cbMaxChunk = pCtx->cbMaxChunkSize;
1830 uint32_t cbSent = 0;
1831
1832 HGCMFunctionParameter *pParm = (pCtx->uProtocol >= 3)
1833 ? &Msg.u.v3.pvData
1834 : &Msg.u.v1.pvData;
1835 while (cbSent < cbData)
1836 {
1837 cbCurChunk = RT_MIN(cbData - cbSent, cbMaxChunk);
1838 pParm->SetPtr(static_cast<uint8_t *>(pvData) + cbSent, cbCurChunk);
1839 if (pCtx->uProtocol > 2)
1840 Msg.u.v3.cbData.SetUInt32(cbCurChunk);
1841
1842 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1843 if (RT_SUCCESS(rc))
1844 rc = Msg.hdr.result;
1845
1846 if (RT_FAILURE(rc))
1847 break;
1848
1849 cbSent += cbCurChunk;
1850 }
1851
1852 LogFlowFunc(("cbMaxChunk=%RU32, cbData=%RU64, cbSent=%RU32, rc=%Rrc\n",
1853 cbMaxChunk, cbData, cbSent, rc));
1854
1855 if (RT_SUCCESS(rc))
1856 Assert(cbSent == cbData);
1857 }
1858
1859 LogFlowFuncLeaveRC(rc);
1860 return rc;
1861}
1862
1863static int vbglR3DnDGHSendDir(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
1864{
1865 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1866 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1867 AssertReturn(pObj->GetType() == DnDURIObject::Directory, VERR_INVALID_PARAMETER);
1868
1869 RTCString strPath = pObj->GetDestPath();
1870 LogFlowFunc(("strDir=%s (%zu), fMode=0x%x\n",
1871 strPath.c_str(), strPath.length(), pObj->GetMode()));
1872
1873 if (strPath.length() > RTPATH_MAX)
1874 return VERR_INVALID_PARAMETER;
1875
1876 const uint32_t cbPath = (uint32_t)strPath.length() + 1; /* Include termination. */
1877
1878 VBOXDNDGHSENDDIRMSG Msg;
1879 RT_ZERO(Msg);
1880 Msg.hdr.result = VERR_WRONG_ORDER;
1881 Msg.hdr.u32ClientID = pCtx->uClientID;
1882 Msg.hdr.u32Function = GUEST_DND_GH_SND_DIR;
1883
1884 if (pCtx->uProtocol < 3)
1885 {
1886 Msg.hdr.cParms = 3;
1887
1888 Msg.u.v1.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)cbPath);
1889 Msg.u.v1.cbName.SetUInt32((uint32_t)cbPath);
1890 Msg.u.v1.fMode.SetUInt32(pObj->GetMode());
1891 }
1892 else
1893 {
1894 Msg.hdr.cParms = 4;
1895
1896 /** @todo Context ID not used yet. */
1897 Msg.u.v3.uContext.SetUInt32(0);
1898 Msg.u.v3.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)cbPath);
1899 Msg.u.v3.cbName.SetUInt32((uint32_t)cbPath);
1900 Msg.u.v3.fMode.SetUInt32(pObj->GetMode());
1901 }
1902
1903 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1904 if (RT_SUCCESS(rc))
1905 rc = Msg.hdr.result;
1906
1907 LogFlowFuncLeaveRC(rc);
1908 return rc;
1909}
1910
1911static int vbglR3DnDGHSendFile(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
1912{
1913 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1914 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1915 AssertReturn(pObj->GetType() == DnDURIObject::File, VERR_INVALID_PARAMETER);
1916 AssertReturn(pObj->IsOpen(), VERR_INVALID_STATE);
1917
1918 uint32_t cbBuf = _64K; /** @todo Make this configurable? */
1919 void *pvBuf = RTMemAlloc(cbBuf); /** @todo Make this buffer part of PVBGLR3GUESTDNDCMDCTX? */
1920 if (!pvBuf)
1921 return VERR_NO_MEMORY;
1922
1923 int rc;
1924
1925 RTCString strPath = pObj->GetDestPath();
1926
1927 LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", strPath.c_str(), strPath.length(),
1928 pObj->GetSize(), pObj->GetMode()));
1929 LogFlowFunc(("uProtocol=%RU32, uClientID=%RU32\n", pCtx->uProtocol, pCtx->uClientID));
1930
1931 if (pCtx->uProtocol >= 2) /* Protocol version 2 and up sends a file header first. */
1932 {
1933 VBOXDNDGHSENDFILEHDRMSG MsgHdr;
1934 RT_ZERO(MsgHdr);
1935 MsgHdr.hdr.result = VERR_WRONG_ORDER;
1936 MsgHdr.hdr.u32ClientID = pCtx->uClientID;
1937 MsgHdr.hdr.u32Function = GUEST_DND_GH_SND_FILE_HDR;
1938 MsgHdr.hdr.cParms = 6;
1939
1940 MsgHdr.uContext.SetUInt32(0); /* Context ID; unused at the moment. */
1941 MsgHdr.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)(strPath.length() + 1));
1942 MsgHdr.cbName.SetUInt32((uint32_t)(strPath.length() + 1));
1943 MsgHdr.uFlags.SetUInt32(0); /* Flags; unused at the moment. */
1944 MsgHdr.fMode.SetUInt32(pObj->GetMode()); /* File mode */
1945 MsgHdr.cbTotal.SetUInt64(pObj->GetSize()); /* File size (in bytes). */
1946
1947 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(MsgHdr)), &MsgHdr, sizeof(MsgHdr));
1948 if (RT_SUCCESS(rc))
1949 rc = MsgHdr.hdr.result;
1950
1951 LogFlowFunc(("Sending file header resulted in %Rrc\n", rc));
1952 }
1953 else
1954 rc = VINF_SUCCESS;
1955
1956 if (RT_SUCCESS(rc))
1957 {
1958 /*
1959 * Send the actual file data, chunk by chunk.
1960 */
1961 VBOXDNDGHSENDFILEDATAMSG Msg;
1962 RT_ZERO(Msg);
1963 Msg.hdr.result = VERR_WRONG_ORDER;
1964 Msg.hdr.u32ClientID = pCtx->uClientID;
1965 Msg.hdr.u32Function = GUEST_DND_GH_SND_FILE_DATA;
1966
1967 switch (pCtx->uProtocol)
1968 {
1969 case 3:
1970 {
1971 Msg.hdr.cParms = 5;
1972
1973 Msg.u.v3.uContext.SetUInt32(0);
1974 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
1975 Msg.u.v3.cbChecksum.SetUInt32(0);
1976 break;
1977 }
1978
1979 case 2:
1980 {
1981 Msg.hdr.cParms = 3;
1982
1983 Msg.u.v2.uContext.SetUInt32(0);
1984 break;
1985 }
1986
1987 default: /* Protocol v1 */
1988 {
1989 Msg.hdr.cParms = 5;
1990
1991 Msg.u.v1.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)(strPath.length() + 1));
1992 Msg.u.v1.cbName.SetUInt32((uint32_t)(strPath.length() + 1));
1993 Msg.u.v1.fMode.SetUInt32(pObj->GetMode());
1994 break;
1995 }
1996 }
1997
1998 uint64_t cbToReadTotal = pObj->GetSize();
1999 uint64_t cbWrittenTotal = 0;
2000 while (cbToReadTotal)
2001 {
2002 uint32_t cbToRead = RT_MIN(cbToReadTotal, cbBuf);
2003 uint32_t cbRead = 0;
2004 if (cbToRead)
2005 rc = pObj->Read(pvBuf, cbToRead, &cbRead);
2006
2007 LogFlowFunc(("cbToReadTotal=%RU64, cbToRead=%RU32, cbRead=%RU32, rc=%Rrc\n",
2008 cbToReadTotal, cbToRead, cbRead, rc));
2009
2010 if ( RT_SUCCESS(rc)
2011 && cbRead)
2012 {
2013 switch (pCtx->uProtocol)
2014 {
2015 case 3:
2016 {
2017 Msg.u.v3.pvData.SetPtr(pvBuf, cbRead);
2018 Msg.u.v3.cbData.SetUInt32(cbRead);
2019 /** @todo Calculate + set checksums. */
2020 break;
2021 }
2022
2023 case 2:
2024 {
2025 Msg.u.v2.pvData.SetPtr(pvBuf, cbRead);
2026 Msg.u.v2.cbData.SetUInt32(cbRead);
2027 break;
2028 }
2029
2030 default:
2031 {
2032 Msg.u.v1.pvData.SetPtr(pvBuf, cbRead);
2033 Msg.u.v1.cbData.SetUInt32(cbRead);
2034 break;
2035 }
2036 }
2037
2038 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
2039 if (RT_SUCCESS(rc))
2040 rc = Msg.hdr.result;
2041 }
2042
2043 if (RT_FAILURE(rc))
2044 {
2045 LogFlowFunc(("Reading failed with rc=%Rrc\n", rc));
2046 break;
2047 }
2048
2049 Assert(cbRead <= cbToReadTotal);
2050 cbToReadTotal -= cbRead;
2051 cbWrittenTotal += cbRead;
2052
2053 LogFlowFunc(("%RU64/%RU64 -- %RU8%%\n", cbWrittenTotal, pObj->GetSize(), cbWrittenTotal * 100 / pObj->GetSize()));
2054 };
2055 }
2056
2057 RTMemFree(pvBuf);
2058
2059 LogFlowFuncLeaveRC(rc);
2060 return rc;
2061}
2062
2063static int vbglR3DnDGHSendURIObject(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
2064{
2065 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2066 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
2067
2068 int rc;
2069
2070 switch (pObj->GetType())
2071 {
2072 case DnDURIObject::Directory:
2073 rc = vbglR3DnDGHSendDir(pCtx, pObj);
2074 break;
2075
2076 case DnDURIObject::File:
2077 rc = vbglR3DnDGHSendFile(pCtx, pObj);
2078 break;
2079
2080 default:
2081 AssertMsgFailed(("Object type %ld not implemented\n", pObj->GetType()));
2082 rc = VERR_NOT_IMPLEMENTED;
2083 break;
2084 }
2085
2086 return rc;
2087}
2088
2089static int vbglR3DnDGHSendRawData(PVBGLR3GUESTDNDCMDCTX pCtx, void *pvData, size_t cbData)
2090{
2091 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2092 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
2093 /* cbData can be 0. */
2094
2095 VBOXDNDDATAHDR dataHdr;
2096 RT_ZERO(dataHdr);
2097
2098 /* For raw data only the total size is required to be specified. */
2099 dataHdr.cbTotal = cbData;
2100
2101 return vbglR3DnDGHSendDataInternal(pCtx, pvData, cbData, &dataHdr);
2102}
2103
2104static int vbglR3DnDGHSendURIData(PVBGLR3GUESTDNDCMDCTX pCtx, const void *pvData, size_t cbData)
2105{
2106 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2107 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
2108 AssertReturn(cbData, VERR_INVALID_PARAMETER);
2109
2110 RTCList<RTCString> lstPaths =
2111 RTCString((const char *)pvData, cbData).split("\r\n");
2112
2113 /** @todo Add symlink support (DNDURILIST_FLAGS_RESOLVE_SYMLINKS) here. */
2114 /** @todo Add lazy loading (DNDURILIST_FLAGS_LAZY) here. */
2115 uint32_t fFlags = DNDURILIST_FLAGS_KEEP_OPEN;
2116
2117 DnDURIList lstURI;
2118 int rc = lstURI.AppendURIPathsFromList(lstPaths, fFlags);
2119 if (RT_SUCCESS(rc))
2120 {
2121 /*
2122 * Send the (meta) data; in case of URIs it's the (non-recursive) file/directory
2123 * URI list the host needs to know upfront to set up the drag'n drop operation.
2124 */
2125 RTCString strRootDest = lstURI.RootToString();
2126 if (strRootDest.isNotEmpty())
2127 {
2128 void *pvURIList = (void *)strRootDest.c_str(); /* URI root list. */
2129 uint32_t cbURLIist = (uint32_t)strRootDest.length() + 1; /* Include string termination. */
2130
2131 /* The total size also contains the size of the meta data. */
2132 uint64_t cbTotal = cbURLIist;
2133 cbTotal += lstURI.TotalBytes();
2134
2135 /* We're going to send an URI list in text format. */
2136 const char szMetaFmt[] = "text/uri-list";
2137 const uint32_t cbMetaFmt = (uint32_t)strlen(szMetaFmt) + 1; /* Include termination. */
2138
2139 VBOXDNDDATAHDR dataHdr;
2140 dataHdr.uFlags = 0; /* Flags not used yet. */
2141 dataHdr.cbTotal = cbTotal;
2142 dataHdr.cbMeta = cbURLIist;
2143 dataHdr.pvMetaFmt = (void *)szMetaFmt;
2144 dataHdr.cbMetaFmt = cbMetaFmt;
2145 dataHdr.cObjects = lstURI.TotalCount();
2146
2147 rc = vbglR3DnDGHSendDataInternal(pCtx,
2148 pvURIList, cbURLIist, &dataHdr);
2149 }
2150 else
2151 rc = VERR_INVALID_PARAMETER;
2152 }
2153
2154 if (RT_SUCCESS(rc))
2155 {
2156 while (!lstURI.IsEmpty())
2157 {
2158 DnDURIObject *pNextObj = lstURI.First();
2159
2160 rc = vbglR3DnDGHSendURIObject(pCtx, pNextObj);
2161 if (RT_FAILURE(rc))
2162 break;
2163
2164 lstURI.RemoveFirst();
2165 }
2166 }
2167
2168 return rc;
2169}
2170
2171VBGLR3DECL(int) VbglR3DnDGHSendData(PVBGLR3GUESTDNDCMDCTX pCtx, const char *pszFormat, void *pvData, uint32_t cbData)
2172{
2173 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2174 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
2175 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
2176 AssertReturn(cbData, VERR_INVALID_PARAMETER);
2177
2178 LogFlowFunc(("pszFormat=%s, pvData=%p, cbData=%RU32\n", pszFormat, pvData, cbData));
2179
2180 int rc;
2181 if (DnDMIMEHasFileURLs(pszFormat, strlen(pszFormat)))
2182 {
2183 /* Send file data. */
2184 rc = vbglR3DnDGHSendURIData(pCtx, pvData, cbData);
2185 }
2186 else
2187 rc = vbglR3DnDGHSendRawData(pCtx, pvData, cbData);
2188
2189 if (RT_FAILURE(rc))
2190 {
2191 int rc2 = VbglR3DnDGHSendError(pCtx, rc);
2192 if (RT_FAILURE(rc2))
2193 LogFlowFunc(("Unable to send error (%Rrc) to host, rc=%Rrc\n", rc, rc2));
2194 }
2195
2196 return rc;
2197}
2198
2199VBGLR3DECL(int) VbglR3DnDGHSendError(PVBGLR3GUESTDNDCMDCTX pCtx, int rcErr)
2200{
2201 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2202
2203 VBOXDNDGHEVTERRORMSG Msg;
2204 RT_ZERO(Msg);
2205 Msg.hdr.result = VERR_WRONG_ORDER;
2206 Msg.hdr.u32ClientID = pCtx->uClientID;
2207 Msg.hdr.u32Function = GUEST_DND_GH_EVT_ERROR;
2208
2209 if (pCtx->uProtocol < 3)
2210 {
2211 Msg.hdr.cParms = 1;
2212
2213 Msg.u.v1.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
2214 }
2215 else
2216 {
2217 Msg.hdr.cParms = 2;
2218
2219 /** @todo Context ID not used yet. */
2220 Msg.u.v3.uContext.SetUInt32(0);
2221 Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
2222 }
2223
2224 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
2225 if (RT_SUCCESS(rc))
2226 {
2227 if (RT_FAILURE(Msg.hdr.result))
2228 {
2229 LogFlowFunc(("Sending error %Rrc failed with rc=%Rrc\n", rcErr, Msg.hdr.result));
2230
2231 /* Never return an error if the host did not accept the error at
2232 * the current time. This can be due to the host not having any appropriate
2233 * callbacks set which would handle that error. */
2234 rc = VINF_SUCCESS;
2235 }
2236 }
2237
2238 return rc;
2239}
2240#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
2241
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