FAT16 與 FAT32 的結構
FAT16 和 FAT32 分割區的結構類似。每個 FAT16 分割區依序可分為四部份:保留區域 ( reserved region )、FAT 區域 ( FAT region )、根目錄區域 ( root directory region )以及資料區域 ( file and directory data region )四部份;對 FAT32 分割區而言,沒有根目錄區域,其餘均相同,FAT32 分割區把根目錄視為一個目錄,包含在資料區域內。底下就先探討啟動區域。
保留區域
FAT16 分割的保留區域僅僅含一個磁區,就是啟動磁區 ( boot sector );FAT32 分割的保留磁區中除了第一個磁區是啟動磁區外,還包含了檔案系統資訊磁區和啟動磁區的備份。
啟動磁區以及啟動磁區內的 BPB ( BIOS parameter block )
FAT16 和 FAT32 的啟動磁區 ( 啟動磁區在實體硬碟的位置,請參閱前一章,對邏輯磁碟而言,啟動磁區是編號 0 的邏輯磁區 ) 裏都有 BPB 資料,BPB 裏面存有這個分割裏的重要資訊,而 FAT16 分割的 BPB 內容與 FAT32 分割大同小異,比較如下表:( 亦可參閱第 18 章 )
名稱 | 位址範圍 | 大小 位元組 | 所代表意義 | ||
FAT16 | FAT32 | FAT16 | FAT32 | ||
BS_jmpBoot | 00-02 | 3 | 跳躍指令 | 同左 | |
BS_OEMName | 03-0A | 8 | 廠商軟體名 | ||
BPB_BytsPerSec | 0B-0C | 2 | 每一磁區的位元組數 | 同左 | |
BPB_SecPerClu | 0D | 1 | 每一磁叢所佔磁區數 | ||
BPB_RsvdSecCnt | 0E-0F | 2 | 保留磁區數 | ||
BPB_NumFATs | 10 | 1 | FAT 份數 | ||
BPB_RootEntCnt | 11-12 | 2 | 根目錄所含 FDB 數 | FAT32 不使用此欄 | |
BPB_TotSec16 | 13-14 | 2 | 邏輯磁區總數,若為零表示此硬碟大於 32MB,而在位址 20-23 記錄真正的邏輯磁區總數 | ||
BPB_Media | 15 | 1 | 磁碟種類 | 同左 | |
BPB_FATSz16 | 16-17 | 2 | 每份 FAT 所佔磁區數 | FAT32 不使用此欄位故此欄為零,而在位址 24-27 表示每份 FAT 磁區數 | |
BPB_SecPerTrk | 18-19 | 2 | 每一磁軌之磁區數 | 同左 | |
BPB_NumHeads | 1A-1B | 2 | 磁頭數 | ||
BPB_HiddSec | 1C-1F | 4 | 隱藏磁區數 | ||
BPB_TotSec32 | 20-23 | 4 | 邏輯磁區總數 | ||
BPB_FATSz32 | FAT16 沒有這些欄位 | 24-27 | 4 | FAT16 沒有這些欄位 | 每份 FAT 所佔磁區數 |
BPB_ExtFlags | 28-29 | 2 | 延伸旗標 | ||
BPB_FSVer | 2A-2B | 2 | 檔案系統版本 | ||
BPB_RootClus | 2C-2F | 4 | 根目錄的第一個磁叢編號 | ||
BPB_FSInfo | 30-31 | 2 | 檔案系統資訊磁區 | ||
BPB_BkBootSec | 32-33 | 2 | 備份啟動磁區磁區 | ||
BPB_Reserved | 34-3F | 12 | 保留 | ||
BS_DrvNum | 24 | 40 | 1 | 磁碟機編號 | 同左 |
BS_Reserved1 | 25 | 41 | 1 | 保留 | |
BS_BootSig | 26 | 42 | 1 | 特徵碼 | |
BS_VolID | 27-2A | 43-46 | 4 | 磁碟序號 | |
BS_VolLab | 2B-35 | 47-51 | 11 | 磁碟標籤 | |
BS_FilSysType | 36-3D | 52-59 | 8 | 字串『FAT16 』 | 字串『FAT32 』 |
3E-1FD | 5A-1FD | 啟動程式碼 | 同左 | ||
1FE-1FF | 2 | 0AA55H,啟動磁區的結束識別碼 |
上表中,位址範圍是以十六進位制表示,各欄位大小是以十進位表示,而其中的欄位以藍色為底色的是 BPB。從上表大約可看出,FAT16 分割與 FAT32 分割的 BPB 大致一樣,但是因為 FAT32 需要管理較大的邏輯硬碟容量,所以增加了一些欄位。FAT16 的 BPB 是在啟動磁區的 0BH~23H,共 25 ( 即十六進位的 19H ) 個位元組,在這範圍以外也提供了一些資訊,但並不真正屬於 BPB。
同樣的,FAT32 的 BPB 是在啟動磁區的 0BH~3FH,共 53 ( 即 35H ) 個位元組,在此範圍之外,也有一些資訊,但也不是屬於 BPB。底下是啟動磁區的說明:
BS_jmpBoot: | 0H~2H,共 3 個位元組的跳躍指令,使程式跳到啟動程式處執行。跳躍指令有三種:短程跳躍 ( 跳躍範圍在 128 個位元組以內,機械碼是 0EBH )、近程跳躍 ( 跳躍範圍在一個區段內,即 64K 內,機械碼是 0E9H )、遠程跳躍 ( 區段之間的跳躍,機械碼是 0EAH )。一般而言,啟動磁區的跳躍指令不超過 128 個位元組,故均為 0EBH,其後接跳躍目的地離跳躍指令的位址差,此差值僅一個位元組,不過此欄位共有三個位元組,故最後再接上一個不做任何事的 CPU 指令,NOP,其機械碼是 90H。 | ||||||
BS_OEMName: | 在位址 03H~0AH,共 8 個位元組,這是格式化此分割區的作業系統所填入的廠商名稱。例如如果用 Win 95 格式化分割區,此欄填入『MSWIN4.0』字串;如果用 Win 95 OSR2 或用 Win 98 或 Win 98SE 格式化,此欄填入『MSWIN4.1』;如果用 Win 2000 格式化,此欄填入『MSDOS 5.0』。 | ||||||
BPB_BytsPerSec: | 位址 0BH~0CH 裏存放每磁區所含位元組數,一般為 512 個位元組,但也可以是 1024, 2048 或 4096 數值,不過微軟不建議使用除了 512 以外的數,小木偶也沒見過,所以我們可以大膽假設 BPB_BytsPerSec 就等於 512。 | ||||||
BPB_SecPerClu: | 位址 0DH 這個位元組表示每一磁叢所佔磁區數,但是要注意不能使每磁叢所含位元組數大於 65536,亦即 BPB_SecPerClu 乘以 BPB_BytsPerSec 不能大於 65536。因此,當 BPB_BytsPerSec 等於 512 時,BPB_SecPerClu 可以是 1、2、4、8、16、32、64、128 等數值。 | ||||||
BPB_RsvdSecCnt: | 位址 0EH~0FH 這個字組表示保留區域的磁區數,對 FAT12 或 FAT16 分割而言,保留區域的磁區數必為一,因為其保留區域只有僅僅含一個啟動磁區而已。但對 FAT32 分割而言,除了啟動磁區外,還有檔案系統資訊磁區和備份啟動磁區,故通常不是 1,而是 20H。 | ||||||
BPB_NumFATs: | 位址 10H 這個位元組表示 FAT 份數。不管是 FAT12、FAT16、FAT32 都有兩份檔案配置表 ( FAT )。 | ||||||
BPB_RootEntCnt: | 位址 11H~12H 字組表示根目錄所含 FDB 項數,即根目錄裏可以存放的檔案名稱及子目錄名稱之總和,FAT12/16 不能超過 BPB_RootEntCnt;但是對 FAt32 而言,此欄位不用,填上 0。 | ||||||
BPB_TotSec16: | 13H~14H 這個字組表示邏輯磁區總數,但最多只能表示 0FFFFH,所以若一個磁碟的邏輯磁區大於 0FFFFH ( 或是說磁碟容量大於 32MB,32MB 是 10000H*512D 來的 ),則此欄位為 0,邏輯磁區總數記錄在 20H 處開始的雙字組。因為 FAT32 分割必定大於 32MB,故此欄位不使用,均填入 0。 | ||||||
BPB_Media: | 位址 15H 這個位元組表示磁碟種類。此欄位和 FAT 的第一個位元組一樣,其意義如下表:
| ||||||
BPB_FATSz16: | 位址 16H~17H 這個字組表示每份 FAT 所佔磁區數;但對 FAT32 而言,此欄位不用,填上 0,而每份 FAT 所佔磁區數記載於位址 24H~27H 處的雙字組。 | ||||||
BPB_SecPerTrk: | 位址 18H~19H 這個字組代表每一磁軌之磁區數。 | ||||||
BPB_NumHeads: | 位址 1AH~1BH 這個字組代表磁頭數。 | ||||||
BPB_HiddSec: | 位址 1CH~1FH 這個雙字組 ( 共 4 個位元組 ) 表示隱藏磁區數,與分割表或擴充分割表中的分割區相對位置的數值一樣。 | ||||||
BPB_TotSec32: | 位址 20H~23H 這個雙字組代表邏輯磁區總數。如果 16H~17H 欄為 0,則此欄不可為零。 |
在啟動磁區偏移位址 24H~3DH 的範圍,FAT 12/16 的欄位和 FAT32 差異很大,因此分開說明,底下先看看 FAT 12/16 的欄位,這些並不是真正的 BPB 內容,早期的作業系統或格式化程式並不支援底下的欄位,所以使用時得小心這些限制。
事實上 FAT32 也有這些欄位,意義也相同。只是 FAT32 的 BPB 較長,而這些欄位 ( 包含磁碟機編號、保留、特徵碼等六個欄位 ) 不屬於 BPB,所以 FAT32 是放在 FAT32 的 BPB 之後,也就是 40H 之後。
BS_DrvNum: | 這個位元組代表磁碟機編號,FAT12/16 是在位址 24H,而 FAT32 是在位址 40H 。若為硬碟,BS_DrvNum 為 80H;若為軟碟,BS_DrvNum 為 0。 |
BS_Reserved1: | 此位元組保留,需填入 0。對 FAT12/16 而言,在位址25H;對 FAT32 而言在位址 41H。 |
BS_BootSig: | 此位元組表示特徵碼,和系統有關,如果是用微軟的作業系統,此欄位是 29H,此外這個特徵碼也表示下面三個欄位有意義。對 FAT12/16 而言 BS_BootSig 在位址 26H;對 FAT32 而言在 42H。 |
BS_VolID: | 磁碟序號,共佔用 4 個位元組,不管是 FAT16 或 FAT32 所表示意義一樣,都是在 MS-DOS 模式用 dir 指令時顯示的『Volume Serial Number』如下圖。但 FAT12/16 存於位址 27H~2AH;FAT32 則存於 43H~46H。 圖一 |
BS_VolLab: | 磁碟標籤,共佔用 11 個位元組,不管是 FAT16 或 FAT32 所表示意義一樣,都是在 MS-DOS 模式用 dir 指令時顯示的『Volume』,如上圖。但 FAT12/16 存於位址 2BH~35H;FAT32 則存於 47H~51H。 |
BS_FilSysType: | 字串『FAT 』、『FAT12 』、『FAT16 』或『FAT32 』其中之一。但是一般不以此欄位判斷是那一個檔案系統,因為早期的作業系統格式化時,BPB 並沒有這一欄,稍後再說明如何判斷檔案系統。FAT12/16 存於 36H~3DH;FAT32 則存於 52H~59H。 |
底下位址 24H~3FH 是 FAT32 的擴充 BPB,其欄位意義與 FAT16 的意義不同,所以在同時支援 FAT32 及 FAT16 的作業系統,如 Win 95 OSR2/Win98/Me/2K/XP 等,得先判斷邏輯硬碟屬於那一種檔案系統,是 FAT16 抑或 FAT32。
BPB_FATSz32: | 位址 24H~27H 內的雙字組表示每份 FAT 所佔磁區數,因為 FAT32 容量大,磁叢也多,所以用 4 個位元組表示每份 FAT 所佔磁區數。 |
BPB_ExtFlags: | 位址 28H~29H 內的字組表示延伸旗標。 |
BPB_FSVer: | 位址 2AH~2BH 內的字組表示檔案系統版本,2BH 表示主要版本,2AH 表示次要版本。 |
BPB_RootClus: | 位址 2CH~2FH 內的雙字組表示根目錄的第一個磁叢編號。 |
BPB_FSInfo: | 位址 30H~31H 裏的字組存放著檔案系統資訊磁區距離啟動磁區有多少磁區,通常是 1 個磁區,也就是啟動磁區之後緊接著案系統資訊磁區。換言之,案系統資訊磁區是編號 1 的邏輯磁區。 |
BPB_BkBootSec: | 位址 32H~33H 內的字組存放著備份啟動磁區距離啟動磁區有多少磁區,通常是 6,微軟建議最好不要是 6 以外的數。換言之,啟動磁區是由邏輯磁區 6 開始。 |
BPB_Reserved: | 位址 34H~3FH 共 12 個位元組保留不使用,作為後續擴充之用,均填入 0。 |
檔案系統資訊磁區與備份啟動磁區
FAT32 的保留區域除了啟動磁區外,還有檔案系統資訊磁區及備份啟動磁區,底下來看看這兩個磁區。檔案系統資訊是為了能快速計算剩餘磁碟空間用的。在 FAT12/16 等磁碟容量不大的時代,要計算剩餘磁碟容量一般是讀取 FAT,並計算還有那些 FAT 可用 ( 註一 ),但到了 FAT32 時代,FAT 變得很大,所以得另闢捷徑才行。
名稱 | 位址範圍 | 大小 位元組 | 所代表意義 |
FSI_LeadSig | 0H~3H | 4 | 引導記號,為一雙字組數值,等於 16 進位的 41615252H,表示此磁區為檔案系統資訊。 |
FSI_Reserved1 | 4H~1E3H | 1E0H | 保留不用,均為 0 |
FSI_StrucSig | 1E4H~1E7H | 4 | 另一個雙字組的數值,61417272H,表示下一雙字組為可用的磁叢數。 |
FSI_Free_Count | 1E8H~1EBH | 4 | 表示此邏輯磁碟上可用的磁叢數,亦即在 MS-DOS 模式中用 DIR 指令時顯示的『bytes free』,如上圖一中但藍色框框框起來的,可用位元組數應等於 BPB_BytsPerSec*BPB_SecPerClu*FSI_StrucSig。如果 FSI_Free_Count 為 0FFFFFFFFH,表示系統尚未計算出可用磁叢數。 |
FSI_Nxt_Free | 1ECH~1EFH | 4 | 第一個未使用的 FAT 編號。如果 FSI_Nxt_Free 為 0FFFFFFFFH,表示系統尚未計算出第一個未使用的 FAT 編號。 |
FSI_Reserved2 | 1F0H~1FBH | 12 | 保留,均填入 0。 |
FSI_TrailSig | 1FCH~1FFH | 4 | 檔案系統資訊磁區結束的記號,為一雙字組,0AA550000H。 |
在檔案系統資訊磁區之後,有數個不知做為何用的磁區,小木偶手邊也沒有資料,所以也不知其所代表的意義。但在第 6 個邏輯磁區,就是啟動備份磁區,它是啟動磁區、檔案系統資訊磁區以及後面 4 個不知何用的磁區的拷貝,內容跟這六個磁區一樣,所以也得佔用 6 個磁區。接下來的保留區域,還有數個磁區,但觀其內容均為 0,也沒有資料可查。
如何判斷 FAT12、FAT16,還是 FAT32?
如果您寫一個程式要低階的讀取檔案內容 ( 亦即不靠 DOS 中斷服務或 Windows API ),那麼您得先判斷此邏輯分割屬於那個檔案系統?是 FAT12、FAT16 還是 FAT32?
要判斷檔案系統,一般的方法,也是微軟建議的方法是檢查磁叢總數,第 18 章提到當磁叢數小於 4085 時為 FAT12;大於或等於 4085 且小於 65525 時為 FAT16;大於或等於 65525 為 FAT32。而磁叢總數則等於資料區域的磁區總數除以每個磁叢所含磁區數,而資料區域的磁區數是邏輯分割總磁區數減去保留區域、FAT 區域、根目錄區域的磁區數。可寫成下面數學算式:
TotSec = 邏輯磁區總數
FATSz = 每份 FAT 的磁區數
DataSec = 資料區域的磁區總數
= TotSec - BPB_ResvdSecCnt - ( BPB_NumFATs * FATSz )
- RootDirSectors ...... (1)
磁叢總數 = CountofClusters = DataSec / BPB_SecPerClus ...... (2)
邏輯磁區總數 ( TotSec ) 可由 BPB 裏的 BPB_TotSec16 或 BPB_TotSec32 取得。依據 BPB 的資料,若 BPB_TotSec16 為零,則邏輯磁區總數存於 BPB_TotSec32 裏,否則存於 BPB_TotSec16 內。
FAT 區域的磁區數是每份 FAT 所佔磁區數與 FAT 份數之乘積,但是前面曾經提到,FAT12/FAT16 與 FAT32 存放每份 FAT 所佔磁區數的位置不同,因此得先看看 BPB_FATSz16 是否為零,若為零則每份 FAT 所佔磁區數存放 BPB_FATSz32 處,若不為零則每份 FAT 所佔磁區數存放於 BPB_FATSz16 處。
到此,所需資料皆已具備,僅剩下根目錄區域的磁區數 ( RootDirSectors ) 了,在整個 BPB 裏,和根目錄有關的是 BPB_RootEntCnt,此字組記錄著根目錄裏的檔案描述區塊 ( FDB ) 總數。而每個 FDB 佔有 32 個位元組,因此根目錄所佔位元組大小為 BPB_RootEntCnt*32,再除以每磁區所佔位元組數 ( BPB_BytsPerSec ) 即得根目錄區域的磁區數:
RootDirSectors =
( BPB_RootEntCnt * 32 + BPB_BytsPerSec - 1 )/BPB_BytsPerSec ...... (3)
對 FAT32 而言,BPB_RootEntCnt 為零,故式 (3) 所得結果為零,符合 FAT32 沒有根目錄區域;但對 FAT16 或 FAT12 而言,因為 BPB_RootEntCnt 不為零,所以結果也不會是零。至於加上 BPB_BytsPerSec - 1 的目的是為使被除數不為零,但加或不加無所謂。依據上述結論,我們可以寫成下列組合語言程式:
.386
;***********************************************************
data segment
drv_bpb bpb <?>
data ends
;***********************************************************
code segment public 'code' use16
assume cs:code,ds:data
;-----------------------------------------------------------
fat_type proc near
;由 bpb_drv 判斷此分割為 FAT12、FAT16 還是 FAT32
;輸出:CL=12H 表示 FAT12
; CL=16H 表示 FAT16
; CL=32H 表示 FAT32
fat_type proc near
sub edx,edx
sub eax,eax
mov ecx,edx
mov ax,drv_bpb.BPB_FATSz16
or ax,ax
jnz fat_ok
mov eax,drv_bpb.BPB_FATSz32
fat_ok: mov cl,drv_bpb.BPB_NumFATs
mul ecx
mov total_FAT_sector,eax ;EAX=所有 FAT 所佔磁區數
mov ax,drv_bpb.BPB_RootEntCnt
mov cx,drv_bpb.BPB_BytsPerSec
shl eax,5
add eax,ecx
sub edx,edx
dec eax
div ecx ;EAX=根目錄所佔磁區數
mov dx,drv_bpb.BPB_TotSec16
or dx,dx
jnz total_sector_in_edx
mov edx,drv_bpb.BPB_TotSec32
mov total_sector,edx
total_sector_in_edx: ;EDX=邏輯磁區總數
sub edx,eax
sub edx,total_FAT_sector
mov ax,drv_bpb.BPB_RsvdSecCnt
cwd ;EAX=保留磁區數
sub edx,eax ;EDX=資料區域的磁區數
sub eax,eax
xchg eax,edx
mov cl,drv_bpb.BPB_SecPerClu
mov ch,0
div ecx ;EAX=磁叢總數
mov cl,32h
cmp eax,65525
jae got_it
mov cl,16h
cmp eax,4085
jae got_it
mov cl,12h
got_it: ret
fat_type endp
;-----------------------------------------------------------
資料區域的第零個邏輯磁區編號
資料區域是在保留區域、FAT 區域及根目錄區域之後,因此資料區域的第零個邏輯磁區編號 ( FirstDataSector ) 也就是這些區域磁區數之和:
FirstDataSector = BPB_RsvdSecCnt + BPB_NumFATs*FATSz + RootDirSectors ... (4)
FAT 區域
在保留區域之後,就是稱為檔案配置表 ( File Allocation Table ) 的區域。這塊區域記載著每個檔案佔據了那些磁叢 ( cluster ),磁叢是由數個連續的邏輯磁區組成,其大小是隨著磁碟容量以及 DOS 版本不同而變。每當作業系統寫入資料到磁碟上時,是以磁叢為單位,為檔案配置佔用的磁叢,換句話說,每個檔案都佔有整數個磁叢。
那麼 FAT 怎樣記錄檔案佔用了那些磁叢呢?在第 18 章中曾對 FAT12 有過詳細的介紹,FAT16 或 FAT32 記錄檔案佔有那些磁叢的原理與 FAT12 相同。對 FAT12、FAT16、FAT32 這幾種檔案系統而言,每個磁叢都以一個編號表示,這些編號是由記錄在 FAT 區域裏的位置決定,而各種檔案系統的磁叢編號所佔位元長度不同,您可以由它們的名稱很容易就得知,FAT12 的磁叢編號是 12 個位元 ( 即 1.5 個位元組 ),FAT16 的磁叢編號是 16 個位元 ( 即 2 個位元組 ),FAT32 的磁叢編號是 32 個位元 ( 即 4 個位元組 )。
在第 18 章已看過 FAT12 的 FAT 區域,底下來就來看看另一個不同的例子,一個 FAT32 的 FAT 區域,此邏輯硬碟是小木偶 Windows 98 SE 的啟動硬碟。小木偶將藉著這個例子來說明 FAT32 中,FAT 內欄位與邏輯磁區的關係以及 FAT 各欄位的意義。
第 N 個 FAT 欄位與其對應的邏輯磁區
首先先在 MS-DOS 模式底下輸入 DEBUG,( 照往例,黃色的是小木偶輸入的指令,[Enter] 表示按下 Enter 鍵,其餘都是電腦的回應 ) 接著載入 C: 邏輯硬碟的啟動磁區:
C:\WINDOWS>DEBUG [Enter]
-L 100 2 0 1 [Enter]
DEBUG 的 L 指令可載入磁區,『100』表示載入後的磁區資料放在位址 100H 處,『2』表示『C:』磁碟,『0』表示載入第零磁區 ( 邏輯磁區皆由 0 開始 ),『1』表示載入磁區數。載入後要尋找 FAT 區域,照前面所說,FAT 區域在保留區域之後,因此知道保留區域多大就知道 FAT 區域由那個磁區開始。因此由螢幕印出 BPB:
-D 100 13F [Enter]
136C:0100 EB 58 90 4D 53 57 49 4E-34 2E 31 00 02 08 20 00 .X.MSWIN4.1... .
136C:0110 02 00 00 00 00 F8 00 00-3F 00 FF 00 80 60 1F 00 ........?....`..
136C:0120 7E 04 7D 00 32 1F 00 00-00 00 00 00 02 00 00 00 ~.}.2...........
136C:0130 01 00 06 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
-L 300 2 20 1 [Enter]
保留磁區大小是在 BPB_RsvdSecCnt 處,亦即 010E 起的一個字組 ( 因為存放啟動磁區的資料起始於位址 0100H 處,故 BPB_RsvdSecCnt 在 010E 處,即上面天藍色處 ),然後載入 FAT 區域的第一個磁區,如上『L 300 2 20 1』,這裏的 20 就是保留磁區大小。然後輸入『D 300』觀察 FAT:
對 FAT32 來說,每個 FAT 欄位佔了 32 位元,亦即一個雙字組,每個欄位以一個編號表示。位址 0300~0303H 的雙字組是 FAT 的第 0 個欄位,即編號 0;0304~0307H 是第 1 個欄位,即編號 1;0308~030BH 是第 2 個欄位,即編號 2;……依此類推。第零個欄位的最低位元組是 0F8H,與 BPB_Media 相同,其餘位元均設為一。第一個欄位的位元均設為一,這兩個 FAT 欄位均為系統使用。
從第 2 個 FAT 欄位才開始做為儲存資料,所以資料區域的磁叢編號也是 2 開始,那麼編號為二的磁叢是由那一個邏輯磁區開始呢?或者可問第 2 個 FAT 代表那些邏輯磁區呢?
先看看底下的圖,可能比較好瞭解:
FirstSectorofCluster = ( N - 2 ) * BPB_SecPerClu + FirstDataSector .... (5)
在這個例子裏,根目錄區域所佔磁區數為零,保留區域所佔磁區數為 20H,兩份 FAT 區域共佔有 1F23H*2,即 3E64H,因此這三個區域共佔 3E84H,故資料磁區的第零個邏輯磁區編號為 3E84H,而連此磁區以及其後的磁區,共 8 個磁區是編號 2 磁叢所含有的磁區;再接著的 8 個磁區則是編號 3 磁叢的磁區……。
FAT 欄位內容之意義
前面已解釋過 FAT 第 0、1 欄位內之數值意義,它們是供系統使用。而其他 FAT 欄位內的數值是代表什麼意義呢?請看下表:
FAT12 | FAT16 | FAT32 | 意 義 |
000 | 0000 | 0000000 | 此 FAT 欄位對應的磁叢並未使用 |
3~FF6 | 3~FFF6 | 3~FFFFFF6 | 表示此檔案的下一個磁叢編號 |
FF7 | FFF7 | FFFFFF7 | 此 FAT 欄位所對應的磁叢已損壞,不可使用 |
大於或等於 FF8 | 大於或等於 FFF8 | 大於或等於 FFFFFF8 | 此 FAT 欄位所對應的磁叢為檔案的最後一個磁叢 |
我想還是延續上面的例子說明好了。剛提過,小木偶的 Windows 98 SE 的啟動硬碟中,第一個資料磁區是 3E84,載入到記憶體觀察看看:
-L 500 2 3E84 1 [Enter]
-D 500 [Enter]
136C:0500 53 55 48 44 4C 4F 47 20-44 41 54 03 00 00 00 00 SUHDLOG DAT.....
136C:0510 00 00 43 33 00 00 4C 6C-6F 30 03 00 4E 14 00 00 ..C3..Llo0..N...
136C:0520 42 4F 4F 54 4C 4F 47 20-54 58 54 22 00 00 00 00 BOOTLOG TXT"....
136C:0530 00 00 43 33 00 00 93 6D-6F 30 05 00 6E B5 00 00 ..C3...mo0..n...
136C:0540 46 52 55 4E 4C 4F 47 20-54 58 54 20 00 95 7A 6C FRUNLOG TXT ..zl
136C:0550 6F 30 43 33 00 00 92 6C-6F 30 11 00 ED 03 00 00 o0C3...lo0......
136C:0560 57 49 4E 39 38 53 45 20-20 20 20 28 00 00 00 00 WIN98SE (....
136C:0570 00 00 6F 30 00 00 58 6B-6F 30 00 00 00 00 00 00 ..o0..Xko0......
看起來似乎是一些檔案名稱及某些資料,的確,這就是根目錄磁區。由 BPB_RootClus 得知 ( 見上面 BPB 白色處 ),根目錄是在編號 2 的磁叢處,代入式 (5),便可得根目錄磁區佔據編號 3E48 的邏輯磁區起,共 8 個磁區。根目錄是不是只佔據了這 8 個磁區呢?可以由 FAT 區域的第 2 個欄位得知,該欄位是 0FFFFFFF ( 見上面黃線框起來處 ),就知道根目錄結束於此。
再舉另一例說明。見根目錄 SUHDLOG.TXT 這個檔案的磁叢指位器指的是 3 ( 上面紅色字 ),表示此檔由編號 3 的磁叢開始存放,亦可代入式 (5) 求得邏輯磁區,而 FAT 區域的第 3 個欄位內的數值是 4,表示此檔不只佔據一個磁叢,還佔據編號 4 的磁叢,再看 FAT 區域的第 4 個欄位是結束記號,表示此檔就佔有這兩個連續的磁叢。因此您可以把一個檔案所含的 FAT 欄位視為一連串的鏈,此鏈的開端是在 FDB,而中間是靠 FAT 裏的欄位內容連接,尾端是以 FFFFFF8 結尾 ( 對 FAT32 而言 )。
取得編號 N 的 FAT 欄位內容
由上面的說明,應當明白,取得 FAT 欄為裏的內容是很重要的,因為 FAT 欄位裏的內容是下一個檔案磁叢所在,或是結束記號,能取得 FAT 欄位內容,才能存取整個檔案。底下來看看怎麼樣取得編號為 N 的 FAT 欄位內容。由下面的 FAT 區域部份內容
mov eax,N ;第 N 個 FAT 欄位
.if fat_type==12h
mov ecx,eax
shr ecx,1 ;ECX = EAX/2
add eax,ecx ;EAX = EAX + EAX/2,即 1.5*N
.elseif fat_type==16h
shl eax,1
.elseif fat_type==32h
shl eax,2
.endif ;執行完後,EAX 為第 N 個編號的 FAT 欄
;位距離 FAT 區域起始有多少個位元組
而每 512 個位元組組成一磁區,因此執行完上面程式的 EAX 再除以 512,就可求得 FAT 區域的第幾個磁區,而餘數所指的位址之數值就是第 N 個 FAT 欄位的內容。此數值的長度與 FAT 型態有關,若為 FAT32 則必須取得 32 位元長,若為 FAT16 則必須取得 16 位元長,若為 FAT12 則必須取得 12 位元長。對 FAT32/16 而言,只要取得實際長度即可,但是 FAT12 的欄位有 1.5 位元組長,比較麻煩,一般是依照下面公式計算:
movzx eax,word ptr fs:[bx] ;BX = EAX 除以 512 後的餘數
.if fat_type==12h
test N,1
jz even_n
shr eax,4
even_n: and ax,0fffh
.elseif fat_type==16h
.elseif fat_type==32h
mov eax,dword ptr fs:[bx]
and eax,0fffffffh
.endif
執行完後,EAX 就是第 N 個磁叢編號之內容。
根目錄區域
對 FAT12/16 檔案系統而言,FAT 區域之後是根目錄區域,根目錄是最上層的目錄,所有的子目錄或檔案都是以這個目錄為基準點存放的。根目錄裏記載著許多檔案的資料,例如檔名、建立日期時間、起始磁叢等等,每個檔案都以 32 個位元組記錄這些資料,稱為檔案描述區塊 ( FDB )。
根目錄是在磁碟格式化的時候就已建立的目錄,其所佔有的磁區數無法改變,所以存放在根目錄裏的檔案或子目錄總數是有限制的,這個總數存放於 BPB 裏的 BPB_RootEntCnt 欄位裏。因為這樣,所以如果在一個很大的磁碟機裏,如果不建立子目錄的話,當根目錄的 FDB 耗盡,即使資料區域還有空間,也無法存入資料。
不過經由建立子目錄可以迴避這個限制,此外也可以使檔案分門別類存放,可說好處多多。子目錄與根目錄相同都是由許多 FDB 組成,不過有兩點不同:
- 子目錄的前兩個 FDB 檔名必定是『.』和『..』,分別代表這個子目錄以及上一層目錄。
- 子目錄是屬於資料區域,其長度可以改變。
當新建立一個子目錄時,系統會分配一個磁叢記錄這個子目錄的所有 FDB 資料,而且會在這個子目錄的上層目錄中,以一個 FDB 記錄這個子目錄的資料,包含起始磁叢等等,而起始磁叢在 FAT 欄位會先記錄成 結束記號。當這個子目錄的檔案漸多,以致使得一個磁叢不夠用時,系統可以再分配另一磁叢給這個子目錄,同時修改起始磁叢的 FAT 欄位,使其指向被新分配到的磁叢。系統以上述方法使子目錄裏的檔案數只受磁碟容量的限制。
對 FAT32 檔案系統而言,並沒有所謂的根目錄區域,而是把根目錄看成是資料區域的一部份,根目錄就像是一個比較特別的子目錄一樣。FAT32 檔案系統建立根目錄時僅分配一個磁叢給根目錄,而這個磁叢會存入 BPB 的 BPB_RootClus 欄位,當根目錄 FDB 不夠時,系統會比照子目錄的方式再分配一個磁叢給根目錄,因此根目錄檔案不會有限制。
8.3 檔名格式與 VFAT
在 DOS 6.x 及其之前的 DOS 版本,可能為了磁碟容量或其他理由,檔案名稱有許多規則,或許您也可以說是限制。這些規則是:
- 檔名通常可以分為主檔名以及副檔名,中間以『.』分隔。副檔名可有可無,但副檔名常代表這個檔案的用途、性質等等,例如可執行檔是以 .EXE 表示。
- 主檔名最多只能有八個 ASCII 字元組成,副檔名最多只能有 3 個 ASCII 字元組成,並且兩者皆不能用特殊符號例如『\』、『|』等 ( 這些被當做 DOS 的特殊字元,如重新導向、管線等 ),也不能用 ASCII 字元小於 32 以下的數值,例如 Carrier Return、Escape 等字元。
由於主檔名、副檔名最多為 8、3 個 ASCII 字元,故稱為『8.3 檔名格式』或者稱為短檔名。當然,這些限制式很不合理的,所幸到了 Windows 95 上市時,推出了 VFAT。雖然有點兒晚,但是總比沒有來得好。
VFAT 除了可使用長檔名 ( LFN,Long File Name,最多可達 255 位元組 ) 外,還有幾項重大的性質,一是檔名使用萬國碼 ( UniCode,請參閱附錄十 ) 編碼而不再用 ASCII 碼 ( 這使得檔名可以用中文、阿拉伯文等世界上大多的文字作為檔名 );二是相容於原有的 FAT12/16/32 檔案系統,也就是說在 Windows 9x/Me 系統下,原來用 DOS 格式化的邏輯磁碟也可以用 VFAT。要達到這些目的,微軟是怎麼做到的呢?
原來微軟修改了 FDB,變成兩種形式,一種即短檔名形式,與原來的相同;另一種是長檔名形式,專門用來儲存長檔名的。假如某個檔案的檔名符合『8.3 檔名格式』,那麼這個檔案就只有一個短檔名形式的 FDB,而沒有長檔名形式的 FDB;如果某個檔案的檔名不符合『8.3 檔名格式』,那麼這個檔案就有一個短檔名形式的 FDB 與一個或數個長檔名形式的 FDB 兩種 FDB,長檔名形式的 FDB 只儲存長檔名,而檔案的建立日期、長度、起始磁叢等仍儲存於短檔名形式的 FDB 裏,而且長檔名形式的 FDB 之後,緊接著檔檔名形式的 FDB,底下先介紹長檔名形式的 FDB。每個長檔名形式的 FDB 也擁有 32 個位元組:
位址 | 長度 (位元組) | 意 義 |
0 | 1 | 長檔名形式的 FDB 順序,一個具有長檔名的檔案很可能擁有數個長檔名形式的 FDB,這些 FDB 由短檔名形式的 FDB 開始向低位址排列,而這個位元組就表示其順序,由 1 開始,最多到 20,但是如果是最後一個 FDB,則須加上 40H。 |
1~0AH | 0AH | 長檔名,此部份可以存 5 個萬國碼字元 |
0BH | 1 | 長檔名形式 FDB 屬性,必為 0FH,檢查此位元組,若為 0FH,表示此 FDB 為長檔名形式的 FDB |
0CH | 1 | 保留,必為 00H |
0DH | 1 | 查核碼 |
0EH~19H | 0CH | 長檔名,此部份可以存 6 個萬國碼字元 |
1AH~1BH | 2 | 保留,必為 00H |
1CH~1FH | 4 | 長檔名,此部份可以存 2 個萬國碼字元 |
由上表得知,每個長檔名形式的 FDB,雖然佔有 32 個位元組,但是順序碼、查核碼、屬性、保留等位元組,實際上能夠儲存檔名只有 26 個位元組,但是每個萬國碼需佔用 2 個位元組,因此實際上每個長檔名形式的 FDB 只能存有 13 個字。而這 13 個字是以萬國碼編碼而成,並且散落於 FDB 的三個部份。
底下來看看一個例子,這是小木偶硬碟中的『I learn assembly.txt 』檔案,小木偶用 DEBUG.EXE 載入它所在的目錄磁區,然後傾印的結果:
128B:0000 42 62 00 6C 00 79 00 2E-00 74 00 0F 00 D4 78 00 Bb.l.y...t....x.
128B:0010 74 00 00 00 FF FF FF FF-FF FF 00 00 FF FF FF FF t...............
128B:0020 01 49 00 20 00 6C 00 65-00 61 00 0F 00 D4 72 00 .I. .l.e.a....r.
128B:0030 6E 00 20 00 61 00 73 00-73 00 00 00 65 00 6D 00 n. .a.s.s...e.m.
128B:0040 49 4C 45 41 52 4E 7E 31-54 58 54 20 00 5C 76 30 ILEARN~1TXT .\v0
128B:0050 97 34 97 34 0F 00 7B 30-97 34 B6 49 03 00 00 00 .4.4..{0.4.I....
最底下藍色的部份就是短檔名形式的 FDB,包含了檔案屬性、建立日期等等,而長檔名則記載於低位址的黃色與橙色的兩個長檔名形式的 FDB 裏,黃色部份則為長檔名的第一個 FDB,故第零個位元組為 1,橙色為第二個,也是最後一個 FDB,故第零個位元組為 42H。而完整的長檔名則是由短檔名形式的 FDB 低一個位址的 FDB 開始,把每個長檔名形式的 FDB 裏的三部份檔名組合,若一個 FDB 不夠用則往低位址的 FDB 延伸而組成,直到遇到零為止。
註一:對 FAT16 分割而言,最多有 65536 個磁叢,所以有 65536 個磁叢編號,每個磁叢編號佔 16 位元,亦即 2 個位元組,因此 FAT 最多佔有 256 個磁區。
一個 FAT 所佔位元組數:65536*2=131072
每個磁區有 512 位元組,故一個 FAT 所佔磁區數:131072/512=256
還不至影響太大,但是對 FAT32 分割而言,不只每個磁叢編號佔用 32 位元,同時磁叢編號數也增加了,所以如果用上述讀取 FAT,計算未使用的磁叢數,將會曠日費時,所以用 FSI_StrucSig 來計算剩餘容量。
註二:在第 18、32 和這一章提到了許多磁區編號與存取磁區的服務程式,我想可能有許多人會搞混,在此稍做解釋。
首先得要會分辨實體磁碟與邏輯磁碟。實體硬碟就是您肉眼所見的硬碟,例如現在的 IDE 界面最多可以接上四顆硬碟,此處便是指實體硬碟。但是一顆硬碟可以藉由 FDISK.EXE 等工具,把它分割成好幾個分割區,每個分割區可視為一顆邏輯硬碟,因此邏輯硬碟是並非您看見的硬碟,不過我們在作業系統中見到的 C:、D:…等磁碟編號就是邏輯磁碟。存取實體硬碟時,用 INT 13H/INT 13H Extension 服務程式,其定址方式有 CHS 或 LBA 邏輯定址。而存取邏輯磁區則是用 INT 25H/INT 26H 或 AX=7305/INT 21H。其他請參考下表:
硬碟 | 實體硬碟 | 邏輯硬碟 | |
磁區編號 名稱 | CHS | LBA | |
磁區編號 | 由最外圈為零磁柱,向內圈直到最大磁柱;每磁柱分成若干磁頭,磁頭由零到最大磁頭;每磁頭再分成若干磁區,磁區由一開始到最大磁區 | 由實體硬碟的 MBR 開始編號,此磁區為零一直到實體硬碟的磁區總數減一 | 由邏輯磁碟的最前面磁區 ( 即啟動磁區 ) 開始標號,此磁區為零磁區一直到這個邏輯磁碟的磁區總數減一 |
服務程式 | INT 13H | INT 13H Extension | INT 25H/26H 或 AX=7305H/INT 21H |
磁碟編號 | 軟碟由 0 開始,0:A:,1:B:… 硬碟由 80H 開始,80H:第一顆實體硬碟,81H:第二顆實體硬碟…依此類推 | 0 表示 A: 磁碟機,1 表示 B: 磁碟機,2 表示 C: 磁碟機…依此類推 | |
磁碟機放於何處 | DL 暫存器 | AL 暫存器 |
留言列表