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 HRESULT result = S_OK; 156 if (_stream) 157 result = _stream->Write(data, cur, &cur); 158 if (_calcCrc) 159 _crc = CrcUpdate(_crc, data, cur); 160 if (processedSize) 161 *processedSize += cur; 162 data = (const Byte *)data + cur; 163 size -= cur; 164 _rem -= cur; 165 if (_rem == 0) 166 { 167 RINOK(CloseFile()); 168 RINOK(ProcessEmptyFiles()); 169 } 170 RINOK(result); 171 if (cur == 0) 172 break; 173 continue; 174 } 175 176 RINOK(ProcessEmptyFiles()); 177 if (_numFiles == 0) 178 { 179 // we support partial extracting 180 /* 181 if (processedSize) 182 *processedSize += size; 183 break; 184 */ 185 ExtraWriteWasCut = true; 186 // return S_FALSE; 187 return k_My_HRESULT_WritingWasCut; 188 } 189 RINOK(OpenFile()); 190 } 191 192 return S_OK; 193 } 194 195 HRESULT CFolderOutStream::FlushCorrupted(Int32 callbackOperationResult) 196 { 197 while (_numFiles != 0) 198 { 199 if (_fileIsOpen) 200 { 201 RINOK(CloseFile_and_SetResult(callbackOperationResult)); 202 } 203 else 204 { 205 RINOK(OpenFile(true)); 206 } 207 } 208 return S_OK; 209 } 210 211 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, 212 Int32 testModeSpec, IArchiveExtractCallback *extractCallbackSpec) 213 { 214 COM_TRY_BEGIN 215 216 CMyComPtr<IArchiveExtractCallback> extractCallback = extractCallbackSpec; 217 218 UInt64 importantTotalUnpacked = 0; 219 220 // numItems = (UInt32)(Int32)-1; 221 222 bool allFilesMode = (numItems == (UInt32)(Int32)-1); 223 if (allFilesMode) 224 numItems = _db.Files.Size(); 225 226 if (numItems == 0) 227 return S_OK; 228 229 { 230 CNum prevFolder = kNumNoIndex; 231 UInt32 nextFile = 0; 232 233 UInt32 i; 234 235 for (i = 0; i < numItems; i++) 236 { 237 UInt32 fileIndex = allFilesMode ? i : indices[i]; 238 CNum folderIndex = _db.FileIndexToFolderIndexMap[fileIndex]; 239 if (folderIndex == kNumNoIndex) 240 continue; 241 if (folderIndex != prevFolder || fileIndex < nextFile) 242 nextFile = _db.FolderStartFileIndex[folderIndex]; 243 for (CNum index = nextFile; index <= fileIndex; index++) 244 importantTotalUnpacked += _db.Files[index].Size; 245 nextFile = fileIndex + 1; 246 prevFolder = folderIndex; 247 } 248 } 249 250 RINOK(extractCallback->SetTotal(importantTotalUnpacked)); 251 252 CLocalProgress *lps = new CLocalProgress; 253 CMyComPtr<ICompressProgressInfo> progress = lps; 254 lps->Init(extractCallback, false); 255 256 CDecoder decoder( 257 #if !defined(USE_MIXER_MT) 258 false 259 #elif !defined(USE_MIXER_ST) 260 true 261 #elif !defined(__7Z_SET_PROPERTIES) 262 #ifdef _7ZIP_ST 263 false 264 #else 265 true 266 #endif 267 #else 268 _useMultiThreadMixer 269 #endif 270 ); 271 272 UInt64 curPacked, curUnpacked; 273 274 CMyComPtr<IArchiveExtractCallbackMessage> callbackMessage; 275 extractCallback.QueryInterface(IID_IArchiveExtractCallbackMessage, &callbackMessage); 276 277 CFolderOutStream *folderOutStream = new CFolderOutStream; 278 CMyComPtr<ISequentialOutStream> outStream(folderOutStream); 279 280 folderOutStream->_db = &_db; 281 folderOutStream->ExtractCallback = extractCallback; 282 folderOutStream->TestMode = (testModeSpec != 0); 283 folderOutStream->CheckCrc = (_crcSize != 0); 284 285 for (UInt32 i = 0;; lps->OutSize += curUnpacked, lps->InSize += curPacked) 286 { 287 RINOK(lps->SetCur()); 288 289 if (i >= numItems) 290 break; 291 292 curUnpacked = 0; 293 curPacked = 0; 294 295 UInt32 fileIndex = allFilesMode ? i : indices[i]; 296 CNum folderIndex = _db.FileIndexToFolderIndexMap[fileIndex]; 297 298 UInt32 numSolidFiles = 1; 299 300 if (folderIndex != kNumNoIndex) 301 { 302 curPacked = _db.GetFolderFullPackSize(folderIndex); 303 UInt32 nextFile = fileIndex + 1; 304 fileIndex = _db.FolderStartFileIndex[folderIndex]; 305 UInt32 k; 306 307 for (k = i + 1; k < numItems; k++) 308 { 309 UInt32 fileIndex2 = allFilesMode ? k : indices[k]; 310 if (_db.FileIndexToFolderIndexMap[fileIndex2] != folderIndex 311 || fileIndex2 < nextFile) 312 break; 313 nextFile = fileIndex2 + 1; 314 } 315 316 numSolidFiles = k - i; 317 318 for (k = fileIndex; k < nextFile; k++) 319 curUnpacked += _db.Files[k].Size; 320 } 321 322 { 323 HRESULT result = folderOutStream->Init(fileIndex, 324 allFilesMode ? NULL : indices + i, 325 numSolidFiles); 326 327 i += numSolidFiles; 328 329 RINOK(result); 330 } 331 332 // to test solid block with zero unpacked size we disable that code 333 if (folderOutStream->WasWritingFinished()) 334 continue; 335 336 #ifndef _NO_CRYPTO 337 CMyComPtr<ICryptoGetTextPassword> getTextPassword; 338 if (extractCallback) 339 extractCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword); 340 #endif 341 342 try 343 { 344 #ifndef _NO_CRYPTO 345 bool isEncrypted = false; 346 bool passwordIsDefined = false; 347 UString password; 348 #endif 349 350 351 HRESULT result = decoder.Decode( 352 EXTERNAL_CODECS_VARS 353 _inStream, 354 _db.ArcInfo.DataStartPosition, 355 _db, folderIndex, 356 &curUnpacked, 357 358 outStream, 359 progress, 360 NULL // *inStreamMainRes 361 362 _7Z_DECODER_CRYPRO_VARS 363 #if !defined(_7ZIP_ST) && !defined(_SFX) 364 , true, _numThreads 365 #endif 366 ); 367 368 if (result == S_FALSE || result == E_NOTIMPL) 369 { 370 bool wasFinished = folderOutStream->WasWritingFinished(); 371 372 int resOp = (result == S_FALSE ? 373 NExtract::NOperationResult::kDataError : 374 NExtract::NOperationResult::kUnsupportedMethod); 375 376 RINOK(folderOutStream->FlushCorrupted(resOp)); 377 378 if (wasFinished) 379 { 380 // we don't show error, if it's after required files 381 if (/* !folderOutStream->ExtraWriteWasCut && */ callbackMessage) 382 { 383 RINOK(callbackMessage->ReportExtractResult(NEventIndexType::kBlockIndex, folderIndex, resOp)); 384 } 385 } 386 continue; 387 } 388 389 if (result != S_OK) 390 return result; 391 392 RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)); 393 continue; 394 } 395 catch(...) 396 { 397 RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError)); 398 // continue; 399 return E_FAIL; 400 } 401 } 402 403 return S_OK; 404 405 COM_TRY_END 406 } 407 408 }} 409