diff options
Diffstat (limited to 'src/ntfsx.c')
-rw-r--r-- | src/ntfsx.c | 605 |
1 files changed, 353 insertions, 252 deletions
diff --git a/src/ntfsx.c b/src/ntfsx.c index b2f8a9f..8910f89 100644 --- a/src/ntfsx.c +++ b/src/ntfsx.c @@ -1,391 +1,492 @@ -// -// 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" +/* + * AUTHOR + * N. Nielsen + * + * 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> + */ + +#include <io.h> +#include <stdio.h> +#include <malloc.h> +#include <string.h> + +#include "scrounge.h" +#include "memref.h" #include "ntfs.h" #include "ntfsx.h" -#include "scrounge.h" -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// +ntfsx_datarun* ntfsx_datarun_alloc(byte* mem, byte* datarun) +{ + ntfsx_datarun* dr = (ntfsx_datarun*)malloc(sizeof(ntfsx_datarun)); + if(dr) + { + ASSERT(datarun); + dr->_mem = (byte*)refadd(mem); + dr->_datarun = datarun; + dr->_curpos = NULL; + + dr->cluster = 0; + dr->length = 0; + dr->sparse = false; + } + + return dr; +} -bool NTFS_Cluster::New(PartitionInfo* pInfo) +void ntfsx_datarun_free(ntfsx_datarun* dr) { - Free(); + if(dr->_mem) + { + refrelease(dr->_mem); + dr->_mem = NULL; + } - m_cbCluster = CLUSTER_SIZE(*pInfo); + free(dr); +} - m_pCluster = (byte*)refalloc(m_cbCluster); - return m_pCluster != NULL; +bool ntfsx_datarun_first(ntfsx_datarun* dr) +{ + dr->_curpos = dr->_datarun; + dr->cluster = 0; + dr->length = 0; + dr->sparse = false; + return ntfsx_datarun_next(dr); } -bool NTFS_Cluster::Read(PartitionInfo* pInfo, uint64 begSector, HANDLE hIn) +bool ntfsx_datarun_next(ntfsx_datarun* dr) { - if(!m_pCluster) - { - if(!New(pInfo)) - return false; - } + byte length; + byte roffset; + int64 offset; - // 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(); + ASSERT(dr->_curpos); + + if(!*(dr->_curpos)) return false; - } - DWORD dwRead = 0; - if(!ReadFile(hIn, m_pCluster, m_cbCluster, &dwRead, NULL) || - dwRead != m_cbCluster) - { - Free(); - ::SetLastError(ERROR_READ_FAULT); + length = *(dr->_curpos) & 0x0F; + roffset = *(dr->_curpos) >> 4; + + /* ASSUMPTION: length and offset are less 64 bit numbers */ + if(length == 0 || length > 8 || roffset > 8) return false; + + ASSERT(length <= 8); + ASSERT(roffset <= 8); + + (dr->_curpos)++; + + memset(&(dr->length), 0, sizeof(uint64)); + + memcpy(&(dr->length), (dr->_curpos), length); + (dr->_curpos) += length; + + + /* Note that offset can be negative */ + if(*((dr->_curpos) + (roffset - 1)) & 0x80) + memset(&offset, ~0, sizeof(int64)); + else + memset(&offset, 0, sizeof(int64)); + + memcpy(&offset, (dr->_curpos), roffset); + (dr->_curpos) += roffset; + + if(offset == 0) + { + dr->sparse = true; + } + else + { + dr->sparse = false; + dr->cluster += offset; } - ::SetLastError(ERROR_SUCCESS); return true; } -void NTFS_Cluster::Free() -{ - if(m_pCluster) - refrelease(m_pCluster); - - m_pCluster = NULL; -} -NTFS_Record::NTFS_Record(PartitionInfo* pInfo) -{ - m_pInfo = (PartitionInfo*)refadd(pInfo); -} -NTFS_Record::~NTFS_Record() + + + +bool ntfsx_cluster_reserve(ntfsx_cluster* clus, partitioninfo* info) { - refrelease(m_pInfo); + ntfsx_cluster_release(clus); + clus->size = CLUSTER_SIZE(*info); + + ASSERT(clus->size != 0); + clus->data = (byte*)refalloc(clus->size); + if(!clus->data) + { + errno = ENOMEM; + return false; + } + + return true; } -bool NTFS_Record::Read(uint64 begSector, HANDLE hIn) +bool ntfsx_cluster_read(ntfsx_cluster* clus, partitioninfo* info, uint64 begSector, int dd) { - if(!NTFS_Cluster::Read(m_pInfo, begSector, hIn)) - return false; + int64 pos; + size_t sz; - // 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; - } + if(!clus->data) + { + if(!ntfsx_cluster_reserve(clus, info)) + return false; + } + + pos = SECTOR_TO_BYTES(begSector); + if(_lseeki64(dd, pos, SEEK_SET) == -1) + return false; + + sz = read(dd, clus->data, clus->size); + if(sz == -1) + return false; + + if(sz != clus->size) + { + errno = ERANGE; + return false; + } return true; } -NTFS_Attribute* NTFS_Record::FindAttribute(uint32 attrType, HANDLE hIn) +void ntfsx_cluster_release(ntfsx_cluster* clus) { - NTFS_Attribute* pAttribute = NULL; + if(clus->data) + refrelease(clus->data); - // Make sure we have a valid record - ASSERT(GetHeader()); - NTFS_AttribHeader* pHeader = NTFS_FindAttribute(GetHeader(), attrType, (m_pCluster + m_cbCluster)); + clus->data = NULL; + clus->size = 0; +} - 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; - } +ntfsx_attribute* ntfsx_attribute_alloc(ntfsx_cluster* clus, ntfs_attribheader* header) +{ + ntfsx_attribute* attr = (ntfsx_attribute*)malloc(sizeof(ntfsx_attribute)); + if(attr) + { + attr->_header = header; + attr->_mem = (byte*)refadd(clus->data); + attr->_length = clus->size; + } - } + return attr; +} - pAttr = (NTFS_AttrListRecord*)((byte*)pAttr + pAttr->cbRecord); - } - } - } +void ntfsx_attribute_free(ntfsx_attribute* attr) +{ + if(attr->_mem) + { + refrelease(attr->_mem); + attr->_mem = NULL; + } - return pAttribute; + free(attr); } -bool NTFS_Attribute::NextAttribute(uint32 attrType) +ntfs_attribheader* ntfsx_attribute_header(ntfsx_attribute* attr) { - NTFS_AttribHeader* pHeader = NTFS_NextAttribute(GetHeader(), attrType, m_pMem + m_cbMem); - if(pHeader) - { - m_pHeader = pHeader; - return true; - } - - return false; + return attr->_header; } -NTFS_Attribute::NTFS_Attribute(NTFS_Cluster& clus, NTFS_AttribHeader* pHeader) +void* ntfsx_attribute_getresidentdata(ntfsx_attribute* attr) { - m_pHeader = pHeader; - m_pMem = clus.m_pCluster; - m_cbMem = clus.m_cbCluster; - refadd(m_pMem); + ntfs_attribresident* res = (ntfs_attribresident*)attr->_header; + ASSERT(!attr->_header->bNonResident); + return (byte*)(attr->_header) + res->offAttribData; } -void* NTFS_Attribute::GetResidentData() +uint32 ntfsx_attribute_getresidentsize(ntfsx_attribute* attr) { - ASSERT(!m_pHeader->bNonResident); - NTFS_AttribResident* pRes = (NTFS_AttribResident*)m_pHeader; - return (byte*)m_pHeader + pRes->offAttribData; + ntfs_attribresident* res = (ntfs_attribresident*)attr->_header; + ASSERT(!attr->_header->bNonResident); + return res->cbAttribData; } -uint32 NTFS_Attribute::GetResidentSize() +ntfsx_datarun* ntfsx_attribute_getdatarun(ntfsx_attribute* attr) { - ASSERT(!m_pHeader->bNonResident); - NTFS_AttribResident* pRes = (NTFS_AttribResident*)m_pHeader; - return pRes->cbAttribData; + ntfs_attribnonresident* nonres = (ntfs_attribnonresident*)attr->_header; + ASSERT(attr->_header->bNonResident); + return ntfsx_datarun_alloc(attr->_mem, (byte*)(attr->_header) + nonres->offDataRuns); } -NTFS_DataRun* NTFS_Attribute::GetDataRun() +bool ntfsx_attribute_next(ntfsx_attribute* attr, uint32 attrType) { - ASSERT(m_pHeader->bNonResident); - NTFS_AttribNonResident* pNonRes = (NTFS_AttribNonResident*)m_pHeader; + ntfs_attribheader* header = ntfs_nextattribute(attr->_header, attrType, + attr->_mem + attr->_length); + if(header) + { + attr->_header = header; + return true; + } - return new NTFS_DataRun(m_pMem, (byte*)m_pHeader + pNonRes->offDataRuns); + return false; } -NTFS_DataRun::NTFS_DataRun(byte* pClus, byte* pDataRun) + +ntfsx_record* ntfsx_record_alloc(partitioninfo* info) { - ASSERT(pDataRun); - m_pMem = (byte*)refadd(pClus); - m_pDataRun = pDataRun; - m_pCurPos = NULL; - m_firstCluster = m_numClusters = 0; - m_bSparse = false; + ntfsx_record* rec = (ntfsx_record*)malloc(sizeof(ntfsx_record)); + if(rec) + { + rec->info = info; + memset(&(rec->_clus), 0, sizeof(ntfsx_cluster)); + } + + return rec; } -bool NTFS_DataRun::First() +void ntfsx_record_free(ntfsx_record* record) { - m_pCurPos = m_pDataRun; - m_firstCluster = m_numClusters = 0; - m_bSparse = false; - return Next(); + ntfsx_cluster_release(&(record->_clus)); + free(record); } -bool NTFS_DataRun::Next() +bool ntfsx_record_read(ntfsx_record* record, uint64 begSector, int dd) { - ASSERT(m_pCurPos); + ntfs_recordheader* rechead; - if(!*m_pCurPos) - return false; + if(!ntfsx_cluster_read(&(record->_clus), record->info, begSector, dd)) + err(1, "couldn't read mft record from drive"); - byte cbLen = *m_pCurPos & 0x0F; - byte cbOff = *m_pCurPos >> 4; + /* Check and validate this record */ + rechead = ntfsx_record_header(record); + if(rechead->magic != kNTFS_RecMagic || + !ntfs_dofixups(record->_clus.data, record->_clus.size)) + { + warnx("invalid mft record"); + ntfsx_cluster_release(&(record->_clus)); + return false; + } - // ASSUMPTION length and offset are less 64 bit numbers - if(cbLen == 0 || cbLen > 8 || cbOff > 8) - return false; - - ASSERT(cbLen <= 8); - ASSERT(cbOff <= 8); + return true; +} - m_pCurPos++; +ntfs_recordheader* ntfsx_record_header(ntfsx_record* record) +{ + return (ntfs_recordheader*)(record->_clus.data); +} - memset(&m_numClusters, 0, sizeof(uint64)); +ntfsx_attribute* ntfsx_record_findattribute(ntfsx_record* record, uint32 attrType, int dd) +{ + ntfsx_attribute* attr = NULL; + ntfs_attribheader* attrhead; + ntfs_attriblistrecord* atlr; + ntfs_attribresident* resident; + uint64 mftRecord; + ntfsx_record* r2; + + /* Make sure we have a valid record */ + ASSERT(ntfsx_record_header(record)); + attrhead = ntfs_findattribute(ntfsx_record_header(record), + attrType, (record->_clus.data) + (record->_clus.size)); + + if(attrhead) + { + attr = ntfsx_attribute_alloc(&(record->_clus), attrhead); + } + else + { + /* Do attribute list thing here! */ + attrhead = ntfs_findattribute(ntfsx_record_header(record), kNTFS_ATTRIBUTE_LIST, + (record->_clus.data) + (record->_clus.size)); - memcpy(&m_numClusters, m_pCurPos, cbLen); - m_pCurPos += cbLen; + /* For now we only support Resident Attribute lists */ + if(dd && attrhead && !attrhead->bNonResident && record->info->mftmap) + { + resident = (ntfs_attribresident*)attrhead; + atlr = (ntfs_attriblistrecord*)((byte*)attrhead + resident->offAttribData); - int64 offset; + /* Go through AttrList records looking for this attribute */ + while((byte*)atlr < (byte*)attrhead + attrhead->cbAttribute) + { + /* Found it! */ + if(atlr->type == attrType) + { + /* Read in appropriate cluster */ + mftRecord = ntfsx_mftmap_sectorforindex(record->info->mftmap, atlr->refAttrib & 0xFFFFFFFFFFFF); - // Note that offset can be negative - if(*(m_pCurPos + (cbOff - 1)) & 0x80) - memset(&offset, ~0, sizeof(int64)); - else - memset(&offset, 0, sizeof(int64)); + r2 = ntfsx_record_alloc(record->info); + if(!r2) + return NULL; - memcpy(&offset, m_pCurPos, cbOff); - m_pCurPos += cbOff; + if(ntfsx_record_read(r2, mftRecord, dd)) + attr = ntfsx_record_findattribute(r2, attrType, dd); - if(offset == 0) - { - m_bSparse = true; - } - else - { - m_bSparse = false; - m_firstCluster += offset; + ntfsx_record_free(r2); + + if(attr) + break; + } + + atlr = (ntfs_attriblistrecord*)((byte*)atlr + atlr->cbRecord); + } + } } - return true; + return attr; } -NTFS_MFTMap::NTFS_MFTMap(PartitionInfo* pInfo) + + + +struct _ntfsx_mftmap_block { - m_pInfo = (PartitionInfo*)refadd(pInfo); - m_pBlocks = NULL; - m_count = 0; -} + uint64 firstSector; /* relative to the entire drive */ + uint64 length; /* length in MFT records */ +}; -NTFS_MFTMap::~NTFS_MFTMap() +void ntfsx_mftmap_init(ntfsx_mftmap* map, partitioninfo* info) { - refrelease(m_pInfo); + map->info = info; + map->_blocks = NULL; + map->_count = 0; +} - if(m_pBlocks) - free(m_pBlocks); +void ntfsx_mftmap_destroy(ntfsx_mftmap* map) +{ + if(map->_blocks) + { + free(map->_blocks); + map->_blocks = NULL; + map->_count = 0; + } } -bool NTFS_MFTMap::Load(NTFS_Record* pRecord, HANDLE hIn) +bool ntfsx_mftmap_load(ntfsx_mftmap* map, ntfsx_record* record, int dd) { - bool bRet = TRUE; - NTFS_Attribute* pAttribData = NULL; // Data Attribute - NTFS_DataRun* pDataRun = NULL; // Data runs for nonresident data + bool ret = true; + ntfsx_attribute* attribdata = NULL; /* Data Attribute */ + ntfsx_datarun* datarun = NULL; /* Data runs for nonresident data */ { - printf("[Processing MFT] "); + ntfs_attribheader* header; + ntfs_attribnonresident* nonres; + uint64 length; + uint64 firstSector; + uint32 allocated; - // TODO: Check here whether MFT has already been loaded + /* TODO: Check here whether MFT has already been loaded */ - // Get the MFT's data - pAttribData = pRecord->FindAttribute(kNTFS_DATA, hIn); - if(!pAttribData) RET_ERROR(ERROR_NTFS_INVALID); + /* Get the MFT's data */ + attribdata = ntfsx_record_findattribute(record, kNTFS_DATA, dd); + if(!attribdata) + RETWARNBX("invalid mft. no data attribute"); - if(!pAttribData->GetHeader()->bNonResident) - RET_ERROR(ERROR_NTFS_INVALID); + header = ntfsx_attribute_header(attribdata); + if(!header->bNonResident) + RETWARNBX("invalid mft. data attribute non-resident"); - pDataRun = pAttribData->GetDataRun(); - if(!pDataRun) - RET_ERROR(ERROR_NTFS_INVALID); + datarun = ntfsx_attribute_getdatarun(attribdata); + if(!datarun) + RETWARNBX("invalid mft. no data runs"); - NTFS_AttribNonResident* pNonRes = (NTFS_AttribNonResident*)pAttribData->GetHeader(); + nonres = (ntfs_attribnonresident*)header; - if(m_pBlocks) + if(map->_blocks) { - free(m_pBlocks); - m_pBlocks = NULL; + free(map->_blocks); + map->_blocks = NULL; } - m_count = 0; - uint32 allocated = 0; + map->_count = 0; + allocated = 0; - // Now loop through the data run - if(pDataRun->First()) + /* Now loop through the data run */ + if(ntfsx_datarun_first(datarun)) { do { - if(pDataRun->m_bSparse) - RET_ERROR(ERROR_NTFS_INVALID); + if(datarun->sparse) + RETWARNBX("invalid mft. sparse data runs"); - if(m_count >= allocated) + if(map->_count >= allocated) { allocated += 16; - m_pBlocks = (NTFS_Block*)realloc(m_pBlocks, allocated * sizeof(NTFS_Block)); - if(!m_pBlocks) - RET_FATAL(ERROR_NOT_ENOUGH_MEMORY); + map->_blocks = (struct _ntfsx_mftmap_block*)realloc(map->_blocks, + allocated * sizeof(struct _ntfsx_mftmap_block)); + if(!(map->_blocks)) + errx(1, "out of memory"); } - uint64 length = pDataRun->m_numClusters * ((m_pInfo->clusterSize * kSectorSize) / kNTFS_RecordLen); + ASSERT(map->info->cluster != 0); + + length = datarun->length * ((map->info->cluster * kSectorSize) / kNTFS_RecordLen); if(length == 0) continue; - uint64 firstSector = (pDataRun->m_firstCluster * m_pInfo->clusterSize) + m_pInfo->firstSector; - if(firstSector >= m_pInfo->lastSector) + firstSector = (datarun->cluster * map->info->cluster) + map->info->first; + if(firstSector >= map->info->end) continue; - m_pBlocks[m_count].length = length; - m_pBlocks[m_count].firstSector = firstSector; - m_count++; + map->_blocks[map->_count].length = length; + map->_blocks[map->_count].firstSector = firstSector; + map->_count++; } - while(pDataRun->Next()); + while(ntfsx_datarun_next(datarun)); } - bRet = true; - ::SetLastError(ERROR_SUCCESS); + ret = true; } -clean_up: +cleanup: - if(pAttribData) - delete pAttribData; - if(pDataRun) - delete pDataRun; + if(attribdata) + ntfsx_attribute_free(attribdata); + if(datarun) + ntfsx_datarun_free(datarun); - return bRet; + return ret; } -uint64 NTFS_MFTMap::GetLength() +uint64 ntfsx_mftmap_length(ntfsx_mftmap* map) { uint64 length = 0; + uint32 i; - for(uint32 i = 0; i < m_count; i++) - length += m_pBlocks[i].length; + for(i = 0; i < map->_count; i++) + length += map->_blocks[i].length; return length; } -uint64 NTFS_MFTMap::SectorForIndex(uint64 index) +uint64 ntfsx_mftmap_sectorforindex(ntfsx_mftmap* map, uint64 index) { - for(uint32 i = 0; i < m_count; i++) + uint32 i; + struct _ntfsx_mftmap_block* p; + uint64 sector; + + for(i = 0; i < map->_count; i++) { - NTFS_Block* p = m_pBlocks + i; + p = map->_blocks + i; - if(index > p->length) + if(index >= p->length) { index -= p->length; } else { - uint64 sector = index * (kNTFS_RecordLen / kSectorSize); + sector = index * (kNTFS_RecordLen / kSectorSize); sector += p->firstSector; - if(sector > m_pInfo->lastSector) + if(sector >= map->info->end) return kInvalidSector; return sector; |