VirtualBox

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

Last change on this file since 79812 was 79302, checked in by vboxsync, 6 years ago

Main/GuestFileImpl.cpp: More doc updates wrt file offsets, reading and writing. bugref:9320

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.0 KB
Line 
1/* $Id: GuestFileImpl.cpp 79302 2019-06-24 10:45:10Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest file handling.
4 */
5
6/*
7 * Copyright (C) 2012-2019 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/* static */ const char *GuestFile::i_guestVrcToString(int rcGuest)
406{
407 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
408 switch (rcGuest)
409 {
410 case VERR_ACCESS_DENIED: return tr("Access denied");
411 case VERR_ALREADY_EXISTS: return tr("File already exists");
412 case VERR_FILE_NOT_FOUND: return tr("File not found");
413 case VERR_NET_HOST_NOT_FOUND: return tr("Host name not found");
414 case VERR_SHARING_VIOLATION: return tr("Sharing violation");
415 default: return RTErrGetDefine(rcGuest);
416 }
417}
418
419/**
420 * @todo r=bird: This is an absolutely cryptic way of reporting errors. You may convert
421 * this to a const char * returning function for explaining rcGuest and
422 * use that as part of a _proper_ error message. This alone extremely
423 * user unfriendly. E.g. which file is not found? One of the source files,
424 * a destination file, what are you referring to?!?
425 *
426 * I've addressed one of these that annoyed me, you can do the rest of them.
427 */
428/* static */ Utf8Str GuestFile::i_guestErrorToString(int rcGuest)
429{
430 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
431 return i_guestVrcToString(rcGuest);
432}
433
434int GuestFile::i_onFileNotify(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
435{
436 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
437 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
438
439 LogFlowThisFuncEnter();
440
441 if (pSvcCbData->mParms < 3)
442 return VERR_INVALID_PARAMETER;
443
444 int idx = 1; /* Current parameter index. */
445 CALLBACKDATA_FILE_NOTIFY dataCb;
446 /* pSvcCb->mpaParms[0] always contains the context ID. */
447 HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.uType);
448 HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.rc);
449
450 int rcGuest = (int)dataCb.rc; /* uint32_t vs. int. */
451
452 LogFlowThisFunc(("uType=%RU32, rcGuest=%Rrc\n", dataCb.uType, rcGuest));
453
454 if (RT_FAILURE(rcGuest))
455 {
456 int rc2 = i_setFileStatus(FileStatus_Error, rcGuest);
457 AssertRC(rc2);
458
459 /* Ignore rc, as the event to signal might not be there (anymore). */
460 signalWaitEventInternal(pCbCtx, rcGuest, NULL /* pPayload */);
461 return VINF_SUCCESS; /* Report to the guest. */
462 }
463
464 AssertMsg(mObjectID == VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCbCtx->uContextID),
465 ("File ID %RU32 does not match object ID %RU32\n", mObjectID,
466 VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCbCtx->uContextID)));
467
468 int rc = VERR_NOT_SUPPORTED; /* Play safe by default. */
469
470 switch (dataCb.uType)
471 {
472 case GUEST_FILE_NOTIFYTYPE_ERROR:
473 {
474 rc = i_setFileStatus(FileStatus_Error, rcGuest);
475 break;
476 }
477
478 case GUEST_FILE_NOTIFYTYPE_OPEN:
479 {
480 if (pSvcCbData->mParms == 4)
481 {
482 rc = HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.u.open.uHandle);
483 if (RT_FAILURE(rc))
484 break;
485
486 /* Set the process status. */
487 rc = i_setFileStatus(FileStatus_Open, rcGuest);
488 }
489 break;
490 }
491
492 case GUEST_FILE_NOTIFYTYPE_CLOSE:
493 {
494 rc = i_setFileStatus(FileStatus_Closed, rcGuest);
495 break;
496 }
497
498 case GUEST_FILE_NOTIFYTYPE_READ:
499 {
500 if (pSvcCbData->mParms == 4)
501 {
502 rc = HGCMSvcGetPv(&pSvcCbData->mpaParms[idx++], &dataCb.u.read.pvData,
503 &dataCb.u.read.cbData);
504 if (RT_FAILURE(rc))
505 break;
506
507 const uint32_t cbRead = dataCb.u.read.cbData;
508
509 Log3ThisFunc(("cbRead=%RU32\n", cbRead));
510
511 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
512
513 mData.mOffCurrent += cbRead; /* Bogus for readAt, which is why we've got GUEST_FILE_NOTIFYTYPE_READ_OFFSET. */
514
515 alock.release();
516
517 com::SafeArray<BYTE> data((size_t)cbRead);
518 data.initFrom((BYTE*)dataCb.u.read.pvData, cbRead);
519
520 fireGuestFileReadEvent(mEventSource, mSession, this, mData.mOffCurrent,
521 cbRead, ComSafeArrayAsInParam(data));
522 }
523 break;
524 }
525
526 case GUEST_FILE_NOTIFYTYPE_READ_OFFSET:
527 {
528 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mParms == 5, ("mParms=%u\n", pSvcCbData->mParms),
529 rc = VERR_WRONG_PARAMETER_COUNT);
530 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mpaParms[idx].type == VBOX_HGCM_SVC_PARM_PTR,
531 ("type=%u\n", pSvcCbData->mpaParms[idx].type),
532 rc = VERR_WRONG_PARAMETER_TYPE);
533 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mpaParms[idx + 1].type == VBOX_HGCM_SVC_PARM_64BIT,
534 ("type=%u\n", pSvcCbData->mpaParms[idx].type),
535 rc = VERR_WRONG_PARAMETER_TYPE);
536 BYTE const * const pbData = (BYTE const *)pSvcCbData->mpaParms[idx].u.pointer.addr;
537 uint32_t const cbRead = pSvcCbData->mpaParms[idx].u.pointer.size;
538 int64_t offNew = (int64_t)pSvcCbData->mpaParms[idx + 1].u.uint64;
539 Log3ThisFunc(("cbRead=%RU32 offNew=%RI64 (%#RX64)\n", cbRead, offNew, offNew));
540
541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
542 if (offNew < 0) /* non-seekable */
543 offNew = mData.mOffCurrent + cbRead;
544 mData.mOffCurrent = offNew;
545 alock.release();
546
547 try
548 {
549 com::SafeArray<BYTE> data((size_t)cbRead);
550 data.initFrom(pbData, cbRead);
551 fireGuestFileReadEvent(mEventSource, mSession, this, offNew, cbRead, ComSafeArrayAsInParam(data));
552 rc = VINF_SUCCESS;
553 }
554 catch (std::bad_alloc &)
555 {
556 rc = VERR_NO_MEMORY;
557 }
558 break;
559 }
560
561 case GUEST_FILE_NOTIFYTYPE_WRITE:
562 {
563 if (pSvcCbData->mParms == 4)
564 {
565 rc = HGCMSvcGetU32(&pSvcCbData->mpaParms[idx++], &dataCb.u.write.cbWritten);
566 if (RT_FAILURE(rc))
567 break;
568
569 const uint32_t cbWritten = dataCb.u.write.cbWritten;
570
571 Log3ThisFunc(("cbWritten=%RU32\n", cbWritten));
572
573 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
574
575 mData.mOffCurrent += cbWritten; /* Bogus for writeAt and append mode, thus GUEST_FILE_NOTIFYTYPE_WRITE_OFFSET. */
576
577 alock.release();
578
579 fireGuestFileWriteEvent(mEventSource, mSession, this, mData.mOffCurrent, cbWritten);
580 }
581 break;
582 }
583
584 case GUEST_FILE_NOTIFYTYPE_WRITE_OFFSET:
585 {
586 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mParms == 5, ("mParms=%u\n", pSvcCbData->mParms),
587 rc = VERR_WRONG_PARAMETER_COUNT);
588 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mpaParms[idx].type == VBOX_HGCM_SVC_PARM_32BIT,
589 ("type=%u\n", pSvcCbData->mpaParms[idx].type),
590 rc = VERR_WRONG_PARAMETER_TYPE);
591 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mpaParms[idx + 1].type == VBOX_HGCM_SVC_PARM_64BIT,
592 ("type=%u\n", pSvcCbData->mpaParms[idx].type),
593 rc = VERR_WRONG_PARAMETER_TYPE);
594 uint32_t const cbWritten = pSvcCbData->mpaParms[idx].u.uint32;
595 int64_t offNew = (int64_t)pSvcCbData->mpaParms[idx + 1].u.uint64;
596 Log3ThisFunc(("cbWritten=%RU32 offNew=%RI64 (%#RX64)\n", cbWritten, offNew, offNew));
597
598 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
599 if (offNew < 0) /* non-seekable */
600 offNew = mData.mOffCurrent + cbWritten;
601 mData.mOffCurrent = offNew;
602 alock.release();
603
604 try
605 {
606 fireGuestFileWriteEvent(mEventSource, mSession, this, offNew, cbWritten);
607 rc = VINF_SUCCESS;
608 }
609 catch (std::bad_alloc &)
610 {
611 rc = VERR_NO_MEMORY;
612 }
613 break;
614 }
615
616 case GUEST_FILE_NOTIFYTYPE_SEEK:
617 {
618 if (pSvcCbData->mParms == 4)
619 {
620 rc = HGCMSvcGetU64(&pSvcCbData->mpaParms[idx++], &dataCb.u.seek.uOffActual);
621 if (RT_FAILURE(rc))
622 break;
623
624 Log3ThisFunc(("uOffActual=%RU64\n", dataCb.u.seek.uOffActual));
625
626 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
627
628 mData.mOffCurrent = dataCb.u.seek.uOffActual;
629
630 alock.release();
631
632 fireGuestFileOffsetChangedEvent(mEventSource, mSession, this, dataCb.u.seek.uOffActual, 0 /* Processed */);
633 }
634 break;
635 }
636
637 case GUEST_FILE_NOTIFYTYPE_TELL:
638 /* We don't issue any HOST_MSG_FILE_TELL, so we shouldn't get these notifications! */
639 AssertFailed();
640 break;
641
642 case GUEST_FILE_NOTIFYTYPE_SET_SIZE:
643 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mParms == 4, ("mParms=%u\n", pSvcCbData->mParms),
644 rc = VERR_WRONG_PARAMETER_COUNT);
645 ASSERT_GUEST_MSG_STMT_BREAK(pSvcCbData->mpaParms[idx].type == VBOX_HGCM_SVC_PARM_64BIT,
646 ("type=%u\n", pSvcCbData->mpaParms[idx].type),
647 rc = VERR_WRONG_PARAMETER_TYPE);
648 dataCb.u.SetSize.cbSize = pSvcCbData->mpaParms[idx].u.uint64;
649 Log3ThisFunc(("cbSize=%RU64\n", dataCb.u.SetSize.cbSize));
650
651 fireGuestFileSizeChangedEvent(mEventSource, mSession, this, dataCb.u.SetSize.cbSize);
652 rc = VINF_SUCCESS;
653 break;
654
655 default:
656 break;
657 }
658
659 if (RT_SUCCESS(rc))
660 {
661 GuestWaitEventPayload payload(dataCb.uType, &dataCb, sizeof(dataCb));
662
663 /* Ignore rc, as the event to signal might not be there (anymore). */
664 signalWaitEventInternal(pCbCtx, rcGuest, &payload);
665 }
666
667 LogFlowThisFunc(("uType=%RU32, rcGuest=%Rrc, rc=%Rrc\n", dataCb.uType, rcGuest, rc));
668 return rc;
669}
670
671int GuestFile::i_onGuestDisconnected(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
672{
673 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
674 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
675
676 int vrc = i_setFileStatus(FileStatus_Down, VINF_SUCCESS);
677
678 LogFlowFuncLeaveRC(vrc);
679 return vrc;
680}
681
682/**
683 * @copydoc GuestObject::i_onUnregister
684 */
685int GuestFile::i_onUnregister(void)
686{
687 LogFlowThisFuncEnter();
688
689 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
690
691 int vrc = VINF_SUCCESS;
692
693 /*
694 * Note: The event source stuff holds references to this object,
695 * so make sure that this is cleaned up *before* calling uninit().
696 */
697 if (!mEventSource.isNull())
698 {
699 mEventSource->UnregisterListener(mLocalListener);
700
701 mLocalListener.setNull();
702 unconst(mEventSource).setNull();
703 }
704
705 LogFlowFuncLeaveRC(vrc);
706 return vrc;
707}
708
709/**
710 * @copydoc GuestObject::i_onSessionStatusChange
711 */
712int GuestFile::i_onSessionStatusChange(GuestSessionStatus_T enmSessionStatus)
713{
714 LogFlowThisFuncEnter();
715
716 int vrc = VINF_SUCCESS;
717
718 /* If the session now is in a terminated state, set the file status
719 * to "down", as there is not much else we can do now. */
720 if (GuestSession::i_isTerminated(enmSessionStatus))
721 vrc = i_setFileStatus(FileStatus_Down, 0 /* fileRc, ignored */);
722
723 LogFlowFuncLeaveRC(vrc);
724 return vrc;
725}
726
727int GuestFile::i_openFile(uint32_t uTimeoutMS, int *prcGuest)
728{
729 AssertReturn(mData.mOpenInfo.mFilename.isNotEmpty(), VERR_INVALID_PARAMETER);
730
731 LogFlowThisFuncEnter();
732
733 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
734
735 LogFlowThisFunc(("strFile=%s, enmAccessMode=%d, enmOpenAction=%d, uCreationMode=%o, mfOpenEx=%#x\n",
736 mData.mOpenInfo.mFilename.c_str(), mData.mOpenInfo.mAccessMode, mData.mOpenInfo.mOpenAction,
737 mData.mOpenInfo.mCreationMode, mData.mOpenInfo.mfOpenEx));
738
739 /* Validate and translate open action. */
740 const char *pszOpenAction = NULL;
741 switch (mData.mOpenInfo.mOpenAction)
742 {
743 case FileOpenAction_OpenExisting: pszOpenAction = "oe"; break;
744 case FileOpenAction_OpenOrCreate: pszOpenAction = "oc"; break;
745 case FileOpenAction_CreateNew: pszOpenAction = "ce"; break;
746 case FileOpenAction_CreateOrReplace: pszOpenAction = "ca"; break;
747 case FileOpenAction_OpenExistingTruncated: pszOpenAction = "ot"; break;
748 case FileOpenAction_AppendOrCreate:
749 pszOpenAction = "oa"; /** @todo get rid of this one and implement AppendOnly/AppendRead. */
750 break;
751 default:
752 return VERR_INVALID_PARAMETER;
753 }
754
755 /* Validate and translate access mode. */
756 const char *pszAccessMode = NULL;
757 switch (mData.mOpenInfo.mAccessMode)
758 {
759 case FileAccessMode_ReadOnly: pszAccessMode = "r"; break;
760 case FileAccessMode_WriteOnly: pszAccessMode = "w"; break;
761 case FileAccessMode_ReadWrite: pszAccessMode = "r+"; break;
762 case FileAccessMode_AppendOnly: pszAccessMode = "a"; break;
763 case FileAccessMode_AppendRead: pszAccessMode = "a+"; break;
764 default: return VERR_INVALID_PARAMETER;
765 }
766
767 /* Validate and translate sharing mode. */
768 const char *pszSharingMode = NULL;
769 switch (mData.mOpenInfo.mSharingMode)
770 {
771 case FileSharingMode_All: pszSharingMode = ""; break;
772 case FileSharingMode_Read: RT_FALL_THRU();
773 case FileSharingMode_Write: RT_FALL_THRU();
774 case FileSharingMode_ReadWrite: RT_FALL_THRU();
775 case FileSharingMode_Delete: RT_FALL_THRU();
776 case FileSharingMode_ReadDelete: RT_FALL_THRU();
777 case FileSharingMode_WriteDelete: return VERR_NOT_IMPLEMENTED;
778 default: return VERR_INVALID_PARAMETER;
779 }
780
781 int vrc;
782
783 GuestWaitEvent *pEvent = NULL;
784 GuestEventTypes eventTypes;
785 try
786 {
787 eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
788
789 vrc = registerWaitEvent(eventTypes, &pEvent);
790 }
791 catch (std::bad_alloc &)
792 {
793 vrc = VERR_NO_MEMORY;
794 }
795
796 if (RT_FAILURE(vrc))
797 return vrc;
798
799 /* Prepare HGCM call. */
800 VBOXHGCMSVCPARM paParms[8];
801 int i = 0;
802 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
803 HGCMSvcSetPv(&paParms[i++], (void*)mData.mOpenInfo.mFilename.c_str(),
804 (ULONG)mData.mOpenInfo.mFilename.length() + 1);
805 HGCMSvcSetStr(&paParms[i++], pszAccessMode);
806 HGCMSvcSetStr(&paParms[i++], pszOpenAction);
807 HGCMSvcSetStr(&paParms[i++], pszSharingMode);
808 HGCMSvcSetU32(&paParms[i++], mData.mOpenInfo.mCreationMode);
809 HGCMSvcSetU64(&paParms[i++], 0 /*unused offset*/);
810 /** @todo Next protocol version: add flags, replace strings, remove initial offset. */
811
812 alock.release(); /* Drop write lock before sending. */
813
814 vrc = sendMessage(HOST_MSG_FILE_OPEN, i, paParms);
815 if (RT_SUCCESS(vrc))
816 vrc = i_waitForStatusChange(pEvent, uTimeoutMS, NULL /* FileStatus */, prcGuest);
817
818 unregisterWaitEvent(pEvent);
819
820 LogFlowFuncLeaveRC(vrc);
821 return vrc;
822}
823
824int GuestFile::i_queryInfo(GuestFsObjData &objData, int *prcGuest)
825{
826 AssertPtr(mSession);
827 return mSession->i_fsQueryInfo(mData.mOpenInfo.mFilename, FALSE /* fFollowSymlinks */, objData, prcGuest);
828}
829
830int GuestFile::i_readData(uint32_t uSize, uint32_t uTimeoutMS,
831 void* pvData, uint32_t cbData, uint32_t* pcbRead)
832{
833 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
834 AssertReturn(cbData, VERR_INVALID_PARAMETER);
835
836 LogFlowThisFunc(("uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
837 uSize, uTimeoutMS, pvData, cbData));
838
839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
840
841 int vrc;
842
843 GuestWaitEvent *pEvent = NULL;
844 GuestEventTypes eventTypes;
845 try
846 {
847 eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
848 eventTypes.push_back(VBoxEventType_OnGuestFileRead);
849
850 vrc = registerWaitEvent(eventTypes, &pEvent);
851 }
852 catch (std::bad_alloc &)
853 {
854 vrc = VERR_NO_MEMORY;
855 }
856
857 if (RT_FAILURE(vrc))
858 return vrc;
859
860 /* Prepare HGCM call. */
861 VBOXHGCMSVCPARM paParms[4];
862 int i = 0;
863 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
864 HGCMSvcSetU32(&paParms[i++], mObjectID /* File handle */);
865 HGCMSvcSetU32(&paParms[i++], uSize /* Size (in bytes) to read */);
866
867 alock.release(); /* Drop write lock before sending. */
868
869 vrc = sendMessage(HOST_MSG_FILE_READ, i, paParms);
870 if (RT_SUCCESS(vrc))
871 {
872 uint32_t cbRead = 0;
873 vrc = i_waitForRead(pEvent, uTimeoutMS, pvData, cbData, &cbRead);
874 if (RT_SUCCESS(vrc))
875 {
876 LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
877 if (pcbRead)
878 *pcbRead = cbRead;
879 }
880 else if (pEvent->HasGuestError()) /* Return guest rc if available. */
881 {
882 vrc = pEvent->GetGuestError();
883 }
884 }
885
886 unregisterWaitEvent(pEvent);
887
888 LogFlowFuncLeaveRC(vrc);
889 return vrc;
890}
891
892int GuestFile::i_readDataAt(uint64_t uOffset, uint32_t uSize, uint32_t uTimeoutMS,
893 void* pvData, size_t cbData, size_t* pcbRead)
894{
895 LogFlowThisFunc(("uOffset=%RU64, uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
896 uOffset, uSize, uTimeoutMS, pvData, cbData));
897
898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
899
900 int vrc;
901
902 GuestWaitEvent *pEvent = NULL;
903 GuestEventTypes eventTypes;
904 try
905 {
906 eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
907 eventTypes.push_back(VBoxEventType_OnGuestFileRead);
908
909 vrc = registerWaitEvent(eventTypes, &pEvent);
910 }
911 catch (std::bad_alloc &)
912 {
913 vrc = VERR_NO_MEMORY;
914 }
915
916 if (RT_FAILURE(vrc))
917 return vrc;
918
919 /* Prepare HGCM call. */
920 VBOXHGCMSVCPARM paParms[4];
921 int i = 0;
922 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
923 HGCMSvcSetU32(&paParms[i++], mObjectID /* File handle */);
924 HGCMSvcSetU64(&paParms[i++], uOffset /* Offset (in bytes) to start reading */);
925 HGCMSvcSetU32(&paParms[i++], uSize /* Size (in bytes) to read */);
926
927 alock.release(); /* Drop write lock before sending. */
928
929 vrc = sendMessage(HOST_MSG_FILE_READ_AT, i, paParms);
930 if (RT_SUCCESS(vrc))
931 {
932 uint32_t cbRead = 0;
933 vrc = i_waitForRead(pEvent, uTimeoutMS, pvData, cbData, &cbRead);
934 if (RT_SUCCESS(vrc))
935 {
936 LogFlowThisFunc(("cbRead=%RU32\n", cbRead));
937
938 if (pcbRead)
939 *pcbRead = cbRead;
940 }
941 else if (pEvent->HasGuestError()) /* Return guest rc if available. */
942 {
943 vrc = pEvent->GetGuestError();
944 }
945 }
946
947 unregisterWaitEvent(pEvent);
948
949 LogFlowFuncLeaveRC(vrc);
950 return vrc;
951}
952
953int GuestFile::i_seekAt(int64_t iOffset, GUEST_FILE_SEEKTYPE eSeekType,
954 uint32_t uTimeoutMS, uint64_t *puOffset)
955{
956 LogFlowThisFunc(("iOffset=%RI64, uTimeoutMS=%RU32\n",
957 iOffset, uTimeoutMS));
958
959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
960
961 int vrc;
962
963 GuestWaitEvent *pEvent = NULL;
964 GuestEventTypes eventTypes;
965 try
966 {
967 eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
968 eventTypes.push_back(VBoxEventType_OnGuestFileOffsetChanged);
969
970 vrc = registerWaitEvent(eventTypes, &pEvent);
971 }
972 catch (std::bad_alloc &)
973 {
974 vrc = VERR_NO_MEMORY;
975 }
976
977 if (RT_FAILURE(vrc))
978 return vrc;
979
980 /* Prepare HGCM call. */
981 VBOXHGCMSVCPARM paParms[4];
982 int i = 0;
983 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
984 HGCMSvcSetU32(&paParms[i++], mObjectID /* File handle */);
985 HGCMSvcSetU32(&paParms[i++], eSeekType /* Seek method */);
986 /** @todo uint64_t vs. int64_t! */
987 HGCMSvcSetU64(&paParms[i++], (uint64_t)iOffset /* Offset (in bytes) to start reading */);
988
989 alock.release(); /* Drop write lock before sending. */
990
991 vrc = sendMessage(HOST_MSG_FILE_SEEK, i, paParms);
992 if (RT_SUCCESS(vrc))
993 {
994 uint64_t uOffset;
995 vrc = i_waitForOffsetChange(pEvent, uTimeoutMS, &uOffset);
996 if (RT_SUCCESS(vrc))
997 {
998 LogFlowThisFunc(("uOffset=%RU64\n", uOffset));
999
1000 if (puOffset)
1001 *puOffset = uOffset;
1002 }
1003 else if (pEvent->HasGuestError()) /* Return guest rc if available. */
1004 {
1005 vrc = pEvent->GetGuestError();
1006 }
1007 }
1008
1009 unregisterWaitEvent(pEvent);
1010
1011 LogFlowFuncLeaveRC(vrc);
1012 return vrc;
1013}
1014
1015/* static */
1016HRESULT GuestFile::i_setErrorExternal(VirtualBoxBase *pInterface, int rcGuest)
1017{
1018 AssertPtr(pInterface);
1019 AssertMsg(RT_FAILURE(rcGuest), ("Guest rc does not indicate a failure when setting error\n"));
1020
1021 return pInterface->setError(VBOX_E_IPRT_ERROR, GuestFile::i_guestErrorToString(rcGuest).c_str());
1022}
1023
1024int GuestFile::i_setFileStatus(FileStatus_T fileStatus, int fileRc)
1025{
1026 LogFlowThisFuncEnter();
1027
1028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1029
1030 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, fileRc=%Rrc\n",
1031 mData.mStatus, fileStatus, fileRc));
1032
1033#ifdef VBOX_STRICT
1034 if (fileStatus == FileStatus_Error)
1035 {
1036 AssertMsg(RT_FAILURE(fileRc), ("Guest rc must be an error (%Rrc)\n", fileRc));
1037 }
1038 else
1039 AssertMsg(RT_SUCCESS(fileRc), ("Guest rc must not be an error (%Rrc)\n", fileRc));
1040#endif
1041
1042 if (mData.mStatus != fileStatus)
1043 {
1044 mData.mStatus = fileStatus;
1045 mData.mLastError = fileRc;
1046
1047 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
1048 HRESULT hr = errorInfo.createObject();
1049 ComAssertComRC(hr);
1050 if (RT_FAILURE(fileRc))
1051 {
1052 hr = errorInfo->initEx(VBOX_E_IPRT_ERROR, fileRc,
1053 COM_IIDOF(IGuestFile), getComponentName(),
1054 i_guestErrorToString(fileRc));
1055 ComAssertComRC(hr);
1056 }
1057
1058 alock.release(); /* Release lock before firing off event. */
1059
1060 fireGuestFileStateChangedEvent(mEventSource, mSession,
1061 this, fileStatus, errorInfo);
1062 }
1063
1064 return VINF_SUCCESS;
1065}
1066
1067int GuestFile::i_waitForOffsetChange(GuestWaitEvent *pEvent,
1068 uint32_t uTimeoutMS, uint64_t *puOffset)
1069{
1070 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1071
1072 VBoxEventType_T evtType;
1073 ComPtr<IEvent> pIEvent;
1074 int vrc = waitForEvent(pEvent, uTimeoutMS,
1075 &evtType, pIEvent.asOutParam());
1076 if (RT_SUCCESS(vrc))
1077 {
1078 if (evtType == VBoxEventType_OnGuestFileOffsetChanged)
1079 {
1080 if (puOffset)
1081 {
1082 ComPtr<IGuestFileOffsetChangedEvent> pFileEvent = pIEvent;
1083 Assert(!pFileEvent.isNull());
1084
1085 HRESULT hr = pFileEvent->COMGETTER(Offset)((LONG64*)puOffset);
1086 ComAssertComRC(hr);
1087 }
1088 }
1089 else
1090 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1091 }
1092
1093 return vrc;
1094}
1095
1096int GuestFile::i_waitForRead(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
1097 void *pvData, size_t cbData, uint32_t *pcbRead)
1098{
1099 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1100
1101 VBoxEventType_T evtType;
1102 ComPtr<IEvent> pIEvent;
1103 int vrc = waitForEvent(pEvent, uTimeoutMS,
1104 &evtType, pIEvent.asOutParam());
1105 if (RT_SUCCESS(vrc))
1106 {
1107 if (evtType == VBoxEventType_OnGuestFileRead)
1108 {
1109 vrc = VINF_SUCCESS;
1110
1111 ComPtr<IGuestFileReadEvent> pFileEvent = pIEvent;
1112 Assert(!pFileEvent.isNull());
1113
1114 if (pvData)
1115 {
1116 com::SafeArray <BYTE> data;
1117 HRESULT hrc1 = pFileEvent->COMGETTER(Data)(ComSafeArrayAsOutParam(data));
1118 ComAssertComRC(hrc1);
1119 const size_t cbRead = data.size();
1120 if (cbRead)
1121 {
1122 if (cbRead <= cbData)
1123 memcpy(pvData, data.raw(), cbRead);
1124 else
1125 vrc = VERR_BUFFER_OVERFLOW;
1126 }
1127 /* else: used to be VERR_NO_DATA, but that messes stuff up. */
1128
1129 if (pcbRead)
1130 {
1131 *pcbRead = (uint32_t)cbRead;
1132 Assert(*pcbRead == cbRead);
1133 }
1134 }
1135 else if (pcbRead)
1136 {
1137 *pcbRead = 0;
1138 HRESULT hrc2 = pFileEvent->COMGETTER(Processed)((ULONG *)pcbRead);
1139 ComAssertComRC(hrc2); NOREF(hrc2);
1140 }
1141 }
1142 else
1143 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1144 }
1145
1146 return vrc;
1147}
1148
1149/**
1150 * Undocumented, use with great care.
1151 *
1152 * @note Similar code in GuestProcess::i_waitForStatusChange() and
1153 * GuestSession::i_waitForStatusChange().
1154 */
1155int GuestFile::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t uTimeoutMS,
1156 FileStatus_T *pFileStatus, int *prcGuest)
1157{
1158 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1159 /* pFileStatus is optional. */
1160
1161 VBoxEventType_T evtType;
1162 ComPtr<IEvent> pIEvent;
1163 int vrc = waitForEvent(pEvent, uTimeoutMS,
1164 &evtType, pIEvent.asOutParam());
1165 if (RT_SUCCESS(vrc))
1166 {
1167 Assert(evtType == VBoxEventType_OnGuestFileStateChanged);
1168 ComPtr<IGuestFileStateChangedEvent> pFileEvent = pIEvent;
1169 Assert(!pFileEvent.isNull());
1170
1171 HRESULT hr;
1172 if (pFileStatus)
1173 {
1174 hr = pFileEvent->COMGETTER(Status)(pFileStatus);
1175 ComAssertComRC(hr);
1176 }
1177
1178 ComPtr<IVirtualBoxErrorInfo> errorInfo;
1179 hr = pFileEvent->COMGETTER(Error)(errorInfo.asOutParam());
1180 ComAssertComRC(hr);
1181
1182 LONG lGuestRc;
1183 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
1184 ComAssertComRC(hr);
1185
1186 LogFlowThisFunc(("resultDetail=%RI32 (%Rrc)\n",
1187 lGuestRc, lGuestRc));
1188
1189 if (RT_FAILURE((int)lGuestRc))
1190 vrc = VERR_GSTCTL_GUEST_ERROR;
1191
1192 if (prcGuest)
1193 *prcGuest = (int)lGuestRc;
1194 }
1195 /* waitForEvent may also return VERR_GSTCTL_GUEST_ERROR like we do above, so make prcGuest is set. */
1196 /** @todo r=bird: Andy, you seem to have forgotten this scenario. Showed up occasionally when
1197 * using the wrong password with a copyto command in a debug build on windows, error info
1198 * contained "Unknown Status -858993460 (0xcccccccc)". As you know windows fills the stack frames
1199 * with 0xcccccccc in debug builds to highlight use of uninitialized data, so that's what happened
1200 * here. It's actually good you didn't initialize lGuest, as it would be heck to find otherwise.
1201 *
1202 * I'm still not very impressed with the error managment or the usuefullness of the documentation
1203 * in this code, though the latter is getting better! */
1204 else if (vrc == VERR_GSTCTL_GUEST_ERROR && prcGuest)
1205 *prcGuest = pEvent->GuestResult();
1206 Assert(vrc != VERR_GSTCTL_GUEST_ERROR || !prcGuest || *prcGuest != (int)0xcccccccc);
1207
1208 return vrc;
1209}
1210
1211int GuestFile::i_waitForWrite(GuestWaitEvent *pEvent,
1212 uint32_t uTimeoutMS, uint32_t *pcbWritten)
1213{
1214 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1215
1216 VBoxEventType_T evtType;
1217 ComPtr<IEvent> pIEvent;
1218 int vrc = waitForEvent(pEvent, uTimeoutMS,
1219 &evtType, pIEvent.asOutParam());
1220 if (RT_SUCCESS(vrc))
1221 {
1222 if (evtType == VBoxEventType_OnGuestFileWrite)
1223 {
1224 if (pcbWritten)
1225 {
1226 ComPtr<IGuestFileWriteEvent> pFileEvent = pIEvent;
1227 Assert(!pFileEvent.isNull());
1228
1229 HRESULT hr = pFileEvent->COMGETTER(Processed)((ULONG*)pcbWritten);
1230 ComAssertComRC(hr);
1231 }
1232 }
1233 else
1234 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1235 }
1236
1237 return vrc;
1238}
1239
1240int GuestFile::i_writeData(uint32_t uTimeoutMS, const void *pvData, uint32_t cbData,
1241 uint32_t *pcbWritten)
1242{
1243 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1244 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1245
1246 LogFlowThisFunc(("uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
1247 uTimeoutMS, pvData, cbData));
1248
1249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1250
1251 int vrc;
1252
1253 GuestWaitEvent *pEvent = NULL;
1254 GuestEventTypes eventTypes;
1255 try
1256 {
1257 eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
1258 eventTypes.push_back(VBoxEventType_OnGuestFileWrite);
1259
1260 vrc = registerWaitEvent(eventTypes, &pEvent);
1261 }
1262 catch (std::bad_alloc &)
1263 {
1264 vrc = VERR_NO_MEMORY;
1265 }
1266
1267 if (RT_FAILURE(vrc))
1268 return vrc;
1269
1270 /* Prepare HGCM call. */
1271 VBOXHGCMSVCPARM paParms[8];
1272 int i = 0;
1273 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1274 HGCMSvcSetU32(&paParms[i++], mObjectID /* File handle */);
1275 HGCMSvcSetU32(&paParms[i++], cbData /* Size (in bytes) to write */);
1276 HGCMSvcSetPv (&paParms[i++], unconst(pvData), cbData);
1277
1278 alock.release(); /* Drop write lock before sending. */
1279
1280 vrc = sendMessage(HOST_MSG_FILE_WRITE, i, paParms);
1281 if (RT_SUCCESS(vrc))
1282 {
1283 uint32_t cbWritten = 0;
1284 vrc = i_waitForWrite(pEvent, uTimeoutMS, &cbWritten);
1285 if (RT_SUCCESS(vrc))
1286 {
1287 LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
1288 if (pcbWritten)
1289 *pcbWritten = cbWritten;
1290 }
1291 else if (pEvent->HasGuestError()) /* Return guest rc if available. */
1292 {
1293 vrc = pEvent->GetGuestError();
1294 }
1295 }
1296
1297 unregisterWaitEvent(pEvent);
1298
1299 LogFlowFuncLeaveRC(vrc);
1300 return vrc;
1301}
1302
1303int GuestFile::i_writeDataAt(uint64_t uOffset, uint32_t uTimeoutMS,
1304 const void *pvData, uint32_t cbData, uint32_t *pcbWritten)
1305{
1306 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1307 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1308
1309 LogFlowThisFunc(("uOffset=%RU64, uTimeoutMS=%RU32, pvData=%p, cbData=%zu\n",
1310 uOffset, uTimeoutMS, pvData, cbData));
1311
1312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1313
1314 int vrc;
1315
1316 GuestWaitEvent *pEvent = NULL;
1317 GuestEventTypes eventTypes;
1318 try
1319 {
1320 eventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
1321 eventTypes.push_back(VBoxEventType_OnGuestFileWrite);
1322
1323 vrc = registerWaitEvent(eventTypes, &pEvent);
1324 }
1325 catch (std::bad_alloc &)
1326 {
1327 vrc = VERR_NO_MEMORY;
1328 }
1329
1330 if (RT_FAILURE(vrc))
1331 return vrc;
1332
1333 /* Prepare HGCM call. */
1334 VBOXHGCMSVCPARM paParms[8];
1335 int i = 0;
1336 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1337 HGCMSvcSetU32(&paParms[i++], mObjectID /* File handle */);
1338 HGCMSvcSetU64(&paParms[i++], uOffset /* Offset where to starting writing */);
1339 HGCMSvcSetU32(&paParms[i++], cbData /* Size (in bytes) to write */);
1340 HGCMSvcSetPv (&paParms[i++], unconst(pvData), cbData);
1341
1342 alock.release(); /* Drop write lock before sending. */
1343
1344 vrc = sendMessage(HOST_MSG_FILE_WRITE_AT, i, paParms);
1345 if (RT_SUCCESS(vrc))
1346 {
1347 uint32_t cbWritten = 0;
1348 vrc = i_waitForWrite(pEvent, uTimeoutMS, &cbWritten);
1349 if (RT_SUCCESS(vrc))
1350 {
1351 LogFlowThisFunc(("cbWritten=%RU32\n", cbWritten));
1352 if (pcbWritten)
1353 *pcbWritten = cbWritten;
1354 }
1355 else if (pEvent->HasGuestError()) /* Return guest rc if available. */
1356 {
1357 vrc = pEvent->GetGuestError();
1358 }
1359 }
1360
1361 unregisterWaitEvent(pEvent);
1362
1363 LogFlowFuncLeaveRC(vrc);
1364 return vrc;
1365}
1366
1367// Wrapped IGuestFile methods
1368/////////////////////////////////////////////////////////////////////////////
1369HRESULT GuestFile::close()
1370{
1371 AutoCaller autoCaller(this);
1372 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1373
1374 LogFlowThisFuncEnter();
1375
1376 /* Close file on guest. */
1377 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1378 int vrc = i_closeFile(&rcGuest);
1379 /* On failure don't return here, instead do all the cleanup
1380 * work first and then return an error. */
1381
1382 AssertPtr(mSession);
1383 int vrc2 = mSession->i_fileUnregister(this);
1384 if (RT_SUCCESS(vrc))
1385 vrc = vrc2;
1386
1387 if (RT_FAILURE(vrc))
1388 {
1389 if (vrc == VERR_GSTCTL_GUEST_ERROR)
1390 return GuestFile::i_setErrorExternal(this, rcGuest);
1391 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Closing guest file failed with %Rrc\n"), vrc);
1392 }
1393
1394 LogFlowThisFunc(("Returning S_OK / vrc=%Rrc\n", vrc));
1395 return S_OK;
1396}
1397
1398HRESULT GuestFile::queryInfo(ComPtr<IFsObjInfo> &aObjInfo)
1399{
1400 AutoCaller autoCaller(this);
1401 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1402
1403 LogFlowThisFuncEnter();
1404
1405 HRESULT hr = S_OK;
1406
1407 GuestFsObjData fsObjData;
1408 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1409 int vrc = i_queryInfo(fsObjData, &rcGuest);
1410 if (RT_SUCCESS(vrc))
1411 {
1412 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
1413 hr = ptrFsObjInfo.createObject();
1414 if (SUCCEEDED(hr))
1415 {
1416 vrc = ptrFsObjInfo->init(fsObjData);
1417 if (RT_SUCCESS(vrc))
1418 hr = ptrFsObjInfo.queryInterfaceTo(aObjInfo.asOutParam());
1419 else
1420 hr = setErrorVrc(vrc);
1421 }
1422 }
1423 else
1424 {
1425 if (GuestProcess::i_isGuestError(vrc))
1426 hr = GuestProcess::i_setErrorExternal(this, rcGuest);
1427 else
1428 hr = setErrorVrc(vrc, tr("Querying file information failed: %Rrc"), vrc);
1429 }
1430
1431 LogFlowFuncLeaveRC(vrc);
1432 return hr;
1433}
1434
1435HRESULT GuestFile::querySize(LONG64 *aSize)
1436{
1437 AutoCaller autoCaller(this);
1438 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1439
1440 LogFlowThisFuncEnter();
1441
1442 HRESULT hr = S_OK;
1443
1444 GuestFsObjData fsObjData;
1445 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1446 int vrc = i_queryInfo(fsObjData, &rcGuest);
1447 if (RT_SUCCESS(vrc))
1448 {
1449 *aSize = fsObjData.mObjectSize;
1450 }
1451 else
1452 {
1453 if (GuestProcess::i_isGuestError(vrc))
1454 hr = GuestProcess::i_setErrorExternal(this, rcGuest);
1455 else
1456 hr = setErrorVrc(vrc, tr("Querying file size failed: %Rrc"), vrc);
1457 }
1458
1459 LogFlowFuncLeaveRC(vrc);
1460 return hr;
1461}
1462
1463HRESULT GuestFile::read(ULONG aToRead, ULONG aTimeoutMS, std::vector<BYTE> &aData)
1464{
1465 AutoCaller autoCaller(this);
1466 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1467
1468 if (aToRead == 0)
1469 return setError(E_INVALIDARG, tr("The size to read is zero"));
1470
1471 LogFlowThisFuncEnter();
1472
1473 /* Cap the read at 1MiB because that's all the guest will return anyway. */
1474 if (aToRead > _1M)
1475 aToRead = _1M;
1476
1477 aData.resize(aToRead);
1478
1479 HRESULT hr = S_OK;
1480
1481 uint32_t cbRead;
1482 int vrc = i_readData(aToRead, aTimeoutMS,
1483 &aData.front(), aToRead, &cbRead);
1484
1485 if (RT_SUCCESS(vrc))
1486 {
1487 if (aData.size() != cbRead)
1488 aData.resize(cbRead);
1489 }
1490 else
1491 {
1492 aData.resize(0);
1493
1494 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading from file \"%s\" failed: %Rrc"),
1495 mData.mOpenInfo.mFilename.c_str(), vrc);
1496 }
1497
1498 LogFlowFuncLeaveRC(vrc);
1499 return hr;
1500}
1501
1502HRESULT GuestFile::readAt(LONG64 aOffset, ULONG aToRead, ULONG aTimeoutMS, std::vector<BYTE> &aData)
1503{
1504 AutoCaller autoCaller(this);
1505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1506
1507 if (aToRead == 0)
1508 return setError(E_INVALIDARG, tr("The size to read is zero"));
1509
1510 LogFlowThisFuncEnter();
1511
1512 /* Cap the read at 1MiB because that's all the guest will return anyway. */
1513 if (aToRead > _1M)
1514 aToRead = _1M;
1515
1516 aData.resize(aToRead);
1517
1518 HRESULT hr = S_OK;
1519
1520 size_t cbRead;
1521 int vrc = i_readDataAt(aOffset, aToRead, aTimeoutMS,
1522 &aData.front(), aToRead, &cbRead);
1523 if (RT_SUCCESS(vrc))
1524 {
1525 if (aData.size() != cbRead)
1526 aData.resize(cbRead);
1527 }
1528 else
1529 {
1530 aData.resize(0);
1531
1532 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading from file \"%s\" (at offset %RU64) failed: %Rrc"),
1533 mData.mOpenInfo.mFilename.c_str(), aOffset, vrc);
1534 }
1535
1536 LogFlowFuncLeaveRC(vrc);
1537 return hr;
1538}
1539
1540HRESULT GuestFile::seek(LONG64 aOffset, FileSeekOrigin_T aWhence, LONG64 *aNewOffset)
1541{
1542 AutoCaller autoCaller(this);
1543 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1544
1545 HRESULT hr = S_OK;
1546
1547 GUEST_FILE_SEEKTYPE eSeekType;
1548 switch (aWhence)
1549 {
1550 case FileSeekOrigin_Begin:
1551 eSeekType = GUEST_FILE_SEEKTYPE_BEGIN;
1552 break;
1553
1554 case FileSeekOrigin_Current:
1555 eSeekType = GUEST_FILE_SEEKTYPE_CURRENT;
1556 break;
1557
1558 case FileSeekOrigin_End:
1559 eSeekType = GUEST_FILE_SEEKTYPE_END;
1560 break;
1561
1562 default:
1563 return setError(E_INVALIDARG, tr("Invalid seek type specified"));
1564 }
1565
1566 LogFlowThisFuncEnter();
1567
1568 uint64_t uNewOffset;
1569 int vrc = i_seekAt(aOffset, eSeekType,
1570 30 * 1000 /* 30s timeout */, &uNewOffset);
1571 if (RT_SUCCESS(vrc))
1572 *aNewOffset = RT_MIN(uNewOffset, (uint64_t)INT64_MAX);
1573 else
1574 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Seeking file \"%s\" (to offset %RI64) failed: %Rrc"),
1575 mData.mOpenInfo.mFilename.c_str(), aOffset, vrc);
1576
1577 LogFlowFuncLeaveRC(vrc);
1578 return hr;
1579}
1580
1581HRESULT GuestFile::setACL(const com::Utf8Str &aAcl, ULONG aMode)
1582{
1583 RT_NOREF(aAcl, aMode);
1584 ReturnComNotImplemented();
1585}
1586
1587HRESULT GuestFile::setSize(LONG64 aSize)
1588{
1589 LogFlowThisFuncEnter();
1590
1591 /*
1592 * Validate.
1593 */
1594 if (aSize < 0)
1595 return setError(E_INVALIDARG, tr("The size (%RI64) cannot be a negative value"), aSize);
1596
1597 /*
1598 * Register event callbacks.
1599 */
1600 int vrc;
1601 GuestWaitEvent *pWaitEvent = NULL;
1602 GuestEventTypes lstEventTypes;
1603 try
1604 {
1605 lstEventTypes.push_back(VBoxEventType_OnGuestFileStateChanged);
1606 lstEventTypes.push_back(VBoxEventType_OnGuestFileSizeChanged);
1607 }
1608 catch (std::bad_alloc &)
1609 {
1610 return E_OUTOFMEMORY;
1611 }
1612
1613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1614
1615 vrc = registerWaitEvent(lstEventTypes, &pWaitEvent);
1616 if (RT_SUCCESS(vrc))
1617 {
1618 /*
1619 * Send of the HGCM message.
1620 */
1621 VBOXHGCMSVCPARM aParms[3];
1622 HGCMSvcSetU32(&aParms[0], pWaitEvent->ContextID());
1623 HGCMSvcSetU32(&aParms[1], mObjectID /* File handle */);
1624 HGCMSvcSetU64(&aParms[2], aSize);
1625
1626 alock.release(); /* Drop write lock before sending. */
1627
1628 vrc = sendMessage(HOST_MSG_FILE_SET_SIZE, RT_ELEMENTS(aParms), aParms);
1629 if (RT_SUCCESS(vrc))
1630 {
1631 /*
1632 * Wait for the event.
1633 */
1634 VBoxEventType_T enmEvtType;
1635 ComPtr<IEvent> pIEvent;
1636 vrc = waitForEvent(pWaitEvent, RT_MS_1MIN / 2, &enmEvtType, pIEvent.asOutParam());
1637 if (RT_SUCCESS(vrc))
1638 {
1639 if (enmEvtType == VBoxEventType_OnGuestFileSizeChanged)
1640 vrc = VINF_SUCCESS;
1641 else
1642 vrc = VWRN_GSTCTL_OBJECTSTATE_CHANGED;
1643 }
1644 if (RT_FAILURE(vrc) && pWaitEvent->HasGuestError()) /* Return guest rc if available. */
1645 vrc = pWaitEvent->GetGuestError();
1646 }
1647
1648 /*
1649 * Unregister the wait event and deal with error reporting if needed.
1650 */
1651 unregisterWaitEvent(pWaitEvent);
1652 }
1653 HRESULT hrc;
1654 if (RT_SUCCESS(vrc))
1655 hrc = S_OK;
1656 else
1657 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Setting the file size of '%s' to %RU64 (%#RX64) bytes failed: %Rrc"),
1658 mData.mOpenInfo.mFilename.c_str(), aSize, aSize, vrc);
1659 LogFlowFuncLeaveRC(vrc);
1660 return hrc;
1661}
1662
1663HRESULT GuestFile::write(const std::vector<BYTE> &aData, ULONG aTimeoutMS, ULONG *aWritten)
1664{
1665 AutoCaller autoCaller(this);
1666 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1667
1668 if (aData.size() == 0)
1669 return setError(E_INVALIDARG, tr("No data to write specified"));
1670
1671 LogFlowThisFuncEnter();
1672
1673 HRESULT hr = S_OK;
1674
1675 const uint32_t cbData = (uint32_t)aData.size();
1676 const void *pvData = (void *)&aData.front();
1677 int vrc = i_writeData(aTimeoutMS, pvData, cbData, (uint32_t*)aWritten);
1678 if (RT_FAILURE(vrc))
1679 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Writing %zubytes to file \"%s\" failed: %Rrc"),
1680 aData.size(), mData.mOpenInfo.mFilename.c_str(), vrc);
1681
1682 LogFlowFuncLeaveRC(vrc);
1683 return hr;
1684}
1685
1686HRESULT GuestFile::writeAt(LONG64 aOffset, const std::vector<BYTE> &aData, ULONG aTimeoutMS, ULONG *aWritten)
1687{
1688 AutoCaller autoCaller(this);
1689 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1690
1691 if (aData.size() == 0)
1692 return setError(E_INVALIDARG, tr("No data to write at specified"));
1693
1694 LogFlowThisFuncEnter();
1695
1696 HRESULT hr = S_OK;
1697
1698 const uint32_t cbData = (uint32_t)aData.size();
1699 const void *pvData = (void *)&aData.front();
1700 int vrc = i_writeDataAt(aOffset, aTimeoutMS, pvData, cbData, (uint32_t*)aWritten);
1701 if (RT_FAILURE(vrc))
1702 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Writing %zubytes to file \"%s\" (at offset %RU64) failed: %Rrc"),
1703 aData.size(), mData.mOpenInfo.mFilename.c_str(), aOffset, vrc);
1704
1705 LogFlowFuncLeaveRC(vrc);
1706 return hr;
1707}
1708
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