From be3b47d0d504a3409ce66bd77bb8c0acff87c4f5 Mon Sep 17 00:00:00 2001 From: kh1 Date: Thu, 15 Mar 2012 14:53:47 +0100 Subject: Reorganize the tree, have better ifw.pri. Shadow build support. Change-Id: I01fb12537f863ed0744979973c7e4153889cc5cb Reviewed-by: Tim Jenssen --- src/libs/7zip/win/CPP/7zip/Archive/NtfsHandler.cpp | 1764 ++++++++++++++++++++ 1 file changed, 1764 insertions(+) create mode 100644 src/libs/7zip/win/CPP/7zip/Archive/NtfsHandler.cpp (limited to 'src/libs/7zip/win/CPP/7zip/Archive/NtfsHandler.cpp') diff --git a/src/libs/7zip/win/CPP/7zip/Archive/NtfsHandler.cpp b/src/libs/7zip/win/CPP/7zip/Archive/NtfsHandler.cpp new file mode 100644 index 000000000..505486fc5 --- /dev/null +++ b/src/libs/7zip/win/CPP/7zip/Archive/NtfsHandler.cpp @@ -0,0 +1,1764 @@ +// NtfsHandler.cpp + +#include "StdAfx.h" + +// #define SHOW_DEBUG_INFO +// #define SHOW_DEBUG_INFO2 + +#if defined(SHOW_DEBUG_INFO) || defined(SHOW_DEBUG_INFO2) +#include +#endif + +#include "../../../C/CpuArch.h" + +#include "Common/Buffer.h" +#include "Common/ComTry.h" +#include "Common/IntToString.h" +#include "Common/MyCom.h" +#include "Common/StringConvert.h" + +#include "Windows/PropVariant.h" +#include "Windows/Time.h" + +#include "../Common/ProgressUtils.h" +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +#include "Common/DummyOutStream.h" + +#ifdef SHOW_DEBUG_INFO +#define PRF(x) x +#else +#define PRF(x) +#endif + +#ifdef SHOW_DEBUG_INFO2 +#define PRF2(x) x +#else +#define PRF2(x) +#endif + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) +#define Get64(p) GetUi64(p) + +#define G16(p, dest) dest = Get16(p); +#define G32(p, dest) dest = Get32(p); +#define G64(p, dest) dest = Get64(p); + +namespace NArchive { +namespace Ntfs { + +static const UInt32 kNumSysRecs = 16; +static const UInt32 kRecIndex_Volume = 3; +static const UInt32 kRecIndex_BadClus = 8; + +struct CHeader +{ + Byte SectorSizeLog; + Byte ClusterSizeLog; + // Byte MediaType; + UInt32 NumHiddenSectors; + UInt64 NumClusters; + UInt64 MftCluster; + UInt64 SerialNumber; + UInt16 SectorsPerTrack; + UInt16 NumHeads; + + UInt64 GetPhySize() const { return NumClusters << ClusterSizeLog; } + UInt32 ClusterSize() const { return (UInt32)1 << ClusterSizeLog; } + bool Parse(const Byte *p); +}; + +static int GetLog(UInt32 num) +{ + for (int i = 0; i < 31; i++) + if (((UInt32)1 << i) == num) + return i; + return -1; +} + +bool CHeader::Parse(const Byte *p) +{ + if (p[0x1FE] != 0x55 || p[0x1FF] != 0xAA) + return false; + + int codeOffset = 0; + switch (p[0]) + { + case 0xE9: codeOffset = 3 + (Int16)Get16(p + 1); break; + case 0xEB: if (p[2] != 0x90) return false; codeOffset = 2 + (signed char)p[1]; break; + default: return false; + } + Byte sectorsPerClusterLog; + + if (memcmp(p + 3, "NTFS ", 8) != 0) + return false; + { + int s = GetLog(Get16(p + 11)); + if (s < 9 || s > 12) + return false; + SectorSizeLog = (Byte)s; + s = GetLog(p[13]); + if (s < 0) + return false; + sectorsPerClusterLog = (Byte)s; + ClusterSizeLog = SectorSizeLog + sectorsPerClusterLog; + } + + for (int i = 14; i < 21; i++) + if (p[i] != 0) + return false; + + // MediaType = p[21]; + if (Get16(p + 22) != 0) // NumFatSectors + return false; + G16(p + 24, SectorsPerTrack); + G16(p + 26, NumHeads); + G32(p + 28, NumHiddenSectors); + if (Get32(p + 32) != 0) // NumSectors32 + return false; + + // DriveNumber = p[0x24]; + if (p[0x25] != 0) // CurrentHead + return false; + /* + NTFS-HDD: p[0x26] = 0x80 + NTFS-FLASH: p[0x26] = 0 + */ + if (p[0x26] != 0x80 && p[0x26] != 0) // ExtendedBootSig + return false; + if (p[0x27] != 0) // reserved + return false; + UInt64 numSectors = Get64(p + 0x28); + NumClusters = numSectors >> sectorsPerClusterLog; + + G64(p + 0x30, MftCluster); + // G64(p + 0x38, Mft2Cluster); + G64(p + 0x48, SerialNumber); + UInt32 numClustersInMftRec; + UInt32 numClustersInIndexBlock; + G32(p + 0x40, numClustersInMftRec); + G32(p + 0x44, numClustersInIndexBlock); + return (numClustersInMftRec < 256 && numClustersInIndexBlock < 256); +} + +struct CMftRef +{ + UInt64 Val; + UInt64 GetIndex() const { return Val & (((UInt64)1 << 48) - 1); } + UInt16 GetNumber() const { return (UInt16)(Val >> 48); } + bool IsBaseItself() const { return Val == 0; } +}; + +#define ATNAME(n) ATTR_TYPE_ ## n +#define DEF_ATTR_TYPE(v, n) ATNAME(n) = v + +enum +{ + DEF_ATTR_TYPE(0x00, UNUSED), + DEF_ATTR_TYPE(0x10, STANDARD_INFO), + DEF_ATTR_TYPE(0x20, ATTRIBUTE_LIST), + DEF_ATTR_TYPE(0x30, FILE_NAME), + DEF_ATTR_TYPE(0x40, OBJECT_ID), + DEF_ATTR_TYPE(0x50, SECURITY_DESCRIPTOR), + DEF_ATTR_TYPE(0x60, VOLUME_NAME), + DEF_ATTR_TYPE(0x70, VOLUME_INFO), + DEF_ATTR_TYPE(0x80, DATA), + DEF_ATTR_TYPE(0x90, INDEX_ROOT), + DEF_ATTR_TYPE(0xA0, INDEX_ALLOCATION), + DEF_ATTR_TYPE(0xB0, BITMAP), + DEF_ATTR_TYPE(0xC0, REPARSE_POINT), + DEF_ATTR_TYPE(0xD0, EA_INFO), + DEF_ATTR_TYPE(0xE0, EA), + DEF_ATTR_TYPE(0xF0, PROPERTY_SET), + DEF_ATTR_TYPE(0x100, LOGGED_UTILITY_STREAM), + DEF_ATTR_TYPE(0x1000, FIRST_USER_DEFINED_ATTRIBUTE) +}; + +static const Byte kFileNameType_Posix = 0; +static const Byte kFileNameType_Win32 = 1; +static const Byte kFileNameType_Dos = 2; +static const Byte kFileNameType_Win32Dos = 3; + +struct CFileNameAttr +{ + CMftRef ParentDirRef; + // UInt64 CTime; + // UInt64 MTime; + // UInt64 ThisRecMTime; + // UInt64 ATime; + // UInt64 AllocatedSize; + // UInt64 DataSize; + // UInt16 PackedEaSize; + UString Name; + UInt32 Attrib; + Byte NameType; + + bool IsDos() const { return NameType == kFileNameType_Dos; } + bool Parse(const Byte *p, unsigned size); +}; + +static void GetString(const Byte *p, unsigned length, UString &res) +{ + wchar_t *s = res.GetBuffer(length); + for (unsigned i = 0; i < length; i++) + s[i] = Get16(p + i * 2); + s[length] = 0; + res.ReleaseBuffer(); +} + +bool CFileNameAttr::Parse(const Byte *p, unsigned size) +{ + if (size < 0x42) + return false; + G64(p + 0x00, ParentDirRef.Val); + // G64(p + 0x08, CTime); + // G64(p + 0x10, MTime); + // G64(p + 0x18, ThisRecMTime); + // G64(p + 0x20, ATime); + // G64(p + 0x28, AllocatedSize); + // G64(p + 0x30, DataSize); + G32(p + 0x38, Attrib); + // G16(p + 0x3C, PackedEaSize); + NameType = p[0x41]; + unsigned length = p[0x40]; + if (0x42 + length > size) + return false; + GetString(p + 0x42, length, Name); + return true; +} + +struct CSiAttr +{ + UInt64 CTime; + UInt64 MTime; + // UInt64 ThisRecMTime; + UInt64 ATime; + UInt32 Attrib; + + /* + UInt32 MaxVersions; + UInt32 Version; + UInt32 ClassId; + UInt32 OwnerId; + UInt32 SecurityId; + UInt64 QuotaCharged; + */ + + bool Parse(const Byte *p, unsigned size); +}; + +bool CSiAttr::Parse(const Byte *p, unsigned size) +{ + if (size < 0x24) + return false; + G64(p + 0x00, CTime); + G64(p + 0x08, MTime); + // G64(p + 0x10, ThisRecMTime); + G64(p + 0x18, ATime); + G32(p + 0x20, Attrib); + return true; +} + +static const UInt64 kEmptyExtent = (UInt64)(Int64)-1; + +struct CExtent +{ + UInt64 Virt; + UInt64 Phy; + + bool IsEmpty() const { return Phy == kEmptyExtent; } +}; + +struct CVolInfo +{ + Byte MajorVer; + Byte MinorVer; + // UInt16 Flags; + + bool Parse(const Byte *p, unsigned size); +}; + +bool CVolInfo::Parse(const Byte *p, unsigned size) +{ + if (size < 12) + return false; + MajorVer = p[8]; + MinorVer = p[9]; + // Flags = Get16(p + 10); + return true; +} + +struct CAttr +{ + UInt32 Type; + // UInt32 Length; + UString Name; + // UInt16 Flags; + // UInt16 Instance; + CByteBuffer Data; + Byte NonResident; + + // Non-Resident + Byte CompressionUnit; + UInt64 LowVcn; + UInt64 HighVcn; + UInt64 AllocatedSize; + UInt64 Size; + UInt64 PackSize; + UInt64 InitializedSize; + + // Resident + // UInt16 ResidentFlags; + + bool IsCompressionUnitSupported() const { return CompressionUnit == 0 || CompressionUnit == 4; } + + UInt32 Parse(const Byte *p, unsigned size); + bool ParseFileName(CFileNameAttr &a) const { return a.Parse(Data, (unsigned)Data.GetCapacity()); } + bool ParseSi(CSiAttr &a) const { return a.Parse(Data, (unsigned)Data.GetCapacity()); } + bool ParseVolInfo(CVolInfo &a) const { return a.Parse(Data, (unsigned)Data.GetCapacity()); } + bool ParseExtents(CRecordVector &extents, UInt64 numClustersMax, int compressionUnit) const; + UInt64 GetSize() const { return NonResident ? Size : Data.GetCapacity(); } + UInt64 GetPackSize() const + { + if (!NonResident) + return Data.GetCapacity(); + if (CompressionUnit != 0) + return PackSize; + return AllocatedSize; + } +}; + +#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } + +static int CompareAttr(void *const *elem1, void *const *elem2, void *) +{ + const CAttr &a1 = *(*((const CAttr **)elem1)); + const CAttr &a2 = *(*((const CAttr **)elem2)); + RINOZ(MyCompare(a1.Type, a2.Type)); + RINOZ(MyCompare(a1.Name, a2.Name)); + return MyCompare(a1.LowVcn, a2.LowVcn); +} + +UInt32 CAttr::Parse(const Byte *p, unsigned size) +{ + if (size < 4) + return 0; + G32(p, Type); + if (Type == 0xFFFFFFFF) + return 4; + if (size < 0x18) + return 0; + PRF(printf(" T=%2X", Type)); + + UInt32 length = Get32(p + 0x04); + PRF(printf(" L=%3d", length)); + if (length > size) + return 0; + NonResident = p[0x08]; + { + int nameLength = p[9]; + UInt32 nameOffset = Get16(p + 0x0A); + if (nameLength != 0) + { + if (nameOffset + nameLength * 2 > length) + return 0; + GetString(p + nameOffset, nameLength, Name); + PRF(printf(" N=%S", Name)); + } + } + + // G16(p + 0x0C, Flags); + // G16(p + 0x0E, Instance); + // PRF(printf(" F=%4X", Flags)); + // PRF(printf(" Inst=%d", Instance)); + + UInt32 dataSize; + UInt32 offs; + if (NonResident) + { + if (length < 0x40) + return 0; + PRF(printf(" NR")); + G64(p + 0x10, LowVcn); + G64(p + 0x18, HighVcn); + G64(p + 0x28, AllocatedSize); + G64(p + 0x30, Size); + G64(p + 0x38, InitializedSize); + G16(p + 0x20, offs); + CompressionUnit = p[0x22]; + + PackSize = Size; + if (CompressionUnit != 0) + { + if (length < 0x48) + return 0; + G64(p + 0x40, PackSize); + PRF(printf(" PS=%I64x", PackSize)); + } + + // PRF(printf("\n")); + PRF(printf(" ASize=%4I64d", AllocatedSize)); + PRF(printf(" Size=%I64d", Size)); + PRF(printf(" IS=%I64d", InitializedSize)); + PRF(printf(" Low=%I64d", LowVcn)); + PRF(printf(" High=%I64d", HighVcn)); + PRF(printf(" CU=%d", (int)CompressionUnit)); + dataSize = length - offs; + } + else + { + if (length < 0x18) + return 0; + PRF(printf(" RES")); + dataSize = Get32(p + 0x10); + PRF(printf(" dataSize=%3d", dataSize)); + offs = Get16(p + 0x14); + // G16(p + 0x16, ResidentFlags); + // PRF(printf(" ResFlags=%4X", ResidentFlags)); + } + if (offs > length || dataSize > length || length - dataSize < offs) + return 0; + Data.SetCapacity(dataSize); + memcpy(Data, p + offs, dataSize); + #ifdef SHOW_DEBUG_INFO + PRF(printf(" : ")); + for (unsigned i = 0; i < Data.GetCapacity(); i++) + { + PRF(printf(" %02X", (int)Data[i])); + } + #endif + return length; +} + +bool CAttr::ParseExtents(CRecordVector &extents, UInt64 numClustersMax, int compressionUnit) const +{ + const Byte *p = Data; + unsigned size = (unsigned)Data.GetCapacity(); + UInt64 vcn = LowVcn; + UInt64 lcn = 0; + UInt64 highVcn1 = HighVcn + 1; + if (LowVcn != extents.Back().Virt || highVcn1 > (UInt64)1 << 63) + return false; + + extents.DeleteBack(); + + PRF2(printf("\n# ParseExtents # LowVcn = %4I64X # HighVcn = %4I64X", LowVcn, HighVcn)); + + while (size > 0) + { + Byte b = *p++; + size--; + if (b == 0) + break; + UInt32 num = b & 0xF; + if (num == 0 || num > 8 || num > size) + return false; + + int i; + UInt64 vSize = p[num - 1]; + for (i = (int)num - 2; i >= 0; i--) + vSize = (vSize << 8) | p[i]; + if (vSize == 0) + return false; + p += num; + size -= num; + if ((highVcn1 - vcn) < vSize) + return false; + + num = (b >> 4) & 0xF; + if (num > 8 || num > size) + return false; + CExtent e; + e.Virt = vcn; + if (num == 0) + { + if (compressionUnit == 0) + return false; + e.Phy = kEmptyExtent; + } + else + { + Int64 v = (signed char)p[num - 1]; + for (i = (int)num - 2; i >= 0; i--) + v = (v << 8) | p[i]; + p += num; + size -= num; + lcn += v; + if (lcn > numClustersMax) + return false; + e.Phy = lcn; + } + extents.Add(e); + vcn += vSize; + } + CExtent e; + e.Phy = kEmptyExtent; + e.Virt = vcn; + extents.Add(e); + return (highVcn1 == vcn); +} + +static const UInt64 kEmptyTag = (UInt64)(Int64)-1; + +static const int kNumCacheChunksLog = 1; +static const UInt32 kNumCacheChunks = (1 << kNumCacheChunksLog); + +class CInStream: + public IInStream, + public CMyUnknownImp +{ + UInt64 _virtPos; + UInt64 _physPos; + UInt64 _curRem; + bool _sparseMode; + size_t _compressedPos; + + UInt64 _tags[kNumCacheChunks]; + int _chunkSizeLog; + CByteBuffer _inBuf; + CByteBuffer _outBuf; +public: + CMyComPtr Stream; + UInt64 Size; + UInt64 InitializedSize; + int BlockSizeLog; + int CompressionUnit; + bool InUse; + CRecordVector Extents; + + HRESULT SeekToPhys() { return Stream->Seek(_physPos, STREAM_SEEK_SET, NULL); } + + UInt32 GetCuSize() const { return (UInt32)1 << (BlockSizeLog + CompressionUnit); } + HRESULT InitAndSeek(int compressionUnit) + { + CompressionUnit = compressionUnit; + if (compressionUnit != 0) + { + UInt32 cuSize = GetCuSize(); + _inBuf.SetCapacity(cuSize); + _chunkSizeLog = BlockSizeLog + CompressionUnit; + _outBuf.SetCapacity(kNumCacheChunks << _chunkSizeLog); + } + for (int i = 0; i < kNumCacheChunks; i++) + _tags[i] = kEmptyTag; + + _sparseMode = false; + _curRem = 0; + _virtPos = 0; + _physPos = 0; + const CExtent &e = Extents[0]; + if (!e.IsEmpty()) + _physPos = e.Phy << BlockSizeLog; + return SeekToPhys(); + } + + MY_UNKNOWN_IMP1(IInStream) + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); +}; + +static size_t Lznt1Dec(Byte *dest, size_t outBufLim, size_t destLen, const Byte *src, size_t srcLen) +{ + size_t destSize = 0; + while (destSize < destLen) + { + if (srcLen < 2 || (destSize & 0xFFF) != 0) + break; + UInt32 v = Get16(src); + if (v == 0) + break; + src += 2; + srcLen -= 2; + UInt32 comprSize = (v & 0xFFF) + 1; + if (comprSize > srcLen) + break; + srcLen -= comprSize; + if ((v & 0x8000) == 0) + { + if (comprSize != (1 << 12)) + break; + memcpy(dest + destSize, src, comprSize); + src += comprSize; + destSize += comprSize; + } + else + { + if (destSize + (1 << 12) > outBufLim || (src[0] & 1) != 0) + return 0; + int numDistBits = 4; + UInt32 sbOffset = 0; + UInt32 pos = 0; + + do + { + comprSize--; + for (UInt32 mask = src[pos++] | 0x100; mask > 1 && comprSize > 0; mask >>= 1) + { + if ((mask & 1) == 0) + { + if (sbOffset >= (1 << 12)) + return 0; + dest[destSize++] = src[pos++]; + sbOffset++; + comprSize--; + } + else + { + if (comprSize < 2) + return 0; + UInt32 v = Get16(src + pos); + pos += 2; + comprSize -= 2; + + while (((sbOffset - 1) >> numDistBits) != 0) + numDistBits++; + + UInt32 len = (v & (0xFFFF >> numDistBits)) + 3; + if (sbOffset + len > (1 << 12)) + return 0; + UInt32 dist = (v >> (16 - numDistBits)); + if (dist >= sbOffset) + return 0; + Int32 offs = -1 - dist; + Byte *p = dest + destSize; + for (UInt32 t = 0; t < len; t++) + p[t] = p[t + offs]; + destSize += len; + sbOffset += len; + } + } + } + while (comprSize > 0); + src += pos; + } + } + return destSize; +} + +STDMETHODIMP CInStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize != NULL) + *processedSize = 0; + if (_virtPos >= Size) + return (Size == _virtPos) ? S_OK: E_FAIL; + if (size == 0) + return S_OK; + UInt64 rem = Size - _virtPos; + if (size > rem) + size = (UInt32)rem; + if (_virtPos >= InitializedSize) + { + memset((Byte *)data, 0, size); + _virtPos += size; + *processedSize = size; + return S_OK; + } + rem = InitializedSize - _virtPos; + if (size > rem) + size = (UInt32)rem; + + while (_curRem == 0) + { + UInt64 cacheTag = _virtPos >> _chunkSizeLog; + UInt32 cacheIndex = (UInt32)cacheTag & (kNumCacheChunks - 1); + if (_tags[cacheIndex] == cacheTag) + { + UInt32 chunkSize = (UInt32)1 << _chunkSizeLog; + UInt32 offset = (UInt32)_virtPos & (chunkSize - 1); + UInt32 cur = MyMin(chunkSize - offset, size); + memcpy(data, _outBuf + (cacheIndex << _chunkSizeLog) + offset, cur); + *processedSize = cur; + _virtPos += cur; + return S_OK; + } + + PRF2(printf("\nVirtPos = %6d", _virtPos)); + + UInt32 comprUnitSize = (UInt32)1 << CompressionUnit; + UInt64 virtBlock = _virtPos >> BlockSizeLog; + UInt64 virtBlock2 = virtBlock & ~((UInt64)comprUnitSize - 1); + + int left = 0, right = Extents.Size(); + for (;;) + { + int mid = (left + right) / 2; + if (mid == left) + break; + if (virtBlock2 < Extents[mid].Virt) + right = mid; + else + left = mid; + } + + bool isCompressed = false; + UInt64 virtBlock2End = virtBlock2 + comprUnitSize; + if (CompressionUnit != 0) + for (int i = left; i < Extents.Size(); i++) + { + const CExtent &e = Extents[i]; + if (e.Virt >= virtBlock2End) + break; + if (e.IsEmpty()) + { + isCompressed = true; + break; + } + } + + int i; + for (i = left; Extents[i + 1].Virt <= virtBlock; i++); + + _sparseMode = false; + if (!isCompressed) + { + const CExtent &e = Extents[i]; + UInt64 newPos = (e.Phy << BlockSizeLog) + _virtPos - (e.Virt << BlockSizeLog); + if (newPos != _physPos) + { + _physPos = newPos; + RINOK(SeekToPhys()); + } + UInt64 next = Extents[i + 1].Virt; + if (next > virtBlock2End) + next &= ~((UInt64)comprUnitSize - 1); + next <<= BlockSizeLog; + if (next > Size) + next = Size; + _curRem = next - _virtPos; + break; + } + bool thereArePhy = false; + for (int i2 = left; i2 < Extents.Size(); i2++) + { + const CExtent &e = Extents[i2]; + if (e.Virt >= virtBlock2End) + break; + if (!e.IsEmpty()) + { + thereArePhy = true; + break; + } + } + if (!thereArePhy) + { + _curRem = (Extents[i + 1].Virt << BlockSizeLog) - _virtPos; + _sparseMode = true; + break; + } + + size_t offs = 0; + UInt64 curVirt = virtBlock2; + for (i = left; i < Extents.Size(); i++) + { + const CExtent &e = Extents[i]; + if (e.IsEmpty()) + break; + if (e.Virt >= virtBlock2End) + return S_FALSE; + UInt64 newPos = (e.Phy + (curVirt - e.Virt)) << BlockSizeLog; + if (newPos != _physPos) + { + _physPos = newPos; + RINOK(SeekToPhys()); + } + UInt64 numChunks = Extents[i + 1].Virt - curVirt; + if (curVirt + numChunks > virtBlock2End) + numChunks = virtBlock2End - curVirt; + size_t compressed = (size_t)numChunks << BlockSizeLog; + RINOK(ReadStream_FALSE(Stream, _inBuf + offs, compressed)); + curVirt += numChunks; + _physPos += compressed; + offs += compressed; + } + size_t destLenMax = GetCuSize(); + size_t destLen = destLenMax; + UInt64 rem = Size - (virtBlock2 << BlockSizeLog); + if (destLen > rem) + destLen = (size_t)rem; + + Byte *dest = _outBuf + (cacheIndex << _chunkSizeLog); + size_t destSizeRes = Lznt1Dec(dest, destLenMax, destLen, _inBuf, offs); + _tags[cacheIndex] = cacheTag; + + // some files in Vista have destSize > destLen + if (destSizeRes < destLen) + { + memset(dest, 0, destLenMax); + if (InUse) + return S_FALSE; + } + } + if (size > _curRem) + size = (UInt32)_curRem; + HRESULT res = S_OK; + if (_sparseMode) + memset(data, 0, size); + else + { + res = Stream->Read(data, size, &size); + _physPos += size; + } + if (processedSize != NULL) + *processedSize = size; + _virtPos += size; + _curRem -= size; + return res; +} + +STDMETHODIMP CInStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + UInt64 newVirtPos = offset; + switch(seekOrigin) + { + case STREAM_SEEK_SET: break; + case STREAM_SEEK_CUR: newVirtPos += _virtPos; break; + case STREAM_SEEK_END: newVirtPos += Size; break; + default: return STG_E_INVALIDFUNCTION; + } + if (_virtPos != newVirtPos) + _curRem = 0; + _virtPos = newVirtPos; + if (newPosition) + *newPosition = newVirtPos; + return S_OK; +} + +class CByteBufStream: + public IInStream, + public CMyUnknownImp +{ + UInt64 _virtPos; +public: + CByteBuffer Buf; + void Init() { _virtPos = 0; } + + MY_UNKNOWN_IMP1(IInStream) + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); +}; + +STDMETHODIMP CByteBufStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize != NULL) + *processedSize = 0; + if (_virtPos >= Buf.GetCapacity()) + return (_virtPos == Buf.GetCapacity()) ? S_OK: E_FAIL; + UInt64 rem = Buf.GetCapacity() - _virtPos; + if (rem < size) + size = (UInt32)rem; + memcpy(data, Buf + (size_t)_virtPos, size); + if (processedSize != NULL) + *processedSize = size; + _virtPos += size; + return S_OK; +} + +STDMETHODIMP CByteBufStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + switch(seekOrigin) + { + case STREAM_SEEK_SET: _virtPos = offset; break; + case STREAM_SEEK_CUR: _virtPos += offset; break; + case STREAM_SEEK_END: _virtPos = Buf.GetCapacity() + offset; break; + default: return STG_E_INVALIDFUNCTION; + } + if (newPosition) + *newPosition = _virtPos; + return S_OK; +} + +static HRESULT DataParseExtents(int clusterSizeLog, const CObjectVector &attrs, + int attrIndex, int attrIndexLim, UInt64 numPhysClusters, CRecordVector &Extents) +{ + CExtent e; + e.Virt = 0; + e.Phy = kEmptyExtent; + Extents.Add(e); + const CAttr &attr0 = attrs[attrIndex]; + + if (attr0.AllocatedSize < attr0.Size || + (attrs[attrIndexLim - 1].HighVcn + 1) != (attr0.AllocatedSize >> clusterSizeLog) || + (attr0.AllocatedSize & ((1 << clusterSizeLog) - 1)) != 0) + return S_FALSE; + + for (int i = attrIndex; i < attrIndexLim; i++) + if (!attrs[i].ParseExtents(Extents, numPhysClusters, attr0.CompressionUnit)) + return S_FALSE; + + UInt64 packSizeCalc = 0; + for (int k = 0; k < Extents.Size(); k++) + { + CExtent &e = Extents[k]; + if (!e.IsEmpty()) + packSizeCalc += (Extents[k + 1].Virt - e.Virt) << clusterSizeLog; + PRF2(printf("\nSize = %4I64X", Extents[k + 1].Virt - e.Virt)); + PRF2(printf(" Pos = %4I64X", e.Phy)); + } + + if (attr0.CompressionUnit != 0) + { + if (packSizeCalc != attr0.PackSize) + return S_FALSE; + } + else + { + if (packSizeCalc != attr0.AllocatedSize) + return S_FALSE; + } + return S_OK; +} + +struct CDataRef +{ + int Start; + int Num; +}; + +static const UInt32 kMagic_FILE = 0x454c4946; +static const UInt32 kMagic_BAAD = 0x44414142; + +struct CMftRec +{ + UInt32 Magic; + // UInt64 Lsn; + UInt16 SeqNumber; + UInt16 Flags; + // UInt16 LinkCount; + // UInt16 NextAttrInstance; + CMftRef BaseMftRef; + // UInt32 ThisRecNumber; + UInt32 MyNumNameLinks; + + CObjectVector DataAttrs; + CObjectVector FileNames; + CRecordVector DataRefs; + + CSiAttr SiAttr; + + void MoveAttrsFrom(CMftRec &src) + { + DataAttrs += src.DataAttrs; + FileNames += src.FileNames; + src.DataAttrs.ClearAndFree(); + src.FileNames.ClearAndFree(); + } + + UInt64 GetPackSize() const + { + UInt64 res = 0; + for (int i = 0; i < DataRefs.Size(); i++) + res += DataAttrs[DataRefs[i].Start].GetPackSize(); + return res; + } + + bool Parse(Byte *p, int sectorSizeLog, UInt32 numSectors, UInt32 recNumber, CObjectVector *attrs); + + bool IsEmpty() const { return (Magic <= 2); } + bool IsFILE() const { return (Magic == kMagic_FILE); } + bool IsBAAD() const { return (Magic == kMagic_BAAD); } + + bool InUse() const { return (Flags & 1) != 0; } + bool IsDir() const { return (Flags & 2) != 0; } + + void ParseDataNames(); + HRESULT GetStream(IInStream *mainStream, int dataIndex, + int clusterSizeLog, UInt64 numPhysClusters, IInStream **stream) const; + int GetNumExtents(int dataIndex, int clusterSizeLog, UInt64 numPhysClusters) const; + + UInt64 GetSize(int dataIndex) const { return DataAttrs[DataRefs[dataIndex].Start].GetSize(); } + + CMftRec(): MyNumNameLinks(0) {} +}; + +void CMftRec::ParseDataNames() +{ + DataRefs.Clear(); + DataAttrs.Sort(CompareAttr, 0); + + for (int i = 0; i < DataAttrs.Size();) + { + CDataRef ref; + ref.Start = i; + for (i++; i < DataAttrs.Size(); i++) + if (DataAttrs[ref.Start].Name != DataAttrs[i].Name) + break; + ref.Num = i - ref.Start; + DataRefs.Add(ref); + } +} + +HRESULT CMftRec::GetStream(IInStream *mainStream, int dataIndex, + int clusterSizeLog, UInt64 numPhysClusters, IInStream **destStream) const +{ + *destStream = 0; + CByteBufStream *streamSpec = new CByteBufStream; + CMyComPtr streamTemp = streamSpec; + + if (dataIndex < 0) + return E_FAIL; + + if (dataIndex < DataRefs.Size()) + { + const CDataRef &ref = DataRefs[dataIndex]; + int numNonResident = 0; + int i; + for (i = ref.Start; i < ref.Start + ref.Num; i++) + if (DataAttrs[i].NonResident) + numNonResident++; + + const CAttr &attr0 = DataAttrs[ref.Start]; + + if (numNonResident != 0 || ref.Num != 1) + { + if (numNonResident != ref.Num || !attr0.IsCompressionUnitSupported()) + return S_FALSE; + CInStream *streamSpec = new CInStream; + CMyComPtr streamTemp = streamSpec; + RINOK(DataParseExtents(clusterSizeLog, DataAttrs, ref.Start, ref.Start + ref.Num, numPhysClusters, streamSpec->Extents)); + streamSpec->Size = attr0.Size; + streamSpec->InitializedSize = attr0.InitializedSize; + streamSpec->Stream = mainStream; + streamSpec->BlockSizeLog = clusterSizeLog; + streamSpec->InUse = InUse(); + RINOK(streamSpec->InitAndSeek(attr0.CompressionUnit)); + *destStream = streamTemp.Detach(); + return S_OK; + } + streamSpec->Buf = attr0.Data; + } + streamSpec->Init(); + *destStream = streamTemp.Detach(); + return S_OK; +} + +int CMftRec::GetNumExtents(int dataIndex, int clusterSizeLog, UInt64 numPhysClusters) const +{ + if (dataIndex < 0) + return 0; + { + const CDataRef &ref = DataRefs[dataIndex]; + int numNonResident = 0; + int i; + for (i = ref.Start; i < ref.Start + ref.Num; i++) + if (DataAttrs[i].NonResident) + numNonResident++; + + const CAttr &attr0 = DataAttrs[ref.Start]; + + if (numNonResident != 0 || ref.Num != 1) + { + if (numNonResident != ref.Num || !attr0.IsCompressionUnitSupported()) + return 0; // error; + CRecordVector extents; + if (DataParseExtents(clusterSizeLog, DataAttrs, ref.Start, ref.Start + ref.Num, numPhysClusters, extents) != S_OK) + return 0; // error; + return extents.Size() - 1; + } + // if (attr0.Data.GetCapacity() != 0) + // return 1; + return 0; + } +} + +bool CMftRec::Parse(Byte *p, int sectorSizeLog, UInt32 numSectors, UInt32 recNumber, + CObjectVector *attrs) +{ + G32(p, Magic); + if (!IsFILE()) + return IsEmpty() || IsBAAD(); + + UInt32 usaOffset; + UInt32 numUsaItems; + G16(p + 0x04, usaOffset); + G16(p + 0x06, numUsaItems); + + if ((usaOffset & 1) != 0 || usaOffset + numUsaItems * 2 > ((UInt32)1 << sectorSizeLog) - 2 || + numUsaItems == 0 || numUsaItems - 1 != numSectors) + return false; + + UInt16 usn = Get16(p + usaOffset); + // PRF(printf("\nusn = %d", usn)); + for (UInt32 i = 1; i < numUsaItems; i++) + { + void *pp = p + (i << sectorSizeLog) - 2; + if (Get16(pp) != usn) + return false; + SetUi16(pp, Get16(p + usaOffset + i * 2)); + } + + // G64(p + 0x08, Lsn); + G16(p + 0x10, SeqNumber); + // G16(p + 0x12, LinkCount); + // PRF(printf(" L=%d", LinkCount)); + UInt32 attrOffs = Get16(p + 0x14); + G16(p + 0x16, Flags); + PRF(printf(" F=%4X", Flags)); + + UInt32 bytesInUse = Get32(p + 0x18); + UInt32 bytesAlloc = Get32(p + 0x1C); + G64(p + 0x20, BaseMftRef.Val); + if (BaseMftRef.Val != 0) + { + PRF(printf(" BaseRef=%d", (int)BaseMftRef.Val)); + // return false; // Check it; + } + // G16(p + 0x28, NextAttrInstance); + if (usaOffset >= 0x30) + if (Get32(p + 0x2C) != recNumber) // NTFS 3.1+ + return false; + + UInt32 limit = numSectors << sectorSizeLog; + if (attrOffs >= limit || (attrOffs & 7) != 0 || bytesInUse > limit + || bytesAlloc != limit) + return false; + + + for (UInt32 t = attrOffs; t < limit;) + { + CAttr attr; + // PRF(printf("\n %2d:", Attrs.Size())); + PRF(printf("\n")); + UInt32 length = attr.Parse(p + t, limit - t); + if (length == 0 || limit - t < length) + return false; + t += length; + if (attr.Type == 0xFFFFFFFF) + break; + switch(attr.Type) + { + case ATTR_TYPE_FILE_NAME: + { + CFileNameAttr fna; + if (!attr.ParseFileName(fna)) + return false; + FileNames.Add(fna); + PRF(printf(" flags = %4x", (int)fna.NameType)); + PRF(printf("\n %S", fna.Name)); + break; + } + case ATTR_TYPE_STANDARD_INFO: + if (!attr.ParseSi(SiAttr)) + return false; + break; + case ATTR_TYPE_DATA: + DataAttrs.Add(attr); + break; + default: + if (attrs) + attrs->Add(attr); + break; + } + } + + return true; +} + +struct CItem +{ + int RecIndex; + int DataIndex; + CMftRef ParentRef; + UString Name; + UInt32 Attrib; + + bool IsDir() const { return (DataIndex < 0); } +}; + +struct CDatabase +{ + CHeader Header; + CObjectVector Items; + CObjectVector Recs; + CMyComPtr InStream; + IArchiveOpenCallback *OpenCallback; + + CByteBuffer ByteBuf; + + CObjectVector VolAttrs; + + ~CDatabase() { ClearAndClose(); } + + void Clear(); + void ClearAndClose(); + + UString GetItemPath(Int32 index) const; + HRESULT Open(); + HRESULT ReadDir(Int32 parent, UInt32 cluster, int level); + + HRESULT SeekToCluster(UInt64 cluster); + + int FindMtfRec(const CMftRef &ref) const + { + UInt64 val = ref.GetIndex(); + int left = 0, right = Items.Size(); + while (left != right) + { + int mid = (left + right) / 2; + UInt64 midValue = Items[mid].RecIndex; + if (val == midValue) + return mid; + if (val < midValue) + right = mid; + else + left = mid + 1; + } + return -1; + } + +}; + +HRESULT CDatabase::SeekToCluster(UInt64 cluster) +{ + return InStream->Seek(cluster << Header.ClusterSizeLog, STREAM_SEEK_SET, NULL); +} + +void CDatabase::Clear() +{ + Items.Clear(); + Recs.Clear(); +} + +void CDatabase::ClearAndClose() +{ + Clear(); + InStream.Release(); +} + +#define MY_DIR_PREFIX(x) L"[" x L"]" WSTRING_PATH_SEPARATOR + +UString CDatabase::GetItemPath(Int32 index) const +{ + const CItem *item = &Items[index]; + UString name = item->Name; + for (int j = 0; j < 256; j++) + { + CMftRef ref = item->ParentRef; + index = FindMtfRec(ref); + if (ref.GetIndex() == 5) + return name; + if (index < 0 || Recs[Items[index].RecIndex].SeqNumber != ref.GetNumber()) + return MY_DIR_PREFIX(L"UNKNOWN") + name; + item = &Items[index]; + name = item->Name + WCHAR_PATH_SEPARATOR + name; + } + return MY_DIR_PREFIX(L"BAD") + name; +} + +HRESULT CDatabase::Open() +{ + Clear(); + + static const UInt32 kHeaderSize = 512; + Byte buf[kHeaderSize]; + RINOK(ReadStream_FALSE(InStream, buf, kHeaderSize)); + if (!Header.Parse(buf)) + return S_FALSE; + UInt64 fileSize; + RINOK(InStream->Seek(0, STREAM_SEEK_END, &fileSize)); + if (fileSize < Header.GetPhySize()) + return S_FALSE; + + SeekToCluster(Header.MftCluster); + + CMftRec mftRec; + UInt32 numSectorsInRec; + int recSizeLog; + CMyComPtr mftStream; + { + UInt32 blockSize = 1 << 12; + ByteBuf.SetCapacity(blockSize); + RINOK(ReadStream_FALSE(InStream, ByteBuf, blockSize)); + + UInt32 allocSize = Get32(ByteBuf + 0x1C); + recSizeLog = GetLog(allocSize); + if (recSizeLog < Header.SectorSizeLog) + return false; + numSectorsInRec = 1 << (recSizeLog - Header.SectorSizeLog); + if (!mftRec.Parse(ByteBuf, Header.SectorSizeLog, numSectorsInRec, NULL, 0)) + return S_FALSE; + if (!mftRec.IsFILE()) + return S_FALSE; + mftRec.ParseDataNames(); + if (mftRec.DataRefs.IsEmpty()) + return S_FALSE; + RINOK(mftRec.GetStream(InStream, 0, Header.ClusterSizeLog, Header.NumClusters, &mftStream)); + if (!mftStream) + return S_FALSE; + } + + UInt64 mftSize = mftRec.DataAttrs[0].Size; + if ((mftSize >> 4) > Header.GetPhySize()) + return S_FALSE; + + UInt64 numFiles = mftSize >> recSizeLog; + if (numFiles > (1 << 30)) + return S_FALSE; + if (OpenCallback) + { + RINOK(OpenCallback->SetTotal(&numFiles, &mftSize)); + } + const UInt32 kBufSize = (1 << 15); + if (kBufSize < (1 << recSizeLog)) + return S_FALSE; + + ByteBuf.SetCapacity((size_t)kBufSize); + Recs.Reserve((int)numFiles); + for (UInt64 pos64 = 0;;) + { + if (OpenCallback) + { + UInt64 numFiles = Recs.Size(); + if ((numFiles & 0x3FF) == 0) + { + RINOK(OpenCallback->SetCompleted(&numFiles, &pos64)); + } + } + UInt32 readSize = kBufSize; + UInt64 rem = mftSize - pos64; + if (readSize > rem) + readSize = (UInt32)rem; + if (readSize < ((UInt32)1 << recSizeLog)) + break; + RINOK(ReadStream_FALSE(mftStream, ByteBuf, (size_t)readSize)); + pos64 += readSize; + for (int i = 0; ((UInt32)(i + 1) << recSizeLog) <= readSize; i++) + { + PRF(printf("\n---------------------")); + PRF(printf("\n%5d:", Recs.Size())); + Byte *p = ByteBuf + ((UInt32)i << recSizeLog); + CMftRec rec; + if (!rec.Parse(p, Header.SectorSizeLog, numSectorsInRec, (UInt32)Recs.Size(), + (Recs.Size() == kRecIndex_Volume) ? &VolAttrs: NULL)) + return S_FALSE; + Recs.Add(rec); + } + } + + int i; + for (i = 0; i < Recs.Size(); i++) + { + CMftRec &rec = Recs[i]; + if (!rec.BaseMftRef.IsBaseItself()) + { + UInt64 refIndex = rec.BaseMftRef.GetIndex(); + if (refIndex > (UInt32)Recs.Size()) + return S_FALSE; + CMftRec &refRec = Recs[(int)refIndex]; + bool moveAttrs = (refRec.SeqNumber == rec.BaseMftRef.GetNumber() && refRec.BaseMftRef.IsBaseItself()); + if (rec.InUse() && refRec.InUse()) + { + if (!moveAttrs) + return S_FALSE; + } + else if (rec.InUse() || refRec.InUse()) + moveAttrs = false; + if (moveAttrs) + refRec.MoveAttrsFrom(rec); + } + } + + for (i = 0; i < Recs.Size(); i++) + Recs[i].ParseDataNames(); + + for (i = 0; i < Recs.Size(); i++) + { + CMftRec &rec = Recs[i]; + if (!rec.IsFILE() || !rec.BaseMftRef.IsBaseItself()) + continue; + int numNames = 0; + // printf("\n%4d: ", i); + for (int t = 0; t < rec.FileNames.Size(); t++) + { + const CFileNameAttr &fna = rec.FileNames[t]; + // printf("%4d %S | ", (int)fna.NameType, fna.Name); + if (fna.IsDos()) + continue; + int numDatas = rec.DataRefs.Size(); + + // For hard linked files we show substreams only for first Name. + if (numDatas > 1 && numNames > 0) + numDatas = 1; + numNames++; + + if (rec.IsDir()) + { + CItem item; + item.Name = fna.Name; + item.RecIndex = i; + item.DataIndex = -1; + item.ParentRef = fna.ParentDirRef; + item.Attrib = rec.SiAttr.Attrib | 0x10; + // item.Attrib = fna.Attrib; + Items.Add(item); + } + for (int di = 0; di < numDatas; di++) + { + CItem item; + item.Name = fna.Name; + item.Attrib = rec.SiAttr.Attrib; + const UString &subName = rec.DataAttrs[rec.DataRefs[di].Start].Name; + if (!subName.IsEmpty()) + { + // $BadClus:$Bad is sparse file for all clusters. So we skip it. + if (i == kRecIndex_BadClus && subName == L"$Bad") + continue; + item.Name += L":"; + item.Name += subName; + item.Attrib = fna.Attrib; + } + + PRF(printf("\n%3d", i)); + PRF(printf(" attrib=%2x", rec.SiAttr.Attrib)); + PRF(printf(" %S", item.Name)); + + item.RecIndex = i; + item.DataIndex = di; + item.ParentRef = fna.ParentDirRef; + + Items.Add(item); + rec.MyNumNameLinks++; + } + } + rec.FileNames.ClearAndFree(); + } + + return S_OK; +} + +class CHandler: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp, + CDatabase +{ +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive(;) + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + +STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + IInStream *stream2; + const CItem &item = Items[index]; + const CMftRec &rec = Recs[item.RecIndex]; + HRESULT res = rec.GetStream(InStream, item.DataIndex, Header.ClusterSizeLog, Header.NumClusters, &stream2); + *stream = (ISequentialInStream *)stream2; + return res; + COM_TRY_END +} + +static const STATPROPSTG kProps[] = +{ + { NULL, kpidPath, VT_BSTR}, + { NULL, kpidIsDir, VT_BOOL}, + { NULL, kpidSize, VT_UI8}, + { NULL, kpidPackSize, VT_UI8}, + { NULL, kpidMTime, VT_FILETIME}, + { NULL, kpidCTime, VT_FILETIME}, + { NULL, kpidATime, VT_FILETIME}, + { NULL, kpidAttrib, VT_UI4}, + { NULL, kpidLinks, VT_UI4}, + { NULL, kpidNumBlocks, VT_UI4} +}; + +static const STATPROPSTG kArcProps[] = +{ + { NULL, kpidVolumeName, VT_BSTR}, + { NULL, kpidFileSystem, VT_BSTR}, + { NULL, kpidClusterSize, VT_UI4}, + { NULL, kpidPhySize, VT_UI8}, + { NULL, kpidHeadersSize, VT_UI8}, + { NULL, kpidCTime, VT_FILETIME}, + + { NULL, kpidSectorSize, VT_UI4}, + { NULL, kpidId, VT_UI8} + // { NULL, kpidSectorsPerTrack, VT_UI4}, + // { NULL, kpidNumHeads, VT_UI4}, + // { NULL, kpidHiddenSectors, VT_UI4} +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +static void NtfsTimeToProp(UInt64 t, NWindows::NCOM::CPropVariant &prop) +{ + FILETIME ft; + ft.dwLowDateTime = (DWORD)t; + ft.dwHighDateTime = (DWORD)(t >> 32); + prop = ft; +} + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + + const CMftRec *volRec = (Recs.Size() > kRecIndex_Volume ? &Recs[kRecIndex_Volume] : NULL); + + switch(propID) + { + case kpidClusterSize: prop = Header.ClusterSize(); break; + case kpidPhySize: prop = Header.GetPhySize(); break; + /* + case kpidHeadersSize: + { + UInt64 val = 0; + for (int i = 0; i < kNumSysRecs; i++) + { + printf("\n%2d: %8I64d ", i, Recs[i].GetPackSize()); + if (i == 8) + i = i + val += Recs[i].GetPackSize(); + } + prop = val; + break; + } + */ + case kpidCTime: if (volRec) NtfsTimeToProp(volRec->SiAttr.CTime, prop); break;break; + case kpidVolumeName: + { + for (int i = 0; i < VolAttrs.Size(); i++) + { + const CAttr &attr = VolAttrs[i]; + if (attr.Type == ATTR_TYPE_VOLUME_NAME) + { + UString name; + GetString(attr.Data, (int)attr.Data.GetCapacity() / 2, name); + prop = name; + break; + } + } + break; + } + case kpidFileSystem: + { + AString s = "NTFS"; + for (int i = 0; i < VolAttrs.Size(); i++) + { + const CAttr &attr = VolAttrs[i]; + if (attr.Type == ATTR_TYPE_VOLUME_INFO) + { + CVolInfo vi; + if (attr.ParseVolInfo(vi)) + { + s += ' '; + char temp[16]; + ConvertUInt32ToString(vi.MajorVer, temp); + s += temp; + s += '.'; + ConvertUInt32ToString(vi.MinorVer, temp); + s += temp; + } + break; + } + } + prop = s; + break; + } + case kpidSectorSize: prop = (UInt32)1 << Header.SectorSizeLog; break; + case kpidId: prop = Header.SerialNumber; break; + // case kpidMediaType: prop = Header.MediaType; break; + // case kpidSectorsPerTrack: prop = Header.SectorsPerTrack; break; + // case kpidNumHeads: prop = Header.NumHeads; break; + // case kpidHiddenSectors: prop = Header.NumHiddenSectors; break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NWindows::NCOM::CPropVariant prop; + const CItem &item = Items[index]; + const CMftRec &rec = Recs[item.RecIndex]; + + const CAttr *data= NULL; + if (item.DataIndex >= 0) + data = &rec.DataAttrs[rec.DataRefs[item.DataIndex].Start]; + + switch(propID) + { + case kpidPath: + { + UString name = GetItemPath(index); + const wchar_t *prefix = NULL; + if (!rec.InUse()) + prefix = MY_DIR_PREFIX(L"DELETED"); + else if (item.RecIndex < kNumSysRecs) + prefix = MY_DIR_PREFIX(L"SYSTEM"); + if (prefix) + name = prefix + name; + prop = name; + break; + } + + case kpidIsDir: prop = item.IsDir(); break; + case kpidMTime: NtfsTimeToProp(rec.SiAttr.MTime, prop); break; + + case kpidCTime: NtfsTimeToProp(rec.SiAttr.CTime, prop); break; + case kpidATime: NtfsTimeToProp(rec.SiAttr.ATime, prop); break; + case kpidAttrib: + prop = item.Attrib; + break; + case kpidLinks: prop = rec.MyNumNameLinks; break; + case kpidSize: if (data) prop = data->GetSize(); break; + case kpidPackSize: if (data) prop = data->GetPackSize(); break; + case kpidNumBlocks: if (data) prop = (UInt32)rec.GetNumExtents(item.DataIndex, Header.ClusterSizeLog, Header.NumClusters); break; + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + { + OpenCallback = callback; + InStream = stream; + HRESULT res; + try + { + res = CDatabase::Open(); + if (res == S_OK) + return S_OK; + } + catch(...) + { + Close(); + throw; + } + Close(); + return res; + } + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + ClearAndClose(); + return S_OK; +} + +STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)-1); + if (allFilesMode) + numItems = Items.Size(); + if (numItems == 0) + return S_OK; + UInt32 i; + UInt64 totalSize = 0; + for (i = 0; i < numItems; i++) + { + const CItem &item = Items[allFilesMode ? i : indices[i]]; + const CMftRec &rec = Recs[item.RecIndex]; + if (!rec.IsDir()) + totalSize += rec.GetSize(item.DataIndex); + } + RINOK(extractCallback->SetTotal(totalSize)); + + UInt64 totalPackSize; + totalSize = totalPackSize = 0; + + CByteBuffer buf; + UInt32 clusterSize = Header.ClusterSize(); + buf.SetCapacity(clusterSize); + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr progress = lps; + lps->Init(extractCallback, false); + + CDummyOutStream *outStreamSpec = new CDummyOutStream; + CMyComPtr outStream(outStreamSpec); + + for (i = 0; i < numItems; i++) + { + lps->InSize = totalPackSize; + lps->OutSize = totalSize; + RINOK(lps->SetCur()); + CMyComPtr realOutStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); + + const CItem &item = Items[index]; + if (item.IsDir()) + { + RINOK(extractCallback->PrepareOperation(askMode)); + RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); + continue; + } + + if (!testMode && !realOutStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + + outStreamSpec->SetStream(realOutStream); + realOutStream.Release(); + outStreamSpec->Init(); + + const CMftRec &rec = Recs[item.RecIndex]; + const CAttr &data = rec.DataAttrs[rec.DataRefs[item.DataIndex].Start]; + + int res = NExtract::NOperationResult::kDataError; + { + CMyComPtr inStream; + HRESULT hres = rec.GetStream(InStream, item.DataIndex, Header.ClusterSizeLog, Header.NumClusters, &inStream); + if (hres == S_FALSE) + res = NExtract::NOperationResult::kUnSupportedMethod; + else + { + RINOK(hres); + if (inStream) + { + HRESULT hres = copyCoder->Code(inStream, outStream, NULL, NULL, progress); + if (hres != S_OK && hres != S_FALSE) + { + RINOK(hres); + } + if (/* copyCoderSpec->TotalSize == item.GetSize() && */ hres == S_OK) + res = NExtract::NOperationResult::kOK; + } + } + } + totalPackSize += data.GetPackSize(); + totalSize += data.GetSize(); + outStreamSpec->ReleaseStream(); + RINOK(extractCallback->SetOperationResult(res)); + } + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = Items.Size(); + return S_OK; +} + +static IInArchive *CreateArc() { return new CHandler; } + +static CArcInfo g_ArcInfo = + { L"NTFS", L"ntfs img", 0, 0xD9, { 'N', 'T', 'F', 'S', ' ', ' ', ' ', ' ', 0 }, 9, false, CreateArc, 0 }; + +REGISTER_ARC(Fat) + +}} -- cgit v1.2.3