VirtualBox

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

Last change on this file since 59131 was 58089, checked in by vboxsync, 9 years ago

VBoxGuest,VBoxService,Doxyfile.Core: fixes.

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