VirtualBox

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

Last change on this file since 60906 was 60641, checked in by vboxsync, 9 years ago

VBoxService/VBoxServiceToolBox.cpp: Updated assertion.

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