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(¤tTotalPacked));
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