VirtualBox

source: vbox/trunk/src/VBox/Main/VBoxExtPackHelperApp.cpp@ 34434

最後變更 在這個檔案從34434是 34422,由 vboxsync 提交於 14 年 前

VBoxExtPackHelperApp.cpp: More code.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 44.6 KB
 
1/* $Id: VBoxExtPackHelperApp.cpp 34422 2010-11-26 17:47:08Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Extension Pack Helper Application, usually set-uid-to-root.
4 */
5
6/*
7 * Copyright (C) 2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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 "include/ExtPackUtil.h"
23
24#include <iprt/buildconfig.h>
25//#include <iprt/ctype.h>
26#include <iprt/dir.h>
27//#include <iprt/env.h>
28#include <iprt/file.h>
29#include <iprt/fs.h>
30#include <iprt/getopt.h>
31#include <iprt/initterm.h>
32#include <iprt/manifest.h>
33#include <iprt/message.h>
34#include <iprt/param.h>
35#include <iprt/path.h>
36//#include <iprt/pipe.h>
37#include <iprt/process.h>
38#include <iprt/string.h>
39#include <iprt/stream.h>
40#include <iprt/vfs.h>
41#include <iprt/zip.h>
42
43#include <VBox/log.h>
44#include <VBox/err.h>
45#include <VBox/sup.h>
46#include <VBox/version.h>
47
48
49/*******************************************************************************
50* Defined Constants And Macros *
51*******************************************************************************/
52/** The maximum entry name length.
53 * Play short and safe. */
54#define VBOX_EXTPACK_MAX_ENTRY_NAME_LENGTH 128
55
56
57#ifdef IN_RT_R3
58/* Override RTAssertShouldPanic to prevent gdb process creation. */
59RTDECL(bool) RTAssertShouldPanic(void)
60{
61 return true;
62}
63#endif
64
65
66
67/**
68 * Handle the special standard options when these are specified after the
69 * command.
70 *
71 * @param ch The option character.
72 */
73static RTEXITCODE DoStandardOption(int ch)
74{
75 switch (ch)
76 {
77 case 'h':
78 {
79 RTMsgInfo(VBOX_PRODUCT " Extension Pack Helper App\n"
80 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
81 "All rights reserved.\n"
82 "\n"
83 "This NOT intended for general use, please use VBoxManage instead\n"
84 "or call the IExtPackManager API directly.\n"
85 "\n"
86 "Usage: %s <command> [options]\n"
87 "Commands:\n"
88 " install --base-dir <dir> --certificate-dir <dir> --name <name> \\\n"
89 " --tarball <tarball> --tarball-fd <fd>\n"
90 " uninstall --base-dir <dir> --name <name>\n"
91 " cleanup --base-dir <dir>\n"
92 , RTProcShortName());
93 return RTEXITCODE_SUCCESS;
94 }
95
96 case 'V':
97 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
98 return RTEXITCODE_SUCCESS;
99
100 default:
101 AssertFailedReturn(RTEXITCODE_FAILURE);
102 }
103}
104
105
106/**
107 * Checks if the cerficiate directory is valid.
108 *
109 * @returns true if it is valid, false if it isn't.
110 * @param pszCertDir The certificate directory to validate.
111 */
112static bool IsValidCertificateDir(const char *pszCertDir)
113{
114 /*
115 * Just be darn strict for now.
116 */
117 char szCorrect[RTPATH_MAX];
118 int rc = RTPathAppPrivateNoArch(szCorrect, sizeof(szCorrect));
119 if (RT_FAILURE(rc))
120 return false;
121 rc = RTPathAppend(szCorrect, sizeof(szCorrect), VBOX_EXTPACK_CERT_DIR);
122 if (RT_FAILURE(rc))
123 return false;
124
125 return RTPathCompare(szCorrect, pszCertDir) == 0;
126}
127
128
129/**
130 * Checks if the base directory is valid.
131 *
132 * @returns true if it is valid, false if it isn't.
133 * @param pszBaesDir The base directory to validate.
134 */
135static bool IsValidBaseDir(const char *pszBaseDir)
136{
137 /*
138 * Just be darn strict for now.
139 */
140 char szCorrect[RTPATH_MAX];
141 int rc = RTPathAppPrivateArch(szCorrect, sizeof(szCorrect));
142 if (RT_FAILURE(rc))
143 return false;
144 rc = RTPathAppend(szCorrect, sizeof(szCorrect), VBOX_EXTPACK_INSTALL_DIR);
145 if (RT_FAILURE(rc))
146 return false;
147
148 return RTPathCompare(szCorrect, pszBaseDir) == 0;
149}
150
151
152/**
153 * Cleans up a temporary extension pack directory.
154 *
155 * This is used by 'uninstall', 'cleanup' and in the failure path of 'install'.
156 *
157 * @returns The program exit code.
158 * @param pszDir The directory to clean up. The caller is
159 * responsible for making sure this is valid.
160 * @param fTemporary Whether this is a temporary install directory or
161 * not.
162 */
163static RTEXITCODE RemoveExtPackDir(const char *pszDir, bool fTemporary)
164{
165 /** @todo May have to undo 555 modes here later. */
166 int rc = RTDirRemoveRecursive(pszDir, RTDIRRMREC_F_CONTENT_AND_DIR);
167 if (RT_FAILURE(rc))
168 return RTMsgErrorExit(RTEXITCODE_FAILURE,
169 "Failed to delete the %sextension pack directory: %Rrc ('%s')",
170 fTemporary ? "temporary " : "", rc, pszDir);
171 return RTEXITCODE_SUCCESS;
172}
173
174
175/**
176 * Rewinds the tarball file handle and creates a gunzip | tar chain that
177 * results in a filesystem stream.
178 *
179 * @returns success or failure, message displayed on failure.
180 * @param hTarballFile The handle to the tarball file.
181 * @param phTarFss Where to return the filesystem stream handle.
182 */
183static RTEXITCODE OpenTarFss(RTFILE hTarballFile, PRTVFSFSSTREAM phTarFss)
184{
185 /*
186 * Rewind the file and set up a VFS chain for it.
187 */
188 int rc = RTFileSeek(hTarballFile, 0, RTFILE_SEEK_BEGIN, NULL);
189 if (RT_FAILURE(rc))
190 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed seeking to the start of the tarball: %Rrc\n", rc);
191
192 RTVFSIOSTREAM hTarballIos;
193 rc = RTVfsIoStrmFromRTFile(hTarballFile, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN, true /*fLeaveOpen*/,
194 &hTarballIos);
195 if (RT_FAILURE(rc))
196 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsIoStrmFromRTFile failed: %Rrc\n", rc);
197
198 RTVFSIOSTREAM hGunzipIos;
199 rc = RTZipGzipDecompressIoStream(hTarballIos, 0 /*fFlags*/, &hGunzipIos);
200 if (RT_SUCCESS(rc))
201 {
202 RTVFSFSSTREAM hTarFss;
203 rc = RTZipTarFsStreamFromIoStream(hGunzipIos, 0 /*fFlags*/, &hTarFss);
204 if (RT_SUCCESS(rc))
205 {
206 RTVfsIoStrmRelease(hGunzipIos);
207 RTVfsIoStrmRelease(hTarballIos);
208 *phTarFss = hTarFss;
209 return RTEXITCODE_SUCCESS;
210 }
211 RTMsgError("RTZipTarFsStreamFromIoStream failed: %Rrc\n", rc);
212 RTVfsIoStrmRelease(hGunzipIos);
213 }
214 else
215 RTMsgError("RTZipGzipDecompressIoStream failed: %Rrc\n", rc);
216 RTVfsIoStrmRelease(hTarballIos);
217 return RTEXITCODE_FAILURE;
218}
219
220
221/**
222 * Sets the permissions of the temporary extension pack directory just before
223 * renaming it.
224 *
225 * By default the temporary directory is only accessible by root, this function
226 * will make it world readable and browseable.
227 *
228 * @returns The program exit code.
229 * @param pszDir The temporary extension pack directory.
230 */
231static RTEXITCODE SetExtPackPermissions(const char *pszDir)
232{
233#if !defined(RT_OS_WINDOWS)
234 int rc = RTPathSetMode(pszDir, 0755);
235 if (RT_FAILURE(rc))
236 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to set directory permissions: %Rrc ('%s')", rc, pszDir);
237#else
238 /** @todo */
239#endif
240
241 return RTEXITCODE_SUCCESS;
242}
243
244
245/**
246 * Verifies the manifest and its signature.
247 *
248 * @returns Program exit code, failure with message.
249 * @param hOurManifest The manifest we compiled.
250 * @param hManifestFile The manifest file in the extension pack.
251 * @param hSignatureFile The manifest signature file.
252 */
253static RTEXITCODE VerifyManifestAndSignature(RTMANIFEST hOurManifest, RTVFSFILE hManifestFile, RTVFSFILE hSignatureFile)
254{
255 /*
256 * Read the manifest from the extension pack.
257 */
258 RTMANIFEST hTheirManifest;
259 int rc = RTManifestCreate(0 /*fFlags*/, &hTheirManifest);
260 if (RT_FAILURE(rc))
261 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTManifestCreate failed: %Rrc", rc);
262
263 RTEXITCODE rcExit;
264 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hManifestFile);
265 rc = RTManifestReadStandard(hTheirManifest, hVfsIos);
266 RTVfsIoStrmRelease(hVfsIos);
267 if (RT_SUCCESS(rc))
268 {
269 /*
270 * Compare the manifests.
271 */
272 static const char *s_apszIgnoreEntries[] =
273 {
274 VBOX_EXTPACK_MANIFEST_NAME,
275 VBOX_EXTPACK_SIGNATURE_NAME,
276 "./" VBOX_EXTPACK_MANIFEST_NAME,
277 "./" VBOX_EXTPACK_SIGNATURE_NAME,
278 NULL
279 };
280 char szEntry[RTPATH_MAX];
281 rc = RTManifestEqualsEx(hOurManifest, hTheirManifest, &s_apszIgnoreEntries[0], NULL, szEntry, sizeof(szEntry));
282 if (RT_SUCCESS(rc))
283 {
284 /*
285 * Validate the manifest file signature.
286 */
287 /** @todo implement signature stuff */
288
289 }
290 else if (rc == VERR_NOT_EQUAL && szEntry[0])
291 RTMsgError("Manifest mismatch: '%s'", szEntry);
292 else
293 RTMsgError("RTManifestEqualsEx failed: %Rrc", rc);
294 }
295 else
296 RTMsgError("Error parsing '%s': %Rrc", VBOX_EXTPACK_MANIFEST_NAME);
297
298 RTManifestRelease(hTheirManifest);
299 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
300}
301
302
303/**
304 * Validates a name in an extension pack.
305 *
306 * We restrict the charset to try make sure the extension pack can be unpacked
307 * on all file systems.
308 *
309 * @returns Program exit code, failure with message.
310 * @param pszName The name to validate.
311 */
312static RTEXITCODE ValidateNameInExtPack(const char *pszName)
313{
314 if (RTPathStartsWithRoot(pszName))
315 return RTMsgErrorExit(RTEXITCODE_FAILURE, "'%s': starts with root spec", pszName);
316
317 const char *pszErr = NULL;
318 const char *psz = pszName;
319 int ch;
320 while ((ch = *psz) != '\0')
321 {
322 /* Character set restrictions. */
323 if (ch < 0 || ch >= 128)
324 {
325 pszErr = "Only 7-bit ASCII allowed";
326 break;
327 }
328 if (ch <= 31 || ch == 127)
329 {
330 pszErr = "No control characters are not allowed";
331 break;
332 }
333 if (ch == '\\')
334 {
335 pszErr = "Only backward slashes are not allowed";
336 break;
337 }
338 if (strchr("'\":;*?|[]<>(){}", ch))
339 {
340 pszErr = "The characters ', \", :, ;, *, ?, |, [, ], <, >, (, ), { and } are not allowed";
341 break;
342 }
343
344 /* Take the simple way out and ban all ".." sequences. */
345 if ( ch == '.'
346 && psz[1] == '.')
347 {
348 pszErr = "Double dot sequence are not allowed";
349 break;
350 }
351
352 /* Keep the tree shallow or the hardening checks will fail. */
353 if (psz - pszName > VBOX_EXTPACK_MAX_ENTRY_NAME_LENGTH)
354 {
355 pszErr = "Too long";
356 break;
357 }
358 }
359
360 if (pszErr)
361 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Bad member name '%s' (pos %zu): %s", pszName, (size_t)(psz - pszName), pszErr);
362 return RTEXITCODE_SUCCESS;
363}
364
365
366/**
367 * Validates a file in an extension pack.
368 *
369 * @returns Program exit code, failure with message.
370 * @param pszName The name of the file.
371 * @param hVfsObj The VFS object.
372 */
373static RTEXITCODE ValidateFileInExtPack(const char *pszName, RTVFSOBJ hVfsObj)
374{
375 RTEXITCODE rcExit = ValidateNameInExtPack(pszName);
376 if (rcExit == RTEXITCODE_SUCCESS)
377 {
378 RTFSOBJINFO ObjInfo;
379 int rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
380 if (RT_SUCCESS(rc))
381 {
382 if (ObjInfo.cbObject >= 9*_1G64)
383 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "'%s': too large (%'RU64 bytes)",
384 pszName, (uint64_t)ObjInfo.cbObject);
385 if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
386 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
387 "The alleged file '%s' has a mode mask saying differently (%RTfmode)",
388 pszName, ObjInfo.Attr.fMode);
389 }
390 else
391 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszName, rc);
392 }
393 return rcExit;
394}
395
396
397/**
398 * Validates a directory in an extension pack.
399 *
400 * @returns Program exit code, failure with message.
401 * @param pszName The name of the directory.
402 * @param hVfsObj The VFS object.
403 */
404static RTEXITCODE ValidateDirInExtPack(const char *pszName, RTVFSOBJ hVfsObj)
405{
406 RTEXITCODE rcExit = ValidateNameInExtPack(pszName);
407 if (rcExit == RTEXITCODE_SUCCESS)
408 {
409 RTFSOBJINFO ObjInfo;
410 int rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
411 if (RT_SUCCESS(rc))
412 {
413 if (!RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
414 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
415 "The alleged directory '%s' has a mode mask saying differently (%RTfmode)",
416 pszName, ObjInfo.Attr.fMode);
417 }
418 else
419 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszName, rc);
420 }
421 return rcExit;
422}
423
424/**
425 * Validates a member of an extension pack.
426 *
427 * @returns Program exit code, failure with message.
428 * @param pszName The name of the directory.
429 * @param enmType The object type.
430 * @param hVfsObj The VFS object.
431 */
432static RTEXITCODE ValidateMemberOfExtPack(const char *pszName, RTVFSOBJTYPE enmType, RTVFSOBJ hVfsObj)
433{
434 RTEXITCODE rcExit;
435 if ( enmType == RTVFSOBJTYPE_FILE
436 || enmType == RTVFSOBJTYPE_IO_STREAM)
437 rcExit = ValidateFileInExtPack(pszName, hVfsObj);
438 else if ( enmType == RTVFSOBJTYPE_DIR
439 || enmType == RTVFSOBJTYPE_BASE)
440 rcExit = ValidateDirInExtPack(pszName, hVfsObj);
441 else
442 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "'%s' is not a file or directory (enmType=%d)", pszName, enmType);
443 return rcExit;
444}
445
446
447/**
448 * Validates the extension pack tarball prior to unpacking.
449 *
450 * Operations performed:
451 * - Hardening checks.
452 * - XML validity check.
453 * - Name check (against XML).
454 *
455 * @returns The program exit code.
456 * @param pszDir The directory where the extension pack has been
457 * unpacked.
458 * @param pszName The expected extension pack name.
459 * @param pszTarball The name of the tarball in case we have to
460 * complain about something.
461 */
462static RTEXITCODE ValidateUnpackedExtPack(const char *pszDir, const char *pszTarball, const char *pszName)
463{
464 /** @todo */
465 return RTEXITCODE_SUCCESS;
466}
467
468
469/**
470 * Unpacks the extension pack into the specified directory.
471 *
472 * This will apply ownership and permission changes to all the content, the
473 * exception is @a pszDirDst which will be handled by SetExtPackPermissions.
474 *
475 * @returns The program exit code.
476 * @param hTarballFile The tarball to unpack.
477 * @param pszDirDst Where to unpack it.
478 * @param pszTarball The name of the tarball in case we have to
479 * complain about something.
480 * @todo Needs to take the previous verified manifest as input.
481 */
482static RTEXITCODE UnpackExtPack(RTFILE hTarballFile, const char *pszDirDst, const char *pszTarball)
483{
484 /*
485 * Set up the destination path and directory.
486 */
487 char szDstPath[RTPATH_MAX];
488 int rc = RTPathAbs(pszDirDst, szDstPath, sizeof(szDstPath) - VBOX_EXTPACK_MAX_ENTRY_NAME_LENGTH - 2);
489 if (RT_FAILURE(rc))
490 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs('%s',,) failed: %Rrc", pszDirDst, rc);
491 size_t offDstPath = RTPathStripTrailingSlash(szDstPath);
492
493 rc = RTDirCreate(szDstPath, 0700);
494 if (RT_FAILURE(rc))
495 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDirCreate('%s',0700) failed: %Rrc", szDstPath, rc);
496 szDstPath[offDstPath++] = '/';
497 szDstPath[offDstPath] = '\0';
498
499 /*
500 * Open the tar.gz filesystem stream and set up an manifest in-memory file.
501 */
502 RTVFSFSSTREAM hTarFss;
503 RTEXITCODE rcExit = OpenTarFss(hTarballFile, &hTarFss);
504 if (rcExit != RTEXITCODE_SUCCESS)
505 return rcExit;
506
507 RTMANIFEST hUnpackManifest;
508 rc = RTManifestCreate(0 /*fFlags*/, &hUnpackManifest);
509 if (RT_SUCCESS(rc))
510 {
511 /*
512 * Process the tarball (would be nice to move this to a function).
513 */
514 for (;;)
515 {
516 /*
517 * Get the next stream object.
518 */
519 char *pszName;
520 RTVFSOBJ hVfsObj;
521 RTVFSOBJTYPE enmType;
522 rc = RTVfsFsStrmNext(hTarFss, &pszName, &enmType, &hVfsObj);
523 if (RT_FAILURE(rc))
524 {
525 if (rc != VERR_EOF)
526 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsFsStrmNext failed: %Rrc", rc);
527 break;
528 }
529
530 /*
531 * Check the type & name validity then unpack it.
532 */
533 rcExit = ValidateMemberOfExtPack(pszName, enmType, hVfsObj);
534 if (rcExit == RTEXITCODE_SUCCESS)
535 {
536 szDstPath[offDstPath] = '\0';
537 rc = RTStrCopy(&szDstPath[offDstPath], sizeof(szDstPath) - offDstPath, pszName);
538 if (RT_SUCCESS(rc))
539 {
540 if ( enmType == RTVFSOBJTYPE_FILE
541 || enmType == RTVFSOBJTYPE_IO_STREAM)
542 {
543 RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
544 /// @todo rc = RTManifestEntryAddIoStream(hOurManifest, hVfsIos, pszName, RTMANIFEST_ATTR_SIZE | RTMANIFEST_ATTR_SHA256);
545 // if (RT_FAILURE(rc))
546 // rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTManifestEntryAddIoStream failed on '%s': %Rrc", pszName, rc);
547 RTVfsIoStrmRelease(hVfsIos);
548 }
549 else
550 {
551 rc = RTDirCreate(szDstPath, 0755);
552 if (RT_FAILURE(rc))
553 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create directory '%s': %Rrc", pszName, rc);
554 }
555 }
556 else
557 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Name is too long: '%s' (%Rrc)", pszName, rc);
558 }
559
560 /*
561 * Clean up and break out on failure.
562 */
563 RTVfsObjRelease(hVfsObj);
564 RTStrFree(pszName);
565 if (rcExit != RTEXITCODE_SUCCESS)
566 break;
567 }
568
569 /*
570 * Check that what we just extracted matches the already verified
571 * manifest.
572 */
573 //if (rcExit == RTEXITCODE_SUCCESS)
574 /// @todo
575
576 RTManifestRelease(hUnpackManifest);
577 }
578 RTVfsFsStrmRelease(hTarFss);
579
580 return rcExit;
581}
582
583
584/**
585 * Validates the extension pack tarball prior to unpacking.
586 *
587 * Operations performed:
588 * - Manifest check.
589 * - Manifest seal check.
590 * - Mandatory files.
591 *
592 * @returns The program exit code.
593 * @param hTarballFile The handle to open the @a pszTarball file.
594 * @param pszTarball The name of the tarball in case we have to
595 * complain about something.
596 *
597 * @todo Should validate the XML and name.
598 * @todo Needs to return a manifest.
599 */
600static RTEXITCODE ValidateExtPackTarball(RTFILE hTarballFile, const char *pszTarball)
601{
602 /*
603 * Open the tar.gz filesystem stream and set up an manifest in-memory file.
604 */
605 RTVFSFSSTREAM hTarFss;
606 RTEXITCODE rcExit = OpenTarFss(hTarballFile, &hTarFss);
607 if (rcExit != RTEXITCODE_SUCCESS)
608 return rcExit;
609
610 RTMANIFEST hOurManifest;
611 int rc = RTManifestCreate(0 /*fFlags*/, &hOurManifest);
612 if (RT_SUCCESS(rc))
613 {
614 /*
615 * Process the tarball (would be nice to move this to a function).
616 */
617 RTVFSFILE hXmlFile = NIL_RTVFSFILE;
618 RTVFSFILE hManifestFile = NIL_RTVFSFILE;
619 RTVFSFILE hSignatureFile= NIL_RTVFSFILE;
620 for (;;)
621 {
622 /*
623 * Get the next stream object.
624 */
625 char *pszName;
626 RTVFSOBJ hVfsObj;
627 RTVFSOBJTYPE enmType;
628 rc = RTVfsFsStrmNext(hTarFss, &pszName, &enmType, &hVfsObj);
629 if (RT_FAILURE(rc))
630 {
631 if (rc != VERR_EOF)
632 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsFsStrmNext failed: %Rrc", rc);
633 break;
634 }
635
636 /*
637 * Check the type & name validity.
638 */
639 rcExit = ValidateMemberOfExtPack(pszName, enmType, hVfsObj);
640 if (rcExit == RTEXITCODE_SUCCESS)
641 {
642 /*
643 * Check if this is one of the standard files.
644 */
645 const char *pszAdjName = pszName[0] == '.' && pszName[1] == '/' ? &pszName[2] : pszName;
646 PRTVFSFILE phVfsFile;
647 if (!strcmp(pszAdjName, VBOX_EXTPACK_DESCRIPTION_NAME))
648 phVfsFile = &hXmlFile;
649 else if (!strcmp(pszAdjName, VBOX_EXTPACK_MANIFEST_NAME))
650 phVfsFile = &hManifestFile;
651 else if (!strcmp(pszAdjName, VBOX_EXTPACK_SIGNATURE_NAME))
652 phVfsFile = &hSignatureFile;
653 else
654 phVfsFile = NULL;
655 if (phVfsFile)
656 {
657 /*
658 * Make sure it's a file and that it isn't too large.
659 */
660 if (*phVfsFile != NIL_RTVFSFILE)
661 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "There can only be one '%s'", pszAdjName);
662 else if (enmType != RTVFSOBJTYPE_IO_STREAM && enmType != RTVFSOBJTYPE_FILE)
663 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Standard member '%s' is not a file", pszAdjName);
664 else
665 {
666 RTFSOBJINFO ObjInfo;
667 rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
668 if (RT_SUCCESS(rc))
669 {
670 if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
671 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Standard member '%s' is not a file", pszAdjName);
672 else if (ObjInfo.cbObject >= _1M)
673 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
674 "Standard member '%s' is too large: %'RU64 bytes (max 1 MB)",
675 pszAdjName, (uint64_t)ObjInfo.cbObject);
676 else
677 {
678 /*
679 * Make an in memory copy of the stream.
680 */
681 RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
682 rc = RTVfsMemorizeIoStreamAsFile(hVfsIos, RTFILE_O_READ, phVfsFile);
683 if (RT_SUCCESS(rc))
684 {
685 /*
686 * To simplify the code below, replace
687 * hVfsObj with the memorized file.
688 */
689 RTVfsObjRelease(hVfsObj);
690 hVfsObj = RTVfsObjFromFile(*phVfsFile);
691 }
692 else
693 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
694 "RTVfsMemorizeIoStreamAsFile failed on '%s': %Rrc", pszName, rc);
695 RTVfsIoStrmRelease(hVfsIos);
696 }
697 }
698 else
699 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszName, rc);
700 }
701 }
702 }
703
704 /*
705 * Add any I/O stream to the manifest
706 */
707 if ( rcExit == RTEXITCODE_SUCCESS
708 && ( enmType == RTVFSOBJTYPE_FILE
709 || enmType == RTVFSOBJTYPE_IO_STREAM))
710 {
711 RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
712 rc = RTManifestEntryAddIoStream(hOurManifest, hVfsIos, pszName, RTMANIFEST_ATTR_SIZE | RTMANIFEST_ATTR_SHA256);
713 if (RT_FAILURE(rc))
714 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTManifestEntryAddIoStream failed on '%s': %Rrc", pszName, rc);
715 RTVfsIoStrmRelease(hVfsIos);
716 }
717
718 /*
719 * Clean up and break out on failure.
720 */
721 RTVfsObjRelease(hVfsObj);
722 RTStrFree(pszName);
723 if (rcExit != RTEXITCODE_SUCCESS)
724 break;
725 }
726
727 /*
728 * If we've successfully processed the tarball, verify that the
729 * mandatory files are present.
730 */
731 if (rcExit == RTEXITCODE_SUCCESS)
732 {
733 if (hXmlFile == NIL_RTVFSFILE)
734 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Mandator file '%s' is missing", VBOX_EXTPACK_DESCRIPTION_NAME);
735 if (hManifestFile == NIL_RTVFSFILE)
736 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Mandator file '%s' is missing", VBOX_EXTPACK_MANIFEST_NAME);
737 if (hSignatureFile == NIL_RTVFSFILE)
738 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Mandator file '%s' is missing", VBOX_EXTPACK_SIGNATURE_NAME);
739 }
740
741 /*
742 * Check the manifest and it's signature.
743 */
744 if (rcExit == RTEXITCODE_SUCCESS)
745 rcExit = VerifyManifestAndSignature(hOurManifest, hManifestFile, hSignatureFile);
746
747 RTManifestRelease(hOurManifest); /** @todo return this and use it during unpacking */
748
749 RTVfsFileRelease(hXmlFile);
750 RTVfsFileRelease(hManifestFile);
751 RTVfsFileRelease(hSignatureFile);
752 }
753 RTVfsFsStrmRelease(hTarFss);
754
755 return rcExit;
756}
757
758
759/**
760 * The 2nd part of the installation process.
761 *
762 * @returns The program exit code.
763 * @param pszBaseDir The base directory.
764 * @param pszCertDir The certificat directory.
765 * @param pszTarball The tarball name.
766 * @param hTarballFile The handle to open the @a pszTarball file.
767 * @param hTarballFileOpt The tarball file handle (optional).
768 * @param pszName The extension pack name.
769 */
770static RTEXITCODE DoInstall2(const char *pszBaseDir, const char *pszCertDir, const char *pszTarball,
771 RTFILE hTarballFile, RTFILE hTarballFileOpt, const char *pszName)
772{
773 /*
774 * Do some basic validation of the tarball file.
775 */
776 RTFSOBJINFO ObjInfo;
777 int rc = RTFileQueryInfo(hTarballFile, &ObjInfo, RTFSOBJATTRADD_UNIX);
778 if (RT_FAILURE(rc))
779 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTFileQueryInfo failed with %Rrc on '%s'", rc, pszTarball);
780 if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
781 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Not a regular file: %s", pszTarball);
782
783 if (hTarballFileOpt != NIL_RTFILE)
784 {
785 RTFSOBJINFO ObjInfo2;
786 rc = RTFileQueryInfo(hTarballFileOpt, &ObjInfo2, RTFSOBJATTRADD_UNIX);
787 if (RT_FAILURE(rc))
788 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTFileQueryInfo failed with %Rrc on --tarball-fd", rc);
789 if ( ObjInfo.Attr.u.Unix.INodeIdDevice != ObjInfo2.Attr.u.Unix.INodeIdDevice
790 || ObjInfo.Attr.u.Unix.INodeId != ObjInfo2.Attr.u.Unix.INodeId)
791 return RTMsgErrorExit(RTEXITCODE_FAILURE, "--tarball and --tarball-fd does not match");
792 }
793
794 /*
795 * Construct the paths to the two directories we'll be using.
796 */
797 char szFinalPath[RTPATH_MAX];
798 rc = RTPathJoin(szFinalPath, sizeof(szFinalPath), pszBaseDir, pszName);
799 if (RT_FAILURE(rc))
800 return RTMsgErrorExit(RTEXITCODE_FAILURE,
801 "Failed to construct the path to the final extension pack directory: %Rrc", rc);
802
803 char szTmpPath[RTPATH_MAX];
804 rc = RTPathJoin(szTmpPath, sizeof(szTmpPath) - 64, pszBaseDir, pszName);
805 if (RT_SUCCESS(rc))
806 {
807 size_t cchTmpPath = strlen(szTmpPath);
808 RTStrPrintf(&szTmpPath[cchTmpPath], sizeof(szTmpPath) - cchTmpPath, "-_-inst-%u", (uint32_t)RTProcSelf());
809 }
810 if (RT_FAILURE(rc))
811 return RTMsgErrorExit(RTEXITCODE_FAILURE,
812 "Failed to construct the path to the temporary extension pack directory: %Rrc", rc);
813
814 /*
815 * Check that they don't exist at this point in time.
816 */
817 rc = RTPathQueryInfoEx(szFinalPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
818 if (RT_SUCCESS(rc) && RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
819 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The extension pack is already installed. You must uninstall the old one first.");
820 if (RT_SUCCESS(rc))
821 return RTMsgErrorExit(RTEXITCODE_FAILURE,
822 "Found non-directory file system object where the extension pack would be installed ('%s')",
823 szFinalPath);
824 if (rc != VERR_FILE_NOT_FOUND && rc != VERR_PATH_NOT_FOUND)
825 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unexpected RTPathQueryInfoEx status code %Rrc for '%s'", rc, szFinalPath);
826
827 rc = RTPathQueryInfoEx(szTmpPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
828 if (rc != VERR_FILE_NOT_FOUND && rc != VERR_PATH_NOT_FOUND)
829 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unexpected RTPathQueryInfoEx status code %Rrc for '%s'", rc, szFinalPath);
830
831 /*
832 * Create the temporary directory and prepare the extension pack within it.
833 * If all checks out correctly, rename it to the final directory.
834 */
835 rc = RTDirCreate(szTmpPath, 0700);
836 if (RT_FAILURE(rc))
837 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create temporary directory: %Rrc ('%s')", rc, szTmpPath);
838
839 RTEXITCODE rcExit = ValidateExtPackTarball(hTarballFile, pszTarball);
840 if (rcExit == RTEXITCODE_SUCCESS)
841 rcExit = UnpackExtPack(hTarballFile, szTmpPath, pszTarball);
842 if (rcExit == RTEXITCODE_SUCCESS)
843 rcExit = ValidateUnpackedExtPack(szTmpPath, pszTarball, pszName);
844 if (rcExit == RTEXITCODE_SUCCESS)
845 rcExit = SetExtPackPermissions(szTmpPath);
846 if (rcExit == RTEXITCODE_SUCCESS)
847 {
848 rc = RTDirRename(szTmpPath, szFinalPath, RTPATHRENAME_FLAGS_NO_REPLACE);
849 if (RT_SUCCESS(rc))
850 RTMsgInfo("Successfully installed '%s' (%s)", pszName, pszTarball);
851 else
852 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
853 "Failed to rename the temporary directory to the final one: %Rrc ('%s' -> '%s')",
854 rc, szTmpPath, szFinalPath);
855 }
856
857 /*
858 * Clean up the temporary directory on failure.
859 */
860 if (rcExit != RTEXITCODE_SUCCESS)
861 RemoveExtPackDir(szTmpPath, true /*fTemporary*/);
862
863 return rcExit;
864}
865
866
867/**
868 * Implements the 'install' command.
869 *
870 * @returns The program exit code.
871 * @param argc The number of program arguments.
872 * @param argv The program arguments.
873 */
874static RTEXITCODE DoInstall(int argc, char **argv)
875{
876 /*
877 * Parse the parameters.
878 *
879 * Note! The --base-dir and --cert-dir are only for checking that the
880 * caller and this help applications have the same idea of where
881 * things are. Likewise, the --name is for verifying assumptions
882 * the caller made about the name. The optional --tarball-fd option
883 * is just for easing the paranoia on the user side.
884 */
885 static const RTGETOPTDEF s_aOptions[] =
886 {
887 { "--base-dir", 'b', RTGETOPT_REQ_STRING },
888 { "--cert-dir", 'c', RTGETOPT_REQ_STRING },
889 { "--name", 'n', RTGETOPT_REQ_STRING },
890 { "--tarball", 't', RTGETOPT_REQ_STRING },
891 { "--tarball-fd", 'f', RTGETOPT_REQ_UINT64 }
892 };
893 RTGETOPTSTATE GetState;
894 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /*fFlags*/);
895 if (RT_FAILURE(rc))
896 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
897
898 const char *pszBaseDir = NULL;
899 const char *pszCertDir = NULL;
900 const char *pszName = NULL;
901 const char *pszTarball = NULL;
902 RTFILE hTarballFileOpt = NIL_RTFILE;
903 RTGETOPTUNION ValueUnion;
904 int ch;
905 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
906 {
907 switch (ch)
908 {
909 case 'b':
910 if (pszBaseDir)
911 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --base-dir options");
912 pszBaseDir = ValueUnion.psz;
913 if (!IsValidBaseDir(pszBaseDir))
914 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid base directory: '%s'", pszBaseDir);
915 break;
916
917 case 'c':
918 if (pszCertDir)
919 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --cert-dir options");
920 pszCertDir = ValueUnion.psz;
921 if (!IsValidCertificateDir(pszCertDir))
922 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid certificate directory: '%s'", pszCertDir);
923 break;
924
925 case 'n':
926 if (pszName)
927 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --name options");
928 pszName = ValueUnion.psz;
929 if (!VBoxExtPackIsValidName(pszName))
930 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid extension pack name: '%s'", pszName);
931 break;
932
933 case 't':
934 if (pszTarball)
935 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --tarball options");
936 pszTarball = ValueUnion.psz;
937 break;
938
939 case 'd':
940 {
941 if (hTarballFileOpt != NIL_RTFILE)
942 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --tarball-fd options");
943 RTHCUINTPTR hNative = (RTHCUINTPTR)ValueUnion.u64;
944 if (hNative != ValueUnion.u64)
945 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The --tarball-fd value is out of range: %#RX64", ValueUnion.u64);
946 rc = RTFileFromNative(&hTarballFileOpt, hNative);
947 if (RT_FAILURE(rc))
948 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "RTFileFromNative failed on --target-fd value: %Rrc", rc);
949 break;
950 }
951
952 case 'h':
953 case 'V':
954 return DoStandardOption(ch);
955
956 default:
957 return RTGetOptPrintError(ch, &ValueUnion);
958 }
959 }
960 if (!pszName)
961 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --name option");
962 if (!pszBaseDir)
963 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --base-dir option");
964 if (!pszCertDir)
965 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --cert-dir option");
966 if (!pszTarball)
967 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --tarball option");
968
969 /*
970 * Ok, down to business.
971 */
972 RTFILE hTarballFile;
973 rc = RTFileOpen(&hTarballFile, pszTarball, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
974 if (RT_FAILURE(rc))
975 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open the extension pack tarball: %Rrc ('%s')", rc, pszTarball);
976
977 RTEXITCODE rcExit = DoInstall2(pszBaseDir, pszCertDir, pszTarball, hTarballFile, hTarballFileOpt, pszName);
978 RTFileClose(hTarballFile);
979 return rcExit;
980}
981
982
983/**
984 * Implements the 'uninstall' command.
985 *
986 * @returns The program exit code.
987 * @param argc The number of program arguments.
988 * @param argv The program arguments.
989 */
990static RTEXITCODE DoUninstall(int argc, char **argv)
991{
992 /*
993 * Parse the parameters.
994 *
995 * Note! The --base-dir is only for checking that the caller and this help
996 * applications have the same idea of where things are.
997 */
998 static const RTGETOPTDEF s_aOptions[] =
999 {
1000 { "--base-dir", 'b', RTGETOPT_REQ_STRING },
1001 { "--name", 'n', RTGETOPT_REQ_STRING }
1002 };
1003 RTGETOPTSTATE GetState;
1004 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /*fFlags*/);
1005 if (RT_FAILURE(rc))
1006 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
1007
1008 const char *pszBaseDir = NULL;
1009 const char *pszName = NULL;
1010 RTGETOPTUNION ValueUnion;
1011 int ch;
1012 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1013 {
1014 switch (ch)
1015 {
1016 case 'b':
1017 if (pszBaseDir)
1018 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --base-dir options");
1019 pszBaseDir = ValueUnion.psz;
1020 if (!IsValidBaseDir(pszBaseDir))
1021 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid base directory: '%s'", pszBaseDir);
1022 break;
1023
1024 case 'n':
1025 if (pszName)
1026 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --name options");
1027 pszName = ValueUnion.psz;
1028 if (!VBoxExtPackIsValidName(pszName))
1029 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid extension pack name: '%s'", pszName);
1030 break;
1031
1032 case 'h':
1033 case 'V':
1034 return DoStandardOption(ch);
1035
1036 default:
1037 return RTGetOptPrintError(ch, &ValueUnion);
1038 }
1039 }
1040 if (!pszName)
1041 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --name option");
1042 if (!pszBaseDir)
1043 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --base-dir option");
1044
1045 /*
1046 * Ok, down to business.
1047 */
1048 /* Check that it exists. */
1049 char szExtPackDir[RTPATH_MAX];
1050 rc = RTPathJoin(szExtPackDir, sizeof(szExtPackDir), pszBaseDir, pszName);
1051 if (RT_FAILURE(rc))
1052 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to construct extension pack path: %Rrc", rc);
1053
1054 if (!RTDirExists(szExtPackDir))
1055 {
1056 RTMsgInfo("Extension pack not installed. Nothing to do.");
1057 return RTEXITCODE_SUCCESS;
1058 }
1059
1060 /* Rename the extension pack directory before deleting it to prevent new
1061 VM processes from picking it up. */
1062 char szExtPackUnInstDir[RTPATH_MAX];
1063 rc = RTPathJoin(szExtPackUnInstDir, sizeof(szExtPackUnInstDir), pszBaseDir, pszName);
1064 if (RT_SUCCESS(rc))
1065 rc = RTStrCat(szExtPackUnInstDir, sizeof(szExtPackUnInstDir), "-_-uninst");
1066 if (RT_FAILURE(rc))
1067 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to construct temporary extension pack path: %Rrc", rc);
1068
1069 rc = RTDirRename(szExtPackDir, szExtPackUnInstDir, RTPATHRENAME_FLAGS_NO_REPLACE);
1070 if (RT_FAILURE(rc))
1071 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to rename the extension pack directory: %Rrc", rc);
1072
1073 /* Recursively delete the directory content. */
1074 RTEXITCODE rcExit = RemoveExtPackDir(szExtPackUnInstDir, false /*fTemporary*/);
1075 if (rcExit == RTEXITCODE_SUCCESS)
1076 RTMsgInfo("Successfully removed extension pack '%s'\n", pszName);
1077
1078 return rcExit;
1079}
1080
1081/**
1082 * Implements the 'cleanup' command.
1083 *
1084 * @returns The program exit code.
1085 * @param argc The number of program arguments.
1086 * @param argv The program arguments.
1087 */
1088static RTEXITCODE DoCleanup(int argc, char **argv)
1089{
1090 /*
1091 * Parse the parameters.
1092 *
1093 * Note! The --base-dir is only for checking that the caller and this help
1094 * applications have the same idea of where things are.
1095 */
1096 static const RTGETOPTDEF s_aOptions[] =
1097 {
1098 { "--base-dir", 'b', RTGETOPT_REQ_STRING },
1099 };
1100 RTGETOPTSTATE GetState;
1101 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /*fFlags*/);
1102 if (RT_FAILURE(rc))
1103 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
1104
1105 const char *pszBaseDir = NULL;
1106 RTGETOPTUNION ValueUnion;
1107 int ch;
1108 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1109 {
1110 switch (ch)
1111 {
1112 case 'b':
1113 if (pszBaseDir)
1114 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --base-dir options");
1115 pszBaseDir = ValueUnion.psz;
1116 if (!IsValidBaseDir(pszBaseDir))
1117 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid base directory: '%s'", pszBaseDir);
1118 break;
1119
1120 case 'h':
1121 case 'V':
1122 return DoStandardOption(ch);
1123
1124 default:
1125 return RTGetOptPrintError(ch, &ValueUnion);
1126 }
1127 }
1128 if (!pszBaseDir)
1129 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --base-dir option");
1130
1131 /*
1132 * Ok, down to business.
1133 */
1134 PRTDIR pDir;
1135 rc = RTDirOpen(&pDir, pszBaseDir);
1136 if (RT_FAILURE(rc))
1137 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed open the base directory: %Rrc ('%s')", rc, pszBaseDir);
1138
1139 uint32_t cCleaned = 0;
1140 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1141 for (;;)
1142 {
1143 RTDIRENTRYEX Entry;
1144 rc = RTDirReadEx(pDir, &Entry, NULL /*pcbDirEntry*/, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1145 if (RT_FAILURE(rc))
1146 {
1147 if (rc != VERR_NO_MORE_FILES)
1148 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDirReadEx returns %Rrc", rc);
1149 break;
1150 }
1151 if ( RTFS_IS_DIRECTORY(Entry.Info.Attr.fMode)
1152 && strcmp(Entry.szName, ".") != 0
1153 && strcmp(Entry.szName, "..") != 0
1154 && !VBoxExtPackIsValidName(Entry.szName) )
1155 {
1156 char szPath[RTPATH_MAX];
1157 rc = RTPathJoin(szPath, sizeof(szPath), pszBaseDir, Entry.szName);
1158 if (RT_SUCCESS(rc))
1159 {
1160 RTEXITCODE rcExit2 = RemoveExtPackDir(szPath, true /*fTemporary*/);
1161 if (rcExit2 == RTEXITCODE_SUCCESS)
1162 RTMsgInfo("Successfully removed '%s'.", Entry.szName);
1163 else if (rcExit == RTEXITCODE_SUCCESS)
1164 rcExit = rcExit2;
1165 }
1166 else
1167 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathJoin failed with %Rrc for '%s'", rc, Entry.szName);
1168 cCleaned++;
1169 }
1170 }
1171 RTDirClose(pDir);
1172 if (!cCleaned)
1173 RTMsgInfo("Nothing to clean.");
1174 return rcExit;
1175}
1176
1177
1178
1179int main(int argc, char **argv)
1180{
1181 /*
1182 * Initialize IPRT and check that we're correctly installed.
1183 */
1184 int rc = RTR3Init();
1185 if (RT_FAILURE(rc))
1186 return RTMsgInitFailure(rc);
1187
1188 char szErr[2048];
1189 rc = SUPR3HardenedVerifySelf(argv[0], true /*fInternal*/, szErr, sizeof(szErr));
1190 if (RT_FAILURE(rc))
1191 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s", szErr);
1192
1193 /*
1194 * Parse the top level arguments until we find a command.
1195 */
1196 static const RTGETOPTDEF s_aOptions[] =
1197 {
1198#define CMD_INSTALL 1000
1199 { "install", CMD_INSTALL, RTGETOPT_REQ_NOTHING },
1200#define CMD_UNINSTALL 1001
1201 { "uninstall", CMD_UNINSTALL, RTGETOPT_REQ_NOTHING },
1202#define CMD_CLEANUP 1002
1203 { "cleanup", CMD_CLEANUP, RTGETOPT_REQ_NOTHING },
1204 };
1205 RTGETOPTSTATE GetState;
1206 rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
1207 if (RT_FAILURE(rc))
1208 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
1209 for (;;)
1210 {
1211 RTGETOPTUNION ValueUnion;
1212 int ch = RTGetOpt(&GetState, &ValueUnion);
1213 switch (ch)
1214 {
1215 case 0:
1216 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No command specified");
1217
1218 case CMD_INSTALL:
1219 return DoInstall( argc - GetState.iNext, argv + GetState.iNext);
1220
1221 case CMD_UNINSTALL:
1222 return DoUninstall(argc - GetState.iNext, argv + GetState.iNext);
1223
1224 case CMD_CLEANUP:
1225 return DoCleanup( argc - GetState.iNext, argv + GetState.iNext);
1226
1227 case 'h':
1228 case 'V':
1229 return DoStandardOption(ch);
1230
1231 default:
1232 return RTGetOptPrintError(ch, &ValueUnion);
1233 }
1234 /* not currently reached */
1235 }
1236 /* not reached */
1237}
1238
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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