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