您的位置:首页 > 运维架构 > Linux

Linux内核2.4.18创建硬链接的系统调用sys_link

2017-04-05 22:27 465 查看
1、磁盘目录项结构

/*

 * The new version of the directory entry.  Since EXT2 structures are

 * stored in intel byte order, and the name_len field could never be

 * bigger than 255 chars, it's safe to reclaim the extra byte for the

 * file_type field.

 */

struct ext2_dir_entry_2 {

    __u32    inode;            /* Inode number */

    __u16    rec_len;        /* Directory entry length */

    __u8    name_len;        /* Name length */

    __u8    file_type;

    char    name[EXT2_NAME_LEN];    /* File name */

};

inode:inode节点号

rec_len:目录项长度

name_len:实际的目录项名称长度(一般不等于EXT2_NAME_LEN)

file_type:文件类型编码

name:目录项名称(不含'\0')

2、调用路径:sys_link->vfs_link->ext2_link->ext2_add_nondir->ext2_add_link

3、函数分析

/*

 * Hardlinks are often used in delicate situations.  We avoid

 * security-related surprises by not following symlinks on the

 * newname.  --KAB

 *

 * We don't follow them on the oldname either to be compatible

 * with linux 2.0, and to avoid hard-linking to directories

 * and other special files.  --ADM

 */

asmlinkage long sys_link(const char * oldname, const char * newname)

{

    int error;

    char * from;

    char * to;

    from = getname(oldname);

    if(IS_ERR(from))

        return PTR_ERR(from);

    to = getname(newname);

    error = PTR_ERR(to);

    if (!IS_ERR(to)) {

        struct dentry *new_dentry;

        struct nameidata nd, old_nd;

        error = 0;

        if (path_init(from, LOOKUP_POSITIVE, &old_nd))

            error = path_walk(from, &old_nd);

        if (error)

            goto exit;

        if (path_init(to, LOOKUP_PARENT, &nd))

            error = path_walk(to, &nd);

        if (error)

            goto out;

        error = -EXDEV;

        if (old_nd.mnt != nd.mnt)

            goto out_release;

        new_dentry = lookup_create(&nd, 0);

        error = PTR_ERR(new_dentry);

        if (!IS_ERR(new_dentry)) {

            error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry);

            dput(new_dentry);

        }

        up(&nd.dentry->d_inode->i_sem);

out_release:

        path_release(&nd);

out:

        path_release(&old_nd);

exit:

        putname(to);

    }

    putname(from);

    return error;

}

//作为系统调用API和指定文件系统层的接口,处于VFS层。

int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)

{

    struct inode *inode;

    int error;

    down(&dir->i_zombie);

    error = -ENOENT;

    inode = old_dentry->d_inode;

    if (!inode)

        goto exit_lock;

    error = may_create(dir, new_dentry);//检查是否具备创建权限

    if (error)

        goto exit_lock;

    error = -EXDEV;

    if (dir->i_dev != inode->i_dev) //硬链接不能跨设备,跨文件系统建立。

        goto exit_lock;

    /*

     * A link to an append-only or immutable file cannot be created.

     */

    error = -EPERM;

    if (IS_APPEND(inode) || IS_IMMUTABLE(inode))

        goto exit_lock;

    if (!dir->i_op || !dir->i_op->link)

        goto exit_lock;

    DQUOT_INIT(dir);

    lock_kernel();

    error = dir->i_op->link(old_dentry, dir, new_dentry);//调用ext2_link

    unlock_kernel();

exit_lock:

    up(&dir->i_zombie);

    if (!error)

        inode_dir_notify(dir, DN_CREATE);

    return error;

}

//ext2文件系统的创建链接函数,调用ext2_add_nondir。

static int ext2_link (struct dentry * old_dentry, struct inode * dir,

    struct dentry *dentry)

{

    struct inode *inode = old_dentry->d_inode;

    if (S_ISDIR(inode->i_mode)) //不能创建指向目录节点的硬链接

        return -EPERM;

    if (inode->i_nlink >= EXT2_LINK_MAX)

        return -EMLINK;

    inode->i_ctime = CURRENT_TIME;

    ext2_inc_count(inode);

    atomic_inc(&inode->i_count);

    return ext2_add_nondir(dentry, inode);

}

//在dentry->parent目录中创建指向节点inode(节点号inode->i_ino)的链接,并将对应的内存目录项dentry和inode建立关联

static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode)

{

    int err = ext2_add_link(dentry, inode);

    if (!err) {

        d_instantiate(dentry, inode);

        return 0;

    }

    ext2_dec_count(inode);

    iput(inode);

    return err;

}

/*

 *    Parent is locked.

 */

int ext2_add_link (struct dentry *dentry, struct inode *inode)

{

    struct inode *dir = dentry->d_parent->d_inode;

    const char *name = dentry->d_name.name;

    int namelen = dentry->d_name.len;

    unsigned reclen = EXT2_DIR_REC_LEN(namelen);//根据创建目录项名称长度计算出目录项长度reclen

    unsigned short rec_len, name_len;

    struct page *page = NULL;

    ext2_dirent * de;

    unsigned long npages = dir_pages(dir);//根据目录长度,推算出目录文件的页面数

    unsigned long n;

    char *kaddr;

    unsigned from, to;

    int err;

    /* We take care of directory expansion in the same loop */

    for (n = 0; n <= npages; n++) {//循环检测每一页,寻找目录项空位

        page = ext2_get_page(dir, n);

        err = PTR_ERR(page);

        if (IS_ERR(page))

            goto out;

        kaddr = page_address(page);//本页起始地址:页结构指针转换为线性地址

        de = (ext2_dirent *)kaddr;//当前目录项地址

        kaddr += PAGE_CACHE_SIZE - reclen;//kaddr=本页最后一个目录项地址(页起始地址+页长-新目录项长度)

        while ((char *)de <= kaddr) { //页内循环检测每个目录项,一旦下个检测地址高于kaddr,那么说明本页没有适合的位置,必须看下页

            err = -EEXIST;

            if (ext2_match (namelen, name, de))//目录中存在同名的目录项,则退出,返回-EEXIST

                goto out_page;

            name_len = EXT2_DIR_REC_LEN(de->name_len);//name_len=当前目录项需要的长度

            rec_len = le16_to_cpu(de->rec_len); //rec_len=当前目录项实际占用的长度

            if (!de->inode && rec_len >= reclen)//情况1:如果当前目录项空闲,且长度合适,那么可以放置新的目录项,转向got_it。

                goto got_it;

            if (rec_len >= name_len + reclen)//情况2:如果当前目录项长度>=当前目录项需要长度+新目录项需要长度,那么可以从中分出尾部部分放置新目录项

                goto got_it;

            de = (ext2_dirent *) ((char *) de + rec_len);//否则看下个目录项

        }

        ext2_put_page(page);

    }

    BUG();

    return -EINVAL;

got_it:

    from = (char*)de - (char*)page_address(page);

    to = from + rec_len;

    lock_page(page);

    err = page->mapping->a_ops->prepare_write(NULL, page, from, to);

    if (err)

        goto out_unlock;

    if (de->inode) {//针对情况2

        ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len);//新目录项的放置位置de1在当前目录项后边,即当前目录项位置de+当前目录项实际需要的长度name_len

        de1->rec_len = cpu_to_le16(rec_len - name_len);//新目录项的实际占用长度de1->rec_len=当前目录项实际占用长度rec_len - 当前目录项需要的长度name_len

        de->rec_len = cpu_to_le16(name_len);//修改当前目录项实际占用长度rec_len为需要的长度name_len

        de = de1;//de指向新目录项的位置

    }

    de->name_len = namelen;

    memcpy (de->name, name, namelen);//拷贝目录项名称字符串

    de->inode = cpu_to_le32(inode->i_ino);//关联i节点号

    ext2_set_de_type (de, inode);

    err = ext2_commit_chunk(page, from, to);//提交页面的修改部分(相关bh置为脏),如果块设备有MS_SYNCHRONOUS标志或者目录文件有S_SYNC标志,则立刻同步的磁盘。

    dir->i_mtime = dir->i_ctime = CURRENT_TIME;//修改目录节点的时间戳。

    mark_inode_dirty(dir);//目录文件节点置为脏

    /* OFFSET_CACHE */

out_unlock:

    UnlockPage(page);

out_page:

    ext2_put_page(page);

out:

    return err;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息