VirtualBox

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

Last change on this file since 98273 was 98262, checked in by vboxsync, 23 months ago

Main: rc() -> hrc()/vrc(). bugref:10223

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