VirtualBox

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

Last change on this file since 72437 was 69219, checked in by vboxsync, 7 years ago

Runtime: scm cleanups

  • 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 69219 2017-10-24 15:01:30Z vboxsync $ */
2/** @file
3 * IPRT - TAR Virtual Filesystem, Reader.
4 */
5
6/*
7 * Copyright (C) 2010-2017 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
775 default:
776 return VERR_TAR_UNKNOWN_TYPE_FLAG; /* Should've been caught in validate. */
777 }
778 if ( (pObjInfo->Attr.fMode & RTFS_TYPE_MASK)
779 && (pObjInfo->Attr.fMode & RTFS_TYPE_MASK) != fModeType)
780 return VERR_TAR_MODE_WITH_TYPE;
781 pObjInfo->Attr.fMode &= ~RTFS_TYPE_MASK;
782 pObjInfo->Attr.fMode |= fModeType;
783
784 switch (pThis->Hdr.Common.typeflag)
785 {
786 case RTZIPTAR_TF_CHR:
787 case RTZIPTAR_TF_BLK:
788 case RTZIPTAR_TF_DIR:
789 case RTZIPTAR_TF_FIFO:
790 pObjInfo->cbObject = 0;
791 pObjInfo->cbAllocated = 0;
792 break;
793 }
794
795 return VINF_SUCCESS;
796}
797
798
799/**
800 * Checks if the reader is expecting more headers.
801 *
802 * @returns true / false.
803 * @param pThis The TAR reader instance.
804 */
805static bool rtZipTarReaderExpectingMoreHeaders(PRTZIPTARREADER pThis)
806{
807 return pThis->enmState != RTZIPTARREADERSTATE_FIRST;
808}
809
810
811/**
812 * Checks if we're at the end of the TAR file.
813 *
814 * @returns true / false.
815 * @param pThis The TAR reader instance.
816 */
817static bool rtZipTarReaderIsAtEnd(PRTZIPTARREADER pThis)
818{
819 /*
820 * In theory there shall always be two zero headers at the end of the
821 * archive, but life isn't that simple. We've been creating archives
822 * without any zero headers at the end ourselves for a long long time
823 * (old tar.cpp).
824 *
825 * So, we're fine if the state is 'FIRST' or 'ZERO' here, but we'll barf
826 * if we're in the middle of a multi-header stream (long GNU names, sparse
827 * files, PAX, etc).
828 */
829 return pThis->enmState == RTZIPTARREADERSTATE_FIRST
830 || pThis->enmState == RTZIPTARREADERSTATE_ZERO;
831}
832
833
834/**
835 * Checks if the current TAR object is a hard link or not.
836 *
837 * @returns true if it is, false if not.
838 * @param pThis The TAR reader instance.
839 */
840static bool rtZipTarReaderIsHardlink(PRTZIPTARREADER pThis)
841{
842 return pThis->Hdr.Common.typeflag == RTZIPTAR_TF_LINK;
843}
844
845
846/**
847 * Checks if the TAR header includes a POSIX or GNU user name field.
848 *
849 * @returns true / false.
850 * @param pThis The TAR reader instance.
851 */
852DECLINLINE(bool) rtZipTarReaderHasUserName(PRTZIPTARREADER pThis)
853{
854 return pThis->Hdr.Common.uname[0] != '\0'
855 && ( pThis->enmType == RTZIPTARTYPE_POSIX
856 || pThis->enmType == RTZIPTARTYPE_GNU);
857}
858
859
860/**
861 * Checks if the TAR header includes a POSIX or GNU group name field.
862 *
863 * @returns true / false.
864 * @param pThis The TAR reader instance.
865 */
866DECLINLINE(bool) rtZipTarReaderHasGroupName(PRTZIPTARREADER pThis)
867{
868 return pThis->Hdr.Common.gname[0] != '\0'
869 && ( pThis->enmType == RTZIPTARTYPE_POSIX
870 || pThis->enmType == RTZIPTARTYPE_GNU);
871}
872
873
874/*
875 *
876 * 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.
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 *
880 */
881
882/**
883 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
884 */
885static DECLCALLBACK(int) rtZipTarFssBaseObj_Close(void *pvThis)
886{
887 PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis;
888
889 /* Currently there is nothing we really have to do here. */
890 pThis->offHdr = -1;
891
892 return VINF_SUCCESS;
893}
894
895
896/**
897 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
898 */
899static DECLCALLBACK(int) rtZipTarFssBaseObj_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
900{
901 PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis;
902
903 /*
904 * Copy the desired data.
905 */
906 switch (enmAddAttr)
907 {
908 case RTFSOBJATTRADD_NOTHING:
909 case RTFSOBJATTRADD_UNIX:
910 *pObjInfo = pThis->ObjInfo;
911 break;
912
913 case RTFSOBJATTRADD_UNIX_OWNER:
914 *pObjInfo = pThis->ObjInfo;
915 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
916 pObjInfo->Attr.u.UnixOwner.uid = pThis->ObjInfo.Attr.u.Unix.uid;
917 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
918 if (rtZipTarReaderHasUserName(pThis->pTarReader))
919 RTStrCopy(pObjInfo->Attr.u.UnixOwner.szName, sizeof(pObjInfo->Attr.u.UnixOwner.szName),
920 pThis->pTarReader->Hdr.Common.uname);
921 break;
922
923 case RTFSOBJATTRADD_UNIX_GROUP:
924 *pObjInfo = pThis->ObjInfo;
925 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
926 pObjInfo->Attr.u.UnixGroup.gid = pThis->ObjInfo.Attr.u.Unix.gid;
927 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
928 if (rtZipTarReaderHasGroupName(pThis->pTarReader))
929 RTStrCopy(pObjInfo->Attr.u.UnixGroup.szName, sizeof(pObjInfo->Attr.u.UnixGroup.szName),
930 pThis->pTarReader->Hdr.Common.gname);
931 break;
932
933 case RTFSOBJATTRADD_EASIZE:
934 *pObjInfo = pThis->ObjInfo;
935 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
936 RT_ZERO(pObjInfo->Attr.u);
937 break;
938
939 default:
940 return VERR_NOT_SUPPORTED;
941 }
942
943 return VINF_SUCCESS;
944}
945
946
947/**
948 * Tar filesystem base object operations.
949 */
950static const RTVFSOBJOPS g_rtZipTarFssBaseObjOps =
951{
952 RTVFSOBJOPS_VERSION,
953 RTVFSOBJTYPE_BASE,
954 "TarFsStream::Obj",
955 rtZipTarFssBaseObj_Close,
956 rtZipTarFssBaseObj_QueryInfo,
957 RTVFSOBJOPS_VERSION
958};
959
960
961/**
962 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
963 */
964static DECLCALLBACK(int) rtZipTarFssIos_Close(void *pvThis)
965{
966 PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis;
967
968 RTVfsIoStrmRelease(pThis->hVfsIos);
969 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
970
971 return rtZipTarFssBaseObj_Close(&pThis->BaseObj);
972}
973
974
975/**
976 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
977 */
978static DECLCALLBACK(int) rtZipTarFssIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
979{
980 PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis;
981 return rtZipTarFssBaseObj_QueryInfo(&pThis->BaseObj, pObjInfo, enmAddAttr);
982}
983
984
985/**
986 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
987 */
988static DECLCALLBACK(int) rtZipTarFssIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
989{
990 PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis;
991 Assert(pSgBuf->cSegs == 1);
992
993 /*
994 * Make offset into a real offset so it's possible to do random access
995 * on TAR files that are seekable. Fend of reads beyond the end of the
996 * stream.
997 */
998 if (off < 0)
999 off = pThis->offFile;
1000 if (off >= pThis->cbFile)
1001 return pcbRead ? VINF_EOF : VERR_EOF;
1002
1003
1004 Assert(pThis->cbFile >= pThis->offFile);
1005 uint64_t cbLeft = (uint64_t)(pThis->cbFile - pThis->offFile);
1006 size_t cbToRead = pSgBuf->paSegs[0].cbSeg;
1007 if (cbToRead > cbLeft)
1008 {
1009 if (!pcbRead)
1010 return VERR_EOF;
1011 cbToRead = (size_t)cbLeft;
1012 }
1013
1014 /*
1015 * Do the reading.
1016 */
1017 size_t cbReadStack = 0;
1018 if (!pcbRead)
1019 pcbRead = &cbReadStack;
1020 int rc = RTVfsIoStrmReadAt(pThis->hVfsIos, pThis->offStart + off, pSgBuf->paSegs[0].pvSeg, cbToRead, fBlocking, pcbRead);
1021 pThis->offFile = off + *pcbRead;
1022 if (pThis->offFile >= pThis->cbFile)
1023 {
1024 Assert(pThis->offFile == pThis->cbFile);
1025 pThis->fEndOfStream = true;
1026 RTVfsIoStrmSkip(pThis->hVfsIos, pThis->cbPadding);
1027 }
1028
1029 return rc;
1030}
1031
1032
1033/**
1034 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
1035 */
1036static DECLCALLBACK(int) rtZipTarFssIos_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
1037{
1038 /* Cannot write to a read-only I/O stream. */
1039 NOREF(pvThis); NOREF(off); NOREF(pSgBuf); NOREF(fBlocking); NOREF(pcbWritten);
1040 return VERR_ACCESS_DENIED;
1041}
1042
1043
1044/**
1045 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
1046 */
1047static DECLCALLBACK(int) rtZipTarFssIos_Flush(void *pvThis)
1048{
1049 /* It's a read only stream, nothing dirty to flush. */
1050 NOREF(pvThis);
1051 return VINF_SUCCESS;
1052}
1053
1054
1055/**
1056 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
1057 */
1058static DECLCALLBACK(int) rtZipTarFssIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
1059 uint32_t *pfRetEvents)
1060{
1061 PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis;
1062
1063 /* When we've reached the end, read will be set to indicate it. */
1064 if ( (fEvents & RTPOLL_EVT_READ)
1065 && pThis->fEndOfStream)
1066 {
1067 int rc = RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, 0, fIntr, pfRetEvents);
1068 if (RT_SUCCESS(rc))
1069 *pfRetEvents |= RTPOLL_EVT_READ;
1070 else
1071 *pfRetEvents = RTPOLL_EVT_READ;
1072 return VINF_SUCCESS;
1073 }
1074
1075 return RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents);
1076}
1077
1078
1079/**
1080 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
1081 */
1082static DECLCALLBACK(int) rtZipTarFssIos_Tell(void *pvThis, PRTFOFF poffActual)
1083{
1084 PRTZIPTARIOSTREAM pThis = (PRTZIPTARIOSTREAM)pvThis;
1085 *poffActual = pThis->offFile;
1086 return VINF_SUCCESS;
1087}
1088
1089
1090/**
1091 * Tar I/O stream operations.
1092 */
1093static const RTVFSIOSTREAMOPS g_rtZipTarFssIosOps =
1094{
1095 { /* Obj */
1096 RTVFSOBJOPS_VERSION,
1097 RTVFSOBJTYPE_IO_STREAM,
1098 "TarFsStream::IoStream",
1099 rtZipTarFssIos_Close,
1100 rtZipTarFssIos_QueryInfo,
1101 RTVFSOBJOPS_VERSION
1102 },
1103 RTVFSIOSTREAMOPS_VERSION,
1104 RTVFSIOSTREAMOPS_FEAT_NO_SG,
1105 rtZipTarFssIos_Read,
1106 rtZipTarFssIos_Write,
1107 rtZipTarFssIos_Flush,
1108 rtZipTarFssIos_PollOne,
1109 rtZipTarFssIos_Tell,
1110 NULL /*Skip*/,
1111 NULL /*ZeroFill*/,
1112 RTVFSIOSTREAMOPS_VERSION
1113};
1114
1115
1116/**
1117 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1118 */
1119static DECLCALLBACK(int) rtZipTarFssSym_Close(void *pvThis)
1120{
1121 PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis;
1122 return rtZipTarFssBaseObj_Close(pThis);
1123}
1124
1125
1126/**
1127 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1128 */
1129static DECLCALLBACK(int) rtZipTarFssSym_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1130{
1131 PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis;
1132 return rtZipTarFssBaseObj_QueryInfo(pThis, pObjInfo, enmAddAttr);
1133}
1134
1135/**
1136 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
1137 */
1138static DECLCALLBACK(int) rtZipTarFssSym_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
1139{
1140 NOREF(pvThis); NOREF(fMode); NOREF(fMask);
1141 return VERR_ACCESS_DENIED;
1142}
1143
1144
1145/**
1146 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
1147 */
1148static DECLCALLBACK(int) rtZipTarFssSym_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1149 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1150{
1151 NOREF(pvThis); NOREF(pAccessTime); NOREF(pModificationTime); NOREF(pChangeTime); NOREF(pBirthTime);
1152 return VERR_ACCESS_DENIED;
1153}
1154
1155
1156/**
1157 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
1158 */
1159static DECLCALLBACK(int) rtZipTarFssSym_SetOwner(void *pvThis, RTUID uid, RTGID gid)
1160{
1161 NOREF(pvThis); NOREF(uid); NOREF(gid);
1162 return VERR_ACCESS_DENIED;
1163}
1164
1165
1166/**
1167 * @interface_method_impl{RTVFSSYMLINKOPS,pfnRead}
1168 */
1169static DECLCALLBACK(int) rtZipTarFssSym_Read(void *pvThis, char *pszTarget, size_t cbTarget)
1170{
1171 PRTZIPTARBASEOBJ pThis = (PRTZIPTARBASEOBJ)pvThis;
1172 return RTStrCopy(pszTarget, cbTarget, pThis->pTarReader->szTarget);
1173}
1174
1175
1176/**
1177 * Tar symbolic (and hardlink) operations.
1178 */
1179static const RTVFSSYMLINKOPS g_rtZipTarFssSymOps =
1180{
1181 { /* Obj */
1182 RTVFSOBJOPS_VERSION,
1183 RTVFSOBJTYPE_SYMLINK,
1184 "TarFsStream::Symlink",
1185 rtZipTarFssSym_Close,
1186 rtZipTarFssSym_QueryInfo,
1187 RTVFSOBJOPS_VERSION
1188 },
1189 RTVFSSYMLINKOPS_VERSION,
1190 0,
1191 { /* ObjSet */
1192 RTVFSOBJSETOPS_VERSION,
1193 RT_OFFSETOF(RTVFSSYMLINKOPS, Obj) - RT_OFFSETOF(RTVFSSYMLINKOPS, ObjSet),
1194 rtZipTarFssSym_SetMode,
1195 rtZipTarFssSym_SetTimes,
1196 rtZipTarFssSym_SetOwner,
1197 RTVFSOBJSETOPS_VERSION
1198 },
1199 rtZipTarFssSym_Read,
1200 RTVFSSYMLINKOPS_VERSION
1201};
1202
1203
1204/**
1205 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1206 */
1207static DECLCALLBACK(int) rtZipTarFss_Close(void *pvThis)
1208{
1209 PRTZIPTARFSSTREAM pThis = (PRTZIPTARFSSTREAM)pvThis;
1210
1211 RTVfsObjRelease(pThis->hVfsCurObj);
1212 pThis->hVfsCurObj = NIL_RTVFSOBJ;
1213 pThis->pCurIosData = NULL;
1214
1215 RTVfsIoStrmRelease(pThis->hVfsIos);
1216 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
1217
1218 return VINF_SUCCESS;
1219}
1220
1221
1222/**
1223 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1224 */
1225static DECLCALLBACK(int) rtZipTarFss_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1226{
1227 PRTZIPTARFSSTREAM pThis = (PRTZIPTARFSSTREAM)pvThis;
1228 /* Take the lazy approach here, with the sideffect of providing some info
1229 that is actually kind of useful. */
1230 return RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
1231}
1232
1233
1234/**
1235 * @interface_method_impl{RTVFSFSSTREAMOPS,pfnNext}
1236 */
1237static DECLCALLBACK(int) rtZipTarFss_Next(void *pvThis, char **ppszName, RTVFSOBJTYPE *penmType, PRTVFSOBJ phVfsObj)
1238{
1239 PRTZIPTARFSSTREAM pThis = (PRTZIPTARFSSTREAM)pvThis;
1240
1241 /*
1242 * Dispense with the current object.
1243 */
1244 if (pThis->hVfsCurObj != NIL_RTVFSOBJ)
1245 {
1246 if (pThis->pCurIosData)
1247 {
1248 pThis->pCurIosData->fEndOfStream = true;
1249 pThis->pCurIosData->offFile = pThis->pCurIosData->cbFile;
1250 pThis->pCurIosData = NULL;
1251 }
1252
1253 RTVfsObjRelease(pThis->hVfsCurObj);
1254 pThis->hVfsCurObj = NIL_RTVFSOBJ;
1255 }
1256
1257 /*
1258 * Check if we've already reached the end in some way.
1259 */
1260 if (pThis->fEndOfStream)
1261 return VERR_EOF;
1262 if (pThis->rcFatal != VINF_SUCCESS)
1263 return pThis->rcFatal;
1264
1265 /*
1266 * Make sure the input stream is in the right place.
1267 */
1268 RTFOFF offHdr = RTVfsIoStrmTell(pThis->hVfsIos);
1269 while ( offHdr >= 0
1270 && offHdr < pThis->offNextHdr)
1271 {
1272 int rc = RTVfsIoStrmSkip(pThis->hVfsIos, pThis->offNextHdr - offHdr);
1273 if (RT_FAILURE(rc))
1274 {
1275 /** @todo Ignore if we're at the end of the stream? */
1276 return pThis->rcFatal = rc;
1277 }
1278
1279 offHdr = RTVfsIoStrmTell(pThis->hVfsIos);
1280 }
1281
1282 if (offHdr < 0)
1283 return pThis->rcFatal = (int)offHdr;
1284 if (offHdr > pThis->offNextHdr)
1285 return pThis->rcFatal = VERR_INTERNAL_ERROR_3;
1286
1287 /*
1288 * Consume TAR headers.
1289 */
1290 size_t cbHdrs = 0;
1291 int rc;
1292 do
1293 {
1294 /*
1295 * Read the next header.
1296 */
1297 RTZIPTARHDR Hdr;
1298 size_t cbRead;
1299 rc = RTVfsIoStrmRead(pThis->hVfsIos, &Hdr, sizeof(Hdr), true /*fBlocking*/, &cbRead);
1300 if (RT_FAILURE(rc))
1301 return pThis->rcFatal = rc;
1302 if (rc == VINF_EOF && cbRead == 0)
1303 {
1304 pThis->fEndOfStream = true;
1305 return rtZipTarReaderIsAtEnd(&pThis->TarReader) ? VERR_EOF : VERR_TAR_UNEXPECTED_EOS;
1306 }
1307 if (cbRead != sizeof(Hdr))
1308 return pThis->rcFatal = VERR_TAR_UNEXPECTED_EOS;
1309
1310 cbHdrs += sizeof(Hdr);
1311
1312 /*
1313 * Parse the it.
1314 */
1315 rc = rtZipTarReaderParseHeader(&pThis->TarReader, &Hdr);
1316 if (RT_FAILURE(rc))
1317 return pThis->rcFatal = rc;
1318 } while (rtZipTarReaderExpectingMoreHeaders(&pThis->TarReader));
1319
1320 pThis->offNextHdr = offHdr + cbHdrs;
1321
1322 /*
1323 * Fill an object info structure from the current TAR state.
1324 */
1325 RTFSOBJINFO Info;
1326 rc = rtZipTarReaderGetFsObjInfo(&pThis->TarReader, &Info);
1327 if (RT_FAILURE(rc))
1328 return pThis->rcFatal = rc;
1329
1330 /*
1331 * Create an object of the appropriate type.
1332 */
1333 RTVFSOBJTYPE enmType;
1334 RTVFSOBJ hVfsObj;
1335 RTFMODE fType = Info.Attr.fMode & RTFS_TYPE_MASK;
1336 if (rtZipTarReaderIsHardlink(&pThis->TarReader))
1337 fType = RTFS_TYPE_SYMLINK;
1338 switch (fType)
1339 {
1340 /*
1341 * Files are represented by a VFS I/O stream.
1342 */
1343 case RTFS_TYPE_FILE:
1344 {
1345 RTVFSIOSTREAM hVfsIos;
1346 PRTZIPTARIOSTREAM pIosData;
1347 rc = RTVfsNewIoStream(&g_rtZipTarFssIosOps,
1348 sizeof(*pIosData),
1349 RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
1350 NIL_RTVFS,
1351 NIL_RTVFSLOCK,
1352 &hVfsIos,
1353 (void **)&pIosData);
1354 if (RT_FAILURE(rc))
1355 return pThis->rcFatal = rc;
1356
1357 pIosData->BaseObj.offHdr = offHdr;
1358 pIosData->BaseObj.pTarReader= &pThis->TarReader;
1359 pIosData->BaseObj.ObjInfo = Info;
1360 pIosData->cbFile = Info.cbObject;
1361 pIosData->offFile = 0;
1362 pIosData->offStart = RTVfsIoStrmTell(pThis->hVfsIos);
1363 pIosData->cbPadding = (uint32_t)(Info.cbAllocated - Info.cbObject);
1364 pIosData->fEndOfStream = false;
1365 pIosData->hVfsIos = pThis->hVfsIos;
1366 RTVfsIoStrmRetain(pThis->hVfsIos);
1367
1368 pThis->pCurIosData = pIosData;
1369 pThis->offNextHdr += pIosData->cbFile + pIosData->cbPadding;
1370
1371 enmType = RTVFSOBJTYPE_IO_STREAM;
1372 hVfsObj = RTVfsObjFromIoStream(hVfsIos);
1373 RTVfsIoStrmRelease(hVfsIos);
1374 break;
1375 }
1376
1377 /*
1378 * We represent hard links using a symbolic link object. This fits
1379 * best with the way TAR stores it and there is currently no better
1380 * fitting VFS type alternative.
1381 */
1382 case RTFS_TYPE_SYMLINK:
1383 {
1384 RTVFSSYMLINK hVfsSym;
1385 PRTZIPTARBASEOBJ pBaseObjData;
1386 rc = RTVfsNewSymlink(&g_rtZipTarFssSymOps,
1387 sizeof(*pBaseObjData),
1388 NIL_RTVFS,
1389 NIL_RTVFSLOCK,
1390 &hVfsSym,
1391 (void **)&pBaseObjData);
1392 if (RT_FAILURE(rc))
1393 return pThis->rcFatal = rc;
1394
1395 pBaseObjData->offHdr = offHdr;
1396 pBaseObjData->pTarReader= &pThis->TarReader;
1397 pBaseObjData->ObjInfo = Info;
1398
1399 enmType = RTVFSOBJTYPE_SYMLINK;
1400 hVfsObj = RTVfsObjFromSymlink(hVfsSym);
1401 RTVfsSymlinkRelease(hVfsSym);
1402 break;
1403 }
1404
1405 /*
1406 * All other objects are repesented using a VFS base object since they
1407 * carry no data streams (unless some TAR extension implements extended
1408 * attributes / alternative streams).
1409 */
1410 case RTFS_TYPE_DEV_BLOCK:
1411 case RTFS_TYPE_DEV_CHAR:
1412 case RTFS_TYPE_DIRECTORY:
1413 case RTFS_TYPE_FIFO:
1414 {
1415 PRTZIPTARBASEOBJ pBaseObjData;
1416 rc = RTVfsNewBaseObj(&g_rtZipTarFssBaseObjOps,
1417 sizeof(*pBaseObjData),
1418 NIL_RTVFS,
1419 NIL_RTVFSLOCK,
1420 &hVfsObj,
1421 (void **)&pBaseObjData);
1422 if (RT_FAILURE(rc))
1423 return pThis->rcFatal = rc;
1424
1425 pBaseObjData->offHdr = offHdr;
1426 pBaseObjData->pTarReader= &pThis->TarReader;
1427 pBaseObjData->ObjInfo = Info;
1428
1429 enmType = RTVFSOBJTYPE_BASE;
1430 break;
1431 }
1432
1433 default:
1434 AssertFailed();
1435 return pThis->rcFatal = VERR_INTERNAL_ERROR_5;
1436 }
1437 pThis->hVfsCurObj = hVfsObj;
1438
1439 /*
1440 * Set the return data and we're done.
1441 */
1442 if (ppszName)
1443 {
1444 rc = RTStrDupEx(ppszName, pThis->TarReader.szName);
1445 if (RT_FAILURE(rc))
1446 return rc;
1447 }
1448
1449 if (phVfsObj)
1450 {
1451 RTVfsObjRetain(hVfsObj);
1452 *phVfsObj = hVfsObj;
1453 }
1454
1455 if (penmType)
1456 *penmType = enmType;
1457
1458 return VINF_SUCCESS;
1459}
1460
1461
1462
1463/**
1464 * Tar filesystem stream operations.
1465 */
1466static const RTVFSFSSTREAMOPS rtZipTarFssOps =
1467{
1468 { /* Obj */
1469 RTVFSOBJOPS_VERSION,
1470 RTVFSOBJTYPE_FS_STREAM,
1471 "TarFsStream",
1472 rtZipTarFss_Close,
1473 rtZipTarFss_QueryInfo,
1474 RTVFSOBJOPS_VERSION
1475 },
1476 RTVFSFSSTREAMOPS_VERSION,
1477 0,
1478 rtZipTarFss_Next,
1479 NULL,
1480 NULL,
1481 NULL,
1482 RTVFSFSSTREAMOPS_VERSION
1483};
1484
1485
1486RTDECL(int) RTZipTarFsStreamFromIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
1487{
1488 /*
1489 * Input validation.
1490 */
1491 AssertPtrReturn(phVfsFss, VERR_INVALID_HANDLE);
1492 *phVfsFss = NIL_RTVFSFSSTREAM;
1493 AssertPtrReturn(hVfsIosIn, VERR_INVALID_HANDLE);
1494 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
1495
1496 RTFOFF const offStart = RTVfsIoStrmTell(hVfsIosIn);
1497 AssertReturn(offStart >= 0, (int)offStart);
1498
1499 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosIn);
1500 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
1501
1502 /*
1503 * Retain the input stream and create a new filesystem stream handle.
1504 */
1505 PRTZIPTARFSSTREAM pThis;
1506 RTVFSFSSTREAM hVfsFss;
1507 int rc = RTVfsNewFsStream(&rtZipTarFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, true /*fReadOnly*/,
1508 &hVfsFss, (void **)&pThis);
1509 if (RT_SUCCESS(rc))
1510 {
1511 pThis->hVfsIos = hVfsIosIn;
1512 pThis->hVfsCurObj = NIL_RTVFSOBJ;
1513 pThis->pCurIosData = NULL;
1514 pThis->offStart = offStart;
1515 pThis->offNextHdr = offStart;
1516 pThis->fEndOfStream = false;
1517 pThis->rcFatal = VINF_SUCCESS;
1518 pThis->TarReader.enmPrevType= RTZIPTARTYPE_INVALID;
1519 pThis->TarReader.enmType = RTZIPTARTYPE_INVALID;
1520 pThis->TarReader.enmState = RTZIPTARREADERSTATE_FIRST;
1521
1522 /* Don't check if it's a TAR stream here, do that in the
1523 rtZipTarFss_Next. */
1524
1525 *phVfsFss = hVfsFss;
1526 return VINF_SUCCESS;
1527 }
1528
1529 RTVfsIoStrmRelease(hVfsIosIn);
1530 return rc;
1531}
1532
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