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