TestDisk: replace ntfs_get_attr() by several functions to parse MFT record

This commit is contained in:
Christophe Grenier 2013-08-31 19:23:28 +02:00
parent db079c0e26
commit 94f6943be7
3 changed files with 368 additions and 242 deletions

View file

@ -46,9 +46,8 @@
/* #include "guid_cmp.h" */
extern const arch_fnct_t arch_i386;
static int set_NTFS_info(disk_t *disk_car, const struct ntfs_boot_sector*ntfs_header,partition_t *partition,const int verbose);
static int ntfs_read_MFT(disk_t *disk_car, partition_t *partition, const struct ntfs_boot_sector*ntfs_header, const int my_type, const int verbose);
static int ntfs_get_attr_aux(const char *attr_record, const int my_type, partition_t *partition, const char *end, const int verbose, const char*file_name_to_find);
static void set_NTFS_info(disk_t *disk_car, const struct ntfs_boot_sector*ntfs_header, partition_t *partition);
static void ntfs_get_volume_name(disk_t *disk_car, partition_t *partition, const struct ntfs_boot_sector*ntfs_header);
unsigned int ntfs_sector_size(const struct ntfs_boot_sector *ntfs_header)
{ return (ntfs_header->sector_size[1]<<8)+ntfs_header->sector_size[0]; }
@ -67,7 +66,7 @@ int check_NTFS(disk_t *disk_car,partition_t *partition,const int verbose,const i
free(buffer);
return 1;
}
set_NTFS_info(disk_car, (struct ntfs_boot_sector*)buffer, partition, verbose);
set_NTFS_info(disk_car, (struct ntfs_boot_sector*)buffer, partition);
free(buffer);
return 0;
}
@ -106,11 +105,11 @@ int recover_NTFS(disk_t *disk_car, const struct ntfs_boot_sector*ntfs_header,par
partition->part_size=part_size;
partition->part_type_i386=P_NTFS;
partition->part_type_gpt=GPT_ENT_TYPE_MS_BASIC_DATA;
set_NTFS_info(disk_car, ntfs_header, partition, verbose);
set_NTFS_info(disk_car, ntfs_header, partition);
return 0;
}
static int set_NTFS_info(disk_t *disk_car, const struct ntfs_boot_sector*ntfs_header,partition_t *partition,const int verbose)
static void set_NTFS_info(disk_t *disk_car, const struct ntfs_boot_sector*ntfs_header, partition_t *partition)
{
partition->fsname[0]='\0';
partition->blocksize=ntfs_header->sectors_per_cluster*ntfs_sector_size(ntfs_header);
@ -118,7 +117,7 @@ static int set_NTFS_info(disk_t *disk_car, const struct ntfs_boot_sector*ntfs_he
snprintf(partition->info, sizeof(partition->info), "NTFS, blocksize=%u", partition->blocksize);
else
snprintf(partition->info, sizeof(partition->info), "NTFS found using backup sector, blocksize=%u", partition->blocksize);
return ntfs_read_MFT(disk_car, partition, ntfs_header, 0x60, verbose);
ntfs_get_volume_name(disk_car, partition, ntfs_header);
}
int test_NTFS(const disk_t *disk_car,const struct ntfs_boot_sector*ntfs_header, partition_t *partition,const int verbose, const int dump_ind)
@ -185,231 +184,134 @@ int test_NTFS(const disk_t *disk_car,const struct ntfs_boot_sector*ntfs_header,
return 0;
}
/* */
int ntfs_get_attr(const char *mft_record, const int my_type, partition_t *partition, const char *end, const int verbose, const char*file_name_to_find)
const ntfs_attribheader *ntfs_getattributeheaders(const ntfs_recordheader* record)
{
const char *attr_record;
/* Only check for magic DWORD here, fixup should have happened before */
if(memcmp(mft_record,"FILE",4)) return 2; /* NTFS_RECORD_TYPES == magic_FILE ?*/
if(NTFS_GETU16(mft_record + 0x14)%8!=0)
return 2;
if(NTFS_GETU16(mft_record + 0x14)<42) /* sizeof(MFT_RECORD)>=42 */
return 2;
/* screen_buffer_add("FILE\n"); */
/* screen_buffer_add("seq nbr %lu ",NTFS_GETU16(mft_record+0x10)); */
/* screen_buffer_add("main MFT record %lu ",NTFS_GETU64(mft_record+0x20)); */
/* location of first attribute */
attr_record= mft_record + NTFS_GETU16(mft_record + 0x14);
return ntfs_get_attr_aux(attr_record, my_type, partition, end, verbose, file_name_to_find);
const char* location = (const char*)record;
if(le32(record->magic)!=NTFS_Magic ||
le16(record->attrs_offset)%8!=0 ||
le16(record->attrs_offset)<42)
return NULL;
location += le16(record->attrs_offset);
return (const ntfs_attribheader *)location;
}
static int ntfs_get_attr_aux(const char *attr_record, const int my_type, partition_t *partition, const char *end, const int verbose, const char*file_name_to_find)
static const ntfs_attribheader* ntfs_searchattribute(const ntfs_attribheader* attrib, uint32_t attrType, const char* end, int skip)
{
while(1)
if(attrib==NULL)
return NULL;
/* Now we should be at attributes */
while((const char *)attrib + sizeof(ntfs_attribheader) < end &&
le32(attrib->type)!= -1)
{
int attr_type;
/* Resident attributes attr_len>=24(0x18), non resident is bigger */
unsigned int attr_len;
if(attr_record+0x18>=end)
const unsigned int attr_len=le32(attrib->cbAttribute);
if(attr_len%8!=0 || attr_len<0x18 || attr_len>0x10000000 ||
(const char *)attrib + attr_len >= end)
return NULL;
if(!skip)
{
if(verbose>1)
{
log_error("ntfs_get_attr attr_record+0x18>=end\n");
}
return 2;
}
attr_type=NTFS_GETU32(attr_record);
if(attr_type==-1) /* attribute list end with type -1 */
return 0;
attr_len=NTFS_GETU16(attr_record+4);
if((attr_len%8!=0)||(attr_len<0x18))
{
if(verbose>1)
{
log_error("ntfs_get_attr attr_type=%x attr_len=%u (attr_len%%8!0)||(attr_len<0x18)\n",attr_type,attr_len);
}
return 2;
}
if(verbose>1)
{
log_info("attr_type=%x %s\n",attr_type,(NTFS_GETU8(attr_record+8)==0?"resident":"non resident"));
dump_log(attr_record,attr_len);
}
if(NTFS_GETU8(attr_record+8)==0) /* attribute is resident */
{
unsigned int attr_value_length=NTFS_GETU16(attr_record+0x10);
unsigned int attr_value_offset=NTFS_GETU16(attr_record+0x14);
const char *attr_td_list_entry=attr_record+attr_value_offset;
if(attr_value_offset%8!=0)
{
#ifdef NTFS_DEBUG
log_debug("ntfs_get_attr attr_value_offset=%u (%%8!=0)\n",attr_value_offset);
#endif
return 2;
}
if(attr_td_list_entry+26>=end)
{
#ifdef NTFS_DEBUG
log_debug("ntfs_get_attr attr_td_list_entry+26=%p, end=%p\n",attr_td_list_entry+26,end);
#endif
return 2;
}
/* We found the attribute type. Is the name correct, too? */
if((attr_value_offset+attr_value_length>attr_len) || (attr_td_list_entry+attr_len >= end))
{
#ifdef NTFS_DEBUG
// log_debug("ntfs_get_attr \n");
#endif
return 2;
}
if((attr_type==my_type)&&(attr_value_offset!=0))
{
switch(attr_type)
{
case 0x30: /* AT_FILE_NAME */
{
const char *file_name_attr=attr_td_list_entry;
unsigned int file_name_length;
const char *name_it;
if(file_name_attr+0x42>=end)
return 2;
file_name_length=NTFS_GETU8(file_name_attr+0x40); /* size in unicode char */
if(file_name_attr+0x42+2*file_name_length>=end)
return 2;
{
char file_name[256+1]; /* used size is file_name_length+1 */
unsigned int i;
/* screen_buffer_add("MFT record nbr %lu ",NTFS_GETU64(file_name_attr)); */
for(name_it=file_name_attr+0x42,i=0;i<file_name_length; name_it+=2,i++)
file_name[i]=*name_it;
file_name[i]='\0';
if(verbose>1)
{
log_verbose("file_name=%s\n",file_name);
}
if(file_name_to_find!=NULL)
{
if(attr_type==my_type)
{
if(strcmp(file_name_to_find,file_name)==0)
return 1;
else
return 2;
}
} else
screen_buffer_add("%s\n",file_name);
}
}
break;
case 0x60: /* AT_VOLUME_NAME */
{
unsigned int volume_name_length=attr_value_length;
const char *name_it;
char *dest=partition->fsname;
volume_name_length/=2; /* Unicode */
if(volume_name_length>sizeof(partition->fsname)-1)
volume_name_length=sizeof(partition->fsname)-1;
for(name_it=attr_td_list_entry;(volume_name_length>0) && (*name_it!='\0') && (name_it[1]=='\0'); name_it+=2,volume_name_length--)
*dest++=*name_it;
*dest='\0'; /* 27 january 2003: Correct a bug found by Andreas du Plessis-Denz */
}
return 1;
case 0x90: /* AT_INDEX_ROOT */
return NTFS_GETU32(attr_td_list_entry+8); /* index_block_size */
}
}
if(attrib->type == attrType)
return attrib;
}
else
{ /* attribute is not resident */
if(attr_type==my_type)
{
switch(attr_type)
{
case 0x80: /* AT_DATA */
{
/* buf must be unsigned! */
const unsigned char *buf;
uint8_t b; /* Current byte offset in buf. */
uint16_t mapping_pairs_offset;
const unsigned char*attr_end; /* End of attribute. */
long lcn;
int64_t deltaxcn = (int64_t)-1; /* Change in [vl]cn. */
mapping_pairs_offset=NTFS_GETU16(attr_record+32);
buf=(const unsigned char*)attr_record + mapping_pairs_offset;
attr_end = (const unsigned char*)attr_record + attr_len;
lcn = 0;
/* return first element of the run_list */
b = *buf & 0xf;
if (b){
if (buf + b > attr_end)
{
log_error("Attribut AT_DATA: bad size\n");
return 0;
}
for (deltaxcn = (int8_t)buf[b--]; b; b--)
deltaxcn = (deltaxcn << 8) + (uint8_t)buf[b];
/* Assume a negative length to indicate data corruption */
if (deltaxcn < 0)
log_error("Invalid length in mapping pairs array.\n");
} else { /* The length entry is compulsory. */
log_error("Missing length entry in mapping pairs array.\n");
}
if (deltaxcn >= 0)
{
if (!(*buf & 0xf0))
{
log_info("LCN_HOLE\n");
}
else
{
/* Get the lcn change which really can be negative. */
uint8_t b2 = *buf & 0xf;
b = b2 + ((*buf >> 4) & 0xf);
if (buf + b > attr_end)
{
log_error("Attribut AT_DATA: bad size\n");
return 0;
}
for (deltaxcn = (int8_t)buf[b--]; b > b2; b--)
deltaxcn = (deltaxcn << 8) + (uint8_t)buf[b];
/* Change the current lcn to it's new value. */
lcn += deltaxcn;
/* Check lcn is not below -1. */
if (lcn < -1) {
log_error("Invalid LCN < -1 in mapping pairs array.");
return 0;
}
if(verbose>1)
{
log_verbose("LCN %ld\n",lcn);
}
if(attr_type==my_type)
return lcn;
}
}
}
break;
}
}
skip = 0;
attrib=(const ntfs_attribheader*)((const char*)attrib + attr_len);
}
return NULL;
}
const ntfs_attribheader* ntfs_findattribute(const ntfs_recordheader* record, uint32_t attrType, const char* end)
{
const ntfs_attribheader *attrib = ntfs_getattributeheaders(record);
return ntfs_searchattribute(attrib, attrType, end, 0);
}
const ntfs_attribheader* ntfs_nextattribute(const ntfs_attribheader* attrib, uint32_t attrType, const char* end)
{
return ntfs_searchattribute(attrib, attrType, end, 1);
}
const char* ntfs_getattributedata(const ntfs_attribresident* attrib, const char* end)
{
const char* data = ((const char*)attrib) + le16(attrib->offAttribData);
if(le16(attrib->offAttribData)+le32(attrib->cbAttribData) > le32(attrib->header.cbAttribute) ||
data > end)
return NULL;
return data;
}
long int ntfs_get_first_rl_element(const ntfs_attribnonresident *attrnr, const char* end)
{
/* return first element of the run_list */
/* buf must be unsigned! */
const unsigned char *buf;
uint8_t b; /* Current byte offset in buf. */
const unsigned char*attr_end; /* End of attribute. */
long lcn=0;
int64_t deltaxcn = (int64_t)-1; /* Change in [vl]cn. */
buf=(const unsigned char*)attrnr + le16(attrnr->offDataRuns);
attr_end = (const unsigned char*)attrnr + le32(attrnr->header.cbAttribute);
if((const char *)attr_end > end)
return 0;
b = *buf & 0xf;
if(b==0)
{
log_error("Missing length entry in mapping pairs array.\n");
return 0;
}
if (buf + b > attr_end)
{
log_error("Attribut AT_DATA: bad size\n");
return 0;
}
for (deltaxcn = (int8_t)buf[b--]; b; b--)
deltaxcn = (deltaxcn << 8) + (uint8_t)buf[b];
/* Assume a negative length to indicate data corruption */
if (deltaxcn < 0)
{
log_error("Invalid length in mapping pairs array.\n");
return 0;
}
if (!(*buf & 0xf0))
{
log_info("LCN_HOLE\n");
return 0;
}
{
/* Get the lcn change which really can be negative. */
const uint8_t b2 = *buf & 0xf;
b = b2 + ((*buf >> 4) & 0xf);
if (buf + b > attr_end)
{
log_error("Attribut AT_DATA: bad size\n");
return 0;
}
attr_record+=attr_len;
for (deltaxcn = (int8_t)buf[b--]; b > b2; b--)
deltaxcn = (deltaxcn << 8) + (uint8_t)buf[b];
/* Change the current lcn to it's new value. */
lcn += deltaxcn;
/* Check lcn is not below -1. */
if (lcn < -1) {
log_error("Invalid LCN < -1 in mapping pairs array.");
return 0;
}
return lcn;
}
}
static int ntfs_read_MFT(disk_t *disk_car, partition_t *partition, const struct ntfs_boot_sector*ntfs_header, const int my_type, const int verbose)
static void ntfs_get_volume_name(disk_t *disk_car, partition_t *partition, const struct ntfs_boot_sector*ntfs_header)
{
unsigned char *buffer;
char *attr;
uint64_t mft_pos;
unsigned int mft_record_size;
unsigned int mft_size;
mft_pos=partition->part_offset+(uint64_t)(le16(ntfs_header->reserved)+le64(ntfs_header->mft_lcn)*ntfs_header->sectors_per_cluster)*ntfs_sector_size(ntfs_header);
if(ntfs_header->clusters_per_mft_record>0)
mft_record_size=ntfs_header->sectors_per_cluster*ntfs_header->clusters_per_mft_record;
else
mft_record_size=1<<(-ntfs_header->clusters_per_mft_record);
/* Only need the first 4 MFT record */
mft_size=4*mft_record_size*ntfs_sector_size(ntfs_header);
mft_size=mft_record_size*ntfs_sector_size(ntfs_header);
mft_pos=partition->part_offset+(uint64_t)(le16(ntfs_header->reserved)+le64(ntfs_header->mft_lcn)*ntfs_header->sectors_per_cluster)*ntfs_sector_size(ntfs_header);
/* Record 3 = $Volume */
mft_pos+=3*mft_size;
#ifdef NTFS_DEBUG
log_debug("NTFS cluster size = %u\n",ntfs_header->sectors_per_cluster);
log_debug("NTFS MFT cluster = %lu\n",le64(ntfs_header->mft_lcn));
@ -419,28 +321,34 @@ static int ntfs_read_MFT(disk_t *disk_car, partition_t *partition, const struct
if(mft_size==0)
{
log_error("Invalid MFT record size or NTFS sector size\n");
return 1;
return;
}
buffer=(unsigned char *)MALLOC(mft_size);
if((unsigned)disk_car->pread(disk_car, buffer, mft_size, mft_pos) != mft_size)
{
log_error("NTFS: Can't read MFT\n");
free(buffer);
return 1;
return;
}
attr=(char*)buffer;
while(attr+0x30<=(char*)(buffer+mft_size))
{
int res=ntfs_get_attr(attr, my_type, partition, (char*)buffer+mft_size, verbose, NULL);
if((res>0)|| (NTFS_GETU32(attr + 0x1C)<0x30))
const ntfs_attribresident *attrib=(const ntfs_attribresident *)ntfs_findattribute((const ntfs_recordheader*)buffer, 0x60, (char*)buffer+mft_size);
if(attrib && attrib->header.bNonResident==0) /* attribute is resident */
{
free(buffer);
return res;
char *dest=partition->fsname;
const char *name_it;
unsigned int volume_name_length=le32(attrib->cbAttribData);
volume_name_length/=2; /* Unicode */
if(volume_name_length>sizeof(partition->fsname)-1)
volume_name_length=sizeof(partition->fsname)-1;
for(name_it=ntfs_getattributedata(attrib, (char*)(buffer+mft_size));
volume_name_length>0 && *name_it!='\0' && name_it[1]=='\0';
name_it+=2,volume_name_length--)
*dest++=*name_it;
*dest='\0'; /* 27 january 2003: Correct a bug found by Andreas du Plessis-Denz */
}
attr+= NTFS_GETU32(attr + 0x1C);
}
free(buffer);
return 0;
return;
}
int is_part_ntfs(const partition_t *partition)

View file

@ -56,6 +56,8 @@ struct ntfs_boot_sector {
uint16_t marker; /* 0x1FE */
} __attribute__ ((__packed__));
#define NTFS_Magic 0x454c4946 /* FILE */
struct ntfs_mft_record {
uint32_t magic; /* FILE */
uint16_t usa_ofs;
@ -73,12 +75,177 @@ struct ntfs_mft_record {
uint32_t mft_record_number; /* NTFS 3.1+ */
} __attribute__ ((__packed__));
typedef struct ntfs_mft_record ntfs_recordheader;
typedef struct _ntfs_attribheader
{
uint32_t type; /* Attribute Type (e.g. 0x10, 0x60) */
uint32_t cbAttribute; /* Length (including this header) */
uint8_t bNonResident; /* Non-resident flag */
uint8_t cName; /* Name length */
uint16_t offName; /* Offset to the Attribute */
uint16_t flags; /* Flags */
uint16_t idAttribute; /* Attribute Id (a) */
}
ntfs_attribheader;
typedef struct _ntfs_attribresident
{
ntfs_attribheader header;
uint32_t cbAttribData; /* Length of the Attribute */
uint16_t offAttribData; /* Offset to the Attribute */
uint8_t bIndexed; /* Indexed flag */
uint8_t padding; /* 0x00 Padding */
}
ntfs_attribresident;
typedef struct _ntfs_attribnonresident
{
ntfs_attribheader header;
uint64_t startVCN; /* Starting VCN */
uint64_t lastVCN; /* Last VCN */
uint16_t offDataRuns; /* Offset to the Data Runs */
uint16_t compUnitSize; /* Compression Unit Size (b) */
uint32_t padding; /* Padding */
uint64_t cbAllocated; /* Allocated size of the attribute (c) */
uint64_t cbAttribData; /* Real size of the attribute */
uint64_t cbInitData; /* Initialized data size of the stream (d) */
}
ntfs_attribnonresident;
/* The original definitions come from ntfs-3g/layout.h */
/**
* struct INDEX_HEADER -
*
* This is the header for indexes, describing the INDEX_ENTRY records, which
* follow the INDEX_HEADER. Together the index header and the index entries
* make up a complete index.
*
* IMPORTANT NOTE: The offset, length and size structure members are counted
* relative to the start of the index header structure and not relative to the
* start of the index root or index allocation structures themselves.
*/
typedef struct {
/* 0*/ uint32_t entries_offset;/* Byte offset from the INDEX_HEADER to first
INDEX_ENTRY, aligned to 8-byte boundary. */
/* 4*/ uint32_t index_length; /* Data size in byte of the INDEX_ENTRY's,
including the INDEX_HEADER, aligned to 8. */
/* 8*/ uint32_t allocated_size;/* Allocated byte size of this index (block),
multiple of 8 bytes. See more below. */
/*
For the index root attribute, the above two numbers are always
equal, as the attribute is resident and it is resized as needed.
For the index allocation attribute, the attribute is not resident
and the allocated_size is equal to the index_block_size specified
by the corresponding INDEX_ROOT attribute minus the INDEX_BLOCK
size not counting the INDEX_HEADER part (i.e. minus -24).
*/
/* 12*/ uint8_t ih_flags; /* Bit field of INDEX_HEADER_FLAGS. */
/* 13*/ uint8_t reserved[3]; /* Reserved/align to 8-byte boundary.*/
/* sizeof() == 16 */
} __attribute__((__packed__)) TD_INDEX_HEADER;
/**
* struct FILE_NAME_ATTR - Attribute: Filename (0x30).
*
* NOTE: Always resident.
* NOTE: All fields, except the parent_directory, are only updated when the
* filename is changed. Until then, they just become out of sync with
* reality and the more up to date values are present in the standard
* information attribute.
* NOTE: There is conflicting information about the meaning of each of the time
* fields but the meaning as defined below has been verified to be
* correct by practical experimentation on Windows NT4 SP6a and is hence
* assumed to be the one and only correct interpretation.
*/
typedef struct {
/*hex ofs*/
/* 0*/ uint64_t parent_directory; /* Directory this filename is
referenced from. */
/* 8*/ int64_t creation_time; /* Time file was created. */
/* 10*/ int64_t last_data_change_time; /* Time the data attribute was last
modified. */
/* 18*/ int64_t last_mft_change_time; /* Time this mft record was last
modified. */
/* 20*/ int64_t last_access_time; /* Last time this mft record was
accessed. */
/* 28*/ int64_t allocated_size; /* Byte size of on-disk allocated space
for the data attribute. So for
normal $DATA, this is the
allocated_size from the unnamed
$DATA attribute and for compressed
and/or sparse $DATA, this is the
compressed_size from the unnamed
$DATA attribute. NOTE: This is a
multiple of the cluster size. */
/* 30*/ int64_t data_size; /* Byte size of actual data in data
attribute. */
/* 38*/ uint32_t file_attributes; /* Flags describing the file. */
/* 3c*/ union {
/* 3c*/ struct {
/* 3c*/ uint16_t packed_ea_size; /* Size of the buffer needed to
pack the extended attributes
(EAs), if such are present.*/
/* 3e*/ uint16_t reserved; /* Reserved for alignment. */
} __attribute__((__packed__));
/* 3c*/ uint32_t reparse_point_tag; /* Type of reparse point,
present only in reparse
points and only if there are
no EAs. */
} __attribute__((__packed__));
/* 40*/ uint8_t file_name_length; /* Length of file name in
(Unicode) characters. */
/* 41*/ uint8_t file_name_type; /* Namespace of the file name.*/
/* 42*/ char *file_name[0]; /* File name in Unicode. */
} __attribute__((__packed__)) TD_FILE_NAME_ATTR;
/**
* struct INDEX_ROOT - Attribute: Index root (0x90).
*
* NOTE: Always resident.
*
* This is followed by a sequence of index entries (INDEX_ENTRY structures)
* as described by the index header.
*
* When a directory is small enough to fit inside the index root then this
* is the only attribute describing the directory. When the directory is too
* large to fit in the index root, on the other hand, two additional attributes
* are present: an index allocation attribute, containing sub-nodes of the B+
* directory tree (see below), and a bitmap attribute, describing which virtual
* cluster numbers (vcns) in the index allocation attribute are in use by an
* index block.
*
* NOTE: The root directory (FILE_root) contains an entry for itself. Other
* directories do not contain entries for themselves, though.
*/
typedef struct {
/* 0*/ uint32_t type; /* Type of the indexed attribute. Is
$FILE_NAME for directories, zero
for view indexes. No other values
allowed. */
/* 4*/ uint32_t collation_rule; /* Collation rule used to sort the
index entries. If type is $FILE_NAME,
this must be COLLATION_FILE_NAME. */
/* 8*/ uint32_t index_block_size; /* Size of index block in bytes (in
the index allocation attribute). */
/* 12*/ int8_t clusters_per_index_block;/* Size of index block in clusters (in
the index allocation attribute), when
an index block is >= than a cluster,
otherwise sectors per index block. */
/* 13*/ uint8_t reserved[3]; /* Reserved/align to 8-byte boundary. */
/* 16*/ TD_INDEX_HEADER index; /* Index header describing the
following index entries. */
/* sizeof()= 32 bytes */
} __attribute__((__packed__)) TD_INDEX_ROOT;
int check_NTFS(disk_t *disk_car,partition_t *partition,const int verbose,const int dump_ind);
int log_ntfs2_info(const struct ntfs_boot_sector *nh1, const struct ntfs_boot_sector *nh2);
int log_ntfs_info(const struct ntfs_boot_sector *ntfs_header);
int is_ntfs(const partition_t *partition);
int is_part_ntfs(const partition_t *partition);
int ntfs_get_attr(const char *mft_record, const int my_type, partition_t *partition, const char *end, const int verbose, const char*file_name_to_find);
int recover_NTFS(disk_t *disk_car, const struct ntfs_boot_sector*ntfs_header,partition_t *partition,const int verbose, const int dump_ind, const int backup);
int test_NTFS(const disk_t *disk_car,const struct ntfs_boot_sector*ntfs_header, partition_t *partition,const int verbose, const int dump_ind);
#define NTFS_GETU8(p) (*(const uint8_t*)(p))
@ -87,6 +254,11 @@ int test_NTFS(const disk_t *disk_car,const struct ntfs_boot_sector*ntfs_header,
#define NTFS_GETU64(p) (le64(*(const uint64_t*)(p)))
unsigned int ntfs_sector_size(const struct ntfs_boot_sector *ntfs_header);
int rebuild_NTFS_BS(disk_t *disk_car,partition_t *partition, const int verbose, const int interface, const unsigned int expert, char**current_cmd);
const ntfs_attribheader *ntfs_getattributeheaders(const ntfs_recordheader* record);
const ntfs_attribheader* ntfs_findattribute(const ntfs_recordheader* record, uint32_t attrType, const char* end);
const ntfs_attribheader* ntfs_nextattribute(const ntfs_attribheader* attrib, uint32_t attrType, const char* end);
const char* ntfs_getattributedata(const ntfs_attribresident* attrib, const char* end);
long int ntfs_get_first_rl_element(const ntfs_attribnonresident *attrnr, const char* end);
#ifdef __cplusplus
} /* closing brace for extern "C" */

View file

@ -361,28 +361,37 @@ static int create_ntfs_boot_sector(disk_t *disk_car, partition_t *partition, con
static int read_mft_info(disk_t *disk_car, partition_t *partition, const uint64_t mft_sector, const int verbose, unsigned int *sectors_per_cluster, uint64_t *mft_lcn, uint64_t *mftmirr_lcn, unsigned int *mft_record_size)
{
char buffer[8*DEFAULT_SECTOR_SIZE];
const char *attr=buffer;
const struct ntfs_mft_record *record=(const struct ntfs_mft_record *)buffer;
const ntfs_attribnonresident *attr80;
if(disk_car->pread(disk_car, &buffer, sizeof(buffer), partition->part_offset + (uint64_t)mft_sector * disk_car->sector_size) != sizeof(buffer))
{
display_message("NTFS: Can't read mft_sector\n");
return 1;
}
*mft_lcn=ntfs_get_attr(attr, 0x80, partition, buffer+sizeof(buffer), verbose, NULL);
*mft_record_size=NTFS_GETU32(attr + 0x1C);
*mft_record_size=le32(record->bytes_allocated);
if(*mft_record_size==0)
{
if(verbose>0)
log_warning("read_mft_info failed: mft_record_size=0\n");
return 2;
}
attr+= NTFS_GETU32(attr + 0x1C);
if(attr < buffer || attr > buffer+sizeof(buffer))
attr80=(const ntfs_attribnonresident *)ntfs_findattribute(record, 0x80, buffer+sizeof(buffer));
if(attr80 && attr80->header.bNonResident)
{
*mft_lcn=ntfs_get_first_rl_element(attr80, buffer+sizeof(buffer));
}
record=(const struct ntfs_mft_record *)(buffer + (*mft_record_size));
if((const char *)record< buffer || (const char *)record> buffer+sizeof(buffer))
{
if(verbose<0)
log_warning("read_mft_info failed: bad record.\n");
return 2;
}
*mftmirr_lcn=ntfs_get_attr(attr, 0x80, partition,buffer+sizeof(buffer), verbose, NULL);
attr80=(const ntfs_attribnonresident *)ntfs_findattribute(record, 0x80, buffer+sizeof(buffer));
if(attr80 && attr80->header.bNonResident)
{
*mftmirr_lcn=ntfs_get_first_rl_element(attr80, buffer+sizeof(buffer));
}
/* Try to divide by the biggest number first */
if(*mft_lcn<*mftmirr_lcn)
{
@ -475,19 +484,35 @@ int rebuild_NTFS_BS(disk_t *disk_car, partition_t *partition, const int verbose,
}
#endif
/* try to find MFT Backup first */
for(sector=(partition->part_size/disk_car->sector_size/2-20>0?partition->part_size/disk_car->sector_size/2-20:1);(sector<partition->part_size/disk_car->sector_size)&&(sector<=partition->part_size/disk_car->sector_size/2+20)&&(ind_stop==0);sector++)
for(sector=(partition->part_size/disk_car->sector_size/2-20>0?partition->part_size/disk_car->sector_size/2-20:1);
sector<partition->part_size/disk_car->sector_size &&
sector<=partition->part_size/disk_car->sector_size/2+20 &&
ind_stop==0;
sector++)
{
if(disk_car->pread(disk_car, &buffer, 2 * DEFAULT_SECTOR_SIZE, partition->part_offset + sector * (uint64_t)disk_car->sector_size) == 2 * DEFAULT_SECTOR_SIZE)
if(disk_car->pread(disk_car, &buffer, 0x400, partition->part_offset + sector * (uint64_t)disk_car->sector_size) == 0x400)
{
if(memcmp(buffer,"FILE",4)==0 && (NTFS_GETU16(buffer+ 0x14)%8==0) && (NTFS_GETU16(buffer+ 0x14)>=42)
&&(NTFS_GETU16(buffer+22)==1)) /* MFT_RECORD_IN_USE */
const struct ntfs_mft_record *record=(const struct ntfs_mft_record *)&buffer;
if(memcmp(buffer,"FILE",4)==0 &&
le16(record->attrs_offset)%8==0 &&
le16(record->attrs_offset)>=42 &&
le16(record->flags)==1) /* MFT_RECORD_IN_USE */
{
int res;
res=ntfs_get_attr(buffer, 0x30, partition, buffer+2*DEFAULT_SECTOR_SIZE, verbose, "$MFT");
const ntfs_attribheader *attr30;
int res=0;
attr30=ntfs_findattribute(record, 0x30, buffer+0x400);
if(attr30 && attr30->bNonResident==0)
{
const TD_FILE_NAME_ATTR *file_name_attr=(const TD_FILE_NAME_ATTR *)ntfs_getattributedata((const ntfs_attribresident *)attr30, buffer+0x400);
if(file_name_attr->file_name_length==4 &&
(const char*)&file_name_attr->file_name[0]+8 <= buffer+0x400 &&
memcmp(file_name_attr->file_name,"$\0M\0F\0T\0", 8)==0)
res=1;
}
if(res==1)
{
int tmp;
log_info("mft at %lu, seq=%u, main=%u res=%d\n",(long unsigned)sector,NTFS_GETU8(buffer+0x10),(unsigned int)NTFS_GETU32(buffer+0x20),res);
log_info("mft at %lu\n",(long unsigned)sector);
tmp=read_mft_info(disk_car, partition, sector, verbose, &sectors_per_cluster, &mft_lcn, &mftmirr_lcn, &mft_record_size);
if(tmp==0)
{
@ -532,16 +557,29 @@ int rebuild_NTFS_BS(disk_t *disk_car, partition_t *partition, const int verbose,
}
}
#endif
if(disk_car->pread(disk_car, &buffer, 2 * DEFAULT_SECTOR_SIZE, partition->part_offset + sector * (uint64_t)disk_car->sector_size) == 2 * DEFAULT_SECTOR_SIZE)
if(disk_car->pread(disk_car, &buffer, 0x400, partition->part_offset + sector * (uint64_t)disk_car->sector_size) == 0x400)
{
if(memcmp(buffer,"FILE",4)==0 && (NTFS_GETU16(buffer+ 0x14)%8==0) && (NTFS_GETU16(buffer+ 0x14)>=42))
const struct ntfs_mft_record *record=(const struct ntfs_mft_record *)&buffer;
if(memcmp(buffer,"FILE",4)==0 &&
le16(record->attrs_offset)%8==0 &&
le16(record->attrs_offset)>=42 &&
le16(record->flags)==1) /* MFT_RECORD_IN_USE */
{
int res;
res=ntfs_get_attr(buffer, 0x30, partition, buffer+2*DEFAULT_SECTOR_SIZE, verbose, "$MFT");
const ntfs_attribheader *attr30;
int res=0;
attr30=ntfs_findattribute(record, 0x30, buffer+0x400);
if(attr30 && attr30->bNonResident==0)
{
const TD_FILE_NAME_ATTR *file_name_attr=(const TD_FILE_NAME_ATTR *)ntfs_getattributedata((const ntfs_attribresident *)attr30, buffer+0x400);
if(file_name_attr->file_name_length==4 &&
(const char*)&file_name_attr->file_name[0]+8 <= buffer+0x400 &&
memcmp(file_name_attr->file_name,"$\0M\0F\0T\0", 8)==0)
res=1;
}
if(res==1)
{
int tmp;
log_info("mft at %lu, seq=%u, main=%u res=%d\n",(long unsigned)sector,NTFS_GETU8(buffer+0x10),(unsigned int)NTFS_GETU32(buffer+0x20),res);
log_info("mft at %lu\n", (long unsigned)sector);
tmp=read_mft_info(disk_car, partition, sector, verbose, &sectors_per_cluster, &mft_lcn, &mftmirr_lcn, &mft_record_size);
if(tmp==0)
{
@ -617,6 +655,8 @@ int rebuild_NTFS_BS(disk_t *disk_car, partition_t *partition, const int verbose,
/* TODO read_mft_info(partition,sector,*sectors_per_cluster,*mft_lcn,*mftmirr_lcn,*mft_record_size); */
if(sectors_per_cluster>0 && mft_record_size>0)
{
// 0x90 AT_INDEX_ROOT
const ntfs_attribheader *attr90;
unsigned int index_block_size=4096;
log_info("ntfs_find_mft: sectors_per_cluster %u\n",sectors_per_cluster);
log_info("ntfs_find_mft: mft_lcn %lu\n",(long unsigned int)mft_lcn);
@ -628,8 +668,14 @@ int rebuild_NTFS_BS(disk_t *disk_car, partition_t *partition, const int verbose,
display_message("NTFS Can't read \"root directory\" in MFT\n");
return 1;
}
index_block_size=ntfs_get_attr(buffer, 0x90, partition, buffer+mft_record_size, verbose, NULL);
if(index_block_size%512!=0)
attr90=ntfs_findattribute((const ntfs_recordheader*)buffer, 0x90, buffer+mft_record_size);
if(attr90 && attr90->bNonResident==0)
{
const TD_INDEX_ROOT *index_root=(const TD_INDEX_ROOT *)ntfs_getattributedata((const ntfs_attribresident *)attr90, buffer+mft_record_size);
if(index_root)
index_block_size=le32(index_root->index_block_size);
}
if(index_block_size%512!=0 || index_block_size==0)
index_block_size=4096;
log_info("ntfs_find_mft: index_block_size %u\n",index_block_size);
create_ntfs_boot_sector(disk_car,partition, interface, sectors_per_cluster*disk_car->sector_size, mft_lcn, mftmirr_lcn, mft_record_size, index_block_size,current_cmd);