1664 lines
38 KiB
C++
1664 lines
38 KiB
C++
/*
|
|
* 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
|