summaryrefslogtreecommitdiff
path: root/fs/overlayfs/xattrs.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/overlayfs/xattrs.c')
-rw-r--r--fs/overlayfs/xattrs.c81
1 files changed, 78 insertions, 3 deletions
diff --git a/fs/overlayfs/xattrs.c b/fs/overlayfs/xattrs.c
index d20032f8a537..383978e4663c 100644
--- a/fs/overlayfs/xattrs.c
+++ b/fs/overlayfs/xattrs.c
@@ -4,7 +4,19 @@
#include <linux/xattr.h>
#include "overlayfs.h"
-bool ovl_is_private_xattr(struct super_block *sb, const char *name)
+static bool ovl_is_escaped_xattr(struct super_block *sb, const char *name)
+{
+ struct ovl_fs *ofs = sb->s_fs_info;
+
+ if (ofs->config.userxattr)
+ return strncmp(name, OVL_XATTR_ESCAPE_USER_PREFIX,
+ OVL_XATTR_ESCAPE_USER_PREFIX_LEN) == 0;
+ else
+ return strncmp(name, OVL_XATTR_ESCAPE_TRUSTED_PREFIX,
+ OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN - 1) == 0;
+}
+
+static bool ovl_is_own_xattr(struct super_block *sb, const char *name)
{
struct ovl_fs *ofs = OVL_FS(sb);
@@ -16,6 +28,11 @@ bool ovl_is_private_xattr(struct super_block *sb, const char *name)
OVL_XATTR_TRUSTED_PREFIX_LEN) == 0;
}
+bool ovl_is_private_xattr(struct super_block *sb, const char *name)
+{
+ return ovl_is_own_xattr(sb, name) && !ovl_is_escaped_xattr(sb, name);
+}
+
static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
@@ -95,10 +112,12 @@ static bool ovl_can_list(struct super_block *sb, const char *s)
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
{
struct dentry *realdentry = ovl_dentry_real(dentry);
+ struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
ssize_t res;
size_t len;
char *s;
const struct cred *old_cred;
+ size_t prefix_len, name_len;
old_cred = ovl_override_creds(dentry->d_sb);
res = vfs_listxattr(realdentry, list, size);
@@ -106,6 +125,9 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
if (res <= 0 || size == 0)
return res;
+ prefix_len = ofs->config.userxattr ?
+ OVL_XATTR_USER_PREFIX_LEN : OVL_XATTR_TRUSTED_PREFIX_LEN;
+
/* filter out private xattrs */
for (s = list, len = res; len;) {
size_t slen = strnlen(s, len) + 1;
@@ -118,6 +140,12 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
if (!ovl_can_list(dentry->d_sb, s)) {
res -= slen;
memmove(s, s + slen, len);
+ } else if (ovl_is_escaped_xattr(dentry->d_sb, s)) {
+ res -= OVL_XATTR_ESCAPE_PREFIX_LEN;
+ name_len = slen - prefix_len - OVL_XATTR_ESCAPE_PREFIX_LEN;
+ s += prefix_len;
+ memmove(s, s + OVL_XATTR_ESCAPE_PREFIX_LEN, name_len + len);
+ s += name_len;
} else {
s += slen;
}
@@ -126,11 +154,47 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
return res;
}
+static char *ovl_xattr_escape_name(const char *prefix, const char *name)
+{
+ size_t prefix_len = strlen(prefix);
+ size_t name_len = strlen(name);
+ size_t escaped_len;
+ char *escaped, *s;
+
+ escaped_len = prefix_len + OVL_XATTR_ESCAPE_PREFIX_LEN + name_len;
+ if (escaped_len > XATTR_NAME_MAX)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ escaped = kmalloc(escaped_len + 1, GFP_KERNEL);
+ if (escaped == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ s = escaped;
+ memcpy(s, prefix, prefix_len);
+ s += prefix_len;
+ memcpy(s, OVL_XATTR_ESCAPE_PREFIX, OVL_XATTR_ESCAPE_PREFIX_LEN);
+ s += OVL_XATTR_ESCAPE_PREFIX_LEN;
+ memcpy(s, name, name_len + 1);
+
+ return escaped;
+}
+
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;
+ char *escaped;
+ int r;
+
+ escaped = ovl_xattr_escape_name(handler->prefix, name);
+ if (IS_ERR(escaped))
+ return PTR_ERR(escaped);
+
+ r = ovl_xattr_get(dentry, inode, escaped, buffer, size);
+
+ kfree(escaped);
+
+ return r;
}
static int ovl_own_xattr_set(const struct xattr_handler *handler,
@@ -139,7 +203,18 @@ static int ovl_own_xattr_set(const struct xattr_handler *handler,
const char *name, const void *value,
size_t size, int flags)
{
- return -EOPNOTSUPP;
+ char *escaped;
+ int r;
+
+ escaped = ovl_xattr_escape_name(handler->prefix, name);
+ if (IS_ERR(escaped))
+ return PTR_ERR(escaped);
+
+ r = ovl_xattr_set(dentry, inode, escaped, value, size, flags);
+
+ kfree(escaped);
+
+ return r;
}
static int ovl_other_xattr_get(const struct xattr_handler *handler,