Skip to contentSkip to conversation

Richacls

RFC Superseded Andreas Gruenbacher 8 participants 110 messages Apr 24, 2015 – Jul 15, 2016

Subsystems (9)
Networking Documentation VFS Staging +5 more subsystems

v3 of 10 Superseded Latest is v10 →

lore
Share & tools
lore
#0 lore raw

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
#1 lore raw

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
index fa2db08..d6f9ab4 100644
@@ -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
index 966b228..700cdf6 100644
@@ -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
#2 lore raw

RFC v3 2/45 uapi: Remove kernel internal declaration

In reply to Andreas Gruenbacher

The 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
index ed43cb7..32201c2 100644
@@ -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
index 35f5f4c..adc0aff 100644
@@ -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
#3 lore raw

RFC v3 3/45 nfsd: Checking for acl support does not require fetching any acls

In reply to Andreas Gruenbacher

Whether 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
index 5fb7e78..e2d602d 100644
@@ -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
#4 lore raw

RFC v3 4/45 vfs: Shrink struct posix_acl

In reply to Andreas Gruenbacher

There is a hole in struct posix_acl because its struct rcu_head member is too large; at least on on 64-bit architectures, the hole cannot be closed by changing the definition of struct posix_acl. So instead, remove the struct rcu_head member from struct posix_acl, make sure that acls are always big enough to fit a struct rcu_head, and cast to struct rcu_head * when disposing of an acl.

---
 fs/posix_acl.c            | 5 +++--
 include/linux/posix_acl.h | 7 ++-----
 2 files changed, 5 insertions(+), 7 deletions(-)
fs/posix_acl.c +3 -2
index 3a48bb7..efe983e 100644
@@ -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
index 3e96a6a..66cf477 100644
@@ -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
#5 lore raw

Re: [RFC v3 04/45] vfs: Shrink struct posix_acl

Quoted 46 lines · Andreas Gruenbacher

There is a hole in struct posix_acl because its struct rcu_head member is too large; at least on on 64-bit architectures, the hole cannot be closed by changing the definition of struct posix_acl. So instead, remove the struct rcu_head member from struct posix_acl, make sure that acls are always big enough to fit a struct rcu_head, and cast to struct rcu_head * when disposing of an acl.

---
 fs/posix_acl.c            | 5 +++--
 include/linux/posix_acl.h | 7 ++-----
 2 files changed, 5 insertions(+), 7 deletions(-)
fs/posix_acl.c +3 -2
index 3a48bb7..efe983e 100644
@@ -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
index 3e96a6a..66cf477 100644
@@ -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

#6 lore raw

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_count and part of the first struct posix_acl_entry while there might still be rcu users?

You are right, this patch is broken. I'll remove it.

Thanks,
Andreas

#7 lore raw

RFC v3 5/45 vfs: Add IS_ACL() and IS_RICHACL() tests

In reply to Andreas Gruenbacher

The vfs does not apply the umask for file systems that support acls. The test used for this used to be called IS_POSIXACL(). Switch to a new IS_ACL() test to check for either posix acls or richacls instead. Add a new MS_RICHACL flag and IS_RICHACL() test for richacls alone. The IS_POSIXACL() test is still needed by file systems that specifically support POSIX ACLs, like nfsd.

---
 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
index ec35851..8b84f99 100644
@@ -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
index c83145a..0ba4bbc 100644
@@ -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
index 52cc449..b64a316 100644
@@ -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
index 9b964a5..6ac6bc9 100644
@@ -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
#8 lore raw

RFC v3 6/45 vfs: Add MAY_CREATE_FILE and MAY_CREATE_DIR permission flags

In reply to Andreas Gruenbacher

Richacls distinguish between creating non-directories and directories. To support that, add an isdir parameter to may_create(). When checking inode_permission() for create permission, pass in an additional MAY_CREATE_FILE or MAY_CREATE_DIR mask flag.

To allow checking for delete *and* create access when replacing an existing file via vfs_rename(), add a replace parameter to may_delete().

---
 fs/namei.c         | 42 ++++++++++++++++++++++++------------------
 include/linux/fs.h |  2 ++
 2 files changed, 26 insertions(+), 18 deletions(-)
fs/namei.c +24 -18
index 0ba4bbc..a8bc030 100644
@@ -454,7 +454,8 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
  * this, letting us set arbitrary permissions for filesystem access without
  * changing the "normal" UIDs which are used for other things.
  *
- * When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
+ * When checking for MAY_APPEND, MAY_CREATE_FILE, MAY_CREATE_DIR,
+ * MAY_WRITE must also be set in @mask.
  */
 int inode_permission(struct inode *inode, int mask)
 {
@@ -2447,10 +2448,11 @@ EXPORT_SYMBOL(__check_sticky);
  * 10. We don't allow removal of NFS sillyrenamed files; it's handled by
  *     nfs_async_unlink().
  */
-static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
+static int may_delete(struct inode *dir, struct dentry *victim,
+		      bool isdir, bool replace)
 {
 	struct inode *inode = victim->d_inode;
-	int error;
+	int error, mask = MAY_WRITE | MAY_EXEC;
 
 	if (d_is_negative(victim))
 		return -ENOENT;
@@ -2459,7 +2461,9 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
 	BUG_ON(victim->d_parent->d_inode != dir);
 	audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
 
-	error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+	if (replace)
+		mask |= isdir ? MAY_CREATE_DIR : MAY_CREATE_FILE;
+	error = inode_permission(dir, mask);
 	if (error)
 		return error;
 	if (IS_APPEND(dir))
@@ -2490,14 +2494,16 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
  *  3. We should have write and exec permissions on dir
  *  4. We can't do it if dir is immutable (done in permission())
  */
-static inline int may_create(struct inode *dir, struct dentry *child)
+static inline int may_create(struct inode *dir, struct dentry *child, bool isdir)
 {
+	int mask = isdir ? MAY_CREATE_DIR : MAY_CREATE_FILE;
+
 	audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
 	if (child->d_inode)
 		return -EEXIST;
 	if (IS_DEADDIR(dir))
 		return -ENOENT;
-	return inode_permission(dir, MAY_WRITE | MAY_EXEC);
+	return inode_permission(dir, MAY_WRITE | MAY_EXEC | mask);
 }
 
 /*
@@ -2547,7 +2553,7 @@ EXPORT_SYMBOL(unlock_rename);
 int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
 		bool want_excl)
 {
-	int error = may_create(dir, dentry);
+	int error = may_create(dir, dentry, false);
 	if (error)
 		return error;
 
@@ -3422,7 +3428,7 @@ EXPORT_SYMBOL(user_path_create);
 
 int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
 {
-	int error = may_create(dir, dentry);
+	int error = may_create(dir, dentry, false);
 
 	if (error)
 		return error;
@@ -3514,7 +3520,7 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
 
 int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 {
-	int error = may_create(dir, dentry);
+	int error = may_create(dir, dentry, true);
 	unsigned max_links = dir->i_sb->s_max_links;
 
 	if (error)
@@ -3595,7 +3601,7 @@ EXPORT_SYMBOL(dentry_unhash);
 
 int vfs_rmdir(struct inode *dir, struct dentry *dentry)
 {
-	int error = may_delete(dir, dentry, 1);
+	int error = may_delete(dir, dentry, true, false);
 
 	if (error)
 		return error;
@@ -3715,7 +3721,7 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname)
 int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
 {
 	struct inode *target = dentry->d_inode;
-	int error = may_delete(dir, dentry, 0);
+	int error = may_delete(dir, dentry, false, false);
 
 	if (error)
 		return error;
@@ -3847,7 +3853,7 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname)
 
 int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
 {
-	int error = may_create(dir, dentry);
+	int error = may_create(dir, dentry, false);
 
 	if (error)
 		return error;
@@ -3930,7 +3936,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
 	if (!inode)
 		return -ENOENT;
 
-	error = may_create(dir, new_dentry);
+	error = may_create(dir, new_dentry, false);
 	if (error)
 		return error;
 
@@ -4118,19 +4124,19 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 	if (source == target)
 		return 0;
 
-	error = may_delete(old_dir, old_dentry, is_dir);
+	error = may_delete(old_dir, old_dentry, is_dir, false);
 	if (error)
 		return error;
 
 	if (!target) {
-		error = may_create(new_dir, new_dentry);
+		error = may_create(new_dir, new_dentry, is_dir);
 	} else {
 		new_is_dir = d_is_dir(new_dentry);
 
 		if (!(flags & RENAME_EXCHANGE))
-			error = may_delete(new_dir, new_dentry, is_dir);
+			error = may_delete(new_dir, new_dentry, is_dir, true);
 		else
-			error = may_delete(new_dir, new_dentry, new_is_dir);
+			error = may_delete(new_dir, new_dentry, new_is_dir, true);
 	}
 	if (error)
 		return error;
@@ -4394,7 +4400,7 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna
 
 int vfs_whiteout(struct inode *dir, struct dentry *dentry)
 {
-	int error = may_create(dir, dentry);
+	int error = may_create(dir, dentry, false);
 	if (error)
 		return error;
 
include/linux/fs.h +2 -0
index b64a316..2cced7c 100644
@@ -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
#9 lore raw

RFC v3 7/45 vfs: Add MAY_DELETE_SELF and MAY_DELETE_CHILD permission flags

In reply to Andreas Gruenbacher

Normally, deleting a file requires write and execute access to the parent directory. With Richacls, a process with MAY_DELETE_SELF access to a file may delete the file even without write access to the parent directory.

To support that, pass the MAY_DELETE_CHILD mask flag to inode_permission() when checking for delete access inside a directory, and MAY_DELETE_SELF when checking for delete access to a file itelf.

The MAY_DELETE_SELF permission does not override the sticky directory check.

---
 fs/namei.c         | 15 +++++++++++----
 include/linux/fs.h |  2 ++
 2 files changed, 13 insertions(+), 4 deletions(-)
fs/namei.c +11 -4
index a8bc030..a8d1674 100644
@@ -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
index 2cced7c..5701f90 100644
@@ -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
#10 lore raw

RFC v3 8/45 vfs: Make the inode passed to inode_change_ok non-const

In reply to Andreas Gruenbacher

We will need to call iop->permission and iop->get_acl from inode_change_ok() for additional permission checks, and both take a non-const inode.

---
 fs/attr.c          | 2 +-
 include/linux/fs.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
fs/attr.c +1 -1
index 6530ced..328be71 100644
@@ -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
index 5701f90..8347f81 100644
@@ -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
#11 lore raw

RFC v3 9/45 vfs: Add permission flags for setting file attributes

In reply to Andreas Gruenbacher

Richacls support permissions that allow to take ownership of a file, change the file permissions, and set the file timestamps. Support that by introducing new permission mask flags and by checking for those mask flags in inode_change_ok().

---
 fs/attr.c          | 79 +++++++++++++++++++++++++++++++++++++++++++++---------
 include/linux/fs.h |  3 +++
 2 files changed, 70 insertions(+), 12 deletions(-)
fs/attr.c +67 -12
index 328be71..85483e0 100644
@@ -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
index 8347f81..0121047 100644
@@ -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
#12 lore raw

RFC v3 10/45 richacl: In-memory representation and helper functions

In reply to Andreas Gruenbacher

A richacl consists of an NFSv4 acl and an owner, group, and other mask. These three masks correspond to the owner, group, and other file permission bits, but they contain NFSv4 permissions instead of POSIX permissions.

Each entry in the NFSv4 acl applies to the file owner (OWNER@), the owning group (GROUP@), everyone (EVERYONE@), or to a specific uid or gid.

As in the standard POSIX file permission model, each process is the owner, group, or other file class. A richacl grants a requested access only if the NFSv4 acl in the richacl grants the access (according to the NFSv4 permission check algorithm), and the file mask that applies to the process includes the requested permissions.

---
 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
index a88ac48..8f0a59c 100644
@@ -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
#13 lore raw

RFC v3 11/45 richacl: Permission mapping functions

In reply to Andreas Gruenbacher

We need to map from POSIX permissions to NFSv4 permissions when a chmod() is done, from NFSv4 permissions to POSIX permissions when an acl is set (which implicitly sets the file permission bits), and from the MAY_READ/MAY_WRITE/MAY_EXEC/MAY_APPEND flags to NFSv4 permissions when doing an access check in a richacl.

---
 fs/richacl_base.c       | 116 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |  46 +++++++++++++++++++
 2 files changed, 162 insertions(+)
fs/richacl_base.c +116 -0
index 164b902..978f221 100644
@@ -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
#14 lore raw

RFC v3 12/45 richacl: Compute maximum file masks from an acl

In reply to 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().

---
 fs/richacl_base.c       | 154 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |   1 +
 2 files changed, 155 insertions(+)
fs/richacl_base.c +154 -0
index 978f221..1e14c38 100644
@@ -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
index d19843b..cd0cdaa 100644
@@ -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
#15 lore raw

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.

#16 lore raw

RFC v3 13/45 richacl: Update the file masks in chmod()

In reply to Andreas Gruenbacher

Doing a chmod() sets the file mode, which includes the file permission bits. When a file has a richacl, the permissions that the richacl grants need to be limited to what the new file permission bits allow.

This is done by setting the file masks in the richacl to what the file permission bits map to. The richacl access check algorithm takes the file masks into account, which ensures that the richacl cannot grant too many permissions.

It is possible to explicitly add permissions to the file masks which go beyond what the file permission bits can grant (like the ACE4_WRITE_ACL permission). The POSIX.1 standard calls this an alternate file access control mechanism. A subsequent chmod() would ensure that those permissions are disabled again.

---
 fs/richacl_base.c       | 40 ++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |  1 +
 2 files changed, 41 insertions(+)
fs/richacl_base.c +40 -0
index 1e14c38..261ddd2 100644
@@ -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
index cd0cdaa..a27ff10 100644
@@ -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
#17 lore raw

RFC v3 14/45 richacl: Permission check algorithm

In reply to 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.

---
 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
index 8f0a59c..bb96ad7 100644
@@ -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
index a27ff10..d11e8f3 100644
@@ -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
#18 lore raw

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
---
 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
index 8f0a59c..bb96ad7 100644
@@ -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
new file mode 100644
index 0000000..33e591a
@@ -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
index a27ff10..d11e8f3 100644
@@ -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
#19 lore raw

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

#20 lore raw

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  :-::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
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 by richacl_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.

#21 lore raw

RFC v3 15/45 vfs: Cache base_acl objects in inodes

In reply to Andreas Gruenbacher

POSIX ACLs and richacls are both objects allocated by kmalloc() with a reference count which are freed by kfree_rcu(). An inode can either cache an access and a default POSIX ACL, or a richacl. (Richacls do not have default acls). To allow an inode to cache either of the two kinds of acls, introduce a new base_acl type and convert i_acl and i_default_acl to that type. In most cases, the vfs then doesn't have to care which kind of acl an inode caches (if any).

---
 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
index 0c1b583..c8cae33 100644
@@ -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
index 7422027..ccb2c7c 100644
@@ -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
index f00b16f..555fe9c 100644
@@ -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
index efe983e..2fbfec8 100644
@@ -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
index 0121047..6e0da10 100644
@@ -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
index 66cf477..2c46441 100644
@@ -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
index d11e8f3..5eb198d 100644
@@ -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
#22 lore raw

RFC v3 16/45 vfs: Cache richacl in struct inode

In reply to Andreas Gruenbacher

Cache 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
index 555fe9c..5272412 100644
@@ -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
index 2fbfec8..ebf96b2 100644
@@ -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
index 261ddd2..5273ccb 100644
@@ -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
index 6e0da10..7584a63 100644
@@ -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
index 5eb198d..6d88ee9 100644
@@ -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
#23 lore raw

RFC v3 17/45 richacl: Create-time inheritance

In reply to Andreas Gruenbacher

When a new file is created, it can inherit an acl from its parent directory; this is similar to how default acls work in POSIX (draft) ACLs.

As with POSIX ACLs, if a file inherits an acl from its parent directory, the intersection between the create mode and the permissions granted by the inherited acl determines the file masks and file permission bits, and the umask is ignored.

---
 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
index 5273ccb..5d45539 100644
@@ -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
index 6d88ee9..ab9fde1 100644
@@ -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
#24 lore raw

RFC v3 18/45 richacl: Check if an acl is equivalent to a file mode

In reply to Andreas Gruenbacher

This 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
index 5d45539..db27542 100644
@@ -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
index ab9fde1..c6fd0a1 100644
@@ -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
#25 lore raw

RFC v3 19/45 richacl: Also recognize nontrivial mode-equivalent acls

In reply to 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.

---
 fs/richacl_base.c       | 150 ++++++++++++++++++++++++++++++++++++++----------
 include/linux/richacl.h |   1 +
 2 files changed, 122 insertions(+), 29 deletions(-)
fs/richacl_base.c +121 -29
index db27542..54cb482 100644
@@ -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
index c6fd0a1..8a92b89 100644
@@ -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
#26 lore raw

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.

---
 fs/richacl_base.c       | 150 ++++++++++++++++++++++++++++++++++++++----------
 include/linux/richacl.h |   1 +
 2 files changed, 122 insertions(+), 29 deletions(-)
fs/richacl_base.c +18 -18
index db27542..54cb482 100644
@@ -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
index c6fd0a1..8a92b89 100644
@@ -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
#27 lore raw

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.

---
 fs/richacl_base.c       | 150 ++++++++++++++++++++++++++++++++++++++----------
 include/linux/richacl.h |   1 +
 2 files changed, 122 insertions(+), 29 deletions(-)
fs/richacl_base.c +18 -18
index db27542..54cb482 100644
@@ -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_WHO check, 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
index c6fd0a1..8a92b89 100644
@@ -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

#28 lore raw

Re: [RFC v3 19/45] richacl: Also recognize nontrivial mode-equivalent acls

In reply to Andreas Gruenbacher
Quoted 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.

---
 fs/richacl_base.c       | 150 ++++++++++++++++++++++++++++++++++++++----------
 include/linux/richacl.h |   1 +
 2 files changed, 122 insertions(+), 29 deletions(-)
fs/richacl_base.c +0 -0
index db27542..54cb482 100644

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

#29 lore raw

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

#30 lore raw

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

#31 lore raw

RFC v3 20/45 richacl: Automatic Inheritance

In reply to 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.

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
index 54cb482..572f1b8 100644
@@ -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
index 1a5b868..dfbb15a 100644
@@ -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
index 8a92b89..bcc2b64 100644
@@ -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
#32 lore raw

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 Inheritance

Automatic Inheritance (AI) allows changes to the acl of a directory to recursively propagate down to files and directories in the directory.

To implement this, the kernel keeps track of which permissions have been inherited, and makes sure that permission propagation is turned off when the file permission bits of a file are changed (upon create or chmod).

The actual permission propagation is implemented in user space.

Automatic Inheritance works as follows:

- When the ACL4_AUTO_INHERIT flag in the acl of a file is not set, the file is not affected by AI.

- When the ACL4_AUTO_INHERIT flag in the acl of a directory is set and a file or subdirectory is created in that directory, files created in the directory will have the ACL4_AUTO_INHERIT flag set, and all inherited aces will have the ACE4_INHERITED_ACE flag set. This allows user space to distinguish between aces which have been inherited and aces which have been explicitly added.

Quoted 4 lines

- 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).

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_INHERIT set

will

Quoted 1 line

also have the ACL4_PROTECTED flag set. This effectively disables

Automatic

Quoted 4 lines

Inheritance.

Protocols which support creating files without specifying permissions can explicitly clear the ACL4_PROTECTED flag 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
---
 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 -0

100644

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 -0
 	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)

fs/richacl_inode.c index

Quoted 12 lines
fs/richacl_base.c +5 -0
 			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 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 -0

8a92b89..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

#33 lore raw

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 of ACL4_ 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_INHERIT set will also have the ACL4_PROTECTED flag set. This effectively disables Automatic Inheritance.

Quoted 7 lines

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.

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

#34 lore raw

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

#35 lore raw

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

#36 lore raw

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

#37 lore raw

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

#38 lore raw

RE: [RFC v3 20/45] richacl: Automatic Inheritance

In reply to Andreas Grünbacher
Quoted 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 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 of ACL4_ 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_INHERIT set will also have the ACL4_PROTECTED flag set. This effectively disables Automatic Inheritance.

Quoted 7 lines

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.

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 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

#39 lore raw

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

#40 lore raw

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 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...).

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_PROTECTED flag 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

#41 lore raw

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_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).

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

#42 lore raw

Re: [RFC v3 20/45] richacl: Automatic Inheritance

In reply to Andreas Gruenbacher

Looks 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_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
index 54cb482..572f1b8 100644
@@ -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
index 1a5b868..dfbb15a 100644
@@ -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
index 8a92b89..bcc2b64 100644
@@ -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
#43 lore raw

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.git

it 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

#44 lore raw

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.git

it 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:

https://code.wireshark.org/review/#/c/8526/1

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

#45 lore raw

RFC v3 21/45 richacl: xattr mapping functions

In reply to Andreas Gruenbacher

Map 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
index bb96ad7..6155cc4 100644
@@ -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
index 4ef6985..a09e654 100644
@@ -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 && copy_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
index 1590c49..1996903 100644
@@ -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
#46 lore raw

RFC v3 22/45 vfs: Add richacl permission checking

In reply to Andreas Gruenbacher

Hook 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
index a8d1674..d5b4fcd 100644
@@ -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
index ebf96b2..16464f0 100644
@@ -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
#47 lore raw

RFC v3 23/45 ext4: Add richacl support

In reply to Andreas Gruenbacher

From: "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
index efea5d5..8c821d2 100644
@@ -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
index 0310fec..b9a3e2e 100644
@@ -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
index d40c8db..7c508f7 100644
@@ -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
index da2c795..450b4d1 100644
@@ -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 NULL
 
 static inline int
-ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
+ext4_init_posix_acl(handle_t *handle, struct inode *inode, struct inode *dir)
 {
 	return 0;
 }
fs/ext4/file.c +4 -2
index 33a09da..be466f7 100644
@@ -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
index ac644c3..97d1c4b 100644
@@ -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
index 5cb9a21..c379742 100644
@@ -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
index 28fe71a..da8f498 100644
@@ -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
index 1e09fc7..815a306 100644
@@ -100,6 +100,9 @@ static const struct xattr_handler *ext4_xattr_handler_map[] = {
 #ifdef CONFIG_EXT4_FS_SECURITY
 	[EXT4_XATTR_INDEX_SECURITY]	     = &ext4_xattr_security_handler,
 #endif
+#ifdef CONFIG_EXT4_FS_RICHACL
+	[EXT4_XATTR_INDEX_RICHACL]           = &ext4_richacl_xattr_handler,
+#endif
 };
 
 const struct xattr_handler *ext4_xattr_handlers[] = {
@@ -112,6 +115,9 @@ const struct xattr_handler *ext4_xattr_handlers[] = {
 #ifdef CONFIG_EXT4_FS_SECURITY
 	&ext4_xattr_security_handler,
 #endif
+#ifdef CONFIG_EXT4_FS_RICHACL
+	&ext4_richacl_xattr_handler,
+#endif
 	NULL
 };
 
fs/ext4/xattr.h +1 -0
index 29bedf5..065821e 100644
@@ -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
#48 lore raw

RFC v3 24/45 ext4: Add richacl feature flag

In reply to Andreas Gruenbacher

From: "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
index f63c3d5..64187cd 100644
@@ -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
#49 lore raw

RFC v3 25/45 richacl: acl editing helper functions

In reply to Andreas Gruenbacher

The 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
index 6155cc4..5983276 100644
@@ -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
new file mode 100644
index 0000000..a9ff630
@@ -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
#50 lore raw

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

#51 lore raw

RFC v3 26/45 richacl: Move everyone@ aces down the acl

In reply to 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.

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
index 2ea6658..63f4373 100644
@@ -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
#52 lore raw

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.

---
 fs/richacl_compat.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)
fs/richacl_compat.c +4 -0
index 2ea6658..63f4373 100644
@@ -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
#53 lore raw

RFC v3 27/45 richacl: Propagate everyone@ permissions to other aces

In reply to Andreas Gruenbacher

The 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
index 63f4373..f43d007 100644
@@ -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
#54 lore raw

RFC v3 28/45 richacl: Isolate the owner and group classes

In reply to Andreas Gruenbacher

When 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
index f43d007..645917f 100644
@@ -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
#55 lore raw

RFC v3 29/45 richacl: Apply the file masks to a richacl

In reply to 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.

---
 fs/richacl_compat.c     | 103 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |   3 ++
 2 files changed, 106 insertions(+)
fs/richacl_compat.c +103 -0
index 645917f..49af600 100644
@@ -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
index bcc2b64..6a97dca 100644
@@ -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
#56 lore raw

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.

---
 fs/richacl_compat.c     | 103 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |   3 ++
 2 files changed, 106 insertions(+)
fs/richacl_compat.c +61 -0
index 645917f..49af600 100644
@@ -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
index bcc2b64..6a97dca 100644
@@ -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

#57 lore raw

RFC v3 30/45 richacl: Create richacl from mode values

In reply to Andreas Gruenbacher

Create 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
index 49af600..8ebe772 100644
@@ -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
index 6a97dca..25ff4df 100644
@@ -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
#58 lore raw

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.

---
 fs/richacl_compat.c     | 38 ++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |  1 +
 2 files changed, 39 insertions(+)
fs/richacl_compat.c +0 -0
index 49af600..8ebe772 100644

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
index 6a97dca..25ff4df 100644
@@ -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

#59 lore raw

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.

---
 fs/richacl_compat.c     | 38 ++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |  1 +
 2 files changed, 39 insertions(+)
fs/richacl_compat.c +0 -0
index 49af600..8ebe772 100644

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:

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
index 6a97dca..25ff4df 100644
@@ -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
#60 lore raw

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

#61 lore raw

RFC v3 31/45 richacl: Create acl with masks applied in richacl_from_mode()

In reply to Andreas Gruenbacher

When 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
index 8ebe772..33fc50c 100644
@@ -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
#62 lore raw

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
index 59fd766..c630651 100644
@@ -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
#63 lore raw

RFC v3 33/45 nfsd: Keep list of acls to dispose of in compoundargs

In reply to 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.

---
 fs/nfsd/nfs4xdr.c | 26 ++++++++++++++++++++++++++
 fs/nfsd/xdr4.h    |  6 ++++++
 2 files changed, 32 insertions(+)
fs/nfsd/nfs4xdr.c +26 -0
index e2d602d..f25d1e7 100644
@@ -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
index 0bda93e..4ed4db0 100644
@@ -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
#64 lore raw

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
---
 fs/nfsd/nfs4xdr.c | 26 ++++++++++++++++++++++++++
 fs/nfsd/xdr4.h    |  6 ++++++
 2 files changed, 32 insertions(+)
fs/nfsd/nfs4xdr.c +26 -0
index e2d602d..f25d1e7 100644
@@ -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
index 0bda93e..4ed4db0 100644
@@ -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

#65 lore raw

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

#66 lore raw

RFC v3 34/45 nfsd: Use richacls as internal acl representation

In reply to Andreas Gruenbacher

When 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
index 8b84f99..f459f75 100644
@@ -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
index d153ca3..e055139 100644
@@ -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
index 683bf71..ff24643 100644
@@ -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
index 4cd7c69..1c5deb5 100644
@@ -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
index 92b9d97..658da6e 100644
@@ -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
index 4ed4db0..092c340 100644
@@ -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
index 32201c2..095854c 100644
@@ -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	2048
 
 struct nfs4_label {
include/linux/nfs4acl.h new file +7 -0
new file mode 100644
index 0000000..db9f9a6
@@ -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
#67 lore raw

RFC v3 35/45 nfsd: Add richacl support

In reply to Andreas Gruenbacher

On 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
index 1c5deb5..d73c664 100644
@@ -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
index 658da6e..2117e52 100644
@@ -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
index f3c1880..c0e1b3c 100644
@@ -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
#68 lore raw

RFC v3 36/45 NFSv4: Fix GETATTR bitmap verification

In reply to 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.

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
index 5c399ec..36b5e95 100644
@@ -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_t
 	int 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, stru
 	if (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, stru
 			memcpy(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_t
 	int 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 *bitma
 	int 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, uin
 	int 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, uint
 	int 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, uin
 	int 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, uin
 	int 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, uint32
 	int 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, uint32
 		if (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 *m
 	int 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_t
 	int 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 *rde
 	int 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, uin
 	int 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, uint
 	int 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, uin
 	int 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, uint
 	int 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_t
 	return 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
#69 lore raw

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

#70 lore raw

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

#71 lore raw

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

#72 lore raw

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

#73 lore raw

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

#74 lore raw

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 in decode_getfattr_statfs(), and one in decode_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

#75 lore raw

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 in decode_getfattr_statfs(), and one in decode_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

#76 lore raw

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

#77 lore raw

Re: [RFC v3 36/45] NFSv4: Fix GETATTR bitmap verification

In reply to J. Bruce Fields

2015-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

#78 lore raw

Re: [RFC v3 36/45] NFSv4: Fix GETATTR bitmap verification

In reply to J. Bruce Fields

2015-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

#79 lore raw

RFC v3 37/45 nfs/sunrpc: No more encode and decode function pointer casting

In reply to Andreas Gruenbacher

Instead 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
index d3e40db..693f71a 100644
@@ -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
index 3e9f787..6ca581b 100644
@@ -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
index 47a32b6..395c39f 100644
@@ -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
index 99a4528..c02962a 100644
@@ -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
index 5827785..a7167b8 100644
@@ -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
index 70c6b92..7177815 100644
@@ -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
index 59eeed4..d06117a 100644
@@ -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
index 1ec19f6..6b9dc37 100644
@@ -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
index 9d88c62..eb98597 100644
@@ -179,10 +179,10 @@ struct gssx_res_accept_sec_context {
 #define gssx_dec_init_sec_context NULL
 void 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
index e6ce151..47a9a11 100644
@@ -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
#80 lore raw

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_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.

How is this even remotely relevant to ACL functionality, and why does it deserve to bypass the NFS tree?

Trond

#81 lore raw

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

#82 lore raw

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

#83 lore raw

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

#84 lore raw

RFC v3 38/45 nfs/sunrpc: Return status code from encode functions

In reply to Andreas Gruenbacher

Return 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
index 693f71a..61df64c 100644
@@ -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
index 395c39f..2308983 100644
@@ -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
index c02962a..cd1cc75 100644
@@ -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
index a7167b8..f658f6d 100644
@@ -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
index 7177815..d68f71e 100644
@@ -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
index 47f38be..11f78ac2 100644
@@ -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
index 6b9dc37..26601cd 100644
@@ -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
index eb98597..425085c 100644
@@ -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
index 47a9a11..7d864eb 100644
@@ -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
index 7c71283..1d31943 100644
@@ -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
#85 lore raw

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 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.

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

#86 lore raw

RFC v3 39/45 nfs3: Return posix acl encode errors

In reply to Andreas Gruenbacher

When 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
index 8b880f7..de6f12c 100644
@@ -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
#87 lore raw

RFC v3 40/45 nfs: Remove unused xdr page offsets in getacl/setacl arguments

In reply to Andreas Gruenbacher

The 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
index 627f37c..8c50670 100644
@@ -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 bufl
 		return -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
index dd7a800..4372fc4 100644
@@ -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
index 4cb3eaa..92134cf 100644
@@ -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
#88 lore raw

RFC v3 41/45 rpc: Allow to demand-allocate pages to encode into

In reply to 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.

---
 net/sunrpc/xdr.c | 8 ++++++++
 1 file changed, 8 insertions(+)
net/sunrpc/xdr.c +8 -0
index 4439ac4..062951b 100644
@@ -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
#89 lore raw

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.

---
 net/sunrpc/xdr.c | 8 ++++++++
 1 file changed, 8 insertions(+)
net/sunrpc/xdr.c +1 -0
index 4439ac4..062951b 100644
@@ -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

#90 lore raw

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 a GFP_KERNEL allocation inside an XDR routine anyway? That's not an I/O safe sleep.

Fixed, thanks.

Andreas

#91 lore raw

RFC v3 42/45 nfs: Add richacl support

In reply to 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.

---
 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
index d42dff6..e67f72e 100644
@@ -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
index 322b2de02..fb904ec 100644
@@ -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
index b01ccf3..4eac89d7 100644
@@ -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
index 5e1273d..df994aa 100644
@@ -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
index 92134cf..77097ec 100644
@@ -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
#92 lore raw

Re: [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

#93 lore raw

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

#94 lore raw

Re: [RFC v3 42/45] nfs: Add richacl support

In reply to Trond Myklebust
Quoted 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

#95 lore raw

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

#96 lore raw

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

#97 lore raw

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

#98 lore raw

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

#99 lore raw

RE: [RFC v3 42/45] nfs: Add richacl support

In reply to Trond Myklebust
Quoted 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

#100 lore raw

Re: [RFC v3 42/45] nfs: Add richacl support

In reply to Andreas Gruenbacher
Quoted 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.

---

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
index d42dff6..e67f72e 100644
@@ -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
index 8c50670..c2ba4f0 100644
@@ -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 65536
 
 struct 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 bufl
 	return 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,
 #endif
fs/nfs/nfs4xdr.c +120 -5
index 4372fc4..8ccc2a0 100644
@@ -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
index 322b2de02..fb904ec 100644
@@ -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
index b01ccf3..4eac89d7 100644
@@ -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
index 5e1273d..df994aa 100644
@@ -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
index 92134cf..77097ec 100644
@@ -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
#101 lore raw

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_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!

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_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?

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

#102 lore raw

Re: [RFC v3 42/45] nfs: Add richacl support

In reply to Weston Andros Adamson

Hi 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.

---

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
index d42dff6..e67f72e 100644
@@ -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
index 8c50670..c2ba4f0 100644
@@ -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 65536
 
 struct 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 bufl
      return 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_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.

Thanks!

Quoted 11 lines

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?

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

#103 lore raw

RFC v3 43/45 uapi/nfs: Add NFSv4.1 ACL definitions

In reply to Andreas Gruenbacher

Add 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
index adc0aff..2119c7c 100644
@@ -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
#104 lore raw

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
---
 include/uapi/linux/nfs4.h | 7 +++++++
 1 file changed, 7 insertions(+)
include/uapi/linux/nfs4.h +7 -0
index adc0aff..2119c7c 100644
@@ -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 · 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
#105 lore raw

RFC v3 44/45 nfsd: Add support for the v4.1 dacl attribute

In reply to Andreas Gruenbacher

Richacls 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
index c0e1b3c..772039e 100644
@@ -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
index 565c4da..474ff70 100644
@@ -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
index 095854c..814ab60 100644
@@ -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
index 2119c7c..64e4c6c 100644
@@ -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
#106 lore raw

RFC v3 45/45 nfs: Add support for the v4.1 dacl attribute

In reply to Andreas Gruenbacher

The 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
index c2ba4f0..acf39e8 100644
@@ -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
index 77097ec..3767624 100644
@@ -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
#107 lore raw
In reply to Andreas Gruenbacher
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_retention and write_retention_hold permissions.

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.

#108 lore raw

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

#109 lore raw
In reply to Jeremy Allison

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_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:

Quoted 8 lines
Quoted 2 lines
  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.

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