VirtualBox

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

Last change on this file since 61439 was 61093, checked in by vboxsync, 9 years ago

oops

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