VirtualBox

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

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

Main/GuestCtrl: Introduced host<->guest PID mapping for immediate returns of executeProcess(); bugfixes.

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