VirtualBox

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

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

GuestCtrl/Main: Check for tool status when creating guest directories.

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