原帖地址:
https://zhangshurong.github.io/2018/03/25/%E5%8A%A8%E6%89%8B%E5%86%99%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F/
項(xiàng)目地址:https://github.com/ZhangShurong/HUST_OS_fs_experiment
title: 動(dòng)手寫一個(gè)簡單的文件系統(tǒng)
date: 2018-03-25 16:53:50
categories: "Linux"
tags:
- Linux
- Kernel
- FileSystem
總體設(shè)計(jì)
本文件系統(tǒng)的磁盤結(jié)構(gòu)參考minix的文件系統(tǒng)實(shí)現(xiàn)。但是自舉塊(或稱引導(dǎo)塊)中沒有數(shù)據(jù)潜叛。切不采用二級(jí)或者多級(jí)索引,其結(jié)構(gòu)如下:
|Dummy Block|Super Block|IMap|BMap|Inode Table|Data blocks|
其中每個(gè)塊的大小定義為4096bytes,每個(gè)Inode含有10個(gè)塊所以單個(gè)文件最大為40KB,支持的最小磁盤大小為24K木柬。以下詳細(xì)闡述文件系統(tǒng)中所需要的三個(gè)基本數(shù)據(jù)結(jié)構(gòu)或舞。
1. 超級(jí)塊結(jié)構(gòu):
struct HUST_fs_super_block {
uint64_t version;
uint64_t magic;
uint64_t block_size;
uint64_t inodes_count;
uint64_t free_blocks;
uint64_t blocks_count;
uint64_t bmap_block;
uint64_t imap_block;
uint64_t inode_table_block;
uint64_t data_block_number;
char padding[4016];
};
超級(jí)塊中的padding數(shù)組,是為了使超級(jí)塊的大小為4096bytes勇婴,以簡化后期的工作;“Magic”為1314522嘱腥;indoe_count記錄文件系統(tǒng)所支持的inode個(gè)數(shù)耕渴,這個(gè)值在格式化時(shí)就已經(jīng)計(jì)算并寫入超級(jí)塊了。Bmap_block記錄著bmap開始的數(shù)據(jù)塊索引,imap_block齿兔,inode_table_block和data_block_number同理橱脸,記錄索引是為了簡化文件塊的定位操作。
2. HUST_inode結(jié)構(gòu)
struct HUST_inode {
mode_t mode; //sizeof(mode_t) is 4
uint64_t inode_no;
uint64_t blocks;
uint64_t block[HUST_N_BLOCKS];
union {
uint64_t file_size;
uint64_t dir_children_count;
};
int32_t i_uid;
int32_t i_gid;
int32_t i_nlink;
int64_t i_atime;
int64_t i_mtime;
int64_t i_ctime;
char padding[112];
};
HUST_inode對(duì)應(yīng)著磁盤上的inode結(jié)構(gòu)分苇,在后文會(huì)描述它是如何轉(zhuǎn)換為VFS中的inode的添诉。在上述結(jié)構(gòu)體中,mode代表該inode是文件還是目錄医寿,blocks代表該inode的大欣父啊(所占?jí)K的數(shù)目),i_uid和i_gid用于后面的多用戶管理糟红。Padding數(shù)組是為了讓HUST_inode結(jié)構(gòu)體能夠被4096整除艾帐;宏HUST_N_BLOCK被定義為10,意味著每個(gè)文件(目錄)最大的大小為10個(gè)塊盆偿;block數(shù)組存儲(chǔ)著每個(gè)塊的索引柒爸,用于定位文件。
3. 文件系統(tǒng)的目錄結(jié)構(gòu)
struct HUST_dir_record {
char filename[HUST_FILENAME_MAX_LEN];
uint64_t inode_no;
};
文件記錄是為了儲(chǔ)存目錄項(xiàng)事扭,其中HUST_FILENAME_MAX_LEN定義為256也就是說捎稚,文件名最大長度256。
編寫文件系統(tǒng)除了設(shè)計(jì)文件系統(tǒng)的磁盤結(jié)構(gòu)求橄,定義文件系統(tǒng)支持的操作也是十分重要的今野,這個(gè)直接影響了文件系統(tǒng)的功能。本文件系統(tǒng)支持基本的文件的增刪改查罐农,多用戶等功能条霜,但是不支持文件的移動(dòng),軟硬鏈接等操作涵亏。
mkfs的實(shí)現(xiàn)
要使用這個(gè)文件系統(tǒng)宰睡,必須首先創(chuàng)建一個(gè)符合磁盤布局的映像文件,所以我們需要實(shí)現(xiàn)一個(gè)格式化程序气筋,這個(gè)程序按照慣例叫做mkfs拆内。本節(jié)詳細(xì)描述mkfs的實(shí)現(xiàn)。
mkfs的作用是將一個(gè)文件改寫成對(duì)應(yīng)于我們文件系統(tǒng)的結(jié)構(gòu)宠默,其主要功能點(diǎn)為寫入超級(jí)塊麸恍,寫入imap,bmap,寫入inode table搀矫,以及創(chuàng)建一個(gè)根目錄和測(cè)試文件抹沪。
超級(jí)塊包含了文件系統(tǒng)的基本信息刻肄,其信息在上文中有詳細(xì)描述。寫入超級(jí)塊信息采够,需要計(jì)算整個(gè)磁盤的大小肄方,然后計(jì)算imap,bmap以及inode table的大小蹬癌,這樣才能確定各個(gè)區(qū)域在磁盤中的位置权她。這些工作都是在init_disk這個(gè)函數(shù)中完成的∈判剑基本邏輯為讀取需要格式化的文件大小隅要,計(jì)算出整個(gè)磁盤中的塊的個(gè)數(shù),簡單的將塊的個(gè)數(shù)與inode的個(gè)數(shù)等同起來董济;然后通過塊數(shù)以及inode個(gè)數(shù)計(jì)算imap和bmap的大小步清。其中bmap的大小如下(imap大小計(jì)算公式與bmap一致):
$$
bmapsize = blockcount/ HUST_BLOCKSIZE * 8
$$
關(guān)鍵代碼如下:
static int init_disk(int fd, const char* path)
{
//獲取基本信息
//... ...
//計(jì)算bmap
bmap_size = super_block.blocks_count/(8*HUST_BLOCKSIZE);
super_block.bmap_block = RESERVE_BLOCKS;
if (super_block.blocks_count%(8*HUST_BLOCKSIZE) != 0) {
bmap_size += 1;
}
bmap = (uint8_t *)malloc(bmap_size*HUST_BLOCKSIZE);
memset(bmap,0,bmap_size*HUST_BLOCKSIZE);
//計(jì)算imap
imap_size = super_block.inodes_count/(8*HUST_BLOCKSIZE);
super_block.imap_block = super_block.bmap_block + bmap_size;
if(super_block.inodes_count%(8*HUST_BLOCKSIZE) != 0) {
imap_size += 1;
}
imap = (uint8_t *)malloc(imap_size*HUST_BLOCKSIZE);
memset(imap,0,imap_size*HUST_BLOCKSIZE);
//計(jì)算inode_table
inode_table_size = super_block.inodes_count/(HUST_BLOCKSIZE/HUST_INODE_SIZE);
super_block.inode_table_block = super_block.imap_block + imap_size;
super_block.data_block_number = RESERVE_BLOCKS + bmap_size + imap_size + inode_table_size;
super_block.free_blocks = super_block.blocks_count - super_block.data_block_number - 1;
//設(shè)置bmap以及imap
//... ...
}
其中,imap和bmap為uint8_t的全局?jǐn)?shù)組虏肾。
計(jì)算完基本信息之后廓啊,我們需要將其寫入文件并創(chuàng)建根目錄和測(cè)試文件。文件創(chuàng)建的基本步驟如下:
- 檢測(cè)(獲确夂馈)磁盤(文件)大小谴轮,確認(rèn)是否有足夠的空間
- 找的空閑的inode和block,并標(biāo)記imap和bmap吹埠。
- 生成相應(yīng)的數(shù)據(jù)第步,并寫入對(duì)應(yīng)的塊中。對(duì)于根目錄來講缘琅,寫入的數(shù)據(jù)為三個(gè)目錄項(xiàng)粘都,目錄項(xiàng)的內(nèi)容為文件(目錄)名以及對(duì)應(yīng)的inode編號(hào)。第一個(gè)目錄項(xiàng)為當(dāng)前目錄和對(duì)應(yīng)的inode編號(hào)0刷袍,第二個(gè)目錄項(xiàng)為上一級(jí)目錄和對(duì)應(yīng)的inode編號(hào)0翩隧,第三個(gè)目錄項(xiàng)為歡迎文件,內(nèi)容為文件名“file”和對(duì)應(yīng)的inode編號(hào)1呻纹。
- 設(shè)置對(duì)應(yīng)的inode信息鸽心,如是文件還是目錄(mode信息),創(chuàng)建時(shí)間修改時(shí)間(i_ctime和i_mtime)居暖,用戶id和組id信息(i_uid和i_gid)等。
- 更新超級(jí)塊信息藤肢。
在我們的文件系統(tǒng)寫完之前太闺,我們可以新建一個(gè)文件來測(cè)試我們的mkfs是否能正常運(yùn)行,通過16進(jìn)制編輯器來查看是否功能正常嘁圈。具體步驟如下:
- 運(yùn)行下列命令創(chuàng)建文件省骂。
dd bs=4096 count=100 if=/dev/zero of=image
- 編譯mkfs.c
gcc mkfs.c -o mkfs
- 格式化image文件
./mkfs ./image
- 通過hexdumo來查看文件的結(jié)構(gòu)蟀淮,結(jié)果如下圖。通過檢查钞澳,我們發(fā)現(xiàn)怠惶,image文件結(jié)構(gòu)寫入正確無誤。
文件系統(tǒng)的實(shí)現(xiàn)
一個(gè)通常意義上的文件系統(tǒng)驅(qū)動(dòng)可以單獨(dú)被編譯成模塊動(dòng)態(tài)加載轧粟,也可以被直接編譯到內(nèi)核中策治,為了調(diào)試的方便,本文中的文件系統(tǒng)采用動(dòng)態(tài)加載的方式實(shí)現(xiàn)兰吟。實(shí)現(xiàn)一個(gè)文件系統(tǒng)必須遵照內(nèi)核的一些“規(guī)則”通惫,以下我將以遞進(jìn)的順序闡述文件系統(tǒng)的實(shí)現(xiàn)過程。
一混蔼、 文件系統(tǒng)的加載與卸載
首先為了能夠成功加載文件系統(tǒng)履腋,文件系統(tǒng)需要提供文件系統(tǒng)的名字,超級(jí)塊的加載和刪除方法惭嚣。這些東西反應(yīng)在file_system,_type中遵湖。
struct file_system_type HUST_fs_type = {
.owner = THIS_MODULE,
.name = "HUST_fs",
.mount = HUST_fs_mount,
.kill_sb = HUST_fs_kill_superblock, /* unmount */
};
文件系統(tǒng)作為一種塊設(shè)備驅(qū)動(dòng),自然也需要實(shí)現(xiàn)module_init以及mocule_exit晚吞。代碼如下:
/* Called when the module is loaded. */
int HUST_fs_init(void)
{
int ret;
ret = register_filesystem(&HUST_fs_type);
if (ret == 0)
printk(KERN_INFO "Sucessfully registered HUST_fs\n");
else
printk(KERN_ERR "Failed to register HUST_fs. Error: [%d]\n", ret);
return ret;
}
/* Called when the module is unloaded. */
void HUST_fs_exit(void)
int ret;
ret = unregister_filesystem(&HUST_fs_type);
if (ret == 0)
printk(KERN_INFO "Sucessfully unregistered HUST_fs\n");
else
printk(KERN_ERR "Failed to unregister HUST_fs. Error: [%d]\n", ret);
}
module_init(HUST_fs_init);
module_exit(HUST_fs_exit);
MODULE_LICENSE("MIT");
MODULE_AUTHOR("cv");
我們可以看到延旧,設(shè)備驅(qū)動(dòng)加載的時(shí)候,驅(qū)動(dòng)向內(nèi)核注冊(cè)了文件系統(tǒng)载矿,而驅(qū)動(dòng)卸載的時(shí)候垄潮,文件系統(tǒng)的信息也被刪除。文件系統(tǒng)加載時(shí)調(diào)用的函數(shù)為HUST_fs_mount闷盔,實(shí)際上弯洗,這個(gè)函數(shù)向內(nèi)核注冊(cè)了一個(gè)回調(diào):
int HUST_fs_fill_super(struct super_block *sb, void *data, int silent)
這個(gè)函數(shù)是用來與VFS交互從而生成VFS超級(jí)塊的。在HUST fs中逢勾,超級(jí)塊在磁盤的第二個(gè)4096字節(jié)上牡整,即塊號(hào)為1。這個(gè)函數(shù)執(zhí)行時(shí)會(huì)從磁盤中讀取信息溺拱,填充到VFS提供的超級(jí)塊結(jié)構(gòu)體中逃贝,下列為部分關(guān)鍵代碼。
int HUST_fs_fill_super(struct super_block *sb, void *data, int silent) {
struct buffer_head *bh;
bh = sb_bread(sb, 1);
struct HUST_fs_super_block *sb_disk;
sb_disk = (struct HUST_fs_super_block *)bh->b_data;
struct inode *root_inode;
if (sb_disk->block_size != 4096) {
printk(KERN_ERR "HUST_fs expects a blocksize of %d\n", 4096);
ret = -EFAULT;
goto release;
}
//fill vfs super block
sb->s_magic = sb_disk->magic;
sb->s_fs_info = sb_disk;
sb->s_maxbytes = HUST_BLOCKSIZE * HUST_N_BLOCKS; /* Max file size */
sb->s_op = &HUST_fs_super_ops;
}
從上述代碼可以看出迫摔,我們用sb_read來讀取磁盤上的內(nèi)容沐扳,然后填充super_block結(jié)構(gòu)體。值得注意的是句占,有關(guān)超級(jí)塊的操作函數(shù)即superblock_operations也是在此處賦值的沪摄。由于super_block* sb在文件系統(tǒng)卸載之前是一直存在于內(nèi)存中的,所以我們可以使用s_fs_info來存儲(chǔ)原始的超級(jí)塊信息,避免后期交互時(shí) 再次讀取磁盤杨拐。
文件系統(tǒng)卸載的時(shí)候超級(jí)塊信息需要被刪除祈餐,所以HUST_fs_kill_superblock的作用時(shí)釋放該超級(jí)塊,通知VFS該掛載點(diǎn)已經(jīng)卸載哄陶。
實(shí)現(xiàn)基本函數(shù)后帆阳,可以對(duì)文件系統(tǒng)進(jìn)行掛載操作,掛載操作的腳本內(nèi)容如下:
sudo umount ./test
sudo rmmod HUST_fs
dd bs=4096 count=100 if=/dev/zero of=image
./mkfs image
insmod HUST_fs.ko
mount -o loop -t HUST_fs image ./test
dmesg
上述腳本屋吨,將項(xiàng)目下的test文件夾作為文件系統(tǒng)的掛載點(diǎn)蜒谤,并在掛載之后答應(yīng)出了內(nèi)核調(diào)試目錄。成功執(zhí)行該腳本的截圖如下:
我們可以看到test目錄已經(jīng)掛載成功而且內(nèi)核調(diào)試信息顯示文件系統(tǒng)掛載成功离赫。
二芭逝、 ls命令的實(shí)現(xiàn)
加載文件系統(tǒng)之后第一個(gè)要實(shí)現(xiàn)的功能是讀取文件系統(tǒng)中的數(shù)據(jù),所以選擇實(shí)現(xiàn)文件夾讀取操作渊胸,這一操作在2.x內(nèi)核中是.readdir函數(shù)指針旬盯,在最新版本中是,.iterate函數(shù)指針。這個(gè)指針在保存在file_operation中翎猛,如下所示胖翰。
const struct file_operations HUST_fs_dir_ops = {
.owner = THIS_MODULE,
.iterate = HUST_fs_iterate,
};
HUST_fs_iterate函數(shù)主要功能邏輯是讀取inode的塊數(shù)據(jù),并且將塊數(shù)據(jù)中的inode和文件名通過dir_emit函數(shù)傳輸?shù)絍FS層切厘。以根目錄為例萨咳,根目錄的包含三個(gè)數(shù)據(jù)項(xiàng),分別是父目錄疫稿,當(dāng)前目錄和歡迎文件培他,所以該函數(shù)會(huì)執(zhí)行以下三個(gè)語句
//參數(shù)分別表示上下文,文件/目錄名遗座,文件/目錄名長度舀凛,inode號(hào),文件類型
dir_emit(ctx, ".", 1,0, DT_DIR);
dir_emit(ctx, "..", 2,0, DT_DIR);
dir_emit(ctx, "file", 4,1, DT_REG);
完成該函數(shù)后途蒋,在填充根目錄inode時(shí)將HUST_fs_dir_ops指針賦值猛遍,即可在掛在文件系統(tǒng)后執(zhí)行l(wèi)s命令。
如上圖所示号坡,我們成功看到了歡迎文件懊烤。但是此時(shí)我們不能對(duì)文件進(jìn)行任何操作,因?yàn)檫€沒有實(shí)現(xiàn)其他的接口宽堆。
三腌紧、 磁盤管理相關(guān)邏輯的實(shí)現(xiàn)
這個(gè)磁盤管理的內(nèi)涵包括向磁盤寫入和從磁盤取出讀取inode,更新inode信息畜隶,維護(hù)imap寄啼,bmap逮光,inode table等操作。為了使磁盤上的內(nèi)容有序的組合起來墩划,磁盤空間的管理十分的重要,后續(xù)的文件讀寫操作都與此相關(guān)嗡综。
寫入和刪除inode的操作存放在super_operations這個(gè)結(jié)構(gòu)體中乙帮。
const struct super_operations HUST_fs_super_ops = {
.evict_inode = HUST_evict_inode,
.write_inode = HUST_write_inode,
};
HUST_fs_super_ops需要在填充超級(jí)塊時(shí)賦值到super_block的s_ops字段中。HUST_write_inode函數(shù)的功能是將內(nèi)存中的inode保存在磁盤上极景。關(guān)鍵代碼如下察净。
int HUST_write_inode(struct inode *inode, struct writeback_control *wbc)
{
struct buffer_head * bh;
struct HUST_inode * raw_inode = NULL;
HUST_fs_get_inode(inode->i_sb, inode->i_ino, raw_inode);
if (!raw_inode)
return -EFAULT;
raw_inode->mode = inode->i_mode;
raw_inode->i_uid = fs_high2lowuid(i_uid_read(inode));
raw_inode->i_gid = fs_high2lowgid(i_gid_read(inode));
raw_inode->i_nlink = inode->i_nlink;
raw_inode->file_size = inode->i_size;
raw_inode->i_atime = (inode->i_atime.tv_sec);
raw_inode->i_mtime = (inode->i_mtime.tv_sec);
raw_inode->i_ctime = (inode->i_ctime.tv_sec);
mark_buffer_dirty(bh);
brelse(bh);
return 0;
}
可以看到,該函數(shù)的將vfs inode中的相關(guān)信息存儲(chǔ)到HUST_inode結(jié)構(gòu)體中盼樟,然后寫入磁盤氢卡。這個(gè)是單獨(dú)的寫入磁盤操作,事實(shí)上晨缴,當(dāng)我們申請(qǐng)inode時(shí)译秦,imap也是需要檢查刷新的,需要把相應(yīng)位置標(biāo)記為1击碗。同理筑悴,evict_inode函數(shù)的作用時(shí)刪除inode,刪除成功后稍途,我們需要刷新imap的值阁吝,把相應(yīng)位置標(biāo)記為0。
設(shè)置和寫入map的操作都在map.c中械拍,以下以imap為例突勇。對(duì)于imap來講,申請(qǐng)inode的時(shí)候需要檢查第一個(gè)空閑的inode編號(hào)坷虑,當(dāng)inode被釋放的時(shí)候也要及時(shí)清零對(duì)應(yīng)的imap甲馋。與此相關(guān)的函數(shù)如下。
//從磁盤中讀取數(shù)據(jù)并存在imap數(shù)組中
int get_imap(struct super_block* sb, uint8_t* imap, ssize_t imap_size);
//在vaddr數(shù)組中找到第一個(gè)為0的bit猖吴,這個(gè)函數(shù)用于定位空inode或者block
int HUST_find_first_zero_bit(const void *vaddr, unsigned size);
//將imap的某一位置0或者1摔刁,并保存在磁盤上
int set_and_save_imap(struct super_block* sb, uint64_t inode_num, uint8_t value);
//定義的位操作宏如下
#define setbit(number,x) number |= 1UL << x
#define clearbit(number, x) number &= ~(1UL << x)
由于本文件系統(tǒng)并不是為了實(shí)際使用,所以上述的操作都沒有考慮性能以及準(zhǔn)確性問題海蔽。事實(shí)上共屈,能夠加上校驗(yàn)或者冗余備份是最好的。
四党窜、讀寫文件內(nèi)容
為了能夠快速看到文件系統(tǒng)在正常工作拗引,所以接下來需要實(shí)現(xiàn)文件的讀寫操作。文件讀寫操作按照一般處理幌衣,應(yīng)該是實(shí)現(xiàn)在struct file_operations這個(gè)結(jié)構(gòu)體中的矾削。事實(shí)上壤玫,最開始我是實(shí)現(xiàn)在這個(gè)結(jié)構(gòu)體中的read_iter函數(shù)指針中的。但是比較有趣的一點(diǎn)是哼凯,如果我們實(shí)現(xiàn)了struct address_space_operations結(jié)構(gòu)體中的函數(shù)欲间,那么struct file_operations結(jié)構(gòu)體中的函數(shù)則可以交由VFS實(shí)現(xiàn)。代碼如下:
const struct file_operations HUST_fs_file_ops = {
.owner = THIS_MODULE,
.llseek = generic_file_llseek,
.mmap = generic_file_mmap,
.fsync = generic_file_fsync,
.read_iter = generic_file_read_iter,
.write_iter = generic_file_write_iter,
};
const struct address_space_operations HUST_fs_aops = {
.readpage = HUST_fs_readpage,
.writepage = HUST_fs_writepage,
.write_begin = HUST_fs_write_begin,
.write_end = generic_write_end,
};
上述的generic開頭的函數(shù)是不需要我們手動(dòng)實(shí)現(xiàn)的断部。上述的address_space_operations操作其實(shí)是實(shí)現(xiàn)了頁高速緩存的一些操作猎贴。頁高速緩存是linux內(nèi)核實(shí)現(xiàn)的一種主要磁盤緩存,它主要用來減少對(duì)磁盤的IO操作蝴光,具體地講她渴,是通過把磁盤中的數(shù)據(jù)緩存到物理內(nèi)存中,把對(duì)磁盤的訪問變?yōu)閷?duì)物理內(nèi)存的訪問蔑祟。這些接口一旦實(shí)現(xiàn)趁耗,那么對(duì)文件的操作就可以轉(zhuǎn)移到內(nèi)存中,這就是為什么可以使用generic開頭的這些函數(shù)來代替手寫疆虚。
HUST_fs_readpage, HUST_fs_writepage以及HUST_fs_write_begin都被注冊(cè)回調(diào)到同一個(gè)函數(shù)HUST_fs_get_block苛败。HUST_fs_get_block主要返回內(nèi)核請(qǐng)求長度的數(shù)據(jù)。至于讀寫操作装蓬,內(nèi)核調(diào)用__bwrite函數(shù)最終調(diào)用塊設(shè)備驅(qū)動(dòng)執(zhí)行著拭。因?yàn)樵谖覜]有采用二級(jí)或者多級(jí)索引,故而HUST_fs_get_block函數(shù)邏輯比較簡單牍帚,部分代碼如下:
int HUST_fs_get_block(struct inode *inode, sector_t block,
struct buffer_head *bh, int create)
{
struct super_block *sb = inode->i_sb;
if (block > HUST_N_BLOCKS)
return -ENOSPC;
struct HUST_inode H_inode;
if (-1 == HUST_fs_get_inode(sb, inode->i_ino, &H_inode))
return -EFAULT;
if (H_inode.blocks == 0)
if(alloc_block_for_inode(sb, &H_inode, 1))
return -EFAULT;
map_bh(bh, sb, H_inode.block[block]);
return 0;
}
如上所示儡遮,該函數(shù)判斷傳入的block的大小,并將磁盤內(nèi)容映射到bh中暗赶。后續(xù)的讀寫操作將有VFS幫我們完成鄙币。
五、inode操作
Inode操作涉及文件(夾)的創(chuàng)建刪除蹂随,將HUST_inode映射到VFS中的inode等操作十嘿。具體實(shí)現(xiàn)的函數(shù)如下。
const struct inode_operations HUST_fs_inode_ops = {
.lookup = HUST_fs_lookup,
.mkdir = HUST_fs_mkdir,
.create = HUST_fs_create,
.unlink = HUST_fs_unlink,
};
HUST_fs_lookup是其中比較復(fù)雜的一個(gè)函數(shù)岳锁,它負(fù)責(zé)將一個(gè)目錄下的inode信息交由VFS管理绩衷。首先,HUST_fs_lookup讀取文件夾的內(nèi)容激率,然后遍歷文件夾下面的HUST_inode咳燕,找到我們想要的HUST_inode,根據(jù)不同的文件屬性乒躺,申請(qǐng)vfs_inode招盲;并對(duì)不同的vfs_inode設(shè)置不同的操作。假設(shè)vfs_inode對(duì)應(yīng)的是一個(gè)文件嘉冒,那么就設(shè)置vfs_inode->mapping->a_ops曹货,如果vfs_inode對(duì)應(yīng)的是文件夾咆繁,那么就設(shè)置vfs_inode->f_ops = &HUST_fs_dir_ops;最后將vfs_inode注冊(cè)到VFS中。這部分的關(guān)鍵代碼如下:
struct dentry *HUST_fs_lookup(struct inode *parent_inode,
struct dentry *child_dentry, unsigned int flags)
{
struct super_block *sb = parent_inode->i_sb;
struct HUST_inode H_inode;
//省略代碼
for (i = 0; i < H_inode.dir_children_count; i++) {
if (strncmp
(child_dentry->d_name.name, dtptr[i].filename,
HUST_FILENAME_MAX_LEN) == 0){
inode = iget_locked(sb, dtptr[i].inode_no);
if (inode->i_state & I_NEW) {
inode_init_owner(inode, parent_inode, 0);
struct HUST_inode H_child_inode;
if (-1 == HUST_fs_get_inode(sb, dtptr[i].inode_no, &H_child_inode))
return ERR_PTR(-EFAULT);
HUST_fs_convert_inode(&H_child_inode, inode);
inode->i_op = &HUST_fs_inode_ops;
if (S_ISDIR(H_child_inode.mode)) {
inode->i_fop = &HUST_fs_dir_ops;
} else if (S_ISREG(H_child_inode.mode)) {
inode->i_fop = &HUST_fs_file_ops;;
inode->i_mapping->a_ops = &HUST_fs_aops;
}
inode->i_mode = H_child_inode.mode;
inode->i_size = H_child_inode.file_size;
insert_inode_hash(inode);
unlock_new_inode(inode);
}
}
}
//省略代碼
}
只有在這里注冊(cè)了相關(guān)函數(shù)顶籽,系統(tǒng)調(diào)用才能正常執(zhí)行玩般。不然就會(huì)出現(xiàn)不支持的操作這種報(bào)錯(cuò)信息。
.create與.mkdir都是對(duì)應(yīng)了inode的創(chuàng)建礼饱,只是inode的屬性不能而已壤短。.create創(chuàng)建普通文件而.mkdir創(chuàng)建文件夾。所以這兩個(gè)函數(shù)的功能被函數(shù)HUST_fs_create_obj所處理慨仿。這個(gè)函數(shù)接受新建文件(夾)的請(qǐng)求,檢查磁盤的大小纳胧,檢查是否有空余的indoe镰吆,并且分配inode號(hào),然后更新imap信息跑慕,最后更新超級(jí)塊信息万皿。由于該函數(shù)邏輯簡單但是代碼量比較大,故而不在此展示其具體實(shí)現(xiàn)核行。
在完成上述工作之后牢硅,我們的文件系統(tǒng)基本已經(jīng)完成了,這個(gè)系統(tǒng)采用線性(區(qū)別于minixi二級(jí)索引用樹來管理)的方式管理磁盤空間芝雪,支持基本的增刪改查文件操作减余,支持文件權(quán)限,支持多用戶惩系。