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