VirtualBox

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

Last change on this file since 51713 was 49028, checked in by vboxsync, 11 years ago

iprt/cpp/xml: Fixed attribute lookup with namespace by doing the same way as for elements. Also renamed the two methods with namespace prefix and name in the 'logical' order so they can safely be changed to the order dictated C++ default parameter value handling (name first, then optionally ns-prefix), like the rest.

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