Linux文件系统的层次结构:
思考:作为与文件平级的磁盘设备文件与作为文件系统底层的磁盘设备之间有什么区别呢?
分析代码,内核的do_sys_open加入检查点,过滤进程touch的目的是,这样可以简单的在用户态通过touch命令创建文件的方式触发进入此分支。
caozilong@caozilong-Vostro-3268:~/Workspace/linux-compile/linux-5.4.129$ git diff
diff --git a/fs/open.c b/fs/open.c
index dcbd01611..f23170ddb 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -1081,6 +1081,10 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
struct open_flags op;
int fd = build_open_flags(flags, mode, &op);
struct filename *tmp;
+ char buf[TASK_COMM_LEN];
+
+ memset(buf, 0x00, TASK_COMM_LEN);
+ get_task_comm(buf, current);
if (fd)
return fd;
@@ -1098,6 +1102,28 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
} else {
fsnotify_open(f);
fd_install(fd, f);
+ if(strcmp(buf, "touch") == 0)
+ {
+ struct path path;
+ struct vfsmount *mnt;
+ char *name1, *name2;
+ char buf[64];
+ char buf1[128];
+
+ memset(buf, 0, sizeof(buf));
+ memset(buf1, 0, sizeof(buf1));
+
+ mnt = f->f_path.mnt;
+ path.mnt = mnt;
+ path.dentry = mnt->mnt_root;
+ name1 = d_path(&path, buf, sizeof(buf));
+ name2 = file_path(f, buf1, sizeof(buf1));
+ if(mnt && strstr(name2, ".txt"))
+ {
+ printk("%s line %d, fstype = %s,mount-root=%s, mountpoint=%s, filepath=%s, vfsmount=0x%p.\n", \
+ __func__, __LINE__, mnt->mnt_sb->s_type->name, mnt->mnt_root->d_name.name, name1, name2, mnt);
+ }
+ }
}
}
putname(tmp);
df命令查看文件系统挂载情况
caozilong@caozilong-Vostro-3268:~/Workspace/linux-compile/linux-5.4.129$ df
文件系统 1K-块 已用 可用 已用% 挂载点
udev 3804456 0 3804456 0% /dev
tmpfs 805864 2044 803820 1% /run
/dev/sda7 150140200 95560684 46883152 68% /
tmpfs 4029312 0 4029312 0% /dev/shm
tmpfs 5120 4 5116 1% /run/lock
tmpfs 4029312 0 4029312 0% /sys/fs/cgroup
/dev/loop0 56832 56832 0 100% /snap/core18/2074
/dev/loop2 2560 2560 0 100% /snap/gnome-calculator/884
/dev/loop4 66688 66688 0 100% /snap/gtk-common-themes/1515
/dev/loop3 1024 1024 0 100% /snap/gnome-logs/81
/dev/loop5 384 384 0 100% /snap/gnome-characters/708
/dev/loop6 640 640 0 100% /snap/gnome-logs/103
/dev/loop7 768 768 0 100% /snap/gnome-characters/723
/dev/loop8 101760 101760 0 100% /snap/core/11316
/dev/loop1 249856 249856 0 100% /snap/gnome-3-38-2004/39
/dev/loop9 46080 46080 0 100% /snap/gtk-common-themes/1440
/dev/loop10 224256 224256 0 100% /snap/gnome-3-34-1804/72
/dev/loop11 4352 4352 0 100% /snap/gnome-calculator/544
/dev/loop12 164096 164096 0 100% /snap/gnome-3-28-1804/116
/dev/loop13 166784 166784 0 100% /snap/gnome-3-28-1804/145
/dev/loop15 2560 2560 0 100% /snap/gnome-system-monitor/160
/dev/loop14 63232 63232 0 100% /snap/core20/1026
/dev/loop16 56064 56064 0 100% /snap/core18/1668
/dev/loop17 91264 91264 0 100% /snap/core/8268
/dev/loop18 2304 2304 0 100% /snap/gnome-system-monitor/157
/dev/sdb2 3871400 605860 3049172 17% /boot
/dev/sda2 97280 32433 64847 34% /boot/efi
tmpfs 805860 16 805844 1% /run/user/121
tmpfs 805860 36 805824 1% /run/user/1000
创建脚本,分别在上述路径下创建一个.txt后缀名的文件
touch /dev/zilong.txt
touch /run/zilong.txt
touch /zilong.txt
touch /dev/shm/zilong.txt
touch /run/lock/zilong.txt
touch /sys/fs/cgroup/zilong.txt
touch /snap/core18/2074/zilong.txt
touch /snap/gnome-calculator/884/zilong.txt
touch /snap/gtk-common-themes/1515/zilong.txt
touch /snap/gnome-logs/81/zilong.txt
touch /snap/gnome-characters/708/zilong.txt
touch /snap/gnome-logs/103/zilong.txt
touch /snap/gnome-characters/723/zilong.txt
touch /snap/core/11316/zilong.txt
touch /snap/gnome-3-38-2004/39/zilong.txt
touch /snap/gtk-common-themes/1440/zilong.txt
touch /snap/gnome-3-34-1804/72/zilong.txt
touch /snap/gnome-calculator/544/zilong.txt
touch /snap/gnome-3-28-1804/116/zilong.txt
touch /snap/gnome-3-28-1804/145/zilong.txt
touch /snap/gnome-system-monitor/160/zilong.txt
touch /snap/core20/1026/zilong.txt
touch /snap/core18/1668/zilong.txt
touch /snap/core/8268/zilong.txt
touch /snap/gnome-system-monitor/157/zilong.txt
touch /boot/zilong.txt
touch /boot/efi/zilong.txt
touch /run/user/121/zilong.txt
touch /run/user/1000/zilong.txt
touch /tmp/zilong.txt
执行sudo bash ./a.sh, 可以看到有些文件无法写入,所以touch写的方式调试失效了,不过没关系,先看其它几个文件系统。
caozilong@caozilong-Vostro-3268:~/Workspace/linux-compile/opentest$ sudo bash ./a.sh
touch: 无法创建'/sys/fs/cgroup/zilong.txt': 只读文件系统
touch: 无法创建'/snap/core18/2074/zilong.txt': 只读文件系统
touch: 无法创建'/snap/gnome-calculator/884/zilong.txt': 只读文件系统
touch: 无法创建'/snap/gtk-common-themes/1515/zilong.txt': 只读文件系统
touch: 无法创建'/snap/gnome-logs/81/zilong.txt': 只读文件系统
touch: 无法创建'/snap/gnome-characters/708/zilong.txt': 只读文件系统
touch: 无法创建'/snap/gnome-logs/103/zilong.txt': 只读文件系统
touch: 无法创建'/snap/gnome-characters/723/zilong.txt': 只读文件系统
touch: 无法创建'/snap/core/11316/zilong.txt': 只读文件系统
touch: 无法创建'/snap/gnome-3-38-2004/39/zilong.txt': 只读文件系统
touch: 无法创建'/snap/gtk-common-themes/1440/zilong.txt': 只读文件系统
touch: 无法创建'/snap/gnome-3-34-1804/72/zilong.txt': 只读文件系统
touch: 无法创建'/snap/gnome-calculator/544/zilong.txt': 只读文件系统
touch: 无法创建'/snap/gnome-3-28-1804/116/zilong.txt': 只读文件系统
touch: 无法创建'/snap/gnome-3-28-1804/145/zilong.txt': 只读文件系统
touch: 无法创建'/snap/gnome-system-monitor/160/zilong.txt': 只读文件系统
touch: 无法创建'/snap/core20/1026/zilong.txt': 只读文件系统
touch: 无法创建'/snap/core18/1668/zilong.txt': 只读文件系统
touch: 无法创建'/snap/core/8268/zilong.txt': 只读文件系统
touch: 无法创建'/snap/gnome-system-monitor/157/zilong.txt': 只读文件系统
caozilong@caozilong-Vostro-3268:~/Workspace/linux-compile/opentest$
dmesg查看内核打印输出
[ 444.029396] do_sys_open line 1124, fstype = devtmpfs,mount-root=/, mountpoint=/dev, filepath=/dev/zilong.txt, vfsmount=0x0000000094f582a3.
[ 444.029897] do_sys_open line 1124, fstype = tmpfs,mount-root=/, mountpoint=/run, filepath=/run/zilong.txt, vfsmount=0x000000006412958f.
[ 444.030829] do_sys_open line 1124, fstype = ext4,mount-root=/, mountpoint=/, filepath=/zilong.txt, vfsmount=0x0000000068fd3026.
[ 444.031419] do_sys_open line 1124, fstype = tmpfs,mount-root=/, mountpoint=/dev/shm, filepath=/dev/shm/zilong.txt, vfsmount=0x000000000e2b123f.
[ 444.031970] do_sys_open line 1124, fstype = tmpfs,mount-root=/, mountpoint=/run/lock, filepath=/run/lock/zilong.txt, vfsmount=0x000000007d4d921d.
[ 444.048815] do_sys_open line 1124, fstype = ext4,mount-root=/, mountpoint=/boot, filepath=/boot/zilong.txt, vfsmount=0x000000003c744766.
[ 444.049554] do_sys_open line 1124, fstype = vfat,mount-root=/, mountpoint=/boot/efi, filepath=/boot/efi/zilong.txt, vfsmount=0x0000000034dc4c99.
[ 444.050638] do_sys_open line 1124, fstype = tmpfs,mount-root=/, mountpoint=/run/user/121, filepath=/run/user/121/zilong.txt, vfsmount=0x000000004e8529a4.
[ 444.051385] do_sys_open line 1124, fstype = tmpfs,mount-root=/, mountpoint=/run/user/1000, filepath=/run/user/1000/zilong.txt, vfsmount=0x00000000799dbb6f.
[ 444.052149] do_sys_open line 1124, fstype = ext4,mount-root=/, mountpoint=/, filepath=/tmp/zilong.txt, vfsmount=0x0000000068fd3026.
可以看到,系统中的挂载点有 /dev, /run, /, /dev/shm, /run/lock, /boot,/boot/efi, /run/user/121, /run/user/1000,图示表示如下:
让人感到意外的是/tmp文件系统这里没有新的挂载点,仅仅是/根文件系统的一个普通目录而已,之前一直认为/tmp文件系统是一个内存文件系统的挂载点的。
简单分析一下vfsmount结构:
对于某个文件系统实例,内存中 super_block 和 vfsmount 都是唯一的。比如,我们将某个挂载硬盘分区 mount -t vfat /dev/hda2 /mnt/d。实际上就是新建一个 vfsmount 结构作为连接件,vfsmount->mnt_sb = /dev/hda2 的超级块结构;vfsmount->mnt_root =/dev/hda2 的"根"目录的 dentry。
修改pattern,增加对mnt->mnt_root->d_parent和mnt->mnt_root的打印输出,观察它们是不是同一个
caozilong@caozilong-Vostro-3268:~/Workspace/linux-compile/linux-5.4.129$ git diff
diff --git a/fs/open.c b/fs/open.c
index dcbd01611..2cafc865d 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -1081,6 +1081,10 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
struct open_flags op;
int fd = build_open_flags(flags, mode, &op);
struct filename *tmp;
+ char buf[TASK_COMM_LEN];
+
+ memset(buf, 0x00, TASK_COMM_LEN);
+ get_task_comm(buf, current);
if (fd)
return fd;
@@ -1098,6 +1102,30 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
} else {
fsnotify_open(f);
fd_install(fd, f);
+ if(strcmp(buf, "touch") == 0)
+ {
+ struct path path;
+ struct dentry *parent;
+ struct vfsmount *mnt;
+ char *name1, *name2;
+ char buf[64];
+ char buf1[128];
+
+ memset(buf, 0, sizeof(buf));
+ memset(buf1, 0, sizeof(buf1));
+
+ mnt = f->f_path.mnt;
+ path.mnt = mnt;
+ path.dentry = mnt->mnt_root;
+ parent = mnt->mnt_root->d_parent;
+ name1 = d_path(&path, buf, sizeof(buf));
+ name2 = file_path(f, buf1, sizeof(buf1));
+ if(mnt && strstr(name2, ".txt"))
+ {
+ printk("%s line %d, fstype = %s,mount-root=%s, mountpoint=%s, filepath=%s, vfsmount=0x%p, parent=%s.parent=0x%p, mnt->mnt_root=0x%p\n", \
+ __func__, __LINE__, mnt->mnt_sb->s_type->name, mnt->mnt_root->d_name.name, name1, name2, mnt, parent->d_name.name, parent, mnt->mnt_root);
+ }
+ }
}
}
putname(tmp);
caozilong@caozilong-Vostro-3268:~/Workspace/linux-compile/linux-5.4.129$
执行sudo touch /tmp/zilong.txt后,查看dmesg. 根据输出结果,可以看出它们确实是相同的
这说明在挂载点构造的vfsmount结构并没有指向父文件系统的dentry结构,那么疑问就产生了,系统是如何找到挂载点的父文件系统的dentry结构的呢?
揭开文件系统根目录背后的“影子”的真面目
diff --git a/fs/open.c b/fs/open.c
index dcbd01611..43bb0a09a 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -32,8 +32,8 @@
#include <linux/ima.h>
#include <linux/dnotify.h>
#include <linux/compat.h>
-
#include "internal.h"
+#include "mount.h"
int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
struct file *filp)
@@ -1081,6 +1081,10 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
struct open_flags op;
int fd = build_open_flags(flags, mode, &op);
struct filename *tmp;
+ char buf[TASK_COMM_LEN];
+
+ memset(buf, 0x00, TASK_COMM_LEN);
+ get_task_comm(buf, current);
if (fd)
return fd;
@@ -1098,6 +1102,35 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
} else {
fsnotify_open(f);
fd_install(fd, f);
+ if(strcmp(buf, "touch") == 0)
+ {
+ struct path path;
+ struct dentry *parent;
+ struct vfsmount *mnt;
+ struct mount *realmnt;
+ struct mount *mnt_parent;
+ char *name1, *name2;
+ char buf[64];
+ char buf1[128];
+
+ memset(buf, 0, sizeof(buf));
+ memset(buf1, 0, sizeof(buf1));
+
+ mnt = f->f_path.mnt;
+ path.mnt = mnt;
+ path.dentry = mnt->mnt_root;
+ parent = mnt->mnt_root->d_parent;
+ name1 = d_path(&path, buf, sizeof(buf));
+ name2 = file_path(f, buf1, sizeof(buf1));
+ //realmnt = container_of(mnt, struct mount, mnt);
+ realmnt = real_mount(mnt);
+ mnt_parent = realmnt->mnt_parent;
+ if(mnt && strstr(name2, ".txt"))
+ {
+ printk("%s line %d, fstype = %s,mount-root=%s, mountpoint=%s, filepath=%s, vfsmount=0x%p, parent=%s.parent=0x%p, mnt->mnt_root=0x%p, realmnt->mnt_mountpoint=0x%p,mnt_parent->d_name.name=%s, mnt_parent->mnt_mountpoint->d_name.name=%s.mnt_parent->mnt_mountpoint=0x%p,mnt_parent->mnt.mnt_sb->s_type->name=%s.\n", \
+ __func__, __LINE__, mnt->mnt_sb->s_type->name, mnt->mnt_root->d_name.name, name1, name2, mnt, parent->d_name.name, parent, mnt->mnt_root, realmnt->mnt_mountpoint, mnt_parent->mnt.mnt_root->d_name.name,mnt_parent->mnt_mountpoint->d_name.name, mnt_parent->mnt_mountpoint, mnt_parent->mnt.mnt_sb->s_type->name);
+ }
+ }
}
}
putname(tmp);
执行sudo touch /tmp/zilong.txt, dmesg后:
可以看到vfsmount内嵌于struct mount结构中,根据struct mount可以找到挂载树结构
由于tmp隶属于"/"文件系统,所以我们直接找到了"/" dentry. 但是令人意外的是,"/"的 mount parent不为空,并且根据strucdt mount找到的这个"\"背后的文件系统竟然是rootfs! 这里有一个问题,假设现在的根文件系统对应块设备/dev/sda1, 那么mount -t ext4 /dev/sda1 时,/目录还不存在,那么dev和sda1又从何来呢?内核在启动时mount一个临时的根目录,这个临时的根目录的文件系统类别就是rootfs,它不对应任何磁盘文件,也就是它属于一个内存文件系统。rootfs在ubuntu 系统中同样存在,可以通过cat /proc/filesystems查看.
sudo touch /dev/zilong.txt 的输出为
diff --git a/fs/open.c b/fs/open.c
index dcbd01611..a0c79fe60 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -32,8 +32,8 @@
#include <linux/ima.h>
#include <linux/dnotify.h>
#include <linux/compat.h>
-
#include "internal.h"
+#include "mount.h"
int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
struct file *filp)
@@ -1081,6 +1081,10 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
struct open_flags op;
int fd = build_open_flags(flags, mode, &op);
struct filename *tmp;
+ char buf[TASK_COMM_LEN];
+
+ memset(buf, 0x00, TASK_COMM_LEN);
+ get_task_comm(buf, current);
if (fd)
return fd;
@@ -1098,6 +1102,35 @@ long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
} else {
fsnotify_open(f);
fd_install(fd, f);
+ if(strcmp(buf, "touch") == 0)
+ {
+ struct path path;
+ struct dentry *parent;
+ struct vfsmount *mnt;
+ struct mount *realmnt;
+ struct mount *mnt_parent;
+ char *name1, *name2;
+ char buf[64];
+ char buf1[128];
+
+ memset(buf, 0, sizeof(buf));
+ memset(buf1, 0, sizeof(buf1));
+
+ mnt = f->f_path.mnt;
+ path.mnt = mnt;
+ path.dentry = mnt->mnt_root;
+ parent = mnt->mnt_root->d_parent;
+ name1 = d_path(&path, buf, sizeof(buf));
+ name2 = file_path(f, buf1, sizeof(buf1));
+ //realmnt = container_of(mnt, struct mount, mnt);
+ realmnt = real_mount(mnt);
+ mnt_parent = realmnt->mnt_parent;
+ if(mnt && strstr(name2, ".txt"))
+ {
+ printk("%s line %d, fstype = %s,mount-root=%s, mountpoint=%s, filepath=%s, vfsmount=0x%p, parent=%s.parent=0x%p, mnt->mnt_root=0x%p, realmnt->mnt_mountpoint=0x%p,mnt_parent->d_name.name=%s, mnt_parent->mnt_mountpoint->d_name.name=%s.mnt_parent->mnt_mountpoint=0x%p,mnt_parent->mnt.mnt_sb->s_type->name=%s realmnt->mnt_mountpoint->d_name.name=%s.\n", \
+ __func__, __LINE__, mnt->mnt_sb->s_type->name, mnt->mnt_root->d_name.name, name1, name2, mnt, parent->d_name.name, parent, mnt->mnt_root, realmnt->mnt_mountpoint, mnt_parent->mnt.mnt_root->d_name.name,mnt_parent->mnt_mountpoint->d_name.name, mnt_parent->mnt_mountpoint, mnt_parent->mnt.mnt_sb->s_type->name, realmnt->mnt_mountpoint->d_name.name);
+ }
+ }
}
}
putname(tmp);
[ 34.621278] do_sys_open line 1131, fstype = devtmpfs,mount-root=/, mountpoint=/dev, filepath=/dev/zilong.txt, vfsmount=0x00000000e67b240c, parent=/.parent=0x000000008c557a3d, mnt->mnt_root=0x000000008c557a3d, realmnt->mnt_mountpoint=0x0000000092f6d290,mnt_parent->d_name.name=/, mnt_parent->mnt_mountpoint->d_name.name=/.mnt_parent->mnt_mountpoint=0x0000000009bdf69c,mnt_parent->mnt.mnt_sb->s_type->name=ext4 realmnt->mnt_mountpoint->d_name.name=dev.
可以看到挂载点的dev目录的dentry.
devtmpfs文件系统:
前面分析我们知道了,/dev目录下挂载的是设备文件系统,它的类型就是devtmpfs.它本质上是一个内存文件系统.
"/"是临时挂载点,最后的挂载点在/dev目录,由下面的流程完成:
devtmpfs的挂载发起是在用户态的启动过程中,以busybox启动为例,在rootfs阶段,将会执行/etc/init.d/rcS脚本,内容中即包括挂在devtmpfs.
mount -t devpts devpts /dev/pts