1 // Bz2Handler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../Common/ComTry.h"
6 
7 #include "../Common/ProgressUtils.h"
8 #include "../Common/RegisterArc.h"
9 #include "../Common/StreamUtils.h"
10 
11 #include "../Compress/BZip2Decoder.h"
12 #include "../Compress/BZip2Encoder.h"
13 #include "../Compress/CopyCoder.h"
14 
15 #include "Common/DummyOutStream.h"
16 #include "Common/HandlerOut.h"
17 
18 using namespace NWindows;
19 
20 namespace NArchive {
21 namespace NBz2 {
22 
23 class CHandler:
24   public IInArchive,
25   public IArchiveOpenSeq,
26   public IOutArchive,
27   public ISetProperties,
28   public CMyUnknownImp
29 {
30   CMyComPtr<IInStream> _stream;
31   CMyComPtr<ISequentialInStream> _seqStream;
32 
33   bool _isArc;
34   bool _needSeekToStart;
35   bool _dataAfterEnd;
36   bool _needMoreInput;
37 
38   bool _packSize_Defined;
39   bool _unpackSize_Defined;
40   bool _numStreams_Defined;
41   bool _numBlocks_Defined;
42 
43   UInt64 _packSize;
44   UInt64 _unpackSize;
45   UInt64 _numStreams;
46   UInt64 _numBlocks;
47 
48   CSingleMethodProps _props;
49 
50 public:
51   MY_UNKNOWN_IMP4(
52       IInArchive,
53       IArchiveOpenSeq,
54       IOutArchive,
55       ISetProperties)
56   INTERFACE_IInArchive(;)
57   INTERFACE_IOutArchive(;)
58   STDMETHOD(OpenSeq)(ISequentialInStream *stream);
59   STDMETHOD(SetProperties)(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps);
60 
CHandler()61   CHandler() { }
62 };
63 
64 static const Byte kProps[] =
65 {
66   kpidSize,
67   kpidPackSize
68 };
69 
70 static const Byte kArcProps[] =
71 {
72   kpidNumStreams,
73   kpidNumBlocks
74 };
75 
76 IMP_IInArchive_Props
77 IMP_IInArchive_ArcProps
78 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)79 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
80 {
81   NCOM::CPropVariant prop;
82   switch (propID)
83   {
84     case kpidPhySize: if (_packSize_Defined) prop = _packSize; break;
85     case kpidUnpackSize: if (_unpackSize_Defined) prop = _unpackSize; break;
86     case kpidNumStreams: if (_numStreams_Defined) prop = _numStreams; break;
87     case kpidNumBlocks: if (_numBlocks_Defined) prop = _numBlocks; break;
88     case kpidErrorFlags:
89     {
90       UInt32 v = 0;
91       if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;;
92       if (_needMoreInput) v |= kpv_ErrorFlags_UnexpectedEnd;
93       if (_dataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd;
94       prop = v;
95     }
96   }
97   prop.Detach(value);
98   return S_OK;
99 }
100 
GetNumberOfItems(UInt32 * numItems)101 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
102 {
103   *numItems = 1;
104   return S_OK;
105 }
106 
GetProperty(UInt32,PROPID propID,PROPVARIANT * value)107 STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
108 {
109   NCOM::CPropVariant prop;
110   switch (propID)
111   {
112     case kpidPackSize: if (_packSize_Defined) prop = _packSize; break;
113     case kpidSize: if (_unpackSize_Defined) prop = _unpackSize; break;
114   }
115   prop.Detach(value);
116   return S_OK;
117 }
118 
119 static const unsigned kSignatureCheckSize = 10;
120 
IsArc_BZip2(const Byte * p,size_t size)121 API_FUNC_static_IsArc IsArc_BZip2(const Byte *p, size_t size)
122 {
123   if (size < kSignatureCheckSize)
124     return k_IsArc_Res_NEED_MORE;
125   if (p[0] != 'B' || p[1] != 'Z' || p[2] != 'h' || p[3] < '1' || p[3] > '9')
126     return k_IsArc_Res_NO;
127   p += 4;
128   if (NCompress::NBZip2::IsBlockSig(p))
129     return k_IsArc_Res_YES;
130   if (NCompress::NBZip2::IsEndSig(p))
131     return k_IsArc_Res_YES;
132   return k_IsArc_Res_NO;
133 }
134 }
135 
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback *)136 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *)
137 {
138   COM_TRY_BEGIN
139   Close();
140   {
141     Byte buf[kSignatureCheckSize];
142     RINOK(ReadStream_FALSE(stream, buf, kSignatureCheckSize));
143     if (IsArc_BZip2(buf, kSignatureCheckSize) == k_IsArc_Res_NO)
144       return S_FALSE;
145     _isArc = true;
146     _stream = stream;
147     _seqStream = stream;
148     _needSeekToStart = true;
149   }
150   return S_OK;
151   COM_TRY_END
152 }
153 
154 
OpenSeq(ISequentialInStream * stream)155 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
156 {
157   Close();
158   _isArc = true;
159   _seqStream = stream;
160   return S_OK;
161 }
162 
Close()163 STDMETHODIMP CHandler::Close()
164 {
165   _isArc = false;
166   _needSeekToStart = false;
167   _dataAfterEnd = false;
168   _needMoreInput = false;
169 
170   _packSize_Defined = false;
171   _unpackSize_Defined = false;
172   _numStreams_Defined = false;
173   _numBlocks_Defined = false;
174 
175   _packSize = 0;
176 
177   _seqStream.Release();
178   _stream.Release();
179   return S_OK;
180 }
181 
182 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)183 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
184     Int32 testMode, IArchiveExtractCallback *extractCallback)
185 {
186   COM_TRY_BEGIN
187   if (numItems == 0)
188     return S_OK;
189   if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
190     return E_INVALIDARG;
191 
192   if (_packSize_Defined)
193     extractCallback->SetTotal(_packSize);
194 
195   CMyComPtr<ISequentialOutStream> realOutStream;
196   Int32 askMode = testMode ?
197       NExtract::NAskMode::kTest :
198       NExtract::NAskMode::kExtract;
199   RINOK(extractCallback->GetStream(0, &realOutStream, askMode));
200   if (!testMode && !realOutStream)
201     return S_OK;
202 
203   extractCallback->PrepareOperation(askMode);
204 
205   if (_needSeekToStart)
206   {
207     if (!_stream)
208       return E_FAIL;
209     RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL));
210   }
211   else
212     _needSeekToStart = true;
213 
214   // try {
215 
216   NCompress::NBZip2::CDecoder *decoderSpec = new NCompress::NBZip2::CDecoder;
217   CMyComPtr<ICompressCoder> decoder = decoderSpec;
218 
219   #ifndef _7ZIP_ST
220   RINOK(decoderSpec->SetNumberOfThreads(_props._numThreads));
221   #endif
222 
223   CDummyOutStream *outStreamSpec = new CDummyOutStream;
224   CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
225   outStreamSpec->SetStream(realOutStream);
226   outStreamSpec->Init();
227 
228   realOutStream.Release();
229 
230   CLocalProgress *lps = new CLocalProgress;
231   CMyComPtr<ICompressProgressInfo> progress = lps;
232   lps->Init(extractCallback, true);
233 
234   decoderSpec->FinishMode = true;
235   decoderSpec->Base.DecodeAllStreams = true;
236 
237   _dataAfterEnd = false;
238   _needMoreInput = false;
239 
240   lps->InSize = 0;
241   lps->OutSize = 0;
242 
243   HRESULT result = decoderSpec->Code(_seqStream, outStream, NULL, NULL, progress);
244 
245   if (result != S_FALSE && result != S_OK)
246     return result;
247 
248   if (decoderSpec->Base.NumStreams == 0)
249   {
250     _isArc = false;
251     result = S_FALSE;
252   }
253   else
254   {
255     const UInt64 inProcessedSize = decoderSpec->GetInputProcessedSize();
256     UInt64 packSize = inProcessedSize;
257 
258     if (decoderSpec->Base.NeedMoreInput)
259       _needMoreInput = true;
260 
261     if (!decoderSpec->Base.IsBz)
262     {
263       packSize = decoderSpec->Base.FinishedPackSize;
264       if (packSize != inProcessedSize)
265         _dataAfterEnd = true;
266     }
267 
268     _packSize = packSize;
269     _unpackSize = decoderSpec->GetOutProcessedSize();
270     _numStreams = decoderSpec->Base.NumStreams;
271     _numBlocks = decoderSpec->GetNumBlocks();
272 
273     _packSize_Defined = true;
274     _unpackSize_Defined = true;
275     _numStreams_Defined = true;
276     _numBlocks_Defined = true;
277   }
278 
279   outStream.Release();
280 
281   Int32 opRes;
282 
283   if (!_isArc)
284     opRes = NExtract::NOperationResult::kIsNotArc;
285   else if (_needMoreInput)
286     opRes = NExtract::NOperationResult::kUnexpectedEnd;
287   else if (decoderSpec->GetCrcError())
288     opRes = NExtract::NOperationResult::kCRCError;
289   else if (_dataAfterEnd)
290     opRes = NExtract::NOperationResult::kDataAfterEnd;
291   else if (result == S_FALSE)
292     opRes = NExtract::NOperationResult::kDataError;
293   else if (decoderSpec->Base.MinorError)
294     opRes = NExtract::NOperationResult::kDataError;
295   else if (result == S_OK)
296     opRes = NExtract::NOperationResult::kOK;
297   else
298     return result;
299 
300   return extractCallback->SetOperationResult(opRes);
301 
302   // } catch(...)  { return E_FAIL; }
303 
304   COM_TRY_END
305 }
306 
307 
308 
UpdateArchive(UInt64 unpackSize,ISequentialOutStream * outStream,const CProps & props,IArchiveUpdateCallback * updateCallback)309 static HRESULT UpdateArchive(
310     UInt64 unpackSize,
311     ISequentialOutStream *outStream,
312     const CProps &props,
313     IArchiveUpdateCallback *updateCallback)
314 {
315   RINOK(updateCallback->SetTotal(unpackSize));
316   CMyComPtr<ISequentialInStream> fileInStream;
317   RINOK(updateCallback->GetStream(0, &fileInStream));
318   CLocalProgress *localProgressSpec = new CLocalProgress;
319   CMyComPtr<ICompressProgressInfo> localProgress = localProgressSpec;
320   localProgressSpec->Init(updateCallback, true);
321   NCompress::NBZip2::CEncoder *encoderSpec = new NCompress::NBZip2::CEncoder;
322   CMyComPtr<ICompressCoder> encoder = encoderSpec;
323   RINOK(props.SetCoderProps(encoderSpec, NULL));
324   RINOK(encoder->Code(fileInStream, outStream, NULL, NULL, localProgress));
325   return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK);
326 }
327 
GetFileTimeType(UInt32 * type)328 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
329 {
330   *type = NFileTimeType::kUnix;
331   return S_OK;
332 }
333 
UpdateItems(ISequentialOutStream * outStream,UInt32 numItems,IArchiveUpdateCallback * updateCallback)334 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
335     IArchiveUpdateCallback *updateCallback)
336 {
337   COM_TRY_BEGIN
338 
339   if (numItems != 1)
340     return E_INVALIDARG;
341 
342   Int32 newData, newProps;
343   UInt32 indexInArchive;
344   if (!updateCallback)
345     return E_FAIL;
346   RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive));
347 
348   if (IntToBool(newProps))
349   {
350     {
351       NCOM::CPropVariant prop;
352       RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop));
353       if (prop.vt != VT_EMPTY)
354         if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE)
355           return E_INVALIDARG;
356     }
357   }
358 
359   if (IntToBool(newData))
360   {
361     UInt64 size;
362     {
363       NCOM::CPropVariant prop;
364       RINOK(updateCallback->GetProperty(0, kpidSize, &prop));
365       if (prop.vt != VT_UI8)
366         return E_INVALIDARG;
367       size = prop.uhVal.QuadPart;
368     }
369 
370     CMethodProps props2 = _props;
371     #ifndef _7ZIP_ST
372     props2.AddProp_NumThreads(_props._numThreads);
373     #endif
374 
375     return UpdateArchive(size, outStream, props2, updateCallback);
376   }
377 
378   if (indexInArchive != 0)
379     return E_INVALIDARG;
380 
381   CLocalProgress *lps = new CLocalProgress;
382   CMyComPtr<ICompressProgressInfo> progress = lps;
383   lps->Init(updateCallback, true);
384 
385   CMyComPtr<IArchiveUpdateCallbackFile> opCallback;
386   updateCallback->QueryInterface(IID_IArchiveUpdateCallbackFile, (void **)&opCallback);
387   if (opCallback)
388   {
389     RINOK(opCallback->ReportOperation(
390         NEventIndexType::kInArcIndex, 0,
391         NUpdateNotifyOp::kReplicate))
392   }
393 
394   if (_stream)
395     RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL));
396 
397   return NCompress::CopyStream(_stream, outStream, progress);
398 
399   COM_TRY_END
400 }
401 
SetProperties(const wchar_t * const * names,const PROPVARIANT * values,UInt32 numProps)402 STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)
403 {
404   return _props.SetProperties(names, values, numProps);
405 }
406 
407 static const Byte k_Signature[] = { 'B', 'Z', 'h' };
408 
409 REGISTER_ARC_IO(
410   "bzip2", "bz2 bzip2 tbz2 tbz", "* * .tar .tar", 2,
411   k_Signature,
412   0,
413   NArcInfoFlags::kKeepName,
414   IsArc_BZip2)
415 
416 }}
417