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_ANY_MASK, 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 = FTEXT(".") FSTRING_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.SetFromAscii("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 if (options.StdInMode)
201 {
202 result = archive->Extract(NULL, (UInt32)(Int32)-1, testMode, ecs);
203 NCOM::CPropVariant prop;
204 if (archive->GetArchiveProperty(kpidPhySize, &prop) == S_OK)
205 ConvertPropVariantToUInt64(prop, stdInProcessed);
206 }
207 else
208 result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, ecs);
209 if (result == S_OK && !options.StdInMode)
210 result = ecs->SetDirsTimes();
211 return callback->ExtractResult(result);
212 }
213
214 /* v9.31: BUG was fixed:
215 Sorted list for file paths was sorted with case insensitive compare function.
216 But FindInSorted function did binary search via case sensitive compare function */
217
Find_FileName_InSortedVector(const UStringVector & fileName,const UString & name)218 int Find_FileName_InSortedVector(const UStringVector &fileName, const UString &name)
219 {
220 unsigned left = 0, right = fileName.Size();
221 while (left != right)
222 {
223 unsigned mid = (left + right) / 2;
224 const UString &midValue = fileName[mid];
225 int compare = CompareFileNames(name, midValue);
226 if (compare == 0)
227 return mid;
228 if (compare < 0)
229 right = mid;
230 else
231 left = mid + 1;
232 }
233 return -1;
234 }
235
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)236 HRESULT Extract(
237 CCodecs *codecs,
238 const CObjectVector<COpenType> &types,
239 const CIntVector &excludedFormats,
240 UStringVector &arcPaths, UStringVector &arcPathsFull,
241 const NWildcard::CCensorNode &wildcardCensor,
242 const CExtractOptions &options,
243 IOpenCallbackUI *openCallback,
244 IExtractCallbackUI *extractCallback,
245 #ifndef _SFX
246 IHashCalc *hash,
247 #endif
248 UString &errorMessage,
249 CDecompressStat &st)
250 {
251 st.Clear();
252 UInt64 totalPackSize = 0;
253 CRecordVector<UInt64> arcSizes;
254
255 unsigned numArcs = options.StdInMode ? 1 : arcPaths.Size();
256
257 unsigned i;
258
259 for (i = 0; i < numArcs; i++)
260 {
261 NFind::CFileInfo fi;
262 fi.Size = 0;
263 if (!options.StdInMode)
264 {
265 const FString &arcPath = us2fs(arcPaths[i]);
266 if (!fi.Find(arcPath))
267 throw "there is no such archive";
268 if (fi.IsDir())
269 throw "can't decompress folder";
270 }
271 arcSizes.Add(fi.Size);
272 totalPackSize += fi.Size;
273 }
274
275 CBoolArr skipArcs(numArcs);
276 for (i = 0; i < numArcs; i++)
277 skipArcs[i] = false;
278
279 CArchiveExtractCallback *ecs = new CArchiveExtractCallback;
280 CMyComPtr<IArchiveExtractCallback> ec(ecs);
281 bool multi = (numArcs > 1);
282 ecs->InitForMulti(multi, options.PathMode, options.OverwriteMode);
283 #ifndef _SFX
284 ecs->SetHashMethods(hash);
285 #endif
286
287 if (multi)
288 {
289 RINOK(extractCallback->SetTotal(totalPackSize));
290 }
291
292 UInt64 totalPackProcessed = 0;
293 bool thereAreNotOpenArcs = false;
294
295 for (i = 0; i < numArcs; i++)
296 {
297 if (skipArcs[i])
298 continue;
299
300 const UString &arcPath = arcPaths[i];
301 NFind::CFileInfo fi;
302 if (options.StdInMode)
303 {
304 fi.Size = 0;
305 fi.Attrib = 0;
306 }
307 else
308 {
309 if (!fi.Find(us2fs(arcPath)) || fi.IsDir())
310 throw "there is no such archive";
311 }
312
313 /*
314 #ifndef _NO_CRYPTO
315 openCallback->Open_Clear_PasswordWasAsked_Flag();
316 #endif
317 */
318
319 RINOK(extractCallback->BeforeOpen(arcPath, options.TestMode));
320 CArchiveLink arcLink;
321
322 CObjectVector<COpenType> types2 = types;
323 /*
324 #ifndef _SFX
325 if (types.IsEmpty())
326 {
327 int pos = arcPath.ReverseFind(L'.');
328 if (pos >= 0)
329 {
330 UString s = arcPath.Ptr(pos + 1);
331 int index = codecs->FindFormatForExtension(s);
332 if (index >= 0 && s == L"001")
333 {
334 s = arcPath.Left(pos);
335 pos = s.ReverseFind(L'.');
336 if (pos >= 0)
337 {
338 int index2 = codecs->FindFormatForExtension(s.Ptr(pos + 1));
339 if (index2 >= 0) // && s.CompareNoCase(L"rar") != 0
340 {
341 types2.Add(index2);
342 types2.Add(index);
343 }
344 }
345 }
346 }
347 }
348 #endif
349 */
350
351 COpenOptions op;
352 #ifndef _SFX
353 op.props = &options.Properties;
354 #endif
355 op.codecs = codecs;
356 op.types = &types2;
357 op.excludedFormats = &excludedFormats;
358 op.stdInMode = options.StdInMode;
359 op.stream = NULL;
360 op.filePath = arcPath;
361
362 HRESULT result = arcLink.Open_Strict(op, openCallback);
363
364 if (result == E_ABORT)
365 return result;
366
367 // arcLink.Set_ErrorsText();
368 RINOK(extractCallback->OpenResult(codecs, arcLink, arcPath, result));
369
370 if (result != S_OK)
371 {
372 thereAreNotOpenArcs = true;
373 if (!options.StdInMode)
374 {
375 NFind::CFileInfo fi2;
376 if (fi2.Find(us2fs(arcPath)))
377 if (!fi2.IsDir())
378 totalPackProcessed += fi2.Size;
379 }
380 continue;
381 }
382
383 if (!options.StdInMode)
384 {
385 // numVolumes += arcLink.VolumePaths.Size();
386 // arcLink.VolumesSize;
387
388 // totalPackSize -= DeleteUsedFileNamesFromList(arcLink, i + 1, arcPaths, arcPathsFull, &arcSizes);
389 // numArcs = arcPaths.Size();
390 if (arcLink.VolumePaths.Size() != 0)
391 {
392 Int64 correctionSize = arcLink.VolumesSize;
393 FOR_VECTOR (v, arcLink.VolumePaths)
394 {
395 int index = Find_FileName_InSortedVector(arcPathsFull, arcLink.VolumePaths[v]);
396 if (index >= 0)
397 {
398 if ((unsigned)index > i)
399 {
400 skipArcs[(unsigned)index] = true;
401 correctionSize -= arcSizes[(unsigned)index];
402 }
403 }
404 }
405 if (correctionSize != 0)
406 {
407 Int64 newPackSize = (Int64)totalPackSize + correctionSize;
408 if (newPackSize < 0)
409 newPackSize = 0;
410 totalPackSize = newPackSize;
411 RINOK(extractCallback->SetTotal(totalPackSize));
412 }
413 }
414 }
415
416 /*
417 // Now openCallback and extractCallback use same object. So we don't need to send password.
418
419 #ifndef _NO_CRYPTO
420 bool passwordIsDefined;
421 UString password;
422 RINOK(openCallback->Open_GetPasswordIfAny(passwordIsDefined, password));
423 if (passwordIsDefined)
424 {
425 RINOK(extractCallback->SetPassword(password));
426 }
427 #endif
428 */
429
430 CArc &arc = arcLink.Arcs.Back();
431 arc.MTimeDefined = (!options.StdInMode && !fi.IsDevice);
432 arc.MTime = fi.MTime;
433
434 UInt64 packProcessed;
435 bool calcCrc =
436 #ifndef _SFX
437 (hash != NULL);
438 #else
439 false;
440 #endif
441
442 RINOK(DecompressArchive(
443 codecs,
444 arcLink,
445 fi.Size + arcLink.VolumesSize,
446 wildcardCensor,
447 options,
448 calcCrc,
449 extractCallback, ecs, errorMessage, packProcessed));
450
451 if (!options.StdInMode)
452 packProcessed = fi.Size + arcLink.VolumesSize;
453 totalPackProcessed += packProcessed;
454 ecs->LocalProgressSpec->InSize += packProcessed;
455 ecs->LocalProgressSpec->OutSize = ecs->UnpackSize;
456 if (!errorMessage.IsEmpty())
457 return E_FAIL;
458 }
459
460 if (multi || thereAreNotOpenArcs)
461 {
462 RINOK(extractCallback->SetTotal(totalPackSize));
463 RINOK(extractCallback->SetCompleted(&totalPackProcessed));
464 }
465
466 st.NumFolders = ecs->NumFolders;
467 st.NumFiles = ecs->NumFiles;
468 st.NumAltStreams = ecs->NumAltStreams;
469 st.UnpackSize = ecs->UnpackSize;
470 st.AltStreams_UnpackSize = ecs->AltStreams_UnpackSize;
471 st.NumArchives = arcPaths.Size();
472 st.PackSize = ecs->LocalProgressSpec->InSize;
473 return S_OK;
474 }
475