VirtualBox

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

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

Guest Control/VBoxServiceToolbox: Resolved several @todos for the ls tool (rewritten ls directory traversing, ++). bugref:9320

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 64.2 KB
Line 
1/* $Id: VBoxServiceToolBox.cpp 83434 2020-03-26 11:58:18Z 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 AssertPtr(pList);
295 /** @todo use RTListForEachSafe */
296 PVBOXSERVICETOOLBOXPATHENTRY pNode = RTListGetFirst(pList, VBOXSERVICETOOLBOXPATHENTRY, Node);
297 while (pNode)
298 {
299 PVBOXSERVICETOOLBOXPATHENTRY pNext = RTListNodeIsLast(pList, &pNode->Node)
300 ? NULL
301 : RTListNodeGetNext(&pNode->Node, VBOXSERVICETOOLBOXPATHENTRY, Node);
302 RTListNodeRemove(&pNode->Node);
303
304 RTStrFree(pNode->pszName);
305
306 RTMemFree(pNode);
307 pNode = pNext;
308 }
309}
310
311
312/**
313 * Adds a path entry (file/directory/whatever) to a given path buffer list.
314 *
315 * @return IPRT status code.
316 * @param pList Pointer to list to add entry to.
317 * @param pszName Name of entry to add.
318 */
319static int vgsvcToolboxPathBufAddPathEntry(PRTLISTNODE pList, const char *pszName)
320{
321 AssertPtrReturn(pList, VERR_INVALID_PARAMETER);
322
323 int rc = VINF_SUCCESS;
324 PVBOXSERVICETOOLBOXPATHENTRY pNode = (PVBOXSERVICETOOLBOXPATHENTRY)RTMemAlloc(sizeof(VBOXSERVICETOOLBOXPATHENTRY));
325 if (pNode)
326 {
327 pNode->pszName = RTStrDup(pszName);
328 AssertPtr(pNode->pszName);
329
330 RTListAppend(pList, &pNode->Node);
331 }
332 else
333 rc = VERR_NO_MEMORY;
334 return rc;
335}
336
337
338/**
339 * Performs the actual output operation of "vbox_cat".
340 *
341 * @return IPRT status code.
342 * @param hInput Handle of input file (if any) to use;
343 * else stdin will be used.
344 * @param hOutput Handle of output file (if any) to use;
345 * else stdout will be used.
346 */
347static int vgsvcToolboxCatOutput(RTFILE hInput, RTFILE hOutput)
348{
349 int rc = VINF_SUCCESS;
350 if (hInput == NIL_RTFILE)
351 {
352 rc = RTFileFromNative(&hInput, RTFILE_NATIVE_STDIN);
353 if (RT_FAILURE(rc))
354 RTMsgError("Could not translate input file to native handle, rc=%Rrc\n", rc);
355 }
356
357 if (hOutput == NIL_RTFILE)
358 {
359 rc = RTFileFromNative(&hOutput, RTFILE_NATIVE_STDOUT);
360 if (RT_FAILURE(rc))
361 RTMsgError("Could not translate output file to native handle, rc=%Rrc\n", rc);
362 }
363
364 if (RT_SUCCESS(rc))
365 {
366 uint8_t abBuf[_64K];
367 size_t cbRead;
368 for (;;)
369 {
370 rc = RTFileRead(hInput, abBuf, sizeof(abBuf), &cbRead);
371 if (RT_SUCCESS(rc) && cbRead > 0)
372 {
373 rc = RTFileWrite(hOutput, abBuf, cbRead, NULL /* Try to write all at once! */);
374 if (RT_FAILURE(rc))
375 {
376 RTMsgError("Error while writing output, rc=%Rrc\n", rc);
377 break;
378 }
379 }
380 else
381 {
382 if (rc == VERR_BROKEN_PIPE)
383 rc = VINF_SUCCESS;
384 else if (RT_FAILURE(rc))
385 RTMsgError("Error while reading input, rc=%Rrc\n", rc);
386 break;
387 }
388 }
389 }
390 return rc;
391}
392
393
394/** @todo Document options! */
395static char g_paszCatHelp[] =
396 " VBoxService [--use-toolbox] vbox_cat [<general options>] <file>...\n\n"
397 "Concatenate files, or standard input, to standard output.\n"
398 "\n";
399
400
401/**
402 * Main function for tool "vbox_cat".
403 *
404 * @return RTEXITCODE.
405 * @param argc Number of arguments.
406 * @param argv Pointer to argument array.
407 */
408static RTEXITCODE vgsvcToolboxCat(int argc, char **argv)
409{
410 static const RTGETOPTDEF s_aOptions[] =
411 {
412 /* Sorted by short ops. */
413 { "--show-all", 'a', RTGETOPT_REQ_NOTHING },
414 { "--number-nonblank", 'b', RTGETOPT_REQ_NOTHING},
415 { NULL, 'e', RTGETOPT_REQ_NOTHING},
416 { NULL, 'E', RTGETOPT_REQ_NOTHING},
417 { "--flags", 'f', RTGETOPT_REQ_STRING},
418 { "--no-content-indexed", VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED, RTGETOPT_REQ_NOTHING},
419 { "--number", 'n', RTGETOPT_REQ_NOTHING},
420 { "--output", 'o', RTGETOPT_REQ_STRING},
421 { "--squeeze-blank", 's', RTGETOPT_REQ_NOTHING},
422 { NULL, 't', RTGETOPT_REQ_NOTHING},
423 { "--show-tabs", 'T', RTGETOPT_REQ_NOTHING},
424 { NULL, 'u', RTGETOPT_REQ_NOTHING},
425 { "--show-noneprinting", 'v', RTGETOPT_REQ_NOTHING}
426 };
427
428 int ch;
429 RTGETOPTUNION ValueUnion;
430 RTGETOPTSTATE GetState;
431
432 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /*iFirst*/, 0 /*fFlags*/);
433
434 int rc = VINF_SUCCESS;
435
436 const char *pszOutput = NULL;
437 RTFILE hOutput = NIL_RTFILE;
438 uint32_t fFlags = RTFILE_O_CREATE_REPLACE /* Output file flags. */
439 | RTFILE_O_WRITE
440 | RTFILE_O_DENY_WRITE;
441
442 /* Init directory list. */
443 RTLISTANCHOR inputList;
444 RTListInit(&inputList);
445
446 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
447 && RT_SUCCESS(rc))
448 {
449 /* For options that require an argument, ValueUnion has received the value. */
450 switch (ch)
451 {
452 case 'a':
453 case 'b':
454 case 'e':
455 case 'E':
456 case 'n':
457 case 's':
458 case 't':
459 case 'T':
460 case 'v':
461 RTMsgError("Sorry, option '%s' is not implemented yet!\n",
462 ValueUnion.pDef->pszLong);
463 rc = VERR_INVALID_PARAMETER;
464 break;
465
466 case 'h':
467 vgsvcToolboxShowUsageHeader();
468 RTPrintf("%s", g_paszCatHelp);
469 return RTEXITCODE_SUCCESS;
470
471 case 'o':
472 pszOutput = ValueUnion.psz;
473 break;
474
475 case 'u':
476 /* Ignored. */
477 break;
478
479 case 'V':
480 vgsvcToolboxShowVersion();
481 return RTEXITCODE_SUCCESS;
482
483 case VBOXSERVICETOOLBOXCATOPT_NO_CONTENT_INDEXED:
484 fFlags |= RTFILE_O_NOT_CONTENT_INDEXED;
485 break;
486
487 case VINF_GETOPT_NOT_OPTION:
488 /* Add file(s) to buffer. This enables processing multiple paths
489 * at once.
490 *
491 * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
492 * processing this loop it's safe to immediately exit on syntax errors
493 * or showing the help text (see above). */
494 rc = vgsvcToolboxPathBufAddPathEntry(&inputList, ValueUnion.psz);
495 break;
496
497 default:
498 return RTGetOptPrintError(ch, &ValueUnion);
499 }
500 }
501
502 if (RT_SUCCESS(rc))
503 {
504 if (pszOutput)
505 {
506 rc = RTFileOpen(&hOutput, pszOutput, fFlags);
507 if (RT_FAILURE(rc))
508 RTMsgError("Could not create output file '%s', rc=%Rrc\n", pszOutput, rc);
509 }
510
511 if (RT_SUCCESS(rc))
512 {
513 /* Process each input file. */
514 RTFILE hInput = NIL_RTFILE;
515 PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
516 RTListForEach(&inputList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
517 {
518 rc = RTFileOpen(&hInput, pNodeIt->pszName,
519 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
520 if (RT_SUCCESS(rc))
521 {
522 rc = vgsvcToolboxCatOutput(hInput, hOutput);
523 RTFileClose(hInput);
524 }
525 else
526 {
527 PCRTSTATUSMSG pMsg = RTErrGet(rc);
528 if (pMsg)
529 RTMsgError("Could not open input file '%s': %s\n", pNodeIt->pszName, pMsg->pszMsgFull);
530 else
531 RTMsgError("Could not open input file '%s', rc=%Rrc\n", pNodeIt->pszName, rc);
532 }
533
534 if (RT_FAILURE(rc))
535 break;
536 }
537
538 /* If no input files were defined, process stdin. */
539 if (RTListNodeIsFirst(&inputList, &inputList))
540 rc = vgsvcToolboxCatOutput(hInput, hOutput);
541 }
542 }
543
544 if (hOutput != NIL_RTFILE)
545 RTFileClose(hOutput);
546 vgsvcToolboxPathBufDestroy(&inputList);
547
548 if (RT_FAILURE(rc))
549 {
550 switch (rc)
551 {
552 case VERR_ACCESS_DENIED:
553 return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_ACCESS_DENIED;
554
555 case VERR_FILE_NOT_FOUND:
556 return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_FILE_NOT_FOUND;
557
558 case VERR_PATH_NOT_FOUND:
559 return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_PATH_NOT_FOUND;
560
561 case VERR_SHARING_VIOLATION:
562 return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_SHARING_VIOLATION;
563
564 case VERR_IS_A_DIRECTORY:
565 return (RTEXITCODE)VBOXSERVICETOOLBOX_CAT_EXITCODE_IS_A_DIRECTORY;
566
567 default:
568#ifdef DEBUG_andy
569 AssertMsgFailed(("Exit code for %Rrc not implemented\n", rc));
570#endif
571 break;
572 }
573
574 return RTEXITCODE_FAILURE;
575 }
576
577 return RTEXITCODE_SUCCESS;
578}
579
580
581/**
582 * Resolves the UID to a name as best as we can.
583 *
584 * @returns Read-only name string. Only valid till the next cache call.
585 * @param pIdCache The ID cache.
586 * @param uid The UID to resolve.
587 * @param pszEntry The filename of the UID.
588 * @param pszRelativeTo What @a pszEntry is relative to, NULL if absolute.
589 */
590static const char *vgsvcToolboxIdCacheGetUidName(PVGSVCTOOLBOXIDCACHE pIdCache, RTUID uid,
591 const char *pszEntry, const char *pszRelativeTo)
592{
593 /* Check cached entries. */
594 for (uint32_t i = 0; i < pIdCache->cEntries; i++)
595 if ( pIdCache->aEntries[i].id == uid
596 && pIdCache->aEntries[i].fIsUid)
597 return pIdCache->aEntries[i].szName;
598
599 /* Miss. */
600 RTFSOBJINFO ObjInfo;
601 RT_ZERO(ObjInfo); /* shut up msc */
602 int rc;
603 if (!pszRelativeTo)
604 rc = RTPathQueryInfoEx(pszEntry, &ObjInfo, RTFSOBJATTRADD_UNIX_OWNER, RTPATH_F_ON_LINK);
605 else
606 {
607 char szPath[RTPATH_MAX];
608 rc = RTPathJoin(szPath, sizeof(szPath), pszRelativeTo, pszEntry);
609 if (RT_SUCCESS(rc))
610 rc = RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_UNIX_OWNER, RTPATH_F_ON_LINK);
611 }
612
613 if ( RT_SUCCESS(rc)
614 && ObjInfo.Attr.u.UnixOwner.uid == uid)
615 {
616 uint32_t i = pIdCache->cEntries;
617 if (i < RT_ELEMENTS(pIdCache->aEntries))
618 pIdCache->cEntries = i + 1;
619 else
620 i = pIdCache->iNextReplace++ % RT_ELEMENTS(pIdCache->aEntries);
621 pIdCache->aEntries[i].id = uid;
622 pIdCache->aEntries[i].fIsUid = true;
623 RTStrCopy(pIdCache->aEntries[i].szName, sizeof(pIdCache->aEntries[i].szName), ObjInfo.Attr.u.UnixOwner.szName);
624 return pIdCache->aEntries[i].szName;
625 }
626 return "";
627}
628
629
630/**
631 * Resolves the GID to a name as best as we can.
632 *
633 * @returns Read-only name string. Only valid till the next cache call.
634 * @param pIdCache The ID cache.
635 * @param gid The GID to resolve.
636 * @param pszEntry The filename of the GID.
637 * @param pszRelativeTo What @a pszEntry is relative to, NULL if absolute.
638 */
639static const char *vgsvcToolboxIdCacheGetGidName(PVGSVCTOOLBOXIDCACHE pIdCache, RTGID gid,
640 const char *pszEntry, const char *pszRelativeTo)
641{
642 /* Check cached entries. */
643 for (uint32_t i = 0; i < pIdCache->cEntries; i++)
644 if ( pIdCache->aEntries[i].id == gid
645 && !pIdCache->aEntries[i].fIsUid)
646 return pIdCache->aEntries[i].szName;
647
648 /* Miss. */
649 RTFSOBJINFO ObjInfo;
650 RT_ZERO(ObjInfo); /* shut up msc */
651 int rc;
652 if (!pszRelativeTo)
653 rc = RTPathQueryInfoEx(pszEntry, &ObjInfo, RTFSOBJATTRADD_UNIX_GROUP, RTPATH_F_ON_LINK);
654 else
655 {
656 char szPath[RTPATH_MAX];
657 rc = RTPathJoin(szPath, sizeof(szPath), pszRelativeTo, pszEntry);
658 if (RT_SUCCESS(rc))
659 rc = RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_UNIX_GROUP, RTPATH_F_ON_LINK);
660 }
661
662 if ( RT_SUCCESS(rc)
663 && ObjInfo.Attr.u.UnixGroup.gid == gid)
664 {
665 uint32_t i = pIdCache->cEntries;
666 if (i < RT_ELEMENTS(pIdCache->aEntries))
667 pIdCache->cEntries = i + 1;
668 else
669 i = pIdCache->iNextReplace++ % RT_ELEMENTS(pIdCache->aEntries);
670 pIdCache->aEntries[i].id = gid;
671 pIdCache->aEntries[i].fIsUid = false;
672 RTStrCopy(pIdCache->aEntries[i].szName, sizeof(pIdCache->aEntries[i].szName), ObjInfo.Attr.u.UnixGroup.szName);
673 return pIdCache->aEntries[i].szName;
674 }
675 return "";
676}
677
678
679/**
680 * Prints information (based on given flags) of a file system object (file/directory/...)
681 * to stdout.
682 *
683 * @return IPRT status code.
684 * @param pszName Object name.
685 * @param cchName Length of pszName.
686 * @param fOutputFlags Output / handling flags of type
687 * VBOXSERVICETOOLBOXOUTPUTFLAG.
688 * @param pszRelativeTo What pszName is relative to.
689 * @param pIdCache The ID cache.
690 * @param pObjInfo Pointer to object information.
691 */
692static int vgsvcToolboxPrintFsInfo(const char *pszName, size_t cchName, uint32_t fOutputFlags, const char *pszRelativeTo,
693 PVGSVCTOOLBOXIDCACHE pIdCache, PRTFSOBJINFO pObjInfo)
694{
695 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
696 AssertReturn(cchName, VERR_INVALID_PARAMETER);
697 AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
698
699 RTFMODE fMode = pObjInfo->Attr.fMode;
700 char chFileType;
701 switch (fMode & RTFS_TYPE_MASK)
702 {
703 case RTFS_TYPE_FIFO: chFileType = 'f'; break;
704 case RTFS_TYPE_DEV_CHAR: chFileType = 'c'; break;
705 case RTFS_TYPE_DIRECTORY: chFileType = 'd'; break;
706 case RTFS_TYPE_DEV_BLOCK: chFileType = 'b'; break;
707 case RTFS_TYPE_FILE: chFileType = '-'; break;
708 case RTFS_TYPE_SYMLINK: chFileType = 'l'; break;
709 case RTFS_TYPE_SOCKET: chFileType = 's'; break;
710 case RTFS_TYPE_WHITEOUT: chFileType = 'w'; break;
711 default: chFileType = '?'; break;
712 }
713 /** @todo sticy bits++ */
714
715/** @todo r=bird: turns out the host doesn't use or need cname_len, so perhaps we could drop it? */
716 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_LONG))
717 {
718 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
719 {
720 RTPrintf("ftype=%c%cnode_id=%RU64%inode_dev=%RU32%ccname_len=%zu%cname=%s%c",
721 chFileType, 0, (uint64_t)pObjInfo->Attr.u.Unix.INodeId, 0,
722 (uint32_t)pObjInfo->Attr.u.Unix.INodeIdDevice, 0, cchName, 0, pszName, 0);
723 RTPrintf("%c%c", 0, 0);
724 }
725 else
726 RTPrintf("%c %#18llx %3zu %s\n", chFileType, (uint64_t)pObjInfo->Attr.u.Unix.INodeId, cchName, pszName);
727 }
728 else
729 {
730 char szTimeBirth[RTTIME_STR_LEN];
731 char szTimeChange[RTTIME_STR_LEN];
732 char szTimeModification[RTTIME_STR_LEN];
733 char szTimeAccess[RTTIME_STR_LEN];
734
735 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
736 {
737 RTPrintf("ftype=%c%c", chFileType, 0);
738 if (pObjInfo->Attr.u.Unix.INodeId || pObjInfo->Attr.u.Unix.INodeIdDevice)
739 RTPrintf("node_id=%RU64%cinode_dev=%RU32%c", (uint64_t)pObjInfo->Attr.u.Unix.INodeId, 0,
740 (uint32_t)pObjInfo->Attr.u.Unix.INodeIdDevice, 0);
741 RTPrintf("owner_mask=%c%c%c%c",
742 fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
743 fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
744 fMode & RTFS_UNIX_IXUSR ? 'x' : '-', 0);
745 RTPrintf("group_mask=%c%c%c%c",
746 fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
747 fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
748 fMode & RTFS_UNIX_IXGRP ? 'x' : '-', 0);
749 RTPrintf("other_mask=%c%c%c%c",
750 fMode & RTFS_UNIX_IROTH ? 'r' : '-',
751 fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
752 fMode & RTFS_UNIX_IXOTH ? 'x' : '-', 0);
753 /** @todo sticky bits. */
754 RTPrintf("dos_mask=%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
755 fMode & RTFS_DOS_READONLY ? 'R' : '-',
756 fMode & RTFS_DOS_HIDDEN ? 'H' : '-',
757 fMode & RTFS_DOS_SYSTEM ? 'S' : '-',
758 fMode & RTFS_DOS_DIRECTORY ? 'D' : '-',
759 fMode & RTFS_DOS_ARCHIVED ? 'A' : '-',
760 fMode & RTFS_DOS_NT_DEVICE ? 'd' : '-',
761 fMode & RTFS_DOS_NT_NORMAL ? 'N' : '-',
762 fMode & RTFS_DOS_NT_TEMPORARY ? 'T' : '-',
763 fMode & RTFS_DOS_NT_SPARSE_FILE ? 'P' : '-',
764 fMode & RTFS_DOS_NT_REPARSE_POINT ? 'J' : '-',
765 fMode & RTFS_DOS_NT_COMPRESSED ? 'C' : '-',
766 fMode & RTFS_DOS_NT_OFFLINE ? 'O' : '-',
767 fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
768 fMode & RTFS_DOS_NT_ENCRYPTED ? 'E' : '-', 0);
769 RTPrintf("hlinks=%RU32%cst_size=%RI64%calloc=%RI64%c",
770 pObjInfo->Attr.u.Unix.cHardlinks, 0,
771 pObjInfo->cbObject, 0,
772 pObjInfo->cbAllocated, 0);
773 RTPrintf("st_birthtime=%s%cst_ctime=%s%cst_mtime=%s%cst_atime=%s%c",
774 RTTimeSpecToString(&pObjInfo->BirthTime, szTimeBirth, sizeof(szTimeBirth)), 0,
775 RTTimeSpecToString(&pObjInfo->ChangeTime, szTimeChange, sizeof(szTimeChange)), 0,
776 RTTimeSpecToString(&pObjInfo->ModificationTime, szTimeModification, sizeof(szTimeModification)), 0,
777 RTTimeSpecToString(&pObjInfo->AccessTime, szTimeAccess, sizeof(szTimeAccess)), 0);
778 if (pObjInfo->Attr.u.Unix.uid != NIL_RTUID)
779 RTPrintf("uid=%RU32%cusername=%s%c", pObjInfo->Attr.u.Unix.uid, 0,
780 vgsvcToolboxIdCacheGetUidName(pIdCache, pObjInfo->Attr.u.Unix.uid, pszName, pszRelativeTo), 0);
781 if (pObjInfo->Attr.u.Unix.gid != NIL_RTGID)
782 RTPrintf("gid=%RU32%cgroupname=%s%c", pObjInfo->Attr.u.Unix.gid, 0,
783 vgsvcToolboxIdCacheGetGidName(pIdCache, pObjInfo->Attr.u.Unix.gid, pszName, pszRelativeTo), 0);
784 if ( (RTFS_IS_DEV_BLOCK(pObjInfo->Attr.fMode) || RTFS_IS_DEV_CHAR(pObjInfo->Attr.fMode))
785 && pObjInfo->Attr.u.Unix.Device)
786 RTPrintf("st_rdev=%RU32%c", pObjInfo->Attr.u.Unix.Device, 0);
787 if (pObjInfo->Attr.u.Unix.GenerationId)
788 RTPrintf("st_gen=%RU32%c", pObjInfo->Attr.u.Unix.GenerationId, 0);
789 if (pObjInfo->Attr.u.Unix.fFlags)
790 RTPrintf("st_flags=%RU32%c", pObjInfo->Attr.u.Unix.fFlags, 0);
791 RTPrintf("cname_len=%zu%cname=%s%c", cchName, 0, pszName, 0);
792 RTPrintf("%c%c", 0, 0); /* End of data block. */
793 }
794 else
795 {
796 RTPrintf("%c", chFileType);
797 RTPrintf("%c%c%c",
798 fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
799 fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
800 fMode & RTFS_UNIX_IXUSR ? 'x' : '-');
801 RTPrintf("%c%c%c",
802 fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
803 fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
804 fMode & RTFS_UNIX_IXGRP ? 'x' : '-');
805 RTPrintf("%c%c%c",
806 fMode & RTFS_UNIX_IROTH ? 'r' : '-',
807 fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
808 fMode & RTFS_UNIX_IXOTH ? 'x' : '-');
809 RTPrintf(" %c%c%c%c%c%c%c%c%c%c%c%c%c%c",
810 fMode & RTFS_DOS_READONLY ? 'R' : '-',
811 fMode & RTFS_DOS_HIDDEN ? 'H' : '-',
812 fMode & RTFS_DOS_SYSTEM ? 'S' : '-',
813 fMode & RTFS_DOS_DIRECTORY ? 'D' : '-',
814 fMode & RTFS_DOS_ARCHIVED ? 'A' : '-',
815 fMode & RTFS_DOS_NT_DEVICE ? 'd' : '-',
816 fMode & RTFS_DOS_NT_NORMAL ? 'N' : '-',
817 fMode & RTFS_DOS_NT_TEMPORARY ? 'T' : '-',
818 fMode & RTFS_DOS_NT_SPARSE_FILE ? 'P' : '-',
819 fMode & RTFS_DOS_NT_REPARSE_POINT ? 'J' : '-',
820 fMode & RTFS_DOS_NT_COMPRESSED ? 'C' : '-',
821 fMode & RTFS_DOS_NT_OFFLINE ? 'O' : '-',
822 fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
823 fMode & RTFS_DOS_NT_ENCRYPTED ? 'E' : '-');
824 RTPrintf(" %d %4d %4d %10lld %10lld",
825 pObjInfo->Attr.u.Unix.cHardlinks,
826 pObjInfo->Attr.u.Unix.uid,
827 pObjInfo->Attr.u.Unix.gid,
828 pObjInfo->cbObject,
829 pObjInfo->cbAllocated);
830 RTPrintf(" %s %s %s %s",
831 RTTimeSpecToString(&pObjInfo->BirthTime, szTimeBirth, sizeof(szTimeBirth)),
832 RTTimeSpecToString(&pObjInfo->ChangeTime, szTimeChange, sizeof(szTimeChange)),
833 RTTimeSpecToString(&pObjInfo->ModificationTime, szTimeModification, sizeof(szTimeModification)),
834 RTTimeSpecToString(&pObjInfo->AccessTime, szTimeAccess, sizeof(szTimeAccess)) );
835 RTPrintf(" %2zu %s\n", cchName, pszName);
836 }
837 }
838
839 return VINF_SUCCESS;
840}
841
842/**
843 * Helper routine for ls tool for handling sub directories.
844 *
845 * @return IPRT status code.
846 * @param pszDir Pointer to the directory buffer.
847 * @param cchDir The length of pszDir in pszDir.
848 * @param pDirEntry Pointer to the directory entry.
849 * @param fFlags Flags of type VBOXSERVICETOOLBOXLSFLAG.
850 * @param fOutputFlags Flags of type VBOXSERVICETOOLBOXOUTPUTFLAG.
851 * @param pIdCache The ID cache.
852 */
853static int vgsvcToolboxLsHandleDirSub(char *pszDir, size_t cchDir, PRTDIRENTRYEX pDirEntry,
854 uint32_t fFlags, uint32_t fOutputFlags, PVGSVCTOOLBOXIDCACHE pIdCache)
855{
856 Assert(cchDir > 0); Assert(pszDir[cchDir] == '\0');
857
858 if (fFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
859 RTPrintf("dname=%s%c", pszDir, 0);
860 else if (fFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE)
861 RTPrintf("%s:\n", pszDir);
862
863 /* Make sure we've got some room in the path, to save us extra work further down. */
864 if (cchDir + 3 >= RTPATH_MAX)
865 {
866 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
867 RTMsgError("Path too long: '%s'\n", pszDir);
868 return VERR_BUFFER_OVERFLOW;
869 }
870
871 /* Open directory. */
872 RTDIR hDir;
873 int rc = RTDirOpen(&hDir, pszDir);
874 if (RT_FAILURE(rc))
875 {
876 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
877 RTMsgError("Failed to open directory '%s', rc=%Rrc\n", pszDir, rc);
878 return rc;
879 }
880
881 /* Ensure we've got a trailing slash (there is space for it see above). */
882 if (!RTPATH_IS_SEP(pszDir[cchDir - 1]))
883 {
884 pszDir[cchDir++] = RTPATH_SLASH;
885 pszDir[cchDir] = '\0';
886 }
887
888 /*
889 * Process the files and subdirs.
890 */
891 for (;;)
892 {
893 /* Get the next directory. */
894 size_t cbDirEntry = VBOXSERVICETOOLBOX_DIRENTRY_BUF_SIZE;
895 rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
896 if (RT_FAILURE(rc))
897 break;
898
899 /* Skip the dot and dot-dot links. */
900 if (RTDirEntryExIsStdDotLink(pDirEntry))
901 continue;
902
903 /* Check length. */
904 if (pDirEntry->cbName + cchDir + 3 >= RTPATH_MAX)
905 {
906 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
907 RTMsgError("Path too long: '%s' in '%.*s'\n", pDirEntry->szName, cchDir, pszDir);
908 rc = VERR_BUFFER_OVERFLOW;
909 break;
910 }
911
912 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
913 {
914 case RTFS_TYPE_SYMLINK:
915 {
916 if (!(fFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS))
917 break;
918 RT_FALL_THRU();
919 }
920 case RTFS_TYPE_DIRECTORY:
921 {
922 if (RTDirEntryExIsStdDotLink(pDirEntry))
923 continue;
924
925 if (!(fFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE))
926 continue;
927
928 memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
929 int rc2 = vgsvcToolboxLsHandleDirSub(pszDir, cchDir + pDirEntry->cbName, pDirEntry, fFlags, fOutputFlags, pIdCache);
930 if (RT_SUCCESS(rc))
931 rc = rc2;
932 break;
933 }
934
935 case RTFS_TYPE_FILE:
936 {
937 rc = vgsvcToolboxPrintFsInfo(pDirEntry->szName, pDirEntry->cbName, fOutputFlags, pszDir,
938 pIdCache, &pDirEntry->Info);
939 break;
940 }
941
942 default:
943 {
944 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
945 RTMsgError("Entry '%.*s%s' of mode %#x not supported, skipping",
946 cchDir, pszDir, pDirEntry->szName, pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK);
947 break;
948 }
949 }
950 }
951 if (rc != VERR_NO_MORE_FILES)
952 {
953 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
954 RTMsgError("RTDirReadEx failed: %Rrc\npszDir=%.*s", rc, cchDir, pszDir);
955 }
956
957 rc = RTDirClose(hDir);
958 if (RT_FAILURE(rc))
959 {
960 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
961 RTMsgError("RTDirClose failed: %Rrc\npszDir=%.*s", rc, cchDir, pszDir);
962 }
963
964 return rc;
965}
966
967/**
968 * Helper routine for ls tool doing the actual parsing and output of
969 * a specified directory.
970 *
971 * @return IPRT status code.
972 * @param pszDir Absolute path to directory to ouptut.
973 * @param fFlags Flags of type VBOXSERVICETOOLBOXLSFLAG.
974 * @param fOutputFlags Flags of type VBOXSERVICETOOLBOXOUTPUTFLAG.
975 * @param pIdCache The ID cache.
976 */
977static int vgsvcToolboxLsHandleDir(const char *pszDir, uint32_t fFlags, uint32_t fOutputFlags, PVGSVCTOOLBOXIDCACHE pIdCache)
978{
979 AssertPtrReturn(pszDir, VERR_INVALID_PARAMETER);
980 AssertPtrReturn(pIdCache, VERR_INVALID_PARAMETER);
981
982 char szPath[RTPATH_MAX];
983 int rc = RTPathAbs(pszDir, szPath, sizeof(szPath));
984 if (RT_FAILURE(rc))
985 {
986 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
987 RTMsgError("RTPathAbs failed on '%s': %Rrc\n", pszDir, rc);
988 return rc;
989 }
990
991 union
992 {
993 uint8_t abPadding[VBOXSERVICETOOLBOX_DIRENTRY_BUF_SIZE];
994 RTDIRENTRYEX DirEntry;
995 } uBuf;
996 return vgsvcToolboxLsHandleDirSub(szPath, strlen(szPath), &uBuf.DirEntry, fFlags, fOutputFlags, pIdCache);
997}
998
999
1000/** @todo Document options! */
1001static char g_paszLsHelp[] =
1002 " VBoxService [--use-toolbox] vbox_ls [<general options>] [option]...\n"
1003 " [<file>...]\n\n"
1004 "List information about files (the current directory by default).\n\n"
1005 "Options:\n\n"
1006 " [--dereference|-L]\n"
1007 " [-l][-R]\n"
1008 " [--verbose|-v]\n"
1009 " [<file>...]\n"
1010 "\n";
1011
1012
1013/**
1014 * Main function for tool "vbox_ls".
1015 *
1016 * @return RTEXITCODE.
1017 * @param argc Number of arguments.
1018 * @param argv Pointer to argument array.
1019 */
1020static RTEXITCODE vgsvcToolboxLs(int argc, char **argv)
1021{
1022 static const RTGETOPTDEF s_aOptions[] =
1023 {
1024 { "--machinereadable", VBOXSERVICETOOLBOXOPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
1025 { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
1026 { NULL, 'l', RTGETOPT_REQ_NOTHING },
1027 { NULL, 'R', RTGETOPT_REQ_NOTHING },
1028 { "--verbose", VBOXSERVICETOOLBOXOPT_VERBOSE, RTGETOPT_REQ_NOTHING}
1029 };
1030
1031 int ch;
1032 RTGETOPTUNION ValueUnion;
1033 RTGETOPTSTATE GetState;
1034 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions),
1035 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1036 AssertRCReturn(rc, RTEXITCODE_INIT);
1037
1038 bool fVerbose = false;
1039 uint32_t fFlags = VBOXSERVICETOOLBOXLSFLAG_NONE;
1040 uint32_t fOutputFlags = VBOXSERVICETOOLBOXOUTPUTFLAG_NONE;
1041
1042 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1043 {
1044 /* For options that require an argument, ValueUnion has received the value. */
1045 switch (ch)
1046 {
1047 case 'h':
1048 vgsvcToolboxShowUsageHeader();
1049 RTPrintf("%s", g_paszLsHelp);
1050 return RTEXITCODE_SUCCESS;
1051
1052 case 'L': /* Dereference symlinks. */
1053 fFlags |= VBOXSERVICETOOLBOXLSFLAG_SYMLINKS;
1054 break;
1055
1056 case 'l': /* Print long format. */
1057 fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_LONG;
1058 break;
1059
1060 case VBOXSERVICETOOLBOXOPT_MACHINE_READABLE:
1061 fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE;
1062 break;
1063
1064 case 'R': /* Recursive processing. */
1065 fFlags |= VBOXSERVICETOOLBOXLSFLAG_RECURSIVE;
1066 break;
1067
1068 case VBOXSERVICETOOLBOXOPT_VERBOSE:
1069 fVerbose = true;
1070 break;
1071
1072 case 'V':
1073 vgsvcToolboxShowVersion();
1074 return RTEXITCODE_SUCCESS;
1075
1076 case VINF_GETOPT_NOT_OPTION:
1077 Assert(GetState.iNext);
1078 GetState.iNext--;
1079 break;
1080
1081 default:
1082 return RTGetOptPrintError(ch, &ValueUnion);
1083 }
1084
1085 /* All flags / options processed? Bail out here.
1086 * Processing the file / directory list comes down below. */
1087 if (ch == VINF_GETOPT_NOT_OPTION)
1088 break;
1089 }
1090
1091 /* Print magic/version. */
1092 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
1093 {
1094 rc = vgsvcToolboxStrmInit();
1095 if (RT_FAILURE(rc))
1096 RTMsgError("Error while initializing parseable streams, rc=%Rrc\n", rc);
1097 vgsvcToolboxPrintStrmHeader("vbt_ls", 1 /* Stream version */);
1098 }
1099
1100 VGSVCTOOLBOXIDCACHE IdCache;
1101 RT_ZERO(IdCache);
1102
1103 char szDirCur[RTPATH_MAX];
1104 rc = RTPathGetCurrent(szDirCur, sizeof(szDirCur));
1105 if (RT_FAILURE(rc))
1106 {
1107 RTMsgError("Getting current directory failed, rc=%Rrc\n", rc);
1108 return RTEXITCODE_FAILURE;
1109 }
1110
1111 ch = RTGetOpt(&GetState, &ValueUnion);
1112 do
1113 {
1114 char const *pszPath;
1115
1116 if (ch == 0) /* Use current directory if no element specified. */
1117 pszPath = szDirCur;
1118 else
1119 pszPath = ValueUnion.psz;
1120
1121 RTFSOBJINFO objInfo;
1122 int rc2 = RTPathQueryInfoEx(pszPath, &objInfo,
1123 RTFSOBJATTRADD_UNIX,
1124 fFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS ? RTPATH_F_FOLLOW_LINK : RTPATH_F_ON_LINK);
1125 if (RT_SUCCESS(rc2))
1126 {
1127 if ( RTFS_IS_FILE(objInfo.Attr.fMode)
1128 || ( RTFS_IS_SYMLINK(objInfo.Attr.fMode)
1129 && (fFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS)))
1130 {
1131 rc2 = vgsvcToolboxPrintFsInfo(pszPath, strlen(pszPath), fOutputFlags, NULL, &IdCache, &objInfo);
1132 if (RT_SUCCESS(rc)) /* Keep initial failing rc. */
1133 rc = rc2;
1134 }
1135 else if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1136 {
1137 rc2 = vgsvcToolboxLsHandleDir(pszPath, fFlags, fOutputFlags, &IdCache);
1138 if (RT_SUCCESS(rc)) /* Keep initial failing rc. */
1139 rc = rc2;
1140 }
1141 }
1142 else
1143 {
1144 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
1145 RTMsgError("Cannot access '%s': No such file or directory\n", pszPath);
1146 if (RT_SUCCESS(rc))
1147 rc = VERR_FILE_NOT_FOUND;
1148 /* Do not break here -- process every element in the list
1149 * and keep failing rc. */
1150 }
1151
1152 } while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0);
1153
1154 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
1155 vgsvcToolboxPrintStrmTermination();
1156
1157 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1158}
1159
1160
1161/* Try using RTPathRmCmd. */
1162static RTEXITCODE vgsvcToolboxRm(int argc, char **argv)
1163{
1164 return RTPathRmCmd(argc, argv);
1165}
1166
1167
1168static char g_paszMkTempHelp[] =
1169 " VBoxService [--use-toolbox] vbox_mktemp [<general options>] [<options>]\n"
1170 " <template>\n\n"
1171 "Create a temporary directory based on the template supplied. The first string\n"
1172 "of consecutive 'X' characters in the template will be replaced to form a unique\n"
1173 "name for the directory. The template may not contain a path. The default\n"
1174 "creation mode is 0600 for files and 0700 for directories. If no path is\n"
1175 "specified the default temporary directory will be used.\n"
1176 "Options:\n\n"
1177 " [--directory|-d] Create a directory instead of a file.\n"
1178 " [--mode|-m <mode>] Create the object with mode <mode>.\n"
1179 " [--secure|-s] Fail if the object cannot be created securely.\n"
1180 " [--tmpdir|-t <path>] Create the object with the absolute path <path>.\n"
1181 "\n";
1182
1183
1184/**
1185 * Report the result of a vbox_mktemp operation.
1186 *
1187 * Either errors to stderr (not machine-readable) or everything to stdout as
1188 * {name}\0{rc}\0 (machine- readable format). The message may optionally
1189 * contain a '%s' for the file name and an %Rrc for the result code in that
1190 * order. In future a "verbose" flag may be added, without which nothing will
1191 * be output in non-machine- readable mode. Sets prc if rc is a non-success
1192 * code.
1193 */
1194static void toolboxMkTempReport(const char *pcszMessage, const char *pcszFile,
1195 bool fActive, int rc, uint32_t fOutputFlags, int *prc)
1196{
1197 if (!fActive)
1198 return;
1199 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
1200 if (RT_SUCCESS(rc))
1201 RTPrintf(pcszMessage, pcszFile, rc);
1202 else
1203 RTMsgError(pcszMessage, pcszFile, rc);
1204 else
1205 RTPrintf("name=%s%crc=%d%c", pcszFile, 0, rc, 0);
1206 if (prc && RT_FAILURE(rc))
1207 *prc = rc;
1208}
1209
1210
1211/**
1212 * Main function for tool "vbox_mktemp".
1213 *
1214 * @return RTEXITCODE.
1215 * @param argc Number of arguments.
1216 * @param argv Pointer to argument array.
1217 */
1218static RTEXITCODE vgsvcToolboxMkTemp(int argc, char **argv)
1219{
1220 static const RTGETOPTDEF s_aOptions[] =
1221 {
1222 { "--machinereadable", VBOXSERVICETOOLBOXOPT_MACHINE_READABLE,
1223 RTGETOPT_REQ_NOTHING },
1224 { "--directory", 'd', RTGETOPT_REQ_NOTHING },
1225 { "--mode", 'm', RTGETOPT_REQ_STRING },
1226 { "--secure", 's', RTGETOPT_REQ_NOTHING },
1227 { "--tmpdir", 't', RTGETOPT_REQ_STRING },
1228 };
1229
1230 enum
1231 {
1232 /* Isn't that a bit long? s/VBOXSERVICETOOLBOX/VSTB/ ? */
1233 /** Create a temporary directory instead of a temporary file. */
1234 VBOXSERVICETOOLBOXMKTEMPFLAG_DIRECTORY = RT_BIT_32(0),
1235 /** Only create the temporary object if the operation is expected
1236 * to be secure. Not guaranteed to be supported on a particular
1237 * set-up. */
1238 VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE = RT_BIT_32(1)
1239 };
1240
1241 int ch, rc;
1242 RTGETOPTUNION ValueUnion;
1243 RTGETOPTSTATE GetState;
1244 rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1245 AssertRCReturn(rc, RTEXITCODE_INIT);
1246
1247 uint32_t fFlags = 0;
1248 uint32_t fOutputFlags = 0;
1249 int cNonOptions = 0;
1250 RTFMODE fMode = 0700;
1251 bool fModeSet = false;
1252 const char *pcszPath = NULL;
1253 const char *pcszTemplate;
1254 char szTemplateWithPath[RTPATH_MAX] = "";
1255
1256 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
1257 && RT_SUCCESS(rc))
1258 {
1259 /* For options that require an argument, ValueUnion has received the value. */
1260 switch (ch)
1261 {
1262 case 'h':
1263 vgsvcToolboxShowUsageHeader();
1264 RTPrintf("%s", g_paszMkTempHelp);
1265 return RTEXITCODE_SUCCESS;
1266
1267 case 'V':
1268 vgsvcToolboxShowVersion();
1269 return RTEXITCODE_SUCCESS;
1270
1271 case VBOXSERVICETOOLBOXOPT_MACHINE_READABLE:
1272 fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE;
1273 break;
1274
1275 case 'd':
1276 fFlags |= VBOXSERVICETOOLBOXMKTEMPFLAG_DIRECTORY;
1277 break;
1278
1279 case 'm':
1280 rc = vgsvcToolboxParseMode(ValueUnion.psz, &fMode);
1281 if (RT_FAILURE(rc))
1282 return RTEXITCODE_SYNTAX;
1283 fModeSet = true;
1284#ifndef RT_OS_WINDOWS
1285 umask(0); /* RTDirCreate workaround */
1286#endif
1287 break;
1288 case 's':
1289 fFlags |= VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE;
1290 break;
1291
1292 case 't':
1293 pcszPath = ValueUnion.psz;
1294 break;
1295
1296 case VINF_GETOPT_NOT_OPTION:
1297 /* RTGetOpt will sort these to the end of the argv vector so
1298 * that we will deal with them afterwards. */
1299 ++cNonOptions;
1300 break;
1301
1302 default:
1303 return RTGetOptPrintError(ch, &ValueUnion);
1304 }
1305 }
1306
1307 /* Print magic/version. */
1308 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE)
1309 {
1310 rc = vgsvcToolboxStrmInit();
1311 if (RT_FAILURE(rc))
1312 RTMsgError("Error while initializing parseable streams, rc=%Rrc\n", rc);
1313 vgsvcToolboxPrintStrmHeader("vbt_mktemp", 1 /* Stream version */);
1314 }
1315
1316 if (fFlags & VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE && fModeSet)
1317 {
1318 toolboxMkTempReport("'-s' and '-m' parameters cannot be used together.\n", "",
1319 true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1320 return RTEXITCODE_SYNTAX;
1321 }
1322
1323 /* We need exactly one template, containing at least one 'X'. */
1324 if (cNonOptions != 1)
1325 {
1326 toolboxMkTempReport("Please specify exactly one template.\n", "", true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1327 return RTEXITCODE_SYNTAX;
1328 }
1329 pcszTemplate = argv[argc - 1];
1330
1331 /* Validate that the template is as IPRT requires (asserted by IPRT). */
1332 if ( RTPathHasPath(pcszTemplate)
1333 || ( !strstr(pcszTemplate, "XXX")
1334 && pcszTemplate[strlen(pcszTemplate) - 1] != 'X'))
1335 {
1336 toolboxMkTempReport("Template '%s' should contain a file name with no path and at least three consecutive 'X' characters or ending in 'X'.\n",
1337 pcszTemplate, true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1338 return RTEXITCODE_FAILURE;
1339 }
1340 if (pcszPath && !RTPathStartsWithRoot(pcszPath))
1341 {
1342 toolboxMkTempReport("Path '%s' should be absolute.\n", pcszPath, true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1343 return RTEXITCODE_FAILURE;
1344 }
1345 if (pcszPath)
1346 {
1347 rc = RTStrCopy(szTemplateWithPath, sizeof(szTemplateWithPath), pcszPath);
1348 if (RT_FAILURE(rc))
1349 {
1350 toolboxMkTempReport("Path '%s' too long.\n", pcszPath, true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1351 return RTEXITCODE_FAILURE;
1352 }
1353 }
1354 else
1355 {
1356 rc = RTPathTemp(szTemplateWithPath, sizeof(szTemplateWithPath));
1357 if (RT_FAILURE(rc))
1358 {
1359 toolboxMkTempReport("Failed to get the temporary directory.\n", "", true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1360 return RTEXITCODE_FAILURE;
1361 }
1362 }
1363 rc = RTPathAppend(szTemplateWithPath, sizeof(szTemplateWithPath), pcszTemplate);
1364 if (RT_FAILURE(rc))
1365 {
1366 toolboxMkTempReport("Template '%s' too long for path.\n", pcszTemplate, true, VERR_INVALID_PARAMETER, fOutputFlags, &rc);
1367 return RTEXITCODE_FAILURE;
1368 }
1369
1370 if (fFlags & VBOXSERVICETOOLBOXMKTEMPFLAG_DIRECTORY)
1371 {
1372 rc = fFlags & VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE
1373 ? RTDirCreateTempSecure(szTemplateWithPath)
1374 : RTDirCreateTemp(szTemplateWithPath, fMode);
1375 toolboxMkTempReport("Created temporary directory '%s'.\n",
1376 szTemplateWithPath, RT_SUCCESS(rc), rc,
1377 fOutputFlags, NULL);
1378 /* RTDirCreateTemp[Secure] sets the template to "" on failure. */
1379 toolboxMkTempReport("The following error occurred while creating a temporary directory from template '%s': %Rrc.\n",
1380 pcszTemplate, RT_FAILURE(rc), rc, fOutputFlags, NULL /*prc*/);
1381 }
1382 else
1383 {
1384 rc = fFlags & VBOXSERVICETOOLBOXMKTEMPFLAG_SECURE
1385 ? RTFileCreateTempSecure(szTemplateWithPath)
1386 : RTFileCreateTemp(szTemplateWithPath, fMode);
1387 toolboxMkTempReport("Created temporary file '%s'.\n",
1388 szTemplateWithPath, RT_SUCCESS(rc), rc,
1389 fOutputFlags, NULL);
1390 /* RTFileCreateTemp[Secure] sets the template to "" on failure. */
1391 toolboxMkTempReport("The following error occurred while creating a temporary file from template '%s': %Rrc.\n",
1392 pcszTemplate, RT_FAILURE(rc), rc, fOutputFlags, NULL /*prc*/);
1393 }
1394 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
1395 vgsvcToolboxPrintStrmTermination();
1396 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1397}
1398
1399
1400/** @todo Document options! */
1401static char g_paszMkDirHelp[] =
1402 " VBoxService [--use-toolbox] vbox_mkdir [<general options>] [<options>]\n"
1403 " <directory>...\n\n"
1404 "Options:\n\n"
1405 " [--mode|-m <mode>] The file mode to set (chmod) on the created\n"
1406 " directories. Default: a=rwx & umask.\n"
1407 " [--parents|-p] Create parent directories as needed, no\n"
1408 " error if the directory already exists.\n"
1409 " [--verbose|-v] Display a message for each created directory.\n"
1410 "\n";
1411
1412
1413/**
1414 * Main function for tool "vbox_mkdir".
1415 *
1416 * @return RTEXITCODE.
1417 * @param argc Number of arguments.
1418 * @param argv Pointer to argument array.
1419 */
1420static RTEXITCODE vgsvcToolboxMkDir(int argc, char **argv)
1421{
1422 static const RTGETOPTDEF s_aOptions[] =
1423 {
1424 { "--mode", 'm', RTGETOPT_REQ_STRING },
1425 { "--parents", 'p', RTGETOPT_REQ_NOTHING},
1426 { "--verbose", 'v', RTGETOPT_REQ_NOTHING}
1427 };
1428
1429 int ch;
1430 RTGETOPTUNION ValueUnion;
1431 RTGETOPTSTATE GetState;
1432 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions),
1433 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1434 AssertRCReturn(rc, RTEXITCODE_INIT);
1435
1436 bool fMakeParentDirs = false;
1437 bool fVerbose = false;
1438 RTFMODE fDirMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG | RTFS_UNIX_IRWXO;
1439 int cDirsCreated = 0;
1440
1441 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1442 {
1443 /* For options that require an argument, ValueUnion has received the value. */
1444 switch (ch)
1445 {
1446 case 'p':
1447 fMakeParentDirs = true;
1448 break;
1449
1450 case 'm':
1451 rc = vgsvcToolboxParseMode(ValueUnion.psz, &fDirMode);
1452 if (RT_FAILURE(rc))
1453 return RTEXITCODE_SYNTAX;
1454#ifndef RT_OS_WINDOWS
1455 umask(0); /* RTDirCreate workaround */
1456#endif
1457 break;
1458
1459 case 'v':
1460 fVerbose = true;
1461 break;
1462
1463 case 'h':
1464 vgsvcToolboxShowUsageHeader();
1465 RTPrintf("%s", g_paszMkDirHelp);
1466 return RTEXITCODE_SUCCESS;
1467
1468 case 'V':
1469 vgsvcToolboxShowVersion();
1470 return RTEXITCODE_SUCCESS;
1471
1472 case VINF_GETOPT_NOT_OPTION:
1473 if (fMakeParentDirs)
1474 /** @todo r=bird: If fVerbose is set, we should also show
1475 * which directories that get created, parents as well as
1476 * omitting existing final dirs. Annoying, but check any
1477 * mkdir implementation (try "mkdir -pv asdf/1/2/3/4"
1478 * twice). */
1479 rc = RTDirCreateFullPath(ValueUnion.psz, fDirMode);
1480 else
1481 rc = RTDirCreate(ValueUnion.psz, fDirMode, 0);
1482 if (RT_FAILURE(rc))
1483 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Could not create directory '%s': %Rra\n",
1484 ValueUnion.psz, rc);
1485 if (fVerbose)
1486 RTMsgInfo("Created directory '%s', mode %#RTfmode\n", ValueUnion.psz, fDirMode);
1487 cDirsCreated++;
1488 break;
1489
1490 default:
1491 return RTGetOptPrintError(ch, &ValueUnion);
1492 }
1493 }
1494 AssertRC(rc);
1495
1496 if (cDirsCreated == 0)
1497 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No directory argument.");
1498
1499 return RTEXITCODE_SUCCESS;
1500}
1501
1502
1503/** @todo Document options! */
1504static char g_paszStatHelp[] =
1505 " VBoxService [--use-toolbox] vbox_stat [<general options>] [<options>]\n"
1506 " <file>...\n\n"
1507 "Display file or file system status.\n\n"
1508 "Options:\n\n"
1509 " [--file-system|-f]\n"
1510 " [--dereference|-L]\n"
1511 " [--terse|-t]\n"
1512 " [--verbose|-v]\n"
1513 "\n";
1514
1515
1516/**
1517 * Main function for tool "vbox_stat".
1518 *
1519 * @return RTEXITCODE.
1520 * @param argc Number of arguments.
1521 * @param argv Pointer to argument array.
1522 */
1523static RTEXITCODE vgsvcToolboxStat(int argc, char **argv)
1524{
1525 static const RTGETOPTDEF s_aOptions[] =
1526 {
1527 { "--file-system", 'f', RTGETOPT_REQ_NOTHING },
1528 { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
1529 { "--machinereadable", VBOXSERVICETOOLBOXOPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
1530 { "--terse", 't', RTGETOPT_REQ_NOTHING },
1531 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }
1532 };
1533
1534 int ch;
1535 RTGETOPTUNION ValueUnion;
1536 RTGETOPTSTATE GetState;
1537 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1538
1539 int rc = VINF_SUCCESS;
1540 uint32_t fOutputFlags = VBOXSERVICETOOLBOXOUTPUTFLAG_LONG; /* Use long mode by default. */
1541 uint32_t fQueryInfoFlags = RTPATH_F_ON_LINK;
1542
1543 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
1544 && RT_SUCCESS(rc))
1545 {
1546 /* For options that require an argument, ValueUnion has received the value. */
1547 switch (ch)
1548 {
1549 case 'f':
1550 RTMsgError("Sorry, option '%s' is not implemented yet!\n", ValueUnion.pDef->pszLong);
1551 rc = VERR_INVALID_PARAMETER;
1552 break;
1553
1554 case 'L':
1555 fQueryInfoFlags &= ~RTPATH_F_ON_LINK;
1556 fQueryInfoFlags |= RTPATH_F_FOLLOW_LINK;
1557 break;
1558
1559 case VBOXSERVICETOOLBOXOPT_MACHINE_READABLE:
1560 fOutputFlags |= VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE;
1561 break;
1562
1563 case 'h':
1564 vgsvcToolboxShowUsageHeader();
1565 RTPrintf("%s", g_paszStatHelp);
1566 return RTEXITCODE_SUCCESS;
1567
1568 case 'V':
1569 vgsvcToolboxShowVersion();
1570 return RTEXITCODE_SUCCESS;
1571
1572 case VINF_GETOPT_NOT_OPTION:
1573 {
1574 Assert(GetState.iNext);
1575 GetState.iNext--;
1576 break;
1577 }
1578
1579 default:
1580 return RTGetOptPrintError(ch, &ValueUnion);
1581 }
1582
1583 /* All flags / options processed? Bail out here.
1584 * Processing the file / directory list comes down below. */
1585 if (ch == VINF_GETOPT_NOT_OPTION)
1586 break;
1587 }
1588
1589 if (RT_SUCCESS(rc))
1590 {
1591 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
1592 {
1593 rc = vgsvcToolboxStrmInit();
1594 if (RT_FAILURE(rc))
1595 RTMsgError("Error while initializing parseable streams, rc=%Rrc\n", rc);
1596 vgsvcToolboxPrintStrmHeader("vbt_stat", 1 /* Stream version */);
1597 }
1598
1599 VGSVCTOOLBOXIDCACHE IdCache;
1600 RT_ZERO(IdCache);
1601
1602 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1603 {
1604 RTFSOBJINFO objInfo;
1605 int rc2 = RTPathQueryInfoEx(ValueUnion.psz, &objInfo, RTFSOBJATTRADD_UNIX, fQueryInfoFlags);
1606 if (RT_FAILURE(rc2))
1607 {
1608 if (!(fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE))
1609 RTMsgError("Cannot stat for '%s': %Rrc\n", ValueUnion.psz, rc2);
1610 }
1611 else
1612 rc2 = vgsvcToolboxPrintFsInfo(ValueUnion.psz, strlen(ValueUnion.psz), fOutputFlags, NULL, &IdCache, &objInfo);
1613 /** @todo r=bird: You're checking rc not rc2 here... */
1614 if (RT_SUCCESS(rc))
1615 rc = rc2;
1616 /* Do not break here -- process every element in the list
1617 * and keep (initial) failing rc. */
1618 }
1619
1620 if (fOutputFlags & VBOXSERVICETOOLBOXOUTPUTFLAG_PARSEABLE) /* Output termination. */
1621 vgsvcToolboxPrintStrmTermination();
1622
1623 /* At this point the overall result (success/failure) should be in rc. */
1624 }
1625 else
1626 RTMsgError("Failed with rc=%Rrc\n", rc);
1627
1628 if (RT_FAILURE(rc))
1629 {
1630 switch (rc)
1631 {
1632 case VERR_ACCESS_DENIED:
1633 return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_ACCESS_DENIED;
1634
1635 case VERR_FILE_NOT_FOUND:
1636 return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_FILE_NOT_FOUND;
1637
1638 case VERR_PATH_NOT_FOUND:
1639 return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_PATH_NOT_FOUND;
1640
1641 case VERR_NET_PATH_NOT_FOUND:
1642 return (RTEXITCODE)VBOXSERVICETOOLBOX_STAT_EXITCODE_NET_PATH_NOT_FOUND;
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