VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp@ 83826

Last change on this file since 83826 was 83651, checked in by vboxsync, 5 years ago

Guest Control/VBoxSerivce: Added an own vbox_stat toolbox exit code for INVALID_NAME. bugref:9320

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 64.1 KB
Line 
1/* $Id: VBoxServiceToolBox.cpp 83651 2020-04-09 15:06:32Z vboxsync $ */
2/** @file
3 * VBoxServiceToolbox - Internal (BusyBox-like) toolbox.
4 */
5
6/*
7 * Copyright (C) 2012-2020 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
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <stdio.h>
23
24#include <iprt/assert.h>
25#include <iprt/buildconfig.h>
26#include <iprt/dir.h>
27#include <iprt/file.h>
28#include <iprt/getopt.h>
29#include <iprt/list.h>
30#include <iprt/mem.h>
31#include <iprt/message.h>
32#include <iprt/path.h>
33#include <iprt/string.h>
34#include <iprt/stream.h>
35#include <iprt/symlink.h>
36
37#ifndef RT_OS_WINDOWS
38# include <sys/stat.h> /* need umask */
39#endif
40
41#include <VBox/VBoxGuestLib.h>
42#include <VBox/version.h>
43
44#include <VBox/GuestHost/GuestControl.h>
45
46#include "VBoxServiceInternal.h"
47#include "VBoxServiceToolBox.h"
48#include "VBoxServiceUtils.h"
49
50using namespace guestControl;
51
52
53/*********************************************************************************************************************************
54* Defined Constants And Macros *
55*********************************************************************************************************************************/
56
57/** Generic option indices for commands. */
58enum
59{
60 VBOXSERVICETOOLBOXOPT_MACHINE_READABLE = 1000,
61 VBOXSERVICETOOLBOXOPT_VERBOSE
62};
63
64/** Options indices for "vbox_cat". */
65typedef enum VBOXSERVICETOOLBOXCATOPT
66{
67 VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED = 1000
68} VBOXSERVICETOOLBOXCATOPT;
69
70/** Flags for "vbox_ls". */
71typedef enum VBOXSERVICETOOLBOXLSFLAG
72{
73 VBOXSERVICETOOLBOXLSFLAG_NONE,
74 VBOXSERVICETOOLBOXLSFLAG_RECURSIVE,
75 VBOXSERVICETOOLBOXLSFLAG_SYMLINKS
76} VBOXSERVICETOOLBOXLSFLAG;
77
78/** Flags for fs object output. */
79typedef enum VBOXSERVICETOOLBOXOUTPUTFLAG
80{
81 VBOXSERVICETOOLBOXOUTPUTFLAG_NONE,
82 VBOXSERVICETOOLBOXOUTPUTFLAG_LONG,
83 VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE
84} VBOXSERVICETOOLBOXOUTPUTFLAG;
85
86/** The size of the directory entry buffer we're using. */
87#define VBOXSERVICETOOLBOX_DIRENTRY_BUF_SIZE (sizeof(RTDIRENTRYEX) + RTPATH_MAX)
88
89
90/*********************************************************************************************************************************
91* Structures and Typedefs *
92*********************************************************************************************************************************/
93/** Pointer to a tool handler function. */
94typedef RTEXITCODE (*PFNHANDLER)(int , char **);
95
96/** Definition for a specific toolbox tool. */
97typedef struct VBOXSERVICETOOLBOXTOOL
98{
99 /** Friendly name of the tool. */
100 const char *pszName;
101 /** Main handler to be invoked to use the tool. */
102 RTEXITCODE (*pfnHandler)(int argc, char **argv);
103 /** Conversion routine to convert the tool's exit code back to an IPRT rc. Optional.
104 *
105 * @todo r=bird: You better revert this, i.e. having pfnHandler return a VBox
106 * status code and have a routine for converting it to RTEXITCODE.
107 * Unless, what you really want to do here is to get a cached status, in
108 * which case you better call it what it is.
109 */
110 int (*pfnExitCodeConvertToRc)(RTEXITCODE rcExit);
111} VBOXSERVICETOOLBOXTOOL;
112/** Pointer to a const tool definition. */
113typedef VBOXSERVICETOOLBOXTOOL const *PCVBOXSERVICETOOLBOXTOOL;
114
115/**
116 * An file/directory entry. Used to cache
117 * file names/paths for later processing.
118 */
119typedef struct VBOXSERVICETOOLBOXPATHENTRY
120{
121 /** Our node. */
122 RTLISTNODE Node;
123 /** Name of the entry. */
124 char *pszName;
125} VBOXSERVICETOOLBOXPATHENTRY, *PVBOXSERVICETOOLBOXPATHENTRY;
126
127/** ID cache entry. */
128typedef struct VGSVCTOOLBOXUIDENTRY
129{
130 /** The identifier name. */
131 uint32_t id;
132 /** Set if UID, clear if GID. */
133 bool fIsUid;
134 /** The name. */
135 char szName[128 - 4 - 1];
136} VGSVCTOOLBOXUIDENTRY;
137typedef VGSVCTOOLBOXUIDENTRY *PVGSVCTOOLBOXUIDENTRY;
138
139
140/** ID cache. */
141typedef struct VGSVCTOOLBOXIDCACHE
142{
143 /** Number of valid cache entries. */
144 uint32_t cEntries;
145 /** The next entry to replace. */
146 uint32_t iNextReplace;
147 /** The cache entries. */
148 VGSVCTOOLBOXUIDENTRY aEntries[16];
149} VGSVCTOOLBOXIDCACHE;
150typedef VGSVCTOOLBOXIDCACHE *PVGSVCTOOLBOXIDCACHE;
151
152
153/*********************************************************************************************************************************
154* Internal Functions *
155*********************************************************************************************************************************/
156static RTEXITCODE vgsvcToolboxCat(int argc, char **argv);
157static RTEXITCODE vgsvcToolboxLs(int argc, char **argv);
158static RTEXITCODE vgsvcToolboxRm(int argc, char **argv);
159static RTEXITCODE vgsvcToolboxMkTemp(int argc, char **argv);
160static RTEXITCODE vgsvcToolboxMkDir(int argc, char **argv);
161static RTEXITCODE vgsvcToolboxStat(int argc, char **argv);
162
163
164/*********************************************************************************************************************************
165* Global Variables *
166*********************************************************************************************************************************/
167/** Tool definitions. */
168static VBOXSERVICETOOLBOXTOOL const g_aTools[] =
169{
170 { VBOXSERVICE_TOOL_CAT, vgsvcToolboxCat , NULL },
171 { VBOXSERVICE_TOOL_LS, vgsvcToolboxLs , NULL },
172 { VBOXSERVICE_TOOL_RM, vgsvcToolboxRm , NULL },
173 { VBOXSERVICE_TOOL_MKTEMP, vgsvcToolboxMkTemp, NULL },
174 { VBOXSERVICE_TOOL_MKDIR, vgsvcToolboxMkDir , NULL },
175 { VBOXSERVICE_TOOL_STAT, vgsvcToolboxStat , NULL }
176};
177
178
179
180
181/**
182 * Displays a common header for all help text to stdout.
183 */
184static void vgsvcToolboxShowUsageHeader(void)
185{
186 RTPrintf(VBOX_PRODUCT " Guest Toolbox Version "
187 VBOX_VERSION_STRING "\n"
188 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
189 "All rights reserved.\n"
190 "\n");
191 RTPrintf("Usage:\n\n");
192}
193
194
195/**
196 * Displays a help text to stdout.
197 */
198static void vgsvcToolboxShowUsage(void)
199{
200 vgsvcToolboxShowUsageHeader();
201 RTPrintf(" VBoxService [--use-toolbox] vbox_<command> [<general options>] <parameters>\n\n"
202 "General options:\n\n"
203 " --machinereadable produce all output in machine-readable form\n"
204 " -V print version number and exit\n"
205 "\n"
206 "Commands:\n\n"
207 " vbox_cat [<general options>] <file>...\n"
208 " vbox_ls [<general options>] [--dereference|-L] [-l] [-R]\n"
209 " [--verbose|-v] [<file>...]\n"
210 " vbox_rm [<general options>] [-r|-R] <file>...\n"
211 " vbox_mktemp [<general options>] [--directory|-d] [--mode|-m <mode>]\n"
212 " [--secure|-s] [--tmpdir|-t <path>] <template>\n"
213 " vbox_mkdir [<general options>] [--mode|-m <mode>] [--parents|-p]\n"
214 " [--verbose|-v] <directory>...\n"
215 " vbox_stat [<general options>] [--file-system|-f]\n"
216 " [--dereference|-L] [--terse|-t] [--verbose|-v] <file>...\n"
217 "\n");
218}
219
220
221/**
222 * Displays the program's version number.
223 */
224static void vgsvcToolboxShowVersion(void)
225{
226 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
227}
228
229
230/**
231 * Initializes the parseable stream(s).
232 *
233 * @return IPRT status code.
234 */
235static int vgsvcToolboxStrmInit(void)
236{
237 /* Set stdout's mode to binary. This is required for outputting all the machine-readable
238 * data correctly. */
239 int rc = RTStrmSetMode(g_pStdOut, 1 /* Binary mode */, -1 /* Current code set, not changed */);
240 if (RT_FAILURE(rc))
241 RTMsgError("Unable to set stdout to binary mode, rc=%Rrc\n", rc);
242
243 return rc;
244}
245
246
247/**
248 * Prints a parseable stream header which contains the actual tool
249 * which was called/used along with its stream version.
250 *
251 * @param pszToolName Name of the tool being used, e.g. "vbt_ls".
252 * @param uVersion Stream version name. Handy for distinguishing
253 * different stream versions later.
254 */
255static void vgsvcToolboxPrintStrmHeader(const char *pszToolName, uint32_t uVersion)
256{
257 AssertPtrReturnVoid(pszToolName);
258 RTPrintf("hdr_id=%s%chdr_ver=%u%c", pszToolName, 0, uVersion, 0);
259}
260
261
262/**
263 * Prints a standardized termination sequence indicating that the
264 * parseable stream just ended.
265 *
266 */
267static void vgsvcToolboxPrintStrmTermination()
268{
269 RTPrintf("%c%c%c%c", 0, 0, 0, 0);
270}
271
272
273/**
274 * Parse a file mode string from the command line (currently octal only)
275 * and print an error message and return an error if necessary.
276 */
277static int vgsvcToolboxParseMode(const char *pcszMode, RTFMODE *pfMode)
278{
279 int rc = RTStrToUInt32Ex(pcszMode, NULL, 8 /* Base */, pfMode);
280 if (RT_FAILURE(rc)) /* Only octet based values supported right now! */
281 RTMsgError("Mode flag strings not implemented yet! Use octal numbers instead. (%s)\n", pcszMode);
282 return rc;
283}
284
285
286/**
287 * Destroys a path buffer list.
288 *
289 * @return IPRT status code.
290 * @param pList Pointer to list to destroy.
291 */
292static void vgsvcToolboxPathBufDestroy(PRTLISTNODE pList)
293{
294 if (!pList)
295 return;
296
297 PVBOXSERVICETOOLBOXPATHENTRY pEntry, pEntryNext;
298 RTListForEachSafe(pList, pEntry, pEntryNext, VBOXSERVICETOOLBOXPATHENTRY, Node)
299 {
300 RTListNodeRemove(&pEntry->Node);
301
302 RTStrFree(pEntry->pszName);
303 RTMemFree(pEntry);
304 }
305}
306
307
308/**
309 * Adds a path entry (file/directory/whatever) to a given path buffer list.
310 *
311 * @return IPRT status code.
312 * @param pList Pointer to list to add entry to.
313 * @param pszName Name of entry to add.
314 */
315static int vgsvcToolboxPathBufAddPathEntry(PRTLISTNODE pList, const char *pszName)
316{
317 AssertPtrReturn(pList, VERR_INVALID_PARAMETER);
318
319 int rc = VINF_SUCCESS;
320 PVBOXSERVICETOOLBOXPATHENTRY pNode = (PVBOXSERVICETOOLBOXPATHENTRY)RTMemAlloc(sizeof(VBOXSERVICETOOLBOXPATHENTRY));
321 if (pNode)
322 {
323 pNode->pszName = RTStrDup(pszName);
324 AssertPtr(pNode->pszName);
325
326 RTListAppend(pList, &pNode->Node);
327 }
328 else
329 rc = VERR_NO_MEMORY;
330 return rc;
331}
332
333
334/**
335 * Performs the actual output operation of "vbox_cat".
336 *
337 * @return IPRT status code.
338 * @param hInput Handle of input file (if any) to use;
339 * else stdin will be used.
340 * @param hOutput Handle of output file (if any) to use;
341 * else stdout will be used.
342 */
343static int vgsvcToolboxCatOutput(RTFILE hInput, RTFILE hOutput)
344{
345 int rc = VINF_SUCCESS;
346 if (hInput == NIL_RTFILE)
347 {
348 rc = RTFileFromNative(&hInput, RTFILE_NATIVE_STDIN);
349 if (RT_FAILURE(rc))
350 RTMsgError("Could not translate input file to native handle, rc=%Rrc\n", rc);
351 }
352
353 if (hOutput == NIL_RTFILE)
354 {
355 rc = RTFileFromNative(&hOutput, RTFILE_NATIVE_STDOUT);
356 if (RT_FAILURE(rc))
357 RTMsgError("Could not translate output file to native handle, rc=%Rrc\n", rc);
358 }
359
360 if (RT_SUCCESS(rc))
361 {
362 uint8_t abBuf[_64K];
363 size_t cbRead;
364 for (;;)
365 {
366 rc = RTFileRead(hInput, abBuf, sizeof(abBuf), &cbRead);
367 if (RT_SUCCESS(rc) && cbRead > 0)
368 {
369 rc = RTFileWrite(hOutput, abBuf, cbRead, NULL /* Try to write all at once! */);
370 if (RT_FAILURE(rc))
371 {
372 RTMsgError("Error while writing output, rc=%Rrc\n", rc);
373 break;
374 }
375 }
376 else
377 {
378 if (rc == VERR_BROKEN_PIPE)
379 rc = VINF_SUCCESS;
380 else if (RT_FAILURE(rc))
381 RTMsgError("Error while reading input, rc=%Rrc\n", rc);
382 break;
383 }
384 }
385 }
386 return rc;
387}
388
389
390/** @todo Document options! */
391static char g_paszCatHelp[] =
392 " VBoxService [--use-toolbox] vbox_cat [<general options>] <file>...\n\n"
393 "Concatenate files, or standard input, to standard output.\n"
394 "\n";
395
396
397/**
398 * Main function for tool "vbox_cat".
399 *
400 * @return RTEXITCODE.
401 * @param argc Number of arguments.
402 * @param argv Pointer to argument array.
403 */
404static RTEXITCODE vgsvcToolboxCat(int argc, char **argv)
405{
406 static const RTGETOPTDEF s_aOptions[] =
407 {
408 /* Sorted by short ops. */
409 { "--show-all", 'a', RTGETOPT_REQ_NOTHING },
410 { "--number-nonblank", 'b', RTGETOPT_REQ_NOTHING},
411 { NULL, 'e', RTGETOPT_REQ_NOTHING},
412 { NULL, 'E', RTGETOPT_REQ_NOTHING},
413 { "--flags", 'f', RTGETOPT_REQ_STRING},
414 { "--no-content-indexed", VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED, RTGETOPT_REQ_NOTHING},
415 { "--number", 'n', RTGETOPT_REQ_NOTHING},
416 { "--output", 'o', RTGETOPT_REQ_STRING},
417 { "--squeeze-blank", 's', RTGETOPT_REQ_NOTHING},
418 { NULL, 't', RTGETOPT_REQ_NOTHING},
419 { "--show-tabs", 'T', RTGETOPT_REQ_NOTHING},
420 { NULL, 'u', RTGETOPT_REQ_NOTHING},
421 { "--show-noneprinting", 'v', RTGETOPT_REQ_NOTHING}
422 };
423
424 int ch;
425 RTGETOPTUNION ValueUnion;
426 RTGETOPTSTATE GetState;
427
428 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /*iFirst*/, 0 /*fFlags*/);
429
430 int rc = VINF_SUCCESS;
431
432 const char *pszOutput = NULL;
433 RTFILE hOutput = NIL_RTFILE;
434 uint32_t fFlags = RTFILE_O_CREATE_REPLACE /* Output file flags. */
435 | RTFILE_O_WRITE
436 | RTFILE_O_DENY_WRITE;
437
438 /* Init directory list. */
439 RTLISTANCHOR inputList;
440 RTListInit(&inputList);
441
442 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
443 && RT_SUCCESS(rc))
444 {
445 /* For options that require an argument, ValueUnion has received the value. */
446 switch (ch)
447 {
448 case 'a':
449 case 'b':
450 case 'e':
451 case 'E':
452 case 'n':
453 case 's':
454 case 't':
455 case 'T':
456 case 'v':
457 RTMsgError("Sorry, option '%s' is not implemented yet!\n",
458 ValueUnion.pDef->pszLong);
459 rc = VERR_INVALID_PARAMETER;
460 break;
461
462 case 'h':
463 vgsvcToolboxShowUsageHeader();
464 RTPrintf("%s", g_paszCatHelp);
465 return RTEXITCODE_SUCCESS;
466
467 case 'o':
468 pszOutput = ValueUnion.psz;
469 break;
470
471 case 'u':
472 /* Ignored. */
473 break;
474
475 case 'V':
476 vgsvcToolboxShowVersion();
477 return RTEXITCODE_SUCCESS;
478
479 case VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED:
480 fFlags |= RTFILE_O_NOT_CONTENT_INDEXED;
481 break;
482
483 case VINF_GETOPT_NOT_OPTION:
484 /* Add file(s) to buffer. This enables processing multiple paths
485 * at once.
486 *
487 * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
488 * processing this loop it's safe to immediately exit on syntax errors
489 * or showing the help text (see above). */
490 rc = vgsvcToolboxPathBufAddPathEntry(&inputList, ValueUnion.psz);
491 break;
492
493 default:
494 return RTGetOptPrintError(ch, &ValueUnion);
495 }
496 }
497
498 if (RT_SUCCESS(rc))
499 {
500 if (pszOutput)
501 {
502 rc = RTFileOpen(&hOutput, pszOutput, fFlags);
503 if (RT_FAILURE(rc))
504 RTMsgError("Could not create output file '%s', rc=%Rrc\n", pszOutput, rc);
505 }
506
507 if (RT_SUCCESS(rc))
508 {
509 /* Process each input file. */
510 RTFILE hInput = NIL_RTFILE;
511 PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
512 RTListForEach(&inputList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
513 {
514 rc = RTFileOpen(&hInput, pNodeIt->pszName,
515 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
516 if (RT_SUCCESS(rc))
517 {
518 rc = vgsvcToolboxCatOutput(hInput, hOutput);
519 RTFileClose(hInput);
520 }
521 else
522 {
523 PCRTSTATUSMSG pMsg = RTErrGet(rc);
524 if (pMsg)
525 RTMsgError("Could not open input file '%s': %s\n", pNodeIt->pszName, pMsg->pszMsgFull);
526 else
527 RTMsgError("Could not open input file '%s', rc=%Rrc\n", pNodeIt->pszName, rc);
528 }
529
530 if (RT_FAILURE(rc))
531 break;
532 }
533
534 /* If no input files were defined, process stdin. */
535 if (RTListNodeIsFirst(&inputList, &inputList))
536 rc = vgsvcToolboxCatOutput(hInput, hOutput);
537 }
538 }
539
540 if (hOutput != NIL_RTFILE)
541 RTFileClose(hOutput);
542 vgsvcToolboxPathBufDestroy(&inputList);
543
544 if (RT_FAILURE(rc))
545 {
546 switch (rc)
547 {
548 case VERR_ACCESS_DENIED:
549 return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_ACCESS_DENIED;
550
551 case VERR_FILE_NOT_FOUND:
552 return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_FILE_NOT_FOUND;
553
554 case VERR_PATH_NOT_FOUND:
555 return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_PATH_NOT_FOUND;
556
557 case VERR_SHARING_VIOLATION:
558 return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_SHARING_VIOLATION;
559
560 case VERR_IS_A_DIRECTORY:
561 return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_IS_A_DIRECTORY;
562
563 default:
564#ifdef DEBUG_andy
565 AssertMsgFailed(("Exit code for %Rrc not implemented\n", rc));
566#endif
567 break;
568 }
569
570 return RTEXITCODE_FAILURE;
571 }
572
573 return RTEXITCODE_SUCCESS;
574}
575
576
577/**
578 * Resolves the UID to a name as best as we can.
579 *
580 * @returns Read-only name string. Only valid till the next cache call.
581 * @param pIdCache The ID cache.
582 * @param uid The UID to resolve.
583 * @param pszEntry The filename of the UID.
584 * @param pszRelativeTo What @a pszEntry is relative to, NULL if absolute.
585 */
586static const char *vgsvcToolboxIdCacheGetUidName(PVGSVCTOOLBOXIDCACHE pIdCache, RTUID uid,
587 const char *pszEntry, const char *pszRelativeTo)
588{
589 /* Check cached entries. */
590 for (uint32_t i = 0; i < pIdCache->cEntries; i++)
591 if ( pIdCache->aEntries[i].id == uid
592 && pIdCache->aEntries[i].fIsUid)
593 return pIdCache->aEntries[i].szName;
594
595 /* Miss. */
596 RTFSOBJINFO ObjInfo;
597 RT_ZERO(ObjInfo); /* shut up msc */
598 int rc;
599 if (!pszRelativeTo)
600 rc = RTPathQueryInfoEx(pszEntry, &ObjInfo, RTFSOBJATTRADD_UNIX_OWNER, RTPATH_F_ON_LINK);
601 else
602 {
603 char szPath[RTPATH_MAX];
604 rc = RTPathJoin(szPath, sizeof(szPath), pszRelativeTo, pszEntry);
605 if (RT_SUCCESS(rc))
606 rc = RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_UNIX_OWNER, RTPATH_F_ON_LINK);
607 }
608
609 if ( RT_SUCCESS(rc)
610 && ObjInfo.Attr.u.UnixOwner.uid == uid)
611 {
612 uint32_t i = pIdCache->cEntries;
613 if (i < RT_ELEMENTS(pIdCache->aEntries))
614 pIdCache->cEntries = i + 1;
615 else
616 i = pIdCache->iNextReplace++ % RT_ELEMENTS(pIdCache->aEntries);
617 pIdCache->aEntries[i].id = uid;
618 pIdCache->aEntries[i].fIsUid = true;
619 RTStrCopy(pIdCache->aEntries[i].szName, sizeof(pIdCache->aEntries[i].szName), ObjInfo.Attr.u.UnixOwner.szName);
620 return pIdCache->aEntries[i].szName;
621 }
622 return "";
623}
624
625
626/**
627 * Resolves the GID to a name as best as we can.
628 *
629 * @returns Read-only name string. Only valid till the next cache call.
630 * @param pIdCache The ID cache.
631 * @param gid The GID to resolve.
632 * @param pszEntry The filename of the GID.
633 * @param pszRelativeTo What @a pszEntry is relative to, NULL if absolute.
634 */
635static const char *vgsvcToolboxIdCacheGetGidName(PVGSVCTOOLBOXIDCACHE pIdCache, RTGID gid,
636 const char *pszEntry, const char *pszRelativeTo)
637{
638 /* Check cached entries. */
639 for (uint32_t i = 0; i < pIdCache->cEntries; i++)
640 if ( pIdCache->aEntries[i].id == gid
641 && !pIdCache->aEntries[i].fIsUid)
642 return pIdCache->aEntries[i].szName;
643
644 /* Miss. */
645 RTFSOBJINFO ObjInfo;
646 RT_ZERO(ObjInfo); /* shut up msc */
647 int rc;
648 if (!pszRelativeTo)
649 rc = RTPathQueryInfoEx(pszEntry, &ObjInfo, RTFSOBJATTRADD_UNIX_GROUP, RTPATH_F_ON_LINK);
650 else
651 {
652 char szPath[RTPATH_MAX];
653 rc = RTPathJoin(szPath, sizeof(szPath), pszRelativeTo, pszEntry);
654 if (RT_SUCCESS(rc))
655 rc = RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_UNIX_GROUP, RTPATH_F_ON_LINK);
656 }
657
658 if ( RT_SUCCESS(rc)
659 && ObjInfo.Attr.u.UnixGroup.gid == gid)
660 {
661 uint32_t i = pIdCache->cEntries;
662 if (i < RT_ELEMENTS(pIdCache->aEntries))
663 pIdCache->cEntries = i + 1;
664 else
665 i = pIdCache->iNextReplace++ % RT_ELEMENTS(pIdCache->aEntries);
666 pIdCache->aEntries[i].id = gid;
667 pIdCache->aEntries[i].fIsUid = false;
668 RTStrCopy(pIdCache->aEntries[i].szName, sizeof(pIdCache->aEntries[i].szName), ObjInfo.Attr.u.UnixGroup.szName);
669 return pIdCache->aEntries[i].szName;
670 }
671 return "";
672}
673
674
675/**
676 * Prints information (based on given flags) of a file system object (file/directory/...)
677 * to stdout.
678 *
679 * @return IPRT status code.
680 * @param pszName Object name.
681 * @param cchName Length of pszName.
682 * @param fOutputFlags Output / handling flags of type
683 * VBOXSERVICETOOLBOXOUTPUTFLAG.
684 * @param pszRelativeTo What pszName is relative to.
685 * @param pIdCache The ID cache.
686 * @param pObjInfo Pointer to object information.
687 */
688static int vgsvcToolboxPrintFsInfo(const char *pszName, size_t cchName, uint32_t fOutputFlags, const char *pszRelativeTo,
689 PVGSVCTOOLBOXIDCACHE pIdCache, PRTFSOBJINFO pObjInfo)
690{
691 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
692 AssertReturn(cchName, VERR_INVALID_PARAMETER);
693 AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
694
695 RTFMODE fMode = pObjInfo->Attr.fMode;
696 char chFileType;
697 switch (fMode & RTFS_TYPE_MASK)
698 {
699 case RTFS_TYPE_FIFO: chFileType = 'f'; break;
700 case RTFS_TYPE_DEV_CHAR: chFileType = 'c'; break;
701 case RTFS_TYPE_DIRECTORY: chFileType = 'd'; break;
702 case RTFS_TYPE_DEV_BLOCK: chFileType = 'b'; break;
703 case RTFS_TYPE_FILE: chFileType = '-'; break;
704 case RTFS_TYPE_SYMLINK: chFileType = 'l'; break;
705 case RTFS_TYPE_SOCKET: chFileType = 's'; break;
706 case RTFS_TYPE_WHITEOUT: chFileType = 'w'; break;
707 default: chFileType = '?'; break;
708 }
709 /** @todo sticy bits++ */
710
711/** @todo r=bird: turns out the host doesn't use or need cname_len, so perhaps we could drop it? */
712 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_LONG))
713 {
714 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
715 {
716 RTPrintf("ftype=%c%cnode_id=%RU64%inode_dev=%RU32%ccname_len=%zu%cname=%s%c",
717 chFileType, 0, (uint64_t)pObjInfo->Attr.u.Unix.INodeId, 0,
718 (uint32_t)pObjInfo->Attr.u.Unix.INodeIdDevice, 0, cchName, 0, pszName, 0);
719 RTPrintf("%c%c", 0, 0);
720 }
721 else
722 RTPrintf("%c %#18llx %3zu %s\n", chFileType, (uint64_t)pObjInfo->Attr.u.Unix.INodeId, cchName, pszName);
723 }
724 else
725 {
726 char szTimeBirth[RTTIME_STR_LEN];
727 char szTimeChange[RTTIME_STR_LEN];
728 char szTimeModification[RTTIME_STR_LEN];
729 char szTimeAccess[RTTIME_STR_LEN];
730
731 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
732 {
733 RTPrintf("ftype=%c%c", chFileType, 0);
734 if (pObjInfo->Attr.u.Unix.INodeId || pObjInfo->Attr.u.Unix.INodeIdDevice)
735 RTPrintf("node_id=%RU64%cinode_dev=%RU32%c", (uint64_t)pObjInfo->Attr.u.Unix.INodeId, 0,
736 (uint32_t)pObjInfo->Attr.u.Unix.INodeIdDevice, 0);
737 RTPrintf("owner_mask=%c%c%c%c",
738 fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
739 fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
740 fMode & RTFS_UNIX_IXUSR ? 'x' : '-', 0);
741 RTPrintf("group_mask=%c%c%c%c",
742 fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
743 fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
744 fMode & RTFS_UNIX_IXGRP ? 'x' : '-', 0);
745 RTPrintf("other_mask=%c%c%c%c",
746 fMode & RTFS_UNIX_IROTH ? 'r' : '-',
747 fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
748 fMode & RTFS_UNIX_IXOTH ? 'x' : '-', 0);
749 /** @todo sticky bits. */
750 RTPrintf("dos_mask=%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
751 fMode & RTFS_DOS_READONLY ? 'R' : '-',
752 fMode & RTFS_DOS_HIDDEN ? 'H' : '-',
753 fMode & RTFS_DOS_SYSTEM ? 'S' : '-',
754 fMode & RTFS_DOS_DIRECTORY ? 'D' : '-',
755 fMode & RTFS_DOS_ARCHIVED ? 'A' : '-',
756 fMode & RTFS_DOS_NT_DEVICE ? 'd' : '-',
757 fMode & RTFS_DOS_NT_NORMAL ? 'N' : '-',
758 fMode & RTFS_DOS_NT_TEMPORARY ? 'T' : '-',
759 fMode & RTFS_DOS_NT_SPARSE_FILE ? 'P' : '-',
760 fMode & RTFS_DOS_NT_REPARSE_POINT ? 'J' : '-',
761 fMode & RTFS_DOS_NT_COMPRESSED ? 'C' : '-',
762 fMode & RTFS_DOS_NT_OFFLINE ? 'O' : '-',
763 fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
764 fMode & RTFS_DOS_NT_ENCRYPTED ? 'E' : '-', 0);
765 RTPrintf("hlinks=%RU32%cst_size=%RI64%calloc=%RI64%c",
766 pObjInfo->Attr.u.Unix.cHardlinks, 0,
767 pObjInfo->cbObject, 0,
768 pObjInfo->cbAllocated, 0);
769 RTPrintf("st_birthtime=%s%cst_ctime=%s%cst_mtime=%s%cst_atime=%s%c",
770 RTTimeSpecToString(&pObjInfo->BirthTime, szTimeBirth, sizeof(szTimeBirth)), 0,
771 RTTimeSpecToString(&pObjInfo->ChangeTime, szTimeChange, sizeof(szTimeChange)), 0,
772 RTTimeSpecToString(&pObjInfo->ModificationTime, szTimeModification, sizeof(szTimeModification)), 0,
773 RTTimeSpecToString(&pObjInfo->AccessTime, szTimeAccess, sizeof(szTimeAccess)), 0);
774 if (pObjInfo->Attr.u.Unix.uid != NIL_RTUID)
775 RTPrintf("uid=%RU32%cusername=%s%c", pObjInfo->Attr.u.Unix.uid, 0,
776 vgsvcToolboxIdCacheGetUidName(pIdCache, pObjInfo->Attr.u.Unix.uid, pszName, pszRelativeTo), 0);
777 if (pObjInfo->Attr.u.Unix.gid != NIL_RTGID)
778 RTPrintf("gid=%RU32%cgroupname=%s%c", pObjInfo->Attr.u.Unix.gid, 0,
779 vgsvcToolboxIdCacheGetGidName(pIdCache, pObjInfo->Attr.u.Unix.gid, pszName, pszRelativeTo), 0);
780 if ( (RTFS_IS_DEV_BLOCK(pObjInfo->Attr.fMode) || RTFS_IS_DEV_CHAR(pObjInfo->Attr.fMode))
781 && pObjInfo->Attr.u.Unix.Device)
782 RTPrintf("st_rdev=%RU32%c", pObjInfo->Attr.u.Unix.Device, 0);
783 if (pObjInfo->Attr.u.Unix.GenerationId)
784 RTPrintf("st_gen=%RU32%c", pObjInfo->Attr.u.Unix.GenerationId, 0);
785 if (pObjInfo->Attr.u.Unix.fFlags)
786 RTPrintf("st_flags=%RU32%c", pObjInfo->Attr.u.Unix.fFlags, 0);
787 RTPrintf("cname_len=%zu%cname=%s%c", cchName, 0, pszName, 0);
788 RTPrintf("%c%c", 0, 0); /* End of data block. */
789 }
790 else
791 {
792 RTPrintf("%c", chFileType);
793 RTPrintf("%c%c%c",
794 fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
795 fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
796 fMode & RTFS_UNIX_IXUSR ? 'x' : '-');
797 RTPrintf("%c%c%c",
798 fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
799 fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
800 fMode & RTFS_UNIX_IXGRP ? 'x' : '-');
801 RTPrintf("%c%c%c",
802 fMode & RTFS_UNIX_IROTH ? 'r' : '-',
803 fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
804 fMode & RTFS_UNIX_IXOTH ? 'x' : '-');
805 RTPrintf(" %c%c%c%c%c%c%c%c%c%c%c%c%c%c",
806 fMode & RTFS_DOS_READONLY ? 'R' : '-',
807 fMode & RTFS_DOS_HIDDEN ? 'H' : '-',
808 fMode & RTFS_DOS_SYSTEM ? 'S' : '-',
809 fMode & RTFS_DOS_DIRECTORY ? 'D' : '-',
810 fMode & RTFS_DOS_ARCHIVED ? 'A' : '-',
811 fMode & RTFS_DOS_NT_DEVICE ? 'd' : '-',
812 fMode & RTFS_DOS_NT_NORMAL ? 'N' : '-',
813 fMode & RTFS_DOS_NT_TEMPORARY ? 'T' : '-',
814 fMode & RTFS_DOS_NT_SPARSE_FILE ? 'P' : '-',
815 fMode & RTFS_DOS_NT_REPARSE_POINT ? 'J' : '-',
816 fMode & RTFS_DOS_NT_COMPRESSED ? 'C' : '-',
817 fMode & RTFS_DOS_NT_OFFLINE ? 'O' : '-',
818 fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
819 fMode & RTFS_DOS_NT_ENCRYPTED ? 'E' : '-');
820 RTPrintf(" %d %4d %4d %10lld %10lld",
821 pObjInfo->Attr.u.Unix.cHardlinks,
822 pObjInfo->Attr.u.Unix.uid,
823 pObjInfo->Attr.u.Unix.gid,
824 pObjInfo->cbObject,
825 pObjInfo->cbAllocated);
826 RTPrintf(" %s %s %s %s",
827 RTTimeSpecToString(&pObjInfo->BirthTime, szTimeBirth, sizeof(szTimeBirth)),
828 RTTimeSpecToString(&pObjInfo->ChangeTime, szTimeChange, sizeof(szTimeChange)),
829 RTTimeSpecToString(&pObjInfo->ModificationTime, szTimeModification, sizeof(szTimeModification)),
830 RTTimeSpecToString(&pObjInfo->AccessTime, szTimeAccess, sizeof(szTimeAccess)) );
831 RTPrintf(" %2zu %s\n", cchName, pszName);
832 }
833 }
834
835 return VINF_SUCCESS;
836}
837
838/**
839 * Helper routine for ls tool for handling sub directories.
840 *
841 * @return IPRT status code.
842 * @param pszDir Pointer to the directory buffer.
843 * @param cchDir The length of pszDir in pszDir.
844 * @param pDirEntry Pointer to the directory entry.
845 * @param fFlags Flags of type VBOXSERVICETOOLBOXLSFLAG.
846 * @param fOutputFlags Flags of type VBOXSERVICETOOLBOXOUTPUTFLAG.
847 * @param pIdCache The ID cache.
848 */
849static int vgsvcToolboxLsHandleDirSub(char *pszDir, size_t cchDir, PRTDIRENTRYEX pDirEntry,
850 uint32_t fFlags, uint32_t fOutputFlags, PVGSVCTOOLBOXIDCACHE pIdCache)
851{
852 Assert(cchDir > 0); Assert(pszDir[cchDir] == '\0');
853
854 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
855 RTPrintf("dname=%s%c", pszDir, 0);
856 else if (fFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE)
857 RTPrintf("%s:\n", pszDir);
858
859 /* Make sure we've got some room in the path, to save us extra work further down. */
860 if (cchDir + 3 >= RTPATH_MAX)
861 {
862 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
863 RTMsgError("Path too long: '%s'\n", pszDir);
864 return VERR_BUFFER_OVERFLOW;
865 }
866
867 /* Open directory. */
868 RTDIR hDir;
869 int rc = RTDirOpen(&hDir, pszDir);
870 if (RT_FAILURE(rc))
871 {
872 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
873 RTMsgError("Failed to open directory '%s', rc=%Rrc\n", pszDir, rc);
874 return rc;
875 }
876
877 /* Ensure we've got a trailing slash (there is space for it see above). */
878 if (!RTPATH_IS_SEP(pszDir[cchDir - 1]))
879 {
880 pszDir[cchDir++] = RTPATH_SLASH;
881 pszDir[cchDir] = '\0';
882 }
883
884 /*
885 * Process the files and subdirs.
886 */
887 for (;;)
888 {
889 /* Get the next directory. */
890 size_t cbDirEntry = VBOXSERVICETOOLBOX_DIRENTRY_BUF_SIZE;
891 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
892 if (RT_FAILURE(rc))
893 break;
894
895 /* Check length. */
896 if (pDirEntry->cbName + cchDir + 3 >= RTPATH_MAX)
897 {
898 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
899 RTMsgError("Path too long: '%s' in '%.*s'\n", pDirEntry->szName, cchDir, pszDir);
900 rc = VERR_BUFFER_OVERFLOW;
901 break;
902 }
903
904 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
905 {
906 case RTFS_TYPE_SYMLINK:
907 {
908 if (!(fFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS))
909 break;
910 RT_FALL_THRU();
911 }
912 case RTFS_TYPE_DIRECTORY:
913 {
914 rc = vgsvcToolboxPrintFsInfo(pDirEntry->szName, pDirEntry->cbName, fOutputFlags, pszDir,
915 pIdCache, &pDirEntry->Info);
916 if (RT_FAILURE(rc))
917 break;
918
919 if (RTDirEntryExIsStdDotLink(pDirEntry))
920 continue;
921
922 if (!(fFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE))
923 continue;
924
925 memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
926 int rc2 = vgsvcToolboxLsHandleDirSub(pszDir, cchDir + pDirEntry->cbName, pDirEntry, fFlags, fOutputFlags, pIdCache);
927 if (RT_SUCCESS(rc))
928 rc = rc2;
929 break;
930 }
931
932 case RTFS_TYPE_FILE:
933 {
934 rc = vgsvcToolboxPrintFsInfo(pDirEntry->szName, pDirEntry->cbName, fOutputFlags, pszDir,
935 pIdCache, &pDirEntry->Info);
936 break;
937 }
938
939 default:
940 {
941 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
942 RTMsgError("Entry '%.*s%s' of mode %#x not supported, skipping",
943 cchDir, pszDir, pDirEntry->szName, pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK);
944 break;
945 }
946 }
947 }
948 if (rc != VERR_NO_MORE_FILES)
949 {
950 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
951 RTMsgError("RTDirReadEx failed: %Rrc\npszDir=%.*s", rc, cchDir, pszDir);
952 }
953
954 rc = RTDirClose(hDir);
955 if (RT_FAILURE(rc))
956 {
957 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
958 RTMsgError("RTDirClose failed: %Rrc\npszDir=%.*s", rc, cchDir, pszDir);
959 }
960
961 return rc;
962}
963
964/**
965 * Helper routine for ls tool doing the actual parsing and output of
966 * a specified directory.
967 *
968 * @return IPRT status code.
969 * @param pszDir Absolute path to directory to ouptut.
970 * @param fFlags Flags of type VBOXSERVICETOOLBOXLSFLAG.
971 * @param fOutputFlags Flags of type VBOXSERVICETOOLBOXOUTPUTFLAG.
972 * @param pIdCache The ID cache.
973 */
974static int vgsvcToolboxLsHandleDir(const char *pszDir, uint32_t fFlags, uint32_t fOutputFlags, PVGSVCTOOLBOXIDCACHE pIdCache)
975{
976 AssertPtrReturn(pszDir, VERR_INVALID_PARAMETER);
977 AssertPtrReturn(pIdCache, VERR_INVALID_PARAMETER);
978
979 char szPath[RTPATH_MAX];
980 int rc = RTPathAbs(pszDir, szPath, sizeof(szPath));
981 if (RT_FAILURE(rc))
982 {
983 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
984 RTMsgError("RTPathAbs failed on '%s': %Rrc\n", pszDir, rc);
985 return rc;
986 }
987
988 union
989 {
990 uint8_t abPadding[VBOXSERVICETOOLBOX_DIRENTRY_BUF_SIZE];
991 RTDIRENTRYEX DirEntry;
992 } uBuf;
993 return vgsvcToolboxLsHandleDirSub(szPath, strlen(szPath), &uBuf.DirEntry, fFlags, fOutputFlags, pIdCache);
994}
995
996
997/** @todo Document options! */
998static char g_paszLsHelp[] =
999 " VBoxService [--use-toolbox] vbox_ls [<general options>] [option]...\n"
1000 " [<file>...]\n\n"
1001 "List information about files (the current directory by default).\n\n"
1002 "Options:\n\n"
1003 " [--dereference|-L]\n"
1004 " [-l][-R]\n"
1005 " [--verbose|-v]\n"
1006 " [<file>...]\n"
1007 "\n";
1008
1009
1010/**
1011 * Main function for tool "vbox_ls".
1012 *
1013 * @return RTEXITCODE.
1014 * @param argc Number of arguments.
1015 * @param argv Pointer to argument array.
1016 */
1017static RTEXITCODE vgsvcToolboxLs(int argc, char **argv)
1018{
1019 static const RTGETOPTDEF s_aOptions[] =
1020 {
1021 { "--machinereadable", VBOXSERVICETOOLBOXOPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
1022 { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
1023 { NULL, 'l', RTGETOPT_REQ_NOTHING },
1024 { NULL, 'R', RTGETOPT_REQ_NOTHING },
1025 { "--verbose", VBOXSERVICETOOLBOXOPT_VERBOSE, RTGETOPT_REQ_NOTHING}
1026 };
1027
1028 int ch;
1029 RTGETOPTUNION ValueUnion;
1030 RTGETOPTSTATE GetState;
1031 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions),
1032 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1033 AssertRCReturn(rc, RTEXITCODE_INIT);
1034
1035 bool fVerbose = false;
1036 uint32_t fFlags = VBOXSERVICETOOLBOXLSFLAG_NONE;
1037 uint32_t fOutputFlags = VBOXSERVICETOOLBOXOUTPUTFLAG_NONE;
1038
1039 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1040 {
1041 /* For options that require an argument, ValueUnion has received the value. */
1042 switch (ch)
1043 {
1044 case 'h':
1045 vgsvcToolboxShowUsageHeader();
1046 RTPrintf("%s", g_paszLsHelp);
1047 return RTEXITCODE_SUCCESS;
1048
1049 case 'L': /* Dereference symlinks. */
1050 fFlags |= VBOXSERVICETOOLBOXLSFLAG_SYMLINKS;
1051 break;
1052
1053 case 'l': /* Print long format. */
1054 fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_LONG;
1055 break;
1056
1057 case VBOXSERVICETOOLBOXOPT_MACHINE_READABLE:
1058 fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE;
1059 break;
1060
1061 case 'R': /* Recursive processing. */
1062 fFlags |= VBOXSERVICETOOLBOXLSFLAG_RECURSIVE;
1063 break;
1064
1065 case VBOXSERVICETOOLBOXOPT_VERBOSE:
1066 fVerbose = true;
1067 break;
1068
1069 case 'V':
1070 vgsvcToolboxShowVersion();
1071 return RTEXITCODE_SUCCESS;
1072
1073 case VINF_GETOPT_NOT_OPTION:
1074 Assert(GetState.iNext);
1075 GetState.iNext--;
1076 break;
1077
1078 default:
1079 return RTGetOptPrintError(ch, &ValueUnion);
1080 }
1081
1082 /* All flags / options processed? Bail out here.
1083 * Processing the file / directory list comes down below. */
1084 if (ch == VINF_GETOPT_NOT_OPTION)
1085 break;
1086 }
1087
1088 /* Print magic/version. */
1089 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
1090 {
1091 rc = vgsvcToolboxStrmInit();
1092 if (RT_FAILURE(rc))
1093 RTMsgError("Error while initializing parseable streams, rc=%Rrc\n", rc);
1094 vgsvcToolboxPrintStrmHeader("vbt_ls", 1 /* Stream version */);
1095 }
1096
1097 VGSVCTOOLBOXIDCACHE IdCache;
1098 RT_ZERO(IdCache);
1099
1100 char szDirCur[RTPATH_MAX];
1101 rc = RTPathGetCurrent(szDirCur, sizeof(szDirCur));
1102 if (RT_FAILURE(rc))
1103 {
1104 RTMsgError("Getting current directory failed, rc=%Rrc\n", rc);
1105 return RTEXITCODE_FAILURE;
1106 }
1107
1108 ch = RTGetOpt(&GetState, &ValueUnion);
1109 do
1110 {
1111 char const *pszPath;
1112
1113 if (ch == 0) /* Use current directory if no element specified. */
1114 pszPath = szDirCur;
1115 else
1116 pszPath = ValueUnion.psz;
1117
1118 RTFSOBJINFO objInfo;
1119 int rc2 = RTPathQueryInfoEx(pszPath, &objInfo,
1120 RTFSOBJATTRADD_UNIX,
1121 fFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS ? RTPATH_F_FOLLOW_LINK : RTPATH_F_ON_LINK);
1122 if (RT_SUCCESS(rc2))
1123 {
1124 if ( RTFS_IS_FILE(objInfo.Attr.fMode)
1125 || ( RTFS_IS_SYMLINK(objInfo.Attr.fMode)
1126 && (fFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS)))
1127 {
1128 rc2 = vgsvcToolboxPrintFsInfo(pszPath, strlen(pszPath), fOutputFlags, NULL, &IdCache, &objInfo);
1129 if (RT_SUCCESS(rc)) /* Keep initial failing rc. */
1130 rc = rc2;
1131 }
1132 else if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1133 {
1134 rc2 = vgsvcToolboxLsHandleDir(pszPath, fFlags, fOutputFlags, &IdCache);
1135 if (RT_SUCCESS(rc)) /* Keep initial failing rc. */
1136 rc = rc2;
1137 }
1138 }
1139 else
1140 {
1141 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
1142 RTMsgError("Cannot access '%s': No such file or directory\n", pszPath);
1143 if (RT_SUCCESS(rc))
1144 rc = VERR_FILE_NOT_FOUND;
1145 /* Do not break here -- process every element in the list
1146 * and keep failing rc. */
1147 }
1148
1149 } while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0);
1150
1151 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
1152 vgsvcToolboxPrintStrmTermination();
1153
1154 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1155}
1156
1157
1158/* Try using RTPathRmCmd. */
1159static RTEXITCODE vgsvcToolboxRm(int argc, char **argv)
1160{
1161 return RTPathRmCmd(argc, argv);
1162}
1163
1164
1165static char g_paszMkTempHelp[] =
1166 " VBoxService [--use-toolbox] vbox_mktemp [<general options>] [<options>]\n"
1167 " <template>\n\n"
1168 "Create a temporary directory based on the template supplied. The first string\n"
1169 "of consecutive 'X' characters in the template will be replaced to form a unique\n"
1170 "name for the directory. The template may not contain a path. The default\n"
1171 "creation mode is 0600 for files and 0700 for directories. If no path is\n"
1172 "specified the default temporary directory will be used.\n"
1173 "Options:\n\n"
1174 " [--directory|-d] Create a directory instead of a file.\n"
1175 " [--mode|-m <mode>] Create the object with mode <mode>.\n"
1176 " [--secure|-s] Fail if the object cannot be created securely.\n"
1177 " [--tmpdir|-t <path>] Create the object with the absolute path <path>.\n"
1178 "\n";
1179
1180
1181/**
1182 * Report the result of a vbox_mktemp operation.
1183 *
1184 * Either errors to stderr (not machine-readable) or everything to stdout as
1185 * {name}\0{rc}\0 (machine- readable format). The message may optionally
1186 * contain a '%s' for the file name and an %Rrc for the result code in that
1187 * order. In future a "verbose" flag may be added, without which nothing will
1188 * be output in non-machine- readable mode. Sets prc if rc is a non-success
1189 * code.
1190 */
1191static void toolboxMkTempReport(const char *pcszMessage, const char *pcszFile,
1192 bool fActive, int rc, uint32_t fOutputFlags, int *prc)
1193{
1194 if (!fActive)
1195 return;
1196 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
1197 if (RT_SUCCESS(rc))
1198 RTPrintf(pcszMessage, pcszFile, rc);
1199 else
1200 RTMsgError(pcszMessage, pcszFile, rc);
1201 else
1202 RTPrintf("name=%s%crc=%d%c", pcszFile, 0, rc, 0);
1203 if (prc && RT_FAILURE(rc))
1204 *prc = rc;
1205}
1206
1207
1208/**
1209 * Main function for tool "vbox_mktemp".
1210 *
1211 * @return RTEXITCODE.
1212 * @param argc Number of arguments.
1213 * @param argv Pointer to argument array.
1214 */
1215static RTEXITCODE vgsvcToolboxMkTemp(int argc, char **argv)
1216{
1217 static const RTGETOPTDEF s_aOptions[] =
1218 {
1219 { "--machinereadable", VBOXSERVICETOOLBOXOPT_MACHINE_READABLE,
1220 RTGETOPT_REQ_NOTHING },
1221 { "--directory", 'd', RTGETOPT_REQ_NOTHING },
1222 { "--mode", 'm', RTGETOPT_REQ_STRING },
1223 { "--secure", 's', RTGETOPT_REQ_NOTHING },
1224 { "--tmpdir", 't', RTGETOPT_REQ_STRING },
1225 };
1226
1227 enum
1228 {
1229 /* Isn't that a bit long? s/VBOXSERVICETOOLBOX/VSTB/ ? */
1230 /** Create a temporary directory instead of a temporary file. */
1231 VBOXSERVICETOOLBOXMKTEMPFLAG_DIRECTORY = RT_BIT_32(0),
1232 /** Only create the temporary object if the operation is expected
1233 * to be secure. Not guaranteed to be supported on a particular
1234 * set-up. */
1235 VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE = RT_BIT_32(1)
1236 };
1237
1238 int ch, rc;
1239 RTGETOPTUNION ValueUnion;
1240 RTGETOPTSTATE GetState;
1241 rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1242 AssertRCReturn(rc, RTEXITCODE_INIT);
1243
1244 uint32_t fFlags = 0;
1245 uint32_t fOutputFlags = 0;
1246 int cNonOptions = 0;
1247 RTFMODE fMode = 0700;
1248 bool fModeSet = false;
1249 const char *pcszPath = NULL;
1250 const char *pcszTemplate;
1251 char szTemplateWithPath[RTPATH_MAX] = "";
1252
1253 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
1254 && RT_SUCCESS(rc))
1255 {
1256 /* For options that require an argument, ValueUnion has received the value. */
1257 switch (ch)
1258 {
1259 case 'h':
1260 vgsvcToolboxShowUsageHeader();
1261 RTPrintf("%s", g_paszMkTempHelp);
1262 return RTEXITCODE_SUCCESS;
1263
1264 case 'V':
1265 vgsvcToolboxShowVersion();
1266 return RTEXITCODE_SUCCESS;
1267
1268 case VBOXSERVICETOOLBOXOPT_MACHINE_READABLE:
1269 fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE;
1270 break;
1271
1272 case 'd':
1273 fFlags |= VBOXSERVICETOOLBOXMKTEMPFLAG_DIRECTORY;
1274 break;
1275
1276 case 'm':
1277 rc = vgsvcToolboxParseMode(ValueUnion.psz, &fMode);
1278 if (RT_FAILURE(rc))
1279 return RTEXITCODE_SYNTAX;
1280 fModeSet = true;
1281#ifndef RT_OS_WINDOWS
1282 umask(0); /* RTDirCreate workaround */
1283#endif
1284 break;
1285 case 's':
1286 fFlags |= VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE;
1287 break;
1288
1289 case 't':
1290 pcszPath = ValueUnion.psz;
1291 break;
1292
1293 case VINF_GETOPT_NOT_OPTION:
1294 /* RTGetOpt will sort these to the end of the argv vector so
1295 * that we will deal with them afterwards. */
1296 ++cNonOptions;
1297 break;
1298
1299 default:
1300 return RTGetOptPrintError(ch, &ValueUnion);
1301 }
1302 }
1303
1304 /* Print magic/version. */
1305 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
1306 {
1307 rc = vgsvcToolboxStrmInit();
1308 if (RT_FAILURE(rc))
1309 RTMsgError("Error while initializing parseable streams, rc=%Rrc\n", rc);
1310 vgsvcToolboxPrintStrmHeader("vbt_mktemp", 1 /* Stream version */);
1311 }
1312
1313 if (fFlags & VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE && fModeSet)
1314 {
1315 toolboxMkTempReport("'-s' and '-m' parameters cannot be used together.\n", "",
1316 true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1317 return RTEXITCODE_SYNTAX;
1318 }
1319
1320 /* We need exactly one template, containing at least one 'X'. */
1321 if (cNonOptions != 1)
1322 {
1323 toolboxMkTempReport("Please specify exactly one template.\n", "", true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1324 return RTEXITCODE_SYNTAX;
1325 }
1326 pcszTemplate = argv[argc - 1];
1327
1328 /* Validate that the template is as IPRT requires (asserted by IPRT). */
1329 if ( RTPathHasPath(pcszTemplate)
1330 || ( !strstr(pcszTemplate, "XXX")
1331 && pcszTemplate[strlen(pcszTemplate) - 1] != 'X'))
1332 {
1333 toolboxMkTempReport("Template '%s' should contain a file name with no path and at least three consecutive 'X' characters or ending in 'X'.\n",
1334 pcszTemplate, true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1335 return RTEXITCODE_FAILURE;
1336 }
1337 if (pcszPath && !RTPathStartsWithRoot(pcszPath))
1338 {
1339 toolboxMkTempReport("Path '%s' should be absolute.\n", pcszPath, true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1340 return RTEXITCODE_FAILURE;
1341 }
1342 if (pcszPath)
1343 {
1344 rc = RTStrCopy(szTemplateWithPath, sizeof(szTemplateWithPath), pcszPath);
1345 if (RT_FAILURE(rc))
1346 {
1347 toolboxMkTempReport("Path '%s' too long.\n", pcszPath, true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1348 return RTEXITCODE_FAILURE;
1349 }
1350 }
1351 else
1352 {
1353 rc = RTPathTemp(szTemplateWithPath, sizeof(szTemplateWithPath));
1354 if (RT_FAILURE(rc))
1355 {
1356 toolboxMkTempReport("Failed to get the temporary directory.\n", "", true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1357 return RTEXITCODE_FAILURE;
1358 }
1359 }
1360 rc = RTPathAppend(szTemplateWithPath, sizeof(szTemplateWithPath), pcszTemplate);
1361 if (RT_FAILURE(rc))
1362 {
1363 toolboxMkTempReport("Template '%s' too long for path.\n", pcszTemplate, true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1364 return RTEXITCODE_FAILURE;
1365 }
1366
1367 if (fFlags & VBOXSERVICETOOLBOXMKTEMPFLAG_DIRECTORY)
1368 {
1369 rc = fFlags & VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE
1370 ? RTDirCreateTempSecure(szTemplateWithPath)
1371 : RTDirCreateTemp(szTemplateWithPath, fMode);
1372 toolboxMkTempReport("Created temporary directory '%s'.\n",
1373 szTemplateWithPath, RT_SUCCESS(rc), rc,
1374 fOutputFlags, NULL);
1375 /* RTDirCreateTemp[Secure] sets the template to "" on failure. */
1376 toolboxMkTempReport("The following error occurred while creating a temporary directory from template '%s': %Rrc.\n",
1377 pcszTemplate, RT_FAILURE(rc), rc, fOutputFlags, NULL /*prc*/);
1378 }
1379 else
1380 {
1381 rc = fFlags & VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE
1382 ? RTFileCreateTempSecure(szTemplateWithPath)
1383 : RTFileCreateTemp(szTemplateWithPath, fMode);
1384 toolboxMkTempReport("Created temporary file '%s'.\n",
1385 szTemplateWithPath, RT_SUCCESS(rc), rc,
1386 fOutputFlags, NULL);
1387 /* RTFileCreateTemp[Secure] sets the template to "" on failure. */
1388 toolboxMkTempReport("The following error occurred while creating a temporary file from template '%s': %Rrc.\n",
1389 pcszTemplate, RT_FAILURE(rc), rc, fOutputFlags, NULL /*prc*/);
1390 }
1391 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
1392 vgsvcToolboxPrintStrmTermination();
1393 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1394}
1395
1396
1397/** @todo Document options! */
1398static char g_paszMkDirHelp[] =
1399 " VBoxService [--use-toolbox] vbox_mkdir [<general options>] [<options>]\n"
1400 " <directory>...\n\n"
1401 "Options:\n\n"
1402 " [--mode|-m <mode>] The file mode to set (chmod) on the created\n"
1403 " directories. Default: a=rwx & umask.\n"
1404 " [--parents|-p] Create parent directories as needed, no\n"
1405 " error if the directory already exists.\n"
1406 " [--verbose|-v] Display a message for each created directory.\n"
1407 "\n";
1408
1409
1410/**
1411 * Main function for tool "vbox_mkdir".
1412 *
1413 * @return RTEXITCODE.
1414 * @param argc Number of arguments.
1415 * @param argv Pointer to argument array.
1416 */
1417static RTEXITCODE vgsvcToolboxMkDir(int argc, char **argv)
1418{
1419 static const RTGETOPTDEF s_aOptions[] =
1420 {
1421 { "--mode", 'm', RTGETOPT_REQ_STRING },
1422 { "--parents", 'p', RTGETOPT_REQ_NOTHING},
1423 { "--verbose", 'v', RTGETOPT_REQ_NOTHING}
1424 };
1425
1426 int ch;
1427 RTGETOPTUNION ValueUnion;
1428 RTGETOPTSTATE GetState;
1429 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions),
1430 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1431 AssertRCReturn(rc, RTEXITCODE_INIT);
1432
1433 bool fMakeParentDirs = false;
1434 bool fVerbose = false;
1435 RTFMODE fDirMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG | RTFS_UNIX_IRWXO;
1436 int cDirsCreated = 0;
1437
1438 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1439 {
1440 /* For options that require an argument, ValueUnion has received the value. */
1441 switch (ch)
1442 {
1443 case 'p':
1444 fMakeParentDirs = true;
1445 break;
1446
1447 case 'm':
1448 rc = vgsvcToolboxParseMode(ValueUnion.psz, &fDirMode);
1449 if (RT_FAILURE(rc))
1450 return RTEXITCODE_SYNTAX;
1451#ifndef RT_OS_WINDOWS
1452 umask(0); /* RTDirCreate workaround */
1453#endif
1454 break;
1455
1456 case 'v':
1457 fVerbose = true;
1458 break;
1459
1460 case 'h':
1461 vgsvcToolboxShowUsageHeader();
1462 RTPrintf("%s", g_paszMkDirHelp);
1463 return RTEXITCODE_SUCCESS;
1464
1465 case 'V':
1466 vgsvcToolboxShowVersion();
1467 return RTEXITCODE_SUCCESS;
1468
1469 case VINF_GETOPT_NOT_OPTION:
1470 if (fMakeParentDirs)
1471 /** @todo r=bird: If fVerbose is set, we should also show
1472 * which directories that get created, parents as well as
1473 * omitting existing final dirs. Annoying, but check any
1474 * mkdir implementation (try "mkdir -pv asdf/1/2/3/4"
1475 * twice). */
1476 rc = RTDirCreateFullPath(ValueUnion.psz, fDirMode);
1477 else
1478 rc = RTDirCreate(ValueUnion.psz, fDirMode, 0);
1479 if (RT_FAILURE(rc))
1480 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Could not create directory '%s': %Rra\n",
1481 ValueUnion.psz, rc);
1482 if (fVerbose)
1483 RTMsgInfo("Created directory '%s', mode %#RTfmode\n", ValueUnion.psz, fDirMode);
1484 cDirsCreated++;
1485 break;
1486
1487 default:
1488 return RTGetOptPrintError(ch, &ValueUnion);
1489 }
1490 }
1491 AssertRC(rc);
1492
1493 if (cDirsCreated == 0)
1494 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No directory argument.");
1495
1496 return RTEXITCODE_SUCCESS;
1497}
1498
1499
1500/** @todo Document options! */
1501static char g_paszStatHelp[] =
1502 " VBoxService [--use-toolbox] vbox_stat [<general options>] [<options>]\n"
1503 " <file>...\n\n"
1504 "Display file or file system status.\n\n"
1505 "Options:\n\n"
1506 " [--file-system|-f]\n"
1507 " [--dereference|-L]\n"
1508 " [--terse|-t]\n"
1509 " [--verbose|-v]\n"
1510 "\n";
1511
1512
1513/**
1514 * Main function for tool "vbox_stat".
1515 *
1516 * @return RTEXITCODE.
1517 * @param argc Number of arguments.
1518 * @param argv Pointer to argument array.
1519 */
1520static RTEXITCODE vgsvcToolboxStat(int argc, char **argv)
1521{
1522 static const RTGETOPTDEF s_aOptions[] =
1523 {
1524 { "--file-system", 'f', RTGETOPT_REQ_NOTHING },
1525 { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
1526 { "--machinereadable", VBOXSERVICETOOLBOXOPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
1527 { "--terse", 't', RTGETOPT_REQ_NOTHING },
1528 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
1529 };
1530
1531 int ch;
1532 RTGETOPTUNION ValueUnion;
1533 RTGETOPTSTATE GetState;
1534 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1535
1536 int rc = VINF_SUCCESS;
1537 uint32_t fOutputFlags = VBOXSERVICETOOLBOXOUTPUTFLAG_LONG; /* Use long mode by default. */
1538 uint32_t fQueryInfoFlags = RTPATH_F_ON_LINK;
1539
1540 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
1541 && RT_SUCCESS(rc))
1542 {
1543 /* For options that require an argument, ValueUnion has received the value. */
1544 switch (ch)
1545 {
1546 case 'f':
1547 RTMsgError("Sorry, option '%s' is not implemented yet!\n", ValueUnion.pDef->pszLong);
1548 rc = VERR_INVALID_PARAMETER;
1549 break;
1550
1551 case 'L':
1552 fQueryInfoFlags &= ~RTPATH_F_ON_LINK;
1553 fQueryInfoFlags |= RTPATH_F_FOLLOW_LINK;
1554 break;
1555
1556 case VBOXSERVICETOOLBOXOPT_MACHINE_READABLE:
1557 fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE;
1558 break;
1559
1560 case 'h':
1561 vgsvcToolboxShowUsageHeader();
1562 RTPrintf("%s", g_paszStatHelp);
1563 return RTEXITCODE_SUCCESS;
1564
1565 case 'V':
1566 vgsvcToolboxShowVersion();
1567 return RTEXITCODE_SUCCESS;
1568
1569 case VINF_GETOPT_NOT_OPTION:
1570 {
1571 Assert(GetState.iNext);
1572 GetState.iNext--;
1573 break;
1574 }
1575
1576 default:
1577 return RTGetOptPrintError(ch, &ValueUnion);
1578 }
1579
1580 /* All flags / options processed? Bail out here.
1581 * Processing the file / directory list comes down below. */
1582 if (ch == VINF_GETOPT_NOT_OPTION)
1583 break;
1584 }
1585
1586 if (RT_SUCCESS(rc))
1587 {
1588 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
1589 {
1590 rc = vgsvcToolboxStrmInit();
1591 if (RT_FAILURE(rc))
1592 RTMsgError("Error while initializing parseable streams, rc=%Rrc\n", rc);
1593 vgsvcToolboxPrintStrmHeader("vbt_stat", 1 /* Stream version */);
1594 }
1595
1596 VGSVCTOOLBOXIDCACHE IdCache;
1597 RT_ZERO(IdCache);
1598
1599 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1600 {
1601 RTFSOBJINFO objInfo;
1602 int rc2 = RTPathQueryInfoEx(ValueUnion.psz, &objInfo, RTFSOBJATTRADD_UNIX, fQueryInfoFlags);
1603 if (RT_FAILURE(rc2))
1604 {
1605 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
1606 RTMsgError("Cannot stat for '%s': %Rrc\n", ValueUnion.psz, rc2);
1607 }
1608 else
1609 rc2 = vgsvcToolboxPrintFsInfo(ValueUnion.psz, strlen(ValueUnion.psz), fOutputFlags, NULL, &IdCache, &objInfo);
1610
1611 if (RT_SUCCESS(rc))
1612 rc = rc2;
1613 /* Do not break here -- process every element in the list
1614 * and keep (initial) failing rc. */
1615 }
1616
1617 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
1618 vgsvcToolboxPrintStrmTermination();
1619
1620 /* At this point the overall result (success/failure) should be in rc. */
1621 }
1622 else
1623 RTMsgError("Failed with rc=%Rrc\n", rc);
1624
1625 if (RT_FAILURE(rc))
1626 {
1627 switch (rc)
1628 {
1629 case VERR_ACCESS_DENIED:
1630 return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_ACCESS_DENIED;
1631
1632 case VERR_FILE_NOT_FOUND:
1633 return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_FILE_NOT_FOUND;
1634
1635 case VERR_PATH_NOT_FOUND:
1636 return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_PATH_NOT_FOUND;
1637
1638 case VERR_NET_PATH_NOT_FOUND:
1639 return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_NET_PATH_NOT_FOUND;
1640
1641 case VERR_INVALID_NAME:
1642 return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_INVALID_NAME;
1643
1644 default:
1645#ifdef DEBUG_andy
1646 AssertMsgFailed(("Exit code for %Rrc not implemented\n", rc));
1647#endif
1648 break;
1649 }
1650
1651 return RTEXITCODE_FAILURE;
1652 }
1653
1654 return RTEXITCODE_SUCCESS;
1655}
1656
1657
1658/**
1659 * Looks up the tool definition entry for the tool give by @a pszTool.
1660 *
1661 * @returns Pointer to the tool definition. NULL if not found.
1662 * @param pszTool The name of the tool.
1663 */
1664static PCVBOXSERVICETOOLBOXTOOL vgsvcToolboxLookUp(const char *pszTool)
1665{
1666 AssertPtrReturn(pszTool, NULL);
1667
1668 /* Do a linear search, since we don't have that much stuff in the table. */
1669 for (unsigned i = 0; i < RT_ELEMENTS(g_aTools); i++)
1670 if (!strcmp(g_aTools[i].pszName, pszTool))
1671 return &g_aTools[i];
1672
1673 return NULL;
1674}
1675
1676
1677/**
1678 * Converts a tool's exit code back to an IPRT error code.
1679 *
1680 * @return Converted IPRT status code.
1681 * @param pszTool Name of the toolbox tool to convert exit code for.
1682 * @param rcExit The tool's exit code to convert.
1683 */
1684int VGSvcToolboxExitCodeConvertToRc(const char *pszTool, RTEXITCODE rcExit)
1685{
1686 AssertPtrReturn(pszTool, VERR_INVALID_POINTER);
1687
1688 PCVBOXSERVICETOOLBOXTOOL pTool = vgsvcToolboxLookUp(pszTool);
1689 if (pTool)
1690 return pTool->pfnExitCodeConvertToRc(rcExit);
1691
1692 AssertMsgFailed(("Tool '%s' not found\n", pszTool));
1693 return VERR_GENERAL_FAILURE; /* Lookup failed, should not happen. */
1694}
1695
1696
1697/**
1698 * Entry point for internal toolbox.
1699 *
1700 * @return True if an internal tool was handled, false if not.
1701 * @param argc Number of arguments.
1702 * @param argv Pointer to argument array.
1703 * @param prcExit Where to store the exit code when an
1704 * internal toolbox command was handled.
1705 */
1706bool VGSvcToolboxMain(int argc, char **argv, RTEXITCODE *prcExit)
1707{
1708
1709 /*
1710 * Check if the file named in argv[0] is one of the toolbox programs.
1711 */
1712 AssertReturn(argc > 0, false);
1713 const char *pszTool = RTPathFilename(argv[0]);
1714 PCVBOXSERVICETOOLBOXTOOL pTool = vgsvcToolboxLookUp(pszTool);
1715 if (!pTool)
1716 {
1717 /*
1718 * For debugging and testing purposes we also allow toolbox program access
1719 * when the first VBoxService argument is --use-toolbox.
1720 */
1721 if (argc < 2 || strcmp(argv[1], "--use-toolbox"))
1722 return false;
1723
1724 /* No tool specified? Show toolbox help. */
1725 if (argc < 3)
1726 {
1727 vgsvcToolboxShowUsage();
1728 *prcExit = RTEXITCODE_SYNTAX;
1729 return true;
1730 }
1731
1732 argc -= 2;
1733 argv += 2;
1734 pszTool = argv[0];
1735 pTool = vgsvcToolboxLookUp(pszTool);
1736 if (!pTool)
1737 {
1738 *prcExit = RTEXITCODE_SUCCESS;
1739 if (!strcmp(pszTool, "-V"))
1740 {
1741 vgsvcToolboxShowVersion();
1742 return true;
1743 }
1744 if ( strcmp(pszTool, "help")
1745 && strcmp(pszTool, "--help")
1746 && strcmp(pszTool, "-h"))
1747 *prcExit = RTEXITCODE_SYNTAX;
1748 vgsvcToolboxShowUsage();
1749 return true;
1750 }
1751 }
1752
1753 /*
1754 * Invoke the handler.
1755 */
1756 RTMsgSetProgName("VBoxService/%s", pszTool);
1757 AssertPtr(pTool);
1758 *prcExit = pTool->pfnHandler(argc, argv);
1759
1760 return true;
1761}
1762
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