VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestFileImpl.cpp@ 45426

Last change on this file since 45426 was 45426, checked in by vboxsync, 12 years ago

GuestCtrl: Added EventSource getters + OnGuestFileRegistered event.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.3 KB
Line 
1
2/* $Id: GuestFileImpl.cpp 45426 2013-04-09 09:30:55Z vboxsync $ */
3/** @file
4 * VirtualBox Main - Guest file handling.
5 */
6
7/*
8 * Copyright (C) 2012-2013 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.215389.xyz. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#include "GuestErrorInfoImpl.h"
24#include "GuestFileImpl.h"
25#include "GuestSessionImpl.h"
26#include "GuestCtrlImplPrivate.h"
27#include "ConsoleImpl.h"
28
29#include "Global.h"
30#include "AutoCaller.h"
31#include "VBoxEvents.h"
32
33#include <iprt/cpp/utils.h> /* For unconst(). */
34#include <iprt/file.h>
35#include <VBox/com/array.h>
36
37#ifdef LOG_GROUP
38 #undef LOG_GROUP
39#endif
40#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
41#include <VBox/log.h>
42
43
44// constructor / destructor
45/////////////////////////////////////////////////////////////////////////////
46
47DEFINE_EMPTY_CTOR_DTOR(GuestFile)
48
49HRESULT GuestFile::FinalConstruct(void)
50{
51 LogFlowThisFunc(("\n"));
52 return BaseFinalConstruct();
53}
54
55void GuestFile::FinalRelease(void)
56{
57 LogFlowThisFuncEnter();
58 uninit();
59 BaseFinalRelease();
60 LogFlowThisFuncLeave();
61}
62
63// public initializer/uninitializer for internal purposes only
64/////////////////////////////////////////////////////////////////////////////
65
66/**
67 * Initializes a file object but does *not* open the file on the guest
68 * yet. This is done in the dedidcated openFile call.
69 *
70 * @return IPRT status code.
71 * @param pConsole Pointer to console object.
72 * @param pSession Pointer to session object.
73 * @param uFileID Host-based file ID (part of the context ID).
74 * @param openInfo File opening information.
75 */
76int GuestFile::init(Console *pConsole, GuestSession *pSession, ULONG uFileID, const GuestFileOpenInfo &openInfo)
77{
78 LogFlowThisFunc(("pConsole=%p, pSession=%p, uFileID=%RU32, strPath=%s\n",
79 pConsole, pSession, uFileID, openInfo.mFileName.c_str()));
80
81 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
82 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
83
84 /* Enclose the state transition NotReady->InInit->Ready. */
85 AutoInitSpan autoInitSpan(this);
86 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
87
88 int vrc = bindToSession(pConsole, pSession, uFileID /* Object ID */);
89 if (RT_SUCCESS(vrc))
90 {
91 mData.mID = 0;
92 mData.mInitialSize = 0;
93 mData.mStatus = FileStatus_Undefined;
94
95 unconst(mEventSource).createObject();
96 HRESULT hr = mEventSource->init(static_cast<IGuestFile*>(this));
97 if (FAILED(hr))
98 vrc = VERR_COM_UNEXPECTED;
99 }
100
101 if (RT_SUCCESS(vrc))
102 {
103 /* Confirm a successful initialization when it's the case. */
104 autoInitSpan.setSucceeded();
105 }
106 else
107 autoInitSpan.setFailed();
108
109 LogFlowFuncLeaveRC(vrc);
110 return vrc;
111}
112
113/**
114 * Uninitializes the instance.
115 * Called from FinalRelease().
116 */
117void GuestFile::uninit(void)
118{
119 LogFlowThisFunc(("\n"));
120
121 /* Enclose the state transition Ready->InUninit->NotReady. */
122 AutoUninitSpan autoUninitSpan(this);
123 if (autoUninitSpan.uninitDone())
124 return;
125
126#ifdef VBOX_WITH_GUEST_CONTROL
127 unconst(mEventSource).setNull();
128#endif
129
130 LogFlowThisFuncLeave();
131}
132
133// implementation of public getters/setters for attributes
134/////////////////////////////////////////////////////////////////////////////
135
136STDMETHODIMP GuestFile::COMGETTER(CreationMode)(ULONG *aCreationMode)
137{
138#ifndef VBOX_WITH_GUEST_CONTROL
139 ReturnComNotImplemented();
140#else
141 AutoCaller autoCaller(this);
142 if (FAILED(autoCaller.rc())) return autoCaller.rc();
143
144 CheckComArgOutPointerValid(aCreationMode);
145
146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
147
148 *aCreationMode = mData.mOpenInfo.mCreationMode;
149
150 return S_OK;
151#endif /* VBOX_WITH_GUEST_CONTROL */
152}
153
154/** @todo For 4.3: Change ULONG* to BSTR* ?*/
155STDMETHODIMP GuestFile::COMGETTER(Disposition)(ULONG *aDisposition)
156{
157#ifndef VBOX_WITH_GUEST_CONTROL
158 ReturnComNotImplemented();
159#else
160 AutoCaller autoCaller(this);
161 if (FAILED(autoCaller.rc())) return autoCaller.rc();
162
163 CheckComArgOutPointerValid(aDisposition);
164
165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
166
167 *aDisposition = getDispositionFromString(mData.mOpenInfo.mDisposition);
168
169 return S_OK;
170#endif /* VBOX_WITH_GUEST_CONTROL */
171}
172
173STDMETHODIMP GuestFile::COMGETTER(EventSource)(IEventSource ** aEventSource)
174{
175 CheckComArgOutPointerValid(aEventSource);
176
177 AutoCaller autoCaller(this);
178 if (FAILED(autoCaller.rc())) return autoCaller.rc();
179
180 // no need to lock - lifetime constant
181 mEventSource.queryInterfaceTo(aEventSource);
182
183 return S_OK;
184}
185
186STDMETHODIMP GuestFile::COMGETTER(FileName)(BSTR *aFileName)
187{
188#ifndef VBOX_WITH_GUEST_CONTROL
189 ReturnComNotImplemented();
190#else
191 AutoCaller autoCaller(this);
192 if (FAILED(autoCaller.rc())) return autoCaller.rc();
193
194 CheckComArgOutPointerValid(aFileName);
195
196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
197
198 mData.mOpenInfo.mFileName.cloneTo(aFileName);
199
200 return S_OK;
201#endif /* VBOX_WITH_GUEST_CONTROL */
202}
203
204STDMETHODIMP GuestFile::COMGETTER(InitialSize)(LONG64 *aInitialSize)
205{
206#ifndef VBOX_WITH_GUEST_CONTROL
207 ReturnComNotImplemented();
208#else
209 AutoCaller autoCaller(this);
210 if (FAILED(autoCaller.rc())) return autoCaller.rc();
211
212 CheckComArgOutPointerValid(aInitialSize);
213
214 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
215
216 *aInitialSize = mData.mInitialSize;
217
218 return S_OK;
219#endif /* VBOX_WITH_GUEST_CONTROL */
220}
221
222STDMETHODIMP GuestFile::COMGETTER(Offset)(LONG64 *aOffset)
223{
224#ifndef VBOX_WITH_GUEST_CONTROL
225 ReturnComNotImplemented();
226#else
227 AutoCaller autoCaller(this);
228 if (FAILED(autoCaller.rc())) return autoCaller.rc();
229
230 CheckComArgOutPointerValid(aOffset);
231
232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
233
234 *aOffset = mData.mOffCurrent;
235
236 return S_OK;
237#endif /* VBOX_WITH_GUEST_CONTROL */
238}
239
240/** @todo For 4.3: Change ULONG* to BSTR* ?*/
241STDMETHODIMP GuestFile::COMGETTER(OpenMode)(ULONG *aOpenMode)
242{
243#ifndef VBOX_WITH_GUEST_CONTROL
244 ReturnComNotImplemented();
245#else
246 AutoCaller autoCaller(this);
247 if (FAILED(autoCaller.rc())) return autoCaller.rc();
248
249 CheckComArgOutPointerValid(aOpenMode);
250
251 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
252
253 *aOpenMode = getOpenModeFromString(mData.mOpenInfo.mOpenMode);
254
255 return S_OK;
256#endif /* VBOX_WITH_GUEST_CONTROL */
257}
258
259STDMETHODIMP GuestFile::COMGETTER(Status)(FileStatus_T *aStatus)
260{
261#ifndef VBOX_WITH_GUEST_CONTROL
262 ReturnComNotImplemented();
263#else
264 LogFlowThisFuncEnter();
265
266 AutoCaller autoCaller(this);
267 if (FAILED(autoCaller.rc())) return autoCaller.rc();
268
269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
270
271 *aStatus = mData.mStatus;
272
273 return S_OK;
274#endif /* VBOX_WITH_GUEST_CONTROL */
275}
276
277// private methods
278/////////////////////////////////////////////////////////////////////////////
279
280int GuestFile::callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
281{
282#ifdef DEBUG
283 LogFlowThisFunc(("strName=%s, uContextID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
284 mData.mOpenInfo.mFileName.c_str(), pCbCtx->uContextID, pCbCtx->uFunction, pSvcCb));
285#endif
286 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
287
288 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
289
290 int vrc;
291 switch (pCbCtx->uFunction)
292 {
293 case GUEST_DISCONNECTED:
294 vrc = onGuestDisconnected(pCbCtx, pSvcCb);
295 break;
296
297 case GUEST_FILE_NOTIFY:
298 vrc = onFileNotify(pCbCtx, pSvcCb);
299 break;
300
301 default:
302 /* Silently ignore not implemented functions. */
303 vrc = VERR_NOT_SUPPORTED;
304 break;
305 }
306
307#ifdef DEBUG
308 LogFlowFuncLeaveRC(vrc);
309#endif
310 return vrc;
311}
312
313int GuestFile::closeFile(int *pGuestRc)
314{
315 LogFlowThisFunc(("strFile=%s\n", mData.mOpenInfo.mFileName.c_str()));
316
317 uint32_t uContextID;
318 int vrc = generateContextID(mSession->getId(), mObjectID,
319 &uContextID);
320 if (RT_SUCCESS(vrc))
321 {
322 /* Prepare HGCM call. */
323 VBOXHGCMSVCPARM paParms[4];
324 int i = 0;
325 paParms[i++].setUInt32(mData.mID /* Guest file ID */);
326
327 vrc = sendCommand(HOST_FILE_CLOSE, i, paParms);
328 if (RT_SUCCESS(vrc))
329 vrc = waitForStatusChange(30 * 1000 /* Timeout in ms */,
330 NULL /* FileStatus */);
331 }
332
333 LogFlowFuncLeaveRC(vrc);
334 return vrc;
335}
336
337/* static */
338uint32_t GuestFile::getDispositionFromString(const Utf8Str &strDisposition)
339{
340 return 0; /** @todo Implement me! */
341}
342
343/* static */
344uint32_t GuestFile::getOpenModeFromString(const Utf8Str &strOpenMode)
345{
346 uint32_t uOpenMode = 0;
347
348 const char *pc = strOpenMode.c_str();
349 while (*pc != '\0')
350 {
351 switch (*pc++)
352 {
353 case 'r':
354 uOpenMode |= RTFILE_O_READ;
355 break;
356
357 case 'w':
358 uOpenMode |= RTFILE_O_WRITE;
359 break;
360
361 default:
362 /* Silently skip unknown values. */
363 break;
364 }
365 }
366
367 return uOpenMode;
368}
369
370/* static */
371Utf8Str GuestFile::guestErrorToString(int guestRc)
372{
373 Utf8Str strError;
374
375 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
376 switch (guestRc)
377 {
378 case VERR_INVALID_VM_HANDLE:
379 strError += Utf8StrFmt(tr("VMM device is not available (is the VM running?)"));
380 break;
381
382 case VERR_HGCM_SERVICE_NOT_FOUND:
383 strError += Utf8StrFmt(tr("The guest execution service is not available"));
384 break;
385
386 case VERR_TIMEOUT:
387 strError += Utf8StrFmt(tr("The guest did not respond within time"));
388 break;
389
390 case VERR_CANCELLED:
391 strError += Utf8StrFmt(tr("The session operation was canceled"));
392 break;
393
394 case VERR_MAX_PROCS_REACHED:
395 strError += Utf8StrFmt(tr("Maximum number of concurrent guest files has been reached"));
396 break;
397
398 case VERR_NOT_EQUAL: /** @todo Imprecise to the user; can mean anything and all. */
399 strError += Utf8StrFmt(tr("Unable to retrieve requested information"));
400 break;
401
402 case VERR_NOT_FOUND:
403 strError += Utf8StrFmt(tr("The guest execution service is not ready (yet)"));
404 break;
405
406 default:
407 strError += Utf8StrFmt("%Rrc", guestRc);
408 break;
409 }
410
411 return strError;
412}
413
414int GuestFile::onFileNotify(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
415{
416 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
417 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
418
419 if (pSvcCbData->mParms < 3)
420 return VERR_INVALID_PARAMETER;
421
422 int vrc = VINF_SUCCESS;
423
424 int idx = 0; /* Current parameter index. */
425 CALLBACKDATA_FILE_NOTIFY dataCb;
426 /* pSvcCb->mpaParms[0] always contains the context ID. */
427 pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.uType);
428 pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.rc);
429
430 int guestRc = (int)dataCb.rc; /* uint32_t vs. int. */
431
432 switch (dataCb.uType)
433 {
434 case GUEST_FILE_NOTIFYTYPE_ERROR:
435 {
436 AssertMsg(mData.mStatus != FileStatus_Error, ("File status already set to error\n"));
437
438 int rc2 = setFileStatus(FileStatus_Error, guestRc);
439 AssertRC(rc2);
440 break;
441 }
442
443 case GUEST_FILE_NOTIFYTYPE_OPEN:
444 {
445 if (pSvcCbData->mParms == 4)
446 {
447 pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.u.open.uHandle);
448
449 AssertMsg(mData.mID == 0, ("File ID already set to %RU32\n", mData.mID));
450 mData.mID = dataCb.u.open.uHandle;
451 AssertMsg(mData.mID == VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCbCtx->uContextID),
452 ("File ID %RU32 does not match context ID %RU32\n", mData.mID,
453 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCbCtx->uContextID)));
454
455 /* Set the process status. */
456 int rc2 = setFileStatus(FileStatus_Open, guestRc);
457 if (RT_SUCCESS(vrc))
458 vrc = rc2;
459 }
460 else
461 vrc = VERR_NOT_SUPPORTED;
462
463 break;
464 }
465
466 case GUEST_FILE_NOTIFYTYPE_CLOSE:
467 {
468 int rc2 = setFileStatus(FileStatus_Closed, guestRc);
469 AssertRC(rc2);
470
471 break;
472 }
473
474 case GUEST_FILE_NOTIFYTYPE_READ:
475 if (pSvcCbData->mParms == 4)
476 {
477 pSvcCbData->mpaParms[idx++].getPointer(&dataCb.u.read.pvData,
478 &dataCb.u.read.cbData);
479 uint32_t cbRead = dataCb.u.read.cbData;
480 if (cbRead)
481 {
482 mData.mOffCurrent += cbRead;
483
484 com::SafeArray<BYTE> data((size_t)cbRead);
485 data.initFrom((BYTE*)dataCb.u.read.pvData, cbRead);
486 fireGuestFileReadEvent(mEventSource, mSession, this, mData.mOffCurrent,
487 cbRead, ComSafeArrayAsInParam(data));
488 }
489 }
490 else
491 vrc = VERR_NOT_SUPPORTED;
492 break;
493
494 case GUEST_FILE_NOTIFYTYPE_WRITE:
495 if (pSvcCbData->mParms == 4)
496 {
497 pSvcCbData->mpaParms[idx++].getUInt32(&dataCb.u.write.cbWritten);
498
499 mData.mOffCurrent += dataCb.u.write.cbWritten;
500
501 if (dataCb.u.write.cbWritten)
502 fireGuestFileWriteEvent(mEventSource, mSession, this, mData.mOffCurrent,
503 dataCb.u.write.cbWritten);
504 }
505 else
506 vrc = VERR_NOT_SUPPORTED;
507 break;
508
509 case GUEST_FILE_NOTIFYTYPE_SEEK:
510 if (pSvcCbData->mParms == 4)
511 {
512 pSvcCbData->mpaParms[idx++].getUInt64(&dataCb.u.seek.uOffActual);
513
514 mData.mOffCurrent = dataCb.u.seek.uOffActual;
515
516 if (dataCb.u.seek.uOffActual)
517 fireGuestFileOffsetChangedEvent(mEventSource, mSession, this,
518 mData.mOffCurrent, 0 /* Processed */);
519 }
520 else
521 vrc = VERR_NOT_SUPPORTED;
522 break;
523
524 case GUEST_FILE_NOTIFYTYPE_TELL:
525 if (pSvcCbData->mParms == 4)
526 {
527 pSvcCbData->mpaParms[idx++].getUInt64(&dataCb.u.tell.uOffActual);
528
529 if (mData.mOffCurrent != dataCb.u.tell.uOffActual)
530 {
531 mData.mOffCurrent = dataCb.u.tell.uOffActual;
532
533 fireGuestFileOffsetChangedEvent(mEventSource, mSession, this,
534 mData.mOffCurrent, 0 /* Processed */);
535 }
536 }
537 else
538 vrc = VERR_NOT_SUPPORTED;
539 break;
540
541 default:
542 vrc = VERR_NOT_SUPPORTED;
543 break;
544 }
545
546 LogFlowThisFunc(("strName=%s, uType=%RU32, guestRc=%Rrc\n",
547 mData.mOpenInfo.mFileName.c_str(), dataCb.uType, dataCb.rc));
548
549 if (RT_SUCCESS(vrc))
550 {
551 /* Nothing to do here yet. */
552 }
553 else if (vrc == VERR_NOT_SUPPORTED)
554 {
555 /* Also let the callback know. */
556 guestRc = VERR_NOT_SUPPORTED;
557 }
558
559 LogFlowFuncLeaveRC(vrc);
560 return vrc;
561}
562
563int GuestFile::onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
564{
565 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
566 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
567
568 LogFlowThisFunc(("strFile=%s\n",
569 mData.mOpenInfo.mFileName.c_str()));
570
571 int vrc = setFileStatus(FileStatus_Down, VINF_SUCCESS);
572
573 LogFlowFuncLeaveRC(vrc);
574 return vrc;
575}
576
577int GuestFile::openFile(int *pGuestRc)
578{
579 LogFlowThisFunc(("strFile=%s, strOpenMode=%s, strDisposition=%s, uCreationMode=%RU32\n",
580 mData.mOpenInfo.mFileName.c_str(), mData.mOpenInfo.mOpenMode.c_str(),
581 mData.mOpenInfo.mDisposition.c_str(), mData.mOpenInfo.mCreationMode));
582
583 /* Prepare HGCM call. */
584 VBOXHGCMSVCPARM paParms[8];
585 int i = 0;
586 paParms[i++].setPointer((void*)mData.mOpenInfo.mFileName.c_str(),
587 (ULONG)mData.mOpenInfo.mFileName.length() + 1);
588 paParms[i++].setPointer((void*)mData.mOpenInfo.mOpenMode.c_str(),
589 (ULONG)mData.mOpenInfo.mOpenMode.length() + 1);
590 paParms[i++].setPointer((void*)mData.mOpenInfo.mDisposition.c_str(),
591 (ULONG)mData.mOpenInfo.mDisposition.length() + 1);
592 paParms[i++].setUInt32(mData.mOpenInfo.mCreationMode);
593 paParms[i++].setUInt64(mData.mOpenInfo.mInitialOffset);
594
595 int vrc = sendCommand(HOST_FILE_OPEN, i, paParms);
596 if (RT_SUCCESS(vrc))
597 vrc = waitForStatusChange(30 * 1000 /* Timeout in ms */,
598 NULL /* FileStatus */);
599
600 LogFlowFuncLeaveRC(vrc);
601 return vrc;
602}
603
604int GuestFile::readData(uint32_t uSize, uint32_t uTimeoutMS,
605 void* pvData, uint32_t cbData, uint32_t* pcbRead)
606{
607 LogFlowThisFunc(("uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
608 uSize, uTimeoutMS, pvData, cbData));
609
610 uint32_t uContextID;
611 int vrc = generateContextID(mSession->getId(), mObjectID,
612 &uContextID);
613 if (RT_SUCCESS(vrc))
614 {
615 /* Prepare HGCM call. */
616 VBOXHGCMSVCPARM paParms[4];
617 int i = 0;
618 paParms[i++].setUInt32(mData.mID /* File handle */);
619 paParms[i++].setUInt32(uSize /* Size (in bytes) to read */);
620
621 uint32_t cbRead;
622 vrc = sendCommand(HOST_FILE_READ, i, paParms);
623 if (RT_SUCCESS(vrc))
624 vrc = waitForRead(uTimeoutMS, pvData, cbData, &cbRead);
625
626 if (RT_SUCCESS(vrc))
627 {
628 LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
629
630 if (pcbRead)
631 *pcbRead = cbRead;
632 }
633 }
634
635 LogFlowFuncLeaveRC(vrc);
636 return vrc;
637}
638
639int GuestFile::readDataAt(uint64_t uOffset, uint32_t uSize, uint32_t uTimeoutMS,
640 void* pvData, size_t cbData, size_t* pcbRead)
641{
642 LogFlowThisFunc(("uOffset=%RU64, uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
643 uOffset, uSize, uTimeoutMS, pvData, cbData));
644
645 uint32_t uContextID;
646 int vrc = generateContextID(mSession->getId(), mObjectID,
647 &uContextID);
648 if (RT_SUCCESS(vrc))
649 {
650
651 /* Prepare HGCM call. */
652 VBOXHGCMSVCPARM paParms[4];
653 int i = 0;
654 paParms[i++].setUInt32(mData.mID /* File handle */);
655 paParms[i++].setUInt64(uOffset /* Offset (in bytes) to start reading */);
656 paParms[i++].setUInt32(uSize /* Size (in bytes) to read */);
657
658 uint32_t cbRead;
659 vrc = sendCommand(HOST_FILE_READ_AT, i, paParms);
660 if (RT_SUCCESS(vrc))
661 vrc = waitForRead(uTimeoutMS, pvData, cbData, &cbRead);
662
663 if (RT_SUCCESS(vrc))
664 {
665 LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
666
667 if (pcbRead)
668 *pcbRead = cbRead;
669 }
670 }
671
672 LogFlowFuncLeaveRC(vrc);
673 return vrc;
674}
675
676int GuestFile::seekAt(uint64_t uOffset, GUEST_FILE_SEEKTYPE eSeekType,
677 uint32_t uTimeoutMS, uint64_t *puOffset)
678{
679 LogFlowThisFunc(("uOffset=%RU64, uTimeoutMS=%RU32\n",
680 uOffset, uTimeoutMS));
681
682 /* Prepare HGCM call. */
683 VBOXHGCMSVCPARM paParms[4];
684 int i = 0;
685 paParms[i++].setUInt32(mData.mID /* File handle */);
686 paParms[i++].setUInt32(eSeekType /* Seek method */);
687 paParms[i++].setUInt64(uOffset /* Offset (in bytes) to start reading */);
688
689 int vrc = sendCommand(HOST_FILE_SEEK, i, paParms);
690 if (RT_SUCCESS(vrc))
691 vrc = waitForOffsetChange(uTimeoutMS, puOffset);
692
693 LogFlowFuncLeaveRC(vrc);
694 return vrc;
695}
696
697/* static */
698HRESULT GuestFile::setErrorExternal(VirtualBoxBase *pInterface, int guestRc)
699{
700 AssertPtr(pInterface);
701 AssertMsg(RT_FAILURE(guestRc), ("Guest rc does not indicate a failure when setting error\n"));
702
703 return pInterface->setError(VBOX_E_IPRT_ERROR, GuestFile::guestErrorToString(guestRc).c_str());
704}
705
706/* Does not do locking; caller is responsible for that! */
707int GuestFile::setFileStatus(FileStatus_T fileStatus, int fileRc)
708{
709 LogFlowThisFunc(("oldStatus=%ld, newStatus=%ld, fileRc=%Rrc\n",
710 mData.mStatus, fileStatus, fileRc));
711
712#ifdef VBOX_STRICT
713 if (fileStatus == FileStatus_Error)
714 {
715 AssertMsg(RT_FAILURE(fileRc), ("Guest rc must be an error (%Rrc)\n", fileRc));
716 }
717 else
718 AssertMsg(RT_SUCCESS(fileRc), ("Guest rc must not be an error (%Rrc)\n", fileRc));
719#endif
720
721 if (mData.mStatus != fileStatus)
722 {
723 mData.mStatus = fileStatus;
724
725 ComObjPtr<GuestErrorInfo> errorInfo;
726 HRESULT hr = errorInfo.createObject();
727 ComAssertComRC(hr);
728 if (RT_FAILURE(fileRc))
729 {
730 int rc2 = errorInfo->init(fileRc, guestErrorToString(fileRc));
731 AssertRC(rc2);
732 }
733
734 fireGuestFileStateChangedEvent(mEventSource, mSession,
735 this, mData.mStatus, errorInfo);
736 }
737
738 return VINF_SUCCESS;
739}
740
741int GuestFile::waitForOffsetChange(uint32_t uTimeoutMS, uint64_t *puOffset)
742{
743 return VINF_SUCCESS;
744}
745
746int GuestFile::waitForRead(uint32_t uTimeoutMS, void* pvData, size_t cbData, uint32_t *pcbRead)
747{
748 return VINF_SUCCESS;
749}
750
751int GuestFile::waitForStatusChange(uint32_t uTimeoutMS, FileStatus_T *pFileStatus)
752{
753 int vrc;
754
755 /** @todo Parameter validation. */
756
757 ComPtr<IEventListener> pListener;
758 HRESULT hr = mEventSource->CreateListener(pListener.asOutParam());
759 if (SUCCEEDED(hr))
760 {
761 com::SafeArray <VBoxEventType_T> eventTypes(1);
762 eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
763 hr = mEventSource->RegisterListener(pListener, ComSafeArrayAsInParam(eventTypes), false);
764 }
765 else
766 vrc = VERR_COM_UNEXPECTED;
767
768 if (SUCCEEDED(hr))
769 {
770 LogFlowThisFunc(("Waiting for guest file status change event (timeout=%RU32ms) ...\n",
771 uTimeoutMS));
772
773 vrc = VINF_SUCCESS;
774
775 uint64_t u64Started = RTTimeMilliTS();
776 bool fSignalled = false;
777 do
778 {
779 unsigned cMsWait;
780 if (uTimeoutMS == RT_INDEFINITE_WAIT)
781 cMsWait = 1000;
782 else
783 {
784 uint64_t cMsElapsed = RTTimeMilliTS() - u64Started;
785 if (cMsElapsed >= uTimeoutMS)
786 break; /* timed out */
787 cMsWait = RT_MIN(1000, uTimeoutMS - (uint32_t)cMsElapsed);
788 }
789
790 ComPtr<IEvent> pEvent;
791 hr = mEventSource->GetEvent(pListener, cMsWait, pEvent.asOutParam());
792 if ( SUCCEEDED(hr)
793 && !pEvent.isNull())
794 {
795 VBoxEventType_T aType;
796 hr = pEvent->COMGETTER(Type)(&aType);
797 ComAssertComRC(hr);
798 switch (aType)
799 {
800 case VBoxEventType_OnGuestFileStateChanged:
801 {
802 ComPtr<IGuestFileStateChangedEvent> pChangedEvent = pEvent;
803 Assert(!pChangedEvent.isNull());
804
805 ComPtr<IGuestFile> pFile;
806 pChangedEvent->COMGETTER(File)(pFile.asOutParam());
807 Assert(!pFile.isNull());
808
809 if (pFile != this)
810 continue;
811 }
812
813 default:
814 AssertMsgFailed(("Unhandled event type %ld\n", aType));
815 break;
816 }
817
818 fSignalled = true;
819 break;
820 }
821
822 } while (!fSignalled);
823
824 if ( RT_SUCCESS(vrc)
825 && !fSignalled)
826 {
827 vrc = VERR_TIMEOUT;
828 }
829
830 mEventSource->UnregisterListener(pListener);
831 }
832 else
833 vrc = VERR_COM_UNEXPECTED;
834
835 LogFlowFuncLeaveRC(vrc);
836 return vrc;
837}
838
839int GuestFile::waitForWrite(uint32_t uTimeoutMS, uint32_t *pcbWritten)
840{
841 return VINF_SUCCESS;
842}
843
844int GuestFile::writeData(uint32_t uTimeoutMS, void *pvData, uint32_t cbData,
845 uint32_t *pcbWritten)
846{
847 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
848 AssertReturn(cbData, VERR_INVALID_PARAMETER);
849
850 LogFlowThisFunc(("uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
851 uTimeoutMS, pvData, cbData));
852
853 uint32_t uContextID;
854 int vrc = generateContextID(mSession->getId(), mObjectID,
855 &uContextID);
856 if (RT_SUCCESS(vrc))
857 {
858 /* Prepare HGCM call. */
859 VBOXHGCMSVCPARM paParms[8];
860 int i = 0;
861 paParms[i++].setUInt32(uContextID);
862 paParms[i++].setUInt32(mData.mID /* File handle */);
863 paParms[i++].setUInt32(cbData /* Size (in bytes) to write */);
864 paParms[i++].setPointer(pvData, cbData);
865
866 uint32_t cbWritten;
867 vrc = sendCommand(HOST_FILE_WRITE, i, paParms);
868 if (RT_SUCCESS(vrc))
869 vrc = waitForWrite(uTimeoutMS, &cbWritten);
870
871 if (RT_SUCCESS(vrc))
872 {
873 LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
874
875 if (cbWritten)
876 *pcbWritten = cbWritten;
877 }
878 }
879
880 LogFlowFuncLeaveRC(vrc);
881 return vrc;
882}
883
884int GuestFile::writeDataAt(uint64_t uOffset, uint32_t uTimeoutMS,
885 void *pvData, uint32_t cbData, uint32_t *pcbWritten)
886{
887 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
888 AssertReturn(cbData, VERR_INVALID_PARAMETER);
889
890 LogFlowThisFunc(("uOffset=%RU64, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
891 uOffset, uTimeoutMS, pvData, cbData));
892
893 uint32_t uContextID;
894 int vrc = generateContextID(mSession->getId(), mObjectID,
895 &uContextID);
896 if (RT_SUCCESS(vrc))
897 {
898 /* Prepare HGCM call. */
899 VBOXHGCMSVCPARM paParms[8];
900 int i = 0;
901 paParms[i++].setUInt32(mData.mID /* File handle */);
902 paParms[i++].setUInt64(uOffset /* Offset where to starting writing */);
903 paParms[i++].setUInt32(cbData /* Size (in bytes) to write */);
904 paParms[i++].setPointer(pvData, cbData);
905
906 vrc = sendCommand(HOST_FILE_WRITE_AT, i, paParms);
907 if (RT_SUCCESS(vrc))
908 {
909 uint32_t cbWritten;
910 vrc = sendCommand(HOST_FILE_WRITE, i, paParms);
911 if (RT_SUCCESS(vrc))
912 vrc = waitForWrite(uTimeoutMS, &cbWritten);
913
914 if (RT_SUCCESS(vrc))
915 {
916 LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
917
918 if (cbWritten)
919 *pcbWritten = cbWritten;
920 }
921 }
922 }
923
924 LogFlowFuncLeaveRC(vrc);
925 return vrc;
926}
927
928// implementation of public methods
929/////////////////////////////////////////////////////////////////////////////
930
931STDMETHODIMP GuestFile::Close(void)
932{
933#ifndef VBOX_WITH_GUEST_CONTROL
934 ReturnComNotImplemented();
935#else
936 LogFlowThisFuncEnter();
937
938 AutoCaller autoCaller(this);
939 if (FAILED(autoCaller.rc())) return autoCaller.rc();
940
941 /* Close file on guest. */
942 int guestRc;
943 int rc = closeFile(&guestRc);
944 /* On failure don't return here, instead do all the cleanup
945 * work first and then return an error. */
946
947 AssertPtr(mSession);
948 int rc2 = mSession->fileRemoveFromList(this);
949 if (RT_SUCCESS(rc))
950 rc = rc2;
951
952 /*
953 * Release autocaller before calling uninit.
954 */
955 autoCaller.release();
956
957 uninit();
958
959 LogFlowFuncLeaveRC(rc);
960 if (RT_FAILURE(rc))
961 {
962 if (rc == VERR_GSTCTL_GUEST_ERROR)
963 return GuestFile::setErrorExternal(this, guestRc);
964
965 return setError(VBOX_E_IPRT_ERROR,
966 tr("Closing guest file failed with %Rrc\n"), rc);
967 }
968
969 return S_OK;
970#endif /* VBOX_WITH_GUEST_CONTROL */
971}
972
973STDMETHODIMP GuestFile::QueryInfo(IFsObjInfo **aInfo)
974{
975#ifndef VBOX_WITH_GUEST_CONTROL
976 ReturnComNotImplemented();
977#else
978 AutoCaller autoCaller(this);
979 if (FAILED(autoCaller.rc())) return autoCaller.rc();
980
981 ReturnComNotImplemented();
982#endif /* VBOX_WITH_GUEST_CONTROL */
983}
984
985STDMETHODIMP GuestFile::Read(ULONG aToRead, ULONG aTimeoutMS, ComSafeArrayOut(BYTE, aData))
986{
987#ifndef VBOX_WITH_GUEST_CONTROL
988 ReturnComNotImplemented();
989#else
990 if (aToRead == 0)
991 return setError(E_INVALIDARG, tr("The size to read is zero"));
992 CheckComArgOutSafeArrayPointerValid(aData);
993
994 AutoCaller autoCaller(this);
995 if (FAILED(autoCaller.rc())) return autoCaller.rc();
996
997 com::SafeArray<BYTE> data((size_t)aToRead);
998 Assert(data.size() >= aToRead);
999
1000 HRESULT hr = S_OK;
1001
1002 uint32_t cbRead;
1003 int vrc = readData(aToRead, aTimeoutMS,
1004 data.raw(), aToRead, &cbRead);
1005 if (RT_SUCCESS(vrc))
1006 {
1007 if (data.size() != cbRead)
1008 data.resize(cbRead);
1009 data.detachTo(ComSafeArrayOutArg(aData));
1010 }
1011 else
1012 {
1013 switch (vrc)
1014 {
1015 default:
1016 hr = setError(VBOX_E_IPRT_ERROR,
1017 tr("Reading from file \"%s\" failed: %Rrc"),
1018 mData.mOpenInfo.mFileName.c_str(), vrc);
1019 break;
1020 }
1021 }
1022
1023 LogFlowThisFunc(("rc=%Rrc, cbRead=%RU64\n", vrc, cbRead));
1024
1025 LogFlowFuncLeaveRC(vrc);
1026 return hr;
1027#endif /* VBOX_WITH_GUEST_CONTROL */
1028}
1029
1030STDMETHODIMP GuestFile::ReadAt(LONG64 aOffset, ULONG aToRead, ULONG aTimeoutMS, ComSafeArrayOut(BYTE, aData))
1031{
1032#ifndef VBOX_WITH_GUEST_CONTROL
1033 ReturnComNotImplemented();
1034#else
1035 if (aToRead == 0)
1036 return setError(E_INVALIDARG, tr("The size to read is zero"));
1037 CheckComArgOutSafeArrayPointerValid(aData);
1038
1039 AutoCaller autoCaller(this);
1040 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1041
1042 com::SafeArray<BYTE> data((size_t)aToRead);
1043 Assert(data.size() >= aToRead);
1044
1045 HRESULT hr = S_OK;
1046
1047 size_t cbRead;
1048 int vrc = readDataAt(aOffset, aToRead, aTimeoutMS,
1049 data.raw(), aToRead, &cbRead);
1050 if (RT_SUCCESS(vrc))
1051 {
1052 if (data.size() != cbRead)
1053 data.resize(cbRead);
1054 data.detachTo(ComSafeArrayOutArg(aData));
1055 }
1056 else
1057 {
1058 switch (vrc)
1059 {
1060 default:
1061 hr = setError(VBOX_E_IPRT_ERROR,
1062 tr("Reading from file \"%s\" (at offset %RU64) failed: %Rrc"),
1063 mData.mOpenInfo.mFileName.c_str(), aOffset, vrc);
1064 break;
1065 }
1066 }
1067
1068 LogFlowThisFunc(("rc=%Rrc, cbRead=%RU64\n", vrc, cbRead));
1069
1070 LogFlowFuncLeaveRC(vrc);
1071 return hr;
1072#endif /* VBOX_WITH_GUEST_CONTROL */
1073}
1074
1075STDMETHODIMP GuestFile::Seek(LONG64 aOffset, FileSeekType_T aType)
1076{
1077#ifndef VBOX_WITH_GUEST_CONTROL
1078 ReturnComNotImplemented();
1079#else
1080 LogFlowThisFuncEnter();
1081
1082 AutoCaller autoCaller(this);
1083 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1084
1085 HRESULT hr = S_OK;
1086
1087 GUEST_FILE_SEEKTYPE eSeekType;
1088 switch (aType)
1089 {
1090 case FileSeekType_Set:
1091 eSeekType = GUEST_FILE_SEEKTYPE_BEGIN;
1092 break;
1093
1094 case FileSeekType_Current:
1095 eSeekType = GUEST_FILE_SEEKTYPE_CURRENT;
1096 break;
1097
1098 default:
1099 return setError(E_INVALIDARG, tr("Invalid seek type specified"));
1100 break;
1101 }
1102
1103 int vrc = seekAt(aOffset, eSeekType,
1104 30 * 1000 /* 30s timeout */, NULL /* puOffset */);
1105 if (RT_FAILURE(vrc))
1106 {
1107 switch (vrc)
1108 {
1109 default:
1110 hr = setError(VBOX_E_IPRT_ERROR,
1111 tr("Seeking file \"%s\" (to offset %RU64) failed: %Rrc"),
1112 mData.mOpenInfo.mFileName.c_str(), aOffset, vrc);
1113 break;
1114 }
1115 }
1116
1117 LogFlowFuncLeaveRC(vrc);
1118 return hr;
1119#endif /* VBOX_WITH_GUEST_CONTROL */
1120}
1121
1122STDMETHODIMP GuestFile::SetACL(IN_BSTR aACL)
1123{
1124#ifndef VBOX_WITH_GUEST_CONTROL
1125 ReturnComNotImplemented();
1126#else
1127 AutoCaller autoCaller(this);
1128 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1129
1130 ReturnComNotImplemented();
1131#endif /* VBOX_WITH_GUEST_CONTROL */
1132}
1133
1134STDMETHODIMP GuestFile::Write(ComSafeArrayIn(BYTE, aData), ULONG aTimeoutMS, ULONG *aWritten)
1135{
1136#ifndef VBOX_WITH_GUEST_CONTROL
1137 ReturnComNotImplemented();
1138#else
1139 LogFlowThisFuncEnter();
1140
1141 CheckComArgOutPointerValid(aWritten);
1142
1143 AutoCaller autoCaller(this);
1144 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1145
1146 HRESULT hr = S_OK;
1147
1148 com::SafeArray<BYTE> data(ComSafeArrayInArg(aData));
1149 int vrc = writeData(aTimeoutMS, data.raw(), (uint32_t)data.size(),
1150 (uint32_t*)aWritten);
1151 if (RT_FAILURE(vrc))
1152 {
1153 switch (vrc)
1154 {
1155 default:
1156 hr = setError(VBOX_E_IPRT_ERROR,
1157 tr("Writing to file \"%s\" failed: %Rrc"),
1158 mData.mOpenInfo.mFileName.c_str(), vrc);
1159 break;
1160 }
1161 }
1162
1163 LogFlowThisFunc(("rc=%Rrc, aWritten=%RU32\n", vrc, aWritten));
1164
1165 LogFlowFuncLeaveRC(vrc);
1166 return hr;
1167#endif /* VBOX_WITH_GUEST_CONTROL */
1168}
1169
1170STDMETHODIMP GuestFile::WriteAt(LONG64 aOffset, ComSafeArrayIn(BYTE, aData), ULONG aTimeoutMS, ULONG *aWritten)
1171{
1172#ifndef VBOX_WITH_GUEST_CONTROL
1173 ReturnComNotImplemented();
1174#else
1175 LogFlowThisFuncEnter();
1176
1177 CheckComArgOutPointerValid(aWritten);
1178
1179 AutoCaller autoCaller(this);
1180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1181
1182 HRESULT hr = S_OK;
1183
1184 com::SafeArray<BYTE> data(ComSafeArrayInArg(aData));
1185 int vrc = writeData(aTimeoutMS, data.raw(), (uint32_t)data.size(),
1186 (uint32_t*)aWritten);
1187 if (RT_FAILURE(vrc))
1188 {
1189 switch (vrc)
1190 {
1191 default:
1192 hr = setError(VBOX_E_IPRT_ERROR,
1193 tr("Writing to file \"%s\" (at offset %RU64) failed: %Rrc"),
1194 mData.mOpenInfo.mFileName.c_str(), aOffset, vrc);
1195 break;
1196 }
1197 }
1198
1199 LogFlowThisFunc(("rc=%Rrc, aWritten=%RU32\n", vrc, aWritten));
1200
1201 LogFlowFuncLeaveRC(vrc);
1202 return hr;
1203#endif /* VBOX_WITH_GUEST_CONTROL */
1204}
1205
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