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