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