VirtualBox

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

Last change on this file since 62477 was 62477, checked in by vboxsync, 8 years ago

(C) 2016

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

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