summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stef@thewalter.net>2003-09-17 19:37:33 +0000
committerStef Walter <stef@thewalter.net>2003-09-17 19:37:33 +0000
commitedd737cd17f4e6712ba289e977c330ca3adc5495 (patch)
treed34e14b816314b0819f5eaa22bc1216d45e24dd2
Initial Import
-rw-r--r--.cvsignore3
-rw-r--r--src/StdAfx.cpp29
-rw-r--r--src/StdAfx.h44
-rw-r--r--src/drive.cpp137
-rw-r--r--src/drive.h93
-rw-r--r--src/locks.h32
-rw-r--r--src/memref.h49
-rw-r--r--src/misc.c107
-rw-r--r--src/ntfs.c122
-rw-r--r--src/ntfs.h203
-rw-r--r--src/ntfsx.c259
-rw-r--r--src/ntfsx.h114
-rw-r--r--src/scrounge.c654
-rw-r--r--src/scrounge.h33
-rw-r--r--src/usuals.h45
-rw-r--r--win32/win32.dsp147
-rw-r--r--win32/win32.dsw29
17 files changed, 2100 insertions, 0 deletions
diff --git a/.cvsignore b/.cvsignore
new file mode 100644
index 0000000..a243ba0
--- /dev/null
+++ b/.cvsignore
@@ -0,0 +1,3 @@
+Debug
+Release
+Trash \ No newline at end of file
diff --git a/src/StdAfx.cpp b/src/StdAfx.cpp
new file mode 100644
index 0000000..d939e46
--- /dev/null
+++ b/src/StdAfx.cpp
@@ -0,0 +1,29 @@
+//
+// 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>
+//
+
+
+// stdafx.cpp : source file that includes just the standard includes
+// Scrounge.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
diff --git a/src/StdAfx.h b/src/StdAfx.h
new file mode 100644
index 0000000..38f0a95
--- /dev/null
+++ b/src/StdAfx.h
@@ -0,0 +1,44 @@
+//
+// 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>
+//
+
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#if !defined(AFX_STDAFX_H__552C4258_1192_4B38_948A_887884424C46__INCLUDED_)
+#define AFX_STDAFX_H__552C4258_1192_4B38_948A_887884424C46__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include <stdio.h>
+#include <windows.h>
+#include <tchar.h>
+#include "memref.h"
+
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__552C4258_1192_4B38_948A_887884424C46__INCLUDED_)
diff --git a/src/drive.cpp b/src/drive.cpp
new file mode 100644
index 0000000..f485cf6
--- /dev/null
+++ b/src/drive.cpp
@@ -0,0 +1,137 @@
+//
+// 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>
+//
+
+
+#include "drive.h"
+#include "memref.h"
+#include <malloc.h>
+#include <string.h>
+#include <stdio.h>
+
+
+PartitionInfo* CreatePartitionInfo()
+{
+ PartitionInfo* pInfo = (PartitionInfo*)refalloc(sizeof(PartitionInfo));
+ if(pInfo)
+ memset(pInfo, 0, sizeof(PartitionInfo));
+
+ return pInfo;
+}
+
+void FreePartitionInfo(PartitionInfo* pInfo)
+{
+ if(pInfo)
+ {
+ if(pInfo->pLocks)
+ free(pInfo->pLocks);
+
+ refrelease(pInfo);
+ }
+}
+
+// These locks are used to signify which
+struct DriveLock
+{
+ uint64 beg;
+ uint64 end;
+};
+
+
+bool IntersectRange(uint64& b1, uint64& e1, uint64 b2, uint64 e2)
+{
+ // TODO: (later) This is stupid! There's a simple quick way
+ // need to update
+
+ if(b1 <= b2 && e1 > b2 && b1 < e2) // Overlaps the first portion
+ e1 = e2;
+ else if(b1 < e2 && e1 >= e2 && b2 < e1) // Overlaps second portion
+ b1 = b2;
+ else if(b1 > b2 && e1 < e2) // Encompassed
+ { }
+ else if (b1 == b2 && e1 == e2) // Identical
+ { }
+ else
+ return false;
+
+ return true;
+}
+
+void AddLocationLock(PartitionInfo* pInfo, uint64 beg, uint64 end)
+{
+ if(pInfo->cLocks <= pInfo->curLock)
+ {
+ pInfo->cLocks += 0x400;
+ pInfo->pLocks = (DriveLock*)realloc(pInfo->pLocks, sizeof(DriveLock) * pInfo->cLocks);
+ }
+
+ // TODO: Implement a more efficient method here!
+
+ if(pInfo->pLocks)
+ {
+ bool bFound = false;
+
+ // Go through and check for a current lock we can tag onto
+ for(uint32 i = 0; i < pInfo->curLock; i++)
+ {
+ if(IntersectRange(pInfo->pLocks[i].beg, pInfo->pLocks[i].end,
+ beg, end))
+ {
+ bFound = true;
+ }
+ }
+
+ if(!bFound)
+ {
+ pInfo->pLocks[pInfo->curLock].beg = beg;
+ pInfo->pLocks[pInfo->curLock].end = end;
+ pInfo->curLock++;
+ }
+ }
+}
+
+bool CheckLocationLock(PartitionInfo* pInfo, uint64& sec)
+{
+ // Go through and check for a lock
+ for(uint32 i = 0; i < pInfo->curLock; i++)
+ {
+ if(sec >= pInfo->pLocks[i].beg &&
+ sec < pInfo->pLocks[i].end)
+ {
+ sec = pInfo->pLocks[i].end;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#ifdef _DEBUG
+void DumpLocationLocks(PartitionInfo* pInfo)
+{
+ for(uint32 i = 0; i < pInfo->curLock; i++)
+ {
+ printf("%u\t%u\n", (uint32)pInfo->pLocks[i].beg, (uint32)pInfo->pLocks[i].end);
+ }
+
+ printf("\n");
+}
+#endif
diff --git a/src/drive.h b/src/drive.h
new file mode 100644
index 0000000..bec704e
--- /dev/null
+++ b/src/drive.h
@@ -0,0 +1,93 @@
+//
+// 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>
+//
+
+
+#ifndef __DRIVE_H__20010822
+#define __DRIVE_H__20010822
+
+#include "usuals.h"
+
+const uint16 kSectorSize = 0x200;
+struct DriveLock;
+
+struct PartitionInfo
+{
+ uint32 firstSector;
+ uint32 lastSector;
+ uint32 offMFT; // In sectors
+ byte clusterSize; // In sectors
+
+ DriveLock* pLocks;
+ uint32 cLocks;
+ uint32 curLock;
+};
+
+PartitionInfo* CreatePartitionInfo();
+void FreePartitionInfo(PartitionInfo* pInfo);
+
+
+#pragma pack(push, drive)
+#pragma pack(1)
+
+const byte kPartition_Invalid = 0;
+const byte kPartition_Extended = 5;
+const byte kPartition_ExtendedLBA = 15;
+
+
+// Partition table entry
+struct Drive_PartEntry
+{
+ byte active; // partition bootable flag
+ byte starthead; // starting head
+ byte startsector; // starting sector and 2 MS bits of cylinder
+ byte startcylinder; // starting cylinder (low 8 bits)
+ byte system; // partition type
+ byte endhead; // ending head
+ byte endsector; // ending sector and 2 MS bits of cylinder
+ byte endcylinder; // ending cylinder (low 8 bits)
+ uint32 startsec; // absolute starting sector
+ uint32 endsec; // absolute ending sector
+};
+
+const uint16 kMBR_Sig = 0xAA55;
+
+// Master Boot Record
+struct Drive_MBR
+{
+ byte fill[0x1be]; // boot code
+ Drive_PartEntry partitions[4]; // partition table
+ uint16 sig; // 55AAh boot signature
+};
+
+#pragma pack(pop, drive)
+
+#define CLUSTER_TO_SECTOR(info, clus) (((clus) * (info).clusterSize) + (info).firstSector)
+#define SECTOR_TO_BYTES(sec) ((sec) * kSectorSize)
+#define CLUSTER_SIZE(info) ((info).clusterSize * kSectorSize)
+
+bool IntersectRange(uint64& b1, uint64& e1, uint64 b2, uint64 e2);
+void AddLocationLock(PartitionInfo* pInfo, uint64 beg, uint64 end);
+bool CheckLocationLock(PartitionInfo* pInfo, uint64& sec);
+
+
+
+#endif //__DRIVE_H__20010822 \ No newline at end of file
diff --git a/src/locks.h b/src/locks.h
new file mode 100644
index 0000000..d60878c
--- /dev/null
+++ b/src/locks.h
@@ -0,0 +1,32 @@
+//
+// 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>
+//
+
+#ifndef __LOCKS_H__20010828
+#define __LOCKS_H__20010828
+
+
+#include "drive.h"
+
+
+
+
+#endif //__LOCKS_H__20010828 \ No newline at end of file
diff --git a/src/memref.h b/src/memref.h
new file mode 100644
index 0000000..a26ac34
--- /dev/null
+++ b/src/memref.h
@@ -0,0 +1,49 @@
+//
+// 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>
+//
+
+
+#ifndef __MEMREF_H__20010827
+#define __MEMREF_H__20010827
+
+#ifdef _DEBUG
+
+void* _refalloc_dbg(size_t sz);
+void* _refadd_dbg(void* pBuff);
+void _refrelease_dbg(void* pBuff);
+
+#define refalloc _refalloc_dbg
+#define refadd _refadd_dbg
+#define refrelease _refrelease_dbg
+
+#else
+
+void* _refalloc(size_t sz);
+void* _refadd(void* pBuff);
+void _refrelease(void* pBuff);
+
+#define refalloc _refalloc
+#define refadd _refadd
+#define refrelease _refrelease
+
+#endif
+
+#endif //__MEMREF_H__20010827 \ No newline at end of file
diff --git a/src/misc.c b/src/misc.c
new file mode 100644
index 0000000..5ea67cd
--- /dev/null
+++ b/src/misc.c
@@ -0,0 +1,107 @@
+//
+// 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>
+//
+
+
+#include "memref.h"
+#include "malloc.h"
+#include "assert.h"
+
+// WARNING!! Not thread safe or very efficient for large
+// amounts of memory allocations
+
+const size_t kRefSig = 0x1F2F3F4F;
+
+void* _refalloc_dbg(size_t sz)
+{
+ // Allocate extra counter value before memory
+ size_t* pMem = (size_t*)malloc(sz * sizeof(size_t) * 2);
+
+ if(pMem)
+ {
+ pMem[0] = kRefSig;
+ pMem[1] = 1;
+ return pMem + 2;
+ }
+
+ return pMem;
+}
+
+void* _refalloc(size_t sz)
+{
+ // Allocate extra counter value before memory
+ size_t* pMem = (size_t*)malloc(sz * sizeof(size_t) * 1);
+
+ if(pMem)
+ {
+ pMem[0] = 1;
+ return pMem + 1;
+ }
+
+ return pMem;
+}
+
+void* _refadd_dbg(void* pBuff)
+{
+ if(pBuff)
+ {
+ // Increment the counter value
+ size_t* pMem = (size_t*)pBuff - 2;
+ assert(pMem[0] = kRefSig);
+ pMem[1]++;
+ }
+
+ return pBuff;
+}
+
+void* _refadd(void* pBuff)
+{
+ if(pBuff)
+ // Increment the counter value
+ ((size_t*)pBuff)[-1]++;
+
+ return pBuff;
+}
+
+void _refrelease_dbg(void* pBuff)
+{
+ if(pBuff)
+ {
+ // Decrement the counter value
+ size_t* pMem = (size_t*)pBuff - 2;
+ assert(pMem[0] = kRefSig);
+
+ if(!--pMem[1])
+ free(pMem);
+ }
+}
+
+void _refrelease(void* pBuff)
+{
+ if(pBuff)
+ {
+ // Decrement the counter value
+ size_t* pMem = (size_t*)pBuff - 1;
+
+ if(!--pMem[0])
+ free(pMem);
+ }
+} \ No newline at end of file
diff --git a/src/ntfs.c b/src/ntfs.c
new file mode 100644
index 0000000..28b4cca
--- /dev/null
+++ b/src/ntfs.c
@@ -0,0 +1,122 @@
+//
+// 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>
+//
+
+
+#include "usuals.h"
+#include "ntfs.h"
+
+#include "malloc.h"
+#include "string.h"
+
+
+NTFS_AttribHeader* NTFS_SearchAttribute(byte* pLocation, uint32 attrType, void* pEnd, bool bSkip)
+{
+ // Now we should be at attributes
+ while((!pEnd || (pLocation + sizeof(NTFS_AttribHeader) < pEnd)) &&
+ *((uint32*)pLocation) != kNTFS_RecEnd)
+ {
+ NTFS_AttribHeader* pAttrib = (NTFS_AttribHeader*)pLocation;
+
+ if(!bSkip)
+ {
+ if(pAttrib->type == attrType)
+ return pAttrib;
+ }
+ else
+ bSkip = false;
+
+ pLocation += pAttrib->cbAttribute;
+ }
+
+ return NULL;
+}
+
+
+NTFS_AttribHeader* NTFS_FindAttribute(NTFS_RecordHeader* pRecord, uint32 attrType, void* pEnd)
+{
+ byte* pLocation = (byte*)pRecord;
+ pLocation += kNTFS_RecHeaderLen;
+
+ return NTFS_SearchAttribute(pLocation, attrType, pEnd, false);
+}
+
+NTFS_AttribHeader* NTFS_NextAttribute(NTFS_AttribHeader* pAttrib, uint32 attrType, void* pEnd)
+{
+ return NTFS_SearchAttribute((byte*)pAttrib, attrType, pEnd, true);
+}
+
+void* NTFS_GetAttributeData(NTFS_AttribResident* pAttrib, void* pEnd)
+{
+ void* pData = ((byte*)pAttrib) + pAttrib->offAttribData;
+ if(!pEnd && pData > pEnd)
+ return NULL;
+
+ return pData;
+}
+
+bool NTFS_IsBetterNameSpace(byte n1, byte n2)
+{
+ // We like our namespaces in this order
+ // 1. WIN32
+ // 2. WIN32/DOS
+ // 3. DOS
+ // 4. POSIX
+
+ if(n1 == kNTFS_NameSpacePOSIX)
+ return true;
+ if(n1 == kNTFS_NameSpaceDOS &&
+ (n2 == kNTFS_NameSpaceWIN32 || n2 == kNTFS_NameSpaceWINDOS))
+ return true;
+ if(n1 == kNTFS_NameSpaceWINDOS &&
+ n2 == kNTFS_NameSpaceWIN32)
+ return true;
+
+ return false;
+}
+
+bool NTFS_DoFixups(byte* pCluster, uint32 cbCluster)
+{
+ ASSERT(cbCluster % kSectorSize == 0);
+ NTFS_RecordHeader* pRecord = (NTFS_RecordHeader*)pCluster;
+
+ byte numSectors = (byte)(cbCluster / kSectorSize);
+
+ // Check the number of sectors against array
+ if(pRecord->cwUpdSeq - 1 < numSectors)
+ numSectors = pRecord->cwUpdSeq - 1;
+
+ uint16* pUpdSeq = (uint16*)(pCluster + pRecord->offUpdSeq);
+
+ for(byte i = 0; i < numSectors; i++)
+ {
+ // Check last 2 bytes in each sector against
+ // first double byte value in update sequence
+ uint16* pSectorFooter = (uint16*)((pCluster + (kSectorSize - 2)) + (i * kSectorSize));
+ if(*pSectorFooter == pUpdSeq[0])
+ *pSectorFooter = pUpdSeq[i + 1];
+ else
+ return false;
+ }
+
+ return true;
+}
+
diff --git a/src/ntfs.h b/src/ntfs.h
new file mode 100644
index 0000000..59a129b
--- /dev/null
+++ b/src/ntfs.h
@@ -0,0 +1,203 @@
+//
+// 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>
+//
+
+#ifndef __NTFS_H__
+#define __NTFS_H__
+
+#include "usuals.h"
+#include "stddef.h"
+#include "drive.h"
+
+#pragma pack(push, ntfs)
+#pragma pack(1)
+
+
+// WARNING Assumptions:
+const uint32 kNTFS_RecordLen = 0x0400;
+
+
+const char kNTFS_SysId[] = "NTFS ";
+
+struct NTFS_BootSector
+{
+ byte jmp[3]; // Jump to the boot loader routine
+ char sysId[8]; // System Id: "NTFS "
+ uint16 bytePerSec; // Bytes per sector
+ byte secPerClus; // Sectors per cluster
+ byte padding[7]; // Unused
+ byte mediaDescriptor; // Media descriptor (a)
+ byte padding2[2]; // Unused
+ uint16 secPerTrack; // Sectors per track
+ uint16 numHeads; // Number of heads
+ byte padding3[8]; // Unused
+ uint32 signature; // Always 80 00 80 00
+ uint64 cSectors; // Number of sectors in the volume
+ uint64 offMFT; // LCN of VCN 0 of the $MFT
+ uint64 offMFTMirr; // LCN of VCN 0 of the $MFTMirr
+ uint32 clusPerMFT; // Clusters per MFT Record (b)
+ uint32 clusPerIndex; // Clusters per Index Record
+ uint64 serialNum; // Volume serial number
+};
+
+const uint32 kNTFS_RecMagic = 'ELIF';
+const uint32 kNTFS_RecEnd = 0xFFFFFFFF;
+const uint32 kNTFS_RecHeaderLen = 0x30;
+
+const uint16 kNTFS_RecFlagUse = 0x01;
+const uint16 kNTFS_RecFlagDir = 0x02;
+
+struct NTFS_RecordHeader
+{
+ uint32 magic; // Magic number 'FILE'
+ uint16 offUpdSeq; // Offset to the update sequence
+ uint16 cwUpdSeq; // Size in words of Update Sequence Number & Array (S)
+ uint64 logSeqNum; // $LogFile Sequence Number (LSN)
+ uint16 seqNum; // Sequence number
+ uint16 cHardlinks; // Hard link count
+ uint16 x_offUpdSeqArr; // Offset to Update Sequence Array (DON'T THINK SO)
+ uint16 flags; // Flags
+ uint32 cbRecord; // Real size of the FILE record
+ uint32 cbAllocated; // Allocated size of the FILE record
+ uint64 refBaseRecord; // File reference to the base FILE record
+ uint16 nextAttrId; // Next Attribute Id
+ uint16 padding; // (XP) Align to 4 byte boundary
+ uint32 recordNum; // (XP) Number of this MFT Record
+};
+
+
+
+const uint16 kNTFS_AttrCompressed = 0x0001;
+const uint16 kNTFS_AttrEncrypted = 0x0002;
+const uint16 kNTFS_AttrSparse = 0x0004;
+
+struct NTFS_AttribHeader
+{
+ uint32 type; // Attribute Type (e.g. 0x10, 0x60)
+ uint32 cbAttribute; // Length (including this header)
+ byte bNonResident; // Non-resident flag
+ byte cName; // Name length
+ uint16 offName; // Offset to the Attribute
+ uint16 flags; // Flags
+ uint16 idAttribute; // Attribute Id (a)
+};
+
+struct NTFS_AttribResident
+{
+ NTFS_AttribHeader header;
+
+ uint32 cbAttribData; // Length of the Attribute
+ uint16 offAttribData; // Offset to the Attribute
+ byte bIndexed; // Indexed flag
+ byte padding; // 0x00 Padding
+};
+
+struct NTFS_AttribNonResident
+{
+ NTFS_AttribHeader header;
+
+ uint64 startVCN; // Starting VCN
+ uint64 lastVCN; // Last VCN
+ uint16 offDataRuns; // Offset to the Data Runs
+ uint16 compUnitSize; // Compression Unit Size (b)
+ uint32 padding; // Padding
+ uint64 cbAllocated; // Allocated size of the attribute (c)
+ uint64 cbAttribData; // Real size of the attribute
+ uint64 cbInitData; // Initialized data size of the stream (d)
+};
+
+
+const uint32 kNTFS_ATTRIBUTE_LIST = 0x20;
+const uint32 kNTFS_FILENAME = 0x30;
+const uint32 kNTFS_DATA = 0x80;
+
+
+const uint32 kNTFS_FileReadOnly = 0x0001;
+const uint32 kNTFS_FileHidden = 0x0002;
+const uint32 kNTFS_FileSystem = 0x0004;
+const uint32 kNTFS_FileArchive = 0x0020;
+const uint32 kNTFS_FileDevice = 0x0040;
+const uint32 kNTFS_FileNormal = 0x0080;
+const uint32 kNTFS_FileTemorary = 0x0100;
+const uint32 kNTFS_FileSparse = 0x0200;
+const uint32 kNTFS_FileReparse = 0x0400;
+const uint32 kNTFS_FileCompressed = 0x0800;
+const uint32 kNTFS_FileOffline = 0x1000;
+const uint32 kNTFS_FileNotIndexed = 0x2000;
+const uint32 kNTFS_FileEncrypted = 0x4000;
+
+const byte kNTFS_NameSpacePOSIX = 0x00;
+const byte kNTFS_NameSpaceWIN32 = 0x01;
+const byte kNTFS_NameSpaceDOS = 0x02;
+const byte kNTFS_NameSpaceWINDOS = 0x03;
+
+const wchar_t kNTFS_MFTName[] = L"$MFT";
+
+struct NTFS_AttrFileName
+{
+ uint64 refParent; // File reference to the parent directory.
+ uint64 timeCreated; // C Time - File Creation
+ uint64 timeAltered; // A Time - File Altered
+ uint64 timeModified; // M Time - MFT Changed
+ uint64 timeRead; // R Time - File Read
+ uint64 cbAllocated; // Allocated size of the file
+ uint64 cbFileSize; // Real size of the file
+ uint32 flags; // Flags, e.g. Directory, compressed, hidden
+ uint32 eaReparse; // Used by EAs and Reparse
+ byte cFileName; // Filename length in characters (L)
+ byte nameSpace; // Filename namespace
+ // File Name comes here
+};
+
+struct NTFS_AttrListRecord
+{
+ uint32 type; // Type
+ uint16 cbRecord; // Record length
+ byte cName; // Name length (N)
+ byte offName; // Offset to Name (a)
+ uint64 startVCN; // Starting VCN (b)
+ uint64 refAttrib; // Base File Reference of the attribute
+ uint16 idAttribute; // Attribute Id (c)
+ // Attribute name here
+};
+
+#pragma pack(pop, ntfs)
+
+NTFS_AttribHeader* NTFS_FindAttribute(NTFS_RecordHeader* pRecord, uint32 attrType, void* pEnd);
+NTFS_AttribHeader* NTFS_NextAttribute(NTFS_AttribHeader* pAttrib, uint32 attrType, void* pEnd);
+
+void* NTFS_GetAttributeData(NTFS_AttribResident* pAttrib, void* pEnd);
+bool NTFS_IsBetterNameSpace(byte n1, byte n2);
+bool NTFS_DoFixups(byte* pCluster, uint32 cbCluster);
+
+
+#define NTFS_RefToSector(info, ref) (((ref & 0xFFFFFFFFFFFF) * (kNTFS_RecordLen / kSectorSize)) + (info).firstSector + (info).offMFT)
+#define NTFS_MakeFileTime(i64, ft) ((ft).dwLowDateTime = LOWDWORD(i64), (ft).dwHighDateTime = HIGHDWORD(i64))
+
+
+
+//
+// The record, attribute or whatever was invalid on disk
+//
+#define ERROR_NTFS_INVALID 10801L
+#define ERROR_NTFS_NOTIMPLEMENT 10802L
+
+#endif //__NTFS_H__ \ No newline at end of file
diff --git a/src/ntfsx.c b/src/ntfsx.c
new file mode 100644
index 0000000..fe7d898
--- /dev/null
+++ b/src/ntfsx.c
@@ -0,0 +1,259 @@
+//
+// AUTHOR
+// N. Nielsen
+//
+// VERSION
+// 0.7
+//
+// LICENSE
+// This software is in the public domain.
+//
+// The software is provided "as is", without warranty of any kind,
+// express or implied, including but not limited to the warranties
+// of merchantability, fitness for a particular purpose, and
+// noninfringement. In no event shall the author(s) be liable for any
+// claim, damages, or other liability, whether in an action of
+// contract, tort, or otherwise, arising from, out of, or in connection
+// with the software or the use or other dealings in the software.
+//
+// SUPPORT
+// Send bug reports to: <nielsen@memberwebs.com>
+//
+
+// ntfsx.cpp: implementation of the NTFS_Record class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+#include "ntfs.h"
+#include "ntfsx.h"
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+bool NTFS_Cluster::New(PartitionInfo* pInfo)
+{
+ Free();
+
+ m_cbCluster = CLUSTER_SIZE(*pInfo);
+
+ m_pCluster = (byte*)refalloc(m_cbCluster);
+ return m_pCluster != NULL;
+}
+
+bool NTFS_Cluster::Read(PartitionInfo* pInfo, uint64 begSector, HANDLE hIn)
+{
+ if(!m_pCluster)
+ {
+ if(!New(pInfo))
+ return false;
+ }
+
+ // Read the mftRecord
+ uint64 offRecord = SECTOR_TO_BYTES(begSector);
+ LONG lHigh = HIGHDWORD(offRecord);
+ if(SetFilePointer(hIn, LOWDWORD(offRecord), &lHigh, FILE_BEGIN) == -1
+ && GetLastError() != NO_ERROR)
+ {
+ Free();
+ return false;
+ }
+
+ DWORD dwRead = 0;
+ if(!ReadFile(hIn, m_pCluster, m_cbCluster, &dwRead, NULL) ||
+ dwRead != m_cbCluster)
+ {
+ Free();
+ ::SetLastError(ERROR_READ_FAULT);
+ return false;
+ }
+
+ ::SetLastError(ERROR_SUCCESS);
+ return true;
+}
+
+NTFS_Record::NTFS_Record(PartitionInfo* pInfo)
+{
+ m_pInfo = (PartitionInfo*)refadd(pInfo);
+}
+
+NTFS_Record::~NTFS_Record()
+{
+ refrelease(m_pInfo);
+}
+
+bool NTFS_Record::Read(uint64 begSector, HANDLE hIn)
+{
+ if(!NTFS_Cluster::Read(m_pInfo, begSector, hIn))
+ return false;
+
+ // Check and validate this record
+ NTFS_RecordHeader* pRecord = GetHeader();
+ if(pRecord->magic != kNTFS_RecMagic ||
+ !NTFS_DoFixups(m_pCluster, m_cbCluster))
+ {
+ NTFS_Cluster::Free();
+ ::SetLastError(ERROR_NTFS_INVALID);
+ return false;
+ }
+
+ return true;
+}
+
+NTFS_Attribute* NTFS_Record::FindAttribute(uint32 attrType, HANDLE hIn)
+{
+ NTFS_Attribute* pAttribute = NULL;
+
+ // Make sure we have a valid record
+ ASSERT(GetHeader());
+ NTFS_AttribHeader* pHeader = NTFS_FindAttribute(GetHeader(), attrType, (m_pCluster + m_cbCluster));
+
+ if(pHeader)
+ {
+ pAttribute = new NTFS_Attribute(*this, pHeader);
+ }
+ else
+ {
+ // Do attribute list thing here!
+ pHeader = NTFS_FindAttribute(GetHeader(), kNTFS_ATTRIBUTE_LIST, (m_pCluster + m_cbCluster));
+
+ // For now we only support Resident Attribute lists
+ if(hIn && pHeader && !pHeader->bNonResident)
+ {
+ NTFS_AttribResident* pResident = (NTFS_AttribResident*)pHeader;
+ NTFS_AttrListRecord* pAttr = (NTFS_AttrListRecord*)((byte*)pHeader + pResident->offAttribData);
+
+ // Go through AttrList records looking for this attribute
+ while((byte*)pAttr < (byte*)pHeader + pHeader->cbAttribute)
+ {
+ // Found it!
+ if(pAttr->type == attrType)
+ {
+ // Read in appropriate cluster
+ uint64 mftRecord = NTFS_RefToSector(*m_pInfo, pAttr->refAttrib);
+
+ NTFS_Record recAttr(m_pInfo);
+ if(recAttr.Read(mftRecord, hIn))
+ {
+ pAttribute = recAttr.FindAttribute(attrType, hIn);
+ break;
+ }
+
+ }
+
+ pAttr = (NTFS_AttrListRecord*)((byte*)pAttr + pAttr->cbRecord);
+ }
+ }
+ }
+
+ return pAttribute;
+}
+
+bool NTFS_Attribute::NextAttribute(uint32 attrType)
+{
+ NTFS_AttribHeader* pHeader = NTFS_NextAttribute(GetHeader(), attrType, m_pMem + m_cbMem);
+ if(pHeader)
+ {
+ m_pHeader = pHeader;
+ return true;
+ }
+
+ return false;
+}
+
+NTFS_Attribute::NTFS_Attribute(NTFS_Cluster& clus, NTFS_AttribHeader* pHeader)
+{
+ m_pHeader = pHeader;
+ m_pMem = clus.m_pCluster;
+ m_cbMem = clus.m_cbCluster;
+ refadd(m_pMem);
+}
+
+void* NTFS_Attribute::GetResidentData()
+{
+ ASSERT(!m_pHeader->bNonResident);
+ NTFS_AttribResident* pRes = (NTFS_AttribResident*)m_pHeader;
+ return (byte*)m_pHeader + pRes->offAttribData;
+}
+
+uint32 NTFS_Attribute::GetResidentSize()
+{
+ ASSERT(!m_pHeader->bNonResident);
+ NTFS_AttribResident* pRes = (NTFS_AttribResident*)m_pHeader;
+ return pRes->cbAttribData;
+}
+
+NTFS_DataRun* NTFS_Attribute::GetDataRun()
+{
+ ASSERT(m_pHeader->bNonResident);
+ NTFS_AttribNonResident* pNonRes = (NTFS_AttribNonResident*)m_pHeader;
+
+ return new NTFS_DataRun(m_pMem, (byte*)m_pHeader + pNonRes->offDataRuns);
+}
+
+NTFS_DataRun::NTFS_DataRun(byte* pClus, byte* pDataRun)
+{
+ ASSERT(pDataRun);
+ m_pMem = (byte*)refadd(pClus);
+ m_pDataRun = pDataRun;
+ m_pCurPos = NULL;
+ m_firstCluster = m_numClusters = 0;
+ m_bSparse = false;
+}
+
+bool NTFS_DataRun::First()
+{
+ m_pCurPos = m_pDataRun;
+ m_firstCluster = m_numClusters = 0;
+ m_bSparse = false;
+ return Next();
+}
+
+bool NTFS_DataRun::Next()
+{
+ ASSERT(m_pCurPos);
+
+ if(!*m_pCurPos)
+ return false;
+
+ byte cbLen = *m_pCurPos & 0x0F;
+ byte cbOff = *m_pCurPos >> 4;
+
+ // ASSUMPTION length and offset are less 64 bit numbers
+ if(cbLen == 0 || cbLen > 8 || cbOff > 8)
+ return false;
+
+ ASSERT(cbLen <= 8);
+ ASSERT(cbOff <= 8);
+
+ m_pCurPos++;
+
+ memset(&m_numClusters, 0, sizeof(uint64));
+
+ memcpy(&m_numClusters, m_pCurPos, cbLen);
+ m_pCurPos += cbLen;
+
+ int64 offset;
+
+ // Note that offset can be negative
+ if(*(m_pCurPos + (cbOff - 1)) & 0x80)
+ memset(&offset, ~0, sizeof(int64));
+ else
+ memset(&offset, 0, sizeof(int64));
+
+ memcpy(&offset, m_pCurPos, cbOff);
+ m_pCurPos += cbOff;
+
+ if(offset == 0)
+ {
+ m_bSparse = true;
+ }
+ else
+ {
+ m_bSparse = false;
+ m_firstCluster += offset;
+ }
+
+ return true;
+}
diff --git a/src/ntfsx.h b/src/ntfsx.h
new file mode 100644
index 0000000..586b36b
--- /dev/null
+++ b/src/ntfsx.h
@@ -0,0 +1,114 @@
+//
+// AUTHOR
+// N. Nielsen
+//
+// VERSION
+// 0.7
+//
+// LICENSE
+// This software is in the public domain.
+//
+// The software is provided "as is", without warranty of any kind,
+// express or implied, including but not limited to the warranties
+// of merchantability, fitness for a particular purpose, and
+// noninfringement. In no event shall the author(s) be liable for any
+// claim, damages, or other liability, whether in an action of
+// contract, tort, or otherwise, arising from, out of, or in connection
+// with the software or the use or other dealings in the software.
+//
+// SUPPORT
+// Send bug reports to: <nielsen@memberwebs.com>
+//
+
+
+// ntfsx
+//
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_NTFSX__9363C7D2_D3CC_4D49_BEE0_27AD025670F2__INCLUDED_)
+#define AFX_NTFSX__9363C7D2_D3CC_4D49_BEE0_27AD025670F2__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "ntfs.h"
+
+class NTFS_DataRun
+{
+public:
+ NTFS_DataRun(byte* pClus, byte* pDataRun);
+ ~NTFS_DataRun()
+ { refrelease(m_pMem); }
+
+ bool First();
+ bool Next();
+
+ uint64 m_firstCluster;
+ uint64 m_numClusters;
+ bool m_bSparse;
+
+protected:
+ byte* m_pMem;
+ byte* m_pDataRun;
+ byte* m_pCurPos;
+};
+
+class NTFS_Cluster
+{
+public:
+ NTFS_Cluster()
+ { m_pCluster = NULL; };
+ ~NTFS_Cluster()
+ { Free(); }
+
+ bool New(PartitionInfo* pInfo);
+ bool Read(PartitionInfo* pInfo, uint64 begSector, HANDLE hIn);
+ void Free()
+ { if(m_pCluster) refrelease(m_pCluster); }
+
+ uint32 m_cbCluster;
+ byte* m_pCluster;
+};
+
+class NTFS_Attribute
+{
+public:
+ NTFS_Attribute(NTFS_Cluster& clus, NTFS_AttribHeader* pHeader);
+ ~NTFS_Attribute()
+ { refrelease(m_pMem); }
+
+ NTFS_AttribHeader* GetHeader()
+ { return m_pHeader; }
+
+ void* GetResidentData();
+ uint32 GetResidentSize();
+
+ NTFS_DataRun* GetDataRun();
+
+ bool NextAttribute(uint32 attrType);
+
+
+protected:
+ NTFS_AttribHeader* m_pHeader;
+ byte* m_pMem;
+ uint32 m_cbMem;
+};
+
+class NTFS_Record : public NTFS_Cluster
+{
+public:
+ NTFS_Record(PartitionInfo* pInfo);
+ ~NTFS_Record();
+
+ bool Read(uint64 begSector, HANDLE hIn);
+ NTFS_RecordHeader* GetHeader()
+ { return (NTFS_RecordHeader*)m_pCluster; }
+
+ NTFS_Attribute* FindAttribute(uint32 attrType, HANDLE hIn);
+
+protected:
+ PartitionInfo* m_pInfo;
+};
+
+#endif // !defined(AFX_NTFSX__9363C7D2_D3CC_4D49_BEE0_27AD025670F2__INCLUDED_)
diff --git a/src/scrounge.c b/src/scrounge.c
new file mode 100644
index 0000000..8841aa9
--- /dev/null
+++ b/src/scrounge.c
@@ -0,0 +1,654 @@
+//
+// 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"
+#include "ntfs.h"
+#include "ntfsx.h"
+#include "usuals.h"
+#include "drive.h"
+#include "locks.h"
+#include "scrounge.h"
+
+#define RET_ERROR(l) { ::SetLastError(l); bRet = TRUE; goto clean_up; }
+#define PASS_ERROR() {bRet = TRUE; goto clean_up; }
+#define RET_FATAL(l) { ::SetLastError(l); bRet = FALSE; goto clean_up; }
+#define PASS_FATAL() {bRet = FALSE; goto clean_up; }
+
+
+// ----------------------------------------------------------------------
+// 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 mftRecord, HANDLE hIn)
+{
+ // 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
+
+
+ // Tracks whether or not we output a file
+ bool bFile = false;
+
+ {
+
+ // Read the MFT record
+ NTFS_Record record(pInfo);
+ if(!record.Read(mftRecord, hIn))
+ PASS_ERROR();
+
+ NTFS_RecordHeader* pRecord = record.GetHeader();
+
+
+ // Check if this record is in use
+ if(!(pRecord->flags & kNTFS_RecFlagUse))
+ RET_ERROR(ERROR_SUCCESS);
+
+
+ // Info that we use later
+ WCHAR fileName[MAX_PATH + 1];
+ FILETIME ftCreated;
+ FILETIME ftModified;
+ FILETIME ftAccessed;
+ DWORD fileAttrib = 0;
+ uint64 mftParent = 0;
+ byte* pResidentData = NULL;
+
+
+ // Now get the name and info...
+ pAttribName = record.FindAttribute(kNTFS_FILENAME, hIn);
+ if(!pAttribName) RET_ERROR(ERROR_SUCCESS);
+
+
+ byte nameSpace = kNTFS_NameSpacePOSIX;
+ memset(fileName, 0, sizeof(fileName));
+
+ do
+ {
+ // TODO ASSUMPTION: File name is always resident
+ ASSERT(!pAttribName->GetHeader()->bNonResident);
+
+
+ // Get out all the info we need
+ NTFS_AttrFileName* pFileName = (NTFS_AttrFileName*)pAttribName->GetResidentData();
+
+ // There can be multiple filenames with different namespaces
+ // so choose the best one
+ if(NTFS_IsBetterNameSpace(nameSpace, pFileName->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
+ mftParent = NTFS_RefToSector(*pInfo, pFileName->refParent);
+
+ // Namespace
+ nameSpace = pFileName->nameSpace;
+ }
+ }
+ while(pAttribName->NextAttribute(kNTFS_FILENAME));
+
+
+ // Check if we got a file name
+ if(fileName[0] == 0)
+ RET_ERROR(ERROR_NTFS_INVALID);
+
+
+
+ // Check if it's the root
+ // If so then bumm out cuz we don't want to have anything to do with it
+ if(mftRecord == mftParent || // Root is it's own parent
+ !wcscmp(fileName, L".") || // Or is called '.'
+ !wcscmp(fileName, kNTFS_MFTName)) // Or it's the MFT
+ RET_ERROR(ERROR_SUCCESS);
+
+
+
+ // Create Parent folders
+ if(!ProcessMFTRecord(pInfo, mftParent, hIn))
+ PASS_FATAL();
+
+
+
+ // 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);
+ }
+ }
+
+ wprintf(L"\\%s", fileName);
+ }
+
+
+ // Otherwise write the file data
+ else
+ {
+ // Write to the File
+ hFile = CreateFileW(fileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
+
+ uint16 nRename = 0;
+ wchar_t fileName2[MAX_PATH + 1];
+ wcscpy(fileName2, fileName);
+
+ // 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();
+
+
+ DWORD dwDone = 0;
+ uint64 fileSize = 0;
+
+
+ // Get the File's data
+ pAttribData = record.FindAttribute(kNTFS_DATA, hIn);
+ if(!pAttribData) RET_ERROR(ERROR_NTFS_INVALID);
+
+
+ // For Resident data just write it out
+ if(!pAttribData->GetHeader()->bNonResident)
+ {
+ if(!WriteFile(hFile, pAttribData->GetResidentData(), pAttribData->GetResidentSize(), &dwDone, NULL))
+ PASS_FATAL();
+ }
+
+ // For Nonresident data a bit more involved
+ else
+ {
+ pDataRun = pAttribData->GetDataRun();
+ ASSERT(pDataRun != NULL);
+
+ NTFS_AttribNonResident* pNonRes = (NTFS_AttribNonResident*)pAttribData->GetHeader();
+ fileSize = pNonRes->cbAttribData;
+
+ // Allocate a cluster for reading and writing
+ NTFS_Cluster clus;
+ if(!clus.New(pInfo))
+ RET_FATAL(ERROR_NOT_ENOUGH_MEMORY);
+
+
+ // 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());
+ }
+ }
+
+ // TODO: More intelligence needed here
+ if(fileSize != 0)
+ printf(" (Entire file not written)");
+
+ SetFileTime(hFile, &ftCreated, &ftAccessed, &ftModified);
+
+ CloseHandle(hFile);
+ hFile = NULL;
+
+ SetFileAttributesW(fileName, fileAttrib);
+ }
+ }
+
+ bRet = TRUE;
+ ::SetLastError(ERROR_SUCCESS);
+
+clean_up:
+ if(hFile && hFile != INVALID_HANDLE_VALUE)
+ CloseHandle(hFile);
+ if(pAttribName)
+ delete pAttribName;
+ if(pAttribData)
+ delete pAttribData;
+ if(pDataRun)
+ delete pDataRun;
+
+ if(bFile)
+ {
+ if(::GetLastError() != ERROR_SUCCESS)
+ {
+ printf(" (");
+ PrintLastError();
+ fputc(')', stdout);
+ }
+ }
+
+
+ return bRet;
+}
+
+
+// ----------------------------------------------------------------------
+// Helper function to print out errors
+
+void PrintLastError()
+{
+ DWORD dwErr = ::GetLastError();
+ switch(dwErr)
+ {
+ case ERROR_NTFS_INVALID:
+ printf("Invalid NTFS data structure");
+ break;
+
+ case ERROR_NTFS_NOTIMPLEMENT:
+ printf("NTFS feature not implemented");
+ break;
+
+ 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;
+
+ wprintf((LPTSTR)lpMsgBuf);
+
+ // Free the buffer.
+ ::LocalFree(lpMsgBuf);
+ }
+ }
+ };
+}
+
+
+// ----------------------------------------------------------------------
+// Scrounge the partition for MFT Records and hand off to
+// ProcessMFTRecord for processing
+
+BOOL ScroungeMFTRecords(PartitionInfo* pInfo, HANDLE hIn)
+{
+ byte buffSec[kSectorSize];
+ DWORD dwDummy = 0;
+
+ uint64 numRecords = 0;
+
+ // Save current directory away
+ TCHAR curDir[MAX_PATH + 1];
+ GetCurrentDirectory(MAX_PATH, curDir);
+
+ // 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;
+ }
+
+ // 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;
+
+ 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, 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)
+ {
+ 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;
+}
+
+const WCHAR kDriveName[] = L"\\\\.\\PhysicalDrive%d";
+
+int PrintData()
+{
+ printf(kPrintHeader);
+ printf(kPrintData);
+
+ 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)
+ {
+ printf(kPrintDrive, i);
+
+ PrintPartitionInfo(hDrive, 0);
+
+ CloseHandle(hDrive);
+ }
+ }
+
+ return 2;
+}
+
+
+// ----------------------------------------------------------------------
+// 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
diff --git a/src/scrounge.h b/src/scrounge.h
new file mode 100644
index 0000000..dd908ea
--- /dev/null
+++ b/src/scrounge.h
@@ -0,0 +1,33 @@
+//
+// 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>
+//
+
+#ifndef __SCROUNGE_H__
+#define __SCROUNGE_H__
+
+
+BOOL ProcessMFTRecord(PartitionInfo* pInfo, uint64 mftRecord, HANDLE hIn);
+BOOL ScroungeMFTRecords(PartitionInfo* pInfo, HANDLE hIn);
+void PrintLastError();
+
+
+
+#endif //__SCROUNGE_H__ \ No newline at end of file
diff --git a/src/usuals.h b/src/usuals.h
new file mode 100644
index 0000000..4193675
--- /dev/null
+++ b/src/usuals.h
@@ -0,0 +1,45 @@
+//
+// 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>
+//
+
+#ifndef __USUALS_H__20010822
+#define __USUALS_H__20010822
+
+#include <debug.h>
+
+typedef unsigned __int64 uint64;
+typedef unsigned long uint32;
+typedef unsigned short uint16;
+typedef unsigned char byte;
+
+typedef signed __int64 int64;
+typedef signed long int32;
+typedef signed short int16;
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+
+#define HIGHDWORD(i64) (DWORD)((i64) >> 32)
+#define LOWDWORD(i64) (DWORD)((i64) & 0xFFFFFFFF)
+
+#endif //__USUALS_H__20010822
diff --git a/win32/win32.dsp b/win32/win32.dsp
new file mode 100644
index 0000000..dbb5213
--- /dev/null
+++ b/win32/win32.dsp
@@ -0,0 +1,147 @@
+# Microsoft Developer Studio Project File - Name="Scrounge" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=Scrounge - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "Scrounge.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "Scrounge.mak" CFG="Scrounge - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "Scrounge - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "Scrounge - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "Scrounge - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /D "UNICODE" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /opt:nowin98
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF "$(CFG)" == "Scrounge - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "UNICODE" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "Scrounge - Win32 Release"
+# Name "Scrounge - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\drive.cpp
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=.\memref.cpp
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=.\ntfs.cpp
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=.\ntfsx.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Scrounge.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.cpp
+# ADD CPP /Yc"stdafx.h"
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\drive.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ntfs.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ntfsx.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\scrounge.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/win32/win32.dsw b/win32/win32.dsw
new file mode 100644
index 0000000..b27fd20
--- /dev/null
+++ b/win32/win32.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Scrounge"=.\Scrounge.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+