VirtualBox

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

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

GuestCtrl: Disable copying directories from guest -> host, reverted API interface change.

  • 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.215389.xyz. 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
205#if 0
206STDMETHODIMP Guest::DirectoryExists(IN_BSTR aDirectory, IN_BSTR aUsername, IN_BSTR aPassword, BOOL *aExists)
207{
208#ifndef VBOX_WITH_GUEST_CONTROL
209 ReturnComNotImplemented();
210#else /* VBOX_WITH_GUEST_CONTROL */
211 using namespace guestControl;
212
213 CheckComArgStrNotEmptyOrNull(aDirectory);
214
215 /* Do not allow anonymous executions (with system rights). */
216 if (RT_UNLIKELY((aUsername) == NULL || *(aUsername) == '\0'))
217 return setError(E_INVALIDARG, tr("No user name specified"));
218
219 return directoryExistsInternal(aDirectory,
220 aUsername, aPassword, aExists);
221#endif
222}
223#endif
224
225#ifdef VBOX_WITH_GUEST_CONTROL
226HRESULT Guest::directoryExistsInternal(IN_BSTR aDirectory, IN_BSTR aUsername, IN_BSTR aPassword, BOOL *aExists)
227{
228 using namespace guestControl;
229
230 CheckComArgStrNotEmptyOrNull(aDirectory);
231
232 AutoCaller autoCaller(this);
233 if (FAILED(autoCaller.rc())) return autoCaller.rc();
234
235 RTFSOBJINFO objInfo;
236 int rc;
237 HRESULT hr = directoryQueryInfoInternal(aDirectory,
238 aUsername, aPassword,
239 &objInfo, RTFSOBJATTRADD_NOTHING, &rc);
240 if (SUCCEEDED(hr))
241 {
242 switch (rc)
243 {
244 case VINF_SUCCESS:
245 *aExists = TRUE;
246 break;
247
248 case VERR_FILE_NOT_FOUND:
249 *aExists = FALSE;
250 break;
251
252 case VERR_NOT_FOUND:
253 rc = setError(VBOX_E_IPRT_ERROR,
254 Guest::tr("Unable to query directory existence"));
255 break;
256
257 default:
258 AssertReleaseMsgFailed(("directoryExistsInternal: Unknown return value (%Rrc)\n", 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_MORE_FILES if no more entries available.
286 *
287 * @return IPRT status code.
288 * @param uHandle Directory handle to get entry for.
289 * @param streamBlock Reference that receives the next stream block data.
290 */
291int Guest::directoryGetNextEntry(uint32_t uHandle, GuestProcessStreamBlock &streamBlock)
292{
293 // LOCK DOES NOT WORK HERE!?
294 //AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
295
296 GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
297 if (it != mGuestDirectoryMap.end())
298 {
299 return executeStreamGetNextBlock(it->second.mPID,
300 it->second.mStream, streamBlock);
301 }
302
303 return VERR_NOT_FOUND;
304}
305
306/**
307 * Checks whether a specified directory handle exists (is valid)
308 * or not.
309 *
310 * @return bool True if handle exists, false if not.
311 * @param uHandle Directory handle to check.
312 */
313bool Guest::directoryHandleExists(uint32_t uHandle)
314{
315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
316
317 GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
318 if (it != mGuestDirectoryMap.end())
319 return true;
320
321 return false;
322}
323#endif /* VBOX_WITH_GUEST_CONTROL */
324
325STDMETHODIMP Guest::DirectoryOpen(IN_BSTR aDirectory, IN_BSTR aFilter,
326 ULONG aFlags, IN_BSTR aUserName, IN_BSTR aPassword,
327 ULONG *aHandle)
328{
329#ifndef VBOX_WITH_GUEST_CONTROL
330 ReturnComNotImplemented();
331#else /* VBOX_WITH_GUEST_CONTROL */
332 using namespace guestControl;
333
334 CheckComArgStrNotEmptyOrNull(aDirectory);
335 CheckComArgNotNull(aHandle);
336
337 /* Do not allow anonymous executions (with system rights). */
338 if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
339 return setError(E_INVALIDARG, tr("No user name specified"));
340
341 return directoryOpenInternal(aDirectory, aFilter,
342 aFlags,
343 aUserName, aPassword,
344 aHandle, NULL /* rc */);
345#endif
346}
347
348#ifdef VBOX_WITH_GUEST_CONTROL
349HRESULT Guest::directoryOpenInternal(IN_BSTR aDirectory, IN_BSTR aFilter,
350 ULONG aFlags,
351 IN_BSTR aUsername, IN_BSTR aPassword,
352 ULONG *aHandle, int *pRC)
353{
354 using namespace guestControl;
355
356 CheckComArgStrNotEmptyOrNull(aDirectory);
357 CheckComArgNotNull(aHandle);
358
359 AutoCaller autoCaller(this);
360 if (FAILED(autoCaller.rc())) return autoCaller.rc();
361
362 /* Validate flags. No flags supported yet. */
363 if (aFlags != DirectoryOpenFlag_None)
364 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
365
366 HRESULT hr = S_OK;
367 try
368 {
369 Utf8Str Utf8Directory(aDirectory);
370 Utf8Str Utf8Filter(aFilter);
371
372 com::SafeArray<IN_BSTR> args;
373 com::SafeArray<IN_BSTR> env;
374
375 /*
376 * Prepare tool command line.
377 */
378
379 /* We need to get output which is machine-readable in form
380 * of "key=value\0..key=value\0\0". */
381 args.push_back(Bstr("--machinereadable").raw());
382
383 /* We want the long output format. Handy for getting a lot of
384 * details we could (should?) use (later). */
385 args.push_back(Bstr("-l").raw());
386
387 /* As we want to keep this stuff simple we don't do recursive (-R)
388 * or dereferencing (--dereference) lookups here. This has to be done by
389 * the user. */
390
391 /* Construct and hand in actual directory name + filter we want to open. */
392 char *pszDirectoryFinal;
393 int cbRet;
394 if (Utf8Filter.isEmpty())
395 cbRet = RTStrAPrintf(&pszDirectoryFinal, "%s", Utf8Directory.c_str());
396 else
397 cbRet = RTStrAPrintf(&pszDirectoryFinal, "%s/%s",
398 Utf8Directory.c_str(), Utf8Filter.c_str());
399 if (!cbRet)
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
404 ULONG uPID;
405 /** @todo Don't wait for tool to finish! Might take a lot of time! */
406 hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_LS).raw(), Bstr("Opening directory").raw(),
407 ComSafeArrayAsInParam(args),
408 ComSafeArrayAsInParam(env),
409 aUsername, aPassword,
410 NULL /* Progress */, &uPID);
411 if (SUCCEEDED(hr))
412 {
413 /* Assign new directory handle ID. */
414 ULONG uHandleNew;
415 int vrc = directoryCreateHandle(&uHandleNew, uPID,
416 aDirectory, aFilter, aFlags);
417 if (RT_SUCCESS(vrc))
418 {
419 *aHandle = uHandleNew;
420 }
421 else
422 hr = setError(VBOX_E_IPRT_ERROR,
423 tr("Unable to create guest directory handle (%Rrc)"), vrc);
424 }
425 }
426 catch (std::bad_alloc &)
427 {
428 hr = E_OUTOFMEMORY;
429 }
430 return hr;
431}
432
433HRESULT Guest::directoryQueryInfoInternal(IN_BSTR aDirectory,
434 IN_BSTR aUsername, IN_BSTR aPassword,
435 PRTFSOBJINFO aObjInfo, RTFSOBJATTRADD enmAddAttribs,
436 int *pRC)
437{
438 using namespace guestControl;
439
440 /** @todo Search directory cache first? */
441
442 CheckComArgStrNotEmptyOrNull(aDirectory);
443 /* aUsername is optional. */
444 /* aPassword is optional. */
445 /* aObjInfo is optional. */
446
447 AutoCaller autoCaller(this);
448 if (FAILED(autoCaller.rc())) return autoCaller.rc();
449
450 HRESULT hr = S_OK;
451 try
452 {
453 Utf8Str Utf8Dir(aDirectory);
454 Utf8Str Utf8Username(aUsername);
455 Utf8Str Utf8Password(aPassword);
456
457 com::SafeArray<IN_BSTR> args;
458 com::SafeArray<IN_BSTR> env;
459
460 /*
461 * Prepare tool command line.
462 */
463
464 /* We need to get output which is machine-readable in form
465 * of "key=value\0..key=value\0\0". */
466 args.push_back(Bstr("--machinereadable").raw());
467
468 /* Only the actual file name to chekc is needed for now. */
469 args.push_back(Bstr(Utf8Dir).raw());
470
471 /*
472 * Execute guest process.
473 */
474 ULONG uPID;
475 hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_STAT).raw(), Bstr("Querying directory information").raw(),
476 ComSafeArrayAsInParam(args),
477 ComSafeArrayAsInParam(env),
478 aUsername, aPassword,
479 NULL /* Progress */, &uPID);
480 if (SUCCEEDED(hr))
481 {
482 GuestCtrlStreamObjects streamObjs;
483 hr = executeStreamParse(uPID, streamObjs);
484 if (SUCCEEDED(hr))
485 {
486 int rc = VINF_SUCCESS;
487
488 GuestProcessStreamBlock *pBlock = streamObjs[0];
489 AssertPtr(pBlock);
490 const char *pszFsType = pBlock->GetString("ftype");
491 if (!pszFsType) /* Attribute missing? */
492 rc = VERR_NOT_FOUND;
493 if ( RT_SUCCESS(rc)
494 && strcmp(pszFsType, "d")) /* Directory? */
495 {
496 rc = VERR_FILE_NOT_FOUND;
497 }
498 if ( RT_SUCCESS(rc)
499 && aObjInfo) /* Do we want object details? */
500 {
501 hr = executeStreamQueryFsObjInfo(aDirectory, pBlock,
502 aObjInfo, enmAddAttribs);
503 }
504
505 executeStreamFree(streamObjs);
506
507 if (pRC)
508 *pRC = rc;
509 }
510 }
511 }
512 catch (std::bad_alloc &)
513 {
514 hr = E_OUTOFMEMORY;
515 }
516 return hr;
517}
518#endif /* VBOX_WITH_GUEST_CONTROL */
519
520STDMETHODIMP Guest::DirectoryRead(ULONG aHandle, IGuestDirEntry **aDirEntry)
521{
522#ifndef VBOX_WITH_GUEST_CONTROL
523 ReturnComNotImplemented();
524#else /* VBOX_WITH_GUEST_CONTROL */
525 using namespace guestControl;
526
527 CheckComArgOutPointerValid(aDirEntry);
528
529 AutoCaller autoCaller(this);
530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
531
532 HRESULT hr = S_OK;
533 try
534 {
535 GuestProcessStreamBlock streamBlock;
536 int rc = directoryGetNextEntry(aHandle, streamBlock);
537 if (RT_SUCCESS(rc))
538 {
539 ComObjPtr <GuestDirEntry> pDirEntry;
540 hr = pDirEntry.createObject();
541 ComAssertComRC(hr);
542
543 hr = pDirEntry->init(this, streamBlock);
544 if (SUCCEEDED(hr))
545 {
546 pDirEntry.queryInterfaceTo(aDirEntry);
547 }
548 else
549 hr = setError(VBOX_E_IPRT_ERROR,
550 Guest::tr("Unable to init guest directory entry"));
551 }
552 else if (rc == VERR_NO_MORE_FILES)
553 {
554 /* No more directory entries to read. */
555 hr = E_ABORT; /** @todo Find/define a better rc! */
556 }
557 else
558 hr = setError(VBOX_E_IPRT_ERROR,
559 Guest::tr("Failed getting next directory entry (%Rrc)"), rc);
560 }
561 catch (std::bad_alloc &)
562 {
563 hr = E_OUTOFMEMORY;
564 }
565 return hr;
566#endif
567}
568
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