VirtualBox

Changeset 67134 in vbox for trunk/src/VBox


Ignore:
Timestamp:
May 29, 2017 5:52:28 PM (8 years ago)
Author:
vboxsync
Message:

IPRT: Got the new tar writer working and did basic RTZipTarCmd integration.

Location:
trunk/src/VBox/Runtime/common/zip
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/zip/tar.h

    r67123 r67134  
    8989
    9090/** The uniform standard tape archive format magic value. */
    91 #define RTZIPTAR_USTAR_MAGIC    "ustar"
     91#define RTZIPTAR_USTAR_MAGIC        "ustar"
    9292/** The ustar version string.
    9393 * @remarks The terminator character is not part of the field.  */
    94 #define RTZIPTAR_USTAR_VERSION  "00"
     94#define RTZIPTAR_USTAR_VERSION      "00"
    9595
    9696/** The GNU magic + version value. */
    97 #define RTZIPTAR_USTAR_GNU_MAGIC    "ustar  "
     97#define RTZIPTAR_GNU_MAGIC          "ustar  "
    9898
    9999
     
    140140
    141141/**
    142  * GNU sparse data segment descriptor entry.
     142 * GNU sparse data segment descriptor.
    143143 */
    144144typedef struct RTZIPTARGNUSPARSE
     
    150150AssertCompileMemberOffset(RTZIPTARGNUSPARSE, offset,    0);
    151151AssertCompileMemberOffset(RTZIPTARGNUSPARSE, numbytes,  12);
     152/** Pointer to a GNU sparse data segment descriptor. */
     153typedef RTZIPTARGNUSPARSE *PRTZIPTARGNUSPARSE;
     154/** Pointer to a const GNU sparse data segment descriptor. */
     155typedef RTZIPTARGNUSPARSE *PCRTZIPTARGNUSPARSE;
    152156
    153157/**
     
    172176    char    atime[12];
    173177    char    ctime[12];
    174     char    offset[12];
    175     char    longnames[4];
     178    char    offset[12];         /**< for multi-volume? */
     179    char    longnames[4];       /**< Seems to be unused. */
    176180    char    unused[1];
    177181    RTZIPTARGNUSPARSE sparse[4];
     
    284288    char                ab[512];
    285289    /** The standard header. */
    286     RTZIPTARHDRANCIENT  Ancient;
     290    RTZIPTARHDRANCIENT      Ancient;
    287291    /** The standard header. */
    288     RTZIPTARHDRPOSIX    Posix;
     292    RTZIPTARHDRPOSIX        Posix;
    289293    /** The GNU header. */
    290     RTZIPTARHDRGNU      Gnu;
     294    RTZIPTARHDRGNU          Gnu;
    291295    /** The bits common to both GNU and the standard header. */
    292     RTZIPTARHDRCOMMON   Common;
     296    RTZIPTARHDRCOMMON       Common;
     297    /** GNU sparse header. */
     298    RTZIPTARHDRGNUSPARSE    GnuSparse;
    293299} RTZIPTARHDR;
    294300AssertCompileSize(RTZIPTARHDR, 512);
  • trunk/src/VBox/Runtime/common/zip/tarcmd.cpp

    r67124 r67134  
    139139     * Terminated by a NULL entry. */
    140140    const char * const *papszFiles;
     141
     142    /** The TAR format to create. */
     143    RTZIPTARFORMAT  enmTarFormat;
     144    /** TAR creation flags. */
     145    uint32_t        fTarCreate;
     146
    141147} RTZIPTARCMDOPS;
    142148/** Pointer to the IPRT tar options. */
     
    174180        }
    175181    return false;
     182}
     183
     184
     185/**
     186 * Archives a file.
     187 *
     188 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
     189 * @param   pOpts           The options.
     190 * @param   hVfsFss         The TAR filesystem stream handle.
     191 * @param   pszSrc          The file path or VFS spec.
     192 * @param   pszDst          The name to archive the file under.
     193 * @param   pErrInfo        Error info buffer (saves stack space).
     194 */
     195static RTEXITCODE rtZipTarCmdArchiveFile(PRTZIPTARCMDOPS pOpts, RTVFSFSSTREAM hVfsFss,
     196                                         const char *pszSrc, const char *pszDst, PRTERRINFOSTATIC pErrInfo)
     197{
     198    if (pOpts->fVerbose)
     199        RTPrintf("%s\n", pszDst);
     200
     201    /* Open the file. */
     202    uint32_t        offError;
     203    RTVFSIOSTREAM   hVfsIos;
     204    int rc = RTVfsChainOpenIoStream(pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
     205                                    &hVfsIos, &offError, RTErrInfoInitStatic(pErrInfo));
     206    if (RT_FAILURE(rc))
     207        return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenIoStream", pszSrc, rc, offError, &pErrInfo->Core);
     208
     209    /* I/O stream to base object. */
     210    RTVFSOBJ hVfsObj = RTVfsObjFromIoStream(hVfsIos);
     211    RTVfsIoStrmRelease(hVfsIos);
     212    if (hVfsObj == NIL_RTVFSOBJ)
     213        return RTMsgErrorExitFailure("RTVfsObjFromIoStream failed unexpectedly!");
     214
     215    /* Add it to the stream. */
     216    rc = RTVfsFsStrmAdd(hVfsFss, pszDst, hVfsObj, 0 /*fFlags*/);
     217    RTVfsObjRelease(hVfsObj);
     218
     219    if (RT_SUCCESS(rc))
     220    {
     221        if (rc != VINF_SUCCESS)
     222            RTMsgWarning("%Rrc adding '%s'", rc, pszDst);
     223        return RTEXITCODE_SUCCESS;
     224    }
     225    return RTMsgErrorExitFailure("%Rrc adding '%s'", rc, pszDst);
     226}
     227
     228
     229/**
     230 * Archives a directory recursively .
     231 *
     232 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
     233 * @param   pOpts           The options.
     234 * @param   hVfsFss         The TAR filesystem stream handle.
     235 * @param   pszSrc          The directory path or VFS spec.  We append to the
     236 *                          buffer as we decend.
     237 * @param   cchSrc          The length of the input.
     238 * @param   pszDst          The name to archive it the under.  We append to the
     239 *                          buffer as we decend.
     240 * @param   cchDst          The length of the input.
     241 * @param   pErrInfo        Error info buffer (saves stack space).
     242 */
     243static RTEXITCODE rtZipTarCmdArchiveDir(PRTZIPTARCMDOPS pOpts, RTVFSFSSTREAM hVfsFss, char pszSrc[RTPATH_MAX], size_t cchSrc,
     244                                        char pszDst[RTPATH_MAX], size_t cchDst, PRTERRINFOSTATIC pErrInfo)
     245{
     246    RT_NOREF(pOpts, hVfsFss, pszSrc, cchSrc, pszDst, cchDst, pErrInfo);
     247    return RTMsgErrorExitFailure("Adding directories has not yet been implemented! Sorry.");
     248}
     249
     250
     251
     252/**
     253 * Opens the output archive specified by the options.
     254 *
     255 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
     256 * @param   pOpts           The options.
     257 * @param   phVfsFss        Where to return the TAR filesystem stream handle.
     258 */
     259static RTEXITCODE rtZipTarCmdOpenOutputArchive(PRTZIPTARCMDOPS pOpts, PRTVFSFSSTREAM phVfsFss)
     260{
     261    int rc;
     262
     263    /*
     264     * Open the output file.
     265     */
     266    RTVFSIOSTREAM   hVfsIos;
     267    if (   pOpts->pszFile
     268        && strcmp(pOpts->pszFile, "-") != 0)
     269    {
     270        uint32_t        offError = 0;
     271        RTERRINFOSTATIC ErrInfo;
     272        rc = RTVfsChainOpenIoStream(pOpts->pszFile, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
     273                                    &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
     274        if (RT_FAILURE(rc))
     275            return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenIoStream", pOpts->pszFile, rc, offError, &ErrInfo.Core);
     276    }
     277    else
     278    {
     279        rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT,
     280                                      RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
     281                                      true /*fLeaveOpen*/,
     282                                      &hVfsIos);
     283        if (RT_FAILURE(rc))
     284            return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to prepare standard output for writing: %Rrc", rc);
     285    }
     286
     287    /*
     288     * Pass it thru a compressor?
     289     */
     290    RTVFSIOSTREAM hVfsIosComp = NIL_RTVFSIOSTREAM;
     291    switch (pOpts->chZipper)
     292    {
     293        /* no */
     294        case '\0':
     295            rc = VINF_SUCCESS;
     296            break;
     297
     298        /* gunzip */
     299        case 'z':
     300            rc = RTZipGzipCompressIoStream(hVfsIos, 0 /*fFlags*/, 6, &hVfsIosComp);
     301            if (RT_FAILURE(rc))
     302                RTMsgError("Failed to open gzip decompressor: %Rrc", rc);
     303            break;
     304
     305        /* bunzip2 */
     306        case 'j':
     307            rc = VERR_NOT_SUPPORTED;
     308            RTMsgError("bzip2 is not supported by this build");
     309            break;
     310
     311        /* bug */
     312        default:
     313            rc = VERR_INTERNAL_ERROR_2;
     314            RTMsgError("unknown decompression method '%c'",  pOpts->chZipper);
     315            break;
     316    }
     317    if (RT_FAILURE(rc))
     318    {
     319        RTVfsIoStrmRelease(hVfsIos);
     320        return RTEXITCODE_FAILURE;
     321    }
     322
     323    if (hVfsIosComp != NIL_RTVFSIOSTREAM)
     324    {
     325        RTVfsIoStrmRelease(hVfsIos);
     326        hVfsIos = hVfsIosComp;
     327        hVfsIosComp = NIL_RTVFSIOSTREAM;
     328    }
     329
     330    /*
     331     * Open the filesystem stream creator.
     332     */
     333    if (   pOpts->enmFormat == RTZIPTARCMDFORMAT_TAR
     334        || pOpts->enmFormat == RTZIPTARCMDFORMAT_AUTO_DEFAULT)
     335        rc = RTZipTarFsStreamToIoStream(hVfsIos, pOpts->enmTarFormat, pOpts->fTarCreate, phVfsFss);
     336    else
     337        rc = VERR_NOT_SUPPORTED;
     338    RTVfsIoStrmRelease(hVfsIos);
     339    if (RT_FAILURE(rc))
     340        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open tar filesystem stream: %Rrc", rc);
     341
     342    return RTEXITCODE_SUCCESS;
     343}
     344
     345
     346/**
     347 * Implements archive creation.
     348 *
     349 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + printed message.
     350 * @param   pOpts           The options.
     351 */
     352static RTEXITCODE rtZipTarCreate(PRTZIPTARCMDOPS pOpts)
     353{
     354    /*
     355     * Refuse to create empty archive.
     356     */
     357    if (pOpts->cFiles == 0)
     358        return RTMsgErrorExitFailure("Nothing to archive - refusing to create empty archive!");
     359
     360    /*
     361     * First open the output file.
     362     */
     363    RTVFSFSSTREAM hVfsFss;
     364    RTEXITCODE rcExit = rtZipTarCmdOpenOutputArchive(pOpts, &hVfsFss);
     365    if (rcExit != RTEXITCODE_SUCCESS)
     366        return rcExit;
     367
     368    /*
     369     * Process the input files.
     370     */
     371    for (uint32_t iFile = 0; iFile < pOpts->cFiles; iFile++)
     372    {
     373        const char *pszFile = pOpts->papszFiles[iFile];
     374
     375        /*
     376         * Construct/copy the source name.
     377         */
     378        int         rc = VINF_SUCCESS;
     379        char        szSrc[RTPATH_MAX];
     380        if (   RTPathStartsWithRoot(pszFile)
     381            || RTVfsChainIsSpec(pszFile))
     382            rc = RTStrCopy(szSrc, sizeof(szSrc), pszFile);
     383        else
     384            rc = RTPathJoin(szSrc, sizeof(szSrc), pOpts->pszDirectory ? pOpts->pszDirectory : ".", pOpts->papszFiles[iFile]);
     385        if (RT_SUCCESS(rc))
     386        {
     387            /*
     388             * Construct the archived name.  We must strip leading root specifier.
     389             */
     390            char *pszFinalPath = NULL;
     391            char  szDst[RTPATH_MAX];
     392            const char *pszDst = pszFile;
     393            if (RTVfsChainIsSpec(pszFile))
     394            {
     395                uint32_t offError;
     396                rc = RTVfsChainQueryFinalPath(pszFile, &pszFinalPath, &offError);
     397                if (RT_SUCCESS(rc))
     398                    pszDst = pszFinalPath;
     399                else
     400                    rcExit = RTVfsChainMsgErrorExitFailure("RTVfsChainQueryFinalPath", pszFile, rc, offError, NULL);
     401            }
     402            if (RT_SUCCESS(rc))
     403            {
     404                pszDst = RTPathSkipRootSpec(pszDst);
     405                if (*pszDst == '\0')
     406                {
     407                    pszDst = pOpts->pszPrefix ? pOpts->pszPrefix : ".";
     408                    rc = RTStrCopy(szDst, sizeof(szDst), pszDst);
     409                }
     410                else
     411                {
     412                    if (pOpts->pszPrefix)
     413                        rc = RTPathJoin(szDst, sizeof(szDst), pOpts->pszPrefix, pszDst);
     414                    else
     415                        rc = RTStrCopy(szDst, sizeof(szDst), pszDst);
     416                }
     417                if (RT_SUCCESS(rc))
     418                {
     419                    /*
     420                     * What kind of object is this?
     421                     */
     422                    RTERRINFOSTATIC ErrInfo;
     423                    uint32_t        offError;
     424                    RTFSOBJINFO     ObjInfo;
     425                    rc = RTVfsChainQueryInfo(szSrc, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK,
     426                                             &offError, RTErrInfoInitStatic(&ErrInfo));
     427                    if (RT_SUCCESS(rc))
     428                    {
     429                        RTEXITCODE rcExit2;
     430                        if (RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
     431                            rcExit2 = rtZipTarCmdArchiveDir(pOpts, hVfsFss, szSrc, strlen(szSrc), szDst, strlen(szDst), &ErrInfo);
     432                        else
     433                            rcExit2 = rtZipTarCmdArchiveFile(pOpts, hVfsFss, szSrc, szDst, &ErrInfo);
     434                    }
     435                    else
     436                        rcExit = RTVfsChainMsgErrorExitFailure("RTVfsChainQueryInfo", pszFile, rc, offError, &ErrInfo.Core);
     437                }
     438                else
     439                    rcExit = RTMsgErrorExitFailure("archived file name is too long, skipping '%s' (%s)", pszDst, pszFile);
     440            }
     441        }
     442        else
     443            rcExit = RTMsgErrorExitFailure("input file name is too long, skipping '%s'",  pszFile);
     444    }
     445
     446    /*
     447     * Finalize the archive.
     448     */
     449    int rc = RTVfsFsStrmEnd(hVfsFss);
     450    if (RT_FAILURE(rc))
     451        rcExit = RTMsgErrorExitFailure("RTVfsFsStrmEnd failed: %Rrc", rc);
     452
     453    RTVfsFsStrmRelease(hVfsFss);
     454    return rcExit;
    176455}
    177456
     
    9081187             );
    9091188    RTPrintf("Basic Options:\n"
    910              "    -C <dir>, --directory <dir>           (-A, -C, -d, -r, -u, -x)\n"
     1189             "    -C <dir>, --directory <dir>           (-A, -c, -d, -r, -u, -x)\n"
    9111190             "        Sets the base directory for input and output file members.\n"
    9121191             "        This does not apply to --file, even if it preceeds it.\n"
     
    9251204             "\n");
    9261205    RTPrintf("Misc Options:\n"
    927              "    --owner <uid/username>                (-A, -C, -d, -r, -u, -x)\n"
     1206             "    --owner <uid/username>                (-A, -c, -d, -r, -u, -x)\n"
    9281207             "        Set the owner of extracted and archived files to the user specified.\n"
    929              "    --group <uid/username>                (-A, -C, -d, -r, -u, -x)\n"
     1208             "    --group <uid/username>                (-A, -c, -d, -r, -u, -x)\n"
    9301209             "        Set the group of extracted and archived files to the group specified.\n"
    9311210             "    --utc                                 (-t)\n"
    9321211             "        Display timestamps as UTC instead of local time.\n"
     1212             "    -S, --sparse                          (-A, -c, -u)\n"
     1213             "        Detect sparse files and store them (gnu tar extension).\n"
     1214             "    --format <format>                     (-A, -c, -u, but also -d, -r, -x)\n"
     1215             "        The file format:\n"
     1216             "                  auto (gnu tar)\n"
     1217             "                  default (gnu tar)\n"
     1218             "                  tar (gnu tar)"
     1219             "                  gnu (tar v1.13+), "
     1220             "                  ustar (tar POSIX.1-1988), "
     1221             "                  pax (tar POSIX.1-2001),\n"
     1222             "                  xar\n"
     1223             "        Note! Because XAR/TAR detection isn't implemented yet, it\n"
     1224             "              is necessary to specifcy --format=xar when reading a\n"
     1225             "              XAR file.  Otherwise this option is only for creation.\n"
    9331226             "\n");
    9341227    RTPrintf("IPRT Options:\n"
    935              "    --prefix <dir-prefix>                 (-A, -C, -d, -r, -u)\n"
     1228             "    --prefix <dir-prefix>                 (-A, -c, -d, -r, -u)\n"
    9361229             "        Directory prefix to give the members added to the archive.\n"
    937              "    --file-mode-and-mask <octal-mode>     (-A, -C, -d, -r, -u, -x)\n"
     1230             "    --file-mode-and-mask <octal-mode>     (-A, -c, -d, -r, -u, -x)\n"
    9381231             "        Restrict the access mode of regular and special files.\n"
    939              "    --file-mode-or-mask <octal-mode>     (-A, -C, -d, -r, -u, -x)\n"
     1232             "    --file-mode-or-mask <octal-mode>      (-A, -c, -d, -r, -u, -x)\n"
    9401233             "        Include the given access mode for regular and special files.\n"
    941              "    --dir-mode-and-mask <octal-mode>      (-A, -C, -d, -r, -u, -x)\n"
     1234             "    --dir-mode-and-mask <octal-mode>      (-A, -c, -d, -r, -u, -x)\n"
    9421235             "        Restrict the access mode of directories.\n"
    943              "    --dir-mode-or-mask <octal-mode>      (-A, -C, -d, -r, -u, -x)\n"
     1236             "    --dir-mode-or-mask <octal-mode>       (-A, -c, -d, -r, -u, -x)\n"
    9441237             "        Include the given access mode for directories.\n"
    9451238             "    --read-ahead                          (-x)\n"
     
    9661259    {
    9671260        /* operations */
    968         { "--concatenate",          'A', RTGETOPT_REQ_NOTHING },
    969         { "--catenate",             'A', RTGETOPT_REQ_NOTHING },
    970         { "--create",               'c', RTGETOPT_REQ_NOTHING },
    971         { "--diff",                 'd', RTGETOPT_REQ_NOTHING },
    972         { "--compare",              'd', RTGETOPT_REQ_NOTHING },
    973         { "--append",               'r', RTGETOPT_REQ_NOTHING },
    974         { "--list",                 't', RTGETOPT_REQ_NOTHING },
    975         { "--update",               'u', RTGETOPT_REQ_NOTHING },
    976         { "--extract",              'x', RTGETOPT_REQ_NOTHING },
    977         { "--get",                  'x', RTGETOPT_REQ_NOTHING },
    978         { "--delete",       RTZIPTARCMD_OPT_DELETE, RTGETOPT_REQ_NOTHING },
     1261        { "--concatenate",          'A',                                RTGETOPT_REQ_NOTHING },
     1262        { "--catenate",             'A',                                RTGETOPT_REQ_NOTHING },
     1263        { "--create",               'c',                                RTGETOPT_REQ_NOTHING },
     1264        { "--diff",                 'd',                                RTGETOPT_REQ_NOTHING },
     1265        { "--compare",              'd',                                RTGETOPT_REQ_NOTHING },
     1266        { "--append",               'r',                                RTGETOPT_REQ_NOTHING },
     1267        { "--list",                 't',                                RTGETOPT_REQ_NOTHING },
     1268        { "--update",               'u',                                RTGETOPT_REQ_NOTHING },
     1269        { "--extract",              'x',                                RTGETOPT_REQ_NOTHING },
     1270        { "--get",                  'x',                                RTGETOPT_REQ_NOTHING },
     1271        { "--delete",               RTZIPTARCMD_OPT_DELETE,            RTGETOPT_REQ_NOTHING },
    9791272
    9801273        /* basic options */
    981         { "--directory",            'C', RTGETOPT_REQ_STRING },
    982         { "--file",                 'f', RTGETOPT_REQ_STRING },
    983         { "--verbose",              'v', RTGETOPT_REQ_NOTHING },
    984         { "--preserve-permissions", 'p', RTGETOPT_REQ_NOTHING },
    985         { "--bzip2",                'j', RTGETOPT_REQ_NOTHING },
    986         { "--gzip",                 'z', RTGETOPT_REQ_NOTHING },
    987         { "--gunzip",               'z', RTGETOPT_REQ_NOTHING },
    988         { "--ungzip",               'z', RTGETOPT_REQ_NOTHING },
     1274        { "--directory",            'C',                                RTGETOPT_REQ_STRING },
     1275        { "--file",                 'f',                                RTGETOPT_REQ_STRING },
     1276        { "--verbose",              'v',                                RTGETOPT_REQ_NOTHING },
     1277        { "--preserve-permissions", 'p',                                RTGETOPT_REQ_NOTHING },
     1278        { "--bzip2",                'j',                                RTGETOPT_REQ_NOTHING },
     1279        { "--gzip",                 'z',                                RTGETOPT_REQ_NOTHING },
     1280        { "--gunzip",               'z',                                RTGETOPT_REQ_NOTHING },
     1281        { "--ungzip",               'z',                                RTGETOPT_REQ_NOTHING },
    9891282
    9901283        /* other options. */
    991         { "--owner",                RTZIPTARCMD_OPT_OWNER, RTGETOPT_REQ_STRING },
    992         { "--group",                RTZIPTARCMD_OPT_GROUP, RTGETOPT_REQ_STRING },
    993         { "--utc",                  RTZIPTARCMD_OPT_UTC,   RTGETOPT_REQ_NOTHING },
     1284        { "--owner",                RTZIPTARCMD_OPT_OWNER,              RTGETOPT_REQ_STRING },
     1285        { "--group",                RTZIPTARCMD_OPT_GROUP,              RTGETOPT_REQ_STRING },
     1286        { "--utc",                  RTZIPTARCMD_OPT_UTC,                RTGETOPT_REQ_NOTHING },
     1287        { "--sparse",               'S',                                RTGETOPT_REQ_NOTHING },
     1288        { "--format",               RTZIPTARCMD_OPT_FORMAT,             RTGETOPT_REQ_STRING },
    9941289
    9951290        /* IPRT extensions */
     
    9991294        { "--dir-mode-and-mask",    RTZIPTARCMD_OPT_DIR_MODE_AND_MASK,  RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
    10001295        { "--dir-mode-or-mask",     RTZIPTARCMD_OPT_DIR_MODE_OR_MASK,   RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
    1001         { "--format",               RTZIPTARCMD_OPT_FORMAT,             RTGETOPT_REQ_STRING },
    10021296        { "--read-ahead",           RTZIPTARCMD_OPT_READ_AHEAD,         RTGETOPT_REQ_NOTHING },
    10031297    };
     
    10251319    }
    10261320#endif
     1321    Opts.enmTarFormat = RTZIPTARFORMAT_DEFAULT;
     1322    Opts.fTarCreate   = 0;
    10271323
    10281324    RTGETOPTUNION   ValueUnion;
     
    11151411                break;
    11161412
     1413            /* GNU */
     1414            case 'S':
     1415                Opts.fTarCreate |= RTZIPTAR_C_SPARSE;
     1416                break;
     1417
    11171418            /* iprt extensions */
    11181419            case RTZIPTARCMD_OPT_PREFIX:
     
    11401441            case RTZIPTARCMD_OPT_FORMAT:
    11411442                if (!strcmp(ValueUnion.psz, "auto") || !strcmp(ValueUnion.psz, "default"))
    1142                     Opts.enmFormat = RTZIPTARCMDFORMAT_AUTO_DEFAULT;
     1443                {
     1444                    Opts.enmFormat    = RTZIPTARCMDFORMAT_AUTO_DEFAULT;
     1445                    Opts.enmTarFormat = RTZIPTARFORMAT_DEFAULT;
     1446                }
    11431447                else if (!strcmp(ValueUnion.psz, "tar"))
    1144                     Opts.enmFormat = RTZIPTARCMDFORMAT_TAR;
     1448                {
     1449                    Opts.enmFormat    = RTZIPTARCMDFORMAT_TAR;
     1450                    Opts.enmTarFormat = RTZIPTARFORMAT_DEFAULT;
     1451                }
     1452                else if (!strcmp(ValueUnion.psz, "gnu"))
     1453                {
     1454                    Opts.enmFormat    = RTZIPTARCMDFORMAT_TAR;
     1455                    Opts.enmTarFormat = RTZIPTARFORMAT_GNU;
     1456                }
     1457                else if (!strcmp(ValueUnion.psz, "ustar"))
     1458                {
     1459                    Opts.enmFormat    = RTZIPTARCMDFORMAT_TAR;
     1460                    Opts.enmTarFormat = RTZIPTARFORMAT_USTAR;
     1461                }
     1462                else if (   !strcmp(ValueUnion.psz, "posix")
     1463                         || !strcmp(ValueUnion.psz, "pax"))
     1464                {
     1465                    Opts.enmFormat    = RTZIPTARCMDFORMAT_TAR;
     1466                    Opts.enmTarFormat = RTZIPTARFORMAT_PAX;
     1467                }
    11451468                else if (!strcmp(ValueUnion.psz, "xar"))
    1146                     Opts.enmFormat = RTZIPTARCMDFORMAT_XAR;
     1469                    Opts.enmFormat    = RTZIPTARCMDFORMAT_XAR;
    11471470                else
    11481471                    return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown archive format: '%s'", ValueUnion.psz);
     
    12031526            return rtZipTarDoWithMembers(&Opts, rtZipTarCmdExtractCallback);
    12041527
     1528        case 'c':
     1529            return rtZipTarCreate(&Opts);
     1530
    12051531        case 'A':
    1206         case 'c':
    12071532        case 'd':
    12081533        case 'r':
  • trunk/src/VBox/Runtime/common/zip/tarvfswriter.cpp

    r67123 r67134  
    3232#include <iprt/zip.h>
    3333
     34#include <iprt/asm.h>
    3435#include <iprt/assert.h>
    3536#include <iprt/err.h>
     
    4647
    4748/*********************************************************************************************************************************
     49*   Defined Constants And Macros                                                                                                 *
     50*********************************************************************************************************************************/
     51/** The TAR block size we're using in this implementation.
     52 * @remarks Should technically be user configurable, but we don't currently need that. */
     53#define RTZIPTAR_BLOCKSIZE      sizeof(RTZIPTARHDR)
     54
     55/** Minimum file size we consider for sparse files. */
     56#define RTZIPTAR_MIN_SPARSE     _64K
     57
     58
     59/*********************************************************************************************************************************
    4860*   Structures and Typedefs                                                                                                      *
    4961*********************************************************************************************************************************/
    5062/**
     63 * A data span descriptor in a sparse file.
     64 */
     65typedef struct RTZIPTARSPARSESPAN
     66{
     67    /** Byte offset into the file of the data. */
     68    uint64_t        off;
     69    /** Number of bytes of data, rounded up to a multiple of blocksize. */
     70    uint64_t        cb;
     71} RTZIPTARSPARSESPAN;
     72/** Pointer to a data span. */
     73typedef RTZIPTARSPARSESPAN *PRTZIPTARSPARSESPAN;
     74/** Pointer to a const data span. */
     75typedef RTZIPTARSPARSESPAN const *PCRTZIPTARSPARSESPAN;
     76
     77/**
     78 * Chunk of TAR sparse file data spans.
     79 */
     80typedef struct RTZIPTARSPARSECHUNK
     81{
     82    /** List entry. */
     83    RTLISTNODE          Entry;
     84    /** Array of data spans. */
     85    RTZIPTARSPARSESPAN  aSpans[63];
     86} RTZIPTARSPARSECHUNK;
     87AssertCompile(sizeof(RTZIPTARSPARSECHUNK) <= 1024);
     88AssertCompile(sizeof(RTZIPTARSPARSECHUNK) >= 1008);
     89/** Pointer to a chunk of TAR data spans. */
     90typedef RTZIPTARSPARSECHUNK *PRTZIPTARSPARSECHUNK;
     91/** Pointer to a const chunk of TAR data spans. */
     92typedef RTZIPTARSPARSECHUNK const *PCRTZIPTARSPARSECHUNK;
     93
     94/**
     95 * TAR sparse file info.
     96 */
     97typedef struct RTZIPTARSPARSE
     98{
     99    /** Number of data bytes (real size).  */
     100    uint64_t            cbDataSpans;
     101    /** Number of data spans. */
     102    uint32_t            cDataSpans;
     103    /** The index of the next span in the tail chunk (to avoid modulus 63). */
     104    uint32_t            iNextSpan;
     105    /** Head of the data span chunk list (PRTZIPTARSPARSECHUNK). */
     106    RTLISTANCHOR        ChunkHead;
     107} RTZIPTARSPARSE;
     108/** Pointer to TAR sparse file info. */
     109typedef RTZIPTARSPARSE *PRTZIPTARSPARSE;
     110/** Pointer to a const TAR sparse file info. */
     111typedef RTZIPTARSPARSE const *PCRTZIPTARSPARSE;
     112
     113/**
    51114 * Tar filesystem stream private data.
    52115 */
     
    55118    /** The output I/O stream. */
    56119    RTVFSIOSTREAM           hVfsIos;
     120    /** Non-nil if the output is a file.  */
     121    RTVFSFILE               hVfsFile;
    57122
    58123    /** The TAR format. */
     
    75140
    76141
     142/*********************************************************************************************************************************
     143*   Internal Functions                                                                                                           *
     144*********************************************************************************************************************************/
     145static int rtZipTarFssWriter_AddFile(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos,
     146                                     PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm);
     147
     148
    77149/**
    78150 * Calculates the header checksum and stores it in the chksum field.
     
    87159
    88160    int rc = RTStrFormatU32(pHdr->Common.chksum, sizeof(pHdr->Common.chksum), iUnsignedChksum,
    89                             8 /*uBase*/, -1 /*cchWidth*/, sizeof(pHdr->Common.chksum) - 1, RTSTR_F_ZEROPAD);
     161                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pHdr->Common.chksum) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
    90162    AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
    91163    return VINF_SUCCESS;
     
    114186    if (off < _4G * 2U)
    115187    {
    116         int rc = RTStrFormatU64(pach12Field, 12, off, 8 /*uBase*/, -1 /*cchWidth*/, 12 - 1, RTSTR_F_ZEROPAD);
     188        int rc = RTStrFormatU64(pach12Field, 12, off, 8 /*uBase*/, -1 /*cchWidth*/, 12 - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
    117189        AssertRCReturn(rc, rc);
    118190    }
     
    127199     *       trouble as they are not picky about what they read.
    128200     */
     201    /** @todo above note is wrong:  GNU tar only uses base-256 with the GNU tar
     202     * format, i.e. "ustar   \0", see create.c line 303 in v1.29. */
    129203    else
    130204    {
     
    192266    int rc;
    193267    rc = RTStrFormatU32(pThis->aHdrs[0].Common.mode, sizeof(pThis->aHdrs[0].Common.mode), pObjInfo->Attr.fMode & RTFS_UNIX_MASK,
    194                         8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.mode) - 1, RTSTR_F_ZEROPAD);
     268                        8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.mode) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
    195269    AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
    196270
     
    201275    uint32_t uValue = pObjInfo->Attr.u.Unix.uid != NIL_RTUID ? pObjInfo->Attr.u.Unix.uid : 0;
    202276    rc = RTStrFormatU32(pThis->aHdrs[0].Common.uid, sizeof(pThis->aHdrs[0].Common.uid), uValue,
    203                         8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.uid) - 1, RTSTR_F_ZEROPAD);
     277                        8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.uid) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
    204278    AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
    205279
    206280    uValue = pObjInfo->Attr.u.Unix.gid != NIL_RTUID ? pObjInfo->Attr.u.Unix.gid : 0;
    207281    rc = RTStrFormatU32(pThis->aHdrs[0].Common.gid, sizeof(pThis->aHdrs[0].Common.gid), uValue,
    208                         8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.gid) - 1, RTSTR_F_ZEROPAD);
     282                        8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.gid) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
    209283    AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
    210284
     
    220294    rc = RTStrFormatU64(pThis->aHdrs[0].Common.mtime, sizeof(pThis->aHdrs[0].Common.mtime),
    221295                        RTTimeSpecGetSeconds(&pObjInfo->ModificationTime),
    222                         8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.mtime) - 1, RTSTR_F_ZEROPAD);
     296                        8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.mtime) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
    223297    AssertRCReturn(rc, rc);
    224298
     
    247321     * Set TAR record magic and version.
    248322     */
    249     memcpy(pThis->aHdrs[0].Common.magic, RT_STR_TUPLE("ustar "));
    250     pThis->aHdrs[0].Common.version[0] = ' ';
    251     pThis->aHdrs[0].Common.version[1] = ' ';
     323    if (pThis->enmFormat == RTZIPTARFORMAT_GNU)
     324        memcpy(pThis->aHdrs[0].Gnu.magic, RTZIPTAR_GNU_MAGIC, sizeof(pThis->aHdrs[0].Gnu.magic));
     325    else if (   pThis->enmFormat == RTZIPTARFORMAT_USTAR
     326             || pThis->enmFormat == RTZIPTARFORMAT_PAX)
     327    {
     328        memcpy(pThis->aHdrs[0].Common.magic, RTZIPTAR_USTAR_MAGIC, sizeof(pThis->aHdrs[0].Common.magic));
     329        memcpy(pThis->aHdrs[0].Common.version, RTZIPTAR_USTAR_VERSION, sizeof(pThis->aHdrs[0].Common.version));
     330    }
     331    else
     332        AssertFailedReturn(VERR_INTERNAL_ERROR_4);
    252333
    253334    /*
     
    265346        rc = RTStrFormatU32(pThis->aHdrs[0].Common.devmajor, sizeof(pThis->aHdrs[0].Common.devmajor),
    266347                            RTDEV_MAJOR(pObjInfo->Attr.u.Unix.Device),
    267                             8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.devmajor) - 1, RTSTR_F_ZEROPAD);
     348                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.devmajor) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
    268349        AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
    269350
    270351        rc = RTStrFormatU32(pThis->aHdrs[0].Common.devminor, sizeof(pThis->aHdrs[0].Common.devmajor),
    271352                            RTDEV_MINOR(pObjInfo->Attr.u.Unix.Device),
    272                             8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.devmajor) - 1, RTSTR_F_ZEROPAD);
     353                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Common.devmajor) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
    273354        AssertRCReturn(rc, VERR_TAR_NUM_VALUE_TOO_LARGE);
    274355    }
    275356
     357#if 0 /** @todo why doesn't this work? */
     358    /*
     359     * Set GNU specific stuff.
     360     */
     361    if (pThis->enmFormat == RTZIPTARFORMAT_GNU)
     362    {
     363        rc = RTStrFormatU64(pThis->aHdrs[0].Gnu.ctime, sizeof(pThis->aHdrs[0].Gnu.ctime),
     364                            RTTimeSpecGetSeconds(&pObjInfo->ChangeTime),
     365                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Gnu.ctime) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
     366        AssertRCReturn(rc, rc);
     367
     368        rc = RTStrFormatU64(pThis->aHdrs[0].Gnu.atime, sizeof(pThis->aHdrs[0].Gnu.atime),
     369                            RTTimeSpecGetSeconds(&pObjInfo->ChangeTime),
     370                            8 /*uBase*/, -1 /*cchWidth*/, sizeof(pThis->aHdrs[0].Gnu.atime) - 1, RTSTR_F_ZEROPAD | RTSTR_F_PRECISION);
     371        AssertRCReturn(rc, rc);
     372    }
     373#endif
     374
    276375    /*
    277376     * Finally the checksum.
    278377     */
     378    pThis->cHdrs = 1;
    279379    return rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
    280380}
     
    282382
    283383/**
    284  * Adds a file to the stream.
     384 * Allocates a buffer for transfering file data.
     385 *
     386 * @note    Will use the 3rd TAR header as fallback buffer if we're out of
     387 *          memory!
     388 *
     389 * @returns Pointer to buffer (won't ever fail).
     390 * @param   pThis           The TAR writer instance.
     391 * @param   pcbBuf          Where to return the buffer size.  This will be a
     392 *                          multiple of the TAR block size.
     393 * @param   ppvFree         Where to return the pointer to pass to RTMemTmpFree
     394 *                          when done with the buffer.
     395 * @param   cbFile          The file size.  Used as a buffer size hint.
     396 */
     397static uint8_t *rtZipTarFssWrite_AllocBuf(PRTZIPTARFSSTREAMWRITER pThis, size_t *pcbBuf, void **ppvFree, uint64_t cbObject)
     398{
     399    uint8_t *pbBuf;
     400
     401    /*
     402     * If this is a large file, try for a large buffer with 16KB alignment.
     403     */
     404    if (cbObject >= _64M)
     405    {
     406        pbBuf = (uint8_t *)RTMemTmpAlloc(_2M + _16K - 1);
     407        if (pbBuf)
     408        {
     409            *pcbBuf  = _2M;
     410            *ppvFree = pbBuf;
     411            return RT_ALIGN_PT(pbBuf, _16K, uint8_t *);
     412        }
     413    }
     414    /*
     415     * 4KB aligned 512KB buffer if larger 512KB or larger.
     416     */
     417    else if (cbObject >= _512K)
     418    {
     419        pbBuf = (uint8_t *)RTMemTmpAlloc(_512K + _4K - 1);
     420        if (pbBuf)
     421        {
     422            *pcbBuf  = _512K;
     423            *ppvFree = pbBuf;
     424            return RT_ALIGN_PT(pbBuf, _4K, uint8_t *);
     425        }
     426    }
     427    /*
     428     * Otherwise a 4KB aligned 128KB buffer.
     429     */
     430    else
     431    {
     432        pbBuf = (uint8_t *)RTMemTmpAlloc(_128K + _4K - 1);
     433        if (pbBuf)
     434        {
     435            *pcbBuf  = _128K;
     436            *ppvFree = pbBuf;
     437            return RT_ALIGN_PT(pbBuf, _4K, uint8_t *);
     438        }
     439    }
     440
     441    /*
     442     * If allocation failed, fallback on a 16KB buffer without any extra alignment.
     443     */
     444    pbBuf = (uint8_t *)RTMemTmpAlloc(_16K);
     445    if (pbBuf)
     446    {
     447        *pcbBuf  = _16K;
     448        *ppvFree = pbBuf;
     449        return pbBuf;
     450    }
     451
     452    /*
     453     * Final fallback, 512KB buffer using the 3rd header.
     454     */
     455    AssertCompile(RT_ELEMENTS(pThis->aHdrs) >= 3);
     456    *pcbBuf  = sizeof(pThis->aHdrs[2]);
     457    *ppvFree = NULL;
     458    return (uint8_t *)&pThis->aHdrs[2];
     459}
     460
     461
     462/**
     463 * Frees the sparse info for a TAR file.
     464 *
     465 * @param   pSparse         The sparse info to free.
     466 */
     467static void rtZipTarFssWriter_SparseInfoDestroy(PRTZIPTARSPARSE pSparse)
     468{
     469    PRTZIPTARSPARSECHUNK pCur;
     470    PRTZIPTARSPARSECHUNK pNext;
     471    RTListForEachSafe(&pSparse->ChunkHead, pCur, pNext, RTZIPTARSPARSECHUNK, Entry)
     472        RTMemTmpFree(pCur);
     473    RTMemTmpFree(pSparse);
     474}
     475
     476
     477/**
     478 * Adds a data span to the sparse info.
     479 *
     480 * @returns VINF_SUCCESS or VINF_NO_TMP_MEMORY.
     481 * @param   pSparse         The sparse info to free.
     482 * @param   offSpan         Offset of the span.
     483 * @param   cbSpan          Number of bytes.
     484 */
     485static int rtZipTarFssWriter_SparseInfoAddSpan(PRTZIPTARSPARSE pSparse, uint64_t offSpan, uint64_t cbSpan)
     486{
     487    /*
     488     * Get the chunk we're adding it to.
     489     */
     490    PRTZIPTARSPARSECHUNK pChunk;
     491    if (pSparse->iNextSpan != 0)
     492    {
     493        pChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
     494        Assert(pSparse->iNextSpan < RT_ELEMENTS(pChunk->aSpans));
     495    }
     496    else
     497    {
     498        pChunk = (PRTZIPTARSPARSECHUNK)RTMemTmpAllocZ(sizeof(*pChunk));
     499        if (!pChunk)
     500            return VERR_NO_TMP_MEMORY;
     501        RTListAppend(&pSparse->ChunkHead, &pChunk->Entry);
     502    }
     503
     504    /*
     505     * Append it.
     506     */
     507    pSparse->cDataSpans  += 1;
     508    pSparse->cbDataSpans += cbSpan;
     509    pChunk->aSpans[pSparse->iNextSpan].cb  = cbSpan;
     510    pChunk->aSpans[pSparse->iNextSpan].off = offSpan;
     511    if (++pSparse->iNextSpan >= RT_ELEMENTS(pChunk->aSpans))
     512        pSparse->iNextSpan = 0;
     513    return VINF_SUCCESS;
     514}
     515
     516
     517/**
     518 * Scans the input stream recording non-zero blocks.
     519 */
     520static int rtZipTarFssWriter_ScanSparseFile(PRTZIPTARFSSTREAMWRITER pThis, RTVFSFILE hVfsFile, uint64_t cbFile,
     521                                            size_t cbBuf, uint8_t *pbBuf, PRTZIPTARSPARSE *ppSparse)
     522{
     523    RT_NOREF(pThis);
     524
     525    /*
     526     * Create an empty sparse info bundle.
     527     */
     528    PRTZIPTARSPARSE pSparse = (PRTZIPTARSPARSE)RTMemTmpAlloc(sizeof(*pSparse));
     529    AssertReturn(pSparse, VERR_NO_MEMORY);
     530    pSparse->cbDataSpans = 0;
     531    pSparse->cDataSpans  = 0;
     532    pSparse->iNextSpan   = 0;
     533    RTListInit(&pSparse->ChunkHead);
     534
     535    /*
     536     * Scan the file from the start.
     537     */
     538    int rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
     539    if (RT_SUCCESS(rc))
     540    {
     541        bool        fZeroSpan = false;
     542        uint64_t    offSpan   = 0;
     543        uint64_t    cbSpan    = 0;
     544
     545        for (uint64_t off = 0; off < cbFile;)
     546        {
     547            uint64_t cbLeft   = cbFile - off;
     548            size_t   cbToRead = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
     549            rc = RTVfsFileRead(hVfsFile, pbBuf, cbToRead, NULL);
     550            if (RT_FAILURE(rc))
     551                break;
     552            size_t cBlocks = cbToRead / RTZIPTAR_BLOCKSIZE;
     553
     554            /* Zero pad the final buffer to a multiple of the blocksize. */
     555            if (!(cbToRead & (RTZIPTAR_BLOCKSIZE - 1)))
     556            { /* likely */ }
     557            else
     558            {
     559                AssertBreakStmt(cbLeft == cbToRead, rc = VERR_INTERNAL_ERROR_3);
     560                RT_BZERO(&pbBuf[cbToRead], RTZIPTAR_BLOCKSIZE - (cbToRead & (RTZIPTAR_BLOCKSIZE - 1)));
     561                cBlocks++;
     562            }
     563
     564            /*
     565             * Process the blocks we've just read one by one.
     566             */
     567            uint8_t const *pbBlock = pbBuf;
     568            for (size_t iBlock = 0; iBlock < cBlocks; iBlock++)
     569            {
     570                bool fZeroBlock = ASMMemIsZero(pbBlock, RTZIPTAR_BLOCKSIZE);
     571                if (fZeroBlock == fZeroSpan)
     572                    cbSpan += RTZIPTAR_BLOCKSIZE;
     573                else
     574                {
     575                    if (!fZeroSpan && cbSpan)
     576                    {
     577                        rc = rtZipTarFssWriter_SparseInfoAddSpan(pSparse, offSpan, cbSpan);
     578                        if (RT_FAILURE(rc))
     579                            break;
     580                    }
     581                    fZeroSpan = fZeroBlock;
     582                    offSpan   = off;
     583                    cbSpan    = RTZIPTAR_BLOCKSIZE;
     584                }
     585
     586                /* next block. */
     587                pbBlock += RTZIPTAR_BLOCKSIZE;
     588                off     += RTZIPTAR_BLOCKSIZE;
     589            }
     590        }
     591
     592        /*
     593         * Deal with the final span.  If we've got zeros thowards the end, we
     594         * must add a zero byte data span at the end.
     595         */
     596        if (RT_SUCCESS(rc))
     597        {
     598            if (!fZeroSpan && cbSpan)
     599            {
     600                if (cbFile & (RTZIPTAR_BLOCKSIZE - 1))
     601                {
     602                    Assert(!(cbSpan & (RTZIPTAR_BLOCKSIZE - 1)));
     603                    cbSpan -= RTZIPTAR_BLOCKSIZE;
     604                    cbSpan |= cbFile & (RTZIPTAR_BLOCKSIZE - 1);
     605                }
     606                rc = rtZipTarFssWriter_SparseInfoAddSpan(pSparse, offSpan, cbSpan);
     607            }
     608            if (RT_SUCCESS(rc))
     609                rc = rtZipTarFssWriter_SparseInfoAddSpan(pSparse, cbFile, 0);
     610        }
     611    }
     612
     613    if (RT_SUCCESS(rc))
     614    {
     615        /*
     616         * Return the file back to the start position before we return so that we
     617         * can segue into the regular rtZipTarFssWriter_AddFile without further ado.
     618         */
     619        rc = RTVfsFileSeek(hVfsFile, 0, RTFILE_SEEK_BEGIN, NULL);
     620        if (RT_SUCCESS(rc))
     621        {
     622            *ppSparse = pSparse;
     623            return VINF_SUCCESS;
     624        }
     625    }
     626
     627    rtZipTarFssWriter_SparseInfoDestroy(pSparse);
     628    *ppSparse = NULL;
     629    return rc;
     630}
     631
     632
     633/**
     634 * Writes GNU the sparse file headers.
     635 *
     636 * @returns IPRT status code.
     637 * @param   pThis           The TAR writer instance.
     638 * @param   pszPath         The path to the file.
     639 * @param   pObjInfo        The object information.
     640 * @param   pszOwnerNm      The owner name.
     641 * @param   pszGroupNm      The group name.
     642 * @param   pSparse         The sparse file info.
     643 */
     644static int rtZipTarFssWriter_WriteGnuSparseHeaders(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath,  PCRTFSOBJINFO pObjInfo,
     645                                                   const char *pszOwnerNm, const char *pszGroupNm, PCRTZIPTARSPARSE pSparse)
     646{
     647    /*
     648     * Format the first header.
     649     */
     650    int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, RTZIPTAR_TF_GNU_SPARSE);
     651    AssertRCReturn(rc, rc);
     652    AssertReturn(pThis->cHdrs == 1, VERR_INTERNAL_ERROR_2);
     653
     654    /* data size. */
     655    rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Common.size, pSparse->cbDataSpans);
     656    AssertRCReturn(rc, rc);
     657
     658    /* realsize. */
     659    rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Gnu.realsize, pObjInfo->cbObject);
     660    AssertRCReturn(rc, rc);
     661
     662    /*
     663     * Walk the sparse spans, fill and write headers one by one.
     664     */
     665    PRTZIPTARGNUSPARSE  paSparse    = &pThis->aHdrs[0].Gnu.sparse[0];
     666    uint32_t            cSparse     = RT_ELEMENTS(pThis->aHdrs[0].Gnu.sparse);
     667    uint32_t            iSparse     = 0;
     668
     669    PRTZIPTARSPARSECHUNK const pLastChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
     670    PRTZIPTARSPARSECHUNK pChunk;
     671    RTListForEach(&pSparse->ChunkHead, pChunk, RTZIPTARSPARSECHUNK, Entry)
     672    {
     673        uint32_t cSpans = pChunk != pLastChunk || pSparse->iNextSpan == 0
     674                        ? RT_ELEMENTS(pChunk->aSpans) : pSparse->iNextSpan;
     675        for (uint32_t iSpan = 0; iSpan < cSpans; iSpan++)
     676        {
     677            /* Flush the header? */
     678            if (iSparse >= cSparse)
     679            {
     680                pThis->aHdrs[0].Gnu.isextended = 1; /* more headers to come */
     681                if (cSparse == RT_ELEMENTS(pThis->aHdrs[0].Gnu.sparse))
     682                    rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
     683                if (RT_SUCCESS(rc))
     684                    rc = RTVfsIoStrmWrite(pThis->hVfsIos, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
     685                if (RT_FAILURE(rc))
     686                    return rc;
     687                RT_ZERO(pThis->aHdrs[0]);
     688                cSparse  = RT_ELEMENTS(pThis->aHdrs[0].GnuSparse.sp);
     689                iSparse  = 0;
     690                paSparse = &pThis->aHdrs[0].GnuSparse.sp[0];
     691            }
     692
     693            /* Append sparse data segment. */
     694            rc = rtZipTarFssWriter_FormatOffset(paSparse[iSparse].offset, pChunk->aSpans[iSpan].off);
     695            AssertRCReturn(rc, rc);
     696            rc = rtZipTarFssWriter_FormatOffset(paSparse[iSparse].numbytes, pChunk->aSpans[iSpan].cb);
     697            AssertRCReturn(rc, rc);
     698            iSparse++;
     699        }
     700    }
     701
     702    /*
     703     * The final header.
     704     */
     705    if (iSparse != 0)
     706    {
     707        Assert(pThis->aHdrs[0].Gnu.isextended == 0);
     708        rc = RTVfsIoStrmWrite(pThis->hVfsIos, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
     709    }
     710    pThis->cHdrs = 0;
     711    return rc;
     712}
     713
     714
     715/**
     716 * Adds a potentially sparse file to the output.
     717 *
     718 * @returns IPRT status code.
     719 * @param   pThis           The TAR writer instance.
     720 * @param   pszPath         The path to the file.
     721 * @param   hVfsFile        The potentially sparse file.
     722 * @param   hVfsIos         The I/O stream of the file. Same as @a hVfsFile.
     723 * @param   pObjInfo        The object information.
     724 * @param   pszOwnerNm      The owner name.
     725 * @param   pszGroupNm      The group name.
     726 */
     727static int rtZipTarFssWriter_AddFileSparse(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSFILE hVfsFile,
     728                                           RTVFSIOSTREAM hVfsIos, PCRTFSOBJINFO pObjInfo,
     729                                           const char *pszOwnerNm, const char *pszGroupNm)
     730{
     731    /*
     732     * Scan the input file to locate all zero blocks.
     733     */
     734    void    *pvBufFree;
     735    size_t   cbBuf;
     736    uint8_t *pbBuf = rtZipTarFssWrite_AllocBuf(pThis, &cbBuf, &pvBufFree, pObjInfo->cbObject);
     737
     738    PRTZIPTARSPARSE pSparse;
     739    int rc = rtZipTarFssWriter_ScanSparseFile(pThis, hVfsFile, pObjInfo->cbObject, cbBuf, pbBuf, &pSparse);
     740    if (RT_SUCCESS(rc))
     741    {
     742        /*
     743         * If there aren't at least 2 zero blocks in the file, don't bother
     744         * doing the sparse stuff and store it as a normal file.
     745         */
     746        if (pSparse->cbDataSpans + RTZIPTAR_BLOCKSIZE > (uint64_t)pObjInfo->cbObject)
     747        {
     748            rtZipTarFssWriter_SparseInfoDestroy(pSparse);
     749            RTMemTmpFree(pvBufFree);
     750            return rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, pObjInfo, pszOwnerNm, pszGroupNm);
     751        }
     752
     753        /*
     754         * Produce and write the headers.
     755         */
     756        if (pThis->enmFormat == RTZIPTARFORMAT_GNU)
     757            rc = rtZipTarFssWriter_WriteGnuSparseHeaders(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, pSparse);
     758        else
     759            AssertStmt(pThis->enmFormat != RTZIPTARFORMAT_GNU, rc = VERR_NOT_IMPLEMENTED);
     760        if (RT_SUCCESS(rc))
     761        {
     762            /*
     763             * Write the file bytes.
     764             */
     765            PRTZIPTARSPARSECHUNK const pLastChunk = RTListGetLast(&pSparse->ChunkHead, RTZIPTARSPARSECHUNK, Entry);
     766            PRTZIPTARSPARSECHUNK pChunk;
     767            RTListForEach(&pSparse->ChunkHead, pChunk, RTZIPTARSPARSECHUNK, Entry)
     768            {
     769                uint32_t cSpans = pChunk != pLastChunk || pSparse->iNextSpan == 0
     770                                ? RT_ELEMENTS(pChunk->aSpans) : pSparse->iNextSpan;
     771                for (uint32_t iSpan = 0; iSpan < cSpans; iSpan++)
     772                {
     773                    rc = RTVfsFileSeek(hVfsFile, pChunk->aSpans[iSpan].off, RTFILE_SEEK_BEGIN, NULL);
     774                    if (RT_FAILURE(rc))
     775                        break;
     776                    uint64_t cbLeft = pChunk->aSpans[iSpan].cb;
     777                    Assert(   !(cbLeft & (RTZIPTAR_BLOCKSIZE - 1))
     778                           || (iSpan + 1 == cSpans && pChunk == pLastChunk));
     779                    while (cbLeft > 0)
     780                    {
     781                        size_t cbToRead = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
     782                        rc = RTVfsFileRead(hVfsFile, pbBuf, cbToRead, NULL);
     783                        if (RT_SUCCESS(rc))
     784                        {
     785                            rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbToRead, true /*fBlocking*/, NULL);
     786                            if (RT_SUCCESS(rc))
     787                            {
     788                                pThis->cbWritten += cbToRead;
     789                                cbLeft           -= cbToRead;
     790                                continue;
     791                            }
     792                        }
     793                        break;
     794                    }
     795                    if (RT_FAILURE(rc))
     796                        break;
     797                }
     798            }
     799
     800            /*
     801             * Do the zero padding.
     802             */
     803            if (   RT_SUCCESS(rc)
     804                && (pSparse->cbDataSpans & (RTZIPTAR_BLOCKSIZE - 1)))
     805            {
     806                size_t cbToZero = RTZIPTAR_BLOCKSIZE - (pSparse->cbDataSpans & (RTZIPTAR_BLOCKSIZE - 1));
     807                rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, cbToZero, true /*fBlocking*/, NULL);
     808                if (RT_SUCCESS(rc))
     809                    pThis->cbWritten += cbToZero;
     810            }
     811        }
     812
     813        if (RT_FAILURE(rc))
     814            pThis->rcFatal = rc;
     815        rtZipTarFssWriter_SparseInfoDestroy(pSparse);
     816    }
     817    RTMemTmpFree(pvBufFree);
     818    return rc;
     819}
     820
     821
     822/**
     823 * Adds an I/O stream of indeterminate length to the TAR file.
     824 *
     825 * This requires the output to be seekable, i.e. a file, because we need to go
     826 * back and update @c size field of the TAR header after pumping all the data
     827 * bytes thru and establishing the file length.
    285828 *
    286829 * @returns IPRT status code.
     
    288831 * @param   pszPath         The path to the file.
    289832 * @param   hVfsIos         The I/O stream of the file.
    290  * @param   fFlags          The RTVFSFSSTREAMOPS::pfnAdd flags.
    291833 * @param   pObjInfo        The object information.
    292834 * @param   pszOwnerNm      The owner name.
    293835 * @param   pszGroupNm      The group name.
    294836 */
    295 static int rtZipTarFssWriter_AddFile(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos, uint32_t fFlags,
    296                                      PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
    297 {
     837static int rtZipTarFssWriter_AddFileStream(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos,
     838                                           PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
     839{
     840    AssertReturn(pThis->hVfsFile != NIL_RTVFSFILE, VERR_NOT_A_FILE);
     841
    298842    /*
    299843     * Append the header.
     
    302846    if (RT_SUCCESS(rc))
    303847    {
    304         RTFOFF const offHdr = fFlags & RTVFSFSSTRM_ADD_F_STREAM ? RTVfsIoStrmTell(pThis->hVfsIos) : RTFOFF_MAX;
    305         rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
    306         if (RT_SUCCESS(rc))
    307         {
    308             pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
    309 
    310             /*
    311              * Allocate a tmporary buffer
    312              */
    313             uint8_t *pbFree;
    314             size_t   cbBuf = _512K;
    315             uint8_t *pbBuf = pbFree = (uint8_t *)RTMemTmpAlloc(cbBuf);
    316             if (!pbBuf)
     848        RTFOFF const offHdr = RTVfsFileTell(pThis->hVfsFile);
     849        if (offHdr >= 0)
     850        {
     851            rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
     852            if (RT_SUCCESS(rc))
    317853            {
    318                 cbBuf = _32K;
    319                 pbBuf = pbFree = (uint8_t *)RTMemTmpAlloc(cbBuf);
    320                 if (!pbBuf)
    321                 {
    322                     cbBuf = sizeof(pThis->aHdrs) - sizeof(pThis->aHdrs[0]);
    323                     pbBuf = (uint8_t *)&pThis->aHdrs[1];
    324                 }
    325             }
    326 
    327             /*
    328              * Copy the bytes.  Padding the last buffer to a multiple of 512.
    329              */
    330             if (!(fFlags & RTVFSFSSTRM_ADD_F_STREAM))
    331             {
    332                 uint64_t cbLeft = pObjInfo->cbObject;
    333                 while (cbLeft > 0)
    334                 {
    335                     size_t cbRead = cbLeft > cbBuf ? cbBuf : (size_t)cbBuf;
    336                     rc = RTVfsIoStrmRead(hVfsIos, pbBuf, cbRead, true /*fBlocking*/, NULL);
    337                     if (RT_FAILURE(rc))
    338                         break;
    339 
    340                     size_t cbToWrite = cbRead;
    341                     if (cbRead & (sizeof(RTZIPTARHDR) - 1))
    342                     {
    343                         size_t cbToZero = sizeof(RTZIPTARHDR) - (cbRead & (sizeof(RTZIPTARHDR) - 1));
    344                         memset(&pbBuf[cbRead], 0, cbToZero);
    345                         cbToWrite += cbToZero;
    346                     }
    347 
    348                     rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbToWrite, true /*fBlocking*/, NULL);
    349                     if (RT_FAILURE(rc))
    350                         break;
    351                     pThis->cbWritten += cbToWrite;
    352                     cbLeft -= cbRead;
    353                 }
    354             }
    355             /*
    356              * Same as above, only here we don't know the exact input length.
    357              */
    358             else
    359             {
     854                pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
     855
     856                /*
     857                 * Transfer the bytes.
     858                 */
     859                void    *pvBufFree;
     860                size_t   cbBuf;
     861                uint8_t *pbBuf = rtZipTarFssWrite_AllocBuf(pThis, &cbBuf, &pvBufFree,
     862                                                           pObjInfo->cbObject > 0 && pObjInfo->cbObject != RTFOFF_MAX
     863                                                           ? pObjInfo->cbObject : _1G);
     864
    360865                uint64_t cbReadTotal = 0;
    361866                for (;;)
     
    378883                }
    379884
    380                 /* Do the zero padding. */
    381                 if ((cbReadTotal & (sizeof(RTZIPTARHDR) - 1)) && RT_SUCCESS(rc))
     885                RTMemTmpFree(pvBufFree);
     886
     887                /*
     888                 * Do the zero padding.
     889                 */
     890                if ((cbReadTotal & (RTZIPTAR_BLOCKSIZE - 1)) && RT_SUCCESS(rc))
    382891                {
    383                     size_t cbToZero = sizeof(RTZIPTARHDR) - (cbReadTotal & (sizeof(RTZIPTARHDR) - 1));
     892                    size_t cbToZero = RTZIPTAR_BLOCKSIZE - (cbReadTotal & (RTZIPTAR_BLOCKSIZE - 1));
    384893                    rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, cbToZero, true /*fBlocking*/, NULL);
    385894                    if (RT_SUCCESS(rc))
     
    391900                 * from before the data pumping above and just update the size.
    392901                 */
    393                 if (RT_SUCCESS(rc) && (RTFOFF)cbReadTotal != pObjInfo->cbObject)
     902                if ((RTFOFF)cbReadTotal != pObjInfo->cbObject && RT_SUCCESS(rc))
    394903                {
    395                     RTVFSFILE hVfsFile = RTVfsIoStrmToFile(pThis->hVfsIos);
    396                     if (hVfsFile != NIL_RTVFSFILE)
     904                    RTFOFF const offRestore = RTVfsFileTell(pThis->hVfsFile);
     905                    if (offRestore >= 0)
    397906                    {
    398                         RTFOFF offRestore = RTVfsFileTell(hVfsFile);
    399                         if (offRestore >= 0)
     907                        rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Common.size, cbReadTotal);
     908                        if (RT_SUCCESS(rc))
     909                            rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
     910                        if (RT_SUCCESS(rc))
    400911                        {
    401                             rc = rtZipTarFssWriter_FormatOffset(pThis->aHdrs[0].Common.size, cbReadTotal);
     912                            rc = RTVfsFileWriteAt(pThis->hVfsFile, offHdr, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), NULL);
    402913                            if (RT_SUCCESS(rc))
    403                                 rc = rtZipTarFssWriter_ChecksumHdr(&pThis->aHdrs[0]);
    404                             if (RT_SUCCESS(rc))
    405                             {
    406                                 rc = RTVfsFileWriteAt(hVfsFile, offHdr, &pThis->aHdrs[0], sizeof(pThis->aHdrs[0]), NULL);
    407                                 if (RT_SUCCESS(rc))
    408                                     rc = RTVfsFileSeek(hVfsFile, offRestore, RTFILE_SEEK_BEGIN, NULL);
    409                             }
     914                                rc = RTVfsFileSeek(pThis->hVfsFile, offRestore, RTFILE_SEEK_BEGIN, NULL);
    410915                        }
    411                         else
    412                             rc = (int)offRestore;
    413                         RTVfsFileRelease(hVfsFile);
    414916                    }
    415917                    else
    416                         AssertFailedStmt(rc = VERR_NOT_A_FILE);
     918                        rc = (int)offRestore;
    417919                }
     920
     921                if (RT_SUCCESS(rc))
     922                    return VINF_SUCCESS;
    418923            }
    419 
    420             RTMemTmpFree(pbFree);
     924        }
     925        else
     926            rc = (int)offHdr;
     927        pThis->rcFatal = rc;
     928    }
     929    return rc;
     930}
     931
     932
     933/**
     934 * Adds a file to the stream.
     935 *
     936 * @returns IPRT status code.
     937 * @param   pThis           The TAR writer instance.
     938 * @param   pszPath         The path to the file.
     939 * @param   hVfsIos         The I/O stream of the file.
     940 * @param   fFlags          The RTVFSFSSTREAMOPS::pfnAdd flags.
     941 * @param   pObjInfo        The object information.
     942 * @param   pszOwnerNm      The owner name.
     943 * @param   pszGroupNm      The group name.
     944 */
     945static int rtZipTarFssWriter_AddFile(PRTZIPTARFSSTREAMWRITER pThis, const char *pszPath, RTVFSIOSTREAM hVfsIos,
     946                                     PCRTFSOBJINFO pObjInfo, const char *pszOwnerNm, const char *pszGroupNm)
     947{
     948    /*
     949     * Append the header.
     950     */
     951    int rc = rtZipTarFssWriter_ObjInfoToHdr(pThis, pszPath, pObjInfo, pszOwnerNm, pszGroupNm, UINT8_MAX);
     952    if (RT_SUCCESS(rc))
     953    {
     954        rc = RTVfsIoStrmWrite(pThis->hVfsIos, pThis->aHdrs, pThis->cHdrs * sizeof(pThis->aHdrs[0]), true /*fBlocking*/, NULL);
     955        if (RT_SUCCESS(rc))
     956        {
     957            pThis->cbWritten += pThis->cHdrs * sizeof(pThis->aHdrs[0]);
     958
     959            /*
     960             * Copy the bytes.  Padding the last buffer to a multiple of 512.
     961             */
     962            void    *pvBufFree;
     963            size_t   cbBuf;
     964            uint8_t *pbBuf = rtZipTarFssWrite_AllocBuf(pThis, &cbBuf, &pvBufFree, pObjInfo->cbObject);
     965
     966            uint64_t cbLeft = pObjInfo->cbObject;
     967            while (cbLeft > 0)
     968            {
     969                size_t cbRead = cbLeft > cbBuf ? cbBuf : (size_t)cbLeft;
     970                rc = RTVfsIoStrmRead(hVfsIos, pbBuf, cbRead, true /*fBlocking*/, NULL);
     971                if (RT_FAILURE(rc))
     972                    break;
     973
     974                size_t cbToWrite = cbRead;
     975                if (cbRead & (RTZIPTAR_BLOCKSIZE - 1))
     976                {
     977                    size_t cbToZero = RTZIPTAR_BLOCKSIZE - (cbRead & (RTZIPTAR_BLOCKSIZE - 1));
     978                    memset(&pbBuf[cbRead], 0, cbToZero);
     979                    cbToWrite += cbToZero;
     980                }
     981
     982                rc = RTVfsIoStrmWrite(pThis->hVfsIos, pbBuf, cbToWrite, true /*fBlocking*/, NULL);
     983                if (RT_FAILURE(rc))
     984                    break;
     985                pThis->cbWritten += cbToWrite;
     986                cbLeft -= cbRead;
     987            }
     988
     989            RTMemTmpFree(pvBufFree);
     990
    421991            if (RT_SUCCESS(rc))
    422992                return VINF_SUCCESS;
     
    5331103    pThis->hVfsIos = NIL_RTVFSIOSTREAM;
    5341104
     1105    if (pThis->hVfsFile != NIL_RTVFSFILE)
     1106    {
     1107        RTVfsFileRelease(pThis->hVfsFile);
     1108        pThis->hVfsFile = NIL_RTVFSFILE;
     1109    }
     1110
    5351111    return VINF_SUCCESS;
    5361112}
     
    5801156
    5811157    /*
    582      * Do type specific handling.
     1158     * Do type specific handling.  File have several options and variations to
     1159     * take into account, thus the mess.
    5831160     */
    5841161    if (RTFS_IS_FILE(ObjInfo.Attr.fMode))
     
    5861163        RTVFSIOSTREAM hVfsIos = RTVfsObjToIoStream(hVfsObj);
    5871164        AssertReturn(hVfsIos != NIL_RTVFSIOSTREAM, VERR_WRONG_TYPE);
    588         rc = rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, fFlags, &ObjInfo,
    589                                        ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
     1165
     1166        if (fFlags & RTVFSFSSTRM_ADD_F_STREAM)
     1167            rc = rtZipTarFssWriter_AddFileStream(pThis, pszPath, hVfsIos, &ObjInfo,
     1168                                                 ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
     1169        else if (   !(pThis->fFlags & RTZIPTAR_C_SPARSE)
     1170                 || ObjInfo.cbObject < RTZIPTAR_MIN_SPARSE)
     1171            rc = rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, &ObjInfo,
     1172                                           ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
     1173        else
     1174        {
     1175            RTVFSFILE hVfsFile = RTVfsObjToFile(hVfsObj);
     1176            if (hVfsFile != NIL_RTVFSFILE)
     1177            {
     1178                rc = rtZipTarFssWriter_AddFileSparse(pThis, pszPath, hVfsFile, hVfsIos, &ObjInfo,
     1179                                                     ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
     1180                RTVfsFileRelease(hVfsFile);
     1181            }
     1182            else
     1183                rc = rtZipTarFssWriter_AddFile(pThis, pszPath, hVfsIos, &ObjInfo,
     1184                                               ObjOwnerName.Attr.u.UnixOwner.szName, ObjGrpName.Attr.u.UnixOwner.szName);
     1185        }
    5901186        RTVfsIoStrmRelease(hVfsIos);
    5911187    }
     
    6121208{
    6131209    PRTZIPTARFSSTREAMWRITER pThis = (PRTZIPTARFSSTREAMWRITER)pvThis;
    614     if (RT_SUCCESS(pThis->rcFatal))
    615     {
    616         /** @todo investigate zero end records. */
    617         return VINF_SUCCESS;
    618     }
    619     return pThis->rcFatal;
     1210    int rc = pThis->rcFatal;
     1211    if (RT_SUCCESS(rc))
     1212    {
     1213        /*
     1214         * There are supposed to be two zero headers at the end of the archive.
     1215         * GNU tar may write more because of the way it does buffering,
     1216         * libarchive OTOH writes exactly two.
     1217         */
     1218        rc = RTVfsIoStrmWrite(pThis->hVfsIos, g_abRTZero4K, RTZIPTAR_BLOCKSIZE * 2, true /*fBlocking*/, NULL);
     1219        if (RT_SUCCESS(rc))
     1220        {
     1221            pThis->cbWritten += RTZIPTAR_BLOCKSIZE * 2;
     1222
     1223            /*
     1224             * Flush the output.
     1225             */
     1226            rc = RTVfsIoStrmFlush(pThis->hVfsIos);
     1227            if (RT_SUCCESS(rc))
     1228                return rc;
     1229        }
     1230        pThis->rcFatal = rc;
     1231    }
     1232    return rc;
    6201233}
    6211234
     
    6571270    if (enmFormat == RTZIPTARFORMAT_DEFAULT)
    6581271        enmFormat = RTZIPTARFORMAT_GNU;
    659     AssertReturn(enmFormat == RTZIPTARFORMAT_GNU, VERR_NOT_IMPLEMENTED); /* Only implementing GNU output at the moment. */
     1272    AssertReturn(   enmFormat == RTZIPTARFORMAT_GNU
     1273                 || enmFormat == RTZIPTARFORMAT_USTAR
     1274                 , VERR_NOT_IMPLEMENTED); /* Only implementing GNU and USTAR output at the moment. */
    6601275
    6611276    uint32_t cRefs = RTVfsIoStrmRetain(hVfsIosOut);
     
    6721287    {
    6731288        pThis->hVfsIos   = hVfsIosOut;
     1289        pThis->hVfsFile  = RTVfsIoStrmToFile(hVfsIosOut);
     1290
    6741291        pThis->enmFormat = enmFormat;
     1292        pThis->fFlags    = fFlags;
    6751293        pThis->rcFatal   = VINF_SUCCESS;
    6761294
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette