Linux hard link and soft link implementation comparison

Overview

There is a special file in linux system called link ( link). In layman's terms, a link is a path from one file to another. There are two types of links in Linux, hard links and soft links. Simply put, a hard link is equivalent to sharing one of the source file and the link file in the disk and memory inode. Therefore, the link file and the source file are different dentry. Therefore, this feature determines that the hard link cannot cross the file system, and we cannot create a directory. Create hard links. Different soft links and hard links, first of all soft links can span file systems, and secondly, links and source files have different inodeand dentry, therefore, the properties and the contents of two files is also very different, file content soft link file is the source file file name.

Insert picture description here

Hard link implementation

After reading the previous introduction about hard links and soft links, let's carefully study the implementation of hard links and soft links in the Linux kernel.

Using the stracetool, you can find that the function called by the hard link is established link(). The kernel entry of the function is SYSCALL_DEFINE2(), in fact, it is sys_link(). We start from this entry to follow the implementation principle step by step.

SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname)
{
    
    
    return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
}

sys_link()The function is actually called sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0).

SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
int, newdfd, const char __user *, newname, int, flags)
{
    
    
    struct dentry *new_dentry;
    struct nameidata nd;
    struct path old_path;
    int error;
    char *to;

    if ((flags & ~AT_SYMLINK_FOLLOW) != 0)
        return -EINVAL;
    error = user_path_at(olddfd, oldname,
		         flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0,
		        &old_path);
    if (error)
        return error;

    /* 查找目的链接名的父目录的dentry */
    error = user_path_parent(newdfd, newname, &nd, &to);
    if (error)
        goto out;

    error = -EXDEV;
    /* 如果源和目的不是同一个文件系统,则返回错误 */	
    if (old_path.mnt != nd.path.mnt)
        goto out_release;

    /* 为链接文件创建dentry结构 */
    new_dentry = lookup_create(&nd, 0);
    error = PTR_ERR(new_dentry);
    if (IS_ERR(new_dentry))
        goto out_unlock;
    error = mnt_want_write(nd.path.mnt);
    if (error)
        goto out_dput;
    error = security_path_link(old_path.dentry, &nd.path, new_dentry);
    if (error)
        goto out_drop_write;
    error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry);
    ...
    return error;
}

In fact, we can think carefully + the above illustration can understand that the main things that create a hard link do include: create one for the link file dentry, initialize it (mainly to initialize its inodenumber); write the link file dentryinto the data block of the parent directory in. Therefore, the above code page is clear at a glance. The main things the code does are:

  • For legality check, we said earlier that hard links cannot cross file systems. This is because the link file and the source file share one inode, and the inodenumber is meaningful in the same file system;

  • Get the inodestructure of the parent directory of the linked file ;

  • Create a dentrystructure for the linked file ;

  • After all the preparations are ready, initialize dentrythe inodenumber in the link file structure and add it to the data block of the parent directory.

Each of the above steps 1, 2, corresponding to the above function, and step 4 is the main work is vfs_link()underway, which argument passed meaning code also made a more detailed description vfs_link()of The implementation is as follows:

int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
    
    
    struct inode *inode = old_dentry->d_inode;
    int error;

    if (!inode)
        return -ENOENT;

    /* 检查是否有创建文件目录项权限 */
    error = may_create(dir, new_dentry);
    if (error)
        return error;

    if (dir->i_sb != inode->i_sb)
        return -EXDEV;

    if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
        return -EPERM;

    /* 调用具体文件系统的link,如ext3_link() */
    if (!dir->i_op->link)
        return -EPERM;
    if (S_ISDIR(inode->i_mode))
        return -EPERM;

    error = security_inode_link(old_dentry, dir, new_dentry);
    if (error)
        return error;
    mutex_lock(&inode->i_mutex);
    error = dir->i_op->link(old_dentry, dir, new_dentry);
    mutex_unlock(&inode->i_mutex);
    if (!error)
        fsnotify_link(dir, inode, new_dentry);
     return error;
}

vfs_link()It mainly completes some parameter checking tasks, and finally calls the implementation of the specific file system link, such as the ext3file system ext3_link().

static int ext3_link (struct dentry * old_dentry,
		       struct inode * dir, struct dentry *dentry)
{
    
    
    handle_t *handle;
    struct inode *inode = old_dentry->d_inode;
    int err, retries = 0;

    /* 如果文件上的链接数过多,返回Too many links错误 */
    if (inode->i_nlink >= EXT3_LINK_MAX)
        return -EMLINK;

    dquot_initialize(dir);

    if (inode->i_nlink == 0)
        return -ENOENT;
retry:
    handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
					EXT3_INDEX_EXTRA_TRANS_BLOCKS);
    if (IS_ERR(handle))
        return PTR_ERR(handle);

    if (IS_DIRSYNC(dir))
        handle->h_sync = 1;

    inode->i_ctime = CURRENT_TIME_SEC;
    /* 将源文件inode上的链接数 + 1 */
    inc_nlink(inode);
    atomic_inc(&inode->i_count);

    /* 将链接文件的dentry写入到其父目录的数据块中 */
    err = ext3_add_entry(handle, dentry, inode);
    if (!err) {
    
    
        ext3_mark_inode_dirty(handle, inode);
	d_instantiate(dentry, inode);
    } else {
    
    
	drop_nlink(inode);
	iput(inode);
    }
    ext3_journal_stop(handle);
    if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))
        goto retry;
     return err;
}

In ext3_link()the completion of specific work linked, to put aside some of the log-related content, we can see the main call ext3_add_entry()to the linked file dentryis added to the data block in the parent directory, at the same time also the source file inodenumber recorded in In the link file dentry, this achieves the purpose of dentrysharing the source file and the link file with different structures inode.

Soft link implementation

Using the stracetool, you can find that the function called to establish a hard link is symlink(). The kernel entry of this function is SYSCALL_DEFINE2(symlink,...), in fact, it is sys_symlink(). We start from this entry and follow the internal implementation principles step by step.

SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newname)
{
    
    
    return sys_symlinkat(oldname, AT_FDCWD, newname);
}

sys_symlink()The function was called sys_symlinkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0).

SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
 int, newdfd, const char __user *, newname)
{
    
    
    ......
    from = getname(oldname);
    if (IS_ERR(from))
        return PTR_ERR(from);

    /* 查找软链接父目录结构,存于nd之中 */
    error = user_path_parent(newdfd, newname, &nd, &to);
    if (error)
        goto out_putname;

    /* 在上面查找的父目录下创建软连接dentry,作为返回值 */
    dentry = lookup_create(&nd, 0);
    error = PTR_ERR(dentry);
    if (IS_ERR(dentry))
        goto out_unlock;

    error = mnt_want_write(nd.path.mnt);
    if (error)
        goto out_dput;
    error = security_path_symlink(&nd.path, dentry, from);
    if (error)
        goto out_drop_write;
 
    /* d_inode:链接文件父目录inode结构
     * dentry:链接文件的dentry结构
     * from:源文件名
     */
    error = vfs_symlink(nd.path.dentry->d_inode, dentry, from);
    ......
    return error;
}

As you can see from the code, the basic function call process is sys_linkatexactly the same, but the last call is vfs_symlinkat. Moreover, the meaning of the parameters is slightly different, see the code comments:

/* 建立软链接
 * @dir:软连接父目录inode
 * @dentry:软连接的dentry
 * @oldname:源文件或目录的名字
 */
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
{
    
    
    int error = may_create(dir, dentry);
    if (error)
        return error;

    if (!dir->i_op->symlink)
        return -EPERM;

    error = security_inode_symlink(dir, dentry, oldname);
    if (error)
        return error;

    error = dir->i_op->symlink(dir, dentry, oldname);
    if (!error)
	fsnotify_create(dir, dentry);

    return error;
}

In the end, the symlinkfunctions of the specific file system are called , such as ext3_symlink().

//ext3建立软连接函数
//@dir:软连接的父目录的inode
//@dentry:软连接的dentry结构
//@symname:源文件名称
static int ext3_symlink (struct inode * dir,
		struct dentry *dentry, const char * symname)
{
    
    
    handle_t *handle;
    struct inode * inode;
    int l, err, retries = 0;
 
    l = strlen(symname)+1;
    if (l > dir->i_sb->s_blocksize)
        return -ENAMETOOLONG;
    dquot_initialize(dir);

retry:
    handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) +
					EXT3_INDEX_EXTRA_TRANS_BLOCKS + 5 +
					EXT3_MAXQUOTAS_INIT_BLOCKS(dir->i_sb));

    if (IS_ERR(handle))
        return PTR_ERR(handle);

    if (IS_DIRSYNC(dir))
	handle->h_sync = 1;

    // 为软连接创建一个新的inode结构
    inode = ext3_new_inode (handle, dir, S_IFLNK|S_IRWXUGO);
    err = PTR_ERR(inode);
    if (IS_ERR(inode))
        goto out_stop;

    if (l > sizeof (EXT3_I(inode)->i_data)) {
    
    
	inode->i_op = &ext3_symlink_inode_operations;
	ext3_set_aops(inode);
	err = __page_symlink(inode, symname, l, 1);
        if (err) {
    
    
	  ......
	}
    } else {
    
    
        /* 如果源文件名称不够长
         * 可直接将其保存在inode的i_data中
         */
	inode->i_op = &ext3_fast_symlink_inode_operations;
        memcpy((char*)&EXT3_I(inode)->i_data,symname,l);
        inode->i_size = l-1;
    }
    EXT3_I(inode)->i_disksize = inode->i_size;
    /* 将链接文件的inode和dentry关联并
     * 与其父目录建立关联
     */
    err = ext3_add_nondir(handle, dentry, inode);

out_stop:
    .....
}

ext3_symlinkThe realization of analysis , regardless of the log and other modules, has the following key steps:

  • The code will create a inodestructure for the link file , which ext3_new_inode()is implemented in the function , which is also the biggest difference between hard link and soft link;
  • The file content of the link file is the file name of the source file, and if the file name is not very long (less than 60 bytes), the file name will be saved directly in the file inodewithout assigning data blocks to it;
  • Finally, the file will link inodewith the dentryassociation, and link files dentrywritten to the data block parent directory, call a function ext3_add_nondir().

Linux, C/C++ technology exchange group: [960994558] I have compiled some good study books, interview questions from major companies, and popular technology teaching video materials to share in it (including C/C++, Linux, Nginx, ZeroMQ, MySQL) , Redis, fastdfs, MongoDB, ZK, streaming media, CDN, P2P, K8S, Docker, TCP/IP, coroutine, DPDK, etc.), you can add it yourself if you need it! ~Insert picture description here

Insert picture description here

Guess you like

Origin blog.csdn.net/weixin_52622200/article/details/113250372