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