3
* VirtualBox Guest Control - Guest directory handling.
7
* Copyright (C) 2011 Oracle Corporation
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.
18
#include "GuestImpl.h"
19
#include "GuestCtrlImplPrivate.h"
20
#include "GuestDirEntryImpl.h"
23
#include "ConsoleImpl.h"
24
#include "ProgressImpl.h"
27
#include "AutoCaller.h"
30
#include <VBox/VMMDev.h>
31
#ifdef VBOX_WITH_GUEST_CONTROL
32
# include <VBox/com/array.h>
33
# include <VBox/com/ErrorInfo.h>
36
STDMETHODIMP Guest::DirectoryClose(ULONG aHandle)
38
#ifndef VBOX_WITH_GUEST_CONTROL
39
ReturnComNotImplemented();
40
#else /* VBOX_WITH_GUEST_CONTROL */
41
using namespace guestControl;
43
if (directoryHandleExists(aHandle))
45
directoryDestroyHandle(aHandle);
49
return setError(VBOX_E_IPRT_ERROR,
50
Guest::tr("Directory handle is invalid"));
54
STDMETHODIMP Guest::DirectoryCreate(IN_BSTR aDirectory,
55
IN_BSTR aUserName, IN_BSTR aPassword,
56
ULONG aMode, ULONG aFlags)
58
#ifndef VBOX_WITH_GUEST_CONTROL
59
ReturnComNotImplemented();
60
#else /* VBOX_WITH_GUEST_CONTROL */
61
using namespace guestControl;
63
CheckComArgStrNotEmptyOrNull(aDirectory);
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"));
69
LogRel(("Creating guest directory \"%s\" as user \"%s\" ...\n",
70
Utf8Str(aDirectory).c_str(), Utf8Str(aUserName).c_str()));
72
return directoryCreateInternal(aDirectory,
74
aMode, aFlags, NULL /* rc */);
78
#ifdef VBOX_WITH_GUEST_CONTROL
79
HRESULT Guest::directoryCreateInternal(IN_BSTR aDirectory,
80
IN_BSTR aUsername, IN_BSTR aPassword,
81
ULONG aMode, ULONG aFlags, int *pRC)
83
using namespace guestControl;
85
CheckComArgStrNotEmptyOrNull(aDirectory);
87
AutoCaller autoCaller(this);
88
if (FAILED(autoCaller.rc())) return autoCaller.rc();
91
if (aFlags != DirectoryCreateFlag_None)
93
if (!(aFlags & DirectoryCreateFlag_Parents))
95
return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
102
Utf8Str Utf8Directory(aDirectory);
104
com::SafeArray<IN_BSTR> args;
105
com::SafeArray<IN_BSTR> env;
108
* Prepare tool command line.
110
if (aFlags & DirectoryCreateFlag_Parents)
111
args.push_back(Bstr("--parents").raw()); /* We also want to create the parent directories. */
114
args.push_back(Bstr("--mode").raw()); /* Set the creation mode. */
117
RTStrPrintf(szMode, sizeof(szMode), "%o", aMode);
118
args.push_back(Bstr(szMode).raw());
120
args.push_back(Bstr(Utf8Directory).raw()); /* The directory we want to create. */
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 */);
128
catch (std::bad_alloc &)
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).
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.
147
int Guest::directoryCreateHandle(ULONG *puHandle, ULONG uPID,
148
IN_BSTR aDirectory, IN_BSTR aFilter, ULONG uFlags)
150
AssertReturn(uPID, VERR_INVALID_PARAMETER);
151
CheckComArgStrNotEmptyOrNull(aDirectory);
152
/* aFilter is optional. */
153
/* uFlags are optional. */
155
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
157
int rc = VERR_TOO_MUCH_DATA;
158
for (uint32_t i = 0; i < UINT32_MAX - 1; i++)
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 ... */
165
mGuestDirectoryMap[uHandleTry].mDirectory = aDirectory;
166
mGuestDirectoryMap[uHandleTry].mFilter = aFilter;
167
mGuestDirectoryMap[uHandleTry].mPID = uPID;
168
mGuestDirectoryMap[uHandleTry].mFlags = uFlags;
169
Assert(mGuestDirectoryMap.size());
174
*puHandle = uHandleTry;
183
* Destroys a previously created directory handle and its
186
* @return IPRT status code.
187
* @param uHandle Handle to destroy.
189
void Guest::directoryDestroyHandle(uint32_t uHandle)
191
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
193
GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
194
if (it != mGuestDirectoryMap.end())
196
/* Destroy raw guest stream buffer - not used
198
it->second.mStream.Destroy();
200
/* Remove callback context (not used anymore). */
201
mGuestDirectoryMap.erase(it);
206
STDMETHODIMP Guest::DirectoryExists(IN_BSTR aDirectory, IN_BSTR aUsername, IN_BSTR aPassword, BOOL *aExists)
208
#ifndef VBOX_WITH_GUEST_CONTROL
209
ReturnComNotImplemented();
210
#else /* VBOX_WITH_GUEST_CONTROL */
211
using namespace guestControl;
213
CheckComArgStrNotEmptyOrNull(aDirectory);
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"));
219
return directoryExistsInternal(aDirectory,
220
aUsername, aPassword, aExists);
225
#ifdef VBOX_WITH_GUEST_CONTROL
226
HRESULT Guest::directoryExistsInternal(IN_BSTR aDirectory, IN_BSTR aUsername, IN_BSTR aPassword, BOOL *aExists)
228
using namespace guestControl;
230
CheckComArgStrNotEmptyOrNull(aDirectory);
232
AutoCaller autoCaller(this);
233
if (FAILED(autoCaller.rc())) return autoCaller.rc();
236
HRESULT hr = directoryQueryInfoInternal(aDirectory,
237
aUsername, aPassword,
238
NULL /* No RTFSOBJINFO needed */,
239
RTFSOBJATTRADD_NOTHING, &rc);
248
case VERR_FILE_NOT_FOUND:
253
rc = setError(VBOX_E_IPRT_ERROR,
254
Guest::tr("Unable to query directory existence"));
258
AssertReleaseMsgFailed(("directoryExistsInternal: Unknown return value (%Rrc)\n", rc));
267
* Gets the associated PID from a directory handle.
269
* @return uint32_t Associated PID, 0 if handle not found/invalid.
270
* @param uHandle Directory handle to get PID for.
272
uint32_t Guest::directoryGetPID(uint32_t uHandle)
274
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
276
GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
277
if (it != mGuestDirectoryMap.end())
278
return it->second.mPID;
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.
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.
292
int Guest::directoryGetNextEntry(uint32_t uHandle, GuestProcessStreamBlock &streamBlock)
294
// LOCK DOES NOT WORK HERE!?
295
//AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
297
GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
298
if (it != mGuestDirectoryMap.end())
300
return executeStreamGetNextBlock(it->second.mPID,
301
it->second.mStream, streamBlock);
304
return VERR_NOT_FOUND;
308
* Checks whether a specified directory handle exists (is valid)
311
* @return bool True if handle exists, false if not.
312
* @param uHandle Directory handle to check.
314
bool Guest::directoryHandleExists(uint32_t uHandle)
316
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
318
GuestDirectoryMapIter it = mGuestDirectoryMap.find(uHandle);
319
if (it != mGuestDirectoryMap.end())
324
#endif /* VBOX_WITH_GUEST_CONTROL */
326
STDMETHODIMP Guest::DirectoryOpen(IN_BSTR aDirectory, IN_BSTR aFilter,
327
ULONG aFlags, IN_BSTR aUserName, IN_BSTR aPassword,
330
#ifndef VBOX_WITH_GUEST_CONTROL
331
ReturnComNotImplemented();
332
#else /* VBOX_WITH_GUEST_CONTROL */
333
using namespace guestControl;
335
CheckComArgStrNotEmptyOrNull(aDirectory);
336
CheckComArgNotNull(aHandle);
338
/* Do not allow anonymous executions (with system rights). */
339
if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
340
return setError(E_INVALIDARG, tr("No user name specified"));
342
return directoryOpenInternal(aDirectory, aFilter,
344
aUserName, aPassword,
345
aHandle, NULL /* rc */);
349
#ifdef VBOX_WITH_GUEST_CONTROL
350
HRESULT Guest::directoryOpenInternal(IN_BSTR aDirectory, IN_BSTR aFilter,
352
IN_BSTR aUsername, IN_BSTR aPassword,
353
ULONG *aHandle, int *pRC)
355
using namespace guestControl;
357
CheckComArgStrNotEmptyOrNull(aDirectory);
358
CheckComArgNotNull(aHandle);
360
AutoCaller autoCaller(this);
361
if (FAILED(autoCaller.rc())) return autoCaller.rc();
363
/* Validate flags. No flags supported yet. */
364
if (aFlags != DirectoryOpenFlag_None)
365
return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
370
Utf8Str Utf8Directory(aDirectory);
371
Utf8Str Utf8Filter(aFilter);
373
com::SafeArray<IN_BSTR> args;
374
com::SafeArray<IN_BSTR> env;
377
* Prepare tool command line.
380
/* We need to get output which is machine-readable in form
381
* of "key=value\0..key=value\0\0". */
382
args.push_back(Bstr("--machinereadable").raw());
384
/* We want the long output format. Handy for getting a lot of
385
* details we could (should?) use (later). */
386
args.push_back(Bstr("-l").raw());
388
/* As we want to keep this stuff simple we don't do recursive (-R)
389
* or dereferencing (--dereference) lookups here. This has to be done by
392
/* Construct and hand in actual directory name + filter we want to open. */
393
char *pszDirectoryFinal;
395
if (Utf8Filter.isEmpty())
396
cbRet = RTStrAPrintf(&pszDirectoryFinal, "%s", Utf8Directory.c_str());
398
cbRet = RTStrAPrintf(&pszDirectoryFinal, "%s/%s",
399
Utf8Directory.c_str(), Utf8Filter.c_str());
401
return setError(E_OUTOFMEMORY, tr("Out of memory while allocating final directory"));
403
args.push_back(Bstr(pszDirectoryFinal).raw()); /* The directory we want to open. */
406
/** @todo Don't wait for tool to finish! Might take a lot of time! */
407
hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_LS).raw(), Bstr("Opening directory").raw(),
408
ComSafeArrayAsInParam(args),
409
ComSafeArrayAsInParam(env),
410
aUsername, aPassword,
411
NULL /* Progress */, &uPID);
414
/* Assign new directory handle ID. */
416
int vrc = directoryCreateHandle(&uHandleNew, uPID,
417
aDirectory, aFilter, aFlags);
420
*aHandle = uHandleNew;
423
hr = setError(VBOX_E_IPRT_ERROR,
424
tr("Unable to create guest directory handle (%Rrc)"), vrc);
427
catch (std::bad_alloc &)
434
HRESULT Guest::directoryQueryInfoInternal(IN_BSTR aDirectory,
435
IN_BSTR aUsername, IN_BSTR aPassword,
436
PRTFSOBJINFO aObjInfo, RTFSOBJATTRADD enmAddAttribs,
439
using namespace guestControl;
441
/** @todo Search directory cache first? */
443
CheckComArgStrNotEmptyOrNull(aDirectory);
444
/* aUsername is optional. */
445
/* aPassword is optional. */
446
/* aObjInfo is optional. */
448
AutoCaller autoCaller(this);
449
if (FAILED(autoCaller.rc())) return autoCaller.rc();
454
Utf8Str Utf8Dir(aDirectory);
455
Utf8Str Utf8Username(aUsername);
456
Utf8Str Utf8Password(aPassword);
458
com::SafeArray<IN_BSTR> args;
459
com::SafeArray<IN_BSTR> env;
462
* Prepare tool command line.
465
/* We need to get output which is machine-readable in form
466
* of "key=value\0..key=value\0\0". */
467
args.push_back(Bstr("--machinereadable").raw());
469
/* Only the actual file name to chekc is needed for now. */
470
args.push_back(Bstr(Utf8Dir).raw());
473
* Execute guest process.
476
hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_STAT).raw(), Bstr("Querying directory information").raw(),
477
ComSafeArrayAsInParam(args),
478
ComSafeArrayAsInParam(env),
479
aUsername, aPassword,
480
NULL /* Progress */, &uPID);
483
GuestCtrlStreamObjects streamObjs;
484
hr = executeStreamParse(uPID, streamObjs);
487
int rc = VINF_SUCCESS;
489
Assert(streamObjs.size());
490
const char *pszFsType = streamObjs[0].GetString("ftype");
491
if (!pszFsType) /* Attribute missing? */
494
&& strcmp(pszFsType, "d")) /* Directory? */
496
rc = VERR_FILE_NOT_FOUND;
497
/* This is not critical for Main, so don't set hr --
498
* we will take care of rc then. */
501
&& aObjInfo) /* Do we want object details? */
503
hr = executeStreamQueryFsObjInfo(aDirectory, streamObjs[0],
504
aObjInfo, enmAddAttribs);
512
catch (std::bad_alloc &)
518
#endif /* VBOX_WITH_GUEST_CONTROL */
520
STDMETHODIMP Guest::DirectoryRead(ULONG aHandle, IGuestDirEntry **aDirEntry)
522
#ifndef VBOX_WITH_GUEST_CONTROL
523
ReturnComNotImplemented();
524
#else /* VBOX_WITH_GUEST_CONTROL */
525
using namespace guestControl;
527
CheckComArgOutPointerValid(aDirEntry);
529
AutoCaller autoCaller(this);
530
if (FAILED(autoCaller.rc())) return autoCaller.rc();
535
GuestProcessStreamBlock streamBlock;
536
int rc = directoryGetNextEntry(aHandle, streamBlock);
539
ComObjPtr <GuestDirEntry> pDirEntry;
540
hr = pDirEntry.createObject();
543
Assert(streamBlock.GetCount());
544
hr = pDirEntry->init(this, streamBlock);
547
pDirEntry.queryInterfaceTo(aDirEntry);
550
hr = setError(VBOX_E_IPRT_ERROR,
551
Guest::tr("Failed to init guest directory entry"));
553
else if (rc == VERR_NO_DATA)
555
/* No more directory entries to read. That's fine. */
556
hr = E_ABORT; /** @todo Find/define a better rc! */
559
hr = setError(VBOX_E_IPRT_ERROR,
560
Guest::tr("Failed getting next directory entry (%Rrc)"), rc);
562
catch (std::bad_alloc &)