summaryrefslogtreecommitdiffstats
path: root/src/libs/7zip/win/CPP/7zip/Archive/NtfsHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/7zip/win/CPP/7zip/Archive/NtfsHandler.cpp')
-rw-r--r--src/libs/7zip/win/CPP/7zip/Archive/NtfsHandler.cpp1764
1 files changed, 1764 insertions, 0 deletions
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 <stdio.h>
+#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<CExtent> &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<CExtent> &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<IInStream> Stream;
+ UInt64 Size;
+ UInt64 InitializedSize;
+ int BlockSizeLog;
+ int CompressionUnit;
+ bool InUse;
+ CRecordVector<CExtent> 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<CAttr> &attrs,
+ int attrIndex, int attrIndexLim, UInt64 numPhysClusters, CRecordVector<CExtent> &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<CAttr> DataAttrs;
+ CObjectVector<CFileNameAttr> FileNames;
+ CRecordVector<CDataRef> 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<CAttr> *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<IInStream> 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<IInStream> 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<CExtent> 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<CAttr> *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<CItem> Items;
+ CObjectVector<CMftRec> Recs;
+ CMyComPtr<IInStream> InStream;
+ IArchiveOpenCallback *OpenCallback;
+
+ CByteBuffer ByteBuf;
+
+ CObjectVector<CAttr> 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<IInStream> 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<ICompressCoder> copyCoder = copyCoderSpec;
+
+ CLocalProgress *lps = new CLocalProgress;
+ CMyComPtr<ICompressProgressInfo> progress = lps;
+ lps->Init(extractCallback, false);
+
+ CDummyOutStream *outStreamSpec = new CDummyOutStream;
+ CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
+
+ for (i = 0; i < numItems; i++)
+ {
+ lps->InSize = totalPackSize;
+ lps->OutSize = totalSize;
+ RINOK(lps->SetCur());
+ CMyComPtr<ISequentialOutStream> 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<IInStream> 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)
+
+}}