• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DecompressArchive(CCodecs * codecs,const CArchiveLink & arcLink,UInt64 packSize,const NWildcard::CCensorNode & wildcardCensor,const CExtractOptions & options,bool calcCrc,IExtractCallbackUI * callback,CArchiveExtractCallback * ecs,UString & errorMessage,UInt64 & stdInProcessed)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 
Find_FileName_InSortedVector(const UStringVector & fileName,const UString & name)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 
Extract(CCodecs * codecs,const CObjectVector<COpenType> & types,const CIntVector & excludedFormats,UStringVector & arcPaths,UStringVector & arcPathsFull,const NWildcard::CCensorNode & wildcardCensor,const CExtractOptions & options,IOpenCallbackUI * openCallback,IExtractCallbackUI * extractCallback,IHashCalc * hash,UString & errorMessage,CDecompressStat & st)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