VirtualBox

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

Last change on this file since 75930 was 75930, checked in by vboxsync, 6 years ago

VBoxService: doxygen fix

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