VirtualBox

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

Last change on this file since 67482 was 67149, checked in by vboxsync, 8 years ago

IPRT/vfstarwriter.cpp: Early version of API for pushing file data into the FS stream.

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