VirtualBox

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

Last change on this file since 84744 was 84744, checked in by vboxsync, 4 years ago

Guest Control/Main: Refined API error logging. bugref:9320

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.9 KB
Line 
1/* $Id: GuestFileImpl.cpp 84744 2020-06-09 18:55:07Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest file handling.
4 */
5
6/*
7 * Copyright (C) 2012-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN_GUESTFILE
23#include "LoggingNew.h"
24
25#ifndef VBOX_WITH_GUEST_CONTROL
26# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
27#endif
28#include "GuestFileImpl.h"
29#include "GuestSessionImpl.h"
30#include "GuestCtrlImplPrivate.h"
31#include "ConsoleImpl.h"
32#include "VirtualBoxErrorInfoImpl.h"
33
34#include "Global.h"
35#include "AutoCaller.h"
36#include "VBoxEvents.h"
37
38#include <iprt/cpp/utils.h> /* For unconst(). */
39#include <iprt/file.h>
40
41#include <VBox/com/array.h>
42#include <VBox/com/listeners.h>
43#include <VBox/AssertGuest.h>
44
45
46/**
47 * Internal listener class to serve events in an
48 * active manner, e.g. without polling delays.
49 */
50class GuestFileListener
51{
52public:
53
54 GuestFileListener(void)
55 {
56 }
57
58 virtual ~GuestFileListener()
59 {
60 }
61
62 HRESULT init(GuestFile *pFile)
63 {
64 AssertPtrReturn(pFile, E_POINTER);
65 mFile = pFile;
66 return S_OK;
67 }
68
69 void uninit(void)
70 {
71 mFile = NULL;
72 }
73
74 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
75 {
76 switch (aType)
77 {
78 case VBoxEventType_OnGuestFileStateChanged:
79 case VBoxEventType_OnGuestFileOffsetChanged:
80 case VBoxEventType_OnGuestFileRead:
81 case VBoxEventType_OnGuestFileWrite:
82 {
83 AssertPtrReturn(mFile, E_POINTER);
84 int rc2 = mFile->signalWaitEvent(aType, aEvent);
85 RT_NOREF(rc2);
86#ifdef DEBUG_andy
87 LogFlowFunc(("Signalling events of type=%RU32, file=%p resulted in rc=%Rrc\n",
88 aType, mFile, rc2));
89#endif
90 break;
91 }
92
93 default:
94 AssertMsgFailed(("Unhandled event %RU32\n", aType));
95 break;
96 }
97
98 return S_OK;
99 }
100
101private:
102
103 GuestFile *mFile;
104};
105typedef ListenerImpl<GuestFileListener, GuestFile*> GuestFileListenerImpl;
106
107VBOX_LISTENER_DECLARE(GuestFileListenerImpl)
108
109// constructor / destructor
110/////////////////////////////////////////////////////////////////////////////
111
112DEFINE_EMPTY_CTOR_DTOR(GuestFile)
113
114HRESULT GuestFile::FinalConstruct(void)
115{
116 LogFlowThisFuncEnter();
117 return BaseFinalConstruct();
118}
119
120void GuestFile::FinalRelease(void)
121{
122 LogFlowThisFuncEnter();
123 uninit();
124 BaseFinalRelease();
125 LogFlowThisFuncLeave();
126}
127
128// public initializer/uninitializer for internal purposes only
129/////////////////////////////////////////////////////////////////////////////
130
131/**
132 * Initializes a file object but does *not* open the file on the guest
133 * yet. This is done in the dedidcated openFile call.
134 *
135 * @return IPRT status code.
136 * @param pConsole Pointer to console object.
137 * @param pSession Pointer to session object.
138 * @param aObjectID The object's ID.
139 * @param openInfo File opening information.
140 */
141int GuestFile::init(Console *pConsole, GuestSession *pSession,
142 ULONG aObjectID, const GuestFileOpenInfo &openInfo)
143{
144 LogFlowThisFunc(("pConsole=%p, pSession=%p, aObjectID=%RU32, strPath=%s\n",
145 pConsole, pSession, aObjectID, openInfo.mFilename.c_str()));
146
147 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
148 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
149
150 /* Enclose the state transition NotReady->InInit->Ready. */
151 AutoInitSpan autoInitSpan(this);
152 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
153
154 int vrc = bindToSession(pConsole, pSession, aObjectID);
155 if (RT_SUCCESS(vrc))
156 {
157 mSession = pSession;
158
159 mData.mOpenInfo = openInfo;
160 mData.mInitialSize = 0;
161 mData.mStatus = FileStatus_Undefined;
162 mData.mLastError = VINF_SUCCESS;
163 mData.mOffCurrent = 0;
164
165 unconst(mEventSource).createObject();
166 HRESULT hr = mEventSource->init();
167 if (FAILED(hr))
168 vrc = VERR_COM_UNEXPECTED;
169 }
170
171 if (RT_SUCCESS(vrc))
172 {
173 try
174 {
175 GuestFileListener *pListener = new GuestFileListener();
176 ComObjPtr<GuestFileListenerImpl> thisListener;
177 HRESULT hr = thisListener.createObject();
178 if (SUCCEEDED(hr))
179 hr = thisListener->init(pListener, this);
180
181 if (SUCCEEDED(hr))
182 {
183 com::SafeArray <VBoxEventType_T> eventTypes;
184 eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
185 eventTypes.push_back(VBoxEventType_OnGuestFileOffsetChanged);
186 eventTypes.push_back(VBoxEventType_OnGuestFileRead);
187 eventTypes.push_back(VBoxEventType_OnGuestFileWrite);
188 hr = mEventSource->RegisterListener(thisListener,
189 ComSafeArrayAsInParam(eventTypes),
190 TRUE /* Active listener */);
191 if (SUCCEEDED(hr))
192 {
193 vrc = baseInit();
194 if (RT_SUCCESS(vrc))
195 {
196 mLocalListener = thisListener;
197 }
198 }
199 else
200 vrc = VERR_COM_UNEXPECTED;
201 }
202 else
203 vrc = VERR_COM_UNEXPECTED;
204 }
205 catch(std::bad_alloc &)
206 {
207 vrc = VERR_NO_MEMORY;
208 }
209 }
210
211 if (RT_SUCCESS(vrc))
212 {
213 /* Confirm a successful initialization when it's the case. */
214 autoInitSpan.setSucceeded();
215 }
216 else
217 autoInitSpan.setFailed();
218
219 LogFlowFuncLeaveRC(vrc);
220 return vrc;
221}
222
223/**
224 * Uninitializes the instance.
225 * Called from FinalRelease().
226 */
227void GuestFile::uninit(void)
228{
229 /* Enclose the state transition Ready->InUninit->NotReady. */
230 AutoUninitSpan autoUninitSpan(this);
231 if (autoUninitSpan.uninitDone())
232 return;
233
234 LogFlowThisFuncEnter();
235
236 baseUninit();
237 LogFlowThisFuncLeave();
238}
239
240// implementation of public getters/setters for attributes
241/////////////////////////////////////////////////////////////////////////////
242
243HRESULT GuestFile::getCreationMode(ULONG *aCreationMode)
244{
245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
246
247 *aCreationMode = mData.mOpenInfo.mCreationMode;
248
249 return S_OK;
250}
251
252HRESULT GuestFile::getOpenAction(FileOpenAction_T *aOpenAction)
253{
254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
255
256 *aOpenAction = mData.mOpenInfo.mOpenAction;
257
258 return S_OK;
259}
260
261HRESULT GuestFile::getEventSource(ComPtr<IEventSource> &aEventSource)
262{
263 /* No need to lock - lifetime constant. */
264 mEventSource.queryInterfaceTo(aEventSource.asOutParam());
265
266 return S_OK;
267}
268
269HRESULT GuestFile::getFilename(com::Utf8Str &aFilename)
270{
271 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
272
273 aFilename = mData.mOpenInfo.mFilename;
274
275 return S_OK;
276}
277
278HRESULT GuestFile::getId(ULONG *aId)
279{
280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
281
282 *aId = mObjectID;
283
284 return S_OK;
285}
286
287HRESULT GuestFile::getInitialSize(LONG64 *aInitialSize)
288{
289 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
290
291 *aInitialSize = mData.mInitialSize;
292
293 return S_OK;
294}
295
296HRESULT GuestFile::getOffset(LONG64 *aOffset)
297{
298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
299
300 /*
301 * This is updated by GuestFile::i_onFileNotify() when read, write and seek
302 * confirmation messages are recevied.
303 *
304 * Note! This will not be accurate with older (< 5.2.32, 6.0.0 - 6.0.9)
305 * Guest Additions when using writeAt, readAt or writing to a file
306 * opened in append mode.
307 */
308 *aOffset = mData.mOffCurrent;
309
310 return S_OK;
311}
312
313HRESULT GuestFile::getAccessMode(FileAccessMode_T *aAccessMode)
314{
315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
316
317 *aAccessMode = mData.mOpenInfo.mAccessMode;
318
319 return S_OK;
320}
321
322HRESULT GuestFile::getStatus(FileStatus_T *aStatus)
323{
324 LogFlowThisFuncEnter();
325
326 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
327
328 *aStatus = mData.mStatus;
329
330 return S_OK;
331}
332
333// private methods
334/////////////////////////////////////////////////////////////////////////////
335
336int GuestFile::i_callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
337{
338 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
339 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
340
341 LogFlowThisFunc(("strName=%s, uContextID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
342 mData.mOpenInfo.mFilename.c_str(), pCbCtx->uContextID, pCbCtx->uMessage, pSvcCb));
343
344 int vrc;
345 switch (pCbCtx->uMessage)
346 {
347 case GUEST_MSG_DISCONNECTED:
348 vrc = i_onGuestDisconnected(pCbCtx, pSvcCb);
349 break;
350
351 case GUEST_MSG_FILE_NOTIFY:
352 vrc = i_onFileNotify(pCbCtx, pSvcCb);
353 break;
354
355 default:
356 /* Silently ignore not implemented functions. */
357 vrc = VERR_NOT_SUPPORTED;
358 break;
359 }
360
361#ifdef DEBUG
362 LogFlowFuncLeaveRC(vrc);
363#endif
364 return vrc;
365}
366
367int GuestFile::i_closeFile(int *prcGuest)
368{
369 LogFlowThisFunc(("strFile=%s\n", mData.mOpenInfo.mFilename.c_str()));
370
371 int vrc;
372
373 GuestWaitEvent *pEvent = NULL;
374 GuestEventTypes eventTypes;
375 try
376 {
377 eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
378
379 vrc = registerWaitEvent(eventTypes, &pEvent);
380 }
381 catch (std::bad_alloc &)
382 {
383 vrc = VERR_NO_MEMORY;
384 }
385
386 if (RT_FAILURE(vrc))
387 return vrc;
388
389 /* Prepare HGCM call. */
390 VBOXHGCMSVCPARM paParms[4];
391 int i = 0;
392 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
393 HGCMSvcSetU32(&paParms[i++], mObjectID /* Guest file ID */);
394
395 vrc = sendMessage(HOST_MSG_FILE_CLOSE, i, paParms);
396 if (RT_SUCCESS(vrc))
397 vrc = i_waitForStatusChange(pEvent, 30 * 1000 /* Timeout in ms */,
398 NULL /* FileStatus */, prcGuest);
399 unregisterWaitEvent(pEvent);
400
401 LogFlowFuncLeaveRC(vrc);
402 return vrc;
403}
404
405/**
406 * Converts a given guest file error to a string.
407 *
408 * @returns Error string.
409 * @param rcGuest Guest file error to return string for.
410 * @param pcszWhat Hint of what was involved when the error occurred.
411 */
412/* static */
413Utf8Str GuestFile::i_guestErrorToString(int rcGuest, const char *pcszWhat)
414{
415 AssertPtrReturn(pcszWhat, "");
416
417 Utf8Str strErr;
418
419#define CASE_MSG(a_iRc, a_strFormatString, ...) \
420 case a_iRc: strErr = Utf8StrFmt(a_strFormatString, ##__VA_ARGS__); break;
421
422 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
423 switch (rcGuest)
424 {
425 CASE_MSG(VERR_ACCESS_DENIED , tr("Access to guest file \"%s\" denied"), pcszWhat);
426 CASE_MSG(VERR_ALREADY_EXISTS , tr("Guest file \"%s\" already exists"), pcszWhat);
427 CASE_MSG(VERR_FILE_NOT_FOUND , tr("Guest file \"%s\" not found"), pcszWhat);
428 CASE_MSG(VERR_NET_HOST_NOT_FOUND, tr("Host name \"%s\", not found"), pcszWhat);
429 CASE_MSG(VERR_SHARING_VIOLATION , tr("Sharing violation for guest file \"%s\""), pcszWhat);
430 default:
431 {
432 char szDefine[80];
433 RTErrQueryDefine(rcGuest, szDefine, sizeof(szDefine), false /*fFailIfUnknown*/);
434 strErr = Utf8StrFmt(tr("Error %s for guest file \"%s\" occurred\n"), szDefine, pcszWhat);
435 break;
436 }
437 }
438
439#undef CASE_MSG
440
441 return strErr;
442}
443
444int GuestFile::i_onFileNotify(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
445{
446 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
447 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
448
449 LogFlowThisFuncEnter();
450
451 if (pSvcCbData->mParms < 3)
452 return VERR_INVALID_PARAMETER;
453
454 int idx = 1; /* Current parameter index. */
455 CALLBACKDATA_FILE_NOTIFY dataCb;
456 RT_ZERO(dataCb);
457 /* pSvcCb->mpaParms[0] always contains the context ID. */
458 HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.uType);
459 HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.rc);
460
461 int rcGuest = (int)dataCb.rc; /* uint32_t vs. int. */
462
463 LogFlowThisFunc(("uType=%RU32, rcGuest=%Rrc\n", dataCb.uType, rcGuest));
464
465 if (RT_FAILURE(rcGuest))
466 {
467 int rc2 = i_setFileStatus(FileStatus_Error, rcGuest);
468 AssertRC(rc2);
469
470 /* Ignore rc, as the event to signal might not be there (anymore). */
471 signalWaitEventInternal(pCbCtx, rcGuest, NULL /* pPayload */);
472 return VINF_SUCCESS; /* Report to the guest. */
473 }
474
475 AssertMsg(mObjectID == VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCbCtx->uContextID),
476 ("File ID %RU32 does not match object ID %RU32\n", mObjectID,
477 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCbCtx->uContextID)));
478
479 int rc = VERR_NOT_SUPPORTED; /* Play safe by default. */
480
481 switch (dataCb.uType)
482 {
483 case GUEST_FILE_NOTIFYTYPE_ERROR:
484 {
485 rc = i_setFileStatus(FileStatus_Error, rcGuest);
486 break;
487 }
488
489 case GUEST_FILE_NOTIFYTYPE_OPEN:
490 {
491 if (pSvcCbData->mParms == 4)
492 {
493 rc = HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.u.open.uHandle);
494 if (RT_FAILURE(rc))
495 break;
496
497 /* Set the process status. */
498 rc = i_setFileStatus(FileStatus_Open, rcGuest);
499 }
500 break;
501 }
502
503 case GUEST_FILE_NOTIFYTYPE_CLOSE:
504 {
505 rc = i_setFileStatus(FileStatus_Closed, rcGuest);
506 break;
507 }
508
509 case GUEST_FILE_NOTIFYTYPE_READ:
510 {
511 if (pSvcCbData->mParms == 4)
512 {
513 rc = HGCMSvcGetPv(&pSvcCbData->mpaParms[idx++], &dataCb.u.read.pvData,
514 &dataCb.u.read.cbData);
515 if (RT_FAILURE(rc))
516 break;
517
518 const uint32_t cbRead = dataCb.u.read.cbData;
519
520 Log3ThisFunc(("cbRead=%RU32\n", cbRead));
521
522 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
523
524 mData.mOffCurrent += cbRead; /* Bogus for readAt, which is why we've got GUEST_FILE_NOTIFYTYPE_READ_OFFSET. */
525
526 alock.release();
527
528 com::SafeArray<BYTE> data((size_t)cbRead);
529 data.initFrom((BYTE*)dataCb.u.read.pvData, cbRead);
530
531 fireGuestFileReadEvent(mEventSource, mSession, this, mData.mOffCurrent,
532 cbRead, ComSafeArrayAsInParam(data));
533 }
534 break;
535 }
536
537 case GUEST_FILE_NOTIFYTYPE_READ_OFFSET:
538 {
539 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mParms == 5, ("mParms=%u\n", pSvcCbData->mParms),
540 rc = VERR_WRONG_PARAMETER_COUNT);
541 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mpaParms[idx].type == VBOX_HGCM_SVC_PARM_PTR,
542 ("type=%u\n", pSvcCbData->mpaParms[idx].type),
543 rc = VERR_WRONG_PARAMETER_TYPE);
544 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mpaParms[idx + 1].type == VBOX_HGCM_SVC_PARM_64BIT,
545 ("type=%u\n", pSvcCbData->mpaParms[idx].type),
546 rc = VERR_WRONG_PARAMETER_TYPE);
547 BYTE const * const pbData = (BYTE const *)pSvcCbData->mpaParms[idx].u.pointer.addr;
548 uint32_t const cbRead = pSvcCbData->mpaParms[idx].u.pointer.size;
549 int64_t offNew = (int64_t)pSvcCbData->mpaParms[idx + 1].u.uint64;
550 Log3ThisFunc(("cbRead=%RU32 offNew=%RI64 (%#RX64)\n", cbRead, offNew, offNew));
551
552 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
553 if (offNew < 0) /* non-seekable */
554 offNew = mData.mOffCurrent + cbRead;
555 mData.mOffCurrent = offNew;
556 alock.release();
557
558 try
559 {
560 com::SafeArray<BYTE> data((size_t)cbRead);
561 data.initFrom(pbData, cbRead);
562 fireGuestFileReadEvent(mEventSource, mSession, this, offNew, cbRead, ComSafeArrayAsInParam(data));
563 rc = VINF_SUCCESS;
564 }
565 catch (std::bad_alloc &)
566 {
567 rc = VERR_NO_MEMORY;
568 }
569 break;
570 }
571
572 case GUEST_FILE_NOTIFYTYPE_WRITE:
573 {
574 if (pSvcCbData->mParms == 4)
575 {
576 rc = HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.u.write.cbWritten);
577 if (RT_FAILURE(rc))
578 break;
579
580 const uint32_t cbWritten = dataCb.u.write.cbWritten;
581
582 Log3ThisFunc(("cbWritten=%RU32\n", cbWritten));
583
584 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
585
586 mData.mOffCurrent += cbWritten; /* Bogus for writeAt and append mode, thus GUEST_FILE_NOTIFYTYPE_WRITE_OFFSET. */
587
588 alock.release();
589
590 fireGuestFileWriteEvent(mEventSource, mSession, this, mData.mOffCurrent, cbWritten);
591 }
592 break;
593 }
594
595 case GUEST_FILE_NOTIFYTYPE_WRITE_OFFSET:
596 {
597 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mParms == 5, ("mParms=%u\n", pSvcCbData->mParms),
598 rc = VERR_WRONG_PARAMETER_COUNT);
599 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mpaParms[idx].type == VBOX_HGCM_SVC_PARM_32BIT,
600 ("type=%u\n", pSvcCbData->mpaParms[idx].type),
601 rc = VERR_WRONG_PARAMETER_TYPE);
602 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mpaParms[idx + 1].type == VBOX_HGCM_SVC_PARM_64BIT,
603 ("type=%u\n", pSvcCbData->mpaParms[idx].type),
604 rc = VERR_WRONG_PARAMETER_TYPE);
605 uint32_t const cbWritten = pSvcCbData->mpaParms[idx].u.uint32;
606 int64_t offNew = (int64_t)pSvcCbData->mpaParms[idx + 1].u.uint64;
607 Log3ThisFunc(("cbWritten=%RU32 offNew=%RI64 (%#RX64)\n", cbWritten, offNew, offNew));
608
609 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
610 if (offNew < 0) /* non-seekable */
611 offNew = mData.mOffCurrent + cbWritten;
612 mData.mOffCurrent = offNew;
613 alock.release();
614
615 try
616 {
617 fireGuestFileWriteEvent(mEventSource, mSession, this, offNew, cbWritten);
618 rc = VINF_SUCCESS;
619 }
620 catch (std::bad_alloc &)
621 {
622 rc = VERR_NO_MEMORY;
623 }
624 break;
625 }
626
627 case GUEST_FILE_NOTIFYTYPE_SEEK:
628 {
629 if (pSvcCbData->mParms == 4)
630 {
631 rc = HGCMSvcGetU64(&pSvcCbData->mpaParms[idx++], &dataCb.u.seek.uOffActual);
632 if (RT_FAILURE(rc))
633 break;
634
635 Log3ThisFunc(("uOffActual=%RU64\n", dataCb.u.seek.uOffActual));
636
637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
638
639 mData.mOffCurrent = dataCb.u.seek.uOffActual;
640
641 alock.release();
642
643 fireGuestFileOffsetChangedEvent(mEventSource, mSession, this, dataCb.u.seek.uOffActual, 0 /* Processed */);
644 }
645 break;
646 }
647
648 case GUEST_FILE_NOTIFYTYPE_TELL:
649 /* We don't issue any HOST_MSG_FILE_TELL, so we shouldn't get these notifications! */
650 AssertFailed();
651 break;
652
653 case GUEST_FILE_NOTIFYTYPE_SET_SIZE:
654 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mParms == 4, ("mParms=%u\n", pSvcCbData->mParms),
655 rc = VERR_WRONG_PARAMETER_COUNT);
656 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mpaParms[idx].type == VBOX_HGCM_SVC_PARM_64BIT,
657 ("type=%u\n", pSvcCbData->mpaParms[idx].type),
658 rc = VERR_WRONG_PARAMETER_TYPE);
659 dataCb.u.SetSize.cbSize = pSvcCbData->mpaParms[idx].u.uint64;
660 Log3ThisFunc(("cbSize=%RU64\n", dataCb.u.SetSize.cbSize));
661
662 fireGuestFileSizeChangedEvent(mEventSource, mSession, this, dataCb.u.SetSize.cbSize);
663 rc = VINF_SUCCESS;
664 break;
665
666 default:
667 break;
668 }
669
670 if (RT_SUCCESS(rc))
671 {
672 GuestWaitEventPayload payload(dataCb.uType, &dataCb, sizeof(dataCb));
673
674 /* Ignore rc, as the event to signal might not be there (anymore). */
675 signalWaitEventInternal(pCbCtx, rcGuest, &payload);
676 }
677
678 LogFlowThisFunc(("uType=%RU32, rcGuest=%Rrc, rc=%Rrc\n", dataCb.uType, rcGuest, rc));
679 return rc;
680}
681
682int GuestFile::i_onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
683{
684 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
685 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
686
687 int vrc = i_setFileStatus(FileStatus_Down, VINF_SUCCESS);
688
689 LogFlowFuncLeaveRC(vrc);
690 return vrc;
691}
692
693/**
694 * @copydoc GuestObject::i_onUnregister
695 */
696int GuestFile::i_onUnregister(void)
697{
698 LogFlowThisFuncEnter();
699
700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
701
702 int vrc = VINF_SUCCESS;
703
704 /*
705 * Note: The event source stuff holds references to this object,
706 * so make sure that this is cleaned up *before* calling uninit().
707 */
708 if (!mEventSource.isNull())
709 {
710 mEventSource->UnregisterListener(mLocalListener);
711
712 mLocalListener.setNull();
713 unconst(mEventSource).setNull();
714 }
715
716 LogFlowFuncLeaveRC(vrc);
717 return vrc;
718}
719
720/**
721 * @copydoc GuestObject::i_onSessionStatusChange
722 */
723int GuestFile::i_onSessionStatusChange(GuestSessionStatus_T enmSessionStatus)
724{
725 LogFlowThisFuncEnter();
726
727 int vrc = VINF_SUCCESS;
728
729 /* If the session now is in a terminated state, set the file status
730 * to "down", as there is not much else we can do now. */
731 if (GuestSession::i_isTerminated(enmSessionStatus))
732 vrc = i_setFileStatus(FileStatus_Down, 0 /* fileRc, ignored */);
733
734 LogFlowFuncLeaveRC(vrc);
735 return vrc;
736}
737
738int GuestFile::i_openFile(uint32_t uTimeoutMS, int *prcGuest)
739{
740 AssertReturn(mData.mOpenInfo.mFilename.isNotEmpty(), VERR_INVALID_PARAMETER);
741
742 LogFlowThisFuncEnter();
743
744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
745
746 LogFlowThisFunc(("strFile=%s, enmAccessMode=%d, enmOpenAction=%d, uCreationMode=%o, mfOpenEx=%#x\n",
747 mData.mOpenInfo.mFilename.c_str(), mData.mOpenInfo.mAccessMode, mData.mOpenInfo.mOpenAction,
748 mData.mOpenInfo.mCreationMode, mData.mOpenInfo.mfOpenEx));
749
750 /* Validate and translate open action. */
751 const char *pszOpenAction = NULL;
752 switch (mData.mOpenInfo.mOpenAction)
753 {
754 case FileOpenAction_OpenExisting: pszOpenAction = "oe"; break;
755 case FileOpenAction_OpenOrCreate: pszOpenAction = "oc"; break;
756 case FileOpenAction_CreateNew: pszOpenAction = "ce"; break;
757 case FileOpenAction_CreateOrReplace: pszOpenAction = "ca"; break;
758 case FileOpenAction_OpenExistingTruncated: pszOpenAction = "ot"; break;
759 case FileOpenAction_AppendOrCreate:
760 pszOpenAction = "oa"; /** @todo get rid of this one and implement AppendOnly/AppendRead. */
761 break;
762 default:
763 return VERR_INVALID_PARAMETER;
764 }
765
766 /* Validate and translate access mode. */
767 const char *pszAccessMode = NULL;
768 switch (mData.mOpenInfo.mAccessMode)
769 {
770 case FileAccessMode_ReadOnly: pszAccessMode = "r"; break;
771 case FileAccessMode_WriteOnly: pszAccessMode = "w"; break;
772 case FileAccessMode_ReadWrite: pszAccessMode = "r+"; break;
773 case FileAccessMode_AppendOnly: pszAccessMode = "a"; break;
774 case FileAccessMode_AppendRead: pszAccessMode = "a+"; break;
775 default: return VERR_INVALID_PARAMETER;
776 }
777
778 /* Validate and translate sharing mode. */
779 const char *pszSharingMode = NULL;
780 switch (mData.mOpenInfo.mSharingMode)
781 {
782 case FileSharingMode_All: pszSharingMode = ""; break;
783 case FileSharingMode_Read: RT_FALL_THRU();
784 case FileSharingMode_Write: RT_FALL_THRU();
785 case FileSharingMode_ReadWrite: RT_FALL_THRU();
786 case FileSharingMode_Delete: RT_FALL_THRU();
787 case FileSharingMode_ReadDelete: RT_FALL_THRU();
788 case FileSharingMode_WriteDelete: return VERR_NOT_IMPLEMENTED;
789 default: return VERR_INVALID_PARAMETER;
790 }
791
792 int vrc;
793
794 GuestWaitEvent *pEvent = NULL;
795 GuestEventTypes eventTypes;
796 try
797 {
798 eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
799
800 vrc = registerWaitEvent(eventTypes, &pEvent);
801 }
802 catch (std::bad_alloc &)
803 {
804 vrc = VERR_NO_MEMORY;
805 }
806
807 if (RT_FAILURE(vrc))
808 return vrc;
809
810 /* Prepare HGCM call. */
811 VBOXHGCMSVCPARM paParms[8];
812 int i = 0;
813 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
814 HGCMSvcSetPv(&paParms[i++], (void*)mData.mOpenInfo.mFilename.c_str(),
815 (ULONG)mData.mOpenInfo.mFilename.length() + 1);
816 HGCMSvcSetStr(&paParms[i++], pszAccessMode);
817 HGCMSvcSetStr(&paParms[i++], pszOpenAction);
818 HGCMSvcSetStr(&paParms[i++], pszSharingMode);
819 HGCMSvcSetU32(&paParms[i++], mData.mOpenInfo.mCreationMode);
820 HGCMSvcSetU64(&paParms[i++], 0 /*unused offset*/);
821 /** @todo Next protocol version: add flags, replace strings, remove initial offset. */
822
823 alock.release(); /* Drop write lock before sending. */
824
825 vrc = sendMessage(HOST_MSG_FILE_OPEN, i, paParms);
826 if (RT_SUCCESS(vrc))
827 vrc = i_waitForStatusChange(pEvent, uTimeoutMS, NULL /* FileStatus */, prcGuest);
828
829 unregisterWaitEvent(pEvent);
830
831 LogFlowFuncLeaveRC(vrc);
832 return vrc;
833}
834
835int GuestFile::i_queryInfo(GuestFsObjData &objData, int *prcGuest)
836{
837 AssertPtr(mSession);
838 return mSession->i_fsQueryInfo(mData.mOpenInfo.mFilename, FALSE /* fFollowSymlinks */, objData, prcGuest);
839}
840
841int GuestFile::i_readData(uint32_t uSize, uint32_t uTimeoutMS,
842 void* pvData, uint32_t cbData, uint32_t* pcbRead)
843{
844 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
845 AssertReturn(cbData, VERR_INVALID_PARAMETER);
846
847 LogFlowThisFunc(("uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
848 uSize, uTimeoutMS, pvData, cbData));
849
850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
851
852 int vrc;
853
854 GuestWaitEvent *pEvent = NULL;
855 GuestEventTypes eventTypes;
856 try
857 {
858 eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
859 eventTypes.push_back(VBoxEventType_OnGuestFileRead);
860
861 vrc = registerWaitEvent(eventTypes, &pEvent);
862 }
863 catch (std::bad_alloc &)
864 {
865 vrc = VERR_NO_MEMORY;
866 }
867
868 if (RT_FAILURE(vrc))
869 return vrc;
870
871 /* Prepare HGCM call. */
872 VBOXHGCMSVCPARM paParms[4];
873 int i = 0;
874 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
875 HGCMSvcSetU32(&paParms[i++], mObjectID /* File handle */);
876 HGCMSvcSetU32(&paParms[i++], uSize /* Size (in bytes) to read */);
877
878 alock.release(); /* Drop write lock before sending. */
879
880 vrc = sendMessage(HOST_MSG_FILE_READ, i, paParms);
881 if (RT_SUCCESS(vrc))
882 {
883 uint32_t cbRead = 0;
884 vrc = i_waitForRead(pEvent, uTimeoutMS, pvData, cbData, &cbRead);
885 if (RT_SUCCESS(vrc))
886 {
887 LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
888 if (pcbRead)
889 *pcbRead = cbRead;
890 }
891 else if (pEvent->HasGuestError()) /* Return guest rc if available. */
892 {
893 vrc = pEvent->GetGuestError();
894 }
895 }
896
897 unregisterWaitEvent(pEvent);
898
899 LogFlowFuncLeaveRC(vrc);
900 return vrc;
901}
902
903int GuestFile::i_readDataAt(uint64_t uOffset, uint32_t uSize, uint32_t uTimeoutMS,
904 void* pvData, size_t cbData, size_t* pcbRead)
905{
906 LogFlowThisFunc(("uOffset=%RU64, uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
907 uOffset, uSize, uTimeoutMS, pvData, cbData));
908
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 int vrc;
912
913 GuestWaitEvent *pEvent = NULL;
914 GuestEventTypes eventTypes;
915 try
916 {
917 eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
918 eventTypes.push_back(VBoxEventType_OnGuestFileRead);
919
920 vrc = registerWaitEvent(eventTypes, &pEvent);
921 }
922 catch (std::bad_alloc &)
923 {
924 vrc = VERR_NO_MEMORY;
925 }
926
927 if (RT_FAILURE(vrc))
928 return vrc;
929
930 /* Prepare HGCM call. */
931 VBOXHGCMSVCPARM paParms[4];
932 int i = 0;
933 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
934 HGCMSvcSetU32(&paParms[i++], mObjectID /* File handle */);
935 HGCMSvcSetU64(&paParms[i++], uOffset /* Offset (in bytes) to start reading */);
936 HGCMSvcSetU32(&paParms[i++], uSize /* Size (in bytes) to read */);
937
938 alock.release(); /* Drop write lock before sending. */
939
940 vrc = sendMessage(HOST_MSG_FILE_READ_AT, i, paParms);
941 if (RT_SUCCESS(vrc))
942 {
943 uint32_t cbRead = 0;
944 vrc = i_waitForRead(pEvent, uTimeoutMS, pvData, cbData, &cbRead);
945 if (RT_SUCCESS(vrc))
946 {
947 LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
948
949 if (pcbRead)
950 *pcbRead = cbRead;
951 }
952 else if (pEvent->HasGuestError()) /* Return guest rc if available. */
953 {
954 vrc = pEvent->GetGuestError();
955 }
956 }
957
958 unregisterWaitEvent(pEvent);
959
960 LogFlowFuncLeaveRC(vrc);
961 return vrc;
962}
963
964int GuestFile::i_seekAt(int64_t iOffset, GUEST_FILE_SEEKTYPE eSeekType,
965 uint32_t uTimeoutMS, uint64_t *puOffset)
966{
967 LogFlowThisFunc(("iOffset=%RI64, uTimeoutMS=%RU32\n",
968 iOffset, uTimeoutMS));
969
970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
971
972 int vrc;
973
974 GuestWaitEvent *pEvent = NULL;
975 GuestEventTypes eventTypes;
976 try
977 {
978 eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
979 eventTypes.push_back(VBoxEventType_OnGuestFileOffsetChanged);
980
981 vrc = registerWaitEvent(eventTypes, &pEvent);
982 }
983 catch (std::bad_alloc &)
984 {
985 vrc = VERR_NO_MEMORY;
986 }
987
988 if (RT_FAILURE(vrc))
989 return vrc;
990
991 /* Prepare HGCM call. */
992 VBOXHGCMSVCPARM paParms[4];
993 int i = 0;
994 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
995 HGCMSvcSetU32(&paParms[i++], mObjectID /* File handle */);
996 HGCMSvcSetU32(&paParms[i++], eSeekType /* Seek method */);
997 /** @todo uint64_t vs. int64_t! */
998 HGCMSvcSetU64(&paParms[i++], (uint64_t)iOffset /* Offset (in bytes) to start reading */);
999
1000 alock.release(); /* Drop write lock before sending. */
1001
1002 vrc = sendMessage(HOST_MSG_FILE_SEEK, i, paParms);
1003 if (RT_SUCCESS(vrc))
1004 {
1005 uint64_t uOffset;
1006 vrc = i_waitForOffsetChange(pEvent, uTimeoutMS, &uOffset);
1007 if (RT_SUCCESS(vrc))
1008 {
1009 LogFlowThisFunc(("uOffset=%RU64\n", uOffset));
1010
1011 if (puOffset)
1012 *puOffset = uOffset;
1013 }
1014 else if (pEvent->HasGuestError()) /* Return guest rc if available. */
1015 {
1016 vrc = pEvent->GetGuestError();
1017 }
1018 }
1019
1020 unregisterWaitEvent(pEvent);
1021
1022 LogFlowFuncLeaveRC(vrc);
1023 return vrc;
1024}
1025
1026int GuestFile::i_setFileStatus(FileStatus_T fileStatus, int fileRc)
1027{
1028 LogFlowThisFuncEnter();
1029
1030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1031
1032 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, fileRc=%Rrc\n",
1033 mData.mStatus, fileStatus, fileRc));
1034
1035#ifdef VBOX_STRICT
1036 if (fileStatus == FileStatus_Error)
1037 {
1038 AssertMsg(RT_FAILURE(fileRc), ("Guest rc must be an error (%Rrc)\n", fileRc));
1039 }
1040 else
1041 AssertMsg(RT_SUCCESS(fileRc), ("Guest rc must not be an error (%Rrc)\n", fileRc));
1042#endif
1043
1044 if (mData.mStatus != fileStatus)
1045 {
1046 mData.mStatus = fileStatus;
1047 mData.mLastError = fileRc;
1048
1049 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1050 HRESULT hr = errorInfo.createObject();
1051 ComAssertComRC(hr);
1052 if (RT_FAILURE(fileRc))
1053 {
1054 hr = errorInfo->initEx(VBOX_E_IPRT_ERROR, fileRc,
1055 COM_IIDOF(IGuestFile), getComponentName(),
1056 i_guestErrorToString(fileRc, mData.mOpenInfo.mFilename.c_str()));
1057 ComAssertComRC(hr);
1058 }
1059
1060 alock.release(); /* Release lock before firing off event. */
1061
1062 fireGuestFileStateChangedEvent(mEventSource, mSession,
1063 this, fileStatus, errorInfo);
1064 }
1065
1066 return VINF_SUCCESS;
1067}
1068
1069int GuestFile::i_waitForOffsetChange(GuestWaitEvent *pEvent,
1070 uint32_t uTimeoutMS, uint64_t *puOffset)
1071{
1072 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1073
1074 VBoxEventType_T evtType;
1075 ComPtr<IEvent> pIEvent;
1076 int vrc = waitForEvent(pEvent, uTimeoutMS,
1077 &evtType, pIEvent.asOutParam());
1078 if (RT_SUCCESS(vrc))
1079 {
1080 if (evtType == VBoxEventType_OnGuestFileOffsetChanged)
1081 {
1082 if (puOffset)
1083 {
1084 ComPtr<IGuestFileOffsetChangedEvent> pFileEvent = pIEvent;
1085 Assert(!pFileEvent.isNull());
1086
1087 HRESULT hr = pFileEvent->COMGETTER(Offset)((LONG64*)puOffset);
1088 ComAssertComRC(hr);
1089 }
1090 }
1091 else
1092 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1093 }
1094
1095 return vrc;
1096}
1097
1098int GuestFile::i_waitForRead(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
1099 void *pvData, size_t cbData, uint32_t *pcbRead)
1100{
1101 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1102
1103 VBoxEventType_T evtType;
1104 ComPtr<IEvent> pIEvent;
1105 int vrc = waitForEvent(pEvent, uTimeoutMS,
1106 &evtType, pIEvent.asOutParam());
1107 if (RT_SUCCESS(vrc))
1108 {
1109 if (evtType == VBoxEventType_OnGuestFileRead)
1110 {
1111 vrc = VINF_SUCCESS;
1112
1113 ComPtr<IGuestFileReadEvent> pFileEvent = pIEvent;
1114 Assert(!pFileEvent.isNull());
1115
1116 if (pvData)
1117 {
1118 com::SafeArray <BYTE> data;
1119 HRESULT hrc1 = pFileEvent->COMGETTER(Data)(ComSafeArrayAsOutParam(data));
1120 ComAssertComRC(hrc1);
1121 const size_t cbRead = data.size();
1122 if (cbRead)
1123 {
1124 if (cbRead <= cbData)
1125 memcpy(pvData, data.raw(), cbRead);
1126 else
1127 vrc = VERR_BUFFER_OVERFLOW;
1128 }
1129 /* else: used to be VERR_NO_DATA, but that messes stuff up. */
1130
1131 if (pcbRead)
1132 {
1133 *pcbRead = (uint32_t)cbRead;
1134 Assert(*pcbRead == cbRead);
1135 }
1136 }
1137 else if (pcbRead)
1138 {
1139 *pcbRead = 0;
1140 HRESULT hrc2 = pFileEvent->COMGETTER(Processed)((ULONG *)pcbRead);
1141 ComAssertComRC(hrc2); NOREF(hrc2);
1142 }
1143 }
1144 else
1145 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1146 }
1147
1148 return vrc;
1149}
1150
1151/**
1152 * Undocumented, use with great care.
1153 *
1154 * @note Similar code in GuestProcess::i_waitForStatusChange() and
1155 * GuestSession::i_waitForStatusChange().
1156 */
1157int GuestFile::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
1158 FileStatus_T *pFileStatus, int *prcGuest)
1159{
1160 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1161 /* pFileStatus is optional. */
1162
1163 VBoxEventType_T evtType;
1164 ComPtr<IEvent> pIEvent;
1165 int vrc = waitForEvent(pEvent, uTimeoutMS,
1166 &evtType, pIEvent.asOutParam());
1167 if (RT_SUCCESS(vrc))
1168 {
1169 Assert(evtType == VBoxEventType_OnGuestFileStateChanged);
1170 ComPtr<IGuestFileStateChangedEvent> pFileEvent = pIEvent;
1171 Assert(!pFileEvent.isNull());
1172
1173 HRESULT hr;
1174 if (pFileStatus)
1175 {
1176 hr = pFileEvent->COMGETTER(Status)(pFileStatus);
1177 ComAssertComRC(hr);
1178 }
1179
1180 ComPtr<IVirtualBoxErrorInfo> errorInfo;
1181 hr = pFileEvent->COMGETTER(Error)(errorInfo.asOutParam());
1182 ComAssertComRC(hr);
1183
1184 LONG lGuestRc;
1185 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
1186 ComAssertComRC(hr);
1187
1188 LogFlowThisFunc(("resultDetail=%RI32 (%Rrc)\n",
1189 lGuestRc, lGuestRc));
1190
1191 if (RT_FAILURE((int)lGuestRc))
1192 vrc = VERR_GSTCTL_GUEST_ERROR;
1193
1194 if (prcGuest)
1195 *prcGuest = (int)lGuestRc;
1196 }
1197 /* waitForEvent may also return VERR_GSTCTL_GUEST_ERROR like we do above, so make prcGuest is set. */
1198 /** @todo r=bird: Andy, you seem to have forgotten this scenario. Showed up occasionally when
1199 * using the wrong password with a copyto command in a debug build on windows, error info
1200 * contained "Unknown Status -858993460 (0xcccccccc)". As you know windows fills the stack frames
1201 * with 0xcccccccc in debug builds to highlight use of uninitialized data, so that's what happened
1202 * here. It's actually good you didn't initialize lGuest, as it would be heck to find otherwise.
1203 *
1204 * I'm still not very impressed with the error managment or the usuefullness of the documentation
1205 * in this code, though the latter is getting better! */
1206 else if (vrc == VERR_GSTCTL_GUEST_ERROR && prcGuest)
1207 *prcGuest = pEvent->GuestResult();
1208 Assert(vrc != VERR_GSTCTL_GUEST_ERROR || !prcGuest || *prcGuest != (int)0xcccccccc);
1209
1210 return vrc;
1211}
1212
1213int GuestFile::i_waitForWrite(GuestWaitEvent *pEvent,
1214 uint32_t uTimeoutMS, uint32_t *pcbWritten)
1215{
1216 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1217
1218 VBoxEventType_T evtType;
1219 ComPtr<IEvent> pIEvent;
1220 int vrc = waitForEvent(pEvent, uTimeoutMS,
1221 &evtType, pIEvent.asOutParam());
1222 if (RT_SUCCESS(vrc))
1223 {
1224 if (evtType == VBoxEventType_OnGuestFileWrite)
1225 {
1226 if (pcbWritten)
1227 {
1228 ComPtr<IGuestFileWriteEvent> pFileEvent = pIEvent;
1229 Assert(!pFileEvent.isNull());
1230
1231 HRESULT hr = pFileEvent->COMGETTER(Processed)((ULONG*)pcbWritten);
1232 ComAssertComRC(hr);
1233 }
1234 }
1235 else
1236 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1237 }
1238
1239 return vrc;
1240}
1241
1242int GuestFile::i_writeData(uint32_t uTimeoutMS, const void *pvData, uint32_t cbData,
1243 uint32_t *pcbWritten)
1244{
1245 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1246 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1247
1248 LogFlowThisFunc(("uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
1249 uTimeoutMS, pvData, cbData));
1250
1251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1252
1253 int vrc;
1254
1255 GuestWaitEvent *pEvent = NULL;
1256 GuestEventTypes eventTypes;
1257 try
1258 {
1259 eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
1260 eventTypes.push_back(VBoxEventType_OnGuestFileWrite);
1261
1262 vrc = registerWaitEvent(eventTypes, &pEvent);
1263 }
1264 catch (std::bad_alloc &)
1265 {
1266 vrc = VERR_NO_MEMORY;
1267 }
1268
1269 if (RT_FAILURE(vrc))
1270 return vrc;
1271
1272 /* Prepare HGCM call. */
1273 VBOXHGCMSVCPARM paParms[8];
1274 int i = 0;
1275 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1276 HGCMSvcSetU32(&paParms[i++], mObjectID /* File handle */);
1277 HGCMSvcSetU32(&paParms[i++], cbData /* Size (in bytes) to write */);
1278 HGCMSvcSetPv (&paParms[i++], unconst(pvData), cbData);
1279
1280 alock.release(); /* Drop write lock before sending. */
1281
1282 vrc = sendMessage(HOST_MSG_FILE_WRITE, i, paParms);
1283 if (RT_SUCCESS(vrc))
1284 {
1285 uint32_t cbWritten = 0;
1286 vrc = i_waitForWrite(pEvent, uTimeoutMS, &cbWritten);
1287 if (RT_SUCCESS(vrc))
1288 {
1289 LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
1290 if (pcbWritten)
1291 *pcbWritten = cbWritten;
1292 }
1293 else if (pEvent->HasGuestError()) /* Return guest rc if available. */
1294 {
1295 vrc = pEvent->GetGuestError();
1296 }
1297 }
1298
1299 unregisterWaitEvent(pEvent);
1300
1301 LogFlowFuncLeaveRC(vrc);
1302 return vrc;
1303}
1304
1305int GuestFile::i_writeDataAt(uint64_t uOffset, uint32_t uTimeoutMS,
1306 const void *pvData, uint32_t cbData, uint32_t *pcbWritten)
1307{
1308 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1309 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1310
1311 LogFlowThisFunc(("uOffset=%RU64, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
1312 uOffset, uTimeoutMS, pvData, cbData));
1313
1314 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1315
1316 int vrc;
1317
1318 GuestWaitEvent *pEvent = NULL;
1319 GuestEventTypes eventTypes;
1320 try
1321 {
1322 eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
1323 eventTypes.push_back(VBoxEventType_OnGuestFileWrite);
1324
1325 vrc = registerWaitEvent(eventTypes, &pEvent);
1326 }
1327 catch (std::bad_alloc &)
1328 {
1329 vrc = VERR_NO_MEMORY;
1330 }
1331
1332 if (RT_FAILURE(vrc))
1333 return vrc;
1334
1335 /* Prepare HGCM call. */
1336 VBOXHGCMSVCPARM paParms[8];
1337 int i = 0;
1338 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1339 HGCMSvcSetU32(&paParms[i++], mObjectID /* File handle */);
1340 HGCMSvcSetU64(&paParms[i++], uOffset /* Offset where to starting writing */);
1341 HGCMSvcSetU32(&paParms[i++], cbData /* Size (in bytes) to write */);
1342 HGCMSvcSetPv (&paParms[i++], unconst(pvData), cbData);
1343
1344 alock.release(); /* Drop write lock before sending. */
1345
1346 vrc = sendMessage(HOST_MSG_FILE_WRITE_AT, i, paParms);
1347 if (RT_SUCCESS(vrc))
1348 {
1349 uint32_t cbWritten = 0;
1350 vrc = i_waitForWrite(pEvent, uTimeoutMS, &cbWritten);
1351 if (RT_SUCCESS(vrc))
1352 {
1353 LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
1354 if (pcbWritten)
1355 *pcbWritten = cbWritten;
1356 }
1357 else if (pEvent->HasGuestError()) /* Return guest rc if available. */
1358 {
1359 vrc = pEvent->GetGuestError();
1360 }
1361 }
1362
1363 unregisterWaitEvent(pEvent);
1364
1365 LogFlowFuncLeaveRC(vrc);
1366 return vrc;
1367}
1368
1369// Wrapped IGuestFile methods
1370/////////////////////////////////////////////////////////////////////////////
1371HRESULT GuestFile::close()
1372{
1373 AutoCaller autoCaller(this);
1374 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1375
1376 LogFlowThisFuncEnter();
1377
1378 /* Close file on guest. */
1379 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1380 int vrc = i_closeFile(&rcGuest);
1381 /* On failure don't return here, instead do all the cleanup
1382 * work first and then return an error. */
1383
1384 AssertPtr(mSession);
1385 int vrc2 = mSession->i_fileUnregister(this);
1386 if (RT_SUCCESS(vrc))
1387 vrc = vrc2;
1388
1389 if (RT_FAILURE(vrc))
1390 {
1391 if (vrc == VERR_GSTCTL_GUEST_ERROR)
1392 return setErrorExternal(this, tr("Closing guest file failed"),
1393 GuestErrorInfo(GuestErrorInfo::Type_File, rcGuest, mData.mOpenInfo.mFilename.c_str()));
1394 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Closing guest file \"%s\" failed with %Rrc\n"),
1395 mData.mOpenInfo.mFilename.c_str(), vrc);
1396 }
1397
1398 LogFlowThisFunc(("Returning S_OK / vrc=%Rrc\n", vrc));
1399 return S_OK;
1400}
1401
1402HRESULT GuestFile::queryInfo(ComPtr<IFsObjInfo> &aObjInfo)
1403{
1404 AutoCaller autoCaller(this);
1405 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1406
1407 LogFlowThisFuncEnter();
1408
1409 HRESULT hr = S_OK;
1410
1411 GuestFsObjData fsObjData;
1412 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1413 int vrc = i_queryInfo(fsObjData, &rcGuest);
1414 if (RT_SUCCESS(vrc))
1415 {
1416 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
1417 hr = ptrFsObjInfo.createObject();
1418 if (SUCCEEDED(hr))
1419 {
1420 vrc = ptrFsObjInfo->init(fsObjData);
1421 if (RT_SUCCESS(vrc))
1422 hr = ptrFsObjInfo.queryInterfaceTo(aObjInfo.asOutParam());
1423 else
1424 hr = setErrorVrc(vrc,
1425 tr("Initialization of guest file object for \"%s\" failed: %Rrc"),
1426 mData.mOpenInfo.mFilename.c_str(), vrc);
1427 }
1428 }
1429 else
1430 {
1431 if (GuestProcess::i_isGuestError(vrc))
1432 hr = setErrorExternal(this, tr("Querying guest file information failed"),
1433 GuestErrorInfo(GuestErrorInfo::Type_ToolStat, rcGuest, mData.mOpenInfo.mFilename.c_str()));
1434 else
1435 hr = setErrorVrc(vrc,
1436 tr("Querying guest file information for \"%s\" failed: %Rrc"), mData.mOpenInfo.mFilename.c_str(), vrc);
1437 }
1438
1439 LogFlowFuncLeaveRC(vrc);
1440 return hr;
1441}
1442
1443HRESULT GuestFile::querySize(LONG64 *aSize)
1444{
1445 AutoCaller autoCaller(this);
1446 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1447
1448 LogFlowThisFuncEnter();
1449
1450 HRESULT hr = S_OK;
1451
1452 GuestFsObjData fsObjData;
1453 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1454 int vrc = i_queryInfo(fsObjData, &rcGuest);
1455 if (RT_SUCCESS(vrc))
1456 {
1457 *aSize = fsObjData.mObjectSize;
1458 }
1459 else
1460 {
1461 if (GuestProcess::i_isGuestError(vrc))
1462 hr = setErrorExternal(this, tr("Querying guest file size failed"),
1463 GuestErrorInfo(GuestErrorInfo::Type_ToolStat, rcGuest, mData.mOpenInfo.mFilename.c_str()));
1464 else
1465 hr = setErrorVrc(vrc, tr("Querying guest file size for \"%s\" failed: %Rrc"), mData.mOpenInfo.mFilename.c_str(), vrc);
1466 }
1467
1468 LogFlowFuncLeaveRC(vrc);
1469 return hr;
1470}
1471
1472HRESULT GuestFile::read(ULONG aToRead, ULONG aTimeoutMS, std::vector<BYTE> &aData)
1473{
1474 AutoCaller autoCaller(this);
1475 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1476
1477 if (aToRead == 0)
1478 return setError(E_INVALIDARG, tr("The size to read is zero"));
1479
1480 LogFlowThisFuncEnter();
1481
1482 /* Cap the read at 1MiB because that's all the guest will return anyway. */
1483 if (aToRead > _1M)
1484 aToRead = _1M;
1485
1486 aData.resize(aToRead);
1487
1488 HRESULT hr = S_OK;
1489
1490 uint32_t cbRead;
1491 int vrc = i_readData(aToRead, aTimeoutMS,
1492 &aData.front(), aToRead, &cbRead);
1493
1494 if (RT_SUCCESS(vrc))
1495 {
1496 if (aData.size() != cbRead)
1497 aData.resize(cbRead);
1498 }
1499 else
1500 {
1501 aData.resize(0);
1502
1503 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading from file \"%s\" failed: %Rrc"),
1504 mData.mOpenInfo.mFilename.c_str(), vrc);
1505 }
1506
1507 LogFlowFuncLeaveRC(vrc);
1508 return hr;
1509}
1510
1511HRESULT GuestFile::readAt(LONG64 aOffset, ULONG aToRead, ULONG aTimeoutMS, std::vector<BYTE> &aData)
1512{
1513 AutoCaller autoCaller(this);
1514 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1515
1516 if (aToRead == 0)
1517 return setError(E_INVALIDARG, tr("The size to read for guest file \"%s\" is zero"), mData.mOpenInfo.mFilename.c_str());
1518
1519 LogFlowThisFuncEnter();
1520
1521 /* Cap the read at 1MiB because that's all the guest will return anyway. */
1522 if (aToRead > _1M)
1523 aToRead = _1M;
1524
1525 aData.resize(aToRead);
1526
1527 HRESULT hr = S_OK;
1528
1529 size_t cbRead;
1530 int vrc = i_readDataAt(aOffset, aToRead, aTimeoutMS,
1531 &aData.front(), aToRead, &cbRead);
1532 if (RT_SUCCESS(vrc))
1533 {
1534 if (aData.size() != cbRead)
1535 aData.resize(cbRead);
1536 }
1537 else
1538 {
1539 aData.resize(0);
1540
1541 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading from file \"%s\" (at offset %RU64) failed: %Rrc"),
1542 mData.mOpenInfo.mFilename.c_str(), aOffset, vrc);
1543 }
1544
1545 LogFlowFuncLeaveRC(vrc);
1546 return hr;
1547}
1548
1549HRESULT GuestFile::seek(LONG64 aOffset, FileSeekOrigin_T aWhence, LONG64 *aNewOffset)
1550{
1551 AutoCaller autoCaller(this);
1552 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1553
1554 HRESULT hr = S_OK;
1555
1556 GUEST_FILE_SEEKTYPE eSeekType;
1557 switch (aWhence)
1558 {
1559 case FileSeekOrigin_Begin:
1560 eSeekType = GUEST_FILE_SEEKTYPE_BEGIN;
1561 break;
1562
1563 case FileSeekOrigin_Current:
1564 eSeekType = GUEST_FILE_SEEKTYPE_CURRENT;
1565 break;
1566
1567 case FileSeekOrigin_End:
1568 eSeekType = GUEST_FILE_SEEKTYPE_END;
1569 break;
1570
1571 default:
1572 return setError(E_INVALIDARG, tr("Invalid seek type for guest file \"%s\" specified"),
1573 mData.mOpenInfo.mFilename.c_str());
1574 }
1575
1576 LogFlowThisFuncEnter();
1577
1578 uint64_t uNewOffset;
1579 int vrc = i_seekAt(aOffset, eSeekType,
1580 30 * 1000 /* 30s timeout */, &uNewOffset);
1581 if (RT_SUCCESS(vrc))
1582 *aNewOffset = RT_MIN(uNewOffset, (uint64_t)INT64_MAX);
1583 else
1584 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Seeking file \"%s\" (to offset %RI64) failed: %Rrc"),
1585 mData.mOpenInfo.mFilename.c_str(), aOffset, vrc);
1586
1587 LogFlowFuncLeaveRC(vrc);
1588 return hr;
1589}
1590
1591HRESULT GuestFile::setACL(const com::Utf8Str &aAcl, ULONG aMode)
1592{
1593 RT_NOREF(aAcl, aMode);
1594 ReturnComNotImplemented();
1595}
1596
1597HRESULT GuestFile::setSize(LONG64 aSize)
1598{
1599 LogFlowThisFuncEnter();
1600
1601 /*
1602 * Validate.
1603 */
1604 if (aSize < 0)
1605 return setError(E_INVALIDARG, tr("The size (%RI64) for guest file \"%s\" cannot be a negative value"),
1606 aSize, mData.mOpenInfo.mFilename.c_str());
1607
1608 /*
1609 * Register event callbacks.
1610 */
1611 int vrc;
1612 GuestWaitEvent *pWaitEvent = NULL;
1613 GuestEventTypes lstEventTypes;
1614 try
1615 {
1616 lstEventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
1617 lstEventTypes.push_back(VBoxEventType_OnGuestFileSizeChanged);
1618 }
1619 catch (std::bad_alloc &)
1620 {
1621 return E_OUTOFMEMORY;
1622 }
1623
1624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1625
1626 vrc = registerWaitEvent(lstEventTypes, &pWaitEvent);
1627 if (RT_SUCCESS(vrc))
1628 {
1629 /*
1630 * Send of the HGCM message.
1631 */
1632 VBOXHGCMSVCPARM aParms[3];
1633 HGCMSvcSetU32(&aParms[0], pWaitEvent->ContextID());
1634 HGCMSvcSetU32(&aParms[1], mObjectID /* File handle */);
1635 HGCMSvcSetU64(&aParms[2], aSize);
1636
1637 alock.release(); /* Drop write lock before sending. */
1638
1639 vrc = sendMessage(HOST_MSG_FILE_SET_SIZE, RT_ELEMENTS(aParms), aParms);
1640 if (RT_SUCCESS(vrc))
1641 {
1642 /*
1643 * Wait for the event.
1644 */
1645 VBoxEventType_T enmEvtType;
1646 ComPtr<IEvent> pIEvent;
1647 vrc = waitForEvent(pWaitEvent, RT_MS_1MIN / 2, &enmEvtType, pIEvent.asOutParam());
1648 if (RT_SUCCESS(vrc))
1649 {
1650 if (enmEvtType == VBoxEventType_OnGuestFileSizeChanged)
1651 vrc = VINF_SUCCESS;
1652 else
1653 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1654 }
1655 if (RT_FAILURE(vrc) && pWaitEvent->HasGuestError()) /* Return guest rc if available. */
1656 vrc = pWaitEvent->GetGuestError();
1657 }
1658
1659 /*
1660 * Unregister the wait event and deal with error reporting if needed.
1661 */
1662 unregisterWaitEvent(pWaitEvent);
1663 }
1664 HRESULT hrc;
1665 if (RT_SUCCESS(vrc))
1666 hrc = S_OK;
1667 else
1668 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Setting the guest file size of \"%s\" to %RU64 (%#RX64) bytes failed: %Rrc"),
1669 mData.mOpenInfo.mFilename.c_str(), aSize, aSize, vrc);
1670 LogFlowFuncLeaveRC(vrc);
1671 return hrc;
1672}
1673
1674HRESULT GuestFile::write(const std::vector<BYTE> &aData, ULONG aTimeoutMS, ULONG *aWritten)
1675{
1676 AutoCaller autoCaller(this);
1677 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1678
1679 if (aData.size() == 0)
1680 return setError(E_INVALIDARG, tr("No data to write specified"), mData.mOpenInfo.mFilename.c_str());
1681
1682 LogFlowThisFuncEnter();
1683
1684 HRESULT hr = S_OK;
1685
1686 const uint32_t cbData = (uint32_t)aData.size();
1687 const void *pvData = (void *)&aData.front();
1688 int vrc = i_writeData(aTimeoutMS, pvData, cbData, (uint32_t*)aWritten);
1689 if (RT_FAILURE(vrc))
1690 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Writing %zu bytes to guest file \"%s\" failed: %Rrc"),
1691 aData.size(), mData.mOpenInfo.mFilename.c_str(), vrc);
1692
1693 LogFlowFuncLeaveRC(vrc);
1694 return hr;
1695}
1696
1697HRESULT GuestFile::writeAt(LONG64 aOffset, const std::vector<BYTE> &aData, ULONG aTimeoutMS, ULONG *aWritten)
1698{
1699 AutoCaller autoCaller(this);
1700 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1701
1702 if (aData.size() == 0)
1703 return setError(E_INVALIDARG, tr("No data to write at for guest file \"%s\" specified"), mData.mOpenInfo.mFilename.c_str());
1704
1705 LogFlowThisFuncEnter();
1706
1707 HRESULT hr = S_OK;
1708
1709 const uint32_t cbData = (uint32_t)aData.size();
1710 const void *pvData = (void *)&aData.front();
1711 int vrc = i_writeDataAt(aOffset, aTimeoutMS, pvData, cbData, (uint32_t*)aWritten);
1712 if (RT_FAILURE(vrc))
1713 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Writing %zu bytes to file \"%s\" (at offset %RU64) failed: %Rrc"),
1714 aData.size(), mData.mOpenInfo.mFilename.c_str(), aOffset, vrc);
1715
1716 LogFlowFuncLeaveRC(vrc);
1717 return hr;
1718}
1719
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