Home | History | Annotate | Download | only in Common
      1 // Extract.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "../../../../C/Sort.h"
      6 
      7 #include "../../../Common/StringConvert.h"
      8 
      9 #include "../../../Windows/FileDir.h"
     10 #include "../../../Windows/PropVariant.h"
     11 #include "../../../Windows/PropVariantConv.h"
     12 
     13 #include "../Common/ExtractingFilePath.h"
     14 
     15 #include "Extract.h"
     16 #include "SetProperties.h"
     17 
     18 using namespace NWindows;
     19 using namespace NFile;
     20 using namespace NDir;
     21 
     22 static HRESULT DecompressArchive(
     23     CCodecs *codecs,
     24     const CArchiveLink &arcLink,
     25     UInt64 packSize,
     26     const NWildcard::CCensorNode &wildcardCensor,
     27     const CExtractOptions &options,
     28     bool calcCrc,
     29     IExtractCallbackUI *callback,
     30     CArchiveExtractCallback *ecs,
     31     UString &errorMessage,
     32     UInt64 &stdInProcessed)
     33 {
     34   const CArc &arc = arcLink.Arcs.Back();
     35   stdInProcessed = 0;
     36   IInArchive *archive = arc.Archive;
     37   CRecordVector<UInt32> realIndices;
     38 
     39   UStringVector removePathParts;
     40 
     41   FString outDir = options.OutputDir;
     42   UString replaceName = arc.DefaultName;
     43 
     44   if (arcLink.Arcs.Size() > 1)
     45   {
     46     // Most "pe" archives have same name of archive subfile "[0]" or ".rsrc_1".
     47     // So it extracts different archives to one folder.
     48     // We will use top level archive name
     49     const CArc &arc0 = arcLink.Arcs[0];
     50     if (StringsAreEqualNoCase_Ascii(codecs->Formats[arc0.FormatIndex].Name, "pe"))
     51       replaceName = arc0.DefaultName;
     52   }
     53 
     54   outDir.Replace(FString("*"), us2fs(Get_Correct_FsFile_Name(replaceName)));
     55 
     56   bool elimIsPossible = false;
     57   UString elimPrefix; // only pure name without dir delimiter
     58   FString outDirReduced = outDir;
     59 
     60   if (options.ElimDup.Val && options.PathMode != NExtract::NPathMode::kAbsPaths)
     61   {
     62     UString dirPrefix;
     63     SplitPathToParts_Smart(fs2us(outDir), dirPrefix, elimPrefix);
     64     if (!elimPrefix.IsEmpty())
     65     {
     66       if (IsPathSepar(elimPrefix.Back()))
     67         elimPrefix.DeleteBack();
     68       if (!elimPrefix.IsEmpty())
     69       {
     70         outDirReduced = us2fs(dirPrefix);
     71         elimIsPossible = true;
     72       }
     73     }
     74   }
     75 
     76   bool allFilesAreAllowed = wildcardCensor.AreAllAllowed();
     77 
     78   if (!options.StdInMode)
     79   {
     80     UInt32 numItems;
     81     RINOK(archive->GetNumberOfItems(&numItems));
     82 
     83     CReadArcItem item;
     84 
     85     for (UInt32 i = 0; i < numItems; i++)
     86     {
     87       if (elimIsPossible || !allFilesAreAllowed)
     88       {
     89         RINOK(arc.GetItem(i, item));
     90       }
     91       else
     92       {
     93         #ifdef SUPPORT_ALT_STREAMS
     94         item.IsAltStream = false;
     95         if (!options.NtOptions.AltStreams.Val && arc.Ask_AltStream)
     96         {
     97           RINOK(Archive_IsItem_AltStream(arc.Archive, i, item.IsAltStream));
     98         }
     99         #endif
    100       }
    101 
    102       #ifdef SUPPORT_ALT_STREAMS
    103       if (!options.NtOptions.AltStreams.Val && item.IsAltStream)
    104         continue;
    105       #endif
    106 
    107       if (elimIsPossible)
    108       {
    109         const UString &s =
    110           #ifdef SUPPORT_ALT_STREAMS
    111             item.MainPath;
    112           #else
    113             item.Path;
    114           #endif
    115         if (!IsPath1PrefixedByPath2(s, elimPrefix))
    116           elimIsPossible = false;
    117         else
    118         {
    119           wchar_t c = s[elimPrefix.Len()];
    120           if (c == 0)
    121           {
    122             if (!item.MainIsDir)
    123               elimIsPossible = false;
    124           }
    125           else if (!IsPathSepar(c))
    126             elimIsPossible = false;
    127         }
    128       }
    129 
    130       if (!allFilesAreAllowed)
    131       {
    132         if (!CensorNode_CheckPath(wildcardCensor, item))
    133           continue;
    134       }
    135 
    136       realIndices.Add(i);
    137     }
    138 
    139     if (realIndices.Size() == 0)
    140     {
    141       callback->ThereAreNoFiles();
    142       return callback->ExtractResult(S_OK);
    143     }
    144   }
    145 
    146   if (elimIsPossible)
    147   {
    148     removePathParts.Add(elimPrefix);
    149     // outDir = outDirReduced;
    150   }
    151 
    152   #ifdef _WIN32
    153   // GetCorrectFullFsPath doesn't like "..".
    154   // outDir.TrimRight();
    155   // outDir = GetCorrectFullFsPath(outDir);
    156   #endif
    157 
    158   if (outDir.IsEmpty())
    159     outDir = "." STRING_PATH_SEPARATOR;
    160   /*
    161   #ifdef _WIN32
    162   else if (NName::IsAltPathPrefix(outDir)) {}
    163   #endif
    164   */
    165   else if (!CreateComplexDir(outDir))
    166   {
    167     HRESULT res = ::GetLastError();
    168     if (res == S_OK)
    169       res = E_FAIL;
    170     errorMessage = "Can not create output directory: ";
    171     errorMessage += fs2us(outDir);
    172     return res;
    173   }
    174 
    175   ecs->Init(
    176       options.NtOptions,
    177       options.StdInMode ? &wildcardCensor : NULL,
    178       &arc,
    179       callback,
    180       options.StdOutMode, options.TestMode,
    181       outDir,
    182       removePathParts, false,
    183       packSize);
    184 
    185 
    186   #ifdef SUPPORT_LINKS
    187 
    188   if (!options.StdInMode &&
    189       !options.TestMode &&
    190       options.NtOptions.HardLinks.Val)
    191   {
    192     RINOK(ecs->PrepareHardLinks(&realIndices));
    193   }
    194 
    195   #endif
    196 
    197 
    198   HRESULT result;
    199   Int32 testMode = (options.TestMode && !calcCrc) ? 1: 0;
    200 
    201   CArchiveExtractCallback_Closer ecsCloser(ecs);
    202 
    203   if (options.StdInMode)
    204   {
    205     result = archive->Extract(NULL, (UInt32)(Int32)-1, testMode, ecs);
    206     NCOM::CPropVariant prop;
    207     if (archive->GetArchiveProperty(kpidPhySize, &prop) == S_OK)
    208       ConvertPropVariantToUInt64(prop, stdInProcessed);
    209   }
    210   else
    211     result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, ecs);
    212 
    213   HRESULT res2 = ecsCloser.Close();
    214   if (result == S_OK)
    215     result = res2;
    216 
    217   return callback->ExtractResult(result);
    218 }
    219 
    220 /* v9.31: BUG was fixed:
    221    Sorted list for file paths was sorted with case insensitive compare function.
    222    But FindInSorted function did binary search via case sensitive compare function */
    223 
    224 int Find_FileName_InSortedVector(const UStringVector &fileName, const UString &name)
    225 {
    226   unsigned left = 0, right = fileName.Size();
    227   while (left != right)
    228   {
    229     unsigned mid = (left + right) / 2;
    230     const UString &midValue = fileName[mid];
    231     int compare = CompareFileNames(name, midValue);
    232     if (compare == 0)
    233       return mid;
    234     if (compare < 0)
    235       right = mid;
    236     else
    237       left = mid + 1;
    238   }
    239   return -1;
    240 }
    241 
    242 HRESULT Extract(
    243     CCodecs *codecs,
    244     const CObjectVector<COpenType> &types,
    245     const CIntVector &excludedFormats,
    246     UStringVector &arcPaths, UStringVector &arcPathsFull,
    247     const NWildcard::CCensorNode &wildcardCensor,
    248     const CExtractOptions &options,
    249     IOpenCallbackUI *openCallback,
    250     IExtractCallbackUI *extractCallback,
    251     #ifndef _SFX
    252     IHashCalc *hash,
    253     #endif
    254     UString &errorMessage,
    255     CDecompressStat &st)
    256 {
    257   st.Clear();
    258   UInt64 totalPackSize = 0;
    259   CRecordVector<UInt64> arcSizes;
    260 
    261   unsigned numArcs = options.StdInMode ? 1 : arcPaths.Size();
    262 
    263   unsigned i;
    264 
    265   for (i = 0; i < numArcs; i++)
    266   {
    267     NFind::CFileInfo fi;
    268     fi.Size = 0;
    269     if (!options.StdInMode)
    270     {
    271       const FString &arcPath = us2fs(arcPaths[i]);
    272       if (!fi.Find(arcPath))
    273         throw "there is no such archive";
    274       if (fi.IsDir())
    275         throw "can't decompress folder";
    276     }
    277     arcSizes.Add(fi.Size);
    278     totalPackSize += fi.Size;
    279   }
    280 
    281   CBoolArr skipArcs(numArcs);
    282   for (i = 0; i < numArcs; i++)
    283     skipArcs[i] = false;
    284 
    285   CArchiveExtractCallback *ecs = new CArchiveExtractCallback;
    286   CMyComPtr<IArchiveExtractCallback> ec(ecs);
    287   bool multi = (numArcs > 1);
    288   ecs->InitForMulti(multi, options.PathMode, options.OverwriteMode,
    289       false // keepEmptyDirParts
    290       );
    291   #ifndef _SFX
    292   ecs->SetHashMethods(hash);
    293   #endif
    294 
    295   if (multi)
    296   {
    297     RINOK(extractCallback->SetTotal(totalPackSize));
    298   }
    299 
    300   UInt64 totalPackProcessed = 0;
    301   bool thereAreNotOpenArcs = false;
    302 
    303   for (i = 0; i < numArcs; i++)
    304   {
    305     if (skipArcs[i])
    306       continue;
    307 
    308     const UString &arcPath = arcPaths[i];
    309     NFind::CFileInfo fi;
    310     if (options.StdInMode)
    311     {
    312       fi.Size = 0;
    313       fi.Attrib = 0;
    314     }
    315     else
    316     {
    317       if (!fi.Find(us2fs(arcPath)) || fi.IsDir())
    318         throw "there is no such archive";
    319     }
    320 
    321     /*
    322     #ifndef _NO_CRYPTO
    323     openCallback->Open_Clear_PasswordWasAsked_Flag();
    324     #endif
    325     */
    326 
    327     RINOK(extractCallback->BeforeOpen(arcPath, options.TestMode));
    328     CArchiveLink arcLink;
    329 
    330     CObjectVector<COpenType> types2 = types;
    331     /*
    332     #ifndef _SFX
    333     if (types.IsEmpty())
    334     {
    335       int pos = arcPath.ReverseFind(L'.');
    336       if (pos >= 0)
    337       {
    338         UString s = arcPath.Ptr(pos + 1);
    339         int index = codecs->FindFormatForExtension(s);
    340         if (index >= 0 && s == L"001")
    341         {
    342           s = arcPath.Left(pos);
    343           pos = s.ReverseFind(L'.');
    344           if (pos >= 0)
    345           {
    346             int index2 = codecs->FindFormatForExtension(s.Ptr(pos + 1));
    347             if (index2 >= 0) // && s.CompareNoCase(L"rar") != 0
    348             {
    349               types2.Add(index2);
    350               types2.Add(index);
    351             }
    352           }
    353         }
    354       }
    355     }
    356     #endif
    357     */
    358 
    359     COpenOptions op;
    360     #ifndef _SFX
    361     op.props = &options.Properties;
    362     #endif
    363     op.codecs = codecs;
    364     op.types = &types2;
    365     op.excludedFormats = &excludedFormats;
    366     op.stdInMode = options.StdInMode;
    367     op.stream = NULL;
    368     op.filePath = arcPath;
    369 
    370     HRESULT result = arcLink.Open_Strict(op, openCallback);
    371 
    372     if (result == E_ABORT)
    373       return result;
    374 
    375     // arcLink.Set_ErrorsText();
    376     RINOK(extractCallback->OpenResult(codecs, arcLink, arcPath, result));
    377 
    378     if (result != S_OK)
    379     {
    380       thereAreNotOpenArcs = true;
    381       if (!options.StdInMode)
    382       {
    383         NFind::CFileInfo fi2;
    384         if (fi2.Find(us2fs(arcPath)))
    385           if (!fi2.IsDir())
    386             totalPackProcessed += fi2.Size;
    387       }
    388       continue;
    389     }
    390 
    391     if (!options.StdInMode)
    392     {
    393       // numVolumes += arcLink.VolumePaths.Size();
    394       // arcLink.VolumesSize;
    395 
    396       // totalPackSize -= DeleteUsedFileNamesFromList(arcLink, i + 1, arcPaths, arcPathsFull, &arcSizes);
    397       // numArcs = arcPaths.Size();
    398       if (arcLink.VolumePaths.Size() != 0)
    399       {
    400         Int64 correctionSize = arcLink.VolumesSize;
    401         FOR_VECTOR (v, arcLink.VolumePaths)
    402         {
    403           int index = Find_FileName_InSortedVector(arcPathsFull, arcLink.VolumePaths[v]);
    404           if (index >= 0)
    405           {
    406             if ((unsigned)index > i)
    407             {
    408               skipArcs[(unsigned)index] = true;
    409               correctionSize -= arcSizes[(unsigned)index];
    410             }
    411           }
    412         }
    413         if (correctionSize != 0)
    414         {
    415           Int64 newPackSize = (Int64)totalPackSize + correctionSize;
    416           if (newPackSize < 0)
    417             newPackSize = 0;
    418           totalPackSize = newPackSize;
    419           RINOK(extractCallback->SetTotal(totalPackSize));
    420         }
    421       }
    422     }
    423 
    424     /*
    425     // Now openCallback and extractCallback use same object. So we don't need to send password.
    426 
    427     #ifndef _NO_CRYPTO
    428     bool passwordIsDefined;
    429     UString password;
    430     RINOK(openCallback->Open_GetPasswordIfAny(passwordIsDefined, password));
    431     if (passwordIsDefined)
    432     {
    433       RINOK(extractCallback->SetPassword(password));
    434     }
    435     #endif
    436     */
    437 
    438     CArc &arc = arcLink.Arcs.Back();
    439     arc.MTimeDefined = (!options.StdInMode && !fi.IsDevice);
    440     arc.MTime = fi.MTime;
    441 
    442     UInt64 packProcessed;
    443     bool calcCrc =
    444         #ifndef _SFX
    445           (hash != NULL);
    446         #else
    447           false;
    448         #endif
    449 
    450     RINOK(DecompressArchive(
    451         codecs,
    452         arcLink,
    453         fi.Size + arcLink.VolumesSize,
    454         wildcardCensor,
    455         options,
    456         calcCrc,
    457         extractCallback, ecs, errorMessage, packProcessed));
    458 
    459     if (!options.StdInMode)
    460       packProcessed = fi.Size + arcLink.VolumesSize;
    461     totalPackProcessed += packProcessed;
    462     ecs->LocalProgressSpec->InSize += packProcessed;
    463     ecs->LocalProgressSpec->OutSize = ecs->UnpackSize;
    464     if (!errorMessage.IsEmpty())
    465       return E_FAIL;
    466   }
    467 
    468   if (multi || thereAreNotOpenArcs)
    469   {
    470     RINOK(extractCallback->SetTotal(totalPackSize));
    471     RINOK(extractCallback->SetCompleted(&totalPackProcessed));
    472   }
    473 
    474   st.NumFolders = ecs->NumFolders;
    475   st.NumFiles = ecs->NumFiles;
    476   st.NumAltStreams = ecs->NumAltStreams;
    477   st.UnpackSize = ecs->UnpackSize;
    478   st.AltStreams_UnpackSize = ecs->AltStreams_UnpackSize;
    479   st.NumArchives = arcPaths.Size();
    480   st.PackSize = ecs->LocalProgressSpec->InSize;
    481   return S_OK;
    482 }
    483