VirtualBox

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

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

VBoxExtPackIsValidVersionString: Allow a tag at the end of the version string indicating the extension pack "edition".

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

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