VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp@ 97748

最後變更 在這個檔案從97748是 97748,由 vboxsync 提交於 2 年 前

DnD: Renamed GUEST_DND_FN_GH_EVT_ERROR -> GUEST_DND_FN_EVT_ERROR to make this message more generic and not only bound to guest -> host (G->H) transfers. We also need a way to pass back errors for host -> guest (H->G) transfers.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 70.3 KB
 
1/* $Id: VBoxGuestR3LibDragAndDrop.cpp 97748 2022-12-06 09:26:48Z vboxsync $ */
2/** @file
3 * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Drag & Drop.
4 */
5
6/*
7 * Copyright (C) 2011-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.alldomusa.eu.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/path.h>
42#include <iprt/dir.h>
43#include <iprt/file.h>
44#include <iprt/uri.h>
45#include <iprt/thread.h>
46
47#include <iprt/cpp/list.h>
48#include <iprt/cpp/ministring.h>
49
50#ifdef LOG_GROUP
51 #undef LOG_GROUP
52#endif
53#define LOG_GROUP LOG_GROUP_GUEST_DND
54#include <VBox/log.h>
55
56#include <VBox/VBoxGuestLib.h>
57#include <VBox/GuestHost/DragAndDrop.h>
58#include <VBox/HostServices/DragAndDropSvc.h>
59
60using namespace DragAndDropSvc;
61
62#include "VBoxGuestR3LibInternal.h"
63
64
65/*********************************************************************************************************************************
66* Private internal functions *
67*********************************************************************************************************************************/
68
69/**
70 * Receives the next upcoming message for a given DnD context.
71 *
72 * @returns IPRT status code.
73 * Will return VERR_CANCELLED (implemented by the host service) if we need to bail out.
74 * @param pCtx DnD context to use.
75 * @param puMsg Where to store the message type.
76 * @param pcParms Where to store the number of parameters required for receiving the message.
77 * @param fWait Whether to wait (block) for a new message to arrive or not.
78 */
79static int vbglR3DnDGetNextMsgType(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puMsg, uint32_t *pcParms, bool fWait)
80{
81 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
82 AssertPtrReturn(puMsg, VERR_INVALID_POINTER);
83 AssertPtrReturn(pcParms, VERR_INVALID_POINTER);
84
85 int rc;
86
87 do
88 {
89 HGCMMsgGetNext Msg;
90 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GET_NEXT_HOST_MSG, 3);
91 Msg.uMsg.SetUInt32(0);
92 Msg.cParms.SetUInt32(0);
93 Msg.fBlock.SetUInt32(fWait ? 1 : 0);
94
95 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
96 if (RT_SUCCESS(rc))
97 {
98 rc = Msg.uMsg.GetUInt32(puMsg); AssertRC(rc);
99 rc = Msg.cParms.GetUInt32(pcParms); AssertRC(rc);
100 }
101
102 LogRel(("DnD: Received message %s (%#x) from host\n", DnDHostMsgToStr(*puMsg), *puMsg));
103
104 } while (rc == VERR_INTERRUPTED);
105
106 return rc;
107}
108
109
110/**
111 * Sends a DnD error back to the host.
112 *
113 * @returns IPRT status code.
114 * @param pCtx DnD context to use.
115 * @param rcErr Error (IPRT-style) to send.
116 */
117VBGLR3DECL(int) VbglR3DnDSendError(PVBGLR3GUESTDNDCMDCTX pCtx, int rcErr)
118{
119 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
120
121 HGCMMsgGHError Msg;
122 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_EVT_ERROR, 2);
123 /** @todo Context ID not used yet. */
124 Msg.u.v3.uContext.SetUInt32(0);
125 Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
126
127 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
128
129 /*
130 * Never return an error if the host did not accept the error at the current
131 * time. This can be due to the host not having any appropriate callbacks
132 * set which would handle that error.
133 *
134 * bird: Looks like VERR_NOT_SUPPORTED is what the host will return if it
135 * doesn't an appropriate callback. The code used to ignore ALL errors
136 * the host would return, also relevant ones.
137 */
138 if (RT_FAILURE(rc))
139 LogFlowFunc(("Sending error %Rrc failed with rc=%Rrc\n", rcErr, rc));
140 if (rc == VERR_NOT_SUPPORTED)
141 rc = VINF_SUCCESS;
142
143 return rc;
144}
145
146/**
147 * Host -> Guest
148 * Utility function to receive a so-called "action message" from the host.
149 * Certain DnD messages use the same amount / sort of parameters and grouped as "action messages".
150 *
151 * @returns IPRT status code.
152 * @param pCtx DnD context to use.
153 * @param uMsg Which kind of message to receive.
154 * @param puScreenID Where to store the host screen ID the message is bound to. Optional.
155 * @param puX Where to store the absolute X coordinates. Optional.
156 * @param puY Where to store the absolute Y coordinates. Optional.
157 * @param puDefAction Where to store the default action to perform. Optional.
158 * @param puAllActions Where to store the available actions. Optional.
159 * @param ppszFormats Where to store List of formats. Optional.
160 * @param pcbFormats Size (in bytes) of where to store the list of formats. Optional.
161 *
162 * @todo r=andy Get rid of this function as soon as we resolved the protocol TODO #1.
163 * This was part of the initial protocol and needs to go.
164 */
165static int vbglR3DnDHGRecvAction(PVBGLR3GUESTDNDCMDCTX pCtx,
166 uint32_t uMsg,
167 uint32_t *puScreenID,
168 uint32_t *puX,
169 uint32_t *puY,
170 uint32_t *puDefAction,
171 uint32_t *puAllActions,
172 char **ppszFormats,
173 uint32_t *pcbFormats)
174{
175 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
176 /* The rest is optional. */
177
178 const uint32_t cbFormatsTmp = pCtx->cbMaxChunkSize;
179
180 char *pszFormatsTmp = static_cast<char *>(RTMemAlloc(cbFormatsTmp));
181 if (!pszFormatsTmp)
182 return VERR_NO_MEMORY;
183
184 HGCMMsgHGAction Msg;
185 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, uMsg, 8);
186 Msg.u.v3.uContext.SetUInt32(0);
187 Msg.u.v3.uScreenId.SetUInt32(0);
188 Msg.u.v3.uX.SetUInt32(0);
189 Msg.u.v3.uY.SetUInt32(0);
190 Msg.u.v3.uDefAction.SetUInt32(0);
191 Msg.u.v3.uAllActions.SetUInt32(0);
192 Msg.u.v3.pvFormats.SetPtr(pszFormatsTmp, cbFormatsTmp);
193 Msg.u.v3.cbFormats.SetUInt32(0);
194
195 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
196 if (RT_SUCCESS(rc))
197 {
198 /** @todo Context ID not used yet. */
199 if (RT_SUCCESS(rc) && puScreenID)
200 rc = Msg.u.v3.uScreenId.GetUInt32(puScreenID);
201 if (RT_SUCCESS(rc) && puX)
202 rc = Msg.u.v3.uX.GetUInt32(puX);
203 if (RT_SUCCESS(rc) && puY)
204 rc = Msg.u.v3.uY.GetUInt32(puY);
205 if (RT_SUCCESS(rc) && puDefAction)
206 rc = Msg.u.v3.uDefAction.GetUInt32(puDefAction);
207 if (RT_SUCCESS(rc) && puAllActions)
208 rc = Msg.u.v3.uAllActions.GetUInt32(puAllActions);
209 if (RT_SUCCESS(rc) && pcbFormats)
210 rc = Msg.u.v3.cbFormats.GetUInt32(pcbFormats);
211
212 if (RT_SUCCESS(rc))
213 {
214 if (ppszFormats)
215 {
216 *ppszFormats = RTStrDup(pszFormatsTmp);
217 if (!*ppszFormats)
218 rc = VERR_NO_MEMORY;
219 }
220 }
221 }
222
223 RTStrFree(pszFormatsTmp);
224
225 return rc;
226}
227
228/**
229 * Host -> Guest
230 * Utility function to receive a HOST_DND_FN_HG_EVT_LEAVE message from the host.
231 *
232 * @returns IPRT status code.
233 * @param pCtx DnD context to use.
234 */
235static int vbglR3DnDHGRecvLeave(PVBGLR3GUESTDNDCMDCTX pCtx)
236{
237 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
238
239 HGCMMsgHGLeave Msg;
240 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_EVT_LEAVE, 1);
241 /** @todo Context ID not used yet. */
242 Msg.u.v3.uContext.SetUInt32(0);
243
244 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
245}
246
247/**
248 * Host -> Guest
249 * Utility function to receive a HOST_DND_FN_HG_EVT_CANCEL message from the host.
250 *
251 * @returns IPRT status code.
252 * @param pCtx DnD context to use.
253 */
254static int vbglR3DnDHGRecvCancel(PVBGLR3GUESTDNDCMDCTX pCtx)
255{
256 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
257
258 HGCMMsgHGCancel Msg;
259 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_CANCEL, 1);
260 /** @todo Context ID not used yet. */
261 Msg.u.v3.uContext.SetUInt32(0);
262
263 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
264}
265
266/**
267 * Host -> Guest
268 * Utility function to receive a HOST_DND_FN_HG_SND_DIR message from the host.
269 *
270 * @returns IPRT status code.
271 * @param pCtx DnD context to use.
272 * @param pszDirname Where to store the directory name of the directory being created.
273 * @param cbDirname Size (in bytes) of where to store the directory name of the directory being created.
274 * @param pcbDirnameRecv Size (in bytes) of the actual directory name received.
275 * @param pfMode Where to store the directory creation mode.
276 */
277static int vbglR3DnDHGRecvDir(PVBGLR3GUESTDNDCMDCTX pCtx,
278 char *pszDirname,
279 uint32_t cbDirname,
280 uint32_t *pcbDirnameRecv,
281 uint32_t *pfMode)
282{
283 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
284 AssertPtrReturn(pszDirname, VERR_INVALID_POINTER);
285 AssertReturn(cbDirname, VERR_INVALID_PARAMETER);
286 AssertPtrReturn(pcbDirnameRecv, VERR_INVALID_POINTER);
287 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
288
289 HGCMMsgHGSendDir Msg;
290 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_DIR, 4);
291 /** @todo Context ID not used yet. */
292 Msg.u.v3.uContext.SetUInt32(0);
293 Msg.u.v3.pvName.SetPtr(pszDirname, cbDirname);
294 Msg.u.v3.cbName.SetUInt32(cbDirname);
295 Msg.u.v3.fMode.SetUInt32(0);
296
297 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
298 if (RT_SUCCESS(rc))
299 {
300 /** @todo Context ID not used yet. */
301 rc = Msg.u.v3.cbName.GetUInt32(pcbDirnameRecv); AssertRC(rc);
302 rc = Msg.u.v3.fMode.GetUInt32(pfMode); AssertRC(rc);
303
304 AssertReturn(cbDirname >= *pcbDirnameRecv, VERR_TOO_MUCH_DATA);
305 }
306
307 return rc;
308}
309
310/**
311 * Host -> Guest
312 * Utility function to receive a HOST_DND_FN_HG_SND_FILE_DATA message from the host.
313 *
314 * @returns IPRT status code.
315 * @param pCtx DnD context to use.
316 * @param pvData Where to store the file data chunk.
317 * @param cbData Size (in bytes) of where to store the data chunk.
318 * @param pcbDataRecv Size (in bytes) of the actual data chunk size received.
319 */
320static int vbglR3DnDHGRecvFileData(PVBGLR3GUESTDNDCMDCTX pCtx,
321 void *pvData,
322 uint32_t cbData,
323 uint32_t *pcbDataRecv)
324{
325 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
326 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
327 AssertReturn(cbData, VERR_INVALID_PARAMETER);
328 AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER);
329
330 HGCMMsgHGSendFileData Msg;
331 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_FILE_DATA, 5);
332 Msg.u.v3.uContext.SetUInt32(0);
333 Msg.u.v3.pvData.SetPtr(pvData, cbData);
334 Msg.u.v3.cbData.SetUInt32(0);
335 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
336 Msg.u.v3.cbChecksum.SetUInt32(0);
337
338 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
339 if (RT_SUCCESS(rc))
340 {
341 /** @todo Context ID not used yet. */
342 rc = Msg.u.v3.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
343 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
344 /** @todo Add checksum support. */
345 }
346
347 return rc;
348}
349
350/**
351 * Host -> Guest
352 * Utility function to receive the HOST_DND_FN_HG_SND_FILE_HDR message from the host.
353 *
354 * @returns IPRT status code.
355 * @param pCtx DnD context to use.
356 * @param pszFilename Where to store the file name of the file being transferred.
357 * @param cbFilename Size (in bytes) of where to store the file name of the file being transferred.
358 * @param puFlags File transfer flags. Currently not being used.
359 * @param pfMode Where to store the file creation mode.
360 * @param pcbTotal Where to store the file size (in bytes).
361 */
362static int vbglR3DnDHGRecvFileHdr(PVBGLR3GUESTDNDCMDCTX pCtx,
363 char *pszFilename,
364 uint32_t cbFilename,
365 uint32_t *puFlags,
366 uint32_t *pfMode,
367 uint64_t *pcbTotal)
368{
369 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
370 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
371 AssertReturn(cbFilename, VERR_INVALID_PARAMETER);
372 AssertPtrReturn(puFlags, VERR_INVALID_POINTER);
373 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
374 AssertReturn(pcbTotal, VERR_INVALID_POINTER);
375
376 HGCMMsgHGSendFileHdr Msg;
377 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_FILE_HDR, 6);
378 Msg.uContext.SetUInt32(0); /** @todo Not used yet. */
379 Msg.pvName.SetPtr(pszFilename, cbFilename);
380 Msg.cbName.SetUInt32(cbFilename);
381 Msg.uFlags.SetUInt32(0);
382 Msg.fMode.SetUInt32(0);
383 Msg.cbTotal.SetUInt64(0);
384
385 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
386 if (RT_SUCCESS(rc))
387 {
388 /** @todo Get context ID. */
389 rc = Msg.uFlags.GetUInt32(puFlags); AssertRC(rc);
390 rc = Msg.fMode.GetUInt32(pfMode); AssertRC(rc);
391 rc = Msg.cbTotal.GetUInt64(pcbTotal); AssertRC(rc);
392 }
393
394 return rc;
395}
396
397/**
398 * Host -> Guest
399 * Helper function for receiving URI data from the host. Do not call directly.
400 * This function also will take care of the file creation / locking on the guest.
401 *
402 * @returns IPRT status code.
403 * @param pCtx DnD context to use.
404 * @param pDataHdr DnD data header to use. Needed for accounting.
405 * @param pDroppedFiles Dropped files object to use for maintaining the file creation / locking.
406 */
407static int vbglR3DnDHGRecvURIData(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr, PDNDDROPPEDFILES 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 /* Anything to do at all? */
422 /* Note: Do not check for cbToRecvBytes == 0 here, as this might be just
423 * a bunch of 0-byte files to be transferred. */
424 if (!cToRecvObjs)
425 return VINF_SUCCESS;
426
427 LogRel2(("DnD: Receiving URI data started\n"));
428
429 /*
430 * Allocate temporary chunk buffer.
431 */
432 uint32_t cbChunkMax = pCtx->cbMaxChunkSize;
433 void *pvChunk = RTMemAlloc(cbChunkMax);
434 if (!pvChunk)
435 return VERR_NO_MEMORY;
436 uint32_t cbChunkRead = 0;
437
438 uint64_t cbFileSize = 0; /* Total file size (in bytes). */
439 uint64_t cbFileWritten = 0; /* Written bytes. */
440
441 const char *pszDropDir = DnDDroppedFilesGetDirAbs(pDroppedFiles);
442 AssertPtr(pszDropDir);
443
444 int rc;
445
446 /*
447 * Enter the main loop of retieving files + directories.
448 */
449 DNDTRANSFEROBJECT objCur;
450 RT_ZERO(objCur);
451
452 char szPathName[RTPATH_MAX] = { 0 };
453 uint32_t cbPathName = 0;
454 uint32_t fFlags = 0;
455 uint32_t fMode = 0;
456
457 do
458 {
459 LogFlowFunc(("Waiting for new message ...\n"));
460
461 uint32_t uNextMsg;
462 uint32_t cNextParms;
463 rc = vbglR3DnDGetNextMsgType(pCtx, &uNextMsg, &cNextParms, true /* fWait */);
464 if (RT_SUCCESS(rc))
465 {
466 LogFlowFunc(("uNextMsg=%RU32, cNextParms=%RU32\n", uNextMsg, cNextParms));
467
468 switch (uNextMsg)
469 {
470 case HOST_DND_FN_HG_SND_DIR:
471 {
472 rc = vbglR3DnDHGRecvDir(pCtx,
473 szPathName,
474 sizeof(szPathName),
475 &cbPathName,
476 &fMode);
477 LogFlowFunc(("HOST_DND_FN_HG_SND_DIR: "
478 "pszPathName=%s, cbPathName=%RU32, fMode=0x%x, rc=%Rrc\n",
479 szPathName, cbPathName, fMode, rc));
480
481 char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName);
482 if (pszPathAbs)
483 {
484#ifdef RT_OS_WINDOWS
485 uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL;
486#else
487 uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRWXU;
488#endif
489 rc = RTDirCreate(pszPathAbs, fCreationMode, 0);
490 if (RT_SUCCESS(rc))
491 rc = DnDDroppedFilesAddDir(pDroppedFiles, pszPathAbs);
492
493 if (RT_SUCCESS(rc))
494 {
495 Assert(cToRecvObjs);
496 cToRecvObjs--;
497 }
498
499 RTStrFree(pszPathAbs);
500 }
501 else
502 rc = VERR_NO_MEMORY;
503 break;
504 }
505 case HOST_DND_FN_HG_SND_FILE_HDR:
506 RT_FALL_THROUGH();
507 case HOST_DND_FN_HG_SND_FILE_DATA:
508 {
509 if (uNextMsg == HOST_DND_FN_HG_SND_FILE_HDR)
510 {
511 rc = vbglR3DnDHGRecvFileHdr(pCtx,
512 szPathName,
513 sizeof(szPathName),
514 &fFlags,
515 &fMode,
516 &cbFileSize);
517 LogFlowFunc(("HOST_DND_FN_HG_SND_FILE_HDR: "
518 "szPathName=%s, fFlags=0x%x, fMode=0x%x, cbFileSize=%RU64, rc=%Rrc\n",
519 szPathName, fFlags, fMode, cbFileSize, rc));
520 }
521 else
522 {
523 rc = vbglR3DnDHGRecvFileData(pCtx,
524 pvChunk,
525 cbChunkMax,
526 &cbChunkRead);
527 LogFlowFunc(("HOST_DND_FN_HG_SND_FILE_DATA: "
528 "cbChunkRead=%RU32, rc=%Rrc\n", cbChunkRead, rc));
529 }
530
531 if ( RT_SUCCESS(rc)
532 && uNextMsg == HOST_DND_FN_HG_SND_FILE_HDR)
533 {
534 char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName);
535 if (pszPathAbs)
536 {
537 LogFlowFunc(("Opening pszPathName=%s, cbPathName=%RU32, fMode=0x%x, cbFileSize=%zu\n",
538 szPathName, cbPathName, fMode, cbFileSize));
539
540 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE
541 | RTFILE_O_CREATE_REPLACE;
542
543 /* Is there already a file open, e.g. in transfer? */
544 if (!DnDTransferObjectIsOpen(&objCur))
545 {
546#ifdef RT_OS_WINDOWS
547 uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL;
548#else
549 uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR;
550#endif
551 rc = DnDTransferObjectInitEx(&objCur, DNDTRANSFEROBJTYPE_FILE,
552 pszDropDir /* Source (base) path */, szPathName /* Destination path */);
553 if (RT_SUCCESS(rc))
554 {
555 rc = DnDTransferObjectOpen(&objCur, fOpen, fCreationMode, DNDTRANSFEROBJECT_FLAGS_NONE);
556 if (RT_SUCCESS(rc))
557 {
558 rc = DnDDroppedFilesAddFile(pDroppedFiles, pszPathAbs);
559 if (RT_SUCCESS(rc))
560 {
561 cbFileWritten = 0;
562 DnDTransferObjectSetSize(&objCur, cbFileSize);
563 }
564 }
565 }
566 }
567 else
568 {
569 AssertMsgFailed(("ObjType=%RU32\n", DnDTransferObjectGetType(&objCur)));
570 rc = VERR_WRONG_ORDER;
571 }
572
573 RTStrFree(pszPathAbs);
574 }
575 else
576 rc = VERR_NO_MEMORY;
577 }
578
579 if ( RT_SUCCESS(rc)
580 && uNextMsg == HOST_DND_FN_HG_SND_FILE_DATA
581 && cbChunkRead)
582 {
583 uint32_t cbChunkWritten;
584 rc = DnDTransferObjectWrite(&objCur, pvChunk, cbChunkRead, &cbChunkWritten);
585 if (RT_SUCCESS(rc))
586 {
587 LogFlowFunc(("HOST_DND_FN_HG_SND_FILE_DATA: "
588 "cbChunkRead=%RU32, cbChunkWritten=%RU32, cbFileWritten=%RU64 cbFileSize=%RU64\n",
589 cbChunkRead, cbChunkWritten, cbFileWritten + cbChunkWritten, cbFileSize));
590
591 cbFileWritten += cbChunkWritten;
592
593 Assert(cbChunkRead <= cbToRecvBytes);
594 cbToRecvBytes -= cbChunkRead;
595 }
596 }
597
598 /* Data transfer complete? Close the file. */
599 bool fClose = DnDTransferObjectIsComplete(&objCur);
600 if (fClose)
601 {
602 Assert(cToRecvObjs);
603 cToRecvObjs--;
604 }
605
606 /* Only since protocol v2 we know the file size upfront. */
607 Assert(cbFileWritten <= cbFileSize);
608
609 if (fClose)
610 {
611 LogFlowFunc(("Closing file\n"));
612 DnDTransferObjectDestroy(&objCur);
613 }
614
615 break;
616 }
617 case HOST_DND_FN_CANCEL:
618 {
619 rc = vbglR3DnDHGRecvCancel(pCtx);
620 if (RT_SUCCESS(rc))
621 rc = VERR_CANCELLED;
622 break;
623 }
624 default:
625 {
626 LogRel(("DnD: Warning: Message %s (%#x) from host not supported or in wrong order\n", DnDHostMsgToStr(uNextMsg), uNextMsg));
627 rc = VERR_NOT_SUPPORTED;
628 break;
629 }
630 }
631 }
632
633 if (RT_FAILURE(rc))
634 break;
635
636 LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64\n", cbToRecvBytes, cToRecvObjs));
637 if ( !cbToRecvBytes
638 && !cToRecvObjs)
639 {
640 break;
641 }
642
643 } while (RT_SUCCESS(rc));
644
645 LogFlowFunc(("Loop ended with %Rrc\n", rc));
646
647 /* All URI data processed? */
648 if (rc == VERR_NO_DATA)
649 rc = VINF_SUCCESS;
650
651 /* Delete temp buffer again. */
652 if (pvChunk)
653 RTMemFree(pvChunk);
654
655 /* Cleanup on failure or if the user has canceled the operation or
656 * something else went wrong. */
657 if (RT_FAILURE(rc))
658 {
659 LogRel(("DnD: Receiving URI data failed with %Rrc\n", rc));
660
661 DnDTransferObjectDestroy(&objCur);
662 DnDDroppedFilesRollback(pDroppedFiles);
663 }
664 else
665 {
666 LogRel2(("DnD: Receiving URI data finished\n"));
667
668 /** @todo Compare the transfer list with the dirs/files we really transferred. */
669 /** @todo Implement checksum verification, if any. */
670 }
671
672 /*
673 * Close the dropped files directory.
674 * Don't try to remove it here, however, as the files are being needed
675 * by the client's drag'n drop operation lateron.
676 */
677 int rc2 = DnDDroppedFilesReset(pDroppedFiles, false /* fRemoveDropDir */);
678 if (RT_FAILURE(rc2)) /* Not fatal, don't report back to host. */
679 LogFlowFunc(("Closing dropped files directory failed with %Rrc\n", rc2));
680
681 LogFlowFuncLeaveRC(rc);
682 return rc;
683}
684
685/**
686 * Host -> Guest
687 * Utility function to receive the HOST_DND_FN_HG_SND_DATA message from the host.
688 *
689 * @returns IPRT status code.
690 * @param pCtx DnD context to use.
691 * @param pDataHdr DnD data header to use. Need for accounting and stuff.
692 * @param pvData Where to store the received data from the host.
693 * @param cbData Size (in bytes) of where to store the received data.
694 * @param pcbDataRecv Where to store the received amount of data (in bytes).
695 */
696static int vbglR3DnDHGRecvDataRaw(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr,
697 void *pvData, uint32_t cbData, uint32_t *pcbDataRecv)
698{
699 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
700 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
701 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
702 AssertReturn(cbData, VERR_INVALID_PARAMETER);
703 AssertPtrNullReturn(pcbDataRecv, VERR_INVALID_POINTER);
704
705 LogFlowFunc(("pvDate=%p, cbData=%RU32\n", pvData, cbData));
706
707 HGCMMsgHGSendData Msg;
708 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_DATA, 5);
709 Msg.u.v3.uContext.SetUInt32(0);
710 Msg.u.v3.pvData.SetPtr(pvData, cbData);
711 Msg.u.v3.cbData.SetUInt32(0);
712 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
713 Msg.u.v3.cbChecksum.SetUInt32(0);
714
715 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
716 if (RT_SUCCESS(rc))
717 {
718 uint32_t cbDataRecv;
719 rc = Msg.u.v3.cbData.GetUInt32(&cbDataRecv);
720 AssertRC(rc);
721 if (RT_SUCCESS(rc))
722 {
723 /** @todo Use checksum for validating the received data. */
724 if (pcbDataRecv)
725 *pcbDataRecv = cbDataRecv;
726 LogFlowFuncLeaveRC(rc);
727 return rc;
728 }
729 }
730
731 /* failure */
732 LogFlowFuncLeaveRC(rc);
733 return rc;
734}
735
736/**
737 * Host -> Guest
738 * Utility function to receive the HOST_DND_FN_HG_SND_DATA_HDR message from the host.
739 *
740 * @returns IPRT status code.
741 * @param pCtx DnD context to use.
742 * @param pDataHdr Where to store the receivd DnD data header.
743 */
744static int vbglR3DnDHGRecvDataHdr(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr)
745{
746 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
747 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
748
749 Assert(pCtx->uProtocolDeprecated >= 3); /* Only for protocol v3 and up. */
750
751 HGCMMsgHGSendDataHdr Msg;
752 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_DATA_HDR, 12);
753 Msg.uContext.SetUInt32(0);
754 Msg.uFlags.SetUInt32(0);
755 Msg.uScreenId.SetUInt32(0);
756 Msg.cbTotal.SetUInt64(0);
757 Msg.cbMeta.SetUInt32(0);
758 Msg.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
759 Msg.cbMetaFmt.SetUInt32(0);
760 Msg.cObjects.SetUInt64(0);
761 Msg.enmCompression.SetUInt32(0);
762 Msg.enmChecksumType.SetUInt32(0);
763 Msg.pvChecksum.SetPtr(pDataHdr->pvChecksum, pDataHdr->cbChecksum);
764 Msg.cbChecksum.SetUInt32(0);
765
766 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
767 if (RT_SUCCESS(rc))
768 {
769 /* Msg.uContext not needed here. */
770 Msg.uFlags.GetUInt32(&pDataHdr->uFlags);
771 Msg.uScreenId.GetUInt32(&pDataHdr->uScreenId);
772 Msg.cbTotal.GetUInt64(&pDataHdr->cbTotal);
773 Msg.cbMeta.GetUInt32(&pDataHdr->cbMeta);
774 Msg.cbMetaFmt.GetUInt32(&pDataHdr->cbMetaFmt);
775 Msg.cObjects.GetUInt64(&pDataHdr->cObjects);
776 Msg.enmCompression.GetUInt32(&pDataHdr->enmCompression);
777 Msg.enmChecksumType.GetUInt32((uint32_t *)&pDataHdr->enmChecksumType);
778 Msg.cbChecksum.GetUInt32(&pDataHdr->cbChecksum);
779 }
780
781 LogFlowFuncLeaveRC(rc);
782 return rc;
783}
784
785/**
786 * Host -> Guest
787 * Helper function for receiving the actual DnD data from the host. Do not call directly.
788 *
789 * @returns IPRT status code.
790 * @param pCtx DnD context to use.
791 * @param pDataHdr Where to store the data header data.
792 * @param ppvData Returns the received meta data. Needs to be free'd by the caller.
793 * @param pcbData Where to store the size (in bytes) of the received meta data.
794 */
795static int vbglR3DnDHGRecvDataLoop(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr,
796 void **ppvData, uint64_t *pcbData)
797{
798 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
799 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
800 AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
801 AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
802
803 int rc;
804 uint32_t cbDataRecv;
805
806 LogFlowFuncEnter();
807
808 rc = vbglR3DnDHGRecvDataHdr(pCtx, pDataHdr);
809 if (RT_FAILURE(rc))
810 return rc;
811
812 LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU32\n", pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects));
813 if (pDataHdr->cbMeta)
814 {
815 uint64_t cbDataTmp = 0;
816 void *pvDataTmp = RTMemAlloc(pDataHdr->cbMeta);
817 if (!pvDataTmp)
818 rc = VERR_NO_MEMORY;
819
820 if (RT_SUCCESS(rc))
821 {
822 uint8_t *pvDataOff = (uint8_t *)pvDataTmp;
823 while (cbDataTmp < pDataHdr->cbMeta)
824 {
825 rc = vbglR3DnDHGRecvDataRaw(pCtx, pDataHdr,
826 pvDataOff, RT_MIN(pDataHdr->cbMeta - cbDataTmp, pCtx->cbMaxChunkSize),
827 &cbDataRecv);
828 if (RT_SUCCESS(rc))
829 {
830 LogFlowFunc(("cbDataRecv=%RU32, cbDataTmp=%RU64\n", cbDataRecv, cbDataTmp));
831 Assert(cbDataTmp + cbDataRecv <= pDataHdr->cbMeta);
832 cbDataTmp += cbDataRecv;
833 pvDataOff += cbDataRecv;
834 }
835 else
836 break;
837 }
838
839 if (RT_SUCCESS(rc))
840 {
841 Assert(cbDataTmp == pDataHdr->cbMeta);
842
843 LogFlowFunc(("Received %RU64 bytes of data\n", cbDataTmp));
844
845 *ppvData = pvDataTmp;
846 *pcbData = cbDataTmp;
847 }
848 else
849 RTMemFree(pvDataTmp);
850 }
851 }
852 else
853 {
854 *ppvData = NULL;
855 *pcbData = 0;
856 }
857
858 LogFlowFuncLeaveRC(rc);
859 return rc;
860}
861
862/**
863 * Host -> Guest
864 * Main function for receiving the actual DnD data from the host.
865 *
866 * @returns IPRT status code.
867 * @param pCtx DnD context to use.
868 * @param pMeta Where to store the actual meta data received from the host.
869 */
870static int vbglR3DnDHGRecvDataMain(PVBGLR3GUESTDNDCMDCTX pCtx,
871 PVBGLR3GUESTDNDMETADATA pMeta)
872{
873 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
874 AssertPtrReturn(pMeta, VERR_INVALID_POINTER);
875
876 AssertMsgReturn(pCtx->cbMaxChunkSize, ("Maximum chunk size must not be 0\n"), VERR_INVALID_PARAMETER);
877
878 VBOXDNDDATAHDR dataHdr;
879 RT_ZERO(dataHdr);
880 dataHdr.cbMetaFmt = pCtx->cbMaxChunkSize;
881 dataHdr.pvMetaFmt = RTMemAlloc(dataHdr.cbMetaFmt);
882 if (!dataHdr.pvMetaFmt)
883 return VERR_NO_MEMORY;
884
885 void *pvData = NULL;
886 uint64_t cbData = 0;
887
888 int rc = vbglR3DnDHGRecvDataLoop(pCtx, &dataHdr, &pvData, &cbData);
889 if (RT_SUCCESS(rc))
890 {
891 LogRel2(("DnD: Received %RU64 bytes meta data in format '%s'\n", cbData, (char *)dataHdr.pvMetaFmt));
892
893 /**
894 * Check if this is an URI event. If so, let VbglR3 do all the actual
895 * data transfer + file/directory creation internally without letting
896 * the caller know.
897 *
898 * This keeps the actual (guest OS-)dependent client (like VBoxClient /
899 * VBoxTray) small by not having too much redundant code.
900 */
901 Assert(dataHdr.cbMetaFmt);
902 AssertPtr(dataHdr.pvMetaFmt);
903 if (DnDMIMEHasFileURLs((char *)dataHdr.pvMetaFmt, dataHdr.cbMetaFmt)) /* URI data. */
904 {
905 DNDDROPPEDFILES droppedFiles;
906 RT_ZERO(droppedFiles);
907
908 rc = DnDDroppedFilesInit(&droppedFiles);
909 if (RT_SUCCESS(rc))
910 rc = DnDDroppedFilesOpenTemp(&droppedFiles, DNDURIDROPPEDFILE_FLAGS_NONE);
911
912 if (RT_FAILURE(rc))
913 {
914 LogRel(("DnD: Initializing dropped files directory failed with %Rrc\n", rc));
915 }
916 else
917 {
918 AssertPtr(pvData);
919 Assert(cbData);
920
921 /* Use the dropped files directory as the root directory for the current transfer. */
922 rc = DnDTransferListInitEx(&pMeta->u.URI.Transfer, DnDDroppedFilesGetDirAbs(&droppedFiles),
923 DNDTRANSFERLISTFMT_NATIVE);
924 if (RT_SUCCESS(rc))
925 {
926 rc = DnDTransferListAppendRootsFromBuffer(&pMeta->u.URI.Transfer, DNDTRANSFERLISTFMT_URI, (const char *)pvData, cbData,
927 DND_PATH_SEPARATOR_STR, 0 /* fFlags */);
928 if (RT_SUCCESS(rc))
929 {
930 rc = vbglR3DnDHGRecvURIData(pCtx, &dataHdr, &droppedFiles);
931 if (RT_SUCCESS(rc))
932 {
933 pMeta->enmType = VBGLR3GUESTDNDMETADATATYPE_URI_LIST;
934 }
935 }
936 }
937 }
938 }
939 else /* Raw data. */
940 {
941 pMeta->u.Raw.cbMeta = cbData;
942 pMeta->u.Raw.pvMeta = pvData;
943
944 pMeta->enmType = VBGLR3GUESTDNDMETADATATYPE_RAW;
945 }
946 }
947
948 if (dataHdr.pvMetaFmt)
949 RTMemFree(dataHdr.pvMetaFmt);
950
951 if (RT_FAILURE(rc))
952 {
953 if (pvData)
954 RTMemFree(pvData);
955
956 if (rc != VERR_CANCELLED)
957 {
958 LogRel(("DnD: Receiving data failed with %Rrc\n", rc));
959
960 int rc2 = VbglR3DnDHGSendProgress(pCtx, DND_PROGRESS_ERROR, 100 /* Percent */, rc);
961 if (RT_FAILURE(rc2))
962 LogRel(("DnD: Unable to send progress error %Rrc to host: %Rrc\n", rc, rc2));
963 }
964 }
965
966 LogFlowFuncLeaveRC(rc);
967 return rc;
968}
969
970#ifdef VBOX_WITH_DRAG_AND_DROP_GH
971/**
972 * Guest -> Host
973 * Utility function to receive the HOST_DND_FN_GH_REQ_PENDING message from the host.
974 *
975 * @returns IPRT status code.
976 * @param pCtx DnD context to use.
977 * @param puScreenID For which screen on the host the request is for. Optional.
978 */
979static int vbglR3DnDGHRecvPending(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puScreenID)
980{
981 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
982 /* pScreenID is optional. */
983
984 HGCMMsgGHReqPending Msg;
985 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_GH_REQ_PENDING, 2);
986 /** @todo Context ID not used yet. */
987 Msg.u.v3.uContext.SetUInt32(0);
988 Msg.u.v3.uScreenId.SetUInt32(0);
989
990 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
991 if (RT_SUCCESS(rc))
992 {
993 /** @todo Context ID not used yet. */
994 if (puScreenID)
995 rc = Msg.u.v3.uContext.GetUInt32(puScreenID);
996 }
997
998 return rc;
999}
1000
1001/**
1002 * Guest -> Host
1003 * Utility function to receive the HOST_DND_FN_GH_EVT_DROPPED message from the host.
1004 *
1005 * @returns IPRT status code.
1006 * @param pCtx DnD context to use.
1007 * @param ppszFormat Requested data format from the host. Optional.
1008 * @param pcbFormat Size of requested data format (in bytes). Optional.
1009 * @param puAction Requested action from the host. Optional.
1010 */
1011static int vbglR3DnDGHRecvDropped(PVBGLR3GUESTDNDCMDCTX pCtx,
1012 char **ppszFormat,
1013 uint32_t *pcbFormat,
1014 uint32_t *puAction)
1015{
1016 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1017 /* The rest is optional. */
1018
1019 const uint32_t cbFormatTmp = pCtx->cbMaxChunkSize;
1020
1021 char *pszFormatTmp = static_cast<char *>(RTMemAlloc(cbFormatTmp));
1022 if (!pszFormatTmp)
1023 return VERR_NO_MEMORY;
1024
1025 HGCMMsgGHDropped Msg;
1026 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_GH_EVT_DROPPED, 4);
1027 Msg.u.v3.uContext.SetUInt32(0);
1028 Msg.u.v3.pvFormat.SetPtr(pszFormatTmp, cbFormatTmp);
1029 Msg.u.v3.cbFormat.SetUInt32(0);
1030 Msg.u.v3.uAction.SetUInt32(0);
1031
1032 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1033 if (RT_SUCCESS(rc))
1034 {
1035 /** @todo Context ID not used yet. */
1036 if (pcbFormat)
1037 rc = Msg.u.v3.cbFormat.GetUInt32(pcbFormat);
1038 if (RT_SUCCESS(rc) && puAction)
1039 rc = Msg.u.v3.uAction.GetUInt32(puAction);
1040
1041 if (RT_SUCCESS(rc))
1042 {
1043 *ppszFormat = RTStrDup(pszFormatTmp);
1044 if (!*ppszFormat)
1045 rc = VERR_NO_MEMORY;
1046 }
1047 }
1048
1049 RTMemFree(pszFormatTmp);
1050
1051 return rc;
1052}
1053#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1054
1055
1056/*********************************************************************************************************************************
1057* Public functions *
1058*********************************************************************************************************************************/
1059
1060/**
1061 * Connects a DnD context to the DnD host service.
1062 *
1063 * @returns IPRT status code.
1064 * @param pCtx DnD context to connect.
1065 */
1066VBGLR3DECL(int) VbglR3DnDConnect(PVBGLR3GUESTDNDCMDCTX pCtx)
1067{
1068 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1069
1070 /* Initialize header */
1071 int rc = VbglR3HGCMConnect("VBoxDragAndDropSvc", &pCtx->uClientID);
1072 if (RT_FAILURE(rc))
1073 return rc;
1074 Assert(pCtx->uClientID);
1075
1076 /* Set the default protocol version we would like to use.
1077 * Deprecated since VBox 6.1.x, but let this set to 3 to (hopefully) not break things. */
1078 pCtx->uProtocolDeprecated = 3;
1079
1080 pCtx->fHostFeatures = VBOX_DND_HF_NONE;
1081 pCtx->fGuestFeatures = VBOX_DND_GF_NONE;
1082
1083 /*
1084 * Get the VM's session ID.
1085 * This is not fatal in case we're running with an ancient VBox version.
1086 */
1087 pCtx->uSessionID = 0;
1088 int rc2 = VbglR3GetSessionId(&pCtx->uSessionID); RT_NOREF(rc2);
1089 LogFlowFunc(("uSessionID=%RU64, rc=%Rrc\n", pCtx->uSessionID, rc2));
1090
1091 /*
1092 * Try sending the connect message to tell the protocol version to use.
1093 * Note: This might fail when the Guest Additions run on an older VBox host (< VBox 5.0) which
1094 * does not implement this command.
1095 */
1096 HGCMMsgConnect Msg;
1097 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_CONNECT, 3);
1098 Msg.u.v3.uContext.SetUInt32(0); /** @todo Context ID not used yet. */
1099 Msg.u.v3.uProtocol.SetUInt32(pCtx->uProtocolDeprecated); /* Deprecated since VBox 6.1.x. */
1100 Msg.u.v3.uFlags.SetUInt32(0); /* Unused at the moment. */
1101
1102 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1103 if (RT_SUCCESS(rc))
1104 {
1105 /* Set the protocol version we're going to use as told by the host. */
1106 rc = Msg.u.v3.uProtocol.GetUInt32(&pCtx->uProtocolDeprecated); AssertRC(rc);
1107
1108 /*
1109 * Next is reporting our features. If this fails, assume older host.
1110 */
1111 rc2 = VbglR3DnDReportFeatures(pCtx->uClientID, pCtx->fGuestFeatures, &pCtx->fHostFeatures);
1112 if (RT_SUCCESS(rc2))
1113 {
1114 LogRel2(("DnD: Guest features: %#RX64 - Host features: %#RX64\n",
1115 pCtx->fGuestFeatures, pCtx->fHostFeatures));
1116 }
1117 else /* Failing here is not fatal; might be running with an older host. */
1118 {
1119 AssertLogRelMsg(rc2 == VERR_NOT_SUPPORTED || rc2 == VERR_NOT_IMPLEMENTED,
1120 ("Reporting features failed: %Rrc\n", rc2));
1121 }
1122
1123 pCtx->cbMaxChunkSize = DND_DEFAULT_CHUNK_SIZE; /** @todo Use a scratch buffer on the heap? */
1124 }
1125 else
1126 pCtx->uProtocolDeprecated = 0; /* We're using protocol v0 (initial draft) as a fallback. */
1127
1128 LogFlowFunc(("uClient=%RU32, uProtocol=%RU32, rc=%Rrc\n", pCtx->uClientID, pCtx->uProtocolDeprecated, rc));
1129 return rc;
1130}
1131
1132/**
1133 * Disconnects a given DnD context from the DnD host service.
1134 *
1135 * @returns IPRT status code.
1136 * @param pCtx DnD context to disconnect.
1137 * The context is invalid afterwards on successful disconnection.
1138 */
1139VBGLR3DECL(int) VbglR3DnDDisconnect(PVBGLR3GUESTDNDCMDCTX pCtx)
1140{
1141 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1142
1143 if (!pCtx->uClientID) /* Already disconnected? Bail out early. */
1144 return VINF_SUCCESS;
1145
1146 int rc = VbglR3HGCMDisconnect(pCtx->uClientID);
1147 if (RT_SUCCESS(rc))
1148 pCtx->uClientID = 0;
1149
1150 return rc;
1151}
1152
1153/**
1154 * Reports features to the host and retrieve host feature set.
1155 *
1156 * @returns VBox status code.
1157 * @param idClient The client ID returned by VbglR3DnDConnect().
1158 * @param fGuestFeatures Features to report, VBOX_DND_GF_XXX.
1159 * @param pfHostFeatures Where to store the features VBOX_DND_HF_XXX.
1160 */
1161VBGLR3DECL(int) VbglR3DnDReportFeatures(uint32_t idClient, uint64_t fGuestFeatures, uint64_t *pfHostFeatures)
1162{
1163 int rc;
1164 do
1165 {
1166 struct
1167 {
1168 VBGLIOCHGCMCALL Hdr;
1169 HGCMFunctionParameter f64Features0;
1170 HGCMFunctionParameter f64Features1;
1171 } Msg;
1172 VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_DND_FN_REPORT_FEATURES, 2);
1173 VbglHGCMParmUInt64Set(&Msg.f64Features0, fGuestFeatures);
1174 VbglHGCMParmUInt64Set(&Msg.f64Features1, VBOX_DND_GF_1_MUST_BE_ONE);
1175
1176 rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
1177 if (RT_SUCCESS(rc))
1178 {
1179 Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit);
1180 Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit);
1181 if (Msg.f64Features1.u.value64 & VBOX_DND_GF_1_MUST_BE_ONE)
1182 rc = VERR_NOT_SUPPORTED;
1183 else if (pfHostFeatures)
1184 *pfHostFeatures = Msg.f64Features0.u.value64;
1185 break;
1186 }
1187 } while (rc == VERR_INTERRUPTED);
1188 return rc;
1189
1190}
1191
1192/**
1193 * Receives the next upcoming DnD event.
1194 *
1195 * This is the main function DnD clients call in order to implement any DnD functionality.
1196 * The purpose of it is to abstract the actual DnD protocol handling as much as possible from
1197 * the clients -- those only need to react to certain events, regardless of how the underlying
1198 * protocol actually is working.
1199 *
1200 * @returns IPRT status code.
1201 * @param pCtx DnD context to work with.
1202 * @param ppEvent Next DnD event received on success; needs to be free'd by the client calling
1203 * VbglR3DnDEventFree() when done.
1204 */
1205VBGLR3DECL(int) VbglR3DnDEventGetNext(PVBGLR3GUESTDNDCMDCTX pCtx, PVBGLR3DNDEVENT *ppEvent)
1206{
1207 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1208 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
1209
1210 PVBGLR3DNDEVENT pEvent = (PVBGLR3DNDEVENT)RTMemAllocZ(sizeof(VBGLR3DNDEVENT));
1211 if (!pEvent)
1212 return VERR_NO_MEMORY;
1213
1214 uint32_t uMsg = 0;
1215 uint32_t cParms = 0;
1216 int rc = vbglR3DnDGetNextMsgType(pCtx, &uMsg, &cParms, true /* fWait */);
1217 if (RT_SUCCESS(rc))
1218 {
1219 /* Check for VM session change. */
1220 uint64_t uSessionID;
1221 int rc2 = VbglR3GetSessionId(&uSessionID);
1222 if ( RT_SUCCESS(rc2)
1223 && (uSessionID != pCtx->uSessionID))
1224 {
1225 LogRel2(("DnD: VM session ID changed to %RU64\n", uSessionID));
1226 rc = VbglR3DnDDisconnect(pCtx);
1227 if (RT_SUCCESS(rc))
1228 rc = VbglR3DnDConnect(pCtx);
1229 }
1230 }
1231
1232 if (rc == VERR_CANCELLED) /* Host service told us that we have to bail out. */
1233 {
1234 LogRel2(("DnD: Host service requested termination\n"));
1235
1236 pEvent->enmType = VBGLR3DNDEVENTTYPE_QUIT;
1237 *ppEvent = pEvent;
1238
1239 return VINF_SUCCESS;
1240 }
1241
1242 if (RT_SUCCESS(rc))
1243 {
1244 LogFunc(("Handling uMsg=%RU32\n", uMsg));
1245
1246 switch(uMsg)
1247 {
1248 case HOST_DND_FN_HG_EVT_ENTER:
1249 {
1250 rc = vbglR3DnDHGRecvAction(pCtx,
1251 uMsg,
1252 &pEvent->u.HG_Enter.uScreenID,
1253 NULL /* puXPos */,
1254 NULL /* puYPos */,
1255 NULL /* uDefAction */,
1256 &pEvent->u.HG_Enter.dndLstActionsAllowed,
1257 &pEvent->u.HG_Enter.pszFormats,
1258 &pEvent->u.HG_Enter.cbFormats);
1259 if (RT_SUCCESS(rc))
1260 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_ENTER;
1261 break;
1262 }
1263 case HOST_DND_FN_HG_EVT_MOVE:
1264 {
1265 rc = vbglR3DnDHGRecvAction(pCtx,
1266 uMsg,
1267 NULL /* puScreenId */,
1268 &pEvent->u.HG_Move.uXpos,
1269 &pEvent->u.HG_Move.uYpos,
1270 &pEvent->u.HG_Move.dndActionDefault,
1271 NULL /* puAllActions */,
1272 NULL /* pszFormats */,
1273 NULL /* pcbFormats */);
1274 if (RT_SUCCESS(rc))
1275 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_MOVE;
1276 break;
1277 }
1278 case HOST_DND_FN_HG_EVT_DROPPED:
1279 {
1280 rc = vbglR3DnDHGRecvAction(pCtx,
1281 uMsg,
1282 NULL /* puScreenId */,
1283 &pEvent->u.HG_Drop.uXpos,
1284 &pEvent->u.HG_Drop.uYpos,
1285 &pEvent->u.HG_Drop.dndActionDefault,
1286 NULL /* puAllActions */,
1287 NULL /* pszFormats */,
1288 NULL /* pcbFormats */);
1289 if (RT_SUCCESS(rc))
1290 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_DROP;
1291 break;
1292 }
1293 case HOST_DND_FN_HG_EVT_LEAVE:
1294 {
1295 rc = vbglR3DnDHGRecvLeave(pCtx);
1296 if (RT_SUCCESS(rc))
1297 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_LEAVE;
1298 break;
1299 }
1300 case HOST_DND_FN_HG_SND_DATA_HDR:
1301 {
1302 rc = vbglR3DnDHGRecvDataMain(pCtx, &pEvent->u.HG_Received.Meta);
1303 if (RT_SUCCESS(rc))
1304 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_RECEIVE;
1305 break;
1306 }
1307 case HOST_DND_FN_HG_SND_DIR:
1308 RT_FALL_THROUGH();
1309 case HOST_DND_FN_HG_SND_FILE_HDR:
1310 RT_FALL_THROUGH();
1311 case HOST_DND_FN_HG_SND_FILE_DATA:
1312 {
1313 /*
1314 * All messages for this block are handled internally
1315 * by vbglR3DnDHGRecvDataMain(), see above.
1316 *
1317 * So if we land here our code is buggy.
1318 */
1319 rc = VERR_WRONG_ORDER;
1320 break;
1321 }
1322 case HOST_DND_FN_CANCEL:
1323 {
1324 rc = vbglR3DnDHGRecvCancel(pCtx);
1325 if (RT_SUCCESS(rc))
1326 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_CANCEL;
1327 break;
1328 }
1329#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1330 case HOST_DND_FN_GH_REQ_PENDING:
1331 {
1332 rc = vbglR3DnDGHRecvPending(pCtx, &pEvent->u.GH_IsPending.uScreenID);
1333 if (RT_SUCCESS(rc))
1334 pEvent->enmType = VBGLR3DNDEVENTTYPE_GH_REQ_PENDING;
1335 break;
1336 }
1337 case HOST_DND_FN_GH_EVT_DROPPED:
1338 {
1339 rc = vbglR3DnDGHRecvDropped(pCtx,
1340 &pEvent->u.GH_Drop.pszFormat,
1341 &pEvent->u.GH_Drop.cbFormat,
1342 &pEvent->u.GH_Drop.dndActionRequested);
1343 if (RT_SUCCESS(rc))
1344 pEvent->enmType = VBGLR3DNDEVENTTYPE_GH_DROP;
1345 break;
1346 }
1347#endif
1348 default:
1349 {
1350 rc = VERR_NOT_SUPPORTED;
1351 break;
1352 }
1353 }
1354 }
1355
1356 if (RT_FAILURE(rc))
1357 {
1358 VbglR3DnDEventFree(pEvent);
1359 LogRel(("DnD: Handling message %s (%#x) failed with %Rrc\n", DnDHostMsgToStr(uMsg), uMsg, rc));
1360 }
1361 else
1362 *ppEvent = pEvent;
1363
1364 return rc;
1365}
1366
1367/**
1368 * Frees (destroys) a formerly allocated DnD event.
1369 *
1370 * @returns IPRT status code.
1371 * @param pEvent Event to free (destroy).
1372 */
1373VBGLR3DECL(void) VbglR3DnDEventFree(PVBGLR3DNDEVENT pEvent)
1374{
1375 if (!pEvent)
1376 return;
1377
1378 /* Some messages require additional cleanup. */
1379 switch (pEvent->enmType)
1380 {
1381 case VBGLR3DNDEVENTTYPE_HG_ENTER:
1382 {
1383 if (pEvent->u.HG_Enter.pszFormats)
1384 RTStrFree(pEvent->u.HG_Enter.pszFormats);
1385 break;
1386 }
1387
1388#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1389 case VBGLR3DNDEVENTTYPE_GH_DROP:
1390 {
1391 if (pEvent->u.GH_Drop.pszFormat)
1392 RTStrFree(pEvent->u.GH_Drop.pszFormat);
1393 break;
1394 }
1395#endif
1396 case VBGLR3DNDEVENTTYPE_HG_RECEIVE:
1397 {
1398 PVBGLR3GUESTDNDMETADATA pMeta = &pEvent->u.HG_Received.Meta;
1399 switch (pMeta->enmType)
1400 {
1401 case VBGLR3GUESTDNDMETADATATYPE_RAW:
1402 {
1403 if (pMeta->u.Raw.pvMeta)
1404 {
1405 Assert(pMeta->u.Raw.cbMeta);
1406 RTMemFree(pMeta->u.Raw.pvMeta);
1407 pMeta->u.Raw.cbMeta = 0;
1408 }
1409 break;
1410 }
1411
1412 case VBGLR3GUESTDNDMETADATATYPE_URI_LIST:
1413 {
1414 DnDTransferListDestroy(&pMeta->u.URI.Transfer);
1415 break;
1416 }
1417
1418 default:
1419 break;
1420 }
1421 break;
1422 }
1423
1424 default:
1425 break;
1426 }
1427
1428 RTMemFree(pEvent);
1429 pEvent = NULL;
1430}
1431
1432VBGLR3DECL(int) VbglR3DnDHGSendAckOp(PVBGLR3GUESTDNDCMDCTX pCtx, VBOXDNDACTION dndAction)
1433{
1434 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1435
1436 HGCMMsgHGAck Msg;
1437 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_HG_ACK_OP, 2);
1438 /** @todo Context ID not used yet. */
1439 Msg.u.v3.uContext.SetUInt32(0);
1440 Msg.u.v3.uAction.SetUInt32(dndAction);
1441
1442 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1443}
1444
1445/**
1446 * Host -> Guest
1447 * Requests the actual DnD data to be sent from the host.
1448 *
1449 * @returns IPRT status code.
1450 * @param pCtx DnD context to use.
1451 * @param pcszFormat Format to request the data from the host in.
1452 */
1453VBGLR3DECL(int) VbglR3DnDHGSendReqData(PVBGLR3GUESTDNDCMDCTX pCtx, const char* pcszFormat)
1454{
1455 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1456 AssertPtrReturn(pcszFormat, VERR_INVALID_POINTER);
1457 if (!RTStrIsValidEncoding(pcszFormat))
1458 return VERR_INVALID_PARAMETER;
1459
1460 const uint32_t cbFormat = (uint32_t)strlen(pcszFormat) + 1; /* Include termination */
1461
1462 HGCMMsgHGReqData Msg;
1463 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_HG_REQ_DATA, 3);
1464 /** @todo Context ID not used yet. */
1465 Msg.u.v3.uContext.SetUInt32(0);
1466 Msg.u.v3.pvFormat.SetPtr((void*)pcszFormat, cbFormat);
1467 Msg.u.v3.cbFormat.SetUInt32(cbFormat);
1468
1469 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1470}
1471
1472/**
1473 * Host -> Guest
1474 * Reports back its progress back to the host.
1475 *
1476 * @returns IPRT status code.
1477 * @param pCtx DnD context to use.
1478 * @param uStatus DnD status to report.
1479 * @param uPercent Overall progress (in percent) to report.
1480 * @param rcErr Error code (IPRT-style) to report.
1481 */
1482VBGLR3DECL(int) VbglR3DnDHGSendProgress(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uStatus, uint8_t uPercent, int rcErr)
1483{
1484 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1485 AssertReturn(uStatus > DND_PROGRESS_UNKNOWN, VERR_INVALID_PARAMETER);
1486
1487 HGCMMsgHGProgress Msg;
1488 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_HG_EVT_PROGRESS, 4);
1489 /** @todo Context ID not used yet. */
1490 Msg.u.v3.uContext.SetUInt32(0);
1491 Msg.u.v3.uStatus.SetUInt32(uStatus);
1492 Msg.u.v3.uPercent.SetUInt32(uPercent);
1493 Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1494
1495 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1496}
1497
1498#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1499/**
1500 * Guest -> Host
1501 * Acknowledges that there currently is a drag'n drop operation in progress on the guest,
1502 * which eventually could be dragged over to the host.
1503 *
1504 * @returns IPRT status code.
1505 * @param pCtx DnD context to use.
1506 * @param dndActionDefault Default action for the operation to report.
1507 * @param dndLstActionsAllowed All available actions for the operation to report.
1508 * @param pcszFormats Available formats for the operation to report.
1509 * @param cbFormats Size (in bytes) of formats to report.
1510 */
1511VBGLR3DECL(int) VbglR3DnDGHSendAckPending(PVBGLR3GUESTDNDCMDCTX pCtx,
1512 VBOXDNDACTION dndActionDefault, VBOXDNDACTIONLIST dndLstActionsAllowed,
1513 const char* pcszFormats, uint32_t cbFormats)
1514{
1515 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1516 AssertPtrReturn(pcszFormats, VERR_INVALID_POINTER);
1517 AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
1518
1519 if (!RTStrIsValidEncoding(pcszFormats))
1520 return VERR_INVALID_UTF8_ENCODING;
1521
1522 HGCMMsgGHAckPending Msg;
1523 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GH_ACK_PENDING, 5);
1524 /** @todo Context ID not used yet. */
1525 Msg.u.v3.uContext.SetUInt32(0);
1526 Msg.u.v3.uDefAction.SetUInt32(dndActionDefault);
1527 Msg.u.v3.uAllActions.SetUInt32(dndLstActionsAllowed);
1528 Msg.u.v3.pvFormats.SetPtr((void*)pcszFormats, cbFormats);
1529 Msg.u.v3.cbFormats.SetUInt32(cbFormats);
1530
1531 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1532}
1533
1534/**
1535 * Guest -> Host
1536 * Utility function to send DnD data from guest to the host.
1537 *
1538 * @returns IPRT status code.
1539 * @param pCtx DnD context to use.
1540 * @param pvData Data block to send.
1541 * @param cbData Size (in bytes) of data block to send.
1542 * @param pDataHdr Data header to use -- needed for accounting.
1543 */
1544static int vbglR3DnDGHSendDataInternal(PVBGLR3GUESTDNDCMDCTX pCtx,
1545 void *pvData, uint64_t cbData, PVBOXDNDSNDDATAHDR pDataHdr)
1546{
1547 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1548 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1549 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1550 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
1551
1552 HGCMMsgGHSendDataHdr MsgHdr;
1553 VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_DATA_HDR, 12);
1554 MsgHdr.uContext.SetUInt32(0); /** @todo Not used yet. */
1555 MsgHdr.uFlags.SetUInt32(0); /** @todo Not used yet. */
1556 MsgHdr.uScreenId.SetUInt32(0); /** @todo Not used for guest->host (yet). */
1557 MsgHdr.cbTotal.SetUInt64(pDataHdr->cbTotal);
1558 MsgHdr.cbMeta.SetUInt32(pDataHdr->cbMeta);
1559 MsgHdr.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
1560 MsgHdr.cbMetaFmt.SetUInt32(pDataHdr->cbMetaFmt);
1561 MsgHdr.cObjects.SetUInt64(pDataHdr->cObjects);
1562 MsgHdr.enmCompression.SetUInt32(0); /** @todo Not used yet. */
1563 MsgHdr.enmChecksumType.SetUInt32(RTDIGESTTYPE_INVALID); /** @todo Not used yet. */
1564 MsgHdr.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
1565 MsgHdr.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
1566
1567 int rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr));
1568
1569 LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU64, rc=%Rrc\n",
1570 pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects, rc));
1571
1572 if (RT_SUCCESS(rc))
1573 {
1574 HGCMMsgGHSendData MsgData;
1575 VBGL_HGCM_HDR_INIT(&MsgData.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_DATA, 5);
1576 MsgData.u.v3.uContext.SetUInt32(0); /** @todo Not used yet. */
1577 MsgData.u.v3.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
1578 MsgData.u.v3.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
1579
1580 uint32_t cbCurChunk;
1581 const uint32_t cbMaxChunk = pCtx->cbMaxChunkSize;
1582 uint32_t cbSent = 0;
1583
1584 while (cbSent < cbData)
1585 {
1586 cbCurChunk = RT_MIN(cbData - cbSent, cbMaxChunk);
1587 MsgData.u.v3.pvData.SetPtr(static_cast<uint8_t *>(pvData) + cbSent, cbCurChunk);
1588 MsgData.u.v3.cbData.SetUInt32(cbCurChunk);
1589
1590 rc = VbglR3HGCMCall(&MsgData.hdr, sizeof(MsgData));
1591 if (RT_FAILURE(rc))
1592 break;
1593
1594 cbSent += cbCurChunk;
1595 }
1596
1597 LogFlowFunc(("cbMaxChunk=%RU32, cbData=%RU64, cbSent=%RU32, rc=%Rrc\n",
1598 cbMaxChunk, cbData, cbSent, rc));
1599
1600 if (RT_SUCCESS(rc))
1601 Assert(cbSent == cbData);
1602 }
1603
1604 LogFlowFuncLeaveRC(rc);
1605 return rc;
1606}
1607
1608/**
1609 * Guest -> Host
1610 * Utility function to send a guest directory to the host.
1611 *
1612 * @returns IPRT status code.
1613 * @param pCtx DnD context to use.
1614 * @param pObj transfer object containing the directory to send.
1615 */
1616static int vbglR3DnDGHSendDir(PVBGLR3GUESTDNDCMDCTX pCtx, DNDTRANSFEROBJECT *pObj)
1617{
1618 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1619 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1620 AssertReturn(DnDTransferObjectGetType(pObj) == DNDTRANSFEROBJTYPE_DIRECTORY, VERR_INVALID_PARAMETER);
1621
1622 const char *pcszPath = DnDTransferObjectGetDestPath(pObj);
1623 const size_t cbPath = RTStrNLen(pcszPath, RTPATH_MAX) + 1 /* Include termination. */;
1624 const RTFMODE fMode = DnDTransferObjectGetMode(pObj);
1625
1626 LogFlowFunc(("strDir=%s (%zu bytes), fMode=0x%x\n", pcszPath, cbPath, fMode));
1627
1628 if (cbPath > RTPATH_MAX + 1) /* Can't happen, but check anyway. */
1629 return VERR_INVALID_PARAMETER;
1630
1631 HGCMMsgGHSendDir Msg;
1632 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_DIR, 4);
1633 /** @todo Context ID not used yet. */
1634 Msg.u.v3.uContext.SetUInt32(0);
1635 Msg.u.v3.pvName.SetPtr((void *)pcszPath, (uint32_t)cbPath);
1636 Msg.u.v3.cbName.SetUInt32((uint32_t)cbPath);
1637 Msg.u.v3.fMode.SetUInt32(fMode);
1638
1639 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1640}
1641
1642/**
1643 * Guest -> Host
1644 * Utility function to send a file from the guest to the host.
1645 *
1646 * @returns IPRT status code.
1647 * @param pCtx DnD context to use.
1648 * @param pObj Transfer object containing the file to send.
1649 */
1650static int vbglR3DnDGHSendFile(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFEROBJECT pObj)
1651{
1652 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1653 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1654 AssertReturn(DnDTransferObjectIsOpen(pObj) == false, VERR_INVALID_STATE);
1655 AssertReturn(DnDTransferObjectGetType(pObj) == DNDTRANSFEROBJTYPE_FILE, VERR_INVALID_PARAMETER);
1656
1657 uint64_t fOpen = RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE;
1658
1659 int rc = DnDTransferObjectOpen(pObj, fOpen, 0 /* fMode */, DNDTRANSFEROBJECT_FLAGS_NONE);
1660 if (RT_FAILURE(rc))
1661 return rc;
1662
1663 uint32_t cbBuf = pCtx->cbMaxChunkSize;
1664 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1665 void *pvBuf = RTMemAlloc(cbBuf); /** @todo Make this buffer part of PVBGLR3GUESTDNDCMDCTX? */
1666 if (!pvBuf)
1667 {
1668 DnDTransferObjectClose(pObj);
1669 return VERR_NO_MEMORY;
1670 }
1671
1672 const char *pcszPath = DnDTransferObjectGetDestPath(pObj);
1673 const size_t cchPath = RTStrNLen(pcszPath, RTPATH_MAX);
1674 const uint64_t cbSize = DnDTransferObjectGetSize(pObj);
1675 const RTFMODE fMode = DnDTransferObjectGetMode(pObj);
1676
1677 LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", pcszPath, cchPath, cbSize, fMode));
1678
1679 HGCMMsgGHSendFileHdr MsgHdr;
1680 VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_FILE_HDR, 6);
1681 MsgHdr.uContext.SetUInt32(0); /* Context ID; unused at the moment. */
1682 MsgHdr.pvName.SetPtr((void *)pcszPath, (uint32_t)(cchPath + 1)); /* Include termination. */
1683 MsgHdr.cbName.SetUInt32((uint32_t)(cchPath + 1)); /* Ditto. */
1684 MsgHdr.uFlags.SetUInt32(0); /* Flags; unused at the moment. */
1685 MsgHdr.fMode.SetUInt32(fMode); /* File mode */
1686 MsgHdr.cbTotal.SetUInt64(cbSize); /* File size (in bytes). */
1687
1688 rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr));
1689
1690 LogFlowFunc(("Sending file header resulted in %Rrc\n", rc));
1691
1692 if (RT_SUCCESS(rc))
1693 {
1694 /*
1695 * Send the actual file data, chunk by chunk.
1696 */
1697 HGCMMsgGHSendFileData Msg;
1698 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_FILE_DATA, 5);
1699 Msg.u.v3.uContext.SetUInt32(0);
1700 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
1701 Msg.u.v3.cbChecksum.SetUInt32(0);
1702
1703 uint64_t cbToReadTotal = cbSize;
1704 uint64_t cbWrittenTotal = 0;
1705 while (cbToReadTotal)
1706 {
1707 uint32_t cbToRead = RT_MIN(cbToReadTotal, cbBuf);
1708 uint32_t cbRead = 0;
1709 if (cbToRead)
1710 rc = DnDTransferObjectRead(pObj, pvBuf, cbToRead, &cbRead);
1711
1712 LogFlowFunc(("cbToReadTotal=%RU64, cbToRead=%RU32, cbRead=%RU32, rc=%Rrc\n",
1713 cbToReadTotal, cbToRead, cbRead, rc));
1714
1715 if ( RT_SUCCESS(rc)
1716 && cbRead)
1717 {
1718 Msg.u.v3.pvData.SetPtr(pvBuf, cbRead);
1719 Msg.u.v3.cbData.SetUInt32(cbRead);
1720 /** @todo Calculate + set checksums. */
1721
1722 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1723 }
1724
1725 if (RT_FAILURE(rc))
1726 {
1727 LogFlowFunc(("Reading failed with rc=%Rrc\n", rc));
1728 break;
1729 }
1730
1731 Assert(cbRead <= cbToReadTotal);
1732 cbToReadTotal -= cbRead;
1733 cbWrittenTotal += cbRead;
1734
1735 LogFlowFunc(("%RU64/%RU64 -- %RU8%%\n", cbWrittenTotal, cbSize, cbWrittenTotal * 100 / cbSize));
1736 };
1737 }
1738
1739 RTMemFree(pvBuf);
1740 DnDTransferObjectClose(pObj);
1741
1742 LogFlowFuncLeaveRC(rc);
1743 return rc;
1744}
1745
1746/**
1747 * Guest -> Host
1748 * Utility function to send a transfer object from guest to the host.
1749 *
1750 * @returns IPRT status code.
1751 * @param pCtx DnD context to use.
1752 * @param pObj Transfer object to send from guest to the host.
1753 */
1754static int vbglR3DnDGHSendURIObject(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFEROBJECT pObj)
1755{
1756 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1757 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1758
1759 int rc;
1760
1761 const DNDTRANSFEROBJTYPE enmType = DnDTransferObjectGetType(pObj);
1762
1763 switch (enmType)
1764 {
1765 case DNDTRANSFEROBJTYPE_DIRECTORY:
1766 rc = vbglR3DnDGHSendDir(pCtx, pObj);
1767 break;
1768
1769 case DNDTRANSFEROBJTYPE_FILE:
1770 rc = vbglR3DnDGHSendFile(pCtx, pObj);
1771 break;
1772
1773 default:
1774 AssertMsgFailed(("Object type %ld not implemented\n", enmType));
1775 rc = VERR_NOT_IMPLEMENTED;
1776 break;
1777 }
1778
1779 return rc;
1780}
1781
1782/**
1783 * Guest -> Host
1784 * Utility function to send raw data from guest to the host.
1785 *
1786 * @returns IPRT status code.
1787 * @param pCtx DnD context to use.
1788 * @param pvData Block to raw data to send.
1789 * @param cbData Size (in bytes) of raw data to send.
1790 */
1791static int vbglR3DnDGHSendRawData(PVBGLR3GUESTDNDCMDCTX pCtx, void *pvData, size_t cbData)
1792{
1793 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1794 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1795 /* cbData can be 0. */
1796
1797 VBOXDNDDATAHDR dataHdr;
1798 RT_ZERO(dataHdr);
1799
1800 dataHdr.cbMeta = (uint32_t)cbData;
1801 dataHdr.cbTotal = cbData;
1802
1803 return vbglR3DnDGHSendDataInternal(pCtx, pvData, cbData, &dataHdr);
1804}
1805
1806/**
1807 * Guest -> Host
1808 * Utility function to send transfer data from guest to the host.
1809 *
1810 * @returns IPRT status code.
1811 * @param pCtx DnD context to use.
1812 * @param pTransferList Dnd transfer list to send.
1813 */
1814static int vbglR3DnDGHSendTransferData(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFERLIST pTransferList)
1815{
1816 AssertPtrReturn(pCtx,VERR_INVALID_POINTER);
1817 AssertPtrReturn(pTransferList, VERR_INVALID_POINTER);
1818
1819 /*
1820 * Send the (meta) data; in case of URIs it's the root entries of a
1821 * transfer list the host needs to know upfront to set up the drag'n drop operation.
1822 */
1823 char *pszList = NULL;
1824 size_t cbList;
1825 int rc = DnDTransferListGetRoots(pTransferList, DNDTRANSFERLISTFMT_URI, &pszList, &cbList);
1826 if (RT_FAILURE(rc))
1827 return rc;
1828
1829 void *pvURIList = (void *)pszList;
1830 uint32_t cbURLIist = (uint32_t)cbList;
1831
1832 /* The total size also contains the size of the meta data. */
1833 uint64_t cbTotal = cbURLIist;
1834 cbTotal += DnDTransferListObjTotalBytes(pTransferList);
1835
1836 /* We're going to send a transfer list in text format. */
1837 const char szMetaFmt[] = "text/uri-list";
1838 const uint32_t cbMetaFmt = (uint32_t)strlen(szMetaFmt) + 1; /* Include termination. */
1839
1840 VBOXDNDDATAHDR dataHdr;
1841 dataHdr.uFlags = 0; /* Flags not used yet. */
1842 dataHdr.cbTotal = cbTotal;
1843 dataHdr.cbMeta = cbURLIist;
1844 dataHdr.pvMetaFmt = (void *)szMetaFmt;
1845 dataHdr.cbMetaFmt = cbMetaFmt;
1846 dataHdr.cObjects = DnDTransferListObjCount(pTransferList);
1847
1848 rc = vbglR3DnDGHSendDataInternal(pCtx, pvURIList, cbURLIist, &dataHdr);
1849
1850 if (RT_SUCCESS(rc))
1851 {
1852 while (DnDTransferListObjCount(pTransferList))
1853 {
1854 PDNDTRANSFEROBJECT pObj = DnDTransferListObjGetFirst(pTransferList);
1855
1856 rc = vbglR3DnDGHSendURIObject(pCtx, pObj);
1857 if (RT_FAILURE(rc))
1858 break;
1859
1860 DnDTransferListObjRemoveFirst(pTransferList);
1861 }
1862
1863 Assert(DnDTransferListObjCount(pTransferList) == 0);
1864 }
1865
1866 return rc;
1867}
1868
1869/**
1870 * Guest -> Host
1871 * Sends data, which either can be raw or URI data, from guest to the host. This function
1872 * initiates the actual data transfer from guest to the host.
1873 *
1874 * @returns IPRT status code.
1875 * @param pCtx DnD context to use.
1876 * @param pszFormat In which format the data will be sent.
1877 * @param pvData Data block to send.
1878 * For URI data this must contain the absolute local URI paths, separated by DND_PATH_SEPARATOR_STR.
1879 * @param cbData Size (in bytes) of data block to send.
1880 */
1881VBGLR3DECL(int) VbglR3DnDGHSendData(PVBGLR3GUESTDNDCMDCTX pCtx, const char *pszFormat, void *pvData, uint32_t cbData)
1882{
1883 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1884 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
1885 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1886 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1887
1888 LogFlowFunc(("pszFormat=%s, pvData=%p, cbData=%RU32\n", pszFormat, pvData, cbData));
1889
1890 LogRel2(("DnD: Sending %RU32 bytes meta data in format '%s'\n", cbData, pszFormat));
1891
1892 int rc;
1893 if (DnDMIMEHasFileURLs(pszFormat, strlen(pszFormat)))
1894 {
1895 DNDTRANSFERLIST lstTransfer;
1896 RT_ZERO(lstTransfer);
1897
1898 rc = DnDTransferListInit(&lstTransfer);
1899 if (RT_SUCCESS(rc))
1900 {
1901 /** @todo Add symlink support (DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS) here. */
1902 /** @todo Add lazy loading (DNDTRANSFERLIST_FLAGS_LAZY) here. */
1903 const DNDTRANSFERLISTFLAGS fFlags = DNDTRANSFERLIST_FLAGS_RECURSIVE;
1904
1905 rc = DnDTransferListAppendPathsFromBuffer(&lstTransfer, DNDTRANSFERLISTFMT_URI, (const char *)pvData, cbData,
1906 DND_PATH_SEPARATOR_STR, fFlags);
1907 if (RT_SUCCESS(rc))
1908 rc = vbglR3DnDGHSendTransferData(pCtx, &lstTransfer);
1909 DnDTransferListDestroy(&lstTransfer);
1910 }
1911 }
1912 else
1913 rc = vbglR3DnDGHSendRawData(pCtx, pvData, cbData);
1914
1915 if (RT_FAILURE(rc))
1916 {
1917 LogRel(("DnD: Sending data failed with rc=%Rrc\n", rc));
1918
1919 if (rc != VERR_CANCELLED)
1920 {
1921 int rc2 = VbglR3DnDSendError(pCtx, rc);
1922 if (RT_FAILURE(rc2))
1923 LogFlowFunc(("Unable to send error (%Rrc) to host, rc=%Rrc\n", rc, rc2));
1924 }
1925 }
1926
1927 return rc;
1928}
1929#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1930
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette