VirtualBox

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

Last change on this file since 62477 was 62477, checked in by vboxsync, 8 years ago

(C) 2016

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

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