scrounge.c
来自「磁盘格式解读」· C语言 代码 · 共 659 行 · 第 1/2 页
C
659 行
/*
* 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 "scrounge.h"
#include "ntfs.h"
#include "ntfsx.h"
#include "locks.h"
#define PROCESS_MFT_FLAG_SUB 1 << 1
#define DEF_FILE_MODE 0x180#define DEF_DIR_MODE 0x1C0typedef struct _filebasics
{
fchar_t filename[MAX_PATH + 1];
uint64 created;
uint64 modified;
uint64 accessed;
uint32 flags;
uint64 parent;
}
filebasics;
void processRecordFileBasics(partitioninfo* pi, ntfsx_record* record, filebasics* basics)
{
/* Data Attribute */
ntfsx_attribute* attr = NULL;
ntfsx_attrib_enum* attrenum = NULL;
{
byte* resident = NULL;
ntfs_attribfilename* filename;
byte nameSpace;
ntfs_char* name;
size_t len;
#ifndef FC_WIDE
char* temp;
#endif
ASSERT(record);
memset(basics, 0, sizeof(filebasics));
basics->parent = kInvalidSector;
/* Now get the name and info... */
attrenum = ntfsx_attrib_enum_alloc(kNTFS_FILENAME, true);
nameSpace = kNTFS_NameSpacePOSIX;
memset(basics->filename, 0, sizeof(basics->filename));
while((attr = ntfsx_attrib_enum_all(attrenum, record)) != NULL)
{
/* TODO ASSUMPTION: File name is always resident */
if(!ntfsx_attribute_header(attr)->bNonResident)
{
/* Get out all the info we need */
filename = (ntfs_attribfilename*)ntfsx_attribute_getresidentdata(attr);
ASSERT(filename);
/*
* There can be multiple filenames with different
* namespaces so choose the best one
*/
if(ntfs_isbetternamespace(nameSpace, filename->nameSpace))
{
/* Dates */
basics->created = filename->timeCreated;
basics->modified = filename->timeModified;
basics->accessed = filename->timeRead;
/* File Name */
name = (ntfs_char*)(((byte*)filename) + sizeof(ntfs_attribfilename));
len = filename->cFileName;
if(len > MAX_PATH)
len = MAX_PATH;
#ifdef FC_WIDE
wcsncpy(basics->filename, name, len);
#else
temp = unicode_transcode16to8(name, len);
len = strlen(temp);
if(len > MAX_PATH)
len = MAX_PATH;
strncpy(basics->filename, temp, len);
#endif
basics->filename[len] = 0;
/* Attributes */
basics->flags = filename->flags;
/* Parent Directory */
basics->parent = filename->refParent & kNTFS_RefMask;
/* Namespace */
nameSpace = filename->nameSpace;
}
}
ntfsx_attribute_free(attr);
attr = NULL;
}
}
if(attr)
ntfsx_attribute_free(attr);
if(attrenum)
ntfsx_attrib_enum_free(attrenum);
}
void processMFTRecord(partitioninfo* pi, uint64 sector, uint32 flags)
{
ntfsx_record* record = NULL;
ntfsx_attribute* attribdata = NULL;
ntfsx_attrib_enum* attrenum = NULL;
ntfsx_datarun* datarun = NULL;
int ofile = -1;
ntfsx_cluster cluster;
memset(&cluster, 0, sizeof(cluster));
{
filebasics basics;
ntfs_recordheader* header;
uint64 parentSector;
uint64 dataSector;
uint16 rename = 0;
uint64 dataSize = 0; /* Length of initialized file data */
uint64 sparseSize = 0; /* Length of sparse data following */
uint32 i;
bool haddata = false;
uint32 num;
fchar_t filename2[MAX_PATH + 1];
ntfs_attribheader* attrhead;
ntfs_attribnonresident* nonres;
ASSERT(sector != kInvalidSector);
record = ntfsx_record_alloc(pi);
/* 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);
/* Without files we skip */
if(basics.filename[0] == 0)
RETURN;
/* If it's the root folder then return */
if(!fcscmp(basics.filename, FC_DOT))
RETURN;
/* System, Hidden files that begin with $ are skipped */
if(basics.flags & kNTFS_FileSystem &&
basics.flags & kNTFS_FileHidden &&
basics.filename[0] == kNTFS_SysPrefix)
RETURN;
/* Process parent folders if available */
if(basics.parent != kInvalidSector)
{
/* Only if we have MFT map info available */
if(pi->mftmap)
{
parentSector = ntfsx_mftmap_sectorforindex(pi->mftmap, basics.parent);
if(parentSector == kInvalidSector)
warnx("invalid parent directory for file: " FC_PRINTF, basics.filename);
else
processMFTRecord(pi, parentSector, flags | PROCESS_MFT_FLAG_SUB);
}
}
printf(flags & PROCESS_MFT_FLAG_SUB ?
"\\" FC_PRINTF : "\\" FC_PRINTF "\n", basics.filename);
/* Directory handling: */
if(header->flags & kNTFS_RecFlagDir)
{
/* Try to change to the directory */
if(fc_chdir(basics.filename) == -1)
{
#ifdef _DEBUG
if(!g_verifyMode)
#endif
{
#ifdef _WIN32
if(fc_mkdir(basics.filename) == -1)
#else
if(fc_mkdir(basics.filename, DEF_DIR_MODE) == -1)
#endif
{
warn("couldn't create directory '" FC_PRINTF "' putting files in parent directory", basics.filename);
}
else
{
setFileAttributes(basics.filename, basics.flags);
fc_chdir(basics.filename);
}
}
}
RETURN;
}
#ifdef _DEBUG
/* If in verify mode */
if(g_verifyMode)
{
ofile = fc_open(basics.filename, O_BINARY | O_RDONLY);
if(ofile == -1)
{
warn("couldn't open verify file: " FC_PRINTF, basics.filename);
goto cleanup;
}
}
/* Normal file handling: */
else
#endif
{ ofile = fc_open(basics.filename, O_BINARY | O_CREAT | O_EXCL | O_WRONLY, DEF_FILE_MODE);
fcsncpy(filename2, basics.filename, MAX_PATH);
filename2[MAX_PATH] = 0;
while(ofile == -1 && errno == EEXIST && rename < 0x1000)
{
if(fcslen(basics.filename) + 7 >= MAX_PATH)
{
warnx("file name too long on duplicate file: " FC_PRINTF, basics.filename);
goto cleanup;
}
fcscpy(basics.filename, filename2);
fcscat(basics.filename, FC_DOT);
itofc(rename, basics.filename + fcslen(basics.filename), 10);
rename++;
ofile = fc_open(basics.filename, O_BINARY | O_CREAT | O_EXCL | O_WRONLY, DEF_FILE_MODE);
}
if(ofile == -1)
{
warn("couldn't open output file: " FC_PRINTF, basics.filename);
goto cleanup;
}
}
attrenum = ntfsx_attrib_enum_alloc(kNTFS_DATA, true);
while((attribdata = ntfsx_attrib_enum_all(attrenum, record)) != NULL)
{
attrhead = ntfsx_attribute_header(attribdata);
/*
* We don't do compressed/encrypted files. Eventually
* we may be able to write in some support :)
*/
if(attrhead->flags & kNTFS_AttrCompressed)
RETWARNX("compressed file. skipping.");
if(attrhead->flags & kNTFS_AttrEncrypted)
RETWARNX("encrypted file. skipping.");
/* On the first round figure out the file size */
if(!haddata)
{
if(attrhead->bNonResident)
{
nonres = (ntfs_attribnonresident*)attrhead;
if(nonres->cbInitData > nonres->cbAttribData)
RETWARNX("invalid file length.");
dataSize = nonres->cbInitData;
sparseSize = nonres->cbAttribData - nonres->cbInitData;
}
else
{
dataSize = ntfsx_attribute_getresidentsize(attribdata);
sparseSize = 0;
}
}
haddata = true;
/* 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");
#ifdef _DEBUG
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?