其他分享
首页 > 其他分享> > ext4 fs lookup

ext4 fs lookup

作者:互联网

 

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;
}

 

标签:ext4,struct,bh,fs,lookup,dir,block,ra
来源: https://www.cnblogs.com/aspirs/p/15733613.html