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