概述 php
起先全部的FAT文件系統都是爲IBM PC機器而設計的,這說明了一個重要的問題:FAT文件系統在磁盤上的數據是用「小端」(Little Endian)結構存儲的。咱們使用4個8-bit的字節——起始字節爲byte[0],結束字節爲byte[3]——來存儲一個32-bit的FAT項(FAT entry)。而後分別給這32位編號爲00-31。 算法
這對於那些使用「大端」(big-endian)存儲結構的機器就顯得尤其重要,由於在磁盤存取數據以前,必須先完成Big-Endian到Little-Endian之間的轉換。每一個FAT文件系統都由4部分組成,這些基本區域按以下順序排列。 數據結構
0——保留區(Reserved Region);
1——FAT區(FAT Region);
2——根目錄區(Root Directory Region,FAT32卷沒有此域);
3——文件和目錄數據區(File and Directory Region) 架構
啓動扇區與BPB 函數
BPB(BIOS Parameter Block)是FAT文件系統中第一個很重要的數據區,它位於該FAT卷的第一個扇區,同時也屬於FAT文件系統基本區域的保留區。這個扇區又叫作「啓動扇區」、「保留扇區」、「0扇區」,衆多的說法都說明一個相同的問題:該扇區是FAT卷的第一個扇區。 工具
這是FAT文件系統中第一個讓人感到迷惑的地方,對於MS-DOS 1.x的版本,啓動扇區中並無BPB這麼一個東西,FAT文件系統的最先期版本只有兩種不一樣的格式:使用於單面或雙面的360K 5寸軟盤。這兩種格式是經過FAT的第一個字節(FAT[0]的低8位)來區分的。 ui
在MS-DOS 2.x之後,啓動扇區裏增長了BPB用於區分磁盤介質,同時再也不支持老的磁盤介質區分方式(用FAT的第一個字節來區分),全部的FAT文件系統卷必須在啓動扇區中包含BPB。 this
這又是一個迷惑人的地方,BPB具體是什麼樣的?在MS-DOS 2.x的定義中,每一個FAT卷的扇區數不能多於65536(每一個扇區512字節的話最多32MB——2^16×2^9=32MB),這一個限定是因爲定義「總扇區數」的變量自己是一個16-bit的數據類型。這一個限制在MS-DOS 3.x中有所改進,它使用一個32bit的變量來存儲「總扇區數」(若每一個扇區512字節,則每一個FAT卷的最大容量爲2^32×2^9=2^41=2048GB)。 spa
在WIN95操做系統中,確切地說應該是在OSR2(OEM Service Release 2)出現的時候BPB的內容有了新的變化,在這一版本中引入了新的FAT類型——FAT32。在FAT16中,因爲FAT表的大小限制了有效的簇數(Cluster),同時也就限制了磁盤空間的大小,若是每一個扇區爲512字節的話,那麼FAT16格式只能支持到2G。FAT32的引入改變了這一個情況。再也不須要增長分區來管理大於2G的硬盤。 操作系統
FAT32的BPB內容和FAT12/FAT16的內容在BPB_ToSet32區域之前徹底一致,而從偏移量36開始,他們的內容有所區別,具體內容要看FAT類型爲FAT12/FAT16仍是FAT32(後面的內容會提到如何區分FAT格式),這點保證了在啓動扇區中包含一個完整的FAT12/FAT16或FAT32的BPB內容,這麼作是爲了達到最好的兼容性,同時也爲了保證全部的FAT文件系統驅動程序能正確地識別和驅動不一樣的FAT格式,並讓他們良好地工做,由於他們包含了現有的所有內容。
NOTE:在如下的描述中,凡是名稱與BPB_開頭的域都是BPB的一部分,凡是名稱與BS_開頭的項都是啓動扇區(boot sector)的一部分,而不是真正屬於BPB內容。下面是FAT 0扇區的內容,BPB也包含其中。
|
|
|
0x00 |
3個字節 |
跳轉指令 |
0x03 |
8個字節 |
廠商標誌和OS版本號 |
0x0B |
53個字節 |
BPB |
0x40 |
26個字節 |
擴展BPB |
0x5A |
420個字節 |
引導程序代碼 |
0x01FE |
2個字節 |
有效結束標誌 |
|
|
|
|
BS_jmpBoot |
0 |
3 |
跳轉指令。指向啓動代碼,容許如下兩種形式: jmpBoot[0]=0xEB,jmpBoot[1]=0x??,jmpBoot[2]=0x90;以及 jmpBoot[0]=0xE9,jmpBoot[1]=0x??,jmpBoot[2]=0x?? 0x??表示該字節能夠爲任意8-bit值,這是Intel x86架構3字節的無條件轉移指令,跳轉到操做系統的啓動代碼,這些啓動代碼每每緊接BPB後面0扇區裏的剩餘字節,固然也可能位於其餘扇區。以上的兩種形式任取。jmpBoot[0]=0xEB是一種經常使用的格式。 |
BS_OEMName |
3 |
8 |
建議值爲MSWIN4.1,此域常常引發人們的誤解,其實這只是一個字符串而已,Microsoft的操做系統彷佛並不關心此域。但其餘廠商的FAT驅動程序可能會檢測到此項,這就是爲何建議將此域設定爲「MSWIN4.1」 的緣由,這樣能夠儘可能避免兼容性的問題。你能夠更改它的內容,但這有可能形成某些FAT驅動程序沒法識別該磁盤。在不少狀況下,該域用於顯示格式化該FAT卷的操做系統的名稱。 (廠商標誌和OS版本號) |
BPB_BytesPersec |
11 |
2 |
每扇區字節數,取值只能是如下的幾種狀況:5十二、102四、2048或者是4096,通常狀況下,設置爲512將會取得更好的兼容性,目前有不少的FAT代碼都是硬性的規定每扇區字節數爲512,而不是實際檢測該區域的值。Microsoft操做系統可以很好地支持102四、2048和4096各類數值。 NOTE:請不要曲解此處「最好的兼容性」的意思,若是某些存儲介質的物體特性決定其值爲N,那麼你就必須使用該數值N,該數值N必定是小於等於4096。那麼取得「最好的兼容性」的辦法就是使用該特定的N值。 |
BPB_SecPerClus |
13 |
1 |
每簇扇區數,其值必須是2的整數次方(該整數必須大於等於0),如一、二、四、八、1六、3二、64或128,同時還必須保證每簇的字節數不超過32K,即——保證(BPB_BytesPersec * BPB_SecPerClus ≤ 32K)(1024×32)該值大於32K是絕對不容許的,雖然有些版本的操做系統支持每簇字節數最大到64K,但不少應用程序的安裝程序都沒法在這樣的FAT文件系統上正常運行。 |
BPB_RsvdSecCnt |
14 |
2 |
保留區中保留扇區的數目,保留扇區從FAT卷的第一個扇區開始,此域不能爲0,對於FAT12和FAT16必須爲1,FAT32的典型取值爲32,目前不少FAT程序都是硬性規定FAT12/FAT16的保留扇區爲1,而不對此域進行實際的檢測,Microsoft的操做系統支持任何非零的值。 |
BPB_NumFATs |
16 |
1 |
此卷中FAT表的份數。任何FAT格式此域都建議爲2。雖然此域取值爲其餘≥1的數值也是合法的,可是對於不少FAT程序和部分操做系統來講,此項不爲2的時候將沒法正常工做。可是當不爲2時,Microsoft的操做系統仍然能良好工做。可依然強烈建議此項值爲2。 選擇此項的標準值爲2的緣由是爲了提供一份FAT表的備份,當其中一個FAT表所在的扇區被損壞時咱們能夠從備份的FAT表中讀出正確的數據。但是對於一些非磁盤介質的存儲器(如FLASH卡),這一特性變得毫無用處,若是想使用1個FAT表來節省空間,那麼帶來的問題將是某些操做系統沒法識別該FAT卷。 |
BPB_RootEntCnt |
17 |
2 |
對於FAT12和FAT16,此域中包含根目錄中的目錄項數(每一個項長度爲32Kbytes),對於FAT32,此項必須爲0。對於FAT12和FAT16,此數值乘以32,必須爲BPB_BytesPersec的偶數倍,爲了達到更好的兼容性,FAT12/FAT16應該取值爲512。 |
BPB_TotSec16 |
19 |
2 |
早期版本中16-bit的總扇區數,這裏的總扇區數包括FAT捲上四個基本區的所有扇區,此域能夠爲0,若此域爲0,那麼BPB_TotSec32必須非0,對於FAT32,此域必須爲0。對於FAT12/FAT16,此域填寫總扇區數,若是該數值小於0x10000的話,BPB_TotSec32必須爲0。 |
BPB_Media |
21 |
1 |
對於「固定」(不可移動)的存儲介質而言,0xF8是標準值,對於能夠移動的存儲介質,常用的數值是0xF0,此域合法的取值能夠是0xF0,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE和0xFF。另外要提醒的一點是,不管此域寫入什麼數值,同時也必須在FAT[0]的低字節寫入相同的值,這是由於早期的MSDOS 1.x使用該字節來斷定是何種存儲介質。 |
BPB_FATSz16 |
22 |
2 |
FAT12/FAT16一個FAT表所佔的扇區數,對於FAT32來講,此域必須爲0,在BPB_FATSz32中有指定其FAT表的大小。 |
BPB_SecPerTrk |
24 |
2 |
每磁道扇區數,用於BIOS中斷0x13,此域只對於有「特殊形狀」(由磁頭和柱面分割爲若干磁道)的存儲介質有效,同時必須能夠調用BIOS的0x13中斷獲得此數值。 |
BPB_NumHeads |
26 |
2 |
磁頭數,用於BIOS的0x13中斷,相似於上面的BPB_SecPerTrk,只有對特殊的介質纔有效,此域包含一個至少爲1的數值,好比1.4M的軟盤,此域爲2。 |
BPB_HiddSec |
28 |
4 |
在此FAT分區以前所隱藏的扇區數,必須使用調用BIOS的0x13中斷能夠獲得這個數值,對於那些沒有分區的存儲介質,此域必須爲0,具體使用什麼由操做系統決定。 |
BPB_TotSec32 |
32 |
4 |
該卷總扇區數(32-bit),這裏的總扇區數包括FAT捲上四個基本區的所有扇區,此域能夠爲0,若此域爲0,則BPB_TotSec16必須爲非0,對於FAT32,此域必定是非0。對於FAT12/FAT16,若是總扇區數大於或等於0x10000(64KB)的話,此域就是總扇區數,同時BPB_TotSec16的值爲0。 |
從Offset 36(0x24)開始FAT12/16的內容開始區別於FAT32,如今分兩個表格列出來,下表爲FAT12/16的內容:
名稱 | Offset(Byte) | 大小(Byte) | 描述 |
BS_drvNum | 36(0x24) | 1 | 用於BIOS中斷0x13獲得磁盤驅動器參數,(0x00爲軟盤,0x80爲硬盤)。 NOTE:此域的值實際上由操做系統來決定。 |
BS_Reserved1(壹) | 37(0x25) | 1 | 保留(供NT使用),格式化FAT卷時必須把此域設置爲0。 |
BS_BootSig | 38(0x26) | 1 | 擴展引導標記(0x29),用於指明此後的三個域可用。 |
BS_VolID | 39(0x27) | 4 | 卷標序列號,此域以BS_VolLab一塊兒,可用用來檢測磁盤是否正確,FAT文件系統可用用此判斷鏈接的可移動磁盤是否正確,此域每每是由時間和日期組成的一個32位值。 |
BS_VolLab | 43(0x2B) | 11 | 磁盤卷標,此域的值必須與根目錄中11字節長的卷標一致。 NOTE:FAT文件系統必須保證在根目錄的卷標文件更改或是建立的同時,此域的內容能獲得及時的更新,當FAT卷沒有卷標時,此域的內容爲「NO NAME」。 |
BS_FileSysType | 54(0x36) | 8 | 如下的幾種之一:「FAT12」、「FAT16」、「FAT32」。 NOTE:很多人錯誤認爲FAT文件系統的類型由此域來肯定,仔細點你就能發現此域並非BPB的一部分,只是一個字符串而已,Microsoft的操做系統並不使用此域來肯定FAT文件的類型,由於它經常被寫錯,或是根本不存在,後面將討論如何來檢測一個FAT文件系統的類型,但無論如何,建議您在此域填寫正確信息,由於一些非Microsoft的操做系統會檢測此域。 |
下標爲FAT32的內容:
名稱 | Offset(Byte) | 大小(Byte) | 描述 |
BPB_FATSz32 | 36(0x24) | 4 | 一個FAT表所佔的扇區數,此域爲FAT32特有,同時BPB_FATSz16必須爲0。 |
BPB_ExtFlags | 40(0x28) | 2 | 此域FAT32特有。 Bits 0~3:不小於0的活動FAT(active FAT)數目,只有在鏡像 (mirroring)禁止時纔有效。 Bits 4~6:保留; Bits 7:——0 表示FAT實時鏡像到全部的FAT表中; ——1 表示只有一個活動的FAT表,這個表就是Bits 0~3 所指定的那個。 Bits 8~15:保留。 |
BPB_FSVer | 42(0x2A) | 2 | 此域FAT32特有。高位爲FAT32的主版本號,低位爲次版本號,這個版本號是爲了之後更高級的FAT版本考慮,假設當前的操做系統所能支持的FAT32版本號爲0:0。那麼該操做系統檢測到此域不爲0時,它便會忽略這個FAT卷,由於它的版本號比系統能支持的版本要高。 |
BPB_RootClus | 44(0x2C) | 4 | 此域FAT32特有。根目錄所在第一個簇的簇號,一般該數值爲2,擔不是必須爲2。 NOTE:磁盤工具在改變根目錄位置時,必須想辦法讓磁盤上的第一個非壞簇做爲根目錄的第一個簇(好比第2簇,除非它已經被標記爲壞簇),這樣的話,若是此域正好爲0的話,磁盤檢測工具也能很輕鬆地找到根目錄所在簇的位置。 |
BPB_FSInfo | 48(0x30) | 2 | 此域FAT32特有。保留區中FAT32卷FSINFO結構所佔的扇區數,一般爲1。 NOTE:在Backup Boot中會有一個FSINFO的備份,但該備份只是更新其中的指針,也就是說不管是主引導記錄,仍是備份引導記錄都是指向同一個FSINFO結構。 |
BPB_BkBootSec | 50(0x32) | 2 | 此域FAT32特有。若是不爲0,表示在保留區中引導記錄的備份數據所佔的扇區數,一般爲6。同時不建議使用6之外的其餘數值。 |
BPB_Reserved | 52(0x34) | 12 | 此域FAT32特有。用於之後的FAT擴展使用,對於FAT32,此域用0填充。 |
BS_DrvNum | 64(0x40) | 1 | 與FAT12/FAT16的定義相同,只不過二者位於啓動扇區不一樣位置而已。 |
BS_Reserved1 | 65(0x41) | 1 | 與FAT12/FAT16的定義相同,只不過二者位於啓動扇區不一樣位置而已。 |
BS_BootSig | 66(0x42) | 1 | 與FAT12/FAT16的定義相同,只不過二者位於啓動扇區不一樣位置而已。 |
BS_VolID | 67(0x43) | 4 | 與FAT12/FAT16的定義相同,只不過二者位於啓動扇區不一樣位置而已。 |
BS_FileSysType | 71(0x47) | 11 | 與FAT12/FAT16的定義相同,只不過二者位於啓動扇區不一樣位置而已。 |
BS_FileSysType | 82(0x52) | 8 | 一般設置爲「FAT32」,請參照FAT12/FAT16部分關於此域的陳述,該域的內容和FAT類型的斷定無關。 |
關於FAT的啓動扇區還有一點重要的說明:咱們假設裏面的內容是按照字節排序的,那麼扇區[510]的內容必定是0x55,扇區[511]的內容必定是0xAA。
NOTE:不少FAT的資料文檔會錯誤地把0xAA55說成是「啓動扇區最後兩字節的內容」,這樣的陳述是正確的,若是,僅僅是若是——BPB_BytesPerSec的值爲512的話。如果BPB_BytesPerSec的值大於512,該標記的位置並無變(雖然在啓動扇區的最後兩個字節寫上0xAA55徹底沒有問題)。
關於BPB_TotSec16/32這裏再做一點補充:假設如今咱們有一塊磁盤或者一個分區,其扇區數爲DskSz,若是BPB_aTotSec(BPB_TotSec16或是BPB_TotSec32其中不爲0的那個)的值小於或等於DskSz並不會使該FAT卷在使用中出現什麼錯誤,實際上,BPB_TotSec16/32的值不要比DskSz小得離譜就不會有什麼錯誤。
這樣作將形成磁盤空間的浪費,程序自己並不會認爲該FAT卷存在什麼錯誤。可是,若是BPB_TotSec16/32的值比DskSz大的話將會使FAT卷遭受嚴重的損壞。由於它超出了存儲介質或是磁盤分區的邊界。當BPB_TotSec16/32的值比DskSz大時,一些數據將不幸地被丟失。
FAT數據結構(FAT Data Structure)
接下來一個重要的數據結構就是FAT表(Fat Allocation Table),它是——對應於數據區簇號的列表。
文件系統分配磁盤空間是按照簇來分配的。所以,文件佔用磁盤空間時,基本單位不是字節而是簇,即便某個文件只有一個字節,操做系統也會給它分配一個最小存儲單元——簇。爲了能夠將磁盤空間有序地分配給相應的文件,而讀取文件的時候又可以從相應的地址讀出文件,咱們把數據區空間分爲BPB_BytesPerSec(每扇區字節數)×BPB_SecPerClus(每簇扇區數)字節長的簇來管理,FAT表項的大小與FAT類型有關,FAT12的表項爲12bit,FAT16的表項爲16bit,而FAT32的表項則爲32bit。對於大文件,須要分配多個簇。同一個文件的數據並不必定完整地存放在磁盤中一個連續的區域內!而每每會分紅若干段,像鏈條同樣存放。這種存儲方式稱爲文件的鏈式存儲。爲了實現文件的鏈式存儲,文件系統必須準確地記錄哪些簇已經被文件佔用,還必須爲每一個已經佔用的簇指明存儲後繼內容的下一個簇的簇號,對於文件的最後一個簇,則須要指明本簇無後繼簇。這些都是由FAT表來保存的,FAT表的對應表項中記錄着它所表明的簇的有關信息:諸如是否空,是否壞簇,是否已是某個文件的尾簇等等。
RootDirSectors=((BPB_RootEntCnt × 32) + (BPB_BytesPerSec - 1))/BPB_BytesPerSec;
根目錄扇區數=((根目錄數 × 32) + (每扇區字節數 - 1))/每扇區字節數(根目錄數表明目錄的項數,每一個項目長度爲32bytes)
上圖中BPB_RootEntCnt=512,BPB_BytesPerSec=512,則按照上述計算公式,能夠得出RootDirSectors=32,也就是說,這個U盤根目錄扇區數爲32,約爲16KB。
由於FAT32的BPB_RootEntCnt(根目錄項數)爲0,因此對於FAT32卷RootDirSectors的值也必定是0。上式中的32是每一個目錄項所佔的字節數。計算結果四捨五入。
數據區的起始地址,簇2的第一個扇區由下面的公式計算:
If(BPB_FATSz16 != 0) (若是FAT12/16一個FAT表所佔的扇區數不爲0,則該FAT必爲FAT12/16)
FATSz = BPB_FATSz16;
Else
FATSz = BPB_FATSz32;
FirstDataSector = BPB_RsvdSecCnt + (BPB_NumFATs × FATSz) + RootDirSectors;
仍以上圖爲例,咱們來計算一下這個U盤的數據區第一個扇區號。
上圖中BPB_RsvdSecCnt=4,BPB_NumFATs=2,FATSz=246,RootDirSectors已經算出,爲32。則數據區起始扇區號=4+246×2+32=528.換言之,數據區緊跟在根目錄後,根目錄的最後一個扇區號爲527。
數據區第一個扇區 = 保留扇區數 + (FAT表的份數 × FAT表所佔的扇區數) + 根目錄扇區數。
NOTE:扇區號指的是針對卷中包含BPB的第一個扇區的偏移量(包含BPB的第一個扇區是扇區0),並非必須直接和磁盤的扇區相對應。由於卷的扇區0並不必定就是磁盤的扇區0。
給一個合法的簇號N,該簇的第一個扇區號(針對FAT卷扇區0的偏移量)由下式計算:
FirstSectorofCluster = ((N-2)×BPB_SecPerClust) + FirstDataSector;(-2是由於起始標誌F8 FF FF FF佔用兩個存放簇號的位置的緣故)。
例如:下圖中是U盤的FAT1截圖(WinHex)
要注意,這裏的簇號指的是至關於FAT1的偏移位置,而非指磁盤的最小單位。
根據上面提供的公式:咱們計算出任意簇號對應的第一個扇區號。好比,簇號爲7。那麼簇7的第一個扇區號=((7-2)×64)+528=848。(這裏的BPB_SecPerClust爲64)。
NOTE:由於BPB_SecPerClus老是2的整數次方(1,2,4,8,……)這意味着BPB_SecPerClus的乘除法運算能夠經過移位(SHIFT)來進行。在當前Intel x86架構2進制的乘法(MULT)和除法(DIV)的機器指令很是的繁雜和強大,而使用移位來運算則會相對的快不少。
FAT類型辨別
這是一個常常產生錯誤的地方,而且經常會出現諸如「off by 1」,「off by 2」,「off by 10」和「massively off」的錯誤,事實上,FAT類型的檢測十分簡單,FAT的類型——FAT12,或是FAT16或是FAT32——只能經過FAT卷中簇的數量來斷定,沒有其餘辦法。
請仔細閱讀本段的每個細節,每一個詞都很關鍵。好比「簇數(count of cluster)」並非指「最大可取得的簇的數量(maximum valid cluster number)」,由於數據區的第一個簇是簇2而不是0或1。
首先咱們討論這個「簇數」是如何計算的,它徹底根據BPB的內容來肯定,咱們先計算根目錄所佔的扇區數(前面已經有敘述)。
RootDirSectors = ((BPB_RootEntCnt × 32) + (BPB_BytesPerSec - 1))/BPB_BytesPerSec;
FAT32的RootDirSectors爲0。
接下來咱們檢測數據區中的扇區數:
If(BPB_FATSz16 != 0)
FATSz = BPB_FATSz16;
Else
FATSz = BPB_FATSz32;
If(BPB_TotSec16 != 0)
TotSec = BPB_TotSec16;
Else
TotSec = BPB_TotSec32;
DataSec = TotSec – (BPB_RsvdSecCnt + (BPB_NumFATs × FATSz) + RootDirSectors);
數據扇區 = 總扇區數 – (保留扇區數 + (FAT數 × FAT表所佔扇區數) + 根目錄扇區數)
仍然以上圖中的U盤數據爲例,因爲U盤的文件系統是FAT16,因此FAT表的大小即BPB_FATSz16,又由於圖示U盤中BPB_TotSec16爲0,因此FAT捲上四個基本區的總扇區數取決於BPB_TotSec32,這裏的BPB_TotSec32的值爲42029408。BPB_RsvdSecCnt爲4,BPB_NumFATs爲2,FATSz爲246,RootDirSectors上面已經計算出爲32,則數據扇區數=42029408-(4+(2×246)+32)=4028880。
計算簇數:
CountofClusters = DataSec / BPB_SecPerClus;
簇數 = 數據扇區數 / 每簇扇區數
一樣,數據區的簇數=4028880/64=62951。這個計算結果和實際結果吻合!
請記住計算結果四捨五入。
如今咱們就能夠斷定FAT的類型了,這部分請仔細閱讀,不然會致使off by 1的錯誤。
在下面的程序中,「<」和「<=」是不同的,另外注意數字不要弄錯。
If(CountofCluster < 4085) {
}
Else if(CountofCluster < 65525) {
}
Else {
}
這是檢測FAT類型的惟一辦法。世界上不存在簇數大於4084的FAT12卷,也不存在簇數小於4085或是大於65524的FAT16卷,一樣沒有哪一個FAT32卷的簇數小於65525。若是你堅持要違背這個規則來建立一個FAT卷,那麼Microsoft的操做系統將沒法對此捲進行操做,由於它不認爲這是FAT文件系統。
NOTE:如前面所說,目前有不少FAT的代碼有一些錯誤,經常會出現 off by 1,2,8,10或是off by 16的錯誤。由於,爲了和現存的代碼取得最好的兼容性,強烈建議在格式化FAT文件系統時,儘可能使簇數的取值不要接近4085或65525,最好能和這個分割點的值相差16或更多。
同時請注意這裏的簇數(Count of Cluster)是指數據區所佔簇的數量(the count of data clusters),從簇2開始算起,而「最大可用的簇數」(Maximum valid cluster number for the volume)是簇數+1,「包括保留簇的簇數(count of cluster including the two reserved cluster)」則爲簇數+2。
FAT的另外一個重要計算公式:給一個簇號N,它位於FAT表的什麼位置呢?對於FAT16和FAT32都比較容易計算,而FAT12則會複雜一些:
If(BPB_FATSz16 != 0)
FATSz = BPB_FATSz16;
Else
FATSz = BPB_FATSz32
If(FATType == FAT16)
FATOffset = N * 2;
Else if (FATType == FAT32)
FATOffset = N * 4;
ThisFATSecNum = BPB_RsvdSecCnt + (FATOffset / BPB_BytesPerSec); //FAT表中包含簇N的扇區數 = 保留扇區數 + (簇N位於FAT表的位置/每扇區字節數)
ThisFATEntOffset = REM (FATOffset / BPB_BytesPerSec);
這裏要注意,簇的概念,既然FAT分區中存儲數據的最小單位是簇,那麼如今就是以簇來看待FAT表,對於FAT16來講,每一個簇號佔據兩個字節,因此簇N前的字節數就是(N-1+1)*2,也就是N*2,N*2/每扇區的字節數就能夠獲得簇N前的扇區數,再加上保留扇區數,就獲得簇號在FAT表中的位置。
例如:仍然以U盤的BPB爲例。如今想要知道簇128位於FAT表中的什麼位置。則按照以上的計算公式,有:
該FAT扇區號=保留扇區+(FAT偏移地址N*2/每扇區字節數)=4+(128×2/512)=4。這與實際結果吻合(看winhex的info panel)。
REM(…)爲求餘符號,就是求FATOffset除以BPB_BytesPerSec的餘數。ThisFATSecNum是FAT表中包含簇N的扇區數,若是你想獲得第二個FAT表中的扇區數,只要加上FATSz(FAT表大小)就是了,若是想獲得第三個FAT表中的扇區數,只須要加上FATSz × 2,依此類推。
如今你獲得扇區數ThisFATSecNum(記住這是針對FAT卷扇區0的偏移量),假設把該值讀入到一個指定的8-bit SecBuff,同時假定數據類型WORD是一個16-bit的帶符號類型,而DWORD是一個32-bit的無符號類型。
If(FATType == FAT16)
FAT16ClusEntryVal = *((WORD * ) & SecBuff[ThisFATEntOffset]);// 返回FAT16類型下ThisFATEntOffset的內容。
Else
FAT32ClusEntryVal = (*((DWORD *) & SecBuff[ThisFATEntOffset])) & 0x0FFFFFFF; //返回FAT32類型下ThisFATEntOffset的內容。
這裏要注意:&是取址運算符。運算方向從右到左。加*號後變成指針變量,字節型被強制轉換成WORD型。這樣,對於FAT16,能夠讀出一個字的內容,而對於FAT32,能夠讀出雙字——32位。
取得該扇區的內容。
設置該扇區的值使用以下算式:
If(FATType == FAT16)
*((WORD *) & SecBuff[ThisFATEntOffset]) = FAT16ClusEntryVal;
Else {
FAT32ClusEntryVal = FAT32ClusEntryVal & 0x0FFFFFFF; //捨棄高4位,實際上FAT32的FAT表項被使用的只有28位。
*((DWORD *) & SecBuff[ThisFATEntOffset]) = (*((DWORD *) & SecBuff[ThisFATEntOffset])) & 0xF0000000;//取出高4位的內容,並清空低28bit的內容。
*((DWORD *) & SecBuff[ThisFATEntOffset]) = (*((DWORD *) & SecBuff[ThisFATEntOffset])) | FAT32ClusEntryVal; //放入低28位的內容。
}
咱們看看上述FAT代碼是如何工做的,實際上每一個FAT32的FAT表項只有28-bit可使用,它的高4位保留,這4位只有在被格式化的時候會被使用到,在格式化時整個FAT32單元的32bit都被設置爲0,包括高位的4-bit。
另外要說明的一點,這也是常常被混淆的地方,由於FAT32表項實際上被使用的只有28-bit而不是32-bit。好比,如下幾個FAT32簇的值爲0x10000000,0xF0000000和0x00000000都表示該簇爲空,由於程序忽略了高位4-bit的值。若是當前簇的值爲0x30000000,你想要把數值0x0FFFFFF7寫入當前簇來標記壞簇,那麼當你的操做結束後該簇的實際值爲0x3FFFFFF7,由於你必須捨去0x0FFFFFF7這個壞簇標記高位的4-bit。
由於BPB_BytesPerSec的值必定可以被2和4整除,對於FAT16/FAT32來講,你沒必要擔憂元素會超越扇區的邊界,但對於FAT12,你就必須當心了。
FAT12的代碼會顯得複雜一點,由於它每一個元素(簇號)佔1.5個字節(12-bit)。
If(FATType == FAT12)
FATOffset = N + (N/2); //注意等式並無乘以浮點數1.5,除以2的值四捨五入;
ThisFATSecNum = BPB_RsvdSecCnt + (FATOffset / BPB_BytesPerSec);
ThisFATEntOffset = REM(FATOffset / BPB_BytesPerSec);
如今咱們必須考慮扇區邊界的狀況。
If(ThisFATEntOffset == (BPB_BytesPerSec - 1))
{
}
如今咱們能夠像FAT16同樣使用WORD數據類型來對FAT12的數據進行讀取,但仍須要注意,若是簇號爲偶數,咱們取16-Bit中的低12-Bit,若是是奇數則取高12-Bit,若是簇號是奇數,則取高12-Bit(這是由於對於FAT12來講,簇號佔據12Bit,也就是說,要存儲兩個簇號,須要3個字節,回憶一下FAT16裏面的簇號排列,
如今一會兒取16位,即兩個字節,FAT12表裏的簇號也是從0開始的,第0簇和第1簇一共三個字節存放FAT12的起始標誌,從第2簇(第四個字節)開始存放FAT表項,如今假設要讀取第2簇的內容,ThisFATEntOffset的值爲3,也就是說從第三個字節開始讀取第2簇的內容,記住,簇號爲12bit,第3個字節存放這12bit的低8位,第4個字節的低4位存放這12bit的高4位。見下圖:
FAT12ClusEntryVal = *((WORD *) &SecBuff[ThisFATEntOffset]);
If(N & 0x0001)
FAT12ClusEntryVal = FAT12ClusEntryVal >> 4;
Else
FAT12ClusEntryVal = FAT12ClusEntryVal & 0x0FFF;
設置簇的值按照下面的代碼進行:
If(N & 0x0001) {
FAT12ClusEntryVal = FAT12ClusEntryVal << 4;
*((WORD *) &SecBuff[ThisFATEntOffset]) =
(*((WORD *) &SecBuff[ThisFATEntOffset])) & 0x000F;
}
Else {
FAT12ClusEntryVal = FAT12ClusEntryVal & 0x0FFF;
*((WORD *) &SecBuff[ThisFATEntOffset]) =
(*((WORD *) &SecBuff[ThisFATEntOffset])) & 0xF000;
}
*((WORD *) &SecBuff[ThisFATEntOffset]) =
(*((WORD *) &SecBuff[ThisFATEntOffset])) | FAT12ClusEntryVal;
NOTE:這裏的>>爲右移操做符,並往高4位填充0;<<爲左移操做符,同時低4位用0填充。數據區中的文件是按照如下方式與FAT表相對應的:數據區中文件存放的第一個簇號被記錄在目錄項中,文件就是根據這個簇號與FAT表相關聯,數據區中文件的位置由前面討論的FirstSectorofCluster來計算。
對於一個大小爲0的文件——一個沒有數據的文件——在目錄項中分配的第一個簇的簇號爲0,此簇(參見前面討論的ThisFATSecNum和ThisFATEntryOffset)的內容要麼是一個EOC標記(End Of Cluster chain簇鏈表結束標記)要麼就是該文件的下一個簇的簇號。EOC的值和FAT類型有關(假設FATContent是須要檢測看是否包含EOC標記的簇的內容)。
isEOF = FALSE;
if (FATType == FAT12) {
if(FATContent >= 0x0FF8)
isEOF = TRUE;
}
Else if (FATType == FAT16) {
if (FATContent >= 0xFFF8)
isEOF = TRUE;
}
Else if (FATType == FAT32) {
if (FATContent >= 0x0FFFFFF8)
isEOF = TRUE;
}
NOTE:包含EOC標記的簇屬於當前文件而且是當前文件的最後一個簇。Microsoft的操做系統設置EOC標記時FAT12使用0x0FFF,FAT16使用0xFFFF,FAT32使用0x0FFFFFFF,但有一些運行於Microsoft系統的工具並不使用這個值。
還有一個特殊的標記就是「壞簇(Bad Cluster)標記」,任何包含「壞簇」標記的簇都不該該被列入到剩餘簇的範疇內,這個「壞簇」標記對於FAT12是0x0FF7,FAT16是0xFFF7,FAT32是0xFFFFFF7。另外,這些壞簇看起來也像是丟失的簇——它們彷佛已經被分配出去,由於它們的值並不爲0,但同時它們又不屬於任何文件。磁盤修復程序必定要認出這些被標記爲壞簇標記的「丟失簇」,而且不去修改它們的內容。
NOTE:對於FAT12和FAT16而言,壞簇標記不多是某個已經分配簇的簇號,可是對於FAT32而言,0x0FFFFFF7就有多是某個簇的簇號,所以FAT32文件系統必須避免把0x0FFFFFF7這個數字分配出去做爲某個簇的簇號。
FAT表中剩餘簇的列表就是卷中全部內容爲0的簇的列表。這些數據必須儘早取得並記錄下來以表示剩餘簇是已經被使用的。這個列表並無存儲在卷的任何一個地方,它必須在系統掛上(mount)該卷時,由FAT掃描程序得到內容爲0的簇的列表。FAT32的BPB_FSInfo扇區可能會包含剩餘簇的數量,請參閱FAT32關於BPB_FSInfo扇區的討論部分。
在FAT捲起始部分的兩個保留扇區究竟是作什麼用的呢?第一個保留簇FAT[0],它的低位8-bit爲BPB_Media,剩餘的位用1填充,好比BPB_Media的內容爲0xF8,那麼FAT12的內容爲0x0FF8,FAT16爲0xFFF8,FAT32爲0x0FFFFFF8,第二個保留簇FAT[1]在格式化的時候被填充EOC標記。FAT12卷此域不用,其值始終爲EOC標記。FAT16和FAT32此域的高2bit能夠被用於標記磁盤是否爲「髒」(下面描述),剩餘的位均用「1」填充。請注意FAT16和FAT32這兩位的位置是不同的,由於是高位2-bit。
對於FAT16:
ClnShutBitMask = 0x8000;
HrdErrBitMask = 0x4000;
對於FAT32:
ClnShutBitMask = 0x08000000;
HrdErrBitMask = 0x04000000;
Bit ClnShutBitMask : 若是此位爲1,那麼卷是「乾淨」的。若是爲0,那麼此卷是「髒」的。這意味着系統在上次卸載(unmount)此卷時,沒有正常地斷開鏈接,此時建議使用ChkDsk/ScanDisk等工具來檢測磁盤是否有錯誤。
Bit HrdErrBitMask : 若是此位爲1,表示沒有發生磁盤讀/寫錯誤。若是爲0,表示系統在上次掛載該卷時,有發生過磁盤讀/寫錯誤,此時建議運行ChkDsk/Scandisk等工具來掃描磁盤表面看看是否有出現新的壞簇。
關於FAT表這裏還有兩個重要的問題:
1. FAT表的結束扇區不必定就是FAT表的最後一個扇區,FAT表的結尾扇區位於簇號爲CountofClusters + 1(參閱前面關於CountofClusters的計算式)的簇中。這個扇區未必在FAT表的最後面。FAT程序不該該嘗試着去訪問CountofClusters + 1之後的簇,FAT格式化程序應該把這個簇號後面全部的簇用0填充;
2. BPB_FATSz16(對於FAT32爲BPB_FATSz32)的值會比它實際須要的大,也就是說,在FAT表中可能有部分扇區沒有被使用。所以,FAT表的結束扇區都是由CountofClusters + 1來計算獲得,而不是使用BPB_FATSz16/32來計算。FAT程序不該該嘗試去訪問這些「額外」的扇區。FAT格式化程序應該把這些扇區用0來填充。
初始化FAT卷
讀到這裏,細心的讀者必定會發現一個有趣的問題,前面說過FAT的類型(FAT12,FAT16或是FAT32)是根據總的簇數來判別——而且數據區中最大可取得的扇區數由FAT表的大小來決定——那麼當一個磁盤尚未被格式化時,咱們沒法獲得這BPB數據,這時是如何檢測並計算出正確的值來放到BPB_SecPerClus和BPB_FATSz16或是BPB_FATSz32中呢?Microsoft的操做系統使用一些固定的值和表格配合一個巧妙的算法來完成這些工做。
Microsoft只在軟盤上使用FAT12文件系統,由於軟盤的種類不多,而且都有固定的參數,格式化時使用的是一張簡單的表:
「若是這是一張這種格式的軟盤,那麼它的BPB看起來就是這個樣子(If it is a floppy of this type, then the BPB look like this.)」
對於FAT12格式的計算相對簡單,全部寫到BPB中的值均可以在一張紙上用手計算出來的(固然得當心——Cluster的值始終小於4085),若是存儲介質的容量大於4M,那就別再麻煩FAT12了,只須要把BPB_SecPerClus的值改小一點,這個卷就成FAT16了。
本節如下部分描述如何驅動每扇區512字節的FAT卷。若是扇區大小不是這樣子的話,你將不能使用這個表格和算法。不一樣磁盤的扇區大小不一。這裏根據磁盤容量的大小來選擇一個「合適的值」來簡單區分FAT類型。若是磁盤容量小於該值就是FAT16;若是大於或等於該值,就是FAT32。Windows操做系統選擇該值爲512MB,任何小於512MB的卷都是FAT16卷,任何大於或等於512MB的卷都是FAT32卷。
這裏請特別注意,別過早下結論。
有不少FAT16卷的容量都大於512MB,由於有不少不一樣的方法能夠強制把磁盤格式化成FAT16格式而不是象默認的那樣格式化成FAT32格式,而且不一樣的FAT程序遵循不一樣的規定來格式化磁盤。這裏咱們討論的只是MS-DOS和Windows默認狀況下是如何處理未格式化的磁盤。這裏有兩個表格,一個用於FAT16,一個用於FAT32。這個表格其中的一項是根據每扇區512字節的磁盤空間大小計算而來(該數值將寫入到BPB_TotSec16或是BPB_TotSec32中),還有一個值用來設置BPB_SecPerClus。
struct DSKSZTOSECPERCLUS {
DWORD DiskSize;
BYTE SecPerClusVal;
};
DSKSZTOSECPERCLUS DskTableFAT16 [ ] = {
{ 8400, 0 }, / *磁盤容量最大爲4.1MB,SecPerClusVal的值爲0表示這是一個錯誤 */
{ 32680, 2 }, / *磁盤容量最大爲16MB,1K Cluster */
{ 262144, 4 }, / *磁盤容量最大爲128MB,2K Cluster */
{ 524288, 8 }, / *磁盤容量最大爲256MB,4K Cluster */
{ 1048576, 16 }, / *磁盤容量最大爲512MB,8K Cluster */
/ *除非強制使用FAT16,不然如下數據不使用 */
{ 2097152, 32 }, / *磁盤容量最大爲1GB,16K Cluster */
{ 4194304, 64 }, / *磁盤容量最大爲2GB,32K Cluster */
{ 0xFFFFFFFF, 0 } / *磁盤容量超過2GB,SecPerClusVal的值爲0意味着這是一個錯誤 */
};
DSKSZTOSECPERCLUS DskTableFAT32 [ ] = {
{ 66600, 0 }, / *磁盤容量最大爲32.5MB,SecPerClusVal的值爲0表示這是一個錯誤 */
{ 532480, 1 }, / *磁盤容量最大爲260MB,5K Cluster */
{ 16777216, 8 }, / *磁盤容量最大爲8GB,4K Cluster */
{ 33554432, 16 }, / *磁盤容量最大爲16GB,8K Cluster */
{ 67108864, 32 }, / *磁盤容量最大爲32GB,16K Cluster */
{ 0xFFFFFFFF, 64 }, / *磁盤容量超過了32GB,32K Cluster */
};
這樣,只要給出磁盤的大小和FAT的類型就能夠肯定BPB_SecPerClus的值,如今咱們惟一所缺乏的就是FATSz16和FATSz32的大小。這裏咱們假設BPB_RootEntCnt,BPB_RsvdSecCnt和BPB_NumFATs的值已經按照上面的約定正確地設置。同時咱們還假設DiskSize就是咱們要寫到BPB_TotSec32或是BPB_TotSec16的值。
RootDirSectors = ((BPB_RootEntCnt * 32) + (BPB_BytesPerSec - 1)) / BPB_BytesPerSec;
TmpVal1 = DskSize – (BPB_RsvdSecCnt + RootDirSectors);
TmpVal2 = (256 * BPB_SecPerClus) + BPB_NumFATs;
If (FATType == FAT32)
TmpVal2 = TmpVal2 / 2;
FATSz = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2;
If (FATType == FAT32) {
BPB_FATSz16 = 0;
BPB_FATSz32 = FATSz;
}
else {
BPB_FATSz16 = LOWORD(FATSz);
}
不要花費太多功夫去琢磨上面的代碼是如何工做的,這個算法的原理十分複雜,放在這裏的目的只是說明Microsoft的操做系統是如何實現磁盤初始化的。可是,上面的代碼並不完美,在一些偶然的狀況下,它會使FAT16的FATSz比實際須要的要多出2個扇區,或使FAT32的FATSz多8個扇區,不過它永遠不會使FATSz的值比實際須要的小。由於FATSz的值比實際的大並不會影響文件系統的正常工做,惟一的缺點就是形成不多一部分磁盤空間的浪費。因爲以上的代碼是如此的簡單可靠,這在很大程度上彌補了它的不足。
FAT32 FSInfo扇區結構和備份啓動扇區
FAT3的FAT表可能很是大,而不像FAT16的大小被限制在128K之內,或是FAT12的FAT表大小被限制在6K範圍之內同樣,所以,在FAT32的卷中存放着「最新」的剩餘簇的數量,在API函數想知道剩餘空間時(好比在DIR命令的最後顯示剩餘空間)沒必要立刻去計算該數值,FSInfo的扇區號存放在BPB_FSInfo中,對於Microsoft的操做系統此值爲1,下表是FSInfo的結構:
名稱 | Offset(byte) | 大小(Byte) | 描述 |
FSI_LeadSig | 0 | 4 | 值爲0x41615252,這個標記用來表示該扇區爲FSInfo扇區。 |
FSI_Reserved1 | 4 | 480 | 保留爲之後擴展使用,FAT32格式化程序應該把此域所有設置爲0,當前版本的FAT程序不能夠訪問該域。 |
FSI_StrucSig | 484 | 4 | 值爲0x61417272,更具體地代表該扇區已經被使用。 |
FSI_Free_Count | 488 | 4 | 保存最新的剩餘簇的數量,若是爲0xFFFFFFFF表示剩餘簇未知,須要從新計算,除此以外的其餘值均可以用,並且不要求十分精確,但必須保證其值≤磁盤全部的簇數。 |
FSI_Nxt_Free | 492 | 4 | 該域爲FAT驅動程序提供一條有利的線索,它告訴驅動程序應該從哪裏開始尋找剩餘簇,由於FAT32的FAT表可能很是龐大,若是已經分配的簇不少的話要從頭開始查找剩餘簇,這將耗費大量時間。一般這個值被設定爲驅動程序最後分配出去的簇號,若是值爲0xFFFFFFFF,那麼驅動程序必須從簇2開始查找,除此以外其餘的值均可以使用,固然,前提是這個值必須是合法的。 |
FSI_Reserved2 | 496 | 12 | 保留爲之後擴展使用,FAT32格式化程序應該把此域所有設置爲0,當前版本的FAT程序不能夠訪問該域。 |
FSI_TrailSig | 508 | 4 | 值爲0xAA550000,此結束標記用來表示這是一個FSInfo扇區,注意此域的高兩位偏移量爲510和511,這和啓動扇區在相同偏移處的標記是同樣的。 |
FAT32區別於FAT12/FAT16的另一個地方就是BPB_BkBootSec,FAT12/FAT16有可能因爲丟失啓動扇區的內容而使整個卷都沒法訪問,這是一個「單點錯誤(Single Point of Failure)」,爲了不這種嚴重的後果,FAT32引進了BPB_BkBootSec,FAT32在扇區號爲6的地方完整地拷貝了一份啓動扇區的備份,包括BPB的內容。
當啓動扇區的內容被損壞後,磁盤修復程序只須要把啓動備份扇區中的數據拷貝回啓動扇區便可,即便在啓動扇區被損壞的狀況下,磁盤驅動程序仍然能夠在更換硬盤以前正常訪問該卷。
在第二種狀況下——扇區0被損壞——這就是爲何BPB_BkBootSec的值爲何必須爲6的緣由,當扇區0不可讀時,不少不一樣的操做系統都是硬性檢查FAT32卷在扇區6的啓動備份。保存在扇區6中的是一個完整的啓動記錄。Microsoft的FAT32的「啓動扇區」其實是由3個長度爲512字節的扇區組成,在BPB_BkBootSec扇區開始的啓動備份中完整的包含了這3個扇區,FSInfo也在其中,雖然在備份中BPB_FSInfo的內容和0扇區中BPB_FSInfo所指向的是同一個FSInfo結構。
NOTE:這三個扇區和啓動扇區同樣也在偏移量爲510和511的地方包含標記0xAA55(參看前面的敘述)。
FAT目錄結構(FAT Directory Structure)
這裏咱們先忽略長目錄項的狀況,只討論短目錄項。
FAT目錄其實就是一個由32Bytes的線性表構成的「文件」。根目錄(root directory)是一個特殊的目錄,它存在於每個FAT卷中,對於FAT12/16,根目錄的扇區號是相對於該FAT卷第一個扇區(0扇區)的偏移量。
FirstDirRootSecNum = BPB_RsvdSecCnt + (BPB_NumFATs * BPB_FATsz16);
FAT32的根目錄由簇鏈組成,其扇區數是不肯定的,這點和普通文件相同,根目錄的第一個扇區號存儲在BPB_RootClus中,根目錄不一樣於其餘的目錄,沒有日期和時間戳,也沒有目錄名(「/」並非其目錄名),同時根目錄裏沒有「.」和「..」這兩個目錄項,根目錄另外一個特殊的地方在於,根目錄中有一個設置了ATTR_VOLUME_ID位(見下表)的文件,這個文件在整個FAT卷中是惟一的。
名稱 | Offset (Byte) | 大小(Byte) | 描述 |
DIR_Name | 0 | 11 | 短文件名 |
DIR_Attr | 11 | 1 | 文件屬性: ATTR_READ_ONLY 0x01 ATTR_HIDDEN 0x02 ATTR_SYSTEM 0x04 ATTR_VOLUME_ID 0x08 ATTR_DIRECTORY 0x10 ATTR_ARCHIVE 0x20 ATTR_LONG_NAME ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID 前兩個屬性位爲保留位,在文件建立時應該把這兩位設爲0,在之後的使用中不能讀寫和更改。 |
DIR_NTRes | 12 | 1 | 保留給Windows NT使用,在文件建立時設置該位爲0,在之後的使用中不能讀寫和更改。 |
DIR_CrtTimeTeenth | 13 | 1 | 文件建立時間的毫秒級時間戳,因爲DIR_CrtTime的精度爲2秒,因此此域的有效值在0-199之間。 |
DIR_CrtTime | 14 | 2 | 文件建立時間。 |
DIR_CrtData | 16 | 2 | 文件建立日期。 |
DIR_LastAccDate | 18 | 2 | 最後訪問日期,請注意並無最後訪問時間域,而只有日期,這日期是指文件被讀寫的日期,若是是寫,該日期還應該被寫到DIR_WrDate中。 |
DIR_FstClusHI | 20 | 2 | 該目錄項簇號的高位字(FAT12/16此位爲0) |
DIR_WrtTime | 22 | 2 | 最後寫的時間,文件建立被認做寫 |
DIR_WrtDate | 24 | 2 | 最後寫的日期,文件建立被認做寫 |
DIR_FstClusL0 | 26 | 2 | 該目錄項簇號的低位字 |
DIR_FileSize | 28 | 2 | 文件大小,由32-bit雙字組成 |
DIR_Name[0]
此處特別註釋目錄項的第一個字節(DIR_Name[0])。
●若是DIR_Name[0] == 0xE5,則此目錄爲空(目錄項不包含文件和目錄)
●若是DIR_Name[0] == 0x00,則此目錄爲空(同0xE5),而且此後的再也不分配有目錄項(此後全部的DIR_Name[0]均爲0)。
不一樣於0xE5,若是DIR_Name[0]的值爲0,那麼FAT程序將不會再去檢測其後續的磁盤空間,由於這些空間都是空閒的。
●若是DIR_Name[0] == 0x05,則文件名在該位的實際值爲0xE5,但0xE5是日文中合法的字符,當須要用0xE5來做爲DIR_Name[0]時使用0x05來代替,避免程序誤認爲該目錄項爲空。
DIR_Name域實際由兩部分組成:8個字符的主文件名和3個字符的擴展名。兩部分若是字符數不夠的話用空格(0x20)填充(Trailing Space Padded)。
DIR_Name[0]不容許爲0x20,主文件名和擴展文件名的間隔「.」並不真實存在於DIR_Name中,小寫字母不容許出如今DIR_Name中(這些字符由於不一樣的國家和地區而異)。
●如下字符不容許出如今DIR_Name中的任何位置:
0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D還有0x7C。
如下是一些例子顯示用戶的輸入文件名如何與DIR_Name對應:
「foo.bar」 → 「FOO BAR」
「FOO.BAR」 → 「FOO BAR」
「Foo.Bar" → 「FOO BAR」
「foo」 → 「FOO」
「PICKLE.A」 → 「PICKLE A」
「prettybg.big」 → 「PRETTYBGBIG」
「.big」 → 非法,DIR_Name[0]不能爲0x20。
在FAT的目錄中,全部的文件名都是惟一的,上面例子中的三個文件名看起來彷佛各有千秋,事實上它們都是相同的文件名,在同一個目錄中,只能有一個DIR_Name被設置爲「FOO BAR」。
文件屬性(DIR_Attr):
ATTR_READ_ONLY:對這個文件的寫操做將會失敗。
ATTR_HIDDEN:正常模式顯示該目錄列表時不顯示該文件。
ATTR_SYSTEM:是系統文件。
ATTR_VOLUME_ID:在一個FAT卷中,只能有一個「文件」設置此位,而且該文件必須在根目錄中,該文件的文件名實際上就是該卷的卷標,而且該文件的DIR_FstClusHI和DIR_FstClusL0必須爲0(卷標文件不分配空間)。
ATTR_DIRECTORY:目錄
ATTR_ARCHIEVE:此屬性用於支持一些備份程序,當文件建立,更名或寫入時,FAT文件系統會設置此位,備份程序能夠利用此位來判斷卷中的哪些程序從上次備份到如今有更改過。 另外,ATTR_LONG_NAME位實際上代表該「文件」實際上爲另一個有長文件名的文件的一部分,在下一個章節咱們將詳細討論長文件名的情形。 建立一個目錄時,該「文件」的ATTR_DIRECTORY位被置位的同時DIR_FileSize被設置爲0,ATTR_DIRECTORY被置位的文件不使用DIR_FileSize而且該域一般爲0(目錄所佔空間爲其起始簇到EOC結束的簇鏈所佔的空間),每一個目錄項分配一個簇(除非是FAT12/FAT16的根目錄),將DIR_FstClusL0和DIR_FstClusHI的值設置爲該簇的簇號,而後在FAT表中爲該簇設置一個EOC標誌,並把該簇的每個字節設置爲0,若是這是根目錄,那麼你的工做就完成了(根目錄沒有「.」和「..」)。不然,你必須在該目錄空間(就是剛剛分配的那個簇)的頭兩個32-byte 建立兩個特殊的目錄項。 第一個目錄項的DIR_Name設置爲: 「. 」 「.. 」 咱們稱這兩個目錄爲「點」和「點點」,這兩個目錄的DIR_FileSize均設置爲0,同時兩個目錄的時間和日期和日期標誌也設置爲以包含它們自己的目錄相同,將「.」目錄項的DIR_ClusL0和DIR_ClusHI設置爲與它自身所在的目錄(包含這兩個「.」和「..」目錄項的目錄)相同。 最後把「..」目錄項的DIR_ClusL0和CLUS_ClusHI設置爲與剛剛建立目錄項所在目錄的第一個簇號(若是剛建立的目錄在根目錄則這些值爲0,FAT32也是如此)。 「.」和「..」的特徵能夠歸納以下: 「.」目錄指向該目錄自己; 「..」目錄指向該目錄的上級目錄。 還記得DOS操做系統嗎?要返回上級目錄須要輸入CD .. 這裏的「..」就是上級目錄的意思,這個指令的意思就是進入當前目錄的上級目錄。