Merge tag 'fs.acl.rework.v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 13 Dec 2022 02:46:39 +0000 (18:46 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 13 Dec 2022 02:46:39 +0000 (18:46 -0800)
Pull VFS acl updates from Christian Brauner:
 "This contains the work that builds a dedicated vfs posix acl api.

  The origins of this work trace back to v5.19 but it took quite a while
  to understand the various filesystem specific implementations in
  sufficient detail and also come up with an acceptable solution.

  As we discussed and seen multiple times the current state of how posix
  acls are handled isn't nice and comes with a lot of problems: The
  current way of handling posix acls via the generic xattr api is error
  prone, hard to maintain, and type unsafe for the vfs until we call
  into the filesystem's dedicated get and set inode operations.

  It is already the case that posix acls are special-cased to death all
  the way through the vfs. There are an uncounted number of hacks that
  operate on the uapi posix acl struct instead of the dedicated vfs
  struct posix_acl. And the vfs must be involved in order to interpret
  and fixup posix acls before storing them to the backing store, caching
  them, reporting them to userspace, or for permission checking.

  Currently a range of hacks and duct tape exist to make this work. As
  with most things this is really no ones fault it's just something that
  happened over time. But the code is hard to understand and difficult
  to maintain and one is constantly at risk of introducing bugs and
  regressions when having to touch it.

  Instead of continuing to hack posix acls through the xattr handlers
  this series builds a dedicated posix acl api solely around the get and
  set inode operations.

  Going forward, the vfs_get_acl(), vfs_remove_acl(), and vfs_set_acl()
  helpers must be used in order to interact with posix acls. They
  operate directly on the vfs internal struct posix_acl instead of
  abusing the uapi posix acl struct as we currently do. In the end this
  removes all of the hackiness, makes the codepaths easier to maintain,
  and gets us type safety.

  This series passes the LTP and xfstests suites without any
  regressions. For xfstests the following combinations were tested:
   - xfs
   - ext4
   - btrfs
   - overlayfs
   - overlayfs on top of idmapped mounts
   - orangefs
   - (limited) cifs

  There's more simplifications for posix acls that we can make in the
  future if the basic api has made it.

  A few implementation details:

   - The series makes sure to retain exactly the same security and
     integrity module permission checks. Especially for the integrity
     modules this api is a win because right now they convert the uapi
     posix acl struct passed to them via a void pointer into the vfs
     struct posix_acl format to perform permission checking on the mode.

     There's a new dedicated security hook for setting posix acls which
     passes the vfs struct posix_acl not a void pointer. Basing checking
     on the posix acl stored in the uapi format is really unreliable.
     The vfs currently hacks around directly in the uapi struct storing
     values that frankly the security and integrity modules can't
     correctly interpret as evidenced by bugs we reported and fixed in
     this area. It's not necessarily even their fault it's just that the
     format we provide to them is sub optimal.

   - Some filesystems like 9p and cifs need access to the dentry in
     order to get and set posix acls which is why they either only
     partially or not even at all implement get and set inode
     operations. For example, cifs allows setxattr() and getxattr()
     operations but doesn't allow permission checking based on posix
     acls because it can't implement a get acl inode operation.

     Thus, this patch series updates the set acl inode operation to take
     a dentry instead of an inode argument. However, for the get acl
     inode operation we can't do this as the old get acl method is
     called in e.g., generic_permission() and inode_permission(). These
     helpers in turn are called in various filesystem's permission inode
     operation. So passing a dentry argument to the old get acl inode
     operation would amount to passing a dentry to the permission inode
     operation which we shouldn't and probably can't do.

     So instead of extending the existing inode operation Christoph
     suggested to add a new one. He also requested to ensure that the
     get and set acl inode operation taking a dentry are consistently
     named. So for this version the old get acl operation is renamed to
     ->get_inode_acl() and a new ->get_acl() inode operation taking a
     dentry is added. With this we can give both 9p and cifs get and set
     acl inode operations and in turn remove their complex custom posix
     xattr handlers.

     In the future I hope to get rid of the inode method duplication but
     it isn't like we have never had this situation. Readdir is just one
     example. And frankly, the overall gain in type safety and the more
     pleasant api wise are simply too big of a benefit to not accept
     this duplication for a while.

   - We've done a full audit of every codepaths using variant of the
     current generic xattr api to get and set posix acls and
     surprisingly it isn't that many places. There's of course always a
     chance that we might have missed some and if so I'm sure we'll find
     them soon enough.

     The crucial codepaths to be converted are obviously stacking
     filesystems such as ecryptfs and overlayfs.

     For a list of all callers currently using generic xattr api helpers
     see [2] including comments whether they support posix acls or not.

   - The old vfs generic posix acl infrastructure doesn't obey the
     create and replace semantics promised on the setxattr(2) manpage.
     This patch series doesn't address this. It really is something we
     should revisit later though.

  The patches are roughly organized as follows:

   (1) Change existing set acl inode operation to take a dentry
       argument (Intended to be a non-functional change)

   (2) Rename existing get acl method (Intended to be a non-functional
       change)

   (3) Implement get and set acl inode operations for filesystems that
       couldn't implement one before because of the missing dentry.
       That's mostly 9p and cifs (Intended to be a non-functional
       change)

   (4) Build posix acl api, i.e., add vfs_get_acl(), vfs_remove_acl(),
       and vfs_set_acl() including security and integrity hooks
       (Intended to be a non-functional change)

   (5) Implement get and set acl inode operations for stacking
       filesystems (Intended to be a non-functional change)

   (6) Switch posix acl handling in stacking filesystems to new posix
       acl api now that all filesystems it can stack upon support it.

   (7) Switch vfs to new posix acl api (semantical change)

   (8) Remove all now unused helpers

   (9) Additional regression fixes reported after we merged this into
       linux-next

  Thanks to Seth for a lot of good discussion around this and
  encouragement and input from Christoph"

* tag 'fs.acl.rework.v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/idmapping: (36 commits)
  posix_acl: Fix the type of sentinel in get_acl
  orangefs: fix mode handling
  ovl: call posix_acl_release() after error checking
  evm: remove dead code in evm_inode_set_acl()
  cifs: check whether acl is valid early
  acl: make vfs_posix_acl_to_xattr() static
  acl: remove a slew of now unused helpers
  9p: use stub posix acl handlers
  cifs: use stub posix acl handlers
  ovl: use stub posix acl handlers
  ecryptfs: use stub posix acl handlers
  evm: remove evm_xattr_acl_change()
  xattr: use posix acl api
  ovl: use posix acl api
  ovl: implement set acl method
  ovl: implement get acl method
  ecryptfs: implement set acl method
  ecryptfs: implement get acl method
  ksmbd: use vfs_remove_acl()
  acl: add vfs_remove_acl()
  ...

109 files changed:
Documentation/filesystems/locking.rst
Documentation/filesystems/porting.rst
Documentation/filesystems/vfs.rst
fs/9p/acl.c
fs/9p/acl.h
fs/9p/vfs_inode_dotl.c
fs/9p/xattr.c
fs/9p/xattr.h
fs/bad_inode.c
fs/btrfs/acl.c
fs/btrfs/ctree.h
fs/btrfs/inode.c
fs/ceph/acl.c
fs/ceph/dir.c
fs/ceph/inode.c
fs/ceph/super.h
fs/cifs/cifsacl.c
fs/cifs/cifsfs.c
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/xattr.c
fs/ecryptfs/inode.c
fs/erofs/inode.c
fs/erofs/namei.c
fs/ext2/acl.c
fs/ext2/acl.h
fs/ext2/file.c
fs/ext2/inode.c
fs/ext2/namei.c
fs/ext4/acl.c
fs/ext4/acl.h
fs/ext4/file.c
fs/ext4/ialloc.c
fs/ext4/inode.c
fs/ext4/namei.c
fs/f2fs/acl.c
fs/f2fs/acl.h
fs/f2fs/file.c
fs/f2fs/namei.c
fs/fuse/acl.c
fs/fuse/dir.c
fs/fuse/fuse_i.h
fs/gfs2/acl.c
fs/gfs2/acl.h
fs/gfs2/inode.c
fs/internal.h
fs/jffs2/acl.c
fs/jffs2/acl.h
fs/jffs2/dir.c
fs/jffs2/file.c
fs/jffs2/fs.c
fs/jfs/acl.c
fs/jfs/file.c
fs/jfs/jfs_acl.h
fs/jfs/namei.c
fs/ksmbd/smb2pdu.c
fs/ksmbd/smbacl.c
fs/ksmbd/vfs.c
fs/ksmbd/vfs.h
fs/namei.c
fs/nfs/nfs3_fs.h
fs/nfs/nfs3acl.c
fs/nfs/nfs3proc.c
fs/nfsd/nfs2acl.c
fs/nfsd/nfs3acl.c
fs/nfsd/nfs4acl.c
fs/nfsd/vfs.c
fs/ntfs3/file.c
fs/ntfs3/namei.c
fs/ntfs3/ntfs_fs.h
fs/ntfs3/xattr.c
fs/ocfs2/acl.c
fs/ocfs2/acl.h
fs/ocfs2/file.c
fs/ocfs2/namei.c
fs/orangefs/acl.c
fs/orangefs/inode.c
fs/orangefs/namei.c
fs/orangefs/orangefs-kernel.h
fs/overlayfs/copy_up.c
fs/overlayfs/dir.c
fs/overlayfs/inode.c
fs/overlayfs/overlayfs.h
fs/overlayfs/super.c
fs/posix_acl.c
fs/reiserfs/acl.h
fs/reiserfs/file.c
fs/reiserfs/inode.c
fs/reiserfs/namei.c
fs/reiserfs/xattr_acl.c
fs/xattr.c
fs/xfs/xfs_acl.c
fs/xfs/xfs_acl.h
fs/xfs/xfs_iops.c
include/linux/evm.h
include/linux/fs.h
include/linux/ima.h
include/linux/lsm_hook_defs.h
include/linux/lsm_hooks.h
include/linux/posix_acl.h
include/linux/posix_acl_xattr.h
include/linux/security.h
include/linux/xattr.h
mm/shmem.c
security/integrity/evm/evm_main.c
security/integrity/ima/ima_appraise.c
security/security.c
security/selinux/hooks.c
security/smack/smack_lsm.c

index 8f737e7..36fa2a8 100644 (file)
@@ -70,7 +70,7 @@ prototypes::
        const char *(*get_link) (struct dentry *, struct inode *, struct delayed_call *);
        void (*truncate) (struct inode *);
        int (*permission) (struct inode *, int, unsigned int);
-       struct posix_acl * (*get_acl)(struct inode *, int, bool);
+       struct posix_acl * (*get_inode_acl)(struct inode *, int, bool);
        int (*setattr) (struct dentry *, struct iattr *);
        int (*getattr) (const struct path *, struct kstat *, u32, unsigned int);
        ssize_t (*listxattr) (struct dentry *, char *, size_t);
@@ -84,13 +84,14 @@ prototypes::
        int (*fileattr_set)(struct user_namespace *mnt_userns,
                            struct dentry *dentry, struct fileattr *fa);
        int (*fileattr_get)(struct dentry *dentry, struct fileattr *fa);
+       struct posix_acl * (*get_acl)(struct user_namespace *, struct dentry *, int);
 
 locking rules:
        all may block
 
-=============  =============================================
+============== =============================================
 ops            i_rwsem(inode)
-=============  =============================================
+============== =============================================
 lookup:                shared
 create:                exclusive
 link:          exclusive (both)
@@ -104,6 +105,7 @@ readlink:   no
 get_link:      no
 setattr:       exclusive
 permission:    no (may not block if called in rcu-walk mode)
+get_inode_acl: no
 get_acl:       no
 getattr:       no
 listxattr:     no
@@ -113,7 +115,7 @@ atomic_open:        shared (exclusive if O_CREAT is set in open flags)
 tmpfile:       no
 fileattr_get:  no or exclusive
 fileattr_set:  exclusive
-=============  =============================================
+============== =============================================
 
 
        Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_rwsem
index df0dc37..d2d684a 100644 (file)
@@ -462,8 +462,8 @@ ERR_PTR(...).
 argument; instead of passing IPERM_FLAG_RCU we add MAY_NOT_BLOCK into mask.
 
 generic_permission() has also lost the check_acl argument; ACL checking
-has been taken to VFS and filesystems need to provide a non-NULL ->i_op->get_acl
-to read an ACL from disk.
+has been taken to VFS and filesystems need to provide a non-NULL
+->i_op->get_inode_acl to read an ACL from disk.
 
 ---
 
index 2b55f71..2c15e70 100644 (file)
@@ -435,7 +435,7 @@ As of kernel 2.6.22, the following members are defined:
                const char *(*get_link) (struct dentry *, struct inode *,
                                         struct delayed_call *);
                int (*permission) (struct user_namespace *, struct inode *, int);
-               struct posix_acl * (*get_acl)(struct inode *, int, bool);
+               struct posix_acl * (*get_inode_acl)(struct inode *, int, bool);
                int (*setattr) (struct user_namespace *, struct dentry *, struct iattr *);
                int (*getattr) (struct user_namespace *, const struct path *, struct kstat *, u32, unsigned int);
                ssize_t (*listxattr) (struct dentry *, char *, size_t);
@@ -443,7 +443,8 @@ As of kernel 2.6.22, the following members are defined:
                int (*atomic_open)(struct inode *, struct dentry *, struct file *,
                                   unsigned open_flag, umode_t create_mode);
                int (*tmpfile) (struct user_namespace *, struct inode *, struct file *, umode_t);
-               int (*set_acl)(struct user_namespace *, struct inode *, struct posix_acl *, int);
+               struct posix_acl * (*get_acl)(struct user_namespace *, struct dentry *, int);
+               int (*set_acl)(struct user_namespace *, struct dentry *, struct posix_acl *, int);
                int (*fileattr_set)(struct user_namespace *mnt_userns,
                                    struct dentry *dentry, struct fileattr *fa);
                int (*fileattr_get)(struct dentry *dentry, struct fileattr *fa);
index 4dac4a0..c397c51 100644 (file)
 #include "v9fs_vfs.h"
 #include "fid.h"
 
-static struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, char *name)
+static struct posix_acl *v9fs_fid_get_acl(struct p9_fid *fid, const char *name)
 {
        ssize_t size;
        void *value = NULL;
        struct posix_acl *acl = NULL;
 
        size = v9fs_fid_xattr_get(fid, name, NULL, 0);
-       if (size > 0) {
-               value = kzalloc(size, GFP_NOFS);
-               if (!value)
-                       return ERR_PTR(-ENOMEM);
-               size = v9fs_fid_xattr_get(fid, name, value, size);
-               if (size > 0) {
-                       acl = posix_acl_from_xattr(&init_user_ns, value, size);
-                       if (IS_ERR(acl))
-                               goto err_out;
-               }
-       } else if (size == -ENODATA || size == 0 ||
-                  size == -ENOSYS || size == -EOPNOTSUPP) {
-               acl = NULL;
-       } else
-               acl = ERR_PTR(-EIO);
-
-err_out:
+       if (size < 0)
+               return ERR_PTR(size);
+       if (size == 0)
+               return ERR_PTR(-ENODATA);
+
+       value = kzalloc(size, GFP_NOFS);
+       if (!value)
+               return ERR_PTR(-ENOMEM);
+
+       size = v9fs_fid_xattr_get(fid, name, value, size);
+       if (size < 0)
+               acl = ERR_PTR(size);
+       else if (size == 0)
+               acl = ERR_PTR(-ENODATA);
+       else
+               acl = posix_acl_from_xattr(&init_user_ns, value, size);
        kfree(value);
        return acl;
 }
 
+static struct posix_acl *v9fs_acl_get(struct dentry *dentry, const char *name)
+{
+       struct p9_fid *fid;
+       struct posix_acl *acl = NULL;
+
+       fid = v9fs_fid_lookup(dentry);
+       if (IS_ERR(fid))
+               return ERR_CAST(fid);
+
+       acl = v9fs_fid_get_acl(fid, name);
+       p9_fid_put(fid);
+       return acl;
+}
+
+static struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, const char *name)
+{
+       int retval;
+       struct posix_acl *acl = NULL;
+
+       acl = v9fs_fid_get_acl(fid, name);
+       if (!IS_ERR(acl))
+               return acl;
+
+       retval = PTR_ERR(acl);
+       if (retval == -ENODATA || retval == -ENOSYS || retval == -EOPNOTSUPP)
+               return NULL;
+
+       /* map everything else to -EIO */
+       return ERR_PTR(-EIO);
+}
+
 int v9fs_get_acl(struct inode *inode, struct p9_fid *fid)
 {
        int retval = 0;
@@ -89,7 +119,7 @@ static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type)
        return acl;
 }
 
-struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type, bool rcu)
+struct posix_acl *v9fs_iop_get_inode_acl(struct inode *inode, int type, bool rcu)
 {
        struct v9fs_session_info *v9ses;
 
@@ -109,6 +139,112 @@ struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type, bool rcu)
 
 }
 
+struct posix_acl *v9fs_iop_get_acl(struct user_namespace *mnt_userns,
+                                  struct dentry *dentry, int type)
+{
+       struct v9fs_session_info *v9ses;
+
+       v9ses = v9fs_dentry2v9ses(dentry);
+       /* We allow set/get/list of acl when access=client is not specified. */
+       if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT)
+               return v9fs_acl_get(dentry, posix_acl_xattr_name(type));
+       return v9fs_get_cached_acl(d_inode(dentry), type);
+}
+
+int v9fs_iop_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+                    struct posix_acl *acl, int type)
+{
+       int retval;
+       size_t size = 0;
+       void *value = NULL;
+       const char *acl_name;
+       struct v9fs_session_info *v9ses;
+       struct inode *inode = d_inode(dentry);
+
+       if (acl) {
+               retval = posix_acl_valid(inode->i_sb->s_user_ns, acl);
+               if (retval)
+                       goto err_out;
+
+               size = posix_acl_xattr_size(acl->a_count);
+
+               value = kzalloc(size, GFP_NOFS);
+               if (!value) {
+                       retval = -ENOMEM;
+                       goto err_out;
+               }
+
+               retval = posix_acl_to_xattr(&init_user_ns, acl, value, size);
+               if (retval < 0)
+                       goto err_out;
+       }
+
+       /*
+        * set the attribute on the remote. Without even looking at the
+        * xattr value. We leave it to the server to validate
+        */
+       acl_name = posix_acl_xattr_name(type);
+       v9ses = v9fs_dentry2v9ses(dentry);
+       if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) {
+               retval = v9fs_xattr_set(dentry, acl_name, value, size, 0);
+               goto err_out;
+       }
+
+       if (S_ISLNK(inode->i_mode)) {
+               retval = -EOPNOTSUPP;
+               goto err_out;
+       }
+
+       if (!inode_owner_or_capable(&init_user_ns, inode)) {
+               retval = -EPERM;
+               goto err_out;
+       }
+
+       switch (type) {
+       case ACL_TYPE_ACCESS:
+               if (acl) {
+                       struct iattr iattr = {};
+                       struct posix_acl *acl_mode = acl;
+
+                       retval = posix_acl_update_mode(&init_user_ns, inode,
+                                                      &iattr.ia_mode,
+                                                      &acl_mode);
+                       if (retval)
+                               goto err_out;
+                       if (!acl_mode) {
+                               /*
+                                * ACL can be represented by the mode bits.
+                                * So don't update ACL below.
+                                */
+                               kfree(value);
+                               value = NULL;
+                               size = 0;
+                       }
+                       iattr.ia_valid = ATTR_MODE;
+                       /*
+                        * FIXME should we update ctime ?
+                        * What is the following setxattr update the mode ?
+                        */
+                       v9fs_vfs_setattr_dotl(&init_user_ns, dentry, &iattr);
+               }
+               break;
+       case ACL_TYPE_DEFAULT:
+               if (!S_ISDIR(inode->i_mode)) {
+                       retval = acl ? -EINVAL : 0;
+                       goto err_out;
+               }
+               break;
+       }
+
+       retval = v9fs_xattr_set(dentry, acl_name, value, size, 0);
+       if (!retval)
+               set_cached_acl(inode, type, acl);
+
+err_out:
+       kfree(value);
+       return retval;
+}
+
 static int v9fs_set_acl(struct p9_fid *fid, int type, struct posix_acl *acl)
 {
        int retval;
@@ -207,124 +343,3 @@ int v9fs_acl_mode(struct inode *dir, umode_t *modep,
        *modep  = mode;
        return 0;
 }
-
-static int v9fs_xattr_get_acl(const struct xattr_handler *handler,
-                             struct dentry *dentry, struct inode *inode,
-                             const char *name, void *buffer, size_t size)
-{
-       struct v9fs_session_info *v9ses;
-       struct posix_acl *acl;
-       int error;
-
-       v9ses = v9fs_dentry2v9ses(dentry);
-       /*
-        * We allow set/get/list of acl when access=client is not specified
-        */
-       if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT)
-               return v9fs_xattr_get(dentry, handler->name, buffer, size);
-
-       acl = v9fs_get_cached_acl(inode, handler->flags);
-       if (IS_ERR(acl))
-               return PTR_ERR(acl);
-       if (acl == NULL)
-               return -ENODATA;
-       error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
-       posix_acl_release(acl);
-
-       return error;
-}
-
-static int v9fs_xattr_set_acl(const struct xattr_handler *handler,
-                             struct user_namespace *mnt_userns,
-                             struct dentry *dentry, struct inode *inode,
-                             const char *name, const void *value,
-                             size_t size, int flags)
-{
-       int retval;
-       struct posix_acl *acl;
-       struct v9fs_session_info *v9ses;
-
-       v9ses = v9fs_dentry2v9ses(dentry);
-       /*
-        * set the attribute on the remote. Without even looking at the
-        * xattr value. We leave it to the server to validate
-        */
-       if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT)
-               return v9fs_xattr_set(dentry, handler->name, value, size,
-                                     flags);
-
-       if (S_ISLNK(inode->i_mode))
-               return -EOPNOTSUPP;
-       if (!inode_owner_or_capable(&init_user_ns, inode))
-               return -EPERM;
-       if (value) {
-               /* update the cached acl value */
-               acl = posix_acl_from_xattr(&init_user_ns, value, size);
-               if (IS_ERR(acl))
-                       return PTR_ERR(acl);
-               else if (acl) {
-                       retval = posix_acl_valid(inode->i_sb->s_user_ns, acl);
-                       if (retval)
-                               goto err_out;
-               }
-       } else
-               acl = NULL;
-
-       switch (handler->flags) {
-       case ACL_TYPE_ACCESS:
-               if (acl) {
-                       struct iattr iattr = { 0 };
-                       struct posix_acl *old_acl = acl;
-
-                       retval = posix_acl_update_mode(&init_user_ns, inode,
-                                                      &iattr.ia_mode, &acl);
-                       if (retval)
-                               goto err_out;
-                       if (!acl) {
-                               /*
-                                * ACL can be represented
-                                * by the mode bits. So don't
-                                * update ACL.
-                                */
-                               posix_acl_release(old_acl);
-                               value = NULL;
-                               size = 0;
-                       }
-                       iattr.ia_valid = ATTR_MODE;
-                       /* FIXME should we update ctime ?
-                        * What is the following setxattr update the
-                        * mode ?
-                        */
-                       v9fs_vfs_setattr_dotl(&init_user_ns, dentry, &iattr);
-               }
-               break;
-       case ACL_TYPE_DEFAULT:
-               if (!S_ISDIR(inode->i_mode)) {
-                       retval = acl ? -EINVAL : 0;
-                       goto err_out;
-               }
-               break;
-       default:
-               BUG();
-       }
-       retval = v9fs_xattr_set(dentry, handler->name, value, size, flags);
-       if (!retval)
-               set_cached_acl(inode, handler->flags, acl);
-err_out:
-       posix_acl_release(acl);
-       return retval;
-}
-
-const struct xattr_handler v9fs_xattr_acl_access_handler = {
-       .name   = XATTR_NAME_POSIX_ACL_ACCESS,
-       .flags  = ACL_TYPE_ACCESS,
-       .get    = v9fs_xattr_get_acl,
-       .set    = v9fs_xattr_set_acl,
-};
-
-const struct xattr_handler v9fs_xattr_acl_default_handler = {
-       .name   = XATTR_NAME_POSIX_ACL_DEFAULT,
-       .flags  = ACL_TYPE_DEFAULT,
-       .get    = v9fs_xattr_get_acl,
-       .set    = v9fs_xattr_set_acl,
-};
index ce5175d..4c60a2b 100644 (file)
@@ -8,8 +8,12 @@
 
 #ifdef CONFIG_9P_FS_POSIX_ACL
 int v9fs_get_acl(struct inode *inode, struct p9_fid *fid);
-struct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type,
+struct posix_acl *v9fs_iop_get_inode_acl(struct inode *inode, int type,
                                   bool rcu);
+struct posix_acl *v9fs_iop_get_acl(struct user_namespace *mnt_userns,
+                                         struct dentry *dentry, int type);
+int v9fs_iop_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+                    struct posix_acl *acl, int type);
 int v9fs_acl_chmod(struct inode *inode, struct p9_fid *fid);
 int v9fs_set_create_acl(struct inode *inode, struct p9_fid *fid,
                        struct posix_acl *dacl, struct posix_acl *acl);
@@ -17,7 +21,9 @@ int v9fs_acl_mode(struct inode *dir, umode_t *modep,
                  struct posix_acl **dpacl, struct posix_acl **pacl);
 void v9fs_put_acl(struct posix_acl *dacl, struct posix_acl *acl);
 #else
+#define v9fs_iop_get_inode_acl NULL
 #define v9fs_iop_get_acl NULL
+#define v9fs_iop_set_acl NULL
 static inline int v9fs_get_acl(struct inode *inode, struct p9_fid *fid)
 {
        return 0;
index 5cfa4b4..03c1743 100644 (file)
@@ -983,14 +983,18 @@ const struct inode_operations v9fs_dir_inode_operations_dotl = {
        .getattr = v9fs_vfs_getattr_dotl,
        .setattr = v9fs_vfs_setattr_dotl,
        .listxattr = v9fs_listxattr,
+       .get_inode_acl = v9fs_iop_get_inode_acl,
        .get_acl = v9fs_iop_get_acl,
+       .set_acl = v9fs_iop_set_acl,
 };
 
 const struct inode_operations v9fs_file_inode_operations_dotl = {
        .getattr = v9fs_vfs_getattr_dotl,
        .setattr = v9fs_vfs_setattr_dotl,
        .listxattr = v9fs_listxattr,
+       .get_inode_acl = v9fs_iop_get_inode_acl,
        .get_acl = v9fs_iop_get_acl,
+       .set_acl = v9fs_iop_set_acl,
 };
 
 const struct inode_operations v9fs_symlink_inode_operations_dotl = {
index 2807bb6..b698431 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/fs.h>
 #include <linux/sched.h>
 #include <linux/uio.h>
+#include <linux/posix_acl_xattr.h>
 #include <net/9p/9p.h>
 #include <net/9p/client.h>
 
@@ -182,9 +183,9 @@ static struct xattr_handler v9fs_xattr_security_handler = {
 const struct xattr_handler *v9fs_xattr_handlers[] = {
        &v9fs_xattr_user_handler,
        &v9fs_xattr_trusted_handler,
-#ifdef CONFIG_9P_FS_POSIX_ACL
-       &v9fs_xattr_acl_access_handler,
-       &v9fs_xattr_acl_default_handler,
+#ifdef CONFIG_FS_POSIX_ACL
+       &posix_acl_access_xattr_handler,
+       &posix_acl_default_xattr_handler,
 #endif
 #ifdef CONFIG_9P_FS_SECURITY
        &v9fs_xattr_security_handler,
index 3e11fc3..b5636e5 100644 (file)
@@ -11,8 +11,6 @@
 #include <net/9p/client.h>
 
 extern const struct xattr_handler *v9fs_xattr_handlers[];
-extern const struct xattr_handler v9fs_xattr_acl_access_handler;
-extern const struct xattr_handler v9fs_xattr_acl_default_handler;
 
 ssize_t v9fs_fid_xattr_get(struct p9_fid *fid, const char *name,
                           void *buffer, size_t buffer_size);
index 9d1cde8..9273716 100644 (file)
@@ -154,7 +154,7 @@ static int bad_inode_tmpfile(struct user_namespace *mnt_userns,
 }
 
 static int bad_inode_set_acl(struct user_namespace *mnt_userns,
-                            struct inode *inode, struct posix_acl *acl,
+                            struct dentry *dentry, struct posix_acl *acl,
                             int type)
 {
        return -EIO;
@@ -177,7 +177,7 @@ static const struct inode_operations bad_inode_ops =
        .setattr        = bad_inode_setattr,
        .listxattr      = bad_inode_listxattr,
        .get_link       = bad_inode_get_link,
-       .get_acl        = bad_inode_get_acl,
+       .get_inode_acl  = bad_inode_get_acl,
        .fiemap         = bad_inode_fiemap,
        .update_time    = bad_inode_update_time,
        .atomic_open    = bad_inode_atomic_open,
index 548d6a5..1e47b3e 100644 (file)
@@ -110,10 +110,11 @@ out:
        return ret;
 }
 
-int btrfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int btrfs_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                  struct posix_acl *acl, int type)
 {
        int ret;
+       struct inode *inode = d_inode(dentry);
        umode_t old_mode = inode->i_mode;
 
        if (type == ACL_TYPE_ACCESS && acl) {
index 9e6d48f..919670d 100644 (file)
@@ -3993,7 +3993,7 @@ static inline int __btrfs_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag)
 /* acl.c */
 #ifdef CONFIG_BTRFS_FS_POSIX_ACL
 struct posix_acl *btrfs_get_acl(struct inode *inode, int type, bool rcu);
-int btrfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int btrfs_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                  struct posix_acl *acl, int type);
 int __btrfs_set_acl(struct btrfs_trans_handle *trans, struct inode *inode,
                    struct posix_acl *acl, int type);
index 0e516ae..5a54bb9 100644 (file)
@@ -5256,7 +5256,7 @@ static int btrfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentr
                err = btrfs_dirty_inode(inode);
 
                if (!err && attr->ia_valid & ATTR_MODE)
-                       err = posix_acl_chmod(mnt_userns, inode, inode->i_mode);
+                       err = posix_acl_chmod(mnt_userns, dentry, inode->i_mode);
        }
 
        return err;
@@ -11296,7 +11296,7 @@ static const struct inode_operations btrfs_dir_inode_operations = {
        .mknod          = btrfs_mknod,
        .listxattr      = btrfs_listxattr,
        .permission     = btrfs_permission,
-       .get_acl        = btrfs_get_acl,
+       .get_inode_acl  = btrfs_get_acl,
        .set_acl        = btrfs_set_acl,
        .update_time    = btrfs_update_time,
        .tmpfile        = btrfs_tmpfile,
@@ -11349,7 +11349,7 @@ static const struct inode_operations btrfs_file_inode_operations = {
        .listxattr      = btrfs_listxattr,
        .permission     = btrfs_permission,
        .fiemap         = btrfs_fiemap,
-       .get_acl        = btrfs_get_acl,
+       .get_inode_acl  = btrfs_get_acl,
        .set_acl        = btrfs_set_acl,
        .update_time    = btrfs_update_time,
        .fileattr_get   = btrfs_fileattr_get,
@@ -11360,7 +11360,7 @@ static const struct inode_operations btrfs_special_inode_operations = {
        .setattr        = btrfs_setattr,
        .permission     = btrfs_permission,
        .listxattr      = btrfs_listxattr,
-       .get_acl        = btrfs_get_acl,
+       .get_inode_acl  = btrfs_get_acl,
        .set_acl        = btrfs_set_acl,
        .update_time    = btrfs_update_time,
 };
index f4fc8e0..c7e8dd5 100644 (file)
@@ -85,13 +85,14 @@ retry:
        return acl;
 }
 
-int ceph_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int ceph_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                 struct posix_acl *acl, int type)
 {
        int ret = 0, size = 0;
        const char *name = NULL;
        char *value = NULL;
        struct iattr newattrs;
+       struct inode *inode = d_inode(dentry);
        struct timespec64 old_ctime = inode->i_ctime;
        umode_t new_mode = inode->i_mode, old_mode = inode->i_mode;
 
index e7e2eba..6c7026c 100644 (file)
@@ -2033,7 +2033,7 @@ const struct inode_operations ceph_dir_iops = {
        .getattr = ceph_getattr,
        .setattr = ceph_setattr,
        .listxattr = ceph_listxattr,
-       .get_acl = ceph_get_acl,
+       .get_inode_acl = ceph_get_acl,
        .set_acl = ceph_set_acl,
        .mknod = ceph_mknod,
        .symlink = ceph_symlink,
index d533999..f23c5a6 100644 (file)
@@ -126,7 +126,7 @@ const struct inode_operations ceph_file_iops = {
        .setattr = ceph_setattr,
        .getattr = ceph_getattr,
        .listxattr = ceph_listxattr,
-       .get_acl = ceph_get_acl,
+       .get_inode_acl = ceph_get_acl,
        .set_acl = ceph_set_acl,
 };
 
@@ -2255,7 +2255,7 @@ int ceph_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
        err = __ceph_setattr(inode, attr);
 
        if (err >= 0 && (attr->ia_valid & ATTR_MODE))
-               err = posix_acl_chmod(&init_user_ns, inode, attr->ia_mode);
+               err = posix_acl_chmod(&init_user_ns, dentry, attr->ia_mode);
 
        return err;
 }
index 40630e6..50e57a1 100644 (file)
@@ -1117,7 +1117,7 @@ void ceph_release_acl_sec_ctx(struct ceph_acl_sec_ctx *as_ctx);
 
 struct posix_acl *ceph_get_acl(struct inode *, int, bool);
 int ceph_set_acl(struct user_namespace *mnt_userns,
-                struct inode *inode, struct posix_acl *acl, int type);
+                struct dentry *dentry, struct posix_acl *acl, int type);
 int ceph_pre_init_acls(struct inode *dir, umode_t *mode,
                       struct ceph_acl_sec_ctx *as_ctx);
 void ceph_init_inode_acls(struct inode *inode,
index fa480d6..c647f0d 100644 (file)
@@ -13,6 +13,9 @@
 #include <linux/string.h>
 #include <linux/keyctl.h>
 #include <linux/key-type.h>
+#include <uapi/linux/posix_acl.h>
+#include <linux/posix_acl.h>
+#include <linux/posix_acl_xattr.h>
 #include <keys/user-type.h>
 #include "cifspdu.h"
 #include "cifsglob.h"
@@ -20,6 +23,8 @@
 #include "cifsproto.h"
 #include "cifs_debug.h"
 #include "fs_context.h"
+#include "cifs_fs_sb.h"
+#include "cifs_unicode.h"
 
 /* security id for everyone/world system group */
 static const struct cifs_sid sid_everyone = {
@@ -1668,3 +1673,137 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
        kfree(pntsd);
        return rc;
 }
+
+struct posix_acl *cifs_get_acl(struct user_namespace *mnt_userns,
+                              struct dentry *dentry, int type)
+{
+#if defined(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) && defined(CONFIG_CIFS_POSIX)
+       struct posix_acl *acl = NULL;
+       ssize_t rc = -EOPNOTSUPP;
+       unsigned int xid;
+       struct super_block *sb = dentry->d_sb;
+       struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+       struct tcon_link *tlink;
+       struct cifs_tcon *pTcon;
+       const char *full_path;
+       void *page;
+
+       tlink = cifs_sb_tlink(cifs_sb);
+       if (IS_ERR(tlink))
+               return ERR_CAST(tlink);
+       pTcon = tlink_tcon(tlink);
+
+       xid = get_xid();
+       page = alloc_dentry_path();
+
+       full_path = build_path_from_dentry(dentry, page);
+       if (IS_ERR(full_path)) {
+               acl = ERR_CAST(full_path);
+               goto out;
+       }
+
+       /* return alt name if available as pseudo attr */
+       switch (type) {
+       case ACL_TYPE_ACCESS:
+               if (sb->s_flags & SB_POSIXACL)
+                       rc = cifs_do_get_acl(xid, pTcon, full_path, &acl,
+                                            ACL_TYPE_ACCESS,
+                                            cifs_sb->local_nls,
+                                            cifs_remap(cifs_sb));
+               break;
+
+       case ACL_TYPE_DEFAULT:
+               if (sb->s_flags & SB_POSIXACL)
+                       rc = cifs_do_get_acl(xid, pTcon, full_path, &acl,
+                                            ACL_TYPE_DEFAULT,
+                                            cifs_sb->local_nls,
+                                            cifs_remap(cifs_sb));
+               break;
+       }
+
+       if (rc < 0) {
+               if (rc == -EINVAL)
+                       acl = ERR_PTR(-EOPNOTSUPP);
+               else
+                       acl = ERR_PTR(rc);
+       }
+
+out:
+       free_dentry_path(page);
+       free_xid(xid);
+       cifs_put_tlink(tlink);
+       return acl;
+#else
+       return ERR_PTR(-EOPNOTSUPP);
+#endif
+}
+
+int cifs_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+                struct posix_acl *acl, int type)
+{
+#if defined(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) && defined(CONFIG_CIFS_POSIX)
+       int rc = -EOPNOTSUPP;
+       unsigned int xid;
+       struct super_block *sb = dentry->d_sb;
+       struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+       struct tcon_link *tlink;
+       struct cifs_tcon *pTcon;
+       const char *full_path;
+       void *page;
+
+       tlink = cifs_sb_tlink(cifs_sb);
+       if (IS_ERR(tlink))
+               return PTR_ERR(tlink);
+       pTcon = tlink_tcon(tlink);
+
+       xid = get_xid();
+       page = alloc_dentry_path();
+
+       full_path = build_path_from_dentry(dentry, page);
+       if (IS_ERR(full_path)) {
+               rc = PTR_ERR(full_path);
+               goto out;
+       }
+
+       if (!acl)
+               goto out;
+
+       /* return dos attributes as pseudo xattr */
+       /* return alt name if available as pseudo attr */
+
+       /* if proc/fs/cifs/streamstoxattr is set then
+               search server for EAs or streams to
+               returns as xattrs */
+       if (posix_acl_xattr_size(acl->a_count) > CIFSMaxBufSize) {
+               cifs_dbg(FYI, "size of EA value too large\n");
+               rc = -EOPNOTSUPP;
+               goto out;
+       }
+
+       switch (type) {
+       case ACL_TYPE_ACCESS:
+               if (sb->s_flags & SB_POSIXACL)
+                       rc = cifs_do_set_acl(xid, pTcon, full_path, acl,
+                                            ACL_TYPE_ACCESS,
+                                            cifs_sb->local_nls,
+                                            cifs_remap(cifs_sb));
+               break;
+
+       case ACL_TYPE_DEFAULT:
+               if (sb->s_flags & SB_POSIXACL)
+                       rc = cifs_do_set_acl(xid, pTcon, full_path, acl,
+                                            ACL_TYPE_DEFAULT,
+                                            cifs_sb->local_nls,
+                                            cifs_remap(cifs_sb));
+               break;
+       }
+
+out:
+       free_dentry_path(page);
+       free_xid(xid);
+       cifs_put_tlink(tlink);
+       return rc;
+#else
+       return -EOPNOTSUPP;
+#endif
+}
index 712a431..040267e 100644 (file)
@@ -1133,6 +1133,8 @@ const struct inode_operations cifs_dir_inode_ops = {
        .symlink = cifs_symlink,
        .mknod   = cifs_mknod,
        .listxattr = cifs_listxattr,
+       .get_acl = cifs_get_acl,
+       .set_acl = cifs_set_acl,
 };
 
 const struct inode_operations cifs_file_inode_ops = {
@@ -1141,6 +1143,8 @@ const struct inode_operations cifs_file_inode_ops = {
        .permission = cifs_permission,
        .listxattr = cifs_listxattr,
        .fiemap = cifs_fiemap,
+       .get_acl = cifs_get_acl,
+       .set_acl = cifs_set_acl,
 };
 
 const char *cifs_get_link(struct dentry *dentry, struct inode *inode,
index 83e83d8..f50f96e 100644 (file)
@@ -224,6 +224,10 @@ extern struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *, struct inode *,
                                      const char *, u32 *, u32);
 extern struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *,
                                const struct cifs_fid *, u32 *, u32);
+extern struct posix_acl *cifs_get_acl(struct user_namespace *mnt_userns,
+                                     struct dentry *dentry, int type);
+extern int cifs_set_acl(struct user_namespace *mnt_userns,
+                       struct dentry *dentry, struct posix_acl *acl, int type);
 extern int set_cifs_acl(struct cifs_ntsd *, __u32, struct inode *,
                                const char *, int);
 extern unsigned int setup_authusers_ACE(struct cifs_ace *pace);
@@ -537,14 +541,14 @@ extern int CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon,
                        __u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen);
 extern int CIFSSMBSetCIFSACL(const unsigned int, struct cifs_tcon *, __u16,
                        struct cifs_ntsd *, __u32, int);
-extern int CIFSSMBGetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,
-               const unsigned char *searchName,
-               char *acl_inf, const int buflen, const int acl_type,
-               const struct nls_table *nls_codepage, int remap_special_chars);
-extern int CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,
-               const unsigned char *fileName,
-               const char *local_acl, const int buflen, const int acl_type,
-               const struct nls_table *nls_codepage, int remap_special_chars);
+extern int cifs_do_get_acl(const unsigned int xid, struct cifs_tcon *tcon,
+                          const unsigned char *searchName,
+                          struct posix_acl **acl, const int acl_type,
+                          const struct nls_table *nls_codepage, int remap);
+extern int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
+                          const unsigned char *fileName,
+                          const struct posix_acl *acl, const int acl_type,
+                          const struct nls_table *nls_codepage, int remap);
 extern int CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon,
                        const int netfid, __u64 *pExtAttrBits, __u64 *pMask);
 #endif /* CIFS_ALLOW_INSECURE_LEGACY */
index 1724066..23f10e0 100644 (file)
@@ -2914,32 +2914,57 @@ CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
 
 #ifdef CONFIG_CIFS_POSIX
 
-/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
-static void cifs_convert_ace(struct posix_acl_xattr_entry *ace,
-                            struct cifs_posix_ace *cifs_ace)
+#ifdef CONFIG_FS_POSIX_ACL
+/**
+ * cifs_init_posix_acl - convert ACL from cifs to POSIX ACL format
+ * @ace: POSIX ACL entry to store converted ACL into
+ * @cifs_ace: ACL in cifs format
+ *
+ * Convert an Access Control Entry from wire format to local POSIX xattr
+ * format.
+ *
+ * Note that the @cifs_uid member is used to store both {g,u}id_t.
+ */
+static void cifs_init_posix_acl(struct posix_acl_entry *ace,
+                               struct cifs_posix_ace *cifs_ace)
 {
        /* u8 cifs fields do not need le conversion */
-       ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm);
-       ace->e_tag  = cpu_to_le16(cifs_ace->cifs_e_tag);
-       ace->e_id   = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid));
-/*
-       cifs_dbg(FYI, "perm %d tag %d id %d\n",
-                ace->e_perm, ace->e_tag, ace->e_id);
-*/
+       ace->e_perm = cifs_ace->cifs_e_perm;
+       ace->e_tag = cifs_ace->cifs_e_tag;
 
+       switch (ace->e_tag) {
+       case ACL_USER:
+               ace->e_uid = make_kuid(&init_user_ns,
+                                      le64_to_cpu(cifs_ace->cifs_uid));
+               break;
+       case ACL_GROUP:
+               ace->e_gid = make_kgid(&init_user_ns,
+                                      le64_to_cpu(cifs_ace->cifs_uid));
+               break;
+       }
        return;
 }
 
-/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
-static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
-                              const int acl_type, const int size_of_data_area)
+/**
+ * cifs_to_posix_acl - copy cifs ACL format to POSIX ACL format
+ * @acl: ACLs returned in POSIX ACL format
+ * @src: ACLs in cifs format
+ * @acl_type: type of POSIX ACL requested
+ * @size_of_data_area: size of SMB we got
+ *
+ * This function converts ACLs from cifs format to POSIX ACL format.
+ * If @acl is NULL then the size of the buffer required to store POSIX ACLs in
+ * their uapi format is returned.
+ */
+static int cifs_to_posix_acl(struct posix_acl **acl, char *src,
+                            const int acl_type, const int size_of_data_area)
 {
        int size =  0;
-       int i;
        __u16 count;
        struct cifs_posix_ace *pACE;
        struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
-       struct posix_acl_xattr_header *local_acl = (void *)trgt;
+       struct posix_acl *kacl = NULL;
+       struct posix_acl_entry *pa, *pe;
 
        if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
                return -EOPNOTSUPP;
@@ -2959,7 +2984,7 @@ static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
                count = le16_to_cpu(cifs_acl->access_entry_count);
                size = sizeof(struct cifs_posix_acl);
                size += sizeof(struct cifs_posix_ace) * count;
-/* skip past access ACEs to get to default ACEs */
+               /* skip past access ACEs to get to default ACEs */
                pACE = &cifs_acl->ace_array[count];
                count = le16_to_cpu(cifs_acl->default_entry_count);
                size += sizeof(struct cifs_posix_ace) * count;
@@ -2971,62 +2996,75 @@ static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
                return -EINVAL;
        }
 
-       size = posix_acl_xattr_size(count);
-       if ((buflen == 0) || (local_acl == NULL)) {
-               /* used to query ACL EA size */
-       } else if (size > buflen) {
-               return -ERANGE;
-       } else /* buffer big enough */ {
-               struct posix_acl_xattr_entry *ace = (void *)(local_acl + 1);
-
-               local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
-               for (i = 0; i < count ; i++) {
-                       cifs_convert_ace(&ace[i], pACE);
-                       pACE++;
-               }
+       /* Allocate number of POSIX ACLs to store in VFS format. */
+       kacl = posix_acl_alloc(count, GFP_NOFS);
+       if (!kacl)
+               return -ENOMEM;
+
+       FOREACH_ACL_ENTRY(pa, kacl, pe) {
+               cifs_init_posix_acl(pa, pACE);
+               pACE++;
        }
-       return size;
+
+       *acl = kacl;
+       return 0;
 }
 
-static void convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,
-                                    const struct posix_acl_xattr_entry *local_ace)
+/**
+ * cifs_init_ace - convert ACL entry from POSIX ACL to cifs format
+ * @cifs_ace: the cifs ACL entry to store into
+ * @local_ace: the POSIX ACL entry to convert
+ */
+static void cifs_init_ace(struct cifs_posix_ace *cifs_ace,
+                         const struct posix_acl_entry *local_ace)
 {
-       cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm);
-       cifs_ace->cifs_e_tag =  le16_to_cpu(local_ace->e_tag);
-       /* BB is there a better way to handle the large uid? */
-       if (local_ace->e_id == cpu_to_le32(-1)) {
-       /* Probably no need to le convert -1 on any arch but can not hurt */
+       cifs_ace->cifs_e_perm = local_ace->e_perm;
+       cifs_ace->cifs_e_tag =  local_ace->e_tag;
+
+       switch (local_ace->e_tag) {
+       case ACL_USER:
+               cifs_ace->cifs_uid =
+                       cpu_to_le64(from_kuid(&init_user_ns, local_ace->e_uid));
+               break;
+       case ACL_GROUP:
+               cifs_ace->cifs_uid =
+                       cpu_to_le64(from_kgid(&init_user_ns, local_ace->e_gid));
+               break;
+       default:
                cifs_ace->cifs_uid = cpu_to_le64(-1);
-       } else
-               cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id));
-/*
-       cifs_dbg(FYI, "perm %d tag %d id %d\n",
-                ace->e_perm, ace->e_tag, ace->e_id);
-*/
+       }
 }
 
-/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
-static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
-                              const int buflen, const int acl_type)
+/**
+ * posix_acl_to_cifs - convert ACLs from POSIX ACL to cifs format
+ * @parm_data: ACLs in cifs format to conver to
+ * @acl: ACLs in POSIX ACL format to convert from
+ * @acl_type: the type of POSIX ACLs stored in @acl
+ *
+ * Return: the number cifs ACL entries after conversion
+ */
+static __u16 posix_acl_to_cifs(char *parm_data, const struct posix_acl *acl,
+                              const int acl_type)
 {
        __u16 rc = 0;
        struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data;
-       struct posix_acl_xattr_header *local_acl = (void *)pACL;
-       struct posix_acl_xattr_entry *ace = (void *)(local_acl + 1);
+       const struct posix_acl_entry *pa, *pe;
        int count;
-       int i;
+       int i = 0;
 
-       if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
+       if ((acl == NULL) || (cifs_acl == NULL))
                return 0;
 
-       count = posix_acl_xattr_count((size_t)buflen);
-       cifs_dbg(FYI, "setting acl with %d entries from buf of length %d and version of %d\n",
-                count, buflen, le32_to_cpu(local_acl->a_version));
-       if (le32_to_cpu(local_acl->a_version) != 2) {
-               cifs_dbg(FYI, "unknown POSIX ACL version %d\n",
-                        le32_to_cpu(local_acl->a_version));
-               return 0;
-       }
+       count = acl->a_count;
+       cifs_dbg(FYI, "setting acl with %d entries\n", count);
+
+       /*
+        * Note that the uapi POSIX ACL version is verified by the VFS and is
+        * independent of the cifs ACL version. Changing the POSIX ACL version
+        * is a uapi change and if it's changed we will pass down the POSIX ACL
+        * version in struct posix_acl from the VFS. For now there's really
+        * only one that all filesystems know how to deal with.
+        */
        cifs_acl->version = cpu_to_le16(1);
        if (acl_type == ACL_TYPE_ACCESS) {
                cifs_acl->access_entry_count = cpu_to_le16(count);
@@ -3038,8 +3076,9 @@ static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
                cifs_dbg(FYI, "unknown ACL type %d\n", acl_type);
                return 0;
        }
-       for (i = 0; i < count; i++)
-               convert_ace_to_cifs_ace(&cifs_acl->ace_array[i], &ace[i]);
+       FOREACH_ACL_ENTRY(pa, acl, pe) {
+               cifs_init_ace(&cifs_acl->ace_array[i++], pa);
+       }
        if (rc == 0) {
                rc = (__u16)(count * sizeof(struct cifs_posix_ace));
                rc += sizeof(struct cifs_posix_acl);
@@ -3048,11 +3087,10 @@ static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
        return rc;
 }
 
-int
-CIFSSMBGetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,
-                  const unsigned char *searchName,
-                  char *acl_inf, const int buflen, const int acl_type,
-                  const struct nls_table *nls_codepage, int remap)
+int cifs_do_get_acl(const unsigned int xid, struct cifs_tcon *tcon,
+                   const unsigned char *searchName, struct posix_acl **acl,
+                   const int acl_type, const struct nls_table *nls_codepage,
+                   int remap)
 {
 /* SMB_QUERY_POSIX_ACL */
        TRANSACTION2_QPI_REQ *pSMB = NULL;
@@ -3124,23 +3162,26 @@ queryAclRetry:
                else {
                        __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
                        __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
-                       rc = cifs_copy_posix_acl(acl_inf,
+                       rc = cifs_to_posix_acl(acl,
                                (char *)&pSMBr->hdr.Protocol+data_offset,
-                               buflen, acl_type, count);
+                               acl_type, count);
                }
        }
        cifs_buf_release(pSMB);
+       /*
+        * The else branch after SendReceive() doesn't return EAGAIN so if we
+        * allocated @acl in cifs_to_posix_acl() we are guaranteed to return
+        * here and don't leak POSIX ACLs.
+        */
        if (rc == -EAGAIN)
                goto queryAclRetry;
        return rc;
 }
 
-int
-CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,
-                  const unsigned char *fileName,
-                  const char *local_acl, const int buflen,
-                  const int acl_type,
-                  const struct nls_table *nls_codepage, int remap)
+int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
+                   const unsigned char *fileName, const struct posix_acl *acl,
+                   const int acl_type, const struct nls_table *nls_codepage,
+                   int remap)
 {
        struct smb_com_transaction2_spi_req *pSMB = NULL;
        struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
@@ -3181,7 +3222,7 @@ setAclRetry:
        pSMB->ParameterOffset = cpu_to_le16(param_offset);
 
        /* convert to on the wire format for POSIX ACL */
-       data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type);
+       data_count = posix_acl_to_cifs(parm_data, acl, acl_type);
 
        if (data_count == 0) {
                rc = -EOPNOTSUPP;
@@ -3211,6 +3252,23 @@ setACLerrorExit:
                goto setAclRetry;
        return rc;
 }
+#else
+int cifs_do_get_acl(const unsigned int xid, struct cifs_tcon *tcon,
+                   const unsigned char *searchName, struct posix_acl **acl,
+                   const int acl_type, const struct nls_table *nls_codepage,
+                   int remap)
+{
+       return -EOPNOTSUPP;
+}
+
+int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
+                   const unsigned char *fileName, const struct posix_acl *acl,
+                   const int acl_type, const struct nls_table *nls_codepage,
+                   int remap)
+{
+       return -EOPNOTSUPP;
+}
+#endif /* CONFIG_FS_POSIX_ACL */
 
 int
 CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon,
index 998fa51..5f2fb2f 100644 (file)
@@ -200,32 +200,6 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
                }
                break;
        }
-
-#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
-       case XATTR_ACL_ACCESS:
-#ifdef CONFIG_CIFS_POSIX
-               if (!value)
-                       goto out;
-               if (sb->s_flags & SB_POSIXACL)
-                       rc = CIFSSMBSetPosixACL(xid, pTcon, full_path,
-                               value, (const int)size,
-                               ACL_TYPE_ACCESS, cifs_sb->local_nls,
-                               cifs_remap(cifs_sb));
-#endif  /* CONFIG_CIFS_POSIX */
-               break;
-
-       case XATTR_ACL_DEFAULT:
-#ifdef CONFIG_CIFS_POSIX
-               if (!value)
-                       goto out;
-               if (sb->s_flags & SB_POSIXACL)
-                       rc = CIFSSMBSetPosixACL(xid, pTcon, full_path,
-                               value, (const int)size,
-                               ACL_TYPE_DEFAULT, cifs_sb->local_nls,
-                               cifs_remap(cifs_sb));
-#endif  /* CONFIG_CIFS_POSIX */
-               break;
-#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
        }
 
 out:
@@ -366,27 +340,6 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
                }
                break;
        }
-#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
-       case XATTR_ACL_ACCESS:
-#ifdef CONFIG_CIFS_POSIX
-               if (sb->s_flags & SB_POSIXACL)
-                       rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
-                               value, size, ACL_TYPE_ACCESS,
-                               cifs_sb->local_nls,
-                               cifs_remap(cifs_sb));
-#endif  /* CONFIG_CIFS_POSIX */
-               break;
-
-       case XATTR_ACL_DEFAULT:
-#ifdef CONFIG_CIFS_POSIX
-               if (sb->s_flags & SB_POSIXACL)
-                       rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
-                               value, size, ACL_TYPE_DEFAULT,
-                               cifs_sb->local_nls,
-                               cifs_remap(cifs_sb));
-#endif  /* CONFIG_CIFS_POSIX */
-               break;
-#endif /* ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
        }
 
        /* We could add an additional check for streams ie
@@ -525,21 +478,6 @@ static const struct xattr_handler smb3_ntsd_full_xattr_handler = {
        .set = cifs_xattr_set,
 };
 
-
-static const struct xattr_handler cifs_posix_acl_access_xattr_handler = {
-       .name = XATTR_NAME_POSIX_ACL_ACCESS,
-       .flags = XATTR_ACL_ACCESS,
-       .get = cifs_xattr_get,
-       .set = cifs_xattr_set,
-};
-
-static const struct xattr_handler cifs_posix_acl_default_xattr_handler = {
-       .name = XATTR_NAME_POSIX_ACL_DEFAULT,
-       .flags = XATTR_ACL_DEFAULT,
-       .get = cifs_xattr_get,
-       .set = cifs_xattr_set,
-};
-
 const struct xattr_handler *cifs_xattr_handlers[] = {
        &cifs_user_xattr_handler,
        &cifs_os2_xattr_handler,
@@ -549,7 +487,9 @@ const struct xattr_handler *cifs_xattr_handlers[] = {
        &smb3_ntsd_xattr_handler, /* alias for above since avoiding "cifs" */
        &cifs_cifs_ntsd_full_xattr_handler,
        &smb3_ntsd_full_xattr_handler, /* alias for above since avoiding "cifs" */
-       &cifs_posix_acl_access_xattr_handler,
-       &cifs_posix_acl_default_xattr_handler,
+#ifdef CONFIG_FS_POSIX_ACL
+       &posix_acl_access_xattr_handler,
+       &posix_acl_default_xattr_handler,
+#endif
        NULL
 };
index c214fe0..f3cd00f 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/fs_stack.h>
 #include <linux/slab.h>
 #include <linux/xattr.h>
+#include <linux/posix_acl.h>
+#include <linux/posix_acl_xattr.h>
 #include <linux/fileattr.h>
 #include <asm/unaligned.h>
 #include "ecryptfs_kernel.h"
@@ -1120,6 +1122,28 @@ static int ecryptfs_fileattr_set(struct user_namespace *mnt_userns,
        return rc;
 }
 
+static struct posix_acl *ecryptfs_get_acl(struct user_namespace *mnt_userns,
+                                         struct dentry *dentry, int type)
+{
+       return vfs_get_acl(mnt_userns, ecryptfs_dentry_to_lower(dentry),
+                          posix_acl_xattr_name(type));
+}
+
+static int ecryptfs_set_acl(struct user_namespace *mnt_userns,
+                           struct dentry *dentry, struct posix_acl *acl,
+                           int type)
+{
+       int rc;
+       struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
+       struct inode *lower_inode = d_inode(lower_dentry);
+
+       rc = vfs_set_acl(&init_user_ns, lower_dentry,
+                        posix_acl_xattr_name(type), acl);
+       if (!rc)
+               fsstack_copy_attr_all(d_inode(dentry), lower_inode);
+       return rc;
+}
+
 const struct inode_operations ecryptfs_symlink_iops = {
        .get_link = ecryptfs_get_link,
        .permission = ecryptfs_permission,
@@ -1143,6 +1167,8 @@ const struct inode_operations ecryptfs_dir_iops = {
        .listxattr = ecryptfs_listxattr,
        .fileattr_get = ecryptfs_fileattr_get,
        .fileattr_set = ecryptfs_fileattr_set,
+       .get_acl = ecryptfs_get_acl,
+       .set_acl = ecryptfs_set_acl,
 };
 
 const struct inode_operations ecryptfs_main_iops = {
@@ -1152,6 +1178,8 @@ const struct inode_operations ecryptfs_main_iops = {
        .listxattr = ecryptfs_listxattr,
        .fileattr_get = ecryptfs_fileattr_get,
        .fileattr_set = ecryptfs_fileattr_set,
+       .get_acl = ecryptfs_get_acl,
+       .set_acl = ecryptfs_set_acl,
 };
 
 static int ecryptfs_xattr_get(const struct xattr_handler *handler,
@@ -1182,6 +1210,10 @@ static const struct xattr_handler ecryptfs_xattr_handler = {
 };
 
 const struct xattr_handler *ecryptfs_xattr_handlers[] = {
+#ifdef CONFIG_FS_POSIX_ACL
+       &posix_acl_access_xattr_handler,
+       &posix_acl_default_xattr_handler,
+#endif
        &ecryptfs_xattr_handler,
        NULL
 };
index ad2a82f..2d57134 100644 (file)
@@ -371,7 +371,7 @@ int erofs_getattr(struct user_namespace *mnt_userns, const struct path *path,
 const struct inode_operations erofs_generic_iops = {
        .getattr = erofs_getattr,
        .listxattr = erofs_listxattr,
-       .get_acl = erofs_get_acl,
+       .get_inode_acl = erofs_get_acl,
        .fiemap = erofs_fiemap,
 };
 
@@ -379,12 +379,12 @@ const struct inode_operations erofs_symlink_iops = {
        .get_link = page_get_link,
        .getattr = erofs_getattr,
        .listxattr = erofs_listxattr,
-       .get_acl = erofs_get_acl,
+       .get_inode_acl = erofs_get_acl,
 };
 
 const struct inode_operations erofs_fast_symlink_iops = {
        .get_link = simple_get_link,
        .getattr = erofs_getattr,
        .listxattr = erofs_listxattr,
-       .get_acl = erofs_get_acl,
+       .get_inode_acl = erofs_get_acl,
 };
index 0dc3472..b64a108 100644 (file)
@@ -228,6 +228,6 @@ const struct inode_operations erofs_dir_iops = {
        .lookup = erofs_lookup,
        .getattr = erofs_getattr,
        .listxattr = erofs_listxattr,
-       .get_acl = erofs_get_acl,
+       .get_inode_acl = erofs_get_acl,
        .fiemap = erofs_fiemap,
 };
index bf29896..440d5f1 100644 (file)
@@ -219,11 +219,12 @@ __ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
  * inode->i_mutex: down
  */
 int
-ext2_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+ext2_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
             struct posix_acl *acl, int type)
 {
        int error;
        int update_mode = 0;
+       struct inode *inode = d_inode(dentry);
        umode_t mode = inode->i_mode;
 
        if (type == ACL_TYPE_ACCESS && acl) {
index 925ab62..3841bec 100644 (file)
@@ -56,7 +56,7 @@ static inline int ext2_acl_count(size_t size)
 
 /* acl.c */
 extern struct posix_acl *ext2_get_acl(struct inode *inode, int type, bool rcu);
-extern int ext2_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+extern int ext2_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                        struct posix_acl *acl, int type);
 extern int ext2_init_acl (struct inode *, struct inode *);
 
index eb97aa3..6b4bebe 100644 (file)
@@ -200,7 +200,7 @@ const struct inode_operations ext2_file_inode_operations = {
        .listxattr      = ext2_listxattr,
        .getattr        = ext2_getattr,
        .setattr        = ext2_setattr,
-       .get_acl        = ext2_get_acl,
+       .get_inode_acl  = ext2_get_acl,
        .set_acl        = ext2_set_acl,
        .fiemap         = ext2_fiemap,
        .fileattr_get   = ext2_fileattr_get,
index 918ab2f..e97e77b 100644 (file)
@@ -1652,7 +1652,7 @@ int ext2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
        }
        setattr_copy(&init_user_ns, inode, iattr);
        if (iattr->ia_valid & ATTR_MODE)
-               error = posix_acl_chmod(&init_user_ns, inode, inode->i_mode);
+               error = posix_acl_chmod(&init_user_ns, dentry, inode->i_mode);
        mark_inode_dirty(inode);
 
        return error;
index 9125eab..c056957 100644 (file)
@@ -427,7 +427,7 @@ const struct inode_operations ext2_dir_inode_operations = {
        .listxattr      = ext2_listxattr,
        .getattr        = ext2_getattr,
        .setattr        = ext2_setattr,
-       .get_acl        = ext2_get_acl,
+       .get_inode_acl  = ext2_get_acl,
        .set_acl        = ext2_set_acl,
        .tmpfile        = ext2_tmpfile,
        .fileattr_get   = ext2_fileattr_get,
@@ -438,6 +438,6 @@ const struct inode_operations ext2_special_inode_operations = {
        .listxattr      = ext2_listxattr,
        .getattr        = ext2_getattr,
        .setattr        = ext2_setattr,
-       .get_acl        = ext2_get_acl,
+       .get_inode_acl  = ext2_get_acl,
        .set_acl        = ext2_set_acl,
 };
index 57e82e2..a9f8953 100644 (file)
@@ -225,12 +225,13 @@ __ext4_set_acl(handle_t *handle, struct inode *inode, int type,
 }
 
 int
-ext4_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+ext4_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
             struct posix_acl *acl, int type)
 {
        handle_t *handle;
        int error, credits, retries = 0;
        size_t acl_size = acl ? ext4_acl_size(acl->a_count) : 0;
+       struct inode *inode = d_inode(dentry);
        umode_t mode = inode->i_mode;
        int update_mode = 0;
 
index 3219669..09c4a8a 100644 (file)
@@ -56,7 +56,7 @@ static inline int ext4_acl_count(size_t size)
 
 /* acl.c */
 struct posix_acl *ext4_get_acl(struct inode *inode, int type, bool rcu);
-int ext4_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int ext4_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                 struct posix_acl *acl, int type);
 extern int ext4_init_acl(handle_t *, struct inode *, struct inode *);
 
index a7a597c..7ac0a81 100644 (file)
@@ -955,7 +955,7 @@ const struct inode_operations ext4_file_inode_operations = {
        .setattr        = ext4_setattr,
        .getattr        = ext4_file_getattr,
        .listxattr      = ext4_listxattr,
-       .get_acl        = ext4_get_acl,
+       .get_inode_acl  = ext4_get_acl,
        .set_acl        = ext4_set_acl,
        .fiemap         = ext4_fiemap,
        .fileattr_get   = ext4_fileattr_get,
index 9fc1af8..9aa8b18 100644 (file)
@@ -870,7 +870,7 @@ static int ext4_xattr_credits_for_new_inode(struct inode *dir, mode_t mode,
        struct super_block *sb = dir->i_sb;
        int nblocks = 0;
 #ifdef CONFIG_EXT4_FS_POSIX_ACL
-       struct posix_acl *p = get_acl(dir, ACL_TYPE_DEFAULT);
+       struct posix_acl *p = get_inode_acl(dir, ACL_TYPE_DEFAULT);
 
        if (IS_ERR(p))
                return PTR_ERR(p);
index 2b5ef1b..a8e12ce 100644 (file)
@@ -5550,7 +5550,7 @@ out_mmap_sem:
                ext4_orphan_del(NULL, inode);
 
        if (!error && (ia_valid & ATTR_MODE))
-               rc = posix_acl_chmod(mnt_userns, inode, inode->i_mode);
+               rc = posix_acl_chmod(mnt_userns, dentry, inode->i_mode);
 
 err_out:
        if  (error)
index c08c0ab..27a863e 100644 (file)
@@ -4194,7 +4194,7 @@ const struct inode_operations ext4_dir_inode_operations = {
        .setattr        = ext4_setattr,
        .getattr        = ext4_getattr,
        .listxattr      = ext4_listxattr,
-       .get_acl        = ext4_get_acl,
+       .get_inode_acl  = ext4_get_acl,
        .set_acl        = ext4_set_acl,
        .fiemap         = ext4_fiemap,
        .fileattr_get   = ext4_fileattr_get,
@@ -4205,6 +4205,6 @@ const struct inode_operations ext4_special_inode_operations = {
        .setattr        = ext4_setattr,
        .getattr        = ext4_getattr,
        .listxattr      = ext4_listxattr,
-       .get_acl        = ext4_get_acl,
+       .get_inode_acl  = ext4_get_acl,
        .set_acl        = ext4_set_acl,
 };
index 5bbc44a..c1c74aa 100644 (file)
@@ -276,9 +276,11 @@ static int __f2fs_set_acl(struct user_namespace *mnt_userns,
        return error;
 }
 
-int f2fs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int f2fs_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                 struct posix_acl *acl, int type)
 {
+       struct inode *inode = d_inode(dentry);
+
        if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
                return -EIO;
 
index a26e33c..ea2bbb3 100644 (file)
@@ -34,7 +34,7 @@ struct f2fs_acl_header {
 #ifdef CONFIG_F2FS_FS_POSIX_ACL
 
 extern struct posix_acl *f2fs_get_acl(struct inode *, int, bool);
-extern int f2fs_set_acl(struct user_namespace *, struct inode *,
+extern int f2fs_set_acl(struct user_namespace *, struct dentry *,
                        struct posix_acl *, int);
 extern int f2fs_init_acl(struct inode *, struct inode *, struct page *,
                                                        struct page *);
index 82cda12..83df6f6 100644 (file)
@@ -1025,7 +1025,7 @@ int f2fs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
        __setattr_copy(mnt_userns, inode, attr);
 
        if (attr->ia_valid & ATTR_MODE) {
-               err = posix_acl_chmod(mnt_userns, inode, f2fs_get_inode_mode(inode));
+               err = posix_acl_chmod(mnt_userns, dentry, f2fs_get_inode_mode(inode));
 
                if (is_inode_flag_set(inode, FI_ACL_MODE)) {
                        if (!err)
@@ -1046,7 +1046,7 @@ int f2fs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
 const struct inode_operations f2fs_file_inode_operations = {
        .getattr        = f2fs_getattr,
        .setattr        = f2fs_setattr,
-       .get_acl        = f2fs_get_acl,
+       .get_inode_acl  = f2fs_get_acl,
        .set_acl        = f2fs_set_acl,
        .listxattr      = f2fs_listxattr,
        .fiemap         = f2fs_fiemap,
index a389772..c227113 100644 (file)
@@ -1379,7 +1379,7 @@ const struct inode_operations f2fs_dir_inode_operations = {
        .tmpfile        = f2fs_tmpfile,
        .getattr        = f2fs_getattr,
        .setattr        = f2fs_setattr,
-       .get_acl        = f2fs_get_acl,
+       .get_inode_acl  = f2fs_get_acl,
        .set_acl        = f2fs_set_acl,
        .listxattr      = f2fs_listxattr,
        .fiemap         = f2fs_fiemap,
@@ -1397,7 +1397,7 @@ const struct inode_operations f2fs_symlink_inode_operations = {
 const struct inode_operations f2fs_special_inode_operations = {
        .getattr        = f2fs_getattr,
        .setattr        = f2fs_setattr,
-       .get_acl        = f2fs_get_acl,
+       .get_inode_acl  = f2fs_get_acl,
        .set_acl        = f2fs_set_acl,
        .listxattr      = f2fs_listxattr,
 };
index 337cb29..8edd0f3 100644 (file)
@@ -53,9 +53,10 @@ struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu)
        return acl;
 }
 
-int fuse_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                 struct posix_acl *acl, int type)
 {
+       struct inode *inode = d_inode(dentry);
        struct fuse_conn *fc = get_fuse_conn(inode);
        const char *name;
        int ret;
index bb97a38..25e6b0f 100644 (file)
@@ -1935,7 +1935,7 @@ static const struct inode_operations fuse_dir_inode_operations = {
        .permission     = fuse_permission,
        .getattr        = fuse_getattr,
        .listxattr      = fuse_listxattr,
-       .get_acl        = fuse_get_acl,
+       .get_inode_acl  = fuse_get_acl,
        .set_acl        = fuse_set_acl,
        .fileattr_get   = fuse_fileattr_get,
        .fileattr_set   = fuse_fileattr_set,
@@ -1957,7 +1957,7 @@ static const struct inode_operations fuse_common_inode_operations = {
        .permission     = fuse_permission,
        .getattr        = fuse_getattr,
        .listxattr      = fuse_listxattr,
-       .get_acl        = fuse_get_acl,
+       .get_inode_acl  = fuse_get_acl,
        .set_acl        = fuse_set_acl,
        .fileattr_get   = fuse_fileattr_get,
        .fileattr_set   = fuse_fileattr_set,
index 98a9cf5..26a7c52 100644 (file)
@@ -1269,7 +1269,7 @@ extern const struct xattr_handler *fuse_no_acl_xattr_handlers[];
 
 struct posix_acl;
 struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu);
-int fuse_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                 struct posix_acl *acl, int type);
 
 /* readdir.c */
index 734d1f0..3dcde49 100644 (file)
@@ -109,9 +109,10 @@ out:
        return error;
 }
 
-int gfs2_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int gfs2_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                 struct posix_acl *acl, int type)
 {
+       struct inode *inode = d_inode(dentry);
        struct gfs2_inode *ip = GFS2_I(inode);
        struct gfs2_holder gh;
        bool need_unlock = false;
index cd180ca..b8de8c1 100644 (file)
@@ -13,7 +13,7 @@
 
 extern struct posix_acl *gfs2_get_acl(struct inode *inode, int type, bool rcu);
 extern int __gfs2_set_acl(struct inode *inode, struct posix_acl *acl, int type);
-extern int gfs2_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+extern int gfs2_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                        struct posix_acl *acl, int type);
 
 #endif /* __ACL_DOT_H__ */
index 04a2015..1371e06 100644 (file)
@@ -1997,7 +1997,7 @@ static int gfs2_setattr(struct user_namespace *mnt_userns,
        else {
                error = gfs2_setattr_simple(inode, attr);
                if (!error && attr->ia_valid & ATTR_MODE)
-                       error = posix_acl_chmod(&init_user_ns, inode,
+                       error = posix_acl_chmod(&init_user_ns, dentry,
                                                inode->i_mode);
        }
 
@@ -2149,7 +2149,7 @@ static const struct inode_operations gfs2_file_iops = {
        .getattr = gfs2_getattr,
        .listxattr = gfs2_listxattr,
        .fiemap = gfs2_fiemap,
-       .get_acl = gfs2_get_acl,
+       .get_inode_acl = gfs2_get_acl,
        .set_acl = gfs2_set_acl,
        .update_time = gfs2_update_time,
        .fileattr_get = gfs2_fileattr_get,
@@ -2171,7 +2171,7 @@ static const struct inode_operations gfs2_dir_iops = {
        .getattr = gfs2_getattr,
        .listxattr = gfs2_listxattr,
        .fiemap = gfs2_fiemap,
-       .get_acl = gfs2_get_acl,
+       .get_inode_acl = gfs2_get_acl,
        .set_acl = gfs2_set_acl,
        .update_time = gfs2_update_time,
        .atomic_open = gfs2_atomic_open,
index 6f0386b..e377eb7 100644 (file)
@@ -232,5 +232,26 @@ ssize_t do_getxattr(struct user_namespace *mnt_userns,
 int setxattr_copy(const char __user *name, struct xattr_ctx *ctx);
 int do_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
                struct xattr_ctx *ctx);
+int may_write_xattr(struct user_namespace *mnt_userns, struct inode *inode);
+
+#ifdef CONFIG_FS_POSIX_ACL
+int do_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+              const char *acl_name, const void *kvalue, size_t size);
+ssize_t do_get_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+                  const char *acl_name, void *kvalue, size_t size);
+#else
+static inline int do_set_acl(struct user_namespace *mnt_userns,
+                            struct dentry *dentry, const char *acl_name,
+                            const void *kvalue, size_t size)
+{
+       return -EOPNOTSUPP;
+}
+static inline ssize_t do_get_acl(struct user_namespace *mnt_userns,
+                                struct dentry *dentry, const char *acl_name,
+                                void *kvalue, size_t size)
+{
+       return -EOPNOTSUPP;
+}
+#endif
 
 ssize_t __kernel_write_iter(struct file *file, struct iov_iter *from, loff_t *pos);
index e945e34..8bb58ce 100644 (file)
@@ -229,10 +229,11 @@ static int __jffs2_set_acl(struct inode *inode, int xprefix, struct posix_acl *a
        return rc;
 }
 
-int jffs2_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int jffs2_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                  struct posix_acl *acl, int type)
 {
        int rc, xprefix;
+       struct inode *inode = d_inode(dentry);
 
        switch (type) {
        case ACL_TYPE_ACCESS:
index 9d9fb7c..ca36a6e 100644 (file)
@@ -28,7 +28,7 @@ struct jffs2_acl_header {
 #ifdef CONFIG_JFFS2_FS_POSIX_ACL
 
 struct posix_acl *jffs2_get_acl(struct inode *inode, int type, bool rcu);
-int jffs2_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int jffs2_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                  struct posix_acl *acl, int type);
 extern int jffs2_init_acl_pre(struct inode *, struct inode *, umode_t *);
 extern int jffs2_init_acl_post(struct inode *);
index c0aabbc..f399b39 100644 (file)
@@ -62,7 +62,7 @@ const struct inode_operations jffs2_dir_inode_operations =
        .rmdir =        jffs2_rmdir,
        .mknod =        jffs2_mknod,
        .rename =       jffs2_rename,
-       .get_acl =      jffs2_get_acl,
+       .get_inode_acl =        jffs2_get_acl,
        .set_acl =      jffs2_set_acl,
        .setattr =      jffs2_setattr,
        .listxattr =    jffs2_listxattr,
index ba86acb..3cf71be 100644 (file)
@@ -64,7 +64,7 @@ const struct file_operations jffs2_file_operations =
 
 const struct inode_operations jffs2_file_inode_operations =
 {
-       .get_acl =      jffs2_get_acl,
+       .get_inode_acl =        jffs2_get_acl,
        .set_acl =      jffs2_set_acl,
        .setattr =      jffs2_setattr,
        .listxattr =    jffs2_listxattr,
index 39cec28..66af51c 100644 (file)
@@ -202,7 +202,7 @@ int jffs2_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
 
        rc = jffs2_do_setattr(inode, iattr);
        if (!rc && (iattr->ia_valid & ATTR_MODE))
-               rc = posix_acl_chmod(&init_user_ns, inode, inode->i_mode);
+               rc = posix_acl_chmod(&init_user_ns, dentry, inode->i_mode);
 
        return rc;
 }
index a653f34..3b667ec 100644 (file)
@@ -94,12 +94,13 @@ out:
        return rc;
 }
 
-int jfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int jfs_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                struct posix_acl *acl, int type)
 {
        int rc;
        tid_t tid;
        int update_mode = 0;
+       struct inode *inode = d_inode(dentry);
        umode_t mode = inode->i_mode;
 
        tid = txBegin(inode->i_sb, 0);
index 332dc9a..8866346 100644 (file)
@@ -123,7 +123,7 @@ int jfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
        mark_inode_dirty(inode);
 
        if (iattr->ia_valid & ATTR_MODE)
-               rc = posix_acl_chmod(&init_user_ns, inode, inode->i_mode);
+               rc = posix_acl_chmod(&init_user_ns, dentry, inode->i_mode);
        return rc;
 }
 
@@ -133,7 +133,7 @@ const struct inode_operations jfs_file_inode_operations = {
        .fileattr_get   = jfs_fileattr_get,
        .fileattr_set   = jfs_fileattr_set,
 #ifdef CONFIG_JFS_POSIX_ACL
-       .get_acl        = jfs_get_acl,
+       .get_inode_acl  = jfs_get_acl,
        .set_acl        = jfs_set_acl,
 #endif
 };
index 3de4028..f0704a2 100644 (file)
@@ -8,7 +8,7 @@
 #ifdef CONFIG_JFS_POSIX_ACL
 
 struct posix_acl *jfs_get_acl(struct inode *inode, int type, bool rcu);
-int jfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int jfs_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                struct posix_acl *acl, int type);
 int jfs_init_acl(tid_t, struct inode *, struct inode *);
 
index 9db4f57..b50afaf 100644 (file)
@@ -1525,7 +1525,7 @@ const struct inode_operations jfs_dir_inode_operations = {
        .fileattr_get   = jfs_fileattr_get,
        .fileattr_set   = jfs_fileattr_set,
 #ifdef CONFIG_JFS_POSIX_ACL
-       .get_acl        = jfs_get_acl,
+       .get_inode_acl  = jfs_get_acl,
        .set_acl        = jfs_set_acl,
 #endif
 };
index b2fc85d..9306e10 100644 (file)
@@ -2487,9 +2487,9 @@ static void ksmbd_acls_fattr(struct smb_fattr *fattr,
        fattr->cf_dacls = NULL;
 
        if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) {
-               fattr->cf_acls = get_acl(inode, ACL_TYPE_ACCESS);
+               fattr->cf_acls = get_inode_acl(inode, ACL_TYPE_ACCESS);
                if (S_ISDIR(inode->i_mode))
-                       fattr->cf_dacls = get_acl(inode, ACL_TYPE_DEFAULT);
+                       fattr->cf_dacls = get_inode_acl(inode, ACL_TYPE_DEFAULT);
        }
 }
 
@@ -2956,7 +2956,7 @@ int smb2_open(struct ksmbd_work *work)
                struct inode *inode = d_inode(path.dentry);
 
                posix_acl_rc = ksmbd_vfs_inherit_posix_acl(user_ns,
-                                                          inode,
+                                                          path.dentry,
                                                           d_inode(path.dentry->d_parent));
                if (posix_acl_rc)
                        ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc);
@@ -2972,7 +2972,7 @@ int smb2_open(struct ksmbd_work *work)
                        if (rc) {
                                if (posix_acl_rc)
                                        ksmbd_vfs_set_init_posix_acl(user_ns,
-                                                                    inode);
+                                                                    path.dentry);
 
                                if (test_share_config_flag(work->tcon->share_conf,
                                                           KSMBD_SHARE_FLAG_ACL_XATTR)) {
index b05ff9b..ab5c68c 100644 (file)
@@ -1289,7 +1289,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
        }
 
        if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) {
-               posix_acls = get_acl(d_inode(path->dentry), ACL_TYPE_ACCESS);
+               posix_acls = get_inode_acl(d_inode(path->dentry), ACL_TYPE_ACCESS);
                if (posix_acls && !found) {
                        unsigned int id = -1;
 
@@ -1386,14 +1386,14 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
        ksmbd_vfs_remove_acl_xattrs(user_ns, path->dentry);
        /* Update posix acls */
        if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && fattr.cf_dacls) {
-               rc = set_posix_acl(user_ns, inode,
+               rc = set_posix_acl(user_ns, path->dentry,
                                   ACL_TYPE_ACCESS, fattr.cf_acls);
                if (rc < 0)
                        ksmbd_debug(SMB,
                                    "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n",
                                    rc);
                if (S_ISDIR(inode->i_mode) && fattr.cf_dacls) {
-                       rc = set_posix_acl(user_ns, inode,
+                       rc = set_posix_acl(user_ns, path->dentry,
                                           ACL_TYPE_DEFAULT, fattr.cf_dacls);
                        if (rc)
                                ksmbd_debug(SMB,
index 72e981c..ff0e7a4 100644 (file)
@@ -1321,7 +1321,7 @@ int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns,
                             sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) ||
                    !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT,
                             sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) {
-                       err = ksmbd_vfs_remove_xattr(user_ns, dentry, name);
+                       err = vfs_remove_acl(user_ns, dentry, name);
                        if (err)
                                ksmbd_debug(SMB,
                                            "remove acl xattr failed : %s\n", name);
@@ -1375,7 +1375,7 @@ static struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct user_namespac
        if (!IS_ENABLED(CONFIG_FS_POSIX_ACL))
                return NULL;
 
-       posix_acls = get_acl(inode, acl_type);
+       posix_acls = get_inode_acl(inode, acl_type);
        if (!posix_acls)
                return NULL;
 
@@ -1824,10 +1824,11 @@ void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock)
 }
 
 int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns,
-                                struct inode *inode)
+                                struct dentry *dentry)
 {
        struct posix_acl_state acl_state;
        struct posix_acl *acls;
+       struct inode *inode = d_inode(dentry);
        int rc;
 
        if (!IS_ENABLED(CONFIG_FS_POSIX_ACL))
@@ -1856,14 +1857,13 @@ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns,
                return -ENOMEM;
        }
        posix_state_to_acl(&acl_state, acls->a_entries);
-       rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls);
+       rc = set_posix_acl(user_ns, dentry, ACL_TYPE_ACCESS, acls);
        if (rc < 0)
                ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n",
                            rc);
        else if (S_ISDIR(inode->i_mode)) {
                posix_state_to_acl(&acl_state, acls->a_entries);
-               rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT,
-                                  acls);
+               rc = set_posix_acl(user_ns, dentry, ACL_TYPE_DEFAULT, acls);
                if (rc < 0)
                        ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n",
                                    rc);
@@ -1874,16 +1874,17 @@ int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns,
 }
 
 int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns,
-                               struct inode *inode, struct inode *parent_inode)
+                               struct dentry *dentry, struct inode *parent_inode)
 {
        struct posix_acl *acls;
        struct posix_acl_entry *pace;
+       struct inode *inode = d_inode(dentry);
        int rc, i;
 
        if (!IS_ENABLED(CONFIG_FS_POSIX_ACL))
                return -EOPNOTSUPP;
 
-       acls = get_acl(parent_inode, ACL_TYPE_DEFAULT);
+       acls = get_inode_acl(parent_inode, ACL_TYPE_DEFAULT);
        if (!acls)
                return -ENOENT;
        pace = acls->a_entries;
@@ -1895,12 +1896,12 @@ int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns,
                }
        }
 
-       rc = set_posix_acl(user_ns, inode, ACL_TYPE_ACCESS, acls);
+       rc = set_posix_acl(user_ns, dentry, ACL_TYPE_ACCESS, acls);
        if (rc < 0)
                ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n",
                            rc);
        if (S_ISDIR(inode->i_mode)) {
-               rc = set_posix_acl(user_ns, inode, ACL_TYPE_DEFAULT,
+               rc = set_posix_acl(user_ns, dentry, ACL_TYPE_DEFAULT,
                                   acls);
                if (rc < 0)
                        ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n",
index 593059c..0d73d73 100644 (file)
@@ -160,8 +160,8 @@ int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns,
                                   struct dentry *dentry,
                                   struct xattr_dos_attrib *da);
 int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns,
-                                struct inode *inode);
+                                struct dentry *dentry);
 int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns,
-                               struct inode *inode,
+                               struct dentry *dentry,
                                struct inode *parent_inode);
 #endif /* __KSMBD_VFS_H__ */
index 9155ecb..7bfebfa 100644 (file)
@@ -297,13 +297,13 @@ static int check_acl(struct user_namespace *mnt_userns,
                acl = get_cached_acl_rcu(inode, ACL_TYPE_ACCESS);
                if (!acl)
                        return -EAGAIN;
-               /* no ->get_acl() calls in RCU mode... */
+               /* no ->get_inode_acl() calls in RCU mode... */
                if (is_uncached_acl(acl))
                        return -ECHILD;
                return posix_acl_permission(mnt_userns, inode, acl, mask);
        }
 
-       acl = get_acl(inode, ACL_TYPE_ACCESS);
+       acl = get_inode_acl(inode, ACL_TYPE_ACCESS);
        if (IS_ERR(acl))
                return PTR_ERR(acl);
        if (acl) {
index 03a4e67..df9ca56 100644 (file)
@@ -12,7 +12,7 @@
  */
 #ifdef CONFIG_NFS_V3_ACL
 extern struct posix_acl *nfs3_get_acl(struct inode *inode, int type, bool rcu);
-extern int nfs3_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+extern int nfs3_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                        struct posix_acl *acl, int type);
 extern int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
                struct posix_acl *dfacl);
index 93de0b5..74d11e3 100644 (file)
@@ -255,23 +255,24 @@ int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
 
 }
 
-int nfs3_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int nfs3_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                 struct posix_acl *acl, int type)
 {
        struct posix_acl *orig = acl, *dfacl = NULL, *alloc;
+       struct inode *inode = d_inode(dentry);
        int status;
 
        if (S_ISDIR(inode->i_mode)) {
                switch(type) {
                case ACL_TYPE_ACCESS:
-                       alloc = get_acl(inode, ACL_TYPE_DEFAULT);
+                       alloc = get_inode_acl(inode, ACL_TYPE_DEFAULT);
                        if (IS_ERR(alloc))
                                goto fail;
                        dfacl = alloc;
                        break;
 
                case ACL_TYPE_DEFAULT:
-                       alloc = get_acl(inode, ACL_TYPE_ACCESS);
+                       alloc = get_inode_acl(inode, ACL_TYPE_ACCESS);
                        if (IS_ERR(alloc))
                                goto fail;
                        dfacl = acl;
@@ -312,7 +313,7 @@ nfs3_list_one_acl(struct inode *inode, int type, const char *name, void *data,
        struct posix_acl *acl;
        char *p = data + *result;
 
-       acl = get_acl(inode, type);
+       acl = get_inode_acl(inode, type);
        if (IS_ERR_OR_NULL(acl))
                return 0;
 
index 2e75796..4bf208a 100644 (file)
@@ -998,7 +998,7 @@ static const struct inode_operations nfs3_dir_inode_operations = {
        .setattr        = nfs_setattr,
 #ifdef CONFIG_NFS_V3_ACL
        .listxattr      = nfs3_listxattr,
-       .get_acl        = nfs3_get_acl,
+       .get_inode_acl  = nfs3_get_acl,
        .set_acl        = nfs3_set_acl,
 #endif
 };
@@ -1009,7 +1009,7 @@ static const struct inode_operations nfs3_file_inode_operations = {
        .setattr        = nfs_setattr,
 #ifdef CONFIG_NFS_V3_ACL
        .listxattr      = nfs3_listxattr,
-       .get_acl        = nfs3_get_acl,
+       .get_inode_acl  = nfs3_get_acl,
        .set_acl        = nfs3_set_acl,
 #endif
 };
index 13e6e68..c43c25a 100644 (file)
@@ -55,7 +55,7 @@ static __be32 nfsacld_proc_getacl(struct svc_rqst *rqstp)
                goto out;
 
        if (resp->mask & (NFS_ACL|NFS_ACLCNT)) {
-               acl = get_acl(inode, ACL_TYPE_ACCESS);
+               acl = get_inode_acl(inode, ACL_TYPE_ACCESS);
                if (acl == NULL) {
                        /* Solaris returns the inode's minimum ACL. */
                        acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
@@ -69,7 +69,7 @@ static __be32 nfsacld_proc_getacl(struct svc_rqst *rqstp)
        if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) {
                /* Check how Solaris handles requests for the Default ACL
                   of a non-directory! */
-               acl = get_acl(inode, ACL_TYPE_DEFAULT);
+               acl = get_inode_acl(inode, ACL_TYPE_DEFAULT);
                if (IS_ERR(acl)) {
                        resp->status = nfserrno(PTR_ERR(acl));
                        goto fail;
@@ -113,11 +113,11 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp)
 
        inode_lock(inode);
 
-       error = set_posix_acl(&init_user_ns, inode, ACL_TYPE_ACCESS,
+       error = set_posix_acl(&init_user_ns, fh->fh_dentry, ACL_TYPE_ACCESS,
                              argp->acl_access);
        if (error)
                goto out_drop_lock;
-       error = set_posix_acl(&init_user_ns, inode, ACL_TYPE_DEFAULT,
+       error = set_posix_acl(&init_user_ns, fh->fh_dentry, ACL_TYPE_DEFAULT,
                              argp->acl_default);
        if (error)
                goto out_drop_lock;
index 2fb9ee3..9daa621 100644 (file)
@@ -47,7 +47,7 @@ static __be32 nfsd3_proc_getacl(struct svc_rqst *rqstp)
        resp->mask = argp->mask;
 
        if (resp->mask & (NFS_ACL|NFS_ACLCNT)) {
-               acl = get_acl(inode, ACL_TYPE_ACCESS);
+               acl = get_inode_acl(inode, ACL_TYPE_ACCESS);
                if (acl == NULL) {
                        /* Solaris returns the inode's minimum ACL. */
                        acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
@@ -61,7 +61,7 @@ static __be32 nfsd3_proc_getacl(struct svc_rqst *rqstp)
        if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) {
                /* Check how Solaris handles requests for the Default ACL
                   of a non-directory! */
-               acl = get_acl(inode, ACL_TYPE_DEFAULT);
+               acl = get_inode_acl(inode, ACL_TYPE_DEFAULT);
                if (IS_ERR(acl)) {
                        resp->status = nfserrno(PTR_ERR(acl));
                        goto fail;
@@ -103,11 +103,11 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp)
 
        inode_lock(inode);
 
-       error = set_posix_acl(&init_user_ns, inode, ACL_TYPE_ACCESS,
+       error = set_posix_acl(&init_user_ns, fh->fh_dentry, ACL_TYPE_ACCESS,
                              argp->acl_access);
        if (error)
                goto out_drop_lock;
-       error = set_posix_acl(&init_user_ns, inode, ACL_TYPE_DEFAULT,
+       error = set_posix_acl(&init_user_ns, fh->fh_dentry, ACL_TYPE_DEFAULT,
                              argp->acl_default);
 
 out_drop_lock:
index bb8e2f6..5182038 100644 (file)
@@ -135,7 +135,7 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
        unsigned int flags = 0;
        int size = 0;
 
-       pacl = get_acl(inode, ACL_TYPE_ACCESS);
+       pacl = get_inode_acl(inode, ACL_TYPE_ACCESS);
        if (!pacl)
                pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
 
@@ -147,7 +147,7 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
 
        if (S_ISDIR(inode->i_mode)) {
                flags = NFS4_ACL_DIR;
-               dpacl = get_acl(inode, ACL_TYPE_DEFAULT);
+               dpacl = get_inode_acl(inode, ACL_TYPE_DEFAULT);
                if (IS_ERR(dpacl)) {
                        error = PTR_ERR(dpacl);
                        goto rel_pacl;
index a996cbd..08a9296 100644 (file)
@@ -480,12 +480,12 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
                        attr->na_seclabel->data, attr->na_seclabel->len);
        if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && attr->na_pacl)
                attr->na_aclerr = set_posix_acl(&init_user_ns,
-                                               inode, ACL_TYPE_ACCESS,
+                                               dentry, ACL_TYPE_ACCESS,
                                                attr->na_pacl);
        if (IS_ENABLED(CONFIG_FS_POSIX_ACL) &&
            !attr->na_aclerr && attr->na_dpacl && S_ISDIR(inode->i_mode))
                attr->na_aclerr = set_posix_acl(&init_user_ns,
-                                               inode, ACL_TYPE_DEFAULT,
+                                               dentry, ACL_TYPE_DEFAULT,
                                                attr->na_dpacl);
        inode_unlock(inode);
        if (size_change)
index 4f2ffc7..c5e4a88 100644 (file)
@@ -802,7 +802,7 @@ int ntfs3_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
        setattr_copy(mnt_userns, inode, attr);
 
        if (mode != inode->i_mode) {
-               err = ntfs_acl_chmod(mnt_userns, inode);
+               err = ntfs_acl_chmod(mnt_userns, dentry);
                if (err)
                        goto out;
 
@@ -1255,7 +1255,7 @@ const struct inode_operations ntfs_file_inode_operations = {
        .setattr        = ntfs3_setattr,
        .listxattr      = ntfs_listxattr,
        .permission     = ntfs_permission,
-       .get_acl        = ntfs_get_acl,
+       .get_inode_acl  = ntfs_get_acl,
        .set_acl        = ntfs_set_acl,
        .fiemap         = ntfs_fiemap,
 };
index bc22cc3..053cc0e 100644 (file)
@@ -367,7 +367,7 @@ const struct inode_operations ntfs_dir_inode_operations = {
        .mknod          = ntfs_mknod,
        .rename         = ntfs_rename,
        .permission     = ntfs_permission,
-       .get_acl        = ntfs_get_acl,
+       .get_inode_acl  = ntfs_get_acl,
        .set_acl        = ntfs_set_acl,
        .setattr        = ntfs3_setattr,
        .getattr        = ntfs_getattr,
@@ -379,7 +379,7 @@ const struct inode_operations ntfs_special_inode_operations = {
        .setattr        = ntfs3_setattr,
        .getattr        = ntfs_getattr,
        .listxattr      = ntfs_listxattr,
-       .get_acl        = ntfs_get_acl,
+       .get_inode_acl  = ntfs_get_acl,
        .set_acl        = ntfs_set_acl,
 };
 // clang-format on
index 2c79122..a4d2928 100644 (file)
@@ -843,7 +843,7 @@ int ntfs_cmp_names_cpu(const struct cpu_str *uni1, const struct le_str *uni2,
 /* globals from xattr.c */
 #ifdef CONFIG_NTFS3_FS_POSIX_ACL
 struct posix_acl *ntfs_get_acl(struct inode *inode, int type, bool rcu);
-int ntfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int ntfs_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                 struct posix_acl *acl, int type);
 int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode,
                  struct inode *dir);
@@ -852,7 +852,7 @@ int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode,
 #define ntfs_set_acl NULL
 #endif
 
-int ntfs_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode);
+int ntfs_acl_chmod(struct user_namespace *mnt_userns, struct dentry *dentry);
 int ntfs_permission(struct user_namespace *mnt_userns, struct inode *inode,
                    int mask);
 ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
index 7de8718..aafe98e 100644 (file)
@@ -619,10 +619,10 @@ out:
 /*
  * ntfs_set_acl - inode_operations::set_acl
  */
-int ntfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int ntfs_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                 struct posix_acl *acl, int type)
 {
-       return ntfs_set_acl_ex(mnt_userns, inode, acl, type, false);
+       return ntfs_set_acl_ex(mnt_userns, d_inode(dentry), acl, type, false);
 }
 
 /*
@@ -664,8 +664,9 @@ int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode,
 /*
  * ntfs_acl_chmod - Helper for ntfs3_setattr().
  */
-int ntfs_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode)
+int ntfs_acl_chmod(struct user_namespace *mnt_userns, struct dentry *dentry)
 {
+       struct inode *inode = d_inode(dentry);
        struct super_block *sb = inode->i_sb;
 
        if (!(sb->s_flags & SB_POSIXACL))
@@ -674,7 +675,7 @@ int ntfs_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode)
        if (S_ISLNK(inode->i_mode))
                return -EOPNOTSUPP;
 
-       return posix_acl_chmod(mnt_userns, inode, inode->i_mode);
+       return posix_acl_chmod(mnt_userns, dentry, inode->i_mode);
 }
 
 /*
index 23a72a4..9f19cf9 100644 (file)
@@ -260,12 +260,13 @@ static int ocfs2_set_acl(handle_t *handle,
        return ret;
 }
 
-int ocfs2_iop_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int ocfs2_iop_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                      struct posix_acl *acl, int type)
 {
        struct buffer_head *bh = NULL;
        int status, had_lock;
        struct ocfs2_lock_holder oh;
+       struct inode *inode = d_inode(dentry);
 
        had_lock = ocfs2_inode_lock_tracker(inode, &bh, 1, &oh);
        if (had_lock < 0)
index 95a57c8..a897c4e 100644 (file)
@@ -17,7 +17,7 @@ struct ocfs2_acl_entry {
 };
 
 struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type, bool rcu);
-int ocfs2_iop_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int ocfs2_iop_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                      struct posix_acl *acl, int type);
 extern int ocfs2_acl_chmod(struct inode *, struct buffer_head *);
 extern int ocfs2_init_acl(handle_t *, struct inode *, struct inode *,
index 9c67edd..af900aa 100644 (file)
@@ -2712,7 +2712,7 @@ const struct inode_operations ocfs2_file_iops = {
        .permission     = ocfs2_permission,
        .listxattr      = ocfs2_listxattr,
        .fiemap         = ocfs2_fiemap,
-       .get_acl        = ocfs2_iop_get_acl,
+       .get_inode_acl  = ocfs2_iop_get_acl,
        .set_acl        = ocfs2_iop_set_acl,
        .fileattr_get   = ocfs2_fileattr_get,
        .fileattr_set   = ocfs2_fileattr_set,
@@ -2722,7 +2722,7 @@ const struct inode_operations ocfs2_special_file_iops = {
        .setattr        = ocfs2_setattr,
        .getattr        = ocfs2_getattr,
        .permission     = ocfs2_permission,
-       .get_acl        = ocfs2_iop_get_acl,
+       .get_inode_acl  = ocfs2_iop_get_acl,
        .set_acl        = ocfs2_iop_set_acl,
 };
 
index 05f3298..a8fd51a 100644 (file)
@@ -2915,7 +2915,7 @@ const struct inode_operations ocfs2_dir_iops = {
        .permission     = ocfs2_permission,
        .listxattr      = ocfs2_listxattr,
        .fiemap         = ocfs2_fiemap,
-       .get_acl        = ocfs2_iop_get_acl,
+       .get_inode_acl  = ocfs2_iop_get_acl,
        .set_acl        = ocfs2_iop_set_acl,
        .fileattr_get   = ocfs2_fileattr_get,
        .fileattr_set   = ocfs2_fileattr_set,
index 605e5a3..c5da209 100644 (file)
@@ -64,8 +64,7 @@ struct posix_acl *orangefs_get_acl(struct inode *inode, int type, bool rcu)
        return acl;
 }
 
-static int __orangefs_set_acl(struct inode *inode, struct posix_acl *acl,
-                             int type)
+int __orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
 {
        int error = 0;
        void *value = NULL;
@@ -119,12 +118,13 @@ out:
        return error;
 }
 
-int orangefs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int orangefs_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                     struct posix_acl *acl, int type)
 {
        int error;
        struct iattr iattr;
        int rc;
+       struct inode *inode = d_inode(dentry);
 
        memset(&iattr, 0, sizeof iattr);
 
@@ -153,46 +153,7 @@ int orangefs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
        rc = __orangefs_set_acl(inode, acl, type);
 
        if (!rc && (iattr.ia_valid == ATTR_MODE))
-               rc = __orangefs_setattr(inode, &iattr);
+               rc = __orangefs_setattr_mode(dentry, &iattr);
 
        return rc;
 }
-
-int orangefs_init_acl(struct inode *inode, struct inode *dir)
-{
-       struct posix_acl *default_acl, *acl;
-       umode_t mode = inode->i_mode;
-       struct iattr iattr;
-       int error = 0;
-
-       error = posix_acl_create(dir, &mode, &default_acl, &acl);
-       if (error)
-               return error;
-
-       if (default_acl) {
-               error = __orangefs_set_acl(inode, default_acl,
-                                          ACL_TYPE_DEFAULT);
-               posix_acl_release(default_acl);
-       } else {
-               inode->i_default_acl = NULL;
-       }
-
-       if (acl) {
-               if (!error)
-                       error = __orangefs_set_acl(inode, acl, ACL_TYPE_ACCESS);
-               posix_acl_release(acl);
-       } else {
-               inode->i_acl = NULL;
-       }
-
-       /* If mode of the inode was changed, then do a forcible ->setattr */
-       if (mode != inode->i_mode) {
-               memset(&iattr, 0, sizeof iattr);
-               inode->i_mode = mode;
-               iattr.ia_mode = mode;
-               iattr.ia_valid |= ATTR_MODE;
-               __orangefs_setattr(inode, &iattr);
-       }
-
-       return error;
-}
index b3bbb5a..370bd3b 100644 (file)
@@ -828,15 +828,23 @@ again:
        spin_unlock(&inode->i_lock);
        mark_inode_dirty(inode);
 
-       if (iattr->ia_valid & ATTR_MODE)
-               /* change mod on a file that has ACLs */
-               ret = posix_acl_chmod(&init_user_ns, inode, inode->i_mode);
-
        ret = 0;
 out:
        return ret;
 }
 
+int __orangefs_setattr_mode(struct dentry *dentry, struct iattr *iattr)
+{
+       int ret;
+       struct inode *inode = d_inode(dentry);
+
+       ret = __orangefs_setattr(inode, iattr);
+       /* change mode on a file that has ACLs */
+       if (!ret && (iattr->ia_valid & ATTR_MODE))
+               ret = posix_acl_chmod(&init_user_ns, dentry, inode->i_mode);
+       return ret;
+}
+
 /*
  * Change attributes of an object referenced by dentry.
  */
@@ -849,7 +857,7 @@ int orangefs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
        ret = setattr_prepare(&init_user_ns, dentry, iattr);
        if (ret)
                goto out;
-       ret = __orangefs_setattr(d_inode(dentry), iattr);
+       ret = __orangefs_setattr_mode(dentry, iattr);
        sync_inode_metadata(d_inode(dentry), 1);
 out:
        gossip_debug(GOSSIP_INODE_DEBUG, "orangefs_setattr: returning %d\n",
@@ -967,7 +975,7 @@ static int orangefs_fileattr_set(struct user_namespace *mnt_userns,
 
 /* ORANGEFS2 implementation of VFS inode operations for files */
 static const struct inode_operations orangefs_file_inode_operations = {
-       .get_acl = orangefs_get_acl,
+       .get_inode_acl = orangefs_get_acl,
        .set_acl = orangefs_set_acl,
        .setattr = orangefs_setattr,
        .getattr = orangefs_getattr,
@@ -1097,8 +1105,9 @@ struct inode *orangefs_iget(struct super_block *sb,
  * Allocate an inode for a newly created file and insert it into the inode hash.
  */
 struct inode *orangefs_new_inode(struct super_block *sb, struct inode *dir,
-               int mode, dev_t dev, struct orangefs_object_kref *ref)
+               umode_t mode, dev_t dev, struct orangefs_object_kref *ref)
 {
+       struct posix_acl *acl = NULL, *default_acl = NULL;
        unsigned long hash = orangefs_handle_hash(ref);
        struct inode *inode;
        int error;
@@ -1115,6 +1124,10 @@ struct inode *orangefs_new_inode(struct super_block *sb, struct inode *dir,
        if (!inode)
                return ERR_PTR(-ENOMEM);
 
+       error = posix_acl_create(dir, &mode, &default_acl, &acl);
+       if (error)
+               goto out_iput;
+
        orangefs_set_inode(inode, ref);
        inode->i_ino = hash;    /* needed for stat etc */
 
@@ -1125,6 +1138,19 @@ struct inode *orangefs_new_inode(struct super_block *sb, struct inode *dir,
        orangefs_init_iops(inode);
        inode->i_rdev = dev;
 
+       if (default_acl) {
+               error = __orangefs_set_acl(inode, default_acl,
+                                          ACL_TYPE_DEFAULT);
+               if (error)
+                       goto out_iput;
+       }
+
+       if (acl) {
+               error = __orangefs_set_acl(inode, acl, ACL_TYPE_ACCESS);
+               if (error)
+                       goto out_iput;
+       }
+
        error = insert_inode_locked4(inode, hash, orangefs_test_inode, ref);
        if (error < 0)
                goto out_iput;
@@ -1132,10 +1158,22 @@ struct inode *orangefs_new_inode(struct super_block *sb, struct inode *dir,
        gossip_debug(GOSSIP_INODE_DEBUG,
                     "Initializing ACL's for inode %pU\n",
                     get_khandle_from_ino(inode));
-       orangefs_init_acl(inode, dir);
+       if (mode != inode->i_mode) {
+               struct iattr iattr = {
+                       .ia_mode = mode,
+                       .ia_valid = ATTR_MODE,
+               };
+               inode->i_mode = mode;
+               __orangefs_setattr(inode, &iattr);
+               __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
+       }
+       posix_acl_release(acl);
+       posix_acl_release(default_acl);
        return inode;
 
 out_iput:
        iput(inode);
+       posix_acl_release(acl);
+       posix_acl_release(default_acl);
        return ERR_PTR(error);
 }
index 600e8ee..75c1a3d 100644 (file)
@@ -430,7 +430,7 @@ static int orangefs_rename(struct user_namespace *mnt_userns,
 /* ORANGEFS implementation of VFS inode operations for directories */
 const struct inode_operations orangefs_dir_inode_operations = {
        .lookup = orangefs_lookup,
-       .get_acl = orangefs_get_acl,
+       .get_inode_acl = orangefs_get_acl,
        .set_acl = orangefs_set_acl,
        .create = orangefs_create,
        .unlink = orangefs_unlink,
index b5940ec..6e0cc01 100644 (file)
@@ -103,13 +103,13 @@ enum orangefs_vfs_op_states {
 #define ORANGEFS_CACHE_CREATE_FLAGS 0
 #endif
 
-extern int orangefs_init_acl(struct inode *inode, struct inode *dir);
 extern const struct xattr_handler *orangefs_xattr_handlers[];
 
 extern struct posix_acl *orangefs_get_acl(struct inode *inode, int type, bool rcu);
 extern int orangefs_set_acl(struct user_namespace *mnt_userns,
-                           struct inode *inode, struct posix_acl *acl,
+                           struct dentry *dentry, struct posix_acl *acl,
                            int type);
+int __orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type);
 
 /*
  * orangefs data structures
@@ -356,11 +356,12 @@ void fsid_key_table_finalize(void);
 vm_fault_t orangefs_page_mkwrite(struct vm_fault *);
 struct inode *orangefs_new_inode(struct super_block *sb,
                              struct inode *dir,
-                             int mode,
+                             umode_t mode,
                              dev_t dev,
                              struct orangefs_object_kref *ref);
 
 int __orangefs_setattr(struct inode *, struct iattr *);
+int __orangefs_setattr_mode(struct dentry *dentry, struct iattr *iattr);
 int orangefs_setattr(struct user_namespace *, struct dentry *, struct iattr *);
 
 int orangefs_getattr(struct user_namespace *mnt_userns, const struct path *path,
index f436d88..6e4e65e 100644 (file)
@@ -44,6 +44,35 @@ static bool ovl_must_copy_xattr(const char *name)
               !strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN);
 }
 
+static int ovl_copy_acl(struct ovl_fs *ofs, const struct path *path,
+                       struct dentry *dentry, const char *acl_name)
+{
+       int err;
+       struct posix_acl *clone, *real_acl = NULL;
+
+       real_acl = ovl_get_acl_path(path, acl_name, false);
+       if (!real_acl)
+               return 0;
+
+       if (IS_ERR(real_acl)) {
+               err = PTR_ERR(real_acl);
+               if (err == -ENODATA || err == -EOPNOTSUPP)
+                       return 0;
+               return err;
+       }
+
+       clone = posix_acl_clone(real_acl, GFP_KERNEL);
+       posix_acl_release(real_acl); /* release original acl */
+       if (!clone)
+               return -ENOMEM;
+
+       err = ovl_do_set_acl(ofs, dentry, acl_name, clone);
+
+       /* release cloned acl */
+       posix_acl_release(clone);
+       return err;
+}
+
 int ovl_copy_xattr(struct super_block *sb, const struct path *oldpath, struct dentry *new)
 {
        struct dentry *old = oldpath->dentry;
@@ -93,6 +122,15 @@ int ovl_copy_xattr(struct super_block *sb, const struct path *oldpath, struct de
                        error = 0;
                        continue; /* Discard */
                }
+
+               if (is_posix_acl_xattr(name)) {
+                       error = ovl_copy_acl(OVL_FS(sb), oldpath, new, name);
+                       if (!error)
+                               continue;
+                       /* POSIX ACLs must be copied. */
+                       break;
+               }
+
 retry:
                size = ovl_do_getxattr(oldpath, name, value, value_size);
                if (size == -ERANGE)
index 6b03457..cbb569d 100644 (file)
@@ -435,28 +435,12 @@ out:
 }
 
 static int ovl_set_upper_acl(struct ovl_fs *ofs, struct dentry *upperdentry,
-                            const char *name, const struct posix_acl *acl)
+                            const char *acl_name, struct posix_acl *acl)
 {
-       void *buffer;
-       size_t size;
-       int err;
-
        if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !acl)
                return 0;
 
-       size = posix_acl_xattr_size(acl->a_count);
-       buffer = kmalloc(size, GFP_KERNEL);
-       if (!buffer)
-               return -ENOMEM;
-
-       err = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
-       if (err < 0)
-               goto out_free;
-
-       err = ovl_do_setxattr(ofs, upperdentry, name, buffer, size, XATTR_CREATE);
-out_free:
-       kfree(buffer);
-       return err;
+       return ovl_do_set_acl(ofs, upperdentry, acl_name, acl);
 }
 
 static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
@@ -1311,7 +1295,9 @@ const struct inode_operations ovl_dir_inode_operations = {
        .permission     = ovl_permission,
        .getattr        = ovl_getattr,
        .listxattr      = ovl_listxattr,
+       .get_inode_acl  = ovl_get_inode_acl,
        .get_acl        = ovl_get_acl,
+       .set_acl        = ovl_set_acl,
        .update_time    = ovl_update_time,
        .fileattr_get   = ovl_fileattr_get,
        .fileattr_set   = ovl_fileattr_set,
index 9e61511..ee6dfa5 100644 (file)
@@ -14,6 +14,8 @@
 #include <linux/fileattr.h>
 #include <linux/security.h>
 #include <linux/namei.h>
+#include <linux/posix_acl.h>
+#include <linux/posix_acl_xattr.h>
 #include "overlayfs.h"
 
 
@@ -460,7 +462,7 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
  * of the POSIX ACLs retrieved from the lower layer to this function to not
  * alter the POSIX ACLs for the underlying filesystem.
  */
-static void ovl_idmap_posix_acl(struct inode *realinode,
+static void ovl_idmap_posix_acl(const struct inode *realinode,
                                struct user_namespace *mnt_userns,
                                struct posix_acl *acl)
 {
@@ -484,6 +486,64 @@ static void ovl_idmap_posix_acl(struct inode *realinode,
        }
 }
 
+/*
+ * The @noperm argument is used to skip permission checking and is a temporary
+ * measure. Quoting Miklos from an earlier discussion:
+ *
+ * > So there are two paths to getting an acl:
+ * > 1) permission checking and 2) retrieving the value via getxattr(2).
+ * > This is a similar situation as reading a symlink vs. following it.
+ * > When following a symlink overlayfs always reads the link on the
+ * > underlying fs just as if it was a readlink(2) call, calling
+ * > security_inode_readlink() instead of security_inode_follow_link().
+ * > This is logical: we are reading the link from the underlying storage,
+ * > and following it on overlayfs.
+ * >
+ * > Applying the same logic to acl: we do need to call the
+ * > security_inode_getxattr() on the underlying fs, even if just want to
+ * > check permissions on overlay. This is currently not done, which is an
+ * > inconsistency.
+ * >
+ * > Maybe adding the check to ovl_get_acl() is the right way to go, but
+ * > I'm a little afraid of a performance regression.  Will look into that.
+ *
+ * Until we have made a decision allow this helper to take the @noperm
+ * argument. We should hopefully be able to remove it soon.
+ */
+struct posix_acl *ovl_get_acl_path(const struct path *path,
+                                  const char *acl_name, bool noperm)
+{
+       struct posix_acl *real_acl, *clone;
+       struct user_namespace *mnt_userns;
+       struct inode *realinode = d_inode(path->dentry);
+
+       mnt_userns = mnt_user_ns(path->mnt);
+
+       if (noperm)
+               real_acl = get_inode_acl(realinode, posix_acl_type(acl_name));
+       else
+               real_acl = vfs_get_acl(mnt_userns, path->dentry, acl_name);
+       if (IS_ERR_OR_NULL(real_acl))
+               return real_acl;
+
+       if (!is_idmapped_mnt(path->mnt))
+               return real_acl;
+
+       /*
+        * We cannot alter the ACLs returned from the relevant layer as that
+        * would alter the cached values filesystem wide for the lower
+        * filesystem. Instead we can clone the ACLs and then apply the
+        * relevant idmapping of the layer.
+        */
+       clone = posix_acl_clone(real_acl, GFP_KERNEL);
+       posix_acl_release(real_acl); /* release original acl */
+       if (!clone)
+               return ERR_PTR(-ENOMEM);
+
+       ovl_idmap_posix_acl(realinode, mnt_userns, clone);
+       return clone;
+}
+
 /*
  * When the relevant layer is an idmapped mount we need to take the idmapping
  * of the layer into account and translate any ACL_{GROUP,USER} values
@@ -495,10 +555,12 @@ static void ovl_idmap_posix_acl(struct inode *realinode,
  *
  * This is obviously only relevant when idmapped layers are used.
  */
-struct posix_acl *ovl_get_acl(struct inode *inode, int type, bool rcu)
+struct posix_acl *do_ovl_get_acl(struct user_namespace *mnt_userns,
+                                struct inode *inode, int type,
+                                bool rcu, bool noperm)
 {
        struct inode *realinode = ovl_inode_real(inode);
-       struct posix_acl *acl, *clone;
+       struct posix_acl *acl;
        struct path realpath;
 
        if (!IS_POSIXACL(realinode))
@@ -512,40 +574,115 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type, bool rcu)
        }
 
        if (rcu) {
+               /*
+                * If the layer is idmapped drop out of RCU path walk
+                * so we can clone the ACLs.
+                */
+               if (is_idmapped_mnt(realpath.mnt))
+                       return ERR_PTR(-ECHILD);
+
                acl = get_cached_acl_rcu(realinode, type);
        } else {
                const struct cred *old_cred;
 
                old_cred = ovl_override_creds(inode->i_sb);
-               acl = get_acl(realinode, type);
+               acl = ovl_get_acl_path(&realpath, posix_acl_xattr_name(type), noperm);
                revert_creds(old_cred);
        }
-       /*
-        * If there are no POSIX ACLs, or we encountered an error,
-        * or the layer isn't idmapped we don't need to do anything.
-        */
-       if (!is_idmapped_mnt(realpath.mnt) || IS_ERR_OR_NULL(acl))
-               return acl;
+
+       return acl;
+}
+
+static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode,
+                                struct posix_acl *acl, int type)
+{
+       int err;
+       struct path realpath;
+       const char *acl_name;
+       const struct cred *old_cred;
+       struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
+       struct dentry *upperdentry = ovl_dentry_upper(dentry);
+       struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
+
+       err = ovl_want_write(dentry);
+       if (err)
+               return err;
 
        /*
-        * We only get here if the layer is idmapped. So drop out of RCU path
-        * walk so we can clone the ACLs. There's no need to release the ACLs
-        * since get_cached_acl_rcu() doesn't take a reference on the ACLs.
+        * If ACL is to be removed from a lower file, check if it exists in
+        * the first place before copying it up.
         */
-       if (rcu)
-               return ERR_PTR(-ECHILD);
+       acl_name = posix_acl_xattr_name(type);
+       if (!acl && !upperdentry) {
+               struct posix_acl *real_acl;
 
-       clone = posix_acl_clone(acl, GFP_KERNEL);
-       if (!clone)
-               clone = ERR_PTR(-ENOMEM);
+               ovl_path_lower(dentry, &realpath);
+               old_cred = ovl_override_creds(dentry->d_sb);
+               real_acl = vfs_get_acl(mnt_user_ns(realpath.mnt), realdentry,
+                                      acl_name);
+               revert_creds(old_cred);
+               if (IS_ERR(real_acl)) {
+                       err = PTR_ERR(real_acl);
+                       goto out_drop_write;
+               }
+               posix_acl_release(real_acl);
+       }
+
+       if (!upperdentry) {
+               err = ovl_copy_up(dentry);
+               if (err)
+                       goto out_drop_write;
+
+               realdentry = ovl_dentry_upper(dentry);
+       }
+
+       old_cred = ovl_override_creds(dentry->d_sb);
+       if (acl)
+               err = ovl_do_set_acl(ofs, realdentry, acl_name, acl);
        else
-               ovl_idmap_posix_acl(realinode, mnt_user_ns(realpath.mnt), clone);
+               err = ovl_do_remove_acl(ofs, realdentry, acl_name);
+       revert_creds(old_cred);
+
+       /* copy c/mtime */
+       ovl_copyattr(inode);
+
+out_drop_write:
+       ovl_drop_write(dentry);
+       return err;
+}
+
+int ovl_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+               struct posix_acl *acl, int type)
+{
+       int err;
+       struct inode *inode = d_inode(dentry);
+       struct dentry *workdir = ovl_workdir(dentry);
+       struct inode *realinode = ovl_inode_real(inode);
+
+       if (!IS_POSIXACL(d_inode(workdir)))
+               return -EOPNOTSUPP;
+       if (!realinode->i_op->set_acl)
+               return -EOPNOTSUPP;
+       if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode))
+               return acl ? -EACCES : 0;
+       if (!inode_owner_or_capable(&init_user_ns, inode))
+               return -EPERM;
+
        /*
-        * Since we're not in RCU path walk we always need to release the
-        * original ACLs.
+        * Check if sgid bit needs to be cleared (actual setacl operation will
+        * be done with mounter's capabilities and so that won't do it for us).
         */
-       posix_acl_release(acl);
-       return clone;
+       if (unlikely(inode->i_mode & S_ISGID) && type == ACL_TYPE_ACCESS &&
+           !in_group_p(inode->i_gid) &&
+           !capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID)) {
+               struct iattr iattr = { .ia_valid = ATTR_KILL_SGID };
+
+               err = ovl_setattr(&init_user_ns, dentry, &iattr);
+               if (err)
+                       return err;
+       }
+
+       return ovl_set_or_remove_acl(dentry, inode, acl, type);
 }
 #endif
 
@@ -721,7 +858,9 @@ static const struct inode_operations ovl_file_inode_operations = {
        .permission     = ovl_permission,
        .getattr        = ovl_getattr,
        .listxattr      = ovl_listxattr,
+       .get_inode_acl  = ovl_get_inode_acl,
        .get_acl        = ovl_get_acl,
+       .set_acl        = ovl_set_acl,
        .update_time    = ovl_update_time,
        .fiemap         = ovl_fiemap,
        .fileattr_get   = ovl_fileattr_get,
@@ -741,7 +880,9 @@ static const struct inode_operations ovl_special_inode_operations = {
        .permission     = ovl_permission,
        .getattr        = ovl_getattr,
        .listxattr      = ovl_listxattr,
+       .get_inode_acl  = ovl_get_inode_acl,
        .get_acl        = ovl_get_acl,
+       .set_acl        = ovl_set_acl,
        .update_time    = ovl_update_time,
 };
 
index eee8f08..480e6aa 100644 (file)
@@ -8,6 +8,8 @@
 #include <linux/uuid.h>
 #include <linux/fs.h>
 #include <linux/namei.h>
+#include <linux/posix_acl.h>
+#include <linux/posix_acl_xattr.h>
 #include "ovl_entry.h"
 
 #undef pr_fmt
@@ -278,6 +280,18 @@ static inline int ovl_removexattr(struct ovl_fs *ofs, struct dentry *dentry,
        return ovl_do_removexattr(ofs, dentry, ovl_xattr(ofs, ox));
 }
 
+static inline int ovl_do_set_acl(struct ovl_fs *ofs, struct dentry *dentry,
+                                const char *acl_name, struct posix_acl *acl)
+{
+       return vfs_set_acl(ovl_upper_mnt_userns(ofs), dentry, acl_name, acl);
+}
+
+static inline int ovl_do_remove_acl(struct ovl_fs *ofs, struct dentry *dentry,
+                                   const char *acl_name)
+{
+       return vfs_remove_acl(ovl_upper_mnt_userns(ofs), dentry, acl_name);
+}
+
 static inline int ovl_do_rename(struct ovl_fs *ofs, struct inode *olddir,
                                struct dentry *olddentry, struct inode *newdir,
                                struct dentry *newdentry, unsigned int flags)
@@ -594,9 +608,33 @@ int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
 ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
 
 #ifdef CONFIG_FS_POSIX_ACL
-struct posix_acl *ovl_get_acl(struct inode *inode, int type, bool rcu);
+struct posix_acl *do_ovl_get_acl(struct user_namespace *mnt_userns,
+                                struct inode *inode, int type,
+                                bool rcu, bool noperm);
+static inline struct posix_acl *ovl_get_inode_acl(struct inode *inode, int type,
+                                                 bool rcu)
+{
+       return do_ovl_get_acl(&init_user_ns, inode, type, rcu, true);
+}
+static inline struct posix_acl *ovl_get_acl(struct user_namespace *mnt_userns,
+                                           struct dentry *dentry, int type)
+{
+       return do_ovl_get_acl(mnt_userns, d_inode(dentry), type, false, false);
+}
+int ovl_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+               struct posix_acl *acl, int type);
+struct posix_acl *ovl_get_acl_path(const struct path *path,
+                                  const char *acl_name, bool noperm);
 #else
-#define ovl_get_acl    NULL
+#define ovl_get_inode_acl      NULL
+#define ovl_get_acl            NULL
+#define ovl_set_acl            NULL
+static inline struct posix_acl *ovl_get_acl_path(const struct path *path,
+                                                const char *acl_name,
+                                                bool noperm)
+{
+       return NULL;
+}
 #endif
 
 int ovl_update_time(struct inode *inode, struct timespec64 *ts, int flags);
index a29a8af..2addafe 100644 (file)
@@ -813,13 +813,11 @@ retry:
                 * allowed as upper are limited to "normal" ones, where checking
                 * for the above two errors is sufficient.
                 */
-               err = ovl_do_removexattr(ofs, work,
-                                        XATTR_NAME_POSIX_ACL_DEFAULT);
+               err = ovl_do_remove_acl(ofs, work, XATTR_NAME_POSIX_ACL_DEFAULT);
                if (err && err != -ENODATA && err != -EOPNOTSUPP)
                        goto out_dput;
 
-               err = ovl_do_removexattr(ofs, work,
-                                        XATTR_NAME_POSIX_ACL_ACCESS);
+               err = ovl_do_remove_acl(ofs, work, XATTR_NAME_POSIX_ACL_ACCESS);
                if (err && err != -ENODATA && err != -EOPNOTSUPP)
                        goto out_dput;
 
@@ -1001,83 +999,6 @@ static unsigned int ovl_split_lowerdirs(char *str)
        return ctr;
 }
 
-static int __maybe_unused
-ovl_posix_acl_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, handler->name, buffer, size);
-}
-
-static int __maybe_unused
-ovl_posix_acl_xattr_set(const struct xattr_handler *handler,
-                       struct user_namespace *mnt_userns,
-                       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);
-       struct posix_acl *acl = NULL;
-       int err;
-
-       /* Check that everything is OK before copy-up */
-       if (value) {
-               /* The above comment can be understood in two ways:
-                *
-                * 1. We just want to check whether the basic POSIX ACL format
-                *    is ok. For example, if the header is correct and the size
-                *    is sane.
-                * 2. We want to know whether the ACL_{GROUP,USER} entries can
-                *    be mapped according to the underlying filesystem.
-                *
-                * Currently, we only check 1. If we wanted to check 2. we
-                * would need to pass the mnt_userns and the fs_userns of the
-                * underlying filesystem. But frankly, I think checking 1. is
-                * enough to start the copy-up.
-                */
-               acl = vfs_set_acl_prepare(&init_user_ns, &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(&init_user_ns, inode))
-               goto out_acl_release;
-
-       posix_acl_release(acl);
-
-       /*
-        * Check if sgid bit needs to be cleared (actual setacl operation will
-        * be done with mounter's capabilities and so that won't do it for us).
-        */
-       if (unlikely(inode->i_mode & S_ISGID) &&
-           handler->flags == ACL_TYPE_ACCESS &&
-           !in_group_p(inode->i_gid) &&
-           !capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID)) {
-               struct iattr iattr = { .ia_valid = ATTR_KILL_SGID };
-
-               err = ovl_setattr(&init_user_ns, dentry, &iattr);
-               if (err)
-                       return err;
-       }
-
-       err = ovl_xattr_set(dentry, inode, handler->name, value, size, flags);
-       return err;
-
-out_acl_release:
-       posix_acl_release(acl);
-       return err;
-}
-
 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)
@@ -1110,22 +1031,6 @@ static int ovl_other_xattr_set(const struct xattr_handler *handler,
        return ovl_xattr_set(dentry, inode, name, value, size, flags);
 }
 
-static const struct xattr_handler __maybe_unused
-ovl_posix_acl_access_xattr_handler = {
-       .name = XATTR_NAME_POSIX_ACL_ACCESS,
-       .flags = ACL_TYPE_ACCESS,
-       .get = ovl_posix_acl_xattr_get,
-       .set = ovl_posix_acl_xattr_set,
-};
-
-static const struct xattr_handler __maybe_unused
-ovl_posix_acl_default_xattr_handler = {
-       .name = XATTR_NAME_POSIX_ACL_DEFAULT,
-       .flags = ACL_TYPE_DEFAULT,
-       .get = ovl_posix_acl_xattr_get,
-       .set = ovl_posix_acl_xattr_set,
-};
-
 static const struct xattr_handler ovl_own_trusted_xattr_handler = {
        .prefix = OVL_XATTR_TRUSTED_PREFIX,
        .get = ovl_own_xattr_get,
@@ -1146,8 +1051,8 @@ static const struct xattr_handler ovl_other_xattr_handler = {
 
 static const struct xattr_handler *ovl_trusted_xattr_handlers[] = {
 #ifdef CONFIG_FS_POSIX_ACL
-       &ovl_posix_acl_access_xattr_handler,
-       &ovl_posix_acl_default_xattr_handler,
+       &posix_acl_access_xattr_handler,
+       &posix_acl_default_xattr_handler,
 #endif
        &ovl_own_trusted_xattr_handler,
        &ovl_other_xattr_handler,
@@ -1156,8 +1061,8 @@ static const struct xattr_handler *ovl_trusted_xattr_handlers[] = {
 
 static const struct xattr_handler *ovl_user_xattr_handlers[] = {
 #ifdef CONFIG_FS_POSIX_ACL
-       &ovl_posix_acl_access_xattr_handler,
-       &ovl_posix_acl_default_xattr_handler,
+       &posix_acl_access_xattr_handler,
+       &posix_acl_default_xattr_handler,
 #endif
        &ovl_own_user_xattr_handler,
        &ovl_other_xattr_handler,
index 74dc0f5..e6643db 100644 (file)
 #include <linux/namei.h>
 #include <linux/mnt_idmapping.h>
 #include <linux/iversion.h>
+#include <linux/security.h>
+#include <linux/evm.h>
+#include <linux/fsnotify.h>
+
+#include "internal.h"
 
 static struct posix_acl **acl_by_type(struct inode *inode, int type)
 {
@@ -64,7 +69,7 @@ struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type)
        if (acl == ACL_DONT_CACHE) {
                struct posix_acl *ret;
 
-               ret = inode->i_op->get_acl(inode, type, LOOKUP_RCU);
+               ret = inode->i_op->get_inode_acl(inode, type, LOOKUP_RCU);
                if (!IS_ERR(ret))
                        acl = ret;
        }
@@ -106,15 +111,17 @@ void forget_all_cached_acls(struct inode *inode)
 }
 EXPORT_SYMBOL(forget_all_cached_acls);
 
-struct posix_acl *get_acl(struct inode *inode, int type)
+static struct posix_acl *__get_acl(struct user_namespace *mnt_userns,
+                                  struct dentry *dentry, struct inode *inode,
+                                  int type)
 {
-       void *sentinel;
+       struct posix_acl *sentinel;
        struct posix_acl **p;
        struct posix_acl *acl;
 
        /*
         * The sentinel is used to detect when another operation like
-        * set_cached_acl() or forget_cached_acl() races with get_acl().
+        * set_cached_acl() or forget_cached_acl() races with get_inode_acl().
         * It is guaranteed that is_uncached_acl(sentinel) is true.
         */
 
@@ -133,25 +140,27 @@ struct posix_acl *get_acl(struct inode *inode, int type)
         * current value of the ACL will not be ACL_NOT_CACHED and so our own
         * sentinel will not be set; another task will update the cache.  We
         * could wait for that other task to complete its job, but it's easier
-        * to just call ->get_acl to fetch the ACL ourself.  (This is going to
-        * be an unlikely race.)
+        * to just call ->get_inode_acl to fetch the ACL ourself.  (This is
+        * going to be an unlikely race.)
         */
        cmpxchg(p, ACL_NOT_CACHED, sentinel);
 
        /*
-        * Normally, the ACL returned by ->get_acl will be cached.
+        * Normally, the ACL returned by ->get{_inode}_acl will be cached.
         * A filesystem can prevent that by calling
-        * forget_cached_acl(inode, type) in ->get_acl.
+        * forget_cached_acl(inode, type) in ->get{_inode}_acl.
         *
-        * If the filesystem doesn't have a get_acl() function at all, we'll
-        * just create the negative cache entry.
+        * If the filesystem doesn't have a get{_inode}_ acl() function at all,
+        * we'll just create the negative cache entry.
         */
-       if (!inode->i_op->get_acl) {
+       if (dentry && inode->i_op->get_acl) {
+               acl = inode->i_op->get_acl(mnt_userns, dentry, type);
+       } else if (inode->i_op->get_inode_acl) {
+               acl = inode->i_op->get_inode_acl(inode, type, false);
+       } else {
                set_cached_acl(inode, type, NULL);
                return NULL;
        }
-       acl = inode->i_op->get_acl(inode, type, false);
-
        if (IS_ERR(acl)) {
                /*
                 * Remove our sentinel so that we don't block future attempts
@@ -169,7 +178,12 @@ struct posix_acl *get_acl(struct inode *inode, int type)
                posix_acl_release(acl);
        return acl;
 }
-EXPORT_SYMBOL(get_acl);
+
+struct posix_acl *get_inode_acl(struct inode *inode, int type)
+{
+       return __get_acl(&init_user_ns, NULL, inode, type);
+}
+EXPORT_SYMBOL(get_inode_acl);
 
 /*
  * Init a fresh posix_acl
@@ -578,19 +592,20 @@ EXPORT_SYMBOL(__posix_acl_chmod);
  * posix_acl_chmod - chmod a posix acl
  *
  * @mnt_userns:        user namespace of the mount @inode was found from
- * @inode:     inode to check permissions on
+ * @dentry:    dentry to check permissions on
  * @mode:      the new mode of @inode
  *
- * If the inode has been found through an idmapped mount the user namespace of
+ * If the dentry has been found through an idmapped mount the user namespace of
  * the vfsmount must be passed through @mnt_userns. This function will then
  * take care to map the inode according to @mnt_userns before checking
  * permissions. On non-idmapped mounts or if permission checking is to be
  * performed on the raw inode simply passs init_user_ns.
  */
 int
- posix_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode,
+ posix_acl_chmod(struct user_namespace *mnt_userns, struct dentry *dentry,
                    umode_t mode)
 {
+       struct inode *inode = d_inode(dentry);
        struct posix_acl *acl;
        int ret = 0;
 
@@ -599,7 +614,7 @@ int
        if (!inode->i_op->set_acl)
                return -EOPNOTSUPP;
 
-       acl = get_acl(inode, ACL_TYPE_ACCESS);
+       acl = get_inode_acl(inode, ACL_TYPE_ACCESS);
        if (IS_ERR_OR_NULL(acl)) {
                if (acl == ERR_PTR(-EOPNOTSUPP))
                        return 0;
@@ -609,7 +624,7 @@ int
        ret = __posix_acl_chmod(&acl, GFP_KERNEL, mode);
        if (ret)
                return ret;
-       ret = inode->i_op->set_acl(mnt_userns, inode, acl, ACL_TYPE_ACCESS);
+       ret = inode->i_op->set_acl(mnt_userns, dentry, acl, ACL_TYPE_ACCESS);
        posix_acl_release(acl);
        return ret;
 }
@@ -629,7 +644,7 @@ posix_acl_create(struct inode *dir, umode_t *mode,
        if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
                return 0;
 
-       p = get_acl(dir, ACL_TYPE_DEFAULT);
+       p = get_inode_acl(dir, ACL_TYPE_DEFAULT);
        if (!p || p == ERR_PTR(-EOPNOTSUPP)) {
                *mode &= ~current_umask();
                return 0;
@@ -732,118 +747,32 @@ static int posix_acl_fix_xattr_common(const void *value, size_t size)
        return count;
 }
 
-void posix_acl_getxattr_idmapped_mnt(struct user_namespace *mnt_userns,
-                                    const struct inode *inode,
-                                    void *value, size_t size)
-{
-       struct posix_acl_xattr_header *header = value;
-       struct posix_acl_xattr_entry *entry = (void *)(header + 1), *end;
-       struct user_namespace *fs_userns = i_user_ns(inode);
-       int count;
-       vfsuid_t vfsuid;
-       vfsgid_t vfsgid;
-       kuid_t uid;
-       kgid_t gid;
-
-       if (no_idmapping(mnt_userns, i_user_ns(inode)))
-               return;
-
-       count = posix_acl_fix_xattr_common(value, size);
-       if (count <= 0)
-               return;
-
-       for (end = entry + count; entry != end; entry++) {
-               switch (le16_to_cpu(entry->e_tag)) {
-               case ACL_USER:
-                       uid = make_kuid(&init_user_ns, le32_to_cpu(entry->e_id));
-                       vfsuid = make_vfsuid(mnt_userns, fs_userns, uid);
-                       entry->e_id = cpu_to_le32(from_kuid(&init_user_ns,
-                                               vfsuid_into_kuid(vfsuid)));
-                       break;
-               case ACL_GROUP:
-                       gid = make_kgid(&init_user_ns, le32_to_cpu(entry->e_id));
-                       vfsgid = make_vfsgid(mnt_userns, fs_userns, gid);
-                       entry->e_id = cpu_to_le32(from_kgid(&init_user_ns,
-                                               vfsgid_into_kgid(vfsgid)));
-                       break;
-               default:
-                       break;
-               }
-       }
-}
-
-static void posix_acl_fix_xattr_userns(
-       struct user_namespace *to, struct user_namespace *from,
-       void *value, size_t size)
-{
-       struct posix_acl_xattr_header *header = value;
-       struct posix_acl_xattr_entry *entry = (void *)(header + 1), *end;
-       int count;
-       kuid_t uid;
-       kgid_t gid;
-
-       count = posix_acl_fix_xattr_common(value, size);
-       if (count <= 0)
-               return;
-
-       for (end = entry + count; entry != end; entry++) {
-               switch(le16_to_cpu(entry->e_tag)) {
-               case ACL_USER:
-                       uid = make_kuid(from, le32_to_cpu(entry->e_id));
-                       entry->e_id = cpu_to_le32(from_kuid(to, uid));
-                       break;
-               case ACL_GROUP:
-                       gid = make_kgid(from, le32_to_cpu(entry->e_id));
-                       entry->e_id = cpu_to_le32(from_kgid(to, gid));
-                       break;
-               default:
-                       break;
-               }
-       }
-}
-
-void posix_acl_fix_xattr_from_user(void *value, size_t size)
-{
-       struct user_namespace *user_ns = current_user_ns();
-       if (user_ns == &init_user_ns)
-               return;
-       posix_acl_fix_xattr_userns(&init_user_ns, user_ns, value, size);
-}
-
-void posix_acl_fix_xattr_to_user(void *value, size_t size)
-{
-       struct user_namespace *user_ns = current_user_ns();
-       if (user_ns == &init_user_ns)
-               return;
-       posix_acl_fix_xattr_userns(user_ns, &init_user_ns, value, size);
-}
-
 /**
- * make_posix_acl - convert POSIX ACLs from uapi to VFS format using the
- *                  provided callbacks to map ACL_{GROUP,USER} entries into the
- *                  appropriate format
- * @mnt_userns: the mount's idmapping
- * @fs_userns: the filesystem's idmapping
+ * posix_acl_from_xattr - convert POSIX ACLs from backing store to VFS format
+ * @userns: the filesystem's idmapping
  * @value: the uapi representation of POSIX ACLs
  * @size: the size of @void
- * @uid_cb: callback to use for mapping the uid stored in ACL_USER entries
- * @gid_cb: callback to use for mapping the gid stored in ACL_GROUP entries
  *
- * The make_posix_acl() helper is an abstraction to translate from uapi format
- * into the VFS format allowing the caller to specific callbacks to map
- * ACL_{GROUP,USER} entries into the expected format. This is used in
- * posix_acl_from_xattr() and vfs_set_acl_prepare() and avoids pointless code
- * duplication.
+ * Filesystems that store POSIX ACLs in the unaltered uapi format should use
+ * posix_acl_from_xattr() when reading them from the backing store and
+ * converting them into the struct posix_acl VFS format. The helper is
+ * specifically intended to be called from the acl inode operation.
+ *
+ * The posix_acl_from_xattr() function will map the raw {g,u}id values stored
+ * in ACL_{GROUP,USER} entries into idmapping in @userns.
+ *
+ * Note that posix_acl_from_xattr() does not take idmapped mounts into account.
+ * If it did it calling it from the get acl inode operation would return POSIX
+ * ACLs mapped according to an idmapped mount which would mean that the value
+ * couldn't be cached for the filesystem. Idmapped mounts are taken into
+ * account on the fly during permission checking or right at the VFS -
+ * userspace boundary before reporting them to the user.
  *
  * Return: Allocated struct posix_acl on success, NULL for a valid header but
  *         without actual POSIX ACL entries, or ERR_PTR() encoded error code.
  */
-static struct posix_acl *make_posix_acl(struct user_namespace *mnt_userns,
-       struct user_namespace *fs_userns, const void *value, size_t size,
-       kuid_t (*uid_cb)(struct user_namespace *, struct user_namespace *,
-                        const struct posix_acl_xattr_entry *),
-       kgid_t (*gid_cb)(struct user_namespace *, struct user_namespace *,
-                        const struct posix_acl_xattr_entry *))
+struct posix_acl *posix_acl_from_xattr(struct user_namespace *userns,
+                                      const void *value, size_t size)
 {
        const struct posix_acl_xattr_header *header = value;
        const struct posix_acl_xattr_entry *entry = (const void *)(header + 1), *end;
@@ -874,12 +803,14 @@ static struct posix_acl *make_posix_acl(struct user_namespace *mnt_userns,
                                break;
 
                        case ACL_USER:
-                               acl_e->e_uid = uid_cb(mnt_userns, fs_userns, entry);
+                               acl_e->e_uid = make_kuid(userns,
+                                               le32_to_cpu(entry->e_id));
                                if (!uid_valid(acl_e->e_uid))
                                        goto fail;
                                break;
                        case ACL_GROUP:
-                               acl_e->e_gid = gid_cb(mnt_userns, fs_userns, entry);
+                               acl_e->e_gid = make_kgid(userns,
+                                               le32_to_cpu(entry->e_id));
                                if (!gid_valid(acl_e->e_gid))
                                        goto fail;
                                break;
@@ -894,181 +825,6 @@ fail:
        posix_acl_release(acl);
        return ERR_PTR(-EINVAL);
 }
-
-/**
- * vfs_set_acl_prepare_kuid - map ACL_USER uid according to mount- and
- *                            filesystem idmapping
- * @mnt_userns: the mount's idmapping
- * @fs_userns: the filesystem's idmapping
- * @e: a ACL_USER entry in POSIX ACL uapi format
- *
- * The uid stored as ACL_USER entry in @e is a kuid_t stored as a raw {g,u}id
- * value. The vfs_set_acl_prepare_kuid() will recover the kuid_t through
- * KUIDT_INIT() and then map it according to the idmapped mount. The resulting
- * kuid_t is the value which the filesystem can map up into a raw backing store
- * id in the filesystem's idmapping.
- *
- * This is used in vfs_set_acl_prepare() to generate the proper VFS
- * representation of POSIX ACLs with ACL_USER entries during setxattr().
- *
- * Return: A kuid in @fs_userns for the uid stored in @e.
- */
-static inline kuid_t
-vfs_set_acl_prepare_kuid(struct user_namespace *mnt_userns,
-                        struct user_namespace *fs_userns,
-                        const struct posix_acl_xattr_entry *e)
-{
-       kuid_t kuid = KUIDT_INIT(le32_to_cpu(e->e_id));
-       return from_vfsuid(mnt_userns, fs_userns, VFSUIDT_INIT(kuid));
-}
-
-/**
- * vfs_set_acl_prepare_kgid - map ACL_GROUP gid according to mount- and
- *                            filesystem idmapping
- * @mnt_userns: the mount's idmapping
- * @fs_userns: the filesystem's idmapping
- * @e: a ACL_GROUP entry in POSIX ACL uapi format
- *
- * The gid stored as ACL_GROUP entry in @e is a kgid_t stored as a raw {g,u}id
- * value. The vfs_set_acl_prepare_kgid() will recover the kgid_t through
- * KGIDT_INIT() and then map it according to the idmapped mount. The resulting
- * kgid_t is the value which the filesystem can map up into a raw backing store
- * id in the filesystem's idmapping.
- *
- * This is used in vfs_set_acl_prepare() to generate the proper VFS
- * representation of POSIX ACLs with ACL_GROUP entries during setxattr().
- *
- * Return: A kgid in @fs_userns for the gid stored in @e.
- */
-static inline kgid_t
-vfs_set_acl_prepare_kgid(struct user_namespace *mnt_userns,
-                        struct user_namespace *fs_userns,
-                        const struct posix_acl_xattr_entry *e)
-{
-       kgid_t kgid = KGIDT_INIT(le32_to_cpu(e->e_id));
-       return from_vfsgid(mnt_userns, fs_userns, VFSGIDT_INIT(kgid));
-}
-
-/**
- * vfs_set_acl_prepare - convert POSIX ACLs from uapi to VFS format taking
- *                       mount and filesystem idmappings into account
- * @mnt_userns: the mount's idmapping
- * @fs_userns: the filesystem's idmapping
- * @value: the uapi representation of POSIX ACLs
- * @size: the size of @void
- *
- * When setting POSIX ACLs with ACL_{GROUP,USER} entries they need to be
- * mapped according to the relevant mount- and filesystem idmapping. It is
- * important that the ACL_{GROUP,USER} entries in struct posix_acl will be
- * mapped into k{g,u}id_t that are supposed to be mapped up in the filesystem
- * idmapping. This is crucial since the resulting struct posix_acl might be
- * cached filesystem wide. The vfs_set_acl_prepare() function will take care to
- * perform all necessary idmappings.
- *
- * Note, that since basically forever the {g,u}id values encoded as
- * ACL_{GROUP,USER} entries in the uapi POSIX ACLs passed via @value contain
- * values that have been mapped according to the caller's idmapping. In other
- * words, POSIX ACLs passed in uapi format as @value during setxattr() contain
- * {g,u}id values in their ACL_{GROUP,USER} entries that should actually have
- * been stored as k{g,u}id_t.
- *
- * This means, vfs_set_acl_prepare() needs to first recover the k{g,u}id_t by
- * calling K{G,U}IDT_INIT(). Afterwards they can be interpreted as vfs{g,u}id_t
- * through from_vfs{g,u}id() to account for any idmapped mounts. The
- * vfs_set_acl_prepare_k{g,u}id() helpers will take care to generate the
- * correct k{g,u}id_t.
- *
- * The filesystem will then receive the POSIX ACLs ready to be cached
- * filesystem wide and ready to be written to the backing store taking the
- * filesystem's idmapping into account.
- *
- * Return: Allocated struct posix_acl on success, NULL for a valid header but
- *         without actual POSIX ACL entries, or ERR_PTR() encoded error code.
- */
-struct posix_acl *vfs_set_acl_prepare(struct user_namespace *mnt_userns,
-                                     struct user_namespace *fs_userns,
-                                     const void *value, size_t size)
-{
-       return make_posix_acl(mnt_userns, fs_userns, value, size,
-                             vfs_set_acl_prepare_kuid,
-                             vfs_set_acl_prepare_kgid);
-}
-EXPORT_SYMBOL(vfs_set_acl_prepare);
-
-/**
- * posix_acl_from_xattr_kuid - map ACL_USER uid into filesystem idmapping
- * @mnt_userns: unused
- * @fs_userns: the filesystem's idmapping
- * @e: a ACL_USER entry in POSIX ACL uapi format
- *
- * Map the uid stored as ACL_USER entry in @e into the filesystem's idmapping.
- * This is used in posix_acl_from_xattr() to generate the proper VFS
- * representation of POSIX ACLs with ACL_USER entries.
- *
- * Return: A kuid in @fs_userns for the uid stored in @e.
- */
-static inline kuid_t
-posix_acl_from_xattr_kuid(struct user_namespace *mnt_userns,
-                         struct user_namespace *fs_userns,
-                         const struct posix_acl_xattr_entry *e)
-{
-       return make_kuid(fs_userns, le32_to_cpu(e->e_id));
-}
-
-/**
- * posix_acl_from_xattr_kgid - map ACL_GROUP gid into filesystem idmapping
- * @mnt_userns: unused
- * @fs_userns: the filesystem's idmapping
- * @e: a ACL_GROUP entry in POSIX ACL uapi format
- *
- * Map the gid stored as ACL_GROUP entry in @e into the filesystem's idmapping.
- * This is used in posix_acl_from_xattr() to generate the proper VFS
- * representation of POSIX ACLs with ACL_GROUP entries.
- *
- * Return: A kgid in @fs_userns for the gid stored in @e.
- */
-static inline kgid_t
-posix_acl_from_xattr_kgid(struct user_namespace *mnt_userns,
-                         struct user_namespace *fs_userns,
-                         const struct posix_acl_xattr_entry *e)
-{
-       return make_kgid(fs_userns, le32_to_cpu(e->e_id));
-}
-
-/**
- * posix_acl_from_xattr - convert POSIX ACLs from backing store to VFS format
- * @fs_userns: the filesystem's idmapping
- * @value: the uapi representation of POSIX ACLs
- * @size: the size of @void
- *
- * Filesystems that store POSIX ACLs in the unaltered uapi format should use
- * posix_acl_from_xattr() when reading them from the backing store and
- * converting them into the struct posix_acl VFS format. The helper is
- * specifically intended to be called from the ->get_acl() inode operation.
- *
- * The posix_acl_from_xattr() function will map the raw {g,u}id values stored
- * in ACL_{GROUP,USER} entries into the filesystem idmapping in @fs_userns. The
- * posix_acl_from_xattr_k{g,u}id() helpers will take care to generate the
- * correct k{g,u}id_t. The returned struct posix_acl can be cached.
- *
- * Note that posix_acl_from_xattr() does not take idmapped mounts into account.
- * If it did it calling is from the ->get_acl() inode operation would return
- * POSIX ACLs mapped according to an idmapped mount which would mean that the
- * value couldn't be cached for the filesystem. Idmapped mounts are taken into
- * account on the fly during permission checking or right at the VFS -
- * userspace boundary before reporting them to the user.
- *
- * Return: Allocated struct posix_acl on success, NULL for a valid header but
- *         without actual POSIX ACL entries, or ERR_PTR() encoded error code.
- */
-struct posix_acl *
-posix_acl_from_xattr(struct user_namespace *fs_userns,
-                    const void *value, size_t size)
-{
-       return make_posix_acl(&init_user_ns, fs_userns, value, size,
-                             posix_acl_from_xattr_kuid,
-                             posix_acl_from_xattr_kgid);
-}
 EXPORT_SYMBOL (posix_acl_from_xattr);
 
 /*
@@ -1113,35 +869,74 @@ posix_acl_to_xattr(struct user_namespace *user_ns, const struct posix_acl *acl,
 }
 EXPORT_SYMBOL (posix_acl_to_xattr);
 
-static int
-posix_acl_xattr_get(const struct xattr_handler *handler,
-                   struct dentry *unused, struct inode *inode,
-                   const char *name, void *value, size_t size)
-{
-       struct posix_acl *acl;
-       int error;
+/**
+ * vfs_posix_acl_to_xattr - convert from kernel to userspace representation
+ * @mnt_userns: user namespace of the mount
+ * @inode: inode the posix acls are set on
+ * @acl: the posix acls as represented by the vfs
+ * @buffer: the buffer into which to convert @acl
+ * @size: size of @buffer
+ *
+ * This converts @acl from the VFS representation in the filesystem idmapping
+ * to the uapi form reportable to userspace. And mount and caller idmappings
+ * are handled appropriately.
+ *
+ * Return: On success, the size of the stored uapi posix acls, on error a
+ * negative errno.
+ */
+static ssize_t vfs_posix_acl_to_xattr(struct user_namespace *mnt_userns,
+                                     struct inode *inode,
+                                     const struct posix_acl *acl, void *buffer,
+                                     size_t size)
 
-       if (!IS_POSIXACL(inode))
-               return -EOPNOTSUPP;
-       if (S_ISLNK(inode->i_mode))
-               return -EOPNOTSUPP;
+{
+       struct posix_acl_xattr_header *ext_acl = buffer;
+       struct posix_acl_xattr_entry *ext_entry;
+       struct user_namespace *fs_userns, *caller_userns;
+       ssize_t real_size, n;
+       vfsuid_t vfsuid;
+       vfsgid_t vfsgid;
 
-       acl = get_acl(inode, handler->flags);
-       if (IS_ERR(acl))
-               return PTR_ERR(acl);
-       if (acl == NULL)
-               return -ENODATA;
+       real_size = posix_acl_xattr_size(acl->a_count);
+       if (!buffer)
+               return real_size;
+       if (real_size > size)
+               return -ERANGE;
 
-       error = posix_acl_to_xattr(&init_user_ns, acl, value, size);
-       posix_acl_release(acl);
+       ext_entry = (void *)(ext_acl + 1);
+       ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
 
-       return error;
+       fs_userns = i_user_ns(inode);
+       caller_userns = current_user_ns();
+       for (n=0; n < acl->a_count; n++, ext_entry++) {
+               const struct posix_acl_entry *acl_e = &acl->a_entries[n];
+               ext_entry->e_tag  = cpu_to_le16(acl_e->e_tag);
+               ext_entry->e_perm = cpu_to_le16(acl_e->e_perm);
+               switch(acl_e->e_tag) {
+               case ACL_USER:
+                       vfsuid = make_vfsuid(mnt_userns, fs_userns, acl_e->e_uid);
+                       ext_entry->e_id = cpu_to_le32(from_kuid(
+                               caller_userns, vfsuid_into_kuid(vfsuid)));
+                       break;
+               case ACL_GROUP:
+                       vfsgid = make_vfsgid(mnt_userns, fs_userns, acl_e->e_gid);
+                       ext_entry->e_id = cpu_to_le32(from_kgid(
+                               caller_userns, vfsgid_into_kgid(vfsgid)));
+                       break;
+               default:
+                       ext_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID);
+                       break;
+               }
+       }
+       return real_size;
 }
 
 int
-set_posix_acl(struct user_namespace *mnt_userns, struct inode *inode,
+set_posix_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
              int type, struct posix_acl *acl)
 {
+       struct inode *inode = d_inode(dentry);
+
        if (!IS_POSIXACL(inode))
                return -EOPNOTSUPP;
        if (!inode->i_op->set_acl)
@@ -1157,40 +952,10 @@ set_posix_acl(struct user_namespace *mnt_userns, struct inode *inode,
                if (ret)
                        return ret;
        }
-       return inode->i_op->set_acl(mnt_userns, inode, acl, type);
+       return inode->i_op->set_acl(mnt_userns, dentry, acl, type);
 }
 EXPORT_SYMBOL(set_posix_acl);
 
-static int
-posix_acl_xattr_set(const struct xattr_handler *handler,
-                          struct user_namespace *mnt_userns,
-                          struct dentry *unused, struct inode *inode,
-                          const char *name, const void *value, size_t size,
-                          int flags)
-{
-       struct posix_acl *acl = NULL;
-       int ret;
-
-       if (value) {
-               /*
-                * By the time we end up here the {g,u}ids stored in
-                * ACL_{GROUP,USER} have already been mapped according to the
-                * caller's idmapping. The vfs_set_acl_prepare() helper will
-                * recover them and take idmapped mounts into account. The
-                * filesystem will receive the POSIX ACLs in the correct
-                * format ready to be cached or written to the backing store
-                * taking the filesystem idmapping into account.
-                */
-               acl = vfs_set_acl_prepare(mnt_userns, i_user_ns(inode),
-                                         value, size);
-               if (IS_ERR(acl))
-                       return PTR_ERR(acl);
-       }
-       ret = set_posix_acl(mnt_userns, inode, handler->flags, acl);
-       posix_acl_release(acl);
-       return ret;
-}
-
 static bool
 posix_acl_xattr_list(struct dentry *dentry)
 {
@@ -1201,8 +966,6 @@ const struct xattr_handler posix_acl_access_xattr_handler = {
        .name = XATTR_NAME_POSIX_ACL_ACCESS,
        .flags = ACL_TYPE_ACCESS,
        .list = posix_acl_xattr_list,
-       .get = posix_acl_xattr_get,
-       .set = posix_acl_xattr_set,
 };
 EXPORT_SYMBOL_GPL(posix_acl_access_xattr_handler);
 
@@ -1210,15 +973,14 @@ const struct xattr_handler posix_acl_default_xattr_handler = {
        .name = XATTR_NAME_POSIX_ACL_DEFAULT,
        .flags = ACL_TYPE_DEFAULT,
        .list = posix_acl_xattr_list,
-       .get = posix_acl_xattr_get,
-       .set = posix_acl_xattr_set,
 };
 EXPORT_SYMBOL_GPL(posix_acl_default_xattr_handler);
 
-int simple_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int simple_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                   struct posix_acl *acl, int type)
 {
        int error;
+       struct inode *inode = d_inode(dentry);
 
        if (type == ACL_TYPE_ACCESS) {
                error = posix_acl_update_mode(mnt_userns, inode,
@@ -1252,3 +1014,252 @@ int simple_acl_create(struct inode *dir, struct inode *inode)
                posix_acl_release(acl);
        return 0;
 }
+
+static int vfs_set_acl_idmapped_mnt(struct user_namespace *mnt_userns,
+                                   struct user_namespace *fs_userns,
+                                   struct posix_acl *acl)
+{
+       for (int n = 0; n < acl->a_count; n++) {
+               struct posix_acl_entry *acl_e = &acl->a_entries[n];
+
+               switch (acl_e->e_tag) {
+               case ACL_USER:
+                       acl_e->e_uid = from_vfsuid(mnt_userns, fs_userns,
+                                                  VFSUIDT_INIT(acl_e->e_uid));
+                       break;
+               case ACL_GROUP:
+                       acl_e->e_gid = from_vfsgid(mnt_userns, fs_userns,
+                                                  VFSGIDT_INIT(acl_e->e_gid));
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * vfs_set_acl - set posix acls
+ * @mnt_userns: user namespace of the mount
+ * @dentry: the dentry based on which to set the posix acls
+ * @acl_name: the name of the posix acl
+ * @kacl: the posix acls in the appropriate VFS format
+ *
+ * This function sets @kacl. The caller must all posix_acl_release() on @kacl
+ * afterwards.
+ *
+ * Return: On success 0, on error negative errno.
+ */
+int vfs_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+               const char *acl_name, struct posix_acl *kacl)
+{
+       int acl_type;
+       int error;
+       struct inode *inode = d_inode(dentry);
+       struct inode *delegated_inode = NULL;
+
+       acl_type = posix_acl_type(acl_name);
+       if (acl_type < 0)
+               return -EINVAL;
+
+       if (kacl) {
+               /*
+                * If we're on an idmapped mount translate from mount specific
+                * vfs{g,u}id_t into global filesystem k{g,u}id_t.
+                * Afterwards we can cache the POSIX ACLs filesystem wide and -
+                * if this is a filesystem with a backing store - ultimately
+                * translate them to backing store values.
+                */
+               error = vfs_set_acl_idmapped_mnt(mnt_userns, i_user_ns(inode), kacl);
+               if (error)
+                       return error;
+       }
+
+retry_deleg:
+       inode_lock(inode);
+
+       /*
+        * We only care about restrictions the inode struct itself places upon
+        * us otherwise POSIX ACLs aren't subject to any VFS restrictions.
+        */
+       error = may_write_xattr(mnt_userns, inode);
+       if (error)
+               goto out_inode_unlock;
+
+       error = security_inode_set_acl(mnt_userns, dentry, acl_name, kacl);
+       if (error)
+               goto out_inode_unlock;
+
+       error = try_break_deleg(inode, &delegated_inode);
+       if (error)
+               goto out_inode_unlock;
+
+       if (inode->i_opflags & IOP_XATTR)
+               error = set_posix_acl(mnt_userns, dentry, acl_type, kacl);
+       else if (unlikely(is_bad_inode(inode)))
+               error = -EIO;
+       else
+               error = -EOPNOTSUPP;
+       if (!error) {
+               fsnotify_xattr(dentry);
+               evm_inode_post_set_acl(dentry, acl_name, kacl);
+       }
+
+out_inode_unlock:
+       inode_unlock(inode);
+
+       if (delegated_inode) {
+               error = break_deleg_wait(&delegated_inode);
+               if (!error)
+                       goto retry_deleg;
+       }
+
+       return error;
+}
+EXPORT_SYMBOL_GPL(vfs_set_acl);
+
+/**
+ * vfs_get_acl - get posix acls
+ * @mnt_userns: user namespace of the mount
+ * @dentry: the dentry based on which to retrieve the posix acls
+ * @acl_name: the name of the posix acl
+ *
+ * This function retrieves @kacl from the filesystem. The caller must all
+ * posix_acl_release() on @kacl.
+ *
+ * Return: On success POSIX ACLs in VFS format, on error negative errno.
+ */
+struct posix_acl *vfs_get_acl(struct user_namespace *mnt_userns,
+                             struct dentry *dentry, const char *acl_name)
+{
+       struct inode *inode = d_inode(dentry);
+       struct posix_acl *acl;
+       int acl_type, error;
+
+       acl_type = posix_acl_type(acl_name);
+       if (acl_type < 0)
+               return ERR_PTR(-EINVAL);
+
+       /*
+        * The VFS has no restrictions on reading POSIX ACLs so calling
+        * something like xattr_permission() isn't needed. Only LSMs get a say.
+        */
+       error = security_inode_get_acl(mnt_userns, dentry, acl_name);
+       if (error)
+               return ERR_PTR(error);
+
+       if (!IS_POSIXACL(inode))
+               return ERR_PTR(-EOPNOTSUPP);
+       if (S_ISLNK(inode->i_mode))
+               return ERR_PTR(-EOPNOTSUPP);
+
+       acl = __get_acl(mnt_userns, dentry, inode, acl_type);
+       if (IS_ERR(acl))
+               return acl;
+       if (!acl)
+               return ERR_PTR(-ENODATA);
+
+       return acl;
+}
+EXPORT_SYMBOL_GPL(vfs_get_acl);
+
+/**
+ * vfs_remove_acl - remove posix acls
+ * @mnt_userns: user namespace of the mount
+ * @dentry: the dentry based on which to retrieve the posix acls
+ * @acl_name: the name of the posix acl
+ *
+ * This function removes posix acls.
+ *
+ * Return: On success 0, on error negative errno.
+ */
+int vfs_remove_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+                  const char *acl_name)
+{
+       int acl_type;
+       int error;
+       struct inode *inode = d_inode(dentry);
+       struct inode *delegated_inode = NULL;
+
+       acl_type = posix_acl_type(acl_name);
+       if (acl_type < 0)
+               return -EINVAL;
+
+retry_deleg:
+       inode_lock(inode);
+
+       /*
+        * We only care about restrictions the inode struct itself places upon
+        * us otherwise POSIX ACLs aren't subject to any VFS restrictions.
+        */
+       error = may_write_xattr(mnt_userns, inode);
+       if (error)
+               goto out_inode_unlock;
+
+       error = security_inode_remove_acl(mnt_userns, dentry, acl_name);
+       if (error)
+               goto out_inode_unlock;
+
+       error = try_break_deleg(inode, &delegated_inode);
+       if (error)
+               goto out_inode_unlock;
+
+       if (inode->i_opflags & IOP_XATTR)
+               error = set_posix_acl(mnt_userns, dentry, acl_type, NULL);
+       else if (unlikely(is_bad_inode(inode)))
+               error = -EIO;
+       else
+               error = -EOPNOTSUPP;
+       if (!error) {
+               fsnotify_xattr(dentry);
+               evm_inode_post_remove_acl(mnt_userns, dentry, acl_name);
+       }
+
+out_inode_unlock:
+       inode_unlock(inode);
+
+       if (delegated_inode) {
+               error = break_deleg_wait(&delegated_inode);
+               if (!error)
+                       goto retry_deleg;
+       }
+
+       return error;
+}
+EXPORT_SYMBOL_GPL(vfs_remove_acl);
+
+int do_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+              const char *acl_name, const void *kvalue, size_t size)
+{
+       int error;
+       struct posix_acl *acl = NULL;
+
+       if (size) {
+               /*
+                * Note that posix_acl_from_xattr() uses GFP_NOFS when it
+                * probably doesn't need to here.
+                */
+               acl = posix_acl_from_xattr(current_user_ns(), kvalue, size);
+               if (IS_ERR(acl))
+                       return PTR_ERR(acl);
+       }
+
+       error = vfs_set_acl(mnt_userns, dentry, acl_name, acl);
+       posix_acl_release(acl);
+       return error;
+}
+
+ssize_t do_get_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+                  const char *acl_name, void *kvalue, size_t size)
+{
+       ssize_t error;
+       struct posix_acl *acl;
+
+       acl = vfs_get_acl(mnt_userns, dentry, acl_name);
+       if (IS_ERR(acl))
+               return PTR_ERR(acl);
+
+       error = vfs_posix_acl_to_xattr(mnt_userns, d_inode(dentry),
+                                      acl, kvalue, size);
+       posix_acl_release(acl);
+       return error;
+}
index d9052b8..29c503a 100644 (file)
@@ -49,9 +49,9 @@ static inline int reiserfs_acl_count(size_t size)
 
 #ifdef CONFIG_REISERFS_FS_POSIX_ACL
 struct posix_acl *reiserfs_get_acl(struct inode *inode, int type, bool rcu);
-int reiserfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+int reiserfs_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                     struct posix_acl *acl, int type);
-int reiserfs_acl_chmod(struct inode *inode);
+int reiserfs_acl_chmod(struct dentry *dentry);
 int reiserfs_inherit_default_acl(struct reiserfs_transaction_handle *th,
                                 struct inode *dir, struct dentry *dentry,
                                 struct inode *inode);
@@ -63,7 +63,7 @@ int reiserfs_cache_default_acl(struct inode *dir);
 #define reiserfs_get_acl NULL
 #define reiserfs_set_acl NULL
 
-static inline int reiserfs_acl_chmod(struct inode *inode)
+static inline int reiserfs_acl_chmod(struct dentry *dentry)
 {
        return 0;
 }
index 6e228bf..467d13d 100644 (file)
@@ -256,7 +256,7 @@ const struct inode_operations reiserfs_file_inode_operations = {
        .setattr = reiserfs_setattr,
        .listxattr = reiserfs_listxattr,
        .permission = reiserfs_permission,
-       .get_acl = reiserfs_get_acl,
+       .get_inode_acl = reiserfs_get_acl,
        .set_acl = reiserfs_set_acl,
        .fileattr_get = reiserfs_fileattr_get,
        .fileattr_set = reiserfs_fileattr_set,
index b9580a6..c7d1fa5 100644 (file)
@@ -3404,7 +3404,7 @@ int reiserfs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
 
        if (!error && reiserfs_posixacl(inode->i_sb)) {
                if (attr->ia_valid & ATTR_MODE)
-                       error = reiserfs_acl_chmod(inode);
+                       error = reiserfs_acl_chmod(dentry);
        }
 
 out:
index 3d7a35d..4d428e8 100644 (file)
@@ -1659,7 +1659,7 @@ const struct inode_operations reiserfs_dir_inode_operations = {
        .setattr = reiserfs_setattr,
        .listxattr = reiserfs_listxattr,
        .permission = reiserfs_permission,
-       .get_acl = reiserfs_get_acl,
+       .get_inode_acl = reiserfs_get_acl,
        .set_acl = reiserfs_set_acl,
        .fileattr_get = reiserfs_fileattr_get,
        .fileattr_set = reiserfs_fileattr_set,
@@ -1683,6 +1683,6 @@ const struct inode_operations reiserfs_special_inode_operations = {
        .setattr = reiserfs_setattr,
        .listxattr = reiserfs_listxattr,
        .permission = reiserfs_permission,
-       .get_acl = reiserfs_get_acl,
+       .get_inode_acl = reiserfs_get_acl,
        .set_acl = reiserfs_set_acl,
 };
index d6fcddc..93fe414 100644 (file)
@@ -18,7 +18,7 @@ static int __reiserfs_set_acl(struct reiserfs_transaction_handle *th,
 
 
 int
-reiserfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+reiserfs_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                 struct posix_acl *acl, int type)
 {
        int error, error2;
@@ -26,6 +26,7 @@ reiserfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
        size_t jcreate_blocks;
        int size = acl ? posix_acl_xattr_size(acl->a_count) : 0;
        int update_mode = 0;
+       struct inode *inode = d_inode(dentry);
        umode_t mode = inode->i_mode;
 
        /*
@@ -371,7 +372,7 @@ int reiserfs_cache_default_acl(struct inode *inode)
        if (IS_PRIVATE(inode))
                return 0;
 
-       acl = get_acl(inode, ACL_TYPE_DEFAULT);
+       acl = get_inode_acl(inode, ACL_TYPE_DEFAULT);
 
        if (acl && !IS_ERR(acl)) {
                int size = reiserfs_acl_size(acl->a_count);
@@ -396,13 +397,15 @@ int reiserfs_cache_default_acl(struct inode *inode)
 /*
  * Called under i_mutex
  */
-int reiserfs_acl_chmod(struct inode *inode)
+int reiserfs_acl_chmod(struct dentry *dentry)
 {
+       struct inode *inode = d_inode(dentry);
+
        if (IS_PRIVATE(inode))
                return 0;
        if (get_inode_sd_version(inode) == STAT_DATA_V1 ||
            !reiserfs_posixacl(inode->i_sb))
                return 0;
 
-       return posix_acl_chmod(&init_user_ns, inode, inode->i_mode);
+       return posix_acl_chmod(&init_user_ns, dentry, inode->i_mode);
 }
index 61107b6..df3af9f 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,17 +597,13 @@ 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,
                struct xattr_ctx *ctx)
 {
-       setxattr_convert(mnt_userns, dentry, ctx);
+       if (is_posix_acl_xattr(ctx->kname->name))
+               return do_set_acl(mnt_userns, dentry, ctx->kname->name,
+                                 ctx->kvalue, ctx->size);
+
        return vfs_setxattr(mnt_userns, dentry, ctx->kname->name,
                        ctx->kvalue, ctx->size, ctx->flags);
 }
@@ -697,10 +710,11 @@ 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(mnt_userns, d, kname, ctx->kvalue, ctx->size);
+       else
+               error = vfs_getxattr(mnt_userns, 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) {
@@ -875,6 +889,9 @@ removexattr(struct user_namespace *mnt_userns, struct dentry *d,
        if (error < 0)
                return error;
 
+       if (is_posix_acl_xattr(kname))
+               return vfs_remove_acl(mnt_userns, d, kname);
+
        return vfs_removexattr(mnt_userns, d, kname);
 }
 
index b744c62..a05f44e 100644 (file)
@@ -242,12 +242,13 @@ xfs_acl_set_mode(
 }
 
 int
-xfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+xfs_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
            struct posix_acl *acl, int type)
 {
        umode_t mode;
        bool set_mode = false;
        int error = 0;
+       struct inode *inode = d_inode(dentry);
 
        if (!acl)
                goto set_acl;
index 263404d..dcd1761 100644 (file)
@@ -11,7 +11,7 @@ struct posix_acl;
 
 #ifdef CONFIG_XFS_POSIX_ACL
 extern struct posix_acl *xfs_get_acl(struct inode *inode, int type, bool rcu);
-extern int xfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
+extern int xfs_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
                       struct posix_acl *acl, int type);
 extern int __xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type);
 void xfs_forget_acl(struct inode *inode, const char *name);
index 2e10e1c..7122383 100644 (file)
@@ -651,6 +651,7 @@ xfs_vn_change_ok(
 static int
 xfs_setattr_nonsize(
        struct user_namespace   *mnt_userns,
+       struct dentry           *dentry,
        struct xfs_inode        *ip,
        struct iattr            *iattr)
 {
@@ -757,7 +758,7 @@ xfs_setattr_nonsize(
         *           Posix ACL code seems to care about this issue either.
         */
        if (mask & ATTR_MODE) {
-               error = posix_acl_chmod(mnt_userns, inode, inode->i_mode);
+               error = posix_acl_chmod(mnt_userns, dentry, inode->i_mode);
                if (error)
                        return error;
        }
@@ -779,6 +780,7 @@ out_dqrele:
 STATIC int
 xfs_setattr_size(
        struct user_namespace   *mnt_userns,
+       struct dentry           *dentry,
        struct xfs_inode        *ip,
        struct iattr            *iattr)
 {
@@ -810,7 +812,7 @@ xfs_setattr_size(
                 * Use the regular setattr path to update the timestamps.
                 */
                iattr->ia_valid &= ~ATTR_SIZE;
-               return xfs_setattr_nonsize(mnt_userns, ip, iattr);
+               return xfs_setattr_nonsize(mnt_userns, dentry, ip, iattr);
        }
 
        /*
@@ -987,7 +989,7 @@ xfs_vn_setattr_size(
        error = xfs_vn_change_ok(mnt_userns, dentry, iattr);
        if (error)
                return error;
-       return xfs_setattr_size(mnt_userns, ip, iattr);
+       return xfs_setattr_size(mnt_userns, dentry, ip, iattr);
 }
 
 STATIC int
@@ -1019,7 +1021,7 @@ xfs_vn_setattr(
 
                error = xfs_vn_change_ok(mnt_userns, dentry, iattr);
                if (!error)
-                       error = xfs_setattr_nonsize(mnt_userns, ip, iattr);
+                       error = xfs_setattr_nonsize(mnt_userns, dentry, ip, iattr);
        }
 
        return error;
@@ -1101,7 +1103,7 @@ xfs_vn_tmpfile(
 }
 
 static const struct inode_operations xfs_inode_operations = {
-       .get_acl                = xfs_get_acl,
+       .get_inode_acl          = xfs_get_acl,
        .set_acl                = xfs_set_acl,
        .getattr                = xfs_vn_getattr,
        .setattr                = xfs_vn_setattr,
@@ -1128,7 +1130,7 @@ static const struct inode_operations xfs_dir_inode_operations = {
        .rmdir                  = xfs_vn_unlink,
        .mknod                  = xfs_vn_mknod,
        .rename                 = xfs_vn_rename,
-       .get_acl                = xfs_get_acl,
+       .get_inode_acl          = xfs_get_acl,
        .set_acl                = xfs_set_acl,
        .getattr                = xfs_vn_getattr,
        .setattr                = xfs_vn_setattr,
@@ -1155,7 +1157,7 @@ static const struct inode_operations xfs_dir_ci_inode_operations = {
        .rmdir                  = xfs_vn_unlink,
        .mknod                  = xfs_vn_mknod,
        .rename                 = xfs_vn_rename,
-       .get_acl                = xfs_get_acl,
+       .get_inode_acl          = xfs_get_acl,
        .set_acl                = xfs_set_acl,
        .getattr                = xfs_vn_getattr,
        .setattr                = xfs_vn_setattr,
index aa63e0b..7a9ee21 100644 (file)
@@ -35,6 +35,27 @@ extern int evm_inode_removexattr(struct user_namespace *mnt_userns,
                                 struct dentry *dentry, const char *xattr_name);
 extern void evm_inode_post_removexattr(struct dentry *dentry,
                                       const char *xattr_name);
+static inline void evm_inode_post_remove_acl(struct user_namespace *mnt_userns,
+                                            struct dentry *dentry,
+                                            const char *acl_name)
+{
+       evm_inode_post_removexattr(dentry, acl_name);
+}
+extern int evm_inode_set_acl(struct user_namespace *mnt_userns,
+                            struct dentry *dentry, const char *acl_name,
+                            struct posix_acl *kacl);
+static inline int evm_inode_remove_acl(struct user_namespace *mnt_userns,
+                                      struct dentry *dentry,
+                                      const char *acl_name)
+{
+       return evm_inode_set_acl(mnt_userns, dentry, acl_name, NULL);
+}
+static inline void evm_inode_post_set_acl(struct dentry *dentry,
+                                         const char *acl_name,
+                                         struct posix_acl *kacl)
+{
+       return evm_inode_post_setxattr(dentry, acl_name, NULL, 0);
+}
 extern int evm_inode_init_security(struct inode *inode,
                                   const struct xattr *xattr_array,
                                   struct xattr *evm);
@@ -108,6 +129,34 @@ static inline void evm_inode_post_removexattr(struct dentry *dentry,
        return;
 }
 
+static inline void evm_inode_post_remove_acl(struct user_namespace *mnt_userns,
+                                            struct dentry *dentry,
+                                            const char *acl_name)
+{
+       return;
+}
+
+static inline int evm_inode_set_acl(struct user_namespace *mnt_userns,
+                                   struct dentry *dentry, const char *acl_name,
+                                   struct posix_acl *kacl)
+{
+       return 0;
+}
+
+static inline int evm_inode_remove_acl(struct user_namespace *mnt_userns,
+                                      struct dentry *dentry,
+                                      const char *acl_name)
+{
+       return 0;
+}
+
+static inline void evm_inode_post_set_acl(struct dentry *dentry,
+                                         const char *acl_name,
+                                         struct posix_acl *kacl)
+{
+       return;
+}
+
 static inline int evm_inode_init_security(struct inode *inode,
                                          const struct xattr *xattr_array,
                                          struct xattr *evm)
index 1e469b8..d134731 100644 (file)
@@ -560,8 +560,8 @@ struct posix_acl;
 #define ACL_NOT_CACHED ((void *)(-1))
 /*
  * ACL_DONT_CACHE is for stacked filesystems, that rely on underlying fs to
- * cache the ACL.  This also means that ->get_acl() can be called in RCU mode
- * with the LOOKUP_RCU flag.
+ * cache the ACL.  This also means that ->get_inode_acl() can be called in RCU
+ * mode with the LOOKUP_RCU flag.
  */
 #define ACL_DONT_CACHE ((void *)(-3))
 
@@ -2169,7 +2169,7 @@ struct inode_operations {
        struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
        const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
        int (*permission) (struct user_namespace *, struct inode *, int);
-       struct posix_acl * (*get_acl)(struct inode *, int, bool);
+       struct posix_acl * (*get_inode_acl)(struct inode *, int, bool);
 
        int (*readlink) (struct dentry *, char __user *,int);
 
@@ -2199,7 +2199,9 @@ struct inode_operations {
                           umode_t create_mode);
        int (*tmpfile) (struct user_namespace *, struct inode *,
                        struct file *, umode_t);
-       int (*set_acl)(struct user_namespace *, struct inode *,
+       struct posix_acl *(*get_acl)(struct user_namespace *, struct dentry *,
+                                    int);
+       int (*set_acl)(struct user_namespace *, struct dentry *,
                       struct posix_acl *, int);
        int (*fileattr_set)(struct user_namespace *mnt_userns,
                            struct dentry *dentry, struct fileattr *fa);
index 81708ca..5a0b2a2 100644 (file)
@@ -187,6 +187,15 @@ extern void ima_inode_post_setattr(struct user_namespace *mnt_userns,
                                   struct dentry *dentry);
 extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
                       const void *xattr_value, size_t xattr_value_len);
+extern int ima_inode_set_acl(struct user_namespace *mnt_userns,
+                            struct dentry *dentry, const char *acl_name,
+                            struct posix_acl *kacl);
+static inline int ima_inode_remove_acl(struct user_namespace *mnt_userns,
+                                      struct dentry *dentry,
+                                      const char *acl_name)
+{
+       return ima_inode_set_acl(mnt_userns, dentry, acl_name, NULL);
+}
 extern int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name);
 #else
 static inline bool is_ima_appraise_enabled(void)
@@ -208,11 +217,26 @@ static inline int ima_inode_setxattr(struct dentry *dentry,
        return 0;
 }
 
+static inline int ima_inode_set_acl(struct user_namespace *mnt_userns,
+                                   struct dentry *dentry, const char *acl_name,
+                                   struct posix_acl *kacl)
+{
+
+       return 0;
+}
+
 static inline int ima_inode_removexattr(struct dentry *dentry,
                                        const char *xattr_name)
 {
        return 0;
 }
+
+static inline int ima_inode_remove_acl(struct user_namespace *mnt_userns,
+                                      struct dentry *dentry,
+                                      const char *acl_name)
+{
+       return 0;
+}
 #endif /* CONFIG_IMA_APPRAISE */
 
 #if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
index ec119da..7f4aadd 100644 (file)
@@ -145,6 +145,12 @@ LSM_HOOK(int, 0, inode_getxattr, struct dentry *dentry, const char *name)
 LSM_HOOK(int, 0, inode_listxattr, struct dentry *dentry)
 LSM_HOOK(int, 0, inode_removexattr, struct user_namespace *mnt_userns,
         struct dentry *dentry, const char *name)
+LSM_HOOK(int, 0, inode_set_acl, struct user_namespace *mnt_userns,
+        struct dentry *dentry, const char *acl_name, struct posix_acl *kacl)
+LSM_HOOK(int, 0, inode_get_acl, struct user_namespace *mnt_userns,
+        struct dentry *dentry, const char *acl_name)
+LSM_HOOK(int, 0, inode_remove_acl, struct user_namespace *mnt_userns,
+        struct dentry *dentry, const char *acl_name)
 LSM_HOOK(int, 0, inode_need_killpriv, struct dentry *dentry)
 LSM_HOOK(int, 0, inode_killpriv, struct user_namespace *mnt_userns,
         struct dentry *dentry)
index 4ec80b9..1d02d11 100644 (file)
  *     Check permission before removing the extended attribute
  *     identified by @name for @dentry.
  *     Return 0 if permission is granted.
+ * @inode_set_acl:
+ *     Check permission before setting posix acls
+ *     The posix acls in @kacl are identified by @acl_name.
+ *     Return 0 if permission is granted.
+ * @inode_get_acl:
+ *     Check permission before getting osix acls
+ *     The posix acls are identified by @acl_name.
+ *     Return 0 if permission is granted.
+ * @inode_remove_acl:
+ *     Check permission before removing posix acls
+ *     The posix acls are identified by @acl_name.
+ *     Return 0 if permission is granted.
  * @inode_getsecurity:
  *     Retrieve a copy of the extended attribute representation of the
  *     security label associated with @name for @inode via @buffer.  Note that
index 7d1e604..ee608d2 100644 (file)
@@ -69,21 +69,21 @@ extern int __posix_acl_create(struct posix_acl **, gfp_t, umode_t *);
 extern int __posix_acl_chmod(struct posix_acl **, gfp_t, umode_t);
 
 extern struct posix_acl *get_posix_acl(struct inode *, int);
-extern int set_posix_acl(struct user_namespace *, struct inode *, int,
-                        struct posix_acl *);
+int set_posix_acl(struct user_namespace *, struct dentry *, int,
+                 struct posix_acl *);
 
 struct posix_acl *get_cached_acl_rcu(struct inode *inode, int type);
 struct posix_acl *posix_acl_clone(const struct posix_acl *acl, gfp_t flags);
 
 #ifdef CONFIG_FS_POSIX_ACL
-int posix_acl_chmod(struct user_namespace *, struct inode *, umode_t);
+int posix_acl_chmod(struct user_namespace *, struct dentry *, umode_t);
 extern int posix_acl_create(struct inode *, umode_t *, struct posix_acl **,
                struct posix_acl **);
 int posix_acl_update_mode(struct user_namespace *, struct inode *, umode_t *,
                          struct posix_acl **);
 
-extern int simple_set_acl(struct user_namespace *, struct inode *,
-                         struct posix_acl *, int);
+int simple_set_acl(struct user_namespace *, struct dentry *,
+                  struct posix_acl *, int);
 extern int simple_acl_create(struct inode *, struct inode *);
 
 struct posix_acl *get_cached_acl(struct inode *inode, int type);
@@ -99,9 +99,16 @@ static inline void cache_no_acl(struct inode *inode)
        inode->i_acl = NULL;
        inode->i_default_acl = NULL;
 }
+
+int vfs_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+               const char *acl_name, struct posix_acl *kacl);
+struct posix_acl *vfs_get_acl(struct user_namespace *mnt_userns,
+                             struct dentry *dentry, const char *acl_name);
+int vfs_remove_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+                  const char *acl_name);
 #else
 static inline int posix_acl_chmod(struct user_namespace *mnt_userns,
-                                 struct inode *inode, umode_t mode)
+                                 struct dentry *dentry, umode_t mode)
 {
        return 0;
 }
@@ -126,8 +133,28 @@ static inline int posix_acl_create(struct inode *inode, umode_t *mode,
 static inline void forget_all_cached_acls(struct inode *inode)
 {
 }
+
+static inline int vfs_set_acl(struct user_namespace *mnt_userns,
+                             struct dentry *dentry, const char *name,
+                             struct posix_acl *acl)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline struct posix_acl *vfs_get_acl(struct user_namespace *mnt_userns,
+                                           struct dentry *dentry,
+                                           const char *acl_name)
+{
+       return ERR_PTR(-EOPNOTSUPP);
+}
+
+static inline int vfs_remove_acl(struct user_namespace *mnt_userns,
+                                struct dentry *dentry, const char *acl_name)
+{
+       return -EOPNOTSUPP;
+}
 #endif /* CONFIG_FS_POSIX_ACL */
 
-struct posix_acl *get_acl(struct inode *inode, int type);
+struct posix_acl *get_inode_acl(struct inode *inode, int type);
 
 #endif  /* __LINUX_POSIX_ACL_H */
index 8163dd4..54cd7a1 100644 (file)
@@ -33,33 +33,40 @@ posix_acl_xattr_count(size_t size)
 }
 
 #ifdef CONFIG_FS_POSIX_ACL
-void posix_acl_fix_xattr_from_user(void *value, size_t size);
-void posix_acl_fix_xattr_to_user(void *value, size_t size);
-void posix_acl_getxattr_idmapped_mnt(struct user_namespace *mnt_userns,
-                                    const struct inode *inode,
-                                    void *value, size_t size);
+struct posix_acl *posix_acl_from_xattr(struct user_namespace *user_ns,
+                                      const void *value, size_t size);
 #else
-static inline void posix_acl_fix_xattr_from_user(void *value, size_t size)
-{
-}
-static inline void posix_acl_fix_xattr_to_user(void *value, size_t size)
-{
-}
-static inline void
-posix_acl_getxattr_idmapped_mnt(struct user_namespace *mnt_userns,
-                               const struct inode *inode, void *value,
-                               size_t size)
+static inline struct posix_acl *
+posix_acl_from_xattr(struct user_namespace *user_ns, const void *value,
+                    size_t size)
 {
+       return ERR_PTR(-EOPNOTSUPP);
 }
 #endif
 
-struct posix_acl *posix_acl_from_xattr(struct user_namespace *user_ns, 
-                                      const void *value, size_t size);
 int posix_acl_to_xattr(struct user_namespace *user_ns,
                       const struct posix_acl *acl, void *buffer, size_t size);
-struct posix_acl *vfs_set_acl_prepare(struct user_namespace *mnt_userns,
-                                     struct user_namespace *fs_userns,
-                                     const void *value, size_t size);
+static inline const char *posix_acl_xattr_name(int type)
+{
+       switch (type) {
+       case ACL_TYPE_ACCESS:
+               return XATTR_NAME_POSIX_ACL_ACCESS;
+       case ACL_TYPE_DEFAULT:
+               return XATTR_NAME_POSIX_ACL_DEFAULT;
+       }
+
+       return "";
+}
+
+static inline int posix_acl_type(const char *name)
+{
+       if (strcmp(name, XATTR_NAME_POSIX_ACL_ACCESS) == 0)
+               return ACL_TYPE_ACCESS;
+       else if (strcmp(name, XATTR_NAME_POSIX_ACL_DEFAULT) == 0)
+               return ACL_TYPE_DEFAULT;
+
+       return -1;
+}
 
 extern const struct xattr_handler posix_acl_access_xattr_handler;
 extern const struct xattr_handler posix_acl_default_xattr_handler;
index ca1b710..2bfc2e1 100644 (file)
@@ -361,6 +361,13 @@ int security_inode_getattr(const struct path *path);
 int security_inode_setxattr(struct user_namespace *mnt_userns,
                            struct dentry *dentry, const char *name,
                            const void *value, size_t size, int flags);
+int security_inode_set_acl(struct user_namespace *mnt_userns,
+                          struct dentry *dentry, const char *acl_name,
+                          struct posix_acl *kacl);
+int security_inode_get_acl(struct user_namespace *mnt_userns,
+                          struct dentry *dentry, const char *acl_name);
+int security_inode_remove_acl(struct user_namespace *mnt_userns,
+                             struct dentry *dentry, const char *acl_name);
 void security_inode_post_setxattr(struct dentry *dentry, const char *name,
                                  const void *value, size_t size, int flags);
 int security_inode_getxattr(struct dentry *dentry, const char *name);
@@ -872,6 +879,28 @@ static inline int security_inode_setxattr(struct user_namespace *mnt_userns,
        return cap_inode_setxattr(dentry, name, value, size, flags);
 }
 
+static inline int security_inode_set_acl(struct user_namespace *mnt_userns,
+                                        struct dentry *dentry,
+                                        const char *acl_name,
+                                        struct posix_acl *kacl)
+{
+       return 0;
+}
+
+static inline int security_inode_get_acl(struct user_namespace *mnt_userns,
+                                        struct dentry *dentry,
+                                        const char *acl_name)
+{
+       return 0;
+}
+
+static inline int security_inode_remove_acl(struct user_namespace *mnt_userns,
+                                           struct dentry *dentry,
+                                           const char *acl_name)
+{
+       return 0;
+}
+
 static inline void security_inode_post_setxattr(struct dentry *dentry,
                const char *name, const void *value, size_t size, int flags)
 { }
index 4c379d2..c523874 100644 (file)
 struct inode;
 struct dentry;
 
+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);
+}
+
 /*
  * struct xattr_handler: When @name is set, match attributes with exactly that
  * name.  When @prefix is set instead, match attributes with that prefix and
index 82911fe..c45eaf0 100644 (file)
@@ -1132,7 +1132,7 @@ static int shmem_setattr(struct user_namespace *mnt_userns,
 
        setattr_copy(&init_user_ns, inode, attr);
        if (attr->ia_valid & ATTR_MODE)
-               error = posix_acl_chmod(&init_user_ns, inode, inode->i_mode);
+               error = posix_acl_chmod(&init_user_ns, dentry, inode->i_mode);
        if (!error && update_ctime) {
                inode->i_ctime = current_time(inode);
                if (update_mtime)
index 23d484e..e01cfd4 100644 (file)
@@ -8,7 +8,7 @@
  *
  * File: evm_main.c
  *     implements evm_inode_setxattr, evm_inode_post_setxattr,
- *     evm_inode_removexattr, and evm_verifyxattr
+ *     evm_inode_removexattr, evm_verifyxattr, and evm_inode_set_acl.
  */
 
 #define pr_fmt(fmt) "EVM: "fmt
@@ -434,66 +434,6 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry)
        return evm_verify_hmac(dentry, NULL, NULL, 0, NULL);
 }
 
-/*
- * evm_xattr_acl_change - check if passed ACL changes the inode mode
- * @mnt_userns: user namespace of the idmapped mount
- * @dentry: pointer to the affected dentry
- * @xattr_name: requested xattr
- * @xattr_value: requested xattr value
- * @xattr_value_len: requested xattr value length
- *
- * Check if passed ACL changes the inode mode, which is protected by EVM.
- *
- * Returns 1 if passed ACL causes inode mode change, 0 otherwise.
- */
-static int evm_xattr_acl_change(struct user_namespace *mnt_userns,
-                               struct dentry *dentry, const char *xattr_name,
-                               const void *xattr_value, size_t xattr_value_len)
-{
-#ifdef CONFIG_FS_POSIX_ACL
-       umode_t mode;
-       struct posix_acl *acl = NULL, *acl_res;
-       struct inode *inode = d_backing_inode(dentry);
-       int rc;
-
-       /*
-        * An earlier comment here mentioned that the idmappings for
-        * ACL_{GROUP,USER} don't matter since EVM is only interested in the
-        * mode stored as part of POSIX ACLs. Nonetheless, if it must translate
-        * from the uapi POSIX ACL representation to the VFS internal POSIX ACL
-        * representation it should do so correctly. There's no guarantee that
-        * we won't change POSIX ACLs in a way that ACL_{GROUP,USER} matters
-        * for the mode at some point and it's difficult to keep track of all
-        * the LSM and integrity modules and what they do to POSIX ACLs.
-        *
-        * Frankly, EVM shouldn't try to interpret the uapi struct for POSIX
-        * ACLs it received. It requires knowledge that only the VFS is
-        * guaranteed to have.
-        */
-       acl = vfs_set_acl_prepare(mnt_userns, i_user_ns(inode),
-                                 xattr_value, xattr_value_len);
-       if (IS_ERR_OR_NULL(acl))
-               return 1;
-
-       acl_res = acl;
-       /*
-        * Passing mnt_userns is necessary to correctly determine the GID in
-        * an idmapped mount, as the GID is used to clear the setgid bit in
-        * the inode mode.
-        */
-       rc = posix_acl_update_mode(mnt_userns, inode, &mode, &acl_res);
-
-       posix_acl_release(acl);
-
-       if (rc)
-               return 1;
-
-       if (inode->i_mode != mode)
-               return 1;
-#endif
-       return 0;
-}
-
 /*
  * evm_xattr_change - check if passed xattr value differs from current value
  * @mnt_userns: user namespace of the idmapped mount
@@ -513,10 +453,6 @@ static int evm_xattr_change(struct user_namespace *mnt_userns,
        char *xattr_data = NULL;
        int rc = 0;
 
-       if (posix_xattr_acl(xattr_name))
-               return evm_xattr_acl_change(mnt_userns, dentry, xattr_name,
-                                           xattr_value, xattr_value_len);
-
        rc = vfs_getxattr_alloc(&init_user_ns, dentry, xattr_name, &xattr_data,
                                0, GFP_NOFS);
        if (rc < 0)
@@ -670,6 +606,86 @@ int evm_inode_removexattr(struct user_namespace *mnt_userns,
        return evm_protect_xattr(mnt_userns, dentry, xattr_name, NULL, 0);
 }
 
+#ifdef CONFIG_FS_POSIX_ACL
+static int evm_inode_set_acl_change(struct user_namespace *mnt_userns,
+                                   struct dentry *dentry, const char *name,
+                                   struct posix_acl *kacl)
+{
+       int rc;
+
+       umode_t mode;
+       struct inode *inode = d_backing_inode(dentry);
+
+       if (!kacl)
+               return 1;
+
+       rc = posix_acl_update_mode(mnt_userns, inode, &mode, &kacl);
+       if (rc || (inode->i_mode != mode))
+               return 1;
+
+       return 0;
+}
+#else
+static inline int evm_inode_set_acl_change(struct user_namespace *mnt_userns,
+                                          struct dentry *dentry,
+                                          const char *name,
+                                          struct posix_acl *kacl)
+{
+       return 0;
+}
+#endif
+
+/**
+ * evm_inode_set_acl - protect the EVM extended attribute from posix acls
+ * @mnt_userns: user namespace of the idmapped mount
+ * @dentry: pointer to the affected dentry
+ * @acl_name: name of the posix acl
+ * @kacl: pointer to the posix acls
+ *
+ * Prevent modifying posix acls causing the EVM HMAC to be re-calculated
+ * and 'security.evm' xattr updated, unless the existing 'security.evm' is
+ * valid.
+ */
+int evm_inode_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+                     const char *acl_name, struct posix_acl *kacl)
+{
+       enum integrity_status evm_status;
+
+       /* Policy permits modification of the protected xattrs even though
+        * there's no HMAC key loaded
+        */
+       if (evm_initialized & EVM_ALLOW_METADATA_WRITES)
+               return 0;
+
+       evm_status = evm_verify_current_integrity(dentry);
+       if ((evm_status == INTEGRITY_PASS) ||
+           (evm_status == INTEGRITY_NOXATTRS))
+               return 0;
+
+       /* Exception if the HMAC is not going to be calculated. */
+       if (evm_hmac_disabled() && (evm_status == INTEGRITY_NOLABEL ||
+           evm_status == INTEGRITY_UNKNOWN))
+               return 0;
+
+       /*
+        * Writing other xattrs is safe for portable signatures, as portable
+        * signatures are immutable and can never be updated.
+        */
+       if (evm_status == INTEGRITY_FAIL_IMMUTABLE)
+               return 0;
+
+       if (evm_status == INTEGRITY_PASS_IMMUTABLE &&
+           !evm_inode_set_acl_change(mnt_userns, dentry, acl_name, kacl))
+               return 0;
+
+       if (evm_status != INTEGRITY_PASS_IMMUTABLE)
+               integrity_audit_msg(AUDIT_INTEGRITY_METADATA, d_backing_inode(dentry),
+                                   dentry->d_name.name, "appraise_metadata",
+                                   integrity_status_msg[evm_status],
+                                   -EPERM, 0);
+       return -EPERM;
+}
+
 static void evm_reset_status(struct inode *inode)
 {
        struct integrity_iint_cache *iint;
index 3e0fbbd..3c9af3d 100644 (file)
@@ -774,6 +774,15 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
        return result;
 }
 
+int ima_inode_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
+                     const char *acl_name, struct posix_acl *kacl)
+{
+       if (evm_revalidate_status(acl_name))
+               ima_reset_appraise_flags(d_backing_inode(dentry), 0);
+
+       return 0;
+}
+
 int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
 {
        int result;
index 79d82cb..bdc295a 100644 (file)
@@ -1372,6 +1372,48 @@ int security_inode_setxattr(struct user_namespace *mnt_userns,
        return evm_inode_setxattr(mnt_userns, dentry, name, value, size);
 }
 
+int security_inode_set_acl(struct user_namespace *mnt_userns,
+                          struct dentry *dentry, const char *acl_name,
+                          struct posix_acl *kacl)
+{
+       int ret;
+
+       if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+               return 0;
+       ret = call_int_hook(inode_set_acl, 0, mnt_userns, dentry, acl_name,
+                           kacl);
+       if (ret)
+               return ret;
+       ret = ima_inode_set_acl(mnt_userns, dentry, acl_name, kacl);
+       if (ret)
+               return ret;
+       return evm_inode_set_acl(mnt_userns, dentry, acl_name, kacl);
+}
+
+int security_inode_get_acl(struct user_namespace *mnt_userns,
+                          struct dentry *dentry, const char *acl_name)
+{
+       if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+               return 0;
+       return call_int_hook(inode_get_acl, 0, mnt_userns, dentry, acl_name);
+}
+
+int security_inode_remove_acl(struct user_namespace *mnt_userns,
+                             struct dentry *dentry, const char *acl_name)
+{
+       int ret;
+
+       if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+               return 0;
+       ret = call_int_hook(inode_remove_acl, 0, mnt_userns, dentry, acl_name);
+       if (ret)
+               return ret;
+       ret = ima_inode_remove_acl(mnt_userns, dentry, acl_name);
+       if (ret)
+               return ret;
+       return evm_inode_remove_acl(mnt_userns, dentry, acl_name);
+}
+
 void security_inode_post_setxattr(struct dentry *dentry, const char *name,
                                  const void *value, size_t size, int flags)
 {
index f553c37..7c5c8d1 100644 (file)
@@ -3240,6 +3240,25 @@ static int selinux_inode_setxattr(struct user_namespace *mnt_userns,
                            &ad);
 }
 
+static int selinux_inode_set_acl(struct user_namespace *mnt_userns,
+                                struct dentry *dentry, const char *acl_name,
+                                struct posix_acl *kacl)
+{
+       return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
+}
+
+static int selinux_inode_get_acl(struct user_namespace *mnt_userns,
+                                struct dentry *dentry, const char *acl_name)
+{
+       return dentry_has_perm(current_cred(), dentry, FILE__GETATTR);
+}
+
+static int selinux_inode_remove_acl(struct user_namespace *mnt_userns,
+                                   struct dentry *dentry, const char *acl_name)
+{
+       return dentry_has_perm(current_cred(), dentry, FILE__SETATTR);
+}
+
 static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
                                        const void *value, size_t size,
                                        int flags)
@@ -7088,6 +7107,9 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
        LSM_HOOK_INIT(inode_getxattr, selinux_inode_getxattr),
        LSM_HOOK_INIT(inode_listxattr, selinux_inode_listxattr),
        LSM_HOOK_INIT(inode_removexattr, selinux_inode_removexattr),
+       LSM_HOOK_INIT(inode_set_acl, selinux_inode_set_acl),
+       LSM_HOOK_INIT(inode_get_acl, selinux_inode_get_acl),
+       LSM_HOOK_INIT(inode_remove_acl, selinux_inode_remove_acl),
        LSM_HOOK_INIT(inode_getsecurity, selinux_inode_getsecurity),
        LSM_HOOK_INIT(inode_setsecurity, selinux_inode_setsecurity),
        LSM_HOOK_INIT(inode_listsecurity, selinux_inode_listsecurity),
index b6306d7..cadef2f 100644 (file)
@@ -1392,6 +1392,74 @@ static int smack_inode_removexattr(struct user_namespace *mnt_userns,
        return 0;
 }
 
+/**
+ * smack_inode_set_acl - Smack check for setting posix acls
+ * @mnt_userns: the userns attached to the mnt this request came from
+ * @dentry: the object
+ * @acl_name: name of the posix acl
+ * @kacl: the posix acls
+ *
+ * Returns 0 if access is permitted, an error code otherwise
+ */
+static int smack_inode_set_acl(struct user_namespace *mnt_userns,
+                              struct dentry *dentry, const char *acl_name,
+                              struct posix_acl *kacl)
+{
+       struct smk_audit_info ad;
+       int rc;
+
+       smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
+       smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
+
+       rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad);
+       rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc);
+       return rc;
+}
+
+/**
+ * smack_inode_get_acl - Smack check for getting posix acls
+ * @mnt_userns: the userns attached to the mnt this request came from
+ * @dentry: the object
+ * @acl_name: name of the posix acl
+ *
+ * Returns 0 if access is permitted, an error code otherwise
+ */
+static int smack_inode_get_acl(struct user_namespace *mnt_userns,
+                              struct dentry *dentry, const char *acl_name)
+{
+       struct smk_audit_info ad;
+       int rc;
+
+       smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
+       smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
+
+       rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_READ, &ad);
+       rc = smk_bu_inode(d_backing_inode(dentry), MAY_READ, rc);
+       return rc;
+}
+
+/**
+ * smack_inode_remove_acl - Smack check for getting posix acls
+ * @mnt_userns: the userns attached to the mnt this request came from
+ * @dentry: the object
+ * @acl_name: name of the posix acl
+ *
+ * Returns 0 if access is permitted, an error code otherwise
+ */
+static int smack_inode_remove_acl(struct user_namespace *mnt_userns,
+                                 struct dentry *dentry, const char *acl_name)
+{
+       struct smk_audit_info ad;
+       int rc;
+
+       smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY);
+       smk_ad_setfield_u_fs_path_dentry(&ad, dentry);
+
+       rc = smk_curacc(smk_of_inode(d_backing_inode(dentry)), MAY_WRITE, &ad);
+       rc = smk_bu_inode(d_backing_inode(dentry), MAY_WRITE, rc);
+       return rc;
+}
+
 /**
  * smack_inode_getsecurity - get smack xattrs
  * @mnt_userns: active user namespace
@@ -4816,6 +4884,9 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
        LSM_HOOK_INIT(inode_post_setxattr, smack_inode_post_setxattr),
        LSM_HOOK_INIT(inode_getxattr, smack_inode_getxattr),
        LSM_HOOK_INIT(inode_removexattr, smack_inode_removexattr),
+       LSM_HOOK_INIT(inode_set_acl, smack_inode_set_acl),
+       LSM_HOOK_INIT(inode_get_acl, smack_inode_get_acl),
+       LSM_HOOK_INIT(inode_remove_acl, smack_inode_remove_acl),
        LSM_HOOK_INIT(inode_getsecurity, smack_inode_getsecurity),
        LSM_HOOK_INIT(inode_setsecurity, smack_inode_setsecurity),
        LSM_HOOK_INIT(inode_listsecurity, smack_inode_listsecurity),