VirtualBox

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

Last change on this file since 68458 was 68458, checked in by vboxsync, 7 years ago

Introducing macros for initializing the VBoxGuestHGCMCallInfo structure.

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