VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/zip/tarvfs.cpp@ 34381

最後變更 在這個檔案從34381是 34181,由 vboxsync 提交於 14 年 前

spaces

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 48.3 KB
 
1/* $Id: tarvfs.cpp 34181 2010-11-18 17:00:45Z vboxsync $ */
2/** @file
3 * IPRT - TAR Virtual Filesystem.
4 */
5
6/*
7 * Copyright (C) 2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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/poll.h>
39#include <iprt/file.h>
40#include <iprt/string.h>
41#include <iprt/vfs.h>
42#include <iprt/vfslowlevel.h>
43
44#include "tar.h"
45
46
47/*******************************************************************************
48* Structures and Typedefs *
49*******************************************************************************/
50/**
51 * TAR reader state machine states.
52 */
53typedef enum RTZIPTARREADERSTATE
54{
55 /** Invalid state. */
56 RTZIPTARREADERSTATE_INVALID = 0,
57 /** Expecting the next file/dir/whatever entry. */
58 RTZIPTARREADERSTATE_FIRST,
59 /** Expecting more zero headers or the end of the stream. */
60 RTZIPTARREADERSTATE_ZERO,
61 /** Expecting a GNU long name. */
62 RTZIPTARREADERSTATE_GNU_LONGNAME,
63 /** Expecting a GNU long link. */
64 RTZIPTARREADERSTATE_GNU_LONGLINK,
65 /** Expecting a normal header or another GNU specific one. */
66 RTZIPTARREADERSTATE_GNU_NEXT,
67 /** End of valid states (not included). */
68 RTZIPTARREADERSTATE_END
69} RTZIPTARREADERSTATE;
70
71/**
72 * Tar reader instance data.
73 */
74typedef struct RTZIPTARREADER
75{
76 /** Zero header counter. */
77 uint32_t cZeroHdrs;
78 /** The state machine state. */
79 RTZIPTARREADERSTATE enmState;
80 /** The type of the previous TAR header. */
81 RTZIPTARTYPE enmPrevType;
82 /** The type of the current TAR header. */
83 RTZIPTARTYPE enmType;
84 /** The current header. */
85 RTZIPTARHDR Hdr;
86 /** The expected long name/link length (GNU). */
87 uint32_t cbGnuLongExpect;
88 /** The current long name/link length (GNU). */
89 uint32_t offGnuLongCur;
90 /** The name of the current object.
91 * This is for handling GNU and PAX long names. */
92 char szName[RTPATH_MAX];
93 /** The current link target if symlink or hardlink. */
94 char szTarget[RTPATH_MAX];
95} RTZIPTARREADER;
96/** Pointer to the TAR reader instance data. */
97typedef RTZIPTARREADER *PRTZIPTARREADER;
98
99/**
100 * Tar directory, character device, block device, fifo socket or symbolic link.
101 */
102typedef struct RTZIPTARBASEOBJ
103{
104 /** The stream offset of the (first) header. */
105 RTFOFF offHdr;
106 /** Pointer to the reader instance data (resides in the filesystem
107 * stream).
108 * @todo Fix this so it won't go stale... Back ref from this obj to fss? */
109 PRTZIPTARREADER pTarReader;
110 /** The object info with unix attributes. */
111 RTFSOBJINFO ObjInfo;
112} RTZIPTARBASEOBJ;
113/** Pointer to a TAR filesystem stream base object. */
114typedef RTZIPTARBASEOBJ *PRTZIPTARBASEOBJ;
115
116
117/**
118 * Tar file represented as a VFS I/O stream.
119 */
120typedef struct RTZIPTARIOSTREAM
121{
122 /** The basic TAR object data. */
123 RTZIPTARBASEOBJ BaseObj;
124 /** The number of bytes in the file. */
125 RTFOFF cbFile;
126 /** The current file position. */
127 RTFOFF offFile;
128 /** The number of padding bytes following the file. */
129 uint32_t cbPadding;
130 /** Set if we've reached the end of the file. */
131 bool fEndOfStream;
132 /** The input I/O stream. */
133 RTVFSIOSTREAM hVfsIos;
134} RTZIPTARIOSTREAM;
135/** Pointer to a the private data of a TAR file I/O stream. */
136typedef RTZIPTARIOSTREAM *PRTZIPTARIOSTREAM;
137
138
139/**
140 * Tar filesystem stream private data.
141 */
142typedef struct RTZIPTARFSSTREAM
143{
144 /** The input I/O stream. */
145 RTVFSIOSTREAM hVfsIos;
146
147 /** The current object (referenced). */
148 RTVFSOBJ hVfsCurObj;
149 /** Pointer to the private data if hVfsCurObj is representing a file. */
150 PRTZIPTARIOSTREAM pCurIosData;
151
152 /** The start offset. */
153 RTFOFF offStart;
154 /** The offset of the next header. */
155 RTFOFF offNextHdr;
156
157 /** Set if we've reached the end of the stream. */
158 bool fEndOfStream;
159 /** Set if we've encountered a fatal error. */
160 int rcFatal;
161
162 /** The TAR reader instance data. */
163 RTZIPTARREADER TarReader;
164} RTZIPTARFSSTREAM;
165/** Pointer to a the private data of a TAR filesystem stream. */
166typedef RTZIPTARFSSTREAM *PRTZIPTARFSSTREAM;
167
168
169
170/**
171 * Converts a numeric header field to the C native type.
172 *
173 * @returns IPRT status code.
174 *
175 * @param pszField The TAR header field.
176 * @param cchField The length of the field.
177 * @param fOctalOnly Must be octal.
178 * @param pi64 Where to store the value.
179 */
180static int rtZipTarHdrFieldToNum(const char *pszField, size_t cchField, bool fOctalOnly, int64_t *pi64)
181{
182 size_t const cchFieldOrg = cchField;
183 if ( fOctalOnly
184 || !(*(unsigned char *)pszField & 0x80))
185 {
186 /*
187 * Skip leading spaces. Include zeros to save a few slower loops below.
188 */
189 char ch;
190 while (cchField > 0 && ((ch = *pszField) == ' '|| ch == '0'))
191 cchField--, pszField++;
192
193 /*
194 * Convert octal digits.
195 */
196 int64_t i64 = 0;
197 while (cchField > 0)
198 {
199 unsigned char uDigit = *pszField - '0';
200 if (uDigit >= 8)
201 break;
202 i64 <<= 3;
203 i64 |= uDigit;
204
205 pszField++;
206 cchField--;
207 }
208 *pi64 = i64;
209
210 /*
211 * Was it terminated correctly?
212 */
213 while (cchField > 0)
214 {
215 ch = *pszField++;
216 if (ch != 0 && ch != ' ')
217 return cchField < cchFieldOrg
218 ? VERR_TAR_BAD_NUM_FIELD_TERM
219 : VERR_TAR_BAD_NUM_FIELD;
220 cchField--;
221 }
222 }
223 else
224 {
225 /** @todo implement base-256 encoded fields. */
226 return VERR_TAR_BASE_256_NOT_SUPPORTED;
227 }
228
229 return VINF_SUCCESS;
230}
231
232
233/**
234 * Calculates the TAR header checksums and detects if it's all zeros.
235 *
236 * @returns true if all zeros, false if not.
237 * @param pHdr The header to checksum.
238 * @param pi32Unsigned Where to store the checksum calculated using
239 * unsigned chars. This is the one POSIX
240 * specifies.
241 * @param pi32Signed Where to store the checksum calculated using
242 * signed chars.
243 *
244 * @remarks The reason why we calculate the checksum as both signed and unsigned
245 * has to do with various the char C type being signed on some hosts
246 * and unsigned on others.
247 */
248static bool rtZipTarCalcChkSum(PCRTZIPTARHDR pHdr, int32_t *pi32Unsigned, int32_t *pi32Signed)
249{
250 int32_t i32Unsigned = 0;
251 int32_t i32Signed = 0;
252
253 /*
254 * Sum up the entire header.
255 */
256 const char *pch = (const char *)pHdr;
257 const char *pchEnd = pch + sizeof(*pHdr);
258 do
259 {
260 i32Unsigned += *(unsigned char *)pch;
261 i32Signed += *(signed char *)pch;
262 } while (++pch != pchEnd);
263
264 /*
265 * Check if it's all zeros and replace the chksum field with spaces.
266 */
267 bool const fZeroHdr = i32Unsigned == 0;
268
269 pch = pHdr->Common.chksum;
270 pchEnd = pch + sizeof(pHdr->Common.chksum);
271 do
272 {
273 i32Unsigned -= *(unsigned char *)pch;
274 i32Signed -= *(signed char *)pch;
275 } while (++pch != pchEnd);
276
277 i32Unsigned += (unsigned char)' ' * sizeof(pHdr->Common.chksum);
278 i32Signed += (signed char)' ' * sizeof(pHdr->Common.chksum);
279
280 *pi32Unsigned = i32Unsigned;
281 if (pi32Signed)
282 *pi32Signed = i32Signed;
283 return fZeroHdr;
284}
285
286
287/**
288 * Validates the TAR header.
289 *
290 * @returns VINF_SUCCESS if valid, VERR_TAR_ZERO_HEADER if all zeros, and
291 * the appropriate VERR_TAR_XXX otherwise.
292 * @param pTar The TAR header.
293 * @param penmType Where to return the type of header on success.
294 */
295static int rtZipTarHdrValidate(PCRTZIPTARHDR pTar, PRTZIPTARTYPE penmType)
296{
297 /*
298 * Calc the checksum first since this enables us to detect zero headers.
299 */
300 int32_t i32ChkSum;
301 int32_t i32ChkSumSignedAlt;
302 if (rtZipTarCalcChkSum(pTar, &i32ChkSum, &i32ChkSumSignedAlt))
303 return VERR_TAR_ZERO_HEADER;
304
305 /*
306 * Read the checksum field and match the checksums.
307 */
308 int64_t i64HdrChkSum;
309 int rc = rtZipTarHdrFieldToNum(pTar->Common.chksum, sizeof(pTar->Common.chksum), true /*fOctalOnly*/, &i64HdrChkSum);
310 if (RT_FAILURE(rc))
311 return VERR_TAR_BAD_CHKSUM_FIELD;
312 if ( i32ChkSum != i64HdrChkSum
313 && i32ChkSumSignedAlt != i64HdrChkSum) /** @todo test this */
314 return VERR_TAR_CHKSUM_MISMATCH;
315
316 /*
317 * Detect the TAR type.
318 */
319 RTZIPTARTYPE enmType;
320 if ( pTar->Common.magic[0] == 'u'
321 && pTar->Common.magic[1] == 's'
322 && pTar->Common.magic[2] == 't'
323 && pTar->Common.magic[3] == 'a'
324 && pTar->Common.magic[4] == 'r')
325 {
326/** @todo detect star headers */
327 if ( pTar->Common.magic[5] == '\0'
328 && pTar->Common.version[0] == '0'
329 && pTar->Common.version[1] == '0')
330 enmType = RTZIPTARTYPE_POSIX;
331 else if ( pTar->Common.magic[5] == ' '
332 && pTar->Common.version[0] == ' '
333 && pTar->Common.version[1] == '\0')
334 enmType = RTZIPTARTYPE_GNU;
335 else
336 return VERR_TAR_NOT_USTAR_V00;
337 }
338 else
339 enmType = RTZIPTARTYPE_ANCIENT;
340 *penmType = enmType;
341
342 /*
343 * Perform some basic checks.
344 */
345 switch (enmType)
346 {
347 case RTZIPTARTYPE_POSIX:
348 if ( !RT_C_IS_ALNUM(pTar->Common.typeflag)
349 && !pTar->Common.typeflag == '\0')
350 return VERR_TAR_UNKNOWN_TYPE_FLAG;
351 break;
352
353 case RTZIPTARTYPE_GNU:
354 switch (pTar->Common.typeflag)
355 {
356 case RTZIPTAR_TF_OLDNORMAL:
357 case RTZIPTAR_TF_NORMAL:
358 case RTZIPTAR_TF_CONTIG:
359 case RTZIPTAR_TF_DIR:
360 case RTZIPTAR_TF_CHR:
361 case RTZIPTAR_TF_BLK:
362 case RTZIPTAR_TF_LINK:
363 case RTZIPTAR_TF_SYMLINK:
364 case RTZIPTAR_TF_FIFO:
365 break;
366
367 case RTZIPTAR_TF_GNU_LONGLINK:
368 case RTZIPTAR_TF_GNU_LONGNAME:
369 break;
370
371 case RTZIPTAR_TF_GNU_DUMPDIR:
372 case RTZIPTAR_TF_GNU_MULTIVOL:
373 case RTZIPTAR_TF_GNU_SPARSE:
374 case RTZIPTAR_TF_GNU_VOLDHR:
375 /** @todo Implement full GNU TAR support. .*/
376 return VERR_TAR_UNSUPPORTED_GNU_HDR_TYPE;
377
378 default:
379 return VERR_TAR_UNKNOWN_TYPE_FLAG;
380 }
381 break;
382
383 case RTZIPTARTYPE_ANCIENT:
384 switch (pTar->Common.typeflag)
385 {
386 case RTZIPTAR_TF_OLDNORMAL:
387 case RTZIPTAR_TF_NORMAL:
388 case RTZIPTAR_TF_CONTIG:
389 case RTZIPTAR_TF_DIR:
390 case RTZIPTAR_TF_LINK:
391 case RTZIPTAR_TF_SYMLINK:
392 case RTZIPTAR_TF_FIFO:
393 break;
394 default:
395 return VERR_TAR_UNKNOWN_TYPE_FLAG;
396 }
397 break;
398 default: /* shut up gcc */
399 AssertFailedReturn(VERR_INTERNAL_ERROR_3);
400 }
401
402 return VINF_SUCCESS;
403}
404
405
406/**
407 * Parses and validates the first TAR header of a archive/file/dir/whatever.
408 *
409 * @returns IPRT status code.
410 * @param pThis The TAR reader stat.
411 * @param pTar The TAR header that has been read.
412 * @param fFirst Set if this is the first header, otherwise
413 * clear.
414 */
415static int rtZipTarReaderParseNextHeader(PRTZIPTARREADER pThis, PCRTZIPTARHDR pHdr, bool fFirst)
416{
417 int rc;
418
419 /*
420 * Basic header validation and detection first.
421 */
422 RTZIPTARTYPE enmType;
423 rc = rtZipTarHdrValidate(pHdr, &enmType);
424 if (RT_FAILURE_NP(rc))
425 {
426 if (rc == VERR_TAR_ZERO_HEADER)
427 {
428 pThis->cZeroHdrs = 1;
429 pThis->enmState = RTZIPTARREADERSTATE_ZERO;
430 return VINF_SUCCESS;
431 }
432 return rc;
433 }
434 if (fFirst)
435 pThis->enmType = enmType;
436
437 /*
438 * Handle the header by type.
439 */
440 switch (pHdr->Common.typeflag)
441 {
442 case RTZIPTAR_TF_OLDNORMAL:
443 case RTZIPTAR_TF_NORMAL:
444 case RTZIPTAR_TF_CONTIG:
445 case RTZIPTAR_TF_LINK:
446 case RTZIPTAR_TF_SYMLINK:
447 case RTZIPTAR_TF_CHR:
448 case RTZIPTAR_TF_BLK:
449 case RTZIPTAR_TF_FIFO:
450 case RTZIPTAR_TF_DIR:
451 /*
452 * Extract the name first.
453 */
454 if (!pHdr->Common.name[0])
455 return VERR_TAR_EMPTY_NAME;
456 if (pThis->enmType == RTZIPTARTYPE_POSIX)
457 {
458 Assert(pThis->offGnuLongCur == 0); Assert(pThis->szName[0] == '\0');
459 pThis->szName[0] = '\0';
460 if (pHdr->Posix.prefix[0])
461 {
462 rc = RTStrCopyEx(pThis->szName, sizeof(pThis->szName), pHdr->Posix.prefix, sizeof(pHdr->Posix.prefix));
463 AssertRC(rc); /* shall not fail */
464 rc = RTStrCat(pThis->szName, sizeof(pThis->szName), "/");
465 AssertRC(rc); /* ditto */
466 }
467 rc = RTStrCatEx(pThis->szName, sizeof(pThis->szName), pHdr->Common.name, sizeof(pHdr->Common.name));
468 AssertRCReturn(rc, rc);
469 }
470 else if (pThis->enmType == RTZIPTARTYPE_GNU)
471 {
472 if (!pThis->szName[0])
473 {
474 rc = RTStrCopyEx(pThis->szName, sizeof(pThis->szName), pHdr->Common.name, sizeof(pHdr->Common.name));
475 AssertRCReturn(rc, rc);
476 }
477 }
478 else
479 {
480 /* Old TAR */
481 Assert(pThis->offGnuLongCur == 0); Assert(pThis->szName[0] == '\0');
482 rc = RTStrCopyEx(pThis->szName, sizeof(pThis->szName), pHdr->Common.name, sizeof(pHdr->Common.name));
483 AssertRCReturn(rc, rc);
484 }
485
486 /*
487 * Extract the link target.
488 */
489 if ( pHdr->Common.typeflag == RTZIPTAR_TF_LINK
490 || pHdr->Common.typeflag == RTZIPTAR_TF_SYMLINK)
491 {
492 if ( pThis->enmType == RTZIPTARTYPE_POSIX
493 || pThis->enmType == RTZIPTARTYPE_ANCIENT
494 || (pThis->enmType == RTZIPTARTYPE_GNU && pThis->szTarget[0] == '\0')
495 )
496 {
497 Assert(pThis->szTarget[0] == '\0');
498 rc = RTStrCopyEx(pThis->szTarget, sizeof(pThis->szTarget),
499 pHdr->Common.linkname, sizeof(pHdr->Common.linkname));
500 AssertRCReturn(rc, rc);
501 }
502 }
503 else
504 pThis->szTarget[0] = '\0';
505
506 pThis->Hdr = *pHdr;
507 break;
508
509 case RTZIPTAR_TF_X_HDR:
510 case RTZIPTAR_TF_X_GLOBAL:
511 /** @todo implement PAX */
512 return VERR_TAR_UNSUPPORTED_PAX_TYPE;
513
514 case RTZIPTAR_TF_SOLARIS_XHDR:
515 /** @todo implement solaris / pax attribute lists. */
516 return VERR_TAR_UNSUPPORTED_SOLARIS_HDR_TYPE;
517
518
519 /*
520 * A GNU long name or long link is a dummy record followed by one or
521 * more 512 byte string blocks holding the long name/link. The name
522 * lenght is encoded in the size field, null terminator included. If
523 * it is a symlink or hard link the long name may be followed by a
524 * long link sequence.
525 */
526 case RTZIPTAR_TF_GNU_LONGNAME:
527 case RTZIPTAR_TF_GNU_LONGLINK:
528 {
529 if (strcmp(pHdr->Gnu.name, "././@LongLink"))
530 return VERR_TAR_MALFORMED_GNU_LONGXXXX;
531
532 int64_t cb64;
533 rc = rtZipTarHdrFieldToNum(pHdr->Gnu.size, sizeof(pHdr->Gnu.size), false /*fOctalOnly*/, &cb64);
534 if (RT_FAILURE(rc) || cb64 < 0 || cb64 > _1M)
535 return VERR_TAR_MALFORMED_GNU_LONGXXXX;
536 uint32_t cb = (uint32_t)cb64;
537 if (cb >= sizeof(pThis->szName))
538 return VERR_TAR_NAME_TOO_LONG;
539
540 pThis->cbGnuLongExpect = cb;
541 pThis->offGnuLongCur = 0;
542 pThis->enmState = pHdr->Common.typeflag == RTZIPTAR_TF_GNU_LONGNAME
543 ? RTZIPTARREADERSTATE_GNU_LONGNAME
544 : RTZIPTARREADERSTATE_GNU_LONGLINK;
545 break;
546 }
547
548 case RTZIPTAR_TF_GNU_DUMPDIR:
549 case RTZIPTAR_TF_GNU_MULTIVOL:
550 case RTZIPTAR_TF_GNU_SPARSE:
551 case RTZIPTAR_TF_GNU_VOLDHR:
552 /** @todo Implement or skip GNU headers */
553 return VERR_TAR_UNSUPPORTED_GNU_HDR_TYPE;
554
555 default:
556 return VERR_TAR_UNKNOWN_TYPE_FLAG;
557 }
558
559 return VINF_SUCCESS;
560}
561
562
563/**
564 * Parses and validates a TAR header.
565 *
566 * @returns IPRT status code.
567 * @param pThis The TAR reader stat.
568 * @param pTar The TAR header that has been read.
569 */
570static int rtZipTarReaderParseHeader(PRTZIPTARREADER pThis, PCRTZIPTARHDR pHdr)
571{
572 switch (pThis->enmState)
573 {
574 /*
575 * The first record for a file/directory/whatever.
576 */
577 case RTZIPTARREADERSTATE_FIRST:
578 pThis->Hdr.Common.typeflag = 0x7f;
579 pThis->enmPrevType = pThis->enmType;
580 pThis->enmType = RTZIPTARTYPE_INVALID;
581 pThis->offGnuLongCur = 0;
582 pThis->cbGnuLongExpect = 0;
583 pThis->szName[0] = '\0';
584 pThis->szTarget[0] = '\0';
585 return rtZipTarReaderParseNextHeader(pThis, pHdr, true /*fFirst*/);
586
587 /*
588 * There should only be so many zero headers at the end of the file as
589 * it is a function of the block size used when writing. Don't go on
590 * reading them forever in case someone points us to /dev/zero.
591 */
592 case RTZIPTARREADERSTATE_ZERO:
593 if (ASMMemIsAllU32(pHdr, sizeof(*pHdr), 0) != NULL)
594 return VERR_TAR_ZERO_HEADER;
595 pThis->cZeroHdrs++;
596 if (pThis->cZeroHdrs <= _64K / 512 + 2)
597 return VINF_SUCCESS;
598 return VERR_TAR_ZERO_HEADER;
599
600 case RTZIPTARREADERSTATE_GNU_LONGNAME:
601 case RTZIPTARREADERSTATE_GNU_LONGLINK:
602 {
603 size_t cbIncoming = RTStrNLen((const char *)pHdr->ab, sizeof(*pHdr));
604 if (cbIncoming < sizeof(*pHdr))
605 cbIncoming += 1;
606
607 if (cbIncoming + pThis->offGnuLongCur > pThis->cbGnuLongExpect)
608 return VERR_TAR_MALFORMED_GNU_LONGXXXX;
609 if ( cbIncoming < sizeof(*pHdr)
610 && cbIncoming + pThis->offGnuLongCur != pThis->cbGnuLongExpect)
611 return VERR_TAR_MALFORMED_GNU_LONGXXXX;
612
613 char *pszDst = pThis->enmState == RTZIPTARREADERSTATE_GNU_LONGNAME ? pThis->szName : pThis->szTarget;
614 pszDst += pThis->offGnuLongCur;
615 memcpy(pszDst, pHdr->ab, cbIncoming);
616
617 pThis->offGnuLongCur += (uint32_t)cbIncoming;
618 if (pThis->offGnuLongCur == pThis->cbGnuLongExpect)
619 pThis->enmState = RTZIPTARREADERSTATE_GNU_NEXT;
620 return VINF_SUCCESS;
621 }
622
623 case RTZIPTARREADERSTATE_GNU_NEXT:
624 pThis->enmState = RTZIPTARREADERSTATE_FIRST;
625 return rtZipTarReaderParseNextHeader(pThis, pHdr, false /*fFirst*/);
626
627 default:
628 return VERR_INTERNAL_ERROR_5;
629 }
630}
631
632
633/**
634 * Translate a TAR header to an IPRT object info structure with additional UNIX
635 * attributes.
636 *
637 * This completes the validation done by rtZipTarHdrValidate.
638 *
639 * @returns VINF_SUCCESS if valid, appropriate VERR_TAR_XXX if not.
640 * @param pThis The TAR reader instance.
641 * @param pObjInfo The object info structure (output).
642 */
643static int rtZipTarReaderGetFsObjInfo(PRTZIPTARREADER pThis, PRTFSOBJINFO pObjInfo)
644{
645 /*
646 * Zap the whole structure, this takes care of unused space in the union.
647 */
648 RT_ZERO(*pObjInfo);
649
650 /*
651 * Convert the TAR field in RTFSOBJINFO order.
652 */
653 int rc;
654 int64_t i64Tmp;
655#define GET_TAR_NUMERIC_FIELD_RET(a_Var, a_Field) \
656 do { \
657 rc = rtZipTarHdrFieldToNum(a_Field, sizeof(a_Field), false /*fOctalOnly*/, &i64Tmp); \
658 if (RT_FAILURE(rc)) \
659 return rc; \
660 (a_Var) = i64Tmp; \
661 if ((a_Var) != i64Tmp) \
662 return VERR_TAR_NUM_VALUE_TOO_LARGE; \
663 } while (0)
664
665 GET_TAR_NUMERIC_FIELD_RET(pObjInfo->cbObject, pThis->Hdr.Common.size);
666 pObjInfo->cbAllocated = RT_ALIGN_64(pObjInfo->cbObject, 512);
667 int64_t c64SecModTime;
668 GET_TAR_NUMERIC_FIELD_RET(c64SecModTime, pThis->Hdr.Common.mtime);
669 RTTimeSpecSetSeconds(&pObjInfo->ChangeTime, c64SecModTime);
670 RTTimeSpecSetSeconds(&pObjInfo->ModificationTime, c64SecModTime);
671 RTTimeSpecSetSeconds(&pObjInfo->AccessTime, c64SecModTime);
672 RTTimeSpecSetSeconds(&pObjInfo->BirthTime, c64SecModTime);
673 if (c64SecModTime != RTTimeSpecGetSeconds(&pObjInfo->ModificationTime))
674 return VERR_TAR_NUM_VALUE_TOO_LARGE;
675 GET_TAR_NUMERIC_FIELD_RET(pObjInfo->Attr.fMode, pThis->Hdr.Common.mode);
676 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
677 GET_TAR_NUMERIC_FIELD_RET(pObjInfo->Attr.u.Unix.uid, pThis->Hdr.Common.uid);
678 GET_TAR_NUMERIC_FIELD_RET(pObjInfo->Attr.u.Unix.gid, pThis->Hdr.Common.gid);
679 pObjInfo->Attr.u.Unix.cHardlinks = 1;
680 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
681 pObjInfo->Attr.u.Unix.INodeId = 0;
682 pObjInfo->Attr.u.Unix.fFlags = 0;
683 pObjInfo->Attr.u.Unix.GenerationId = 0;
684 pObjInfo->Attr.u.Unix.Device = 0;
685 switch (pThis->enmType)
686 {
687 case RTZIPTARTYPE_POSIX:
688 case RTZIPTARTYPE_GNU:
689 if ( pThis->Hdr.Common.typeflag == RTZIPTAR_TF_CHR
690 || pThis->Hdr.Common.typeflag == RTZIPTAR_TF_BLK)
691 {
692 uint32_t uMajor, uMinor;
693 GET_TAR_NUMERIC_FIELD_RET(uMajor, pThis->Hdr.Common.devmajor);
694 GET_TAR_NUMERIC_FIELD_RET(uMinor, pThis->Hdr.Common.devminor);
695 pObjInfo->Attr.u.Unix.Device = RTDEV_MAKE(uMajor, uMinor);
696 if ( uMajor != RTDEV_MAJOR(pObjInfo->Attr.u.Unix.Device)
697 || uMinor != RTDEV_MINOR(pObjInfo->Attr.u.Unix.Device))
698 return VERR_TAR_DEV_VALUE_TOO_LARGE;
699 }
700 break;
701
702 default:
703 if ( pThis->Hdr.Common.typeflag == RTZIPTAR_TF_CHR
704 || pThis->Hdr.Common.typeflag == RTZIPTAR_TF_BLK)
705 return VERR_TAR_UNKNOWN_TYPE_FLAG;
706 }
707
708#undef GET_TAR_NUMERIC_FIELD_RET
709
710 /*
711 * Massage the result a little bit.
712 * Also validate some more now that we've got the numbers to work with.
713 */
714 if ( (pObjInfo->Attr.fMode & ~RTFS_UNIX_MASK)
715 && pThis->enmType == RTZIPTARTYPE_POSIX)
716 return VERR_TAR_BAD_MODE_FIELD;
717 pObjInfo->Attr.fMode &= RTFS_UNIX_MASK;
718
719 RTFMODE fModeType = 0;
720 switch (pThis->Hdr.Common.typeflag)
721 {
722 case RTZIPTAR_TF_OLDNORMAL:
723 case RTZIPTAR_TF_NORMAL:
724 case RTZIPTAR_TF_CONTIG:
725 {
726 const char *pszEnd = strchr(pThis->szName, '\0');
727 if (pszEnd == &pThis->szName[0] || pszEnd[-1] != '/')
728 fModeType |= RTFS_TYPE_FILE;
729 else
730 fModeType |= RTFS_TYPE_DIRECTORY;
731 break;
732 }
733
734 case RTZIPTAR_TF_LINK:
735 if (pObjInfo->cbObject != 0)
736#if 0 /* too strict */
737 return VERR_TAR_SIZE_NOT_ZERO;
738#else
739 pObjInfo->cbObject = pObjInfo->cbAllocated = 0;
740#endif
741 fModeType |= RTFS_TYPE_FILE; /* no better idea for now */
742 break;
743
744 case RTZIPTAR_TF_SYMLINK:
745 fModeType |= RTFS_TYPE_SYMLINK;
746 break;
747
748 case RTZIPTAR_TF_CHR:
749 fModeType |= RTFS_TYPE_DEV_CHAR;
750 break;
751
752 case RTZIPTAR_TF_BLK:
753 fModeType |= RTFS_TYPE_DEV_BLOCK;
754 break;
755
756 case RTZIPTAR_TF_DIR:
757 fModeType |= RTFS_TYPE_DIRECTORY;
758 break;
759
760 case RTZIPTAR_TF_FIFO:
761 fModeType |= RTFS_TYPE_FIFO;
762 break;
763
764 case RTZIPTAR_TF_GNU_LONGLINK:
765 case RTZIPTAR_TF_GNU_LONGNAME:
766 /* ASSUMES RTFS_TYPE_XXX uses the same values as GNU stored in the mode field. */
767 fModeType = pObjInfo->Attr.fMode & RTFS_TYPE_MASK;
768 switch (fModeType)
769 {
770 case RTFS_TYPE_FILE:
771 case RTFS_TYPE_DIRECTORY:
772 case RTFS_TYPE_SYMLINK:
773 case RTFS_TYPE_DEV_BLOCK:
774 case RTFS_TYPE_DEV_CHAR:
775 case RTFS_TYPE_FIFO:
776 break;
777
778 default:
779 case 0:
780 return VERR_TAR_UNKNOWN_TYPE_FLAG; /** @todo new status code */
781 }
782
783 default:
784 return VERR_TAR_UNKNOWN_TYPE_FLAG; /* Should've been caught in validate. */
785 }
786 if ( (pObjInfo->Attr.fMode & RTFS_TYPE_MASK)
787 && (pObjInfo->Attr.fMode & RTFS_TYPE_MASK) != fModeType)
788 return VERR_TAR_MODE_WITH_TYPE;
789 pObjInfo->Attr.fMode &= ~RTFS_TYPE_MASK;
790 pObjInfo->Attr.fMode |= fModeType;
791
792 switch (pThis->Hdr.Common.typeflag)
793 {
794 case RTZIPTAR_TF_CHR:
795 case RTZIPTAR_TF_BLK:
796 case RTZIPTAR_TF_DIR:
797 case RTZIPTAR_TF_FIFO:
798 pObjInfo->cbObject = 0;
799 pObjInfo->cbAllocated = 0;
800 break;
801 }
802
803 return VINF_SUCCESS;
804}
805
806
807/**
808 * Checks if the reader is expecting more headers.
809 *
810 * @returns true / false.
811 * @param pThis The TAR reader instance.
812 */
813static bool rtZipTarReaderExpectingMoreHeaders(PRTZIPTARREADER pThis)
814{
815 return pThis->enmState != RTZIPTARREADERSTATE_FIRST;
816}
817
818
819/**
820 * Checks if we're at the end of the TAR file.
821 *
822 * @returns true / false.
823 * @param pThis The TAR reader instance.
824 */
825static bool rtZipTarReaderIsAtEnd(PRTZIPTARREADER pThis)
826{
827 if (!pThis->cZeroHdrs)
828 return false;
829
830 /* Here is a kludge to try deal with archivers not putting at least two
831 zero headers at the end. Afraid it may require further relaxing
832 later on, but let's try be strict about things for now. */
833 return pThis->cZeroHdrs >= (pThis->enmPrevType == RTZIPTARTYPE_POSIX ? 2U : 1U);
834}
835
836
837/**
838 * Checks if the current TAR object is a hard link or not.
839 *
840 * @returns true if it is, false if not.
841 * @param pThis The TAR reader instance.
842 */
843static bool rtZipTarReaderIsHardlink(PRTZIPTARREADER pThis)
844{
845 return pThis->Hdr.Common.typeflag == RTZIPTAR_TF_LINK;
846}
847
848
849/**
850 * Checks if the TAR header includes a POSIX or GNU user name field.
851 *
852 * @returns true / false.
853 * @param pThis The TAR reader instance.
854 */
855DECLINLINE(bool) rtZipTarReaderHasUserName(PRTZIPTARREADER pThis)
856{
857 return pThis->Hdr.Common.uname[0] != '\0'
858 && ( pThis->enmType == RTZIPTARTYPE_POSIX
859 || pThis->enmType == RTZIPTARTYPE_GNU);
860}
861
862
863/**
864 * Checks if the TAR header includes a POSIX or GNU group name field.
865 *
866 * @returns true / false.
867 * @param pThis The TAR reader instance.
868 */
869DECLINLINE(bool) rtZipTarReaderHasGroupName(PRTZIPTARREADER pThis)
870{
871 return pThis->Hdr.Common.gname[0] != '\0'
872 && ( pThis->enmType == RTZIPTARTYPE_POSIX
873 || pThis->enmType == RTZIPTARTYPE_GNU);
874}
875
876
877/*
878 *
879 * 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.
880 * 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.
881 * 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.
882 *
883 */
884
885/**
886 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
887 */
888static DECLCALLBACK(int) rtZipTarFssBaseObj_Close(void *pvThis)
889{
890 PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis;
891
892 /* Currently there is nothing we really have to do here. */
893 pThis->offHdr = -1;
894
895 return VINF_SUCCESS;
896}
897
898
899/**
900 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
901 */
902static DECLCALLBACK(int) rtZipTarFssBaseObj_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
903{
904 PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis;
905
906 /*
907 * Copy the desired data.
908 */
909 switch (enmAddAttr)
910 {
911 case RTFSOBJATTRADD_NOTHING:
912 case RTFSOBJATTRADD_UNIX:
913 *pObjInfo = pThis->ObjInfo;
914 break;
915
916 case RTFSOBJATTRADD_UNIX_OWNER:
917 *pObjInfo = pThis->ObjInfo;
918 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
919 pObjInfo->Attr.u.UnixOwner.uid = pThis->ObjInfo.Attr.u.Unix.uid;
920 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
921 if (rtZipTarReaderHasUserName(pThis->pTarReader))
922 RTStrCopy(pObjInfo->Attr.u.UnixOwner.szName, sizeof(pObjInfo->Attr.u.UnixOwner.szName),
923 pThis->pTarReader->Hdr.Common.uname);
924 break;
925
926 case RTFSOBJATTRADD_UNIX_GROUP:
927 *pObjInfo = pThis->ObjInfo;
928 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
929 pObjInfo->Attr.u.UnixGroup.gid = pThis->ObjInfo.Attr.u.Unix.gid;
930 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
931 if (rtZipTarReaderHasGroupName(pThis->pTarReader))
932 RTStrCopy(pObjInfo->Attr.u.UnixGroup.szName, sizeof(pObjInfo->Attr.u.UnixGroup.szName),
933 pThis->pTarReader->Hdr.Common.gname);
934 break;
935
936 case RTFSOBJATTRADD_EASIZE:
937 *pObjInfo = pThis->ObjInfo;
938 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
939 RT_ZERO(pObjInfo->Attr.u);
940 break;
941
942 default:
943 return VERR_NOT_SUPPORTED;
944 }
945
946 return VINF_SUCCESS;
947}
948
949
950/**
951 * Tar filesystem base object operations.
952 */
953static const RTVFSOBJOPS g_rtZipTarFssBaseObjOps =
954{
955 RTVFSOBJOPS_VERSION,
956 RTVFSOBJTYPE_BASE,
957 "TarFsStream::Obj",
958 rtZipTarFssBaseObj_Close,
959 rtZipTarFssBaseObj_QueryInfo,
960 RTVFSOBJOPS_VERSION
961};
962
963
964/**
965 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
966 */
967static DECLCALLBACK(int) rtZipTarFssIos_Close(void *pvThis)
968{
969 PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis;
970
971 RTVfsIoStrmRelease(pThis->hVfsIos);
972 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
973
974 return rtZipTarFssBaseObj_Close(&pThis->BaseObj);
975}
976
977
978/**
979 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
980 */
981static DECLCALLBACK(int) rtZipTarFssIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
982{
983 PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis;
984 return rtZipTarFssBaseObj_QueryInfo(&pThis->BaseObj, pObjInfo, enmAddAttr);
985}
986
987
988/**
989 * Reads one segment.
990 *
991 * @returns IPRT status code.
992 * @param pThis The instance data.
993 * @param pvBuf Where to put the read bytes.
994 * @param cbToRead The number of bytes to read.
995 * @param fBlocking Whether to block or not.
996 * @param pcbRead Where to store the number of bytes actually read.
997 */
998static int rtZipTarFssIos_ReadOneSeg(PRTZIPTARIOSTREAM pThis, void *pvBuf, size_t cbToRead, bool fBlocking, size_t *pcbRead)
999{
1000 /*
1001 * Fend of reads beyond the end of the stream here.
1002 */
1003 if (pThis->fEndOfStream)
1004 return pcbRead ? VINF_EOF : VERR_EOF;
1005
1006 Assert(pThis->cbFile >= pThis->offFile);
1007 uint64_t cbLeft = (uint64_t)(pThis->cbFile - pThis->offFile);
1008 if (cbToRead > cbLeft)
1009 {
1010 if (!pcbRead)
1011 return VERR_EOF;
1012 cbToRead = (size_t)cbLeft;
1013 }
1014
1015 /*
1016 * Do the reading.
1017 */
1018 size_t cbReadStack = 0;
1019 if (!pcbRead)
1020 pcbRead = &cbReadStack;
1021 int rc = RTVfsIoStrmRead(pThis->hVfsIos, pvBuf, cbToRead, fBlocking, pcbRead);
1022 pThis->offFile += *pcbRead;
1023 if (pThis->offFile >= pThis->cbFile)
1024 {
1025 Assert(pThis->offFile == pThis->cbFile);
1026 pThis->fEndOfStream = true;
1027 RTVfsIoStrmSkip(pThis->hVfsIos, pThis->cbPadding);
1028 }
1029
1030 return rc;
1031}
1032
1033
1034/**
1035 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
1036 */
1037static DECLCALLBACK(int) rtZipTarFssIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
1038{
1039 PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis;
1040 int rc;
1041
1042 if (pSgBuf->cSegs == 1)
1043 rc = rtZipTarFssIos_ReadOneSeg(pThis, pSgBuf->paSegs[0].pvSeg, pSgBuf->paSegs[0].cbSeg, fBlocking, pcbRead);
1044 else
1045 {
1046 rc = VINF_SUCCESS;
1047 size_t cbRead = 0;
1048 size_t cbReadSeg;
1049 size_t *pcbReadSeg = pcbRead ? &cbReadSeg : NULL;
1050 for (uint32_t iSeg = 0; iSeg < pSgBuf->cSegs; iSeg++)
1051 {
1052 cbReadSeg = 0;
1053 rc = rtZipTarFssIos_ReadOneSeg(pThis, pSgBuf->paSegs[iSeg].pvSeg, pSgBuf->paSegs[iSeg].cbSeg, fBlocking, pcbReadSeg);
1054 if (RT_FAILURE(rc))
1055 break;
1056 if (pcbRead)
1057 {
1058 cbRead += cbReadSeg;
1059 if (cbReadSeg != pSgBuf->paSegs[iSeg].cbSeg)
1060 break;
1061 }
1062 }
1063 if (pcbRead)
1064 *pcbRead = cbRead;
1065 }
1066
1067 return rc;
1068}
1069
1070
1071/**
1072 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
1073 */
1074static DECLCALLBACK(int) rtZipTarFssIos_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
1075{
1076 /* Cannot write to a read-only I/O stream. */
1077 NOREF(pvThis); NOREF(off); NOREF(pSgBuf); NOREF(fBlocking); NOREF(pcbWritten);
1078 return VERR_ACCESS_DENIED;
1079}
1080
1081
1082/**
1083 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
1084 */
1085static DECLCALLBACK(int) rtZipTarFssIos_Flush(void *pvThis)
1086{
1087 /* It's a read only stream, nothing dirty to flush. */
1088 NOREF(pvThis);
1089 return VINF_SUCCESS;
1090}
1091
1092
1093/**
1094 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
1095 */
1096static DECLCALLBACK(int) rtZipTarFssIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
1097 uint32_t *pfRetEvents)
1098{
1099 PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis;
1100
1101 /* When we've reached the end, read will be set to indicate it. */
1102 if ( (fEvents & RTPOLL_EVT_READ)
1103 && pThis->fEndOfStream)
1104 {
1105 int rc = RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, 0, fIntr, pfRetEvents);
1106 if (RT_SUCCESS(rc))
1107 *pfRetEvents |= RTPOLL_EVT_READ;
1108 else
1109 *pfRetEvents = RTPOLL_EVT_READ;
1110 return VINF_SUCCESS;
1111 }
1112
1113 return RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents);
1114}
1115
1116
1117/**
1118 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
1119 */
1120static DECLCALLBACK(int) rtZipTarFssIos_Tell(void *pvThis, PRTFOFF poffActual)
1121{
1122 PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis;
1123 return pThis->offFile;
1124}
1125
1126
1127/**
1128 * Tar I/O stream operations.
1129 */
1130static const RTVFSIOSTREAMOPS g_rtZipTarFssIosOps =
1131{
1132 { /* Obj */
1133 RTVFSOBJOPS_VERSION,
1134 RTVFSOBJTYPE_IO_STREAM,
1135 "TarFsStream::IoStream",
1136 rtZipTarFssIos_Close,
1137 rtZipTarFssIos_QueryInfo,
1138 RTVFSOBJOPS_VERSION
1139 },
1140 RTVFSIOSTREAMOPS_VERSION,
1141 0,
1142 rtZipTarFssIos_Read,
1143 rtZipTarFssIos_Write,
1144 rtZipTarFssIos_Flush,
1145 rtZipTarFssIos_PollOne,
1146 rtZipTarFssIos_Tell,
1147 NULL /*Skip*/,
1148 NULL /*ZeroFill*/,
1149 RTVFSIOSTREAMOPS_VERSION
1150};
1151
1152
1153/**
1154 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1155 */
1156static DECLCALLBACK(int) rtZipTarFssSym_Close(void *pvThis)
1157{
1158 PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis;
1159 return rtZipTarFssBaseObj_Close(pThis);
1160}
1161
1162
1163/**
1164 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1165 */
1166static DECLCALLBACK(int) rtZipTarFssSym_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1167{
1168 PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis;
1169 return rtZipTarFssBaseObj_QueryInfo(pThis, pObjInfo, enmAddAttr);
1170}
1171
1172/**
1173 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
1174 */
1175static DECLCALLBACK(int) rtZipTarFssSym_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
1176{
1177 NOREF(pvThis); NOREF(fMode); NOREF(fMask);
1178 return VERR_ACCESS_DENIED;
1179}
1180
1181
1182/**
1183 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
1184 */
1185static DECLCALLBACK(int) rtZipTarFssSym_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1186 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1187{
1188 NOREF(pvThis); NOREF(pAccessTime); NOREF(pModificationTime); NOREF(pChangeTime); NOREF(pBirthTime);
1189 return VERR_ACCESS_DENIED;
1190}
1191
1192
1193/**
1194 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
1195 */
1196static DECLCALLBACK(int) rtZipTarFssSym_SetOwner(void *pvThis, RTUID uid, RTGID gid)
1197{
1198 NOREF(pvThis); NOREF(uid); NOREF(gid);
1199 return VERR_ACCESS_DENIED;
1200}
1201
1202
1203/**
1204 * @interface_method_impl{RTVFSSYMLINKOPS,pfnRead}
1205 */
1206static DECLCALLBACK(int) rtZipTarFssSym_Read(void *pvThis, char *pszTarget, size_t cbTarget)
1207{
1208 PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis;
1209 return RTStrCopy(pszTarget, cbTarget, pThis->pTarReader->szTarget);
1210}
1211
1212
1213/**
1214 * Tar symbolic (and hardlink) operations.
1215 */
1216static const RTVFSSYMLINKOPS g_rtZipTarFssSymOps =
1217{
1218 { /* Obj */
1219 RTVFSOBJOPS_VERSION,
1220 RTVFSOBJTYPE_SYMLINK,
1221 "TarFsStream::Symlink",
1222 rtZipTarFssSym_Close,
1223 rtZipTarFssSym_QueryInfo,
1224 RTVFSOBJOPS_VERSION
1225 },
1226 RTVFSSYMLINKOPS_VERSION,
1227 0,
1228 { /* ObjSet */
1229 RTVFSOBJSETOPS_VERSION,
1230 RT_OFFSETOF(RTVFSSYMLINKOPS, Obj) - RT_OFFSETOF(RTVFSSYMLINKOPS, ObjSet),
1231 rtZipTarFssSym_SetMode,
1232 rtZipTarFssSym_SetTimes,
1233 rtZipTarFssSym_SetOwner,
1234 RTVFSOBJSETOPS_VERSION
1235 },
1236 rtZipTarFssSym_Read,
1237 RTVFSSYMLINKOPS_VERSION
1238};
1239
1240
1241/**
1242 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1243 */
1244static DECLCALLBACK(int) rtZipTarFss_Close(void *pvThis)
1245{
1246 PRTZIPTARFSSTREAM pThis = (PRTZIPTARFSSTREAM)pvThis;
1247
1248 RTVfsObjRelease(pThis->hVfsCurObj);
1249 pThis->hVfsCurObj = NIL_RTVFSOBJ;
1250 pThis->pCurIosData = NULL;
1251
1252 RTVfsIoStrmRelease(pThis->hVfsIos);
1253 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
1254
1255 return VINF_SUCCESS;
1256}
1257
1258
1259/**
1260 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1261 */
1262static DECLCALLBACK(int) rtZipTarFss_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1263{
1264 PRTZIPTARFSSTREAM pThis = (PRTZIPTARFSSTREAM)pvThis;
1265 /* Take the lazy approach here, with the sideffect of providing some info
1266 that is actually kind of useful. */
1267 return RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
1268}
1269
1270
1271/**
1272 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnNext}
1273 */
1274static DECLCALLBACK(int) rtZipTarFss_Next(void *pvThis, char **ppszName, RTVFSOBJTYPE *penmType, PRTVFSOBJ phVfsObj)
1275{
1276 PRTZIPTARFSSTREAM pThis = (PRTZIPTARFSSTREAM)pvThis;
1277
1278 /*
1279 * Dispense with the current object.
1280 */
1281 if (pThis->hVfsCurObj != NIL_RTVFSOBJ)
1282 {
1283 if (pThis->pCurIosData)
1284 {
1285 pThis->pCurIosData->fEndOfStream = true;
1286 pThis->pCurIosData->offFile = pThis->pCurIosData->cbFile;
1287 pThis->pCurIosData = NULL;
1288 }
1289
1290 RTVfsObjRelease(pThis->hVfsCurObj);
1291 pThis->hVfsCurObj = NIL_RTVFSOBJ;
1292 }
1293
1294 /*
1295 * Check if we've already reached the end in some way.
1296 */
1297 if (pThis->fEndOfStream)
1298 return VERR_EOF;
1299 if (pThis->rcFatal != VINF_SUCCESS)
1300 return pThis->rcFatal;
1301
1302 /*
1303 * Make sure the input stream is in the right place.
1304 */
1305 RTFOFF offHdr = RTVfsIoStrmTell(pThis->hVfsIos);
1306 while ( offHdr >= 0
1307 && offHdr < pThis->offNextHdr)
1308 {
1309 int rc = RTVfsIoStrmSkip(pThis->hVfsIos, pThis->offNextHdr - offHdr);
1310 if (RT_FAILURE(rc))
1311 {
1312 /** @todo Ignore if we're at the end of the stream? */
1313 return pThis->rcFatal = rc;
1314 }
1315
1316 offHdr = RTVfsIoStrmTell(pThis->hVfsIos);
1317 }
1318
1319 if (offHdr < 0)
1320 return pThis->rcFatal = (int)offHdr;
1321 if (offHdr > pThis->offNextHdr)
1322 return pThis->rcFatal = VERR_INTERNAL_ERROR_3;
1323
1324 /*
1325 * Consume TAR headers.
1326 */
1327 size_t cbHdrs = 0;
1328 int rc;
1329 do
1330 {
1331 /*
1332 * Read the next header.
1333 */
1334 RTZIPTARHDR Hdr;
1335 size_t cbRead;
1336 rc = RTVfsIoStrmRead(pThis->hVfsIos, &Hdr, sizeof(Hdr), true /*fBlocking*/, &cbRead);
1337 if (RT_FAILURE(rc))
1338 return pThis->rcFatal = rc;
1339 if (rc == VINF_EOF && cbRead == 0)
1340 {
1341 pThis->fEndOfStream = true;
1342 return rtZipTarReaderIsAtEnd(&pThis->TarReader) ? VERR_EOF : VERR_TAR_UNEXPECTED_EOS;
1343 }
1344 if (cbRead != sizeof(Hdr))
1345 return pThis->rcFatal = VERR_TAR_UNEXPECTED_EOS;
1346
1347 cbHdrs += sizeof(Hdr);
1348
1349 /*
1350 * Parse the it.
1351 */
1352 rc = rtZipTarReaderParseHeader(&pThis->TarReader, &Hdr);
1353 if (RT_FAILURE(rc))
1354 return pThis->rcFatal = rc;
1355 } while (rtZipTarReaderExpectingMoreHeaders(&pThis->TarReader));
1356
1357 pThis->offNextHdr = offHdr + cbHdrs;
1358
1359 /*
1360 * Fill an object info structure from the current TAR state.
1361 */
1362 RTFSOBJINFO Info;
1363 rc = rtZipTarReaderGetFsObjInfo(&pThis->TarReader, &Info);
1364 if (RT_FAILURE(rc))
1365 return pThis->rcFatal = rc;
1366
1367 /*
1368 * Create an object of the appropriate type.
1369 */
1370 RTVFSOBJTYPE enmType;
1371 RTVFSOBJ hVfsObj;
1372 RTFMODE fType = Info.Attr.fMode & RTFS_TYPE_MASK;
1373 if (rtZipTarReaderIsHardlink(&pThis->TarReader))
1374 fType = RTFS_TYPE_SYMLINK;
1375 switch (fType)
1376 {
1377 /*
1378 * Files are represented by a VFS I/O stream.
1379 */
1380 case RTFS_TYPE_FILE:
1381 {
1382 RTVFSIOSTREAM hVfsIos;
1383 PRTZIPTARIOSTREAM pIosData;
1384 rc = RTVfsNewIoStream(&g_rtZipTarFssIosOps,
1385 sizeof(*pIosData),
1386 RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
1387 NIL_RTVFS,
1388 NIL_RTVFSLOCK,
1389 &hVfsIos,
1390 (void **)&pIosData);
1391 if (RT_FAILURE(rc))
1392 return pThis->rcFatal = rc;
1393
1394 pIosData->BaseObj.offHdr = offHdr;
1395 pIosData->BaseObj.pTarReader= &pThis->TarReader;
1396 pIosData->BaseObj.ObjInfo = Info;
1397 pIosData->cbFile = Info.cbObject;
1398 pIosData->offFile = 0;
1399 pIosData->cbPadding = Info.cbAllocated - Info.cbObject;
1400 pIosData->fEndOfStream = false;
1401 pIosData->hVfsIos = pThis->hVfsIos;
1402 RTVfsIoStrmRetain(pThis->hVfsIos);
1403
1404 pThis->pCurIosData = pIosData;
1405 pThis->offNextHdr += pIosData->cbFile + pIosData->cbPadding;
1406
1407 enmType = RTVFSOBJTYPE_IO_STREAM;
1408 hVfsObj = RTVfsObjFromIoStream(hVfsIos);
1409 RTVfsIoStrmRelease(hVfsIos);
1410 break;
1411 }
1412
1413 /*
1414 * We represent hard links using a symbolic link object. This fits
1415 * best with the way TAR stores it and there is currently no better
1416 * fitting VFS type alternative.
1417 */
1418 case RTFS_TYPE_SYMLINK:
1419 {
1420 RTVFSSYMLINK hVfsSym;
1421 PRTZIPTARBASEOBJ pBaseObjData;
1422 rc = RTVfsNewSymlink(&g_rtZipTarFssSymOps,
1423 sizeof(*pBaseObjData),
1424 NIL_RTVFS,
1425 NIL_RTVFSLOCK,
1426 &hVfsSym,
1427 (void **)&pBaseObjData);
1428 if (RT_FAILURE(rc))
1429 return pThis->rcFatal = rc;
1430
1431 pBaseObjData->offHdr = offHdr;
1432 pBaseObjData->pTarReader= &pThis->TarReader;
1433 pBaseObjData->ObjInfo = Info;
1434
1435 enmType = RTVFSOBJTYPE_SYMLINK;
1436 hVfsObj = RTVfsObjFromSymlink(hVfsSym);
1437 RTVfsSymlinkRelease(hVfsSym);
1438 break;
1439 }
1440
1441 /*
1442 * All other objects are repesented using a VFS base object since they
1443 * carry no data streams (unless some TAR extension implements extended
1444 * attributes / alternative streams).
1445 */
1446 case RTFS_TYPE_DEV_BLOCK:
1447 case RTFS_TYPE_DEV_CHAR:
1448 case RTFS_TYPE_DIRECTORY:
1449 case RTFS_TYPE_FIFO:
1450 {
1451 PRTZIPTARBASEOBJ pBaseObjData;
1452 rc = RTVfsNewBaseObj(&g_rtZipTarFssBaseObjOps,
1453 sizeof(*pBaseObjData),
1454 NIL_RTVFS,
1455 NIL_RTVFSLOCK,
1456 &hVfsObj,
1457 (void **)&pBaseObjData);
1458 if (RT_FAILURE(rc))
1459 return pThis->rcFatal = rc;
1460
1461 pBaseObjData->offHdr = offHdr;
1462 pBaseObjData->pTarReader= &pThis->TarReader;
1463 pBaseObjData->ObjInfo = Info;
1464
1465 enmType = RTVFSOBJTYPE_BASE;
1466 break;
1467 }
1468
1469 default:
1470 AssertFailed();
1471 return pThis->rcFatal = VERR_INTERNAL_ERROR_5;
1472 }
1473 pThis->hVfsCurObj = hVfsObj;
1474
1475 /*
1476 * Set the return data and we're done.
1477 */
1478 if (ppszName)
1479 {
1480 rc = RTStrDupEx(ppszName, pThis->TarReader.szName);
1481 if (RT_FAILURE(rc))
1482 return rc;
1483 }
1484
1485 if (phVfsObj)
1486 {
1487 RTVfsObjRetain(hVfsObj);
1488 *phVfsObj = hVfsObj;
1489 }
1490
1491 if (penmType)
1492 *penmType = enmType;
1493
1494 return VINF_SUCCESS;
1495}
1496
1497
1498
1499/**
1500 * Tar filesystem stream operations.
1501 */
1502static const RTVFSFSSTREAMOPS rtZipTarFssOps =
1503{
1504 { /* Obj */
1505 RTVFSOBJOPS_VERSION,
1506 RTVFSOBJTYPE_FS_STREAM,
1507 "TarFsStream",
1508 rtZipTarFss_Close,
1509 rtZipTarFss_QueryInfo,
1510 RTVFSOBJOPS_VERSION
1511 },
1512 RTVFSFSSTREAMOPS_VERSION,
1513 0,
1514 rtZipTarFss_Next,
1515 RTVFSFSSTREAMOPS_VERSION
1516};
1517
1518
1519RTDECL(int) RTZipTarFsStreamFromIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
1520{
1521 /*
1522 * Input validation.
1523 */
1524 AssertPtrReturn(phVfsFss, VERR_INVALID_HANDLE);
1525 *phVfsFss = NIL_RTVFSFSSTREAM;
1526 AssertPtrReturn(hVfsIosIn, VERR_INVALID_HANDLE);
1527 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
1528
1529 RTFOFF const offStart = RTVfsIoStrmTell(hVfsIosIn);
1530 AssertReturn(offStart >= 0, (int)offStart);
1531
1532 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosIn);
1533 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
1534
1535 /*
1536 * Retain the input stream and create a new filesystem stream handle.
1537 */
1538 PRTZIPTARFSSTREAM pThis;
1539 RTVFSFSSTREAM hVfsFss;
1540 int rc = RTVfsNewFsStream(&rtZipTarFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFss, (void **)&pThis);
1541 if (RT_SUCCESS(rc))
1542 {
1543 pThis->hVfsIos = hVfsIosIn;
1544 pThis->hVfsCurObj = NIL_RTVFSOBJ;
1545 pThis->pCurIosData = NULL;
1546 pThis->offStart = offStart;
1547 pThis->offNextHdr = offStart;
1548 pThis->fEndOfStream = false;
1549 pThis->rcFatal = VINF_SUCCESS;
1550 pThis->TarReader.enmPrevType= RTZIPTARTYPE_INVALID;
1551 pThis->TarReader.enmType = RTZIPTARTYPE_INVALID;
1552 pThis->TarReader.enmState = RTZIPTARREADERSTATE_FIRST;
1553
1554 /* Don't check if it's a TAR stream here, do that in the
1555 rtZipTarFss_Next. */
1556
1557 *phVfsFss = hVfsFss;
1558 return VINF_SUCCESS;
1559 }
1560
1561 RTVfsIoStrmRelease(hVfsIosIn);
1562 return rc;
1563}
1564
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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