linux/fs/overlayfs/xattrs.c

197 lines
4.9 KiB
C
Raw Normal View History

// 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,
OVL_XATTR_USER_PREFIX_LEN) == 0;
else
return strncmp(name, OVL_XATTR_TRUSTED_PREFIX,
OVL_XATTR_TRUSTED_PREFIX_LEN) == 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;
}