"内核OVERLAY--目录搜索之搜索和显示"

  "第二章"

Posted by Xu on January 8, 2018

内核overlay--目录搜索

在上一章的研究中我们了解了目录搜索的第一个步骤openat(),知道在overlay文件系统下指定路径的文件是如何打开的,并得到了对应的文件对象结构,及该文件在overlay文件系统中所特有的信息的结构体ovl_dir_file,并保存在文件对象的private_data字段。

今天在这一章的学习中我们学习目录搜索的第二个步骤:搜索及显示

overlay_search

getdents()

这部分主要实现的是在知道文件对象及在overlay中所特有的文件信息的基础上如何整合upper和lower层之间的关系并将文件正确显示在merged目录中。

经过getdents系统调用,会进入如下函数:

SYSCALL_DEFINE3(getdents, unsigned int, fd,
		struct linux_dirent __user *, dirent, unsigned int, count)
{
	struct fd f;
	struct linux_dirent __user * lastdirent;//__user指lastdirent为用户空间的地址
	struct getdents_callback buf = {
		.ctx.actor = filldir,
		.count = count,
		.current_dir = dirent
	};//这是乱序初始化的一种方法,还有一种乱序初始化是用:实现的
	int error;

	if (!access_ok(VERIFY_WRITE, dirent, count))//功能是检查dirent分配在用户空间内存是否合法,它的第一个参数:type,有两种类型:VERIFY_READ和VERIFY_WRITE,前者为可读,后者可写,注意:如果标志为可写(VERIFY_WRITE)时,必然可读!因为可写是可读的超集(%VERIFY_WRITE is a superset of %VERIFY_READ
		return -EFAULT;

	f = fdget(fd);//根据文件描述符获取当前进程的文件对象
	if (!f.file)
		return -EBADF;

	error = iterate_dir(f.file, &buf.ctx);//调用文件对象中操作函数的迭代函数iterate
	if (error >= 0)
		error = buf.error;
	lastdirent = buf.previous;
	if (lastdirent) {
		if (put_user(buf.ctx.pos, &lastdirent->d_off))
			error = -EFAULT;
		else
			error = count - buf.count;
	}
	fdput(f);
	return error;
}

iterate_dir调用文件对象中的迭代操作函数file->f_op->iterate(file, ctx);

ovl_iterate():Overlay文件系统的目录检索函数

目录检索的工作主要就是构造目录项dentry,及目录项在指定文件系统中所特有的文件信息和子目录缓存信息ovl_cache_entry

根据我们上一章对vfs_open的分析过程可以看出,ovl文件系统的文件对象的操作函数都定义在&ovl_dir_operations,而其中有.iterate = ovl_iterate,所以我们直接分析ovl_iterate:

static int ovl_iterate(struct file *file, struct dir_context *ctx)
{
	struct ovl_dir_file *od = file->private_data;//获取文件对象在overlay文件系统中的特有信息od
	struct dentry *dentry = file->f_path.dentry;//获取目录项对象
	struct ovl_cache_entry *p;

	if (!ctx->pos)
		ovl_dir_reset(file);

	if (od->is_real)//当目录不是合并目录,目录只存在upper层目录或lower层目录时条件成立,具体见上一章对od的赋值解释
		return iterate_dir(od->realfile, ctx);

	if (!od->cache) {//根据上一章对od的赋值,条件成立
		struct ovl_dir_cache *cache;

		cache = ovl_cache_get(dentry);//根据目录项的dentry->d_fsdata获取ovl_dir_cache
		if (IS_ERR(cache))
			return PTR_ERR(cache);

		od->cache = cache;//设置特有信息od 的缓存
		ovl_seek_cursor(od, ctx->pos);//设置特有信息od 的文件位置指针
	}
    //遍历缓存链表ovl_cache_entry,显示到merged目录中去
	while (od->cursor != &od->cache->entries) {
		p = list_entry(od->cursor, struct ovl_cache_entry, l_node);
		if (!p->is_whiteout)
			if (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
				break;
		od->cursor = p->l_node.next;
		ctx->pos++;
	}
	return 0;
}

ovl_cache_get()

ovl_dir_cache的获取:检测是否存在合法(匹配版本)的缓存,不存在则重新构造获取

static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
{
	int res;
	struct ovl_dir_cache *cache;

	cache = ovl_dir_cache(dentry);//获取dentry->d_fsdata(这里就是ovl_entry层次信息)中的cache对象
	if (cache && ovl_dentry_version_get(dentry) == cache->version) {
	//如果dentry->d_fsdata->version和cache中的version匹配则直接使用该缓存
		cache->refcount++;
		return cache;
	}
	ovl_set_dir_cache(dentry, NULL);//不匹配则设置dentry->d_fsdata中的cahce为空

	cache = kzalloc(sizeof(struct ovl_dir_cache), GFP_KERNEL);//重新分配一个ovl_dir_cache缓存
	if (!cache)
		return ERR_PTR(-ENOMEM);

	cache->refcount = 1;
	INIT_LIST_HEAD(&cache->entries);//初始化该缓存链表头

	res = ovl_dir_read_merged(dentry, &cache->entries);
	if (res) {
		ovl_cache_free(&cache->entries);
		kfree(cache);
		return ERR_PTR(res);
	}

	cache->version = ovl_dentry_version_get(dentry);
	ovl_set_dir_cache(dentry, cache);

	return cache;
}

ovl_dir_read_merged()

循环读取每一层的子目录缓存,并添加到缓存链表list中去

static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
{
	int err;
	struct path realpath;//真实路径
	struct ovl_readdir_data rdd = {
		.ctx.actor = ovl_fill_merge,
		.list = list,
		.root = RB_ROOT,
		.is_merge = false,
	};//目录读取数据
	int idx, next;
     //idx=0表示从upper层开始检索遍历
	for (idx = 0; idx != -1; idx = next) {
		next = ovl_path_next(idx, dentry, &realpath);//遍历层次信息,返回下一镜像层,并把下一层的路径信息存放到realpath变量中,当遍历到该文件对象oe->numlower最底层时返回-1
		if (next != -1) {
			err = ovl_dir_read(&realpath, &rdd);//根据路径读取这一层的子目录信息
			if (err)
				break;
		} else {
			/*
			 * Insert lowest layer entries before upper ones, this
			 * allows offsets to be reasonably constant
			 */
			list_add(&rdd.middle, rdd.list);
			rdd.is_merge = true;
			err = ovl_dir_read(&realpath, &rdd);
			list_del(&rdd.middle);
		}
	}
	return err;
}

//读取镜像层目录数据
static inline int ovl_dir_read(struct path *realpath,
			       struct ovl_readdir_data *rdd)
{
	struct file *realfile;
	int err;

	realfile = ovl_path_open(realpath, O_RDONLY | O_DIRECTORY);//打开这层镜像层的目录,并获得对应文件对象,真实的文件对象
	if (IS_ERR(realfile))
		return PTR_ERR(realfile);

	rdd->first_maybe_whiteout = NULL;
	rdd->ctx.pos = 0;
	do {
		rdd->count = 0;
		rdd->err = 0;
		err = iterate_dir(realfile, &rdd->ctx);//根据真实的文件对象,迭代读取该目录下的子目录信息,通过rdd.ctx.actor来填充到缓存链表,并进行冲突检测
		if (err >= 0)
			err = rdd->err;
	} while (!err && rdd->count);

	if (!err && rdd->first_maybe_whiteout)
		err = ovl_check_whiteouts(realpath->dentry, rdd);

	fput(realfile);

	return err;
}

ovl_fill_merge()

iterate_dir(realfile, &rdd->ctx);中传入&rdd->ctx,其中ctx.actor为ovl_fill_merge。当调用具体文件系统的iterate_dir时,会通过回调ovl_fill_merge来填充到overlay文件系统中dentry的缓存dentry->cache中

static int ovl_fill_merge(struct dir_context *ctx, const char *name,
			  int namelen, loff_t offset, u64 ino,
			  unsigned int d_type)
{
	struct ovl_readdir_data *rdd =
		container_of(ctx, struct ovl_readdir_data, ctx);

	rdd->count++;
	if (!rdd->is_merge)
		return ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type);//判断是最底层的目录,添加到红黑树结构
	else
		return ovl_fill_lower(rdd, name, namelen, offset, ino, d_type);//判断不是最底层的目录,不添加到红黑树结构
}