VirtualBox

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

Last change on this file was 100908, checked in by vboxsync, 9 months ago

IPRT,Storage,Puel: Changed the pfnRead and pfnWrite VFS methods and the RTVfsIoStrmSgRead, RTVfsIoStrmSgWrite, RTVfsFileSgRead and RTVfsFileSgWrite APIs to advance pSgBuf and respect the incoming position just like RTFileSgRead & RTFileSgWrite.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use