VirtualBox

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

Last change on this file since 92154 was 91718, checked in by vboxsync, 3 years ago

Main: bugref:1909: Added initial translation to Russian of API messages. Fixed errors and plurals wherever needed. Fixed type of the plural argument in the tr()

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

© 2023 Oracle
ContactPrivacy policyTerms of Use