关于inode,dentry结合软链接及硬链接的实验
一、背景
在之前的博客 缺页异常导致的iowait打印出相关文件的绝对路径-CSDN博客 里 2.2.3 一节里,我们讲到了file,fd,inode,dentry,super_block这几个概念,在这篇博客里,我们针对inode和dentry做一些实验,针对的是软链接和硬链接的场景。
二、实验源码及最普通常见的场景
2.1 实验源码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/dcache.h>
#include <linux/namei.h>// 模块参数
static char *filepath = "/tmp/testfile"; // 默认文件路径
module_param(filepath, charp, S_IRUGO);
MODULE_PARM_DESC(filepath, "Path of the file to open");char buffer[4096];int getfullpath(struct inode *inode)
{struct dentry *dentry;hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) {char *path;path = dentry_path_raw(dentry, buffer, PAGE_SIZE);if (IS_ERR(path)){continue; }printk("dentry name = %s , path = %s\n", dentry->d_name.name, path);}return 0;
}static int __init my_module_init(void) {struct file *file;printk(KERN_INFO "Opening file: %s\n", filepath);// 打开文件file = filp_open(filepath, O_RDONLY, 0);if (IS_ERR(file)) {printk(KERN_ERR "Error opening file: %ld\n", PTR_ERR(file));return PTR_ERR(file);}getfullpath(file->f_inode);// 关闭文件filp_close(file, NULL);return -EINVAL;
}static void __exit my_module_exit(void) {printk(KERN_INFO "Module exiting\n");
}module_init(my_module_init);
module_exit(my_module_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zhaoxin");
MODULE_DESCRIPTION("A simple module to read dentry from a file's inode");
2.2 源码分析
上面的源码还是比较简单的,在内核模块里打开一个文件,使用传入的filepath参数,要注意,如果用file_path这个名字会造成和内核里的file_path的符号相冲突导致编译不过的情况。
内核模块根据传入的filepath的路径(可以是相对路径)打开文件,然后使用打开的文件里的file->f_inode作为参数,传给getfullpath函数,这个getfullpath函数改造自之前的博客 缺页异常导致的iowait打印出相关文件的绝对路径-CSDN博客 里的 2.2.3 一节里的getfullpath函数,改得更简单,只是打印到dmesg里:
insmod ko一定会返回失败,为了方便调试,不用rmmod,可直接再运行insmod:
2.3 最普通场景的场景的运行情况
我们创建一个aa.txt文件,然后执行insmod testinode.ko filepath=aa.txt
dmesg的内容如下:
这时候,这个aa.txt就一个文件,没有创建过相关的硬链接或软链接。
三、增加一个硬链接后的运行情况
我们通过如下命令创建一个硬链接:
ln aa.txt bb.txt
然后执行一样的命令:
insmod testinode.ko filepath=aa.txt
dmesg里的内容如下:
如果用:
insmod testinode.ko filepath=bb.txt
dmesg里的内容是一样的:
可以看到,如果创建一个硬链接后,用其中任意一个符号来open文件,通过其对应的inode遍历得到的dentry全路径是一模一样的。
为了进一步做实验,我们把inode的指针也打出来:
可以从上图里看到,无论是打开aa.txt还是打开bb.txt,其inode的地址是一样的。
四、增加一个软链接后的运行情况
通过ln -s bb.txt slinkbb.txt之后,再运行抓到的dmesg情况:
可以看到用软链接的名字来open和用硬链接的名字来open,得到的file的f_inode地址是一样的,自然通过f_inode指针遍历得到的dentry也是一样的。
我们用file里的f_path.dentry来打印出其name:
do {char *path;path = dentry_path_raw(file->f_path.dentry, buffer, PAGE_SIZE);if (IS_ERR(path)){break;}printk("[2] dentry name = %s , path = %s\n", file->f_path.dentry->d_name.name, path);} while(0);
得到的dmesg如下:
说明file里的f_path.dentry可以得到它的软链接的源头文件路径。
4.1 软链接不同于硬链接,会新分配一个inode
通过ls -li如下图就可以看到新增一个硬链接并不会新增一个inode,而新增一个软链接就会新增一个inode(另外,之所以用open出来的file看似关联不上这个新的inode,因为它已经在filp_open期间根据软链接的inode找到了实体文件)
另外,软链接可以链接一个文件夹或一个不存在的文件,而硬链接不行。
4.2 软链接相关的内核实现
我们看一下软链接相关的内核实现,看看是哪里分配的一个inode。
创建软链接的系统调用是symlink,如下图实现:
看一下它调用的do_symlinkat的实现:
上图里创建软链接的核心函数是红色框出的vfs_symlink函数,如下图调用的是对应文件系统的symlink实现:
比如ext4的symlink实现,如下图里的ext4_new_inode_start_handle函数来创建新的inode:
然后调用下图里的ext4_add_nondir函数来把创建出来的inode放到所在的目录的数据块里(也建立了inode和dentry的链接):