7zExtract.cpp (10540B)
1 // 7zExtract.cpp 2 3 #include "StdAfx.h" 4 5 #include "../../../../C/7zCrc.h" 6 7 #include "../../../Common/ComTry.h" 8 9 #include "../../Common/ProgressUtils.h" 10 11 #include "7zDecode.h" 12 #include "7zHandler.h" 13 14 // EXTERN_g_ExternalCodecs 15 16 namespace NArchive { 17 namespace N7z { 18 19 class CFolderOutStream: 20 public ISequentialOutStream, 21 public CMyUnknownImp 22 { 23 CMyComPtr<ISequentialOutStream> _stream; 24 public: 25 bool TestMode; 26 bool CheckCrc; 27 private: 28 bool _fileIsOpen; 29 bool _calcCrc; 30 UInt32 _crc; 31 UInt64 _rem; 32 33 const UInt32 *_indexes; 34 unsigned _numFiles; 35 unsigned _fileIndex; 36 37 HRESULT OpenFile(bool isCorrupted = false); 38 HRESULT CloseFile_and_SetResult(Int32 res); 39 HRESULT CloseFile(); 40 HRESULT ProcessEmptyFiles(); 41 42 public: 43 MY_UNKNOWN_IMP1(ISequentialOutStream) 44 45 const CDbEx *_db; 46 CMyComPtr<IArchiveExtractCallback> ExtractCallback; 47 48 bool ExtraWriteWasCut; 49 50 CFolderOutStream(): 51 TestMode(false), 52 CheckCrc(true) 53 {} 54 55 STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); 56 57 HRESULT Init(unsigned startIndex, const UInt32 *indexes, unsigned numFiles); 58 HRESULT FlushCorrupted(Int32 callbackOperationResult); 59 60 bool WasWritingFinished() const { return _numFiles == 0; } 61 }; 62 63 64 HRESULT CFolderOutStream::Init(unsigned startIndex, const UInt32 *indexes, unsigned numFiles) 65 { 66 _fileIndex = startIndex; 67 _indexes = indexes; 68 _numFiles = numFiles; 69 70 _fileIsOpen = false; 71 ExtraWriteWasCut = false; 72 73 return ProcessEmptyFiles(); 74 } 75 76 HRESULT CFolderOutStream::OpenFile(bool isCorrupted) 77 { 78 const CFileItem &fi = _db->Files[_fileIndex]; 79 UInt32 nextFileIndex = (_indexes ? *_indexes : _fileIndex); 80 Int32 askMode = (_fileIndex == nextFileIndex) ? 81 (TestMode ? 82 NExtract::NAskMode::kTest : 83 NExtract::NAskMode::kExtract) : 84 NExtract::NAskMode::kSkip; 85 86 if (isCorrupted 87 && askMode == NExtract::NAskMode::kExtract 88 && !_db->IsItemAnti(_fileIndex) 89 && !fi.IsDir) 90 askMode = NExtract::NAskMode::kTest; 91 92 CMyComPtr<ISequentialOutStream> realOutStream; 93 RINOK(ExtractCallback->GetStream(_fileIndex, &realOutStream, askMode)); 94 95 _stream = realOutStream; 96 _crc = CRC_INIT_VAL; 97 _calcCrc = (CheckCrc && fi.CrcDefined && !fi.IsDir); 98 99 _fileIsOpen = true; 100 _rem = fi.Size; 101 102 if (askMode == NExtract::NAskMode::kExtract 103 && !realOutStream 104 && !_db->IsItemAnti(_fileIndex) 105 && !fi.IsDir) 106 askMode = NExtract::NAskMode::kSkip; 107 return ExtractCallback->PrepareOperation(askMode); 108 } 109 110 HRESULT CFolderOutStream::CloseFile_and_SetResult(Int32 res) 111 { 112 _stream.Release(); 113 _fileIsOpen = false; 114 115 if (!_indexes) 116 _numFiles--; 117 else if (*_indexes == _fileIndex) 118 { 119 _indexes++; 120 _numFiles--; 121 } 122 123 _fileIndex++; 124 return ExtractCallback->SetOperationResult(res); 125 } 126 127 HRESULT CFolderOutStream::CloseFile() 128 { 129 const CFileItem &fi = _db->Files[_fileIndex]; 130 return CloseFile_and_SetResult((!_calcCrc || fi.Crc == CRC_GET_DIGEST(_crc)) ? 131 NExtract::NOperationResult::kOK : 132 NExtract::NOperationResult::kCRCError); 133 } 134 135 HRESULT CFolderOutStream::ProcessEmptyFiles() 136 { 137 while (_numFiles != 0 && _db->Files[_fileIndex].Size == 0) 138 { 139 RINOK(OpenFile()); 140 RINOK(CloseFile()); 141 } 142 return S_OK; 143 } 144 145 STDMETHODIMP CFolderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) 146 { 147 if (processedSize) 148 *processedSize = 0; 149 150 while (size != 0) 151 { 152 if (_fileIsOpen) 153 { 154 UInt32 cur = (size < _rem ? size : (UInt32)_rem); 155 if (_calcCrc) 156 { 157 const UInt32 k_Step = (UInt32)1 << 20; 158 if (cur > k_Step) 159 cur = k_Step; 160 } 161 HRESULT result = S_OK; 162 if (_stream) 163 result = _stream->Write(data, cur, &cur); 164 if (_calcCrc) 165 _crc = CrcUpdate(_crc, data, cur); 166 if (processedSize) 167 *processedSize += cur; 168 data = (const Byte *)data + cur; 169 size -= cur; 170 _rem -= cur; 171 if (_rem == 0) 172 { 173 RINOK(CloseFile()); 174 RINOK(ProcessEmptyFiles()); 175 } 176 RINOK(result); 177 if (cur == 0) 178 break; 179 continue; 180 } 181 182 RINOK(ProcessEmptyFiles()); 183 if (_numFiles == 0) 184 { 185 // we support partial extracting 186 /* 187 if (processedSize) 188 *processedSize += size; 189 break; 190 */ 191 ExtraWriteWasCut = true; 192 // return S_FALSE; 193 return k_My_HRESULT_WritingWasCut; 194 } 195 RINOK(OpenFile()); 196 } 197 198 return S_OK; 199 } 200 201 HRESULT CFolderOutStream::FlushCorrupted(Int32 callbackOperationResult) 202 { 203 while (_numFiles != 0) 204 { 205 if (_fileIsOpen) 206 { 207 RINOK(CloseFile_and_SetResult(callbackOperationResult)); 208 } 209 else 210 { 211 RINOK(OpenFile(true)); 212 } 213 } 214 return S_OK; 215 } 216 217 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, 218 Int32 testModeSpec, IArchiveExtractCallback *extractCallbackSpec) 219 { 220 COM_TRY_BEGIN 221 222 CMyComPtr<IArchiveExtractCallback> extractCallback = extractCallbackSpec; 223 224 UInt64 importantTotalUnpacked = 0; 225 226 // numItems = (UInt32)(Int32)-1; 227 228 bool allFilesMode = (numItems == (UInt32)(Int32)-1); 229 if (allFilesMode) 230 numItems = _db.Files.Size(); 231 232 if (numItems == 0) 233 return S_OK; 234 235 { 236 CNum prevFolder = kNumNoIndex; 237 UInt32 nextFile = 0; 238 239 UInt32 i; 240 241 for (i = 0; i < numItems; i++) 242 { 243 UInt32 fileIndex = allFilesMode ? i : indices[i]; 244 CNum folderIndex = _db.FileIndexToFolderIndexMap[fileIndex]; 245 if (folderIndex == kNumNoIndex) 246 continue; 247 if (folderIndex != prevFolder || fileIndex < nextFile) 248 nextFile = _db.FolderStartFileIndex[folderIndex]; 249 for (CNum index = nextFile; index <= fileIndex; index++) 250 importantTotalUnpacked += _db.Files[index].Size; 251 nextFile = fileIndex + 1; 252 prevFolder = folderIndex; 253 } 254 } 255 256 RINOK(extractCallback->SetTotal(importantTotalUnpacked)); 257 258 CLocalProgress *lps = new CLocalProgress; 259 CMyComPtr<ICompressProgressInfo> progress = lps; 260 lps->Init(extractCallback, false); 261 262 CDecoder decoder( 263 #if !defined(USE_MIXER_MT) 264 false 265 #elif !defined(USE_MIXER_ST) 266 true 267 #elif !defined(__7Z_SET_PROPERTIES) 268 #ifdef _7ZIP_ST 269 false 270 #else 271 true 272 #endif 273 #else 274 _useMultiThreadMixer 275 #endif 276 ); 277 278 UInt64 curPacked, curUnpacked; 279 280 CMyComPtr<IArchiveExtractCallbackMessage> callbackMessage; 281 extractCallback.QueryInterface(IID_IArchiveExtractCallbackMessage, &callbackMessage); 282 283 CFolderOutStream *folderOutStream = new CFolderOutStream; 284 CMyComPtr<ISequentialOutStream> outStream(folderOutStream); 285 286 folderOutStream->_db = &_db; 287 folderOutStream->ExtractCallback = extractCallback; 288 folderOutStream->TestMode = (testModeSpec != 0); 289 folderOutStream->CheckCrc = (_crcSize != 0); 290 291 for (UInt32 i = 0;; lps->OutSize += curUnpacked, lps->InSize += curPacked) 292 { 293 RINOK(lps->SetCur()); 294 295 if (i >= numItems) 296 break; 297 298 curUnpacked = 0; 299 curPacked = 0; 300 301 UInt32 fileIndex = allFilesMode ? i : indices[i]; 302 CNum folderIndex = _db.FileIndexToFolderIndexMap[fileIndex]; 303 304 UInt32 numSolidFiles = 1; 305 306 if (folderIndex != kNumNoIndex) 307 { 308 curPacked = _db.GetFolderFullPackSize(folderIndex); 309 UInt32 nextFile = fileIndex + 1; 310 fileIndex = _db.FolderStartFileIndex[folderIndex]; 311 UInt32 k; 312 313 for (k = i + 1; k < numItems; k++) 314 { 315 UInt32 fileIndex2 = allFilesMode ? k : indices[k]; 316 if (_db.FileIndexToFolderIndexMap[fileIndex2] != folderIndex 317 || fileIndex2 < nextFile) 318 break; 319 nextFile = fileIndex2 + 1; 320 } 321 322 numSolidFiles = k - i; 323 324 for (k = fileIndex; k < nextFile; k++) 325 curUnpacked += _db.Files[k].Size; 326 } 327 328 { 329 HRESULT result = folderOutStream->Init(fileIndex, 330 allFilesMode ? NULL : indices + i, 331 numSolidFiles); 332 333 i += numSolidFiles; 334 335 RINOK(result); 336 } 337 338 // to test solid block with zero unpacked size we disable that code 339 if (folderOutStream->WasWritingFinished()) 340 continue; 341 342 #ifndef _NO_CRYPTO 343 CMyComPtr<ICryptoGetTextPassword> getTextPassword; 344 if (extractCallback) 345 extractCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword); 346 #endif 347 348 try 349 { 350 #ifndef _NO_CRYPTO 351 bool isEncrypted = false; 352 bool passwordIsDefined = false; 353 UString password; 354 #endif 355 356 357 bool dataAfterEnd_Error = false; 358 359 HRESULT result = decoder.Decode( 360 EXTERNAL_CODECS_VARS 361 _inStream, 362 _db.ArcInfo.DataStartPosition, 363 _db, folderIndex, 364 &curUnpacked, 365 366 outStream, 367 progress, 368 NULL // *inStreamMainRes 369 , dataAfterEnd_Error 370 371 _7Z_DECODER_CRYPRO_VARS 372 #if !defined(_7ZIP_ST) 373 , true, _numThreads, _memUsage 374 #endif 375 ); 376 377 if (result == S_FALSE || result == E_NOTIMPL || dataAfterEnd_Error) 378 { 379 bool wasFinished = folderOutStream->WasWritingFinished(); 380 381 int resOp = NExtract::NOperationResult::kDataError; 382 383 if (result != S_FALSE) 384 { 385 if (result == E_NOTIMPL) 386 resOp = NExtract::NOperationResult::kUnsupportedMethod; 387 else if (wasFinished && dataAfterEnd_Error) 388 resOp = NExtract::NOperationResult::kDataAfterEnd; 389 } 390 391 RINOK(folderOutStream->FlushCorrupted(resOp)); 392 393 if (wasFinished) 394 { 395 // we don't show error, if it's after required files 396 if (/* !folderOutStream->ExtraWriteWasCut && */ callbackMessage) 397 { 398 RINOK(callbackMessage->ReportExtractResult(NEventIndexType::kBlockIndex, folderIndex, resOp)); 399 } 400 } 401 continue; 402 } 403 404 if (result != S_OK) 405 return result; 406 407 RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)); 408 continue; 409 } 410 catch(...) 411 { 412 RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)); 413 // continue; 414 return E_FAIL; 415 } 416 } 417 418 return S_OK; 419 420 COM_TRY_END 421 } 422 423 }}