VirtualBox

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

Last change on this file since 50201 was 50201, checked in by vboxsync, 11 years ago

rtZipTarReaderIsAtEnd: Hack for our tar.cpp writer code.

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