summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvboxsync <vboxsync@cfe28804-0f27-0410-a406-dd0f0b0b656f>2020-09-06 20:31:16 +0000
committervboxsync <vboxsync@cfe28804-0f27-0410-a406-dd0f0b0b656f>2020-09-06 20:31:16 +0000
commitf4ef3ebecea225f63b830842eeb586841c966563 (patch)
tree348dad4372e291f604c73b3102a76c0805848ad6
parent7e8b8c0499a32911ebe877c5ce958497dfb7fe5f (diff)
downloadVirtualBox-svn-f4ef3ebecea225f63b830842eeb586841c966563.tar.gz
Runtime/cpiovfs.cpp: A simple CPIO archive reader (WIP, writer comes next and then some cleanup)
git-svn-id: https://www.virtualbox.org/svn/vbox/trunk@86035 cfe28804-0f27-0410-a406-dd0f0b0b656f
-rw-r--r--include/iprt/formats/cpio.h200
-rw-r--r--include/iprt/mangling.h1
-rw-r--r--include/iprt/zip.h15
-rw-r--r--src/VBox/Runtime/Makefile.kmk1
-rw-r--r--src/VBox/Runtime/common/zip/cpiovfs.cpp1132
-rw-r--r--src/VBox/Runtime/common/zip/cpiovfsreader.h159
-rw-r--r--src/VBox/Runtime/common/zip/tarcmd.cpp14
7 files changed, 1519 insertions, 3 deletions
diff --git a/include/iprt/formats/cpio.h b/include/iprt/formats/cpio.h
new file mode 100644
index 00000000000..1d898a6217e
--- /dev/null
+++ b/include/iprt/formats/cpio.h
@@ -0,0 +1,200 @@
+/** @file
+ * IPRT - CPIO archive format.
+ */
+
+/*
+ * Copyright (C) 2020 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+#ifndef IPRT_INCLUDED_formats_cpio_h
+#define IPRT_INCLUDED_formats_cpio_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+#include <iprt/assertcompile.h>
+
+
+/** @defgroup grp_rt_formats_cpio CPIO Archive format
+ * @ingroup grp_rt_formats
+ *
+ * @{ */
+
+/** This denotes the end of the archive (record with this filename, zero size and
+ * a zero mode). */
+#define CPIO_EOS_FILE_NAME "TRAILER!!!"
+
+
+/**
+ * The old binary header.
+ */
+typedef struct CPIOHDRBIN
+{
+ /** 0x00: Magic identifying the old header. */
+ uint16_t u16Magic;
+ /** 0x02: Device number. */
+ uint16_t u16Dev;
+ /** 0x04: Inode number. */
+ uint16_t u16Inode;
+ /** 0x06: Mode. */
+ uint16_t u16Mode;
+ /** 0x08: User ID. */
+ uint16_t u16Uid;
+ /** 0x0a: Group ID. */
+ uint16_t u16Gid;
+ /** 0x0c: Number of links to this file. */
+ uint16_t u16NLinks;
+ /** 0x0e: Associated device number for block and character device entries. */
+ uint16_t u16RDev;
+ /** 0x10: Modification time stored as two independent 16bit integers. */
+ uint16_t au16MTime[2];
+ /** 0x14: Number of bytes in the path name (including zero terminator) following the header. */
+ uint16_t u16NameSize;
+ /** 0x16: Size of the file stored as two independent 16bit integers. */
+ uint16_t au16FileSize[2];
+} CPIOHDRBIN;
+AssertCompileSize(CPIOHDRBIN, 13 * 2);
+typedef CPIOHDRBIN *PCPIOHDRBIN;
+typedef const CPIOHDRBIN *PCCPIOHDRBIN;
+
+
+/** The magic for the binary header. */
+#define CPIO_HDR_BIN_MAGIC UINT16_C(070707)
+
+
+/**
+ * Portable ASCII format header as defined by SUSv2.
+ */
+typedef struct CPIOHDRSUSV2
+{
+ /** 0x00: Magic identifying the header. */
+ char achMagic[6];
+ /** 0x06: Device number. */
+ char achDev[6];
+ /** 0x0c: Inode number. */
+ char achInode[6];
+ /** 0x12: Mode. */
+ char achMode[6];
+ /** 0x18: User ID. */
+ char achUid[6];
+ /** 0x1e: Group ID. */
+ char achGid[6];
+ /** 0x24: Number of links to this file. */
+ char achNLinks[6];
+ /** 0x2a: Associated device number for block and character device entries. */
+ char achRDev[6];
+ /** 0x30: Modification time stored as two independent 16bit integers. */
+ char achMTime[11];
+ /** 0x36: Number of bytes in the path name (including zero terminator) following the header. */
+ char achNameSize[6];
+ /** 0x3c: Size of the file stored as two independent 16bit integers. */
+ char achFileSize[11];
+} CPIOHDRSUSV2;
+AssertCompileSize(CPIOHDRSUSV2, 9 * 6 + 2 * 11);
+typedef CPIOHDRSUSV2 *PCPIOHDRSUSV2;
+typedef const CPIOHDRSUSV2 *PCCPIOHDRSUSV2;
+
+
+/** The magic for the SuSv2 CPIO header. */
+#define CPIO_HDR_SUSV2_MAGIC "070707"
+
+
+/**
+ * New ASCII format header.
+ */
+typedef struct CPIOHDRNEW
+{
+ /** 0x00: Magic identifying the header. */
+ char achMagic[6];
+ /** 0x06: Inode number. */
+ char achInode[8];
+ /** 0x0e: Mode. */
+ char achMode[8];
+ /** 0x16: User ID. */
+ char achUid[8];
+ /** 0x1e: Group ID. */
+ char achGid[8];
+ /** 0x26: Number of links to this file. */
+ char achNLinks[8];
+ /** 0x2e: Modification time. */
+ char achMTime[8];
+ /** 0x36: Size of the file stored as two independent 16bit integers. */
+ char achFileSize[8];
+ /** 0x3e: Device major number. */
+ char achDevMajor[8];
+ /** 0x46: Device minor number. */
+ char achDevMinor[8];
+ /** 0x4e: Assigned device major number for block or character device files. */
+ char achRDevMajor[8];
+ /** 0x56: Assigned device minor number for block or character device files. */
+ char achRDevMinor[8];
+ /** 0x5e: Number of bytes in the path name (including zero terminator) following the header. */
+ char achNameSize[8];
+ /** 0x66: Checksum of the file data if used. */
+ char achCheck[8];
+} CPIOHDRNEW;
+AssertCompileSize(CPIOHDRNEW, 6 + 13 * 8);
+AssertCompileMemberOffset(CPIOHDRNEW, achMagic, 0x00);
+AssertCompileMemberOffset(CPIOHDRNEW, achInode, 0x06);
+AssertCompileMemberOffset(CPIOHDRNEW, achMode, 0x0e);
+AssertCompileMemberOffset(CPIOHDRNEW, achUid, 0x16);
+AssertCompileMemberOffset(CPIOHDRNEW, achGid, 0x1e);
+AssertCompileMemberOffset(CPIOHDRNEW, achNLinks, 0x26);
+AssertCompileMemberOffset(CPIOHDRNEW, achMTime, 0x2e);
+AssertCompileMemberOffset(CPIOHDRNEW, achFileSize, 0x36);
+AssertCompileMemberOffset(CPIOHDRNEW, achDevMajor, 0x3e);
+AssertCompileMemberOffset(CPIOHDRNEW, achDevMinor, 0x46);
+AssertCompileMemberOffset(CPIOHDRNEW, achRDevMajor, 0x4e);
+AssertCompileMemberOffset(CPIOHDRNEW, achRDevMinor, 0x56);
+AssertCompileMemberOffset(CPIOHDRNEW, achNameSize, 0x5e);
+AssertCompileMemberOffset(CPIOHDRNEW, achCheck, 0x66);
+typedef CPIOHDRNEW *PCPIOHDRNEW;
+typedef const CPIOHDRNEW *PCCPIOHDRNEW;
+
+
+/** The magic for the new ASCII CPIO header. */
+#define CPIO_HDR_NEW_MAGIC "070701"
+/** The magic for the new ASCII CPIO header + checksum. */
+#define CPIO_HDR_NEW_CHKSUM_MAGIC "070702"
+
+
+/**
+ * CPIO header union.
+ */
+typedef union CPIOHDR
+{
+ /** byte view. */
+ uint8_t ab[110];
+ /** The ancient binary header. */
+ CPIOHDRBIN AncientBin;
+ /** The SuSv2 ASCII header. */
+ CPIOHDRSUSV2 AsciiSuSv2;
+ /** The new ASCII header format. */
+ CPIOHDRNEW AsciiNew;
+} CPIOHDR;
+typedef CPIOHDR *PCPIOHDR;
+typedef const CPIOHDR *PCCPIOHDR;
+
+
+/** @} */
+
+#endif /* !IPRT_INCLUDED_formats_cpio_h */
+
diff --git a/include/iprt/mangling.h b/include/iprt/mangling.h
index cb388635c20..0f9f10a88c2 100644
--- a/include/iprt/mangling.h
+++ b/include/iprt/mangling.h
@@ -2919,6 +2919,7 @@
# define RTZipTarFsStreamTruncate RT_MANGLER(RTZipTarFsStreamTruncate)
# define RTZipXarFsStreamFromIoStream RT_MANGLER(RTZipXarFsStreamFromIoStream)
# define RTZipTarFsStreamForFile RT_MANGLER(RTZipTarFsStreamForFile)
+# define RTZipCpioFsStreamFromIoStream RT_MANGLER(RTZipCpioFsStreamFromIoStream)
/* sort/merge into the above later: */
# define RTAsn1ContentAllocZ RT_MANGLER(RTAsn1ContentAllocZ)
diff --git a/include/iprt/zip.h b/include/iprt/zip.h
index d5272360120..d27979c3c49 100644
--- a/include/iprt/zip.h
+++ b/include/iprt/zip.h
@@ -500,6 +500,21 @@ RTDECL(int) RTZipPkzipMemDecompress(void **ppvDst, size_t *pcbDst, const void *p
*/
RTDECL(int) RTZipXarFsStreamFromIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss);
+/**
+ * Opens a CPIO filesystem stream.
+ *
+ * This is used to extract, list or check a CPIO archive.
+ *
+ * @returns IPRT status code.
+ *
+ * @param hVfsIosIn The input stream. The reference is not
+ * consumed, instead another one is retained.
+ * @param fFlags Flags, MBZ.
+ * @param phVfsFss Where to return the handle to the CPIO
+ * filesystem stream.
+ */
+RTDECL(int) RTZipCpioFsStreamFromIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss);
+
/** @} */
RT_C_DECLS_END
diff --git a/src/VBox/Runtime/Makefile.kmk b/src/VBox/Runtime/Makefile.kmk
index 4f8ef0e86c0..4810a6effe0 100644
--- a/src/VBox/Runtime/Makefile.kmk
+++ b/src/VBox/Runtime/Makefile.kmk
@@ -681,6 +681,7 @@ RuntimeR3_SOURCES = \
common/vfs/vfsstdfile.cpp \
common/vfs/vfsstdpipe.cpp \
common/vfs/vfsprintf.cpp \
+ common/zip/cpiovfs.cpp \
common/zip/tar.cpp \
common/zip/tarcmd.cpp \
common/zip/tarvfs.cpp \
diff --git a/src/VBox/Runtime/common/zip/cpiovfs.cpp b/src/VBox/Runtime/common/zip/cpiovfs.cpp
new file mode 100644
index 00000000000..d8b2f8fffe0
--- /dev/null
+++ b/src/VBox/Runtime/common/zip/cpiovfs.cpp
@@ -0,0 +1,1132 @@
+/* $Id$ */
+/** @file
+ * IPRT - CPIO Virtual Filesystem, Reader.
+ */
+
+/*
+ * Copyright (C) 2020 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/zip.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/poll.h>
+#include <iprt/file.h>
+#include <iprt/string.h>
+#include <iprt/vfs.h>
+#include <iprt/vfslowlevel.h>
+
+#include "cpiovfsreader.h"
+
+
+/**
+ * Converts a octal numeric header field to the C native type.
+ *
+ * @returns IPRT status code.
+ * @param pachField The CPIO header field.
+ * @param cchField The length of the field.
+ * @param pi64 Where to store the value.
+ */
+static int rtZipCpioHdrOctalFieldToNum(const char *pachField, size_t cchField, int64_t *pi64)
+{
+ /*
+ * Skip leading zeros to save a few slower loops below.
+ */
+ while (cchField > 0 && *pachField == '0')
+ cchField--, pachField++;
+
+ /*
+ * Convert octal digits.
+ */
+ int64_t i64 = 0;
+ while (cchField > 0)
+ {
+ unsigned char uDigit = *pachField - '0';
+ if (uDigit >= 8)
+ return VERR_TAR_BAD_NUM_FIELD;
+ i64 <<= 3;
+ i64 |= uDigit;
+
+ pachField++;
+ cchField--;
+ }
+ *pi64 = i64;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Converts a hex character to the appropriate nibble.
+ *
+ * @returns Nibble of the character.
+ * @param chVal The value to convert.
+ */
+static inline uint8_t rtZipCpioHexToNibble(char chVal)
+{
+ if (chVal >= '0' && chVal <= '9')
+ return chVal - '0';
+ else if (chVal >= 'a' && chVal <= 'f')
+ return chVal - 'a' + 10;
+ else if (chVal >= 'A' && chVal <= 'F')
+ return chVal - 'A' + 10;
+
+ return 0xff;
+}
+
+
+/**
+ * Converts a hexadecimal numeric header field to the C native type.
+ *
+ * @returns IPRT status code.
+ * @param pachField The CPIO header field.
+ * @param cchField The length of the field.
+ * @param pi64 Where to store the value.
+ */
+static int rtZipCpioHdrHexFieldToNum(const char *pachField, size_t cchField, int64_t *pi64)
+{
+ uint64_t u64 = 0;
+
+ while (cchField-- > 0)
+ {
+ uint8_t bNb = rtZipCpioHexToNibble(*pachField++);
+
+ if (RT_LIKELY(bNb != 0xff))
+ u64 = (u64 << 4) | bNb;
+ else
+ return VERR_TAR_BAD_NUM_FIELD;
+ }
+
+ *pi64 = (int64_t)u64;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Parses the given ancient binary header and converts it to an FS object info structure.
+ *
+ * @returns IPRT status code.
+ * @param pThis The CPIO reader state.
+ * @param pHdr The header to convert.
+ * @param pcbFilePath Where to store the file path size on success.
+ * @param pcbPad Where to store the number of bytes padded after the header and file path
+ * before the content begins.
+ */
+static int rtZipCpioReaderParseHeaderAncientBin(PRTZIPCPIOREADER pThis, PCCPIOHDRBIN pHdr,
+ uint32_t *pcbFilePath, uint32_t *pcbPad)
+{
+ RT_NOREF(pThis, pHdr, pcbFilePath, pcbPad);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * Parses the given SuSv2 ASCII header and converts it to an FS object info structure.
+ *
+ * @returns IPRT status code.
+ * @param pThis The CPIO reader state.
+ * @param pHdr The header to convert.
+ * @param pcbFilePath Where to store the file path size on success.
+ * @param pcbPad Where to store the number of bytes padded after the header and file path
+ * before the content begins.
+ */
+static int rtZipCpioReaderParseHeaderAsciiSusV2(PRTZIPCPIOREADER pThis, PCCPIOHDRSUSV2 pHdr,
+ uint32_t *pcbFilePath, uint32_t *pcbPad)
+{
+ PRTFSOBJINFO pObjInfo = &pThis->ObjInfo;
+ int rc;
+ int64_t i64Tmp;
+ int64_t c64SecModTime;
+
+ pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
+ pObjInfo->Attr.u.Unix.Device = 0;
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+
+#define GET_CPIO_NUMERIC_FIELD_RET(a_Var, a_Field) \
+ do { \
+ rc = rtZipCpioHdrOctalFieldToNum(a_Field, sizeof(a_Field), &i64Tmp); \
+ if (RT_FAILURE(rc)) \
+ return rc; \
+ (a_Var) = i64Tmp; \
+ if ((a_Var) != i64Tmp) \
+ return VERR_TAR_NUM_VALUE_TOO_LARGE; \
+ } while (0)
+
+#define GET_CPIO_NUMERIC_FIELD_RET_U64(a_Var, a_Field) \
+ do { \
+ rc = rtZipCpioHdrOctalFieldToNum(a_Field, sizeof(a_Field), &i64Tmp); \
+ if (RT_FAILURE(rc)) \
+ return rc; \
+ (a_Var) = (uint64_t)i64Tmp; \
+ if ((a_Var) != (uint64_t)i64Tmp) \
+ return VERR_TAR_NUM_VALUE_TOO_LARGE; \
+ } while (0)
+
+ GET_CPIO_NUMERIC_FIELD_RET( pObjInfo->Attr.fMode, pHdr->achMode);
+ GET_CPIO_NUMERIC_FIELD_RET( pObjInfo->Attr.u.Unix.uid, pHdr->achUid);
+ GET_CPIO_NUMERIC_FIELD_RET( pObjInfo->Attr.u.Unix.gid, pHdr->achGid);
+ GET_CPIO_NUMERIC_FIELD_RET( pObjInfo->Attr.u.Unix.cHardlinks, pHdr->achNLinks);
+ GET_CPIO_NUMERIC_FIELD_RET_U64(pObjInfo->Attr.u.Unix.INodeId, pHdr->achInode);
+ GET_CPIO_NUMERIC_FIELD_RET( pObjInfo->Attr.u.Unix.Device, pHdr->achDev);
+ GET_CPIO_NUMERIC_FIELD_RET( pObjInfo->cbObject, pHdr->achFileSize);
+ pObjInfo->cbAllocated = pObjInfo->cbObject;
+ GET_CPIO_NUMERIC_FIELD_RET( c64SecModTime, pHdr->achMTime);
+ RTTimeSpecSetSeconds(&pObjInfo->ChangeTime, c64SecModTime);
+ RTTimeSpecSetSeconds(&pObjInfo->ModificationTime, c64SecModTime);
+ RTTimeSpecSetSeconds(&pObjInfo->AccessTime, c64SecModTime);
+ RTTimeSpecSetSeconds(&pObjInfo->BirthTime, c64SecModTime);
+ if (c64SecModTime != RTTimeSpecGetSeconds(&pObjInfo->ModificationTime))
+ return VERR_TAR_NUM_VALUE_TOO_LARGE;
+
+ GET_CPIO_NUMERIC_FIELD_RET(*pcbFilePath, pHdr->achNameSize);
+
+ /* There is never any padding. */
+ *pcbPad = 0;
+
+#undef GET_CPIO_NUMERIC_FIELD_RET
+#undef GET_CPIO_NUMERIC_FIELD_RET_U64
+
+ return rc;
+}
+
+
+/**
+ * Parses the given "new" ASCII header and converts it to an FS object info structure.
+ *
+ * @returns IPRT status code.
+ * @param pThis The CPIO reader state.
+ * @param pHdr The header to convert.
+ * @param fWithChksum Flag whether the header uses the checksum field.
+ * @param pcbFilePath Where to store the file path size on success.
+ * @param pcbPad Where to store the number of bytes padded after the header and file path
+ * before the content begins.
+ */
+static int rtZipCpioReaderParseHeaderAsciiNew(PRTZIPCPIOREADER pThis, PCCPIOHDRNEW pHdr, bool fWithChksum,
+ uint32_t *pcbFilePath, uint32_t *pcbPad)
+{
+ RT_NOREF(fWithChksum); /** @todo */
+ PRTFSOBJINFO pObjInfo = &pThis->ObjInfo;
+ int rc;
+ int64_t i64Tmp;
+ int64_t c64SecModTime;
+ uint32_t uMajor, uMinor;
+
+ pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
+ pObjInfo->Attr.u.Unix.Device = 0;
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+
+#define GET_CPIO_NUMERIC_FIELD_RET(a_Var, a_Field) \
+ do { \
+ rc = rtZipCpioHdrHexFieldToNum(a_Field, sizeof(a_Field), &i64Tmp); \
+ if (RT_FAILURE(rc)) \
+ return rc; \
+ (a_Var) = i64Tmp; \
+ if ((a_Var) != i64Tmp) \
+ return VERR_TAR_NUM_VALUE_TOO_LARGE; \
+ } while (0)
+
+#define GET_CPIO_NUMERIC_FIELD_RET_U64(a_Var, a_Field) \
+ do { \
+ rc = rtZipCpioHdrHexFieldToNum(a_Field, sizeof(a_Field), &i64Tmp); \
+ if (RT_FAILURE(rc)) \
+ return rc; \
+ (a_Var) = (uint64_t)i64Tmp; \
+ if ((a_Var) != (uint64_t)i64Tmp) \
+ return VERR_TAR_NUM_VALUE_TOO_LARGE; \
+ } while (0)
+
+ GET_CPIO_NUMERIC_FIELD_RET( pObjInfo->Attr.fMode, pHdr->achMode);
+ GET_CPIO_NUMERIC_FIELD_RET( pObjInfo->Attr.u.Unix.uid, pHdr->achUid);
+ GET_CPIO_NUMERIC_FIELD_RET( pObjInfo->Attr.u.Unix.gid, pHdr->achGid);
+ GET_CPIO_NUMERIC_FIELD_RET( pObjInfo->Attr.u.Unix.cHardlinks, pHdr->achNLinks);
+ GET_CPIO_NUMERIC_FIELD_RET_U64(pObjInfo->Attr.u.Unix.INodeId, pHdr->achInode);
+ GET_CPIO_NUMERIC_FIELD_RET( uMajor, pHdr->achDevMajor);
+ GET_CPIO_NUMERIC_FIELD_RET( uMinor, pHdr->achDevMinor);
+ GET_CPIO_NUMERIC_FIELD_RET( pObjInfo->cbObject, pHdr->achFileSize);
+ pObjInfo->cbAllocated = RT_ALIGN_64(pObjInfo->cbObject, 4);
+ GET_CPIO_NUMERIC_FIELD_RET( c64SecModTime, pHdr->achMTime);
+ RTTimeSpecSetSeconds(&pObjInfo->ChangeTime, c64SecModTime);
+ RTTimeSpecSetSeconds(&pObjInfo->ModificationTime, c64SecModTime);
+ RTTimeSpecSetSeconds(&pObjInfo->AccessTime, c64SecModTime);
+ RTTimeSpecSetSeconds(&pObjInfo->BirthTime, c64SecModTime);
+ if (c64SecModTime != RTTimeSpecGetSeconds(&pObjInfo->ModificationTime))
+ return VERR_TAR_NUM_VALUE_TOO_LARGE;
+ pObjInfo->Attr.u.Unix.Device = RTDEV_MAKE(uMajor, uMinor);
+ if ( uMajor != RTDEV_MAJOR(pObjInfo->Attr.u.Unix.Device)
+ || uMinor != RTDEV_MINOR(pObjInfo->Attr.u.Unix.Device))
+ return VERR_TAR_DEV_VALUE_TOO_LARGE;
+
+ GET_CPIO_NUMERIC_FIELD_RET(*pcbFilePath, pHdr->achNameSize);
+
+ /* Header and file path are padded with 0 bytes to a 4 byte boundary. */
+ uint32_t cbComp = *pcbFilePath + sizeof(*pHdr);
+ *pcbPad = RT_ALIGN_32(cbComp, 4) - cbComp;
+
+#undef GET_CPIO_NUMERIC_FIELD_RET
+#undef GET_CPIO_NUMERIC_FIELD_RET_U64
+
+ return rc;
+}
+
+
+/**
+ * Parses and validates a CPIO header.
+ *
+ * @returns IPRT status code.
+ * @param pThis The CPIO reader state.
+ * @param enmType The CPIO header type.
+ * @param pHdr The CPIO header that has been read.
+ * @param pcbFilePath Where to store the size of the file path on success.
+ * @param pcbPad Where to store the number of bytes padded after the header and file path
+ * before the content begins.
+ */
+static int rtZipCpioReaderParseHeader(PRTZIPCPIOREADER pThis, RTZIPCPIOTYPE enmType, PCCPIOHDR pHdr,
+ uint32_t *pcbFilePath, uint32_t *pcbPad)
+{
+ int rc;
+
+ switch (enmType)
+ {
+ case RTZIPCPIOTYPE_ANCIENT_BIN:
+ rc = rtZipCpioReaderParseHeaderAncientBin(pThis, &pHdr->AncientBin,
+ pcbFilePath, pcbPad);
+ break;
+ case RTZIPCPIOTYPE_ASCII_SUSV2:
+ rc = rtZipCpioReaderParseHeaderAsciiSusV2(pThis, &pHdr->AsciiSuSv2,
+ pcbFilePath, pcbPad);
+ break;
+ case RTZIPCPIOTYPE_ASCII_NEW:
+ rc = rtZipCpioReaderParseHeaderAsciiNew(pThis, &pHdr->AsciiNew, false /*fWithChksum*/,
+ pcbFilePath, pcbPad);
+ break;
+ case RTZIPCPIOTYPE_ASCII_NEW_CHKSUM:
+ rc = rtZipCpioReaderParseHeaderAsciiNew(pThis, &pHdr->AsciiNew, true /*fWithChksum*/,
+ pcbFilePath, pcbPad);
+ break;
+ default:
+ AssertMsgFailedBreakStmt(("Invalid CPIO type %d\n", enmType), rc = VERR_INTERNAL_ERROR);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Reads the file path from the CPIO archive stream.
+ *
+ * @returns IPRT status code.
+ * @param hVfsIos The I/O stream to read from.
+ * @param pThis The CPIO reader state.
+ * @param cbFilePath Size of the file path in bytes.
+ */
+static int rtZipCpioReaderReadPath(RTVFSIOSTREAM hVfsIos, PRTZIPCPIOREADER pThis, size_t cbFilePath)
+{
+ if (cbFilePath >= sizeof(pThis->szName))
+ return VERR_TAR_NAME_TOO_LONG;
+
+ size_t cbRead;
+ int rc = RTVfsIoStrmRead(hVfsIos, &pThis->szName[0], cbFilePath, true /*fBlocking*/, &cbRead);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (cbRead != cbFilePath)
+ return VERR_TAR_UNEXPECTED_EOS;
+
+ /* The read file name should be zero terminated at the end. */
+ if (pThis->szName[cbFilePath - 1] != '\0')
+ return VERR_TAR_MALFORMED_GNU_LONGXXXX;
+
+ return VINF_SUCCESS;
+}
+
+
+/*
+ *
+ * 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.
+ * 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.
+ * 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.
+ *
+ */
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtZipCpioFssBaseObj_Close(void *pvThis)
+{
+ PRTZIPCPIOBASEOBJ pThis = (PRTZIPCPIOBASEOBJ)pvThis;
+
+ /* Currently there is nothing we really have to do here. */
+ pThis->offHdr = -1;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtZipCpioFssBaseObj_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTZIPCPIOBASEOBJ pThis = (PRTZIPCPIOBASEOBJ)pvThis;
+
+ /*
+ * Copy the desired data.
+ */
+ switch (enmAddAttr)
+ {
+ case RTFSOBJATTRADD_NOTHING:
+ case RTFSOBJATTRADD_UNIX:
+ *pObjInfo = pThis->ObjInfo;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ *pObjInfo = pThis->ObjInfo;
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
+ pObjInfo->Attr.u.UnixOwner.uid = pThis->ObjInfo.Attr.u.Unix.uid;
+ pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ *pObjInfo = pThis->ObjInfo;
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
+ pObjInfo->Attr.u.UnixGroup.gid = pThis->ObjInfo.Attr.u.Unix.gid;
+ pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
+ break;
+
+ case RTFSOBJATTRADD_EASIZE:
+ *pObjInfo = pThis->ObjInfo;
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
+ RT_ZERO(pObjInfo->Attr.u);
+ break;
+
+ default:
+ return VERR_NOT_SUPPORTED;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Tar filesystem base object operations.
+ */
+static const RTVFSOBJOPS g_rtZipCpioFssBaseObjOps =
+{
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_BASE,
+ "CpioFsStream::Obj",
+ rtZipCpioFssBaseObj_Close,
+ rtZipCpioFssBaseObj_QueryInfo,
+ RTVFSOBJOPS_VERSION
+};
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtZipCpioFssIos_Close(void *pvThis)
+{
+ PRTZIPCPIOIOSTREAM pThis = (PRTZIPCPIOIOSTREAM)pvThis;
+
+ RTVfsIoStrmRelease(pThis->hVfsIos);
+ pThis->hVfsIos = NIL_RTVFSIOSTREAM;
+
+ return rtZipCpioFssBaseObj_Close(&pThis->BaseObj);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtZipCpioFssIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTZIPCPIOIOSTREAM pThis = (PRTZIPCPIOIOSTREAM)pvThis;
+ return rtZipCpioFssBaseObj_QueryInfo(&pThis->BaseObj, pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
+ */
+static DECLCALLBACK(int) rtZipCpioFssIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
+{
+ PRTZIPCPIOIOSTREAM pThis = (PRTZIPCPIOIOSTREAM)pvThis;
+ Assert(pSgBuf->cSegs == 1);
+
+ /*
+ * Make offset into a real offset so it's possible to do random access
+ * on CPIO files that are seekable. Fend of reads beyond the end of the
+ * stream.
+ */
+ if (off < 0)
+ off = pThis->offFile;
+ if (off >= pThis->cbFile)
+ return pcbRead ? VINF_EOF : VERR_EOF;
+
+
+ Assert(pThis->cbFile >= pThis->offFile);
+ uint64_t cbLeft = (uint64_t)(pThis->cbFile - off);
+ size_t cbToRead = pSgBuf->paSegs[0].cbSeg;
+ if (cbToRead > cbLeft)
+ {
+ if (!pcbRead)
+ return VERR_EOF;
+ cbToRead = (size_t)cbLeft;
+ }
+
+ /*
+ * Do the reading.
+ */
+ size_t cbReadStack = 0;
+ if (!pcbRead)
+ pcbRead = &cbReadStack;
+ int rc = RTVfsIoStrmReadAt(pThis->hVfsIos, pThis->offStart + off, pSgBuf->paSegs[0].pvSeg, cbToRead, fBlocking, pcbRead);
+ pThis->offFile = off + *pcbRead;
+ if (pThis->offFile >= pThis->cbFile)
+ {
+ Assert(pThis->offFile == pThis->cbFile);
+ pThis->fEndOfStream = true;
+ RTVfsIoStrmSkip(pThis->hVfsIos, pThis->cbPadding);
+ }
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
+ */
+static DECLCALLBACK(int) rtZipCpioFssIos_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
+{
+ /* Cannot write to a read-only I/O stream. */
+ NOREF(pvThis); NOREF(off); NOREF(pSgBuf); NOREF(fBlocking); NOREF(pcbWritten);
+ return VERR_ACCESS_DENIED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
+ */
+static DECLCALLBACK(int) rtZipCpioFssIos_Flush(void *pvThis)
+{
+ /* It's a read only stream, nothing dirty to flush. */
+ NOREF(pvThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
+ */
+static DECLCALLBACK(int) rtZipCpioFssIos_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
+ uint32_t *pfRetEvents)
+{
+ PRTZIPCPIOIOSTREAM pThis = (PRTZIPCPIOIOSTREAM)pvThis;
+
+ /* When we've reached the end, read will be set to indicate it. */
+ if ( (fEvents & RTPOLL_EVT_READ)
+ && pThis->fEndOfStream)
+ {
+ int rc = RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, 0, fIntr, pfRetEvents);
+ if (RT_SUCCESS(rc))
+ *pfRetEvents |= RTPOLL_EVT_READ;
+ else
+ *pfRetEvents = RTPOLL_EVT_READ;
+ return VINF_SUCCESS;
+ }
+
+ return RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
+ */
+static DECLCALLBACK(int) rtZipCpioFssIos_Tell(void *pvThis, PRTFOFF poffActual)
+{
+ PRTZIPCPIOIOSTREAM pThis = (PRTZIPCPIOIOSTREAM)pvThis;
+ *poffActual = pThis->offFile;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Tar I/O stream operations.
+ */
+static const RTVFSIOSTREAMOPS g_rtZipCpioFssIosOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_IO_STREAM,
+ "CpioFsStream::IoStream",
+ rtZipCpioFssIos_Close,
+ rtZipCpioFssIos_QueryInfo,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSIOSTREAMOPS_VERSION,
+ RTVFSIOSTREAMOPS_FEAT_NO_SG,
+ rtZipCpioFssIos_Read,
+ rtZipCpioFssIos_Write,
+ rtZipCpioFssIos_Flush,
+ rtZipCpioFssIos_PollOne,
+ rtZipCpioFssIos_Tell,
+ NULL /*Skip*/,
+ NULL /*ZeroFill*/,
+ RTVFSIOSTREAMOPS_VERSION
+};
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtZipCpioFssSym_Close(void *pvThis)
+{
+ PRTZIPCPIOBASEOBJ pThis = (PRTZIPCPIOBASEOBJ)pvThis;
+ return rtZipCpioFssBaseObj_Close(pThis);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtZipCpioFssSym_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTZIPCPIOBASEOBJ pThis = (PRTZIPCPIOBASEOBJ)pvThis;
+ return rtZipCpioFssBaseObj_QueryInfo(pThis, pObjInfo, enmAddAttr);
+}
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
+ */
+static DECLCALLBACK(int) rtZipCpioFssSym_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
+{
+ NOREF(pvThis); NOREF(fMode); NOREF(fMask);
+ return VERR_ACCESS_DENIED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
+ */
+static DECLCALLBACK(int) rtZipCpioFssSym_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ NOREF(pvThis); NOREF(pAccessTime); NOREF(pModificationTime); NOREF(pChangeTime); NOREF(pBirthTime);
+ return VERR_ACCESS_DENIED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
+ */
+static DECLCALLBACK(int) rtZipCpioFssSym_SetOwner(void *pvThis, RTUID uid, RTGID gid)
+{
+ NOREF(pvThis); NOREF(uid); NOREF(gid);
+ return VERR_ACCESS_DENIED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSSYMLINKOPS,pfnRead}
+ */
+static DECLCALLBACK(int) rtZipCpioFssSym_Read(void *pvThis, char *pszTarget, size_t cbTarget)
+{
+ PRTZIPCPIOBASEOBJ pThis = (PRTZIPCPIOBASEOBJ)pvThis;
+ return RTStrCopy(pszTarget, cbTarget, pThis->pCpioReader->szTarget);
+}
+
+
+/**
+ * CPIO symbolic (and hardlink) operations.
+ */
+static const RTVFSSYMLINKOPS g_rtZipCpioFssSymOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_SYMLINK,
+ "CpioFsStream::Symlink",
+ rtZipCpioFssSym_Close,
+ rtZipCpioFssSym_QueryInfo,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSSYMLINKOPS_VERSION,
+ 0,
+ { /* ObjSet */
+ RTVFSOBJSETOPS_VERSION,
+ RT_UOFFSETOF(RTVFSSYMLINKOPS, ObjSet) - RT_UOFFSETOF(RTVFSSYMLINKOPS, Obj),
+ rtZipCpioFssSym_SetMode,
+ rtZipCpioFssSym_SetTimes,
+ rtZipCpioFssSym_SetOwner,
+ RTVFSOBJSETOPS_VERSION
+ },
+ rtZipCpioFssSym_Read,
+ RTVFSSYMLINKOPS_VERSION
+};
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtZipCpioFss_Close(void *pvThis)
+{
+ PRTZIPCPIOFSSTREAM pThis = (PRTZIPCPIOFSSTREAM)pvThis;
+
+ RTVfsObjRelease(pThis->hVfsCurObj);
+ pThis->hVfsCurObj = NIL_RTVFSOBJ;
+ pThis->pCurIosData = NULL;
+
+ RTVfsIoStrmRelease(pThis->hVfsIos);
+ pThis->hVfsIos = NIL_RTVFSIOSTREAM;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtZipCpioFss_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTZIPCPIOFSSTREAM pThis = (PRTZIPCPIOFSSTREAM)pvThis;
+ /* Take the lazy approach here, with the sideffect of providing some info
+ that is actually kind of useful. */
+ return RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFSSTREAMOPS,pfnNext}
+ */
+DECL_HIDDEN_CALLBACK(int) rtZipCpioFss_Next(void *pvThis, char **ppszName, RTVFSOBJTYPE *penmType, PRTVFSOBJ phVfsObj)
+{
+ PRTZIPCPIOFSSTREAM pThis = (PRTZIPCPIOFSSTREAM)pvThis;
+
+ /*
+ * Dispense with the current object.
+ */
+ if (pThis->hVfsCurObj != NIL_RTVFSOBJ)
+ {
+ if (pThis->pCurIosData)
+ {
+ pThis->pCurIosData->fEndOfStream = true;
+ pThis->pCurIosData->offFile = pThis->pCurIosData->cbFile;
+ pThis->pCurIosData = NULL;
+ }
+
+ RTVfsObjRelease(pThis->hVfsCurObj);
+ pThis->hVfsCurObj = NIL_RTVFSOBJ;
+ }
+
+ /*
+ * Check if we've already reached the end in some way.
+ */
+ if (pThis->fEndOfStream)
+ return VERR_EOF;
+ if (pThis->rcFatal != VINF_SUCCESS)
+ return pThis->rcFatal;
+
+ /*
+ * Make sure the input stream is in the right place.
+ */
+ RTFOFF offHdr = RTVfsIoStrmTell(pThis->hVfsIos);
+ while ( offHdr >= 0
+ && offHdr < pThis->offNextHdr)
+ {
+ int rc = RTVfsIoStrmSkip(pThis->hVfsIos, pThis->offNextHdr - offHdr);
+ if (RT_FAILURE(rc))
+ {
+ /** @todo Ignore if we're at the end of the stream? */
+ return pThis->rcFatal = rc;
+ }
+
+ offHdr = RTVfsIoStrmTell(pThis->hVfsIos);
+ }
+
+ if (offHdr < 0)
+ return pThis->rcFatal = (int)offHdr;
+ if (offHdr > pThis->offNextHdr)
+ return pThis->rcFatal = VERR_INTERNAL_ERROR_3;
+ Assert(pThis->offNextHdr == offHdr);
+ pThis->offCurHdr = offHdr;
+
+ /*
+ * Consume CPIO headers.
+ */
+ size_t cbHdr = 0;
+ /*
+ * Read the next header.
+ *
+ * Read the first 6 bytes to determine the header type and continue reading the
+ * rest of the header.
+ */
+ CPIOHDR Hdr;
+ RTZIPCPIOTYPE enmHdrType = RTZIPCPIOTYPE_INVALID;
+ size_t cbRead;
+ int rc = RTVfsIoStrmRead(pThis->hVfsIos, &Hdr.ab[0], sizeof(Hdr.AsciiNew.achMagic), true /*fBlocking*/, &cbRead);
+ if (RT_FAILURE(rc))
+ return pThis->rcFatal = rc;
+ if (rc == VINF_EOF && cbRead == 0)
+ {
+ pThis->fEndOfStream = true;
+ return VERR_EOF;
+ }
+ if (cbRead != sizeof(Hdr.AsciiNew.achMagic))
+ return pThis->rcFatal = VERR_TAR_UNEXPECTED_EOS;
+
+ if (Hdr.AncientBin.u16Magic == CPIO_HDR_BIN_MAGIC)
+ {
+ cbHdr = sizeof(Hdr.AncientBin);
+ enmHdrType = RTZIPCPIOTYPE_ANCIENT_BIN;
+ }
+ else if (!strncmp(&Hdr.AsciiSuSv2.achMagic[0], CPIO_HDR_SUSV2_MAGIC, sizeof(Hdr.AsciiSuSv2.achMagic)))
+ {
+ cbHdr = sizeof(Hdr.AsciiSuSv2);
+ enmHdrType = RTZIPCPIOTYPE_ASCII_SUSV2;
+ }
+ else if (!strncmp(&Hdr.AsciiNew.achMagic[0], CPIO_HDR_NEW_MAGIC, sizeof(Hdr.AsciiNew.achMagic)))
+ {
+ cbHdr = sizeof(Hdr.AsciiNew);
+ enmHdrType = RTZIPCPIOTYPE_ASCII_NEW;
+ }
+ else if (!strncmp(&Hdr.AsciiNew.achMagic[0], CPIO_HDR_NEW_CHKSUM_MAGIC, sizeof(Hdr.AsciiNew.achMagic)))
+ {
+ cbHdr = sizeof(Hdr.AsciiNew);
+ enmHdrType = RTZIPCPIOTYPE_ASCII_NEW_CHKSUM;
+ }
+ else
+ return pThis->rcFatal = VERR_TAR_UNKNOWN_TYPE_FLAG; /** @todo Dedicated status code. */
+
+ /* Read the rest of the header. */
+ size_t cbHdrLeft = cbHdr - sizeof(Hdr.AsciiNew.achMagic);
+ rc = RTVfsIoStrmRead(pThis->hVfsIos, &Hdr.ab[sizeof(Hdr.AsciiNew.achMagic)], cbHdr - sizeof(Hdr.AsciiNew.achMagic), true /*fBlocking*/, &cbRead);
+ if (RT_FAILURE(rc))
+ return pThis->rcFatal = rc;
+ if (cbRead != cbHdrLeft)
+ return pThis->rcFatal = VERR_TAR_UNEXPECTED_EOS;
+
+ /*
+ * Parse it.
+ */
+ uint32_t cbFilePath = 0;
+ uint32_t cbPad = 0;
+ rc = rtZipCpioReaderParseHeader(&pThis->CpioReader, enmHdrType, &Hdr, &cbFilePath, &cbPad);
+ if (RT_FAILURE(rc))
+ return pThis->rcFatal = rc;
+
+ /* Read the file path following the header. */
+ rc = rtZipCpioReaderReadPath(pThis->hVfsIos, &pThis->CpioReader, cbFilePath);
+ if (RT_FAILURE(rc))
+ return pThis->rcFatal = rc;
+
+ if (cbPad)
+ RTVfsIoStrmSkip(pThis->hVfsIos, cbPad);
+ pThis->offNextHdr = offHdr + cbHdr + cbFilePath + cbPad;
+
+ /*
+ * CPIO uses a special trailer file record with a 0 mode and size and using a special
+ * marker filename. The filesystem stream is marked EOS When such a record is encountered
+ * to not try to read anything which might come behind it, imagine an initramfs image consisting
+ * of multiple archives which don't need to be necessarily be all of the CPIO kind (yes, this is
+ * a reality with ubuntu for example containing microcode updates as seperate CPIO archives
+ * coming before the main LZ4 compressed CPIO archive...).
+ */
+ PCRTFSOBJINFO pInfo = &pThis->CpioReader.ObjInfo;
+ if (RT_UNLIKELY( !pInfo->Attr.fMode
+ && !pInfo->cbAllocated
+ && !strcmp(&pThis->CpioReader.szName[0], CPIO_EOS_FILE_NAME)))
+ {
+ pThis->fEndOfStream = true;
+ return VERR_EOF;
+ }
+
+ /*
+ * Create an object of the appropriate type.
+ */
+ RTVFSOBJTYPE enmType;
+ RTVFSOBJ hVfsObj;
+ RTFMODE fType = pInfo->Attr.fMode & RTFS_TYPE_MASK;
+ switch (fType)
+ {
+ /*
+ * Files are represented by a VFS I/O stream, hardlinks have their content
+ * embedded as it is another file.
+ */
+ case RTFS_TYPE_FILE:
+ {
+ RTVFSIOSTREAM hVfsIos;
+ PRTZIPCPIOIOSTREAM pIosData;
+ rc = RTVfsNewIoStream(&g_rtZipCpioFssIosOps,
+ sizeof(*pIosData),
+ RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
+ NIL_RTVFS,
+ NIL_RTVFSLOCK,
+ &hVfsIos,
+ (void **)&pIosData);
+ if (RT_FAILURE(rc))
+ return pThis->rcFatal = rc;
+
+ pIosData->BaseObj.offHdr = offHdr;
+ pIosData->BaseObj.offNextHdr = pThis->offNextHdr;
+ pIosData->BaseObj.pCpioReader = &pThis->CpioReader;
+ pIosData->BaseObj.ObjInfo = *pInfo;
+ pIosData->cbFile = pInfo->cbObject;
+ pIosData->offFile = 0;
+ pIosData->offStart = RTVfsIoStrmTell(pThis->hVfsIos);
+ pIosData->cbPadding = (uint32_t)(pInfo->cbAllocated - pInfo->cbObject);
+ pIosData->fEndOfStream = false;
+ pIosData->hVfsIos = pThis->hVfsIos;
+ RTVfsIoStrmRetain(pThis->hVfsIos);
+
+ pThis->pCurIosData = pIosData;
+ pThis->offNextHdr += pIosData->cbFile + pIosData->cbPadding;
+
+ enmType = RTVFSOBJTYPE_IO_STREAM;
+ hVfsObj = RTVfsObjFromIoStream(hVfsIos);
+ RTVfsIoStrmRelease(hVfsIos);
+ break;
+ }
+
+ case RTFS_TYPE_SYMLINK:
+ {
+ RTVFSSYMLINK hVfsSym;
+ PRTZIPCPIOBASEOBJ pBaseObjData;
+ rc = RTVfsNewSymlink(&g_rtZipCpioFssSymOps,
+ sizeof(*pBaseObjData),
+ NIL_RTVFS,
+ NIL_RTVFSLOCK,
+ &hVfsSym,
+ (void **)&pBaseObjData);
+ if (RT_FAILURE(rc))
+ return pThis->rcFatal = rc;
+
+ pBaseObjData->offHdr = offHdr;
+ pBaseObjData->offNextHdr = pThis->offNextHdr;
+ pBaseObjData->pCpioReader = &pThis->CpioReader;
+ pBaseObjData->ObjInfo = *pInfo;
+
+ /* Read the body of the symlink (as normal file data). */
+ if (pInfo->cbObject + 1 > (RTFOFF)sizeof(pThis->CpioReader.szTarget))
+ return VERR_TAR_NAME_TOO_LONG;
+
+ cbPad = (uint32_t)(pInfo->cbAllocated - pInfo->cbObject);
+ rc = RTVfsIoStrmRead(pThis->hVfsIos, &pThis->CpioReader.szTarget[0], pInfo->cbObject, true /*fBlocking*/, &cbRead);
+ if (RT_FAILURE(rc))
+ return pThis->rcFatal = rc;
+ if (cbRead != (uint32_t)pInfo->cbObject)
+ return pThis->rcFatal = VERR_TAR_UNEXPECTED_EOS;
+
+ pThis->CpioReader.szTarget[pInfo->cbObject] = '\0';
+
+ if (cbPad)
+ rc = RTVfsIoStrmSkip(pThis->hVfsIos, cbPad);
+ if (RT_FAILURE(rc))
+ return pThis->rcFatal = rc;
+
+ pThis->offNextHdr += pInfo->cbAllocated;
+
+ enmType = RTVFSOBJTYPE_SYMLINK;
+ hVfsObj = RTVfsObjFromSymlink(hVfsSym);
+ RTVfsSymlinkRelease(hVfsSym);
+ break;
+ }
+
+ /*
+ * All other objects are repesented using a VFS base object since they
+ * carry no data streams.
+ */
+ case RTFS_TYPE_DEV_BLOCK:
+ case RTFS_TYPE_DEV_CHAR:
+ case RTFS_TYPE_DIRECTORY:
+ case RTFS_TYPE_FIFO:
+ {
+ PRTZIPCPIOBASEOBJ pBaseObjData;
+ rc = RTVfsNewBaseObj(&g_rtZipCpioFssBaseObjOps,
+ sizeof(*pBaseObjData),
+ NIL_RTVFS,
+ NIL_RTVFSLOCK,
+ &hVfsObj,
+ (void **)&pBaseObjData);
+ if (RT_FAILURE(rc))
+ return pThis->rcFatal = rc;
+
+ pBaseObjData->offHdr = offHdr;
+ pBaseObjData->offNextHdr = pThis->offNextHdr;
+ pBaseObjData->pCpioReader = &pThis->CpioReader;
+ pBaseObjData->ObjInfo = *pInfo;
+
+ enmType = RTVFSOBJTYPE_BASE;
+ break;
+ }
+
+ default:
+ AssertFailed();
+ return pThis->rcFatal = VERR_INTERNAL_ERROR_5;
+ }
+ pThis->hVfsCurObj = hVfsObj;
+
+ /*
+ * Set the return data and we're done.
+ */
+ if (ppszName)
+ {
+ rc = RTStrDupEx(ppszName, pThis->CpioReader.szName);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ if (phVfsObj)
+ {
+ RTVfsObjRetain(hVfsObj);
+ *phVfsObj = hVfsObj;
+ }
+
+ if (penmType)
+ *penmType = enmType;
+
+ return VINF_SUCCESS;
+}
+
+
+
+/**
+ * CPIO filesystem stream operations.
+ */
+static const RTVFSFSSTREAMOPS rtZipCpioFssOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_FS_STREAM,
+ "CpioFsStream",
+ rtZipCpioFss_Close,
+ rtZipCpioFss_QueryInfo,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSFSSTREAMOPS_VERSION,
+ 0,
+ rtZipCpioFss_Next,
+ NULL,
+ NULL,
+ NULL,
+ RTVFSFSSTREAMOPS_VERSION
+};
+
+
+/**
+ * Internal function use both by RTZipCpioFsStreamFromIoStream() and by
+ * RTZipCpioFsStreamForFile() in updating mode.
+ */
+DECLHIDDEN(void) rtZipCpioReaderInit(PRTZIPCPIOFSSTREAM pThis, RTVFSIOSTREAM hVfsIos, uint64_t offStart)
+{
+ pThis->hVfsIos = hVfsIos;
+ pThis->hVfsCurObj = NIL_RTVFSOBJ;
+ pThis->pCurIosData = NULL;
+ pThis->offStart = offStart;
+ pThis->offNextHdr = offStart;
+ pThis->fEndOfStream = false;
+ pThis->rcFatal = VINF_SUCCESS;
+
+ /* Don't check if it's a CPIO stream here, do that in the
+ rtZipCpioFss_Next. */
+}
+
+
+RTDECL(int) RTZipCpioFsStreamFromIoStream(RTVFSIOSTREAM hVfsIosIn, uint32_t fFlags, PRTVFSFSSTREAM phVfsFss)
+{
+ /*
+ * Input validation.
+ */
+ AssertPtrReturn(phVfsFss, VERR_INVALID_HANDLE);
+ *phVfsFss = NIL_RTVFSFSSTREAM;
+ AssertPtrReturn(hVfsIosIn, VERR_INVALID_HANDLE);
+ AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
+
+ RTFOFF const offStart = RTVfsIoStrmTell(hVfsIosIn);
+ AssertReturn(offStart >= 0, (int)offStart);
+
+ uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosIn);
+ AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ /*
+ * Retain the input stream and create a new filesystem stream handle.
+ */
+ PRTZIPCPIOFSSTREAM pThis;
+ RTVFSFSSTREAM hVfsFss;
+ int rc = RTVfsNewFsStream(&rtZipCpioFssOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, RTFILE_O_READ,
+ &hVfsFss, (void **)&pThis);
+ if (RT_SUCCESS(rc))
+ {
+ rtZipCpioReaderInit(pThis, hVfsIosIn, fFlags);
+ *phVfsFss = hVfsFss;
+ return VINF_SUCCESS;
+ }
+
+ RTVfsIoStrmRelease(hVfsIosIn);
+ return rc;
+}
+
+
+/**
+ * Used by RTZipCpioFsStreamTruncate to resolve @a hVfsObj.
+ */
+DECLHIDDEN(PRTZIPCPIOBASEOBJ) rtZipCpioFsStreamBaseObjToPrivate(PRTZIPCPIOFSSTREAM pThis, RTVFSOBJ hVfsObj)
+{
+ PRTZIPCPIOBASEOBJ pThisObj;
+ RTVFSOBJTYPE enmType = RTVfsObjGetType(hVfsObj);
+ switch (enmType)
+ {
+ case RTVFSOBJTYPE_IO_STREAM:
+ {
+ RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
+ AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, NULL);
+ PRTZIPCPIOIOSTREAM pThisStrm = (PRTZIPCPIOIOSTREAM)RTVfsIoStreamToPrivate(hVfsIos, &g_rtZipCpioFssIosOps);
+ RTVfsIoStrmRelease(hVfsIos);
+ pThisObj = &pThisStrm->BaseObj;
+ break;
+ }
+
+ case RTVFSOBJTYPE_SYMLINK:
+ {
+ RTVFSSYMLINK hVfsSymlink = RTVfsObjToSymlink(hVfsObj);
+ AssertReturn(hVfsSymlink != NIL_RTVFSSYMLINK, NULL);
+ pThisObj = (PRTZIPCPIOBASEOBJ)RTVfsSymlinkToPrivate(hVfsSymlink, &g_rtZipCpioFssSymOps);
+ RTVfsSymlinkRelease(hVfsSymlink);
+ break;
+ }
+
+ case RTVFSOBJTYPE_BASE:
+ pThisObj = (PRTZIPCPIOBASEOBJ)RTVfsObjToPrivate(hVfsObj, &g_rtZipCpioFssBaseObjOps);
+ break;
+
+ default:
+ /** @todo implement. */
+ AssertFailedReturn(NULL);
+ }
+
+ AssertReturn(pThisObj->pCpioReader == &pThis->CpioReader, NULL);
+ return pThisObj;
+}
+
diff --git a/src/VBox/Runtime/common/zip/cpiovfsreader.h b/src/VBox/Runtime/common/zip/cpiovfsreader.h
new file mode 100644
index 00000000000..2f86addbb99
--- /dev/null
+++ b/src/VBox/Runtime/common/zip/cpiovfsreader.h
@@ -0,0 +1,159 @@
+/* $Id$ */
+/** @file
+ * IPRT - CPIO Virtual Filesystem.
+ */
+
+/*
+ * Copyright (C) 2020 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+#ifndef IPRT_INCLUDED_SRC_common_zip_cpiovfsreader_h
+#define IPRT_INCLUDED_SRC_common_zip_cpiovfsreader_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/formats/cpio.h>
+
+
+/**
+ * CPIO archive type.
+ */
+typedef enum RTZIPCPIOTYPE
+{
+ /** Invalid type value. */
+ RTZIPCPIOTYPE_INVALID = 0,
+ /** Ancient binary archive. */
+ RTZIPCPIOTYPE_ANCIENT_BIN,
+ /** Portable ASCII format as defined by SuSV2. */
+ RTZIPCPIOTYPE_ASCII_SUSV2,
+ /** "New" ASCII format. */
+ RTZIPCPIOTYPE_ASCII_NEW,
+ /** "New" ASCII format with checksumming. */
+ RTZIPCPIOTYPE_ASCII_NEW_CHKSUM,
+ /** End of the valid type values (this is not valid). */
+ RTZIPCPIOTYPE_END,
+ /** The usual type blow up. */
+ RTZIPCPIOTYPE_32BIT_HACK = 0x7fffffff
+} RTZIPCPIOTYPE;
+typedef RTZIPCPIOTYPE *PRTZIPCPIOTYPE;
+
+
+/**
+ * CPIO reader instance data.
+ */
+typedef struct RTZIPCPIOREADER
+{
+ /** The object info with unix attributes. */
+ RTFSOBJINFO ObjInfo;
+ /** The path length. */
+ uint32_t cbPath;
+ /** The name of the current object. */
+ char szName[RTPATH_MAX];
+ /** The current link target if symlink. */
+ char szTarget[RTPATH_MAX];
+} RTZIPCPIOREADER;
+/** Pointer to the CPIO reader instance data. */
+typedef RTZIPCPIOREADER *PRTZIPCPIOREADER;
+
+/**
+ * CPIO directory, character device, block device, fifo socket or symbolic link.
+ */
+typedef struct RTZIPCPIOBASEOBJ
+{
+ /** The stream offset of the (first) header in the input stream/file. */
+ RTFOFF offHdr;
+ /** The stream offset of the first header of the next object (for truncating the
+ * tar file after this object (updating)). */
+ RTFOFF offNextHdr;
+ /** Pointer to the reader instance data (resides in the filesystem
+ * stream).
+ * @todo Fix this so it won't go stale... Back ref from this obj to fss? */
+ PRTZIPCPIOREADER pCpioReader;
+ /** The object info with unix attributes. */
+ RTFSOBJINFO ObjInfo;
+} RTZIPCPIOBASEOBJ;
+/** Pointer to a CPIO filesystem stream base object. */
+typedef RTZIPCPIOBASEOBJ *PRTZIPCPIOBASEOBJ;
+
+
+/**
+ * CPIO file represented as a VFS I/O stream.
+ */
+typedef struct RTZIPCPIOIOSTREAM
+{
+ /** The basic TAR object data. */
+ RTZIPCPIOBASEOBJ BaseObj;
+ /** The number of bytes in the file. */
+ RTFOFF cbFile;
+ /** The current file position. */
+ RTFOFF offFile;
+ /** The start position in the hVfsIos (for seekable hVfsIos). */
+ RTFOFF offStart;
+ /** The number of padding bytes following the file. */
+ uint32_t cbPadding;
+ /** Set if we've reached the end of this file. */
+ bool fEndOfStream;
+ /** The input I/O stream. */
+ RTVFSIOSTREAM hVfsIos;
+} RTZIPCPIOIOSTREAM;
+/** Pointer to a the private data of a CPIO file I/O stream. */
+typedef RTZIPCPIOIOSTREAM *PRTZIPCPIOIOSTREAM;
+
+
+/**
+ * CPIO filesystem stream private data.
+ */
+typedef struct RTZIPCPIOFSSTREAM
+{
+ /** The input I/O stream. */
+ RTVFSIOSTREAM hVfsIos;
+
+ /** The current object (referenced). */
+ RTVFSOBJ hVfsCurObj;
+ /** Pointer to the private data if hVfsCurObj is representing a file. */
+ PRTZIPCPIOIOSTREAM pCurIosData;
+
+ /** The start offset. */
+ RTFOFF offStart;
+ /** The offset of the next header. */
+ RTFOFF offNextHdr;
+ /** The offset of the first header for the current object.
+ * When reaching the end, this will be the same as offNextHdr which will be
+ * pointing to the first zero header */
+ RTFOFF offCurHdr;
+
+ /** Set if we've reached the end of the stream. */
+ bool fEndOfStream;
+ /** Set if we've encountered a fatal error. */
+ int rcFatal;
+
+ /** The CPIO reader instance data. */
+ RTZIPCPIOREADER CpioReader;
+} RTZIPCPIOFSSTREAM;
+/** Pointer to a the private data of a CPIO filesystem stream. */
+typedef RTZIPCPIOFSSTREAM *PRTZIPCPIOFSSTREAM;
+
+DECLHIDDEN(void) rtZipCpioReaderInit(PRTZIPCPIOFSSTREAM pThis, RTVFSIOSTREAM hVfsIos, uint64_t offStart);
+DECL_HIDDEN_CALLBACK(int) rtZipCpioFss_Next(void *pvThis, char **ppszName, RTVFSOBJTYPE *penmType, PRTVFSOBJ phVfsObj);
+DECLHIDDEN(PRTZIPCPIOBASEOBJ) rtZipCpioFsStreamBaseObjToPrivate(PRTZIPCPIOFSSTREAM pThis, RTVFSOBJ hVfsObj);
+
+#endif /* !IPRT_INCLUDED_SRC_common_zip_cpiovfsreader_h */
+
diff --git a/src/VBox/Runtime/common/zip/tarcmd.cpp b/src/VBox/Runtime/common/zip/tarcmd.cpp
index 16e571adeb5..29e412e2a2a 100644
--- a/src/VBox/Runtime/common/zip/tarcmd.cpp
+++ b/src/VBox/Runtime/common/zip/tarcmd.cpp
@@ -74,7 +74,9 @@ typedef enum RTZIPTARCMDFORMAT
/** TAR. */
RTZIPTARCMDFORMAT_TAR,
/** XAR. */
- RTZIPTARCMDFORMAT_XAR
+ RTZIPTARCMDFORMAT_XAR,
+ /** CPIO. */
+ RTZIPTARCMDFORMAT_CPIO,
} RTZIPTARCMDFORMAT;
@@ -817,6 +819,8 @@ static RTEXITCODE rtZipTarCmdOpenInputArchive(PRTZIPTARCMDOPS pOpts, PRTVFSFSSTR
#else
rc = VERR_NOT_SUPPORTED;
#endif
+ else if (pOpts->enmFormat == RTZIPTARCMDFORMAT_CPIO)
+ rc = RTZipCpioFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
else /** @todo make RTZipTarFsStreamFromIoStream fail if not tar file! */
rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0/*fFlags*/, phVfsFss);
RTVfsIoStrmRelease(hVfsIos);
@@ -1605,9 +1609,11 @@ static void rtZipTarUsage(const char *pszProgName)
" ustar (tar POSIX.1-1988), "
" pax (tar POSIX.1-2001),\n"
" xar\n"
- " Note! Because XAR/TAR detection isn't implemented yet, it\n"
+ " cpio\n"
+ " Note! Because XAR/TAR/CPIO detection isn't implemented yet, it\n"
" is necessary to specifcy --format=xar when reading a\n"
- " XAR file. Otherwise this option is only for creation.\n"
+ " XAR file or --format=cpio for a CPIO file.\n"
+ " Otherwise this option is only for creation.\n"
"\n");
RTPrintf("IPRT Options:\n"
" --prefix <dir-prefix> (-A, -c, -d, -r, -u)\n"
@@ -1860,6 +1866,8 @@ RTDECL(RTEXITCODE) RTZipTarCmd(unsigned cArgs, char **papszArgs)
}
else if (!strcmp(ValueUnion.psz, "xar"))
Opts.enmFormat = RTZIPTARCMDFORMAT_XAR;
+ else if (!strcmp(ValueUnion.psz, "cpio"))
+ Opts.enmFormat = RTZIPTARCMDFORMAT_CPIO;
else
return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown archive format: '%s'", ValueUnion.psz);
break;