VirtualBox

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

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

tarvfs.cpp: Fix for VMWare header with non-posix version field content.

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