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