神舟战神贴吧:什么是NTFS分区?

来源:百度文库 编辑:高考问答 时间:2024/05/10 06:32:49

您好
NTFS 是 Windows NT 操作环境和 Windows NT 高级服务器网络操作系统环境的文件系统。NTFS 的目标是提供:可靠性,通过可恢复能力(事件跟踪)和热定位的容错特征实现;增加功能性的一个平台;对 POSIX 需求的支持;消除 FAT 和 HPFS 文件系统中的限制。
NTFS 提供长文件名、数据保护和恢复,并通过目录和文件许可实现安全性。NTFS 支持大硬盘和在多个硬盘上存储文件(称为跨越分区)。例如,一个大公司的数据库可能大得必须跨越不同的硬盘。NTFS 提供内置安全性特征,它控制文件的隶属关系和访问。从 DOS 或其他操作系统上不能访问 NTFS 分区上的文件。这是Windows NT 安全性系统的一部分,但是,只有在使用 NTFS 时才是这样。
NTFS 允许文件名的长度可达 256 个字符。虽然 DOS 用户不能访问 NTFS 分区,但是 NTFS 文件可以拷贝到 DOS 分区。每个 NTFS 文件包含一个可被 DOS 文件名格式认可的 DOS 可读文件名。这个文件名是 NTFS 从长文件名的开始字符中产生的。

NTFS是Windows NT引入的新型文件系统,它具有许多新特性。本文旨在探索NTFS的底层结构,所叙述的也仅是文件在NTFS卷上的分布。NTFS中,卷中所有存放的数据均在一个叫$MFT的文件中,叫主文件表(Master File Table)。而$MFT则由文件记录(File Record)数组构成。File Record的大小一般是固定的,通常情况下均为1KB,这个概念相当于Linux中的inode。File Record在$MFT文件中物理上是连续的,且从0开始编号。$MFT仅供File System本身组织、架构文件系统使用,这在NTFS中称为元数据(Metadata)。以下列出Windows 2000 Release出的NTFS的元数据文件(我将要给出的示例代码的部分输出结果)。
File Record(inode) FileName
------------------ --------
0 $MFT
1 $MFTMirr
2 $LogFile
3 $Volume
4 $AttrDef
5 .
6 $Bitmap
7 $Boot
8 $BadClus
9 $Secure
10 $UpCase
11 $Extend

Windows 2000中不能使用dir命令(甚至加上/ah参数)像普通文件一样列出这些元数据文件。实际上File System Driver(ntfs.sys)维护了一个系统变量NtfsProtectSystemFiles用于隐藏这些元数据。默认情况下,这个变量被设为TRUE,所以使用dir /ah将得不到任何文件。知道这个行为后使用i386kd修改NtfsProtectSystemFiles后即可以列出元数据文件:

kd> x ntfs!NtfsProtect*
fe213498 Ntfs!NtfsProtectSystemFiles
fe21349c Ntfs!NtfsProtectSystemAttributes
kd> dd ntfs!NtfsProtectSystemFiles l 2
fe213498 00000001 00000001
kd> ed ntfs!NtfsProtectSystemFiles 0
kd> dd ntfs!NtfsProtectSystemFiles l 2
fe213498 00000000 00000001
kd>

D:\>ver

Microsoft Windows 2000 [Version 5.00.2195]

D:\>dir /ah $*
驱动器 D 中的卷是 W2KNTFS
卷的序列号是 E831-9D04

D:\ 的目录

2000-04-27 19:31 36,000 $AttrDef
2000-04-27 19:31 0 $BadClus
2000-04-27 19:31 67,336 $Bitmap
2000-04-27 19:31 8,192 $Boot
2000-04-27 19:31 <DIR> $Extend
2000-04-27 19:31 13,139,968 $LogFile
2000-04-27 19:31 27,575,296 $MFT
2000-04-27 19:31 4,096 $MFTMirr
2000-04-27 19:31 131,072 $UpCase
2000-04-27 19:31 0 $Volume
9 个文件 40,961,960 字节
1 个目录 51,863,552 可用字节

需要指出的是ntfs.sys将元数据文件以一种特殊的方式打开,所以在打开NtfsProtectSystemFiles后,如果使用ReadFile等产生IRP_MJ_READ等IRP包时将会导致Page Fault(详见Gary Nebbett的《Windows NT/2000 Native API Reference》)。

以上的讨论均是基于$MFT文件而讨论的,即基于$MFT中的File Record(inode)讨论的。为更好的继续以下的讨论,这儿我列出File Record Header的结构:

typedef struct {
ULONG Type;
USHORT UsaOffset;
USHORT UsaCount;
USN Usn;
} NTFS_RECORD_HEADER, *PNTFS_RECORD_HEADER;

typedef struct {
NTFS_RECORD_HEADER Ntfs;
USHORT SequenceNumber;
USHORT LinkCount;
USHORT AttributesOffset;
USHORT Flags; // 0x0001 = InUse, 0x0002 = Directory
ULONG BytesInUse;
ULONG BytesAllocated;
ULONGLONG BaseFileRecord;
USHORT NextAttributeNumber;
} FILE_RECORD_HEADER, *PFILE_RECORD_HEADER;

下面我将讨论如何定位$MFT。稍微有点操作系统知识的人都会知道引导扇区(Boot Sector),其物理位置为卷中的第一个扇区。以下由dskprobe.exe(Windows 2000 Resource Kit中的一个小工具)分析的第一个扇区(当然也可以使用WinHex等其他应用程序):

file: d:\Sector00.bin
Size: 0x00000200 (512)

Address | 00 01 02 03-04 05 06 07 : 08 09 0A 0B-0C 0D 0E 0F | 0123456789ABCDEF
---------|-------------------------:-------------------------|-----------------
00000000 | EB 52 90 4E-54 46 53 20 : 20 20 20 00-02 08 00 00 | ?R?NTFS .....
00000010 | 00 00 00 00-00 F8 00 00 : 3F 00 F0 00-3F 00 00 00 | .....?..?.e.?...
00000020 | 00 00 00 00-80 00 80 00 : 90 C0 41 00-00 00 00 00 | ....€.€.恼A.....
00000030 | 04 00 00 00-00 00 00 00 : 09 1C 04 00-00 00 00 00 | ................
00000040 | F6 00 00 00-01 00 00 00 : 04 9D 31 E8-BB 31 E8 94 | ?.......?杌1钄
. .
. .
. .
000001F0 | 00 00 00 00-00 00 00 00 : 83 A0 B3 C9-00 00 55 AA | ........儬成..U?

这512字节为如下的格式:(摘自Gary Nebbett书中,本文许多代码均来自或参考此书。)

#pragma pack(push, 1)

typedef struct {
UCHAR Jump[3];
UCHAR Format[8];
USHORT BytesPerSector;
UCHAR SectorsPerCluster;
USHORT BootSectors;
UCHAR Mbz1;
USHORT Mbz2;
USHORT Reserved1;
UCHAR MediaType;
USHORT Mbz3;
USHORT SectorsPerTrack;
USHORT NumberOfHeads;
ULONG PartitionOffset;
ULONG Reserved2[2];
ULONGLONG TotalSectors;
ULONGLONG MftStartLcn;
ULONGLONG Mft2StartLcn;
ULONG ClustersPerFileRecord;
ULONG ClustersPerIndexBlock;
ULONGLONG VolumeSerialNumber;
UCHAR Code[0x1AE];
USHORT BootSignature;
} BOOT_BLOCK, *PBOOT_BLOCK;

#pragma pack(pop)

各个字段的详细意义从字段名中即可大致清楚。在linux-ntfs的GNU工程(http://sf.net/projects/linux-ntfs)中也有详细的文档,限于篇幅我不将其列出。可以使用如下代码读出卷中的第一个扇区:

hVolume = CreateFile(drive, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
OPEN_EXISTING, 0, 0);

ReadFile(hVolume, &bootb, sizeof(bootb), &n, 0);

bootb是一个BOOT_BLOCK结构,在我的卷中如下格式(请对应Sector00.bin分析):

Dump BootBlock at below:
BytesPerSector:200
SectorsPerCluster:8
BootSectors:0
SectorsPerTrack:3F
NumberOfHeads:F0
PartitionOffset:3F
TotalSectors:41C090
MftStartLcn:4
Mft2StartLcn:41C09
ClustersPerFileRecord:F6
ClustersPerIndexBlock:1
VolumeSerialNumber:E8319D04
BootSignature:AA55

以上的MftStartLcn其实是$MFT在卷中的簇(Cluster)号。簇是NTFS的基本单位,最小单位。一个只有1Byte的文件也要占用一簇的空间。NTFS使用LCN(Logical Cluster Number)来代表NTFS卷中的物理位置,其简单的从0到卷中的总簇数减一进行编号。对于一个特定的文件NTFS则使用VCN(Virtual Cluster Number)来映射LCN实现文件的组织。从MftStartLcn的值4可以知道$MFT的LCN为4与SectorsPerCluster、BytesPerSector的大小即可定位$MFT的位置。得到$MFT的位置后,如果遍历$MFT中所有的File Record即可以得到卷中所有的文件列表(前面已经提到File Record只是简单的从0开始编号)。也就是说到目前为止已经可以对文件组织有最简单的认识,但如何得到文件的信息呢,如文件名等等。NTFS中所有文件包括普通的用户文件、元数据文件均用同样的方式组织数据、属性等。我将nfi.exe(来自Windows NT/2000 OEM Support Tools)的输出结果列出,作为我叙述的开始:

D:\>copy con file
testforntfs^Z
已复制 1 个文件。

D:\>nfi d:\file
NTFS File Sector Information Utility.
Copyright (C) Microsoft Corporation 1999. All rights reserved.

\file
$STANDARD_INFORMATION (resident)
$FILE_NAME (resident)
$DATA (resident)

D:\>echo testforattr>file:ATTR

D:\>nfi d:\file
NTFS File Sector Information Utility.
Copyright (C) Microsoft Corporation 1999. All rights reserved.

\file
$STANDARD_INFORMATION (resident)
$FILE_NAME (resident)
$DATA (resident)
$DATA ATTR (resident)

nfi的输出结果$STANDARD_INFORMATION、$FILE_NAME、$DATA等在NTFS中称为属性(Attribute)。属性分为常驻属性(Resident Attribute)与非常驻属性(Nonresident Attribute)。文件的数据也包含在属性中,似乎与属性这个名称有点混谣。不过这又让NTFS有了更加统一的组织文件的形式。这也同时让NTFS有MultiStreams的特性(上面也演示了这个特性)。通过指定的File Record定位给定的Attribute的实现代码如下:

template <class T1, class T2> inline
T1* Padd(T1* p, T2 n) { return (T1*)((char *)p + n); }

PATTRIBUTE FindAttribute(PFILE_RECORD_HEADER file,
ATTRIBUTE_TYPE type, PWSTR name)
{
for (PATTRIBUTE attr = PATTRIBUTE(Padd(file, file->AttributesOffset));
attr->AttributeType != -1;
attr = Padd(attr, attr->Length)) {

if (attr->AttributeType == type) {
if (name == 0 && attr->NameLength == 0) return attr;

if (name != 0 && wcslen(name) == attr->NameLength
&& _wcsicmp(name, PWSTR(Padd(attr, attr->NameOffset))) == 0) return attr;
}
}

return 0;
}

Gary Nebbett提供的这个FindAttribute函数在Attribute name(即第三个参数)不为空串时可能会出现bug,主要原因是_wcsicmp对UNICODE字符串比较时应该是以\0结束的标准的C字符串。我在提供的代码中已经纠正了这个错误。

下面我将通过使用SoftICE来分析这段代码得到$MFT的$FILE_NAME属性来得到$MFT的file name。这个示例同样适用于得到其它文件的$FILE_NAME(如上面的file)、还有其它的属性如$DATA等等。

:bpx FindAttribute

Break due to BPX FindAttribute (ET=6.89 seconds)

:locals
[EBP-4] +struct ATTRIBUTE * attr = 0x00344D68 <{...}>
[EBP+8] +struct FILE_RECORD_HEADER * file = 0x00344D38 <{...}>
[EBP+C] enum ATTRIBUTE_TYPE type = AttributeFileName (30)
[EBP+10] +unsigned short * name = 0x004041BC <"$MFT">

:?file
struct FILE_RECORD_HEADER * = 0x00344D38 <{...}>
struct NTFS_RECORD_HEADER Ntfs = {...}
unsigned short SequenceNumber = 0x1, "\0\x01"
unsigned short LinkCount = 0x1, "\0\x01"
unsigned short AttributesOffset = 0x30, "\00"
unsigned short Flags = 0x1, "\0\x01"
unsigned long BytesInUse = 0x2D8, "\0\0\x02\xD8"
unsigned long BytesAllocated = 0x400, "\0\0\x04\0"
unsigned quad BaseFileRecord = 0x0, "\0\0\0\0\0\0\0\0"
unsigned short NextAttributeNumber = 0x6, "\0\x06"

file参数我传入的是$MFT,从$MFT的LCN=4可以得到其在卷中的物理地址,这在上面已说明。你也可以使用dskprobe(我机子中为第LCN*SectorsPerCluster=4*8扇区)得到底下SoftICE的输出结果:

:dd @file //以下的注释可对照文中开头列出的FILE_RECORD_HEADER定义。
0023:00344D38 454C4946 0003002A 6D4AC04D 00000000 FILE*...M.Jm....
0023:00344D48 00010001 00010030 000002D8 00000400 ....0...........
----
|__AttributeOffset
0023:00344D58 00000000 00000000 04340006 0000FA0D ..........4.....
0023:00344D68 00000010 00000060 00180000 00000000 ....`...........
-------- --------
| |_指出这个Attribute的长度。定义如下。
|_根据AttributeOffset得到的Attribute头,定义如下。00000010指出这个Attribute为StandardInformation

0023:00344D78 00000048 00000018 2C1761D0 01BFB03C H........a.,<...

Attribute头如下定义:

typedef struct {
ATTRIBUTE_TYPE AttributeType;
ULONG Length;
BOOLEAN Nonresident;
UCHAR NameLength;
USHORT NameOffset;
USHORT Flags; // 0x0001 = Compressed
USHORT AttributeNumber;
} ATTRIBUTE, *PATTRIBUTE;

typedef struct {
ATTRIBUTE Attribute;
ULONG ValueLength;
USHORT ValueOffset;
USHORT Flags; // 0x0001 = Indexed
} RESIDENT_ATTRIBUTE, *PRESIDENT_ATTRIBUTE;

typedef struct {
ULONGLONG DirectoryFileReferenceNumber;
ULONGLONG CreationTime; // Saved when filename last changed
ULONGLONG ChangeTime; // ditto
ULONGLONG LastWriteTime; // ditto
ULONGLONG LastAccessTime; // ditto
ULONGLONG AllocatedSize; // ditto
ULONGLONG DataSize; // ditto
ULONG FileAttributes; // ditto
ULONG AlignmentOrReserved;
UCHAR NameLength;
UCHAR NameType; // 0x01 = Long, 0x02 = Short
WCHAR Name[1];
} FILENAME_ATTRIBUTE, *PFILENAME_ATTRIBUTE;

ATTRIBUTE_TYPE是一个Enum型定义。其中00000010为StandardInformation。30为FileName。因为FileNameAttribute总是一个常驻Attribute,所以我将RESIDENT_ATTRIBUTE定义也给出。OK,现在可以继续Dump下一个Attribute:

// dd @file+file->AttributeOffset+length(StandardInformationAttribute)

:dd @file+30+60
0023:00344DC8 00000030 00000068 00180000 00030000 0...h...........
-------- ------
| |___这里的NameLength与NameOffset指FileNameAttribute名。不要与$MFT FileName混谣。
|_指出这是一个FileNameAttribute。
0023:00344DD8 0000004A 00010018 00000005 00050000 J...............
-------- ---- --------
| | |_根据ValueOffset的值,得到FILENAME_ATTRIBUTE的具体位置。
| |_ValueOffset值
|_ValueLength值
0023:00344DE8 2C1761D0 01BFB03C 2C1761D0 01BFB03C .a.,<....a.,<...
0023:00344DF8 2C1761D0 01BFB03C 2C1761D0 01BFB03C .a.,<....a.,<...
0023:00344E08 00004000 00000000 00004000 00000000 .@.......@......
0023:00344E18 00000006 00000000 00240304 0046004D ..........$.M.F.
-- --------
| |___找到$MFT的FileName了吧。
|_NameLength
0023:00344E28 00000054 00000000 00000080 00000190 T...............
0023:00344E38 00400001 00010000 00000000 00000000 ..@.............

这儿给出了Dump Attribute的一个具体方法。最后我将给出遍历File Record的代码,在给出代码前应该说明一下$MFT中$BITMAP属性。NTFS的这个Attribute相当于LINUX EXT2的s_inode_bitmap数组(Linux 2.0版本)。所以很容易明白$BITMAP的作用,即每bit指出相应File Record的在用情况。以下是DumpAllFileRecord的代码:

BOOL bitset(PUCHAR bitmap, ULONG i)
{
return (bitmap[i >> 3] & (1 << (i & 7))) != 0;
}

VOID DumpAllFileRecord()
{
PATTRIBUTE attr = FindAttribute(MFT, AttributeBitmap, 0);
PUCHAR bitmap = new UCHAR[AttributeLengthAllocated(attr)];

ReadAttribute(attr, bitmap);

ULONG n = AttributeLength(FindAttribute(MFT, AttributeData, 0)) / BytesPerFileRecord;

PFILE_RECORD_HEADER file = PFILE_RECORD_HEADER(new UCHAR[BytesPerFileRecord]);

for (ULONG i = 0; i < n; i++) {
if (!bitset(bitmap, i)) continue;

ReadFileRecord(i, file);

if (file->Ntfs.Type == 'ELIF' && (file->Flags & 3 )) {
attr = FindAttribute(file, AttributeFileName, 0);
if (attr == 0) continue;

PFILENAME_ATTRIBUTE name
= PFILENAME_ATTRIBUTE(Padd(attr, PRESIDENT_ATTRIBUTE(attr)->ValueOffset));

printf("%8lu %.*ws\n", i, int(name->NameLength),name->Name)
}
}
}

本文引用Gary Nebbett的些定义可能对Windows 2000版本有些很小的出入,不过Internet有其神奇的地方,虽然Microsoft不提供这些信息,但诸如linux-ntfs GNU工程等均是学习NTFS的一个很好的资料,本文也参考了很多它提供的文档。另外Mark Russinovich的《Inside Win2K NTFS》、《Inside NTFS》、《Exploring NTFS On-disk Structures》等也是很好的NTFS资料。本文仍未涉及NTFS中目录的组织(B+树)等等,可能的话我会另行介绍。文中介绍的完整代码可到http://webcrazy.yeah.net下载。出现的错误也欢迎来信指教(tsu00@263.net)!

最后感谢Anton Altaparmakov,感谢我的同事在出差时抽空给我买到Gary Nebbett的书。感谢我看到的所有资料的原作者们。感谢他们!

参考资料:
1.Gary Nebbett《Windows NT/2000 Native API Reference》
2.Linux-NTFS Project NTFS Documentation Version 0.4
3.Mark Russinovich相关文档
4.David Solomom《Inside Windows NT,2nd Edition》

1、什么是NTFS-新(N)技术(T)文件(F)系统(S)?

想要了解NTFS,我们首先应该认识一下FAT。FAT(FileAllocationTable)是“文件分配表”的意思。对我们来说,它的意义在于对硬盘分区的管理。FAT16、FAT32、NTFS是目前最常见的三种文件系统。

FAT16:我们以前用的DOS、Windows95都使用FAT16文件系统,现在常用的Windows98/2000/XP等系统均支持FAT16文件系统。它最大可以管理大到2GB的分区,但每个分区最多只能有65525个簇(簇是磁盘空间的配置单位)。随着硬盘或分区容量的增大,每个簇所占的空间将越来越大,从而导致硬盘空间的浪费。

FAT32:随着大容量硬盘的出现,从Windows98开始,FAT32开始流行。它是FAT16的增强版本,可以支持大到2TB(2048G的分区。FAT32使用的簇比FAT16小,从而有效地节约了硬盘空间。

NTFS:微软WindowsNT内核的系列操作系统支持的、一个特别为网络和磁盘配额、文件加密等管理安全特性设计的磁盘格式。随着以NT为内核的Windows2000/XP的普及,很多个人用户开始用到了NTFS。NTFS也是以簇为单位来存储数据文件,但NTFS中簇的大小并不依赖于磁盘或分区的大小。簇尺寸的缩小不但降低了磁盘空间的浪费,还减少了产生磁盘碎片的可能。NTFS支持文件加密管理功能,可为用户提供更高层次的安全保证。

就是小和大之分,一般小的分区就用FAT32管理文件,大的分区超过30G以上用NTFS分区,这样运行的速度,管理都会快,方便

想要了解NTFS,我们首先应该认识一下FAT。FAT(File Allocation Table)是“文件分配表”的意思。对我们来说,它的意义在于对硬盘分区的管理。FAT16、FAT32、NTFS是目前最常见的三种文件系统。 #_ X{ ma$Hd~5@

({Q/`yP FAT16:我们以前用的DOS、Windows 95都使用FAT16文件系统,现在常用的Windows 98/2000/ XP等系统均支持FAT16文件系统。它最大可以管理大到2GB的分区,但每个分区最多只能有65525个簇。
,M k"UpGWX FAT32:随着大容量硬盘的出现,从Windows 98开始,FAT32开始流行。它是FAT16的增强版本,可以支持大到2TB(2048G的分区。FAT32使用的簇比FAT16小,从而有效地节约了硬盘空间。
1e/U qc#TH_ q NTFS:微软Windows NT内核的系列操作系统支持的、一个特别为网络和磁盘配额、文件加密等管理安全特性设计的磁盘格式。随着以NT为内核的Windows 2000/ XP的普及,很多个人用户开始用到了NTFS。NTFS也是以簇为单位来存储数据文件,但NTFS中簇的大小并不依赖于磁盘或分区的大小。簇尺寸的缩小不但降低了磁盘空间的浪费,还减少了产生磁盘碎片的可能。NTFS支持文件加密管
k1dPL}9l 理功能,可为用户提供更高层次的安全保证。 _I.a!J7X
M z y&W/]_Dd
2、什么系统可以支持NTFS文件系统?
8R\9Z Y0uv |5a R s6B"u1Ec1N1o0Hq
只有Windows NT/2000/XP才能识别NTFS系统,Windows 9x/ Me以及DOS等操作系统都不能支持、识别NTFS格式的磁盘。由于DOS系统不支持NTFS系统,所以最好不要将C:盘制作为NTFS系统,这样在系统崩溃后便于在DOS系统下修复。
3yf q$`.A6kj9a!f drR `$D ];y'rLDz/hDS
NTFS与操作系统支持情况如下:
h|0{n!w;yb.N FAT16 windows 95/98/me/nt/2000/xp unix,linux,dos
's'L%C"HgHb&h FAT32 windows 95/98/me/2000/xp
dSpL)C7n%?^V2O9U NTFS windows nt/2000/xp y7g2eOz,q

scc6_Jj ~] { 3、我们需要NTFS吗? !b8[t l E"~nR

@d@Qb5x%\Y Windows 2000/XP在文件系统上是向下兼容的,它可以很好地支持FAT16/FAT32和NTFS,其中NTFS是Windows NT/2000/XP专用格式,它能更充分有效地利用磁盘空间、支持文件级压缩、具备更好的文件安全性。如果你只安装Windows 2000/ XP,建议选择NTFS文件系统。如果多重引导系统,则系统盘(C盘)必须为FAT16或FAT32,否则不支持多重引导。