Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752542AbbBZFoz (ORCPT ); Thu, 26 Feb 2015 00:44:55 -0500 Received: from mail-oi0-f52.google.com ([209.85.218.52]:54907 "EHLO mail-oi0-f52.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750737AbbBZFov (ORCPT ); Thu, 26 Feb 2015 00:44:51 -0500 MIME-Version: 1.0 In-Reply-To: References: From: Michael Kerrisk Date: Thu, 26 Feb 2015 06:44:29 +0100 X-Google-Sender-Auth: __h302biVsnpxJNodYEkzpZh_Tg Message-ID: Subject: Re: [RFC 00/21] Richacls To: Andreas Gruenbacher Cc: Linux Kernel , Linux-Fsdevel , linux-nfs@vger.kernel.org, Linux API Content-Type: text/plain; charset=UTF-8 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 150905 Lines: 3979 [CC += linux-api] Hi Andreas, Since this patch series implements kernel-user-space API changes, could you please CC linux-api@ on future iterations. (The kernel source file Documentation/SubmitChecklist notes that all Linux kernel patches that change userspace interfaces should be CCed to linux-api@vger.kernel.org, so that the various parties who are interested in API changes are informed. See https://www.kernel.org/doc/man-pages/linux-api-ml.html ) Cheers, Michael On Thu, Feb 26, 2015 at 12:31 AM, Andreas Gruenbacher wrote: > Hello, > > here is an updated richacl patch queue, also available in git [1]. For those > who might not know, richacls are an implementation of NFSv4 ACLs that cleanly > integrates into the POSIX file permission model. The goal is to improve > interoperability between Linux and other systems, mainly across the NFSv4 and > CIFS/SMB protocols. A file system can either contain posix acls or richacls, > but not both. > > This patch queue includes the vfs and ext4 changes needed for local richacl > support. A previous version of this patch queue was last posted about a year > ago [2]; I have updated the patches to v4.0-rc1 and tried to incorporate the > feedback from the previous discussion. The changes include: > > * Introduction of a base_acl object type so that an inode can either cache > a posix acl or a richacl largely without caring which of the two kinds > it is dealing with. > > * RCU support as for posix acls. > > * Various cleanups and more documentation. > > Things I'm not entirely happy with: > > * A new get_richacl inode operation is introduced. This is needed because > we need to perform permission checks in contexts where the dentry of the > inode to check is not available and we cannot use the getxattr inode > operation. It would be nice if we could either convert the getxattr inode > operation to take an inode instead, or pass the dentries down to where > the get_richacl inode operation is currently used. > > * The base_acl code is rather ugly; maybe the previous version which was > criticized wasn't so bad after all. > > * It would be nice if the MAY_DELETE_SELF flag could override the sticky > directory check as it did in the previous version of this patch queue. I > couldn't come up with a clean way of achieving that, though. > > Because the code has changed quite a bit since the last posting, I have removed > the previous sign-offs. > > At this point, I would like to ask for your feedback as to what should be > changed before these patches can be merged, even if merging these patches alone > doesn't make a while lot of sense. I will follow up with additional pieces to > the puzzle like the nfsv4 support as I get them into shape again. > > -- > > Which kind of acls an ext4 file system supports is determined by the "richacl" > ext4 feature (mkfs.ext4 -O richacl or tune2fs -O richacl). The file system > also needs to be mounted with the "acl" mount option, which is the default > nowadays. > > A version of e2fsprogs with support for the "richacl" feature can be found on > github [3], but the feature can also be enabled "hard" in debugfs. Note that > unpatched versions of e2fsck will not check file systems with the feature > enabled though. > > The acls themselves can be manipulated with the richacl command-line utility > [4]. Some details on the permission model and examples of its use can be found > at the richacl page, http://acl.bestbits.at/richacl/. > > [1] git://git.kernel.org/pub/scm/linux/kernel/git/agruen/linux-richacl.git richacl > [2] http://lwn.net/Articles/596517/ > [3] https://github.com/andreas-gruenbacher/e2fsprogs > [4] https://github.com/andreas-gruenbacher/richacl > > Thanks, > Andreas > > -- > > Andreas Gruenbacher (19): > vfs: Minor documentation fix > vfs: Shrink struct posix_acl > vfs: Add IS_ACL() and IS_RICHACL() tests > vfs: Add MAY_CREATE_FILE and MAY_CREATE_DIR permission flags > vfs: Add MAY_DELETE_SELF and MAY_DELETE_CHILD permission flags > vfs: Make the inode passed to inode_change_ok non-const > vfs: Add permission flags for setting file attributes > richacl: In-memory representation and helper functions > richacl: Permission mapping functions > richacl: Compute maximum file masks from an acl > richacl: Update the file masks in chmod() > richacl: Permission check algorithm > richacl: Create-time inheritance > richacl: Check if an acl is equivalent to a file mode > richacl: Automatic Inheritance > richacl: xattr mapping functions > vfs: Cache base_acl objects in inodes > vfs: Cache richacl in struct inode > vfs: Add richacl permission checking > > Aneesh Kumar K.V (2): > ext4: Implement rich acl for ext4 > ext4: Add richacl feature flag > > Documentation/filesystems/porting | 8 +- > Documentation/filesystems/vfs.txt | 3 + > drivers/staging/lustre/lustre/llite/llite_lib.c | 2 +- > fs/Kconfig | 3 + > fs/Makefile | 2 + > fs/attr.c | 81 ++- > fs/ext4/Kconfig | 15 + > fs/ext4/Makefile | 1 + > fs/ext4/acl.c | 7 +- > fs/ext4/acl.h | 12 +- > fs/ext4/ext4.h | 6 +- > fs/ext4/file.c | 6 +- > fs/ext4/ialloc.c | 7 +- > fs/ext4/inode.c | 10 +- > fs/ext4/namei.c | 11 +- > fs/ext4/richacl.c | 229 ++++++++ > fs/ext4/richacl.h | 47 ++ > fs/ext4/super.c | 41 +- > fs/ext4/xattr.c | 6 + > fs/ext4/xattr.h | 1 + > fs/f2fs/acl.c | 4 +- > fs/inode.c | 15 +- > fs/namei.c | 108 +++- > fs/posix_acl.c | 31 +- > fs/richacl_base.c | 660 ++++++++++++++++++++++++ > fs/richacl_inode.c | 67 +++ > fs/richacl_xattr.c | 131 +++++ > include/linux/fs.h | 47 +- > include/linux/posix_acl.h | 12 +- > include/linux/richacl.h | 329 ++++++++++++ > include/linux/richacl_xattr.h | 47 ++ > include/uapi/linux/fs.h | 3 +- > 32 files changed, 1844 insertions(+), 108 deletions(-) > create mode 100644 fs/ext4/richacl.c > create mode 100644 fs/ext4/richacl.h > create mode 100644 fs/richacl_base.c > create mode 100644 fs/richacl_inode.c > create mode 100644 fs/richacl_xattr.c > create mode 100644 include/linux/richacl.h > create mode 100644 include/linux/richacl_xattr.h > > -- > 2.1.0 > > From a7ae9dc44b9772622cb5d17b142a43cea2d18d10 Mon Sep 17 00:00:00 2001 > Message-Id: > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Wed, 4 Feb 2015 15:47:36 +0100 > Subject: [RFC 01/21] vfs: Minor documentation fix > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > The check_acl inode operation and the IPERM_FLAG_RCU are long gone. > Document what get_acl does instead. > > Signed-off-by: Andreas Gruenbacher > --- > Documentation/filesystems/porting | 8 ++++---- > Documentation/filesystems/vfs.txt | 3 +++ > 2 files changed, 7 insertions(+), 4 deletions(-) > > diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting > index fa2db08..d6f9ab4 100644 > --- a/Documentation/filesystems/porting > +++ b/Documentation/filesystems/porting > @@ -379,10 +379,10 @@ may now be called in rcu-walk mode (nd->flags & LOOKUP_RCU). -ECHILD should be > returned if the filesystem cannot handle rcu-walk. See > Documentation/filesystems/vfs.txt for more details. > > - permission and check_acl are inode permission checks that are called > -on many or all directory inodes on the way down a path walk (to check for > -exec permission). These must now be rcu-walk aware (flags & IPERM_FLAG_RCU). > -See Documentation/filesystems/vfs.txt for more details. > + permission is an inode permission check that is called on many or all > +directory inodes on the way down a path walk (to check for exec permission). It > +must now be rcu-walk aware (mask & MAY_NOT_BLOCK). See > +Documentation/filesystems/vfs.txt for more details. > > -- > [mandatory] > diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt > index 966b228..700cdf6 100644 > --- a/Documentation/filesystems/vfs.txt > +++ b/Documentation/filesystems/vfs.txt > @@ -457,6 +457,9 @@ otherwise noted. > If a situation is encountered that rcu-walk cannot handle, return > -ECHILD and it will be called again in ref-walk mode. > > + get_acl: called by the VFS to get the posix acl of an inode. Called during > + permission checks. The returned acl is cached in the inode. > + > setattr: called by the VFS to set attributes for a file. This method > is called by chmod(2) and related system calls. > > -- > 2.1.0 > > > From d89155579f576fbe07756462212365f678afdb75 Mon Sep 17 00:00:00 2001 > Message-Id: > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Wed, 4 Feb 2015 14:46:15 +0100 > Subject: [RFC 02/21] vfs: Shrink struct posix_acl > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > There is a hole in struct posix_acl because its struct rcu_head member is too > large; at least on on 64-bit architectures, the hole cannot be closed by > changing the definition of struct posix_acl. So instead, remove the struct > rcu_head member from struct posix_acl, make sure that acls are always big > enough to fit a struct rcu_head, and cast to struct rcu_head * when disposing > of an acl. > > Signed-off-by: Andreas Gruenbacher > --- > fs/posix_acl.c | 5 +++-- > include/linux/posix_acl.h | 7 ++----- > 2 files changed, 5 insertions(+), 7 deletions(-) > > diff --git a/fs/posix_acl.c b/fs/posix_acl.c > index 3a48bb7..efe983e 100644 > --- a/fs/posix_acl.c > +++ b/fs/posix_acl.c > @@ -140,8 +140,9 @@ EXPORT_SYMBOL(posix_acl_init); > struct posix_acl * > posix_acl_alloc(int count, gfp_t flags) > { > - const size_t size = sizeof(struct posix_acl) + > - count * sizeof(struct posix_acl_entry); > + const size_t size = max(sizeof(struct rcu_head), > + sizeof(struct posix_acl) + > + count * sizeof(struct posix_acl_entry)); > struct posix_acl *acl = kmalloc(size, flags); > if (acl) > posix_acl_init(acl, count); > diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h > index 3e96a6a..66cf477 100644 > --- a/include/linux/posix_acl.h > +++ b/include/linux/posix_acl.h > @@ -43,10 +43,7 @@ struct posix_acl_entry { > }; > > struct posix_acl { > - union { > - atomic_t a_refcount; > - struct rcu_head a_rcu; > - }; > + atomic_t a_refcount; > unsigned int a_count; > struct posix_acl_entry a_entries[0]; > }; > @@ -73,7 +70,7 @@ static inline void > posix_acl_release(struct posix_acl *acl) > { > if (acl && atomic_dec_and_test(&acl->a_refcount)) > - kfree_rcu(acl, a_rcu); > + __kfree_rcu((struct rcu_head *)acl, 0); > } > > > -- > 2.1.0 > > > From 611a0b6fe640f6d4ff7bb98931edf8c2fe81471c Mon Sep 17 00:00:00 2001 > Message-Id: <611a0b6fe640f6d4ff7bb98931edf8c2fe81471c.1424900921.git.agruenba@redhat.com> > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Tue, 1 Apr 2014 00:19:53 +0530 > Subject: [RFC 03/21] vfs: Add IS_ACL() and IS_RICHACL() tests > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > The vfs does not apply the umask for file systems that support acls. The test > used for this used to be called IS_POSIXACL(). Switch to a new IS_ACL() test to > check for either posix acls or richacls instead. Add a new MS_RICHACL flag and > IS_RICHACL() test for richacls alone. The IS_POSIXACL() test is still needed > by file systems that specifically support POSIX ACLs, like nfsd. > > Signed-off-by: Andreas Gruenbacher > --- > fs/Kconfig | 3 +++ > fs/namei.c | 8 ++++---- > include/linux/fs.h | 12 ++++++++++++ > include/uapi/linux/fs.h | 3 ++- > 4 files changed, 21 insertions(+), 5 deletions(-) > > diff --git a/fs/Kconfig b/fs/Kconfig > index ec35851..8b84f99 100644 > --- a/fs/Kconfig > +++ b/fs/Kconfig > @@ -58,6 +58,9 @@ endif # BLOCK > config FS_POSIX_ACL > def_bool n > > +config FS_RICHACL > + def_bool n > + > config EXPORTFS > tristate > > diff --git a/fs/namei.c b/fs/namei.c > index c83145a..0ba4bbc 100644 > --- a/fs/namei.c > +++ b/fs/namei.c > @@ -2696,7 +2696,7 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry, > } > > mode = op->mode; > - if ((open_flag & O_CREAT) && !IS_POSIXACL(dir)) > + if ((open_flag & O_CREAT) && !IS_ACL(dir)) > mode &= ~current_umask(); > > excl = (open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT); > @@ -2880,7 +2880,7 @@ static int lookup_open(struct nameidata *nd, struct path *path, > /* Negative dentry, just create the file */ > if (!dentry->d_inode && (op->open_flag & O_CREAT)) { > umode_t mode = op->mode; > - if (!IS_POSIXACL(dir->d_inode)) > + if (!IS_ACL(dir->d_inode)) > mode &= ~current_umask(); > /* > * This write is needed to ensure that a > @@ -3481,7 +3481,7 @@ retry: > if (IS_ERR(dentry)) > return PTR_ERR(dentry); > > - if (!IS_POSIXACL(path.dentry->d_inode)) > + if (!IS_ACL(path.dentry->d_inode)) > mode &= ~current_umask(); > error = security_path_mknod(&path, dentry, mode, dev); > if (error) > @@ -3550,7 +3550,7 @@ retry: > if (IS_ERR(dentry)) > return PTR_ERR(dentry); > > - if (!IS_POSIXACL(path.dentry->d_inode)) > + if (!IS_ACL(path.dentry->d_inode)) > mode &= ~current_umask(); > error = security_path_mkdir(&path, dentry, mode); > if (!error) > diff --git a/include/linux/fs.h b/include/linux/fs.h > index b4d71b5..f64eb45 100644 > --- a/include/linux/fs.h > +++ b/include/linux/fs.h > @@ -1708,6 +1708,12 @@ struct super_operations { > #define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE) > #define IS_POSIXACL(inode) __IS_FLG(inode, MS_POSIXACL) > > +#ifdef CONFIG_FS_RICHACL > +#define IS_RICHACL(inode) __IS_FLG(inode, MS_RICHACL) > +#else > +#define IS_RICHACL(inode) 0 > +#endif > + > #define IS_DEADDIR(inode) ((inode)->i_flags & S_DEAD) > #define IS_NOCMTIME(inode) ((inode)->i_flags & S_NOCMTIME) > #define IS_SWAPFILE(inode) ((inode)->i_flags & S_SWAPFILE) > @@ -1721,6 +1727,12 @@ struct super_operations { > (inode)->i_rdev == WHITEOUT_DEV) > > /* > + * IS_ACL() tells the VFS to not apply the umask > + * and use check_acl for acl permission checks when defined. > + */ > +#define IS_ACL(inode) __IS_FLG(inode, MS_POSIXACL | MS_RICHACL) > + > +/* > * Inode state bits. Protected by inode->i_lock > * > * Three bits determine the dirty state of the inode, I_DIRTY_SYNC, > diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h > index 9b964a5..6ac6bc9 100644 > --- a/include/uapi/linux/fs.h > +++ b/include/uapi/linux/fs.h > @@ -81,7 +81,7 @@ struct inodes_stat_t { > #define MS_VERBOSE 32768 /* War is peace. Verbosity is silence. > MS_VERBOSE is deprecated. */ > #define MS_SILENT 32768 > -#define MS_POSIXACL (1<<16) /* VFS does not apply the umask */ > +#define MS_POSIXACL (1<<16) /* Supports POSIX ACLs */ > #define MS_UNBINDABLE (1<<17) /* change to unbindable */ > #define MS_PRIVATE (1<<18) /* change to private */ > #define MS_SLAVE (1<<19) /* change to slave */ > @@ -91,6 +91,7 @@ struct inodes_stat_t { > #define MS_I_VERSION (1<<23) /* Update inode I_version field */ > #define MS_STRICTATIME (1<<24) /* Always perform atime updates */ > #define MS_LAZYTIME (1<<25) /* Update the on-disk [acm]times lazily */ > +#define MS_RICHACL (1<<26) /* Supports richacls */ > > /* These sb flags are internal to the kernel */ > #define MS_NOSEC (1<<28) > -- > 2.1.0 > > > From f8b04df08a0dd950d47e17c901773258f0653eed Mon Sep 17 00:00:00 2001 > Message-Id: > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Wed, 28 Jan 2015 20:23:15 +0100 > Subject: [RFC 04/21] vfs: Add MAY_CREATE_FILE and MAY_CREATE_DIR > permission flags > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > Richacls distinguish between creating non-directories and directories. To > support that, add an isdir parameter to may_create(). When checking > inode_permission() for create permission, pass in an additional MAY_CREATE_FILE > or MAY_CREATE_DIR mask flag. > > To allow checking for delete *and* create access when replacing an existing > file via vfs_rename(), add a replace parameter to may_delete(). > > Signed-off-by: Andreas Gruenbacher > --- > fs/namei.c | 42 ++++++++++++++++++++++++------------------ > include/linux/fs.h | 2 ++ > 2 files changed, 26 insertions(+), 18 deletions(-) > > diff --git a/fs/namei.c b/fs/namei.c > index 0ba4bbc..a8bc030 100644 > --- a/fs/namei.c > +++ b/fs/namei.c > @@ -454,7 +454,8 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask) > * this, letting us set arbitrary permissions for filesystem access without > * changing the "normal" UIDs which are used for other things. > * > - * When checking for MAY_APPEND, MAY_WRITE must also be set in @mask. > + * When checking for MAY_APPEND, MAY_CREATE_FILE, MAY_CREATE_DIR, > + * MAY_WRITE must also be set in @mask. > */ > int inode_permission(struct inode *inode, int mask) > { > @@ -2447,10 +2448,11 @@ EXPORT_SYMBOL(__check_sticky); > * 10. We don't allow removal of NFS sillyrenamed files; it's handled by > * nfs_async_unlink(). > */ > -static int may_delete(struct inode *dir, struct dentry *victim, bool isdir) > +static int may_delete(struct inode *dir, struct dentry *victim, > + bool isdir, bool replace) > { > struct inode *inode = victim->d_inode; > - int error; > + int error, mask = MAY_WRITE | MAY_EXEC; > > if (d_is_negative(victim)) > return -ENOENT; > @@ -2459,7 +2461,9 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir) > BUG_ON(victim->d_parent->d_inode != dir); > audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); > > - error = inode_permission(dir, MAY_WRITE | MAY_EXEC); > + if (replace) > + mask |= isdir ? MAY_CREATE_DIR : MAY_CREATE_FILE; > + error = inode_permission(dir, mask); > if (error) > return error; > if (IS_APPEND(dir)) > @@ -2490,14 +2494,16 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir) > * 3. We should have write and exec permissions on dir > * 4. We can't do it if dir is immutable (done in permission()) > */ > -static inline int may_create(struct inode *dir, struct dentry *child) > +static inline int may_create(struct inode *dir, struct dentry *child, bool isdir) > { > + int mask = isdir ? MAY_CREATE_DIR : MAY_CREATE_FILE; > + > audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE); > if (child->d_inode) > return -EEXIST; > if (IS_DEADDIR(dir)) > return -ENOENT; > - return inode_permission(dir, MAY_WRITE | MAY_EXEC); > + return inode_permission(dir, MAY_WRITE | MAY_EXEC | mask); > } > > /* > @@ -2547,7 +2553,7 @@ EXPORT_SYMBOL(unlock_rename); > int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, > bool want_excl) > { > - int error = may_create(dir, dentry); > + int error = may_create(dir, dentry, false); > if (error) > return error; > > @@ -3422,7 +3428,7 @@ EXPORT_SYMBOL(user_path_create); > > int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) > { > - int error = may_create(dir, dentry); > + int error = may_create(dir, dentry, false); > > if (error) > return error; > @@ -3514,7 +3520,7 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d > > int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) > { > - int error = may_create(dir, dentry); > + int error = may_create(dir, dentry, true); > unsigned max_links = dir->i_sb->s_max_links; > > if (error) > @@ -3595,7 +3601,7 @@ EXPORT_SYMBOL(dentry_unhash); > > int vfs_rmdir(struct inode *dir, struct dentry *dentry) > { > - int error = may_delete(dir, dentry, 1); > + int error = may_delete(dir, dentry, true, false); > > if (error) > return error; > @@ -3715,7 +3721,7 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname) > int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode) > { > struct inode *target = dentry->d_inode; > - int error = may_delete(dir, dentry, 0); > + int error = may_delete(dir, dentry, false, false); > > if (error) > return error; > @@ -3847,7 +3853,7 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname) > > int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) > { > - int error = may_create(dir, dentry); > + int error = may_create(dir, dentry, false); > > if (error) > return error; > @@ -3930,7 +3936,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de > if (!inode) > return -ENOENT; > > - error = may_create(dir, new_dentry); > + error = may_create(dir, new_dentry, false); > if (error) > return error; > > @@ -4118,19 +4124,19 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, > if (source == target) > return 0; > > - error = may_delete(old_dir, old_dentry, is_dir); > + error = may_delete(old_dir, old_dentry, is_dir, false); > if (error) > return error; > > if (!target) { > - error = may_create(new_dir, new_dentry); > + error = may_create(new_dir, new_dentry, is_dir); > } else { > new_is_dir = d_is_dir(new_dentry); > > if (!(flags & RENAME_EXCHANGE)) > - error = may_delete(new_dir, new_dentry, is_dir); > + error = may_delete(new_dir, new_dentry, is_dir, true); > else > - error = may_delete(new_dir, new_dentry, new_is_dir); > + error = may_delete(new_dir, new_dentry, new_is_dir, true); > } > if (error) > return error; > @@ -4394,7 +4400,7 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna > > int vfs_whiteout(struct inode *dir, struct dentry *dentry) > { > - int error = may_create(dir, dentry); > + int error = may_create(dir, dentry, false); > if (error) > return error; > > diff --git a/include/linux/fs.h b/include/linux/fs.h > index f64eb45..bbe1d26 100644 > --- a/include/linux/fs.h > +++ b/include/linux/fs.h > @@ -80,6 +80,8 @@ typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset, > #define MAY_CHDIR 0x00000040 > /* called from RCU mode, don't block */ > #define MAY_NOT_BLOCK 0x00000080 > +#define MAY_CREATE_FILE 0x00000100 > +#define MAY_CREATE_DIR 0x00000200 > > /* > * flags in file.f_mode. Note that FMODE_READ and FMODE_WRITE must correspond > -- > 2.1.0 > > > From a858d4a82fe74516f5036cb0b8ff8f177830025f Mon Sep 17 00:00:00 2001 > Message-Id: > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Tue, 1 Apr 2014 05:06:26 +0530 > Subject: [RFC 05/21] vfs: Add MAY_DELETE_SELF and MAY_DELETE_CHILD > permission flags > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > Normally, deleting a file requires write and execute access to the parent > directory. With Richacls, a process with MAY_DELETE_SELF access to a file may > delete the file even without write access to the parent directory. > > To support that, pass the MAY_DELETE_CHILD mask flag to inode_permission() when > checking for delete access inside a directory, and MAY_DELETE_SELF when > checking for delete access to a file itelf. > > The MAY_DELETE_SELF permission does not override the sticky directory check. > > Signed-off-by: Andreas Gruenbacher > --- > fs/namei.c | 15 +++++++++++---- > include/linux/fs.h | 2 ++ > 2 files changed, 13 insertions(+), 4 deletions(-) > > diff --git a/fs/namei.c b/fs/namei.c > index a8bc030..a8d1674 100644 > --- a/fs/namei.c > +++ b/fs/namei.c > @@ -455,7 +455,7 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask) > * changing the "normal" UIDs which are used for other things. > * > * When checking for MAY_APPEND, MAY_CREATE_FILE, MAY_CREATE_DIR, > - * MAY_WRITE must also be set in @mask. > + * MAY_DELETE_CHILD, MAY_DELETE_SELF, MAY_WRITE must also be set in @mask. > */ > int inode_permission(struct inode *inode, int mask) > { > @@ -2452,7 +2452,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, > bool isdir, bool replace) > { > struct inode *inode = victim->d_inode; > - int error, mask = MAY_WRITE | MAY_EXEC; > + int error, mask = MAY_EXEC; > > if (d_is_negative(victim)) > return -ENOENT; > @@ -2462,8 +2462,15 @@ static int may_delete(struct inode *dir, struct dentry *victim, > audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE); > > if (replace) > - mask |= isdir ? MAY_CREATE_DIR : MAY_CREATE_FILE; > - error = inode_permission(dir, mask); > + mask |= MAY_WRITE | (isdir ? MAY_CREATE_DIR : MAY_CREATE_FILE); > + error = inode_permission(dir, mask | MAY_WRITE | MAY_DELETE_CHILD); > + if (error && IS_RICHACL(inode)) { > + /* Deleting is also permitted with MAY_EXEC on the directory > + * and MAY_DELETE_SELF on the inode. */ > + if (!inode_permission(inode, MAY_DELETE_SELF) && > + !inode_permission(dir, mask)) > + error = 0; > + } > if (error) > return error; > if (IS_APPEND(dir)) > diff --git a/include/linux/fs.h b/include/linux/fs.h > index bbe1d26..101abcf 100644 > --- a/include/linux/fs.h > +++ b/include/linux/fs.h > @@ -82,6 +82,8 @@ typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset, > #define MAY_NOT_BLOCK 0x00000080 > #define MAY_CREATE_FILE 0x00000100 > #define MAY_CREATE_DIR 0x00000200 > +#define MAY_DELETE_CHILD 0x00000400 > +#define MAY_DELETE_SELF 0x00000800 > > /* > * flags in file.f_mode. Note that FMODE_READ and FMODE_WRITE must correspond > -- > 2.1.0 > > > From 19510de7d710a34c47eadb9b8f71881b5621574a Mon Sep 17 00:00:00 2001 > Message-Id: <19510de7d710a34c47eadb9b8f71881b5621574a.1424900921.git.agruenba@redhat.com> > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Tue, 1 Apr 2014 05:13:56 +0530 > Subject: [RFC 06/21] vfs: Make the inode passed to inode_change_ok > non-const > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > We will need to call iop->permission and iop->get_acl from > inode_change_ok() for additional permission checks, and both take a > non-const inode. > > Signed-off-by: Andreas Gruenbacher > --- > fs/attr.c | 2 +- > include/linux/fs.h | 2 +- > 2 files changed, 2 insertions(+), 2 deletions(-) > > diff --git a/fs/attr.c b/fs/attr.c > index 6530ced..328be71 100644 > --- a/fs/attr.c > +++ b/fs/attr.c > @@ -28,7 +28,7 @@ > * Should be called as the first thing in ->setattr implementations, > * possibly after taking additional locks. > */ > -int inode_change_ok(const struct inode *inode, struct iattr *attr) > +int inode_change_ok(struct inode *inode, struct iattr *attr) > { > unsigned int ia_valid = attr->ia_valid; > > diff --git a/include/linux/fs.h b/include/linux/fs.h > index 101abcf..f688ea6 100644 > --- a/include/linux/fs.h > +++ b/include/linux/fs.h > @@ -2760,7 +2760,7 @@ extern int buffer_migrate_page(struct address_space *, > #define buffer_migrate_page NULL > #endif > > -extern int inode_change_ok(const struct inode *, struct iattr *); > +extern int inode_change_ok(struct inode *, struct iattr *); > extern int inode_newsize_ok(const struct inode *, loff_t offset); > extern void setattr_copy(struct inode *inode, const struct iattr *attr); > > -- > 2.1.0 > > > From e710237138b0ee9012bc616012d1f8511cf6af4a Mon Sep 17 00:00:00 2001 > Message-Id: > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Tue, 1 Apr 2014 05:29:34 +0530 > Subject: [RFC 07/21] vfs: Add permission flags for setting file > attributes > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > Richacls support permissions that allow to take ownership of a file, change the > file permissions, and set the file timestamps. Support that by introducing new > permission mask flags and by checking for those mask flags in > inode_change_ok(). > > Signed-off-by: Andreas Gruenbacher > --- > fs/attr.c | 79 +++++++++++++++++++++++++++++++++++++++++++++--------- > include/linux/fs.h | 3 +++ > 2 files changed, 70 insertions(+), 12 deletions(-) > > diff --git a/fs/attr.c b/fs/attr.c > index 328be71..85483e0 100644 > --- a/fs/attr.c > +++ b/fs/attr.c > @@ -17,6 +17,65 @@ > #include > > /** > + * inode_extended_permission - permissions beyond read/write/execute > + * > + * Check for permissions that only richacls can currently grant. > + */ > +static int inode_extended_permission(struct inode *inode, int mask) > +{ > + if (!IS_RICHACL(inode)) > + return -EPERM; > + return inode_permission(inode, mask); > +} > + > +static bool inode_uid_change_ok(struct inode *inode, kuid_t ia_uid) > +{ > + if (uid_eq(current_fsuid(), inode->i_uid) && > + uid_eq(ia_uid, inode->i_uid)) > + return true; > + if (uid_eq(current_fsuid(), ia_uid) && > + inode_extended_permission(inode, MAY_TAKE_OWNERSHIP) == 0) > + return true; > + if (capable_wrt_inode_uidgid(inode, CAP_CHOWN)) > + return true; > + return false; > +} > + > +static bool inode_gid_change_ok(struct inode *inode, kgid_t ia_gid) > +{ > + int in_group = in_group_p(ia_gid); > + if (uid_eq(current_fsuid(), inode->i_uid) && > + (in_group || gid_eq(ia_gid, inode->i_gid))) > + return true; > + if (in_group && inode_extended_permission(inode, MAY_TAKE_OWNERSHIP) == 0) > + return true; > + if (capable_wrt_inode_uidgid(inode, CAP_CHOWN)) > + return true; > + return false; > +} > + > +/** > + * inode_owner_permitted_or_capable > + * > + * Check for permissions implicitly granted to the owner, like MAY_CHMOD or > + * MAY_SET_TIMES. Equivalent to inode_owner_or_capable for file systems > + * without support for those permissions. > + */ > +static bool inode_owner_permitted_or_capable(struct inode *inode, int mask) > +{ > + struct user_namespace *ns; > + > + if (uid_eq(current_fsuid(), inode->i_uid)) > + return true; > + if (inode_extended_permission(inode, mask) == 0) > + return true; > + ns = current_user_ns(); > + if (ns_capable(ns, CAP_FOWNER) && kuid_has_mapping(ns, inode->i_uid)) > + return true; > + return false; > +} > + > +/** > * inode_change_ok - check if attribute changes to an inode are allowed > * @inode: inode to check > * @attr: attributes to change > @@ -47,22 +106,18 @@ int inode_change_ok(struct inode *inode, struct iattr *attr) > return 0; > > /* Make sure a caller can chown. */ > - if ((ia_valid & ATTR_UID) && > - (!uid_eq(current_fsuid(), inode->i_uid) || > - !uid_eq(attr->ia_uid, inode->i_uid)) && > - !capable_wrt_inode_uidgid(inode, CAP_CHOWN)) > - return -EPERM; > + if (ia_valid & ATTR_UID) > + if (!inode_uid_change_ok(inode, attr->ia_uid)) > + return -EPERM; > > /* Make sure caller can chgrp. */ > - if ((ia_valid & ATTR_GID) && > - (!uid_eq(current_fsuid(), inode->i_uid) || > - (!in_group_p(attr->ia_gid) && !gid_eq(attr->ia_gid, inode->i_gid))) && > - !capable_wrt_inode_uidgid(inode, CAP_CHOWN)) > - return -EPERM; > + if (ia_valid & ATTR_GID) > + if (!inode_gid_change_ok(inode, attr->ia_gid)) > + return -EPERM; > > /* Make sure a caller can chmod. */ > if (ia_valid & ATTR_MODE) { > - if (!inode_owner_or_capable(inode)) > + if (!inode_owner_permitted_or_capable(inode, MAY_CHMOD)) > return -EPERM; > /* Also check the setgid bit! */ > if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid : > @@ -73,7 +128,7 @@ int inode_change_ok(struct inode *inode, struct iattr *attr) > > /* Check for setting the inode time. */ > if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) { > - if (!inode_owner_or_capable(inode)) > + if (!inode_owner_permitted_or_capable(inode, MAY_SET_TIMES)) > return -EPERM; > } > > diff --git a/include/linux/fs.h b/include/linux/fs.h > index f688ea6..e3e1e42 100644 > --- a/include/linux/fs.h > +++ b/include/linux/fs.h > @@ -84,6 +84,9 @@ typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset, > #define MAY_CREATE_DIR 0x00000200 > #define MAY_DELETE_CHILD 0x00000400 > #define MAY_DELETE_SELF 0x00000800 > +#define MAY_TAKE_OWNERSHIP 0x00001000 > +#define MAY_CHMOD 0x00002000 > +#define MAY_SET_TIMES 0x00004000 > > /* > * flags in file.f_mode. Note that FMODE_READ and FMODE_WRITE must correspond > -- > 2.1.0 > > > From a47d85681cea868d4e34794982297950533c2930 Mon Sep 17 00:00:00 2001 > Message-Id: > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Tue, 1 Apr 2014 18:10:17 +0530 > Subject: [RFC 08/21] richacl: In-memory representation and helper > functions > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > A richacl consists of an NFSv4 acl and an owner, group, and other mask. > These three masks correspond to the owner, group, and other file > permission bits, but they contain NFSv4 permissions instead of POSIX > permissions. > > Each entry in the NFSv4 acl applies to the file owner (OWNER@), the owning > group (GROUP@), everyone (EVERYONE@), or to a specific uid or gid. > > As in the standard POSIX file permission model, each process is the > owner, group, or other file class. A richacl grants a requested access > only if the NFSv4 acl in the richacl grants the access (according to the > NFSv4 permission check algorithm), and the file mask that applies to the > process includes the requested permissions. > > Signed-off-by: Andreas Gruenbacher > --- > fs/Makefile | 2 + > fs/richacl_base.c | 57 +++++++++++ > include/linux/richacl.h | 248 ++++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 307 insertions(+) > create mode 100644 fs/richacl_base.c > create mode 100644 include/linux/richacl.h > > diff --git a/fs/Makefile b/fs/Makefile > index a88ac48..8f0a59c 100644 > --- a/fs/Makefile > +++ b/fs/Makefile > @@ -47,6 +47,8 @@ obj-$(CONFIG_COREDUMP) += coredump.o > obj-$(CONFIG_SYSCTL) += drop_caches.o > > obj-$(CONFIG_FHANDLE) += fhandle.o > +obj-$(CONFIG_FS_RICHACL) += richacl.o > +richacl-y := richacl_base.o > > obj-y += quota/ > > diff --git a/fs/richacl_base.c b/fs/richacl_base.c > new file mode 100644 > index 0000000..abf8bce > --- /dev/null > +++ b/fs/richacl_base.c > @@ -0,0 +1,57 @@ > +/* > + * Copyright (C) 2006, 2010 Novell, Inc. > + * Copyright (C) 2015 Red Hat, Inc. > + * Written by Andreas Gruenbacher > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the > + * Free Software Foundation; either version 2, or (at your option) any > + * later version. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + */ > + > +#include > +#include > +#include > +#include > + > +MODULE_LICENSE("GPL"); > + > +/** > + * richacl_alloc - allocate a richacl > + * @count: number of entries > + */ > +struct richacl * > +richacl_alloc(int count) > +{ > + size_t size = sizeof(struct richacl) + count * sizeof(struct richace); > + struct richacl *acl = kzalloc(size, GFP_KERNEL); > + > + if (acl) { > + atomic_set(&acl->a_refcount, 1); > + acl->a_count = count; > + } > + return acl; > +} > +EXPORT_SYMBOL_GPL(richacl_alloc); > + > +/** > + * richacl_clone - create a copy of a richacl > + */ > +static struct richacl * > +richacl_clone(const struct richacl *acl) > +{ > + int count = acl->a_count; > + size_t size = sizeof(struct richacl) + count * sizeof(struct richace); > + struct richacl *dup = kmalloc(size, GFP_KERNEL); > + > + if (dup) { > + memcpy(dup, acl, size); > + atomic_set(&dup->a_refcount, 1); > + } > + return dup; > +} > diff --git a/include/linux/richacl.h b/include/linux/richacl.h > new file mode 100644 > index 0000000..b16d865 > --- /dev/null > +++ b/include/linux/richacl.h > @@ -0,0 +1,248 @@ > +/* > + * Copyright (C) 2006, 2010 Novell, Inc. > + * Copyright (C) 2015 Red Hat, Inc. > + * Written by Andreas Gruenbacher > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the > + * Free Software Foundation; either version 2, or (at your option) any > + * later version. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + */ > + > +#ifndef __RICHACL_H > +#define __RICHACL_H > +#include > + > +#define ACE_OWNER_ID 130 > +#define ACE_GROUP_ID 131 > +#define ACE_EVERYONE_ID 110 > + > +struct richace { > + unsigned short e_type; > + unsigned short e_flags; > + unsigned int e_mask; > + unsigned int e_id; > +}; > + > +struct richacl { > + atomic_t a_refcount; > + unsigned int a_owner_mask; > + unsigned int a_group_mask; > + unsigned int a_other_mask; > + unsigned short a_count; > + unsigned short a_flags; > + struct richace a_entries[0]; > +}; > + > +#define richacl_for_each_entry(_ace, _acl) \ > + for (_ace = (_acl)->a_entries; \ > + _ace != (_acl)->a_entries + (_acl)->a_count; \ > + _ace++) > + > +#define richacl_for_each_entry_reverse(_ace, _acl) \ > + for (_ace = (_acl)->a_entries + (_acl)->a_count - 1; \ > + _ace != (_acl)->a_entries - 1; \ > + _ace--) > + > +/* Flag values defined by richacls */ > +#define ACL4_MASKED 0x80 > + > +#define ACL4_VALID_FLAGS ( \ > + ACL4_MASKED) > + > +/* e_type values */ > +#define ACE4_ACCESS_ALLOWED_ACE_TYPE 0x0000 > +#define ACE4_ACCESS_DENIED_ACE_TYPE 0x0001 > +/*#define ACE4_SYSTEM_AUDIT_ACE_TYPE 0x0002*/ > +/*#define ACE4_SYSTEM_ALARM_ACE_TYPE 0x0003*/ > + > +/* e_flags bitflags */ > +#define ACE4_FILE_INHERIT_ACE 0x0001 > +#define ACE4_DIRECTORY_INHERIT_ACE 0x0002 > +#define ACE4_NO_PROPAGATE_INHERIT_ACE 0x0004 > +#define ACE4_INHERIT_ONLY_ACE 0x0008 > +/*#define ACE4_SUCCESSFUL_ACCESS_ACE_FLAG 0x0010*/ > +/*#define ACE4_FAILED_ACCESS_ACE_FLAG 0x0020*/ > +#define ACE4_IDENTIFIER_GROUP 0x0040 > +/* richacl specific flag values */ > +#define ACE4_SPECIAL_WHO 0x4000 > + > +#define ACE4_VALID_FLAGS ( \ > + ACE4_FILE_INHERIT_ACE | \ > + ACE4_DIRECTORY_INHERIT_ACE | \ > + ACE4_NO_PROPAGATE_INHERIT_ACE | \ > + ACE4_INHERIT_ONLY_ACE | \ > + ACE4_IDENTIFIER_GROUP | \ > + ACE4_SPECIAL_WHO) > + > +/* e_mask bitflags */ > +#define ACE4_READ_DATA 0x00000001 > +#define ACE4_LIST_DIRECTORY 0x00000001 > +#define ACE4_WRITE_DATA 0x00000002 > +#define ACE4_ADD_FILE 0x00000002 > +#define ACE4_APPEND_DATA 0x00000004 > +#define ACE4_ADD_SUBDIRECTORY 0x00000004 > +#define ACE4_READ_NAMED_ATTRS 0x00000008 > +#define ACE4_WRITE_NAMED_ATTRS 0x00000010 > +#define ACE4_EXECUTE 0x00000020 > +#define ACE4_DELETE_CHILD 0x00000040 > +#define ACE4_READ_ATTRIBUTES 0x00000080 > +#define ACE4_WRITE_ATTRIBUTES 0x00000100 > +#define ACE4_WRITE_RETENTION 0x00000200 > +#define ACE4_WRITE_RETENTION_HOLD 0x00000400 > +#define ACE4_DELETE 0x00010000 > +#define ACE4_READ_ACL 0x00020000 > +#define ACE4_WRITE_ACL 0x00040000 > +#define ACE4_WRITE_OWNER 0x00080000 > +#define ACE4_SYNCHRONIZE 0x00100000 > + > +/* Valid ACE4_* flags for directories and non-directories */ > +#define ACE4_VALID_MASK ( \ > + ACE4_READ_DATA | ACE4_LIST_DIRECTORY | \ > + ACE4_WRITE_DATA | ACE4_ADD_FILE | \ > + ACE4_APPEND_DATA | ACE4_ADD_SUBDIRECTORY | \ > + ACE4_READ_NAMED_ATTRS | \ > + ACE4_WRITE_NAMED_ATTRS | \ > + ACE4_EXECUTE | \ > + ACE4_DELETE_CHILD | \ > + ACE4_READ_ATTRIBUTES | \ > + ACE4_WRITE_ATTRIBUTES | \ > + ACE4_WRITE_RETENTION | \ > + ACE4_WRITE_RETENTION_HOLD | \ > + ACE4_DELETE | \ > + ACE4_READ_ACL | \ > + ACE4_WRITE_ACL | \ > + ACE4_WRITE_OWNER | \ > + ACE4_SYNCHRONIZE) > + > +/** > + * richacl_get - grab another reference to a richacl handle > + */ > +static inline struct richacl * > +richacl_get(struct richacl *acl) > +{ > + if (acl) > + atomic_inc(&acl->a_refcount); > + return acl; > +} > + > +/** > + * richacl_put - free a richacl handle > + */ > +static inline void > +richacl_put(struct richacl *acl) > +{ > + if (acl && atomic_dec_and_test(&acl->a_refcount)) > + kfree(acl); > +} > + > +/** > + * richace_is_owner - check if @ace is an OWNER@ entry > + */ > +static inline bool > +richace_is_owner(const struct richace *ace) > +{ > + return (ace->e_flags & ACE4_SPECIAL_WHO) && > + ace->e_id == ACE_OWNER_ID; > +} > + > +/** > + * richace_is_group - check if @ace is a GROUP@ entry > + */ > +static inline bool > +richace_is_group(const struct richace *ace) > +{ > + return (ace->e_flags & ACE4_SPECIAL_WHO) && > + ace->e_id == ACE_GROUP_ID; > +} > + > +/** > + * richace_is_everyone - check if @ace is an EVERYONE@ entry > + */ > +static inline bool > +richace_is_everyone(const struct richace *ace) > +{ > + return (ace->e_flags & ACE4_SPECIAL_WHO) && > + ace->e_id == ACE_EVERYONE_ID; > +} > + > +/** > + * richace_is_unix_id - check if @ace applies to a specific uid or gid > + */ > +static inline bool > +richace_is_unix_id(const struct richace *ace) > +{ > + return !(ace->e_flags & ACE4_SPECIAL_WHO); > +} > + > +/** > + * richace_is_inherit_only - check if @ace is for inheritance only > + * > + * ACEs with the %ACE4_INHERIT_ONLY_ACE flag set have no effect during > + * permission checking. > + */ > +static inline bool > +richace_is_inherit_only(const struct richace *ace) > +{ > + return ace->e_flags & ACE4_INHERIT_ONLY_ACE; > +} > + > +/** > + * richace_is_inheritable - check if @ace is inheritable > + */ > +static inline bool > +richace_is_inheritable(const struct richace *ace) > +{ > + return ace->e_flags & (ACE4_FILE_INHERIT_ACE | > + ACE4_DIRECTORY_INHERIT_ACE); > +} > + > +/** > + * richace_clear_inheritance_flags - clear all inheritance flags in @ace > + */ > +static inline void > +richace_clear_inheritance_flags(struct richace *ace) > +{ > + ace->e_flags &= ~(ACE4_FILE_INHERIT_ACE | > + ACE4_DIRECTORY_INHERIT_ACE | > + ACE4_NO_PROPAGATE_INHERIT_ACE | > + ACE4_INHERIT_ONLY_ACE); > +} > + > +/** > + * richace_is_allow - check if @ace is an %ALLOW type entry > + */ > +static inline bool > +richace_is_allow(const struct richace *ace) > +{ > + return ace->e_type == ACE4_ACCESS_ALLOWED_ACE_TYPE; > +} > + > +/** > + * richace_is_deny - check if @ace is a %DENY type entry > + */ > +static inline bool > +richace_is_deny(const struct richace *ace) > +{ > + return ace->e_type == ACE4_ACCESS_DENIED_ACE_TYPE; > +} > + > +/** > + * richace_is_same_identifier - are both identifiers the same? > + */ > +static inline bool > +richace_is_same_identifier(const struct richace *a, const struct richace *b) > +{ > + return !((a->e_flags ^ b->e_flags) & > + (ACE4_SPECIAL_WHO | ACE4_IDENTIFIER_GROUP)) && > + a->e_id == b->e_id; > +} > + > +extern struct richacl *richacl_alloc(int); > + > +#endif /* __RICHACL_H */ > -- > 2.1.0 > > > From fe15273975043bc6064de8395e41ba3066f8d5d4 Mon Sep 17 00:00:00 2001 > Message-Id: > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Tue, 1 Apr 2014 18:11:56 +0530 > Subject: [RFC 09/21] richacl: Permission mapping functions > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > We need to map from POSIX permissions to NFSv4 permissions when a > chmod() is done, from NFSv4 permissions to POSIX permissions when an acl > is set (which implicitly sets the file permission bits), and from the > MAY_READ/MAY_WRITE/MAY_EXEC/MAY_APPEND flags to NFSv4 permissions when > doing an access check in a richacl. > > Signed-off-by: Andreas Gruenbacher > --- > fs/richacl_base.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ > include/linux/richacl.h | 46 +++++++++++++++++++ > 2 files changed, 163 insertions(+) > > diff --git a/fs/richacl_base.c b/fs/richacl_base.c > index abf8bce..83731c7 100644 > --- a/fs/richacl_base.c > +++ b/fs/richacl_base.c > @@ -55,3 +55,120 @@ richacl_clone(const struct richacl *acl) > } > return dup; > } > + > +/** > + * richacl_mask_to_mode - compute the file permission bits which correspond to @mask > + * @mask: %ACE4_* permission mask > + * > + * See richacl_masks_to_mode(). > + */ > +static int > +richacl_mask_to_mode(unsigned int mask) > +{ > + int mode = 0; > + > + if (mask & ACE4_POSIX_MODE_READ) > + mode |= S_IROTH; > + if (mask & ACE4_POSIX_MODE_WRITE) > + mode |= S_IWOTH; > + if (mask & ACE4_POSIX_MODE_EXEC) > + mode |= S_IXOTH; > + > + return mode; > +} > + > +/** > + * richacl_masks_to_mode - compute the file permission bits from the file masks > + * > + * When setting a richacl, we set the file permission bits to indicate maximum > + * permissions: for example, we set the Write permission when a mask contains > + * ACE4_APPEND_DATA even if it does not also contain ACE4_WRITE_DATA. > + * > + * Permissions which are not in ACE4_POSIX_MODE_READ, ACE4_POSIX_MODE_WRITE, or > + * ACE4_POSIX_MODE_EXEC cannot be represented in the file permission bits. > + * Such permissions can still be effective, but not for new files or after a > + * chmod(), and only if they were set explicitly, for example, by setting a > + * richacl. > + */ > +int > +richacl_masks_to_mode(const struct richacl *acl) > +{ > + return richacl_mask_to_mode(acl->a_owner_mask) << 6 | > + richacl_mask_to_mode(acl->a_group_mask) << 3 | > + richacl_mask_to_mode(acl->a_other_mask); > +} > +EXPORT_SYMBOL_GPL(richacl_masks_to_mode); > + > +/** > + * richacl_mode_to_mask - compute a file mask from the lowest three mode bits > + * > + * When the file permission bits of a file are set with chmod(), this specifies > + * the maximum permissions that processes will get. All permissions beyond > + * that will be removed from the file masks, and become ineffective. > + * > + * We also add in the permissions which are always allowed no matter what the > + * acl says. > + */ > +unsigned int > +richacl_mode_to_mask(mode_t mode) > +{ > + unsigned int mask = ACE4_POSIX_ALWAYS_ALLOWED; > + > + if (mode & S_IROTH) > + mask |= ACE4_POSIX_MODE_READ; > + if (mode & S_IWOTH) > + mask |= ACE4_POSIX_MODE_WRITE; > + if (mode & S_IXOTH) > + mask |= ACE4_POSIX_MODE_EXEC; > + > + return mask; > +} > + > +/** > + * richacl_want_to_mask - convert the iop->permission want argument to a mask > + * @want: @want argument of the permission inode operation > + * > + * When checking for append, @want is (MAY_WRITE | MAY_APPEND). > + * > + * Richacls use the iop->may_create and iop->may_delete hooks which are > + * used for checking if creating and deleting files is allowed. These hooks do > + * not use richacl_want_to_mask(), so we do not have to deal with mapping > + * MAY_WRITE to ACE4_ADD_FILE, ACE4_ADD_SUBDIRECTORY, and ACE4_DELETE_CHILD > + * here. > + */ > +unsigned int > +richacl_want_to_mask(unsigned int want) > +{ > + unsigned int mask = 0; > + > + if (want & MAY_READ) > + mask |= ACE4_READ_DATA; > + if (want & MAY_DELETE_SELF) > + mask |= ACE4_DELETE; > + if (want & MAY_TAKE_OWNERSHIP) > + mask |= ACE4_WRITE_OWNER; > + if (want & MAY_CHMOD) > + mask |= ACE4_WRITE_ACL; > + if (want & MAY_SET_TIMES) > + mask |= ACE4_WRITE_ATTRIBUTES; > + if (want & MAY_EXEC) > + mask |= ACE4_EXECUTE; > + /* > + * differentiate MAY_WRITE from these request > + */ > + if (want & (MAY_APPEND | > + MAY_CREATE_FILE | MAY_CREATE_DIR | > + MAY_DELETE_CHILD)) { > + if (want & MAY_APPEND) > + mask |= ACE4_APPEND_DATA; > + if (want & MAY_CREATE_FILE) > + mask |= ACE4_ADD_FILE; > + if (want & MAY_CREATE_DIR) > + mask |= ACE4_ADD_SUBDIRECTORY; > + if (want & MAY_DELETE_CHILD) > + mask |= ACE4_DELETE_CHILD; > + } else if (want & MAY_WRITE) > + mask |= ACE4_WRITE_DATA; > + return mask; > +} > +EXPORT_SYMBOL_GPL(richacl_want_to_mask); > diff --git a/include/linux/richacl.h b/include/linux/richacl.h > index b16d865..41819f4 100644 > --- a/include/linux/richacl.h > +++ b/include/linux/richacl.h > @@ -120,6 +120,49 @@ struct richacl { > ACE4_WRITE_OWNER | \ > ACE4_SYNCHRONIZE) > > +/* > + * The POSIX permissions are supersets of the following NFSv4 permissions: > + * > + * - MAY_READ maps to READ_DATA or LIST_DIRECTORY, depending on the type > + * of the file system object. > + * > + * - MAY_WRITE maps to WRITE_DATA or ACE4_APPEND_DATA for files, and to > + * ADD_FILE, ACE4_ADD_SUBDIRECTORY, or ACE4_DELETE_CHILD for directories. > + * > + * - MAY_EXECUTE maps to ACE4_EXECUTE. > + * > + * (Some of these NFSv4 permissions have the same bit values.) > + */ > +#define ACE4_POSIX_MODE_READ ( \ > + ACE4_READ_DATA | \ > + ACE4_LIST_DIRECTORY) > +#define ACE4_POSIX_MODE_WRITE ( \ > + ACE4_WRITE_DATA | \ > + ACE4_ADD_FILE | \ > + ACE4_APPEND_DATA | \ > + ACE4_ADD_SUBDIRECTORY | \ > + ACE4_DELETE_CHILD) > +#define ACE4_POSIX_MODE_EXEC ACE4_EXECUTE > +#define ACE4_POSIX_MODE_ALL ( \ > + ACE4_POSIX_MODE_READ | \ > + ACE4_POSIX_MODE_WRITE | \ > + ACE4_POSIX_MODE_EXEC) > +/* > + * These permissions are always allowed > + * no matter what the acl says. > + */ > +#define ACE4_POSIX_ALWAYS_ALLOWED ( \ > + ACE4_SYNCHRONIZE | \ > + ACE4_READ_ATTRIBUTES | \ > + ACE4_READ_ACL) > +/* > + * The owner is implicitly granted > + * these permissions under POSIX. > + */ > +#define ACE4_POSIX_OWNER_ALLOWED ( \ > + ACE4_WRITE_ATTRIBUTES | \ > + ACE4_WRITE_OWNER | \ > + ACE4_WRITE_ACL) > /** > * richacl_get - grab another reference to a richacl handle > */ > @@ -244,5 +287,8 @@ richace_is_same_identifier(const struct richace *a, const struct richace *b) > } > > extern struct richacl *richacl_alloc(int); > +extern int richacl_masks_to_mode(const struct richacl *); > +extern unsigned int richacl_mode_to_mask(mode_t); > +extern unsigned int richacl_want_to_mask(unsigned int); > > #endif /* __RICHACL_H */ > -- > 2.1.0 > > > From ae4e31aeac1c56249ae7092c84fe554ccb34df41 Mon Sep 17 00:00:00 2001 > Message-Id: > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Tue, 1 Apr 2014 18:13:16 +0530 > Subject: [RFC 10/21] richacl: Compute maximum file masks from an acl > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > Compute upper bound owner, group, and other file masks with as few > permissions as possible without denying any permissions that the NFSv4 > acl in a richacl grants. > > This algorithm is used when a file inherits an acl at create time and > when an acl is set via a mechanism that does not specify file modes > (such as via nfsd). When user-space sets an acl, the file masks are > passed in as part of the xattr. > > When setting a richacl, the file masks determine what the file > permission bits will be set to; see richacl_masks_to_mode(). > > Signed-off-by: Andreas Gruenbacher > --- > fs/richacl_base.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++ > include/linux/richacl.h | 1 + > 2 files changed, 129 insertions(+) > > diff --git a/fs/richacl_base.c b/fs/richacl_base.c > index 83731c7..683bde2 100644 > --- a/fs/richacl_base.c > +++ b/fs/richacl_base.c > @@ -172,3 +172,131 @@ richacl_want_to_mask(unsigned int want) > return mask; > } > EXPORT_SYMBOL_GPL(richacl_want_to_mask); > + > +/** > + * richacl_allowed_to_who - mask flags allowed to a specific who value > + * > + * Computes the mask values allowed to a specific who value, taking > + * EVERYONE@ entries into account. > + */ > +static unsigned int richacl_allowed_to_who(struct richacl *acl, > + struct richace *who) > +{ > + struct richace *ace; > + unsigned int allowed = 0; > + > + richacl_for_each_entry_reverse(ace, acl) { > + if (richace_is_inherit_only(ace)) > + continue; > + if (richace_is_same_identifier(ace, who) || > + richace_is_everyone(ace)) { > + if (richace_is_allow(ace)) > + allowed |= ace->e_mask; > + else if (richace_is_deny(ace)) > + allowed &= ~ace->e_mask; > + } > + } > + return allowed; > +} > + > +/** > + * richacl_group_class_allowed - maximum permissions the group class is allowed > + * > + * See richacl_compute_max_masks(). > + */ > +static unsigned int richacl_group_class_allowed(struct richacl *acl) > +{ > + struct richace *ace; > + unsigned int everyone_allowed = 0, group_class_allowed = 0; > + int had_group_ace = 0; > + > + richacl_for_each_entry_reverse(ace, acl) { > + if (richace_is_inherit_only(ace) || > + richace_is_owner(ace)) > + continue; > + > + if (richace_is_everyone(ace)) { > + if (richace_is_allow(ace)) > + everyone_allowed |= ace->e_mask; > + else if (richace_is_deny(ace)) > + everyone_allowed &= ~ace->e_mask; > + } else { > + group_class_allowed |= > + richacl_allowed_to_who(acl, ace); > + > + if (richace_is_group(ace)) > + had_group_ace = 1; > + } > + } > + if (!had_group_ace) > + group_class_allowed |= everyone_allowed; > + return group_class_allowed; > +} > + > +/** > + * richacl_compute_max_masks - compute upper bound masks > + * > + * Computes upper bound owner, group, and other masks so that none of > + * the mask flags allowed by the acl are disabled (for any choice of the > + * file owner or group membership). > + */ > +void richacl_compute_max_masks(struct richacl *acl) > +{ > + unsigned int gmask = ~0; > + struct richace *ace; > + > + /* > + * @gmask contains all permissions which the group class is ever > + * allowed. We use it to avoid adding permissions to the group mask > + * from everyone@ allow aces which the group class is always denied > + * through other aces. For example, the following acl would otherwise > + * result in a group mask or rw: > + * > + * group@:w::deny > + * everyone@:rw::allow > + * > + * Avoid computing @gmask for acls which do not include any group class > + * deny aces: in such acls, the group class is never denied any > + * permissions from everyone@ allow aces. > + */ > + > +restart: > + acl->a_owner_mask = 0; > + acl->a_group_mask = 0; > + acl->a_other_mask = 0; > + > + richacl_for_each_entry_reverse(ace, acl) { > + if (richace_is_inherit_only(ace)) > + continue; > + > + if (richace_is_owner(ace)) { > + if (richace_is_allow(ace)) > + acl->a_owner_mask |= ace->e_mask; > + else if (richace_is_deny(ace)) > + acl->a_owner_mask &= ~ace->e_mask; > + } else if (richace_is_everyone(ace)) { > + if (richace_is_allow(ace)) { > + acl->a_owner_mask |= ace->e_mask; > + acl->a_group_mask |= ace->e_mask & gmask; > + acl->a_other_mask |= ace->e_mask; > + } else if (richace_is_deny(ace)) { > + acl->a_owner_mask &= ~ace->e_mask; > + acl->a_group_mask &= ~ace->e_mask; > + acl->a_other_mask &= ~ace->e_mask; > + } > + } else { > + if (richace_is_allow(ace)) { > + acl->a_owner_mask |= ace->e_mask & gmask; > + acl->a_group_mask |= ace->e_mask & gmask; > + } else if (richace_is_deny(ace) && gmask == ~0) { > + gmask = richacl_group_class_allowed(acl); > + if (likely(gmask != ~0)) > + /* should always be true */ > + goto restart; > + } > + } > + } > + > + acl->a_flags &= ~ACL4_MASKED; > +} > +EXPORT_SYMBOL_GPL(richacl_compute_max_masks); > diff --git a/include/linux/richacl.h b/include/linux/richacl.h > index 41819f4..05d79ac 100644 > --- a/include/linux/richacl.h > +++ b/include/linux/richacl.h > @@ -290,5 +290,6 @@ extern struct richacl *richacl_alloc(int); > extern int richacl_masks_to_mode(const struct richacl *); > extern unsigned int richacl_mode_to_mask(mode_t); > extern unsigned int richacl_want_to_mask(unsigned int); > +extern void richacl_compute_max_masks(struct richacl *); > > #endif /* __RICHACL_H */ > -- > 2.1.0 > > > From ae450198a6c8cb199f43005757598a41cc50937d Mon Sep 17 00:00:00 2001 > Message-Id: > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Tue, 1 Apr 2014 18:14:18 +0530 > Subject: [RFC 11/21] richacl: Update the file masks in chmod() > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > Doing a chmod() sets the file mode, which includes the file permission > bits. When a file has a richacl, the permissions that the richacl > grants need to be limited to what the new file permission bits allow. > > This is done by setting the file masks in the richacl to what the file > permission bits map to. The richacl access check algorithm takes the > file masks into account, which ensures that the richacl cannot grant too > many permissions. > > It is possible to explicitly add permissions to the file masks which go > beyond what the file permission bits can grant (like the ACE4_WRITE_ACL > permission). The POSIX.1 standard calls this an alternate file access > control mechanism. A subsequent chmod() would ensure that those > permissions are disabled again. > > Signed-off-by: Andreas Gruenbacher > --- > fs/richacl_base.c | 40 ++++++++++++++++++++++++++++++++++++++++ > include/linux/richacl.h | 1 + > 2 files changed, 41 insertions(+) > > diff --git a/fs/richacl_base.c b/fs/richacl_base.c > index 683bde2..7de2e9e 100644 > --- a/fs/richacl_base.c > +++ b/fs/richacl_base.c > @@ -300,3 +300,43 @@ restart: > acl->a_flags &= ~ACL4_MASKED; > } > EXPORT_SYMBOL_GPL(richacl_compute_max_masks); > + > +/** > + * richacl_chmod - update the file masks to reflect the new mode > + * @mode: new file permission bits > + * > + * Return a copy of @acl where the file masks have been replaced by the file > + * masks corresponding to the file permission bits in @mode, or returns @acl > + * itself if the file masks are already up to date. Takes over a reference > + * to @acl. > + */ > +struct richacl * > +richacl_chmod(struct richacl *acl, mode_t mode) > +{ > + unsigned int owner_mask, group_mask, other_mask; > + struct richacl *clone; > + > + owner_mask = richacl_mode_to_mask(mode >> 6) | > + ACE4_POSIX_OWNER_ALLOWED; > + group_mask = richacl_mode_to_mask(mode >> 3); > + other_mask = richacl_mode_to_mask(mode); > + > + if (acl->a_owner_mask == owner_mask && > + acl->a_group_mask == group_mask && > + acl->a_other_mask == other_mask && > + (acl->a_flags & ACL4_MASKED)) > + return acl; > + > + clone = richacl_clone(acl); > + richacl_put(acl); > + if (!clone) > + return ERR_PTR(-ENOMEM); > + > + clone->a_flags |= ACL4_MASKED; > + clone->a_owner_mask = owner_mask; > + clone->a_group_mask = group_mask; > + clone->a_other_mask = other_mask; > + > + return clone; > +} > +EXPORT_SYMBOL_GPL(richacl_chmod); > diff --git a/include/linux/richacl.h b/include/linux/richacl.h > index 05d79ac..f347125 100644 > --- a/include/linux/richacl.h > +++ b/include/linux/richacl.h > @@ -291,5 +291,6 @@ extern int richacl_masks_to_mode(const struct richacl *); > extern unsigned int richacl_mode_to_mask(mode_t); > extern unsigned int richacl_want_to_mask(unsigned int); > extern void richacl_compute_max_masks(struct richacl *); > +extern struct richacl *richacl_chmod(struct richacl *, mode_t); > > #endif /* __RICHACL_H */ > -- > 2.1.0 > > > From 516c44e08972125aee20a90e0399aaefe8e6d553 Mon Sep 17 00:00:00 2001 > Message-Id: <516c44e08972125aee20a90e0399aaefe8e6d553.1424900921.git.agruenba@redhat.com> > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Tue, 1 Apr 2014 18:15:22 +0530 > Subject: [RFC 12/21] richacl: Permission check algorithm > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > A richacl grants a requested access if the NFSv4 acl in the richacl grants the > requested permissions (according to the NFSv4 permission check algorithm) and > the file mask that applies to the process includes the requested permissions. > > Signed-off-by: Andreas Gruenbacher > --- > fs/richacl_base.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++ > include/linux/richacl.h | 1 + > 2 files changed, 113 insertions(+) > > diff --git a/fs/richacl_base.c b/fs/richacl_base.c > index 7de2e9e..7723bc8 100644 > --- a/fs/richacl_base.c > +++ b/fs/richacl_base.c > @@ -340,3 +340,115 @@ richacl_chmod(struct richacl *acl, mode_t mode) > return clone; > } > EXPORT_SYMBOL_GPL(richacl_chmod); > + > +/** > + * richacl_permission - richacl permission check algorithm > + * @inode: inode to check > + * @acl: rich acl of the inode > + * @want: requested access (MAY_* flags) > + * > + * Checks if the current process is granted @mask flags in @acl. > + */ > +int > +richacl_permission(struct inode *inode, const struct richacl *acl, > + int want) > +{ > + const struct richace *ace; > + unsigned int mask = richacl_want_to_mask(want); > + unsigned int requested = mask, denied = 0; > + int in_owning_group = in_group_p(inode->i_gid); > + int in_owner_or_group_class = in_owning_group; > + > + /* > + * We don't need to know which class the process is in when the acl is > + * not masked. > + */ > + if (!(acl->a_flags & ACL4_MASKED)) > + in_owner_or_group_class = 1; > + > + /* > + * A process is > + * - in the owner file class if it owns the file, > + * - in the group file class if it is in the file's owning group or > + * it matches any of the user or group entries, and > + * - in the other file class otherwise. > + */ > + > + /* > + * Check if the acl grants the requested access and determine which > + * file class the process is in. > + */ > + richacl_for_each_entry(ace, acl) { > + unsigned int ace_mask = ace->e_mask; > + > + if (richace_is_inherit_only(ace)) > + continue; > + if (richace_is_owner(ace)) { > + if (!uid_eq(current_fsuid(), inode->i_uid)) > + continue; > + goto is_owner; > + } else if (richace_is_group(ace)) { > + if (!in_owning_group) > + continue; > + } else if (richace_is_unix_id(ace)) { > + if (ace->e_flags & ACE4_IDENTIFIER_GROUP) { > + if (!in_group_p(make_kgid(current_user_ns(), > + ace->e_id))) > + continue; > + } else { > + if (!uid_eq(current_fsuid(), > + make_kuid(current_user_ns(), > + ace->e_id))) > + continue; > + } > + } else > + goto is_everyone; > + > + /* > + * Apply the group file mask to entries other than OWNER@ and > + * EVERYONE@. This is not required for correct access checking > + * but ensures that we grant the same permissions as the acl > + * computed by richacl_apply_masks() would grant. > + */ > + if ((acl->a_flags & ACL4_MASKED) && richace_is_allow(ace)) > + ace_mask &= acl->a_group_mask; > + > +is_owner: > + /* The process is in the owner or group file class. */ > + in_owner_or_group_class = 1; > + > +is_everyone: > + /* Check which mask flags the ACE allows or denies. */ > + if (richace_is_deny(ace)) > + denied |= ace_mask & mask; > + mask &= ~ace_mask; > + > + /* > + * Keep going until we know which file class > + * the process is in. > + */ > + if (!mask && in_owner_or_group_class) > + break; > + } > + denied |= mask; > + > + if (acl->a_flags & ACL4_MASKED) { > + unsigned int file_mask; > + > + /* > + * The file class a process is in determines which file mask > + * applies. Check if that file mask also grants the requested > + * access. > + */ > + if (uid_eq(current_fsuid(), inode->i_uid)) > + file_mask = acl->a_owner_mask; > + else if (in_owner_or_group_class) > + file_mask = acl->a_group_mask; > + else > + file_mask = acl->a_other_mask; > + denied |= requested & ~file_mask; > + } > + > + return denied ? -EACCES : 0; > +} > +EXPORT_SYMBOL_GPL(richacl_permission); > diff --git a/include/linux/richacl.h b/include/linux/richacl.h > index f347125..d92e1c2 100644 > --- a/include/linux/richacl.h > +++ b/include/linux/richacl.h > @@ -292,5 +292,6 @@ extern unsigned int richacl_mode_to_mask(mode_t); > extern unsigned int richacl_want_to_mask(unsigned int); > extern void richacl_compute_max_masks(struct richacl *); > extern struct richacl *richacl_chmod(struct richacl *, mode_t); > +extern int richacl_permission(struct inode *, const struct richacl *, int); > > #endif /* __RICHACL_H */ > -- > 2.1.0 > > > From 213ba5b03fffbcf6a7ff78a3585568eff7b43527 Mon Sep 17 00:00:00 2001 > Message-Id: <213ba5b03fffbcf6a7ff78a3585568eff7b43527.1424900921.git.agruenba@redhat.com> > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Tue, 1 Apr 2014 18:17:22 +0530 > Subject: [RFC 13/21] richacl: Create-time inheritance > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > When a new file is created, it can inherit an acl from its parent > directory; this is similar to how default acls work in POSIX (draft) > ACLs. > > As with POSIX ACLs, if a file inherits an acl from its parent directory, > the intersection between the create mode and the permissions granted by > the inherited acl determines the file masks and file permission bits, > and the umask is ignored. > > Signed-off-by: Andreas Gruenbacher > --- > fs/Makefile | 2 +- > fs/richacl_base.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ > fs/richacl_inode.c | 62 ++++++++++++++++++++++++++++++++++++++++++++ > include/linux/richacl.h | 4 +++ > 4 files changed, 136 insertions(+), 1 deletion(-) > create mode 100644 fs/richacl_inode.c > > diff --git a/fs/Makefile b/fs/Makefile > index 8f0a59c..bb96ad7 100644 > --- a/fs/Makefile > +++ b/fs/Makefile > @@ -48,7 +48,7 @@ obj-$(CONFIG_SYSCTL) += drop_caches.o > > obj-$(CONFIG_FHANDLE) += fhandle.o > obj-$(CONFIG_FS_RICHACL) += richacl.o > -richacl-y := richacl_base.o > +richacl-y := richacl_base.o richacl_inode.o > > obj-y += quota/ > > diff --git a/fs/richacl_base.c b/fs/richacl_base.c > index 7723bc8..8d9dc2c 100644 > --- a/fs/richacl_base.c > +++ b/fs/richacl_base.c > @@ -452,3 +452,72 @@ is_everyone: > return denied ? -EACCES : 0; > } > EXPORT_SYMBOL_GPL(richacl_permission); > + > +/** > + * richacl_inherit - compute the inherited acl of a new file > + * @dir_acl: acl of the containing directory > + * @isdir: inherit by a directory or non-directory? > + * > + * A directory can have acl entries which files and/or directories created > + * inside the directory will inherit. This function computes the acl for such > + * a new file. If there is no inheritable acl, it will return %NULL. > + */ > +struct richacl * > +richacl_inherit(const struct richacl *dir_acl, int isdir) > +{ > + const struct richace *dir_ace; > + struct richacl *acl = NULL; > + struct richace *ace; > + int count = 0; > + > + if (isdir) { > + richacl_for_each_entry(dir_ace, dir_acl) { > + if (!richace_is_inheritable(dir_ace)) > + continue; > + count++; > + } > + if (!count) > + return NULL; > + acl = richacl_alloc(count); > + if (!acl) > + return ERR_PTR(-ENOMEM); > + ace = acl->a_entries; > + richacl_for_each_entry(dir_ace, dir_acl) { > + if (!richace_is_inheritable(dir_ace)) > + continue; > + memcpy(ace, dir_ace, sizeof(struct richace)); > + if (dir_ace->e_flags & ACE4_NO_PROPAGATE_INHERIT_ACE) > + richace_clear_inheritance_flags(ace); > + if ((dir_ace->e_flags & ACE4_FILE_INHERIT_ACE) && > + !(dir_ace->e_flags & ACE4_DIRECTORY_INHERIT_ACE)) > + ace->e_flags |= ACE4_INHERIT_ONLY_ACE; > + ace++; > + } > + } else { > + richacl_for_each_entry(dir_ace, dir_acl) { > + if (!(dir_ace->e_flags & ACE4_FILE_INHERIT_ACE)) > + continue; > + count++; > + } > + if (!count) > + return NULL; > + acl = richacl_alloc(count); > + if (!acl) > + return ERR_PTR(-ENOMEM); > + ace = acl->a_entries; > + richacl_for_each_entry(dir_ace, dir_acl) { > + if (!(dir_ace->e_flags & ACE4_FILE_INHERIT_ACE)) > + continue; > + memcpy(ace, dir_ace, sizeof(struct richace)); > + richace_clear_inheritance_flags(ace); > + /* > + * ACE4_DELETE_CHILD is meaningless for > + * non-directories, so clear it. > + */ > + ace->e_mask &= ~ACE4_DELETE_CHILD; > + ace++; > + } > + } > + > + return acl; > +} > diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c > new file mode 100644 > index 0000000..b95a584 > --- /dev/null > +++ b/fs/richacl_inode.c > @@ -0,0 +1,62 @@ > +/* > + * Copyright (C) 2010 Novell, Inc. > + * Copyright (C) 2015 Red Hat, Inc. > + * Written by Andreas Gruenbacher > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the > + * Free Software Foundation; either version 2, or (at your option) any > + * later version. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + */ > + > +#include > +#include > +#include > +#include > + > +/** > + * richacl_inherit_inode - compute inherited acl and file mode > + * @dir_acl: acl of the containing directory > + * @inode: inode of the new file (create mode in i_mode) > + * > + * The file permission bits in inode->i_mode must be set to the create mode by > + * the caller. > + * > + * If there is an inheritable acl, the maximum permissions that the acl grants > + * will be computed and permissions not granted by the acl will be removed from > + * inode->i_mode. If there is no inheritable acl, the umask will be applied > + * instead. > + */ > +struct richacl * > +richacl_inherit_inode(const struct richacl *dir_acl, struct inode *inode) > +{ > + struct richacl *acl; > + mode_t mask; > + > + acl = richacl_inherit(dir_acl, S_ISDIR(inode->i_mode)); > + if (acl) { > + > + richacl_compute_max_masks(acl); > + > + /* > + * Ensure that the acl will not grant any permissions beyond > + * the create mode. > + */ > + acl->a_flags |= ACL4_MASKED; > + acl->a_owner_mask &= richacl_mode_to_mask(inode->i_mode >> 6) | > + ACE4_POSIX_OWNER_ALLOWED; > + acl->a_group_mask &= richacl_mode_to_mask(inode->i_mode >> 3); > + acl->a_other_mask &= richacl_mode_to_mask(inode->i_mode); > + mask = ~S_IRWXUGO | richacl_masks_to_mode(acl); > + } else > + mask = ~current_umask(); > + > + inode->i_mode &= mask; > + return acl; > +} > +EXPORT_SYMBOL_GPL(richacl_inherit_inode); > diff --git a/include/linux/richacl.h b/include/linux/richacl.h > index d92e1c2..fd3eeb4 100644 > --- a/include/linux/richacl.h > +++ b/include/linux/richacl.h > @@ -293,5 +293,9 @@ extern unsigned int richacl_want_to_mask(unsigned int); > extern void richacl_compute_max_masks(struct richacl *); > extern struct richacl *richacl_chmod(struct richacl *, mode_t); > extern int richacl_permission(struct inode *, const struct richacl *, int); > +extern struct richacl *richacl_inherit(const struct richacl *, int); > > +/* richacl_inode.c */ > +extern struct richacl *richacl_inherit_inode(const struct richacl *, > + struct inode *); > #endif /* __RICHACL_H */ > -- > 2.1.0 > > > From 410d49744f16fb757be06a4c2a9e97b9eb760d70 Mon Sep 17 00:00:00 2001 > Message-Id: <410d49744f16fb757be06a4c2a9e97b9eb760d70.1424900921.git.agruenba@redhat.com> > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Tue, 1 Apr 2014 18:18:38 +0530 > Subject: [RFC 14/21] richacl: Check if an acl is equivalent to a file > mode > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > This function is used to avoid storing richacls if the acl can be computed from > the file permission bits. > > Signed-off-by: Andreas Gruenbacher > --- > fs/richacl_base.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ > include/linux/richacl.h | 1 + > 2 files changed, 55 insertions(+) > > diff --git a/fs/richacl_base.c b/fs/richacl_base.c > index 8d9dc2c..c853f7e 100644 > --- a/fs/richacl_base.c > +++ b/fs/richacl_base.c > @@ -521,3 +521,57 @@ richacl_inherit(const struct richacl *dir_acl, int isdir) > > return acl; > } > + > +/** > + * richacl_equiv_mode - check if @acl is equivalent to file permission bits > + * @mode_p: the file mode (including the file type) > + * > + * If @acl can be fully represented by file permission bits, this function > + * returns 0, and the file permission bits in @mode_p are set to the equivalent > + * of @acl. > + * > + * This function is used to avoid storing richacls on disk if the acl can be > + * computed from the file permission bits. It allows user-space to make sure > + * that a file has no explicit richacl set. > + */ > +int > +richacl_equiv_mode(const struct richacl *acl, mode_t *mode_p) > +{ > + const struct richace *ace = acl->a_entries; > + unsigned int x; > + mode_t mode; > + > + if (acl->a_count != 1 || > + acl->a_flags != ACL4_MASKED || > + !richace_is_everyone(ace) || > + !richace_is_allow(ace) || > + ace->e_flags & ~ACE4_SPECIAL_WHO) > + return -1; > + > + /* > + * Figure out the permissions we care about: ACE4_DELETE_CHILD is > + * meaningless for non-directories, so we ignore it. > + */ > + x = ~ACE4_POSIX_ALWAYS_ALLOWED; > + if (!S_ISDIR(*mode_p)) > + x &= ~ACE4_DELETE_CHILD; > + > + mode = richacl_masks_to_mode(acl); > + if ((acl->a_group_mask & x) != (richacl_mode_to_mask(mode >> 3) & x) || > + (acl->a_other_mask & x) != (richacl_mode_to_mask(mode) & x)) > + return -1; > + > + /* > + * Ignore permissions which the owner is always allowed. > + */ > + x &= ~ACE4_POSIX_OWNER_ALLOWED; > + if ((acl->a_owner_mask & x) != (richacl_mode_to_mask(mode >> 6) & x)) > + return -1; > + > + if ((ace->e_mask & x) != (ACE4_POSIX_MODE_ALL & x)) > + return -1; > + > + *mode_p = (*mode_p & ~S_IRWXUGO) | mode; > + return 0; > +} > +EXPORT_SYMBOL_GPL(richacl_equiv_mode); > diff --git a/include/linux/richacl.h b/include/linux/richacl.h > index fd3eeb4..39072a0 100644 > --- a/include/linux/richacl.h > +++ b/include/linux/richacl.h > @@ -294,6 +294,7 @@ extern void richacl_compute_max_masks(struct richacl *); > extern struct richacl *richacl_chmod(struct richacl *, mode_t); > extern int richacl_permission(struct inode *, const struct richacl *, int); > extern struct richacl *richacl_inherit(const struct richacl *, int); > +extern int richacl_equiv_mode(const struct richacl *, mode_t *); > > /* richacl_inode.c */ > extern struct richacl *richacl_inherit_inode(const struct richacl *, > -- > 2.1.0 > > > From 39c338514faf1b135b8515db11c58720f6897e9d Mon Sep 17 00:00:00 2001 > Message-Id: <39c338514faf1b135b8515db11c58720f6897e9d.1424900921.git.agruenba@redhat.com> > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Tue, 1 Apr 2014 18:19:48 +0530 > Subject: [RFC 15/21] richacl: Automatic Inheritance > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > Automatic Inheritance (AI) allows changes to the acl of a directory to > recursively propagate down to files and directories in the directory. > > To implement this, the kernel keeps track of which permissions have been > inherited, and makes sure that permission propagation is turned off when the > file permission bits of a file are changed (upon create or chmod). > > The actual permission propagation is implemented in user space. > > Automatic Inheritance works as follows: > > - When the ACL4_AUTO_INHERIT flag in the acl of a file is not set, the > file is not affected by AI. > > - When the ACL4_AUTO_INHERIT flag in the acl of a directory is set and > a file or subdirectory is created in that directory, files created in > the directory will have the ACL4_AUTO_INHERIT flag set, and all > inherited aces will have the ACE4_INHERITED_ACE flag set. This > allows user space to distinguish between aces which have been > inherited and aces which have been explicitly added. > > - When the ACL4_PROTECTED acl flag in the acl of a file is set, AI will > not modify the acl of the file. This does not affect propagation of > permissions from the file to its children (if the file is a > directory). > > Linux does not have a way of creating files without setting the file permission > bits, so all files created inside a directory with ACL4_AUTO_INHERIT set will > also have the ACL4_PROTECTED flag set. This effectively disables Automatic > Inheritance. > > Protocols which support creating files without specifying permissions can > explicitly clear the ACL4_PROTECTED flag after creating a file and reset the > file masks to "undo" applying the create mode; see richacl_compute_max_masks(). > This is a workaround; a mechanism that would allow a process to indicate to the > kernel to ignore the create mode when there are inherited permissions would fix > this problem. > > Signed-off-by: Andreas Gruenbacher > --- > fs/richacl_base.c | 10 +++++++++- > fs/richacl_inode.c | 7 ++++++- > include/linux/richacl.h | 24 +++++++++++++++++++++++- > 3 files changed, 38 insertions(+), 3 deletions(-) > > diff --git a/fs/richacl_base.c b/fs/richacl_base.c > index c853f7e..ec570ef 100644 > --- a/fs/richacl_base.c > +++ b/fs/richacl_base.c > @@ -324,7 +324,8 @@ richacl_chmod(struct richacl *acl, mode_t mode) > if (acl->a_owner_mask == owner_mask && > acl->a_group_mask == group_mask && > acl->a_other_mask == other_mask && > - (acl->a_flags & ACL4_MASKED)) > + (acl->a_flags & ACL4_MASKED) && > + (!richacl_is_auto_inherit(acl) || richacl_is_protected(acl))) > return acl; > > clone = richacl_clone(acl); > @@ -336,6 +337,8 @@ richacl_chmod(struct richacl *acl, mode_t mode) > clone->a_owner_mask = owner_mask; > clone->a_group_mask = group_mask; > clone->a_other_mask = other_mask; > + if (richacl_is_auto_inherit(clone)) > + clone->a_flags |= ACL4_PROTECTED; > > return clone; > } > @@ -518,6 +521,11 @@ richacl_inherit(const struct richacl *dir_acl, int isdir) > ace++; > } > } > + if (richacl_is_auto_inherit(dir_acl)) { > + acl->a_flags = ACL4_AUTO_INHERIT; > + richacl_for_each_entry(ace, acl) > + ace->e_flags |= ACE4_INHERITED_ACE; > + } > > return acl; > } > diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c > index b95a584..9f96564 100644 > --- a/fs/richacl_inode.c > +++ b/fs/richacl_inode.c > @@ -40,9 +40,14 @@ richacl_inherit_inode(const struct richacl *dir_acl, struct inode *inode) > > acl = richacl_inherit(dir_acl, S_ISDIR(inode->i_mode)); > if (acl) { > + /* > + * We need to set ACL4_PROTECTED because we are > + * doing an implicit chmod > + */ > + if (richacl_is_auto_inherit(acl)) > + acl->a_flags |= ACL4_PROTECTED; > > richacl_compute_max_masks(acl); > - > /* > * Ensure that the acl will not grant any permissions beyond > * the create mode. > diff --git a/include/linux/richacl.h b/include/linux/richacl.h > index 39072a0..a607d6f 100644 > --- a/include/linux/richacl.h > +++ b/include/linux/richacl.h > @@ -49,10 +49,17 @@ struct richacl { > _ace != (_acl)->a_entries - 1; \ > _ace--) > > +/* a_flags values */ > +#define ACL4_AUTO_INHERIT 0x01 > +#define ACL4_PROTECTED 0x02 > +#define ACL4_DEFAULTED 0x04 > /* Flag values defined by richacls */ > #define ACL4_MASKED 0x80 > > #define ACL4_VALID_FLAGS ( \ > + ACL4_AUTO_INHERIT | \ > + ACL4_PROTECTED | \ > + ACL4_DEFAULTED | \ > ACL4_MASKED) > > /* e_type values */ > @@ -69,6 +76,7 @@ struct richacl { > /*#define ACE4_SUCCESSFUL_ACCESS_ACE_FLAG 0x0010*/ > /*#define ACE4_FAILED_ACCESS_ACE_FLAG 0x0020*/ > #define ACE4_IDENTIFIER_GROUP 0x0040 > +#define ACE4_INHERITED_ACE 0x0080 > /* richacl specific flag values */ > #define ACE4_SPECIAL_WHO 0x4000 > > @@ -78,6 +86,7 @@ struct richacl { > ACE4_NO_PROPAGATE_INHERIT_ACE | \ > ACE4_INHERIT_ONLY_ACE | \ > ACE4_IDENTIFIER_GROUP | \ > + ACE4_INHERITED_ACE | \ > ACE4_SPECIAL_WHO) > > /* e_mask bitflags */ > @@ -184,6 +193,18 @@ richacl_put(struct richacl *acl) > kfree(acl); > } > > +static inline int > +richacl_is_auto_inherit(const struct richacl *acl) > +{ > + return acl->a_flags & ACL4_AUTO_INHERIT; > +} > + > +static inline int > +richacl_is_protected(const struct richacl *acl) > +{ > + return acl->a_flags & ACL4_PROTECTED; > +} > + > /** > * richace_is_owner - check if @ace is an OWNER@ entry > */ > @@ -254,7 +275,8 @@ richace_clear_inheritance_flags(struct richace *ace) > ace->e_flags &= ~(ACE4_FILE_INHERIT_ACE | > ACE4_DIRECTORY_INHERIT_ACE | > ACE4_NO_PROPAGATE_INHERIT_ACE | > - ACE4_INHERIT_ONLY_ACE); > + ACE4_INHERIT_ONLY_ACE | > + ACE4_INHERITED_ACE); > } > > /** > -- > 2.1.0 > > > From 38f525822b15ec67c337cc90659fecb3737a0767 Mon Sep 17 00:00:00 2001 > Message-Id: <38f525822b15ec67c337cc90659fecb3737a0767.1424900921.git.agruenba@redhat.com> > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Tue, 1 Apr 2014 18:20:43 +0530 > Subject: [RFC 16/21] richacl: xattr mapping functions > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > Map between "system.richacl" xattrs and the in-kernel representation. > > Signed-off-by: Andreas Gruenbacher > --- > fs/Makefile | 2 +- > fs/richacl_xattr.c | 131 ++++++++++++++++++++++++++++++++++++++++++ > include/linux/richacl_xattr.h | 47 +++++++++++++++ > 3 files changed, 179 insertions(+), 1 deletion(-) > create mode 100644 fs/richacl_xattr.c > create mode 100644 include/linux/richacl_xattr.h > > diff --git a/fs/Makefile b/fs/Makefile > index bb96ad7..6155cc4 100644 > --- a/fs/Makefile > +++ b/fs/Makefile > @@ -48,7 +48,7 @@ obj-$(CONFIG_SYSCTL) += drop_caches.o > > obj-$(CONFIG_FHANDLE) += fhandle.o > obj-$(CONFIG_FS_RICHACL) += richacl.o > -richacl-y := richacl_base.o richacl_inode.o > +richacl-y := richacl_base.o richacl_inode.o richacl_xattr.o > > obj-y += quota/ > > diff --git a/fs/richacl_xattr.c b/fs/richacl_xattr.c > new file mode 100644 > index 0000000..05e5e97 > --- /dev/null > +++ b/fs/richacl_xattr.c > @@ -0,0 +1,131 @@ > +/* > + * Copyright (C) 2006, 2010 Novell, Inc. > + * Copyright (C) 2015 Red Hat, Inc. > + * Written by Andreas Gruenbacher > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the > + * Free Software Foundation; either version 2, or (at your option) any > + * later version. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + */ > + > +#include > +#include > +#include > +#include > +#include > + > +MODULE_LICENSE("GPL"); > + > +/** > + * richacl_from_xattr - convert a richacl xattr into the in-memory representation > + */ > +struct richacl * > +richacl_from_xattr(const void *value, size_t size) > +{ > + const struct richacl_xattr *xattr_acl = value; > + const struct richace_xattr *xattr_ace = (void *)(xattr_acl + 1); > + struct richacl *acl; > + struct richace *ace; > + int count; > + > + if (size < sizeof(struct richacl_xattr) || > + xattr_acl->a_version != ACL4_XATTR_VERSION || > + (xattr_acl->a_flags & ~ACL4_VALID_FLAGS)) > + return ERR_PTR(-EINVAL); > + > + count = le16_to_cpu(xattr_acl->a_count); > + if (count > ACL4_XATTR_MAX_COUNT) > + return ERR_PTR(-EINVAL); > + > + acl = richacl_alloc(count); > + if (!acl) > + return ERR_PTR(-ENOMEM); > + > + acl->a_flags = xattr_acl->a_flags; > + acl->a_owner_mask = le32_to_cpu(xattr_acl->a_owner_mask); > + if (acl->a_owner_mask & ~ACE4_VALID_MASK) > + goto fail_einval; > + acl->a_group_mask = le32_to_cpu(xattr_acl->a_group_mask); > + if (acl->a_group_mask & ~ACE4_VALID_MASK) > + goto fail_einval; > + acl->a_other_mask = le32_to_cpu(xattr_acl->a_other_mask); > + if (acl->a_other_mask & ~ACE4_VALID_MASK) > + goto fail_einval; > + > + if (((void *)xattr_ace + count * sizeof(*xattr_ace)) > (value + size)) > + goto fail_einval; > + > + richacl_for_each_entry(ace, acl) { > + > + ace->e_type = le16_to_cpu(xattr_ace->e_type); > + ace->e_flags = le16_to_cpu(xattr_ace->e_flags); > + ace->e_mask = le32_to_cpu(xattr_ace->e_mask); > + ace->e_id = le32_to_cpu(xattr_ace->e_id); > + > + if (ace->e_flags & ~ACE4_VALID_FLAGS) > + goto fail_einval; > + if (ace->e_type > ACE4_ACCESS_DENIED_ACE_TYPE || > + (ace->e_mask & ~ACE4_VALID_MASK)) > + goto fail_einval; > + > + xattr_ace++; > + } > + > + return acl; > + > +fail_einval: > + richacl_put(acl); > + return ERR_PTR(-EINVAL); > +} > +EXPORT_SYMBOL_GPL(richacl_from_xattr); > + > +/** > + * richacl_xattr_size - compute the size of the xattr representation of @acl > + */ > +size_t > +richacl_xattr_size(const struct richacl *acl) > +{ > + size_t size = sizeof(struct richacl_xattr); > + > + size += sizeof(struct richace_xattr) * acl->a_count; > + return size; > +} > +EXPORT_SYMBOL_GPL(richacl_xattr_size); > + > +/** > + * richacl_to_xattr - convert @acl into its xattr representation > + * @acl: the richacl to convert > + * @buffer: buffer of size richacl_xattr_size(@acl) for the result > + */ > +void > +richacl_to_xattr(const struct richacl *acl, void *buffer) > +{ > + struct richacl_xattr *xattr_acl = buffer; > + struct richace_xattr *xattr_ace; > + const struct richace *ace; > + > + xattr_acl->a_version = ACL4_XATTR_VERSION; > + xattr_acl->a_flags = acl->a_flags; > + xattr_acl->a_count = cpu_to_le16(acl->a_count); > + > + xattr_acl->a_owner_mask = cpu_to_le32(acl->a_owner_mask); > + xattr_acl->a_group_mask = cpu_to_le32(acl->a_group_mask); > + xattr_acl->a_other_mask = cpu_to_le32(acl->a_other_mask); > + > + xattr_ace = (void *)(xattr_acl + 1); > + richacl_for_each_entry(ace, acl) { > + xattr_ace->e_type = cpu_to_le16(ace->e_type); > + xattr_ace->e_flags = cpu_to_le16(ace->e_flags & > + ACE4_VALID_FLAGS); > + xattr_ace->e_mask = cpu_to_le32(ace->e_mask); > + xattr_ace->e_id = cpu_to_le32(ace->e_id); > + xattr_ace++; > + } > +} > +EXPORT_SYMBOL_GPL(richacl_to_xattr); > diff --git a/include/linux/richacl_xattr.h b/include/linux/richacl_xattr.h > new file mode 100644 > index 0000000..32ae512 > --- /dev/null > +++ b/include/linux/richacl_xattr.h > @@ -0,0 +1,47 @@ > +/* > + * Copyright (C) 2006, 2010 Novell, Inc. > + * Copyright (C) 2015 Red Hat, Inc. > + * Written by Andreas Gruenbacher > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the > + * Free Software Foundation; either version 2, or (at your option) any > + * later version. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + */ > + > +#ifndef __RICHACL_XATTR_H > +#define __RICHACL_XATTR_H > + > +#include > + > +#define RICHACL_XATTR "system.richacl" > + > +struct richace_xattr { > + __le16 e_type; > + __le16 e_flags; > + __le32 e_mask; > + __le32 e_id; > +}; > + > +struct richacl_xattr { > + unsigned char a_version; > + unsigned char a_flags; > + __le16 a_count; > + __le32 a_owner_mask; > + __le32 a_group_mask; > + __le32 a_other_mask; > +}; > + > +#define ACL4_XATTR_VERSION 0 > +#define ACL4_XATTR_MAX_COUNT 1024 > + > +extern struct richacl *richacl_from_xattr(const void *, size_t); > +extern size_t richacl_xattr_size(const struct richacl *acl); > +extern void richacl_to_xattr(const struct richacl *, void *); > + > +#endif /* __RICHACL_XATTR_H */ > -- > 2.1.0 > > > From ae174bdfb12f44f592301bec7c0e69688bb4d3b7 Mon Sep 17 00:00:00 2001 > Message-Id: > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Sat, 14 Feb 2015 19:31:38 +0100 > Subject: [RFC 17/21] vfs: Cache base_acl objects in inodes > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > POSIX ACLs and richacls are both objects allocated by kmalloc() with a > reference count which are freed by kfree_rcu(). An inode can either cache an > access and a default POSIX ACL, or a richacl. (Richacls do not have default > acls). To allow an inode to cache either of the two kinds of acls, introduce a > new base_acl type and convert i_acl and i_default_acl to that type. In most > cases, the vfs then doesn't have to care which kind of acl an inode caches (if > any). > > Signed-off-by: Andreas Gruenbacher > --- > drivers/staging/lustre/lustre/llite/llite_lib.c | 2 +- > fs/f2fs/acl.c | 4 ++-- > fs/inode.c | 4 ++-- > fs/posix_acl.c | 18 +++++++++--------- > include/linux/fs.h | 22 +++++++++++++++++++--- > include/linux/posix_acl.h | 9 ++++----- > include/linux/richacl.h | 2 +- > 7 files changed, 38 insertions(+), 23 deletions(-) > > diff --git a/drivers/staging/lustre/lustre/llite/llite_lib.c b/drivers/staging/lustre/lustre/llite/llite_lib.c > index 0c1b583..c8cae33 100644 > --- a/drivers/staging/lustre/lustre/llite/llite_lib.c > +++ b/drivers/staging/lustre/lustre/llite/llite_lib.c > @@ -1145,7 +1145,7 @@ void ll_clear_inode(struct inode *inode) > } > #ifdef CONFIG_FS_POSIX_ACL > else if (lli->lli_posix_acl) { > - LASSERT(atomic_read(&lli->lli_posix_acl->a_refcount) == 1); > + LASSERT(atomic_read(&lli->lli_posix_acl->a_base.ba_refcount) == 1); > LASSERT(lli->lli_remote_perms == NULL); > posix_acl_release(lli->lli_posix_acl); > lli->lli_posix_acl = NULL; > diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c > index 7422027..ccb2c7c 100644 > --- a/fs/f2fs/acl.c > +++ b/fs/f2fs/acl.c > @@ -270,7 +270,7 @@ static struct posix_acl *f2fs_acl_clone(const struct posix_acl *acl, > sizeof(struct posix_acl_entry); > clone = kmemdup(acl, size, flags); > if (clone) > - atomic_set(&clone->a_refcount, 1); > + atomic_set(&clone->a_base.ba_refcount, 1); > } > return clone; > } > @@ -282,7 +282,7 @@ static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p) > umode_t mode = *mode_p; > int not_equiv = 0; > > - /* assert(atomic_read(acl->a_refcount) == 1); */ > + /* assert(atomic_read(acl->a_base.ba_refcount) == 1); */ > > FOREACH_ACL_ENTRY(pa, acl, pe) { > switch(pa->e_tag) { > diff --git a/fs/inode.c b/fs/inode.c > index f00b16f..555fe9c 100644 > --- a/fs/inode.c > +++ b/fs/inode.c > @@ -233,9 +233,9 @@ void __destroy_inode(struct inode *inode) > > #ifdef CONFIG_FS_POSIX_ACL > if (inode->i_acl && inode->i_acl != ACL_NOT_CACHED) > - posix_acl_release(inode->i_acl); > + put_base_acl(inode->i_acl); > if (inode->i_default_acl && inode->i_default_acl != ACL_NOT_CACHED) > - posix_acl_release(inode->i_default_acl); > + put_base_acl(inode->i_default_acl); > #endif > this_cpu_dec(nr_inodes); > } > diff --git a/fs/posix_acl.c b/fs/posix_acl.c > index efe983e..2fbfec8 100644 > --- a/fs/posix_acl.c > +++ b/fs/posix_acl.c > @@ -25,9 +25,9 @@ struct posix_acl **acl_by_type(struct inode *inode, int type) > { > switch (type) { > case ACL_TYPE_ACCESS: > - return &inode->i_acl; > + return (struct posix_acl **)&inode->i_acl; > case ACL_TYPE_DEFAULT: > - return &inode->i_default_acl; > + return (struct posix_acl **)&inode->i_default_acl; > default: > BUG(); > } > @@ -83,16 +83,16 @@ EXPORT_SYMBOL(forget_cached_acl); > > void forget_all_cached_acls(struct inode *inode) > { > - struct posix_acl *old_access, *old_default; > + struct base_acl *old_access, *old_default; > spin_lock(&inode->i_lock); > old_access = inode->i_acl; > old_default = inode->i_default_acl; > inode->i_acl = inode->i_default_acl = ACL_NOT_CACHED; > spin_unlock(&inode->i_lock); > if (old_access != ACL_NOT_CACHED) > - posix_acl_release(old_access); > + put_base_acl(old_access); > if (old_default != ACL_NOT_CACHED) > - posix_acl_release(old_default); > + put_base_acl(old_default); > } > EXPORT_SYMBOL(forget_all_cached_acls); > > @@ -129,7 +129,7 @@ EXPORT_SYMBOL(get_acl); > void > posix_acl_init(struct posix_acl *acl, int count) > { > - atomic_set(&acl->a_refcount, 1); > + atomic_set(&acl->a_base.ba_refcount, 1); > acl->a_count = count; > } > EXPORT_SYMBOL(posix_acl_init); > @@ -163,7 +163,7 @@ posix_acl_clone(const struct posix_acl *acl, gfp_t flags) > sizeof(struct posix_acl_entry); > clone = kmemdup(acl, size, flags); > if (clone) > - atomic_set(&clone->a_refcount, 1); > + atomic_set(&clone->a_base.ba_refcount, 1); > } > return clone; > } > @@ -385,7 +385,7 @@ static int posix_acl_create_masq(struct posix_acl *acl, umode_t *mode_p) > umode_t mode = *mode_p; > int not_equiv = 0; > > - /* assert(atomic_read(acl->a_refcount) == 1); */ > + /* assert(atomic_read(acl->a_base.ba_refcount) == 1); */ > > FOREACH_ACL_ENTRY(pa, acl, pe) { > switch(pa->e_tag) { > @@ -440,7 +440,7 @@ static int __posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode) > struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; > struct posix_acl_entry *pa, *pe; > > - /* assert(atomic_read(acl->a_refcount) == 1); */ > + /* assert(atomic_read(acl->a_base.ba_refcount) == 1); */ > > FOREACH_ACL_ENTRY(pa, acl, pe) { > switch(pa->e_tag) { > diff --git a/include/linux/fs.h b/include/linux/fs.h > index e3e1e42..518b990 100644 > --- a/include/linux/fs.h > +++ b/include/linux/fs.h > @@ -547,6 +547,9 @@ static inline void mapping_allow_writable(struct address_space *mapping) > #define i_size_ordered_init(inode) do { } while (0) > #endif > > +struct base_acl { > + atomic_t ba_refcount; > +}; > struct posix_acl; > #define ACL_NOT_CACHED ((void *)(-1)) > > @@ -566,9 +569,9 @@ struct inode { > kgid_t i_gid; > unsigned int i_flags; > > -#ifdef CONFIG_FS_POSIX_ACL > - struct posix_acl *i_acl; > - struct posix_acl *i_default_acl; > +#if defined(CONFIG_FS_POSIX_ACL) > + struct base_acl *i_acl; > + struct base_acl *i_default_acl; > #endif > > const struct inode_operations *i_op; > @@ -2936,4 +2939,17 @@ static inline bool dir_relax(struct inode *inode) > return !IS_DEADDIR(inode); > } > > +static inline struct base_acl *get_base_acl(struct base_acl *acl) > +{ > + if (acl) > + atomic_inc(&acl->ba_refcount); > + return acl; > +} > + > +static inline void put_base_acl(struct base_acl *acl) > +{ > + if (acl && atomic_dec_and_test(&acl->ba_refcount)) > + __kfree_rcu((struct rcu_head *)acl, 0); > +} > + > #endif /* _LINUX_FS_H */ > diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h > index 66cf477..2c46441 100644 > --- a/include/linux/posix_acl.h > +++ b/include/linux/posix_acl.h > @@ -43,7 +43,7 @@ struct posix_acl_entry { > }; > > struct posix_acl { > - atomic_t a_refcount; > + struct base_acl a_base; > unsigned int a_count; > struct posix_acl_entry a_entries[0]; > }; > @@ -58,8 +58,7 @@ struct posix_acl { > static inline struct posix_acl * > posix_acl_dup(struct posix_acl *acl) > { > - if (acl) > - atomic_inc(&acl->a_refcount); > + get_base_acl(&acl->a_base); > return acl; > } > > @@ -69,8 +68,8 @@ posix_acl_dup(struct posix_acl *acl) > static inline void > posix_acl_release(struct posix_acl *acl) > { > - if (acl && atomic_dec_and_test(&acl->a_refcount)) > - __kfree_rcu((struct rcu_head *)acl, 0); > + BUILD_BUG_ON(offsetof(struct posix_acl, a_base) != 0); > + put_base_acl(&acl->a_base); > } > > > diff --git a/include/linux/richacl.h b/include/linux/richacl.h > index a607d6f..60568c5 100644 > --- a/include/linux/richacl.h > +++ b/include/linux/richacl.h > @@ -179,7 +179,7 @@ static inline struct richacl * > richacl_get(struct richacl *acl) > { > if (acl) > - atomic_inc(&acl->a_refcount); > + atomic_inc(&acl->a_base.ba_refcount); > return acl; > } > > -- > 2.1.0 > > > From 3f5c803548a9fc24f1b7f0be25524fb6bd41ccdd Mon Sep 17 00:00:00 2001 > Message-Id: <3f5c803548a9fc24f1b7f0be25524fb6bd41ccdd.1424900921.git.agruenba@redhat.com> > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Tue, 1 Apr 2014 19:28:44 +0530 > Subject: [RFC 18/21] vfs: Cache richacl in struct inode > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > Cache richacls in struct inode so that this doesn't have to be done > individually in each filesystem. This is similar to POSIX ACLs. > > Signed-off-by: Andreas Gruenbacher > --- > fs/inode.c | 11 +++++-- > fs/posix_acl.c | 2 +- > fs/richacl_base.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++-- > include/linux/fs.h | 6 +++- > include/linux/richacl.h | 15 ++++++--- > 5 files changed, 102 insertions(+), 13 deletions(-) > > diff --git a/fs/inode.c b/fs/inode.c > index 555fe9c..5272412 100644 > --- a/fs/inode.c > +++ b/fs/inode.c > @@ -175,8 +175,11 @@ int inode_init_always(struct super_block *sb, struct inode *inode) > inode->i_private = NULL; > inode->i_mapping = mapping; > INIT_HLIST_HEAD(&inode->i_dentry); /* buggered by rcu freeing */ > -#ifdef CONFIG_FS_POSIX_ACL > - inode->i_acl = inode->i_default_acl = ACL_NOT_CACHED; > +#if defined(CONFIG_FS_POSIX_ACL) || defined(CONFIG_FS_RICHACL) > + inode->i_acl = ACL_NOT_CACHED; > +# if defined(CONFIG_FS_POSIX_ACL) > + inode->i_default_acl = ACL_NOT_CACHED; > +# endif > #endif > > #ifdef CONFIG_FSNOTIFY > @@ -231,11 +234,13 @@ void __destroy_inode(struct inode *inode) > atomic_long_dec(&inode->i_sb->s_remove_count); > } > > -#ifdef CONFIG_FS_POSIX_ACL > +#if defined(CONFIG_FS_POSIX_ACL) || defined(CONFIG_FS_RICHACL) > if (inode->i_acl && inode->i_acl != ACL_NOT_CACHED) > put_base_acl(inode->i_acl); > +# if defined(CONFIG_FS_POSIX_ACL) > if (inode->i_default_acl && inode->i_default_acl != ACL_NOT_CACHED) > put_base_acl(inode->i_default_acl); > +# endif > #endif > this_cpu_dec(nr_inodes); > } > diff --git a/fs/posix_acl.c b/fs/posix_acl.c > index 2fbfec8..ebf96b2 100644 > --- a/fs/posix_acl.c > +++ b/fs/posix_acl.c > @@ -38,7 +38,7 @@ struct posix_acl *get_cached_acl(struct inode *inode, int type) > { > struct posix_acl **p = acl_by_type(inode, type); > struct posix_acl *acl = ACCESS_ONCE(*p); > - if (acl) { > + if (acl && IS_POSIXACL(inode)) { > spin_lock(&inode->i_lock); > acl = *p; > if (acl != ACL_NOT_CACHED) > diff --git a/fs/richacl_base.c b/fs/richacl_base.c > index ec570ef..ea53ad5 100644 > --- a/fs/richacl_base.c > +++ b/fs/richacl_base.c > @@ -21,6 +21,79 @@ > > MODULE_LICENSE("GPL"); > > +struct richacl *get_cached_richacl(struct inode *inode) > +{ > + struct richacl *acl; > + > + acl = (struct richacl *)ACCESS_ONCE(inode->i_acl); > + if (acl && IS_RICHACL(inode)) { > + spin_lock(&inode->i_lock); > + acl = (struct richacl *)inode->i_acl; > + if (acl != ACL_NOT_CACHED) > + acl = richacl_get(acl); > + spin_unlock(&inode->i_lock); > + } > + return acl; > +} > +EXPORT_SYMBOL(get_cached_richacl); > + > +struct richacl *get_cached_richacl_rcu(struct inode *inode) > +{ > + return (struct richacl *)rcu_dereference(inode->i_acl); > +} > +EXPORT_SYMBOL(get_cached_richacl_rcu); > + > +void set_cached_richacl(struct inode *inode, struct richacl *acl) > +{ > + struct base_acl *old = NULL; > + spin_lock(&inode->i_lock); > + old = inode->i_acl; > + inode->i_acl = &(richacl_get(acl)->a_base); > + spin_unlock(&inode->i_lock); > + if (old != ACL_NOT_CACHED) > + put_base_acl(old); > +} > +EXPORT_SYMBOL(set_cached_richacl); > + > +void forget_cached_richacl(struct inode *inode) > +{ > + struct base_acl *old = NULL; > + spin_lock(&inode->i_lock); > + old = inode->i_acl; > + inode->i_acl = ACL_NOT_CACHED; > + spin_unlock(&inode->i_lock); > + if (old != ACL_NOT_CACHED) > + put_base_acl(old); > +} > +EXPORT_SYMBOL(forget_cached_richacl); > + > +struct richacl *get_richacl(struct inode *inode) > +{ > + struct richacl *acl; > + > + acl = get_cached_richacl(inode); > + if (acl != ACL_NOT_CACHED) > + return acl; > + > + if (!IS_RICHACL(inode)) > + return NULL; > + > + /* > + * A filesystem can force a ACL callback by just never filling the > + * ACL cache. But normally you'd fill the cache either at inode > + * instantiation time, or on the first ->get_richacl call. > + * > + * If the filesystem doesn't have a get_richacl() function at all, > + * we'll just create the negative cache entry. > + */ > + if (!inode->i_op->get_richacl) { > + set_cached_richacl(inode, NULL); > + return NULL; > + } > + return inode->i_op->get_richacl(inode); > +} > +EXPORT_SYMBOL_GPL(get_richacl); > + > /** > * richacl_alloc - allocate a richacl > * @count: number of entries > @@ -28,11 +101,13 @@ MODULE_LICENSE("GPL"); > struct richacl * > richacl_alloc(int count) > { > - size_t size = sizeof(struct richacl) + count * sizeof(struct richace); > + size_t size = max(sizeof(struct rcu_head), > + sizeof(struct richacl) + > + count * sizeof(struct richace)); > struct richacl *acl = kzalloc(size, GFP_KERNEL); > > if (acl) { > - atomic_set(&acl->a_refcount, 1); > + atomic_set(&acl->a_base.ba_refcount, 1); > acl->a_count = count; > } > return acl; > @@ -51,7 +126,7 @@ richacl_clone(const struct richacl *acl) > > if (dup) { > memcpy(dup, acl, size); > - atomic_set(&dup->a_refcount, 1); > + atomic_set(&dup->a_base.ba_refcount, 1); > } > return dup; > } > diff --git a/include/linux/fs.h b/include/linux/fs.h > index 518b990..e3f27b5 100644 > --- a/include/linux/fs.h > +++ b/include/linux/fs.h > @@ -551,6 +551,7 @@ struct base_acl { > atomic_t ba_refcount; > }; > struct posix_acl; > +struct richacl; > #define ACL_NOT_CACHED ((void *)(-1)) > > #define IOP_FASTPERM 0x0001 > @@ -569,9 +570,11 @@ struct inode { > kgid_t i_gid; > unsigned int i_flags; > > -#if defined(CONFIG_FS_POSIX_ACL) > +#if defined(CONFIG_FS_POSIX_ACL) || defined(CONFIG_FS_RICHACL) > struct base_acl *i_acl; > +# if defined(CONFIG_FS_POSIX_ACL) > struct base_acl *i_default_acl; > +# endif > #endif > > const struct inode_operations *i_op; > @@ -1586,6 +1589,7 @@ struct inode_operations { > void * (*follow_link) (struct dentry *, struct nameidata *); > int (*permission) (struct inode *, int); > struct posix_acl * (*get_acl)(struct inode *, int); > + struct richacl * (*get_richacl)(struct inode *); > > int (*readlink) (struct dentry *, char __user *,int); > void (*put_link) (struct dentry *, struct nameidata *, void *); > diff --git a/include/linux/richacl.h b/include/linux/richacl.h > index 60568c5..b314643 100644 > --- a/include/linux/richacl.h > +++ b/include/linux/richacl.h > @@ -30,7 +30,7 @@ struct richace { > }; > > struct richacl { > - atomic_t a_refcount; > + struct base_acl a_base; > unsigned int a_owner_mask; > unsigned int a_group_mask; > unsigned int a_other_mask; > @@ -178,8 +178,7 @@ struct richacl { > static inline struct richacl * > richacl_get(struct richacl *acl) > { > - if (acl) > - atomic_inc(&acl->a_base.ba_refcount); > + get_base_acl(&acl->a_base); > return acl; > } > > @@ -189,10 +188,16 @@ richacl_get(struct richacl *acl) > static inline void > richacl_put(struct richacl *acl) > { > - if (acl && atomic_dec_and_test(&acl->a_refcount)) > - kfree(acl); > + BUILD_BUG_ON(offsetof(struct richacl, a_base) != 0); > + put_base_acl(&acl->a_base); > } > > +extern struct richacl *get_cached_richacl(struct inode *); > +extern struct richacl *get_cached_richacl_rcu(struct inode *); > +extern void set_cached_richacl(struct inode *, struct richacl *); > +extern void forget_cached_richacl(struct inode *); > +extern struct richacl *get_richacl(struct inode *); > + > static inline int > richacl_is_auto_inherit(const struct richacl *acl) > { > -- > 2.1.0 > > > From b467e4dcfbff041accd57839765468c4042a20c5 Mon Sep 17 00:00:00 2001 > Message-Id: > In-Reply-To: > References: > From: Andreas Gruenbacher > Date: Tue, 1 Apr 2014 18:08:42 +0530 > Subject: [RFC 19/21] vfs: Add richacl permission checking > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > Hook the richacl permission checking function into the vfs. > > Signed-off-by: Andreas Gruenbacher > --- > fs/namei.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- > fs/posix_acl.c | 6 +++--- > 2 files changed, 52 insertions(+), 5 deletions(-) > > diff --git a/fs/namei.c b/fs/namei.c > index a8d1674..d5b4fcd 100644 > --- a/fs/namei.c > +++ b/fs/namei.c > @@ -35,6 +35,7 @@ > #include > #include > #include > +#include > #include > > #include "internal.h" > @@ -256,7 +257,40 @@ void putname(struct filename *name) > __putname(name); > } > > -static int check_acl(struct inode *inode, int mask) > +static int check_richacl(struct inode *inode, int mask) > +{ > +#ifdef CONFIG_FS_RICHACL > + struct richacl *acl; > + > + if (mask & MAY_NOT_BLOCK) { > + acl = get_cached_richacl_rcu(inode); > + if (!acl) > + goto no_acl; > + /* no ->get_richacl() calls in RCU mode... */ > + if (acl == ACL_NOT_CACHED) > + return -ECHILD; > + return richacl_permission(inode, acl, mask & ~MAY_NOT_BLOCK); > + } > + > + acl = get_richacl(inode); > + if (IS_ERR(acl)) > + return PTR_ERR(acl); > + if (acl) { > + int error = richacl_permission(inode, acl, mask); > + richacl_put(acl); > + return error; > + } > +no_acl: > +#endif > + if (mask & (MAY_DELETE_SELF | MAY_TAKE_OWNERSHIP | > + MAY_CHMOD | MAY_SET_TIMES)) { > + /* File permission bits cannot grant this. */ > + return -EACCES; > + } > + return -EAGAIN; > +} > + > +static int check_posix_acl(struct inode *inode, int mask) > { > #ifdef CONFIG_FS_POSIX_ACL > struct posix_acl *acl; > @@ -291,11 +325,24 @@ static int acl_permission_check(struct inode *inode, int mask) > { > unsigned int mode = inode->i_mode; > > + /* > + * With POSIX ACLs, the (mode & S_IRWXU) bits exactly match the owner > + * permissions, and we can skip checking posix acls for the owner. > + * With richacls, the owner may be granted fewer permissions than the > + * mode bits seem to suggest (for example, append but not write), and > + * we always need to check the richacl. > + */ > + > + if (IS_RICHACL(inode)) { > + int error = check_richacl(inode, mask); > + if (error != -EAGAIN) > + return error; > + } > if (likely(uid_eq(current_fsuid(), inode->i_uid))) > mode >>= 6; > else { > if (IS_POSIXACL(inode) && (mode & S_IRWXG)) { > - int error = check_acl(inode, mask); > + int error = check_posix_acl(inode, mask); > if (error != -EAGAIN) > return error; > } > diff --git a/fs/posix_acl.c b/fs/posix_acl.c > index ebf96b2..16464f0 100644 > --- a/fs/posix_acl.c > +++ b/fs/posix_acl.c > @@ -100,13 +100,13 @@ struct posix_acl *get_acl(struct inode *inode, int type) > { > struct posix_acl *acl; > > + if (!IS_POSIXACL(inode)) > + return NULL; > + > acl = get_cached_acl(inode, type); > if (acl != ACL_NOT_CACHED) > return acl; > > - if (!IS_POSIXACL(inode)) > - return NULL; > - > /* > * A filesystem can force a ACL callback by just never filling the > * ACL cache. But normally you'd fill the cache either at inode > -- > 2.1.0 > > > From c6043a752cec38940291b0caca452826afb1fa04 Mon Sep 17 00:00:00 2001 > Message-Id: > In-Reply-To: > References: > From: "Aneesh Kumar K.V" > Date: Wed, 23 Apr 2014 20:54:41 +0530 > Subject: [RFC 20/21] ext4: Implement rich acl for ext4 > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > Support the richacl permission model in ext4. The richacls are stored in > "system.richacl" xattrs. Richacls need to be enabled by tune2fs or at file > system create time. > > Signed-off-by: Andreas Gruenbacher > --- > fs/ext4/Kconfig | 15 ++++ > fs/ext4/Makefile | 1 + > fs/ext4/acl.c | 7 +- > fs/ext4/acl.h | 12 +-- > fs/ext4/file.c | 6 +- > fs/ext4/ialloc.c | 7 +- > fs/ext4/inode.c | 10 ++- > fs/ext4/namei.c | 11 ++- > fs/ext4/richacl.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > fs/ext4/richacl.h | 47 +++++++++++ > fs/ext4/xattr.c | 6 ++ > fs/ext4/xattr.h | 1 + > 12 files changed, 332 insertions(+), 20 deletions(-) > create mode 100644 fs/ext4/richacl.c > create mode 100644 fs/ext4/richacl.h > > diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig > index efea5d5..8c821d2 100644 > --- a/fs/ext4/Kconfig > +++ b/fs/ext4/Kconfig > @@ -73,3 +73,18 @@ config EXT4_DEBUG > If you select Y here, then you will be able to turn on debugging > with a command such as: > echo 1 > /sys/module/ext4/parameters/mballoc_debug > + > +config EXT4_FS_RICHACL > + bool "Ext4 Rich Access Control Lists (EXPERIMENTAL)" > + depends on EXT4_FS > + select FS_RICHACL > + help > + Rich ACLs are an implementation of NFSv4 ACLs, extended by file masks > + to fit into the standard POSIX file permission model. They are > + designed to work seamlessly locally as well as across the NFSv4 and > + CIFS/SMB2 network file system protocols. > + > + To learn more about Rich ACL, visit > + http://acl.bestbits.at/richacl/ > + > + If you don't know what Rich ACLs are, say N > diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile > index 0310fec..b9a3e2e 100644 > --- a/fs/ext4/Makefile > +++ b/fs/ext4/Makefile > @@ -12,3 +12,4 @@ ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \ > > ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o > ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o > +ext4-$(CONFIG_EXT4_FS_RICHACL) += richacl.o > diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c > index d40c8db..7c508f7 100644 > --- a/fs/ext4/acl.c > +++ b/fs/ext4/acl.c > @@ -144,8 +144,7 @@ fail: > * > * inode->i_mutex: don't care > */ > -struct posix_acl * > -ext4_get_acl(struct inode *inode, int type) > +struct posix_acl *ext4_get_posix_acl(struct inode *inode, int type) > { > int name_index; > char *value = NULL; > @@ -239,7 +238,7 @@ __ext4_set_acl(handle_t *handle, struct inode *inode, int type, > } > > int > -ext4_set_acl(struct inode *inode, struct posix_acl *acl, int type) > +ext4_set_posix_acl(struct inode *inode, struct posix_acl *acl, int type) > { > handle_t *handle; > int error, retries = 0; > @@ -264,7 +263,7 @@ retry: > * inode->i_mutex: up (access to inode is still exclusive) > */ > int > -ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir) > +ext4_init_posix_acl(handle_t *handle, struct inode *inode, struct inode *dir) > { > struct posix_acl *default_acl, *acl; > int error; > diff --git a/fs/ext4/acl.h b/fs/ext4/acl.h > index da2c795..450b4d1 100644 > --- a/fs/ext4/acl.h > +++ b/fs/ext4/acl.h > @@ -54,17 +54,17 @@ static inline int ext4_acl_count(size_t size) > #ifdef CONFIG_EXT4_FS_POSIX_ACL > > /* acl.c */ > -struct posix_acl *ext4_get_acl(struct inode *inode, int type); > -int ext4_set_acl(struct inode *inode, struct posix_acl *acl, int type); > -extern int ext4_init_acl(handle_t *, struct inode *, struct inode *); > +struct posix_acl *ext4_get_posix_acl(struct inode *inode, int type); > +int ext4_set_posix_acl(struct inode *inode, struct posix_acl *acl, int type); > +extern int ext4_init_posix_acl(handle_t *, struct inode *, struct inode *); > > #else /* CONFIG_EXT4_FS_POSIX_ACL */ > #include > -#define ext4_get_acl NULL > -#define ext4_set_acl NULL > +#define ext4_get_posix_acl NULL > +#define ext4_set_posix_acl NULL > > static inline int > -ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir) > +ext4_init_posix_acl(handle_t *handle, struct inode *inode, struct inode *dir) > { > return 0; > } > diff --git a/fs/ext4/file.c b/fs/ext4/file.c > index 33a09da..be466f7 100644 > --- a/fs/ext4/file.c > +++ b/fs/ext4/file.c > @@ -30,6 +30,7 @@ > #include "ext4_jbd2.h" > #include "xattr.h" > #include "acl.h" > +#include "richacl.h" > > /* > * Called when an inode is released. Note that this is different > @@ -651,8 +652,9 @@ const struct inode_operations ext4_file_inode_operations = { > .getxattr = generic_getxattr, > .listxattr = ext4_listxattr, > .removexattr = generic_removexattr, > - .get_acl = ext4_get_acl, > - .set_acl = ext4_set_acl, > + .get_acl = ext4_get_posix_acl, > + .set_acl = ext4_set_posix_acl, > + .get_richacl = ext4_get_richacl, > .fiemap = ext4_fiemap, > }; > > diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c > index ac644c3..97d1c4b 100644 > --- a/fs/ext4/ialloc.c > +++ b/fs/ext4/ialloc.c > @@ -28,6 +28,7 @@ > #include "ext4_jbd2.h" > #include "xattr.h" > #include "acl.h" > +#include "richacl.h" > > #include > > @@ -1039,7 +1040,11 @@ got: > if (err) > goto fail_drop; > > - err = ext4_init_acl(handle, inode, dir); > + if (EXT4_IS_RICHACL(dir)) > + err = ext4_init_richacl(handle, inode, dir); > + else > + err = ext4_init_posix_acl(handle, inode, dir); > + > if (err) > goto fail_free_drop; > > diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c > index 5cb9a21..c379742 100644 > --- a/fs/ext4/inode.c > +++ b/fs/ext4/inode.c > @@ -44,6 +44,7 @@ > #include "xattr.h" > #include "acl.h" > #include "truncate.h" > +#include "richacl.h" > > #include > > @@ -4657,9 +4658,12 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) > if (orphan && inode->i_nlink) > ext4_orphan_del(NULL, inode); > > - if (!rc && (ia_valid & ATTR_MODE)) > - rc = posix_acl_chmod(inode, inode->i_mode); > - > + if (!rc && (ia_valid & ATTR_MODE)) { > + if (EXT4_IS_RICHACL(inode)) > + rc = ext4_richacl_chmod(inode); > + else > + rc = posix_acl_chmod(inode, inode->i_mode); > + } > err_out: > ext4_std_error(inode->i_sb, error); > if (!error) > diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c > index 28fe71a..da8f498 100644 > --- a/fs/ext4/namei.c > +++ b/fs/ext4/namei.c > @@ -39,6 +39,7 @@ > > #include "xattr.h" > #include "acl.h" > +#include "richacl.h" > > #include > /* > @@ -3541,8 +3542,9 @@ const struct inode_operations ext4_dir_inode_operations = { > .getxattr = generic_getxattr, > .listxattr = ext4_listxattr, > .removexattr = generic_removexattr, > - .get_acl = ext4_get_acl, > - .set_acl = ext4_set_acl, > + .get_acl = ext4_get_posix_acl, > + .set_acl = ext4_set_posix_acl, > + .get_richacl = ext4_get_richacl, > .fiemap = ext4_fiemap, > }; > > @@ -3552,6 +3554,7 @@ const struct inode_operations ext4_special_inode_operations = { > .getxattr = generic_getxattr, > .listxattr = ext4_listxattr, > .removexattr = generic_removexattr, > - .get_acl = ext4_get_acl, > - .set_acl = ext4_set_acl, > + .get_acl = ext4_get_posix_acl, > + .set_acl = ext4_set_posix_acl, > + .get_richacl = ext4_get_richacl, > }; > diff --git a/fs/ext4/richacl.c b/fs/ext4/richacl.c > new file mode 100644 > index 0000000..89c10ab > --- /dev/null > +++ b/fs/ext4/richacl.c > @@ -0,0 +1,229 @@ > +/* > + * Copyright IBM Corporation, 2010 > + * Copyright (C) 2015 Red Hat, Inc. > + * Author Aneesh Kumar K.V > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of version 2.1 of the GNU Lesser General Public License > + * as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it would be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. > + * > + */ > + > +#include > +#include > +#include > + > +#include "ext4.h" > +#include "ext4_jbd2.h" > +#include "xattr.h" > +#include "acl.h" > +#include "richacl.h" > + > +struct richacl * > +ext4_get_richacl(struct inode *inode) > +{ > + const int name_index = EXT4_XATTR_INDEX_RICHACL; > + void *value = NULL; > + struct richacl *acl; > + int retval; > + > + if (!IS_RICHACL(inode)) > + return ERR_PTR(-EOPNOTSUPP); > + acl = get_cached_richacl(inode); > + if (acl != ACL_NOT_CACHED) > + return acl; > + retval = ext4_xattr_get(inode, name_index, "", NULL, 0); > + if (retval > 0) { > + value = kmalloc(retval, GFP_KERNEL); > + if (!value) > + return ERR_PTR(-ENOMEM); > + retval = ext4_xattr_get(inode, name_index, "", value, retval); > + } > + if (retval > 0) { > + acl = richacl_from_xattr(value, retval); > + if (acl == ERR_PTR(-EINVAL)) > + acl = ERR_PTR(-EIO); > + } else if (retval == -ENODATA || retval == -ENOSYS) > + acl = NULL; > + else > + acl = ERR_PTR(retval); > + kfree(value); > + > + if (!IS_ERR_OR_NULL(acl)) > + set_cached_richacl(inode, acl); > + > + return acl; > +} > + > +static int > +ext4_set_richacl(handle_t *handle, struct inode *inode, struct richacl *acl) > +{ > + const int name_index = EXT4_XATTR_INDEX_RICHACL; > + size_t size = 0; > + void *value = NULL; > + int retval; > + > + if (acl) { > + mode_t mode = inode->i_mode; > + if (richacl_equiv_mode(acl, &mode) == 0) { > + inode->i_mode = mode; > + ext4_mark_inode_dirty(handle, inode); > + acl = NULL; > + } > + } > + if (acl) { > + size = richacl_xattr_size(acl); > + value = kmalloc(size, GFP_KERNEL); > + if (!value) > + return -ENOMEM; > + richacl_to_xattr(acl, value); > + } > + if (handle) > + retval = ext4_xattr_set_handle(handle, inode, name_index, "", > + value, size, 0); > + else > + retval = ext4_xattr_set(inode, name_index, "", value, size, 0); > + kfree(value); > + if (!retval) > + set_cached_richacl(inode, acl); > + > + return retval; > +} > + > +int > +ext4_init_richacl(handle_t *handle, struct inode *inode, struct inode *dir) > +{ > + struct richacl *dir_acl = NULL; > + > + if (!S_ISLNK(inode->i_mode)) { > + dir_acl = ext4_get_richacl(dir); > + if (IS_ERR(dir_acl)) > + return PTR_ERR(dir_acl); > + } > + if (dir_acl) { > + struct richacl *acl; > + int retval; > + > + acl = richacl_inherit_inode(dir_acl, inode); > + richacl_put(dir_acl); > + > + retval = PTR_ERR(acl); > + if (acl && !IS_ERR(acl)) { > + retval = ext4_set_richacl(handle, inode, acl); > + richacl_put(acl); > + } > + return retval; > + } else { > + inode->i_mode &= ~current_umask(); > + return 0; > + } > +} > + > +int > +ext4_richacl_chmod(struct inode *inode) > +{ > + struct richacl *acl; > + int retval; > + > + if (S_ISLNK(inode->i_mode)) > + return -EOPNOTSUPP; > + acl = ext4_get_richacl(inode); > + if (IS_ERR_OR_NULL(acl)) > + return PTR_ERR(acl); > + acl = richacl_chmod(acl, inode->i_mode); > + if (IS_ERR(acl)) > + return PTR_ERR(acl); > + retval = ext4_set_richacl(NULL, inode, acl); > + richacl_put(acl); > + > + return retval; > +} > + > +static size_t > +ext4_xattr_list_richacl(struct dentry *dentry, char *list, size_t list_len, > + const char *name, size_t name_len, int type) > +{ > + const size_t size = sizeof(RICHACL_XATTR); > + if (!IS_RICHACL(dentry->d_inode)) > + return 0; > + if (list && size <= list_len) > + memcpy(list, RICHACL_XATTR, size); > + return size; > +} > + > +static int > +ext4_xattr_get_richacl(struct dentry *dentry, const char *name, void *buffer, > + size_t buffer_size, int type) > +{ > + struct richacl *acl; > + size_t size; > + > + if (strcmp(name, "") != 0) > + return -EINVAL; > + acl = ext4_get_richacl(dentry->d_inode); > + if (IS_ERR(acl)) > + return PTR_ERR(acl); > + if (acl == NULL) > + return -ENODATA; > + size = richacl_xattr_size(acl); > + if (buffer) { > + if (size > buffer_size) > + return -ERANGE; > + richacl_to_xattr(acl, buffer); > + } > + richacl_put(acl); > + > + return size; > +} > + > +static int > +ext4_xattr_set_richacl(struct dentry *dentry, const char *name, > + const void *value, size_t size, int flags, int type) > +{ > + handle_t *handle; > + struct richacl *acl = NULL; > + int retval, retries = 0; > + struct inode *inode = dentry->d_inode; > + > + if (!IS_RICHACL(dentry->d_inode)) > + return -EOPNOTSUPP; > + if (S_ISLNK(inode->i_mode)) > + return -EOPNOTSUPP; > + if (strcmp(name, "") != 0) > + return -EINVAL; > + if (!uid_eq(current_fsuid(), inode->i_uid) && > + inode_permission(inode, MAY_CHMOD) && > + !capable(CAP_FOWNER)) > + return -EPERM; > + if (value) { > + acl = richacl_from_xattr(value, size); > + if (IS_ERR(acl)) > + return PTR_ERR(acl); > + > + inode->i_mode &= ~S_IRWXUGO; > + inode->i_mode |= richacl_masks_to_mode(acl); > + } > + > +retry: > + handle = ext4_journal_start(inode, EXT4_HT_XATTR, > + EXT4_DATA_TRANS_BLOCKS(inode->i_sb)); > + if (IS_ERR(handle)) > + return PTR_ERR(handle); > + retval = ext4_set_richacl(handle, inode, acl); > + ext4_journal_stop(handle); > + if (retval == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) > + goto retry; > + richacl_put(acl); > + return retval; > +} > + > +const struct xattr_handler ext4_richacl_xattr_handler = { > + .prefix = RICHACL_XATTR, > + .list = ext4_xattr_list_richacl, > + .get = ext4_xattr_get_richacl, > + .set = ext4_xattr_set_richacl, > +}; > diff --git a/fs/ext4/richacl.h b/fs/ext4/richacl.h > new file mode 100644 > index 0000000..09a5cad > --- /dev/null > +++ b/fs/ext4/richacl.h > @@ -0,0 +1,47 @@ > +/* > + * Copyright IBM Corporation, 2010 > + * Copyright (C) 2015 Red Hat, Inc. > + * Author Aneesh Kumar K.V > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of version 2.1 of the GNU Lesser General Public License > + * as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it would be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. > + * > + */ > + > +#ifndef __FS_EXT4_RICHACL_H > +#define __FS_EXT4_RICHACL_H > + > +#include > + > +#ifdef CONFIG_EXT4_FS_RICHACL > + > +#define EXT4_IS_RICHACL(inode) IS_RICHACL(inode) > + > +extern struct richacl *ext4_get_richacl(struct inode *); > +extern int ext4_init_richacl(handle_t *, struct inode *, struct inode *); > +extern int ext4_richacl_chmod(struct inode *); > + > +#else /* CONFIG_FS_EXT4_RICHACL */ > + > +#define EXT4_IS_RICHACL(inode) (0) > +#define ext4_get_richacl NULL > + > +static inline int > +ext4_init_richacl(handle_t *handle, struct inode *inode, struct inode *dir) > +{ > + return 0; > +} > + > +static inline int > +ext4_richacl_chmod(struct inode *inode) > +{ > + return 0; > +} > + > +#endif /* CONFIG_FS_EXT4_RICHACL */ > +#endif /* __FS_EXT4_RICHACL_H */ > diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c > index 1e09fc7..815a306 100644 > --- a/fs/ext4/xattr.c > +++ b/fs/ext4/xattr.c > @@ -100,6 +100,9 @@ static const struct xattr_handler *ext4_xattr_handler_map[] = { > #ifdef CONFIG_EXT4_FS_SECURITY > [EXT4_XATTR_INDEX_SECURITY] = &ext4_xattr_security_handler, > #endif > +#ifdef CONFIG_EXT4_FS_RICHACL > + [EXT4_XATTR_INDEX_RICHACL] = &ext4_richacl_xattr_handler, > +#endif > }; > > const struct xattr_handler *ext4_xattr_handlers[] = { > @@ -112,6 +115,9 @@ const struct xattr_handler *ext4_xattr_handlers[] = { > #ifdef CONFIG_EXT4_FS_SECURITY > &ext4_xattr_security_handler, > #endif > +#ifdef CONFIG_EXT4_FS_RICHACL > + &ext4_richacl_xattr_handler, > +#endif > NULL > }; > > diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h > index 29bedf5..065821e 100644 > --- a/fs/ext4/xattr.h > +++ b/fs/ext4/xattr.h > @@ -97,6 +97,7 @@ struct ext4_xattr_ibody_find { > extern const struct xattr_handler ext4_xattr_user_handler; > extern const struct xattr_handler ext4_xattr_trusted_handler; > extern const struct xattr_handler ext4_xattr_security_handler; > +extern const struct xattr_handler ext4_richacl_xattr_handler; > > extern ssize_t ext4_listxattr(struct dentry *, char *, size_t); > > -- > 2.1.0 > > > From 2743598850b5ac481b91b7fea5f6f00a04e8beae Mon Sep 17 00:00:00 2001 > Message-Id: <2743598850b5ac481b91b7fea5f6f00a04e8beae.1424900921.git.agruenba@redhat.com> > In-Reply-To: > References: > From: "Aneesh Kumar K.V" > Date: Wed, 23 Apr 2014 20:54:54 +0530 > Subject: [RFC 21/21] ext4: Add richacl feature flag > To: linux-kernel@vger.kernel.org, > linux-fsdevel@vger.kernel.org, > linux-nfs@vger.kernel.org > > This feature flag selects richacl instead of posix acl support on the file > system. In addition, the "acl" mount option is needed for enabling either of > the two kinds of acls. > > Signed-off-by: Andreas Gruenbacher > --- > fs/ext4/ext4.h | 6 ++++-- > fs/ext4/super.c | 41 ++++++++++++++++++++++++++++++++--------- > 2 files changed, 36 insertions(+), 11 deletions(-) > > diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h > index f63c3d5..64187cd 100644 > --- a/fs/ext4/ext4.h > +++ b/fs/ext4/ext4.h > @@ -978,7 +978,7 @@ struct ext4_inode_info { > #define EXT4_MOUNT_UPDATE_JOURNAL 0x01000 /* Update the journal format */ > #define EXT4_MOUNT_NO_UID32 0x02000 /* Disable 32-bit UIDs */ > #define EXT4_MOUNT_XATTR_USER 0x04000 /* Extended user attributes */ > -#define EXT4_MOUNT_POSIX_ACL 0x08000 /* POSIX Access Control Lists */ > +#define EXT4_MOUNT_ACL 0x08000 /* Access Control Lists */ > #define EXT4_MOUNT_NO_AUTO_DA_ALLOC 0x10000 /* No auto delalloc mapping */ > #define EXT4_MOUNT_BARRIER 0x20000 /* Use block barriers */ > #define EXT4_MOUNT_QUOTA 0x80000 /* Some quota option set */ > @@ -1552,6 +1552,7 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei) > #define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */ > #define EXT4_FEATURE_INCOMPAT_INLINE_DATA 0x8000 /* data in inode */ > #define EXT4_FEATURE_INCOMPAT_ENCRYPT 0x10000 > +#define EXT4_FEATURE_INCOMPAT_RICHACL 0x20000 > > #define EXT2_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR > #define EXT2_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \ > @@ -1576,7 +1577,8 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei) > EXT4_FEATURE_INCOMPAT_64BIT| \ > EXT4_FEATURE_INCOMPAT_FLEX_BG| \ > EXT4_FEATURE_INCOMPAT_MMP | \ > - EXT4_FEATURE_INCOMPAT_INLINE_DATA) > + EXT4_FEATURE_INCOMPAT_INLINE_DATA | \ > + EXT4_FEATURE_INCOMPAT_RICHACL) > #define EXT4_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \ > EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \ > EXT4_FEATURE_RO_COMPAT_GDT_CSUM| \ > diff --git a/fs/ext4/super.c b/fs/ext4/super.c > index e061e66..4226898 100644 > --- a/fs/ext4/super.c > +++ b/fs/ext4/super.c > @@ -1242,6 +1242,27 @@ static ext4_fsblk_t get_sb_block(void **data) > return sb_block; > } > > +static int enable_acl(struct super_block *sb) > +{ > + sb->s_flags &= ~(MS_POSIXACL | MS_RICHACL); > + if (test_opt(sb, ACL)) { > + if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RICHACL)) { > +#ifdef CONFIG_EXT4_FS_RICHACL > + sb->s_flags |= MS_RICHACL; > +#else > + return -EOPNOTSUPP; > +#endif > + } else { > +#ifdef CONFIG_EXT4_FS_POSIX_ACL > + sb->s_flags |= MS_POSIXACL; > +#else > + return -EOPNOTSUPP; > +#endif > + } > + } > + return 0; > +} > + > #define DEFAULT_JOURNAL_IOPRIO (IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 3)) > static char deprecated_msg[] = "Mount option \"%s\" will be removed by %s\n" > "Contact linux-ext4@vger.kernel.org if you think we should keep it.\n"; > @@ -1388,9 +1409,9 @@ static const struct mount_opts { > MOPT_NO_EXT2 | MOPT_DATAJ}, > {Opt_user_xattr, EXT4_MOUNT_XATTR_USER, MOPT_SET}, > {Opt_nouser_xattr, EXT4_MOUNT_XATTR_USER, MOPT_CLEAR}, > -#ifdef CONFIG_EXT4_FS_POSIX_ACL > - {Opt_acl, EXT4_MOUNT_POSIX_ACL, MOPT_SET}, > - {Opt_noacl, EXT4_MOUNT_POSIX_ACL, MOPT_CLEAR}, > +#if defined(CONFIG_EXT4_FS_POSIX_ACL) || defined(CONFIG_EXT4_FS_RICHACL) > + {Opt_acl, EXT4_MOUNT_ACL, MOPT_SET}, > + {Opt_noacl, EXT4_MOUNT_ACL, MOPT_CLEAR}, > #else > {Opt_acl, 0, MOPT_NOSUPPORT}, > {Opt_noacl, 0, MOPT_NOSUPPORT}, > @@ -3538,8 +3559,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) > set_opt(sb, NO_UID32); > /* xattr user namespace & acls are now defaulted on */ > set_opt(sb, XATTR_USER); > -#ifdef CONFIG_EXT4_FS_POSIX_ACL > - set_opt(sb, POSIX_ACL); > +#if defined(CONFIG_EXT4_FS_POSIX_ACL) || defined(CONFIG_EXT4_FS_RICHACL) > + set_opt(sb, ACL); > #endif > /* don't forget to enable journal_csum when metadata_csum is enabled. */ > if (ext4_has_metadata_csum(sb)) > @@ -3620,8 +3641,9 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) > clear_opt(sb, DELALLOC); > } > > - sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | > - (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0); > + err = enable_acl(sb); > + if (err) > + goto failed_mount; > > if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV && > (EXT4_HAS_COMPAT_FEATURE(sb, ~0U) || > @@ -4913,8 +4935,9 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) > if (sbi->s_mount_flags & EXT4_MF_FS_ABORTED) > ext4_abort(sb, "Abort forced by user"); > > - sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | > - (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0); > + err = enable_acl(sb); > + if (err) > + goto restore_opts; > > es = sbi->s_es; > > -- > 2.1.0 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/ -- Michael Kerrisk Linux man-pages maintainer; http://www.kernel.org/doc/man-pages/ Author of "The Linux Programming Interface", http://blog.man7.org/ -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/