VirtualBox

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

Last change on this file since 55876 was 55823, checked in by vboxsync, 10 years ago

DnD: Main/GuestDnDSourceImpl.cpp: Don't route reported guest errors back to the guest, logging.

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

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