VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestCtrlImplDir.cpp@ 39487

Last change on this file since 39487 was 39418, checked in by vboxsync, 13 years ago

GuestCtrl: Added support for explicitly waiting on stdout/stderr, bugfixes, logging adjustments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.5 KB
Line 
1/* $Id: */
2/** @file
3 * VirtualBox Guest Control - Guest directory handling.
4 */
5
6/*
7 * Copyright (C) 2011 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#include "GuestImpl.h"
19#include "GuestCtrlImplPrivate.h"
20#include "GuestDirEntryImpl.h"
21
22#include "Global.h"
23#include "ConsoleImpl.h"
24#include "ProgressImpl.h"
25#include "VMMDev.h"
26
27#include "AutoCaller.h"
28#include "Logging.h"
29
30#include <VBox/VMMDev.h>
31#ifdef VBOX_WITH_GUEST_CONTROL
32# include <iprt/path.h>
33
34# include <VBox/com/array.h>
35# include <VBox/com/ErrorInfo.h>
36#endif
37
38STDMETHODIMP Guest::DirectoryClose(ULONG aHandle)
39{
40#ifndef VBOX_WITH_GUEST_CONTROL
41 ReturnComNotImplemented();
42#else /* VBOX_WITH_GUEST_CONTROL */
43 using namespace guestControl;
44
45 if (directoryHandleExists(aHandle))
46 {
47 directoryDestroyHandle(aHandle);
48 return S_OK;
49 }
50
51 return setError(VBOX_E_IPRT_ERROR,
52 Guest::tr("Directory handle is invalid"));
53#endif
54}
55
56STDMETHODIMP Guest::DirectoryCreate(IN_BSTR aDirectory,
57 IN_BSTR aUserName, IN_BSTR aPassword,
58 ULONG aMode, ULONG aFlags)
59{
60#ifndef VBOX_WITH_GUEST_CONTROL
61 ReturnComNotImplemented();
62#else /* VBOX_WITH_GUEST_CONTROL */
63 using namespace guestControl;
64
65 CheckComArgStrNotEmptyOrNull(aDirectory);
66
67 /* Do not allow anonymous executions (with system rights). */
68 if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
69 return setError(E_INVALIDARG, tr("No user name specified"));
70
71 LogRel(("Creating guest directory \"%s\" as user \"%s\" ...\n",
72 Utf8Str(aDirectory).c_str(), Utf8Str(aUserName).c_str()));
73
74 return directoryCreateInternal(aDirectory,
75 aUserName, aPassword,
76 aMode, aFlags, NULL /* rc */);
77#endif
78}
79
80#ifdef VBOX_WITH_GUEST_CONTROL
81HRESULT Guest::directoryCreateInternal(IN_BSTR aDirectory,
82 IN_BSTR aUsername, IN_BSTR aPassword,
83 ULONG aMode, ULONG aFlags, int *pRC)
84{
85 using namespace guestControl;
86
87 CheckComArgStrNotEmptyOrNull(aDirectory);
88
89 AutoCaller autoCaller(this);
90 if (FAILED(autoCaller.rc())) return autoCaller.rc();
91
92 /* Validate flags. */
93 if (aFlags != DirectoryCreateFlag_None)
94 {
95 if (!(aFlags & DirectoryCreateFlag_Parents))
96 {
97 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
98 }
99 }
100
101 HRESULT rc = S_OK;
102 try
103 {
104 Utf8Str Utf8Directory(aDirectory);
105
106 com::SafeArray<IN_BSTR> args;
107 com::SafeArray<IN_BSTR> env;
108
109 /*
110 * Prepare tool command line.
111 */
112 if (aFlags & DirectoryCreateFlag_Parents)
113 args.push_back(Bstr("--parents").raw()); /* We also want to create the parent directories. */
114 if (aMode > 0)
115 {
116 args.push_back(Bstr("--mode").raw()); /* Set the creation mode. */
117
118 char szMode[16];
119 RTStrPrintf(szMode, sizeof(szMode), "%o", aMode);
120 args.push_back(Bstr(szMode).raw());
121 }
122 args.push_back(Bstr(Utf8Directory).raw()); /* The directory we want to create. */
123
124 rc = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_MKDIR).raw(), Bstr("Creating directory").raw(),
125 ComSafeArrayAsInParam(args),
126 ComSafeArrayAsInParam(env),
127 aUsername, aPassword,
128 ExecuteProcessFlag_None,
129 NULL, NULL,
130 NULL /* Progress */, NULL /* PID */);
131 }
132 catch (std::bad_alloc &)
133 {
134 rc = E_OUTOFMEMORY;
135 }
136 return rc;
137}
138
139/**
140 * Creates a new directory handle ID and returns it. Returns VERR_TOO_MUCH_DATA
141 * if no free handles left, otherwise VINF_SUCCESS (or some other IPRT error).
142 *
143 * @return IPRT status code.
144 * @param puHandle Pointer where the handle gets stored to. Optional.
145 * @param uPID PID of guest process running the associated "vbox_ls".
146 * @param aDirectory Directory the handle is assigned to.
147 * @param aFilter Directory filter. Optional.
148 * @param uFlags Directory open flags.
149 *
150 */
151int Guest::directoryCreateHandle(ULONG *puHandle, ULONG uPID,
152 IN_BSTR aDirectory, IN_BSTR aFilter, ULONG uFlags)
153{
154 AssertReturn(uPID, VERR_INVALID_PARAMETER);
155 CheckComArgStrNotEmptyOrNull(aDirectory);
156 /* aFilter is optional. */
157 /* uFlags are optional. */
158
159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
160
161 int rc = VERR_TOO_MUCH_DATA;
162 for (uint32_t i = 0; i < UINT32_MAX - 1; i++)
163 {
164 /* Create a new context ID ... */
165 uint32_t uHandleTry = ASMAtomicIncU32(&mNextDirectoryID);
166 GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandleTry);
167 if (it == mGuestDirectoryMap.end()) /* We found a free slot ... */
168 {
169 mGuestDirectoryMap[uHandleTry].mDirectory = aDirectory;
170 mGuestDirectoryMap[uHandleTry].mFilter = aFilter;
171 mGuestDirectoryMap[uHandleTry].mPID = uPID;
172 mGuestDirectoryMap[uHandleTry].mFlags = uFlags;
173 Assert(mGuestDirectoryMap.size());
174
175 rc = VINF_SUCCESS;
176
177 if (puHandle)
178 *puHandle = uHandleTry;
179 break;
180 }
181 }
182
183 return rc;
184}
185
186/**
187 * Destroys a previously created directory handle and its
188 * associated data.
189 *
190 * @return IPRT status code.
191 * @param uHandle Handle to destroy.
192 */
193void Guest::directoryDestroyHandle(uint32_t uHandle)
194{
195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
196
197 GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
198 if (it != mGuestDirectoryMap.end())
199 {
200 /* Destroy raw guest stream buffer - not used
201 * anymore. */
202 it->second.mStream.Destroy();
203
204 /* Remove callback context (not used anymore). */
205 mGuestDirectoryMap.erase(it);
206 }
207}
208
209#if 0
210STDMETHODIMP Guest::DirectoryExists(IN_BSTR aDirectory, IN_BSTR aUsername, IN_BSTR aPassword, BOOL *aExists)
211{
212#ifndef VBOX_WITH_GUEST_CONTROL
213 ReturnComNotImplemented();
214#else /* VBOX_WITH_GUEST_CONTROL */
215 using namespace guestControl;
216
217 CheckComArgStrNotEmptyOrNull(aDirectory);
218
219 /* Do not allow anonymous executions (with system rights). */
220 if (RT_UNLIKELY((aUsername) == NULL || *(aUsername) == '\0'))
221 return setError(E_INVALIDARG, tr("No user name specified"));
222
223 return directoryExistsInternal(aDirectory,
224 aUsername, aPassword, aExists);
225#endif
226}
227#endif
228
229#ifdef VBOX_WITH_GUEST_CONTROL
230HRESULT Guest::directoryExistsInternal(IN_BSTR aDirectory, IN_BSTR aUsername, IN_BSTR aPassword, BOOL *aExists)
231{
232 using namespace guestControl;
233
234 CheckComArgStrNotEmptyOrNull(aDirectory);
235
236 AutoCaller autoCaller(this);
237 if (FAILED(autoCaller.rc())) return autoCaller.rc();
238
239 int rc;
240 HRESULT hr = directoryQueryInfoInternal(aDirectory,
241 aUsername, aPassword,
242 NULL /* No RTFSOBJINFO needed */,
243 RTFSOBJATTRADD_NOTHING, &rc);
244 if (SUCCEEDED(hr))
245 {
246 switch (rc)
247 {
248 case VINF_SUCCESS:
249 *aExists = TRUE;
250 break;
251
252 case VERR_FILE_NOT_FOUND:
253 *aExists = FALSE;
254 break;
255
256 case VERR_NOT_FOUND:
257 rc = setError(VBOX_E_IPRT_ERROR,
258 Guest::tr("Unable to query directory existence"));
259 break;
260
261 default:
262 AssertReleaseMsgFailed(("directoryExistsInternal: Unknown return value (%Rrc)\n", rc));
263 break;
264 }
265 }
266 return hr;
267}
268#endif
269
270/**
271 * Gets the associated PID from a directory handle.
272 *
273 * @return uint32_t Associated PID, 0 if handle not found/invalid.
274 * @param uHandle Directory handle to get PID for.
275 */
276uint32_t Guest::directoryGetPID(uint32_t uHandle)
277{
278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
279
280 GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
281 if (it != mGuestDirectoryMap.end())
282 return it->second.mPID;
283
284 return 0;
285}
286
287/**
288 * Returns the next directory entry of an open guest directory.
289 * Returns VERR_NO_DATA if no more entries available or VERR_NOT_FOUND
290 * if directory handle is invalid.
291 *
292 * @return IPRT status code.
293 * @param uHandle Directory handle to get entry for.
294 * @param streamBlock Reference that receives the next stream block data.
295 */
296int Guest::directoryGetNextEntry(uint32_t uHandle, GuestProcessStreamBlock &streamBlock)
297{
298 // LOCK DOES NOT WORK HERE!?
299 //AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
300
301 GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
302 if (it != mGuestDirectoryMap.end())
303 {
304 return executeStreamGetNextBlock(it->second.mPID,
305 ProcessOutputFlag_None /* StdOut */,
306 it->second.mStream, streamBlock);
307 }
308
309 return VERR_NOT_FOUND;
310}
311
312/**
313 * Checks whether a specified directory handle exists (is valid)
314 * or not.
315 *
316 * @return bool True if handle exists, false if not.
317 * @param uHandle Directory handle to check.
318 */
319bool Guest::directoryHandleExists(uint32_t uHandle)
320{
321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
322
323 GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
324 if (it != mGuestDirectoryMap.end())
325 return true;
326
327 return false;
328}
329#endif /* VBOX_WITH_GUEST_CONTROL */
330
331STDMETHODIMP Guest::DirectoryOpen(IN_BSTR aDirectory, IN_BSTR aFilter,
332 ULONG aFlags, IN_BSTR aUserName, IN_BSTR aPassword,
333 ULONG *aHandle)
334{
335#ifndef VBOX_WITH_GUEST_CONTROL
336 ReturnComNotImplemented();
337#else /* VBOX_WITH_GUEST_CONTROL */
338 using namespace guestControl;
339
340 CheckComArgStrNotEmptyOrNull(aDirectory);
341 CheckComArgNotNull(aHandle);
342
343 /* Do not allow anonymous executions (with system rights). */
344 if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
345 return setError(E_INVALIDARG, tr("No user name specified"));
346
347 return directoryOpenInternal(aDirectory, aFilter,
348 aFlags,
349 aUserName, aPassword,
350 aHandle, NULL /* rc */);
351#endif
352}
353
354#ifdef VBOX_WITH_GUEST_CONTROL
355HRESULT Guest::directoryOpenInternal(IN_BSTR aDirectory, IN_BSTR aFilter,
356 ULONG aFlags,
357 IN_BSTR aUsername, IN_BSTR aPassword,
358 ULONG *aHandle, int *pRC)
359{
360 using namespace guestControl;
361
362 CheckComArgStrNotEmptyOrNull(aDirectory);
363 CheckComArgNotNull(aHandle);
364
365 AutoCaller autoCaller(this);
366 if (FAILED(autoCaller.rc())) return autoCaller.rc();
367
368 /* Validate flags. No flags supported yet. */
369 if (aFlags != DirectoryOpenFlag_None)
370 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
371
372 HRESULT hr = S_OK;
373 try
374 {
375 Utf8Str Utf8Directory(aDirectory);
376 Utf8Str Utf8Filter(aFilter);
377
378 com::SafeArray<IN_BSTR> args;
379 com::SafeArray<IN_BSTR> env;
380
381 /*
382 * Prepare tool command line.
383 */
384
385 /* We need to get output which is machine-readable in form
386 * of "key=value\0..key=value\0\0". */
387 args.push_back(Bstr("--machinereadable").raw());
388
389 /* We want the long output format. Handy for getting a lot of
390 * details we could (should?) use (later). */
391 args.push_back(Bstr("-l").raw());
392
393 /* As we want to keep this stuff simple we don't do recursive (-R)
394 * or dereferencing (--dereference) lookups here. This has to be done by
395 * the user. */
396
397 /* Construct and hand in actual directory name + filter we want to open. */
398 char *pszDirectoryFinal;
399 if (Utf8Filter.isEmpty())
400 pszDirectoryFinal = RTStrDup(Utf8Directory.c_str());
401 else
402 pszDirectoryFinal = RTPathJoinA(Utf8Directory.c_str(), Utf8Filter.c_str());
403 if (!pszDirectoryFinal)
404 return setError(E_OUTOFMEMORY, tr("Out of memory while allocating final directory"));
405
406 args.push_back(Bstr(pszDirectoryFinal).raw()); /* The directory we want to open. */
407 RTStrFree(pszDirectoryFinal);
408
409 ULONG uPID;
410 /* We only start the directory listing and requesting stdout data but don't get its
411 * data here; this is done in sequential IGuest::DirectoryRead calls then. */
412 hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_LS).raw(), Bstr("Opening directory").raw(),
413 ComSafeArrayAsInParam(args),
414 ComSafeArrayAsInParam(env),
415 aUsername, aPassword,
416 ExecuteProcessFlag_WaitForStdOut,
417 NULL, NULL,
418 NULL /* Progress */, &uPID);
419 if (SUCCEEDED(hr))
420 {
421 /* Assign new directory handle ID. */
422 ULONG uHandleNew;
423 int rc = directoryCreateHandle(&uHandleNew, uPID,
424 aDirectory, aFilter, aFlags);
425 if (RT_SUCCESS(rc))
426 {
427 *aHandle = uHandleNew;
428 }
429 else
430 hr = setError(VBOX_E_IPRT_ERROR,
431 tr("Unable to create guest directory handle (%Rrc)"), rc);
432 if (pRC)
433 *pRC = rc;
434 }
435 }
436 catch (std::bad_alloc &)
437 {
438 hr = E_OUTOFMEMORY;
439 }
440 return hr;
441}
442
443HRESULT Guest::directoryQueryInfoInternal(IN_BSTR aDirectory,
444 IN_BSTR aUsername, IN_BSTR aPassword,
445 PRTFSOBJINFO aObjInfo, RTFSOBJATTRADD enmAddAttribs,
446 int *pRC)
447{
448 using namespace guestControl;
449
450 /** @todo Search directory cache first? */
451
452 CheckComArgStrNotEmptyOrNull(aDirectory);
453 /* aUsername is optional. */
454 /* aPassword is optional. */
455 /* aObjInfo is optional. */
456
457 AutoCaller autoCaller(this);
458 if (FAILED(autoCaller.rc())) return autoCaller.rc();
459
460 HRESULT hr = S_OK;
461 try
462 {
463 Utf8Str Utf8Dir(aDirectory);
464 Utf8Str Utf8Username(aUsername);
465 Utf8Str Utf8Password(aPassword);
466
467 com::SafeArray<IN_BSTR> args;
468 com::SafeArray<IN_BSTR> env;
469
470 /*
471 * Prepare tool command line.
472 */
473
474 /* We need to get output which is machine-readable in form
475 * of "key=value\0..key=value\0\0". */
476 args.push_back(Bstr("--machinereadable").raw());
477
478 /* Only the actual file name to chekc is needed for now. */
479 args.push_back(Bstr(Utf8Dir).raw());
480
481 /*
482 * Execute guest process.
483 */
484 ULONG uPID;
485 GuestCtrlStreamObjects stdOut;
486 hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_STAT).raw(), Bstr("Querying directory information").raw(),
487 ComSafeArrayAsInParam(args),
488 ComSafeArrayAsInParam(env),
489 aUsername, aPassword,
490 ExecuteProcessFlag_WaitForStdOut,
491 &stdOut, NULL /* stdErr */,
492 NULL /* Progress */, &uPID);
493 if (SUCCEEDED(hr))
494 {
495 int rc = VINF_SUCCESS;
496
497 Assert(stdOut.size());
498 const char *pszFsType = stdOut[0].GetString("ftype");
499 if (!pszFsType) /* Attribute missing? */
500 rc = VERR_NOT_FOUND;
501 if ( RT_SUCCESS(rc)
502 && strcmp(pszFsType, "d")) /* Directory? */
503 {
504 rc = VERR_FILE_NOT_FOUND;
505 /* This is not critical for Main, so don't set hr --
506 * we will take care of rc then. */
507 }
508 if ( RT_SUCCESS(rc)
509 && aObjInfo) /* Do we want object details? */
510 {
511 hr = executeStreamQueryFsObjInfo(aDirectory, stdOut[0],
512 aObjInfo, enmAddAttribs);
513 }
514
515 if (pRC)
516 *pRC = rc;
517 }
518 }
519 catch (std::bad_alloc &)
520 {
521 hr = E_OUTOFMEMORY;
522 }
523 return hr;
524}
525#endif /* VBOX_WITH_GUEST_CONTROL */
526
527STDMETHODIMP Guest::DirectoryRead(ULONG aHandle, IGuestDirEntry **aDirEntry)
528{
529#ifndef VBOX_WITH_GUEST_CONTROL
530 ReturnComNotImplemented();
531#else /* VBOX_WITH_GUEST_CONTROL */
532 using namespace guestControl;
533
534 CheckComArgOutPointerValid(aDirEntry);
535
536 AutoCaller autoCaller(this);
537 if (FAILED(autoCaller.rc())) return autoCaller.rc();
538
539 HRESULT hr = S_OK;
540 try
541 {
542 GuestProcessStreamBlock streamBlock;
543 int rc = directoryGetNextEntry(aHandle, streamBlock);
544 if (RT_SUCCESS(rc))
545 {
546 if (streamBlock.GetCount())
547 {
548 ComObjPtr <GuestDirEntry> pDirEntry;
549 hr = pDirEntry.createObject();
550 ComAssertComRC(hr);
551
552 Assert(streamBlock.GetCount());
553 hr = pDirEntry->init(this, streamBlock);
554 if (SUCCEEDED(hr))
555 {
556 pDirEntry.queryInterfaceTo(aDirEntry);
557 }
558 else
559 hr = setError(VBOX_E_IPRT_ERROR,
560 Guest::tr("Failed to init guest directory entry"));
561 }
562 else
563 {
564 /* No more directory entries to read. That's fine. */
565 hr = E_ABORT; /** @todo Find/define a better rc! */
566 }
567 }
568 else
569 hr = setError(VBOX_E_IPRT_ERROR,
570 Guest::tr("Failed getting next directory entry (%Rrc)"), rc);
571 }
572 catch (std::bad_alloc &)
573 {
574 hr = E_OUTOFMEMORY;
575 }
576 return hr;
577#endif
578}
579
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