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