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_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 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 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