今天我们来看ext2源代码的dir.c文件,这个文件是做和目录有关的操作的,也是很重要的一个文件,大家认真看哦,有什么和我不一致的大家一起探讨~~
在讲这个之前我觉得还是先说下ext2文件系统的目录方面的基础比较好,这样会更容易看明白。
ext2文件系统分为inode和block,inode是索引,block是数据块,那我们怎么定义目录呢?其实,目录其实也是文件的一种,只不过是目录的内容是目录内的文件信息,目录也有inode,而目录的inode索引导向的block内的内容就是一个一个的目录内文件信息结构体,就是ext2_dir_entry_2,新版的ext2目录结构体,定义如下
rec_len代表当前目录项的长度,这个字段比较方便的一点是,当删除这个目录项的时候,只需要把上一个的目录项的rec_len加上要删除的目录项的长度就可以了,而不需要把删除的节点的后边的目录项一个一个的向前对齐,这样就节省了IO开销。
file_type字段的值,也就是文件类型,有以下几种
还有EXT2_DIR_REC_LEN宏,ext2的目录项规定大小必须是4的倍数,EXT2_DIR_REC_LEN宏的参数是文件名的长度,我们看一下宏的定义
/
前边的8大家不明白的话,可以看ext2_dir_entry_2的实现,前边的几个变量加起来就是8个字节,再加上名称长度就得到了目录项的理论长度,加上EXT2_DIR_ROUND,就是3,与3,得到边界按照四对齐的大小
那么开始吧!
在讲这个之前我觉得还是先说下ext2文件系统的目录方面的基础比较好,这样会更容易看明白。
ext2文件系统分为inode和block,inode是索引,block是数据块,那我们怎么定义目录呢?其实,目录其实也是文件的一种,只不过是目录的内容是目录内的文件信息,目录也有inode,而目录的inode索引导向的block内的内容就是一个一个的目录内文件信息结构体,就是ext2_dir_entry_2,新版的ext2目录结构体,定义如下
struct ext2_dir_entry_2 {
__le32 inode; /* Inode号*/
__le16 rec_len; /*本目录项的长度*/
__u8 name_len; /*文件名长度 */
__u8 file_type; /*这个文件的文件类型*/
char name[EXT2_NAME_LEN]; /*文件名*/
};
rec_len代表当前目录项的长度,这个字段比较方便的一点是,当删除这个目录项的时候,只需要把上一个的目录项的rec_len加上要删除的目录项的长度就可以了,而不需要把删除的节点的后边的目录项一个一个的向前对齐,这样就节省了IO开销。
file_type字段的值,也就是文件类型,有以下几种
enum {
EXT2_FT_UNKNOWN, /*未知*/
EXT2_FT_REG_FILE, /*常规文件*/
EXT2_FT_DIR, /*目录文件*/
EXT2_FT_CHRDEV, /*字符设备文件*/
EXT2_FT_BLKDEV, /*块设备文件*/
EXT2_FT_FIFO, /*命名管道文件*/
EXT2_FT_SOCK, /*套接字文件*/
EXT2_FT_SYMLINK, /*符号连文件*/
EXT2_FT_MAX /*文件类型的最大个数*/
};
还有EXT2_DIR_REC_LEN宏,ext2的目录项规定大小必须是4的倍数,EXT2_DIR_REC_LEN宏的参数是文件名的长度,我们看一下宏的定义
/
*
* EXT2_DIR_PAD defines the directory entries boundaries
*
* NOTE: It must be a multiple of 4
*/
#define EXT2_DIR_PAD 4
#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
~EXT2_DIR_ROUND)
前边的8大家不明白的话,可以看ext2_dir_entry_2的实现,前边的几个变量加起来就是8个字节,再加上名称长度就得到了目录项的理论长度,加上EXT2_DIR_ROUND,就是3,与3,得到边界按照四对齐的大小
那么开始吧!
/*
* linux/fs/ext2/dir.c
* 作者版权信息
* Copyright (C) 1992, 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie (Paris VI)
* 法国最好的大学,法国巴黎第六大学的Remy Card大神,内核ext2的好多代码都是他写的
* from
*
* linux/fs/minix/dir.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* ext2 directory handling functions
*
* Big-endian to little-endian byte-swapping/bitmaps by
* David S. Miller (davem@caip.rutgers.edu), 1995
*
* All code that works with directory layout had been switched to pagecache
* and moved here. AV
*/
#include "ext2.h"
#include <linux/pagemap.h>
typedef struct ext2_dir_entry_2 ext2_dirent;
/*返回inode结构体所属的文件系统的块大小字节数*/
static inline unsigned ext2_chunk_size(struct inode *inode)
{
return inode->i_sb->s_blocksize;
}
/*释放申请的页*/
static inline void ext2_put_page(struct page *page)
{
kunmap(page);
page_cache_release(page);
}
/*返回一个文件占用的页的数目*/
static inline unsigned long dir_pages(struct inode *inode)
{
/*inode->i_size是文件大小的字节数,PAGE_CACHE_SIZE是缓存页的大小,PAGE_CACHE_SHIFT是缓存页大小换算成二进制位有多少位,这样的算法得到的就是inode对应的文件的占用缓存页的数目*/
return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT;
}
/*返回inode对应的文件的页号为page_nr的最后一个合法的字节的位置,再加一,提示:页号从零开始*/
static unsigned
ext2_last_byte(struct inode *inode, unsigned long page_nr)
{
/*先获得文件字节大小*/
unsigned last_byte = inode->i_size;
/*减去前边的页的字节数,page_nr << PAGE_CACHE_SHIFT就等于page_nr乘上页大小*/
last_byte -= page_nr << PAGE_CACHE_SHIFT;
/*如果page_nr不是最后一页,就返回当前页的最后一个字节位置加一*/
if (last_byte > PAGE_CACHE_SIZE)
last_byte = PAGE_CACHE_SIZE;
return last_byte;
}
/*把page页缓存上的from到to的字节修改提交上去*/
static int ext2_commit_chunk(struct page *page, unsigned from, unsigned to)
{
/*找到这个缓冲区的拥有着*/
struct inode *dir = page->mapping->host;
int err = 0;
dir->i_version++;
/*调用页缓冲区的函数把修改提交*/
page->mapping->a_ops->commit_write(NULL, page, from, to);
/*如果标志上要求写入立刻同步,就同步,否则释放此页*/
if (IS_DIRSYNC(dir))
err = write_one_page(page, 1);
else
unlock_page(page);
return err;
}
/*检验页有没有错误*/
static void ext2_check_page(struct page *page)
{
/*dir是页的主人*/
struct inode *dir = page->mapping->host;
/*sb是dir的文件系统vfs层的超级块*/
struct super_block *sb = dir->i_sb;
/*chunk_size是文件大小*/
unsigned chunk_size = ext2_chunk_size(dir);
/*返回页的虚拟地址*/
char *kaddr = page_address(page);
/*文件系统总的inode数目*/
u32 max_inumber = le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count);
unsigned offs, rec_len;
/*limit是页缓存的大小*/
unsigned limit = PAGE_CACHE_SIZE;
ext2_dirent *p;
char *error;
/*文件大小右移PAGE_CACHE_SHIFT位得到的是文件的最后一个缓存页的号码,如果等于page的index,就是说page就是文件的最后一部分对应的缓存页,并且文件都在缓冲区里*/
if ((dir->i_size >> PAGE_CACHE_SHIFT) == page->index) {
/*limit得到文件大小最后一个页的页内偏移*/
limit = dir->i_size & ~PAGE_CACHE_MASK;
/*如果不为零,说明这个文件目录不是一个完整的块大小的倍数,文件可能不是块大小的倍数,但是文件目录必定是块大小的倍数,这里就返回大小错误*/
if (limit & (chunk_size - 1))
goto Ebadsize;
/*如果limit为0说明没问题*/
if (!limit)
goto out;
}
/*EXT2_DIR_REC_LEN宏我们前边讲过的,这里就是遍历目录的block块的内容,遍历块内的每一个ext2_dir_entry_2结构体*/
for (offs = 0; offs <= limit - EXT2_DIR_REC_LEN(1); offs += rec_len) {
/*p指针指向当前应该指向的ext2_dir_entry_2结构体*/
p =