VirtualBox

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

Last change on this file since 57358 was 57358, checked in by vboxsync, 9 years ago

*: scm cleanup run.

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