VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestDnDSourceImpl.cpp@ 55535

最後變更 在這個檔案從55535是 55525,由 vboxsync 提交於 10 年 前

DnD: Handle receiving 0-byte files, only do workaround accounting on root URI objects.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 36.2 KB
 
1/* $Id: GuestDnDSourceImpl.cpp 55525 2015-04-29 15:21:19Z vboxsync $ */
2/** @file
3 * VBox Console COM Class implementation - Guest drag and drop source.
4 */
5
6/*
7 * Copyright (C) 2014-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include "GuestImpl.h"
23#include "GuestDnDSourceImpl.h"
24#include "GuestDnDPrivate.h"
25
26#include "Global.h"
27#include "AutoCaller.h"
28
29#include <iprt/dir.h>
30#include <iprt/file.h>
31#include <iprt/path.h>
32#include <iprt/uri.h>
33
34#include <iprt/cpp/utils.h> /* For unconst(). */
35
36#include <VBox/com/array.h>
37#include <VBox/GuestHost/DragAndDrop.h>
38#include <VBox/HostServices/DragAndDropSvc.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/**
47 * Base class for a source task.
48 */
49class GuestDnDSourceTask
50{
51public:
52
53 GuestDnDSourceTask(GuestDnDSource *pSource)
54 : mSource(pSource),
55 mRC(VINF_SUCCESS) { }
56
57 virtual ~GuestDnDSourceTask(void) { }
58
59 int getRC(void) const { return mRC; }
60 bool isOk(void) const { return RT_SUCCESS(mRC); }
61 const ComObjPtr<GuestDnDSource> &getSource(void) const { return mSource; }
62
63protected:
64
65 const ComObjPtr<GuestDnDSource> mSource;
66 int mRC;
67};
68
69/**
70 * Task structure for receiving data from a source using
71 * a worker thread.
72 */
73class RecvDataTask : public GuestDnDSourceTask
74{
75public:
76
77 RecvDataTask(GuestDnDSource *pSource, PRECVDATACTX pCtx)
78 : GuestDnDSourceTask(pSource)
79 , mpCtx(pCtx) { }
80
81 virtual ~RecvDataTask(void) { }
82
83 PRECVDATACTX getCtx(void) { return mpCtx; }
84
85protected:
86
87 /** Pointer to receive data context. */
88 PRECVDATACTX mpCtx;
89};
90
91// constructor / destructor
92/////////////////////////////////////////////////////////////////////////////
93
94DEFINE_EMPTY_CTOR_DTOR(GuestDnDSource)
95
96HRESULT GuestDnDSource::FinalConstruct(void)
97{
98 /* Set the maximum block size this source can handle to 64K. This always has
99 * been hardcoded until now. */
100 /* Note: Never ever rely on information from the guest; the host dictates what and
101 * how to do something, so try to negogiate a sensible value here later. */
102 mData.mcbBlockSize = _64K; /** @todo Make this configurable. */
103 mData.mfDropIsPending = false;
104
105 LogFlowThisFunc(("\n"));
106 return BaseFinalConstruct();
107}
108
109void GuestDnDSource::FinalRelease(void)
110{
111 LogFlowThisFuncEnter();
112 uninit();
113 BaseFinalRelease();
114 LogFlowThisFuncLeave();
115}
116
117// public initializer/uninitializer for internal purposes only
118/////////////////////////////////////////////////////////////////////////////
119
120int GuestDnDSource::init(const ComObjPtr<Guest>& pGuest)
121{
122 LogFlowThisFuncEnter();
123
124 /* Enclose the state transition NotReady->InInit->Ready. */
125 AutoInitSpan autoInitSpan(this);
126 AssertReturn(autoInitSpan.isOk(), E_FAIL);
127
128 unconst(m_pGuest) = pGuest;
129
130 /* Confirm a successful initialization when it's the case. */
131 autoInitSpan.setSucceeded();
132
133 return VINF_SUCCESS;
134}
135
136/**
137 * Uninitializes the instance.
138 * Called from FinalRelease().
139 */
140void GuestDnDSource::uninit(void)
141{
142 LogFlowThisFunc(("\n"));
143
144 /* Enclose the state transition Ready->InUninit->NotReady. */
145 AutoUninitSpan autoUninitSpan(this);
146 if (autoUninitSpan.uninitDone())
147 return;
148}
149
150// implementation of wrapped IDnDBase methods.
151/////////////////////////////////////////////////////////////////////////////
152
153HRESULT GuestDnDSource::isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
154{
155#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
156 ReturnComNotImplemented();
157#else /* VBOX_WITH_DRAG_AND_DROP */
158
159 AutoCaller autoCaller(this);
160 if (FAILED(autoCaller.rc())) return autoCaller.rc();
161
162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
163
164 return GuestDnDBase::i_isFormatSupported(aFormat, aSupported);
165#endif /* VBOX_WITH_DRAG_AND_DROP */
166}
167
168HRESULT GuestDnDSource::getFormats(std::vector<com::Utf8Str> &aFormats)
169{
170#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
171 ReturnComNotImplemented();
172#else /* VBOX_WITH_DRAG_AND_DROP */
173
174 AutoCaller autoCaller(this);
175 if (FAILED(autoCaller.rc())) return autoCaller.rc();
176
177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
178
179 return GuestDnDBase::i_getFormats(aFormats);
180#endif /* VBOX_WITH_DRAG_AND_DROP */
181}
182
183HRESULT GuestDnDSource::addFormats(const std::vector<com::Utf8Str> &aFormats)
184{
185#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
186 ReturnComNotImplemented();
187#else /* VBOX_WITH_DRAG_AND_DROP */
188
189 AutoCaller autoCaller(this);
190 if (FAILED(autoCaller.rc())) return autoCaller.rc();
191
192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
193
194 return GuestDnDBase::i_addFormats(aFormats);
195#endif /* VBOX_WITH_DRAG_AND_DROP */
196}
197
198HRESULT GuestDnDSource::removeFormats(const std::vector<com::Utf8Str> &aFormats)
199{
200#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
201 ReturnComNotImplemented();
202#else /* VBOX_WITH_DRAG_AND_DROP */
203
204 AutoCaller autoCaller(this);
205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
206
207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
208
209 return GuestDnDBase::i_removeFormats(aFormats);
210#endif /* VBOX_WITH_DRAG_AND_DROP */
211}
212
213HRESULT GuestDnDSource::getProtocolVersion(ULONG *aProtocolVersion)
214{
215#if !defined(VBOX_WITH_DRAG_AND_DROP)
216 ReturnComNotImplemented();
217#else /* VBOX_WITH_DRAG_AND_DROP */
218
219 AutoCaller autoCaller(this);
220 if (FAILED(autoCaller.rc())) return autoCaller.rc();
221
222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
223
224 return GuestDnDBase::i_getProtocolVersion(aProtocolVersion);
225#endif /* VBOX_WITH_DRAG_AND_DROP */
226}
227
228// implementation of wrapped IDnDTarget methods.
229/////////////////////////////////////////////////////////////////////////////
230
231HRESULT GuestDnDSource::dragIsPending(ULONG uScreenId,
232 std::vector<com::Utf8Str> &aFormats,
233 std::vector<DnDAction_T> &aAllowedActions,
234 DnDAction_T *aDefaultAction)
235{
236#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
237 ReturnComNotImplemented();
238#else /* VBOX_WITH_DRAG_AND_DROP */
239
240 AutoCaller autoCaller(this);
241 if (FAILED(autoCaller.rc())) return autoCaller.rc();
242
243 /* Determine guest DnD protocol to use. */
244 GuestDnDBase::getProtocolVersion(&mDataBase.mProtocolVersion);
245
246 /* Default is ignoring the action. */
247 DnDAction_T defaultAction = DnDAction_Ignore;
248
249 HRESULT hr = S_OK;
250
251 GuestDnDMsg Msg;
252 Msg.setType(DragAndDropSvc::HOST_DND_GH_REQ_PENDING);
253 Msg.setNextUInt32(uScreenId);
254
255 int rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
256 if (RT_SUCCESS(rc))
257 {
258 bool fFetchResult = true;
259 GuestDnDResponse *pResp = GuestDnDInst()->response();
260 if (pResp)
261 {
262 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
263 fFetchResult = false;
264
265 if (isDnDIgnoreAction(pResp->defAction()))
266 fFetchResult = false;
267
268 /* Fetch the default action to use. */
269 if (fFetchResult)
270 {
271 defaultAction = GuestDnD::toMainAction(pResp->defAction());
272
273 GuestDnD::toFormatVector(m_strFormats, pResp->format(), aFormats);
274 GuestDnD::toMainActions(pResp->allActions(), aAllowedActions);
275 }
276 }
277
278 if (aDefaultAction)
279 *aDefaultAction = defaultAction;
280 }
281
282 if (RT_FAILURE(rc))
283 hr = setError(VBOX_E_IPRT_ERROR,
284 tr("Error retrieving drag'n drop pending status (%Rrc)\n"), rc);
285
286 LogFlowFunc(("hr=%Rhrc, defaultAction=0x%x\n", hr, defaultAction));
287 return hr;
288#endif /* VBOX_WITH_DRAG_AND_DROP */
289}
290
291HRESULT GuestDnDSource::drop(const com::Utf8Str &aFormat,
292 DnDAction_T aAction, ComPtr<IProgress> &aProgress)
293{
294#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
295 ReturnComNotImplemented();
296#else /* VBOX_WITH_DRAG_AND_DROP */
297
298 /* Input validation. */
299 if (RT_UNLIKELY((aFormat.c_str()) == NULL || *(aFormat.c_str()) == '\0'))
300 return setError(E_INVALIDARG, tr("No drop format specified"));
301
302 AutoCaller autoCaller(this);
303 if (FAILED(autoCaller.rc())) return autoCaller.rc();
304
305 uint32_t uAction = GuestDnD::toHGCMAction(aAction);
306 /* If there is no usable action, ignore this request. */
307 if (isDnDIgnoreAction(uAction))
308 return S_OK;
309
310 if (ASMAtomicReadBool(&mData.mfDropIsPending))
311 return setError(E_INVALIDARG, tr("Another drop operation already is in progress"));
312
313 ASMAtomicWriteBool(&mData.mfDropIsPending, true);
314
315 HRESULT hr = S_OK;
316
317 /* Note: At the moment we only support one response at a time. */
318 GuestDnDResponse *pResp = GuestDnDInst()->response();
319 if (pResp)
320 {
321 pResp->resetProgress(m_pGuest);
322
323 int rc;
324
325 try
326 {
327 mData.mRecvCtx.mpSource = this;
328 mData.mRecvCtx.mpResp = pResp;
329 mData.mRecvCtx.mFormat = aFormat;
330
331 RecvDataTask *pTask = new RecvDataTask(this, &mData.mRecvCtx);
332 AssertReturn(pTask->isOk(), pTask->getRC());
333
334 rc = RTThreadCreate(NULL, GuestDnDSource::i_receiveDataThread,
335 (void *)pTask, 0, RTTHREADTYPE_MAIN_WORKER, 0, "dndSrcRcvData");
336 if (RT_SUCCESS(rc))
337 {
338 hr = pResp->queryProgressTo(aProgress.asOutParam());
339 ComAssertComRC(hr);
340
341 /* Note: pTask is now owned by the worker thread. */
342 }
343 }
344 catch(std::bad_alloc &)
345 {
346 rc = VERR_NO_MEMORY;
347 }
348
349 /*if (RT_FAILURE(vrc)) @todo SetError(...) */
350 }
351 /** @todo SetError(...) */
352
353 ASMAtomicWriteBool(&mData.mfDropIsPending, false);
354
355 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
356 return hr;
357#endif /* VBOX_WITH_DRAG_AND_DROP */
358}
359
360HRESULT GuestDnDSource::receiveData(std::vector<BYTE> &aData)
361{
362#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
363 ReturnComNotImplemented();
364#else /* VBOX_WITH_DRAG_AND_DROP */
365
366 /* Input validation. */
367
368 AutoCaller autoCaller(this);
369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
370
371 if (ASMAtomicReadBool(&mData.mfDropIsPending))
372 return setError(E_INVALIDARG, tr("Current drop operation still running"));
373
374 PRECVDATACTX pCtx = &mData.mRecvCtx;
375
376 if (pCtx->mData.vecData.empty())
377 {
378 aData.resize(0);
379 return S_OK;
380 }
381
382 HRESULT hr = S_OK;
383 size_t cbData;
384
385 try
386 {
387 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length());
388 if (fHasURIList)
389 {
390 Utf8Str strURIs = pCtx->mURI.lstURI.RootToString(pCtx->mURI.strDropDir);
391 cbData = strURIs.length();
392
393 LogFlowFunc(("Found %zu root URIs (%zu bytes)\n", pCtx->mURI.lstURI.RootCount(), cbData));
394
395 aData.resize(cbData + 1 /* Include termination */);
396 memcpy(&aData.front(), strURIs.c_str(), cbData);
397 }
398 else
399 {
400 cbData = pCtx->mData.vecData.size();
401
402 /* Copy the data into a safe array of bytes. */
403 aData.resize(cbData);
404 memcpy(&aData.front(), &pCtx->mData.vecData[0], cbData);
405 }
406 }
407 catch (std::bad_alloc &)
408 {
409 hr = E_OUTOFMEMORY;
410 }
411
412 LogFlowFunc(("Returning cbData=%zu, hr=%Rhrc\n", cbData, hr));
413 return hr;
414#endif /* VBOX_WITH_DRAG_AND_DROP */
415}
416
417// implementation of internal methods.
418/////////////////////////////////////////////////////////////////////////////
419
420#ifdef VBOX_WITH_DRAG_AND_DROP_GH
421int GuestDnDSource::i_onReceiveData(PRECVDATACTX pCtx, const void *pvData, uint32_t cbData, uint64_t cbTotalSize)
422{
423 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
424 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
425 AssertReturn(cbData, VERR_INVALID_PARAMETER);
426 AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER);
427
428 LogFlowFunc(("cbData=%RU32, cbTotalSize=%RU64\n", cbData, cbTotalSize));
429
430 int rc = VINF_SUCCESS;
431
432 try
433 {
434 if ( cbData > cbTotalSize
435 || cbData > mData.mcbBlockSize)
436 {
437 LogFlowFunc(("Data sizes invalid: cbData=%RU32, cbTotalSize=%RU64\n", cbData, cbTotalSize));
438 rc = VERR_INVALID_PARAMETER;
439 }
440 else if (cbData < pCtx->mData.vecData.size())
441 {
442 AssertMsgFailed(("New size (%RU64) is smaller than current size (%zu)\n", cbTotalSize, pCtx->mData.vecData.size()));
443 rc = VERR_INVALID_PARAMETER;
444 }
445
446 if (RT_SUCCESS(rc))
447 {
448 pCtx->mData.vecData.insert(pCtx->mData.vecData.begin(), (BYTE *)pvData, (BYTE *)pvData + cbData);
449
450 LogFlowFunc(("vecDataSize=%zu, cbData=%RU32, cbTotalSize=%RU64\n", pCtx->mData.vecData.size(), cbData, cbTotalSize));
451
452 /* Data transfer complete? */
453 Assert(cbData <= pCtx->mData.vecData.size());
454 if (cbData == pCtx->mData.vecData.size())
455 {
456 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length());
457 LogFlowFunc(("fHasURIList=%RTbool, cbTotalSize=%RU32\n", fHasURIList, cbTotalSize));
458 if (fHasURIList)
459 {
460 /* Try parsing the data as URI list. */
461 rc = pCtx->mURI.lstURI.RootFromURIData(&pCtx->mData.vecData[0], pCtx->mData.vecData.size(), 0 /* uFlags */);
462 if (RT_SUCCESS(rc))
463 {
464 pCtx->mData.cbProcessed = 0;
465
466 /*
467 * Assign new total size which also includes all file data to receive
468 * from the guest.
469 */
470 pCtx->mData.cbToProcess = cbTotalSize;
471
472 LogFlowFunc(("URI data => cbToProcess=%RU64\n", pCtx->mData.cbToProcess));
473 }
474 }
475 }
476 }
477 }
478 catch (std::bad_alloc &)
479 {
480 rc = VERR_NO_MEMORY;
481 }
482
483 LogFlowFuncLeaveRC(rc);
484 return rc;
485}
486
487int GuestDnDSource::i_onReceiveDir(PRECVDATACTX pCtx, const char *pszPath, uint32_t cbPath, uint32_t fMode)
488{
489 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
490 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
491 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
492
493 LogFlowFunc(("pszPath=%s, cbPath=%RU32, fMode=0x%x\n", pszPath, cbPath, fMode));
494
495 int rc;
496 char *pszDir = RTPathJoinA(pCtx->mURI.strDropDir.c_str(), pszPath);
497 if (pszDir)
498 {
499 rc = RTDirCreateFullPath(pszDir, fMode);
500 if (RT_FAILURE(rc))
501 LogRel2(("DnD: Error creating guest directory \"%s\" on the host, rc=%Rrc\n", pszDir, rc));
502
503 RTStrFree(pszDir);
504 }
505 else
506 rc = VERR_NO_MEMORY;
507
508 if (RT_SUCCESS(rc))
509 {
510 if (mDataBase.mProtocolVersion <= 2)
511 {
512 /*
513 * BUG: Protocol v1 does *not* send root directory names in URI format,
514 * however, if this is a root URI directory (which came with the initial
515 * GUEST_DND_GH_SND_DATA message(s)) the total data announced was for
516 * root directory names which came in URI format, as an URI list.
517 *
518 * So construct an URI path locally to keep the accounting right.
519 */
520 char *pszPathURI = RTUriCreate("file" /* pszScheme */, "/" /* pszAuthority */,
521 pszPath /* pszPath */,
522 NULL /* pszQuery */, NULL /* pszFragment */);
523 if (pszPathURI)
524 {
525 bool fHasPath = RTPathHasPath(pszPath); /* Use original data received. */
526 if (!fHasPath) /* Root path? */
527 {
528 cbPath = strlen(pszPathURI);
529 cbPath += 3; /* Include "\r" + "\n" + termination -- see above. */
530
531 rc = i_updateProcess(pCtx, cbPath);
532 }
533
534 LogFlowFunc(("URI pszPathURI=%s, fHasPath=%RTbool, cbPath=%RU32\n", pszPathURI, fHasPath, cbPath));
535 RTStrFree(pszPathURI);
536 }
537 else
538 rc = VERR_NO_MEMORY;
539 }
540 }
541
542 LogFlowFuncLeaveRC(rc);
543 return rc;
544}
545
546int GuestDnDSource::i_onReceiveFileHdr(PRECVDATACTX pCtx, const char *pszPath, uint32_t cbPath,
547 uint64_t cbSize, uint32_t fMode, uint32_t fFlags)
548{
549 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
550 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
551 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
552 AssertReturn(fMode, VERR_INVALID_PARAMETER);
553 /* fFlags are optional. */
554
555 LogFlowFunc(("pszPath=%s, cbPath=%RU32, cbSize=%RU64, fMode=0x%x, fFlags=0x%x\n", pszPath, cbPath, cbSize, fMode, fFlags));
556
557 int rc = VINF_SUCCESS;
558
559 do
560 {
561 if (!pCtx->mURI.objURI.IsComplete())
562 {
563 LogFlowFunc(("Warning: Object \"%s\" not complete yet\n", pCtx->mURI.objURI.GetDestPath().c_str()));
564 rc = VERR_INVALID_PARAMETER;
565 break;
566 }
567
568 if (pCtx->mURI.objURI.IsOpen()) /* File already opened? */
569 {
570 LogFlowFunc(("Warning: Current opened object is \"%s\"\n", pCtx->mURI.objURI.GetDestPath().c_str()));
571 rc = VERR_WRONG_ORDER;
572 break;
573 }
574
575 char pszPathAbs[RTPATH_MAX];
576 rc = RTPathJoin(pszPathAbs, sizeof(pszPathAbs), pCtx->mURI.strDropDir.c_str(), pszPath);
577 if (RT_FAILURE(rc))
578 {
579 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
580 break;
581 }
582
583 rc = DnDPathSanitize(pszPathAbs, sizeof(pszPathAbs));
584 if (RT_FAILURE(rc))
585 {
586 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
587 break;
588 }
589
590 LogFunc(("Rebased to: %s\n", pszPathAbs));
591
592 /** @todo Add sparse file support based on fFlags? (Use Open(..., fFlags | SPARSE). */
593 /** @todo Add fMode to opening flags. */
594 rc = pCtx->mURI.objURI.OpenEx(pszPathAbs, DnDURIObject::File, DnDURIObject::Target,
595 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
596 if (RT_SUCCESS(rc))
597 {
598 /** @todo Unescpae path before printing. */
599 LogRel2(("DnD: Transferring file to host: %s\n", pCtx->mURI.objURI.GetDestPath().c_str()));
600
601 /* Note: Protocol v1 does not send any file sizes, so always 0. */
602 if (mDataBase.mProtocolVersion >= 2)
603 rc = pCtx->mURI.objURI.SetSize(cbSize);
604
605 if (!cbSize) /* 0-byte file? Close again. */
606 pCtx->mURI.objURI.Close();
607 }
608 else
609 {
610 LogRel2(("DnD: Error opening/creating guest file \"%s\" on host, rc=%Rrc\n",
611 pCtx->mURI.objURI.GetDestPath().c_str(), rc));
612 break;
613 }
614
615 } while (0);
616
617 LogFlowFuncLeaveRC(rc);
618 return rc;
619}
620
621int GuestDnDSource::i_onReceiveFileData(PRECVDATACTX pCtx, const void *pvData, uint32_t cbData)
622{
623 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
624 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
625 AssertReturn(cbData, VERR_INVALID_PARAMETER);
626
627 int rc = VINF_SUCCESS;
628
629 do
630 {
631 if (pCtx->mURI.objURI.IsComplete())
632 {
633 LogFlowFunc(("Warning: Object \"%s\" already completed\n", pCtx->mURI.objURI.GetDestPath().c_str()));
634 rc = VERR_WRONG_ORDER;
635 break;
636 }
637
638 if (!pCtx->mURI.objURI.IsOpen()) /* File opened on host? */
639 {
640 LogFlowFunc(("Warning: Object \"%s\" not opened\n", pCtx->mURI.objURI.GetDestPath().c_str()));
641 rc = VERR_WRONG_ORDER;
642 break;
643 }
644
645 uint32_t cbWritten;
646 rc = pCtx->mURI.objURI.Write(pvData, cbData, &cbWritten);
647 if (RT_SUCCESS(rc))
648 {
649 Assert(cbWritten <= cbData);
650 if (cbWritten < cbData)
651 {
652 /** @todo What to do when the host's disk is full? */
653 rc = VERR_DISK_FULL;
654 }
655
656 if (RT_SUCCESS(rc))
657 rc = i_updateProcess(pCtx, cbWritten);
658 }
659
660 if (RT_SUCCESS(rc))
661 {
662 if (pCtx->mURI.objURI.IsComplete())
663 {
664 /** @todo Sanitize path. */
665 LogRel2(("DnD: File transfer to host complete: %s\n", pCtx->mURI.objURI.GetDestPath().c_str()));
666 rc = VINF_EOF;
667
668 /* Prepare URI object for next use. */
669 pCtx->mURI.objURI.Reset();
670 }
671 }
672 else
673 LogRel(("DnD: Error: Can't write guest file to host to \"%s\": %Rrc\n", pCtx->mURI.objURI.GetDestPath().c_str(), rc));
674
675 } while (0);
676
677 LogFlowFuncLeaveRC(rc);
678 return rc;
679}
680#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
681
682int GuestDnDSource::i_receiveData(PRECVDATACTX pCtx)
683{
684 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
685
686 GuestDnD *pInst = GuestDnDInst();
687 if (!pInst)
688 return VERR_INVALID_POINTER;
689
690 GuestDnDResponse *pResp = pCtx->mpResp;
691 AssertPtr(pCtx->mpResp);
692
693 ASMAtomicWriteBool(&pCtx->mIsActive, true);
694
695 int rc = pCtx->mCallback.Reset();
696 if (RT_FAILURE(rc))
697 return rc;
698
699 pCtx->mData.vecData.clear();
700 pCtx->mData.cbToProcess = 0;
701 pCtx->mData.cbProcessed = 0;
702
703 do
704 {
705 /* Reset any old data. */
706 pResp->reset();
707 pResp->resetProgress(m_pGuest);
708
709 /* Set the format we are going to retrieve to have it around
710 * when retrieving the data later. */
711 pResp->setFormat(pCtx->mFormat);
712
713 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length());
714 LogFlowFunc(("strFormat=%s, uAction=0x%x, fHasURIList=%RTbool\n", pCtx->mFormat.c_str(), pCtx->mAction, fHasURIList));
715
716 if (fHasURIList)
717 {
718 rc = i_receiveURIData(pCtx);
719 }
720 else
721 {
722 rc = i_receiveRawData(pCtx);
723 }
724
725 } while (0);
726
727 ASMAtomicWriteBool(&pCtx->mIsActive, false);
728
729 LogFlowFuncLeaveRC(rc);
730 return rc;
731}
732
733/* static */
734DECLCALLBACK(int) GuestDnDSource::i_receiveDataThread(RTTHREAD Thread, void *pvUser)
735{
736 LogFlowFunc(("pvUser=%p\n", pvUser));
737
738 RecvDataTask *pTask = (RecvDataTask *)pvUser;
739 AssertPtrReturn(pTask, VERR_INVALID_POINTER);
740
741 const ComObjPtr<GuestDnDSource> pSource(pTask->getSource());
742 Assert(!pSource.isNull());
743
744 int rc;
745
746 AutoCaller autoCaller(pSource);
747 if (SUCCEEDED(autoCaller.rc()))
748 {
749 rc = pSource->i_receiveData(pTask->getCtx());
750 }
751 else
752 rc = VERR_COM_INVALID_OBJECT_STATE;
753
754 LogFlowFunc(("pSource=%p returning rc=%Rrc\n", (GuestDnDSource *)pSource, rc));
755
756 if (pTask)
757 delete pTask;
758 return rc;
759}
760
761int GuestDnDSource::i_receiveRawData(PRECVDATACTX pCtx)
762{
763 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
764
765 int rc;
766
767 GuestDnDResponse *pResp = pCtx->mpResp;
768 AssertPtr(pCtx->mpResp);
769
770 GuestDnD *pInst = GuestDnDInst();
771 if (!pInst)
772 return VERR_INVALID_POINTER;
773
774#define REGISTER_CALLBACK(x) \
775 rc = pResp->setCallback(x, i_receiveRawDataCallback, pCtx); \
776 if (RT_FAILURE(rc)) \
777 return rc;
778
779#define UNREGISTER_CALLBACK(x) \
780 rc = pCtx->mpResp->setCallback(x, NULL); \
781 AssertRC(rc);
782
783 /*
784 * Register callbacks.
785 */
786 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
787 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
788
789 do
790 {
791 /*
792 * Receive the raw data.
793 */
794 GuestDnDMsg Msg;
795 Msg.setType(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED);
796 Msg.setNextPointer((void*)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1);
797 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1);
798 Msg.setNextUInt32(pCtx->mAction);
799
800 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
801 * the host and therefore now waiting for the actual raw data. */
802 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
803 if (RT_SUCCESS(rc))
804 {
805 /*
806 * Wait until our callback i_receiveRawDataCallback triggered the
807 * wait event.
808 */
809 LogFlowFunc(("Waiting for raw data callback ...\n"));
810 rc = pCtx->mCallback.Wait(RT_INDEFINITE_WAIT);
811 LogFlowFunc(("Raw callback done, rc=%Rrc\n", rc));
812 }
813
814 } while (0);
815
816 /*
817 * Unregister callbacks.
818 */
819 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
820 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
821
822#undef REGISTER_CALLBACK
823#undef UNREGISTER_CALLBACK
824
825 LogFlowFuncLeaveRC(rc);
826 return rc;
827}
828
829int GuestDnDSource::i_receiveURIData(PRECVDATACTX pCtx)
830{
831 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
832
833 int rc;
834
835 GuestDnDResponse *pResp = pCtx->mpResp;
836 AssertPtr(pCtx->mpResp);
837
838 GuestDnD *pInst = GuestDnDInst();
839 if (!pInst)
840 return VERR_INVALID_POINTER;
841
842#define REGISTER_CALLBACK(x) \
843 rc = pResp->setCallback(x, i_receiveURIDataCallback, pCtx); \
844 if (RT_FAILURE(rc)) \
845 return rc;
846
847#define UNREGISTER_CALLBACK(x) \
848 rc = pResp->setCallback(x, NULL); \
849 AssertRC(rc);
850
851 /*
852 * Register callbacks.
853 */
854 /* Guest callbacks. */
855 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
856 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
857 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DIR);
858 if (mDataBase.mProtocolVersion >= 2)
859 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR);
860 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA);
861
862 do
863 {
864 char szDropDir[RTPATH_MAX];
865 rc = DnDDirCreateDroppedFiles(szDropDir, sizeof(szDropDir));
866 LogFlowFunc(("rc=%Rrc, szDropDir=%s\n", rc, szDropDir));
867 if (RT_FAILURE(rc))
868 break;
869
870 pCtx->mURI.strDropDir = szDropDir; /** @todo Keep directory handle open? */
871
872 /*
873 * Receive the URI list.
874 */
875 GuestDnDMsg Msg;
876 Msg.setType(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED);
877 Msg.setNextPointer((void*)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1);
878 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1);
879 Msg.setNextUInt32(pCtx->mAction);
880
881 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
882 * the host and therefore now waiting for the actual URI actual data. */
883 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
884 if (RT_SUCCESS(rc))
885 {
886 /*
887 * Wait until our callback i_receiveURIDataCallback triggered the
888 * wait event.
889 */
890 LogFlowFunc(("Waiting for URI callback ...\n"));
891 rc = pCtx->mCallback.Wait(RT_INDEFINITE_WAIT);
892 LogFlowFunc(("URI callback done, rc=%Rrc\n", rc));
893 }
894
895 } while (0);
896
897 /*
898 * Unregister callbacks.
899 */
900 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
901 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
902 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DIR);
903 if (mDataBase.mProtocolVersion >= 2)
904 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR);
905 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA);
906
907#undef REGISTER_CALLBACK
908#undef UNREGISTER_CALLBACK
909
910 if (RT_FAILURE(rc))
911 {
912 LogFlowFunc(("Rolling back ...\n"));
913
914 /* Rollback by removing any stuff created. */
915 for (size_t i = 0; i < pCtx->mURI.lstFiles.size(); ++i)
916 RTFileDelete(pCtx->mURI.lstFiles.at(i).c_str());
917 for (size_t i = 0; i < pCtx->mURI.lstDirs.size(); ++i)
918 RTDirRemove(pCtx->mURI.lstDirs.at(i).c_str());
919 }
920
921 /* Try removing (hopefully) empty drop directory in any case. */
922 if (pCtx->mURI.strDropDir.isNotEmpty())
923 RTDirRemove(pCtx->mURI.strDropDir.c_str());
924
925 LogFlowFuncLeaveRC(rc);
926 return rc;
927}
928
929/* static */
930DECLCALLBACK(int) GuestDnDSource::i_receiveRawDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
931{
932 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser;
933 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
934
935 GuestDnDSource *pThis = pCtx->mpSource;
936 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
937
938 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
939
940 int rc = VINF_SUCCESS;
941 bool fNotify = false;
942
943 switch (uMsg)
944 {
945#ifdef VBOX_WITH_DRAG_AND_DROP_GH
946 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
947 {
948 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
949 AssertPtr(pCBData);
950 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
951 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
952
953 rc = pThis->i_onReceiveData(pCtx, pCBData->pvData, pCBData->cbData, pCBData->cbTotalSize);
954 break;
955 }
956 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
957 {
958 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
959 AssertPtr(pCBData);
960 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
961 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
962
963 pCtx->mpResp->reset();
964 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
965 if (RT_SUCCESS(rc))
966 rc = pCBData->rc;
967 break;
968 }
969#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
970 default:
971 rc = VERR_NOT_SUPPORTED;
972 break;
973 }
974
975 if (RT_FAILURE(rc))
976 fNotify = true;
977
978 if (fNotify)
979 {
980 int rc2 = pCtx->mCallback.Notify(rc);
981 AssertRC(rc2);
982 }
983
984 LogFlowFuncLeaveRC(rc);
985 return rc; /* Tell the guest. */
986}
987
988/* static */
989DECLCALLBACK(int) GuestDnDSource::i_receiveURIDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
990{
991 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser;
992 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
993
994 GuestDnDSource *pThis = pCtx->mpSource;
995 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
996
997 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
998
999 int rc = VINF_SUCCESS;
1000 bool fNotify = false;
1001
1002 switch (uMsg)
1003 {
1004#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1005 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
1006 {
1007 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
1008 AssertPtr(pCBData);
1009 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1010 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1011
1012 rc = pThis->i_onReceiveData(pCtx, pCBData->pvData, pCBData->cbData, pCBData->cbTotalSize);
1013 break;
1014 }
1015 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
1016 {
1017 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);
1018 AssertPtr(pCBData);
1019 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
1020 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1021
1022 rc = pThis->i_onReceiveDir(pCtx, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
1023 break;
1024 }
1025 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR:
1026 {
1027 DragAndDropSvc::PVBOXDNDCBSNDFILEHDRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEHDRDATA>(pvParms);
1028 AssertPtr(pCBData);
1029 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEHDRDATA) == cbParms, VERR_INVALID_PARAMETER);
1030 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_HDR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1031
1032 rc = pThis->i_onReceiveFileHdr(pCtx, pCBData->pszFilePath, pCBData->cbFilePath,
1033 pCBData->cbSize, pCBData->fMode, pCBData->fFlags);
1034 break;
1035 }
1036 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA:
1037 {
1038 DragAndDropSvc::PVBOXDNDCBSNDFILEDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATADATA>(pvParms);
1039 AssertPtr(pCBData);
1040 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1041 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1042
1043 if (pThis->mDataBase.mProtocolVersion <= 1)
1044 {
1045 /**
1046 * Notes for protocol v1 (< VBox 5.0):
1047 * - Every time this command is being sent it includes the file header,
1048 * so just process both calls here.
1049 * - There was no information whatsoever about the total file size; the old code only
1050 * appended data to the desired file. So just pass 0 as cbSize.
1051 */
1052 rc = pThis->i_onReceiveFileHdr(pCtx,
1053 pCBData->u.v1.pszFilePath, pCBData->u.v1.cbFilePath,
1054 0 /* cbSize */, pCBData->u.v1.fMode, 0 /* fFlags */);
1055 if (RT_SUCCESS(rc))
1056 rc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData);
1057 }
1058 else /* Protocol v2 and up. */
1059 rc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData);
1060 break;
1061 }
1062 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
1063 {
1064 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
1065 AssertPtr(pCBData);
1066 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1067 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1068
1069 pCtx->mpResp->reset();
1070 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
1071 if (RT_SUCCESS(rc))
1072 rc = pCBData->rc;
1073 break;
1074 }
1075#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1076 default:
1077 rc = VERR_NOT_SUPPORTED;
1078 break;
1079 }
1080
1081 if (RT_FAILURE(rc))
1082 fNotify = true;
1083
1084 /* All URI data processed? */
1085 if (pCtx->mData.cbProcessed >= pCtx->mData.cbToProcess)
1086 {
1087 Assert(pCtx->mData.cbProcessed == pCtx->mData.cbToProcess);
1088 fNotify = true;
1089 }
1090
1091 LogFlowFunc(("cbProcessed=%RU64, cbToProcess=%RU64, fNotify=%RTbool\n",
1092 pCtx->mData.cbProcessed, pCtx->mData.cbToProcess, fNotify));
1093
1094 if (fNotify)
1095 {
1096 int rc2 = pCtx->mCallback.Notify(rc);
1097 AssertRC(rc2);
1098 }
1099
1100 LogFlowFuncLeaveRC(rc);
1101 return rc; /* Tell the guest. */
1102}
1103
1104int GuestDnDSource::i_updateProcess(PRECVDATACTX pCtx, uint32_t cbDataAdd)
1105{
1106 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1107
1108 pCtx->mData.cbProcessed += cbDataAdd;
1109 Assert(pCtx->mData.cbProcessed <= pCtx->mData.cbToProcess);
1110
1111 int64_t cbTotal = pCtx->mData.cbToProcess;
1112 uint8_t uPercent = pCtx->mData.cbProcessed * 100 / (cbTotal ? cbTotal : 1);
1113
1114 int rc = pCtx->mpResp->setProgress(uPercent,
1115 uPercent >= 100
1116 ? DragAndDropSvc::DND_PROGRESS_COMPLETE
1117 : DragAndDropSvc::DND_PROGRESS_RUNNING);
1118 return rc;
1119}
1120
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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