VirtualBox

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

Last change on this file since 37830 was 37814, checked in by vboxsync, 14 years ago

VBoxService/vbox_stat: Also check for directories.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.9 KB
Line 
1/* $Id: VBoxServiceToolBox.cpp 37814 2011-07-07 10:00:48Z vboxsync $ */
2/** @file
3 * VBoxServiceToolBox - Internal (BusyBox-like) toolbox.
4 */
5
6/*
7 * Copyright (C) 2011 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
36#ifndef RT_OS_WINDOWS
37#include <sys/stat.h>
38#endif
39
40#include <VBox/VBoxGuestLib.h>
41#include <VBox/version.h>
42#include "VBoxServiceInternal.h"
43#include "VBoxServiceUtils.h"
44
45
46#define CAT_OPT_NO_CONTENT_INDEXED 1000
47
48#define LS_OPT_MACHINE_READABLE 1000
49
50typedef enum VBOXSERVICETOOLBOXLSFLAG
51{
52 VBOXSERVICETOOLBOXLSFLAG_NONE = 0,
53 VBOXSERVICETOOLBOXLSFLAG_RECURSIVE = 0x00000001,
54 VBOXSERVICETOOLBOXLSFLAG_LONG = 0x00000002,
55 VBOXSERVICETOOLBOXLSFLAG_PARSEABLE = 0x00000004,
56 VBOXSERVICETOOLBOXLSFLAG_SYMLINKS = 0x00000008
57} VBOXSERVICETOOLBOXLSFLAG;
58
59/* Enable the following define to be able to debug/invoke the toolbox
60 * commandos directly from command line, e.g. VBoxService vbox_cat [args] */
61#ifdef DEBUG
62//# define VBOXSERVICE_TOOLBOX_DEBUG
63#endif
64
65/**
66 * An file/directory entry. Used to cache
67 * file names/paths for later processing.
68 */
69typedef struct VBOXSERVICETOOLBOXPATHENTRY
70{
71 /** Our node. */
72 RTLISTNODE Node;
73 /** Name of the entry. */
74 char *pszName;
75} VBOXSERVICETOOLBOXPATHENTRY, *PVBOXSERVICETOOLBOXPATHENTRY;
76
77typedef struct VBOXSERVICETOOLBOXDIRENTRY
78{
79 /** Our node. */
80 RTLISTNODE Node;
81 /** The actual entry. */
82 RTDIRENTRYEX dirEntry;
83} VBOXSERVICETOOLBOXDIRENTRY, *PVBOXSERVICETOOLBOXDIRENTRY;
84
85
86/**
87 * Displays a help text to stdout.
88 */
89static void VBoxServiceToolboxShowUsage(void)
90{
91 RTPrintf("Toolbox Usage:\n"
92 "cat [FILE] - Concatenate FILE(s), or standard input, to standard output.\n"
93 "\n"
94 /** @todo Document options! */
95 "ls [OPTION]... FILE... - List information about the FILEs (the current directory by default).\n"
96 "\n"
97 /** @todo Document options! */
98 "mkdir [OPTION]... DIRECTORY... - Create the DIRECTORY(ies), if they do not already exist.\n"
99 "\n"
100 /** @todo Document options! */
101 "stat [OPTION]... FILE... - Display file or file system status.\n"
102 "\n"
103 /** @todo Document options! */
104 "\n");
105}
106
107
108/**
109 * Displays the program's version number.
110 */
111static void VBoxServiceToolboxShowVersion(void)
112{
113 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
114}
115
116
117/**
118 * Displays an error message because of syntax error.
119 *
120 * @return VERR_INVALID_PARAMETER
121 * @param pszFormat
122 */
123static int VBoxServiceToolboxErrorSyntax(const char *pszFormat, ...)
124{
125 va_list args;
126
127 va_start(args, pszFormat);
128 RTPrintf("\n"
129 "Syntax error: %N\n", pszFormat, &args);
130 va_end(args);
131 return VERR_INVALID_PARAMETER;
132}
133
134
135/**
136 * Destroys a path buffer list.
137 *
138 * @return IPRT status code.
139 * @param pList Pointer to list to destroy.
140 */
141static void VBoxServiceToolboxPathBufDestroy(PRTLISTNODE pList)
142{
143 AssertPtr(pList);
144 /** @todo use RTListForEachSafe */
145 PVBOXSERVICETOOLBOXPATHENTRY pNode = RTListGetFirst(pList, VBOXSERVICETOOLBOXPATHENTRY, Node);
146 while (pNode)
147 {
148 PVBOXSERVICETOOLBOXPATHENTRY pNext = RTListNodeIsLast(pList, &pNode->Node)
149 ? NULL
150 : RTListNodeGetNext(&pNode->Node, VBOXSERVICETOOLBOXPATHENTRY, Node);
151 RTListNodeRemove(&pNode->Node);
152
153 RTStrFree(pNode->pszName);
154
155 RTMemFree(pNode);
156 pNode = pNext;
157 }
158}
159
160
161/**
162 * Adds a path entry (file/directory/whatever) to a given path buffer list.
163 *
164 * @return IPRT status code.
165 * @param pList Pointer to list to add entry to.
166 * @param pszName Name of entry to add.
167 */
168static int VBoxServiceToolboxPathBufAddPathEntry(PRTLISTNODE pList, const char *pszName)
169{
170 AssertPtrReturn(pList, VERR_INVALID_PARAMETER);
171
172 int rc = VINF_SUCCESS;
173 PVBOXSERVICETOOLBOXPATHENTRY pNode = (PVBOXSERVICETOOLBOXPATHENTRY)RTMemAlloc(sizeof(VBOXSERVICETOOLBOXPATHENTRY));
174 if (pNode)
175 {
176 pNode->pszName = RTStrDup(pszName);
177 AssertPtr(pNode->pszName);
178
179 /*rc =*/ RTListAppend(pList, &pNode->Node);
180 }
181 else
182 rc = VERR_NO_MEMORY;
183 return rc;
184}
185
186
187/**
188 * Performs the actual output operation of "vbox_cat".
189 *
190 * @return IPRT status code.
191 * @param hInput Handle of input file (if any) to use;
192 * else stdin will be used.
193 * @param hOutput Handle of output file (if any) to use;
194 * else stdout will be used.
195 */
196static int VBoxServiceToolboxCatOutput(RTFILE hInput, RTFILE hOutput)
197{
198 int rc = VINF_SUCCESS;
199 if (hInput == NIL_RTFILE)
200 {
201 rc = RTFileFromNative(&hInput, RTFILE_NATIVE_STDIN);
202 if (RT_FAILURE(rc))
203 RTMsgError("cat: Could not translate input file to native handle, rc=%Rrc\n", rc);
204 }
205
206 if (hOutput == NIL_RTFILE)
207 {
208 rc = RTFileFromNative(&hOutput, RTFILE_NATIVE_STDOUT);
209 if (RT_FAILURE(rc))
210 RTMsgError("cat: Could not translate output file to native handle, rc=%Rrc\n", rc);
211 }
212
213 if (RT_SUCCESS(rc))
214 {
215 uint8_t abBuf[_64K];
216 size_t cbRead;
217 for (;;)
218 {
219 rc = RTFileRead(hInput, abBuf, sizeof(abBuf), &cbRead);
220 if (RT_SUCCESS(rc) && cbRead > 0)
221 {
222 rc = RTFileWrite(hOutput, abBuf, cbRead, NULL /* Try to write all at once! */);
223 cbRead = 0;
224 }
225 else
226 {
227 if (rc == VERR_BROKEN_PIPE)
228 rc = VINF_SUCCESS;
229 else if (RT_FAILURE(rc))
230 RTMsgError("cat: Error while reading input, rc=%Rrc\n", rc);
231 break;
232 }
233 }
234 }
235 return rc;
236}
237
238
239/**
240 * Main function for tool "vbox_cat".
241 *
242 * @return RTEXITCODE.
243 * @param argc Number of arguments.
244 * @param argv Pointer to argument array.
245 */
246static RTEXITCODE VBoxServiceToolboxCat(int argc, char **argv)
247{
248 static const RTGETOPTDEF s_aOptions[] =
249 {
250 /* Sorted by short ops. */
251 { "--show-all", 'a', RTGETOPT_REQ_NOTHING },
252 { "--number-nonblank", 'b', RTGETOPT_REQ_NOTHING},
253 { NULL, 'e', RTGETOPT_REQ_NOTHING},
254 { NULL, 'E', RTGETOPT_REQ_NOTHING},
255 { "--flags", 'f', RTGETOPT_REQ_STRING},
256 { "--no-content-indexed", CAT_OPT_NO_CONTENT_INDEXED, RTGETOPT_REQ_NOTHING},
257 { "--number", 'n', RTGETOPT_REQ_NOTHING},
258 { "--output", 'o', RTGETOPT_REQ_STRING},
259 { "--squeeze-blank", 's', RTGETOPT_REQ_NOTHING},
260 { NULL, 't', RTGETOPT_REQ_NOTHING},
261 { "--show-tabs", 'T', RTGETOPT_REQ_NOTHING},
262 { NULL, 'u', RTGETOPT_REQ_NOTHING},
263 { "--show-noneprinting", 'v', RTGETOPT_REQ_NOTHING}
264 };
265
266 int ch;
267 RTGETOPTUNION ValueUnion;
268 RTGETOPTSTATE GetState;
269
270 RTGetOptInit(&GetState, argc, argv,
271 s_aOptions, RT_ELEMENTS(s_aOptions),
272 /* Index of argv to start with. */
273#ifdef VBOXSERVICE_TOOLBOX_DEBUG
274 2,
275#else
276 1,
277#endif
278 0);
279
280 int rc = VINF_SUCCESS;
281 bool fUsageOK = true;
282
283 char szOutput[RTPATH_MAX] = { 0 };
284 RTFILE hOutput = NIL_RTFILE;
285 uint32_t fFlags = RTFILE_O_CREATE_REPLACE /* Output file flags. */
286 | RTFILE_O_WRITE
287 | RTFILE_O_DENY_WRITE;
288
289 /* Init directory list. */
290 RTLISTNODE inputList;
291 RTListInit(&inputList);
292
293 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
294 && RT_SUCCESS(rc))
295 {
296 /* For options that require an argument, ValueUnion has received the value. */
297 switch (ch)
298 {
299 case 'a':
300 case 'b':
301 case 'e':
302 case 'E':
303 case 'n':
304 case 's':
305 case 't':
306 case 'T':
307 case 'v':
308 RTMsgError("cat: Sorry, option '%s' is not implemented yet!\n",
309 ValueUnion.pDef->pszLong);
310 rc = VERR_INVALID_PARAMETER;
311 break;
312
313 case 'h':
314 VBoxServiceToolboxShowUsage();
315 return RTEXITCODE_SUCCESS;
316
317 case 'o':
318 if (!RTStrPrintf(szOutput, sizeof(szOutput), ValueUnion.psz))
319 rc = VERR_NO_MEMORY;
320 break;
321
322 case 'u':
323 /* Ignored. */
324 break;
325
326 case 'V':
327 VBoxServiceToolboxShowVersion();
328 return RTEXITCODE_SUCCESS;
329
330 case CAT_OPT_NO_CONTENT_INDEXED:
331 fFlags |= RTFILE_O_NOT_CONTENT_INDEXED;
332 break;
333
334 case VINF_GETOPT_NOT_OPTION:
335 {
336 /* Add file(s) to buffer. This enables processing multiple paths
337 * at once.
338 *
339 * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
340 * processing this loop it's safe to immediately exit on syntax errors
341 * or showing the help text (see above). */
342 rc = VBoxServiceToolboxPathBufAddPathEntry(&inputList, ValueUnion.psz);
343 break;
344 }
345
346 default:
347 return RTGetOptPrintError(ch, &ValueUnion);
348 }
349 }
350
351 if (RT_SUCCESS(rc))
352 {
353 if (strlen(szOutput))
354 {
355 rc = RTFileOpen(&hOutput, szOutput, fFlags);
356 if (RT_FAILURE(rc))
357 RTMsgError("cat: Could not create output file '%s', rc=%Rrc\n",
358 szOutput, rc);
359 }
360
361 if (RT_SUCCESS(rc))
362 {
363 /* Process each input file. */
364 PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
365 RTFILE hInput = NIL_RTFILE;
366 RTListForEach(&inputList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
367 {
368 rc = RTFileOpen(&hInput, pNodeIt->pszName,
369 RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
370 if (RT_SUCCESS(rc))
371 {
372 rc = VBoxServiceToolboxCatOutput(hInput, hOutput);
373 RTFileClose(hInput);
374 }
375 else
376 {
377 PCRTSTATUSMSG pMsg = RTErrGet(rc);
378 if (pMsg)
379 RTMsgError("cat: Could not open input file '%s': %s\n",
380 pNodeIt->pszName, pMsg->pszMsgFull);
381 else
382 RTMsgError("cat: Could not open input file '%s', rc=%Rrc\n", pNodeIt->pszName, rc);
383 }
384
385 if (RT_FAILURE(rc))
386 break;
387 }
388
389 /* If not input files were defined, process stdin. */
390 if (RTListNodeIsFirst(&inputList, &inputList))
391 rc = VBoxServiceToolboxCatOutput(hInput, hOutput);
392 }
393 }
394
395 if (hOutput != NIL_RTFILE)
396 RTFileClose(hOutput);
397 VBoxServiceToolboxPathBufDestroy(&inputList);
398
399 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
400}
401
402/**
403 * Prints information (based on given flags) of a file system object (file/directory/...)
404 * to stdout.
405 *
406 * @return IPRT status code.
407 * @param pszName Object name.
408 * @param cbName Size of object name.
409 * @param uFlags Output / handling flags.
410 * @param pObjInfo Pointer to object information.
411 */
412static int VBoxServiceToolboxLsPrintFsInfo(const char *pszName, uint16_t cbName, uint32_t uFlags,
413 PRTFSOBJINFO pObjInfo)
414{
415 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
416 AssertReturn(cbName, VERR_INVALID_PARAMETER);
417 AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
418
419 RTFMODE fMode = pObjInfo->Attr.fMode;
420 char cFileType;
421 switch (fMode & RTFS_TYPE_MASK)
422 {
423 case RTFS_TYPE_FIFO: cFileType = 'f'; break;
424 case RTFS_TYPE_DEV_CHAR: cFileType = 'c'; break;
425 case RTFS_TYPE_DIRECTORY: cFileType = 'd'; break;
426 case RTFS_TYPE_DEV_BLOCK: cFileType = 'b'; break;
427 case RTFS_TYPE_FILE: cFileType = '-'; break;
428 case RTFS_TYPE_SYMLINK: cFileType = 'l'; break;
429 case RTFS_TYPE_SOCKET: cFileType = 's'; break;
430 case RTFS_TYPE_WHITEOUT: cFileType = 'w'; break;
431 default:
432 cFileType = '?';
433 break;
434 }
435 /** @todo sticy bits++ */
436
437 if (!(uFlags & VBOXSERVICETOOLBOXLSFLAG_LONG))
438 {
439 if (uFlags & VBOXSERVICETOOLBOXLSFLAG_PARSEABLE)
440 {
441 /** @todo Skip node_id if not present/available! */
442 RTPrintf("ftype=%c%cnode_id=%RU64%cname_len=%RU16%cname=%s%c",
443 cFileType, 0, (uint64_t)pObjInfo->Attr.u.Unix.INodeId, 0,
444 cbName, 0, pszName, 0);
445 }
446 else
447 RTPrintf("%c %#18llx %3d %s\n", (uint64_t)pObjInfo->Attr.u.Unix.INodeId,
448 cFileType, cbName, pszName);
449
450 if (uFlags & VBOXSERVICETOOLBOXLSFLAG_PARSEABLE) /* End of data block. */
451 RTPrintf("%c%c", 0, 0);
452 }
453 else
454 {
455 if (uFlags & VBOXSERVICETOOLBOXLSFLAG_PARSEABLE)
456 {
457 RTPrintf("ftype=%c%c", cFileType, 0);
458 RTPrintf("owner_mask=%c%c%c%c",
459 fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
460 fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
461 fMode & RTFS_UNIX_IXUSR ? 'x' : '-', 0);
462 RTPrintf("group_mask=%c%c%c%c",
463 fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
464 fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
465 fMode & RTFS_UNIX_IXGRP ? 'x' : '-', 0);
466 RTPrintf("other_mask=%c%c%c%c",
467 fMode & RTFS_UNIX_IROTH ? 'r' : '-',
468 fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
469 fMode & RTFS_UNIX_IXOTH ? 'x' : '-', 0);
470 RTPrintf("dos_mask=%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
471 fMode & RTFS_DOS_READONLY ? 'R' : '-',
472 fMode & RTFS_DOS_HIDDEN ? 'H' : '-',
473 fMode & RTFS_DOS_SYSTEM ? 'S' : '-',
474 fMode & RTFS_DOS_DIRECTORY ? 'D' : '-',
475 fMode & RTFS_DOS_ARCHIVED ? 'A' : '-',
476 fMode & RTFS_DOS_NT_DEVICE ? 'd' : '-',
477 fMode & RTFS_DOS_NT_NORMAL ? 'N' : '-',
478 fMode & RTFS_DOS_NT_TEMPORARY ? 'T' : '-',
479 fMode & RTFS_DOS_NT_SPARSE_FILE ? 'P' : '-',
480 fMode & RTFS_DOS_NT_REPARSE_POINT ? 'J' : '-',
481 fMode & RTFS_DOS_NT_COMPRESSED ? 'C' : '-',
482 fMode & RTFS_DOS_NT_OFFLINE ? 'O' : '-',
483 fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
484 fMode & RTFS_DOS_NT_ENCRYPTED ? 'E' : '-', 0);
485
486 char szTimeBirth[256];
487 RTTimeSpecToString(&pObjInfo->BirthTime, szTimeBirth, sizeof(szTimeBirth));
488 char szTimeChange[256];
489 RTTimeSpecToString(&pObjInfo->ChangeTime, szTimeChange, sizeof(szTimeChange));
490 char szTimeModification[256];
491 RTTimeSpecToString(&pObjInfo->ModificationTime, szTimeModification, sizeof(szTimeModification));
492 char szTimeAccess[256];
493 RTTimeSpecToString(&pObjInfo->AccessTime, szTimeAccess, sizeof(szTimeAccess));
494
495 RTPrintf("hlinks=%RU32%cuid=%RU32%cgid=%RU32%cst_size=%RI64%calloc=%RI64%c"
496 "st_birthtime=%s%cst_ctime=%s%cst_mtime=%s%cst_atime=%s%c",
497 pObjInfo->Attr.u.Unix.cHardlinks, 0,
498 pObjInfo->Attr.u.Unix.uid, 0,
499 pObjInfo->Attr.u.Unix.gid, 0,
500 pObjInfo->cbObject, 0,
501 pObjInfo->cbAllocated, 0,
502 szTimeBirth, 0,
503 szTimeChange, 0,
504 szTimeModification, 0,
505 szTimeAccess, 0);
506 RTPrintf("cname_len=%RU16%cname=%s%c",
507 cbName, 0, pszName, 0);
508
509 /* End of data block. */
510 RTPrintf("%c%c", 0, 0);
511 }
512 else
513 {
514 RTPrintf("%c", cFileType);
515 RTPrintf("%c%c%c",
516 fMode & RTFS_UNIX_IRUSR ? 'r' : '-',
517 fMode & RTFS_UNIX_IWUSR ? 'w' : '-',
518 fMode & RTFS_UNIX_IXUSR ? 'x' : '-');
519 RTPrintf("%c%c%c",
520 fMode & RTFS_UNIX_IRGRP ? 'r' : '-',
521 fMode & RTFS_UNIX_IWGRP ? 'w' : '-',
522 fMode & RTFS_UNIX_IXGRP ? 'x' : '-');
523 RTPrintf("%c%c%c",
524 fMode & RTFS_UNIX_IROTH ? 'r' : '-',
525 fMode & RTFS_UNIX_IWOTH ? 'w' : '-',
526 fMode & RTFS_UNIX_IXOTH ? 'x' : '-');
527 RTPrintf(" %c%c%c%c%c%c%c%c%c%c%c%c%c%c",
528 fMode & RTFS_DOS_READONLY ? 'R' : '-',
529 fMode & RTFS_DOS_HIDDEN ? 'H' : '-',
530 fMode & RTFS_DOS_SYSTEM ? 'S' : '-',
531 fMode & RTFS_DOS_DIRECTORY ? 'D' : '-',
532 fMode & RTFS_DOS_ARCHIVED ? 'A' : '-',
533 fMode & RTFS_DOS_NT_DEVICE ? 'd' : '-',
534 fMode & RTFS_DOS_NT_NORMAL ? 'N' : '-',
535 fMode & RTFS_DOS_NT_TEMPORARY ? 'T' : '-',
536 fMode & RTFS_DOS_NT_SPARSE_FILE ? 'P' : '-',
537 fMode & RTFS_DOS_NT_REPARSE_POINT ? 'J' : '-',
538 fMode & RTFS_DOS_NT_COMPRESSED ? 'C' : '-',
539 fMode & RTFS_DOS_NT_OFFLINE ? 'O' : '-',
540 fMode & RTFS_DOS_NT_NOT_CONTENT_INDEXED ? 'I' : '-',
541 fMode & RTFS_DOS_NT_ENCRYPTED ? 'E' : '-');
542 RTPrintf(" %d %4d %4d %10lld %10lld %#llx %#llx %#llx %#llx",
543 pObjInfo->Attr.u.Unix.cHardlinks,
544 pObjInfo->Attr.u.Unix.uid,
545 pObjInfo->Attr.u.Unix.gid,
546 pObjInfo->cbObject,
547 pObjInfo->cbAllocated,
548 pObjInfo->BirthTime,
549 pObjInfo->ChangeTime,
550 pObjInfo->ModificationTime,
551 pObjInfo->AccessTime);
552 RTPrintf(" %2d %s\n", cbName, pszName);
553 }
554 }
555
556 return VINF_SUCCESS;
557}
558
559
560/**
561 * Helper routine for ls tool doing the actual parsing and output of
562 * a specified directory.
563 *
564 * @return IPRT status code.
565 * @param pszDir Directory (path) to ouptut.
566 * @param uFlags Flags of type VBOXSERVICETOOLBOXLSFLAG.
567 */
568static int VBoxServiceToolboxLsHandleDir(const char *pszDir, uint32_t uFlags)
569{
570 AssertPtrReturn(pszDir, VERR_INVALID_PARAMETER);
571
572 if (uFlags & VBOXSERVICETOOLBOXLSFLAG_PARSEABLE)
573 RTPrintf("dname=%s%c", pszDir, 0);
574 else if (uFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE)
575 RTPrintf("%s:\n", pszDir);
576
577 char szPathAbs[RTPATH_MAX + 1];
578 int rc = RTPathAbs(pszDir, szPathAbs, sizeof(szPathAbs));
579 if (RT_FAILURE(rc))
580 {
581 RTMsgError("ls: Failed to retrieve absolute path of '%s', rc=%Rrc\n", pszDir, rc);
582 return rc;
583 }
584
585 PRTDIR pDir;
586 rc = RTDirOpen(&pDir, szPathAbs);
587 if (RT_FAILURE(rc))
588 {
589 RTMsgError("ls: Failed to open directory '%s', rc=%Rrc\n", szPathAbs, rc);
590 return rc;
591 }
592
593 RTLISTNODE dirList;
594 RTListInit(&dirList);
595
596 /* To prevent races we need to read in the directory entries once
597 * and process them afterwards: First loop is displaying the current
598 * directory's content and second loop is diving deeper into
599 * sub directories (if wanted). */
600 for (;RT_SUCCESS(rc);)
601 {
602 RTDIRENTRYEX DirEntry;
603 rc = RTDirReadEx(pDir, &DirEntry, NULL, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
604 if (RT_SUCCESS(rc))
605 {
606 PVBOXSERVICETOOLBOXDIRENTRY pNode = (PVBOXSERVICETOOLBOXDIRENTRY)RTMemAlloc(sizeof(VBOXSERVICETOOLBOXDIRENTRY));
607 if (pNode)
608 {
609 memcpy(&pNode->dirEntry, &DirEntry, sizeof(RTDIRENTRYEX));
610 /*rc =*/ RTListAppend(&dirList, &pNode->Node);
611 }
612 else
613 rc = VERR_NO_MEMORY;
614 }
615 }
616
617 if (rc == VERR_NO_MORE_FILES)
618 rc = VINF_SUCCESS;
619
620 int rc2 = RTDirClose(pDir);
621 if (RT_FAILURE(rc2))
622 {
623 RTMsgError("ls: Failed to close dir '%s', rc=%Rrc\n",
624 pszDir, rc2);
625 if (RT_SUCCESS(rc))
626 rc = rc2;
627 }
628
629 if (RT_SUCCESS(rc))
630 {
631 PVBOXSERVICETOOLBOXDIRENTRY pNodeIt;
632 RTListForEach(&dirList, pNodeIt, VBOXSERVICETOOLBOXDIRENTRY, Node)
633 {
634 rc = VBoxServiceToolboxLsPrintFsInfo(pNodeIt->dirEntry.szName, pNodeIt->dirEntry.cbName,
635 uFlags,
636 &pNodeIt->dirEntry.Info);
637 if (RT_FAILURE(rc))
638 break;
639 }
640
641 /* If everything went fine we do the second run (if needed) ... */
642 if ( RT_SUCCESS(rc)
643 && (uFlags & VBOXSERVICETOOLBOXLSFLAG_RECURSIVE))
644 {
645 /* Process all sub-directories. */
646 RTListForEach(&dirList, pNodeIt, VBOXSERVICETOOLBOXDIRENTRY, Node)
647 {
648 RTFMODE fMode = pNodeIt->dirEntry.Info.Attr.fMode;
649 switch (fMode & RTFS_TYPE_MASK)
650 {
651 case RTFS_TYPE_SYMLINK:
652 if (!(uFlags & VBOXSERVICETOOLBOXLSFLAG_SYMLINKS))
653 break;
654 /* Fall through is intentional. */
655 case RTFS_TYPE_DIRECTORY:
656 {
657 const char *pszName = pNodeIt->dirEntry.szName;
658 if ( !RTStrICmp(pszName, ".")
659 || !RTStrICmp(pszName, ".."))
660 {
661 /* Skip dot directories. */
662 continue;
663 }
664
665 char szPath[RTPATH_MAX];
666 rc = RTPathJoin(szPath, sizeof(szPath),
667 pszDir, pNodeIt->dirEntry.szName);
668 if (RT_SUCCESS(rc))
669 rc = VBoxServiceToolboxLsHandleDir(szPath, uFlags);
670 }
671 break;
672
673 default: /* Ignore the rest. */
674 break;
675 }
676 if (RT_FAILURE(rc))
677 break;
678 }
679 }
680 }
681
682 /* Clean up the mess. */
683 PVBOXSERVICETOOLBOXDIRENTRY pNode, pSafe;
684 RTListForEachSafe(&dirList, pNode, pSafe, VBOXSERVICETOOLBOXDIRENTRY, Node)
685 {
686 RTListNodeRemove(&pNode->Node);
687 RTMemFree(pNode);
688 }
689 return rc;
690}
691
692
693/**
694 * Main function for tool "vbox_ls".
695 *
696 * @return RTEXITCODE.
697 * @param argc Number of arguments.
698 * @param argv Pointer to argument array.
699 */
700static RTEXITCODE VBoxServiceToolboxLs(int argc, char **argv)
701{
702 static const RTGETOPTDEF s_aOptions[] =
703 {
704 { "--machinereadable", LS_OPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING },
705 { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
706 { NULL, 'l', RTGETOPT_REQ_NOTHING },
707 { NULL, 'R', RTGETOPT_REQ_NOTHING },
708 { "--verbose", 'v', RTGETOPT_REQ_NOTHING}
709 };
710
711 int ch;
712 RTGETOPTUNION ValueUnion;
713 RTGETOPTSTATE GetState;
714 RTGetOptInit(&GetState, argc, argv,
715 s_aOptions, RT_ELEMENTS(s_aOptions),
716 /* Index of argv to start with. */
717#ifdef VBOXSERVICE_TOOLBOX_DEBUG
718 2,
719#else
720 1,
721#endif
722 RTGETOPTINIT_FLAGS_OPTS_FIRST);
723
724 int rc = VINF_SUCCESS;
725 bool fVerbose = false;
726 uint32_t fFlags = VBOXSERVICETOOLBOXLSFLAG_NONE;
727
728 /* Init file list. */
729 RTLISTNODE fileList;
730 RTListInit(&fileList);
731
732 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
733 && RT_SUCCESS(rc))
734 {
735 /* For options that require an argument, ValueUnion has received the value. */
736 switch (ch)
737 {
738 case 'h':
739 VBoxServiceToolboxShowUsage();
740 return RTEXITCODE_SUCCESS;
741
742 case 'L': /* Dereference symlinks. */
743 fFlags |= VBOXSERVICETOOLBOXLSFLAG_SYMLINKS;
744 break;
745
746 case 'l': /* Print long format. */
747 fFlags |= VBOXSERVICETOOLBOXLSFLAG_LONG;
748 break;
749
750 case LS_OPT_MACHINE_READABLE:
751 fFlags |= VBOXSERVICETOOLBOXLSFLAG_PARSEABLE;
752 break;
753
754 case 'R': /* Recursive processing. */
755 fFlags |= VBOXSERVICETOOLBOXLSFLAG_RECURSIVE;
756 break;
757
758 case 'v':
759 fVerbose = true;
760 break;
761
762 case 'V':
763 VBoxServiceToolboxShowVersion();
764 return RTEXITCODE_SUCCESS;
765
766 case VINF_GETOPT_NOT_OPTION:
767 {
768 /* Add file(s) to buffer. This enables processing multiple files
769 * at once.
770 *
771 * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
772 * processing this loop it's safe to immediately exit on syntax errors
773 * or showing the help text (see above). */
774 rc = VBoxServiceToolboxPathBufAddPathEntry(&fileList, ValueUnion.psz);
775 break;
776 }
777
778 default:
779 return RTGetOptPrintError(ch, &ValueUnion);
780 }
781 }
782
783 if (RT_SUCCESS(rc))
784 {
785 /* If not files given add current directory to list. */
786 if (RTListIsEmpty(&fileList))
787 {
788 char szDirCur[RTPATH_MAX + 1];
789 rc = RTPathGetCurrent(szDirCur, sizeof(szDirCur));
790 if (RT_SUCCESS(rc))
791 {
792 rc = VBoxServiceToolboxPathBufAddPathEntry(&fileList, szDirCur);
793 if (RT_FAILURE(rc))
794 RTMsgError("ls: Adding current directory failed, rc=%Rrc\n", rc);
795 }
796 else
797 RTMsgError("ls: Getting current directory failed, rc=%Rrc\n", rc);
798 }
799
800 /* Print magic/version. */
801 if (fFlags & VBOXSERVICETOOLBOXLSFLAG_PARSEABLE)
802 RTPrintf("hdr_id=vbt_ls%chdr_ver=%u%c", 0, 1 /* Version 1 */, 0);
803
804 PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
805 RTListForEach(&fileList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
806 {
807 if (RTFileExists(pNodeIt->pszName))
808 {
809 RTFILE file;
810 rc = RTFileOpen(&file, pNodeIt->pszName, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ);
811 if (RT_SUCCESS(rc))
812 {
813 RTFSOBJINFO objInfo;
814 rc = RTFileQueryInfo(file, &objInfo, RTFSOBJATTRADD_UNIX);
815 if (RT_SUCCESS(rc))
816 {
817 rc = VBoxServiceToolboxLsPrintFsInfo(pNodeIt->pszName, strlen(pNodeIt->pszName),
818 fFlags, &objInfo);
819 }
820 else
821 RTMsgError("ls: Unable to query information for '%s', rc=%Rrc\n",
822 pNodeIt->pszName, rc);
823 RTFileClose(file);
824 }
825 else
826 RTMsgError("ls: Failed opening '%s', rc=%Rrc\n",
827 pNodeIt->pszName, rc);
828 }
829 else
830 rc = VBoxServiceToolboxLsHandleDir(pNodeIt->pszName, fFlags);
831 if (RT_FAILURE(rc))
832 RTMsgError("ls: Failed while enumerating '%s', rc=%Rrc\n",
833 pNodeIt->pszName, rc);
834 }
835
836 if (fFlags & VBOXSERVICETOOLBOXLSFLAG_PARSEABLE) /* Output termination. */
837 RTPrintf("%c%c%c%c", 0, 0, 0, 0);
838 }
839 else if (fVerbose)
840 RTMsgError("ls: Failed with rc=%Rrc\n", rc);
841
842 VBoxServiceToolboxPathBufDestroy(&fileList);
843 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
844}
845
846
847/**
848 * Main function for tool "vbox_mkdir".
849 *
850 * @return RTEXITCODE.
851 * @param argc Number of arguments.
852 * @param argv Pointer to argument array.
853 */
854static RTEXITCODE VBoxServiceToolboxMkDir(int argc, char **argv)
855{
856 static const RTGETOPTDEF s_aOptions[] =
857 {
858 { "--mode", 'm', RTGETOPT_REQ_STRING },
859 { "--parents", 'p', RTGETOPT_REQ_NOTHING},
860 { "--verbose", 'v', RTGETOPT_REQ_NOTHING}
861 };
862
863 int ch;
864 RTGETOPTUNION ValueUnion;
865 RTGETOPTSTATE GetState;
866 RTGetOptInit(&GetState, argc, argv,
867 s_aOptions, RT_ELEMENTS(s_aOptions),
868 /* Index of argv to start with. */
869#ifdef VBOXSERVICE_TOOLBOX_DEBUG
870 2,
871#else
872 1,
873#endif
874 RTGETOPTINIT_FLAGS_OPTS_FIRST);
875
876 int rc = VINF_SUCCESS;
877 bool fMakeParentDirs = false;
878 bool fVerbose = false;
879
880 RTFMODE newMode = 0;
881 RTFMODE dirMode = RTFS_UNIX_IRWXU | RTFS_UNIX_IRWXG | RTFS_UNIX_IRWXO;
882
883 /* Init directory list. */
884 RTLISTNODE dirList;
885 RTListInit(&dirList);
886
887 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
888 && RT_SUCCESS(rc))
889 {
890 /* For options that require an argument, ValueUnion has received the value. */
891 switch (ch)
892 {
893 case 'h':
894 VBoxServiceToolboxShowUsage();
895 return RTEXITCODE_SUCCESS;
896
897 case 'p':
898 fMakeParentDirs = true;
899 break;
900
901 case 'm':
902 rc = RTStrToUInt32Ex(ValueUnion.psz, NULL, 8 /* Base */, &newMode);
903 if (RT_FAILURE(rc)) /* Only octet based values supported right now! */
904 {
905 RTMsgError("mkdir: Mode flag strings not implemented yet! Use octal numbers instead.\n");
906 return RTEXITCODE_SYNTAX;
907 }
908 break;
909
910 case 'v':
911 fVerbose = true;
912 break;
913
914 case 'V':
915 VBoxServiceToolboxShowVersion();
916 return RTEXITCODE_SUCCESS;
917
918 case VINF_GETOPT_NOT_OPTION:
919 {
920 /* Add path(s) to buffer. This enables processing multiple paths
921 * at once.
922 *
923 * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
924 * processing this loop it's safe to immediately exit on syntax errors
925 * or showing the help text (see above). */
926 rc = VBoxServiceToolboxPathBufAddPathEntry(&dirList, ValueUnion.psz);
927 break;
928 }
929
930 default:
931 return RTGetOptPrintError(ch, &ValueUnion);
932 }
933 }
934
935 if (RT_SUCCESS(rc))
936 {
937 if (fMakeParentDirs || newMode)
938 {
939#ifndef RT_OS_WINDOWS
940 mode_t umaskMode = umask(0); /* Get current umask. */
941 if (newMode)
942 dirMode = newMode;
943#endif
944 }
945
946 PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
947 RTListForEach(&dirList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
948 {
949 rc = fMakeParentDirs ?
950 RTDirCreateFullPath(pNodeIt->pszName, dirMode)
951 : RTDirCreate(pNodeIt->pszName, dirMode);
952
953 if (RT_SUCCESS(rc) && fVerbose)
954 RTMsgError("mkdir: Created directory 's', mode %#RTfmode\n", pNodeIt->pszName, dirMode);
955 else if (RT_FAILURE(rc)) /** @todo Add a switch with more helpful error texts! */
956 {
957 PCRTSTATUSMSG pMsg = RTErrGet(rc);
958 if (pMsg)
959 RTMsgError("mkdir: Could not create directory '%s': %s\n",
960 pNodeIt->pszName, pMsg->pszMsgFull);
961 else
962 RTMsgError("mkdir: Could not create directory '%s', rc=%Rrc\n", pNodeIt->pszName, rc);
963 break;
964 }
965 }
966 }
967 else if (fVerbose)
968 RTMsgError("mkdir: Failed with rc=%Rrc\n", rc);
969
970 VBoxServiceToolboxPathBufDestroy(&dirList);
971 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
972}
973
974
975/**
976 * Main function for tool "vbox_stat".
977 *
978 * @return RTEXITCODE.
979 * @param argc Number of arguments.
980 * @param argv Pointer to argument array.
981 */
982static RTEXITCODE VBoxServiceToolboxStat(int argc, char **argv)
983{
984 static const RTGETOPTDEF s_aOptions[] =
985 {
986 { "--file-system", 'f', RTGETOPT_REQ_NOTHING },
987 { "--dereference", 'L', RTGETOPT_REQ_NOTHING},
988 { "--terse", 't', RTGETOPT_REQ_NOTHING},
989 { "--verbose", 'v', RTGETOPT_REQ_NOTHING}
990 };
991
992 int ch;
993 RTGETOPTUNION ValueUnion;
994 RTGETOPTSTATE GetState;
995 RTGetOptInit(&GetState, argc, argv,
996 s_aOptions, RT_ELEMENTS(s_aOptions),
997 /* Index of argv to start with. */
998#ifdef VBOXSERVICE_TOOLBOX_DEBUG
999 2,
1000#else
1001 1,
1002#endif
1003 RTGETOPTINIT_FLAGS_OPTS_FIRST);
1004
1005 int rc = VINF_SUCCESS;
1006 bool fVerbose = false;
1007
1008 /* Init file list. */
1009 RTLISTNODE fileList;
1010 RTListInit(&fileList);
1011
1012 while ( (ch = RTGetOpt(&GetState, &ValueUnion))
1013 && RT_SUCCESS(rc))
1014 {
1015 /* For options that require an argument, ValueUnion has received the value. */
1016 switch (ch)
1017 {
1018 case 'h':
1019 VBoxServiceToolboxShowUsage();
1020 return RTEXITCODE_SUCCESS;
1021
1022 case 'f':
1023 case 'L':
1024 RTMsgError("stat: Sorry, option '%s' is not implemented yet!\n",
1025 ValueUnion.pDef->pszLong);
1026 rc = VERR_INVALID_PARAMETER;
1027 break;
1028
1029 case 'v':
1030 fVerbose = true;
1031 break;
1032
1033 case 'V':
1034 VBoxServiceToolboxShowVersion();
1035 return RTEXITCODE_SUCCESS;
1036
1037 case VINF_GETOPT_NOT_OPTION:
1038 {
1039 /* Add file(s) to buffer. This enables processing multiple files
1040 * at once.
1041 *
1042 * Since the non-options (RTGETOPTINIT_FLAGS_OPTS_FIRST) come last when
1043 * processing this loop it's safe to immediately exit on syntax errors
1044 * or showing the help text (see above). */
1045 rc = VBoxServiceToolboxPathBufAddPathEntry(&fileList, ValueUnion.psz);
1046 break;
1047 }
1048
1049 default:
1050 return RTGetOptPrintError(ch, &ValueUnion);
1051 }
1052 }
1053
1054 if (RT_SUCCESS(rc))
1055 {
1056 PVBOXSERVICETOOLBOXPATHENTRY pNodeIt;
1057 RTListForEach(&fileList, pNodeIt, VBOXSERVICETOOLBOXPATHENTRY, Node)
1058 {
1059 /* Only check for file existence for now. */
1060 if (RTFileExists(pNodeIt->pszName))
1061 {
1062 /** @todo Do some more work (query size etc.) here later.
1063 * Not needed for now. */
1064 }
1065 else if (RTDirExists(pNodeIt->pszName))
1066 {
1067 /** @todo Do some more work (query size etc.) here later.
1068 * Not needed for now. */
1069 }
1070 else
1071 {
1072 RTMsgError("stat: Cannot stat for '%s': No such file or directory\n",
1073 pNodeIt->pszName);
1074 rc = VERR_FILE_NOT_FOUND;
1075 /* Do not break here -- process every element in the list
1076 * and keep failing rc. */
1077 }
1078 }
1079
1080 if (RTListIsEmpty(&fileList))
1081 RTMsgError("stat: Missing operand\n");
1082 }
1083 else if (fVerbose)
1084 RTMsgError("stat: Failed with rc=%Rrc\n", rc);
1085
1086 VBoxServiceToolboxPathBufDestroy(&fileList);
1087 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1088}
1089
1090
1091/**
1092 * Entry point for internal toolbox.
1093 *
1094 * @return True if an internal tool was handled, false if not.
1095 * @param argc Number of arguments.
1096 * @param argv Pointer to argument array.
1097 * @param prcExit Where to store the exit code when an
1098 * internal toolbox command was handled.
1099 */
1100bool VBoxServiceToolboxMain(int argc, char **argv, RTEXITCODE *prcExit)
1101{
1102 if (argc > 0) /* Do we have at least a main command? */
1103 {
1104 int iCmdIdx = 0;
1105#ifdef VBOXSERVICE_TOOLBOX_DEBUG
1106 iCmdIdx = 1;
1107#endif
1108 if ( !strcmp(argv[iCmdIdx], "cat")
1109 || !strcmp(argv[iCmdIdx], "vbox_cat"))
1110 {
1111 *prcExit = VBoxServiceToolboxCat(argc, argv);
1112 return true;
1113 }
1114
1115 if ( !strcmp(argv[iCmdIdx], "ls")
1116 || !strcmp(argv[iCmdIdx], "vbox_ls"))
1117 {
1118 *prcExit = VBoxServiceToolboxLs(argc, argv);
1119 return true;
1120 }
1121
1122 if ( !strcmp(argv[iCmdIdx], "mkdir")
1123 || !strcmp(argv[iCmdIdx], "vbox_mkdir"))
1124 {
1125 *prcExit = VBoxServiceToolboxMkDir(argc, argv);
1126 return true;
1127 }
1128
1129 if ( !strcmp(argv[iCmdIdx], "stat")
1130 || !strcmp(argv[iCmdIdx], "vbox_stat"))
1131 {
1132 *prcExit = VBoxServiceToolboxStat(argc, argv);
1133 return true;
1134 }
1135 }
1136 return false;
1137}
1138
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette