1、引言
NTFS文件系统的的工作流程主要依赖于两个部分,文件记录部分用于记录文件本身的信息,而索引部分则通过树的形式存储着文件系统的结构信息。
而无论是文件记录还是索引,NTFS文件系统都为其设计了标准的数据结构,便于解析。
2、结构
从结构层面上说,无论文件记录还是索引,其本身的结构都相对简单,如下所示:
| 文件记录头 | 属性列表 | 属性列表 |… …| 结束标志 |
| 索引记录头 | 索引头 | 索引数据 | … … | 结束标志 |文章来源:https://www.toymoban.com/news/detail-743795.html
// File Record Layout
// Record Header
// Attribute
// Attribute
// ...
// End Marker (0xFFFFFFFF)
typedef struct {
b32 magic; // MFT标志,始终为"FILE"
ub16 usn_offset; // 更新序列号USN(update sequence number)偏移
ub16 usn_size; // 更新序列号数组大小,单位:word
b64 lsn; // 日志文件序列号($LogFile Sequence Number,LSN)
ub16 sn; // 序列号(used times of this record)
ub16 hard_links; // 硬链接数
ub16 attr_offset; // 第一个属性列表的偏移
ub16 flags; // 标志(Flag),00H表示文件被删除,01H表示文件正在使用,02H表示目录被删除,03H表示目录正在使用
b32 used; // 文件记录实际使用的长度,可以理解为文件大小
b32 alloced; // 文件记录分配的长度,可以理解为磁盘占用大小
b64 record_ref; // 基本文件记录的文件索引号。当一个文件存在0x20属性列表时,用于区分是否主文件记录。
ub16 next_attr; // 下一属性ID
ub16 border; // 填充
b32 record_number; // 文件记录的参考号
} FileRecordHeader;
typedef struct {
// Index Record Header
b32 magic; // 索引记录标志,始终为"INDX"
ub16 usn_offset; // 更新序列号USN(update sequence number)偏移
ub16 usn_size; // 更新序列号数组大小,单位:word
b64 lsn; // 日志文件序列号
b64 vcn; // 虚拟簇号
// Index Header
b32 entry_offset; // 第一个索引的偏移
b32 total_size; // 所有索引的实际大小
b32 alloc_size; // 所有索引的分配大小
b8 flags; // 标志(Flag),1表示存在子节点,2表示叶子节点
b8 padding[3]; // 填充
} IndexBlock;
3、更新序列号(USN)
NTFS文件系统为了保持数据的一致性,在文件和索引记录的每个扇区的最后2个字节都会写入更新序列号。正常情况下,不需要考虑这种特殊情况。
但是,当一个记录的恰巧写到扇区最后2个字节时,由于扇区尾部已经写入了2个字节的更新序列号。因此,NTFS文件系统会将本应写入扇区尾部的2个字节记录到文件记录的USN数组中。所以当需要解析文件记录时,我们需要将USN数组中的内容还原到对应的位置上去。文章来源地址https://www.toymoban.com/news/detail-743795.html
bool CFileRecord::PatchUS(b16 *sector_pointer, b32 sector_num, b16 usn,
b16 *us_data) const {
// 遍历文件激烈的所有扇区
for (b32 i = 0; i < sector_num; i++) {
// 偏移到扇区的最后一个字
sector_pointer += ((ntfs_volume_->sector_size_ >> 1) - 1);
// 检测更新序列号是否一致
if (*sector_pointer != usn) return false; // USN check
// 还原数据
*sector_pointer = us_data[i]; // set data to update us
sector_pointer++;
}
return true;
}
// 当读取到文件记录时
if (frh->magic == kFileRecordMagic) {
// 计算出USN数组的位置
b16 *usn_addr = (b16 *)((b8 *)frh + frh->usn_offset);
// 第一个字存储USN
b16 usn = *usn_addr;
// 第二个字开始存储实际数据
b16 *usarray = usn_addr + 1;
if (PatchUS((b16 *)frh,
ntfs_volume_->mft_size_ / ntfs_volume_->sector_size_, usn,
usarray)) {
file_record_ = frh;
return true;
}
}
到了这里,关于NTFS文件系统解析(二)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!