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 + -
显示快捷键?