VirtualBox

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

Last change on this file since 84141 was 84054, checked in by vboxsync, 5 years ago

IPRT,++: Apply bldprog-strtab.h and friends to the IPRT status message database (errmsg.cpp) to reduce size. The interface (RTErrMsg*) has been reworked as we no longer have C-strings in the database, but 'compressed' string w/o zero terminators. bugref:9726

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

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