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