VirtualBox

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

Last change on this file since 57444 was 57368, checked in by vboxsync, 9 years ago

fix

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