aboutsummaryrefslogtreecommitdiff
path: root/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Attribute.h
diff options
context:
space:
mode:
authorMatt Graeber <mattgraeber@gmail.com>2013-10-01 14:29:34 -0700
committerMatt Graeber <mattgraeber@gmail.com>2013-10-01 14:29:34 -0700
commit6ad050fe7a54ae7c47fda4505043df8efd82bc2e (patch)
tree9c99d9aa042a4752991cfe8f0069c9a4823c8d42 /Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Attribute.h
parent23850a6337bf79d02f68912e49df12f3cde4a8dd (diff)
parent59cd18360764af6e6133ad11ec9cd8295372e587 (diff)
downloadPowerSploit-6ad050fe7a54ae7c47fda4505043df8efd82bc2e.tar.gz
PowerSploit-6ad050fe7a54ae7c47fda4505043df8efd82bc2e.zip
Merge pull request #15 from clymb3r/master
Adding GitIgnore, adding Invoke-NinjaCopy and Invoke-Mimikatz
Diffstat (limited to 'Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Attribute.h')
-rw-r--r--Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Attribute.h1663
1 files changed, 1663 insertions, 0 deletions
diff --git a/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Attribute.h b/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Attribute.h
new file mode 100644
index 0000000..19ab7ce
--- /dev/null
+++ b/Exfiltration/NTFSParser/NTFSParserDLL/NTFS_Attribute.h
@@ -0,0 +1,1663 @@
+/*
+ * NTFS Attribute Classes
+ *
+ * Copyright(C) 2010 cyb70289 <cyb70289@gmail.com>
+ *
+ * This program/include file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program/include file is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __NTFS_ATTRIBUTE_H_CYB70289
+#define __NTFS_ATTRIBUTE_H_CYB70289
+
+
+////////////////////////////////
+// List to hold parsed DataRuns
+////////////////////////////////
+typedef struct tagDataRun_Entry
+{
+ LONGLONG LCN; // -1 to indicate sparse data
+ ULONGLONG Clusters;
+ ULONGLONG StartVCN;
+ ULONGLONG LastVCN;
+} DataRun_Entry;
+typedef class CSList<DataRun_Entry> CDataRunList;
+
+////////////////////////////////////
+// List to hold Index Entry objects
+////////////////////////////////////
+class CIndexEntry;
+typedef class CSList<CIndexEntry> CIndexEntryList;
+
+
+////////////////////////////////
+// Attributes base class
+////////////////////////////////
+class CAttrBase
+{
+public:
+ CAttrBase(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr);
+ virtual ~CAttrBase();
+
+protected:
+ const ATTR_HEADER_COMMON *AttrHeader;
+ WORD _SectorSize;
+ DWORD _ClusterSize;
+ DWORD _IndexBlockSize;
+ HANDLE _hVolume;
+ const CFileRecord *FileRecord;
+
+public:
+ __inline const ATTR_HEADER_COMMON* GetAttrHeader() const;
+ __inline DWORD GetAttrType() const;
+ __inline DWORD GetAttrTotalSize() const;
+ __inline BOOL IsNonResident() const;
+ __inline WORD GetAttrFlags() const;
+ int GetAttrName(char *buf, DWORD bufLen) const;
+ int GetAttrName(wchar_t *buf, DWORD bufLen) const;
+ __inline BOOL IsUnNamed() const;
+
+protected:
+ virtual __inline BOOL IsDataRunOK() const = 0;
+
+public:
+ virtual __inline ULONGLONG GetDataSize(ULONGLONG *allocSize = NULL) const = 0;
+ virtual BOOL ReadData(const ULONGLONG &offset, void *bufv, DWORD bufLen, DWORD *actural) const = 0;
+}; // CAttrBase
+
+CAttrBase::CAttrBase(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr)
+{
+ _ASSERT(ahc);
+ _ASSERT(fr);
+
+ AttrHeader = ahc;
+ FileRecord = fr;
+
+ _SectorSize = fr->Volume->SectorSize;
+ _ClusterSize = fr->Volume->ClusterSize;
+ _IndexBlockSize = fr->Volume->IndexBlockSize;
+ _hVolume = fr->Volume->hVolume;
+}
+
+CAttrBase::~CAttrBase()
+{
+}
+
+__inline const ATTR_HEADER_COMMON* CAttrBase::GetAttrHeader() const
+{
+ return AttrHeader;
+}
+
+__inline DWORD CAttrBase::GetAttrType() const
+{
+ return AttrHeader->Type;
+}
+
+__inline DWORD CAttrBase::GetAttrTotalSize() const
+{
+ return AttrHeader->TotalSize;
+}
+
+__inline BOOL CAttrBase::IsNonResident() const
+{
+ return AttrHeader->NonResident;
+}
+
+__inline WORD CAttrBase::GetAttrFlags() const
+{
+ return AttrHeader->Flags;
+}
+
+// Get ANSI Attribute name
+// Return 0: Unnamed, <0: buffer too small, -buffersize, >0 Name length
+int CAttrBase::GetAttrName(char *buf, DWORD bufLen) const
+{
+ if (AttrHeader->NameLength)
+ {
+ if (bufLen < AttrHeader->NameLength)
+ return -1*AttrHeader->NameLength; // buffer too small
+
+ wchar_t *namePtr = (wchar_t*)((BYTE*)AttrHeader + AttrHeader->NameOffset);
+ int len = WideCharToMultiByte(CP_ACP, 0, namePtr, AttrHeader->NameLength,
+ buf, bufLen, NULL, NULL);
+ if (len)
+ {
+ buf[len] = '\0';
+ NTFS_TRACE1("Attribute name: %s\n", buf);
+ return len;
+ }
+ else
+ {
+ NTFS_TRACE("Unrecognized attribute name or Name buffer too small\n");
+ return -1*AttrHeader->NameLength;
+ }
+ }
+ else
+ {
+ NTFS_TRACE("Attribute is unnamed\n");
+ return 0;
+ }
+}
+
+// Get UNICODE Attribute name
+// Return 0: Unnamed, <0: buffer too small, -buffersize, >0 Name length
+int CAttrBase::GetAttrName(wchar_t *buf, DWORD bufLen) const
+{
+ if (AttrHeader->NameLength)
+ {
+ if (bufLen < AttrHeader->NameLength)
+ return -1*AttrHeader->NameLength; // buffer too small
+
+ bufLen = AttrHeader->NameLength;
+ wchar_t *namePtr = (wchar_t*)((BYTE*)AttrHeader + AttrHeader->NameOffset);
+ wcsncpy(buf, namePtr, bufLen);
+ buf[bufLen] = '\0\0';
+
+ NTFS_TRACE("Unicode Attribute Name\n");
+ return bufLen;
+ }
+ else
+ {
+ NTFS_TRACE("Attribute is unnamed\n");
+ return 0;
+ }
+}
+
+// Verify if this attribute is unnamed
+// Useful in analyzing MultiStream files
+__inline BOOL CAttrBase::IsUnNamed() const
+{
+ return (AttrHeader->NameLength == 0);
+}
+
+
+////////////////////////////////
+// Resident Attributes
+////////////////////////////////
+class CAttrResident : public CAttrBase
+{
+public:
+ CAttrResident(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr);
+ virtual ~CAttrResident();
+
+protected:
+ const ATTR_HEADER_RESIDENT *AttrHeaderR;
+ const void *AttrBody; // Points to Resident Data
+ DWORD AttrBodySize; // Attribute Data Size
+
+ virtual __inline BOOL IsDataRunOK() const;
+
+public:
+ virtual __inline ULONGLONG GetDataSize(ULONGLONG *allocSize = NULL) const;
+ virtual BOOL ReadData(const ULONGLONG &offset, void *bufv, DWORD bufLen, DWORD *actural) const;
+}; // CAttrResident
+
+CAttrResident::CAttrResident(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : CAttrBase(ahc, fr)
+{
+ AttrHeaderR = (ATTR_HEADER_RESIDENT*)ahc;
+ AttrBody = (void*)((BYTE*)AttrHeaderR + AttrHeaderR->AttrOffset);
+ AttrBodySize = AttrHeaderR->AttrSize;
+}
+
+CAttrResident::~CAttrResident()
+{
+}
+
+__inline BOOL CAttrResident::IsDataRunOK() const
+{
+ return TRUE; // Always OK for a resident attribute
+}
+
+// Return Actural Data Size
+// *allocSize = Allocated Size
+__inline ULONGLONG CAttrResident::GetDataSize(ULONGLONG *allocSize) const
+{
+ if (allocSize)
+ *allocSize = AttrBodySize;
+
+ return (ULONGLONG)AttrBodySize;
+}
+
+// Read "bufLen" bytes from "offset" into "bufv"
+// Number of bytes acturally read is returned in "*actural"
+BOOL CAttrResident::ReadData(const ULONGLONG &offset, void *bufv, DWORD bufLen, DWORD *actural) const
+{
+ _ASSERT(bufv);
+
+ *actural = 0;
+ if (bufLen == 0)
+ return TRUE;
+
+ DWORD offsetd = (DWORD)offset;
+ if (offsetd >= AttrBodySize)
+ return FALSE; // offset parameter error
+
+ if ((offsetd + bufLen) > AttrBodySize)
+ *actural = AttrBodySize - offsetd; // Beyond scope
+ else
+ *actural = bufLen;
+
+ memcpy(bufv, (BYTE*)AttrBody + offsetd, *actural);
+
+ return TRUE;
+}
+
+
+////////////////////////////////
+// NonResident Attributes
+////////////////////////////////
+class CAttrNonResident : public CAttrBase
+{
+public:
+ CAttrNonResident(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr);
+ virtual ~CAttrNonResident();
+
+protected:
+ const ATTR_HEADER_NON_RESIDENT *AttrHeaderNR;
+ CDataRunList DataRunList;
+
+private:
+ BOOL bDataRunOK;
+ BYTE *UnalignedBuf; // Buffer to hold not cluster aligned data
+ BOOL PickData(const BYTE **dataRun, LONGLONG *length, LONGLONG *LCNOffset);
+ BOOL ParseDataRun();
+ BOOL ReadClusters(void *buf, DWORD clusters, LONGLONG lcn);
+ BOOL ReadVirtualClusters(ULONGLONG vcn, DWORD clusters,
+ void *bufv, DWORD bufLen, DWORD *actural);
+
+protected:
+ virtual __inline BOOL IsDataRunOK() const;
+
+public:
+ virtual __inline ULONGLONG GetDataSize(ULONGLONG *allocSize = NULL) const;
+ virtual BOOL ReadData(const ULONGLONG &offset, void *bufv, DWORD bufLen, DWORD *actural) const;
+}; // CAttrNonResident
+
+CAttrNonResident::CAttrNonResident(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : CAttrBase(ahc, fr)
+{
+ AttrHeaderNR = (ATTR_HEADER_NON_RESIDENT*)ahc;
+
+ UnalignedBuf = new BYTE[_ClusterSize];
+
+ bDataRunOK = ParseDataRun();
+}
+
+CAttrNonResident::~CAttrNonResident()
+{
+ delete UnalignedBuf;
+
+ DataRunList.RemoveAll();
+}
+
+// Parse a single DataRun unit
+BOOL CAttrNonResident::PickData(const BYTE **dataRun, LONGLONG *length, LONGLONG *LCNOffset)
+{
+ BYTE size = **dataRun;
+ (*dataRun)++;
+ int lengthBytes = size & 0x0F;
+ int offsetBytes = size >> 4;
+
+ if (lengthBytes > 8 || offsetBytes > 8)
+ {
+ NTFS_TRACE1("DataRun decode error 1: 0x%02X\n", size);
+ return FALSE;
+ }
+
+ *length = 0;
+ memcpy(length, *dataRun, lengthBytes);
+ if (*length < 0)
+ {
+ NTFS_TRACE1("DataRun length error: %I64d\n", *length);
+ return FALSE;
+ }
+
+ (*dataRun) += lengthBytes;
+ *LCNOffset = 0;
+ if (offsetBytes) // Not Sparse File
+ {
+ if ((*dataRun)[offsetBytes-1] & 0x80)
+ *LCNOffset = -1;
+ memcpy(LCNOffset, *dataRun, offsetBytes);
+
+ (*dataRun) += offsetBytes;
+ }
+
+ return TRUE;
+}
+
+// Travers DataRun and insert into a link list
+BOOL CAttrNonResident::ParseDataRun()
+{
+ NTFS_TRACE("Parsing Non Resident DataRun\n");
+ NTFS_TRACE2("Start VCN = %I64u, End VCN = %I64u\n",
+ AttrHeaderNR->StartVCN, AttrHeaderNR->LastVCN);
+
+ const BYTE *dataRun = (BYTE*)AttrHeaderNR + AttrHeaderNR->DataRunOffset;
+ LONGLONG length;
+ LONGLONG LCNOffset;
+ LONGLONG LCN = 0;
+ ULONGLONG VCN = 0;
+
+ while (*dataRun)
+ {
+ if (PickData(&dataRun, &length, &LCNOffset))
+ {
+ LCN += LCNOffset;
+ if (LCN < 0)
+ {
+ NTFS_TRACE("DataRun decode error 2\n");
+ return FALSE;
+ }
+
+ NTFS_TRACE2("Data length = %I64d clusters, LCN = %I64d", length, LCN);
+ NTFS_TRACE(LCNOffset == 0 ? ", Sparse Data\n" : "\n");
+
+ // Store LCN, Data size (clusters) into list
+ DataRun_Entry *dr = new DataRun_Entry;
+ dr->LCN = (LCNOffset == 0) ? -1 : LCN;
+ dr->Clusters = length;
+ dr->StartVCN = VCN;
+ VCN += length;
+ dr->LastVCN = VCN - 1;
+
+ if (dr->LastVCN <= (AttrHeaderNR->LastVCN - AttrHeaderNR->StartVCN))
+ {
+ DataRunList.InsertEntry(dr);
+ }
+ else
+ {
+ NTFS_TRACE("DataRun decode error: VCN exceeds bound\n");
+
+ // Remove entries
+ DataRunList.RemoveAll();
+
+ return FALSE;
+ }
+ }
+ else
+ break;
+ }
+
+ return TRUE;
+}
+
+// Read clusters from disk, or sparse data
+// *actural = Clusters acturally read
+BOOL CAttrNonResident::ReadClusters(void *buf, DWORD clusters, LONGLONG lcn)
+{
+ if (lcn == -1) // sparse data
+ {
+ NTFS_TRACE("Sparse Data, Fill the buffer with 0\n");
+
+ // Fill the buffer with 0
+ memset(buf, 0, clusters * _ClusterSize);
+
+ return TRUE;
+ }
+
+ LARGE_INTEGER addr;
+ DWORD len;
+
+ addr.QuadPart = lcn * _ClusterSize;
+ len = SetFilePointer(_hVolume, addr.LowPart, &addr.HighPart, FILE_BEGIN);
+
+ if (len == (DWORD)-1 && GetLastError() != NO_ERROR)
+ {
+ NTFS_TRACE1("Cannot locate cluster with LCN %I64d\n", lcn);
+ }
+ else
+ {
+ if (ReadFile(_hVolume, buf, clusters*_ClusterSize, &len, NULL) &&
+ len == clusters*_ClusterSize)
+ {
+ NTFS_TRACE2("Successfully read %u clusters from LCN %I64d\n", clusters, lcn);
+ return TRUE;
+ }
+ else
+ {
+ NTFS_TRACE1("Cannot read cluster with LCN %I64d\n", lcn);
+ }
+ }
+
+ return FALSE;
+}
+
+// Read Data, cluster based
+// clusterNo: Begnning cluster Number
+// clusters: Clusters to read
+// bufv, bufLen: Returned data
+// *actural = Number of bytes acturally read
+BOOL CAttrNonResident::ReadVirtualClusters(ULONGLONG vcn, DWORD clusters,
+ void *bufv, DWORD bufLen, DWORD *actural)
+{
+ _ASSERT(bufv);
+ _ASSERT(clusters);
+
+ *actural = 0;
+ BYTE *buf = (BYTE*)bufv;
+
+ // Verify if clusters exceeds DataRun bounds
+ if (vcn + clusters > (AttrHeaderNR->LastVCN - AttrHeaderNR->StartVCN +1))
+ {
+ NTFS_TRACE("Cluster exceeds DataRun bounds\n");
+ return FALSE;
+ }
+
+ // Verify buffer size
+ if (bufLen < clusters*_ClusterSize)
+ {
+ NTFS_TRACE("Buffer size too small\n");
+ return FALSE;
+ }
+
+ // Traverse the DataRun List to find the according LCN
+ const DataRun_Entry *dr = DataRunList.FindFirstEntry();
+ while(dr)
+ {
+ if (vcn>=dr->StartVCN && vcn<=dr->LastVCN)
+ {
+ DWORD clustersToRead;
+
+ ULONGLONG vcns = dr->LastVCN - vcn + 1; // Clusters from read pointer to the end
+
+ if ((ULONGLONG)clusters > vcns) // Fragmented data, we must go on
+ clustersToRead = (DWORD)vcns;
+ else
+ clustersToRead = clusters;
+ if (ReadClusters(buf, clustersToRead, dr->LCN+(vcn-dr->StartVCN)))
+ {
+ buf += clustersToRead*_ClusterSize;
+ clusters -= clustersToRead;
+ *actural += clustersToRead;
+ vcn += clustersToRead;
+ }
+ else
+ break;
+
+ if (clusters == 0)
+ break;
+ }
+
+ dr = DataRunList.FindNextEntry();
+ }
+
+ *actural *= _ClusterSize;
+ return TRUE;
+}
+
+// Judge if the DataRun is successfully parsed
+__inline BOOL CAttrNonResident::IsDataRunOK() const
+{
+ return bDataRunOK;
+}
+
+// Return Actural Data Size
+// *allocSize = Allocated Size
+__inline ULONGLONG CAttrNonResident::GetDataSize(ULONGLONG *allocSize) const
+{
+ if (allocSize)
+ *allocSize = AttrHeaderNR->AllocSize;
+
+ return AttrHeaderNR->RealSize;
+}
+
+// Read "bufLen" bytes from "offset" into "bufv"
+// Number of bytes acturally read is returned in "*actural"
+BOOL CAttrNonResident::ReadData(const ULONGLONG &offset, void *bufv, DWORD bufLen, DWORD *actural) const
+{
+ // Hard disks can only be accessed by sectors
+ // To be simple and efficient, only implemented cluster based accessing
+ // So cluster unaligned data address should be processed carefully here
+
+ _ASSERT(bufv);
+
+ *actural = 0;
+ if (bufLen == 0)
+ return TRUE;
+
+ // Bounds check
+ if (offset > AttrHeaderNR->RealSize)
+ return FALSE;
+ if ((offset + bufLen) > AttrHeaderNR->RealSize)
+ bufLen = (DWORD)(AttrHeaderNR->RealSize - offset);
+
+ DWORD len;
+ BYTE *buf = (BYTE*)bufv;
+
+ // First cluster Number
+ ULONGLONG startVCN = offset / _ClusterSize;
+ // Bytes in first cluster
+ DWORD startBytes = _ClusterSize - (DWORD)(offset % _ClusterSize);
+ // Read first cluster
+ if (startBytes != _ClusterSize)
+ {
+ // First cluster, Unaligned
+ if (((CAttrNonResident*)this)->ReadVirtualClusters(startVCN, 1, UnalignedBuf, _ClusterSize, &len)
+ && len == _ClusterSize)
+ {
+ len = (startBytes < bufLen) ? startBytes : bufLen;
+ memcpy(buf, UnalignedBuf + _ClusterSize - startBytes, len);
+ buf += len;
+ bufLen -= len;
+ *actural += len;
+ startVCN++;
+ }
+ else
+ return FALSE;
+ }
+ if (bufLen == 0)
+ return TRUE;
+
+ DWORD alignedClusters = bufLen / _ClusterSize;
+ if (alignedClusters)
+ {
+ // Aligned clusters
+ DWORD alignedSize = alignedClusters*_ClusterSize;
+ if (((CAttrNonResident*)this)->ReadVirtualClusters(startVCN, alignedClusters, buf, alignedSize, &len)
+ && len == alignedSize)
+ {
+ startVCN += alignedClusters;
+ buf += alignedSize;
+ bufLen %= _ClusterSize;
+ *actural += len;
+
+ if (bufLen == 0)
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+
+ // Last cluster, Unaligned
+ if (((CAttrNonResident*)this)->ReadVirtualClusters(startVCN, 1, UnalignedBuf, _ClusterSize, &len)
+ && len == _ClusterSize)
+ {
+ memcpy(buf, UnalignedBuf, bufLen);
+ *actural += bufLen;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+///////////////////////////////////
+// Attribute: Standard Information
+///////////////////////////////////
+class CAttr_StdInfo : public CAttrResident
+{
+public:
+ CAttr_StdInfo(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr);
+ virtual ~CAttr_StdInfo();
+
+private:
+ const ATTR_STANDARD_INFORMATION *StdInfo;
+
+public:
+ void GetFileTime(FILETIME *writeTm, FILETIME *createTm = NULL, FILETIME *accessTm = NULL) const;
+ __inline DWORD GetFilePermission() const;
+ __inline BOOL IsReadOnly() const;
+ __inline BOOL IsHidden() const;
+ __inline BOOL IsSystem() const;
+ __inline BOOL IsCompressed() const;
+ __inline BOOL IsEncrypted() const;
+ __inline BOOL IsSparse() const;
+
+ static void UTC2Local(const ULONGLONG &ultm, FILETIME *lftm);
+}; // CAttr_StdInfo
+
+CAttr_StdInfo::CAttr_StdInfo(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : CAttrResident(ahc, fr)
+{
+ NTFS_TRACE("Attribute: Standard Information\n");
+
+ StdInfo = (ATTR_STANDARD_INFORMATION*)AttrBody;
+}
+
+CAttr_StdInfo::~CAttr_StdInfo()
+{
+ NTFS_TRACE("CAttr_StdInfo deleted\n");
+}
+
+// Change from UTC time to local time
+void CAttr_StdInfo::GetFileTime(FILETIME *writeTm, FILETIME *createTm, FILETIME *accessTm) const
+{
+ UTC2Local(StdInfo->AlterTime, writeTm);
+
+ if (createTm)
+ UTC2Local(StdInfo->CreateTime, createTm);
+
+ if (accessTm)
+ UTC2Local(StdInfo->ReadTime, accessTm);
+}
+
+__inline DWORD CAttr_StdInfo::GetFilePermission() const
+{
+ return StdInfo->Permission;
+}
+
+__inline BOOL CAttr_StdInfo::IsReadOnly() const
+{
+ return ((StdInfo->Permission) & ATTR_STDINFO_PERMISSION_READONLY);
+}
+
+__inline BOOL CAttr_StdInfo::IsHidden() const
+{
+ return ((StdInfo->Permission) & ATTR_STDINFO_PERMISSION_HIDDEN);
+}
+
+__inline BOOL CAttr_StdInfo::IsSystem() const
+{
+ return ((StdInfo->Permission) & ATTR_STDINFO_PERMISSION_SYSTEM);
+}
+
+__inline BOOL CAttr_StdInfo::IsCompressed() const
+{
+ return ((StdInfo->Permission) & ATTR_STDINFO_PERMISSION_COMPRESSED);
+}
+
+__inline BOOL CAttr_StdInfo::IsEncrypted() const
+{
+ return ((StdInfo->Permission) & ATTR_STDINFO_PERMISSION_ENCRYPTED);
+}
+
+__inline BOOL CAttr_StdInfo::IsSparse() const
+{
+ return ((StdInfo->Permission) & ATTR_STDINFO_PERMISSION_SPARSE);
+}
+
+// UTC filetime to Local filetime
+void CAttr_StdInfo::UTC2Local(const ULONGLONG &ultm, FILETIME *lftm)
+{
+ LARGE_INTEGER fti;
+ FILETIME ftt;
+
+ fti.QuadPart = ultm;
+ ftt.dwHighDateTime = fti.HighPart;
+ ftt.dwLowDateTime = fti.LowPart;
+
+ if (!FileTimeToLocalFileTime(&ftt, lftm))
+ *lftm = ftt;
+}
+
+
+////////////////////////////////////////
+// FileName helper class
+// used by FileName and IndexEntry
+////////////////////////////////////////
+class CFileName
+{
+public:
+ CFileName(ATTR_FILE_NAME *fn = NULL);
+ virtual ~CFileName();
+
+protected:
+ const ATTR_FILE_NAME *FileName; // May be NULL for an IndexEntry
+ wchar_t *FileNameWUC; // Uppercase Unicode File Name, used to compare file names
+ int FileNameLength;
+ BOOL IsCopy;
+
+ __inline void SetFileName(ATTR_FILE_NAME *fn);
+ void CFileName::CopyFileName(const CFileName *fn, const ATTR_FILE_NAME *afn);
+
+private:
+ void GetFileNameWUC();
+
+public:
+ int Compare(const wchar_t *fn) const;
+ int Compare(const char *fn) const;
+
+ __inline ULONGLONG GetFileSize() const;
+ __inline DWORD GetFilePermission() const;
+ __inline BOOL IsReadOnly() const;
+ __inline BOOL IsHidden() const;
+ __inline BOOL IsSystem() const;
+ __inline BOOL IsDirectory() const;
+ __inline BOOL IsCompressed() const;
+ __inline BOOL IsEncrypted() const;
+ __inline BOOL IsSparse() const;
+
+ int GetFileName(char *buf, DWORD bufLen) const;
+ int GetFileName(wchar_t *buf, DWORD bufLen) const;
+ __inline BOOL HasName() const;
+ __inline BOOL IsWin32Name() const;
+
+ void GetFileTime(FILETIME *writeTm, FILETIME *createTm = NULL, FILETIME *accessTm = NULL) const;
+}; // CFileName
+
+CFileName::CFileName(ATTR_FILE_NAME *fn)
+{
+ IsCopy = FALSE;
+
+ FileName = fn;
+
+ FileNameWUC = NULL;
+ FileNameLength = 0;
+
+ if (fn)
+ GetFileNameWUC();
+}
+
+CFileName::~CFileName()
+{
+ if (FileNameWUC)
+ delete FileNameWUC;
+}
+
+__inline void CFileName::SetFileName(ATTR_FILE_NAME *fn)
+{
+ FileName = fn;
+
+ GetFileNameWUC();
+}
+
+// Copy pointer buffers
+void CFileName::CopyFileName(const CFileName *fn, const ATTR_FILE_NAME *afn)
+{
+ if (!IsCopy)
+ {
+ NTFS_TRACE("Cannot call this routine\n");
+ return;
+ }
+
+ _ASSERT(fn && afn);
+
+ NTFS_TRACE("FileName Copied\n");
+
+ if (FileNameWUC)
+ delete FileNameWUC;
+
+ FileNameLength = fn->FileNameLength;
+ FileName = afn;
+
+ if (fn->FileNameWUC)
+ {
+ FileNameWUC = new wchar_t[FileNameLength+1];
+ wcsncpy(FileNameWUC, fn->FileNameWUC, FileNameLength);
+ FileNameWUC[FileNameLength] = wchar_t('\0');
+ }
+ else
+ FileNameWUC = NULL;
+}
+
+// Get uppercase unicode filename and store it in a buffer
+void CFileName::GetFileNameWUC()
+{
+#ifdef _DEBUG
+ char fna[MAX_PATH];
+ GetFileName(fna, MAX_PATH); // Just show filename in debug window
+#endif
+
+ if (FileNameWUC)
+ {
+ delete FileNameWUC;
+ FileNameWUC = NULL;
+ FileNameLength = 0;
+ }
+
+ wchar_t fns[MAX_PATH];
+ FileNameLength = GetFileName(fns, MAX_PATH);
+
+ if (FileNameLength > 0)
+ {
+ FileNameWUC = new wchar_t[FileNameLength+1];
+ for (int i=0; i<FileNameLength; i++)
+ FileNameWUC[i] = towupper(fns[i]);
+ FileNameWUC[FileNameLength] = wchar_t('\0');
+ }
+ else
+ {
+ FileNameLength = 0;
+ FileNameWUC = NULL;
+ }
+}
+
+// Compare Unicode file name
+int CFileName::Compare(const wchar_t *fn) const
+{
+ // Change fn to upper case
+ int len = wcslen(fn);
+ if (len > MAX_PATH)
+ return 1; // Assume bigger
+
+ wchar_t fns[MAX_PATH];
+
+ for (int i=0; i<len; i++)
+ fns[i] = towupper(fn[i]);
+ fns[len] = wchar_t('\0');
+
+ return wcscmp(fns, FileNameWUC);
+}
+
+// Compare ANSI file name
+int CFileName::Compare(const char *fn) const
+{
+ wchar_t fnw[MAX_PATH];
+
+ int len = MultiByteToWideChar(CP_ACP, 0, fn, -1, fnw, MAX_PATH);
+ if (len)
+ return Compare(fnw);
+ else
+ return 1; // Assume bigger
+}
+
+__inline ULONGLONG CFileName::GetFileSize() const
+{
+ return FileName ? FileName->RealSize : 0;
+}
+
+__inline DWORD CFileName::GetFilePermission() const
+{
+ return FileName ? FileName->Flags : 0;
+}
+
+__inline BOOL CFileName::IsReadOnly() const
+{
+ return FileName ? ((FileName->Flags) & ATTR_FILENAME_FLAG_READONLY) : FALSE;
+}
+
+__inline BOOL CFileName::IsHidden() const
+{
+ return FileName ? ((FileName->Flags) & ATTR_FILENAME_FLAG_HIDDEN) : FALSE;
+}
+
+__inline BOOL CFileName::IsSystem() const
+{
+ return FileName ? ((FileName->Flags) & ATTR_FILENAME_FLAG_SYSTEM) : FALSE;
+}
+
+__inline BOOL CFileName::IsDirectory() const
+{
+ return FileName ? ((FileName->Flags) & ATTR_FILENAME_FLAG_DIRECTORY) : FALSE;
+}
+
+__inline BOOL CFileName::IsCompressed() const
+{
+ return FileName ? ((FileName->Flags) & ATTR_FILENAME_FLAG_COMPRESSED) : FALSE;
+}
+
+__inline BOOL CFileName::IsEncrypted() const
+{
+ return FileName ? ((FileName->Flags) & ATTR_FILENAME_FLAG_ENCRYPTED) : FALSE;
+}
+
+__inline BOOL CFileName::IsSparse() const
+{
+ return FileName ? ((FileName->Flags) & ATTR_FILENAME_FLAG_SPARSE) : FALSE;
+}
+
+// Get ANSI File Name
+// Return 0: Unnamed, <0: buffer too small, -buffersize, >0 Name length
+int CFileName::GetFileName(char *buf, DWORD bufLen) const
+{
+ if (FileName == NULL)
+ return 0;
+
+ int len = 0;
+
+ if (FileName->NameLength)
+ {
+ if (bufLen < FileName->NameLength)
+ return -1*FileName->NameLength; // buffer too small
+
+ len = WideCharToMultiByte(CP_ACP, 0, (wchar_t*)FileName->Name, FileName->NameLength,
+ buf, bufLen, NULL, NULL);
+ if (len)
+ {
+ buf[len] = '\0';
+ NTFS_TRACE1("File Name: %s\n", buf);
+ NTFS_TRACE4("File Permission: %s\t%c%c%c\n", IsDirectory()?"Directory":"File",
+ IsReadOnly()?'R':' ', IsHidden()?'H':' ', IsSystem()?'S':' ');
+ }
+ else
+ {
+ NTFS_TRACE("Unrecognized File Name or FileName buffer too small\n");
+ }
+ }
+
+ return len;
+}
+
+// Get Unicode File Name
+// Return 0: Unnamed, <0: buffer too small, -buffersize, >0 Name length
+int CFileName::GetFileName(wchar_t *buf, DWORD bufLen) const
+{
+ if (FileName == NULL)
+ return 0;
+
+ if (FileName->NameLength)
+ {
+ if (bufLen < FileName->NameLength)
+ return -1*FileName->NameLength; // buffer too small
+
+ bufLen = FileName->NameLength;
+ wcsncpy(buf, (wchar_t*)FileName->Name, bufLen);
+ buf[bufLen] = wchar_t('\0');
+
+ return bufLen;
+ }
+
+ return 0;
+}
+
+__inline BOOL CFileName::HasName() const
+{
+ return FileNameLength > 0;
+}
+
+__inline BOOL CFileName::IsWin32Name() const
+{
+ if (FileName == NULL || FileNameLength <= 0)
+ return FALSE;
+
+ return (FileName->NameSpace != ATTR_FILENAME_NAMESPACE_DOS); // POSIX, WIN32, WIN32_DOS
+}
+
+// Change from UTC time to local time
+void CFileName::GetFileTime(FILETIME *writeTm, FILETIME *createTm, FILETIME *accessTm) const
+{
+ CAttr_StdInfo::UTC2Local(FileName ? FileName->AlterTime : 0, writeTm);
+
+ if (createTm)
+ CAttr_StdInfo::UTC2Local(FileName ? FileName->CreateTime : 0, createTm);
+
+ if (accessTm)
+ CAttr_StdInfo::UTC2Local(FileName ? FileName->ReadTime : 0, accessTm);
+}
+
+
+////////////////////////////////
+// Attribute: File Name
+////////////////////////////////
+class CAttr_FileName : public CAttrResident, public CFileName
+{
+public:
+ CAttr_FileName(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : CAttrResident(ahc, fr)
+ {
+ NTFS_TRACE("Attribute: File Name\n");
+
+ SetFileName((ATTR_FILE_NAME*)AttrBody);
+ }
+
+ virtual ~CAttr_FileName()
+ {
+ NTFS_TRACE("CAttr_FileName deleted\n");
+ }
+
+private:
+ // File permission and time in $FILE_NAME only updates when the filename changes
+ // So hide these functions to prevent user from getting the error information
+ // Standard Information and IndexEntry keeps the most recent file time and permission infomation
+ void GetFileTime(FILETIME *writeTm, FILETIME *createTm = NULL, FILETIME *accessTm = NULL) const {}
+ __inline DWORD GetFilePermission(){}
+ __inline BOOL IsReadOnly() const {}
+ __inline BOOL IsHidden() const {}
+ __inline BOOL IsSystem() const {}
+ __inline BOOL IsCompressed() const {}
+ __inline BOOL IsEncrypted() const {}
+ __inline BOOL IsSparse() const {}
+}; // CAttr_FileName
+
+
+//////////////////////////////////
+// Attribute: Volume Information
+//////////////////////////////////
+class CAttr_VolInfo : public CAttrResident
+{
+public:
+ CAttr_VolInfo(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : CAttrResident(ahc, fr)
+ {
+ NTFS_TRACE("Attribute: Volume Information\n");
+
+ VolInfo = (ATTR_VOLUME_INFORMATION*)AttrBody;
+ }
+
+ virtual ~CAttr_VolInfo()
+ {
+ NTFS_TRACE("CAttr_VolInfo deleted\n");
+ }
+
+private:
+ const ATTR_VOLUME_INFORMATION *VolInfo;
+
+public:
+ // Get NTFS Volume Version
+ __inline WORD GetVersion()
+ {
+ return MAKEWORD(VolInfo->MinorVersion, VolInfo->MajorVersion);
+ }
+}; // CAttr_VolInfo
+
+
+///////////////////////////
+// Attribute: Volume Name
+///////////////////////////
+class CAttr_VolName : public CAttrResident
+{
+public:
+ CAttr_VolName(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : CAttrResident(ahc, fr)
+ {
+ NTFS_TRACE("Attribute: Volume Name\n");
+
+ NameLength = AttrBodySize >> 1;
+ VolNameU = new wchar_t[NameLength+1];
+ VolNameA = new char[NameLength+1];
+
+ memcpy(VolNameU, AttrBody, AttrBodySize);
+ VolNameU[NameLength] = wchar_t('\0');
+
+ int len = WideCharToMultiByte(CP_ACP, 0, VolNameU, NameLength,
+ VolNameA, NameLength, NULL, NULL);
+ VolNameA[NameLength] = '\0';
+ }
+
+ virtual ~CAttr_VolName()
+ {
+ NTFS_TRACE("CAttr_VolName deleted\n");
+
+ delete VolNameU;
+ delete VolNameA;
+ }
+
+private:
+ wchar_t *VolNameU;
+ char *VolNameA;
+ DWORD NameLength;
+
+public:
+ // Get NTFS Volume Unicode Name
+ __inline int GetName(wchar_t *buf, DWORD len) const
+ {
+ if (len < NameLength)
+ return -1*NameLength; // buffer too small
+
+ wcsncpy(buf, VolNameU, NameLength+1);
+ return NameLength;
+ }
+
+ // ANSI Name
+ __inline int GetName(char *buf, DWORD len) const
+ {
+ if (len < NameLength)
+ return -1*NameLength; // buffer too small
+
+ strncpy(buf, VolNameA, NameLength+1);
+ return NameLength;
+ }
+}; // CAttr_VolInfo
+
+
+/////////////////////////////////////
+// Attribute: Data
+/////////////////////////////////////
+template <class TYPE_RESIDENT>
+class CAttr_Data : public TYPE_RESIDENT
+{
+public:
+ CAttr_Data(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : TYPE_RESIDENT(ahc, fr)
+ {
+ NTFS_TRACE1("Attribute: Data (%sResident)\n", IsNonResident() ? "Non" : "");
+ }
+
+ virtual ~CAttr_Data()
+ {
+ NTFS_TRACE("CAttr_Data deleted\n");
+ }
+}; // CAttr_Data
+
+
+/////////////////////////////
+// Index Entry helper class
+/////////////////////////////
+class CIndexEntry : public CFileName
+{
+public:
+ CIndexEntry()
+ {
+ NTFS_TRACE("Index Entry\n");
+
+ IsDefault = TRUE;
+
+ IndexEntry = NULL;
+ SetFileName(NULL);
+ }
+
+ CIndexEntry(const INDEX_ENTRY *ie)
+ {
+ NTFS_TRACE("Index Entry\n");
+
+ IsDefault = FALSE;
+
+ _ASSERT(ie);
+ IndexEntry = ie;
+
+ if (IsSubNodePtr())
+ {
+ NTFS_TRACE("Points to sub-node\n");
+ }
+
+ if (ie->StreamSize)
+ {
+ SetFileName((ATTR_FILE_NAME*)(ie->Stream));
+ }
+ else
+ {
+ NTFS_TRACE("No FileName stream found\n");
+ }
+ }
+
+ virtual ~CIndexEntry()
+ {
+ // Never touch *IndexEntry here if IsCopy == FALSE !
+ // As the memory have been deallocated by ~CIndexBlock()
+
+ if (IsCopy && IndexEntry)
+ delete (void*)IndexEntry;
+
+ NTFS_TRACE("CIndexEntry deleted\n");
+ }
+
+private:
+ BOOL IsDefault;
+
+protected:
+ const INDEX_ENTRY *IndexEntry;
+
+public:
+ // Use with caution !
+ CIndexEntry& operator = (const CIndexEntry &ieClass)
+ {
+ if (!IsDefault)
+ {
+ NTFS_TRACE("Cannot call this routine\n");
+ return *this;
+ }
+
+ NTFS_TRACE("Index Entry Copied\n");
+
+ IsCopy = TRUE;
+
+ if (IndexEntry)
+ {
+ delete (void*)IndexEntry;
+ IndexEntry = NULL;
+ }
+
+ const INDEX_ENTRY *ie = ieClass.IndexEntry;
+ _ASSERT(ie && (ie->Size > 0));
+
+ IndexEntry = (INDEX_ENTRY*)new BYTE[ie->Size];
+ memcpy((void*)IndexEntry, ie, ie->Size);
+ CopyFileName(&ieClass, (ATTR_FILE_NAME*)(IndexEntry->Stream));
+
+ return *this;
+ }
+
+ __inline ULONGLONG GetFileReference() const
+ {
+ if (IndexEntry)
+ return IndexEntry->FileReference & 0x0000FFFFFFFFFFFFUL;
+ else
+ return (ULONGLONG)-1;
+ }
+
+ __inline BOOL IsSubNodePtr() const
+ {
+ if (IndexEntry)
+ return (IndexEntry->Flags & INDEX_ENTRY_FLAG_SUBNODE);
+ else
+ return FALSE;
+ }
+
+ __inline ULONGLONG GetSubNodeVCN() const
+ {
+ if (IndexEntry)
+ return *(ULONGLONG*)((BYTE*)IndexEntry + IndexEntry->Size - 8);
+ else
+ return (ULONGLONG)-1;
+ }
+}; // CIndexEntry
+
+
+///////////////////////////////
+// Index Block helper class
+///////////////////////////////
+class CIndexBlock : public CIndexEntryList
+{
+public:
+ CIndexBlock()
+ {
+ NTFS_TRACE("Index Block\n");
+
+ IndexBlock = NULL;
+ }
+
+ virtual ~CIndexBlock()
+ {
+ NTFS_TRACE("IndexBlock deleted\n");
+
+ if (IndexBlock)
+ delete IndexBlock;
+ }
+
+private:
+ INDEX_BLOCK *IndexBlock;
+
+public:
+ INDEX_BLOCK *AllocIndexBlock(DWORD size)
+ {
+ // Free previous data if any
+ if (GetCount() > 0)
+ RemoveAll();
+ if (IndexBlock)
+ delete IndexBlock;
+
+ IndexBlock = (INDEX_BLOCK*)new BYTE[size];
+
+ return IndexBlock;
+ }
+}; // CIndexBlock
+
+
+/////////////////////////////////////
+// Attribute: Index Root (Resident)
+/////////////////////////////////////
+class CAttr_IndexRoot : public CAttrResident, public CIndexEntryList
+{
+public:
+ CAttr_IndexRoot(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr);
+ virtual ~CAttr_IndexRoot();
+
+private:
+ const ATTR_INDEX_ROOT *IndexRoot;
+
+ void ParseIndexEntries();
+
+public:
+ __inline BOOL IsFileName() const;
+}; // CAttr_IndexRoot
+
+CAttr_IndexRoot::CAttr_IndexRoot(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr): CAttrResident(ahc, fr)
+{
+ NTFS_TRACE("Attribute: Index Root\n");
+
+ IndexRoot = (ATTR_INDEX_ROOT*)AttrBody;
+
+ if (IsFileName())
+ {
+ ParseIndexEntries();
+ }
+ else
+ {
+ NTFS_TRACE("Index View not supported\n");
+ }
+}
+
+CAttr_IndexRoot::~CAttr_IndexRoot()
+{
+ NTFS_TRACE("CAttr_IndexRoot deleted\n");
+}
+
+// Get all the index entries
+void CAttr_IndexRoot::ParseIndexEntries()
+{
+ INDEX_ENTRY *ie;
+ ie = (INDEX_ENTRY*)((BYTE*)(&(IndexRoot->EntryOffset)) + IndexRoot->EntryOffset);
+
+ DWORD ieTotal = ie->Size;
+
+ while (ieTotal <= IndexRoot->TotalEntrySize)
+ {
+ CIndexEntry *ieClass = new CIndexEntry(ie);
+ InsertEntry(ieClass);
+
+ if (ie->Flags & INDEX_ENTRY_FLAG_LAST)
+ {
+ NTFS_TRACE("Last Index Entry\n");
+ break;
+ }
+
+ ie = (INDEX_ENTRY*)((BYTE*)ie + ie->Size); // Pick next
+ ieTotal += ie->Size;
+ }
+}
+
+// Check if this IndexRoot contains FileName or IndexView
+__inline BOOL CAttr_IndexRoot::IsFileName() const
+{
+ return (IndexRoot->AttrType == ATTR_TYPE_FILE_NAME);
+}
+
+
+/////////////////////////////////////////////
+// Attribute: Index Allocation (NonResident)
+/////////////////////////////////////////////
+class CAttr_IndexAlloc : public CAttrNonResident
+{
+public:
+ CAttr_IndexAlloc(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr);
+ virtual ~CAttr_IndexAlloc();
+
+private:
+ ULONGLONG IndexBlockCount;
+
+ BOOL PatchUS(WORD *sector, int sectors, WORD usn, WORD *usarray);
+
+public:
+ __inline ULONGLONG GetIndexBlockCount();
+ BOOL ParseIndexBlock(const ULONGLONG &vcn, CIndexBlock &ibClass);
+}; // CAttr_IndexAlloc
+
+CAttr_IndexAlloc::CAttr_IndexAlloc(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : CAttrNonResident(ahc, fr)
+{
+ NTFS_TRACE("Attribute: Index Allocation\n");
+
+ IndexBlockCount = 0;
+
+ if (IsDataRunOK())
+ {
+ // Get total number of Index Blocks
+ ULONGLONG ibTotalSize;
+ ibTotalSize = GetDataSize();
+ if (ibTotalSize % _IndexBlockSize)
+ {
+ NTFS_TRACE2("Cannot calulate number of IndexBlocks, total size = %I64u, unit = %u\n",
+ ibTotalSize, _IndexBlockSize);
+ return;
+ }
+ IndexBlockCount = ibTotalSize / _IndexBlockSize;
+ }
+ else
+ {
+ NTFS_TRACE("Index Allocation DataRun parse error\n");
+ }
+}
+
+CAttr_IndexAlloc::~CAttr_IndexAlloc()
+{
+ NTFS_TRACE("CAttr_IndexAlloc deleted\n");
+}
+
+// Verify US and update sectors
+BOOL CAttr_IndexAlloc::PatchUS(WORD *sector, int sectors, WORD usn, WORD *usarray)
+{
+ int i;
+
+ for (i=0; i<sectors; i++)
+ {
+ sector += ((_SectorSize>>1) - 1);
+ if (*sector != usn)
+ return FALSE; // USN error
+ *sector = usarray[i]; // Write back correct data
+ sector++;
+ }
+ return TRUE;
+}
+
+__inline ULONGLONG CAttr_IndexAlloc::GetIndexBlockCount()
+{
+ return IndexBlockCount;
+}
+
+// Parse a single Index Block
+// vcn = Index Block VCN in Index Allocation Data Attributes
+// ibClass holds the parsed Index Entries
+BOOL CAttr_IndexAlloc::ParseIndexBlock(const ULONGLONG &vcn, CIndexBlock &ibClass)
+{
+ if (vcn >= IndexBlockCount) // Bounds check
+ return FALSE;
+
+ // Allocate buffer for a single Index Block
+ INDEX_BLOCK *ibBuf = ibClass.AllocIndexBlock(_IndexBlockSize);
+
+ // Sectors Per Index Block
+ DWORD sectors = _IndexBlockSize / _SectorSize;
+
+ // Read one Index Block
+ DWORD len;
+ if (ReadData(vcn*_IndexBlockSize, ibBuf, _IndexBlockSize, &len) &&
+ len == _IndexBlockSize)
+ {
+ if (ibBuf->Magic != INDEX_BLOCK_MAGIC)
+ {
+ NTFS_TRACE("Index Block parse error: Magic mismatch\n");
+ return FALSE;
+ }
+
+ // Patch US
+ WORD *usnaddr = (WORD*)((BYTE*)ibBuf + ibBuf->OffsetOfUS);
+ WORD usn = *usnaddr;
+ WORD *usarray = usnaddr + 1;
+ if (!PatchUS((WORD*)ibBuf, sectors, usn, usarray))
+ {
+ NTFS_TRACE("Index Block parse error: Update Sequence Number\n");
+ return FALSE;
+ }
+
+ INDEX_ENTRY *ie;
+ ie = (INDEX_ENTRY*)((BYTE*)(&(ibBuf->EntryOffset)) + ibBuf->EntryOffset);
+
+ DWORD ieTotal = ie->Size;
+
+ while (ieTotal <= ibBuf->TotalEntrySize)
+ {
+ CIndexEntry *ieClass = new CIndexEntry(ie);
+ ibClass.InsertEntry(ieClass);
+
+ if (ie->Flags & INDEX_ENTRY_FLAG_LAST)
+ {
+ NTFS_TRACE("Last Index Entry\n");
+ break;
+ }
+
+ ie = (INDEX_ENTRY*)((BYTE*)ie + ie->Size); // Pick next
+ ieTotal += ie->Size;
+ }
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+////////////////////////////////////////////
+// Attribute: Bitmap
+////////////////////////////////////////////
+template <class TYPE_RESIDENT>
+class CAttr_Bitmap : public TYPE_RESIDENT
+{
+public:
+ CAttr_Bitmap(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr);
+ virtual ~CAttr_Bitmap();
+
+private:
+ ULONGLONG BitmapSize; // Bitmap data size
+ BYTE *BitmapBuf; // Bitmap data buffer
+ LONGLONG CurrentCluster;
+
+public:
+ BOOL IsClusterFree(const ULONGLONG &cluster) const;
+}; // CAttr_Bitmap
+
+template <class TYPE_RESIDENT>
+CAttr_Bitmap<TYPE_RESIDENT>::CAttr_Bitmap(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : TYPE_RESIDENT(ahc, fr)
+{
+ NTFS_TRACE1("Attribute: Bitmap (%sResident)\n", IsNonResident() ? "Non" : "");
+
+ CurrentCluster = -1;
+
+ if (IsDataRunOK())
+ {
+ BitmapSize = GetDataSize();
+
+ if (IsNonResident())
+ BitmapBuf = new BYTE[_ClusterSize];
+ else
+ {
+ BitmapBuf = new BYTE[(DWORD)BitmapSize];
+
+ DWORD len;
+ if (!(ReadData(0, BitmapBuf, (DWORD)BitmapSize, &len)
+ && len == (DWORD)BitmapSize))
+ {
+ BitmapBuf = NULL;
+ NTFS_TRACE("Read Resident Bitmap data failed\n");
+ }
+ else
+ {
+ NTFS_TRACE1("%u bytes of resident Bitmap data read\n", len);
+ }
+ }
+ }
+ else
+ {
+ BitmapSize = 0;
+ BitmapBuf = 0;
+ }
+}
+
+template <class TYPE_RESIDENT>
+CAttr_Bitmap<TYPE_RESIDENT>::~CAttr_Bitmap()
+{
+ if (BitmapBuf)
+ delete BitmapBuf;
+
+ NTFS_TRACE("CAttr_Bitmap deleted\n");
+}
+
+// Verify if a single cluster is free
+template <class TYPE_RESIDENT>
+BOOL CAttr_Bitmap<TYPE_RESIDENT>::IsClusterFree(const ULONGLONG &cluster) const
+{
+ if (!IsDataRunOK() || !BitmapBuf)
+ return FALSE;
+
+ if (IsNonResident())
+ {
+ LONGLONG idx = (LONGLONG)cluster >> 3;
+ DWORD clusterSize = ((CNTFSVolume*)Volume)->GetClusterSize();
+
+ LONGLONG clusterOffset = idx/clusterSize;
+ cluster -= (clusterOffset*clusterSize*8);
+
+ // Read one cluster of data if buffer mismatch
+ if (CurrentCluster != clusterOffset)
+ {
+ DWORD len;
+ if (ReadData(clusterOffset, BitmapBuf, clusterSize, &len) && len == clusterSize)
+ {
+ CurrentCluster = clusterOffset;
+ }
+ else
+ {
+ CurrentCluster = -1;
+ return FALSE;
+ }
+ }
+ }
+
+ // All the Bitmap data is already in BitmapBuf
+ DWORD idx = (DWORD)(cluster >> 3);
+ if (IsNonResident() == FALSE)
+ {
+ if (idx >= BitmapSize)
+ return TRUE; // Resident data bounds check error
+ }
+
+ BYTE fac = (BYTE)(cluster % 8);
+
+ return ((BitmapBuf[idx] & (1<<fac)) == 0);
+}
+
+
+////////////////////////////////////////////
+// List to hold external File Records
+////////////////////////////////////////////
+typedef CSList<CFileRecord> CFileRecordList;
+
+////////////////////////////////////////////
+// Attribute: Attribute List
+////////////////////////////////////////////
+template <class TYPE_RESIDENT>
+class CAttr_AttrList : public TYPE_RESIDENT
+{
+public:
+ CAttr_AttrList(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr);
+ virtual ~CAttr_AttrList();
+
+private:
+ CFileRecordList FileRecordList;
+}; // CAttr_AttrList
+
+template <class TYPE_RESIDENT>
+CAttr_AttrList<TYPE_RESIDENT>::CAttr_AttrList(const ATTR_HEADER_COMMON *ahc, const CFileRecord *fr) : TYPE_RESIDENT(ahc, fr)
+{
+ NTFS_TRACE("Attribute: Attribute List\n");
+ if (fr->FileReference == (ULONGLONG)-1)
+ return;
+
+ ULONGLONG offset = 0;
+ DWORD len;
+ ATTR_ATTRIBUTE_LIST alRecord;
+
+ while (ReadData(offset, &alRecord, sizeof(ATTR_ATTRIBUTE_LIST), &len) &&
+ len == sizeof(ATTR_ATTRIBUTE_LIST))
+ {
+ if (ATTR_INDEX(alRecord.AttrType) > ATTR_NUMS)
+ {
+ NTFS_TRACE("Attribute List parse error1\n");
+ break;
+ }
+
+ NTFS_TRACE1("Attribute List: 0x%04x\n", alRecord.AttrType);
+
+ ULONGLONG recordRef = alRecord.BaseRef & 0x0000FFFFFFFFFFFFUL;
+ if (recordRef != fr->FileReference) // Skip contained attributes
+ {
+ DWORD am = ATTR_MASK(alRecord.AttrType);
+ if (am & fr->AttrMask) // Skip unwanted attributes
+ {
+ CFileRecord *frnew = new CFileRecord(fr->Volume);
+ FileRecordList.InsertEntry(frnew);
+
+ frnew->AttrMask = am;
+ if (!frnew->ParseFileRecord(recordRef))
+ {
+ NTFS_TRACE("Attribute List parse error2\n");
+ break;
+ }
+ frnew->ParseAttrs();
+
+ // Insert new found AttrList to fr->AttrList
+ const CAttrBase *ab = (CAttrBase*)frnew->FindFirstAttr(alRecord.AttrType);
+ while (ab)
+ {
+ CAttrList *al = (CAttrList*)&fr->AttrList[ATTR_INDEX(alRecord.AttrType)];
+ al->InsertEntry((CAttrBase*)ab);
+ ab = frnew->FindNextAttr(alRecord.AttrType);
+ }
+
+ // Throw away frnew->AttrList entries to prevent free twice (fr will delete them)
+ frnew->AttrList[ATTR_INDEX(alRecord.AttrType)].ThrowAll();
+ }
+ }
+
+ offset += alRecord.RecordSize;
+ }
+}
+
+template <class TYPE_RESIDENT>
+CAttr_AttrList<TYPE_RESIDENT>::~CAttr_AttrList()
+{
+ NTFS_TRACE("CAttr_AttrList deleted\n");
+}
+
+#endif