VirtualBox

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

Last change on this file since 81106 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 69.3 KB
Line 
1/* $Id: xarvfs.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * IPRT - XAR Virtual Filesystem.
4 */
5
6/*
7 * Copyright (C) 2010-2019 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_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
1166 rtZipXarFssFile_SetMode,
1167 rtZipXarFssFile_SetTimes,
1168 rtZipXarFssFile_SetOwner,
1169 RTVFSOBJSETOPS_VERSION
1170 },
1171 rtZipXarFssFile_Seek,
1172 rtZipXarFssFile_QuerySize,
1173 NULL /*SetSize*/,
1174 NULL /*QueryMaxSize*/,
1175 RTVFSFILEOPS_VERSION,
1176};
1177
1178
1179
1180
1181/**
1182 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1183 */
1184static DECLCALLBACK(int) rtZipXarFssDecompIos_Close(void *pvThis)
1185{
1186 PRTZIPXARDECOMPIOS pThis = (PRTZIPXARDECOMPIOS)pvThis;
1187
1188 RTVfsIoStrmRelease(pThis->hVfsIosDecompressor);
1189 pThis->hVfsIosDecompressor = NIL_RTVFSIOSTREAM;
1190
1191 RTVfsIoStrmRelease(pThis->hVfsIosRaw);
1192 pThis->hVfsIosRaw = NIL_RTVFSIOSTREAM;
1193 pThis->pIosRaw = NULL;
1194
1195 return VINF_SUCCESS;
1196}
1197
1198
1199/**
1200 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1201 */
1202static DECLCALLBACK(int) rtZipXarFssDecompIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1203{
1204 PRTZIPXARDECOMPIOS pThis = (PRTZIPXARDECOMPIOS)pvThis;
1205
1206 int rc = rtZipXarFssBaseObj_QueryInfo(&pThis->pIosRaw->BaseObj, pObjInfo, enmAddAttr);
1207 pObjInfo->cbObject = pThis->pIosRaw->DataAttr.cbDataExtracted;
1208 return rc;
1209}
1210
1211
1212/**
1213 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
1214 */
1215static DECLCALLBACK(int) rtZipXarFssDecompIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
1216{
1217 PRTZIPXARDECOMPIOS pThis = (PRTZIPXARDECOMPIOS)pvThis;
1218 AssertReturn(pSgBuf->cSegs == 1, VERR_INVALID_PARAMETER);
1219
1220 /*
1221 * Enforce the cbDataExtracted limit.
1222 */
1223 if (pThis->offCurPos > pThis->pIosRaw->DataAttr.cbDataExtracted)
1224 return VERR_XAR_EXTRACTED_SIZE_EXCEEDED;
1225
1226 /*
1227 * Read the data.
1228 *
1229 * ASSUMES the decompressor stream isn't seekable, so we don't have to
1230 * validate off wrt data digest updating.
1231 */
1232 int rc = RTVfsIoStrmReadAt(pThis->hVfsIosDecompressor, off, pSgBuf->paSegs[0].pvSeg, pSgBuf->paSegs[0].cbSeg,
1233 fBlocking, pcbRead);
1234 if (RT_FAILURE(rc))
1235 return rc;
1236
1237 /*
1238 * Hash the data. When reaching the end match against the expected digest.
1239 */
1240 size_t cbActuallyRead = pcbRead ? *pcbRead : pSgBuf->paSegs[0].cbSeg;
1241 pThis->offCurPos += cbActuallyRead;
1242 rtZipXarHashUpdate(&pThis->CtxExtracted, pThis->uHashFunExtracted, pSgBuf->paSegs[0].pvSeg, cbActuallyRead);
1243 if (rc == VINF_EOF)
1244 {
1245 if (pThis->offCurPos == pThis->pIosRaw->DataAttr.cbDataExtracted)
1246 {
1247 if (pThis->uHashState == RTZIPXAR_HASH_PENDING)
1248 {
1249 RTZIPXARHASHDIGEST Digest;
1250 rtZipXarHashFinal(&pThis->CtxExtracted, pThis->uHashFunExtracted, &Digest);
1251 if (rtZipXarHashIsEqual(pThis->uHashFunExtracted, &Digest, &pThis->DigestExtracted))
1252 pThis->uHashState = RTZIPXAR_HASH_OK;
1253 else
1254 {
1255 pThis->uHashState = RTZIPXAR_HASH_FAILED_EXTRACTED;
1256 rc = VERR_XAR_EXTRACTED_HASH_MISMATCH;
1257 }
1258 }
1259 else if (pThis->uHashState != RTZIPXAR_HASH_OK)
1260 rc = VERR_XAR_EXTRACTED_HASH_MISMATCH;
1261 }
1262 else
1263 rc = VERR_XAR_EXTRACTED_SIZE_EXCEEDED;
1264
1265 /* Ensure that the raw stream is also at the end so that both
1266 message digests are checked. */
1267 if (RT_SUCCESS(rc))
1268 {
1269 if ( pThis->pIosRaw->offCurPos < pThis->pIosRaw->DataAttr.cbDataArchived
1270 || pThis->pIosRaw->uHashState == RTZIPXAR_HASH_PENDING)
1271 rc = VERR_XAR_UNUSED_ARCHIVED_DATA;
1272 else if (pThis->pIosRaw->uHashState != RTZIPXAR_HASH_OK)
1273 rc = VERR_XAR_ARCHIVED_HASH_MISMATCH;
1274 }
1275 }
1276
1277 return rc;
1278}
1279
1280
1281/**
1282 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
1283 */
1284static DECLCALLBACK(int) rtZipXarFssDecompIos_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
1285{
1286 /* Cannot write to a read-only I/O stream. */
1287 NOREF(pvThis); NOREF(off); NOREF(pSgBuf); NOREF(fBlocking); NOREF(pcbWritten);
1288 return VERR_ACCESS_DENIED;
1289}
1290
1291
1292/**
1293 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
1294 */
1295static DECLCALLBACK(int) rtZipXarFssDecompIos_Flush(void *pvThis)
1296{
1297 /* It's a read only stream, nothing dirty to flush. */
1298 NOREF(pvThis);
1299 return VINF_SUCCESS;
1300}
1301
1302
1303/**
1304 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
1305 */
1306static DECLCALLBACK(int) rtZipXarFssDecompIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
1307 uint32_t *pfRetEvents)
1308{
1309 PRTZIPXARDECOMPIOS pThis = (PRTZIPXARDECOMPIOS)pvThis;
1310 return RTVfsIoStrmPoll(pThis->hVfsIosDecompressor, fEvents, cMillies, fIntr, pfRetEvents);
1311}
1312
1313
1314/**
1315 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
1316 */
1317static DECLCALLBACK(int) rtZipXarFssDecompIos_Tell(void *pvThis, PRTFOFF poffActual)
1318{
1319 PRTZIPXARDECOMPIOS pThis = (PRTZIPXARDECOMPIOS)pvThis;
1320 *poffActual = pThis->offCurPos;
1321 return VINF_SUCCESS;
1322}
1323
1324
1325/**
1326 * Xar I/O stream operations.
1327 */
1328static const RTVFSIOSTREAMOPS g_rtZipXarFssDecompIosOps =
1329{
1330 { /* Obj */
1331 RTVFSOBJOPS_VERSION,
1332 RTVFSOBJTYPE_IO_STREAM,
1333 "XarFsStream::DecompIoStream",
1334 rtZipXarFssDecompIos_Close,
1335 rtZipXarFssDecompIos_QueryInfo,
1336 RTVFSOBJOPS_VERSION
1337 },
1338 RTVFSIOSTREAMOPS_VERSION,
1339 0,
1340 rtZipXarFssDecompIos_Read,
1341 rtZipXarFssDecompIos_Write,
1342 rtZipXarFssDecompIos_Flush,
1343 rtZipXarFssDecompIos_PollOne,
1344 rtZipXarFssDecompIos_Tell,
1345 NULL /*Skip*/,
1346 NULL /*ZeroFill*/,
1347 RTVFSIOSTREAMOPS_VERSION
1348};
1349
1350
1351
1352
1353/**
1354 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1355 */
1356static DECLCALLBACK(int) rtZipXarFssSym_Close(void *pvThis)
1357{
1358 PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
1359 return rtZipXarFssBaseObj_Close(pThis);
1360}
1361
1362
1363/**
1364 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1365 */
1366static DECLCALLBACK(int) rtZipXarFssSym_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1367{
1368 PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
1369 return rtZipXarFssBaseObj_QueryInfo(pThis, pObjInfo, enmAddAttr);
1370}
1371
1372/**
1373 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
1374 */
1375static DECLCALLBACK(int) rtZipXarFssSym_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
1376{
1377 NOREF(pvThis); NOREF(fMode); NOREF(fMask);
1378 return VERR_ACCESS_DENIED;
1379}
1380
1381
1382/**
1383 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
1384 */
1385static DECLCALLBACK(int) rtZipXarFssSym_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1386 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1387{
1388 NOREF(pvThis); NOREF(pAccessTime); NOREF(pModificationTime); NOREF(pChangeTime); NOREF(pBirthTime);
1389 return VERR_ACCESS_DENIED;
1390}
1391
1392
1393/**
1394 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
1395 */
1396static DECLCALLBACK(int) rtZipXarFssSym_SetOwner(void *pvThis, RTUID uid, RTGID gid)
1397{
1398 NOREF(pvThis); NOREF(uid); NOREF(gid);
1399 return VERR_ACCESS_DENIED;
1400}
1401
1402
1403/**
1404 * @interface_method_impl{RTVFSSYMLINKOPS,pfnRead}
1405 */
1406static DECLCALLBACK(int) rtZipXarFssSym_Read(void *pvThis, char *pszTarget, size_t cbTarget)
1407{
1408 PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
1409#if 0
1410 return RTStrCopy(pszTarget, cbXarget, pThis->pXarReader->szTarget);
1411#else
1412 RT_NOREF_PV(pThis); RT_NOREF_PV(pszTarget); RT_NOREF_PV(cbTarget);
1413 return VERR_NOT_IMPLEMENTED;
1414#endif
1415}
1416
1417
1418/**
1419 * Xar symbolic (and hardlink) operations.
1420 */
1421static const RTVFSSYMLINKOPS g_rtZipXarFssSymOps =
1422{
1423 { /* Obj */
1424 RTVFSOBJOPS_VERSION,
1425 RTVFSOBJTYPE_SYMLINK,
1426 "XarFsStream::Symlink",
1427 rtZipXarFssSym_Close,
1428 rtZipXarFssSym_QueryInfo,
1429 RTVFSOBJOPS_VERSION
1430 },
1431 RTVFSSYMLINKOPS_VERSION,
1432 0,
1433 { /* ObjSet */
1434 RTVFSOBJSETOPS_VERSION,
1435 RT_UOFFSETOF(RTVFSSYMLINKOPS, ObjSet) - RT_UOFFSETOF(RTVFSSYMLINKOPS, Obj),
1436 rtZipXarFssSym_SetMode,
1437 rtZipXarFssSym_SetTimes,
1438 rtZipXarFssSym_SetOwner,
1439 RTVFSOBJSETOPS_VERSION
1440 },
1441 rtZipXarFssSym_Read,
1442 RTVFSSYMLINKOPS_VERSION
1443};
1444
1445
1446/**
1447 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1448 */
1449static DECLCALLBACK(int) rtZipXarFss_Close(void *pvThis)
1450{
1451 PRTZIPXARFSSTREAM pThis = (PRTZIPXARFSSTREAM)pvThis;
1452
1453 RTVfsIoStrmRelease(pThis->hVfsIos);
1454 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
1455
1456 RTVfsFileRelease(pThis->hVfsFile);
1457 pThis->hVfsFile = NIL_RTVFSFILE;
1458
1459 if (pThis->XarReader.pDoc)
1460 delete pThis->XarReader.pDoc;
1461 pThis->XarReader.pDoc = NULL;
1462 /* The other XarReader fields only point to elements within pDoc. */
1463 pThis->XarReader.pToc = NULL;
1464 pThis->XarReader.cCurDepth = 0;
1465 pThis->XarReader.pCurFile = NULL;
1466
1467 return VINF_SUCCESS;
1468}
1469
1470
1471/**
1472 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1473 */
1474static DECLCALLBACK(int) rtZipXarFss_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1475{
1476 PRTZIPXARFSSTREAM pThis = (PRTZIPXARFSSTREAM)pvThis;
1477 /* Take the lazy approach here, with the sideffect of providing some info
1478 that is actually kind of useful. */
1479 return RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
1480}
1481
1482
1483/**
1484 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnNext}
1485 */
1486static DECLCALLBACK(int) rtZipXarFss_Next(void *pvThis, char **ppszName, RTVFSOBJTYPE *penmType, PRTVFSOBJ phVfsObj)
1487{
1488 PRTZIPXARFSSTREAM pThis = (PRTZIPXARFSSTREAM)pvThis;
1489
1490 /*
1491 * Check if we've already reached the end in some way.
1492 */
1493 if (pThis->fEndOfStream)
1494 return VERR_EOF;
1495 if (pThis->rcFatal != VINF_SUCCESS)
1496 return pThis->rcFatal;
1497
1498 /*
1499 * Get the next file element.
1500 */
1501 xml::ElementNode const *pCurFile = pThis->XarReader.pCurFile;
1502 if (pCurFile)
1503 pThis->XarReader.pCurFile = pCurFile = rtZipXarGetNextFileElement(pCurFile, &pThis->XarReader.cCurDepth);
1504 else if (!pThis->fEndOfStream)
1505 {
1506 pThis->XarReader.cCurDepth = 0;
1507 pThis->XarReader.pCurFile = pCurFile = pThis->XarReader.pToc->findChildElement("file");
1508 }
1509 if (!pCurFile)
1510 {
1511 pThis->fEndOfStream = true;
1512 return VERR_EOF;
1513 }
1514
1515 /*
1516 * Retrive the fundamental attributes (elements actually).
1517 */
1518 const char *pszName = pCurFile->findChildElementValueP("name");
1519 const char *pszType = pCurFile->findChildElementValueP("type");
1520 if (RT_UNLIKELY(!pszName || !pszType))
1521 return pThis->rcFatal = VERR_XAR_BAD_FILE_ELEMENT;
1522
1523 /*
1524 * Validate the filename. Being a little too paranoid here, perhaps, wrt
1525 * path separators and escapes...
1526 */
1527 if ( !*pszName
1528 || strchr(pszName, '/')
1529 || strchr(pszName, '\\')
1530 || strchr(pszName, ':')
1531 || !strcmp(pszName, "..") )
1532 return pThis->rcFatal = VERR_XAR_INVALID_FILE_NAME;
1533
1534 /*
1535 * Gather any additional attributes that are essential to the file type,
1536 * then create the VFS object we're going to return.
1537 */
1538 int rc;
1539 RTVFSOBJ hVfsObj;
1540 RTVFSOBJTYPE enmType;
1541 if (!strcmp(pszType, "file"))
1542 {
1543 RTZIPXARDATASTREAM DataAttr;
1544 rc = rtZipXarGetDataStreamAttributes(pCurFile, &DataAttr);
1545 if (RT_FAILURE(rc))
1546 return pThis->rcFatal = rc;
1547 DataAttr.offData += pThis->offZero + pThis->offStart;
1548
1549 if ( pThis->hVfsFile != NIL_RTVFSFILE
1550 && DataAttr.enmEncoding == RTZIPXARENCODING_STORE)
1551 {
1552 /*
1553 * The input is seekable and the XAR file isn't compressed, so we
1554 * can provide a seekable file to the user.
1555 */
1556 RTVFSFILE hVfsFile;
1557 PRTZIPXARFILE pFileData;
1558 rc = RTVfsNewFile(&g_rtZipXarFssFileOps,
1559 sizeof(*pFileData),
1560 RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
1561 NIL_RTVFS,
1562 NIL_RTVFSLOCK,
1563 &hVfsFile,
1564 (void **)&pFileData);
1565 if (RT_FAILURE(rc))
1566 return pThis->rcFatal = rc;
1567
1568 pFileData->Ios.BaseObj.pFileElem = pCurFile;
1569 pFileData->Ios.BaseObj.fModeType = RTFS_TYPE_FILE;
1570 pFileData->Ios.DataAttr = DataAttr;
1571 pFileData->Ios.offCurPos = 0;
1572 pFileData->Ios.fEndOfStream = false;
1573 pFileData->Ios.fSeekable = true;
1574 pFileData->Ios.uHashState = RTZIPXAR_HASH_PENDING;
1575 pFileData->Ios.cbDigested = 0;
1576 rtZipXarHashInit(&pFileData->Ios.CtxArchived, pFileData->Ios.DataAttr.uHashFunArchived);
1577 rtZipXarHashInit(&pFileData->Ios.CtxExtracted, pFileData->Ios.DataAttr.uHashFunExtracted);
1578
1579 pFileData->Ios.hVfsIos = pThis->hVfsIos;
1580 RTVfsIoStrmRetain(pFileData->Ios.hVfsIos);
1581 pFileData->hVfsFile = pThis->hVfsFile;
1582 RTVfsFileRetain(pFileData->hVfsFile);
1583
1584 /* Try avoid double content hashing. */
1585 if (pFileData->Ios.DataAttr.uHashFunArchived == pFileData->Ios.DataAttr.uHashFunExtracted)
1586 pFileData->Ios.DataAttr.uHashFunExtracted = XAR_HASH_NONE;
1587
1588 enmType = RTVFSOBJTYPE_FILE;
1589 hVfsObj = RTVfsObjFromFile(hVfsFile);
1590 RTVfsFileRelease(hVfsFile);
1591 }
1592 else
1593 {
1594 RTVFSIOSTREAM hVfsIosRaw;
1595 PRTZIPXARIOSTREAM pIosData;
1596 rc = RTVfsNewIoStream(&g_rtZipXarFssIosOps,
1597 sizeof(*pIosData),
1598 RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
1599 NIL_RTVFS,
1600 NIL_RTVFSLOCK,
1601 &hVfsIosRaw,
1602 (void **)&pIosData);
1603 if (RT_FAILURE(rc))
1604 return pThis->rcFatal = rc;
1605
1606 pIosData->BaseObj.pFileElem = pCurFile;
1607 pIosData->BaseObj.fModeType = RTFS_TYPE_FILE;
1608 pIosData->DataAttr = DataAttr;
1609 pIosData->offCurPos = 0;
1610 pIosData->fEndOfStream = false;
1611 pIosData->fSeekable = pThis->hVfsFile != NIL_RTVFSFILE;
1612 pIosData->uHashState = RTZIPXAR_HASH_PENDING;
1613 pIosData->cbDigested = 0;
1614 rtZipXarHashInit(&pIosData->CtxArchived, pIosData->DataAttr.uHashFunArchived);
1615 rtZipXarHashInit(&pIosData->CtxExtracted, pIosData->DataAttr.uHashFunExtracted);
1616
1617 pIosData->hVfsIos = pThis->hVfsIos;
1618 RTVfsIoStrmRetain(pThis->hVfsIos);
1619
1620 if ( pIosData->DataAttr.enmEncoding != RTZIPXARENCODING_STORE
1621 && pIosData->DataAttr.enmEncoding != RTZIPXARENCODING_UNSUPPORTED)
1622 {
1623 /*
1624 * We need to set up a decompression chain.
1625 */
1626 RTVFSIOSTREAM hVfsIosDecomp;
1627 PRTZIPXARDECOMPIOS pIosDecompData;
1628 rc = RTVfsNewIoStream(&g_rtZipXarFssDecompIosOps,
1629 sizeof(*pIosDecompData),
1630 RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
1631 NIL_RTVFS,
1632 NIL_RTVFSLOCK,
1633 &hVfsIosDecomp,
1634 (void **)&pIosDecompData);
1635 if (RT_FAILURE(rc))
1636 {
1637 RTVfsIoStrmRelease(hVfsIosRaw);
1638 return pThis->rcFatal = rc;
1639 }
1640
1641 pIosDecompData->hVfsIosDecompressor = NIL_RTVFSIOSTREAM;
1642 pIosDecompData->hVfsIosRaw = hVfsIosRaw;
1643 pIosDecompData->pIosRaw = pIosData;
1644 pIosDecompData->offCurPos = 0;
1645 pIosDecompData->uHashFunExtracted = DataAttr.uHashFunExtracted;
1646 pIosDecompData->uHashState = RTZIPXAR_HASH_PENDING;
1647 rtZipXarHashInit(&pIosDecompData->CtxExtracted, pIosDecompData->uHashFunExtracted);
1648 pIosDecompData->DigestExtracted = DataAttr.DigestExtracted;
1649
1650 /* Tell the raw end to only hash the archived data. */
1651 pIosData->DataAttr.uHashFunExtracted = XAR_HASH_NONE;
1652
1653 /*
1654 * Hook up the decompressor.
1655 */
1656 switch (DataAttr.enmEncoding)
1657 {
1658 case RTZIPXARENCODING_GZIP:
1659 /* Must allow zlib header, all examples I've got seems
1660 to be using it rather than the gzip one. Makes
1661 sense as there is no need to repeat the file name
1662 and the attributes. */
1663 rc = RTZipGzipDecompressIoStream(hVfsIosRaw, RTZIPGZIPDECOMP_F_ALLOW_ZLIB_HDR,
1664 &pIosDecompData->hVfsIosDecompressor);
1665 break;
1666 default:
1667 rc = VERR_INTERNAL_ERROR_5;
1668 break;
1669 }
1670 if (RT_FAILURE(rc))
1671 {
1672 RTVfsIoStrmRelease(hVfsIosDecomp);
1673 return pThis->rcFatal = rc;
1674 }
1675
1676 /* What to return. */
1677 hVfsObj = RTVfsObjFromIoStream(hVfsIosDecomp);
1678 RTVfsIoStrmRelease(hVfsIosDecomp);
1679 }
1680 else
1681 {
1682 /* Try avoid double content hashing. */
1683 if (pIosData->DataAttr.uHashFunArchived == pIosData->DataAttr.uHashFunExtracted)
1684 pIosData->DataAttr.uHashFunExtracted = XAR_HASH_NONE;
1685
1686 /* What to return. */
1687 hVfsObj = RTVfsObjFromIoStream(hVfsIosRaw);
1688 RTVfsIoStrmRelease(hVfsIosRaw);
1689 }
1690 enmType = RTVFSOBJTYPE_IO_STREAM;
1691 }
1692 }
1693 else if (!strcmp(pszType, "directory"))
1694 {
1695 PRTZIPXARBASEOBJ pBaseObjData;
1696 rc = RTVfsNewBaseObj(&g_rtZipXarFssBaseObjOps,
1697 sizeof(*pBaseObjData),
1698 NIL_RTVFS,
1699 NIL_RTVFSLOCK,
1700 &hVfsObj,
1701 (void **)&pBaseObjData);
1702 if (RT_FAILURE(rc))
1703 return pThis->rcFatal = rc;
1704
1705 pBaseObjData->pFileElem = pCurFile;
1706 pBaseObjData->fModeType = RTFS_TYPE_DIRECTORY;
1707
1708 enmType = RTVFSOBJTYPE_BASE;
1709 }
1710 else if (!strcmp(pszType, "symlink"))
1711 {
1712 RTVFSSYMLINK hVfsSym;
1713 PRTZIPXARBASEOBJ pBaseObjData;
1714 rc = RTVfsNewSymlink(&g_rtZipXarFssSymOps,
1715 sizeof(*pBaseObjData),
1716 NIL_RTVFS,
1717 NIL_RTVFSLOCK,
1718 &hVfsSym,
1719 (void **)&pBaseObjData);
1720 if (RT_FAILURE(rc))
1721 return pThis->rcFatal = rc;
1722
1723 pBaseObjData->pFileElem = pCurFile;
1724 pBaseObjData->fModeType = RTFS_TYPE_SYMLINK;
1725
1726 enmType = RTVFSOBJTYPE_SYMLINK;
1727 hVfsObj = RTVfsObjFromSymlink(hVfsSym);
1728 RTVfsSymlinkRelease(hVfsSym);
1729 }
1730 else
1731 return pThis->rcFatal = VERR_XAR_UNKNOWN_FILE_TYPE;
1732
1733 /*
1734 * Set the return data and we're done.
1735 */
1736 if (ppszName)
1737 {
1738 /* Figure the length. */
1739 size_t const cbCurName = strlen(pszName) + 1;
1740 size_t cbFullName = cbCurName;
1741 const xml::ElementNode *pAncestor = pCurFile;
1742 uint32_t cLeft = pThis->XarReader.cCurDepth;
1743 while (cLeft-- > 0)
1744 {
1745 pAncestor = (const xml::ElementNode *)pAncestor->getParent(); Assert(pAncestor);
1746 const char *pszAncestorName = pAncestor->findChildElementValueP("name"); Assert(pszAncestorName);
1747 cbFullName += strlen(pszAncestorName) + 1;
1748 }
1749
1750 /* Allocate a buffer. */
1751 char *psz = *ppszName = RTStrAlloc(cbFullName);
1752 if (!psz)
1753 {
1754 RTVfsObjRelease(hVfsObj);
1755 return VERR_NO_STR_MEMORY;
1756 }
1757
1758 /* Construct it, from the end. */
1759 psz += cbFullName;
1760 psz -= cbCurName;
1761 memcpy(psz, pszName, cbCurName);
1762
1763 pAncestor = pCurFile;
1764 cLeft = pThis->XarReader.cCurDepth;
1765 while (cLeft-- > 0)
1766 {
1767 pAncestor = (const xml::ElementNode *)pAncestor->getParent(); Assert(pAncestor);
1768 const char *pszAncestorName = pAncestor->findChildElementValueP("name"); Assert(pszAncestorName);
1769 *--psz = '/';
1770 size_t cchAncestorName = strlen(pszAncestorName);
1771 psz -= cchAncestorName;
1772 memcpy(psz, pszAncestorName, cchAncestorName);
1773 }
1774 Assert(*ppszName == psz);
1775 }
1776
1777 if (phVfsObj)
1778 *phVfsObj = hVfsObj;
1779 else
1780 RTVfsObjRelease(hVfsObj);
1781
1782 if (penmType)
1783 *penmType = enmType;
1784
1785 return VINF_SUCCESS;
1786}
1787
1788
1789
1790/**
1791 * Xar filesystem stream operations.
1792 */
1793static const RTVFSFSSTREAMOPS rtZipXarFssOps =
1794{
1795 { /* Obj */
1796 RTVFSOBJOPS_VERSION,
1797 RTVFSOBJTYPE_FS_STREAM,
1798 "XarFsStream",
1799 rtZipXarFss_Close,
1800 rtZipXarFss_QueryInfo,
1801 RTVFSOBJOPS_VERSION
1802 },
1803 RTVFSFSSTREAMOPS_VERSION,
1804 0,
1805 rtZipXarFss_Next,
1806 NULL,
1807 NULL,
1808 NULL,
1809 RTVFSFSSTREAMOPS_VERSION
1810};
1811
1812
1813
1814/**
1815 * TOC validation part 2.
1816 *
1817 * Will advance the input stream past the TOC hash and signature data.
1818 *
1819 * @returns IPRT status code.
1820 * @param pThis The FS stream instance being created.
1821 * @param pXarHdr The XAR header.
1822 * @param pTocDigest The TOC input data digest.
1823 */
1824static int rtZipXarValidateTocPart2(PRTZIPXARFSSTREAM pThis, PCXARHEADER pXarHdr, PCRTZIPXARHASHDIGEST pTocDigest)
1825{
1826 int rc;
1827 RT_NOREF_PV(pXarHdr);
1828
1829 /*
1830 * Check that the hash function in the TOC matches the one in the XAR header.
1831 */
1832 const xml::ElementNode *pChecksumElem = pThis->XarReader.pToc->findChildElement("checksum");
1833 if (pChecksumElem)
1834 {
1835 const xml::AttributeNode *pAttr = pChecksumElem->findAttribute("style");
1836 if (!pAttr)
1837 return VERR_XAR_BAD_CHECKSUM_ELEMENT;
1838
1839 const char *pszStyle = pAttr->getValue();
1840 if (!pszStyle)
1841 return VERR_XAR_BAD_CHECKSUM_ELEMENT;
1842
1843 uint8_t uHashFunction;
1844 rc = rtZipXarParseChecksumStyle(pszStyle, &uHashFunction);
1845 if (RT_FAILURE(rc))
1846 return rc;
1847 if (uHashFunction != pThis->uHashFunction)
1848 return VERR_XAR_HASH_FUNCTION_MISMATCH;
1849
1850 /*
1851 * Verify the checksum if we got one.
1852 */
1853 if (pThis->uHashFunction != XAR_HASH_NONE)
1854 {
1855 RTFOFF offChecksum;
1856 RTFOFF cbChecksum;
1857 rc = rtZipXarGetOffsetSizeLengthFromElem(pChecksumElem, &offChecksum, &cbChecksum, NULL);
1858 if (RT_FAILURE(rc))
1859 return rc;
1860 if (cbChecksum != (RTFOFF)pThis->cbHashDigest)
1861 return VERR_XAR_BAD_DIGEST_LENGTH;
1862 if (offChecksum != 0 && pThis->hVfsFile == NIL_RTVFSFILE)
1863 return VERR_XAR_NOT_STREAMBLE_ELEMENT_ORDER;
1864
1865 RTZIPXARHASHDIGEST StoredDigest;
1866 rc = RTVfsIoStrmReadAt(pThis->hVfsIos, pThis->offZero + offChecksum, &StoredDigest, pThis->cbHashDigest,
1867 true /*fBlocking*/, NULL /*pcbRead*/);
1868 if (RT_FAILURE(rc))
1869 return rc;
1870 if (memcmp(&StoredDigest, pTocDigest, pThis->cbHashDigest))
1871 return VERR_XAR_TOC_DIGEST_MISMATCH;
1872 }
1873 }
1874 else if (pThis->uHashFunction != XAR_HASH_NONE)
1875 return VERR_XAR_BAD_CHECKSUM_ELEMENT;
1876
1877 /*
1878 * Check the signature, if we got one.
1879 */
1880 /** @todo signing. */
1881
1882 return VINF_SUCCESS;
1883}
1884
1885
1886/**
1887 * Reads and validates the table of content.
1888 *
1889 * @returns IPRT status code.
1890 * @param hVfsIosIn The input stream.
1891 * @param pXarHdr The XAR header.
1892 * @param pDoc The TOC XML document.
1893 * @param ppTocElem Where to return the pointer to the TOC element on
1894 * success.
1895 * @param pTocDigest Where to return the TOC digest on success.
1896 */
1897static int rtZipXarReadAndValidateToc(RTVFSIOSTREAM hVfsIosIn, PCXARHEADER pXarHdr,
1898 xml::Document *pDoc, xml::ElementNode const **ppTocElem, PRTZIPXARHASHDIGEST pTocDigest)
1899{
1900 /*
1901 * Decompress it, calculating the hash while doing so.
1902 */
1903 char *pszOutput = (char *)RTMemTmpAlloc(pXarHdr->cbTocUncompressed + 1);
1904 if (!pszOutput)
1905 return VERR_NO_TMP_MEMORY;
1906 int rc = VERR_NO_TMP_MEMORY;
1907 void *pvInput = RTMemTmpAlloc(pXarHdr->cbTocCompressed);
1908 if (pvInput)
1909 {
1910 rc = RTVfsIoStrmRead(hVfsIosIn, pvInput, pXarHdr->cbTocCompressed, true /*fBlocking*/, NULL);
1911 if (RT_SUCCESS(rc))
1912 {
1913 rtZipXarCalcHash(pXarHdr->uHashFunction, pvInput, pXarHdr->cbTocCompressed, pTocDigest);
1914
1915 size_t cbActual;
1916 rc = RTZipBlockDecompress(RTZIPTYPE_ZLIB, 0 /*fFlags*/,
1917 pvInput, pXarHdr->cbTocCompressed, NULL,
1918 pszOutput, pXarHdr->cbTocUncompressed, &cbActual);
1919 if (RT_SUCCESS(rc) && cbActual != pXarHdr->cbTocUncompressed)
1920 rc = VERR_XAR_TOC_UNCOMP_SIZE_MISMATCH;
1921 }
1922 RTMemTmpFree(pvInput);
1923 }
1924 if (RT_SUCCESS(rc))
1925 {
1926 pszOutput[pXarHdr->cbTocUncompressed] = '\0';
1927
1928 /*
1929 * Parse the TOC (XML document) and do some basic validations.
1930 */
1931 size_t cchToc = strlen(pszOutput);
1932 if ( cchToc == pXarHdr->cbTocUncompressed
1933 || cchToc + 1 == pXarHdr->cbTocUncompressed)
1934 {
1935 rc = RTStrValidateEncoding(pszOutput);
1936 if (RT_SUCCESS(rc))
1937 {
1938 xml::XmlMemParser Parser;
1939 try
1940 {
1941 Parser.read(pszOutput, cchToc, RTCString("xar-toc.xml"), *pDoc);
1942 }
1943 catch (xml::XmlError &)
1944 {
1945 rc = VERR_XAR_TOC_XML_PARSE_ERROR;
1946 }
1947 catch (...)
1948 {
1949 rc = VERR_NO_MEMORY;
1950 }
1951 if (RT_SUCCESS(rc))
1952 {
1953 xml::ElementNode const *pRootElem = pDoc->getRootElement();
1954 xml::ElementNode const *pTocElem = NULL;
1955 if (pRootElem && pRootElem->nameEquals("xar"))
1956 pTocElem = pRootElem ? pRootElem->findChildElement("toc") : NULL;
1957 if (pTocElem)
1958 {
1959#ifndef USE_STD_LIST_FOR_CHILDREN
1960 Assert(pRootElem->getParent() == NULL);
1961 Assert(pTocElem->getParent() == pRootElem);
1962 if ( !pTocElem->getNextSibiling()
1963 && !pTocElem->getPrevSibiling())
1964#endif
1965 {
1966 /*
1967 * Further parsing and validation is done after the
1968 * caller has created an file system stream instance.
1969 */
1970 *ppTocElem = pTocElem;
1971
1972 RTMemTmpFree(pszOutput);
1973 return VINF_SUCCESS;
1974 }
1975
1976 rc = VERR_XML_TOC_ELEMENT_HAS_SIBLINGS;
1977 }
1978 else
1979 rc = VERR_XML_TOC_ELEMENT_MISSING;
1980 }
1981 }
1982 else
1983 rc = VERR_XAR_TOC_UTF8_ENCODING;
1984 }
1985 else
1986 rc = VERR_XAR_TOC_STRLEN_MISMATCH;
1987 }
1988
1989 RTMemTmpFree(pszOutput);
1990 return rc;
1991}
1992
1993
1994/**
1995 * Reads and validates the XAR header.
1996 *
1997 * @returns IPRT status code.
1998 * @param hVfsIosIn The input stream.
1999 * @param pXarHdr Where to return the XAR header in host byte order.
2000 */
2001static int rtZipXarReadAndValidateHeader(RTVFSIOSTREAM hVfsIosIn, PXARHEADER pXarHdr)
2002{
2003 /*
2004 * Read it and check the signature.
2005 */
2006 int rc = RTVfsIoStrmRead(hVfsIosIn, pXarHdr, sizeof(*pXarHdr), true /*fBlocking*/, NULL);
2007 if (RT_FAILURE(rc))
2008 return rc;
2009 if (pXarHdr->u32Magic != XAR_HEADER_MAGIC)
2010 return VERR_XAR_WRONG_MAGIC;
2011
2012 /*
2013 * Correct the byte order.
2014 */
2015 pXarHdr->cbHeader = RT_BE2H_U16(pXarHdr->cbHeader);
2016 pXarHdr->uVersion = RT_BE2H_U16(pXarHdr->uVersion);
2017 pXarHdr->cbTocCompressed = RT_BE2H_U64(pXarHdr->cbTocCompressed);
2018 pXarHdr->cbTocUncompressed = RT_BE2H_U64(pXarHdr->cbTocUncompressed);
2019 pXarHdr->uHashFunction = RT_BE2H_U32(pXarHdr->uHashFunction);
2020
2021 /*
2022 * Validate the header.
2023 */
2024 if (pXarHdr->uVersion > XAR_HEADER_VERSION)
2025 return VERR_XAR_UNSUPPORTED_VERSION;
2026 if (pXarHdr->cbHeader < sizeof(XARHEADER))
2027 return VERR_XAR_BAD_HDR_SIZE;
2028 if (pXarHdr->uHashFunction > XAR_HASH_MAX)
2029 return VERR_XAR_UNSUPPORTED_HASH_FUNCTION;
2030 if (pXarHdr->cbTocUncompressed < 16)
2031 return VERR_XAR_TOC_TOO_SMALL;
2032 if (pXarHdr->cbTocUncompressed > _4M)
2033 return VERR_XAR_TOC_TOO_BIG;
2034 if (pXarHdr->cbTocCompressed > _4M)
2035 return VERR_XAR_TOC_TOO_BIG_COMPRESSED;
2036
2037 /*
2038 * Skip over bytes we don't understand (could be padding).
2039 */
2040 if (pXarHdr->cbHeader > sizeof(XARHEADER))
2041 {
2042 rc = RTVfsIoStrmSkip(hVfsIosIn, pXarHdr->cbHeader - sizeof(XARHEADER));
2043 if (RT_FAILURE(rc))
2044 return rc;
2045 }
2046
2047 return VINF_SUCCESS;
2048}
2049
2050
2051RTDECL(int) RTZipXarFsStreamFromIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
2052{
2053 /*
2054 * Input validation.
2055 */
2056 AssertPtrReturn(phVfsFss, VERR_INVALID_HANDLE);
2057 *phVfsFss = NIL_RTVFSFSSTREAM;
2058 AssertPtrReturn(hVfsIosIn, VERR_INVALID_HANDLE);
2059 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
2060
2061 RTFOFF const offStart = RTVfsIoStrmTell(hVfsIosIn);
2062 AssertReturn(offStart >= 0, (int)offStart);
2063
2064 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosIn);
2065 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
2066
2067 /*
2068 * Read and validate the header, then uncompress the TOC.
2069 */
2070 XARHEADER XarHdr;
2071 int rc = rtZipXarReadAndValidateHeader(hVfsIosIn, &XarHdr);
2072 if (RT_SUCCESS(rc))
2073 {
2074 xml::Document *pDoc = NULL;
2075 try { pDoc = new xml::Document(); }
2076 catch (...) { }
2077 if (pDoc)
2078 {
2079 RTZIPXARHASHDIGEST TocDigest;
2080 xml::ElementNode const *pTocElem = NULL;
2081 rc = rtZipXarReadAndValidateToc(hVfsIosIn, &XarHdr, pDoc, &pTocElem, &TocDigest);
2082 if (RT_SUCCESS(rc))
2083 {
2084 size_t offZero = RTVfsIoStrmTell(hVfsIosIn);
2085 if (offZero > 0)
2086 {
2087 /*
2088 * Create a file system stream before we continue the parsing.
2089 */
2090 PRTZIPXARFSSTREAM pThis;
2091 RTVFSFSSTREAM hVfsFss;
2092 rc = RTVfsNewFsStream(&rtZipXarFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, true /*fReadOnly*/,
2093 &hVfsFss, (void **)&pThis);
2094 if (RT_SUCCESS(rc))
2095 {
2096 pThis->hVfsIos = hVfsIosIn;
2097 pThis->hVfsFile = RTVfsIoStrmToFile(hVfsIosIn);
2098 pThis->offStart = offStart;
2099 pThis->offZero = offZero;
2100 pThis->uHashFunction = (uint8_t)XarHdr.uHashFunction;
2101 switch (pThis->uHashFunction)
2102 {
2103 case XAR_HASH_MD5: pThis->cbHashDigest = sizeof(TocDigest.abMd5); break;
2104 case XAR_HASH_SHA1: pThis->cbHashDigest = sizeof(TocDigest.abSha1); break;
2105 default: pThis->cbHashDigest = 0; break;
2106 }
2107 pThis->fEndOfStream = false;
2108 pThis->rcFatal = VINF_SUCCESS;
2109 pThis->XarReader.pDoc = pDoc;
2110 pThis->XarReader.pToc = pTocElem;
2111 pThis->XarReader.pCurFile = 0;
2112 pThis->XarReader.cCurDepth = 0;
2113
2114 /*
2115 * Next validation step.
2116 */
2117 rc = rtZipXarValidateTocPart2(pThis, &XarHdr, &TocDigest);
2118 if (RT_SUCCESS(rc))
2119 {
2120 *phVfsFss = hVfsFss;
2121 return VINF_SUCCESS;
2122 }
2123
2124 RTVfsFsStrmRelease(hVfsFss);
2125 return rc;
2126 }
2127 }
2128 else
2129 rc = (int)offZero;
2130 }
2131 delete pDoc;
2132 }
2133 else
2134 rc = VERR_NO_MEMORY;
2135 }
2136
2137 RTVfsIoStrmRelease(hVfsIosIn);
2138 return rc;
2139}
2140
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