VirtualBox

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

Last change on this file since 44278 was 39083, checked in by vboxsync, 13 years ago

IPRT: -Wunused-parameter.

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