1 // GzHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 // #include  <stdio.h>
6 
7 #include "../../../C/CpuArch.h"
8 
9 #include "../../Common/ComTry.h"
10 #include "../../Common/Defs.h"
11 #include "../../Common/StringConvert.h"
12 
13 #include "../../Windows/PropVariant.h"
14 #include "../../Windows/PropVariantUtils.h"
15 #include "../../Windows/TimeUtils.h"
16 
17 #include "../Common/ProgressUtils.h"
18 #include "../Common/RegisterArc.h"
19 #include "../Common/StreamUtils.h"
20 
21 #include "../Compress/CopyCoder.h"
22 #include "../Compress/DeflateDecoder.h"
23 #include "../Compress/DeflateEncoder.h"
24 
25 #include "Common/HandlerOut.h"
26 #include "Common/InStreamWithCRC.h"
27 #include "Common/OutStreamWithCRC.h"
28 
29 #define Get32(p) GetUi32(p)
30 
31 using namespace NWindows;
32 
33 using namespace NCompress;
34 using namespace NDeflate;
35 
36 namespace NArchive {
37 namespace NGz {
38 
39   static const Byte kSignature_0 = 0x1F;
40   static const Byte kSignature_1 = 0x8B;
41   static const Byte kSignature_2 = 8; //  NCompressionMethod::kDeflate
42 
43   // Latest versions of gzip program don't write comment field to gz archive.
44   // We also don't write comment field to gz archive.
45 
46   namespace NFlags
47   {
48     // const Byte kIsText  = 1 << 0;
49     const Byte kCrc     = 1 << 1;
50     const Byte kExtra   = 1 << 2;
51     const Byte kName    = 1 << 3;
52     const Byte kComment = 1 << 4;
53     const Byte kReserved = 0xE0;
54   }
55 
56   namespace NExtraFlags
57   {
58     const Byte kMaximum = 2;
59     const Byte kFastest = 4;
60   }
61 
62   namespace NHostOS
63   {
64     enum EEnum
65     {
66       kFAT = 0,
67       kAMIGA,
68       kVMS,
69       kUnix,
70       kVM_CMS,
71       kAtari,
72       kHPFS,
73       kMac,
74       kZ_System,
75       kCPM,
76       kTOPS20,
77       kNTFS,
78       kQDOS,
79       kAcorn,
80       kVFAT,
81       kMVS,
82       kBeOS,
83       kTandem,
84 
85       kUnknown = 255
86     };
87   }
88 
89 static const char * const kHostOSes[] =
90 {
91     "FAT"
92   , "AMIGA"
93   , "VMS"
94   , "Unix"
95   , "VM/CMS"
96   , "Atari"
97   , "HPFS"
98   , "Macintosh"
99   , "Z-System"
100   , "CP/M"
101   , "TOPS-20"
102   , "NTFS"
103   , "SMS/QDOS"
104   , "Acorn"
105   , "VFAT"
106   , "MVS"
107   , "BeOS"
108   , "Tandem"
109   , "OS/400"
110   , "OS/X"
111 };
112 
113 
114 class CItem
115 {
TestFlag(Byte flag) const116   bool TestFlag(Byte flag) const { return (Flags & flag) != 0; }
117 public:
118   Byte Flags;
119   Byte ExtraFlags;
120   Byte HostOS;
121   UInt32 Time;
122   UInt32 Crc;
123   UInt32 Size32;
124 
125   AString Name;
126   AString Comment;
127   // CByteBuffer Extra;
128 
CItem()129   CItem():
130     Flags(0),
131     ExtraFlags(0),
132     HostOS(0),
133     Time(0),
134     Crc(0),
135     Size32(0) {}
136 
Clear()137   void Clear()
138   {
139     Name.Empty();
140     Comment.Empty();
141     // Extra.Free();
142   }
143 
CopyMetaPropsFrom(const CItem & a)144   void CopyMetaPropsFrom(const CItem &a)
145   {
146     Flags = a.Flags;
147     HostOS = a.HostOS;
148     Time = a.Time;
149     Name = a.Name;
150     Comment = a.Comment;
151     // Extra = a.Extra;
152   }
153 
CopyDataPropsFrom(const CItem & a)154   void CopyDataPropsFrom(const CItem &a)
155   {
156     ExtraFlags = a.ExtraFlags;
157     Crc = a.Crc;
158     Size32 = a.Size32;
159   }
160 
161   // bool IsText() const { return TestFlag(NFlags::kIsText); }
HeaderCrcIsPresent() const162   bool HeaderCrcIsPresent() const { return TestFlag(NFlags::kCrc); }
ExtraFieldIsPresent() const163   bool ExtraFieldIsPresent() const { return TestFlag(NFlags::kExtra); }
NameIsPresent() const164   bool NameIsPresent() const { return TestFlag(NFlags::kName); }
CommentIsPresent() const165   bool CommentIsPresent() const { return TestFlag(NFlags::kComment); }
IsSupported() const166   bool IsSupported() const { return (Flags & NFlags::kReserved) == 0; }
167 
168   HRESULT ReadHeader(NDecoder::CCOMCoder *stream);
169   HRESULT ReadFooter1(NDecoder::CCOMCoder *stream);
170   HRESULT ReadFooter2(ISequentialInStream *stream);
171 
172   HRESULT WriteHeader(ISequentialOutStream *stream);
173   HRESULT WriteFooter(ISequentialOutStream *stream);
174 };
175 
ReadBytes(NDecoder::CCOMCoder * stream,Byte * data,UInt32 size)176 static HRESULT ReadBytes(NDecoder::CCOMCoder *stream, Byte *data, UInt32 size)
177 {
178   for (UInt32 i = 0; i < size; i++)
179     data[i] = stream->ReadAlignedByte();
180   return stream->InputEofError() ? S_FALSE : S_OK;
181 }
182 
SkipBytes(NDecoder::CCOMCoder * stream,UInt32 size)183 static HRESULT SkipBytes(NDecoder::CCOMCoder *stream, UInt32 size)
184 {
185   for (UInt32 i = 0; i < size; i++)
186     stream->ReadAlignedByte();
187   return stream->InputEofError() ? S_FALSE : S_OK;
188 }
189 
ReadUInt16(NDecoder::CCOMCoder * stream,UInt32 & value)190 static HRESULT ReadUInt16(NDecoder::CCOMCoder *stream, UInt32 &value /* , UInt32 &crc */)
191 {
192   value = 0;
193   for (int i = 0; i < 2; i++)
194   {
195     Byte b = stream->ReadAlignedByte();
196     if (stream->InputEofError())
197       return S_FALSE;
198     // crc = CRC_UPDATE_BYTE(crc, b);
199     value |= ((UInt32)(b) << (8 * i));
200   }
201   return S_OK;
202 }
203 
ReadString(NDecoder::CCOMCoder * stream,AString & s,size_t limit)204 static HRESULT ReadString(NDecoder::CCOMCoder *stream, AString &s, size_t limit /* , UInt32 &crc */)
205 {
206   s.Empty();
207   for (size_t i = 0; i < limit; i++)
208   {
209     Byte b = stream->ReadAlignedByte();
210     if (stream->InputEofError())
211       return S_FALSE;
212     // crc = CRC_UPDATE_BYTE(crc, b);
213     if (b == 0)
214       return S_OK;
215     s += (char)b;
216   }
217   return S_FALSE;
218 }
219 
Is_Deflate(const Byte * p,size_t size)220 static UInt32 Is_Deflate(const Byte *p, size_t size)
221 {
222   if (size < 1)
223     return k_IsArc_Res_NEED_MORE;
224   Byte b = *p;
225   p++;
226   size--;
227   unsigned type = ((unsigned)b >> 1) & 3;
228   if (type == 3)
229     return k_IsArc_Res_NO;
230   if (type == 0)
231   {
232      // Stored (uncompreessed data)
233     if ((b >> 3) != 0)
234       return k_IsArc_Res_NO;
235     if (size < 4)
236       return k_IsArc_Res_NEED_MORE;
237     UInt16 r = (UInt16)~GetUi16(p + 2);
238     if (GetUi16(p) != r)
239       return k_IsArc_Res_NO;
240   }
241   else if (type == 2)
242   {
243     // Dynamic Huffman
244     if (size < 1)
245       return k_IsArc_Res_NEED_MORE;
246     if ((*p & 0x1F) + 1 > 30) // numDistLevels
247       return k_IsArc_Res_NO;
248   }
249   return k_IsArc_Res_YES;
250 }
251 
252 static const unsigned kNameMaxLen = 1 << 12;
253 static const unsigned kCommentMaxLen = 1 << 16;
254 
IsArc_Gz(const Byte * p,size_t size)255 API_FUNC_static_IsArc IsArc_Gz(const Byte *p, size_t size)
256 {
257   if (size < 10)
258     return k_IsArc_Res_NEED_MORE;
259   if (p[0] != kSignature_0 ||
260       p[1] != kSignature_1 ||
261       p[2] != kSignature_2)
262     return k_IsArc_Res_NO;
263 
264   Byte flags = p[3];
265   if ((flags & NFlags::kReserved) != 0)
266     return k_IsArc_Res_NO;
267 
268   Byte extraFlags = p[8];
269   // maybe that flag can have another values for some gz archives?
270   if (extraFlags != 0 &&
271       extraFlags != NExtraFlags::kMaximum &&
272       extraFlags != NExtraFlags::kFastest)
273     return k_IsArc_Res_NO;
274 
275   size -= 10;
276   p += 10;
277 
278   if ((flags & NFlags::kExtra) != 0)
279   {
280     if (size < 2)
281       return k_IsArc_Res_NEED_MORE;
282     unsigned xlen = GetUi16(p);
283     size -= 2;
284     p += 2;
285     while (xlen != 0)
286     {
287       if (xlen < 4)
288         return k_IsArc_Res_NO;
289       if (size < 4)
290         return k_IsArc_Res_NEED_MORE;
291       unsigned len = GetUi16(p + 2);
292       size -= 4;
293       xlen -= 4;
294       p += 4;
295       if (len > xlen)
296         return k_IsArc_Res_NO;
297       if (len > size)
298         return k_IsArc_Res_NEED_MORE;
299       size -= len;
300       xlen -= len;
301       p += len;
302     }
303   }
304 
305   if ((flags & NFlags::kName) != 0)
306   {
307     size_t limit = kNameMaxLen;
308     if (limit > size)
309       limit = size;
310     size_t i;
311     for (i = 0; i < limit && p[i] != 0; i++);
312     if (i == size)
313       return k_IsArc_Res_NEED_MORE;
314     if (i == limit)
315       return k_IsArc_Res_NO;
316     i++;
317     p += i;
318     size -= i;
319   }
320 
321   if ((flags & NFlags::kComment) != 0)
322   {
323     size_t limit = kCommentMaxLen;
324     if (limit > size)
325       limit = size;
326     size_t i;
327     for (i = 0; i < limit && p[i] != 0; i++);
328     if (i == size)
329       return k_IsArc_Res_NEED_MORE;
330     if (i == limit)
331       return k_IsArc_Res_NO;
332     i++;
333     p += i;
334     size -= i;
335   }
336 
337   if ((flags & NFlags::kCrc) != 0)
338   {
339     if (size < 2)
340       return k_IsArc_Res_NEED_MORE;
341     p += 2;
342     size -= 2;
343   }
344 
345   return Is_Deflate(p, size);
346 }
347 }
348 
ReadHeader(NDecoder::CCOMCoder * stream)349 HRESULT CItem::ReadHeader(NDecoder::CCOMCoder *stream)
350 {
351   Clear();
352 
353   // Header-CRC field had another meaning in old version of gzip!
354   // UInt32 crc = CRC_INIT_VAL;
355   Byte buf[10];
356 
357   RINOK(ReadBytes(stream, buf, 10));
358 
359   if (buf[0] != kSignature_0 ||
360       buf[1] != kSignature_1 ||
361       buf[2] != kSignature_2)
362     return S_FALSE;
363 
364   Flags = buf[3];
365   if (!IsSupported())
366     return S_FALSE;
367 
368   Time = Get32(buf + 4);
369   ExtraFlags = buf[8];
370   HostOS = buf[9];
371 
372   // crc = CrcUpdate(crc, buf, 10);
373 
374   if (ExtraFieldIsPresent())
375   {
376     UInt32 xlen;
377     RINOK(ReadUInt16(stream, xlen /* , crc */));
378     RINOK(SkipBytes(stream, xlen));
379     // Extra.SetCapacity(xlen);
380     // RINOK(ReadStream_FALSE(stream, Extra, xlen));
381     // crc = CrcUpdate(crc, Extra, xlen);
382   }
383   if (NameIsPresent())
384     RINOK(ReadString(stream, Name, kNameMaxLen /* , crc */));
385   if (CommentIsPresent())
386     RINOK(ReadString(stream, Comment, kCommentMaxLen /* , crc */));
387 
388   if (HeaderCrcIsPresent())
389   {
390     UInt32 headerCRC;
391     // UInt32 dummy = 0;
392     RINOK(ReadUInt16(stream, headerCRC /* , dummy */));
393     /*
394     if ((UInt16)CRC_GET_DIGEST(crc) != headerCRC)
395       return S_FALSE;
396     */
397   }
398   return stream->InputEofError() ? S_FALSE : S_OK;
399 }
400 
ReadFooter1(NDecoder::CCOMCoder * stream)401 HRESULT CItem::ReadFooter1(NDecoder::CCOMCoder *stream)
402 {
403   Byte buf[8];
404   RINOK(ReadBytes(stream, buf, 8));
405   Crc = Get32(buf);
406   Size32 = Get32(buf + 4);
407   return stream->InputEofError() ? S_FALSE : S_OK;
408 }
409 
ReadFooter2(ISequentialInStream * stream)410 HRESULT CItem::ReadFooter2(ISequentialInStream *stream)
411 {
412   Byte buf[8];
413   RINOK(ReadStream_FALSE(stream, buf, 8));
414   Crc = Get32(buf);
415   Size32 = Get32(buf + 4);
416   return S_OK;
417 }
418 
WriteHeader(ISequentialOutStream * stream)419 HRESULT CItem::WriteHeader(ISequentialOutStream *stream)
420 {
421   Byte buf[10];
422   buf[0] = kSignature_0;
423   buf[1] = kSignature_1;
424   buf[2] = kSignature_2;
425   buf[3] = (Byte)(Flags & NFlags::kName);
426   // buf[3] |= NFlags::kCrc;
427   SetUi32(buf + 4, Time);
428   buf[8] = ExtraFlags;
429   buf[9] = HostOS;
430   RINOK(WriteStream(stream, buf, 10));
431   // crc = CrcUpdate(CRC_INIT_VAL, buf, 10);
432   if (NameIsPresent())
433   {
434     // crc = CrcUpdate(crc, (const char *)Name, Name.Len() + 1);
435     RINOK(WriteStream(stream, (const char *)Name, Name.Len() + 1));
436   }
437   // SetUi16(buf, (UInt16)CRC_GET_DIGEST(crc));
438   // RINOK(WriteStream(stream, buf, 2));
439   return S_OK;
440 }
441 
WriteFooter(ISequentialOutStream * stream)442 HRESULT CItem::WriteFooter(ISequentialOutStream *stream)
443 {
444   Byte buf[8];
445   SetUi32(buf, Crc);
446   SetUi32(buf + 4, Size32);
447   return WriteStream(stream, buf, 8);
448 }
449 
450 class CHandler:
451   public IInArchive,
452   public IArchiveOpenSeq,
453   public IOutArchive,
454   public ISetProperties,
455   public CMyUnknownImp
456 {
457   CItem _item;
458 
459   bool _isArc;
460   bool _needSeekToStart;
461   bool _dataAfterEnd;
462   bool _needMoreInput;
463 
464   bool _packSize_Defined;
465   bool _unpackSize_Defined;
466   bool _numStreams_Defined;
467 
468   UInt64 _packSize;
469   UInt64 _unpackSize; // real unpack size (NOT from footer)
470   UInt64 _numStreams;
471   UInt64 _headerSize; // only start header (without footer)
472 
473   CMyComPtr<IInStream> _stream;
474   CMyComPtr<ICompressCoder> _decoder;
475   NDecoder::CCOMCoder *_decoderSpec;
476 
477   CSingleMethodProps _props;
478 
479 public:
480   MY_UNKNOWN_IMP4(
481       IInArchive,
482       IArchiveOpenSeq,
483       IOutArchive,
484       ISetProperties)
485   INTERFACE_IInArchive(;)
486   INTERFACE_IOutArchive(;)
487   STDMETHOD(OpenSeq)(ISequentialInStream *stream);
488   STDMETHOD(SetProperties)(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps);
489 
CHandler()490   CHandler()
491   {
492     _decoderSpec = new NDecoder::CCOMCoder;
493     _decoder = _decoderSpec;
494   }
495 };
496 
497 static const Byte kProps[] =
498 {
499   kpidPath,
500   kpidSize,
501   kpidPackSize,
502   kpidMTime,
503   kpidHostOS,
504   kpidCRC
505   // kpidComment
506 };
507 
508 static const Byte kArcProps[] =
509 {
510   kpidHeadersSize,
511   kpidNumStreams
512 };
513 
514 
515 IMP_IInArchive_Props
516 IMP_IInArchive_ArcProps
517 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)518 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
519 {
520   COM_TRY_BEGIN
521   NCOM::CPropVariant prop;
522   switch (propID)
523   {
524     case kpidPhySize: if (_packSize_Defined) prop = _packSize; break;
525     case kpidUnpackSize: if (_unpackSize_Defined) prop = _unpackSize; break;
526     case kpidNumStreams: if (_numStreams_Defined) prop = _numStreams; break;
527     case kpidHeadersSize: if (_headerSize != 0) prop = _headerSize; break;
528     case kpidErrorFlags:
529     {
530       UInt32 v = 0;
531       if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;;
532       if (_needMoreInput) v |= kpv_ErrorFlags_UnexpectedEnd;
533       if (_dataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd;
534       prop = v;
535       break;
536     }
537     case kpidName:
538       if (_item.NameIsPresent())
539       {
540         UString s = MultiByteToUnicodeString(_item.Name, CP_ACP);
541         s += ".gz";
542         prop = s;
543       }
544       break;
545   }
546   prop.Detach(value);
547   return S_OK;
548   COM_TRY_END
549 }
550 
551 
GetNumberOfItems(UInt32 * numItems)552 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
553 {
554   *numItems = 1;
555   return S_OK;
556 }
557 
GetProperty(UInt32,PROPID propID,PROPVARIANT * value)558 STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
559 {
560   COM_TRY_BEGIN
561   NCOM::CPropVariant prop;
562   switch (propID)
563   {
564     case kpidPath:
565       if (_item.NameIsPresent())
566         prop = MultiByteToUnicodeString(_item.Name, CP_ACP);
567       break;
568     // case kpidComment: if (_item.CommentIsPresent()) prop = MultiByteToUnicodeString(_item.Comment, CP_ACP); break;
569     case kpidMTime:
570       if (_item.Time != 0)
571       {
572         FILETIME utc;
573         NTime::UnixTimeToFileTime(_item.Time, utc);
574         prop = utc;
575       }
576       break;
577     case kpidSize:
578     {
579       if (_unpackSize_Defined)
580         prop = _unpackSize;
581       else if (_stream)
582         prop = (UInt64)_item.Size32;
583       break;
584     }
585     case kpidPackSize:
586     {
587       if (_packSize_Defined || _stream)
588         prop = _packSize;
589       break;
590     }
591     case kpidHostOS: TYPE_TO_PROP(kHostOSes, _item.HostOS, prop); break;
592     case kpidCRC: if (_stream) prop = _item.Crc; break;
593   }
594   prop.Detach(value);
595   return S_OK;
596   COM_TRY_END
597 }
598 
599 class CCompressProgressInfoImp:
600   public ICompressProgressInfo,
601   public CMyUnknownImp
602 {
603   CMyComPtr<IArchiveOpenCallback> Callback;
604 public:
605   UInt64 Offset;
606   MY_UNKNOWN_IMP1(ICompressProgressInfo)
607   STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
Init(IArchiveOpenCallback * callback)608   void Init(IArchiveOpenCallback *callback) { Callback = callback; }
609 };
610 
SetRatioInfo(const UInt64 * inSize,const UInt64 *)611 STDMETHODIMP CCompressProgressInfoImp::SetRatioInfo(const UInt64 *inSize, const UInt64 * /* outSize */)
612 {
613   if (Callback)
614   {
615     UInt64 files = 0;
616     UInt64 value = Offset + *inSize;
617     return Callback->SetCompleted(&files, &value);
618   }
619   return S_OK;
620 }
621 
622 /*
623 */
624 
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback *)625 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *)
626 {
627   COM_TRY_BEGIN
628   RINOK(OpenSeq(stream));
629   _isArc = false;
630   UInt64 endPos;
631   RINOK(stream->Seek(-8, STREAM_SEEK_END, &endPos));
632   _packSize = endPos + 8;
633   RINOK(_item.ReadFooter2(stream));
634   _stream = stream;
635   _isArc = true;
636   _needSeekToStart = true;
637   return S_OK;
638   COM_TRY_END
639 }
640 
OpenSeq(ISequentialInStream * stream)641 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
642 {
643   COM_TRY_BEGIN
644   try
645   {
646     Close();
647     _decoderSpec->SetInStream(stream);
648     _decoderSpec->InitInStream(true);
649     RINOK(_item.ReadHeader(_decoderSpec));
650     if (_decoderSpec->InputEofError())
651       return S_FALSE;
652     _headerSize = _decoderSpec->GetInputProcessedSize();
653     _isArc = true;
654     return S_OK;
655   }
656   catch(const CInBufferException &e) { return e.ErrorCode; }
657   COM_TRY_END
658 }
659 
Close()660 STDMETHODIMP CHandler::Close()
661 {
662   _isArc = false;
663   _needSeekToStart = false;
664   _dataAfterEnd = false;
665   _needMoreInput = false;
666 
667   _packSize_Defined = false;
668   _unpackSize_Defined = false;
669   _numStreams_Defined = false;
670 
671   _packSize = 0;
672   _headerSize = 0;
673 
674   _stream.Release();
675   _decoderSpec->ReleaseInStream();
676   return S_OK;
677 }
678 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)679 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
680     Int32 testMode, IArchiveExtractCallback *extractCallback)
681 {
682   COM_TRY_BEGIN
683   if (numItems == 0)
684     return S_OK;
685   if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
686     return E_INVALIDARG;
687 
688   if (_packSize_Defined)
689     extractCallback->SetTotal(_packSize);
690   // UInt64 currentTotalPacked = 0;
691   // RINOK(extractCallback->SetCompleted(&currentTotalPacked));
692   CMyComPtr<ISequentialOutStream> realOutStream;
693   Int32 askMode = testMode ?
694       NExtract::NAskMode::kTest :
695       NExtract::NAskMode::kExtract;
696   RINOK(extractCallback->GetStream(0, &realOutStream, askMode));
697   if (!testMode && !realOutStream)
698     return S_OK;
699 
700   extractCallback->PrepareOperation(askMode);
701 
702   COutStreamWithCRC *outStreamSpec = new COutStreamWithCRC;
703   CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
704   outStreamSpec->SetStream(realOutStream);
705   outStreamSpec->Init();
706   realOutStream.Release();
707 
708   CLocalProgress *lps = new CLocalProgress;
709   CMyComPtr<ICompressProgressInfo> progress = lps;
710   lps->Init(extractCallback, true);
711 
712   bool needReadFirstItem = _needSeekToStart;
713 
714   if (_needSeekToStart)
715   {
716     if (!_stream)
717       return E_FAIL;
718     RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL));
719     _decoderSpec->InitInStream(true);
720     // printf("\nSeek");
721   }
722   else
723     _needSeekToStart = true;
724 
725   bool firstItem = true;
726 
727   UInt64 packSize = _decoderSpec->GetInputProcessedSize();
728   // printf("\npackSize = %d", (unsigned)packSize);
729 
730   UInt64 unpackedSize = 0;
731   UInt64 numStreams = 0;
732 
733   bool crcError = false;
734 
735   HRESULT result = S_OK;
736 
737   try {
738 
739   for (;;)
740   {
741     lps->InSize = packSize;
742     lps->OutSize = unpackedSize;
743 
744     RINOK(lps->SetCur());
745 
746     CItem item;
747 
748     if (!firstItem || needReadFirstItem)
749     {
750       result = item.ReadHeader(_decoderSpec);
751 
752       if (result != S_OK && result != S_FALSE)
753         return result;
754 
755       if (_decoderSpec->InputEofError())
756         result = S_FALSE;
757 
758       if (result != S_OK && firstItem)
759       {
760         _isArc = false;
761         break;
762       }
763 
764       if (packSize == _decoderSpec->GetStreamSize())
765       {
766         result = S_OK;
767         break;
768       }
769 
770       if (result != S_OK)
771       {
772         _dataAfterEnd = true;
773         break;
774       }
775     }
776 
777     numStreams++;
778     firstItem = false;
779 
780     UInt64 startOffset = outStreamSpec->GetSize();
781     outStreamSpec->InitCRC();
782 
783     result = _decoderSpec->CodeResume(outStream, NULL, progress);
784 
785     packSize = _decoderSpec->GetInputProcessedSize();
786     unpackedSize = outStreamSpec->GetSize();
787 
788     if (result != S_OK && result != S_FALSE)
789       return result;
790 
791     if (_decoderSpec->InputEofError())
792     {
793       packSize = _decoderSpec->GetStreamSize();
794       _needMoreInput = true;
795       result = S_FALSE;
796     }
797 
798     if (result != S_OK)
799       break;
800 
801     _decoderSpec->AlignToByte();
802 
803     result = item.ReadFooter1(_decoderSpec);
804 
805     packSize = _decoderSpec->GetInputProcessedSize();
806 
807     if (result != S_OK && result != S_FALSE)
808       return result;
809 
810     if (result != S_OK)
811     {
812       if (_decoderSpec->InputEofError())
813       {
814         _needMoreInput = true;
815         result = S_FALSE;
816       }
817       break;
818     }
819 
820     if (item.Crc != outStreamSpec->GetCRC() ||
821         item.Size32 != (UInt32)(unpackedSize - startOffset))
822     {
823       crcError = true;
824       result = S_FALSE;
825       break;
826     }
827 
828     // break; // we can use break, if we need only first stream
829   }
830 
831   } catch(const CInBufferException &e) { return e.ErrorCode; }
832 
833   if (!firstItem)
834   {
835     _packSize = packSize;
836     _unpackSize = unpackedSize;
837     _numStreams = numStreams;
838 
839     _packSize_Defined = true;
840     _unpackSize_Defined = true;
841     _numStreams_Defined = true;
842   }
843 
844   outStream.Release();
845 
846   Int32 retResult = NExtract::NOperationResult::kDataError;
847 
848   if (!_isArc)
849     retResult = NExtract::NOperationResult::kIsNotArc;
850   else if (_needMoreInput)
851     retResult = NExtract::NOperationResult::kUnexpectedEnd;
852   else if (crcError)
853     retResult = NExtract::NOperationResult::kCRCError;
854   else if (_dataAfterEnd)
855     retResult = NExtract::NOperationResult::kDataAfterEnd;
856   else if (result == S_FALSE)
857     retResult = NExtract::NOperationResult::kDataError;
858   else if (result == S_OK)
859     retResult = NExtract::NOperationResult::kOK;
860   else
861     return result;
862 
863   return extractCallback->SetOperationResult(retResult);
864 
865 
866   COM_TRY_END
867 }
868 
869 static const Byte kHostOS =
870   #ifdef _WIN32
871   NHostOS::kFAT;
872   #else
873   NHostOS::kUnix;
874   #endif
875 
UpdateArchive(ISequentialOutStream * outStream,UInt64 unpackSize,CItem & item,const CSingleMethodProps & props,IArchiveUpdateCallback * updateCallback)876 static HRESULT UpdateArchive(
877     ISequentialOutStream *outStream,
878     UInt64 unpackSize,
879     CItem &item,
880     const CSingleMethodProps &props,
881     IArchiveUpdateCallback *updateCallback)
882 {
883   UInt64 complexity = 0;
884   RINOK(updateCallback->SetTotal(unpackSize));
885   RINOK(updateCallback->SetCompleted(&complexity));
886 
887   CMyComPtr<ISequentialInStream> fileInStream;
888 
889   RINOK(updateCallback->GetStream(0, &fileInStream));
890 
891   CSequentialInStreamWithCRC *inStreamSpec = new CSequentialInStreamWithCRC;
892   CMyComPtr<ISequentialInStream> crcStream(inStreamSpec);
893   inStreamSpec->SetStream(fileInStream);
894   inStreamSpec->Init();
895 
896   CLocalProgress *lps = new CLocalProgress;
897   CMyComPtr<ICompressProgressInfo> progress = lps;
898   lps->Init(updateCallback, true);
899 
900   item.ExtraFlags = props.GetLevel() >= 7 ?
901       NExtraFlags::kMaximum :
902       NExtraFlags::kFastest;
903 
904   item.HostOS = kHostOS;
905 
906   RINOK(item.WriteHeader(outStream));
907 
908   NEncoder::CCOMCoder *deflateEncoderSpec = new NEncoder::CCOMCoder;
909   CMyComPtr<ICompressCoder> deflateEncoder = deflateEncoderSpec;
910   RINOK(props.SetCoderProps(deflateEncoderSpec, NULL));
911   RINOK(deflateEncoder->Code(crcStream, outStream, NULL, NULL, progress));
912 
913   item.Crc = inStreamSpec->GetCRC();
914   item.Size32 = (UInt32)inStreamSpec->GetSize();
915   RINOK(item.WriteFooter(outStream));
916   return updateCallback->SetOperationResult(NUpdate::NOperationResult::kOK);
917 }
918 
GetFileTimeType(UInt32 * timeType)919 STDMETHODIMP CHandler::GetFileTimeType(UInt32 *timeType)
920 {
921   *timeType = NFileTimeType::kUnix;
922   return S_OK;
923 }
924 
UpdateItems(ISequentialOutStream * outStream,UInt32 numItems,IArchiveUpdateCallback * updateCallback)925 STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
926     IArchiveUpdateCallback *updateCallback)
927 {
928   COM_TRY_BEGIN
929 
930   if (numItems != 1)
931     return E_INVALIDARG;
932 
933   Int32 newData, newProps;
934   UInt32 indexInArchive;
935   if (!updateCallback)
936     return E_FAIL;
937   RINOK(updateCallback->GetUpdateItemInfo(0, &newData, &newProps, &indexInArchive));
938 
939   CItem newItem;
940 
941   if (!IntToBool(newProps))
942   {
943     newItem.CopyMetaPropsFrom(_item);
944   }
945   else
946   {
947     newItem.HostOS = kHostOS;
948     {
949       NCOM::CPropVariant prop;
950       RINOK(updateCallback->GetProperty(0, kpidMTime, &prop));
951       if (prop.vt == VT_FILETIME)
952         NTime::FileTimeToUnixTime(prop.filetime, newItem.Time);
953       else if (prop.vt == VT_EMPTY)
954         newItem.Time = 0;
955       else
956         return E_INVALIDARG;
957     }
958     {
959       NCOM::CPropVariant prop;
960       RINOK(updateCallback->GetProperty(0, kpidPath, &prop));
961       if (prop.vt == VT_BSTR)
962       {
963         UString name = prop.bstrVal;
964         int slashPos = name.ReverseFind_PathSepar();
965         if (slashPos >= 0)
966           name.DeleteFrontal((unsigned)(slashPos + 1));
967         newItem.Name = UnicodeStringToMultiByte(name, CP_ACP);
968         if (!newItem.Name.IsEmpty())
969           newItem.Flags |= NFlags::kName;
970       }
971       else if (prop.vt != VT_EMPTY)
972         return E_INVALIDARG;
973     }
974     {
975       NCOM::CPropVariant prop;
976       RINOK(updateCallback->GetProperty(0, kpidIsDir, &prop));
977       if (prop.vt != VT_EMPTY)
978         if (prop.vt != VT_BOOL || prop.boolVal != VARIANT_FALSE)
979           return E_INVALIDARG;
980     }
981   }
982 
983   if (IntToBool(newData))
984   {
985     UInt64 size;
986     {
987       NCOM::CPropVariant prop;
988       RINOK(updateCallback->GetProperty(0, kpidSize, &prop));
989       if (prop.vt != VT_UI8)
990         return E_INVALIDARG;
991       size = prop.uhVal.QuadPart;
992     }
993     return UpdateArchive(outStream, size, newItem, _props, updateCallback);
994   }
995 
996   if (indexInArchive != 0)
997     return E_INVALIDARG;
998 
999   if (!_stream)
1000     return E_NOTIMPL;
1001 
1002   CLocalProgress *lps = new CLocalProgress;
1003   CMyComPtr<ICompressProgressInfo> progress = lps;
1004   lps->Init(updateCallback, true);
1005 
1006   CMyComPtr<IArchiveUpdateCallbackFile> opCallback;
1007   updateCallback->QueryInterface(IID_IArchiveUpdateCallbackFile, (void **)&opCallback);
1008   if (opCallback)
1009   {
1010     RINOK(opCallback->ReportOperation(
1011         NEventIndexType::kInArcIndex, 0,
1012         NUpdateNotifyOp::kReplicate))
1013   }
1014 
1015   newItem.CopyDataPropsFrom(_item);
1016 
1017   UInt64 offset = 0;
1018   if (IntToBool(newProps))
1019   {
1020     newItem.WriteHeader(outStream);
1021     offset += _headerSize;
1022   }
1023   RINOK(_stream->Seek((Int64)offset, STREAM_SEEK_SET, NULL));
1024 
1025   return NCompress::CopyStream(_stream, outStream, progress);
1026 
1027   COM_TRY_END
1028 }
1029 
SetProperties(const wchar_t * const * names,const PROPVARIANT * values,UInt32 numProps)1030 STDMETHODIMP CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps)
1031 {
1032   return _props.SetProperties(names, values, numProps);
1033 }
1034 
1035 static const Byte k_Signature[] = { kSignature_0, kSignature_1, kSignature_2 };
1036 
1037 REGISTER_ARC_IO(
1038   "gzip", "gz gzip tgz tpz apk", "* * .tar .tar .tar", 0xEF,
1039   k_Signature,
1040   0,
1041   NArcInfoFlags::kKeepName,
1042   IsArc_Gz)
1043 
1044 }}
1045