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

Commit 320cd413 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull overlayfs updates from Miklos Szeredi:
 "This relaxes the requirements on the lower layer filesystem: now ones
  that implement .d_revalidate, such as NFS, can be used.

  Upper layer filesystems still has the "no .d_revalidate" requirement.

  Also a bad interaction with jffs2 locking has been fixed"

* 'overlayfs-next' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: lookup whiteouts outside iterate_dir()
  ovl: allow distributed fs as lower layer
  ovl: don't traverse automount points
parents a7ba4bf5 cdb67279
Loading
Loading
Loading
Loading
+49 −28
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ struct ovl_cache_entry {
	u64 ino;
	struct list_head l_node;
	struct rb_node node;
	struct ovl_cache_entry *next_maybe_whiteout;
	bool is_whiteout;
	char name[];
};
@@ -39,7 +40,7 @@ struct ovl_readdir_data {
	struct rb_root root;
	struct list_head *list;
	struct list_head middle;
	struct dentry *dir;
	struct ovl_cache_entry *first_maybe_whiteout;
	int count;
	int err;
};
@@ -79,7 +80,7 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
	return NULL;
}

static struct ovl_cache_entry *ovl_cache_entry_new(struct dentry *dir,
static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
						   const char *name, int len,
						   u64 ino, unsigned int d_type)
{
@@ -98,29 +99,8 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct dentry *dir,
	p->is_whiteout = false;

	if (d_type == DT_CHR) {
		struct dentry *dentry;
		const struct cred *old_cred;
		struct cred *override_cred;

		override_cred = prepare_creds();
		if (!override_cred) {
			kfree(p);
			return NULL;
		}

		/*
		 * CAP_DAC_OVERRIDE for lookup
		 */
		cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
		old_cred = override_creds(override_cred);

		dentry = lookup_one_len(name, dir, len);
		if (!IS_ERR(dentry)) {
			p->is_whiteout = ovl_is_whiteout(dentry);
			dput(dentry);
		}
		revert_creds(old_cred);
		put_cred(override_cred);
		p->next_maybe_whiteout = rdd->first_maybe_whiteout;
		rdd->first_maybe_whiteout = p;
	}
	return p;
}
@@ -148,7 +128,7 @@ static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd,
			return 0;
	}

	p = ovl_cache_entry_new(rdd->dir, name, len, ino, d_type);
	p = ovl_cache_entry_new(rdd, name, len, ino, d_type);
	if (p == NULL)
		return -ENOMEM;

@@ -169,7 +149,7 @@ static int ovl_fill_lower(struct ovl_readdir_data *rdd,
	if (p) {
		list_move_tail(&p->l_node, &rdd->middle);
	} else {
		p = ovl_cache_entry_new(rdd->dir, name, namelen, ino, d_type);
		p = ovl_cache_entry_new(rdd, name, namelen, ino, d_type);
		if (p == NULL)
			rdd->err = -ENOMEM;
		else
@@ -219,6 +199,43 @@ static int ovl_fill_merge(struct dir_context *ctx, const char *name,
		return ovl_fill_lower(rdd, name, namelen, offset, ino, d_type);
}

static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
{
	int err;
	struct ovl_cache_entry *p;
	struct dentry *dentry;
	const struct cred *old_cred;
	struct cred *override_cred;

	override_cred = prepare_creds();
	if (!override_cred)
		return -ENOMEM;

	/*
	 * CAP_DAC_OVERRIDE for lookup
	 */
	cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
	old_cred = override_creds(override_cred);

	err = mutex_lock_killable(&dir->d_inode->i_mutex);
	if (!err) {
		while (rdd->first_maybe_whiteout) {
			p = rdd->first_maybe_whiteout;
			rdd->first_maybe_whiteout = p->next_maybe_whiteout;
			dentry = lookup_one_len(p->name, dir, p->len);
			if (!IS_ERR(dentry)) {
				p->is_whiteout = ovl_is_whiteout(dentry);
				dput(dentry);
			}
		}
		mutex_unlock(&dir->d_inode->i_mutex);
	}
	revert_creds(old_cred);
	put_cred(override_cred);

	return err;
}

static inline int ovl_dir_read(struct path *realpath,
			       struct ovl_readdir_data *rdd)
{
@@ -229,7 +246,7 @@ static inline int ovl_dir_read(struct path *realpath,
	if (IS_ERR(realfile))
		return PTR_ERR(realfile);

	rdd->dir = realpath->dentry;
	rdd->first_maybe_whiteout = NULL;
	rdd->ctx.pos = 0;
	do {
		rdd->count = 0;
@@ -238,6 +255,10 @@ static inline int ovl_dir_read(struct path *realpath,
		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;
+90 −23
Original line number Diff line number Diff line
@@ -273,10 +273,57 @@ static void ovl_dentry_release(struct dentry *dentry)
	}
}

static int ovl_dentry_revalidate(struct dentry *dentry, unsigned int flags)
{
	struct ovl_entry *oe = dentry->d_fsdata;
	unsigned int i;
	int ret = 1;

	for (i = 0; i < oe->numlower; i++) {
		struct dentry *d = oe->lowerstack[i].dentry;

		if (d->d_flags & DCACHE_OP_REVALIDATE) {
			ret = d->d_op->d_revalidate(d, flags);
			if (ret < 0)
				return ret;
			if (!ret) {
				if (!(flags & LOOKUP_RCU))
					d_invalidate(d);
				return -ESTALE;
			}
		}
	}
	return 1;
}

static int ovl_dentry_weak_revalidate(struct dentry *dentry, unsigned int flags)
{
	struct ovl_entry *oe = dentry->d_fsdata;
	unsigned int i;
	int ret = 1;

	for (i = 0; i < oe->numlower; i++) {
		struct dentry *d = oe->lowerstack[i].dentry;

		if (d->d_flags & DCACHE_OP_WEAK_REVALIDATE) {
			ret = d->d_op->d_weak_revalidate(d, flags);
			if (ret <= 0)
				break;
		}
	}
	return ret;
}

static const struct dentry_operations ovl_dentry_operations = {
	.d_release = ovl_dentry_release,
};

static const struct dentry_operations ovl_reval_dentry_operations = {
	.d_release = ovl_dentry_release,
	.d_revalidate = ovl_dentry_revalidate,
	.d_weak_revalidate = ovl_dentry_weak_revalidate,
};

static struct ovl_entry *ovl_alloc_entry(unsigned int numlower)
{
	size_t size = offsetof(struct ovl_entry, lowerstack[numlower]);
@@ -288,6 +335,20 @@ static struct ovl_entry *ovl_alloc_entry(unsigned int numlower)
	return oe;
}

static bool ovl_dentry_remote(struct dentry *dentry)
{
	return dentry->d_flags &
		(DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE);
}

static bool ovl_dentry_weird(struct dentry *dentry)
{
	return dentry->d_flags & (DCACHE_NEED_AUTOMOUNT |
				  DCACHE_MANAGE_TRANSIT |
				  DCACHE_OP_HASH |
				  DCACHE_OP_COMPARE);
}

static inline struct dentry *ovl_lookup_real(struct dentry *dir,
					     struct qstr *name)
{
@@ -303,6 +364,10 @@ static inline struct dentry *ovl_lookup_real(struct dentry *dir,
	} else if (!dentry->d_inode) {
		dput(dentry);
		dentry = NULL;
	} else if (ovl_dentry_weird(dentry)) {
		dput(dentry);
		/* Don't support traversing automounts and other weirdness */
		dentry = ERR_PTR(-EREMOTE);
	}
	return dentry;
}
@@ -350,6 +415,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
			goto out;

		if (this) {
			if (unlikely(ovl_dentry_remote(this))) {
				dput(this);
				err = -EREMOTE;
				goto out;
			}
			if (ovl_is_whiteout(this)) {
				dput(this);
				this = NULL;
@@ -694,25 +764,6 @@ static void ovl_unescape(char *s)
	}
}

static bool ovl_is_allowed_fs_type(struct dentry *root)
{
	const struct dentry_operations *dop = root->d_op;

	/*
	 * We don't support:
	 *  - automount filesystems
	 *  - filesystems with revalidate (FIXME for lower layer)
	 *  - filesystems with case insensitive names
	 */
	if (dop &&
	    (dop->d_manage || dop->d_automount ||
	     dop->d_revalidate || dop->d_weak_revalidate ||
	     dop->d_compare || dop->d_hash)) {
		return false;
	}
	return true;
}

static int ovl_mount_dir_noesc(const char *name, struct path *path)
{
	int err = -EINVAL;
@@ -727,7 +778,7 @@ static int ovl_mount_dir_noesc(const char *name, struct path *path)
		goto out;
	}
	err = -EINVAL;
	if (!ovl_is_allowed_fs_type(path->dentry)) {
	if (ovl_dentry_weird(path->dentry)) {
		pr_err("overlayfs: filesystem on '%s' not supported\n", name);
		goto out_put;
	}
@@ -751,13 +802,21 @@ static int ovl_mount_dir(const char *name, struct path *path)
	if (tmp) {
		ovl_unescape(tmp);
		err = ovl_mount_dir_noesc(tmp, path);

		if (!err)
			if (ovl_dentry_remote(path->dentry)) {
				pr_err("overlayfs: filesystem on '%s' not supported as upperdir\n",
				       tmp);
				path_put(path);
				err = -EINVAL;
			}
		kfree(tmp);
	}
	return err;
}

static int ovl_lower_dir(const char *name, struct path *path, long *namelen,
			 int *stack_depth)
			 int *stack_depth, bool *remote)
{
	int err;
	struct kstatfs statfs;
@@ -774,6 +833,9 @@ static int ovl_lower_dir(const char *name, struct path *path, long *namelen,
	*namelen = max(*namelen, statfs.f_namelen);
	*stack_depth = max(*stack_depth, path->mnt->mnt_sb->s_stack_depth);

	if (ovl_dentry_remote(path->dentry))
		*remote = true;

	return 0;

out_put:
@@ -827,6 +889,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
	unsigned int numlower;
	unsigned int stacklen = 0;
	unsigned int i;
	bool remote = false;
	int err;

	err = -ENOMEM;
@@ -900,7 +963,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
	lower = lowertmp;
	for (numlower = 0; numlower < stacklen; numlower++) {
		err = ovl_lower_dir(lower, &stack[numlower],
				    &ufs->lower_namelen, &sb->s_stack_depth);
				    &ufs->lower_namelen, &sb->s_stack_depth,
				    &remote);
		if (err)
			goto out_put_lowerpath;

@@ -958,6 +1022,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
	if (!ufs->upper_mnt)
		sb->s_flags |= MS_RDONLY;

	if (remote)
		sb->s_d_op = &ovl_reval_dentry_operations;
	else
		sb->s_d_op = &ovl_dentry_operations;

	err = -ENOMEM;