VirtualBox

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

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

GuestCtrl: More stuff for IGuestFile and IGuestSession handling (work in progress).

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

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