VirtualBox

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

Last change on this file since 48844 was 48844, checked in by vboxsync, 12 years ago

build fix

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

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