xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/SquashfsHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // SquashfsHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/Alloc.h"
6 #include "../../../C/LzmaDec.h"
7 #include "../../../C/Xz.h"
8 #include "../../../C/ZstdDec.h"
9 #include "../../../C/CpuArch.h"
10 
11 #include "../../Common/ComTry.h"
12 #include "../../Common/MyLinux.h"
13 #include "../../Common/IntToString.h"
14 #include "../../Common/StringConvert.h"
15 #include "../../Common/UTFConvert.h"
16 
17 #include "../../Windows/PropVariantUtils.h"
18 #include "../../Windows/TimeUtils.h"
19 
20 #include "../Common/CWrappers.h"
21 #include "../Common/LimitedStreams.h"
22 #include "../Common/ProgressUtils.h"
23 #include "../Common/RegisterArc.h"
24 #include "../Common/StreamObjects.h"
25 #include "../Common/StreamUtils.h"
26 
27 #include "../Compress/CopyCoder.h"
28 #include "../Compress/ZlibDecoder.h"
29 // #include "../Compress/LzmaDecoder.h"
30 
31 namespace NArchive {
32 namespace NSquashfs {
33 
34 static const UInt32 kNumFilesMax = 1 << 28;
35 static const unsigned kNumDirLevelsMax = 1 << 10;
36 
37 // Layout: Header, Data, inodes, Directories, Fragments, UIDs, GIDs
38 
39 /*
40 #define Get16(p) (be ? GetBe16(p) : GetUi16(p))
41 #define Get32(p) (be ? GetBe32(p) : GetUi32(p))
42 #define Get64(p) (be ? GetBe64(p) : GetUi64(p))
43 */
44 
Get16b(const Byte * p,bool be)45 static UInt16 Get16b(const Byte *p, bool be) { return be ? GetBe16(p) : GetUi16(p); }
Get32b(const Byte * p,bool be)46 static UInt32 Get32b(const Byte *p, bool be) { return be ? GetBe32(p) : GetUi32(p); }
Get64b(const Byte * p,bool be)47 static UInt64 Get64b(const Byte *p, bool be) { return be ? GetBe64(p) : GetUi64(p); }
48 
49 #define Get16(p) Get16b(p, be)
50 #define Get32(p) Get32b(p, be)
51 #define Get64(p) Get64b(p, be)
52 
53 #define LE_16(offs, dest) dest = GetUi16(p + (offs))
54 #define LE_32(offs, dest) dest = GetUi32(p + (offs))
55 #define LE_64(offs, dest) dest = GetUi64(p + (offs))
56 
57 #define GET_16(offs, dest) dest = Get16(p + (offs))
58 #define GET_32(offs, dest) dest = Get32(p + (offs))
59 #define GET_64(offs, dest) dest = Get64(p + (offs))
60 
61 static const UInt32 kSignature32_LE = 0x73717368;
62 static const UInt32 kSignature32_BE = 0x68737173;
63 static const UInt32 kSignature32_LZ = 0x71736873;
64 static const UInt32 kSignature32_B2 = 0x73687371;
65 
66 #define kMethod_ZLIB 1
67 #define kMethod_LZMA 2
68 #define kMethod_LZO  3
69 #define kMethod_XZ   4
70 // #define kMethod_LZ4  5
71 #define kMethod_ZSTD 6
72 
73 static const char * const k_Methods[] =
74 {
75     "0"
76   , "ZLIB"
77   , "LZMA"
78   , "LZO"
79   , "XZ"
80   , "LZ4"
81   , "ZSTD"
82 };
83 
84 static const unsigned kMetadataBlockSizeLog = 13;
85 static const UInt32 kMetadataBlockSize = (1 << kMetadataBlockSizeLog);
86 
87 enum
88 {
89   kType_IPC,
90   kType_DIR,
91   kType_FILE,
92   kType_LNK,
93   kType_BLK,
94   kType_CHR,
95   kType_FIFO,
96   kType_SOCK
97 };
98 
99 static const UInt32 k_TypeToMode[] =
100 {
101   0,
102   MY_LIN_S_IFDIR, MY_LIN_S_IFREG, MY_LIN_S_IFLNK, MY_LIN_S_IFBLK, MY_LIN_S_IFCHR, MY_LIN_S_IFIFO, MY_LIN_S_IFSOCK,
103   MY_LIN_S_IFDIR, MY_LIN_S_IFREG, MY_LIN_S_IFLNK, MY_LIN_S_IFBLK, MY_LIN_S_IFCHR, MY_LIN_S_IFIFO, MY_LIN_S_IFSOCK
104 };
105 
106 
107 enum
108 {
109   kFlag_UNC_INODES,
110   kFlag_UNC_DATA,
111   kFlag_CHECK,
112   kFlag_UNC_FRAGS,
113   kFlag_NO_FRAGS,
114   kFlag_ALWAYS_FRAG,
115   kFlag_DUPLICATE,
116   kFlag_EXPORT
117 };
118 
119 static const char * const k_Flags[] =
120 {
121     "UNCOMPRESSED_INODES"
122   , "UNCOMPRESSED_DATA"
123   , "CHECK"
124   , "UNCOMPRESSED_FRAGMENTS"
125   , "NO_FRAGMENTS"
126   , "ALWAYS_FRAGMENTS"
127   , "DUPLICATES_REMOVED"
128   , "EXPORTABLE"
129   , "UNCOMPRESSED_XATTRS"
130   , "NO_XATTRS"
131   , "COMPRESSOR_OPTIONS"
132   , "UNCOMPRESSED_IDS"
133 };
134 
135 static const UInt32 kNotCompressedBit16 = 1 << 15;
136 static const UInt32 kNotCompressedBit32 = 1 << 24;
137 
138 #define GET_COMPRESSED_BLOCK_SIZE(size) ((size) & ~kNotCompressedBit32)
139 #define IS_COMPRESSED_BLOCK(size) (((size) & kNotCompressedBit32) == 0)
140 
141 // static const UInt32 kHeaderSize1 = 0x33;
142 // static const UInt32 kHeaderSize2 = 0x3F;
143 static const UInt32 kHeaderSize3 = 0x77;
144 // static const UInt32 kHeaderSize4 = 0x60;
145 
146 struct CHeader
147 {
148   bool be;
149   bool SeveralMethods;
150   Byte NumUids;
151   Byte NumGids;
152 
153   UInt32 NumInodes;
154   UInt32 CTime;
155   UInt32 BlockSize;
156   UInt32 NumFrags;
157   UInt16 Method;
158   UInt16 BlockSizeLog;
159   UInt16 Flags;
160   UInt16 NumIDs;
161   UInt16 Major;
162   UInt16 Minor;
163   UInt64 RootInode;
164   UInt64 Size;
165   UInt64 UidTable;
166   UInt64 GidTable;
167   UInt64 XattrIdTable;
168   UInt64 InodeTable;
169   UInt64 DirTable;
170   UInt64 FragTable;
171   UInt64 LookupTable;
172 
Parse3NArchive::NSquashfs::CHeader173   void Parse3(const Byte *p)
174   {
175     Method = kMethod_ZLIB;
176     GET_32 (0x08, Size);
177     GET_32 (0x0C, UidTable);
178     GET_32 (0x10, GidTable);
179     GET_32 (0x14, InodeTable);
180     GET_32 (0x18, DirTable);
181     GET_16 (0x20, BlockSize);
182     GET_16 (0x22, BlockSizeLog);
183     Flags   = p[0x24];
184     NumUids = p[0x25];
185     NumGids = p[0x26];
186     GET_32 (0x27, CTime);
187     GET_64 (0x2B, RootInode);
188     NumFrags = 0;
189     FragTable = UidTable;
190 
191     if (Major >= 2)
192     {
193       GET_32 (0x33, BlockSize);
194       GET_32 (0x37, NumFrags);
195       GET_32 (0x3B, FragTable);
196       if (Major == 3)
197       {
198         GET_64 (0x3F, Size);
199         GET_64 (0x47, UidTable);
200         GET_64 (0x4F, GidTable);
201         GET_64 (0x57, InodeTable);
202         GET_64 (0x5F, DirTable);
203         GET_64 (0x67, FragTable);
204         GET_64 (0x6F, LookupTable);
205       }
206     }
207   }
208 
Parse4NArchive::NSquashfs::CHeader209   void Parse4(const Byte *p)
210   {
211     LE_32 (0x08, CTime);
212     LE_32 (0x0C, BlockSize);
213     LE_32 (0x10, NumFrags);
214     LE_16 (0x14, Method);
215     LE_16 (0x16, BlockSizeLog);
216     LE_16 (0x18, Flags);
217     LE_16 (0x1A, NumIDs);
218     LE_64 (0x20, RootInode);
219     LE_64 (0x28, Size);
220     LE_64 (0x30, UidTable);
221     LE_64 (0x38, XattrIdTable);
222     LE_64 (0x40, InodeTable);
223     LE_64 (0x48, DirTable);
224     LE_64 (0x50, FragTable);
225     LE_64 (0x58, LookupTable);
226     GidTable = 0;
227   }
228 
ParseNArchive::NSquashfs::CHeader229   bool Parse(const Byte *p)
230   {
231     be = false;
232     SeveralMethods = false;
233     switch (GetUi32(p))
234     {
235       case kSignature32_LE: break;
236       case kSignature32_BE: be = true; break;
237       case kSignature32_LZ: SeveralMethods = true; break;
238       case kSignature32_B2: SeveralMethods = true; be = true; break;
239       default: return false;
240     }
241     GET_32 (4, NumInodes);
242     GET_16 (0x1C, Major);
243     GET_16 (0x1E, Minor);
244     if (Major <= 3)
245       Parse3(p);
246     else
247     {
248       if (be)
249         return false;
250       Parse4(p);
251     }
252     return
253       InodeTable < DirTable &&
254       DirTable <= FragTable &&
255       FragTable <= Size &&
256       UidTable <= Size &&
257       BlockSizeLog >= 12 &&
258       BlockSizeLog < 31 &&
259       BlockSize == ((UInt32)1 << BlockSizeLog);
260   }
261 
IsSupportedNArchive::NSquashfs::CHeader262   bool IsSupported() const { return Major > 0 && Major <= 4 && BlockSizeLog <= 23; }
IsOldVersionNArchive::NSquashfs::CHeader263   bool IsOldVersion() const { return Major < 4; }
NeedCheckDataNArchive::NSquashfs::CHeader264   bool NeedCheckData() const { return (Flags & (1 << kFlag_CHECK)) != 0; }
GetFileNameOffsetNArchive::NSquashfs::CHeader265   unsigned GetFileNameOffset() const { return Major <= 2 ? 3 : (Major == 3 ? 5 : 8); }
GetSymLinkOffsetNArchive::NSquashfs::CHeader266   unsigned GetSymLinkOffset() const { return Major <= 1 ? 5: (Major <= 2 ? 6: (Major == 3 ? 18 : 24)); }
GetSpecGuidIndexNArchive::NSquashfs::CHeader267   unsigned GetSpecGuidIndex() const { return Major <= 1 ? 0xF: 0xFF; }
268 };
269 
270 static const UInt32 kFrag_Empty = (UInt32)(Int32)-1;
271 // static const UInt32 kXattr_Empty = (UInt32)(Int32)-1;
272 
273 struct CNode
274 {
275   UInt16 Type;
276   UInt16 Mode;
277   UInt16 Uid;
278   UInt16 Gid;
279   UInt32 Frag;
280   UInt32 Offset;
281   // UInt32 MTime;
282   // UInt32 Number;
283   // UInt32 NumLinks;
284   // UInt32 RDev;
285   // UInt32 Xattr;
286   // UInt32 Parent;
287 
288   UInt64 FileSize;
289   UInt64 StartBlock;
290   // UInt64 Sparse;
291 
292   UInt32 Parse1(const Byte *p, UInt32 size, const CHeader &_h);
293   UInt32 Parse2(const Byte *p, UInt32 size, const CHeader &_h);
294   UInt32 Parse3(const Byte *p, UInt32 size, const CHeader &_h);
295   UInt32 Parse4(const Byte *p, UInt32 size, const CHeader &_h);
296 
IsDirNArchive::NSquashfs::CNode297   bool IsDir() const { return (Type == kType_DIR || Type == kType_DIR + 7); }
IsLinkNArchive::NSquashfs::CNode298   bool IsLink() const { return (Type == kType_LNK || Type == kType_LNK + 7); }
GetSizeNArchive::NSquashfs::CNode299   UInt64 GetSize() const { return IsDir() ? 0 : FileSize; }
300 
ThereAreFragsNArchive::NSquashfs::CNode301   bool ThereAreFrags() const { return Frag != kFrag_Empty; }
GetNumBlocksNArchive::NSquashfs::CNode302   UInt64 GetNumBlocks(const CHeader &_h) const
303   {
304     return (FileSize >> _h.BlockSizeLog) +
305       (!ThereAreFrags() && (FileSize & (_h.BlockSize - 1)) != 0);
306   }
307 };
308 
Parse1(const Byte * p,UInt32 size,const CHeader & _h)309 UInt32 CNode::Parse1(const Byte *p, UInt32 size, const CHeader &_h)
310 {
311   const bool be = _h.be;
312   if (size < 4)
313     return 0;
314   {
315     const UInt32 t = Get16(p);
316     if (be)
317     {
318       Type = (UInt16)(t >> 12);
319       Mode = (UInt16)(t & 0xFFF);
320       Uid = (UInt16)(p[2] >> 4);
321       Gid = (UInt16)(p[2] & 0xF);
322     }
323     else
324     {
325       Type = (UInt16)(t & 0xF);
326       Mode = (UInt16)(t >> 4);
327       Uid = (UInt16)(p[2] & 0xF);
328       Gid = (UInt16)(p[2] >> 4);
329     }
330   }
331 
332   // Xattr = kXattr_Empty;
333   // MTime = 0;
334   FileSize = 0;
335   StartBlock = 0;
336   Frag = kFrag_Empty;
337 
338   if (Type == 0)
339   {
340     Byte t = p[3];
341     if (be)
342     {
343       Type = (UInt16)(t >> 4);
344       Offset = (UInt16)(t & 0xF);
345     }
346     else
347     {
348       Type = (UInt16)(t & 0xF);
349       Offset = (UInt16)(t >> 4);
350     }
351     return (Type == kType_FIFO || Type == kType_SOCK) ? 4 : 0;
352   }
353 
354   Type--;
355   Uid = (UInt16)(Uid + (Type / 5) * 16);
356   Type = (UInt16)((Type % 5) + 1);
357 
358   if (Type == kType_FILE)
359   {
360     if (size < 15)
361       return 0;
362     // GET_32 (3, MTime);
363     GET_32 (7, StartBlock);
364     UInt32 t;
365     GET_32 (11, t);
366     FileSize = t;
367     UInt32 numBlocks = t >> _h.BlockSizeLog;
368     if ((t & (_h.BlockSize - 1)) != 0)
369       numBlocks++;
370     UInt32 pos = numBlocks * 2 + 15;
371     return (pos <= size) ? pos : 0;
372   }
373 
374   if (Type == kType_DIR)
375   {
376     if (size < 14)
377       return 0;
378     UInt32 t = Get32(p + 3);
379     if (be)
380     {
381       FileSize = t >> 13;
382       Offset = t & 0x1FFF;
383     }
384     else
385     {
386       FileSize = t & 0x7FFFF;
387       Offset = t >> 19;
388     }
389     // GET_32 (7, MTime);
390     GET_32 (10, StartBlock);
391     if (be)
392       StartBlock &= 0xFFFFFF;
393     else
394       StartBlock >>= 8;
395     return 14;
396   }
397 
398   if (size < 5)
399     return 0;
400 
401   if (Type == kType_LNK)
402   {
403     UInt32 len;
404     GET_16 (3, len);
405     FileSize = len;
406     len += 5;
407     return (len <= size) ? len : 0;
408   }
409 
410   // GET_32 (3, RDev);
411   return 5;
412 }
413 
Parse2(const Byte * p,UInt32 size,const CHeader & _h)414 UInt32 CNode::Parse2(const Byte *p, UInt32 size, const CHeader &_h)
415 {
416   const bool be = _h.be;
417   if (size < 4)
418     return 0;
419   {
420     const UInt32 t = Get16(p);
421     if (be)
422     {
423       Type = (UInt16)(t >> 12);
424       Mode = (UInt16)(t & 0xFFF);
425     }
426     else
427     {
428       Type = (UInt16)(t & 0xF);
429       Mode = (UInt16)(t >> 4);
430     }
431   }
432 
433   Uid = p[2];
434   Gid = p[3];
435 
436   // Xattr = kXattr_Empty;
437 
438   if (Type == kType_FILE)
439   {
440     if (size < 24)
441       return 0;
442     // GET_32 (4, MTime);
443     GET_32 (8, StartBlock);
444     GET_32 (12, Frag);
445     GET_32 (16, Offset);
446     UInt32 t;
447     GET_32 (20, t);
448     FileSize = t;
449     UInt32 numBlocks = t >> _h.BlockSizeLog;
450     if (!ThereAreFrags() && (t & (_h.BlockSize - 1)) != 0)
451       numBlocks++;
452     UInt32 pos = numBlocks * 4 + 24;
453     return (pos <= size) ? (UInt32)pos : 0;
454   }
455 
456   FileSize = 0;
457   // MTime = 0;
458   StartBlock = 0;
459   Frag = kFrag_Empty;
460 
461   if (Type == kType_DIR)
462   {
463     if (size < 15)
464       return 0;
465     UInt32 t = Get32(p + 4);
466     if (be)
467     {
468       FileSize = t >> 13;
469       Offset = t & 0x1FFF;
470     }
471     else
472     {
473       FileSize = t & 0x7FFFF;
474       Offset = t >> 19;
475     }
476     // GET_32 (8, MTime);
477     GET_32 (11, StartBlock);
478     if (be)
479       StartBlock &= 0xFFFFFF;
480     else
481       StartBlock >>= 8;
482     return 15;
483   }
484 
485   if (Type == kType_DIR + 7)
486   {
487     if (size < 18)
488       return 0;
489     UInt32 t = Get32(p + 4);
490     UInt32 t2 = Get16(p + 7);
491     if (be)
492     {
493       FileSize = t >> 5;
494       Offset = t2 & 0x1FFF;
495     }
496     else
497     {
498       FileSize = t & 0x7FFFFFF;
499       Offset = t2 >> 3;
500     }
501     // GET_32 (9, MTime);
502     GET_32 (12, StartBlock);
503     if (be)
504       StartBlock &= 0xFFFFFF;
505     else
506       StartBlock >>= 8;
507     UInt32 iCount;
508     GET_16 (16, iCount);
509     UInt32 pos = 18;
510     for (UInt32 i = 0; i < iCount; i++)
511     {
512       // 27 bits: index
513       // 29 bits: startBlock
514       if (pos + 8 > size)
515         return 0;
516       pos += 8 + (UInt32)p[pos + 7] + 1; // nameSize
517       if (pos > size)
518         return 0;
519     }
520     return pos;
521   }
522 
523   if (Type == kType_FIFO || Type == kType_SOCK)
524     return 4;
525 
526   if (size < 6)
527     return 0;
528 
529   if (Type == kType_LNK)
530   {
531     UInt32 len;
532     GET_16 (4, len);
533     FileSize = len;
534     len += 6;
535     return (len <= size) ? len : 0;
536   }
537 
538   if (Type == kType_BLK || Type == kType_CHR)
539   {
540     // GET_16 (4, RDev);
541     return 6;
542   }
543 
544   return 0;
545 }
546 
Parse3(const Byte * p,UInt32 size,const CHeader & _h)547 UInt32 CNode::Parse3(const Byte *p, UInt32 size, const CHeader &_h)
548 {
549   const bool be = _h.be;
550   if (size < 12)
551     return 0;
552 
553   {
554     const UInt32 t = Get16(p);
555     if (be)
556     {
557       Type = (UInt16)(t >> 12);
558       Mode = (UInt16)(t & 0xFFF);
559     }
560     else
561     {
562       Type = (UInt16)(t & 0xF);
563       Mode = (UInt16)(t >> 4);
564     }
565   }
566 
567   Uid = p[2];
568   Gid = p[3];
569   // GET_32 (4, MTime);
570   // GET_32 (8, Number);
571   // Xattr = kXattr_Empty;
572   FileSize = 0;
573   StartBlock = 0;
574 
575   if (Type == kType_FILE || Type == kType_FILE + 7)
576   {
577     UInt32 offset;
578     if (Type == kType_FILE)
579     {
580       if (size < 32)
581         return 0;
582       GET_64 (12, StartBlock);
583       GET_32 (20, Frag);
584       GET_32 (24, Offset);
585       GET_32 (28, FileSize);
586       offset = 32;
587     }
588     else
589     {
590       if (size < 40)
591         return 0;
592       // GET_32 (12, NumLinks);
593       GET_64 (16, StartBlock);
594       GET_32 (24, Frag);
595       GET_32 (28, Offset);
596       GET_64 (32, FileSize);
597       offset = 40;
598     }
599     UInt64 pos = GetNumBlocks(_h) * 4 + offset;
600     return (pos <= size) ? (UInt32)pos : 0;
601   }
602 
603   if (size < 16)
604     return 0;
605   // GET_32 (12, NumLinks);
606 
607   if (Type == kType_DIR)
608   {
609     if (size < 28)
610       return 0;
611     UInt32 t = Get32(p + 16);
612     if (be)
613     {
614       FileSize = t >> 13;
615       Offset = t & 0x1FFF;
616     }
617     else
618     {
619       FileSize = t & 0x7FFFF;
620       Offset = t >> 19;
621     }
622     GET_32 (20, StartBlock);
623     // GET_32 (24, Parent);
624     return 28;
625   }
626 
627   if (Type == kType_DIR + 7)
628   {
629     if (size < 31)
630       return 0;
631     UInt32 t = Get32(p + 16);
632     UInt32 t2 = Get16(p + 19);
633     if (be)
634     {
635       FileSize = t >> 5;
636       Offset = t2 & 0x1FFF;
637     }
638     else
639     {
640       FileSize = t & 0x7FFFFFF;
641       Offset = t2 >> 3;
642     }
643     GET_32 (21, StartBlock);
644     UInt32 iCount;
645     GET_16 (25, iCount);
646     // GET_32 (27, Parent);
647     UInt32 pos = 31;
648     for (UInt32 i = 0; i < iCount; i++)
649     {
650       // UInt32 index
651       // UInt32 startBlock
652       if (pos + 9 > size)
653         return 0;
654       pos += 9 + (unsigned)p[pos + 8] + 1; // nameSize
655       if (pos > size)
656         return 0;
657     }
658     return pos;
659   }
660 
661   if (Type == kType_FIFO || Type == kType_SOCK)
662     return 16;
663 
664   if (size < 18)
665     return 0;
666   if (Type == kType_LNK)
667   {
668     UInt32 len;
669     GET_16 (16, len);
670     FileSize = len;
671     len += 18;
672     return (len <= size) ? len : 0;
673   }
674 
675   if (Type == kType_BLK || Type == kType_CHR)
676   {
677     // GET_16 (16, RDev);
678     return 18;
679   }
680 
681   return 0;
682 }
683 
Parse4(const Byte * p,UInt32 size,const CHeader & _h)684 UInt32 CNode::Parse4(const Byte *p, UInt32 size, const CHeader &_h)
685 {
686   if (size < 20)
687     return 0;
688   LE_16 (0, Type);
689   LE_16 (2, Mode);
690   LE_16 (4, Uid);
691   LE_16 (6, Gid);
692   // LE_32 (8, MTime);
693   // LE_32 (12, Number);
694 
695   // Xattr = kXattr_Empty;
696   FileSize = 0;
697   StartBlock = 0;
698 
699   if (Type == kType_FILE || Type == kType_FILE + 7)
700   {
701     UInt32 offset;
702     if (Type == kType_FILE)
703     {
704       if (size < 32)
705         return 0;
706       LE_32 (16, StartBlock);
707       LE_32 (20, Frag);
708       LE_32 (24, Offset);
709       LE_32 (28, FileSize);
710       offset = 32;
711     }
712     else
713     {
714       if (size < 56)
715         return 0;
716       LE_64 (16, StartBlock);
717       LE_64 (24, FileSize);
718       // LE_64 (32, Sparse);
719       // LE_32 (40, NumLinks);
720       LE_32 (44, Frag);
721       LE_32 (48, Offset);
722       // LE_32 (52, Xattr);
723       offset = 56;
724     }
725     UInt64 pos = GetNumBlocks(_h) * 4 + offset;
726     return (pos <= size) ? (UInt32)pos : 0;
727   }
728 
729   if (Type == kType_DIR)
730   {
731     if (size < 32)
732       return 0;
733     LE_32 (16, StartBlock);
734     // LE_32 (20, NumLinks);
735     LE_16 (24, FileSize);
736     LE_16 (26, Offset);
737     // LE_32 (28, Parent);
738     return 32;
739   }
740 
741   // LE_32 (16, NumLinks);
742 
743   if (Type == kType_DIR + 7)
744   {
745     if (size < 40)
746       return 0;
747     LE_32 (20, FileSize);
748     LE_32 (24, StartBlock);
749     // LE_32 (28, Parent);
750     UInt32 iCount;
751     LE_16 (32, iCount);
752     LE_16 (34, Offset);
753     // LE_32 (36, Xattr);
754 
755     UInt32 pos = 40;
756     for (UInt32 i = 0; i < iCount; i++)
757     {
758       // UInt32 index
759       // UInt32 startBlock
760       if (pos + 12 > size)
761         return 0;
762       UInt32 nameLen = GetUi32(p + pos + 8);
763       pos += 12 + nameLen + 1;
764       if (pos > size || nameLen > (1 << 10))
765         return 0;
766     }
767     return pos;
768   }
769 
770   unsigned offset = 20;
771   switch (Type)
772   {
773     case kType_FIFO: case kType_FIFO + 7:
774     case kType_SOCK: case kType_SOCK + 7:
775       break;
776     case kType_LNK: case kType_LNK + 7:
777     {
778       if (size < 24)
779         return 0;
780       UInt32 len;
781       LE_32 (20, len);
782       FileSize = len;
783       offset = len + 24;
784       if (size < offset || len > (1 << 30))
785         return 0;
786       break;
787     }
788     case kType_BLK: case kType_BLK + 7:
789     case kType_CHR: case kType_CHR + 7:
790       if (size < 24)
791         return 0;
792       // LE_32 (20, RDev);
793       offset = 24;
794       break;
795     default:
796       return 0;
797   }
798 
799   if (Type >= 8)
800   {
801     if (size < offset + 4)
802       return 0;
803     // LE_32 (offset, Xattr);
804     offset += 4;
805   }
806   return offset;
807 }
808 
809 struct CItem
810 {
811   int Node;
812   int Parent;
813   UInt32 Ptr;
814 
CItemNArchive::NSquashfs::CItem815   CItem(): Node(-1), Parent(-1), Ptr(0) {}
816 };
817 
818 struct CData
819 {
820   CByteBuffer Data;
821   CRecordVector<UInt32> PackPos;
822   CRecordVector<UInt32> UnpackPos; // additional item at the end contains TotalUnpackSize
823 
GetNumBlocksNArchive::NSquashfs::CData824   UInt32 GetNumBlocks() const { return PackPos.Size(); }
ClearNArchive::NSquashfs::CData825   void Clear()
826   {
827     Data.Free();
828     PackPos.Clear();
829     UnpackPos.Clear();
830   }
831 };
832 
833 struct CFrag
834 {
835   UInt64 StartBlock;
836   UInt32 Size;
837 };
838 
839 
840 Z7_CLASS_IMP_CHandler_IInArchive_1(
841   IInArchiveGetStream
842 )
843   bool _noPropsLZMA;
844   bool _needCheckLzma;
845 
846   CRecordVector<CItem> _items;
847   CRecordVector<CNode> _nodes;
848   CRecordVector<UInt32> _nodesPos;
849   CRecordVector<UInt32> _blockToNode;
850   CData _inodesData;
851   CData _dirs;
852   CRecordVector<CFrag> _frags;
853   CByteBuffer _uids;
854   CByteBuffer _gids;
855   CHeader _h;
856 
857   UInt64 _sizeCalculated;
858   CMyComPtr<IInStream> _stream;
859 
860   IArchiveOpenCallback *_openCallback;
861 
862   UInt32 _openCodePage;
863   int _nodeIndex;
864   CRecordVector<bool> _blockCompressed;
865   CRecordVector<UInt64> _blockOffsets;
866 
867   CByteBuffer _cachedBlock;
868   UInt64 _cachedBlockStartPos;
869   UInt32 _cachedPackBlockSize;
870   UInt32 _cachedUnpackBlockSize;
871 
872   CMyComPtr2_Create<ISequentialInStream, CLimitedSequentialInStream> _limitedInStream;
873   CMyComPtr2_Create<ISequentialOutStream, CBufPtrSeqOutStream> _outStream;
874   CMyComPtr2_Create<ISequentialOutStream, CDynBufSeqOutStream> _dynOutStream;
875 
876   // CMyComPtr2<ICompressCoder, NCompress::NLzma::CDecoder> _lzmaDecoder;
877   CMyComPtr2<ICompressCoder, NCompress::NZlib::CDecoder> _zlibDecoder;
878 
879   CXzUnpacker _xz;
880   CZstdDecHandle _zstd;
881 
882   CByteBuffer _inputBuffer;
883 
ClearCache()884   void ClearCache()
885   {
886     _cachedBlockStartPos = 0;
887     _cachedPackBlockSize = 0;
888     _cachedUnpackBlockSize = 0;
889   }
890 
Seek2(UInt64 offset)891   HRESULT Seek2(UInt64 offset)
892   {
893     return InStream_SeekSet(_stream, offset);
894   }
895 
896   HRESULT Decompress(ISequentialOutStream *outStream, Byte *outBuf, bool *outBufWasWritten, UInt32 *outBufWasWrittenSize,
897       UInt32 inSize, UInt32 outSizeMax);
898   HRESULT ReadMetadataBlock(UInt32 &packSize);
899   HRESULT ReadMetadataBlock2();
900   HRESULT ReadData(CData &data, UInt64 start, UInt64 end);
901 
902   HRESULT OpenDir(int parent, UInt32 startBlock, UInt32 offset, unsigned level, int &nodeIndex);
903   HRESULT ScanInodes(UInt64 ptr);
904   HRESULT ReadUids(UInt64 start, UInt32 num, CByteBuffer &ids);
905   HRESULT Open2(IInStream *inStream);
906   AString GetPath(unsigned index) const;
907   bool GetPackSize(unsigned index, UInt64 &res, bool fillOffsets);
908 
909 public:
910   CHandler();
911   ~CHandler()
912   {
913     XzUnpacker_Free(&_xz);
914     if (_zstd)
915       ZstdDec_Destroy(_zstd);
916   }
917 
918   HRESULT ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize);
919 };
920 
921 
922 CHandler::CHandler():
923     _zstd(NULL)
924 {
925   XzUnpacker_Construct(&_xz, &g_Alloc);
926 }
927 
928 static const Byte kProps[] =
929 {
930   kpidPath,
931   kpidIsDir,
932   kpidSize,
933   kpidPackSize,
934   kpidMTime,
935   kpidPosixAttrib,
936   kpidUserId,
937   kpidGroupId
938   // kpidLinks,
939   // kpidOffset
940 };
941 
942 static const Byte kArcProps[] =
943 {
944   kpidHeadersSize,
945   kpidFileSystem,
946   kpidMethod,
947   kpidClusterSize,
948   kpidBigEndian,
949   kpidCTime,
950   kpidCharacts,
951   kpidCodePage
952   // kpidNumBlocks
953 };
954 
955 IMP_IInArchive_Props
956 IMP_IInArchive_ArcProps
957 
958 static HRESULT LzoDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen)
959 {
960   SizeT destRem = *destLen;
961   SizeT srcRem = *srcLen;
962   *destLen = 0;
963   *srcLen = 0;
964   const Byte *destStart = dest;
965   const Byte *srcStart = src;
966   unsigned mode = 0;
967 
968   {
969     if (srcRem == 0)
970       return S_FALSE;
971     UInt32 b = *src;
972     if (b > 17)
973     {
974       src++;
975       srcRem--;
976       b -= 17;
977       mode = (b < 4 ? 1 : 4);
978       if (b > srcRem || b > destRem)
979         return S_FALSE;
980       srcRem -= b;
981       destRem -= b;
982       do
983         *dest++ = *src++;
984       while (--b);
985     }
986   }
987 
988   for (;;)
989   {
990     if (srcRem < 3)
991       return S_FALSE;
992     UInt32 b = *src++;
993     srcRem--;
994     UInt32 len, back;
995 
996     if (b >= 64)
997     {
998       srcRem--;
999       back = ((b >> 2) & 7) + ((UInt32)*src++ << 3);
1000       len = (b >> 5) + 1;
1001     }
1002     else if (b < 16)
1003     {
1004       if (mode == 0)
1005       {
1006         if (b == 0)
1007         {
1008           for (b = 15;; b += 255)
1009           {
1010             if (srcRem == 0)
1011               return S_FALSE;
1012             UInt32 b2 = *src++;
1013             srcRem--;
1014             if (b2 != 0)
1015             {
1016               b += b2;
1017               break;
1018             }
1019           }
1020         }
1021 
1022         b += 3;
1023         if (b > srcRem || b > destRem)
1024           return S_FALSE;
1025         srcRem -= b;
1026         destRem -= b;
1027         mode = 4;
1028         do
1029           *dest++ = *src++;
1030         while (--b);
1031         continue;
1032       }
1033 
1034       srcRem--;
1035       back = (b >> 2) + ((UInt32)*src++ << 2);
1036       len = 2;
1037       if (mode == 4)
1038       {
1039         back += (1 << 11);
1040         len = 3;
1041       }
1042     }
1043     else
1044     {
1045       UInt32 bOld = b;
1046       b = (b < 32 ? 7 : 31);
1047       len = bOld & b;
1048 
1049       if (len == 0)
1050       {
1051         for (len = b;; len += 255)
1052         {
1053           if (srcRem == 0)
1054             return S_FALSE;
1055           UInt32 b2 = *src++;
1056           srcRem--;
1057           if (b2 != 0)
1058           {
1059             len += b2;
1060             break;
1061           }
1062         }
1063       }
1064 
1065       len += 2;
1066       if (srcRem < 2)
1067         return S_FALSE;
1068       b = *src;
1069       back = (b >> 2) + ((UInt32)src[1] << 6);
1070       src += 2;
1071       srcRem -= 2;
1072       if (bOld < 32)
1073       {
1074         back += ((bOld & 8) << 11);
1075         if (back == 0)
1076         {
1077           *destLen = (size_t)(dest - destStart);
1078           *srcLen = (size_t)(src - srcStart);
1079           return S_OK;
1080         }
1081         back += (1 << 14) - 1;
1082       }
1083     }
1084 
1085     back++;
1086     if (len > destRem || (size_t)(dest - destStart) < back)
1087       return S_FALSE;
1088     destRem -= len;
1089     Byte *destTemp = dest - back;
1090     dest += len;
1091 
1092     do
1093     {
1094       *(destTemp + back) = *destTemp;
1095       destTemp++;
1096     }
1097     while (--len);
1098 
1099     b &= 3;
1100     mode = b;
1101     if (b == 0)
1102       continue;
1103     if (b > srcRem || b > destRem)
1104       return S_FALSE;
1105     srcRem -= b;
1106     destRem -= b;
1107     *dest++ = *src++;
1108     if (b > 1)
1109     {
1110       *dest++ = *src++;
1111       if (b > 2)
1112         *dest++ = *src++;
1113     }
1114   }
1115 }
1116 
1117 HRESULT CHandler::Decompress(ISequentialOutStream *outStream, Byte *outBuf, bool *outBufWasWritten, UInt32 *outBufWasWrittenSize, UInt32 inSize, UInt32 outSizeMax)
1118 {
1119   if (outBuf)
1120   {
1121     *outBufWasWritten = false;
1122     *outBufWasWrittenSize = 0;
1123   }
1124   UInt32 method = _h.Method;
1125   if (_h.SeveralMethods)
1126   {
1127     Byte b;
1128     RINOK(ReadStream_FALSE(_stream, &b, 1))
1129     RINOK(_stream->Seek(-1, STREAM_SEEK_CUR, NULL))
1130     method = (b == 0x5D ? kMethod_LZMA : kMethod_ZLIB);
1131   }
1132 
1133   if (method == kMethod_ZLIB && _needCheckLzma)
1134   {
1135     Byte b;
1136     RINOK(ReadStream_FALSE(_stream, &b, 1))
1137     RINOK(_stream->Seek(-1, STREAM_SEEK_CUR, NULL))
1138     if (b == 0)
1139     {
1140       _noPropsLZMA = true;
1141       method = _h.Method = kMethod_LZMA;
1142     }
1143     _needCheckLzma = false;
1144   }
1145 
1146   if (method == kMethod_ZLIB)
1147   {
1148     _zlibDecoder.Create_if_Empty();
1149     RINOK(_zlibDecoder.Interface()->Code(_limitedInStream, outStream, NULL, NULL, NULL))
1150     if (inSize != _zlibDecoder->GetInputProcessedSize())
1151       return S_FALSE;
1152   }
1153   /*
1154   else if (method == kMethod_LZMA)
1155   {
1156     _lzmaDecoder.Create_if_Empty();
1157     // _lzmaDecoder->FinishStream = true;
1158     const UInt32 kPropsSize = LZMA_PROPS_SIZE + 8;
1159     Byte props[kPropsSize];
1160     UInt32 propsSize;
1161     UInt64 outSize;
1162     if (_noPropsLZMA)
1163     {
1164       props[0] = 0x5D;
1165       SetUi32(&props[1], _h.BlockSize);
1166       propsSize = 0;
1167       outSize = outSizeMax;
1168     }
1169     else
1170     {
1171       RINOK(ReadStream_FALSE(_limitedInStream, props, kPropsSize));
1172       propsSize = kPropsSize;
1173       outSize = GetUi64(&props[LZMA_PROPS_SIZE]);
1174       if (outSize > outSizeMax)
1175         return S_FALSE;
1176     }
1177     RINOK(_lzmaDecoderSpec->SetDecoderProperties2(props, LZMA_PROPS_SIZE));
1178     RINOK(_lzmaDecoder->Code(_limitedInStream, outStream, NULL, &outSize, NULL));
1179     if (inSize != propsSize + _lzmaDecoderSpec->GetInputProcessedSize())
1180       return S_FALSE;
1181   }
1182   */
1183   else
1184   {
1185     if (_inputBuffer.Size() < inSize)
1186       _inputBuffer.Alloc(inSize);
1187     RINOK(ReadStream_FALSE(_stream, _inputBuffer, inSize))
1188 
1189     Byte *dest = outBuf;
1190     if (!outBuf)
1191     {
1192       dest = _dynOutStream->GetBufPtrForWriting(outSizeMax);
1193       if (!dest)
1194         return E_OUTOFMEMORY;
1195     }
1196 
1197     SizeT destLen = outSizeMax, srcLen = inSize;
1198 
1199     if (method == kMethod_LZO)
1200     {
1201       RINOK(LzoDecode(dest, &destLen, _inputBuffer, &srcLen))
1202     }
1203     else if (method == kMethod_LZMA)
1204     {
1205       Byte props[5];
1206       const Byte *src = _inputBuffer;
1207 
1208       if (_noPropsLZMA)
1209       {
1210         props[0] = 0x5D;
1211         SetUi32(&props[1], _h.BlockSize)
1212       }
1213       else
1214       {
1215         const UInt32 kPropsSize = LZMA_PROPS_SIZE + 8;
1216         if (inSize < kPropsSize)
1217           return S_FALSE;
1218         memcpy(props, src, LZMA_PROPS_SIZE);
1219         UInt64 outSize = GetUi64(src + LZMA_PROPS_SIZE);
1220         if (outSize > outSizeMax)
1221           return S_FALSE;
1222         destLen = (SizeT)outSize;
1223         src += kPropsSize;
1224         inSize -= kPropsSize;
1225         srcLen = inSize;
1226       }
1227 
1228       ELzmaStatus status;
1229       SRes res = LzmaDecode(dest, &destLen,
1230           src, &srcLen,
1231           props, LZMA_PROPS_SIZE,
1232           LZMA_FINISH_END,
1233           &status, &g_Alloc);
1234       if (res != 0)
1235         return SResToHRESULT(res);
1236       if (status != LZMA_STATUS_FINISHED_WITH_MARK
1237           && status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)
1238         return S_FALSE;
1239     }
1240     else if (method == kMethod_ZSTD)
1241     {
1242       const Byte *src = _inputBuffer;
1243 
1244       if (!_zstd)
1245       {
1246         _zstd = ZstdDec_Create(&g_AlignedAlloc, &g_AlignedAlloc);
1247         if (!_zstd)
1248           return E_OUTOFMEMORY;
1249       }
1250 
1251       CZstdDecState state;
1252       ZstdDecState_Clear(&state);
1253 
1254       state.inBuf = src;
1255       state.inLim = srcLen; //  + 1; for debug
1256       // state.outStep = outSizeMax;
1257 
1258       state.outBuf_fromCaller = dest;
1259       state.outBufSize_fromCaller = outSizeMax;
1260       // state.mustBeFinished = True;
1261 
1262       ZstdDec_Init(_zstd);
1263       SRes sres;
1264       for (;;)
1265       {
1266         sres = ZstdDec_Decode(_zstd, &state);
1267         if (sres != SZ_OK)
1268           break;
1269         if (state.inLim == state.inPos
1270             && (state.status == ZSTD_STATUS_NEEDS_MORE_INPUT ||
1271                 state.status == ZSTD_STATUS_FINISHED_FRAME))
1272           break;
1273         // sres = sres;
1274         // break; // for debug
1275       }
1276 
1277       CZstdDecResInfo info;
1278       // ZstdDecInfo_Clear(&stat);
1279       // stat->InSize = state.inPos;
1280       ZstdDec_GetResInfo(_zstd, &state, sres, &info);
1281       sres = info.decode_SRes;
1282       if (sres == SZ_OK)
1283       {
1284         if (state.status != ZSTD_STATUS_FINISHED_FRAME
1285             // ||stat.UnexpededEnd
1286             || info.extraSize != 0
1287             || state.inLim != state.inPos)
1288           sres = SZ_ERROR_DATA;
1289       }
1290       if (sres != SZ_OK)
1291         return SResToHRESULT(sres);
1292       if (state.winPos > outSizeMax)
1293         return E_FAIL;
1294       // memcpy(dest, state.dic, state.dicPos);
1295       destLen = state.winPos;
1296     }
1297     else
1298     {
1299       ECoderStatus status;
1300       const SRes res = XzUnpacker_CodeFull(&_xz,
1301           dest, &destLen,
1302           _inputBuffer, &srcLen,
1303           CODER_FINISH_END, &status);
1304       if (res != 0)
1305         return SResToHRESULT(res);
1306       if (status != CODER_STATUS_NEEDS_MORE_INPUT || !XzUnpacker_IsStreamWasFinished(&_xz))
1307         return S_FALSE;
1308     }
1309 
1310     if (inSize != srcLen)
1311       return S_FALSE;
1312     if (outBuf)
1313     {
1314       *outBufWasWritten = true;
1315       *outBufWasWrittenSize = (UInt32)destLen;
1316     }
1317     else
1318       _dynOutStream->UpdateSize(destLen);
1319   }
1320   return S_OK;
1321 }
1322 
1323 HRESULT CHandler::ReadMetadataBlock(UInt32 &packSize)
1324 {
1325   Byte temp[3];
1326   const unsigned offset = _h.NeedCheckData() ? 3 : 2;
1327   if (offset > packSize)
1328     return S_FALSE;
1329   RINOK(ReadStream_FALSE(_stream, temp, offset))
1330   // if (NeedCheckData && Major < 4) checkByte must be = 0xFF
1331   const bool be = _h.be;
1332   UInt32 size = Get16(temp);
1333   const bool isCompressed = ((size & kNotCompressedBit16) == 0);
1334   if (size != kNotCompressedBit16)
1335     size &= ~kNotCompressedBit16;
1336 
1337   if (size > kMetadataBlockSize || offset + size > packSize)
1338     return S_FALSE;
1339   packSize = offset + size;
1340   if (isCompressed)
1341   {
1342     _limitedInStream->Init(size);
1343     RINOK(Decompress(_dynOutStream, NULL, NULL, NULL, size, kMetadataBlockSize))
1344   }
1345   else
1346   {
1347     // size != 0 here
1348     Byte *buf = _dynOutStream->GetBufPtrForWriting(size);
1349     if (!buf)
1350       return E_OUTOFMEMORY;
1351     RINOK(ReadStream_FALSE(_stream, buf, size))
1352     _dynOutStream->UpdateSize(size);
1353   }
1354   return S_OK;
1355 }
1356 
1357 
1358 HRESULT CHandler::ReadMetadataBlock2()
1359 {
1360   _dynOutStream->Init();
1361   UInt32 packSize = kMetadataBlockSize + 3; // check it
1362   return ReadMetadataBlock(packSize);
1363 }
1364 
1365 HRESULT CHandler::ReadData(CData &data, UInt64 start, UInt64 end)
1366 {
1367   if (end < start || end - start >= ((UInt64)1 << 32))
1368     return S_FALSE;
1369   const UInt32 size = (UInt32)(end - start);
1370   RINOK(Seek2(start))
1371   _dynOutStream->Init();
1372   UInt32 packPos = 0;
1373   while (packPos != size)
1374   {
1375     data.PackPos.Add(packPos);
1376     data.UnpackPos.Add((UInt32)_dynOutStream->GetSize());
1377     if (packPos > size)
1378       return S_FALSE;
1379     UInt32 packSize = size - packPos;
1380     RINOK(ReadMetadataBlock(packSize))
1381     {
1382       const size_t tSize = _dynOutStream->GetSize();
1383       if (tSize != (UInt32)tSize)
1384         return S_FALSE;
1385     }
1386     packPos += packSize;
1387   }
1388   data.UnpackPos.Add((UInt32)_dynOutStream->GetSize());
1389   _dynOutStream->CopyToBuffer(data.Data);
1390   return S_OK;
1391 }
1392 
1393 struct CTempItem
1394 {
1395   UInt32 StartBlock;
1396   // UInt32 iNodeNumber1;
1397   UInt32 Offset;
1398   // UInt16 iNodeNumber2;
1399   UInt16 Type;
1400 };
1401 
1402 HRESULT CHandler::OpenDir(int parent, UInt32 startBlock, UInt32 offset, unsigned level, int &nodeIndex)
1403 {
1404   if (level > kNumDirLevelsMax)
1405     return S_FALSE;
1406 
1407   int blockIndex = _inodesData.PackPos.FindInSorted(startBlock);
1408   if (blockIndex < 0)
1409     return S_FALSE;
1410   UInt32 unpackPos = _inodesData.UnpackPos[blockIndex] + offset;
1411   if (unpackPos < offset)
1412     return S_FALSE;
1413 
1414   nodeIndex = _nodesPos.FindInSorted(unpackPos, _blockToNode[blockIndex], _blockToNode[blockIndex + 1]);
1415   // nodeIndex = _nodesPos.FindInSorted(unpackPos);
1416   if (nodeIndex < 0)
1417     return S_FALSE;
1418 
1419   const CNode &n = _nodes[nodeIndex];
1420   if (!n.IsDir())
1421     return S_OK;
1422   blockIndex = _dirs.PackPos.FindInSorted((UInt32)n.StartBlock);
1423   if (blockIndex < 0)
1424     return S_FALSE;
1425   unpackPos = _dirs.UnpackPos[blockIndex] + n.Offset;
1426   if (unpackPos < n.Offset || unpackPos > _dirs.Data.Size())
1427     return S_FALSE;
1428 
1429   UInt32 rem = (UInt32)_dirs.Data.Size() - unpackPos;
1430   const Byte *p = _dirs.Data + unpackPos;
1431   UInt32 fileSize = (UInt32)n.FileSize;
1432 
1433   // for some squashfs files: fileSize = rem + 3  !!!
1434   if (_h.Major >= 3)
1435   {
1436     if (fileSize < 3)
1437       return S_FALSE;
1438     fileSize -= 3;
1439   }
1440   if (fileSize > rem)
1441     return S_FALSE;
1442   rem = fileSize;
1443 
1444   AString tempString;
1445 
1446   CRecordVector<CTempItem> tempItems;
1447   while (rem != 0)
1448   {
1449     const bool be = _h.be;
1450     UInt32 count;
1451     CTempItem tempItem;
1452     if (_h.Major <= 2)
1453     {
1454       if (rem < 4)
1455         return S_FALSE;
1456       count = p[0];
1457       tempItem.StartBlock = Get32(p);
1458       if (be)
1459         tempItem.StartBlock &= 0xFFFFFF;
1460       else
1461         tempItem.StartBlock >>= 8;
1462       p += 4;
1463       rem -= 4;
1464     }
1465     else
1466     {
1467       if (_h.Major == 3)
1468       {
1469         if (rem < 9)
1470           return S_FALSE;
1471         count = p[0];
1472         p += 1;
1473         rem -= 1;
1474       }
1475       else
1476       {
1477         if (rem < 12)
1478           return S_FALSE;
1479         count = GetUi32(p);
1480         p += 4;
1481         rem -= 4;
1482       }
1483       GET_32 (0, tempItem.StartBlock);
1484       // GET_32 (4, tempItem.iNodeNumber1);
1485       p += 8;
1486       rem -= 8;
1487     }
1488     count++;
1489 
1490     for (UInt32 i = 0; i < count; i++)
1491     {
1492       if (rem == 0)
1493         return S_FALSE;
1494 
1495       UInt32 nameOffset = _h.GetFileNameOffset();
1496       if (rem < nameOffset)
1497         return S_FALSE;
1498 
1499       if (_items.Size() >= kNumFilesMax)
1500         return S_FALSE;
1501       if (_openCallback)
1502       {
1503         UInt64 numFiles = _items.Size();
1504         if ((numFiles & 0xFFFF) == 0)
1505         {
1506           RINOK(_openCallback->SetCompleted(&numFiles, NULL))
1507         }
1508       }
1509 
1510       CItem item;
1511       item.Ptr = (UInt32)(p - (const Byte *)_dirs.Data);
1512 
1513       UInt32 size;
1514       if (_h.IsOldVersion())
1515       {
1516         UInt32 t = Get16(p);
1517         if (be)
1518         {
1519           tempItem.Offset = t >> 3;
1520           tempItem.Type = (UInt16)(t & 0x7);
1521         }
1522         else
1523         {
1524           tempItem.Offset = t & 0x1FFF;
1525           tempItem.Type = (UInt16)(t >> 13);
1526         }
1527         size = (UInt32)p[2];
1528         /*
1529         if (_h.Major > 2)
1530           tempItem.iNodeNumber2 = Get16(p + 3);
1531         */
1532       }
1533       else
1534       {
1535         GET_16 (0, tempItem.Offset);
1536         // GET_16 (2, tempItem.iNodeNumber2);
1537         GET_16 (4, tempItem.Type);
1538         GET_16 (6, size);
1539       }
1540       p += nameOffset;
1541       rem -= nameOffset;
1542       size++;
1543       if (rem < size)
1544         return S_FALSE;
1545 
1546       if (_openCodePage == CP_UTF8)
1547       {
1548         tempString.SetFrom_CalcLen((const char *)p, size);
1549         if (!CheckUTF8_AString(tempString))
1550           _openCodePage = CP_OEMCP;
1551       }
1552 
1553       p += size;
1554       rem -= size;
1555       item.Parent = parent;
1556       _items.Add(item);
1557       tempItems.Add(tempItem);
1558     }
1559   }
1560 
1561   const unsigned startItemIndex = _items.Size() - tempItems.Size();
1562   FOR_VECTOR (i, tempItems)
1563   {
1564     const CTempItem &tempItem = tempItems[i];
1565     const unsigned index = startItemIndex + i;
1566     CItem &item = _items[index];
1567     RINOK(OpenDir((int)index, tempItem.StartBlock, tempItem.Offset, level + 1, item.Node))
1568   }
1569 
1570   return S_OK;
1571 }
1572 
1573 HRESULT CHandler::ReadUids(UInt64 start, UInt32 num, CByteBuffer &ids)
1574 {
1575   const size_t size = (size_t)num * 4;
1576   ids.Alloc(size);
1577   if (num == 0)
1578     return S_OK;
1579   RINOK(Seek2(start))
1580   return ReadStream_FALSE(_stream, ids, size);
1581 }
1582 
1583 HRESULT CHandler::Open2(IInStream *inStream)
1584 {
1585   {
1586     Byte buf[kHeaderSize3];
1587     RINOK(ReadStream_FALSE(inStream, buf, kHeaderSize3))
1588     if (!_h.Parse(buf))
1589       return S_FALSE;
1590     if (!_h.IsSupported())
1591       return E_NOTIMPL;
1592 
1593     _noPropsLZMA = false;
1594     _needCheckLzma = false;
1595     switch (_h.Method)
1596     {
1597       case kMethod_ZLIB: _needCheckLzma = true; break;
1598       case kMethod_LZMA:
1599       case kMethod_LZO:
1600       case kMethod_XZ:
1601       case kMethod_ZSTD:
1602         break;
1603       default:
1604         return E_NOTIMPL;
1605     }
1606   }
1607 
1608   _stream = inStream;
1609 
1610   if (_h.NumFrags != 0)
1611   {
1612     if (_h.NumFrags > kNumFilesMax)
1613       return S_FALSE;
1614     _frags.ClearAndReserve(_h.NumFrags);
1615     const unsigned bigFrag = (_h.Major > 2);
1616 
1617     const unsigned fragPtrsInBlockLog = kMetadataBlockSizeLog - (3 + bigFrag);
1618     const UInt32 numBlocks = (_h.NumFrags + (1 << fragPtrsInBlockLog) - 1) >> fragPtrsInBlockLog;
1619     const size_t numBlocksBytes = (size_t)numBlocks << (2 + bigFrag);
1620     CByteBuffer data(numBlocksBytes);
1621     RINOK(Seek2(_h.FragTable))
1622     RINOK(ReadStream_FALSE(inStream, data, numBlocksBytes))
1623     const bool be = _h.be;
1624 
1625     for (UInt32 i = 0; i < numBlocks; i++)
1626     {
1627       const UInt64 offset = bigFrag ? Get64(data + i * 8) : Get32(data + i * 4);
1628       RINOK(Seek2(offset))
1629       RINOK(ReadMetadataBlock2())
1630       const UInt32 unpackSize = (UInt32)_dynOutStream->GetSize();
1631       if (unpackSize != kMetadataBlockSize)
1632         if (i != numBlocks - 1 || unpackSize != ((_h.NumFrags << (3 + bigFrag)) & (kMetadataBlockSize - 1)))
1633           return S_FALSE;
1634       const Byte *buf = _dynOutStream->GetBuffer();
1635       for (UInt32 j = 0; j < kMetadataBlockSize && j < unpackSize;)
1636       {
1637         CFrag frag;
1638         if (bigFrag)
1639         {
1640           frag.StartBlock = Get64(buf + j);
1641           frag.Size = Get32(buf + j + 8);
1642           // some archives contain nonzero in unused (buf + j + 12)
1643           j += 16;
1644         }
1645         else
1646         {
1647           frag.StartBlock = Get32(buf + j);
1648           frag.Size = Get32(buf + j + 4);
1649           j += 8;
1650         }
1651         _frags.Add(frag);
1652       }
1653     }
1654     if ((UInt32)_frags.Size() != _h.NumFrags)
1655       return S_FALSE;
1656   }
1657 
1658   RINOK(ReadData(_inodesData, _h.InodeTable, _h.DirTable))
1659   RINOK(ReadData(_dirs, _h.DirTable, _h.FragTable))
1660 
1661   UInt64 absOffset = _h.RootInode >> 16;
1662   if (absOffset >= ((UInt64)1 << 32))
1663     return S_FALSE;
1664   {
1665     UInt32 pos = 0;
1666     UInt32 totalSize = (UInt32)_inodesData.Data.Size();
1667     const unsigned kMinNodeParseSize = 4;
1668     if (_h.NumInodes > totalSize / kMinNodeParseSize)
1669       return S_FALSE;
1670     _nodesPos.ClearAndReserve(_h.NumInodes);
1671     _nodes.ClearAndReserve(_h.NumInodes);
1672     // we use _blockToNode for binary search seed optimizations
1673     _blockToNode.ClearAndReserve(_inodesData.GetNumBlocks() + 1);
1674     unsigned curBlock = 0;
1675     for (UInt32 i = 0; i < _h.NumInodes; i++)
1676     {
1677       CNode n;
1678       const Byte *p = _inodesData.Data + pos;
1679       UInt32 size = totalSize - pos;
1680 
1681       switch (_h.Major)
1682       {
1683         case 1:  size = n.Parse1(p, size, _h); break;
1684         case 2:  size = n.Parse2(p, size, _h); break;
1685         case 3:  size = n.Parse3(p, size, _h); break;
1686         default: size = n.Parse4(p, size, _h); break;
1687       }
1688       if (size == 0)
1689         return S_FALSE;
1690       while (pos >= _inodesData.UnpackPos[curBlock])
1691       {
1692         _blockToNode.Add(_nodesPos.Size());
1693         curBlock++;
1694       }
1695       _nodesPos.AddInReserved(pos);
1696       _nodes.AddInReserved(n);
1697       pos += size;
1698     }
1699     _blockToNode.Add(_nodesPos.Size());
1700     if (pos != totalSize)
1701       return S_FALSE;
1702   }
1703   int rootNodeIndex;
1704   RINOK(OpenDir(-1, (UInt32)absOffset, (UInt32)_h.RootInode & 0xFFFF, 0, rootNodeIndex))
1705 
1706   if (_h.Major < 4)
1707   {
1708     RINOK(ReadUids(_h.UidTable, _h.NumUids, _uids))
1709     RINOK(ReadUids(_h.GidTable, _h.NumGids, _gids))
1710   }
1711   else
1712   {
1713     const UInt32 size = (UInt32)_h.NumIDs * 4;
1714     _uids.Alloc(size);
1715 
1716     const UInt32 numBlocks = (size + kMetadataBlockSize - 1) / kMetadataBlockSize;
1717     const UInt32 numBlocksBytes = numBlocks << 3;
1718     CByteBuffer data(numBlocksBytes);
1719     RINOK(Seek2(_h.UidTable))
1720     RINOK(ReadStream_FALSE(inStream, data, numBlocksBytes))
1721 
1722     for (UInt32 i = 0; i < numBlocks; i++)
1723     {
1724       const UInt64 offset = GetUi64(data + i * 8);
1725       RINOK(Seek2(offset))
1726       // RINOK(ReadMetadataBlock(NULL, _uids + kMetadataBlockSize * i, packSize, unpackSize));
1727       RINOK(ReadMetadataBlock2())
1728       const size_t unpackSize = _dynOutStream->GetSize();
1729       const UInt32 remSize = (i == numBlocks - 1)  ?
1730           (size & (kMetadataBlockSize - 1)) : kMetadataBlockSize;
1731       if (unpackSize != remSize)
1732         return S_FALSE;
1733       memcpy(_uids + kMetadataBlockSize * i, _dynOutStream->GetBuffer(), remSize);
1734     }
1735   }
1736 
1737   {
1738     const UInt32 alignSize = 1 << 12;
1739     Byte buf[alignSize];
1740     RINOK(Seek2(_h.Size))
1741     UInt32 rem = (UInt32)(0 - _h.Size) & (alignSize - 1);
1742     _sizeCalculated = _h.Size;
1743     if (rem != 0)
1744     {
1745       if (ReadStream_FALSE(_stream, buf, rem) == S_OK)
1746       {
1747         size_t i;
1748         for (i = 0; i < rem && buf[i] == 0; i++);
1749         if (i == rem)
1750           _sizeCalculated = _h.Size + rem;
1751       }
1752     }
1753   }
1754   return S_OK;
1755 }
1756 
1757 AString CHandler::GetPath(unsigned index) const
1758 {
1759   unsigned len = 0;
1760   const unsigned indexMem = index;
1761   const bool be = _h.be;
1762   for (;;)
1763   {
1764     const CItem &item = _items[index];
1765     const Byte *p = _dirs.Data + item.Ptr;
1766     const unsigned size = (_h.IsOldVersion() ? (unsigned)p[2] : (unsigned)Get16(p + 6)) + 1;
1767     p += _h.GetFileNameOffset();
1768     unsigned i;
1769     for (i = 0; i < size && p[i]; i++);
1770     len += i + 1;
1771     index = (unsigned)item.Parent;
1772     if (item.Parent < 0)
1773       break;
1774   }
1775   len--;
1776 
1777   AString path;
1778   char *dest = path.GetBuf_SetEnd(len) + len;
1779   index = indexMem;
1780   for (;;)
1781   {
1782     const CItem &item = _items[index];
1783     const Byte *p = _dirs.Data + item.Ptr;
1784     const unsigned size = (_h.IsOldVersion() ? (unsigned)p[2] : (unsigned)Get16(p + 6)) + 1;
1785     p += _h.GetFileNameOffset();
1786     unsigned i;
1787     for (i = 0; i < size && p[i]; i++);
1788     dest -= i;
1789     memcpy(dest, p, i);
1790     index = (unsigned)item.Parent;
1791     if (item.Parent < 0)
1792       break;
1793     *(--dest) = CHAR_PATH_SEPARATOR;
1794   }
1795   return path;
1796 }
1797 
1798 Z7_COM7F_IMF(CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback))
1799 {
1800   COM_TRY_BEGIN
1801   {
1802     Close();
1803     _limitedInStream->SetStream(stream);
1804     HRESULT res;
1805     try
1806     {
1807       _openCallback = callback;
1808       res = Open2(stream);
1809     }
1810     catch(...)
1811     {
1812       Close();
1813       throw;
1814     }
1815     if (res != S_OK)
1816     {
1817       Close();
1818       return res;
1819     }
1820     _stream = stream;
1821   }
1822   return S_OK;
1823   COM_TRY_END
1824 }
1825 
1826 Z7_COM7F_IMF(CHandler::Close())
1827 {
1828   _openCodePage = CP_UTF8;
1829   _sizeCalculated = 0;
1830 
1831   _limitedInStream->ReleaseStream();
1832   _stream.Release();
1833 
1834   _items.Clear();
1835   _nodes.Clear();
1836   _nodesPos.Clear();
1837   _blockToNode.Clear();
1838   _frags.Clear();
1839   _inodesData.Clear();
1840   _dirs.Clear();
1841 
1842   _uids.Free();
1843   _gids.Free();
1844 
1845   _cachedBlock.Free();
1846   ClearCache();
1847 
1848   return S_OK;
1849 }
1850 
1851 bool CHandler::GetPackSize(unsigned index, UInt64 &totalPack, bool fillOffsets)
1852 {
1853   totalPack = 0;
1854   const CItem &item = _items[index];
1855   const CNode &node = _nodes[item.Node];
1856   const UInt32 ptr = _nodesPos[item.Node];
1857   const Byte *p = _inodesData.Data + ptr;
1858   const bool be = _h.be;
1859 
1860   const UInt32 type = node.Type;
1861   UInt32 offset;
1862   if (node.IsLink() || node.FileSize == 0)
1863   {
1864     totalPack = node.FileSize;
1865     return true;
1866   }
1867 
1868   const UInt32 numBlocks = (UInt32)node.GetNumBlocks(_h);
1869 
1870   if (fillOffsets)
1871   {
1872     _blockOffsets.Clear();
1873     _blockCompressed.Clear();
1874     _blockOffsets.Add(totalPack);
1875   }
1876 
1877   if (_h.Major <= 1)
1878   {
1879     offset = 15;
1880     p += offset;
1881 
1882     for (UInt32 i = 0; i < numBlocks; i++)
1883     {
1884       UInt32 t = Get16(p + i * 2);
1885       if (fillOffsets)
1886         _blockCompressed.Add((t & kNotCompressedBit16) == 0);
1887       if (t != kNotCompressedBit16)
1888         t &= ~kNotCompressedBit16;
1889       totalPack += t;
1890       if (fillOffsets)
1891         _blockOffsets.Add(totalPack);
1892     }
1893   }
1894   else
1895   {
1896     if (_h.Major <= 2)
1897       offset = 24;
1898     else if (type == kType_FILE)
1899       offset = 32;
1900     else if (type == kType_FILE + 7)
1901       offset = (_h.Major <= 3 ? 40 : 56);
1902     else
1903       return false;
1904 
1905     p += offset;
1906 
1907     for (UInt64 i = 0; i < numBlocks; i++)
1908     {
1909       UInt32 t = Get32(p + i * 4);
1910       if (fillOffsets)
1911         _blockCompressed.Add(IS_COMPRESSED_BLOCK(t));
1912       UInt32 size = GET_COMPRESSED_BLOCK_SIZE(t);
1913       if (size > _h.BlockSize)
1914         return false;
1915       totalPack += size;
1916       if (fillOffsets)
1917         _blockOffsets.Add(totalPack);
1918     }
1919 
1920     if (node.ThereAreFrags())
1921     {
1922       if (node.Frag >= (UInt32)_frags.Size())
1923         return false;
1924       const CFrag &frag = _frags[node.Frag];
1925       if (node.Offset == 0)
1926       {
1927         UInt32 size = GET_COMPRESSED_BLOCK_SIZE(frag.Size);
1928         if (size > _h.BlockSize)
1929           return false;
1930         totalPack += size;
1931       }
1932     }
1933   }
1934   return true;
1935 }
1936 
1937 
1938 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
1939 {
1940   *numItems = _items.Size();
1941   return S_OK;
1942 }
1943 
1944 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
1945 {
1946   COM_TRY_BEGIN
1947   NWindows::NCOM::CPropVariant prop;
1948   switch (propID)
1949   {
1950     case kpidMethod:
1951     {
1952       char sz[16];
1953       const char *s;
1954       if (_noPropsLZMA)
1955         s = "LZMA Spec";
1956       else if (_h.SeveralMethods)
1957         s = "LZMA ZLIB";
1958       else
1959       {
1960         s = NULL;
1961         if (_h.Method < Z7_ARRAY_SIZE(k_Methods))
1962           s = k_Methods[_h.Method];
1963         if (!s)
1964         {
1965           ConvertUInt32ToString(_h.Method, sz);
1966           s = sz;
1967         }
1968       }
1969       prop = s;
1970       break;
1971     }
1972     case kpidFileSystem:
1973     {
1974       AString res ("SquashFS");
1975       if (_h.SeveralMethods)
1976         res += "-LZMA";
1977       res.Add_Space();
1978       res.Add_UInt32(_h.Major);
1979       res.Add_Dot();
1980       res.Add_UInt32(_h.Minor);
1981       prop = res;
1982       break;
1983     }
1984     case kpidClusterSize: prop = _h.BlockSize; break;
1985     case kpidBigEndian: prop = _h.be; break;
1986     case kpidCTime:
1987       if (_h.CTime != 0)
1988         PropVariant_SetFrom_UnixTime(prop, _h.CTime);
1989       break;
1990     case kpidCharacts: FLAGS_TO_PROP(k_Flags, _h.Flags, prop); break;
1991     // case kpidNumBlocks: prop = _h.NumFrags; break;
1992     case kpidPhySize: prop = _sizeCalculated; break;
1993     case kpidHeadersSize:
1994       if (_sizeCalculated >= _h.InodeTable)
1995         prop = _sizeCalculated - _h.InodeTable;
1996       break;
1997 
1998     case kpidCodePage:
1999     {
2000       char sz[16];
2001       const char *name = NULL;
2002       switch (_openCodePage)
2003       {
2004         case CP_OEMCP: name = "OEM"; break;
2005         case CP_UTF8: name = "UTF-8"; break;
2006       }
2007       if (!name)
2008       {
2009         ConvertUInt32ToString(_openCodePage, sz);
2010         name = sz;
2011       }
2012       prop = name;
2013       break;
2014     }
2015   }
2016   prop.Detach(value);
2017   return S_OK;
2018   COM_TRY_END
2019 }
2020 
2021 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
2022 {
2023   COM_TRY_BEGIN
2024   NWindows::NCOM::CPropVariant prop;
2025   const CItem &item = _items[index];
2026   const CNode &node = _nodes[item.Node];
2027   const bool isDir = node.IsDir();
2028   const bool be = _h.be;
2029 
2030   switch (propID)
2031   {
2032     case kpidPath:
2033     {
2034       AString path (GetPath(index));
2035       UString s;
2036       if (_openCodePage == CP_UTF8)
2037         ConvertUTF8ToUnicode(path, s);
2038       else
2039         MultiByteToUnicodeString2(s, path, _openCodePage);
2040       prop = s;
2041       break;
2042     }
2043     case kpidIsDir: prop = isDir; break;
2044     // case kpidOffset: if (!node.IsLink()) prop = (UInt64)node.StartBlock; break;
2045     case kpidSize: if (!isDir) prop = node.GetSize(); break;
2046     case kpidPackSize:
2047       if (!isDir)
2048       {
2049         UInt64 size;
2050         if (GetPackSize(index, size, false))
2051           prop = size;
2052       }
2053       break;
2054     case kpidMTime:
2055     {
2056       UInt32 offset = 0;
2057       switch (_h.Major)
2058       {
2059         case 1:
2060           if (node.Type == kType_FILE)
2061             offset = 3;
2062           else if (node.Type == kType_DIR)
2063             offset = 7;
2064           break;
2065         case 2:
2066           if (node.Type == kType_FILE)
2067             offset = 4;
2068           else if (node.Type == kType_DIR)
2069             offset = 8;
2070           else if (node.Type == kType_DIR + 7)
2071             offset = 9;
2072           break;
2073         case 3: offset = 4; break;
2074         case 4: offset = 8; break;
2075       }
2076       if (offset != 0)
2077       {
2078         const Byte *p = _inodesData.Data + _nodesPos[item.Node] + offset;
2079         PropVariant_SetFrom_UnixTime(prop, Get32(p));
2080       }
2081       break;
2082     }
2083     case kpidPosixAttrib:
2084     {
2085       if (node.Type != 0 && node.Type < Z7_ARRAY_SIZE(k_TypeToMode))
2086         prop = (UInt32)(node.Mode & 0xFFF) | k_TypeToMode[node.Type];
2087       break;
2088     }
2089     case kpidUserId:
2090     case kpidGroupId:
2091     {
2092       UInt32 id = node.Uid;
2093       const CByteBuffer *ids = &_uids;
2094       if (propID == kpidGroupId)
2095       {
2096         id = node.Gid;
2097         if (_h.Major < 4)
2098         {
2099           if (id == _h.GetSpecGuidIndex())
2100             id = node.Uid;
2101           else
2102             ids = &_gids;
2103         }
2104       }
2105       const UInt32 offset = (UInt32)id * 4;
2106       if (offset < ids->Size())
2107         prop = (UInt32)Get32(*ids + offset);
2108       break;
2109     }
2110     /*
2111     case kpidLinks:
2112       if (_h.Major >= 3 && node.Type != kType_FILE)
2113         prop = node.NumLinks;
2114       break;
2115     */
2116   }
2117   prop.Detach(value);
2118   return S_OK;
2119   COM_TRY_END
2120 }
2121 
2122 class CSquashfsInStream: public CCachedInStream
2123 {
2124   HRESULT ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize) Z7_override;
2125 public:
2126   CHandler *Handler;
2127 };
2128 
2129 HRESULT CSquashfsInStream::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize)
2130 {
2131   return Handler->ReadBlock(blockIndex, dest, blockSize);
2132 }
2133 
2134 HRESULT CHandler::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize)
2135 {
2136   const CNode &node = _nodes[_nodeIndex];
2137   UInt64 blockOffset;
2138   UInt32 packBlockSize;
2139   UInt32 offsetInBlock = 0;
2140   bool compressed;
2141   if (blockIndex < _blockCompressed.Size())
2142   {
2143     compressed = _blockCompressed[(unsigned)blockIndex];
2144     blockOffset = _blockOffsets[(unsigned)blockIndex];
2145     packBlockSize = (UInt32)(_blockOffsets[(unsigned)blockIndex + 1] - blockOffset);
2146     blockOffset += node.StartBlock;
2147   }
2148   else
2149   {
2150     if (!node.ThereAreFrags())
2151       return S_FALSE;
2152     const CFrag &frag = _frags[node.Frag];
2153     offsetInBlock = node.Offset;
2154     blockOffset = frag.StartBlock;
2155     packBlockSize = GET_COMPRESSED_BLOCK_SIZE(frag.Size);
2156     compressed = IS_COMPRESSED_BLOCK(frag.Size);
2157   }
2158 
2159   if (packBlockSize == 0)
2160   {
2161     // sparse file ???
2162     memset(dest, 0, blockSize);
2163     return S_OK;
2164   }
2165 
2166   if (blockOffset != _cachedBlockStartPos ||
2167       packBlockSize != _cachedPackBlockSize)
2168   {
2169     ClearCache();
2170     RINOK(Seek2(blockOffset))
2171     _limitedInStream->Init(packBlockSize);
2172 
2173     if (compressed)
2174     {
2175       _outStream->Init((Byte *)_cachedBlock, _h.BlockSize);
2176       bool outBufWasWritten;
2177       UInt32 outBufWasWrittenSize;
2178       HRESULT res = Decompress(_outStream, _cachedBlock, &outBufWasWritten, &outBufWasWrittenSize, packBlockSize, _h.BlockSize);
2179       RINOK(res)
2180       if (outBufWasWritten)
2181         _cachedUnpackBlockSize = outBufWasWrittenSize;
2182       else
2183         _cachedUnpackBlockSize = (UInt32)_outStream->GetPos();
2184     }
2185     else
2186     {
2187       if (packBlockSize > _h.BlockSize)
2188         return S_FALSE;
2189       RINOK(ReadStream_FALSE(_limitedInStream, _cachedBlock, packBlockSize))
2190       _cachedUnpackBlockSize = packBlockSize;
2191     }
2192     _cachedBlockStartPos = blockOffset;
2193     _cachedPackBlockSize = packBlockSize;
2194   }
2195   if (offsetInBlock + blockSize > _cachedUnpackBlockSize)
2196     return S_FALSE;
2197   if (blockSize != 0)
2198     memcpy(dest, _cachedBlock + offsetInBlock, blockSize);
2199   return S_OK;
2200 }
2201 
2202 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
2203     Int32 testMode, IArchiveExtractCallback *extractCallback))
2204 {
2205   COM_TRY_BEGIN
2206   const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
2207   if (allFilesMode)
2208     numItems = _items.Size();
2209   if (numItems == 0)
2210     return S_OK;
2211   UInt64 totalSize = 0;
2212   UInt32 i;
2213   for (i = 0; i < numItems; i++)
2214   {
2215     const CItem &item = _items[allFilesMode ? i : indices[i]];
2216     const CNode &node = _nodes[item.Node];
2217     totalSize += node.GetSize();
2218   }
2219   RINOK(extractCallback->SetTotal(totalSize))
2220 
2221   UInt64 totalPackSize;
2222   totalSize = totalPackSize = 0;
2223 
2224   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
2225   lps->Init(extractCallback, false);
2226   CMyComPtr2_Create<ICompressCoder, NCompress::CCopyCoder> copyCoder;
2227 
2228   for (i = 0;; i++)
2229   {
2230     lps->InSize = totalPackSize;
2231     lps->OutSize = totalSize;
2232     RINOK(lps->SetCur())
2233     if (i >= numItems)
2234       break;
2235 
2236     int res;
2237    {
2238     CMyComPtr<ISequentialOutStream> outStream;
2239     const Int32 askMode = testMode ?
2240         NExtract::NAskMode::kTest :
2241         NExtract::NAskMode::kExtract;
2242     const UInt32 index = allFilesMode ? i : indices[i];
2243     const CItem &item = _items[index];
2244     const CNode &node = _nodes[item.Node];
2245     RINOK(extractCallback->GetStream(index, &outStream, askMode))
2246     // const Byte *p = _data + item.Offset;
2247 
2248     if (node.IsDir())
2249     {
2250       RINOK(extractCallback->PrepareOperation(askMode))
2251       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
2252       continue;
2253     }
2254     const UInt64 unpackSize = node.GetSize();
2255     totalSize += unpackSize;
2256     UInt64 packSize;
2257     if (GetPackSize(index, packSize, false))
2258       totalPackSize += packSize;
2259 
2260     if (!testMode && !outStream)
2261       continue;
2262     RINOK(extractCallback->PrepareOperation(askMode))
2263 
2264     res = NExtract::NOperationResult::kDataError;
2265     {
2266       CMyComPtr<ISequentialInStream> inSeqStream;
2267       HRESULT hres = GetStream(index, &inSeqStream);
2268       if (hres == S_FALSE || !inSeqStream)
2269       {
2270         if (hres == E_OUTOFMEMORY)
2271           return hres;
2272         res = NExtract::NOperationResult::kUnsupportedMethod;
2273       }
2274       else
2275       {
2276         RINOK(hres)
2277         {
2278           hres = copyCoder.Interface()->Code(inSeqStream, outStream, NULL, NULL, lps);
2279           if (hres == S_OK)
2280           {
2281             if (copyCoder->TotalSize == unpackSize)
2282               res = NExtract::NOperationResult::kOK;
2283           }
2284           else if (hres == E_NOTIMPL)
2285           {
2286             res = NExtract::NOperationResult::kUnsupportedMethod;
2287           }
2288           else if (hres != S_FALSE)
2289           {
2290             RINOK(hres)
2291           }
2292         }
2293       }
2294     }
2295    }
2296     RINOK(extractCallback->SetOperationResult(res))
2297   }
2298 
2299   return S_OK;
2300   COM_TRY_END
2301 }
2302 
2303 
2304 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
2305 {
2306   COM_TRY_BEGIN
2307 
2308   const CItem &item = _items[index];
2309   const CNode &node = _nodes[item.Node];
2310 
2311   if (node.IsDir())
2312     return E_FAIL;
2313 
2314   const Byte *p = _inodesData.Data + _nodesPos[item.Node];
2315 
2316   if (node.FileSize == 0 || node.IsLink())
2317   {
2318     CBufInStream *streamSpec = new CBufInStream;
2319     CMyComPtr<IInStream> streamTemp = streamSpec;
2320     if (node.IsLink())
2321       streamSpec->Init(p + _h.GetSymLinkOffset(), (size_t)node.FileSize);
2322     else
2323       streamSpec->Init(NULL, 0);
2324     *stream = streamTemp.Detach();
2325     return S_OK;
2326   }
2327 
2328   UInt64 packSize;
2329   if (!GetPackSize(index, packSize, true))
2330     return S_FALSE;
2331 
2332   _nodeIndex = item.Node;
2333 
2334   size_t cacheSize = _h.BlockSize;
2335   if (_cachedBlock.Size() != cacheSize)
2336   {
2337     ClearCache();
2338     _cachedBlock.Alloc(cacheSize);
2339   }
2340 
2341   CSquashfsInStream *streamSpec = new CSquashfsInStream;
2342   CMyComPtr<IInStream> streamTemp = streamSpec;
2343   streamSpec->Handler = this;
2344   unsigned cacheSizeLog = 22;
2345   if (cacheSizeLog <= _h.BlockSizeLog)
2346     cacheSizeLog = _h.BlockSizeLog + 1;
2347   if (!streamSpec->Alloc(_h.BlockSizeLog, cacheSizeLog - _h.BlockSizeLog))
2348     return E_OUTOFMEMORY;
2349   streamSpec->Init(node.FileSize);
2350   *stream = streamTemp.Detach();
2351 
2352   return S_OK;
2353 
2354   COM_TRY_END
2355 }
2356 
2357 static const Byte k_Signature[] = {
2358     4, 'h', 's', 'q', 's',
2359     4, 's', 'q', 's', 'h',
2360     4, 's', 'h', 's', 'q',
2361     4, 'q', 's', 'h', 's' };
2362 
2363 REGISTER_ARC_I(
2364   "SquashFS", "squashfs", NULL, 0xD2,
2365   k_Signature,
2366   0,
2367   NArcInfoFlags::kMultiSignature,
2368   NULL)
2369 
2370 }}
2371