xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/7z/7zExtract.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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 
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 
58   bool WasWritingFinished() const { return _numFiles == 0; }
59 };
60 
61 
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 
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 
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 
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 
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 
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 
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 
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   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
275   lps->Init(extractCallback, false);
276 
277   CDecoder decoder(
278     #if !defined(USE_MIXER_MT)
279       false
280     #elif !defined(USE_MIXER_ST)
281       true
282     #elif !defined(Z7_7Z_SET_PROPERTIES)
283       #ifdef Z7_ST
284         false
285       #else
286         true
287       #endif
288     #else
289       _useMultiThreadMixer
290     #endif
291     );
292 
293   UInt64 curPacked, curUnpacked;
294 
295   CMyComPtr<IArchiveExtractCallbackMessage2> callbackMessage;
296   extractCallback.QueryInterface(IID_IArchiveExtractCallbackMessage2, &callbackMessage);
297 
298   CFolderOutStream *folderOutStream = new CFolderOutStream;
299   CMyComPtr<ISequentialOutStream> outStream(folderOutStream);
300 
301   folderOutStream->_db = &_db;
302   folderOutStream->ExtractCallback = extractCallback;
303   folderOutStream->TestMode = (testModeSpec != 0);
304   folderOutStream->CheckCrc = (_crcSize != 0);
305 
306   for (UInt32 i = 0;; lps->OutSize += curUnpacked, lps->InSize += curPacked)
307   {
308     RINOK(lps->SetCur())
309 
310     if (i >= numItems)
311       break;
312 
313     curUnpacked = 0;
314     curPacked = 0;
315 
316     UInt32 fileIndex = allFilesMode ? i : indices[i];
317     const CNum folderIndex = _db.FileIndexToFolderIndexMap[fileIndex];
318 
319     UInt32 numSolidFiles = 1;
320 
321     if (folderIndex != kNumNoIndex)
322     {
323       curPacked = _db.GetFolderFullPackSize(folderIndex);
324       UInt32 nextFile = fileIndex + 1;
325       fileIndex = _db.FolderStartFileIndex[folderIndex];
326       UInt32 k;
327 
328       for (k = i + 1; k < numItems; k++)
329       {
330         const UInt32 fileIndex2 = allFilesMode ? k : indices[k];
331         if (_db.FileIndexToFolderIndexMap[fileIndex2] != folderIndex
332             || fileIndex2 < nextFile)
333           break;
334         nextFile = fileIndex2 + 1;
335       }
336 
337       numSolidFiles = k - i;
338 
339       for (k = fileIndex; k < nextFile; k++)
340         curUnpacked += _db.Files[k].Size;
341     }
342 
343     {
344       const HRESULT result = folderOutStream->Init(fileIndex,
345           allFilesMode ? NULL : indices + i,
346           numSolidFiles);
347 
348       i += numSolidFiles;
349 
350       RINOK(result)
351     }
352 
353     if (folderOutStream->WasWritingFinished())
354     {
355       // for debug: to test zero size stream unpacking
356       // if (folderIndex == kNumNoIndex)  // enable this check for debug
357       continue;
358     }
359 
360     if (folderIndex == kNumNoIndex)
361       return E_FAIL;
362 
363     #ifndef Z7_NO_CRYPTO
364     CMyComPtr<ICryptoGetTextPassword> getTextPassword;
365     if (extractCallback)
366       extractCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword);
367     #endif
368 
369     try
370     {
371       #ifndef Z7_NO_CRYPTO
372         bool isEncrypted = false;
373         bool passwordIsDefined = false;
374         UString_Wipe password;
375       #endif
376 
377       bool dataAfterEnd_Error = false;
378 
379       const HRESULT result = decoder.Decode(
380           EXTERNAL_CODECS_VARS
381           _inStream,
382           _db.ArcInfo.DataStartPosition,
383           _db, folderIndex,
384           &curUnpacked,
385 
386           outStream,
387           lps,
388           NULL // *inStreamMainRes
389           , dataAfterEnd_Error
390 
391           Z7_7Z_DECODER_CRYPRO_VARS
392           #if !defined(Z7_ST)
393             , true, _numThreads, _memUsage_Decompress
394           #endif
395           );
396 
397       if (result == S_FALSE || result == E_NOTIMPL || dataAfterEnd_Error)
398       {
399         const bool wasFinished = folderOutStream->WasWritingFinished();
400 
401         int resOp = NExtract::NOperationResult::kDataError;
402 
403         if (result != S_FALSE)
404         {
405           if (result == E_NOTIMPL)
406             resOp = NExtract::NOperationResult::kUnsupportedMethod;
407           else if (wasFinished && dataAfterEnd_Error)
408             resOp = NExtract::NOperationResult::kDataAfterEnd;
409         }
410 
411         RINOK(folderOutStream->FlushCorrupted(resOp))
412 
413         if (wasFinished)
414         {
415           // we don't show error, if it's after required files
416           if (/* !folderOutStream->ExtraWriteWasCut && */ callbackMessage)
417           {
418             RINOK(callbackMessage->ReportExtractResult(NEventIndexType::kBlockIndex, folderIndex, resOp))
419           }
420         }
421         continue;
422       }
423 
424       if (result != S_OK)
425         return result;
426 
427       RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError))
428       continue;
429     }
430     catch(...)
431     {
432       RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError))
433       // continue;
434       // return E_FAIL;
435       throw;
436     }
437   }
438 
439   return S_OK;
440 
441   COM_TRY_END
442 }
443 
444 }}
445