信息安全系統設計與實現:第十一章學習筆記
20191331 lyx
教材學習內容總結
第十一章 EXT2文件系統
學習目標
充分理解一個文件系統,掌握EXT2文件系統的數據結構,通過簡單編程實現EXT2文件系統的功能,了解如何實現支持linux內核文件的操作的EXT2文件系統,了解文件系統的三個級別。
什么是EXT2文件系統
EXT2第二代擴展文件系統second extended filesystem,縮寫為ext2,是LINUX內核所用的文件系統。它開始由Rémy Card設計,用以代替ext,於1993年1月加入linux核心支持之中。ext2 的經典實現為LINUX內核中的ext2fs文件系統驅動,最大可支持2TB的文件系統,至linux核心2.6版時,擴展到可支持32TB。其他的實現包括GNU Hurd,Mac OS X ,Darwin ,BSD。ext2為數個LINUX發行版的默認文件系統,如Debian、Red Hat Linux等。
其單一文件大小與文件系統本身的容量上限與文件系統本身的簇大小有關,在一般常見的 x86 電腦系統中,簇最大為 4KB, 則單一文件大小上限為 2048GB, 而文件系統的容量上限為 16384GB。
但由於目前核心 2.4 所能使用的單一分割區最大只有 2048GB,實際上能使用的文件系統容量最多也只有 2048GB。
至於Ext3文件系統,它屬於一種日志文件系統,是對ext2系統的擴展。它兼容ext2,並且從ext2轉換成ext3並不復雜。
EXT2文件系統的結構和布局
文件系統中存儲的最小單元是塊(block),一個塊的大小是在格式化時確定的。啟動塊(Boot Block)的大小為1KB,由PC標准規定,用來存儲磁盤分區信息和啟動信息,任何文件系統都不能修改啟動塊。
啟動塊之后才是ext2文件系統的開始,ext2文件系統將整個分區划分成若干個同樣大小的塊組(Block Group)。
在整體的規划當中,文件系統最前面有一個啟動扇區(boot sector),這個啟動扇區可以安裝啟動管理程序, 這是個非常重要的設計,因為如此一來我們就能夠將不同的啟動管理程序安裝到個別的文件系統最前端,而不用覆蓋整顆硬盤唯一的 MBR 。
每個塊組的組成:
-
超級塊(Super Block)描述整個分區的文件系統信息,如inode/block的大小、總量、使用量、剩余量,以及文件系統的格式與相關信息。超級塊在每個塊組的開頭都有一份拷貝(第一個塊組必須有,后面的塊組可以沒有)。 為了保證文件系統在磁盤部分扇區出現物理問題的情況下還能正常工作,就必須保證文件系統的super block信息在這種情況下也能正常訪問。所以一個文件系統的super block會在多個block group中進行備份,這些super block區域的數據保持一致。
-
超級塊記錄的信息有:
-
1、block 與 inode 的總量(分區內所有Block Group的block和inode總量);
-
2、未使用與已使用的 inode / block 數量;
-
3、block 與 inode 的大小 (block 為 1, 2, 4K,inode 為 128 bytes);
-
4、filesystem 的掛載時間、最近一次寫入數據的時間、最近一次檢驗磁盤 (fsck) 的時間等文件系統的相關信息;
-
5、一個 valid bit 數值,若此文件系統已被掛載,則 valid bit 為 0 ,若未被掛載,則 valid bit 為 1 。
-
每個區段與 superblock 的信息都可以使用 dumpe2fs 這個命令查詢
- 塊組描述符表(GDT,Group Descriptor Table)由很多塊組描述符組成,整個分區分成多個塊組就對應有多少個塊組描述符。
每個塊組描述符存儲一個塊組的描述信息,如在這個塊組中從哪里開始是inode Table,從哪里開始是Data Blocks,空閑的inode和數據塊還有多少個等等。塊組描述符在每個塊組的開頭都有一份拷貝。
-
塊位圖(Block Bitmap)用來描述整個塊組中哪些塊已用哪些塊空閑。塊位圖本身占一個塊,其中的每個bit代表本塊組的一個block,這個bit為1代表該塊已用,為0表示空閑可用。假設格式化時block大小為1KB,這樣大小的一個塊位圖就可以表示1024*8個塊的占用情況,因此一個塊組最多可以有10248個塊。
-
inode位圖(inode Bitmap)和塊位圖類似,本身占一個塊,其中每個bit表示一個inode是否空閑可用。 Inode bitmap的作用是記錄block group中Inode區域的使用情況,Ext文件系統中一個block group中可以有16384個Inode,代表着這個Ext文件系統中一個block group最多可以描述16384個文件。
-
inode表(inode Table)由一個塊組中的所有inode組成。一個文件除了數據需要存儲之外,一些描述信息也需要存儲,如文件類型,權限,文件大小,創建、修改、訪問時間等,這些信息存在inode中而不是數據塊中。inode表占多少個塊在格式化時就要寫入塊組描述符中。
在Ext2/Ext3文件系統中,每個文件在磁盤上的位置都由文件系統block group中的一個Inode指針進行索引,Inode將會把具體的位置指向一些真正記錄文件數據的block塊,需要注意的是這些block可能和Inode同屬於一個block group也可能分屬於不同的block group。我們把文件系統上這些真實記錄文件數據的block稱為Data blocks。
- 數據塊(Data Block)是用來放置文件內容數據的地方。
注意到OpenEuler操作系統內核也使用EXT2作為文件系統
其結構為
文件描述符與打開文件之間的關系
在Linux系統中一切皆可以看成是文件,文件又可分為:普通文件、目錄文件、鏈接文件和設備文件。文件描述符(file descriptor)是內核為了高效管理已被打開的文件所創建的索引,其是一個非負整數(通常是小整數),用於指代被打開的文件,所有執行I/O操作的系統調用都通過文件描述符。程序剛剛啟動的時候,0是標准輸入,1是標准輸出,2是標准錯誤。如果此時去打開一個新的文件,它的文件描述符會是3。
每一個文件描述符會與一個打開文件相對應,同時,不同的文件描述符也會指向同一個文件。相同的文件可以被不同的進程打開也可以在同一個進程中被多次打開。系統為每一個進程維護了一個文件描述符表,該表的值都是從0開始的,所以在不同的進程中你會看到相同的文件描述符,這種情況下相同文件描述符有可能指向同一個文件,也有可能指向不同的文件。具體情況要具體分析,要理解具體其概況如何,需要查看由內核維護的3個數據結構。
-
- 進程級的文件描述符表
-
- 系統級的打開文件描述符表
-
- 文件系統的i-node表
文件描述符、打開的文件句柄以及i-node之間的關系
例如要查找"/home/ealtieri/hello.txt"這個文件,先從inode2找到root inode,然后從i_block中找到home的ext2_dir_entry_2,然后得到home的inode編號,再遞歸尋找。
EXT2文件系統在OpenEuler中的實踐
ext2分區划分的基本單位是block,磁盤上前1024byte是boot block,記錄操作系統在分區中的位置。
之后的空間划分為多個block group(一般代表柱面組)。每個block group的第一個block為super block每個block group的super block都相同,是為了增加冗余,提高可靠性。
通過 mkfs 創建虛擬磁盤
mke2fs -b blkesize -N ninodes device nblocks
在設備上創建一個帶有nblocks個塊(每個塊大小為blksize字節)和ninodes個索引節點的EXT2文件系統。設備可以是真實設備,也可以是虛擬磁盤文件。如果未指定blksize,則默認塊大小為1KB。如果未指定ninoides,mke2fs將根據 nblocks 計算一個默認的ninodes數。
查看現有磁盤信息
使用命令
dd if=/dev/zero of=20191331d bs=1024 count=1440
創建名為20191331d
的虛擬磁盤
格式化為EXT2文件系統
mke2fs 20191331d 1440
EXT2文件系統的的一些數據結構
一個ext2文件系統的文件或目錄包括索引結點和數據塊兩個部分,索引結點存放文件的屬性、存取權限、修改時間以及其他的一些信息,而數據塊存放文件的內容。
目錄:
當我們在 Linux 下的 ext2 檔案系統建立一個目錄時, ext2 會分配一個 inode 與至少一塊Block 給該目錄。其中,inode 記錄該目錄的相關屬性,並指向分配到的那塊 Block ;而 Block則是記錄在這個目錄下的相關連的目錄的關連性!
檔案:
當我們在 Linux 下的 ext2 建立一個一般檔案時, ext2 會分配至少一個 inode 與相對於該檔案大小的 Block 數量給該檔案。例如:假設我的一個 Block 為 4 Kbytes ,而我要建立一個 100KBytes 的檔案,那么 linux 將分配一個 inode 與 25 個 Block 來儲存該檔案。
Block#0:
引導塊,文件系統不會使用它。它用於容納從磁盤引導操作系統的引導程序。
Block#1:
超級塊(在硬盤分區中字節偏移量為1024)。用於容納關於整個文件系統的信息。
超級塊中一些重要字段。
1.SuperBlock超級塊
記錄了block的總數量,inode的數量,block的size等等。
struct ext2_super_block {
__u32 s_inodes_count; /* Inodes count */
__u32 s_blocks_count; /* Blocks count */
...
__u32 s_free_blocks_count; /* Free blocks count */
__u32 s_free_inodes_count; /* Free inodes count */
__u32 s_first_data_block; /* First Data Block */
__u32 s_log_block_size; /* Block size */
...
__u32 s_blocks_per_group; /* # Blocks per group */
...
__u16 s_magic; /* Magic signature */
...
2.Group Description塊組描述符
記錄了磁盤上所有block group的信息(為了冗余),當然包含本block group的信息:block bitmap的位置,inode bitmap的位置,inode table的位置
struct ext2_group_desc
{
__u32 bg_block_bitmap; /* Blocks bitmap block */
__u32 bg_inode_bitmap; /* Inodes bitmap block */
__u32 bg_inode_table; /* Inodes table block */
__u16 bg_free_blocks_count; /* Free blocks count */
__u16 bg_free_inodes_count; /* Free inodes count */
__u16 bg_used_dirs_count; /* Directories count */
__u16 bg_pad;
__u32 bg_reserved[3];
};
3.block bitmap,inode bitmap
0表示block是free,1表示block被占用
4.Inode table索引節點
存儲ext2_inode數據結構的地方,每個ext2_inode有個編號,編號從1開始,root inode的編號為2
struct ext2_inode {
__u16 i_mode; /* File type and access rights */
__u16 i_uid; /* Low 16 bits of Owner Uid */
__u32 i_size; /* Size in bytes */
__u32 i_atime; /* Access time */
__u32 i_ctime; /* Creation time */
__u32 i_mtime; /* Modification time */
__u32 i_dtime; /* Deletion Time */
__u16 i_gid; /* Low 16 bits of Group Id */
__u16 i_links_count; /* Links count */
__u32 i_blocks; /* Blocks count */
__u32 i_flags; /* File flags */
...
__u32 i_block[EXT2_N_BLOCKS]; /* Pointers to blocks */
...
};
i_mode保存文件類型和讀取權限
當i_mode為文件時,i_blocks表示data block的指針。
當i_mode為文件夾時,i_blocks表示ext2_dir_entry_2的指針。
每個ext2_dir_entry_2的大小都不一樣,所以查找ext2_dir_entry_2時要線性搜索,這是時間換空間。
使用df命令查看各個分區掛在情況
查看超級塊
郵差算法
郵差算法:在計算機系統中, 經常出現下面這個問題。 一個城市有M 個街區, 編號從 0到M-1。 每個街區有N座房子, 編號從0 到 N-1。每座房子有一個唯一的街區地址, 用(街區, 房子)表示, 其中0<=街區<M, 0<=房子<N。 來自外太空的外星人可能不熟悉地球上的街區尋址方案, 傾向於采用線性方法將這些房子地址編為 0, 1, ···, N-1, N, N+1 等。 已知某個街區地址 BA= (街區, 房子), 怎么把它轉換為線性地址 LA, 反過來,已知線性地址,怎么把它轉換為街區地址?如果都從0開始計數,轉換就會非常簡單。
Linear_addraaa LA=N*block + house;
Blook_address BA=(LA/N, LA % N);
EXT2文件系統樹的遍歷
(1)讀取超級塊。檢查幻數s_magic(0xEF53),驗證它確實是 EXT2 FS。
(2)讀取塊組描述符塊(1 + s_first_data_block),以訪問組0描述符。從塊組描述符的bg_inode_table條目中找到索引節點的起始塊編號,並將其稱為InodesBeginBlock。
(3)讀取 InodeBeginBlock,獲取/的索引節點,即 INODE#2。
(4)將路徑名標記為組件字符串,假設組件數量為n。例如,如果路徑名 =/a/b/c,則組件字符串是"a""b""c",其中n=3。用name[0],name[1],…,name[n-1]來表示組件。
(5)從(3)中的根索引節點開始,在其數據塊中搜索 name[0]。為簡單起見,我們可以假設某個目錄中的條目數量很少,因此一個目錄索引節點只有12個直接數據塊。有了這個假設,就可以在12個(非零)直接塊中搜索 name[0]。目錄索引節點的每個數據塊都包含以下形式的 dir entry 結構體;
[ino rec_len name_len NAME] [ino rec_len name_len NAME]....
其中NAME是一系列nlen字符,不含終止NUL。對於每個數據塊,將該塊讀入內存並使用 dir_entry *dp指向加載的數據塊。然后使用 name_len將NAME提取為字符串,並與name[0]進行比較。如果它們不匹配,則通過以下代碼轉到下一個dir_entry:
dp =(dir_entry*)((char *)dp + dp->rec_len);
繼續搜索。如果存在 name[0],則可以找到它的 dir_entry,從而找到它的索引節點號。
(6)使用索引節點號ino來定位相應的索引節點。回想前面的內容,ino 從1開始計數。使用郵差算法計算包含索引節點的磁盤塊及其在該塊中的偏移量。
blk =(ino - 1) / INODE8_PER_BLOCK + InodesBeginBlock;
offset = (ino - 1) % INODES_PER_BLOCK;
然后在索引節點中讀取/a,從中確定它是否是一個目錄(DIR)。如果/a不是目錄,則不能有/a/b,因此搜索失敗。如果它是目錄,並且有更多需要搜索的組件,那么繼續搜索下一個組件 name[1]。現在的問題是∶在索引節點中搜索/a的 name[1],與第(5)步完全相同。
(7)由於(5)~(6)步將會重復n次,所以最好編寫一個搜索函數∶
u32 search (INODE *inodePtr, char *name)
{
// search for name in the data blocks of current DIR inode
// if found, return its ino; else return 0 )
}
然后我們只需調用 search()n次,如下所示。
Assume:n,name[0],....,name[n-1] are globals
INODE *ip points at INODE of /
for(i=0; i<n; i++)
{
ino = search(ip, name[4])
if(!ino){ // can't find name[i], exit;}
use ino to zead in INODE and let ip point to INODE
}
如果搜索循環成功結束,ip必須指向路徑名的索引節點。遍歷有多個組的大型 EXT2/3 文件系統也是類似操作。
文件系統的級別
文件系統的實現分為三個級別。每個級別處理文件系統的不同部分。這使得實現過程模塊化,更容易理解。在文件系統的實現過程中,FS目錄包含實現EXT2文件系統的文件。
使用第1級別FS 函數的用戶命令程序有:
mkdir、creat.mknod. rmdir.link.unlink.symlink, rm、ls、cd和 pwd等。
- 第1級別實現了基本文件系統樹。它包含以下文件,實現了指定函數。
mkdir_creat.c : make directory, create regular file
ls_cd_pwd.c : list directory, change directory, get CWD path
rmdir.c : remove directory
link_unlink.c : hard link and unlink files
symlink_readlink.c : symbolic link files
stat.c : return file information
misc1.c : access,chmod, chown, utime, etc.
- 第2級別實現了文件內容讀/寫函數。
open_close_lseek.c : open file for RBAD | WRITE|APPEND,close file and lseek
read.c : read from file descriptor of an opened regular file
write.c : write to file descriptor of an opened regular file
opendir_readdir.c : open and read directory
- 第3級別實現了文件系統的掛載、卸載和文件保護。
mount__umount.c : mount/umount file systems
file_protection : access permission checking
file-locking : lock/unlock files
文件系統掛載
1.系統調用處理
用戶執行掛載是通過系統調用路徑進入內核處理,拷貝用戶空間傳遞參數到內核,掛載委托do_mount。
2.掛載點路徑查找
掛載點路徑查找,掛載委托path_mount.
3.參數合法性檢查
參數合法性檢查, 新掛載委托do_new_mount
4.調用具體文件系統掛載方法
5.掛載實例添加到全局文件系統樹
編程實現ls-l命令
代碼托管:https://gitee.com/DKY2019/xxaqxt/blob/master/myls_l.c
參考資料
詳解EXT2 https://blog.csdn.net/gongjiwei/article/details/82025142
ext2文件系統結構 https://www.jianshu.com/p/18c74734911b
文件系統掛載 https://zhuanlan.zhihu.com/p/378011720
ext2文件系統學習 https://blog.csdn.net/weixin_30415801/article/details/95911807
20191331lyx
2021/10/17