summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compat.c16
-rw-r--r--src/compat.h6
-rw-r--r--src/main.c23
-rw-r--r--src/misc.c35
-rw-r--r--src/ntfs.c4
-rw-r--r--src/ntfs.h4
-rw-r--r--src/ntfsx.c480
-rw-r--r--src/ntfsx.h21
-rw-r--r--src/scrounge.c416
-rw-r--r--src/scrounge.h6
-rw-r--r--src/unicode.c6
11 files changed, 671 insertions, 346 deletions
diff --git a/src/compat.c b/src/compat.c
index 013675f..fa171f1 100644
--- a/src/compat.c
+++ b/src/compat.c
@@ -288,11 +288,21 @@ void vwarnx(const char *fmt, va_list ap)
void* reallocf(void* ptr, size_t size)
{
void* ret = realloc(ptr, size);
+ if(!ret)
+ errx(1, "out of memory");
+ return ret;
+}
+
+#endif
- if(!ret && size)
- free(ptr);
+#ifndef HAVE_MALLOCF
- return ret;
+void* mallocf(size_t size)
+{
+ void* ret = malloc(size);
+ if(!ret)
+ errx(1, "out of memory");
+ return ret;
}
#endif
diff --git a/src/compat.h b/src/compat.h
index c2b4128..65a0c8e 100644
--- a/src/compat.h
+++ b/src/compat.h
@@ -192,6 +192,10 @@ void vwarnx(const char *fmt, va_list ap);
void* reallocf(void* p, size_t sz);
#endif
+#ifndef HAVE_MALLOCF
+void* mallocf(size_t sz);
+#endif
+
/* Some number conversion stuff */
#include <wchar.h>
@@ -223,7 +227,7 @@ void* reallocf(void* p, size_t sz);
#ifdef _WIN32
- #ifdef SIZEOF_WHCHAR_T != 2
+ #if SIZEOF_WHCHAR_T != 2
#error Incompatible size of wchar_t
#endif
diff --git a/src/main.c b/src/main.c
index ef5be8e..ca8aa79 100644
--- a/src/main.c
+++ b/src/main.c
@@ -70,6 +70,10 @@ usage: scrounge [-m mftoffset] [-c clustersize] [-o outdir] disk start end \n\
/* Forward decls */
void usage();
+#ifdef _DEBUG
+bool g_verifyMode = false;
+#endif
+
int main(int argc, char* argv[])
{
int ch = 0;
@@ -88,9 +92,9 @@ int main(int argc, char* argv[])
pi.cluster = 8;
#ifdef _WIN32
- while((ch = getopt(argc, argv, "c:d:hlm:o:s")) != -1)
+ while((ch = getopt(argc, argv, "c:d:hlm:o:sv")) != -1)
#else
- while((ch = getopt(argc, argv, "c:hlm:o:s")) != -1)
+ while((ch = getopt(argc, argv, "c:hlm:o:sv")) != -1)
#endif
{
switch(ch)
@@ -125,12 +129,6 @@ int main(int argc, char* argv[])
break;
#endif
- /* help mode */
- case '?':
- case 'h':
- usage();
- break;
-
/* list mode */
case 'l':
{
@@ -171,6 +169,15 @@ int main(int argc, char* argv[])
}
break;
+#ifdef _DEBUG
+ case 'v':
+ g_verifyMode = true;
+ break;
+#endif
+
+ /* help mode */
+ case '?':
+ case 'h':
default:
usage();
break;
diff --git a/src/misc.c b/src/misc.c
index 0778467..686e460 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -38,9 +38,6 @@ void addLocationLock(drivelocks* locks, uint64 beg, uint64 end)
{
locks->_count += 0x400;
locks->_locks = (struct drivelock*)reallocf(locks->_locks, sizeof(struct drivelock) * locks->_count);
-
- if(!locks->_locks)
- errx(1, "out of memory");
}
/* TODO: Implement a more efficient method here! */
@@ -110,7 +107,7 @@ const size_t kRefSig = 0x1F2F3F4F;
void* _refalloc_dbg(size_t sz)
{
/* Allocate extra counter value before memory */
- size_t* mem = (size_t*)malloc(sz * sizeof(size_t) * 2);
+ size_t* mem = (size_t*)mallocf(sz * sizeof(size_t) * 2);
if(mem)
{
@@ -126,7 +123,7 @@ void* _refalloc_dbg(size_t sz)
void* _refalloc(size_t sz)
{
/* Allocate extra counter value before memory */
- size_t* mem = (size_t*)malloc(sz * sizeof(size_t) * 1);
+ size_t* mem = (size_t*)mallocf(sz * sizeof(size_t) * 1);
if(mem)
{
@@ -188,4 +185,32 @@ void _refrelease(void* buf)
}
}
+#define COMPARE_BLOCK_SIZE 4096
+int compareFileData(int f, void* data, size_t length)
+{
+ unsigned char buf[COMPARE_BLOCK_SIZE];
+ unsigned char* d = (unsigned char*)data;
+ int num, r;
+
+ while(length > 0)
+ {
+ num = min(COMPARE_BLOCK_SIZE, length);
+ r = read(f, buf, num);
+
+ if(r < 0)
+ err(1, "error reading comparison file");
+
+ if(r < num)
+ return -1;
+
+ r = memcmp(d, buf, num);
+ if(r != 0)
+ return r;
+
+ d += num;
+ length -= num;
+ }
+
+ return 0;
+}
diff --git a/src/ntfs.c b/src/ntfs.c
index 88bab7e..3e2b657 100644
--- a/src/ntfs.c
+++ b/src/ntfs.c
@@ -46,7 +46,7 @@ ntfs_attribheader* ntfs_searchattribute(byte* location, uint32 attrType, byte* e
return NULL;
}
-byte* ntfs_getattributelist(ntfs_recordheader* record)
+byte* ntfs_getattributeheaders(ntfs_recordheader* record)
{
byte* location = (byte*)record;
ASSERT(record->offAttrs != 0);
@@ -57,7 +57,7 @@ byte* ntfs_getattributelist(ntfs_recordheader* record)
ntfs_attribheader* ntfs_findattribute(ntfs_recordheader* record, uint32 attrType, byte* end)
{
- byte* location = ntfs_getattributelist(record);
+ byte* location = ntfs_getattributeheaders(record);
return ntfs_searchattribute(location, attrType, end, false);
}
diff --git a/src/ntfs.h b/src/ntfs.h
index ae7ea6e..62a92cb 100644
--- a/src/ntfs.h
+++ b/src/ntfs.h
@@ -158,8 +158,10 @@ ntfs_attribnonresident;
#ifdef FC_WIDE
#define kNTFS_MFTName L"$MFT"
+#define kNTFS_SysPrefix L'$'
#else
#define kNTFS_MFTName "$MFT"
+#define kNTFS_SysPrefix '$'
#endif
typedef struct _ntfs_attribfilename
@@ -196,7 +198,7 @@ ntfs_attriblistrecord;
ntfs_attribheader* ntfs_findattribute(ntfs_recordheader* record, uint32 attrType, byte* end);
ntfs_attribheader* ntfs_nextattribute(ntfs_attribheader* attrib, uint32 attrType, byte* end);
-byte* ntfs_getattributelist(ntfs_recordheader* record);
+byte* ntfs_getattributeheaders(ntfs_recordheader* record);
byte* ntfs_getattributedata(ntfs_attribresident* attrib, byte* end);
bool ntfs_isbetternamespace(byte n1, byte n2);
diff --git a/src/ntfsx.c b/src/ntfsx.c
index fd60f50..60ddb66 100644
--- a/src/ntfsx.c
+++ b/src/ntfsx.c
@@ -24,18 +24,16 @@
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;
- }
+ ntfsx_datarun* dr = (ntfsx_datarun*)mallocf(sizeof(ntfsx_datarun));
+
+ ASSERT(datarun);
+ dr->_mem = (byte*)refadd(mem);
+ dr->_datarun = datarun;
+ dr->_curpos = NULL;
+
+ dr->cluster = 0;
+ dr->length = 0;
+ dr->sparse = false;
return dr;
}
@@ -116,20 +114,13 @@ bool ntfsx_datarun_next(ntfsx_datarun* dr)
-bool ntfsx_cluster_reserve(ntfsx_cluster* clus, partitioninfo* info)
+void ntfsx_cluster_reserve(ntfsx_cluster* clus, partitioninfo* info)
{
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 ntfsx_cluster_read(ntfsx_cluster* clus, partitioninfo* info, uint64 begSector, int dd)
@@ -138,10 +129,7 @@ bool ntfsx_cluster_read(ntfsx_cluster* clus, partitioninfo* info, uint64 begSect
size_t sz;
if(!clus->data)
- {
- if(!ntfsx_cluster_reserve(clus, info))
- return false;
- }
+ ntfsx_cluster_reserve(clus, info);
pos = SECTOR_TO_BYTES(begSector);
if(lseek64(dd, pos, SEEK_SET) == -1)
@@ -174,14 +162,10 @@ void ntfsx_cluster_release(ntfsx_cluster* clus)
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;
- }
-
+ ntfsx_attribute* attr = (ntfsx_attribute*)mallocf(sizeof(ntfsx_attribute));
+ attr->_header = header;
+ attr->_mem = (byte*)refadd(clus->data);
+ attr->_length = clus->size;
return attr;
}
@@ -236,15 +220,226 @@ bool ntfsx_attribute_next(ntfsx_attribute* attr, uint32 attrType)
}
-ntfsx_record* ntfsx_record_alloc(partitioninfo* info)
+
+#define ATTR_ENUM_LISTPRI 1 << 1
+#define ATTR_ENUM_DONEINLINE 1 << 2
+#define ATTR_ENUM_DONELIST 1 << 3
+#define ATTR_ENUM_FOUNDLIST 1 << 4
+
+ntfsx_attrib_enum* ntfsx_attrib_enum_alloc(uint32 type, bool normal)
{
- ntfsx_record* rec = (ntfsx_record*)malloc(sizeof(ntfsx_record));
- if(rec)
+ ntfsx_attrib_enum* attrenum = (ntfsx_attrib_enum*)mallocf(sizeof(ntfsx_attrib_enum));
+ attrenum->type = type;
+ attrenum->_attrhead = NULL;
+ attrenum->_listrec = NULL;
+ attrenum->_flags = normal ? ATTR_ENUM_LISTPRI : 0;
+ return attrenum;
+}
+
+ntfsx_attribute* ntfsx_attrib_enum_inline(ntfsx_attrib_enum* attrenum, ntfsx_record* record)
+{
+ ntfsx_attribute* attr;
+ ntfsx_cluster* cluster;
+ ntfs_recordheader* rechead;
+
+ /* If we're done */
+ if(attrenum->_flags & ATTR_ENUM_DONEINLINE)
+ return NULL;
+
+ cluster = ntfsx_record_cluster(record);
+ rechead = ntfsx_record_header(record);
+
+ /* If this is the first time */
+ if(!attrenum->_attrhead && !attrenum->_listrec)
{
- rec->info = info;
- memset(&(rec->_clus), 0, sizeof(ntfsx_cluster));
+ attrenum->_attrhead = ntfs_findattribute(rechead, attrenum->type,
+ cluster->data + cluster->size);
+
+ if(attrenum->_attrhead)
+ {
+ attr = ntfsx_attribute_alloc(cluster, attrenum->_attrhead);
+ return attr;
+ }
+
+ /* Otherwise we fall through to the attr list stuff below */
}
+ /* Look for another attribute in the record */
+ if(attrenum->_attrhead && attrenum->_attrhead->type == attrenum->type)
+ {
+ attrenum->_attrhead = ntfs_nextattribute(attrenum->_attrhead, attrenum->type,
+ cluster->data + cluster->size);
+
+ if(attrenum->_attrhead)
+ {
+ attr = ntfsx_attribute_alloc(cluster, attrenum->_attrhead);
+ return attr;
+ }
+
+ /* Otherwise we're done */
+ }
+
+ attrenum->_flags |= ATTR_ENUM_DONEINLINE;
+ return NULL;
+}
+
+ntfsx_attribute* ntfsx_attrib_enum_list(ntfsx_attrib_enum* attrenum, ntfsx_record* record)
+{
+ ntfsx_cluster* cluster;
+ ntfs_recordheader* rechead;
+ ntfs_attribresident* resident;
+ ntfs_attribheader* attrhead;
+ ntfsx_attribute* attr;
+ uint64 mftRecord;
+ ntfsx_record* r2;
+ ntfsx_cluster* c2;
+
+ ASSERT(record && attrenum);
+
+ /* If we're done */
+ if(attrenum->_flags & ATTR_ENUM_DONELIST)
+ return NULL;
+
+ cluster = ntfsx_record_cluster(record);
+ rechead = ntfsx_record_header(record);
+
+ /* Okay first check for attribute lists */
+ if(!attrenum->_listrec && !attrenum->_attrhead)
+ {
+ attrenum->_attrhead = ntfs_findattribute(rechead, kNTFS_ATTRIBUTE_LIST,
+ cluster->data + cluster->size);
+
+ /* If no attribute list, end of story */
+ if(!attrenum->_attrhead)
+ {
+ attrenum->_flags |= ATTR_ENUM_DONELIST;
+ return NULL;
+ }
+
+ /* We don't support non-resident attribute lists (which are stupid!) */
+ if(attrenum->_attrhead->bNonResident)
+ {
+ warnx("brain dead, incredibly fragmented file data. skipping");
+ attrenum->_flags |= ATTR_ENUM_DONELIST;
+ return NULL;
+ }
+ }
+
+ /* This has to be set by now */
+ ASSERT(record->info->mftmap);
+ ASSERT(attrenum->_attrhead);
+ ASSERT(attrenum->_attrhead->type == kNTFS_ATTRIBUTE_LIST);
+
+ resident = (ntfs_attribresident*)attrenum->_attrhead;
+
+ for(;;)
+ {
+ if(attrenum->_listrec) /* progress to next record */
+ attrenum->_listrec = (ntfs_attriblistrecord*)(((byte*)attrenum->_listrec) + attrenum->_listrec->cbRecord);
+
+ else /* get first record */
+ attrenum->_listrec = (ntfs_attriblistrecord*)((byte*)resident + resident->offAttribData);
+
+ if(((byte*)attrenum->_listrec) >= ((byte*)attrenum->_attrhead) + attrenum->_attrhead->cbAttribute)
+ {
+ attrenum->_listrec = NULL;
+ attrenum->_flags |= ATTR_ENUM_DONELIST;
+ return NULL;
+ }
+
+ if(attrenum->_listrec->type == attrenum->type)
+ {
+ attr = NULL;
+ r2 = NULL;
+
+ /* Read in appropriate cluster */
+ mftRecord = ntfsx_mftmap_sectorforindex(record->info->mftmap, attrenum->_listrec->refAttrib & kNTFS_RefMask);
+ if(mftRecord == kInvalidSector)
+ {
+ warnx("invalid sector in mft map. screwed up file. skipping data");
+ }
+ else
+ {
+ r2 = ntfsx_record_alloc(record->info);
+
+ if(ntfsx_record_read(r2, mftRecord, record->info->device))
+ {
+ rechead = ntfsx_record_header(r2);
+ c2 = ntfsx_record_cluster(r2);
+ attrhead = ntfs_findattribute(rechead, attrenum->type,
+ c2->data + c2->size);
+
+ if(attrhead)
+ attr = ntfsx_attribute_alloc(c2, attrhead);
+ }
+ }
+
+ if(r2)
+ ntfsx_record_free(r2);
+
+ if(attr)
+ return attr;
+ }
+ }
+
+ /* Not reached */
+ ASSERT(false);
+}
+
+ntfsx_attribute* ntfsx_attrib_enum_all(ntfsx_attrib_enum* attrenum, ntfsx_record* record)
+{
+ ntfsx_attribute* attr = NULL;
+
+ ASSERT(record && attrenum);
+
+ /*
+ * When in this mode list attributes completely override
+ * any inline attributes. This is the normal mode of
+ * operation
+ */
+ if(attrenum->_flags & ATTR_ENUM_LISTPRI)
+ {
+ if(!(attrenum->_flags & ATTR_ENUM_DONELIST))
+ {
+ attr = ntfsx_attrib_enum_list(attrenum, record);
+
+ if(attr)
+ attrenum->_flags |= ATTR_ENUM_FOUNDLIST;
+ }
+
+ if(!attr && !(attrenum->_flags & ATTR_ENUM_FOUNDLIST) &&
+ !(attrenum->_flags & ATTR_ENUM_DONEINLINE))
+ attr = ntfsx_attrib_enum_inline(attrenum, record);
+ }
+
+ /*
+ * The other mode of operation is to find everything
+ * inline first and then stuff in the lists.
+ */
+ else
+ {
+ if(!(attrenum->_flags & ATTR_ENUM_DONEINLINE))
+ attr = ntfsx_attrib_enum_inline(attrenum, record);
+
+ if(!attr && !(attrenum->_flags & ATTR_ENUM_DONELIST))
+ attr = ntfsx_attrib_enum_list(attrenum, record);
+ }
+
+ return attr;
+}
+
+void ntfsx_attrib_enum_free(ntfsx_attrib_enum* attrenum)
+{
+ free(attrenum);
+}
+
+
+
+ntfsx_record* ntfsx_record_alloc(partitioninfo* info)
+{
+ ntfsx_record* rec = (ntfsx_record*)mallocf(sizeof(ntfsx_record));
+ rec->info = info;
+ memset(&(rec->_clus), 0, sizeof(ntfsx_cluster));
return rec;
}
@@ -274,6 +469,11 @@ bool ntfsx_record_read(ntfsx_record* record, uint64 begSector, int dd)
return true;
}
+ntfsx_cluster* ntfsx_record_cluster(ntfsx_record* record)
+{
+ return &(record->_clus);
+}
+
ntfs_recordheader* ntfsx_record_header(ntfsx_record* record)
{
return (ntfs_recordheader*)(record->_clus.data);
@@ -281,62 +481,13 @@ ntfs_recordheader* ntfsx_record_header(ntfsx_record* record)
ntfsx_attribute* ntfsx_record_findattribute(ntfsx_record* record, uint32 attrType, int dd)
{
+ ntfsx_attrib_enum* attrenum = NULL;
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));
-
- /* 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);
-
- /* 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 & kNTFS_RefMask);
-
- r2 = ntfsx_record_alloc(record->info);
- if(!r2)
- return NULL;
-
- if(ntfsx_record_read(r2, mftRecord, dd))
- attr = ntfsx_record_findattribute(r2, attrType, dd);
-
- ntfsx_record_free(r2);
-
- if(attr)
- break;
- }
-
- atlr = (ntfs_attriblistrecord*)((byte*)atlr + atlr->cbRecord);
- }
- }
- }
-
- return attr;
+ attrenum = ntfsx_attrib_enum_alloc(attrType, true);
+ attr = ntfsx_attrib_enum_all(attrenum, record);
+ ntfsx_attrib_enum_free(attrenum);
+ return attr;
}
@@ -372,8 +523,6 @@ static void mftmap_expand(ntfsx_mftmap* map, uint32* allocated)
(*allocated) += 16;
map->_blocks = (struct _ntfsx_mftmap_block*)reallocf(map->_blocks,
(*allocated) * sizeof(struct _ntfsx_mftmap_block));
- if(!(map->_blocks))
- errx(1, "out of memory");
}
}
@@ -382,6 +531,7 @@ bool ntfsx_mftmap_load(ntfsx_mftmap* map, ntfsx_record* record, int dd)
bool ret = true;
ntfsx_attribute* attribdata = NULL; /* Data Attribute */
ntfsx_datarun* datarun = NULL; /* Data runs for nonresident data */
+ ntfsx_attrib_enum* attrenum = NULL;
{
ntfs_attribheader* header;
@@ -390,24 +540,8 @@ bool ntfsx_mftmap_load(ntfsx_mftmap* map, ntfsx_record* record, int dd)
uint64 firstSector;
uint32 allocated;
uint64 total;
+ bool hasdata = false;
- /* TODO: Check here whether MFT has already been loaded */
-
- /* Get the MFT's data */
- attribdata = ntfsx_record_findattribute(record, kNTFS_DATA, dd);
- if(!attribdata)
- RETWARNBX("invalid mft. no data attribute");
-
- header = ntfsx_attribute_header(attribdata);
- if(!header->bNonResident)
- RETWARNBX("invalid mft. data attribute non-resident");
-
- datarun = ntfsx_attribute_getdatarun(attribdata);
- if(!datarun)
- RETWARNBX("invalid mft. no data runs");
-
- nonres = (ntfs_attribnonresident*)header;
-
if(map->_blocks)
{
free(map->_blocks);
@@ -416,65 +550,87 @@ bool ntfsx_mftmap_load(ntfsx_mftmap* map, ntfsx_record* record, int dd)
map->_count = 0;
allocated = 0;
- mftmap_expand(map, &allocated);
-
- total = nonres->cbAttribData / kSectorSize;
-
- /* Now loop through the data run */
- if(ntfsx_datarun_first(datarun))
- {
- do
- {
- if(datarun->sparse)
- RETWARNBX("invalid mft. sparse data runs");
-
- mftmap_expand(map, &allocated);
-
- ASSERT(map->info->cluster != 0);
-
- length = datarun->length * ((map->info->cluster * kSectorSize) / kNTFS_RecordLen);
- if(length == 0)
- continue;
-
- firstSector = (datarun->cluster * map->info->cluster) + map->info->first;
- if(firstSector >= map->info->end)
- continue;
-
- map->_blocks[map->_count].length = length;
- map->_blocks[map->_count].firstSector = firstSector;
- map->_count++;
-
- /*
- * In some cases the data runs for the MFT don't specify the entire
- * MFT file, and so we track the remainder and tack it onto
- * the last block.
- */
- total -= length;
- }
- while(ntfsx_datarun_next(datarun));
-
- }
+ total = 0;
+
+ attrenum = ntfsx_attrib_enum_alloc(kNTFS_DATA, false);
- if(total > 0)
+ while((attribdata = ntfsx_attrib_enum_all(attrenum, record)) != NULL)
{
- if(map->_count == 0)
+ header = ntfsx_attribute_header(attribdata);
+ if(!header->bNonResident)
{
- /*
- * When no data runs were found we start off right
- * at the MFT and go for the specified length.
- */
- ASSERT(allocated > 0);
- map->_blocks[0].length = total;
- map->_blocks[0].firstSector = map->info->mft + map->info->first;
+ warnx("invalid mft. data attribute non-resident");
}
-
else
{
- /* Add the remainder of the missing blocks here */
- map->_blocks[map->_count - 1].length += total;
- }
+ datarun = ntfsx_attribute_getdatarun(attribdata);
+ if(!datarun)
+ {
+ warnx("invalid mft. no data runs in data attribute");
+ }
+ else
+ {
+ hasdata = true;
+ nonres = (ntfs_attribnonresident*)header;
+
+ /* Check total length against nonres->cbAllocated */
+ if(map->_count == 0)
+ total = nonres->cbAllocated;
+
+ /* Now loop through the data run */
+ if(ntfsx_datarun_first(datarun))
+ {
+ do
+ {
+ if(datarun->sparse)
+ {
+ warnx("invalid mft. sparse data runs");
+ }
+ else
+ {
+ mftmap_expand(map, &allocated);
+
+ ASSERT(map->info->cluster != 0);
+
+ length = datarun->length * ((map->info->cluster * kSectorSize) / kNTFS_RecordLen);
+ if(length == 0)
+ continue;
+
+ firstSector = (datarun->cluster * map->info->cluster) + map->info->first;
+ if(firstSector >= map->info->end)
+ continue;
+
+ /*
+ * When the same as the last one skip. This occurs in really
+ * fragmented MFTs where we read the inline DATA attribute first
+ * and then move on to the ATTRLIST one.
+ */
+ if(map->_count > 0 && map->_blocks[map->_count - 1].length == length &&
+ map->_blocks[map->_count - 1].firstSector == firstSector)
+ continue;
+
+ map->_blocks[map->_count].length = length;
+ map->_blocks[map->_count].firstSector = firstSector;
+ map->_count++;
+
+ total -= length * kSectorSize;
+ }
+ }
+ while(ntfsx_datarun_next(datarun));
+ }
+
+ ntfsx_datarun_free(datarun);
+ datarun = NULL;
+ }
+ }
+
+ ntfsx_attribute_free(attribdata);
+ attribdata = NULL;
}
+ if(!hasdata)
+ RETWARNBX("invalid mft. no data attribute");
+
ret = true;
}
@@ -484,6 +640,8 @@ cleanup:
ntfsx_attribute_free(attribdata);
if(datarun)
ntfsx_datarun_free(datarun);
+ if(attrenum)
+ ntfsx_attrib_enum_free(attrenum);
return ret;
}
diff --git a/src/ntfsx.h b/src/ntfsx.h
index 04ebabd..2c1054c 100644
--- a/src/ntfsx.h
+++ b/src/ntfsx.h
@@ -52,7 +52,7 @@ typedef struct _ntfsx_cluster
}
ntfsx_cluster;
-bool ntfsx_cluster_reserve(ntfsx_cluster* clus, partitioninfo* info);
+void ntfsx_cluster_reserve(ntfsx_cluster* clus, partitioninfo* info);
bool ntfsx_cluster_read(ntfsx_cluster* clus, partitioninfo* info, uint64 begSector, int dd);
void ntfsx_cluster_release(ntfsx_cluster* clus);
@@ -73,7 +73,6 @@ ntfs_attribheader* ntfsx_attribute_header(ntfsx_attribute* attr);
void* ntfsx_attribute_getresidentdata(ntfsx_attribute* attr);
uint32 ntfsx_attribute_getresidentsize(ntfsx_attribute* attr);
ntfsx_datarun* ntfsx_attribute_getdatarun(ntfsx_attribute* attr);
-bool ntfsx_attribute_next(ntfsx_attribute* attr, uint32 attrType);
@@ -86,12 +85,30 @@ typedef struct _ntfsx_record
ntfsx_record;
ntfsx_record* ntfsx_record_alloc(partitioninfo* info);
+ntfsx_cluster* ntfsx_record_cluster(ntfsx_record* record);
void ntfsx_record_free(ntfsx_record* record);
bool ntfsx_record_read(ntfsx_record* record, uint64 begSector, int dd);
ntfs_recordheader* ntfsx_record_header(ntfsx_record* record);
ntfsx_attribute* ntfsx_record_findattribute(ntfsx_record* record, uint32 attrType, int dd);
+/* used as a heap based object */
+typedef struct _ntfsx_attrib_enum
+{
+ ntfs_attribheader* _attrhead; /* The last attribute examined */
+ ntfs_attriblistrecord* _listrec; /* The last attr list record examined */
+ unsigned char _flags; /* Whether to search through the list first */
+ uint32 type; /* The type we're going for */
+}
+ntfsx_attrib_enum;
+
+ntfsx_attrib_enum* ntfsx_attrib_enum_alloc(uint32 type, bool normal);
+ntfsx_attribute* ntfsx_attrib_enum_all(ntfsx_attrib_enum* attrenum, ntfsx_record* record);
+ntfsx_attribute* ntfsx_attrib_enum_inline(ntfsx_attrib_enum* attrenum, ntfsx_record* record);
+ntfsx_attribute* ntfsx_attrib_enum_list(ntfsx_attrib_enum* attrenum, ntfsx_record* record);
+void ntfsx_attrib_enum_free(ntfsx_attrib_enum* attrenum);
+
+
/* used as a stack based object */
struct _ntfsx_mftmap_block;
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");
diff --git a/src/scrounge.h b/src/scrounge.h
index 6ebed43..9e9f37c 100644
--- a/src/scrounge.h
+++ b/src/scrounge.h
@@ -34,4 +34,10 @@ void scroungeUsingRaw(partitioninfo* pi);
void setFileAttributes(fchar_t* filename, uint32 flags);
void setFileTime(fchar_t* filename, uint64* created, uint64* accessed, uint64* modified);
+int compareFileData(int f, void* data, size_t length);
+
+#ifdef _DEBUG
+ extern bool g_verifyMode;
+#endif
+
#endif /* __SCROUNGE_H__ */
diff --git a/src/unicode.c b/src/unicode.c
index b715a47..a2dcacb 100644
--- a/src/unicode.c
+++ b/src/unicode.c
@@ -19,8 +19,7 @@ char* unicode_transcode16to8(const ntfs_char* src, size_t len)
/* Allocate 1.25 times the length initially */
alloc = len + (len / 4) + 1;
- ret = (char*)malloc(alloc * sizeof(char));
- if(!ret) return NULL;
+ ret = (char*)mallocf(alloc * sizeof(char));
c = src;
e = c + len;
@@ -31,8 +30,7 @@ char* unicode_transcode16to8(const ntfs_char* src, size_t len)
if(pos + 4 >= alloc)
{
alloc += (len / 2) + 1;
- if(!(ret = (char*)reallocf(ret, alloc * sizeof(char))))
- return NULL;
+ ret = (char*)reallocf(ret, alloc * sizeof(char));
}
/* Encode as one character */