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