VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 8 weeks ago

Copyright year updates by scm.

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