VirtualBox

source: vbox/trunk/src/VBox/Main/src-all/ExtPackUtil.cpp@ 39180

Last change on this file since 39180 was 39180, checked in by vboxsync, 13 years ago

ExtPack: Adding a 'edition' attribute to the Version tag and cleaning up the version string validation confusion. Currently hacking the edition into the version property of IExtPack, adding a property later.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 43.7 KB
Line 
1/* $Id: ExtPackUtil.cpp 39180 2011-11-02 20:59:30Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Extension Pack Utilities and definitions, VBoxC, VBoxSVC, ++.
4 */
5
6/*
7 * Copyright (C) 2010-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 "../include/ExtPackUtil.h"
23
24#include <iprt/ctype.h>
25#include <iprt/dir.h>
26#include <iprt/file.h>
27#include <iprt/manifest.h>
28#include <iprt/param.h>
29#include <iprt/path.h>
30#include <iprt/string.h>
31#include <iprt/vfs.h>
32#include <iprt/tar.h>
33#include <iprt/zip.h>
34#include <iprt/cpp/xml.h>
35
36#include <VBox/log.h>
37
38
39/**
40 * Worker for VBoxExtPackLoadDesc that loads the plug-in descriptors.
41 *
42 * @returns Same as VBoxExtPackLoadDesc.
43 * @param pVBoxExtPackElm
44 * @param pcPlugIns Where to return the number of plug-ins in the
45 * array.
46 * @param paPlugIns Where to return the plug-in descriptor array.
47 * (RTMemFree it even on failure)
48 */
49static RTCString *
50vboxExtPackLoadPlugInDescs(const xml::ElementNode *pVBoxExtPackElm,
51 uint32_t *pcPlugIns, PVBOXEXTPACKPLUGINDESC *paPlugIns)
52{
53 *pcPlugIns = 0;
54 *paPlugIns = NULL;
55
56 /** @todo plug-ins */
57 NOREF(pVBoxExtPackElm);
58
59 return NULL;
60}
61
62/**
63 * Clears the extension pack descriptor.
64 *
65 * @param a_pExtPackDesc The descriptor to clear.
66 */
67static void vboxExtPackClearDesc(PVBOXEXTPACKDESC a_pExtPackDesc)
68{
69 a_pExtPackDesc->strName.setNull();
70 a_pExtPackDesc->strDescription.setNull();
71 a_pExtPackDesc->strVersion.setNull();
72 a_pExtPackDesc->strEdition.setNull();
73 a_pExtPackDesc->uRevision = 0;
74 a_pExtPackDesc->strMainModule.setNull();
75 a_pExtPackDesc->strVrdeModule.setNull();
76 a_pExtPackDesc->cPlugIns = 0;
77 a_pExtPackDesc->paPlugIns = NULL;
78 a_pExtPackDesc->fShowLicense = false;
79}
80
81/**
82 * Initializes an extension pack descriptor so that it's safe to call free on
83 * it whatever happens later on.
84 *
85 * @param a_pExtPackDesc The descirptor to initialize.
86 */
87void VBoxExtPackInitDesc(PVBOXEXTPACKDESC a_pExtPackDesc)
88{
89 vboxExtPackClearDesc(a_pExtPackDesc);
90}
91
92
93/**
94 * Load the extension pack descriptor from an XML document.
95 *
96 * @returns NULL on success, pointer to an error message on failure (caller
97 * deletes it).
98 * @param a_pDoc Pointer to the the XML document.
99 * @param a_pExtPackDesc Where to store the extension pack descriptor.
100 */
101static RTCString *vboxExtPackLoadDescFromDoc(xml::Document *a_pDoc, PVBOXEXTPACKDESC a_pExtPackDesc)
102{
103 /*
104 * Get the main element and check its version.
105 */
106 const xml::ElementNode *pVBoxExtPackElm = a_pDoc->getRootElement();
107 if ( !pVBoxExtPackElm
108 || strcmp(pVBoxExtPackElm->getName(), "VirtualBoxExtensionPack") != 0)
109 return new RTCString("No VirtualBoxExtensionPack element");
110
111 RTCString strFormatVersion;
112 if (!pVBoxExtPackElm->getAttributeValue("version", strFormatVersion))
113 return new RTCString("Missing format version");
114 if (!strFormatVersion.equals("1.0"))
115 return &(new RTCString("Unsupported format version: "))->append(strFormatVersion);
116
117 /*
118 * Read and validate mandatory bits.
119 */
120 const xml::ElementNode *pNameElm = pVBoxExtPackElm->findChildElement("Name");
121 if (!pNameElm)
122 return new RTCString("The 'Name' element is missing");
123 const char *pszName = pNameElm->getValue();
124 if (!VBoxExtPackIsValidName(pszName))
125 return &(new RTCString("Invalid name: "))->append(pszName);
126
127 const xml::ElementNode *pDescElm = pVBoxExtPackElm->findChildElement("Description");
128 if (!pDescElm)
129 return new RTCString("The 'Description' element is missing");
130 const char *pszDesc = pDescElm->getValue();
131 if (!pszDesc || *pszDesc == '\0')
132 return new RTCString("The 'Description' element is empty");
133 if (strpbrk(pszDesc, "\n\r\t\v\b") != NULL)
134 return new RTCString("The 'Description' must not contain control characters");
135
136 const xml::ElementNode *pVersionElm = pVBoxExtPackElm->findChildElement("Version");
137 if (!pVersionElm)
138 return new RTCString("The 'Version' element is missing");
139 const char *pszVersion = pVersionElm->getValue();
140 if (!pszVersion || *pszVersion == '\0')
141 return new RTCString("The 'Version' element is empty");
142 if (!VBoxExtPackIsValidVersionString(pszVersion))
143 return &(new RTCString("Invalid version string: "))->append(pszVersion);
144
145 uint32_t uRevision;
146 if (!pVersionElm->getAttributeValue("revision", uRevision))
147 uRevision = 0;
148
149 const char *pszEdition;
150 if (!pVersionElm->getAttributeValue("edition", pszEdition))
151 pszEdition = "";
152 if (!VBoxExtPackIsValidEditionString(pszEdition))
153 return &(new RTCString("Invalid edition string: "))->append(pszEdition);
154
155 const xml::ElementNode *pMainModuleElm = pVBoxExtPackElm->findChildElement("MainModule");
156 if (!pMainModuleElm)
157 return new RTCString("The 'MainModule' element is missing");
158 const char *pszMainModule = pMainModuleElm->getValue();
159 if (!pszMainModule || *pszMainModule == '\0')
160 return new RTCString("The 'MainModule' element is empty");
161 if (!VBoxExtPackIsValidModuleString(pszMainModule))
162 return &(new RTCString("Invalid main module string: "))->append(pszMainModule);
163
164 /*
165 * The VRDE module, optional.
166 * Accept both none and empty as tokens of no VRDE module.
167 */
168 const char *pszVrdeModule = NULL;
169 const xml::ElementNode *pVrdeModuleElm = pVBoxExtPackElm->findChildElement("VRDEModule");
170 if (pVrdeModuleElm)
171 {
172 pszVrdeModule = pVrdeModuleElm->getValue();
173 if (!pszVrdeModule || *pszVrdeModule == '\0')
174 pszVrdeModule = NULL;
175 else if (!VBoxExtPackIsValidModuleString(pszVrdeModule))
176 return &(new RTCString("Invalid VRDE module string: "))->append(pszVrdeModule);
177 }
178
179 /*
180 * Whether to show the license, optional. (presense is enough here)
181 */
182 const xml::ElementNode *pShowLicenseElm = pVBoxExtPackElm->findChildElement("ShowLicense");
183 bool fShowLicense = pShowLicenseElm != NULL;
184
185 /*
186 * Parse plug-in descriptions (last because of the manual memory management).
187 */
188 uint32_t cPlugIns = 0;
189 PVBOXEXTPACKPLUGINDESC paPlugIns = NULL;
190 RTCString *pstrRet = vboxExtPackLoadPlugInDescs(pVBoxExtPackElm, &cPlugIns, &paPlugIns);
191 if (pstrRet)
192 {
193 RTMemFree(paPlugIns);
194 return pstrRet;
195 }
196
197 /*
198 * Everything seems fine, fill in the return values and return successfully.
199 */
200 a_pExtPackDesc->strName = pszName;
201 a_pExtPackDesc->strDescription = pszDesc;
202 a_pExtPackDesc->strVersion = pszVersion;
203 a_pExtPackDesc->strEdition = pszEdition;
204 a_pExtPackDesc->uRevision = uRevision;
205 a_pExtPackDesc->strMainModule = pszMainModule;
206 a_pExtPackDesc->strVrdeModule = pszVrdeModule;
207 a_pExtPackDesc->cPlugIns = cPlugIns;
208 a_pExtPackDesc->paPlugIns = paPlugIns;
209 a_pExtPackDesc->fShowLicense = fShowLicense;
210
211 return NULL;
212}
213
214/**
215 * Reads the extension pack descriptor.
216 *
217 * @returns NULL on success, pointer to an error message on failure (caller
218 * deletes it).
219 * @param a_pszDir The directory containing the description file.
220 * @param a_pExtPackDesc Where to store the extension pack descriptor.
221 * @param a_pObjInfo Where to store the object info for the file (unix
222 * attribs). Optional.
223 */
224RTCString *VBoxExtPackLoadDesc(const char *a_pszDir, PVBOXEXTPACKDESC a_pExtPackDesc, PRTFSOBJINFO a_pObjInfo)
225{
226 vboxExtPackClearDesc(a_pExtPackDesc);
227
228 /*
229 * Validate, open and parse the XML file.
230 */
231 char szFilePath[RTPATH_MAX];
232 int vrc = RTPathJoin(szFilePath, sizeof(szFilePath), a_pszDir, VBOX_EXTPACK_DESCRIPTION_NAME);
233 if (RT_FAILURE(vrc))
234 return new RTCString("RTPathJoin failed with %Rrc", vrc);
235
236 RTFSOBJINFO ObjInfo;
237 vrc = RTPathQueryInfoEx(szFilePath, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
238 if (RT_FAILURE(vrc))
239 return &(new RTCString())->printf("RTPathQueryInfoEx failed with %Rrc", vrc);
240 if (a_pObjInfo)
241 *a_pObjInfo = ObjInfo;
242 if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
243 {
244 if (RTFS_IS_SYMLINK(ObjInfo.Attr.fMode))
245 return new RTCString("The XML file is symlinked, that is not allowed");
246 return &(new RTCString)->printf("The XML file is not a file (fMode=%#x)", ObjInfo.Attr.fMode);
247 }
248
249 xml::Document Doc;
250 {
251 xml::XmlFileParser Parser;
252 try
253 {
254 Parser.read(szFilePath, Doc);
255 }
256 catch (xml::XmlError Err)
257 {
258 return new RTCString(Err.what());
259 }
260 }
261
262 /*
263 * Hand the xml doc over to the common code.
264 */
265 return vboxExtPackLoadDescFromDoc(&Doc, a_pExtPackDesc);
266}
267
268/**
269 * Reads the extension pack descriptor.
270 *
271 * @returns NULL on success, pointer to an error message on failure (caller
272 * deletes it).
273 * @param a_pszDir The directory containing the description file.
274 * @param a_pExtPackDesc Where to store the extension pack descriptor.
275 * @param a_pObjInfo Where to store the object info for the file (unix
276 * attribs). Optional.
277 */
278RTCString *VBoxExtPackLoadDescFromVfsFile(RTVFSFILE hVfsFile, PVBOXEXTPACKDESC a_pExtPackDesc, PRTFSOBJINFO a_pObjInfo)
279{
280 vboxExtPackClearDesc(a_pExtPackDesc);
281
282 /*
283 * Query the object info.
284 */
285 RTFSOBJINFO ObjInfo;
286 int rc = RTVfsFileQueryInfo(hVfsFile, &ObjInfo, RTFSOBJATTRADD_UNIX);
287 if (RT_FAILURE(rc))
288 return &(new RTCString)->printf("RTVfsFileQueryInfo failed: %Rrc", rc);
289 if (a_pObjInfo)
290 *a_pObjInfo = ObjInfo;
291
292 /*
293 * The simple approach, read the whole thing into memory and pass this to
294 * the XML parser.
295 */
296
297 /* Check the file size. */
298 if (ObjInfo.cbObject > _1M || ObjInfo.cbObject < 0)
299 return &(new RTCString)->printf("The XML file is too large (%'RU64 bytes)", ObjInfo.cbObject);
300 size_t const cbFile = (size_t)ObjInfo.cbObject;
301
302 /* Rewind to the start of the file. */
303 rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
304 if (RT_FAILURE(rc))
305 return &(new RTCString)->printf("RTVfsFileSeek(,0,BEGIN) failed: %Rrc", rc);
306
307 /* Allocate memory and read the file content into it. */
308 void *pvFile = RTMemTmpAlloc(cbFile);
309 if (!pvFile)
310 return &(new RTCString)->printf("RTMemTmpAlloc(%zu) failed", cbFile);
311
312 RTCString *pstrErr = NULL;
313 rc = RTVfsFileRead(hVfsFile, pvFile, cbFile, NULL);
314 if (RT_FAILURE(rc))
315 pstrErr = &(new RTCString)->printf("RTVfsFileRead failed: %Rrc", rc);
316
317 /*
318 * Parse the file.
319 */
320 xml::Document Doc;
321 if (RT_SUCCESS(rc))
322 {
323 xml::XmlMemParser Parser;
324 RTCString strFileName = VBOX_EXTPACK_DESCRIPTION_NAME;
325 try
326 {
327 Parser.read(pvFile, cbFile, strFileName, Doc);
328 }
329 catch (xml::XmlError Err)
330 {
331 pstrErr = new RTCString(Err.what());
332 rc = VERR_PARSE_ERROR;
333 }
334 }
335 RTMemTmpFree(pvFile);
336
337 /*
338 * Hand the xml doc over to the common code.
339 */
340 if (RT_SUCCESS(rc))
341 pstrErr = vboxExtPackLoadDescFromDoc(&Doc, a_pExtPackDesc);
342
343 return pstrErr;
344}
345
346/**
347 * Frees all resources associated with a extension pack descriptor.
348 *
349 * @param a_pExtPackDesc The extension pack descriptor which members
350 * should be freed.
351 */
352void VBoxExtPackFreeDesc(PVBOXEXTPACKDESC a_pExtPackDesc)
353{
354 if (!a_pExtPackDesc)
355 return;
356
357 a_pExtPackDesc->strName.setNull();
358 a_pExtPackDesc->strDescription.setNull();
359 a_pExtPackDesc->strVersion.setNull();
360 a_pExtPackDesc->strEdition.setNull();
361 a_pExtPackDesc->uRevision = 0;
362 a_pExtPackDesc->strMainModule.setNull();
363 a_pExtPackDesc->strVrdeModule.setNull();
364 a_pExtPackDesc->cPlugIns = 0;
365 RTMemFree(a_pExtPackDesc->paPlugIns);
366 a_pExtPackDesc->paPlugIns = NULL;
367 a_pExtPackDesc->fShowLicense = false;
368}
369
370/**
371 * Extract the extension pack name from the tarball path.
372 *
373 * @returns String containing the name on success, the caller must delete it.
374 * NULL if no valid name was found or if we ran out of memory.
375 * @param pszTarball The path to the tarball.
376 */
377RTCString *VBoxExtPackExtractNameFromTarballPath(const char *pszTarball)
378{
379 /*
380 * Skip ahead to the filename part and count the number of characters
381 * that matches the criteria for a mangled extension pack name.
382 */
383 const char *pszSrc = RTPathFilename(pszTarball);
384 if (!pszSrc)
385 return NULL;
386
387 size_t off = 0;
388 while (RT_C_IS_ALNUM(pszSrc[off]) || pszSrc[off] == '_')
389 off++;
390
391 /*
392 * Check min and max name limits.
393 */
394 if ( off > VBOX_EXTPACK_NAME_MAX_LEN
395 || off < VBOX_EXTPACK_NAME_MIN_LEN)
396 return NULL;
397
398 /*
399 * Return the unmangled name.
400 */
401 return VBoxExtPackUnmangleName(pszSrc, off);
402}
403
404/**
405 * Validates the extension pack name.
406 *
407 * @returns true if valid, false if not.
408 * @param pszName The name to validate.
409 * @sa VBoxExtPackExtractNameFromTarballPath
410 */
411bool VBoxExtPackIsValidName(const char *pszName)
412{
413 if (!pszName)
414 return false;
415
416 /*
417 * Check the characters making up the name, only english alphabet
418 * characters, decimal digits and spaces are allowed.
419 */
420 size_t off = 0;
421 while (pszName[off])
422 {
423 if (!RT_C_IS_ALNUM(pszName[off]) && pszName[off] != ' ')
424 return false;
425 off++;
426 }
427
428 /*
429 * Check min and max name limits.
430 */
431 if ( off > VBOX_EXTPACK_NAME_MAX_LEN
432 || off < VBOX_EXTPACK_NAME_MIN_LEN)
433 return false;
434
435 return true;
436}
437
438/**
439 * Checks if an alledged manged extension pack name.
440 *
441 * @returns true if valid, false if not.
442 * @param pszMangledName The mangled name to validate.
443 * @param cchMax The max number of chars to test.
444 * @sa VBoxExtPackMangleName
445 */
446bool VBoxExtPackIsValidMangledName(const char *pszMangledName, size_t cchMax /*= RTSTR_MAX*/)
447{
448 if (!pszMangledName)
449 return false;
450
451 /*
452 * Check the characters making up the name, only english alphabet
453 * characters, decimal digits and underscores (=space) are allowed.
454 */
455 size_t off = 0;
456 while (off < cchMax && pszMangledName[off])
457 {
458 if (!RT_C_IS_ALNUM(pszMangledName[off]) && pszMangledName[off] != '_')
459 return false;
460 off++;
461 }
462
463 /*
464 * Check min and max name limits.
465 */
466 if ( off > VBOX_EXTPACK_NAME_MAX_LEN
467 || off < VBOX_EXTPACK_NAME_MIN_LEN)
468 return false;
469
470 return true;
471}
472
473/**
474 * Mangle an extension pack name so it can be used by a directory or file name.
475 *
476 * @returns String containing the mangled name on success, the caller must
477 * delete it. NULL on failure.
478 * @param pszName The unmangled name.
479 * @sa VBoxExtPackUnmangleName, VBoxExtPackIsValidMangledName
480 */
481RTCString *VBoxExtPackMangleName(const char *pszName)
482{
483 AssertReturn(VBoxExtPackIsValidName(pszName), NULL);
484
485 char szTmp[VBOX_EXTPACK_NAME_MAX_LEN + 1];
486 size_t off = 0;
487 char ch;
488 while ((ch = pszName[off]) != '\0')
489 {
490 if (ch == ' ')
491 ch = '_';
492 szTmp[off++] = ch;
493 }
494 szTmp[off] = '\0';
495 Assert(VBoxExtPackIsValidMangledName(szTmp));
496
497 return new RTCString(szTmp, off);
498}
499
500/**
501 * Unmangle an extension pack name (reverses VBoxExtPackMangleName).
502 *
503 * @returns String containing the mangled name on success, the caller must
504 * delete it. NULL on failure.
505 * @param pszMangledName The mangled name.
506 * @param cchMax The max name length. RTSTR_MAX is fine.
507 * @sa VBoxExtPackMangleName, VBoxExtPackIsValidMangledName
508 */
509RTCString *VBoxExtPackUnmangleName(const char *pszMangledName, size_t cchMax)
510{
511 AssertReturn(VBoxExtPackIsValidMangledName(pszMangledName, cchMax), NULL);
512
513 char szTmp[VBOX_EXTPACK_NAME_MAX_LEN + 1];
514 size_t off = 0;
515 char ch;
516 while ( off < cchMax
517 && (ch = pszMangledName[off]) != '\0')
518 {
519 if (ch == '_')
520 ch = ' ';
521 else
522 AssertReturn(RT_C_IS_ALNUM(ch) || ch == ' ', NULL);
523 szTmp[off++] = ch;
524 }
525 szTmp[off] = '\0';
526 AssertReturn(VBoxExtPackIsValidName(szTmp), NULL);
527
528 return new RTCString(szTmp, off);
529}
530
531/**
532 * Constructs the extension pack directory path.
533 *
534 * A combination of RTPathJoin and VBoxExtPackMangleName.
535 *
536 * @returns IPRT status code like RTPathJoin.
537 * @param pszExtPackDir Where to return the directory path.
538 * @param cbExtPackDir The size of the return buffer.
539 * @param pszParentDir The parent directory (".../Extensions").
540 * @param pszName The extension pack name, unmangled.
541 */
542int VBoxExtPackCalcDir(char *pszExtPackDir, size_t cbExtPackDir, const char *pszParentDir, const char *pszName)
543{
544 AssertReturn(VBoxExtPackIsValidName(pszName), VERR_INTERNAL_ERROR_5);
545
546 RTCString *pstrMangledName = VBoxExtPackMangleName(pszName);
547 if (!pstrMangledName)
548 return VERR_INTERNAL_ERROR_4;
549
550 int vrc = RTPathJoin(pszExtPackDir, cbExtPackDir, pszParentDir, pstrMangledName->c_str());
551 delete pstrMangledName;
552
553 return vrc;
554}
555
556
557/**
558 * Validates the extension pack version string.
559 *
560 * @returns true if valid, false if not.
561 * @param pszVersion The version string to validate.
562 */
563bool VBoxExtPackIsValidVersionString(const char *pszVersion)
564{
565 if (!pszVersion || *pszVersion == '\0')
566 return false;
567
568 /* 1.x.y.z... */
569 for (;;)
570 {
571 if (!RT_C_IS_DIGIT(*pszVersion))
572 return false;
573 do
574 pszVersion++;
575 while (RT_C_IS_DIGIT(*pszVersion));
576 if (*pszVersion != '.')
577 break;
578 pszVersion++;
579 }
580
581 /* upper case string + numbers indicating the build type */
582 if (*pszVersion == '-' || *pszVersion == '_')
583 {
584 /** @todo Should probably restrict this to known build types (alpha,
585 * beta, rc, ++). */
586 do
587 pszVersion++;
588 while ( RT_C_IS_DIGIT(*pszVersion)
589 || RT_C_IS_UPPER(*pszVersion)
590 || *pszVersion == '-'
591 || *pszVersion == '_');
592 }
593
594 return *pszVersion == '\0';
595}
596
597/**
598 * Validates the extension pack edition string.
599 *
600 * @returns true if valid, false if not.
601 * @param pszEdition The edition string to validate.
602 */
603bool VBoxExtPackIsValidEditionString(const char *pszEdition)
604{
605 if (*pszEdition)
606 {
607 if (!RT_C_IS_UPPER(*pszEdition))
608 return false;
609
610 do
611 pszEdition++;
612 while ( RT_C_IS_UPPER(*pszEdition)
613 || RT_C_IS_DIGIT(*pszEdition)
614 || *pszEdition == '-'
615 || *pszEdition == '_');
616 }
617 return *pszEdition == '\0';
618}
619
620/**
621 * Validates an extension pack module string.
622 *
623 * @returns true if valid, false if not.
624 * @param pszModule The module string to validate.
625 */
626bool VBoxExtPackIsValidModuleString(const char *pszModule)
627{
628 if (!pszModule || *pszModule == '\0')
629 return false;
630
631 /* Restricted charset, no extensions (dots). */
632 while ( RT_C_IS_ALNUM(*pszModule)
633 || *pszModule == '-'
634 || *pszModule == '_')
635 pszModule++;
636
637 return *pszModule == '\0';
638}
639
640/**
641 * RTStrPrintfv wrapper.
642 *
643 * @returns @a rc
644 * @param rc The status code to return.
645 * @param pszError The error buffer.
646 * @param cbError The size of the buffer.
647 * @param pszFormat The error message format string.
648 * @param ... Format arguments.
649 */
650static int vboxExtPackReturnError(int rc, char *pszError, size_t cbError, const char *pszFormat, ...)
651{
652 va_list va;
653 va_start(va, pszFormat);
654 RTStrPrintfV(pszError, cbError, pszFormat, va);
655 va_end(va);
656 return rc;
657}
658
659/**
660 * RTStrPrintfv wrapper.
661 *
662 * @param pszError The error buffer.
663 * @param cbError The size of the buffer.
664 * @param pszFormat The error message format string.
665 * @param ... Format arguments.
666 */
667static void vboxExtPackSetError(char *pszError, size_t cbError, const char *pszFormat, ...)
668{
669 va_list va;
670 va_start(va, pszFormat);
671 RTStrPrintfV(pszError, cbError, pszFormat, va);
672 va_end(va);
673}
674
675/**
676 * Verifies the manifest and its signature.
677 *
678 * @returns VBox status code, failures with message.
679 * @param hManifestFile The xml from the extension pack.
680 * @param pszExtPackName The expected extension pack name. This can be
681 * NULL, in which we don't have any expectations.
682 * @param pszError Where to store an error message on failure.
683 * @param cbError The size of the buffer @a pszError points to.
684 */
685static int vboxExtPackVerifyXml(RTVFSFILE hXmlFile, const char *pszExtPackName, char *pszError, size_t cbError)
686{
687 /*
688 * Load the XML.
689 */
690 VBOXEXTPACKDESC ExtPackDesc;
691 RTCString *pstrErr = VBoxExtPackLoadDescFromVfsFile(hXmlFile, &ExtPackDesc, NULL);
692 if (pstrErr)
693 {
694 RTStrCopy(pszError, cbError, pstrErr->c_str());
695 delete pstrErr;
696 return VERR_PARSE_ERROR;
697 }
698
699 /*
700 * Check the name.
701 */
702 /** @todo drop this restriction after the old install interface is
703 * dropped. */
704 int rc = VINF_SUCCESS;
705 if ( pszExtPackName
706 && !ExtPackDesc.strName.equalsIgnoreCase(pszExtPackName))
707 rc = vboxExtPackReturnError(VERR_NOT_EQUAL, pszError, cbError,
708 "The name of the downloaded file and the name stored inside the extension pack does not match"
709 " (xml='%s' file='%s')", ExtPackDesc.strName.c_str(), pszExtPackName);
710 return rc;
711}
712
713/**
714 * Verifies the manifest and its signature.
715 *
716 * @returns VBox status code, failures with message.
717 * @param hOurManifest The manifest we compiled.
718 * @param hManifestFile The manifest file in the extension pack.
719 * @param hSignatureFile The manifest signature file.
720 * @param pszError Where to store an error message on failure.
721 * @param cbError The size of the buffer @a pszError points to.
722 */
723static int vboxExtPackVerifyManifestAndSignature(RTMANIFEST hOurManifest, RTVFSFILE hManifestFile, RTVFSFILE hSignatureFile,
724 char *pszError, size_t cbError)
725{
726 /*
727 * Read the manifest from the extension pack.
728 */
729 int rc = RTVfsFileSeek(hManifestFile, 0, RTFILE_SEEK_BEGIN, NULL);
730 if (RT_FAILURE(rc))
731 return vboxExtPackReturnError(rc, pszError, cbError, "RTVfsFileSeek failed: %Rrc", rc);
732
733 RTMANIFEST hTheirManifest;
734 rc = RTManifestCreate(0 /*fFlags*/, &hTheirManifest);
735 if (RT_FAILURE(rc))
736 return vboxExtPackReturnError(rc, pszError, cbError, "RTManifestCreate failed: %Rrc", rc);
737
738 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hManifestFile);
739 rc = RTManifestReadStandard(hTheirManifest, hVfsIos);
740 RTVfsIoStrmRelease(hVfsIos);
741 if (RT_SUCCESS(rc))
742 {
743 /*
744 * Compare the manifests.
745 */
746 static const char *s_apszIgnoreEntries[] =
747 {
748 VBOX_EXTPACK_MANIFEST_NAME,
749 VBOX_EXTPACK_SIGNATURE_NAME,
750 "./" VBOX_EXTPACK_MANIFEST_NAME,
751 "./" VBOX_EXTPACK_SIGNATURE_NAME,
752 NULL
753 };
754 char szError[RTPATH_MAX];
755 rc = RTManifestEqualsEx(hOurManifest, hTheirManifest, &s_apszIgnoreEntries[0], NULL,
756 RTMANIFEST_EQUALS_IGN_MISSING_ATTRS /*fFlags*/,
757 szError, sizeof(szError));
758 if (RT_SUCCESS(rc))
759 {
760 /*
761 * Validate the manifest file signature.
762 */
763 /** @todo implement signature stuff */
764 NOREF(hSignatureFile);
765
766 }
767 else if (rc == VERR_NOT_EQUAL && szError[0])
768 vboxExtPackSetError(pszError, cbError, "Manifest mismatch: %s", szError);
769 else
770 vboxExtPackSetError(pszError, cbError, "RTManifestEqualsEx failed: %Rrc", rc);
771#if 0
772 RTVFSIOSTREAM hVfsIosStdOut = NIL_RTVFSIOSTREAM;
773 RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE, true, &hVfsIosStdOut);
774 RTVfsIoStrmWrite(hVfsIosStdOut, "Our:\n", sizeof("Our:\n") - 1, true, NULL);
775 RTManifestWriteStandard(hOurManifest, hVfsIosStdOut);
776 RTVfsIoStrmWrite(hVfsIosStdOut, "Their:\n", sizeof("Their:\n") - 1, true, NULL);
777 RTManifestWriteStandard(hTheirManifest, hVfsIosStdOut);
778#endif
779 }
780 else
781 vboxExtPackSetError(pszError, cbError, "Error parsing '%s': %Rrc", VBOX_EXTPACK_MANIFEST_NAME, rc);
782
783 RTManifestRelease(hTheirManifest);
784 return rc;
785}
786
787
788/**
789 * Validates a standard file.
790 *
791 * Generally all files are
792 *
793 * @returns VBox status code, failure message in @a pszError.
794 * @param pszAdjName The adjusted member name.
795 * @param enmType The VFS object type.
796 * @param phVfsObj The pointer to the VFS object handle variable.
797 * This is both input and output.
798 * @param phVfsFile Where to store the handle to the memorized
799 * file. This is NULL for license files.
800 * @param pszError Where to write an error message on failure.
801 * @param cbError The size of the @a pszError buffer.
802 */
803static int VBoxExtPackValidateStandardFile(const char *pszAdjName, RTVFSOBJTYPE enmType,
804 PRTVFSOBJ phVfsObj, PRTVFSFILE phVfsFile, char *pszError, size_t cbError)
805{
806 int rc;
807
808 /*
809 * Make sure it's a file and that it isn't too large.
810 */
811 if (phVfsFile && *phVfsFile != NIL_RTVFSFILE)
812 rc = vboxExtPackReturnError(VERR_DUPLICATE, pszError, cbError,
813 "There can only be one '%s'", pszAdjName);
814 else if (enmType != RTVFSOBJTYPE_IO_STREAM && enmType != RTVFSOBJTYPE_FILE)
815 rc = vboxExtPackReturnError(VERR_NOT_A_FILE, pszError, cbError,
816 "Standard member '%s' is not a file", pszAdjName);
817 else
818 {
819 RTFSOBJINFO ObjInfo;
820 rc = RTVfsObjQueryInfo(*phVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
821 if (RT_SUCCESS(rc))
822 {
823 if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
824 rc = vboxExtPackReturnError(VERR_NOT_A_FILE, pszError, cbError,
825 "Standard member '%s' is not a file", pszAdjName);
826 else if (ObjInfo.cbObject >= _1M)
827 rc = vboxExtPackReturnError(VERR_OUT_OF_RANGE, pszError, cbError,
828 "Standard member '%s' is too large: %'RU64 bytes (max 1 MB)",
829 pszAdjName, (uint64_t)ObjInfo.cbObject);
830 else
831 {
832 /*
833 * Make an in memory copy of the stream and check that the file
834 * is UTF-8 clean.
835 */
836 RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(*phVfsObj);
837 RTVFSFILE hVfsFile;
838 rc = RTVfsMemorizeIoStreamAsFile(hVfsIos, RTFILE_O_READ, &hVfsFile);
839 if (RT_SUCCESS(rc))
840 {
841 rc = RTVfsIoStrmValidateUtf8Encoding(hVfsIos,
842 RTVFS_VALIDATE_UTF8_BY_RTC_3629 | RTVFS_VALIDATE_UTF8_NO_NULL,
843 NULL);
844 if (RT_SUCCESS(rc))
845 {
846 /*
847 * Replace *phVfsObj with the memorized file.
848 */
849 rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
850 if (RT_SUCCESS(rc))
851 {
852 RTVfsObjRelease(*phVfsObj);
853 *phVfsObj = RTVfsObjFromFile(hVfsFile);
854 }
855 else
856 vboxExtPackSetError(pszError, cbError, "RTVfsFileSeek failed on '%s': %Rrc", pszAdjName, rc);
857 }
858
859 if (phVfsFile && RT_SUCCESS(rc))
860 *phVfsFile = hVfsFile;
861 else
862 RTVfsFileRelease(hVfsFile);
863 }
864 else
865 vboxExtPackSetError(pszError, cbError, "RTVfsMemorizeIoStreamAsFile failed on '%s': %Rrc", pszAdjName, rc);
866 RTVfsIoStrmRelease(hVfsIos);
867 }
868 }
869 else
870 vboxExtPackSetError(pszError, cbError, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszAdjName, rc);
871 }
872 return rc;
873}
874
875
876/**
877 * Validates a name in an extension pack.
878 *
879 * We restrict the charset to try make sure the extension pack can be unpacked
880 * on all file systems.
881 *
882 * @returns VBox status code, failures with message.
883 * @param pszName The name to validate.
884 * @param pszError Where to store an error message on failure.
885 * @param cbError The size of the buffer @a pszError points to.
886 */
887static int vboxExtPackValidateMemberName(const char *pszName, char *pszError, size_t cbError)
888{
889 if (RTPathStartsWithRoot(pszName))
890 return vboxExtPackReturnError(VERR_PATH_IS_NOT_RELATIVE, pszError, cbError, "'%s': starts with root spec", pszName);
891
892 const char *pszErr = NULL;
893 const char *psz = pszName;
894 int ch;
895 while ((ch = *psz) != '\0')
896 {
897 /* Character set restrictions. */
898 if (ch < 0 || ch >= 128)
899 {
900 pszErr = "Only 7-bit ASCII allowed";
901 break;
902 }
903 if (ch <= 31 || ch == 127)
904 {
905 pszErr = "No control characters are not allowed";
906 break;
907 }
908 if (ch == '\\')
909 {
910 pszErr = "Only backward slashes are not allowed";
911 break;
912 }
913 if (strchr("'\":;*?|[]<>(){}", ch))
914 {
915 pszErr = "The characters ', \", :, ;, *, ?, |, [, ], <, >, (, ), { and } are not allowed";
916 break;
917 }
918
919 /* Take the simple way out and ban all ".." sequences. */
920 if ( ch == '.'
921 && psz[1] == '.')
922 {
923 pszErr = "Double dot sequence are not allowed";
924 break;
925 }
926
927 /* Keep the tree shallow or the hardening checks will fail. */
928 if (psz - pszName > VBOX_EXTPACK_MAX_MEMBER_NAME_LENGTH)
929 {
930 pszErr = "Too long";
931 break;
932 }
933
934 /* advance */
935 psz++;
936 }
937
938 if (pszErr)
939 return vboxExtPackReturnError(VERR_INVALID_NAME, pszError, cbError,
940 "Bad member name '%s' (pos %zu): %s", pszName, (size_t)(psz - pszName), pszErr);
941 return RTEXITCODE_SUCCESS;
942}
943
944
945/**
946 * Validates a file in an extension pack.
947 *
948 * @returns VBox status code, failures with message.
949 * @param pszName The name of the file.
950 * @param hVfsObj The VFS object.
951 * @param pszError Where to store an error message on failure.
952 * @param cbError The size of the buffer @a pszError points to.
953 */
954static int vboxExtPackValidateMemberFile(const char *pszName, RTVFSOBJ hVfsObj, char *pszError, size_t cbError)
955{
956 int rc = vboxExtPackValidateMemberName(pszName, pszError, cbError);
957 if (RT_SUCCESS(rc))
958 {
959 RTFSOBJINFO ObjInfo;
960 rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
961 if (RT_SUCCESS(rc))
962 {
963 if (ObjInfo.cbObject >= 9*_1G64)
964 rc = vboxExtPackReturnError(VERR_OUT_OF_RANGE, pszError, cbError,
965 "'%s': too large (%'RU64 bytes)",
966 pszName, (uint64_t)ObjInfo.cbObject);
967 if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
968 rc = vboxExtPackReturnError(VERR_NOT_A_FILE, pszError, cbError,
969 "The alleged file '%s' has a mode mask stating otherwise (%RTfmode)",
970 pszName, ObjInfo.Attr.fMode);
971 }
972 else
973 vboxExtPackSetError(pszError, cbError, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszName, rc);
974 }
975 return rc;
976}
977
978
979/**
980 * Validates a directory in an extension pack.
981 *
982 * @returns VBox status code, failures with message.
983 * @param pszName The name of the directory.
984 * @param hVfsObj The VFS object.
985 * @param pszError Where to store an error message on failure.
986 * @param cbError The size of the buffer @a pszError points to.
987 */
988static int vboxExtPackValidateMemberDir(const char *pszName, RTVFSOBJ hVfsObj, char *pszError, size_t cbError)
989{
990 int rc = vboxExtPackValidateMemberName(pszName, pszError, cbError);
991 if (RT_SUCCESS(rc))
992 {
993 RTFSOBJINFO ObjInfo;
994 rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
995 if (RT_SUCCESS(rc))
996 {
997 if (!RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
998 rc = vboxExtPackReturnError(VERR_NOT_A_DIRECTORY, pszError, cbError,
999 "The alleged directory '%s' has a mode mask saying differently (%RTfmode)",
1000 pszName, ObjInfo.Attr.fMode);
1001 }
1002 else
1003 vboxExtPackSetError(pszError, cbError, "RTVfsObjQueryInfo failed on '%s': %Rrc", pszName, rc);
1004 }
1005 return rc;
1006}
1007
1008/**
1009 * Validates a member of an extension pack.
1010 *
1011 * @returns VBox status code, failures with message.
1012 * @param pszName The name of the directory.
1013 * @param enmType The object type.
1014 * @param hVfsObj The VFS object.
1015 * @param pszError Where to store an error message on failure.
1016 * @param cbError The size of the buffer @a pszError points to.
1017 */
1018int VBoxExtPackValidateMember(const char *pszName, RTVFSOBJTYPE enmType, RTVFSOBJ hVfsObj, char *pszError, size_t cbError)
1019{
1020 Assert(cbError > 0);
1021 *pszError = '\0';
1022
1023 int rc;
1024 if ( enmType == RTVFSOBJTYPE_FILE
1025 || enmType == RTVFSOBJTYPE_IO_STREAM)
1026 rc = vboxExtPackValidateMemberFile(pszName, hVfsObj, pszError, cbError);
1027 else if ( enmType == RTVFSOBJTYPE_DIR
1028 || enmType == RTVFSOBJTYPE_BASE)
1029 rc = vboxExtPackValidateMemberDir(pszName, hVfsObj, pszError, cbError);
1030 else
1031 rc = vboxExtPackReturnError(VERR_UNEXPECTED_FS_OBJ_TYPE, pszError, cbError,
1032 "'%s' is not a file or directory (enmType=%d)", pszName, enmType);
1033 return rc;
1034}
1035
1036
1037/**
1038 * Rewinds the tarball file handle and creates a gunzip | tar chain that
1039 * results in a filesystem stream.
1040 *
1041 * @returns VBox status code, failures with message.
1042 * @param hTarballFile The handle to the tarball file.
1043 * @param pszError Where to store an error message on failure.
1044 * @param cbError The size of the buffer @a pszError points to.
1045 * @param phTarFss Where to return the filesystem stream handle.
1046 */
1047int VBoxExtPackOpenTarFss(RTFILE hTarballFile, char *pszError, size_t cbError, PRTVFSFSSTREAM phTarFss)
1048{
1049 Assert(cbError > 0);
1050 *pszError = '\0';
1051 *phTarFss = NIL_RTVFSFSSTREAM;
1052
1053 /*
1054 * Rewind the file and set up a VFS chain for it.
1055 */
1056 int rc = RTFileSeek(hTarballFile, 0, RTFILE_SEEK_BEGIN, NULL);
1057 if (RT_FAILURE(rc))
1058 return vboxExtPackReturnError(rc, pszError, cbError, "Failed seeking to the start of the tarball: %Rrc", rc);
1059
1060 RTVFSIOSTREAM hTarballIos;
1061 rc = RTVfsIoStrmFromRTFile(hTarballFile, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN, true /*fLeaveOpen*/,
1062 &hTarballIos);
1063 if (RT_FAILURE(rc))
1064 return vboxExtPackReturnError(rc, pszError, cbError, "RTVfsIoStrmFromRTFile failed: %Rrc", rc);
1065
1066 RTVFSIOSTREAM hGunzipIos;
1067 rc = RTZipGzipDecompressIoStream(hTarballIos, 0 /*fFlags*/, &hGunzipIos);
1068 if (RT_SUCCESS(rc))
1069 {
1070 RTVFSFSSTREAM hTarFss;
1071 rc = RTZipTarFsStreamFromIoStream(hGunzipIos, 0 /*fFlags*/, &hTarFss);
1072 if (RT_SUCCESS(rc))
1073 {
1074 RTVfsIoStrmRelease(hGunzipIos);
1075 RTVfsIoStrmRelease(hTarballIos);
1076 *phTarFss = hTarFss;
1077 return VINF_SUCCESS;
1078 }
1079 vboxExtPackSetError(pszError, cbError, "RTZipTarFsStreamFromIoStream failed: %Rrc", rc);
1080 RTVfsIoStrmRelease(hGunzipIos);
1081 }
1082 else
1083 vboxExtPackSetError(pszError, cbError, "RTZipGzipDecompressIoStream failed: %Rrc", rc);
1084 RTVfsIoStrmRelease(hTarballIos);
1085 return rc;
1086}
1087
1088
1089/**
1090 * Validates the extension pack tarball prior to unpacking.
1091 *
1092 * Operations performed:
1093 * - Mandatory files.
1094 * - Manifest check.
1095 * - Manifest seal check.
1096 * - XML check, match name.
1097 *
1098 * @returns VBox status code, failures with message.
1099 * @param hTarballFile The handle to open the @a pszTarball file.
1100 * @param pszExtPackName The name of the extension pack name. NULL if
1101 * the name is not fixed.
1102 * @param pszTarball The name of the tarball in case we have to
1103 * complain about something.
1104 * @param pszError Where to store an error message on failure.
1105 * @param cbError The size of the buffer @a pszError points to.
1106 * @param phValidManifest Where to optionally return the handle to fully
1107 * validated the manifest for the extension pack.
1108 * This includes all files.
1109 * @param phXmlFile Where to optionally return the memorized XML
1110 * file.
1111 *
1112 * @todo This function is a bit too long and should be split up if possible.
1113 */
1114int VBoxExtPackValidateTarball(RTFILE hTarballFile, const char *pszExtPackName, const char *pszTarball,
1115 char *pszError, size_t cbError, PRTMANIFEST phValidManifest, PRTVFSFILE phXmlFile)
1116{
1117 /*
1118 * Clear return values.
1119 */
1120 if (phValidManifest)
1121 *phValidManifest = NIL_RTMANIFEST;
1122 if (phXmlFile)
1123 *phXmlFile = NIL_RTVFSFILE;
1124 Assert(cbError > 1);
1125 *pszError = '\0';
1126 NOREF(pszTarball);
1127
1128 /*
1129 * Open the tar.gz filesystem stream and set up an manifest in-memory file.
1130 */
1131 RTVFSFSSTREAM hTarFss;
1132 int rc = VBoxExtPackOpenTarFss(hTarballFile, pszError, cbError, &hTarFss);
1133 if (RT_FAILURE(rc))
1134 return rc;
1135
1136 RTMANIFEST hOurManifest;
1137 rc = RTManifestCreate(0 /*fFlags*/, &hOurManifest);
1138 if (RT_SUCCESS(rc))
1139 {
1140 /*
1141 * Process the tarball (would be nice to move this to a function).
1142 */
1143 RTVFSFILE hXmlFile = NIL_RTVFSFILE;
1144 RTVFSFILE hManifestFile = NIL_RTVFSFILE;
1145 RTVFSFILE hSignatureFile= NIL_RTVFSFILE;
1146 for (;;)
1147 {
1148 /*
1149 * Get the next stream object.
1150 */
1151 char *pszName;
1152 RTVFSOBJ hVfsObj;
1153 RTVFSOBJTYPE enmType;
1154 rc = RTVfsFsStrmNext(hTarFss, &pszName, &enmType, &hVfsObj);
1155 if (RT_FAILURE(rc))
1156 {
1157 if (rc != VERR_EOF)
1158 vboxExtPackSetError(pszError, cbError, "RTVfsFsStrmNext failed: %Rrc", rc);
1159 else
1160 rc = VINF_SUCCESS;
1161 break;
1162 }
1163 const char *pszAdjName = pszName[0] == '.' && pszName[1] == '/' ? &pszName[2] : pszName;
1164
1165 /*
1166 * Check the type & name validity, performing special tests on
1167 * standard extension pack member files.
1168 *
1169 * N.B. We will always reach the end of the loop before breaking on
1170 * failure - cleanup reasons.
1171 */
1172 rc = VBoxExtPackValidateMember(pszName, enmType, hVfsObj, pszError, cbError);
1173 if (RT_SUCCESS(rc))
1174 {
1175 PRTVFSFILE phVfsFile = NULL;
1176 if (!strcmp(pszAdjName, VBOX_EXTPACK_DESCRIPTION_NAME))
1177 phVfsFile = &hXmlFile;
1178 else if (!strcmp(pszAdjName, VBOX_EXTPACK_MANIFEST_NAME))
1179 phVfsFile = &hManifestFile;
1180 else if (!strcmp(pszAdjName, VBOX_EXTPACK_SIGNATURE_NAME))
1181 phVfsFile = &hSignatureFile;
1182 else if (!strncmp(pszAdjName, VBOX_EXTPACK_LICENSE_NAME_PREFIX, sizeof(VBOX_EXTPACK_LICENSE_NAME_PREFIX) - 1))
1183 rc = VBoxExtPackValidateStandardFile(pszAdjName, enmType, &hVfsObj, NULL, pszError, cbError);
1184 if (phVfsFile)
1185 rc = VBoxExtPackValidateStandardFile(pszAdjName, enmType, &hVfsObj, phVfsFile, pszError, cbError);
1186 }
1187
1188 /*
1189 * Add any I/O stream to the manifest
1190 */
1191 if ( RT_SUCCESS(rc)
1192 && ( enmType == RTVFSOBJTYPE_FILE
1193 || enmType == RTVFSOBJTYPE_IO_STREAM))
1194 {
1195 RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
1196 rc = RTManifestEntryAddIoStream(hOurManifest, hVfsIos, pszAdjName, RTMANIFEST_ATTR_SIZE | RTMANIFEST_ATTR_SHA256);
1197 if (RT_FAILURE(rc))
1198 vboxExtPackSetError(pszError, cbError, "RTManifestEntryAddIoStream failed on '%s': %Rrc", pszAdjName, rc);
1199 RTVfsIoStrmRelease(hVfsIos);
1200 }
1201
1202 /*
1203 * Clean up and break out on failure.
1204 */
1205 RTVfsObjRelease(hVfsObj);
1206 RTStrFree(pszName);
1207 if (RT_FAILURE(rc))
1208 break;
1209 }
1210
1211 /*
1212 * If we've successfully processed the tarball, verify that the
1213 * mandatory files are present.
1214 */
1215 if (RT_SUCCESS(rc))
1216 {
1217 if (hXmlFile == NIL_RTVFSFILE)
1218 rc = vboxExtPackReturnError(VERR_MISSING, pszError, cbError, "Mandator file '%s' is missing", VBOX_EXTPACK_DESCRIPTION_NAME);
1219 if (hManifestFile == NIL_RTVFSFILE)
1220 rc = vboxExtPackReturnError(VERR_MISSING, pszError, cbError, "Mandator file '%s' is missing", VBOX_EXTPACK_MANIFEST_NAME);
1221 if (hSignatureFile == NIL_RTVFSFILE)
1222 rc = vboxExtPackReturnError(VERR_MISSING, pszError, cbError, "Mandator file '%s' is missing", VBOX_EXTPACK_SIGNATURE_NAME);
1223 }
1224
1225 /*
1226 * Check the manifest and it's signature.
1227 */
1228 if (RT_SUCCESS(rc))
1229 rc = vboxExtPackVerifyManifestAndSignature(hOurManifest, hManifestFile, hSignatureFile, pszError, cbError);
1230
1231 /*
1232 * Check the XML.
1233 */
1234 if (RT_SUCCESS(rc))
1235 rc = vboxExtPackVerifyXml(hXmlFile, pszExtPackName, pszError, cbError);
1236
1237 /*
1238 * Returns objects.
1239 */
1240 if (RT_SUCCESS(rc))
1241 {
1242 if (phValidManifest)
1243 {
1244 RTManifestRetain(hOurManifest);
1245 *phValidManifest = hOurManifest;
1246 }
1247 if (phXmlFile)
1248 {
1249 RTVfsFileRetain(hXmlFile);
1250 *phXmlFile = hXmlFile;
1251 }
1252 }
1253
1254 /*
1255 * Release our object references.
1256 */
1257 RTManifestRelease(hOurManifest);
1258 RTVfsFileRelease(hXmlFile);
1259 RTVfsFileRelease(hManifestFile);
1260 RTVfsFileRelease(hSignatureFile);
1261 }
1262 else
1263 vboxExtPackSetError(pszError, cbError, "RTManifestCreate failed: %Rrc", rc);
1264 RTVfsFsStrmRelease(hTarFss);
1265
1266 return rc;
1267}
1268
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use