blob: 505486fc51d9f7f690afe99a22e8a40bfb7126ec [file] [log] [blame]
// 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)
}}