Merge tag 'dlm-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/teigland/linux-dlm
[linux-2.6-microblaze.git] / fs / xattr.c
index 61107b6..86668d2 100644 (file)
@@ -80,6 +80,31 @@ xattr_resolve_name(struct inode *inode, const char **name)
        return ERR_PTR(-EOPNOTSUPP);
 }
 
+/**
+ * may_write_xattr - check whether inode allows writing xattr
+ * @mnt_userns:        User namespace of the mount the inode was found from
+ * @inode: the inode on which to set an xattr
+ *
+ * Check whether the inode allows writing xattrs. Specifically, we can never
+ * set or remove an extended attribute on a read-only filesystem  or on an
+ * immutable / append-only inode.
+ *
+ * We also need to ensure that the inode has a mapping in the mount to
+ * not risk writing back invalid i_{g,u}id values.
+ *
+ * Return: On success zero is returned. On error a negative errno is returned.
+ */
+int may_write_xattr(struct user_namespace *mnt_userns, struct inode *inode)
+{
+       if (IS_IMMUTABLE(inode))
+               return -EPERM;
+       if (IS_APPEND(inode))
+               return -EPERM;
+       if (HAS_UNMAPPED_ID(mnt_userns, inode))
+               return -EPERM;
+       return 0;
+}
+
 /*
  * Check permissions for extended attribute access.  This is a bit complicated
  * because different namespaces have very different rules.
@@ -88,20 +113,12 @@ static int
 xattr_permission(struct user_namespace *mnt_userns, struct inode *inode,
                 const char *name, int mask)
 {
-       /*
-        * We can never set or remove an extended attribute on a read-only
-        * filesystem  or on an immutable / append-only inode.
-        */
        if (mask & MAY_WRITE) {
-               if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
-                       return -EPERM;
-               /*
-                * Updating an xattr will likely cause i_uid and i_gid
-                * to be writen back improperly if their true value is
-                * unknown to the vfs.
-                */
-               if (HAS_UNMAPPED_ID(mnt_userns, inode))
-                       return -EPERM;
+               int ret;
+
+               ret = may_write_xattr(mnt_userns, inode);
+               if (ret)
+                       return ret;
        }
 
        /*
@@ -172,6 +189,9 @@ __vfs_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
 {
        const struct xattr_handler *handler;
 
+       if (is_posix_acl_xattr(name))
+               return -EOPNOTSUPP;
+
        handler = xattr_resolve_name(inode, &name);
        if (IS_ERR(handler))
                return PTR_ERR(handler);
@@ -282,12 +302,6 @@ out:
 }
 EXPORT_SYMBOL_GPL(__vfs_setxattr_locked);
 
-static inline bool is_posix_acl_xattr(const char *name)
-{
-       return (strcmp(name, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
-              (strcmp(name, XATTR_NAME_POSIX_ACL_DEFAULT) == 0);
-}
-
 int
 vfs_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
             const char *name, const void *value, size_t size, int flags)
@@ -399,6 +413,9 @@ __vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name,
 {
        const struct xattr_handler *handler;
 
+       if (is_posix_acl_xattr(name))
+               return -EOPNOTSUPP;
+
        handler = xattr_resolve_name(inode, &name);
        if (IS_ERR(handler))
                return PTR_ERR(handler);
@@ -437,10 +454,7 @@ vfs_getxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
                return ret;
        }
 nolsm:
-       error = __vfs_getxattr(dentry, inode, name, value, size);
-       if (error > 0 && is_posix_acl_xattr(name))
-               posix_acl_getxattr_idmapped_mnt(mnt_userns, inode, value, size);
-       return error;
+       return __vfs_getxattr(dentry, inode, name, value, size);
 }
 EXPORT_SYMBOL_GPL(vfs_getxattr);
 
@@ -471,6 +485,9 @@ __vfs_removexattr(struct user_namespace *mnt_userns, struct dentry *dentry,
        struct inode *inode = d_inode(dentry);
        const struct xattr_handler *handler;
 
+       if (is_posix_acl_xattr(name))
+               return -EOPNOTSUPP;
+
        handler = xattr_resolve_name(inode, &name);
        if (IS_ERR(handler))
                return PTR_ERR(handler);
@@ -580,23 +597,19 @@ int setxattr_copy(const char __user *name, struct xattr_ctx *ctx)
        return error;
 }
 
-static void setxattr_convert(struct user_namespace *mnt_userns,
-                            struct dentry *d, struct xattr_ctx *ctx)
-{
-       if (ctx->size && is_posix_acl_xattr(ctx->kname->name))
-               posix_acl_fix_xattr_from_user(ctx->kvalue, ctx->size);
-}
-
-int do_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
+int do_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
                struct xattr_ctx *ctx)
 {
-       setxattr_convert(mnt_userns, dentry, ctx);
-       return vfs_setxattr(mnt_userns, dentry, ctx->kname->name,
+       if (is_posix_acl_xattr(ctx->kname->name))
+               return do_set_acl(idmap, dentry, ctx->kname->name,
+                                 ctx->kvalue, ctx->size);
+
+       return vfs_setxattr(mnt_idmap_owner(idmap), dentry, ctx->kname->name,
                        ctx->kvalue, ctx->size, ctx->flags);
 }
 
 static long
-setxattr(struct user_namespace *mnt_userns, struct dentry *d,
+setxattr(struct mnt_idmap *idmap, struct dentry *d,
        const char __user *name, const void __user *value, size_t size,
        int flags)
 {
@@ -614,7 +627,7 @@ setxattr(struct user_namespace *mnt_userns, struct dentry *d,
        if (error)
                return error;
 
-       error = do_setxattr(mnt_userns, d, &ctx);
+       error = do_setxattr(idmap, d, &ctx);
 
        kvfree(ctx.kvalue);
        return error;
@@ -633,7 +646,7 @@ retry:
                return error;
        error = mnt_want_write(path.mnt);
        if (!error) {
-               error = setxattr(mnt_user_ns(path.mnt), path.dentry, name,
+               error = setxattr(mnt_idmap(path.mnt), path.dentry, name,
                                 value, size, flags);
                mnt_drop_write(path.mnt);
        }
@@ -670,7 +683,7 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
        audit_file(f.file);
        error = mnt_want_write_file(f.file);
        if (!error) {
-               error = setxattr(file_mnt_user_ns(f.file),
+               error = setxattr(file_mnt_idmap(f.file),
                                 f.file->f_path.dentry, name,
                                 value, size, flags);
                mnt_drop_write_file(f.file);
@@ -683,7 +696,7 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
  * Extended attribute GET operations
  */
 ssize_t
-do_getxattr(struct user_namespace *mnt_userns, struct dentry *d,
+do_getxattr(struct mnt_idmap *idmap, struct dentry *d,
        struct xattr_ctx *ctx)
 {
        ssize_t error;
@@ -697,10 +710,12 @@ do_getxattr(struct user_namespace *mnt_userns, struct dentry *d,
                        return -ENOMEM;
        }
 
-       error = vfs_getxattr(mnt_userns, d, kname, ctx->kvalue, ctx->size);
+       if (is_posix_acl_xattr(ctx->kname->name))
+               error = do_get_acl(idmap, d, kname, ctx->kvalue, ctx->size);
+       else
+               error = vfs_getxattr(mnt_idmap_owner(idmap), d, kname,
+                                    ctx->kvalue, ctx->size);
        if (error > 0) {
-               if (is_posix_acl_xattr(kname))
-                       posix_acl_fix_xattr_to_user(ctx->kvalue, error);
                if (ctx->size && copy_to_user(ctx->value, ctx->kvalue, error))
                        error = -EFAULT;
        } else if (error == -ERANGE && ctx->size >= XATTR_SIZE_MAX) {
@@ -713,7 +728,7 @@ do_getxattr(struct user_namespace *mnt_userns, struct dentry *d,
 }
 
 static ssize_t
-getxattr(struct user_namespace *mnt_userns, struct dentry *d,
+getxattr(struct mnt_idmap *idmap, struct dentry *d,
         const char __user *name, void __user *value, size_t size)
 {
        ssize_t error;
@@ -732,7 +747,7 @@ getxattr(struct user_namespace *mnt_userns, struct dentry *d,
        if (error < 0)
                return error;
 
-       error =  do_getxattr(mnt_userns, d, &ctx);
+       error =  do_getxattr(idmap, d, &ctx);
 
        kvfree(ctx.kvalue);
        return error;
@@ -748,7 +763,7 @@ retry:
        error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
        if (error)
                return error;
-       error = getxattr(mnt_user_ns(path.mnt), path.dentry, name, value, size);
+       error = getxattr(mnt_idmap(path.mnt), path.dentry, name, value, size);
        path_put(&path);
        if (retry_estale(error, lookup_flags)) {
                lookup_flags |= LOOKUP_REVAL;
@@ -778,7 +793,7 @@ SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name,
        if (!f.file)
                return error;
        audit_file(f.file);
-       error = getxattr(file_mnt_user_ns(f.file), f.file->f_path.dentry,
+       error = getxattr(file_mnt_idmap(f.file), f.file->f_path.dentry,
                         name, value, size);
        fdput(f);
        return error;
@@ -863,7 +878,7 @@ SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size)
  * Extended attribute REMOVE operations
  */
 static long
-removexattr(struct user_namespace *mnt_userns, struct dentry *d,
+removexattr(struct mnt_idmap *idmap, struct dentry *d,
            const char __user *name)
 {
        int error;
@@ -875,7 +890,10 @@ removexattr(struct user_namespace *mnt_userns, struct dentry *d,
        if (error < 0)
                return error;
 
-       return vfs_removexattr(mnt_userns, d, kname);
+       if (is_posix_acl_xattr(kname))
+               return vfs_remove_acl(mnt_idmap_owner(idmap), d, kname);
+
+       return vfs_removexattr(mnt_idmap_owner(idmap), d, kname);
 }
 
 static int path_removexattr(const char __user *pathname,
@@ -889,7 +907,7 @@ retry:
                return error;
        error = mnt_want_write(path.mnt);
        if (!error) {
-               error = removexattr(mnt_user_ns(path.mnt), path.dentry, name);
+               error = removexattr(mnt_idmap(path.mnt), path.dentry, name);
                mnt_drop_write(path.mnt);
        }
        path_put(&path);
@@ -922,7 +940,7 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name)
        audit_file(f.file);
        error = mnt_want_write_file(f.file);
        if (!error) {
-               error = removexattr(file_mnt_user_ns(f.file),
+               error = removexattr(file_mnt_idmap(f.file),
                                    f.file->f_path.dentry, name);
                mnt_drop_write_file(f.file);
        }
@@ -1140,7 +1158,7 @@ static int xattr_list_one(char **buffer, ssize_t *remaining_size,
 ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs *xattrs,
                          char *buffer, size_t size)
 {
-       bool trusted = capable(CAP_SYS_ADMIN);
+       bool trusted = ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN);
        struct simple_xattr *xattr;
        ssize_t remaining_size = size;
        int err = 0;