ovl: fix POSIX ACL setting
authorMiklos Szeredi <mszeredi@redhat.com>
Fri, 29 Jul 2016 10:05:24 +0000 (12:05 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Fri, 29 Jul 2016 10:05:24 +0000 (12:05 +0200)
Setting POSIX ACL needs special handling:

1) Some permission checks are done by ->setxattr() which now uses mounter's
creds ("ovl: do operations on underlying file system in mounter's
context").  These permission checks need to be done with current cred as
well.

2) Setting ACL can fail for various reasons.  We do not need to copy up in
these cases.

In the mean time switch to using generic_setxattr.

[Arnd Bergmann] Fix link error without POSIX ACL. posix_acl_from_xattr()
doesn't have a 'static inline' implementation when CONFIG_FS_POSIX_ACL is
disabled, and I could not come up with an obvious way to do it.

This instead avoids the link error by defining two sets of ACL operations
and letting the compiler drop one of the two at compile time depending
on CONFIG_FS_POSIX_ACL. This avoids all references to the ACL code,
also leading to smaller code.

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/overlayfs/dir.c
fs/overlayfs/inode.c
fs/overlayfs/overlayfs.h
fs/overlayfs/super.c

index d456e9f..8f95c21 100644 (file)
@@ -953,7 +953,7 @@ const struct inode_operations ovl_dir_inode_operations = {
        .mknod          = ovl_mknod,
        .permission     = ovl_permission,
        .getattr        = ovl_dir_getattr,
-       .setxattr       = ovl_setxattr,
+       .setxattr       = generic_setxattr,
        .getxattr       = ovl_getxattr,
        .listxattr      = ovl_listxattr,
        .removexattr    = ovl_removexattr,
index 6be0d27..f7caf16 100644 (file)
@@ -190,7 +190,9 @@ static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
 
 static bool ovl_is_private_xattr(const char *name)
 {
-       return strncmp(name, OVL_XATTR_PRE_NAME, OVL_XATTR_PRE_LEN) == 0;
+#define OVL_XATTR_PRE_NAME OVL_XATTR_PREFIX "."
+       return strncmp(name, OVL_XATTR_PRE_NAME,
+                      sizeof(OVL_XATTR_PRE_NAME) - 1) == 0;
 }
 
 int ovl_setxattr(struct dentry *dentry, struct inode *inode,
@@ -205,10 +207,6 @@ int ovl_setxattr(struct dentry *dentry, struct inode *inode,
        if (err)
                goto out;
 
-       err = -EPERM;
-       if (ovl_is_private_xattr(name))
-               goto out_drop_write;
-
        err = ovl_copy_up(dentry);
        if (err)
                goto out_drop_write;
@@ -389,7 +387,7 @@ static const struct inode_operations ovl_file_inode_operations = {
        .setattr        = ovl_setattr,
        .permission     = ovl_permission,
        .getattr        = ovl_getattr,
-       .setxattr       = ovl_setxattr,
+       .setxattr       = generic_setxattr,
        .getxattr       = ovl_getxattr,
        .listxattr      = ovl_listxattr,
        .removexattr    = ovl_removexattr,
@@ -402,7 +400,7 @@ static const struct inode_operations ovl_symlink_inode_operations = {
        .get_link       = ovl_get_link,
        .readlink       = ovl_readlink,
        .getattr        = ovl_getattr,
-       .setxattr       = ovl_setxattr,
+       .setxattr       = generic_setxattr,
        .getxattr       = ovl_getxattr,
        .listxattr      = ovl_listxattr,
        .removexattr    = ovl_removexattr,
index abeef1e..e4f5c95 100644 (file)
@@ -23,9 +23,9 @@ enum ovl_path_type {
 #define OVL_TYPE_MERGE_OR_LOWER(type) \
        (OVL_TYPE_MERGE(type) || !OVL_TYPE_UPPER(type))
 
-#define OVL_XATTR_PRE_NAME "trusted.overlay."
-#define OVL_XATTR_PRE_LEN  16
-#define OVL_XATTR_OPAQUE   OVL_XATTR_PRE_NAME"opaque"
+
+#define OVL_XATTR_PREFIX XATTR_TRUSTED_PREFIX "overlay"
+#define OVL_XATTR_OPAQUE OVL_XATTR_PREFIX ".opaque"
 
 #define OVL_ISUPPER_MASK 1UL
 
index 44c4510..0d40f9a 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/sched.h>
 #include <linux/statfs.h>
 #include <linux/seq_file.h>
+#include <linux/posix_acl_xattr.h>
 #include "overlayfs.h"
 
 MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
@@ -966,6 +967,96 @@ static unsigned int ovl_split_lowerdirs(char *str)
        return ctr;
 }
 
+static int ovl_posix_acl_xattr_set(const struct xattr_handler *handler,
+                                  struct dentry *dentry, struct inode *inode,
+                                  const char *name, const void *value,
+                                  size_t size, int flags)
+{
+       struct dentry *workdir = ovl_workdir(dentry);
+       struct inode *realinode = ovl_inode_real(inode, NULL);
+       struct posix_acl *acl = NULL;
+       int err;
+
+       /* Check that everything is OK before copy-up */
+       if (value) {
+               acl = posix_acl_from_xattr(&init_user_ns, value, size);
+               if (IS_ERR(acl))
+                       return PTR_ERR(acl);
+       }
+       err = -EOPNOTSUPP;
+       if (!IS_POSIXACL(d_inode(workdir)))
+               goto out_acl_release;
+       if (!realinode->i_op->set_acl)
+               goto out_acl_release;
+       if (handler->flags == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode)) {
+               err = acl ? -EACCES : 0;
+               goto out_acl_release;
+       }
+       err = -EPERM;
+       if (!inode_owner_or_capable(inode))
+               goto out_acl_release;
+
+       posix_acl_release(acl);
+
+       return ovl_setxattr(dentry, inode, handler->name, value, size, flags);
+
+out_acl_release:
+       posix_acl_release(acl);
+       return err;
+}
+
+static int ovl_other_xattr_set(const struct xattr_handler *handler,
+                              struct dentry *dentry, struct inode *inode,
+                              const char *name, const void *value,
+                              size_t size, int flags)
+{
+       return ovl_setxattr(dentry, inode, name, value, size, flags);
+}
+
+static int ovl_own_xattr_set(const struct xattr_handler *handler,
+                            struct dentry *dentry, struct inode *inode,
+                            const char *name, const void *value,
+                            size_t size, int flags)
+{
+       return -EPERM;
+}
+
+static const struct xattr_handler ovl_posix_acl_access_xattr_handler = {
+       .name = XATTR_NAME_POSIX_ACL_ACCESS,
+       .flags = ACL_TYPE_ACCESS,
+       .set = ovl_posix_acl_xattr_set,
+};
+
+static const struct xattr_handler ovl_posix_acl_default_xattr_handler = {
+       .name = XATTR_NAME_POSIX_ACL_DEFAULT,
+       .flags = ACL_TYPE_DEFAULT,
+       .set = ovl_posix_acl_xattr_set,
+};
+
+static const struct xattr_handler ovl_own_xattr_handler = {
+       .prefix = OVL_XATTR_PREFIX,
+       .set = ovl_own_xattr_set,
+};
+
+static const struct xattr_handler ovl_other_xattr_handler = {
+       .prefix = "", /* catch all */
+       .set = ovl_other_xattr_set,
+};
+
+static const struct xattr_handler *ovl_xattr_handlers[] = {
+       &ovl_posix_acl_access_xattr_handler,
+       &ovl_posix_acl_default_xattr_handler,
+       &ovl_own_xattr_handler,
+       &ovl_other_xattr_handler,
+       NULL
+};
+
+static const struct xattr_handler *ovl_xattr_noacl_handlers[] = {
+       &ovl_own_xattr_handler,
+       &ovl_other_xattr_handler,
+       NULL,
+};
+
 static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 {
        struct path upperpath = { NULL, NULL };
@@ -1178,6 +1269,10 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 
        sb->s_magic = OVERLAYFS_SUPER_MAGIC;
        sb->s_op = &ovl_super_operations;
+       if (IS_ENABLED(CONFIG_FS_POSIX_ACL))
+               sb->s_xattr = ovl_xattr_handlers;
+       else
+               sb->s_xattr = ovl_xattr_noacl_handlers;
        sb->s_root = root_dentry;
        sb->s_fs_info = ufs;
        sb->s_flags |= MS_POSIXACL;