VirtualBox

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

Last change on this file since 50146 was 50146, checked in by vboxsync, 11 years ago

gzipvfs.cpp,tarvfs.cpp: Allow the pfnRead off parameter to be the current stream offset, and in the tar case anything within the current-file in the tar stream.

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