1 // ExtHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 // #define SHOW_DEBUG_INFO
6 
7 // #include <stdio.h>
8 // #define PRF2(x) x
9 
10 #define PRF2(x)
11 
12 #ifdef SHOW_DEBUG_INFO
13 #include <stdio.h>
14 #define PRF(x) x
15 #else
16 #define PRF(x)
17 #endif
18 
19 #include "../../../C/Alloc.h"
20 #include "../../../C/CpuArch.h"
21 
22 #include "../../Common/ComTry.h"
23 #include "../../Common/MyLinux.h"
24 #include "../../Common/StringConvert.h"
25 #include "../../Common/UTFConvert.h"
26 
27 #include "../../Windows/PropVariantUtils.h"
28 #include "../../Windows/TimeUtils.h"
29 
30 #include "../Common/ProgressUtils.h"
31 #include "../Common/RegisterArc.h"
32 #include "../Common/StreamObjects.h"
33 #include "../Common/StreamUtils.h"
34 
35 #include "../Compress/CopyCoder.h"
36 
37 using namespace NWindows;
38 
39 UInt32 LzhCrc16Update(UInt32 crc, const void *data, size_t size);
40 
41 namespace NArchive {
42 namespace NExt {
43 
44 #define Get16(p) GetUi16(p)
45 #define Get32(p) GetUi32(p)
46 #define Get64(p) GetUi64(p)
47 
48 #define LE_16(offs, dest) dest = Get16(p + (offs));
49 #define LE_32(offs, dest) dest = Get32(p + (offs));
50 #define LE_64(offs, dest) dest = Get64(p + (offs));
51 
52 #define HI_16(offs, dest) dest |= (((UInt32)Get16(p + (offs))) << 16);
53 #define HI_32(offs, dest) dest |= (((UInt64)Get32(p + (offs))) << 32);
54 
55 /*
56 static UInt32 g_Crc32CTable[256];
57 
58 static struct CInitCrc32C
59 {
60   CInitCrc32C()
61   {
62     for (unsigned i = 0; i < 256; i++)
63     {
64       UInt32 r = i;
65       unsigned j;
66       for (j = 0; j < 8; j++)
67         r = (r >> 1) ^ (0x82F63B78 & ~((r & 1) - 1));
68       g_Crc32CTable[i] = r;
69     }
70   }
71 } g_InitCrc32C;
72 
73 #define CRC32C_INIT_VAL 0xFFFFFFFF
74 #define CRC32C_GET_DIGEST(crc) ((crc) ^ CRC_INIT_VAL)
75 #define CRC32C_UPDATE_BYTE(crc, b) (g_Crc32CTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8))
76 
77 static UInt32 Crc32C_Update(UInt32 crc, Byte const *data, size_t size)
78 {
79   for (size_t i = 0; i < size; i++)
80     crc = CRC32C_UPDATE_BYTE(crc, data[i]);
81   return crc;
82 }
83 
84 static UInt32 Crc32C_Calc(Byte const *data, size_t size)
85 {
86   return Crc32C_Update(CRC32C_INIT_VAL, data, size);
87 }
88 */
89 
90 
91 #define CRC16_INIT_VAL 0xFFFF
92 
93 #define Crc16Update(crc, data, size)  LzhCrc16Update(crc, data, size)
94 
Crc16Calc(Byte const * data,size_t size)95 static UInt32 Crc16Calc(Byte const *data, size_t size)
96 {
97   return Crc16Update(CRC16_INIT_VAL, data, size);
98 }
99 
100 
101 #define EXT4_GOOD_OLD_INODE_SIZE 128
102 #define EXT_NODE_SIZE_MIN 128
103 
104 
105 // inodes numbers
106 
107 // #define k_INODE_BAD          1  // Bad blocks
108 #define k_INODE_ROOT         2  // Root dir
109 // #define k_INODE_USR_QUOTA    3  // User quota
110 // #define k_INODE_GRP_QUOTA    4  // Group quota
111 // #define k_INODE_BOOT_LOADER  5  // Boot loader
112 // #define k_INODE_UNDEL_DIR    6  // Undelete dir
113 #define k_INODE_RESIZE       7  // Reserved group descriptors
114 // #define k_INODE_JOURNAL      8  // Journal
115 
116 // First non-reserved inode for old ext4 filesystems
117 #define k_INODE_GOOD_OLD_FIRST  11
118 
119 static const char * const k_SysInode_Names[] =
120 {
121     "0"
122   , "Bad"
123   , "Root"
124   , "UserQuota"
125   , "GroupQuota"
126   , "BootLoader"
127   , "Undelete"
128   , "Resize"
129   , "Journal"
130   , "Exclude"
131   , "Replica"
132 };
133 
134 static const char * const kHostOS[] =
135 {
136     "Linux"
137   , "Hurd"
138   , "Masix"
139   , "FreeBSD"
140   , "Lites"
141 };
142 
143 static const char * const g_FeatureCompat_Flags[] =
144 {
145     "DIR_PREALLOC"
146   , "IMAGIC_INODES"
147   , "HAS_JOURNAL"
148   , "EXT_ATTR"
149   , "RESIZE_INODE"
150   , "DIR_INDEX"
151   , "LAZY_BG" // not in Linux
152   , NULL // { 7, "EXCLUDE_INODE" // not used
153   , NULL // { 8, "EXCLUDE_BITMAP" // not in kernel
154   , "SPARSE_SUPER2"
155 };
156 
157 
158 #define EXT4_FEATURE_INCOMPAT_FILETYPE (1 << 1)
159 #define EXT4_FEATURE_INCOMPAT_64BIT (1 << 7)
160 
161 static const char * const g_FeatureIncompat_Flags[] =
162 {
163     "COMPRESSION"
164   , "FILETYPE"
165   , "RECOVER" /* Needs recovery */
166   , "JOURNAL_DEV" /* Journal device */
167   , "META_BG"
168   , NULL
169   , "EXTENTS" /* extents support */
170   , "64BIT"
171   , "MMP"
172   , "FLEX_BG"
173   , "EA_INODE" /* EA in inode */
174   , NULL
175   , "DIRDATA" /* data in dirent */
176   , "BG_USE_META_CSUM" /* use crc32c for bg */
177   , "LARGEDIR" /* >2GB or 3-lvl htree */
178   , "INLINE_DATA" /* data in inode */
179   , "ENCRYPT" // 16
180 };
181 
182 
183 static const UInt32 RO_COMPAT_GDT_CSUM = 1 << 4;
184 static const UInt32 RO_COMPAT_METADATA_CSUM = 1 << 10;
185 
186 static const char * const g_FeatureRoCompat_Flags[] =
187 {
188     "SPARSE_SUPER"
189   , "LARGE_FILE"
190   , "BTREE_DIR"
191   , "HUGE_FILE"
192   , "GDT_CSUM"
193   , "DIR_NLINK"
194   , "EXTRA_ISIZE"
195   , "HAS_SNAPSHOT"
196   , "QUOTA"
197   , "BIGALLOC"
198   , "METADATA_CSUM"
199   , "REPLICA"
200   , "READONLY" // 12
201 };
202 
203 
204 
205 static const UInt32 k_NodeFlags_HUGE = (UInt32)1 << 18;
206 static const UInt32 k_NodeFlags_EXTENTS = (UInt32)1 << 19;
207 
208 
209 static const char * const g_NodeFlags[] =
210 {
211     "SECRM"
212   , "UNRM"
213   , "COMPR"
214   , "SYNC"
215   , "IMMUTABLE"
216   , "APPEND"
217   , "NODUMP"
218   , "NOATIME"
219   , "DIRTY"
220   , "COMPRBLK"
221   , "NOCOMPR"
222   , "ENCRYPT"
223   , "INDEX"
224   , "IMAGIC"
225   , "JOURNAL_DATA"
226   , "NOTAIL"
227   , "DIRSYNC"
228   , "TOPDIR"
229   , "HUGE_FILE"
230   , "EXTENTS"
231   , NULL
232   , "EA_INODE"
233   , "EOFBLOCKS"
234   , NULL
235   , NULL
236   , NULL
237   , NULL
238   , NULL
239   , "INLINE_DATA" // 28
240 };
241 
242 
GetHex(unsigned t)243 static inline char GetHex(unsigned t) { return (char)(((t < 10) ? ('0' + t) : ('A' + (t - 10)))); }
244 
PrintHex(unsigned v,char * s)245 static inline void PrintHex(unsigned v, char *s)
246 {
247   s[0] = GetHex((v >> 4) & 0xF);
248   s[1] = GetHex(v & 0xF);
249 }
250 
251 
252 enum
253 {
254   k_Type_UNKNOWN,
255   k_Type_REG_FILE,
256   k_Type_DIR,
257   k_Type_CHRDEV,
258   k_Type_BLKDEV,
259   k_Type_FIFO,
260   k_Type_SOCK,
261   k_Type_SYMLINK
262 };
263 
264 static const UInt16 k_TypeToMode[] =
265 {
266   0,
267   MY_LIN_S_IFREG,
268   MY_LIN_S_IFDIR,
269   MY_LIN_S_IFCHR,
270   MY_LIN_S_IFBLK,
271   MY_LIN_S_IFIFO,
272   MY_LIN_S_IFSOCK,
273   MY_LIN_S_IFLNK
274 };
275 
276 
277 #define EXT4_GOOD_OLD_REV 0  // old (original) format
278 // #define EXT4_DYNAMIC_REV 1  // V2 format with dynamic inode sizes
279 
280 struct CHeader
281 {
282   unsigned BlockBits;
283   unsigned ClusterBits;
284 
285   UInt32 NumInodes;
286   UInt64 NumBlocks;
287   // UInt64 NumBlocksSuper;
288   UInt64 NumFreeBlocks;
289   UInt32 NumFreeInodes;
290   // UInt32 FirstDataBlock;
291 
292   UInt32 BlocksPerGroup;
293   UInt32 ClustersPerGroup;
294   UInt32 InodesPerGroup;
295 
296   UInt32 MountTime;
297   UInt32 WriteTime;
298 
299   // UInt16 NumMounts;
300   // UInt16 NumMountsMax;
301 
302   // UInt16 State;
303   // UInt16 Errors;
304   // UInt16 MinorRevLevel;
305 
306   UInt32 LastCheckTime;
307   // UInt32 CheckInterval;
308   UInt32 CreatorOs;
309   UInt32 RevLevel;
310 
311   // UInt16 DefResUid;
312   // UInt16 DefResGid;
313 
314   UInt32 FirstInode;
315   UInt16 InodeSize;
316   UInt16 BlockGroupNr;
317   UInt32 FeatureCompat;
318   UInt32 FeatureIncompat;
319   UInt32 FeatureRoCompat;
320   Byte Uuid[16];
321   char VolName[16];
322   char LastMount[64];
323   // UInt32 BitmapAlgo;
324 
325   UInt32 JournalInode;
326   UInt16 GdSize; // = 64 if 64-bit();
327   UInt32 CTime;
328   UInt16 MinExtraISize;
329   // UInt16 WantExtraISize;
330   // UInt32 Flags;
331   // Byte LogGroupsPerFlex;
332   // Byte ChecksumType;
333 
334   UInt64 WrittenKB;
335 
IsOldRevNArchive::NExt::CHeader336   bool IsOldRev() const { return RevLevel == EXT4_GOOD_OLD_REV; }
337 
GetNumGroupsNArchive::NExt::CHeader338   UInt64 GetNumGroups() const { return (NumBlocks + BlocksPerGroup - 1) / BlocksPerGroup; }
GetNumGroups2NArchive::NExt::CHeader339   UInt64 GetNumGroups2() const { return ((UInt64)NumInodes + InodesPerGroup - 1) / InodesPerGroup; }
340 
IsThereFileTypeNArchive::NExt::CHeader341   bool IsThereFileType() const { return (FeatureIncompat & EXT4_FEATURE_INCOMPAT_FILETYPE) != 0; }
Is64BitNArchive::NExt::CHeader342   bool Is64Bit() const { return (FeatureIncompat & EXT4_FEATURE_INCOMPAT_64BIT) != 0; }
UseGdtChecksumNArchive::NExt::CHeader343   bool UseGdtChecksum() const { return (FeatureRoCompat & RO_COMPAT_GDT_CSUM) != 0; }
UseMetadataChecksumNArchive::NExt::CHeader344   bool UseMetadataChecksum() const { return (FeatureRoCompat & RO_COMPAT_METADATA_CSUM) != 0; }
345 
346   bool Parse(const Byte *p);
347 };
348 
349 
GetLog(UInt32 num)350 static int inline GetLog(UInt32 num)
351 {
352   for (unsigned i = 0; i < 32; i++)
353     if (((UInt32)1 << i) == num)
354       return i;
355   return -1;
356 }
357 
IsEmptyData(const Byte * data,unsigned size)358 static bool inline IsEmptyData(const Byte *data, unsigned size)
359 {
360   for (unsigned i = 0; i < size; i++)
361     if (data[i] != 0)
362       return false;
363   return true;
364 }
365 
366 
Parse(const Byte * p)367 bool CHeader::Parse(const Byte *p)
368 {
369   if (GetUi16(p + 0x38) != 0xEF53)
370     return false;
371 
372   LE_32 (0x18, BlockBits);
373   LE_32 (0x1C, ClusterBits);
374 
375   if (ClusterBits != 0 && BlockBits != ClusterBits)
376     return false;
377 
378   if (BlockBits > 16 - 10)
379     return false;
380   BlockBits += 10;
381 
382   LE_32 (0x00, NumInodes);
383   LE_32 (0x04, NumBlocks);
384   // LE_32 (0x08, NumBlocksSuper);
385   LE_32 (0x0C, NumFreeBlocks);
386   LE_32 (0x10, NumFreeInodes);
387 
388   if (NumInodes < 2 || NumInodes <= NumFreeInodes)
389     return false;
390 
391   UInt32 FirstDataBlock;
392   LE_32 (0x14, FirstDataBlock);
393   if (FirstDataBlock != (unsigned)(BlockBits == 10 ? 1 : 0))
394     return false;
395 
396   LE_32 (0x20, BlocksPerGroup);
397   LE_32 (0x24, ClustersPerGroup);
398 
399   if (BlocksPerGroup != ClustersPerGroup)
400     return false;
401   if (BlocksPerGroup == 0)
402     return false;
403   if (BlocksPerGroup != ((UInt32)1 << (BlockBits + 3)))
404   {
405     // it's allowed in ext2
406     // return false;
407   }
408 
409   LE_32 (0x28, InodesPerGroup);
410 
411   if (InodesPerGroup < 1 || InodesPerGroup > NumInodes)
412     return false;
413 
414   LE_32 (0x2C, MountTime);
415   LE_32 (0x30, WriteTime);
416 
417   // LE_16 (0x34, NumMounts);
418   // LE_16 (0x36, NumMountsMax);
419 
420   // LE_16 (0x3A, State);
421   // LE_16 (0x3C, Errors);
422   // LE_16 (0x3E, MinorRevLevel);
423 
424   LE_32 (0x40, LastCheckTime);
425   // LE_32 (0x44, CheckInterval);
426   LE_32 (0x48, CreatorOs);
427   LE_32 (0x4C, RevLevel);
428 
429   // LE_16 (0x50, DefResUid);
430   // LE_16 (0x52, DefResGid);
431 
432   FirstInode = k_INODE_GOOD_OLD_FIRST;
433   InodeSize = EXT4_GOOD_OLD_INODE_SIZE;
434 
435   if (!IsOldRev())
436   {
437     LE_32 (0x54, FirstInode);
438     LE_16 (0x58, InodeSize);
439     if (FirstInode < k_INODE_GOOD_OLD_FIRST)
440       return false;
441     if (InodeSize > ((UInt32)1 << BlockBits)
442         || InodeSize < EXT_NODE_SIZE_MIN
443         || GetLog(InodeSize) < 0)
444       return false;
445   }
446 
447   LE_16 (0x5A, BlockGroupNr);
448   LE_32 (0x5C, FeatureCompat);
449   LE_32 (0x60, FeatureIncompat);
450   LE_32 (0x64, FeatureRoCompat);
451 
452   memcpy(Uuid, p + 0x68, sizeof(Uuid));
453   memcpy(VolName, p + 0x78, sizeof(VolName));
454   memcpy(LastMount, p + 0x88, sizeof(LastMount));
455 
456   // LE_32 (0xC8, BitmapAlgo);
457 
458   LE_32 (0xE0, JournalInode);
459 
460   LE_16 (0xFE, GdSize);
461 
462   LE_32 (0x108, CTime);
463 
464   if (Is64Bit())
465   {
466     HI_32(0x150, NumBlocks);
467     // HI_32(0x154, NumBlocksSuper);
468     HI_32(0x158, NumFreeBlocks);
469   }
470 
471   if (NumBlocks >= (UInt64)1 << (63 - BlockBits))
472     return false;
473 
474 
475   LE_16(0x15C, MinExtraISize);
476   // LE_16(0x15E, WantExtraISize);
477   // LE_32(0x160, Flags);
478   // LE_16(0x164, RaidStride);
479   // LE_16(0x166, MmpInterval);
480   // LE_64(0x168, MmpBlock);
481 
482   // LogGroupsPerFlex = p[0x174];
483   // ChecksumType = p[0x175];
484 
485   LE_64 (0x178, WrittenKB);
486 
487   // LE_32(0x194, ErrorCount);
488   // LE_32(0x198, ErrorTime);
489   // LE_32(0x19C, ErrorINode);
490   // LE_32(0x1A0, ErrorBlock);
491 
492   if (NumBlocks < 1)
493     return false;
494   if (NumFreeBlocks > NumBlocks)
495     return false;
496 
497   if (GetNumGroups() != GetNumGroups2())
498     return false;
499 
500   return true;
501 }
502 
503 
504 struct CGroupDescriptor
505 {
506   UInt64 BlockBitmap;
507   UInt64 InodeBitmap;
508   UInt64 InodeTable;
509   UInt32 NumFreeBlocks;
510   UInt32 NumFreeInodes;
511   UInt32 DirCount;
512 
513   UInt16 Flags;
514 
515   UInt64 ExcludeBitmap;
516   UInt32 BlockBitmap_Checksum;
517   UInt32 InodeBitmap_Checksum;
518   UInt32 UnusedCount;
519   UInt16 Checksum;
520 
521   void Parse(const Byte *p, unsigned size);
522 };
523 
Parse(const Byte * p,unsigned size)524 void CGroupDescriptor::Parse(const Byte *p, unsigned size)
525 {
526   LE_32 (0x00, BlockBitmap);
527   LE_32 (0x04, InodeBitmap);
528   LE_32 (0x08, InodeTable);
529   LE_16 (0x0C, NumFreeBlocks);
530   LE_16 (0x0E, NumFreeInodes);
531   LE_16 (0x10, DirCount);
532   LE_16 (0x12, Flags);
533   LE_32 (0x14, ExcludeBitmap);
534   LE_16 (0x18, BlockBitmap_Checksum);
535   LE_16 (0x1A, InodeBitmap_Checksum);
536   LE_16 (0x1C, UnusedCount);
537   LE_16 (0x1E, Checksum);
538 
539   if (size >= 64)
540   {
541     p += 0x20;
542     HI_32 (0x00, BlockBitmap);
543     HI_32 (0x04, InodeBitmap);
544     HI_32 (0x08, InodeTable);
545     HI_16 (0x0C, NumFreeBlocks);
546     HI_16 (0x0E, NumFreeInodes);
547     HI_16 (0x10, DirCount);
548     HI_16 (0x12, UnusedCount); // instead of Flags
549     HI_32 (0x14, ExcludeBitmap);
550     HI_16 (0x18, BlockBitmap_Checksum);
551     HI_16 (0x1A, InodeBitmap_Checksum);
552     // HI_16 (0x1C, Reserved);
553   }
554 }
555 
556 
557 static const unsigned kNodeBlockFieldSize = 60;
558 
559 struct CExtentTreeHeader
560 {
561   UInt16 NumEntries;
562   UInt16 MaxEntries;
563   UInt16 Depth;
564   // UInt32 Generation;
565 
ParseNArchive::NExt::CExtentTreeHeader566   bool Parse(const Byte *p)
567   {
568     LE_16 (0x02, NumEntries);
569     LE_16 (0x04, MaxEntries);
570     LE_16 (0x06, Depth);
571     // LE_32 (0x08, Generation);
572     return Get16(p) == 0xF30A; // magic
573   }
574 };
575 
576 struct CExtentIndexNode
577 {
578   UInt32 VirtBlock;
579   UInt64 PhyLeaf;
580 
ParseNArchive::NExt::CExtentIndexNode581   void Parse(const Byte *p)
582   {
583     LE_32 (0x00, VirtBlock);
584     LE_32 (0x04, PhyLeaf);
585     PhyLeaf |= (((UInt64)Get16(p + 8)) << 32);
586     // unused 16-bit field (at offset 0x0A) can be not zero in some cases. Why?
587   }
588 };
589 
590 struct CExtent
591 {
592   UInt32 VirtBlock;
593   UInt16 Len;
594   bool IsInited;
595   UInt64 PhyStart;
596 
GetVirtEndNArchive::NExt::CExtent597   UInt32 GetVirtEnd() const { return VirtBlock + Len; }
IsLenOKNArchive::NExt::CExtent598   bool IsLenOK() const { return VirtBlock + Len >= VirtBlock; }
599 
ParseNArchive::NExt::CExtent600   void Parse(const Byte *p)
601   {
602     LE_32 (0x00, VirtBlock);
603     LE_16 (0x04, Len);
604     IsInited = true;
605     if (Len > (UInt32)0x8000)
606     {
607       IsInited = false;
608       Len = (UInt16)(Len - (UInt32)0x8000);
609     }
610     LE_32 (0x08, PhyStart);
611     UInt16 hi;
612     LE_16 (0x06, hi);
613     PhyStart |= ((UInt64)hi << 32);
614   }
615 };
616 
617 
618 
619 struct CExtTime
620 {
621   UInt32 Val;
622   UInt32 Extra;
623 };
624 
625 struct CNode
626 {
627   Int32 ParentNode;   // in _refs[], -1 if not dir
628   int ItemIndex;      // in _items[]
629   int SymLinkIndex;   // in _symLinks[]
630   int DirIndex;       // in _dirs[]
631 
632   UInt16 Mode;
633   UInt32 Uid; // fixed 21.02
634   UInt32 Gid; // fixed 21.02
635   // UInt16 Checksum;
636 
637   UInt64 FileSize;
638   CExtTime MTime;
639   CExtTime ATime;
640   CExtTime CTime;
641   // CExtTime InodeChangeTime;
642   // CExtTime DTime;
643 
644   UInt64 NumBlocks;
645   UInt32 NumLinks;
646   UInt32 Flags;
647 
648   UInt32 NumLinksCalced;
649 
650   Byte Block[kNodeBlockFieldSize];
651 
CNodeNArchive::NExt::CNode652   CNode():
653       ParentNode(-1),
654       ItemIndex(-1),
655       SymLinkIndex(-1),
656       DirIndex(0),
657       NumLinksCalced(0)
658         {}
659 
IsFlags_HUGENArchive::NExt::CNode660   bool IsFlags_HUGE()    const { return (Flags & k_NodeFlags_HUGE) != 0; }
IsFlags_EXTENTSNArchive::NExt::CNode661   bool IsFlags_EXTENTS() const { return (Flags & k_NodeFlags_EXTENTS) != 0; }
662 
IsDirNArchive::NExt::CNode663   bool IsDir()     const { return MY_LIN_S_ISDIR(Mode); }
IsRegularNArchive::NExt::CNode664   bool IsRegular() const { return MY_LIN_S_ISREG(Mode); }
IsLinkNArchive::NExt::CNode665   bool IsLink()    const { return MY_LIN_S_ISLNK(Mode); }
666 
667   bool Parse(const Byte *p, const CHeader &_h);
668 };
669 
670 
Parse(const Byte * p,const CHeader & _h)671 bool CNode::Parse(const Byte *p, const CHeader &_h)
672 {
673   MTime.Extra = 0;
674   ATime.Extra = 0;
675   CTime.Extra = 0;
676   CTime.Val = 0;
677   // InodeChangeTime.Extra = 0;
678   // DTime.Extra = 0;
679 
680   LE_16 (0x00, Mode);
681   LE_16 (0x02, Uid);
682   LE_32 (0x04, FileSize);
683   LE_32 (0x08, ATime.Val);
684   // LE_32 (0x0C, InodeChangeTime.Val);
685   LE_32 (0x10, MTime.Val);
686   // LE_32 (0x14, DTime.Val);
687   LE_16 (0x18, Gid);
688   LE_16 (0x1A, NumLinks);
689 
690   LE_32 (0x1C, NumBlocks);
691   LE_32 (0x20, Flags);
692   // LE_32 (0x24, Union osd1);
693 
694   memcpy(Block, p + 0x28, kNodeBlockFieldSize);
695 
696   // LE_32 (0x64, Generation);  // File version (for NFS)
697   // LE_32 (0x68, ACL);
698 
699   {
700     UInt32 highSize;
701     LE_32 (0x6C, highSize); // In ext2/3 this field was named i_dir_acl
702 
703     if (IsRegular()) // do we need that check ?
704       FileSize |= ((UInt64)highSize << 32);
705   }
706 
707   // UInt32 fragmentAddress;
708   // LE_32 (0x70, fragmentAddress);
709 
710   // osd2
711   {
712     // Linux;
713     // ext2:
714     // Byte FragmentNumber = p[0x74];
715     // Byte FragmentSize = p[0x74 + 1];
716 
717     // ext4:
718     UInt32 numBlocksHigh;
719     LE_16 (0x74, numBlocksHigh);
720     NumBlocks |= (UInt64)numBlocksHigh << 32;
721 
722     HI_16 (0x74 + 4, Uid);
723     HI_16 (0x74 + 6, Gid);
724     /*
725     UInt32 checksum;
726     LE_16 (0x74 + 8, checksum);
727     checksum = checksum;
728     */
729   }
730 
731   // 0x74: Byte Union osd2[12];
732 
733   if (_h.InodeSize > 128)
734   {
735     // InodeSize is power of 2, so the following check is not required:
736     // if (_h.InodeSize < 128 + 2) return false;
737     UInt16 extra_isize;
738     LE_16 (0x80, extra_isize);
739     if (128 + extra_isize > _h.InodeSize)
740       return false;
741     if (extra_isize >= 0x1C)
742     {
743       // UInt16 checksumUpper;
744       // LE_16 (0x82, checksumUpper);
745       // LE_32 (0x84, InodeChangeTime.Extra);
746       LE_32 (0x88, MTime.Extra);
747       LE_32 (0x8C, ATime.Extra);
748       LE_32 (0x90, CTime.Val);
749       LE_32 (0x94, CTime.Extra);
750       // LE_32 (0x98, VersionHi);
751     }
752   }
753 
754   PRF(printf("size = %5d", (unsigned)FileSize));
755 
756   return true;
757 }
758 
759 
760 struct CItem
761 {
762   unsigned Node;        // in _refs[]
763   int ParentNode;       // in _refs[]
764   int SymLinkItemIndex; // in _items[], if the Node contains SymLink to existing dir
765   Byte Type;
766 
767   AString Name;
768 
CItemNArchive::NExt::CItem769   CItem():
770       Node(0),
771       ParentNode(-1),
772       SymLinkItemIndex(-1),
773       Type(k_Type_UNKNOWN)
774         {}
775 
ClearNArchive::NExt::CItem776   void Clear()
777   {
778     Node = 0;
779     ParentNode = -1;
780     SymLinkItemIndex = -1;
781     Type = k_Type_UNKNOWN;
782     Name.Empty();
783   }
784 
IsDirNArchive::NExt::CItem785   bool IsDir() const { return Type == k_Type_DIR; }
786   // bool IsNotDir() const { return Type != k_Type_DIR && Type != k_Type_UNKNOWN; }
787 
788 };
789 
790 
791 
792 static const unsigned kNumTreeLevelsMax = 6; // must be >= 3
793 
794 
795 class CHandler:
796   public IInArchive,
797   public IArchiveGetRawProps,
798   public IInArchiveGetStream,
799   public CMyUnknownImp
800 {
801   CObjectVector<CItem> _items;
802   CIntVector _refs;
803   CRecordVector<CNode> _nodes;
804   CObjectVector<CUIntVector> _dirs; // each CUIntVector contains indexes in _items[] only for dir items;
805   AStringVector _symLinks;
806   AStringVector _auxItems;
807   int _auxSysIndex;
808   int _auxUnknownIndex;
809 
810   CMyComPtr<IInStream> _stream;
811   UInt64 _phySize;
812   bool _isArc;
813   bool _headersError;
814   bool _headersWarning;
815   bool _linksError;
816 
817   bool _isUTF;
818 
819   CHeader _h;
820 
821   IArchiveOpenCallback *_openCallback;
822   UInt64 _totalRead;
823   UInt64 _totalReadPrev;
824 
825   CByteBuffer _tempBufs[kNumTreeLevelsMax];
826 
827 
CheckProgress2()828   HRESULT CheckProgress2()
829   {
830     const UInt64 numFiles = _items.Size();
831     return _openCallback->SetCompleted(&numFiles, &_totalRead);
832   }
833 
CheckProgress()834   HRESULT CheckProgress()
835   {
836     HRESULT res = S_OK;
837     if (_openCallback)
838     {
839       if (_totalRead - _totalReadPrev >= ((UInt32)1 << 20))
840       {
841         _totalReadPrev = _totalRead;
842         res = CheckProgress2();
843       }
844     }
845     return res;
846   }
847 
848 
GetParentAux(const CItem & item) const849   int GetParentAux(const CItem &item) const
850   {
851     if (item.Node < _h.FirstInode && _auxSysIndex >= 0)
852       return _auxSysIndex;
853     return _auxUnknownIndex;
854   }
855 
856   HRESULT SeekAndRead(IInStream *inStream, UInt64 block, Byte *data, size_t size);
857   HRESULT ParseDir(const Byte *data, size_t size, unsigned iNodeDir);
858   int FindTargetItem_for_SymLink(unsigned dirNode, const AString &path) const;
859 
860   HRESULT FillFileBlocks2(UInt32 block, unsigned level, unsigned numBlocks, CRecordVector<UInt32> &blocks);
861   HRESULT FillFileBlocks(const Byte *p, unsigned numBlocks, CRecordVector<UInt32> &blocks);
862   HRESULT FillExtents(const Byte *p, size_t size, CRecordVector<CExtent> &extents, int parentDepth);
863 
864   HRESULT GetStream_Node(unsigned nodeIndex, ISequentialInStream **stream);
865   HRESULT ExtractNode(unsigned nodeIndex, CByteBuffer &data);
866 
867   void ClearRefs();
868   HRESULT Open2(IInStream *inStream);
869 
870   void GetPath(unsigned index, AString &s) const;
871   bool GetPackSize(unsigned index, UInt64 &res) const;
872 
873 public:
CHandler()874   CHandler() {}
~CHandler()875   ~CHandler() {}
876 
877   MY_UNKNOWN_IMP3(IInArchive, IArchiveGetRawProps, IInArchiveGetStream)
878 
879   INTERFACE_IInArchive(;)
880   INTERFACE_IArchiveGetRawProps(;)
881   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
882 };
883 
884 
885 
ParseDir(const Byte * p,size_t size,unsigned iNodeDir)886 HRESULT CHandler::ParseDir(const Byte *p, size_t size, unsigned iNodeDir)
887 {
888   bool isThereSelfLink = false;
889 
890   PRF(printf("\n\n========= node = %5d    size = %5d", (unsigned)iNodeDir, (unsigned)size));
891 
892   CNode &nodeDir = _nodes[_refs[iNodeDir]];
893   nodeDir.DirIndex = _dirs.Size();
894   CUIntVector &dir = _dirs.AddNew();
895   int parentNode = -1;
896 
897   CItem item;
898 
899   for (;;)
900   {
901     if (size == 0)
902       break;
903     if (size < 8)
904       return S_FALSE;
905     UInt32 iNode;
906     LE_32 (0x00, iNode);
907     unsigned recLen;
908     LE_16 (0x04, recLen);
909     unsigned nameLen = p[6];
910     Byte type = p[7];
911 
912     if (recLen > size)
913       return S_FALSE;
914     if (nameLen + 8 > recLen)
915       return S_FALSE;
916 
917     if (iNode >= _refs.Size())
918       return S_FALSE;
919 
920     item.Clear();
921 
922     if (_h.IsThereFileType())
923       item.Type = type;
924     else if (type != 0)
925       return S_FALSE;
926 
927     item.ParentNode = iNodeDir;
928     item.Node = iNode;
929     item.Name.SetFrom_CalcLen((const char *)(p + 8), nameLen);
930 
931     p += recLen;
932     size -= recLen;
933 
934     if (item.Name.Len() != nameLen)
935       return S_FALSE;
936 
937     if (_isUTF)
938     {
939       // 21.07 : we force UTF8
940       // _isUTF = CheckUTF8_AString(item.Name);
941     }
942 
943     if (iNode == 0)
944     {
945       /*
946       ext3 deleted??
947       if (item.Name.Len() != 0)
948         return S_FALSE;
949       */
950 
951       PRF(printf("\n EMPTY %6d %d %s", (unsigned)recLen, (unsigned)type, (const char *)item.Name));
952       if (type == 0xDE)
953       {
954         // checksum
955       }
956       continue;
957     }
958 
959     int nodeIndex = _refs[iNode];
960     if (nodeIndex < 0)
961       return S_FALSE;
962     CNode &node = _nodes[nodeIndex];
963 
964     if (_h.IsThereFileType() && type != 0)
965     {
966       if (type >= ARRAY_SIZE(k_TypeToMode))
967         return S_FALSE;
968       if (k_TypeToMode[type] != (node.Mode & MY_LIN_S_IFMT))
969         return S_FALSE;
970     }
971 
972     node.NumLinksCalced++;
973 
974     PRF(printf("\n%s %6d %s", item.IsDir() ? "DIR  " : "     ", (unsigned)item.Node, (const char *)item.Name));
975 
976     if (item.Name[0] == '.')
977     {
978       if (item.Name[1] == 0)
979       {
980         if (isThereSelfLink)
981           return S_FALSE;
982         isThereSelfLink = true;
983         if (iNode != iNodeDir)
984           return S_FALSE;
985         continue;
986       }
987 
988       if (item.Name[1] == '.' && item.Name[2] == 0)
989       {
990         if (parentNode >= 0)
991           return S_FALSE;
992         if (!node.IsDir())
993           return S_FALSE;
994         if (iNode == iNodeDir && iNode != k_INODE_ROOT)
995           return S_FALSE;
996 
997         parentNode = iNode;
998 
999         if (nodeDir.ParentNode < 0)
1000           nodeDir.ParentNode = iNode;
1001         else if ((unsigned)nodeDir.ParentNode != iNode)
1002           return S_FALSE;
1003 
1004         continue;
1005       }
1006     }
1007 
1008     if (iNode == iNodeDir)
1009       return S_FALSE;
1010 
1011     if (parentNode < 0)
1012       return S_FALSE;
1013 
1014     if (node.IsDir())
1015     {
1016       if (node.ParentNode < 0)
1017         node.ParentNode = iNodeDir;
1018       else if ((unsigned)node.ParentNode != iNodeDir)
1019         return S_FALSE;
1020       const unsigned itemIndex = _items.Size();
1021       dir.Add(itemIndex);
1022       node.ItemIndex = itemIndex;
1023     }
1024 
1025     _items.Add(item);
1026   }
1027 
1028   if (parentNode < 0 || !isThereSelfLink)
1029     return S_FALSE;
1030 
1031   return S_OK;
1032 }
1033 
1034 
FindTargetItem_for_SymLink(unsigned iNode,const AString & path) const1035 int CHandler::FindTargetItem_for_SymLink(unsigned iNode, const AString &path) const
1036 {
1037   unsigned pos = 0;
1038 
1039   if (path.IsEmpty())
1040     return -1;
1041 
1042   if (path[0] == '/')
1043   {
1044     iNode = k_INODE_ROOT;
1045     if (iNode >= _refs.Size())
1046       return -1;
1047     pos = 1;
1048   }
1049 
1050   AString s;
1051 
1052   while (pos != path.Len())
1053   {
1054     const CNode &node = _nodes[_refs[iNode]];
1055     int slash = path.Find('/', pos);
1056 
1057     if (slash < 0)
1058     {
1059       s = path.Ptr(pos);
1060       pos = path.Len();
1061     }
1062     else
1063     {
1064       s.SetFrom(path.Ptr(pos), slash - pos);
1065       pos = slash + 1;
1066     }
1067 
1068     if (s[0] == '.')
1069     {
1070       if (s[1] == 0)
1071         continue;
1072       else if (s[1] == '.' && s[2] == 0)
1073       {
1074         if (node.ParentNode < 0)
1075           return -1;
1076         if (iNode == k_INODE_ROOT)
1077           return -1;
1078         iNode = node.ParentNode;
1079         continue;
1080       }
1081     }
1082 
1083     if (node.DirIndex < 0)
1084       return -1;
1085 
1086     const CUIntVector &dir = _dirs[node.DirIndex];
1087 
1088     for (unsigned i = 0;; i++)
1089     {
1090       if (i >= dir.Size())
1091         return -1;
1092       const CItem &item = _items[dir[i]];
1093       if (item.Name == s)
1094       {
1095         iNode = item.Node;
1096         break;
1097       }
1098     }
1099   }
1100 
1101   return _nodes[_refs[iNode]].ItemIndex;
1102 }
1103 
1104 
SeekAndRead(IInStream * inStream,UInt64 block,Byte * data,size_t size)1105 HRESULT CHandler::SeekAndRead(IInStream *inStream, UInt64 block, Byte *data, size_t size)
1106 {
1107   if (block == 0 || block >= _h.NumBlocks)
1108     return S_FALSE;
1109   if (((size + ((size_t)1 << _h.BlockBits) - 1) >> _h.BlockBits) > _h.NumBlocks - block)
1110     return S_FALSE;
1111   RINOK(inStream->Seek((UInt64)block << _h.BlockBits, STREAM_SEEK_SET, NULL));
1112   _totalRead += size;
1113   return ReadStream_FALSE(inStream, data, size);
1114 }
1115 
1116 
1117 static const unsigned kHeaderSize = 2 * 1024;
1118 static const unsigned kHeaderDataOffset = 1024;
1119 
Open2(IInStream * inStream)1120 HRESULT CHandler::Open2(IInStream *inStream)
1121 {
1122   {
1123     Byte buf[kHeaderSize];
1124     RINOK(ReadStream_FALSE(inStream, buf, kHeaderSize));
1125     if (!_h.Parse(buf + kHeaderDataOffset))
1126       return S_FALSE;
1127     if (_h.BlockGroupNr != 0)
1128       return S_FALSE; // it's just copy of super block
1129   }
1130 
1131   {
1132     // ---------- Read groups and nodes ----------
1133 
1134     unsigned numGroups;
1135     {
1136       UInt64 numGroups64 = _h.GetNumGroups();
1137       if (numGroups64 > (UInt32)1 << 31)
1138         return S_FALSE;
1139       numGroups = (unsigned)numGroups64;
1140     }
1141 
1142     unsigned gdBits = 5;
1143     if (_h.Is64Bit())
1144     {
1145       if (_h.GdSize != 64)
1146         return S_FALSE;
1147       gdBits = 6;
1148     }
1149 
1150     _isArc = true;
1151     _phySize = _h.NumBlocks << _h.BlockBits;
1152 
1153     if (_openCallback)
1154     {
1155       RINOK(_openCallback->SetTotal(NULL, &_phySize));
1156     }
1157 
1158     UInt64 fileSize = 0;
1159     RINOK(inStream->Seek(0, STREAM_SEEK_END, &fileSize));
1160 
1161     CRecordVector<CGroupDescriptor> groups;
1162 
1163     {
1164       // ---------- Read groups ----------
1165 
1166       CByteBuffer gdBuf;
1167       size_t gdBufSize = (size_t)numGroups << gdBits;
1168       if ((gdBufSize >> gdBits) != numGroups)
1169         return S_FALSE;
1170       gdBuf.Alloc(gdBufSize);
1171       RINOK(SeekAndRead(inStream, (_h.BlockBits <= 10 ? 2 : 1), gdBuf, gdBufSize));
1172 
1173       for (unsigned i = 0; i < numGroups; i++)
1174       {
1175         CGroupDescriptor gd;
1176 
1177         const Byte *p = gdBuf + ((size_t)i << gdBits);
1178         unsigned gd_Size = (unsigned)1 << gdBits;
1179         gd.Parse(p, gd_Size);
1180 
1181         if (_h.UseMetadataChecksum())
1182         {
1183           // use CRC32c
1184         }
1185         else if (_h.UseGdtChecksum())
1186         {
1187           UInt32 crc = Crc16Calc(_h.Uuid, sizeof(_h.Uuid));
1188           Byte i_le[4];
1189           SetUi32(i_le, i);
1190           crc = Crc16Update(crc, i_le, 4);
1191           crc = Crc16Update(crc, p, 32 - 2);
1192           if (gd_Size != 32)
1193             crc = Crc16Update(crc, p + 32, gd_Size - 32);
1194           if (crc != gd.Checksum)
1195             return S_FALSE;
1196         }
1197 
1198         groups.Add(gd);
1199       }
1200     }
1201 
1202     {
1203       // ---------- Read nodes ----------
1204 
1205       if (_h.NumInodes < _h.NumFreeInodes)
1206         return S_FALSE;
1207 
1208       UInt32 numNodes = _h.InodesPerGroup;
1209       if (numNodes > _h.NumInodes)
1210         numNodes = _h.NumInodes;
1211       const size_t nodesDataSize = (size_t)numNodes * _h.InodeSize;
1212 
1213       if (nodesDataSize / _h.InodeSize != numNodes)
1214         return S_FALSE;
1215 
1216       // that code to reduce false detecting cases
1217       if (nodesDataSize > fileSize)
1218       {
1219         if (numNodes > (1 << 24))
1220           return S_FALSE;
1221       }
1222 
1223       const UInt32 numReserveInodes = _h.NumInodes - _h.NumFreeInodes + 1;
1224       // numReserveInodes = _h.NumInodes + 1;
1225       if (numReserveInodes != 0)
1226       {
1227         _nodes.Reserve(numReserveInodes);
1228         _refs.Reserve(numReserveInodes);
1229       }
1230 
1231       CByteBuffer nodesData;
1232       nodesData.Alloc(nodesDataSize);
1233 
1234       CByteBuffer nodesMap;
1235       const size_t blockSize = (size_t)1 << _h.BlockBits;
1236       nodesMap.Alloc(blockSize);
1237 
1238       unsigned globalNodeIndex = 0;
1239       // unsigned numEmpty = 0;
1240       unsigned numEmpty_in_Maps = 0;
1241 
1242       FOR_VECTOR (gi, groups)
1243       {
1244         if (globalNodeIndex >= _h.NumInodes)
1245           break;
1246 
1247         const CGroupDescriptor &gd = groups[gi];
1248 
1249         PRF(printf("\n\ng%6d block = %6x\n", gi, (unsigned)gd.InodeTable));
1250 
1251         RINOK(SeekAndRead(inStream, gd.InodeBitmap, nodesMap, blockSize));
1252         RINOK(SeekAndRead(inStream, gd.InodeTable, nodesData, nodesDataSize));
1253 
1254         unsigned numEmpty_in_Map = 0;
1255 
1256         for (size_t n = 0; n < numNodes && globalNodeIndex < _h.NumInodes; n++, globalNodeIndex++)
1257         {
1258           if ((nodesMap[n >> 3] & ((unsigned)1 << (n & 7))) == 0)
1259           {
1260             numEmpty_in_Map++;
1261             continue;
1262           }
1263 
1264           const Byte *p = nodesData + (size_t)n * _h.InodeSize;
1265           if (IsEmptyData(p, _h.InodeSize))
1266           {
1267             if (globalNodeIndex + 1 >= _h.FirstInode)
1268             {
1269               _headersError = true;
1270               // return S_FALSE;
1271             }
1272             continue;
1273           }
1274 
1275           CNode node;
1276 
1277           PRF(printf("\nnode = %5d ", (unsigned)n));
1278 
1279           if (!node.Parse(p, _h))
1280             return S_FALSE;
1281 
1282           // PRF(printf("\n %6d", (unsigned)n));
1283           /*
1284             SetUi32(p + 0x7C, 0)
1285             SetUi32(p + 0x82, 0)
1286 
1287             UInt32 crc = Crc32C_Calc(_h.Uuid, sizeof(_h.Uuid));
1288             Byte i_le[4];
1289             SetUi32(i_le, n);
1290             crc = Crc32C_Update(crc, i_le, 4);
1291             crc = Crc32C_Update(crc, p, _h.InodeSize);
1292             if (crc != node.Checksum) return S_FALSE;
1293           */
1294 
1295           while (_refs.Size() < globalNodeIndex + 1)
1296           {
1297             // numEmpty++;
1298             _refs.Add(-1);
1299           }
1300 
1301           _refs.Add(_nodes.Add(node));
1302         }
1303 
1304 
1305         numEmpty_in_Maps += numEmpty_in_Map;
1306 
1307         if (numEmpty_in_Map != gd.NumFreeInodes)
1308         {
1309           _headersWarning = true;
1310           // return S_FALSE;
1311         }
1312       }
1313 
1314       if (numEmpty_in_Maps != _h.NumFreeInodes)
1315       {
1316         // some ext2 examples has incorrect value in _h.NumFreeInodes.
1317         // so we disable check;
1318         _headersWarning = true;
1319       }
1320 
1321       if (_refs.Size() <= k_INODE_ROOT)
1322         return S_FALSE;
1323 
1324       // printf("\n numReserveInodes = %6d, _refs.Size() = %d, numEmpty = %7d\n", numReserveInodes, _refs.Size(), (unsigned)numEmpty);
1325     }
1326   }
1327 
1328   _stream = inStream; // we need stream for dir nodes
1329 
1330   {
1331     // ---------- Read Dirs ----------
1332 
1333     CByteBuffer dataBuf;
1334 
1335     FOR_VECTOR (i, _refs)
1336     {
1337       int nodeIndex = _refs[i];
1338       {
1339         if (nodeIndex < 0)
1340           continue;
1341         const CNode &node = _nodes[nodeIndex];
1342         if (!node.IsDir())
1343           continue;
1344       }
1345       RINOK(ExtractNode(nodeIndex, dataBuf));
1346       if (dataBuf.Size() == 0)
1347       {
1348         // _headersError = true;
1349         return S_FALSE;
1350       }
1351       else
1352       {
1353         RINOK(ParseDir(dataBuf, dataBuf.Size(), i));
1354       }
1355       RINOK(CheckProgress());
1356     }
1357 
1358     int ref = _refs[k_INODE_ROOT];
1359     if (ref < 0 || _nodes[ref].ParentNode != k_INODE_ROOT)
1360       return S_FALSE;
1361   }
1362 
1363   {
1364     // ---------- Check NumLinks and unreferenced dir nodes ----------
1365 
1366     FOR_VECTOR (i, _refs)
1367     {
1368       int nodeIndex = _refs[i];
1369       if (nodeIndex < 0)
1370         continue;
1371       const CNode &node = _nodes[nodeIndex];
1372 
1373       if (node.NumLinks != node.NumLinksCalced)
1374       {
1375         if (node.NumLinks != 1 || node.NumLinksCalced != 0
1376             // ) && i >= _h.FirstInode
1377             )
1378         {
1379           _linksError = true;
1380           // return S_FALSE;
1381         }
1382       }
1383 
1384       if (!node.IsDir())
1385         continue;
1386 
1387       if (node.ParentNode < 0)
1388       {
1389         if (i >= _h.FirstInode)
1390           return S_FALSE;
1391         continue;
1392       }
1393     }
1394   }
1395 
1396   {
1397     // ---------- Check that there is no loops in parents list ----------
1398 
1399     unsigned numNodes = _refs.Size();
1400     CIntArr UsedByNode(numNodes);
1401     {
1402       {
1403         for (unsigned i = 0; i < numNodes; i++)
1404           UsedByNode[i] = -1;
1405       }
1406     }
1407 
1408     FOR_VECTOR (i, _refs)
1409     {
1410       {
1411         int nodeIndex = _refs[i];
1412         if (nodeIndex < 0)
1413           continue;
1414         const CNode &node = _nodes[nodeIndex];
1415         if (node.ParentNode < 0 // not dir
1416             || i == k_INODE_ROOT)
1417           continue;
1418       }
1419 
1420       unsigned c = i;
1421 
1422       for (;;)
1423       {
1424         int nodeIndex = _refs[c];
1425         if (nodeIndex < 0)
1426           return S_FALSE;
1427         CNode &node = _nodes[nodeIndex];
1428 
1429         if (UsedByNode[c] != -1)
1430         {
1431           if ((unsigned)UsedByNode[c] == i)
1432             return S_FALSE;
1433           break;
1434         }
1435 
1436         UsedByNode[c] = i;
1437         if (node.ParentNode < 0 || node.ParentNode == k_INODE_ROOT)
1438           break;
1439         if ((unsigned)node.ParentNode == i)
1440           return S_FALSE;
1441         c = node.ParentNode;
1442       }
1443     }
1444   }
1445 
1446   {
1447     // ---------- Fill SymLinks data ----------
1448 
1449     AString s;
1450     CByteBuffer data;
1451 
1452     unsigned i;
1453     for (i = 0; i < _refs.Size(); i++)
1454     {
1455       int nodeIndex = _refs[i];
1456       if (nodeIndex < 0)
1457         continue;
1458       CNode &node = _nodes[nodeIndex];
1459       if (!node.IsLink())
1460         continue;
1461       if (node.FileSize > ((UInt32)1 << 14))
1462         continue;
1463       if (ExtractNode(nodeIndex, data) == S_OK && data.Size() != 0)
1464       {
1465         s.SetFrom_CalcLen((const char *)(const Byte *)data, (unsigned)data.Size());
1466         if (s.Len() == data.Size())
1467           node.SymLinkIndex = _symLinks.Add(s);
1468         RINOK(CheckProgress());
1469       }
1470     }
1471 
1472     unsigned prev = 0;
1473     unsigned complex = 0;
1474 
1475     for (i = 0; i < _items.Size(); i++)
1476     {
1477       CItem &item = _items[i];
1478       int sym = _nodes[_refs[item.Node]].SymLinkIndex;
1479       if (sym >= 0 && item.ParentNode >= 0)
1480       {
1481         item.SymLinkItemIndex = FindTargetItem_for_SymLink(item.ParentNode, _symLinks[sym]);
1482         if (_openCallback)
1483         {
1484           complex++;
1485           if (complex - prev >= (1 << 10))
1486           {
1487             RINOK(CheckProgress2());
1488             prev = complex;
1489           }
1490         }
1491       }
1492     }
1493   }
1494 
1495   {
1496     // ---------- Add items and aux folders for unreferenced files ----------
1497 
1498     bool useSys = false;
1499     bool useUnknown = false;
1500 
1501     FOR_VECTOR (i, _refs)
1502     {
1503       int nodeIndex = _refs[i];
1504       if (nodeIndex < 0)
1505         continue;
1506       const CNode &node = _nodes[nodeIndex];
1507 
1508       if (node.NumLinksCalced == 0 /* || i > 100 && i < 150 */) // for debug
1509       {
1510         CItem item;
1511         item.Node = i;
1512 
1513         // we don't know how to work with k_INODE_RESIZE node (strange FileSize and Block values).
1514         // so we ignore it;
1515 
1516         if (i == k_INODE_RESIZE)
1517           continue;
1518 
1519         if (node.FileSize == 0)
1520           continue;
1521 
1522         if (i < _h.FirstInode)
1523         {
1524           if (item.Node < ARRAY_SIZE(k_SysInode_Names))
1525             item.Name = k_SysInode_Names[item.Node];
1526           useSys = true;
1527         }
1528         else
1529           useUnknown = true;
1530 
1531         if (item.Name.IsEmpty())
1532           item.Name.Add_UInt32(item.Node);
1533 
1534         _items.Add(item);
1535       }
1536     }
1537 
1538     if (useSys)
1539       _auxSysIndex = _auxItems.Add((AString)"[SYS]");
1540     if (useUnknown)
1541       _auxUnknownIndex = _auxItems.Add((AString)"[UNKNOWN]");
1542   }
1543 
1544   return S_OK;
1545 }
1546 
1547 
Open(IInStream * stream,const UInt64 *,IArchiveOpenCallback * callback)1548 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback)
1549 {
1550   COM_TRY_BEGIN
1551   {
1552     Close();
1553     HRESULT res;
1554     try
1555     {
1556       _openCallback = callback;
1557       res = Open2(stream);
1558     }
1559     catch(...)
1560     {
1561       ClearRefs();
1562       throw;
1563     }
1564 
1565     if (res != S_OK)
1566     {
1567       ClearRefs();
1568       return res;
1569     }
1570     _stream = stream;
1571   }
1572   return S_OK;
1573   COM_TRY_END
1574 }
1575 
1576 
ClearRefs()1577 void CHandler::ClearRefs()
1578 {
1579   _stream.Release();
1580   _items.Clear();
1581   _nodes.Clear();
1582   _refs.Clear();
1583   _auxItems.Clear();
1584   _symLinks.Clear();
1585   _dirs.Clear();
1586   _auxSysIndex = -1;
1587   _auxUnknownIndex = -1;
1588 }
1589 
1590 
Close()1591 STDMETHODIMP CHandler::Close()
1592 {
1593   _totalRead = 0;
1594   _totalReadPrev = 0;
1595   _phySize = 0;
1596   _isArc = false;
1597   _headersError = false;
1598   _headersWarning = false;
1599   _linksError = false;
1600   _isUTF = true;
1601 
1602   ClearRefs();
1603   return S_OK;
1604 }
1605 
1606 
ChangeSeparatorsInName(char * s,unsigned num)1607 static void ChangeSeparatorsInName(char *s, unsigned num)
1608 {
1609   for (unsigned i = 0; i < num; i++)
1610   {
1611     char c = s[i];
1612     if (c == CHAR_PATH_SEPARATOR || c == '/')
1613       s[i] = '_';
1614   }
1615 }
1616 
1617 
GetPath(unsigned index,AString & s) const1618 void CHandler::GetPath(unsigned index, AString &s) const
1619 {
1620   s.Empty();
1621 
1622   if (index >= _items.Size())
1623   {
1624     s = _auxItems[index - _items.Size()];
1625     return;
1626   }
1627 
1628   for (;;)
1629   {
1630     const CItem &item = _items[index];
1631     if (!s.IsEmpty())
1632       s.InsertAtFront(CHAR_PATH_SEPARATOR);
1633     s.Insert(0, item.Name);
1634     // 18.06
1635     ChangeSeparatorsInName(s.GetBuf(), item.Name.Len());
1636 
1637     if (item.ParentNode == k_INODE_ROOT)
1638       return;
1639 
1640     if (item.ParentNode < 0)
1641     {
1642       int aux = GetParentAux(item);
1643       if (aux < 0)
1644         break;
1645       s.InsertAtFront(CHAR_PATH_SEPARATOR);
1646       s.Insert(0, _auxItems[aux]);
1647       return;
1648     }
1649 
1650     const CNode &node = _nodes[_refs[item.ParentNode]];
1651     if (node.ItemIndex < 0)
1652       return;
1653     index = node.ItemIndex;
1654 
1655     if (s.Len() > ((UInt32)1 << 16))
1656     {
1657       s.Insert(0, "[LONG]" STRING_PATH_SEPARATOR);
1658       return;
1659     }
1660   }
1661 }
1662 
1663 
GetPackSize(unsigned index,UInt64 & totalPack) const1664 bool CHandler::GetPackSize(unsigned index, UInt64 &totalPack) const
1665 {
1666   if (index >= _items.Size())
1667   {
1668     totalPack = 0;
1669     return false;
1670   }
1671 
1672   const CItem &item = _items[index];
1673   const CNode &node = _nodes[_refs[item.Node]];
1674 
1675   // if (!node.IsFlags_EXTENTS())
1676   {
1677     totalPack = (UInt64)node.NumBlocks << (node.IsFlags_HUGE() ? _h.BlockBits : 9);
1678     return true;
1679   }
1680 
1681   /*
1682   CExtentTreeHeader eth;
1683   if (!eth.Parse(node.Block))
1684     return false;
1685   if (eth.NumEntries > 3)
1686     return false;
1687   if (!eth.Depth == 0)
1688     return false;
1689 
1690   UInt64 numBlocks = 0;
1691   {
1692     for (unsigned i = 0; i < eth.NumEntries; i++)
1693     {
1694       CExtent e;
1695       e.Parse(node.Block + 12 + i * 12);
1696       // const CExtent &e = node.leafs[i];
1697       if (e.IsInited)
1698         numBlocks += e.Len;
1699     }
1700   }
1701 
1702   totalPack = numBlocks << _h.BlockBits;
1703   return true;
1704   */
1705 }
1706 
1707 
GetNumberOfItems(UInt32 * numItems)1708 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
1709 {
1710   *numItems = _items.Size() + _auxItems.Size();
1711   return S_OK;
1712 }
1713 
1714 enum
1715 {
1716   kpidMountTime = kpidUserDefined,
1717   kpidLastCheckTime,
1718   kpidRevision,
1719   kpidINodeSize,
1720   kpidLastMount,
1721   kpidFeatureIncompat,
1722   kpidFeatureRoCompat,
1723   kpidWrittenKB
1724 
1725   // kpidGroupSize,
1726 
1727   // kpidChangeTime = kpidUserDefined + 256,
1728   // kpidDTime
1729 };
1730 
1731 static const UInt32 kProps[] =
1732 {
1733   kpidPath,
1734   kpidIsDir,
1735   kpidSize,
1736   kpidPackSize,
1737   kpidPosixAttrib,
1738   kpidMTime,
1739   kpidCTime,
1740   kpidATime,
1741   // kpidChangeTime,
1742   // kpidDTime,
1743   kpidINode,
1744   kpidLinks,
1745   kpidSymLink,
1746   kpidCharacts,
1747   kpidUser,
1748   kpidGroup
1749 };
1750 
1751 
1752 static const CStatProp kArcProps[] =
1753 {
1754   { NULL, kpidHeadersSize, VT_BSTR },
1755   // { NULL, kpidFileSystem, VT_BSTR },
1756   // kpidMethod,
1757   { NULL, kpidClusterSize, VT_UI4 },
1758   // { "Group Size", kpidGroupSize, VT_UI8 },
1759   { NULL, kpidFreeSpace, VT_UI8 },
1760 
1761   { NULL, kpidMTime, VT_FILETIME },
1762   { NULL, kpidCTime, VT_FILETIME },
1763   { "Mount Time", kpidMountTime, VT_FILETIME },
1764   { "Last Check Time", kpidLastCheckTime, VT_FILETIME },
1765 
1766   { NULL, kpidHostOS, VT_BSTR},
1767   { "Revision", kpidRevision, VT_UI4},
1768   { "inode Size", kpidINodeSize, VT_UI4},
1769   { NULL, kpidCodePage, VT_BSTR},
1770   { NULL, kpidVolumeName, VT_BSTR},
1771   { "Last Mounted", kpidLastMount, VT_BSTR},
1772   { NULL, kpidId, VT_BSTR},
1773   { NULL, kpidCharacts, VT_BSTR },
1774   { "Incompatible Features", kpidFeatureIncompat, VT_BSTR },
1775   { "Readonly-compatible Features", kpidFeatureRoCompat, VT_BSTR },
1776   { "Written KiB", kpidWrittenKB, VT_UI8 }
1777 };
1778 
1779 IMP_IInArchive_Props
1780 IMP_IInArchive_ArcProps_WITH_NAME
1781 
StringToProp(bool isUTF,const char * s,unsigned size,NCOM::CPropVariant & prop)1782 static void StringToProp(bool isUTF, const char *s, unsigned size, NCOM::CPropVariant &prop)
1783 {
1784   UString u;
1785   AString a;
1786   a.SetFrom_CalcLen(s, size);
1787   if (!isUTF || !ConvertUTF8ToUnicode(a, u))
1788     MultiByteToUnicodeString2(u, a);
1789   prop = u;
1790 }
1791 
UnixTimeToProp(UInt32 val,NCOM::CPropVariant & prop)1792 static void UnixTimeToProp(UInt32 val, NCOM::CPropVariant &prop)
1793 {
1794   if (val != 0)
1795   {
1796     FILETIME ft;
1797     NTime::UnixTimeToFileTime(val, ft);
1798     prop = ft;
1799   }
1800 }
1801 
GetArchiveProperty(PROPID propID,PROPVARIANT * value)1802 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
1803 {
1804   COM_TRY_BEGIN
1805 
1806   NCOM::CPropVariant prop;
1807 
1808   switch (propID)
1809   {
1810     /*
1811     case kpidFileSystem:
1812     {
1813       AString res = "Ext4";
1814       prop = res;
1815       break;
1816     }
1817     */
1818 
1819     case kpidIsTree: prop = true; break;
1820     case kpidIsAux: prop = true; break;
1821     case kpidINode: prop = true; break;
1822 
1823     case kpidClusterSize: prop = (UInt32)1 << _h.BlockBits; break;
1824     // case kpidGroupSize: prop = (UInt64)_h.BlocksPerGroup << _h.BlockBits; break;
1825 
1826     case kpidFreeSpace: prop = (UInt64)_h.NumFreeBlocks << _h.BlockBits; break;
1827 
1828     case kpidCTime: UnixTimeToProp(_h.CTime, prop); break;
1829     case kpidMTime: UnixTimeToProp(_h.WriteTime, prop); break;
1830     case kpidMountTime: UnixTimeToProp(_h.MountTime, prop); break;
1831     case kpidLastCheckTime: UnixTimeToProp(_h.LastCheckTime, prop); break;
1832 
1833     case kpidHostOS:
1834     {
1835       TYPE_TO_PROP(kHostOS, _h.CreatorOs, prop);
1836       break;
1837     }
1838 
1839     case kpidRevision: prop = _h.RevLevel; break;
1840 
1841     case kpidINodeSize: prop = (UInt32)_h.InodeSize; break;
1842 
1843     case kpidId:
1844     {
1845       if (!IsEmptyData(_h.Uuid, 16))
1846       {
1847         char s[16 * 2 + 2];
1848         for (unsigned i = 0; i < 16; i++)
1849           PrintHex(_h.Uuid[i], s + i * 2);
1850         s[16 * 2] = 0;
1851         prop = s;
1852       }
1853       break;
1854     }
1855 
1856     case kpidCodePage: if (_isUTF) prop = "UTF-8"; break;
1857 
1858     case kpidShortComment:
1859     case kpidVolumeName:
1860         StringToProp(_isUTF, _h.VolName, sizeof(_h.VolName), prop); break;
1861 
1862     case kpidLastMount: StringToProp(_isUTF, _h.LastMount, sizeof(_h.LastMount), prop); break;
1863 
1864     case kpidCharacts: FLAGS_TO_PROP(g_FeatureCompat_Flags, _h.FeatureCompat, prop); break;
1865     case kpidFeatureIncompat: FLAGS_TO_PROP(g_FeatureIncompat_Flags, _h.FeatureIncompat, prop); break;
1866     case kpidFeatureRoCompat: FLAGS_TO_PROP(g_FeatureRoCompat_Flags, _h.FeatureRoCompat, prop); break;
1867     case kpidWrittenKB: if (_h.WrittenKB != 0) prop = _h.WrittenKB; break;
1868 
1869     case kpidPhySize: prop = _phySize; break;
1870 
1871     case kpidErrorFlags:
1872     {
1873       UInt32 v = 0;
1874       if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;;
1875       if (_linksError) v |= kpv_ErrorFlags_HeadersError;
1876       if (_headersError) v |= kpv_ErrorFlags_HeadersError;
1877       if (!_stream && v == 0 && _isArc)
1878         v = kpv_ErrorFlags_HeadersError;
1879       if (v != 0)
1880         prop = v;
1881       break;
1882     }
1883 
1884     case kpidWarningFlags:
1885     {
1886       UInt32 v = 0;
1887       if (_headersWarning) v |= kpv_ErrorFlags_HeadersError;
1888       if (v != 0)
1889         prop = v;
1890       break;
1891     }
1892   }
1893 
1894   prop.Detach(value);
1895   return S_OK;
1896 
1897   COM_TRY_END
1898 }
1899 
1900 
1901 /*
1902 static const Byte kRawProps[] =
1903 {
1904   // kpidSha1,
1905 };
1906 */
1907 
GetNumRawProps(UInt32 * numProps)1908 STDMETHODIMP CHandler::GetNumRawProps(UInt32 *numProps)
1909 {
1910   // *numProps = ARRAY_SIZE(kRawProps);
1911   *numProps = 0;
1912   return S_OK;
1913 }
1914 
GetRawPropInfo(UInt32,BSTR * name,PROPID * propID)1915 STDMETHODIMP CHandler::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID)
1916 {
1917   // *propID = kRawProps[index];
1918   *propID = 0;
1919   *name = 0;
1920   return S_OK;
1921 }
1922 
GetParent(UInt32 index,UInt32 * parent,UInt32 * parentType)1923 STDMETHODIMP CHandler::GetParent(UInt32 index, UInt32 *parent, UInt32 *parentType)
1924 {
1925   *parentType = NParentType::kDir;
1926   *parent = (UInt32)(Int32)-1;
1927 
1928   if (index >= _items.Size())
1929     return S_OK;
1930 
1931   const CItem &item = _items[index];
1932 
1933   if (item.ParentNode < 0)
1934   {
1935     int aux = GetParentAux(item);
1936     if (aux >= 0)
1937       *parent = _items.Size() + aux;
1938   }
1939   else
1940   {
1941     int itemIndex = _nodes[_refs[item.ParentNode]].ItemIndex;
1942     if (itemIndex >= 0)
1943       *parent = itemIndex;
1944   }
1945 
1946   return S_OK;
1947 }
1948 
1949 
GetRawProp(UInt32 index,PROPID propID,const void ** data,UInt32 * dataSize,UInt32 * propType)1950 STDMETHODIMP CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)
1951 {
1952   *data = NULL;
1953   *dataSize = 0;
1954   *propType = 0;
1955 
1956   if (propID == kpidName && _isUTF)
1957   {
1958     if (index < _items.Size())
1959     {
1960       const AString &s = _items[index].Name;
1961       if (!s.IsEmpty())
1962       {
1963         *data = (void *)(const char *)s;
1964         *dataSize = (UInt32)s.Len() + 1;
1965         *propType = NPropDataType::kUtf8z;
1966       }
1967       return S_OK;
1968     }
1969     else
1970     {
1971       const AString &s = _auxItems[index - _items.Size()];
1972       {
1973         *data = (void *)(const char *)s;
1974         *dataSize = (UInt32)s.Len() + 1;
1975         *propType = NPropDataType::kUtf8z;
1976       }
1977       return S_OK;
1978     }
1979   }
1980 
1981   return S_OK;
1982 }
1983 
1984 
ExtTimeToProp(const CExtTime & t,NCOM::CPropVariant & prop)1985 static void ExtTimeToProp(const CExtTime &t, NCOM::CPropVariant &prop)
1986 {
1987   if (t.Val == 0 && t.Extra == 0)
1988     return;
1989 
1990   FILETIME ft;
1991   // if (t.Extra != 0)
1992   {
1993     // 1901-2446 :
1994     Int64 v = (Int64)(Int32)t.Val;
1995     v += (UInt64)(t.Extra & 3) << 32;  // 2 low bits are offset for main timestamp
1996     UInt64 ft64 = NTime::UnixTime64ToFileTime64(v);
1997     const UInt32 ns = (t.Extra >> 2);
1998     if (ns < 1000000000)
1999       ft64 += ns / 100;
2000     ft.dwLowDateTime = (DWORD)ft64;
2001     ft.dwHighDateTime = (DWORD)(ft64 >> 32);
2002   }
2003   /*
2004   else
2005   {
2006     // 1901-2038 : that code is good for ext4 and compatibility with Extra
2007     NTime::UnixTime64ToFileTime((Int32)t.Val, ft); // for
2008 
2009     // 1970-2106 : that code is good if timestamp is used as unsigned 32-bit
2010     // are there such systems?
2011     // NTime::UnixTimeToFileTime(t.Val, ft); // for
2012   }
2013   */
2014   prop = ft;
2015 }
2016 
2017 
GetProperty(UInt32 index,PROPID propID,PROPVARIANT * value)2018 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
2019 {
2020   COM_TRY_BEGIN
2021   NCOM::CPropVariant prop;
2022 
2023   if (index >= _items.Size())
2024   {
2025     switch (propID)
2026     {
2027       case kpidPath:
2028       case kpidName:
2029       {
2030         prop = _auxItems[index - _items.Size()];
2031         break;
2032       }
2033       case kpidIsDir: prop = true; break;
2034       case kpidIsAux: prop = true; break;
2035     }
2036   }
2037   else
2038   {
2039 
2040   const CItem &item = _items[index];
2041   const CNode &node = _nodes[_refs[item.Node]];
2042   bool isDir = node.IsDir();
2043 
2044   switch (propID)
2045   {
2046     case kpidPath:
2047     {
2048       UString u;
2049       {
2050         AString s;
2051         GetPath(index, s);
2052         if (!_isUTF || !ConvertUTF8ToUnicode(s, u))
2053           MultiByteToUnicodeString2(u, s);
2054       }
2055       prop = u;
2056       break;
2057     }
2058 
2059     case kpidName:
2060     {
2061       {
2062         UString u;
2063         {
2064           if (!_isUTF || !ConvertUTF8ToUnicode(item.Name, u))
2065             MultiByteToUnicodeString2(u, item.Name);
2066         }
2067         prop = u;
2068       }
2069       break;
2070     }
2071 
2072     case kpidIsDir:
2073     {
2074       bool isDir2 = isDir;
2075       if (item.SymLinkItemIndex >= 0)
2076         isDir2 = _nodes[_refs[_items[item.SymLinkItemIndex].Node]].IsDir();
2077       prop = isDir2;
2078       break;
2079     }
2080 
2081     case kpidSize: if (!isDir) prop = node.FileSize; break;
2082 
2083     case kpidPackSize:
2084       if (!isDir)
2085       {
2086         UInt64 size;
2087         if (GetPackSize(index, size))
2088           prop = size;
2089       }
2090       break;
2091 
2092     case kpidPosixAttrib:
2093     {
2094       /*
2095       if (node.Type != 0 && node.Type < ARRAY_SIZE(k_TypeToMode))
2096         prop = (UInt32)(node.Mode & 0xFFF) | k_TypeToMode[node.Type];
2097       */
2098       prop = (UInt32)(node.Mode);
2099       break;
2100     }
2101 
2102     case kpidMTime: ExtTimeToProp(node.MTime, prop); break;
2103     case kpidCTime: ExtTimeToProp(node.CTime, prop); break;
2104     case kpidATime: ExtTimeToProp(node.ATime, prop); break;
2105     // case kpidDTime: ExtTimeToProp(node.DTime, prop); break;
2106     // case kpidChangeTime: ExtTimeToProp(node.InodeChangeTime, prop); break;
2107 
2108     case kpidUser: prop = (UInt32)node.Uid; break;
2109     case kpidGroup: prop = (UInt32)node.Gid; break;
2110     case kpidLinks: prop = node.NumLinks; break;
2111     case kpidINode: prop = (UInt32)item.Node; break;
2112     case kpidStreamId: if (!isDir) prop = (UInt32)item.Node; break;
2113     case kpidCharacts: FLAGS_TO_PROP(g_NodeFlags, (UInt32)node.Flags, prop); break;
2114 
2115     case kpidSymLink:
2116     {
2117       if (node.SymLinkIndex >= 0)
2118       {
2119         UString u;
2120         {
2121           const AString &s = _symLinks[node.SymLinkIndex];
2122           if (!_isUTF || !ConvertUTF8ToUnicode(s, u))
2123             MultiByteToUnicodeString2(u, s);
2124         }
2125         prop = u;
2126       }
2127       break;
2128     }
2129   }
2130 
2131   }
2132 
2133   prop.Detach(value);
2134   return S_OK;
2135   COM_TRY_END
2136 }
2137 
2138 
2139 class CClusterInStream2:
2140   public IInStream,
2141   public CMyUnknownImp
2142 {
2143   UInt64 _virtPos;
2144   UInt64 _physPos;
2145   UInt32 _curRem;
2146 public:
2147   unsigned BlockBits;
2148   UInt64 Size;
2149   CMyComPtr<IInStream> Stream;
2150   CRecordVector<UInt32> Vector;
2151 
SeekToPhys()2152   HRESULT SeekToPhys() { return Stream->Seek(_physPos, STREAM_SEEK_SET, NULL); }
2153 
InitAndSeek()2154   HRESULT InitAndSeek()
2155   {
2156     _curRem = 0;
2157     _virtPos = 0;
2158     _physPos = 0;
2159     if (Vector.Size() > 0)
2160     {
2161       _physPos = (Vector[0] << BlockBits);
2162       return SeekToPhys();
2163     }
2164     return S_OK;
2165   }
2166 
2167   MY_UNKNOWN_IMP2(ISequentialInStream, IInStream)
2168 
2169   STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
2170   STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
2171 };
2172 
2173 
Read(void * data,UInt32 size,UInt32 * processedSize)2174 STDMETHODIMP CClusterInStream2::Read(void *data, UInt32 size, UInt32 *processedSize)
2175 {
2176   if (processedSize)
2177     *processedSize = 0;
2178   if (_virtPos >= Size)
2179     return S_OK;
2180   {
2181     UInt64 rem = Size - _virtPos;
2182     if (size > rem)
2183       size = (UInt32)rem;
2184   }
2185   if (size == 0)
2186     return S_OK;
2187 
2188   if (_curRem == 0)
2189   {
2190     const UInt32 blockSize = (UInt32)1 << BlockBits;
2191     const UInt32 virtBlock = (UInt32)(_virtPos >> BlockBits);
2192     const UInt32 offsetInBlock = (UInt32)_virtPos & (blockSize - 1);
2193     const UInt32 phyBlock = Vector[virtBlock];
2194 
2195     if (phyBlock == 0)
2196     {
2197       UInt32 cur = blockSize - offsetInBlock;
2198       if (cur > size)
2199         cur = size;
2200       memset(data, 0, cur);
2201       _virtPos += cur;
2202       if (processedSize)
2203         *processedSize = cur;
2204       return S_OK;
2205     }
2206 
2207     UInt64 newPos = ((UInt64)phyBlock << BlockBits) + offsetInBlock;
2208     if (newPos != _physPos)
2209     {
2210       _physPos = newPos;
2211       RINOK(SeekToPhys());
2212     }
2213 
2214     _curRem = blockSize - offsetInBlock;
2215 
2216     for (unsigned i = 1; i < 64 && (virtBlock + i) < (UInt32)Vector.Size() && phyBlock + i == Vector[virtBlock + i]; i++)
2217       _curRem += (UInt32)1 << BlockBits;
2218   }
2219 
2220   if (size > _curRem)
2221     size = _curRem;
2222   HRESULT res = Stream->Read(data, size, &size);
2223   if (processedSize)
2224     *processedSize = size;
2225   _physPos += size;
2226   _virtPos += size;
2227   _curRem -= size;
2228   return res;
2229 }
2230 
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)2231 STDMETHODIMP CClusterInStream2::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
2232 {
2233   switch (seekOrigin)
2234   {
2235     case STREAM_SEEK_SET: break;
2236     case STREAM_SEEK_CUR: offset += _virtPos; break;
2237     case STREAM_SEEK_END: offset += Size; break;
2238     default: return STG_E_INVALIDFUNCTION;
2239   }
2240   if (offset < 0)
2241     return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
2242   if (_virtPos != (UInt64)offset)
2243     _curRem = 0;
2244   _virtPos = offset;
2245   if (newPosition)
2246     *newPosition = offset;
2247   return S_OK;
2248 }
2249 
2250 
2251 class CExtInStream:
2252   public IInStream,
2253   public CMyUnknownImp
2254 {
2255   UInt64 _virtPos;
2256   UInt64 _phyPos;
2257 public:
2258   unsigned BlockBits;
2259   UInt64 Size;
2260   CMyComPtr<IInStream> Stream;
2261   CRecordVector<CExtent> Extents;
2262 
CExtInStream()2263   CExtInStream() {}
2264 
StartSeek()2265   HRESULT StartSeek()
2266   {
2267     _virtPos = 0;
2268     _phyPos = 0;
2269     return Stream->Seek(_phyPos, STREAM_SEEK_SET, NULL);
2270   }
2271 
2272   MY_UNKNOWN_IMP2(ISequentialInStream, IInStream)
2273   STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
2274   STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
2275 };
2276 
Read(void * data,UInt32 size,UInt32 * processedSize)2277 STDMETHODIMP CExtInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
2278 {
2279   if (processedSize)
2280     *processedSize = 0;
2281   if (_virtPos >= Size)
2282     return S_OK;
2283   {
2284     UInt64 rem = Size - _virtPos;
2285     if (size > rem)
2286       size = (UInt32)rem;
2287   }
2288   if (size == 0)
2289     return S_OK;
2290 
2291   UInt32 blockIndex = (UInt32)(_virtPos >> BlockBits);
2292 
2293   unsigned left = 0, right = Extents.Size();
2294   for (;;)
2295   {
2296     unsigned mid = (left + right) / 2;
2297     if (mid == left)
2298       break;
2299     if (blockIndex < Extents[mid].VirtBlock)
2300       right = mid;
2301     else
2302       left = mid;
2303   }
2304 
2305   {
2306     const CExtent &extent = Extents[left];
2307     if (blockIndex < extent.VirtBlock)
2308       return E_FAIL;
2309     UInt32 bo = blockIndex - extent.VirtBlock;
2310     if (bo >= extent.Len)
2311       return E_FAIL;
2312 
2313     UInt32 offset = ((UInt32)_virtPos & (((UInt32)1 << BlockBits) - 1));
2314     UInt32 remBlocks = extent.Len - bo;
2315     UInt64 remBytes = ((UInt64)remBlocks << BlockBits);
2316     remBytes -= offset;
2317 
2318     if (size > remBytes)
2319       size = (UInt32)remBytes;
2320 
2321     if (!extent.IsInited)
2322     {
2323       memset(data, 0, size);
2324       _virtPos += size;
2325       if (processedSize)
2326         *processedSize = size;
2327       return S_OK;
2328     }
2329 
2330     UInt64 phyBlock = extent.PhyStart + bo;
2331     UInt64 phy = (phyBlock << BlockBits) + offset;
2332 
2333     if (phy != _phyPos)
2334     {
2335       RINOK(Stream->Seek(phy, STREAM_SEEK_SET, NULL));
2336       _phyPos = phy;
2337     }
2338 
2339     UInt32 realProcessSize = 0;
2340 
2341     HRESULT res = Stream->Read(data, size, &realProcessSize);
2342 
2343     _phyPos += realProcessSize;
2344     _virtPos += realProcessSize;
2345     if (processedSize)
2346       *processedSize = realProcessSize;
2347     return res;
2348   }
2349 }
2350 
2351 
Seek(Int64 offset,UInt32 seekOrigin,UInt64 * newPosition)2352 STDMETHODIMP CExtInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
2353 {
2354   switch (seekOrigin)
2355   {
2356     case STREAM_SEEK_SET: break;
2357     case STREAM_SEEK_CUR: offset += _virtPos; break;
2358     case STREAM_SEEK_END: offset += Size; break;
2359     default: return STG_E_INVALIDFUNCTION;
2360   }
2361   if (offset < 0)
2362     return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
2363   _virtPos = offset;
2364   if (newPosition)
2365     *newPosition = offset;
2366   return S_OK;
2367 }
2368 
2369 
2370 
FillFileBlocks2(UInt32 block,unsigned level,unsigned numBlocks,CRecordVector<UInt32> & blocks)2371 HRESULT CHandler::FillFileBlocks2(UInt32 block, unsigned level, unsigned numBlocks, CRecordVector<UInt32> &blocks)
2372 {
2373   const size_t blockSize = (size_t)1 << _h.BlockBits;
2374   CByteBuffer &tempBuf = _tempBufs[level];
2375   tempBuf.Alloc(blockSize);
2376 
2377   PRF2(printf("\n level = %d, block = %7d", level, (unsigned)block));
2378 
2379   RINOK(SeekAndRead(_stream, block, tempBuf, blockSize));
2380 
2381   const Byte *p = tempBuf;
2382   size_t num = (size_t)1 << (_h.BlockBits - 2);
2383 
2384   for (size_t i = 0; i < num; i++)
2385   {
2386     if (blocks.Size() == numBlocks)
2387       break;
2388     UInt32 val = GetUi32(p + 4 * i);
2389     if (val >= _h.NumBlocks)
2390       return S_FALSE;
2391 
2392     if (level != 0)
2393     {
2394       if (val == 0)
2395       {
2396         /*
2397         size_t num = (size_t)1 << ((_h.BlockBits - 2) * (level));
2398         PRF2(printf("\n num empty = %3d", (unsigned)num));
2399         for (size_t k = 0; k < num; k++)
2400         {
2401           blocks.Add(0);
2402           if (blocks.Size() == numBlocks)
2403             return S_OK;
2404         }
2405         continue;
2406         */
2407         return S_FALSE;
2408       }
2409 
2410       RINOK(FillFileBlocks2(val, level - 1, numBlocks, blocks));
2411       continue;
2412     }
2413 
2414     PRF2(printf("\n i = %3d,  blocks.Size() = %6d, block = %5d ", i, blocks.Size(), (unsigned)val));
2415 
2416     PRF(printf("\n i = %3d,  start = %5d ", (unsigned)i, (unsigned)val));
2417 
2418     blocks.Add(val);
2419   }
2420 
2421   return S_OK;
2422 }
2423 
2424 
2425 static const unsigned kNumDirectNodeBlocks = 12;
2426 
FillFileBlocks(const Byte * p,unsigned numBlocks,CRecordVector<UInt32> & blocks)2427 HRESULT CHandler::FillFileBlocks(const Byte *p, unsigned numBlocks, CRecordVector<UInt32> &blocks)
2428 {
2429   // ext2 supports zero blocks (blockIndex == 0).
2430 
2431   blocks.ClearAndReserve(numBlocks);
2432 
2433   for (unsigned i = 0; i < kNumDirectNodeBlocks; i++)
2434   {
2435     if (i == numBlocks)
2436       return S_OK;
2437     UInt32 val = GetUi32(p + 4 * i);
2438     if (val >= _h.NumBlocks)
2439       return S_FALSE;
2440     blocks.Add(val);
2441   }
2442 
2443   for (unsigned level = 0; level < 3; level++)
2444   {
2445     if (blocks.Size() == numBlocks)
2446       break;
2447     UInt32 val = GetUi32(p + 4 * (kNumDirectNodeBlocks + level));
2448     if (val >= _h.NumBlocks)
2449       return S_FALSE;
2450 
2451     if (val == 0)
2452     {
2453       /*
2454       size_t num = (size_t)1 << ((_h.BlockBits - 2) * (level + 1));
2455       for (size_t k = 0; k < num; k++)
2456       {
2457         blocks.Add(0);
2458         if (blocks.Size() == numBlocks)
2459           return S_OK;
2460       }
2461       continue;
2462       */
2463       return S_FALSE;
2464     }
2465 
2466     RINOK(FillFileBlocks2(val, level, numBlocks, blocks));
2467   }
2468 
2469   return S_OK;
2470 }
2471 
2472 
AddSkipExtents(CRecordVector<CExtent> & extents,UInt32 virtBlock,UInt32 numBlocks)2473 static void AddSkipExtents(CRecordVector<CExtent> &extents, UInt32 virtBlock, UInt32 numBlocks)
2474 {
2475   while (numBlocks != 0)
2476   {
2477     UInt32 len = numBlocks;
2478     const UInt32 kLenMax = (UInt32)1 << 15;
2479     if (len > kLenMax)
2480       len = kLenMax;
2481     CExtent e;
2482     e.VirtBlock = virtBlock;
2483     e.Len = (UInt16)len;
2484     e.IsInited = false;
2485     e.PhyStart = 0;
2486     extents.Add(e);
2487     virtBlock += len;
2488     numBlocks -= len;
2489   }
2490 }
2491 
UpdateExtents(CRecordVector<CExtent> & extents,UInt32 block)2492 static bool UpdateExtents(CRecordVector<CExtent> &extents, UInt32 block)
2493 {
2494   if (extents.IsEmpty())
2495   {
2496     if (block == 0)
2497       return true;
2498     AddSkipExtents(extents, 0, block);
2499     return true;
2500   }
2501 
2502   const CExtent &prev = extents.Back();
2503   if (block < prev.VirtBlock)
2504     return false;
2505   UInt32 prevEnd = prev.GetVirtEnd();
2506   if (block == prevEnd)
2507     return true;
2508   AddSkipExtents(extents, prevEnd, block - prevEnd);
2509   return true;
2510 }
2511 
2512 
FillExtents(const Byte * p,size_t size,CRecordVector<CExtent> & extents,int parentDepth)2513 HRESULT CHandler::FillExtents(const Byte *p, size_t size, CRecordVector<CExtent> &extents, int parentDepth)
2514 {
2515   CExtentTreeHeader eth;
2516   if (!eth.Parse(p))
2517     return S_FALSE;
2518 
2519   if (parentDepth >= 0 && eth.Depth != parentDepth - 1) // (eth.Depth >= parentDepth)
2520     return S_FALSE;
2521 
2522   if (12 + 12 * (size_t)eth.NumEntries > size)
2523     return S_FALSE;
2524 
2525   if (eth.Depth >= kNumTreeLevelsMax)
2526     return S_FALSE;
2527 
2528   if (eth.Depth == 0)
2529   {
2530     for (unsigned i = 0; i < eth.NumEntries; i++)
2531     {
2532       CExtent e;
2533       e.Parse(p + 12 + i * 12);
2534       if (e.PhyStart == 0
2535           || e.PhyStart > _h.NumBlocks
2536           || e.PhyStart + e.Len > _h.NumBlocks
2537           || !e.IsLenOK())
2538         return S_FALSE;
2539       if (!UpdateExtents(extents, e.VirtBlock))
2540         return S_FALSE;
2541       extents.Add(e);
2542     }
2543 
2544     return S_OK;
2545   }
2546 
2547   const size_t blockSize = (size_t)1 << _h.BlockBits;
2548   CByteBuffer &tempBuf = _tempBufs[eth.Depth];
2549   tempBuf.Alloc(blockSize);
2550 
2551   for (unsigned i = 0; i < eth.NumEntries; i++)
2552   {
2553     CExtentIndexNode e;
2554     e.Parse(p + 12 + i * 12);
2555 
2556     if (e.PhyLeaf == 0 || e.PhyLeaf >= _h.NumBlocks)
2557       return S_FALSE;
2558 
2559     if (!UpdateExtents(extents, e.VirtBlock))
2560       return S_FALSE;
2561 
2562     RINOK(SeekAndRead(_stream, e.PhyLeaf, tempBuf, blockSize));
2563     RINOK(FillExtents(tempBuf, blockSize, extents, eth.Depth));
2564   }
2565 
2566   return S_OK;
2567 }
2568 
2569 
GetStream_Node(unsigned nodeIndex,ISequentialInStream ** stream)2570 HRESULT CHandler::GetStream_Node(unsigned nodeIndex, ISequentialInStream **stream)
2571 {
2572   COM_TRY_BEGIN
2573 
2574   *stream = NULL;
2575 
2576   const CNode &node = _nodes[nodeIndex];
2577 
2578   if (!node.IsFlags_EXTENTS())
2579   {
2580     // maybe sparse file can have (node.NumBlocks == 0) ?
2581 
2582     /* The following code doesn't work correctly for some CentOS images,
2583        where there are nodes with inline data and (node.NumBlocks != 0).
2584        If you know better way to detect inline data, please notify 7-Zip developers. */
2585 
2586     if (node.NumBlocks == 0 && node.FileSize < kNodeBlockFieldSize)
2587     {
2588       Create_BufInStream_WithNewBuffer(node.Block, (size_t)node.FileSize, stream);
2589       return S_OK;
2590     }
2591   }
2592 
2593   if (node.FileSize >= ((UInt64)1 << 63))
2594     return S_FALSE;
2595 
2596   CMyComPtr<IInStream> streamTemp;
2597 
2598   UInt64 numBlocks64 = (node.FileSize + (UInt64)(((UInt32)1 << _h.BlockBits) - 1)) >> _h.BlockBits;
2599 
2600   if (node.IsFlags_EXTENTS())
2601   {
2602     if ((UInt32)numBlocks64 != numBlocks64)
2603       return S_FALSE;
2604 
2605     CExtInStream *streamSpec = new CExtInStream;
2606     streamTemp = streamSpec;
2607 
2608     streamSpec->BlockBits = _h.BlockBits;
2609     streamSpec->Size = node.FileSize;
2610     streamSpec->Stream = _stream;
2611 
2612     RINOK(FillExtents(node.Block, kNodeBlockFieldSize, streamSpec->Extents, -1));
2613 
2614     UInt32 end = 0;
2615     if (!streamSpec->Extents.IsEmpty())
2616       end = streamSpec->Extents.Back().GetVirtEnd();
2617     if (end < numBlocks64)
2618     {
2619       AddSkipExtents(streamSpec->Extents, end, (UInt32)(numBlocks64 - end));
2620       // return S_FALSE;
2621     }
2622 
2623     RINOK(streamSpec->StartSeek());
2624   }
2625   else
2626   {
2627     {
2628       UInt64 numBlocks2 = numBlocks64;
2629 
2630       if (numBlocks64 > kNumDirectNodeBlocks)
2631       {
2632         UInt64 rem = numBlocks64 - kNumDirectNodeBlocks;
2633         const unsigned refBits = (_h.BlockBits - 2);
2634         const size_t numRefsInBlocks = (size_t)1 << refBits;
2635         numBlocks2++;
2636         if (rem > numRefsInBlocks)
2637         {
2638           numBlocks2++;
2639           const UInt64 numL2 = (rem - 1) >> refBits;
2640           numBlocks2 += numL2;
2641           if (numL2 > numRefsInBlocks)
2642           {
2643             numBlocks2++;
2644             numBlocks2 += (numL2 - 1) >> refBits;
2645           }
2646         }
2647       }
2648 
2649       const unsigned specBits = (node.IsFlags_HUGE() ? 0 : _h.BlockBits - 9);
2650       const UInt32 specMask = ((UInt32)1 << specBits) - 1;;
2651       if ((node.NumBlocks & specMask) != 0)
2652         return S_FALSE;
2653       const UInt64 numBlocks64_from_header = node.NumBlocks >> specBits;
2654       if (numBlocks64_from_header < numBlocks2)
2655       {
2656         // why (numBlocks64_from_header > numBlocks2) in some cases?
2657         // return S_FALSE;
2658       }
2659     }
2660 
2661     unsigned numBlocks = (unsigned)numBlocks64;
2662     if (numBlocks != numBlocks64)
2663       return S_FALSE;
2664 
2665     CClusterInStream2 *streamSpec = new CClusterInStream2;
2666     streamTemp = streamSpec;
2667 
2668     streamSpec->BlockBits = _h.BlockBits;
2669     streamSpec->Size = node.FileSize;
2670     streamSpec->Stream = _stream;
2671 
2672     RINOK(FillFileBlocks(node.Block, numBlocks, streamSpec->Vector));
2673     streamSpec->InitAndSeek();
2674   }
2675 
2676   *stream = streamTemp.Detach();
2677 
2678   return S_OK;
2679 
2680   COM_TRY_END
2681 }
2682 
2683 
ExtractNode(unsigned nodeIndex,CByteBuffer & data)2684 HRESULT CHandler::ExtractNode(unsigned nodeIndex, CByteBuffer &data)
2685 {
2686   data.Free();
2687   const CNode &node = _nodes[nodeIndex];
2688   size_t size = (size_t)node.FileSize;
2689   if (size != node.FileSize)
2690     return S_FALSE;
2691   CMyComPtr<ISequentialInStream> inSeqStream;
2692   RINOK(GetStream_Node(nodeIndex, &inSeqStream));
2693   if (!inSeqStream)
2694     return S_FALSE;
2695   data.Alloc(size);
2696   _totalRead += size;
2697   return ReadStream_FALSE(inSeqStream, data, size);
2698 }
2699 
2700 
Extract(const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback)2701 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
2702     Int32 testMode, IArchiveExtractCallback *extractCallback)
2703 {
2704   COM_TRY_BEGIN
2705   bool allFilesMode = (numItems == (UInt32)(Int32)-1);
2706   if (allFilesMode)
2707     numItems = _items.Size() + _auxItems.Size();
2708   if (numItems == 0)
2709     return S_OK;
2710 
2711   UInt64 totalSize = 0;
2712   UInt32 i;
2713 
2714   for (i = 0; i < numItems; i++)
2715   {
2716     UInt32 index = allFilesMode ? i : indices[i];
2717     if (index >= _items.Size())
2718       continue;
2719     const CItem &item = _items[index];
2720     const CNode &node = _nodes[_refs[item.Node]];
2721     if (!node.IsDir())
2722       totalSize += node.FileSize;
2723   }
2724 
2725   extractCallback->SetTotal(totalSize);
2726 
2727   UInt64 totalPackSize;
2728   totalSize = totalPackSize = 0;
2729 
2730   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
2731   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
2732 
2733   CLocalProgress *lps = new CLocalProgress;
2734   CMyComPtr<ICompressProgressInfo> progress = lps;
2735   lps->Init(extractCallback, false);
2736 
2737   for (i = 0;; i++)
2738   {
2739     lps->InSize = totalPackSize;
2740     lps->OutSize = totalSize;
2741     RINOK(lps->SetCur());
2742 
2743     if (i == numItems)
2744       break;
2745 
2746     CMyComPtr<ISequentialOutStream> outStream;
2747     Int32 askMode = testMode ?
2748         NExtract::NAskMode::kTest :
2749         NExtract::NAskMode::kExtract;
2750 
2751     UInt32 index = allFilesMode ? i : indices[i];
2752 
2753     RINOK(extractCallback->GetStream(index, &outStream, askMode));
2754 
2755     if (index >= _items.Size())
2756     {
2757       RINOK(extractCallback->PrepareOperation(askMode));
2758       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
2759       continue;
2760     }
2761 
2762     const CItem &item = _items[index];
2763     const CNode &node = _nodes[_refs[item.Node]];
2764 
2765     if (node.IsDir())
2766     {
2767       RINOK(extractCallback->PrepareOperation(askMode));
2768       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
2769       continue;
2770     }
2771 
2772     UInt64 unpackSize = node.FileSize;
2773     totalSize += unpackSize;
2774     UInt64 packSize;
2775     if (GetPackSize(index, packSize))
2776       totalPackSize += packSize;
2777 
2778     if (!testMode && !outStream)
2779       continue;
2780     RINOK(extractCallback->PrepareOperation(askMode));
2781 
2782     int res = NExtract::NOperationResult::kDataError;
2783     {
2784       CMyComPtr<ISequentialInStream> inSeqStream;
2785       HRESULT hres = GetStream(index, &inSeqStream);
2786       if (hres == S_FALSE || !inSeqStream)
2787       {
2788         if (hres == E_OUTOFMEMORY)
2789           return hres;
2790         res = NExtract::NOperationResult::kUnsupportedMethod;
2791       }
2792       else
2793       {
2794         RINOK(hres);
2795         {
2796           hres = copyCoder->Code(inSeqStream, outStream, NULL, NULL, progress);
2797           if (hres == S_OK)
2798           {
2799             if (copyCoderSpec->TotalSize == unpackSize)
2800               res = NExtract::NOperationResult::kOK;
2801           }
2802           else if (hres == E_NOTIMPL)
2803           {
2804             res = NExtract::NOperationResult::kUnsupportedMethod;
2805           }
2806           else if (hres != S_FALSE)
2807           {
2808             RINOK(hres);
2809           }
2810         }
2811       }
2812     }
2813     RINOK(extractCallback->SetOperationResult(res));
2814   }
2815 
2816   return S_OK;
2817   COM_TRY_END
2818 }
2819 
2820 
GetStream(UInt32 index,ISequentialInStream ** stream)2821 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
2822 {
2823   *stream = NULL;
2824   if (index >= _items.Size())
2825     return S_FALSE;
2826   return GetStream_Node(_refs[_items[index].Node], stream);
2827 }
2828 
2829 
IsArc_Ext(const Byte * p,size_t size)2830 API_FUNC_static_IsArc IsArc_Ext(const Byte *p, size_t size)
2831 {
2832   if (size < kHeaderSize)
2833     return k_IsArc_Res_NEED_MORE;
2834   CHeader h;
2835   if (!h.Parse(p + kHeaderDataOffset))
2836     return k_IsArc_Res_NO;
2837   return k_IsArc_Res_YES;
2838 }
2839 }
2840 
2841 static const Byte k_Signature[] = { 0x53, 0xEF };
2842 
2843 REGISTER_ARC_I(
2844   "Ext", "ext ext2 ext3 ext4 img", 0, 0xC7,
2845   k_Signature,
2846   0x438,
2847   0,
2848   IsArc_Ext)
2849 
2850 }}
2851