static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
ext4_lookup参数列表说明如下:
dir:当前目录@dentry的父目录
dentry:需要查找的当前目录
ext4_lookup()首先调用了ext4_lookup_entry,这个函数根据当前路径的dentry的d_name成员在当前目录的父目录文件(用inode表示)里查找,这个会open父目录文件会涉及到io读操作。查找到后,得到当前目录的ext4_dir_entry_2,此结构体里有当前目录的inode number,然后根据此inode number调用ext4_iget,获得这个inode number对应的inode struct,得到这个inode后调用d_splice_alias()将dentry和inode绑定,即将inode赋值给dentry的d_inode成员。
ext4_lookup_entry里的ext4_fname_prepare_lookup根据dentry的d_name成员设置fname,__ext4_find_entry将根据这个fname进行查找
static struct buffer_head *ext4_lookup_entry(struct inode *dir, struct dentry *dentry, struct ext4_dir_entry_2 **res_dir) { int err; struct ext4_filename fname; struct buffer_head *bh; err = ext4_fname_prepare_lookup(dir, dentry, &fname); if (err == -ENOENT) return NULL; if (err) return ERR_PTR(err); bh = __ext4_find_entry(dir, &fname, res_dir, NULL); ext4_fname_free_filename(&fname); return bh; }
static struct buffer_head *__ext4_find_entry(struct inode *dir, struct ext4_filename *fname, struct ext4_dir_entry_2 **res_dir, int *inlined) { struct super_block *sb; struct buffer_head *bh_use[NAMEI_RA_SIZE]; struct buffer_head *bh, *ret = NULL; ext4_lblk_t start, block; const u8 *name = fname->usr_fname->name; size_t ra_max = 0; /* Number of bh's in the readahead buffer, bh_use[] */ size_t ra_ptr = 0; /* Current index into readahead buffer */ ext4_lblk_t nblocks; int i, namelen, retval; *res_dir = NULL; sb = dir->i_sb; namelen = fname->usr_fname->len; if (namelen > EXT4_NAME_LEN) return NULL; if (ext4_has_inline_data(dir)) { int has_inline_data = 1; ret = ext4_find_inline_entry(dir, fname, res_dir, &has_inline_data); if (has_inline_data) { if (inlined) *inlined = 1; goto cleanup_and_exit; } } if ((namelen <= 2) && (name[0] == '.') && (name[1] == '.' || name[1] == '\0')) { /* * "." or ".." will only be in the first block * NFS may look up ".."; "." should be handled by the VFS */ block = start = 0; nblocks = 1; goto restart; } if (is_dx(dir)) { ret = ext4_dx_find_entry(dir, fname, res_dir); /* * On success, or if the error was file not found, * return. Otherwise, fall back to doing a search the * old fashioned way. */ if (!IS_ERR(ret) || PTR_ERR(ret) != ERR_BAD_DX_DIR) goto cleanup_and_exit; dxtrace(printk(KERN_DEBUG "ext4_find_entry: dx failed, " "falling back\n")); ret = NULL; } nblocks = dir->i_size >> EXT4_BLOCK_SIZE_BITS(sb); //dir->i_size表示dir目录文件的size,所以nblocks表示目录文件size的block num if (!nblocks) { ret = NULL; goto cleanup_and_exit; } start = EXT4_I(dir)->i_dir_start_lookup; //如果之前有读过此dir目录文件,i_dir_start_lookup是上次读这个目录文件找到了目标目录时的block idx;如果dir目录文件之前没有被open过,则i_dir_start_lookup为0 if (start >= nblocks) start = 0; block = start; restart: do { /* * We deal with the read-ahead logic here. */ cond_resched(); if (ra_ptr >= ra_max) { /* Refill the readahead buffer */ ra_ptr = 0; if (block < start) ra_max = start - block; else ra_max = nblocks - block; ra_max = min(ra_max, ARRAY_SIZE(bh_use)); retval = ext4_bread_batch(dir, block, ra_max, //这里进行io read操作,一次读ra_max block,ra表示read ahead(预读) false /* wait */, bh_use); if (retval) { ret = ERR_PTR(retval); ra_max = 0; goto cleanup_and_exit; } } if ((bh = bh_use[ra_ptr++]) == NULL) goto next; wait_on_buffer(bh); if (!buffer_uptodate(bh)) { EXT4_ERROR_INODE(dir, "reading directory lblock %lu", (unsigned long) block); brelse(bh); ret = ERR_PTR(-EIO); goto cleanup_and_exit; } if (!buffer_verified(bh) && !is_dx_internal_node(dir, block, (struct ext4_dir_entry *)bh->b_data) && !ext4_dirent_csum_verify(dir, (struct ext4_dir_entry *)bh->b_data)) { EXT4_ERROR_INODE(dir, "checksumming directory " "block %lu", (unsigned long)block); brelse(bh); ret = ERR_PTR(-EFSBADCRC); goto cleanup_and_exit; } set_buffer_verified(bh); i = search_dirblock(bh, dir, fname, //在读到的block buffer里查找,一次查找一个block,等查找了ra_max个block后仍然没有找到当前目录(fname),再调用上面的ext4_bread_batch()再读ra_max block。 block << EXT4_BLOCK_SIZE_BITS(sb), res_dir); if (i == 1) { //条件满足,表示查找到目标目录/文件 EXT4_I(dir)->i_dir_start_lookup = block; //记录当前查找到的当前目录所在的block的index ret = bh; goto cleanup_and_exit; } else { brelse(bh); if (i < 0) goto cleanup_and_exit; } next: if (++block >= nblocks) //条件成立表示查找到dir目录文件末尾了,因为block可能不是从dir目录文件开头处开始的,即可能不是block 0开始的,所以将block设置为0从目录文件头开始查找,直到block等于start将停止这个while(这个条件即下面while(block != start)) block = 0; } while (block != start); /* * If the directory has grown while we were searching, then * search the last part of the directory before giving up. */ block = nblocks; nblocks = dir->i_size >> EXT4_BLOCK_SIZE_BITS(sb); if (block < nblocks) { start = 0; goto restart; } cleanup_and_exit: /* Clean up the read-ahead blocks */ for (; ra_ptr < ra_max; ra_ptr++) brelse(bh_use[ra_ptr]); return ret; }
ext4_search_dir()参数说明:
bh: dir目录文件内容的buffer head
search_buf: 即bh的data buffer;
buf_size:文件系统super block结构体里的block size成员;
fname: 要查找的目录的name
dir目录文件内容由ext4_dir_entry_2这样的结构体组成,所以ext4_search_dir根据目标目录name fname在bh里查找即可,查找到后将匹配的ext4_dir_entry_2赋值给res_dir:
/* * Returns 0 if not found, -1 on failure, and 1 on success */ int ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size, struct inode *dir, struct ext4_filename *fname, unsigned int offset, struct ext4_dir_entry_2 **res_dir) { struct ext4_dir_entry_2 * de; char * dlimit; int de_len; de = (struct ext4_dir_entry_2 *)search_buf; dlimit = search_buf + buf_size; while ((char *) de < dlimit) { /* this code is executed quadratically often */ /* do minimal checking `by hand' */ if ((char *) de + de->name_len <= dlimit && ext4_match(fname, de)) { /* found a match - just to be sure, do * a full check */ if (ext4_check_dir_entry(dir, NULL, de, bh, search_buf, buf_size, offset)) return -1; *res_dir = de; return 1; } /* prevent looping on a bad block */ de_len = ext4_rec_len_from_disk(de->rec_len, dir->i_sb->s_blocksize); if (de_len <= 0) return -1; offset += de_len; de = (struct ext4_dir_entry_2 *) ((char *) de + de_len); } return 0; }