p7zip/CPP/7zip/Archive/ExtHandler.cpp
2017-10-11 12:35:36 +02:00

2868 lines
63 KiB
C++

// ExtHandler.cpp
#include "StdAfx.h"
// #define SHOW_DEBUG_INFO
// #include <stdio.h>
// #define PRF2(x) x
#define PRF2(x)
#ifdef SHOW_DEBUG_INFO
#include <stdio.h>
#define PRF(x) x
#else
#define PRF(x)
#endif
#include "../../../C/Alloc.h"
#include "../../../C/CpuArch.h"
#include "../../Common/ComTry.h"
#include "../../Common/IntToString.h"
#include "../../Common/MyLinux.h"
#include "../../Common/StringConvert.h"
#include "../../Common/UTFConvert.h"
#include "../../Windows/PropVariantUtils.h"
#include "../../Windows/TimeUtils.h"
#include "../Common/ProgressUtils.h"
#include "../Common/RegisterArc.h"
#include "../Common/StreamObjects.h"
#include "../Common/StreamUtils.h"
#include "../Compress/CopyCoder.h"
using namespace NWindows;
namespace NArchive {
namespace NExt {
#define Get16(p) GetUi16(p)
#define Get32(p) GetUi32(p)
#define Get64(p) GetUi64(p)
#define LE_16(offs, dest) dest = Get16(p + (offs));
#define LE_32(offs, dest) dest = Get32(p + (offs));
#define LE_64(offs, dest) dest = Get64(p + (offs));
#define HI_16(offs, dest) dest |= (((UInt32)Get16(p + (offs))) << 16);
#define HI_32(offs, dest) dest |= (((UInt64)Get32(p + (offs))) << 32);
/*
static UInt32 g_Crc32CTable[256];
static struct CInitCrc32C
{
CInitCrc32C()
{
for (unsigned i = 0; i < 256; i++)
{
UInt32 r = i;
unsigned j;
for (j = 0; j < 8; j++)
r = (r >> 1) ^ (0x82F63B78 & ~((r & 1) - 1));
g_Crc32CTable[i] = r;
}
}
} g_InitCrc32C;
#define CRC32C_INIT_VAL 0xFFFFFFFF
#define CRC32C_GET_DIGEST(crc) ((crc) ^ CRC_INIT_VAL)
#define CRC32C_UPDATE_BYTE(crc, b) (g_Crc32CTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8))
static UInt32 Crc32C_Update(UInt32 crc, Byte const *data, size_t size)
{
for (size_t i = 0; i < size; i++)
crc = CRC32C_UPDATE_BYTE(crc, data[i]);
return crc;
}
static UInt32 Crc32C_Calc(Byte const *data, size_t size)
{
return Crc32C_Update(CRC32C_INIT_VAL, data, size);
}
*/
// CRC-16-ANSI. The poly is 0x8005 (x^16 + x^15 + x^2 + 1)
static UInt16 g_Crc16Table[256];
static struct CInitCrc16
{
CInitCrc16()
{
for (unsigned i = 0; i < 256; i++)
{
UInt32 r = i;
unsigned j;
for (j = 0; j < 8; j++)
r = (r >> 1) ^ (0xA001 & ~((r & 1) - 1));
g_Crc16Table[i] = (UInt16)r;
}
}
} g_InitCrc16;
#define CRC16_UPDATE_BYTE(crc, b) (g_Crc16Table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8))
#define CRC16_INIT_VAL 0xFFFF
static UInt32 Crc16Update(UInt32 crc, Byte const *data, size_t size)
{
for (size_t i = 0; i < size; i++)
crc = CRC16_UPDATE_BYTE(crc, data[i]);
return crc;
}
static UInt32 Crc16Calc(Byte const *data, size_t size)
{
return Crc16Update(CRC16_INIT_VAL, data, size);
}
#define EXT4_GOOD_OLD_INODE_SIZE 128
// inodes numbers
// #define k_INODE_BAD 1 // Bad blocks
#define k_INODE_ROOT 2 // Root dir
// #define k_INODE_USR_QUOTA 3 // User quota
// #define k_INODE_GRP_QUOTA 4 // Group quota
// #define k_INODE_BOOT_LOADER 5 // Boot loader
// #define k_INODE_UNDEL_DIR 6 // Undelete dir
#define k_INODE_RESIZE 7 // Reserved group descriptors
// #define k_INODE_JOURNAL 8 // Journal
// First non-reserved inode for old ext4 filesystems
#define k_INODE_GOOD_OLD_FIRST 11
static const char * const k_SysInode_Names[] =
{
"0"
, "Bad"
, "Root"
, "UserQuota"
, "GroupQuota"
, "BootLoader"
, "Undelete"
, "Resize"
, "Journal"
, "Exclude"
, "Replica"
};
static const char * const kHostOS[] =
{
"Linux"
, "Hurd"
, "Masix"
, "FreeBSD"
, "Lites"
};
static const CUInt32PCharPair g_FeatureCompat_Flags[] =
{
{ 0, "DIR_PREALLOC" },
{ 1, "IMAGIC_INODES" },
{ 2, "HAS_JOURNAL" },
{ 3, "EXT_ATTR" },
{ 4, "RESIZE_INODE" },
{ 5, "DIR_INDEX" },
{ 6, "LAZY_BG" }, // not in Linux
// { 7, "EXCLUDE_INODE" }, // not used
// { 8, "EXCLUDE_BITMAP" }, // not in kernel
{ 9, "SPARSE_SUPER2" }
};
#define EXT4_FEATURE_INCOMPAT_FILETYPE (1 << 1)
#define EXT4_FEATURE_INCOMPAT_64BIT (1 << 7)
static const CUInt32PCharPair g_FeatureIncompat_Flags[] =
{
{ 0, "COMPRESSION" },
{ 1, "FILETYPE" },
{ 2, "RECOVER" }, /* Needs recovery */
{ 3, "JOURNAL_DEV" }, /* Journal device */
{ 4, "META_BG" },
{ 6, "EXTENTS" }, /* extents support */
{ 7, "64BIT" },
{ 8, "MMP" },
{ 9, "FLEX_BG" },
{ 10, "EA_INODE" }, /* EA in inode */
{ 12, "DIRDATA" }, /* data in dirent */
{ 13, "BG_USE_META_CSUM" }, /* use crc32c for bg */
{ 14, "LARGEDIR" }, /* >2GB or 3-lvl htree */
{ 15, "INLINE_DATA" }, /* data in inode */
{ 16, "ENCRYPT" }
};
static const UInt32 RO_COMPAT_GDT_CSUM = 1 << 4;
static const UInt32 RO_COMPAT_METADATA_CSUM = 1 << 10;
static const CUInt32PCharPair g_FeatureRoCompat_Flags[] =
{
{ 0, "SPARSE_SUPER" },
{ 1, "LARGE_FILE" },
{ 2, "BTREE_DIR" },
{ 3, "HUGE_FILE" },
{ 4, "GDT_CSUM" },
{ 5, "DIR_NLINK" },
{ 6, "EXTRA_ISIZE" },
{ 7, "HAS_SNAPSHOT" },
{ 8, "QUOTA" },
{ 9, "BIGALLOC" },
{ 10, "METADATA_CSUM" },
{ 11, "REPLICA" },
{ 12, "READONLY" }
};
static const UInt32 k_NodeFlags_HUGE = (UInt32)1 << 18;
static const UInt32 k_NodeFlags_EXTENTS = (UInt32)1 << 19;
static const CUInt32PCharPair g_NodeFlags[] =
{
{ 0, "SECRM" },
{ 1, "UNRM" },
{ 2, "COMPR" },
{ 3, "SYNC" },
{ 4, "IMMUTABLE" },
{ 5, "APPEND" },
{ 6, "NODUMP" },
{ 7, "NOATIME" },
{ 8, "DIRTY" },
{ 9, "COMPRBLK" },
{ 10, "NOCOMPR" },
{ 11, "ENCRYPT" },
{ 12, "INDEX" },
{ 13, "IMAGIC" },
{ 14, "JOURNAL_DATA" },
{ 15, "NOTAIL" },
{ 16, "DIRSYNC" },
{ 17, "TOPDIR" },
{ 18, "HUGE_FILE" },
{ 19, "EXTENTS" },
{ 21, "EA_INODE" },
{ 22, "EOFBLOCKS" },
{ 28, "INLINE_DATA" }
};
static inline char GetHex(unsigned t) { return (char)(((t < 10) ? ('0' + t) : ('A' + (t - 10)))); }
static inline void PrintHex(unsigned v, char *s)
{
s[0] = GetHex((v >> 4) & 0xF);
s[1] = GetHex(v & 0xF);
}
enum
{
k_Type_UNKNOWN,
k_Type_REG_FILE,
k_Type_DIR,
k_Type_CHRDEV,
k_Type_BLKDEV,
k_Type_FIFO,
k_Type_SOCK,
k_Type_SYMLINK
};
static const UInt16 k_TypeToMode[] =
{
0,
MY_LIN_S_IFREG,
MY_LIN_S_IFDIR,
MY_LIN_S_IFCHR,
MY_LIN_S_IFBLK,
MY_LIN_S_IFIFO,
MY_LIN_S_IFSOCK,
MY_LIN_S_IFLNK
};
#define EXT4_GOOD_OLD_REV 0 // old (original) format
// #define EXT4_DYNAMIC_REV 1 // V2 format with dynamic inode sizes
struct CHeader
{
unsigned BlockBits;
unsigned ClusterBits;
UInt32 NumInodes;
UInt64 NumBlocks;
// UInt64 NumBlocksSuper;
UInt64 NumFreeBlocks;
UInt32 NumFreeInodes;
// UInt32 FirstDataBlock;
UInt32 BlocksPerGroup;
UInt32 ClustersPerGroup;
UInt32 InodesPerGroup;
UInt32 MountTime;
UInt32 WriteTime;
// UInt16 NumMounts;
// UInt16 NumMountsMax;
// UInt16 State;
// UInt16 Errors;
// UInt16 MinorRevLevel;
UInt32 LastCheckTime;
// UInt32 CheckInterval;
UInt32 CreatorOs;
UInt32 RevLevel;
// UInt16 DefResUid;
// UInt16 DefResGid;
UInt32 FirstInode;
UInt16 InodeSize;
UInt16 BlockGroupNr;
UInt32 FeatureCompat;
UInt32 FeatureIncompat;
UInt32 FeatureRoCompat;
Byte Uuid[16];
char VolName[16];
char LastMount[64];
// UInt32 BitmapAlgo;
UInt32 JournalInode;
UInt16 GdSize; // = 64 if 64-bit();
UInt32 CTime;
UInt16 MinExtraISize;
// UInt16 WantExtraISize;
// UInt32 Flags;
// Byte LogGroupsPerFlex;
// Byte ChecksumType;
UInt64 WrittenKB;
bool IsOldRev() const { return RevLevel == EXT4_GOOD_OLD_REV; }
UInt64 GetNumGroups() const { return (NumBlocks + BlocksPerGroup - 1) / BlocksPerGroup; }
UInt64 GetNumGroups2() const { return ((UInt64)NumInodes + InodesPerGroup - 1) / InodesPerGroup; }
bool IsThereFileType() const { return (FeatureIncompat & EXT4_FEATURE_INCOMPAT_FILETYPE) != 0; }
bool Is64Bit() const { return (FeatureIncompat & EXT4_FEATURE_INCOMPAT_64BIT) != 0; }
bool UseGdtChecksum() const { return (FeatureRoCompat & RO_COMPAT_GDT_CSUM) != 0; }
bool UseMetadataChecksum() const { return (FeatureRoCompat & RO_COMPAT_METADATA_CSUM) != 0; }
bool Parse(const Byte *p);
};
static int inline GetLog(UInt32 num)
{
for (unsigned i = 0; i < 32; i++)
if (((UInt32)1 << i) == num)
return i;
return -1;
}
static bool inline IsEmptyData(const Byte *data, unsigned size)
{
for (unsigned i = 0; i < size; i++)
if (data[i] != 0)
return false;
return true;
}
bool CHeader::Parse(const Byte *p)
{
if (GetUi16(p + 0x38) != 0xEF53)
return false;
LE_32 (0x18, BlockBits);
LE_32 (0x1C, ClusterBits);
if (ClusterBits != 0 && BlockBits != ClusterBits)
return false;
if (BlockBits > 16 - 10)
return false;
BlockBits += 10;
LE_32 (0x00, NumInodes);
LE_32 (0x04, NumBlocks);
// LE_32 (0x08, NumBlocksSuper);
LE_32 (0x0C, NumFreeBlocks);
LE_32 (0x10, NumFreeInodes);
if (NumInodes < 2 || NumInodes <= NumFreeInodes)
return false;
UInt32 FirstDataBlock;
LE_32 (0x14, FirstDataBlock);
if (FirstDataBlock != (unsigned)(BlockBits == 10 ? 1 : 0))
return false;
LE_32 (0x20, BlocksPerGroup);
LE_32 (0x24, ClustersPerGroup);
if (BlocksPerGroup != ClustersPerGroup)
return false;
if (BlocksPerGroup == 0)
return false;
if (BlocksPerGroup != ((UInt32)1 << (BlockBits + 3)))
{
// it's allowed in ext2
// return false;
}
LE_32 (0x28, InodesPerGroup);
if (InodesPerGroup < 1 || InodesPerGroup > NumInodes)
return false;
LE_32 (0x2C, MountTime);
LE_32 (0x30, WriteTime);
// LE_16 (0x34, NumMounts);
// LE_16 (0x36, NumMountsMax);
// LE_16 (0x3A, State);
// LE_16 (0x3C, Errors);
// LE_16 (0x3E, MinorRevLevel);
LE_32 (0x40, LastCheckTime);
// LE_32 (0x44, CheckInterval);
LE_32 (0x48, CreatorOs);
LE_32 (0x4C, RevLevel);
// LE_16 (0x50, DefResUid);
// LE_16 (0x52, DefResGid);
FirstInode = k_INODE_GOOD_OLD_FIRST;
InodeSize = EXT4_GOOD_OLD_INODE_SIZE;
if (!IsOldRev())
{
LE_32 (0x54, FirstInode);
LE_16 (0x58, InodeSize);
if (FirstInode < k_INODE_GOOD_OLD_FIRST)
return false;
if (InodeSize > (UInt32)1 << BlockBits)
return false;
if (GetLog(InodeSize) < 0)
return false;
}
LE_16 (0x5A, BlockGroupNr);
LE_32 (0x5C, FeatureCompat);
LE_32 (0x60, FeatureIncompat);
LE_32 (0x64, FeatureRoCompat);
memcpy(Uuid, p + 0x68, sizeof(Uuid));
memcpy(VolName, p + 0x78, sizeof(VolName));
memcpy(LastMount, p + 0x88, sizeof(LastMount));
// LE_32 (0xC8, BitmapAlgo);
LE_32 (0xE0, JournalInode);
LE_16 (0xFE, GdSize);
LE_32 (0x108, CTime);
if (Is64Bit())
{
HI_32(150, NumBlocks);
// HI_32(154, NumBlocksSuper);
HI_32(0x158, NumFreeBlocks);
}
if (NumBlocks >= (UInt64)1 << (63 - BlockBits))
return false;
LE_16(0x15C, MinExtraISize);
// LE_16(0x15E, WantExtraISize);
// LE_32(0x160, Flags);
// LE_16(0x164, RaidStride);
// LE_16(0x166, MmpInterval);
// LE_64(0x168, MmpBlock);
// LogGroupsPerFlex = p[0x174];
// ChecksumType = p[0x175];
LE_64 (0x178, WrittenKB);
// LE_32(0x194, ErrorCount);
// LE_32(0x198, ErrorTime);
// LE_32(0x19C, ErrorINode);
// LE_32(0x1A0, ErrorBlock);
if (NumBlocks < 1)
return false;
if (NumFreeBlocks > NumBlocks)
return false;
if (GetNumGroups() != GetNumGroups2())
return false;
return true;
}
struct CGroupDescriptor
{
UInt64 BlockBitmap;
UInt64 InodeBitmap;
UInt64 InodeTable;
UInt32 NumFreeBlocks;
UInt32 NumFreeInodes;
UInt32 DirCount;
UInt16 Flags;
UInt64 ExcludeBitmap;
UInt32 BlockBitmap_Checksum;
UInt32 InodeBitmap_Checksum;
UInt32 UnusedCount;
UInt16 Checksum;
void Parse(const Byte *p, unsigned size);
};
void CGroupDescriptor::Parse(const Byte *p, unsigned size)
{
LE_32 (0x00, BlockBitmap);
LE_32 (0x04, InodeBitmap);
LE_32 (0x08, InodeTable);
LE_16 (0x0C, NumFreeBlocks);
LE_16 (0x0E, NumFreeInodes);
LE_16 (0x10, DirCount);
LE_16 (0x12, Flags);
LE_32 (0x14, ExcludeBitmap);
LE_16 (0x18, BlockBitmap_Checksum);
LE_16 (0x1A, InodeBitmap_Checksum);
LE_16 (0x1C, UnusedCount);
LE_16 (0x1E, Checksum);
if (size >= 64)
{
p += 0x20;
HI_32 (0x00, BlockBitmap);
HI_32 (0x04, InodeBitmap);
HI_32 (0x08, InodeTable);
HI_16 (0x0C, NumFreeBlocks);
HI_16 (0x0E, NumFreeInodes);
HI_16 (0x10, DirCount);
HI_16 (0x12, UnusedCount); // instead of Flags
HI_32 (0x14, ExcludeBitmap);
HI_16 (0x18, BlockBitmap_Checksum);
HI_16 (0x1A, InodeBitmap_Checksum);
// HI_16 (0x1C, Reserved);
}
}
static const unsigned kNodeBlockFieldSize = 60;
struct CExtentTreeHeader
{
UInt16 NumEntries;
UInt16 MaxEntries;
UInt16 Depth;
// UInt32 Generation;
bool Parse(const Byte *p)
{
LE_16 (0x02, NumEntries);
LE_16 (0x04, MaxEntries);
LE_16 (0x06, Depth);
// LE_32 (0x08, Generation);
return Get16(p) == 0xF30A; // magic
}
};
struct CExtentIndexNode
{
UInt32 VirtBlock;
UInt64 PhyLeaf;
void Parse(const Byte *p)
{
LE_32 (0x00, VirtBlock);
LE_32 (0x04, PhyLeaf);
PhyLeaf |= (((UInt64)Get16(p + 8)) << 32);
// unused 16-bit field (at offset 0x0A) can be not zero in some cases. Why?
}
};
struct CExtent
{
UInt32 VirtBlock;
UInt16 Len;
bool IsInited;
UInt64 PhyStart;
UInt32 GetVirtEnd() const { return VirtBlock + Len; }
bool IsLenOK() const { return VirtBlock + Len >= VirtBlock; }
void Parse(const Byte *p)
{
LE_32 (0x00, VirtBlock);
LE_16 (0x04, Len);
IsInited = true;
if (Len > (UInt32)0x8000)
{
IsInited = false;
Len -= (UInt32)0x8000;
}
LE_32 (0x08, PhyStart);
UInt16 hi;
LE_16 (0x06, hi);
PhyStart |= ((UInt64)hi << 32);
}
};
struct CExtTime
{
UInt32 Val;
UInt32 Extra;
};
struct CNode
{
Int32 ParentNode; // in _refs[], -1 if not dir
int ItemIndex; // in _items[]
int SymLinkIndex; // in _symLinks[]
int DirIndex; // in _dirs[]
UInt16 Mode;
UInt16 Uid;
UInt16 Gid;
// UInt16 Checksum;
UInt64 FileSize;
CExtTime MTime;
CExtTime ATime;
CExtTime CTime;
// CExtTime InodeChangeTime;
// CExtTime DTime;
UInt64 NumBlocks;
UInt32 NumLinks;
UInt32 Flags;
UInt32 NumLinksCalced;
Byte Block[kNodeBlockFieldSize];
CNode():
ParentNode(-1),
ItemIndex(-1),
SymLinkIndex(-1),
DirIndex(0),
NumLinksCalced(0)
{}
bool IsFlags_HUGE() const { return (Flags & k_NodeFlags_HUGE) != 0; }
bool IsFlags_EXTENTS() const { return (Flags & k_NodeFlags_EXTENTS) != 0; }
bool IsDir() const { return MY_LIN_S_ISDIR(Mode); }
bool IsRegular() const { return MY_LIN_S_ISREG(Mode); }
bool IsLink() const { return MY_LIN_S_ISLNK(Mode); }
bool Parse(const Byte *p, const CHeader &_h);
};
bool CNode::Parse(const Byte *p, const CHeader &_h)
{
MTime.Extra = 0;
ATime.Extra = 0;
CTime.Extra = 0;
CTime.Val = 0;
// InodeChangeTime.Extra = 0;
// DTime.Extra = 0;
LE_16 (0x00, Mode);
LE_16 (0x02, Uid);
LE_32 (0x04, FileSize);
LE_32 (0x08, ATime.Val);
// LE_32 (0x0C, InodeChangeTime.Val);
LE_32 (0x10, MTime.Val);
// LE_32 (0x14, DTime.Val);
LE_16 (0x18, Gid);
LE_16 (0x1A, NumLinks);
LE_32 (0x1C, NumBlocks);
LE_32 (0x20, Flags);
// LE_32 (0x24, Union osd1);
memcpy(Block, p + 0x28, kNodeBlockFieldSize);
// LE_32 (0x64, Generation); // File version (for NFS)
// LE_32 (0x68, ACL);
{
UInt32 highSize;
LE_32 (0x6C, highSize); // In ext2/3 this field was named i_dir_acl
if (IsRegular()) // do we need that check ?
FileSize |= ((UInt64)highSize << 32);
}
// UInt32 fragmentAddress;
// LE_32 (0x70, fragmentAddress);
// osd2
{
// Linux;
// ext2:
// Byte FragmentNumber = p[0x74];
// Byte FragmentSize = p[0x74 + 1];
// ext4:
UInt32 numBlocksHigh;
LE_16 (0x74, numBlocksHigh);
NumBlocks |= (UInt64)numBlocksHigh << 32;
HI_16 (0x74 + 4, Uid);
HI_16 (0x74 + 6, Gid);
/*
UInt32 checksum;
LE_16 (0x74 + 8, checksum);
checksum = checksum;
*/
}
// 0x74: Byte Union osd2[12];
if (_h.InodeSize > 128)
{
UInt16 extra_isize;
LE_16 (0x80, extra_isize);
if (128 + extra_isize > _h.InodeSize)
return false;
if (extra_isize >= 0x1C)
{
// UInt16 checksumUpper;
// LE_16 (0x82, checksumUpper);
// LE_32 (0x84, InodeChangeTime.Extra);
LE_32 (0x88, MTime.Extra);
LE_32 (0x8C, ATime.Extra);
LE_32 (0x90, CTime.Val);
LE_32 (0x94, CTime.Extra);
// LE_32 (0x98, VersionHi);
}
}
PRF(printf("size = %5d", (unsigned)FileSize));
return true;
}
struct CItem
{
unsigned Node; // in _refs[]
int ParentNode; // in _refs[]
int SymLinkItemIndex; // in _items[], if the Node contains SymLink to existing dir
Byte Type;
AString Name;
CItem():
Node(0),
ParentNode(-1),
SymLinkItemIndex(-1),
Type(k_Type_UNKNOWN)
{}
void Clear()
{
Node = 0;
ParentNode = -1;
SymLinkItemIndex = -1;
Type = k_Type_UNKNOWN;
Name.Empty();
}
bool IsDir() const { return Type == k_Type_DIR; }
// bool IsNotDir() const { return Type != k_Type_DIR && Type != k_Type_UNKNOWN; }
};
static const unsigned kNumTreeLevelsMax = 6; // must be >= 3
class CHandler:
public IInArchive,
public IArchiveGetRawProps,
public IInArchiveGetStream,
public CMyUnknownImp
{
CObjectVector<CItem> _items;
CIntVector _refs;
CRecordVector<CNode> _nodes;
CObjectVector<CUIntVector> _dirs; // each CUIntVector contains indexes in _items[] only for dir items;
AStringVector _symLinks;
AStringVector _auxItems;
int _auxSysIndex;
int _auxUnknownIndex;
CMyComPtr<IInStream> _stream;
UInt64 _phySize;
bool _isArc;
bool _headersError;
bool _headersWarning;
bool _linksError;
bool _isUTF;
CHeader _h;
IArchiveOpenCallback *_openCallback;
UInt64 _totalRead;
UInt64 _totalReadPrev;
CByteBuffer _tempBufs[kNumTreeLevelsMax];
HRESULT CheckProgress2()
{
const UInt64 numFiles = _items.Size();
return _openCallback->SetCompleted(&numFiles, &_totalRead);
}
HRESULT CheckProgress()
{
HRESULT res = S_OK;
if (_openCallback)
{
if (_totalRead - _totalReadPrev >= ((UInt32)1 << 20))
{
_totalReadPrev = _totalRead;
res = CheckProgress2();
}
}
return res;
}
const int GetParentAux(const CItem &item) const
{
if (item.Node < _h.FirstInode && _auxSysIndex >= 0)
return _auxSysIndex;
return _auxUnknownIndex;
}
HRESULT SeekAndRead(IInStream *inStream, UInt64 block, Byte *data, size_t size);
HRESULT ParseDir(const Byte *data, size_t size, unsigned iNodeDir);
int FindTargetItem_for_SymLink(unsigned dirNode, const AString &path) const;
HRESULT FillFileBlocks2(UInt32 block, unsigned level, unsigned numBlocks, CRecordVector<UInt32> &blocks);
HRESULT FillFileBlocks(const Byte *p, unsigned numBlocks, CRecordVector<UInt32> &blocks);
HRESULT FillExtents(const Byte *p, size_t size, CRecordVector<CExtent> &extents, int parentDepth);
HRESULT GetStream_Node(unsigned nodeIndex, ISequentialInStream **stream);
HRESULT ExtractNode(unsigned nodeIndex, CByteBuffer &data);
void ClearRefs();
HRESULT Open2(IInStream *inStream);
void GetPath(unsigned index, AString &s) const;
bool GetPackSize(unsigned index, UInt64 &res) const;
public:
CHandler() {}
~CHandler() {}
MY_UNKNOWN_IMP3(IInArchive, IArchiveGetRawProps, IInArchiveGetStream)
INTERFACE_IInArchive(;)
INTERFACE_IArchiveGetRawProps(;)
STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
};
HRESULT CHandler::ParseDir(const Byte *p, size_t size, unsigned iNodeDir)
{
bool isThereSelfLink = false;
PRF(printf("\n\n========= node = %5d size = %5d", (unsigned)iNodeDir, (unsigned)size));
CNode &nodeDir = _nodes[_refs[iNodeDir]];
nodeDir.DirIndex = _dirs.Size();
CUIntVector &dir = _dirs.AddNew();
int parentNode = -1;
CItem item;
for (;;)
{
if (size == 0)
break;
if (size < 8)
return S_FALSE;
UInt32 iNode;
LE_32 (0x00, iNode);
unsigned recLen;
LE_16 (0x04, recLen);
unsigned nameLen = p[6];
Byte type = p[7];
if (recLen > size)
return S_FALSE;
if (nameLen + 8 > recLen)
return S_FALSE;
if (iNode >= _refs.Size())
return S_FALSE;
item.Clear();
if (_h.IsThereFileType())
item.Type = type;
else if (type != 0)
return S_FALSE;
item.ParentNode = iNodeDir;
item.Node = iNode;
item.Name.SetFrom_CalcLen((const char *)(p + 8), nameLen);
p += recLen;
size -= recLen;
if (item.Name.Len() != nameLen)
return S_FALSE;
if (_isUTF)
_isUTF = CheckUTF8(item.Name);
if (iNode == 0)
{
/*
ext3 deleted??
if (item.Name.Len() != 0)
return S_FALSE;
*/
PRF(printf("\n EMPTY %6d %d %s", (unsigned)recLen, (unsigned)type, (const char *)item.Name));
if (type == 0xDE)
{
// checksum
}
continue;
}
int nodeIndex = _refs[iNode];
if (nodeIndex < 0)
return S_FALSE;
CNode &node = _nodes[nodeIndex];
if (_h.IsThereFileType() && type != 0)
{
if (type >= ARRAY_SIZE(k_TypeToMode))
return S_FALSE;
if (k_TypeToMode[type] != (node.Mode & MY_LIN_S_IFMT))
return S_FALSE;
}
node.NumLinksCalced++;
PRF(printf("\n%s %6d %s", item.IsDir() ? "DIR " : " ", (unsigned)item.Node, (const char *)item.Name));
if (item.Name[0] == '.')
{
if (item.Name[1] == 0)
{
if (isThereSelfLink)
return S_FALSE;
isThereSelfLink = true;
if (iNode != iNodeDir)
return S_FALSE;
continue;
}
if (item.Name[1] == '.' && item.Name[2] == 0)
{
if (parentNode >= 0)
return S_FALSE;
if (!node.IsDir())
return S_FALSE;
if (iNode == iNodeDir && iNode != k_INODE_ROOT)
return S_FALSE;
parentNode = iNode;
if (nodeDir.ParentNode < 0)
nodeDir.ParentNode = iNode;
else if ((unsigned)nodeDir.ParentNode != iNode)
return S_FALSE;
continue;
}
}
if (iNode == iNodeDir)
return S_FALSE;
if (parentNode < 0)
return S_FALSE;
if (node.IsDir())
{
if (node.ParentNode < 0)
node.ParentNode = iNodeDir;
else if ((unsigned)node.ParentNode != iNodeDir)
return S_FALSE;
const unsigned itemIndex = _items.Size();
dir.Add(itemIndex);
node.ItemIndex = itemIndex;
}
_items.Add(item);
}
if (parentNode < 0 || !isThereSelfLink)
return S_FALSE;
return S_OK;
}
int CHandler::FindTargetItem_for_SymLink(unsigned iNode, const AString &path) const
{
unsigned pos = 0;
if (path.IsEmpty())
return -1;
if (path[0] == '/')
{
iNode = k_INODE_ROOT;
if (iNode >= _refs.Size())
return -1;
pos = 1;
}
AString s;
while (pos != path.Len())
{
const CNode &node = _nodes[_refs[iNode]];
int slash = path.Find('/', pos);
if (slash < 0)
{
s = path.Ptr(pos);
pos = path.Len();
}
else
{
s.SetFrom(path.Ptr(pos), slash - pos);
pos = slash + 1;
}
if (s[0] == '.')
{
if (s[1] == 0)
continue;
else if (s[1] == '.' && s[2] == 0)
{
if (node.ParentNode < 0)
return -1;
if (iNode == k_INODE_ROOT)
return -1;
iNode = node.ParentNode;
continue;
}
}
if (node.DirIndex < 0)
return -1;
const CUIntVector &dir = _dirs[node.DirIndex];
for (unsigned i = 0;; i++)
{
if (i >= dir.Size())
return -1;
const CItem &item = _items[dir[i]];
if (item.Name == s)
{
iNode = item.Node;
break;
}
}
}
return _nodes[_refs[iNode]].ItemIndex;
}
HRESULT CHandler::SeekAndRead(IInStream *inStream, UInt64 block, Byte *data, size_t size)
{
if (block == 0 || block >= _h.NumBlocks)
return S_FALSE;
if (((size + ((size_t)1 << _h.BlockBits) - 1) >> _h.BlockBits) > _h.NumBlocks - block)
return S_FALSE;
RINOK(inStream->Seek((UInt64)block << _h.BlockBits, STREAM_SEEK_SET, NULL));
_totalRead += size;
return ReadStream_FALSE(inStream, data, size);
}
static const unsigned kHeaderSize = 2 * 1024;
static const unsigned kHeaderDataOffset = 1024;
HRESULT CHandler::Open2(IInStream *inStream)
{
{
Byte buf[kHeaderSize];
RINOK(ReadStream_FALSE(inStream, buf, kHeaderSize));
if (!_h.Parse(buf + kHeaderDataOffset))
return S_FALSE;
if (_h.BlockGroupNr != 0)
return S_FALSE; // it's just copy of super block
}
{
// ---------- Read groups and nodes ----------
unsigned numGroups;
{
UInt64 numGroups64 = _h.GetNumGroups();
if (numGroups64 > (UInt32)1 << 31)
return S_FALSE;
numGroups = (unsigned)numGroups64;
}
unsigned gdBits = 5;
if (_h.Is64Bit())
{
if (_h.GdSize != 64)
return S_FALSE;
gdBits = 6;
}
_isArc = true;
_phySize = _h.NumBlocks << _h.BlockBits;
if (_openCallback)
{
RINOK(_openCallback->SetTotal(NULL, &_phySize));
}
UInt64 fileSize = 0;
RINOK(inStream->Seek(0, STREAM_SEEK_END, &fileSize));
CRecordVector<CGroupDescriptor> groups;
{
// ---------- Read groups ----------
CByteBuffer gdBuf;
size_t gdBufSize = (size_t)numGroups << gdBits;
if ((gdBufSize >> gdBits) != numGroups)
return S_FALSE;
gdBuf.Alloc(gdBufSize);
RINOK(SeekAndRead(inStream, (_h.BlockBits <= 10 ? 2 : 1), gdBuf, gdBufSize));
for (unsigned i = 0; i < numGroups; i++)
{
CGroupDescriptor gd;
const Byte *p = gdBuf + ((size_t)i << gdBits);
unsigned gd_Size = (unsigned)1 << gdBits;
gd.Parse(p, gd_Size);
if (_h.UseMetadataChecksum())
{
// use CRC32c
}
else if (_h.UseGdtChecksum())
{
UInt32 crc = Crc16Calc(_h.Uuid, sizeof(_h.Uuid));
Byte i_le[4];
SetUi32(i_le, i);
crc = Crc16Update(crc, i_le, 4);
crc = Crc16Update(crc, p, 32 - 2);
if (gd_Size != 32)
crc = Crc16Update(crc, p + 32, gd_Size - 32);
if (crc != gd.Checksum)
return S_FALSE;
}
groups.Add(gd);
}
}
{
// ---------- Read nodes ----------
if (_h.NumInodes < _h.NumFreeInodes)
return S_FALSE;
UInt32 numNodes = _h.InodesPerGroup;
if (numNodes > _h.NumInodes)
numNodes = _h.NumInodes;
size_t nodesDataSize = (size_t)numNodes * _h.InodeSize;
if (nodesDataSize / _h.InodeSize != numNodes)
return S_FALSE;
// that code to reduce false detecting cases
if (nodesDataSize > fileSize)
{
if (numNodes > (1 << 24))
return S_FALSE;
}
UInt32 numReserveInodes = _h.NumInodes - _h.NumFreeInodes + 1;
// numReserveInodes = _h.NumInodes + 1;
if (numReserveInodes != 0)
{
_nodes.Reserve(numReserveInodes);
_refs.Reserve(numReserveInodes);
}
CByteBuffer nodesData;
nodesData.Alloc(nodesDataSize);
CByteBuffer nodesMap;
const size_t blockSize = (size_t)1 << _h.BlockBits;
nodesMap.Alloc(blockSize);
unsigned globalNodeIndex = 0;
// unsigned numEmpty = 0;
unsigned numEmpty_in_Maps = 0;
FOR_VECTOR (gi, groups)
{
if (globalNodeIndex >= _h.NumInodes)
break;
const CGroupDescriptor &gd = groups[gi];
PRF(printf("\n\ng%6d block = %6x\n", gi, (unsigned)gd.InodeTable));
RINOK(SeekAndRead(inStream, gd.InodeBitmap, nodesMap, blockSize));
RINOK(SeekAndRead(inStream, gd.InodeTable, nodesData, nodesDataSize));
unsigned numEmpty_in_Map = 0;
for (size_t n = 0; n < numNodes && globalNodeIndex < _h.NumInodes; n++, globalNodeIndex++)
{
if ((nodesMap[n >> 3] & ((unsigned)1 << (n & 7))) == 0)
{
numEmpty_in_Map++;
continue;
}
const Byte *p = nodesData + (size_t)n * _h.InodeSize;
if (IsEmptyData(p, _h.InodeSize))
{
if (globalNodeIndex + 1 >= _h.FirstInode)
{
_headersError = true;
// return S_FALSE;
}
continue;
}
CNode node;
PRF(printf("\nnode = %5d ", (unsigned)n));
if (!node.Parse(p, _h))
return S_FALSE;
// PRF(printf("\n %6d", (unsigned)n));
/*
SetUi32(p + 0x7C, 0)
SetUi32(p + 0x82, 0)
UInt32 crc = Crc32C_Calc(_h.Uuid, sizeof(_h.Uuid));
Byte i_le[4];
SetUi32(i_le, n);
crc = Crc32C_Update(crc, i_le, 4);
crc = Crc32C_Update(crc, p, _h.InodeSize);
if (crc != node.Checksum) return S_FALSE;
*/
while (_refs.Size() < globalNodeIndex + 1)
{
// numEmpty++;
_refs.Add(-1);
}
_refs.Add(_nodes.Add(node));
}
numEmpty_in_Maps += numEmpty_in_Map;
if (numEmpty_in_Map != gd.NumFreeInodes)
{
_headersWarning = true;
// return S_FALSE;
}
}
if (numEmpty_in_Maps != _h.NumFreeInodes)
{
// some ext2 examples has incorrect value in _h.NumFreeInodes.
// so we disable check;
_headersWarning = true;
}
if (_refs.Size() <= k_INODE_ROOT)
return S_FALSE;
// printf("\n numReserveInodes = %6d, _refs.Size() = %d, numEmpty = %7d\n", numReserveInodes, _refs.Size(), (unsigned)numEmpty);
}
}
_stream = inStream; // we need stream for dir nodes
{
// ---------- Read Dirs ----------
CByteBuffer dataBuf;
FOR_VECTOR (i, _refs)
{
int nodeIndex = _refs[i];
{
if (nodeIndex < 0)
continue;
const CNode &node = _nodes[nodeIndex];
if (!node.IsDir())
continue;
}
RINOK(ExtractNode(nodeIndex, dataBuf));
if (dataBuf.Size() == 0)
{
// _headersError = true;
return S_FALSE;
}
else
{
RINOK(ParseDir(dataBuf, dataBuf.Size(), i));
}
RINOK(CheckProgress());
}
if (_nodes[_refs[k_INODE_ROOT]].ParentNode != k_INODE_ROOT)
return S_FALSE;
}
{
// ---------- Check NumLinks and unreferenced dir nodes ----------
FOR_VECTOR (i, _refs)
{
int nodeIndex = _refs[i];
if (nodeIndex < 0)
continue;
const CNode &node = _nodes[nodeIndex];
if (node.NumLinks != node.NumLinksCalced)
{
if (node.NumLinks != 1 || node.NumLinksCalced != 0
// ) && i >= _h.FirstInode
)
{
_linksError = true;
// return S_FALSE;
}
}
if (!node.IsDir())
continue;
if (node.ParentNode < 0)
{
if (i >= _h.FirstInode)
return S_FALSE;
continue;
}
}
}
{
// ---------- Check that there is no loops in parents list ----------
unsigned numNodes = _refs.Size();
CIntArr UsedByNode(numNodes);
{
{
for (unsigned i = 0; i < numNodes; i++)
UsedByNode[i] = -1;
}
}
FOR_VECTOR (i, _refs)
{
{
int nodeIndex = _refs[i];
if (nodeIndex < 0)
continue;
const CNode &node = _nodes[nodeIndex];
if (node.ParentNode < 0 // not dir
|| i == k_INODE_ROOT)
continue;
}
unsigned c = i;
for (;;)
{
int nodeIndex = _refs[c];
if (nodeIndex < 0)
return S_FALSE;
CNode &node = _nodes[nodeIndex];
if (UsedByNode[c] != -1)
{
if ((unsigned)UsedByNode[c] == i)
return S_FALSE;
break;
}
UsedByNode[c] = i;
if (node.ParentNode < 0 || node.ParentNode == k_INODE_ROOT)
break;
if ((unsigned)node.ParentNode == i)
return S_FALSE;
c = node.ParentNode;
}
}
}
{
// ---------- Fill SymLinks data ----------
AString s;
CByteBuffer data;
unsigned i;
for (i = 0; i < _refs.Size(); i++)
{
int nodeIndex = _refs[i];
if (nodeIndex < 0)
continue;
CNode &node = _nodes[nodeIndex];
if (!node.IsLink())
continue;
if (node.FileSize > ((UInt32)1 << 14))
continue;
if (ExtractNode(nodeIndex, data) == S_OK && data.Size() != 0)
{
s.SetFrom_CalcLen((const char *)(const Byte *)data, (unsigned)data.Size());
if (s.Len() == data.Size())
node.SymLinkIndex = _symLinks.Add(s);
RINOK(CheckProgress());
}
}
unsigned prev = 0;
unsigned complex = 0;
for (i = 0; i < _items.Size(); i++)
{
CItem &item = _items[i];
int sym = _nodes[_refs[item.Node]].SymLinkIndex;
if (sym >= 0 && item.ParentNode >= 0)
{
item.SymLinkItemIndex = FindTargetItem_for_SymLink(item.ParentNode, _symLinks[sym]);
if (_openCallback)
{
complex++;
if (complex - prev >= (1 << 10))
{
RINOK(CheckProgress2());
prev = complex;
}
}
}
}
}
{
// ---------- Add items and aux folders for unreferenced files ----------
bool useSys = false;
bool useUnknown = false;
FOR_VECTOR (i, _refs)
{
int nodeIndex = _refs[i];
if (nodeIndex < 0)
continue;
const CNode &node = _nodes[nodeIndex];
if (node.NumLinksCalced == 0 /* || i > 100 && i < 150 */) // for debug
{
CItem item;
item.Node = i;
// we don't know how to work with k_INODE_RESIZE node (strange FileSize and Block values).
// so we ignore it;
if (i == k_INODE_RESIZE)
continue;
if (node.FileSize == 0)
continue;
if (i < _h.FirstInode)
{
if (item.Node < ARRAY_SIZE(k_SysInode_Names))
item.Name = k_SysInode_Names[item.Node];
useSys = true;
}
else
useUnknown = true;
if (item.Name.IsEmpty())
{
char temp[16];
ConvertUInt32ToString(item.Node, temp);
item.Name = temp;
}
_items.Add(item);
}
}
if (useSys)
_auxSysIndex = _auxItems.Add("[SYS]");
if (useUnknown)
_auxUnknownIndex = _auxItems.Add("[UNKNOWN]");
}
return S_OK;
}
STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback)
{
COM_TRY_BEGIN
{
Close();
HRESULT res;
try
{
_openCallback = callback;
res = Open2(stream);
}
catch(...)
{
ClearRefs();
throw;
}
if (res != S_OK)
{
ClearRefs();
return res;
}
_stream = stream;
}
return S_OK;
COM_TRY_END
}
void CHandler::ClearRefs()
{
_stream.Release();
_items.Clear();
_nodes.Clear();
_refs.Clear();
_auxItems.Clear();
_symLinks.Clear();
_dirs.Clear();
_auxSysIndex = -1;
_auxUnknownIndex = -1;
}
STDMETHODIMP CHandler::Close()
{
_totalRead = 0;
_totalReadPrev = 0;
_phySize = 0;
_isArc = false;
_headersError = false;
_headersWarning = false;
_linksError = false;
_isUTF = true;
ClearRefs();
return S_OK;
}
void CHandler::GetPath(unsigned index, AString &s) const
{
s.Empty();
if (index >= _items.Size())
{
s = _auxItems[index - _items.Size()];
return;
}
for (;;)
{
const CItem &item = _items[index];
if (!s.IsEmpty())
s.InsertAtFront(CHAR_PATH_SEPARATOR);
s.Insert(0, item.Name);
if (item.ParentNode == k_INODE_ROOT)
return;
if (item.ParentNode < 0)
{
int aux = GetParentAux(item);
if (aux < 0)
break;
s.InsertAtFront(CHAR_PATH_SEPARATOR);
s.Insert(0, _auxItems[aux]);
return;
}
const CNode &node = _nodes[_refs[item.ParentNode]];
if (node.ItemIndex < 0)
return;
index = node.ItemIndex;
if (s.Len() > ((UInt32)1 << 16))
{
s.Insert(0, "[LONG]" STRING_PATH_SEPARATOR);
return;
}
}
}
bool CHandler::GetPackSize(unsigned index, UInt64 &totalPack) const
{
if (index >= _items.Size())
{
totalPack = 0;
return false;
}
const CItem &item = _items[index];
const CNode &node = _nodes[_refs[item.Node]];
// if (!node.IsFlags_EXTENTS())
{
totalPack = (UInt64)node.NumBlocks << (node.IsFlags_HUGE() ? _h.BlockBits : 9);
return true;
}
/*
CExtentTreeHeader eth;
if (!eth.Parse(node.Block))
return false;
if (eth.NumEntries > 3)
return false;
if (!eth.Depth == 0)
return false;
UInt64 numBlocks = 0;
{
for (unsigned i = 0; i < eth.NumEntries; i++)
{
CExtent e;
e.Parse(node.Block + 12 + i * 12);
// const CExtent &e = node.leafs[i];
if (e.IsInited)
numBlocks += e.Len;
}
}
totalPack = numBlocks << _h.BlockBits;
return true;
*/
}
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
{
*numItems = _items.Size() + _auxItems.Size();
return S_OK;
}
enum
{
kpidMountTime = kpidUserDefined,
kpidLastCheckTime,
kpidRevision,
kpidINodeSize,
kpidLastMount,
kpidFeatureIncompat,
kpidFeatureRoCompat,
kpidWrittenKB
// kpidGroupSize,
// kpidChangeTime = kpidUserDefined + 256,
// kpidDTime
};
static const UInt32 kProps[] =
{
kpidPath,
kpidIsDir,
kpidSize,
kpidPackSize,
kpidPosixAttrib,
kpidMTime,
kpidCTime,
kpidATime,
// kpidChangeTime,
// kpidDTime,
kpidINode,
kpidLinks,
kpidSymLink,
kpidCharacts,
kpidUser,
kpidGroup
};
static const CStatProp kArcProps[] =
{
{ NULL, kpidHeadersSize, VT_BSTR },
// { NULL, kpidFileSystem, VT_BSTR },
// kpidMethod,
{ NULL, kpidClusterSize, VT_UI4 },
// { "Group Size", kpidGroupSize, VT_UI8 },
{ NULL, kpidFreeSpace, VT_UI8 },
{ NULL, kpidMTime, VT_FILETIME },
{ NULL, kpidCTime, VT_FILETIME },
{ "Mount Time", kpidMountTime, VT_FILETIME },
{ "Last Check Time", kpidLastCheckTime, VT_FILETIME },
{ NULL, kpidHostOS, VT_BSTR},
{ "Revision", kpidRevision, VT_UI4},
{ "inode Size", kpidINodeSize, VT_UI4},
{ NULL, kpidCodePage, VT_BSTR},
{ NULL, kpidVolumeName, VT_BSTR},
{ "Last Mounted", kpidLastMount, VT_BSTR},
{ NULL, kpidId, VT_BSTR},
{ NULL, kpidCharacts, VT_BSTR },
{ "Incompatible Features", kpidFeatureIncompat, VT_BSTR },
{ "Readonly-compatible Features", kpidFeatureRoCompat, VT_BSTR },
{ "Written KiB", kpidWrittenKB, VT_UI8 }
};
IMP_IInArchive_Props
IMP_IInArchive_ArcProps_WITH_NAME
static void StringToProp(bool isUTF, const char *s, unsigned size, NCOM::CPropVariant &prop)
{
UString u;
AString a;
a.SetFrom_CalcLen(s, size);
if (!isUTF || !ConvertUTF8ToUnicode(a, u))
MultiByteToUnicodeString2(u, a);
prop = u;
}
static void UnixTimeToProp(UInt32 val, NCOM::CPropVariant &prop)
{
if (val != 0)
{
FILETIME ft;
NTime::UnixTimeToFileTime(val, ft);
prop = ft;
}
}
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
{
COM_TRY_BEGIN
NCOM::CPropVariant prop;
switch (propID)
{
/*
case kpidFileSystem:
{
AString res = "Ext4";
prop = res;
break;
}
*/
case kpidIsTree: prop = true; break;
case kpidIsAux: prop = true; break;
case kpidINode: prop = true; break;
case kpidClusterSize: prop = (UInt32)1 << _h.BlockBits; break;
// case kpidGroupSize: prop = (UInt64)_h.BlocksPerGroup << _h.BlockBits; break;
case kpidFreeSpace: prop = (UInt64)_h.NumFreeBlocks << _h.BlockBits; break;
case kpidCTime: UnixTimeToProp(_h.CTime, prop); break;
case kpidMTime: UnixTimeToProp(_h.WriteTime, prop); break;
case kpidMountTime: UnixTimeToProp(_h.MountTime, prop); break;
case kpidLastCheckTime: UnixTimeToProp(_h.LastCheckTime, prop); break;
case kpidHostOS:
{
char temp[16];
const char *s = NULL;
if (_h.CreatorOs < ARRAY_SIZE(kHostOS))
s = kHostOS[_h.CreatorOs];
else
{
ConvertUInt32ToString(_h.CreatorOs, temp);
s = temp;
}
prop = s;
break;
}
case kpidRevision: prop = _h.RevLevel; break;
case kpidINodeSize: prop = _h.InodeSize; break;
case kpidId:
{
if (!IsEmptyData(_h.Uuid, 16))
{
char s[16 * 2 + 2];
for (unsigned i = 0; i < 16; i++)
PrintHex(_h.Uuid[i], s + i * 2);
s[16 * 2] = 0;
prop = s;
}
break;
}
case kpidCodePage: if (_isUTF) prop = "UTF-8"; break;
case kpidShortComment:
case kpidVolumeName:
StringToProp(_isUTF, _h.VolName, sizeof(_h.VolName), prop); break;
case kpidLastMount: StringToProp(_isUTF, _h.LastMount, sizeof(_h.LastMount), prop); break;
case kpidCharacts: FLAGS_TO_PROP(g_FeatureCompat_Flags, _h.FeatureCompat, prop); break;
case kpidFeatureIncompat: FLAGS_TO_PROP(g_FeatureIncompat_Flags, _h.FeatureIncompat, prop); break;
case kpidFeatureRoCompat: FLAGS_TO_PROP(g_FeatureRoCompat_Flags, _h.FeatureRoCompat, prop); break;
case kpidWrittenKB: if (_h.WrittenKB != 0) prop = _h.WrittenKB; break;
case kpidPhySize: prop = _phySize; break;
case kpidErrorFlags:
{
UInt32 v = 0;
if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;;
if (_linksError) v |= kpv_ErrorFlags_HeadersError;
if (_headersError) v |= kpv_ErrorFlags_HeadersError;
if (!_stream && v == 0 && _isArc)
v = kpv_ErrorFlags_HeadersError;
if (v != 0)
prop = v;
break;
}
case kpidWarningFlags:
{
UInt32 v = 0;
if (_headersWarning) v |= kpv_ErrorFlags_HeadersError;
if (v != 0)
prop = v;
break;
}
}
prop.Detach(value);
return S_OK;
COM_TRY_END
}
/*
static const Byte kRawProps[] =
{
// kpidSha1,
};
*/
STDMETHODIMP CHandler::GetNumRawProps(UInt32 *numProps)
{
// *numProps = ARRAY_SIZE(kRawProps);
*numProps = 0;
return S_OK;
}
STDMETHODIMP CHandler::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID)
{
// *propID = kRawProps[index];
*propID = 0;
*name = 0;
return S_OK;
}
STDMETHODIMP CHandler::GetParent(UInt32 index, UInt32 *parent, UInt32 *parentType)
{
*parentType = NParentType::kDir;
*parent = (UInt32)(Int32)-1;
if (index >= _items.Size())
return S_OK;
const CItem &item = _items[index];
if (item.ParentNode < 0)
{
int aux = GetParentAux(item);
if (aux >= 0)
*parent = _items.Size() + aux;
}
else
{
int itemIndex = _nodes[_refs[item.ParentNode]].ItemIndex;
if (itemIndex >= 0)
*parent = itemIndex;
}
return S_OK;
}
STDMETHODIMP CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)
{
*data = NULL;
*dataSize = 0;
*propType = 0;
if (propID == kpidName && _isUTF)
{
if (index < _items.Size())
{
const AString &s = _items[index].Name;
if (!s.IsEmpty())
{
*data = (void *)(const char *)s;
*dataSize = (UInt32)s.Len() + 1;
*propType = NPropDataType::kUtf8z;
}
return S_OK;
}
else
{
const AString &s = _auxItems[index - _items.Size()];
{
*data = (void *)(const char *)s;
*dataSize = (UInt32)s.Len() + 1;
*propType = NPropDataType::kUtf8z;
}
return S_OK;
}
}
return S_OK;
}
static void ExtTimeToProp(const CExtTime &t, NCOM::CPropVariant &prop)
{
/*
UInt32 nano = 0;
if (t.Extra != 0)
{
UInt32 mask = t.Extra & 3;
if (mask != 0)
return;
nano = t.Extra >> 2;
}
UInt64 v;
if (t.Val == 0 && nano == 0)
return;
if (!NTime::UnixTime_to_FileTime64(t.Val, v))
return;
if (nano != 0)
v += nano / 100;
FILETIME ft;
ft.dwLowDateTime = (DWORD)v;
ft.dwHighDateTime = (DWORD)(v >> 32);
prop = ft;
*/
if (t.Val == 0)
return;
if (t.Extra != 0)
{
UInt32 mask = t.Extra & 3;
if (mask != 0)
return;
}
FILETIME ft;
if (NTime::UnixTime64ToFileTime(t.Val, ft))
prop = ft;
}
STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
{
COM_TRY_BEGIN
NCOM::CPropVariant prop;
if (index >= _items.Size())
{
switch (propID)
{
case kpidPath:
case kpidName:
{
AString s = _auxItems[index - _items.Size()];
prop = s;
break;
}
case kpidIsDir: prop = true; break;
case kpidIsAux: prop = true; break;
}
}
else
{
const CItem &item = _items[index];
const CNode &node = _nodes[_refs[item.Node]];
bool isDir = node.IsDir();
switch (propID)
{
case kpidPath:
{
UString u;
{
AString s;
GetPath(index, s);
if (!_isUTF || !ConvertUTF8ToUnicode(s, u))
MultiByteToUnicodeString2(u, s);
}
prop = u;
break;
}
case kpidName:
{
{
UString u;
{
if (!_isUTF || !ConvertUTF8ToUnicode(item.Name, u))
MultiByteToUnicodeString2(u, item.Name);
}
prop = u;
}
break;
}
case kpidIsDir:
{
bool isDir2 = isDir;
if (item.SymLinkItemIndex >= 0)
isDir2 = _nodes[_refs[_items[item.SymLinkItemIndex].Node]].IsDir();
prop = isDir2;
break;
}
case kpidSize: if (!isDir) prop = node.FileSize; break;
case kpidPackSize:
if (!isDir)
{
UInt64 size;
if (GetPackSize(index, size))
prop = size;
}
break;
case kpidPosixAttrib:
{
/*
if (node.Type != 0 && node.Type < ARRAY_SIZE(k_TypeToMode))
prop = (UInt32)(node.Mode & 0xFFF) | k_TypeToMode[node.Type];
*/
prop = (UInt32)(node.Mode);
break;
}
case kpidMTime: ExtTimeToProp(node.MTime, prop); break;
case kpidCTime: ExtTimeToProp(node.CTime, prop); break;
case kpidATime: ExtTimeToProp(node.ATime, prop); break;
// case kpidDTime: ExtTimeToProp(node.DTime, prop); break;
// case kpidChangeTime: ExtTimeToProp(node.InodeChangeTime, prop); break;
case kpidUser: prop = (UInt32)node.Uid; break;
case kpidGroup: prop = (UInt32)node.Gid; break;
case kpidLinks: prop = node.NumLinks; break;
case kpidINode: prop = (UInt32)item.Node; break;
case kpidStreamId: if (!isDir) prop = (UInt32)item.Node; break;
case kpidCharacts: FLAGS_TO_PROP(g_NodeFlags, (UInt32)node.Flags, prop); break;
case kpidSymLink:
{
if (node.SymLinkIndex >= 0)
{
UString u;
{
const AString &s = _symLinks[node.SymLinkIndex];
if (!_isUTF || !ConvertUTF8ToUnicode(s, u))
MultiByteToUnicodeString2(u, s);
}
prop = u;
}
break;
}
}
}
prop.Detach(value);
return S_OK;
COM_TRY_END
}
class CClusterInStream2:
public IInStream,
public CMyUnknownImp
{
UInt64 _virtPos;
UInt64 _physPos;
UInt32 _curRem;
public:
unsigned BlockBits;
UInt64 Size;
CMyComPtr<IInStream> Stream;
CRecordVector<UInt32> Vector;
HRESULT SeekToPhys() { return Stream->Seek(_physPos, STREAM_SEEK_SET, NULL); }
HRESULT InitAndSeek()
{
_curRem = 0;
_virtPos = 0;
_physPos = 0;
if (Vector.Size() > 0)
{
_physPos = (Vector[0] << BlockBits);
return SeekToPhys();
}
return S_OK;
}
MY_UNKNOWN_IMP2(ISequentialInStream, IInStream)
STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
};
STDMETHODIMP CClusterInStream2::Read(void *data, UInt32 size, UInt32 *processedSize)
{
if (processedSize)
*processedSize = 0;
if (_virtPos >= Size)
return S_OK;
{
UInt64 rem = Size - _virtPos;
if (size > rem)
size = (UInt32)rem;
}
if (size == 0)
return S_OK;
if (_curRem == 0)
{
const UInt32 blockSize = (UInt32)1 << BlockBits;
const UInt32 virtBlock = (UInt32)(_virtPos >> BlockBits);
const UInt32 offsetInBlock = (UInt32)_virtPos & (blockSize - 1);
const UInt32 phyBlock = Vector[virtBlock];
if (phyBlock == 0)
{
UInt32 cur = blockSize - offsetInBlock;
if (cur > size)
cur = size;
memset(data, 0, cur);
_virtPos += cur;
if (processedSize)
*processedSize = cur;
return S_OK;
}
UInt64 newPos = ((UInt64)phyBlock << BlockBits) + offsetInBlock;
if (newPos != _physPos)
{
_physPos = newPos;
RINOK(SeekToPhys());
}
_curRem = blockSize - offsetInBlock;
for (unsigned i = 1; i < 64 && (virtBlock + i) < (UInt32)Vector.Size() && phyBlock + i == Vector[virtBlock + i]; i++)
_curRem += (UInt32)1 << BlockBits;
}
if (size > _curRem)
size = _curRem;
HRESULT res = Stream->Read(data, size, &size);
if (processedSize)
*processedSize = size;
_physPos += size;
_virtPos += size;
_curRem -= size;
return res;
}
STDMETHODIMP CClusterInStream2::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
{
switch (seekOrigin)
{
case STREAM_SEEK_SET: break;
case STREAM_SEEK_CUR: offset += _virtPos; break;
case STREAM_SEEK_END: offset += Size; break;
default: return STG_E_INVALIDFUNCTION;
}
if (offset < 0)
return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
if (_virtPos != (UInt64)offset)
_curRem = 0;
_virtPos = offset;
if (newPosition)
*newPosition = offset;
return S_OK;
}
class CExtInStream:
public IInStream,
public CMyUnknownImp
{
UInt64 _virtPos;
UInt64 _phyPos;
public:
unsigned BlockBits;
UInt64 Size;
CMyComPtr<IInStream> Stream;
CRecordVector<CExtent> Extents;
CExtInStream() {}
HRESULT StartSeek()
{
_virtPos = 0;
_phyPos = 0;
return Stream->Seek(_phyPos, STREAM_SEEK_SET, NULL);
}
MY_UNKNOWN_IMP2(ISequentialInStream, IInStream)
STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
};
STDMETHODIMP CExtInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
{
if (processedSize)
*processedSize = 0;
if (_virtPos >= Size)
return S_OK;
{
UInt64 rem = Size - _virtPos;
if (size > rem)
size = (UInt32)rem;
}
if (size == 0)
return S_OK;
UInt32 blockIndex = (UInt32)(_virtPos >> BlockBits);
unsigned left = 0, right = Extents.Size();
for (;;)
{
unsigned mid = (left + right) / 2;
if (mid == left)
break;
if (blockIndex < Extents[mid].VirtBlock)
right = mid;
else
left = mid;
}
{
const CExtent &extent = Extents[left];
if (blockIndex < extent.VirtBlock)
return E_FAIL;
UInt32 bo = blockIndex - extent.VirtBlock;
if (bo >= extent.Len)
return E_FAIL;
UInt32 offset = ((UInt32)_virtPos & (((UInt32)1 << BlockBits) - 1));
UInt32 remBlocks = extent.Len - bo;
UInt64 remBytes = ((UInt64)remBlocks << BlockBits);
remBytes -= offset;
if (size > remBytes)
size = (UInt32)remBytes;
if (!extent.IsInited)
{
memset(data, 0, size);
_virtPos += size;
if (processedSize)
*processedSize = size;
return S_OK;
}
UInt64 phyBlock = extent.PhyStart + bo;
UInt64 phy = (phyBlock << BlockBits) + offset;
if (phy != _phyPos)
{
RINOK(Stream->Seek(phy, STREAM_SEEK_SET, NULL));
_phyPos = phy;
}
UInt32 realProcessSize = 0;
HRESULT res = Stream->Read(data, size, &realProcessSize);
_phyPos += realProcessSize;
_virtPos += realProcessSize;
if (processedSize)
*processedSize = realProcessSize;
return res;
}
}
STDMETHODIMP CExtInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
{
switch (seekOrigin)
{
case STREAM_SEEK_SET: break;
case STREAM_SEEK_CUR: offset += _virtPos; break;
case STREAM_SEEK_END: offset += Size; break;
default: return STG_E_INVALIDFUNCTION;
}
if (offset < 0)
return HRESULT_WIN32_ERROR_NEGATIVE_SEEK;
_virtPos = offset;
if (newPosition)
*newPosition = offset;
return S_OK;
}
HRESULT CHandler::FillFileBlocks2(UInt32 block, unsigned level, unsigned numBlocks, CRecordVector<UInt32> &blocks)
{
const size_t blockSize = (size_t)1 << _h.BlockBits;
CByteBuffer &tempBuf = _tempBufs[level];
tempBuf.Alloc(blockSize);
PRF2(printf("\n level = %d, block = %7d", level, (unsigned)block));
RINOK(SeekAndRead(_stream, block, tempBuf, blockSize));
const Byte *p = tempBuf;
size_t num = (size_t)1 << (_h.BlockBits - 2);
for (size_t i = 0; i < num; i++)
{
if (blocks.Size() == numBlocks)
break;
UInt32 val = GetUi32(p + 4 * i);
if (val >= _h.NumBlocks)
return S_FALSE;
if (level != 0)
{
if (val == 0)
{
/*
size_t num = (size_t)1 << ((_h.BlockBits - 2) * (level));
PRF2(printf("\n num empty = %3d", (unsigned)num));
for (size_t k = 0; k < num; k++)
{
blocks.Add(0);
if (blocks.Size() == numBlocks)
return S_OK;
}
continue;
*/
return S_FALSE;
}
RINOK(FillFileBlocks2(val, level - 1, numBlocks, blocks));
continue;
}
PRF2(printf("\n i = %3d, blocks.Size() = %6d, block = %5d ", i, blocks.Size(), (unsigned)val));
PRF(printf("\n i = %3d, start = %5d ", (unsigned)i, (unsigned)val));
blocks.Add(val);
}
return S_OK;
}
static const unsigned kNumDirectNodeBlocks = 12;
HRESULT CHandler::FillFileBlocks(const Byte *p, unsigned numBlocks, CRecordVector<UInt32> &blocks)
{
// ext2 supports zero blocks (blockIndex == 0).
blocks.ClearAndReserve(numBlocks);
for (unsigned i = 0; i < kNumDirectNodeBlocks; i++)
{
if (i == numBlocks)
return S_OK;
UInt32 val = GetUi32(p + 4 * i);
if (val >= _h.NumBlocks)
return S_FALSE;
blocks.Add(val);
}
for (unsigned level = 0; level < 3; level++)
{
if (blocks.Size() == numBlocks)
break;
UInt32 val = GetUi32(p + 4 * (kNumDirectNodeBlocks + level));
if (val >= _h.NumBlocks)
return S_FALSE;
if (val == 0)
{
/*
size_t num = (size_t)1 << ((_h.BlockBits - 2) * (level + 1));
for (size_t k = 0; k < num; k++)
{
blocks.Add(0);
if (blocks.Size() == numBlocks)
return S_OK;
}
continue;
*/
return S_FALSE;
}
RINOK(FillFileBlocks2(val, level, numBlocks, blocks));
}
return S_OK;
}
static void AddSkipExtents(CRecordVector<CExtent> &extents, UInt32 virtBlock, UInt32 numBlocks)
{
while (numBlocks != 0)
{
UInt32 len = numBlocks;
const UInt32 kLenMax = (UInt32)1 << 15;
if (len > kLenMax)
len = kLenMax;
CExtent e;
e.VirtBlock = virtBlock;
e.Len = (UInt16)len;
e.IsInited = false;
e.PhyStart = 0;
extents.Add(e);
virtBlock += len;
numBlocks -= len;
}
}
static bool UpdateExtents(CRecordVector<CExtent> &extents, UInt32 block)
{
if (extents.IsEmpty())
{
if (block == 0)
return true;
AddSkipExtents(extents, 0, block);
return true;
}
const CExtent &prev = extents.Back();
if (block < prev.VirtBlock)
return false;
UInt32 prevEnd = prev.GetVirtEnd();
if (block == prevEnd)
return true;
AddSkipExtents(extents, prevEnd, block - prevEnd);
return true;
}
HRESULT CHandler::FillExtents(const Byte *p, size_t size, CRecordVector<CExtent> &extents, int parentDepth)
{
CExtentTreeHeader eth;
if (!eth.Parse(p))
return S_FALSE;
if (parentDepth >= 0 && eth.Depth != parentDepth - 1) // (eth.Depth >= parentDepth)
return S_FALSE;
if (12 + 12 * (size_t)eth.NumEntries > size)
return S_FALSE;
if (eth.Depth >= kNumTreeLevelsMax)
return S_FALSE;
if (eth.Depth == 0)
{
for (unsigned i = 0; i < eth.NumEntries; i++)
{
CExtent e;
e.Parse(p + 12 + i * 12);
if (e.PhyStart == 0
|| e.PhyStart > _h.NumBlocks
|| e.PhyStart + e.Len > _h.NumBlocks
|| !e.IsLenOK())
return S_FALSE;
if (!UpdateExtents(extents, e.VirtBlock))
return S_FALSE;
extents.Add(e);
}
return S_OK;
}
const size_t blockSize = (size_t)1 << _h.BlockBits;
CByteBuffer &tempBuf = _tempBufs[eth.Depth];
tempBuf.Alloc(blockSize);
for (unsigned i = 0; i < eth.NumEntries; i++)
{
CExtentIndexNode e;
e.Parse(p + 12 + i * 12);
if (e.PhyLeaf == 0 || e.PhyLeaf >= _h.NumBlocks)
return S_FALSE;
if (!UpdateExtents(extents, e.VirtBlock))
return S_FALSE;
RINOK(SeekAndRead(_stream, e.PhyLeaf, tempBuf, blockSize));
RINOK(FillExtents(tempBuf, blockSize, extents, eth.Depth));
}
return S_OK;
}
HRESULT CHandler::GetStream_Node(unsigned nodeIndex, ISequentialInStream **stream)
{
COM_TRY_BEGIN
*stream = NULL;
const CNode &node = _nodes[nodeIndex];
if (!node.IsFlags_EXTENTS())
{
// maybe sparse file can have (node.NumBlocks == 0) ?
/* The following code doesn't work correctly for some CentOS images,
where there are nodes with inline data and (node.NumBlocks != 0).
If you know better way to detect inline data, please notify 7-Zip developers. */
if (node.NumBlocks == 0 && node.FileSize < kNodeBlockFieldSize)
{
Create_BufInStream_WithNewBuffer(node.Block, (size_t)node.FileSize, stream);
return S_OK;
}
}
if (node.FileSize >= ((UInt64)1 << 63))
return S_FALSE;
CMyComPtr<IInStream> streamTemp;
UInt64 numBlocks64 = (node.FileSize + (UInt64)(((UInt32)1 << _h.BlockBits) - 1)) >> _h.BlockBits;
if (node.IsFlags_EXTENTS())
{
if ((UInt32)numBlocks64 != numBlocks64)
return S_FALSE;
CExtInStream *streamSpec = new CExtInStream;
streamTemp = streamSpec;
streamSpec->BlockBits = _h.BlockBits;
streamSpec->Size = node.FileSize;
streamSpec->Stream = _stream;
RINOK(FillExtents(node.Block, kNodeBlockFieldSize, streamSpec->Extents, -1));
UInt32 end = 0;
if (!streamSpec->Extents.IsEmpty())
end = streamSpec->Extents.Back().GetVirtEnd();
if (end < numBlocks64)
{
AddSkipExtents(streamSpec->Extents, end, (UInt32)(numBlocks64 - end));
// return S_FALSE;
}
RINOK(streamSpec->StartSeek());
}
else
{
{
UInt64 numBlocks2 = numBlocks64;
if (numBlocks64 > kNumDirectNodeBlocks)
{
UInt64 rem = numBlocks64 - kNumDirectNodeBlocks;
const unsigned refBits = (_h.BlockBits - 2);
const size_t numRefsInBlocks = (size_t)1 << refBits;
numBlocks2++;
if (rem > numRefsInBlocks)
{
numBlocks2++;
const UInt64 numL2 = (rem - 1) >> refBits;
numBlocks2 += numL2;
if (numL2 > numRefsInBlocks)
{
numBlocks2++;
numBlocks2 += (numL2 - 1) >> refBits;
}
}
}
const unsigned specBits = (node.IsFlags_HUGE() ? 0 : _h.BlockBits - 9);
const UInt32 specMask = ((UInt32)1 << specBits) - 1;;
if ((node.NumBlocks & specMask) != 0)
return S_FALSE;
const UInt64 numBlocks64_from_header = node.NumBlocks >> specBits;
if (numBlocks64_from_header < numBlocks2)
{
// why (numBlocks64_from_header > numBlocks2) in some cases?
// return S_FALSE;
}
}
unsigned numBlocks = (unsigned)numBlocks64;
if (numBlocks != numBlocks64)
return S_FALSE;
CClusterInStream2 *streamSpec = new CClusterInStream2;
streamTemp = streamSpec;
streamSpec->BlockBits = _h.BlockBits;
streamSpec->Size = node.FileSize;
streamSpec->Stream = _stream;
RINOK(FillFileBlocks(node.Block, numBlocks, streamSpec->Vector));
streamSpec->InitAndSeek();
}
*stream = streamTemp.Detach();
return S_OK;
COM_TRY_END
}
HRESULT CHandler::ExtractNode(unsigned nodeIndex, CByteBuffer &data)
{
data.Free();
const CNode &node = _nodes[nodeIndex];
size_t size = (size_t)node.FileSize;
if (size != node.FileSize)
return S_FALSE;
CMyComPtr<ISequentialInStream> inSeqStream;
RINOK(GetStream_Node(nodeIndex, &inSeqStream));
if (!inSeqStream)
return S_FALSE;
data.Alloc(size);
_totalRead += size;
return ReadStream_FALSE(inSeqStream, data, size);
}
STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
Int32 testMode, IArchiveExtractCallback *extractCallback)
{
COM_TRY_BEGIN
bool allFilesMode = (numItems == (UInt32)(Int32)-1);
if (allFilesMode)
numItems = _items.Size() + _auxItems.Size();
if (numItems == 0)
return S_OK;
UInt64 totalSize = 0;
UInt32 i;
for (i = 0; i < numItems; i++)
{
UInt32 index = allFilesMode ? i : indices[i];
if (index >= _items.Size())
continue;
const CItem &item = _items[index];
const CNode &node = _nodes[_refs[item.Node]];
if (!node.IsDir())
totalSize += node.FileSize;
}
extractCallback->SetTotal(totalSize);
UInt64 totalPackSize;
totalSize = totalPackSize = 0;
NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
CLocalProgress *lps = new CLocalProgress;
CMyComPtr<ICompressProgressInfo> progress = lps;
lps->Init(extractCallback, false);
for (i = 0;; i++)
{
lps->InSize = totalPackSize;
lps->OutSize = totalSize;
RINOK(lps->SetCur());
if (i == numItems)
break;
CMyComPtr<ISequentialOutStream> outStream;
Int32 askMode = testMode ?
NExtract::NAskMode::kTest :
NExtract::NAskMode::kExtract;
UInt32 index = allFilesMode ? i : indices[i];
RINOK(extractCallback->GetStream(index, &outStream, askMode));
if (index >= _items.Size())
{
RINOK(extractCallback->PrepareOperation(askMode));
RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
continue;
}
const CItem &item = _items[index];
const CNode &node = _nodes[_refs[item.Node]];
if (node.IsDir())
{
RINOK(extractCallback->PrepareOperation(askMode));
RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
continue;
}
UInt64 unpackSize = node.FileSize;
totalSize += unpackSize;
UInt64 packSize;
if (GetPackSize(index, packSize))
totalPackSize += packSize;
if (!testMode && !outStream)
continue;
RINOK(extractCallback->PrepareOperation(askMode));
int res = NExtract::NOperationResult::kDataError;
{
CMyComPtr<ISequentialInStream> inSeqStream;
HRESULT hres = GetStream(index, &inSeqStream);
if (hres == S_FALSE || !inSeqStream)
{
if (hres == E_OUTOFMEMORY)
return hres;
res = NExtract::NOperationResult::kUnsupportedMethod;
}
else
{
RINOK(hres);
{
hres = copyCoder->Code(inSeqStream, outStream, NULL, NULL, progress);
if (hres == S_OK)
{
if (copyCoderSpec->TotalSize == unpackSize)
res = NExtract::NOperationResult::kOK;
}
else if (hres == E_NOTIMPL)
{
res = NExtract::NOperationResult::kUnsupportedMethod;
}
else if (hres != S_FALSE)
{
RINOK(hres);
}
}
}
}
RINOK(extractCallback->SetOperationResult(res));
}
return S_OK;
COM_TRY_END
}
STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
{
*stream = NULL;
if (index >= _items.Size())
return S_FALSE;
return GetStream_Node(_refs[_items[index].Node], stream);
}
API_FUNC_static_IsArc IsArc_Ext(const Byte *p, size_t size)
{
if (size < kHeaderSize)
return k_IsArc_Res_NEED_MORE;
CHeader h;
if (!h.Parse(p + kHeaderDataOffset))
return k_IsArc_Res_NO;
return k_IsArc_Res_YES;
}
}
static const Byte k_Signature[] = { 0x53, 0xEF };
REGISTER_ARC_I(
"Ext", "ext ext2 ext3 ext4 img", 0, 0xC7,
k_Signature,
0x438,
0,
IsArc_Ext)
}}