TestDisk: replace ntfs_get_attr() by several functions to parse MFT record
This commit is contained in:
parent
db079c0e26
commit
94f6943be7
3 changed files with 368 additions and 242 deletions
350
src/ntfs.c
350
src/ntfs.c
|
@ -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)
|
||||
|
|
174
src/ntfs.h
174
src/ntfs.h
|
@ -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" */
|
||||
|
|
|
@ -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, §ors_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, §ors_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);
|
||||
|
|
Loading…
Reference in a new issue