VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/zip/xarvfs.cpp@ 57358

最後變更 在這個檔案從57358是 57358,由 vboxsync 提交於 10 年 前

*: scm cleanup run.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 68.2 KB
 
1/* $Id: xarvfs.cpp 57358 2015-08-14 15:16:38Z vboxsync $ */
2/** @file
3 * IPRT - XAR Virtual Filesystem.
4 */
5
6/*
7 * Copyright (C) 2010-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/******************************************************************************
29#include "internal/iprt.h"
30#include <iprt/zip.h>
31
32#include <iprt/asm.h>
33#include <iprt/assert.h>
34#include <iprt/ctype.h>
35#include <iprt/err.h>
36#include <iprt/md5.h>
37#include <iprt/poll.h>
38#include <iprt/file.h>
39#include <iprt/sha.h>
40#include <iprt/string.h>
41#include <iprt/vfs.h>
42#include <iprt/vfslowlevel.h>
43#include <iprt/formats/xar.h>
44#include <iprt/cpp/xml.h>
45
46
47/*********************************************************************************************************************************
48* Defined Constants And Macros *
49*********************************************************************************************************************************/
50/** @name Hash state
51 * @{ */
52#define RTZIPXAR_HASH_PENDING 0
53#define RTZIPXAR_HASH_OK 1
54#define RTZIPXAR_HASH_FAILED_ARCHIVED 2
55#define RTZIPXAR_HASH_FAILED_EXTRACTED 3
56/** @} */
57
58
59/*********************************************************************************************************************************
60* Structures and Typedefs *
61*********************************************************************************************************************************/
62/**
63 * Hash digest value union for the supported XAR hash functions.
64 * @todo This could be generalized in iprt/checksum.h or somewhere.
65 */
66typedef union RTZIPXARHASHDIGEST
67{
68 uint8_t abMd5[RTMD5_HASH_SIZE];
69 uint8_t abSha1[RTSHA1_HASH_SIZE];
70} RTZIPXARHASHDIGEST;
71/** Pointer to a XAR hash digest union. */
72typedef RTZIPXARHASHDIGEST *PRTZIPXARHASHDIGEST;
73/** Pointer to a const XAR hash digest union. */
74typedef RTZIPXARHASHDIGEST *PCRTZIPXARHASHDIGEST;
75
76/**
77 * Hash context union.
78 */
79typedef union RTZIPXARHASHCTX
80{
81 RTMD5CONTEXT Md5;
82 RTSHA1CONTEXT Sha1;
83} RTZIPXARHASHCTX;
84/** Pointer to a hash context union. */
85typedef RTZIPXARHASHCTX *PRTZIPXARHASHCTX;
86
87/**
88 * XAR reader instance data.
89 */
90typedef struct RTZIPXARREADER
91{
92 /** The TOC XML element. */
93 xml::ElementNode const *pToc;
94 /** The TOC XML document. */
95 xml::Document *pDoc;
96
97 /** The current file. */
98 xml::ElementNode const *pCurFile;
99 /** The depth of the current file, with 0 being the root level. */
100 uint32_t cCurDepth;
101} RTZIPXARREADER;
102/** Pointer to the XAR reader instance data. */
103typedef RTZIPXARREADER *PRTZIPXARREADER;
104
105/**
106 * Xar directory, character device, block device, fifo socket or symbolic link.
107 */
108typedef struct RTZIPXARBASEOBJ
109{
110 /** The file TOC element. */
111 xml::ElementNode const *pFileElem;
112 /** RTFS_TYPE_XXX value for the object. */
113 RTFMODE fModeType;
114} RTZIPXARBASEOBJ;
115/** Pointer to a XAR filesystem stream base object. */
116typedef RTZIPXARBASEOBJ *PRTZIPXARBASEOBJ;
117
118
119/**
120 * XAR data encoding.
121 */
122typedef enum RTZIPXARENCODING
123{
124 RTZIPXARENCODING_INVALID = 0,
125 RTZIPXARENCODING_STORE,
126 RTZIPXARENCODING_GZIP,
127 RTZIPXARENCODING_UNSUPPORTED,
128 RTZIPXARENCODING_END
129} RTZIPXARENCODING;
130
131
132/**
133 * Data stream attributes.
134 */
135typedef struct RTZIPXARDATASTREAM
136{
137 /** Offset of the data in the stream.
138 * @remarks The I/O stream and file constructor will adjust this so that it
139 * relative to the start of the input stream, instead of the first byte
140 * after the TOC. */
141 RTFOFF offData;
142 /** The size of the archived data. */
143 RTFOFF cbDataArchived;
144 /** The size of the extracted data. */
145 RTFOFF cbDataExtracted;
146 /** The encoding of the archived ata. */
147 RTZIPXARENCODING enmEncoding;
148 /** The hash function used for the archived data. */
149 uint8_t uHashFunArchived;
150 /** The hash function used for the extracted data. */
151 uint8_t uHashFunExtracted;
152 /** The digest of the archived data. */
153 RTZIPXARHASHDIGEST DigestArchived;
154 /** The digest of the extracted data. */
155 RTZIPXARHASHDIGEST DigestExtracted;
156} RTZIPXARDATASTREAM;
157/** Pointer to XAR data stream attributes. */
158typedef RTZIPXARDATASTREAM *PRTZIPXARDATASTREAM;
159
160
161/**
162 * Xar file represented as a VFS I/O stream.
163 */
164typedef struct RTZIPXARIOSTREAM
165{
166 /** The basic XAR object data. */
167 RTZIPXARBASEOBJ BaseObj;
168 /** The attributes of the primary data stream. */
169 RTZIPXARDATASTREAM DataAttr;
170 /** The current file position in the archived file. */
171 RTFOFF offCurPos;
172 /** The input I/O stream. */
173 RTVFSIOSTREAM hVfsIos;
174 /** Set if we've reached the end of the file or if the next object in the
175 * file system stream has been requested. */
176 bool fEndOfStream;
177 /** Whether the stream is seekable. */
178 bool fSeekable;
179 /** Hash state. */
180 uint8_t uHashState;
181 /** The size of the file that we've currently hashed.
182 * We use this to check whether the user skips part of the file while reading
183 * and when to compare the digests. */
184 RTFOFF cbDigested;
185 /** The digest of the archived data. */
186 RTZIPXARHASHCTX CtxArchived;
187 /** The digest of the extracted data. */
188 RTZIPXARHASHCTX CtxExtracted;
189} RTZIPXARIOSTREAM;
190/** Pointer to a the private data of a XAR file I/O stream. */
191typedef RTZIPXARIOSTREAM *PRTZIPXARIOSTREAM;
192
193
194/**
195 * Xar file represented as a VFS file.
196 */
197typedef struct RTZIPXARFILE
198{
199 /** The XAR I/O stream data. */
200 RTZIPXARIOSTREAM Ios;
201 /** The input file. */
202 RTVFSFILE hVfsFile;
203} RTZIPXARFILE;
204/** Pointer to the private data of a XAR file. */
205typedef RTZIPXARFILE *PRTZIPXARFILE;
206
207
208/**
209 * Decompressed I/O stream instance.
210 *
211 * This is just a front that checks digests and other sanity stuff.
212 */
213typedef struct RTZIPXARDECOMPIOS
214{
215 /** The decompressor I/O stream. */
216 RTVFSIOSTREAM hVfsIosDecompressor;
217 /** The raw XAR I/O stream. */
218 RTVFSIOSTREAM hVfsIosRaw;
219 /** Pointer to the raw XAR I/O stream instance data. */
220 PRTZIPXARIOSTREAM pIosRaw;
221 /** The current file position in the archived file. */
222 RTFOFF offCurPos;
223 /** The hash function to use on the extracted data. */
224 uint8_t uHashFunExtracted;
225 /** Hash state on the extracted data. */
226 uint8_t uHashState;
227 /** The digest of the extracted data. */
228 RTZIPXARHASHCTX CtxExtracted;
229 /** The expected digest of the extracted data. */
230 RTZIPXARHASHDIGEST DigestExtracted;
231} RTZIPXARDECOMPIOS;
232/** Pointer to the private data of a XAR decompressed I/O stream. */
233typedef RTZIPXARDECOMPIOS *PRTZIPXARDECOMPIOS;
234
235
236/**
237 * Xar filesystem stream private data.
238 */
239typedef struct RTZIPXARFSSTREAM
240{
241 /** The input I/O stream. */
242 RTVFSIOSTREAM hVfsIos;
243 /** The input file, if the stream is actually a file. */
244 RTVFSFILE hVfsFile;
245
246 /** The start offset in the input I/O stream. */
247 RTFOFF offStart;
248 /** The zero offset in the file which all others are relative to. */
249 RTFOFF offZero;
250
251 /** The hash function we're using (XAR_HASH_XXX). */
252 uint8_t uHashFunction;
253 /** The size of the digest produced by the hash function we're using. */
254 uint8_t cbHashDigest;
255
256 /** Set if we've reached the end of the stream. */
257 bool fEndOfStream;
258 /** Set if we've encountered a fatal error. */
259 int rcFatal;
260
261
262 /** The XAR reader instance data. */
263 RTZIPXARREADER XarReader;
264} RTZIPXARFSSTREAM;
265/** Pointer to a the private data of a XAR filesystem stream. */
266typedef RTZIPXARFSSTREAM *PRTZIPXARFSSTREAM;
267
268
269/**
270 * Hashes a block of data.
271 *
272 * @param uHashFunction The hash function to use.
273 * @param pvSrc The data to hash.
274 * @param cbSrc The size of the data to hash.
275 * @param pHashDigest Where to return the message digest.
276 */
277static void rtZipXarCalcHash(uint32_t uHashFunction, void const *pvSrc, size_t cbSrc, PRTZIPXARHASHDIGEST pHashDigest)
278{
279 switch (uHashFunction)
280 {
281 case XAR_HASH_SHA1:
282 RTSha1(pvSrc, cbSrc, pHashDigest->abSha1);
283 break;
284 case XAR_HASH_MD5:
285 RTMd5(pvSrc, cbSrc, pHashDigest->abMd5);
286 break;
287 default:
288 RT_ZERO(*pHashDigest);
289 break;
290 }
291}
292
293
294/**
295 * Initializes a hash context.
296 *
297 * @param pCtx Pointer to the context union.
298 * @param uHashFunction The hash function to use.
299 */
300static void rtZipXarHashInit(PRTZIPXARHASHCTX pCtx, uint32_t uHashFunction)
301{
302 switch (uHashFunction)
303 {
304 case XAR_HASH_SHA1:
305 RTSha1Init(&pCtx->Sha1);
306 break;
307 case XAR_HASH_MD5:
308 RTMd5Init(&pCtx->Md5);;
309 break;
310 default:
311 RT_ZERO(*pCtx);
312 break;
313 }
314}
315
316
317/**
318 * Adds a block to the hash calculation.
319 *
320 * @param pCtx Pointer to the context union.
321 * @param uHashFunction The hash function to use.
322 * @param pvSrc The data to add to the hash.
323 * @param cbSrc The size of the data.
324 */
325static void rtZipXarHashUpdate(PRTZIPXARHASHCTX pCtx, uint32_t uHashFunction, void const *pvSrc, size_t cbSrc)
326{
327 switch (uHashFunction)
328 {
329 case XAR_HASH_SHA1:
330 RTSha1Update(&pCtx->Sha1, pvSrc, cbSrc);
331 break;
332 case XAR_HASH_MD5:
333 RTMd5Update(&pCtx->Md5, pvSrc, cbSrc);
334 break;
335 }
336}
337
338
339/**
340 * Finalizes the hash, producing the message digest.
341 *
342 * @param pCtx Pointer to the context union.
343 * @param uHashFunction The hash function to use.
344 * @param pHashDigest Where to return the message digest.
345 */
346static void rtZipXarHashFinal(PRTZIPXARHASHCTX pCtx, uint32_t uHashFunction, PRTZIPXARHASHDIGEST pHashDigest)
347{
348 switch (uHashFunction)
349 {
350 case XAR_HASH_SHA1:
351 RTSha1Final(&pCtx->Sha1, pHashDigest->abSha1);
352 break;
353 case XAR_HASH_MD5:
354 RTMd5Final(pHashDigest->abMd5, &pCtx->Md5);
355 break;
356 default:
357 RT_ZERO(*pHashDigest);
358 break;
359 }
360}
361
362
363/**
364 * Compares two hash digests.
365 *
366 * @returns true if equal, false if not.
367 * @param uHashFunction The hash function to use.
368 * @param pHashDigest1 The first hash digest.
369 * @param pHashDigest2 The second hash digest.
370 */
371static bool rtZipXarHashIsEqual(uint32_t uHashFunction, PRTZIPXARHASHDIGEST pHashDigest1, PRTZIPXARHASHDIGEST pHashDigest2)
372{
373 switch (uHashFunction)
374 {
375 case XAR_HASH_SHA1:
376 return memcmp(pHashDigest1->abSha1, pHashDigest2->abSha1, sizeof(pHashDigest1->abSha1)) == 0;
377 case XAR_HASH_MD5:
378 return memcmp(pHashDigest1->abMd5, pHashDigest2->abMd5, sizeof(pHashDigest1->abMd5)) == 0;
379 default:
380 return true;
381 }
382}
383
384
385/**
386 * Gets the 'offset', 'size' and optionally 'length' sub elements.
387 *
388 * @returns IPRT status code.
389 * @param pElement The parent element.
390 * @param poff Where to return the offset value.
391 * @param pcbSize Where to return the size value.
392 * @param pcbLength Where to return the length value, optional.
393 */
394static int rtZipXarGetOffsetSizeLengthFromElem(xml::ElementNode const *pElement,
395 PRTFOFF poff, PRTFOFF pcbSize, PRTFOFF pcbLength)
396{
397 /*
398 * The offset.
399 */
400 xml::ElementNode const *pElem = pElement->findChildElement("offset");
401 if (!pElem)
402 return VERR_XAR_MISSING_OFFSET_ELEMENT;
403 const char *pszValue = pElem->getValue();
404 if (!pszValue)
405 return VERR_XAR_BAD_OFFSET_ELEMENT;
406
407 int rc = RTStrToInt64Full(pszValue, 0, poff);
408 if ( RT_FAILURE(rc)
409 || rc == VWRN_NUMBER_TOO_BIG
410 || *poff > RTFOFF_MAX / 2 /* make sure to not overflow calculating offsets. */
411 || *poff < 0)
412 return VERR_XAR_BAD_OFFSET_ELEMENT;
413
414 /*
415 * The 'size' stored in the archive.
416 */
417 pElem = pElement->findChildElement("size");
418 if (!pElem)
419 return VERR_XAR_MISSING_SIZE_ELEMENT;
420
421 pszValue = pElem->getValue();
422 if (!pszValue)
423 return VERR_XAR_BAD_SIZE_ELEMENT;
424
425 rc = RTStrToInt64Full(pszValue, 0, pcbSize);
426 if ( RT_FAILURE(rc)
427 || rc == VWRN_NUMBER_TOO_BIG
428 || *pcbSize >= RTFOFF_MAX - _1M
429 || *pcbSize < 0)
430 return VERR_XAR_BAD_SIZE_ELEMENT;
431 AssertCompile(RTFOFF_MAX == UINT64_MAX / 2);
432
433 /*
434 * The 'length' of the uncompressed data. Not present for checksums, so
435 * the caller might not want it.
436 */
437 if (pcbLength)
438 {
439 pElem = pElement->findChildElement("length");
440 if (!pElem)
441 return VERR_XAR_MISSING_LENGTH_ELEMENT;
442
443 pszValue = pElem->getValue();
444 if (!pszValue)
445 return VERR_XAR_BAD_LENGTH_ELEMENT;
446
447 rc = RTStrToInt64Full(pszValue, 0, pcbLength);
448 if ( RT_FAILURE(rc)
449 || rc == VWRN_NUMBER_TOO_BIG
450 || *pcbLength >= RTFOFF_MAX - _1M
451 || *pcbLength < 0)
452 return VERR_XAR_BAD_LENGTH_ELEMENT;
453 AssertCompile(RTFOFF_MAX == UINT64_MAX / 2);
454 }
455
456 return VINF_SUCCESS;
457}
458
459
460/**
461 * Convers a checksum style value into a XAR hash function number.
462 *
463 * @returns IPRT status code.
464 * @param pszStyle The XAR checksum style.
465 * @param puHashFunction Where to return the hash function number on success.
466 */
467static int rtZipXarParseChecksumStyle(const char *pszStyle, uint8_t *puHashFunction)
468{
469 size_t cchStyle = strlen(pszStyle);
470 if ( cchStyle == 4
471 && (pszStyle[0] == 's' || pszStyle[0] == 'S')
472 && (pszStyle[1] == 'h' || pszStyle[1] == 'H')
473 && (pszStyle[2] == 'a' || pszStyle[2] == 'A')
474 && pszStyle[3] == '1' )
475 *puHashFunction = XAR_HASH_SHA1;
476 else if ( cchStyle == 3
477 && (pszStyle[0] == 'm' || pszStyle[0] == 'M')
478 && (pszStyle[1] == 'd' || pszStyle[1] == 'D')
479 && pszStyle[2] == '5' )
480 *puHashFunction = XAR_HASH_MD5;
481 else if ( cchStyle == 4
482 && (pszStyle[0] == 'n' || pszStyle[0] == 'N')
483 && (pszStyle[1] == 'o' || pszStyle[1] == 'O')
484 && (pszStyle[2] == 'n' || pszStyle[2] == 'N')
485 && (pszStyle[3] == 'e' || pszStyle[3] == 'E') )
486 *puHashFunction = XAR_HASH_NONE;
487 else
488 {
489 *puHashFunction = UINT8_MAX;
490 return VERR_XAR_BAD_CHECKSUM_ELEMENT;
491 }
492 return VINF_SUCCESS;
493}
494
495
496/**
497 * Parses a checksum element typically found under 'data'.
498 *
499 * @returns IPRT status code.
500 * @param pParentElem The parent element ('data').
501 * @param pszName The name of the element, like 'checksum-archived' or
502 * 'checksum-extracted'.
503 * @param puHashFunction Where to return the XAR hash function number.
504 * @param pDigest Where to return the expected message digest.
505 */
506static int rtZipXarParseChecksumElem(xml::ElementNode const *pParentElem, const char *pszName,
507 uint8_t *puHashFunction, PRTZIPXARHASHDIGEST pDigest)
508{
509 /* Default is no checksum. */
510 *puHashFunction = XAR_HASH_NONE;
511 RT_ZERO(*pDigest);
512
513 xml::ElementNode const *pChecksumElem = pParentElem->findChildElement(pszName);
514 if (!pChecksumElem)
515 return VINF_SUCCESS;
516
517 /* The style. */
518 const char *pszStyle = pChecksumElem->findAttributeValue("style");
519 if (!pszStyle)
520 return VERR_XAR_BAD_CHECKSUM_ELEMENT;
521 int rc = rtZipXarParseChecksumStyle(pszStyle, puHashFunction);
522 if (RT_FAILURE(rc))
523 return rc;
524
525 if (*puHashFunction == XAR_HASH_NONE)
526 return VINF_SUCCESS;
527
528 /* The digest. */
529 const char *pszDigest = pChecksumElem->getValue();
530 if (!pszDigest)
531 return VERR_XAR_BAD_CHECKSUM_ELEMENT;
532
533 switch (*puHashFunction)
534 {
535 case XAR_HASH_SHA1:
536 rc = RTSha1FromString(pszDigest, pDigest->abSha1);
537 break;
538 case XAR_HASH_MD5:
539 rc = RTMd5FromString(pszDigest, pDigest->abMd5);
540 break;
541 default:
542 rc = VERR_INTERNAL_ERROR_2;
543 }
544 return rc;
545}
546
547
548/**
549 * Gets all the attributes of the primary data stream.
550 *
551 * @returns IPRT status code.
552 * @param pFileElem The file element, we'll be parsing the 'data'
553 * sub element of this.
554 * @param pDataAttr Where to return the attributes.
555 */
556static int rtZipXarGetDataStreamAttributes(xml::ElementNode const *pFileElem, PRTZIPXARDATASTREAM pDataAttr)
557{
558 /*
559 * Get the data element.
560 */
561 xml::ElementNode const *pDataElem = pFileElem->findChildElement("data");
562 if (!pDataElem)
563 return VERR_XAR_MISSING_DATA_ELEMENT;
564
565 /*
566 * Checksums.
567 */
568 int rc = rtZipXarParseChecksumElem(pDataElem, "extracted-checksum",
569 &pDataAttr->uHashFunExtracted, &pDataAttr->DigestExtracted);
570 if (RT_FAILURE(rc))
571 return rc;
572 rc = rtZipXarParseChecksumElem(pDataElem, "archived-checksum",
573 &pDataAttr->uHashFunArchived, &pDataAttr->DigestArchived);
574 if (RT_FAILURE(rc))
575 return rc;
576
577 /*
578 * The encoding.
579 */
580 const char *pszEncoding = pDataElem->findChildElementAttributeValueP("encoding", "style");
581 if (!pszEncoding)
582 return VERR_XAR_NO_ENCODING;
583 if (!strcmp(pszEncoding, "application/octet-stream"))
584 pDataAttr->enmEncoding = RTZIPXARENCODING_STORE;
585 else if (!strcmp(pszEncoding, "application/x-gzip"))
586 pDataAttr->enmEncoding = RTZIPXARENCODING_GZIP;
587 else
588 pDataAttr->enmEncoding = RTZIPXARENCODING_UNSUPPORTED;
589
590 /*
591 * The data offset and the compressed and uncompressed sizes.
592 */
593 rc = rtZipXarGetOffsetSizeLengthFromElem(pDataElem, &pDataAttr->offData,
594 &pDataAttr->cbDataExtracted, &pDataAttr->cbDataArchived);
595 if (RT_FAILURE(rc))
596 return rc;
597
598 /* No zero padding or other alignment crap, please. */
599 if ( pDataAttr->enmEncoding == RTZIPXARENCODING_STORE
600 && pDataAttr->cbDataExtracted != pDataAttr->cbDataArchived)
601 return VERR_XAR_ARCHIVED_AND_EXTRACTED_SIZES_MISMATCH;
602
603 return VINF_SUCCESS;
604}
605
606
607/**
608 * Parses a timestamp.
609 *
610 * We consider all timestamps optional, and will only fail (return @c false) on
611 * parse errors. If the specified element isn't found, we'll return epoc time.
612 *
613 * @returns boolean success indicator.
614 * @param pParent The parent element (typically 'file').
615 * @param pszChild The name of the child element.
616 * @param pTimeSpec Where to return the timespec on success.
617 */
618static bool rtZipXarParseTimestamp(const xml::ElementNode *pParent, const char *pszChild, PRTTIMESPEC pTimeSpec)
619{
620 const char *pszValue = pParent->findChildElementValueP(pszChild);
621 if (pszValue)
622 {
623 if (RTTimeSpecFromString(pTimeSpec, pszValue))
624 return true;
625 return false;
626 }
627 RTTimeSpecSetNano(pTimeSpec, 0);
628 return true;
629}
630
631
632/**
633 * Gets the next file element in the TOC.
634 *
635 * @returns Pointer to the next file, NULL if we've reached the end.
636 * @param pCurFile The current element.
637 * @param pcCurDepth Depth gauge we update when decending and
638 * acending thru the tree.
639 */
640static xml::ElementNode const *rtZipXarGetNextFileElement(xml::ElementNode const *pCurFile, uint32_t *pcCurDepth)
641{
642 /*
643 * Consider children first.
644 */
645 xml::ElementNode const *pChild = pCurFile->findChildElement("file");
646 if (pChild)
647 {
648 *pcCurDepth += 1;
649 return pChild;
650 }
651
652 /*
653 * Siblings and ancestor siblings.
654 */
655 for (;;)
656 {
657 xml::ElementNode const *pSibling = pCurFile->findNextSibilingElement("file");
658 if (pSibling != NULL)
659 return pSibling;
660
661 if (*pcCurDepth == 0)
662 break;
663 *pcCurDepth -= 1;
664 pCurFile = static_cast<const xml::ElementNode *>(pCurFile->getParent());
665 AssertBreak(pCurFile);
666 Assert(pCurFile->nameEquals("file"));
667 }
668
669 return NULL;
670}
671
672
673
674/*
675 *
676 * T h e V F S F i l e s y s t e m S t r e a m B i t s.
677 * T h e V F S F i l e s y s t e m S t r e a m B i t s.
678 * T h e V F S F i l e s y s t e m S t r e a m B i t s.
679 *
680 */
681
682
683/**
684 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
685 */
686static DECLCALLBACK(int) rtZipXarFssBaseObj_Close(void *pvThis)
687{
688 PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
689
690 /* Currently there is nothing we really have to do here. */
691 NOREF(pThis);
692
693 return VINF_SUCCESS;
694}
695
696
697/**
698 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
699 */
700static DECLCALLBACK(int) rtZipXarFssBaseObj_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
701{
702 PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
703
704 /*
705 * Get the common data.
706 */
707
708 /* Sizes. */
709 if (pThis->fModeType == RTFS_TYPE_FILE)
710 {
711 PRTZIPXARIOSTREAM pThisIos = RT_FROM_MEMBER(pThis, RTZIPXARIOSTREAM, BaseObj);
712 pObjInfo->cbObject = pThisIos->DataAttr.cbDataArchived; /* Modified by decomp ios. */
713 pObjInfo->cbAllocated = pThisIos->DataAttr.cbDataArchived;
714 }
715 else
716 {
717 pObjInfo->cbObject = 0;
718 pObjInfo->cbAllocated = 0;
719 }
720
721 /* The file mode. */
722 if (RT_UNLIKELY(!pThis->pFileElem->getChildElementValueDefP("mode", 0755, &pObjInfo->Attr.fMode)))
723 return VERR_XAR_BAD_FILE_MODE;
724 if (pObjInfo->Attr.fMode & RTFS_TYPE_MASK)
725 return VERR_XAR_BAD_FILE_MODE;
726 pObjInfo->Attr.fMode &= RTFS_UNIX_MASK & ~RTFS_TYPE_MASK;
727 pObjInfo->Attr.fMode |= pThis->fModeType;
728
729 /* File times. */
730 if (RT_UNLIKELY(!rtZipXarParseTimestamp(pThis->pFileElem, "atime", &pObjInfo->AccessTime)))
731 return VERR_XAR_BAD_FILE_TIMESTAMP;
732 if (RT_UNLIKELY(!rtZipXarParseTimestamp(pThis->pFileElem, "ctime", &pObjInfo->ChangeTime)))
733 return VERR_XAR_BAD_FILE_TIMESTAMP;
734 if (RT_UNLIKELY(!rtZipXarParseTimestamp(pThis->pFileElem, "mtime", &pObjInfo->ModificationTime)))
735 return VERR_XAR_BAD_FILE_TIMESTAMP;
736 pObjInfo->BirthTime = RTTimeSpecGetNano(&pObjInfo->AccessTime) <= RTTimeSpecGetNano(&pObjInfo->ChangeTime)
737 ? pObjInfo->AccessTime : pObjInfo->ChangeTime;
738 if (RTTimeSpecGetNano(&pObjInfo->BirthTime) > RTTimeSpecGetNano(&pObjInfo->ModificationTime))
739 pObjInfo->BirthTime = pObjInfo->ModificationTime;
740
741 /*
742 * Copy the desired data.
743 */
744 switch (enmAddAttr)
745 {
746 case RTFSOBJATTRADD_NOTHING:
747 case RTFSOBJATTRADD_UNIX:
748 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
749 if (RT_UNLIKELY(!pThis->pFileElem->getChildElementValueDefP("uid", 0, &pObjInfo->Attr.u.Unix.uid)))
750 return VERR_XAR_BAD_FILE_UID;
751 if (RT_UNLIKELY(!pThis->pFileElem->getChildElementValueDefP("gid", 0, &pObjInfo->Attr.u.Unix.gid)))
752 return VERR_XAR_BAD_FILE_GID;
753 if (RT_UNLIKELY(!pThis->pFileElem->getChildElementValueDefP("deviceno", 0, &pObjInfo->Attr.u.Unix.INodeIdDevice)))
754 return VERR_XAR_BAD_FILE_DEVICE_NO;
755 if (RT_UNLIKELY(!pThis->pFileElem->getChildElementValueDefP("inode", 0, &pObjInfo->Attr.u.Unix.INodeId)))
756 return VERR_XAR_BAD_FILE_INODE;
757 pObjInfo->Attr.u.Unix.cHardlinks = 1;
758 pObjInfo->Attr.u.Unix.fFlags = 0;
759 pObjInfo->Attr.u.Unix.GenerationId = 0;
760 pObjInfo->Attr.u.Unix.Device = 0;
761 break;
762
763 case RTFSOBJATTRADD_UNIX_OWNER:
764 {
765 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
766 if (RT_UNLIKELY(!pThis->pFileElem->getChildElementValueDefP("uid", 0, &pObjInfo->Attr.u.Unix.uid)))
767 return VERR_XAR_BAD_FILE_UID;
768 const char *pszUser = pThis->pFileElem->findChildElementValueP("user");
769 if (pszUser)
770 RTStrCopy(pObjInfo->Attr.u.UnixOwner.szName, sizeof(pObjInfo->Attr.u.UnixOwner.szName), pszUser);
771 else
772 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
773 break;
774 }
775
776 case RTFSOBJATTRADD_UNIX_GROUP:
777 {
778 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
779 if (RT_UNLIKELY(!pThis->pFileElem->getChildElementValueDefP("gid", 0, &pObjInfo->Attr.u.Unix.gid)))
780 return VERR_XAR_BAD_FILE_GID;
781 const char *pszGroup = pThis->pFileElem->findChildElementValueP("group");
782 if (pszGroup)
783 RTStrCopy(pObjInfo->Attr.u.UnixGroup.szName, sizeof(pObjInfo->Attr.u.UnixGroup.szName), pszGroup);
784 else
785 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
786 break;
787 }
788
789 case RTFSOBJATTRADD_EASIZE:
790 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
791 RT_ZERO(pObjInfo->Attr.u);
792 break;
793
794 default:
795 return VERR_NOT_SUPPORTED;
796 }
797
798 return VINF_SUCCESS;
799}
800
801
802/**
803 * Xar filesystem base object operations.
804 */
805static const RTVFSOBJOPS g_rtZipXarFssBaseObjOps =
806{
807 RTVFSOBJOPS_VERSION,
808 RTVFSOBJTYPE_BASE,
809 "XarFsStream::Obj",
810 rtZipXarFssBaseObj_Close,
811 rtZipXarFssBaseObj_QueryInfo,
812 RTVFSOBJOPS_VERSION
813};
814
815
816/**
817 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
818 */
819static DECLCALLBACK(int) rtZipXarFssIos_Close(void *pvThis)
820{
821 PRTZIPXARIOSTREAM pThis = (PRTZIPXARIOSTREAM)pvThis;
822
823 RTVfsIoStrmRelease(pThis->hVfsIos);
824 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
825
826 return rtZipXarFssBaseObj_Close(&pThis->BaseObj);
827}
828
829
830/**
831 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
832 */
833static DECLCALLBACK(int) rtZipXarFssIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
834{
835 PRTZIPXARIOSTREAM pThis = (PRTZIPXARIOSTREAM)pvThis;
836 return rtZipXarFssBaseObj_QueryInfo(&pThis->BaseObj, pObjInfo, enmAddAttr);
837}
838
839
840/**
841 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
842 */
843static DECLCALLBACK(int) rtZipXarFssIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
844{
845 PRTZIPXARIOSTREAM pThis = (PRTZIPXARIOSTREAM)pvThis;
846 AssertReturn(off >= -1, VERR_INVALID_PARAMETER);
847 AssertReturn(pSgBuf->cSegs == 1, VERR_INVALID_PARAMETER);
848
849 /*
850 * Fend of reads beyond the end of the stream here. If
851 */
852 if (off == -1)
853 off = pThis->offCurPos;
854 if (off < 0 || off > pThis->DataAttr.cbDataArchived)
855 return VERR_EOF;
856 if (pThis->fEndOfStream)
857 {
858 if (off >= pThis->DataAttr.cbDataArchived)
859 return pcbRead ? VINF_EOF : VERR_EOF;
860 if (!pThis->fSeekable)
861 return VERR_SEEK_ON_DEVICE;
862 pThis->fEndOfStream = false;
863 }
864
865 size_t cbToRead = pSgBuf->paSegs[0].cbSeg;
866 uint64_t cbLeft = pThis->DataAttr.cbDataArchived - off;
867 if (cbToRead > cbLeft)
868 {
869 if (!pcbRead)
870 return VERR_EOF;
871 cbToRead = (size_t)cbLeft;
872 }
873
874 /*
875 * Do the reading.
876 */
877 size_t cbReadStack = 0;
878 if (!pcbRead)
879 pcbRead = &cbReadStack;
880 int rc = RTVfsIoStrmReadAt(pThis->hVfsIos, off + pThis->DataAttr.offData, pSgBuf->paSegs[0].pvSeg,
881 cbToRead, fBlocking, pcbRead);
882
883 /* Feed the hashes. */
884 size_t cbActuallyRead = *pcbRead;
885 if (pThis->uHashState == RTZIPXAR_HASH_PENDING)
886 {
887 if (pThis->offCurPos == pThis->cbDigested)
888 {
889 rtZipXarHashUpdate(&pThis->CtxArchived, pThis->DataAttr.uHashFunArchived, pSgBuf->paSegs[0].pvSeg, cbActuallyRead);
890 rtZipXarHashUpdate(&pThis->CtxExtracted, pThis->DataAttr.uHashFunExtracted, pSgBuf->paSegs[0].pvSeg, cbActuallyRead);
891 pThis->cbDigested += cbActuallyRead;
892 }
893 else if ( pThis->cbDigested > pThis->offCurPos
894 && pThis->cbDigested < (RTFOFF)(pThis->offCurPos + cbActuallyRead))
895 {
896 size_t offHash = pThis->cbDigested - pThis->offCurPos;
897 void const *pvHash = (uint8_t const *)pSgBuf->paSegs[0].pvSeg + offHash;
898 size_t cbHash = cbActuallyRead - offHash;
899 rtZipXarHashUpdate(&pThis->CtxArchived, pThis->DataAttr.uHashFunArchived, pvHash, cbHash);
900 rtZipXarHashUpdate(&pThis->CtxExtracted, pThis->DataAttr.uHashFunExtracted, pvHash, cbHash);
901 pThis->cbDigested += cbHash;
902 }
903 }
904
905 /* Update the file position. */
906 pThis->offCurPos += cbActuallyRead;
907
908 /*
909 * Check for end of stream, also check the hash.
910 */
911 if (pThis->offCurPos >= pThis->DataAttr.cbDataArchived)
912 {
913 Assert(pThis->offCurPos == pThis->DataAttr.cbDataArchived);
914 pThis->fEndOfStream = true;
915
916 /* Check hash. */
917 if ( pThis->uHashState == RTZIPXAR_HASH_PENDING
918 && pThis->cbDigested == pThis->DataAttr.cbDataArchived)
919 {
920 RTZIPXARHASHDIGEST Digest;
921 rtZipXarHashFinal(&pThis->CtxArchived, pThis->DataAttr.uHashFunArchived, &Digest);
922 if (rtZipXarHashIsEqual(pThis->DataAttr.uHashFunArchived, &Digest, &pThis->DataAttr.DigestArchived))
923 {
924 rtZipXarHashFinal(&pThis->CtxExtracted, pThis->DataAttr.uHashFunExtracted, &Digest);
925 if (rtZipXarHashIsEqual(pThis->DataAttr.uHashFunExtracted, &Digest, &pThis->DataAttr.DigestExtracted))
926 pThis->uHashState = RTZIPXAR_HASH_OK;
927 else
928 {
929 pThis->uHashState = RTZIPXAR_HASH_FAILED_EXTRACTED;
930 rc = VERR_XAR_EXTRACTED_HASH_MISMATCH;
931 }
932 }
933 else
934 {
935 pThis->uHashState = RTZIPXAR_HASH_FAILED_ARCHIVED;
936 rc = VERR_XAR_ARCHIVED_HASH_MISMATCH;
937 }
938 }
939 else if (pThis->uHashState == RTZIPXAR_HASH_FAILED_ARCHIVED)
940 rc = VERR_XAR_ARCHIVED_HASH_MISMATCH;
941 else if (pThis->uHashState == RTZIPXAR_HASH_FAILED_EXTRACTED)
942 rc = VERR_XAR_EXTRACTED_HASH_MISMATCH;
943 }
944
945 return rc;
946}
947
948
949/**
950 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
951 */
952static DECLCALLBACK(int) rtZipXarFssIos_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
953{
954 /* Cannot write to a read-only I/O stream. */
955 NOREF(pvThis); NOREF(off); NOREF(pSgBuf); NOREF(fBlocking); NOREF(pcbWritten);
956 return VERR_ACCESS_DENIED;
957}
958
959
960/**
961 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
962 */
963static DECLCALLBACK(int) rtZipXarFssIos_Flush(void *pvThis)
964{
965 /* It's a read only stream, nothing dirty to flush. */
966 NOREF(pvThis);
967 return VINF_SUCCESS;
968}
969
970
971/**
972 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
973 */
974static DECLCALLBACK(int) rtZipXarFssIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
975 uint32_t *pfRetEvents)
976{
977 PRTZIPXARIOSTREAM pThis = (PRTZIPXARIOSTREAM)pvThis;
978
979 /* When we've reached the end, read will be set to indicate it. */
980 if ( (fEvents & RTPOLL_EVT_READ)
981 && pThis->fEndOfStream)
982 {
983 int rc = RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, 0, fIntr, pfRetEvents);
984 if (RT_SUCCESS(rc))
985 *pfRetEvents |= RTPOLL_EVT_READ;
986 else
987 *pfRetEvents = RTPOLL_EVT_READ;
988 return VINF_SUCCESS;
989 }
990
991 return RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents);
992}
993
994
995/**
996 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
997 */
998static DECLCALLBACK(int) rtZipXarFssIos_Tell(void *pvThis, PRTFOFF poffActual)
999{
1000 PRTZIPXARIOSTREAM pThis = (PRTZIPXARIOSTREAM)pvThis;
1001 *poffActual = pThis->offCurPos;
1002 return VINF_SUCCESS;
1003}
1004
1005
1006/**
1007 * Xar I/O stream operations.
1008 */
1009static const RTVFSIOSTREAMOPS g_rtZipXarFssIosOps =
1010{
1011 { /* Obj */
1012 RTVFSOBJOPS_VERSION,
1013 RTVFSOBJTYPE_IO_STREAM,
1014 "XarFsStream::IoStream",
1015 rtZipXarFssIos_Close,
1016 rtZipXarFssIos_QueryInfo,
1017 RTVFSOBJOPS_VERSION
1018 },
1019 RTVFSIOSTREAMOPS_VERSION,
1020 0,
1021 rtZipXarFssIos_Read,
1022 rtZipXarFssIos_Write,
1023 rtZipXarFssIos_Flush,
1024 rtZipXarFssIos_PollOne,
1025 rtZipXarFssIos_Tell,
1026 NULL /*Skip*/,
1027 NULL /*ZeroFill*/,
1028 RTVFSIOSTREAMOPS_VERSION
1029};
1030
1031
1032/**
1033 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
1034 */
1035static DECLCALLBACK(int) rtZipXarFssFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
1036{
1037 NOREF(pvThis);
1038 NOREF(fMode);
1039 NOREF(fMask);
1040 return VERR_NOT_SUPPORTED;
1041}
1042
1043
1044/**
1045 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
1046 */
1047static DECLCALLBACK(int) rtZipXarFssFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1048 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1049{
1050 NOREF(pvThis);
1051 NOREF(pAccessTime);
1052 NOREF(pModificationTime);
1053 NOREF(pChangeTime);
1054 NOREF(pBirthTime);
1055 return VERR_NOT_SUPPORTED;
1056}
1057
1058
1059/**
1060 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
1061 */
1062static DECLCALLBACK(int) rtZipXarFssFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
1063{
1064 NOREF(pvThis);
1065 NOREF(uid);
1066 NOREF(gid);
1067 return VERR_NOT_SUPPORTED;
1068}
1069
1070
1071/**
1072 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
1073 */
1074static DECLCALLBACK(int) rtZipXarFssFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
1075{
1076 PRTZIPXARFILE pThis = (PRTZIPXARFILE)pvThis;
1077
1078 /* Recalculate the request to RTFILE_SEEK_BEGIN. */
1079 switch (uMethod)
1080 {
1081 case RTFILE_SEEK_BEGIN:
1082 break;
1083 case RTFILE_SEEK_CURRENT:
1084 offSeek += pThis->Ios.offCurPos;
1085 break;
1086 case RTFILE_SEEK_END:
1087 offSeek = pThis->Ios.DataAttr.cbDataArchived + offSeek;
1088 break;
1089 default:
1090 AssertFailedReturn(VERR_INVALID_PARAMETER);
1091 }
1092
1093 /* Do limit checks. */
1094 if (offSeek < 0)
1095 offSeek = 0;
1096 else if (offSeek > pThis->Ios.DataAttr.cbDataArchived)
1097 offSeek = pThis->Ios.DataAttr.cbDataArchived;
1098
1099 /* Apply and return. */
1100 pThis->Ios.fEndOfStream = (offSeek >= pThis->Ios.DataAttr.cbDataArchived);
1101 pThis->Ios.offCurPos = offSeek;
1102 if (poffActual)
1103 *poffActual = offSeek;
1104
1105 return VINF_SUCCESS;
1106}
1107
1108
1109/**
1110 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
1111 */
1112static DECLCALLBACK(int) rtZipXarFssFile_QuerySize(void *pvThis, uint64_t *pcbFile)
1113{
1114 PRTZIPXARFILE pThis = (PRTZIPXARFILE)pvThis;
1115 *pcbFile = pThis->Ios.DataAttr.cbDataArchived;
1116 return VINF_SUCCESS;
1117}
1118
1119
1120/**
1121 * Xar file operations.
1122 */
1123static const RTVFSFILEOPS g_rtZipXarFssFileOps =
1124{
1125 { /* I/O stream */
1126 { /* Obj */
1127 RTVFSOBJOPS_VERSION,
1128 RTVFSOBJTYPE_FILE,
1129 "XarFsStream::File",
1130 rtZipXarFssIos_Close,
1131 rtZipXarFssIos_QueryInfo,
1132 RTVFSOBJOPS_VERSION
1133 },
1134 RTVFSIOSTREAMOPS_VERSION,
1135 RTVFSIOSTREAMOPS_FEAT_NO_SG,
1136 rtZipXarFssIos_Read,
1137 rtZipXarFssIos_Write,
1138 rtZipXarFssIos_Flush,
1139 rtZipXarFssIos_PollOne,
1140 rtZipXarFssIos_Tell,
1141 NULL /*Skip*/,
1142 NULL /*ZeroFill*/,
1143 RTVFSIOSTREAMOPS_VERSION
1144 },
1145 RTVFSFILEOPS_VERSION,
1146 0,
1147 { /* ObjSet */
1148 RTVFSOBJSETOPS_VERSION,
1149 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
1150 rtZipXarFssFile_SetMode,
1151 rtZipXarFssFile_SetTimes,
1152 rtZipXarFssFile_SetOwner,
1153 RTVFSOBJSETOPS_VERSION
1154 },
1155 rtZipXarFssFile_Seek,
1156 rtZipXarFssFile_QuerySize,
1157 RTVFSFILEOPS_VERSION,
1158};
1159
1160
1161
1162
1163/**
1164 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1165 */
1166static DECLCALLBACK(int) rtZipXarFssDecompIos_Close(void *pvThis)
1167{
1168 PRTZIPXARDECOMPIOS pThis = (PRTZIPXARDECOMPIOS)pvThis;
1169
1170 RTVfsIoStrmRelease(pThis->hVfsIosDecompressor);
1171 pThis->hVfsIosDecompressor = NIL_RTVFSIOSTREAM;
1172
1173 int rc = RTVfsIoStrmRelease(pThis->hVfsIosRaw);
1174 pThis->hVfsIosRaw = NIL_RTVFSIOSTREAM;
1175 pThis->pIosRaw = NULL;
1176
1177 return rc;
1178}
1179
1180
1181/**
1182 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1183 */
1184static DECLCALLBACK(int) rtZipXarFssDecompIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1185{
1186 PRTZIPXARDECOMPIOS pThis = (PRTZIPXARDECOMPIOS)pvThis;
1187
1188 int rc = rtZipXarFssBaseObj_QueryInfo(&pThis->pIosRaw->BaseObj, pObjInfo, enmAddAttr);
1189 pObjInfo->cbObject = pThis->pIosRaw->DataAttr.cbDataExtracted;
1190 return rc;
1191}
1192
1193
1194/**
1195 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
1196 */
1197static DECLCALLBACK(int) rtZipXarFssDecompIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
1198{
1199 PRTZIPXARDECOMPIOS pThis = (PRTZIPXARDECOMPIOS)pvThis;
1200 AssertReturn(pSgBuf->cSegs == 1, VERR_INVALID_PARAMETER);
1201
1202 /*
1203 * Enforce the cbDataExtracted limit.
1204 */
1205 if (pThis->offCurPos > pThis->pIosRaw->DataAttr.cbDataExtracted)
1206 return VERR_XAR_EXTRACTED_SIZE_EXCEEDED;
1207
1208 /*
1209 * Read the data.
1210 *
1211 * ASSUMES the decompressor stream isn't seekable, so we don't have to
1212 * validate off wrt data digest updating.
1213 */
1214 int rc = RTVfsIoStrmReadAt(pThis->hVfsIosDecompressor, off, pSgBuf->paSegs[0].pvSeg, pSgBuf->paSegs[0].cbSeg,
1215 fBlocking, pcbRead);
1216 if (RT_FAILURE(rc))
1217 return rc;
1218
1219 /*
1220 * Hash the data. When reaching the end match against the expected digest.
1221 */
1222 size_t cbActuallyRead = pcbRead ? *pcbRead : pSgBuf->paSegs[0].cbSeg;
1223 pThis->offCurPos += cbActuallyRead;
1224 rtZipXarHashUpdate(&pThis->CtxExtracted, pThis->uHashFunExtracted, pSgBuf->paSegs[0].pvSeg, cbActuallyRead);
1225 if (rc == VINF_EOF)
1226 {
1227 if (pThis->offCurPos == pThis->pIosRaw->DataAttr.cbDataExtracted)
1228 {
1229 if (pThis->uHashState == RTZIPXAR_HASH_PENDING)
1230 {
1231 RTZIPXARHASHDIGEST Digest;
1232 rtZipXarHashFinal(&pThis->CtxExtracted, pThis->uHashFunExtracted, &Digest);
1233 if (rtZipXarHashIsEqual(pThis->uHashFunExtracted, &Digest, &pThis->DigestExtracted))
1234 pThis->uHashState = RTZIPXAR_HASH_OK;
1235 else
1236 {
1237 pThis->uHashState = RTZIPXAR_HASH_FAILED_EXTRACTED;
1238 rc = VERR_XAR_EXTRACTED_HASH_MISMATCH;
1239 }
1240 }
1241 else if (pThis->uHashState != RTZIPXAR_HASH_OK)
1242 rc = VERR_XAR_EXTRACTED_HASH_MISMATCH;
1243 }
1244 else
1245 rc = VERR_XAR_EXTRACTED_SIZE_EXCEEDED;
1246
1247 /* Ensure that the raw stream is also at the end so that both
1248 message digests are checked. */
1249 if (RT_SUCCESS(rc))
1250 {
1251 if ( pThis->pIosRaw->offCurPos < pThis->pIosRaw->DataAttr.cbDataArchived
1252 || pThis->pIosRaw->uHashState == RTZIPXAR_HASH_PENDING)
1253 rc = VERR_XAR_UNUSED_ARCHIVED_DATA;
1254 else if (pThis->pIosRaw->uHashState != RTZIPXAR_HASH_OK)
1255 rc = VERR_XAR_ARCHIVED_HASH_MISMATCH;
1256 }
1257 }
1258
1259 return rc;
1260}
1261
1262
1263/**
1264 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
1265 */
1266static DECLCALLBACK(int) rtZipXarFssDecompIos_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
1267{
1268 /* Cannot write to a read-only I/O stream. */
1269 NOREF(pvThis); NOREF(off); NOREF(pSgBuf); NOREF(fBlocking); NOREF(pcbWritten);
1270 return VERR_ACCESS_DENIED;
1271}
1272
1273
1274/**
1275 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
1276 */
1277static DECLCALLBACK(int) rtZipXarFssDecompIos_Flush(void *pvThis)
1278{
1279 /* It's a read only stream, nothing dirty to flush. */
1280 NOREF(pvThis);
1281 return VINF_SUCCESS;
1282}
1283
1284
1285/**
1286 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
1287 */
1288static DECLCALLBACK(int) rtZipXarFssDecompIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
1289 uint32_t *pfRetEvents)
1290{
1291 PRTZIPXARDECOMPIOS pThis = (PRTZIPXARDECOMPIOS)pvThis;
1292 return RTVfsIoStrmPoll(pThis->hVfsIosDecompressor, fEvents, cMillies, fIntr, pfRetEvents);
1293}
1294
1295
1296/**
1297 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
1298 */
1299static DECLCALLBACK(int) rtZipXarFssDecompIos_Tell(void *pvThis, PRTFOFF poffActual)
1300{
1301 PRTZIPXARDECOMPIOS pThis = (PRTZIPXARDECOMPIOS)pvThis;
1302 *poffActual = pThis->offCurPos;
1303 return VINF_SUCCESS;
1304}
1305
1306
1307/**
1308 * Xar I/O stream operations.
1309 */
1310static const RTVFSIOSTREAMOPS g_rtZipXarFssDecompIosOps =
1311{
1312 { /* Obj */
1313 RTVFSOBJOPS_VERSION,
1314 RTVFSOBJTYPE_IO_STREAM,
1315 "XarFsStream::DecompIoStream",
1316 rtZipXarFssDecompIos_Close,
1317 rtZipXarFssDecompIos_QueryInfo,
1318 RTVFSOBJOPS_VERSION
1319 },
1320 RTVFSIOSTREAMOPS_VERSION,
1321 0,
1322 rtZipXarFssDecompIos_Read,
1323 rtZipXarFssDecompIos_Write,
1324 rtZipXarFssDecompIos_Flush,
1325 rtZipXarFssDecompIos_PollOne,
1326 rtZipXarFssDecompIos_Tell,
1327 NULL /*Skip*/,
1328 NULL /*ZeroFill*/,
1329 RTVFSIOSTREAMOPS_VERSION
1330};
1331
1332
1333
1334
1335/**
1336 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1337 */
1338static DECLCALLBACK(int) rtZipXarFssSym_Close(void *pvThis)
1339{
1340 PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
1341 return rtZipXarFssBaseObj_Close(pThis);
1342}
1343
1344
1345/**
1346 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1347 */
1348static DECLCALLBACK(int) rtZipXarFssSym_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1349{
1350 PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
1351 return rtZipXarFssBaseObj_QueryInfo(pThis, pObjInfo, enmAddAttr);
1352}
1353
1354/**
1355 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
1356 */
1357static DECLCALLBACK(int) rtZipXarFssSym_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
1358{
1359 NOREF(pvThis); NOREF(fMode); NOREF(fMask);
1360 return VERR_ACCESS_DENIED;
1361}
1362
1363
1364/**
1365 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
1366 */
1367static DECLCALLBACK(int) rtZipXarFssSym_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1368 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1369{
1370 NOREF(pvThis); NOREF(pAccessTime); NOREF(pModificationTime); NOREF(pChangeTime); NOREF(pBirthTime);
1371 return VERR_ACCESS_DENIED;
1372}
1373
1374
1375/**
1376 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
1377 */
1378static DECLCALLBACK(int) rtZipXarFssSym_SetOwner(void *pvThis, RTUID uid, RTGID gid)
1379{
1380 NOREF(pvThis); NOREF(uid); NOREF(gid);
1381 return VERR_ACCESS_DENIED;
1382}
1383
1384
1385/**
1386 * @interface_method_impl{RTVFSSYMLINKOPS,pfnRead}
1387 */
1388static DECLCALLBACK(int) rtZipXarFssSym_Read(void *pvThis, char *pszTarget, size_t cbXarget)
1389{
1390 PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
1391#if 0
1392 return RTStrCopy(pszTarget, cbXarget, pThis->pXarReader->szTarget);
1393#else
1394 return VERR_NOT_IMPLEMENTED;
1395#endif
1396}
1397
1398
1399/**
1400 * Xar symbolic (and hardlink) operations.
1401 */
1402static const RTVFSSYMLINKOPS g_rtZipXarFssSymOps =
1403{
1404 { /* Obj */
1405 RTVFSOBJOPS_VERSION,
1406 RTVFSOBJTYPE_SYMLINK,
1407 "XarFsStream::Symlink",
1408 rtZipXarFssSym_Close,
1409 rtZipXarFssSym_QueryInfo,
1410 RTVFSOBJOPS_VERSION
1411 },
1412 RTVFSSYMLINKOPS_VERSION,
1413 0,
1414 { /* ObjSet */
1415 RTVFSOBJSETOPS_VERSION,
1416 RT_OFFSETOF(RTVFSSYMLINKOPS, Obj) - RT_OFFSETOF(RTVFSSYMLINKOPS, ObjSet),
1417 rtZipXarFssSym_SetMode,
1418 rtZipXarFssSym_SetTimes,
1419 rtZipXarFssSym_SetOwner,
1420 RTVFSOBJSETOPS_VERSION
1421 },
1422 rtZipXarFssSym_Read,
1423 RTVFSSYMLINKOPS_VERSION
1424};
1425
1426
1427/**
1428 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1429 */
1430static DECLCALLBACK(int) rtZipXarFss_Close(void *pvThis)
1431{
1432 PRTZIPXARFSSTREAM pThis = (PRTZIPXARFSSTREAM)pvThis;
1433
1434 RTVfsIoStrmRelease(pThis->hVfsIos);
1435 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
1436
1437 RTVfsFileRelease(pThis->hVfsFile);
1438 pThis->hVfsFile = NIL_RTVFSFILE;
1439
1440 return VINF_SUCCESS;
1441}
1442
1443
1444/**
1445 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1446 */
1447static DECLCALLBACK(int) rtZipXarFss_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1448{
1449 PRTZIPXARFSSTREAM pThis = (PRTZIPXARFSSTREAM)pvThis;
1450 /* Take the lazy approach here, with the sideffect of providing some info
1451 that is actually kind of useful. */
1452 return RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
1453}
1454
1455
1456/**
1457 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnNext}
1458 */
1459static DECLCALLBACK(int) rtZipXarFss_Next(void *pvThis, char **ppszName, RTVFSOBJTYPE *penmType, PRTVFSOBJ phVfsObj)
1460{
1461 PRTZIPXARFSSTREAM pThis = (PRTZIPXARFSSTREAM)pvThis;
1462
1463 /*
1464 * Check if we've already reached the end in some way.
1465 */
1466 if (pThis->fEndOfStream)
1467 return VERR_EOF;
1468 if (pThis->rcFatal != VINF_SUCCESS)
1469 return pThis->rcFatal;
1470
1471 /*
1472 * Get the next file element.
1473 */
1474 xml::ElementNode const *pCurFile = pThis->XarReader.pCurFile;
1475 if (pCurFile)
1476 pThis->XarReader.pCurFile = pCurFile = rtZipXarGetNextFileElement(pCurFile, &pThis->XarReader.cCurDepth);
1477 else if (!pThis->fEndOfStream)
1478 {
1479 pThis->XarReader.cCurDepth = 0;
1480 pThis->XarReader.pCurFile = pCurFile = pThis->XarReader.pToc->findChildElement("file");
1481 }
1482 if (!pCurFile)
1483 {
1484 pThis->fEndOfStream = true;
1485 return VERR_EOF;
1486 }
1487
1488 /*
1489 * Retrive the fundamental attributes (elements actually).
1490 */
1491 const char *pszName = pCurFile->findChildElementValueP("name");
1492 const char *pszType = pCurFile->findChildElementValueP("type");
1493 if (RT_UNLIKELY(!pszName || !pszType))
1494 return pThis->rcFatal = VERR_XAR_BAD_FILE_ELEMENT;
1495
1496 /*
1497 * Validate the filename. Being a little too paranoid here, perhaps, wrt
1498 * path separators and escapes...
1499 */
1500 if ( !*pszName
1501 || strchr(pszName, '/')
1502 || strchr(pszName, '\\')
1503 || strchr(pszName, ':')
1504 || !strcmp(pszName, "..") )
1505 return pThis->rcFatal = VERR_XAR_INVALID_FILE_NAME;
1506
1507 /*
1508 * Gather any additional attributes that are essential to the file type,
1509 * then create the VFS object we're going to return.
1510 */
1511 int rc;
1512 RTVFSOBJ hVfsObj;
1513 RTVFSOBJTYPE enmType;
1514 if (!strcmp(pszType, "file"))
1515 {
1516 RTZIPXARDATASTREAM DataAttr;
1517 rc = rtZipXarGetDataStreamAttributes(pCurFile, &DataAttr);
1518 if (RT_FAILURE(rc))
1519 return pThis->rcFatal = rc;
1520 DataAttr.offData += pThis->offZero + pThis->offStart;
1521
1522 if ( pThis->hVfsFile != NIL_RTVFSFILE
1523 && DataAttr.enmEncoding == RTZIPXARENCODING_STORE)
1524 {
1525 /*
1526 * The input is seekable and the XAR file isn't compressed, so we
1527 * can provide a seekable file to the user.
1528 */
1529 RTVFSFILE hVfsFile;
1530 PRTZIPXARFILE pFileData;
1531 rc = RTVfsNewFile(&g_rtZipXarFssFileOps,
1532 sizeof(*pFileData),
1533 RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
1534 NIL_RTVFS,
1535 NIL_RTVFSLOCK,
1536 &hVfsFile,
1537 (void **)&pFileData);
1538 if (RT_FAILURE(rc))
1539 return pThis->rcFatal = rc;
1540
1541 pFileData->Ios.BaseObj.pFileElem = pCurFile;
1542 pFileData->Ios.BaseObj.fModeType = RTFS_TYPE_FILE;
1543 pFileData->Ios.DataAttr = DataAttr;
1544 pFileData->Ios.offCurPos = 0;
1545 pFileData->Ios.fEndOfStream = false;
1546 pFileData->Ios.fSeekable = true;
1547 pFileData->Ios.uHashState = RTZIPXAR_HASH_PENDING;
1548 pFileData->Ios.cbDigested = 0;
1549 rtZipXarHashInit(&pFileData->Ios.CtxArchived, pFileData->Ios.DataAttr.uHashFunArchived);
1550 rtZipXarHashInit(&pFileData->Ios.CtxExtracted, pFileData->Ios.DataAttr.uHashFunExtracted);
1551
1552 pFileData->Ios.hVfsIos = pThis->hVfsIos;
1553 RTVfsIoStrmRetain(pFileData->Ios.hVfsIos);
1554 pFileData->hVfsFile = pThis->hVfsFile;
1555 RTVfsFileRetain(pFileData->hVfsFile);
1556
1557 /* Try avoid double content hashing. */
1558 if (pFileData->Ios.DataAttr.uHashFunArchived == pFileData->Ios.DataAttr.uHashFunExtracted)
1559 pFileData->Ios.DataAttr.uHashFunExtracted = XAR_HASH_NONE;
1560
1561 enmType = RTVFSOBJTYPE_FILE;
1562 hVfsObj = RTVfsObjFromFile(hVfsFile);
1563 RTVfsFileRelease(hVfsFile);
1564 }
1565 else
1566 {
1567 RTVFSIOSTREAM hVfsIosRaw;
1568 PRTZIPXARIOSTREAM pIosData;
1569 rc = RTVfsNewIoStream(&g_rtZipXarFssIosOps,
1570 sizeof(*pIosData),
1571 RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
1572 NIL_RTVFS,
1573 NIL_RTVFSLOCK,
1574 &hVfsIosRaw,
1575 (void **)&pIosData);
1576 if (RT_FAILURE(rc))
1577 return pThis->rcFatal = rc;
1578
1579 pIosData->BaseObj.pFileElem = pCurFile;
1580 pIosData->BaseObj.fModeType = RTFS_TYPE_FILE;
1581 pIosData->DataAttr = DataAttr;
1582 pIosData->offCurPos = 0;
1583 pIosData->fEndOfStream = false;
1584 pIosData->fSeekable = pThis->hVfsFile != NIL_RTVFSFILE;
1585 pIosData->uHashState = RTZIPXAR_HASH_PENDING;
1586 pIosData->cbDigested = 0;
1587 rtZipXarHashInit(&pIosData->CtxArchived, pIosData->DataAttr.uHashFunArchived);
1588 rtZipXarHashInit(&pIosData->CtxExtracted, pIosData->DataAttr.uHashFunExtracted);
1589
1590 pIosData->hVfsIos = pThis->hVfsIos;
1591 RTVfsIoStrmRetain(pThis->hVfsIos);
1592
1593 if ( pIosData->DataAttr.enmEncoding != RTZIPXARENCODING_STORE
1594 && pIosData->DataAttr.enmEncoding != RTZIPXARENCODING_UNSUPPORTED)
1595 {
1596 /*
1597 * We need to set up a decompression chain.
1598 */
1599 RTVFSIOSTREAM hVfsIosDecomp;
1600 PRTZIPXARDECOMPIOS pIosDecompData;
1601 rc = RTVfsNewIoStream(&g_rtZipXarFssDecompIosOps,
1602 sizeof(*pIosDecompData),
1603 RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
1604 NIL_RTVFS,
1605 NIL_RTVFSLOCK,
1606 &hVfsIosDecomp,
1607 (void **)&pIosDecompData);
1608 if (RT_FAILURE(rc))
1609 {
1610 RTVfsIoStrmRelease(hVfsIosRaw);
1611 return pThis->rcFatal = rc;
1612 }
1613
1614 pIosDecompData->hVfsIosDecompressor = NIL_RTVFSIOSTREAM;
1615 pIosDecompData->hVfsIosRaw = hVfsIosRaw;
1616 pIosDecompData->pIosRaw = pIosData;
1617 pIosDecompData->offCurPos = 0;
1618 pIosDecompData->uHashFunExtracted = DataAttr.uHashFunExtracted;
1619 pIosDecompData->uHashState = RTZIPXAR_HASH_PENDING;
1620 rtZipXarHashInit(&pIosDecompData->CtxExtracted, pIosDecompData->uHashFunExtracted);
1621 pIosDecompData->DigestExtracted = DataAttr.DigestExtracted;
1622
1623 /* Tell the raw end to only hash the archived data. */
1624 pIosData->DataAttr.uHashFunExtracted = XAR_HASH_NONE;
1625
1626 /*
1627 * Hook up the decompressor.
1628 */
1629 switch (DataAttr.enmEncoding)
1630 {
1631 case RTZIPXARENCODING_GZIP:
1632 /* Must allow zlib header, all examples I've got seems
1633 to be using it rather than the gzip one. Makes
1634 sense as there is no need to repeat the file name
1635 and the attributes. */
1636 rc = RTZipGzipDecompressIoStream(hVfsIosRaw, RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR,
1637 &pIosDecompData->hVfsIosDecompressor);
1638 break;
1639 default:
1640 rc = VERR_INTERNAL_ERROR_5;
1641 break;
1642 }
1643 if (RT_FAILURE(rc))
1644 {
1645 RTVfsIoStrmRelease(hVfsIosDecomp);
1646 return pThis->rcFatal = rc;
1647 }
1648
1649 /* What to return. */
1650 hVfsObj = RTVfsObjFromIoStream(hVfsIosDecomp);
1651 RTVfsIoStrmRelease(hVfsIosDecomp);
1652 }
1653 else
1654 {
1655 /* Try avoid double content hashing. */
1656 if (pIosData->DataAttr.uHashFunArchived == pIosData->DataAttr.uHashFunExtracted)
1657 pIosData->DataAttr.uHashFunExtracted = XAR_HASH_NONE;
1658
1659 /* What to return. */
1660 hVfsObj = RTVfsObjFromIoStream(hVfsIosRaw);
1661 RTVfsIoStrmRelease(hVfsIosRaw);
1662 }
1663 enmType = RTVFSOBJTYPE_IO_STREAM;
1664 }
1665 }
1666 else if (!strcmp(pszType, "directory"))
1667 {
1668 PRTZIPXARBASEOBJ pBaseObjData;
1669 rc = RTVfsNewBaseObj(&g_rtZipXarFssBaseObjOps,
1670 sizeof(*pBaseObjData),
1671 NIL_RTVFS,
1672 NIL_RTVFSLOCK,
1673 &hVfsObj,
1674 (void **)&pBaseObjData);
1675 if (RT_FAILURE(rc))
1676 return pThis->rcFatal = rc;
1677
1678 pBaseObjData->pFileElem = pCurFile;
1679 pBaseObjData->fModeType = RTFS_TYPE_DIRECTORY;
1680
1681 enmType = RTVFSOBJTYPE_BASE;
1682 }
1683 else if (!strcmp(pszType, "symlink"))
1684 {
1685 RTVFSSYMLINK hVfsSym;
1686 PRTZIPXARBASEOBJ pBaseObjData;
1687 rc = RTVfsNewSymlink(&g_rtZipXarFssSymOps,
1688 sizeof(*pBaseObjData),
1689 NIL_RTVFS,
1690 NIL_RTVFSLOCK,
1691 &hVfsSym,
1692 (void **)&pBaseObjData);
1693 if (RT_FAILURE(rc))
1694 return pThis->rcFatal = rc;
1695
1696 pBaseObjData->pFileElem = pCurFile;
1697 pBaseObjData->fModeType = RTFS_TYPE_SYMLINK;
1698
1699 enmType = RTVFSOBJTYPE_SYMLINK;
1700 hVfsObj = RTVfsObjFromSymlink(hVfsSym);
1701 RTVfsSymlinkRelease(hVfsSym);
1702 }
1703 else
1704 return pThis->rcFatal = VERR_XAR_UNKNOWN_FILE_TYPE;
1705
1706 /*
1707 * Set the return data and we're done.
1708 */
1709 if (ppszName)
1710 {
1711 /* Figure the length. */
1712 size_t const cbCurName = strlen(pszName) + 1;
1713 size_t cbFullName = cbCurName;
1714 const xml::ElementNode *pAncestor = pCurFile;
1715 uint32_t cLeft = pThis->XarReader.cCurDepth;
1716 while (cLeft-- > 0)
1717 {
1718 pAncestor = (const xml::ElementNode *)pAncestor->getParent(); Assert(pAncestor);
1719 const char *pszAncestorName = pAncestor->findChildElementValueP("name"); Assert(pszAncestorName);
1720 cbFullName += strlen(pszAncestorName) + 1;
1721 }
1722
1723 /* Allocate a buffer. */
1724 char *psz = *ppszName = RTStrAlloc(cbFullName);
1725 if (!psz)
1726 {
1727 RTVfsObjRelease(hVfsObj);
1728 return VERR_NO_STR_MEMORY;
1729 }
1730
1731 /* Construct it, from the end. */
1732 psz += cbFullName;
1733 psz -= cbCurName;
1734 memcpy(psz, pszName, cbCurName);
1735
1736 pAncestor = pCurFile;
1737 cLeft = pThis->XarReader.cCurDepth;
1738 while (cLeft-- > 0)
1739 {
1740 pAncestor = (const xml::ElementNode *)pAncestor->getParent(); Assert(pAncestor);
1741 const char *pszAncestorName = pAncestor->findChildElementValueP("name"); Assert(pszAncestorName);
1742 *--psz = '/';
1743 size_t cchAncestorName = strlen(pszAncestorName);
1744 psz -= cchAncestorName;
1745 memcpy(psz, pszAncestorName, cchAncestorName);
1746 }
1747 Assert(*ppszName == psz);
1748 }
1749
1750 if (phVfsObj)
1751 {
1752 RTVfsObjRetain(hVfsObj);
1753 *phVfsObj = hVfsObj;
1754 }
1755
1756 if (penmType)
1757 *penmType = enmType;
1758
1759 return VINF_SUCCESS;
1760}
1761
1762
1763
1764/**
1765 * Xar filesystem stream operations.
1766 */
1767static const RTVFSFSSTREAMOPS rtZipXarFssOps =
1768{
1769 { /* Obj */
1770 RTVFSOBJOPS_VERSION,
1771 RTVFSOBJTYPE_FS_STREAM,
1772 "XarFsStream",
1773 rtZipXarFss_Close,
1774 rtZipXarFss_QueryInfo,
1775 RTVFSOBJOPS_VERSION
1776 },
1777 RTVFSFSSTREAMOPS_VERSION,
1778 0,
1779 rtZipXarFss_Next,
1780 RTVFSFSSTREAMOPS_VERSION
1781};
1782
1783
1784
1785/**
1786 * TOC validation part 2.
1787 *
1788 * Will advance the input stream past the TOC hash and signature data.
1789 *
1790 * @returns IPRT status code.
1791 * @param pThis The FS stream instance being created.
1792 * @param pXarHdr The XAR header.
1793 * @param pTocDigest The TOC input data digest.
1794 */
1795static int rtZipXarValidateTocPart2(PRTZIPXARFSSTREAM pThis, PCXARHEADER pXarHdr, PCRTZIPXARHASHDIGEST pTocDigest)
1796{
1797 int rc;
1798
1799 /*
1800 * Check that the hash function in the TOC matches the one in the XAR header.
1801 */
1802 const xml::ElementNode *pChecksumElem = pThis->XarReader.pToc->findChildElement("checksum");
1803 if (pChecksumElem)
1804 {
1805 const xml::AttributeNode *pAttr = pChecksumElem->findAttribute("style");
1806 if (!pAttr)
1807 return VERR_XAR_BAD_CHECKSUM_ELEMENT;
1808
1809 const char *pszStyle = pAttr->getValue();
1810 if (!pszStyle)
1811 return VERR_XAR_BAD_CHECKSUM_ELEMENT;
1812
1813 uint8_t uHashFunction;
1814 rc = rtZipXarParseChecksumStyle(pszStyle, &uHashFunction);
1815 if (RT_FAILURE(rc))
1816 return rc;
1817 if (uHashFunction != pThis->uHashFunction)
1818 return VERR_XAR_HASH_FUNCTION_MISMATCH;
1819
1820 /*
1821 * Verify the checksum if we got one.
1822 */
1823 if (pThis->uHashFunction != XAR_HASH_NONE)
1824 {
1825 RTFOFF offChecksum;
1826 RTFOFF cbChecksum;
1827 rc = rtZipXarGetOffsetSizeLengthFromElem(pChecksumElem, &offChecksum, &cbChecksum, NULL);
1828 if (RT_FAILURE(rc))
1829 return rc;
1830 if (cbChecksum != (RTFOFF)pThis->cbHashDigest)
1831 return VERR_XAR_BAD_DIGEST_LENGTH;
1832 if (offChecksum != 0 && pThis->hVfsFile == NIL_RTVFSFILE)
1833 return VERR_XAR_NOT_STREAMBLE_ELEMENT_ORDER;
1834
1835 RTZIPXARHASHDIGEST StoredDigest;
1836 rc = RTVfsIoStrmReadAt(pThis->hVfsIos, pThis->offZero + offChecksum, &StoredDigest, pThis->cbHashDigest,
1837 true /*fBlocking*/, NULL /*pcbRead*/);
1838 if (RT_FAILURE(rc))
1839 return rc;
1840 if (memcmp(&StoredDigest, pTocDigest, pThis->cbHashDigest))
1841 return VERR_XAR_TOC_DIGEST_MISMATCH;
1842 }
1843 }
1844 else if (pThis->uHashFunction != XAR_HASH_NONE)
1845 return VERR_XAR_BAD_CHECKSUM_ELEMENT;
1846
1847 /*
1848 * Check the signature, if we got one.
1849 */
1850 /** @todo signing. */
1851
1852 return VINF_SUCCESS;
1853}
1854
1855
1856/**
1857 * Reads and validates the table of content.
1858 *
1859 * @returns IPRT status code.
1860 * @param hVfsIosIn The input stream.
1861 * @param pXarHdr The XAR header.
1862 * @param pDoc The TOC XML document.
1863 * @param ppTocElem Where to return the pointer to the TOC element on
1864 * success.
1865 * @param pTocDigest Where to return the TOC digest on success.
1866 */
1867static int rtZipXarReadAndValidateToc(RTVFSIOSTREAM hVfsIosIn, PCXARHEADER pXarHdr,
1868 xml::Document *pDoc, xml::ElementNode const **ppTocElem, PRTZIPXARHASHDIGEST pTocDigest)
1869{
1870 /*
1871 * Decompress it, calculating the hash while doing so.
1872 */
1873 char *pszOutput = (char *)RTMemTmpAlloc(pXarHdr->cbTocUncompressed + 1);
1874 if (!pszOutput)
1875 return VERR_NO_TMP_MEMORY;
1876 int rc = VERR_NO_TMP_MEMORY;
1877 void *pvInput = RTMemTmpAlloc(pXarHdr->cbTocCompressed);
1878 if (pvInput)
1879 {
1880 rc = RTVfsIoStrmRead(hVfsIosIn, pvInput, pXarHdr->cbTocCompressed, true /*fBlocking*/, NULL);
1881 if (RT_SUCCESS(rc))
1882 {
1883 rtZipXarCalcHash(pXarHdr->uHashFunction, pvInput, pXarHdr->cbTocCompressed, pTocDigest);
1884
1885 size_t cbActual;
1886 rc = RTZipBlockDecompress(RTZIPTYPE_ZLIB, 0 /*fFlags*/,
1887 pvInput, pXarHdr->cbTocCompressed, NULL,
1888 pszOutput, pXarHdr->cbTocUncompressed, &cbActual);
1889 if (RT_SUCCESS(rc) && cbActual != pXarHdr->cbTocUncompressed)
1890 rc = VERR_XAR_TOC_UNCOMP_SIZE_MISMATCH;
1891 }
1892 RTMemTmpFree(pvInput);
1893 }
1894 if (RT_SUCCESS(rc))
1895 {
1896 pszOutput[pXarHdr->cbTocUncompressed] = '\0';
1897
1898 /*
1899 * Parse the TOC (XML document) and do some basic validations.
1900 */
1901 size_t cchToc = strlen(pszOutput);
1902 if ( cchToc == pXarHdr->cbTocUncompressed
1903 || cchToc + 1 == pXarHdr->cbTocUncompressed)
1904 {
1905 rc = RTStrValidateEncoding(pszOutput);
1906 if (RT_SUCCESS(rc))
1907 {
1908 xml::XmlMemParser Parser;
1909 try
1910 {
1911 Parser.read(pszOutput, cchToc, RTCString("xar-toc.xml"), *pDoc);
1912 }
1913 catch (xml::XmlError Err)
1914 {
1915 rc = VERR_XAR_TOC_XML_PARSE_ERROR;
1916 }
1917 catch (...)
1918 {
1919 rc = VERR_NO_MEMORY;
1920 }
1921 if (RT_SUCCESS(rc))
1922 {
1923 xml::ElementNode const *pRootElem = pDoc->getRootElement();
1924 xml::ElementNode const *pTocElem = NULL;
1925 if (pRootElem && pRootElem->nameEquals("xar"))
1926 pTocElem = pRootElem ? pRootElem->findChildElement("toc") : NULL;
1927 if (pTocElem)
1928 {
1929#ifndef USE_STD_LIST_FOR_CHILDREN
1930 Assert(pRootElem->getParent() == NULL);
1931 Assert(pTocElem->getParent() == pRootElem);
1932 if ( !pTocElem->getNextSibiling()
1933 && !pTocElem->getPrevSibiling())
1934#endif
1935 {
1936 /*
1937 * Further parsing and validation is done after the
1938 * caller has created an file system stream instance.
1939 */
1940 *ppTocElem = pTocElem;
1941
1942 RTMemTmpFree(pszOutput);
1943 return VINF_SUCCESS;
1944 }
1945
1946 rc = VERR_XML_TOC_ELEMENT_HAS_SIBLINGS;
1947 }
1948 else
1949 rc = VERR_XML_TOC_ELEMENT_MISSING;
1950 }
1951 }
1952 else
1953 rc = VERR_XAR_TOC_UTF8_ENCODING;
1954 }
1955 else
1956 rc = VERR_XAR_TOC_STRLEN_MISMATCH;
1957 }
1958
1959 RTMemTmpFree(pszOutput);
1960 return rc;
1961}
1962
1963
1964/**
1965 * Reads and validates the XAR header.
1966 *
1967 * @returns IPRT status code.
1968 * @param hVfsIosIn The input stream.
1969 * @param pXarHdr Where to return the XAR header in host byte order.
1970 */
1971static int rtZipXarReadAndValidateHeader(RTVFSIOSTREAM hVfsIosIn, PXARHEADER pXarHdr)
1972{
1973 /*
1974 * Read it and check the signature.
1975 */
1976 int rc = RTVfsIoStrmRead(hVfsIosIn, pXarHdr, sizeof(*pXarHdr), true /*fBlocking*/, NULL);
1977 if (RT_FAILURE(rc))
1978 return rc;
1979 if (pXarHdr->u32Magic != XAR_HEADER_MAGIC)
1980 return VERR_XAR_WRONG_MAGIC;
1981
1982 /*
1983 * Correct the byte order.
1984 */
1985 pXarHdr->cbHeader = RT_BE2H_U16(pXarHdr->cbHeader);
1986 pXarHdr->uVersion = RT_BE2H_U16(pXarHdr->uVersion);
1987 pXarHdr->cbTocCompressed = RT_BE2H_U64(pXarHdr->cbTocCompressed);
1988 pXarHdr->cbTocUncompressed = RT_BE2H_U64(pXarHdr->cbTocUncompressed);
1989 pXarHdr->uHashFunction = RT_BE2H_U32(pXarHdr->uHashFunction);
1990
1991 /*
1992 * Validate the header.
1993 */
1994 if (pXarHdr->uVersion > XAR_HEADER_VERSION)
1995 return VERR_XAR_UNSUPPORTED_VERSION;
1996 if (pXarHdr->cbHeader < sizeof(XARHEADER))
1997 return VERR_XAR_BAD_HDR_SIZE;
1998 if (pXarHdr->uHashFunction > XAR_HASH_MAX)
1999 return VERR_XAR_UNSUPPORTED_HASH_FUNCTION;
2000 if (pXarHdr->cbTocUncompressed < 16)
2001 return VERR_XAR_TOC_TOO_SMALL;
2002 if (pXarHdr->cbTocUncompressed > _4M)
2003 return VERR_XAR_TOC_TOO_BIG;
2004 if (pXarHdr->cbTocCompressed > _4M)
2005 return VERR_XAR_TOC_TOO_BIG_COMPRESSED;
2006
2007 /*
2008 * Skip over bytes we don't understand (could be padding).
2009 */
2010 if (pXarHdr->cbHeader > sizeof(XARHEADER))
2011 {
2012 rc = RTVfsIoStrmSkip(hVfsIosIn, pXarHdr->cbHeader - sizeof(XARHEADER));
2013 if (RT_FAILURE(rc))
2014 return rc;
2015 }
2016
2017 return VINF_SUCCESS;
2018}
2019
2020
2021RTDECL(int) RTZipXarFsStreamFromIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
2022{
2023 /*
2024 * Input validation.
2025 */
2026 AssertPtrReturn(phVfsFss, VERR_INVALID_HANDLE);
2027 *phVfsFss = NIL_RTVFSFSSTREAM;
2028 AssertPtrReturn(hVfsIosIn, VERR_INVALID_HANDLE);
2029 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
2030
2031 RTFOFF const offStart = RTVfsIoStrmTell(hVfsIosIn);
2032 AssertReturn(offStart >= 0, (int)offStart);
2033
2034 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosIn);
2035 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
2036
2037 /*
2038 * Read and validate the header, then uncompress the TOC.
2039 */
2040 XARHEADER XarHdr;
2041 int rc = rtZipXarReadAndValidateHeader(hVfsIosIn, &XarHdr);
2042 if (RT_SUCCESS(rc))
2043 {
2044 xml::Document *pDoc = NULL;
2045 try { pDoc = new xml::Document(); }
2046 catch (...) { }
2047 if (pDoc)
2048 {
2049 RTZIPXARHASHDIGEST TocDigest;
2050 xml::ElementNode const *pTocElem = NULL;
2051 rc = rtZipXarReadAndValidateToc(hVfsIosIn, &XarHdr, pDoc, &pTocElem, &TocDigest);
2052 if (RT_SUCCESS(rc))
2053 {
2054 size_t offZero = RTVfsIoStrmTell(hVfsIosIn);
2055 if (offZero > 0)
2056 {
2057 /*
2058 * Create a file system stream before we continue the parsing.
2059 */
2060 PRTZIPXARFSSTREAM pThis;
2061 RTVFSFSSTREAM hVfsFss;
2062 rc = RTVfsNewFsStream(&rtZipXarFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFss, (void **)&pThis);
2063 if (RT_SUCCESS(rc))
2064 {
2065 pThis->hVfsIos = hVfsIosIn;
2066 pThis->hVfsFile = RTVfsIoStrmToFile(hVfsIosIn);
2067 pThis->offStart = offStart;
2068 pThis->offZero = offZero;
2069 pThis->uHashFunction = (uint8_t)XarHdr.uHashFunction;
2070 switch (pThis->uHashFunction)
2071 {
2072 case XAR_HASH_MD5: pThis->cbHashDigest = sizeof(TocDigest.abMd5); break;
2073 case XAR_HASH_SHA1: pThis->cbHashDigest = sizeof(TocDigest.abSha1); break;
2074 default: pThis->cbHashDigest = 0; break;
2075 }
2076 pThis->fEndOfStream = false;
2077 pThis->rcFatal = VINF_SUCCESS;
2078 pThis->XarReader.pDoc = pDoc;
2079 pThis->XarReader.pToc = pTocElem;
2080 pThis->XarReader.pCurFile = 0;
2081 pThis->XarReader.cCurDepth = 0;
2082
2083 /*
2084 * Next validation step.
2085 */
2086 rc = rtZipXarValidateTocPart2(pThis, &XarHdr, &TocDigest);
2087 if (RT_SUCCESS(rc))
2088 {
2089 *phVfsFss = hVfsFss;
2090 return VINF_SUCCESS;
2091 }
2092
2093 RTVfsFsStrmRelease(hVfsFss);
2094 return rc;
2095 }
2096 }
2097 else
2098 rc = (int)offZero;
2099 }
2100 delete pDoc;
2101 }
2102 else
2103 rc = VERR_NO_MEMORY;
2104 }
2105
2106 RTVfsIoStrmRelease(hVfsIosIn);
2107 return rc;
2108}
2109
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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