VirtualBox

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

Last change on this file since 99553 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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