diff options
Diffstat (limited to 'src/scrounge.c')
-rw-r--r-- | src/scrounge.c | 416 |
1 files changed, 257 insertions, 159 deletions
diff --git a/src/scrounge.c b/src/scrounge.c index 5627d36..45cd1bf 100644 --- a/src/scrounge.c +++ b/src/scrounge.c @@ -23,6 +23,8 @@ #include "ntfsx.h" #include "locks.h" +#define PROCESS_MFT_FLAG_SUB 1 << 1 + typedef struct _filebasics { fchar_t filename[MAX_PATH + 1]; @@ -38,6 +40,7 @@ void processRecordFileBasics(partitioninfo* pi, ntfsx_record* record, filebasics { /* Data Attribute */ ntfsx_attribute* attr = NULL; + ntfsx_attrib_enum* attrenum = NULL; { byte* resident = NULL; @@ -54,82 +57,84 @@ void processRecordFileBasics(partitioninfo* pi, ntfsx_record* record, filebasics basics->parent = kInvalidSector; /* Now get the name and info... */ - attr = ntfsx_record_findattribute(record, kNTFS_FILENAME, pi->device); - if(!attr) goto cleanup; + attrenum = ntfsx_attrib_enum_alloc(kNTFS_FILENAME, true); nameSpace = kNTFS_NameSpacePOSIX; memset(basics->filename, 0, sizeof(basics->filename)); - do + while((attr = ntfsx_attrib_enum_all(attrenum, record)) != NULL) { /* TODO ASSUMPTION: File name is always resident */ - ASSERT(!ntfsx_attribute_header(attr)->bNonResident); - - - /* Get out all the info we need */ - filename = (ntfs_attribfilename*)ntfsx_attribute_getresidentdata(attr); - ASSERT(filename); + if(!ntfsx_attribute_header(attr)->bNonResident) + { + /* 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, filename->nameSpace)) - { - /* Dates */ - basics->created = filename->timeCreated; - basics->modified = filename->timeModified; - basics->accessed = filename->timeRead; - - /* File Name */ - name = (ntfs_char*)(((byte*)filename) + sizeof(ntfs_attribfilename)); - len = filename->cFileName; - if(len > MAX_PATH) - len = MAX_PATH; + /* + * There can be multiple filenames with different + * namespaces so choose the best one + */ + if(ntfs_isbetternamespace(nameSpace, filename->nameSpace)) + { + /* Dates */ + basics->created = filename->timeCreated; + basics->modified = filename->timeModified; + basics->accessed = filename->timeRead; + + /* File Name */ + name = (ntfs_char*)(((byte*)filename) + sizeof(ntfs_attribfilename)); + len = filename->cFileName; + if(len > MAX_PATH) + len = MAX_PATH; #ifdef FC_WIDE - wcsncpy(basics->filename, name, len); + wcsncpy(basics->filename, name, len); #else - temp = unicode_transcode16to8(name, len); - if(!temp) - errx(1, "out of memory"); + temp = unicode_transcode16to8(name, len); - len = strlen(temp); - if(len > MAX_PATH) - len = MAX_PATH; + len = strlen(temp); + if(len > MAX_PATH) + len = MAX_PATH; - strncpy(basics->filename, temp, len); + strncpy(basics->filename, temp, len); #endif - basics->filename[len] = 0; + basics->filename[len] = 0; + + /* Attributes */ + basics->flags = filename->flags; - /* Attributes */ - basics->flags = filename->flags; + /* Parent Directory */ + basics->parent = filename->refParent & kNTFS_RefMask; - /* Parent Directory */ - basics->parent = filename->refParent & kNTFS_RefMask; + /* Namespace */ + nameSpace = filename->nameSpace; + } + } - /* Namespace */ - nameSpace = filename->nameSpace; - } + ntfsx_attribute_free(attr); + attr = NULL; } - while(ntfsx_attribute_next(attr, kNTFS_FILENAME)); } -cleanup: if(attr) ntfsx_attribute_free(attr); + + if(attrenum) + ntfsx_attrib_enum_free(attrenum); } -void processMFTRecord(partitioninfo* pi, uint64 sector, int level) +void processMFTRecord(partitioninfo* pi, uint64 sector, uint32 flags) { ntfsx_record* record = NULL; ntfsx_attribute* attribdata = NULL; + ntfsx_attrib_enum* attrenum = NULL; ntfsx_datarun* datarun = NULL; - int outfile = -1; + int ofile = -1; ntfsx_cluster cluster; memset(&cluster, 0, sizeof(cluster)); @@ -142,6 +147,7 @@ void processMFTRecord(partitioninfo* pi, uint64 sector, int level) uint16 rename = 0; uint64 fileSize; uint32 i; + bool haddata = false; uint32 num; fchar_t filename2[MAX_PATH + 1]; ntfs_attribheader* attrhead; @@ -150,8 +156,6 @@ void processMFTRecord(partitioninfo* pi, uint64 sector, int level) 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)) @@ -166,15 +170,20 @@ void processMFTRecord(partitioninfo* pi, uint64 sector, int level) /* Try and get a file name out of the header */ processRecordFileBasics(pi, record, &basics); + /* Without files we skip */ if(basics.filename[0] == 0) - { - RETWARNX("invalid mft record. in use, but no filename"); - } + RETURN; /* If it's the root folder then return */ if(!fcscmp(basics.filename, FC_DOT)) RETURN; + /* System, Hidden files that begin with $ are skipped */ + if(basics.flags & kNTFS_FileSystem && + basics.flags & kNTFS_FileHidden && + basics.filename[0] == kNTFS_SysPrefix) + RETURN; + /* Process parent folders if available */ if(basics.parent != kInvalidSector) { @@ -186,11 +195,12 @@ void processMFTRecord(partitioninfo* pi, uint64 sector, int level) if(parentSector == kInvalidSector) warnx("invalid parent directory for file: " FC_PRINTF, basics.filename); else - processMFTRecord(pi, parentSector, level + 1); + processMFTRecord(pi, parentSector, flags | PROCESS_MFT_FLAG_SUB); } } - printf(level == 0 ? "\\" FC_PRINTF "\n" : "\\" FC_PRINTF, basics.filename); + printf(flags & PROCESS_MFT_FLAG_SUB ? + "\\" FC_PRINTF : "\\" FC_PRINTF "\n", basics.filename); /* Directory handling: */ if(header->flags & kNTFS_RecFlagDir) @@ -198,154 +208,241 @@ void processMFTRecord(partitioninfo* pi, uint64 sector, int level) /* Try to change to the directory */ if(fc_chdir(basics.filename) == -1) { - if(fc_mkdir(basics.filename) == -1) - { - warnx("couldn't create directory '" FC_PRINTF "' putting files in parent directory", basics.filename); - } - else +#ifdef _DEBUG + if(!g_verifyMode) +#endif { - setFileAttributes(basics.filename, basics.flags); - fc_chdir(basics.filename); + if(fc_mkdir(basics.filename) == -1) + { + warn("couldn't create directory '" FC_PRINTF "' putting files in parent directory", basics.filename); + } + else + { + setFileAttributes(basics.filename, basics.flags); + fc_chdir(basics.filename); + } } } RETURN; } - - /* Normal file handling: */ - outfile = fc_open(basics.filename, O_BINARY | O_CREAT | O_EXCL | O_WRONLY); - - fcsncpy(filename2, basics.filename, MAX_PATH); - filename2[MAX_PATH] = 0; - - while(outfile == -1 && errno == EEXIST && rename < 0x1000) +#ifdef _DEBUG + /* If in verify mode */ + if(g_verifyMode) { - if(fcslen(basics.filename) + 7 >= MAX_PATH) + ofile = fc_open(basics.filename, O_BINARY | O_RDONLY); + + if(ofile == -1) { - warnx("file name too long on duplicate file: " FC_PRINTF, basics.filename); + warn("couldn't open verify file: " FC_PRINTF, basics.filename); goto cleanup; } - - fcscpy(basics.filename, filename2); - fcscat(basics.filename, FC_DOT); - - itofc(rename, basics.filename + fcslen(basics.filename), 10); - rename++; - - outfile = fc_open(basics.filename, O_BINARY | O_CREAT | O_EXCL | O_WRONLY); } - if(outfile == -1) + /* Normal file handling: */ + else +#endif { - warnx("couldn't open output file: " FC_PRINTF, basics.filename); - goto cleanup; - } + ofile = fc_open(basics.filename, O_BINARY | O_CREAT | O_EXCL | O_WRONLY); + + fcsncpy(filename2, basics.filename, MAX_PATH); + filename2[MAX_PATH] = 0; + while(ofile == -1 && errno == EEXIST && rename < 0x1000) + { + if(fcslen(basics.filename) + 7 >= MAX_PATH) + { + warnx("file name too long on duplicate file: " FC_PRINTF, basics.filename); + goto cleanup; + } - attribdata = ntfsx_record_findattribute(record, kNTFS_DATA, pi->device); - if(!attribdata) - RETWARNX("invalid mft record. no data attribute found"); - - attrhead = ntfsx_attribute_header(attribdata); + fcscpy(basics.filename, filename2); + fcscat(basics.filename, FC_DOT); - /* For resident data just write it out */ - if(!attrhead->bNonResident) - { - uint32 length = ntfsx_attribute_getresidentsize(attribdata); - byte* data = ntfsx_attribute_getresidentdata(attribdata); + itofc(rename, basics.filename + fcslen(basics.filename), 10); + rename++; - if(!data) - RETWARNX("invalid mft record. resident data screwed up"); + ofile = fc_open(basics.filename, O_BINARY | O_CREAT | O_EXCL | O_WRONLY); + } - if(write(outfile, data, length) != (int32)length) - RETWARN("couldn't write data to output file"); + if(ofile == -1) + { + warn("couldn't open output file: " FC_PRINTF, basics.filename); + goto cleanup; + } } - /* For non resident data it's a bit more involved */ - else + attrenum = ntfsx_attrib_enum_alloc(kNTFS_DATA, true); + + while((attribdata = ntfsx_attrib_enum_all(attrenum, record)) != NULL) { - datarun = ntfsx_attribute_getdatarun(attribdata); - if(!datarun) - errx(1, "out of memory"); + attrhead = ntfsx_attribute_header(attribdata); - nonres = (ntfs_attribnonresident*)attrhead; - fileSize = nonres->cbAttribData; + /* + * We don't do compressed/encrypted files. Eventually + * we may be able to write in some support :) + */ + if(attrhead->flags & kNTFS_AttrCompressed) + RETWARNX("compressed file. skipping."); - /* Allocate a cluster for reading and writing */ - if(!ntfsx_cluster_reserve(&cluster, pi)) - errx(1, "out of memory"); + if(attrhead->flags & kNTFS_AttrEncrypted) + RETWARNX("encrypted file. skipping."); - if(ntfsx_datarun_first(datarun)) + /* On the first round figure out the file size */ + if(!haddata) { - do + if(attrhead->bNonResident) { - /* 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; - } + nonres = (ntfs_attribnonresident*)attrhead; + fileSize = nonres->cbAttribData; + } + else + { + fileSize = ntfsx_attribute_getresidentsize(attribdata); + } + } - /* Sparse clusters we just write zeros */ - if(datarun->sparse) - { - memset(cluster.data, 0, cluster.size); + haddata = true; - for(i = 0; i < datarun->length && fileSize; i++) - { - num = cluster.size; + /* 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"); - if(fileSize < 0xFFFFFFFF && num > (uint32)fileSize) - num = (uint32)fileSize; +#ifdef _DEBUG + if(g_verifyMode) + { + if(compareFileData(ofile, data, length) != 0) + RETWARNX("verify failed. read file data wrong."); + } + else +#endif + if(write(ofile, data, length) != (int32)length) + RETWARN("couldn't write data to output file"); - if(write(outfile, cluster.data, num) != (int32)num) - err(1, "couldn't write to output file: " FC_PRINTF, basics.filename); + fileSize -= length; + } - fileSize -= num; - } - } + /* For non resident data it's a bit more involved */ + else + { + datarun = ntfsx_attribute_getdatarun(attribdata); + nonres = (ntfs_attribnonresident*)attrhead; - /* Handle not sparse clusters */ - else + /* Allocate a cluster for reading and writing */ + ntfsx_cluster_reserve(&cluster, pi); + + if(ntfsx_datarun_first(datarun)) + { + do { - if(pi->locks) + /* + * In some cases NTFS sloppily leaves many extra + * data runs mapped for a file, so just cut out + * when that's the case + */ + if(fileSize == 0) + break; + + /* Sparse clusters we just write zeros */ + if(datarun->sparse) { - /* 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)); + 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; + +#ifdef _DEBUG + if(g_verifyMode) + { + if(compareFileData(ofile, cluster.data, num) != 0) + RETWARNX("verify failed. read file data wrong."); + } + else +#endif + if(write(ofile, cluster.data, num) != (int32)num) + err(1, "couldn't write to output file: " FC_PRINTF, basics.filename); + + fileSize -= num; + } } - for(i = 0; i < datarun->length && fileSize; i++) + /* Handle not sparse clusters */ + else { - 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: " FC_PRINTF, basics.filename); + 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"); + +#ifdef _DEBUG + if(g_verifyMode) + { + if(compareFileData(ofile, cluster.data, num) != 0) + RETWARNX("verify failed. read file data wrong."); + } + else +#endif + if(write(ofile, cluster.data, num) != (int32)num) + err(1, "couldn't write to output file: " FC_PRINTF, basics.filename); - fileSize -= num; + fileSize -= num; + } } } + while(ntfsx_datarun_next(datarun)); } - while(ntfsx_datarun_next(datarun)); + + ntfsx_datarun_free(datarun); + datarun = NULL; } + + ntfsx_attribute_free(attribdata); + attribdata = NULL; + + /* Cut out when there's extra clusters allocated */ + if(fileSize == 0) + break; } + if(!haddata) + RETWARNX("invalid mft record. no data attribute found"); + if(fileSize != 0) warnx("invalid mft record. couldn't find all data for file"); - close(outfile); - outfile = -1; + close(ofile); + ofile = -1; - setFileTime(basics.filename, &(basics.created), - &(basics.accessed), &(basics.modified)); +#ifdef _DEBUG + if(!g_verifyMode) +#endif + { + setFileTime(basics.filename, &(basics.created), + &(basics.accessed), &(basics.modified)); - setFileAttributes(basics.filename, basics.flags); + setFileAttributes(basics.filename, basics.flags); + } } cleanup: @@ -360,8 +457,11 @@ cleanup: if(datarun) ntfsx_datarun_free(datarun); - if(outfile != -1) - close(outfile); + if(attrenum) + ntfsx_attrib_enum_free(attrenum); + + if(ofile != -1) + close(ofile); } @@ -379,8 +479,6 @@ void scroungeMFT(partitioninfo* pi, ntfsx_mftmap* map) errx(2, "invalid mft. past end of partition"); record = ntfsx_record_alloc(pi); - if(!record) - errx(1, "out of memory"); /* Read the MFT record */ if(!ntfsx_record_read(record, sector, pi->device)) @@ -392,6 +490,11 @@ void scroungeMFT(partitioninfo* pi, ntfsx_mftmap* map) if(!(header->flags & kNTFS_RecFlagUse)) errx(2, "invalid mft. marked as not in use"); + /* Load the MFT data runs */ + + if(!ntfsx_mftmap_load(map, record, pi->device)) + err(1, "error reading in mft"); + /* Try and get a file name out of the header */ processRecordFileBasics(pi, record, &basics); @@ -401,11 +504,6 @@ void scroungeMFT(partitioninfo* pi, ntfsx_mftmap* map) fprintf(stderr, "[Processing MFT...]\n"); - /* Load the MFT data runs */ - - if(!ntfsx_mftmap_load(map, record, pi->device)) - err(1, "error reading in mft"); - if(ntfsx_mftmap_length(map) == 0) errx(1, "invalid mft. no records in mft"); |