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
CFolderOutStream()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
WasWritingFinished() const60 bool WasWritingFinished() const { return _numFiles == 0; }
61 };
62
63
Init(unsigned startIndex,const UInt32 * indexes,unsigned numFiles)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
OpenFile(bool isCorrupted)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
CloseFile_and_SetResult(Int32 res)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
CloseFile()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
ProcessEmptyFiles()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
Write(const void * data,UInt32 size,UInt32 * processedSize)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
FlushCorrupted(Int32 callbackOperationResult)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
Extract(const UInt32 * indices,UInt32 numItems,Int32 testModeSpec,IArchiveExtractCallback * extractCallbackSpec)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 bool dataAfterEnd_Error = false;
352
353 HRESULT result = decoder.Decode(
354 EXTERNAL_CODECS_VARS
355 _inStream,
356 _db.ArcInfo.DataStartPosition,
357 _db, folderIndex,
358 &curUnpacked,
359
360 outStream,
361 progress,
362 NULL // *inStreamMainRes
363 , dataAfterEnd_Error
364
365 _7Z_DECODER_CRYPRO_VARS
366 #if !defined(_7ZIP_ST) && !defined(_SFX)
367 , true, _numThreads
368 #endif
369 );
370
371 if (result == S_FALSE || result == E_NOTIMPL || dataAfterEnd_Error)
372 {
373 bool wasFinished = folderOutStream->WasWritingFinished();
374
375 int resOp = NExtract::NOperationResult::kDataError;
376
377 if (result != S_FALSE)
378 {
379 if (result == E_NOTIMPL)
380 resOp = NExtract::NOperationResult::kUnsupportedMethod;
381 else if (wasFinished && dataAfterEnd_Error)
382 resOp = NExtract::NOperationResult::kDataAfterEnd;
383 }
384
385 RINOK(folderOutStream->FlushCorrupted(resOp));
386
387 if (wasFinished)
388 {
389 // we don't show error, if it's after required files
390 if (/* !folderOutStream->ExtraWriteWasCut && */ callbackMessage)
391 {
392 RINOK(callbackMessage->ReportExtractResult(NEventIndexType::kBlockIndex, folderIndex, resOp));
393 }
394 }
395 continue;
396 }
397
398 if (result != S_OK)
399 return result;
400
401 RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError));
402 continue;
403 }
404 catch(...)
405 {
406 RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError));
407 // continue;
408 return E_FAIL;
409 }
410 }
411
412 return S_OK;
413
414 COM_TRY_END
415 }
416
417 }}
418