VirtualBox

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

Last change on this file was 99739, checked in by vboxsync, 13 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use