|
作者:zty,來源:www.,發(fā)布時間:13/05/14 一、硬盤的物理結構:
硬盤存儲數據是根據電、磁轉換原理實現的。硬盤由一個或幾個表面鍍有磁性物質的金屬或玻璃等物質盤片以及盤片兩面所安裝的磁頭和相應的控制電路組成(圖1),其中盤片和磁頭密封在無塵的金屬殼中。 硬盤工作時,盤片以設計轉速高速旋轉,設置在盤片表面的磁頭則在電路控制下徑向移動到指定位置然后將數據存儲或讀取出來。當系統(tǒng)向硬盤寫入數據時,磁頭中“寫數據”電流產生磁場使盤片表面磁性物質狀態(tài)發(fā)生改變,并在寫電流磁場消失后仍能保持,這樣數據就存儲下來了;當系統(tǒng)從硬盤中讀數據時,磁頭經過盤片指定區(qū)域,盤片表面磁場使磁頭產生感應電流或線圈阻抗產生變化,經相關電路處理后還原成數據。因此只要能將盤片表面處理得更平滑、磁頭設計得更精密以及盡量提高盤片旋轉速度,就能造出容量更大、讀寫數據速度更快的硬盤。這是因為盤片表面處理越平、轉速越快就能越使磁頭離盤片表面越近,提高讀、寫靈敏度和速度;磁頭設計越小越精密就能使磁頭在盤片上占用空間越小,使磁頭在一張盤片上建立更多的磁道以存儲更多的數據。 二、硬盤的邏輯結構。 硬盤由很多盤片(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ū)。這三個參數即是硬盤的物理參數。 三、磁盤引導原理。 3.1 MBR(master boot record)扇區(qū): 計算機在按下power鍵以后,開始執(zhí)行主板bios程序。進行完一系列檢測和配置以后。開始按bios中設定的系統(tǒng)引導順序引導系統(tǒng)。假定現在是硬盤。Bios執(zhí)行完自己的程序后如何把執(zhí)行權交給硬盤呢。交給硬盤后又執(zhí)行存儲在哪里的程序呢。其實,稱為mbr的一段代碼起著舉足輕重的作用。MBR(master boot record),即主引導記錄,有時也稱主引導扇區(qū)。位于整個硬盤的0柱面0磁頭1扇區(qū)(可以看作是硬盤的第一個扇區(qū)),bios在執(zhí)行自己固有的程序以后就會jump到mbr中的第一條指令。將系統(tǒng)的控制權交由mbr來執(zhí)行。在總共512byte的主引導記錄中,MBR的引導程序占了其中的前446個字節(jié)(偏移0H~偏移1BDH),隨后的64個字節(jié)(偏移1BEH~偏移1FDH)為DPT(Disk PartitionTable,硬盤分區(qū)表),最后的兩個字節(jié)“55 AA”(偏移1FEH~偏移1FFH)是分區(qū)有效結束標志。 MBR不隨操作系統(tǒng)的不同而不同,意即不同的操作系統(tǒng)可能會存在相同的MBR,即使不同,MBR也不會夾帶操作系統(tǒng)的性質。具有公共引導的特性。 下面是用winhex查看的一塊希捷120GB硬盤的mbr。
你的硬盤的MBR引導代碼可能并非這樣。不過即使不同,所執(zhí)行的功能大體是一樣的。這是wowocock關于磁盤mbr的反編譯,已加了詳細的注釋,感興趣可以細細研究一下。 操作系統(tǒng)為了便于用戶對磁盤的管理,加入了磁盤分區(qū)的概念。即將一塊磁盤邏輯劃分為幾塊。磁盤分區(qū)數目的多少只受限于C~Z的英文字母的數目,在上圖DPT共64個字節(jié)中如何表示多個分區(qū)的屬性呢?microsoft通過鏈接的方法解決了這個問題。在DPT共64個字節(jié)中,以16個字節(jié)為分區(qū)表項單位描述一個分區(qū)的屬性。也就是說,第一個分區(qū)表項描述一個分區(qū)的屬性,一般為基本分區(qū)。第二個分區(qū)表項描述除基本分區(qū)外的其余空間,一般而言,就是我們所說的擴展分區(qū)。這部分的大體說明見表1。
注:上表中的超過1字節(jié)的數據都以實際數據顯示,就是按高位到地位的方式顯示。存儲時是按低位到高位存儲的。兩者表現不同,請仔細看清楚。以后出現的表,圖均同。 也可以在winhex中看到這些參數的意義:
說明:每個分區(qū)表項占用16個字節(jié),假定偏移地址從0開始。如圖3的分區(qū)表項3。分區(qū)表項4同分區(qū)表項3。 1、0H偏移為活動分區(qū)是否標志,只能選00H和80H。80H為活動,00H為非活動。其余值對microsoft而言為非法值。 2、重新說明一下(這個非常重要):大于1個字節(jié)的數被以低字節(jié)在前的存儲格式格式(little endian format)或稱反字節(jié)順序保存下來。低字節(jié)在前的格式是一種保存數的方法,這樣,最低位的字節(jié)最先出現在十六進制數符號中。例如,相對扇區(qū)數字段的值0x3F000000的低字節(jié)在前表示為0x0000003F。這個低字節(jié)在前的格式數的十進制數為63。 3、系統(tǒng)在分區(qū)時,各分區(qū)都不允許跨柱面,即均以柱面為單位,這就是通常所說的分區(qū)粒度。有時候我們分區(qū)是輸入分區(qū)的大小為7000M,分出來卻是6997M,就是這個原因。 偏移2H和偏移6H的扇區(qū)和柱面參數中,扇區(qū)占6位(bit),柱面占10位(bit),以偏移6H為例,其低6位用作扇區(qū)數的二進制表示。其高兩位做柱面數10位中的高兩位,偏移7H組成的8位做柱面數10位中的低8位。由此可知,實際上用這種方式表示的分區(qū)容量是有限的,柱面和磁頭從0開始編號,扇區(qū)從1開始編號,所以最多只能表示1024個柱面×63個扇區(qū)×256個磁頭×512byte=8455716864byte。即通常的8.4GB(實際上應該是7.8GB左右)限制。實際上磁頭數通常只用到255個(由匯編語言的尋址寄存器決定),即使把這3個字節(jié)按線性尋址,依然力不從心。 在后來的操作系統(tǒng)中,超過8.4GB的分區(qū)其實已經不通過C/H/S的方式尋址了。而是通過偏移CH~偏移FH共4個字節(jié)32位線性扇區(qū)地址來表示分區(qū)所占用的扇區(qū)總數??芍ㄟ^4個字節(jié)可以表示2^32個扇區(qū),即2TB=2048GB,目前對于大多數計算機而言,這已經是個天文數字了。在未超過8.4GB的分區(qū)上,C/H/S的表示方法和線性扇區(qū)的表示方法所表示的分區(qū)大小是一致的。也就是說,兩種表示方法是協調的。即使不協調,也以線性尋址為準。(可能在某些系統(tǒng)中會提示出錯)。超過8.4GB的分區(qū)結束C/H/S一般填充為FEH FFH FFH。即C/H/S所能表示的最大值。有時候也會用柱面對1024的模來填充。不過這幾個字節(jié)是什么其實都無關緊要了。 雖然現在的系統(tǒng)均采用線性尋址的方式來處理分區(qū)的大小。但不可跨柱面的原則依然沒變。本分區(qū)的扇區(qū)總數加上與前一分區(qū)之間的保留扇區(qū)數目依然必須是柱面容量的整數倍。(保留扇區(qū)中的第一個扇區(qū)就是存放分區(qū)表的MBR或虛擬MBR的扇區(qū),分區(qū)的扇區(qū)總數在線性表示方式上是不計入保留扇區(qū)的。如果是第一個分區(qū),保留扇區(qū)是本分區(qū)前的所有扇區(qū)。 附:分區(qū)表類型標志如圖4
3.2 擴展分區(qū): 擴展分區(qū)中的每個邏輯驅動器都存在一個類似于MBR的擴展引導記錄( Extended Boot Record, EBR),也有人稱之為虛擬mbr或擴展mbr,意思是一樣的。擴展引導記錄包括一個擴展分區(qū)表和該扇區(qū)的標簽。擴展引導記錄將記錄只包含擴展分區(qū)中每個邏輯驅動器的第一個柱面的第一面的信息。一個邏輯驅動器中的引導扇區(qū)一般位于相對扇區(qū)32或63。但是,如果磁盤上沒有擴展分區(qū),那么就不會有擴展引導記錄和邏輯驅動器。第一個邏輯驅動器的擴展分區(qū)表中的第一項指向它自身的引導扇區(qū)。第二項指向下一個邏輯驅動器的EBR。如果不存在進一步的邏輯驅動器,第二項就不會使用,而且被記錄成一系列零。如果有附加的邏輯驅動器,那么第二個邏輯驅動器的擴展分區(qū)表的第一項會指向它本身的引導扇區(qū)。第二個邏輯驅動器的擴展分區(qū)表的第二項指向下一個邏輯驅動器的EBR。擴展分區(qū)表的第三項和第四項永遠都不會被使用。 通過一幅4分區(qū)的磁盤結構圖可以看到磁盤的大致組織形式。如圖5:
關于擴展分區(qū),如圖6所示,擴展分區(qū)中邏輯驅動器的擴展引導記錄是一個連接表。該圖顯示了一個擴展分區(qū)上的三個邏輯驅動器,說明了前面的邏輯驅動器和最后一個邏輯驅動器之間在擴展分區(qū)表中的差異。
除了擴展分區(qū)上最后一個邏輯驅動器外,表2中所描述的擴展分區(qū)表的格式在每個邏輯驅動器中都是重復的:第一個項標識了邏輯驅動器本身的引導扇區(qū),第二個項標識了下一個邏輯驅動器的EBR。最后一個邏輯驅動器的擴展分區(qū)表只會列出它本身的分區(qū)項。最后一個擴展分區(qū)表的第二個項到第四個項被使用。 擴展分區(qū)表項中的相對扇區(qū)數字段所顯示的是從擴展分區(qū)開始到邏輯驅動器中第一個扇區(qū)的位移的字節(jié)數??偵葏^(qū)數字段中的數是指組成該邏輯驅動器的扇區(qū)數目??偵葏^(qū)數字段的值等于從擴展分區(qū)表項所定義的引導扇區(qū)到邏輯驅動器末尾的扇區(qū)數。 四、FAT分區(qū)原理。 先來一幅結構圖:
現在我們著重研究FAT格式分區(qū)內數據是如何存儲的。FAT分區(qū)格式是MICROSOFT最早支持的分區(qū)格式,依據FAT表中每個簇鏈的所占位數(有關概念,后面會講到)分為fat12、fat16、fat32三種格式“變種”,但其基本存儲方式是相似的。 仔細研究圖7中的fat16和fat32分區(qū)的組成結構。下面依次解釋DBR、FAT1、FAT2、根目錄、數據區(qū)、剩余扇區(qū)的概念。提到的地址如無特別提示均為分區(qū)內部偏移。 4.1 關于DBR. DBR區(qū)(DOS BOOT RECORD)即操作系統(tǒng)引導記錄區(qū)的意思,通常占用分區(qū)的第0扇區(qū)共512個字節(jié)(特殊情況也要占用其它保留扇區(qū),我們先說第0扇)。在這512個字節(jié)中,其實又是由跳轉指令,廠商標志和操作系統(tǒng)版本號,BPB(BIOS Parameter Block),擴展BPB,os引導程序,結束標志幾部分組成。 以用的最多的FAT32為例說明分區(qū)DBR各字節(jié)的含義。見圖8。
圖8的對應解釋見表3
圖9給出了winhex對圖8 DBR的相關參數解釋:
根據上邊圖例,我們來討論DBR各字節(jié)的參數意義。 MBR將CPU執(zhí)行轉移給引導扇區(qū),因此,引導扇區(qū)的前三個字節(jié)必須是合法的可執(zhí)行的基于x86的CPU指令。這通常是一條跳轉指令,該指令負責跳過接下來的幾個不可執(zhí)行的字節(jié)(BPB和擴展BPB),跳到操作系統(tǒng)引導代碼部分。 跳轉指令之后是8字節(jié)長的OEM ID,它是一個字符串,OEM ID標識了格式化該分區(qū)的操作系統(tǒng)的名稱和版本號。為了保留與MS-DOS的兼容性,通常Windows 2000格式化該盤是在FAT16和FAT32磁盤上的該字段中記錄了“MSDOS 5.0”,在NTFS磁盤上(關于ntfs,另述),Windows 2000記錄的是“NTFS”。通常在被Windows 95格式化的磁盤上OEM ID字段出現“MSWIN4.0”,在被Windows 95 OSR2和Windows 98格式化的磁盤上OEM ID字段出現“MSWIN4.1”。 接下來的從偏移0x0B開始的是一段描述能夠使可執(zhí)行引導代碼找到相關參數的信息。通常稱之為BPB(BIOS Parameter Block),BPB一般開始于相同的位移量,因此,標準的參數都處于一個已知的位置。磁盤容量和幾何結構變量都被封在BPB之中。由于引導扇區(qū)的第一部分是一個x86跳轉指令。因此,將來通過在BPB末端附加新的信息,可以對BPB進行擴展。只需要對該跳轉指令作一個小的調整就可以適應BPB的變化。圖9已經列出了項目的名稱和取值,為了系統(tǒng)的研究,針對圖8,將FAT32分區(qū)格式的BPB含義和擴展BPB含義釋義為表格,見表4和表5。
DBR的偏移0x5A開始的數據為操作系統(tǒng)引導代碼。這是由偏移0x00開始的跳轉指令所指向的。在圖8所列出的偏移0x00~0x02的跳轉指令“EB 58 90”清楚地指明了OS引導代碼的偏移位置。jump 58H加上跳轉指令所需的位移量,即開始于0x5A。此段指令在不同的操作系統(tǒng)上和不同的引導方式上,其內容也是不同的。大多數的資料上都說win98,構建于fat基本分區(qū)上的win2000,winxp所使用的DBR只占用基本分區(qū)的第0扇區(qū)。他們提到,對于fat32,一般的32個基本分區(qū)保留扇區(qū)只有第0扇區(qū)是有用的。實際上,以FAT32構建的操作系統(tǒng)如果是win98,系統(tǒng)會使用基本分區(qū)的第0扇區(qū)和第2扇區(qū)存儲os引導代碼;以FAT32構建的操作系統(tǒng)如果是win2000或winxp,系統(tǒng)會使用基本分區(qū)的第0扇區(qū)和第0xC扇區(qū)(win2000或winxp,其第0xC的位置由第0扇區(qū)的0xAB偏移指出)存儲os引導代碼。所以,在fat32分區(qū)格式上,如果DBR一扇區(qū)的內容正確而缺少第2扇區(qū)(win98系統(tǒng))或第0xC扇區(qū)(win2000或winxp系統(tǒng)),系統(tǒng)也是無法啟動的。如果自己手動設置NTLDR雙系統(tǒng),必須知道這一點。 DBR扇區(qū)的最后兩個字節(jié)一般存儲值為0x55AA的DBR有效標志,對于其他的取值,系統(tǒng)將不會執(zhí)行DBR相關指令。上面提到的其他幾個參與os引導的扇區(qū)也需以0x55AA為合法結束標志。 FAT16 DBR: FAT32中DBR的含義大致如此,對于FAT12和FAT16其基本意義類似,只是相關偏移量和參數意義有小的差異,FAT格式的區(qū)別和來因,以后會說到,此處不在多說FAT12與FAT16。我將FAT16的扇區(qū)參數意義列表。感興趣的朋友自己研究一下,和FAT32大同小異的。
4.2 關于保留扇區(qū) 在上述FAT文件系統(tǒng)DBR的偏移0x0E處,用2個字節(jié)存儲保留扇區(qū)的數目。所謂保留扇區(qū)(有時候會叫系統(tǒng)扇區(qū),隱藏扇區(qū)),是指從分區(qū)DBR扇區(qū)開始的僅為系統(tǒng)所有的扇區(qū),包括DBR扇區(qū)。在FAT16文件系統(tǒng)中,保留扇區(qū)的數據通常設置為1,即僅僅DBR扇區(qū)。而在FAT32中,保留扇區(qū)的數據通常取為32,有時候用Partition Magic分過的FAT32分區(qū)會設置36個保留扇區(qū),有的工具可能會設置63個保留扇區(qū)。 FAT32中的保留扇區(qū)除了磁盤總第0扇區(qū)用作DBR,總第2扇區(qū)(win98系統(tǒng))或總第0xC扇區(qū)(win2000,winxp)用作OS引導代碼擴展部分外,其余扇區(qū)都不參與操作系統(tǒng)管理與磁盤數據管理,通常情況下是沒作用的。操作系統(tǒng)之所以在FAT32中設置保留扇區(qū),是為了對DBR作備份或留待以后升級時用。FAT32中,DBR偏移0x34占2字節(jié)的數據指明了DBR備份扇區(qū)所在,一般為0x06,即第6扇區(qū)。當FAT32分區(qū)DBR扇區(qū)被破壞導致分區(qū)無法訪問時??梢杂玫?扇區(qū)的原備份替換第0扇區(qū)來找回數據。 4.3 FAT表和數據的存儲原則。 FAT表(File Allocation Table 文件分配表),是Microsoft在FAT文件系統(tǒng)中用于磁盤數據(文件)索引和定位引進的一種鏈式結構。假如把磁盤比作一本書,FAT表可以認為相當于書中的目錄,而文件就是各個章節(jié)的內容。但FAT表的表示方法卻與目錄有很大的不同。 在FAT文件系統(tǒng)中,文件的存儲依照FAT表制定的簇鏈式數據結構來進行。同時,FAT文件系統(tǒng)將組織數據時使用的目錄也抽象為文件,以簡化對數據的管理。 ★存儲過程假想: 我們模擬對一個分區(qū)存儲數據的過程來說明FAT文件系統(tǒng)中數據的存儲原則。 假定現在有一個空的完全沒有存放數據的磁盤,大小為100KB,我們將其想象為線形的空間地址。為了存儲管理上的便利,我們人為的將這100KB的空間均分成100份,每份1KB。我們來依次存儲這樣幾個文件:A.TXT(大小10KB),B.TXT(大小53.6KB),C.TXT(大小20.5KB)。 最起碼能夠想到,我們可以順序的在這100KB空間中存放這3個文件。同時不要忘了,我們還要記下他們的大小和開始的位置,這樣下次要用時才能找的到,這就像是目錄。為了便于查找,我們假定用第1K的空間來存儲他們的特征(屬性)。還有,我們設計的存儲單位是1KB,所以,A.TXT我們需要10個存儲單位(為了說明方便,我們把存儲單位叫做“簇”吧。也能少打點字,呵呵。),B.TXT需要54個簇,C.TXT需要21個簇??赡苡腥藭fB.TXT和C.TXT不是各自浪費了不到1簇的空間嗎?干嘛不讓他們緊挨著,不是省地方嗎?我的回答是,如果按照這樣的方式存儲,目錄中原本只需要記下簇號,現在還需要記下簇內的偏移,這樣會增加目錄的存儲量,而且存取沒有了規(guī)則,讀取也不太方便,是得不償失的。 根據上面所說的思想,我們設計了這樣的圖4.3.1所示的存儲方式。 ![]() 我們再考慮如何來寫這三個文件的目錄。對于每個文件而言,一定要記錄的有:文件名,開始簇,大小,創(chuàng)建日期、時間,修改日期、時間,文件的讀寫屬性等。這里大小能不能用結束簇來計算呢?一定不能,因為文件的大小不一定就是整數個簇的大小,否則的話像B.TXT的內容就是54KB的內容了,少了固然不行,可多了也是不行的。那么我們怎么記錄呢?可以想象一下。為了管理上的方便,我們用數據庫的管理方式來管理我們的目錄。于是我把1KB再分成10份,假定開始簇號為0,定義每份100B的各個位置的代表含義如圖4.3.2
這樣設計的結構絕對可以對文件進行正確的讀寫了。接著讓我們設計的文件系統(tǒng)工作吧。先改動個文件,比如A.TXT,增加點內容吧!咦?增加后往哪里放呀,雖然存儲塊的后面有很多空間,但緊隨其后B.TXT的數據還頂著呢?要是把A.TXT移到后邊太浪費處理資源,而且也不一定解決問題。這個問題看來暫時解決不了。 那我們換個操作,把B.txt刪了,b.txt的空間隨之釋放。這時候空間如圖4.3.3,目錄如圖4.3.4
這個操作看來還可以,我們接著做,在存入一個文件D.txt(大小為60.3KB),總共100簇的空間只用了31簇,還有68簇剩余,按說能放下??墒??往那里放呢?沒有61個連續(xù)的空間了,目錄行沒辦法寫了,看來無連續(xù)塊存儲暫時也不行。 你一定能夠想到我們可以在連續(xù)空間不夠或增加文件長度的時候轉移影響我們操作的其他文件,從而騰出空間來,但我要問你,那不是成天啥也不要干了,就是倒騰東西了嗎? 看來我們設計的文件系統(tǒng)有致命的漏洞,怎么解決呢?。。。。。。。。。。 其實可以這樣解決: 首先我們允許文件的不連續(xù)存儲。目錄中依然只記錄開始簇和文件的大小。那么我們怎么記錄文件占用那些簇呢,以文件映射簇不太方便,因為文件名是不固定的。我們換個思想,可以用簇來映射文件,在整個存儲空間的前部留下幾簇來記錄數據區(qū)中數據與簇號的關系。對于上例因為總空間也不大,所以用前部的1Kb的空間來記錄這種對應,假設3個文件都存儲,空間分配如圖4.3.5,同時修改一下目錄,如圖4.3.6
第一簇用來記錄數據區(qū)中每一簇的被占用情況,暫時稱其為文件分配表。結合文件分配表和文件目錄就可以達到完全的文件讀取了。我們想到,把文件分配表做成一個數據表,以圖4.3.7的形式記錄簇與數據的對應。 用圖4.3.7的組織方式是完全可以實現對文件占有簇的記錄的。但還不夠效率。比如文件名在文件分配表中記錄太多,浪費空間,而實際上在目錄中已經記錄了文件的開始簇了。所以可以改良一下,用鏈的方式來存放占有簇的關系,變成圖4.3.8的組織方式。
參照圖4.3.8來理解一下文件分配表的意義。如文件a.txt我們根據目錄項中指定的a.txt的首簇為2,然后找到文件分配表的第2簇記錄,上面登記的是3,我們就能確定下一簇是3。找到文件分配表的第3簇記錄,上面登記的是4,我們就能確定下一簇是4......直到指到第11簇,發(fā)現下一個指向是FF,就是結束。文件便絲毫無誤讀取完畢。 我們再看上面提到的第三種情況,就是將b.txt刪除以后,存入一個大小為60.3KB的d.txt。利用簇鏈可以很容易的實現。實現后的磁盤如圖4.3.9 4.3.10 ; 4.3.11
★FAT16存儲原理: 當把一部分磁盤空間格式化為fat文件系統(tǒng)時,fat文件系統(tǒng)就將這個分區(qū)當成整塊可分配的區(qū)域進行規(guī)劃,以便于數據的存儲。一般來講,其劃分形式如圖7所示。我們把FAT16部分提取出來,詳細描述一下: FAT16是Microsoft較早推出的文件系統(tǒng),具有高度兼容性,目前仍然廣泛應用于個人電腦尤其是移動存儲設備中,FAT16簡單來講由圖4.3.11所示的6部分組成(主要是前5部分)。引導扇區(qū)(DBR)我們已經說過,FAT16在DBR之后沒有留有任何保留扇區(qū),其后緊隨的便是FAT表。FAT表是FAT16用來記錄磁盤數據區(qū)簇鏈結構的。像前面我們說過的例子一樣,FAT將磁盤空間按一定數目的扇區(qū)為單位進行劃分,這樣的單位稱為簇。通常情況下,每扇區(qū)512字節(jié)的原則是不變的。簇的大小一般是2n(n為整數)個扇區(qū)的大小,像512B,1K,2K,4K,8K,16K,32K,64K。實際中通常不超過32K。 之所以簇為單位而不以扇區(qū)為單位進行磁盤的分配,是因為當分區(qū)容量較大時,采用大小為512b的扇區(qū)管理會增加fat表的項數,對大文件存取增加消耗,文件系統(tǒng)效率不高。分區(qū)的大小和簇的取值是有關系的,見表9。
注意:少于32680個扇區(qū)的分區(qū)中,簇空間大小可最多達到每個簇8個扇區(qū)。不管用戶是使用磁盤管理器來格式化分區(qū),還是使用命令提示行鍵入format命令格式化,格式化程序都創(chuàng)建一個12位的FAT。少于16MB的分區(qū),系統(tǒng)通常會將其格式化成12位的FAT,FAT12是FAT的初始實現形式,是針對小型介質的。FAT12文件分配表要比FAT16和FAT32的文件分配表小,因為它對每個條目使用的空間較少。這就給數據留下較多的空間。所有用FAT12格式化的5.25英寸軟盤以及1.44MB的3.5英寸軟盤都是由FAT12格式化的。除了FAT表中記錄每簇鏈結的二進制位數與FAT16不同外,其余原理與FAT16均相同,不再單獨解釋。。。 格式化FAT16分區(qū)時,格式化程序根據分區(qū)的大小確定簇的大小,然后根據保留扇區(qū)的數目、根目錄的扇區(qū)數目、數據區(qū)可分的簇數與FAT表本身所占空間來確定FAT表所需的扇區(qū)數目,然后將計算后的結果寫入DBR的相關位置。 FAT16 DBR參數的偏移0x11處記錄了根目錄所占扇區(qū)的數目。偏移0x16記錄了FAT表所占扇區(qū)的數據。偏移0x10記錄了FAT表的副本數目。系統(tǒng)在得到這幾項參數以后,就可以確定數據區(qū)的開始扇區(qū)偏移了。 FAT16文件系統(tǒng)從根目錄所占的32個扇區(qū)之后的第一個扇區(qū)開始以簇為單位進行數據的處理,這之前仍以扇區(qū)為單位。對于根目錄之后的第一個簇,系統(tǒng)并不編號為第0簇或第1簇 (可能是留作關鍵字的原因吧),而是編號為第2簇,也就是說數據區(qū)順序上的第1個簇也是編號上的第2簇。 FAT文件系統(tǒng)之所以有12,16,32不同的版本之分,其根本在于FAT表用來記錄任意一簇鏈接的二進制位數。以FAT16為例,每一簇在FAT表中占據2字節(jié)(二進制16位)。所以,FAT16最大可以表示的簇號為0xFFFF(十進制的65535),以32K為簇的大小的話,FAT32可以管理的最大磁盤空間為:32KB×65535=2048MB,這就是為什么FAT16不支持超過2GB分區(qū)的原因。 FAT表實際上是一個數據表,以2個字節(jié)為單位,我們暫將這個單位稱為FAT記錄項,通常情況其第1、2個記錄項(前4個字節(jié))用作介質描述。從第三個記錄項開始記錄除根目錄外的其他文件及文件夾的簇鏈情況。根據簇的表現情況FAT用相應的取值來描述,見表10
看一幅在winhex所截FAT16的文件分配表,圖10:
如圖,FAT表以"F8 FF FF FF" 開頭,此2字節(jié)為介質描述單元,并不參與FAT表簇鏈關系。小紅字標出的是FAT扇區(qū)每2字節(jié)對應的簇號。 相對偏移0x4~0x5偏移為第2簇(順序上第1簇),此處為FF,表示存儲在第2簇上的文件(目錄)是個小文件,只占用1個簇便結束了。 第3簇中存放的數據是0x0005,這是一個文件或文件夾的首簇。其內容為第5簇,就是說接下來的簇位于第5簇——〉 FAT表指引我們到達FAT表的第5簇指向,上面寫的數據是“FF FF”,意即此文件已至尾簇。 第4簇中存放的數據是0x0006,這又是一個文件或文件夾的首簇。其內容為第6簇,就是說接下來的簇位于第6簇——〉FAT表指引我們到達FAT表的第6簇指向,上面寫的數據是0x0007,就是說接下來的簇位于第7簇——〉FAT表指引我們到達FAT表的第7簇指向……直到根據FAT鏈讀取到扇區(qū)相對偏移0x1A~0x1B,也就是第13簇,上面寫的數據是0x000E,也就是指向第14簇——〉14簇的內容為"FF FF",意即此文件已至尾簇。 后面的FAT表數據與上面的道理相同。不再分析。 FAT表記錄了磁盤數據文件的存儲鏈表,對于數據的讀取而言是極其重要的,以至于Microsoft為其開發(fā)的FAT文件系統(tǒng)中的FAT表創(chuàng)建了一份備份,就是我們看到的FAT2。FAT2與FAT1的內容通常是即時同步的,也就是說如果通過正常的系統(tǒng)讀寫對FAT1做了更改,那么FAT2也同樣被更新。如果從這個角度來看,系統(tǒng)的這個功能在數據恢復時是個天災。 FAT文件系統(tǒng)的目錄結構其實是一顆有向的從根到葉的樹,這里提到的有向是指對于FAT分區(qū)內的任一文件(包括文件夾),均需從根目錄尋址來找到。可以這樣認為:目錄存儲結構的入口就是根目錄。 FAT文件系統(tǒng)根據根目錄來尋址其他文件(包括文件夾),故而根目錄的位置必須在磁盤存取數據之前得以確定。FAT文件系統(tǒng)就是根據分區(qū)的相關DBR參數與DBR中存放的已經計算好的FAT表(2份)的大小來確定的。格式化以后,跟目錄的大小和位置其實都已經確定下來了:位置緊隨FAT2之后,大小通常為32個扇區(qū)。根目錄之后便是數據區(qū)第2簇。 FAT文件系統(tǒng)的一個重要思想是把目錄(文件夾)當作一個特殊的文件來處理,FAT32甚至將根目錄當作文件處理(旁:NTFS將分區(qū)參數、安全權限等好多東西抽象為文件更是這個思想的升華),在FAT16中,雖然根目錄地位并不等同于普通的文件或者說是目錄,但其組織形式和普通的目錄(文件夾)并沒有不同。FAT分區(qū)中所有的文件夾(目錄)文件,實際上可以看作是一個存放其他文件(文件夾)入口參數的數據表。所以目錄的占用空間的大小并不等同于其下所有數據的大小,但也不等同于0。通常是占很小的空間的,可以看作目錄文件是一個簡單的二維表文件。其具體存儲原理是: 不管目錄文件所占空間為多少簇,一簇為多少字節(jié)。系統(tǒng)都會以32個字節(jié)為單位進行目錄文件所占簇的分配。這32個字節(jié)以確定的偏移來定義本目錄下的一個文件(或文件夾)的屬性,實際上是一個簡單的二維表。 這32個字節(jié)的各字節(jié)偏移定義如表11:
對圖10中的一些取值進行說明: (1)、對于短文件名,系統(tǒng)將文件名分成兩部分進行存儲,即主文件名+擴展名。0x0~0x7字節(jié)記錄文件的主文件名,0x8~0xA記錄文件的擴展名,取文件名中的ASCII碼值。不記錄主文件名與擴展名之間的"." 主文件名不足8個字符以空白符(20H)填充,擴展名不足3個字符同樣以空白符(20H)填充。0x0偏移處的取值若為00H,表明目錄項為空;若為E5H,表明目錄項曾被使用,但對應的文件或文件夾已被刪除。(這也是誤刪除后恢復的理論依據)。文件名中的第一個字符若為“.”或“..”表示這個簇記錄的是一個子目錄的目錄項?!?”代表當前目錄;“..”代表上級目錄(和我們在dos或windows中的使用意思是一樣的,如果磁盤數據被破壞,就可以通過這兩個目錄項的具體參數推算磁盤的數據區(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。得出的結果換算成16進制填入即可。也就是:0x16字節(jié)的0~4位是以2秒為單位的量值;0x16字節(jié)的5~7位和0x17字節(jié)的0~2位是分鐘;0x17字節(jié)的3~7位是小時。 (5)、0x18~0x19中的日期=(年份-1980)*512+月份*32+日。得出的結果換算成16進制填入即可。也就是:0x18字節(jié)0~4位是日期數;0x18字節(jié)5~7位和0x19字節(jié)0位是月份;0x19字節(jié)的1~7位為年號,原定義中0~119分別代表1980~2099,目前高版本的Windows允許取0~127,即年號最大可以到2107年。 (6)、0x1A~0x1B存放文件或目錄的表示文件的首簇號,系統(tǒng)根據掌握的首簇號在FAT表中找到入口,然后再跟蹤簇鏈直至簇尾,同時用0x1C~0x1F處字節(jié)判定有效性。就可以完全無誤的讀取文件(目錄)了。 (7)、普通子目錄的尋址過程也是通過其父目錄中的目錄項來指定的,與數據文件(指非目錄文件)不同的是目錄項偏移0xB的第4位置1,而數據文件為0。 對于整個FAT分區(qū)而言,簇的分配并不完全總是分配干凈的。如一個數據區(qū)為99個扇區(qū)的FAT系統(tǒng),如果簇的大小設定為2扇區(qū),就會有1個扇區(qū)無法分配給任何一個簇。這就是分區(qū)的剩余扇區(qū),位于分區(qū)的末尾。有的系統(tǒng)用最后一個剩余扇區(qū)備份本分區(qū)的DBR,這也是一種好的備份方法。 早的FAT16系統(tǒng)并沒有長文件名一說,Windows操作系統(tǒng)已經完全支持在FAT16上的長文件名了。FAT16的長文件名與FAT32長文件名的定義是相同的,關于長文件名,在FAT32部分再詳細作解釋。 ★FAT32存儲原理: FAT32是個非常有功勞的文件系統(tǒng),Microsoft成功地設計并運用了它,直到今天NTFS鋪天蓋地襲來的時候,FAT32依然占據著Microsoft Windows文件系統(tǒng)中重要的地位。FAT32最早是出于FAT16不支持大分區(qū)、單位簇容量大以致空間急劇浪費等缺點設計的。實際應用中,FAT32還是成功的。 FAT32與FAT16的原理基本上是相同的,圖4.3.12標出了FAT32分區(qū)的基本構成。
FAT32在格式化的過程中就根據分區(qū)的特點構建好了它的DBR,其中BPB參數是很重要的,可以回過頭來看一下表4和表5。首先FAT32保留扇區(qū)的數目默認為32個,而不是FAT16的僅僅一個。這樣的好處是有助于磁盤DBR指令的長度擴展,而且可以為DBR扇區(qū)留有備份空間。上面我們已經提到,構建在FAT32上的win98或win2000、winXP,其操作系統(tǒng)引導代碼并非只占一個扇區(qū)了。留有多余的保留扇區(qū)就可以很好的拓展OS引導代碼。在BPB中也記錄了DBR扇區(qū)的備份扇區(qū)編號。備份扇區(qū)可以讓我們在磁盤遭到意外破壞時恢復DBR。 FAT32的文件分配表的數據結構依然和FAT16相同,所不同的是,FAT32將記錄簇鏈的二進制位數擴展到了32位,故而這種文件系統(tǒng)稱為FAT32。32位二進制位的簇鏈決定了FAT表最大可以尋址2T個簇。這樣即使簇的大小為1扇區(qū),理論上仍然能夠尋址1TB范圍內的分區(qū)。但實際中FAT32是不能尋址這樣大的空間的,隨著分區(qū)空間大小的增加,FAT表的記錄數會變得臃腫不堪,嚴重影響系統(tǒng)的性能。所以在實際中通常不格式化超過32GB的FAT32分區(qū)。WIN2000及之上的OS已經不直接支持對超過32GB的分區(qū)格式化成FAT32,但WIN98依然可以格式化大到127GB的FAT32分區(qū),但這樣沒必要也不推薦。同時FAT32也有小的限制,FAT32卷必須至少有65527個簇,所以對于小的分區(qū),仍然需要使用FAT16或FAT12。 分區(qū)變大時,如果簇很小,文件分配表也隨之變大。仍然會有上面的效率問題存在。既要有效地讀寫大文件,又要最大可能的減少空間的浪費。FAT32同樣規(guī)定了相應的分區(qū)空間對應的簇的大小,見表12:
簇的取值意義和FAT16類似,不過是位數長了點罷了,比較見表13:
FAT32的另一項重大改革是根目錄的文件化,即將根目錄等同于普通的文件。這樣根目錄便沒有了FAT16中512個目錄項的限制,不夠用的時候增加簇鏈,分配空簇即可。而且,根目錄的位置也不再硬性地固定了,可以存儲在分區(qū)內可尋址的任意簇內,不過通常根目錄是最早建立的(格式化就生成了)目錄表。所以,我們看到的情況基本上都是根目錄首簇占簇區(qū)順序上的第1個簇。在圖4.3.12中也是按這種情況制作的畫的。 FAT32對簇的編號依然同FAT16。順序上第1個簇仍然編號為第2簇,通常為根目錄所用(這和FAT16是不同的,FAT16的根目錄并不占簇區(qū)空間,32個扇區(qū)的根目錄以后才是簇區(qū)第1個簇) FAT32的文件尋址方法與FAT16相同,但目錄項的各字節(jié)參數意義卻與FAT16有所不同,一方面它啟用了FAT16中的目錄項保留字段,同時又完全支持長文件名了。 對于短文件格式的目錄項。其參數意義見表14:
* 此字段在短文件目錄項中不可取值0FH,如果設值為0FH,目錄段為長文件名目錄段 說明: (1)、這是FAT32短文件格式目錄項的意義。其中文件名、擴展名、時間、日期的算法和FAT16時相同的。 (2)、由于FAT32可尋址的簇號到了32位二進制數。所以系統(tǒng)在記錄文件(文件夾)開始簇地址的時候也需要32位來記錄,FAT32啟用目錄項偏移0x12~0x13來表示起始簇號的高16位。 (3)、文件長度依然用4個字節(jié)表示,這說明FAT32依然只支持小于4GB的文件(目錄),超過4GB的文件(目錄),系統(tǒng)會截斷處理。 FAT32的一個重要的特點是完全支持長文件名。長文件名依然是記錄在目錄項中的。為了低版本的OS或程序能正確讀取長文件名文件,系統(tǒng)自動為所有長文件名文件創(chuàng)建了一個對應的短文件名,使對應數據既可以用長文件名尋址,也可以用短文件名尋址。不支持長文件名的OS或程序會忽略它認為不合法的長文件名字段,而支持長文件名的OS或程序則會以長文件名為顯式項來記錄和編輯,并隱藏起短文件名。 當創(chuàng)建一個長文件名文件時,系統(tǒng)會自動加上對應的短文件名,其一般有的原則: (1)、取長文件名的前6個字符加上"~1"形成短文件名,擴展名不變。 (2)、如果已存在這個文件名,則符號"~"后的數字遞增,直到5。 (3)、如果文件名中"~"后面的數字達到5,則短文件名只使用長文件名的前兩個字母。通過數學操縱長文件名的剩余字母生成短文件名的后四個字母,然后加后綴"~1"直到最后(如果有必要,或是其他數字以避免重復的文件名)。 (4)、如果存在老OS或程序無法讀取的字符,換以"_" 長文件名的實現有賴于目錄項偏移為0xB的屬性字節(jié),當此字節(jié)的屬性為:只讀、隱藏、系統(tǒng)、卷標,即其值為0FH時,DOS和WIN32會認為其不合法而忽略其存在。這正是長文件名存在的依據。將目錄項的0xB置為0F,其他就任由系統(tǒng)定義了,Windows9x或Windows 2000、XP通常支持不超過255個字符的長文件名。系統(tǒng)將長文件名以13個字符為單位進行切割,每一組占據一個目錄項。所以可能一個文件需要多個目錄項,這時長文件名的各個目錄項按倒序排列在目錄表中,以防與其他文件名混淆。 長文件名中的字符采用unicode形式編碼(一個巨大的進步哦),每個字符占據2字節(jié)的空間。其目錄項定義如表15。
系統(tǒng)在存儲長文件名時,總是先按倒序填充長文件名目錄項,然后緊跟其對應的短文件名。從表15可以看出,長文件名中并不存儲對應文件的文件開始簇、文件大小、各種時間和日期屬性。文件的這些屬性還是存放在短文件名目錄項中,一個長文件名總是和其相應的短文件名一一對應,短文件名沒有了長文件名還可以讀,但長文件名如果沒有對應的短文件名,不管什么系統(tǒng)都將忽略其存在。所以短文件名是至關重要的。在不支持長文件名的環(huán)境中對短文件名中的文件名和擴展名字段作更改(包括刪除,因為刪除是對首字符改寫E5H),都會使長文件名形同虛設。長文件名和短文件名之間的聯系光靠他們之間的位置關系維系顯然遠遠不夠。其實,長文件名的0xD字節(jié)的校驗和起很重要的作用,此校驗和是用短文件名的11個字符通過一種運算方式來得到的。系統(tǒng)根據相應的算法來確定相應的長文件名和短文件名是否匹配。這個算法不太容易用公式說明,我們用一段c程序來加以說明。 假設文件名11個字符組成字符串shortname[],校驗和用chknum表示。得到過程如下: int i,j,chknum=0; for (i=11; i>0; i--) chksum = ((chksum & 1) ? 0x80 : 0) + (chksum >> 1) + shortname[j++]; 如果通過短文件名計算出來的校驗和與長文件名中的0xD偏移處數據不相等。系統(tǒng)無論如何都不會將它們配對的。 依據長文件名和短文件名對目錄項的定義,加上對簇的編號和鏈接,FAT32上數據的讀取便游刃有余了。 FAT32文件系統(tǒng)學習
目的:需要編寫SD讀圖片的底層驅動程序。所以要了解一個SD卡常用文件系統(tǒng)基本概念。累計學習用時2.5小時。 一,FAT32的保留區(qū) 1,引導扇區(qū) :引導扇區(qū)是FAT32文件系統(tǒng)的第一個扇區(qū),也稱為DBR扇區(qū)。它包含這樣一些文件系統(tǒng)的基本信息: 【1】 每扇區(qū)字節(jié)數 【2】 每簇扇區(qū)數 【3】 保留扇區(qū)數【4】 FAT表個數 【5】 文件系統(tǒng)大?。ㄉ葏^(qū)數)【6】 每個FAT表大?。ㄉ葏^(qū)數) 【7】 根目錄起始簇號 【8】 其他一些附加信息 邊看說明,邊看圖片不太方便,我就按照說明內容,把說明直接標注在圖片上了。 我的SD卡是手機里的tf卡+sd卡套。之前沒有問題。當我第一次格式化后,就發(fā)現不正常了。雖然存取文件都沒問題。但是放在我的開發(fā)板上測試SD的時候,數據顯示不正確。 現在我初步發(fā)現問題在這里 【13】0x1C~0x1F:4個字節(jié),分區(qū)前已使用扇區(qū)數,137(0x00 00 00 89)。(這個數據要尤其的重視,文件系統(tǒng)初始化的第一步要找的就是這玩意兒。因為我們的SD卡沒有分區(qū),默認就是一個分區(qū),這個數據就是相對于MBR(關于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é)為引導代碼。同時,FAT32還可以利用引導扇區(qū)后的山區(qū)空間存放附加的引導代碼。 一個FAT卷即使不是可引導文件文件系統(tǒng),也會存在引導代碼。 3,FSINFO信息扇區(qū) FAT32在保留區(qū)中增加了一個FSINFO扇區(qū),用以記錄文件系統(tǒng)中空閑簇的數量以及下一可用簇的簇號等信息,以供操作系統(tǒng)作為參考。 FSINFO信息扇區(qū)結構 省略 溫馨提示:通常情況下,文件系統(tǒng)的2號扇區(qū)結尾也會被設置“55 AA”標志。6號扇區(qū)也會有一個引導扇區(qū)的備份,相應的,7號扇區(qū)應該是一個備份FSINFO信息扇區(qū)。8號扇區(qū)可以看做是2號扇區(qū)的備份,它的結尾也會有一個“55 AA”標志。 二,FAT32的FAT表 1 FAT表概述 位于保留區(qū)后的是FAT區(qū),有兩個完全相同的FAT(File Allocation Table, 文件分配表)表組成,FAT文件系統(tǒng)的名字也是因此而來。 重要說明: 1. 對于文件系統(tǒng)來說,FAT表有兩個重要作用:描述簇的分配狀態(tài)以及標明文件或目錄的下一簇的簇號。 2. 通常情況下,一個FAT把文件系統(tǒng)會有兩個FAT表,但有時也允許只有一個FAT表,FAT表的具體個數記錄在引導扇區(qū)的偏移0x10字節(jié)處。 3. 由于FAT區(qū)緊跟在文件系統(tǒng)保留區(qū)后,所以FAT1在文件系統(tǒng)中的位置可以通過引導記錄中偏移0x0E~0x0F字節(jié)處的“保留扇區(qū)數”得到。 4. FAT2緊跟在FAT1之后,它的位置可以通過FAT1的位置加上FAT表的大小扇區(qū)數計算出來。 2 FAT表的特性 FAT表由一系列大小相等的FAT表項組成,總的說來FAT表有如下特性: 1. FAT32中每個簇的簇地址,是有32bit(4個字節(jié))記錄在FAT表中。FAT表中的所有字節(jié)位置以4字節(jié)為單位進行劃分,并對所有劃分后的位置由0進行地址編號。0號地址與1號地址被系統(tǒng)保留并存儲特殊標志內容。從2號地址開始,每個地址對應于數據區(qū)的簇號,FAT表中的地址編號與數據區(qū)中的簇號相同。我們稱FAT表中的這些地址為FAT表項,FAT表項中記錄的值稱為FAT表項值。 2. 當文件系統(tǒng)被創(chuàng)建,也就是進行格式化操作時,分配給FAT區(qū)域的空間將會被清空,在FAT1與FAT2的0號表項與1號表項寫入特定值。由于創(chuàng)建文件系統(tǒng)的同時也會創(chuàng)建根目錄,也就是為根目錄分配了一個簇空間,通常為2號簇,所以2號簇所對應的2號FAT表項也會被寫入一個結束標記。如下圖所示: 3. 如果某個簇未被分配使用,它所對應的FAT表項內的FAT表項值即用0進行填充,表示該FAT表項所對應的簇未被分配。 4. 當某個簇已被分配使用時,則它對應的FAT表項內的FAT表項值也就是該文件的下一個存儲位置的簇號。如果該文件結束于該簇,則在它的FAT表項中記錄的是一個文件結束標記,對于FAT32而言,代表文件結束的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表項將會寫入結束標記。如果新建的文件不只占用一個簇,則在其所占用的每個簇對應的FAT表項中寫入為其分配的下一簇的簇號,在最后一個簇對應的FAT表象中寫入結束標記。 9. 新建目錄時,只為其分配一個簇的空間,對應的FAT表項中寫入結束標記。當目錄增大超出一個簇的大小時,將會在空閑空間中繼續(xù)為其分配一個簇,并在FAT表中為其建立FAT表鏈以描述它所占用的簇情況。 10. 對文件或目錄進行操作時,他們所對應的FAT表項將會被清空,設置為0以表示其所對應的簇處于未分配狀態(tài)。 要找一個簇的FAT表項,只要用它的簇號乘以每個FAT表項的字節(jié)數即可。Winhex提供了直接跳轉到某個指定FAT表項的功能,單擊position|go to FAT Entry,即可彈出轉到FAT項對話框,在對話框輸入目標FAT項號碼后單擊OK,光標即會在該FAT項的第一個字節(jié)上閃爍。 看到這里,我明白了簇地址就是一個大文件,被拆分成小塊后的一個個個像鏈表一樣的地址。保存在FAT表中。 今天主要看了保留區(qū)和FAT表區(qū)。明天繼續(xù)看數據區(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參數當中的Sectors(扇區(qū)總數)這個參數,這一參數代表了這個U盤在出廠時的總扇區(qū)數,筆者手上這個是7884672個,可以從圖2中看到。其中每個扇區(qū)為512 B,也就是說總共可以容納4036952064 B約為3.76GB的數據。但是這其中一部分是要用來存放FAT32文件系統(tǒng)的相關信息參數的,比如FAT表,BPB等。我們這邊來算一下,首先需要減去1016個保留扇區(qū),還有兩個FAT表總共是7684 * 2 = 15368個扇區(qū),所示還剩下的字節(jié)數為4036952064 B - ( 1016 + 15368 ) * 512 B = 4028563456 B 正好是圖中顯示的容量。所以可以得出結論,系統(tǒng)顯示的U盤容量 = ( 總扇區(qū)數 - 保留扇區(qū)數 - FAT表扇區(qū)數 * FAT表個數 ) * 512 B。經計算可得實際的使用率是99.79%。所以相對與整個U盤來說,FAT32文件系統(tǒng)用于存儲相關信息部分的損耗是很小的。
圖 2 筆者用上一篇中寫的工具查看了U盤的各項參數 好了,接下來進入正題,繼續(xù)學習FAT32文件系統(tǒng)的FAT表部分。 2、本文目錄1、題外話 2、FAT表的讀取 3、FAT表項 4、參考文獻 3、FAT表的讀取首先FAT表一般來說有兩張,另一張用于備份。兩張表是前后緊挨在一起的,只要計算出了FAT1表的偏移之后加上FAT表的大小就可以得到FAT2表的偏移。FAT1表的偏移地址計算公式如下[4] FAT1表偏移 = 保留扇區(qū)數 * 每扇區(qū)字節(jié)數 由圖2可知,在本例中,FAT1表的偏移 = 1016 * 512 B = 520192 = 0x7F000。同理: FAT2表的偏移 = FAT1+FAT表的大小 = (保留扇區(qū)數 + FAT表扇區(qū)數) * 每扇區(qū)字節(jié)數 在本例中,FAT2表的偏移 = (1016 + 7684) * 512 B = 4454400 = 0x43F800。用上一篇中講到的程序可以讀取出兩張FAT表的內容,一般情況下兩張表的內容應該是完全一樣的。筆者讀取了第一張FAT表起始部分的內容,如圖3所示:
圖 3 FAT表起始部分內容 4、FAT表項在分析FAT表之前先來說明一下FAT的構成。FAT表即文件分配表(File Allocation Table)。FAT32文件表是由一個個表項組成的一張表,其中每一個表項由一個32位的二進制組成,其值對應了相應簇的使用情況,如2號表項對應了2號簇的使用情況,3號表項對應了3號簇的使用情況,依此類推。(但是第0和第1項例外,下面會有說明)。每個表項對應數值的含義如表1所示[2]:
表 1 表項數值含義 具體每一項填寫的內容規(guī)則如下表所示:如果該簇是文件的最后一簇,填入的值為0x0FFFFFFF;如果該簇不是文件的最后一簇,則填入的值為該文件占用的下一簇號(所以我們可以看到在FAT32中文件是以簇鏈的形式保存起來的)。下面我們根據實際情況,圖3來分析一下FAT表的含義。 FAT表第0項(0x00000000~0x00000003): 0x0FFFFF8 FAT表第1項(0x00000004~0x00000007): 0xFFFFFFFF 這兩項不代表任何簇的使用情況,而是FAT表的表頭,表征了介質描述,是固定值,所以0x00和0x01這兩個簇號是不用的,簇號的下標從2開始。其中1號表項可能被用于記錄臟標志,以說明文件系統(tǒng)沒有被正常卸載或者磁盤表面存在錯誤。接下來 FAT表第2項(0x00000008~0x0000000B): 0x0FFFFFFF 第2項存儲的是第2簇的使用情況,通常第2簇存儲的是文件系統(tǒng)的根目錄。雖然在FAT32文件系統(tǒng)中,根目錄的位置不再硬性地固定,可以存儲在分區(qū)內可尋址的任意簇內,不過通常根目錄是最早建立的(格式化就生成了)目錄表。所以,我們看到的情況基本上都是根目錄首簇緊鄰FAT2,占簇區(qū)順序上的第1個簇(即2號簇)。同時,FAT32文件系統(tǒng)將根目錄當做普通的數據文件來看,所有沒有了目錄項數的限制,在需要的時候可以分配空簇,存儲更多的目錄項[1]。 這一項的值為0x0FFFFFFF ,說明根目錄占用且只占用了1個簇。 FAT表第3 …… 這里再穿插一點題外話,FAT32格式文件分配的最小單位是簇。也就是說你存儲了一個實際大小1kB的文件,那么它占用的存儲空間還是1簇(在這里換算成大小即為8*512B = 4KB)。筆者以一個實際的例子來說明一下:在U盤中放入一個8B大小的temp.txt文件,然后查看文件屬性的時候發(fā)現其占用空間是4KB,和我們上面講的理論符合。
圖 4 temp.txt的大小和占用空間 看了下篇幅也差不多了,那么本文關于FAT表的部分到此結束。其實本來也沒多少內容,筆者想到哪就扯到哪,胡扯了些其他的東西。剩下的數據區(qū)部分就留到下一篇當中再講好了。同樣的,本文當中有一些內容是筆者自己思考理解甚至推測出來的,如果有錯誤的地方歡迎指正,以免誤人子弟了(笑)。 FAT32文件系統(tǒng)學習(3) —— 數據區(qū)(DATA區(qū))今天繼續(xù)學習FAT32文件系統(tǒng)的數據區(qū)部分(Data區(qū))。其實這一篇應該是最有意思的,我們可以通過在U盤內放入一些文件,然后在程序中讀取出來;反過來也可以用程序在U盤內寫入一下數據,然后在windows下可以看到寫入的文件。這些筆者都會在這篇文章中演示(后來發(fā)現并沒有成功,不過筆者也找到相關的原因,詳見后來的更新部分吧:) )。同時,在寫這篇文章的時候筆者也發(fā)現了許多意想不到的規(guī)律。 1、本文目錄1、讀取根目錄 2、短文件名目錄項 3、長文件名目錄項 4、U盤寫入文件夾 5、參考文獻 2、讀取根目錄兩張FAT之后的所有扇區(qū)都是數據區(qū)部分。我們再通過圖1來回顧一下整個FAT32文件系統(tǒng)的分布規(guī)則。
圖 1 FAT32文件系統(tǒng)分布圖 通常情況下根目錄都是位于數據區(qū)頭部的,前面也提到過,有文獻上說是因為一旦U盤格式化完畢之后,根目錄就創(chuàng)建好了。本著探究的精神,筆者嘗試格式化了一下U盤,發(fā)現其實并沒有創(chuàng)建根目錄。不過一旦有文件操作,馬上就會創(chuàng)建根目錄,因為這時整個數據區(qū)都是空的,所以自然是寫入數據區(qū)的頭部。到頭來其實道理是一樣的,也就是說根目錄一般情況下都是在數據區(qū)的頭部(第2簇)。 數據區(qū)偏移計算 經過前兩篇關于BPB和FAT部分學習之后,我們就可以計算出數據區(qū)頭部的偏移: 數據區(qū)偏移 = (保留扇區(qū)數 + FAT表扇區(qū)數 * FAT表個數(通常為2) + (起始簇號-2) * 每簇扇區(qū)數) * 每扇區(qū)字節(jié)數 筆者首先格式化了U盤,通過偏移讀取出了數據區(qū)的頭部,發(fā)現都是0x00。 題外話 這里又要插一些題外話了,筆者試著改了一下U盤的卷標,把它改名為“FAT”。然后還記得BPB當中有一個參數叫做卷標嗎?筆者看了下發(fā)現卷標這個參數還是“NO NAME”并沒有改變。這時筆者把數據區(qū)的頭部讀取了出來,如圖2所示:
圖2 卷標 原來被寫在了這里。最后經筆者的測試,卷標最長長度是11個字節(jié),偏移從0x00~0x0A,而偏移0x0B處的值0x08值的意思就是卷標(關于此處值的意思相面還會詳細描述)。因為這個U盤其實還沒有寫入過任何數據,所以卷標才會顯示在數據區(qū)的開頭,但是如果換種情況呢,文件系統(tǒng)又是如何找到卷標的呢?筆者查閱了相關資料后發(fā)現,卷標一般都是寫在根目錄的下的,如果發(fā)現根目錄項的其中一項其0x0B偏移處的值為0x08那么讀取該項的前11個字節(jié)即為卷標。 3、短文件名目錄項短文件名目錄項參數 好,回到正題。先來講一下理論的東西:目錄區(qū)是由一個個目錄項構成,類似于FAT表。其中每一個目錄項占用32個字節(jié),可以是代表長文件名目錄項、文件目錄項、子目錄項等。對于短文件名格式的目錄項,其參數的含義如表1所示(不會畫這種表,從別處引用了一個)[1]:
表1 FAT32短文件名目錄項參數表 參數解釋 用一個實際的例子來解釋一下這些參數的意思,首先創(chuàng)建一個短文件名文件,如“file1.txt”,讀取根目錄,如圖3所示:
圖3 file1.txt 短文件名目錄項 先不管其他數據,我們來看一下紅色矩形框部分的數據,它就是一個短目錄項。我們來一個個對比表1的參數進行說明:
表2 file1.txt 參數解釋 注釋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, 計算結果需要加上毫秒位上的進位,這邊11100(2進制) = 28(10進制),所以秒數 = 28*2 = 56,再加上毫秒上的進位1所以結果為57。 注釋2:0100010 1000 01000 1)這里高7位代表從1980年開始的年數,筆者計算了下可以到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進制)。 這樣除了文件起始簇號字段,其他字段的意思和計算方法都弄清楚了。下面來看一下文件起始簇號,首先根據高低各16位,計算出完整的文件起始簇號 = 0x00000005 ,文件起始地址偏移的計算: 文件起始地址 = (保留扇區(qū)數 + FAT表扇區(qū)數 * FAT表個數(2) + (文件起始簇號-2)*每簇扇區(qū)數)*每扇區(qū)字節(jié)數 本例中計算結果為0x4010,然后到這個地址讀取內容并切入到磁盤文件中(詳細操作參考第一篇文章),如圖4所示,windows下打開內容如圖5所示:
圖4 圖5 文件內容 這個時候再去看一下FAT表的5號簇,計算方式在上一篇當中,結果如圖6所示:
圖 6 FAT表5號表項 紅色矩形框的位置就是5號簇的位置,可以看到值0x0FFFFFFF,意思就是文件在這一簇結束了。 (具體不同數值的含義詳見上一篇)。如果這里文件大小超過1簇,那么這個值應該是下一簇的簇號,繼續(xù)讀取下一簇的內容即可。雖然我們知道了文件占用的空間是1簇,但是怎么知道文件具體的大小呢?再回過頭來看上面的短文件目錄項,最后一個屬性是文件長度,上面已經計算得到為12,“Hello World!”的長度正好是12:)。 至此短文件目錄項應該已經分析的差不多了。 4、長文件名目錄項長文件名目錄項參數 下面是長文件名目錄項,筆者思考了好久該怎么把它講清楚,畢竟理解是一回事,講清楚就是另一回事了。 在講長文件目錄項之前先來說一下FAT32的一個很重要的特性,支持長文件名。長文件名也是記錄在目錄項當中的,區(qū)別與短目錄項的是,前者可能會占據好幾個目錄項。為了兼容低版本的OS或程序能正確讀取長文件名文件,系統(tǒng)自動為所有長文件名文件創(chuàng)建了一個對應的短文件名,使對應數據既可以用長文件名尋址,也可以用短文件名尋址。不支持長文件名的OS或程序會忽略它認為不合法的長文件名字短,而支持長文件名的OS或程序則會以長文件名為顯式項來記錄和編輯,并隱藏起短文件名[2]。 當創(chuàng)建一個長文件名文件時,系統(tǒng)會自動加上對應的短文件名,其原則如下: (1)、取長文件名的前6個字符加上"~1"形成短文件名,擴展名不變。 (2)、如果已存在這個文件名,則符號"~"后的數字遞增,直到5。 那么系統(tǒng)是如何判斷當前目錄項是短文件名目錄項呢還是長文件名目錄項,這里關鍵是看目錄項的第12個字節(jié)的值,如果為0x0F時則系統(tǒng)認為是長目錄項。而如果是舊版本的系統(tǒng)看到第12個字節(jié)是0x0F則認為是異常而忽略掉。這里可以回過頭去看一下短文件名目錄項,第12個字節(jié)是文件屬性字節(jié),0x0F即為全1是無效的,所以系統(tǒng)認為是異常。系統(tǒng)將長文件名以13個字符為單位進行切割,每一組占據一個目錄項。所以可能一個文件需要多個目錄項,這時長文件名的各個目錄項按倒序排列在目錄表中,以防與其他文件名混淆。 這樣講可能還是很抽象,先來看一下長文件名目錄項的參數,如表3所示[1]:
表3 長文件名目錄項參數表 參數解釋 然后還是以一個實際的例子來說明,在根目錄區(qū)讀入一個長文件名目錄項,如圖7所示:
圖7 長文件名目錄項 圖中選定部分即為多個長文件名目錄項。我們來慢慢分析。系統(tǒng)在讀入一個目錄項的時候首先查看它的第12個字節(jié),發(fā)現是0x0F,所以認為這是一個長文件名目錄項。我們來看長文件名目錄項的參數,如表4所示:
表4 長文件名目錄項參數解釋 注釋1:01000010 第7位為1,說明是文件最后一個目錄項目,低5位為順序 0010(2進制) = 2(10進制),說明這是第2個長目錄項,且是最后一個目錄項。即為這個長文件名占用了兩個目錄項。 注釋2:Unicode 百度百科Unicode 點我詳細解釋 這邊有3個Unicode區(qū),加起來正好是26個字節(jié)即13個Unicode碼,所以這就是為什么上面講的以13個字符為單位切割。因為這是第2個目錄項,所以后面應該還有第1個目錄項,繼續(xù)分析下一個目錄項其余參數同上,看一下3個Unicode分別是“LongL” “engthF” “il”而0x00的屬性字節(jié)是01,說明這是第一個。至此這個長文件名讀取完畢了。按照倒序(這里也解釋了前面說的倒序的意思)的順序拼接起來的話就是“LongLengthFilename”——這就是這個文件的文件名。 下面再來看一下下一個目錄項,長文件名目錄項后面還會跟一個短文件名目錄項,這個目錄項記錄了除文件名以外的這個文件的信息,而文件名部分則用上面提到的短文件名目錄項替換。所以讀取方法和短文件名目錄項是一樣的,這里只看一下文件屬性字節(jié),偏移為0x0B,值為0x10=(00010000) 根據短文件名目錄項參數的意思,這個文件是一個子目錄。其余參數讀者可以根據上面提到的計算方法得出。 最后再來補上剛才的校驗碼計算方法: int i, j = 0, chksum=0; for (i = 11; i > 0; i--) chksum = ((chksum & 1) ? 0x80 : 0) + (chksum >> 1) + shortname[j++]; 其中shortname即長文件名目錄項對應的短文件名,所以這個校驗碼需要等到讀完短文件名目錄項之后才可以計算。這一段程序是筆者從網上摘來的,還沒有時間驗證一下。 5、U盤寫入文件夾這樣關于數據區(qū)的部分差不多就講完了。 最后在做一點有趣的事情,嘗試向磁盤的扇區(qū)中寫入一些數據,然后看是否會生成這個文件。為了方便起見,這里直接在根目錄創(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 這樣應該就可以了,好了,開始編碼: // 短文件名目錄項數據結構 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é)數 DWORD dwRealNumber; // 分配緩沖區(qū) char* buffer = new char[512]; // 讀取一個扇區(qū)的數據 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); // 準備數據 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; // 修改根目錄數據 char* pData = (char*)item; for (int i = 32; i < 64; ++i) { buffer[i] = *(pData++); } // 寫回根目錄 bRet = WriteFile(hDisc, buffer, dwNumber2Read, &dwRealNumber, NULL); // 掃尾工作,釋放緩沖區(qū),關閉句柄 delete[] buffer;48 delete item;49 CloseHandle(hDisc); 但是筆者發(fā)現在win7(準確的說是win7、vista、win8,XP下獲取管理員權限即可執(zhí)行)下調用WriteFile函數無法將數據寫入U盤,可能是由于系統(tǒng)保護措施的關系,由于時間關系,筆者也沒去深究。 后來筆者專門去查找了相關資料,總的來說原因確實是因為系統(tǒng)保護措施的關系導致WriteFile函數操作的失敗,具體的解釋如下: 首先是msdn上的解釋 http://msdn.microsoft.com/en-us/library/windows/hardware/ff551353(v=vs.85).aspx。大概意思是說在win7和vista上加入了一些新的特性,為了能夠更好得保護系統(tǒng),如果應用程序沒有獨占的權限就直接對裝有文件系統(tǒng)的存儲設備進行寫入操作的話,這個操作是會被拒絕的。筆者上面的程序通過GetLastError()函數得到的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ā)送一個鎖定驅動的請求,然后再嘗試寫入。具體做法參考這個帖子22L吧,筆者打算去嘗試一下,成功的話再來更新結果。 好了,看了下篇幅這篇文章也差不多可以結束了。FAT32文件系統(tǒng)其實差不多也都學習完了,為了鞏固學習內容,筆者打算接下去根據前面所學的知識,并去了解一下windows快速格式化FAT32的機制,嘗試自己格式化U盤,還可以根據FAT32的原理嘗試刪除數據的恢復等,總之還是有很多事情可以做的。 最后的最后,如果文章當中有任何錯誤或者遺漏指出,歡迎指出,謝謝。 FAT32 FAT區(qū)__FAT表解析一、 FAT 表概述 位置:緊跟在文件系統(tǒng)的“保留區(qū)”之后 ; 有兩個數據結構完全相同的FAT(FAT,File Allocation Tbale 文件分配表)組成。 作用:FAT表項,描述文件系統(tǒng)內的簇分配狀態(tài),說明文件系統(tǒng)內數據所分配的連續(xù)簇的順序關系(即表明文件或目錄的下一簇的序號)。 常規(guī)規(guī)則: · 數量:通常情況下一個FAT 文件系統(tǒng)會有兩個FAT 表, 但有時候也會允許只有一個FAT 表, FAT 表的具體個數記錄在引導扇區(qū)的 偏移 0x10 字節(jié)處。 · 位置:因為FAT區(qū)位于文件系統(tǒng)的保留區(qū)之后,所以FAT1在文件系統(tǒng)中的位置可以通過引導記錄中偏移0x0E~0x0F 字節(jié)處的“保留扇區(qū)”數得到。 · FAT2 緊跟在FAT1之后, 它的位置可以通過FAT1的位置加上每個FAT 表的大小扇區(qū)數獲得。 FAT 表中記錄了每個文件的簇鏈結構; FAT 表中記錄的與數據區(qū)簇對應的表項,從0號標記開始至當前數據區(qū)所分配的簇的最大數值,記錄簇信息到FAT 項;但是注意:其中 0號~1號簇的值都是操作系統(tǒng)預先不留設定的特殊標記,而數據區(qū)的起始簇是2號簇。 二、、FAT 表的特性 FAT 表由一些列大小相等的表項組成,有如下特性: · FAT32 中每個簇的狀態(tài),使用32bit(4字節(jié))記錄在FAT表中。 FAT 表中的所有字節(jié)位置以 4個字節(jié)為單位進行劃分;并以所有劃分后的位置由0進行地址編號。“0號 和 1號” 地址被系統(tǒng)保留并存儲特殊標識內容。從 2號 地址開始, 每個地址對應于數據區(qū)的簇號,FAT 表中的地址編號與數據區(qū)中的簇號相同。稱FAT 中的這些四字節(jié)一組劃分的項的地址為 FAT 表項,FAT表項中記錄的值為FAT 表項值。(簇編號與簇內內容關系如果 Map中的鍵-值 關系相同) 當文件系統(tǒng)創(chuàng)建時(就是格式化操作時), 分配給FAT 區(qū)域的空間將會被清空, 在FAT1與FAT2 的0號和1號表項寫入特定值。由于創(chuàng)建文件系統(tǒng)的同時,也會創(chuàng)建根目錄, 也就是為根目錄分配了一個簇空間,通常為2號簇,所以2號簇所對應的“2號FAT表項”也會被寫入一個結束標記。 · 如果某個簇未被使用,他所對應的FAT 表項內的FAT 表項值即用0進行填充,表示該FAT 表項所對應的簇未被分配使用 · 當某個簇被分配使用時,那么他所對應的FAT表項的值為文件的下一個存儲文件的簇號。 如果該文件結束于該簇,則在它的FAT表項中記錄的是一個文件結束標記,對于FAT32 而言,代表文件結束的FAT表項值為0x0FFFFFFF。 · 如果某個簇存在壞扇區(qū),則整個簇會用FAT 表項值0x0FFFFFF7 標記為壞簇, 不再使用,這個壞簇標記就記錄在它所對應的FAT表項中。 · 由于簇號起始于2, 所以FAT表的0號表項與1號表項不予任何簇對應。 FAT32 的0號表項值總是“F8FFFF0F” 注意:可以搜索扇區(qū)偏移0字節(jié)處的該值(F8FFF0F)以查找FAT表。 · 1號表項可能被用于記錄“臟標志”, 以說明文件系統(tǒng)沒有被正常卸載或者磁盤表面存在錯誤。 不過此值似乎不重要,正常情況下,1號表項值“FFFFFFFF”或“FFFFFF0F” 項內容填寫規(guī)則: ·在文件系統(tǒng)中新建文件時,如果新建的文件只有一個簇,為其分配的簇所對應的FAT表項將會被寫入結束標記。如果新建的文件不只占用一個簇,則在其所占用的每個簇對應的FAT表項中寫入為其分配的下一個簇的簇號,在最后一個簇對應的FAT 表項中寫入結束標記。 · 新建目錄時,只為其分配一個簇的空間,對應的FAT 表項中寫入結束標記。當目錄增大超過一個簇的大小時,將會在空閑空間中繼續(xù)為其分配一個簇,并在FAT 表中為其建立FAT 表鏈以描述它所占用的簇的情況。 · 對文件或目錄進行刪除操作時,他們所對應的FAT 表項將會被清空,設置為0以表示其所對應的簇處于未分配的狀態(tài)。 三、 FAT表的使用 一個文件的“起始簇號”記錄在它的目錄項中, 該文件的 "其他簇" 則用一個簇連結構記錄在FAT 表中。 如果一個簇所對應的FAT表項的表項值為非零, 則表明該簇已經被分配使用了,但是這時表項值可能為兩種情況,一個是一個文件的下一個簇號值,也有可能是一個文件的結束標記0x0FFFFFFF,或者是一個壞簇標記0x0FFFFFF7 。 如果要尋找一個文件的下一個簇,只需要查看該文件的目錄項中描述的起始簇號所對應的FAT項,如果該文件只有一個簇,則此處的值為一個結束標記0x0FFFFFFF;如果該文件不只一個簇,則此處 的值是它的下一個簇的簇號。 >> 查詢簇連接結構模擬步驟: 當我們要尋找某個文件時, 首先從該文件的“目錄項”中獲取該文件的第一“簇”的簇號,然后根據“第一簇”的“簇號 N”,然后根據N 從FAT區(qū)的FAT表找出N簇號所對應的FAT 表項,查看FAT 表項的內容: 若是文件結束,該表項值為 0x0FFFFFFF; 若是沒有結束,而該文件的大小超出一個簇,則N所對應的FAT表項的表項值為該文件下一個簇的簇號,然后再找到下一個簇號N+1 所對應的FAT表項,查看其表項值,依次類推,就推出了一個文件在FAT表中的 簇鏈連接結構; 也或者表項值為 0xFFFFFFF7 壞簇標識。 >> 其他 查找FAT 表項:要找到一個簇的FAT 表項,只要用他的 簇號乘以每個FAT 表項的字節(jié)數即可 。對于FAT32而言,每個FAT 表項占用4個四字節(jié), 如果我們尋找9號簇的表項位置,則用 4*9=36,也就是說位于FAT 表內偏移36(0x24)字節(jié)處。 注意: WinHex 提供了直接跳轉到某個指定FAT 表項的功能。 文件系統(tǒng)大小的上限值卻絕育FAT 項的大小。 簇鏈中的每個FAT 項記錄著下一個簇的簇地址,FAT 項所能表示的數字有一個上限,這個上限也就是文件系統(tǒng)中的最大簇號。 FAT 32文件系統(tǒng)的FAT 項只使用了32bit 中的28bit ,因此只能描述 268435456個簇(實際上還要考略小于這個值,因為這其中還包含了結束標志及壞簇標志的保留值)。 操作系統(tǒng)通過檢測FAT 表中的表項來確定文件系統(tǒng)中的各個簇是否被分配使用。當我們在Windows 下右擊某個FAT 分區(qū)查看其屬性時,顯示的已用空間和未用空間就是根據FAT 表統(tǒng)計而來的。 有時我們會遇到,查看屬性時發(fā)現已用空間并沒有減少,但存儲的文件卻不見了。這是因為某些病毒在某些文件的目錄項中寫入了刪除標記,但并沒有清楚FAT 表內的簇鏈所至。 FAT32文件系統(tǒng)的存儲組織結構對磁盤的物理結構,邏輯結構和存儲結構有了比較深入的了解后,我們來仔細探討FAT32文件系統(tǒng)的存儲組織結構。說到文件系統(tǒng)的組織結構,我們應該馬上意識到,這指的是文件系統(tǒng)在同一個分區(qū)內的組織結構,在這個話題上,我們完全可以不管分區(qū)之外的所有事情。 為了分析FAT32文件系統(tǒng)的存儲組織結構,我們來建立一個實實在在的文件系統(tǒng):將U盤插入電腦,將U盤格式化成FAT32分區(qū)格式:
以建好的U盤FAT32文件系統(tǒng)為基礎,下面從文件系統(tǒng)的各個組成來分別加以介紹。 分區(qū)引導扇區(qū)DBR 用winhex打開U盤顯示如下:
這是FAT32分區(qū)引導記錄,定義如下: 偏移00H: 3字節(jié)的 跳轉指令 EB 58 90,跳過下面的BPB和擴展BPB部分 偏移03H:8字節(jié)的硬盤分區(qū)類型文本字符名:4D 53 44 4F 53 35 2E 30 即:MSDOS5.0 偏移0BH: 25字節(jié)的分區(qū)參數塊(BPB),細分如下: 偏移0BH:扇區(qū)字節(jié)數:00 02 即0X0200,512字節(jié) 偏移0DH:每簇扇區(qū)數:08即每簇包括8個扇區(qū) 偏移0EH:保留扇區(qū)數:24 00即保留36個扇區(qū) 偏移10H:FAT表份數:02即兩個FAT表 偏移11H:未用:00 00 偏移13H:未用:00 00 偏移15H:介質類型:F8即本地硬盤 偏移16H:未用:00 00 偏移18H:每磁道扇區(qū)數:3F 00 即每磁道63扇區(qū) 偏移1AH:磁頭數:FF 00即255個磁頭 偏移1CH:隱藏扇區(qū)數:80 1F即8064個隱藏扇區(qū) 偏移20H:磁盤總扇區(qū)數 80 F0 77 00即總共7860352個扇區(qū)(7860352*512=4024500224,因為我的U盤是4G) 偏移24H:52字節(jié)的擴展分區(qū)參數塊(擴展BPB),細分如下: 偏移24H:FAT表占用扇區(qū)數: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ū)的內容和0號引導扇區(qū)內容是一樣的 偏移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通常為一隨機數 偏移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ū)結束標志 55 AA 到此分區(qū)引導扇區(qū)介紹結束。 文件分配表FAT 簡介: FAT表(文件分配表),是FAT文件系統(tǒng)中用于磁盤數據索引和定位而引進的一種鏈式結構。在FAT文件系統(tǒng)中,文件的存儲依照FAT表制定的簇鏈式數據結構來進行。同時,FAT文件系統(tǒng)將組織數據時使用的目錄也抽象為文件,以簡化對數據的管理。 FAT1表位置的定位: 在我們前面介紹分區(qū)引導記錄的時候提到,在偏移0EH處存儲了保留扇區(qū)的個數,這個保留扇區(qū)數指的就是當前分區(qū)內DBR到FAT表之間的所有扇區(qū)的個數(包括DBR但不包括FAT表)。因此,我們可以定位FAT表所在的起始偏移位置了,即24H*200H=4800H。我們貼出4800H處得部分內容如下:
顯然沒有錯,這就是我們FAT1所存儲的位置,只是當前沒有存儲文件,所以FAT比較簡單罷了。 FAT2表位置的定位: 在我們前面介紹分區(qū)引導記錄的時候提到,在偏移24H處存儲了FAT表所占用的扇區(qū)個數,我們又知道FAT2是緊鄰FAT1的,所以可以很容易得到FAT2的存儲位置的偏移地址:FAT1的起始偏移地址+FAT1的大小=4800H+1DEEH*200H=3C2400H,我們貼出3C2400H處的部分內容如下:
顯然沒有錯,這就是我們FAT2所存儲的位置,內容與FAT1相同。 FAT表的特性: FAT表由一系列大小相等的FAT表項組成,它有如下特性: FAT32中每個簇的簇地址,使用32bit(4個字節(jié))記錄在FAT表中。FAT表中的所有字節(jié)位置以4個字節(jié)為單位進行劃分,并對所有劃分后的位置由0進行地址編號。0 號地址與1號地址被系統(tǒng)保留并存儲特殊標志內容。從2號地址開始,每個地址對應于數據區(qū)的簇號,FAT表中的地址編號與數據區(qū)中的簇號相同。我們稱FAT中的這些地址為FAT表項,FAT表項中記錄的值稱為FAT表項值。 當文件系統(tǒng)被創(chuàng)建,也就是進行格式化操作時,分配給FAT區(qū)域的空間將會被清空,在FAT1與FAT2的0號表項與1號表項寫入特定值。由于創(chuàng)建文件系統(tǒng)的同時也會創(chuàng)建根目錄,也就是為根目錄分配了一個簇空間,通常為2號簇,所以2號簇所對應的2號FAT表項也會被寫入一個結束標記。 如果某個簇未被分配使用,它所對應的FAT表項內的FAT表項值即用0進行填充,表示該FAT表項所對應的簇未分配使用。 當某個簇已被分配使用時,則它對應的FAT表項值也就是該文件的下一個存儲位置的簇號。如果該文件結束于該簇,則在它的FAT表項中記錄的是一個文件結束標記,對于FAT32而言,代表文件結束的FAT表項值為0x0FFFFFFF。 如果某個簇存在壞扇區(qū),則整個簇會用FAT表項值0x0FFFFFF7標記為壞簇,不再使用,這個壞簇標記就記錄在它所對應的FAT表項中。 由于簇號起始于2,所以FAT表的0號表項與1號表項不與任何簇對應。FAT32的0號表項值總是“F8FFFF0F”。1號表項可能被用于記錄臟標志,以說明文件系統(tǒng)沒有被正常卸載或者磁盤表面存在錯誤。不過此值似乎并不重要,因此我們只要了解就可以。正常情況下,1號表項值為“FFFFFFFF”或“FFFFFF0F"。 在文件系統(tǒng)中新建文件時,如果新建的文件只占用一個簇,為其分配的簇所對應的FAT表項將會被寫入結束標記。如果新建的文件不只占用一個簇,則在其所占用的每個簇對應的FAT表項中寫入為其分配的下一簇的簇號,在最后一個簇對應的FAT表項中寫入結束標記。 新建目錄時,只為其分配一個簇的空間,對應的FAT表項中寫入結束標記。當目錄增大超出一個簇的大小時,將會在空閑空間中繼續(xù)為其分配一個簇,并在FAT表中為其建立FAT表鏈以描述它所占用的簇情況。 對文件或目錄進行刪除操作時,它們所對應的FAT表項將會被清空,設置為0以表示其所對應的簇處于未分配狀態(tài)。 根目錄區(qū) 簡介: 在FAT32文件系統(tǒng)中,根目錄的位置不再硬性地固定,可以存儲在分區(qū)內可尋址的任意簇內,不過通常根目錄是最早建立的(格式化就生成了)目錄表。所以,我們看到的情況基本上都是根目錄首簇緊鄰FAT2,占簇區(qū)順序上的第1個簇(即2號簇)。同時,FAT32文件系統(tǒng)將根目錄當做普通的數據文件來看,所有沒有了目錄項數的限制,在需要的時候可以分配空簇,存儲更多的目錄項。 起始偏移地址定位: 根目錄起始扇區(qū)=保留扇區(qū)數+FAT×2+(起始簇-2)x每簇的扇區(qū)數,在我們前面介紹分區(qū)引導記錄的時候提到,偏移2CH處保存了根目錄起始簇號是2,所以求得根目錄起始扇區(qū)是24H+1DEEH*2H+(2-2)*8H=3C00H,即求得偏移地址3C00H*200H=780000H,我們貼出780000H處的部分內容如下:
目錄區(qū)的一個目錄項占用32個字節(jié),可以是長文件名目錄項、文件目錄項、子目錄項等。 短文件名格式的目錄項 對于短文件名格式的目錄項。其參數意義如下:
根據參數定義,我們來分析一下上圖的目錄項 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)建了一個對應的短文件名,使對應數據既可以用長文件名尋址,也可以用短文件名尋址。不支持長文件名的OS或程序會忽略它認為不合法的長文件名字段,而支持長文件名的OS或程序則會以長文件名為顯式項來記錄和編輯,并隱藏起短文件名。 當創(chuàng)建一個長文件名文件時,系統(tǒng)會自動加上對應的短文件名,其原則如下: (1)、取長文件名的前6個字符加上"~1"形成短文件名,擴展名不變。 (2)、如果已存在這個文件名,則符號"~"后的數字遞增,直到5。 長文件名的實現有賴于目錄項第12字節(jié)屬性字節(jié),當此字節(jié)的值為0FH時,支持長文件名的系統(tǒng)會將其當做長文件名的依據,而只支持短文件名的系統(tǒng)會認為是異常而忽略掉。系統(tǒng)將長文件名以13個字符為單位進行切割,每一組占據一個目錄項。所以可能一個文件需要多個目錄項,這時長文件名的各個目錄項按倒序排列在目錄表中,以防與其他文件名混淆。 長文件名中的字符采用unicode形式編碼,每個字符占據2字節(jié)的空間。其目錄項定義如:
下面是我建立的長文件名文件夾abcdefghijklmnopqrstuvwxyz1234567890的目錄項:
前面已經基于一個格式化的空U盤分析了一下FAT32文件系統(tǒng)存儲的組織結構,下面我們從文件操作的角度來分析一下文件系統(tǒng)的運作機制。由于換了個U盤,所以仍然貼出剛格式化的空U盤的幾個重要的數據區(qū)如下:
我們可以看出,在分區(qū)格式化的時候,系統(tǒng)將卷標TEST_FAT32存儲在2號簇,即跟目錄區(qū),如上面根目錄貼圖所示。同時,在FDT區(qū)2號簇標記位置寫入了文件結束符FF FF FF 0F。顯然,FAT32文件系統(tǒng)將目錄當做普通文件來處理的。 下面我們在根目錄下新建一個文件夾TEST1,看會有什么變化: 建立了TEST1文件夾后,FDT變成如下:
根目錄變成如下:
重新分配了3號簇:
從上面的變化可以直觀的看出,系統(tǒng)在新建文件夾時完成了如下動作: a.在父目錄所在簇上建立新的目錄項,存儲當前所建文件夾信息。 b.分配一個新簇,給新建的文件夾建立兩個目錄項:父目錄和當前目錄。 c.在FDT表中新分配的簇對應的位置上寫下文件結束符。 d.建立各部分的鏈路關系:新建文件夾所對應的目錄項的文件起始簇號字段寫上新分配簇的簇號,新簇上的兩個目錄項的文件起始簇號字段分配寫上父目錄所在簇號(此處是0,本來我以為是2,即根目錄所在簇,不知道為什么,可能特地用0指示根目錄吧)和當前簇號(此處是3)。 為了驗證我們上面分析的正確性,我們再在TEST1文件夾下建立新文件夾TEST11,看是否做了如下操作: a.在父目錄(即TEST1)所在簇(即3號簇)上建立新的目錄項,存儲TEST11文件夾信息。 b.分配一個新簇(應該是4號簇),給新建的文件夾(即TEST11)建立兩個目錄項:父目錄和當前目錄。 c.在FDT表中新分配的簇(應該是4號簇)對應的位置上寫下文件結束符。 d.建立各部分的鏈路關系:新建文件夾(即TEST11)所對應的目錄項的文件起始簇號字段寫上新分配簇的簇號(應該是4號簇),,新簇上的兩個目錄項的文件起始簇號字段分配寫上父目錄所在簇號(3號簇)和當前簇號(應該是4號簇)。 新建TEST11文件夾后FDT變成:
根目錄沒有變化:
3號簇變成:
新分配4號簇:
顯然我們的估計沒有錯的,也進一步證明我們前面的分析是正確的。 下面我們再分析建立文件的情況 我們先建立一個100字節(jié)的文件TEST.TXT,然后把這個文件拷貝到U盤的根目錄下,FDT變成如下:
根目錄變成:
新分配5號簇保存文件內容:
從上面的變化可以直觀的看出,系統(tǒng)新建文件和新建文件夾所完成的操作是一樣一樣的: a.在父目錄所在簇上建立新的目錄項,存儲當前所建文件信息。 b.分配一個新簇,存儲新建的文件的內容。 c.在FDT表中新分配的簇對應的位置上寫下文件結束符。 d.建立鏈路關系:新建文件所對應的目錄項的文件起始簇號字段寫上新分配簇的簇號。 結束總結: 1.在FAT32文件系統(tǒng)中,目錄和文件的存儲采用統(tǒng)一的方式。 2.文件系統(tǒng)的操作的單位是簇,每新建立一個文件或文件夾,至少會重新分配一個簇號。 3.如果一個文件或目錄的內容要多個簇才能存儲得下,則系統(tǒng)會分配多個簇來存儲文件或目錄的內容 4.當需要多個簇時,這些簇可能連續(xù)也可能不連續(xù),但無論是連續(xù)或是不連續(xù),系統(tǒng)都是采用FDT鏈表的形式來組織的。 FAT32系統(tǒng)中長文件名的存儲FAT32的一個重要的特點是完全支持長文件名。長文件名依然是記錄在目錄項中的。 為了低版本的OS或程序能正確讀取長文件名文件,系統(tǒng)自動為所有長文件名文件創(chuàng)建了一個對應的短文件名,使對應數據既可以用長文件名尋址,也可以用短文件名尋址。不支持長文件名的OS或程序會忽略它認為不合法的長文件名字段,而支持長文件名的OS或程序則會以長文件名為顯式項來記錄和編輯,并隱藏起短文件名。 當創(chuàng)建一個長文件名文件時,系統(tǒng)會自動加上對應的短文件名,其一般有的原則: (1)、取長文件名的前6個字符加上"~1"形成短文件名,擴展名不變。 (2)、如果已存在這個文件名,則符號"~"后的數字遞增,直到5。 (3)、如果文件名中"~"后面的數字達到5,則短文件名只使用長文件名的前兩個字母。通過數學操縱長文件名的剩余字母生成短文件名的后四個字母,然后加后綴"~1"直到最后(如果有必要,或是其他數字以避免重復的文件名)。 (4)、如果存在老OS或程序無法讀取的字符,換以"_" 短文件格式的目錄項。其參數意義見表14:
長文件名的實現有賴于目錄項偏移為0xB的屬性字節(jié),當此字節(jié)的屬性為:只讀、隱藏、系統(tǒng)、卷標,即其值為0FH時,DOS和WIN32會認為其不合法而忽略其存在。這正是長文件名存在的依據。 將目錄項的0xB置為0F,其他就任由系統(tǒng)定義了,Windows9x或Windows 2000、XP通常支持不超過255個字符的長文件名。 系統(tǒng)將長文件名以13個字符為單位進行切割,每一組占據一個目錄項。所以可能一個文件需要多個目錄項,這時長文件名的各個目錄項按倒序排列在目錄表中,以防與其他文件名混淆。 長文件名中的字符采用unicode形式編碼(一個巨大的進步哦),每個字符占據2字節(jié)的空間。其目錄項定義如表15。
系統(tǒng)在存儲長文件名時,總是先按倒序填充長文件名目錄項,然后緊跟其對應的短文件名。從表15可以看出,長文件名中并不存儲對應文件的文件開始簇、文件大小、各種時間和日期屬性。文件的這些屬性還是存放在短文件名目錄項中,一個長文件名總是和其相應的短文件名一一對應,短文件名沒有了長文件名還可以讀,但長文件名如果沒有對應的短文件名,不管什么系統(tǒng)都將忽略其存在。所以短文件名是至關重要的。 在不支持長文件名的環(huán)境中對短文件名中的文件名和擴展名字段作更改(包括刪除,因為刪除是對首字符改寫E5H),都會使長文件名形同虛設。 (長文件名如何與短文件名對應?僅靠她們之間的位置關系?) 長文件名和短文件名之間的聯系光靠他們之間的位置關系維系顯然遠遠不夠。其實,長文件名的0xD字節(jié)的校驗和起很重要的作用,此校驗和是用短文件名的11個字符通過一種運算方式來得到的。系統(tǒng)根據相應的算法來確定相應的長文件名和短文件名是否匹配。這個算法不太容易用公式說明,我們用一段c程序來加以說明。 假設文件名11個字符組成字符串shortname[],校驗和用chknum表示。得到過程如下: int i,j,chknum=0; for (i=11; i>0; i--) chksum = ((chksum & 1) ? 0x80 : 0) + (chksum >> 1) + shortname[j++]; 如果通過短文件名計算出來的校驗和與長文件名中的0xD偏移處數據不相等。系統(tǒng)無論如何都不會將它們配對的。 依據長文件名和短文件名對目錄項的定義,加上對簇的編號和鏈接,FAT32上數據的讀取便游刃有余了。 本文出自數據恢復網(www.),疏漏在所難免,希望指正。若需轉載請保留此信息;若需修改,請用以下方式與作者取得聯系 3、sjhf@ FAT文件系統(tǒng)總結MBR:Master Boot Record (主引導記錄) DBR:DOS Boot Record (DOS引導記錄,位于分區(qū)引導扇區(qū)) BPB:BIOS Parameter Block (BIOS參數塊) FAT:File Allocation Table (文件分配表) Sector:扇區(qū) Cluster:簇 一、硬盤組織結構 下面是一個包含 4 個分區(qū)的硬盤結構示意圖,其中分為 3 個基本分區(qū)和一個擴展分區(qū)。
二、FAT文件系統(tǒng)結構 FAT 文件系統(tǒng)是由按照如下順序排列的幾個部分組成的: 0 – Reserved Region 1 – FAT Region 2 – Root Directory Region (FAT32沒有這部分) 3 – File and Directory Data Region
FAT 系統(tǒng)的數據存儲采用小端(Little Endian)方式,注意到這一點很重要,在使用大 端(Big Endian)的系統(tǒng)中,讀取多字節(jié)數據的時候必須要經過轉換,否則,讀取到的數據是不正確的。 例如:一個 32-bit 數據 0x12345678 在 FAT 中的保存方式如下圖所示:
三、主引導扇區(qū) 硬盤主引導扇區(qū) = 硬盤主引導記錄(MBR)+硬盤分區(qū)表(DPT) MBR:扇區(qū)內偏移地址 0 ~ 0x1BD DPT:扇區(qū)內偏移地址 0x1BE ~ 0x1FD,其中又分為 4 個分區(qū)表: 第一個分區(qū)表:0x1BE ~ 0x1CD 第二個分區(qū)表:0x1CE ~ 0x1DD 第三個分區(qū)表:0x1DE ~ 0x1ED 第四個分區(qū)表:0x1EE ~ 0x1FD 每個分區(qū)表的信息如下表所示:
分區(qū)標志類型值及其含義
主引導扇區(qū) 上面是從一張 SD 卡讀到的主引導扇區(qū)信息??梢钥闯?,MBR 區(qū)域數據全部為 0,這張 SD 卡只有一個分區(qū),這個分區(qū)前的扇區(qū)數為 0x0000003F,所以這個分區(qū)的開始位置就是扇區(qū)0x0000003F,總扇區(qū)數為 0x000F1EC1(990913 個扇區(qū))。 四、分區(qū)引導扇區(qū) 也常常稱為啟動扇區(qū),Microsoft稱它為 0 扇區(qū)(0th sector),通過前面的介紹我們知 道,稱它為 0 扇區(qū)其實是不正確的,這樣容易讓人誤解它為磁盤的最前面一個扇區(qū),稱它為 0 扇區(qū)只是表明它是FAT中扇區(qū)的參考點而已。
該扇區(qū)中包含有我們關注的一個重要數據結構 BPB(BIOS Parameter Block)。以下表 格內容翻譯自 Microsoft 的《Microsoft Extensible Firmware Initiative FAT32 File System Specification—version1.03》,其中包含 BPB 各項的描述。 NOTE:在以下的敘述中,名字以 BPB_開頭的屬于 BPB 部分,以 BS 開頭的屬于啟動扇區(qū)(Boot Sector)部分,實際上并不屬于BPB。
FAT32 的 BPB 的內容和 FAT12/16 的內容在地址 0x36 以前是完全一樣的,從偏移量 0x36開始,他們的內容有所區(qū)別,具體的內容要看 FAT 類型為 FAT12/16 還是 FAT32,這點保證了 在啟動扇區(qū)中包含一個完整的 FAT12/16 或 FAT32 的 BPB 的內容,這么做是為了達到最好的兼容性,同時也為了保證所有的 FAT 文件系統(tǒng)驅動程序能正確的識別和驅動不同的 FAT 格式,并 讓他們良好地工作,因為他們包含了現有的全部內容從 offset 36 開始 FAT12/FAT16 的內容開始區(qū)別于 FAT32,下面分兩個表格列出,下 表為 FAT12/FAT16 的內容下表為 FAT32 的內容
關于 FAT 啟動扇區(qū)還有一點重要的說明,我們假設里面的內容是按字節(jié)排序的,那么扇區(qū)[510]的內容一定 0x55,扇區(qū)[511]的內容一定是 0xAA很多 FAT 資數文檔會把 0xAA55 說成是“啟動扇區(qū)最后兩字節(jié)的內容”,這樣的說法是正 確的,但僅僅適用于 BPB_BytsPerSec 值為 512 的情況。若 BPB_BytsSec 的值大于 512,該標記的位置并沒有改變,雖然在啟動扇區(qū)的最后兩個字節(jié)寫 0xAA55 并沒有問題。 五、FAT類型識別 FAT 的字類型(FAT12/16/32)只能通過 FAT 卷中的簇(Cluster)數來判定,沒有其 他的辦法。 Cluster 總數的計算: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 */ } 注意這里的簇數(count of Cluster)是指數據區(qū)所占簇的數量(the count of the data cluster),從簇 2 算起,而“最大可用簇數”(Maximun valid cluster number for the volume)是 CountofClusters +1,“包括保留簇的簇數”(count of cluster including the two reserved cluster)則為CountofClusters +2。根目錄占據的 Sector 數:RootDirSectors = (BPB_RootEntCnt*32) /BPB_BytsPerSec 數據區(qū)(Cluster 2)的起始 Sector: FirstDataSector = BPB_EsvdSecCnt + (BPB_NumFATs * FATSz) + RootDirSectors 給一個合法的簇號 N,該簇的第一個扇區(qū)號由下式計算:FirstSectorofCluster =((N -2) * BPB_SecPerClust) + FirstDataSecot; 因為 BPB_SecPerClus 總是 2 的整數次方,這意味著 BPB_SecPerSlus 的乘法運算可 以通過移動來進行。 NOTE:這里所說的 Sector 號,指的是針對卷中包 BPB 的第一個扇區(qū)(DBR)的偏移量(DBR設為Sector 0)。 六、FAT各部分位置的計算
DBR_Base:從DPT中偏移8Bytes的地址讀取4Bytes數據就可以直接看作是DBR所在的 Sector。 RsvSectors = BPB_RsvdSecCnt。 FAT_Base = DBR_Base + RsvSectors FATSize = BPB_NumFATs * FATSz 其中,FATSz = (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表結構 FAT 表(File Alloacation Table)是一一對應于數據簇號的列表。文件系統(tǒng)分配磁盤空間按簇來分配。因此,文件占有磁盤空間時,基本單位不是字節(jié)而是簇, 即使某個文件只有一個字節(jié),操作系統(tǒng)也會給它分配一個最小單元:即一個簇。為了可以將磁盤 空間有序地分配給相應的文件,而讀取文件的時候又可以從相應的地址讀出文件,我們可以把數 據區(qū)空間分成 BPB_BytsPerSec*BPB_SecPerClus 字節(jié)長的簇來管理,FAT 表項的大小與 FAT 表的類型有關,FAT12 的表項為 12bit ,FAT16 為 16bit, 而 FAT32 則為 32bit。對 于大文件,需要分配多個簇。同一個文件的數據并不一定完整地存放在磁盤中一個連續(xù)地區(qū)域 內,而往往會分若干段,像鏈子一樣存放。這種存儲方式稱為文件的鏈式存儲。為了實現文件的 鏈式存儲,文件系統(tǒng)必須準確地記錄哪些簇已經被文件占用,還必須為每個已經占用的簇指明存 儲后繼內空的下一個簇的簇號,對于文件的最后一簇,則要指明本簇無后繼簇。這些都是由 FAT 表來保存的,FAT 表的對應表項中記錄著它所代表的簇的有關信息:諸如是空,是不是壞簇,是 否是已經是某個文件的尾簇等。 以 FAT16 為例說明 FAT 的結構如下:
FAT各系統(tǒng)記錄項的取值含義(16進制)
八、長文件名 長文件名是在原有的 FAT 系統(tǒng)上引入的,在只支持短文件名的系統(tǒng)上,長文件名就像是不存在一樣。為了達到這個目標,長文件名通過在原有的目錄項中引入新的屬性字(Attribute)得以實現。 ATTR_LONG_NAME = ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID 判斷一個目錄項是否為長文件名,要通過下面 MASK 實現: ATTR_LONG_NAME_MASK = ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID | ATTR_DIRECTORY | ATTR_ARCHIVE 長文件名目錄項數據結構如下:
長文件名字符采用 Unicode 編碼。 Checksum 算法,用 C 語言實現如下: //------------------------------------------------------------------------ //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》中給出的示例: 假設創(chuàng)建一個名為“The quick brown.fox”的文件,系統(tǒng)將為它建立如下的目錄項:
九、目錄結構 目錄所在的扇區(qū),都是以32 Bytes 劃分為一個單位,每個單位稱為一個目錄項(Directory Entry),即每個目錄項的長度都是 32 Bytes。 FAT32短文件目錄項32個字節(jié)的表示定義FAT32 表每簇占用 4 Bytes,從 Sector 內偏移地址 50 讀取 4 Bytes 數據為 0x15,表 示之后的數據占用簇0x15,簇0x15 在Sector 內偏移地址為0x54,從地址0x54 讀取4 Bytes。數據為 0x16,依次類推,最后在偏移地址 0x68 讀取 4 Bytes 數據為 0x0FFFFFFF,表示文 件在該簇就結束了。所以該文件占用的簇號依次為 0x14,0x15,0x16,0x17,0x18,0x19 和 0x20。 |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|