本帖最后由 swlings 于 2019-3-18 11:57 编辑
NTFS是一种日志事务型文件系统,其工作原理就不多说了,感兴趣的可以参考《深入解析windows操作系统》第四版的第12章,这篇重点在于解析$LogFile这个文件的数据结构。言归正传,那么为什么是“浅析”呢?因为不够深。。。咳咳咳,无意开车。。。
NTFS的日志记录分为两个组成部分,其中包含NTFS自身以及LFS日志文件系统。如果说NTFS是日志的作者,那么LFS就是执笔人,一个生产,一个记录。
$LogFile的主要功能就是做事务管理,用于保证NTFS的可恢复性,类似于数据库的事务管理。
$LogFile文件的大小一般为64M,但不固定,分区较小的情况下可能小于64M,在不同版本中$LogFile的组成略有不同,早期版本是2+n的形式,较新的是2+32+n的形式。
第一个2表示的是文件的前2页,也叫重启动区域,第2个区域是最后一次更新的备份区域,用于最后一次更新发生错误时用于恢复的。
最后一块区域是循环日志区域,该区域是环形循环写入,类似于ringbuffer,用SequenceNumber记录循环次数。
日志记录空间分配用页计数,类似于文件系统的簇,页的大小一般为4096Bytes .
先来说说RestartPage,RestartPage占用了日志文件的第一页和第二页,其中第二页是第一页的备份(实际好像并不一样)。
暂时我只分析第一页,结构如下图:
下面是部分源码,但这个是1.0版本(日志版本),在数据结构上和现在的2.0版本还是有些区别的。
[C++] 纯文本查看 复制代码 //
// Log Restart Page Header. This structure is at the head of the restart
// areas in a log file.
//
typedef struct _LFS_RESTART_PAGE_HEADER {
//
// Cache multisector protection header.
//
MULTI_SECTOR_HEADER MultiSectorHeader;
//
// This is the last Lsn found by checkdisk for this volume.
//
LSN ChkDskLsn;
//
// System page size. This is the page size of the system which
// initialized the log file. Unless the log file has been gracefully
// shutdown (there are no clients with restart areas), it is a fatal
// error to attempt to write to a log file on a system with a differen
// page size.
//
ULONG SystemPageSize;
//
// Log Page Size. This is the log page size used for this log file.
// The entire Lfs restart area must fit on a single log page.
//
ULONG LogPageSize;
//
// Lfs restart area offset. This is the offset from the start of this
// structure to the Lfs restart area.
//
USHORT RestartOffset;
//
// The indicates major and minor versions. Note that the pre-release versions
// have -1 in both positions. Major version 0 indicates the transition
// from Beta to USA support.
//
// Major Version
//
// -1 Beta Version
// 0 Transition
// 1 Update sequence support.
//
SHORT MinorVersion;
SHORT MajorVersion;
//
// Update Sequence Array. Used to protect the page block.
//
UPDATE_SEQUENCE_ARRAY UpdateSequenceArray;
} LFS_RESTART_PAGE_HEADER, *PLFS_RESTART_PAGE_HEADER;
我们从RestartPageHeader开始解释其中的结构以及各个成员变量的意义。
前面4个字节是标识 RSTR
紧接着就是更新序列数组的偏移和大小,占用4个字节,更新序列数组是用来确定每页的数据写入是否完整。
接着是ChkDskLsn,占用8个字节,这个是磁盘自检的时候才会用到。
接着是系统分页大小和日志分页大小,各占4个字节,值都是4096。
接着就是数据区的偏移量了,占用4个字节。
后面跟着日志系统的版本号,占用4个字节,高2个字节表示主版本号,低2个字节表示次版本号。
版本号后面跟着的就是更新序列数组了,其中前两个字节表示该页修改的次数,后面2 * 8个字节分别是该页的每个512字节的最后两个字节,而每个512字节的最后两个字节被最开始的两个字节(修改次数)替换,所以解析的时候需要还原回去。
[C++] 纯文本查看 复制代码 //
// Lfs Restart Area. Two copies of these will exist at the beginning of the
// log file.
//
typedef struct _LFS_RESTART_AREA {
//
// Current Lsn. This is periodic snapshot of the current logical end of
// log file to facilitate restart.
//
LSN CurrentLsn;
//
// Number of Clients. This is the maximum number of clients supported
// for this log file.
//
USHORT LogClients;
//
// The following are indexes into the client record arrays. The client
// records are linked into two lists. A free list of client records and
// an in-use list of records.
//
USHORT ClientFreeList;
USHORT ClientInUseList;
//
// Flag field.
//
// RESTART_SINGLE_PAGE_IO All log pages written 1 by 1
//
USHORT Flags;
//
// The following is the number of bits to use for the sequence number.
//
ULONG SeqNumberBits;
//
// Length of this restart area.
//
USHORT RestartAreaLength;
//
// Offset from the start of this structure to the client array.
// Ignored in versions prior to 1.1
//
USHORT ClientArrayOffset;
//
// Usable log file size. We will stop sharing the value in the page header.
//
LONGLONG FileSize;
//
// DataLength of last Lsn. This doesn't include the length of
// the Lfs header.
//
ULONG LastLsnDataLength;
//
// The following apply to log pages. This is the log page data offset and
// the length of the log record header. Ignored in versions prior to 1.1
//
USHORT RecordHeaderLength;
USHORT LogPageDataOffset;
//
// Client data.
//
LFS_CLIENT_RECORD LogClientArray[1];
} LFS_RESTART_AREA, *PLFS_RESTART_AREA;
[C++] 纯文本查看 复制代码 //
// Log Client Record. A log client record exists for each client user of
// the log file. One of these is in each Lfs restart area.
//
#define LFS_NO_CLIENT 0xffff
#define LFS_CLIENT_NAME_MAX 64
typedef struct _LFS_CLIENT_RECORD {
//
// Oldest Lsn. This is the oldest Lsn that this client requires to
// be in the log file.
//
LSN OldestLsn;
//
// Client Restart Lsn. This is the Lsn of the latest client restart
// area written to the disk. A reserved Lsn will indicate that no
// restart area exists for this client.
//
LSN ClientRestartLsn;
//
//
// Previous/Next client area. These are the indexes into an array of
// Log Client Records for the previous and next client records.
//
USHORT PrevClient;
USHORT NextClient;
//
// Sequence Number. Incremented whenever this record is reused. This
// will happen whenever a client opens (reopens) the log file and has
// no current restart area.
USHORT SeqNumber;
//
// Alignment field.
//
USHORT AlignWord;
//
// Align the entire record.
//
ULONG AlignDWord;
//
// The following fields are used to describe the client name. A client
// name consists of at most 32 Unicode character (64 bytes). The Log
// file service will treat client names as case sensitive.
//
ULONG ClientNameLength;
WCHAR ClientName[LFS_CLIENT_NAME_MAX];
} LFS_CLIENT_RECORD, *PLFS_CLIENT_RECORD;
(太多了。。。未完待续) |