Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3d3c6b89 authored by Miklos Szeredi's avatar Miklos Szeredi
Browse files

ovl: multi-layer lookup



Look up dentry in all relevant layers.

Signed-off-by: default avatarMiklos Szeredi <mszeredi@suse.cz>
parent 9d7459d8
Loading
Loading
Loading
Loading
+94 −49
Original line number Diff line number Diff line
@@ -333,82 +333,127 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
			  unsigned int flags)
{
	struct ovl_entry *oe;
	struct dentry *upperdir;
	struct path lowerdir;
	struct dentry *upperdentry = NULL;
	struct dentry *lowerdentry = NULL;
	struct ovl_entry *poe = dentry->d_parent->d_fsdata;
	struct path *stack = NULL;
	struct dentry *upperdir, *upperdentry = NULL;
	unsigned int ctr = 0;
	struct inode *inode = NULL;
	bool upperopaque = false;
	struct dentry *this, *prev = NULL;
	unsigned int i;
	int err;

	err = -ENOMEM;
	oe = ovl_alloc_entry(1);
	if (!oe)
	upperdir = ovl_upperdentry_dereference(poe);
	if (upperdir) {
		this = ovl_lookup_real(upperdir, &dentry->d_name);
		err = PTR_ERR(this);
		if (IS_ERR(this))
			goto out;

	upperdir = ovl_dentry_upper(dentry->d_parent);
	ovl_path_lower(dentry->d_parent, &lowerdir);
		/*
		 * If this is not the lowermost layer, check whiteout and opaque
		 * directory.
		 */
		if (poe->numlower && this) {
			if (ovl_is_whiteout(this)) {
				dput(this);
				this = NULL;
				upperopaque = true;
			} else if (ovl_is_opaquedir(this)) {
				upperopaque = true;
			}
		}
		upperdentry = prev = this;
	}

	if (upperdir) {
		upperdentry = ovl_lookup_real(upperdir, &dentry->d_name);
		err = PTR_ERR(upperdentry);
		if (IS_ERR(upperdentry))
			goto out_put_dir;
	if (!upperopaque && poe->numlower) {
		err = -ENOMEM;
		stack = kcalloc(poe->numlower, sizeof(struct path), GFP_KERNEL);
		if (!stack)
			goto out_put_upper;
	}

		if (lowerdir.dentry && upperdentry) {
			if (ovl_is_whiteout(upperdentry)) {
				dput(upperdentry);
				upperdentry = NULL;
				oe->opaque = true;
			} else if (ovl_is_opaquedir(upperdentry)) {
				oe->opaque = true;
	for (i = 0; !upperopaque && i < poe->numlower; i++) {
		bool opaque = false;
		struct path lowerpath = poe->lowerstack[i];

		opaque = false;
		this = ovl_lookup_real(lowerpath.dentry, &dentry->d_name);
		err = PTR_ERR(this);
		if (IS_ERR(this))
			goto out_put;
		if (!this)
			continue;

		/*
		 * If this is not the lowermost layer, check whiteout and opaque
		 * directory.
		 */
		if (i < poe->numlower - 1) {
			if (ovl_is_whiteout(this)) {
				dput(this);
				break;
			} else if (ovl_is_opaquedir(this)) {
				opaque = true;
			}
		}
		/*
		 * If this is a non-directory then stop here.
		 *
		 * FIXME: check for opaqueness maybe better done in remove code.
		 */
		if (!S_ISDIR(this->d_inode->i_mode)) {
			opaque = true;
		} else if (prev && (!S_ISDIR(prev->d_inode->i_mode) ||
				    !S_ISDIR(this->d_inode->i_mode))) {
			if (prev == upperdentry)
				upperopaque = true;
			dput(this);
			break;
		}
	if (lowerdir.dentry && !oe->opaque) {
		lowerdentry = ovl_lookup_real(lowerdir.dentry, &dentry->d_name);
		err = PTR_ERR(lowerdentry);
		if (IS_ERR(lowerdentry))
			goto out_dput_upper;
		stack[ctr].dentry = this;
		stack[ctr].mnt = lowerpath.mnt;
		ctr++;
		prev = this;
		if (opaque)
			break;
	}

	if (lowerdentry && upperdentry &&
	    (!S_ISDIR(upperdentry->d_inode->i_mode) ||
	     !S_ISDIR(lowerdentry->d_inode->i_mode))) {
		dput(lowerdentry);
		lowerdentry = NULL;
		oe->opaque = true;
	}
	oe = ovl_alloc_entry(ctr);
	err = -ENOMEM;
	if (!oe)
		goto out_put;

	if (lowerdentry || upperdentry) {
	if (upperdentry || ctr) {
		struct dentry *realdentry;

		realdentry = upperdentry ? upperdentry : lowerdentry;
		realdentry = upperdentry ? upperdentry : stack[0].dentry;

		err = -ENOMEM;
		inode = ovl_new_inode(dentry->d_sb, realdentry->d_inode->i_mode,
				      oe);
		if (!inode)
			goto out_dput;
			goto out_free_oe;
		ovl_copyattr(realdentry->d_inode, inode);
	}

	oe->opaque = upperopaque;
	oe->__upperdentry = upperdentry;
	if (lowerdentry) {
		oe->lowerstack[0].dentry = lowerdentry;
		oe->lowerstack[0].mnt = lowerdir.mnt;
	} else {
		oe->numlower = 0;
	}
	memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr);
	kfree(stack);
	dentry->d_fsdata = oe;
	d_add(dentry, inode);

	return NULL;

out_dput:
	dput(lowerdentry);
out_dput_upper:
	dput(upperdentry);
out_put_dir:
out_free_oe:
	kfree(oe);
out_put:
	for (i = 0; i < ctr; i++)
		dput(stack[i].dentry);
	kfree(stack);
out_put_upper:
	dput(upperdentry);
out:
	return ERR_PTR(err);
}