| // 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) |
| |
| }} |