【linux0.12】目录项管理与操作

13 篇文章 0 订阅
10 篇文章 0 订阅

find_entry

前置知识

  这部分需要了解目录项的结构以及i节点的结构,目录项包括一个文件节点号和文件名,见下方代码中的定义。比较重要的是要知道i节点中包含7个直接块和1个一级间接块和1个二级间接块,直接块中存放物理盘块号。

代码

		/* from fs.h */
  163 struct dir_entry {
  164     unsigned short inode;
  165     char name[NAME_LEN];
  166 };
   98 struct m_inode {
   99     unsigned short i_mode;
  100     unsigned short i_uid;
  101     unsigned long i_size;
  102     unsigned long i_mtime;
  103     unsigned char i_gid;
  104     unsigned char i_nlinks;
  105     unsigned short i_zone[9];			/*包括7个直接块和1个一级间接块和1个二级间接块*/
  106 /* these are in memory also */
  107     struct task_struct * i_wait;
  108     struct task_struct * i_wait2;   /* for pipes */
  109     unsigned long i_atime;
  110     unsigned long i_ctime;
  111     unsigned short i_dev;
  112     unsigned short i_num;
  113     unsigned short i_count;
  114     unsigned char i_lock;
  115     unsigned char i_dirt;
  116     unsigned char i_pipe;
  117     unsigned char i_mount;
  118     unsigned char i_seek;
  119     unsigned char i_update;
  120 };

		/*from namei.c */
   86 /*
   87  *  find_entry()
   88  *
   89  * finds an entry in the specified directory with the wanted name. It
   90  * returns the cache buffer in which the entry was found, and the entry
   91  * itself (as a parameter - res_dir). It does NOT read the inode of the
   92  * entry - you'll have to do that yourself if you want to.
   93  *
   94  * This also takes care of the few special cases due to '..'-traversal
   95  * over a pseudo-root and a mount point.
   96  */
   97 static struct buffer_head * find_entry(struct m_inode ** dir,
   98     const char * name, int namelen, struct dir_entry ** res_dir)
   99 {
  100     int entries;
  101     int block,i;
  102     struct buffer_head * bh;
  103     struct dir_entry * de;
  104     struct super_block * sb;
  105
  106 #ifdef NO_TRUNCATE
  107     if (namelen > NAME_LEN)
  108         return NULL;
  109 #else
  110     if (namelen > NAME_LEN)
  111         namelen = NAME_LEN;
  112 #endif
  113     entries = (*dir)->i_size / (sizeof (struct dir_entry));
  114     *res_dir = NULL;
  115 /* check for '..', as we might have to do some "magic" for it */
  116     if (namelen==2 && get_fs_byte(name)=='.' && get_fs_byte(name+1)=='.') {
  117 /* '..' in a pseudo-root results in a faked '.' (just change namelen) */
  118         if ((*dir) == current->root)
  119             namelen=1;
  120         else if ((*dir)->i_num == ROOT_INO) {
  121 /* '..' over a mount-point results in 'dir' being exchanged for the mounted
  122    directory-inode. NOTE! We set mounted, so that we can iput the new dir */
  123             sb=get_super((*dir)->i_dev);
  124             if (sb->s_imount) {
  125                 iput(*dir);
  126                 (*dir)=sb->s_imount;
  127                 (*dir)->i_count++;
  128             }
  129         }
  130     }
  131     if (!(block = (*dir)->i_zone[0]))
  132         return NULL;
  133     if (!(bh = bread((*dir)->i_dev,block)))
  134         return NULL;
  135     i = 0;
  136     de = (struct dir_entry *) bh->b_data;
  137     while (i < entries) {
  138         if ((char *)de >= BLOCK_SIZE+bh->b_data) {
  139             brelse(bh);
  140             bh = NULL;
  141             if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK)) ||
  142                 !(bh = bread((*dir)->i_dev,block))) {
  143                 i += DIR_ENTRIES_PER_BLOCK;
  144                 continue;
  145             }
  146             de = (struct dir_entry *) bh->b_data;
  147         }
  148         if (match(namelen,name,de)) {
  149             *res_dir = de;
  150             return bh;
  151         }
  152         de++;
  153         i++;
  154     }
  155     brelse(bh);
  156     return NULL;
  157 }

讲解

  113行,得到目录文件下的目录项数,i_size即是文件大小
  116-130行,对如果以及是根目录还要进入上级目录的特殊处理,不详细讲代码,只讲下功能。如用户sjc已经在家目录或者已经在根目录还要进入上级目录,前者还是进入当前目录,后者则使用被安装的文件系统的目录i节点上。前者的用意是限制用户不能访问权限以外的目录,后者则是为了处理挂载了多个文件系统的情况,如/tmp/test下挂载了一个文件系统ext2,那么test目录就是这个文件系统的根i节点,此时如果访问test..目录,则使用/tmp的i节点,这样写的原因是对于根节点来说,已经没有目录来包含它的目录项了,只好取得超级块,再取超级块中记录的挂载目录,这个挂载目录是含有根节点的目录项的,于是使用挂载目录的i节点结构。
  131行取直接块块号
  133行从高速缓冲区读取直接块的内容,如果没读到则出错退出。bread的具体过程参见【linux0.12】文件高速缓冲区管理
  137-154行是读取目录项的主要逻辑。138行判断是否以及读完了当前块的内容,如果当前块以及读完了,那么调用bmap得到下一块块号,然后再读取下一块的内容。148行进行对比。bmap的具体过程参见【linux0.12】i节点操作。其实功能很简单,如果传入的逻辑块号是直接块就直接返回,如果是一级间接块,则读取间接块中的内容,返回相应的块号。如果是二级间接块,则读取二级间接块内容,再根据读到的内容读取一级间接块,最后根据一级间接块的内容返回。

总结

  find_entry的主要功能也很清晰,根据块号读取对应块中的内容,然后进行对比。只不过因为有了直接块和间接块的区分,所以把取块号的操作单独抽象为一个函数bmap

new_entry

知道了如何查找目录项,其实新建目录项也就清晰了,本质就是在目录文件中新增一个struct entry结构。下面来看代码

代码

  159 /*
  160  *  add_entry()
  161  *
  162  * adds a file entry to the specified directory, using the same
  163  * semantics as find_entry(). It returns NULL if it failed.
  164  *
  165  * NOTE!! The inode part of 'de' is left at 0 - which means you
  166  * may not sleep between calling this and putting something into
  167  * the entry, as someone else might have used it while you slept.
  168  */
  169 static struct buffer_head * add_entry(struct m_inode * dir,
  170     const char * name, int namelen, struct dir_entry ** res_dir)
  171 {
  172     int block,i;
  173     struct buffer_head * bh;
  174     struct dir_entry * de;
  175
  176     *res_dir = NULL;
  177 #ifdef NO_TRUNCATE
  178     if (namelen > NAME_LEN)
  179         return NULL;
  180 #else
  181     if (namelen > NAME_LEN)
  182         namelen = NAME_LEN;
  183 #endif
  184     if (!namelen)
  185         return NULL;
  186     if (!(block = dir->i_zone[0]))
  187         return NULL;
  188     if (!(bh = bread(dir->i_dev,block)))
  189         return NULL;
  190     i = 0;
  191     de = (struct dir_entry *) bh->b_data;
  192     while (1) {
  193         if ((char *)de >= BLOCK_SIZE+bh->b_data) {
  194             brelse(bh);
  195             bh = NULL;
  196             block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK);
  197             if (!block)
  198                 return NULL;
  199             if (!(bh = bread(dir->i_dev,block))) {
  200                 i += DIR_ENTRIES_PER_BLOCK;
  201                 continue;
  202             }
  203             de = (struct dir_entry *) bh->b_data;
  204         }
  205         if (i*sizeof(struct dir_entry) >= dir->i_size) {
  206             de->inode=0;
  207             dir->i_size = (i+1)*sizeof(struct dir_entry);
  208             dir->i_dirt = 1;
  209             dir->i_ctime = CURRENT_TIME;
  210         }
  211         if (!de->inode) {
  212             dir->i_mtime = CURRENT_TIME;
  213             for (i=0; i < NAME_LEN ; i++)
  214                 de->name[i]=(i<namelen)?get_fs_byte(name+i):0;
  215             bh->b_dirt = 1;
  216             *res_dir = de;
  217             return bh;
  218         }
  219         de++;
  220         i++;
  221     }
  222     brelse(bh);
  223     return NULL;
  224 }

讲解

  186行取得直接块的块号
  188行拿到从高速缓冲区中拿到对应的缓冲头
  191行拿到缓冲区内容
  192-221行完成主要逻辑。首先判断是否读到了该块的末尾,如果是拿到下一个块的块号或者新建一个块,注意这里的create_block并不是真正的新建块,而是如果下个块为空的话才新建,如果不为空则返回块号,create_block的讲解也放在【linux0.12】i节点操作。213-214行设置文件名。注意,这个函数中并不设置文件的inode号,而是在客户调用完这个函数后手动设置。

总结

  以在一个前2个直接块都被占满了的目录文件中新建目录项为例。读取第一个目录块时while循环中三个if判断总是走不到的,于是一直de++;i++;。然后第一个数据块读完发现目录项全被占用,于是走到第一个if分支内,因为第二个直接块是存在的,于是create_block直接返回第二个数据块块号,再读取第二个数据块的内容,读完第二个数据块发现还是第二个数据块的目录项还是全被占用了的,于是下次循环又走到第一个if分支,因为第三个直接块不存在,于是create_block新建一个块并返回块号,接下来再调用reada把新建块的内容读进来。这是突然发现满足了第二个if判断的要求,于是进入代码块执行。因为新建块的inode初始都为0,所以满足第三个if判断,然后设置新目录项的文件名返回。最后调用方再设置新目录项的inode号。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值