VirtualBox

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

Last change on this file since 62521 was 62470, checked in by vboxsync, 9 years ago

Additions: scm

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