VirtualBox

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

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

Build fix.

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