1 // DmgHandler.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../C/Alloc.h"
6 #include "../../../C/CpuArch.h"
7
8 #include "../../Common/AutoPtr.h"
9 #include "../../Common/ComTry.h"
10 #include "../../Common/IntToString.h"
11 #include "../../Common/MyBuffer2.h"
12 #include "../../Common/MyXml.h"
13 #include "../../Common/UTFConvert.h"
14
15 #include "../../Windows/PropVariant.h"
16
17 #include "../Common/LimitedStreams.h"
18 #include "../Common/ProgressUtils.h"
19 #include "../Common/RegisterArc.h"
20 #include "../Common/StreamObjects.h"
21 #include "../Common/StreamUtils.h"
22
23 #include "../Compress/BZip2Decoder.h"
24 #include "../Compress/CopyCoder.h"
25 #include "../Compress/LzfseDecoder.h"
26 #include "../Compress/XzDecoder.h"
27 #include "../Compress/ZlibDecoder.h"
28
29 #include "Common/OutStreamWithCRC.h"
30
31 // #define DMG_SHOW_RAW
32
33 // #define SHOW_DEBUG_INFO
34
35 /* for debug only: we can use block cache also for METHOD_COPY blocks.
36 It can reduce the number of Stream->Read() requests.
37 But it can increase memory usage.
38 Note: METHOD_COPY blocks are not fused usually.
39 But if METHOD_COPY blocks is fused in some dmg example,
40 block size can exceed k_Chunk_Size_MAX.
41 So we don't use cache for METHOD_COPY block, if (block_size > k_Chunk_Size_MAX)
42 */
43 // for debug only:
44 // #define Z7_DMG_USE_CACHE_FOR_COPY_BLOCKS
45
46 #ifdef SHOW_DEBUG_INFO
47 #include <stdio.h>
48 #define PRF(x) x
49 #else
50 #define PRF(x)
51 #endif
52
53
54 #define Get16(p) GetBe16(p)
55 #define Get32(p) GetBe32(p)
56 #define Get64(p) GetBe64(p)
57
58 #define Get32a(p) GetBe32a(p)
59 #define Get64a(p) GetBe64a(p)
60
61 Byte *Base64ToBin(Byte *dest, const char *src);
62
63 namespace NArchive {
64 namespace NDmg {
65
66 // allocation limits for compressed blocks for GetStream() interface:
67 static const unsigned k_NumChunks_MAX = 128;
68 static const size_t k_Chunk_Size_MAX = (size_t)1 << 28;
69 // 256 MB cache for 32-bit:
70 // 4 GB cache for 64-bit:
71 static const size_t k_Chunks_TotalSize_MAX = (size_t)1 << (sizeof(size_t) + 24);
72
73 // 2 GB limit for 32-bit:
74 // 4 GB limit for 64-bit:
75 // that limit can be increased for 64-bit mode, if there are such dmg files
76 static const size_t k_XmlSize_MAX =
77 ((size_t)1 << (sizeof(size_t) / 4 + 30)) - 256;
78
79 static const UInt32 METHOD_ZERO_0 = 0; // sparse
80 static const UInt32 METHOD_COPY = 1;
81 static const UInt32 METHOD_ZERO_2 = 2; // sparse : without file CRC calculation
82 static const UInt32 METHOD_ADC = 0x80000004;
83 static const UInt32 METHOD_ZLIB = 0x80000005;
84 static const UInt32 METHOD_BZIP2 = 0x80000006;
85 static const UInt32 METHOD_LZFSE = 0x80000007;
86 static const UInt32 METHOD_XZ = 0x80000008;
87 static const UInt32 METHOD_COMMENT = 0x7FFFFFFE; // is used to comment "+beg" and "+end" in extra field.
88 static const UInt32 METHOD_END = 0xFFFFFFFF;
89
90
91 struct CBlock
92 {
93 UInt32 Type;
94 UInt64 UnpPos;
95 // UInt64 UnpSize;
96 UInt64 PackPos;
97 UInt64 PackSize;
98
NeedCrcNArchive::NDmg::CBlock99 bool NeedCrc() const { return Type != METHOD_ZERO_2; }
IsZeroMethodNArchive::NDmg::CBlock100 bool IsZeroMethod() const
101 {
102 return (Type & ~(UInt32)METHOD_ZERO_2) == 0;
103 // return Type == METHOD_ZERO_0 || Type == METHOD_ZERO_2;
104 }
105
IsClusteredMethodNArchive::NDmg::CBlock106 bool IsClusteredMethod() const
107 {
108 // most of dmg files have non-fused COPY_METHOD blocks.
109 // so we don't exclude COPY_METHOD blocks when we try to detect size of cluster.
110 return !IsZeroMethod(); // include COPY_METHOD blocks.
111 // Type > METHOD_ZERO_2; // for debug: exclude COPY_METHOD blocks.
112 }
113
NeedAllocateBufferNArchive::NDmg::CBlock114 bool NeedAllocateBuffer() const
115 {
116 return
117 #ifdef Z7_DMG_USE_CACHE_FOR_COPY_BLOCKS
118 !IsZeroMethod();
119 #else
120 Type > METHOD_ZERO_2;
121 // !IsZeroMethod() && Type != METHOD_COPY;
122 #endif
123 }
124 // we don't store non-data blocks now
125 // bool ThereAreDataInBlock() const { return Type != METHOD_COMMENT && Type != METHOD_END; }
126 };
127
128 static const UInt32 kCheckSumType_CRC = 2;
129 static const unsigned kChecksumSize_Max = 0x80;
130
131 struct CChecksum
132 {
133 UInt32 Type;
134 UInt32 NumBits;
135 Byte Data[kChecksumSize_Max];
136
IsCrc32NArchive::NDmg::CChecksum137 bool IsCrc32() const { return Type == kCheckSumType_CRC && NumBits == 32; }
GetCrc32NArchive::NDmg::CChecksum138 UInt32 GetCrc32() const { return Get32(Data); }
139 void Parse(const Byte *p);
140
141 void PrintType(AString &s) const;
142 void Print(AString &s) const;
143 void Print_with_Name(AString &s) const;
144 void AddToComment(AString &s, const char *name) const;
145 };
146
Parse(const Byte * p)147 void CChecksum::Parse(const Byte *p)
148 {
149 Type = Get32(p);
150 NumBits = Get32(p + 4);
151 memcpy(Data, p + 8, kChecksumSize_Max);
152 }
153
154
PrintType(AString & s) const155 void CChecksum::PrintType(AString &s) const
156 {
157 if (NumBits == 0)
158 return;
159 if (IsCrc32())
160 s += "CRC";
161 else
162 {
163 s += "Checksum";
164 s.Add_UInt32(Type);
165 s.Add_Minus();
166 s.Add_UInt32(NumBits);
167 }
168 }
169
Print(AString & s) const170 void CChecksum::Print(AString &s) const
171 {
172 if (NumBits == 0)
173 return;
174 char temp[kChecksumSize_Max * 2 + 2];
175 /*
176 if (IsCrc32())
177 ConvertUInt32ToHex8Digits(GetCrc32(), temp);
178 else
179 */
180 {
181 UInt32 numBits = kChecksumSize_Max * 8;
182 if (numBits > NumBits)
183 numBits = NumBits;
184 const unsigned numBytes = (numBits + 7) >> 3;
185 if (numBytes <= 8)
186 ConvertDataToHex_Upper(temp, Data, numBytes);
187 else
188 ConvertDataToHex_Lower(temp, Data, numBytes);
189 }
190 s += temp;
191 }
192
Print_with_Name(AString & s) const193 void CChecksum::Print_with_Name(AString &s) const
194 {
195 if (NumBits == 0)
196 return;
197 PrintType(s);
198 s += ": ";
199 Print(s);
200 }
201
AddToComment_Prop(AString & s,const char * name,const char * val)202 static void AddToComment_Prop(AString &s, const char *name, const char *val)
203 {
204 s += name;
205 s += ": ";
206 s += val;
207 s.Add_LF();
208 }
209
AddToComment(AString & s,const char * name) const210 void CChecksum::AddToComment(AString &s, const char *name) const
211 {
212 AString s2;
213 Print_with_Name(s2);
214 if (!s2.IsEmpty())
215 AddToComment_Prop(s, name, s2);
216 }
217
218
219 struct CFile
220 {
221 UInt64 Size;
222 CRecordVector<CBlock> Blocks;
223 UInt64 PackSize;
224 UInt64 StartPackPos;
225 UInt64 BlockSize_MAX;
226 UInt64 StartUnpackSector; // unpack sector position of this file from all files
227 UInt64 NumUnpackSectors;
228 Int32 Descriptor;
229 bool IsCorrect;
230 bool FullFileChecksum;
231 AString Name;
232 // AString Id;
233 CChecksum Checksum;
234
GetUnpackSize_of_BlockNArchive::NDmg::CFile235 UInt64 GetUnpackSize_of_Block(unsigned blockIndex) const
236 {
237 return (blockIndex == Blocks.Size() - 1 ?
238 Size : Blocks[blockIndex + 1].UnpPos) - Blocks[blockIndex].UnpPos;
239 }
240
CFileNArchive::NDmg::CFile241 CFile():
242 Size(0),
243 PackSize(0),
244 StartPackPos(0),
245 BlockSize_MAX(0),
246 StartUnpackSector(0),
247 NumUnpackSectors(0),
248 Descriptor(0),
249 IsCorrect(false),
250 FullFileChecksum(false)
251 {
252 Checksum.Type = 0;
253 Checksum.NumBits = 0;
254 }
255 HRESULT Parse(const Byte *p, UInt32 size);
256 };
257
258 #ifdef DMG_SHOW_RAW
259 struct CExtraFile
260 {
261 CByteBuffer Data;
262 AString Name;
263 };
264 #endif
265
266
AddToComment_UInt64(AString & s,UInt64 v,const char * name)267 static void AddToComment_UInt64(AString &s, UInt64 v, const char *name)
268 {
269 s += name;
270 s += ": ";
271 s.Add_UInt64(v);
272 s.Add_LF();
273 }
274
275 struct CForkPair
276 {
277 UInt64 Offset;
278 UInt64 Len;
279
280 // (p) is aligned for 8-bytes
ParseNArchive::NDmg::CForkPair281 void Parse(const Byte *p)
282 {
283 Offset = Get64a(p);
284 Len = Get64a(p + 8);
285 }
286
GetEndPosNArchive::NDmg::CForkPair287 bool GetEndPos(UInt64 &endPos)
288 {
289 endPos = Offset + Len;
290 return endPos >= Offset;
291 }
292
UpdateTopNArchive::NDmg::CForkPair293 bool UpdateTop(UInt64 limit, UInt64 &top)
294 {
295 if (Offset > limit || Len > limit - Offset)
296 return false;
297 const UInt64 top2 = Offset + Len;
298 if (top <= top2)
299 top = top2;
300 return true;
301 }
302
303 void Print(AString &s, const char *name) const;
304 };
305
Print(AString & s,const char * name) const306 void CForkPair::Print(AString &s, const char *name) const
307 {
308 if (Offset != 0 || Len != 0)
309 {
310 s += name; s.Add_Minus(); AddToComment_UInt64(s, Offset, "offset");
311 s += name; s.Add_Minus(); AddToComment_UInt64(s, Len, "length");
312 }
313 }
314
315
316 Z7_CLASS_IMP_CHandler_IInArchive_1(
317 IInArchiveGetStream
318 )
319 bool _masterCrcError;
320 bool _headersError;
321 bool _dataForkError;
322 bool _rsrcMode_wasUsed;
323
324 CMyComPtr<IInStream> _inStream;
325 CObjectVector<CFile> _files;
326
327 // UInt32 _dataStartOffset;
328 UInt64 _startPos;
329 UInt64 _phySize;
330
331 AString _name;
332
333 CForkPair _dataForkPair;
334 CForkPair rsrcPair, xmlPair, blobPair;
335
336 // UInt64 _runningDataForkOffset;
337 // UInt32 _segmentNumber;
338 // UInt32 _segmentCount;
339 UInt64 _numSectors; // total unpacked number of sectors
340 Byte _segmentGUID[16];
341
342 CChecksum _dataForkChecksum;
343 CChecksum _masterChecksum;
344
345 #ifdef DMG_SHOW_RAW
346 CObjectVector<CExtraFile> _extras;
347 #endif
348
349 HRESULT ReadData(IInStream *stream, const CForkPair &pair, CByteBuffer &buf);
350 bool ParseBlob(const CByteBuffer &data);
351 HRESULT Open2(IInStream *stream, IArchiveOpenCallback *openArchiveCallback);
352 HRESULT Extract(IInStream *stream);
353 };
354
355
356 struct CMethods
357 {
358 CRecordVector<UInt32> Types;
359
360 void Update(const CFile &file);
361 void AddToString(AString &s) const;
362 };
363
Update(const CFile & file)364 void CMethods::Update(const CFile &file)
365 {
366 FOR_VECTOR (i, file.Blocks)
367 {
368 if (Types.Size() >= (1 << 8))
369 break;
370 Types.AddToUniqueSorted(file.Blocks[i].Type);
371 }
372 }
373
AddToString(AString & res) const374 void CMethods::AddToString(AString &res) const
375 {
376 FOR_VECTOR (i, Types)
377 {
378 const UInt32 type = Types[i];
379 /*
380 if (type == METHOD_COMMENT || type == METHOD_END)
381 continue;
382 */
383 char buf[16];
384 const char *s;
385 switch (type)
386 {
387 // case METHOD_COMMENT: s = "Comment"; break;
388 case METHOD_ZERO_0: s = "Zero0"; break;
389 case METHOD_ZERO_2: s = "Zero2"; break;
390 case METHOD_COPY: s = "Copy"; break;
391 case METHOD_ADC: s = "ADC"; break;
392 case METHOD_ZLIB: s = "ZLIB"; break;
393 case METHOD_BZIP2: s = "BZip2"; break;
394 case METHOD_LZFSE: s = "LZFSE"; break;
395 case METHOD_XZ: s = "XZ"; break;
396 default: ConvertUInt32ToHex(type, buf); s = buf;
397 }
398 res.Add_OptSpaced(s);
399 }
400 }
401
402
403
404 struct CAppleName
405 {
406 bool IsFs;
407 const char *Ext;
408 const char *AppleName;
409 };
410
411 static const CAppleName k_Names[] =
412 {
413 { true, "hfs", "Apple_HFS" },
414 { true, "hfsx", "Apple_HFSX" },
415 { true, "ufs", "Apple_UFS" },
416 { true, "apfs", "Apple_APFS" },
417 { true, "iso", "Apple_ISO" },
418
419 // efi_sys partition is FAT32, but it's not main file. So we use (IsFs = false)
420 { false, "efi_sys", "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" },
421
422 { false, "free", "Apple_Free" },
423 { false, "ddm", "DDM" },
424 { false, NULL, "Apple_partition_map" },
425 { false, NULL, " GPT " },
426 /* " GPT " is substring of full name entry in dmg that contains
427 some small metadata GPT entry. It's not real FS entry:
428 "Primary GPT Header",
429 "Primary GPT Table"
430 "Backup GPT Header",
431 "Backup GPT Table",
432 */
433 { false, NULL, "MBR" },
434 { false, NULL, "Driver" },
435 { false, NULL, "Patches" }
436 };
437
438 static const unsigned kNumAppleNames = Z7_ARRAY_SIZE(k_Names);
439
440 const char *Find_Apple_FS_Ext(const AString &name);
Find_Apple_FS_Ext(const AString & name)441 const char *Find_Apple_FS_Ext(const AString &name)
442 {
443 for (unsigned i = 0; i < kNumAppleNames; i++)
444 {
445 const CAppleName &a = k_Names[i];
446 if (a.Ext)
447 if (name == a.AppleName)
448 return a.Ext;
449 }
450 return NULL;
451 }
452
453
454 bool Is_Apple_FS_Or_Unknown(const AString &name);
Is_Apple_FS_Or_Unknown(const AString & name)455 bool Is_Apple_FS_Or_Unknown(const AString &name)
456 {
457 for (unsigned i = 0; i < kNumAppleNames; i++)
458 {
459 const CAppleName &a = k_Names[i];
460 // if (name.Find(a.AppleName) >= 0)
461 if (strstr(name, a.AppleName))
462 return a.IsFs;
463 }
464 return true;
465 }
466
467
468
469 // define it for debug only:
470 // #define Z7_DMG_SINGLE_FILE_MODE
471
472 static const Byte kProps[] =
473 {
474 kpidPath,
475 kpidSize,
476 kpidPackSize,
477 kpidComment,
478 kpidMethod,
479 kpidNumBlocks,
480 kpidClusterSize,
481 kpidChecksum,
482 kpidCRC,
483 kpidId
484 // kpidOffset
485 #ifdef Z7_DMG_SINGLE_FILE_MODE
486 , kpidPosition
487 #endif
488 };
489
490 IMP_IInArchive_Props
491
492 static const Byte kArcProps[] =
493 {
494 kpidMethod,
495 kpidNumBlocks,
496 kpidClusterSize,
497 kpidComment
498 };
499
500
Z7_COM7F_IMF(CHandler::GetArchiveProperty (PROPID propID,PROPVARIANT * value))501 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
502 {
503 COM_TRY_BEGIN
504 NWindows::NCOM::CPropVariant prop;
505 switch (propID)
506 {
507 case kpidMethod:
508 {
509 CMethods m;
510 CRecordVector<UInt32> ChecksumTypes;
511 {
512 FOR_VECTOR (i, _files)
513 {
514 const CFile &file = _files[i];
515 m.Update(file);
516 if (ChecksumTypes.Size() < (1 << 8))
517 ChecksumTypes.AddToUniqueSorted(file.Checksum.Type);
518 }
519 }
520 AString s;
521 m.AddToString(s);
522
523 FOR_VECTOR (i, ChecksumTypes)
524 {
525 const UInt32 type = ChecksumTypes[i];
526 switch (type)
527 {
528 case kCheckSumType_CRC:
529 s.Add_OptSpaced("CRC");
530 break;
531 default:
532 s.Add_OptSpaced("Checksum");
533 s.Add_UInt32(type);
534 }
535 }
536
537 if (!s.IsEmpty())
538 prop = s;
539 break;
540 }
541 case kpidNumBlocks:
542 {
543 UInt64 numBlocks = 0;
544 FOR_VECTOR (i, _files)
545 numBlocks += _files[i].Blocks.Size();
546 prop = numBlocks;
547 break;
548 }
549 case kpidClusterSize:
550 {
551 UInt64 blockSize_MAX = 0;
552 FOR_VECTOR (i, _files)
553 {
554 const UInt64 a = _files[i].BlockSize_MAX;
555 if (blockSize_MAX < a)
556 blockSize_MAX = a;
557 }
558 prop = blockSize_MAX;
559 break;
560 }
561 case kpidMainSubfile:
562 {
563 int mainIndex = -1;
564 FOR_VECTOR (i, _files)
565 {
566 if (Is_Apple_FS_Or_Unknown(_files[i].Name))
567 {
568 if (mainIndex != -1)
569 {
570 mainIndex = -1;
571 break;
572 }
573 mainIndex = (int)i;
574 }
575 }
576 if (mainIndex != -1)
577 prop = (UInt32)(Int32)mainIndex;
578 break;
579 }
580 case kpidWarning:
581 if (_masterCrcError)
582 prop = "Master CRC error";
583 break;
584
585 case kpidWarningFlags:
586 {
587 UInt32 v = 0;
588 if (_headersError) v |= kpv_ErrorFlags_HeadersError;
589 if (_dataForkError) v |= kpv_ErrorFlags_CrcError;
590 if (v != 0)
591 prop = v;
592 break;
593 }
594
595 case kpidOffset: prop = _startPos; break;
596 case kpidPhySize: prop = _phySize; break;
597
598 case kpidComment:
599 {
600 AString s;
601 if (!_name.IsEmpty())
602 AddToComment_Prop(s, "Name", _name);
603 AddToComment_UInt64(s, _numSectors << 9, "unpack-size");
604 {
605 char temp[sizeof(_segmentGUID) * 2 + 2];
606 ConvertDataToHex_Lower(temp, _segmentGUID, sizeof(_segmentGUID));
607 AddToComment_Prop(s, "ID", temp);
608 }
609 _masterChecksum.AddToComment(s, "master-checksum");
610 _dataForkChecksum.AddToComment(s, "pack-checksum");
611 {
612 /*
613 if (_dataStartOffset != 0)
614 AddToComment_UInt64(s, _dataStartOffset, "payload-start-offset");
615 */
616 // if (_dataForkPair.Offset != 0)
617 _dataForkPair.Print(s, "pack");
618 rsrcPair.Print(s, "rsrc");
619 xmlPair.Print(s, "xml");
620 blobPair.Print(s, "blob");
621 }
622 if (_rsrcMode_wasUsed)
623 s += "RSRC_MODE\n";
624 if (!s.IsEmpty())
625 prop = s;
626 }
627 break;
628
629 case kpidName:
630 if (!_name.IsEmpty())
631 prop = _name + ".dmg";
632 break;
633 }
634 prop.Detach(value);
635 return S_OK;
636 COM_TRY_END
637 }
638
639 IMP_IInArchive_ArcProps
640
641
642
643 static const UInt64 kSectorNumber_LIMIT = (UInt64)1 << (63 - 9);
644
Parse(const Byte * p,UInt32 size)645 HRESULT CFile::Parse(const Byte *p, UInt32 size)
646 {
647 // CFile was initialized to default values: 0 in size variables and (IsCorrect == false)
648 const unsigned kHeadSize = 0xCC;
649 if (size < kHeadSize)
650 return S_FALSE;
651 if (Get32(p) != 0x6D697368) // "mish" signature
652 return S_FALSE;
653 if (Get32(p + 4) != 1) // version
654 return S_FALSE;
655
656 StartUnpackSector = Get64(p + 8);
657 NumUnpackSectors = Get64(p + 0x10);
658 StartPackPos = Get64(p + 0x18);
659
660 #ifdef SHOW_DEBUG_INFO
661 /* the number of sectors that must be allocated.
662 == 0x208 for 256KB clusters
663 == 0x808 for 1MB clusters
664 == 0x1001 for 1MB clusters in some example
665 */
666 const UInt32 decompressedBufRequested = Get32(p + 0x20);
667 #endif
668
669 // Descriptor is file index. usually started from -1
670 // in one dmg it was started from 0
671 Descriptor = (Int32)Get32(p + 0x24);
672 // char Reserved1[24];
673
674 Checksum.Parse(p + 0x40);
675 PRF(printf("\n" " Checksum Type = %2u"
676 "\n StartUnpackSector = %8x"
677 "\n NumUnpackSectors = %8x"
678 "\n StartPos = %8x"
679 "\n decompressedBufRequested=%8x"
680 "\n blocksDescriptor=%8x"
681 , (unsigned)Checksum.Type
682 , (unsigned)StartUnpackSector
683 , (unsigned)NumUnpackSectors
684 , (unsigned)StartPackPos
685 , (unsigned)decompressedBufRequested
686 , (unsigned)Descriptor
687 );)
688
689 const UInt32 numBlocks = Get32(p + 0xC8);
690 const unsigned kRecordSize = 40;
691 if ((UInt64)numBlocks * kRecordSize + kHeadSize != size)
692 return S_FALSE;
693
694 Blocks.ClearAndReserve(numBlocks);
695 FullFileChecksum = true;
696 /* IsCorrect = false; by default
697 So we return S_OK, if we can ignore some error in headers.
698 */
699
700 p += kHeadSize;
701 UInt32 i;
702
703 for (i = 0; i < numBlocks; i++, p += kRecordSize)
704 {
705 CBlock b;
706 b.Type = Get32(p);
707 {
708 const UInt64 a = Get64(p + 0x08);
709 if (a >= kSectorNumber_LIMIT)
710 return S_OK;
711 b.UnpPos = a << 9;
712 }
713 UInt64 unpSize;
714 {
715 const UInt64 a = Get64(p + 0x10);
716 if (a >= kSectorNumber_LIMIT)
717 return S_OK;
718 unpSize = a << 9;
719 }
720 const UInt64 newSize = b.UnpPos + unpSize;
721 if (newSize >= ((UInt64)1 << 63))
722 return S_OK;
723
724 b.PackPos = Get64(p + 0x18);
725 b.PackSize = Get64(p + 0x20);
726 // b.PackPos can be 0 for some types. So we don't check it
727 if (b.UnpPos != Size)
728 return S_OK;
729
730 PRF(printf("\nType=%8x comment=%8x uPos=%8x uSize=%7x pPos=%8x pSize=%7x",
731 (unsigned)b.Type, Get32(p + 4), (unsigned)b.UnpPos, (unsigned)unpSize, (unsigned)b.PackPos, (unsigned)b.PackSize));
732
733 if (b.Type == METHOD_COMMENT)
734 {
735 // some files contain 2 comment block records:
736 // record[0] : Type=METHOD_COMMENT, comment_field = "+beg"
737 // record[num-2] : Type=METHOD_COMMENT, comment_field = "+end"
738 // we skip these useless records.
739 continue;
740 }
741 if (b.Type == METHOD_END)
742 break;
743
744 // we add only blocks that have non empty unpacked data:
745 if (unpSize != 0)
746 {
747 const UInt64 k_max_pos = (UInt64)1 << 63;
748 if (b.PackPos >= k_max_pos ||
749 b.PackSize >= k_max_pos - b.PackPos)
750 return S_OK;
751
752 /* we don't count non-ZERO blocks here, because
753 ZERO blocks in dmg files are not limited by some cluster size.
754 note: COPY blocks also sometimes are fused to larger blocks.
755 */
756 if (b.IsClusteredMethod())
757 if (BlockSize_MAX < unpSize)
758 BlockSize_MAX = unpSize;
759
760 PackSize += b.PackSize;
761 if (!b.NeedCrc())
762 FullFileChecksum = false;
763 Blocks.AddInReserved(b);
764 Size = newSize;
765 }
766 }
767
768 PRF(printf("\n");)
769
770 if (i != numBlocks - 1)
771 {
772 // return S_FALSE;
773 return S_OK;
774 }
775
776 if ((Size >> 9) == NumUnpackSectors)
777 IsCorrect = true;
778 return S_OK;
779 }
780
781
FindKeyPair(const CXmlItem & item,const char * key,const char * nextTag)782 static const CXmlItem *FindKeyPair(const CXmlItem &item, const char *key, const char *nextTag)
783 {
784 for (unsigned i = 0; i + 1 < item.SubItems.Size(); i++)
785 {
786 const CXmlItem &si = item.SubItems[i];
787 if (si.IsTagged("key") && si.GetSubString() == key)
788 {
789 const CXmlItem *si_1 = &item.SubItems[i + 1];
790 if (si_1->IsTagged(nextTag))
791 return si_1;
792 }
793 }
794 return NULL;
795 }
796
GetStringFromKeyPair(const CXmlItem & item,const char * key,const char * nextTag)797 static const AString *GetStringFromKeyPair(const CXmlItem &item, const char *key, const char *nextTag)
798 {
799 const CXmlItem *si_1 = FindKeyPair(item, key, nextTag);
800 if (si_1)
801 return si_1->GetSubStringPtr();
802 return NULL;
803 }
804
805 static const Byte k_Signature[] = { 'k','o','l','y', 0, 0, 0, 4, 0, 0, 2, 0 };
806
IsKoly(const Byte * p)807 static inline bool IsKoly(const Byte *p)
808 {
809 return memcmp(p, k_Signature, Z7_ARRAY_SIZE(k_Signature)) == 0;
810 /*
811 if (Get32(p) != 0x6B6F6C79) // "koly" signature
812 return false;
813 if (Get32(p + 4) != 4) // version
814 return false;
815 if (Get32(p + 8) != HEADER_SIZE)
816 return false;
817 return true;
818 */
819 }
820
821
ReadData(IInStream * stream,const CForkPair & pair,CByteBuffer & buf)822 HRESULT CHandler::ReadData(IInStream *stream, const CForkPair &pair, CByteBuffer &buf)
823 {
824 size_t size = (size_t)pair.Len;
825 if (size != pair.Len)
826 return E_OUTOFMEMORY;
827 buf.Alloc(size);
828 RINOK(InStream_SeekSet(stream, _startPos + pair.Offset))
829 return ReadStream_FALSE(stream, buf, size);
830 }
831
832
833
ParseBlob(const CByteBuffer & data)834 bool CHandler::ParseBlob(const CByteBuffer &data)
835 {
836 const unsigned kHeaderSize = 3 * 4;
837 if (data.Size() < kHeaderSize)
838 return false;
839 const Byte * const p = (const Byte *)data;
840 if (Get32a(p) != 0xfade0cc0) // CSMAGIC_EMBEDDED_SIGNATURE
841 return true;
842 const UInt32 size = Get32a(p + 4);
843 if (size != data.Size())
844 return false;
845 const UInt32 num = Get32a(p + 8);
846 if (num > (size - kHeaderSize) / 8)
847 return false;
848
849 const UInt32 limit = num * 8 + kHeaderSize;
850 for (size_t i = kHeaderSize; i < limit; i += 8)
851 {
852 // type == 0 == CSSLOT_CODEDIRECTORY for CSMAGIC_CODEDIRECTORY item
853 // UInt32 type = Get32(p + i);
854 const UInt32 offset = Get32a(p + i + 4);
855 if (offset < limit || offset > size - 8)
856 return false;
857 // offset is not aligned for 4 here !!!
858 const Byte * const p2 = p + offset;
859 const UInt32 magic = Get32(p2);
860 const UInt32 len = Get32(p2 + 4);
861 if (size - offset < len || len < 8)
862 return false;
863
864 #ifdef DMG_SHOW_RAW
865 CExtraFile &extra = _extras.AddNew();
866 extra.Name = "_blob_";
867 extra.Data.CopyFrom(p2, len);
868 #endif
869
870 if (magic == 0xfade0c02) // CSMAGIC_CODEDIRECTORY
871 {
872 #ifdef DMG_SHOW_RAW
873 extra.Name += "codedir";
874 #endif
875
876 if (len < 11 * 4)
877 return false;
878 const UInt32 idOffset = Get32(p2 + 5 * 4);
879 if (idOffset >= len)
880 return false;
881 UInt32 len2 = len - idOffset;
882 const UInt32 kNameLenMax = 1 << 8;
883 if (len2 > kNameLenMax)
884 len2 = kNameLenMax;
885 _name.SetFrom_CalcLen((const char *)(p2 + idOffset), len2);
886 /*
887 // #define kSecCodeSignatureHashSHA1 1
888 // #define kSecCodeSignatureHashSHA256 2
889 const UInt32 hashOffset = Get32(p2 + 4 * 4);
890 const UInt32 nSpecialSlots = Get32(p2 + 6 * 4);
891 const UInt32 nCodeSlots = Get32(p2 + 7 * 4);
892 const unsigned hashSize = p2[36];
893 const unsigned hashType = p2[37];
894 // const unsigned unused = p2[38];
895 const unsigned pageSize = p2[39];
896 */
897 }
898 #ifdef DMG_SHOW_RAW
899 else if (magic == 0xfade0c01) extra.Name += "requirements";
900 else if (magic == 0xfade0b01) extra.Name += "signed";
901 else
902 {
903 char temp[16];
904 ConvertUInt32ToHex8Digits(magic, temp);
905 extra.Name += temp;
906 }
907 #endif
908 }
909
910 return true;
911 }
912
913
914
915
Open2(IInStream * stream,IArchiveOpenCallback * openArchiveCallback)916 HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openArchiveCallback)
917 {
918 /*
919 - usual dmg contains Koly Header at the end:
920 - rare case old dmg contains Koly Header at the begin.
921 */
922
923 // _dataStartOffset = 0;
924 UInt64 fileSize;
925 RINOK(InStream_GetPos_GetSize(stream, _startPos, fileSize))
926
927 const unsigned HEADER_SIZE = 0x200;
928 UInt64 buf[HEADER_SIZE / sizeof(UInt64)];
929 RINOK(ReadStream_FALSE(stream, buf, HEADER_SIZE))
930
931 UInt64 headerPos;
932 bool front_Koly_Mode = false;
933
934 /*
935 _dataForkChecksum.Offset == 0 for koly-at-the-end
936 _dataForkChecksum.Offset == 512 for koly-at-the-start
937 so we can use (_dataForkChecksum.Offset) to detect "koly-at-the-start" mode
938 */
939
940 if (IsKoly((const Byte *)(const void *)buf))
941 {
942 // it can be normal koly-at-the-end or koly-at-the-start
943 headerPos = _startPos;
944 /*
945 if (_startPos <= (1 << 8))
946 {
947 // we want to support front_Koly_Mode, even if there is
948 // some data before dmg file, like 128 bytes MacBin header
949 _dataStartOffset = HEADER_SIZE;
950 front_Koly_Mode = true;
951 }
952 */
953 }
954 else
955 {
956 /* we try to open in backward mode only for first attempt
957 when (_startPos == 0) */
958 if (_startPos != 0)
959 return S_FALSE;
960 headerPos = fileSize;
961 if (headerPos < HEADER_SIZE)
962 return S_FALSE;
963 headerPos -= HEADER_SIZE;
964 RINOK(InStream_SeekSet(stream, headerPos))
965 RINOK(ReadStream_FALSE(stream, buf, HEADER_SIZE))
966 if (!IsKoly((const Byte *)(const void *)buf))
967 return S_FALSE;
968 }
969
970 // UInt32 flags = Get32a((const Byte *)(const void *)buf + 12);
971 // _runningDataForkOffset = Get64a((const Byte *)(const void *)buf + 0x10);
972 _dataForkPair.Parse((const Byte *)(const void *)buf + 0x18);
973 rsrcPair.Parse((const Byte *)(const void *)buf + 0x28);
974 // _segmentNumber = Get32a(buf + 0x38); // 0 or 1
975 // _segmentCount = Get32a(buf + 0x3C); // 0 (if not set) or 1
976 memcpy(_segmentGUID, (const Byte *)(const void *)buf + 0x40, 16);
977 _dataForkChecksum.Parse((const Byte *)(const void *)buf + 0x50);
978 xmlPair.Parse((const Byte *)(const void *)buf + 0xD8);
979 // Byte resereved[]
980 blobPair.Parse((const Byte *)(const void *)buf + 0x128);
981 _masterChecksum.Parse((const Byte *)(const void *)buf + 0x160);
982 // UInt32 imageVariant = Get32a((const Byte *)(const void *)buf + 0x1E8); imageVariant = imageVariant;
983 _numSectors = Get64((const Byte *)(const void *)buf + 0x1EC); // it's not aligned for 8-bytes
984 // Byte resereved[12];
985
986 if (_dataForkPair.Offset == HEADER_SIZE
987 && headerPos + HEADER_SIZE < fileSize)
988 front_Koly_Mode = true;
989
990 const UInt64 limit = front_Koly_Mode ? fileSize : headerPos;
991 UInt64 top = 0;
992 if (!_dataForkPair.UpdateTop(limit, top)) return S_FALSE;
993 if (!xmlPair.UpdateTop(limit, top)) return S_FALSE;
994 if (!rsrcPair.UpdateTop(limit, top)) return S_FALSE;
995
996 /* Some old dmg files contain garbage data in blobPair field.
997 So we need to ignore such garbage case;
998 And we still need to detect offset of start of archive for "parser" mode. */
999 const bool useBlob = blobPair.UpdateTop(limit, top);
1000
1001 if (front_Koly_Mode)
1002 _phySize = top;
1003 else
1004 {
1005 _phySize = headerPos + HEADER_SIZE;
1006 _startPos = 0;
1007 if (top != headerPos)
1008 {
1009 /*
1010 if expected absolute offset is not equal to real header offset,
1011 2 cases are possible:
1012 - additional (unknown) headers
1013 - archive with offset.
1014 So we try to read XML with absolute offset to select from these two ways.
1015 */
1016 CForkPair xmlPair2 = xmlPair;
1017 const char *sz = "<?xml version";
1018 const unsigned len = (unsigned)strlen(sz);
1019 if (xmlPair2.Len > len)
1020 xmlPair2.Len = len;
1021 CByteBuffer buf2;
1022 if (xmlPair2.Len < len
1023 || ReadData(stream, xmlPair2, buf2) != S_OK
1024 || memcmp(buf2, sz, len) != 0)
1025 {
1026 // if absolute offset is not OK, probably it's archive with offset
1027 _startPos = headerPos - top;
1028 _phySize = top + HEADER_SIZE;
1029 }
1030 }
1031 }
1032
1033 if (useBlob
1034 && blobPair.Len != 0
1035 && blobPair.Len <= (1u << 24)) // we don't want parsing of big blobs
1036 {
1037 #ifdef DMG_SHOW_RAW
1038 CExtraFile &extra = _extras.AddNew();
1039 extra.Name = "_blob.bin";
1040 CByteBuffer &blobBuf = extra.Data;
1041 #else
1042 CByteBuffer blobBuf;
1043 #endif
1044 RINOK(ReadData(stream, blobPair, blobBuf))
1045 if (!ParseBlob(blobBuf))
1046 _headersError = true;
1047 }
1048
1049
1050 UInt64 openTotal = 0;
1051 UInt64 openCur = 0;
1052 if (_dataForkChecksum.IsCrc32())
1053 openTotal = _dataForkPair.Len;
1054
1055 const UInt32 RSRC_HEAD_SIZE = 0x100;
1056
1057 /* we have 2 ways to read files and blocks metadata:
1058 via Xml or via Rsrc.
1059 But some images have no Rsrc fork.
1060 Is it possible that there is no Xml?
1061 Rsrc method will not work for big files. */
1062 // v23.02: we use xml mode by default
1063 // if (rsrcPair.Len >= RSRC_HEAD_SIZE && rsrcPair.Len <= ((UInt32)1 << 24)) // for debug
1064 if (xmlPair.Len == 0)
1065 {
1066 // We don't know the size of the field "offset" in Rsrc.
1067 // We suppose that it uses 24 bits. So we use Rsrc, only if the rsrcPair.Len <= (1 << 24).
1068 const bool canUseRsrc = (rsrcPair.Len >= RSRC_HEAD_SIZE && rsrcPair.Len <= ((UInt32)1 << 24));
1069 if (!canUseRsrc)
1070 return S_FALSE;
1071
1072 _rsrcMode_wasUsed = true;
1073 #ifdef DMG_SHOW_RAW
1074 CExtraFile &extra = _extras.AddNew();
1075 extra.Name = "rsrc.bin";
1076 CByteBuffer &rsrcBuf = extra.Data;
1077 #else
1078 CByteBuffer rsrcBuf;
1079 #endif
1080
1081 RINOK(ReadData(stream, rsrcPair, rsrcBuf))
1082
1083 const Byte *p = rsrcBuf;
1084 const UInt32 headSize = Get32a(p + 0);
1085 const UInt32 footerOffset = Get32a(p + 4);
1086 const UInt32 mainDataSize = Get32a(p + 8);
1087 const UInt32 footerSize = Get32a(p + 12);
1088 if (headSize != RSRC_HEAD_SIZE
1089 || footerOffset >= rsrcPair.Len
1090 || mainDataSize >= rsrcPair.Len
1091 || footerOffset < mainDataSize
1092 || footerOffset != headSize + mainDataSize)
1093 return S_FALSE;
1094
1095 {
1096 const UInt32 footerEnd = footerOffset + footerSize;
1097 if (footerEnd != rsrcPair.Len)
1098 {
1099 // there is rare case dmg example, where there are 4 additional bytes
1100 const UInt64 rem = rsrcPair.Len - footerOffset;
1101 if (rem < footerSize
1102 || rem - footerSize != 4
1103 || Get32(p + footerEnd) != 0)
1104 return S_FALSE;
1105 }
1106 }
1107
1108 if (footerSize < 0x1e)
1109 return S_FALSE;
1110 if (memcmp(p, p + footerOffset, 16) != 0)
1111 return S_FALSE;
1112
1113 p += footerOffset;
1114
1115 if ((UInt32)Get16(p + 0x18) != 0x1c)
1116 return S_FALSE;
1117 const UInt32 namesOffset = Get16(p + 0x1a);
1118 if (namesOffset > footerSize)
1119 return S_FALSE;
1120
1121 const UInt32 numItems = (UInt32)Get16(p + 0x1c) + 1;
1122 if (numItems * 8 + 0x1e > namesOffset)
1123 return S_FALSE;
1124
1125 for (UInt32 i = 0; i < numItems; i++)
1126 {
1127 const Byte *p2 = p + 0x1e + (size_t)i * 8;
1128 const UInt32 typeId = Get32(p2);
1129 const UInt32 k_typeId_blkx = 0x626c6b78; // blkx
1130 #ifndef DMG_SHOW_RAW
1131 if (typeId != k_typeId_blkx)
1132 continue;
1133 #endif
1134 const UInt32 numFiles = (UInt32)Get16(p2 + 4) + 1;
1135 const UInt32 offs = Get16(p2 + 6);
1136 if (0x1c + offs + 12 * numFiles > namesOffset)
1137 return S_FALSE;
1138
1139 for (UInt32 k = 0; k < numFiles; k++)
1140 {
1141 const Byte *p3 = p + 0x1c + offs + k * 12;
1142 // UInt32 id = Get16(p3);
1143 const UInt32 namePos = Get16(p3 + 2);
1144 // Byte attributes = p3[4]; // = 0x50 for blkx #define (ATTRIBUTE_HDIUTIL)
1145 // we don't know how many bits we can use. So we use 24 bits only
1146 UInt32 blockOffset = Get32(p3 + 4);
1147 blockOffset &= ((UInt32)1 << 24) - 1;
1148 // UInt32 unknown2 = Get32(p3 + 8); // ???
1149 if (blockOffset + 4 >= mainDataSize)
1150 return S_FALSE;
1151 const Byte *pBlock = rsrcBuf + headSize + blockOffset;
1152 const UInt32 blockSize = Get32(pBlock);
1153 if (mainDataSize - (blockOffset + 4) < blockSize)
1154 return S_FALSE;
1155
1156 AString name;
1157
1158 if (namePos != 0xffff)
1159 {
1160 const UInt32 namesBlockSize = footerSize - namesOffset;
1161 if (namePos >= namesBlockSize)
1162 return S_FALSE;
1163 const Byte *namePtr = p + namesOffset + namePos;
1164 const UInt32 nameLen = *namePtr;
1165 if (namesBlockSize - namePos <= nameLen)
1166 return S_FALSE;
1167 for (UInt32 r = 1; r <= nameLen; r++)
1168 {
1169 const Byte c = namePtr[r];
1170 if (c < 0x20 || c >= 0x80)
1171 break;
1172 name += (char)c;
1173 }
1174 }
1175
1176 if (typeId == k_typeId_blkx)
1177 {
1178 CFile &file = _files.AddNew();
1179 file.Name = name;
1180 RINOK(file.Parse(pBlock + 4, blockSize))
1181 if (!file.IsCorrect)
1182 _headersError = true;
1183 }
1184
1185 #ifdef DMG_SHOW_RAW
1186 {
1187 AString name2;
1188 name2.Add_UInt32(i);
1189 name2 += '_';
1190 {
1191 char temp[4 + 1] = { 0 };
1192 memcpy(temp, p2, 4);
1193 name2 += temp;
1194 }
1195 name2.Trim();
1196 name2 += '_';
1197 name2.Add_UInt32(k);
1198 if (!name.IsEmpty())
1199 {
1200 name2 += '_';
1201 name2 += name;
1202 }
1203 CExtraFile &extra2 = _extras.AddNew();
1204 extra2.Name = name2;
1205 extra2.Data.CopyFrom(pBlock + 4, blockSize);
1206 }
1207 #endif
1208 }
1209 }
1210 }
1211 else
1212 {
1213 if (xmlPair.Len > k_XmlSize_MAX)
1214 return S_FALSE;
1215 // if (!canUseXml) return S_FALSE;
1216 const size_t size = (size_t)xmlPair.Len;
1217 // if (size + 1 <= xmlPair.Len) return S_FALSE; // optional check
1218 RINOK(InStream_SeekSet(stream, _startPos + xmlPair.Offset))
1219 CXml xml;
1220 {
1221 openTotal += size;
1222 if (openArchiveCallback)
1223 {
1224 RINOK(openArchiveCallback->SetTotal(NULL, &openTotal))
1225 }
1226 size_t pos = 0;
1227 CAlignedBuffer1 xmlStr(size + 1);
1228 for (;;)
1229 {
1230 const size_t k_OpenStep = 1 << 24;
1231 const size_t cur = MyMin(k_OpenStep, size - pos);
1232 RINOK(ReadStream_FALSE(stream, xmlStr + pos, cur))
1233 pos += cur;
1234 openCur += cur;
1235 if (pos == size)
1236 break;
1237 if (openArchiveCallback)
1238 {
1239 RINOK(openArchiveCallback->SetCompleted(NULL, &openCur))
1240 }
1241 }
1242 xmlStr[size] = 0;
1243 // if (strlen((const char *)(const void *)(const Byte *)xmlStr) != size) return S_FALSE;
1244 if (!xml.Parse((char *)(void *)(Byte *)xmlStr))
1245 return S_FALSE;
1246
1247 #ifdef DMG_SHOW_RAW
1248 CExtraFile &extra = _extras.AddNew();
1249 extra.Name = "a.xml";
1250 extra.Data.CopyFrom(xmlStr, size);
1251 #endif
1252 }
1253
1254 if (xml.Root.Name != "plist")
1255 return S_FALSE;
1256
1257 const CXmlItem *dictItem = xml.Root.FindSubTag_GetPtr("dict");
1258 if (!dictItem)
1259 return S_FALSE;
1260
1261 const CXmlItem *rfDictItem = FindKeyPair(*dictItem, "resource-fork", "dict");
1262 if (!rfDictItem)
1263 return S_FALSE;
1264
1265 const CXmlItem *arrItem = FindKeyPair(*rfDictItem, "blkx", "array");
1266 if (!arrItem)
1267 return S_FALSE;
1268
1269 FOR_VECTOR (i, arrItem->SubItems)
1270 {
1271 const CXmlItem &item = arrItem->SubItems[i];
1272 if (!item.IsTagged("dict"))
1273 continue;
1274
1275 CByteBuffer rawBuf;
1276 unsigned destLen = 0;
1277 {
1278 const AString *dataString = GetStringFromKeyPair(item, "Data", "data");
1279 if (!dataString)
1280 return S_FALSE;
1281 destLen = dataString->Len() / 4 * 3 + 4;
1282 rawBuf.Alloc(destLen);
1283 {
1284 const Byte *endPtr = Base64ToBin(rawBuf, *dataString);
1285 if (!endPtr)
1286 return S_FALSE;
1287 destLen = (unsigned)(endPtr - (const Byte *)rawBuf);
1288 }
1289
1290 #ifdef DMG_SHOW_RAW
1291 CExtraFile &extra = _extras.AddNew();
1292 extra.Name.Add_UInt32(_files.Size());
1293 extra.Data.CopyFrom(rawBuf, destLen);
1294 #endif
1295 }
1296 CFile &file = _files.AddNew();
1297 {
1298 /* xml code removes front space for such string:
1299 <string> (Apple_Free : 3)</string>
1300 maybe we shoud fix xml code and return full string with space.
1301 */
1302 const AString *name = GetStringFromKeyPair(item, "Name", "string");
1303 if (!name || name->IsEmpty())
1304 name = GetStringFromKeyPair(item, "CFName", "string");
1305 if (name)
1306 file.Name = *name;
1307 }
1308 /*
1309 {
1310 const AString *s = GetStringFromKeyPair(item, "ID", "string");
1311 if (s)
1312 file.Id = *s;
1313 }
1314 */
1315 RINOK(file.Parse(rawBuf, destLen))
1316 if (!file.IsCorrect)
1317 _headersError = true;
1318 }
1319 }
1320
1321 if (_masterChecksum.IsCrc32())
1322 {
1323 UInt32 crc = CRC_INIT_VAL;
1324 unsigned i;
1325 for (i = 0; i < _files.Size(); i++)
1326 {
1327 const CChecksum &cs = _files[i].Checksum;
1328 if ((cs.NumBits & 0x7) != 0)
1329 break;
1330 const UInt32 len = cs.NumBits >> 3;
1331 if (len > kChecksumSize_Max)
1332 break;
1333 crc = CrcUpdate(crc, cs.Data, (size_t)len);
1334 }
1335 if (i == _files.Size())
1336 _masterCrcError = (CRC_GET_DIGEST(crc) != _masterChecksum.GetCrc32());
1337 }
1338
1339 {
1340 UInt64 sec = 0;
1341 FOR_VECTOR (i, _files)
1342 {
1343 const CFile &file = _files[i];
1344 /*
1345 if (file.Descriptor != (Int32)i - 1)
1346 _headersError = true;
1347 */
1348 if (file.StartUnpackSector != sec)
1349 _headersError = true;
1350 if (file.NumUnpackSectors >= kSectorNumber_LIMIT)
1351 _headersError = true;
1352 sec += file.NumUnpackSectors;
1353 if (sec >= kSectorNumber_LIMIT)
1354 _headersError = true;
1355 }
1356 if (sec != _numSectors)
1357 _headersError = true;
1358 }
1359
1360 // data checksum calculation can be slow for big dmg file
1361 if (_dataForkChecksum.IsCrc32())
1362 {
1363 UInt64 endPos;
1364 if (!_dataForkPair.GetEndPos(endPos)
1365 || _dataForkPair.Offset >= ((UInt64)1 << 63))
1366 _headersError = true;
1367 else
1368 {
1369 const UInt64 seekPos = _startPos + _dataForkPair.Offset;
1370 if (seekPos > fileSize
1371 || endPos > fileSize - _startPos)
1372 {
1373 _headersError = true;
1374 // kpv_ErrorFlags_UnexpectedEnd
1375 }
1376 else
1377 {
1378 const size_t kBufSize = 1 << 15;
1379 CAlignedBuffer1 buf2(kBufSize);
1380 RINOK(InStream_SeekSet(stream, seekPos))
1381 if (openArchiveCallback)
1382 {
1383 RINOK(openArchiveCallback->SetTotal(NULL, &openTotal))
1384 }
1385 UInt32 crc = CRC_INIT_VAL;
1386 UInt64 pos = 0;
1387 for (;;)
1388 {
1389 const UInt64 rem = _dataForkPair.Len - pos;
1390 size_t cur = kBufSize;
1391 if (cur > rem)
1392 cur = (UInt32)rem;
1393 if (cur == 0)
1394 break;
1395 RINOK(ReadStream_FALSE(stream, buf2, cur))
1396 crc = CrcUpdate(crc, buf2, cur);
1397 pos += cur;
1398 openCur += cur;
1399 if ((pos & ((1 << 24) - 1)) == 0 && openArchiveCallback)
1400 {
1401 RINOK(openArchiveCallback->SetCompleted(NULL, &openCur))
1402 }
1403 }
1404 if (_dataForkChecksum.GetCrc32() != CRC_GET_DIGEST(crc))
1405 _dataForkError = true;
1406 }
1407 }
1408 }
1409
1410 return S_OK;
1411 }
1412
1413
1414
Z7_COM7F_IMF(CHandler::Open (IInStream * stream,const UInt64 *,IArchiveOpenCallback * openArchiveCallback))1415 Z7_COM7F_IMF(CHandler::Open(IInStream *stream,
1416 const UInt64 * /* maxCheckStartPosition */,
1417 IArchiveOpenCallback *openArchiveCallback))
1418 {
1419 COM_TRY_BEGIN
1420 Close();
1421 RINOK(Open2(stream, openArchiveCallback))
1422 _inStream = stream;
1423 return S_OK;
1424 COM_TRY_END
1425 }
1426
Z7_COM7F_IMF(CHandler::Close ())1427 Z7_COM7F_IMF(CHandler::Close())
1428 {
1429 _masterCrcError = false;
1430 _headersError = false;
1431 _dataForkError = false;
1432 _rsrcMode_wasUsed = false;
1433 _phySize = 0;
1434 _startPos = 0;
1435 _name.Empty();
1436 _inStream.Release();
1437 _files.Clear();
1438 #ifdef DMG_SHOW_RAW
1439 _extras.Clear();
1440 #endif
1441 return S_OK;
1442 }
1443
Z7_COM7F_IMF(CHandler::GetNumberOfItems (UInt32 * numItems))1444 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
1445 {
1446 *numItems = _files.Size()
1447 #ifdef DMG_SHOW_RAW
1448 + _extras.Size()
1449 #endif
1450 ;
1451 return S_OK;
1452 }
1453
1454 #ifdef DMG_SHOW_RAW
1455 #define RAW_PREFIX "raw" STRING_PATH_SEPARATOR
1456 #endif
1457
Z7_COM7F_IMF(CHandler::GetProperty (UInt32 index,PROPID propID,PROPVARIANT * value))1458 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
1459 {
1460 COM_TRY_BEGIN
1461 NWindows::NCOM::CPropVariant prop;
1462
1463 #ifdef DMG_SHOW_RAW
1464 if (index >= _files.Size())
1465 {
1466 const CExtraFile &extra = _extras[index - _files.Size()];
1467 switch (propID)
1468 {
1469 case kpidPath:
1470 prop = (AString)RAW_PREFIX + extra.Name;
1471 break;
1472 case kpidSize:
1473 case kpidPackSize:
1474 prop = (UInt64)extra.Data.Size();
1475 break;
1476 }
1477 }
1478 else
1479 #endif
1480 {
1481 const CFile &item = _files[index];
1482 switch (propID)
1483 {
1484 case kpidSize: prop = item.Size; break;
1485 case kpidPackSize: prop = item.PackSize; break;
1486 case kpidCRC:
1487 {
1488 if (item.Checksum.IsCrc32() && item.FullFileChecksum)
1489 prop = item.Checksum.GetCrc32();
1490 break;
1491 }
1492 case kpidChecksum:
1493 {
1494 AString s;
1495 item.Checksum.Print(s);
1496 if (!s.IsEmpty())
1497 prop = s;
1498 break;
1499 }
1500
1501 /*
1502 case kpidOffset:
1503 {
1504 prop = item.StartPackPos;
1505 break;
1506 }
1507 */
1508
1509 case kpidNumBlocks:
1510 prop = (UInt32)item.Blocks.Size();
1511 break;
1512 case kpidClusterSize:
1513 prop = item.BlockSize_MAX;
1514 break;
1515
1516 case kpidMethod:
1517 {
1518 AString s;
1519 if (!item.IsCorrect)
1520 s.Add_OptSpaced("CORRUPTED");
1521 CMethods m;
1522 m.Update(item);
1523 m.AddToString(s);
1524 {
1525 AString s2;
1526 item.Checksum.PrintType(s2);
1527 if (!s2.IsEmpty())
1528 s.Add_OptSpaced(s2);
1529 }
1530 if (!s.IsEmpty())
1531 prop = s;
1532 break;
1533 }
1534
1535 case kpidPath:
1536 {
1537 #ifdef Z7_DMG_SINGLE_FILE_MODE
1538 prop = "a.img";
1539 #else
1540 UString name;
1541 name.Add_UInt32(index);
1542 unsigned num = 10;
1543 unsigned numDigits;
1544 for (numDigits = 1; num < _files.Size(); numDigits++)
1545 num *= 10;
1546 while (name.Len() < numDigits)
1547 name.InsertAtFront(L'0');
1548
1549 AString subName;
1550 int pos1 = item.Name.Find('(');
1551 if (pos1 >= 0)
1552 {
1553 pos1++;
1554 const int pos2 = item.Name.Find(')', pos1);
1555 if (pos2 >= 0)
1556 {
1557 subName.SetFrom(item.Name.Ptr(pos1), pos2 - pos1);
1558 pos1 = subName.Find(':');
1559 if (pos1 >= 0)
1560 subName.DeleteFrom(pos1);
1561 }
1562 }
1563 else
1564 subName = item.Name; // new apfs dmg can be without braces
1565 subName.Trim();
1566 if (!subName.IsEmpty())
1567 {
1568 const char *ext = Find_Apple_FS_Ext(subName);
1569 if (ext)
1570 subName = ext;
1571 UString name2;
1572 ConvertUTF8ToUnicode(subName, name2);
1573 name.Add_Dot();
1574 name += name2;
1575 }
1576 else
1577 {
1578 UString name2;
1579 ConvertUTF8ToUnicode(item.Name, name2);
1580 if (!name2.IsEmpty())
1581 name += "_";
1582 name += name2;
1583 }
1584 prop = name;
1585 #endif
1586
1587 break;
1588 }
1589
1590 case kpidComment:
1591 {
1592 UString name;
1593 ConvertUTF8ToUnicode(item.Name, name);
1594 prop = name;
1595 break;
1596 }
1597 case kpidId:
1598 {
1599 prop.Set_Int32((Int32)item.Descriptor);
1600 /*
1601 if (!item.Id.IsEmpty())
1602 {
1603 UString s;
1604 ConvertUTF8ToUnicode(item.Id, s);
1605 prop = s;
1606 }
1607 */
1608 break;
1609 }
1610 #ifdef Z7_DMG_SINGLE_FILE_MODE
1611 case kpidPosition:
1612 prop = item.StartUnpackSector << 9;
1613 break;
1614 #endif
1615 }
1616 }
1617 prop.Detach(value);
1618 return S_OK;
1619 COM_TRY_END
1620 }
1621
1622
1623 class CAdcDecoder
1624 {
1625 CLzOutWindow m_OutWindowStream;
1626 CInBuffer m_InStream;
1627
1628 class CCoderReleaser Z7_final
1629 {
1630 CAdcDecoder *m_Coder;
1631 public:
1632 bool NeedFlush;
CCoderReleaser(CAdcDecoder * coder)1633 CCoderReleaser(CAdcDecoder *coder): m_Coder(coder), NeedFlush(true) {}
~CCoderReleaser()1634 ~CCoderReleaser()
1635 {
1636 if (NeedFlush)
1637 m_Coder->m_OutWindowStream.Flush();
1638 }
1639 };
1640 friend class CCoderReleaser;
1641
1642 public:
1643 HRESULT Code(ISequentialInStream * const inStream,
1644 ISequentialOutStream *outStream,
1645 const UInt64 * const inSize,
1646 const UInt64 * const outSize,
1647 ICompressProgressInfo * const progress);
1648 };
1649
1650
Code(ISequentialInStream * const inStream,ISequentialOutStream * outStream,const UInt64 * const inSize,const UInt64 * const outSizePtr,ICompressProgressInfo * const progress)1651 HRESULT CAdcDecoder::Code(ISequentialInStream * const inStream,
1652 ISequentialOutStream *outStream,
1653 const UInt64 * const inSize,
1654 const UInt64 * const outSizePtr,
1655 ICompressProgressInfo * const progress)
1656 {
1657 try {
1658
1659 if (!m_OutWindowStream.Create(1 << 18)) // at least (1 << 16) is required here
1660 return E_OUTOFMEMORY;
1661 if (!m_InStream.Create(1 << 18))
1662 return E_OUTOFMEMORY;
1663
1664 m_OutWindowStream.SetStream(outStream);
1665 m_OutWindowStream.Init(false);
1666 m_InStream.SetStream(inStream);
1667 m_InStream.Init();
1668
1669 CCoderReleaser coderReleaser(this);
1670
1671 const UInt32 kStep = 1 << 22;
1672 UInt64 nextLimit = kStep;
1673 const UInt64 outSize = *outSizePtr;
1674 UInt64 pos = 0;
1675 /* match sequences and literal sequences do not cross 64KB range
1676 in some dmg archive examples. But is it so for any Adc stream? */
1677
1678 while (pos < outSize)
1679 {
1680 if (pos >= nextLimit && progress)
1681 {
1682 nextLimit += kStep;
1683 const UInt64 packSize = m_InStream.GetProcessedSize();
1684 RINOK(progress->SetRatioInfo(&packSize, &pos))
1685 }
1686 Byte b;
1687 if (!m_InStream.ReadByte(b))
1688 return S_FALSE;
1689 const UInt64 rem = outSize - pos;
1690 if (b & 0x80)
1691 {
1692 unsigned num = (unsigned)b - 0x80 + 1;
1693 if (num > rem)
1694 return S_FALSE;
1695 pos += num;
1696 do
1697 {
1698 if (!m_InStream.ReadByte(b))
1699 return S_FALSE;
1700 m_OutWindowStream.PutByte(b);
1701 }
1702 while (--num);
1703 continue;
1704 }
1705 Byte b1;
1706 if (!m_InStream.ReadByte(b1))
1707 return S_FALSE;
1708
1709 UInt32 len, dist;
1710
1711 if (b & 0x40)
1712 {
1713 len = (UInt32)b - 0x40 + 4;
1714 Byte b2;
1715 if (!m_InStream.ReadByte(b2))
1716 return S_FALSE;
1717 dist = ((UInt32)b1 << 8) + b2;
1718 }
1719 else
1720 {
1721 len = ((UInt32)b >> 2) + 3;
1722 dist = (((UInt32)b & 3) << 8) + b1;
1723 }
1724
1725 if (/* dist >= pos || */ len > rem)
1726 return S_FALSE;
1727 if (!m_OutWindowStream.CopyBlock(dist, len))
1728 return S_FALSE;
1729 pos += len;
1730 }
1731 if (*inSize != m_InStream.GetProcessedSize())
1732 return S_FALSE;
1733 coderReleaser.NeedFlush = false;
1734 return m_OutWindowStream.Flush();
1735
1736 }
1737 catch(const CInBufferException &e) { return e.ErrorCode; }
1738 catch(const CLzOutWindowException &e) { return e.ErrorCode; }
1739 catch(...) { return S_FALSE; }
1740 }
1741
1742
1743
1744 struct CDecoders
1745 {
1746 CMyComPtr2<ICompressCoder, NCompress::NZlib::CDecoder> zlib;
1747 CMyComPtr2<ICompressCoder, NCompress::NBZip2::CDecoder> bzip2;
1748 CMyComPtr2<ICompressCoder, NCompress::NLzfse::CDecoder> lzfse;
1749 CMyUniquePtr<NCompress::NXz::CDecoder> xz;
1750 CMyUniquePtr<CAdcDecoder> adc;
1751
1752 HRESULT Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
1753 const CBlock &block, const UInt64 *unpSize, ICompressProgressInfo *progress);
1754 };
1755
Code(ISequentialInStream * inStream,ISequentialOutStream * outStream,const CBlock & block,const UInt64 * unpSize,ICompressProgressInfo * progress)1756 HRESULT CDecoders::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
1757 const CBlock &block, const UInt64 *unpSize, ICompressProgressInfo *progress)
1758 {
1759 HRESULT hres;
1760 UInt64 processed;
1761 switch (block.Type)
1762 {
1763 case METHOD_ADC:
1764 adc.Create_if_Empty();
1765 return adc->Code(inStream, outStream, &block.PackSize, unpSize, progress);
1766 case METHOD_LZFSE:
1767 lzfse.Create_if_Empty();
1768 return lzfse.Interface()->Code(inStream, outStream, &block.PackSize, unpSize, progress);
1769 case METHOD_ZLIB:
1770 zlib.Create_if_Empty();
1771 hres = zlib.Interface()->Code(inStream, outStream, NULL, unpSize, progress);
1772 processed = zlib->GetInputProcessedSize();
1773 break;
1774 case METHOD_BZIP2:
1775 bzip2.Create_if_Empty();
1776 hres = bzip2.Interface()->Code(inStream, outStream, NULL, unpSize, progress);
1777 processed = bzip2->GetInputProcessedSize();
1778 break;
1779 case METHOD_XZ:
1780 xz.Create_if_Empty();
1781 hres = xz->Decode(inStream, outStream, unpSize, true, progress);
1782 processed = xz->Stat.InSize;
1783 break;
1784 default:
1785 return E_NOTIMPL;
1786 }
1787 if (hres == S_OK && processed != block.PackSize)
1788 hres = S_FALSE;
1789 return hres;
1790 }
1791
1792
Z7_COM7F_IMF(CHandler::Extract (const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback))1793 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1794 Int32 testMode, IArchiveExtractCallback *extractCallback))
1795 {
1796 COM_TRY_BEGIN
1797 const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
1798 if (allFilesMode)
1799 numItems = _files.Size()
1800 #ifdef DMG_SHOW_RAW
1801 + _extras.Size()
1802 #endif
1803 ;
1804 if (numItems == 0)
1805 return S_OK;
1806 UInt64 totalSize = 0;
1807 UInt32 i;
1808
1809 for (i = 0; i < numItems; i++)
1810 {
1811 const UInt32 index = allFilesMode ? i : indices[i];
1812 #ifdef DMG_SHOW_RAW
1813 if (index >= _files.Size())
1814 totalSize += _extras[index - _files.Size()].Data.Size();
1815 else
1816 #endif
1817 totalSize += _files[index].Size;
1818 }
1819 RINOK(extractCallback->SetTotal(totalSize))
1820
1821 const size_t kZeroBufSize = 1 << 14;
1822 CAlignedBuffer1 zeroBuf(kZeroBufSize);
1823 memset(zeroBuf, 0, kZeroBufSize);
1824
1825 CDecoders decoders;
1826 CMyComPtr2_Create<ICompressCoder, NCompress::CCopyCoder> copyCoder;
1827 CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
1828 lps->Init(extractCallback, false);
1829 CMyComPtr2_Create<ISequentialInStream, CLimitedSequentialInStream> inStream;
1830 inStream->SetStream(_inStream);
1831
1832 UInt64 total_PackSize = 0;
1833 UInt64 total_UnpackSize = 0;
1834 UInt64 cur_PackSize = 0;
1835 UInt64 cur_UnpackSize = 0;
1836
1837 for (i = 0;; i++,
1838 total_PackSize += cur_PackSize,
1839 total_UnpackSize += cur_UnpackSize)
1840 {
1841 lps->InSize = total_PackSize;
1842 lps->OutSize = total_UnpackSize;
1843 cur_PackSize = 0;
1844 cur_UnpackSize = 0;
1845 RINOK(lps->SetCur())
1846 if (i >= numItems)
1847 return S_OK;
1848
1849 Int32 opRes = NExtract::NOperationResult::kOK;
1850 {
1851 CMyComPtr<ISequentialOutStream> realOutStream;
1852 const Int32 askMode = testMode ?
1853 NExtract::NAskMode::kTest :
1854 NExtract::NAskMode::kExtract;
1855 const UInt32 index = allFilesMode ? i : indices[i];
1856 RINOK(extractCallback->GetStream(index, &realOutStream, askMode))
1857
1858 if (!testMode && !realOutStream)
1859 continue;
1860 RINOK(extractCallback->PrepareOperation(askMode))
1861
1862 #ifdef DMG_SHOW_RAW
1863 if (index >= _files.Size())
1864 {
1865 const CByteBuffer &buf = _extras[index - _files.Size()].Data;
1866 if (realOutStream)
1867 RINOK(WriteStream(realOutStream, buf, buf.Size()))
1868 cur_PackSize = cur_UnpackSize = buf.Size();
1869 }
1870 else
1871 #endif
1872 {
1873 const CFile &item = _files[index];
1874 cur_PackSize = item.PackSize;
1875 cur_UnpackSize = item.Size;
1876
1877 if (!item.IsCorrect)
1878 opRes = NExtract::NOperationResult::kHeadersError;
1879 else
1880 {
1881 CMyComPtr2_Create<ISequentialOutStream, COutStreamWithCRC> outCrcStream;
1882 outCrcStream->SetStream(realOutStream);
1883 // realOutStream.Release();
1884 const bool needCrc = item.Checksum.IsCrc32();
1885 outCrcStream->Init(needCrc);
1886
1887 CMyComPtr2_Create<ISequentialOutStream, CLimitedSequentialOutStream> outStream;
1888 outStream->SetStream(outCrcStream);
1889
1890 UInt64 unpPos = 0;
1891 UInt64 packPos = 0;
1892
1893 FOR_VECTOR (blockIndex, item.Blocks)
1894 {
1895 lps->InSize = total_PackSize + packPos;
1896 lps->OutSize = total_UnpackSize + unpPos;
1897 RINOK(lps->SetCur())
1898
1899 const CBlock &block = item.Blocks[blockIndex];
1900 // if (!block.ThereAreDataInBlock()) continue;
1901
1902 packPos += block.PackSize;
1903 if (block.UnpPos != unpPos)
1904 {
1905 opRes = NExtract::NOperationResult::kHeadersError;
1906 break;
1907 }
1908
1909 RINOK(InStream_SeekSet(_inStream, _startPos + _dataForkPair.Offset + item.StartPackPos + block.PackPos))
1910 inStream->Init(block.PackSize);
1911
1912 const UInt64 unpSize = item.GetUnpackSize_of_Block(blockIndex);
1913
1914 outStream->Init(unpSize);
1915 HRESULT res = S_OK;
1916
1917 outCrcStream->EnableCalc(needCrc && block.NeedCrc());
1918
1919 if (block.IsZeroMethod())
1920 {
1921 if (block.PackSize != 0)
1922 opRes = NExtract::NOperationResult::kUnsupportedMethod;
1923 }
1924 else if (block.Type == METHOD_COPY)
1925 {
1926 if (unpSize != block.PackSize)
1927 opRes = NExtract::NOperationResult::kUnsupportedMethod;
1928 else
1929 res = copyCoder.Interface()->Code(inStream, outStream, NULL, NULL, lps);
1930 }
1931 else
1932 res = decoders.Code(inStream, outStream, block, &unpSize, lps);
1933
1934 if (res != S_OK)
1935 {
1936 if (res != S_FALSE)
1937 {
1938 if (res != E_NOTIMPL)
1939 return res;
1940 opRes = NExtract::NOperationResult::kUnsupportedMethod;
1941 }
1942 if (opRes == NExtract::NOperationResult::kOK)
1943 opRes = NExtract::NOperationResult::kDataError;
1944 }
1945
1946 unpPos += unpSize;
1947
1948 if (!outStream->IsFinishedOK())
1949 {
1950 if (!block.IsZeroMethod() && opRes == NExtract::NOperationResult::kOK)
1951 opRes = NExtract::NOperationResult::kDataError;
1952
1953 for (unsigned k = 0;;)
1954 {
1955 const UInt64 rem = outStream->GetRem();
1956 if (rem == 0)
1957 break;
1958 size_t size = kZeroBufSize;
1959 if (size > rem)
1960 size = (size_t)rem;
1961 RINOK(WriteStream(outStream, zeroBuf, size))
1962 k++;
1963 if ((k & 0xfff) == 0)
1964 {
1965 lps->OutSize = total_UnpackSize + unpPos - outStream->GetRem();
1966 RINOK(lps->SetCur())
1967 }
1968 }
1969 }
1970 }
1971 if (needCrc && opRes == NExtract::NOperationResult::kOK)
1972 {
1973 if (outCrcStream->GetCRC() != item.Checksum.GetCrc32())
1974 opRes = NExtract::NOperationResult::kCRCError;
1975 }
1976 }
1977 }
1978 }
1979 RINOK(extractCallback->SetOperationResult(opRes))
1980 }
1981
1982 COM_TRY_END
1983 }
1984
1985
1986
1987
1988 struct CChunk
1989 {
1990 int BlockIndex;
1991 UInt64 AccessMark;
1992 Byte *Buf;
1993 size_t BufSize;
1994
FreeNArchive::CChunk1995 void Free()
1996 {
1997 z7_AlignedFree(Buf);
1998 Buf = NULL;
1999 BufSize = 0;
2000 }
AllocNArchive::CChunk2001 void Alloc(size_t size)
2002 {
2003 Buf = (Byte *)z7_AlignedAlloc(size);
2004 }
2005 };
2006
2007
2008 Z7_CLASS_IMP_IInStream(
2009 CInStream
2010 )
2011 bool _errorMode;
2012 UInt64 _virtPos;
2013 int _latestChunk;
2014 int _latestBlock;
2015 UInt64 _accessMark;
2016 UInt64 _chunks_TotalSize;
2017 CRecordVector<CChunk> _chunks;
2018
2019 public:
2020 CMyComPtr<IInStream> Stream;
2021 const CFile *File;
2022 UInt64 Size;
2023 private:
2024 UInt64 _startPos;
2025
2026 ~CInStream();
2027 CMyComPtr2<ISequentialOutStream, CBufPtrSeqOutStream> outStream;
2028 CMyComPtr2<ISequentialInStream, CLimitedSequentialInStream> inStream;
2029 CDecoders decoders;
2030 public:
2031
2032 // HRESULT
2033 void Init(UInt64 startPos)
2034 {
2035 _errorMode = false;
2036 _startPos = startPos;
2037 _virtPos = 0;
2038 _latestChunk = -1;
2039 _latestBlock = -1;
2040 _accessMark = 0;
2041 _chunks_TotalSize = 0;
2042
2043 inStream.Create_if_Empty();
2044 inStream->SetStream(Stream);
2045
2046 outStream.Create_if_Empty();
2047 // return S_OK;
2048 }
2049 };
2050
2051
2052 CInStream::~CInStream()
2053 {
2054 unsigned i = _chunks.Size();
2055 while (i)
2056 _chunks[--i].Free();
2057 }
2058
2059 static unsigned FindBlock(const CRecordVector<CBlock> &blocks, UInt64 pos)
2060 {
2061 unsigned left = 0, right = blocks.Size();
2062 for (;;)
2063 {
2064 const unsigned mid = (left + right) / 2;
2065 if (mid == left)
2066 return left;
2067 if (pos < blocks[mid].UnpPos)
2068 right = mid;
2069 else
2070 left = mid;
2071 }
2072 }
2073
2074 Z7_COM7F_IMF(CInStream::Read(void *data, UInt32 size, UInt32 *processedSize))
2075 {
2076 // COM_TRY_BEGIN
2077 try {
2078
2079 if (_errorMode)
2080 return E_OUTOFMEMORY;
2081
2082 if (processedSize)
2083 *processedSize = 0;
2084 if (size == 0)
2085 return S_OK;
2086 if (_virtPos >= Size)
2087 return S_OK; // (Size == _virtPos) ? S_OK: E_FAIL;
2088 {
2089 const UInt64 rem = Size - _virtPos;
2090 if (size > rem)
2091 size = (UInt32)rem;
2092 }
2093
2094 if (_latestBlock >= 0)
2095 {
2096 const CBlock &block = File->Blocks[(unsigned)_latestBlock];
2097 if (_virtPos < block.UnpPos ||
2098 _virtPos - block.UnpPos >= File->GetUnpackSize_of_Block((unsigned)_latestBlock))
2099 _latestBlock = -1;
2100 }
2101
2102 if (_latestBlock < 0)
2103 {
2104 _latestChunk = -1;
2105 const unsigned blockIndex = FindBlock(File->Blocks, _virtPos);
2106 const CBlock &block = File->Blocks[blockIndex];
2107 const UInt64 unpSize = File->GetUnpackSize_of_Block(blockIndex);
2108
2109 if (block.NeedAllocateBuffer()
2110 && unpSize <= k_Chunk_Size_MAX)
2111 {
2112 unsigned i = 0;
2113 {
2114 unsigned numChunks = _chunks.Size();
2115 if (numChunks)
2116 {
2117 const CChunk *chunk = _chunks.ConstData();
2118 do
2119 {
2120 if (chunk->BlockIndex == (int)blockIndex)
2121 break;
2122 chunk++;
2123 }
2124 while (--numChunks);
2125 i = _chunks.Size() - numChunks;
2126 }
2127 }
2128 if (i != _chunks.Size())
2129 _latestChunk = (int)i;
2130 else
2131 {
2132 unsigned chunkIndex;
2133 for (;;)
2134 {
2135 if (_chunks.IsEmpty() ||
2136 (_chunks.Size() < k_NumChunks_MAX
2137 && _chunks_TotalSize + unpSize <= k_Chunks_TotalSize_MAX))
2138 {
2139 CChunk chunk;
2140 chunk.Buf = NULL;
2141 chunk.BufSize = 0;
2142 chunk.BlockIndex = -1;
2143 chunk.AccessMark = 0;
2144 chunkIndex = _chunks.Add(chunk);
2145 break;
2146 }
2147 chunkIndex = 0;
2148 if (_chunks.Size() == 1)
2149 break;
2150 {
2151 const CChunk *chunks = _chunks.ConstData();
2152 UInt64 accessMark_min = chunks[chunkIndex].AccessMark;
2153 const unsigned numChunks = _chunks.Size();
2154 for (i = 1; i < numChunks; i++)
2155 {
2156 if (chunks[i].AccessMark < accessMark_min)
2157 {
2158 chunkIndex = i;
2159 accessMark_min = chunks[i].AccessMark;
2160 }
2161 }
2162 }
2163 {
2164 CChunk &chunk = _chunks[chunkIndex];
2165 const UInt64 newTotalSize = _chunks_TotalSize - chunk.BufSize;
2166 if (newTotalSize + unpSize <= k_Chunks_TotalSize_MAX)
2167 break;
2168 _chunks_TotalSize = newTotalSize;
2169 chunk.Free();
2170 }
2171 // we have called chunk.Free() before, because
2172 // _chunks.Delete() doesn't call chunk.Free().
2173 _chunks.Delete(chunkIndex);
2174 PRF(printf("\n++num_chunks=%u, _chunks_TotalSize = %u\n", (unsigned)_chunks.Size(), (unsigned)_chunks_TotalSize);)
2175 }
2176
2177 CChunk &chunk = _chunks[chunkIndex];
2178 chunk.BlockIndex = -1;
2179 chunk.AccessMark = 0;
2180
2181 if (chunk.BufSize < unpSize)
2182 {
2183 _chunks_TotalSize -= chunk.BufSize;
2184 chunk.Free();
2185 // if (unpSize > k_Chunk_Size_MAX) return E_FAIL;
2186 chunk.Alloc((size_t)unpSize);
2187 if (!chunk.Buf)
2188 return E_OUTOFMEMORY;
2189 chunk.BufSize = (size_t)unpSize;
2190 _chunks_TotalSize += chunk.BufSize;
2191 }
2192
2193 RINOK(InStream_SeekSet(Stream, _startPos + File->StartPackPos + block.PackPos))
2194
2195 inStream->Init(block.PackSize);
2196 #ifdef Z7_DMG_USE_CACHE_FOR_COPY_BLOCKS
2197 if (block.Type == METHOD_COPY)
2198 {
2199 if (block.PackSize != unpSize)
2200 return E_FAIL;
2201 RINOK(ReadStream_FAIL(inStream, chunk.Buf, (size_t)unpSize))
2202 }
2203 else
2204 #endif
2205 {
2206 outStream->Init(chunk.Buf, (size_t)unpSize);
2207 RINOK(decoders.Code(inStream, outStream, block, &unpSize, NULL))
2208 if (outStream->GetPos() != unpSize)
2209 return E_FAIL;
2210 }
2211 chunk.BlockIndex = (int)blockIndex;
2212 _latestChunk = (int)chunkIndex;
2213 }
2214
2215 _chunks[_latestChunk].AccessMark = _accessMark++;
2216 }
2217
2218 _latestBlock = (int)blockIndex;
2219 }
2220
2221 const CBlock &block = File->Blocks[(unsigned)_latestBlock];
2222 const UInt64 offset = _virtPos - block.UnpPos;
2223 {
2224 const UInt64 rem = File->GetUnpackSize_of_Block((unsigned)_latestBlock) - offset;
2225 if (size > rem)
2226 size = (UInt32)rem;
2227 if (size == 0) // it's unexpected case. but we check it.
2228 return S_OK;
2229 }
2230 HRESULT res = S_OK;
2231
2232 if (block.IsZeroMethod())
2233 memset(data, 0, size);
2234 else if (_latestChunk >= 0)
2235 memcpy(data, _chunks[_latestChunk].Buf + (size_t)offset, size);
2236 else
2237 {
2238 if (block.Type != METHOD_COPY)
2239 return E_FAIL;
2240 RINOK(InStream_SeekSet(Stream, _startPos + File->StartPackPos + block.PackPos + offset))
2241 res = Stream->Read(data, size, &size);
2242 }
2243
2244 _virtPos += size;
2245 if (processedSize)
2246 *processedSize = size;
2247 return res;
2248 // COM_TRY_END
2249 }
2250 catch(...)
2251 {
2252 _errorMode = true;
2253 return E_OUTOFMEMORY;
2254 }
2255 }
2256
2257
2258 Z7_COM7F_IMF(CInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition))
2259 {
2260 switch (seekOrigin)
2261 {
2262 case STREAM_SEEK_SET: break;
2263 case STREAM_SEEK_CUR: offset += _virtPos; break;
2264 case STREAM_SEEK_END: offset += Size; break;
2265 default: return STG_E_INVALIDFUNCTION;
2266 }
2267 if (offset < 0)
2268 return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
2269 _virtPos = (UInt64)offset;
2270 if (newPosition)
2271 *newPosition = (UInt64)offset;
2272 return S_OK;
2273 }
2274
2275 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
2276 {
2277 COM_TRY_BEGIN
2278
2279 #ifdef DMG_SHOW_RAW
2280 if (index >= _files.Size())
2281 return S_FALSE;
2282 #endif
2283
2284 CMyComPtr2<ISequentialInStream, CInStream> spec;
2285 spec.Create_if_Empty();
2286 spec->File = &_files[index];
2287 const CFile &file = *spec->File;
2288
2289 if (!file.IsCorrect)
2290 return S_FALSE;
2291
2292 FOR_VECTOR (i, file.Blocks)
2293 {
2294 const CBlock &block = file.Blocks[i];
2295 if (!block.NeedAllocateBuffer())
2296 continue;
2297
2298 switch (block.Type)
2299 {
2300 #ifdef Z7_DMG_USE_CACHE_FOR_COPY_BLOCKS
2301 case METHOD_COPY:
2302 break;
2303 #endif
2304 case METHOD_ADC:
2305 case METHOD_ZLIB:
2306 case METHOD_BZIP2:
2307 case METHOD_LZFSE:
2308 case METHOD_XZ:
2309 // case METHOD_END:
2310 if (file.GetUnpackSize_of_Block(i) > k_Chunk_Size_MAX)
2311 return S_FALSE;
2312 break;
2313 default:
2314 return S_FALSE;
2315 }
2316 }
2317
2318 spec->Stream = _inStream;
2319 spec->Size = spec->File->Size;
2320 // RINOK(
2321 spec->Init(_startPos + _dataForkPair.Offset);
2322 *stream = spec.Detach();
2323 return S_OK;
2324
2325 COM_TRY_END
2326 }
2327
2328 REGISTER_ARC_I(
2329 "Dmg", "dmg", NULL, 0xE4,
2330 k_Signature,
2331 0,
2332 NArcInfoFlags::kBackwardOpen |
2333 NArcInfoFlags::kUseGlobalOffset,
2334 NULL)
2335
2336 }}
2337