VirtualBox

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

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

GuestCtrl: Request (IPC) changes, bugfixes, fixed handle leaks.

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