VirtualBox

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

Last change on this file since 78052 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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