Index: trunk/src/VBox/Runtime/common/zip/tar.h
===================================================================
--- trunk/src/VBox/Runtime/common/zip/tar.h	(revision 67133)
+++ trunk/src/VBox/Runtime/common/zip/tar.h	(revision 67134)
@@ -89,11 +89,11 @@
 
 /** The uniform standard tape archive format magic value. */
-#define RTZIPTAR_USTAR_MAGIC    "ustar"
+#define RTZIPTAR_USTAR_MAGIC        "ustar"
 /** The ustar version string.
  * @remarks The terminator character is not part of the field.  */
-#define RTZIPTAR_USTAR_VERSION  "00"
+#define RTZIPTAR_USTAR_VERSION      "00"
 
 /** The GNU magic + version value. */
-#define RTZIPTAR_USTAR_GNU_MAGIC    "ustar  "
+#define RTZIPTAR_GNU_MAGIC          "ustar  "
 
 
@@ -140,5 +140,5 @@
 
 /**
- * GNU sparse data segment descriptor entry.
+ * GNU sparse data segment descriptor.
  */
 typedef struct RTZIPTARGNUSPARSE
@@ -150,4 +150,8 @@
 AssertCompileMemberOffset(RTZIPTARGNUSPARSE, offset,    0);
 AssertCompileMemberOffset(RTZIPTARGNUSPARSE, numbytes,  12);
+/** Pointer to a GNU sparse data segment descriptor. */
+typedef RTZIPTARGNUSPARSE *PRTZIPTARGNUSPARSE;
+/** Pointer to a const GNU sparse data segment descriptor. */
+typedef RTZIPTARGNUSPARSE *PCRTZIPTARGNUSPARSE;
 
 /**
@@ -172,6 +176,6 @@
     char    atime[12];
     char    ctime[12];
-    char    offset[12];
-    char    longnames[4];
+    char    offset[12];         /**< for multi-volume? */
+    char    longnames[4];       /**< Seems to be unused. */
     char    unused[1];
     RTZIPTARGNUSPARSE sparse[4];
@@ -284,11 +288,13 @@
     char                ab[512];
     /** The standard header. */
-    RTZIPTARHDRANCIENT  Ancient;
+    RTZIPTARHDRANCIENT      Ancient;
     /** The standard header. */
-    RTZIPTARHDRPOSIX    Posix;
+    RTZIPTARHDRPOSIX        Posix;
     /** The GNU header. */
-    RTZIPTARHDRGNU      Gnu;
+    RTZIPTARHDRGNU          Gnu;
     /** The bits common to both GNU and the standard header. */
-    RTZIPTARHDRCOMMON   Common;
+    RTZIPTARHDRCOMMON       Common;
+    /** GNU sparse header. */
+    RTZIPTARHDRGNUSPARSE    GnuSparse;
 } RTZIPTARHDR;
 AssertCompileSize(RTZIPTARHDR, 512);
Index: trunk/src/VBox/Runtime/common/zip/tarcmd.cpp
===================================================================
--- trunk/src/VBox/Runtime/common/zip/tarcmd.cpp	(revision 67133)
+++ trunk/src/VBox/Runtime/common/zip/tarcmd.cpp	(revision 67134)
@@ -139,4 +139,10 @@
      * Terminated by a NULL entry. */
     const char * const *papszFiles;
+
+    /** The TAR format to create. */
+    RTZIPTARFORMAT  enmTarFormat;
+    /** TAR creation flags. */
+    uint32_t        fTarCreate;
+
 } RTZIPTARCMDOPS;
 /** Pointer to the IPRT tar options. */
@@ -174,4 +180,277 @@
         }
     return false;
+}
+
+
+/**
+ * Archives a file.
+ *
+ * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
+ * @param   pOpts           The options.
+ * @param   hVfsFss         The TAR filesystem stream handle.
+ * @param   pszSrc          The file path or VFS spec.
+ * @param   pszDst          The name to archive the file under.
+ * @param   pErrInfo        Error info buffer (saves stack space).
+ */
+static RTEXITCODE rtZipTarCmdArchiveFile(PRTZIPTARCMDOPS pOpts, RTVFSFSSTREAM hVfsFss,
+                                         const char *pszSrc, const char *pszDst, PRTERRINFOSTATIC pErrInfo)
+{
+    if (pOpts->fVerbose)
+        RTPrintf("%s\n", pszDst);
+
+    /* Open the file. */
+    uint32_t        offError;
+    RTVFSIOSTREAM   hVfsIos;
+    int rc = RTVfsChainOpenIoStream(pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
+                                    &hVfsIos, &offError, RTErrInfoInitStatic(pErrInfo));
+    if (RT_FAILURE(rc))
+        return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenIoStream", pszSrc, rc, offError, &pErrInfo->Core);
+
+    /* I/O stream to base object. */
+    RTVFSOBJ hVfsObj = RTVfsObjFromIoStream(hVfsIos);
+    RTVfsIoStrmRelease(hVfsIos);
+    if (hVfsObj == NIL_RTVFSOBJ)
+        return RTMsgErrorExitFailure("RTVfsObjFromIoStream failed unexpectedly!");
+
+    /* Add it to the stream. */
+    rc = RTVfsFsStrmAdd(hVfsFss, pszDst, hVfsObj, 0 /*fFlags*/);
+    RTVfsObjRelease(hVfsObj);
+
+    if (RT_SUCCESS(rc))
+    {
+        if (rc != VINF_SUCCESS)
+            RTMsgWarning("%Rrc adding '%s'", rc, pszDst);
+        return RTEXITCODE_SUCCESS;
+    }
+    return RTMsgErrorExitFailure("%Rrc adding '%s'", rc, pszDst);
+}
+
+
+/**
+ * Archives a directory recursively .
+ *
+ * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
+ * @param   pOpts           The options.
+ * @param   hVfsFss         The TAR filesystem stream handle.
+ * @param   pszSrc          The directory path or VFS spec.  We append to the
+ *                          buffer as we decend.
+ * @param   cchSrc          The length of the input.
+ * @param   pszDst          The name to archive it the under.  We append to the
+ *                          buffer as we decend.
+ * @param   cchDst          The length of the input.
+ * @param   pErrInfo        Error info buffer (saves stack space).
+ */
+static RTEXITCODE rtZipTarCmdArchiveDir(PRTZIPTARCMDOPS pOpts, RTVFSFSSTREAM hVfsFss, char pszSrc[RTPATH_MAX], size_t cchSrc,
+                                        char pszDst[RTPATH_MAX], size_t cchDst, PRTERRINFOSTATIC pErrInfo)
+{
+    RT_NOREF(pOpts, hVfsFss, pszSrc, cchSrc, pszDst, cchDst, pErrInfo);
+    return RTMsgErrorExitFailure("Adding directories has not yet been implemented! Sorry.");
+}
+
+
+
+/**
+ * Opens the output archive specified by the options.
+ *
+ * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
+ * @param   pOpts           The options.
+ * @param   phVfsFss        Where to return the TAR filesystem stream handle.
+ */
+static RTEXITCODE rtZipTarCmdOpenOutputArchive(PRTZIPTARCMDOPS pOpts, PRTVFSFSSTREAM phVfsFss)
+{
+    int rc;
+
+    /*
+     * Open the output file.
+     */
+    RTVFSIOSTREAM   hVfsIos;
+    if (   pOpts->pszFile
+        && strcmp(pOpts->pszFile, "-") != 0)
+    {
+        uint32_t        offError = 0;
+        RTERRINFOSTATIC ErrInfo;
+        rc = RTVfsChainOpenIoStream(pOpts->pszFile, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
+                                    &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
+        if (RT_FAILURE(rc))
+            return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenIoStream", pOpts->pszFile, rc, offError, &ErrInfo.Core);
+    }
+    else
+    {
+        rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT,
+                                      RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
+                                      true /*fLeaveOpen*/,
+                                      &hVfsIos);
+        if (RT_FAILURE(rc))
+            return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to prepare standard output for writing: %Rrc", rc);
+    }
+
+    /*
+     * Pass it thru a compressor?
+     */
+    RTVFSIOSTREAM hVfsIosComp = NIL_RTVFSIOSTREAM;
+    switch (pOpts->chZipper)
+    {
+        /* no */
+        case '\0':
+            rc = VINF_SUCCESS;
+            break;
+
+        /* gunzip */
+        case 'z':
+            rc = RTZipGzipCompressIoStream(hVfsIos, 0 /*fFlags*/, 6, &hVfsIosComp);
+            if (RT_FAILURE(rc))
+                RTMsgError("Failed to open gzip decompressor: %Rrc", rc);
+            break;
+
+        /* bunzip2 */
+        case 'j':
+            rc = VERR_NOT_SUPPORTED;
+            RTMsgError("bzip2 is not supported by this build");
+            break;
+
+        /* bug */
+        default:
+            rc = VERR_INTERNAL_ERROR_2;
+            RTMsgError("unknown decompression method '%c'",  pOpts->chZipper);
+            break;
+    }
+    if (RT_FAILURE(rc))
+    {
+        RTVfsIoStrmRelease(hVfsIos);
+        return RTEXITCODE_FAILURE;
+    }
+
+    if (hVfsIosComp != NIL_RTVFSIOSTREAM)
+    {
+        RTVfsIoStrmRelease(hVfsIos);
+        hVfsIos = hVfsIosComp;
+        hVfsIosComp = NIL_RTVFSIOSTREAM;
+    }
+
+    /*
+     * Open the filesystem stream creator.
+     */
+    if (   pOpts->enmFormat == RTZIPTARCMDFORMAT_TAR
+        || pOpts->enmFormat == RTZIPTARCMDFORMAT_AUTO_DEFAULT)
+        rc = RTZipTarFsStreamToIoStream(hVfsIos, pOpts->enmTarFormat, pOpts->fTarCreate, phVfsFss);
+    else
+        rc = VERR_NOT_SUPPORTED;
+    RTVfsIoStrmRelease(hVfsIos);
+    if (RT_FAILURE(rc))
+        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open tar filesystem stream: %Rrc", rc);
+
+    return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Implements archive creation.
+ *
+ * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
+ * @param   pOpts           The options.
+ */
+static RTEXITCODE rtZipTarCreate(PRTZIPTARCMDOPS pOpts)
+{
+    /*
+     * Refuse to create empty archive.
+     */
+    if (pOpts->cFiles == 0)
+        return RTMsgErrorExitFailure("Nothing to archive - refusing to create empty archive!");
+
+    /*
+     * First open the output file.
+     */
+    RTVFSFSSTREAM hVfsFss;
+    RTEXITCODE rcExit = rtZipTarCmdOpenOutputArchive(pOpts, &hVfsFss);
+    if (rcExit != RTEXITCODE_SUCCESS)
+        return rcExit;
+
+    /*
+     * Process the input files.
+     */
+    for (uint32_t iFile = 0; iFile < pOpts->cFiles; iFile++)
+    {
+        const char *pszFile = pOpts->papszFiles[iFile];
+
+        /*
+         * Construct/copy the source name.
+         */
+        int         rc = VINF_SUCCESS;
+        char        szSrc[RTPATH_MAX];
+        if (   RTPathStartsWithRoot(pszFile)
+            || RTVfsChainIsSpec(pszFile))
+            rc = RTStrCopy(szSrc, sizeof(szSrc), pszFile);
+        else
+            rc = RTPathJoin(szSrc, sizeof(szSrc), pOpts->pszDirectory ? pOpts->pszDirectory : ".", pOpts->papszFiles[iFile]);
+        if (RT_SUCCESS(rc))
+        {
+            /*
+             * Construct the archived name.  We must strip leading root specifier.
+             */
+            char *pszFinalPath = NULL;
+            char  szDst[RTPATH_MAX];
+            const char *pszDst = pszFile;
+            if (RTVfsChainIsSpec(pszFile))
+            {
+                uint32_t offError;
+                rc = RTVfsChainQueryFinalPath(pszFile, &pszFinalPath, &offError);
+                if (RT_SUCCESS(rc))
+                    pszDst = pszFinalPath;
+                else
+                    rcExit = RTVfsChainMsgErrorExitFailure("RTVfsChainQueryFinalPath", pszFile, rc, offError, NULL);
+            }
+            if (RT_SUCCESS(rc))
+            {
+                pszDst = RTPathSkipRootSpec(pszDst);
+                if (*pszDst == '\0')
+                {
+                    pszDst = pOpts->pszPrefix ? pOpts->pszPrefix : ".";
+                    rc = RTStrCopy(szDst, sizeof(szDst), pszDst);
+                }
+                else
+                {
+                    if (pOpts->pszPrefix)
+                        rc = RTPathJoin(szDst, sizeof(szDst), pOpts->pszPrefix, pszDst);
+                    else
+                        rc = RTStrCopy(szDst, sizeof(szDst), pszDst);
+                }
+                if (RT_SUCCESS(rc))
+                {
+                    /*
+                     * What kind of object is this?
+                     */
+                    RTERRINFOSTATIC ErrInfo;
+                    uint32_t        offError;
+                    RTFSOBJINFO     ObjInfo;
+                    rc = RTVfsChainQueryInfo(szSrc, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK,
+                                             &offError, RTErrInfoInitStatic(&ErrInfo));
+                    if (RT_SUCCESS(rc))
+                    {
+                        RTEXITCODE rcExit2;
+                        if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
+                            rcExit2 = rtZipTarCmdArchiveDir(pOpts, hVfsFss, szSrc, strlen(szSrc), szDst, strlen(szDst), &ErrInfo);
+                        else
+                            rcExit2 = rtZipTarCmdArchiveFile(pOpts, hVfsFss, szSrc, szDst, &ErrInfo);
+                    }
+                    else
+                        rcExit = RTVfsChainMsgErrorExitFailure("RTVfsChainQueryInfo", pszFile, rc, offError, &ErrInfo.Core);
+                }
+                else
+                    rcExit = RTMsgErrorExitFailure("archived file name is too long, skipping '%s' (%s)", pszDst, pszFile);
+            }
+        }
+        else
+            rcExit = RTMsgErrorExitFailure("input file name is too long, skipping '%s'",  pszFile);
+    }
+
+    /*
+     * Finalize the archive.
+     */
+    int rc = RTVfsFsStrmEnd(hVfsFss);
+    if (RT_FAILURE(rc))
+        rcExit = RTMsgErrorExitFailure("RTVfsFsStrmEnd failed: %Rrc", rc);
+
+    RTVfsFsStrmRelease(hVfsFss);
+    return rcExit;
 }
 
@@ -908,5 +1187,5 @@
              );
     RTPrintf("Basic Options:\n"
-             "    -C <dir>, --directory <dir>           (-A, -C, -d, -r, -u, -x)\n"
+             "    -C <dir>, --directory <dir>           (-A, -c, -d, -r, -u, -x)\n"
              "        Sets the base directory for input and output file members.\n"
              "        This does not apply to --file, even if it preceeds it.\n"
@@ -925,21 +1204,35 @@
              "\n");
     RTPrintf("Misc Options:\n"
-             "    --owner <uid/username>                (-A, -C, -d, -r, -u, -x)\n"
+             "    --owner <uid/username>                (-A, -c, -d, -r, -u, -x)\n"
              "        Set the owner of extracted and archived files to the user specified.\n"
-             "    --group <uid/username>                (-A, -C, -d, -r, -u, -x)\n"
+             "    --group <uid/username>                (-A, -c, -d, -r, -u, -x)\n"
              "        Set the group of extracted and archived files to the group specified.\n"
              "    --utc                                 (-t)\n"
              "        Display timestamps as UTC instead of local time.\n"
+             "    -S, --sparse                          (-A, -c, -u)\n"
+             "        Detect sparse files and store them (gnu tar extension).\n"
+             "    --format <format>                     (-A, -c, -u, but also -d, -r, -x)\n"
+             "        The file format:\n"
+             "                  auto (gnu tar)\n"
+             "                  default (gnu tar)\n"
+             "                  tar (gnu tar)"
+             "                  gnu (tar v1.13+), "
+             "                  ustar (tar POSIX.1-1988), "
+             "                  pax (tar POSIX.1-2001),\n"
+             "                  xar\n"
+             "        Note! Because XAR/TAR 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"
              "\n");
     RTPrintf("IPRT Options:\n"
-             "    --prefix <dir-prefix>                 (-A, -C, -d, -r, -u)\n"
+             "    --prefix <dir-prefix>                 (-A, -c, -d, -r, -u)\n"
              "        Directory prefix to give the members added to the archive.\n"
-             "    --file-mode-and-mask <octal-mode>     (-A, -C, -d, -r, -u, -x)\n"
+             "    --file-mode-and-mask <octal-mode>     (-A, -c, -d, -r, -u, -x)\n"
              "        Restrict the access mode of regular and special files.\n"
-             "    --file-mode-or-mask <octal-mode>     (-A, -C, -d, -r, -u, -x)\n"
+             "    --file-mode-or-mask <octal-mode>      (-A, -c, -d, -r, -u, -x)\n"
              "        Include the given access mode for regular and special files.\n"
-             "    --dir-mode-and-mask <octal-mode>      (-A, -C, -d, -r, -u, -x)\n"
+             "    --dir-mode-and-mask <octal-mode>      (-A, -c, -d, -r, -u, -x)\n"
              "        Restrict the access mode of directories.\n"
-             "    --dir-mode-or-mask <octal-mode>      (-A, -C, -d, -r, -u, -x)\n"
+             "    --dir-mode-or-mask <octal-mode>       (-A, -c, -d, -r, -u, -x)\n"
              "        Include the given access mode for directories.\n"
              "    --read-ahead                          (-x)\n"
@@ -966,30 +1259,32 @@
     {
         /* operations */
-        { "--concatenate",          'A', RTGETOPT_REQ_NOTHING },
-        { "--catenate",             'A', RTGETOPT_REQ_NOTHING },
-        { "--create",               'c', RTGETOPT_REQ_NOTHING },
-        { "--diff",                 'd', RTGETOPT_REQ_NOTHING },
-        { "--compare",              'd', RTGETOPT_REQ_NOTHING },
-        { "--append",               'r', RTGETOPT_REQ_NOTHING },
-        { "--list",                 't', RTGETOPT_REQ_NOTHING },
-        { "--update",               'u', RTGETOPT_REQ_NOTHING },
-        { "--extract",              'x', RTGETOPT_REQ_NOTHING },
-        { "--get",                  'x', RTGETOPT_REQ_NOTHING },
-        { "--delete",       RTZIPTARCMD_OPT_DELETE, RTGETOPT_REQ_NOTHING },
+        { "--concatenate",          'A',                                RTGETOPT_REQ_NOTHING },
+        { "--catenate",             'A',                                RTGETOPT_REQ_NOTHING },
+        { "--create",               'c',                                RTGETOPT_REQ_NOTHING },
+        { "--diff",                 'd',                                RTGETOPT_REQ_NOTHING },
+        { "--compare",              'd',                                RTGETOPT_REQ_NOTHING },
+        { "--append",               'r',                                RTGETOPT_REQ_NOTHING },
+        { "--list",                 't',                                RTGETOPT_REQ_NOTHING },
+        { "--update",               'u',                                RTGETOPT_REQ_NOTHING },
+        { "--extract",              'x',                                RTGETOPT_REQ_NOTHING },
+        { "--get",                  'x',                                RTGETOPT_REQ_NOTHING },
+        { "--delete",               RTZIPTARCMD_OPT_DELETE,             RTGETOPT_REQ_NOTHING },
 
         /* basic options */
-        { "--directory",            'C', RTGETOPT_REQ_STRING },
-        { "--file",                 'f', RTGETOPT_REQ_STRING },
-        { "--verbose",              'v', RTGETOPT_REQ_NOTHING },
-        { "--preserve-permissions", 'p', RTGETOPT_REQ_NOTHING },
-        { "--bzip2",                'j', RTGETOPT_REQ_NOTHING },
-        { "--gzip",                 'z', RTGETOPT_REQ_NOTHING },
-        { "--gunzip",               'z', RTGETOPT_REQ_NOTHING },
-        { "--ungzip",               'z', RTGETOPT_REQ_NOTHING },
+        { "--directory",            'C',                                RTGETOPT_REQ_STRING },
+        { "--file",                 'f',                                RTGETOPT_REQ_STRING },
+        { "--verbose",              'v',                                RTGETOPT_REQ_NOTHING },
+        { "--preserve-permissions", 'p',                                RTGETOPT_REQ_NOTHING },
+        { "--bzip2",                'j',                                RTGETOPT_REQ_NOTHING },
+        { "--gzip",                 'z',                                RTGETOPT_REQ_NOTHING },
+        { "--gunzip",               'z',                                RTGETOPT_REQ_NOTHING },
+        { "--ungzip",               'z',                                RTGETOPT_REQ_NOTHING },
 
         /* other options. */
-        { "--owner",                RTZIPTARCMD_OPT_OWNER, RTGETOPT_REQ_STRING },
-        { "--group",                RTZIPTARCMD_OPT_GROUP, RTGETOPT_REQ_STRING },
-        { "--utc",                  RTZIPTARCMD_OPT_UTC,   RTGETOPT_REQ_NOTHING },
+        { "--owner",                RTZIPTARCMD_OPT_OWNER,              RTGETOPT_REQ_STRING },
+        { "--group",                RTZIPTARCMD_OPT_GROUP,              RTGETOPT_REQ_STRING },
+        { "--utc",                  RTZIPTARCMD_OPT_UTC,                RTGETOPT_REQ_NOTHING },
+        { "--sparse",               'S',                                RTGETOPT_REQ_NOTHING },
+        { "--format",               RTZIPTARCMD_OPT_FORMAT,             RTGETOPT_REQ_STRING },
 
         /* IPRT extensions */
@@ -999,5 +1294,4 @@
         { "--dir-mode-and-mask",    RTZIPTARCMD_OPT_DIR_MODE_AND_MASK,  RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
         { "--dir-mode-or-mask",     RTZIPTARCMD_OPT_DIR_MODE_OR_MASK,   RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
-        { "--format",               RTZIPTARCMD_OPT_FORMAT,             RTGETOPT_REQ_STRING },
         { "--read-ahead",           RTZIPTARCMD_OPT_READ_AHEAD,         RTGETOPT_REQ_NOTHING },
     };
@@ -1025,4 +1319,6 @@
     }
 #endif
+    Opts.enmTarFormat = RTZIPTARFORMAT_DEFAULT;
+    Opts.fTarCreate   = 0;
 
     RTGETOPTUNION   ValueUnion;
@@ -1115,4 +1411,9 @@
                 break;
 
+            /* GNU */
+            case 'S':
+                Opts.fTarCreate |= RTZIPTAR_C_SPARSE;
+                break;
+
             /* iprt extensions */
             case RTZIPTARCMD_OPT_PREFIX:
@@ -1140,9 +1441,31 @@
             case RTZIPTARCMD_OPT_FORMAT:
                 if (!strcmp(ValueUnion.psz, "auto") || !strcmp(ValueUnion.psz, "default"))
-                    Opts.enmFormat = RTZIPTARCMDFORMAT_AUTO_DEFAULT;
+                {
+                    Opts.enmFormat    = RTZIPTARCMDFORMAT_AUTO_DEFAULT;
+                    Opts.enmTarFormat = RTZIPTARFORMAT_DEFAULT;
+                }
                 else if (!strcmp(ValueUnion.psz, "tar"))
-                    Opts.enmFormat = RTZIPTARCMDFORMAT_TAR;
+                {
+                    Opts.enmFormat    = RTZIPTARCMDFORMAT_TAR;
+                    Opts.enmTarFormat = RTZIPTARFORMAT_DEFAULT;
+                }
+                else if (!strcmp(ValueUnion.psz, "gnu"))
+                {
+                    Opts.enmFormat    = RTZIPTARCMDFORMAT_TAR;
+                    Opts.enmTarFormat = RTZIPTARFORMAT_GNU;
+                }
+                else if (!strcmp(ValueUnion.psz, "ustar"))
+                {
+                    Opts.enmFormat    = RTZIPTARCMDFORMAT_TAR;
+                    Opts.enmTarFormat = RTZIPTARFORMAT_USTAR;
+                }
+                else if (   !strcmp(ValueUnion.psz, "posix")
+                         || !strcmp(ValueUnion.psz, "pax"))
+                {
+                    Opts.enmFormat    = RTZIPTARCMDFORMAT_TAR;
+                    Opts.enmTarFormat = RTZIPTARFORMAT_PAX;
+                }
                 else if (!strcmp(ValueUnion.psz, "xar"))
-                    Opts.enmFormat = RTZIPTARCMDFORMAT_XAR;
+                    Opts.enmFormat    = RTZIPTARCMDFORMAT_XAR;
                 else
                     return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown archive format: '%s'", ValueUnion.psz);
@@ -1203,6 +1526,8 @@
             return rtZipTarDoWithMembers(&Opts, rtZipTarCmdExtractCallback);
 
+        case 'c':
+            return rtZipTarCreate(&Opts);
+
         case 'A':
-        case 'c':
         case 'd':
         case 'r':
Index: trunk/src/VBox/Runtime/common/zip/tarvfswriter.cpp
===================================================================
--- trunk/src/VBox/Runtime/common/zip/tarvfswriter.cpp	(revision 67133)
+++ trunk/src/VBox/Runtime/common/zip/tarvfswriter.cpp	(revision 67134)
@@ -32,4 +32,5 @@
 #include <iprt/zip.h>
 
+#include <iprt/asm.h>
 #include <iprt/assert.h>
 #include <iprt/err.h>
@@ -46,7 +47,69 @@
 
 /*********************************************************************************************************************************
+*   Defined Constants And Macros                                                                                                 *
+*********************************************************************************************************************************/
+/** The TAR block size we're using in this implementation.
+ * @remarks Should technically be user configurable, but we don't currently need that. */
+#define RTZIPTAR_BLOCKSIZE      sizeof(RTZIPTARHDR)
+
+/** Minimum file size we consider for sparse files. */
+#define RTZIPTAR_MIN_SPARSE     _64K
+
+
+/*********************************************************************************************************************************
 *   Structures and Typedefs                                                                                                      *
 *********************************************************************************************************************************/
 /**
+ * A data span descriptor in a sparse file.
+ */
+typedef struct RTZIPTARSPARSESPAN
+{
+    /** Byte offset into the file of the data. */
+    uint64_t        off;
+    /** Number of bytes of data, rounded up to a multiple of blocksize. */
+    uint64_t        cb;
+} RTZIPTARSPARSESPAN;
+/** Pointer to a data span. */
+typedef RTZIPTARSPARSESPAN *PRTZIPTARSPARSESPAN;
+/** Pointer to a const data span. */
+typedef RTZIPTARSPARSESPAN const *PCRTZIPTARSPARSESPAN;
+
+/**
+ * Chunk of TAR sparse file data spans.
+ */
+typedef struct RTZIPTARSPARSECHUNK
+{
+    /** List entry. */
+    RTLISTNODE          Entry;
+    /** Array of data spans. */
+    RTZIPTARSPARSESPAN  aSpans[63];
+} RTZIPTARSPARSECHUNK;
+AssertCompile(sizeof(RTZIPTARSPARSECHUNK) <= 1024);
+AssertCompile(sizeof(RTZIPTARSPARSECHUNK) >= 1008);
+/** Pointer to a chunk of TAR data spans. */
+typedef RTZIPTARSPARSECHUNK *PRTZIPTARSPARSECHUNK;
+/** Pointer to a const chunk of TAR data spans. */
+typedef RTZIPTARSPARSECHUNK const *PCRTZIPTARSPARSECHUNK;
+
+/**
+ * TAR sparse file info.
+ */
+typedef struct RTZIPTARSPARSE
+{
+    /** Number of data bytes (real size).  */
+    uint64_t            cbDataSpans;
+    /** Number of data spans. */
+    uint32_t            cDataSpans;
+    /** The index of the next span in the tail chunk (to avoid modulus 63). */
+    uint32_t            iNextSpan;
+    /** Head of the data span chunk list (PRTZIPTARSPARSECHUNK). */
+    RTLISTANCHOR        ChunkHead;
+} RTZIPTARSPARSE;
+/** Pointer to TAR sparse file info. */
+typedef RTZIPTARSPARSE *PRTZIPTARSPARSE;
+/** Pointer to a const TAR sparse file info. */
+typedef RTZIPTARSPARSE const *PCRTZIPTARSPARSE;
+
+/**
  * Tar filesystem stream private data.
  */
@@ -55,4 +118,6 @@
     /** The output I/O stream. */
     RTVFSIOSTREAM           hVfsIos;
+    /** Non-nil if the output is a file.  */
+    RTVFSFILE               hVfsFile;
 
     /** The TAR format. */
@@ -75,4 +140,11 @@
 
 
+/*********************************************************************************************************************************
+*   Internal Functions                                                                                                           *
+*********************************************************************************************************************************/
+static int rtZipTarFssWriter_AddFile(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos,
+                                     PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm);
+
+
 /**
  * Calculates the header checksum and stores it in the chksum field.
@@ -87,5 +159,5 @@
 
     int rc = RTStrFormatU32(pHdr->Common.chksum, sizeof(pHdr->Common.chksum), iUnsignedChksum,
-                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pHdr->Common.chksum) - 1, RTSTR_F_ZEROPAD);
+                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pHdr->Common.chksum) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
     AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
     return VINF_SUCCESS;
@@ -114,5 +186,5 @@
     if (off < _4G * 2U)
     {
-        int rc = RTStrFormatU64(pach12Field, 12, off, 8 /*uBase*/, -1 /*cchWidth*/, 12 - 1, RTSTR_F_ZEROPAD);
+        int rc = RTStrFormatU64(pach12Field, 12, off, 8 /*uBase*/, -1 /*cchWidth*/, 12 - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
         AssertRCReturn(rc, rc);
     }
@@ -127,4 +199,6 @@
      *       trouble as they are not picky about what they read.
      */
+    /** @todo above note is wrong:  GNU tar only uses base-256 with the GNU tar
+     * format, i.e. "ustar   \0", see create.c line 303 in v1.29. */
     else
     {
@@ -192,5 +266,5 @@
     int rc;
     rc = RTStrFormatU32(pThis->aHdrs[0].Common.mode, sizeof(pThis->aHdrs[0].Common.mode), pObjInfo->Attr.fMode & RTFS_UNIX_MASK,
-                        8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.mode) - 1, RTSTR_F_ZEROPAD);
+                        8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.mode) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
     AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
 
@@ -201,10 +275,10 @@
     uint32_t uValue = pObjInfo->Attr.u.Unix.uid != NIL_RTUID ? pObjInfo->Attr.u.Unix.uid : 0;
     rc = RTStrFormatU32(pThis->aHdrs[0].Common.uid, sizeof(pThis->aHdrs[0].Common.uid), uValue,
-                        8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.uid) - 1, RTSTR_F_ZEROPAD);
+                        8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.uid) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
     AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
 
     uValue = pObjInfo->Attr.u.Unix.gid != NIL_RTUID ? pObjInfo->Attr.u.Unix.gid : 0;
     rc = RTStrFormatU32(pThis->aHdrs[0].Common.gid, sizeof(pThis->aHdrs[0].Common.gid), uValue,
-                        8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.gid) - 1, RTSTR_F_ZEROPAD);
+                        8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.gid) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
     AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
 
@@ -220,5 +294,5 @@
     rc = RTStrFormatU64(pThis->aHdrs[0].Common.mtime, sizeof(pThis->aHdrs[0].Common.mtime),
                         RTTimeSpecGetSeconds(&pObjInfo->ModificationTime),
-                        8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.mtime) - 1, RTSTR_F_ZEROPAD);
+                        8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.mtime) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
     AssertRCReturn(rc, rc);
 
@@ -247,7 +321,14 @@
      * Set TAR record magic and version.
      */
-    memcpy(pThis->aHdrs[0].Common.magic, RT_STR_TUPLE("ustar "));
-    pThis->aHdrs[0].Common.version[0] = ' ';
-    pThis->aHdrs[0].Common.version[1] = ' ';
+    if (pThis->enmFormat == RTZIPTARFORMAT_GNU)
+        memcpy(pThis->aHdrs[0].Gnu.magic, RTZIPTAR_GNU_MAGIC, sizeof(pThis->aHdrs[0].Gnu.magic));
+    else if (   pThis->enmFormat == RTZIPTARFORMAT_USTAR
+             || pThis->enmFormat == RTZIPTARFORMAT_PAX)
+    {
+        memcpy(pThis->aHdrs[0].Common.magic, RTZIPTAR_USTAR_MAGIC, sizeof(pThis->aHdrs[0].Common.magic));
+        memcpy(pThis->aHdrs[0].Common.version, RTZIPTAR_USTAR_VERSION, sizeof(pThis->aHdrs[0].Common.version));
+    }
+    else
+        AssertFailedReturn(VERR_INTERNAL_ERROR_4);
 
     /*
@@ -265,16 +346,35 @@
         rc = RTStrFormatU32(pThis->aHdrs[0].Common.devmajor, sizeof(pThis->aHdrs[0].Common.devmajor),
                             RTDEV_MAJOR(pObjInfo->Attr.u.Unix.Device),
-                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.devmajor) - 1, RTSTR_F_ZEROPAD);
+                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.devmajor) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
         AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
 
         rc = RTStrFormatU32(pThis->aHdrs[0].Common.devminor, sizeof(pThis->aHdrs[0].Common.devmajor),
                             RTDEV_MINOR(pObjInfo->Attr.u.Unix.Device),
-                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.devmajor) - 1, RTSTR_F_ZEROPAD);
+                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.devmajor) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
         AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
     }
 
+#if 0 /** @todo why doesn't this work? */
+    /*
+     * Set GNU specific stuff.
+     */
+    if (pThis->enmFormat == RTZIPTARFORMAT_GNU)
+    {
+        rc = RTStrFormatU64(pThis->aHdrs[0].Gnu.ctime, sizeof(pThis->aHdrs[0].Gnu.ctime),
+                            RTTimeSpecGetSeconds(&pObjInfo->ChangeTime),
+                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Gnu.ctime) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
+        AssertRCReturn(rc, rc);
+
+        rc = RTStrFormatU64(pThis->aHdrs[0].Gnu.atime, sizeof(pThis->aHdrs[0].Gnu.atime),
+                            RTTimeSpecGetSeconds(&pObjInfo->ChangeTime),
+                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Gnu.atime) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
+        AssertRCReturn(rc, rc);
+    }
+#endif
+
     /*
      * Finally the checksum.
      */
+    pThis->cHdrs = 1;
     return rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
 }
@@ -282,5 +382,448 @@
 
 /**
- * Adds a file to the stream.
+ * Allocates a buffer for transfering file data.
+ *
+ * @note    Will use the 3rd TAR header as fallback buffer if we're out of
+ *          memory!
+ *
+ * @returns Pointer to buffer (won't ever fail).
+ * @param   pThis           The TAR writer instance.
+ * @param   pcbBuf          Where to return the buffer size.  This will be a
+ *                          multiple of the TAR block size.
+ * @param   ppvFree         Where to return the pointer to pass to RTMemTmpFree
+ *                          when done with the buffer.
+ * @param   cbFile          The file size.  Used as a buffer size hint.
+ */
+static uint8_t *rtZipTarFssWrite_AllocBuf(PRTZIPTARFSSTREAMWRITER pThis, size_t *pcbBuf, void **ppvFree, uint64_t cbObject)
+{
+    uint8_t *pbBuf;
+
+    /*
+     * If this is a large file, try for a large buffer with 16KB alignment.
+     */
+    if (cbObject >= _64M)
+    {
+        pbBuf = (uint8_t *)RTMemTmpAlloc(_2M + _16K - 1);
+        if (pbBuf)
+        {
+            *pcbBuf  = _2M;
+            *ppvFree = pbBuf;
+            return RT_ALIGN_PT(pbBuf, _16K, uint8_t *);
+        }
+    }
+    /*
+     * 4KB aligned 512KB buffer if larger 512KB or larger.
+     */
+    else if (cbObject >= _512K)
+    {
+        pbBuf = (uint8_t *)RTMemTmpAlloc(_512K + _4K - 1);
+        if (pbBuf)
+        {
+            *pcbBuf  = _512K;
+            *ppvFree = pbBuf;
+            return RT_ALIGN_PT(pbBuf, _4K, uint8_t *);
+        }
+    }
+    /*
+     * Otherwise a 4KB aligned 128KB buffer.
+     */
+    else
+    {
+        pbBuf = (uint8_t *)RTMemTmpAlloc(_128K + _4K - 1);
+        if (pbBuf)
+        {
+            *pcbBuf  = _128K;
+            *ppvFree = pbBuf;
+            return RT_ALIGN_PT(pbBuf, _4K, uint8_t *);
+        }
+    }
+
+    /*
+     * If allocation failed, fallback on a 16KB buffer without any extra alignment.
+     */
+    pbBuf = (uint8_t *)RTMemTmpAlloc(_16K);
+    if (pbBuf)
+    {
+        *pcbBuf  = _16K;
+        *ppvFree = pbBuf;
+        return pbBuf;
+    }
+
+    /*
+     * Final fallback, 512KB buffer using the 3rd header.
+     */
+    AssertCompile(RT_ELEMENTS(pThis->aHdrs) >= 3);
+    *pcbBuf  = sizeof(pThis->aHdrs[2]);
+    *ppvFree = NULL;
+    return (uint8_t *)&pThis->aHdrs[2];
+}
+
+
+/**
+ * Frees the sparse info for a TAR file.
+ *
+ * @param   pSparse         The sparse info to free.
+ */
+static void rtZipTarFssWriter_SparseInfoDestroy(PRTZIPTARSPARSE pSparse)
+{
+    PRTZIPTARSPARSECHUNK pCur;
+    PRTZIPTARSPARSECHUNK pNext;
+    RTListForEachSafe(&pSparse->ChunkHead, pCur, pNext, RTZIPTARSPARSECHUNK, Entry)
+        RTMemTmpFree(pCur);
+    RTMemTmpFree(pSparse);
+}
+
+
+/**
+ * Adds a data span to the sparse info.
+ *
+ * @returns VINF_SUCCESS or VINF_NO_TMP_MEMORY.
+ * @param   pSparse         The sparse info to free.
+ * @param   offSpan         Offset of the span.
+ * @param   cbSpan          Number of bytes.
+ */
+static int rtZipTarFssWriter_SparseInfoAddSpan(PRTZIPTARSPARSE pSparse, uint64_t offSpan, uint64_t cbSpan)
+{
+    /*
+     * Get the chunk we're adding it to.
+     */
+    PRTZIPTARSPARSECHUNK pChunk;
+    if (pSparse->iNextSpan != 0)
+    {
+        pChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
+        Assert(pSparse->iNextSpan < RT_ELEMENTS(pChunk->aSpans));
+    }
+    else
+    {
+        pChunk = (PRTZIPTARSPARSECHUNK)RTMemTmpAllocZ(sizeof(*pChunk));
+        if (!pChunk)
+            return VERR_NO_TMP_MEMORY;
+        RTListAppend(&pSparse->ChunkHead, &pChunk->Entry);
+    }
+
+    /*
+     * Append it.
+     */
+    pSparse->cDataSpans  += 1;
+    pSparse->cbDataSpans += cbSpan;
+    pChunk->aSpans[pSparse->iNextSpan].cb  = cbSpan;
+    pChunk->aSpans[pSparse->iNextSpan].off = offSpan;
+    if (++pSparse->iNextSpan >= RT_ELEMENTS(pChunk->aSpans))
+        pSparse->iNextSpan = 0;
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Scans the input stream recording non-zero blocks.
+ */
+static int rtZipTarFssWriter_ScanSparseFile(PRTZIPTARFSSTREAMWRITER pThis, RTVFSFILE hVfsFile, uint64_t cbFile,
+                                            size_t cbBuf, uint8_t *pbBuf, PRTZIPTARSPARSE *ppSparse)
+{
+    RT_NOREF(pThis);
+
+    /*
+     * Create an empty sparse info bundle.
+     */
+    PRTZIPTARSPARSE pSparse = (PRTZIPTARSPARSE)RTMemTmpAlloc(sizeof(*pSparse));
+    AssertReturn(pSparse, VERR_NO_MEMORY);
+    pSparse->cbDataSpans = 0;
+    pSparse->cDataSpans  = 0;
+    pSparse->iNextSpan   = 0;
+    RTListInit(&pSparse->ChunkHead);
+
+    /*
+     * Scan the file from the start.
+     */
+    int rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
+    if (RT_SUCCESS(rc))
+    {
+        bool        fZeroSpan = false;
+        uint64_t    offSpan   = 0;
+        uint64_t    cbSpan    = 0;
+
+        for (uint64_t off = 0; off < cbFile;)
+        {
+            uint64_t cbLeft   = cbFile - off;
+            size_t   cbToRead = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
+            rc = RTVfsFileRead(hVfsFile, pbBuf, cbToRead, NULL);
+            if (RT_FAILURE(rc))
+                break;
+            size_t cBlocks = cbToRead / RTZIPTAR_BLOCKSIZE;
+
+            /* Zero pad the final buffer to a multiple of the blocksize. */
+            if (!(cbToRead & (RTZIPTAR_BLOCKSIZE - 1)))
+            { /* likely */ }
+            else
+            {
+                AssertBreakStmt(cbLeft == cbToRead, rc = VERR_INTERNAL_ERROR_3);
+                RT_BZERO(&pbBuf[cbToRead], RTZIPTAR_BLOCKSIZE - (cbToRead & (RTZIPTAR_BLOCKSIZE - 1)));
+                cBlocks++;
+            }
+
+            /*
+             * Process the blocks we've just read one by one.
+             */
+            uint8_t const *pbBlock = pbBuf;
+            for (size_t iBlock = 0; iBlock < cBlocks; iBlock++)
+            {
+                bool fZeroBlock = ASMMemIsZero(pbBlock, RTZIPTAR_BLOCKSIZE);
+                if (fZeroBlock == fZeroSpan)
+                    cbSpan += RTZIPTAR_BLOCKSIZE;
+                else
+                {
+                    if (!fZeroSpan && cbSpan)
+                    {
+                        rc = rtZipTarFssWriter_SparseInfoAddSpan(pSparse, offSpan, cbSpan);
+                        if (RT_FAILURE(rc))
+                            break;
+                    }
+                    fZeroSpan = fZeroBlock;
+                    offSpan   = off;
+                    cbSpan    = RTZIPTAR_BLOCKSIZE;
+                }
+
+                /* next block. */
+                pbBlock += RTZIPTAR_BLOCKSIZE;
+                off     += RTZIPTAR_BLOCKSIZE;
+            }
+        }
+
+        /*
+         * Deal with the final span.  If we've got zeros thowards the end, we
+         * must add a zero byte data span at the end.
+         */
+        if (RT_SUCCESS(rc))
+        {
+            if (!fZeroSpan && cbSpan)
+            {
+                if (cbFile & (RTZIPTAR_BLOCKSIZE - 1))
+                {
+                    Assert(!(cbSpan & (RTZIPTAR_BLOCKSIZE - 1)));
+                    cbSpan -= RTZIPTAR_BLOCKSIZE;
+                    cbSpan |= cbFile & (RTZIPTAR_BLOCKSIZE - 1);
+                }
+                rc = rtZipTarFssWriter_SparseInfoAddSpan(pSparse, offSpan, cbSpan);
+            }
+            if (RT_SUCCESS(rc))
+                rc = rtZipTarFssWriter_SparseInfoAddSpan(pSparse, cbFile, 0);
+        }
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Return the file back to the start position before we return so that we
+         * can segue into the regular rtZipTarFssWriter_AddFile without further ado.
+         */
+        rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
+        if (RT_SUCCESS(rc))
+        {
+            *ppSparse = pSparse;
+            return VINF_SUCCESS;
+        }
+    }
+
+    rtZipTarFssWriter_SparseInfoDestroy(pSparse);
+    *ppSparse = NULL;
+    return rc;
+}
+
+
+/**
+ * Writes GNU the sparse file headers.
+ *
+ * @returns IPRT status code.
+ * @param   pThis           The TAR writer instance.
+ * @param   pszPath         The path to the file.
+ * @param   pObjInfo        The object information.
+ * @param   pszOwnerNm      The owner name.
+ * @param   pszGroupNm      The group name.
+ * @param   pSparse         The sparse file info.
+ */
+static int rtZipTarFssWriter_WriteGnuSparseHeaders(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath,  PCRTFSOBJINFO pObjInfo,
+                                                   const char *pszOwnerNm, const char *pszGroupNm, PCRTZIPTARSPARSE pSparse)
+{
+    /*
+     * Format the first header.
+     */
+    int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, RTZIPTAR_TF_GNU_SPARSE);
+    AssertRCReturn(rc, rc);
+    AssertReturn(pThis->cHdrs == 1, VERR_INTERNAL_ERROR_2);
+
+    /* data size. */
+    rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Common.size, pSparse->cbDataSpans);
+    AssertRCReturn(rc, rc);
+
+    /* realsize. */
+    rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Gnu.realsize, pObjInfo->cbObject);
+    AssertRCReturn(rc, rc);
+
+    /*
+     * Walk the sparse spans, fill and write headers one by one.
+     */
+    PRTZIPTARGNUSPARSE  paSparse    = &pThis->aHdrs[0].Gnu.sparse[0];
+    uint32_t            cSparse     = RT_ELEMENTS(pThis->aHdrs[0].Gnu.sparse);
+    uint32_t            iSparse     = 0;
+
+    PRTZIPTARSPARSECHUNK const pLastChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
+    PRTZIPTARSPARSECHUNK pChunk;
+    RTListForEach(&pSparse->ChunkHead, pChunk, RTZIPTARSPARSECHUNK, Entry)
+    {
+        uint32_t cSpans = pChunk != pLastChunk || pSparse->iNextSpan == 0
+                        ? RT_ELEMENTS(pChunk->aSpans) : pSparse->iNextSpan;
+        for (uint32_t iSpan = 0; iSpan < cSpans; iSpan++)
+        {
+            /* Flush the header? */
+            if (iSparse >= cSparse)
+            {
+                pThis->aHdrs[0].Gnu.isextended = 1; /* more headers to come */
+                if (cSparse == RT_ELEMENTS(pThis->aHdrs[0].Gnu.sparse))
+                    rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
+                if (RT_SUCCESS(rc))
+                    rc = RTVfsIoStrmWrite(pThis->hVfsIos, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
+                if (RT_FAILURE(rc))
+                    return rc;
+                RT_ZERO(pThis->aHdrs[0]);
+                cSparse  = RT_ELEMENTS(pThis->aHdrs[0].GnuSparse.sp);
+                iSparse  = 0;
+                paSparse = &pThis->aHdrs[0].GnuSparse.sp[0];
+            }
+
+            /* Append sparse data segment. */
+            rc = rtZipTarFssWriter_FormatOffset(paSparse[iSparse].offset, pChunk->aSpans[iSpan].off);
+            AssertRCReturn(rc, rc);
+            rc = rtZipTarFssWriter_FormatOffset(paSparse[iSparse].numbytes, pChunk->aSpans[iSpan].cb);
+            AssertRCReturn(rc, rc);
+            iSparse++;
+        }
+    }
+
+    /*
+     * The final header.
+     */
+    if (iSparse != 0)
+    {
+        Assert(pThis->aHdrs[0].Gnu.isextended == 0);
+        rc = RTVfsIoStrmWrite(pThis->hVfsIos, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
+    }
+    pThis->cHdrs = 0;
+    return rc;
+}
+
+
+/**
+ * Adds a potentially sparse file to the output.
+ *
+ * @returns IPRT status code.
+ * @param   pThis           The TAR writer instance.
+ * @param   pszPath         The path to the file.
+ * @param   hVfsFile        The potentially sparse file.
+ * @param   hVfsIos         The I/O stream of the file. Same as @a hVfsFile.
+ * @param   pObjInfo        The object information.
+ * @param   pszOwnerNm      The owner name.
+ * @param   pszGroupNm      The group name.
+ */
+static int rtZipTarFssWriter_AddFileSparse(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSFILE hVfsFile,
+                                           RTVFSIOSTREAM hVfsIos, PCRTFSOBJINFO pObjInfo,
+                                           const char *pszOwnerNm, const char *pszGroupNm)
+{
+    /*
+     * Scan the input file to locate all zero blocks.
+     */
+    void    *pvBufFree;
+    size_t   cbBuf;
+    uint8_t *pbBuf = rtZipTarFssWrite_AllocBuf(pThis, &cbBuf, &pvBufFree, pObjInfo->cbObject);
+
+    PRTZIPTARSPARSE pSparse;
+    int rc = rtZipTarFssWriter_ScanSparseFile(pThis, hVfsFile, pObjInfo->cbObject, cbBuf, pbBuf, &pSparse);
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * If there aren't at least 2 zero blocks in the file, don't bother
+         * doing the sparse stuff and store it as a normal file.
+         */
+        if (pSparse->cbDataSpans + RTZIPTAR_BLOCKSIZE > (uint64_t)pObjInfo->cbObject)
+        {
+            rtZipTarFssWriter_SparseInfoDestroy(pSparse);
+            RTMemTmpFree(pvBufFree);
+            return rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, pObjInfo, pszOwnerNm, pszGroupNm);
+        }
+
+        /*
+         * Produce and write the headers.
+         */
+        if (pThis->enmFormat == RTZIPTARFORMAT_GNU)
+            rc = rtZipTarFssWriter_WriteGnuSparseHeaders(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, pSparse);
+        else
+            AssertStmt(pThis->enmFormat != RTZIPTARFORMAT_GNU, rc = VERR_NOT_IMPLEMENTED);
+        if (RT_SUCCESS(rc))
+        {
+            /*
+             * Write the file bytes.
+             */
+            PRTZIPTARSPARSECHUNK const pLastChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
+            PRTZIPTARSPARSECHUNK pChunk;
+            RTListForEach(&pSparse->ChunkHead, pChunk, RTZIPTARSPARSECHUNK, Entry)
+            {
+                uint32_t cSpans = pChunk != pLastChunk || pSparse->iNextSpan == 0
+                                ? RT_ELEMENTS(pChunk->aSpans) : pSparse->iNextSpan;
+                for (uint32_t iSpan = 0; iSpan < cSpans; iSpan++)
+                {
+                    rc = RTVfsFileSeek(hVfsFile, pChunk->aSpans[iSpan].off, RTFILE_SEEK_BEGIN, NULL);
+                    if (RT_FAILURE(rc))
+                        break;
+                    uint64_t cbLeft = pChunk->aSpans[iSpan].cb;
+                    Assert(   !(cbLeft & (RTZIPTAR_BLOCKSIZE - 1))
+                           || (iSpan + 1 == cSpans && pChunk == pLastChunk));
+                    while (cbLeft > 0)
+                    {
+                        size_t cbToRead = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
+                        rc = RTVfsFileRead(hVfsFile, pbBuf, cbToRead, NULL);
+                        if (RT_SUCCESS(rc))
+                        {
+                            rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbToRead, true /*fBlocking*/, NULL);
+                            if (RT_SUCCESS(rc))
+                            {
+                                pThis->cbWritten += cbToRead;
+                                cbLeft           -= cbToRead;
+                                continue;
+                            }
+                        }
+                        break;
+                    }
+                    if (RT_FAILURE(rc))
+                        break;
+                }
+            }
+
+            /*
+             * Do the zero padding.
+             */
+            if (   RT_SUCCESS(rc)
+                && (pSparse->cbDataSpans & (RTZIPTAR_BLOCKSIZE - 1)))
+            {
+                size_t cbToZero = RTZIPTAR_BLOCKSIZE - (pSparse->cbDataSpans & (RTZIPTAR_BLOCKSIZE - 1));
+                rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, cbToZero, true /*fBlocking*/, NULL);
+                if (RT_SUCCESS(rc))
+                    pThis->cbWritten += cbToZero;
+            }
+        }
+
+        if (RT_FAILURE(rc))
+            pThis->rcFatal = rc;
+        rtZipTarFssWriter_SparseInfoDestroy(pSparse);
+    }
+    RTMemTmpFree(pvBufFree);
+    return rc;
+}
+
+
+/**
+ * Adds an I/O stream of indeterminate length to the TAR file.
+ *
+ * This requires the output to be seekable, i.e. a file, because we need to go
+ * back and update @c size field of the TAR header after pumping all the data
+ * bytes thru and establishing the file length.
  *
  * @returns IPRT status code.
@@ -288,12 +831,13 @@
  * @param   pszPath         The path to the file.
  * @param   hVfsIos         The I/O stream of the file.
- * @param   fFlags          The RTVFSFSSTREAMOPS::pfnAdd flags.
  * @param   pObjInfo        The object information.
  * @param   pszOwnerNm      The owner name.
  * @param   pszGroupNm      The group name.
  */
-static int rtZipTarFssWriter_AddFile(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos, uint32_t fFlags,
-                                     PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
-{
+static int rtZipTarFssWriter_AddFileStream(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos,
+                                           PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
+{
+    AssertReturn(pThis->hVfsFile != NIL_RTVFSFILE, VERR_NOT_A_FILE);
+
     /*
      * Append the header.
@@ -302,60 +846,21 @@
     if (RT_SUCCESS(rc))
     {
-        RTFOFF const offHdr = fFlags & RTVFSFSSTRM_ADD_F_STREAM ? RTVfsIoStrmTell(pThis->hVfsIos) : RTFOFF_MAX;
-        rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
-        if (RT_SUCCESS(rc))
-        {
-            pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
-
-            /*
-             * Allocate a tmporary buffer
-             */
-            uint8_t *pbFree;
-            size_t   cbBuf = _512K;
-            uint8_t *pbBuf = pbFree = (uint8_t *)RTMemTmpAlloc(cbBuf);
-            if (!pbBuf)
+        RTFOFF const offHdr = RTVfsFileTell(pThis->hVfsFile);
+        if (offHdr >= 0)
+        {
+            rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
+            if (RT_SUCCESS(rc))
             {
-                cbBuf = _32K;
-                pbBuf = pbFree = (uint8_t *)RTMemTmpAlloc(cbBuf);
-                if (!pbBuf)
-                {
-                    cbBuf = sizeof(pThis->aHdrs) - sizeof(pThis->aHdrs[0]);
-                    pbBuf = (uint8_t *)&pThis->aHdrs[1];
-                }
-            }
-
-            /*
-             * Copy the bytes.  Padding the last buffer to a multiple of 512.
-             */
-            if (!(fFlags & RTVFSFSSTRM_ADD_F_STREAM))
-            {
-                uint64_t cbLeft = pObjInfo->cbObject;
-                while (cbLeft > 0)
-                {
-                    size_t cbRead = cbLeft > cbBuf ? cbBuf : (size_t)cbBuf;
-                    rc = RTVfsIoStrmRead(hVfsIos, pbBuf, cbRead, true /*fBlocking*/, NULL);
-                    if (RT_FAILURE(rc))
-                        break;
-
-                    size_t cbToWrite = cbRead;
-                    if (cbRead & (sizeof(RTZIPTARHDR) - 1))
-                    {
-                        size_t cbToZero = sizeof(RTZIPTARHDR) - (cbRead & (sizeof(RTZIPTARHDR) - 1));
-                        memset(&pbBuf[cbRead], 0, cbToZero);
-                        cbToWrite += cbToZero;
-                    }
-
-                    rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbToWrite, true /*fBlocking*/, NULL);
-                    if (RT_FAILURE(rc))
-                        break;
-                    pThis->cbWritten += cbToWrite;
-                    cbLeft -= cbRead;
-                }
-            }
-            /*
-             * Same as above, only here we don't know the exact input length.
-             */
-            else
-            {
+                pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
+
+                /*
+                 * Transfer the bytes.
+                 */
+                void    *pvBufFree;
+                size_t   cbBuf;
+                uint8_t *pbBuf = rtZipTarFssWrite_AllocBuf(pThis, &cbBuf, &pvBufFree,
+                                                           pObjInfo->cbObject > 0 && pObjInfo->cbObject != RTFOFF_MAX
+                                                           ? pObjInfo->cbObject : _1G);
+
                 uint64_t cbReadTotal = 0;
                 for (;;)
@@ -378,8 +883,12 @@
                 }
 
-                /* Do the zero padding. */
-                if ((cbReadTotal & (sizeof(RTZIPTARHDR) - 1)) && RT_SUCCESS(rc))
+                RTMemTmpFree(pvBufFree);
+
+                /*
+                 * Do the zero padding.
+                 */
+                if ((cbReadTotal & (RTZIPTAR_BLOCKSIZE - 1)) && RT_SUCCESS(rc))
                 {
-                    size_t cbToZero = sizeof(RTZIPTARHDR) - (cbReadTotal & (sizeof(RTZIPTARHDR) - 1));
+                    size_t cbToZero = RTZIPTAR_BLOCKSIZE - (cbReadTotal & (RTZIPTAR_BLOCKSIZE - 1));
                     rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, cbToZero, true /*fBlocking*/, NULL);
                     if (RT_SUCCESS(rc))
@@ -391,32 +900,93 @@
                  * from before the data pumping above and just update the size.
                  */
-                if (RT_SUCCESS(rc) && (RTFOFF)cbReadTotal != pObjInfo->cbObject)
+                if ((RTFOFF)cbReadTotal != pObjInfo->cbObject && RT_SUCCESS(rc))
                 {
-                    RTVFSFILE hVfsFile = RTVfsIoStrmToFile(pThis->hVfsIos);
-                    if (hVfsFile != NIL_RTVFSFILE)
+                    RTFOFF const offRestore = RTVfsFileTell(pThis->hVfsFile);
+                    if (offRestore >= 0)
                     {
-                        RTFOFF offRestore = RTVfsFileTell(hVfsFile);
-                        if (offRestore >= 0)
+                        rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Common.size, cbReadTotal);
+                        if (RT_SUCCESS(rc))
+                            rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
+                        if (RT_SUCCESS(rc))
                         {
-                            rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Common.size, cbReadTotal);
+                            rc = RTVfsFileWriteAt(pThis->hVfsFile, offHdr, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), NULL);
                             if (RT_SUCCESS(rc))
-                                rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
-                            if (RT_SUCCESS(rc))
-                            {
-                                rc = RTVfsFileWriteAt(hVfsFile, offHdr, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), NULL);
-                                if (RT_SUCCESS(rc))
-                                    rc = RTVfsFileSeek(hVfsFile, offRestore, RTFILE_SEEK_BEGIN, NULL);
-                            }
+                                rc = RTVfsFileSeek(pThis->hVfsFile, offRestore, RTFILE_SEEK_BEGIN, NULL);
                         }
-                        else
-                            rc = (int)offRestore;
-                        RTVfsFileRelease(hVfsFile);
                     }
                     else
-                        AssertFailedStmt(rc = VERR_NOT_A_FILE);
+                        rc = (int)offRestore;
                 }
+
+                if (RT_SUCCESS(rc))
+                    return VINF_SUCCESS;
             }
-
-            RTMemTmpFree(pbFree);
+        }
+        else
+            rc = (int)offHdr;
+        pThis->rcFatal = rc;
+    }
+    return rc;
+}
+
+
+/**
+ * Adds a file to the stream.
+ *
+ * @returns IPRT status code.
+ * @param   pThis           The TAR writer instance.
+ * @param   pszPath         The path to the file.
+ * @param   hVfsIos         The I/O stream of the file.
+ * @param   fFlags          The RTVFSFSSTREAMOPS::pfnAdd flags.
+ * @param   pObjInfo        The object information.
+ * @param   pszOwnerNm      The owner name.
+ * @param   pszGroupNm      The group name.
+ */
+static int rtZipTarFssWriter_AddFile(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos,
+                                     PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
+{
+    /*
+     * Append the header.
+     */
+    int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
+    if (RT_SUCCESS(rc))
+    {
+        rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
+        if (RT_SUCCESS(rc))
+        {
+            pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
+
+            /*
+             * Copy the bytes.  Padding the last buffer to a multiple of 512.
+             */
+            void    *pvBufFree;
+            size_t   cbBuf;
+            uint8_t *pbBuf = rtZipTarFssWrite_AllocBuf(pThis, &cbBuf, &pvBufFree, pObjInfo->cbObject);
+
+            uint64_t cbLeft = pObjInfo->cbObject;
+            while (cbLeft > 0)
+            {
+                size_t cbRead = cbLeft > cbBuf ? cbBuf : (size_t)cbLeft;
+                rc = RTVfsIoStrmRead(hVfsIos, pbBuf, cbRead, true /*fBlocking*/, NULL);
+                if (RT_FAILURE(rc))
+                    break;
+
+                size_t cbToWrite = cbRead;
+                if (cbRead & (RTZIPTAR_BLOCKSIZE - 1))
+                {
+                    size_t cbToZero = RTZIPTAR_BLOCKSIZE - (cbRead & (RTZIPTAR_BLOCKSIZE - 1));
+                    memset(&pbBuf[cbRead], 0, cbToZero);
+                    cbToWrite += cbToZero;
+                }
+
+                rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbToWrite, true /*fBlocking*/, NULL);
+                if (RT_FAILURE(rc))
+                    break;
+                pThis->cbWritten += cbToWrite;
+                cbLeft -= cbRead;
+            }
+
+            RTMemTmpFree(pvBufFree);
+
             if (RT_SUCCESS(rc))
                 return VINF_SUCCESS;
@@ -533,4 +1103,10 @@
     pThis->hVfsIos = NIL_RTVFSIOSTREAM;
 
+    if (pThis->hVfsFile != NIL_RTVFSFILE)
+    {
+        RTVfsFileRelease(pThis->hVfsFile);
+        pThis->hVfsFile = NIL_RTVFSFILE;
+    }
+
     return VINF_SUCCESS;
 }
@@ -580,5 +1156,6 @@
 
     /*
-     * Do type specific handling.
+     * Do type specific handling.  File have several options and variations to
+     * take into account, thus the mess.
      */
     if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
@@ -586,6 +1163,25 @@
         RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
         AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_WRONG_TYPE);
-        rc = rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, fFlags, &ObjInfo,
-                                       ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
+
+        if (fFlags & RTVFSFSSTRM_ADD_F_STREAM)
+            rc = rtZipTarFssWriter_AddFileStream(pThis, pszPath, hVfsIos, &ObjInfo,
+                                                 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
+        else if (   !(pThis->fFlags & RTZIPTAR_C_SPARSE)
+                 || ObjInfo.cbObject < RTZIPTAR_MIN_SPARSE)
+            rc = rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, &ObjInfo,
+                                           ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
+        else
+        {
+            RTVFSFILE hVfsFile = RTVfsObjToFile(hVfsObj);
+            if (hVfsFile != NIL_RTVFSFILE)
+            {
+                rc = rtZipTarFssWriter_AddFileSparse(pThis, pszPath, hVfsFile, hVfsIos, &ObjInfo,
+                                                     ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
+                RTVfsFileRelease(hVfsFile);
+            }
+            else
+                rc = rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, &ObjInfo,
+                                               ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
+        }
         RTVfsIoStrmRelease(hVfsIos);
     }
@@ -612,10 +1208,27 @@
 {
     PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
-    if (RT_SUCCESS(pThis->rcFatal))
-    {
-        /** @todo investigate zero end records. */
-        return VINF_SUCCESS;
-    }
-    return pThis->rcFatal;
+    int rc = pThis->rcFatal;
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * There are supposed to be two zero headers at the end of the archive.
+         * GNU tar may write more because of the way it does buffering,
+         * libarchive OTOH writes exactly two.
+         */
+        rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, RTZIPTAR_BLOCKSIZE * 2, true /*fBlocking*/, NULL);
+        if (RT_SUCCESS(rc))
+        {
+            pThis->cbWritten += RTZIPTAR_BLOCKSIZE * 2;
+
+            /*
+             * Flush the output.
+             */
+            rc = RTVfsIoStrmFlush(pThis->hVfsIos);
+            if (RT_SUCCESS(rc))
+                return rc;
+        }
+        pThis->rcFatal = rc;
+    }
+    return rc;
 }
 
@@ -657,5 +1270,7 @@
     if (enmFormat == RTZIPTARFORMAT_DEFAULT)
         enmFormat = RTZIPTARFORMAT_GNU;
-    AssertReturn(enmFormat == RTZIPTARFORMAT_GNU, VERR_NOT_IMPLEMENTED); /* Only implementing GNU output at the moment. */
+    AssertReturn(   enmFormat == RTZIPTARFORMAT_GNU
+                 || enmFormat == RTZIPTARFORMAT_USTAR
+                 , VERR_NOT_IMPLEMENTED); /* Only implementing GNU and USTAR output at the moment. */
 
     uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosOut);
@@ -672,5 +1287,8 @@
     {
         pThis->hVfsIos   = hVfsIosOut;
+        pThis->hVfsFile  = RTVfsIoStrmToFile(hVfsIosOut);
+
         pThis->enmFormat = enmFormat;
+        pThis->fFlags    = fFlags;
         pThis->rcFatal   = VINF_SUCCESS;
 
