VirtualBox

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

Last change on this file since 38395 was 38395, checked in by vboxsync, 14 years ago

GuestCtrl: Update.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette