1 // HandlerCont.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../Common/ComTry.h"
6 
7 #include "../Common/LimitedStreams.h"
8 #include "../Common/ProgressUtils.h"
9 #include "../Common/StreamUtils.h"
10 
11 #include "../Compress/CopyCoder.h"
12 
13 #include "HandlerCont.h"
14 
15 namespace NArchive {
16 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)17 STDMETHODIMP CHandlerCont::Extract(const UInt32 *indices, UInt32 numItems,
18     Int32 testMode, IArchiveExtractCallback *extractCallback)
19 {
20   COM_TRY_BEGIN
21   bool allFilesMode = (numItems == (UInt32)(Int32)-1);
22   if (allFilesMode)
23   {
24     RINOK(GetNumberOfItems(&numItems));
25   }
26   if (numItems == 0)
27     return S_OK;
28   UInt64 totalSize = 0;
29   UInt32 i;
30   for (i = 0; i < numItems; i++)
31   {
32     UInt64 pos, size;
33     GetItem_ExtractInfo(allFilesMode ? i : indices[i], pos, size);
34     totalSize += size;
35   }
36   extractCallback->SetTotal(totalSize);
37 
38   totalSize = 0;
39 
40   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
41   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
42 
43   CLocalProgress *lps = new CLocalProgress;
44   CMyComPtr<ICompressProgressInfo> progress = lps;
45   lps->Init(extractCallback, false);
46 
47   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
48   CMyComPtr<ISequentialInStream> inStream(streamSpec);
49   streamSpec->SetStream(_stream);
50 
51   for (i = 0; i < numItems; i++)
52   {
53     lps->InSize = totalSize;
54     lps->OutSize = totalSize;
55     RINOK(lps->SetCur());
56     CMyComPtr<ISequentialOutStream> outStream;
57     Int32 askMode = testMode ?
58         NExtract::NAskMode::kTest :
59         NExtract::NAskMode::kExtract;
60     Int32 index = allFilesMode ? i : indices[i];
61 
62     RINOK(extractCallback->GetStream(index, &outStream, askMode));
63 
64     UInt64 pos, size;
65     int opRes = GetItem_ExtractInfo(index, pos, size);
66     totalSize += size;
67     if (!testMode && !outStream)
68       continue;
69 
70     RINOK(extractCallback->PrepareOperation(askMode));
71 
72     if (opRes == NExtract::NOperationResult::kOK)
73     {
74       RINOK(_stream->Seek(pos, STREAM_SEEK_SET, NULL));
75       streamSpec->Init(size);
76 
77       RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress));
78 
79       opRes = NExtract::NOperationResult::kDataError;
80 
81       if (copyCoderSpec->TotalSize == size)
82         opRes = NExtract::NOperationResult::kOK;
83       else if (copyCoderSpec->TotalSize < size)
84         opRes = NExtract::NOperationResult::kUnexpectedEnd;
85     }
86 
87     outStream.Release();
88     RINOK(extractCallback->SetOperationResult(opRes));
89   }
90 
91   return S_OK;
92   COM_TRY_END
93 }
94 
GetStream(UInt32 index,ISequentialInStream ** stream)95 STDMETHODIMP CHandlerCont::GetStream(UInt32 index, ISequentialInStream **stream)
96 {
97   COM_TRY_BEGIN
98   *stream = NULL;
99   UInt64 pos, size;
100   if (GetItem_ExtractInfo(index, pos, size) != NExtract::NOperationResult::kOK)
101     return S_FALSE;
102   return CreateLimitedInStream(_stream, pos, size, stream);
103   COM_TRY_END
104 }
105 
106 
107 
CHandlerImg()108 CHandlerImg::CHandlerImg()
109 {
110   Clear_HandlerImg_Vars();
111 }
112 
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)113 STDMETHODIMP CHandlerImg::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
114 {
115   switch (seekOrigin)
116   {
117     case STREAM_SEEK_SET: break;
118     case STREAM_SEEK_CUR: offset += _virtPos; break;
119     case STREAM_SEEK_END: offset += _size; break;
120     default: return STG_E_INVALIDFUNCTION;
121   }
122   if (offset < 0)
123   {
124     if (newPosition)
125       *newPosition = _virtPos;
126     return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
127   }
128   _virtPos = offset;
129   if (newPosition)
130     *newPosition = offset;
131   return S_OK;
132 }
133 
134 static const Byte k_GDP_Signature[] = { 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T' };
135 
136 
GetImgExt(ISequentialInStream * stream)137 static const char *GetImgExt(ISequentialInStream *stream)
138 {
139   const size_t kHeaderSize = 1 << 10;
140   Byte buf[kHeaderSize];
141   if (ReadStream_FAIL(stream, buf, kHeaderSize) == S_OK)
142   {
143     if (buf[0x1FE] == 0x55 && buf[0x1FF] == 0xAA)
144     {
145       if (memcmp(buf + 512, k_GDP_Signature, sizeof(k_GDP_Signature)) == 0)
146         return "gpt";
147       return "mbr";
148     }
149   }
150   return NULL;
151 }
152 
CloseAtError()153 void CHandlerImg::CloseAtError()
154 {
155   Stream.Release();
156 }
157 
Clear_HandlerImg_Vars()158 void CHandlerImg::Clear_HandlerImg_Vars()
159 {
160   _imgExt = NULL;
161   _size = 0;
162   ClearStreamVars();
163   Reset_VirtPos();
164   Reset_PosInArc();
165 }
166 
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback * openCallback)167 STDMETHODIMP CHandlerImg::Open(IInStream *stream,
168     const UInt64 * /* maxCheckStartPosition */,
169     IArchiveOpenCallback * openCallback)
170 {
171   COM_TRY_BEGIN
172   {
173     Close();
174     HRESULT res;
175     try
176     {
177       res = Open2(stream, openCallback);
178       if (res == S_OK)
179       {
180         CMyComPtr<ISequentialInStream> inStream;
181         const HRESULT res2 = GetStream(0, &inStream);
182         if (res2 == S_OK && inStream)
183           _imgExt = GetImgExt(inStream);
184         // _imgExt = GetImgExt(this); // for debug
185         /*  we reset (_virtPos) to support cases, if some code will
186             call Read() from Handler object instead of GetStream() object. */
187         Reset_VirtPos();
188         // optional: we reset (_posInArc). if real seek position of stream will be changed in external code
189         Reset_PosInArc();
190         // optional: here we could also reset seek positions in parent streams..
191         return S_OK;
192       }
193     }
194     catch(...)
195     {
196       CloseAtError();
197       throw;
198     }
199     CloseAtError();
200     return res;
201   }
202   COM_TRY_END
203 }
204 
GetNumberOfItems(UInt32 * numItems)205 STDMETHODIMP CHandlerImg::GetNumberOfItems(UInt32 *numItems)
206 {
207   *numItems = 1;
208   return S_OK;
209 }
210 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)211 STDMETHODIMP CHandlerImg::Extract(const UInt32 *indices, UInt32 numItems,
212     Int32 testMode, IArchiveExtractCallback *extractCallback)
213 {
214   COM_TRY_BEGIN
215   if (numItems == 0)
216     return S_OK;
217   if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
218     return E_INVALIDARG;
219 
220   RINOK(extractCallback->SetTotal(_size));
221   CMyComPtr<ISequentialOutStream> outStream;
222   Int32 askMode = testMode ?
223       NExtract::NAskMode::kTest :
224       NExtract::NAskMode::kExtract;
225   RINOK(extractCallback->GetStream(0, &outStream, askMode));
226   if (!testMode && !outStream)
227     return S_OK;
228   RINOK(extractCallback->PrepareOperation(askMode));
229 
230   CLocalProgress *lps = new CLocalProgress;
231   CMyComPtr<ICompressProgressInfo> progress = lps;
232   lps->Init(extractCallback, false);
233 
234   int opRes = NExtract::NOperationResult::kDataError;
235 
236   ClearStreamVars();
237 
238   CMyComPtr<ISequentialInStream> inStream;
239   HRESULT hres = GetStream(0, &inStream);
240   if (hres == S_FALSE)
241     hres = E_NOTIMPL;
242 
243   if (hres == S_OK && inStream)
244   {
245     NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
246     CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
247 
248     hres = copyCoder->Code(inStream, outStream, NULL, &_size, progress);
249     if (hres == S_OK)
250     {
251       if (copyCoderSpec->TotalSize == _size)
252         opRes = NExtract::NOperationResult::kOK;
253 
254       if (_stream_unavailData)
255         opRes = NExtract::NOperationResult::kUnavailable;
256       else if (_stream_unsupportedMethod)
257         opRes = NExtract::NOperationResult::kUnsupportedMethod;
258       else if (_stream_dataError)
259         opRes = NExtract::NOperationResult::kDataError;
260       else if (copyCoderSpec->TotalSize < _size)
261         opRes = NExtract::NOperationResult::kUnexpectedEnd;
262     }
263   }
264 
265   inStream.Release();
266   outStream.Release();
267 
268   if (hres != S_OK)
269   {
270     if (hres == S_FALSE)
271       opRes = NExtract::NOperationResult::kDataError;
272     else if (hres == E_NOTIMPL)
273       opRes = NExtract::NOperationResult::kUnsupportedMethod;
274     else
275       return hres;
276   }
277 
278   return extractCallback->SetOperationResult(opRes);
279   COM_TRY_END
280 }
281 
282 
ReadZeroTail(ISequentialInStream * stream,bool & areThereNonZeros,UInt64 & numZeros,UInt64 maxSize)283 HRESULT ReadZeroTail(ISequentialInStream *stream, bool &areThereNonZeros, UInt64 &numZeros, UInt64 maxSize)
284 {
285   areThereNonZeros = false;
286   numZeros = 0;
287   const size_t kBufSize = 1 << 11;
288   Byte buf[kBufSize];
289   for (;;)
290   {
291     UInt32 size = 0;
292     RINOK(stream->Read(buf, kBufSize, &size));
293     if (size == 0)
294       return S_OK;
295     for (UInt32 i = 0; i < size; i++)
296       if (buf[i] != 0)
297       {
298         areThereNonZeros = true;
299         numZeros += i;
300         return S_OK;
301       }
302     numZeros += size;
303     if (numZeros > maxSize)
304       return S_OK;
305   }
306 }
307 
308 }
309