VirtualBox

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

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

A bit more XAR hacking.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 47.4 KB
Line 
1/* $Id: xarvfs.cpp 48797 2013-10-01 14:43:17Z vboxsync $ */
2/** @file
3 * IPRT - XAR Virtual Filesystem.
4 */
5
6/*
7 * Copyright (C) 2010-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/******************************************************************************
29 * Header Files *
30 ******************************************************************************/
31#include "internal/iprt.h"
32#include <iprt/zip.h>
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/ctype.h>
37#include <iprt/err.h>
38#include <iprt/md5.h>
39#include <iprt/poll.h>
40#include <iprt/file.h>
41#include <iprt/sha.h>
42#include <iprt/string.h>
43#include <iprt/vfs.h>
44#include <iprt/vfslowlevel.h>
45#include <iprt/formats/xar.h>
46#include <iprt/cpp/xml.h>
47
48
49
50/*******************************************************************************
51* Structures and Typedefs *
52*******************************************************************************/
53/**
54 * Hash digest value union for the supported XAR hash functions.
55 */
56typedef union RTZIPXARHASHDIGEST
57{
58 uint8_t abMd5[RTMD5_HASH_SIZE];
59 uint8_t abSha1[RTSHA1_HASH_SIZE];
60} RTZIPXARHASHDIGEST;
61/** Pointer to a XAR hash digest union. */
62typedef RTZIPXARHASHDIGEST *PRTZIPXARHASHDIGEST;
63/** Pointer to a const XAR hash digest union. */
64typedef RTZIPXARHASHDIGEST *PCRTZIPXARHASHDIGEST;
65
66/**
67 * XAR reader instance data.
68 */
69typedef struct RTZIPXARREADER
70{
71 /** The TOC XML element. */
72 xml::ElementNode const *pToc;
73 /** The TOC XML document. */
74 xml::Document *pDoc;
75
76 /** The current file. */
77 xml::ElementNode const *pCurFile;
78 /** The depth of the current file, with 0 being the root level. */
79 uint32_t cCurDepth;
80 /** Set if it's the first file. */
81 bool fFirstFile;
82} RTZIPXARREADER;
83/** Pointer to the XAR reader instance data. */
84typedef RTZIPXARREADER *PRTZIPXARREADER;
85
86/**
87 * Xar directory, character device, block device, fifo socket or symbolic link.
88 */
89typedef struct RTZIPXARBASEOBJ
90{
91 /** Pointer to the reader instance data (resides in the filesystem
92 * stream).
93 * @todo Fix this so it won't go stale... Back ref from this obj to fss? */
94 PRTZIPXARREADER pXarReader;
95 /** The file TOC element. */
96 xml::ElementNode const *pFileElem;
97} RTZIPXARBASEOBJ;
98/** Pointer to a XAR filesystem stream base object. */
99typedef RTZIPXARBASEOBJ *PRTZIPXARBASEOBJ;
100
101
102/**
103 * XAR data encoding.
104 */
105typedef enum RTZIPXARENCODING
106{
107 RTZIPXARENCODING_INVALID = 0,
108 RTZIPXARENCODING_STORE,
109 RTZIPXARENCODING_GZIP,
110 RTZIPXARENCODING_UNSUPPORTED,
111 RTZIPXARENCODING_END
112} RTZIPXARENCODING;
113
114
115/**
116 * Data stream attributes.
117 */
118typedef struct RTZIPXARDATASTREAM
119{
120 /** Offset of the data in the stream, relative to XARREADER::offZero. */
121 RTFOFF offData;
122 /** The size of the archived data. */
123 RTFOFF cbDataArchived;
124 /** The size of the extracted data. */
125 RTFOFF cbDataExtracted;
126 /** The encoding of the archived ata. */
127 RTZIPXARENCODING enmEncoding;
128 /** The hash function used for the archived data. */
129 uint8_t uHashFunArchived;
130 /** The hash function used for the extracted data. */
131 uint8_t uHashFunExtracted;
132 /** The digest of the archived data. */
133 RTZIPXARHASHDIGEST DigestArchived;
134 /** The digest of the extracted data. */
135 RTZIPXARHASHDIGEST DigestExtracted;
136} RTZIPXARDATASTREAM;
137/** Pointer to XAR data stream attributes. */
138typedef RTZIPXARDATASTREAM *PRTZIPXARDATASTREAM;
139
140
141/**
142 * Xar file represented as a VFS I/O stream.
143 */
144typedef struct RTZIPXARIOSTREAM
145{
146 /** The basic XAR object data. */
147 RTZIPXARBASEOBJ BaseObj;
148 /** The attributes of the primary data stream. */
149 RTZIPXARDATASTREAM Data;
150 /** The current file position. */
151 RTFOFF offFile;
152 /** The input I/O stream. */
153 RTVFSIOSTREAM hVfsIos;
154 /** Set if we've reached the end of the file. */
155 bool fEndOfStream;
156 /** The size of the file that we've currently hashed.
157 * We use this to check whether the user skips part of the file while reading
158 * and when to compare the digests. */
159 RTFOFF cbDigested;
160 /** The digest of the archived data. */
161 RTZIPXARHASHDIGEST DigestArchived;
162 /** The digest of the extracted data. */
163 RTZIPXARHASHDIGEST DigestExtracted;
164} RTZIPXARIOSTREAM;
165/** Pointer to a the private data of a XAR file I/O stream. */
166typedef RTZIPXARIOSTREAM *PRTZIPXARIOSTREAM;
167
168
169/**
170 * Xar file represented as a VFS file.
171 */
172typedef struct RTZIPXARFILE
173{
174 /** The XAR I/O stream object. */
175 RTZIPXARIOSTREAM IosObj;
176 /** The input file. */
177 RTVFSFILE hVfsFile;
178} RTZIPXARFILE;
179/** Pointer to a the private data of a XAR file. */
180typedef RTZIPXARFILE *PRTZIPXARFILE;
181
182
183/**
184 * Xar filesystem stream private data.
185 */
186typedef struct RTZIPXARFSSTREAM
187{
188 /** The input I/O stream. */
189 RTVFSIOSTREAM hVfsIos;
190 /** The input file, if the stream is actually a file. */
191 RTVFSFILE hVfsFile;
192
193 /** The current object (referenced). */
194 RTVFSOBJ hVfsCurObj;
195 /** Pointer to the private data if hVfsCurObj is representing a file. */
196 PRTZIPXARIOSTREAM pCurIosData;
197
198 /** The start offset in the input I/O stream. */
199 RTFOFF offStart;
200 /** The zero offset in the file which all others are relative to. */
201 RTFOFF offZero;
202
203 /** The hash function we're using (XAR_HASH_XXX). */
204 uint8_t uHashFunction;
205 /** The size of the digest produced by the hash function we're using. */
206 uint8_t cbHashDigest;
207
208 /** Set if we've reached the end of the stream. */
209 bool fEndOfStream;
210 /** Set if we've encountered a fatal error. */
211 int rcFatal;
212
213
214 /** The XAR reader instance data. */
215 RTZIPXARREADER XarReader;
216} RTZIPXARFSSTREAM;
217/** Pointer to a the private data of a XAR filesystem stream. */
218typedef RTZIPXARFSSTREAM *PRTZIPXARFSSTREAM;
219
220
221/**
222 * Hashes a block of data.
223 *
224 * @param uHashFunction The hash function to use.
225 * @param pvSrc The data to hash.
226 * @param cbSrc The size of the data to hash.
227 * @param pHashDigest Where to return the hash digest.
228 */
229static void rtZipXarCalcHash(uint32_t uHashFunction, void const *pvSrc, size_t cbSrc, PRTZIPXARHASHDIGEST pHashDigest)
230{
231 switch (uHashFunction)
232 {
233 case XAR_HASH_SHA1:
234 RTSha1(pvSrc, cbSrc, pHashDigest->abSha1);
235 break;
236 case XAR_HASH_MD5:
237 RTMd5(pvSrc, cbSrc, pHashDigest->abMd5);
238 break;
239 default:
240 RT_ZERO(*pHashDigest);
241 break;
242 }
243}
244
245
246static int rtZipXarGetOffsetSizeFromElem(xml::ElementNode const *pElement, PRTFOFF poff, uint64_t *pcb)
247{
248 if (pElement)
249 return VERR_XAR_DATA_NODE_NOT_FOUND;
250
251 /*
252 * The offset.
253 */
254 xml::ElementNode const *pElem = pElement->findChildElement("offset");
255 if (!pElem)
256 return VERR_XAR_MISSING_OFFSET_ELEMENT;
257 const char *pszValue = pElem->getValue();
258 if (!pszValue)
259 return VERR_XAR_BAD_OFFSET_ELEMENT;
260
261 int rc = RTStrToInt64Full(pszValue, 0, poff);
262 if ( RT_FAILURE(rc)
263 || rc == VWRN_NUMBER_TOO_BIG
264 || *poff > RTFOFF_MAX / 2 /* make sure to not overflow calculating offsets. */
265 || *poff < 0)
266 return VERR_XAR_BAD_OFFSET_ELEMENT;
267
268 /*
269 * The size.
270 */
271 pElem = pElement->findChildElement("size");
272 if (!pElem)
273 return VERR_XAR_MISSING_SIZE_ELEMENT;
274
275 pszValue = pElem->getValue();
276 if (!pszValue)
277 return VERR_XAR_BAD_SIZE_ELEMENT;
278
279 rc = RTStrToUInt64Full(pszValue, 0, pcb);
280 if ( RT_FAILURE(rc)
281 || rc == VWRN_NUMBER_TOO_BIG
282 || *pcb > UINT64_MAX / 2 /* prevent overflow should be use it for calcuations later. */)
283 return VERR_XAR_BAD_SIZE_ELEMENT;
284
285 return VINF_SUCCESS;
286}
287
288
289/**
290 * Translate a XAR header to an IPRT object info structure with additional UNIX
291 * attributes.
292 *
293 * This completes the validation done by rtZipXarHdrValidate.
294 *
295 * @returns VINF_SUCCESS if valid, appropriate VERR_XAR_XXX if not.
296 * @param pThis The XAR reader instance.
297 * @param pObjInfo The object info structure (output).
298 */
299static int rtZipXarReaderGetFsObjInfo(PRTZIPXARREADER pThis, PRTFSOBJINFO pObjInfo)
300{
301 /*
302 * Zap the whole structure, this takes care of unused space in the union.
303 */
304 RT_ZERO(*pObjInfo);
305
306#if 0
307 /*
308 * Convert the XAR field in RTFSOBJINFO order.
309 */
310 int rc;
311 int64_t i64Tmp;
312#define GET_XAR_NUMERIC_FIELD_RET(a_Var, a_Field) \
313 do { \
314 rc = rtZipXarHdrFieldToNum(a_Field, sizeof(a_Field), false /*fOctalOnly*/, &i64Tmp); \
315 if (RT_FAILURE(rc)) \
316 return rc; \
317 (a_Var) = i64Tmp; \
318 if ((a_Var) != i64Tmp) \
319 return VERR_XAR_NUM_VALUE_TOO_LARGE; \
320 } while (0)
321
322 GET_XAR_NUMERIC_FIELD_RET(pObjInfo->cbObject, pThis->Hdr.Common.size);
323 pObjInfo->cbAllocated = RT_ALIGN_64(pObjInfo->cbObject, 512);
324 int64_t c64SecModTime;
325 GET_XAR_NUMERIC_FIELD_RET(c64SecModTime, pThis->Hdr.Common.mtime);
326 RTTimeSpecSetSeconds(&pObjInfo->ChangeTime, c64SecModTime);
327 RTTimeSpecSetSeconds(&pObjInfo->ModificationTime, c64SecModTime);
328 RTTimeSpecSetSeconds(&pObjInfo->AccessTime, c64SecModTime);
329 RTTimeSpecSetSeconds(&pObjInfo->BirthTime, c64SecModTime);
330 if (c64SecModTime != RTTimeSpecGetSeconds(&pObjInfo->ModificationTime))
331 return VERR_XAR_NUM_VALUE_TOO_LARGE;
332 GET_XAR_NUMERIC_FIELD_RET(pObjInfo->Attr.fMode, pThis->Hdr.Common.mode);
333 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
334 GET_XAR_NUMERIC_FIELD_RET(pObjInfo->Attr.u.Unix.uid, pThis->Hdr.Common.uid);
335 GET_XAR_NUMERIC_FIELD_RET(pObjInfo->Attr.u.Unix.gid, pThis->Hdr.Common.gid);
336 pObjInfo->Attr.u.Unix.cHardlinks = 1;
337 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
338 pObjInfo->Attr.u.Unix.INodeId = 0;
339 pObjInfo->Attr.u.Unix.fFlags = 0;
340 pObjInfo->Attr.u.Unix.GenerationId = 0;
341 pObjInfo->Attr.u.Unix.Device = 0;
342 switch (pThis->enmType)
343 {
344 case RTZIPXARTYPE_POSIX:
345 case RTZIPXARTYPE_GNU:
346 if ( pThis->Hdr.Common.typeflag == RTZIPXAR_TF_CHR
347 || pThis->Hdr.Common.typeflag == RTZIPXAR_TF_BLK)
348 {
349 uint32_t uMajor, uMinor;
350 GET_XAR_NUMERIC_FIELD_RET(uMajor, pThis->Hdr.Common.devmajor);
351 GET_XAR_NUMERIC_FIELD_RET(uMinor, pThis->Hdr.Common.devminor);
352 pObjInfo->Attr.u.Unix.Device = RTDEV_MAKE(uMajor, uMinor);
353 if ( uMajor != RTDEV_MAJOR(pObjInfo->Attr.u.Unix.Device)
354 || uMinor != RTDEV_MINOR(pObjInfo->Attr.u.Unix.Device))
355 return VERR_XAR_DEV_VALUE_TOO_LARGE;
356 }
357 break;
358
359 default:
360 if ( pThis->Hdr.Common.typeflag == RTZIPXAR_TF_CHR
361 || pThis->Hdr.Common.typeflag == RTZIPXAR_TF_BLK)
362 return VERR_XAR_UNKNOWN_TYPE_FLAG;
363 }
364
365#undef GET_XAR_NUMERIC_FIELD_RET
366
367 /*
368 * Massage the result a little bit.
369 * Also validate some more now that we've got the numbers to work with.
370 */
371 if ( (pObjInfo->Attr.fMode & ~RTFS_UNIX_MASK)
372 && pThis->enmType == RTZIPXARTYPE_POSIX)
373 return VERR_XAR_BAD_MODE_FIELD;
374 pObjInfo->Attr.fMode &= RTFS_UNIX_MASK;
375
376 RTFMODE fModeType = 0;
377 switch (pThis->Hdr.Common.typeflag)
378 {
379 case RTZIPXAR_TF_OLDNORMAL:
380 case RTZIPXAR_TF_NORMAL:
381 case RTZIPXAR_TF_CONTIG:
382 {
383 const char *pszEnd = strchr(pThis->szName, '\0');
384 if (pszEnd == &pThis->szName[0] || pszEnd[-1] != '/')
385 fModeType |= RTFS_TYPE_FILE;
386 else
387 fModeType |= RTFS_TYPE_DIRECTORY;
388 break;
389 }
390
391 case RTZIPXAR_TF_LINK:
392 if (pObjInfo->cbObject != 0)
393#if 0 /* too strict */
394 return VERR_XAR_SIZE_NOT_ZERO;
395#else
396 pObjInfo->cbObject = pObjInfo->cbAllocated = 0;
397#endif
398 fModeType |= RTFS_TYPE_FILE; /* no better idea for now */
399 break;
400
401 case RTZIPXAR_TF_SYMLINK:
402 fModeType |= RTFS_TYPE_SYMLINK;
403 break;
404
405 case RTZIPXAR_TF_CHR:
406 fModeType |= RTFS_TYPE_DEV_CHAR;
407 break;
408
409 case RTZIPXAR_TF_BLK:
410 fModeType |= RTFS_TYPE_DEV_BLOCK;
411 break;
412
413 case RTZIPXAR_TF_DIR:
414 fModeType |= RTFS_TYPE_DIRECTORY;
415 break;
416
417 case RTZIPXAR_TF_FIFO:
418 fModeType |= RTFS_TYPE_FIFO;
419 break;
420
421 case RTZIPXAR_TF_GNU_LONGLINK:
422 case RTZIPXAR_TF_GNU_LONGNAME:
423 /* ASSUMES RTFS_TYPE_XXX uses the same values as GNU stored in the mode field. */
424 fModeType = pObjInfo->Attr.fMode & RTFS_TYPE_MASK;
425 switch (fModeType)
426 {
427 case RTFS_TYPE_FILE:
428 case RTFS_TYPE_DIRECTORY:
429 case RTFS_TYPE_SYMLINK:
430 case RTFS_TYPE_DEV_BLOCK:
431 case RTFS_TYPE_DEV_CHAR:
432 case RTFS_TYPE_FIFO:
433 break;
434
435 default:
436 case 0:
437 return VERR_XAR_UNKNOWN_TYPE_FLAG; /** @todo new status code */
438 }
439
440 default:
441 return VERR_XAR_UNKNOWN_TYPE_FLAG; /* Should've been caught in validate. */
442 }
443 if ( (pObjInfo->Attr.fMode & RTFS_TYPE_MASK)
444 && (pObjInfo->Attr.fMode & RTFS_TYPE_MASK) != fModeType)
445 return VERR_XAR_MODE_WITH_TYPE;
446 pObjInfo->Attr.fMode &= ~RTFS_TYPE_MASK;
447 pObjInfo->Attr.fMode |= fModeType;
448
449 switch (pThis->Hdr.Common.typeflag)
450 {
451 case RTZIPXAR_TF_CHR:
452 case RTZIPXAR_TF_BLK:
453 case RTZIPXAR_TF_DIR:
454 case RTZIPXAR_TF_FIFO:
455 pObjInfo->cbObject = 0;
456 pObjInfo->cbAllocated = 0;
457 break;
458 }
459#endif
460
461 return VINF_SUCCESS;
462}
463
464
465
466/*
467 *
468 * 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.
469 * 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.
470 * 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.
471 *
472 */
473
474/**
475 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
476 */
477static DECLCALLBACK(int) rtZipXarFssBaseObj_Close(void *pvThis)
478{
479 PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
480
481 /* Currently there is nothing we really have to do here. */
482 NOREF(pThis);
483
484 return VINF_SUCCESS;
485}
486
487
488/**
489 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
490 */
491static DECLCALLBACK(int) rtZipXarFssBaseObj_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
492{
493 PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
494
495#if 0
496 /*
497 * Copy the desired data.
498 */
499 switch (enmAddAttr)
500 {
501 case RTFSOBJATTRADD_NOTHING:
502 case RTFSOBJATTRADD_UNIX:
503 *pObjInfo = pThis->ObjInfo;
504 break;
505
506 case RTFSOBJATTRADD_UNIX_OWNER:
507 *pObjInfo = pThis->ObjInfo;
508 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
509 pObjInfo->Attr.u.UnixOwner.uid = pThis->ObjInfo.Attr.u.Unix.uid;
510 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
511 if (rtZipXarReaderHasUserName(pThis->pXarReader))
512 RTStrCopy(pObjInfo->Attr.u.UnixOwner.szName, sizeof(pObjInfo->Attr.u.UnixOwner.szName),
513 pThis->pXarReader->Hdr.Common.uname);
514 break;
515
516 case RTFSOBJATTRADD_UNIX_GROUP:
517 *pObjInfo = pThis->ObjInfo;
518 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
519 pObjInfo->Attr.u.UnixGroup.gid = pThis->ObjInfo.Attr.u.Unix.gid;
520 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
521 if (rtZipXarReaderHasGroupName(pThis->pXarReader))
522 RTStrCopy(pObjInfo->Attr.u.UnixGroup.szName, sizeof(pObjInfo->Attr.u.UnixGroup.szName),
523 pThis->pXarReader->Hdr.Common.gname);
524 break;
525
526 case RTFSOBJATTRADD_EASIZE:
527 *pObjInfo = pThis->ObjInfo;
528 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
529 RT_ZERO(pObjInfo->Attr.u);
530 break;
531
532 default:
533 return VERR_NOT_SUPPORTED;
534 }
535#endif
536
537 return VINF_SUCCESS;
538}
539
540
541/**
542 * Xar filesystem base object operations.
543 */
544static const RTVFSOBJOPS g_rtZipXarFssBaseObjOps =
545{
546 RTVFSOBJOPS_VERSION,
547 RTVFSOBJTYPE_BASE,
548 "XarFsStream::Obj",
549 rtZipXarFssBaseObj_Close,
550 rtZipXarFssBaseObj_QueryInfo,
551 RTVFSOBJOPS_VERSION
552};
553
554
555/**
556 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
557 */
558static DECLCALLBACK(int) rtZipXarFssIos_Close(void *pvThis)
559{
560 PRTZIPXARIOSTREAM pThis = (PRTZIPXARIOSTREAM)pvThis;
561
562 RTVfsIoStrmRelease(pThis->hVfsIos);
563 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
564
565 return rtZipXarFssBaseObj_Close(&pThis->BaseObj);
566}
567
568
569/**
570 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
571 */
572static DECLCALLBACK(int) rtZipXarFssIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
573{
574 PRTZIPXARIOSTREAM pThis = (PRTZIPXARIOSTREAM)pvThis;
575 return rtZipXarFssBaseObj_QueryInfo(&pThis->BaseObj, pObjInfo, enmAddAttr);
576}
577
578
579/**
580 * Reads one segment.
581 *
582 * @returns IPRT status code.
583 * @param pThis The instance data.
584 * @param pvBuf Where to put the read bytes.
585 * @param cbToRead The number of bytes to read.
586 * @param fBlocking Whether to block or not.
587 * @param pcbRead Where to store the number of bytes actually read.
588 */
589static int rtZipXarFssIos_ReadOneSeg(PRTZIPXARIOSTREAM pThis, void *pvBuf, size_t cbToRead, bool fBlocking, size_t *pcbRead)
590{
591 /*
592 * Fend of reads beyond the end of the stream here.
593 */
594 if (pThis->fEndOfStream)
595 return pcbRead ? VINF_EOF : VERR_EOF;
596
597 Assert(pThis->Data.cbDataExtracted >= pThis->offFile);
598 uint64_t cbLeft = (uint64_t)(pThis->Data.cbDataExtracted - pThis->offFile);
599 if (cbToRead > cbLeft)
600 {
601 if (!pcbRead)
602 return VERR_EOF;
603 cbToRead = (size_t)cbLeft;
604 }
605
606 /*
607 * Do the reading.
608 */
609 size_t cbReadStack = 0;
610 if (!pcbRead)
611 pcbRead = &cbReadStack;
612 int rc = RTVfsIoStrmRead(pThis->hVfsIos, pvBuf, cbToRead, fBlocking, pcbRead);
613 pThis->offFile += *pcbRead;
614 if (pThis->offFile >= pThis->Data.cbDataExtracted)
615 {
616 Assert(pThis->offFile == pThis->Data.cbDataExtracted);
617 pThis->fEndOfStream = true;
618 /// @todo RTVfsIoStrmSkip(pThis->hVfsIos, pThis->cbPadding);
619 }
620
621 return rc;
622}
623
624
625/**
626 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
627 */
628static DECLCALLBACK(int) rtZipXarFssIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
629{
630 PRTZIPXARIOSTREAM pThis = (PRTZIPXARIOSTREAM)pvThis;
631 int rc;
632 AssertReturn(off == -1, VERR_INVALID_PARAMETER);
633
634 if (pSgBuf->cSegs == 1)
635 rc = rtZipXarFssIos_ReadOneSeg(pThis, pSgBuf->paSegs[0].pvSeg, pSgBuf->paSegs[0].cbSeg, fBlocking, pcbRead);
636 else
637 {
638 rc = VINF_SUCCESS;
639 size_t cbRead = 0;
640 size_t cbReadSeg;
641 size_t *pcbReadSeg = pcbRead ? &cbReadSeg : NULL;
642 for (uint32_t iSeg = 0; iSeg < pSgBuf->cSegs; iSeg++)
643 {
644 cbReadSeg = 0;
645 rc = rtZipXarFssIos_ReadOneSeg(pThis, pSgBuf->paSegs[iSeg].pvSeg, pSgBuf->paSegs[iSeg].cbSeg, fBlocking, pcbReadSeg);
646 if (RT_FAILURE(rc))
647 break;
648 if (pcbRead)
649 {
650 cbRead += cbReadSeg;
651 if (cbReadSeg != pSgBuf->paSegs[iSeg].cbSeg)
652 break;
653 }
654 }
655 if (pcbRead)
656 *pcbRead = cbRead;
657 }
658
659 return rc;
660}
661
662
663/**
664 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
665 */
666static DECLCALLBACK(int) rtZipXarFssIos_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
667{
668 /* Cannot write to a read-only I/O stream. */
669 NOREF(pvThis); NOREF(off); NOREF(pSgBuf); NOREF(fBlocking); NOREF(pcbWritten);
670 return VERR_ACCESS_DENIED;
671}
672
673
674/**
675 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
676 */
677static DECLCALLBACK(int) rtZipXarFssIos_Flush(void *pvThis)
678{
679 /* It's a read only stream, nothing dirty to flush. */
680 NOREF(pvThis);
681 return VINF_SUCCESS;
682}
683
684
685/**
686 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
687 */
688static DECLCALLBACK(int) rtZipXarFssIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
689 uint32_t *pfRetEvents)
690{
691 PRTZIPXARIOSTREAM pThis = (PRTZIPXARIOSTREAM)pvThis;
692
693 /* When we've reached the end, read will be set to indicate it. */
694 if ( (fEvents & RTPOLL_EVT_READ)
695 && pThis->fEndOfStream)
696 {
697 int rc = RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, 0, fIntr, pfRetEvents);
698 if (RT_SUCCESS(rc))
699 *pfRetEvents |= RTPOLL_EVT_READ;
700 else
701 *pfRetEvents = RTPOLL_EVT_READ;
702 return VINF_SUCCESS;
703 }
704
705 return RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents);
706}
707
708
709/**
710 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
711 */
712static DECLCALLBACK(int) rtZipXarFssIos_Tell(void *pvThis, PRTFOFF poffActual)
713{
714 PRTZIPXARIOSTREAM pThis = (PRTZIPXARIOSTREAM)pvThis;
715 *poffActual = pThis->offFile;
716 return VINF_SUCCESS;
717}
718
719
720/**
721 * Xar I/O stream operations.
722 */
723static const RTVFSIOSTREAMOPS g_rtZipXarFssIosOps =
724{
725 { /* Obj */
726 RTVFSOBJOPS_VERSION,
727 RTVFSOBJTYPE_IO_STREAM,
728 "XarFsStream::IoStream",
729 rtZipXarFssIos_Close,
730 rtZipXarFssIos_QueryInfo,
731 RTVFSOBJOPS_VERSION
732 },
733 RTVFSIOSTREAMOPS_VERSION,
734 0,
735 rtZipXarFssIos_Read,
736 rtZipXarFssIos_Write,
737 rtZipXarFssIos_Flush,
738 rtZipXarFssIos_PollOne,
739 rtZipXarFssIos_Tell,
740 NULL /*Skip*/,
741 NULL /*ZeroFill*/,
742 RTVFSIOSTREAMOPS_VERSION
743};
744
745
746/**
747 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
748 */
749static DECLCALLBACK(int) rtZipXarFssSym_Close(void *pvThis)
750{
751 PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
752 return rtZipXarFssBaseObj_Close(pThis);
753}
754
755
756/**
757 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
758 */
759static DECLCALLBACK(int) rtZipXarFssSym_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
760{
761 PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
762 return rtZipXarFssBaseObj_QueryInfo(pThis, pObjInfo, enmAddAttr);
763}
764
765/**
766 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
767 */
768static DECLCALLBACK(int) rtZipXarFssSym_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
769{
770 NOREF(pvThis); NOREF(fMode); NOREF(fMask);
771 return VERR_ACCESS_DENIED;
772}
773
774
775/**
776 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
777 */
778static DECLCALLBACK(int) rtZipXarFssSym_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
779 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
780{
781 NOREF(pvThis); NOREF(pAccessTime); NOREF(pModificationTime); NOREF(pChangeTime); NOREF(pBirthTime);
782 return VERR_ACCESS_DENIED;
783}
784
785
786/**
787 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
788 */
789static DECLCALLBACK(int) rtZipXarFssSym_SetOwner(void *pvThis, RTUID uid, RTGID gid)
790{
791 NOREF(pvThis); NOREF(uid); NOREF(gid);
792 return VERR_ACCESS_DENIED;
793}
794
795
796/**
797 * @interface_method_impl{RTVFSSYMLINKOPS,pfnRead}
798 */
799static DECLCALLBACK(int) rtZipXarFssSym_Read(void *pvThis, char *pszTarget, size_t cbXarget)
800{
801 PRTZIPXARBASEOBJ pThis = (PRTZIPXARBASEOBJ)pvThis;
802#if 0
803 return RTStrCopy(pszTarget, cbXarget, pThis->pXarReader->szTarget);
804#else
805 return VERR_NOT_IMPLEMENTED;
806#endif
807}
808
809
810/**
811 * Xar symbolic (and hardlink) operations.
812 */
813static const RTVFSSYMLINKOPS g_rtZipXarFssSymOps =
814{
815 { /* Obj */
816 RTVFSOBJOPS_VERSION,
817 RTVFSOBJTYPE_SYMLINK,
818 "XarFsStream::Symlink",
819 rtZipXarFssSym_Close,
820 rtZipXarFssSym_QueryInfo,
821 RTVFSOBJOPS_VERSION
822 },
823 RTVFSSYMLINKOPS_VERSION,
824 0,
825 { /* ObjSet */
826 RTVFSOBJSETOPS_VERSION,
827 RT_OFFSETOF(RTVFSSYMLINKOPS, Obj) - RT_OFFSETOF(RTVFSSYMLINKOPS, ObjSet),
828 rtZipXarFssSym_SetMode,
829 rtZipXarFssSym_SetTimes,
830 rtZipXarFssSym_SetOwner,
831 RTVFSOBJSETOPS_VERSION
832 },
833 rtZipXarFssSym_Read,
834 RTVFSSYMLINKOPS_VERSION
835};
836
837
838/**
839 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
840 */
841static DECLCALLBACK(int) rtZipXarFss_Close(void *pvThis)
842{
843 PRTZIPXARFSSTREAM pThis = (PRTZIPXARFSSTREAM)pvThis;
844
845 RTVfsObjRelease(pThis->hVfsCurObj);
846 pThis->hVfsCurObj = NIL_RTVFSOBJ;
847 pThis->pCurIosData = NULL;
848
849 RTVfsIoStrmRelease(pThis->hVfsIos);
850 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
851
852 RTVfsFileRelease(pThis->hVfsFile);
853 pThis->hVfsFile = NIL_RTVFSFILE;
854
855 return VINF_SUCCESS;
856}
857
858
859/**
860 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
861 */
862static DECLCALLBACK(int) rtZipXarFss_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
863{
864 PRTZIPXARFSSTREAM pThis = (PRTZIPXARFSSTREAM)pvThis;
865 /* Take the lazy approach here, with the sideffect of providing some info
866 that is actually kind of useful. */
867 return RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
868}
869
870
871static xml::ElementNode const *rtZipXarGetNextFileElement(xml::ElementNode const *pCurFile, uint32_t *pcCurDepth)
872{
873 /*
874 * Consider children first.
875 */
876 xml::ElementNode const *pChild = pCurFile->findChildElement("file");
877 if (pChild)
878 {
879 *pcCurDepth += 1;
880 return pChild;
881 }
882
883 /*
884 * Then siblings.
885 */
886 xml::ElementNode const *pSibling = pCurFile->findNextSibilingElement("file");
887 if (pSibling != NULL)
888 return pSibling;
889
890 /*
891 * Ascend and see if some parent has more siblings.
892 */
893 while (*pcCurDepth > 0)
894 {
895 pCurFile = static_cast<const xml::ElementNode *>(pCurFile->getParent());
896 AssertBreak(pCurFile);
897 Assert(pCurFile->nameEquals("file"));
898 *pcCurDepth -= 1;
899
900 pSibling = pCurFile->findNextSibilingElement("file");
901 if (pSibling != NULL)
902 return pSibling;
903 }
904
905 return NULL;
906}
907
908
909/**
910 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnNext}
911 */
912static DECLCALLBACK(int) rtZipXarFss_Next(void *pvThis, char **ppszName, RTVFSOBJTYPE *penmType, PRTVFSOBJ phVfsObj)
913{
914 PRTZIPXARFSSTREAM pThis = (PRTZIPXARFSSTREAM)pvThis;
915
916 /*
917 * Dispense with the current object.
918 */
919 if (pThis->hVfsCurObj != NIL_RTVFSOBJ)
920 {
921 if (pThis->pCurIosData)
922 {
923 pThis->pCurIosData->fEndOfStream = true;
924/// @todo pThis->pCurIosData->offFile = pThis->pCurIosData->cbFile;
925 pThis->pCurIosData = NULL;
926 }
927
928 RTVfsObjRelease(pThis->hVfsCurObj);
929 pThis->hVfsCurObj = NIL_RTVFSOBJ;
930 }
931
932 /*
933 * Check if we've already reached the end in some way.
934 */
935 if (pThis->fEndOfStream)
936 return VERR_EOF;
937 if (pThis->rcFatal != VINF_SUCCESS)
938 return pThis->rcFatal;
939
940 /*
941 * Get the next file element.
942 */
943 if (pThis->XarReader.fFirstFile)
944 {
945 pThis->XarReader.pCurFile = pThis->XarReader.pToc->findChildElement("file");
946 pThis->XarReader.cCurDepth = 0;
947 }
948 else if (pThis->XarReader.pCurFile)
949 pThis->XarReader.pCurFile = rtZipXarGetNextFileElement(pThis->XarReader.pCurFile, &pThis->XarReader.cCurDepth);
950 if (!pThis->XarReader.pCurFile)
951 {
952 pThis->fEndOfStream = true;
953 return VERR_EOF;
954 }
955
956 /*
957 * Retrive the fundamental attributes (elements actually).
958 */
959 const char *pszName = pThis->XarReader.pCurFile->findChildElementValueP("name");
960 const char *pszType = pThis->XarReader.pCurFile->findChildElementValueP("type");
961 if (RT_UNLIKELY(!pszName || !pszType))
962 return pThis->rcFatal = VERR_XAR_BAD_FILE_ELEMENT;
963
964 /*
965 * Gather any additional attributes that are essential to the file type,
966 * then create the VFS object we're going to return.
967 */
968 int rc;
969 RTVFSOBJ hVfsObj;
970 RTVFSOBJTYPE enmType;
971 if (!strcmp(pszType, "file"))
972 {
973#if 0 /// @todo continue hacking here
974 rc = rtZipXarGetDataStreamAttributes(pThis->XarReader.pCurFile, &offData, &cbData);
975 if (RT_FAILURE(rc))
976 return pThis->rcFatal = rc;
977
978
979 if (pThis->hVfsFile != NIL_RTVFSFILE)
980 {
981
982 }
983 else
984 {
985 RTVFSIOSTREAM hVfsIos;
986 PRTZIPXARIOSTREAM pIosData;
987 rc = RTVfsNewIoStream(&g_rtZipXarFssIosOps,
988 sizeof(*pIosData),
989 RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
990 NIL_RTVFS,
991 NIL_RTVFSLOCK,
992 &hVfsIos,
993 (void **)&pIosData);
994 if (RT_FAILURE(rc))
995 return pThis->rcFatal = rc;
996
997 pIosData->BaseObj.pXarReader = &pThis->XarReader;
998 pIosData->BaseObj.pFileElem = pThis->XarReader.pCurFile;
999 pIosData->cbFile = cbData;
1000 pIosData->offFile = 0;
1001 pIosData->cbPadding = (uint32_t)(Info.cbAllocated - Info.cbObject);
1002 pIosData->fEndOfStream = false;
1003 pIosData->hVfsIos = pThis->hVfsIos;
1004 RTVfsIoStrmRetain(pThis->hVfsIos);
1005
1006 pThis->pCurIosData = pIosData;
1007 pThis->offNextHdr += pIosData->cbFile + pIosData->cbPadding;
1008
1009 enmType = RTVFSOBJTYPE_IO_STREAM;
1010 hVfsObj = RTVfsObjFromIoStream(hVfsIos);
1011 RTVfsIoStrmRelease(hVfsIos);
1012 }
1013#endif
1014 }
1015 else if (!strcmp(pszType, "directory"))
1016 {
1017 PRTZIPXARBASEOBJ pBaseObjData;
1018 rc = RTVfsNewBaseObj(&g_rtZipXarFssBaseObjOps,
1019 sizeof(*pBaseObjData),
1020 NIL_RTVFS,
1021 NIL_RTVFSLOCK,
1022 &hVfsObj,
1023 (void **)&pBaseObjData);
1024 if (RT_FAILURE(rc))
1025 return pThis->rcFatal = rc;
1026
1027 pBaseObjData->pXarReader = &pThis->XarReader;
1028 pBaseObjData->pFileElem = pThis->XarReader.pCurFile;
1029
1030 enmType = RTVFSOBJTYPE_BASE;
1031 }
1032 else
1033 return pThis->rcFatal = VERR_XAR_BAD_UNKNOWN_FILE_TYPE;
1034
1035#if 0
1036 /*
1037 * Consume XAR headers.
1038 */
1039 size_t cbHdrs = 0;
1040 int rc;
1041 do
1042 {
1043 /*
1044 * Read the next header.
1045 */
1046 RTZIPXARHDR Hdr;
1047 size_t cbRead;
1048 rc = RTVfsIoStrmRead(pThis->hVfsIos, &Hdr, sizeof(Hdr), true /*fBlocking*/, &cbRead);
1049 if (RT_FAILURE(rc))
1050 return pThis->rcFatal = rc;
1051 if (rc == VINF_EOF && cbRead == 0)
1052 {
1053 pThis->fEndOfStream = true;
1054 return rtZipXarReaderIsAtEnd(&pThis->XarReader) ? VERR_EOF : VERR_XAR_UNEXPECTED_EOS;
1055 }
1056 if (cbRead != sizeof(Hdr))
1057 return pThis->rcFatal = VERR_XAR_UNEXPECTED_EOS;
1058
1059 cbHdrs += sizeof(Hdr);
1060
1061 /*
1062 * Parse the it.
1063 */
1064 rc = rtZipXarReaderParseHeader(&pThis->XarReader, &Hdr);
1065 if (RT_FAILURE(rc))
1066 return pThis->rcFatal = rc;
1067 } while (rtZipXarReaderExpectingMoreHeaders(&pThis->XarReader));
1068
1069 pThis->offNextHdr = offHdr + cbHdrs;
1070
1071 /*
1072 * Fill an object info structure from the current XAR state.
1073 */
1074 RTFSOBJINFO Info;
1075 rc = rtZipXarReaderGetFsObjInfo(&pThis->XarReader, &Info);
1076 if (RT_FAILURE(rc))
1077 return pThis->rcFatal = rc;
1078
1079 /*
1080 * Create an object of the appropriate type.
1081 */
1082 RTVFSOBJTYPE enmType;
1083 RTVFSOBJ hVfsObj;
1084 RTFMODE fType = Info.Attr.fMode & RTFS_TYPE_MASK;
1085 if (rtZipXarReaderIsHardlink(&pThis->XarReader))
1086 fType = RTFS_TYPE_SYMLINK;
1087 switch (fType)
1088 {
1089 /*
1090 * Files are represented by a VFS I/O stream.
1091 */
1092 case RTFS_TYPE_FILE:
1093 {
1094 break;
1095 }
1096
1097 /*
1098 * We represent hard links using a symbolic link object. This fits
1099 * best with the way XAR stores it and there is currently no better
1100 * fitting VFS type alternative.
1101 */
1102 case RTFS_TYPE_SYMLINK:
1103 {
1104 RTVFSSYMLINK hVfsSym;
1105 PRTZIPXARBASEOBJ pBaseObjData;
1106 rc = RTVfsNewSymlink(&g_rtZipXarFssSymOps,
1107 sizeof(*pBaseObjData),
1108 NIL_RTVFS,
1109 NIL_RTVFSLOCK,
1110 &hVfsSym,
1111 (void **)&pBaseObjData);
1112 if (RT_FAILURE(rc))
1113 return pThis->rcFatal = rc;
1114
1115 pBaseObjData->offHdr = offHdr;
1116 pBaseObjData->pXarReader= &pThis->XarReader;
1117 pBaseObjData->ObjInfo = Info;
1118
1119 enmType = RTVFSOBJTYPE_SYMLINK;
1120 hVfsObj = RTVfsObjFromSymlink(hVfsSym);
1121 RTVfsSymlinkRelease(hVfsSym);
1122 break;
1123 }
1124
1125 /*
1126 * All other objects are repesented using a VFS base object since they
1127 * carry no data streams (unless some XAR extension implements extended
1128 * attributes / alternative streams).
1129 */
1130 case RTFS_TYPE_DEV_BLOCK:
1131 case RTFS_TYPE_DEV_CHAR:
1132 case RTFS_TYPE_DIRECTORY:
1133 case RTFS_TYPE_FIFO:
1134 {
1135 PRTZIPXARBASEOBJ pBaseObjData;
1136 rc = RTVfsNewBaseObj(&g_rtZipXarFssBaseObjOps,
1137 sizeof(*pBaseObjData),
1138 NIL_RTVFS,
1139 NIL_RTVFSLOCK,
1140 &hVfsObj,
1141 (void **)&pBaseObjData);
1142 if (RT_FAILURE(rc))
1143 return pThis->rcFatal = rc;
1144
1145 pBaseObjData->offHdr = offHdr;
1146 pBaseObjData->pXarReader= &pThis->XarReader;
1147 pBaseObjData->ObjInfo = Info;
1148
1149 enmType = RTVFSOBJTYPE_BASE;
1150 break;
1151 }
1152
1153 default:
1154 AssertFailed();
1155 return pThis->rcFatal = VERR_INTERNAL_ERROR_5;
1156 }
1157 pThis->hVfsCurObj = hVfsObj;
1158#endif
1159
1160 /*
1161 * Set the return data and we're done.
1162 */
1163 if (ppszName)
1164 {
1165 rc = RTStrDupEx(ppszName, pszName); /** @todo fixme */
1166 if (RT_FAILURE(rc))
1167 return rc;
1168 }
1169
1170 if (phVfsObj)
1171 {
1172 RTVfsObjRetain(hVfsObj);
1173 *phVfsObj = hVfsObj;
1174 }
1175
1176 if (penmType)
1177 *penmType = enmType;
1178
1179 return VINF_SUCCESS;
1180}
1181
1182
1183
1184/**
1185 * Xar filesystem stream operations.
1186 */
1187static const RTVFSFSSTREAMOPS rtZipXarFssOps =
1188{
1189 { /* Obj */
1190 RTVFSOBJOPS_VERSION,
1191 RTVFSOBJTYPE_FS_STREAM,
1192 "XarFsStream",
1193 rtZipXarFss_Close,
1194 rtZipXarFss_QueryInfo,
1195 RTVFSOBJOPS_VERSION
1196 },
1197 RTVFSFSSTREAMOPS_VERSION,
1198 0,
1199 rtZipXarFss_Next,
1200 RTVFSFSSTREAMOPS_VERSION
1201};
1202
1203
1204
1205/**
1206 * TOC validation part 2.
1207 *
1208 * Will advance the input stream past the TOC hash and signature data.
1209 *
1210 * @returns IPRT status code.
1211 * @param pThis The FS stream instance being created.
1212 * @param pXarHdr The XAR header.
1213 * @param pTocDigest The TOC input data digest.
1214 */
1215static int rtZipXarValidateTocPart2(PRTZIPXARFSSTREAM pThis, PCXARHEADER pXarHdr, PCRTZIPXARHASHDIGEST pTocDigest)
1216{
1217 int rc;
1218
1219 /*
1220 * Check that the hash function in the TOC matches the one in the XAR header.
1221 */
1222 const xml::ElementNode *pChecksumElem = pThis->XarReader.pToc->findChildElement("checksum");
1223 if (pChecksumElem)
1224 {
1225 const xml::AttributeNode *pAttr = pChecksumElem->findAttribute("style");
1226 if (!pAttr)
1227 return VERR_XAR_BAD_CHECKSUM_ELEMENT;
1228
1229 const char *pszStyle = pAttr->getValue();
1230 if (!pszStyle)
1231 return VERR_XAR_BAD_CHECKSUM_ELEMENT;
1232
1233 uint8_t uHashFunction;
1234 if (!strcmp(pszStyle, "sha1"))
1235 uHashFunction = XAR_HASH_SHA1;
1236 else if (!strcmp(pszStyle, "md5"))
1237 uHashFunction = XAR_HASH_MD5;
1238 else if (!strcmp(pszStyle, "none"))
1239 uHashFunction = XAR_HASH_NONE;
1240 else
1241 return VERR_XAR_BAD_CHECKSUM_ELEMENT;
1242 if (uHashFunction != pThis->uHashFunction)
1243 return VERR_XAR_HASH_FUNCTION_MISMATCH;
1244
1245 /*
1246 * Verify the checksum if we got one.
1247 */
1248 if (pThis->uHashFunction != XAR_HASH_NONE)
1249 {
1250 RTFOFF offChecksum;
1251 uint64_t cbChecksum;
1252 rc = rtZipXarGetOffsetSizeFromElem(pChecksumElem, &offChecksum, &cbChecksum);
1253 if (RT_FAILURE(rc))
1254 return rc;
1255 if (cbChecksum != pThis->cbHashDigest)
1256 return VERR_XAR_BAD_DIGEST_LENGTH;
1257 if (offChecksum != 0 && pThis->hVfsFile == NIL_RTVFSFILE)
1258 return VERR_XAR_NOT_STREAMBLE_ELEMENT_ORDER;
1259
1260 RTZIPXARHASHDIGEST StoredDigest;
1261 rc = RTVfsIoStrmReadAt(pThis->hVfsIos, pThis->offZero + offChecksum, &StoredDigest, cbChecksum,
1262 true /*fBlocking*/, NULL /*pcbRead*/);
1263 if (RT_FAILURE(rc))
1264 return rc;
1265 if (memcmp(&StoredDigest, pTocDigest, pThis->cbHashDigest))
1266 return VERR_XAR_TOC_DIGEST_MISMATCH;
1267 }
1268 }
1269 else if (pThis->uHashFunction != XAR_HASH_NONE)
1270 return VERR_XAR_BAD_CHECKSUM_ELEMENT;
1271
1272 /*
1273 * Check the signature, if we got one.
1274 */
1275 /** @todo signing. */
1276
1277 return VINF_SUCCESS;
1278}
1279
1280
1281/**
1282 * Reads and validates the table of content.
1283 *
1284 * @returns IPRT status code.
1285 * @param hVfsIosIn The input stream.
1286 * @param pXarHdr The XAR header.
1287 * @param pDoc The TOC XML document.
1288 * @param ppTocElem Where to return the pointer to the TOC element on
1289 * success.
1290 * @param pTocDigest Where to return the TOC digest on success.
1291 */
1292static int rtZipXarReadAndValidateToc(RTVFSIOSTREAM hVfsIosIn, PCXARHEADER pXarHdr,
1293 xml::Document *pDoc, xml::ElementNode const **ppTocElem, PRTZIPXARHASHDIGEST pTocDigest)
1294{
1295 /*
1296 * Decompress it, calculating the hash while doing so.
1297 */
1298 char *pszOutput = (char *)RTMemTmpAlloc(pXarHdr->cbTocUncompressed + 1);
1299 if (!pszOutput)
1300 return VERR_NO_TMP_MEMORY;
1301 int rc = VERR_NO_TMP_MEMORY;
1302 void *pvInput = RTMemTmpAlloc(pXarHdr->cbTocCompressed);
1303 if (pvInput)
1304 {
1305 rc = RTVfsIoStrmRead(hVfsIosIn, pvInput, pXarHdr->cbTocCompressed, true /*fBlocking*/, NULL);
1306 if (RT_SUCCESS(rc))
1307 {
1308 rtZipXarCalcHash(pXarHdr->uHashFunction, pvInput, pXarHdr->cbTocCompressed, pTocDigest);
1309
1310 size_t cbActual;
1311 rc = RTZipBlockDecompress(RTZIPTYPE_ZLIB, 0 /*fFlags*/,
1312 pvInput, pXarHdr->cbTocCompressed, NULL,
1313 pszOutput, pXarHdr->cbTocUncompressed, &cbActual);
1314 if (RT_SUCCESS(rc) && cbActual != pXarHdr->cbTocUncompressed)
1315 rc = VERR_XAR_TOC_UNCOMP_SIZE_MISMATCH;
1316 }
1317 RTMemTmpFree(pvInput);
1318 }
1319 if (RT_SUCCESS(rc))
1320 {
1321 pszOutput[pXarHdr->cbTocUncompressed] = '\0';
1322
1323 /*
1324 * Parse the TOC (XML document) and do some basic validations.
1325 */
1326 size_t cchToc = strlen(pszOutput);
1327 if ( cchToc == pXarHdr->cbTocUncompressed
1328 || cchToc + 1 == pXarHdr->cbTocUncompressed)
1329 {
1330 rc = RTStrValidateEncoding(pszOutput);
1331 if (RT_SUCCESS(rc))
1332 {
1333 xml::XmlMemParser Parser;
1334 try
1335 {
1336 Parser.read(pszOutput, cchToc, RTCString("xar-toc.xml"), *pDoc);
1337 }
1338 catch (xml::XmlError Err)
1339 {
1340 rc = VERR_XAR_TOC_XML_PARSE_ERROR;
1341 }
1342 catch (...)
1343 {
1344 rc = VERR_NO_MEMORY;
1345 }
1346 if (RT_SUCCESS(rc))
1347 {
1348 xml::ElementNode const *pRootElem = pDoc->getRootElement();
1349 xml::ElementNode const *pTocElem = NULL;
1350 if (pRootElem && pRootElem->nameEquals("xar"))
1351 pTocElem = pRootElem ? pRootElem->findChildElement(NULL, "toc") : NULL;
1352 if (pTocElem)
1353 {
1354#ifndef USE_STD_LIST_FOR_CHILDREN
1355 Assert(pRootElem->getParent() == NULL);
1356 Assert(pTocElem->getParent() == pRootElem);
1357 if ( !pTocElem->getNextSibiling()
1358 && !pTocElem->getPrevSibiling())
1359#endif
1360 {
1361 /*
1362 * Further parsing and validation is done after the
1363 * caller has created an file system stream instance.
1364 */
1365 *ppTocElem = pTocElem;
1366
1367 RTMemTmpFree(pszOutput);
1368 return VINF_SUCCESS;
1369 }
1370
1371 rc = VERR_XML_TOC_ELEMENT_HAS_SIBLINGS;
1372 }
1373 else
1374 rc = VERR_XML_TOC_ELEMENT_MISSING;
1375 }
1376 }
1377 else
1378 rc = VERR_XAR_TOC_UTF8_ENCODING;
1379 }
1380 else
1381 rc = VERR_XAR_TOC_STRLEN_MISMATCH;
1382 }
1383
1384 RTMemTmpFree(pszOutput);
1385 return rc;
1386}
1387
1388
1389/**
1390 * Reads and validates the XAR header.
1391 *
1392 * @returns IPRT status code.
1393 * @param hVfsIosIn The input stream.
1394 * @param pXarHdr Where to return the XAR header in host byte order.
1395 */
1396static int rtZipXarReadAndValidateHeader(RTVFSIOSTREAM hVfsIosIn, PXARHEADER pXarHdr)
1397{
1398 /*
1399 * Read it and check the signature.
1400 */
1401 int rc = RTVfsIoStrmRead(hVfsIosIn, pXarHdr, sizeof(*pXarHdr), true /*fBlocking*/, NULL);
1402 if (RT_FAILURE(rc))
1403 return rc;
1404 if (pXarHdr->u32Magic != XAR_HEADER_MAGIC)
1405 return VERR_XAR_WRONG_MAGIC;
1406
1407 /*
1408 * Correct the byte order.
1409 */
1410 pXarHdr->cbHeader = RT_BE2H_U16(pXarHdr->cbHeader);
1411 pXarHdr->uVersion = RT_BE2H_U16(pXarHdr->uVersion);
1412 pXarHdr->cbTocCompressed = RT_BE2H_U64(pXarHdr->cbTocCompressed);
1413 pXarHdr->cbTocUncompressed = RT_BE2H_U64(pXarHdr->cbTocUncompressed);
1414 pXarHdr->uHashFunction = RT_BE2H_U32(pXarHdr->uHashFunction);
1415
1416 /*
1417 * Validate the header.
1418 */
1419 if (pXarHdr->uVersion > XAR_HEADER_VERSION)
1420 return VERR_XAR_UNSUPPORTED_VERSION;
1421 if (pXarHdr->cbHeader < sizeof(XARHEADER))
1422 return VERR_XAR_BAD_HDR_SIZE;
1423 if (pXarHdr->uHashFunction > XAR_HASH_MAX)
1424 return VERR_XAR_UNSUPPORTED_HASH_FUNCTION;
1425 if (pXarHdr->cbTocUncompressed < 16)
1426 return VERR_XAR_TOC_TOO_SMALL;
1427 if (pXarHdr->cbTocUncompressed > _4M)
1428 return VERR_XAR_TOC_TOO_BIG;
1429 if (pXarHdr->cbTocCompressed > _4M)
1430 return VERR_XAR_TOC_TOO_BIG_COMPRESSED;
1431
1432 /*
1433 * Skip over bytes we don't understand (could be padding).
1434 */
1435 if (pXarHdr->cbHeader > sizeof(XARHEADER))
1436 {
1437 rc = RTVfsIoStrmSkip(hVfsIosIn, pXarHdr->cbHeader - sizeof(XARHEADER));
1438 if (RT_FAILURE(rc))
1439 return rc;
1440 }
1441
1442 return VINF_SUCCESS;
1443}
1444
1445
1446RTDECL(int) RTZipXarFsStreamFromIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
1447{
1448 /*
1449 * Input validation.
1450 */
1451 AssertPtrReturn(phVfsFss, VERR_INVALID_HANDLE);
1452 *phVfsFss = NIL_RTVFSFSSTREAM;
1453 AssertPtrReturn(hVfsIosIn, VERR_INVALID_HANDLE);
1454 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
1455
1456 RTFOFF const offStart = RTVfsIoStrmTell(hVfsIosIn);
1457 AssertReturn(offStart >= 0, (int)offStart);
1458
1459 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosIn);
1460 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
1461
1462 /*
1463 * Read and validate the header, then uncompress the TOC.
1464 */
1465 XARHEADER XarHdr;
1466 int rc = rtZipXarReadAndValidateHeader(hVfsIosIn, &XarHdr);
1467 if (RT_SUCCESS(rc))
1468 {
1469 xml::Document *pDoc = NULL;
1470 try { pDoc = new xml::Document(); }
1471 catch (...) { }
1472 if (pDoc)
1473 {
1474 RTZIPXARHASHDIGEST TocDigest;
1475 xml::ElementNode const *pTocElem = NULL;
1476 rc = rtZipXarReadAndValidateToc(hVfsIosIn, &XarHdr, pDoc, &pTocElem, &TocDigest);
1477 if (RT_SUCCESS(rc))
1478 {
1479 size_t offZero = RTVfsIoStrmTell(hVfsIosIn);
1480 if (offZero > 0)
1481 {
1482 /*
1483 * Create a file system stream before we continue the parsing.
1484 */
1485 PRTZIPXARFSSTREAM pThis;
1486 RTVFSFSSTREAM hVfsFss;
1487 rc = RTVfsNewFsStream(&rtZipXarFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFss, (void **)&pThis);
1488 if (RT_SUCCESS(rc))
1489 {
1490 pThis->hVfsIos = hVfsIosIn;
1491 pThis->hVfsFile = RTVfsIoStrmToFile(hVfsIosIn);
1492 pThis->hVfsCurObj = NIL_RTVFSOBJ;
1493 pThis->pCurIosData = NULL;
1494 pThis->offStart = offStart;
1495 pThis->offZero = offZero;
1496 pThis->uHashFunction = (uint8_t)XarHdr.uHashFunction;
1497 switch (pThis->uHashFunction)
1498 {
1499 case XAR_HASH_MD5: pThis->cbHashDigest = sizeof(TocDigest.abMd5); break;
1500 case XAR_HASH_SHA1: pThis->cbHashDigest = sizeof(TocDigest.abSha1); break;
1501 default: pThis->cbHashDigest = 0; break;
1502 }
1503 pThis->fEndOfStream = false;
1504 pThis->rcFatal = VINF_SUCCESS;
1505 pThis->XarReader.pDoc = pDoc;
1506 pThis->XarReader.pToc = pTocElem;
1507 pThis->XarReader.pCurFile = 0;
1508 pThis->XarReader.cCurDepth = 0;
1509 pThis->XarReader.fFirstFile = true;
1510
1511 /*
1512 * Next validation step.
1513 */
1514 rc = rtZipXarValidateTocPart2(pThis, &XarHdr, &TocDigest);
1515 if (RT_SUCCESS(rc))
1516 {
1517 *phVfsFss = hVfsFss;
1518 return VINF_SUCCESS;
1519 }
1520
1521 RTVfsFsStrmRelease(hVfsFss);
1522 return rc;
1523 }
1524 }
1525 else
1526 rc = (int)offZero;
1527 }
1528 delete pDoc;
1529 }
1530 else
1531 rc = VERR_NO_MEMORY;
1532 }
1533
1534 RTVfsIoStrmRelease(hVfsIosIn);
1535 return rc;
1536}
1537
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