summaryrefslogtreecommitdiff
path: root/src/scrounge.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/scrounge.c')
-rw-r--r--src/scrounge.c416
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");