"内核OVERLAY--创建文件"

  "创建文件"

Posted by Xu on January 10, 2018

内核OVERLAY-创建文件

在overlay创建和删除一个文件,并不是在overlay文件系统的根目录下进行,而是在upper层进行的,但upper层是属于ext4文件系统的,所以overlay文件系统的创建和删除操作,同前两章描述的内容一样需要经过vfs层的操作转换调用ext4文件系统的具体创建和删除操作。

创建文件

CreateFile

在VFS中我们知道,文件的创建是通过调用目录的索引节点的操作函数来进行创建的,索引节点的操作定义在结构体ovl_dir_inode_operations中:

const struct inode_operations ovl_dir_inode_operations = {
	.lookup		= ovl_lookup,
	.mkdir		= ovl_mkdir,
	.symlink	= ovl_symlink,
	.unlink		= ovl_unlink,
	.rmdir		= ovl_rmdir,
	.rename2	= ovl_rename2,
	.link		= ovl_link,
	.setattr	= ovl_setattr,
	.create		= ovl_create,
	.mknod		= ovl_mknod,
	.permission	= ovl_permission,
	.getattr	= ovl_dir_getattr,
	.setxattr	= ovl_setxattr,
	.getxattr	= ovl_getxattr,
	.listxattr	= ovl_listxattr,
	.removexattr	= ovl_removexattr,
};

从中我们可以看到,文件的创建函数为ovl_create:假设我们在overlay文件系统根目录下创建一个文件file,则我们需要获取该根目录的索引节点,进一步调用其索引节点操作,同时创建文件的目录项结构已经通过查询操作构建好作为参数传入ovl_create中。

static int ovl_create(struct inode *dir, struct dentry *dentry, umode_t mode,
		      bool excl)
{
	return ovl_create_object(dentry, (mode & 07777) | S_IFREG, 0, NULL);
}

static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev,
			     const char *link)
{
	int err;

	err = ovl_want_write(dentry);//获取写权限
	if (!err) {
		err = ovl_create_or_link(dentry, mode, rdev, link, NULL);
		ovl_drop_write(dentry);
	}

	return err;
}

文件创建前的准备工作:

  • 创建该文件在overlay文件系统中的索引节点
  • 通过COW确保该文件的父目录位于upper层
static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev,
			      const char *link, struct dentry *hardlink)
{
	int err;
	struct inode *inode;
	struct kstat stat = {
		.mode = mode,
		.rdev = rdev,
	};

	err = -ENOMEM;
	inode = ovl_new_inode(dentry->d_sb, mode, dentry->d_fsdata);//在超级块链表中新建一个索引节点,添加到链表
	if (!inode)
		goto out;

	err = ovl_copy_up(dentry->d_parent);//父目录可能是位于lower层所以进行copy_on_write操作,cow的原理下一片blog将有具体介绍
	if (err)
		goto out_iput;

	if (!ovl_dentry_is_opaque(dentry)) {//通过opaque(下层文件是否被隐藏)字段来判断调用哪个函数
		err = ovl_create_upper(dentry, inode, &stat, link, hardlink);//没有隐藏下层目录,调用该函数
	} else {
		const struct cred *old_cred;
		struct cred *override_cred;

		err = -ENOMEM;
		override_cred = prepare_creds();
		if (!override_cred)
			goto out_iput;

		/*
		 * CAP_SYS_ADMIN for setting opaque xattr
		 * CAP_DAC_OVERRIDE for create in workdir, rename
		 * CAP_FOWNER for removing whiteout from sticky dir
		 */
		cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN);//设置权限
		cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
		cap_raise(override_cred->cap_effective, CAP_FOWNER);
		old_cred = override_creds(override_cred);

		err = ovl_create_over_whiteout(dentry, inode, &stat, link,
					       hardlink);//下层目录被隐藏,调用该函数

		revert_creds(old_cred);
		put_cred(override_cred);
	}

	if (!err)
		inode = NULL;
out_iput:
	iput(inode);
out:
	return err;
}

ovl_create_upper():

当新创建的文件目录项中opaque没有被设置时,调用该函数:

  • 由upper层父目录的目录项根据新文件名称检索得到新文件的目录项
  • 调用ovl_create_real,根据upper层父目录的索引节点转交给VFS转换来执行具体的文件创建过程
  • 最后进行文件创建后的目录项更新等
static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
			    struct kstat *stat, const char *link,
			    struct dentry *hardlink)
{
	struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);//获取新文件的父目录在上层upper目录下的目录项,此时父目录一定位于upper层,因为经过COW操作检测
	struct inode *udir = upperdir->d_inode;//获取新文件的父目录在上层upper目录下的索引节点
	struct dentry *newdentry;
	int err;

	mutex_lock_nested(&udir->i_mutex, I_MUTEX_PARENT);
	newdentry = lookup_one_len(dentry->d_name.name, upperdir,
				   dentry->d_name.len);//根据文件名检索upper层在父目录下该新文件的目录项
	err = PTR_ERR(newdentry);
	if (IS_ERR(newdentry))
		goto out_unlock;
	err = ovl_create_real(udir, newdentry, stat, link, hardlink, false);//在父目录下创建该新文件
	if (err)
		goto out_dput;

	ovl_dentry_version_inc(dentry->d_parent);//增加overlay文件系统下新文件父目录的目录项的版本
	ovl_dentry_update(dentry, newdentry);//更新overlay文件系统下新文件目录项的层次信息
	ovl_copyattr(newdentry->d_inode, inode);//拷贝索引节点信息
	d_instantiate(dentry, inode);
	newdentry = NULL;
out_dput:
	dput(newdentry);
out_unlock:
	mutex_unlock(&udir->i_mutex);
	return err;
}

当新创建的文件目录项中opaque被设置时,调用该函数:ovl_create_over_whiteout,该函数和ovl_create_upper不同的是,先在工作目录调用具体的文件创建函数(vfs转换),然后通过rename操作移动到upper层目录。

我们脱离代码的思维,从文件系统的角度来看创建的具体流程:

CreateFile_System