Commit 420a62dd authored by Amir Goldstein's avatar Amir Goldstein
Browse files

ovl: Move xattr support to new xattrs.c file



This moves the code from super.c and inode.c, and makes ovl_xattr_get/set()
static.

This is in preparation for doing more work on xattrs support.

Signed-off-by: default avatarAlexander Larsson <alexl@redhat.com>
Reviewed-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarAmir Goldstein <amir73il@gmail.com>
parent 5b02bfc1
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -6,4 +6,4 @@
obj-$(CONFIG_OVERLAY_FS) += overlay.o

overlay-objs := super.o namei.o util.o inode.o file.o dir.o readdir.o \
		copy_up.o export.o params.o
		copy_up.o export.o params.o xattrs.o
+0 −122
Original line number Diff line number Diff line
@@ -339,128 +339,6 @@ static const char *ovl_get_link(struct dentry *dentry,
	return p;
}

bool ovl_is_private_xattr(struct super_block *sb, const char *name)
{
	struct ovl_fs *ofs = OVL_FS(sb);

	if (ofs->config.userxattr)
		return strncmp(name, OVL_XATTR_USER_PREFIX,
			       sizeof(OVL_XATTR_USER_PREFIX) - 1) == 0;
	else
		return strncmp(name, OVL_XATTR_TRUSTED_PREFIX,
			       sizeof(OVL_XATTR_TRUSTED_PREFIX) - 1) == 0;
}

int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
		  const void *value, size_t size, int flags)
{
	int err;
	struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
	struct dentry *upperdentry = ovl_i_dentry_upper(inode);
	struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
	struct path realpath;
	const struct cred *old_cred;

	if (!value && !upperdentry) {
		ovl_path_lower(dentry, &realpath);
		old_cred = ovl_override_creds(dentry->d_sb);
		err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0);
		revert_creds(old_cred);
		if (err < 0)
			goto out;
	}

	if (!upperdentry) {
		err = ovl_copy_up(dentry);
		if (err)
			goto out;

		realdentry = ovl_dentry_upper(dentry);
	}

	err = ovl_want_write(dentry);
	if (err)
		goto out;

	old_cred = ovl_override_creds(dentry->d_sb);
	if (value) {
		err = ovl_do_setxattr(ofs, realdentry, name, value, size,
				      flags);
	} else {
		WARN_ON(flags != XATTR_REPLACE);
		err = ovl_do_removexattr(ofs, realdentry, name);
	}
	revert_creds(old_cred);
	ovl_drop_write(dentry);

	/* copy c/mtime */
	ovl_copyattr(inode);
out:
	return err;
}

int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
		  void *value, size_t size)
{
	ssize_t res;
	const struct cred *old_cred;
	struct path realpath;

	ovl_i_path_real(inode, &realpath);
	old_cred = ovl_override_creds(dentry->d_sb);
	res = vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size);
	revert_creds(old_cred);
	return res;
}

static bool ovl_can_list(struct super_block *sb, const char *s)
{
	/* Never list private (.overlay) */
	if (ovl_is_private_xattr(sb, s))
		return false;

	/* List all non-trusted xattrs */
	if (strncmp(s, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0)
		return true;

	/* list other trusted for superuser only */
	return ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN);
}

ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
{
	struct dentry *realdentry = ovl_dentry_real(dentry);
	ssize_t res;
	size_t len;
	char *s;
	const struct cred *old_cred;

	old_cred = ovl_override_creds(dentry->d_sb);
	res = vfs_listxattr(realdentry, list, size);
	revert_creds(old_cred);
	if (res <= 0 || size == 0)
		return res;

	/* filter out private xattrs */
	for (s = list, len = res; len;) {
		size_t slen = strnlen(s, len) + 1;

		/* underlying fs providing us with an broken xattr list? */
		if (WARN_ON(slen > len))
			return -EIO;

		len -= slen;
		if (!ovl_can_list(dentry->d_sb, s)) {
			res -= slen;
			memmove(s, s + slen, len);
		} else {
			s += slen;
		}
	}

	return res;
}

#ifdef CONFIG_FS_POSIX_ACL
/*
 * Apply the idmapping of the layer to POSIX ACLs. The caller must pass a clone
+9 −9
Original line number Diff line number Diff line
@@ -699,17 +699,8 @@ int ovl_set_nlink_lower(struct dentry *dentry);
unsigned int ovl_get_nlink(struct ovl_fs *ofs, struct dentry *lowerdentry,
			   struct dentry *upperdentry,
			   unsigned int fallback);
int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
		struct iattr *attr);
int ovl_getattr(struct mnt_idmap *idmap, const struct path *path,
		struct kstat *stat, u32 request_mask, unsigned int flags);
int ovl_permission(struct mnt_idmap *idmap, struct inode *inode,
		   int mask);
int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
		  const void *value, size_t size, int flags);
int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
		  void *value, size_t size);
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);

#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *do_ovl_get_acl(struct mnt_idmap *idmap,
@@ -846,3 +837,12 @@ static inline bool ovl_force_readonly(struct ovl_fs *ofs)
{
	return (!ovl_upper_mnt(ofs) || !ofs->workdir);
}

/* xattr.c */

const struct xattr_handler * const *ovl_xattr_handlers(struct ovl_fs *ofs);
int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
		struct iattr *attr);
int ovl_getattr(struct mnt_idmap *idmap, const struct path *path,
		struct kstat *stat, u32 request_mask, unsigned int flags);
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
+1 −64
Original line number Diff line number Diff line
@@ -445,68 +445,6 @@ static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir)
	return ok;
}

static int ovl_own_xattr_get(const struct xattr_handler *handler,
			     struct dentry *dentry, struct inode *inode,
			     const char *name, void *buffer, size_t size)
{
	return -EOPNOTSUPP;
}

static int ovl_own_xattr_set(const struct xattr_handler *handler,
			     struct mnt_idmap *idmap,
			     struct dentry *dentry, struct inode *inode,
			     const char *name, const void *value,
			     size_t size, int flags)
{
	return -EOPNOTSUPP;
}

static int ovl_other_xattr_get(const struct xattr_handler *handler,
			       struct dentry *dentry, struct inode *inode,
			       const char *name, void *buffer, size_t size)
{
	return ovl_xattr_get(dentry, inode, name, buffer, size);
}

static int ovl_other_xattr_set(const struct xattr_handler *handler,
			       struct mnt_idmap *idmap,
			       struct dentry *dentry, struct inode *inode,
			       const char *name, const void *value,
			       size_t size, int flags)
{
	return ovl_xattr_set(dentry, inode, name, value, size, flags);
}

static const struct xattr_handler ovl_own_trusted_xattr_handler = {
	.prefix	= OVL_XATTR_TRUSTED_PREFIX,
	.get = ovl_own_xattr_get,
	.set = ovl_own_xattr_set,
};

static const struct xattr_handler ovl_own_user_xattr_handler = {
	.prefix	= OVL_XATTR_USER_PREFIX,
	.get = ovl_own_xattr_get,
	.set = ovl_own_xattr_set,
};

static const struct xattr_handler ovl_other_xattr_handler = {
	.prefix	= "", /* catch all */
	.get = ovl_other_xattr_get,
	.set = ovl_other_xattr_set,
};

static const struct xattr_handler * const ovl_trusted_xattr_handlers[] = {
	&ovl_own_trusted_xattr_handler,
	&ovl_other_xattr_handler,
	NULL
};

static const struct xattr_handler * const ovl_user_xattr_handlers[] = {
	&ovl_own_user_xattr_handler,
	&ovl_other_xattr_handler,
	NULL
};

static int ovl_setup_trap(struct super_block *sb, struct dentry *dir,
			  struct inode **ptrap, const char *name)
{
@@ -1501,8 +1439,7 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
	cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);

	sb->s_magic = OVERLAYFS_SUPER_MAGIC;
	sb->s_xattr = ofs->config.userxattr ? ovl_user_xattr_handlers :
		ovl_trusted_xattr_handlers;
	sb->s_xattr = ovl_xattr_handlers(ofs);
	sb->s_fs_info = ofs;
#ifdef CONFIG_FS_POSIX_ACL
	sb->s_flags |= SB_POSIXACL;

fs/overlayfs/xattrs.c

0 → 100644
+196 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only

#include <linux/fs.h>
#include <linux/xattr.h>
#include "overlayfs.h"

bool ovl_is_private_xattr(struct super_block *sb, const char *name)
{
	struct ovl_fs *ofs = OVL_FS(sb);

	if (ofs->config.userxattr)
		return strncmp(name, OVL_XATTR_USER_PREFIX,
			       sizeof(OVL_XATTR_USER_PREFIX) - 1) == 0;
	else
		return strncmp(name, OVL_XATTR_TRUSTED_PREFIX,
			       sizeof(OVL_XATTR_TRUSTED_PREFIX) - 1) == 0;
}

static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
			 const void *value, size_t size, int flags)
{
	int err;
	struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
	struct dentry *upperdentry = ovl_i_dentry_upper(inode);
	struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
	struct path realpath;
	const struct cred *old_cred;

	if (!value && !upperdentry) {
		ovl_path_lower(dentry, &realpath);
		old_cred = ovl_override_creds(dentry->d_sb);
		err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0);
		revert_creds(old_cred);
		if (err < 0)
			goto out;
	}

	if (!upperdentry) {
		err = ovl_copy_up(dentry);
		if (err)
			goto out;

		realdentry = ovl_dentry_upper(dentry);
	}

	err = ovl_want_write(dentry);
	if (err)
		goto out;

	old_cred = ovl_override_creds(dentry->d_sb);
	if (value) {
		err = ovl_do_setxattr(ofs, realdentry, name, value, size,
				      flags);
	} else {
		WARN_ON(flags != XATTR_REPLACE);
		err = ovl_do_removexattr(ofs, realdentry, name);
	}
	revert_creds(old_cred);
	ovl_drop_write(dentry);

	/* copy c/mtime */
	ovl_copyattr(inode);
out:
	return err;
}

static int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
			 void *value, size_t size)
{
	ssize_t res;
	const struct cred *old_cred;
	struct path realpath;

	ovl_i_path_real(inode, &realpath);
	old_cred = ovl_override_creds(dentry->d_sb);
	res = vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size);
	revert_creds(old_cred);
	return res;
}

static bool ovl_can_list(struct super_block *sb, const char *s)
{
	/* Never list private (.overlay) */
	if (ovl_is_private_xattr(sb, s))
		return false;

	/* List all non-trusted xattrs */
	if (strncmp(s, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0)
		return true;

	/* list other trusted for superuser only */
	return ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN);
}

ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
{
	struct dentry *realdentry = ovl_dentry_real(dentry);
	ssize_t res;
	size_t len;
	char *s;
	const struct cred *old_cred;

	old_cred = ovl_override_creds(dentry->d_sb);
	res = vfs_listxattr(realdentry, list, size);
	revert_creds(old_cred);
	if (res <= 0 || size == 0)
		return res;

	/* filter out private xattrs */
	for (s = list, len = res; len;) {
		size_t slen = strnlen(s, len) + 1;

		/* underlying fs providing us with an broken xattr list? */
		if (WARN_ON(slen > len))
			return -EIO;

		len -= slen;
		if (!ovl_can_list(dentry->d_sb, s)) {
			res -= slen;
			memmove(s, s + slen, len);
		} else {
			s += slen;
		}
	}

	return res;
}

static int ovl_own_xattr_get(const struct xattr_handler *handler,
			     struct dentry *dentry, struct inode *inode,
			     const char *name, void *buffer, size_t size)
{
	return -EOPNOTSUPP;
}

static int ovl_own_xattr_set(const struct xattr_handler *handler,
			     struct mnt_idmap *idmap,
			     struct dentry *dentry, struct inode *inode,
			     const char *name, const void *value,
			     size_t size, int flags)
{
	return -EOPNOTSUPP;
}

static int ovl_other_xattr_get(const struct xattr_handler *handler,
			       struct dentry *dentry, struct inode *inode,
			       const char *name, void *buffer, size_t size)
{
	return ovl_xattr_get(dentry, inode, name, buffer, size);
}

static int ovl_other_xattr_set(const struct xattr_handler *handler,
			       struct mnt_idmap *idmap,
			       struct dentry *dentry, struct inode *inode,
			       const char *name, const void *value,
			       size_t size, int flags)
{
	return ovl_xattr_set(dentry, inode, name, value, size, flags);
}

static const struct xattr_handler ovl_own_trusted_xattr_handler = {
	.prefix	= OVL_XATTR_TRUSTED_PREFIX,
	.get = ovl_own_xattr_get,
	.set = ovl_own_xattr_set,
};

static const struct xattr_handler ovl_own_user_xattr_handler = {
	.prefix	= OVL_XATTR_USER_PREFIX,
	.get = ovl_own_xattr_get,
	.set = ovl_own_xattr_set,
};

static const struct xattr_handler ovl_other_xattr_handler = {
	.prefix	= "", /* catch all */
	.get = ovl_other_xattr_get,
	.set = ovl_other_xattr_set,
};

static const struct xattr_handler * const ovl_trusted_xattr_handlers[] = {
	&ovl_own_trusted_xattr_handler,
	&ovl_other_xattr_handler,
	NULL
};

static const struct xattr_handler * const ovl_user_xattr_handlers[] = {
	&ovl_own_user_xattr_handler,
	&ovl_other_xattr_handler,
	NULL
};

const struct xattr_handler * const *ovl_xattr_handlers(struct ovl_fs *ofs)
{
	return ofs->config.userxattr ? ovl_user_xattr_handlers :
		ovl_trusted_xattr_handlers;
}