Overlayfs 注册与挂载
OverlayFs 注册
OverlayFs在内核中以模块的形式存在,在加载模块时,会涉及到两个关键函数:入口函数和出口函数
module_init(ovl_init);
module_exit(ovl_exit);
当使用命令 overlay 加载 overlayfs 模块时,会调用入口函数 ovl_init()
static int __init ovl_init(void) {
return register_filesystem(&ovl_fs_type);
}
当加载 overlay 模块时,模块入口函数 register_fileststem 函数注册 overlayfs:
#include <linux/fs.h>
extern int register_filesystem(struct file_system_type *);
注册一个文件系统需要用到 struct file_ststem_type,这个结构定义了文件系统的名称,挂载函数等信息:
struct file_system_type {
const char *name;
int fs_flags;
struct dentry *(*mount) (struct file_system_type *, int,
const char *, void *);
void (*kill_sb) (struct super_block *);
struct module *owner;
struct file_system_type * next;
struct list_head fs_supers;
struct lock_class_key s_lock_key;
struct lock_class_key s_umount_key;
};
OverlayFs 挂载
在 overlayfs/super.c 中指明了挂载函数:.mount = ovl_mount
static struct file_system_type ovl_fs_type = {
.owner = THIS_MODULE,
.name = "overlay",
.mount = ovl_mount,
.kill_sb = kill_anon_super,
};
MODULE_ALIAS_FS("overlay");
ovl_mount 是函数 mount_nodev 的封装:
static struct dentry *ovl_mount(struct file_system_type *fs_type, int flags,
const char *dev_name, void *raw_data)
{
return mount_nodev(fs_type, flags, raw_data, ovl_fill_super);
}
struct dentry *mount_nodev(struct file_system_type *fs_type,
int flags, void *data,
int (*fill_super)(struct super_block *, void *, int))
{
...
// 1.创建一个 superblock ,它已经链接到全局 superblock 链表中
struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL);
...
// 2. 填充 superblock
error = fill_super(s, data, flags & SB_SILENT ? 1 : 0);
...
}
EXPORT_SYMBOL(mount_nodev);
OverlayFs挂载信息
结构体 ovl_fs 以及 ovl_config 是 Overlayfs 的特有信息,它记录了 OverlayFs upper 层和 lower 层的文件系统、文件名以及 work 目录的 dentry 这些挂载信息:
/* private information held for overlayfs's superblock */
struct ovl_fs {
struct vfsmount *upper_mnt;
unsigned int numlayer;
/* Number of unique fs among layers including upper fs */
unsigned int numfs;
struct ovl_layer *layers;
struct ovl_sb *fs;
/* workbasedir is the path at workdir= mount option */
struct dentry *workbasedir;
/* workdir is the 'work' directory under workbasedir */
struct dentry *workdir;
/* index directory listing overlay inodes by origin file handle */
struct dentry *indexdir;
long namelen;
/* pathnames of lower and upper dirs, for show_options */
struct ovl_config config;
/* creds of process who forced instantiation of super block */
const struct cred *creator_cred;
bool tmpfile;
bool noxattr;
/* Did we take the inuse lock? */
bool upperdir_locked;
bool workdir_locked;
/* Traps in ovl inode cache */
struct inode *upperdir_trap;
struct inode *workbasedir_trap;
struct inode *workdir_trap;
struct inode *indexdir_trap;
/* -1: disabled, 0: same fs, 1..32: number of unused ino bits */
int xino_mode;
/* For allocation of non-persistent inode numbers */
atomic_long_t last_ino;
};
struct ovl_config {
char *lowerdir;
char *upperdir;
char *workdir;
bool default_permissions;
bool redirect_dir;
bool redirect_follow;
const char *redirect_mode;
bool index;
bool nfs_export;
int xino;
bool metacopy;
};
填充 super_block
填充 super_block 由函数 ovl_file_super 完成,主要包括下列工作:
- 分配一个 ovl_fs 结构的 ofs,调用函数 ovl_parse_opt() 对挂载命令进行解析,得到 upper、lower、work 目录的字符串,存入 ofs 的 config 字段:
struct ovl_fs *ofs; //存储 overlayfs 挂载信息
...
ofs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL);
...
err = ovl_parse_opt((char *)data, &ofs->config);
...
- 对 upper 目录和 worker 目录的字符串进行解析。
if (ofs->config.upperdir) {
...
err = ovl_get_upper(sb, ofs, &upperpath);
...
err = ovl_get_workdir(sb, ofs, &upperpath);
...
}
内核通过 ovl_get_upper() 以及 ovl_get_workdir() 来获得 upper 层和 work 目录的路径,最终都会调用 kern_path() 执行路径解析,得到目录,在解析完路径之后,函数内部会对其进行有效性检查,要求 upper 目录与 work 目录必须处在相同文件系统下,同时要求两个目录不能在同一子树中
- 函数 ovl_get_lowerstack() 对记录 lower 层的字符串进行解析,分析出 lower 层数,循环调用函数 ovl_lower_dir() 对 lower 层进行解析,该函数最终也会调用到 kern_path() 执行路径解析,得到每个 lower 目录的路径,保存在路径栈 stack 中:
// func ovl_get_lowerstack()
...
// 复制 lower 层的字符串
lowertmp = kstrdup(ofs->config.lowerdir, GFP_KERNEL);
...
// 得到 lower 层数目
stacklen = ovl_split_lowerdirs(lowertmp);
...
// 为路径栈分配空间
stack = kcalloc(stacklen, sizeof(struct path), GFP_KERNEL);
...
// 循环获取每个 lower 层的路径
for (numlower = 0; numlower < stacklen; numlower++) {
err = ovl_lower_dir(lower, &stack[numlower], ofs,
&sb->s_stack_depth, &remote);
...
}
...
-
填充 struct ovl_entry 类型的变量 oe 分配空间(这个变量记录 OverlayFS 根目录的层次信息,由根目录指向)。为 OverlayFS 根目录分配 inode,创建根目录的 dentry。通过前面得到的 upper 层和 lower 层的路径填充 oe,然后设置根目录 dentry 的 d_fsdata 字段指向 oe。
-
填充 super_block 的 s_d_op 字段,这个字段是一个 struct dentry_operations 类型的指针,它描述了 OverlayFS 的 dentry 操作,每当在 OverlayFS 中分配一个 dentry 时,会使用 s_d_op 字段赋值 dentry 的 d_op 字段
// func ovl_get_lowerstack() ... // 填充 oe for (i = 0; i < numlower; i++) { oe->lowerstack[i].dentry = dget(stack[i].dentry); oe->lowerstack[i].layer = &ofs->layers[i+1]; } // 填充 sb->s_d_op if (remote) sb->s_d_op = &ovl_reval_dentry_operations; else sb->s_d_op = &ovl_dentry_operations; ... // func ovl_get_root() ... // 为根目录分配inode,创建dentry root = d_make_root(ovl_new_inode(sb, S_IFDIR, 0)); ...
-
填充 super_block 的剩余字段