VirtualBox

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

Last change on this file since 88485 was 85126, checked in by vboxsync, 5 years ago

iprt/cdefs.h,*: Adding DECL_HIDDEN_CALLBACK to shorten the relatively common DECLHIDDEN(DECLCALLBACK()) combination and to help tweaking DECLHIDDEN to include nothrow. bugref:9794

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