VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestDirectoryImpl.cpp@ 73003

Last change on this file since 73003 was 73003, checked in by vboxsync, 7 years ago

Main: Use setErrorBoth when we've got a VBox status code handy. (The COM status codes aren't too specfic and this may help us decode error messages and provide an alternative to strstr for API clients. setErrorBoth isn't new, btw.)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.9 KB
Line 
1/* $Id: GuestDirectoryImpl.cpp 73003 2018-07-09 11:09:32Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest directory handling.
4 */
5
6/*
7 * Copyright (C) 2012-2018 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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_GUESTDIRECTORY
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 "GuestDirectoryImpl.h"
29#include "GuestSessionImpl.h"
30#include "GuestCtrlImplPrivate.h"
31
32#include "Global.h"
33#include "AutoCaller.h"
34
35#include <VBox/com/array.h>
36
37
38// constructor / destructor
39/////////////////////////////////////////////////////////////////////////////
40
41DEFINE_EMPTY_CTOR_DTOR(GuestDirectory)
42
43HRESULT GuestDirectory::FinalConstruct(void)
44{
45 LogFlowThisFunc(("\n"));
46 return BaseFinalConstruct();
47}
48
49void GuestDirectory::FinalRelease(void)
50{
51 LogFlowThisFuncEnter();
52 uninit();
53 BaseFinalRelease();
54 LogFlowThisFuncLeave();
55}
56
57// public initializer/uninitializer for internal purposes only
58/////////////////////////////////////////////////////////////////////////////
59
60int GuestDirectory::init(Console *pConsole, GuestSession *pSession, ULONG aObjectID, const GuestDirectoryOpenInfo &openInfo)
61{
62 LogFlowThisFunc(("pConsole=%p, pSession=%p, aObjectID=%RU32, strPath=%s, strFilter=%s, uFlags=%x\n",
63 pConsole, pSession, aObjectID, openInfo.mPath.c_str(), openInfo.mFilter.c_str(), openInfo.mFlags));
64
65 AssertPtrReturn(pConsole, VERR_INVALID_POINTER);
66 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
67
68 /* Enclose the state transition NotReady->InInit->Ready. */
69 AutoInitSpan autoInitSpan(this);
70 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
71
72 int vrc = bindToSession(pConsole, pSession, aObjectID);
73 if (RT_SUCCESS(vrc))
74 {
75 mSession = pSession;
76 mObjectID = aObjectID;
77
78 mData.mOpenInfo = openInfo;
79 }
80
81 if (RT_SUCCESS(vrc))
82 {
83 /* Start the directory process on the guest. */
84 GuestProcessStartupInfo procInfo;
85 procInfo.mName = Utf8StrFmt(tr("Reading directory \"%s\""), openInfo.mPath.c_str());
86 procInfo.mTimeoutMS = 5 * 60 * 1000; /* 5 minutes timeout. */
87 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
88 procInfo.mExecutable= Utf8Str(VBOXSERVICE_TOOL_LS);
89
90 procInfo.mArguments.push_back(procInfo.mExecutable);
91 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
92 /* We want the long output format which contains all the object details. */
93 procInfo.mArguments.push_back(Utf8Str("-l"));
94#if 0 /* Flags are not supported yet. */
95 if (uFlags & DirectoryOpenFlag_NoSymlinks)
96 procInfo.mArguments.push_back(Utf8Str("--nosymlinks")); /** @todo What does GNU here? */
97#endif
98 /** @todo Recursion support? */
99 procInfo.mArguments.push_back(openInfo.mPath); /* The directory we want to open. */
100
101 /*
102 * Start the process asynchronously and keep it around so that we can use
103 * it later in subsequent read() calls.
104 * Note: No guest rc available because operation is asynchronous.
105 */
106 vrc = mData.mProcessTool.init(mSession, procInfo,
107 true /* Async */, NULL /* Guest rc */);
108 }
109
110 if (RT_SUCCESS(vrc))
111 {
112 /* Confirm a successful initialization when it's the case. */
113 autoInitSpan.setSucceeded();
114 return vrc;
115 }
116 else
117 autoInitSpan.setFailed();
118
119 return vrc;
120}
121
122/**
123 * Uninitializes the instance.
124 * Called from FinalRelease().
125 */
126void GuestDirectory::uninit(void)
127{
128 LogFlowThisFuncEnter();
129
130 /* Enclose the state transition Ready->InUninit->NotReady. */
131 AutoUninitSpan autoUninitSpan(this);
132 if (autoUninitSpan.uninitDone())
133 return;
134
135 LogFlowThisFuncLeave();
136}
137
138// implementation of private wrapped getters/setters for attributes
139/////////////////////////////////////////////////////////////////////////////
140
141HRESULT GuestDirectory::getDirectoryName(com::Utf8Str &aDirectoryName)
142{
143 LogFlowThisFuncEnter();
144
145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
146
147 aDirectoryName = mData.mOpenInfo.mPath;
148
149 return S_OK;
150}
151
152HRESULT GuestDirectory::getFilter(com::Utf8Str &aFilter)
153{
154 LogFlowThisFuncEnter();
155
156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
157
158 aFilter = mData.mOpenInfo.mFilter;
159
160 return S_OK;
161}
162
163// private methods
164/////////////////////////////////////////////////////////////////////////////
165
166int GuestDirectory::i_callbackDispatcher(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
167{
168 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
169 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
170
171 LogFlowThisFunc(("strPath=%s, uContextID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
172 mData.mOpenInfo.mPath.c_str(), pCbCtx->uContextID, pCbCtx->uFunction, pSvcCb));
173
174 int vrc;
175 switch (pCbCtx->uFunction)
176 {
177 case GUEST_DIR_NOTIFY:
178 {
179 int idx = 1; /* Current parameter index. */
180 CALLBACKDATA_DIR_NOTIFY dataCb;
181 /* pSvcCb->mpaParms[0] always contains the context ID. */
182 pSvcCb->mpaParms[idx++].getUInt32(&dataCb.uType);
183 pSvcCb->mpaParms[idx++].getUInt32(&dataCb.rc);
184
185 LogFlowFunc(("uType=%RU32, vrcGguest=%Rrc\n", dataCb.uType, (int)dataCb.rc));
186
187 switch (dataCb.uType)
188 {
189 /* Nothing here yet, nothing to dispatch further. */
190
191 default:
192 vrc = VERR_NOT_SUPPORTED;
193 break;
194 }
195 break;
196 }
197
198 default:
199 /* Silently ignore not implemented functions. */
200 vrc = VERR_NOT_SUPPORTED;
201 break;
202 }
203
204 LogFlowFuncLeaveRC(vrc);
205 return vrc;
206}
207
208/* static */
209Utf8Str GuestDirectory::i_guestErrorToString(int rcGuest)
210{
211 Utf8Str strError;
212
213 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
214 switch (rcGuest)
215 {
216 case VERR_CANT_CREATE:
217 strError += Utf8StrFmt("Access denied");
218 break;
219
220 case VERR_DIR_NOT_EMPTY:
221 strError += Utf8StrFmt("Not empty");
222 break;
223
224 default:
225 strError += Utf8StrFmt("%Rrc", rcGuest);
226 break;
227 }
228
229 return strError;
230}
231
232/**
233 * Called by IGuestSession right before this directory gets
234 * removed from the public directory list.
235 */
236int GuestDirectory::i_onRemove(void)
237{
238 LogFlowThisFuncEnter();
239
240 int vrc = VINF_SUCCESS;
241
242 LogFlowFuncLeaveRC(vrc);
243 return vrc;
244}
245
246/**
247 * Closes this guest directory and removes it from the
248 * guest session's directory list.
249 *
250 * @return VBox status code.
251 * @param prcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
252 */
253int GuestDirectory::i_closeInternal(int *prcGuest)
254{
255 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
256
257 int rc = mData.mProcessTool.terminate(30 * 1000 /* 30s timeout */, prcGuest);
258 if (RT_FAILURE(rc))
259 return rc;
260
261 AssertPtr(mSession);
262 int rc2 = mSession->i_directoryUnregister(this);
263 if (RT_SUCCESS(rc))
264 rc = rc2;
265
266 LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
267 return rc;
268}
269
270/**
271 * Reads the next directory entry.
272 *
273 * @return VBox status code. Will return VERR_NO_MORE_FILES if no more entries are available.
274 * @param fsObjInfo Where to store the read directory entry.
275 * @param prcGuest Where to store the guest result code in case VERR_GSTCTL_GUEST_ERROR is returned.
276 */
277int GuestDirectory::i_readInternal(ComObjPtr<GuestFsObjInfo> &fsObjInfo, int *prcGuest)
278{
279 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
280
281 /* Create the FS info object. */
282 HRESULT hr = fsObjInfo.createObject();
283 if (FAILED(hr))
284 return VERR_COM_UNEXPECTED;
285
286 GuestProcessStreamBlock curBlock;
287 int rc = mData.mProcessTool.waitEx(GUESTPROCESSTOOL_WAIT_FLAG_STDOUT_BLOCK,
288 &curBlock, prcGuest);
289 if (RT_SUCCESS(rc))
290 {
291 /*
292 * Note: The guest process can still be around to serve the next
293 * upcoming stream block next time.
294 */
295 if (!mData.mProcessTool.isRunning())
296 rc = mData.mProcessTool.terminatedOk();
297
298 if (RT_SUCCESS(rc))
299 {
300 if (curBlock.GetCount()) /* Did we get content? */
301 {
302 GuestFsObjData objData;
303 rc = objData.FromLs(curBlock, true /* fLong */);
304 if (RT_SUCCESS(rc))
305 {
306 rc = fsObjInfo->init(objData);
307 }
308 else
309 rc = VERR_PATH_NOT_FOUND;
310 }
311 else
312 {
313 /* Nothing to read anymore. Tell the caller. */
314 rc = VERR_NO_MORE_FILES;
315 }
316 }
317 }
318
319 LogFlowThisFunc(("Returning rc=%Rrc\n", rc));
320 return rc;
321}
322
323/* static */
324HRESULT GuestDirectory::i_setErrorExternal(VirtualBoxBase *pInterface, int rcGuest)
325{
326 AssertPtr(pInterface);
327 AssertMsg(RT_FAILURE(rcGuest), ("Guest rc does not indicate a failure when setting error\n"));
328
329 return pInterface->setError(VBOX_E_IPRT_ERROR, GuestDirectory::i_guestErrorToString(rcGuest).c_str());
330}
331
332// implementation of public methods
333/////////////////////////////////////////////////////////////////////////////
334HRESULT GuestDirectory::close()
335{
336 AutoCaller autoCaller(this);
337 if (FAILED(autoCaller.rc())) return autoCaller.rc();
338
339 LogFlowThisFuncEnter();
340
341 HRESULT hr = S_OK;
342
343 int rcGuest;
344 int vrc = i_closeInternal(&rcGuest);
345 if (RT_FAILURE(vrc))
346 {
347 switch (vrc)
348 {
349 case VERR_GSTCTL_GUEST_ERROR:
350 hr = GuestDirectory::i_setErrorExternal(this, rcGuest);
351 break;
352
353 case VERR_NOT_SUPPORTED:
354 /* Silently skip old Guest Additions which do not support killing the
355 * the guest directory handling process. */
356 break;
357
358 default:
359 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
360 tr("Terminating open guest directory \"%s\" failed: %Rrc"), mData.mOpenInfo.mPath.c_str(), vrc);
361 break;
362 }
363 }
364
365 return hr;
366}
367
368HRESULT GuestDirectory::read(ComPtr<IFsObjInfo> &aObjInfo)
369{
370 AutoCaller autoCaller(this);
371 if (FAILED(autoCaller.rc())) return autoCaller.rc();
372
373 LogFlowThisFuncEnter();
374
375 HRESULT hr = S_OK;
376
377 ComObjPtr<GuestFsObjInfo> fsObjInfo; int rcGuest;
378 int vrc = i_readInternal(fsObjInfo, &rcGuest);
379 if (RT_SUCCESS(vrc))
380 {
381AssertMsg(vrc != VWRN_GSTCTL_PROCESS_EXIT_CODE, ("Buggy status code handling! See futher down...\n"));
382 /* Return info object to the caller. */
383 hr = fsObjInfo.queryInterfaceTo(aObjInfo.asOutParam());
384 }
385 else
386 {
387 switch (vrc)
388 {
389 case VERR_GSTCTL_GUEST_ERROR:
390 hr = GuestDirectory::i_setErrorExternal(this, rcGuest);
391 break;
392
393/** @todo r=bird: VWRN_GSTCTL_PROCESS_EXIT_CODE won't ever get here because
394 * RT_SUCCESS(VWRN_GSTCTL_PROCESS_EXIT_CODE) -> true.
395 *
396 */
397 case VWRN_GSTCTL_PROCESS_EXIT_CODE:
398 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading directory \"%s\" failed: %Rrc"),
399 mData.mOpenInfo.mPath.c_str(), mData.mProcessTool.getRc());
400 break;
401
402 case VERR_PATH_NOT_FOUND:
403 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading directory \"%s\" failed: Path not found"),
404 mData.mOpenInfo.mPath.c_str());
405 break;
406
407 case VERR_NO_MORE_FILES:
408 /* See SDK reference. */
409 hr = setErrorBoth(VBOX_E_OBJECT_NOT_FOUND, vrc, tr("Reading directory \"%s\" failed: No more entries"),
410 mData.mOpenInfo.mPath.c_str());
411 break;
412
413 default:
414 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Reading directory \"%s\" returned error: %Rrc\n"),
415 mData.mOpenInfo.mPath.c_str(), vrc);
416 break;
417 }
418 }
419
420 LogFlowThisFunc(("Returning hr=%Rhrc / vrc=%Rrc\n", hr, vrc));
421 return hr;
422}
423
Note: See TracBrowser for help on using the repository browser.

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