FAT文件系統(tǒng)原理
作者:zty,來源:www.sjhf.net,發(fā)布時間:13/05/14
一、硬盤的物理結(jié)構(gòu):
硬盤存儲數(shù)據(jù)是根據(jù)電、磁轉(zhuǎn)換原理實現(xiàn)的。硬盤由一個或幾個表面鍍有磁性物質(zhì)的金屬或玻璃等物質(zhì)盤片以及盤片兩面所安裝的磁頭和相應的控制電路組成(圖1),其中盤片和磁頭密封在無塵的金屬殼中。
硬盤工作時,盤片以設(shè)計轉(zhuǎn)速高速旋轉(zhuǎn),設(shè)置在盤片表面的磁頭則在電路控制下徑向移動到指定位置然后將數(shù)據(jù)存儲或讀取出來。當系統(tǒng)向硬盤寫入數(shù)據(jù)時,磁頭中“寫數(shù)據(jù)”電流產(chǎn)生磁場使盤片表面磁性物質(zhì)狀態(tài)發(fā)生改變,并在寫電流磁場消失后仍能保持,這樣數(shù)據(jù)就存儲下來了;當系統(tǒng)從硬盤中讀數(shù)據(jù)時,磁頭經(jīng)過盤片指定區(qū)域,盤片表面磁場使磁頭產(chǎn)生感應電流或線圈阻抗產(chǎn)生變化,經(jīng)相關(guān)電路處理后還原成數(shù)據(jù)。因此只要能將盤片表面處理得更平滑、磁頭設(shè)計得更精密以及盡量提高盤片旋轉(zhuǎn)速度,就能造出容量更大、讀寫數(shù)據(jù)速度更快的硬盤。這是因為盤片表面處理越平、轉(zhuǎn)速越快就能越使磁頭離盤片表面越近,提高讀、寫靈敏度和速度;磁頭設(shè)計越小越精密就能使磁頭在盤片上占用空間越小,使磁頭在一張盤片上建立更多的磁道以存儲更多的數(shù)據(jù)。
二、硬盤的邏輯結(jié)構(gòu)。
硬盤由很多盤片(platter)組成,每個盤片的每個面都有一個讀寫磁頭。如果有N個盤片。就有2N個面,對應2N個磁頭(Heads),從0、1、2開始編號。每個盤片被劃分成若干個同心圓磁道(邏輯上的,是不可見的。)每個盤片的劃分規(guī)則通常是一樣的。這樣每個盤片的半徑均為固定值R的同心圓再邏輯上形成了一個以電機主軸為軸的柱面(Cylinders),從外至里編號為0、1、2……每個盤片上的每個磁道又被劃分為幾十個扇區(qū)(Sector),通常的容量是512byte,并按照一定規(guī)則編號為1、2、3……形成Cylinders×Heads×Sector個扇區(qū)。這三個參數(shù)即是硬盤的物理參數(shù)。
三、磁盤引導原理。
3.1 MBR(master boot record)扇區(qū):
計算機在按下power鍵以后,開始執(zhí)行主板bios程序。進行完一系列檢測和配置以后。開始按bios中設(shè)定的系統(tǒng)引導順序引導系統(tǒng)。假定現(xiàn)在是硬盤。Bios執(zhí)行完自己的程序后如何把執(zhí)行權(quán)交給硬盤呢。交給硬盤后又執(zhí)行存儲在哪里的程序呢。其實,稱為mbr的一段代碼起著舉足輕重的作用。MBR(master boot record),即主引導記錄,有時也稱主引導扇區(qū)。位于整個硬盤的0柱面0磁頭1扇區(qū)(可以看作是硬盤的第一個扇區(qū)),bios在執(zhí)行自己固有的程序以后就會jump到mbr中的第一條指令。將系統(tǒng)的控制權(quán)交由mbr來執(zhí)行。在總共512byte的主引導記錄中,MBR的引導程序占了其中的前446個字節(jié)(偏移0H~偏移1BDH),隨后的64個字節(jié)(偏移1BEH~偏移1FDH)為DPT(Disk PartitionTable,硬盤分區(qū)表),最后的兩個字節(jié)“55 AA”(偏移1FEH~偏移1FFH)是分區(qū)有效結(jié)束標志。
MBR不隨操作系統(tǒng)的不同而不同,意即不同的操作系統(tǒng)可能會存在相同的MBR,即使不同,MBR也不會夾帶操作系統(tǒng)的性質(zhì)。具有公共引導的特性。
下面是用winhex查看的一塊希捷120GB硬盤的mbr。
你的硬盤的MBR引導代碼可能并非這樣。不過即使不同,所執(zhí)行的功能大體是一樣的。這是wowocock關(guān)于磁盤mbr的反編譯,已加了詳細的注釋,感興趣可以細細研究一下。
操作系統(tǒng)為了便于用戶對磁盤的管理,加入了磁盤分區(qū)的概念。即將一塊磁盤邏輯劃分為幾塊。磁盤分區(qū)數(shù)目的多少只受限于C~Z的英文字母的數(shù)目,在上圖DPT共64個字節(jié)中如何表示多個分區(qū)的屬性呢?microsoft通過鏈接的方法解決了這個問題。在DPT共64個字節(jié)中,以16個字節(jié)為分區(qū)表項單位描述一個分區(qū)的屬性。也就是說,第一個分區(qū)表項描述一個分區(qū)的屬性,一般為基本分區(qū)。第二個分區(qū)表項描述除基本分區(qū)外的其余空間,一般而言,就是我們所說的擴展分區(qū)。這部分的大體說明見表1。
表1 圖2分區(qū)表第一字段
字節(jié)位移字段長度值字段名和定義
0x01BEBYTE0x80引導指示符(Boot Indicator):指明該分區(qū)是否是活動分區(qū)。
0x01BFBYTE0x01開始磁頭(Starting Head)
0x01C06位0x01開始扇區(qū)(Starting Sector) 只用了0~5位。后面的兩位(第6位和第7位)被開始柱面字段所使用
0x01C110位0x00開始柱面(Starting Cylinder):除了開始扇區(qū)字段的最后兩位外,還使用了1位來組成該柱面值。開始柱面是一個10位數(shù),最大值為1023
0x01C2BYTE0x07系統(tǒng)ID(System ID) 定義了分區(qū)的類型,詳細定義,請參閱圖4
0x01C3BYTE0xFE結(jié)束磁頭(Ending Head)
0x01C46位0xFF結(jié)束扇區(qū)(Ending Sector):只使用了0~5位。最后兩位(第6、7位)被結(jié)束柱面字段所使用
0x01C510位0x7B結(jié)束柱面(Ending Cylinder) 除了結(jié)束扇區(qū)字段最后的兩位外,還使用了1位,以組成該柱面值。結(jié)束柱面是一個10位的數(shù),最大值為1023
0x01C6DWORD0x0000003F相對扇區(qū)數(shù)(Relative Sectors) 從該磁盤的開始到該分區(qū)的開始的位移量,以扇區(qū)來計算
0x01CADWORD0x00DAA83D總扇區(qū)數(shù)(Total Sectors) 該分區(qū)中的扇區(qū)總數(shù)
注:上表中的超過1字節(jié)的數(shù)據(jù)都以實際數(shù)據(jù)顯示,就是按高位到地位的方式顯示。存儲時是按低位到高位存儲的。兩者表現(xiàn)不同,請仔細看清楚。以后出現(xiàn)的表,圖均同。
也可以在winhex中看到這些參數(shù)的意義:
說明:每個分區(qū)表項占用16個字節(jié),假定偏移地址從0開始。如圖3的分區(qū)表項3。分區(qū)表項4同分區(qū)表項3。
1、0H偏移為活動分區(qū)是否標志,只能選00H和80H。80H為活動,00H為非活動。其余值對microsoft而言為非法值。
2、重新說明一下(這個非常重要):大于1個字節(jié)的數(shù)被以低字節(jié)在前的存儲格式格式(little endian format)或稱反字節(jié)順序保存下來。低字節(jié)在前的格式是一種保存數(shù)的方法,這樣,最低位的字節(jié)最先出現(xiàn)在十六進制數(shù)符號中。例如,相對扇區(qū)數(shù)字段的值0x3F000000的低字節(jié)在前表示為0x0000003F。這個低字節(jié)在前的格式數(shù)的十進制數(shù)為63。
3、系統(tǒng)在分區(qū)時,各分區(qū)都不允許跨柱面,即均以柱面為單位,這就是通常所說的分區(qū)粒度。有時候我們分區(qū)是輸入分區(qū)的大小為7000M,分出來卻是6997M,就是這個原因。 偏移2H和偏移6H的扇區(qū)和柱面參數(shù)中,扇區(qū)占6位(bit),柱面占10位(bit),以偏移6H為例,其低6位用作扇區(qū)數(shù)的二進制表示。其高兩位做柱面數(shù)10位中的高兩位,偏移7H組成的8位做柱面數(shù)10位中的低8位。由此可知,實際上用這種方式表示的分區(qū)容量是有限的,柱面和磁頭從0開始編號,扇區(qū)從1開始編號,所以最多只能表示1024個柱面×63個扇區(qū)×256個磁頭×512byte=8455716864byte。即通常的8.4GB(實際上應該是7.8GB左右)限制。實際上磁頭數(shù)通常只用到255個(由匯編語言的尋址寄存器決定),即使把這3個字節(jié)按線性尋址,依然力不從心。 在后來的操作系統(tǒng)中,超過8.4GB的分區(qū)其實已經(jīng)不通過C/H/S的方式尋址了。而是通過偏移CH~偏移FH共4個字節(jié)32位線性扇區(qū)地址來表示分區(qū)所占用的扇區(qū)總數(shù)??芍ㄟ^4個字節(jié)可以表示2^32個扇區(qū),即2TB=2048GB,目前對于大多數(shù)計算機而言,這已經(jīng)是個天文數(shù)字了。在未超過8.4GB的分區(qū)上,C/H/S的表示方法和線性扇區(qū)的表示方法所表示的分區(qū)大小是一致的。也就是說,兩種表示方法是協(xié)調(diào)的。即使不協(xié)調(diào),也以線性尋址為準。(可能在某些系統(tǒng)中會提示出錯)。超過8.4GB的分區(qū)結(jié)束C/H/S一般填充為FEH FFH FFH。即C/H/S所能表示的最大值。有時候也會用柱面對1024的模來填充。不過這幾個字節(jié)是什么其實都無關(guān)緊要了。
雖然現(xiàn)在的系統(tǒng)均采用線性尋址的方式來處理分區(qū)的大小。但不可跨柱面的原則依然沒變。本分區(qū)的扇區(qū)總數(shù)加上與前一分區(qū)之間的保留扇區(qū)數(shù)目依然必須是柱面容量的整數(shù)倍。(保留扇區(qū)中的第一個扇區(qū)就是存放分區(qū)表的MBR或虛擬MBR的扇區(qū),分區(qū)的扇區(qū)總數(shù)在線性表示方式上是不計入保留扇區(qū)的。如果是第一個分區(qū),保留扇區(qū)是本分區(qū)前的所有扇區(qū)。
附:分區(qū)表類型標志如圖4
3.2 擴展分區(qū):
擴展分區(qū)中的每個邏輯驅(qū)動器都存在一個類似于MBR的擴展引導記錄( Extended Boot Record, EBR),也有人稱之為虛擬mbr或擴展mbr,意思是一樣的。擴展引導記錄包括一個擴展分區(qū)表和該扇區(qū)的標簽。擴展引導記錄將記錄只包含擴展分區(qū)中每個邏輯驅(qū)動器的第一個柱面的第一面的信息。一個邏輯驅(qū)動器中的引導扇區(qū)一般位于相對扇區(qū)32或63。但是,如果磁盤上沒有擴展分區(qū),那么就不會有擴展引導記錄和邏輯驅(qū)動器。第一個邏輯驅(qū)動器的擴展分區(qū)表中的第一項指向它自身的引導扇區(qū)。第二項指向下一個邏輯驅(qū)動器的EBR。如果不存在進一步的邏輯驅(qū)動器,第二項就不會使用,而且被記錄成一系列零。如果有附加的邏輯驅(qū)動器,那么第二個邏輯驅(qū)動器的擴展分區(qū)表的第一項會指向它本身的引導扇區(qū)。第二個邏輯驅(qū)動器的擴展分區(qū)表的第二項指向下一個邏輯驅(qū)動器的EBR。擴展分區(qū)表的第三項和第四項永遠都不會被使用。
通過一幅4分區(qū)的磁盤結(jié)構(gòu)圖可以看到磁盤的大致組織形式。如圖5:
關(guān)于擴展分區(qū),如圖6所示,擴展分區(qū)中邏輯驅(qū)動器的擴展引導記錄是一個連接表。該圖顯示了一個擴展分區(qū)上的三個邏輯驅(qū)動器,說明了前面的邏輯驅(qū)動器和最后一個邏輯驅(qū)動器之間在擴展分區(qū)表中的差異。
除了擴展分區(qū)上最后一個邏輯驅(qū)動器外,表2中所描述的擴展分區(qū)表的格式在每個邏輯驅(qū)動器中都是重復的:第一個項標識了邏輯驅(qū)動器本身的引導扇區(qū),第二個項標識了下一個邏輯驅(qū)動器的EBR。最后一個邏輯驅(qū)動器的擴展分區(qū)表只會列出它本身的分區(qū)項。最后一個擴展分區(qū)表的第二個項到第四個項被使用。
擴展分區(qū)表項中的相對扇區(qū)數(shù)字段所顯示的是從擴展分區(qū)開始到邏輯驅(qū)動器中第一個扇區(qū)的位移的字節(jié)數(shù)。總扇區(qū)數(shù)字段中的數(shù)是指組成該邏輯驅(qū)動器的扇區(qū)數(shù)目。總扇區(qū)數(shù)字段的值等于從擴展分區(qū)表項所定義的引導扇區(qū)到邏輯驅(qū)動器末尾的扇區(qū)數(shù)。
四、FAT分區(qū)原理。
先來一幅結(jié)構(gòu)圖:
現(xiàn)在我們著重研究FAT格式分區(qū)內(nèi)數(shù)據(jù)是如何存儲的。FAT分區(qū)格式是MICROSOFT最早支持的分區(qū)格式,依據(jù)FAT表中每個簇鏈的所占位數(shù)(有關(guān)概念,后面會講到)分為fat12、fat16、fat32三種格式“變種”,但其基本存儲方式是相似的。
仔細研究圖7中的fat16和fat32分區(qū)的組成結(jié)構(gòu)。下面依次解釋DBR、FAT1、FAT2、根目錄、數(shù)據(jù)區(qū)、剩余扇區(qū)的概念。提到的地址如無特別提示均為分區(qū)內(nèi)部偏移。
4.1 關(guān)于DBR.
DBR區(qū)(DOS BOOT RECORD)即操作系統(tǒng)引導記錄區(qū)的意思,通常占用分區(qū)的第0扇區(qū)共512個字節(jié)(特殊情況也要占用其它保留扇區(qū),我們先說第0扇)。在這512個字節(jié)中,其實又是由跳轉(zhuǎn)指令,廠商標志和操作系統(tǒng)版本號,BPB(BIOS Parameter Block),擴展BPB,os引導程序,結(jié)束標志幾部分組成。 以用的最多的FAT32為例說明分區(qū)DBR各字節(jié)的含義。見圖8。
圖8的對應解釋見表3
表3 FAT32分區(qū)上DBR中各部分的位置劃分
字節(jié)位移字段長度字段名對應圖8顏色
0x003個字節(jié)跳轉(zhuǎn)指令
0x038個字節(jié)廠商標志和os版本號
0x0B53個字節(jié)BPB
0x4026個字節(jié)擴展BPB
0x5A420個字節(jié)引導程序代碼
0x01FE2個字節(jié)有效結(jié)束標志
圖9給出了winhex對圖8 DBR的相關(guān)參數(shù)解釋:
根據(jù)上邊圖例,我們來討論DBR各字節(jié)的參數(shù)意義。
MBR將CPU執(zhí)行轉(zhuǎn)移給引導扇區(qū),因此,引導扇區(qū)的前三個字節(jié)必須是合法的可執(zhí)行的基于x86的CPU指令。這通常是一條跳轉(zhuǎn)指令,該指令負責跳過接下來的幾個不可執(zhí)行的字節(jié)(BPB和擴展BPB),跳到操作系統(tǒng)引導代碼部分。
跳轉(zhuǎn)指令之后是8字節(jié)長的OEM ID,它是一個字符串,OEM ID標識了格式化該分區(qū)的操作系統(tǒng)的名稱和版本號。為了保留與MS-DOS的兼容性,通常Windows 2000格式化該盤是在FAT16和FAT32磁盤上的該字段中記錄了“MSDOS 5.0”,在NTFS磁盤上(關(guān)于ntfs,另述),Windows 2000記錄的是“NTFS”。通常在被Windows 95格式化的磁盤上OEM ID字段出現(xiàn)“MSWIN4.0”,在被Windows 95 OSR2和Windows 98格式化的磁盤上OEM ID字段出現(xiàn)“MSWIN4.1”。
接下來的從偏移0x0B開始的是一段描述能夠使可執(zhí)行引導代碼找到相關(guān)參數(shù)的信息。通常稱之為BPB(BIOS Parameter Block),BPB一般開始于相同的位移量,因此,標準的參數(shù)都處于一個已知的位置。磁盤容量和幾何結(jié)構(gòu)變量都被封在BPB之中。由于引導扇區(qū)的第一部分是一個x86跳轉(zhuǎn)指令。因此,將來通過在BPB末端附加新的信息,可以對BPB進行擴展。只需要對該跳轉(zhuǎn)指令作一個小的調(diào)整就可以適應BPB的變化。圖9已經(jīng)列出了項目的名稱和取值,為了系統(tǒng)的研究,針對圖8,將FAT32分區(qū)格式的BPB含義和擴展BPB含義釋義為表格,見表4和表5。
表4 FAT32分區(qū)的BPB字段
字節(jié)位移字段長度(字節(jié))圖8對應取值名稱和定義
0x0B20x0200扇區(qū)字節(jié)數(shù)(Bytes Per Sector) 硬件扇區(qū)的大小。本字段合法的十進制值有512、1024、2048和4096。對大多數(shù)磁盤來說,本字段的值為512
0x0D10x08每簇扇區(qū)數(shù)(Sectors Per Cluster),一簇中的扇區(qū)數(shù)。由于FAT32文件系統(tǒng)只能跟蹤有限個簇(最多為4 294 967 296個),因此,通過增加每簇扇區(qū)數(shù),可以使FAT32文件系統(tǒng)支持最大分區(qū)數(shù)。一個分區(qū)缺省的簇大小取決于該分區(qū)的大小。本字段的合法十進制值有1、2、4、8、16、32、64和128。Windows 2000的FAT32實現(xiàn)只能創(chuàng)建最大為32GB的分區(qū)。但是,Windows 2000能夠訪問由其他操作系統(tǒng)(Windows 95、OSR2及其以后的版本)所創(chuàng)建的更大的分區(qū)
0x0e20x0020保留扇區(qū)數(shù)(Reserved Sector) 第一個FAT開始之前的扇區(qū)數(shù),包括引導扇區(qū)。本字段的十進制值一般為32
0x1010x02FAT數(shù)(Number of FAT) 該分區(qū)上FAT的副本數(shù)。本字段的值一般為2
0x1120x0000根目錄項數(shù)(Root Entries)只有FAT12/FAT16使用此字段。對FAT32分區(qū)而言,本字段必須設(shè)置為 0
0x1320x0000小扇區(qū)數(shù)(Small Sector)(只有FAT12/FAT16使用此字段)對FAT32分區(qū)而言,本字段必須設(shè)置為0
0x1510xF8媒體描述符( Media Descriptor)提供有關(guān)媒體被使用的信息。值0xF8表示硬盤,0xF0表示高密度的3.5寸軟盤。媒體描述符要用于MS-DOS FAT16磁盤,在Windows 2000中未被使用
0x1620x0000每FAT扇區(qū)數(shù)(Sectors Per FAT)只被FAT12/FAT16所使用,對FAT32分區(qū)而言,本字段必須設(shè)置為0
0x1820x003F每道扇區(qū)數(shù)(Sectors Per Track) 包含使用INT13h的磁盤的“每道扇區(qū)數(shù)”幾何結(jié)構(gòu)值。該分區(qū)被多個磁頭的柱面分成了多個磁道
0x1A20x00FF磁頭數(shù)(Number of Head) 本字段包含使用INT 13h的磁盤的“磁頭數(shù)”幾何結(jié)構(gòu)值。例如,在一張1.44MB 3.5英寸的軟盤上,本字段的值為 2
0x1C40x0000003F隱藏扇區(qū)數(shù)(Hidden Sector) 該分區(qū)上引導扇區(qū)之前的扇區(qū)數(shù)。在引導序列計算到根目錄的數(shù)據(jù)區(qū)的絕對位移的過程中使用了該值。本字段一般只對那些在中斷13h上可見的媒體有意義。在沒有分區(qū)的媒體上它必須總是為0
0x2040x007D043F總扇區(qū)數(shù)(Large Sector) 本字段包含F(xiàn)AT32分區(qū)中總的扇區(qū)數(shù)
0x2440x00001F32每FAT扇區(qū)數(shù)(Sectors Per FAT)(只被FAT32使用)該分區(qū)每個FAT所占的扇區(qū)數(shù)。計算機利用這個數(shù)和 FAT數(shù)以及隱藏扇區(qū)數(shù)(本表中所描述的)來決定根目錄從哪里開始。該計算機還可以從目錄中的項數(shù)決定該分區(qū)的用戶數(shù)據(jù)區(qū)從哪里開始
0x2820x00擴展標志(Extended Flag)(只被FAT32使用)該兩個字節(jié)結(jié)構(gòu)中各位的值為:
位0-3:活動 FAT數(shù)(從0開始計數(shù),而不是1).
只有在不使用鏡像時才有效
位4-6:保留
位7:0值意味著在運行時FAT被映射到所有的FAT
1值表示只有一個FAT是活動的
位8-15:保留
0x2A20x0000文件系統(tǒng)版本(File ystem Version)只供FAT32使用,高字節(jié)是主要的修訂號,而低字節(jié)是次要的修訂號。本字段支持將來對該FAT32媒體類型進行擴展。如果本字段非零,以前的Windows版本將不支持這樣的分區(qū)
0x2C40x00000002根目錄簇號(Root Cluster Number)(只供FAT32使用) 根目錄第一簇的簇號。本字段的值一般為2,但不總是如此
0x3020x0001文件系統(tǒng)信息扇區(qū)號(File System Information SectorNumber)(只供FAT32使用) FAT32分區(qū)的保留區(qū)中的文件系統(tǒng)信息(File System Information, FSINFO)結(jié)構(gòu)的扇區(qū)號。其值一般為1。在備份引導扇區(qū)(Backup Boot Sector)中保留了該FSINFO結(jié)構(gòu)的一個副本,但是這個副本不保持更新
0x3420x0006備份引導扇區(qū)(只供FAT32使用) 為一個非零值,這個非零值表示該分區(qū)保存引導扇區(qū)的副本的保留區(qū)中的扇區(qū)號。本字段的值一般為6,建議不要使用其他值
0x361212個字節(jié)均為0x00保留(只供FAT32使用)供以后擴充使用的保留空間。本字段的值總為0
表5 FAT32分區(qū)的擴展BPB字段
字節(jié)位移字段長度(字節(jié))圖8對應取值字段名稱和定義
0x4010x80物理驅(qū)動器號( Physical Drive Number) 與BIOS物理驅(qū)動器號有關(guān)。軟盤驅(qū)動器被標識為0x00,物理硬盤被標識為0x80,而與物理磁盤驅(qū)動器無關(guān)。一般地,在發(fā)出一個INT13h BIOS調(diào)用之前設(shè)置該值,具體指定所訪問的設(shè)備。只有當該設(shè)備是一個引導設(shè)備時,這個值才有意義
0x4110x00保留(Reserved) FAT32分區(qū)總是將本字段的值設(shè)置為0
0x4210x29擴展引導標簽(Extended Boot Signature) 本字段必須要有能被Windows 2000所識別的值0x28或0x29
0x4340x33391CFE分區(qū)序號(Volume Serial Number) 在格式化磁盤時所產(chǎn)生的一個隨機序號,它有助于區(qū)分磁盤
0x4711"NO NAME"卷標(Volume Label) 本字段只能使用一次,它被用來保存卷標號?,F(xiàn)在,卷標被作為一個特殊文件保存在根目錄中
0x528"FAT32"系統(tǒng)ID(System ID) FAT32文件系統(tǒng)中一般取為"FAT32"
DBR的偏移0x5A開始的數(shù)據(jù)為操作系統(tǒng)引導代碼。這是由偏移0x00開始的跳轉(zhuǎn)指令所指向的。在圖8所列出的偏移0x00~0x02的跳轉(zhuǎn)指令“EB 58 90”清楚地指明了OS引導代碼的偏移位置。jump 58H加上跳轉(zhuǎn)指令所需的位移量,即開始于0x5A。此段指令在不同的操作系統(tǒng)上和不同的引導方式上,其內(nèi)容也是不同的。大多數(shù)的資料上都說win98,構(gòu)建于fat基本分區(qū)上的win2000,winxp所使用的DBR只占用基本分區(qū)的第0扇區(qū)。他們提到,對于fat32,一般的32個基本分區(qū)保留扇區(qū)只有第0扇區(qū)是有用的。實際上,以FAT32構(gòu)建的操作系統(tǒng)如果是win98,系統(tǒng)會使用基本分區(qū)的第0扇區(qū)和第2扇區(qū)存儲os引導代碼;以FAT32構(gòu)建的操作系統(tǒng)如果是win2000或winxp,系統(tǒng)會使用基本分區(qū)的第0扇區(qū)和第0xC扇區(qū)(win2000或winxp,其第0xC的位置由第0扇區(qū)的0xAB偏移指出)存儲os引導代碼。所以,在fat32分區(qū)格式上,如果DBR一扇區(qū)的內(nèi)容正確而缺少第2扇區(qū)(win98系統(tǒng))或第0xC扇區(qū)(win2000或winxp系統(tǒng)),系統(tǒng)也是無法啟動的。如果自己手動設(shè)置NTLDR雙系統(tǒng),必須知道這一點。
DBR扇區(qū)的最后兩個字節(jié)一般存儲值為0x55AA的DBR有效標志,對于其他的取值,系統(tǒng)將不會執(zhí)行DBR相關(guān)指令。上面提到的其他幾個參與os引導的扇區(qū)也需以0x55AA為合法結(jié)束標志。
FAT16 DBR:
FAT32中DBR的含義大致如此,對于FAT12和FAT16其基本意義類似,只是相關(guān)偏移量和參數(shù)意義有小的差異,F(xiàn)AT格式的區(qū)別和來因,以后會說到,此處不在多說FAT12與FAT16。我將FAT16的扇區(qū)參數(shù)意義列表。感興趣的朋友自己研究一下,和FAT32大同小異的。
表6 一個FAT16分區(qū)上的引導扇區(qū)段
字節(jié)位移字段長度(字節(jié))字段名稱
0x003跳轉(zhuǎn)指令(Jump Instruction)
0x038OEM ID
0x0B25BPB
0x2426擴展BPB
0x3E448引導程序代碼(Bootstrap Code)
0x01FE4扇區(qū)結(jié)束標識符(0x55AA)
表7 FAT16分區(qū)的BPB字段
字節(jié)位移字段長度(字節(jié))例值名稱和定義
0x0B20x0200扇區(qū)字節(jié)數(shù)(Bytes Per Sector) 硬件扇區(qū)的大小。本字段合法的十進制值有512、1024、2048和4096。對大多數(shù)磁盤來說,本字段的值為512
0x0D10x40每簇扇區(qū)數(shù)(Sectors Per Cluster) 一個簇中的扇區(qū)數(shù)。由于FAT16文件系統(tǒng)只能跟蹤有限個簇(最多為65536個)。因此,通過增加每簇的扇區(qū)數(shù)可以支持最大分區(qū)數(shù)。分區(qū)的缺省的簇的大小取決于該 分區(qū)的大小。本字段合法的十進制值有 1、2、4、8、16、32、64和128。導致簇大于32KB(每扇區(qū)字節(jié)數(shù)*每簇扇區(qū)數(shù))的值會引起磁盤錯誤和軟件錯誤
0x0e20x0001保留扇區(qū)數(shù)(Reserved Sector) 第一個FAT開始之前的扇區(qū)數(shù),包括引導扇區(qū)。本字段的十進制值一般為1
0x1010x02FAT數(shù)(Number of FAT)該分區(qū)上FAT的副本數(shù)。本字段的值一般為2
0x1120x0200根目錄項數(shù)(Root Entries) 能夠保存在該分區(qū)的根目錄文件夾中的32個字節(jié)長的文件和文件夾名稱項的總數(shù)。在一個典型的硬盤上,本字段的值為512。其中一個項常常被用作卷標號(Volume Label),長名稱的文件和文件夾每個文件使用多個項。文件和文件夾項的最大數(shù)一般為511,但是如果使用的長文件名,往往都達不到這個數(shù)
0x1320x0000小扇區(qū)數(shù)(Small Sector) 該分區(qū)上的扇區(qū)數(shù),表示為16位(<65536)。對大于65536個扇區(qū)的分區(qū)來說,本字段的值為0,而使用大扇區(qū)數(shù)來取代它
0x1510xF8媒體描述符( Media Descriptor)提供有關(guān)媒體被使用的信息。值0xF8表示硬盤,0xF0表示高密度的3.5寸軟盤。媒體描述符要用于MS-DOS FAT16磁盤,在Windows 2000中未被使用
0x1620x00FC每FAT扇區(qū)數(shù)(Sectors Per FAT) 該分區(qū)上每個FAT所占用的扇區(qū)數(shù)。計算機利用這個數(shù)和FAT數(shù)以及隱藏扇區(qū)數(shù)來決定根目錄在哪里開始。計算機還可以根據(jù)根目錄中的項數(shù)(512)決定該 分區(qū)的用戶數(shù)據(jù)區(qū)從哪里開始
0x1820x003F每道扇區(qū)數(shù)(Sectors Per Trark)
0x1A20x0040磁頭數(shù)(Number of head)
0x1C40x0000003F隱藏扇區(qū)數(shù)(Hidden Sector) 該分區(qū)上引導扇區(qū)之前的扇區(qū)數(shù)。在引導序列計算到根目錄和數(shù)據(jù)區(qū)的絕對位移的過程中使用了該值
0x2040x003EF001大扇區(qū)數(shù)(Large Sector) 如果小扇區(qū)數(shù)字段的值為0,本字段就包含該FAT16分區(qū)中的總扇區(qū)數(shù)。如果小扇區(qū)數(shù)字段的值不為0,那么本字段的值為0
表8 FAT16分區(qū)的擴展BPB字段
字節(jié)位移字段長度(字節(jié))圖8對應取值字段名稱和定義
0x2410x80物理驅(qū)動器號( Physical Drive Number) 與BIOS物理驅(qū)動器號有關(guān)。軟盤驅(qū)動器被標識為0x00,物理硬盤被標識為0x80,而與物理磁盤驅(qū)動器無關(guān)。一般地,在發(fā)出一個INT13h BIOS調(diào)用之前設(shè)置該值,具體指定所訪問的設(shè)備。只有當該設(shè)備是一個引導設(shè)備時,這個值才有意義
0x2510x00保留(Reserved) FAT16分區(qū)一般將本字段的值設(shè)置為0
0x2610x29擴展引導標簽(Extended Boot Signature) 本字段必須要有能被Windows 2000所識別的值0x28或0x29
0x2720x52368BA8卷序號(Volume Serial Number) 在格式化磁盤時所產(chǎn)生的一個隨機序號,它有助于區(qū)分磁盤
0x2B11"NO NAME"卷標(Volume Label) 本字段只能使用一次,它被用來保存卷標號?,F(xiàn)在,卷標被作為一個特殊文件保存在根目錄中
0x368"FAT16"文件系統(tǒng)類型(File System Type) 根據(jù)該磁盤格式,該字段的值可以為FAT、FAT12或FAT16
4.2 關(guān)于保留扇區(qū)
在上述FAT文件系統(tǒng)DBR的偏移0x0E處,用2個字節(jié)存儲保留扇區(qū)的數(shù)目。所謂保留扇區(qū)(有時候會叫系統(tǒng)扇區(qū),隱藏扇區(qū)),是指從分區(qū)DBR扇區(qū)開始的僅為系統(tǒng)所有的扇區(qū),包括DBR扇區(qū)。在FAT16文件系統(tǒng)中,保留扇區(qū)的數(shù)據(jù)通常設(shè)置為1,即僅僅DBR扇區(qū)。而在FAT32中,保留扇區(qū)的數(shù)據(jù)通常取為32,有時候用Partition Magic分過的FAT32分區(qū)會設(shè)置36個保留扇區(qū),有的工具可能會設(shè)置63個保留扇區(qū)。
FAT32中的保留扇區(qū)除了磁盤總第0扇區(qū)用作DBR,總第2扇區(qū)(win98系統(tǒng))或總第0xC扇區(qū)(win2000,winxp)用作OS引導代碼擴展部分外,其余扇區(qū)都不參與操作系統(tǒng)管理與磁盤數(shù)據(jù)管理,通常情況下是沒作用的。操作系統(tǒng)之所以在FAT32中設(shè)置保留扇區(qū),是為了對DBR作備份或留待以后升級時用。FAT32中,DBR偏移0x34占2字節(jié)的數(shù)據(jù)指明了DBR備份扇區(qū)所在,一般為0x06,即第6扇區(qū)。當FAT32分區(qū)DBR扇區(qū)被破壞導致分區(qū)無法訪問時。可以用第6扇區(qū)的原備份替換第0扇區(qū)來找回數(shù)據(jù)。
4.3 FAT表和數(shù)據(jù)的存儲原則。
FAT表(File Allocation Table 文件分配表),是Microsoft在FAT文件系統(tǒng)中用于磁盤數(shù)據(jù)(文件)索引和定位引進的一種鏈式結(jié)構(gòu)。假如把磁盤比作一本書,F(xiàn)AT表可以認為相當于書中的目錄,而文件就是各個章節(jié)的內(nèi)容。但FAT表的表示方法卻與目錄有很大的不同。
在FAT文件系統(tǒng)中,文件的存儲依照FAT表制定的簇鏈式數(shù)據(jù)結(jié)構(gòu)來進行。同時,F(xiàn)AT文件系統(tǒng)將組織數(shù)據(jù)時使用的目錄也抽象為文件,以簡化對數(shù)據(jù)的管理。
★存儲過程假想:
我們模擬對一個分區(qū)存儲數(shù)據(jù)的過程來說明FAT文件系統(tǒng)中數(shù)據(jù)的存儲原則。
假定現(xiàn)在有一個空的完全沒有存放數(shù)據(jù)的磁盤,大小為100KB,我們將其想象為線形的空間地址。為了存儲管理上的便利,我們?nèi)藶榈膶⑦@100KB的空間均分成100份,每份1KB。我們來依次存儲這樣幾個文件:A.TXT(大小10KB),B.TXT(大小53.6KB),C.TXT(大小20.5KB)。
最起碼能夠想到,我們可以順序的在這100KB空間中存放這3個文件。同時不要忘了,我們還要記下他們的大小和開始的位置,這樣下次要用時才能找的到,這就像是目錄。為了便于查找,我們假定用第1K的空間來存儲他們的特征(屬性)。還有,我們設(shè)計的存儲單位是1KB,所以,A.TXT我們需要10個存儲單位(為了說明方便,我們把存儲單位叫做“簇”吧。也能少打點字,呵呵。),B.TXT需要54個簇,C.TXT需要21個簇。可能有人會說B.TXT和C.TXT不是各自浪費了不到1簇的空間嗎?干嘛不讓他們緊挨著,不是省地方嗎?我的回答是,如果按照這樣的方式存儲,目錄中原本只需要記下簇號,現(xiàn)在還需要記下簇內(nèi)的偏移,這樣會增加目錄的存儲量,而且存取沒有了規(guī)則,讀取也不太方便,是得不償失的。
根據(jù)上面所說的思想,我們設(shè)計了這樣的圖4.3.1所示的存儲方式。
我們再考慮如何來寫這三個文件的目錄。對于每個文件而言,一定要記錄的有:文件名,開始簇,大小,創(chuàng)建日期、時間,修改日期、時間,文件的讀寫屬性等。這里大小能不能用結(jié)束簇來計算呢?一定不能,因為文件的大小不一定就是整數(shù)個簇的大小,否則的話像B.TXT的內(nèi)容就是54KB的內(nèi)容了,少了固然不行,可多了也是不行的。那么我們怎么記錄呢?可以想象一下。為了管理上的方便,我們用數(shù)據(jù)庫的管理方式來管理我們的目錄。于是我把1KB再分成10份,假定開始簇號為0,定義每份100B的各個位置的代表含義如圖4.3.2
這樣設(shè)計的結(jié)構(gòu)絕對可以對文件進行正確的讀寫了。接著讓我們設(shè)計的文件系統(tǒng)工作吧。先改動個文件,比如A.TXT,增加點內(nèi)容吧!咦?增加后往哪里放呀,雖然存儲塊的后面有很多空間,但緊隨其后B.TXT的數(shù)據(jù)還頂著呢?要是把A.TXT移到后邊太浪費處理資源,而且也不一定解決問題。這個問題看來暫時解決不了。
那我們換個操作,把B.txt刪了,b.txt的空間隨之釋放。這時候空間如圖4.3.3,目錄如圖4.3.4
這個操作看來還可以,我們接著做,在存入一個文件D.txt(大小為60.3KB),總共100簇的空間只用了31簇,還有68簇剩余,按說能放下??墒牵客抢锓拍??沒有61個連續(xù)的空間了,目錄行沒辦法寫了,看來無連續(xù)塊存儲暫時也不行。
你一定能夠想到我們可以在連續(xù)空間不夠或增加文件長度的時候轉(zhuǎn)移影響我們操作的其他文件,從而騰出空間來,但我要問你,那不是成天啥也不要干了,就是倒騰東西了嗎?
看來我們設(shè)計的文件系統(tǒng)有致命的漏洞,怎么解決呢?。。。。。。。。。。
其實可以這樣解決:
首先我們允許文件的不連續(xù)存儲。目錄中依然只記錄開始簇和文件的大小。那么我們怎么記錄文件占用那些簇呢,以文件映射簇不太方便,因為文件名是不固定的。我們換個思想,可以用簇來映射文件,在整個存儲空間的前部留下幾簇來記錄數(shù)據(jù)區(qū)中數(shù)據(jù)與簇號的關(guān)系。對于上例因為總空間也不大,所以用前部的1Kb的空間來記錄這種對應,假設(shè)3個文件都存儲,空間分配如圖4.3.5,同時修改一下目錄,如圖4.3.6
第一簇用來記錄數(shù)據(jù)區(qū)中每一簇的被占用情況,暫時稱其為文件分配表。結(jié)合文件分配表和文件目錄就可以達到完全的文件讀取了。我們想到,把文件分配表做成一個數(shù)據(jù)表,以圖4.3.7的形式記錄簇與數(shù)據(jù)的對應。
用圖4.3.7的組織方式是完全可以實現(xiàn)對文件占有簇的記錄的。但還不夠效率。比如文件名在文件分配表中記錄太多,浪費空間,而實際上在目錄中已經(jīng)記錄了文件的開始簇了。所以可以改良一下,用鏈的方式來存放占有簇的關(guān)系,變成圖4.3.8的組織方式。
參照圖4.3.8來理解一下文件分配表的意義。如文件a.txt我們根據(jù)目錄項中指定的a.txt的首簇為2,然后找到文件分配表的第2簇記錄,上面登記的是3,我們就能確定下一簇是3。找到文件分配表的第3簇記錄,上面登記的是4,我們就能確定下一簇是4......直到指到第11簇,發(fā)現(xiàn)下一個指向是FF,就是結(jié)束。文件便絲毫無誤讀取完畢。
我們再看上面提到的第三種情況,就是將b.txt刪除以后,存入一個大小為60.3KB的d.txt。利用簇鏈可以很容易的實現(xiàn)。實現(xiàn)后的磁盤如圖4.3.9 4.3.10 ; 4.3.11
★FAT16存儲原理:
當把一部分磁盤空間格式化為fat文件系統(tǒng)時,fat文件系統(tǒng)就將這個分區(qū)當成整塊可分配的區(qū)域進行規(guī)劃,以便于數(shù)據(jù)的存儲。一般來講,其劃分形式如圖7所示。我們把FAT16部分提取出來,詳細描述一下:
FAT16是Microsoft較早推出的文件系統(tǒng),具有高度兼容性,目前仍然廣泛應用于個人電腦尤其是移動存儲設(shè)備中,F(xiàn)AT16簡單來講由圖4.3.11所示的6部分組成(主要是前5部分)。引導扇區(qū)(DBR)我們已經(jīng)說過,FAT16在DBR之后沒有留有任何保留扇區(qū),其后緊隨的便是FAT表。FAT表是FAT16用來記錄磁盤數(shù)據(jù)區(qū)簇鏈結(jié)構(gòu)的。像前面我們說過的例子一樣,F(xiàn)AT將磁盤空間按一定數(shù)目的扇區(qū)為單位進行劃分,這樣的單位稱為簇。通常情況下,每扇區(qū)512字節(jié)的原則是不變的。簇的大小一般是2n(n為整數(shù))個扇區(qū)的大小,像512B,1K,2K,4K,8K,16K,32K,64K。實際中通常不超過32K。 之所以簇為單位而不以扇區(qū)為單位進行磁盤的分配,是因為當分區(qū)容量較大時,采用大小為512b的扇區(qū)管理會增加fat表的項數(shù),對大文件存取增加消耗,文件系統(tǒng)效率不高。分區(qū)的大小和簇的取值是有關(guān)系的,見表9。
圖4.3.11 Fat16的組織形式
引導扇區(qū)FAT1FAT2(重復的)根文件夾其他文件夾及所有文件剩余扇區(qū)
1扇區(qū)實際情況取大小同F(xiàn)AT132個扇區(qū)開始簇編號(從2開始)不足一簇
表9 FAT16分區(qū)大小與對因簇大小
分區(qū)空間大小每個簇的扇區(qū)簇空間大小
0MB-32MB1512個字節(jié)
33MB-64MB21k
65MB-128MB42k
129MB-225MB84k
256MB-511MB168k
512MB-1023MB3216k
1024MB-2047MB6432k
2048MB-4095MB12864k
注意:少于32680個扇區(qū)的分區(qū)中,簇空間大小可最多達到每個簇8個扇區(qū)。不管用戶是使用磁盤管理器來格式化分區(qū),還是使用命令提示行鍵入format命令格式化,格式化程序都創(chuàng)建一個12位的FAT。少于16MB的分區(qū),系統(tǒng)通常會將其格式化成12位的FAT,F(xiàn)AT12是FAT的初始實現(xiàn)形式,是針對小型介質(zhì)的。FAT12文件分配表要比FAT16和FAT32的文件分配表小,因為它對每個條目使用的空間較少。這就給數(shù)據(jù)留下較多的空間。所有用FAT12格式化的5.25英寸軟盤以及1.44MB的3.5英寸軟盤都是由FAT12格式化的。除了FAT表中記錄每簇鏈結(jié)的二進制位數(shù)與FAT16不同外,其余原理與FAT16均相同,不再單獨解釋。。。
格式化FAT16分區(qū)時,格式化程序根據(jù)分區(qū)的大小確定簇的大小,然后根據(jù)保留扇區(qū)的數(shù)目、根目錄的扇區(qū)數(shù)目、數(shù)據(jù)區(qū)可分的簇數(shù)與FAT表本身所占空間來確定FAT表所需的扇區(qū)數(shù)目,然后將計算后的結(jié)果寫入DBR的相關(guān)位置。
FAT16 DBR參數(shù)的偏移0x11處記錄了根目錄所占扇區(qū)的數(shù)目。偏移0x16記錄了FAT表所占扇區(qū)的數(shù)據(jù)。偏移0x10記錄了FAT表的副本數(shù)目。系統(tǒng)在得到這幾項參數(shù)以后,就可以確定數(shù)據(jù)區(qū)的開始扇區(qū)偏移了。
FAT16文件系統(tǒng)從根目錄所占的32個扇區(qū)之后的第一個扇區(qū)開始以簇為單位進行數(shù)據(jù)的處理,這之前仍以扇區(qū)為單位。對于根目錄之后的第一個簇,系統(tǒng)并不編號為第0簇或第1簇 (可能是留作關(guān)鍵字的原因吧),而是編號為第2簇,也就是說數(shù)據(jù)區(qū)順序上的第1個簇也是編號上的第2簇。
FAT文件系統(tǒng)之所以有12,16,32不同的版本之分,其根本在于FAT表用來記錄任意一簇鏈接的二進制位數(shù)。以FAT16為例,每一簇在FAT表中占據(jù)2字節(jié)(二進制16位)。所以,F(xiàn)AT16最大可以表示的簇號為0xFFFF(十進制的65535),以32K為簇的大小的話,F(xiàn)AT32可以管理的最大磁盤空間為:32KB×65535=2048MB,這就是為什么FAT16不支持超過2GB分區(qū)的原因。
FAT表實際上是一個數(shù)據(jù)表,以2個字節(jié)為單位,我們暫將這個單位稱為FAT記錄項,通常情況其第1、2個記錄項(前4個字節(jié))用作介質(zhì)描述。從第三個記錄項開始記錄除根目錄外的其他文件及文件夾的簇鏈情況。根據(jù)簇的表現(xiàn)情況FAT用相應的取值來描述,見表10
表10 FAT16記錄項的取值含義(16進制)
FAT16記錄項的取值對應簇的表現(xiàn)情況
0000未分配的簇
0002~FFEF已分配的簇
FFF0~FFF6系統(tǒng)保留
FFF7壞簇
FFF8~FFFF文件結(jié)束簇
看一幅在winhex所截FAT16的文件分配表,圖10:
如圖,F(xiàn)AT表以"F8 FF FF FF" 開頭,此2字節(jié)為介質(zhì)描述單元,并不參與FAT表簇鏈關(guān)系。小紅字標出的是FAT扇區(qū)每2字節(jié)對應的簇號。
相對偏移0x4~0x5偏移為第2簇(順序上第1簇),此處為FF,表示存儲在第2簇上的文件(目錄)是個小文件,只占用1個簇便結(jié)束了。
第3簇中存放的數(shù)據(jù)是0x0005,這是一個文件或文件夾的首簇。其內(nèi)容為第5簇,就是說接下來的簇位于第5簇——〉 FAT表指引我們到達FAT表的第5簇指向,上面寫的數(shù)據(jù)是“FF FF”,意即此文件已至尾簇。
第4簇中存放的數(shù)據(jù)是0x0006,這又是一個文件或文件夾的首簇。其內(nèi)容為第6簇,就是說接下來的簇位于第6簇——〉FAT表指引我們到達FAT表的第6簇指向,上面寫的數(shù)據(jù)是0x0007,就是說接下來的簇位于第7簇——〉FAT表指引我們到達FAT表的第7簇指向……直到根據(jù)FAT鏈讀取到扇區(qū)相對偏移0x1A~0x1B,也就是第13簇,上面寫的數(shù)據(jù)是0x000E,也就是指向第14簇——〉14簇的內(nèi)容為"FF FF",意即此文件已至尾簇。
后面的FAT表數(shù)據(jù)與上面的道理相同。不再分析。
FAT表記錄了磁盤數(shù)據(jù)文件的存儲鏈表,對于數(shù)據(jù)的讀取而言是極其重要的,以至于Microsoft為其開發(fā)的FAT文件系統(tǒng)中的FAT表創(chuàng)建了一份備份,就是我們看到的FAT2。FAT2與FAT1的內(nèi)容通常是即時同步的,也就是說如果通過正常的系統(tǒng)讀寫對FAT1做了更改,那么FAT2也同樣被更新。如果從這個角度來看,系統(tǒng)的這個功能在數(shù)據(jù)恢復時是個天災。
FAT文件系統(tǒng)的目錄結(jié)構(gòu)其實是一顆有向的從根到葉的樹,這里提到的有向是指對于FAT分區(qū)內(nèi)的任一文件(包括文件夾),均需從根目錄尋址來找到??梢赃@樣認為:目錄存儲結(jié)構(gòu)的入口就是根目錄。
FAT文件系統(tǒng)根據(jù)根目錄來尋址其他文件(包括文件夾),故而根目錄的位置必須在磁盤存取數(shù)據(jù)之前得以確定。FAT文件系統(tǒng)就是根據(jù)分區(qū)的相關(guān)DBR參數(shù)與DBR中存放的已經(jīng)計算好的FAT表(2份)的大小來確定的。格式化以后,跟目錄的大小和位置其實都已經(jīng)確定下來了:位置緊隨FAT2之后,大小通常為32個扇區(qū)。根目錄之后便是數(shù)據(jù)區(qū)第2簇。
FAT文件系統(tǒng)的一個重要思想是把目錄(文件夾)當作一個特殊的文件來處理,F(xiàn)AT32甚至將根目錄當作文件處理(旁:NTFS將分區(qū)參數(shù)、安全權(quán)限等好多東西抽象為文件更是這個思想的升華),在FAT16中,雖然根目錄地位并不等同于普通的文件或者說是目錄,但其組織形式和普通的目錄(文件夾)并沒有不同。FAT分區(qū)中所有的文件夾(目錄)文件,實際上可以看作是一個存放其他文件(文件夾)入口參數(shù)的數(shù)據(jù)表。所以目錄的占用空間的大小并不等同于其下所有數(shù)據(jù)的大小,但也不等同于0。通常是占很小的空間的,可以看作目錄文件是一個簡單的二維表文件。其具體存儲原理是:
不管目錄文件所占空間為多少簇,一簇為多少字節(jié)。系統(tǒng)都會以32個字節(jié)為單位進行目錄文件所占簇的分配。這32個字節(jié)以確定的偏移來定義本目錄下的一個文件(或文件夾)的屬性,實際上是一個簡單的二維表。
這32個字節(jié)的各字節(jié)偏移定義如表11:
表11 FAT16目錄項32個字節(jié)的表示定義
字節(jié)偏移(16進制)字節(jié)數(shù)定義
0x0~0x78文件名
0x8~0xA3擴展名
0xB1屬性字節(jié)00000000(讀寫)
00000001(只讀)
00000010(隱藏)
00000100(系統(tǒng))
00001000(卷標)
00010000(子目錄)
00100000(歸檔)
0xC~0x1510系統(tǒng)保留
0x16~0x172文件的最近修改時間
0x18~0x192文件的最近修改日期
0x1A~0x1B2表示文件的首簇號
0x1C~0x1F4表示文件的長度
對圖10中的一些取值進行說明:
(1)、對于短文件名,系統(tǒng)將文件名分成兩部分進行存儲,即主文件名+擴展名。0x0~0x7字節(jié)記錄文件的主文件名,0x8~0xA記錄文件的擴展名,取文件名中的ASCII碼值。不記錄主文件名與擴展名之間的"." 主文件名不足8個字符以空白符(20H)填充,擴展名不足3個字符同樣以空白符(20H)填充。0x0偏移處的取值若為00H,表明目錄項為空;若為E5H,表明目錄項曾被使用,但對應的文件或文件夾已被刪除。(這也是誤刪除后恢復的理論依據(jù))。文件名中的第一個字符若為“.”或“..”表示這個簇記錄的是一個子目錄的目錄項?!?”代表當前目錄;“..”代表上級目錄(和我們在dos或windows中的使用意思是一樣的,如果磁盤數(shù)據(jù)被破壞,就可以通過這兩個目錄項的具體參數(shù)推算磁盤的數(shù)據(jù)區(qū)的起始位置,猜測簇的大小等等,故而是比較重要的)
(2)、0xB的屬性字段:可以看作系統(tǒng)將0xB的一個字節(jié)分成8位,用其中的一位代表某種屬性的有或無。這樣,一個字節(jié)中的8位每位取不同的值就能反映各個屬性的不同取值了。如00000101就表示這是個文件,屬性是只讀、系統(tǒng)。
(3)、0xC~0x15在原FAT16的定義中是保留未用的。在高版本的WINDOWS系統(tǒng)中有時也用它來記錄修改時間和最近訪問時間。那樣其字段的意義和FAT32的定義是相同的,見后邊FAT32。
(4)、0x16~0x17中的時間=小時*2048+分鐘*32+秒/2。得出的結(jié)果換算成16進制填入即可。也就是:0x16字節(jié)的0~4位是以2秒為單位的量值;0x16字節(jié)的5~7位和0x17字節(jié)的0~2位是分鐘;0x17字節(jié)的3~7位是小時。
(5)、0x18~0x19中的日期=(年份-1980)*512+月份*32+日。得出的結(jié)果換算成16進制填入即可。也就是:0x18字節(jié)0~4位是日期數(shù);0x18字節(jié)5~7位和0x19字節(jié)0位是月份;0x19字節(jié)的1~7位為年號,原定義中0~119分別代表1980~2099,目前高版本的Windows允許取0~127,即年號最大可以到2107年。
(6)、0x1A~0x1B存放文件或目錄的表示文件的首簇號,系統(tǒng)根據(jù)掌握的首簇號在FAT表中找到入口,然后再跟蹤簇鏈直至簇尾,同時用0x1C~0x1F處字節(jié)判定有效性。就可以完全無誤的讀取文件(目錄)了。
(7)、普通子目錄的尋址過程也是通過其父目錄中的目錄項來指定的,與數(shù)據(jù)文件(指非目錄文件)不同的是目錄項偏移0xB的第4位置1,而數(shù)據(jù)文件為0。
對于整個FAT分區(qū)而言,簇的分配并不完全總是分配干凈的。如一個數(shù)據(jù)區(qū)為99個扇區(qū)的FAT系統(tǒng),如果簇的大小設(shè)定為2扇區(qū),就會有1個扇區(qū)無法分配給任何一個簇。這就是分區(qū)的剩余扇區(qū),位于分區(qū)的末尾。有的系統(tǒng)用最后一個剩余扇區(qū)備份本分區(qū)的DBR,這也是一種好的備份方法。
早的FAT16系統(tǒng)并沒有長文件名一說,Windows操作系統(tǒng)已經(jīng)完全支持在FAT16上的長文件名了。FAT16的長文件名與FAT32長文件名的定義是相同的,關(guān)于長文件名,在FAT32部分再詳細作解釋。
★FAT32存儲原理:
FAT32是個非常有功勞的文件系統(tǒng),Microsoft成功地設(shè)計并運用了它,直到今天NTFS鋪天蓋地襲來的時候,F(xiàn)AT32依然占據(jù)著Microsoft Windows文件系統(tǒng)中重要的地位。FAT32最早是出于FAT16不支持大分區(qū)、單位簇容量大以致空間急劇浪費等缺點設(shè)計的。實際應用中,F(xiàn)AT32還是成功的。
FAT32與FAT16的原理基本上是相同的,圖4.3.12標出了FAT32分區(qū)的基本構(gòu)成。
圖4.3.12 Fat32的組織形式
引導扇區(qū)其余保留扇區(qū)FAT1FAT2(重復的)根文件夾首簇其他文件夾及所有文件剩余扇區(qū)
1扇區(qū)31個扇區(qū)實際情況取大小同F(xiàn)AT1第2簇
不足一簇
保留扇區(qū)
┗━━━━━━━━數(shù)據(jù)區(qū)━━━━━━━━┛
FAT32在格式化的過程中就根據(jù)分區(qū)的特點構(gòu)建好了它的DBR,其中BPB參數(shù)是很重要的,可以回過頭來看一下表4和表5。首先FAT32保留扇區(qū)的數(shù)目默認為32個,而不是FAT16的僅僅一個。這樣的好處是有助于磁盤DBR指令的長度擴展,而且可以為DBR扇區(qū)留有備份空間。上面我們已經(jīng)提到,構(gòu)建在FAT32上的win98或win2000、winXP,其操作系統(tǒng)引導代碼并非只占一個扇區(qū)了。留有多余的保留扇區(qū)就可以很好的拓展OS引導代碼。在BPB中也記錄了DBR扇區(qū)的備份扇區(qū)編號。備份扇區(qū)可以讓我們在磁盤遭到意外破壞時恢復DBR。
FAT32的文件分配表的數(shù)據(jù)結(jié)構(gòu)依然和FAT16相同,所不同的是,F(xiàn)AT32將記錄簇鏈的二進制位數(shù)擴展到了32位,故而這種文件系統(tǒng)稱為FAT32。32位二進制位的簇鏈決定了FAT表最大可以尋址2T個簇。這樣即使簇的大小為1扇區(qū),理論上仍然能夠?qū)ぶ?TB范圍內(nèi)的分區(qū)。但實際中FAT32是不能尋址這樣大的空間的,隨著分區(qū)空間大小的增加,F(xiàn)AT表的記錄數(shù)會變得臃腫不堪,嚴重影響系統(tǒng)的性能。所以在實際中通常不格式化超過32GB的FAT32分區(qū)。WIN2000及之上的OS已經(jīng)不直接支持對超過32GB的分區(qū)格式化成FAT32,但WIN98依然可以格式化大到127GB的FAT32分區(qū),但這樣沒必要也不推薦。同時FAT32也有小的限制,F(xiàn)AT32卷必須至少有65527個簇,所以對于小的分區(qū),仍然需要使用FAT16或FAT12。
分區(qū)變大時,如果簇很小,文件分配表也隨之變大。仍然會有上面的效率問題存在。既要有效地讀寫大文件,又要最大可能的減少空間的浪費。FAT32同樣規(guī)定了相應的分區(qū)空間對應的簇的大小,見表12:
表12 FAT32分區(qū)大小與對因簇大小
分區(qū)空間大小每個簇的扇區(qū)簇空間大小
<8GB84k
>=8GB且<16GB168k
>=16GB且<32GB3216k
>=32GB6432k
簇的取值意義和FAT16類似,不過是位數(shù)長了點罷了,比較見表13:
表13 FAT各系統(tǒng)記錄項的取值含義(16進制)
FAT12記錄項的取值FAT16記錄項的取值FAT32記錄項的取值對應簇的表現(xiàn)情況
000000000000000未分配的簇
002~FFF0002~FFEF00000002~FFFFFFEF已分配的簇
FF0~FF6FFF0~FFF6FFFFFFF0~FFFFFFF6系統(tǒng)保留
FF7FFF7FFFFFFF7壞簇
FF8~FFFFFF8~FFFFFFFFFFF8~FFFFFFFF文件結(jié)束簇
FAT32的另一項重大改革是根目錄的文件化,即將根目錄等同于普通的文件。這樣根目錄便沒有了FAT16中512個目錄項的限制,不夠用的時候增加簇鏈,分配空簇即可。而且,根目錄的位置也不再硬性地固定了,可以存儲在分區(qū)內(nèi)可尋址的任意簇內(nèi),不過通常根目錄是最早建立的(格式化就生成了)目錄表。所以,我們看到的情況基本上都是根目錄首簇占簇區(qū)順序上的第1個簇。在圖4.3.12中也是按這種情況制作的畫的。
FAT32對簇的編號依然同F(xiàn)AT16。順序上第1個簇仍然編號為第2簇,通常為根目錄所用(這和FAT16是不同的,F(xiàn)AT16的根目錄并不占簇區(qū)空間,32個扇區(qū)的根目錄以后才是簇區(qū)第1個簇)
FAT32的文件尋址方法與FAT16相同,但目錄項的各字節(jié)參數(shù)意義卻與FAT16有所不同,一方面它啟用了FAT16中的目錄項保留字段,同時又完全支持長文件名了。
對于短文件格式的目錄項。其參數(shù)意義見表14:
表14 FAT32短文件目錄項32個字節(jié)的表示定義
字節(jié)偏移(16進制)字節(jié)數(shù)定義
0x0~0x78文件名
0x8~0xA3擴展名
0xB*1屬性字節(jié)00000000(讀寫)
00000001(只讀)
00000010(隱藏)
00000100(系統(tǒng))
00001000(卷標)
00010000(子目錄)
00100000(歸檔)
0xC1系統(tǒng)保留
0xD1創(chuàng)建時間的10毫秒位
0xE~0xF2文件創(chuàng)建時間
0x10~0x112文件創(chuàng)建日期
0x12~0x132文件最后訪問日期
0x14~0x152文件起始簇號的高16位
0x16~0x172文件的最近修改時間
0x18~0x192文件的最近修改日期
0x1A~0x1B2文件起始簇號的低16位
0x1C~0x1F4表示文件的長度
* 此字段在短文件目錄項中不可取值0FH,如果設(shè)值為0FH,目錄段為長文件名目錄段
說明:
(1)、這是FAT32短文件格式目錄項的意義。其中文件名、擴展名、時間、日期的算法和FAT16時相同的。
(2)、由于FAT32可尋址的簇號到了32位二進制數(shù)。所以系統(tǒng)在記錄文件(文件夾)開始簇地址的時候也需要32位來記錄,F(xiàn)AT32啟用目錄項偏移0x12~0x13來表示起始簇號的高16位。
(3)、文件長度依然用4個字節(jié)表示,這說明FAT32依然只支持小于4GB的文件(目錄),超過4GB的文件(目錄),系統(tǒng)會截斷處理。
FAT32的一個重要的特點是完全支持長文件名。長文件名依然是記錄在目錄項中的。為了低版本的OS或程序能正確讀取長文件名文件,系統(tǒng)自動為所有長文件名文件創(chuàng)建了一個對應的短文件名,使對應數(shù)據(jù)既可以用長文件名尋址,也可以用短文件名尋址。不支持長文件名的OS或程序會忽略它認為不合法的長文件名字段,而支持長文件名的OS或程序則會以長文件名為顯式項來記錄和編輯,并隱藏起短文件名。
當創(chuàng)建一個長文件名文件時,系統(tǒng)會自動加上對應的短文件名,其一般有的原則:
(1)、取長文件名的前6個字符加上"~1"形成短文件名,擴展名不變。
(2)、如果已存在這個文件名,則符號"~"后的數(shù)字遞增,直到5。
(3)、如果文件名中"~"后面的數(shù)字達到5,則短文件名只使用長文件名的前兩個字母。通過數(shù)學操縱長文件名的剩余字母生成短文件名的后四個字母,然后加后綴"~1"直到最后(如果有必要,或是其他數(shù)字以避免重復的文件名)。
(4)、如果存在老OS或程序無法讀取的字符,換以"_"
長文件名的實現(xiàn)有賴于目錄項偏移為0xB的屬性字節(jié),當此字節(jié)的屬性為:只讀、隱藏、系統(tǒng)、卷標,即其值為0FH時,DOS和WIN32會認為其不合法而忽略其存在。這正是長文件名存在的依據(jù)。將目錄項的0xB置為0F,其他就任由系統(tǒng)定義了,Windows9x或Windows 2000、XP通常支持不超過255個字符的長文件名。系統(tǒng)將長文件名以13個字符為單位進行切割,每一組占據(jù)一個目錄項。所以可能一個文件需要多個目錄項,這時長文件名的各個目錄項按倒序排列在目錄表中,以防與其他文件名混淆。
長文件名中的字符采用unicode形式編碼(一個巨大的進步哦),每個字符占據(jù)2字節(jié)的空間。其目錄項定義如表15。
表15 FAT32長文件目錄項32個字節(jié)的表示定義
字節(jié)偏移(16進制)字節(jié)數(shù)定義
0x01屬性字節(jié)位意義7保留未用
61表示長文件最后一個目錄項
5保留未用
4順序號數(shù)值
3
2
1
0
0x1~0xA10長文件名unicode碼①
0xB1長文件名目錄項標志,取值0FH
0xC1系統(tǒng)保留
0xD1校驗值(根據(jù)短文件名計算得出)
0xE~0x1912長文件名unicode碼②
0x1A~0x1B2文件起始簇號(目前常置0)
0x1C~0x1F4長文件名unicode碼③
系統(tǒng)在存儲長文件名時,總是先按倒序填充長文件名目錄項,然后緊跟其對應的短文件名。從表15可以看出,長文件名中并不存儲對應文件的文件開始簇、文件大小、各種時間和日期屬性。文件的這些屬性還是存放在短文件名目錄項中,一個長文件名總是和其相應的短文件名一一對應,短文件名沒有了長文件名還可以讀,但長文件名如果沒有對應的短文件名,不管什么系統(tǒng)都將忽略其存在。所以短文件名是至關(guān)重要的。在不支持長文件名的環(huán)境中對短文件名中的文件名和擴展名字段作更改(包括刪除,因為刪除是對首字符改寫E5H),都會使長文件名形同虛設(shè)。長文件名和短文件名之間的聯(lián)系光靠他們之間的位置關(guān)系維系顯然遠遠不夠。其實,長文件名的0xD字節(jié)的校驗和起很重要的作用,此校驗和是用短文件名的11個字符通過一種運算方式來得到的。系統(tǒng)根據(jù)相應的算法來確定相應的長文件名和短文件名是否匹配。這個算法不太容易用公式說明,我們用一段c程序來加以說明。
假設(shè)文件名11個字符組成字符串shortname[],校驗和用chknum表示。得到過程如下:
int i,j,chknum=0;
for (i=11; i>0; i--)
chksum = ((chksum & 1) ? 0x80 : 0) + (chksum >> 1) + shortname[j++];
如果通過短文件名計算出來的校驗和與長文件名中的0xD偏移處數(shù)據(jù)不相等。系統(tǒng)無論如何都不會將它們配對的。
依據(jù)長文件名和短文件名對目錄項的定義,加上對簇的編號和鏈接,F(xiàn)AT32上數(shù)據(jù)的讀取便游刃有余了。
FAT32文件系統(tǒng)學習
目的:需要編寫SD讀圖片的底層驅(qū)動程序。所以要了解一個SD卡常用文件系統(tǒng)基本概念。累計學習用時2.5小時。
一,F(xiàn)AT32的保留區(qū)
1,引導扇區(qū) :引導扇區(qū)是FAT32文件系統(tǒng)的第一個扇區(qū),也稱為DBR扇區(qū)。它包含這樣一些文件系統(tǒng)的基本信息:
【1】 每扇區(qū)字節(jié)數(shù) 【2】 每簇扇區(qū)數(shù) 【3】 保留扇區(qū)數(shù)【4】 FAT表個數(shù) 【5】 文件系統(tǒng)大?。ㄉ葏^(qū)數(shù))【6】 每個FAT表大?。ㄉ葏^(qū)數(shù)) 【7】 根目錄起始簇號 【8】 其他一些附加信息
邊看說明,邊看圖片不太方便,我就按照說明內(nèi)容,把說明直接標注在圖片上了。
我的SD卡是手機里的tf卡+sd卡套。之前沒有問題。當我第一次格式化后,就發(fā)現(xiàn)不正常了。雖然存取文件都沒問題。但是放在我的開發(fā)板上測試SD的時候,數(shù)據(jù)顯示不正確。
現(xiàn)在我初步發(fā)現(xiàn)問題在這里
【13】0x1C~0x1F:4個字節(jié),分區(qū)前已使用扇區(qū)數(shù),137(0x00 00 00 89)。(這個數(shù)據(jù)要尤其的重視,文件系統(tǒng)初始化的第一步要找的就是這玩意兒。因為我們的SD卡沒有分區(qū),默認就是一個分區(qū),這個數(shù)據(jù)就是相對于MBR(關(guān)于MBR的介紹請讀者參看8.4小節(jié)的DOC 分區(qū))的地址偏移量,MBR的扇區(qū)地址才是整個SD卡的物理扇區(qū)號為0的那個地址,也就是說文件系統(tǒng)并不是處在整個SD卡最開始的地方,它處在MBR所處的保留區(qū)之后,于是我們可以對使用FAT32文件系統(tǒng)的SD卡整體布局給出如下圖示)
但是我0x1C到0x1F的4個字節(jié)為0.不知道是不是問題。
2,引導代碼
FAT32文件系統(tǒng)引導扇區(qū)的512字節(jié)中,90~509字節(jié)為引導代碼,而FAT12/16則是62~509字節(jié)為引導代碼。同時,F(xiàn)AT32還可以利用引導扇區(qū)后的山區(qū)空間存放附加的引導代碼。
一個FAT卷即使不是可引導文件文件系統(tǒng),也會存在引導代碼。
3,F(xiàn)SINFO信息扇區(qū)
FAT32在保留區(qū)中增加了一個FSINFO扇區(qū),用以記錄文件系統(tǒng)中空閑簇的數(shù)量以及下一可用簇的簇號等信息,以供操作系統(tǒng)作為參考。
FSINFO信息扇區(qū)結(jié)構(gòu)
省略
溫馨提示:通常情況下,文件系統(tǒng)的2號扇區(qū)結(jié)尾也會被設(shè)置“55 AA”標志。6號扇區(qū)也會有一個引導扇區(qū)的備份,相應的,7號扇區(qū)應該是一個備份FSINFO信息扇區(qū)。8號扇區(qū)可以看做是2號扇區(qū)的備份,它的結(jié)尾也會有一個“55 AA”標志。
二,F(xiàn)AT32的FAT表
1 FAT表概述
位于保留區(qū)后的是FAT區(qū),有兩個完全相同的FAT(File Allocation Table, 文件分配表)表組成,F(xiàn)AT文件系統(tǒng)的名字也是因此而來。
重要說明:
1. 對于文件系統(tǒng)來說,F(xiàn)AT表有兩個重要作用:描述簇的分配狀態(tài)以及標明文件或目錄的下一簇的簇號。
2. 通常情況下,一個FAT把文件系統(tǒng)會有兩個FAT表,但有時也允許只有一個FAT表,F(xiàn)AT表的具體個數(shù)記錄在引導扇區(qū)的偏移0x10字節(jié)處。
3. 由于FAT區(qū)緊跟在文件系統(tǒng)保留區(qū)后,所以FAT1在文件系統(tǒng)中的位置可以通過引導記錄中偏移0x0E~0x0F字節(jié)處的“保留扇區(qū)數(shù)”得到。
4. FAT2緊跟在FAT1之后,它的位置可以通過FAT1的位置加上FAT表的大小扇區(qū)數(shù)計算出來。
2 FAT表的特性
FAT表由一系列大小相等的FAT表項組成,總的說來FAT表有如下特性:
1. FAT32中每個簇的簇地址,是有32bit(4個字節(jié))記錄在FAT表中。FAT表中的所有字節(jié)位置以4字節(jié)為單位進行劃分,并對所有劃分后的位置由0進行地址編號。0號地址與1號地址被系統(tǒng)保留并存儲特殊標志內(nèi)容。從2號地址開始,每個地址對應于數(shù)據(jù)區(qū)的簇號,F(xiàn)AT表中的地址編號與數(shù)據(jù)區(qū)中的簇號相同。我們稱FAT表中的這些地址為FAT表項,F(xiàn)AT表項中記錄的值稱為FAT表項值。
2. 當文件系統(tǒng)被創(chuàng)建,也就是進行格式化操作時,分配給FAT區(qū)域的空間將會被清空,在FAT1與FAT2的0號表項與1號表項寫入特定值。由于創(chuàng)建文件系統(tǒng)的同時也會創(chuàng)建根目錄,也就是為根目錄分配了一個簇空間,通常為2號簇,所以2號簇所對應的2號FAT表項也會被寫入一個結(jié)束標記。如下圖所示:
3. 如果某個簇未被分配使用,它所對應的FAT表項內(nèi)的FAT表項值即用0進行填充,表示該FAT表項所對應的簇未被分配。
4. 當某個簇已被分配使用時,則它對應的FAT表項內(nèi)的FAT表項值也就是該文件的下一個存儲位置的簇號。如果該文件結(jié)束于該簇,則在它的FAT表項中記錄的是一個文件結(jié)束標記,對于FAT32而言,代表文件結(jié)束的FAT表項值為0x0FFFFFFF。
5. 如果某個簇存在壞扇區(qū),則整個簇會用FAT表項值0xFFFFFF7標記為壞簇,不再使用,這個壞簇標記就記錄在它所對應的FAT表項中。
6. 由于簇號起始于2號,所以FAT表項的0號表項與1號表項不與任何簇對應。FAT32的0號表項值總是“F8FFFF0F”。如上圖所示。
7. 1號表項可能被用于記錄臟標志,以說明文件系統(tǒng)沒有被正常卸載或者磁盤表面存在錯誤。不過這個值并不重要。正常情況下1號表項的值為“FFFFFFFF”或“FFFFFF0F”。
8. 在文件系統(tǒng)中新建文件時,如果新建的文件只占用一個簇,為其分配的簇對應的FAT表項將會寫入結(jié)束標記。如果新建的文件不只占用一個簇,則在其所占用的每個簇對應的FAT表項中寫入為其分配的下一簇的簇號,在最后一個簇對應的FAT表象中寫入結(jié)束標記。
9. 新建目錄時,只為其分配一個簇的空間,對應的FAT表項中寫入結(jié)束標記。當目錄增大超出一個簇的大小時,將會在空閑空間中繼續(xù)為其分配一個簇,并在FAT表中為其建立FAT表鏈以描述它所占用的簇情況。
10. 對文件或目錄進行操作時,他們所對應的FAT表項將會被清空,設(shè)置為0以表示其所對應的簇處于未分配狀態(tài)。
要找一個簇的FAT表項,只要用它的簇號乘以每個FAT表項的字節(jié)數(shù)即可。Winhex提供了直接跳轉(zhuǎn)到某個指定FAT表項的功能,單擊position|go to FAT Entry,即可彈出轉(zhuǎn)到FAT項對話框,在對話框輸入目標FAT項號碼后單擊OK,光標即會在該FAT項的第一個字節(jié)上閃爍。
看到這里,我明白了簇地址就是一個大文件,被拆分成小塊后的一個個個像鏈表一樣的地址。保存在FAT表中。
今天主要看了保留區(qū)和FAT表區(qū)。明天繼續(xù)看數(shù)據(jù)區(qū)。
FAT32文件系統(tǒng)學習(2) —— FAT表
1、題外話
在繼續(xù)本文學習FAT32文件系統(tǒng)之前,先來插入一點別的話題。我們都知道U盤有一個屬性是容量,就拿筆者的U盤為例,筆者手上的U盤是金士頓的DataTraveler G3 4GB的一個U盤。電腦上顯示的容量如圖1所示為3.75GB。那么這個3.75GB是怎么計算出來的呢?
圖 1 系統(tǒng)顯示U盤屬性
我們先來回顧一下上一篇BPB參數(shù)當中的Sectors(扇區(qū)總數(shù))這個參數(shù),這一參數(shù)代表了這個U盤在出廠時的總扇區(qū)數(shù),筆者手上這個是7884672個,可以從圖2中看到。其中每個扇區(qū)為512 B,也就是說總共可以容納4036952064 B約為3.76GB的數(shù)據(jù)。但是這其中一部分是要用來存放FAT32文件系統(tǒng)的相關(guān)信息參數(shù)的,比如FAT表,BPB等。我們這邊來算一下,首先需要減去1016個保留扇區(qū),還有兩個FAT表總共是7684 * 2 = 15368個扇區(qū),所示還剩下的字節(jié)數(shù)為4036952064 B - ( 1016 + 15368 ) * 512 B = 4028563456 B 正好是圖中顯示的容量。所以可以得出結(jié)論,系統(tǒng)顯示的U盤容量 = ( 總扇區(qū)數(shù) - 保留扇區(qū)數(shù) - FAT表扇區(qū)數(shù) * FAT表個數(shù) ) * 512 B。經(jīng)計算可得實際的使用率是99.79%。所以相對與整個U盤來說,F(xiàn)AT32文件系統(tǒng)用于存儲相關(guān)信息部分的損耗是很小的。
圖 2 筆者用上一篇中寫的工具查看了U盤的各項參數(shù)
好了,接下來進入正題,繼續(xù)學習FAT32文件系統(tǒng)的FAT表部分。
2、本文目錄
1、題外話
2、FAT表的讀取
3、FAT表項
4、參考文獻
3、FAT表的讀取
首先FAT表一般來說有兩張,另一張用于備份。兩張表是前后緊挨在一起的,只要計算出了FAT1表的偏移之后加上FAT表的大小就可以得到FAT2表的偏移。FAT1表的偏移地址計算公式如下[4]
FAT1表偏移 = 保留扇區(qū)數(shù) * 每扇區(qū)字節(jié)數(shù)
由圖2可知,在本例中,F(xiàn)AT1表的偏移 = 1016 * 512 B = 520192 = 0x7F000。同理:
FAT2表的偏移 = FAT1+FAT表的大小 = (保留扇區(qū)數(shù) + FAT表扇區(qū)數(shù)) * 每扇區(qū)字節(jié)數(shù)
在本例中,F(xiàn)AT2表的偏移 = (1016 + 7684) * 512 B = 4454400 = 0x43F800。用上一篇中講到的程序可以讀取出兩張FAT表的內(nèi)容,一般情況下兩張表的內(nèi)容應該是完全一樣的。筆者讀取了第一張FAT表起始部分的內(nèi)容,如圖3所示:
圖 3 FAT表起始部分內(nèi)容
4、FAT表項
在分析FAT表之前先來說明一下FAT的構(gòu)成。FAT表即文件分配表(File Allocation Table)。FAT32文件表是由一個個表項組成的一張表,其中每一個表項由一個32位的二進制組成,其值對應了相應簇的使用情況,如2號表項對應了2號簇的使用情況,3號表項對應了3號簇的使用情況,依此類推。(但是第0和第1項例外,下面會有說明)。每個表項對應數(shù)值的含義如表1所示[2]:
表項數(shù)值對應含義
0x00000000空閑簇,即表示可用
0x00000001保留簇
0x00000002 - 0x0FFFFFEF被占用的簇,其值指向下一個簇號
0x0FFFFFF0 - 0x0FFFFFF6保留值
0x0FFFFFF7壞簇
0x0FFFFFF8 - 0x0FFFFFFF文件最后一個簇
表 1 表項數(shù)值含義
具體每一項填寫的內(nèi)容規(guī)則如下表所示:如果該簇是文件的最后一簇,填入的值為0x0FFFFFFF;如果該簇不是文件的最后一簇,則填入的值為該文件占用的下一簇號(所以我們可以看到在FAT32中文件是以簇鏈的形式保存起來的)。下面我們根據(jù)實際情況,圖3來分析一下FAT表的含義。
FAT表第0項(0x00000000~0x00000003): 0x0FFFFF8
FAT表第1項(0x00000004~0x00000007): 0xFFFFFFFF
這兩項不代表任何簇的使用情況,而是FAT表的表頭,表征了介質(zhì)描述,是固定值,所以0x00和0x01這兩個簇號是不用的,簇號的下標從2開始。其中1號表項可能被用于記錄臟標志,以說明文件系統(tǒng)沒有被正常卸載或者磁盤表面存在錯誤。接下來
FAT表第2項(0x00000008~0x0000000B): 0x0FFFFFFF
第2項存儲的是第2簇的使用情況,通常第2簇存儲的是文件系統(tǒng)的根目錄。雖然在FAT32文件系統(tǒng)中,根目錄的位置不再硬性地固定,可以存儲在分區(qū)內(nèi)可尋址的任意簇內(nèi),不過通常根目錄是最早建立的(格式化就生成了)目錄表。所以,我們看到的情況基本上都是根目錄首簇緊鄰FAT2,占簇區(qū)順序上的第1個簇(即2號簇)。同時,F(xiàn)AT32文件系統(tǒng)將根目錄當做普通的數(shù)據(jù)文件來看,所有沒有了目錄項數(shù)的限制,在需要的時候可以分配空簇,存儲更多的目錄項[1]。
這一項的值為0x0FFFFFFF ,說明根目錄占用且只占用了1個簇。
FAT表第3 ……
這里再穿插一點題外話,F(xiàn)AT32格式文件分配的最小單位是簇。也就是說你存儲了一個實際大小1kB的文件,那么它占用的存儲空間還是1簇(在這里換算成大小即為8*512B = 4KB)。筆者以一個實際的例子來說明一下:在U盤中放入一個8B大小的temp.txt文件,然后查看文件屬性的時候發(fā)現(xiàn)其占用空間是4KB,和我們上面講的理論符合。
圖 4 temp.txt的大小和占用空間
看了下篇幅也差不多了,那么本文關(guān)于FAT表的部分到此結(jié)束。其實本來也沒多少內(nèi)容,筆者想到哪就扯到哪,胡扯了些其他的東西。剩下的數(shù)據(jù)區(qū)部分就留到下一篇當中再講好了。同樣的,本文當中有一些內(nèi)容是筆者自己思考理解甚至推測出來的,如果有錯誤的地方歡迎指正,以免誤人子弟了(笑)。
FAT32文件系統(tǒng)學習(3) —— 數(shù)據(jù)區(qū)(DATA區(qū))
今天繼續(xù)學習FAT32文件系統(tǒng)的數(shù)據(jù)區(qū)部分(Data區(qū))。其實這一篇應該是最有意思的,我們可以通過在U盤內(nèi)放入一些文件,然后在程序中讀取出來;反過來也可以用程序在U盤內(nèi)寫入一下數(shù)據(jù),然后在windows下可以看到寫入的文件。這些筆者都會在這篇文章中演示(后來發(fā)現(xiàn)并沒有成功,不過筆者也找到相關(guān)的原因,詳見后來的更新部分吧:) )。同時,在寫這篇文章的時候筆者也發(fā)現(xiàn)了許多意想不到的規(guī)律。
1、本文目錄
1、讀取根目錄
2、短文件名目錄項
3、長文件名目錄項
4、U盤寫入文件夾
5、參考文獻
2、讀取根目錄
兩張FAT之后的所有扇區(qū)都是數(shù)據(jù)區(qū)部分。我們再通過
圖1來回顧一下整個FAT32文件系統(tǒng)的分布規(guī)則。
圖 1 FAT32文件系統(tǒng)分布圖
通常情況下根目錄都是位于數(shù)據(jù)區(qū)頭部的,前面也提到過,有文獻上說是因為一旦U盤格式化完畢之后,根目錄就創(chuàng)建好了。本著探究的精神,筆者嘗試格式化了一下U盤,發(fā)現(xiàn)其實并沒有創(chuàng)建根目錄。不過一旦有文件操作,馬上就會創(chuàng)建根目錄,因為這時整個數(shù)據(jù)區(qū)都是空的,所以自然是寫入數(shù)據(jù)區(qū)的頭部。到頭來其實道理是一樣的,也就是說根目錄一般情況下都是在數(shù)據(jù)區(qū)的頭部(第2簇)。
數(shù)據(jù)區(qū)偏移計算
經(jīng)過前兩篇關(guān)于BPB和FAT部分學習之后,我們就可以計算出數(shù)據(jù)區(qū)頭部的偏移:
數(shù)據(jù)區(qū)偏移 = (保留扇區(qū)數(shù) + FAT表扇區(qū)數(shù) * FAT表個數(shù)(通常為2) + (起始簇號-2) * 每簇扇區(qū)數(shù)) * 每扇區(qū)字節(jié)數(shù)
筆者首先格式化了U盤,通過偏移讀取出了數(shù)據(jù)區(qū)的頭部,發(fā)現(xiàn)都是0x00。
題外話
這里又要插一些題外話了,筆者試著改了一下U盤的卷標,把它改名為“FAT”。然后還記得BPB當中有一個參數(shù)叫做卷標嗎?筆者看了下發(fā)現(xiàn)卷標這個參數(shù)還是“NO NAME”并沒有改變。這時筆者把數(shù)據(jù)區(qū)的頭部讀取了出來,如圖2所示:
圖2 卷標
原來被寫在了這里。最后經(jīng)筆者的測試,卷標最長長度是11個字節(jié),偏移從0x00~0x0A,而偏移0x0B處的值0x08值的意思就是卷標(關(guān)于此處值的意思相面還會詳細描述)。因為這個U盤其實還沒有寫入過任何數(shù)據(jù),所以卷標才會顯示在數(shù)據(jù)區(qū)的開頭,但是如果換種情況呢,文件系統(tǒng)又是如何找到卷標的呢?筆者查閱了相關(guān)資料后發(fā)現(xiàn),卷標一般都是寫在根目錄的下的,如果發(fā)現(xiàn)根目錄項的其中一項其0x0B偏移處的值為0x08那么讀取該項的前11個字節(jié)即為卷標。
3、短文件名目錄項
短文件名目錄項參數(shù)
好,回到正題。先來講一下理論的東西:目錄區(qū)是由一個個目錄項構(gòu)成,類似于FAT表。其中每一個目錄項占用32個字節(jié),可以是代表長文件名目錄項、文件目錄項、子目錄項等。對于短文件名格式的目錄項,其參數(shù)的含義如表1所示(不會畫這種表,從別處引用了一個)[1]:
表1 FAT32短文件名目錄項參數(shù)表
參數(shù)解釋
用一個實際的例子來解釋一下這些參數(shù)的意思,首先創(chuàng)建一個短文件名文件,如“file1.txt”,讀取根目錄,如圖3所示:
圖3 file1.txt 短文件名目錄項
先不管其他數(shù)據(jù),我們來看一下紅色矩形框部分的數(shù)據(jù),它就是一個短目錄項。我們來一個個對比表1的參數(shù)進行說明:
字節(jié)偏移參數(shù)含義值
0x00~0x07文件名
對應字符串“FILE1”
0x08~0x0A后綴名
對應字符串“TXT”
0x0B屬性字節(jié)
0x20 = 00100000(2進制) 表示歸檔
0x0C系統(tǒng)保留無
0x0D創(chuàng)建時間的10毫秒位=88,即0x88 * 10ms = 1360ms(10進制)
0x0E~0x0F文件創(chuàng)建時間
0x785C = (0111100001011100)(2進制)
即為 15:02:57(注釋1)
0x10~0x11文件創(chuàng)建日期
0x4508 = (0100010100001000)(2進制)
即為 2014/8/8(注釋2)
0x12~0x13文件最后訪問日期
0x4508 = (0100010100001000)(2 進制)
即為 2014/8/8 算法參考創(chuàng)建日期
0x14~0x15文件起始簇號高16位0x0000,可以用來計算出文件實際內(nèi)容的偏移值,
這個放到后面單獨計算。
0x16~0x17文件最近修改時間
0x7869 = (0111100001101001)(2進制)
即為 15:03:18 算法參考創(chuàng)建時間
0x18~0x19文件最近修改日期
0x4508 = (0100010100001000)(2進制)
即為 2014/8/8 算法參考創(chuàng)建日期
0x1A~0x1B文件起始簇號低16位0x0005
0x1C~0x0F文件長度0x0000000C = 12
表2 file1.txt 參數(shù)解釋
注釋1:01111 000010 11100
1)這里高5位代表小時,由于2^5 = 32,足夠表示24小時,這邊01111(2進制) = 15(10進制);
2)次6位代表分鐘,同理2^6 = 64,足夠表示60分鐘,這邊000010(2進制) = 2;
3)低5位表示秒的1/2, 計算結(jié)果需要加上毫秒位上的進位,這邊11100(2進制) = 28(10進制),所以秒數(shù) = 28*2 = 56,再加上毫秒上的進位1所以結(jié)果為57。
注釋2:0100010 1000 01000
1)這里高7位代表從1980年開始的年數(shù),筆者計算了下可以到2108年,總之還有90多年可以使用,這邊0100010(2進制) = 34,所以年份 = 1980+34 = 2014;
2)次4位代表月份,2^4=16,可以表示12個月份,這邊 1000(2進制) = 8(10進制);
3)低5位代表日期,2^5 = 32,可以表示28~31天,這邊 01000(2進制) = 8(10進制)。
這樣除了文件起始簇號字段,其他字段的意思和計算方法都弄清楚了。下面來看一下文件起始簇號,首先根據(jù)高低各16位,計算出完整的文件起始簇號 = 0x00000005 ,文件起始地址偏移的計算:
文件起始地址 = (保留扇區(qū)數(shù) + FAT表扇區(qū)數(shù) * FAT表個數(shù)(2) + (文件起始簇號-2)*每簇扇區(qū)數(shù))*每扇區(qū)字節(jié)數(shù)
本例中計算結(jié)果為0x4010,然后到這個地址讀取內(nèi)容并切入到磁盤文件中(詳細操作參考第一篇文章),如圖4所示,windows下打開內(nèi)容如圖5所示:
圖4 圖5 文件內(nèi)容
這個時候再去看一下FAT表的5號簇,計算方式在上一篇當中,結(jié)果如圖6所示:
圖 6 FAT表5號表項
紅色矩形框的位置就是5號簇的位置,可以看到值0x0FFFFFFF,意思就是文件在這一簇結(jié)束了。 (具體不同數(shù)值的含義詳見上一篇)。如果這里文件大小超過1簇,那么這個值應該是下一簇的簇號,繼續(xù)讀取下一簇的內(nèi)容即可。雖然我們知道了文件占用的空間是1簇,但是怎么知道文件具體的大小呢?再回過頭來看上面的短文件目錄項,最后一個屬性是文件長度,上面已經(jīng)計算得到為12,“Hello World!”的長度正好是12:)。
至此短文件目錄項應該已經(jīng)分析的差不多了。
4、長文件名目錄項
長文件名目錄項參數(shù)
下面是長文件名目錄項,筆者思考了好久該怎么把它講清楚,畢竟理解是一回事,講清楚就是另一回事了。
在講長文件目錄項之前先來說一下FAT32的一個很重要的特性,支持長文件名。長文件名也是記錄在目錄項當中的,區(qū)別與短目錄項的是,前者可能會占據(jù)好幾個目錄項。為了兼容低版本的OS或程序能正確讀取長文件名文件,系統(tǒng)自動為所有長文件名文件創(chuàng)建了一個對應的短文件名,使對應數(shù)據(jù)既可以用長文件名尋址,也可以用短文件名尋址。不支持長文件名的OS或程序會忽略它認為不合法的長文件名字短,而支持長文件名的OS或程序則會以長文件名為顯式項來記錄和編輯,并隱藏起短文件名[2]。
當創(chuàng)建一個長文件名文件時,系統(tǒng)會自動加上對應的短文件名,其原則如下:
(1)、取長文件名的前6個字符加上"~1"形成短文件名,擴展名不變。
(2)、如果已存在這個文件名,則符號"~"后的數(shù)字遞增,直到5。
那么系統(tǒng)是如何判斷當前目錄項是短文件名目錄項呢還是長文件名目錄項,這里關(guān)鍵是看目錄項的第12個字節(jié)的值,如果為0x0F時則系統(tǒng)認為是長目錄項。而如果是舊版本的系統(tǒng)看到第12個字節(jié)是0x0F則認為是異常而忽略掉。這里可以
回過頭去看一下短文件名目錄項,第12個字節(jié)是文件屬性字節(jié),0x0F即為全1是無效的,所以系統(tǒng)認為是異常。系統(tǒng)將長文件名以13個字符為單位進行切割,每一組占據(jù)一個目錄項。所以可能一個文件需要多個目錄項,這時長文件名的各個目錄項按倒序排列在目錄表中,以防與其他文件名混淆。
這樣講可能還是很抽象,先來看一下長文件名目錄項的參數(shù),如表3所示[1]:
表3 長文件名目錄項參數(shù)表
參數(shù)解釋
然后還是以一個實際的例子來說明,在根目錄區(qū)讀入一個長文件名目錄項,如圖7所示:
圖7 長文件名目錄項
圖中選定部分即為多個長文件名目錄項。我們來慢慢分析。系統(tǒng)在讀入一個目錄項的時候首先查看它的第12個字節(jié),發(fā)現(xiàn)是0x0F,所以認為這是一個長文件名目錄項。我們來看長文件名目錄項的參數(shù),如
表4所示:
偏移字段含義值
0x00屬性字節(jié)位0x42 = (01000010)(2進制)(注釋1)
0x01~0x0A10個字節(jié)的Unicode碼即字符串”ename”>注釋2)
0x0B長文件名目錄項
0x0F前面已經(jīng)講過
0x0C系統(tǒng)保留無
0x0D校驗值這個等整個文件名讀取完再講
0x0E~0x1912字節(jié)Unicode即字符串“Test”
0x1A~0x1B文件起始簇號常置0
0x1C~0x1F4字節(jié)Unicode
0xFFFFFFFF
如果文件名已經(jīng)結(jié)束的話則全部為0xFF
表4 長文件名目錄項參數(shù)解釋
注釋1:01000010
第7位為1,說明是文件最后一個目錄項目,低5位為順序 0010(2進制) = 2(10進制),說明這是第2個長目錄項,且是最后一個目錄項。即為這個長文件名占用了兩個目錄項。
注釋2:Unicode 百度百科Unicode
點我詳細解釋這邊有3個Unicode區(qū),加起來正好是26個字節(jié)即13個Unicode碼,所以這就是為什么上面講的以13個字符為單位切割。因為這是第2個目錄項,所以后面應該還有第1個目錄項,繼續(xù)分析下一個目錄項其余參數(shù)同上,看一下3個Unicode分別是“LongL” “engthF” “il”而0x00的屬性字節(jié)是01,說明這是第一個。至此這個長文件名讀取完畢了。按照倒序(這里也解釋了前面說的倒序的意思)的順序拼接起來的話就是“LongLengthFilename”——這就是這個文件的文件名。
下面再來看一下下一個目錄項,長文件名目錄項后面還會跟一個短文件名目錄項,這個目錄項記錄了除文件名以外的這個文件的信息,而文件名部分則用上面提到的短文件名目錄項替換。所以讀取方法和短文件名目錄項是一樣的,這里只看一下文件屬性字節(jié),偏移為0x0B,值為0x10=(00010000) 根據(jù)短文件名目錄項參數(shù)的意思,這個文件是一個子目錄。其余參數(shù)讀者可以根據(jù)上面提到的計算方法得出。
最后再來補上剛才的校驗碼計算方法:
int i, j = 0, chksum=0;
for (i = 11; i > 0; i--)
chksum = ((chksum & 1) ? 0x80 : 0) + (chksum >> 1) + shortname[j++];
其中shortname即長文件名目錄項對應的短文件名,所以這個校驗碼需要等到讀完短文件名目錄項之后才可以計算。這一段程序是筆者從網(wǎng)上摘來的,還沒有時間驗證一下。
5、U盤寫入文件夾
這樣關(guān)于數(shù)據(jù)區(qū)的部分差不多就講完了。
最后在做一點有趣的事情,嘗試向磁盤的扇區(qū)中寫入一些數(shù)據(jù),然后看是否會生成這個文件。為了方便起見,這里直接在根目錄創(chuàng)建一個文件夾好了,
文件夾的名字叫做root,
創(chuàng)建時間日期2014/8/8 18:18:18
訪問日期 2014/8/8
最近修改時間日期 2014/8/8 18:18:18
起始簇低16位 04 00
起始簇高16位 00 00
文件長度 0
同時修改2個FAT表第4項為0x0FFFFFFF
這樣應該就可以了,好了,開始編碼:
// 短文件名目錄項數(shù)據(jù)結(jié)構(gòu)
typedef struct ShortDirItem
{
char strFilename[8];
char strExtension[3];
char attribute;
char reserved;
char millisecond;
unsigned short createTime;
unsigned short createDate;
unsigned short accessDate;
unsigned short highWordCluster;
unsigned short updateTime;
unsigned short updateDate;
unsigned short lowWordCluster;
unsigned int filesize;17 }ShortDirItem;
// 定位到FAT1表
SetFilePointer(hDisc, 1016*512, 0, FILE_BEGIN);
DWORD dwNumber2Read = 512;
// 實際讀取的字節(jié)數(shù)
DWORD dwRealNumber;
// 分配緩沖區(qū)
char* buffer = new char[512];
// 讀取一個扇區(qū)的數(shù)據(jù)
BOOL bRet = ReadFile(hDisc, buffer, dwNumber2Read, &dwRealNumber, NULL);
// 把4第項改為 0x0FFFFFFF
buffer[12] = buffer[13] = buffer[14] = 0xFF;
buffer[15] = 0x0F;
// 寫回FAT1
bRet = WriteFile(hDisc, buffer, dwNumber2Read, &dwRealNumber, NULL);
// 定位到FAT2表
SetFilePointer(hDisc, (1016+7684)*512, 0, FILE_BEGIN);
// 把4第項改為 0x0FFFFFFF
bRet = WriteFile(hDisc, buffer, dwNumber2Read, &dwRealNumber, NULL);
// 定位到根目錄
SetFilePointer(hDisc, (1016+7684*2)*512, 0, FILE_BEGIN);
// 讀取根目錄扇區(qū)
bRet = ReadFile(hDisc, buffer, dwNumber2Read, &dwRealNumber, NULL);
// 準備數(shù)據(jù)
ShortDirItem* item = new ShortDirItem();
strcpy(item->strFilename, "root");
strcpy(item->strExtension, " ");
item->attribute = 0x10;
item->millisecond = 0x00;
item->createTime = 0x9249;
item->createDate = 0x4508;
item->accessDate = 0x4508;
item->highWordCluster = 0x0000;
item->updateTime = 0x9249;
item->updateDate = 0x4508;
item->lowWordCluster = 0x0004;
item->filesize = 0x00;
// 修改根目錄數(shù)據(jù)
char* pData = (char*)item;
for (int i = 32; i < 64; ++i)
{
buffer[i] = *(pData++);
}
// 寫回根目錄
bRet = WriteFile(hDisc, buffer, dwNumber2Read, &dwRealNumber, NULL);
// 掃尾工作,釋放緩沖區(qū),關(guān)閉句柄
delete[] buffer;48 delete item;49 CloseHandle(hDisc);
但是筆者發(fā)現(xiàn)在win7(準確的說是win7、vista、win8,XP下獲取管理員權(quán)限即可執(zhí)行)下調(diào)用WriteFile函數(shù)無法將數(shù)據(jù)寫入U盤,可能是由于系統(tǒng)保護措施的關(guān)系,由于時間關(guān)系,筆者也沒去深究。
后來筆者專門去查找了相關(guān)資料,總的來說原因確實是因為系統(tǒng)保護措施的關(guān)系導致WriteFile函數(shù)操作的失敗,具體的解釋如下:
首先是msdn上的解釋
http://msdn.microsoft.com/en-us/library/windows/hardware/ff551353(v=vs.85).aspx。大概意思是說在win7和vista上加入了一些新的特性,為了能夠更好得保護系統(tǒng),如果應用程序沒有獨占的權(quán)限就直接對裝有文件系統(tǒng)的存儲設(shè)備進行寫入操作的話,這個操作是會被拒絕的。筆者上面的程序通過GetLastError()函數(shù)得到的ErroeCode=5,意思也確實是拒絕訪問。那么到底要如何寫入呢,msdn上給出了以下幾種情況:
Write operations on a DASD volume handle will succeed if the file system is not mounted, or if:
The sectors being written to are the boot sectors.
The sectors being written to reside outside file system space.
The file system has been locked implicitly by requesting exclusive write access.
The file system has been locked explicitly by sending down a lock/dismount request.
The write request has been flagged by a kernel-mode driver that indicates that this check should be bypassed. The flag is called SL_FORCE_DIRECT_WRITE and it is in the IrpSp->flags field. This flag is checked by both the file system and storage drivers.
這里比較方便的做法可以采用第4種,即顯示地發(fā)送一個鎖定驅(qū)動的請求,然后再嘗試寫入。具體做法參考這個帖子22L吧,筆者打算去嘗試一下,成功的話再來更新結(jié)果。
好了,看了下篇幅這篇文章也差不多可以結(jié)束了。FAT32文件系統(tǒng)其實差不多也都學習完了,為了鞏固學習內(nèi)容,筆者打算接下去根據(jù)前面所學的知識,并去了解一下windows快速格式化FAT32的機制,嘗試自己格式化U盤,還可以根據(jù)FAT32的原理嘗試刪除數(shù)據(jù)的恢復等,總之還是有很多事情可以做的。
最后的最后,如果文章當中有任何錯誤或者遺漏指出,歡迎指出,謝謝。
FAT32 FAT區(qū)__FAT表解析
一、 FAT 表概述
位置:緊跟在文件系統(tǒng)的“保留區(qū)”之后 ; 有兩個數(shù)據(jù)結(jié)構(gòu)完全相同的FAT(FAT,File Allocation Tbale 文件分配表)組成。
作用:FAT表項,描述文件系統(tǒng)內(nèi)的簇分配狀態(tài),說明文件系統(tǒng)內(nèi)數(shù)據(jù)所分配的連續(xù)簇的順序關(guān)系(即表明文件或目錄的下一簇的序號)。
常規(guī)規(guī)則:
· 數(shù)量:通常情況下一個FAT 文件系統(tǒng)會有兩個FAT 表, 但有時候也會允許只有一個FAT 表, FAT 表的具體個數(shù)記錄在引導扇區(qū)的 偏移 0x10 字節(jié)處。
· 位置:因為FAT區(qū)位于文件系統(tǒng)的保留區(qū)之后,所以FAT1在文件系統(tǒng)中的位置可以通過引導記錄中偏移0x0E~0x0F 字節(jié)處的“保留扇區(qū)”數(shù)得到。
· FAT2 緊跟在FAT1之后, 它的位置可以通過FAT1的位置加上每個FAT 表的大小扇區(qū)數(shù)獲得。
FAT 表中記錄了每個文件的簇鏈結(jié)構(gòu); FAT 表中記錄的與數(shù)據(jù)區(qū)簇對應的表項,從0號標記開始至當前數(shù)據(jù)區(qū)所分配的簇的最大數(shù)值,記錄簇信息到FAT 項;但是注意:其中 0號~1號簇的值都是操作系統(tǒng)預先不留設(shè)定的特殊標記,而數(shù)據(jù)區(qū)的起始簇是2號簇。
二、、FAT 表的特性
FAT 表由一些列大小相等的表項組成,有如下特性:
· FAT32 中每個簇的狀態(tài),使用32bit(4字節(jié))記錄在FAT表中。 FAT 表中的所有字節(jié)位置以 4個字節(jié)為單位進行劃分;并以所有劃分后的位置由0進行地址編號。“0號 和 1號” 地址被系統(tǒng)保留并存儲特殊標識內(nèi)容。從 2號 地址開始, 每個地址對應于數(shù)據(jù)區(qū)的簇號,F(xiàn)AT 表中的地址編號與數(shù)據(jù)區(qū)中的簇號相同。稱FAT 中的這些四字節(jié)一組劃分的項的地址為 FAT 表項,F(xiàn)AT表項中記錄的值為FAT 表項值。(簇編號與簇內(nèi)內(nèi)容關(guān)系如果 Map中的鍵-值 關(guān)系相同)
當文件系統(tǒng)創(chuàng)建時(就是格式化操作時), 分配給FAT 區(qū)域的空間將會被清空, 在FAT1與FAT2 的0號和1號表項寫入特定值。由于創(chuàng)建文件系統(tǒng)的同時,也會創(chuàng)建根目錄, 也就是為根目錄分配了一個簇空間,通常為2號簇,所以2號簇所對應的“2號FAT表項”也會被寫入一個結(jié)束標記。
· 如果某個簇未被使用,他所對應的FAT 表項內(nèi)的FAT 表項值即用0進行填充,表示該FAT 表項所對應的簇未被分配使用
· 當某個簇被分配使用時,那么他所對應的FAT表項的值為文件的下一個存儲文件的簇號。 如果該文件結(jié)束于該簇,則在它的FAT表項中記錄的是一個文件結(jié)束標記,對于FAT32 而言,代表文件結(jié)束的FAT表項值為0x0FFFFFFF。
· 如果某個簇存在壞扇區(qū),則整個簇會用FAT 表項值0x0FFFFFF7 標記為壞簇, 不再使用,這個壞簇標記就記錄在它所對應的FAT表項中。
· 由于簇號起始于2, 所以FAT表的0號表項與1號表項不予任何簇對應。 FAT32 的0號表項值總是“F8FFFF0F”
注意:可以搜索扇區(qū)偏移0字節(jié)處的該值(F8FFF0F)以查找FAT表。
· 1號表項可能被用于記錄“臟標志”, 以說明文件系統(tǒng)沒有被正常卸載或者磁盤表面存在錯誤。 不過此值似乎不重要,正常情況下,1號表項值“FFFFFFFF”或“FFFFFF0F”
項內(nèi)容填寫規(guī)則:
·在文件系統(tǒng)中新建文件時,如果新建的文件只有一個簇,為其分配的簇所對應的FAT表項將會被寫入結(jié)束標記。如果新建的文件不只占用一個簇,則在其所占用的每個簇對應的FAT表項中寫入為其分配的下一個簇的簇號,在最后一個簇對應的FAT 表項中寫入結(jié)束標記。
· 新建目錄時,只為其分配一個簇的空間,對應的FAT 表項中寫入結(jié)束標記。當目錄增大超過一個簇的大小時,將會在空閑空間中繼續(xù)為其分配一個簇,并在FAT 表中為其建立FAT 表鏈以描述它所占用的簇的情況。
· 對文件或目錄進行刪除操作時,他們所對應的FAT 表項將會被清空,設(shè)置為0以表示其所對應的簇處于未分配的狀態(tài)。
三、 FAT表的使用
一個文件的“起始簇號”記錄在它的目錄項中, 該文件的 "其他簇" 則用一個簇連結(jié)構(gòu)記錄在FAT 表中。
如果一個簇所對應的FAT表項的表項值為非零, 則表明該簇已經(jīng)被分配使用了,但是這時表項值可能為兩種情況,一個是一個文件的下一個簇號值,也有可能是一個文件的結(jié)束標記0x0FFFFFFF,或者是一個壞簇標記0x0FFFFFF7 。
如果要尋找一個文件的下一個簇,只需要查看該文件的目錄項中描述的起始簇號所對應的FAT項,如果該文件只有一個簇,則此處的值為一個結(jié)束標記0x0FFFFFFF;如果該文件不只一個簇,則此處 的值是它的下一個簇的簇號。
>> 查詢簇連接結(jié)構(gòu)模擬步驟:
當我們要尋找某個文件時, 首先從該文件的“目錄項”中獲取該文件的第一“簇”的簇號,然后根據(jù)“第一簇”的“簇號 N”,然后根據(jù)N 從FAT區(qū)的FAT表找出N簇號所對應的FAT 表項,查看FAT 表項的內(nèi)容:
若是文件結(jié)束,該表項值為 0x0FFFFFFF; 若是沒有結(jié)束,而該文件的大小超出一個簇,則N所對應的FAT表項的表項值為該文件下一個簇的簇號,然后再找到下一個簇號N+1 所對應的FAT表項,查看其表項值,依次類推,就推出了一個文件在FAT表中的 簇鏈連接結(jié)構(gòu); 也或者表項值為 0xFFFFFFF7 壞簇標識。
>> 其他
查找FAT 表項:要找到一個簇的FAT 表項,只要用他的 簇號乘以每個FAT 表項的字節(jié)數(shù)即可 。對于FAT32而言,每個FAT 表項占用4個四字節(jié), 如果我們尋找9號簇的表項位置,則用 4*9=36,也就是說位于FAT 表內(nèi)偏移36(0x24)字節(jié)處。
注意:
WinHex 提供了直接跳轉(zhuǎn)到某個指定FAT 表項的功能。
文件系統(tǒng)大小的上限值卻絕育FAT 項的大小。 簇鏈中的每個FAT 項記錄著下一個簇的簇地址,F(xiàn)AT 項所能表示的數(shù)字有一個上限,這個上限也就是文件系統(tǒng)中的最大簇號。 FAT 32文件系統(tǒng)的FAT 項只使用了32bit 中的28bit ,因此只能描述 268435456個簇(實際上還要考略小于這個值,因為這其中還包含了結(jié)束標志及壞簇標志的保留值)。
操作系統(tǒng)通過檢測FAT 表中的表項來確定文件系統(tǒng)中的各個簇是否被分配使用。當我們在Windows 下右擊某個FAT 分區(qū)查看其屬性時,顯示的已用空間和未用空間就是根據(jù)FAT 表統(tǒng)計而來的。
有時我們會遇到,查看屬性時發(fā)現(xiàn)已用空間并沒有減少,但存儲的文件卻不見了。這是因為某些病毒在某些文件的目錄項中寫入了刪除標記,但并沒有清楚FAT 表內(nèi)的簇鏈所至。
FAT32文件系統(tǒng)的存儲組織結(jié)構(gòu)
對磁盤的物理結(jié)構(gòu),邏輯結(jié)構(gòu)和存儲結(jié)構(gòu)有了比較深入的了解后,我們來仔細探討FAT32文件系統(tǒng)的存儲組織結(jié)構(gòu)。說到文件系統(tǒng)的組織結(jié)構(gòu),我們應該馬上意識到,這指的是文件系統(tǒng)在同一個分區(qū)內(nèi)的組織結(jié)構(gòu),在這個話題上,我們完全可以不管分區(qū)之外的所有事情。
為了分析FAT32文件系統(tǒng)的存儲組織結(jié)構(gòu),我們來建立一個實實在在的文件系統(tǒng):將U盤插入電腦,將U盤格式化成FAT32分區(qū)格式:
以建好的U盤FAT32文件系統(tǒng)為基礎(chǔ),下面從文件系統(tǒng)的各個組成來分別加以介紹。
分區(qū)引導扇區(qū)DBR
用winhex打開U盤顯示如下:
這是FAT32分區(qū)引導記錄,定義如下:
偏移00H: 3字節(jié)的 跳轉(zhuǎn)指令 EB 58 90,跳過下面的BPB和擴展BPB部分
偏移03H:8字節(jié)的硬盤分區(qū)類型文本字符名:4D 53 44 4F 53 35 2E 30 即:MSDOS5.0
偏移0BH: 25字節(jié)的分區(qū)參數(shù)塊(BPB),細分如下:
偏移0BH:扇區(qū)字節(jié)數(shù):00 02 即0X0200,512字節(jié)
偏移0DH:每簇扇區(qū)數(shù):08即每簇包括8個扇區(qū)
偏移0EH:保留扇區(qū)數(shù):24 00即保留36個扇區(qū)
偏移10H:FAT表份數(shù):02即兩個FAT表
偏移11H:未用:00 00
偏移13H:未用:00 00
偏移15H:介質(zhì)類型:F8即本地硬盤
偏移16H:未用:00 00
偏移18H:每磁道扇區(qū)數(shù):3F 00 即每磁道63扇區(qū)
偏移1AH:磁頭數(shù):FF 00即255個磁頭
偏移1CH:隱藏扇區(qū)數(shù):80 1F即8064個隱藏扇區(qū)
偏移20H:磁盤總扇區(qū)數(shù) 80 F0 77 00即總共7860352個扇區(qū)(7860352*512=4024500224,因為我的U盤是4G)
偏移24H:52字節(jié)的擴展分區(qū)參數(shù)塊(擴展BPB),細分如下:
偏移24H:FAT表占用扇區(qū)數(shù):EE 1D 00 00即FAT表占7662個扇區(qū)
偏移28H:未用:00 00 00 00
偏移2CH:根目錄入口簇號:02 00 00 00即根目錄從02號簇開始
偏移30H:文件系統(tǒng)信息扇區(qū)號:01 00即扇區(qū)1
偏移32H:備份引導扇區(qū)的位置 06 00即6號扇區(qū)(第7個扇區(qū)),從WINHEX中我們也可以看到,6號扇區(qū)的內(nèi)容和0號引導扇區(qū)內(nèi)容是一樣的
偏移34H:未用:00 00 00 00 00 00 00 00 00 00 00 00
偏移40H:物理磁盤號:00
偏移41H:未用:00
偏移42H:擴展引導標志 29即0X29
偏移43H:磁盤序列號F1 2A 27 04通常為一隨機數(shù)
偏移47H:卷標ASCII 4E 4F 20 4E 41 4D 45 20 20 20 20 即NO NAME
偏移52H:文件系統(tǒng)格式ASCII 46 41 54 33 32 20 20 20即FAT32
偏移5AH:分區(qū)引導代碼 420字節(jié):
:33C98ED1BCF47B8EC18ED9BD007C884E028A5640B408CD137305B9FFFF8AF166
0FB6C640660FB6D180E23FF7E286CDC0ED0641660FB7C966F7E1668946F8837E1
6007538837E2A007732668B461C6683C00CBB0080B90100E82B00E94803A0FA7DB
47D8BF0AC84C074173CFF7409B40EBB0700CD10EBEEA0FB7DEBE5A0F97DEBE0
98CD16CD196660663B46F80F824A00666A0066500653666810000100807E02000F8
52000B441BBAA558A5640CD130F821C0081FB55AA0F851400F6C1010F840D00FE4
602B4428A56408BF4CD13B0F96658665866586658EB2A6633D2660FB74E1866F7F1
FEC28ACA668BD066C1EA10F7761A86D68A56408AE8C0E4060ACCB80102CD13666
10F8254FF81C300026640490F8571FFC34E544C445220202020202000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
00000000000000D0A52656D6F7665206469736B73206F72206F74686572206D656469
612EFF0D0A4469736B206572726F72FF0D0A507265737320616E79206B657920746F2
0726573746172740D0A0000000000ACCBD80000
偏移1FEH:有效扇區(qū)結(jié)束標志 55 AA
到此分區(qū)引導扇區(qū)介紹結(jié)束。
文件分配表FAT
簡介:
FAT表(文件分配表),是FAT文件系統(tǒng)中用于磁盤數(shù)據(jù)索引和定位而引進的一種鏈式結(jié)構(gòu)。在FAT文件系統(tǒng)中,文件的存儲依照FAT表制定的簇鏈式數(shù)據(jù)結(jié)構(gòu)來進行。同時,F(xiàn)AT文件系統(tǒng)將組織數(shù)據(jù)時使用的目錄也抽象為文件,以簡化對數(shù)據(jù)的管理。
FAT1表位置的定位:
在我們前面介紹分區(qū)引導記錄的時候提到,在偏移0EH處存儲了保留扇區(qū)的個數(shù),這個保留扇區(qū)數(shù)指的就是當前分區(qū)內(nèi)DBR到FAT表之間的所有扇區(qū)的個數(shù)(包括DBR但不包括FAT表)。因此,我們可以定位FAT表所在的起始偏移位置了,即24H*200H=4800H。我們貼出4800H處得部分內(nèi)容如下:
顯然沒有錯,這就是我們FAT1所存儲的位置,只是當前沒有存儲文件,所以FAT比較簡單罷了。
FAT2表位置的定位:
在我們前面介紹分區(qū)引導記錄的時候提到,在偏移24H處存儲了FAT表所占用的扇區(qū)個數(shù),我們又知道FAT2是緊鄰FAT1的,所以可以很容易得到FAT2的存儲位置的偏移地址:FAT1的起始偏移地址+FAT1的大小=4800H+1DEEH*200H=3C2400H,我們貼出3C2400H處的部分內(nèi)容如下:
顯然沒有錯,這就是我們FAT2所存儲的位置,內(nèi)容與FAT1相同。
FAT表的特性:
FAT表由一系列大小相等的FAT表項組成,它有如下特性:
FAT32中每個簇的簇地址,使用32bit(4個字節(jié))記錄在FAT表中。FAT表中的所有字節(jié)位置以4個字節(jié)為單位進行劃分,并對所有劃分后的位置由0進行地址編號。0 號地址與1號地址被系統(tǒng)保留并存儲特殊標志內(nèi)容。從2號地址開始,每個地址對應于數(shù)據(jù)區(qū)的簇號,F(xiàn)AT表中的地址編號與數(shù)據(jù)區(qū)中的簇號相同。我們稱FAT中的這些地址為FAT表項,F(xiàn)AT表項中記錄的值稱為FAT表項值。
當文件系統(tǒng)被創(chuàng)建,也就是進行格式化操作時,分配給FAT區(qū)域的空間將會被清空,在FAT1與FAT2的0號表項與1號表項寫入特定值。由于創(chuàng)建文件系統(tǒng)的同時也會創(chuàng)建根目錄,也就是為根目錄分配了一個簇空間,通常為2號簇,所以2號簇所對應的2號FAT表項也會被寫入一個結(jié)束標記。
如果某個簇未被分配使用,它所對應的FAT表項內(nèi)的FAT表項值即用0進行填充,表示該FAT表項所對應的簇未分配使用。
當某個簇已被分配使用時,則它對應的FAT表項值也就是該文件的下一個存儲位置的簇號。如果該文件結(jié)束于該簇,則在它的FAT表項中記錄的是一個文件結(jié)束標記,對于FAT32而言,代表文件結(jié)束的FAT表項值為0x0FFFFFFF。
如果某個簇存在壞扇區(qū),則整個簇會用FAT表項值0x0FFFFFF7標記為壞簇,不再使用,這個壞簇標記就記錄在它所對應的FAT表項中。
由于簇號起始于2,所以FAT表的0號表項與1號表項不與任何簇對應。FAT32的0號表項值總是“F8FFFF0F”。1號表項可能被用于記錄臟標志,以說明文件系統(tǒng)沒有被正常卸載或者磁盤表面存在錯誤。不過此值似乎并不重要,因此我們只要了解就可以。正常情況下,1號表項值為“FFFFFFFF”或“FFFFFF0F"。
在文件系統(tǒng)中新建文件時,如果新建的文件只占用一個簇,為其分配的簇所對應的FAT表項將會被寫入結(jié)束標記。如果新建的文件不只占用一個簇,則在其所占用的每個簇對應的FAT表項中寫入為其分配的下一簇的簇號,在最后一個簇對應的FAT表項中寫入結(jié)束標記。
新建目錄時,只為其分配一個簇的空間,對應的FAT表項中寫入結(jié)束標記。當目錄增大超出一個簇的大小時,將會在空閑空間中繼續(xù)為其分配一個簇,并在FAT表中為其建立FAT表鏈以描述它所占用的簇情況。
對文件或目錄進行刪除操作時,它們所對應的FAT表項將會被清空,設(shè)置為0以表示其所對應的簇處于未分配狀態(tài)。
根目錄區(qū)
簡介:
在FAT32文件系統(tǒng)中,根目錄的位置不再硬性地固定,可以存儲在分區(qū)內(nèi)可尋址的任意簇內(nèi),不過通常根目錄是最早建立的(格式化就生成了)目錄表。所以,我們看到的情況基本上都是根目錄首簇緊鄰FAT2,占簇區(qū)順序上的第1個簇(即2號簇)。同時,F(xiàn)AT32文件系統(tǒng)將根目錄當做普通的數(shù)據(jù)文件來看,所有沒有了目錄項數(shù)的限制,在需要的時候可以分配空簇,存儲更多的目錄項。
起始偏移地址定位:
根目錄起始扇區(qū)=保留扇區(qū)數(shù)+FAT×2+(起始簇-2)x每簇的扇區(qū)數(shù),在我們前面介紹分區(qū)引導記錄的時候提到,偏移2CH處保存了根目錄起始簇號是2,所以求得根目錄起始扇區(qū)是24H+1DEEH*2H+(2-2)*8H=3C00H,即求得偏移地址3C00H*200H=780000H,我們貼出780000H處的部分內(nèi)容如下:
目錄區(qū)的一個目錄項占用32個字節(jié),可以是長文件名目錄項、文件目錄項、子目錄項等。
短文件名格式的目錄項
對于短文件名格式的目錄項。其參數(shù)意義如下:
根據(jù)參數(shù)定義,我們來分析一下上圖的目錄項 54 45 53 54 5F 46 41 54 33 32 20 08 00 00 00 00 00 00 00 00 00 00 19 95 10 3F 00 00 00 00 00 00。其中起始11字節(jié)54 45 53 54 5F 46 41 54 33 32 20 是卷標TEST_FAT32;第12字節(jié)08指示當前目錄項保存的是卷標;第23-24字節(jié)19 95即9519H,是最近修改時間:19點40分50秒;第25-26字節(jié)10 3F即3F10H,是最近修改日期:2011年8月16日;
長文件名格式的目錄項
FAT32的一個重要的特點是完全支持長文件名。長文件名依然是記錄在目錄項中的。為了低版本的OS或程序能正確讀取長文件名文件,系統(tǒng)自動為所有長文件名文件創(chuàng)建了一個對應的短文件名,使對應數(shù)據(jù)既可以用長文件名尋址,也可以用短文件名尋址。不支持長文件名的OS或程序會忽略它認為不合法的長文件名字段,而支持長文件名的OS或程序則會以長文件名為顯式項來記錄和編輯,并隱藏起短文件名。
當創(chuàng)建一個長文件名文件時,系統(tǒng)會自動加上對應的短文件名,其原則如下:
(1)、取長文件名的前6個字符加上"~1"形成短文件名,擴展名不變。
(2)、如果已存在這個文件名,則符號"~"后的數(shù)字遞增,直到5。
長文件名的實現(xiàn)有賴于目錄項第12字節(jié)屬性字節(jié),當此字節(jié)的值為0FH時,支持長文件名的系統(tǒng)會將其當做長文件名的依據(jù),而只支持短文件名的系統(tǒng)會認為是異常而忽略掉。系統(tǒng)將長文件名以13個字符為單位進行切割,每一組占據(jù)一個目錄項。所以可能一個文件需要多個目錄項,這時長文件名的各個目錄項按倒序排列在目錄表中,以防與其他文件名混淆。
長文件名中的字符采用unicode形式編碼,每個字符占據(jù)2字節(jié)的空間。其目錄項定義如:
下面是我建立的長文件名文件夾abcdefghijklmnopqrstuvwxyz1234567890的目錄項:
前面已經(jīng)基于一個格式化的空U盤分析了一下FAT32文件系統(tǒng)存儲的組織結(jié)構(gòu),下面我們從文件操作的角度來分析一下文件系統(tǒng)的運作機制。由于換了個U盤,所以仍然貼出剛格式化的空U盤的幾個重要的數(shù)據(jù)區(qū)如下:
我們可以看出,在分區(qū)格式化的時候,系統(tǒng)將卷標TEST_FAT32存儲在2號簇,即跟目錄區(qū),如上面根目錄貼圖所示。同時,在FDT區(qū)2號簇標記位置寫入了文件結(jié)束符FF FF FF 0F。顯然,F(xiàn)AT32文件系統(tǒng)將目錄當做普通文件來處理的。
下面我們在根目錄下新建一個文件夾TEST1,看會有什么變化:
建立了TEST1文件夾后,F(xiàn)DT變成如下:
根目錄變成如下:
重新分配了3號簇:
從上面的變化可以直觀的看出,系統(tǒng)在新建文件夾時完成了如下動作:
a.在父目錄所在簇上建立新的目錄項,存儲當前所建文件夾信息。
b.分配一個新簇,給新建的文件夾建立兩個目錄項:父目錄和當前目錄。
c.在FDT表中新分配的簇對應的位置上寫下文件結(jié)束符。
d.建立各部分的鏈路關(guān)系:新建文件夾所對應的目錄項的文件起始簇號字段寫上新分配簇的簇號,新簇上的兩個目錄項的文件起始簇號字段分配寫上父目錄所在簇號(此處是0,本來我以為是2,即根目錄所在簇,不知道為什么,可能特地用0指示根目錄吧)和當前簇號(此處是3)。
為了驗證我們上面分析的正確性,我們再在TEST1文件夾下建立新文件夾TEST11,看是否做了如下操作:
a.在父目錄(即TEST1)所在簇(即3號簇)上建立新的目錄項,存儲TEST11文件夾信息。
b.分配一個新簇(應該是4號簇),給新建的文件夾(即TEST11)建立兩個目錄項:父目錄和當前目錄。
c.在FDT表中新分配的簇(應該是4號簇)對應的位置上寫下文件結(jié)束符。
d.建立各部分的鏈路關(guān)系:新建文件夾(即TEST11)所對應的目錄項的文件起始簇號字段寫上新分配簇的簇號(應該是4號簇),,新簇上的兩個目錄項的文件起始簇號字段分配寫上父目錄所在簇號(3號簇)和當前簇號(應該是4號簇)。
新建TEST11文件夾后FDT變成:
根目錄沒有變化:
3號簇變成:
新分配4號簇:
顯然我們的估計沒有錯的,也進一步證明我們前面的分析是正確的。
下面我們再分析建立文件的情況
我們先建立一個100字節(jié)的文件TEST.TXT,然后把這個文件拷貝到U盤的根目錄下,F(xiàn)DT變成如下:
根目錄變成:
新分配5號簇保存文件內(nèi)容:
從上面的變化可以直觀的看出,系統(tǒng)新建文件和新建文件夾所完成的操作是一樣一樣的:
a.在父目錄所在簇上建立新的目錄項,存儲當前所建文件信息。
b.分配一個新簇,存儲新建的文件的內(nèi)容。
c.在FDT表中新分配的簇對應的位置上寫下文件結(jié)束符。
d.建立鏈路關(guān)系:新建文件所對應的目錄項的文件起始簇號字段寫上新分配簇的簇號。
結(jié)束總結(jié):
1.在FAT32文件系統(tǒng)中,目錄和文件的存儲采用統(tǒng)一的方式。
2.文件系統(tǒng)的操作的單位是簇,每新建立一個文件或文件夾,至少會重新分配一個簇號。
3.如果一個文件或目錄的內(nèi)容要多個簇才能存儲得下,則系統(tǒng)會分配多個簇來存儲文件或目錄的內(nèi)容
4.當需要多個簇時,這些簇可能連續(xù)也可能不連續(xù),但無論是連續(xù)或是不連續(xù),系統(tǒng)都是采用FDT鏈表的形式來組織的。
FAT32系統(tǒng)中長文件名的存儲
FAT32的一個重要的特點是完全支持長文件名。長文件名依然是記錄在目錄項中的。
為了低版本的OS或程序能正確讀取長文件名文件,系統(tǒng)自動為所有長文件名文件創(chuàng)建了一個對應的短文件名,使對應數(shù)據(jù)既可以用長文件名尋址,也可以用短文件名尋址。不支持長文件名的OS或程序會忽略它認為不合法的長文件名字段,而支持長文件名的OS或程序則會以長文件名為顯式項來記錄和編輯,并隱藏起短文件名。
當創(chuàng)建一個長文件名文件時,系統(tǒng)會自動加上對應的短文件名,其一般有的原則:
(1)、取長文件名的前6個字符加上"~1"形成短文件名,擴展名不變。
(2)、如果已存在這個文件名,則符號"~"后的數(shù)字遞增,直到5。
(3)、如果文件名中"~"后面的數(shù)字達到5,則短文件名只使用長文件名的前兩個字母。通過數(shù)學操縱長文件名的剩余字母生成短文件名的后四個字母,然后加后綴"~1"直到最后(如果有必要,或是其他數(shù)字以避免重復的文件名)。
(4)、如果存在老OS或程序無法讀取的字符,換以"_"
短文件格式的目錄項。其參數(shù)意義見表14:
表14 FAT32短文件目錄項32個字節(jié)的表示定義
字節(jié)偏移(16進制)字節(jié)數(shù)定義
0x0~0x78文件名
0x8~0xA3擴展名
0xB*1屬性字節(jié)00000000(讀寫)
00000001(只讀)
00000010(隱藏)
00000100(系統(tǒng))
00001000(卷標)
00010000(子目錄)
00100000(歸檔)
0xC1系統(tǒng)保留
0xD1創(chuàng)建時間的10毫秒位
0xE~0xF2文件創(chuàng)建時間
0x10~0x112文件創(chuàng)建日期
0x12~0x132文件最后訪問日期
0x14~0x152文件起始簇號的高16位
0x16~0x172文件的最近修改時間
0x18~0x192文件的最近修改日期
0x1A~0x1B2文件起始簇號的低16位
0x1C~0x1F4表示文件的長度
* 此字段在短文件目錄項中不可取值0FH,如果設(shè)值為0FH,目錄段為長文件名目錄段
長文件名的實現(xiàn)有賴于目錄項偏移為0xB的屬性字節(jié),當此字節(jié)的屬性為:只讀、隱藏、系統(tǒng)、卷標,即其值為0FH時,DOS和WIN32會認為其不合法而忽略其存在。這正是長文件名存在的依據(jù)。
將目錄項的0xB置為0F,其他就任由系統(tǒng)定義了,Windows9x或Windows 2000、XP通常支持不超過255個字符的長文件名。
系統(tǒng)將長文件名以13個字符為單位進行切割,每一組占據(jù)一個目錄項。所以可能一個文件需要多個目錄項,這時長文件名的各個目錄項按倒序排列在目錄表中,以防與其他文件名混淆。
長文件名中的字符采用unicode形式編碼(一個巨大的進步哦),每個字符占據(jù)2字節(jié)的空間。其目錄項定義如表15。
表15 FAT32長文件目錄項32個字節(jié)的表示定義
字節(jié)偏移(16進制)字節(jié)數(shù)定義
0x01屬性字節(jié)位意義7保留未用
61表示長文件最后一個目錄項
5保留未用
4順序號數(shù)值
3
2
1
0
0x1~0xA10長文件名unicode碼①
0xB1長文件名目錄項標志,取值0FH
0xC1系統(tǒng)保留
0xD1校驗值(根據(jù)短文件名計算得出)
0xE~0x1912長文件名unicode碼②
0x1A~0x1B2文件起始簇號(目前常置0)
0x1C~0x1F4長文件名unicode碼③
系統(tǒng)在存儲長文件名時,總是先按倒序填充長文件名目錄項,然后緊跟其對應的短文件名。從表15可以看出,長文件名中并不存儲對應文件的文件開始簇、文件大小、各種時間和日期屬性。文件的這些屬性還是存放在短文件名目錄項中,一個長文件名總是和其相應的短文件名一一對應,短文件名沒有了長文件名還可以讀,但長文件名如果沒有對應的短文件名,不管什么系統(tǒng)都將忽略其存在。所以短文件名是至關(guān)重要的。
在不支持長文件名的環(huán)境中對短文件名中的文件名和擴展名字段作更改(包括刪除,因為刪除是對首字符改寫E5H),都會使長文件名形同虛設(shè)。
(長文件名如何與短文件名對應?僅靠她們之間的位置關(guān)系?)
長文件名和短文件名之間的聯(lián)系光靠他們之間的位置關(guān)系維系顯然遠遠不夠。其實,長文件名的0xD字節(jié)的校驗和起很重要的作用,此校驗和是用短文件名的11個字符通過一種運算方式來得到的。系統(tǒng)根據(jù)相應的算法來確定相應的長文件名和短文件名是否匹配。這個算法不太容易用公式說明,我們用一段c程序來加以說明。
假設(shè)文件名11個字符組成字符串shortname[],校驗和用chknum表示。得到過程如下:
int i,j,chknum=0;
for (i=11; i>0; i--)
chksum = ((chksum & 1) ? 0x80 : 0) + (chksum >> 1) + shortname[j++];
如果通過短文件名計算出來的校驗和與長文件名中的0xD偏移處數(shù)據(jù)不相等。系統(tǒng)無論如何都不會將它們配對的。
依據(jù)長文件名和短文件名對目錄項的定義,加上對簇的編號和鏈接,F(xiàn)AT32上數(shù)據(jù)的讀取便游刃有余了。
本文出自
數(shù)據(jù)恢復網(wǎng)(www.sjhf.net),疏漏在所難免,希望指正。若需轉(zhuǎn)載請保留此信息;若需修改,請用以下方式與作者取得聯(lián)系
1、
http://www.sjhf.net2、
zymail@vip.sina.com3、
sjhf@sjhf.netFAT文件系統(tǒng)總結(jié)
MBR:Master Boot Record (主引導記錄)
DBR:DOS Boot Record (DOS引導記錄,位于分區(qū)引導扇區(qū))
BPB:BIOS Parameter Block (BIOS參數(shù)塊)
FAT:File Allocation Table (文件分配表)
Sector:扇區(qū)
Cluster:簇
一、硬盤組織結(jié)構(gòu)
下面是一個包含 4 個分區(qū)的硬盤結(jié)構(gòu)示意圖,其中分為 3 個基本分區(qū)和一個擴展分區(qū)。
二、FAT文件系統(tǒng)結(jié)構(gòu)
FAT 文件系統(tǒng)是由按照如下順序排列的幾個部分組成的:
0 – Reserved Region
1 – FAT Region
2 – Root Directory Region (FAT32?沒有這部分)
3 – File and Directory Data Region
FAT 系統(tǒng)的數(shù)據(jù)存儲采用小端(Little Endian)方式,注意到這一點很重要,在使用大 端(Big Endian)的系統(tǒng)中,讀取多字節(jié)數(shù)據(jù)的時候必須要經(jīng)過轉(zhuǎn)換,否則,讀取到的數(shù)據(jù)是不正確的。
例如:一個 32-bit 數(shù)據(jù) 0x12345678 在 FAT 中的保存方式如下圖所示:
三、主引導扇區(qū)
硬盤主引導扇區(qū) = 硬盤主引導記錄(MBR)+硬盤分區(qū)表(DPT)
MBR:扇區(qū)內(nèi)偏移地址 0 ~ 0x1BD
DPT:扇區(qū)內(nèi)偏移地址 0x1BE ~ 0x1FD,其中又分為 4 個分區(qū)表:
第一個分區(qū)表:0x1BE ~ 0x1CD
第二個分區(qū)表:0x1CE ~ 0x1DD
第三個分區(qū)表:0x1DE ~ 0x1ED
第四個分區(qū)表:0x1EE ~ 0x1FD
每個分區(qū)表的信息如下表所示:
分區(qū)表信息
字節(jié)位移
字段長度
字段名和定義
0x00
BYTE
引導指示符(Boot Indicator),指明該分區(qū)是否是活動分區(qū),0x80=活動分區(qū),0x00=非活動分區(qū)
0x01
BYTE
開始磁頭(Starting Head)
0x02
6Bits
開始扇區(qū)(Starting Sector),只用了0~5位。后面的兩位(第6位和第7位)被開始柱面字段所使用開始柱面(Starting Cylinder),除了開始扇區(qū)
0x03
10Bits
字段的最后兩位外,還使用了1位來組成該柱面值。開始柱面是一個10位數(shù),最大值為1023
0x04
BYTE
系統(tǒng)ID(System ID),定義了分區(qū)的類型,詳見下表
0x05
BYTE
結(jié)束磁頭(Ending Head)
0x06
6Bits
結(jié)束扇區(qū)(Ending Sector),只使用了0~5位。最后兩位(第6、7位)被結(jié)束柱面字段所使用
0x07
10Bits
結(jié)束柱面(Ending Cylinder),除了結(jié)束扇區(qū)字段最后的兩位外,還使用了1位,以組成該柱面值。結(jié)束柱面是一個10位的數(shù),最大值為1023
0x08
DWORD
相對扇區(qū)數(shù)(Relative Sectors),從該磁盤的開始到該分區(qū)的開始的位移量,以扇區(qū)來計算
0x0C
DWORD
總扇區(qū)數(shù)(Total Sectors),該分區(qū)中的扇區(qū)總數(shù)
分區(qū)標志類型值及其含義
類型值(HEX)
含義
類型值(HEX)
含義
0
空。DOS或windows不允許使用,視為非法
5C
Priam Edisk
1
FAT12
61
Speed Stor
2
XENIX root
63
GNU HURD or Sys
3
XENIX usr
64
Novell Netware
6
FAT16分區(qū)小于32M時為0x04
65
Novell Netware
7
HPFS / NTFS
70
Disk Secure Mult
8
AIX
75
PC/IX
9
AIX bootable
80
Old Minix
0A
OS/2 Boot Manage
81
Minix/Old Linux
0B
Win95 FAT32
82
Linux swap
0C
Win95 FAT32
83
Linux
0E
Win95 FAT16
84
0s/2 hidden C:
0F
Win95 Extended(大于 8GB)
85
Linux extended
10
OPUS
86
NTFS volume set
11
Hidden FAT12
87
NTFS volume set
12
Compaq diagmost
93
Amoeba
14
Hidden FAT16<32MB
94
Amoeba BBT
16
HiddenFAT16
A0
IBM Thinkpad hidden
17
Hidden HPFS/NTFS
A5
BSD/386
18
AST Windows swap
A6
Open BSD
1B
Hidden FAT32
A7
NextSTEP
1C
Hidden FAT32 partition
B7
BSDI fs
(using LBA-mode INT 13 extensions)B8BSDI swap
1E
Hidden LBA VFAT partition
BE
Solaris boot partition
24
NEC DOS
C0
DR-DOS/Novell DOS secured partition
3C
Partition Magic
C1
DRDOS/sec
40
Venix 80286
C4
DRDOS/sec
41
PPC Perp Boot
C6
DRDOS/sec
42
NTFS動態(tài)分區(qū)
C7
Syrinx
4D
QNX4.x
DB
CP/M/CTOS
4E
QNX4.x 2nd part
E1
DOS access
4F
QNX4.x 3rd part
E3
DOS r/0
50
OnTrack DM
E4
Speedstor
51
OnTrack DM6 Aux
EB
BeoS fs
52
CP/M
F1
SpeedStor
53
OnTrack DM6 Aux
F2
DOS 3.3+secondary partition
54
OnTrack DM6
F4
SpeedStor
55
EZ-Drive
FE
LAN step
56
Golden Bow
FF
BBT
主引導扇區(qū)
上面是從一張 SD 卡讀到的主引導扇區(qū)信息??梢钥闯?,MBR 區(qū)域數(shù)據(jù)全部為 0,這張 SD 卡只有一個分區(qū),這個分區(qū)前的扇區(qū)數(shù)為 0x0000003F,所以這個分區(qū)的開始位置就是扇區(qū)0x0000003F,總扇區(qū)數(shù)為 0x000F1EC1(990913 個扇區(qū))。
四、分區(qū)引導扇區(qū)
也常常稱為啟動扇區(qū),Microsoft稱它為 0 扇區(qū)(0th sector),通過前面的介紹我們知 道,稱它為 0 扇區(qū)其實是不正確的,這樣容易讓人誤解它為磁盤的最前面一個扇區(qū),稱它為 0 扇區(qū)只是表明它是FAT中扇區(qū)的參考點而已。
該扇區(qū)中包含有我們關(guān)注的一個重要數(shù)據(jù)結(jié)構(gòu) BPB(BIOS Parameter Block)。以下表 格內(nèi)容翻譯自 Microsoft 的《Microsoft Extensible Firmware Initiative FAT32 File System Specification—version1.03》,其中包含 BPB 各項的描述。
NOTE:在以下的敘述中,名字以 BPB_開頭的屬于 BPB 部分,以 BS 開頭的屬于啟動扇區(qū)(Boot Sector)部分,實際上并不屬于BPB。
offset
長度
描述
BS_jmpBoot
0x00
3
跳轉(zhuǎn)指令,指向啟動代碼
BS_OEMName
0x03
8
建議值為“MSWIN4.1”。有些廠商的 FAT 驅(qū)動可能會檢測此項,所以設(shè)為“MSWIN4.1”可以盡量避免兼容性的問題
BPB_BytsPerSec
0x0b
2
每扇區(qū)的字節(jié)數(shù),取值只能是以下幾種:512,1024,2048 或是 4096。設(shè)為 512 會取得最好的兼容性,目前有很多 FAT 代碼都是硬性規(guī)定每扇區(qū)的字節(jié)數(shù)為 512,而不是實際的檢測此值。但微軟的操作系統(tǒng)能夠很好支持
BPB_SecPerClus
0x0d
1
1024,2048 或是 4096每簇的扇區(qū)數(shù),其值必須中 2 的整數(shù)次方(該整數(shù)必須>=0),同時還要保證每簇的字節(jié)數(shù)不能超過 32K,也就是 1024*32 字節(jié)
BPB_RsvdSecCnt
0x0e
2
保留扇區(qū)的數(shù)目,此域不能為0,F(xiàn)AT12/FAT16 必須為1,F(xiàn)AT32 的典型值取為 32,,微軟的系統(tǒng)支持任何非 0值
BPB_BumFATs
0x10
1
分區(qū)中 FAT 表的份數(shù),,任何 FAT 格式都建議為 2
BPB_RootEntCnt
0x11
2
對于 FAT12 和 FAT16 此域包含根目錄中目錄的個數(shù)(每項長度為 32bytes),對于 FAT32,此項必須為 0。對于 FAT12 和 FAT16 , 此 數(shù) 乘 以 32 必 為BPB_BytesPerSec 的偶數(shù)倍,為了達到更好的兼容性,F(xiàn)AT12 和 FAT16 都應該取值為 512
BPB_ToSec16
0x13
2
早期版本中 16bit 的總扇區(qū),這里總扇區(qū)數(shù)包括 FAT 卷上四個基本分區(qū)的全部扇區(qū),此域可以為 0,若此域為 0,那么 BPB_ToSec32 必須為 0,對于 FAT32,此域必為 0。對于 FAT12/FAT16,此域填寫總扇區(qū)數(shù),如果該值小于0x10000 的話,BPB_ToSec32 必須為 0
BPB_Media
0x15
1
對于“固定”(不可移動)存儲介質(zhì)而言,0xF8 是標準值,對于可移動存儲介質(zhì),經(jīng)常使用的數(shù)值是 0xF0,此域合法的取值可以取 0xF0,0xF8,0xF9,0xFA,0xFC,0xFD,0xFE,0xFF。另外要提醒的是,無論此域?qū)懭胧裁磾?shù)值,同時也必須在 FAT[0]的低字節(jié)寫入相同的值,這是因為早期的 MSDOS 1.x 使用該字節(jié)來判定是何種存儲介質(zhì)
BPB_FATSz16
0x16
2
FAT12/FAT16 一個 FAT 表所占的扇區(qū)數(shù),對于 FAT32來說此域必須為 0,在 BPB_FATZ32 中有指定 FAT 表的大小
BPB_SecPerTrk
0x18
2
每磁道的扇區(qū)數(shù),用于 BIOS 中斷 0x13,此域只對于有“特殊形狀”(由磁頭和柱面每分割為若干磁道)的存儲介質(zhì)有效,同時必須可以調(diào)用 BIOS 的 0x13 中斷得到此數(shù)值
BPB_NumHeads
0x1A
2
磁頭數(shù),用于 BIOS 的 0x13 中斷,類似于上面的 BPB_SecPerTrk,只對特殊的介質(zhì)才有效,此域包含一個至少為 1 的數(shù)值,比如 1,4M 的軟盤此域為 2
BPB_HidSec
0x1C
4
在此 FAT 分區(qū)之前所隱藏的扇區(qū)數(shù),必須使得調(diào)用 BIOS的 0x13 中斷可以得到此數(shù)值,對于那些沒有分區(qū)的存儲介質(zhì),此域必須為 0,具體使用什么值由操作系統(tǒng)決定
BPB_ToSec32
0x20
4
該卷總扇區(qū)數(shù)(32bit),這里的扇區(qū)總數(shù)包括 FAT 卷四個個基本分的全部扇區(qū),此域可以為 0,若此域為 0,BPB_ToSec16 必須為非 0,對 FAT32,此域必須是非 0。對于 FAT12/FAT16 如果總扇區(qū)數(shù)大于或等于 0x10000的話,此域就是扇區(qū)總數(shù),同時 BPB_ToSec16 的值為 0。
FAT32 的 BPB 的內(nèi)容和 FAT12/16 的內(nèi)容在地址 0x36 以前是完全一樣的,從偏移量 0x36開始,他們的內(nèi)容有所區(qū)別,具體的內(nèi)容要看 FAT 類型為 FAT12/16 還是 FAT32,這點保證了 在啟動扇區(qū)中包含一個完整的 FAT12/16 或 FAT32 的 BPB 的內(nèi)容,這么做是為了達到最好的兼容性,同時也為了保證所有的 FAT 文件系統(tǒng)驅(qū)動程序能正確的識別和驅(qū)動不同的 FAT 格式,并 讓他們良好地工作,因為他們包含了現(xiàn)有的全部內(nèi)容從 offset 36 開始 FAT12/FAT16 的內(nèi)容開始區(qū)別于 FAT32,下面分兩個表格列出,下 表為 FAT12/FAT16 的內(nèi)容下表為 FAT32 的內(nèi)容
名稱
offset
長度
描述
BPB_FATSz32
0x24
4
一個 FAT 表所占的扇區(qū)數(shù),此域為 FAT32 特有,同時BPB_FATSz16 必須為 0
BPB_Flags
0x28
2
此域 FAT32 特有。Bits0-3:不小于 0 的 FAT(active FAT)數(shù)目,只有在鏡像(mirrorig)禁止時才有效。
Bits 4-6: 保留
Bits 7: 0 表示 FAT 實時鏡像到所有的 FAT 表中1 表示只有一個活動的 FAT 表。這個表就是Bits0-3 所指定的那個
Bits8-15: 保留
BPB_FSVer
0x2A
2
此域為 FAT32 特有,高位為 FAT32 的主版本號,低位為次版本號,這個版本號是為了以后更高級的 FAT 版本考慮,假設(shè)當前的操作系統(tǒng)只能支持的 FAT32 版本號為 0.0。那么該操作系統(tǒng)檢測到此域不為 0 時,它便會忽略 FAT 卷,因為它的版本號比系統(tǒng)能支持的版式本要高
BPB_RootClus
0x2C
4
根目錄所在第一個簇的簇號,通常該數(shù)值為 2,但不是必須為 2
磁盤工具在改變根目錄位置時,必須想辦法讓磁盤上第一個非壞簇作為根目錄的第一個簇(比如第 2 簇,除非它已經(jīng)被標記為壞簇),這樣的話,如果此域正好為 0 的話磁盤檢測工具也能輕松的找到根目錄所在簇的位置
BPB_FSIfo
0x30
2
保留區(qū)中 FAT32 卷 FSINFO 結(jié)構(gòu)所占的扇區(qū)數(shù),通常為1
在 Backup Boot 中會有一個 FSINFO 的備份,但該備份只是更新其中的指針,也就是說無論是主引導記錄還是備份引導記錄都是指向同一個 FSINFO 結(jié)構(gòu)
BPB__BkBootSec
0x32
2
如果不為 0,表示在保留區(qū)中引導記錄的備數(shù)據(jù)所占的扇區(qū)數(shù),通常為 6。同時不建議使用 6 以外的其他數(shù)值
BPB_Reserved
0x34
12
用于以后 FAT 擴展使用,對 FAT32。此域用 0 填充
BS_DrvNum
0x40
1
與 FAT12/16 的定義相同,只不過兩者位于啟動扇區(qū)不同的位置而已
BS_Reserved1
0x41
1
與 FAT12/16 的定義相同,只不過兩者位于啟動扇區(qū)不同的位置而已
BS_BootSig
0x42
1
與 FAT12/16 的定義相同,只不過兩者位于啟動扇區(qū)不同的位置而已
BS_VolID
0x43
4
與 FAT12/16 的定義相同,只不過兩者位于啟動扇區(qū)不同的位置而已
BS_FilSysType
0x47
11
與 FAT12/16 的定義相同,只不過兩者位于啟動扇區(qū)不同的位置而已
BS_FilSysType
0x52
8
通常設(shè)置為“FAT32”,請參照 FAT12/16 此部分的陳述。
關(guān)于 FAT 啟動扇區(qū)還有一點重要的說明,我們假設(shè)里面的內(nèi)容是按字節(jié)排序的,那么扇區(qū)[510]的內(nèi)容一定 0x55,扇區(qū)[511]的內(nèi)容一定是 0xAA很多 FAT 資數(shù)文檔會把 0xAA55 說成是“啟動扇區(qū)最后兩字節(jié)的內(nèi)容”,這樣的說法是正 確的,但僅僅適用于 BPB_BytsPerSec 值為 512 的情況。若 BPB_BytsSec 的值大于 512,該標記的位置并沒有改變,雖然在啟動扇區(qū)的最后兩個字節(jié)寫 0xAA55 并沒有問題。
五、FAT類型識別
FAT 的字類型(FAT12/16/32)只能通過 FAT 卷中的簇(Cluster)數(shù)來判定,沒有其 他的辦法。
Cluster 總數(shù)的計算:RootDirSectors = (BPB_RootEntCnt*32) /BPB_BytsPerSec DataSect = TotSec – (BPB_RsvdSecCnt +(BPB_NumFATs * FATSz) + RootDirSectors) CountofClusters = DataSec / BPB_SecPerClus
If(CountofClusters < 4085) { /*卷類型是 FAT12 */
} else if(CountofClusters < 65525) { /* 卷類型是 FAT16 */
} else {
/* 卷類型是 FAT32 */
}
注意這里的簇數(shù)(count of Cluster)是指數(shù)據(jù)區(qū)所占簇的數(shù)量(the count of the data cluster),從簇 2 算起,而“最大可用簇數(shù)”(Maximun valid cluster number for the volume)是 CountofClusters +1,“包括保留簇的簇數(shù)”(count of cluster including the two reserved cluster)則為CountofClusters +2。根目錄占據(jù)的 Sector 數(shù):RootDirSectors = (BPB_RootEntCnt*32) /BPB_BytsPerSec 數(shù)據(jù)區(qū)(Cluster 2)的起始 Sector: FirstDataSector = BPB_EsvdSecCnt + (BPB_NumFATs * FATSz) + RootDirSectors 給一個合法的簇號 N,該簇的第一個扇區(qū)號由下式計算:FirstSectorofCluster =((N -2) * BPB_SecPerClust) + FirstDataSecot; 因為 BPB_SecPerClus 總是 2 的整數(shù)次方,這意味著 BPB_SecPerSlus 的乘法運算可 以通過移動來進行。
NOTE:這里所說的 Sector 號,指的是針對卷中包 BPB 的第一個扇區(qū)(DBR)的偏移量(DBR設(shè)為Sector 0)。
六、FAT各部分位置的計算
DBR_Base:從DPT中偏移8Bytes的地址讀取4Bytes數(shù)據(jù)就可以直接看作是DBR所在的 Sector。
RsvSectors = BPB_RsvdSecCnt。
FAT_Base = DBR_Base + RsvSectors FATSize = BPB_NumFATs * FATSz
其中,F(xiàn)ATSz = (FAT32?) BPB_FATSz32 : BPB_FATSz16 Root_Base:如果是FAT12/FAT16,則Root_Base = FAT_Base + FATSize
如果是FAT32,則Root_Base = BPB_RootClus RootSize :如果是FAT32,則沒有限制;否則RootSize = (BPB_RootEntCnt*32) / BPB_BytsPerSec Data_Base:僅對于FAT12/FAT16,Data_Base = Root_Base + RootSize
七、FAT表結(jié)構(gòu)
FAT 表(File Alloacation Table)是一一對應于數(shù)據(jù)簇號的列表。文件系統(tǒng)分配磁盤空間按簇來分配。因此,文件占有磁盤空間時,基本單位不是字節(jié)而是簇, 即使某個文件只有一個字節(jié),操作系統(tǒng)也會給它分配一個最小單元:即一個簇。為了可以將磁盤 空間有序地分配給相應的文件,而讀取文件的時候又可以從相應的地址讀出文件,我們可以把數(shù) 據(jù)區(qū)空間分成 BPB_BytsPerSec*BPB_SecPerClus 字節(jié)長的簇來管理,F(xiàn)AT 表項的大小與 FAT 表的類型有關(guān),F(xiàn)AT12 的表項為 12bit ,F(xiàn)AT16 為 16bit, 而 FAT32 則為 32bit。對 于大文件,需要分配多個簇。同一個文件的數(shù)據(jù)并不一定完整地存放在磁盤中一個連續(xù)地區(qū)域 內(nèi),而往往會分若干段,像鏈子一樣存放。這種存儲方式稱為文件的鏈式存儲。為了實現(xiàn)文件的 鏈式存儲,文件系統(tǒng)必須準確地記錄哪些簇已經(jīng)被文件占用,還必須為每個已經(jīng)占用的簇指明存 儲后繼內(nèi)空的下一個簇的簇號,對于文件的最后一簇,則要指明本簇無后繼簇。這些都是由 FAT 表來保存的,F(xiàn)AT 表的對應表項中記錄著它所代表的簇的有關(guān)信息:諸如是空,是不是壞簇,是 否是已經(jīng)是某個文件的尾簇等。
以 FAT16 為例說明 FAT 的結(jié)構(gòu)如下:
表項
示例代碼
描述
FFF8
磁盤標識字,必須為 FFF8
1
FFFF
第一簇已經(jīng)被占用
2
0003
0000h :可用簇
3
0004
0002h - FFFEF :已用簇,表項中存放文件下個簇的簇號
……
……
N
FFFF
FFFF0h - FFFF6 :保留簇
N+1
0000
FFFF7h :壞簇
……
……
FFFF8h - FFFFFh :文件的最后一簇
FAT各系統(tǒng)記錄項的取值含義(16進制)
FAT12記錄項的取值
FAT16記錄項的取值
FAT32記錄項的取值
對應簇的表現(xiàn)情況
0
0
0
未分配的簇
002~FFF
0002~FFEF
00000002~FFFFFFEF
已分配的簇
FF0~FF6
FFF0~FFF6
FFFFFFF0~FFFFFFF6
系統(tǒng)保留
FF7
FFF7
FFFFFFF7
壞簇
FF8~FFF
FFF8~FFFF
FFFFFFF8~FFFFFFFF
文件結(jié)束簇
八、長文件名
長文件名是在原有的 FAT 系統(tǒng)上引入的,在只支持短文件名的系統(tǒng)上,長文件名就像是不存在一樣。為了達到這個目標,長文件名通過在原有的目錄項中引入新的屬性字(Attribute)得以實現(xiàn)。
ATTR_LONG_NAME = ATTR_READ_ONLY |
ATTR_HIDDEN |
ATTR_SYSTEM |
ATTR_VOLUME_ID
判斷一個目錄項是否為長文件名,要通過下面 MASK 實現(xiàn):
ATTR_LONG_NAME_MASK = ATTR_READ_ONLY |
ATTR_HIDDEN |
ATTR_SYSTEM |
ATTR_VOLUME_ID |
ATTR_DIRECTORY |
ATTR_ARCHIVE
長文件名目錄項數(shù)據(jù)結(jié)構(gòu)如下:
Name
Offset(byte)
Size(bytes)
Description
LDIR_Ord
0x00
1
該項在這組長文件名中的序號。如果第 6 bit 為 1(Mask with 0x40),說明這是該組長文件名的最后一項。每一組有效的長文件名都必須有這個標志位。
LDIR_Name1
0x01
10
長文件名的第 1-5 個字符
LDIR_Attr
0x0B
1
屬性字 – 必須為 0x0F(ATTR_LONG_NAME)
LDIR_Type
0x0C
1
如果是 0,則表示這個目錄項是長文件名的一部分。非 0 數(shù)值為保留設(shè)置。
LDIR_Chksum
0x0D
1
對應的短文件名校驗和(Checksum)
LDIR_Name2
0x0E
12
長文件名的第 6-11 個字符
LDIR_FstClusLO
0x1A
2
Must be ZERO. This is an artifact of the FAT“first cluster”and must be zero for compatibility with existing disk utilities. It's meaningless in the context of a long dir entry.
LDIR_Name3
0x1C
4
長文件名的第 12-13 個字符
長文件名字符采用 Unicode 編碼。
Checksum 算法,用 C 語言實現(xiàn)如下:
//------------------------------------------------------------------------
//ChkSum()
//Returns an unsigned byte checksum computed on an unsigned byte
//array. The array must be 11 bytes long and is assumed to contain
//a name stored in the format of a MS-DOS directory entry.
//
Passed: pFcbName
Pointer to an unsigned byte array assumed to be 11 bytes long.
// Returns: Sum
An 8-bit unsigned checksum of the array pointed
// to by pFcbName.
unsigned char ChkSum (unsigned char *pFcbName)
{
short FcbNameLen; unsigned char Sum;
Sum = 0;
for (FcbNameLen=11; FcbNameLen!=0; FcbNameLen--) {
// NOTE: The operation is an unsigned char rotate right Sum = ((Sum & 1) ? 0x80 : 0) + (Sum >> 1) + *pFcbName++;
}
return (Sum);
}
下面是一組長文件名目錄項。
00 79 96 40 43 70 00 71 00 72 00 73 00 74 00 0F 00 52 2E 00 ...Cp.q.r.s.t R..
00 79 96 50 74 00 78 00 74 00 00 00 FF FF 00 00 FF FF FF FF t.x.t...……
00 79 96 60 02 63 00 64 00 65 00 66 00 67 00 0F 00 52 68 00 .c.d.e.f.g...Rh.
00 79 96 70 69 00 67 00 6B 00 6C 00 6D 00 00 00 6E 00 6F 00 i.g.k.l.m...n.o.
00 79 96 80 01 73 00 75 00 62 00 64 00 69 00 0F 00 52 72 00 .s.u.b.d.i...Rr.
00 79 96 90 66 00 69 00 6C 00 65 00 5F 00 00 00 61 00 62 00 f.i.l.e._...a.b.
00 79 96 A0 53 55 42 44 49 52 7E 31 54 58 54 20 00 22 2E 4F SUBDIR~1TXT .".O
00 79 96 B0 6C 3B 71 3B 00 00 E9 84 6D 3B 14 00 00 0E 00 00 l;q;..閯 m;......
可以看出,長文件名為“subdirfile_abcdefghigklmnopqrst.txt”,對應的短文件 名為“SUBDIR~1.TXT”。
下面是《Microsoft Extensible Firmware Initiative FAT32 File System
Specification—version1.03》中給出的示例:
假設(shè)創(chuàng)建一個名為“The quick brown.fox”的文件,系統(tǒng)將為它建立如下的目錄項:
九、目錄結(jié)構(gòu)
目錄所在的扇區(qū),都是以32 Bytes 劃分為一個單位,每個單位稱為一個目錄項(Directory Entry),即每個目錄項的長度都是 32 Bytes。
FAT32短文件目錄項32個字節(jié)的表示定義FAT32 表每簇占用 4 Bytes,從 Sector 內(nèi)偏移地址 50 讀取 4 Bytes 數(shù)據(jù)為 0x15,表 示之后的數(shù)據(jù)占用簇0x15,簇0x15 在Sector 內(nèi)偏移地址為0x54,從地址0x54 讀取4 Bytes。數(shù)據(jù)為 0x16,依次類推,最后在偏移地址 0x68 讀取 4 Bytes 數(shù)據(jù)為 0x0FFFFFFF,表示文 件在該簇就結(jié)束了。所以該文件占用的簇號依次為 0x14,0x15,0x16,0x17,0x18,0x19 和 0x20。