summaryrefslogtreecommitdiff
path: root/src/ntfsx.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ntfsx.c')
-rw-r--r--src/ntfsx.c259
1 files changed, 259 insertions, 0 deletions
diff --git a/src/ntfsx.c b/src/ntfsx.c
new file mode 100644
index 0000000..fe7d898
--- /dev/null
+++ b/src/ntfsx.c
@@ -0,0 +1,259 @@
+//
+// AUTHOR
+// N. Nielsen
+//
+// VERSION
+// 0.7
+//
+// LICENSE
+// This software is in the public domain.
+//
+// The software is provided "as is", without warranty of any kind,
+// express or implied, including but not limited to the warranties
+// of merchantability, fitness for a particular purpose, and
+// noninfringement. In no event shall the author(s) be liable for any
+// claim, damages, or other liability, whether in an action of
+// contract, tort, or otherwise, arising from, out of, or in connection
+// with the software or the use or other dealings in the software.
+//
+// SUPPORT
+// Send bug reports to: <nielsen@memberwebs.com>
+//
+
+// ntfsx.cpp: implementation of the NTFS_Record class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+#include "ntfs.h"
+#include "ntfsx.h"
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+bool NTFS_Cluster::New(PartitionInfo* pInfo)
+{
+ Free();
+
+ m_cbCluster = CLUSTER_SIZE(*pInfo);
+
+ m_pCluster = (byte*)refalloc(m_cbCluster);
+ return m_pCluster != NULL;
+}
+
+bool NTFS_Cluster::Read(PartitionInfo* pInfo, uint64 begSector, HANDLE hIn)
+{
+ if(!m_pCluster)
+ {
+ if(!New(pInfo))
+ return false;
+ }
+
+ // Read the mftRecord
+ uint64 offRecord = SECTOR_TO_BYTES(begSector);
+ LONG lHigh = HIGHDWORD(offRecord);
+ if(SetFilePointer(hIn, LOWDWORD(offRecord), &lHigh, FILE_BEGIN) == -1
+ && GetLastError() != NO_ERROR)
+ {
+ Free();
+ return false;
+ }
+
+ DWORD dwRead = 0;
+ if(!ReadFile(hIn, m_pCluster, m_cbCluster, &dwRead, NULL) ||
+ dwRead != m_cbCluster)
+ {
+ Free();
+ ::SetLastError(ERROR_READ_FAULT);
+ return false;
+ }
+
+ ::SetLastError(ERROR_SUCCESS);
+ return true;
+}
+
+NTFS_Record::NTFS_Record(PartitionInfo* pInfo)
+{
+ m_pInfo = (PartitionInfo*)refadd(pInfo);
+}
+
+NTFS_Record::~NTFS_Record()
+{
+ refrelease(m_pInfo);
+}
+
+bool NTFS_Record::Read(uint64 begSector, HANDLE hIn)
+{
+ if(!NTFS_Cluster::Read(m_pInfo, begSector, hIn))
+ return false;
+
+ // Check and validate this record
+ NTFS_RecordHeader* pRecord = GetHeader();
+ if(pRecord->magic != kNTFS_RecMagic ||
+ !NTFS_DoFixups(m_pCluster, m_cbCluster))
+ {
+ NTFS_Cluster::Free();
+ ::SetLastError(ERROR_NTFS_INVALID);
+ return false;
+ }
+
+ return true;
+}
+
+NTFS_Attribute* NTFS_Record::FindAttribute(uint32 attrType, HANDLE hIn)
+{
+ NTFS_Attribute* pAttribute = NULL;
+
+ // Make sure we have a valid record
+ ASSERT(GetHeader());
+ NTFS_AttribHeader* pHeader = NTFS_FindAttribute(GetHeader(), attrType, (m_pCluster + m_cbCluster));
+
+ if(pHeader)
+ {
+ pAttribute = new NTFS_Attribute(*this, pHeader);
+ }
+ else
+ {
+ // Do attribute list thing here!
+ pHeader = NTFS_FindAttribute(GetHeader(), kNTFS_ATTRIBUTE_LIST, (m_pCluster + m_cbCluster));
+
+ // For now we only support Resident Attribute lists
+ if(hIn && pHeader && !pHeader->bNonResident)
+ {
+ NTFS_AttribResident* pResident = (NTFS_AttribResident*)pHeader;
+ NTFS_AttrListRecord* pAttr = (NTFS_AttrListRecord*)((byte*)pHeader + pResident->offAttribData);
+
+ // Go through AttrList records looking for this attribute
+ while((byte*)pAttr < (byte*)pHeader + pHeader->cbAttribute)
+ {
+ // Found it!
+ if(pAttr->type == attrType)
+ {
+ // Read in appropriate cluster
+ uint64 mftRecord = NTFS_RefToSector(*m_pInfo, pAttr->refAttrib);
+
+ NTFS_Record recAttr(m_pInfo);
+ if(recAttr.Read(mftRecord, hIn))
+ {
+ pAttribute = recAttr.FindAttribute(attrType, hIn);
+ break;
+ }
+
+ }
+
+ pAttr = (NTFS_AttrListRecord*)((byte*)pAttr + pAttr->cbRecord);
+ }
+ }
+ }
+
+ return pAttribute;
+}
+
+bool NTFS_Attribute::NextAttribute(uint32 attrType)
+{
+ NTFS_AttribHeader* pHeader = NTFS_NextAttribute(GetHeader(), attrType, m_pMem + m_cbMem);
+ if(pHeader)
+ {
+ m_pHeader = pHeader;
+ return true;
+ }
+
+ return false;
+}
+
+NTFS_Attribute::NTFS_Attribute(NTFS_Cluster& clus, NTFS_AttribHeader* pHeader)
+{
+ m_pHeader = pHeader;
+ m_pMem = clus.m_pCluster;
+ m_cbMem = clus.m_cbCluster;
+ refadd(m_pMem);
+}
+
+void* NTFS_Attribute::GetResidentData()
+{
+ ASSERT(!m_pHeader->bNonResident);
+ NTFS_AttribResident* pRes = (NTFS_AttribResident*)m_pHeader;
+ return (byte*)m_pHeader + pRes->offAttribData;
+}
+
+uint32 NTFS_Attribute::GetResidentSize()
+{
+ ASSERT(!m_pHeader->bNonResident);
+ NTFS_AttribResident* pRes = (NTFS_AttribResident*)m_pHeader;
+ return pRes->cbAttribData;
+}
+
+NTFS_DataRun* NTFS_Attribute::GetDataRun()
+{
+ ASSERT(m_pHeader->bNonResident);
+ NTFS_AttribNonResident* pNonRes = (NTFS_AttribNonResident*)m_pHeader;
+
+ return new NTFS_DataRun(m_pMem, (byte*)m_pHeader + pNonRes->offDataRuns);
+}
+
+NTFS_DataRun::NTFS_DataRun(byte* pClus, byte* pDataRun)
+{
+ ASSERT(pDataRun);
+ m_pMem = (byte*)refadd(pClus);
+ m_pDataRun = pDataRun;
+ m_pCurPos = NULL;
+ m_firstCluster = m_numClusters = 0;
+ m_bSparse = false;
+}
+
+bool NTFS_DataRun::First()
+{
+ m_pCurPos = m_pDataRun;
+ m_firstCluster = m_numClusters = 0;
+ m_bSparse = false;
+ return Next();
+}
+
+bool NTFS_DataRun::Next()
+{
+ ASSERT(m_pCurPos);
+
+ if(!*m_pCurPos)
+ return false;
+
+ byte cbLen = *m_pCurPos & 0x0F;
+ byte cbOff = *m_pCurPos >> 4;
+
+ // ASSUMPTION length and offset are less 64 bit numbers
+ if(cbLen == 0 || cbLen > 8 || cbOff > 8)
+ return false;
+
+ ASSERT(cbLen <= 8);
+ ASSERT(cbOff <= 8);
+
+ m_pCurPos++;
+
+ memset(&m_numClusters, 0, sizeof(uint64));
+
+ memcpy(&m_numClusters, m_pCurPos, cbLen);
+ m_pCurPos += cbLen;
+
+ int64 offset;
+
+ // Note that offset can be negative
+ if(*(m_pCurPos + (cbOff - 1)) & 0x80)
+ memset(&offset, ~0, sizeof(int64));
+ else
+ memset(&offset, 0, sizeof(int64));
+
+ memcpy(&offset, m_pCurPos, cbOff);
+ m_pCurPos += cbOff;
+
+ if(offset == 0)
+ {
+ m_bSparse = true;
+ }
+ else
+ {
+ m_bSparse = false;
+ m_firstCluster += offset;
+ }
+
+ return true;
+}