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