VirtualBox

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

Last change on this file since 43316 was 41783, checked in by vboxsync, 12 years ago

Doxygen, comment typos.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use