Richacls
Subsystems (9)
v3 cover Richacls
Hello,
here's another update of the richacl patch queue. The changes since the last posting (https://lwn.net/Articles/638242/) include:
* The nfs client now allocates pages for received acls on demand like the server does. It no longer caches the acl size between calls.
* All possible acls consisting of only owner@, group@, and everyone@ entries which are equivalent to the file mode permission bits are now recognized. This is needed because by the NFSv4 specification, the nfs server must translate the file mode permission bits into an acl if it supports acls at all.
* Support for the dacl attribute over NFSv4.1 for Automatic Inheritance, and also for the write_retention and write_retention_hold permissions.
* The richacl_compute_max_masks() documentation has been improved.
* Various minor bug fixes.
The git version is available here:
git://git.kernel.org/pub/scm/linux/kernel/git/agruen/linux-richacl.git \ richacl-2015-04-24
The richacl command-line has been split into getrichacl and setrichacl, in line with getfacl and setfacl. Watch out for that when updating the user-space.
Things still to be done, or which I'm not entirely happy with:
* The nfs server performs some access checking on its own before calling into the vfs which is rersponsible for the actual access checking (see where it calls inode_permission()). With the additional MAY_ flags introduced in this patch queue, it gets it wrong in some cases; I have yet to figure out how to deal with this.
* We may still need to add back support for the "system.nfs4_acl" attribute on nfs mounts for backwards compatibility; it's not clear to me if anyone is actually using that.
* 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.
* The base_acl code is still rather ugly.
Thanks,
Andreas
Andreas Gruenbacher (43):
vfs: Minor documentation fix
uapi: Remove kernel internal declaration
nfsd: Checking for acl support does not require fetching any acls
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
vfs: Cache base_acl objects in inodes
vfs: Cache richacl in struct inode
richacl: Create-time inheritance
richacl: Check if an acl is equivalent to a file mode
richacl: Also recognize nontrivial mode-equivalent acls
richacl: Automatic Inheritance
richacl: xattr mapping functions
vfs: Add richacl permission checking
richacl: acl editing helper functions
richacl: Move everyone@ aces down the acl
richacl: Propagate everyone@ permissions to other aces
richacl: Isolate the owner and group classes
richacl: Apply the file masks to a richacl
richacl: Create richacl from mode values
richacl: Create acl with masks applied in richacl_from_mode()
nfsd: Remove dead declarations
nfsd: Keep list of acls to dispose of in compoundargs
nfsd: Use richacls as internal acl representation
nfsd: Add richacl support
NFSv4: Fix GETATTR bitmap verification
nfs/sunrpc: No more encode and decode function pointer casting nfs/sunrpc: Return status code from encode functions
nfs3: Return posix acl encode errors
nfs: Remove unused xdr page offsets in getacl/setacl arguments
rpc: Allow to demand-allocate pages to encode into
nfs: Add richacl support
uapi/nfs: Add NFSv4.1 ACL definitions
nfsd: Add support for the v4.1 dacl attribute
nfs: Add support for the v4.1 dacl attribute
Aneesh Kumar K.V (2):
ext4: Add richacl support
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 | 9 + fs/Makefile | 3 + 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 | 211 +++++ 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/lockd/clnt4xdr.c | 58 +- fs/lockd/clntxdr.c | 58 +- fs/lockd/mon.c | 26 +- fs/namei.c | 108 ++- fs/nfs/inode.c | 3 - fs/nfs/mount_clnt.c | 24 +- fs/nfs/nfs2xdr.c | 115 ++- fs/nfs/nfs3xdr.c | 225 +++-- fs/nfs/nfs4proc.c | 357 ++++---- fs/nfs/nfs4xdr.c | 1001 +++++++++++++++-------- fs/nfs/super.c | 4 +- fs/nfs_common/Makefile | 1 + fs/nfs_common/nfs4acl.c | 41 + fs/nfsd/Kconfig | 1 + fs/nfsd/acl.h | 23 +- fs/nfsd/nfs4acl.c | 499 +++++------ fs/nfsd/nfs4callback.c | 29 +- fs/nfsd/nfs4proc.c | 17 +- fs/nfsd/nfs4xdr.c | 280 ++++--- fs/nfsd/nfsd.h | 6 +- fs/nfsd/xdr4.h | 12 +- fs/posix_acl.c | 31 +- fs/richacl_base.c | 587 +++++++++++++ fs/richacl_compat.c | 841 +++++++++++++++++++ fs/richacl_inode.c | 268 ++++++ fs/richacl_xattr.c | 210 +++++ fs/xattr.c | 34 +- include/linux/fs.h | 47 +- include/linux/nfs4.h | 17 +- include/linux/nfs4acl.h | 7 + include/linux/nfs_fs.h | 1 - include/linux/nfs_fs_sb.h | 2 + include/linux/nfs_xdr.h | 12 +- include/linux/posix_acl.h | 12 +- include/linux/richacl.h | 332 ++++++++ include/linux/richacl_compat.h | 40 + include/linux/richacl_xattr.h | 52 ++ include/linux/sunrpc/xdr.h | 5 +- include/uapi/linux/fs.h | 3 +- include/uapi/linux/nfs4.h | 17 +- include/uapi/linux/xattr.h | 2 + net/sunrpc/auth.c | 7 +- net/sunrpc/auth_gss/gss_rpc_upcall.c | 4 +- net/sunrpc/auth_gss/gss_rpc_xdr.c | 11 +- net/sunrpc/auth_gss/gss_rpc_xdr.h | 8 +- net/sunrpc/clnt.c | 5 +- net/sunrpc/rpcb_clnt.c | 57 +- net/sunrpc/xdr.c | 8 + 70 files changed, 4720 insertions(+), 1294 deletions(-) create mode 100644 fs/ext4/richacl.c create mode 100644 fs/ext4/richacl.h create mode 100644 fs/nfs_common/nfs4acl.c create mode 100644 fs/richacl_base.c create mode 100644 fs/richacl_compat.c create mode 100644 fs/richacl_inode.c create mode 100644 fs/richacl_xattr.c create mode 100644 include/linux/nfs4acl.h create mode 100644 include/linux/richacl.h create mode 100644 include/linux/richacl_compat.h create mode 100644 include/linux/richacl_xattr.h
Signature · 1 line
2.1.0
RFC v3 1/45 vfs: Minor documentation fix
The check_acl inode operation and the IPERM_FLAG_RCU are long gone. Document what get_acl does instead.
--- Documentation/filesystems/porting | 8 ++++---- Documentation/filesystems/vfs.txt | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-)
Documentation/filesystems/porting +4 -4
@@ -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]
Documentation/filesystems/vfs.txt +3 -0
@@ -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.
Signature · 1 line
2.1.0
RFC v3 2/45 uapi: Remove kernel internal declaration
In reply to Andreas GruenbacherThe enum nfs4_acl_whotype is only used in nfs4d's internal nfs4 acl representation. No longer expose it to user space.
--- include/linux/nfs4.h | 7 +++++++ include/uapi/linux/nfs4.h | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-)
include/linux/nfs4.h +7 -0
@@ -16,6 +16,13 @@#include <linux/uidgid.h> #include <uapi/linux/nfs4.h> +enum nfs4_acl_whotype { +NFS4_ACL_WHO_NAMED =0 , +NFS4_ACL_WHO_OWNER , +NFS4_ACL_WHO_GROUP , +NFS4_ACL_WHO_EVERYONE , +}; +struct nfs4_ace {uint32_t type;uint32_t flag;
include/uapi/linux/nfs4.h +0 -7
@@ -162,13 +162,6 @@*/ #define NFS4_MAX_BACK_CHANNEL_OPS 2 -enum nfs4_acl_whotype { -NFS4_ACL_WHO_NAMED =0 , -NFS4_ACL_WHO_OWNER , -NFS4_ACL_WHO_GROUP , -NFS4_ACL_WHO_EVERYONE , -}; -#endif /* _UAPI_LINUX_NFS4_H */ /*
Signature · 1 line
2.1.0
RFC v3 3/45 nfsd: Checking for acl support does not require fetching any acls
In reply to Andreas GruenbacherWhether or not a file system supports acls can be determined with IS_POSIXACL(inode) and does not require trying to fetch any acls; the code for computing the supported_attrs and aclsupport attributes can be simplified.
--- fs/nfsd/nfs4xdr.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-)
fs/nfsd/nfs4xdr.c +10 -15
@@ -2227,7 +2227,6 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,u32 rdattr_err =0 ;__be32 status;int err; -int aclsupport =0 ;struct nfs4_acl *acl =NULL ;void *context =NULL ;int contextlen;
@@ -2274,19 +2273,15 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp, goto out; fhp = tempfh; } -if (bmval0 & (FATTR4_WORD0_ACL |FATTR4_WORD0_ACLSUPPORT - |FATTR4_WORD0_SUPPORTED_ATTRS )) { +if (bmval0 &FATTR4_WORD0_ACL ) { err =nfsd4_get_nfs4_acl (rqstp, dentry, &acl); - aclsupport = (err ==0 ); -if (bmval0 &FATTR4_WORD0_ACL ) { -if (err == -EOPNOTSUPP ) - bmval0 &= ~FATTR4_WORD0_ACL ; -else if (err == -EINVAL ) { - status = nfserr_attrnotsupp; - goto out; - }else if (err !=0 ) - goto out_nfserr; - } +if (err == -EOPNOTSUPP ) + bmval0 &= ~FATTR4_WORD0_ACL ; +else if (err == -EINVAL ) { + status = nfserr_attrnotsupp; + goto out; + }else if (err !=0 ) + goto out_nfserr; }#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
@@ -2338,7 +2333,7 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,u32 word1 =nfsd_suppattrs1 (minorversion);u32 word2 =nfsd_suppattrs2 (minorversion); -if (!aclsupport) +if (!IS_POSIXACL (dentry->d_inode)) word0 &= ~FATTR4_WORD0_ACL ;if (!contextsupport) word2 &= ~FATTR4_WORD2_SECURITY_LABEL ;
@@ -2486,7 +2481,7 @@ out_acl: p =xdr_reserve_space (xdr,4 );if (!p) goto out_resource; - *p++ =cpu_to_be32 (aclsupport ? + *p++ =cpu_to_be32 (IS_POSIXACL (dentry->d_inode) ?ACL4_SUPPORT_ALLOW_ACL |ACL4_SUPPORT_DENY_ACL :0 ); }if (bmval0 &FATTR4_WORD0_CANSETTIME ) {
Signature · 1 line
2.1.0
RFC v3 4/45 vfs: Shrink struct posix_acl
In reply to Andreas GruenbacherThere 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.
--- fs/posix_acl.c | 5 +++-- include/linux/posix_acl.h | 7 ++----- 2 files changed, 5 insertions(+), 7 deletions(-)
fs/posix_acl.c +3 -2
@@ -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);
include/linux/posix_acl.h +2 -5
@@ -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 ); }
Signature · 1 line
2.1.0
Re: [RFC v3 04/45] vfs: Shrink struct posix_acl
Quoted 46 lines · Andreas Gruenbacher
There is a hole in struct
posix_aclbecause its structrcu_headmember is too large; at least on on 64-bit architectures, the hole cannot be closed by changing the definition of structposix_acl. So instead, remove the structrcu_headmember from structposix_acl, make sure that acls are always big enough to fit a structrcu_head, and cast to structrcu_head* when disposing of an acl.Signed-off-by Andreas Gruenbacher <[email protected]>--- fs/posix_acl.c | 5 +++-- include/linux/posix_acl.h | 7 ++----- 2 files changed, 5 insertions(+), 7 deletions(-)fs/posix_acl.c +3
-2@@ -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);include/linux/posix_acl.h +1
-4@@ -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 ]; };
include/linux/posix_acl.h static inline void · line 74
Quoted 6 lines
include/linux/posix_acl.h +1
-1@@ -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 ); }
This doesn't seem right. Wouldn't that scribble over the a_count and part of the first struct posix_acl_entry while there might still be rcu users? That might blow up in interesting ways...
Rasmus
Re: [RFC v3 04/45] vfs: Shrink struct posix_acl
2015-04-25 9:56 GMT+02:00 Rasmus Villemoes <[email protected]>:
Quoted 3 lines
This doesn't seem right. Wouldn't that scribble over the
a_countand part of the first structposix_acl_entrywhile there might still be rcu users?
You are right, this patch is broken. I'll remove it.
Thanks,
Andreas
RFC v3 5/45 vfs: Add IS_ACL() and IS_RICHACL() tests
In reply to Andreas GruenbacherThe 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.
--- 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(-)
fs/Kconfig +3 -0
@@ -58,6 +58,9 @@ endif # BLOCK config FS_POSIX_ACL def_bool n +config FS_RICHACL + def_bool n + config EXPORTFS tristate
fs/namei.c +4 -4
@@ -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)
include/linux/fs.h +12 -0
@@ -1709,6 +1709,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)
@@ -1722,6 +1728,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,
include/uapi/linux/fs.h +2 -1
@@ -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)
Signature · 1 line
2.1.0
RFC v3 6/45 vfs: Add MAY_CREATE_FILE and MAY_CREATE_DIR permission flags
In reply to Andreas GruenbacherRichacls 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().
--- fs/namei.c | 42 ++++++++++++++++++++++++------------------ include/linux/fs.h | 2 ++ 2 files changed, 26 insertions(+), 18 deletions(-)
fs/namei.c +24 -18
@@ -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, dint 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_deif (!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 *, newnaint 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;
include/linux/fs.h +2 -0
@@ -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
Signature · 1 line
2.1.0
RFC v3 7/45 vfs: Add MAY_DELETE_SELF and MAY_DELETE_CHILD permission flags
In reply to Andreas GruenbacherNormally, 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.
--- fs/namei.c | 15 +++++++++++---- include/linux/fs.h | 2 ++ 2 files changed, 13 insertions(+), 4 deletions(-)
fs/namei.c +11 -4
@@ -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))
include/linux/fs.h +2 -0
@@ -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
Signature · 1 line
2.1.0
RFC v3 8/45 vfs: Make the inode passed to inode_change_ok non-const
In reply to Andreas GruenbacherWe 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.
--- fs/attr.c | 2 +- include/linux/fs.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
fs/attr.c +1 -1
@@ -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;
include/linux/fs.h +1 -1
@@ -2761,7 +2761,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);
Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC v3 9/45 vfs: Add permission flags for setting file attributes
In reply to Andreas GruenbacherRichacls 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().
--- fs/attr.c | 79 +++++++++++++++++++++++++++++++++++++++++++++--------- include/linux/fs.h | 3 +++ 2 files changed, 70 insertions(+), 12 deletions(-)
fs/attr.c +67 -12
@@ -17,6 +17,65 @@#include <linux/ima.h> /** +* 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 ; }
include/linux/fs.h +3 -0
@@ -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
Signature · 1 line
2.1.0
RFC v3 10/45 richacl: In-memory representation and helper functions
In reply to Andreas GruenbacherA 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.
--- fs/Makefile | 2 + fs/richacl_base.c | 58 ++++++++++++ include/linux/richacl.h | 247 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 307 insertions(+) create mode 100644 fs/richacl_base.c create mode 100644 include/linux/richacl.h
fs/Makefile +2 -0
@@ -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/
fs/richacl_base.c new file +58 -0
58 lines not on this page · raw message
include/linux/richacl.h new file +247 -0
247 lines not on this page · raw message
Signature · 1 line
2.1.0
RFC v3 11/45 richacl: Permission mapping functions
In reply to Andreas GruenbacherWe 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.
--- fs/richacl_base.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/richacl.h | 46 +++++++++++++++++++ 2 files changed, 162 insertions(+)
fs/richacl_base.c +116 -0
@@ -56,3 +56,119 @@ richacl_clone(const struct richacl *acl, gfp_t gfp) }return dup; } + +/** +* richacl_mask_to_mode - compute the file permission bits which correspond to @mask +* @mask: %RICHACE_* permission mask +* +* See richacl_masks_to_mode(). +*/ +static int +richacl_mask_to_mode (unsigned int mask) +{ +int mode =0 ; + +if (mask &RICHACE_POSIX_MODE_READ ) + mode |=S_IROTH ; +if (mask &RICHACE_POSIX_MODE_WRITE ) + mode |=S_IWOTH ; +if (mask &RICHACE_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 +* RICHACE_APPEND_DATA even if it does not also contain RICHACE_WRITE_DATA. +* +* Permissions which are not in RICHACE_POSIX_MODE_READ, RICHACE_POSIX_MODE_WRITE, or +* RICHACE_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(); they must be explicitly enabled in the 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 =RICHACE_POSIX_ALWAYS_ALLOWED ; + +if (mode &S_IROTH ) + mask |=RICHACE_POSIX_MODE_READ ; +if (mode &S_IWOTH ) + mask |=RICHACE_POSIX_MODE_WRITE ; +if (mode &S_IXOTH ) + mask |=RICHACE_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 RICHACE_ADD_FILE, RICHACE_ADD_SUBDIRECTORY, and RICHACE_DELETE_CHILD +* here. +*/ +unsigned int +richacl_want_to_mask (unsigned int want) +{ +unsigned int mask =0 ; + +if (want &MAY_READ ) + mask |=RICHACE_READ_DATA ; +if (want &MAY_DELETE_SELF ) + mask |=RICHACE_DELETE ; +if (want &MAY_TAKE_OWNERSHIP ) + mask |=RICHACE_WRITE_OWNER ; +if (want &MAY_CHMOD ) + mask |=RICHACE_WRITE_ACL ; +if (want &MAY_SET_TIMES ) + mask |=RICHACE_WRITE_ATTRIBUTES ; +if (want &MAY_EXEC ) + mask |=RICHACE_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 |=RICHACE_APPEND_DATA ; +if (want &MAY_CREATE_FILE ) + mask |=RICHACE_ADD_FILE ; +if (want &MAY_CREATE_DIR ) + mask |=RICHACE_ADD_SUBDIRECTORY ; +if (want &MAY_DELETE_CHILD ) + mask |=RICHACE_DELETE_CHILD ; + }else if (want &MAY_WRITE ) + mask |=RICHACE_WRITE_DATA ; +return mask; +} +EXPORT_SYMBOL_GPL (richacl_want_to_mask);
include/linux/richacl.h +46 -0
57 lines not on this page · raw message
Signature · 1 line
2.1.0
RFC v3 12/45 richacl: Compute maximum file masks from an acl
In reply to Andreas GruenbacherCompute 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 provide file masks (such as setting an acl via nfsd). When user-space sets an acl via setxattr, the extended attribute already includes the file masks.
Setting an acl also sets the file mode permission bits: they are determined by the file masks; see richacl_masks_to_mode().
--- fs/richacl_base.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/richacl.h | 1 + 2 files changed, 155 insertions(+)
fs/richacl_base.c +154 -0
@@ -172,3 +172,157 @@ richacl_want_to_mask(unsigned int want)return mask; }EXPORT_SYMBOL_GPL (richacl_want_to_mask); + +/* +* Note: functions like richacl_allowed_to_who(), richacl_group_class_allowed(), +* and richacl_compute_max_masks() iterate through the entire acl in reverse +* order as an optimization. +* +* In the standard algorithm, aces are considered in forward order. When a +* process matches an ace, the permissions in the ace are either allowed or +* denied depending on the ace type. Once a permission has been allowed or +* denied, it is no longer considered in further aces. +* +* By iterating through the acl in reverse order, we can compute the same +* result without having to keep track of which permissions have been allowed +* and denied already. +*/ + +/** +* richacl_allowed_to_who - permissions allowed to a specific who value +* +* Compute the maximum mask values allowed to a specific who value, taking +* everyone@ aces 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 +* +* Compute the maximum mask values allowed to a process in the group class +* (i.e., a process which is not the owner but is in the owning group or +* matches a user or group acl entry). This includes permissions granted or +* denied by everyone@ aces. +* +* 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 the acl doesn't contain any group@ aces, richacl_allowed_to_who() +* wasn't called for the owning group. We could make that call now, but +* we already know the result (everyone_allowed). +*/ +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, and the group class cannot +* have fewer permissions than the other class. +*/ + +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 &= ~RICHACL_MASKED ; +} +EXPORT_SYMBOL_GPL (richacl_compute_max_masks);
include/linux/richacl.h +1 -0
@@ -289,5 +289,6 @@ extern struct richacl *richacl_clone(const struct richacl *, gfp_t);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 */
Signature · 1 line
2.1.0
Re: [RFC v3 12/45] richacl: Compute maximum file masks from an acl
Quoted 11 lines · Andreas Gruenbacher
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 provide file masks (such as setting an acl via nfsd). When user-space sets an acl via setxattr, the extended attribute already includes the file masks.
Setting an acl also sets the file mode permission bits: they are determined by the file masks; see
richacl_masks_to_mode().
Thanks, the verbose comments are welcomed here, just one typo:
fs/richacl_base.c richacl_want_to_mask(unsigned int want) · line 278
Quoted 6 lines
fs/richacl_base.c +6
-0+/* +* @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:
s/or/of/ ?
--b.
RFC v3 13/45 richacl: Update the file masks in chmod()
In reply to Andreas GruenbacherDoing 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.
--- fs/richacl_base.c | 40 ++++++++++++++++++++++++++++++++++++++++ include/linux/richacl.h | 1 + 2 files changed, 41 insertions(+)
fs/richacl_base.c +40 -0
@@ -326,3 +326,43 @@ restart: acl->a_flags &= ~RICHACL_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 ) | +RICHACE_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 &RICHACL_MASKED )) +return acl; + + clone =richacl_clone (acl,GFP_KERNEL ); +richacl_put (acl); +if (!clone) +return ERR_PTR (-ENOMEM ); + + clone->a_flags |=RICHACL_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);
include/linux/richacl.h +1 -0
@@ -290,5 +290,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 */
Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC v3 14/45 richacl: Permission check algorithm
In reply to Andreas GruenbacherA 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.
--- fs/Makefile | 2 +- fs/richacl_inode.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/richacl.h | 3 ++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 fs/richacl_inode.c
fs/Makefile +1 -1
@@ -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/
fs/richacl_inode.c new file +130 -0
130 lines not on this page · raw message
include/linux/richacl.h +3 -0
@@ -292,4 +292,7 @@ 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 ); +/* richacl_inode.c */ +extern int richacl_permission (struct inode *,const struct richacl *,int ); +#endif /* __RICHACL_H */
Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 14/45] richacl: Permission check algorithm
Quoted 4 lines · Andreas Gruenbacher
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.
Is that right? Based on that I'd have thought that an acl like
owner :r::mask group :-::mask other :-::mask
bfields:r::allow
would permit read to bfields in the case bfields is the file owner, because both the mask entries and the NFSv4 ACL would permit access.
But I think it doesn't (because the "bfields" entry is subject to the group mask).
But if I'm right, I'm not sure how to describe the algorithm concisely.
--b.
Quoted 177 lines
Signed-off-by Andreas Gruenbacher <[email protected]>--- fs/Makefile | 2 +- fs/richacl_inode.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/richacl.h | 3 ++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 fs/richacl_inode.cfs/Makefile +1
-1@@ -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/fs/richacl_inode.c new file +130
-0@@ -0,0 +1,130 @@ +/* +* Copyright (C) 2010 Novell, Inc. +* Copyright (C) 2015 Red Hat, Inc. +* Written by Andreas Gruenbacher <[email protected]> +* +* 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 <linux/sched.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/richacl.h> + +/** +* 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 &RICHACL_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 &RICHACE_IDENTIFIER_GROUP ) { +if (!in_group_p (ace->e_id.gid)) +continue ; + }else { +if (!uid_eq (current_fsuid (), ace->e_id.uid)) +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 &RICHACL_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 &RICHACL_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);include/linux/richacl.h +3
-0@@ -292,4 +292,7 @@ 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 ); +/* richacl_inode.c */ +extern int richacl_permission (struct inode *,const struct richacl *,int ); +#endif /* __RICHACL_H */ Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 14/45] richacl: Permission check algorithm
2015-05-22 23:08 GMT+02:00 J. Bruce Fields <[email protected]>:
Quoted 11 lines · Andreas Gruenbacher
Quoted 4 lines
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.
Is that right? Based on that I'd have thought that an acl like
owner :r::mask group :-::mask other :-::mask
Quoted 7 lines
bfields:r::allow
would permit read to bfields in the case bfields is the file owner, because both the mask entries and the NFSv4 ACL would permit access.
But I think it doesn't (because the "bfields" entry is subject to the group mask).
The problem at cause here that if we treat the acl and masks completely separately, we can end up with acl/mask combinations that don't have a representation as "normal" acls. For example, this:
owner:r::mask
group:-::mask
other:-::mask
group@:r::allow
would grant the owner read access when in the owning group, but wouldn't grant the owning group access. This is handled by applying the group mask to all group-class entries; see this comment in the code:
/*
* 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. */
There is no necessity to also apply this rule to user entries matching the current owner though; we can change that here and in richacl_apply_masks().
Quoted 1 line
But if I'm right, I'm not sure how to describe the algorithm concisely.
Right, it's all a bit messy.
Thanks,
Andreas
Re: [RFC v3 14/45] richacl: Permission check algorithm
Quoted 27 lines · Andreas Grünbacher
2015-05-22 23:08 GMT+02:00 J. Bruce Fields <[email protected]>:
Quoted 11 lines · Andreas Gruenbacher
Quoted 4 lines
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.
Is that right? Based on that I'd have thought that an acl like
owner :r::mask group :-::mask other :-::maskQuoted 7 lines
bfields:r::allow
would permit read to bfields in the case bfields is the file owner, because both the mask entries and the NFSv4 ACL would permit access.
But I think it doesn't (because the "bfields" entry is subject to the group mask).
The problem at cause here that if we treat the acl and masks completely separately, we can end up with acl/mask combinations that don't have a representation as "normal" acls. For example, this:
owner:r::mask group:-::mask other:-::mask
Quoted 6 lines
group@:r::allow
would grant the owner read access when in the owning group, but wouldn't grant the owning group access. This is handled by applying the group mask to all group-class entries; see this comment in the code:
OK, good, that makes sense, so I think all that's needed is some updates to documentation; for example:
Quoted 3 lines
/*
* Apply the group file mask to entries other than OWNER@ and
* EVERYONE@. This is not required for correct access checking
This is a strange way to put it; it makes it sound like the logic here might not affect the access check result at all.
And this routine is basically the definition of the access check algorithm, so the logic here is required.
Quoted 2 lines
* but ensures that we grant the same permissions as the acl
* computed byrichacl_apply_masks()would grant.
I mean, I do understand what you're saying here, but it's a slightly backwards way to put it....
Quoted 8 lines
*/There is no necessity to also apply this rule to user entries matching the current owner though; we can change that here and in
richacl_apply_masks().Quoted 1 line
But if I'm right, I'm not sure how to describe the algorithm concisely.
Right, it's all a bit messy.
We need to put some thought into how this will be explained in man pages and such, if you don't have that already. It's inherently a complicated ACL model, and hopefully this sort of corner case can be ignored most of the time, but we need a good complete explanation somewhere that a stressed sysadmin can follow if they need it....
I wish we could do something a little dumber and simpler, e.g., like
http://docs.openafs.org/UserGuide/HDRWQ59.html
but maybe it's not practical.
--b.
RFC v3 15/45 vfs: Cache base_acl objects in inodes
In reply to Andreas GruenbacherPOSIX 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).
--- 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(-)
drivers/staging/lustre/lustre/llite/llite_lib.c +1 -1
@@ -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 ;
fs/f2fs/acl.c +2 -2
@@ -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) {
fs/inode.c +2 -2
@@ -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); }
fs/posix_acl.c +9 -9
@@ -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) {
include/linux/fs.h +19 -3
@@ -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;
@@ -2937,4 +2940,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 */
include/linux/posix_acl.h +4 -5
@@ -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); }
include/linux/richacl.h +1 -1
@@ -168,7 +168,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; }
Signature · 1 line
2.1.0
RFC v3 16/45 vfs: Cache richacl in struct inode
In reply to Andreas GruenbacherCache richacls in struct inode so that this doesn't have to be done individually in each filesystem. This is similar to POSIX ACLs.
--- fs/inode.c | 11 ++++++-- fs/posix_acl.c | 2 +- fs/richacl_base.c | 8 ++++-- fs/richacl_inode.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 6 +++- include/linux/richacl.h | 15 ++++++---- 6 files changed, 102 insertions(+), 13 deletions(-)
fs/inode.c +8 -3
@@ -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); }
fs/posix_acl.c +1 -1
@@ -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 )
fs/richacl_base.c +5 -3
@@ -29,11 +29,13 @@ MODULE_LICENSE("GPL");struct richacl *richacl_alloc (int count,gfp_t gfp) { -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);if (acl) { -atomic_set (&acl->a_refcount,1 ); +atomic_set (&acl->a_base.ba_refcount,1 ); acl->a_count = count; }return acl;
@@ -52,7 +54,7 @@ richacl_clone(const struct richacl *acl, gfp_t gfp)if (dup) {memcpy (dup, acl, size); -atomic_set (&dup->a_refcount,1 ); +atomic_set (&dup->a_base.ba_refcount,1 ); }return dup; }
fs/richacl_inode.c +73 -0
79 lines not on this page · raw message
include/linux/fs.h +5 -1
@@ -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;
@@ -1587,6 +1590,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 *);
include/linux/richacl.h +10 -5
@@ -33,7 +33,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;
@@ -167,8 +167,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; }
@@ -178,10 +177,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 *); +/** * richace_is_owner - check if @ace is an OWNER@ entry */
Signature · 1 line
2.1.0
RFC v3 17/45 richacl: Create-time inheritance
In reply to Andreas GruenbacherWhen 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.
--- fs/richacl_base.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/richacl_inode.c | 60 ++++++++++++++++++++++++++++++++++++++++++ include/linux/richacl.h | 2 ++ 3 files changed, 131 insertions(+)
fs/richacl_base.c +69 -0
@@ -368,3 +368,72 @@ richacl_chmod(struct richacl *acl, mode_t mode)return clone; }EXPORT_SYMBOL_GPL (richacl_chmod); + +/** +* 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,GFP_KERNEL ); +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 &RICHACE_NO_PROPAGATE_INHERIT_ACE ) +richace_clear_inheritance_flags (ace); +if ((dir_ace->e_flags &RICHACE_FILE_INHERIT_ACE ) && + !(dir_ace->e_flags &RICHACE_DIRECTORY_INHERIT_ACE )) + ace->e_flags |=RICHACE_INHERIT_ONLY_ACE ; + ace++; + } + }else { +richacl_for_each_entry (dir_ace, dir_acl) { +if (!(dir_ace->e_flags &RICHACE_FILE_INHERIT_ACE )) +continue ; + count++; + } +if (!count) +return NULL ; + acl =richacl_alloc (count,GFP_KERNEL ); +if (!acl) +return ERR_PTR (-ENOMEM ); + ace = acl->a_entries; +richacl_for_each_entry (dir_ace, dir_acl) { +if (!(dir_ace->e_flags &RICHACE_FILE_INHERIT_ACE )) +continue ; +memcpy (ace, dir_ace,sizeof (struct richace )); +richace_clear_inheritance_flags (ace); +/* +* RICHACE_DELETE_CHILD is meaningless for +* non-directories, so clear it. +*/ + ace->e_mask &= ~RICHACE_DELETE_CHILD ; + ace++; + } + } + +return acl; +}
fs/richacl_inode.c +60 -0
63 lines not on this page · raw message
include/linux/richacl.h +2 -0
@@ -296,8 +296,10 @@ 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 struct richacl *richacl_inherit (const struct richacl *,int );/* richacl_inode.c */ extern int richacl_permission (struct inode *,const struct richacl *,int ); +extern struct richacl *richacl_create (struct inode *,struct inode *);#endif /* __RICHACL_H */
Signature · 1 line
2.1.0
RFC v3 18/45 richacl: Check if an acl is equivalent to a file mode
In reply to Andreas GruenbacherThis function is used to avoid storing richacls if the acl can be computed from the file permission bits.
--- fs/richacl_base.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/richacl.h | 1 + 2 files changed, 49 insertions(+)
fs/richacl_base.c +48 -0
@@ -437,3 +437,51 @@ 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 mask; + +if (acl->a_count !=1 || + acl->a_flags !=RICHACL_MASKED || + !richace_is_everyone (ace) || + !richace_is_allow (ace) || + ace->e_flags & ~RICHACE_SPECIAL_WHO ) +return -1 ; + +/* Mask flags we can ignore */ + x = ~RICHACE_POSIX_ALWAYS_ALLOWED ; +if (!S_ISDIR (*mode_p)) + x &= ~RICHACE_DELETE_CHILD ; + + mask =richacl_masks_to_mode (acl); +if (((acl->a_group_mask ^richacl_mode_to_mask (mask >>3 )) & x) || + ((acl->a_other_mask ^richacl_mode_to_mask (mask)) & x)) +return -1 ; + + x &= ~RICHACE_POSIX_OWNER_ALLOWED ; +if ((acl->a_owner_mask ^richacl_mode_to_mask (mask >>6 )) & x) +return -1 ; + +if ((ace->e_mask ^RICHACE_POSIX_MODE_ALL ) & x) +return -1 ; + + *mode_p = (*mode_p & ~S_IRWXUGO ) | mask; +return 0 ; +} +EXPORT_SYMBOL_GPL (richacl_equiv_mode);
include/linux/richacl.h +1 -0
@@ -297,6 +297,7 @@ 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 struct richacl *richacl_inherit (const struct richacl *,int ); +extern int richacl_equiv_mode (const struct richacl *,mode_t *);/* richacl_inode.c */ extern int richacl_permission (struct inode *,const struct richacl *,int );
Signature · 1 line
2.1.0
RFC v3 19/45 richacl: Also recognize nontrivial mode-equivalent acls
In reply to Andreas GruenbacherSo far, richacl_equiv_mode() is relatively limited in the types of acl it considers equivalent to a file mode: it only accepts masked acls with a single everyone@:rwpxd::allow entry.
Change this to consider all acls equivalent to file modes if they only consist of owner@, group@, and everyone@ entries and the owner@ permissions do not depend on whether the owner is a member in the owning group.
--- fs/richacl_base.c | 150 ++++++++++++++++++++++++++++++++++++++---------- include/linux/richacl.h | 1 + 2 files changed, 122 insertions(+), 29 deletions(-)
fs/richacl_base.c +121 -29
@@ -439,49 +439,141 @@ richacl_inherit(const struct richacl *dir_acl, int isdir) }/** -* richacl_equiv_mode - check if @acl is equivalent to file permission bits -* @mode_p: the file mode (including the file type) +* __richacl_equiv_mode - compute the mode equivalent of @acl * -* 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 does not consider the masks in @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. +* An acl is considered equivalent to a file mode if it only consists of +* owner@, group@, and everyone@ entries and the owner@ permissions do not +* depend on whether the owner is a member in the owning group. */ int -richacl_equiv_mode (const struct richacl *acl,mode_t *mode_p) +__richacl_equiv_mode (const struct richacl *acl,mode_t *mode_p) { -const struct richace *ace = acl->a_entries; -unsigned int x; -mode_t mask; - -if (acl->a_count !=1 || - acl->a_flags !=RICHACL_MASKED || - !richace_is_everyone (ace) || - !richace_is_allow (ace) || - ace->e_flags & ~RICHACE_SPECIAL_WHO ) +mode_t mode = *mode_p; + +/* +* The RICHACE_DELETE_CHILD flag is meaningless for non-directories, so +* we ignore it. +*/ +unsigned int x =S_ISDIR (mode) ?0 :RICHACE_DELETE_CHILD ; +struct { +unsigned int allowed; +unsigned int defined;/* allowed or denied */ + } owner = { + .allowed =RICHACE_POSIX_ALWAYS_ALLOWED , + .defined =RICHACE_POSIX_ALWAYS_ALLOWED |RICHACE_POSIX_OWNER_ALLOWED | x, + }, group = { + .allowed =RICHACE_POSIX_ALWAYS_ALLOWED , + .defined =RICHACE_POSIX_ALWAYS_ALLOWED | x, + }, everyone = { + .allowed =RICHACE_POSIX_ALWAYS_ALLOWED , + .defined =RICHACE_POSIX_ALWAYS_ALLOWED | x, + }; +const struct richace *ace; + +richacl_for_each_entry (ace, acl) { +if (ace->e_flags & ~RICHACE_SPECIAL_WHO ) +return -1 ; + +if (richace_is_owner (ace) ||richace_is_everyone (ace)) { + x = ace->e_mask & ~owner.defined; +if (richace_is_allow (ace)) { +unsigned int group_denied = group.defined & ~group.allowed; + +if (x & group_denied) +return -1 ; + owner.allowed |= x; + }else /* if (richace_is_deny(ace)) */ { +if (x & group.allowed) +return -1 ; + } + owner.defined |= x; + +if (richace_is_everyone (ace)) { + x = ace->e_mask; +if (richace_is_allow (ace)) { + group.allowed |= x & ~group.defined; + everyone.allowed |= x & ~everyone.defined; + } + group.defined |= x; + everyone.defined |= x; + } + }else if (richace_is_group (ace)) { + x = ace->e_mask & ~group.defined; +if (richace_is_allow (ace)) + group.allowed |= x; + group.defined |= x; + }else +return -1 ; + } + +if (group.allowed & ~owner.defined)return -1 ; -/* Mask flags we can ignore */ - x = ~RICHACE_POSIX_ALWAYS_ALLOWED ; -if (!S_ISDIR (*mode_p)) - x &= ~RICHACE_DELETE_CHILD ; +if (acl->a_flags &RICHACL_MASKED ) { + owner.allowed &= acl->a_owner_mask; + group.allowed &= acl->a_group_mask; + everyone.allowed &= acl->a_other_mask; + } - mask =richacl_masks_to_mode (acl); -if (((acl->a_group_mask ^richacl_mode_to_mask (mask >>3 )) & x) || - ((acl->a_other_mask ^richacl_mode_to_mask (mask)) & x)) + mode = (mode & ~S_IRWXUGO ) | + (richacl_mask_to_mode (owner.allowed) <<6 ) | + (richacl_mask_to_mode (group.allowed) <<3 ) | +richacl_mask_to_mode (everyone.allowed); + +/* Mask flags we can ignore */ + x = ~(S_ISDIR (mode) ?0 :RICHACE_DELETE_CHILD ); +if (((richacl_mode_to_mask (mode >>6 ) ^ owner.allowed) & x) || + ((richacl_mode_to_mask (mode >>3 ) ^ group.allowed) & x) || + ((richacl_mode_to_mask (mode) ^ everyone.allowed) & x))return -1 ; - x &= ~RICHACE_POSIX_OWNER_ALLOWED ; -if ((acl->a_owner_mask ^richacl_mode_to_mask (mask >>6 )) & x) + *mode_p = mode; +return 0 ; +} +EXPORT_SYMBOL_GPL (__richacl_equiv_mode); + +/** +* richacl_equiv_mode - determine if @acl is equivalent to a file mode +* @mode_p: the file mode +* +* The file type in @mode_p must be set when calling richacl_equiv_mode(). +* +* Returns with 0 if @acl is equivalent to a file mode; in that case, the +* file permission bits in @mode_p are set to the mode equivalent of @acl. +*/ +int +richacl_equiv_mode (const struct richacl *acl,mode_t *mode_p) +{ +mode_t mode = *mode_p; + +if (acl->a_flags & ~RICHACL_MASKED )return -1 ; -if ((ace->e_mask ^RICHACE_POSIX_MODE_ALL ) & x) +if (__richacl_equiv_mode (acl, &mode))return -1 ; - *mode_p = (*mode_p & ~S_IRWXUGO ) | mask; +if (acl->a_flags &RICHACL_MASKED ) { +mode_t mask =richacl_masks_to_mode (acl); +unsigned int x; + +/* Mask flags we can ignore */ + x = ~(RICHACE_POSIX_ALWAYS_ALLOWED | + (S_ISDIR (mode) ?0 :RICHACE_DELETE_CHILD )); + +if (((acl->a_group_mask ^richacl_mode_to_mask (mask >>3 )) & x) || + ((acl->a_other_mask ^richacl_mode_to_mask (mask)) & x)) +return -1 ; + + x &= ~RICHACE_POSIX_OWNER_ALLOWED ; +if ((acl->a_owner_mask ^richacl_mode_to_mask (mask >>6 )) & x) +return -1 ; + + mode &= ~S_IRWXUGO | mask; + } + + *mode_p = mode;return 0 ; }EXPORT_SYMBOL_GPL (richacl_equiv_mode);
include/linux/richacl.h +1 -0
@@ -297,6 +297,7 @@ 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 struct richacl *richacl_inherit (const struct richacl *,int ); +extern int __richacl_equiv_mode (const struct richacl *,mode_t *);extern int richacl_equiv_mode (const struct richacl *,mode_t *);/* richacl_inode.c */
Signature · 1 line
2.1.0
Re: [RFC v3 19/45] richacl: Also recognize nontrivial mode-equivalent acls
Quoted 63 lines · Andreas Gruenbacher
So far,
richacl_equiv_mode()is relatively limited in the types of acl it considers equivalent to a file mode: it only accepts masked acls with a single everyone@:rwpxd::allow entry.Change this to consider all acls equivalent to file modes if they only consist of owner@, group@, and everyone@ entries and the owner@ permissions do not depend on whether the owner is a member in the owning group.
Signed-off-by Andreas Gruenbacher <[email protected]>--- fs/richacl_base.c | 150 ++++++++++++++++++++++++++++++++++++++---------- include/linux/richacl.h | 1 + 2 files changed, 122 insertions(+), 29 deletions(-)fs/richacl_base.c +18
-18@@ -439,49 +439,141 @@ richacl_inherit(const struct richacl *dir_acl, int isdir) }/** -* richacl_equiv_mode - check if @acl is equivalent to file permission bits -* @mode_p: the file mode (including the file type) +* __richacl_equiv_mode - compute the mode equivalent of @acl * -* 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 does not consider the masks in @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. +* An acl is considered equivalent to a file mode if it only consists of +* owner@, group@, and everyone@ entries and the owner@ permissions do not +* depend on whether the owner is a member in the owning group. */ int -richacl_equiv_mode (const struct richacl *acl,mode_t *mode_p) +__richacl_equiv_mode (const struct richacl *acl,mode_t *mode_p) { -const struct richace *ace = acl->a_entries; -unsigned int x; -mode_t mask; - -if (acl->a_count !=1 || - acl->a_flags !=RICHACL_MASKED || - !richace_is_everyone (ace) || - !richace_is_allow (ace) || - ace->e_flags & ~RICHACE_SPECIAL_WHO ) +mode_t mode = *mode_p; + +/* +* The RICHACE_DELETE_CHILD flag is meaningless for non-directories, so +* we ignore it. +*/ +unsigned int x =S_ISDIR (mode) ?0 :RICHACE_DELETE_CHILD ; +struct { +unsigned int allowed; +unsigned int defined;/* allowed or denied */ + } owner = { + .allowed =RICHACE_POSIX_ALWAYS_ALLOWED ,
fs/richacl_base.c richacl_inherit(const struct richacl *dir_acl, int isdir) · line 474
Quoted 10 lines
fs/richacl_base.c +10
-0+ .defined =RICHACE_POSIX_ALWAYS_ALLOWED |RICHACE_POSIX_OWNER_ALLOWED | x, + }, group = { + .allowed = RICHACE_POSIX_ALWAYS_ALLOWED, + .defined =RICHACE_POSIX_ALWAYS_ALLOWED | x, + }, everyone = { + .allowed = RICHACE_POSIX_ALWAYS_ALLOWED, + .defined = RICHACE_POSIX_ALWAYS_ALLOWED | x, + }; +const struct richace *ace; +
I'm having trouble following this code. Some comments just to see if I can figure it out:
fs/richacl_base.c richacl_inherit(const struct richacl *dir_acl, int isdir) · line 480
Quoted 6 lines
fs/richacl_base.c +6
-0+richacl_for_each_entry (ace, acl) { +if (ace->e_flags & ~RICHACE_SPECIAL_WHO ) +return -1 ; + +if (richace_is_owner (ace) ||richace_is_everyone (ace)) { + x = ace->e_mask & ~owner.defined;
So x is now the acl mask with the always-permitted bits ignored.
fs/richacl_base.c richacl_inherit(const struct richacl *dir_acl, int isdir) · line 485
Quoted 5 lines
fs/richacl_base.c +5
-0+if (richace_is_allow (ace)) { +unsigned int group_denied = group.defined & ~group.allowed; + +if (x & group_denied) +return -1 ;
If we've already denied something to the group we don't want to later allow it to the owner, because that could make the ACL behave differently depending on whether the owner was a member of the group, not something a mode can do. OK, understood.
(This is possibly overkill, since it rejects e.g.
deny owner all
deny group all
allow everyone all
which is equivalent to 007? But some overkill's OK.)
fs/richacl_base.c richacl_inherit(const struct richacl *dir_acl, int isdir) · line 489
Quoted 4 lines
fs/richacl_base.c +4
-0+ owner.allowed |= x; + }else /* if (richace_is_deny(ace)) */ { +if (x & group.allowed) +return -1 ;
Uh, so same rationale as above, I guess, this might lead to different results depending on whether the owner's also a member of the group.
Quoted 9 lines
fs/richacl_base.c +9
-0+ } + owner.defined |= x; + +if (richace_is_everyone (ace)) { + x = ace->e_mask; +if (richace_is_allow (ace)) { + group.allowed |= x & ~group.defined; + everyone.allowed |= x & ~everyone.defined; + }
fs/richacl_base.c richacl_inherit(const struct richacl *dir_acl, int isdir) · line 508
Quoted 10 lines
fs/richacl_base.c +10
-0+ group.defined |= x; + everyone.defined |= x; + } + }else if (richace_is_group (ace)) { + x = ace->e_mask & ~group.defined; +if (richace_is_allow (ace)) + group.allowed |= x; + group.defined |= x; + }else +return -1 ;
This last else clause is redundant with the first RICHACE_SPECIAL_WHO check, right? Not that I necessarily object.
--b.
Quoted 101 lines
fs/richacl_base.c +59
-11+ } + +if (group.allowed & ~owner.defined)return -1 ; -/* Mask flags we can ignore */ - x = ~RICHACE_POSIX_ALWAYS_ALLOWED ; -if (!S_ISDIR (*mode_p)) - x &= ~RICHACE_DELETE_CHILD ; +if (acl->a_flags &RICHACL_MASKED ) { + owner.allowed &= acl->a_owner_mask; + group.allowed &= acl->a_group_mask; + everyone.allowed &= acl->a_other_mask; + } - mask =richacl_masks_to_mode (acl); -if (((acl->a_group_mask ^richacl_mode_to_mask (mask >>3 )) & x) || - ((acl->a_other_mask ^richacl_mode_to_mask (mask)) & x)) + mode = (mode & ~S_IRWXUGO ) | + (richacl_mask_to_mode (owner.allowed) <<6 ) | + (richacl_mask_to_mode (group.allowed) <<3 ) | +richacl_mask_to_mode (everyone.allowed); + +/* Mask flags we can ignore */ + x = ~(S_ISDIR (mode) ?0 :RICHACE_DELETE_CHILD ); +if (((richacl_mode_to_mask (mode >>6 ) ^ owner.allowed) & x) || + ((richacl_mode_to_mask (mode >>3 ) ^ group.allowed) & x) || + ((richacl_mode_to_mask (mode) ^ everyone.allowed) & x))return -1 ; - x &= ~RICHACE_POSIX_OWNER_ALLOWED ; -if ((acl->a_owner_mask ^richacl_mode_to_mask (mask >>6 )) & x) + *mode_p = mode; +return 0 ; +} +EXPORT_SYMBOL_GPL (__richacl_equiv_mode); + +/** +* richacl_equiv_mode - determine if @acl is equivalent to a file mode +* @mode_p: the file mode +* +* The file type in @mode_p must be set when calling richacl_equiv_mode(). +* +* Returns with 0 if @acl is equivalent to a file mode; in that case, the +* file permission bits in @mode_p are set to the mode equivalent of @acl. +*/ +int +richacl_equiv_mode (const struct richacl *acl,mode_t *mode_p) +{ +mode_t mode = *mode_p; + +if (acl->a_flags & ~RICHACL_MASKED )return -1 ; -if ((ace->e_mask ^RICHACE_POSIX_MODE_ALL ) & x) +if (__richacl_equiv_mode (acl, &mode))return -1 ; - *mode_p = (*mode_p & ~S_IRWXUGO ) | mask; +if (acl->a_flags &RICHACL_MASKED ) { +mode_t mask =richacl_masks_to_mode (acl); +unsigned int x; + +/* Mask flags we can ignore */ + x = ~(RICHACE_POSIX_ALWAYS_ALLOWED | + (S_ISDIR (mode) ?0 :RICHACE_DELETE_CHILD )); + +if (((acl->a_group_mask ^richacl_mode_to_mask (mask >>3 )) & x) || + ((acl->a_other_mask ^richacl_mode_to_mask (mask)) & x)) +return -1 ; + + x &= ~RICHACE_POSIX_OWNER_ALLOWED ; +if ((acl->a_owner_mask ^richacl_mode_to_mask (mask >>6 )) & x) +return -1 ; + + mode &= ~S_IRWXUGO | mask; + } + + *mode_p = mode;return 0 ; }EXPORT_SYMBOL_GPL (richacl_equiv_mode);include/linux/richacl.h +1
-0@@ -297,6 +297,7 @@ 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 struct richacl *richacl_inherit (const struct richacl *,int ); +extern int __richacl_equiv_mode (const struct richacl *,mode_t *);extern int richacl_equiv_mode (const struct richacl *,mode_t *);/* richacl_inode.c */ Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 19/45] richacl: Also recognize nontrivial mode-equivalent acls
Quoted 78 lines · bfields
Quoted 63 lines · Andreas Gruenbacher
So far,
richacl_equiv_mode()is relatively limited in the types of acl it considers equivalent to a file mode: it only accepts masked acls with a single everyone@:rwpxd::allow entry.Change this to consider all acls equivalent to file modes if they only consist of owner@, group@, and everyone@ entries and the owner@ permissions do not depend on whether the owner is a member in the owning group.
Signed-off-by Andreas Gruenbacher <[email protected]>--- fs/richacl_base.c | 150 ++++++++++++++++++++++++++++++++++++++---------- include/linux/richacl.h | 1 + 2 files changed, 122 insertions(+), 29 deletions(-)fs/richacl_base.c +18
-18@@ -439,49 +439,141 @@ richacl_inherit(const struct richacl *dir_acl, int isdir) }/** -* richacl_equiv_mode - check if @acl is equivalent to file permission bits -* @mode_p: the file mode (including the file type) +* __richacl_equiv_mode - compute the mode equivalent of @acl * -* 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 does not consider the masks in @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. +* An acl is considered equivalent to a file mode if it only consists of +* owner@, group@, and everyone@ entries and the owner@ permissions do not +* depend on whether the owner is a member in the owning group. */ int -richacl_equiv_mode (const struct richacl *acl,mode_t *mode_p) +__richacl_equiv_mode (const struct richacl *acl,mode_t *mode_p) { -const struct richace *ace = acl->a_entries; -unsigned int x; -mode_t mask; - -if (acl->a_count !=1 || - acl->a_flags !=RICHACL_MASKED || - !richace_is_everyone (ace) || - !richace_is_allow (ace) || - ace->e_flags & ~RICHACE_SPECIAL_WHO ) +mode_t mode = *mode_p; + +/* +* The RICHACE_DELETE_CHILD flag is meaningless for non-directories, so +* we ignore it. +*/ +unsigned int x =S_ISDIR (mode) ?0 :RICHACE_DELETE_CHILD ; +struct { +unsigned int allowed; +unsigned int defined;/* allowed or denied */ + } owner = { + .allowed =RICHACE_POSIX_ALWAYS_ALLOWED ,fs/richacl_base.c richacl_inherit(const struct richacl *dir_acl, int isdir) · line 474
Quoted 10 lines
fs/richacl_base.c +10
-0+ .defined =RICHACE_POSIX_ALWAYS_ALLOWED |RICHACE_POSIX_OWNER_ALLOWED | x, + }, group = { + .allowed = RICHACE_POSIX_ALWAYS_ALLOWED, + .defined =RICHACE_POSIX_ALWAYS_ALLOWED | x, + }, everyone = { + .allowed = RICHACE_POSIX_ALWAYS_ALLOWED, + .defined = RICHACE_POSIX_ALWAYS_ALLOWED | x, + }; +const struct richace *ace; +I'm having trouble following this code. Some comments just to see if I can figure it out:
Quoted 8 lines
fs/richacl_base.c richacl_inherit(const struct richacl *dir_acl, int isdir) · line 480
Quoted 6 lines
fs/richacl_base.c +6
-0+richacl_for_each_entry (ace, acl) { +if (ace->e_flags & ~RICHACE_SPECIAL_WHO ) +return -1 ; + +if (richace_is_owner (ace) ||richace_is_everyone (ace)) { + x = ace->e_mask & ~owner.defined;So x is now the acl mask with the always-permitted bits ignored.
No, I was confused: that's only true the first time through, on subsequent runs it's basically "everything in this mask that's not already been allowed or denied to the owner".
Quoted 12 lines
fs/richacl_base.c richacl_inherit(const struct richacl *dir_acl, int isdir) · line 485
Quoted 6 lines
fs/richacl_base.c +5
-0+if (richace_is_allow (ace)) { +unsigned int group_denied = group.defined & ~group.allowed; + +if (x & group_denied) +return -1 ;If we've already denied something to the group we don't want to later allow it to the owner, because that could make the ACL behave differently depending on whether the owner was a member of the group, not something a mode can do. OK, understood.
Quoted 5 lines
(This is possibly overkill, since it rejects e.g.
deny owner all
deny group all
allow everyone all
So in this example, by the time we get down here, owner.defined has all bits set, so x is 0, so I was wrong, this wouldn't be rejected.
Quoted 11 lines
which is equivalent to 007? But some overkill's OK.)
fs/richacl_base.c richacl_inherit(const struct richacl *dir_acl, int isdir) · line 489
Quoted 4 lines
fs/richacl_base.c +4
-0+ owner.allowed |= x; + }else /* if (richace_is_deny(ace)) */ { +if (x & group.allowed) +return -1 ;Uh, so same rationale as above, I guess, this might lead to different results depending on whether the owner's also a member of the group.
And what the above test and this one are saying is:
- could this ace allow something to the owner previously denied to the group?
- could this ace deny something to the owner previously allowed to the group?
And checking this at each step is the same as testing whether the result might vary depending whether the owner's in the group. Got it! Sorry it took me a while to puzzle through....
Couldn't you also do this by doing the usual reverse scan through the acl applying permissions, but separately tracking:
- mask that would be allowed to the owner assuming it's a member of the group
- mask that would be allowed to the owner assuming it's not a member of the group
and then comparing the two at the end?
Admittedly I don't know if that'd be simpler or not.
--b.
Quoted 119 lines
Quoted 10 lines
fs/richacl_base.c +9
-0+ } + owner.defined |= x; + +if (richace_is_everyone (ace)) { + x = ace->e_mask; +if (richace_is_allow (ace)) { + group.allowed |= x & ~group.defined; + everyone.allowed |= x & ~everyone.defined; + }fs/richacl_base.c richacl_inherit(const struct richacl *dir_acl, int isdir) · line 508
Quoted 10 lines
fs/richacl_base.c +10
-0+ group.defined |= x; + everyone.defined |= x; + } + }else if (richace_is_group (ace)) { + x = ace->e_mask & ~group.defined; +if (richace_is_allow (ace)) + group.allowed |= x; + group.defined |= x; + }else +return -1 ;This last else clause is redundant with the first
RICHACE_SPECIAL_WHOcheck, right? Not that I necessarily object.--b.
Quoted 93 lines
fs/richacl_base.c +59
-11+ } + +if (group.allowed & ~owner.defined)return -1 ; -/* Mask flags we can ignore */ - x = ~RICHACE_POSIX_ALWAYS_ALLOWED ; -if (!S_ISDIR (*mode_p)) - x &= ~RICHACE_DELETE_CHILD ; +if (acl->a_flags &RICHACL_MASKED ) { + owner.allowed &= acl->a_owner_mask; + group.allowed &= acl->a_group_mask; + everyone.allowed &= acl->a_other_mask; + } - mask =richacl_masks_to_mode (acl); -if (((acl->a_group_mask ^richacl_mode_to_mask (mask >>3 )) & x) || - ((acl->a_other_mask ^richacl_mode_to_mask (mask)) & x)) + mode = (mode & ~S_IRWXUGO ) | + (richacl_mask_to_mode (owner.allowed) <<6 ) | + (richacl_mask_to_mode (group.allowed) <<3 ) | +richacl_mask_to_mode (everyone.allowed); + +/* Mask flags we can ignore */ + x = ~(S_ISDIR (mode) ?0 :RICHACE_DELETE_CHILD ); +if (((richacl_mode_to_mask (mode >>6 ) ^ owner.allowed) & x) || + ((richacl_mode_to_mask (mode >>3 ) ^ group.allowed) & x) || + ((richacl_mode_to_mask (mode) ^ everyone.allowed) & x))return -1 ; - x &= ~RICHACE_POSIX_OWNER_ALLOWED ; -if ((acl->a_owner_mask ^richacl_mode_to_mask (mask >>6 )) & x) + *mode_p = mode; +return 0 ; +} +EXPORT_SYMBOL_GPL (__richacl_equiv_mode); + +/** +* richacl_equiv_mode - determine if @acl is equivalent to a file mode +* @mode_p: the file mode +* +* The file type in @mode_p must be set when calling richacl_equiv_mode(). +* +* Returns with 0 if @acl is equivalent to a file mode; in that case, the +* file permission bits in @mode_p are set to the mode equivalent of @acl. +*/ +int +richacl_equiv_mode (const struct richacl *acl,mode_t *mode_p) +{ +mode_t mode = *mode_p; + +if (acl->a_flags & ~RICHACL_MASKED )return -1 ; -if ((ace->e_mask ^RICHACE_POSIX_MODE_ALL ) & x) +if (__richacl_equiv_mode (acl, &mode))return -1 ; - *mode_p = (*mode_p & ~S_IRWXUGO ) | mask; +if (acl->a_flags &RICHACL_MASKED ) { +mode_t mask =richacl_masks_to_mode (acl); +unsigned int x; + +/* Mask flags we can ignore */ + x = ~(RICHACE_POSIX_ALWAYS_ALLOWED | + (S_ISDIR (mode) ?0 :RICHACE_DELETE_CHILD )); + +if (((acl->a_group_mask ^richacl_mode_to_mask (mask >>3 )) & x) || + ((acl->a_other_mask ^richacl_mode_to_mask (mask)) & x)) +return -1 ; + + x &= ~RICHACE_POSIX_OWNER_ALLOWED ; +if ((acl->a_owner_mask ^richacl_mode_to_mask (mask >>6 )) & x) +return -1 ; + + mode &= ~S_IRWXUGO | mask; + } + + *mode_p = mode;return 0 ; }EXPORT_SYMBOL_GPL (richacl_equiv_mode);include/linux/richacl.h +1
-0@@ -297,6 +297,7 @@ 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 struct richacl *richacl_inherit (const struct richacl *,int ); +extern int __richacl_equiv_mode (const struct richacl *,mode_t *);extern int richacl_equiv_mode (const struct richacl *,mode_t *);
Quoted 8 lines
Quoted 8 lines
include/linux/richacl.h +0
-0/* richacl_inode.c */ Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 19/45] richacl: Also recognize nontrivial mode-equivalent acls
In reply to Andreas GruenbacherQuoted 19 lines · Andreas Gruenbacher
So far,
richacl_equiv_mode()is relatively limited in the types of acl it considers equivalent to a file mode: it only accepts masked acls with a single everyone@:rwpxd::allow entry.Change this to consider all acls equivalent to file modes if they only consist of owner@, group@, and everyone@ entries and the owner@ permissions do not depend on whether the owner is a member in the owning group.
Signed-off-by Andreas Gruenbacher <[email protected]>--- fs/richacl_base.c | 150 ++++++++++++++++++++++++++++++++++++++---------- include/linux/richacl.h | 1 + 2 files changed, 122 insertions(+), 29 deletions(-)fs/richacl_base.c +0
-0
fs/richacl_base.c richacl_inherit(const struct richacl *dir_acl, int isdir) · line 447
Quoted 10 lines
fs/richacl_base.c +1
-5@@ -439,49 +439,141 @@ richacl_inherit(const struct richacl *dir_acl, int isdir) }/** -* richacl_equiv_mode - check if @acl is equivalent to file permission bits -* @mode_p: the file mode (including the file type) +* __richacl_equiv_mode - compute the mode equivalent of @acl * -* 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 comment is a little confusing:
fs/richacl_base.c richacl_inherit(const struct richacl *dir_acl, int isdir) · line 444
Quoted 1 line
fs/richacl_base.c +1
-0+* This function does not consider the masks in @acl.
Given that we do this later:
fs/richacl_base.c richacl_inherit(const struct richacl *dir_acl, int isdir) · line 518
Quoted 5 lines
fs/richacl_base.c +5
-0+if (acl->a_flags &RICHACL_MASKED ) { + owner.allowed &= acl->a_owner_mask; + group.allowed &= acl->a_group_mask; + everyone.allowed &= acl->a_other_mask; + }
I think the difference is that here you're checking that the end result after applying masks is mode-equivalent, whereas in riachacl_equiv_mode:
Quoted 8 lines
fs/richacl_base.c +8
-0+if (acl->a_flags &RICHACL_MASKED ) { +mode_t mask =richacl_masks_to_mode (acl); +unsigned int x; + +/* Mask flags we can ignore */ + x = ~(RICHACE_POSIX_ALWAYS_ALLOWED | + (S_ISDIR (mode) ?0 :RICHACE_DELETE_CHILD )); +
fs/richacl_base.c richacl_inherit(const struct richacl *dir_acl, int isdir) · line 574
Quoted 10 lines
fs/richacl_base.c +10
-0+if (((acl->a_group_mask ^richacl_mode_to_mask (mask >>3 )) & x) || + ((acl->a_other_mask ^richacl_mode_to_mask (mask)) & x)) +return -1 ; + + x &= ~RICHACE_POSIX_OWNER_ALLOWED ; +if ((acl->a_owner_mask ^richacl_mode_to_mask (mask >>6 )) & x) +return -1 ; + + mode &= ~S_IRWXUGO | mask; + }
... you're also checking whether the masks themselves are mode-equivalent? Is that the right thing to do?
I've probably misread the code again....
--b.
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 19/45] richacl: Also recognize nontrivial mode-equivalent acls
Bruce,
2015-05-15 22:51 GMT+02:00 J. Bruce Fields <[email protected]>:
Quoted 12 lines · Andreas Gruenbacher
This comment is a little confusing:
Quoted 1 line
+ * This function does not consider the masks in @acl.
Given that we do this later:
Quoted 5 lines
fs/richacl_base.c +5
-0+if (acl->a_flags &RICHACL_MASKED ) { + owner.allowed &= acl->a_owner_mask; + group.allowed &= acl->a_group_mask; + everyone.allowed &= acl->a_other_mask; + }
Indeed, the comment seems to be a left-over from a previous version, sorry.
Quoted 4 lines
I think the difference is that here you're checking that the end result after applying masks is mode-equivalent, whereas in
riachacl_equiv_mode[...] you're also checking whether the masks themselves are mode-equivalent?
Yes.
Quoted 1 line
Is that the right thing to do?
This patch and its consequences probably weren't thought through well enough initially. I meanwhile think that it doesn't matter if the masks themselves are mode-equivalent and that we can drop this check.
Thanks,
Andreas
Re: [RFC v3 19/45] richacl: Also recognize nontrivial mode-equivalent acls
Quoted 23 lines · Andreas Grünbacher
Bruce,
2015-05-15 22:51 GMT+02:00 J. Bruce Fields <[email protected]>:
Quoted 12 lines · Andreas Gruenbacher
This comment is a little confusing:
Quoted 1 line
+ * This function does not consider the masks in @acl.
Given that we do this later:
Quoted 5 lines
fs/richacl_base.c +5
-0+if (acl->a_flags &RICHACL_MASKED ) { + owner.allowed &= acl->a_owner_mask; + group.allowed &= acl->a_group_mask; + everyone.allowed &= acl->a_other_mask; + }Indeed, the comment seems to be a left-over from a previous version, sorry.
Quoted 4 lines
I think the difference is that here you're checking that the end result after applying masks is mode-equivalent, whereas in
riachacl_equiv_mode[...] you're also checking whether the masks themselves are mode-equivalent?
Quoted 7 lines
Yes.
Quoted 1 line
Is that the right thing to do?
This patch and its consequences probably weren't thought through well enough initially. I meanwhile think that it doesn't matter if the masks themselves are mode-equivalent and that we can drop this check.
OK, thanks, that would simplify things.
--b.
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC v3 20/45 richacl: Automatic Inheritance
In reply to Andreas GruenbacherAutomatic 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.
--- fs/richacl_base.c | 10 +++++++++- fs/richacl_inode.c | 7 ++++++- include/linux/richacl.h | 25 +++++++++++++++++++++++-- 3 files changed, 38 insertions(+), 4 deletions(-)
fs/richacl_base.c +9 -1
@@ -352,7 +352,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 &RICHACL_MASKED )) + (acl->a_flags &RICHACL_MASKED ) && + (!richacl_is_auto_inherit (acl) ||richacl_is_protected (acl)))return acl; clone =richacl_clone (acl,GFP_KERNEL );
@@ -364,6 +365,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 |=RICHACL_PROTECTED ;return clone; }
@@ -434,6 +437,11 @@ richacl_inherit(const struct richacl *dir_acl, int isdir) ace++; } } +if (richacl_is_auto_inherit (dir_acl)) { + acl->a_flags =RICHACL_AUTO_INHERIT ; +richacl_for_each_entry (ace, acl) + ace->e_flags |=RICHACE_INHERITED_ACE ; + }return acl; }
fs/richacl_inode.c +6 -1
@@ -223,9 +223,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 RICHACL_PROTECTED because we are +* doing an implicit chmod +*/ +if (richacl_is_auto_inherit (acl)) + acl->a_flags |=RICHACL_PROTECTED ;richacl_compute_max_masks (acl); -/* * Ensure that the acl will not grant any permissions beyond * the create mode.
include/linux/richacl.h +23 -2
@@ -53,9 +53,15 @@ struct richacl { _ace--)/* a_flag values */ +#define RICHACL_AUTO_INHERIT 0x01 +#define RICHACL_PROTECTED 0x02 +#define RICHACL_DEFAULTED 0x04#define RICHACL_MASKED 0x80 -#define RICHACL_VALID_FLAGS ( \ +#define RICHACL_VALID_FLAGS ( \ + RICHACL_AUTO_INHERIT | \ + RICHACL_PROTECTED | \ + RICHACL_DEFAULTED | \ RICHACL_MASKED)/* e_type values */
@@ -68,6 +74,7 @@ struct richacl {#define RICHACE_NO_PROPAGATE_INHERIT_ACE 0x0004#define RICHACE_INHERIT_ONLY_ACE 0x0008#define RICHACE_IDENTIFIER_GROUP 0x0040 +#define RICHACE_INHERITED_ACE 0x0080#define RICHACE_SPECIAL_WHO 0x4000#define RICHACE_VALID_FLAGS ( \
@@ -76,6 +83,7 @@ struct richacl {RICHACE_NO_PROPAGATE_INHERIT_ACE | \RICHACE_INHERIT_ONLY_ACE | \RICHACE_IDENTIFIER_GROUP | \ +RICHACE_INHERITED_ACE | \ RICHACE_SPECIAL_WHO)/* e_mask bitflags */
@@ -187,6 +195,18 @@ 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) +{ +return acl->a_flags &RICHACL_AUTO_INHERIT ; +} + +static inline int +richacl_is_protected (const struct richacl *acl) +{ +return acl->a_flags &RICHACL_PROTECTED ; +} +/** * richace_is_owner - check if @ace is an OWNER@ entry */
@@ -257,7 +277,8 @@ richace_clear_inheritance_flags(struct richace *ace) ace->e_flags &= ~(RICHACE_FILE_INHERIT_ACE |RICHACE_DIRECTORY_INHERIT_ACE |RICHACE_NO_PROPAGATE_INHERIT_ACE | -RICHACE_INHERIT_ONLY_ACE ); +RICHACE_INHERIT_ONLY_ACE | +RICHACE_INHERITED_ACE ); }/**
Signature · 1 line
2.1.0
RE: [RFC v3 20/45] richacl: Automatic Inheritance
Quoted 31 lines
-----Original Message-----
From: [email protected] [mailto:linux-nfs- [email protected]] On Behalf Of Andreas Gruenbacher
Sent: Friday, April 24, 2015 4:04 AM
To: [email protected]; [email protected]; linux- [email protected]
Subject: [RFC v3 20/45] richacl: Automatic InheritanceAutomatic 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_INHERITflag in the acl of a file is not set, the file is not affected by AI.- When the
ACL4_AUTO_INHERITflag 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 theACL4_AUTO_INHERITflag set, and all inherited aces will have theACE4_INHERITED_ACEflag set. This allows user space to distinguish between aces which have been inherited and aces which have been explicitly added.
Quoted 4 lines
- When the
ACL4_PROTECTEDacl 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).
You might want to edit your commit message to use RICHACL_ instead of ACL4_ constants...
Quoted 1 line
Linux does not have a way of creating files without setting the file
permission
Quoted 1 line
bits, so all files created inside a directory with
ACL4_AUTO_INHERITset
will
Quoted 1 line
also have the
ACL4_PROTECTEDflag set. This effectively disables
Automatic
Quoted 4 lines
Inheritance.
Protocols which support creating files without specifying permissions can explicitly clear the
ACL4_PROTECTEDflag after creating a file and reset
the file
Quoted 3 lines
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
Quoted 2 lines
the kernel to ignore the create mode when there are inherited permissions would fix this problem.
I'm unclear what will actually be supported for inherited ACLs here. Is this saying that on a pure-Linux system even with Linux NFS client and Linux NFS server, we still would not see inheritance since the mode will always be present on create?
My interest here is in how we will tie the Ganesha user space NFS server into this feature.
Frank
fs/richacl_base.c richacl_inherit(const struct richacl *dir_acl, int
Quoted 31 lines
Signed-off-by Andreas Gruenbacher <[email protected]>--- fs/richacl_base.c | 10 +++++++++- fs/richacl_inode.c | 7 ++++++- include/linux/richacl.h | 25 +++++++++++++++++++++++-- 3 files changed, 38 insertions(+), 4 deletions(-)fs/richacl_base.c index 54cb482..572f1b8 renamed from fs/richacl_base.c +0
-0100644
fs/richacl_base.c +2
-1@@ -352,7 +352,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 &RICHACL_MASKED )) + (acl->a_flags &RICHACL_MASKED ) && + (!richacl_is_auto_inherit (acl) ||richacl_is_protected (acl)))return acl; clone = richacl_clone(acl, GFP_KERNEL); @@ -364,6 +365,8 @@
richacl_chmod(struct richacl *acl, mode_t mode)fs/richacl_base.c +2
-0clone->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 |=RICHACL_PROTECTED ;return clone; }@@ -434,6 +437,11 @@ richacl_inherit(const struct richacl *dir_acl, int
isdir)
fs/richacl_inode.c index
Quoted 12 lines
fs/richacl_base.c +5
-0ace++; } } +if (richacl_is_auto_inherit (dir_acl)) { + acl->a_flags =RICHACL_AUTO_INHERIT ; +richacl_for_each_entry (ace, acl) + ace->e_flags |=RICHACE_INHERITED_ACE ; + }return acl; }fs/richacl_inode.c index renamed from fs/richacl_inode.c +0
-0
1a5b868..dfbb15a
Quoted 58 lines
100644
fs/richacl_inode.c +6
-1@@ -223,9 +223,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 RICHACL_PROTECTED because we are +* doing an implicit chmod +*/ +if (richacl_is_auto_inherit (acl)) + acl->a_flags |=RICHACL_PROTECTED ;richacl_compute_max_masks (acl); -/* * Ensure that the acl will not grant any permissions beyond fs/richacl_inode.c +0
-0* the create mode. include/linux/richacl.h index renamed from include/linux/richacl.h +0
-08a92b89..bcc2b64 100644
include/linux/richacl.h +9
-1@@ -53,9 +53,15 @@ struct richacl { _ace--)/* a_flag values */ +#define RICHACL_AUTO_INHERIT 0x01 +#define RICHACL_PROTECTED 0x02 +#define RICHACL_DEFAULTED 0x04#define RICHACL_MASKED 0x80 -#define RICHACL_VALID_FLAGS ( \ +#define RICHACL_VALID_FLAGS ( \ + RICHACL_AUTO_INHERIT | \ + RICHACL_PROTECTED | \ + RICHACL_DEFAULTED | \ RICHACL_MASKED)/* e_type values */ @@ -68,6 +74,7 @@ struct richacl {#define RICHACE_NO_PROPAGATE_INHERIT_ACE 0x0004#define RICHACE_INHERIT_ONLY_ACE 0x0008#define RICHACE_IDENTIFIER_GROUP 0x0040 +#define RICHACE_INHERITED_ACE 0x0080#define RICHACE_SPECIAL_WHO 0x4000#define RICHACE_VALID_FLAGS ( \@@ -76,6 +83,7 @@ struct richacl {RICHACE_NO_PROPAGATE_INHERIT_ACE | \RICHACE_INHERIT_ONLY_ACE | \RICHACE_IDENTIFIER_GROUP | \ +RICHACE_INHERITED_ACE | \ RICHACE_SPECIAL_WHO)/* e_mask bitflags */ @@ -187,6 +195,18 @@ extern void set_cached_richacl(struct inode *, struct
Quoted 1 line
richacl *); extern void
forget_cached_richacl(struct inode *); extern
struct
Quoted 26 lines
richacl *get_richacl(struct inode *);
include/linux/richacl.h +10
-2+static inline int +richacl_is_auto_inherit (const struct richacl *acl) { +return acl->a_flags &RICHACL_AUTO_INHERIT ; } + +static inline int +richacl_is_protected (const struct richacl *acl) { +return acl->a_flags &RICHACL_PROTECTED ; } +/** * richace_is_owner - check if @ace is an OWNER@ entry */ @@ -257,7 +277,8 @@ richace_clear_inheritance_flags(struct richace *ace) ace->e_flags &= ~(RICHACE_FILE_INHERIT_ACE |RICHACE_DIRECTORY_INHERIT_ACE |RICHACE_NO_PROPAGATE_INHERIT_ACE | -RICHACE_INHERIT_ONLY_ACE ); +RICHACE_INHERIT_ONLY_ACE | +RICHACE_INHERITED_ACE ); }/** -- 2.1.0
Quoted 2 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the
Quoted 2 lines
body of a message to [email protected] More majordomo info at
http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 20/45] richacl: Automatic Inheritance
2015-05-13 20:01 GMT+02:00 Frank Filz <[email protected]>:
Quoted 2 lines
You might want to edit your commit message to use
RICHACL_instead ofACL4_constants...
Indeed, thanks.
Quoted 13 lines
Quoted 5 lines
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_INHERITset will also have theACL4_PROTECTEDflag set. This effectively disables Automatic Inheritance.Quoted 7 lines
Protocols which support creating files without specifying permissions can explicitly clear the
ACL4_PROTECTEDflag after creating a file and reset the file masks to "undo" applying the create mode; seerichacl_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.
Quoted 4 lines
I'm unclear what will actually be supported for inherited ACLs here. Is this saying that on a pure-Linux system even with Linux NFS client and Linux NFS server, we still would not see inheritance since the mode will always be present on create?
What do you mean by "we still would not see inheritance"? Inheritance at file create time will still happen; a few extra flags will be set when Automatic Inheritance is "on" in the parent directory as indicated by the RICHACL_AUTO_INHERIT flag.
Files are inevitably created with defined permissions (the mode parameter to system calls like creat and mkdir), which means that the RICHACL_PROTECTED flag needs to be set, though. When someone changes the permissions of an entire directory tree, that change will not propagate to or below files with the protected flag set.
That being said, a daemon like Samba can "fake" full Automatic Inheritance by creating files and then updating the inherited acls appropriately. This will inevitably be racy, but unless someone implements a way to create files without a mode, that's the closest Samba can get.
Creating files atomically with explicitly defined acls is another operation which NFSv4 does but the Linux kernel does not support.
Quoted 2 lines
My interest here is in how we will tie the Ganesha user space NFS server into this feature.
I don't know, what do you currently do when somebody creates a file without defining the permissions (mode, acl or dacl)? That's the relevant case. The kernel nfs daemon currently creates a file with mode 0 --- which doesn't seem right.
Andreas
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 20/45] richacl: Automatic Inheritance
Quoted 6 lines · Andreas Grünbacher
That being said, a daemon like Samba can "fake" full Automatic Inheritance by creating files and then updating the inherited acls appropriately. This will inevitably be racy, but unless someone implements a way to create files without a mode, that's the closest Samba can get.
On Windows systems the client fake (no quotes :-) full Automatic Inheritance by creating files and then updating the inherited acls appropriately.
Server doesn't do that logic :-).
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 20/45] richacl: Automatic Inheritance
2015-05-13 22:28 GMT+02:00 Jeremy Allison <[email protected]>:
Quoted 11 lines · Andreas Grünbacher
Quoted 6 lines
That being said, a daemon like Samba can "fake" full Automatic Inheritance by creating files and then updating the inherited acls appropriately. This will inevitably be racy, but unless someone implements a way to create files without a mode, that's the closest Samba can get.
On Windows systems the client fake (no quotes :-) full Automatic Inheritance by creating files and then updating the inherited acls appropriately.
Hmm, interesting, are you *absolutely* sure about that? Is there anywhere I can look that up?
Thanks,
Andreas
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 20/45] richacl: Automatic Inheritance
Quoted 9 lines · Andreas Grünbacher
2015-05-13 22:28 GMT+02:00 Jeremy Allison <[email protected]>:
Quoted 8 lines · Andreas Grünbacher
Quoted 6 lines
That being said, a daemon like Samba can "fake" full Automatic Inheritance by creating files and then updating the inherited acls appropriately. This will inevitably be racy, but unless someone implements a way to create files without a mode, that's the closest Samba can get.
Quoted 6 lines
Quoted 3 lines
On Windows systems the client fake (no quotes :-) full Automatic Inheritance by creating files and then updating the inherited acls appropriately.
Hmm, interesting, are you *absolutely* sure about that? Is there anywhere I can look that up?
Hmm. Just realized we may be talking about different things :-).
In SMB/Samba the clients can create a file with no ACL, and the directory ACL is auto inherited. *That* we fake in Samba by creating then updating.
But in Windows there are the concept of "inherited" ACE entries, which can come from parents of parents of parents (etc.) objects. When a client modifies one of these on an
upper level directory, the server doesn't do the auto
updating that the vision of the file system might lead
you to expect - that updating is done by a tree walk
by the client.
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 20/45] richacl: Automatic Inheritance
2015-05-13 22:55 GMT+02:00 Jeremy Allison <[email protected]>:
Quoted 5 lines
Hmm. Just realized we may be talking about different things :-).
In SMB/Samba the clients can create a file with no ACL, and the directory ACL is auto inherited. *That* we fake in Samba by creating then updating.
Yes, that's what I was trying to say in the commit message of this patch. Samba will have to continue doing that.
Quoted 3 lines
But in Windows there are the concept of "inherited" ACE entries, which can come from parents of parents of parents (etc.) objects.
Fair enough, even if from the child's point of view, all inherited entries come from the direct parent no matter where the parent has gotten them from.
Quoted 5 lines
When a client modifies one of these on an upper level directory, the server doesn't do the auto updating that the vision of the file system might lead you to expect - that updating is done by a tree walk by the client.
Good, then we're on the same page.
Thanks,
Andreas
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RE: [RFC v3 20/45] richacl: Automatic Inheritance
In reply to Andreas GrünbacherQuoted 53 lines
-----Original Message-----
From: Andreas Grünbacher [mailto:[email protected]]
Sent: Wednesday, May 13, 2015 1:22 PM
To: Frank Filz
Cc: [email protected]; [email protected]; linux- [email protected]
Subject: Re: [RFC v3 20/45] richacl: Automatic Inheritance2015-05-13 20:01 GMT+02:00 Frank Filz <[email protected]>:
Quoted 2 lines
You might want to edit your commit message to use
RICHACL_instead ofACL4_constants...Indeed, thanks.
Quoted 13 lines
Quoted 5 lines
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_INHERITset will also have theACL4_PROTECTEDflag set. This effectively disables Automatic Inheritance.Quoted 7 lines
Protocols which support creating files without specifying permissions can explicitly clear the
ACL4_PROTECTEDflag after creating a file and reset the file masks to "undo" applying the create mode; seerichacl_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.Quoted 4 lines
I'm unclear what will actually be supported for inherited ACLs here. Is this saying that on a pure-Linux system even with Linux NFS client and Linux NFS server, we still would not see inheritance since the mode will always be present on create?
What do you mean by "we still would not see inheritance"? Inheritance at file create time will still happen; a few extra flags will be set when Automatic Inheritance is "on" in the parent directory as indicated by the
RICHACL_AUTO_INHERITflag.Files are inevitably created with defined permissions (the mode parameter to system calls like creat and mkdir), which means that the
RICHACL_PROTECTEDflag needs to be set, though. When someone changes the permissions of an entire directory tree, that change will not propagate to or below files with the protected flag set.That being said, a daemon like Samba can "fake" full Automatic Inheritance by creating files and then updating the inherited acls appropriately. This will inevitably be racy, but unless someone implements a way to create files without a mode, that's the closest Samba can get.
Creating files atomically with explicitly defined acls is another operation which NFSv4 does but the Linux kernel does not support.
Quoted 7 lines
Quoted 2 lines
My interest here is in how we will tie the Ganesha user space NFS server into this feature.
I don't know, what do you currently do when somebody creates a file without defining the permissions (mode, acl or dacl)? That's the relevant case. The kernel nfs daemon currently creates a file with mode 0 --- which doesn't seem right.
Ok, I think I'm beginning to understand...
So inheritance will happen, but there is also a mode set as part of the create that I assume is effectively handled the same as a subsequent chmod() on the file?
Any chance we could add a system call to do a open/create and pass an ACL (and heck, if we go there, why not a system call that allows creating with mtime, atime, owner, etc. also...).
Is there a mode that we could pass that would cause the least amount of damage to the inherited ACL?
Frank
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 20/45] richacl: Automatic Inheritance
2015-05-13 22:38 GMT+02:00 Frank Filz <[email protected]>:
Quoted 2 lines
So inheritance will happen, but there is also a mode set as part of the create that I assume is effectively handled the same as a subsequent
chmod()on the file?
The effect is similar to a subsequent chmod except that the effective permissions may be fewer then the create mode:
* In the traditional POSIX case, the effective permissions are (create_mode & ~umask).
* With POSIX ACLs and Richacls, if there are inheritable permissions, the effective permissions are the intersection of the create mode and the maximum permissions the inherited acl grants. So if the inherited acl grants at most rwxr-x---, with a create mode of rw-rw-rw, the effective permissions end up being rw-r-----.
Quoted 3 lines
Any chance we could add a system call to do a open/create and pass an ACL (and heck, if we go there, why not a system call that allows creating with mtime, atime, owner, etc. also...).
Send patches, but expect them to get killed :)
Quoted 2 lines
Is there a mode that we could pass that would cause the least amount of damage to the inherited ACL?
Yes, 0777. But the RICHACL_PROTECTED flag will still be set, and that is the problem in this case.
Andreas
RE: [RFC v3 20/45] richacl: Automatic Inheritance
Quoted 31 lines
-----Original Message-----
From: Andreas Grünbacher [mailto:[email protected]]
Sent: Wednesday, May 13, 2015 2:06 PM
To: Frank Filz
Cc: [email protected]; [email protected]; linux- [email protected]
Subject: Re: [RFC v3 20/45] richacl: Automatic Inheritance2015-05-13 22:38 GMT+02:00 Frank Filz <[email protected]>:
Quoted 2 lines
So inheritance will happen, but there is also a mode set as part of the create that I assume is effectively handled the same as a subsequent
chmod()on the file?The effect is similar to a subsequent chmod except that the effective permissions may be fewer then the create mode:
* In the traditional POSIX case, the effective permissions are (
create_mode& ~umask).* With POSIX ACLs and Richacls, if there are inheritable permissions, the effective permissions are the intersection of the create mode and the maximum permissions the inherited acl grants. So if the inherited acl grants at most rwxr-x---, with a create mode of rw-rw-rw, the effective permissions end up being rw-r-----.
Quoted 3 lines
Any chance we could add a system call to do a open/create and pass an ACL (and heck, if we go there, why not a system call that allows creating with mtime, atime, owner, etc. also...).
Quoted 7 lines
Send patches, but expect them to get killed :)
Quoted 2 lines
Is there a mode that we could pass that would cause the least amount of damage to the inherited ACL?
Yes, 0777. But the
RICHACL_PROTECTEDflag will still be set, and that is the problem in this case.
I'm not clear what the RICHACL_PROTECTED flag actually does. It looks like it's only tested in chmod, and then only if the mode matches the mask (at least if I'm understanding the code right).
Frank
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 20/45] richacl: Automatic Inheritance
2015-05-14 0:56 GMT+02:00 Frank Filz <[email protected]>:
Quoted 3 lines
I'm not clear what the
RICHACL_PROTECTEDflag actually does. It looks like it's only tested in chmod, and then only if the mode matches the mask (at least if I'm understanding the code right).
User space may propagate automatically inherited permissions to files which are subject to automatic inheritance (RICHACL_AUTO_INHERIT) and which are not protected from being modified (RICHACL_PROTECTED).
In richacl_chmod(), we simply detect when a chmod has no effect on the
acl: this is when the file masks are already correct, when the acl is already masked, and when Automatic Inheritance won't affect the acl.
Andreas
Re: [RFC v3 20/45] richacl: Automatic Inheritance
In reply to Andreas GruenbacherLooks fine to me.
Quoted 1 line
From a quick check of
git://github.com/andreas-gruenbacher/richacl.git
it looks like the userspace is all done too?
Do we have wireshark patches?
Quoted 8 lines · Andreas Gruenbacher
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.
Nit, but: I'd find a way to really emphasize this and put it in the very first sentence of the changelog and any documentation. People keep missing it.
--b.
Quoted 165 lines
Automatic Inheritance works as follows:
- When the
ACL4_AUTO_INHERITflag in the acl of a file is not set, the file is not affected by AI.- When the
ACL4_AUTO_INHERITflag 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 theACL4_AUTO_INHERITflag set, and all inherited aces will have theACE4_INHERITED_ACEflag set. This allows user space to distinguish between aces which have been inherited and aces which have been explicitly added.- When the
ACL4_PROTECTEDacl 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_INHERITset will also have theACL4_PROTECTEDflag set. This effectively disables Automatic Inheritance.Protocols which support creating files without specifying permissions can explicitly clear the
ACL4_PROTECTEDflag after creating a file and reset the file masks to "undo" applying the create mode; seerichacl_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 <[email protected]>--- fs/richacl_base.c | 10 +++++++++- fs/richacl_inode.c | 7 ++++++- include/linux/richacl.h | 25 +++++++++++++++++++++++-- 3 files changed, 38 insertions(+), 4 deletions(-)fs/richacl_base.c +9
-1@@ -352,7 +352,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 &RICHACL_MASKED )) + (acl->a_flags &RICHACL_MASKED ) && + (!richacl_is_auto_inherit (acl) ||richacl_is_protected (acl)))return acl; clone =richacl_clone (acl,GFP_KERNEL );@@ -364,6 +365,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 |=RICHACL_PROTECTED ;return clone; }@@ -434,6 +437,11 @@ richacl_inherit(const struct richacl *dir_acl, int isdir) ace++; } } +if (richacl_is_auto_inherit (dir_acl)) { + acl->a_flags =RICHACL_AUTO_INHERIT ; +richacl_for_each_entry (ace, acl) + ace->e_flags |=RICHACE_INHERITED_ACE ; + }return acl; }fs/richacl_inode.c +6
-1@@ -223,9 +223,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 RICHACL_PROTECTED because we are +* doing an implicit chmod +*/ +if (richacl_is_auto_inherit (acl)) + acl->a_flags |=RICHACL_PROTECTED ;richacl_compute_max_masks (acl); -/* * Ensure that the acl will not grant any permissions beyond * the create mode. include/linux/richacl.h +23
-2@@ -53,9 +53,15 @@ struct richacl { _ace--)/* a_flag values */ +#define RICHACL_AUTO_INHERIT 0x01 +#define RICHACL_PROTECTED 0x02 +#define RICHACL_DEFAULTED 0x04#define RICHACL_MASKED 0x80 -#define RICHACL_VALID_FLAGS ( \ +#define RICHACL_VALID_FLAGS ( \ + RICHACL_AUTO_INHERIT | \ + RICHACL_PROTECTED | \ + RICHACL_DEFAULTED | \ RICHACL_MASKED)/* e_type values */ @@ -68,6 +74,7 @@ struct richacl {#define RICHACE_NO_PROPAGATE_INHERIT_ACE 0x0004#define RICHACE_INHERIT_ONLY_ACE 0x0008#define RICHACE_IDENTIFIER_GROUP 0x0040 +#define RICHACE_INHERITED_ACE 0x0080#define RICHACE_SPECIAL_WHO 0x4000#define RICHACE_VALID_FLAGS ( \@@ -76,6 +83,7 @@ struct richacl {RICHACE_NO_PROPAGATE_INHERIT_ACE | \RICHACE_INHERIT_ONLY_ACE | \RICHACE_IDENTIFIER_GROUP | \ +RICHACE_INHERITED_ACE | \ RICHACE_SPECIAL_WHO)/* e_mask bitflags */ @@ -187,6 +195,18 @@ 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) +{ +return acl->a_flags &RICHACL_AUTO_INHERIT ; +} + +static inline int +richacl_is_protected (const struct richacl *acl) +{ +return acl->a_flags &RICHACL_PROTECTED ; +} +/** * richace_is_owner - check if @ace is an OWNER@ entry */ @@ -257,7 +277,8 @@ richace_clear_inheritance_flags(struct richace *ace) ace->e_flags &= ~(RICHACE_FILE_INHERIT_ACE |RICHACE_DIRECTORY_INHERIT_ACE |RICHACE_NO_PROPAGATE_INHERIT_ACE | -RICHACE_INHERIT_ONLY_ACE ); +RICHACE_INHERIT_ONLY_ACE | +RICHACE_INHERITED_ACE ); }/** Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 20/45] richacl: Automatic Inheritance
2015-05-14 17:09 GMT+02:00 J. Bruce Fields <[email protected]>:
Quoted 7 lines
Looks fine to me.
From a quick check of
git://github.com/andreas-gruenbacher/richacl.gitit looks like the userspace is all done too?
Yes, with pretty light testing.
Quoted 1 line
Do we have wireshark patches?
We do now:
https://code.wireshark.org/review/#/c/8526/1
Quoted 4 lines · Andreas Gruenbacher
Nit, but: I'd find a way to really emphasize this and put it in the very first sentence of the changelog and any documentation. People keep missing it.
Okay, I'll try.
Thanks,
Andreas
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 20/45] richacl: Automatic Inheritance
Quoted 9 lines · Andreas Grünbacher
2015-05-14 17:09 GMT+02:00 J. Bruce Fields <[email protected]>:
Quoted 7 lines
Looks fine to me.
From a quick check of
git://github.com/andreas-gruenbacher/richacl.gitit looks like the userspace is all done too?
Quoted 7 lines
Yes, with pretty light testing.
Quoted 1 line
Do we have wireshark patches?
We do now:
Great!--b.
Quoted 10 lines
Quoted 5 lines · Andreas Gruenbacher
Nit, but: I'd find a way to really emphasize this and put it in the very first sentence of the changelog and any documentation. People keep missing it.
Okay, I'll try.
Thanks,
Andreas
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC v3 21/45 richacl: xattr mapping functions
In reply to Andreas GruenbacherMap between "system.richacl" xattrs and the in-kernel representation.
--- fs/Makefile | 2 +- fs/richacl_xattr.c | 210 ++++++++++++++++++++++++++++++++++++++++++ fs/xattr.c | 34 +++++-- include/linux/richacl_xattr.h | 52 +++++++++++ include/uapi/linux/xattr.h | 2 + 5 files changed, 293 insertions(+), 7 deletions(-) create mode 100644 fs/richacl_xattr.c create mode 100644 include/linux/richacl_xattr.h
fs/Makefile +1 -1
@@ -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/
fs/richacl_xattr.c new file +210 -0
210 lines not on this page · raw message
fs/xattr.c +28 -6
@@ -21,6 +21,7 @@#include <linux/audit.h> #include <linux/vmalloc.h> #include <linux/posix_acl_xattr.h> +#include <linux/richacl_xattr.h> #include <asm/uaccess.h>
@@ -314,6 +315,18 @@ vfs_removexattr(struct dentry *dentry, const char *name) }EXPORT_SYMBOL_GPL (vfs_removexattr); +static void +fix_xattr_from_user (const char *kname,void *kvalue,size_t size) +{ +if (strncmp (kname,XATTR_SYSTEM_PREFIX ,XATTR_SYSTEM_PREFIX_LEN )) +return ; + kname +=XATTR_SYSTEM_PREFIX_LEN ; +if (!strcmp (kname,XATTR_POSIX_ACL_ACCESS ) || + !strcmp (kname,XATTR_POSIX_ACL_DEFAULT )) +posix_acl_fix_xattr_from_user (kvalue, size); +else if (!strcmp (kname,XATTR_RICHACL )) +richacl_fix_xattr_from_user (kvalue, size); +}/* * Extended attribute SET operations
@@ -350,9 +363,7 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value, error = -EFAULT ; goto out; } -if ((strcmp (kname,XATTR_NAME_POSIX_ACL_ACCESS ) ==0 ) || - (strcmp (kname,XATTR_NAME_POSIX_ACL_DEFAULT ) ==0 )) -posix_acl_fix_xattr_from_user (kvalue, size); +fix_xattr_from_user (kname, kvalue, size); } error =vfs_setxattr (d, kname, kvalue, size, flags);
@@ -419,6 +430,19 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,return error; } +static void +fix_xattr_to_user (const char *kname,void *kvalue,size_t size) +{ +if (strncmp (kname,XATTR_SYSTEM_PREFIX ,XATTR_SYSTEM_PREFIX_LEN )) +return ; + kname +=XATTR_SYSTEM_PREFIX_LEN ; +if (!strcmp (kname,XATTR_POSIX_ACL_ACCESS ) || + !strcmp (kname,XATTR_POSIX_ACL_DEFAULT )) +posix_acl_fix_xattr_to_user (kvalue, size); +else if (!strcmp (kname,XATTR_RICHACL )) +richacl_fix_xattr_to_user (kvalue, size); +} +/* * Extended attribute GET operations */
@@ -451,9 +475,7 @@ getxattr(struct dentry *d, const char __user *name, void __user *value, error =vfs_getxattr (d, kname, kvalue, size);if (error >0 ) { -if ((strcmp (kname,XATTR_NAME_POSIX_ACL_ACCESS ) ==0 ) || - (strcmp (kname,XATTR_NAME_POSIX_ACL_DEFAULT ) ==0 )) -posix_acl_fix_xattr_to_user (kvalue, size); +fix_xattr_to_user (kname, kvalue, size);if (size &©_to_user (value, kvalue, error)) error = -EFAULT ; }else if (error == -ERANGE && size >=XATTR_SIZE_MAX ) {
include/linux/richacl_xattr.h new file +52 -0
52 lines not on this page · raw message
include/uapi/linux/xattr.h +2 -0
@@ -73,5 +73,7 @@#define XATTR_POSIX_ACL_DEFAULT "posix_acl_default"#define XATTR_NAME_POSIX_ACL_DEFAULT XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_DEFAULT +#define XATTR_RICHACL "richacl" +#define XATTR_NAME_RICHACL XATTR_SYSTEM_PREFIX XATTR_RICHACL#endif /* _UAPI_LINUX_XATTR_H */
Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC v3 22/45 vfs: Add richacl permission checking
In reply to Andreas GruenbacherHook the richacl permission checking function into the vfs.
--- fs/namei.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- fs/posix_acl.c | 6 +++--- 2 files changed, 52 insertions(+), 5 deletions(-)
fs/namei.c +49 -2
@@ -35,6 +35,7 @@#include <linux/fs_struct.h> #include <linux/posix_acl.h> #include <linux/hash.h> +#include <linux/richacl.h> #include <asm/uaccess.h> #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; }
fs/posix_acl.c +3 -3
@@ -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
Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC v3 23/45 ext4: Add richacl support
In reply to Andreas GruenbacherFrom: "Aneesh Kumar K.V" <[email protected]>
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.
--- 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 | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/ext4/richacl.h | 47 ++++++++++++ fs/ext4/xattr.c | 6 ++ fs/ext4/xattr.h | 1 + 12 files changed, 314 insertions(+), 20 deletions(-) create mode 100644 fs/ext4/richacl.c create mode 100644 fs/ext4/richacl.h
fs/ext4/Kconfig +15 -0
@@ -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
fs/ext4/Makefile +1 -0
@@ -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
fs/ext4/acl.c +3 -4
@@ -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;
fs/ext4/acl.h +6 -6
@@ -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 <linux/sched.h> -#define ext4_get_acl NULL -#define ext4_set_acl NULL +#define ext4_get_posix_acl NULL +#define ext4_set_posix_acl NULLstatic 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 ; }
fs/ext4/file.c +4 -2
@@ -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, };
fs/ext4/ialloc.c +6 -1
@@ -28,6 +28,7 @@#include "ext4_jbd2.h" #include "xattr.h" #include "acl.h" +#include "richacl.h" #include <trace/events/ext4.h>
@@ -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;
fs/ext4/inode.c +7 -3
@@ -44,6 +44,7 @@#include "xattr.h" #include "acl.h" #include "truncate.h" +#include "richacl.h" #include <trace/events/ext4.h>
@@ -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)
fs/ext4/namei.c +7 -4
@@ -39,6 +39,7 @@#include "xattr.h" #include "acl.h" +#include "richacl.h" #include <trace/events/ext4.h> /*
@@ -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, };
fs/ext4/richacl.c new file +211 -0
211 lines not on this page · raw message
fs/ext4/richacl.h new file +47 -0
47 lines not on this page · raw message
fs/ext4/xattr.c +6 -0
@@ -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, +#endifNULL };
fs/ext4/xattr.h +1 -0
@@ -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 );
Signature · 1 line
2.1.0
RFC v3 24/45 ext4: Add richacl feature flag
In reply to Andreas GruenbacherFrom: "Aneesh Kumar K.V" <[email protected]>
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.
--- fs/ext4/ext4.h | 6 ++++-- fs/ext4/super.c | 41 ++++++++++++++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 11 deletions(-)
fs/ext4/ext4.h +4 -2
@@ -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| \
fs/ext4/super.c +32 -9
71 lines not on this page · raw message
Signature · 1 line
2.1.0
RFC v3 25/45 richacl: acl editing helper functions
In reply to Andreas GruenbacherThe file masks in richacls make chmod and creating new files more efficient than having to apply file permission bits to the acl directly. They also allow us to regain permissions from an acl even after a restrictive chmod, because the permissions in the acl itself are not being destroyed. In POSIX ACLs, the mask entry has a similar function.
Protocols like nfsv4 do not understand file masks. For those protocols, we need to compute nfs4 acls which represent the effective permissions granted by a richacl: we need to "apply" the file masks to the acl.
This is the first in a series of richacl transformation patches; it implements basic richacl editing functions. The following patches implement algorithms for transforming a richacl so that it can be evaluated as a plain nfs4 acl, with identical permission check results.
--- fs/Makefile | 3 +- fs/richacl_compat.c | 161 +++++++++++++++++++++++++++++++++++++++++ include/linux/richacl_compat.h | 40 ++++++++++ 3 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 fs/richacl_compat.c create mode 100644 include/linux/richacl_compat.h
fs/Makefile +2 -1
@@ -48,7 +48,8 @@ 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_xattr.o +richacl-y := richacl_base.o richacl_inode.o \ + richacl_xattr.o richacl_compat.o obj-y += quota/
fs/richacl_compat.c new file +161 -0
161 lines not on this page · raw message
include/linux/richacl_compat.h new file +40 -0
@@ -0,0 +1,40 @@ +/* +* Copyright (C) 2015 Red Hat, Inc. +* Written by Andreas Gruenbacher <[email protected]> +* +* 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_COMPAT_H +#define __RICHACL_COMPAT_H + +#include <linux/richacl.h> + +/** +* struct richacl_alloc - remember how many entries are actually allocated +* @acl: acl with a_count <= @count +* @count: the actual number of entries allocated in @acl +* +* We pass around this structure while modifying an acl so that we do +* not have to reallocate when we remove existing entries followed by +* adding new entries. +*/ +struct richacl_alloc { +struct richacl *acl; +unsigned int count; +}; + +struct richacl *richacl_prepare (struct richacl_alloc *,unsigned int ); +struct richace *richacl_append_entry (struct richacl_alloc *); +int richacl_insert_entry (struct richacl_alloc *,struct richace **); +void richacl_delete_entry (struct richacl_alloc *,struct richace **); + +#endif /* __RICHACL_COMPAT_H */
Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 25/45] richacl: acl editing helper functions
Quoted 11 lines · Andreas Gruenbacher
+/**
+ *richace_change_mask- set the mask of @ace to @mask
+ * @x: acl and number of allocated entries
+ * @ace: entry to modify
+ * @mask: new mask for @ace
+ *
+ * If @ace is inheritable, a inherit-only ace is inserted before @ace which
+ * includes the inheritable permissions of @ace and the inheritance flags of
+ * @ace are cleared before changing the mask.
+ *
+ * If @mode is 0, the original ace is turned into an inherit-only entry if
s/@mode/@mask/ ?
--b.
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC v3 26/45 richacl: Move everyone@ aces down the acl
In reply to Andreas GruenbacherThe POSIX standard puts processes which are not the owner or a member in the owning group or which match any ace other then everyone@ on the other file class. We only know if a process is in the other class after processing the entire acl.
Move all everyone@ aces in the acl down in the acl so that at most a single everyone@ allow ace remains at the end. Permissions which are not explicitly allowed are implicitly denied, so everyone@ deny ace is needed.
The everyone@ aces can be moved down the acl without changing the permissions that the acl grants. This transformation simplifies the following algorithms, and eventually allows us to turn the final everyone@ allow ace into an entry for the other class.
--- fs/richacl_compat.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+)
fs/richacl_compat.c +65 -0
@@ -159,3 +159,68 @@ richace_change_mask(struct richacl_alloc *x, struct richace **ace, }return 0 ; } + +/** +* richacl_move_everyone_aces_down - move everyone@ aces to the end of the acl +* @x: acl and number of allocated entries +* +* Move all everyone aces to the end of the acl so that only a single everyone@ +* allow ace remains at the end, and update the mask fields of all aces on the +* way. The last ace of the resulting acl will be an everyone@ allow ace only +* if @acl grants any permissions to @everyone. No @everyone deny aces will +* remain. +* +* This transformation does not alter the permissions that the acl grants. +* Having at most one everyone@ allow ace at the end of the acl helps us in the +* following algorithms. +*/ +static int +richacl_move_everyone_aces_down (struct richacl_alloc *x) +{ +struct richace *ace; +unsigned int allowed =0 , denied =0 ; + +richacl_for_each_entry (ace, x->acl) { +if (richace_is_inherit_only (ace)) +continue ; +if (richace_is_everyone (ace)) { +if (richace_is_allow (ace)) + allowed |= (ace->e_mask & ~denied); +else if (richace_is_deny (ace)) + denied |= (ace->e_mask & ~allowed); +else +continue ; +if (richace_change_mask (x, &ace,0 )) +return -1 ; + }else { +if (richace_is_allow (ace)) { +if (richace_change_mask (x, &ace, allowed | + (ace->e_mask & ~denied))) +return -1 ; + }else if (richace_is_deny (ace)) { +if (richace_change_mask (x, &ace, denied | + (ace->e_mask & ~allowed))) +return -1 ; + } + } + } +if (allowed & ~RICHACE_POSIX_ALWAYS_ALLOWED ) { +struct richace *last_ace = ace -1 ; + +if (x->acl->a_entries && +richace_is_everyone (last_ace) && +richace_is_allow (last_ace) && +richace_is_inherit_only (last_ace) && + last_ace->e_mask == allowed) + last_ace->e_flags &= ~RICHACE_INHERIT_ONLY_ACE ; +else { +if (richacl_insert_entry (x, &ace)) +return -1 ; + ace->e_type =RICHACE_ACCESS_ALLOWED_ACE_TYPE ; + ace->e_flags =RICHACE_SPECIAL_WHO ; + ace->e_mask = allowed; + ace->e_id.special =RICHACE_EVERYONE_SPECIAL_ID ; + } + } +return 0 ; +}
Signature · 1 line
2.1.0
Re: [RFC v3 26/45] richacl: Move everyone@ aces down the acl
Looks OK.
Quoted 9 lines · Andreas Gruenbacher
The POSIX standard puts processes which are not the owner or a member in the owning group or which match any ace other then everyone@ on the other file class. We only know if a process is in the other class after processing the entire acl.
Move all everyone@ aces in the acl down in the acl so that at most a single everyone@ allow ace remains at the end. Permissions which are not explicitly allowed are implicitly denied, so everyone@ deny ace is needed.
Should be "an everyone@ deny ace is unneeded".
Quoted 22 lines
The everyone@ aces can be moved down the acl without changing the permissions that the acl grants. This transformation simplifies the following algorithms, and eventually allows us to turn the final everyone@ allow ace into an entry for the other class.
Signed-off-by Andreas Gruenbacher <[email protected]>--- fs/richacl_compat.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+)fs/richacl_compat.c +4
-0@@ -159,3 +159,68 @@ richace_change_mask(struct richacl_alloc *x, struct richace **ace, }return 0 ; } + +/** +* richacl_move_everyone_aces_down - move everyone@ aces to the end of the acl +* @x: acl and number of allocated entries
fs/richacl_compat.c richace_change_mask(struct richacl_alloc *x, struct richace **ace, · line 175
Quoted 10 lines
fs/richacl_compat.c +10
-0+* +* Move all everyone aces to the end of the acl so that only a single everyone@ +* allow ace remains at the end, and update the mask fields of all aces on the +* way. The last ace of the resulting acl will be an everyone@ allow ace only +* if @acl grants any permissions to @everyone. No @everyone deny aces will +* remain. +* +* This transformation does not alter the permissions that the acl grants. +* Having at most one everyone@ allow ace at the end of the acl helps us in the +* following algorithms.
May be worth documenting the return convention (0 == success, -1 == out of memory?), though I guess it's the same for everything here.
Looks fine otherwise.
--b.
Quoted 58 lines
fs/richacl_compat.c +51
-0+*/ +static int +richacl_move_everyone_aces_down (struct richacl_alloc *x) +{ +struct richace *ace; +unsigned int allowed =0 , denied =0 ; + +richacl_for_each_entry (ace, x->acl) { +if (richace_is_inherit_only (ace)) +continue ; +if (richace_is_everyone (ace)) { +if (richace_is_allow (ace)) + allowed |= (ace->e_mask & ~denied); +else if (richace_is_deny (ace)) + denied |= (ace->e_mask & ~allowed); +else +continue ; +if (richace_change_mask (x, &ace,0 )) +return -1 ; + }else { +if (richace_is_allow (ace)) { +if (richace_change_mask (x, &ace, allowed | + (ace->e_mask & ~denied))) +return -1 ; + }else if (richace_is_deny (ace)) { +if (richace_change_mask (x, &ace, denied | + (ace->e_mask & ~allowed))) +return -1 ; + } + } + } +if (allowed & ~RICHACE_POSIX_ALWAYS_ALLOWED ) { +struct richace *last_ace = ace -1 ; + +if (x->acl->a_entries && +richace_is_everyone (last_ace) && +richace_is_allow (last_ace) && +richace_is_inherit_only (last_ace) && + last_ace->e_mask == allowed) + last_ace->e_flags &= ~RICHACE_INHERIT_ONLY_ACE ; +else { +if (richacl_insert_entry (x, &ace)) +return -1 ; + ace->e_type =RICHACE_ACCESS_ALLOWED_ACE_TYPE ; + ace->e_flags =RICHACE_SPECIAL_WHO ; + ace->e_mask = allowed; + ace->e_id.special =RICHACE_EVERYONE_SPECIAL_ID ; + } + } +return 0 ; +}Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC v3 27/45 richacl: Propagate everyone@ permissions to other aces
In reply to Andreas GruenbacherThe trailing everyone@ allow ace can grant permissions to all file classes including the owner and group class. Before we can apply the other mask to this entry to turn it into an "other class" entry, we need to ensure that members of the owner or group class will not lose any permissions from that ace.
Conceptually, we do this by inserting additional <who>:<allow>::allow entries before the trailing everyone@ allow ace with the same permissions as the trailing everyone@ allow ace for owner@, group@, and all explicitly mentioned users and groups. (In practice, we will rarely need to insert any additional aces in this step.)
--- fs/richacl_compat.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+)
fs/richacl_compat.c +190 -0
@@ -224,3 +224,193 @@ richacl_move_everyone_aces_down(struct richacl_alloc *x) }return 0 ; } + +/** +* __richacl_propagate_everyone - propagate everyone@ permissions up for @who +* @x: acl and number of allocated entries +* @who: identifier to propagate permissions for +* @allow: permissions to propagate up +* +* Propagate the permissions in @allow up from the end of the acl to the start +* for the specified principal @who. +* +* The simplest possible approach to achieve this would be to insert a +* "<who>:<allow>::allow" ace before the final everyone@ allow ace. Since this +* would often result in aces which are not needed or which could be merged +* with an existing ace, we make the following optimizations: +* +* - We go through the acl and determine which permissions are already +* allowed or denied to @who, and we remove those permissions from +* @allow. +* +* - If the acl contains an allow ace for @who and no aces after this entry +* deny permissions in @allow, we add the permissions in @allow to this +* ace. (Propagating permissions across a deny ace which can match the +* process can elevate permissions.) +* +* This transformation does not alter the permissions that the acl grants. +*/ +static int +__richacl_propagate_everyone (struct richacl_alloc *x,struct richace *who, +unsigned int allow) +{ +struct richace *allow_last =NULL , *ace; + +/* +* Remove the permissions from allow that are already determined for +* this who value, and figure out if there is an allow entry for +* this who value that is "reachable" from the trailing everyone@ +* allow ace. +*/ +richacl_for_each_entry (ace, x->acl) { +if (richace_is_inherit_only (ace)) +continue ; +if (richace_is_allow (ace)) { +if (richace_is_same_identifier (ace, who)) { + allow &= ~ace->e_mask; + allow_last = ace; + } + }else if (richace_is_deny (ace)) { +if (richace_is_same_identifier (ace, who)) + allow &= ~ace->e_mask; +else if (allow & ace->e_mask) + allow_last =NULL ; + } + } +if (allow) { +if (allow_last) +return richace_change_mask (x, &allow_last, + allow_last->e_mask | allow); +else { +struct richace who_copy; + + ace = x->acl->a_entries + x->acl->a_count -1 ; +memcpy (&who_copy, who,sizeof (struct richace )); +if (richacl_insert_entry (x, &ace)) +return -1 ; +memcpy (ace, &who_copy,sizeof (struct richace )); + ace->e_type =RICHACE_ACCESS_ALLOWED_ACE_TYPE ; +richace_clear_inheritance_flags (ace); + ace->e_mask = allow; + } + } +return 0 ; +} + +/** +* richacl_propagate_everyone - propagate everyone@ permissions up the acl +* @x: acl and number of allocated entries +* +* Make sure that owner@, group@, and all other users and groups mentioned in +* the acl will not lose any permissions when finally applying the other mask +* to the everyone@ allow ace at the end of the acl. If one of those +* principals is not granted some of those permissions, insert an additional +* allow ace for that principal into the acl before the final everyone@ allow +* ace. +* +* For example, the following acl implicitly grants everyone rwx access: +* +* joe:r::allow +* everyone@:rwx::allow +* +* When applying mode 0660 to this acl, owner@ and group@ would lose rw access, +* and joe would lose w access even though the mode does not exclude those +* permissions. To fix this problem, we insert additional allow aces into the +* acl. The result after applying mode 0660 is: +* +* joe:rw::allow +* owner@:rw::allow +* group@:rw::allow +* +* Deny aces complicate the matter. For example, the following acl grants +* everyone but joe write access: +* +* joe:w::deny +* everyone@:rwx::allow +* +* When applying mode 0660 to this acl, owner@ and group@ would lose rw access, +* and joe would lose r access. The result after inserting additional allow +* aces and applying mode 0660 is: +* +* joe:w::deny +* owner@:rw::allow +* group@:rw::allow +* joe:r::allow +* +* Inserting the additional aces does not alter the permissions that the acl +* grants. +*/ +static int +richacl_propagate_everyone (struct richacl_alloc *x) +{ +struct richace who = { .e_flags =RICHACE_SPECIAL_WHO }; +struct richacl *acl = x->acl; +struct richace *ace; +unsigned int owner_allow, group_allow; + +/* +* If the owner mask contains permissions which are not in +* the group mask, the group mask contains permissions which +* are not in the other mask, or the owner class contains +* permissions which are not in the other mask, we may need +* to propagate permissions up from the everyone@ allow ace. +* The third condition is implied by the first two. +*/ +if (!((acl->a_owner_mask & ~acl->a_group_mask) || + (acl->a_group_mask & ~acl->a_other_mask))) +return 0 ; +if (!acl->a_count) +return 0 ; + ace = acl->a_entries + acl->a_count -1 ; +if (richace_is_inherit_only (ace) || !richace_is_everyone (ace)) +return 0 ; +if (!(ace->e_mask & ~(acl->a_group_mask & acl->a_other_mask))) +/* +* None of the allowed permissions will get masked. +*/ +return 0 ; + owner_allow = (ace->e_mask & acl->a_owner_mask) | +RICHACE_POSIX_OWNER_ALLOWED ; + group_allow = ace->e_mask & acl->a_group_mask; + +/* Propagate everyone@ permissions through to owner@. */ +if (owner_allow & ~(acl->a_group_mask & acl->a_other_mask)) { + who.e_id.special =RICHACE_OWNER_SPECIAL_ID ; +if (__richacl_propagate_everyone (x, &who, owner_allow)) +return -1 ; + acl = x->acl; + } + +if (group_allow & ~acl->a_other_mask) { +int n; + +/* Propagate everyone@ permissions through to group@. */ + who.e_id.special =RICHACE_GROUP_SPECIAL_ID ; +if (__richacl_propagate_everyone (x, &who, group_allow)) +return -1 ; + acl = x->acl; + +/* +* Start from the entry before the trailing everyone@ allow +* entry. We will not hit everyone@ entries in the loop. +*/ +for (n = acl->a_count -2 ; n !=-1 ; n--) { + ace = acl->a_entries + n; + +if (richace_is_inherit_only (ace) || +richace_is_owner (ace) || +richace_is_group (ace)) +continue ; +if (richace_is_allow (ace) ||richace_is_deny (ace)) { +/* +* Any inserted entry will end up below the +* current entry +*/ +if (__richacl_propagate_everyone (x, ace, + group_allow)) +return -1 ; + } + } + } +return 0 ; +}
Signature · 1 line
2.1.0
RFC v3 28/45 richacl: Isolate the owner and group classes
In reply to Andreas GruenbacherWhen applying the file masks to an acl, we need to ensure that no process gets more permissions than allowed by its file mask.
This may require inserting an owner@ deny ace to ensure this if the owner mask contains fewer permissions than the group or other mask. For example, when applying mode 0466 to the following acl:
everyone@:rw::allow
A deny ace needs to be inserted so that the owner won't get elevated write access:
owner@:w::deny
everyone@:rw::allow
Likewise, we may need to insert group class deny aces if the group mask contains fewer permissions than the other mask. For example, when applying mode 0646 to the following acl:
owner@:rw::allow
everyone@:rw::allow
A deny ace needs to be inserted so that the owning group won't get elevated write access:
owner@:rw::allow
group@:w::deny
everyone@:rw::allow
--- fs/richacl_compat.c | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+)
fs/richacl_compat.c +233 -0
@@ -414,3 +414,236 @@ richacl_propagate_everyone(struct richacl_alloc *x) }return 0 ; } + +/** +* richacl_max_allowed - maximum permissions that anybody is allowed +*/ +static unsigned int +richacl_max_allowed (struct richacl *acl) +{ +struct richace *ace; +unsigned int allowed =0 ; + +richacl_for_each_entry_reverse (ace, acl) { +if (richace_is_inherit_only (ace)) +continue ; +if (richace_is_allow (ace)) + allowed |= ace->e_mask; +else if (richace_is_deny (ace)) { +if (richace_is_everyone (ace)) + allowed &= ~ace->e_mask; + } + } +return allowed; +} + +/** +* richacl_isolate_owner_class - limit the owner class to the owner file mask +* @x: acl and number of allocated entries +* +* POSIX requires that after a chmod, the owner class is granted no more +* permissions than the owner file permission bits. For richacls, this +* means that the owner class must not be granted any permissions that the +* owner mask does not include. +* +* When we apply file masks to an acl which grant more permissions to the group +* or other class than to the owner class, we may end up in a situation where +* the owner is granted additional permissions from other aces. For example, +* given this acl: +* +* everyone:rwx::allow +* +* when file masks corresponding to mode 0466 are applied, after +* richacl_propagate_everyone() and __richacl_apply_masks(), we end up with: +* +* owner@:r::allow +* everyone@:rw::allow +* +* This acl still grants the owner rw access through the everyone@ allow ace. +* To fix this, we must deny the owner w access: +* +* owner@:w::deny +* owner@:r::allow +* everyone@:rw::allow +*/ +static int +richacl_isolate_owner_class (struct richacl_alloc *x) +{ +struct richace *ace; +unsigned int allowed =0 ; + + allowed =richacl_max_allowed (x->acl); +if (allowed & ~x->acl->a_owner_mask) { +/* +* Figure out if we can update an existig OWNER@ DENY entry. +*/ +richacl_for_each_entry (ace, x->acl) { +if (richace_is_inherit_only (ace)) +continue ; +if (richace_is_deny (ace)) { +if (richace_is_owner (ace)) +break ; + }else if (richace_is_allow (ace)) { + ace = x->acl->a_entries + x->acl->a_count; +break ; + } + } +if (ace != x->acl->a_entries + x->acl->a_count) { +if (richace_change_mask (x, &ace, ace->e_mask | + (allowed & ~x->acl->a_owner_mask))) +return -1 ; + }else { +/* Insert an owner@ deny entry at the front. */ + ace = x->acl->a_entries; +if (richacl_insert_entry (x, &ace)) +return -1 ; + ace->e_type =RICHACE_ACCESS_DENIED_ACE_TYPE ; + ace->e_flags =RICHACE_SPECIAL_WHO ; + ace->e_mask = allowed & ~x->acl->a_owner_mask; + ace->e_id.special =RICHACE_OWNER_SPECIAL_ID ; + } + } +return 0 ; +} + +/** +* __richacl_isolate_who - isolate entry from everyone@ allow entry +* @x: acl and number of allocated entries +* @who: identifier to isolate +* @deny: permissions this identifier should not be allowed +* +* See richacl_isolate_group_class(). +*/ +static int +__richacl_isolate_who (struct richacl_alloc *x,struct richace *who, +unsigned int deny) +{ +struct richace *ace; +unsigned int n; +/* +* Compute the permissions already denied to @who. +*/ +richacl_for_each_entry (ace, x->acl) { +if (richace_is_inherit_only (ace)) +continue ; +if (richace_is_same_identifier (ace, who) && +richace_is_deny (ace)) + deny &= ~ace->e_mask; + } +if (!deny) +return 0 ; + +/* +* Figure out if we can update an existig deny entry. Start from the +* entry before the trailing everyone@ allow entry. We will not hit +* everyone@ entries in the loop. +*/ +for (n = x->acl->a_count -2 ; n !=-1 ; n--) { + ace = x->acl->a_entries + n; +if (richace_is_inherit_only (ace)) +continue ; +if (richace_is_deny (ace)) { +if (richace_is_same_identifier (ace, who)) +break ; + }else if (richace_is_allow (ace) && + (ace->e_mask & deny)) { + n =-1 ; +break ; + } + } +if (n !=-1 ) { +if (richace_change_mask (x, &ace, ace->e_mask | deny)) +return -1 ; + }else { +/* +* Insert a new entry before the trailing everyone@ deny entry. +*/ +struct richace who_copy; + + ace = x->acl->a_entries + x->acl->a_count -1 ; +memcpy (&who_copy, who,sizeof (struct richace )); +if (richacl_insert_entry (x, &ace)) +return -1 ; +memcpy (ace, &who_copy,sizeof (struct richace )); + ace->e_type =RICHACE_ACCESS_DENIED_ACE_TYPE ; +richace_clear_inheritance_flags (ace); + ace->e_mask = deny; + } +return 0 ; +} + +/** +* richacl_isolate_group_class - limit the group class to the group file mask +* @x: acl and number of allocated entries +* +* POSIX requires that after a chmod, the group class is granted no more +* permissions than the group file permission bits. For richacls, this +* means that the group class must not be granted any permissions that the +* group mask does not include. +* +* When we apply file masks to an acl which grant more permissions to the other +* class than to the group class, we may end up in a situation where processes +* in the group class are granted additional permission from other aces. For +* example, given this acl: +* +* joe:rwx::allow +* everyone:rwx::allow +* +* when file masks corresponding to mode 0646 are applied, after +* richacl_propagate_everyone() and __richacl_apply_masks(), we end up with: +* +* joe:r::allow +* owner@:rw::allow +* group@:r::allow +* everyone@:rw::allow +* +* This acl still grants joe and group@ rw access through the everyone@ allow +* ace. To fix this, we must deny w access to group class aces before the +* everyone@ allow ace at the end of the acl: +* +* joe:r::allow +* owner@:rw::allow +* group@:r::allow +* joe:w::deny +* group@:w::deny +* everyone@:rw::allow +*/ +static int +richacl_isolate_group_class (struct richacl_alloc *x) +{ +struct richace who = { + .e_flags =RICHACE_SPECIAL_WHO , + .e_id.special =RICHACE_GROUP_SPECIAL_ID , + }; +struct richace *ace; +unsigned int deny; + +if (!x->acl->a_count) +return 0 ; + ace = x->acl->a_entries + x->acl->a_count -1 ; +if (richace_is_inherit_only (ace) || !richace_is_everyone (ace)) +return 0 ; + deny = ace->e_mask & ~x->acl->a_group_mask; + +if (deny) { +unsigned int n; + +if (__richacl_isolate_who (x, &who, deny)) +return -1 ; +/* +* Start from the entry before the trailing everyone@ allow +* entry. We will not hit everyone@ entries in the loop. +*/ +for (n = x->acl->a_count -2 ; n !=-1 ; n--) { + ace = x->acl->a_entries + n; + +if (richace_is_inherit_only (ace) || +richace_is_owner (ace) || +richace_is_group (ace)) +continue ; +if (__richacl_isolate_who (x, ace, deny)) +return -1 ; + } + } +return 0 ; +}
Signature · 1 line
2.1.0
RFC v3 29/45 richacl: Apply the file masks to a richacl
In reply to Andreas GruenbacherPut all the pieces of the acl transformation puzzle together for computing a richacl which has the file masks "applied" so that the standard nfsv4 access check algorithm can be used on the richacl.
--- fs/richacl_compat.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/richacl.h | 3 ++ 2 files changed, 106 insertions(+)
fs/richacl_compat.c +103 -0
@@ -647,3 +647,106 @@ richacl_isolate_group_class(struct richacl_alloc *x) }return 0 ; } + +/** +* __richacl_apply_masks - apply the file masks to all aces +* @x: acl and number of allocated entries +* +* Apply the owner mask to owner@ aces, the other mask to +* everyone@ aces, and the group mask to all other aces. +* +* The previous transformations have brought the acl into a +* form in which applying the masks will not lead to the +* accidental loss of permissions anymore. +*/ +static int +__richacl_apply_masks (struct richacl_alloc *x) +{ +struct richace *ace; + +richacl_for_each_entry (ace, x->acl) { +unsigned int mask; + +if (richace_is_inherit_only (ace) || !richace_is_allow (ace)) +continue ; +if (richace_is_owner (ace)) + mask = x->acl->a_owner_mask; +else if (richace_is_everyone (ace)) + mask = x->acl->a_other_mask; +else + mask = x->acl->a_group_mask; +if (richace_change_mask (x, &ace, ace->e_mask & mask)) +return -1 ; + } +return 0 ; +} + +/** +* richacl_apply_masks - apply the masks to the acl +* +* Transform @acl so that the standard NFSv4 permission check algorithm (which +* is not aware of file masks) will compute the same access decisions as the +* richacl permission check algorithm (which looks at the acl and the file +* masks). +* +* This algorithm is split into several steps: +* +* - Move everyone@ aces to the end of the acl. This simplifies the other +* transformations, and allows the everyone@ allow ace at the end of the +* acl to eventually allow permissions to the other class only. +* +* - Propagate everyone@ permissions up the acl. This transformation makes +* sure that the owner and group class aces won't lose any permissions when +* we apply the other mask to the everyone@ allow ace at the end of the acl. +* +* - Apply the file masks to all aces. +* +* - Make sure that the owner is not granted any permissions beyond the owner +* mask from group class aces or from everyone@. +* +* - Make sure that the group class is not granted any permissions from +* everyone@. +* +* The algorithm is exact except for richacls which cannot be represented as an +* acl alone: for example, given this acl: +* +* group@:rw::allow +* +* when file masks corresponding to mode 0600 are applied, the owner would only +* get rw access if he is a member of the owning group. This algorithm would +* produce an empty acl in this case. We fix this case by modifying +* richacl_permission() so that the group mask is always applied to group class +* aces. With this fix, the owner would not have any access (beyond the +* implicit permissions always granted to owners). +* +* NOTE: Depending on the acl and file masks, this algorithm can increase the +* number of aces by almost a factor of three in the worst case. This may make +* the acl too large for some purposes. +*/ +int +richacl_apply_masks (struct richacl **acl) +{ +if ((*acl)->a_flags &RICHACL_MASKED ) { +struct richacl_alloc x = { + .acl =richacl_clone (*acl,GFP_KERNEL ), + .count = (*acl)->a_count, + }; +if (!x.acl) +return -ENOMEM ; + +if (richacl_move_everyone_aces_down (&x) || +richacl_propagate_everyone (&x) || +__richacl_apply_masks (&x) || +richacl_isolate_owner_class (&x) || +richacl_isolate_group_class (&x)) { +richacl_put (x.acl); +return -ENOMEM ; + } + + x.acl->a_flags &= ~RICHACL_MASKED ; +richacl_put (*acl); + *acl = x.acl; + } +return 0 ; +} +EXPORT_SYMBOL_GPL (richacl_apply_masks);
include/linux/richacl.h +3 -0
@@ -325,4 +325,7 @@ extern int richacl_equiv_mode(const struct richacl *, mode_t *);extern int richacl_permission (struct inode *,const struct richacl *,int );extern struct richacl *richacl_create (struct inode *,struct inode *); +/* richacl_compat.c */ +extern int richacl_apply_masks (struct richacl **); +#endif /* __RICHACL_H */
Signature · 1 line
2.1.0
Re: [RFC v3 29/45] richacl: Apply the file masks to a richacl
Quoted 79 lines · Andreas Gruenbacher
Put all the pieces of the acl transformation puzzle together for computing a richacl which has the file masks "applied" so that the standard nfsv4 access check algorithm can be used on the richacl.
Signed-off-by Andreas Gruenbacher <[email protected]>--- fs/richacl_compat.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/richacl.h | 3 ++ 2 files changed, 106 insertions(+)fs/richacl_compat.c +61
-0@@ -647,3 +647,106 @@ richacl_isolate_group_class(struct richacl_alloc *x) }return 0 ; } + +/** +* __richacl_apply_masks - apply the file masks to all aces +* @x: acl and number of allocated entries +* +* Apply the owner mask to owner@ aces, the other mask to +* everyone@ aces, and the group mask to all other aces. +* +* The previous transformations have brought the acl into a +* form in which applying the masks will not lead to the +* accidental loss of permissions anymore. +*/ +static int +__richacl_apply_masks (struct richacl_alloc *x) +{ +struct richace *ace; + +richacl_for_each_entry (ace, x->acl) { +unsigned int mask; + +if (richace_is_inherit_only (ace) || !richace_is_allow (ace)) +continue ; +if (richace_is_owner (ace)) + mask = x->acl->a_owner_mask; +else if (richace_is_everyone (ace)) + mask = x->acl->a_other_mask; +else + mask = x->acl->a_group_mask; +if (richace_change_mask (x, &ace, ace->e_mask & mask)) +return -1 ; + } +return 0 ; +} + +/** +* richacl_apply_masks - apply the masks to the acl +* +* Transform @acl so that the standard NFSv4 permission check algorithm (which +* is not aware of file masks) will compute the same access decisions as the +* richacl permission check algorithm (which looks at the acl and the file +* masks). +* +* This algorithm is split into several steps: +* +* - Move everyone@ aces to the end of the acl. This simplifies the other +* transformations, and allows the everyone@ allow ace at the end of the +* acl to eventually allow permissions to the other class only. +* +* - Propagate everyone@ permissions up the acl. This transformation makes +* sure that the owner and group class aces won't lose any permissions when +* we apply the other mask to the everyone@ allow ace at the end of the acl. +* +* - Apply the file masks to all aces. +* +* - Make sure that the owner is not granted any permissions beyond the owner +* mask from group class aces or from everyone@. +* +* - Make sure that the group class is not granted any permissions from +* everyone@. +* +* The algorithm is exact except for richacls which cannot be represented as an
fs/richacl_compat.c richacl_isolate_group_class(struct richacl_alloc *x) · line 720
Quoted 10 lines
fs/richacl_compat.c +10
-0+* acl alone: for example, given this acl: +* +* group@:rw::allow +* +* when file masks corresponding to mode 0600 are applied, the owner would only +* get rw access if he is a member of the owning group. This algorithm would +* produce an empty acl in this case. We fix this case by modifying +* richacl_permission() so that the group mask is always applied to group class +* aces. With this fix, the owner would not have any access (beyond the +* implicit permissions always granted to owners).
This discussion is really confusing.
If I understand correctly, you're saying:
- this mapping is inaccurate in some cases.
- therefore we've modified the permission algorithm to fix those cases.
- therefore actually, wait, the mapping is always accurate.
Do I have this right?
Please just define the group-mask behavior to be part of the richacl permission-checking algorithm from the start, and this will be less confusing.
I don't think I'm convinced yet that the resulting algorithm really is correct. I mean: I think it probably is, I just don't understand the argument quite yet.
fs/richacl_compat.c richacl_isolate_group_class(struct richacl_alloc *x) · line 724
Quoted 4 lines
fs/richacl_compat.c +4
-0+* +* NOTE: Depending on the acl and file masks, this algorithm can increase the +* number of aces by almost a factor of three in the worst case. This may make +* the acl too large for some purposes.
If only for the sake of reviewers, it would be helpful to have some more detail here to convince us that this isn't going to be a big problem in practice.
For example, can we identify a subset of richacls which would cover most reasonable use cases and not be badly mangled by this algorithm? It would be nice to be able to document some simple best practices which if followed would guarantee that the mapped acls will be sane. (Order denies before allows? Stick to a single EVERYONE@ ace at the end? Don't chmod to modes which give more permissions to other than to the group or to the group than to the owner?).
--b.
Quoted 47 lines
fs/richacl_compat.c +28
-0+*/ +int +richacl_apply_masks (struct richacl **acl) +{ +if ((*acl)->a_flags &RICHACL_MASKED ) { +struct richacl_alloc x = { + .acl =richacl_clone (*acl,GFP_KERNEL ), + .count = (*acl)->a_count, + }; +if (!x.acl) +return -ENOMEM ; + +if (richacl_move_everyone_aces_down (&x) || +richacl_propagate_everyone (&x) || +__richacl_apply_masks (&x) || +richacl_isolate_owner_class (&x) || +richacl_isolate_group_class (&x)) { +richacl_put (x.acl); +return -ENOMEM ; + } + + x.acl->a_flags &= ~RICHACL_MASKED ; +richacl_put (*acl); + *acl = x.acl; + } +return 0 ; +} +EXPORT_SYMBOL_GPL (richacl_apply_masks);include/linux/richacl.h +3
-0@@ -325,4 +325,7 @@ extern int richacl_equiv_mode(const struct richacl *, mode_t *);extern int richacl_permission (struct inode *,const struct richacl *,int );extern struct richacl *richacl_create (struct inode *,struct inode *); +/* richacl_compat.c */ +extern int richacl_apply_masks (struct richacl **); +#endif /* __RICHACL_H */ Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC v3 30/45 richacl: Create richacl from mode values
In reply to Andreas GruenbacherCreate a richacl that corresponds to given file mode permission bits.
--- fs/richacl_compat.c | 38 ++++++++++++++++++++++++++++++++++++++ include/linux/richacl.h | 1 + 2 files changed, 39 insertions(+)
fs/richacl_compat.c +38 -0
@@ -750,3 +750,41 @@ richacl_apply_masks(struct richacl **acl)return 0 ; }EXPORT_SYMBOL_GPL (richacl_apply_masks); + +/** +* richacl_from_mode_unmasked - create an acl which corresponds to @mode +* +* The resulting acl doesn't have the RICHACL_MASKED flag set. +* +* @mode: file mode including the file type +*/ +struct richacl * +richacl_from_mode_unmasked (mode_t mode) +{ +struct richacl *acl; +struct richace *ace; + + acl =richacl_alloc (1 ,GFP_KERNEL ); +if (!acl) +return NULL ; + acl->a_flags =RICHACL_MASKED ; + acl->a_owner_mask =richacl_mode_to_mask (mode >>6 ) | +RICHACE_POSIX_OWNER_ALLOWED ; + acl->a_group_mask =richacl_mode_to_mask (mode >>3 ); + acl->a_other_mask =richacl_mode_to_mask (mode); + + ace = acl->a_entries; + ace->e_type =RICHACE_ACCESS_ALLOWED_ACE_TYPE ; + ace->e_flags =RICHACE_SPECIAL_WHO ; + ace->e_mask =RICHACE_POSIX_ALWAYS_ALLOWED | +RICHACE_POSIX_MODE_ALL | +RICHACE_POSIX_OWNER_ALLOWED ; +/* RICHACE_DELETE_CHILD is meaningless for non-directories. */ +if (!S_ISDIR (mode)) + ace->e_mask &= ~RICHACE_DELETE_CHILD ; + ace->e_id.special =RICHACE_EVERYONE_SPECIAL_ID ; + +return acl; + +} +EXPORT_SYMBOL_GPL (richacl_from_mode_unmasked);
include/linux/richacl.h +1 -0
@@ -327,5 +327,6 @@ extern struct richacl *richacl_create(struct inode *, struct inode *);/* richacl_compat.c */ extern int richacl_apply_masks (struct richacl **); +extern struct richacl *richacl_from_mode_unmasked (mode_t );#endif /* __RICHACL_H */
Signature · 1 line
2.1.0
Re: [RFC v3 30/45] richacl: Create richacl from mode values
Quoted 13 lines · Andreas Gruenbacher
Create a richacl that corresponds to given file mode permission bits.
Signed-off-by Andreas Gruenbacher <[email protected]>--- fs/richacl_compat.c | 38 ++++++++++++++++++++++++++++++++++++++ include/linux/richacl.h | 1 + 2 files changed, 39 insertions(+)fs/richacl_compat.c +0
-0
fs/richacl_compat.c richacl_apply_masks(struct richacl **acl) · line 757
Quoted 8 lines
fs/richacl_compat.c +5
-0@@ -750,3 +750,41 @@ richacl_apply_masks(struct richacl **acl)return 0 ; }EXPORT_SYMBOL_GPL (richacl_apply_masks); + +/** +* richacl_from_mode_unmasked - create an acl which corresponds to @mode +* +* The resulting acl doesn't have the RICHACL_MASKED flag set.
That seems to disagree with:
fs/richacl_compat.c richacl_apply_masks(struct richacl **acl) · line 770
Quoted 13 lines
fs/richacl_compat.c +13
-0+* +* @mode: file mode including the file type +*/ +struct richacl * +richacl_from_mode_unmasked (mode_t mode) +{ +struct richacl *acl; +struct richace *ace; + + acl =richacl_alloc (1 ,GFP_KERNEL ); +if (!acl) +return NULL ; + acl->a_flags =RICHACL_MASKED ;
this line?
--b.
Quoted 38 lines
fs/richacl_compat.c +20
-0+ acl->a_owner_mask =richacl_mode_to_mask (mode >>6 ) | +RICHACE_POSIX_OWNER_ALLOWED ; + acl->a_group_mask =richacl_mode_to_mask (mode >>3 ); + acl->a_other_mask =richacl_mode_to_mask (mode); + + ace = acl->a_entries; + ace->e_type =RICHACE_ACCESS_ALLOWED_ACE_TYPE ; + ace->e_flags =RICHACE_SPECIAL_WHO ; + ace->e_mask =RICHACE_POSIX_ALWAYS_ALLOWED | +RICHACE_POSIX_MODE_ALL | +RICHACE_POSIX_OWNER_ALLOWED ; +/* RICHACE_DELETE_CHILD is meaningless for non-directories. */ +if (!S_ISDIR (mode)) + ace->e_mask &= ~RICHACE_DELETE_CHILD ; + ace->e_id.special =RICHACE_EVERYONE_SPECIAL_ID ; + +return acl; + +} +EXPORT_SYMBOL_GPL (richacl_from_mode_unmasked);include/linux/richacl.h +1
-0@@ -327,5 +327,6 @@ extern struct richacl *richacl_create(struct inode *, struct inode *);/* richacl_compat.c */ extern int richacl_apply_masks (struct richacl **); +extern struct richacl *richacl_from_mode_unmasked (mode_t );#endif /* __RICHACL_H */ Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 30/45] richacl: Create richacl from mode values
Quoted 25 lines · bfields
Quoted 13 lines · Andreas Gruenbacher
Create a richacl that corresponds to given file mode permission bits.
Signed-off-by Andreas Gruenbacher <[email protected]>--- fs/richacl_compat.c | 38 ++++++++++++++++++++++++++++++++++++++ include/linux/richacl.h | 1 + 2 files changed, 39 insertions(+)fs/richacl_compat.c +0
-0fs/richacl_compat.c richacl_apply_masks(struct richacl **acl) · line 757
Quoted 8 lines
fs/richacl_compat.c +5
-0@@ -750,3 +750,41 @@ richacl_apply_masks(struct richacl **acl)return 0 ; }EXPORT_SYMBOL_GPL (richacl_apply_masks); + +/** +* richacl_from_mode_unmasked - create an acl which corresponds to @mode +* +* The resulting acl doesn't have the RICHACL_MASKED flag set. That seems to disagree with:
Quoted 15 lines
fs/richacl_compat.c richacl_apply_masks(struct richacl **acl) · line 770
Quoted 13 lines
fs/richacl_compat.c +13
-0+* +* @mode: file mode including the file type +*/ +struct richacl * +richacl_from_mode_unmasked (mode_t mode) +{ +struct richacl *acl; +struct richace *ace; + + acl =richacl_alloc (1 ,GFP_KERNEL ); +if (!acl) +return NULL ; + acl->a_flags =RICHACL_MASKED ;this line?
OK, looks like that's a temporary issue, fixed by the next patch. Might be simpler just to squash the two patches and skip this step?
--b.
Quoted 41 lines
--b.
Quoted 38 lines
fs/richacl_compat.c +20
-0+ acl->a_owner_mask =richacl_mode_to_mask (mode >>6 ) | +RICHACE_POSIX_OWNER_ALLOWED ; + acl->a_group_mask =richacl_mode_to_mask (mode >>3 ); + acl->a_other_mask =richacl_mode_to_mask (mode); + + ace = acl->a_entries; + ace->e_type =RICHACE_ACCESS_ALLOWED_ACE_TYPE ; + ace->e_flags =RICHACE_SPECIAL_WHO ; + ace->e_mask =RICHACE_POSIX_ALWAYS_ALLOWED | +RICHACE_POSIX_MODE_ALL | +RICHACE_POSIX_OWNER_ALLOWED ; +/* RICHACE_DELETE_CHILD is meaningless for non-directories. */ +if (!S_ISDIR (mode)) + ace->e_mask &= ~RICHACE_DELETE_CHILD ; + ace->e_id.special =RICHACE_EVERYONE_SPECIAL_ID ; + +return acl; + +} +EXPORT_SYMBOL_GPL (richacl_from_mode_unmasked);include/linux/richacl.h +1
-0@@ -327,5 +327,6 @@ extern struct richacl *richacl_create(struct inode *, struct inode *);/* richacl_compat.c */ extern int richacl_apply_masks (struct richacl **); +extern struct richacl *richacl_from_mode_unmasked (mode_t );#endif /* __RICHACL_H */ Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 30/45] richacl: Create richacl from mode values
2015-05-28 21:25 GMT+02:00 J. Bruce Fields <[email protected]>:
Quoted 2 lines
OK, looks like that's a temporary issue, fixed by the next patch. Might be simpler just to squash the two patches and skip this step?
Yes, sorry for the mistake. I've already squashed these two commits.
Thanks,
Andreas
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC v3 31/45 richacl: Create acl with masks applied in richacl_from_mode()
In reply to Andreas GruenbacherWhen creating a richacl that represents a particular file mode, one approach is to create one everyone@ allow entry and set the richacl masks according to the file mode; when those masks are applied with richacl_apply_masks(), that everyone@ allow entry gets transformed as needed. The other approach is to compute the result after richacl_apply_masks() directly; this commit does that.
--- fs/richacl_compat.c | 85 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 17 deletions(-)
fs/richacl_compat.c +68 -17
@@ -761,30 +761,81 @@ EXPORT_SYMBOL_GPL(richacl_apply_masks);struct richacl *richacl_from_mode_unmasked (mode_t mode) { +unsigned int owner_mask =richacl_mode_to_mask (mode >>6 ) | +RICHACE_POSIX_OWNER_ALLOWED ; +unsigned int group_mask =richacl_mode_to_mask (mode >>3 ); +unsigned int other_mask =richacl_mode_to_mask (mode); +unsigned int denied; +unsigned int entries =0 ;struct richacl *acl;struct richace *ace; - acl =richacl_alloc (1 ,GFP_KERNEL ); +/* RICHACE_DELETE_CHILD is meaningless for non-directories. */ +if (!S_ISDIR (mode)) { + owner_mask &= ~RICHACE_DELETE_CHILD ; + group_mask &= ~RICHACE_DELETE_CHILD ; + other_mask &= ~RICHACE_DELETE_CHILD ; + } + +if (owner_mask & ~(group_mask & other_mask)) + entries++;/* OWNER@ ALLOW entry needed */ + denied = ~owner_mask & (group_mask | other_mask); +if (denied) + entries++;/* OWNER@ DENY entry needed */ +if (group_mask & ~other_mask) + entries++;/* GROUP@ ALLOW entry needed */ + denied = ~group_mask & other_mask; +if (denied) + entries++;/* GROUP@ DENY entry needed */ +if (other_mask) + entries++;/* EVERYONE@ ALLOW entry needed */ + + acl =richacl_alloc (entries,GFP_KERNEL );if (!acl)return NULL ; - acl->a_flags =RICHACL_MASKED ; - acl->a_owner_mask =richacl_mode_to_mask (mode >>6 ) | -RICHACE_POSIX_OWNER_ALLOWED ; - acl->a_group_mask =richacl_mode_to_mask (mode >>3 ); - acl->a_other_mask =richacl_mode_to_mask (mode); - + acl->a_owner_mask = owner_mask; + acl->a_group_mask = group_mask; + acl->a_other_mask = other_mask; ace = acl->a_entries; - ace->e_type =RICHACE_ACCESS_ALLOWED_ACE_TYPE ; - ace->e_flags =RICHACE_SPECIAL_WHO ; - ace->e_mask =RICHACE_POSIX_ALWAYS_ALLOWED | -RICHACE_POSIX_MODE_ALL | -RICHACE_POSIX_OWNER_ALLOWED ; -/* RICHACE_DELETE_CHILD is meaningless for non-directories. */ -if (!S_ISDIR (mode)) - ace->e_mask &= ~RICHACE_DELETE_CHILD ; - ace->e_id.special =RICHACE_EVERYONE_SPECIAL_ID ; -return acl; +if (owner_mask & ~(group_mask & other_mask)) { + ace->e_type =RICHACE_ACCESS_ALLOWED_ACE_TYPE ; + ace->e_flags =RICHACE_SPECIAL_WHO ; + ace->e_mask = owner_mask; + ace->e_id.special =RICHACE_OWNER_SPECIAL_ID ; + ace++; + } + denied = ~owner_mask & (group_mask | other_mask); +if (denied) { + ace->e_type =RICHACE_ACCESS_DENIED_ACE_TYPE ; + ace->e_flags =RICHACE_SPECIAL_WHO ; + ace->e_mask = denied; + ace->e_id.special =RICHACE_OWNER_SPECIAL_ID ; + ace++; + } +if (group_mask & ~other_mask) { + ace->e_type =RICHACE_ACCESS_ALLOWED_ACE_TYPE ; + ace->e_flags =RICHACE_SPECIAL_WHO ; + ace->e_mask = group_mask; + ace->e_id.special =RICHACE_GROUP_SPECIAL_ID ; + ace++; + } + denied = ~group_mask & other_mask; +if (denied) { + ace->e_type =RICHACE_ACCESS_DENIED_ACE_TYPE ; + ace->e_flags =RICHACE_SPECIAL_WHO ; + ace->e_mask = denied; + ace->e_id.special =RICHACE_GROUP_SPECIAL_ID ; + ace++; + } +if (other_mask) { + ace->e_type =RICHACE_ACCESS_ALLOWED_ACE_TYPE ; + ace->e_flags =RICHACE_SPECIAL_WHO ; + ace->e_mask = other_mask; + ace->e_id.special =RICHACE_EVERYONE_SPECIAL_ID ; + ace++; + } +return acl; }EXPORT_SYMBOL_GPL (richacl_from_mode_unmasked);
Signature · 1 line
2.1.0
RFC v3 32/45 nfsd: Remove dead declarations
In reply to Andreas Gruenbacher--- fs/nfsd/nfs4acl.c | 12 ------------ 1 file changed, 12 deletions(-)
fs/nfsd/nfs4acl.c +0 -12
@@ -52,10 +52,6 @@#define NFS4_ANYONE_MODE (NFS4_ACE_READ_ATTRIBUTES | NFS4_ACE_READ_ACL | NFS4_ACE_SYNCHRONIZE)#define NFS4_OWNER_MODE (NFS4_ACE_WRITE_ATTRIBUTES | NFS4_ACE_WRITE_ACL) -/* We don't support these bits; insist they be neither allowed nor denied */ -#define NFS4_MASK_UNSUPP (NFS4_ACE_DELETE | NFS4_ACE_WRITE_OWNER \ - | NFS4_ACE_READ_NAMED_ATTRS | NFS4_ACE_WRITE_NAMED_ATTRS) -/* flags used to simulate posix default ACLs */ #define NFS4_INHERITANCE_FLAGS (NFS4_ACE_FILE_INHERIT_ACE \ | NFS4_ACE_DIRECTORY_INHERIT_ACE)
@@ -64,9 +60,6 @@ | NFS4_ACE_INHERIT_ONLY_ACE \ | NFS4_ACE_IDENTIFIER_GROUP) -#define MASK_EQUAL (mask1, mask2) \ - ( ((mask1) & NFS4_ACE_MASK_ALL) == ((mask2) & NFS4_ACE_MASK_ALL) ) -static u32 mask_from_posix (unsigned short perm,unsigned int flags) {
@@ -126,11 +119,6 @@ low_mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags) *mode |=ACL_EXECUTE ; } -struct ace_container { -struct nfs4_ace *ace; -struct list_head ace_l; -}; -static short ace2type (struct nfs4_ace *);static void _posix_to_nfsv4_one (struct posix_acl *,struct nfs4_acl *,unsigned int );
Signature · 1 line
2.1.0
RFC v3 33/45 nfsd: Keep list of acls to dispose of in compoundargs
In reply to Andreas GruenbacherWe will decode acls in requests into richacls; those need to be richacl_put() at the end of the request instead of kfree()d; this allows the vfs to cache them whenever possible.
NOTE: If we allow only a single acl per request, we can get rid of the list here.
--- fs/nfsd/nfs4xdr.c | 26 ++++++++++++++++++++++++++ fs/nfsd/xdr4.h | 6 ++++++ 2 files changed, 32 insertions(+)
fs/nfsd/nfs4xdr.c +26 -0
@@ -39,6 +39,7 @@#include <linux/utsname.h> #include <linux/pagemap.h> #include <linux/sunrpc/svcauth_gss.h> +#include <linux/richacl.h> #include "idmap.h" #include "acl.h"
@@ -195,6 +196,24 @@ svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, u32 len)return tb->buf; } +static struct richacl * +svcxdr_alloc_richacl (struct nfsd4_compoundargs *argp,u32 nace) +{ +struct svcxdr_richacl *acls; + + acls =kmalloc (sizeof (*acls),GFP_KERNEL ); +if (!acls) +return NULL ; + acls->acl =richacl_alloc (nace,GFP_KERNEL ); +if (!acls->acl) { +kfree (acls); +return NULL ; + } + acls->next = argp->acls; + argp->acls = acls; +return acls->acl; +} +/* * For xdr strings that need to be passed to other kernel api's * as null-terminated strings.
@@ -4390,6 +4409,12 @@ int nfsd4_release_compoundargs(void *rq, __be32 *p, void *resp) args->to_free = tb->next;kfree (tb); } +while (args->acls) { +struct svcxdr_richacl *acls = args->acls; + args->acls = acls->next; +richacl_put (acls->acl); +kfree (acls); + }return 1 ; }
@@ -4408,6 +4433,7 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_comp args->pagelen = rqstp->rq_arg.page_len; args->tmpp =NULL ; args->to_free =NULL ; + args->acls =NULL ; args->ops = args->iops; args->rqstp = rqstp;
fs/nfsd/xdr4.h +6 -0
@@ -569,6 +569,11 @@ struct svcxdr_tmpbuf {char buf[]; }; +struct svcxdr_richacl { +struct svcxdr_richacl *next; +struct richacl *acl; +}; +struct nfsd4_compoundargs {/* scratch variables for XDR decode */ __be32 * p;
@@ -578,6 +583,7 @@ struct nfsd4_compoundargs {__be32 tmp[8 ];__be32 * tmpp;struct svcxdr_tmpbuf *to_free; +struct svcxdr_richacl *acls;struct svc_rqst *rqstp;
Signature · 1 line
2.1.0
Re: [RFC v3 33/45] nfsd: Keep list of acls to dispose of in compoundargs
Quoted 6 lines · Andreas Gruenbacher
We will decode acls in requests into richacls; those need to be
richacl_put()at the end of the request instead of kfree()d; this allows the vfs to cache them whenever possible.NOTE: If we allow only a single acl per request, we can get rid of the list here.
Technically I guess we're supposed to allow it, and maybe someday someone will come up with a reason.... So I'm not inclined to drop the list.
--b.
Quoted 96 lines
Signed-off-by Andreas Gruenbacher <[email protected]>--- fs/nfsd/nfs4xdr.c | 26 ++++++++++++++++++++++++++ fs/nfsd/xdr4.h | 6 ++++++ 2 files changed, 32 insertions(+)fs/nfsd/nfs4xdr.c +26
-0@@ -39,6 +39,7 @@#include <linux/utsname.h> #include <linux/pagemap.h> #include <linux/sunrpc/svcauth_gss.h> +#include <linux/richacl.h> #include "idmap.h" #include "acl.h" @@ -195,6 +196,24 @@ svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, u32 len)return tb->buf; } +static struct richacl * +svcxdr_alloc_richacl (struct nfsd4_compoundargs *argp,u32 nace) +{ +struct svcxdr_richacl *acls; + + acls =kmalloc (sizeof (*acls),GFP_KERNEL ); +if (!acls) +return NULL ; + acls->acl =richacl_alloc (nace,GFP_KERNEL ); +if (!acls->acl) { +kfree (acls); +return NULL ; + } + acls->next = argp->acls; + argp->acls = acls; +return acls->acl; +} +/* * For xdr strings that need to be passed to other kernel api's * as null-terminated strings. @@ -4390,6 +4409,12 @@ int nfsd4_release_compoundargs(void *rq, __be32 *p, void *resp) args->to_free = tb->next;kfree (tb); } +while (args->acls) { +struct svcxdr_richacl *acls = args->acls; + args->acls = acls->next; +richacl_put (acls->acl); +kfree (acls); + }return 1 ; }@@ -4408,6 +4433,7 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_comp args->pagelen = rqstp->rq_arg.page_len; args->tmpp =NULL ; args->to_free =NULL ; + args->acls =NULL ; args->ops = args->iops; args->rqstp = rqstp;fs/nfsd/xdr4.h +6
-0@@ -569,6 +569,11 @@ struct svcxdr_tmpbuf {char buf[]; }; +struct svcxdr_richacl { +struct svcxdr_richacl *next; +struct richacl *acl; +}; +struct nfsd4_compoundargs {/* scratch variables for XDR decode */ __be32 * p;@@ -578,6 +583,7 @@ struct nfsd4_compoundargs {__be32 tmp[8 ];__be32 * tmpp;struct svcxdr_tmpbuf *to_free; +struct svcxdr_richacl *acls;struct svc_rqst *rqstp;Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 33/45] nfsd: Keep list of acls to dispose of in compoundargs
2015-05-28 21:52 GMT+02:00 J. Bruce Fields <[email protected]>:
Quoted 11 lines · Andreas Gruenbacher
Quoted 6 lines
We will decode acls in requests into richacls; those need to be
richacl_put()at the end of the request instead of kfree()d; this allows the vfs to cache them whenever possible.NOTE: If we allow only a single acl per request, we can get rid of the list here.
Technically I guess we're supposed to allow it, and maybe someday someone will come up with a reason.... So I'm not inclined to drop the list.
Okay, I'll update the comment accordingly.
Thanks,
Andreas
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC v3 34/45 nfsd: Use richacls as internal acl representation
In reply to Andreas GruenbacherWhen converting from NFSv4 ACLs to POSIX ACLs, nfsd so far was using struct nfs4_acl as its internal representation. This representation is a subset of richacls, so get rid of struct nfs4_acl. Richacls even have a more compact in-memory representation, so a few more ACL entries can easily be supported.
--- fs/Kconfig | 6 + fs/nfs_common/Makefile | 1 + fs/nfs_common/nfs4acl.c | 41 ++++++ fs/nfsd/Kconfig | 1 + fs/nfsd/acl.h | 24 ++-- fs/nfsd/nfs4acl.c | 372 ++++++++++++++++++++++-------------------------- fs/nfsd/nfs4proc.c | 15 +- fs/nfsd/nfs4xdr.c | 62 +++----- fs/nfsd/xdr4.h | 6 +- include/linux/nfs4.h | 23 --- include/linux/nfs4acl.h | 7 + 11 files changed, 270 insertions(+), 288 deletions(-) create mode 100644 fs/nfs_common/nfs4acl.c create mode 100644 include/linux/nfs4acl.h
fs/Kconfig +6 -0
@@ -268,6 +268,12 @@ config NFS_COMMON depends on NFSD || NFS_FS || LOCKD default y +config NFS_RICHACL + bool + depends on NFSD_V4 || NFS_V4 + select FS_RICHACL + default y + source "net/sunrpc/Kconfig" source "fs/ceph/Kconfig" source "fs/cifs/Kconfig"
fs/nfs_common/Makefile +1 -0
@@ -4,5 +4,6 @@ obj-$(CONFIG_NFS_ACL_SUPPORT) += nfs_acl.o nfs_acl-objs := nfsacl.o +obj-$(CONFIG_NFS_RICHACL) += nfs4acl.o obj-$(CONFIG_GRACE_PERIOD) += grace.o
fs/nfs_common/nfs4acl.c new file +41 -0
41 lines not on this page · raw message
fs/nfsd/Kconfig +1 -0
@@ -69,6 +69,7 @@ config NFSD_V4 depends on NFSD && PROC_FS select NFSD_V3 select FS_POSIX_ACL + select FS_RICHACL select SUNRPC_GSS select CRYPTO select GRACE_PERIOD
fs/nfsd/acl.h +13 -11
@@ -35,25 +35,27 @@#ifndef LINUX_NFS4_ACL_H #define LINUX_NFS4_ACL_H -struct nfs4_acl ; +struct richacl ; +struct richace ;struct svc_fh ;struct svc_rqst ;/* * Maximum ACL we'll accept from a client; chosen (somewhat * arbitrarily) so that kmalloc'ing the ACL shouldn't require a -* high-order allocation. This allows 204 ACEs on x86_64: +* high-order allocation. This allows 339 ACEs on x86_64: */ -#define NFS4_ACL_MAX ((PAGE_SIZE - sizeof(struct nfs4_acl)) \ - / sizeof(struct nfs4_ace)) +#define NFSD4_ACL_MAX ((PAGE_SIZE - sizeof(struct richacl)) \ + / sizeof(struct richace)) -int nfs4_acl_bytes (int entries); -int nfs4_acl_get_whotype (char *,u32 ); -__be32 nfs4_acl_write_who (struct xdr_stream *xdr,int who); +__be32 nfsd4_decode_ace_who (struct richace *ace,struct svc_rqst *rqstp, +char *who,u32 len); +__be32 nfsd4_encode_ace_who (struct xdr_stream *xdr,struct svc_rqst *rqstp, +struct richace *ace); -int nfsd4_get_nfs4_acl (struct svc_rqst *rqstp,struct dentry *dentry, -struct nfs4_acl **acl); -__be32 nfsd4_set_nfs4_acl (struct svc_rqst *rqstp,struct svc_fh *fhp, -struct nfs4_acl *acl); +int nfsd4_get_acl (struct svc_rqst *rqstp,struct dentry *dentry, +struct richacl **acl); +__be32 nfsd4_set_acl (struct svc_rqst *rqstp,struct svc_fh *fhp, +struct richacl *acl);#endif /* LINUX_NFS4_ACL_H */
fs/nfsd/nfs4acl.c +170 -202
621 lines not on this page · raw message
fs/nfsd/nfs4proc.c +7 -8
@@ -159,12 +159,12 @@ is_create_with_attrs(struct nfsd4_open *open)* in the returned attr bitmap. */ static void -do_set_nfs4_acl (struct svc_rqst *rqstp,struct svc_fh *fhp, -struct nfs4_acl *acl,u32 *bmval) +do_set_acl (struct svc_rqst *rqstp,struct svc_fh *fhp,struct richacl *acl, +u32 *bmval) {__be32 status; - status =nfsd4_set_nfs4_acl (rqstp, fhp, acl); + status =nfsd4_set_acl (rqstp, fhp, acl);if (status)/* * We should probably fail the whole open at this point,
@@ -299,7 +299,7 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru goto out;if (is_create_with_attrs (open) && open->op_acl !=NULL ) -do_set_nfs4_acl (rqstp, *resfh, open->op_acl, open->op_bmval); +do_set_acl (rqstp, *resfh, open->op_acl, open->op_bmval);nfsd4_set_open_owner_reply_cache (cstate, open, *resfh); accmode =NFSD_MAY_NOP ;
@@ -674,8 +674,7 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,nfsd4_security_inode_setsecctx (&resfh, &create->cr_label, create->cr_bmval);if (create->cr_acl !=NULL ) -do_set_nfs4_acl (rqstp, &resfh, create->cr_acl, - create->cr_bmval); +do_set_acl (rqstp, &resfh, create->cr_acl, create->cr_bmval);fh_unlock (&cstate->current_fh);set_change_info (&create->cr_cinfo, &cstate->current_fh);
@@ -942,8 +941,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out;if (setattr->sa_acl !=NULL ) - status =nfsd4_set_nfs4_acl (rqstp, &cstate->current_fh, - setattr->sa_acl); + status =nfsd4_set_acl (rqstp, &cstate->current_fh, + setattr->sa_acl);if (status) goto out;if (setattr->sa_label.len)
fs/nfsd/nfs4xdr.c +21 -41
122 lines not on this page · raw message
fs/nfsd/xdr4.h +3 -3
@@ -119,7 +119,7 @@ struct nfsd4_create {u32 cr_bmval[3 ];/* request */ struct iattr cr_iattr;/* request */ struct nfsd4_change_info cr_cinfo;/* response */ -struct nfs4_acl *cr_acl; +struct richacl *cr_acl;struct xdr_netobj cr_label; };#define cr_datalen u.link.datalen
@@ -248,7 +248,7 @@ struct nfsd4_open {struct nfs4_openowner *op_openowner;/* used during processing */ struct nfs4_file *op_file;/* used during processing */ struct nfs4_ol_stateid *op_stp;/* used during processing */ -struct nfs4_acl *op_acl; +struct richacl *op_acl;struct xdr_netobj op_label; };
@@ -331,7 +331,7 @@ struct nfsd4_setattr {stateid_t sa_stateid;/* request */ u32 sa_bmval[3 ];/* request */ struct iattr sa_iattr;/* request */ -struct nfs4_acl *sa_acl; +struct richacl *sa_acl;struct xdr_netobj sa_label; };
include/linux/nfs4.h +0 -23
@@ -16,29 +16,6 @@#include <linux/uidgid.h> #include <uapi/linux/nfs4.h> -enum nfs4_acl_whotype { -NFS4_ACL_WHO_NAMED =0 , -NFS4_ACL_WHO_OWNER , -NFS4_ACL_WHO_GROUP , -NFS4_ACL_WHO_EVERYONE , -}; - -struct nfs4_ace { -uint32_t type; -uint32_t flag; -uint32_t access_mask; -int whotype; -union { -kuid_t who_uid; -kgid_t who_gid; - }; -}; - -struct nfs4_acl { -uint32_t naces; -struct nfs4_ace aces[0 ]; -}; -#define NFS4_MAXLABELLEN 2048struct nfs4_label {
include/linux/nfs4acl.h new file +7 -0
@@ -0,0 +1,7 @@ +#ifndef __LINUX_NFS4ACL_H +#define __LINUX_NFS4ACL_H + +int nfs4acl_who_to_special_id (const char *,u32 ); +bool nfs4acl_special_id_to_who (unsigned int ,const char **,unsigned int *); + +#endif
Signature · 1 line
2.1.0
RFC v3 35/45 nfsd: Add richacl support
In reply to Andreas GruenbacherOn file systems with richacls enabled, get and set richacls directly instead of converting from / to posix acls.
--- fs/nfsd/acl.h | 3 +- fs/nfsd/nfs4acl.c | 123 +++++++++++++++++++++++++++++++++++++---------------- fs/nfsd/nfs4proc.c | 2 +- fs/nfsd/nfs4xdr.c | 34 +++++++++++---- 4 files changed, 115 insertions(+), 47 deletions(-)
fs/nfsd/acl.h +1 -2
@@ -53,8 +53,7 @@ __be32 nfsd4_decode_ace_who(struct richace *ace, struct svc_rqst *rqstp,__be32 nfsd4_encode_ace_who (struct xdr_stream *xdr,struct svc_rqst *rqstp,struct richace *ace); -int nfsd4_get_acl (struct svc_rqst *rqstp,struct dentry *dentry, -struct richacl **acl); +struct richacl *nfsd4_get_acl (struct svc_rqst *rqstp,struct dentry *dentry);__be32 nfsd4_set_acl (struct svc_rqst *rqstp,struct svc_fh *fhp,struct richacl *acl);
fs/nfsd/nfs4acl.c +87 -36
200 lines not on this page · raw message
fs/nfsd/nfs4proc.c +1 -1
@@ -110,7 +110,7 @@ check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,* in current environment or not. */ if (bmval[0 ] &FATTR4_WORD0_ACL ) { -if (!IS_POSIXACL (dentry->d_inode)) +if (!IS_ACL (dentry->d_inode))return nfserr_attrnotsupp; }
fs/nfsd/nfs4xdr.c +26 -8
@@ -339,11 +339,24 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,richacl_for_each_entry (ace, *acl) {READ_BUF (16 ); len +=16 ; - ace->e_type =be32_to_cpup (p++); - ace->e_flags =be32_to_cpup (p++); - ace->e_mask =be32_to_cpup (p++); -if (ace->e_flags &RICHACE_SPECIAL_WHO ) + + dummy32 =be32_to_cpup (p++); +if (dummy32 >RICHACE_ACCESS_DENIED_ACE_TYPE ) +return nfserr_inval; + ace->e_type = dummy32; + + dummy32 =be32_to_cpup (p++); +if (dummy32 & (~RICHACE_VALID_FLAGS | +RICHACE_INHERITED_ACE | +RICHACE_SPECIAL_WHO ))return nfserr_inval; + ace->e_flags = dummy32; + + dummy32 =be32_to_cpup (p++); +if (dummy32 & ~NFS4_ACE_MASK_ALL ) +return nfserr_inval; + ace->e_mask = dummy32; + dummy32 =be32_to_cpup (p++);READ_BUF (dummy32); len +=XDR_QUADLEN (dummy32) <<2 ;
@@ -2274,7 +2287,11 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp, fhp = tempfh; }if (bmval0 &FATTR4_WORD0_ACL ) { - err =nfsd4_get_nfs4_acl (rqstp, dentry, &acl); + acl =nfsd4_get_acl (rqstp, dentry); +if (IS_ERR (acl)) { + err =PTR_ERR (acl); + acl =NULL ; + }if (err == -EOPNOTSUPP ) bmval0 &= ~FATTR4_WORD0_ACL ;else if (err == -EINVAL ) {
@@ -2333,7 +2350,7 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,u32 word1 =nfsd_suppattrs1 (minorversion);u32 word2 =nfsd_suppattrs2 (minorversion); -if (!IS_POSIXACL (dentry->d_inode)) +if (!IS_ACL (dentry->d_inode)) word0 &= ~FATTR4_WORD0_ACL ;if (!contextsupport) word2 &= ~FATTR4_WORD2_SECURITY_LABEL ;
@@ -2468,7 +2485,8 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,if (!p) goto out_resource; *p++ =cpu_to_be32 (ace->e_type); - *p++ =cpu_to_be32 (ace->e_flags & ~RICHACE_SPECIAL_WHO ); + *p++ =cpu_to_be32 (ace->e_flags & + ~(RICHACE_SPECIAL_WHO |RICHACE_INHERITED_ACE )); *p++ =cpu_to_be32 (ace->e_mask &NFS4_ACE_MASK_ALL ); status =nfsd4_encode_ace_who (xdr, rqstp, ace);if (status)
@@ -2480,7 +2498,7 @@ out_acl: p =xdr_reserve_space (xdr,4 );if (!p) goto out_resource; - *p++ =cpu_to_be32 (IS_POSIXACL (dentry->d_inode) ? + *p++ =cpu_to_be32 (IS_ACL (dentry->d_inode) ?ACL4_SUPPORT_ALLOW_ACL |ACL4_SUPPORT_DENY_ACL :0 ); }if (bmval0 &FATTR4_WORD0_CANSETTIME ) {
Signature · 1 line
2.1.0
RFC v3 36/45 NFSv4: Fix GETATTR bitmap verification
In reply to Andreas GruenbacherThe NFSv4 client sends the server GETATTR requests with different sets of requested attributes depending on the situation. The requested set of attributes is encoded in a bitmap; the server replies with the set of attributes it could return. These bitmaps can be several words wide. The bitmap returned by the server is a subset of the bitmap sent by the client.
While decoding the reply, the client tries to verify the reply bitmap: it checks if any previous, unexpected attributes are left in the same word of the bitmap for each attribute it tries to decode, then it clears the current attribute's bit in the bitmap for the next decode function.
The client fails to detect when unexpected attributes are sent after the last expected attribute in each word in the bitmap.
Fix this by checking the entire bitmap for unexpected attributes first. The server can still send attributes which the client understands but which it didn't request; this doesn't cause any harm.
Because we don't verify the bitmap one bit at a time anymore, there is no need for clearing bitmap bits anymore.
--- fs/nfs/nfs4xdr.c | 186 +++++++++++++++++++------------------------------------ 1 file changed, 63 insertions(+), 123 deletions(-)
fs/nfs/nfs4xdr.c +63 -123
@@ -3151,7 +3151,6 @@ static int decode_attr_supported(struct xdr_stream *xdr, uint32_t *bitmap, uint3 ret =decode_attr_bitmap (xdr, bitmask);if (unlikely (ret <0 ))return ret; - bitmap[0 ] &= ~FATTR4_WORD0_SUPPORTED_ATTRS ; }else bitmask[0 ] = bitmask[1 ] = bitmask[2 ] =0 ; dprintk("%s: bitmask=%08x:%08x:%08x\n", __func__,
@@ -3165,8 +3164,6 @@ static int decode_attr_type(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *int ret =0 ; *type =0 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_TYPE -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_TYPE )) { p =xdr_inline_decode (xdr,4 );if (unlikely (!p))
@@ -3176,7 +3173,6 @@ static int decode_attr_type(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *dprintk ("%s: bad type %d\n" , __func__, *type);return -EIO ; } - bitmap[0 ] &= ~FATTR4_WORD0_TYPE ; ret =NFS_ATTR_FATTR_TYPE ; }dprintk ("%s: type=0%o\n" , __func__, nfs_type2fmt[*type]);
@@ -3192,14 +3188,11 @@ static int decode_attr_fh_expire_type(struct xdr_stream *xdr,__be32 *p; *type =0 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_FH_EXPIRE_TYPE -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_FH_EXPIRE_TYPE )) { p =xdr_inline_decode (xdr,4 );if (unlikely (!p)) goto out_overflow; *type =be32_to_cpup (p); - bitmap[0 ] &= ~FATTR4_WORD0_FH_EXPIRE_TYPE ; }dprintk ("%s: expire type=0x%x\n" , __func__, *type);return 0 ;
@@ -3214,14 +3207,11 @@ static int decode_attr_change(struct xdr_stream *xdr, uint32_t *bitmap, uint64_tint ret =0 ; *change =0 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_CHANGE -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_CHANGE )) { p =xdr_inline_decode (xdr,8 );if (unlikely (!p)) goto out_overflow;xdr_decode_hyper (p, change); - bitmap[0 ] &= ~FATTR4_WORD0_CHANGE ; ret =NFS_ATTR_FATTR_CHANGE ; } dprintk("%s: change attribute=%Lu\n", __func__,
@@ -3238,14 +3228,11 @@ static int decode_attr_size(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *int ret =0 ; *size =0 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_SIZE -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_SIZE )) { p =xdr_inline_decode (xdr,8 );if (unlikely (!p)) goto out_overflow;xdr_decode_hyper (p, size); - bitmap[0 ] &= ~FATTR4_WORD0_SIZE ; ret =NFS_ATTR_FATTR_SIZE ; }dprintk ("%s: file size=%Lu\n" , __func__, (unsigned long long )*size);
@@ -3260,14 +3247,11 @@ static int decode_attr_link_support(struct xdr_stream *xdr, uint32_t *bitmap, ui__be32 *p; *res =0 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_LINK_SUPPORT -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_LINK_SUPPORT )) { p =xdr_inline_decode (xdr,4 );if (unlikely (!p)) goto out_overflow; *res =be32_to_cpup (p); - bitmap[0 ] &= ~FATTR4_WORD0_LINK_SUPPORT ; }dprintk ("%s: link support=%s\n" , __func__, *res ==0 ?"false" :"true" );return 0 ;
@@ -3281,14 +3265,11 @@ static int decode_attr_symlink_support(struct xdr_stream *xdr, uint32_t *bitmap,__be32 *p; *res =0 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_SYMLINK_SUPPORT -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_SYMLINK_SUPPORT )) { p =xdr_inline_decode (xdr,4 );if (unlikely (!p)) goto out_overflow; *res =be32_to_cpup (p); - bitmap[0 ] &= ~FATTR4_WORD0_SYMLINK_SUPPORT ; }dprintk ("%s: symlink support=%s\n" , __func__, *res ==0 ?"false" :"true" );return 0 ;
@@ -3304,15 +3285,12 @@ static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs fsid->major =0 ; fsid->minor =0 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_FSID -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_FSID )) { p =xdr_inline_decode (xdr,16 );if (unlikely (!p)) goto out_overflow; p =xdr_decode_hyper (p, &fsid->major);xdr_decode_hyper (p, &fsid->minor); - bitmap[0 ] &= ~FATTR4_WORD0_FSID ; ret =NFS_ATTR_FATTR_FSID ; } dprintk("%s: fsid=(0x%Lx/0x%Lx)\n", __func__,
@@ -3329,14 +3307,11 @@ static int decode_attr_lease_time(struct xdr_stream *xdr, uint32_t *bitmap, uint__be32 *p; *res =60 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_LEASE_TIME -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_LEASE_TIME )) { p =xdr_inline_decode (xdr,4 );if (unlikely (!p)) goto out_overflow; *res =be32_to_cpup (p); - bitmap[0 ] &= ~FATTR4_WORD0_LEASE_TIME ; }dprintk ("%s: file size=%u\n" , __func__, (unsigned int )*res);return 0 ;
@@ -3349,13 +3324,10 @@ static int decode_attr_error(struct xdr_stream *xdr, uint32_t *bitmap, int32_t * {__be32 *p; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_RDATTR_ERROR -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_RDATTR_ERROR )) { p =xdr_inline_decode (xdr,4 );if (unlikely (!p)) goto out_overflow; - bitmap[0 ] &= ~FATTR4_WORD0_RDATTR_ERROR ; *res = -be32_to_cpup (p); }return 0 ;
@@ -3372,8 +3344,6 @@ static int decode_attr_filehandle(struct xdr_stream *xdr, uint32_t *bitmap, struif (fh !=NULL )memset (fh,0 ,sizeof (*fh)); -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_FILEHANDLE -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_FILEHANDLE )) { p =xdr_inline_decode (xdr,4 );if (unlikely (!p))
@@ -3388,7 +3358,6 @@ static int decode_attr_filehandle(struct xdr_stream *xdr, uint32_t *bitmap, strumemcpy (fh->data, p, len); fh->size = len; } - bitmap[0 ] &= ~FATTR4_WORD0_FILEHANDLE ; }return 0 ; out_overflow:
@@ -3401,14 +3370,11 @@ static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint__be32 *p; *res =0 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_ACLSUPPORT -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_ACLSUPPORT )) { p =xdr_inline_decode (xdr,4 );if (unlikely (!p)) goto out_overflow; *res =be32_to_cpup (p); - bitmap[0 ] &= ~FATTR4_WORD0_ACLSUPPORT ; }dprintk ("%s: ACLs supported=%u\n" , __func__, (unsigned int )*res);return 0 ;
@@ -3423,14 +3389,11 @@ static int decode_attr_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_tint ret =0 ; *fileid =0 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_FILEID -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_FILEID )) { p =xdr_inline_decode (xdr,8 );if (unlikely (!p)) goto out_overflow;xdr_decode_hyper (p, fileid); - bitmap[0 ] &= ~FATTR4_WORD0_FILEID ; ret =NFS_ATTR_FATTR_FILEID ; }dprintk ("%s: fileid=%Lu\n" , __func__, (unsigned long long )*fileid);
@@ -3446,14 +3409,11 @@ static int decode_attr_mounted_on_fileid(struct xdr_stream *xdr, uint32_t *bitmaint ret =0 ; *fileid =0 ; -if (unlikely (bitmap[1 ] & (FATTR4_WORD1_MOUNTED_ON_FILEID -1U ))) -return -EIO ;if (likely (bitmap[1 ] &FATTR4_WORD1_MOUNTED_ON_FILEID )) { p =xdr_inline_decode (xdr,8 );if (unlikely (!p)) goto out_overflow;xdr_decode_hyper (p, fileid); - bitmap[1 ] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID ; ret =NFS_ATTR_FATTR_MOUNTED_ON_FILEID ; }dprintk ("%s: fileid=%Lu\n" , __func__, (unsigned long long )*fileid);
@@ -3469,14 +3429,11 @@ static int decode_attr_files_avail(struct xdr_stream *xdr, uint32_t *bitmap, uinint status =0 ; *res =0 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_FILES_AVAIL -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_FILES_AVAIL )) { p =xdr_inline_decode (xdr,8 );if (unlikely (!p)) goto out_overflow;xdr_decode_hyper (p, res); - bitmap[0 ] &= ~FATTR4_WORD0_FILES_AVAIL ; }dprintk ("%s: files avail=%Lu\n" , __func__, (unsigned long long )*res);return status;
@@ -3491,14 +3448,11 @@ static int decode_attr_files_free(struct xdr_stream *xdr, uint32_t *bitmap, uintint status =0 ; *res =0 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_FILES_FREE -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_FILES_FREE )) { p =xdr_inline_decode (xdr,8 );if (unlikely (!p)) goto out_overflow;xdr_decode_hyper (p, res); - bitmap[0 ] &= ~FATTR4_WORD0_FILES_FREE ; }dprintk ("%s: files free=%Lu\n" , __func__, (unsigned long long )*res);return status;
@@ -3513,14 +3467,11 @@ static int decode_attr_files_total(struct xdr_stream *xdr, uint32_t *bitmap, uinint status =0 ; *res =0 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_FILES_TOTAL -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_FILES_TOTAL )) { p =xdr_inline_decode (xdr,8 );if (unlikely (!p)) goto out_overflow;xdr_decode_hyper (p, res); - bitmap[0 ] &= ~FATTR4_WORD0_FILES_TOTAL ; }dprintk ("%s: files total=%Lu\n" , __func__, (unsigned long long )*res);return status;
@@ -3578,11 +3529,8 @@ static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, st {int n;__be32 *p; -int status = -EIO ; +int status =0 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_FS_LOCATIONS -1U ))) - goto out; - status =0 ;if (unlikely (!(bitmap[0 ] &FATTR4_WORD0_FS_LOCATIONS ))) goto out; status = -EIO ;
@@ -3659,14 +3607,11 @@ static int decode_attr_maxfilesize(struct xdr_stream *xdr, uint32_t *bitmap, uinint status =0 ; *res =0 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_MAXFILESIZE -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_MAXFILESIZE )) { p =xdr_inline_decode (xdr,8 );if (unlikely (!p)) goto out_overflow;xdr_decode_hyper (p, res); - bitmap[0 ] &= ~FATTR4_WORD0_MAXFILESIZE ; }dprintk ("%s: maxfilesize=%Lu\n" , __func__, (unsigned long long )*res);return status;
@@ -3681,14 +3626,11 @@ static int decode_attr_maxlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_int status =0 ; *maxlink =1 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_MAXLINK -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_MAXLINK )) { p =xdr_inline_decode (xdr,4 );if (unlikely (!p)) goto out_overflow; *maxlink =be32_to_cpup (p); - bitmap[0 ] &= ~FATTR4_WORD0_MAXLINK ; }dprintk ("%s: maxlink=%u\n" , __func__, *maxlink);return status;
@@ -3703,14 +3645,11 @@ static int decode_attr_maxname(struct xdr_stream *xdr, uint32_t *bitmap, uint32_int status =0 ; *maxname =1024 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_MAXNAME -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_MAXNAME )) { p =xdr_inline_decode (xdr,4 );if (unlikely (!p)) goto out_overflow; *maxname =be32_to_cpup (p); - bitmap[0 ] &= ~FATTR4_WORD0_MAXNAME ; }dprintk ("%s: maxname=%u\n" , __func__, *maxname);return status;
@@ -3725,8 +3664,6 @@ static int decode_attr_maxread(struct xdr_stream *xdr, uint32_t *bitmap, uint32_int status =0 ; *res =1024 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_MAXREAD -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_MAXREAD )) {uint64_t maxread; p =xdr_inline_decode (xdr,8 );
@@ -3736,7 +3673,6 @@ static int decode_attr_maxread(struct xdr_stream *xdr, uint32_t *bitmap, uint32_if (maxread >0x7FFFFFFF ) maxread =0x7FFFFFFF ; *res = (uint32_t )maxread; - bitmap[0 ] &= ~FATTR4_WORD0_MAXREAD ; }dprintk ("%s: maxread=%lu\n" , __func__, (unsigned long )*res);return status;
@@ -3751,8 +3687,6 @@ static int decode_attr_maxwrite(struct xdr_stream *xdr, uint32_t *bitmap, uint32int status =0 ; *res =1024 ; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_MAXWRITE -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_MAXWRITE )) {uint64_t maxwrite; p =xdr_inline_decode (xdr,8 );
@@ -3762,7 +3696,6 @@ static int decode_attr_maxwrite(struct xdr_stream *xdr, uint32_t *bitmap, uint32if (maxwrite >0x7FFFFFFF ) maxwrite =0x7FFFFFFF ; *res = (uint32_t )maxwrite; - bitmap[0 ] &= ~FATTR4_WORD0_MAXWRITE ; }dprintk ("%s: maxwrite=%lu\n" , __func__, (unsigned long )*res);return status;
@@ -3778,15 +3711,12 @@ static int decode_attr_mode(struct xdr_stream *xdr, uint32_t *bitmap, umode_t *mint ret =0 ; *mode =0 ; -if (unlikely (bitmap[1 ] & (FATTR4_WORD1_MODE -1U ))) -return -EIO ;if (likely (bitmap[1 ] &FATTR4_WORD1_MODE )) { p =xdr_inline_decode (xdr,4 );if (unlikely (!p)) goto out_overflow; tmp =be32_to_cpup (p); *mode = tmp & ~S_IFMT ; - bitmap[1 ] &= ~FATTR4_WORD1_MODE ; ret =NFS_ATTR_FATTR_MODE ; }dprintk ("%s: file mode=0%o\n" , __func__, (unsigned int )*mode);
@@ -3802,14 +3732,11 @@ static int decode_attr_nlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_tint ret =0 ; *nlink =1 ; -if (unlikely (bitmap[1 ] & (FATTR4_WORD1_NUMLINKS -1U ))) -return -EIO ;if (likely (bitmap[1 ] &FATTR4_WORD1_NUMLINKS )) { p =xdr_inline_decode (xdr,4 );if (unlikely (!p)) goto out_overflow; *nlink =be32_to_cpup (p); - bitmap[1 ] &= ~FATTR4_WORD1_NUMLINKS ; ret =NFS_ATTR_FATTR_NLINK ; }dprintk ("%s: nlink=%u\n" , __func__, (unsigned int )*nlink);
@@ -3828,8 +3755,6 @@ static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap,int ret =0 ; *uid =make_kuid (&init_user_ns,-2 ); -if (unlikely (bitmap[1 ] & (FATTR4_WORD1_OWNER -1U ))) -return -EIO ;if (likely (bitmap[1 ] &FATTR4_WORD1_OWNER )) { p =xdr_inline_decode (xdr,4 );if (unlikely (!p))
@@ -3853,7 +3778,6 @@ static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap, }else dprintk ("%s: name too long (%u)!\n" , __func__, len); - bitmap[1 ] &= ~FATTR4_WORD1_OWNER ; }dprintk ("%s: uid=%d\n" , __func__, (int )from_kuid (&init_user_ns, *uid));return ret;
@@ -3871,8 +3795,6 @@ static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap,int ret =0 ; *gid =make_kgid (&init_user_ns,-2 ); -if (unlikely (bitmap[1 ] & (FATTR4_WORD1_OWNER_GROUP -1U ))) -return -EIO ;if (likely (bitmap[1 ] &FATTR4_WORD1_OWNER_GROUP )) { p =xdr_inline_decode (xdr,4 );if (unlikely (!p))
@@ -3896,7 +3818,6 @@ static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap, }else dprintk ("%s: name too long (%u)!\n" , __func__, len); - bitmap[1 ] &= ~FATTR4_WORD1_OWNER_GROUP ; }dprintk ("%s: gid=%d\n" , __func__, (int )from_kgid (&init_user_ns, *gid));return ret;
@@ -3912,8 +3833,6 @@ static int decode_attr_rdev(struct xdr_stream *xdr, uint32_t *bitmap, dev_t *rdeint ret =0 ; *rdev =MKDEV (0 ,0 ); -if (unlikely (bitmap[1 ] & (FATTR4_WORD1_RAWDEV -1U ))) -return -EIO ;if (likely (bitmap[1 ] &FATTR4_WORD1_RAWDEV )) {dev_t tmp;
@@ -3925,7 +3844,6 @@ static int decode_attr_rdev(struct xdr_stream *xdr, uint32_t *bitmap, dev_t *rde tmp =MKDEV (major, minor);if (MAJOR (tmp) == major &&MINOR (tmp) == minor) *rdev = tmp; - bitmap[1 ] &= ~FATTR4_WORD1_RAWDEV ; ret =NFS_ATTR_FATTR_RDEV ; }dprintk ("%s: rdev=(0x%x:0x%x)\n" , __func__, major, minor);
@@ -3941,14 +3859,11 @@ static int decode_attr_space_avail(struct xdr_stream *xdr, uint32_t *bitmap, uinint status =0 ; *res =0 ; -if (unlikely (bitmap[1 ] & (FATTR4_WORD1_SPACE_AVAIL -1U ))) -return -EIO ;if (likely (bitmap[1 ] &FATTR4_WORD1_SPACE_AVAIL )) { p =xdr_inline_decode (xdr,8 );if (unlikely (!p)) goto out_overflow;xdr_decode_hyper (p, res); - bitmap[1 ] &= ~FATTR4_WORD1_SPACE_AVAIL ; }dprintk ("%s: space avail=%Lu\n" , __func__, (unsigned long long )*res);return status;
@@ -3963,14 +3878,11 @@ static int decode_attr_space_free(struct xdr_stream *xdr, uint32_t *bitmap, uintint status =0 ; *res =0 ; -if (unlikely (bitmap[1 ] & (FATTR4_WORD1_SPACE_FREE -1U ))) -return -EIO ;if (likely (bitmap[1 ] &FATTR4_WORD1_SPACE_FREE )) { p =xdr_inline_decode (xdr,8 );if (unlikely (!p)) goto out_overflow;xdr_decode_hyper (p, res); - bitmap[1 ] &= ~FATTR4_WORD1_SPACE_FREE ; }dprintk ("%s: space free=%Lu\n" , __func__, (unsigned long long )*res);return status;
@@ -3985,14 +3897,11 @@ static int decode_attr_space_total(struct xdr_stream *xdr, uint32_t *bitmap, uinint status =0 ; *res =0 ; -if (unlikely (bitmap[1 ] & (FATTR4_WORD1_SPACE_TOTAL -1U ))) -return -EIO ;if (likely (bitmap[1 ] &FATTR4_WORD1_SPACE_TOTAL )) { p =xdr_inline_decode (xdr,8 );if (unlikely (!p)) goto out_overflow;xdr_decode_hyper (p, res); - bitmap[1 ] &= ~FATTR4_WORD1_SPACE_TOTAL ; }dprintk ("%s: space total=%Lu\n" , __func__, (unsigned long long )*res);return status;
@@ -4007,14 +3916,11 @@ static int decode_attr_space_used(struct xdr_stream *xdr, uint32_t *bitmap, uintint ret =0 ; *used =0 ; -if (unlikely (bitmap[1 ] & (FATTR4_WORD1_SPACE_USED -1U ))) -return -EIO ;if (likely (bitmap[1 ] &FATTR4_WORD1_SPACE_USED )) { p =xdr_inline_decode (xdr,8 );if (unlikely (!p)) goto out_overflow;xdr_decode_hyper (p, used); - bitmap[1 ] &= ~FATTR4_WORD1_SPACE_USED ; ret =NFS_ATTR_FATTR_SPACE_USED ; } dprintk("%s: space used=%Lu\n", __func__,
@@ -4050,13 +3956,10 @@ static int decode_attr_time_access(struct xdr_stream *xdr, uint32_t *bitmap, str time->tv_sec =0 ; time->tv_nsec =0 ; -if (unlikely (bitmap[1 ] & (FATTR4_WORD1_TIME_ACCESS -1U ))) -return -EIO ;if (likely (bitmap[1 ] &FATTR4_WORD1_TIME_ACCESS )) { status =decode_attr_time (xdr, time);if (status ==0 ) status =NFS_ATTR_FATTR_ATIME ; - bitmap[1 ] &= ~FATTR4_WORD1_TIME_ACCESS ; }dprintk ("%s: atime=%ld\n" , __func__, (long )time->tv_sec);return status;
@@ -4068,13 +3971,10 @@ static int decode_attr_time_metadata(struct xdr_stream *xdr, uint32_t *bitmap, s time->tv_sec =0 ; time->tv_nsec =0 ; -if (unlikely (bitmap[1 ] & (FATTR4_WORD1_TIME_METADATA -1U ))) -return -EIO ;if (likely (bitmap[1 ] &FATTR4_WORD1_TIME_METADATA )) { status =decode_attr_time (xdr, time);if (status ==0 ) status =NFS_ATTR_FATTR_CTIME ; - bitmap[1 ] &= ~FATTR4_WORD1_TIME_METADATA ; }dprintk ("%s: ctime=%ld\n" , __func__, (long )time->tv_sec);return status;
@@ -4087,12 +3987,8 @@ static int decode_attr_time_delta(struct xdr_stream *xdr, uint32_t *bitmap, time->tv_sec =0 ; time->tv_nsec =0 ; -if (unlikely (bitmap[1 ] & (FATTR4_WORD1_TIME_DELTA -1U ))) -return -EIO ; -if (likely (bitmap[1 ] &FATTR4_WORD1_TIME_DELTA )) { +if (likely (bitmap[1 ] &FATTR4_WORD1_TIME_DELTA )) status =decode_attr_time (xdr, time); - bitmap[1 ] &= ~FATTR4_WORD1_TIME_DELTA ; - }dprintk ("%s: time_delta=%ld %ld\n" , __func__, (long )time->tv_sec, (long )time->tv_nsec);return status;
@@ -4107,8 +4003,6 @@ static int decode_attr_security_label(struct xdr_stream *xdr, uint32_t *bitmap,__be32 *p;int status =0 ; -if (unlikely (bitmap[2 ] & (FATTR4_WORD2_SECURITY_LABEL -1U ))) -return -EIO ;if (likely (bitmap[2 ] &FATTR4_WORD2_SECURITY_LABEL )) { p =xdr_inline_decode (xdr,4 );if (unlikely (!p))
@@ -4133,7 +4027,6 @@ static int decode_attr_security_label(struct xdr_stream *xdr, uint32_t *bitmap, label->lfs = lfs; status =NFS_ATTR_FATTR_V4_SECURITY_LABEL ; } - bitmap[2 ] &= ~FATTR4_WORD2_SECURITY_LABEL ; }else printk (KERN_WARNING "%s: label too long (%u)!\n" , __func__, len);
@@ -4154,13 +4047,10 @@ static int decode_attr_time_modify(struct xdr_stream *xdr, uint32_t *bitmap, str time->tv_sec =0 ; time->tv_nsec =0 ; -if (unlikely (bitmap[1 ] & (FATTR4_WORD1_TIME_MODIFY -1U ))) -return -EIO ;if (likely (bitmap[1 ] &FATTR4_WORD1_TIME_MODIFY )) { status =decode_attr_time (xdr, time);if (status ==0 ) status =NFS_ATTR_FATTR_MTIME ; - bitmap[1 ] &= ~FATTR4_WORD1_TIME_MODIFY ; }dprintk ("%s: mtime=%ld\n" , __func__, (long )time->tv_sec);return status;
@@ -4183,6 +4073,15 @@ static int verify_attr_len(struct xdr_stream *xdr, unsigned int savep, uint32_treturn 0 ; } +static int verify_attrs_allowed (uint32_t *bitmap,const uint32_t *attrs_allowed) +{ +if (unlikely (bitmap[0 ] & ~attrs_allowed[0 ] || + bitmap[1 ] & ~attrs_allowed[1 ] || + bitmap[2 ] & ~attrs_allowed[2 ])) +return -EIO ; +return 0 ; +} +static int decode_change_info (struct xdr_stream *xdr,struct nfs4_change_info *cinfo) {__be32 *p;
@@ -4296,6 +4195,11 @@ out_overflow:static int decode_server_caps (struct xdr_stream *xdr,struct nfs4_server_caps_res *res) { +static const uint32_t attrs_allowed[3 ] = { + [0 ] =FATTR4_WORD0_SUPPORTED_ATTRS |FATTR4_WORD0_FH_EXPIRE_TYPE | +FATTR4_WORD0_LINK_SUPPORT |FATTR4_WORD0_SYMLINK_SUPPORT | +FATTR4_WORD0_ACLSUPPORT , + };unsigned int savep;uint32_t attrlen, bitmap[3 ] = {0 };int status;
@@ -4304,6 +4208,8 @@ static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_re goto xdr_error;if ((status =decode_attr_bitmap (xdr, bitmap)) !=0 ) goto xdr_error; +if ((status =verify_attrs_allowed (bitmap, attrs_allowed)) !=0 ) + goto xdr_error;if ((status =decode_attr_length (xdr, &attrlen, &savep)) !=0 ) goto xdr_error;if ((status =decode_attr_supported (xdr, bitmap, res->attr_bitmask)) !=0 )
@@ -4325,6 +4231,12 @@ xdr_error:static int decode_statfs (struct xdr_stream *xdr,struct nfs_fsstat *fsstat) { +static const uint32_t attrs_allowed[3 ] = { + [0 ] =FATTR4_WORD0_FILES_AVAIL |FATTR4_WORD0_FILES_FREE | +FATTR4_WORD0_FILES_TOTAL , + [1 ] =FATTR4_WORD1_SPACE_AVAIL |FATTR4_WORD1_SPACE_FREE | +FATTR4_WORD1_SPACE_TOTAL , + };unsigned int savep;uint32_t attrlen, bitmap[3 ] = {0 };int status;
@@ -4333,6 +4245,8 @@ static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat) goto xdr_error;if ((status =decode_attr_bitmap (xdr, bitmap)) !=0 ) goto xdr_error; +if ((status =verify_attrs_allowed (bitmap, attrs_allowed)) !=0 ) + goto xdr_error;if ((status =decode_attr_length (xdr, &attrlen, &savep)) !=0 ) goto xdr_error;
@@ -4357,6 +4271,9 @@ xdr_error:static int decode_pathconf (struct xdr_stream *xdr,struct nfs_pathconf *pathconf) { +static const uint32_t attrs_allowed[3 ] = { + [0 ] =FATTR4_WORD0_MAXLINK |FATTR4_WORD0_MAXNAME , + };unsigned int savep;uint32_t attrlen, bitmap[3 ] = {0 };int status;
@@ -4365,6 +4282,8 @@ static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf goto xdr_error;if ((status =decode_attr_bitmap (xdr, bitmap)) !=0 ) goto xdr_error; +if ((status =verify_attrs_allowed (bitmap, attrs_allowed)) !=0 ) + goto xdr_error;if ((status =decode_attr_length (xdr, &attrlen, &savep)) !=0 ) goto xdr_error;
@@ -4462,8 +4381,6 @@ static int decode_attr_mdsthreshold(struct xdr_stream *xdr,int status =0 ;uint32_t num; -if (unlikely (bitmap[2 ] & (FATTR4_WORD2_MDSTHRESHOLD -1U ))) -return -EIO ;if (bitmap[2 ] &FATTR4_WORD2_MDSTHRESHOLD ) {/* Did the server return an unrequested attribute? */ if (unlikely (res ==NULL ))
@@ -4480,7 +4397,6 @@ static int decode_attr_mdsthreshold(struct xdr_stream *xdr, __func__); status =decode_first_threshold_item4 (xdr, res); - bitmap[2 ] &= ~FATTR4_WORD2_MDSTHRESHOLD ; }return status; out_overflow:
@@ -4493,11 +4409,28 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_fs_locations *fs_loc, struct nfs4_label *label, const struct nfs_server *server) { + static const uint32_t attrs_allowed[3] = { + [0 ] =FATTR4_WORD0_TYPE |FATTR4_WORD0_CHANGE | +FATTR4_WORD0_SIZE |FATTR4_WORD0_FSID | +FATTR4_WORD0_RDATTR_ERROR |FATTR4_WORD0_FILEHANDLE | +FATTR4_WORD0_FILEID |FATTR4_WORD0_FS_LOCATIONS , + [1 ] =FATTR4_WORD1_MODE |FATTR4_WORD1_NUMLINKS | +FATTR4_WORD1_OWNER |FATTR4_WORD1_OWNER_GROUP | +FATTR4_WORD1_RAWDEV |FATTR4_WORD1_SPACE_USED | +FATTR4_WORD1_TIME_ACCESS |FATTR4_WORD1_TIME_METADATA | +FATTR4_WORD1_TIME_MODIFY | +FATTR4_WORD1_MOUNTED_ON_FILEID , + [2 ] =FATTR4_WORD2_MDSTHRESHOLD |FATTR4_WORD2_SECURITY_LABEL , + };int status;umode_t fmode =0 ;uint32_t type;int32_t err; + status =verify_attrs_allowed (bitmap, attrs_allowed); +if (status !=0 ) + goto xdr_error; + status =decode_attr_type (xdr, bitmap, &type);if (status <0 ) goto xdr_error;
@@ -4699,12 +4632,9 @@ static int decode_attr_pnfstype(struct xdr_stream *xdr, uint32_t *bitmap,int status =0 ;dprintk ("%s: bitmap is %x\n" , __func__, bitmap[1 ]); -if (unlikely (bitmap[1 ] & (FATTR4_WORD1_FS_LAYOUT_TYPES -1U ))) -return -EIO ; -if (bitmap[1 ] &FATTR4_WORD1_FS_LAYOUT_TYPES ) { +if (bitmap[1 ] &FATTR4_WORD1_FS_LAYOUT_TYPES ) status =decode_first_pnfs_layout_type (xdr, layouttype); - bitmap[1 ] &= ~FATTR4_WORD1_FS_LAYOUT_TYPES ; - }else +else *layouttype =0 ;return status; }
@@ -4726,13 +4656,18 @@ static int decode_attr_layout_blksize(struct xdr_stream *xdr, uint32_t *bitmap,return -EIO ; } *res =be32_to_cpup (p); - bitmap[2 ] &= ~FATTR4_WORD2_LAYOUT_BLKSIZE ; }return 0 ; }static int decode_fsinfo (struct xdr_stream *xdr,struct nfs_fsinfo *fsinfo) { +static const uint32_t attrs_allowed[3 ] = { + [0 ] =FATTR4_WORD0_LEASE_TIME |FATTR4_WORD0_MAXFILESIZE | +FATTR4_WORD0_MAXREAD |FATTR4_WORD0_MAXWRITE , + [1 ] =FATTR4_WORD1_TIME_DELTA |FATTR4_WORD1_FS_LAYOUT_TYPES , + [2 ] =FATTR4_WORD2_LAYOUT_BLKSIZE , + };unsigned int savep;uint32_t attrlen, bitmap[3 ];int status;
@@ -4741,6 +4676,8 @@ static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo) goto xdr_error;if ((status =decode_attr_bitmap (xdr, bitmap)) !=0 ) goto xdr_error; +if ((status =verify_attrs_allowed (bitmap, attrs_allowed) !=0 )) + goto xdr_error;if ((status =decode_attr_length (xdr, &attrlen, &savep)) !=0 ) goto xdr_error;
@@ -5203,6 +5140,9 @@ decode_restorefh(struct xdr_stream *xdr)static int decode_getacl (struct xdr_stream *xdr,struct rpc_rqst *req,struct nfs_getaclres *res) { +static const uint32_t attrs_allowed[3 ] = { + [0 ] =FATTR4_WORD0_ACL , + };unsigned int savep;uint32_t attrlen, bitmap[3 ] = {0 };
@@ -5220,11 +5160,11 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,if ((status =decode_attr_bitmap (xdr, bitmap)) !=0 ) goto out; +if ((status =verify_attrs_allowed (bitmap, attrs_allowed)) !=0 ) + goto out;if ((status =decode_attr_length (xdr, &attrlen, &savep)) !=0 ) goto out; -if (unlikely (bitmap[0 ] & (FATTR4_WORD0_ACL -1U ))) -return -EIO ;if (likely (bitmap[0 ] &FATTR4_WORD0_ACL )) {/* The bitmap (xdr len + bitmaps) and the attr xdr len words
Signature · 1 line
2.1.0
Re: [RFC v3 36/45] NFSv4: Fix GETATTR bitmap verification
Quoted 6 lines · Andreas Gruenbacher
The NFSv4 client sends the server GETATTR requests with different sets of requested attributes depending on the situation. The requested set of attributes is encoded in a bitmap; the server replies with the set of attributes it could return. These bitmaps can be several words wide. The bitmap returned by the server is a subset of the bitmap sent by the client.
Quoted 7 lines
While decoding the reply, the client tries to verify the reply bitmap: it checks if any previous, unexpected attributes are left in the same word of the bitmap for each attribute it tries to decode, then it clears the current attribute's bit in the bitmap for the next decode function.
The client fails to detect when unexpected attributes are sent after the last expected attribute in each word in the bitmap.
Is it important that the client catch that?
Quoted 3 lines
Fix this by checking the entire bitmap for unexpected attributes first. The server can still send attributes which the client understands but which it didn't request; this doesn't cause any harm.
I don't understand that last sentence. On a skim it looks like after this patch we *will* still error out if a server does that, right? (As we should, that would be a server bug.)
...
Quoted 13 lines
fs/nfs/nfs4xdr.c +9
-0+static int verify_attrs_allowed (uint32_t *bitmap,const uint32_t *attrs_allowed) +{ +if (unlikely (bitmap[0 ] & ~attrs_allowed[0 ] || + bitmap[1 ] & ~attrs_allowed[1 ] || + bitmap[2 ] & ~attrs_allowed[2 ])) +return -EIO ; +return 0 ; +} +static int decode_change_info (struct xdr_stream *xdr,struct nfs4_change_info *cinfo) {__be32 *p;
fs/nfs/nfs4xdr.c out_overflow: · line 4202
Quoted 8 lines
fs/nfs/nfs4xdr.c +5
-0@@ -4296,6 +4195,11 @@ out_overflow:static int decode_server_caps (struct xdr_stream *xdr,struct nfs4_server_caps_res *res) { +static const uint32_t attrs_allowed[3 ] = { + [0 ] =FATTR4_WORD0_SUPPORTED_ATTRS |FATTR4_WORD0_FH_EXPIRE_TYPE | +FATTR4_WORD0_LINK_SUPPORT |FATTR4_WORD0_SYMLINK_SUPPORT | +FATTR4_WORD0_ACLSUPPORT , + };
This is the same list as in nfs4-xdr_enc_server_caps. Could we avoid that duplication?
--b.
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 36/45] NFSv4: Fix GETATTR bitmap verification
Quoted 12 lines · J. Bruce Fields
Quoted 11 lines · Andreas Gruenbacher
The NFSv4 client sends the server GETATTR requests with different sets of requested attributes depending on the situation. The requested set of attributes is encoded in a bitmap; the server replies with the set of attributes it could return. These bitmaps can be several words wide. The bitmap returned by the server is a subset of the bitmap sent by the client.
While decoding the reply, the client tries to verify the reply bitmap: it checks if any previous, unexpected attributes are left in the same word of the bitmap for each attribute it tries to decode, then it clears the current attribute's bit in the bitmap for the next decode function.
Quoted 4 lines
Quoted 2 lines
The client fails to detect when unexpected attributes are sent after the last expected attribute in each word in the bitmap.
Is it important that the client catch that?
Right. What is the actual problem or bug that this patch is trying to fix? Why do we care if a buggy server sends us extra info that we didn't ask for?
Trond
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 36/45] NFSv4: Fix GETATTR bitmap verification
2015-05-28 22:50 GMT+02:00 Trond Myklebust <trond.myklebust-7I+n7zu2hftEKMMhf/[email protected]>:
Quoted 13 lines · J. Bruce Fields
Quoted 12 lines · Andreas Gruenbacher
Quoted 11 lines
The NFSv4 client sends the server GETATTR requests with different sets of requested attributes depending on the situation. The requested set of attributes is encoded in a bitmap; the server replies with the set of attributes it could return. These bitmaps can be several words wide. The bitmap returned by the server is a subset of the bitmap sent by the client.
While decoding the reply, the client tries to verify the reply bitmap: it checks if any previous, unexpected attributes are left in the same word of the bitmap for each attribute it tries to decode, then it clears the current attribute's bit in the bitmap for the next decode function.
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
The client fails to detect when unexpected attributes are sent after the last expected attribute in each word in the bitmap.
Is it important that the client catch that?
Right. What is the actual problem or bug that this patch is trying to fix? Why do we care if a buggy server sends us extra info that we didn't ask for?
I think we do care to correctly decode (and reject) well-formed but illegal server replies. In this case, when switching to the next word of a bitmap, the client doesn't check if the previous word has been completely "consumed" yet. If any attributes are "missed", decoding the attribute values gets out of sync, garbage is decoded, and the error may be missed.
Andreas
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 36/45] NFSv4: Fix GETATTR bitmap verification
Quoted 23 lines · Andreas Grünbacher
2015-05-28 22:50 GMT+02:00 Trond Myklebust <[email protected]>:
Quoted 13 lines · J. Bruce Fields
Quoted 12 lines · Andreas Gruenbacher
Quoted 11 lines
The NFSv4 client sends the server GETATTR requests with different sets of requested attributes depending on the situation. The requested set of attributes is encoded in a bitmap; the server replies with the set of attributes it could return. These bitmaps can be several words wide. The bitmap returned by the server is a subset of the bitmap sent by the client.
While decoding the reply, the client tries to verify the reply bitmap: it checks if any previous, unexpected attributes are left in the same word of the bitmap for each attribute it tries to decode, then it clears the current attribute's bit in the bitmap for the next decode function.
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
The client fails to detect when unexpected attributes are sent after the last expected attribute in each word in the bitmap.
Is it important that the client catch that?
Right. What is the actual problem or bug that this patch is trying to fix? Why do we care if a buggy server sends us extra info that we didn't ask for?
Quoted 6 lines
I think we do care to correctly decode (and reject) well-formed but illegal server replies. In this case, when switching to the next word of a bitmap, the client doesn't check if the previous word has been completely "consumed" yet. If any attributes are "missed", decoding the attribute values gets out of sync, garbage is decoded, and the error may be missed.
We already do this kind of check with the existing code. What's wrong with it?
Trond
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 36/45] NFSv4: Fix GETATTR bitmap verification
Quoted 25 lines · Trond Myklebust
Quoted 23 lines · Andreas Grünbacher
2015-05-28 22:50 GMT+02:00 Trond Myklebust <[email protected]>:
Quoted 21 lines · J. Bruce Fields
Quoted 12 lines · Andreas Gruenbacher
Quoted 11 lines
The NFSv4 client sends the server GETATTR requests with different sets of requested attributes depending on the situation. The requested set of attributes is encoded in a bitmap; the server replies with the set of attributes it could return. These bitmaps can be several words wide. The bitmap returned by the server is a subset of the bitmap sent by the client.
While decoding the reply, the client tries to verify the reply bitmap: it checks if any previous, unexpected attributes are left in the same word of the bitmap for each attribute it tries to decode, then it clears the current attribute's bit in the bitmap for the next decode function.
Quoted 4 lines
Quoted 2 lines
The client fails to detect when unexpected attributes are sent after the last expected attribute in each word in the bitmap.
Is it important that the client catch that?
Right. What is the actual problem or bug that this patch is trying to fix? Why do we care if a buggy server sends us extra info that we didn't ask for?
Quoted 9 lines
Quoted 6 lines
I think we do care to correctly decode (and reject) well-formed but illegal server replies. In this case, when switching to the next word of a bitmap, the client doesn't check if the previous word has been completely "consumed" yet. If any attributes are "missed", decoding the attribute values gets out of sync, garbage is decoded, and the error may be missed.
We already do this kind of check with the existing code. What's wrong with it?
Actually, you're right, we don't check for the previous word, however fixing that is a question of adding 2 extra checks in decode_getfattr_attrs(), one in decode_getfattr_statfs(), and one in decode_fsinfo().
It shouldn't require a rewrite of the entire nfs4xdr.c.
Trond
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 36/45] NFSv4: Fix GETATTR bitmap verification
2015-05-28 23:55 GMT+02:00 Trond Myklebust <trond.myklebust-7I+n7zu2hftEKMMhf/[email protected]>:
Quoted 8 lines
Quoted 1 line
We already do this kind of check with the existing code. What's wrong with it?
Actually, you're right, we don't check for the previous word, however fixing that is a question of adding 2 extra checks in
decode_getfattr_attrs(), one indecode_getfattr_statfs(), and one indecode_fsinfo().It shouldn't require a rewrite of the entire
nfs4xdr.c.
I would actually prefer either verifying the reply bitmap against the request bitmap,
or checking the bitmap first as this patch does --- the current approach of knocking
individual bits over and checking if any "before" bits have been missed isn't exactly what I would take as a textbook example.
Thanks,
Andreas
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 36/45] NFSv4: Fix GETATTR bitmap verification
Quoted 8 lines · Andreas Grünbacher
2015-05-28 23:55 GMT+02:00 Trond Myklebust <[email protected]>:
Quoted 7 lines
Quoted 1 line
We already do this kind of check with the existing code. What's wrong with it?
Actually, you're right, we don't check for the previous word, however fixing that is a question of adding 2 extra checks in
decode_getfattr_attrs(), one indecode_getfattr_statfs(), and one indecode_fsinfo().
Quoted 8 lines
Quoted 1 line
It shouldn't require a rewrite of the entire
nfs4xdr.c.I would actually prefer either verifying the reply bitmap against the request bitmap,
or checking the bitmap first as this patch does --- the current approach of knocking
individual bits over and checking if any "before" bits have been missed isn't exactly what I would take as a textbook example.
...and I'd prefer that we don't keep rewriting code that works. Screw the textbooks...
Trond
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 36/45] NFSv4: Fix GETATTR bitmap verification
2015-05-29 0:24 GMT+02:00 Trond Myklebust <[email protected]>:
Quoted 1 line
...and I'd prefer that we don't keep rewriting code that works.
Sure, go ahead.
Thanks,
Andreas
Re: [RFC v3 36/45] NFSv4: Fix GETATTR bitmap verification
In reply to J. Bruce Fields2015-05-28 22:33 GMT+02:00 J. Bruce Fields <[email protected]>:
Quoted 5 lines · Andreas Gruenbacher
Quoted 2 lines
The client fails to detect when unexpected attributes are sent after the last expected attribute in each word in the bitmap.
Is it important that the client catch that?
Yes if there are more attributes in the next word that the client cares about; probably no otherwise.
Quoted 7 lines
Quoted 3 lines
Fix this by checking the entire bitmap for unexpected attributes first. The server can still send attributes which the client understands but which it didn't request; this doesn't cause any harm.
I don't understand that last sentence. On a skim it looks like after this patch we *will* still error out if a server does that, right? (As we should, that would be a server bug.)
We don't actually verify the request bitmap against the reply bitmap; we don't keep
the request bitmap around long enough. Some decode functions understand a set of attributes. Currently, we happily accept any of the attributes the decode function
in question accepts, even ones that were not requested. That could be fixed by checking the request bitmap against the reply bitmap as well of course.
Andreas
Re: [RFC v3 36/45] NFSv4: Fix GETATTR bitmap verification
In reply to J. Bruce Fields2015-05-28 22:33 GMT+02:00 J. Bruce Fields <[email protected]>:
Quoted 2 lines
This is the same list as in nfs4-xdr_enc_server_caps. Could we avoid that duplication?
I don't think it matters at all.
Thanks,
Andreas
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC v3 37/45 nfs/sunrpc: No more encode and decode function pointer casting
In reply to Andreas GruenbacherInstead of casting the encode and decode functions to the required type when assigning them to the p_encode and p_decode members of struct rpc_procinfo, define the functions with their proper type and cast the additional obj argument to the appropriate type inside those functions. This allows slightly better type checking by the compiler at the cost of slightly more verbose code.
--- fs/lockd/clnt4xdr.c | 28 ++-- fs/lockd/clntxdr.c | 28 ++-- fs/lockd/mon.c | 20 ++- fs/nfs/mount_clnt.c | 21 +-- fs/nfs/nfs2xdr.c | 55 ++++--- fs/nfs/nfs3xdr.c | 123 +++++++++------ fs/nfs/nfs4xdr.c | 292 +++++++++++++++++++++++------------ fs/nfsd/nfs4callback.c | 16 +- include/linux/sunrpc/xdr.h | 5 +- net/sunrpc/auth_gss/gss_rpc_upcall.c | 4 +- net/sunrpc/auth_gss/gss_rpc_xdr.c | 6 +- net/sunrpc/auth_gss/gss_rpc_xdr.h | 4 +- net/sunrpc/clnt.c | 4 +- net/sunrpc/rpcb_clnt.c | 51 +++--- 14 files changed, 421 insertions(+), 236 deletions(-)
fs/lockd/clnt4xdr.c +18 -10
@@ -381,8 +381,9 @@ static void encode_nlm4_lock(struct xdr_stream *xdr,*/ static void nlm4_xdr_enc_testargs (struct rpc_rqst *req,struct xdr_stream *xdr, -const struct nlm_args *args) +void *obj) { +const struct nlm_args *args = obj;const struct nlm_lock *lock = &args->lock;encode_cookie (xdr, &args->cookie);
@@ -402,8 +403,9 @@ static void nlm4_xdr_enc_testargs(struct rpc_rqst *req,*/ static void nlm4_xdr_enc_lockargs (struct rpc_rqst *req,struct xdr_stream *xdr, -const struct nlm_args *args) +void *obj) { +const struct nlm_args *args = obj;const struct nlm_lock *lock = &args->lock;encode_cookie (xdr, &args->cookie);
@@ -424,8 +426,9 @@ static void nlm4_xdr_enc_lockargs(struct rpc_rqst *req,*/ static void nlm4_xdr_enc_cancargs (struct rpc_rqst *req,struct xdr_stream *xdr, -const struct nlm_args *args) +void *obj) { +const struct nlm_args *args = obj;const struct nlm_lock *lock = &args->lock;encode_cookie (xdr, &args->cookie);
@@ -442,8 +445,9 @@ static void nlm4_xdr_enc_cancargs(struct rpc_rqst *req,*/ static void nlm4_xdr_enc_unlockargs (struct rpc_rqst *req,struct xdr_stream *xdr, -const struct nlm_args *args) +void *obj) { +const struct nlm_args *args = obj;const struct nlm_lock *lock = &args->lock;encode_cookie (xdr, &args->cookie);
@@ -458,8 +462,9 @@ static void nlm4_xdr_enc_unlockargs(struct rpc_rqst *req,*/ static void nlm4_xdr_enc_res (struct rpc_rqst *req,struct xdr_stream *xdr, -const struct nlm_res *result) +void *obj) { +const struct nlm_res *result = obj;encode_cookie (xdr, &result->cookie);encode_nlm4_stat (xdr, result->status); }
@@ -479,8 +484,9 @@ static void nlm4_xdr_enc_res(struct rpc_rqst *req,*/ static void nlm4_xdr_enc_testres (struct rpc_rqst *req,struct xdr_stream *xdr, -const struct nlm_res *result) +void *obj) { +const struct nlm_res *result = obj;encode_cookie (xdr, &result->cookie);encode_nlm4_stat (xdr, result->status);if (result->status == nlm_lck_denied)
@@ -525,8 +531,9 @@ out:static int nlm4_xdr_dec_testres (struct rpc_rqst *req,struct xdr_stream *xdr, -struct nlm_res *result) +void *obj) { +struct nlm_res *result = obj;int error; error =decode_cookie (xdr, &result->cookie);
@@ -545,8 +552,9 @@ out:*/ static int nlm4_xdr_dec_res (struct rpc_rqst *req,struct xdr_stream *xdr, -struct nlm_res *result) +void *obj) { +struct nlm_res *result = obj;int error; error =decode_cookie (xdr, &result->cookie);
@@ -566,8 +574,8 @@ out:#define PROC (proc, argtype, restype) \ [NLMPROC_##proc] = { \ .p_proc = NLMPROC_##proc, \ - .p_encode = (kxdreproc_t)nlm4_xdr_enc_##argtype, \ - .p_decode = (kxdrdproc_t)nlm4_xdr_dec_##restype, \ + .p_encode = nlm4_xdr_enc_##argtype, \ + .p_decode = nlm4_xdr_dec_##restype, \ .p_arglen = NLM4_##argtype##_sz, \ .p_replen = NLM4_##restype##_sz, \ .p_statidx = NLMPROC_##proc, \
fs/lockd/clntxdr.c +18 -10
@@ -374,8 +374,9 @@ static void encode_nlm_lock(struct xdr_stream *xdr,*/ static void nlm_xdr_enc_testargs (struct rpc_rqst *req,struct xdr_stream *xdr, -const struct nlm_args *args) +void *obj) { +const struct nlm_args *args = obj;const struct nlm_lock *lock = &args->lock;encode_cookie (xdr, &args->cookie);
@@ -395,8 +396,9 @@ static void nlm_xdr_enc_testargs(struct rpc_rqst *req,*/ static void nlm_xdr_enc_lockargs (struct rpc_rqst *req,struct xdr_stream *xdr, -const struct nlm_args *args) +void *obj) { +const struct nlm_args *args = obj;const struct nlm_lock *lock = &args->lock;encode_cookie (xdr, &args->cookie);
@@ -417,8 +419,9 @@ static void nlm_xdr_enc_lockargs(struct rpc_rqst *req,*/ static void nlm_xdr_enc_cancargs (struct rpc_rqst *req,struct xdr_stream *xdr, -const struct nlm_args *args) +void *obj) { +const struct nlm_args *args = obj;const struct nlm_lock *lock = &args->lock;encode_cookie (xdr, &args->cookie);
@@ -435,8 +438,9 @@ static void nlm_xdr_enc_cancargs(struct rpc_rqst *req,*/ static void nlm_xdr_enc_unlockargs (struct rpc_rqst *req,struct xdr_stream *xdr, -const struct nlm_args *args) +void *obj) { +const struct nlm_args *args = obj;const struct nlm_lock *lock = &args->lock;encode_cookie (xdr, &args->cookie);
@@ -451,8 +455,9 @@ static void nlm_xdr_enc_unlockargs(struct rpc_rqst *req,*/ static void nlm_xdr_enc_res (struct rpc_rqst *req,struct xdr_stream *xdr, -const struct nlm_res *result) +void *obj) { +const struct nlm_res *result = obj;encode_cookie (xdr, &result->cookie);encode_nlm_stat (xdr, result->status); }
@@ -479,8 +484,9 @@ static void encode_nlm_testrply(struct xdr_stream *xdr,static void nlm_xdr_enc_testres (struct rpc_rqst *req,struct xdr_stream *xdr, -const struct nlm_res *result) +void *obj) { +const struct nlm_res *result = obj;encode_cookie (xdr, &result->cookie);encode_nlm_stat (xdr, result->status);encode_nlm_testrply (xdr, result);
@@ -523,8 +529,9 @@ out:static int nlm_xdr_dec_testres (struct rpc_rqst *req,struct xdr_stream *xdr, -struct nlm_res *result) +void *obj) { +struct nlm_res *result = obj;int error; error =decode_cookie (xdr, &result->cookie);
@@ -543,8 +550,9 @@ out:*/ static int nlm_xdr_dec_res (struct rpc_rqst *req,struct xdr_stream *xdr, -struct nlm_res *result) +void *obj) { +struct nlm_res *result = obj;int error; error =decode_cookie (xdr, &result->cookie);
@@ -564,8 +572,8 @@ out:#define PROC (proc, argtype, restype) \ [NLMPROC_##proc] = { \ .p_proc = NLMPROC_##proc, \ - .p_encode = (kxdreproc_t)nlm_xdr_enc_##argtype, \ - .p_decode = (kxdrdproc_t)nlm_xdr_dec_##restype, \ + .p_encode = nlm_xdr_enc_##argtype, \ + .p_decode = nlm_xdr_dec_##restype, \ .p_arglen = NLM_##argtype##_sz, \ .p_replen = NLM_##restype##_sz, \ .p_statidx = NLMPROC_##proc, \
fs/lockd/mon.c +12 -8
@@ -527,22 +527,25 @@ static void encode_priv(struct xdr_stream *xdr, const struct nsm_args *argp) }static void nsm_xdr_enc_mon (struct rpc_rqst *req,struct xdr_stream *xdr, -const struct nsm_args *argp) +void *obj) { +const struct nsm_args *argp = obj;encode_mon_id (xdr, argp);encode_priv (xdr, argp); }static void nsm_xdr_enc_unmon (struct rpc_rqst *req,struct xdr_stream *xdr, -const struct nsm_args *argp) +void *obj) { +const struct nsm_args *argp = obj;encode_mon_id (xdr, argp); }static int nsm_xdr_dec_stat_res (struct rpc_rqst *rqstp,struct xdr_stream *xdr, -struct nsm_res *resp) +void *obj) { +struct nsm_res *resp = obj;__be32 *p; p =xdr_inline_decode (xdr,4 +4 );
@@ -558,8 +561,9 @@ static int nsm_xdr_dec_stat_res(struct rpc_rqst *rqstp,static int nsm_xdr_dec_stat (struct rpc_rqst *rqstp,struct xdr_stream *xdr, -struct nsm_res *resp) +void *obj) { +struct nsm_res *resp = obj;__be32 *p; p =xdr_inline_decode (xdr,4 );
@@ -583,8 +587,8 @@ static int nsm_xdr_dec_stat(struct rpc_rqst *rqstp,static struct rpc_procinfo nsm_procedures[] = { [NSMPROC_MON ] = { .p_proc =NSMPROC_MON , - .p_encode = (kxdreproc_t )nsm_xdr_enc_mon, - .p_decode = (kxdrdproc_t )nsm_xdr_dec_stat_res, + .p_encode = nsm_xdr_enc_mon, + .p_decode = nsm_xdr_dec_stat_res, .p_arglen = SM_mon_sz, .p_replen = SM_monres_sz, .p_statidx =NSMPROC_MON ,
@@ -592,8 +596,8 @@ static struct rpc_procinfo nsm_procedures[] = { }, [NSMPROC_UNMON] = { .p_proc = NSMPROC_UNMON, - .p_encode = (kxdreproc_t)nsm_xdr_enc_unmon, - .p_decode = (kxdrdproc_t)nsm_xdr_dec_stat, + .p_encode = nsm_xdr_enc_unmon, + .p_decode = nsm_xdr_dec_stat, .p_arglen = SM_mon_id_sz, .p_replen = SM_unmonres_sz, .p_statidx = NSMPROC_UNMON,
fs/nfs/mount_clnt.c +12 -9
@@ -306,8 +306,9 @@ static void encode_mntdirpath(struct xdr_stream *xdr, const char *pathname) }static void mnt_xdr_enc_dirpath (struct rpc_rqst *req,struct xdr_stream *xdr, -const char *dirpath) +void *obj) { +const char *dirpath = obj;encode_mntdirpath (xdr, dirpath); }
@@ -359,8 +360,9 @@ static int decode_fhandle(struct xdr_stream *xdr, struct mountres *res)static int mnt_xdr_dec_mountres (struct rpc_rqst *req,struct xdr_stream *xdr, -struct mountres *res) +void *obj) { +struct mountres *res = obj;int status; status =decode_status (xdr, res);
@@ -451,8 +453,9 @@ static int decode_auth_flavors(struct xdr_stream *xdr, struct mountres *res)static int mnt_xdr_dec_mountres3 (struct rpc_rqst *req,struct xdr_stream *xdr, -struct mountres *res) +void *obj) { +struct mountres *res = obj;int status; status =decode_fhs_status (xdr, res);
@@ -469,8 +472,8 @@ static int mnt_xdr_dec_mountres3(struct rpc_rqst *req,static struct rpc_procinfo mnt_procedures[] = { [MOUNTPROC_MNT ] = { .p_proc =MOUNTPROC_MNT , - .p_encode = (kxdreproc_t )mnt_xdr_enc_dirpath, - .p_decode = (kxdrdproc_t )mnt_xdr_dec_mountres, + .p_encode = mnt_xdr_enc_dirpath, + .p_decode = mnt_xdr_dec_mountres, .p_arglen = MNT_enc_dirpath_sz, .p_replen = MNT_dec_mountres_sz, .p_statidx =MOUNTPROC_MNT ,
@@ -478,7 +481,7 @@ static struct rpc_procinfo mnt_procedures[] = { }, [MOUNTPROC_UMNT] = { .p_proc = MOUNTPROC_UMNT, - .p_encode = (kxdreproc_t)mnt_xdr_enc_dirpath, + .p_encode = mnt_xdr_enc_dirpath, .p_arglen = MNT_enc_dirpath_sz, .p_statidx = MOUNTPROC_UMNT, .p_name = "UMOUNT",
@@ -488,8 +491,8 @@ static struct rpc_procinfo mnt_procedures[] = {static struct rpc_procinfo mnt3_procedures[] = { [MOUNTPROC3_MNT ] = { .p_proc =MOUNTPROC3_MNT , - .p_encode = (kxdreproc_t )mnt_xdr_enc_dirpath, - .p_decode = (kxdrdproc_t )mnt_xdr_dec_mountres3, + .p_encode = mnt_xdr_enc_dirpath, + .p_decode = mnt_xdr_dec_mountres3, .p_arglen = MNT_enc_dirpath_sz, .p_replen = MNT_dec_mountres3_sz, .p_statidx =MOUNTPROC3_MNT ,
@@ -497,7 +500,7 @@ static struct rpc_procinfo mnt3_procedures[] = { }, [MOUNTPROC3_UMNT] = { .p_proc = MOUNTPROC3_UMNT, - .p_encode = (kxdreproc_t)mnt_xdr_enc_dirpath, + .p_encode = mnt_xdr_enc_dirpath, .p_arglen = MNT_enc_dirpath_sz, .p_statidx = MOUNTPROC3_UMNT, .p_name = "UMOUNT",
fs/nfs/nfs2xdr.c +36 -19
178 lines not on this page · raw message
fs/nfs/nfs3xdr.c +80 -43
400 lines not on this page · raw message
fs/nfs/nfs4xdr.c +194 -98
971 lines not on this page · raw message
fs/nfsd/nfs4callback.c +10 -6
@@ -486,8 +486,9 @@ static void nfs4_xdr_enc_cb_null(struct rpc_rqst *req, struct xdr_stream *xdr,* 20.2. Operation 4: CB_RECALL - Recall a Delegation */ static void nfs4_xdr_enc_cb_recall (struct rpc_rqst *req,struct xdr_stream *xdr, -const struct nfsd4_callback *cb) +void *obj) { +const struct nfsd4_callback *cb = obj;const struct nfs4_delegation *dp =cb_to_delegation (cb);struct nfs4_cb_compound_hdr hdr = { .ident = cb->cb_clp->cl_cb_ident,
@@ -521,8 +522,9 @@ static int nfs4_xdr_dec_cb_null(struct rpc_rqst *req, struct xdr_stream *xdr,*/ static int nfs4_xdr_dec_cb_recall (struct rpc_rqst *rqstp,struct xdr_stream *xdr, -struct nfsd4_callback *cb) +void *obj) { +struct nfsd4_callback *cb = obj;struct nfs4_cb_compound_hdr hdr;enum nfsstat4 nfserr;int status;
@@ -601,8 +603,9 @@ static void encode_cb_layout4args(struct xdr_stream *xdr,static void nfs4_xdr_enc_cb_layout (struct rpc_rqst *req,struct xdr_stream *xdr, -const struct nfsd4_callback *cb) +void *obj) { +const struct nfsd4_callback *cb = obj;const struct nfs4_layout_stateid *ls = container_of(cb, struct nfs4_layout_stateid, ls_recall);struct nfs4_cb_compound_hdr hdr = {
@@ -618,8 +621,9 @@ static void nfs4_xdr_enc_cb_layout(struct rpc_rqst *req,static int nfs4_xdr_dec_cb_layout (struct rpc_rqst *rqstp,struct xdr_stream *xdr, -struct nfsd4_callback *cb) +void *obj) { +struct nfsd4_callback *cb = obj;struct nfs4_cb_compound_hdr hdr;enum nfsstat4 nfserr;int status;
@@ -648,8 +652,8 @@ out:#define PROC (proc, call, argtype, restype) \ [NFSPROC4_CLNT_##proc] = { \ .p_proc = NFSPROC4_CB_##call, \ - .p_encode = (kxdreproc_t)nfs4_xdr_enc_##argtype, \ - .p_decode = (kxdrdproc_t)nfs4_xdr_dec_##restype, \ + .p_encode = nfs4_xdr_enc_##argtype, \ + .p_decode = nfs4_xdr_dec_##restype, \ .p_arglen = NFS4_enc_##argtype##_sz, \ .p_replen = NFS4_dec_##restype##_sz, \ .p_statidx = NFSPROC4_CB_##call, \
include/linux/sunrpc/xdr.h +3 -2
@@ -210,8 +210,9 @@ struct xdr_stream {/* * These are the xdr_stream style generic XDR encode and decode functions. */ -typedef void (*kxdreproc_t )(void *rqstp,struct xdr_stream *xdr,void *obj); -typedef int (*kxdrdproc_t )(void *rqstp,struct xdr_stream *xdr,void *obj); +struct rpc_rqst ; +typedef void (*kxdreproc_t )(struct rpc_rqst *rqstp,struct xdr_stream *xdr,void *obj); +typedef int (*kxdrdproc_t )(struct rpc_rqst *rqstp,struct xdr_stream *xdr,void *obj);extern void xdr_init_encode (struct xdr_stream *xdr,struct xdr_buf *buf,__be32 *p);extern __be32 *xdr_reserve_space (struct xdr_stream *xdr,size_t nbytes);
net/sunrpc/auth_gss/gss_rpc_upcall.c +2 -2
@@ -55,8 +55,8 @@ enum {#define PROC (proc, name) \ [GSSX_##proc] = { \ .p_proc = GSSX_##proc, \ - .p_encode = (kxdreproc_t)gssx_enc_##name, \ - .p_decode = (kxdrdproc_t)gssx_dec_##name, \ + .p_encode = gssx_enc_##name, \ + .p_decode = gssx_dec_##name, \ .p_arglen = GSSX_ARG_##name##_sz, \ .p_replen = GSSX_RES_##name##_sz, \ .p_statidx = GSSX_##proc, \
net/sunrpc/auth_gss/gss_rpc_xdr.c +4 -2
@@ -733,8 +733,9 @@ static int gssx_enc_cb(struct xdr_stream *xdr, struct gssx_cb *cb)void gssx_enc_accept_sec_context (struct rpc_rqst *req,struct xdr_stream *xdr, -struct gssx_arg_accept_sec_context *arg) +void *obj) { +struct gssx_arg_accept_sec_context *arg = obj;int err; err =gssx_enc_call_ctx (xdr, &arg->call_ctx);
@@ -789,8 +790,9 @@ done:int gssx_dec_accept_sec_context (struct rpc_rqst *rqstp,struct xdr_stream *xdr, -struct gssx_res_accept_sec_context *res) +void *obj) { +struct gssx_res_accept_sec_context *res = obj;u32 value_follows;int err;
net/sunrpc/auth_gss/gss_rpc_xdr.h +2 -2
@@ -179,10 +179,10 @@ struct gssx_res_accept_sec_context {#define gssx_dec_init_sec_context NULLvoid gssx_enc_accept_sec_context (struct rpc_rqst *req,struct xdr_stream *xdr, -struct gssx_arg_accept_sec_context *args); +void *);int gssx_dec_accept_sec_context (struct rpc_rqst *rqstp,struct xdr_stream *xdr, -struct gssx_res_accept_sec_context *res); +void *);#define gssx_enc_release_handle NULL#define gssx_dec_release_handle NULL#define gssx_enc_get_mic NULL
net/sunrpc/clnt.c +2 -2
@@ -2390,11 +2390,11 @@ out_overflow: goto out_garbage; } -static void rpcproc_encode_null (void *rqstp,struct xdr_stream *xdr,void *obj) +static void rpcproc_encode_null (struct rpc_rqst *rqstp,struct xdr_stream *xdr,void *obj) { } -static int rpcproc_decode_null (void *rqstp,struct xdr_stream *xdr,void *obj) +static int rpcproc_decode_null (struct rpc_rqst *rqstp,struct xdr_stream *xdr,void *obj) {return 0 ; }
net/sunrpc/rpcb_clnt.c +28 -23
140 lines not on this page · raw message
Signature · 1 line
2.1.0
Re: [RFC v3 37/45] nfs/sunrpc: No more encode and decode function pointer casting
Quoted 5 lines · Andreas Gruenbacher
Instead of casting the encode and decode functions to the required type when assigning them to the
p_encodeandp_decodemembers of structrpc_procinfo, define the functions with their proper type and cast the additional obj argument to the appropriate type inside those functions. This allows slightly better type checking by the compiler at the cost of slightly more verbose code.
How is this even remotely relevant to ACL functionality, and why does it deserve to bypass the NFS tree?
Trond
Re: [RFC v3 37/45] nfs/sunrpc: No more encode and decode function pointer casting
2015-05-29 1:11 GMT+02:00 Trond Myklebust <[email protected]>:
Quoted 2 lines
How is this even remotely relevant to ACL functionality, and why does it deserve to bypass the NFS tree?
I've posted this to the linux-nfs mailing list for review among others, how is that
bypassing the NFS tree? Would you prefer those things sent to you personally as well?
This patch prepares for for the next one which changes the prototype of the encode
functions to return an error code. Without this patch, oversights in the next patch
would go unnoticed; with this patch, the compiler will complain.
Thanks,
Andreas
Re: [RFC v3 37/45] nfs/sunrpc: No more encode and decode function pointer casting
Quoted 8 lines · Andreas Grünbacher
2015-05-29 1:11 GMT+02:00 Trond Myklebust <[email protected]>:
Quoted 2 lines
How is this even remotely relevant to ACL functionality, and why does it deserve to bypass the NFS tree?
I've posted this to the linux-nfs mailing list for review among others, how is that
bypassing the NFS tree? Would you prefer those things sent to you personally as well?
No. I'm saying that changes that affect the core RPC code should not be going through external trees as part of an external feature; they should go through the maintainer trees.
Quoted 6 lines
This patch prepares for for the next one which changes the prototype of the encode
functions to return an error code. Without this patch, oversights in the next patch
would go unnoticed; with this patch, the compiler will complain.
See the comments to that patch too. There are precedents for doing what you are trying to accomplish, and they do not require changes to core code.
Trond
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 37/45] nfs/sunrpc: No more encode and decode function pointer casting
2015-05-29 2:37 GMT+02:00 Trond Myklebust <[email protected]>:
Quoted 6 lines
Quoted 4 lines · Andreas Grünbacher
2015-05-29 1:11 GMT+02:00 Trond Myklebust <[email protected]>:
Quoted 2 lines
How is this even remotely relevant to ACL functionality, and why does it deserve to bypass the NFS tree?
Quoted 8 lines
Quoted 4 lines
I've posted this to the linux-nfs mailing list for review among others, how is that
bypassing the NFS tree? Would you prefer those things sent to you personally as well?No. I'm saying that changes that affect the core RPC code should not be going through external trees as part of an external feature; they should go through the maintainer trees.
I agree, none of that is going to get merged directly.
Andreas
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC v3 38/45 nfs/sunrpc: Return status code from encode functions
In reply to Andreas GruenbacherReturn a status code from the sunrpc xdr encode functions. While these functions were originally returning a status code before commit 9f06c719 from December 2010, it was found that none of them can actually fail and so they were turned into void functions. With more complex objects like NFSv4 ACLs to encode, it makes sense to do memory allocations and some of the error checking inside the encode functions, so those functions may fail.
--- fs/lockd/clnt4xdr.c | 42 +++--- fs/lockd/clntxdr.c | 42 +++--- fs/lockd/mon.c | 10 +- fs/nfs/mount_clnt.c | 3 +- fs/nfs/nfs2xdr.c | 84 ++++++----- fs/nfs/nfs3xdr.c | 133 +++++++++-------- fs/nfs/nfs4xdr.c | 292 ++++++++++++++++++++++---------------- fs/nfsd/nfs4callback.c | 17 ++- include/linux/sunrpc/xdr.h | 2 +- net/sunrpc/auth.c | 7 +- net/sunrpc/auth_gss/gss_rpc_xdr.c | 7 +- net/sunrpc/auth_gss/gss_rpc_xdr.h | 6 +- net/sunrpc/clnt.c | 3 +- net/sunrpc/rpcb_clnt.c | 10 +- 14 files changed, 380 insertions(+), 278 deletions(-)
fs/lockd/clnt4xdr.c +24 -18
@@ -379,9 +379,9 @@ static void encode_nlm4_lock(struct xdr_stream *xdr,* struct nlm4_lock alock; * }; */ -static void nlm4_xdr_enc_testargs (struct rpc_rqst *req, -struct xdr_stream *xdr, -void *obj) +static int nlm4_xdr_enc_testargs (struct rpc_rqst *req, +struct xdr_stream *xdr, +void *obj) {const struct nlm_args *args = obj;const struct nlm_lock *lock = &args->lock;
@@ -389,6 +389,7 @@ static void nlm4_xdr_enc_testargs(struct rpc_rqst *req,encode_cookie (xdr, &args->cookie);encode_bool (xdr, lock->fl.fl_type ==F_WRLCK );encode_nlm4_lock (xdr, lock); +return 0 ; }/*
@@ -401,9 +402,9 @@ static void nlm4_xdr_enc_testargs(struct rpc_rqst *req,* int state; * }; */ -static void nlm4_xdr_enc_lockargs (struct rpc_rqst *req, -struct xdr_stream *xdr, -void *obj) +static int nlm4_xdr_enc_lockargs (struct rpc_rqst *req, +struct xdr_stream *xdr, +void *obj) {const struct nlm_args *args = obj;const struct nlm_lock *lock = &args->lock;
@@ -414,6 +415,7 @@ static void nlm4_xdr_enc_lockargs(struct rpc_rqst *req,encode_nlm4_lock (xdr, lock);encode_bool (xdr, args->reclaim);encode_int32 (xdr, args->state); +return 0 ; }/*
@@ -424,9 +426,9 @@ static void nlm4_xdr_enc_lockargs(struct rpc_rqst *req,* struct nlm4_lock alock; * }; */ -static void nlm4_xdr_enc_cancargs (struct rpc_rqst *req, -struct xdr_stream *xdr, -void *obj) +static int nlm4_xdr_enc_cancargs (struct rpc_rqst *req, +struct xdr_stream *xdr, +void *obj) {const struct nlm_args *args = obj;const struct nlm_lock *lock = &args->lock;
@@ -435,6 +437,7 @@ static void nlm4_xdr_enc_cancargs(struct rpc_rqst *req,encode_bool (xdr, args->block);encode_bool (xdr, lock->fl.fl_type ==F_WRLCK );encode_nlm4_lock (xdr, lock); +return 0 ; }/*
@@ -443,15 +446,16 @@ static void nlm4_xdr_enc_cancargs(struct rpc_rqst *req,* struct nlm4_lock alock; * }; */ -static void nlm4_xdr_enc_unlockargs (struct rpc_rqst *req, -struct xdr_stream *xdr, -void *obj) +static int nlm4_xdr_enc_unlockargs (struct rpc_rqst *req, +struct xdr_stream *xdr, +void *obj) {const struct nlm_args *args = obj;const struct nlm_lock *lock = &args->lock;encode_cookie (xdr, &args->cookie);encode_nlm4_lock (xdr, lock); +return 0 ; }/*
@@ -460,13 +464,14 @@ static void nlm4_xdr_enc_unlockargs(struct rpc_rqst *req,* nlm4_stat stat; * }; */ -static void nlm4_xdr_enc_res (struct rpc_rqst *req, -struct xdr_stream *xdr, -void *obj) +static int nlm4_xdr_enc_res (struct rpc_rqst *req, +struct xdr_stream *xdr, +void *obj) {const struct nlm_res *result = obj;encode_cookie (xdr, &result->cookie);encode_nlm4_stat (xdr, result->status); +return 0 ; }/*
@@ -482,15 +487,16 @@ static void nlm4_xdr_enc_res(struct rpc_rqst *req,* nlm4_testrply test_stat; * }; */ -static void nlm4_xdr_enc_testres (struct rpc_rqst *req, -struct xdr_stream *xdr, -void *obj) +static int nlm4_xdr_enc_testres (struct rpc_rqst *req, +struct xdr_stream *xdr, +void *obj) {const struct nlm_res *result = obj;encode_cookie (xdr, &result->cookie);encode_nlm4_stat (xdr, result->status);if (result->status == nlm_lck_denied)encode_nlm4_holder (xdr, result); +return 0 ; }
fs/lockd/clntxdr.c +24 -18
111 lines not on this page · raw message
fs/lockd/mon.c +6 -4
@@ -526,19 +526,21 @@ static void encode_priv(struct xdr_stream *xdr, const struct nsm_args *argp)xdr_encode_opaque_fixed (p, argp->priv->data,SM_PRIV_SIZE ); } -static void nsm_xdr_enc_mon (struct rpc_rqst *req,struct xdr_stream *xdr, -void *obj) +static int nsm_xdr_enc_mon (struct rpc_rqst *req,struct xdr_stream *xdr, +void *obj) {const struct nsm_args *argp = obj;encode_mon_id (xdr, argp);encode_priv (xdr, argp); +return 0 ; } -static void nsm_xdr_enc_unmon (struct rpc_rqst *req,struct xdr_stream *xdr, -void *obj) +static int nsm_xdr_enc_unmon (struct rpc_rqst *req,struct xdr_stream *xdr, +void *obj) {const struct nsm_args *argp = obj;encode_mon_id (xdr, argp); +return 0 ; } static int nsm_xdr_dec_stat_res(struct rpc_rqst *rqstp,
fs/nfs/mount_clnt.c +2 -1
@@ -305,11 +305,12 @@ static void encode_mntdirpath(struct xdr_stream *xdr, const char *pathname)xdr_encode_opaque (p, pathname, pathname_len); } -static void mnt_xdr_enc_dirpath (struct rpc_rqst *req,struct xdr_stream *xdr, +static int mnt_xdr_enc_dirpath (struct rpc_rqst *req,struct xdr_stream *xdr,void *obj) {const char *dirpath = obj;encode_mntdirpath (xdr, dirpath); +return 0 ; }/*
fs/nfs/nfs2xdr.c +48 -36
196 lines not on this page · raw message
fs/nfs/nfs3xdr.c +76 -57
324 lines not on this page · raw message
fs/nfs/nfs4xdr.c +171 -121
849 lines not on this page · raw message
fs/nfsd/nfs4callback.c +10 -7
@@ -476,17 +476,18 @@ out_default:/* * NB: Without this zero space reservation, callbacks over krb5p fail */ -static void nfs4_xdr_enc_cb_null (struct rpc_rqst *req,struct xdr_stream *xdr, -void *__unused) +static int nfs4_xdr_enc_cb_null (struct rpc_rqst *req,struct xdr_stream *xdr, +void *__unused) {xdr_reserve_space (xdr,0 ); +return 0 ; }/* * 20.2. Operation 4: CB_RECALL - Recall a Delegation */ -static void nfs4_xdr_enc_cb_recall (struct rpc_rqst *req,struct xdr_stream *xdr, -void *obj) +static int nfs4_xdr_enc_cb_recall (struct rpc_rqst *req,struct xdr_stream *xdr, +void *obj) {const struct nfsd4_callback *cb = obj;const struct nfs4_delegation *dp =cb_to_delegation (cb);
@@ -499,6 +500,7 @@ static void nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, struct xdr_stream *xdr,encode_cb_sequence4args (xdr, cb, &hdr);encode_cb_recall4args (xdr, dp, &hdr);encode_cb_nops (&hdr); +return 0 ; }
@@ -601,9 +603,9 @@ static void encode_cb_layout4args(struct xdr_stream *xdr, hdr->nops++; } -static void nfs4_xdr_enc_cb_layout (struct rpc_rqst *req, -struct xdr_stream *xdr, -void *obj) +static int nfs4_xdr_enc_cb_layout (struct rpc_rqst *req, +struct xdr_stream *xdr, +void *obj) {const struct nfsd4_callback *cb = obj; const struct nfs4_layout_stateid *ls =
@@ -617,6 +619,7 @@ static void nfs4_xdr_enc_cb_layout(struct rpc_rqst *req,encode_cb_sequence4args (xdr, cb, &hdr);encode_cb_layout4args (xdr, ls, &hdr);encode_cb_nops (&hdr); +return 0 ; } static int nfs4_xdr_dec_cb_layout(struct rpc_rqst *rqstp,
include/linux/sunrpc/xdr.h +1 -1
@@ -211,7 +211,7 @@ struct xdr_stream {* These are the xdr_stream style generic XDR encode and decode functions. */ struct rpc_rqst ; -typedef void (*kxdreproc_t )(struct rpc_rqst *rqstp,struct xdr_stream *xdr,void *obj); +typedef int (*kxdreproc_t )(struct rpc_rqst *rqstp,struct xdr_stream *xdr,void *obj);typedef int (*kxdrdproc_t )(struct rpc_rqst *rqstp,struct xdr_stream *xdr,void *obj);extern void xdr_init_encode (struct xdr_stream *xdr,struct xdr_buf *buf,__be32 *p);
net/sunrpc/auth.c +3 -4
@@ -767,13 +767,13 @@ rpcauth_checkverf(struct rpc_task *task, __be32 *p)return cred->cr_ops->crvalidate (task, p); } -static void rpcauth_wrap_req_encode (kxdreproc_t encode,struct rpc_rqst *rqstp, +static int rpcauth_wrap_req_encode (kxdreproc_t encode,struct rpc_rqst *rqstp,__be32 *data,void *obj) {struct xdr_stream xdr;xdr_init_encode (&xdr, &rqstp->rq_snd_buf, data); -encode (rqstp, &xdr, obj); +return encode (rqstp, &xdr, obj); }int
@@ -787,8 +787,7 @@ rpcauth_wrap_req(struct rpc_task *task, kxdreproc_t encode, void *rqstp,if (cred->cr_ops->crwrap_req)return cred->cr_ops->crwrap_req (task, encode, rqstp, data, obj);/* By default, we encode the arguments normally. */ -rpcauth_wrap_req_encode (encode, rqstp, data, obj); -return 0 ; +return rpcauth_wrap_req_encode (encode, rqstp, data, obj); } static int
net/sunrpc/auth_gss/gss_rpc_xdr.c +4 -3
@@ -731,9 +731,9 @@ static int gssx_enc_cb(struct xdr_stream *xdr, struct gssx_cb *cb)return err; } -void gssx_enc_accept_sec_context (struct rpc_rqst *req, -struct xdr_stream *xdr, -void *obj) +int gssx_enc_accept_sec_context (struct rpc_rqst *req, +struct xdr_stream *xdr, +void *obj) {struct gssx_arg_accept_sec_context *arg = obj;int err;
@@ -786,6 +786,7 @@ void gssx_enc_accept_sec_context(struct rpc_rqst *req, done:if (err)dprintk ("RPC: gssx_enc_accept_sec_context: %d\n" , err); +return 0 ; } int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp,
net/sunrpc/auth_gss/gss_rpc_xdr.h +3 -3
@@ -177,9 +177,9 @@ struct gssx_res_accept_sec_context {#define gssx_dec_store_cred NULL#define gssx_enc_init_sec_context NULL#define gssx_dec_init_sec_context NULL -void gssx_enc_accept_sec_context (struct rpc_rqst *req, -struct xdr_stream *xdr, -void *); +int gssx_enc_accept_sec_context (struct rpc_rqst *req, +struct xdr_stream *xdr, +void *);int gssx_dec_accept_sec_context (struct rpc_rqst *rqstp,struct xdr_stream *xdr,void *);
net/sunrpc/clnt.c +2 -1
@@ -2390,8 +2390,9 @@ out_overflow: goto out_garbage; } -static void rpcproc_encode_null (struct rpc_rqst *rqstp,struct xdr_stream *xdr,void *obj) +static int rpcproc_encode_null (struct rpc_rqst *rqstp,struct xdr_stream *xdr,void *obj) { +return 0 ; }static int rpcproc_decode_null (struct rpc_rqst *rqstp,struct xdr_stream *xdr,void *obj)
net/sunrpc/rpcb_clnt.c +6 -4
@@ -844,8 +844,8 @@ static void rpcb_getport_done(struct rpc_task *child, void *data)* XDR functions for rpcbind */ -static void rpcb_enc_mapping (struct rpc_rqst *req,struct xdr_stream *xdr, -void *obj) +static int rpcb_enc_mapping (struct rpc_rqst *req,struct xdr_stream *xdr, +void *obj) {const struct rpcbind_args *rpcb = obj;__be32 *p;
@@ -860,6 +860,7 @@ static void rpcb_enc_mapping(struct rpc_rqst *req, struct xdr_stream *xdr, *p++ =cpu_to_be32 (rpcb->r_vers); *p++ =cpu_to_be32 (rpcb->r_prot); *p =cpu_to_be32 (rpcb->r_port); +return 0 ; } static int rpcb_dec_getport(struct rpc_rqst *req, struct xdr_stream *xdr,
@@ -921,8 +922,8 @@ static void encode_rpcb_string(struct xdr_stream *xdr, const char *string,xdr_encode_opaque (p, string, len); } -static void rpcb_enc_getaddr (struct rpc_rqst *req,struct xdr_stream *xdr, -void *obj) +static int rpcb_enc_getaddr (struct rpc_rqst *req,struct xdr_stream *xdr, +void *obj) {const struct rpcbind_args *rpcb = obj;__be32 *p;
@@ -940,6 +941,7 @@ static void rpcb_enc_getaddr(struct rpc_rqst *req, struct xdr_stream *xdr,encode_rpcb_string (xdr, rpcb->r_netid,RPCBIND_MAXNETIDLEN );encode_rpcb_string (xdr, rpcb->r_addr,RPCBIND_MAXUADDRLEN );encode_rpcb_string (xdr, rpcb->r_owner,RPCB_MAXOWNERLEN ); +return 0 ; } static int rpcb_dec_getaddr(struct rpc_rqst *req, struct xdr_stream *xdr,
Signature · 1 line
2.1.0
Re: [RFC v3 38/45] nfs/sunrpc: Return status code from encode functions
Quoted 6 lines · Andreas Gruenbacher
Return a status code from the sunrpc xdr encode functions. While these functions were originally returning a status code before commit
9f06c719from December 2010, it was found that none of them can actually fail and so they were turned into void functions. With more complex objects like NFSv4 ACLs to encode, it makes sense to do memory allocations and some of the error checking inside the encode functions, so those functions may fail.
The standard way to do this is to allocate the xdr buffer and perform the xdr encoding outside the RPC call itself. Please see the layoutcommit function, for instance.
Trond
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC v3 39/45 nfs3: Return posix acl encode errors
In reply to Andreas GruenbacherWhen an error occurs when encoding a posix acl, return the error instead of causing a BUG().
--- fs/nfs/nfs3xdr.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
fs/nfs/nfs3xdr.c +4 -3
@@ -1384,13 +1384,14 @@ static int nfs3_xdr_enc_setacl3args(struct rpc_rqst *req, error =nfsacl_encode (xdr->buf, base, args->inode, (args->mask &NFS_ACL ) ? args->acl_access :NULL ,1 ,0 ); -/* FIXME: this is just broken */ -BUG_ON (error <0 ); +if (error <0 ) +return error; error =nfsacl_encode (xdr->buf, base + error, args->inode, (args->mask &NFS_DFACL ) ? args->acl_default :NULL ,1 ,NFS_ACL_DEFAULT ); -BUG_ON (error <0 ); +if (error <0 ) +return error;return 0 ; }
Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC v3 40/45 nfs: Remove unused xdr page offsets in getacl/setacl arguments
In reply to Andreas GruenbacherThe arguments passed around for getacl and setacl xdr encoding, struct nfs_setaclargs and struct nfs_getaclargs, both contain an array of pages, an offset into the first page, and the length of the page data. The offset is unused as it is always zero; remove it.
--- fs/nfs/nfs4proc.c | 5 ++--- fs/nfs/nfs4xdr.c | 4 ++-- include/linux/nfs_xdr.h | 2 -- 3 files changed, 4 insertions(+), 7 deletions(-)
fs/nfs/nfs4proc.c +2 -3
@@ -4440,7 +4440,7 @@ static inline int nfs4_server_supports_acls(struct nfs_server *server)#define NFS4ACL_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE)static int buf_to_pages_noslab (const void *buf,size_t buflen, -struct page **pages,unsigned int *pgbase) +struct page **pages) {struct page *newpage, **spages;int rc =0 ;
@@ -4584,7 +4584,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu goto out_free; args.acl_len = npages *PAGE_SIZE ; - args.acl_pgbase =0 ;dprintk ("%s buf %p buflen %zu npages %d args.acl_len %zu\n" , __func__, buf, buflen, npages, args.acl_len);
@@ -4676,7 +4675,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflreturn -EOPNOTSUPP ;if (npages >ARRAY_SIZE (pages))return -ERANGE ; - i =buf_to_pages_noslab (buf, buflen, arg.acl_pages, &arg.acl_pgbase); + i =buf_to_pages_noslab (buf, buflen, arg.acl_pages);if (i <0 )return i;nfs4_inode_return_delegation (inode);
fs/nfs/nfs4xdr.c +2 -2
@@ -1644,7 +1644,7 @@ encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compoun *p =cpu_to_be32 (FATTR4_WORD0_ACL ); p =reserve_space (xdr,4 ); *p =cpu_to_be32 (arg->acl_len); -xdr_write_pages (xdr, arg->acl_pages, arg->acl_pgbase, arg->acl_len); +xdr_write_pages (xdr, arg->acl_pages,0 , arg->acl_len); } static void
@@ -2517,7 +2517,7 @@ static int nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,encode_getattr_two (xdr,FATTR4_WORD0_ACL ,0 , &hdr);xdr_inline_pages (&req->rq_rcv_buf, replen <<2 , - args->acl_pages, args->acl_pgbase, args->acl_len); + args->acl_pages,0 , args->acl_len);encode_nops (&hdr);return 0 ;
include/linux/nfs_xdr.h +0 -2
@@ -640,7 +640,6 @@ struct nfs_setaclargs {struct nfs4_sequence_args seq_args;struct nfs_fh * fh;size_t acl_len; -unsigned int acl_pgbase;struct page ** acl_pages; };
@@ -652,7 +651,6 @@ struct nfs_getaclargs {struct nfs4_sequence_args seq_args;struct nfs_fh * fh;size_t acl_len; -unsigned int acl_pgbase;struct page ** acl_pages; };
Signature · 1 line
2.1.0
RFC v3 41/45 rpc: Allow to demand-allocate pages to encode into
In reply to Andreas GruenbacherWhen encoding large, variable-length objects such as acls into xdr_bufs, it is easier to allocate buffer pages on demand rather than computing the required buffer size beforehand.
--- net/sunrpc/xdr.c | 8 ++++++++ 1 file changed, 8 insertions(+)
net/sunrpc/xdr.c +8 -0
@@ -537,6 +537,14 @@ static __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr,*/ xdr->scratch.iov_base = xdr->p; xdr->scratch.iov_len = frag1bytes; + +if (!*xdr->page_ptr) { +struct page *page =alloc_page (GFP_KERNEL ); +if (!page) +return ERR_PTR (-ENOMEM ); + *xdr->page_ptr = page; + } + p =page_address (*xdr->page_ptr);/* * Note this is where the next encode will start after we've
Signature · 1 line
2.1.0
Re: [RFC v3 41/45] rpc: Allow to demand-allocate pages to encode into
Quoted 19 lines · Andreas Gruenbacher
When encoding large, variable-length objects such as acls into
xdr_bufs, it is easier to allocate buffer pages on demand rather than computing the required buffer size beforehand.Signed-off-by Andreas Gruenbacher <[email protected]>--- net/sunrpc/xdr.c | 8 ++++++++ 1 file changed, 8 insertions(+)net/sunrpc/xdr.c +1
-0@@ -537,6 +537,14 @@ static __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr,*/ xdr->scratch.iov_base = xdr->p; xdr->scratch.iov_len = frag1bytes; +
net/sunrpc/xdr.c static __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr, · line 550
Quoted 10 lines
net/sunrpc/xdr.c +7
-0+if (!*xdr->page_ptr) { +struct page *page =alloc_page (GFP_KERNEL ); +if (!page) +return ERR_PTR (-ENOMEM ); + *xdr->page_ptr = page; + } + p =page_address (*xdr->page_ptr);/* * Note this is where the next encode will start after we've
xdr_get_next_encode() should return NULL on failure, not ENOMEM. Why is this trying to do a GFP_KERNEL allocation inside an XDR routine anyway? That's not an I/O safe sleep.
Trond
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 41/45] rpc: Allow to demand-allocate pages to encode into
2015-05-29 1:24 GMT+02:00 Trond Myklebust <[email protected]>:
Quoted 3 lines
xdr_get_next_encode()should return NULL on failure, not ENOMEM. Why is this trying to do aGFP_KERNELallocation inside an XDR routine anyway? That's not an I/O safe sleep.
Fixed, thanks.
Andreas
RFC v3 42/45 nfs: Add richacl support
In reply to Andreas GruenbacherChanges nfs to support the "system.richacl" xattr instead of "system.nfs4_acl".
The "system.nfs4_acl" xattr nfs uses directly exposes the on-the-wire format of NFSv4's acl attribute to user space. This has at least two downsides: (1) the format is different from other file systems, so user-space code needs to be nfs filesystem aware; (2) when symbolic user@domain and group@domain names are used in the acl, user-space needs to perform ID mapping in the same way as the kernel.
Previously, when user-space requested only the length of the "system.nfs4_acl" attribute, nfs didn't need to retrieve the entire "system.nfs4_acl" attribute; retrieving its length was enough. With the "system.richacl" xattr, the length of "system.richacl" cannot be computed from the length of the NFSv4 acl attribute, so we always need to retrieve and cache the acl even when user-space only asks for the length of the "system.richacl" attribute.
Because the nfs client now knows which kind of acl the user is trying to set, it will now no longer sends acls with deny entries to servers which didn't declare support for that feature. The maximum supported size of the NFSv4 acl attribute is now hard coded in the client code and no longer depends on the size of the buffer the user provides to the getxattr system call. When an acl exceeds this limit, getxattr fails with errno set to ENOMEM.
--- fs/nfs/inode.c | 3 - fs/nfs/nfs4proc.c | 354 ++++++++++++++++++++-------------------------- fs/nfs/nfs4xdr.c | 224 +++++++++++++++++++++++++---- fs/nfs/super.c | 4 +- include/linux/nfs_fs.h | 1 - include/linux/nfs_fs_sb.h | 2 + include/linux/nfs_xdr.h | 8 +- 7 files changed, 357 insertions(+), 239 deletions(-)
fs/nfs/inode.c +0 -3
@@ -1824,9 +1824,6 @@ struct inode *nfs_alloc_inode(struct super_block *sb)return NULL ; nfsi->flags =0UL ; nfsi->cache_validity =0UL ; -#if IS_ENABLED (CONFIG_NFS_V4 ) - nfsi->nfs4_acl =NULL ; -#endif /* CONFIG_NFS_V4 */ return &nfsi->vfs_inode; }EXPORT_SYMBOL_GPL (nfs_alloc_inode);
fs/nfs/nfs4proc.c +151 -203
493 lines not on this page · raw message
fs/nfs/nfs4xdr.c +197 -27
291 lines not on this page · raw message
fs/nfs/super.c +2 -2
@@ -2318,7 +2318,7 @@ void nfs_fill_super(struct super_block *sb, struct nfs_mount_info *mount_info)/* The VFS shouldn't apply the umask to mode bits. We will do * so ourselves when necessary. */ - sb->s_flags |=MS_POSIXACL ; + sb->s_flags |=MS_RICHACL ; sb->s_time_gran =1 ; }
@@ -2345,7 +2345,7 @@ void nfs_clone_super(struct super_block *sb, struct nfs_mount_info *mount_info)/* The VFS shouldn't apply the umask to mode bits. We will do * so ourselves when necessary. */ - sb->s_flags |=MS_POSIXACL ; + sb->s_flags |=MS_RICHACL ; }nfs_initialise_sb (sb);
include/linux/nfs_fs.h +0 -1
@@ -176,7 +176,6 @@ struct nfs_inode {wait_queue_head_t waitqueue;#if IS_ENABLED (CONFIG_NFS_V4 ) -struct nfs4_cached_acl *nfs4_acl;/* NFSv4 state */ struct list_head open_states;struct nfs_delegation __rcu *delegation;
include/linux/nfs_fs_sb.h +2 -0
@@ -237,5 +237,7 @@ struct nfs_server {#define NFS_CAP_SEEK (1U << 19)#define NFS_CAP_ALLOCATE (1U << 20)#define NFS_CAP_DEALLOCATE (1U << 21) +#define NFS_CAP_ALLOW_ACLS (1U << 22) +#define NFS_CAP_DENY_ACLS (1U << 23)#endif
include/linux/nfs_xdr.h +5 -3
@@ -638,7 +638,9 @@ struct nfs_setattrargs {struct nfs_setaclargs {struct nfs4_sequence_args seq_args; +const struct nfs_server * server;struct nfs_fh * fh; +struct richacl * acl;size_t acl_len;struct page ** acl_pages; };
@@ -658,9 +660,9 @@ struct nfs_getaclargs {#define NFS4_ACL_TRUNC 0x0001/* ACL was truncated */ struct nfs_getaclres {struct nfs4_sequence_res seq_res; -size_t acl_len; -size_t acl_data_offset; -int acl_flags; +const struct nfs_server * server; +struct richacl * acl; +umode_t mode;struct page * acl_scratch; };
Signature · 1 line
2.1.0
Trond Myklebust
NAKRe: [RFC v3 42/45] nfs: Add richacl support
Quoted 2 lines · Andreas Gruenbacher
Changes nfs to support the "system.richacl" xattr instead of "system.nfs4_acl".
NACK.
You may declare a userspace syscall ABI that is more than 10 years old to be deprecated, but you are not allowed to remove it.
Furthermore, your entire premise of using the mode bits and the acl at the same time is flawed; you are at best trying to set up a private protocol to pass your mask info. According to RFC7530, Section 6.4.1.3, if you try to send the mode bits and ACL in the same SETATTR, then the result should be to apply the mode first, then override all the lower mode bits (i.e. everything except the suid, sgid and ) with the ACL...
Trond
Re: [RFC v3 42/45] nfs: Add richacl support
2015-05-29 1:06 GMT+02:00 Trond Myklebust <[email protected]>:
Quoted 9 lines
Quoted 2 lines · Andreas Gruenbacher
Changes nfs to support the "system.richacl" xattr instead of "system.nfs4_acl".
NACK.
You may declare a userspace syscall ABI that is more than 10 years old to be deprecated, but you are not allowed to remove it.
Okay, we can have "system.nfs4_acl" as well, it's just extra code.
Quoted 7 lines
Furthermore, your entire premise of using the mode bits and the acl at the same time is flawed; you are at best trying to set up a private protocol to pass your mask info. According to RFC7530, Section 6.4.1.3, if you try to send the mode bits and ACL in the same SETATTR, then the result should be to apply the mode first, then override all the lower mode bits (i.e. everything except the suid, sgid and ) with the ACL...
What do you mean? I'm happy with the behavior described in Section 6.4.1.3 of RFC 7530; I'm not using the mode bits and the acl at the same time. What makes you think otherwise?
Thanks,
Andreas
Re: [RFC v3 42/45] nfs: Add richacl support
In reply to Trond MyklebustQuoted 10 lines · Trond Myklebust
Quoted 2 lines · Andreas Gruenbacher
Changes nfs to support the "system.richacl" xattr instead of "system.nfs4_acl".
NACK.
You may declare a userspace syscall ABI that is more than 10 years old to be deprecated, but you are not allowed to remove it.
So having revisited the reasons why I chose the system.nfs4_acl interface when we did NFSv4 ACLs, I'm not sure we should implement system.richacl for the NFS client at all.
The problem is that you are 100% reliant on an accurate idmapper in order to convert the name@domain to a _correct_ uid/gid. It isn't sufficient to convert to just any valid uid/gid, because if your ACL tool is trying to edit the ACL, you can end up converting all those DENY modes for user '[email protected]' into DENY modes for user 'nobody'.
...and yes, libnfsidmap will happily convert all unknown user/groupnames into whatever uid/gid corresponds to 'nobody' without returning an error.
Your assertion that "when symbolic user@domain and group@domain names are used in the acl, user-space needs to perform ID mapping in the same way as the kernel" is WRONG. User space needs do no such thing, and that was the whole point of the interface; to allow the user to specify ACLs in a format that is checked only on the _server_, and not on the client.
Trond
Re: [RFC v3 42/45] nfs: Add richacl support
2015-05-29 15:15 GMT+02:00 Trond Myklebust <[email protected]>:
Quoted 11 lines
[reply reordered]
So having revisited the reasons why I chose the system.nfs4_acl interface when we did NFSv4 ACLs, I'm not sure we should implement system.richacl for the NFS client at all.Your assertion that "when symbolic user@domain and group@domain names are used in the acl, user-space needs to perform ID mapping in the same way as the kernel" is WRONG. User space needs do no such thing, and that was the whole point of the interface; to allow the user to specify ACLs in a format that is checked only on the _server_, and not on the client.
That's only half true. Right now, user-space applications trying to copy permissions between an nfs mount and another file system will fail unless the application has explicitly been made nfs aware and supports the "system.nfs4_acl"
attribute (as well as some other acl mechanism if the permissions go beyond the file mode).
The same problem exists when trying to make sense of acls.
It seems unreasonable to me to expect applications other than special file system maintenance tools to cater to such file system differences; there are just too many file systems out there for that to work. Instead, it would be better
to use an interface that can be generalized across file systems.
Quoted 9 lines
The problem is that you are 100% reliant on an accurate idmapper in order to convert the name@domain to a _correct_ uid/gid. It isn't sufficient to convert to just any valid uid/gid, because if your ACL tool is trying to edit the ACL, you can end up converting all those DENY modes for user '[email protected]' into DENY modes for user 'nobody'.
...and yes, libnfsidmap will happily convert all unknown user/groupnames into whatever uid/gid corresponds to 'nobody' without returning an error.
That's indeed a problem, and I can think of two ways of addressing it:
First, acl editors need to be careful about nobody entries; they need to be aware that nobody could actually stand for somebody else. (We could map unmappable users and groups to something else than nobody, but that might just shift the problem without improve anything.)
Second, we could add support for passing through unmappable Who values as is. But that raises the problem of how to pass thtough and represent different kinds of identifiers: NFSv4 users user@domain and group@domain strings; smb users SIDs; maybe more.
Thanks,
Andreas
Re: [RFC v3 42/45] nfs: Add richacl support
Adding linux-api...
Quoted 20 lines · Andreas Grünbacher
2015-05-29 15:15 GMT+02:00 Trond Myklebust <[email protected]>:
Quoted 11 lines
[reply reordered]
So having revisited the reasons why I chose the system.nfs4_acl interface when we did NFSv4 ACLs, I'm not sure we should implement system.richacl for the NFS client at all.Your assertion that "when symbolic user@domain and group@domain names are used in the acl, user-space needs to perform ID mapping in the same way as the kernel" is WRONG. User space needs do no such thing, and that was the whole point of the interface; to allow the user to specify ACLs in a format that is checked only on the _server_, and not on the client.
That's only half true. Right now, user-space applications trying to copy permissions between an nfs mount and another file system will fail unless the application has explicitly been made nfs aware and supports the "system.nfs4_acl"
attribute (as well as some other acl mechanism if the permissions go beyond the file mode).
Quoted 7 lines
The same problem exists when trying to make sense of acls.
It seems unreasonable to me to expect applications other than special file system maintenance tools to cater to such file system differences; there are just too many file systems out there for that to work. Instead, it would be better
to use an interface that can be generalized across file systems.
My point is that system.richacl is not such an interface. It can only ever work for local filesystems that understand and store local uids and gids. It has no support for the remote users/groups that are stored on your NFS/SMB server unless they happen to have a local mapping into uids and gids, and so the API is inappropriate to replace the existing NFSv4 acl API on the client.
Quoted 10 lines
Quoted 9 lines
The problem is that you are 100% reliant on an accurate idmapper in order to convert the name@domain to a _correct_ uid/gid. It isn't sufficient to convert to just any valid uid/gid, because if your ACL tool is trying to edit the ACL, you can end up converting all those DENY modes for user '[email protected]' into DENY modes for user 'nobody'.
...and yes, libnfsidmap will happily convert all unknown user/groupnames into whatever uid/gid corresponds to 'nobody' without returning an error.
Quoted 6 lines
That's indeed a problem, and I can think of two ways of addressing it:
First, acl editors need to be careful about nobody entries; they need to be aware that nobody could actually stand for somebody else. (We could map unmappable users and groups to something else than nobody, but that might just shift the problem without improve anything.)
What is the editor supposed to do about an entry it cannot even interpret, let alone store back to the server?
Quoted 5 lines
Second, we could add support for passing through unmappable Who values as is. But that raises the problem of how to pass thtough and represent different kinds of identifiers: NFSv4 users user@domain and group@domain strings; smb users SIDs; maybe more.
Now you have a mixture of some stuff that is being translated into local uid/gid format and therefore needs translating back when you're going to update the ACL, and some stuff that is not. What is the value of doing the mapping here?
Trond
Re: [RFC v3 42/45] nfs: Add richacl support
2015-05-29 17:24 GMT+02:00 Trond Myklebust <[email protected]>:
Quoted 6 lines
Quoted 5 lines
It seems unreasonable to me to expect applications other than special file system maintenance tools to cater to such file system differences; there are just too many file systems out there for that to work. Instead, it would be better
to use an interface that can be generalized across file systems.
Quoted 6 lines
My point is that system.richacl is not such an interface. It can only ever work for local filesystems that understand and store local uids and gids. It has no support for the remote users/groups that are stored on your NFS/SMB server unless they happen to have a local mapping into uids and gids, and so the API is inappropriate to replace the existing NFSv4 acl API on the client.
That can be changed if we find a reasonable solution.
Quoted 12 lines
Quoted 12 lines
Quoted 9 lines
The problem is that you are 100% reliant on an accurate idmapper in order to convert the name@domain to a _correct_ uid/gid. It isn't sufficient to convert to just any valid uid/gid, because if your ACL tool is trying to edit the ACL, you can end up converting all those DENY modes for user '[email protected]' into DENY modes for user 'nobody'.
...and yes, libnfsidmap will happily convert all unknown user/groupnames into whatever uid/gid corresponds to 'nobody' without returning an error.That's indeed a problem, and I can think of two ways of addressing it:
Quoted 7 lines
Quoted 4 lines
First, acl editors need to be careful about nobody entries; they need to be aware that nobody could actually stand for somebody else. (We could map unmappable users and groups to something else than nobody, but that might just shift the problem without improve anything.)
What is the editor supposed to do about an entry it cannot even interpret, let alone store back to the server?
In the end, it will have to be a user decision what to do about such entries, the editor can warn the user and make it harder to make mistakes though.
Quoted 9 lines
Quoted 4 lines
Second, we could add support for passing through unmappable Who values as is. But that raises the problem of how to pass thtough and represent different kinds of identifiers: NFSv4 users user@domain and group@domain strings; smb users SIDs; maybe more.
Now you have a mixture of some stuff that is being translated into local uid/gid format and therefore needs translating back when you're going to update the ACL, and some stuff that is not. What is the value of doing the mapping here?
If you don't translate, you cannot copy the permissions to another file system. In the ideal case, everything can be translated; where that fails, the user probably wants to know.
Thanks,
Andreas
Re: [RFC v3 42/45] nfs: Add richacl support
Quoted 44 lines · Andreas Grünbacher
2015-05-29 17:24 GMT+02:00 Trond Myklebust <[email protected]>:
Quoted 6 lines
Quoted 5 lines
It seems unreasonable to me to expect applications other than special file system maintenance tools to cater to such file system differences; there are just too many file systems out there for that to work. Instead, it would be better
to use an interface that can be generalized across file systems.Quoted 6 lines
My point is that system.richacl is not such an interface. It can only ever work for local filesystems that understand and store local uids and gids. It has no support for the remote users/groups that are stored on your NFS/SMB server unless they happen to have a local mapping into uids and gids, and so the API is inappropriate to replace the existing NFSv4 acl API on the client.
That can be changed if we find a reasonable solution.
Quoted 12 lines
Quoted 12 lines
Quoted 9 lines
The problem is that you are 100% reliant on an accurate idmapper in order to convert the name@domain to a _correct_ uid/gid. It isn't sufficient to convert to just any valid uid/gid, because if your ACL tool is trying to edit the ACL, you can end up converting all those DENY modes for user '[email protected]' into DENY modes for user 'nobody'.
...and yes, libnfsidmap will happily convert all unknown user/groupnames into whatever uid/gid corresponds to 'nobody' without returning an error.That's indeed a problem, and I can think of two ways of addressing it:
Quoted 7 lines
Quoted 4 lines
First, acl editors need to be careful about nobody entries; they need to be aware that nobody could actually stand for somebody else. (We could map unmappable users and groups to something else than nobody, but that might just shift the problem without improve anything.)
What is the editor supposed to do about an entry it cannot even interpret, let alone store back to the server?
In the end, it will have to be a user decision what to do about such entries, the editor can warn the user and make it harder to make mistakes though.
Quoted 5 lines
Quoted 4 lines
Second, we could add support for passing through unmappable Who values as is. But that raises the problem of how to pass thtough and represent different kinds of identifiers: NFSv4 users user@domain and group@domain strings; smb users SIDs; maybe more.
Quoted 8 lines
Quoted 4 lines
Now you have a mixture of some stuff that is being translated into local uid/gid format and therefore needs translating back when you're going to update the ACL, and some stuff that is not. What is the value of doing the mapping here?
If you don't translate, you cannot copy the permissions to another file system. In the ideal case, everything can be translated; where that fails, the user probably wants to know.
Why are we more worried about a hypothetical copy of the file to another filesystem than we are about representing the ACL correctly for the filesystem that the file is actually on? Your ACL on the remote SMB server windows.publicserver.com may not be fully represented when you copy the file to your local filesystem; get over it... However if you are authorised to edit the ACL on windows.publicserver.com, and you can't set it to something that can be enforced correctly by windows.publicserver.com, then you have a real problem.
Trond
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
RE: [RFC v3 42/45] nfs: Add richacl support
In reply to Trond MyklebustQuoted 32 lines
Quoted 1 line · Andreas Grünbacher
2015-05-29 15:15 GMT+02:00 Trond Myklebust
<trond.myklebust-7I+n7zu2hftEKMMhf/[email protected]>:
Quoted 6 lines
Quoted 6 lines
[reply reordered]
So having revisited the reasons why I chose the system.nfs4_acl interface when we did NFSv4 ACLs, I'm not sure we should implement system.richacl for the NFS client at all.Your assertion that "when symbolic user@domain and group@domain
names
Quoted 13 lines
Quoted 5 lines
are used in the acl, user-space needs to perform ID mapping in the same way as the kernel" is WRONG. User space needs do no such thing, and that was the whole point of the interface; to allow the user to specify ACLs in a format that is checked only on the _server_, and not on the client.
That's only half true. Right now, user-space applications trying to copy permissions between an nfs mount and another file system will fail unless the application has explicitly been made nfs aware and supports the "system.nfs4_acl"
attribute (as well as some other acl mechanism if the permissions go beyond the file mode).Quoted 7 lines
The same problem exists when trying to make sense of acls.
It seems unreasonable to me to expect applications other than special file system maintenance tools to cater to such file system differences; there are just too many file systems out there for that to work. Instead, it would be better to use an interface that can be generalized across file systems.
Quoted 5 lines
My point is that system.richacl is not such an interface. It can only ever work for local filesystems that understand and store local uids and gids. It has no support for the remote users/groups that are stored on your NFS/SMB server unless they happen to have a local mapping into uids and gids, and so the API is inappropriate to replace the existing NFSv4 acl API on the client.
Could we have both xattrs? Or a mount option that specifies which xattr to have?
That way folks who don't have local idmapping for every remote identity can use system.nfs4_acl while those who have local mapping for all remote identies and need to use a wide variety of tools can use system.richacl? system.richacl would obviously need to be documented that this issue can arise. But that will forever be an issue, unless we store the ACL with symbolic names, copying from a remote server to a local filesystem will always be lossy if the idmapping is incomplete.
Maybe that's too messy...
Frank
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [RFC v3 42/45] nfs: Add richacl support
In reply to Andreas GruenbacherQuoted 466 lines · Andreas Gruenbacher
Changes nfs to support the "system.richacl" xattr instead of "system.nfs4_acl".
The "system.nfs4_acl" xattr nfs uses directly exposes the on-the-wire format of NFSv4's acl attribute to user space. This has at least two downsides: (1) the format is different from other file systems, so user-space code needs to be nfs filesystem aware; (2) when symbolic user@domain and group@domain names are used in the acl, user-space needs to perform ID mapping in the same way as the kernel.
Previously, when user-space requested only the length of the "system.nfs4_acl" attribute, nfs didn't need to retrieve the entire "system.nfs4_acl" attribute; retrieving its length was enough. With the "system.richacl" xattr, the length of "system.richacl" cannot be computed from the length of the NFSv4 acl attribute, so we always need to retrieve and cache the acl even when user-space only asks for the length of the "system.richacl" attribute.
Because the nfs client now knows which kind of acl the user is trying to set, it will now no longer sends acls with deny entries to servers which didn't declare support for that feature. The maximum supported size of the NFSv4 acl attribute is now hard coded in the client code and no longer depends on the size of the buffer the user provides to the getxattr system call. When an acl exceeds this limit, getxattr fails with errno set to ENOMEM.
Signed-off-by Andreas Gruenbacher <[email protected]>---
fs/nfs/inode.c | 3 - fs/nfs/nfs4proc.c | 354 ++++++++++++++++++++-------------------------- fs/nfs/nfs4xdr.c | 224 +++++++++++++++++++++++++---- fs/nfs/super.c | 4 +- include/linux/nfs_fs.h | 1 - include/linux/nfs_fs_sb.h | 2 + include/linux/nfs_xdr.h | 8 +- 7 files changed, 357 insertions(+), 239 deletions(-)fs/nfs/inode.c +0
-3@@ -1824,9 +1824,6 @@ struct inode *nfs_alloc_inode(struct super_block *sb)return NULL ; nfsi->flags =0UL ; nfsi->cache_validity =0UL ; -#if IS_ENABLED (CONFIG_NFS_V4 ) - nfsi->nfs4_acl =NULL ; -#endif /* CONFIG_NFS_V4 */ return &nfsi->vfs_inode; }EXPORT_SYMBOL_GPL (nfs_alloc_inode);fs/nfs/nfs4proc.c +42
-146@@ -55,6 +55,8 @@#include <linux/xattr.h> #include <linux/utsname.h> #include <linux/freezer.h> +#include <linux/richacl.h> +#include <linux/richacl_xattr.h> #include "nfs4_fs.h" #include "delegation.h" @@ -2892,15 +2894,18 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f res.attr_bitmask[2 ] &=FATTR4_WORD2_NFS42_MASK ; }memcpy (server->attr_bitmask, res.attr_bitmask,sizeof (server->attr_bitmask)); - server->caps &= ~(NFS_CAP_ACLS |NFS_CAP_HARDLINKS | -NFS_CAP_SYMLINKS |NFS_CAP_FILEID | + server->caps &= ~(NFS_CAP_ALLOW_ACLS |NFS_CAP_DENY_ACLS | +NFS_CAP_HARDLINKS |NFS_CAP_SYMLINKS |NFS_CAP_FILEID |NFS_CAP_MODE |NFS_CAP_NLINK |NFS_CAP_OWNER |NFS_CAP_OWNER_GROUP |NFS_CAP_ATIME |NFS_CAP_CTIME |NFS_CAP_MTIME |NFS_CAP_SECURITY_LABEL ); -if (res.attr_bitmask[0 ] &FATTR4_WORD0_ACL && - res.acl_bitmask &ACL4_SUPPORT_ALLOW_ACL ) - server->caps |=NFS_CAP_ACLS ; +if (res.attr_bitmask[0 ] &FATTR4_WORD0_ACL ) { +if (res.acl_bitmask &ACL4_SUPPORT_ALLOW_ACL ) + server->caps |=NFS_CAP_ALLOW_ACLS ; +if (res.acl_bitmask &ACL4_SUPPORT_DENY_ACL ) + server->caps |=NFS_CAP_DENY_ACLS ; + }if (res.has_links !=0 ) server->caps |=NFS_CAP_HARDLINKS ;if (res.has_symlinks !=0 )@@ -4428,45 +4433,11 @@ static int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred)return 0 ; } -static inline int nfs4_server_supports_acls (struct nfs_server *server) -{ -return server->caps &NFS_CAP_ACLS ; -} - -/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that -* it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_SIZE) bytes on -* the stack. +/* A arbitrary limit; we allocate at most DIV_ROUND_UP(NFS4ACL_SIZE_MAX, +* PAGE_SIZE) pages and put an array of DIV_ROUND_UP(NFS4ACL_SIZE_MAX, +* PAGE_SIZE) pages on the stack when encoding or decoding acls. */ -#define NFS4ACL_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE) - -static int buf_to_pages_noslab (const void *buf,size_t buflen, -struct page **pages) -{ -struct page *newpage, **spages; -int rc =0 ; -size_t len; - spages = pages; - -do { - len =min_t (size_t,PAGE_SIZE , buflen); - newpage =alloc_page (GFP_KERNEL ); - -if (newpage ==NULL ) - goto unwind; -memcpy (page_address (newpage), buf, len); - buf += len; - buflen -= len; - *pages++ = newpage; - rc++; - }while (buflen !=0 ); - -return rc; - -unwind: -for (; rc >0 ; rc--) -__free_page (spages[rc-1 ]); -return -ENOMEM ; -} +#define NFS4ACL_SIZE_MAX 65536struct nfs4_cached_acl {int cached;@@ -4474,66 +4445,9 @@ struct nfs4_cached_acl {char data[0 ]; }; -static void nfs4_set_cached_acl (struct inode *inode,struct nfs4_cached_acl *acl) -{ -struct nfs_inode *nfsi =NFS_I (inode); - -spin_lock (&inode->i_lock); -kfree (nfsi->nfs4_acl); - nfsi->nfs4_acl = acl; -spin_unlock (&inode->i_lock); -} -static void nfs4_zap_acl_attr (struct inode *inode) { -nfs4_set_cached_acl (inode,NULL ); -} - -static inline ssize_t nfs4_read_cached_acl (struct inode *inode,char *buf,size_t buflen) -{ -struct nfs_inode *nfsi =NFS_I (inode); -struct nfs4_cached_acl *acl; -int ret = -ENOENT ; - -spin_lock (&inode->i_lock); - acl = nfsi->nfs4_acl; -if (acl ==NULL ) - goto out; -if (buf ==NULL )/* user is just asking for length */ - goto out_len; -if (acl->cached ==0 ) - goto out; - ret = -ERANGE ;/* see getxattr(2) man page */ -if (acl->len > buflen) - goto out; -memcpy (buf, acl->data, acl->len); -out_len: - ret = acl->len; -out: -spin_unlock (&inode->i_lock); -return ret; -} - -static void nfs4_write_cached_acl (struct inode *inode,struct page **pages,size_t pgbase,size_t acl_len) -{ -struct nfs4_cached_acl *acl; -size_t buflen =sizeof (*acl) + acl_len; - -if (buflen <=PAGE_SIZE ) { - acl =kmalloc (buflen,GFP_KERNEL ); -if (acl ==NULL ) - goto out; - acl->cached =1 ; -_copy_from_pages (acl->data, pages, pgbase, acl_len); - }else { - acl =kmalloc (sizeof (*acl),GFP_KERNEL ); -if (acl ==NULL ) - goto out; - acl->cached =0 ; - } - acl->len = acl_len; -out: -nfs4_set_cached_acl (inode, acl); +forget_cached_richacl (inode); }/* @@ -4546,121 +4460,128 @@ out:* length. The next getxattr call will then produce another round trip to * the server, this time with the input buf of the required size. */ -static ssize_t __nfs4_get_acl_uncached (struct inode *inode,void *buf,size_t buflen) +static struct richacl *__nfs4_get_acl_uncached (struct inode *inode) { -struct page *pages[NFS4ACL_MAXPAGES ] = {NULL , }; +struct nfs_server *server =NFS_SERVER (inode); +struct page *pages[DIV_ROUND_UP (NFS4ACL_SIZE_MAX ,PAGE_SIZE )] = {};struct nfs_getaclargs args = { .fh =NFS_FH (inode), .acl_pages = pages, - .acl_len = buflen, + .acl_len =ARRAY_SIZE (pages) *PAGE_SIZE , };struct nfs_getaclres res = { - .acl_len = buflen, + .server = server, };struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL ], .rpc_argp = &args, .rpc_resp = &res, }; -unsigned int npages =DIV_ROUND_UP (buflen,PAGE_SIZE ); -int ret = -ENOMEM , i; +mode_t mode; +int err, i; -/* As long as we're doing a round trip to the server anyway, -* let's be prepared for a page of acl data. */ -if (npages ==0 ) - npages =1 ; -if (npages >ARRAY_SIZE (pages)) -return -ERANGE ; - -for (i =0 ; i < npages; i++) { - pages[i] =alloc_page (GFP_KERNEL ); -if (!pages[i]) +if (ARRAY_SIZE (pages) >1 ) { +/* for decoding across pages */ + res.acl_scratch =alloc_page (GFP_KERNEL ); + err = -ENOMEM ; +if (!res.acl_scratch) goto out_free; } -/* for decoding across pages */ - res.acl_scratch =alloc_page (GFP_KERNEL ); -if (!res.acl_scratch) - goto out_free; - - args.acl_len = npages *PAGE_SIZE ; - -dprintk ("%s buf %p buflen %zu npages %d args.acl_len %zu\n" , - __func__, buf, buflen, npages, args.acl_len); - ret =nfs4_call_sync (NFS_SERVER (inode)->client,NFS_SERVER (inode), +dprintk ("%s args.acl_len %zu\n" , + __func__, args.acl_len); + err =nfs4_call_sync (NFS_SERVER (inode)->client,NFS_SERVER (inode), &msg, &args.seq_args, &res.seq_res,0 ); -if (ret) +if (err) goto out_free; -/* Handle the case where the passed-in buffer is too short */ -if (res.acl_flags &NFS4_ACL_TRUNC ) { -/* Did the user only issue a request for the acl length? */ -if (buf ==NULL ) - goto out_ok; - ret = -ERANGE ; - goto out_free; - } -nfs4_write_cached_acl (inode, pages, res.acl_data_offset, res.acl_len); -if (buf) { -if (res.acl_len > buflen) { - ret = -ERANGE ; - goto out_free; - } -_copy_from_pages (buf, pages, res.acl_data_offset, res.acl_len); - } -out_ok: - ret = res.acl_len; + mode = inode->i_mode &S_IFMT ; +if (__richacl_equiv_mode (res.acl, &mode) ==0 && + ((mode ^ res.mode) &S_IRWXUGO ) ==0 ) { +richacl_put (res.acl); + res.acl =NULL ; + }else +richacl_compute_max_masks (res.acl); +/* FIXME: Set inode->i_mode from res->mode? */ +set_cached_richacl (inode, res.acl); + err =0 ; +
out_free:fs/nfs/nfs4proc.c +6
-3-for (i =0 ; i < npages; i++) -if (pages[i]) -__free_page (pages[i]); +if (err) { +richacl_put (res.acl); + res.acl =ERR_PTR (err); + } +for (i =0 ; i <ARRAY_SIZE (pages) && pages[i]; i++) +__free_page (pages[i]);if (res.acl_scratch) __free_page(res.acl_scratch);fs/nfs/nfs4proc.c +1
-1-return ret; +return res.acl;}
fs/nfs/nfs4proc.c +1
-1-static ssize_t nfs4_get_acl_uncached (struct inode *inode,void *buf,size_t buflen) +static struct richacl *nfs4_get_acl_uncached (struct inode *inode){ struct nfs4_exception exception = { };fs/nfs/nfs4proc.c +1
-1-ssize_t ret; +struct richacl *acl;do {fs/nfs/nfs4proc.c +3
-3- ret =__nfs4_get_acl_uncached (inode, buf, buflen); -trace_nfs4_get_acl (inode, ret); -if (ret >=0 ) + acl =__nfs4_get_acl_uncached (inode); +trace_nfs4_get_acl (inode,IS_ERR (acl) ?PTR_ERR (acl) :0 ); +if (!IS_ERR (acl))break;fs/nfs/nfs4proc.c +1
-1- ret =nfs4_handle_exception (NFS_SERVER (inode), ret, &exception); + acl =ERR_PTR (nfs4_handle_exception (NFS_SERVER (inode),PTR_ERR (acl), &exception));} while (exception.retry);fs/nfs/nfs4proc.c +1
-1-return ret; +return acl;}
fs/nfs/nfs4proc.c +1
-1-static ssize_t nfs4_proc_get_acl (struct inode *inode,void *buf,size_t buflen) +static struct richacl *nfs4_proc_get_acl (struct inode *inode){ struct nfs_server *server = NFS_SERVER(inode);fs/nfs/nfs4proc.c +1
-0+struct richacl *acl;int ret;fs/nfs/nfs4proc.c +2
-2-if (!nfs4_server_supports_acls (server)) -return -EOPNOTSUPP ; +if (!(server->caps & (NFS_CAP_ALLOW_ACLS |NFS_CAP_DENY_ACLS ))) +return ERR_PTR (-EOPNOTSUPP );ret = nfs_revalidate_inode(server, inode); if (ret < 0)fs/nfs/nfs4proc.c +1
-1-return ret; +return ERR_PTR (ret);if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL) nfs_zap_acl_cache(inode);fs/nfs/nfs4proc.c +25
-6- ret =nfs4_read_cached_acl (inode, buf, buflen); -if (ret != -ENOENT ) -/* -ENOENT is returned if there is no ACL or if there is an ACL -* but no cached acl data, just the acl length */ -return ret; -return nfs4_get_acl_uncached (inode, buf, buflen); + acl =get_cached_richacl (inode); +if (acl !=ACL_NOT_CACHED ) +return acl; +return nfs4_get_acl_uncached (inode); +} + +static int +richacl_supported (struct nfs_server *server,struct richacl *acl) +{ +struct richace *ace; + +if (!(server->caps & (NFS_CAP_ALLOW_ACLS |NFS_CAP_DENY_ACLS ))) +return -EOPNOTSUPP ; + +richacl_for_each_entry (ace, acl) { +if (richace_is_allow (ace)) { +if (!(server->caps &NFS_CAP_ALLOW_ACLS )) +return -EINVAL ; + }else if (richace_is_deny (ace)) { +if (!(server->caps &NFS_CAP_DENY_ACLS )) +return -EINVAL ; + }else +return -EINVAL ; + } +return 0 ;}
fs/nfs/nfs4proc.c +1
-1-static int __nfs4_proc_set_acl (struct inode *inode,const void *buf,size_t buflen) +static int __nfs4_proc_set_acl (struct inode *inode,struct richacl *acl){ struct nfs_server *server = NFS_SERVER(inode);fs/nfs/nfs4proc.c +1
-1-struct page *pages[NFS4ACL_MAXPAGES ]; +struct page *pages[DIV_ROUND_UP (NFS4ACL_SIZE_MAX ,PAGE_SIZE ) +1 /* scratch */ ] = {};struct nfs_setaclargs arg = {fs/nfs/nfs4proc.c +1
-0+ .server = server,.fh = NFS_FH(inode),fs/nfs/nfs4proc.c +1
-0+ .acl = acl,.acl_pages = pages,fs/nfs/nfs4proc.c +1
-1- .acl_len = buflen, + .acl_len = ARRAY_SIZE(pages) * PAGE_SIZE,}; struct nfs_setaclres res; struct rpc_message msg = {fs/nfs/nfs4proc.c +13
-17@@ -4668,16 +4589,12 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl .rpc_argp = &arg, .rpc_resp = &res, }; -unsigned int npages =DIV_ROUND_UP (buflen,PAGE_SIZE );int ret, i; -if (!nfs4_server_supports_acls (server)) -return -EOPNOTSUPP ; -if (npages >ARRAY_SIZE (pages)) -return -ERANGE ; - i =buf_to_pages_noslab (buf, buflen, arg.acl_pages); -if (i <0 ) -return i; + ret =richacl_supported (server, acl); +if (ret) +return ret; +nfs4_inode_return_delegation (inode); ret =nfs4_call_sync (server->client, server, &msg, &arg.seq_args, &res.seq_res,1 );@@ -4685,8 +4602,8 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl* Free each page after tx, so the only ref left is * held by the network stack */ -for (; i >0 ; i--) -put_page (pages[i-1 ]); +for (i =0 ; pages[i]; i++) +put_page (pages[i]);/* * Acl update can result in inode attribute update. @@ -4700,12 +4617,12 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflreturn ret; } -static int nfs4_proc_set_acl (struct inode *inode,const void *buf,size_t buflen) +static int nfs4_proc_set_acl (struct inode *inode,struct richacl *acl) {struct nfs4_exception exception = { };int err; do { - err = __nfs4_proc_set_acl(inode, buf, buflen); + err = __nfs4_proc_set_acl(inode, acl); trace_nfs4_set_acl(inode, err); err = nfs4_handle_exception(NFS_SERVER(inode), err, &exception);@@ -6102,38 +6019,69 @@ nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_state *lsp)rpc_call_async (server->client, &msg,0 , &nfs4_release_lockowner_ops, data); } -#define XATTR_NAME_NFSV4_ACL "system.nfs4_acl" - -static int nfs4_xattr_set_nfs4_acl (struct dentry *dentry,const char *key, -const void *buf,size_t buflen, -int flags,int type) +static int nfs4_xattr_set_richacl (struct dentry *dentry,const char *key, +const void *buf,size_t buflen, +int flags,int handler_flags) { +struct richacl *acl; +int error;
fs/nfs/nfs4proc.c nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_state *lsp) · line 6036
Quoted 10 lines
fs/nfs/nfs4proc.c +6
-1+if (strcmp (key,"" ) !=0 )return -EINVAL ; -return nfs4_proc_set_acl (dentry->d_inode, buf, buflen); +if (buf) { + acl =richacl_from_xattr (&init_user_ns, buf, buflen); +if (IS_ERR (acl)) +return PTR_ERR (acl); + error =richacl_apply_masks (&acl);
Hi Andreas,
First, let me say thanks for all the work! We (Primary Data) have been using samba with the vfs_richacl module reexporting an nfsv4.2 mount and things are working pretty well. You can count on us for testing, bug fixing and code review.
Now for my question: It looks like this call to richacl_apply_masks in the client xattr_set path exists so that the knfsd permission check algorithm works correctly. This makes some pretty big assumptions about the server’s implementation. There are other servers out there besides knfsd!
I think this will have to be fixed before this patch can be accepted. I’m willing to help, but I’m wondering where this should be fixed:
Do we call richacl_apply_masks on the server before setting the xattr so the normalized acl is saved, or should we save the ACL as-is and call richacl_apply_masks before it’s used?
Thanks,
-dros
Quoted 464 lines
fs/nfs/nfs4proc.c +41
-15+ }else { +/* +* "Remove the acl"; only permissions granted by the mode +* remain. We are using the cached mode here which could be +* outdated; should we do a GETATTR first to narrow down the +* race window? +*/ + acl =richacl_from_mode_unmasked (dentry->d_inode->i_mode); + error =0 ; + } + +if (!error) + error =nfs4_proc_set_acl (dentry->d_inode, acl); +richacl_put (acl); +return error; } -static int nfs4_xattr_get_nfs4_acl (struct dentry *dentry,const char *key, -void *buf,size_t buflen,int type) +static int nfs4_xattr_get_richacl (struct dentry *dentry,const char *key, +void *buf,size_t buflen,int handler_flags) { +struct richacl *acl; +int error; +if (strcmp (key,"" ) !=0 )return -EINVAL ; -return nfs4_proc_get_acl (dentry->d_inode, buf, buflen); + acl =nfs4_proc_get_acl (dentry->d_inode); +if (IS_ERR (acl)) +return PTR_ERR (acl); +if (acl ==NULL ) +return -ENODATA ; + error =richacl_to_xattr (&init_user_ns, acl, buf, buflen); +richacl_put (acl); +return error; } -static size_t nfs4_xattr_list_nfs4_acl (struct dentry *dentry,char *list, -size_t list_len,const char *name, -size_t name_len,int type) +static size_t nfs4_xattr_list_richacl (struct dentry *dentry,char *list, +size_t list_len,const char *name, +size_t name_len,int handler_flags) { -size_t len =sizeof (XATTR_NAME_NFSV4_ACL ); +struct nfs_server *server =NFS_SERVER (dentry->d_inode); +size_t len =sizeof (XATTR_NAME_RICHACL ); -if (!nfs4_server_supports_acls (NFS_SERVER (dentry->d_inode))) +if (!(server->caps & (NFS_CAP_ALLOW_ACLS |NFS_CAP_DENY_ACLS )))return 0 ;if (list && len <= list_len) -memcpy (list,XATTR_NAME_NFSV4_ACL , len); +memcpy (list,XATTR_NAME_RICHACL , len);return len; }@@ -8656,15 +8604,15 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .clone_server = nfs_clone_server, }; -static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = { - .prefix =XATTR_NAME_NFSV4_ACL , - .list = nfs4_xattr_list_nfs4_acl, - .get = nfs4_xattr_get_nfs4_acl, - .set = nfs4_xattr_set_nfs4_acl, +static const struct xattr_handler nfs4_xattr_richacl_handler = { + .prefix =XATTR_NAME_RICHACL , + .list = nfs4_xattr_list_richacl, + .get = nfs4_xattr_get_richacl, + .set = nfs4_xattr_set_richacl, };const struct xattr_handler *nfs4_xattr_handlers[] = { - &nfs4_xattr_nfs4_acl_handler, + &nfs4_xattr_richacl_handler, #ifdef CONFIG_NFS_V4_SECURITY_LABEL &nfs4_xattr_nfs4_label_handler, #endiffs/nfs/nfs4xdr.c +120
-5@@ -53,6 +53,9 @@#include <linux/nfs4.h> #include <linux/nfs_fs.h> #include <linux/nfs_idmap.h> +#include <linux/richacl.h> +#include <linux/richacl_xattr.h> /* for RICHACL_XATTR_MAX_COUNT */ +#include <linux/nfs4acl.h> #include "nfs4_fs.h" #include "internal.h" @@ -1632,19 +1635,131 @@ encode_restorefh(struct xdr_stream *xdr, struct compound_hdr *hdr)encode_op_hdr (xdr,OP_RESTOREFH , decode_restorefh_maxsz, hdr); } -static void -encode_setacl (struct xdr_stream *xdr,struct nfs_setaclargs *arg,struct compound_hdr *hdr) +static int +nfs4_encode_user (struct xdr_stream *xdr,const struct nfs_server *server,kuid_t uid) +{ +char name[IDMAP_NAMESZ ]; +int len; +__be32 *p; + + len =nfs_map_uid_to_name (server, uid, name,IDMAP_NAMESZ ); +if (len <0 ) { +dprintk ("nfs: couldn't resolve uid %d to string\n" , +from_kuid (&init_user_ns, uid)); +return -ENOENT ; + } + p =xdr_reserve_space (xdr,4 + len); +if (!p) +return -EIO ; + p =xdr_encode_opaque (p, name, len); +return 0 ; +} + +static int +nfs4_encode_group (struct xdr_stream *xdr,const struct nfs_server *server,kgid_t gid) +{ +char name[IDMAP_NAMESZ ]; +int len; +__be32 *p; + + len =nfs_map_gid_to_group (server, gid, name,IDMAP_NAMESZ ); +if (len <0 ) { +dprintk ("nfs: couldn't resolve gid %d to string\n" , +from_kgid (&init_user_ns, gid)); +return -ENOENT ; + } + p =xdr_reserve_space (xdr,4 + len); +if (!p) +return -EIO ; + p =xdr_encode_opaque (p, name, len); +return 0 ; +} + +static int +nfs4_encode_ace_who (struct xdr_stream *xdr,const struct nfs_server *server, +struct richace *ace) {__be32 *p; +if (ace->e_flags &RICHACE_SPECIAL_WHO ) { +unsigned int special_id = ace->e_id.special; +const char *who; +unsigned int len; + +if (!nfs4acl_special_id_to_who (special_id, &who, &len)) { +WARN_ON_ONCE (1 ); +return -EIO ; + } + p =xdr_reserve_space (xdr,4 + len); +if (!p) +return -EIO ; + p =xdr_encode_opaque (p, who, len); +return 0 ; + } +if (ace->e_flags &RICHACE_IDENTIFIER_GROUP ) +return nfs4_encode_group (xdr, server, ace->e_id.gid); +else +return nfs4_encode_user (xdr, server, ace->e_id.uid); +} + +static int +encode_setacl (struct xdr_stream *xdr,struct nfs_setaclargs *arg,struct compound_hdr *hdr) +{ +int attrlen_offset; +__be32 attrlen, *p; +struct richace *ace; +encode_op_hdr (xdr,OP_SETATTR , decode_setacl_maxsz, hdr);encode_nfs4_stateid (xdr, &zero_stateid); + +/* Encode attribute bitmap. */ p =reserve_space (xdr,2 *4 ); *p++ =cpu_to_be32 (1 ); *p =cpu_to_be32 (FATTR4_WORD0_ACL ); - p =reserve_space (xdr,4 ); - *p =cpu_to_be32 (arg->acl_len); -xdr_write_pages (xdr, arg->acl_pages,0 , arg->acl_len); + + attrlen_offset = xdr->buf->len; + p =xdr_reserve_space (xdr,4 ); +if (!p) + goto fail; + p++;/* to be backfilled later */ + + p =xdr_reserve_space (xdr,4 ); +if (!p) + goto fail; +if (arg->acl ==NULL ) { + *p++ =cpu_to_be32 (0 ); +return 0 ; + } + *p++ =cpu_to_be32 (arg->acl->a_count); + +/* Add space for the acl entries. */ +xdr_inline_pages (xdr->buf, xdr->buf->len, arg->acl_pages,0 , arg->acl_len); + +if (arg->acl->a_flags) +return -EINVAL ; + +richacl_for_each_entry (ace, arg->acl) { +if (ace->e_flags &RICHACE_INHERITED_ACE ) +return -EINVAL ; +if (ace->e_mask & ~NFS4_ACE_MASK_ALL ) +return -EINVAL ; + + p =xdr_reserve_space (xdr,4 *3 ); +if (!p) + goto fail; + *p++ =cpu_to_be32 (ace->e_type); + *p++ =cpu_to_be32 (ace->e_flags & ~RICHACE_SPECIAL_WHO ); + *p++ =cpu_to_be32 (ace->e_mask &NFS4_ACE_MASK_ALL ); +if (nfs4_encode_ace_who (xdr, arg->server, ace) !=0 ) + goto fail; + } + + attrlen =htonl (xdr->buf->len - attrlen_offset -4 ); +write_bytes_to_xdr_buf (xdr->buf, attrlen_offset, &attrlen,4 ); +return 0 ; + +fail: +return -ENOMEM ; }static void
fs/nfs/nfs4xdr.c +73
-21@@ -2514,7 +2629,7 @@ static int nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,encode_sequence (xdr, &args->seq_args, &hdr);encode_putfh (xdr, args->fh, &hdr); replen = hdr.replen + op_decode_hdr_maxsz +1 ; -encode_getattr_two (xdr,FATTR4_WORD0_ACL ,0 , &hdr); +encode_getattr_two (xdr,FATTR4_WORD0_ACL ,FATTR4_WORD1_MODE , &hdr);xdr_inline_pages (&req->rq_rcv_buf, replen <<2 , args->acl_pages,0 , args->acl_len);@@ -5236,27 +5351,64 @@ decode_restorefh(struct xdr_stream *xdr)return decode_op_hdr (xdr,OP_RESTOREFH ); } +static int +nfs4_decode_ace_who (struct richace *ace,const struct nfs_server *server, +struct xdr_stream *xdr) +{ +char *who; +u32 len; +int special_id; +__be32 *p; +int error; + + p =xdr_inline_decode (xdr,4 ); +if (!p) +return -ENOMEM ;/* acl truncated */ + len =be32_to_cpup (p++); +if (len >=XDR_MAX_NETOBJ ) { +dprintk ("%s: name too long (%u)!\n" , + __func__, len); +return -EIO ; + } + who = (char *)xdr_inline_decode (xdr, len); +if (!who) +return -ENOMEM ;/* acl truncated */ + + special_id =nfs4acl_who_to_special_id (who, len); +if (special_id >=0 ) { + ace->e_flags |=RICHACE_SPECIAL_WHO ; + ace->e_flags &= ~RICHACE_IDENTIFIER_GROUP ; + ace->e_id.special = special_id; +return 0 ; + } +if (ace->e_flags &RICHACE_IDENTIFIER_GROUP ) { + error =nfs_map_group_to_gid (server, who, len, &ace->e_id.gid); +if (error) +dprintk ("%s: nfs_map_group_to_gid failed!\n" , + __func__); + }else { + error =nfs_map_name_to_uid (server, who, len, &ace->e_id.uid); +if (error) +dprintk ("%s: nfs_map_name_to_uid failed!\n" , + __func__); + } +return error; +} +static int decode_getacl (struct xdr_stream *xdr,struct rpc_rqst *req,struct nfs_getaclres *res) {static const uint32_t attrs_allowed[3 ] = { [0 ] =FATTR4_WORD0_ACL , + [1 ] =FATTR4_WORD1_MODE , };unsigned int savep;uint32_t attrlen, bitmap[3 ] = {0 };int status; -unsigned int pg_offset; - res->acl_len =0 ;if ((status =decode_op_hdr (xdr,OP_GETATTR )) !=0 ) goto out; - -xdr_enter_page (xdr, xdr->buf->page_len); - -/* Calculate the offset of the page data */ - pg_offset = xdr->buf->head[0 ].iov_len; -if ((status =decode_attr_bitmap (xdr, bitmap)) !=0 ) goto out;if ((status =verify_attrs_allowed (bitmap, attrs_allowed)) !=0 )@@ -5265,22 +5417,37 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, goto out;if (likely (bitmap[0 ] &FATTR4_WORD0_ACL )) { +struct richace *ace; +uint32_t count; +__be32 *p; -/* The bitmap (xdr len + bitmaps) and the attr xdr len words -* are stored with the acl data to handle the problem of -* variable length bitmaps.*/ - res->acl_data_offset =xdr_stream_pos (xdr) - pg_offset; - res->acl_len = attrlen; - -/* Check for receive buffer overflow */ -if (res->acl_len > (xdr->nwords <<2 ) || - res->acl_len + res->acl_data_offset > xdr->buf->page_len) { - res->acl_flags |=NFS4_ACL_TRUNC ; -dprintk ("NFS: acl reply: attrlen %u > page_len %u\n" , - attrlen, xdr->nwords <<2 ); + p =xdr_inline_decode (xdr,4 ); +if (unlikely (!p)) +return -ENOMEM ;/* acl truncated */ + count =be32_to_cpup (p); +if (count >RICHACL_XATTR_MAX_COUNT ) +return -EIO ; + res->acl =richacl_alloc (count,GFP_KERNEL ); +if (!res->acl) +return -ENOMEM ; +richacl_for_each_entry (ace, res->acl) { + p =xdr_inline_decode (xdr,4 *3 ); +if (unlikely (!p)) +return -ENOMEM ;/* acl truncated */ + ace->e_type =be32_to_cpup (p++); + ace->e_flags =be32_to_cpup (p++); +if (ace->e_flags &RICHACE_SPECIAL_WHO ) +return -EIO ; + ace->e_mask =be32_to_cpup (p++); + status =nfs4_decode_ace_who (ace, res->server, xdr); +if (status) + goto out; } }else status = -EOPNOTSUPP ; +if ((status =decode_attr_mode (xdr, bitmap, &res->mode)) <0 ) + goto out; + status =0 ;out:
return status;fs/nfs/nfs4xdr.c +4
-1@@ -6298,6 +6465,7 @@ out:static int nfs4_xdr_enc_setacl (struct rpc_rqst *req,struct xdr_stream *xdr,void *obj) { +int error;struct nfs_setaclargs *args = obj;struct compound_hdr hdr = { .minorversion =nfs4_xdr_minorversion (&args->seq_args),@@ -6306,7 +6474,9 @@ static int nfs4_xdr_enc_setacl(struct rpc_rqst *req, struct xdr_stream *xdr,encode_compound_hdr (xdr, req, &hdr);encode_sequence (xdr, &args->seq_args, &hdr);encode_putfh (xdr, args->fh, &hdr); -encode_setacl (xdr, args, &hdr); + error =encode_setacl (xdr, args, &hdr); +if (error) +return error;encode_nops (&hdr);return 0 ; }fs/nfs/super.c +2
-2@@ -2318,7 +2318,7 @@ void nfs_fill_super(struct super_block *sb, struct nfs_mount_info *mount_info)/* The VFS shouldn't apply the umask to mode bits. We will do * so ourselves when necessary. */ - sb->s_flags |=MS_POSIXACL ; + sb->s_flags |=MS_RICHACL ; sb->s_time_gran =1 ; }@@ -2345,7 +2345,7 @@ void nfs_clone_super(struct super_block *sb, struct nfs_mount_info *mount_info)/* The VFS shouldn't apply the umask to mode bits. We will do * so ourselves when necessary. */ - sb->s_flags |=MS_POSIXACL ; + sb->s_flags |=MS_RICHACL ; }nfs_initialise_sb (sb);include/linux/nfs_fs.h +0
-1@@ -176,7 +176,6 @@ struct nfs_inode {wait_queue_head_t waitqueue;#if IS_ENABLED (CONFIG_NFS_V4 ) -struct nfs4_cached_acl *nfs4_acl;/* NFSv4 state */ struct list_head open_states;struct nfs_delegation __rcu *delegation;include/linux/nfs_fs_sb.h +2
-0@@ -237,5 +237,7 @@ struct nfs_server {#define NFS_CAP_SEEK (1U << 19)#define NFS_CAP_ALLOCATE (1U << 20)#define NFS_CAP_DEALLOCATE (1U << 21) +#define NFS_CAP_ALLOW_ACLS (1U << 22) +#define NFS_CAP_DENY_ACLS (1U << 23)#endif include/linux/nfs_xdr.h +5
-3@@ -638,7 +638,9 @@ struct nfs_setattrargs {struct nfs_setaclargs {struct nfs4_sequence_args seq_args; +const struct nfs_server * server;struct nfs_fh * fh; +struct richacl * acl;size_t acl_len;struct page ** acl_pages; };@@ -658,9 +660,9 @@ struct nfs_getaclargs {#define NFS4_ACL_TRUNC 0x0001/* ACL was truncated */ struct nfs_getaclres {struct nfs4_sequence_res seq_res; -size_t acl_len; -size_t acl_data_offset; -int acl_flags; +const struct nfs_server * server; +struct richacl * acl; +umode_t mode;struct page * acl_scratch; };Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RE: [RFC v3 42/45] nfs: Add richacl support
Quoted 11 lines
First, let me say thanks for all the work! We (Primary Data) have been using samba with the
vfs_richaclmodule reexporting an nfsv4.2 mount and things are working pretty well. You can count on us for testing, bug fixing and code review.Now for my question: It looks like this call to
richacl_apply_masksin the clientxattr_setpath exists so that the knfsd permission check algorithm works correctly.
This makes some pretty big assumptions about the server’s implementation. There are other servers out there besides knfsd!
Quoted 6 lines
I think this will have to be fixed before this patch can be accepted. I’m willing to help, but I’m wondering where this should be fixed:
Do we call
richacl_apply_maskson the server before setting the xattr so the normalized acl is saved, or should we save the ACL as-is and callrichacl_apply_masksbefore it’s used?
I definitely want to hear more about this. At some point we will utilize RichACLs in Ganesha.
Frank
---
This email has been checked for viruses by Avast antivirus software.
https://www.avast.com/antivirus
Re: [RFC v3 42/45] nfs: Add richacl support
In reply to Weston Andros AdamsonHi Andy,
2016-06-23 18:37 GMT+02:00 Weston Andros Adamson <[email protected]>:
Quoted 477 lines
Quoted 464 lines · Andreas Gruenbacher
Changes nfs to support the "system.richacl" xattr instead of "system.nfs4_acl".
The "system.nfs4_acl" xattr nfs uses directly exposes the on-the-wire format of NFSv4's acl attribute to user space. This has at least two downsides: (1) the format is different from other file systems, so user-space code needs to be nfs filesystem aware; (2) when symbolic user@domain and group@domain names are used in the acl, user-space needs to perform ID mapping in the same way as the kernel.
Previously, when user-space requested only the length of the "system.nfs4_acl" attribute, nfs didn't need to retrieve the entire "system.nfs4_acl" attribute; retrieving its length was enough. With the "system.richacl" xattr, the length of "system.richacl" cannot be computed from the length of the NFSv4 acl attribute, so we always need to retrieve and cache the acl even when user-space only asks for the length of the "system.richacl" attribute.
Because the nfs client now knows which kind of acl the user is trying to set, it will now no longer sends acls with deny entries to servers which didn't declare support for that feature. The maximum supported size of the NFSv4 acl attribute is now hard coded in the client code and no longer depends on the size of the buffer the user provides to the getxattr system call. When an acl exceeds this limit, getxattr fails with errno set to ENOMEM.
Signed-off-by Andreas Gruenbacher <[email protected]>---
fs/nfs/inode.c | 3 - fs/nfs/nfs4proc.c | 354 ++++++++++++++++++++-------------------------- fs/nfs/nfs4xdr.c | 224 +++++++++++++++++++++++++---- fs/nfs/super.c | 4 +- include/linux/nfs_fs.h | 1 - include/linux/nfs_fs_sb.h | 2 + include/linux/nfs_xdr.h | 8 +- 7 files changed, 357 insertions(+), 239 deletions(-)fs/nfs/inode.c +0
-3@@ -1824,9 +1824,6 @@ struct inode *nfs_alloc_inode(struct super_block *sb)return NULL ; nfsi->flags =0UL ; nfsi->cache_validity =0UL ; -#if IS_ENABLED (CONFIG_NFS_V4 ) - nfsi->nfs4_acl =NULL ; -#endif /* CONFIG_NFS_V4 */ return &nfsi->vfs_inode; }EXPORT_SYMBOL_GPL (nfs_alloc_inode);fs/nfs/nfs4proc.c +42
-146@@ -55,6 +55,8 @@#include <linux/xattr.h> #include <linux/utsname.h> #include <linux/freezer.h> +#include <linux/richacl.h> +#include <linux/richacl_xattr.h> #include "nfs4_fs.h" #include "delegation.h" @@ -2892,15 +2894,18 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f res.attr_bitmask[2 ] &=FATTR4_WORD2_NFS42_MASK ; }memcpy (server->attr_bitmask, res.attr_bitmask,sizeof (server->attr_bitmask)); - server->caps &= ~(NFS_CAP_ACLS |NFS_CAP_HARDLINKS | -NFS_CAP_SYMLINKS |NFS_CAP_FILEID | + server->caps &= ~(NFS_CAP_ALLOW_ACLS |NFS_CAP_DENY_ACLS | +NFS_CAP_HARDLINKS |NFS_CAP_SYMLINKS |NFS_CAP_FILEID |NFS_CAP_MODE |NFS_CAP_NLINK |NFS_CAP_OWNER |NFS_CAP_OWNER_GROUP |NFS_CAP_ATIME |NFS_CAP_CTIME |NFS_CAP_MTIME |NFS_CAP_SECURITY_LABEL ); -if (res.attr_bitmask[0 ] &FATTR4_WORD0_ACL && - res.acl_bitmask &ACL4_SUPPORT_ALLOW_ACL ) - server->caps |=NFS_CAP_ACLS ; +if (res.attr_bitmask[0 ] &FATTR4_WORD0_ACL ) { +if (res.acl_bitmask &ACL4_SUPPORT_ALLOW_ACL ) + server->caps |=NFS_CAP_ALLOW_ACLS ; +if (res.acl_bitmask &ACL4_SUPPORT_DENY_ACL ) + server->caps |=NFS_CAP_DENY_ACLS ; + }if (res.has_links !=0 ) server->caps |=NFS_CAP_HARDLINKS ;if (res.has_symlinks !=0 )@@ -4428,45 +4433,11 @@ static int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred)return 0 ; } -static inline int nfs4_server_supports_acls (struct nfs_server *server) -{ -return server->caps &NFS_CAP_ACLS ; -} - -/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that -* it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_SIZE) bytes on -* the stack. +/* A arbitrary limit; we allocate at most DIV_ROUND_UP(NFS4ACL_SIZE_MAX, +* PAGE_SIZE) pages and put an array of DIV_ROUND_UP(NFS4ACL_SIZE_MAX, +* PAGE_SIZE) pages on the stack when encoding or decoding acls. */ -#define NFS4ACL_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE) - -static int buf_to_pages_noslab (const void *buf,size_t buflen, -struct page **pages) -{ -struct page *newpage, **spages; -int rc =0 ; -size_t len; - spages = pages; - -do { - len =min_t (size_t,PAGE_SIZE , buflen); - newpage =alloc_page (GFP_KERNEL ); - -if (newpage ==NULL ) - goto unwind; -memcpy (page_address (newpage), buf, len); - buf += len; - buflen -= len; - *pages++ = newpage; - rc++; - }while (buflen !=0 ); - -return rc; - -unwind: -for (; rc >0 ; rc--) -__free_page (spages[rc-1 ]); -return -ENOMEM ; -} +#define NFS4ACL_SIZE_MAX 65536struct nfs4_cached_acl {int cached;@@ -4474,66 +4445,9 @@ struct nfs4_cached_acl {char data[0 ]; }; -static void nfs4_set_cached_acl (struct inode *inode,struct nfs4_cached_acl *acl) -{ -struct nfs_inode *nfsi =NFS_I (inode); - -spin_lock (&inode->i_lock); -kfree (nfsi->nfs4_acl); - nfsi->nfs4_acl = acl; -spin_unlock (&inode->i_lock); -} -static void nfs4_zap_acl_attr (struct inode *inode) { -nfs4_set_cached_acl (inode,NULL ); -} - -static inline ssize_t nfs4_read_cached_acl (struct inode *inode,char *buf,size_t buflen) -{ -struct nfs_inode *nfsi =NFS_I (inode); -struct nfs4_cached_acl *acl; -int ret = -ENOENT ; - -spin_lock (&inode->i_lock); - acl = nfsi->nfs4_acl; -if (acl ==NULL ) - goto out; -if (buf ==NULL )/* user is just asking for length */ - goto out_len; -if (acl->cached ==0 ) - goto out; - ret = -ERANGE ;/* see getxattr(2) man page */ -if (acl->len > buflen) - goto out; -memcpy (buf, acl->data, acl->len); -out_len: - ret = acl->len; -out: -spin_unlock (&inode->i_lock); -return ret; -} - -static void nfs4_write_cached_acl (struct inode *inode,struct page **pages,size_t pgbase,size_t acl_len) -{ -struct nfs4_cached_acl *acl; -size_t buflen =sizeof (*acl) + acl_len; - -if (buflen <=PAGE_SIZE ) { - acl =kmalloc (buflen,GFP_KERNEL ); -if (acl ==NULL ) - goto out; - acl->cached =1 ; -_copy_from_pages (acl->data, pages, pgbase, acl_len); - }else { - acl =kmalloc (sizeof (*acl),GFP_KERNEL ); -if (acl ==NULL ) - goto out; - acl->cached =0 ; - } - acl->len = acl_len; -out: -nfs4_set_cached_acl (inode, acl); +forget_cached_richacl (inode); }/* @@ -4546,121 +4460,128 @@ out:* length. The next getxattr call will then produce another round trip to * the server, this time with the input buf of the required size. */ -static ssize_t __nfs4_get_acl_uncached (struct inode *inode,void *buf,size_t buflen) +static struct richacl *__nfs4_get_acl_uncached (struct inode *inode) { -struct page *pages[NFS4ACL_MAXPAGES ] = {NULL , }; +struct nfs_server *server =NFS_SERVER (inode); +struct page *pages[DIV_ROUND_UP (NFS4ACL_SIZE_MAX ,PAGE_SIZE )] = {};struct nfs_getaclargs args = { .fh =NFS_FH (inode), .acl_pages = pages, - .acl_len = buflen, + .acl_len =ARRAY_SIZE (pages) *PAGE_SIZE , };struct nfs_getaclres res = { - .acl_len = buflen, + .server = server, };struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL ], .rpc_argp = &args, .rpc_resp = &res, }; -unsigned int npages =DIV_ROUND_UP (buflen,PAGE_SIZE ); -int ret = -ENOMEM , i; +mode_t mode; +int err, i; -/* As long as we're doing a round trip to the server anyway, -* let's be prepared for a page of acl data. */ -if (npages ==0 ) - npages =1 ; -if (npages >ARRAY_SIZE (pages)) -return -ERANGE ; - -for (i =0 ; i < npages; i++) { - pages[i] =alloc_page (GFP_KERNEL ); -if (!pages[i]) +if (ARRAY_SIZE (pages) >1 ) { +/* for decoding across pages */ + res.acl_scratch =alloc_page (GFP_KERNEL ); + err = -ENOMEM ; +if (!res.acl_scratch) goto out_free; } -/* for decoding across pages */ - res.acl_scratch =alloc_page (GFP_KERNEL ); -if (!res.acl_scratch) - goto out_free; - - args.acl_len = npages *PAGE_SIZE ; - -dprintk ("%s buf %p buflen %zu npages %d args.acl_len %zu\n" , - __func__, buf, buflen, npages, args.acl_len); - ret =nfs4_call_sync (NFS_SERVER (inode)->client,NFS_SERVER (inode), +dprintk ("%s args.acl_len %zu\n" , + __func__, args.acl_len); + err =nfs4_call_sync (NFS_SERVER (inode)->client,NFS_SERVER (inode), &msg, &args.seq_args, &res.seq_res,0 ); -if (ret) +if (err) goto out_free; -/* Handle the case where the passed-in buffer is too short */ -if (res.acl_flags &NFS4_ACL_TRUNC ) { -/* Did the user only issue a request for the acl length? */ -if (buf ==NULL ) - goto out_ok; - ret = -ERANGE ; - goto out_free; - } -nfs4_write_cached_acl (inode, pages, res.acl_data_offset, res.acl_len); -if (buf) { -if (res.acl_len > buflen) { - ret = -ERANGE ; - goto out_free; - } -_copy_from_pages (buf, pages, res.acl_data_offset, res.acl_len); - } -out_ok: - ret = res.acl_len; + mode = inode->i_mode &S_IFMT ; +if (__richacl_equiv_mode (res.acl, &mode) ==0 && + ((mode ^ res.mode) &S_IRWXUGO ) ==0 ) { +richacl_put (res.acl); + res.acl =NULL ; + }else +richacl_compute_max_masks (res.acl); +/* FIXME: Set inode->i_mode from res->mode? */ +set_cached_richacl (inode, res.acl); + err =0 ; +
out_free:fs/nfs/nfs4proc.c +7
-4-for (i =0 ; i < npages; i++) -if (pages[i]) -__free_page (pages[i]); +if (err) { +richacl_put (res.acl); + res.acl =ERR_PTR (err); + } +for (i =0 ; i <ARRAY_SIZE (pages) && pages[i]; i++) +__free_page (pages[i]);if (res.acl_scratch)__free_page (res.acl_scratch); -return ret; +return res.acl;}
fs/nfs/nfs4proc.c +1
-1-static ssize_t nfs4_get_acl_uncached (struct inode *inode,void *buf,size_t buflen) +static struct richacl *nfs4_get_acl_uncached (struct inode *inode){ struct nfs4_exception exception = { };fs/nfs/nfs4proc.c +6
-6-ssize_t ret; +struct richacl *acl;do { - ret =__nfs4_get_acl_uncached (inode, buf, buflen); -trace_nfs4_get_acl (inode, ret); -if (ret >=0 ) + acl =__nfs4_get_acl_uncached (inode); +trace_nfs4_get_acl (inode,IS_ERR (acl) ?PTR_ERR (acl) :0 ); +if (!IS_ERR (acl))break ; - ret =nfs4_handle_exception (NFS_SERVER (inode), ret, &exception); + acl =ERR_PTR (nfs4_handle_exception (NFS_SERVER (inode),PTR_ERR (acl), &exception)); }while (exception.retry); -return ret; +return acl;}
fs/nfs/nfs4proc.c +1
-1-static ssize_t nfs4_proc_get_acl (struct inode *inode,void *buf,size_t buflen) +static struct richacl *nfs4_proc_get_acl (struct inode *inode){ struct nfs_server *server = NFS_SERVER(inode);fs/nfs/nfs4proc.c +29
-9+struct richacl *acl;int ret; -if (!nfs4_server_supports_acls (server)) -return -EOPNOTSUPP ; +if (!(server->caps & (NFS_CAP_ALLOW_ACLS |NFS_CAP_DENY_ACLS ))) +return ERR_PTR (-EOPNOTSUPP ); ret =nfs_revalidate_inode (server, inode);if (ret <0 ) -return ret; +return ERR_PTR (ret);if (NFS_I (inode)->cache_validity &NFS_INO_INVALID_ACL )nfs_zap_acl_cache (inode); - ret =nfs4_read_cached_acl (inode, buf, buflen); -if (ret != -ENOENT ) -/* -ENOENT is returned if there is no ACL or if there is an ACL -* but no cached acl data, just the acl length */ -return ret; -return nfs4_get_acl_uncached (inode, buf, buflen); + acl =get_cached_richacl (inode); +if (acl !=ACL_NOT_CACHED ) +return acl; +return nfs4_get_acl_uncached (inode); +} + +static int +richacl_supported (struct nfs_server *server,struct richacl *acl) +{ +struct richace *ace; + +if (!(server->caps & (NFS_CAP_ALLOW_ACLS |NFS_CAP_DENY_ACLS ))) +return -EOPNOTSUPP ; + +richacl_for_each_entry (ace, acl) { +if (richace_is_allow (ace)) { +if (!(server->caps &NFS_CAP_ALLOW_ACLS )) +return -EINVAL ; + }else if (richace_is_deny (ace)) { +if (!(server->caps &NFS_CAP_DENY_ACLS )) +return -EINVAL ; + }else +return -EINVAL ; + } +return 0 ;}
fs/nfs/nfs4proc.c +1
-1-static int __nfs4_proc_set_acl (struct inode *inode,const void *buf,size_t buflen) +static int __nfs4_proc_set_acl (struct inode *inode,struct richacl *acl){ struct nfs_server *server = NFS_SERVER(inode);fs/nfs/nfs4proc.c +17
-19-struct page *pages[NFS4ACL_MAXPAGES ]; +struct page *pages[DIV_ROUND_UP (NFS4ACL_SIZE_MAX ,PAGE_SIZE ) +1 /* scratch */ ] = {};struct nfs_setaclargs arg = { + .server = server, .fh =NFS_FH (inode), + .acl = acl, .acl_pages = pages, - .acl_len = buflen, + .acl_len =ARRAY_SIZE (pages) *PAGE_SIZE , };struct nfs_setaclres res;struct rpc_message msg = {@@ -4668,16 +4589,12 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl .rpc_argp = &arg, .rpc_resp = &res, }; -unsigned int npages =DIV_ROUND_UP (buflen,PAGE_SIZE );int ret, i; -if (!nfs4_server_supports_acls (server)) -return -EOPNOTSUPP ; -if (npages >ARRAY_SIZE (pages)) -return -ERANGE ; - i =buf_to_pages_noslab (buf, buflen, arg.acl_pages); -if (i <0 ) -return i; + ret =richacl_supported (server, acl); +if (ret) +return ret; +nfs4_inode_return_delegation (inode); ret =nfs4_call_sync (server->client, server, &msg, &arg.seq_args, &res.seq_res,1 );@@ -4685,8 +4602,8 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl* Free each page after tx, so the only ref left is * held by the network stack */ -for (; i >0 ; i--) -put_page (pages[i-1 ]); +for (i =0 ; pages[i]; i++) +put_page (pages[i]);/* * Acl update can result in inode attribute update. @@ -4700,12 +4617,12 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflreturn ret; } -static int nfs4_proc_set_acl (struct inode *inode,const void *buf,size_t buflen) +static int nfs4_proc_set_acl (struct inode *inode,struct richacl *acl) {struct nfs4_exception exception = { };int err; do { - err = __nfs4_proc_set_acl(inode, buf, buflen); + err = __nfs4_proc_set_acl(inode, acl); trace_nfs4_set_acl(inode, err); err = nfs4_handle_exception(NFS_SERVER(inode), err, &exception);@@ -6102,38 +6019,69 @@ nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_state *lsp)rpc_call_async (server->client, &msg,0 , &nfs4_release_lockowner_ops, data); } -#define XATTR_NAME_NFSV4_ACL "system.nfs4_acl" - -static int nfs4_xattr_set_nfs4_acl (struct dentry *dentry,const char *key, -const void *buf,size_t buflen, -int flags,int type) +static int nfs4_xattr_set_richacl (struct dentry *dentry,const char *key, +const void *buf,size_t buflen, +int flags,int handler_flags) { +struct richacl *acl; +int error;fs/nfs/nfs4proc.c nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_state *lsp) · line 6036
Quoted 10 lines
fs/nfs/nfs4proc.c +6
-1+if (strcmp (key,"" ) !=0 )return -EINVAL ; -return nfs4_proc_set_acl (dentry->d_inode, buf, buflen); +if (buf) { + acl =richacl_from_xattr (&init_user_ns, buf, buflen); +if (IS_ERR (acl)) +return PTR_ERR (acl); + error =richacl_apply_masks (&acl);Hi Andreas,
Quoted 3 lines
First, let me say thanks for all the work! We (Primary Data) have been using samba with the
vfs_richaclmodule reexporting an nfsv4.2 mount and things are working pretty well. You can count on us for testing, bug fixing and code review.
Thanks!
Quoted 11 lines
Now for my question: It looks like this call to
richacl_apply_masksin the clientxattr_setpath exists so that the knfsd permission check algorithm works correctly. This makes some pretty big assumptions about the server’s implementation. There are other servers out there besides knfsd!I think this will have to be fixed before this patch can be accepted. I’m willing to help, but I’m wondering where this should be fixed:
Do we call
richacl_apply_maskson the server before setting the xattr so the normalized acl is saved, or should we save the ACL as-is and callrichacl_apply_masksbefore it’s used?
nfs4_xattr_set_richacl is invoked client-side, by setting the "system.richacl" xattr, with a Richacl as the argument. The NFSv4 protocol doesn't know about file masks, so the Richacl must be translated into a pure NFSv4 ACL. This is what richacl_apply_masks effectively does. The resulting NFSv4 ACL is then sent to the server.
On the server side, some of the exported filesystems may support richacls. Before those can be sent to an NFSv4 client, richacl_apply_masks is used to turn the Richacl into an NFSv4 ACL as well.
(Ideally, we'd have an NFSv4 protocol extension that would allow us to preserve the file masks between servers and clients that understand them, and we could get rid of richacl_apply_masks in that case.)
Thanks,
Andreas
RFC v3 43/45 uapi/nfs: Add NFSv4.1 ACL definitions
In reply to Andreas GruenbacherAdd the ACL related protocol definitions which were added in the NFSv4.1 specifiction.
--- include/uapi/linux/nfs4.h | 7 +++++++ 1 file changed, 7 insertions(+)
include/uapi/linux/nfs4.h +7 -0
@@ -86,6 +86,10 @@#define ACL4_SUPPORT_AUDIT_ACL 0x04#define ACL4_SUPPORT_ALARM_ACL 0x08 +#define NFS4_ACL_AUTO_INHERIT 0x00000001 +#define NFS4_ACL_PROTECTED 0x00000002 +#define NFS4_ACL_DEFAULTED 0x00000004 +#define NFS4_ACE_FILE_INHERIT_ACE 0x00000001#define NFS4_ACE_DIRECTORY_INHERIT_ACE 0x00000002#define NFS4_ACE_NO_PROPAGATE_INHERIT_ACE 0x00000004
@@ -93,6 +97,7 @@#define NFS4_ACE_SUCCESSFUL_ACCESS_ACE_FLAG 0x00000010#define NFS4_ACE_FAILED_ACCESS_ACE_FLAG 0x00000020#define NFS4_ACE_IDENTIFIER_GROUP 0x00000040 +#define NFS4_ACE_INHERITED_ACE 0x00000080#define NFS4_ACE_READ_DATA 0x00000001#define NFS4_ACE_LIST_DIRECTORY 0x00000001
@@ -106,6 +111,8 @@#define NFS4_ACE_DELETE_CHILD 0x00000040#define NFS4_ACE_READ_ATTRIBUTES 0x00000080#define NFS4_ACE_WRITE_ATTRIBUTES 0x00000100 +#define NFS4_ACE_WRITE_RETENTION 0x00000200 +#define NFS4_ACE_WRITE_RETENTION_HOLD 0x00000400#define NFS4_ACE_DELETE 0x00010000#define NFS4_ACE_READ_ACL 0x00020000#define NFS4_ACE_WRITE_ACL 0x00040000
Signature · 1 line
2.1.0
Re: [RFC v3 43/45] uapi/nfs: Add NFSv4.1 ACL definitions
Quoted 2 lines · Andreas Gruenbacher
Add the ACL related protocol definitions which were added in the NFSv4.1 specifiction.
No harm in taking this now, I guess, I'll queue this one up for 4.2.
--b.
Quoted 45 lines
Signed-off-by Andreas Gruenbacher <[email protected]>--- include/uapi/linux/nfs4.h | 7 +++++++ 1 file changed, 7 insertions(+)include/uapi/linux/nfs4.h +7
-0@@ -86,6 +86,10 @@#define ACL4_SUPPORT_AUDIT_ACL 0x04#define ACL4_SUPPORT_ALARM_ACL 0x08 +#define NFS4_ACL_AUTO_INHERIT 0x00000001 +#define NFS4_ACL_PROTECTED 0x00000002 +#define NFS4_ACL_DEFAULTED 0x00000004 +#define NFS4_ACE_FILE_INHERIT_ACE 0x00000001#define NFS4_ACE_DIRECTORY_INHERIT_ACE 0x00000002#define NFS4_ACE_NO_PROPAGATE_INHERIT_ACE 0x00000004@@ -93,6 +97,7 @@#define NFS4_ACE_SUCCESSFUL_ACCESS_ACE_FLAG 0x00000010#define NFS4_ACE_FAILED_ACCESS_ACE_FLAG 0x00000020#define NFS4_ACE_IDENTIFIER_GROUP 0x00000040 +#define NFS4_ACE_INHERITED_ACE 0x00000080#define NFS4_ACE_READ_DATA 0x00000001#define NFS4_ACE_LIST_DIRECTORY 0x00000001@@ -106,6 +111,8 @@#define NFS4_ACE_DELETE_CHILD 0x00000040#define NFS4_ACE_READ_ATTRIBUTES 0x00000080#define NFS4_ACE_WRITE_ATTRIBUTES 0x00000100 +#define NFS4_ACE_WRITE_RETENTION 0x00000200 +#define NFS4_ACE_WRITE_RETENTION_HOLD 0x00000400#define NFS4_ACE_DELETE 0x00010000#define NFS4_ACE_READ_ACL 0x00020000#define NFS4_ACE_WRITE_ACL 0x00040000Signature · 6 lines
2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC v3 44/45 nfsd: Add support for the v4.1 dacl attribute
In reply to Andreas GruenbacherRichacls support the Automatic Inheritance permission propagation mechanism as specified in NFSv4.1. Over NFS, this requires support for the dacl attribute:
compared to the acl attribute, the dacl attribute has an additional flags field which indicates when Automatic Inheritance is in use.
The server will only indicate dacl attribute support in protocol version 4.1 and later, on file systems with richacl support.
This commit also adds support for the NFSv4.1 NFS4_ACE_WRITE_RETENTION and NFS4_ACE_WRITE_RETENTION_HOLD ACL permissions.
--- fs/nfsd/nfs4xdr.c | 219 ++++++++++++++++++++++++++++++---------------- fs/nfsd/nfsd.h | 6 +- include/linux/nfs4.h | 1 + include/uapi/linux/nfs4.h | 3 +- 4 files changed, 153 insertions(+), 76 deletions(-)
fs/nfsd/nfs4xdr.c +146 -73
@@ -300,6 +300,68 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)DECODE_TAIL ; } +static unsigned int +nfsd4_ace_mask (int minorversion) +{ +return minorversion ==0 ?NFS40_ACE_MASK_ALL :NFS4_ACE_MASK_ALL ; +} + +static __be32 +nfsd4_decode_acl_entries (struct nfsd4_compoundargs *argp,struct richacl **acl, +unsigned short flags_mask,unsigned int ace_mask, +int *plen) +{ +struct richace *ace; +u32 dummy32; +char *buf; +int len =0 ; + +DECODE_HEAD ; + + flags_mask &=RICHACE_VALID_FLAGS & ~RICHACE_SPECIAL_WHO ; + +READ_BUF (4 ); len +=4 ; + dummy32 =be32_to_cpup (p++); + +if (dummy32 >NFSD4_ACL_MAX ) +return nfserr_fbig; + + *acl =svcxdr_alloc_richacl (argp, dummy32); +if (*acl ==NULL ) +return nfserr_jukebox; + +richacl_for_each_entry (ace, *acl) { +READ_BUF (16 ); len +=16 ; + + dummy32 =be32_to_cpup (p++); +if (dummy32 >RICHACE_ACCESS_DENIED_ACE_TYPE ) +return nfserr_inval; + ace->e_type = dummy32; + + dummy32 =be32_to_cpup (p++); +if (dummy32 & ~flags_mask) +return nfserr_inval; + ace->e_flags = dummy32; + + dummy32 =be32_to_cpup (p++); +if (dummy32 & ~ace_mask) +return nfserr_inval; + ace->e_mask = dummy32; + + dummy32 =be32_to_cpup (p++); +READ_BUF (dummy32); + len +=XDR_QUADLEN (dummy32) <<2 ; +READMEM (buf, dummy32); + status =nfsd4_decode_ace_who (ace, argp->rqstp, + buf, dummy32); +if (status) +return status; + } + *plen += len; + +DECODE_TAIL ; +} + static __be32 nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *iattr, struct richacl **acl,
@@ -311,6 +373,7 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,DECODE_HEAD ; iattr->ia_valid =0 ; + *acl =NULL ;if ((status =nfsd4_decode_bitmap (argp, bmval)))return status;
@@ -324,50 +387,18 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, iattr->ia_valid |=ATTR_SIZE ; }if (bmval[0 ] &FATTR4_WORD0_ACL ) { -u32 nace; -struct richace *ace; - -READ_BUF (4 ); len +=4 ; - nace =be32_to_cpup (p++); - -if (nace >NFSD4_ACL_MAX ) -return nfserr_fbig; +if (bmval[1 ] &FATTR4_WORD1_DACL ) +return nfserr_inval; - *acl =svcxdr_alloc_richacl (argp, nace); -if (*acl ==NULL ) + status =nfsd4_decode_acl_entries (argp, acl, + ~NFS4_ACE_INHERITED_ACE , +nfsd4_ace_mask (argp->minorversion), + &len); +if (status) +return status; +else if (*acl ==NULL )return nfserr_jukebox; - -richacl_for_each_entry (ace, *acl) { -READ_BUF (16 ); len +=16 ; - - dummy32 =be32_to_cpup (p++); -if (dummy32 >RICHACE_ACCESS_DENIED_ACE_TYPE ) -return nfserr_inval; - ace->e_type = dummy32; - - dummy32 =be32_to_cpup (p++); -if (dummy32 & (~RICHACE_VALID_FLAGS | -RICHACE_INHERITED_ACE | -RICHACE_SPECIAL_WHO )) -return nfserr_inval; - ace->e_flags = dummy32; - - dummy32 =be32_to_cpup (p++); -if (dummy32 & ~NFS4_ACE_MASK_ALL ) -return nfserr_inval; - ace->e_mask = dummy32; - - dummy32 =be32_to_cpup (p++); -READ_BUF (dummy32); - len +=XDR_QUADLEN (dummy32) <<2 ; -READMEM (buf, dummy32); - status =nfsd4_decode_ace_who (ace, argp->rqstp, - buf, dummy32); -if (status) -return status; - } - }else - *acl =NULL ; + }if (bmval[1 ] &FATTR4_WORD1_MODE ) {READ_BUF (4 ); len +=4 ;
@@ -435,6 +466,22 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, goto xdr_error; } } +if (bmval[1 ] &FATTR4_WORD1_DACL ) { +READ_BUF (4 ); + len +=4 ; + dummy32 =be32_to_cpup (p++); +if (dummy32 & (~RICHACL_VALID_FLAGS |RICHACL_MASKED )) +return nfserr_inval; + status =nfsd4_decode_acl_entries (argp, acl, + ~0 , +nfsd4_ace_mask (argp->minorversion), + &len); +if (status) +return status; +else if (*acl ==NULL ) +return nfserr_jukebox; + (*acl)->a_flags = dummy32; + } label->len =0 ;#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
@@ -2215,6 +2262,42 @@ static int get_parent_attributes(struct svc_export *exp, struct kstat *stat)return err; } +static __be32 nfsd4_encode_acl_entries (struct xdr_stream *xdr, +struct richacl *acl,struct svc_rqst *rqstp, +unsigned short flags_mask,unsigned int ace_mask) +{ +__be32 *p; + + flags_mask &= ~RICHACE_SPECIAL_WHO ; + + p =xdr_reserve_space (xdr,4 ); +if (!p) +return nfserr_resource; + +if (acl ==NULL ) { + *p++ =cpu_to_be32 (0 ); + }else { +struct richace *ace; + + *p++ =cpu_to_be32 (acl->a_count); + +richacl_for_each_entry (ace, acl) { +__be32 status; + + p =xdr_reserve_space (xdr,4 *3 ); +if (!p) +return nfserr_resource; + *p++ =cpu_to_be32 (ace->e_type); + *p++ =cpu_to_be32 (ace->e_flags & flags_mask); + *p++ =cpu_to_be32 (ace->e_mask & ace_mask); + status =nfsd4_encode_ace_who (xdr, rqstp, ace); +if (status) +return status; + } + } +return 0 ; +} +/* * Note: @fhp can be NULL; in this case, we might have to compose the filehandle * ourselves.
@@ -2286,15 +2369,16 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp, goto out; fhp = tempfh; } -if (bmval0 &FATTR4_WORD0_ACL ) { +if ((bmval0 &FATTR4_WORD0_ACL ) || (bmval1 &FATTR4_WORD1_DACL )) { acl =nfsd4_get_acl (rqstp, dentry);if (IS_ERR (acl)) { err =PTR_ERR (acl); acl =NULL ; } -if (err == -EOPNOTSUPP ) +if (err == -EOPNOTSUPP ) { bmval0 &= ~FATTR4_WORD0_ACL ; -else if (err == -EINVAL ) { + bmval1 &= ~FATTR4_WORD1_DACL ; + }else if (err == -EINVAL ) { status = nfserr_attrnotsupp; goto out; }else if (err !=0 )
@@ -2352,6 +2436,8 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,if (!IS_ACL (dentry->d_inode)) word0 &= ~FATTR4_WORD0_ACL ; +if (!IS_RICHACL (dentry->d_inode)) + word1 &= ~FATTR4_WORD1_DACL ;if (!contextsupport) word2 &= ~FATTR4_WORD2_SECURITY_LABEL ;if (!word2) {
@@ -2465,35 +2551,12 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp, *p++ =cpu_to_be32 (rdattr_err); }if (bmval0 &FATTR4_WORD0_ACL ) { -struct richace *ace; - -if (acl ==NULL ) { - p =xdr_reserve_space (xdr,4 ); -if (!p) - goto out_resource; - - *p++ =cpu_to_be32 (0 ); - goto out_acl; - } - p =xdr_reserve_space (xdr,4 ); -if (!p) - goto out_resource; - *p++ =cpu_to_be32 (acl->a_count); - -richacl_for_each_entry (ace, acl) { - p =xdr_reserve_space (xdr,4 *3 ); -if (!p) - goto out_resource; - *p++ =cpu_to_be32 (ace->e_type); - *p++ =cpu_to_be32 (ace->e_flags & - ~(RICHACE_SPECIAL_WHO |RICHACE_INHERITED_ACE )); - *p++ =cpu_to_be32 (ace->e_mask &NFS4_ACE_MASK_ALL ); - status =nfsd4_encode_ace_who (xdr, rqstp, ace); -if (status) - goto out; - } + status =nfsd4_encode_acl_entries (xdr, acl, rqstp, + ~NFS4_ACE_INHERITED_ACE , +nfsd4_ace_mask (minorversion)); +if (status) + goto out; } -out_acl:if (bmval0 &FATTR4_WORD0_ACLSUPPORT ) { p =xdr_reserve_space (xdr,4 );if (!p)
@@ -2702,6 +2765,16 @@ out_acl:get_parent_attributes (exp, &stat); p =xdr_encode_hyper (p, stat.ino); } +if (bmval1 &FATTR4_WORD1_DACL ) { + p =xdr_reserve_space (xdr,4 ); +if (!p) + goto out_resource; + *p++ =cpu_to_be32 (acl->a_flags); + status =nfsd4_encode_acl_entries (xdr, acl, rqstp, + ~0 ,nfsd4_ace_mask (minorversion)); +if (status) + goto out; + }#ifdef CONFIG_NFSD_PNFS if ((bmval1 &FATTR4_WORD1_FS_LAYOUT_TYPES ) || (bmval2 &FATTR4_WORD2_LAYOUT_TYPES )) {
fs/nfsd/nfsd.h +4 -2
@@ -339,7 +339,8 @@ void nfsd_lockd_shutdown(void); NFSD4_SUPPORTED_ATTRS_WORD0#define NFSD4_1_SUPPORTED_ATTRS_WORD1 \ - (NFSD4_SUPPORTED_ATTRS_WORD1 | PNFSD_SUPPORTED_ATTRS_WORD1) + (NFSD4_SUPPORTED_ATTRS_WORD1 | PNFSD_SUPPORTED_ATTRS_WORD1 | \ + FATTR4_WORD1_DACL)#define NFSD4_1_SUPPORTED_ATTRS_WORD2 \ (NFSD4_SUPPORTED_ATTRS_WORD2 | PNFSD_SUPPORTED_ATTRS_WORD2 | \
@@ -386,7 +387,8 @@ static inline u32 nfsd_suppattrs2(u32 minorversion) (FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL)#define NFSD_WRITEABLE_ATTRS_WORD1 \ (FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \ - | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET) + | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET \ + | FATTR4_WORD1_DACL)#ifdef CONFIG_NFSD_V4_SECURITY_LABEL #define NFSD_WRITEABLE_ATTRS_WORD2 FATTR4_WORD2_SECURITY_LABEL#else
include/linux/nfs4.h +1 -0
@@ -394,6 +394,7 @@ enum lock_type4 {#define FATTR4_WORD1_TIME_MODIFY (1UL << 21)#define FATTR4_WORD1_TIME_MODIFY_SET (1UL << 22)#define FATTR4_WORD1_MOUNTED_ON_FILEID (1UL << 23) +#define FATTR4_WORD1_DACL (1UL << 26)#define FATTR4_WORD1_FS_LAYOUT_TYPES (1UL << 30)#define FATTR4_WORD2_LAYOUT_TYPES (1UL << 0)#define FATTR4_WORD2_LAYOUT_BLKSIZE (1UL << 1)
include/uapi/linux/nfs4.h +2 -1
@@ -121,7 +121,8 @@#define NFS4_ACE_GENERIC_READ 0x00120081#define NFS4_ACE_GENERIC_WRITE 0x00160106#define NFS4_ACE_GENERIC_EXECUTE 0x001200A0 -#define NFS4_ACE_MASK_ALL 0x001F01FF +#define NFS40_ACE_MASK_ALL 0x001F01FF +#define NFS4_ACE_MASK_ALL 0x001F07FF#define EXCHGID4_FLAG_SUPP_MOVED_REFER 0x00000001#define EXCHGID4_FLAG_SUPP_MOVED_MIGR 0x00000002
Signature · 1 line
2.1.0
RFC v3 45/45 nfs: Add support for the v4.1 dacl attribute
In reply to Andreas GruenbacherThe dacl attribute is only supported in NFS version 4.1 and later. On systems where NFS version 4.0 is still the default, an additional mount option is needed:
mount -t nfs4 -o minorversion=1 [...]
--- fs/nfs/nfs4proc.c | 2 +- fs/nfs/nfs4xdr.c | 165 ++++++++++++++++++++++++++++++++++++------------ include/linux/nfs_xdr.h | 2 +- 3 files changed, 128 insertions(+), 41 deletions(-)
fs/nfs/nfs4proc.c +1 -1
@@ -4465,7 +4465,7 @@ static struct richacl *__nfs4_get_acl_uncached(struct inode *inode)struct nfs_server *server =NFS_SERVER (inode);struct page *pages[DIV_ROUND_UP (NFS4ACL_SIZE_MAX ,PAGE_SIZE )] = {};struct nfs_getaclargs args = { - .fh =NFS_FH (inode), + .inode = inode, .acl_pages = pages, .acl_len =ARRAY_SIZE (pages) *PAGE_SIZE , };
fs/nfs/nfs4xdr.c +126 -39
234 lines not on this page · raw message
include/linux/nfs_xdr.h +1 -1
@@ -651,7 +651,7 @@ struct nfs_setaclres {struct nfs_getaclargs {struct nfs4_sequence_args seq_args; -struct nfs_fh * fh; +struct inode * inode;size_t acl_len;struct page ** acl_pages; };
Signature · 1 line
2.1.0
Quoted 17 lines · Andreas Gruenbacher
Hello,
here's another update of the richacl patch queue. The changes since the last posting (https://lwn.net/Articles/638242/) include:
* The nfs client now allocates pages for received acls on demand like the server does. It no longer caches the acl size between calls.
* All possible acls consisting of only owner@, group@, and everyone@ entries which are equivalent to the file mode permission bits are now recognized. This is needed because by the NFSv4 specification, the nfs server must translate the file mode permission bits into an acl if it supports acls at all.
* Support for the dacl attribute over NFSv4.1 for Automatic Inheritance, and also for the
write_retentionandwrite_retention_holdpermissions.
Quoted 8 lines
* The
richacl_compute_max_masks()documentation has been improved.* Various minor bug fixes.
The git version is available here:
git://git.kernel.org/pub/scm/linux/kernel/git/agruen/linux-richacl.git \ richacl-2015-04-24
FYI. I have a mostly (needs test suite adding) working module for Samba for Andreas's richacls code.
Using it we map incoming Windows ACLs directly to richacls using the same mapping as we use for existing ZFS ACLs.
Jeremy.
Jeremy,
2015-05-23 17:50 GMT+02:00 Jeremy Allison <[email protected]>:
Quoted 2 lines
FYI. I have a mostly (needs test suite adding) working module for Samba for Andreas's richacls code.
great, thanks. I'll be doing some testing as soon as I get to it.
Andreas
Hi Andreas,
Quoted 21 lines
Quoted 21 lines
here's another update of the richacl patch queue. The changes since the last posting (https://lwn.net/Articles/638242/) include:
* The nfs client now allocates pages for received acls on demand like the server does. It no longer caches the acl size between calls.
* All possible acls consisting of only owner@, group@, and everyone@ entries which are equivalent to the file mode permission bits are now recognized. This is needed because by the NFSv4 specification, the nfs server must translate the file mode permission bits into an acl if it supports acls at all.
* Support for the dacl attribute over NFSv4.1 for Automatic Inheritance, and also for the
write_retentionandwrite_retention_holdpermissions.* The
richacl_compute_max_masks()documentation has been improved.* Various minor bug fixes.
The git version is available here:
Quoted 8 lines
Quoted 2 lines
git://git.kernel.org/pub/scm/linux/kernel/git/agruen/linux-richacl.git \ richacl-2015-04-24FYI. I have a mostly (needs test suite adding) working module for Samba for Andreas's richacls code.
Using it we map incoming Windows ACLs directly to richacls using the same mapping as we use for existing ZFS ACLs.
Yes, we need to make sure the code supports everything we require, proved by working code.
We should avoid the disaster with the mostly unuseable F_SETLEASE code for kernel oplocks.
Thanks!
metze