diff options
Diffstat (limited to 'src/scrounge.c')
-rw-r--r-- | src/scrounge.c | 1002 |
1 files changed, 386 insertions, 616 deletions
diff --git a/src/scrounge.c b/src/scrounge.c index 2cb0d71..f4436de 100644 --- a/src/scrounge.c +++ b/src/scrounge.c @@ -1,717 +1,487 @@ -// -// 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> -// - -// Scrounge.cpp -// - -#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 <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "scrounge.h" #include "ntfs.h" #include "ntfsx.h" -#include "usuals.h" -#include "drive.h" #include "locks.h" -#include "scrounge.h" -// ---------------------------------------------------------------------- -// Process a potential MFT Record. For directories create the directory -// and for files write out the file. -// -// Current Directory is the output directory -// hIn is an open drive handle -// pInfo is partition info about hIn (needs to be a ref counted pointer) - -BOOL ProcessMFTRecord(PartitionInfo* pInfo, uint64 sector, NTFS_MFTMap* map, HANDLE hIn) +typedef struct _filebasics { - // Declare data that needs cleaning up first - BOOL bRet = TRUE; // Return value - HANDLE hFile = NULL; // Output file handle - NTFS_Attribute* pAttribName = NULL; // Filename Attribute - NTFS_Attribute* pAttribData = NULL; // Data Attribute - NTFS_DataRun* pDataRun = NULL; // Data runs for nonresident data - - ASSERT(sector != kInvalidSector); - - - // Tracks whether or not we output a file - bool bFile = false; - - { - // Read the MFT record - NTFS_Record record(pInfo); - if(!record.Read(sector, hIn)) - PASS_ERROR(); - - NTFS_RecordHeader* pRecord = record.GetHeader(); - - - // Check if this record is in use - if(!(pRecord->flags & kNTFS_RecFlagUse)) - RET_ERROR(ERROR_SUCCESS); - + wchar_t filename[MAX_PATH + 1]; + uint64 created; + uint64 modified; + uint64 accessed; + uint32 flags; + uint64 parent; +} +filebasics; - // Info that we use later - WCHAR fileName[MAX_PATH + 1]; - FILETIME ftCreated; - FILETIME ftModified; - FILETIME ftAccessed; - DWORD fileAttrib = 0; - uint64 mftParent = kInvalidSector; - byte* pResidentData = NULL; +void processRecordFileBasics(partitioninfo* pi, ntfsx_record* record, filebasics* basics) +{ + /* Data Attribute */ + ntfsx_attribute* attr = NULL; + { + byte* resident = NULL; + ntfs_attribfilename* filename; + byte nameSpace; - // Now get the name and info... - pAttribName = record.FindAttribute(kNTFS_FILENAME, hIn); - if(!pAttribName) RET_ERROR(ERROR_SUCCESS); + ASSERT(record); + memset(basics, 0, sizeof(filebasics)); + basics->parent = kInvalidSector; + /* Now get the name and info... */ + attr = ntfsx_record_findattribute(record, kNTFS_FILENAME, pi->device); + if(!attr) goto cleanup; - byte nameSpace = kNTFS_NameSpacePOSIX; - memset(fileName, 0, sizeof(fileName)); + nameSpace = kNTFS_NameSpacePOSIX; + memset(basics->filename, 0, sizeof(basics->filename)); do { - // TODO ASSUMPTION: File name is always resident - ASSERT(!pAttribName->GetHeader()->bNonResident); + /* TODO ASSUMPTION: File name is always resident */ + ASSERT(!ntfsx_attribute_header(attr)->bNonResident); - // Get out all the info we need - NTFS_AttrFileName* pFileName = (NTFS_AttrFileName*)pAttribName->GetResidentData(); + /* Get out all the info we need */ + filename = (ntfs_attribfilename*)ntfsx_attribute_getresidentdata(attr); + ASSERT(filename); - // There can be multiple filenames with different namespaces - // so choose the best one - if(NTFS_IsBetterNameSpace(nameSpace, pFileName->nameSpace)) + /* + * There can be multiple filenames with different + * namespaces so choose the best one + */ + if(ntfs_isbetternamespace(nameSpace, filename->nameSpace)) { - // Dates - NTFS_MakeFileTime(pFileName->timeCreated, ftCreated); - NTFS_MakeFileTime(pFileName->timeModified, ftModified); - NTFS_MakeFileTime(pFileName->timeRead, ftAccessed); - - // File Name - wcsncpy(fileName, (wchar_t*)(((byte*)pFileName) + sizeof(NTFS_AttrFileName)), pFileName->cFileName); - fileName[pFileName->cFileName] = 0; - - // Attributes - if(pFileName->flags & kNTFS_FileReadOnly) - fileAttrib |= FILE_ATTRIBUTE_READONLY; - if(pFileName->flags & kNTFS_FileHidden) - fileAttrib |= FILE_ATTRIBUTE_HIDDEN; - if(pFileName->flags & kNTFS_FileArchive) - fileAttrib |= FILE_ATTRIBUTE_ARCHIVE; - if(pFileName->flags & kNTFS_FileSystem) - fileAttrib |= FILE_ATTRIBUTE_SYSTEM; - - // Parent Directory - if(map) - mftParent = map->SectorForIndex(pFileName->refParent & 0xFFFFFFFFFFFF); - - // Namespace - nameSpace = pFileName->nameSpace; + /* Dates */ + basics->created = filename->timeCreated; + basics->modified = filename->timeModified; + basics->accessed = filename->timeRead; + + /* File Name */ + wcsncpy(basics->filename, (wchar_t*)(((byte*)filename) + sizeof(ntfs_attribfilename)), filename->cFileName); + basics->filename[filename->cFileName] = 0; + + /* Attributes */ + basics->flags = filename->flags; + + /* Parent Directory */ + basics->parent = filename->refParent & 0xFFFFFFFFFFFF; + + /* Namespace */ + nameSpace = filename->nameSpace; } } - while(pAttribName->NextAttribute(kNTFS_FILENAME)); + while(ntfsx_attribute_next(attr, kNTFS_FILENAME)); + } +cleanup: + if(attr) + ntfsx_attribute_free(attr); +} - // Check if we got a file name - if(fileName[0] == 0) - RET_ERROR(ERROR_NTFS_INVALID); +void processMFTRecord(partitioninfo* pi, uint64 sector, int level) +{ + ntfsx_record* record = NULL; + ntfsx_attribute* attribdata = NULL; + ntfsx_datarun* datarun = NULL; + int outfile = -1; + ntfsx_cluster cluster; + memset(&cluster, 0, sizeof(cluster)); - // Check if it's the root - // If so then bumm out cuz we don't want to have anything to do with it - if(sector == mftParent || // Root is it's own parent - !wcscmp(fileName, L".")) // Or is called '.' - RET_ERROR(ERROR_SUCCESS); + { + filebasics basics; + ntfs_recordheader* header; + uint64 parentSector; + uint64 dataSector; + uint16 rename = 0; + uint64 fileSize; + uint32 i; + uint32 num; + wchar_t filename2[MAX_PATH + 1]; + ntfs_attribheader* attrhead; + ntfs_attribnonresident* nonres; + + ASSERT(sector != kInvalidSector); + + record = ntfsx_record_alloc(pi); + if(!record) + errx(1, "out of memory"); + + /* Read the MFT record */ + if(!ntfsx_record_read(record, sector, pi->device)) + RETURN; + + header = ntfsx_record_header(record); + ASSERT(header); + + if(!(header->flags & kNTFS_RecFlagUse)) + RETURN; + + /* Try and get a file name out of the header */ + processRecordFileBasics(pi, record, &basics); + + if(basics.filename[0] == 0) + { + RETWARNX("invalid mft record. in use, but no filename"); + } + /* If it's the root folder then return */ + if(!wcscmp(basics.filename, L".")) + RETURN; - // If it's the MFT then we read that in - if(!wcscmp(fileName, kNTFS_MFTName)) + /* Process parent folders if available */ + if(basics.parent != kInvalidSector) { - if(map) + /* Only if we have MFT map info available */ + if(pi->mftmap) { - printf("[Processing MFT] "); + parentSector = ntfsx_mftmap_sectorforindex(pi->mftmap, basics.parent); - // We found the MFT, let's load it up - if(!map->Load(&record, hIn)) - PASS_FATAL(); + if(parentSector == kInvalidSector) + warnx("invalid parent directory for file: %S", basics.filename); + else + processMFTRecord(pi, parentSector, level + 1); } - - RET_ERROR(ERROR_SUCCESS); } + printf(level == 0 ? "\\%S\n" : "\\%S", basics.filename); - if(mftParent != kInvalidSector) + /* Directory handling: */ + if(header->flags & kNTFS_RecFlagDir) { - // Create Parent folders - if(!ProcessMFTRecord(pInfo, mftParent, map, hIn)) - PASS_FATAL(); + /* Try to change to the directory */ + /* PORT: Wide character file functions */ + if(_wchdir(basics.filename) == -1) + { + /* PORT: Wide character file functions */ + if(_wmkdir(basics.filename) == -1) + { + warnx("couldn't create directory '%S' putting files in parent directory", basics.filename); + } + else + { + setFileAttributes(basics.filename, basics.flags); + _wchdir(basics.filename); + } + } + + RETURN; } - // If it's a folder then create it - if(pRecord->flags & kNTFS_RecFlagDir) - { - // Try to change to dir - if(!SetCurrentDirectoryW(fileName)) - { - // Otherwise create dir - if(CreateDirectoryW(fileName, NULL)) - { - // And set attributes - SetFileAttributesW(fileName, fileAttrib); - SetCurrentDirectoryW(fileName); - } - } + /* Normal file handling: */ + /* PORT: Wide character file functions */ + outfile = _wopen(basics.filename, _O_BINARY | _O_CREAT | _O_EXCL | _O_WRONLY); + + wcsncpy(filename2, basics.filename, MAX_PATH); + filename2[MAX_PATH] = 0; - wprintf(L"\\%s", fileName); - } + while(outfile == -1 && errno == EEXIST && rename < 0x1000) + { + if(wcslen(basics.filename) + 7 >= MAX_PATH) + { + warnx("file name too long on duplicate file: %S", basics.filename); + goto cleanup; + } + wcscpy(basics.filename, filename2); + wcscat(basics.filename, L"."); - // Otherwise write the file data - else - { - // Write to the File - hFile = CreateFileW(fileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL); + _itow(rename, basics.filename + wcslen(basics.filename), 10); + rename++; - uint16 nRename = 0; - wchar_t fileName2[MAX_PATH + 1]; - wcscpy(fileName2, fileName); + outfile = _wopen(basics.filename, _O_BINARY | _O_CREAT | _O_EXCL | _O_WRONLY); + } + + if(outfile == -1) + { + warnx("couldn't open output file: %S", basics.filename); + goto cleanup; + } - // For duplicate files we add .x to the file name where x is a number - while(hFile == INVALID_HANDLE_VALUE && ::GetLastError() == ERROR_FILE_EXISTS - && nRename < 0x1000000) - { - wcscpy(fileName, fileName2); - wcscat(fileName, L"."); - uint16 len = wcslen(fileName); - - // Make sure we don't have a buffer overflow - if(len > MAX_PATH - 5) - break; - - _itow(nRename, fileName + len, 10); - nRename++; - - hFile = CreateFileW(fileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL); - } - - wprintf(L"\\%s", fileName); - bFile = true; - // Check if successful after all that - if(hFile == INVALID_HANDLE_VALUE) - PASS_FATAL(); + attribdata = ntfsx_record_findattribute(record, kNTFS_DATA, pi->device); + if(!attribdata) + RETWARNX("invalid mft record. no data attribute found"); + + attrhead = ntfsx_attribute_header(attribdata); - - DWORD dwDone = 0; - uint64 fileSize = 0; + /* For resident data just write it out */ + if(!attrhead->bNonResident) + { + uint32 length = ntfsx_attribute_getresidentsize(attribdata); + byte* data = ntfsx_attribute_getresidentdata(attribdata); + if(!data) + RETWARNX("invalid mft record. resident data screwed up"); - // Get the File's data - pAttribData = record.FindAttribute(kNTFS_DATA, hIn); - if(!pAttribData) RET_ERROR(ERROR_NTFS_INVALID); + if(write(outfile, data, length) != (int32)length) + RETWARN("couldn't write data to output file"); + } - - // For Resident data just write it out - if(!pAttribData->GetHeader()->bNonResident) - { - if(!WriteFile(hFile, pAttribData->GetResidentData(), pAttribData->GetResidentSize(), &dwDone, NULL)) - PASS_FATAL(); - } + /* For non resident data it's a bit more involved */ + else + { + datarun = ntfsx_attribute_getdatarun(attribdata); + if(!datarun) + errx(1, "out of memory"); - // For Nonresident data a bit more involved - else - { - pDataRun = pAttribData->GetDataRun(); - ASSERT(pDataRun != NULL); + nonres = (ntfs_attribnonresident*)attrhead; + fileSize = nonres->cbAttribData; - NTFS_AttribNonResident* pNonRes = (NTFS_AttribNonResident*)pAttribData->GetHeader(); - fileSize = pNonRes->cbAttribData; + /* Allocate a cluster for reading and writing */ + if(!ntfsx_cluster_reserve(&cluster, pi)) + errx(1, "out of memory"); - // Allocate a cluster for reading and writing - NTFS_Cluster clus; - if(!clus.New(pInfo)) - RET_FATAL(ERROR_NOT_ENOUGH_MEMORY); - + if(ntfsx_datarun_first(datarun)) + { + do + { + /* Check to see if we have a bogus data run */ + if(fileSize == 0 && datarun->length) + { + warnx("invalid mft record. file length invalid or extra data in file"); + break; + } + + /* Sparse clusters we just write zeros */ + if(datarun->sparse) + { + memset(cluster.data, 0, cluster.size); + + for(i = 0; i < datarun->length && fileSize; i++) + { + num = cluster.size; + + if(fileSize < 0xFFFFFFFF && num > (uint32)fileSize) + num = (uint32)fileSize; + + if(write(outfile, cluster.data, num) != (int32)num) + err(1, "couldn't write to output file: %S", basics.filename); + + fileSize -= num; + } + } + + /* Handle not sparse clusters */ + else + { + if(pi->locks) + { + /* Add a location lock so any raw scrounging won't do + this cluster later */ + addLocationLock(pi->locks, CLUSTER_TO_SECTOR(*pi, datarun->cluster), + CLUSTER_TO_SECTOR(*pi, datarun->cluster + datarun->length)); + } + + for(i = 0; i < datarun->length && fileSize; i++) + { + num = min(cluster.size, (uint32)fileSize); + dataSector = CLUSTER_TO_SECTOR(*pi, (datarun->cluster + i)); + + if(!ntfsx_cluster_read(&cluster, pi, dataSector, pi->device)) + err(1, "couldn't read sector from disk"); + + if(write(outfile, cluster.data, num) != (int32)num) + err(1, "couldn't write to output file: %S", basics.filename); + + fileSize -= num; + } + } + } + while(ntfsx_datarun_next(datarun)); + } + } - // Now loop through the data run - if(pDataRun->First()) - { - do - { - // If it's a sparse cluster then just write zeros - if(pDataRun->m_bSparse) - { - memset(clus.m_pCluster, 0, clus.m_cbCluster); - - for(uint32 i = 0; i < pDataRun->m_numClusters && fileSize; i++) - { - DWORD dwToWrite = clus.m_cbCluster; - if(!HIGHDWORD(fileSize) && dwToWrite > (DWORD)fileSize) - dwToWrite = (DWORD)fileSize; - - if(!WriteFile(hFile, clus.m_pCluster, dwToWrite, &dwDone, NULL)) - PASS_FATAL(); - - fileSize -= dwToWrite; - } - } - - // Not sparse - else - { - // Add a lock on those clusters so we don't have to scrounge'm later - AddLocationLock(pInfo, CLUSTER_TO_SECTOR(*pInfo, pDataRun->m_firstCluster), - CLUSTER_TO_SECTOR(*pInfo, pDataRun->m_firstCluster + pDataRun->m_numClusters)); - - // Read and write clusters out - for(uint32 i = 0; i < pDataRun->m_numClusters && fileSize; i++) - { - DWORD dwToWrite = min(clus.m_cbCluster, (DWORD)fileSize); - uint64 sector = CLUSTER_TO_SECTOR(*pInfo, (pDataRun->m_firstCluster + i)); - - if(!clus.Read(pInfo, sector, hIn)) - PASS_ERROR(); - - if(!WriteFile(hFile, clus.m_pCluster, dwToWrite, &dwDone, NULL)) - PASS_FATAL(); - - fileSize -= dwToWrite; - } - } - } - while(pDataRun->Next()); - } - } + if(fileSize != 0) + warnx("invalid mft record. couldn't find all data for file"); - // TODO: More intelligence needed here - if(fileSize != 0) - printf(" (Entire file not written)"); + close(outfile); + outfile = -1; - SetFileTime(hFile, &ftCreated, &ftAccessed, &ftModified); - - CloseHandle(hFile); - hFile = NULL; + setFileTime(basics.filename, &(basics.created), + &(basics.accessed), &(basics.modified)); - SetFileAttributesW(fileName, fileAttrib); - } - } + setFileAttributes(basics.filename, basics.flags); + } - bRet = TRUE; - ::SetLastError(ERROR_SUCCESS); +cleanup: + if(record) + ntfsx_record_free(record); -clean_up: - if(hFile && hFile != INVALID_HANDLE_VALUE) - CloseHandle(hFile); - if(pAttribName) - delete pAttribName; - if(pAttribData) - delete pAttribData; - if(pDataRun) - delete pDataRun; + ntfsx_cluster_release(&cluster); - if(bFile) - { - if(::GetLastError() != ERROR_SUCCESS) - { - printf(" ("); - PrintLastError(); - fputc(')', stdout); - } - } + if(attribdata) + ntfsx_attribute_free(attribdata); + if(datarun) + ntfsx_datarun_free(datarun); - return bRet; + if(outfile != -1) + close(outfile); } -// ---------------------------------------------------------------------- -// Helper function to print out errors - -void PrintLastError() +void scroungeMFT(partitioninfo* pi, ntfsx_mftmap* map) { - DWORD dwErr = ::GetLastError(); - switch(dwErr) - { - case ERROR_NTFS_INVALID: - printf("Invalid NTFS data structure"); - break; + ntfsx_record* record = NULL; + uint64 sector; + filebasics basics; + ntfs_recordheader* header; - case ERROR_NTFS_NOTIMPLEMENT: - printf("NTFS feature not implemented"); - break; + /* Try and find the MFT at the given location */ + sector = pi->mft + pi->first; - default: - { - LPVOID lpMsgBuf; - - DWORD dwRet = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_MAX_WIDTH_MASK, - NULL, - dwErr, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language - (LPTSTR) &lpMsgBuf, - 0, - NULL); - - if(dwRet && lpMsgBuf) - { - // Remove return - ((LPTSTR)lpMsgBuf)[dwRet - 2] = 0; + if(sector >= pi->end) + errx(2, "invalid mft. past end of partition"); - wprintf((LPTSTR)lpMsgBuf); + record = ntfsx_record_alloc(pi); + if(!record) + errx(1, "out of memory"); - // Free the buffer. - ::LocalFree(lpMsgBuf); - } - } - }; -} + /* Read the MFT record */ + if(!ntfsx_record_read(record, sector, pi->device)) + err(1, "couldn't read mft"); + header = ntfsx_record_header(record); + ASSERT(header); -// ---------------------------------------------------------------------- -// Scrounge the partition for MFT Records and hand off to -// ProcessMFTRecord for processing + if(!(header->flags & kNTFS_RecFlagUse)) + errx(2, "invalid mft. marked as not in use"); -BOOL ScroungeMFTRecords(PartitionInfo* pInfo, HANDLE hIn) -{ - uint64 numRecords = 0; + /* Try and get a file name out of the header */ - // Save current directory away - TCHAR curDir[MAX_PATH + 1]; - GetCurrentDirectory(MAX_PATH, curDir); + processRecordFileBasics(pi, record, &basics); - NTFS_MFTMap map(pInfo); + if(wcscmp(basics.filename, kNTFS_MFTName)) + errx(2, "invalid mft. wrong record"); - // Try and find the MFT at the given location - uint64 sector = pInfo->offMFT + pInfo->firstSector; + fprintf(stderr, "[Processing MFT...]\n"); - if(sector < pInfo->lastSector) - { - BOOL bRet = ProcessMFTRecord(pInfo, sector, &map, hIn); + /* Load the MFT data runs */ - fputc('\n', stdout); + if(!ntfsx_mftmap_load(map, record, pi->device)) + err(1, "error reading in mft"); - if(!bRet) - return FALSE; - } + if(ntfsx_mftmap_length(map) == 0) + errx(1, "invalid mft. no records in mft"); - uint64 length = map.GetLength(); - if(length == 0) - { - printf("[MFT not found at specified location. No folder structure]\n"); - return ScroungeRawRecords(pInfo, hIn); - } + ntfsx_record_free(record); +} - for(uint64 i = 1; i < length; i += (kNTFS_RecordLen / kSectorSize)) - { - // Move to right output directory - SetCurrentDirectory(curDir); - // TODO: Warn when invalid - uint64 sector = map.SectorForIndex(i); +void scroungeUsingMFT(partitioninfo* pi) +{ + uint64 numRecords = 0; + char dir[MAX_PATH]; + ntfsx_mftmap map; + uint64 length; + uint64 sector; + uint64 i; + + fprintf(stderr, "[Scrounging via MFT...]\n"); + + /* Save current directory away */ + getcwd(dir, MAX_PATH); + + /* Get the MFT map ready */ + memset(&map, 0, sizeof(map)); + ntfsx_mftmap_init(&map, pi); + pi->mftmap = ↦ + + + /* + * Make sure the MFT is actually where they say it is. + * This also fills in the valid cluster size if needed + */ + scroungeMFT(pi, &map); + length = ntfsx_mftmap_length(&map); + + for(i = 1; i < length; i ++) + { + sector = ntfsx_mftmap_sectorforindex(&map, i); if(sector == kInvalidSector) + { + warnx("invalid index in mft: %d", i); continue; + } - // Then process it - BOOL bRet = ProcessMFTRecord(pInfo, sector, &map, hIn); - - fputc('\n', stdout); + /* Process the record */ + processMFTRecord(pi, sector, 0); - if(!bRet) - return FALSE; + /* Move to right output directory */ + chdir(dir); } - return TRUE; + pi->mftmap = NULL; } -BOOL ScroungeRawRecords(PartitionInfo* pInfo, HANDLE hIn) +void scroungeUsingRaw(partitioninfo* pi) { byte buffSec[kSectorSize]; - DWORD dwDummy = 0; + char dir[_MAX_PATH + 1]; + uint64 sec; + drivelocks locks; + int64 pos; + size_t sz; + uint32 magic = kNTFS_RecMagic; - uint64 numRecords = 0; - - // Save current directory away - TCHAR curDir[MAX_PATH + 1]; - GetCurrentDirectory(MAX_PATH, curDir); + fprintf(stderr, "[Scrounging raw records...]\n"); - // Loop through sectors - for(uint64 sec = pInfo->firstSector; sec < pInfo->lastSector; sec++) - { - // See if the current sector has already been read - if(CheckLocationLock(pInfo, sec)) - { - // TODO: check this - sec--; - continue; - } + /* Save current directory away */ + getcwd(dir, _MAX_PATH); - // Read the mftRecord - uint64 offRecord = SECTOR_TO_BYTES(sec); - LONG lHigh = HIGHDWORD(offRecord); - if(SetFilePointer(hIn, LOWDWORD(offRecord), &lHigh, FILE_BEGIN) == -1 - && GetLastError() != NO_ERROR) - return FALSE; + /* Get the locks ready */ + memset(&locks, 0, sizeof(locks)); + pi->locks = &locks; - if(!ReadFile(hIn, buffSec, kSectorSize, &dwDummy, NULL)) - return FALSE; - - // Check beginning of sector for the magic signature - if(!memcmp(&kNTFS_RecMagic, &buffSec, sizeof(kNTFS_RecMagic))) - { - // Move to right output directory - SetCurrentDirectory(curDir); - - // Then process it - BOOL bRet = ProcessMFTRecord(pInfo, sec, NULL, hIn); - - fputc('\n', stdout); - - if(!bRet) - return FALSE; - - } - } - - return TRUE; -} - - -// ---------------------------------------------------------------------- -// Output strings - -const char kPrintHeader[] = "Scrounge (NTFS) Version 0.7\n\n"; - -const char kPrintData[] = "\ - Start Sector End Sector Cluster Size MFT Offset \n\ -==================================================================\n\ -"; - -const char kPrintDrive[] = "\nDrive: %u\n"; -const char kPrintDriveInfo[] = " %-15u %-15u "; -const char kPrintNTFSInfo[] = "%-15u %-15u"; - -const char kPrintHelp[] = "\ -Recovers an NTFS partition with a corrupted MFT. \n\ - \n\ -Usage: scrounge drive start end cluster mft [outdir] \n\ - \n\ - drive: Physical drive number. \n\ - start: First sector of partition. \n\ - end: Last sector of partition. \n\ - cluster: Cluster size for the partition (in sectors). \n\ - mft: Offset from beginning of partition to MFT (in sectors). \n\ - outdir: Output directory (optional). \n\ - \n\ -"; - - -// ---------------------------------------------------------------------- -// Info functions - -int PrintNTFSInfo(HANDLE hDrive, uint64 tblSector) -{ - byte sector[kSectorSize]; - - uint64 pos = SECTOR_TO_BYTES(tblSector); - LONG lHigh = HIGHDWORD(pos); - if(SetFilePointer(hDrive, LOWDWORD(pos), &lHigh, FILE_BEGIN) == -1 - && GetLastError() != NO_ERROR) - return 1; - - DWORD dwRead = 0; - if(!ReadFile(hDrive, sector, kSectorSize, &dwRead, NULL)) - return 1; - - NTFS_BootSector* pBoot = (NTFS_BootSector*)sector; - if(!memcmp(pBoot->sysId, kNTFS_SysId, sizeof(pBoot->sysId))) - printf(kPrintNTFSInfo, pBoot->secPerClus, pBoot->offMFT * pBoot->secPerClus); - - wprintf(L"\n"); - return 0; -} - - -int PrintPartitionInfo(HANDLE hDrive, uint64 tblSector) -{ - ASSERT(sizeof(Drive_MBR) == kSectorSize); - Drive_MBR mbr; - - uint64 pos = SECTOR_TO_BYTES(tblSector); - LONG lHigh = HIGHDWORD(pos); - if(SetFilePointer(hDrive, LOWDWORD(pos), &lHigh, FILE_BEGIN) == -1 - && GetLastError() != NO_ERROR) - return 1; - - DWORD dwRead = 0; - if(!ReadFile(hDrive, &mbr, sizeof(Drive_MBR), &dwRead, NULL)) - return 1; - - if(mbr.sig == kMBR_Sig) + /* Loop through sectors */ + for(sec = pi->first; sec < pi->end; sec++) { - for(int i = 0; i < 4; i++) - { - if(mbr.partitions[i].system == kPartition_Extended || - mbr.partitions[i].system == kPartition_ExtendedLBA) - { - PrintPartitionInfo(hDrive, tblSector + mbr.partitions[i].startsec); - } - else if(!mbr.partitions[i].system == kPartition_Invalid) - { - printf(kPrintDriveInfo, (uint32)tblSector + mbr.partitions[i].startsec, (uint32)tblSector + mbr.partitions[i].endsec); - PrintNTFSInfo(hDrive, tblSector + (uint64)mbr.partitions[i].startsec); - } - } - } - - return 0; -} + if(checkLocationLock(&locks, sec)) + continue; -const WCHAR kDriveName[] = L"\\\\.\\PhysicalDrive%d"; + /* Read the record */ + pos = SECTOR_TO_BYTES(sec); + if(_lseeki64(pi->device, pos, SEEK_SET) == -1) + errx(1, "can't seek device to sector"); -int PrintData() -{ - printf(kPrintHeader); - printf(kPrintData); + sz = read(pi->device, buffSec, kSectorSize); + if(sz == -1 || sz != kSectorSize) + errx(1, "can't read drive sector"); - WCHAR driveName[MAX_PATH]; - - // LIMIT: 256 Drives - for(int i = 0; i < 0x100; i++) - { - wsprintf(driveName, kDriveName, i); - - HANDLE hDrive = CreateFile(driveName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if(hDrive != INVALID_HANDLE_VALUE) + /* Check beginning of sector for the magic signature */ + if(!memcmp(&magic, &buffSec, sizeof(magic))) { - printf(kPrintDrive, i); - - PrintPartitionInfo(hDrive, 0); - - CloseHandle(hDrive); + /* Process the record */ + processMFTRecord(pi, sec, 0); } } - return 2; + pi->locks = NULL; } - - -// ---------------------------------------------------------------------- -// Main Program - -int main(int argc, char* argv[]) -{ - int curArg = 1; - - if(argc < 2) - return PrintData(); - - - // Check for flags - if(*(argv[curArg]) == '-' || *(argv[curArg]) == '/' ) - { - char* arg = argv[curArg]; - arg++; - - while(*arg != '\0') - { - switch(tolower(*arg)) - { - - // Help - case 'h': - printf(kPrintHeader); - printf(kPrintHelp); - return 2; - - default: - printf("scrounge: invalid option '%c'\n", *arg); - return PrintData(); - } - - arg++; - } - - curArg++; - } - - PartitionInfo* pInfo = CreatePartitionInfo(); - if(!pInfo) - { - printf("scrounge: Out of Memory.\n"); - return 1; - } - - if(curArg + 5 > argc) - { - printf("scrounge: invalid option(s).\n"); - return 2; - } - - // Next param should be the drive - byte driveNum = atoi(argv[curArg++]); - - // Followed by the partition info - pInfo->firstSector = atoi(argv[curArg++]); - pInfo->lastSector = atoi(argv[curArg++]); - pInfo->clusterSize = atoi(argv[curArg++]); - pInfo->offMFT = atoi(argv[curArg++]); - - if(pInfo->firstSector == 0 || - pInfo->lastSector == 0 || - pInfo->clusterSize == 0 || - pInfo->offMFT == 0) - { - printf("scrounge: invalid option(s).\n"); - return 2; - } - -// pInfo->clusterSize = 8; -// pInfo->firstSector = 20482938/*128*/; -// pInfo->lastSector = 80019765/*15358077*/; -// pInfo->offMFT = 32; - - if(curArg <= argc) - SetCurrentDirectoryA(argv[curArg++]); - - WCHAR driveName[MAX_PATH]; - - wsprintf(driveName, kDriveName, driveNum); - - HANDLE hDrive = CreateFile(driveName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if(hDrive == INVALID_HANDLE_VALUE) - { - printf("scrounge: Can't open drive %d.\n", driveNum); - return 2; - } - - if(!ScroungeMFTRecords(pInfo, hDrive)) - { - printf("scrounge: "); - PrintLastError(); - fputc('\n', stdout); - return 2; - } - - FreePartitionInfo(pInfo); - - return 0; -}
\ No newline at end of file |