1. 程式人生 > >SimpleFs檔案系統初步四(Inode的建立過程)

SimpleFs檔案系統初步四(Inode的建立過程)

我們在檔案系統下面建立一個檔案或者目錄,毫無疑問,檔案系統必須先去檢查這個檔案或者資料夾是否存在吧,隨後如果檔案系統檢查到檔案或者資料夾不存在才會為你新建,否則返回出錯。

1.檔案或者資料夾的檢查過程
struct dentry *simplefs_lookup(struct inode *parent_inode,
			       struct dentry *child_dentry, unsigned int flags)
{
	//從根節點的Inode獲取在Mount的時候讀取到的磁碟中Sb的資訊
	struct simplefs_inode *parent = SIMPLEFS_INODE(parent_inode);
	struct super_block *sb = parent_inode->i_sb;
	struct buffer_head *bh;
	struct simplefs_dir_record *record;
	int i;
	//因為根節點中會包含Data Block的索引,通過這個索引獲取根節點這個dentry中存放的內容
	/*      根節點(Dentry)的內容如下:
	 *      檔名:  "vanakkam"     
	 *      Inode號:  2
	 */
	bh = sb_bread(sb, parent->data_block_number);
	BUG_ON(!bh);

	//指向父目錄Inode中內容頭部
	record = (struct simplefs_dir_record *)bh->b_data;
	//先確定該目錄下面有多少個Inode(這裡是包含檔案和目錄的)
	for (i = 0; i < parent->dir_children_count; i++) {
		//如果遍歷了整個目錄後,都無法找到這個檔案或者資料夾,那麼直接返回NULL,讓VFS為其新
		//建一個Inode,child_dentry->d_name.name是希望建立的目標檔案或者資料夾  
		if (!strcmp(record->filename, child_dentry->d_name.name)) {
			/* FIXME: There is a corner case where if an allocated inode,
			 * is not written to the inode store, but the inodes_count is
			 * incremented. Then if the random string on the disk matches
			 * with the filename that we are comparing above, then we
			 * will use an invalid uninitialized inode */
			/*
			 * 進入這個分支是一種比較極端的情況,意味著分配的Inode並沒有被寫入儲存區,但是InodesCount確遞增了
			 * 同時磁碟上的Inode的隨機字串和我們比較的字串匹配上了,那麼我們直接使用這個非法的未初始化的
			 * Inode即可
			 */
			struct inode *inode;
			struct simplefs_inode *sfs_inode;

			//使用這個未被寫入儲存區的Inode號,查詢到具體的Inode資訊結構
			sfs_inode = simplefs_get_inode(sb, record->inode_no);
			//使用SuperBlock分配一個空閒的Inode
			inode = new_inode(sb);
			//設定Inode號
			inode->i_ino = record->inode_no;
			//設定這個Inode歸屬於哪個父節點(Inode)下,同時設定其模式(例如:表明其是目錄還是檔案)
			inode_init_owner(inode, parent_inode, sfs_inode->mode);
			//該Inode指向的超級塊指標需要設定
			inode->i_sb = sb;
			//設定該Inode的節點操作指標(因為有可能這個節點它是一個目錄,那就需要支援mkdir,touch等操作)
			inode->i_op = &simplefs_inode_ops;

			//針對目錄和常規檔案的操作指標
			if (S_ISDIR(inode->i_mode))
				inode->i_fop = &simplefs_dir_operations;
			else if (S_ISREG(inode->i_mode))
				inode->i_fop = &simplefs_file_operations;
			else
				printk(KERN_ERR
				       "Unknown inode type. Neither a directory nor a file");

			/* FIXME: We should store these times to disk and retrieve them */
			//設定Inode的建立時間
			inode->i_atime = inode->i_mtime = inode->i_ctime =
			    CURRENT_TIME;

			//如前所述,i_private指標指向的是Inode具體資訊(檔案大小,屬性,資料塊的位置)
			inode->i_private = sfs_inode;

			//將該Inode加入到當前目錄(Dentry)下
			d_add(child_dentry, inode);
			//由於我們使用的是之前分配的inode號(未寫入儲存區),因此並不需要再重新建立了,直接
			//返回NULL即可
			return NULL;
		}
		record++;
	}

	printk(KERN_ERR
	       "No inode found for the filename [%s]\n",
	       child_dentry->d_name.name);

	return NULL;
}

2.檔案的建立過程

當我們檔案系統檢查完,該檔案不存在時,就會通過vfs建立一個檔案

static struct inode_operations simplefs_inode_ops = {
	.create = simplefs_create,
	.lookup = simplefs_lookup,
	.mkdir = simplefs_mkdir,
};

如上對於一個目錄的Inode我們之前為其設定了simplefs_inode_ops的結構,其中有個成員就是create指標。如下

simplefs_create

        ->simplefs_create_fs_object

下面就是Inode的建立過程

static int simplefs_create_fs_object(struct inode *dir, struct dentry *dentry,
				     umode_t mode)
{
	struct inode *inode;
	struct simplefs_inode *sfs_inode;
	struct super_block *sb;
	struct simplefs_inode *parent_dir_inode;
	struct buffer_head *bh;
	struct simplefs_dir_record *dir_contents_datablock;
	uint64_t count;
	int ret;

	if (mutex_lock_interruptible(&simplefs_directory_children_update_lock)) {
		sfs_trace("Failed to acquire mutex lock\n");
		return -EINTR;
	}
	//通過這個父Inode獲取到這個檔案系統的SuperBlock
	sb = dir->i_sb;
	
	//我們首先思考,我們如果想要建立一個Inode是不是應該看下該檔案系統的Inode位置是否		
	//還有空餘來允許我們建立呢,因此,我們先要得到當前檔案系統已經使用的Inode總數。
	ret = simplefs_sb_get_objects_count(sb, &count);
	if (ret < 0) {
		mutex_unlock(&simplefs_directory_children_update_lock);
		return ret;
	}

	//先判斷Inode總數是否超了,如果是,則返回使用者,沒有空間建立了
	if (unlikely(count >= SIMPLEFS_MAX_FILESYSTEM_OBJECTS_SUPPORTED)) {
		/* The above condition can be just == insted of the >= */
		printk(KERN_ERR
		       "Maximum number of objects supported by simplefs is already reached");
		mutex_unlock(&simplefs_directory_children_update_lock);
		return -ENOSPC;
	}

	//該檔案系統只支援目錄和普通檔案的建立,否則返回出錯
	if (!S_ISDIR(mode) && !S_ISREG(mode)) {
		printk(KERN_ERR
		       "Creation request but for neither a file nor a directory");
		mutex_unlock(&simplefs_directory_children_update_lock);
		return -EINVAL;
	}
	
	//通過SuperBlock建立一個空的Inode  
	inode = new_inode(sb);
	if (!inode) {
		mutex_unlock(&simplefs_directory_children_update_lock);
		return -ENOMEM;
	}
	//設定這個Inode指向的SuperBlock   
	inode->i_sb = sb;
	//設定這個Inode的操作指標
	inode->i_op = &simplefs_inode_ops;
	//設定這個Inode的建立時間
	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
	//設定Inode的Inode號
	inode->i_ino = (count + SIMPLEFS_START_INO - SIMPLEFS_RESERVED_INODES + 1);
	//建立特定檔案系統的Inode結構
	sfs_inode = kmem_cache_alloc(sfs_inode_cachep, GFP_KERNEL);
	//對該節點的Inode號賦值
	sfs_inode->inode_no = inode->i_ino;
	//將核心標準節點的私有指標指向當前特定檔案系統的Inode結構
	inode->i_private = sfs_inode;
	//設定檔案系統的屬性
	sfs_inode->mode = mode;

	//對檔案目錄以及普通檔案分別做設定,需要注意的是,如果建立的是一個目錄,那麼毫無疑問,當前目錄下
	//的Inode個數肯定還是為0的
	if (S_ISDIR(mode)) {
		printk(KERN_INFO "New directory creation request\n");
		sfs_inode->dir_children_count = 0;
		inode->i_fop = &simplefs_dir_operations;
	} else if (S_ISREG(mode)) {
		printk(KERN_INFO "New file creation request\n");
		sfs_inode->file_size = 0;
		//針對普通檔案設定讀寫操作
		inode->i_fop = &simplefs_file_operations;
	}

	/* First get a free block and update the free map,
	 * Then add inode to the inode store and update the sb inodes_count,
	 * Then update the parent directory's inode with the new child.
	 *
	 * The above ordering helps us to maintain fs consistency
	 * even in most crashes
	 */
	//從超級塊中獲取空閒的資料塊
	ret = simplefs_sb_get_a_freeblock(sb, &sfs_inode->data_block_number);
	if (ret < 0) {
		printk(KERN_ERR "simplefs could not get a freeblock");
		mutex_unlock(&simplefs_directory_children_update_lock);
		return ret;
	}
	//新建一個Inode需要更新Inode的資料區,並同步
	simplefs_inode_add(sb, sfs_inode);

	/*除了更新Inode的資料區,我們還需要做一件事:在父目錄(Inode)下面,新增該Inode的資訊*/
	//既然要新增資訊,必須首先取得當前父目錄的結構資訊,通過核心的標準Inode獲取特定檔案系統的Inode
	//資訊
	parent_dir_inode = SIMPLEFS_INODE(dir);
	//通過simplefs_inode中的成員從而獲取到資料資訊
	bh = sb_bread(sb, parent_dir_inode->data_block_number);
	BUG_ON(!bh);
	//需要知道的是目錄Inode中存放的內容結構都是固定的,因此做下強制轉換
	dir_contents_datablock = (struct simplefs_dir_record *)bh->b_data;

	/* Navigate to the last record in the directory contents */
	/*讓DIR的內容指標指向空閒區域*/
	dir_contents_datablock += parent_dir_inode->dir_children_count;
	/*讓DIR的內容inode_no以及檔名稱更新到父目錄中*/
	dir_contents_datablock->inode_no = sfs_inode->inode_no;
	strcpy(dir_contents_datablock->filename, dentry->d_name.name);

	/*將父目錄指向的資料塊設定為dirty,並將其回寫到磁碟,之後釋放*/
	mark_buffer_dirty(bh);
	sync_dirty_buffer(bh);
	brelse(bh);

	if (mutex_lock_interruptible(&simplefs_inodes_mgmt_lock)) {
		mutex_unlock(&simplefs_directory_children_update_lock);
		sfs_trace("Failed to acquire mutex lock\n");
		return -EINTR;
	}
	//將父目錄中的dir_children_count也自增
	parent_dir_inode->dir_children_count++;
	//同理我們更改了Inode資料區,這個資料區自然也要同步下了
	ret = simplefs_inode_save(sb, parent_dir_inode);
	if (ret) {
		mutex_unlock(&simplefs_inodes_mgmt_lock);
		mutex_unlock(&simplefs_directory_children_update_lock);

		/* TODO: Remove the newly created inode from the disk and in-memory inode store
		 * and also update the superblock, freemaps etc. to reflect the same.
		 * Basically, Undo all actions done during this create call */
		return ret;
	}

	mutex_unlock(&simplefs_inodes_mgmt_lock);
	mutex_unlock(&simplefs_directory_children_update_lock);
	//將當前Inode和其父目錄關聯
	inode_init_owner(inode, dir, mode);
	//將當前檔案加入到denrty下面
	d_add(dentry, inode);

	return 0;
}