From 94f6943be7eb1a54b2013a94844dfe4667c2d9f4 Mon Sep 17 00:00:00 2001 From: Christophe Grenier Date: Sat, 31 Aug 2013 19:23:28 +0200 Subject: [PATCH] TestDisk: replace ntfs_get_attr() by several functions to parse MFT record --- src/ntfs.c | 350 ++++++++++++++++++------------------------------- src/ntfs.h | 174 +++++++++++++++++++++++- src/ntfs_adv.c | 86 +++++++++--- 3 files changed, 368 insertions(+), 242 deletions(-) diff --git a/src/ntfs.c b/src/ntfs.c index 957fd3ef..342808cc 100644 --- a/src/ntfs.c +++ b/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;i1) - { - 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) diff --git a/src/ntfs.h b/src/ntfs.h index a4b380a0..7d51b62b 100644 --- a/src/ntfs.h +++ b/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" */ diff --git a/src/ntfs_adv.c b/src/ntfs_adv.c index 1877ee29..9cadaae4 100644 --- a/src/ntfs_adv.c +++ b/src/ntfs_adv.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);(sectorpart_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); + sectorpart_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);