VirtualBox

Changeset 31623 in vbox


Ignore:
Timestamp:
Aug 13, 2010 12:24:14 AM (14 years ago)
Author:
vboxsync
Message:

Runtime: add progress callback support to the tar backend

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/iprt/tar.h

    r28800 r31623  
    44
    55/*
    6  * Copyright (C) 2009 Oracle Corporation
     6 * Copyright (C) 2009-2010 Oracle Corporation
    77 *
    88 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    7676 * (The matching is case sensitive.)
    7777 *
    78  * @note    Currently only regular files are supported. Also some of the heade
     78 * @note    Currently only regular files are supported. Also some of the header
    7979 *          fields are not used (uid, gid, uname, gname, mtime).
    8080 *
    8181 * @returns iprt status code.
    8282 *
    83  * @param   pszTarFile      Tar file to extract files from.
    84  * @param   pszOutputDir    Where to store the extracted files. Must exist.
    85  * @param   papszFiles      Which files should be extracted.
    86  * @param   cFiles          The number of files in papszFiles.
     83 * @param   pszTarFile           Tar file to extract files from.
     84 * @param   pszOutputDir         Where to store the extracted files. Must exist.
     85 * @param   papszFiles           Which files should be extracted.
     86 * @param   cFiles               The number of files in papszFiles.
     87 * @param   pfnProgressCallback  Progress callback function. Optional.
     88 * @param   pvUser               User defined data for the progress
     89 *                               callback. Optional.
    8790 */
    88 RTR3DECL(int) RTTarExtractFiles(const char *pszTarFile, const char *pszOutputDir, const char * const *papszFiles, size_t cFiles);
     91RTR3DECL(int) RTTarExtractFiles(const char *pszTarFile, const char *pszOutputDir, const char * const *papszFiles, size_t cFiles, PFNRTPROGRESS pfnProgressCallback, void *pvUser);
    8992
    9093/**
     
    97100 * @retval  VERR_FILE_NOT_FOUND when the index isn't valid.
    98101 *
    99  * @param   pszTarFile      Tar file to extract the file from.
    100  * @param   pszOutputDir    Where to store the extracted file. Must exist.
    101  * @param   iIndex          Which file should be extracted, 0 based.
    102  * @param   ppszFileName    On success the filename of the extracted file. Must
    103  *                          be freed with RTStrFree.
     102 * @param   pszTarFile           Tar file to extract the file from.
     103 * @param   pszOutputDir         Where to store the extracted file. Must exist.
     104 * @param   iIndex               Which file should be extracted, 0 based.
     105 * @param   ppszFileName         On success the filename of the extracted file. Must
     106 *                               be freed with RTStrFree.
     107 * @param   pfnProgressCallback  Progress callback function. Optional.
     108 * @param   pvUser               User defined data for the progress
     109 *                               callback. Optional.
    104110 */
    105 RTR3DECL(int) RTTarExtractByIndex(const char *pszTarFile, const char *pszOutputDir, size_t iIndex, char **ppszFileName);
     111RTR3DECL(int) RTTarExtractByIndex(const char *pszTarFile, const char *pszOutputDir, size_t iIndex, char **ppszFileName, PFNRTPROGRESS pfnProgressCallback, void *pvUser);
     112
     113/**
     114 * Extract all files of the archive.
     115 *
     116 * @note    Currently only regular files are supported. Also some of the header
     117 *          fields are not used (uid, gid, uname, gname, mtime).
     118 *
     119 * @returns iprt status code.
     120 *
     121 * @param   pszTarFile           Tar file to extract the files from.
     122 * @param   pszOutputDir         Where to store the extracted files. Must exist.
     123 * @param   pfnProgressCallback  Progress callback function. Optional.
     124 * @param   pvUser               User defined data for the progress
     125 *                               callback. Optional.
     126 */
     127RTR3DECL(int) RTTarExtractAll(const char *pszTarFile, const char *pszOutputDir, PFNRTPROGRESS pfnProgressCallback, void *pvUser);
    106128
    107129/**
     
    112134 * @returns iprt status code.
    113135 *
    114  * @param   pszTarFile      Where to create the Tar archive.
    115  * @param   papszFiles      Which files should be included.
    116  * @param   cFiles          The number of files in papszFiles.
     136 * @param   pszTarFile           Where to create the Tar archive.
     137 * @param   papszFiles           Which files should be included.
     138 * @param   cFiles               The number of files in papszFiles.
     139 * @param   pfnProgressCallback  Progress callback function. Optional.
     140 * @param   pvUser               User defined data for the progress
     141 *                               callback. Optional.
    117142 */
    118 RTR3DECL(int) RTTarCreate(const char *pszTarFile, const char * const *papszFiles, size_t cFiles);
     143RTR3DECL(int) RTTarCreate(const char *pszTarFile, const char * const *papszFiles, size_t cFiles, PFNRTPROGRESS pfnProgressCallback, void *pvUser);
    119144
    120145/** @} */
  • trunk/src/VBox/Runtime/common/misc/tar.cpp

    r28800 r31623  
    4040#include <iprt/string.h>
    4141
    42 
    4342/*******************************************************************************
    4443*   Structures and Typedefs                                                    *
     
    9291#endif
    9392
    94 
    9593/*******************************************************************************
    9694*   Internal Functions                                                         *
     
    138136}
    139137
    140 static int rtTarCopyFileFrom(RTFILE hFile, const char *pszTargetName, PRTTARRECORD pRecord)
     138static int rtTarCopyFileFrom(RTFILE hFile, const char *pszTargetName, PRTTARRECORD pRecord, const uint64_t cbOverallSize, uint64_t &cbOverallWritten, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
    141139{
    142140    RTFILE hNewFile;
     
    157155    for (;;)
    158156    {
     157        if (pfnProgressCallback)
     158            pfnProgressCallback(100.0 / cbOverallSize * cbOverallWritten, pvUser);
    159159        /* Finished already? */
    160160        if (cbAllWritten == cbToCopy)
     
    174174        /* Count how many bytes are written already */
    175175        cbAllWritten += cbToWrite;
     176        cbOverallWritten += cbToWrite;
    176177    }
    177178
     
    202203}
    203204
    204 static int rtTarCopyFileTo(RTFILE hFile, const char *pszSrcName)
     205static int rtTarCopyFileTo(RTFILE hFile, const char *pszSrcName, const uint64_t cbOverallSize, uint64_t &cbOverallWritten, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
    205206{
    206207    RTFILE hOldFile;
     
    268269            for (;;)
    269270            {
     271                if (pfnProgressCallback)
     272                    pfnProgressCallback(100.0 / cbOverallSize * cbOverallWritten, pvUser);
    270273                if (cbAllWritten >= cbSize)
    271274                    break;
     
    288291                /* Count how many bytes are written already */
    289292                cbAllWritten += sizeof(record);
     293                cbOverallWritten += sizeof(record);
    290294            }
    291295
     
    311315}
    312316
     317static int rtTarGetFilesOverallSize(RTFILE hFile, const char * const *papszFiles, size_t cFiles, uint64_t *pcbOverallSize)
     318{
     319    int rc;
     320    size_t cFound = 0;
     321    RTTARRECORD record;
     322    for (;;)
     323    {
     324/** @todo r=bird: the reading, validation and EOF check done here should be
     325 *        moved to a separate helper function. That would make it easiser to
     326 *        distinguish genuine-end-of-tar-file and VERR_EOF caused by a
     327 *        trunacted file. That said, rtTarSkipData won't return VERR_EOF, at
     328 *        least not on unix, since it's not a sin to seek beyond the end of a
     329 *        file. */
     330        rc = RTFileRead(hFile, &record, sizeof(record), NULL);
     331        /* Check for error or EOF. */
     332        if (RT_FAILURE(rc))
     333            break;
     334        /* Check for EOF & data integrity */
     335        rc = rtTarCheckHeader(&record);
     336        if (RT_FAILURE(rc))
     337            break;
     338        /* We support normal files only */
     339        if (   record.h.linkflag == LF_OLDNORMAL
     340            || record.h.linkflag == LF_NORMAL)
     341        {
     342            for (size_t i = 0; i < cFiles; ++i)
     343            {
     344                if (!RTStrCmp(record.h.name, papszFiles[i]))
     345                {
     346                    uint64_t cbSize;
     347                    /* Get the file size */
     348                    rc = RTStrToUInt64Full(record.h.size, 8, &cbSize);
     349                    /* Sum up the overall size */
     350                    *pcbOverallSize += cbSize;
     351                    ++cFound;
     352                    break;
     353                }
     354            }
     355            if (   cFound == cFiles
     356                || RT_FAILURE(rc))
     357                break;
     358        }
     359        rc = rtTarSkipData(hFile, &record);
     360        if (RT_FAILURE(rc))
     361            break;
     362    }
     363    /* Make sure the file pointer is at the begin of the file again. */
     364    if (RT_SUCCESS(rc))
     365        rc = RTFileSeek(hFile, 0, RTFILE_SEEK_BEGIN, 0);
     366    return rc;
     367}
     368
     369/*******************************************************************************
     370*   Public Functions                                                         *
     371*******************************************************************************/
    313372
    314373RTR3DECL(int) RTTarQueryFileExists(const char *pszTarFile, const char *pszFile)
     
    460519}
    461520
    462 RTR3DECL(int) RTTarExtractFiles(const char *pszTarFile, const char *pszOutputDir, const char * const *papszFiles, size_t cFiles)
     521RTR3DECL(int) RTTarExtractFiles(const char *pszTarFile, const char *pszOutputDir, const char * const *papszFiles, size_t cFiles, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
    463522{
    464523    /* Validate input */
     
    473532        return rc;
    474533
    475     /* Iterate through the tar file record by record. */
    476     RTTARRECORD record;
    477     char **paExtracted = (char **)RTMemTmpAllocZ(sizeof(char *) * cFiles);
    478     if (paExtracted)
    479     {
    480         size_t cExtracted = 0;
    481         for (;;)
    482         {
    483             rc = RTFileRead(hFile, &record, sizeof(record), NULL);
    484             /* Check for error or EOF. */
    485             if (RT_FAILURE(rc))
    486                 break;
    487             /* Check for EOF & data integrity */
    488             rc = rtTarCheckHeader(&record);
    489             if (RT_FAILURE(rc))
    490                 break;
    491             /* We support normal files only */
    492             if (   record.h.linkflag == LF_OLDNORMAL
    493                 || record.h.linkflag == LF_NORMAL)
     534    /* Get the overall size of all files to extract out of the tar archive
     535       headers. Only necessary if there is a progress callback. */
     536    uint64_t cbOverallSize = 0;
     537    if (pfnProgressCallback)
     538        rc = rtTarGetFilesOverallSize(hFile, papszFiles, cFiles, &cbOverallSize);
     539    if (RT_SUCCESS(rc))
     540    {
     541        /* Iterate through the tar file record by record. */
     542        RTTARRECORD record;
     543        char **paExtracted = (char **)RTMemTmpAllocZ(sizeof(char *) * cFiles);
     544        if (paExtracted)
     545        {
     546            size_t cExtracted = 0;
     547            uint64_t cbOverallWritten = 0;
     548            for (;;)
    494549            {
    495                 bool fFound = false;
    496                 for (size_t i = 0; i < cFiles; ++i)
     550                rc = RTFileRead(hFile, &record, sizeof(record), NULL);
     551                /* Check for error or EOF. */
     552                if (RT_FAILURE(rc))
     553                    break;
     554                /* Check for EOF & data integrity */
     555                rc = rtTarCheckHeader(&record);
     556                if (RT_FAILURE(rc))
     557                    break;
     558                /* We support normal files only */
     559                if (   record.h.linkflag == LF_OLDNORMAL
     560                       || record.h.linkflag == LF_NORMAL)
    497561                {
    498                     if (!RTStrCmp(record.h.name, papszFiles[i]))
     562                    bool fFound = false;
     563                    for (size_t i = 0; i < cFiles; ++i)
    499564                    {
    500                         fFound = true;
    501                         if (cExtracted < cFiles)
     565                        if (!RTStrCmp(record.h.name, papszFiles[i]))
    502566                        {
    503                             char *pszTargetFile;
    504                             rc = RTStrAPrintf(&pszTargetFile, "%s/%s", pszOutputDir, papszFiles[i]);
    505                             if (rc > 0)
     567                            fFound = true;
     568                            if (cExtracted < cFiles)
    506569                            {
    507                                 rc = rtTarCopyFileFrom(hFile, pszTargetFile, &record);
    508                                 if (RT_SUCCESS(rc))
    509                                     paExtracted[cExtracted++] = pszTargetFile;
     570                                char *pszTargetFile;
     571                                rc = RTStrAPrintf(&pszTargetFile, "%s/%s", pszOutputDir, papszFiles[i]);
     572                                if (rc > 0)
     573                                {
     574                                    rc = rtTarCopyFileFrom(hFile, pszTargetFile, &record, cbOverallSize, cbOverallWritten, pfnProgressCallback, pvUser);
     575                                    if (RT_SUCCESS(rc))
     576                                        paExtracted[cExtracted++] = pszTargetFile;
     577                                    else
     578                                        RTStrFree(pszTargetFile);
     579                                }
    510580                                else
    511                                     RTStrFree(pszTargetFile);
     581                                    rc = VERR_NO_MEMORY;
    512582                            }
    513583                            else
    514                                 rc = VERR_NO_MEMORY;
     584                                rc = VERR_ALREADY_EXISTS;
     585                            break;
    515586                        }
    516                         else
    517                             rc = VERR_ALREADY_EXISTS;
     587                    }
     588                    if (RT_FAILURE(rc))
    518589                        break;
     590                    /* If the current record isn't a file in the file list we have to
     591                     * skip the data */
     592                    if (!fFound)
     593                    {
     594                        rc = rtTarSkipData(hFile, &record);
     595                        if (RT_FAILURE(rc))
     596                            break;
    519597                    }
    520598                }
     599            }
     600
     601            if (rc == VERR_EOF)
     602                rc = VINF_SUCCESS;
     603
     604            /* If we didn't found all files, indicate an error */
     605            if (cExtracted != cFiles && RT_SUCCESS(rc))
     606                rc = VERR_FILE_NOT_FOUND;
     607
     608            /* Cleanup the names of the extracted files, deleting them on failure. */
     609            while (cExtracted-- > 0)
     610            {
    521611                if (RT_FAILURE(rc))
    522                     break;
    523                 /* If the current record isn't a file in the file list we have to
    524                  * skip the data */
    525                 if (!fFound)
    526                 {
    527                     rc = rtTarSkipData(hFile, &record);
    528                     if (RT_FAILURE(rc))
    529                         break;
    530                 }
     612                    RTFileDelete(paExtracted[cExtracted]);
     613                RTStrFree(paExtracted[cExtracted]);
    531614            }
    532         }
    533 
    534         if (rc == VERR_EOF)
    535             rc = VINF_SUCCESS;
    536 
    537         /* If we didn't found all files, indicate an error */
    538         if (cExtracted != cFiles && RT_SUCCESS(rc))
    539             rc = VERR_FILE_NOT_FOUND;
    540 
    541         /* Cleanup the names of the extracted files, deleting them on failure. */
    542         while (cExtracted-- > 0)
    543         {
    544             if (RT_FAILURE(rc))
    545                 RTFileDelete(paExtracted[cExtracted]);
    546             RTStrFree(paExtracted[cExtracted]);
    547         }
    548         RTMemTmpFree(paExtracted);
    549     }
    550     else
    551         rc = VERR_NO_TMP_MEMORY;
     615            RTMemTmpFree(paExtracted);
     616        }
     617        else
     618            rc = VERR_NO_TMP_MEMORY;
     619    }
    552620
    553621    RTFileClose(hFile);
     
    555623}
    556624
    557 RTR3DECL(int) RTTarExtractByIndex(const char *pszTarFile, const char *pszOutputDir, size_t iIndex, char **ppszFileName)
     625RTR3DECL(int) RTTarExtractByIndex(const char *pszTarFile, const char *pszOutputDir, size_t iIndex, char **ppszFileName, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
    558626{
    559627    /* Validate input */
     
    592660                if (rc > 0)
    593661                {
    594                     rc = rtTarCopyFileFrom(hFile, pszTargetName, &record);
     662                    uint64_t cbOverallSize;
     663                    uint64_t cbOverallWritten = 0;
     664                    /* Get the file size */
     665                    rc = RTStrToUInt64Full(record.h.size, 8, &cbOverallSize);
     666                    if (RT_FAILURE(rc))
     667                        break;
     668                    rc = rtTarCopyFileFrom(hFile, pszTargetName, &record, cbOverallSize, cbOverallWritten, pfnProgressCallback, pvUser);
    595669                    /* On success pass on the filename if requested. */
    596670                    if (    RT_SUCCESS(rc)
     
    623697}
    624698
    625 RTR3DECL(int) RTTarCreate(const char *pszTarFile, const char * const *papszFiles, size_t cFiles)
     699RTR3DECL(int) RTTarExtractAll(const char *pszTarFile, const char *pszOutputDir, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
     700{
     701    /* Validate input */
     702    AssertPtrReturn(pszTarFile, VERR_INVALID_POINTER);
     703    AssertPtrReturn(pszOutputDir, VERR_INVALID_POINTER);
     704
     705    char **papszFiles;
     706    size_t cFiles;
     707
     708    /* First fetch the files names contained in the tar file */
     709    int rc = RTTarList(pszTarFile, &papszFiles, &cFiles);
     710    if (RT_FAILURE(rc))
     711        return rc;
     712
     713    /* Extract all files */
     714    return RTTarExtractFiles(pszTarFile, pszOutputDir, papszFiles, cFiles, pfnProgressCallback, pvUser);
     715}
     716
     717RTR3DECL(int) RTTarCreate(const char *pszTarFile, const char * const *papszFiles, size_t cFiles, PFNRTPROGRESS pfnProgressCallback, void *pvUser)
    626718{
    627719    /* Validate input */
     
    635727        return rc;
    636728
    637     for (size_t i = 0; i < cFiles; ++i)
    638     {
    639         rc = rtTarCopyFileTo(hFile, papszFiles[i]);
    640         if (RT_FAILURE(rc))
    641             break;
    642     }
    643 
    644     /* gtar gives a warning, but the documentation says EOF is indicated by a
    645      * zero block. Disabled for now. */
     729    /* Get the overall size of all files to pack into the tar archive. Only
     730       necessary if there is a progress callback. */
     731    uint64_t cbOverallSize = 0;
     732    if (pfnProgressCallback)
     733        for (size_t i = 0; i < cFiles; ++i)
     734        {
     735            uint64_t cbSize;
     736            rc = RTFileQuerySize(papszFiles[i], &cbSize);
     737            if (RT_FAILURE(rc))
     738                break;
     739            cbOverallSize += cbSize;
     740        }
     741
     742    if (RT_SUCCESS(rc))
     743    {
     744        uint64_t cbOverallWritten = 0;
     745
     746        for (size_t i = 0; i < cFiles; ++i)
     747        {
     748            rc = rtTarCopyFileTo(hFile, papszFiles[i], cbOverallSize, cbOverallWritten, pfnProgressCallback, pvUser);
     749            if (RT_FAILURE(rc))
     750                break;
     751        }
     752
     753        /* gtar gives a warning, but the documentation says EOF is indicated by a
     754         * zero block. Disabled for now. */
    646755#if 0
    647     if (RT_SUCCESS(rc))
    648     {
    649         /* Append the EOF record which is filled all by zeros */
    650         RTTARRECORD record;
    651         ASMMemFill32(&record, sizeof(record), 0);
    652         rc = RTFileWrite(hFile, &record, sizeof(record), NULL);
    653     }
     756        if (RT_SUCCESS(rc))
     757        {
     758            /* Append the EOF record which is filled all by zeros */
     759            RTTARRECORD record;
     760            ASMMemFill32(&record, sizeof(record), 0);
     761            rc = RTFileWrite(hFile, &record, sizeof(record), NULL);
     762        }
    654763#endif
     764    }
    655765
    656766    /* Time to close the new tar archive */
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