VirtualBox

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

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

GuestControl: Bugfixes, logging.

  • 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 default:
257 hr = setError(VBOX_E_IPRT_ERROR,
258 Guest::tr("Unable to query directory existence (%Rrc)"), rc);
259 break;
260 }
261 }
262 return hr;
263}
264#endif
265
266/**
267 * Gets the associated PID from a directory handle.
268 *
269 * @return uint32_t Associated PID, 0 if handle not found/invalid.
270 * @param uHandle Directory handle to get PID for.
271 */
272uint32_t Guest::directoryGetPID(uint32_t uHandle)
273{
274 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
275
276 GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
277 if (it != mGuestDirectoryMap.end())
278 return it->second.mPID;
279
280 return 0;
281}
282
283/**
284 * Returns the next directory entry of an open guest directory.
285 * Returns VERR_NO_DATA if no more entries available or VERR_NOT_FOUND
286 * if directory handle is invalid.
287 *
288 * @return IPRT status code.
289 * @param uHandle Directory handle to get entry for.
290 * @param streamBlock Reference that receives the next stream block data.
291 */
292int Guest::directoryGetNextEntry(uint32_t uHandle, GuestProcessStreamBlock &streamBlock)
293{
294 // LOCK DOES NOT WORK HERE!?
295 //AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
296
297 GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
298 if (it != mGuestDirectoryMap.end())
299 {
300 return executeStreamGetNextBlock(it->second.mPID,
301 ProcessOutputFlag_None /* StdOut */,
302 it->second.mStream, streamBlock);
303 }
304
305 return VERR_NOT_FOUND;
306}
307
308/**
309 * Checks whether a specified directory handle exists (is valid)
310 * or not.
311 *
312 * @return bool True if handle exists, false if not.
313 * @param uHandle Directory handle to check.
314 */
315bool Guest::directoryHandleExists(uint32_t uHandle)
316{
317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
318
319 GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
320 if (it != mGuestDirectoryMap.end())
321 return true;
322
323 return false;
324}
325#endif /* VBOX_WITH_GUEST_CONTROL */
326
327STDMETHODIMP Guest::DirectoryOpen(IN_BSTR aDirectory, IN_BSTR aFilter,
328 ULONG aFlags, IN_BSTR aUserName, IN_BSTR aPassword,
329 ULONG *aHandle)
330{
331#ifndef VBOX_WITH_GUEST_CONTROL
332 ReturnComNotImplemented();
333#else /* VBOX_WITH_GUEST_CONTROL */
334 using namespace guestControl;
335
336 CheckComArgStrNotEmptyOrNull(aDirectory);
337 CheckComArgNotNull(aHandle);
338
339 /* Do not allow anonymous executions (with system rights). */
340 if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
341 return setError(E_INVALIDARG, tr("No user name specified"));
342
343 return directoryOpenInternal(aDirectory, aFilter,
344 aFlags,
345 aUserName, aPassword,
346 aHandle, NULL /* rc */);
347#endif
348}
349
350#ifdef VBOX_WITH_GUEST_CONTROL
351HRESULT Guest::directoryOpenInternal(IN_BSTR aDirectory, IN_BSTR aFilter,
352 ULONG aFlags,
353 IN_BSTR aUsername, IN_BSTR aPassword,
354 ULONG *aHandle, int *pRC)
355{
356 using namespace guestControl;
357
358 CheckComArgStrNotEmptyOrNull(aDirectory);
359 CheckComArgNotNull(aHandle);
360
361 AutoCaller autoCaller(this);
362 if (FAILED(autoCaller.rc())) return autoCaller.rc();
363
364 /* Validate flags. No flags supported yet. */
365 if (aFlags != DirectoryOpenFlag_None)
366 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
367
368 HRESULT hr = S_OK;
369 try
370 {
371 Utf8Str Utf8Directory(aDirectory);
372 Utf8Str Utf8Filter(aFilter);
373
374 com::SafeArray<IN_BSTR> args;
375 com::SafeArray<IN_BSTR> env;
376
377 /*
378 * Prepare tool command line.
379 */
380
381 /* We need to get output which is machine-readable in form
382 * of "key=value\0..key=value\0\0". */
383 args.push_back(Bstr("--machinereadable").raw());
384
385 /* We want the long output format. Handy for getting a lot of
386 * details we could (should?) use (later). */
387 args.push_back(Bstr("-l").raw());
388
389 /* As we want to keep this stuff simple we don't do recursive (-R)
390 * or dereferencing (--dereference) lookups here. This has to be done by
391 * the user. */
392
393 /* Construct and hand in actual directory name + filter we want to open. */
394 char *pszDirectoryFinal;
395 if (Utf8Filter.isEmpty())
396 pszDirectoryFinal = RTStrDup(Utf8Directory.c_str());
397 else
398 pszDirectoryFinal = RTPathJoinA(Utf8Directory.c_str(), Utf8Filter.c_str());
399 if (!pszDirectoryFinal)
400 return setError(E_OUTOFMEMORY, tr("Out of memory while allocating final directory"));
401
402 args.push_back(Bstr(pszDirectoryFinal).raw()); /* The directory we want to open. */
403 RTStrFree(pszDirectoryFinal);
404
405 ULONG uPID;
406 /* We only start the directory listing and requesting stdout data but don't get its
407 * data here; this is done in sequential IGuest::DirectoryRead calls then. */
408 hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_LS).raw(), Bstr("Opening directory").raw(),
409 ComSafeArrayAsInParam(args),
410 ComSafeArrayAsInParam(env),
411 aUsername, aPassword,
412 ExecuteProcessFlag_WaitForStdOut,
413 NULL, NULL,
414 NULL /* Progress */, &uPID);
415 if (SUCCEEDED(hr))
416 {
417 /* Assign new directory handle ID. */
418 ULONG uHandleNew;
419 int rc = directoryCreateHandle(&uHandleNew, uPID,
420 aDirectory, aFilter, aFlags);
421 if (RT_SUCCESS(rc))
422 {
423 *aHandle = uHandleNew;
424 }
425 else
426 hr = setError(VBOX_E_IPRT_ERROR,
427 tr("Unable to create guest directory handle (%Rrc)"), rc);
428 if (pRC)
429 *pRC = rc;
430 }
431 }
432 catch (std::bad_alloc &)
433 {
434 hr = E_OUTOFMEMORY;
435 }
436 return hr;
437}
438
439HRESULT Guest::directoryQueryInfoInternal(IN_BSTR aDirectory,
440 IN_BSTR aUsername, IN_BSTR aPassword,
441 PRTFSOBJINFO aObjInfo, RTFSOBJATTRADD enmAddAttribs,
442 int *pRC)
443{
444 using namespace guestControl;
445
446 /** @todo Search directory cache first? */
447
448 CheckComArgStrNotEmptyOrNull(aDirectory);
449 /* aUsername is optional. */
450 /* aPassword is optional. */
451 /* aObjInfo is optional. */
452
453 AutoCaller autoCaller(this);
454 if (FAILED(autoCaller.rc())) return autoCaller.rc();
455
456 HRESULT hr = S_OK;
457 try
458 {
459 Utf8Str Utf8Dir(aDirectory);
460 Utf8Str Utf8Username(aUsername);
461 Utf8Str Utf8Password(aPassword);
462
463 com::SafeArray<IN_BSTR> args;
464 com::SafeArray<IN_BSTR> env;
465
466 /*
467 * Prepare tool command line.
468 */
469
470 /* We need to get output which is machine-readable in form
471 * of "key=value\0..key=value\0\0". */
472 args.push_back(Bstr("--machinereadable").raw());
473
474 /* Only the actual file name to chekc is needed for now. */
475 args.push_back(Bstr(Utf8Dir).raw());
476
477 /*
478 * Execute guest process.
479 */
480 ULONG uPID;
481 GuestCtrlStreamObjects stdOut;
482 hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_STAT).raw(), Bstr("Querying directory information").raw(),
483 ComSafeArrayAsInParam(args),
484 ComSafeArrayAsInParam(env),
485 aUsername, aPassword,
486 ExecuteProcessFlag_WaitForStdOut,
487 &stdOut, NULL /* stdErr */,
488 NULL /* Progress */, &uPID);
489 if (SUCCEEDED(hr))
490 {
491 int rc = VINF_SUCCESS;
492 if (stdOut.size())
493 {
494 const char *pszFsType = stdOut[0].GetString("ftype");
495 if (!pszFsType) /* Attribute missing? */
496 rc = VERR_NOT_FOUND;
497 if ( RT_SUCCESS(rc)
498 && strcmp(pszFsType, "d")) /* Directory? */
499 {
500 rc = VERR_FILE_NOT_FOUND;
501 /* This is not critical for Main, so don't set hr --
502 * we will take care of rc then. */
503 }
504 if ( RT_SUCCESS(rc)
505 && aObjInfo) /* Do we want object details? */
506 {
507 rc = executeStreamQueryFsObjInfo(aDirectory, stdOut[0],
508 aObjInfo, enmAddAttribs);
509 }
510 }
511 else
512 rc = VERR_NO_DATA;
513
514 if (pRC)
515 *pRC = rc;
516 }
517 }
518 catch (std::bad_alloc &)
519 {
520 hr = E_OUTOFMEMORY;
521 }
522 return hr;
523}
524#endif /* VBOX_WITH_GUEST_CONTROL */
525
526STDMETHODIMP Guest::DirectoryRead(ULONG aHandle, IGuestDirEntry **aDirEntry)
527{
528#ifndef VBOX_WITH_GUEST_CONTROL
529 ReturnComNotImplemented();
530#else /* VBOX_WITH_GUEST_CONTROL */
531 using namespace guestControl;
532
533 CheckComArgOutPointerValid(aDirEntry);
534
535 AutoCaller autoCaller(this);
536 if (FAILED(autoCaller.rc())) return autoCaller.rc();
537
538 HRESULT hr = S_OK;
539 try
540 {
541 GuestProcessStreamBlock streamBlock;
542 int rc = directoryGetNextEntry(aHandle, streamBlock);
543 if (RT_SUCCESS(rc))
544 {
545 if (streamBlock.GetCount())
546 {
547 ComObjPtr <GuestDirEntry> pDirEntry;
548 hr = pDirEntry.createObject();
549 ComAssertComRC(hr);
550
551 Assert(streamBlock.GetCount());
552 hr = pDirEntry->init(this, streamBlock);
553 if (SUCCEEDED(hr))
554 {
555 pDirEntry.queryInterfaceTo(aDirEntry);
556 }
557 else
558 hr = setError(VBOX_E_IPRT_ERROR,
559 Guest::tr("Failed to init guest directory entry"));
560 }
561 else
562 {
563 /* No more directory entries to read. That's fine. */
564 hr = E_ABORT; /** @todo Find/define a better rc! */
565 }
566 }
567 else
568 hr = setError(VBOX_E_IPRT_ERROR,
569 Guest::tr("Failed getting next directory entry (%Rrc)"), rc);
570 }
571 catch (std::bad_alloc &)
572 {
573 hr = E_OUTOFMEMORY;
574 }
575 return hr;
576#endif
577}
578
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