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