背景

    Linux-4.1内核使用overlay挂载文件系统后,在写文件遇突然掉电后重启出现修改文件报错问题;问题原因是写文件过程种会在wrok目录下创建Whiteout文件,文件写成功后会删除Whiteout文件,但在异常掉电时Whiteout文件没有被删除,导致系统重启overlay挂载检查work不为空出现异常;下面来分析overlay启动挂载代码流程。

代码位置

fs/overlayfs/
├── copy_up.c
├── dir.c
├── inode.c
├── Kconfig
├── Makefile
├── overlayfs.h
├── readdir.c
└── super.c

super.c

    ovl_init 初始化函数

register_filesystem(&ovl_fs_type); 注册文件系统到系统

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(fs_type, flags, raw_data, ovl_fill_super);	文件系统挂载类型回调函数ovl_fill_super

    ovl_fill_super 回调函数

解析挂载参数
ovl_parse_opt((char *) data, &ufs->config);
	static const match_table_t ovl_tokens = {
		{OPT_LOWERDIR,			"lowerdir=%s"},
		{OPT_UPPERDIR,			"upperdir=%s"},
		{OPT_WORKDIR,			"workdir=%s"},
		{OPT_ERR,			NULL}
	};

ovl_mount_dir(ufs->config.upperdir, &upperpath);	挂载upper
ovl_mount_dir(ufs->config.workdir, &workpath);		挂载worker

kstrdup(ufs->config.lowerdir, GFP_KERNEL);	分配内存
ovl_split_lowerdirs(lowertmp);	判断lower层级不大于500
ovl_lower_dir(lower, &stack[numlower], &ufs->lower_namelen, &sb->s_stack_depth); 挂载各lower层级

clone_private_mount(&upperpath);
ovl_workdir_create(ufs->upper_mnt, workpath.dentry);	创建work目录
if (IS_ERR(ufs->workdir)) {
    创建失败本该挂载为只读文件系统并打印提示,这里被注释了
	//pr_warn("overlayfs: failed to create directory %s/%s (errno: %i); mounting read-only\n",	
	//	ufs->config.workdir, OVL_WORKDIR_NAME, -err);
	//sb->s_flags |= MS_RDONLY;
	ufs->workdir = NULL;	这里为NULL会导致后面copy-up报空指针错误

clone_private_mount(&stack[i]);	克隆lower私有挂载路径,并设置挂载属性为只读
mnt->mnt_flags |= MNT_READONLY;	
	
d_make_root(ovl_new_inode(sb, S_IFDIR, oe));	判断是否是根目录挂载	

ovl_workdir_create  创建work目录函数

检查work目录是否存在
#define OVL_WORKDIR_NAME "work"
lookup_one_len(OVL_WORKDIR_NAME, dentry, strlen(OVL_WORKDIR_NAME));	
	
目录存在就去清除
ovl_cleanup(dir, work);	
	ovl_do_rmdir	
		vfs_rmdir
			error = dir->i_op->rmdir(dir, dentry);
				fs/ubifs/dir.c +1174
				const struct inode_operations ubifs_dir_inode_operations = {
					.rmdir       = ubifs_rmdir,		
						err = check_dir_empty(c, d_inode(dentry));	目录不为空会返回-39,删除失败
							walk directory or extended attribute entries. 	遍历目录或扩展属性条目。
							ubifs_tnc_next_ent(c, &key, &nm); 	
							err = -ENOTEMPTY;
							#define	ENOTEMPTY	39	/* Directory not empty */		

readdir.c

    ovl_workdir_cleanup 补丁文件函数:递归清除文件目录

if (!d_is_dir(dentry) || level > 1) {
	ovl_cleanup(dir, dentry);	删除文件
	return;
}

err = ovl_do_rmdir(dir, dentry);	删除不为空目录失败就删除目录里面的文件
if (err) {
	ovl_workdir_cleanup_recurse(&path, level + 1);
		ovl_dir_read(path, &rdd);	读出目录信息
		list_for_each_entry(p, &list, l_node) {
			lookup_one_len(p->name, path->dentry, p->len); 
			ovl_workdir_cleanup(dir, path->mnt, dentry, level); 目录存在就递归删除
		}
}

    递归删除work目录下的文件,目录删空后就能删除目录了

扩展

    workdir目录下work下创建文件代码:

snprintf(name, sizeof(name), "#%lx", (unsigned long) dentry);
~ # ls /ubifs/work/work/ -l
c---------    1 root     root        0,   0 Mar  7 15:31 #ffffffc03e44b9d8

 

Logo

更多推荐