diff options
author | Stef Walter <stef@thewalter.net> | 2004-04-01 04:35:55 +0000 |
---|---|---|
committer | Stef Walter <stef@thewalter.net> | 2004-04-01 04:35:55 +0000 |
commit | de5167a304b5e3b2db7462329334ac01d492d72c (patch) | |
tree | f091b1c7679c9120579875d17bbf7295a4ccbc9f /src | |
parent | 048987b4e95b70a4559b9163d90e57dd69097203 (diff) |
- Fixes all round
- Uncontiguous FAT
- Move to C (instead of C++)
- Preparing for porting
Diffstat (limited to 'src')
-rw-r--r-- | src/StdAfx.cpp | 29 | ||||
-rw-r--r-- | src/StdAfx.h | 44 | ||||
-rw-r--r-- | src/compat.c | 457 | ||||
-rw-r--r-- | src/compat.h | 185 | ||||
-rw-r--r-- | src/debug.h | 56 | ||||
-rw-r--r-- | src/drive.cpp | 137 | ||||
-rw-r--r-- | src/drive.h | 139 | ||||
-rw-r--r-- | src/list.c | 131 | ||||
-rw-r--r-- | src/locks.h | 61 | ||||
-rw-r--r-- | src/main.c | 222 | ||||
-rw-r--r-- | src/memref.h | 48 | ||||
-rw-r--r-- | src/misc.c | 152 | ||||
-rw-r--r-- | src/ntfs.c | 140 | ||||
-rw-r--r-- | src/ntfs.h | 341 | ||||
-rw-r--r-- | src/ntfsx.c | 605 | ||||
-rw-r--r-- | src/ntfsx.h | 209 | ||||
-rw-r--r-- | src/scrounge.c | 1002 | ||||
-rw-r--r-- | src/scrounge.h | 58 | ||||
-rw-r--r-- | src/search.c | 32 | ||||
-rw-r--r-- | src/usuals.h | 62 | ||||
-rw-r--r-- | src/win32.c | 78 |
21 files changed, 2533 insertions, 1655 deletions
diff --git a/src/StdAfx.cpp b/src/StdAfx.cpp deleted file mode 100644 index d939e46..0000000 --- a/src/StdAfx.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// -// 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 deleted file mode 100644 index 38f0a95..0000000 --- a/src/StdAfx.h +++ /dev/null @@ -1,44 +0,0 @@ -// -// 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/compat.c b/src/compat.c new file mode 100644 index 0000000..5ee1c0e --- /dev/null +++ b/src/compat.c @@ -0,0 +1,457 @@ +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +#include <sys/types.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include "compat.h" + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#ifndef HAVE_TOLOWER +int tolower(int c) +{ + if('A' <= c && c <= 'Z') + c += 'a' - 'A'; + return c; +} +#define HAVE_TOLOWER +#endif + + +#ifndef HAVE_STRDUP + +#ifndef HAVE_MALLOC +#error Need a working malloc. +#endif + +char* strdup(const char* str) +{ + char* dup = (char*)malloc((strlen(str) + 1) * sizeof(char)); + if(dup) + strcpy(dup, str); + return dup; +} +#define HAVE_STRDUP +#endif + + +#ifndef HAVE_STRNDUP +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif +char* strndup(const char* str, size_t cnt) +{ + char* buff = malloc(sizeof(char) * (cnt + 1)); + if(buff) + { + memcpy(buff, str, sizeof(char) * cnt); + buff[cnt] = 0; + } + return buff; +} +#endif + + +#ifndef HAVE_STRCASESTR +char* strcasestr(const char* buff, const char* str) +{ + const char* ptr = buff; + + while (*ptr != 0x00) + { + const char* one = ptr; + const char* two = str; + + while (tolower(*one) == tolower(*two)) + { + one++; + two++; + if (*two == 0x00) + return (char*) ptr; + } + ptr++; + } + return NULL; +} +#endif + +#ifndef HAVE_STRLCPY + +#ifndef HAVE_STRNCPY +#error neither strncpy or strlcpy found +#endif + +void strlcpy(char* dest, const char* src, size_t count) +{ + strncpy(dest, src, count); + dest[count - 1] = 0; +} +#endif + +#ifndef HAVE_STRLCAT + +#ifndef HAVE_STRNCAT +#error neither strncat or strlcat found +#endif + +void strlcat(char* dest, const char* src, size_t count) +{ + strncat(dest, src, count); + dest[count - 1] = 0; +} +#endif + + +#ifndef HAVE_VASPRINTF + +#ifndef HAVE_VSNPRINTF +#error neither vasprintf or vsnprintf found +#endif + +int vasprintf(char** ret, const char* format, va_list vl) +{ + size_t size = 32; + int c; + *ret = NULL; + + do + { + /* Double the buffer size for next time around */ + size += size; + + if(*ret) + free(*ret); + + *ret = (char*)malloc(sizeof(char) * size); + + if(!(*ret)) + { + errno = ENOMEM; + return -1; + } + + c = vsnprintf(*ret, size, format, vl); + } + while(c < 0); + + return c; +} + +#endif + + +/* + * We need to get a better check for this one. + * Basically have to make a variable __progname if none + * exist + */ + +#ifndef HAVE_UNISTD_H + +char* __progname = 0; +char prognamebuf[256]; + +void fixprogname() +{ + if(__progname == 0) + { + const char* beg = strrchr(_pgmptr, '\\'); + const char* temp = strrchr(_pgmptr, '/'); + beg = (beg > temp) ? beg : temp; + beg = (beg) ? beg + 1 : _pgmptr; + + temp = strrchr(_pgmptr, '.'); + temp = (temp > beg) ? temp : _pgmptr + strlen(_pgmptr); + + if((temp - beg) > 255) + temp = beg + 255; + + strncpy(prognamebuf, beg, temp - beg); + prognamebuf[temp - beg] = 0; + __progname = prognamebuf; + } +} +#endif + + + +#ifndef HAVE_GETOPT + +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +char* optarg; + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int getopt(int nargc, char* const* nargv, const char* ostr) +{ + static char* place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + + fixprogname(); + + if(optreset || !*place) /* update scanning pointer */ + { + optreset = 0; + if(optind >= nargc || *(place = nargv[optind]) != '-') + { + place = EMSG; + return (-1); + } + + if (place[1] && *++place == '-') /* found "--" */ + { + ++optind; + place = EMSG; + return (-1); + } + } /* option letter okay? */ + + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) + { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if(optopt == (int)'-') + return (-1); + if(!*place) + ++optind; + if(opterr && *ostr != ':' && optopt != BADCH) + (void)fprintf(stderr, "%s: illegal option -- %c\n", __progname, optopt); + return(BADCH); + } + if (*++oli != ':') /* don't need argument */ + { + optarg = NULL; + if(!*place) + ++optind; + } + else /* need an argument */ + { + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) /* no arg */ + { + place = EMSG; + if (*ostr == ':') + return (BADARG); + if(opterr) + (void)fprintf(stderr, "%s: option requires an argument -- %c\n", __progname, optopt); + return(BADCH); + } + else /* white space */ + optarg = nargv[optind]; + + place = EMSG; + ++optind; + } + return (optopt); /* dump back option letter */ +} +#endif + + +#ifndef HAVE_ERR_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +static FILE *err_file; /* file to use for error output */ +static void (*err_exit)(int); + +/* + * This is declared to take a `void *' so that the caller is not required + * to include <stdio.h> first. However, it is really a `FILE *', and the + * manual page documents it as such. + */ +void err_set_file(void *fp) +{ + if (fp) + err_file = fp; + else + err_file = stderr; +} + +void err_set_exit(void (*ef)(int)) +{ + err_exit = ef; +} + +void err(int eval, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + verrc(eval, errno, fmt, ap); + va_end(ap); +} + +void verr(int eval, const char *fmt, va_list ap) +{ + verrc(eval, errno, fmt, ap); +} + +void errc(int eval, int code, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + verrc(eval, code, fmt, ap); + va_end(ap); +} + +void verrc(int eval, int code, const char *fmt, va_list ap) +{ + fixprogname(); + if (err_file == 0) + err_set_file((FILE *)0); + fprintf(err_file, "%s: ", __progname); + if (fmt != NULL) { + vfprintf(err_file, fmt, ap); + fprintf(err_file, ": "); + } + fprintf(err_file, "%s\n", strerror(code)); + if (err_exit) + err_exit(eval); + exit(eval); +} + +void errx(int eval, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + verrx(eval, fmt, ap); + va_end(ap); +} + +void verrx(int eval, const char *fmt, va_list ap) +{ + fixprogname(); + if (err_file == 0) + err_set_file((FILE *)0); + fprintf(err_file, "%s: ", __progname); + if (fmt != NULL) + vfprintf(err_file, fmt, ap); + fprintf(err_file, "\n"); + if (err_exit) + err_exit(eval); + exit(eval); +} + +void warn(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vwarnc(errno, fmt, ap); + va_end(ap); +} + +void vwarn(const char *fmt, va_list ap) +{ + vwarnc(errno, fmt, ap); +} + +void warnc(int code, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vwarnc(code, fmt, ap); + va_end(ap); +} + +void vwarnc(int code, const char *fmt, va_list ap) +{ + fixprogname(); + if (err_file == 0) + err_set_file((FILE *)0); + fprintf(err_file, "%s: ", __progname); + if (fmt != NULL) + { + vfprintf(err_file, fmt, ap); + fprintf(err_file, ": "); + } + fprintf(err_file, "%s\n", strerror(code)); +} + +void warnx(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); +} + +void vwarnx(const char *fmt, va_list ap) +{ + fixprogname(); + if(err_file == 0) + err_set_file((FILE*)0); + fprintf(err_file, "%s: ", __progname); + if(fmt != NULL) + vfprintf(err_file, fmt, ap); + fprintf(err_file, "\n"); +} + +#endif + + + +#ifndef HAVE_STRLWR + +char* strlwr(char *string) +{ + char * cp; + for (cp=string; *cp; ++cp) + { + if('A' <= *cp && *cp <= 'Z') + *cp += 'a' - 'A'; + } + return(string); +} + +#endif + + + +#ifndef HAVE_REALLOCF + +void* reallocf(void* ptr, size_t size) +{ + void* ret = realloc(ptr, size); + + if(!ret && size) + free(ptr); + + return ret; +} + +#endif diff --git a/src/compat.h b/src/compat.h new file mode 100644 index 0000000..cc3b73e --- /dev/null +++ b/src/compat.h @@ -0,0 +1,185 @@ +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +#ifndef _COMPAT_H_ +#define _COMPAT_H_ + +/* Force use of win32 configuration if compiling there */ +#ifdef _WIN32 +#include "../config.win32.h" +#else +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif +#endif + +#ifndef MAX_PATH + #ifdef _MAX_PATH + #define MAX_PATH _MAX_PATH + #else + #define MAX_PATH 256 + #endif +#endif + +#ifndef HAVE_STDARG_H +#error ERROR: Must have a working <stdarg.h> header +#else +#include <stdarg.h> +#endif + + +#ifndef HAVE_STAT +#ifdef _WIN32 +#define S_IFDIR _S_IFDIR +#define HAVE_STAT +#else +#error ERROR: Must have a working 'stat' function +#endif +#endif + +#ifndef HAVE_GETCWD +#ifdef _WIN32 +#include <direct.h> +#define getcwd _getcwd +#define HAVE_GETCWD +#else +#error ERROR: Must have a working 'getcwd' function +#endif +#endif + +#ifndef HAVE_CHDIR +#ifdef _WIN32 +#include <direct.h> +#define chdir _chdir +#define HAVE_CHDIR +#else +#error ERROR: Must have a working 'chdir' function +#endif +#endif + +#ifndef HAVE_TOLOWER +int tolower(int c); +#endif + +#ifndef HAVE_STRDUP +char* strdup(const char* str); +#endif + +#ifndef HAVE_STRNDUP +char* strndup(const char* str, size_t cnt); +#endif + +#ifndef HAVE_STRCASESTR +char* strcasestr(const char* big, const char* little); +#endif + +#ifndef HAVE_STRCASECMP +#ifdef HAVE_STRICMP +#define strcasecmp stricmp +#else +#error ERROR: Must have either 'strcasecmp' or 'stricmp' +#endif +#endif + +#ifndef HAVE_STRCASECMP +#ifdef HAVE_STRICMP +#define strncasecmp strnicmp +#else +#error ERROR: Must have either 'strncasecmp' or 'strnicmp' +#endif +#endif + + +#ifndef NULL +#define NULL (void*)0 +#endif + +#ifndef __cplusplus +typedef unsigned char bool; +#define false 0x00 +#define true 0x01 +#endif + +#ifndef HAVE_BYTE +typedef unsigned char byte; +#define HAVE_BYTE +#endif + +#ifndef HAVE_UINT +typedef unsigned int uint; +#define HAVE_UINT +#endif + +#ifndef HAVE_STRLCPY +void strlcpy(char* dest, const char* src, size_t count); +#endif + +#ifndef HAVE_STRLCAT +void strlcat(char* dest, const char* src, size_t count); +#endif + +#ifndef HAVE_VSNPRINTF + +#ifdef _WIN32 +#define vsnprintf _vsnprintf +#define HAVE_VSNPRINTF +#else +#ifndef HAVE_VASPRINTF +#error ERROR: Must have a working 'vsnprintf' or 'vasprintf' function +#endif +#endif +#endif + +#ifndef HAVE_VASPRINTF +int vasprintf(char** ret, const char* format, va_list vl); +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifndef HAVE_GETOPT +extern char* optarg; +extern int optind, opterr, optopt; +int getopt(int nargc, char* const* nargv, const char* ostr); +#endif + +#ifndef HAVE_ERR_H +#include <stdarg.h> +void err_set_file(void *fp); +void err_set_exit(void (*ef)(int)); +void err(int eval, const char *fmt, ...); +void verr(int eval, const char *fmt, va_list ap); +void errc(int eval, int code, const char *fmt, ...); +void verrc(int eval, int code, const char *fmt, va_list ap); +void errx(int eval, const char *fmt, ...); +void verrx(int eval, const char *fmt, va_list ap); +void warn(const char *fmt, ...); +void vwarn(const char *fmt, va_list ap); +void warnc(int code, const char *fmt, ...); +void vwarnc(int code, const char *fmt, va_list ap); +void warnx(const char *fmt, ...); +void vwarnx(const char *fmt, va_list ap); +#endif + +#ifndef HAVE_REALLOCF +void* reallocf(void* ptr, size_t size); +#endif + +#endif diff --git a/src/debug.h b/src/debug.h new file mode 100644 index 0000000..e776fd1 --- /dev/null +++ b/src/debug.h @@ -0,0 +1,56 @@ +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +#ifdef _DEBUG + +#include <stdarg.h> + +#ifndef ASSERT + #ifdef ATLASSERT + #define ASSERT ATLASSERT + #else + #include <assert.h> + #define ASSERT assert + #endif +#endif + +#ifndef VERIFY +#define VERIFY(f) ASSERT(f) +#endif + +#ifndef DEBUG_ONLY +#define DEBUG_ONLY(f) (f) +#endif + + +#else /* !DEBUG */ + +#ifndef ASSERT +#define ASSERT(f) ((void)0) +#endif + +#ifndef VERIFY +#define VERIFY(f) ((void)(f)) +#endif + +#ifndef DEBUG_ONLY +#define DEBUG_ONLY(f) ((void)0) +#endif + +#endif /* _DEBUG */ diff --git a/src/drive.cpp b/src/drive.cpp deleted file mode 100644 index f485cf6..0000000 --- a/src/drive.cpp +++ /dev/null @@ -1,137 +0,0 @@ -// -// 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 index f0df9f1..4c52584 100644 --- a/src/drive.h +++ b/src/drive.h @@ -1,94 +1,89 @@ -// -// 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> -// - +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ #ifndef __DRIVE_H__20010822 #define __DRIVE_H__20010822 #include "usuals.h" -const uint16 kSectorSize = 0x200; -const uint64 kInvalidSector = 0xFFFFFFFFFFFFFFFF; -struct DriveLock; - -struct PartitionInfo -{ - uint32 firstSector; - uint32 lastSector; - uint32 offMFT; // In sectors - byte clusterSize; // In sectors - - DriveLock* pLocks; - uint32 cLocks; - uint32 curLock; -}; +#define kSectorSize 0x200 +#define kInvalidSector 0xFFFFFFFFFFFFFFFF -PartitionInfo* CreatePartitionInfo(); -void FreePartitionInfo(PartitionInfo* pInfo); +typedef struct _ntfsx_mftmap ntfsx_mftmap; +typedef struct _drivelocks drivelocks; +typedef struct _partitioninfo +{ + uint32 first; /* The first sector (in sectors) */ + uint32 end; /* The end sector (in sectors) */ + uint32 mft; /* Offset into the MFT (in sectors) */ + byte cluster; /* Cluster size (in sectors) */ + int device; /* A handle to an open device */ + + /* Some other context stuff about the drive */ + drivelocks* locks; + ntfsx_mftmap* mftmap; +} +partitioninfo; #pragma pack(push, drive) #pragma pack(1) -const byte kPartition_Invalid = 0; -const byte kPartition_Extended = 5; -const byte kPartition_ExtendedLBA = 15; +#define kPartition_Invalid 0 +#define kPartition_Extended 5 +#define kPartition_ExtendedLBA 15 -// Partition table entry -struct Drive_PartEntry +/* Partition table entry */ +typedef 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 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 */ +} +drive_partentry; + +#define kMBR_Sig 0xAA55 + +/* Master Boot Record */ +typedef struct _drive_mbr { - byte fill[0x1be]; // boot code - Drive_PartEntry partitions[4]; // partition table - uint16 sig; // 55AAh boot signature -}; + byte fill[0x1be]; /* boot code */ + drive_partentry partitions[4]; /* partition table */ + uint16 sig; /* 55AAh boot signature */ +} +drive_mbr; #pragma pack(pop, drive) -#define CLUSTER_TO_SECTOR(info, clus) (((clus) * (info).clusterSize) + (info).firstSector) +#define CLUSTER_TO_SECTOR(info, clus) (((clus) * (info).cluster) + (info).first) #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); - +#define CLUSTER_SIZE(info) ((info).cluster * kSectorSize) +/* driveName should be MAX_PATH chars long */ +void makeDriveName(char* driveName, int i); -#endif //__DRIVE_H__20010822
\ No newline at end of file +#endif /* __DRIVE_H__ */ diff --git a/src/list.c b/src/list.c new file mode 100644 index 0000000..28f2952 --- /dev/null +++ b/src/list.c @@ -0,0 +1,131 @@ +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +#include <io.h> +#include <fcntl.h> +#include <sys/types.h> +#include <string.h> +#include <stdio.h> + +#include "usuals.h" +#include "drive.h" +#include "ntfs.h" + +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"; + +int printNTFSInfo(int dd, uint64 tblSector) +{ + byte sector[kSectorSize]; + int64 pos; + size_t sz; + ntfs_bootsector* boot; + + pos = SECTOR_TO_BYTES(tblSector); + + /* PORT: Windows specific */ + if(_lseeki64(dd, pos, SEEK_SET) == -1) + err(1, "couldn't seek drive"); + + sz = read(dd, §or, kSectorSize); + if(sz == -1) + err(1, "couldn't read drive"); + + if(sz != kSectorSize) + errx(1, "unexpected end of drive"); + + boot = (ntfs_bootsector*)sector; + if(!memcmp(boot->sysId, kNTFS_SysId, sizeof(boot->sysId))) + printf(kPrintNTFSInfo, boot->secPerClus, boot->offMFT * boot->secPerClus); + + printf("\n"); + return 0; +} + +int printPartitionInfo(int dd, uint64 tblSector) +{ + drive_mbr mbr; + int64 pos; + size_t sz; + int i; + + ASSERT(sizeof(drive_mbr) == kSectorSize); + + pos = SECTOR_TO_BYTES(tblSector); + + /* PORT: Windows specific */ + if(_lseeki64(dd, pos, SEEK_SET) == -1) + err(1, "couldn't seek drive"); + + sz = read(dd, &mbr, sizeof(drive_mbr)); + if(sz == -1) + err(1, "couldn't read drive"); + + if(sz != sizeof(drive_mbr)) + errx(1, "unexpected end of drive"); + + if(mbr.sig == kMBR_Sig) + { + for(i = 0; i < 4; i++) + { + if(mbr.partitions[i].system == kPartition_Extended || + mbr.partitions[i].system == kPartition_ExtendedLBA) + { + printPartitionInfo(dd, 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(dd, tblSector + (uint64)mbr.partitions[i].startsec); + } + } + } + + return 0; +} + + +void scroungeList() +{ + char driveName[MAX_PATH]; + int dd = -1; + int i; + + printf(kPrintData); + + /* LIMIT: 256 Drives */ + for(i = 0; i < 0x100; i++) + { + makeDriveName(driveName, i); + + dd = open(driveName, _O_BINARY | _O_RDONLY); + if(dd != -1) + { + printf(kPrintDrive, i); + printPartitionInfo(dd, 0); + close(dd); + } + } +} diff --git a/src/locks.h b/src/locks.h index d60878c..afc0508 100644 --- a/src/locks.h +++ b/src/locks.h @@ -1,32 +1,41 @@ -// -// 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> -// +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ -#ifndef __LOCKS_H__20010828 -#define __LOCKS_H__20010828 +#ifndef __LOCKS_H__ +#define __LOCKS_H__ +#include "usuals.h" -#include "drive.h" +struct drivelock; +typedef struct _drivelocks +{ + struct drivelock* _locks; + uint32 _count; + uint32 _current; +} +drivelocks; +void addLocationLock(drivelocks* locks, uint64 beg, uint64 end); +bool checkLocationLock(drivelocks* locks, uint64 sec); +#ifdef _DEBUG +void dumpLocationLocks(drivelocks* locks); +#endif - -#endif //__LOCKS_H__20010828
\ No newline at end of file +#endif /* __LOCKS_H__ */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..39ff0c9 --- /dev/null +++ b/src/main.c @@ -0,0 +1,222 @@ +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +#include <io.h> +#include <fcntl.h> +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "scrounge.h" +#include "compat.h" + +const char kPrintHelp[] = "\ +usage: scrounge -l \n\ + List all drive partition information. \n\ + \n\ +usage: scrounge [-d drive] -s \n\ + Search drive for NTFS partitions. \n\ + \n\ +usage: scrounge [-d drive] [-m mftoffset] [-c clustersize] [-o outdir] start end \n\ + Scrounge data from a partition \n\ + -d Drive number \n\ + -m Offset to mft (in sectors) \n\ + -c Cluster size (in sectors, default of 8) \n\ + -o Directory to put scrounged files in \n\ + start First sector of partition \n\ + end Last sector of partition \n\ + \n\ +"; + +#define MODE_SCROUNGE 1 +#define MODE_LIST 2 +#define MODE_SEARCH 3 + +/* Forward decls */ +int usage(); + +int main(int argc, char* argv[]) +{ + int ch = 0; + int temp = 0; + int mode = 0; + int raw = 0; + int drive = 0; + partitioninfo pi; + char driveName[MAX_PATH]; + + memset(&pi, 0, sizeof(pi)); + + /* TODO: We need to be able to autodetect the cluster size */ + pi.cluster = 8; + + while((ch = getopt(argc, argv, "c:d:hlm:o:s")) != -1) + { + switch(ch) + { + + /* cluster size */ + case 'c': + { + temp = atoi(optarg); + + /* TODO: Check this range */ + if(temp <= 0 || temp > 128) + errx(2, "invalid cluster size (must be between 1 and 128)"); + + pi.cluster = temp; + mode = MODE_SCROUNGE; + } + break; + + /* drive number */ + case 'd': + { + temp = atoi(optarg); + + /* TODO: Check this range */ + if(temp < 0 || temp > 128) + errx(2, "invalid drive number (must be between 0 and 128)"); + + drive = temp; + } + break; + + /* help mode */ + case 'h': + usage(); + break; + + /* list mode */ + case 'l': + { + if(mode == MODE_SCROUNGE) + errx(2, "invalid -l argument in scrounge mode"); + + mode = MODE_LIST; + } + break; + + /* mft offset */ + case 'm': + { + temp = atoi(optarg); + + /* TODO: Check this range */ + if(temp < 0) + errx(2, "invalid mft offset (must be positive)"); + + pi.mft = temp; + mode = MODE_SCROUNGE; + } + break; + + /* output directory */ + case 'o': + if(chdir(optarg) == -1) + err(2, "couldn't change to output directory"); + break; + + /* search mode */ + case 's': + { + if(mode == MODE_SCROUNGE) + errx(2, "invalid -s argument in scrounge mode"); + + mode = MODE_SEARCH; + } + break; + + default: + ASSERT(false); + break; + } + + if(mode != MODE_SCROUNGE && mode != 0) + break; + } + + argc -= optind; + argv += optind; + + if(mode == MODE_SCROUNGE || mode == 0) + { + /* Get the sectors */ + + if(argc < 2) + errx(2, "must specify start and end sector of partition"); + + if(argc > 2) + warnx("ignoring extra arguments"); + + temp = atoi(argv[0]); + if(temp < 0) + errx(2, "invalid start sector (must be positive)"); + + pi.first = temp; + + temp = atoi(argv[1]); + if(temp < 0 || ((unsigned int)temp) <= pi.first) + errx(2, "invalid end sector (must be positive and greater than first)"); + + pi.end = temp; + + makeDriveName(driveName, drive); + + pi.device = open(driveName, _O_BINARY | _O_RDONLY); + if(pi.device == -1) + err(1, "couldn't open drive"); + + /* Use mft type search */ + if(pi.mft != 0) + { + scroungeUsingMFT(&pi); + } + + /* Otherwise it's a raw search */ + else + { + warn("Scrounging via raw search. Directory info will be discarded."); + scroungeUsingRaw(&pi); + } + } + + else + { + if(argc > 0) + warnx("ignoring extra arguments"); + + /* List partition and drive info */ + if(mode == MODE_LIST) + scroungeList(); + + /* Search for NTFS partitions */ + if(mode == MODE_SEARCH) + scroungeSearch(&pi); + } + + return 0; +} + +int usage() +{ + fprintf(stderr, "%s", kPrintHelp); + exit(2); +} diff --git a/src/memref.h b/src/memref.h index a26ac34..1138b63 100644 --- a/src/memref.h +++ b/src/memref.h @@ -1,28 +1,24 @@ -// -// 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 +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +#ifndef __MEMREF_H__ +#define __MEMREF_H__ #ifdef _DEBUG @@ -46,4 +42,4 @@ void _refrelease(void* pBuff); #endif -#endif //__MEMREF_H__20010827
\ No newline at end of file +#endif /* __MEMREF_H__ */ @@ -1,38 +1,119 @@ -// -// 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> -// +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +#include <malloc.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include "usuals.h" +#include "compat.h" +#include "memref.h" +#include "locks.h" -#include "memref.h" -#include "malloc.h" -#include "assert.h" +/* These locks are used to signify which */ +struct drivelock +{ + uint64 beg; + uint64 end; +}; + +void addLocationLock(drivelocks* locks, uint64 beg, uint64 end) +{ + uint32 i; + + if(locks->_count <= locks->_current) + { + locks->_count += 0x400; + locks->_locks = (struct drivelock*)realloc(locks->_locks, sizeof(struct drivelock) * locks->_count); + } -// WARNING!! Not thread safe or very efficient for large -// amounts of memory allocations + /* TODO: Implement a more efficient method here! */ + /* TODO: What happens when the above memory allocation fails? */ + if(locks->_locks) + { + /* Go through and check for a current lock we can tag onto */ + for(i = 0; i < locks->_current; i++) + { + if(INTERSECTS(locks->_locks[i].beg, locks->_locks[i].end, beg, end)) + { + locks->_locks[i].beg = min(locks->_locks[i].beg, beg); + locks->_locks[i].end = max(locks->_locks[i].end, end); + return; + } + } + + locks->_locks[locks->_current].beg = beg; + locks->_locks[locks->_current].end = end; + locks->_current++; + } +} + +bool checkLocationLock(drivelocks* locks, uint64 sec) +{ + uint32 i; + + if(locks->_locks) + { + /* Go through and check for a lock */ + for(i = 0; i < locks->_current; i++) + { + if(sec >= locks->_locks[i].beg && + sec < locks->_locks[i].end) + { + sec = locks->_locks[i].end; + return true; + } + } + } + + return false; +} + +#ifdef _DEBUG +void dumpLocationLocks(drivelocks* locks) +{ + uint32 i; + + for(i = 0; i < locks->_current; i++) + printf("%u\t%u\n", (uint32)locks->_locks[i].beg, (uint32)locks->_locks[i].end); + + printf("\n"); +} +#endif + + + + +/* + * WARNING!! Not thread safe or very efficient for large + * amounts of memory allocations + */ + +#ifdef _DEBUG const size_t kRefSig = 0x1F2F3F4F; void* _refalloc_dbg(size_t sz) { - // Allocate extra counter value before memory + /* Allocate extra counter value before memory */ size_t* pMem = (size_t*)malloc(sz * sizeof(size_t) * 2); if(pMem) @@ -44,10 +125,11 @@ void* _refalloc_dbg(size_t sz) return pMem; } +#endif void* _refalloc(size_t sz) { - // Allocate extra counter value before memory + /* Allocate extra counter value before memory */ size_t* pMem = (size_t*)malloc(sz * sizeof(size_t) * 1); if(pMem) @@ -59,11 +141,12 @@ void* _refalloc(size_t sz) return pMem; } +#ifdef _DEBUG void* _refadd_dbg(void* pBuff) { if(pBuff) { - // Increment the counter value + /* Increment the counter value */ size_t* pMem = (size_t*)pBuff - 2; assert(pMem[0] == kRefSig); pMem[1]++; @@ -71,21 +154,23 @@ void* _refadd_dbg(void* pBuff) return pBuff; } +#endif void* _refadd(void* pBuff) { if(pBuff) - // Increment the counter value + /* Increment the counter value */ ((size_t*)pBuff)[-1]++; return pBuff; } +#ifdef _DEBUG void _refrelease_dbg(void* pBuff) { if(pBuff) { - // Decrement the counter value + /* Decrement the counter value */ size_t* pMem = (size_t*)pBuff - 2; assert(pMem[0] == kRefSig); @@ -93,15 +178,16 @@ void _refrelease_dbg(void* pBuff) free(pMem); } } +#endif void _refrelease(void* pBuff) { if(pBuff) { - // Decrement the counter value + /* Decrement the counter value */ size_t* pMem = (size_t*)pBuff - 1; if(!--pMem[0]) free(pMem); } -}
\ No newline at end of file +} @@ -1,25 +1,21 @@ -// -// 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> -// - +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ #include "usuals.h" #include "ntfs.h" @@ -28,65 +24,66 @@ #include "string.h" -NTFS_AttribHeader* NTFS_SearchAttribute(byte* pLocation, uint32 attrType, void* pEnd, bool bSkip) +ntfs_attribheader* ntfs_searchattribute(byte* location, uint32 attrType, byte* end, bool skip) { - // Now we should be at attributes - while((!pEnd || (pLocation + sizeof(NTFS_AttribHeader) < pEnd)) && - *((uint32*)pLocation) != kNTFS_RecEnd) + /* Now we should be at attributes */ + while((!end || (location + sizeof(ntfs_attribheader) < end)) && + *((uint32*)location) != kNTFS_RecEnd) { - NTFS_AttribHeader* pAttrib = (NTFS_AttribHeader*)pLocation; + ntfs_attribheader* attrib = (ntfs_attribheader*)location; - if(!bSkip) + if(!skip) { - if(pAttrib->type == attrType) - return pAttrib; + if(attrib->type == attrType) + return attrib; } else - bSkip = false; + skip = false; - pLocation += pAttrib->cbAttribute; + location += attrib->cbAttribute; } return NULL; } -byte* NTFS_GetAttributeList(NTFS_RecordHeader* pRecord) +byte* ntfs_getattributelist(ntfs_recordheader* record) { - byte* pLocation = (byte*)pRecord; - ASSERT(pRecord->x_offUpdSeqArr != 0); - ASSERT(pRecord->x_offUpdSeqArr < 0x100); - pLocation += pRecord->x_offUpdSeqArr; - return pLocation; + byte* location = (byte*)record; + ASSERT(record->offAttrs != 0); + ASSERT(record->offAttrs < 0x100); + location += record->offAttrs; + return location; } -NTFS_AttribHeader* NTFS_FindAttribute(NTFS_RecordHeader* pRecord, uint32 attrType, void* pEnd) +ntfs_attribheader* ntfs_findattribute(ntfs_recordheader* record, uint32 attrType, byte* end) { - byte* pLocation = NTFS_GetAttributeList(pRecord); - - return NTFS_SearchAttribute(pLocation, attrType, pEnd, false); + byte* location = ntfs_getattributelist(record); + return ntfs_searchattribute(location, attrType, end, false); } -NTFS_AttribHeader* NTFS_NextAttribute(NTFS_AttribHeader* pAttrib, uint32 attrType, void* pEnd) +ntfs_attribheader* ntfs_nextattribute(ntfs_attribheader* attrib, uint32 attrType, byte* end) { - return NTFS_SearchAttribute((byte*)pAttrib, attrType, pEnd, true); + return ntfs_searchattribute((byte*)attrib, attrType, end, true); } -void* NTFS_GetAttributeData(NTFS_AttribResident* pAttrib, void* pEnd) +byte* ntfs_getattributedata(ntfs_attribresident* attrib, byte* end) { - void* pData = ((byte*)pAttrib) + pAttrib->offAttribData; - if(!pEnd && pData > pEnd) + byte* data = ((byte*)attrib) + attrib->offAttribData; + if(data > end) return NULL; - return pData; + return data; } -bool NTFS_IsBetterNameSpace(byte n1, byte n2) +bool ntfs_isbetternamespace(byte n1, byte n2) { - // We like our namespaces in this order - // 1. WIN32 - // 2. WIN32/DOS - // 3. DOS - // 4. POSIX + /* + * We like our namespaces in this order + * 1. WIN32 + * 2. WIN32/DOS + * 3. DOS + * 4. POSIX + */ if(n1 == kNTFS_NameSpacePOSIX) return true; @@ -100,30 +97,35 @@ bool NTFS_IsBetterNameSpace(byte n1, byte n2) return false; } -bool NTFS_DoFixups(byte* pCluster, uint32 cbCluster) +bool ntfs_dofixups(byte* cluster, uint32 size) { - ASSERT(cbCluster % kSectorSize == 0); - NTFS_RecordHeader* pRecord = (NTFS_RecordHeader*)pCluster; + ntfs_recordheader* record = (ntfs_recordheader*)cluster; + byte numSectors; + uint16* updSeq; + uint16* sectorFooter; + byte i; - byte numSectors = (byte)(cbCluster / kSectorSize); + ASSERT(size % kSectorSize == 0); + numSectors = (byte)(size / kSectorSize); - // Check the number of sectors against array - if(pRecord->cwUpdSeq - 1 < numSectors) - numSectors = pRecord->cwUpdSeq - 1; + /* Check the number of sectors against array */ + if(record->cwUpdSeq - 1 < numSectors) + numSectors = record->cwUpdSeq - 1; - uint16* pUpdSeq = (uint16*)(pCluster + pRecord->offUpdSeq); + updSeq = (uint16*)(cluster + record->offUpdSeq); - for(byte i = 0; i < numSectors; i++) + for(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]; + /* + * Check last 2 bytes in each sector against + * first double byte value in update sequence + */ + sectorFooter = (uint16*)((cluster + (kSectorSize - 2)) + (i * kSectorSize)); + if(*sectorFooter == updSeq[0]) + *sectorFooter = updSeq[i + 1]; else return false; } return true; } - @@ -1,24 +1,21 @@ -// -// 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> -// +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ #ifndef __NTFS_H__ #define __NTFS_H__ @@ -31,173 +28,167 @@ #pragma pack(1) -// WARNING Assumptions: -const uint32 kNTFS_RecordLen = 0x0400; +/* WARNING Assumptions: */ +#define kNTFS_RecordLen 0x0400 +#define kNTFS_SysId "NTFS " -const char kNTFS_SysId[] = "NTFS "; - -struct NTFS_BootSector +typedef 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 + 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 */ +} +ntfs_bootsector; + +#define kNTFS_RecMagic ((uint32)'ELIF') +#define kNTFS_RecEnd 0xFFFFFFFF +#define kNTFS_RecHeaderLen 0x30 + +#define kNTFS_RecFlagUse 0x01 +#define kNTFS_RecFlagDir 0x02 + +typedef 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 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 offAttrs; /* Offset to Attributes */ + 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 */ +} +ntfs_recordheader; + + +#define kNTFS_AttrCompressed 0x0001 +#define kNTFS_AttrEncrypted 0x0002 +#define kNTFS_AttrSparse 0x0004 + +typedef 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 + 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) */ +} +ntfs_attribheader; + +typedef struct _ntfs_attribresident { - NTFS_AttribHeader header; + 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 + uint32 cbAttribData; /* Length of the Attribute */ + uint16 offAttribData; /* Offset to the Attribute */ + byte bIndexed; /* Indexed flag */ + byte padding; /* 0x00 Padding */ +} +ntfs_attribresident; + +typedef struct _ntfs_attribnonresident { - NTFS_AttribHeader header; + 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 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) */ +} +ntfs_attribnonresident; + + +#define kNTFS_ATTRIBUTE_LIST 0x20 +#define kNTFS_FILENAME 0x30 +#define kNTFS_DATA 0x80 + + +#define kNTFS_FileReadOnly 0x0001 +#define kNTFS_FileHidden 0x0002 +#define kNTFS_FileSystem 0x0004 +#define kNTFS_FileArchive 0x0020 +#define kNTFS_FileDevice 0x0040 +#define kNTFS_FileNormal 0x0080 +#define kNTFS_FileTemorary 0x0100 +#define kNTFS_FileSparse 0x0200 +#define kNTFS_FileReparse 0x0400 +#define kNTFS_FileCompressed 0x0800 +#define kNTFS_FileOffline 0x1000 +#define kNTFS_FileNotIndexed 0x2000 +#define kNTFS_FileEncrypted 0x4000 + +#define kNTFS_NameSpacePOSIX 0x00 +#define kNTFS_NameSpaceWIN32 0x01 +#define kNTFS_NameSpaceDOS 0x02 +#define kNTFS_NameSpaceWINDOS 0x03 + +#define kNTFS_MFTName L"$MFT" + +typedef struct _ntfs_attribfilename { - 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 + 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 */ +} +ntfs_attribfilename; + +typedef struct _ntfs_attriblistrecord { - 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 -}; + 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 */ +} +ntfs_attriblistrecord; #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)) - - +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_getattributedata(ntfs_attribresident* attrib, byte* end); -// -// The record, attribute or whatever was invalid on disk -// -#define ERROR_NTFS_INVALID 10801L -#define ERROR_NTFS_NOTIMPLEMENT 10802L +bool ntfs_isbetternamespace(byte n1, byte n2); +bool ntfs_dofixups(byte* cluster, uint32 size); -#endif //__NTFS_H__
\ No newline at end of file +#endif /* __NTFS_H__ */ diff --git a/src/ntfsx.c b/src/ntfsx.c index b2f8a9f..8910f89 100644 --- a/src/ntfsx.c +++ b/src/ntfsx.c @@ -1,391 +1,492 @@ -// -// 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" +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +#include <io.h> +#include <stdio.h> +#include <malloc.h> +#include <string.h> + +#include "scrounge.h" +#include "memref.h" #include "ntfs.h" #include "ntfsx.h" -#include "scrounge.h" -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// +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; + } + + return dr; +} -bool NTFS_Cluster::New(PartitionInfo* pInfo) +void ntfsx_datarun_free(ntfsx_datarun* dr) { - Free(); + if(dr->_mem) + { + refrelease(dr->_mem); + dr->_mem = NULL; + } - m_cbCluster = CLUSTER_SIZE(*pInfo); + free(dr); +} - m_pCluster = (byte*)refalloc(m_cbCluster); - return m_pCluster != NULL; +bool ntfsx_datarun_first(ntfsx_datarun* dr) +{ + dr->_curpos = dr->_datarun; + dr->cluster = 0; + dr->length = 0; + dr->sparse = false; + return ntfsx_datarun_next(dr); } -bool NTFS_Cluster::Read(PartitionInfo* pInfo, uint64 begSector, HANDLE hIn) +bool ntfsx_datarun_next(ntfsx_datarun* dr) { - if(!m_pCluster) - { - if(!New(pInfo)) - return false; - } + byte length; + byte roffset; + int64 offset; - // 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(); + ASSERT(dr->_curpos); + + if(!*(dr->_curpos)) return false; - } - DWORD dwRead = 0; - if(!ReadFile(hIn, m_pCluster, m_cbCluster, &dwRead, NULL) || - dwRead != m_cbCluster) - { - Free(); - ::SetLastError(ERROR_READ_FAULT); + length = *(dr->_curpos) & 0x0F; + roffset = *(dr->_curpos) >> 4; + + /* ASSUMPTION: length and offset are less 64 bit numbers */ + if(length == 0 || length > 8 || roffset > 8) return false; + + ASSERT(length <= 8); + ASSERT(roffset <= 8); + + (dr->_curpos)++; + + memset(&(dr->length), 0, sizeof(uint64)); + + memcpy(&(dr->length), (dr->_curpos), length); + (dr->_curpos) += length; + + + /* Note that offset can be negative */ + if(*((dr->_curpos) + (roffset - 1)) & 0x80) + memset(&offset, ~0, sizeof(int64)); + else + memset(&offset, 0, sizeof(int64)); + + memcpy(&offset, (dr->_curpos), roffset); + (dr->_curpos) += roffset; + + if(offset == 0) + { + dr->sparse = true; + } + else + { + dr->sparse = false; + dr->cluster += offset; } - ::SetLastError(ERROR_SUCCESS); return true; } -void NTFS_Cluster::Free() -{ - if(m_pCluster) - refrelease(m_pCluster); - - m_pCluster = NULL; -} -NTFS_Record::NTFS_Record(PartitionInfo* pInfo) -{ - m_pInfo = (PartitionInfo*)refadd(pInfo); -} -NTFS_Record::~NTFS_Record() + + + +bool ntfsx_cluster_reserve(ntfsx_cluster* clus, partitioninfo* info) { - refrelease(m_pInfo); + 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 NTFS_Record::Read(uint64 begSector, HANDLE hIn) +bool ntfsx_cluster_read(ntfsx_cluster* clus, partitioninfo* info, uint64 begSector, int dd) { - if(!NTFS_Cluster::Read(m_pInfo, begSector, hIn)) - return false; + int64 pos; + size_t sz; - // 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; - } + if(!clus->data) + { + if(!ntfsx_cluster_reserve(clus, info)) + return false; + } + + pos = SECTOR_TO_BYTES(begSector); + if(_lseeki64(dd, pos, SEEK_SET) == -1) + return false; + + sz = read(dd, clus->data, clus->size); + if(sz == -1) + return false; + + if(sz != clus->size) + { + errno = ERANGE; + return false; + } return true; } -NTFS_Attribute* NTFS_Record::FindAttribute(uint32 attrType, HANDLE hIn) +void ntfsx_cluster_release(ntfsx_cluster* clus) { - NTFS_Attribute* pAttribute = NULL; + if(clus->data) + refrelease(clus->data); - // Make sure we have a valid record - ASSERT(GetHeader()); - NTFS_AttribHeader* pHeader = NTFS_FindAttribute(GetHeader(), attrType, (m_pCluster + m_cbCluster)); + clus->data = NULL; + clus->size = 0; +} - 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; - } +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; + } - } + return attr; +} - pAttr = (NTFS_AttrListRecord*)((byte*)pAttr + pAttr->cbRecord); - } - } - } +void ntfsx_attribute_free(ntfsx_attribute* attr) +{ + if(attr->_mem) + { + refrelease(attr->_mem); + attr->_mem = NULL; + } - return pAttribute; + free(attr); } -bool NTFS_Attribute::NextAttribute(uint32 attrType) +ntfs_attribheader* ntfsx_attribute_header(ntfsx_attribute* attr) { - NTFS_AttribHeader* pHeader = NTFS_NextAttribute(GetHeader(), attrType, m_pMem + m_cbMem); - if(pHeader) - { - m_pHeader = pHeader; - return true; - } - - return false; + return attr->_header; } -NTFS_Attribute::NTFS_Attribute(NTFS_Cluster& clus, NTFS_AttribHeader* pHeader) +void* ntfsx_attribute_getresidentdata(ntfsx_attribute* attr) { - m_pHeader = pHeader; - m_pMem = clus.m_pCluster; - m_cbMem = clus.m_cbCluster; - refadd(m_pMem); + ntfs_attribresident* res = (ntfs_attribresident*)attr->_header; + ASSERT(!attr->_header->bNonResident); + return (byte*)(attr->_header) + res->offAttribData; } -void* NTFS_Attribute::GetResidentData() +uint32 ntfsx_attribute_getresidentsize(ntfsx_attribute* attr) { - ASSERT(!m_pHeader->bNonResident); - NTFS_AttribResident* pRes = (NTFS_AttribResident*)m_pHeader; - return (byte*)m_pHeader + pRes->offAttribData; + ntfs_attribresident* res = (ntfs_attribresident*)attr->_header; + ASSERT(!attr->_header->bNonResident); + return res->cbAttribData; } -uint32 NTFS_Attribute::GetResidentSize() +ntfsx_datarun* ntfsx_attribute_getdatarun(ntfsx_attribute* attr) { - ASSERT(!m_pHeader->bNonResident); - NTFS_AttribResident* pRes = (NTFS_AttribResident*)m_pHeader; - return pRes->cbAttribData; + ntfs_attribnonresident* nonres = (ntfs_attribnonresident*)attr->_header; + ASSERT(attr->_header->bNonResident); + return ntfsx_datarun_alloc(attr->_mem, (byte*)(attr->_header) + nonres->offDataRuns); } -NTFS_DataRun* NTFS_Attribute::GetDataRun() +bool ntfsx_attribute_next(ntfsx_attribute* attr, uint32 attrType) { - ASSERT(m_pHeader->bNonResident); - NTFS_AttribNonResident* pNonRes = (NTFS_AttribNonResident*)m_pHeader; + ntfs_attribheader* header = ntfs_nextattribute(attr->_header, attrType, + attr->_mem + attr->_length); + if(header) + { + attr->_header = header; + return true; + } - return new NTFS_DataRun(m_pMem, (byte*)m_pHeader + pNonRes->offDataRuns); + return false; } -NTFS_DataRun::NTFS_DataRun(byte* pClus, byte* pDataRun) + +ntfsx_record* ntfsx_record_alloc(partitioninfo* info) { - ASSERT(pDataRun); - m_pMem = (byte*)refadd(pClus); - m_pDataRun = pDataRun; - m_pCurPos = NULL; - m_firstCluster = m_numClusters = 0; - m_bSparse = false; + ntfsx_record* rec = (ntfsx_record*)malloc(sizeof(ntfsx_record)); + if(rec) + { + rec->info = info; + memset(&(rec->_clus), 0, sizeof(ntfsx_cluster)); + } + + return rec; } -bool NTFS_DataRun::First() +void ntfsx_record_free(ntfsx_record* record) { - m_pCurPos = m_pDataRun; - m_firstCluster = m_numClusters = 0; - m_bSparse = false; - return Next(); + ntfsx_cluster_release(&(record->_clus)); + free(record); } -bool NTFS_DataRun::Next() +bool ntfsx_record_read(ntfsx_record* record, uint64 begSector, int dd) { - ASSERT(m_pCurPos); + ntfs_recordheader* rechead; - if(!*m_pCurPos) - return false; + if(!ntfsx_cluster_read(&(record->_clus), record->info, begSector, dd)) + err(1, "couldn't read mft record from drive"); - byte cbLen = *m_pCurPos & 0x0F; - byte cbOff = *m_pCurPos >> 4; + /* Check and validate this record */ + rechead = ntfsx_record_header(record); + if(rechead->magic != kNTFS_RecMagic || + !ntfs_dofixups(record->_clus.data, record->_clus.size)) + { + warnx("invalid mft record"); + ntfsx_cluster_release(&(record->_clus)); + return false; + } - // ASSUMPTION length and offset are less 64 bit numbers - if(cbLen == 0 || cbLen > 8 || cbOff > 8) - return false; - - ASSERT(cbLen <= 8); - ASSERT(cbOff <= 8); + return true; +} - m_pCurPos++; +ntfs_recordheader* ntfsx_record_header(ntfsx_record* record) +{ + return (ntfs_recordheader*)(record->_clus.data); +} - memset(&m_numClusters, 0, sizeof(uint64)); +ntfsx_attribute* ntfsx_record_findattribute(ntfsx_record* record, uint32 attrType, int dd) +{ + 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)); - memcpy(&m_numClusters, m_pCurPos, cbLen); - m_pCurPos += cbLen; + /* 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); - int64 offset; + /* 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 & 0xFFFFFFFFFFFF); - // Note that offset can be negative - if(*(m_pCurPos + (cbOff - 1)) & 0x80) - memset(&offset, ~0, sizeof(int64)); - else - memset(&offset, 0, sizeof(int64)); + r2 = ntfsx_record_alloc(record->info); + if(!r2) + return NULL; - memcpy(&offset, m_pCurPos, cbOff); - m_pCurPos += cbOff; + if(ntfsx_record_read(r2, mftRecord, dd)) + attr = ntfsx_record_findattribute(r2, attrType, dd); - if(offset == 0) - { - m_bSparse = true; - } - else - { - m_bSparse = false; - m_firstCluster += offset; + ntfsx_record_free(r2); + + if(attr) + break; + } + + atlr = (ntfs_attriblistrecord*)((byte*)atlr + atlr->cbRecord); + } + } } - return true; + return attr; } -NTFS_MFTMap::NTFS_MFTMap(PartitionInfo* pInfo) + + + +struct _ntfsx_mftmap_block { - m_pInfo = (PartitionInfo*)refadd(pInfo); - m_pBlocks = NULL; - m_count = 0; -} + uint64 firstSector; /* relative to the entire drive */ + uint64 length; /* length in MFT records */ +}; -NTFS_MFTMap::~NTFS_MFTMap() +void ntfsx_mftmap_init(ntfsx_mftmap* map, partitioninfo* info) { - refrelease(m_pInfo); + map->info = info; + map->_blocks = NULL; + map->_count = 0; +} - if(m_pBlocks) - free(m_pBlocks); +void ntfsx_mftmap_destroy(ntfsx_mftmap* map) +{ + if(map->_blocks) + { + free(map->_blocks); + map->_blocks = NULL; + map->_count = 0; + } } -bool NTFS_MFTMap::Load(NTFS_Record* pRecord, HANDLE hIn) +bool ntfsx_mftmap_load(ntfsx_mftmap* map, ntfsx_record* record, int dd) { - bool bRet = TRUE; - NTFS_Attribute* pAttribData = NULL; // Data Attribute - NTFS_DataRun* pDataRun = NULL; // Data runs for nonresident data + bool ret = true; + ntfsx_attribute* attribdata = NULL; /* Data Attribute */ + ntfsx_datarun* datarun = NULL; /* Data runs for nonresident data */ { - printf("[Processing MFT] "); + ntfs_attribheader* header; + ntfs_attribnonresident* nonres; + uint64 length; + uint64 firstSector; + uint32 allocated; - // TODO: Check here whether MFT has already been loaded + /* TODO: Check here whether MFT has already been loaded */ - // Get the MFT's data - pAttribData = pRecord->FindAttribute(kNTFS_DATA, hIn); - if(!pAttribData) RET_ERROR(ERROR_NTFS_INVALID); + /* Get the MFT's data */ + attribdata = ntfsx_record_findattribute(record, kNTFS_DATA, dd); + if(!attribdata) + RETWARNBX("invalid mft. no data attribute"); - if(!pAttribData->GetHeader()->bNonResident) - RET_ERROR(ERROR_NTFS_INVALID); + header = ntfsx_attribute_header(attribdata); + if(!header->bNonResident) + RETWARNBX("invalid mft. data attribute non-resident"); - pDataRun = pAttribData->GetDataRun(); - if(!pDataRun) - RET_ERROR(ERROR_NTFS_INVALID); + datarun = ntfsx_attribute_getdatarun(attribdata); + if(!datarun) + RETWARNBX("invalid mft. no data runs"); - NTFS_AttribNonResident* pNonRes = (NTFS_AttribNonResident*)pAttribData->GetHeader(); + nonres = (ntfs_attribnonresident*)header; - if(m_pBlocks) + if(map->_blocks) { - free(m_pBlocks); - m_pBlocks = NULL; + free(map->_blocks); + map->_blocks = NULL; } - m_count = 0; - uint32 allocated = 0; + map->_count = 0; + allocated = 0; - // Now loop through the data run - if(pDataRun->First()) + /* Now loop through the data run */ + if(ntfsx_datarun_first(datarun)) { do { - if(pDataRun->m_bSparse) - RET_ERROR(ERROR_NTFS_INVALID); + if(datarun->sparse) + RETWARNBX("invalid mft. sparse data runs"); - if(m_count >= allocated) + if(map->_count >= allocated) { allocated += 16; - m_pBlocks = (NTFS_Block*)realloc(m_pBlocks, allocated * sizeof(NTFS_Block)); - if(!m_pBlocks) - RET_FATAL(ERROR_NOT_ENOUGH_MEMORY); + map->_blocks = (struct _ntfsx_mftmap_block*)realloc(map->_blocks, + allocated * sizeof(struct _ntfsx_mftmap_block)); + if(!(map->_blocks)) + errx(1, "out of memory"); } - uint64 length = pDataRun->m_numClusters * ((m_pInfo->clusterSize * kSectorSize) / kNTFS_RecordLen); + ASSERT(map->info->cluster != 0); + + length = datarun->length * ((map->info->cluster * kSectorSize) / kNTFS_RecordLen); if(length == 0) continue; - uint64 firstSector = (pDataRun->m_firstCluster * m_pInfo->clusterSize) + m_pInfo->firstSector; - if(firstSector >= m_pInfo->lastSector) + firstSector = (datarun->cluster * map->info->cluster) + map->info->first; + if(firstSector >= map->info->end) continue; - m_pBlocks[m_count].length = length; - m_pBlocks[m_count].firstSector = firstSector; - m_count++; + map->_blocks[map->_count].length = length; + map->_blocks[map->_count].firstSector = firstSector; + map->_count++; } - while(pDataRun->Next()); + while(ntfsx_datarun_next(datarun)); } - bRet = true; - ::SetLastError(ERROR_SUCCESS); + ret = true; } -clean_up: +cleanup: - if(pAttribData) - delete pAttribData; - if(pDataRun) - delete pDataRun; + if(attribdata) + ntfsx_attribute_free(attribdata); + if(datarun) + ntfsx_datarun_free(datarun); - return bRet; + return ret; } -uint64 NTFS_MFTMap::GetLength() +uint64 ntfsx_mftmap_length(ntfsx_mftmap* map) { uint64 length = 0; + uint32 i; - for(uint32 i = 0; i < m_count; i++) - length += m_pBlocks[i].length; + for(i = 0; i < map->_count; i++) + length += map->_blocks[i].length; return length; } -uint64 NTFS_MFTMap::SectorForIndex(uint64 index) +uint64 ntfsx_mftmap_sectorforindex(ntfsx_mftmap* map, uint64 index) { - for(uint32 i = 0; i < m_count; i++) + uint32 i; + struct _ntfsx_mftmap_block* p; + uint64 sector; + + for(i = 0; i < map->_count; i++) { - NTFS_Block* p = m_pBlocks + i; + p = map->_blocks + i; - if(index > p->length) + if(index >= p->length) { index -= p->length; } else { - uint64 sector = index * (kNTFS_RecordLen / kSectorSize); + sector = index * (kNTFS_RecordLen / kSectorSize); sector += p->firstSector; - if(sector > m_pInfo->lastSector) + if(sector >= map->info->end) return kInvalidSector; return sector; diff --git a/src/ntfsx.h b/src/ntfsx.h index 836f010..04ebabd 100644 --- a/src/ntfsx.h +++ b/src/ntfsx.h @@ -1,139 +1,112 @@ -// -// 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 - +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +#ifndef __NTFSX_H__ +#define __NTFSX_H__ + +#include "drive.h" #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 +/* used as a heap based object */ +typedef struct _ntfsx_datarun { -public: - NTFS_Cluster() - { m_pCluster = NULL; }; - ~NTFS_Cluster() - { Free(); } + byte* _mem; /* ref counted */ + byte* _datarun; + byte* _curpos; - bool New(PartitionInfo* pInfo); - bool Read(PartitionInfo* pInfo, uint64 begSector, HANDLE hIn); - void Free(); + bool sparse; + uint64 cluster; + uint64 length; +} +ntfsx_datarun; - - uint32 m_cbCluster; - byte* m_pCluster; -}; - -class NTFS_Attribute -{ -public: - NTFS_Attribute(NTFS_Cluster& clus, NTFS_AttribHeader* pHeader); - ~NTFS_Attribute() - { refrelease(m_pMem); } +ntfsx_datarun* ntfsx_datarun_alloc(byte* mem, byte* datarun); +void ntfsx_datarun_free(ntfsx_datarun* dr); +bool ntfsx_datarun_first(ntfsx_datarun* dr); +bool ntfsx_datarun_next(ntfsx_datarun* dr); - NTFS_AttribHeader* GetHeader() - { return m_pHeader; } - void* GetResidentData(); - uint32 GetResidentSize(); - - NTFS_DataRun* GetDataRun(); - - bool NextAttribute(uint32 attrType); +/* used as a stack based object */ +typedef struct _ntfsx_cluster +{ + uint32 size; + byte* data; /* ref counted */ +} +ntfsx_cluster; + +bool 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); + -protected: - NTFS_AttribHeader* m_pHeader; - byte* m_pMem; - uint32 m_cbMem; -}; -class NTFS_Record : - public NTFS_Cluster +/* used as a heap based object */ +typedef struct _ntfsx_attribute { -public: - NTFS_Record(PartitionInfo* pInfo); - ~NTFS_Record(); + ntfs_attribheader* _header; + byte* _mem; /* ref counted */ + uint32 _length; +} +ntfsx_attribute; - bool Read(uint64 begSector, HANDLE hIn); - NTFS_RecordHeader* GetHeader() - { return (NTFS_RecordHeader*)m_pCluster; } +ntfsx_attribute* ntfsx_attribute_alloc(ntfsx_cluster* clus, ntfs_attribheader* header); +void ntfsx_attribute_free(ntfsx_attribute* attr); +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); - NTFS_Attribute* FindAttribute(uint32 attrType, HANDLE hIn); -protected: - PartitionInfo* m_pInfo; -}; -class NTFS_MFTMap +/* used as a heap based object */ +typedef struct _ntfsx_record { -public: - NTFS_MFTMap(PartitionInfo* pInfo); - ~NTFS_MFTMap(); + partitioninfo* info; + ntfsx_cluster _clus; +} +ntfsx_record; - bool Load(NTFS_Record* pRecord, HANDLE hIn); +ntfsx_record* ntfsx_record_alloc(partitioninfo* info); +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); - uint64 GetLength(); - uint64 SectorForIndex(uint64 index); -protected: - PartitionInfo* m_pInfo; - struct NTFS_Block - { - uint64 firstSector; // relative to the entire drive - uint64 length; // length in MFT records - }; - - NTFS_Block* m_pBlocks; - uint32 m_count; -}; - -#endif // !defined(AFX_NTFSX__9363C7D2_D3CC_4D49_BEE0_27AD025670F2__INCLUDED_) +/* used as a stack based object */ +struct _ntfsx_mftmap_block; +typedef struct _ntfsx_mftmap +{ + partitioninfo* info; + struct _ntfsx_mftmap_block* _blocks; + uint32 _count; +} +ntfsx_mftmap; + +void ntfsx_mftmap_init(ntfsx_mftmap* map,partitioninfo* info); +void ntfsx_mftmap_destroy(ntfsx_mftmap* map); +bool ntfsx_mftmap_load(ntfsx_mftmap* map, ntfsx_record* record, int dd); +uint64 ntfsx_mftmap_length(ntfsx_mftmap* map); +uint64 ntfsx_mftmap_sectorforindex(ntfsx_mftmap* map, uint64 index); + +#endif diff --git a/src/scrounge.c b/src/scrounge.c index 2cb0d71..f4436de 100644 --- a/src/scrounge.c +++ b/src/scrounge.c @@ -1,717 +1,487 @@ -// -// AUTHOR -// N. Nielsen -// -// VERSION -// 0.7 -// -// LICENSE -// This software is in the public domain. -// -// The software is provided "as is", without warranty of any kind, -// express or implied, including but not limited to the warranties -// of merchantability, fitness for a particular purpose, and -// noninfringement. In no event shall the author(s) be liable for any -// claim, damages, or other liability, whether in an action of -// contract, tort, or otherwise, arising from, out of, or in connection -// with the software or the use or other dealings in the software. -// -// SUPPORT -// Send bug reports to: <nielsen@memberwebs.com> -// - -// Scrounge.cpp -// - -#include "stdafx.h" +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +#include <io.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "scrounge.h" #include "ntfs.h" #include "ntfsx.h" -#include "usuals.h" -#include "drive.h" #include "locks.h" -#include "scrounge.h" -// ---------------------------------------------------------------------- -// Process a potential MFT Record. For directories create the directory -// and for files write out the file. -// -// Current Directory is the output directory -// hIn is an open drive handle -// pInfo is partition info about hIn (needs to be a ref counted pointer) - -BOOL ProcessMFTRecord(PartitionInfo* pInfo, uint64 sector, NTFS_MFTMap* map, HANDLE hIn) +typedef struct _filebasics { - // Declare data that needs cleaning up first - BOOL bRet = TRUE; // Return value - HANDLE hFile = NULL; // Output file handle - NTFS_Attribute* pAttribName = NULL; // Filename Attribute - NTFS_Attribute* pAttribData = NULL; // Data Attribute - NTFS_DataRun* pDataRun = NULL; // Data runs for nonresident data - - ASSERT(sector != kInvalidSector); - - - // Tracks whether or not we output a file - bool bFile = false; - - { - // Read the MFT record - NTFS_Record record(pInfo); - if(!record.Read(sector, hIn)) - PASS_ERROR(); - - NTFS_RecordHeader* pRecord = record.GetHeader(); - - - // Check if this record is in use - if(!(pRecord->flags & kNTFS_RecFlagUse)) - RET_ERROR(ERROR_SUCCESS); - + wchar_t filename[MAX_PATH + 1]; + uint64 created; + uint64 modified; + uint64 accessed; + uint32 flags; + uint64 parent; +} +filebasics; - // Info that we use later - WCHAR fileName[MAX_PATH + 1]; - FILETIME ftCreated; - FILETIME ftModified; - FILETIME ftAccessed; - DWORD fileAttrib = 0; - uint64 mftParent = kInvalidSector; - byte* pResidentData = NULL; +void processRecordFileBasics(partitioninfo* pi, ntfsx_record* record, filebasics* basics) +{ + /* Data Attribute */ + ntfsx_attribute* attr = NULL; + { + byte* resident = NULL; + ntfs_attribfilename* filename; + byte nameSpace; - // Now get the name and info... - pAttribName = record.FindAttribute(kNTFS_FILENAME, hIn); - if(!pAttribName) RET_ERROR(ERROR_SUCCESS); + ASSERT(record); + memset(basics, 0, sizeof(filebasics)); + basics->parent = kInvalidSector; + /* Now get the name and info... */ + attr = ntfsx_record_findattribute(record, kNTFS_FILENAME, pi->device); + if(!attr) goto cleanup; - byte nameSpace = kNTFS_NameSpacePOSIX; - memset(fileName, 0, sizeof(fileName)); + nameSpace = kNTFS_NameSpacePOSIX; + memset(basics->filename, 0, sizeof(basics->filename)); do { - // TODO ASSUMPTION: File name is always resident - ASSERT(!pAttribName->GetHeader()->bNonResident); + /* TODO ASSUMPTION: File name is always resident */ + ASSERT(!ntfsx_attribute_header(attr)->bNonResident); - // Get out all the info we need - NTFS_AttrFileName* pFileName = (NTFS_AttrFileName*)pAttribName->GetResidentData(); + /* Get out all the info we need */ + filename = (ntfs_attribfilename*)ntfsx_attribute_getresidentdata(attr); + ASSERT(filename); - // There can be multiple filenames with different namespaces - // so choose the best one - if(NTFS_IsBetterNameSpace(nameSpace, pFileName->nameSpace)) + /* + * There can be multiple filenames with different + * namespaces so choose the best one + */ + if(ntfs_isbetternamespace(nameSpace, filename->nameSpace)) { - // Dates - NTFS_MakeFileTime(pFileName->timeCreated, ftCreated); - NTFS_MakeFileTime(pFileName->timeModified, ftModified); - NTFS_MakeFileTime(pFileName->timeRead, ftAccessed); - - // File Name - wcsncpy(fileName, (wchar_t*)(((byte*)pFileName) + sizeof(NTFS_AttrFileName)), pFileName->cFileName); - fileName[pFileName->cFileName] = 0; - - // Attributes - if(pFileName->flags & kNTFS_FileReadOnly) - fileAttrib |= FILE_ATTRIBUTE_READONLY; - if(pFileName->flags & kNTFS_FileHidden) - fileAttrib |= FILE_ATTRIBUTE_HIDDEN; - if(pFileName->flags & kNTFS_FileArchive) - fileAttrib |= FILE_ATTRIBUTE_ARCHIVE; - if(pFileName->flags & kNTFS_FileSystem) - fileAttrib |= FILE_ATTRIBUTE_SYSTEM; - - // Parent Directory - if(map) - mftParent = map->SectorForIndex(pFileName->refParent & 0xFFFFFFFFFFFF); - - // Namespace - nameSpace = pFileName->nameSpace; + /* Dates */ + basics->created = filename->timeCreated; + basics->modified = filename->timeModified; + basics->accessed = filename->timeRead; + + /* File Name */ + wcsncpy(basics->filename, (wchar_t*)(((byte*)filename) + sizeof(ntfs_attribfilename)), filename->cFileName); + basics->filename[filename->cFileName] = 0; + + /* Attributes */ + basics->flags = filename->flags; + + /* Parent Directory */ + basics->parent = filename->refParent & 0xFFFFFFFFFFFF; + + /* Namespace */ + nameSpace = filename->nameSpace; } } - while(pAttribName->NextAttribute(kNTFS_FILENAME)); + while(ntfsx_attribute_next(attr, kNTFS_FILENAME)); + } +cleanup: + if(attr) + ntfsx_attribute_free(attr); +} - // Check if we got a file name - if(fileName[0] == 0) - RET_ERROR(ERROR_NTFS_INVALID); +void processMFTRecord(partitioninfo* pi, uint64 sector, int level) +{ + ntfsx_record* record = NULL; + ntfsx_attribute* attribdata = NULL; + ntfsx_datarun* datarun = NULL; + int outfile = -1; + ntfsx_cluster cluster; + memset(&cluster, 0, sizeof(cluster)); - // Check if it's the root - // If so then bumm out cuz we don't want to have anything to do with it - if(sector == mftParent || // Root is it's own parent - !wcscmp(fileName, L".")) // Or is called '.' - RET_ERROR(ERROR_SUCCESS); + { + filebasics basics; + ntfs_recordheader* header; + uint64 parentSector; + uint64 dataSector; + uint16 rename = 0; + uint64 fileSize; + uint32 i; + uint32 num; + wchar_t filename2[MAX_PATH + 1]; + ntfs_attribheader* attrhead; + ntfs_attribnonresident* nonres; + + ASSERT(sector != kInvalidSector); + + record = ntfsx_record_alloc(pi); + if(!record) + errx(1, "out of memory"); + + /* Read the MFT record */ + if(!ntfsx_record_read(record, sector, pi->device)) + RETURN; + + header = ntfsx_record_header(record); + ASSERT(header); + + if(!(header->flags & kNTFS_RecFlagUse)) + RETURN; + + /* Try and get a file name out of the header */ + processRecordFileBasics(pi, record, &basics); + + if(basics.filename[0] == 0) + { + RETWARNX("invalid mft record. in use, but no filename"); + } + /* If it's the root folder then return */ + if(!wcscmp(basics.filename, L".")) + RETURN; - // If it's the MFT then we read that in - if(!wcscmp(fileName, kNTFS_MFTName)) + /* Process parent folders if available */ + if(basics.parent != kInvalidSector) { - if(map) + /* Only if we have MFT map info available */ + if(pi->mftmap) { - printf("[Processing MFT] "); + parentSector = ntfsx_mftmap_sectorforindex(pi->mftmap, basics.parent); - // We found the MFT, let's load it up - if(!map->Load(&record, hIn)) - PASS_FATAL(); + if(parentSector == kInvalidSector) + warnx("invalid parent directory for file: %S", basics.filename); + else + processMFTRecord(pi, parentSector, level + 1); } - - RET_ERROR(ERROR_SUCCESS); } + printf(level == 0 ? "\\%S\n" : "\\%S", basics.filename); - if(mftParent != kInvalidSector) + /* Directory handling: */ + if(header->flags & kNTFS_RecFlagDir) { - // Create Parent folders - if(!ProcessMFTRecord(pInfo, mftParent, map, hIn)) - PASS_FATAL(); + /* Try to change to the directory */ + /* PORT: Wide character file functions */ + if(_wchdir(basics.filename) == -1) + { + /* PORT: Wide character file functions */ + if(_wmkdir(basics.filename) == -1) + { + warnx("couldn't create directory '%S' putting files in parent directory", basics.filename); + } + else + { + setFileAttributes(basics.filename, basics.flags); + _wchdir(basics.filename); + } + } + + RETURN; } - // If it's a folder then create it - if(pRecord->flags & kNTFS_RecFlagDir) - { - // Try to change to dir - if(!SetCurrentDirectoryW(fileName)) - { - // Otherwise create dir - if(CreateDirectoryW(fileName, NULL)) - { - // And set attributes - SetFileAttributesW(fileName, fileAttrib); - SetCurrentDirectoryW(fileName); - } - } + /* Normal file handling: */ + /* PORT: Wide character file functions */ + outfile = _wopen(basics.filename, _O_BINARY | _O_CREAT | _O_EXCL | _O_WRONLY); + + wcsncpy(filename2, basics.filename, MAX_PATH); + filename2[MAX_PATH] = 0; - wprintf(L"\\%s", fileName); - } + while(outfile == -1 && errno == EEXIST && rename < 0x1000) + { + if(wcslen(basics.filename) + 7 >= MAX_PATH) + { + warnx("file name too long on duplicate file: %S", basics.filename); + goto cleanup; + } + wcscpy(basics.filename, filename2); + wcscat(basics.filename, L"."); - // Otherwise write the file data - else - { - // Write to the File - hFile = CreateFileW(fileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL); + _itow(rename, basics.filename + wcslen(basics.filename), 10); + rename++; - uint16 nRename = 0; - wchar_t fileName2[MAX_PATH + 1]; - wcscpy(fileName2, fileName); + outfile = _wopen(basics.filename, _O_BINARY | _O_CREAT | _O_EXCL | _O_WRONLY); + } + + if(outfile == -1) + { + warnx("couldn't open output file: %S", basics.filename); + goto cleanup; + } - // For duplicate files we add .x to the file name where x is a number - while(hFile == INVALID_HANDLE_VALUE && ::GetLastError() == ERROR_FILE_EXISTS - && nRename < 0x1000000) - { - wcscpy(fileName, fileName2); - wcscat(fileName, L"."); - uint16 len = wcslen(fileName); - - // Make sure we don't have a buffer overflow - if(len > MAX_PATH - 5) - break; - - _itow(nRename, fileName + len, 10); - nRename++; - - hFile = CreateFileW(fileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL); - } - - wprintf(L"\\%s", fileName); - bFile = true; - // Check if successful after all that - if(hFile == INVALID_HANDLE_VALUE) - PASS_FATAL(); + attribdata = ntfsx_record_findattribute(record, kNTFS_DATA, pi->device); + if(!attribdata) + RETWARNX("invalid mft record. no data attribute found"); + + attrhead = ntfsx_attribute_header(attribdata); - - DWORD dwDone = 0; - uint64 fileSize = 0; + /* For resident data just write it out */ + if(!attrhead->bNonResident) + { + uint32 length = ntfsx_attribute_getresidentsize(attribdata); + byte* data = ntfsx_attribute_getresidentdata(attribdata); + if(!data) + RETWARNX("invalid mft record. resident data screwed up"); - // Get the File's data - pAttribData = record.FindAttribute(kNTFS_DATA, hIn); - if(!pAttribData) RET_ERROR(ERROR_NTFS_INVALID); + if(write(outfile, data, length) != (int32)length) + RETWARN("couldn't write data to output file"); + } - - // For Resident data just write it out - if(!pAttribData->GetHeader()->bNonResident) - { - if(!WriteFile(hFile, pAttribData->GetResidentData(), pAttribData->GetResidentSize(), &dwDone, NULL)) - PASS_FATAL(); - } + /* For non resident data it's a bit more involved */ + else + { + datarun = ntfsx_attribute_getdatarun(attribdata); + if(!datarun) + errx(1, "out of memory"); - // For Nonresident data a bit more involved - else - { - pDataRun = pAttribData->GetDataRun(); - ASSERT(pDataRun != NULL); + nonres = (ntfs_attribnonresident*)attrhead; + fileSize = nonres->cbAttribData; - NTFS_AttribNonResident* pNonRes = (NTFS_AttribNonResident*)pAttribData->GetHeader(); - fileSize = pNonRes->cbAttribData; + /* Allocate a cluster for reading and writing */ + if(!ntfsx_cluster_reserve(&cluster, pi)) + errx(1, "out of memory"); - // Allocate a cluster for reading and writing - NTFS_Cluster clus; - if(!clus.New(pInfo)) - RET_FATAL(ERROR_NOT_ENOUGH_MEMORY); - + if(ntfsx_datarun_first(datarun)) + { + do + { + /* Check to see if we have a bogus data run */ + if(fileSize == 0 && datarun->length) + { + warnx("invalid mft record. file length invalid or extra data in file"); + break; + } + + /* Sparse clusters we just write zeros */ + if(datarun->sparse) + { + memset(cluster.data, 0, cluster.size); + + for(i = 0; i < datarun->length && fileSize; i++) + { + num = cluster.size; + + if(fileSize < 0xFFFFFFFF && num > (uint32)fileSize) + num = (uint32)fileSize; + + if(write(outfile, cluster.data, num) != (int32)num) + err(1, "couldn't write to output file: %S", basics.filename); + + fileSize -= num; + } + } + + /* Handle not sparse clusters */ + else + { + if(pi->locks) + { + /* Add a location lock so any raw scrounging won't do + this cluster later */ + addLocationLock(pi->locks, CLUSTER_TO_SECTOR(*pi, datarun->cluster), + CLUSTER_TO_SECTOR(*pi, datarun->cluster + datarun->length)); + } + + for(i = 0; i < datarun->length && fileSize; i++) + { + num = min(cluster.size, (uint32)fileSize); + dataSector = CLUSTER_TO_SECTOR(*pi, (datarun->cluster + i)); + + if(!ntfsx_cluster_read(&cluster, pi, dataSector, pi->device)) + err(1, "couldn't read sector from disk"); + + if(write(outfile, cluster.data, num) != (int32)num) + err(1, "couldn't write to output file: %S", basics.filename); + + fileSize -= num; + } + } + } + while(ntfsx_datarun_next(datarun)); + } + } - // Now loop through the data run - if(pDataRun->First()) - { - do - { - // If it's a sparse cluster then just write zeros - if(pDataRun->m_bSparse) - { - memset(clus.m_pCluster, 0, clus.m_cbCluster); - - for(uint32 i = 0; i < pDataRun->m_numClusters && fileSize; i++) - { - DWORD dwToWrite = clus.m_cbCluster; - if(!HIGHDWORD(fileSize) && dwToWrite > (DWORD)fileSize) - dwToWrite = (DWORD)fileSize; - - if(!WriteFile(hFile, clus.m_pCluster, dwToWrite, &dwDone, NULL)) - PASS_FATAL(); - - fileSize -= dwToWrite; - } - } - - // Not sparse - else - { - // Add a lock on those clusters so we don't have to scrounge'm later - AddLocationLock(pInfo, CLUSTER_TO_SECTOR(*pInfo, pDataRun->m_firstCluster), - CLUSTER_TO_SECTOR(*pInfo, pDataRun->m_firstCluster + pDataRun->m_numClusters)); - - // Read and write clusters out - for(uint32 i = 0; i < pDataRun->m_numClusters && fileSize; i++) - { - DWORD dwToWrite = min(clus.m_cbCluster, (DWORD)fileSize); - uint64 sector = CLUSTER_TO_SECTOR(*pInfo, (pDataRun->m_firstCluster + i)); - - if(!clus.Read(pInfo, sector, hIn)) - PASS_ERROR(); - - if(!WriteFile(hFile, clus.m_pCluster, dwToWrite, &dwDone, NULL)) - PASS_FATAL(); - - fileSize -= dwToWrite; - } - } - } - while(pDataRun->Next()); - } - } + if(fileSize != 0) + warnx("invalid mft record. couldn't find all data for file"); - // TODO: More intelligence needed here - if(fileSize != 0) - printf(" (Entire file not written)"); + close(outfile); + outfile = -1; - SetFileTime(hFile, &ftCreated, &ftAccessed, &ftModified); - - CloseHandle(hFile); - hFile = NULL; + setFileTime(basics.filename, &(basics.created), + &(basics.accessed), &(basics.modified)); - SetFileAttributesW(fileName, fileAttrib); - } - } + setFileAttributes(basics.filename, basics.flags); + } - bRet = TRUE; - ::SetLastError(ERROR_SUCCESS); +cleanup: + if(record) + ntfsx_record_free(record); -clean_up: - if(hFile && hFile != INVALID_HANDLE_VALUE) - CloseHandle(hFile); - if(pAttribName) - delete pAttribName; - if(pAttribData) - delete pAttribData; - if(pDataRun) - delete pDataRun; + ntfsx_cluster_release(&cluster); - if(bFile) - { - if(::GetLastError() != ERROR_SUCCESS) - { - printf(" ("); - PrintLastError(); - fputc(')', stdout); - } - } + if(attribdata) + ntfsx_attribute_free(attribdata); + if(datarun) + ntfsx_datarun_free(datarun); - return bRet; + if(outfile != -1) + close(outfile); } -// ---------------------------------------------------------------------- -// Helper function to print out errors - -void PrintLastError() +void scroungeMFT(partitioninfo* pi, ntfsx_mftmap* map) { - DWORD dwErr = ::GetLastError(); - switch(dwErr) - { - case ERROR_NTFS_INVALID: - printf("Invalid NTFS data structure"); - break; + ntfsx_record* record = NULL; + uint64 sector; + filebasics basics; + ntfs_recordheader* header; - case ERROR_NTFS_NOTIMPLEMENT: - printf("NTFS feature not implemented"); - break; + /* Try and find the MFT at the given location */ + sector = pi->mft + pi->first; - default: - { - LPVOID lpMsgBuf; - - DWORD dwRet = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_MAX_WIDTH_MASK, - NULL, - dwErr, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language - (LPTSTR) &lpMsgBuf, - 0, - NULL); - - if(dwRet && lpMsgBuf) - { - // Remove return - ((LPTSTR)lpMsgBuf)[dwRet - 2] = 0; + if(sector >= pi->end) + errx(2, "invalid mft. past end of partition"); - wprintf((LPTSTR)lpMsgBuf); + record = ntfsx_record_alloc(pi); + if(!record) + errx(1, "out of memory"); - // Free the buffer. - ::LocalFree(lpMsgBuf); - } - } - }; -} + /* Read the MFT record */ + if(!ntfsx_record_read(record, sector, pi->device)) + err(1, "couldn't read mft"); + header = ntfsx_record_header(record); + ASSERT(header); -// ---------------------------------------------------------------------- -// Scrounge the partition for MFT Records and hand off to -// ProcessMFTRecord for processing + if(!(header->flags & kNTFS_RecFlagUse)) + errx(2, "invalid mft. marked as not in use"); -BOOL ScroungeMFTRecords(PartitionInfo* pInfo, HANDLE hIn) -{ - uint64 numRecords = 0; + /* Try and get a file name out of the header */ - // Save current directory away - TCHAR curDir[MAX_PATH + 1]; - GetCurrentDirectory(MAX_PATH, curDir); + processRecordFileBasics(pi, record, &basics); - NTFS_MFTMap map(pInfo); + if(wcscmp(basics.filename, kNTFS_MFTName)) + errx(2, "invalid mft. wrong record"); - // Try and find the MFT at the given location - uint64 sector = pInfo->offMFT + pInfo->firstSector; + fprintf(stderr, "[Processing MFT...]\n"); - if(sector < pInfo->lastSector) - { - BOOL bRet = ProcessMFTRecord(pInfo, sector, &map, hIn); + /* Load the MFT data runs */ - fputc('\n', stdout); + if(!ntfsx_mftmap_load(map, record, pi->device)) + err(1, "error reading in mft"); - if(!bRet) - return FALSE; - } + if(ntfsx_mftmap_length(map) == 0) + errx(1, "invalid mft. no records in mft"); - uint64 length = map.GetLength(); - if(length == 0) - { - printf("[MFT not found at specified location. No folder structure]\n"); - return ScroungeRawRecords(pInfo, hIn); - } + ntfsx_record_free(record); +} - for(uint64 i = 1; i < length; i += (kNTFS_RecordLen / kSectorSize)) - { - // Move to right output directory - SetCurrentDirectory(curDir); - // TODO: Warn when invalid - uint64 sector = map.SectorForIndex(i); +void scroungeUsingMFT(partitioninfo* pi) +{ + uint64 numRecords = 0; + char dir[MAX_PATH]; + ntfsx_mftmap map; + uint64 length; + uint64 sector; + uint64 i; + + fprintf(stderr, "[Scrounging via MFT...]\n"); + + /* Save current directory away */ + getcwd(dir, MAX_PATH); + + /* Get the MFT map ready */ + memset(&map, 0, sizeof(map)); + ntfsx_mftmap_init(&map, pi); + pi->mftmap = ↦ + + + /* + * Make sure the MFT is actually where they say it is. + * This also fills in the valid cluster size if needed + */ + scroungeMFT(pi, &map); + length = ntfsx_mftmap_length(&map); + + for(i = 1; i < length; i ++) + { + sector = ntfsx_mftmap_sectorforindex(&map, i); if(sector == kInvalidSector) + { + warnx("invalid index in mft: %d", i); continue; + } - // Then process it - BOOL bRet = ProcessMFTRecord(pInfo, sector, &map, hIn); - - fputc('\n', stdout); + /* Process the record */ + processMFTRecord(pi, sector, 0); - if(!bRet) - return FALSE; + /* Move to right output directory */ + chdir(dir); } - return TRUE; + pi->mftmap = NULL; } -BOOL ScroungeRawRecords(PartitionInfo* pInfo, HANDLE hIn) +void scroungeUsingRaw(partitioninfo* pi) { byte buffSec[kSectorSize]; - DWORD dwDummy = 0; + char dir[_MAX_PATH + 1]; + uint64 sec; + drivelocks locks; + int64 pos; + size_t sz; + uint32 magic = kNTFS_RecMagic; - uint64 numRecords = 0; - - // Save current directory away - TCHAR curDir[MAX_PATH + 1]; - GetCurrentDirectory(MAX_PATH, curDir); + fprintf(stderr, "[Scrounging raw records...]\n"); - // Loop through sectors - for(uint64 sec = pInfo->firstSector; sec < pInfo->lastSector; sec++) - { - // See if the current sector has already been read - if(CheckLocationLock(pInfo, sec)) - { - // TODO: check this - sec--; - continue; - } + /* Save current directory away */ + getcwd(dir, _MAX_PATH); - // Read the mftRecord - uint64 offRecord = SECTOR_TO_BYTES(sec); - LONG lHigh = HIGHDWORD(offRecord); - if(SetFilePointer(hIn, LOWDWORD(offRecord), &lHigh, FILE_BEGIN) == -1 - && GetLastError() != NO_ERROR) - return FALSE; + /* Get the locks ready */ + memset(&locks, 0, sizeof(locks)); + pi->locks = &locks; - if(!ReadFile(hIn, buffSec, kSectorSize, &dwDummy, NULL)) - return FALSE; - - // Check beginning of sector for the magic signature - if(!memcmp(&kNTFS_RecMagic, &buffSec, sizeof(kNTFS_RecMagic))) - { - // Move to right output directory - SetCurrentDirectory(curDir); - - // Then process it - BOOL bRet = ProcessMFTRecord(pInfo, sec, NULL, hIn); - - fputc('\n', stdout); - - if(!bRet) - return FALSE; - - } - } - - return TRUE; -} - - -// ---------------------------------------------------------------------- -// Output strings - -const char kPrintHeader[] = "Scrounge (NTFS) Version 0.7\n\n"; - -const char kPrintData[] = "\ - Start Sector End Sector Cluster Size MFT Offset \n\ -==================================================================\n\ -"; - -const char kPrintDrive[] = "\nDrive: %u\n"; -const char kPrintDriveInfo[] = " %-15u %-15u "; -const char kPrintNTFSInfo[] = "%-15u %-15u"; - -const char kPrintHelp[] = "\ -Recovers an NTFS partition with a corrupted MFT. \n\ - \n\ -Usage: scrounge drive start end cluster mft [outdir] \n\ - \n\ - drive: Physical drive number. \n\ - start: First sector of partition. \n\ - end: Last sector of partition. \n\ - cluster: Cluster size for the partition (in sectors). \n\ - mft: Offset from beginning of partition to MFT (in sectors). \n\ - outdir: Output directory (optional). \n\ - \n\ -"; - - -// ---------------------------------------------------------------------- -// Info functions - -int PrintNTFSInfo(HANDLE hDrive, uint64 tblSector) -{ - byte sector[kSectorSize]; - - uint64 pos = SECTOR_TO_BYTES(tblSector); - LONG lHigh = HIGHDWORD(pos); - if(SetFilePointer(hDrive, LOWDWORD(pos), &lHigh, FILE_BEGIN) == -1 - && GetLastError() != NO_ERROR) - return 1; - - DWORD dwRead = 0; - if(!ReadFile(hDrive, sector, kSectorSize, &dwRead, NULL)) - return 1; - - NTFS_BootSector* pBoot = (NTFS_BootSector*)sector; - if(!memcmp(pBoot->sysId, kNTFS_SysId, sizeof(pBoot->sysId))) - printf(kPrintNTFSInfo, pBoot->secPerClus, pBoot->offMFT * pBoot->secPerClus); - - wprintf(L"\n"); - return 0; -} - - -int PrintPartitionInfo(HANDLE hDrive, uint64 tblSector) -{ - ASSERT(sizeof(Drive_MBR) == kSectorSize); - Drive_MBR mbr; - - uint64 pos = SECTOR_TO_BYTES(tblSector); - LONG lHigh = HIGHDWORD(pos); - if(SetFilePointer(hDrive, LOWDWORD(pos), &lHigh, FILE_BEGIN) == -1 - && GetLastError() != NO_ERROR) - return 1; - - DWORD dwRead = 0; - if(!ReadFile(hDrive, &mbr, sizeof(Drive_MBR), &dwRead, NULL)) - return 1; - - if(mbr.sig == kMBR_Sig) + /* Loop through sectors */ + for(sec = pi->first; sec < pi->end; sec++) { - for(int i = 0; i < 4; i++) - { - if(mbr.partitions[i].system == kPartition_Extended || - mbr.partitions[i].system == kPartition_ExtendedLBA) - { - PrintPartitionInfo(hDrive, tblSector + mbr.partitions[i].startsec); - } - else if(!mbr.partitions[i].system == kPartition_Invalid) - { - printf(kPrintDriveInfo, (uint32)tblSector + mbr.partitions[i].startsec, (uint32)tblSector + mbr.partitions[i].endsec); - PrintNTFSInfo(hDrive, tblSector + (uint64)mbr.partitions[i].startsec); - } - } - } - - return 0; -} + if(checkLocationLock(&locks, sec)) + continue; -const WCHAR kDriveName[] = L"\\\\.\\PhysicalDrive%d"; + /* Read the record */ + pos = SECTOR_TO_BYTES(sec); + if(_lseeki64(pi->device, pos, SEEK_SET) == -1) + errx(1, "can't seek device to sector"); -int PrintData() -{ - printf(kPrintHeader); - printf(kPrintData); + sz = read(pi->device, buffSec, kSectorSize); + if(sz == -1 || sz != kSectorSize) + errx(1, "can't read drive sector"); - WCHAR driveName[MAX_PATH]; - - // LIMIT: 256 Drives - for(int i = 0; i < 0x100; i++) - { - wsprintf(driveName, kDriveName, i); - - HANDLE hDrive = CreateFile(driveName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if(hDrive != INVALID_HANDLE_VALUE) + /* Check beginning of sector for the magic signature */ + if(!memcmp(&magic, &buffSec, sizeof(magic))) { - printf(kPrintDrive, i); - - PrintPartitionInfo(hDrive, 0); - - CloseHandle(hDrive); + /* Process the record */ + processMFTRecord(pi, sec, 0); } } - return 2; + pi->locks = NULL; } - - -// ---------------------------------------------------------------------- -// Main Program - -int main(int argc, char* argv[]) -{ - int curArg = 1; - - if(argc < 2) - return PrintData(); - - - // Check for flags - if(*(argv[curArg]) == '-' || *(argv[curArg]) == '/' ) - { - char* arg = argv[curArg]; - arg++; - - while(*arg != '\0') - { - switch(tolower(*arg)) - { - - // Help - case 'h': - printf(kPrintHeader); - printf(kPrintHelp); - return 2; - - default: - printf("scrounge: invalid option '%c'\n", *arg); - return PrintData(); - } - - arg++; - } - - curArg++; - } - - PartitionInfo* pInfo = CreatePartitionInfo(); - if(!pInfo) - { - printf("scrounge: Out of Memory.\n"); - return 1; - } - - if(curArg + 5 > argc) - { - printf("scrounge: invalid option(s).\n"); - return 2; - } - - // Next param should be the drive - byte driveNum = atoi(argv[curArg++]); - - // Followed by the partition info - pInfo->firstSector = atoi(argv[curArg++]); - pInfo->lastSector = atoi(argv[curArg++]); - pInfo->clusterSize = atoi(argv[curArg++]); - pInfo->offMFT = atoi(argv[curArg++]); - - if(pInfo->firstSector == 0 || - pInfo->lastSector == 0 || - pInfo->clusterSize == 0 || - pInfo->offMFT == 0) - { - printf("scrounge: invalid option(s).\n"); - return 2; - } - -// pInfo->clusterSize = 8; -// pInfo->firstSector = 20482938/*128*/; -// pInfo->lastSector = 80019765/*15358077*/; -// pInfo->offMFT = 32; - - if(curArg <= argc) - SetCurrentDirectoryA(argv[curArg++]); - - WCHAR driveName[MAX_PATH]; - - wsprintf(driveName, kDriveName, driveNum); - - HANDLE hDrive = CreateFile(driveName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if(hDrive == INVALID_HANDLE_VALUE) - { - printf("scrounge: Can't open drive %d.\n", driveNum); - return 2; - } - - if(!ScroungeMFTRecords(pInfo, hDrive)) - { - printf("scrounge: "); - PrintLastError(); - fputc('\n', stdout); - return 2; - } - - FreePartitionInfo(pInfo); - - return 0; -}
\ No newline at end of file diff --git a/src/scrounge.h b/src/scrounge.h index b425db9..9baf720 100644 --- a/src/scrounge.h +++ b/src/scrounge.h @@ -1,38 +1,34 @@ -// -// 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> -// +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ #ifndef __SCROUNGE_H__ #define __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; } +#include "drive.h" -BOOL ProcessMFTRecord(PartitionInfo* pInfo, uint64 sector, NTFS_MFTMap* map, HANDLE hIn); -BOOL ScroungeMFTRecords(PartitionInfo* pInfo, HANDLE hIn); -BOOL ScroungeRawRecords(PartitionInfo* pInfo, HANDLE hIn); -void PrintLastError(); +void scroungeSearch(partitioninfo* pi); +void scroungeList(); +void scroungeUsingMFT(partitioninfo* pi); +void scroungeUsingRaw(partitioninfo* pi); +/* For compatibility */ +void setFileAttributes(wchar_t* filename, uint32 flags); +void setFileTime(wchar_t* filename, uint64* created, uint64* accessed, uint64* modified); - -#endif //__SCROUNGE_H__
\ No newline at end of file +#endif /* __SCROUNGE_H__ */ diff --git a/src/search.c b/src/search.c new file mode 100644 index 0000000..4ab9823 --- /dev/null +++ b/src/search.c @@ -0,0 +1,32 @@ +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + + +#include <stdio.h> +#include "drive.h" + +void scroungeSearch(partitioninfo* pi) +{ + fprintf(stderr, "[Performing NTFS partition search...]"); + errx(1, "search functionality not implemented yet."); + + /* go through all sectors until we find an MFT record */ + /* that isn't the MFT mirror */ + +} diff --git a/src/usuals.h b/src/usuals.h index 8961592..0697956 100644 --- a/src/usuals.h +++ b/src/usuals.h @@ -1,29 +1,28 @@ -// -// 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 <win32/debug.h> +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +#ifndef __USUALS_H__ +#define __USUALS_H__ + +#include "debug.h" +#include "compat.h" +#include <errno.h> typedef unsigned __int64 uint64; typedef unsigned long uint32; @@ -38,8 +37,17 @@ typedef signed short int16; #define NULL 0 #endif +#define RETWARNBX(s) { ret = false; warnx(s); goto cleanup; } +#define RETWARNB(s) { ret = false; warn(s); goto cleanup; } +#define RETWARNX(s) { warnx(s); goto cleanup; } +#define RETWARN(s) { warn(s); goto cleanup; } +#define RETURN goto cleanup #define HIGHDWORD(i64) (DWORD)((i64) >> 32) #define LOWDWORD(i64) (DWORD)((i64) & 0xFFFFFFFF) -#endif //__USUALS_H__20010822 + +#define INTERSECTS(b1, e1, b2, e2) \ + ((b1) < (e2) && (e1) > (b2)) + +#endif /* __USUALS_H__ */ diff --git a/src/win32.c b/src/win32.c new file mode 100644 index 0000000..0726fad --- /dev/null +++ b/src/win32.c @@ -0,0 +1,78 @@ +/* + * AUTHOR + * N. Nielsen + * + * LICENSE + * This software is in the public domain. + * + * The software is provided "as is", without warranty of any kind, + * express or implied, including but not limited to the warranties + * of merchantability, fitness for a particular purpose, and + * noninfringement. In no event shall the author(s) be liable for any + * claim, damages, or other liability, whether in an action of + * contract, tort, or otherwise, arising from, out of, or in connection + * with the software or the use or other dealings in the software. + * + * SUPPORT + * Send bug reports to: <nielsen@memberwebs.com> + */ + +#define WIN32_LEAN_AND_MEAN 1 +#include <windows.h> + +#include "usuals.h" +#include "ntfs.h" + +const char kDriveName[] = "\\\\.\\PhysicalDrive%d"; + +#define ntfs_makefiletime(i64, ft) \ + ((ft).dwLowDateTime = LOWDWORD(i64), (ft).dwHighDateTime = HIGHDWORD(i64)) + +void makeDriveName(char* driveName, int i) +{ + wsprintf(driveName, kDriveName, i); +} + +void setFileTime(wchar_t* filename, uint64* created, + uint64* accessed, uint64* modified) +{ + FILETIME ftcr; + FILETIME ftac; + FILETIME ftmd; + HANDLE file; + + ntfs_makefiletime(*created, ftcr); + ntfs_makefiletime(*accessed, ftac); + ntfs_makefiletime(*modified, ftmd); + + /* Write to the File */ + file = CreateFileW(filename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if(file == INVALID_HANDLE_VALUE) + { + warnx("couldn't set file time: %S", filename); + return; + } + + if(!SetFileTime(file, &ftcr, &ftac, &ftmd)) + warnx("couldn't set file time: %S", filename); + + CloseHandle(file); +} + +void setFileAttributes(wchar_t* filename, uint32 flags) +{ + DWORD attributes; + + /* Attributes */ + if(flags & kNTFS_FileReadOnly) + attributes |= FILE_ATTRIBUTE_READONLY; + if(flags & kNTFS_FileHidden) + attributes |= FILE_ATTRIBUTE_HIDDEN; + if(flags & kNTFS_FileArchive) + attributes |= FILE_ATTRIBUTE_ARCHIVE; + if(flags & kNTFS_FileSystem) + attributes |= FILE_ATTRIBUTE_SYSTEM; + + if(!SetFileAttributesW(filename, attributes)) + warnx("couldn't set file attributes: %S", filename); +} |