VirtualBox

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

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

Main/GuestCtrlImplDir: Use RTPathJoinA, fixed memory leak.

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