ovl: fix permission checking for setattr

[Al Viro] The bug is in being too enthusiastic about optimizing ->setattr()
away - instead of "copy verbatim with metadata" + "chmod/chown/utimes"
(with the former being always safe and the latter failing in case of
insufficient permissions) it tries to combine these two.  Note that copyup
itself will have to do ->setattr() anyway; _that_ is where the elevated
capabilities are right.  Having these two ->setattr() (one to set verbatim
copy of metadata, another to do what overlayfs ->setattr() had been asked
to do in the first place) combined is where it breaks.

Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
Cc: <stable@vger.kernel.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
static const char *ovl_follow_link(struct dentry *dentry, void **cookie)
{
	struct dentry *realdentry;
	struct inode *realinode;
	struct ovl_link_data *data = NULL;
	const char *ret;

	realdentry = ovl_dentry_real(dentry);
	realinode = realdentry->d_inode;

	if (WARN_ON(!realinode->i_op->follow_link))
		return ERR_PTR(-EPERM);

	if (realinode->i_op->put_link) {
		data = kmalloc(sizeof(struct ovl_link_data), GFP_KERNEL);
		if (!data)
			return ERR_PTR(-ENOMEM);
		data->realdentry = realdentry;
	}

	ret = realinode->i_op->follow_link(realdentry, cookie);
	if (IS_ERR_OR_NULL(ret)) {
		kfree(data);
		return ret;
	}

	if (data)
		data->cookie = *cookie;

	*cookie = data;

	return ret;
}

static const char *ovl_follow_link(struct dentry *dentry, void **cookie)
{
	struct dentry *realdentry;
	struct inode *realinode;
	struct ovl_link_data *data = NULL;
	const char *ret;

	realdentry = ovl_dentry_real(dentry);
	realinode = realdentry->d_inode;

	if (WARN_ON(!realinode->i_op->follow_link))
		return ERR_PTR(-EPERM);

	if (realinode->i_op->put_link) {
		data = kmalloc(sizeof(struct ovl_link_data), GFP_KERNEL);
		if (!data)
			return ERR_PTR(-ENOMEM);
		data->realdentry = realdentry;
	}

	ret = realinode->i_op->follow_link(realdentry, cookie);
	if (IS_ERR_OR_NULL(ret)) {
		kfree(data);
		return ret;
	}

	if (data)
		data->cookie = *cookie;

	*cookie = data;

	return ret;
}
