2015-04-24 11:04:51

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 00/45] 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

--
2.1.0



2015-04-24 11:05:22

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 22/45] vfs: Add richacl permission checking

Hook the richacl permission checking function into the vfs.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/namei.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++--
fs/posix_acl.c | 6 +++---
2 files changed, 52 insertions(+), 5 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index a8d1674..d5b4fcd 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -35,6 +35,7 @@
#include <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;
}
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index ebf96b2..16464f0 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -100,13 +100,13 @@ struct posix_acl *get_acl(struct inode *inode, int type)
{
struct posix_acl *acl;

+ if (!IS_POSIXACL(inode))
+ return NULL;
+
acl = get_cached_acl(inode, type);
if (acl != ACL_NOT_CACHED)
return acl;

- if (!IS_POSIXACL(inode))
- return NULL;
-
/*
* A filesystem can force a ACL callback by just never filling the
* ACL cache. But normally you'd fill the cache either at inode
--
2.1.0


2015-04-24 11:05:52

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 43/45] uapi/nfs: Add NFSv4.1 ACL definitions

Add the ACL related protocol definitions which were added in the NFSv4.1
specifiction.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
include/uapi/linux/nfs4.h | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/include/uapi/linux/nfs4.h b/include/uapi/linux/nfs4.h
index adc0aff..2119c7c 100644
--- a/include/uapi/linux/nfs4.h
+++ b/include/uapi/linux/nfs4.h
@@ -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
--
2.1.0


2015-04-24 11:05:54

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 44/45] nfsd: Add support for the v4.1 dacl attribute

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.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
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(-)

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index c0e1b3c..772039e 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -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)) {
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 565c4da..474ff70 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -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
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 095854c..814ab60 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -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)
diff --git a/include/uapi/linux/nfs4.h b/include/uapi/linux/nfs4.h
index 2119c7c..64e4c6c 100644
--- a/include/uapi/linux/nfs4.h
+++ b/include/uapi/linux/nfs4.h
@@ -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
--
2.1.0


2015-04-24 11:05:54

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 45/45] nfs: Add support for the v4.1 dacl attribute

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

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/nfs/nfs4proc.c | 2 +-
fs/nfs/nfs4xdr.c | 165 ++++++++++++++++++++++++++++++++++++------------
include/linux/nfs_xdr.h | 2 +-
3 files changed, 128 insertions(+), 41 deletions(-)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index c2ba4f0..acf39e8 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -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,
};
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 8ccc2a0..52863fc 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -1675,6 +1675,12 @@ nfs4_encode_group(struct xdr_stream *xdr, const struct nfs_server *server, kgid_
return 0;
}

+static unsigned int
+nfs4_ace_mask(int minorversion)
+{
+ return minorversion == 0 ? NFS40_ACE_MASK_ALL : NFS4_ACE_MASK_ALL;
+}
+
static int
nfs4_encode_ace_who(struct xdr_stream *xdr, const struct nfs_server *server,
struct richace *ace)
@@ -1705,6 +1711,7 @@ nfs4_encode_ace_who(struct xdr_stream *xdr, const struct nfs_server *server,
static int
encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compound_hdr *hdr)
{
+ unsigned int ace_mask = nfs4_ace_mask(hdr->minorversion);
int attrlen_offset;
__be32 attrlen, *p;
struct richace *ace;
@@ -1713,9 +1720,30 @@ encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compoun
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);
+ if (arg->server->attr_bitmask[1] & FATTR4_WORD1_DACL) {
+ p = reserve_space(xdr, 3*4);
+ *p++ = cpu_to_be32(2);
+ *p++ = 0;
+ *p = cpu_to_be32(FATTR4_WORD1_DACL);
+ } else {
+ p = reserve_space(xdr, 2*4);
+ *p++ = cpu_to_be32(1);
+ *p = cpu_to_be32(FATTR4_WORD0_ACL);
+ }
+
+ /* Reject acls not understood by the server */
+ if (arg->server->attr_bitmask[1] & FATTR4_WORD1_DACL) {
+ BUILD_BUG_ON(NFS4_ACE_MASK_ALL != RICHACE_VALID_MASK);
+ } else {
+ richacl_for_each_entry(ace, arg->acl) {
+ if (ace->e_flags & RICHACE_INHERITED_ACE)
+ return -EINVAL;
+ }
+ }
+ richacl_for_each_entry(ace, arg->acl) {
+ if (ace->e_mask & ~ace_mask)
+ return -EINVAL;
+ }

attrlen_offset = xdr->buf->len;
p = xdr_reserve_space(xdr, 4);
@@ -1723,6 +1751,14 @@ encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compoun
goto fail;
p++; /* to be backfilled later */

+ if (arg->server->attr_bitmask[1] & FATTR4_WORD1_DACL) {
+ p = xdr_reserve_space(xdr, 4);
+ if (!p)
+ goto fail;
+ *p = cpu_to_be32(arg->acl->a_flags);
+ } else if (arg->acl->a_flags)
+ return -EINVAL;
+
p = xdr_reserve_space(xdr, 4);
if (!p)
goto fail;
@@ -1735,15 +1771,7 @@ encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compoun
/* 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;
@@ -2627,9 +2655,12 @@ static int nfs4_xdr_enc_getacl(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_putfh(xdr, NFS_FH(args->inode), &hdr);
replen = hdr.replen + op_decode_hdr_maxsz + 1;
- encode_getattr_two(xdr, FATTR4_WORD0_ACL, FATTR4_WORD1_MODE, &hdr);
+ if (NFS_SERVER(args->inode)->attr_bitmask[1] & FATTR4_WORD1_DACL)
+ encode_getattr_two(xdr, 0, FATTR4_WORD1_MODE | FATTR4_WORD1_DACL, &hdr);
+ else
+ 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);
@@ -5395,16 +5426,61 @@ nfs4_decode_ace_who(struct richace *ace, const struct nfs_server *server,
return error;
}

+static struct richacl *
+decode_acl_entries(struct xdr_stream *xdr, const struct nfs_server *server)
+{
+ struct richacl *acl = NULL;
+ struct richace *ace;
+ uint32_t count;
+ int status;
+ __be32 *p;
+
+ p = xdr_inline_decode(xdr, 4);
+ status = -EIO;
+ if (unlikely(!p))
+ goto out;
+ count = be32_to_cpup(p);
+ status = -ENOMEM;
+ if (count > RICHACL_XATTR_MAX_COUNT)
+ goto out;
+ acl = richacl_alloc(count, GFP_KERNEL);
+ if (!acl)
+ goto out;
+ richacl_for_each_entry(ace, acl) {
+ p = xdr_inline_decode(xdr, 4*3);
+ status = -ENOMEM;
+ if (unlikely(!p))
+ goto out; /* acl truncated */
+ ace->e_type = be32_to_cpup(p++);
+ ace->e_flags = be32_to_cpup(p++);
+ status = -EIO;
+ if (ace->e_flags & RICHACE_SPECIAL_WHO)
+ goto out;
+ ace->e_mask = be32_to_cpup(p++);
+ status = nfs4_decode_ace_who(ace, server, xdr);
+ if (status)
+ return ERR_PTR(status);
+ }
+ status = 0;
+out:
+ if (status != 0) {
+ richacl_put(acl);
+ acl = ERR_PTR(status);
+ }
+ return acl;
+}
+
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,
+ [1] = FATTR4_WORD1_MODE | FATTR4_WORD1_DACL,
};
unsigned int savep;
uint32_t attrlen,
bitmap[3] = {0};
+ struct richacl *acl = NULL;
int status;

if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
@@ -5415,41 +5491,52 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
goto out;
if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
goto out;
-
- if (likely(bitmap[0] & FATTR4_WORD0_ACL)) {
+ if (bitmap[0] & FATTR4_WORD0_ACL) {
struct richace *ace;
- uint32_t count;
- __be32 *p;

- 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)
+ status = -EIO;
+ if (bitmap[1] & FATTR4_WORD1_DACL)
+ goto out;
+
+ acl = decode_acl_entries(xdr, res->server);
+ status = PTR_ERR(acl);
+ if (IS_ERR(acl))
+ goto out;
+ status = -EIO;
+
+ richacl_for_each_entry(ace, acl) {
+ if (ace->e_flags & RICHACE_INHERITED_ACE)
goto out;
}
- } else
+ } else if (!(bitmap[1] & FATTR4_WORD1_DACL)) {
status = -EOPNOTSUPP;
+ goto out;
+ }
if ((status = decode_attr_mode(xdr, bitmap, &res->mode)) < 0)
goto out;
+ if (bitmap[1] & FATTR4_WORD1_DACL) {
+ unsigned int flags;
+ __be32 *p;
+
+ p = xdr_inline_decode(xdr, 4);
+ status = -EIO;
+ if (unlikely(!p))
+ goto out;
+ flags = be32_to_cpup(p);
+
+ acl = decode_acl_entries(xdr, res->server);
+ status = PTR_ERR(acl);
+ if (IS_ERR(acl))
+ goto out;
+ acl->a_flags = flags;
+ }
status = 0;

out:
+ if (status == 0)
+ res->acl = acl;
+ else
+ richacl_put(acl);
return status;
}

diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 77097ec..3767624 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -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;
};
--
2.1.0


2015-04-24 11:05:50

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 42/45] nfs: Add richacl support

Changes nfs to support the "system.richacl" xattr instead of "system.nfs4_acl".

The "system.nfs4_acl" xattr nfs uses directly exposes the on-the-wire format of
NFSv4's acl attribute to user space. This has at least two downsides: (1) the
format is different from other file systems, so user-space code needs to be nfs
filesystem aware; (2) when symbolic user@domain and group@domain names are used
in the acl, user-space needs to perform ID mapping in the same way as the
kernel.

Previously, when user-space requested only the length of the "system.nfs4_acl"
attribute, nfs didn't need to retrieve the entire "system.nfs4_acl" attribute;
retrieving its length was enough. With the "system.richacl" xattr, the length
of "system.richacl" cannot be computed from the length of the NFSv4 acl
attribute, so we always need to retrieve and cache the acl even when user-space
only asks for the length of the "system.richacl" attribute.

Because the nfs client now knows which kind of acl the user is trying to set,
it will now no longer sends acls with deny entries to servers which didn't
declare support for that feature. The maximum supported size of the NFSv4 acl
attribute is now hard coded in the client code and no longer depends on the
size of the buffer the user provides to the getxattr system call. When an acl
exceeds this limit, getxattr fails with errno set to ENOMEM.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/nfs/inode.c | 3 -
fs/nfs/nfs4proc.c | 354 ++++++++++++++++++++--------------------------
fs/nfs/nfs4xdr.c | 224 +++++++++++++++++++++++++----
fs/nfs/super.c | 4 +-
include/linux/nfs_fs.h | 1 -
include/linux/nfs_fs_sb.h | 2 +
include/linux/nfs_xdr.h | 8 +-
7 files changed, 357 insertions(+), 239 deletions(-)

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index d42dff6..e67f72e 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -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);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 8c50670..c2ba4f0 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -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:
- 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;
}

-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 = { };
- 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;
}

-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);
+ 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;
}

-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);
- 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;
+
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);
+ } 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
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 4372fc4..8ccc2a0 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -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
@@ -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;
@@ -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;
}
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 322b2de02..fb904ec 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -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);
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index b01ccf3..4eac89d7 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -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;
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 5e1273d..df994aa 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -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
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 92134cf..77097ec 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -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;
};

--
2.1.0


2015-04-24 11:05:48

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 40/45] nfs: Remove unused xdr page offsets in getacl/setacl arguments

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.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/nfs/nfs4proc.c | 5 ++---
fs/nfs/nfs4xdr.c | 4 ++--
include/linux/nfs_xdr.h | 2 --
3 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 627f37c..8c50670 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -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);
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index dd7a800..4372fc4 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -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;
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 4cb3eaa..92134cf 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -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;
};

--
2.1.0


2015-04-24 11:05:49

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 41/45] rpc: Allow to demand-allocate pages to encode into

When encoding large, variable-length objects such as acls into xdr_bufs, it is
easier to allocate buffer pages on demand rather than computing the required
buffer size beforehand.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
net/sunrpc/xdr.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index 4439ac4..062951b 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -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
--
2.1.0


2015-04-24 11:05:46

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 39/45] nfs3: Return posix acl encode errors

When an error occurs when encoding a posix acl, return the error instead of
causing a BUG().

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/nfs/nfs3xdr.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index 8b880f7..de6f12c 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -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;
}

--
2.1.0


2015-04-24 11:05:36

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 32/45] nfsd: Remove dead declarations

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/nfsd/nfs4acl.c | 12 ------------
1 file changed, 12 deletions(-)

diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c
index 59fd766..c630651 100644
--- a/fs/nfsd/nfs4acl.c
+++ b/fs/nfsd/nfs4acl.c
@@ -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);
--
2.1.0


2015-04-24 11:05:35

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 31/45] richacl: Create acl with masks applied in richacl_from_mode()

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.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/richacl_compat.c | 85 ++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 68 insertions(+), 17 deletions(-)

diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
index 8ebe772..33fc50c 100644
--- a/fs/richacl_compat.c
+++ b/fs/richacl_compat.c
@@ -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);
--
2.1.0


2015-04-24 11:05:44

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 37/45] nfs/sunrpc: No more encode and decode function pointer casting

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.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
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(-)

diff --git a/fs/lockd/clnt4xdr.c b/fs/lockd/clnt4xdr.c
index d3e40db..693f71a 100644
--- a/fs/lockd/clnt4xdr.c
+++ b/fs/lockd/clnt4xdr.c
@@ -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, \
diff --git a/fs/lockd/clntxdr.c b/fs/lockd/clntxdr.c
index 3e9f787..6ca581b 100644
--- a/fs/lockd/clntxdr.c
+++ b/fs/lockd/clntxdr.c
@@ -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, \
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index 47a32b6..395c39f 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -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,
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
index 99a4528..c02962a 100644
--- a/fs/nfs/mount_clnt.c
+++ b/fs/nfs/mount_clnt.c
@@ -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",
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
index b4e03ed..91a988a 100644
--- a/fs/nfs/nfs2xdr.c
+++ b/fs/nfs/nfs2xdr.c
@@ -568,8 +568,9 @@ out_default:

static void nfs2_xdr_enc_fhandle(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_fh *fh)
+ void *obj)
{
+ const struct nfs_fh *fh = obj;
encode_fhandle(xdr, fh);
}

@@ -583,23 +584,26 @@ static void nfs2_xdr_enc_fhandle(struct rpc_rqst *req,
*/
static void nfs2_xdr_enc_sattrargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_sattrargs *args)
+ void *obj)
{
+ const struct nfs_sattrargs *args = obj;
encode_fhandle(xdr, args->fh);
encode_sattr(xdr, args->sattr);
}

static void nfs2_xdr_enc_diropargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_diropargs *args)
+ void *obj)
{
+ const struct nfs_diropargs *args = obj;
encode_diropargs(xdr, args->fh, args->name, args->len);
}

static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_readlinkargs *args)
+ void *obj)
{
+ const struct nfs_readlinkargs *args = obj;
encode_fhandle(xdr, args->fh);
prepare_reply_buffer(req, args->pages, args->pgbase,
args->pglen, NFS_readlinkres_sz);
@@ -632,8 +636,9 @@ static void encode_readargs(struct xdr_stream *xdr,

static void nfs2_xdr_enc_readargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_pgio_args *args)
+ void *obj)
{
+ const struct nfs_pgio_args *args = obj;
encode_readargs(xdr, args);
prepare_reply_buffer(req, args->pages, args->pgbase,
args->count, NFS_readres_sz);
@@ -672,8 +677,9 @@ static void encode_writeargs(struct xdr_stream *xdr,

static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_pgio_args *args)
+ void *obj)
{
+ const struct nfs_pgio_args *args = obj;
encode_writeargs(xdr, args);
xdr->buf->flags |= XDRBUF_WRITE;
}
@@ -688,16 +694,18 @@ static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req,
*/
static void nfs2_xdr_enc_createargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_createargs *args)
+ void *obj)
{
+ const struct nfs_createargs *args = obj;
encode_diropargs(xdr, args->fh, args->name, args->len);
encode_sattr(xdr, args->sattr);
}

static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_removeargs *args)
+ void *obj)
{
+ const struct nfs_removeargs *args = obj;
encode_diropargs(xdr, args->fh, args->name.name, args->name.len);
}

@@ -711,8 +719,9 @@ static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req,
*/
static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_renameargs *args)
+ void *obj)
{
+ const struct nfs_renameargs *args = obj;
const struct qstr *old = args->old_name;
const struct qstr *new = args->new_name;

@@ -730,8 +739,9 @@ static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req,
*/
static void nfs2_xdr_enc_linkargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_linkargs *args)
+ void *obj)
{
+ const struct nfs_linkargs *args = obj;
encode_fhandle(xdr, args->fromfh);
encode_diropargs(xdr, args->tofh, args->toname, args->tolen);
}
@@ -747,8 +757,9 @@ static void nfs2_xdr_enc_linkargs(struct rpc_rqst *req,
*/
static void nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_symlinkargs *args)
+ void *obj)
{
+ const struct nfs_symlinkargs *args = obj;
encode_diropargs(xdr, args->fromfh, args->fromname, args->fromlen);
encode_path(xdr, args->pages, args->pathlen);
encode_sattr(xdr, args->sattr);
@@ -777,8 +788,9 @@ static void encode_readdirargs(struct xdr_stream *xdr,

static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_readdirargs *args)
+ void *obj)
{
+ const struct nfs_readdirargs *args = obj;
encode_readdirargs(xdr, args);
prepare_reply_buffer(req, args->pages, 0,
args->count, NFS_readdirres_sz);
@@ -809,14 +821,16 @@ out_default:
}

static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_fattr *result)
+ void *obj)
{
+ struct nfs_fattr *result = obj;
return decode_attrstat(xdr, result, NULL);
}

static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_diropok *result)
+ void *obj)
{
+ struct nfs_diropok *result = obj;
return decode_diropres(xdr, result);
}

@@ -860,8 +874,9 @@ out_default:
* };
*/
static int nfs2_xdr_dec_readres(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_pgio_res *result)
+ void *obj)
{
+ struct nfs_pgio_res *result = obj;
enum nfs_stat status;
int error;

@@ -882,9 +897,10 @@ out_default:
}

static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_pgio_res *result)
+ void *obj)
{
/* All NFSv2 writes are "file sync" writes */
+ struct nfs_pgio_res *result = obj;
result->verf->committed = NFS_FILE_SYNC;
return decode_attrstat(xdr, result->fattr, &result->op_status);
}
@@ -1034,8 +1050,9 @@ out_overflow:
}

static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs2_fsstat *result)
+ void *obj)
{
+ struct nfs2_fsstat *result = obj;
enum nfs_stat status;
int error;

@@ -1118,8 +1135,8 @@ static int nfs_stat_to_errno(enum nfs_stat status)
#define PROC(proc, argtype, restype, timer) \
[NFSPROC_##proc] = { \
.p_proc = NFSPROC_##proc, \
- .p_encode = (kxdreproc_t)nfs2_xdr_enc_##argtype, \
- .p_decode = (kxdrdproc_t)nfs2_xdr_dec_##restype, \
+ .p_encode = nfs2_xdr_enc_##argtype, \
+ .p_decode = nfs2_xdr_dec_##restype, \
.p_arglen = NFS_##argtype##_sz, \
.p_replen = NFS_##restype##_sz, \
.p_timer = timer, \
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index 53852a4..d8f7744 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -846,8 +846,9 @@ static void encode_diropargs3(struct xdr_stream *xdr, const struct nfs_fh *fh,
*/
static void nfs3_xdr_enc_getattr3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_fh *fh)
+ void *obj)
{
+ const struct nfs_fh *fh = obj;
encode_nfs_fh3(xdr, fh);
}

@@ -884,8 +885,9 @@ static void encode_sattrguard3(struct xdr_stream *xdr,

static void nfs3_xdr_enc_setattr3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs3_sattrargs *args)
+ void *obj)
{
+ const struct nfs3_sattrargs *args = obj;
encode_nfs_fh3(xdr, args->fh);
encode_sattr3(xdr, args->sattr);
encode_sattrguard3(xdr, args);
@@ -900,8 +902,9 @@ static void nfs3_xdr_enc_setattr3args(struct rpc_rqst *req,
*/
static void nfs3_xdr_enc_lookup3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs3_diropargs *args)
+ void *obj)
{
+ const struct nfs3_diropargs *args = obj;
encode_diropargs3(xdr, args->fh, args->name, args->len);
}

@@ -922,8 +925,9 @@ static void encode_access3args(struct xdr_stream *xdr,

static void nfs3_xdr_enc_access3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs3_accessargs *args)
+ void *obj)
{
+ const struct nfs3_accessargs *args = obj;
encode_access3args(xdr, args);
}

@@ -936,8 +940,9 @@ static void nfs3_xdr_enc_access3args(struct rpc_rqst *req,
*/
static void nfs3_xdr_enc_readlink3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs3_readlinkargs *args)
+ void *obj)
{
+ const struct nfs3_readlinkargs *args = obj;
encode_nfs_fh3(xdr, args->fh);
prepare_reply_buffer(req, args->pages, args->pgbase,
args->pglen, NFS3_readlinkres_sz);
@@ -966,8 +971,9 @@ static void encode_read3args(struct xdr_stream *xdr,

static void nfs3_xdr_enc_read3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_pgio_args *args)
+ void *obj)
{
+ const struct nfs_pgio_args *args = obj;
encode_read3args(xdr, args);
prepare_reply_buffer(req, args->pages, args->pgbase,
args->count, NFS3_readres_sz);
@@ -1008,8 +1014,9 @@ static void encode_write3args(struct xdr_stream *xdr,

static void nfs3_xdr_enc_write3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_pgio_args *args)
+ void *obj)
{
+ const struct nfs_pgio_args *args = obj;
encode_write3args(xdr, args);
xdr->buf->flags |= XDRBUF_WRITE;
}
@@ -1055,8 +1062,9 @@ static void encode_createhow3(struct xdr_stream *xdr,

static void nfs3_xdr_enc_create3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs3_createargs *args)
+ void *obj)
{
+ const struct nfs3_createargs *args = obj;
encode_diropargs3(xdr, args->fh, args->name, args->len);
encode_createhow3(xdr, args);
}
@@ -1071,8 +1079,9 @@ static void nfs3_xdr_enc_create3args(struct rpc_rqst *req,
*/
static void nfs3_xdr_enc_mkdir3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs3_mkdirargs *args)
+ void *obj)
{
+ const struct nfs3_mkdirargs *args = obj;
encode_diropargs3(xdr, args->fh, args->name, args->len);
encode_sattr3(xdr, args->sattr);
}
@@ -1099,8 +1108,9 @@ static void encode_symlinkdata3(struct xdr_stream *xdr,

static void nfs3_xdr_enc_symlink3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs3_symlinkargs *args)
+ void *obj)
{
+ const struct nfs3_symlinkargs *args = obj;
encode_diropargs3(xdr, args->fromfh, args->fromname, args->fromlen);
encode_symlinkdata3(xdr, args);
}
@@ -1159,8 +1169,9 @@ static void encode_mknoddata3(struct xdr_stream *xdr,

static void nfs3_xdr_enc_mknod3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs3_mknodargs *args)
+ void *obj)
{
+ const struct nfs3_mknodargs *args = obj;
encode_diropargs3(xdr, args->fh, args->name, args->len);
encode_mknoddata3(xdr, args);
}
@@ -1174,8 +1185,9 @@ static void nfs3_xdr_enc_mknod3args(struct rpc_rqst *req,
*/
static void nfs3_xdr_enc_remove3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_removeargs *args)
+ void *obj)
{
+ const struct nfs_removeargs *args = obj;
encode_diropargs3(xdr, args->fh, args->name.name, args->name.len);
}

@@ -1189,8 +1201,9 @@ static void nfs3_xdr_enc_remove3args(struct rpc_rqst *req,
*/
static void nfs3_xdr_enc_rename3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_renameargs *args)
+ void *obj)
{
+ const struct nfs_renameargs *args = obj;
const struct qstr *old = args->old_name;
const struct qstr *new = args->new_name;

@@ -1208,8 +1221,9 @@ static void nfs3_xdr_enc_rename3args(struct rpc_rqst *req,
*/
static void nfs3_xdr_enc_link3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs3_linkargs *args)
+ void *obj)
{
+ const struct nfs3_linkargs *args = obj;
encode_nfs_fh3(xdr, args->fromfh);
encode_diropargs3(xdr, args->tofh, args->toname, args->tolen);
}
@@ -1239,8 +1253,9 @@ static void encode_readdir3args(struct xdr_stream *xdr,

static void nfs3_xdr_enc_readdir3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs3_readdirargs *args)
+ void *obj)
{
+ const struct nfs3_readdirargs *args = obj;
encode_readdir3args(xdr, args);
prepare_reply_buffer(req, args->pages, 0,
args->count, NFS3_readdirres_sz);
@@ -1279,8 +1294,9 @@ static void encode_readdirplus3args(struct xdr_stream *xdr,

static void nfs3_xdr_enc_readdirplus3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs3_readdirargs *args)
+ void *obj)
{
+ const struct nfs3_readdirargs *args = obj;
encode_readdirplus3args(xdr, args);
prepare_reply_buffer(req, args->pages, 0,
args->count, NFS3_readdirres_sz);
@@ -1309,8 +1325,9 @@ static void encode_commit3args(struct xdr_stream *xdr,

static void nfs3_xdr_enc_commit3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs_commitargs *args)
+ void *obj)
{
+ const struct nfs_commitargs *args = obj;
encode_commit3args(xdr, args);
}

@@ -1318,8 +1335,9 @@ static void nfs3_xdr_enc_commit3args(struct rpc_rqst *req,

static void nfs3_xdr_enc_getacl3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs3_getaclargs *args)
+ void *obj)
{
+ const struct nfs3_getaclargs *args = obj;
encode_nfs_fh3(xdr, args->fh);
encode_uint32(xdr, args->mask);
if (args->mask & (NFS_ACL | NFS_DFACL))
@@ -1330,8 +1348,9 @@ static void nfs3_xdr_enc_getacl3args(struct rpc_rqst *req,

static void nfs3_xdr_enc_setacl3args(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs3_setaclargs *args)
+ void *obj)
{
+ const struct nfs3_setaclargs *args = obj;
unsigned int base;
int error;

@@ -1381,8 +1400,9 @@ static void nfs3_xdr_enc_setacl3args(struct rpc_rqst *req,
*/
static int nfs3_xdr_dec_getattr3res(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs_fattr *result)
+ void *obj)
{
+ struct nfs_fattr *result = obj;
enum nfs_stat status;
int error;

@@ -1418,8 +1438,9 @@ out_default:
*/
static int nfs3_xdr_dec_setattr3res(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs_fattr *result)
+ void *obj)
{
+ struct nfs_fattr *result = obj;
enum nfs_stat status;
int error;

@@ -1459,8 +1480,9 @@ out_status:
*/
static int nfs3_xdr_dec_lookup3res(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs3_diropres *result)
+ void *obj)
{
+ struct nfs3_diropres *result = obj;
enum nfs_stat status;
int error;

@@ -1506,8 +1528,9 @@ out_default:
*/
static int nfs3_xdr_dec_access3res(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs3_accessres *result)
+ void *obj)
{
+ struct nfs3_accessres *result = obj;
enum nfs_stat status;
int error;

@@ -1547,8 +1570,9 @@ out_default:
*/
static int nfs3_xdr_dec_readlink3res(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs_fattr *result)
+ void *obj)
{
+ struct nfs_fattr *result = obj;
enum nfs_stat status;
int error;

@@ -1625,8 +1649,9 @@ out_overflow:
}

static int nfs3_xdr_dec_read3res(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_pgio_res *result)
+ void *obj)
{
+ struct nfs_pgio_res *result = obj;
enum nfs_stat status;
int error;

@@ -1698,8 +1723,9 @@ out_eio:
}

static int nfs3_xdr_dec_write3res(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_pgio_res *result)
+ void *obj)
{
+ struct nfs_pgio_res *result = obj;
enum nfs_stat status;
int error;

@@ -1763,8 +1789,9 @@ out:

static int nfs3_xdr_dec_create3res(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs3_diropres *result)
+ void *obj)
{
+ struct nfs3_diropres *result = obj;
enum nfs_stat status;
int error;

@@ -1803,8 +1830,9 @@ out_default:
*/
static int nfs3_xdr_dec_remove3res(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs_removeres *result)
+ void *obj)
{
+ struct nfs_removeres *result = obj;
enum nfs_stat status;
int error;

@@ -1844,8 +1872,9 @@ out_status:
*/
static int nfs3_xdr_dec_rename3res(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs_renameres *result)
+ void *obj)
{
+ struct nfs_renameres *result = obj;
enum nfs_stat status;
int error;

@@ -1887,8 +1916,9 @@ out_status:
* };
*/
static int nfs3_xdr_dec_link3res(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs3_linkres *result)
+ void *obj)
{
+ struct nfs3_linkres *result = obj;
enum nfs_stat status;
int error;

@@ -2071,8 +2101,9 @@ out:

static int nfs3_xdr_dec_readdir3res(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs3_readdirres *result)
+ void *obj)
{
+ struct nfs3_readdirres *result = obj;
enum nfs_stat status;
int error;

@@ -2139,8 +2170,9 @@ out_overflow:

static int nfs3_xdr_dec_fsstat3res(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs_fsstat *result)
+ void *obj)
{
+ struct nfs_fsstat *result = obj;
enum nfs_stat status;
int error;

@@ -2215,8 +2247,9 @@ out_overflow:

static int nfs3_xdr_dec_fsinfo3res(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs_fsinfo *result)
+ void *obj)
{
+ struct nfs_fsinfo *result = obj;
enum nfs_stat status;
int error;

@@ -2278,8 +2311,9 @@ out_overflow:

static int nfs3_xdr_dec_pathconf3res(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs_pathconf *result)
+ void *obj)
{
+ struct nfs_pathconf *result = obj;
enum nfs_stat status;
int error;

@@ -2319,8 +2353,9 @@ out_status:
*/
static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs_commitres *result)
+ void *obj)
{
+ struct nfs_commitres *result = obj;
enum nfs_stat status;
int error;

@@ -2388,8 +2423,9 @@ out:

static int nfs3_xdr_dec_getacl3res(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs3_getaclres *result)
+ void *obj)
{
+ struct nfs3_getaclres *result = obj;
enum nfs_stat status;
int error;

@@ -2407,8 +2443,9 @@ out_default:

static int nfs3_xdr_dec_setacl3res(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs_fattr *result)
+ void *obj)
{
+ struct nfs_fattr *result = obj;
enum nfs_stat status;
int error;

@@ -2494,8 +2531,8 @@ static int nfs3_stat_to_errno(enum nfs_stat status)
#define PROC(proc, argtype, restype, timer) \
[NFS3PROC_##proc] = { \
.p_proc = NFS3PROC_##proc, \
- .p_encode = (kxdreproc_t)nfs3_xdr_enc_##argtype##3args, \
- .p_decode = (kxdrdproc_t)nfs3_xdr_dec_##restype##3res, \
+ .p_encode = nfs3_xdr_enc_##argtype##3args, \
+ .p_decode = nfs3_xdr_dec_##restype##3res, \
.p_arglen = NFS3_##argtype##args_sz, \
.p_replen = NFS3_##restype##res_sz, \
.p_timer = timer, \
@@ -2537,8 +2574,8 @@ const struct rpc_version nfs_version3 = {
static struct rpc_procinfo nfs3_acl_procedures[] = {
[ACLPROC3_GETACL] = {
.p_proc = ACLPROC3_GETACL,
- .p_encode = (kxdreproc_t)nfs3_xdr_enc_getacl3args,
- .p_decode = (kxdrdproc_t)nfs3_xdr_dec_getacl3res,
+ .p_encode = nfs3_xdr_enc_getacl3args,
+ .p_decode = nfs3_xdr_dec_getacl3res,
.p_arglen = ACL3_getaclargs_sz,
.p_replen = ACL3_getaclres_sz,
.p_timer = 1,
@@ -2546,8 +2583,8 @@ static struct rpc_procinfo nfs3_acl_procedures[] = {
},
[ACLPROC3_SETACL] = {
.p_proc = ACLPROC3_SETACL,
- .p_encode = (kxdreproc_t)nfs3_xdr_enc_setacl3args,
- .p_decode = (kxdrdproc_t)nfs3_xdr_dec_setacl3res,
+ .p_encode = nfs3_xdr_enc_setacl3args,
+ .p_decode = nfs3_xdr_dec_setacl3res,
.p_arglen = ACL3_setaclargs_sz,
.p_replen = ACL3_setaclres_sz,
.p_timer = 0,
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 36b5e95..245f20e 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -2060,8 +2060,9 @@ static u32 nfs4_xdr_minorversion(const struct nfs4_sequence_args *args)
* Encode an ACCESS request
*/
static void nfs4_xdr_enc_access(struct rpc_rqst *req, struct xdr_stream *xdr,
- const struct nfs4_accessargs *args)
+ void *obj)
{
+ const struct nfs4_accessargs *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2078,8 +2079,9 @@ static void nfs4_xdr_enc_access(struct rpc_rqst *req, struct xdr_stream *xdr,
* Encode LOOKUP request
*/
static void nfs4_xdr_enc_lookup(struct rpc_rqst *req, struct xdr_stream *xdr,
- const struct nfs4_lookup_arg *args)
+ void *obj)
{
+ const struct nfs4_lookup_arg *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2098,8 +2100,9 @@ static void nfs4_xdr_enc_lookup(struct rpc_rqst *req, struct xdr_stream *xdr,
*/
static void nfs4_xdr_enc_lookup_root(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs4_lookup_root_arg *args)
+ void *obj)
{
+ const struct nfs4_lookup_root_arg *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2116,8 +2119,9 @@ static void nfs4_xdr_enc_lookup_root(struct rpc_rqst *req,
* Encode REMOVE request
*/
static void nfs4_xdr_enc_remove(struct rpc_rqst *req, struct xdr_stream *xdr,
- const struct nfs_removeargs *args)
+ void *obj)
{
+ const struct nfs_removeargs *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2133,8 +2137,9 @@ static void nfs4_xdr_enc_remove(struct rpc_rqst *req, struct xdr_stream *xdr,
* Encode RENAME request
*/
static void nfs4_xdr_enc_rename(struct rpc_rqst *req, struct xdr_stream *xdr,
- const struct nfs_renameargs *args)
+ void *obj)
{
+ const struct nfs_renameargs *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2152,8 +2157,9 @@ static void nfs4_xdr_enc_rename(struct rpc_rqst *req, struct xdr_stream *xdr,
* Encode LINK request
*/
static void nfs4_xdr_enc_link(struct rpc_rqst *req, struct xdr_stream *xdr,
- const struct nfs4_link_arg *args)
+ void *obj)
{
+ const struct nfs4_link_arg *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2173,8 +2179,9 @@ static void nfs4_xdr_enc_link(struct rpc_rqst *req, struct xdr_stream *xdr,
* Encode CREATE request
*/
static void nfs4_xdr_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr,
- const struct nfs4_create_arg *args)
+ void *obj)
{
+ const struct nfs4_create_arg *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2192,17 +2199,18 @@ static void nfs4_xdr_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr,
* Encode SYMLINK request
*/
static void nfs4_xdr_enc_symlink(struct rpc_rqst *req, struct xdr_stream *xdr,
- const struct nfs4_create_arg *args)
+ void *obj)
{
- nfs4_xdr_enc_create(req, xdr, args);
+ nfs4_xdr_enc_create(req, xdr, obj);
}

/*
* Encode GETATTR request
*/
static void nfs4_xdr_enc_getattr(struct rpc_rqst *req, struct xdr_stream *xdr,
- const struct nfs4_getattr_arg *args)
+ void *obj)
{
+ const struct nfs4_getattr_arg *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2218,8 +2226,9 @@ static void nfs4_xdr_enc_getattr(struct rpc_rqst *req, struct xdr_stream *xdr,
* Encode a CLOSE request
*/
static void nfs4_xdr_enc_close(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_closeargs *args)
+ void *obj)
{
+ struct nfs_closeargs *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2236,8 +2245,9 @@ static void nfs4_xdr_enc_close(struct rpc_rqst *req, struct xdr_stream *xdr,
* Encode an OPEN request
*/
static void nfs4_xdr_enc_open(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_openargs *args)
+ void *obj)
{
+ struct nfs_openargs *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2258,8 +2268,9 @@ static void nfs4_xdr_enc_open(struct rpc_rqst *req, struct xdr_stream *xdr,
*/
static void nfs4_xdr_enc_open_confirm(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs_open_confirmargs *args)
+ void *obj)
{
+ struct nfs_open_confirmargs *args = obj;
struct compound_hdr hdr = {
.nops = 0,
};
@@ -2275,8 +2286,9 @@ static void nfs4_xdr_enc_open_confirm(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_open_noattr(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs_openargs *args)
+ void *obj)
{
+ struct nfs_openargs *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2296,8 +2308,9 @@ static void nfs4_xdr_enc_open_noattr(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs_closeargs *args)
+ void *obj)
{
+ struct nfs_closeargs *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2314,8 +2327,9 @@ static void nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req,
* Encode a LOCK request
*/
static void nfs4_xdr_enc_lock(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_lock_args *args)
+ void *obj)
{
+ struct nfs_lock_args *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2331,8 +2345,9 @@ static void nfs4_xdr_enc_lock(struct rpc_rqst *req, struct xdr_stream *xdr,
* Encode a LOCKT request
*/
static void nfs4_xdr_enc_lockt(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_lockt_args *args)
+ void *obj)
{
+ struct nfs_lockt_args *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2348,8 +2363,9 @@ static void nfs4_xdr_enc_lockt(struct rpc_rqst *req, struct xdr_stream *xdr,
* Encode a LOCKU request
*/
static void nfs4_xdr_enc_locku(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_locku_args *args)
+ void *obj)
{
+ struct nfs_locku_args *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2363,8 +2379,9 @@ static void nfs4_xdr_enc_locku(struct rpc_rqst *req, struct xdr_stream *xdr,

static void nfs4_xdr_enc_release_lockowner(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs_release_lockowner_args *args)
+ void *obj)
{
+ struct nfs_release_lockowner_args *args = obj;
struct compound_hdr hdr = {
.minorversion = 0,
};
@@ -2378,8 +2395,9 @@ static void nfs4_xdr_enc_release_lockowner(struct rpc_rqst *req,
* Encode a READLINK request
*/
static void nfs4_xdr_enc_readlink(struct rpc_rqst *req, struct xdr_stream *xdr,
- const struct nfs4_readlink *args)
+ void *obj)
{
+ const struct nfs4_readlink *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2398,8 +2416,9 @@ static void nfs4_xdr_enc_readlink(struct rpc_rqst *req, struct xdr_stream *xdr,
* Encode a READDIR request
*/
static void nfs4_xdr_enc_readdir(struct rpc_rqst *req, struct xdr_stream *xdr,
- const struct nfs4_readdir_arg *args)
+ void *obj)
{
+ const struct nfs4_readdir_arg *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2421,8 +2440,9 @@ static void nfs4_xdr_enc_readdir(struct rpc_rqst *req, struct xdr_stream *xdr,
* Encode a READ request
*/
static void nfs4_xdr_enc_read(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_pgio_args *args)
+ void *obj)
{
+ struct nfs_pgio_args *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2442,8 +2462,9 @@ static void nfs4_xdr_enc_read(struct rpc_rqst *req, struct xdr_stream *xdr,
* Encode an SETATTR request
*/
static void nfs4_xdr_enc_setattr(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_setattrargs *args)
+ void *obj)
{
+ struct nfs_setattrargs *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2460,8 +2481,9 @@ static void nfs4_xdr_enc_setattr(struct rpc_rqst *req, struct xdr_stream *xdr,
* Encode a GETACL request
*/
static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_getaclargs *args)
+ void *obj)
{
+ struct nfs_getaclargs *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2483,8 +2505,9 @@ static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,
* Encode a WRITE request
*/
static void nfs4_xdr_enc_write(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_pgio_args *args)
+ void *obj)
{
+ struct nfs_pgio_args *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2503,8 +2526,9 @@ static void nfs4_xdr_enc_write(struct rpc_rqst *req, struct xdr_stream *xdr,
* a COMMIT request
*/
static void nfs4_xdr_enc_commit(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_commitargs *args)
+ void *obj)
{
+ struct nfs_commitargs *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2520,8 +2544,9 @@ static void nfs4_xdr_enc_commit(struct rpc_rqst *req, struct xdr_stream *xdr,
* FSINFO request
*/
static void nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs4_fsinfo_arg *args)
+ void *obj)
{
+ struct nfs4_fsinfo_arg *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2537,8 +2562,9 @@ static void nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, struct xdr_stream *xdr,
* a PATHCONF request
*/
static void nfs4_xdr_enc_pathconf(struct rpc_rqst *req, struct xdr_stream *xdr,
- const struct nfs4_pathconf_arg *args)
+ void *obj)
{
+ const struct nfs4_pathconf_arg *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2555,8 +2581,9 @@ static void nfs4_xdr_enc_pathconf(struct rpc_rqst *req, struct xdr_stream *xdr,
* a STATFS request
*/
static void nfs4_xdr_enc_statfs(struct rpc_rqst *req, struct xdr_stream *xdr,
- const struct nfs4_statfs_arg *args)
+ void *obj)
{
+ const struct nfs4_statfs_arg *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2574,8 +2601,9 @@ static void nfs4_xdr_enc_statfs(struct rpc_rqst *req, struct xdr_stream *xdr,
*/
static void nfs4_xdr_enc_server_caps(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs4_server_caps_arg *args)
+ void *obj)
{
+ struct nfs4_server_caps_arg *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2595,8 +2623,9 @@ static void nfs4_xdr_enc_server_caps(struct rpc_rqst *req,
* a RENEW request
*/
static void nfs4_xdr_enc_renew(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_client *clp)
+ void *obj)
{
+ struct nfs_client *clp = obj;
struct compound_hdr hdr = {
.nops = 0,
};
@@ -2611,8 +2640,9 @@ static void nfs4_xdr_enc_renew(struct rpc_rqst *req, struct xdr_stream *xdr,
*/
static void nfs4_xdr_enc_setclientid(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs4_setclientid *sc)
+ void *obj)
{
+ struct nfs4_setclientid *sc = obj;
struct compound_hdr hdr = {
.nops = 0,
};
@@ -2627,8 +2657,9 @@ static void nfs4_xdr_enc_setclientid(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs4_setclientid_res *arg)
+ void *obj)
{
+ struct nfs4_setclientid_res *arg = obj;
struct compound_hdr hdr = {
.nops = 0,
};
@@ -2643,8 +2674,9 @@ static void nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_delegreturn(struct rpc_rqst *req,
struct xdr_stream *xdr,
- const struct nfs4_delegreturnargs *args)
+ void *obj)
{
+ const struct nfs4_delegreturnargs *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2662,8 +2694,9 @@ static void nfs4_xdr_enc_delegreturn(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_fs_locations(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs4_fs_locations_arg *args)
+ void *obj)
{
+ struct nfs4_fs_locations_arg *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2695,8 +2728,9 @@ static void nfs4_xdr_enc_fs_locations(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_secinfo(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs4_secinfo_arg *args)
+ void *obj)
{
+ struct nfs4_secinfo_arg *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2713,8 +2747,9 @@ static void nfs4_xdr_enc_secinfo(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_fsid_present(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs4_fsid_present_arg *args)
+ void *obj)
{
+ struct nfs4_fsid_present_arg *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2734,8 +2769,9 @@ static void nfs4_xdr_enc_fsid_present(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_bind_conn_to_session(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs41_bind_conn_to_session_args *args)
+ void *obj)
{
+ struct nfs41_bind_conn_to_session_args *args = obj;
struct compound_hdr hdr = {
.minorversion = args->client->cl_mvops->minor_version,
};
@@ -2750,8 +2786,9 @@ static void nfs4_xdr_enc_bind_conn_to_session(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_exchange_id(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs41_exchange_id_args *args)
+ void *obj)
{
+ struct nfs41_exchange_id_args *args = obj;
struct compound_hdr hdr = {
.minorversion = args->client->cl_mvops->minor_version,
};
@@ -2766,8 +2803,9 @@ static void nfs4_xdr_enc_exchange_id(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_create_session(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs41_create_session_args *args)
+ void *obj)
{
+ struct nfs41_create_session_args *args = obj;
struct compound_hdr hdr = {
.minorversion = args->client->cl_mvops->minor_version,
};
@@ -2782,8 +2820,9 @@ static void nfs4_xdr_enc_create_session(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_destroy_session(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs4_session *session)
+ void *obj)
{
+ struct nfs4_session *session = obj;
struct compound_hdr hdr = {
.minorversion = session->clp->cl_mvops->minor_version,
};
@@ -2798,8 +2837,9 @@ static void nfs4_xdr_enc_destroy_session(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_destroy_clientid(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs_client *clp)
+ void *obj)
{
+ struct nfs_client *clp = obj;
struct compound_hdr hdr = {
.minorversion = clp->cl_mvops->minor_version,
};
@@ -2813,8 +2853,9 @@ static void nfs4_xdr_enc_destroy_clientid(struct rpc_rqst *req,
* a SEQUENCE request
*/
static void nfs4_xdr_enc_sequence(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs4_sequence_args *args)
+ void *obj)
{
+ struct nfs4_sequence_args *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(args),
};
@@ -2829,8 +2870,9 @@ static void nfs4_xdr_enc_sequence(struct rpc_rqst *req, struct xdr_stream *xdr,
*/
static void nfs4_xdr_enc_get_lease_time(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs4_get_lease_time_args *args)
+ void *obj)
{
+ struct nfs4_get_lease_time_args *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->la_seq_args),
};
@@ -2848,8 +2890,9 @@ static void nfs4_xdr_enc_get_lease_time(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_reclaim_complete(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs41_reclaim_complete_args *args)
+ void *obj)
{
+ struct nfs41_reclaim_complete_args *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args)
};
@@ -2865,8 +2908,9 @@ static void nfs4_xdr_enc_reclaim_complete(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_getdeviceinfo(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs4_getdeviceinfo_args *args)
+ void *obj)
{
+ struct nfs4_getdeviceinfo_args *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2889,8 +2933,9 @@ static void nfs4_xdr_enc_getdeviceinfo(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_layoutget(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs4_layoutget_args *args)
+ void *obj)
{
+ struct nfs4_layoutget_args *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2911,8 +2956,9 @@ static void nfs4_xdr_enc_layoutget(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_layoutcommit(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs4_layoutcommit_args *args)
+ void *obj)
{
+ struct nfs4_layoutcommit_args *args = obj;
struct nfs4_layoutcommit_data *data =
container_of(args, struct nfs4_layoutcommit_data, args);
struct compound_hdr hdr = {
@@ -2932,8 +2978,9 @@ static void nfs4_xdr_enc_layoutcommit(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_layoutreturn(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs4_layoutreturn_args *args)
+ void *obj)
{
+ struct nfs4_layoutreturn_args *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2950,8 +2997,9 @@ static void nfs4_xdr_enc_layoutreturn(struct rpc_rqst *req,
*/
static int nfs4_xdr_enc_secinfo_no_name(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs41_secinfo_no_name_args *args)
+ void *obj)
{
+ struct nfs41_secinfo_no_name_args *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2969,8 +3017,9 @@ static int nfs4_xdr_enc_secinfo_no_name(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_test_stateid(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs41_test_stateid_args *args)
+ void *obj)
{
+ struct nfs41_test_stateid_args *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -2986,8 +3035,9 @@ static void nfs4_xdr_enc_test_stateid(struct rpc_rqst *req,
*/
static void nfs4_xdr_enc_free_stateid(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs41_free_stateid_args *args)
+ void *obj)
{
+ struct nfs41_free_stateid_args *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -5927,8 +5977,9 @@ static int decode_free_stateid(struct xdr_stream *xdr,
*/
static int nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs_closeres *res)
+ void *obj)
{
+ struct nfs_closeres *res = obj;
struct compound_hdr hdr;
int status;

@@ -5953,8 +6004,9 @@ out:
* Decode ACCESS response
*/
static int nfs4_xdr_dec_access(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs4_accessres *res)
+ void *obj)
{
+ struct nfs4_accessres *res = obj;
struct compound_hdr hdr;
int status;

@@ -5979,8 +6031,9 @@ out:
* Decode LOOKUP response
*/
static int nfs4_xdr_dec_lookup(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs4_lookup_res *res)
+ void *obj)
{
+ struct nfs4_lookup_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6009,8 +6062,9 @@ out:
*/
static int nfs4_xdr_dec_lookup_root(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs4_lookup_res *res)
+ void *obj)
{
+ struct nfs4_lookup_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6035,8 +6089,9 @@ out:
* Decode REMOVE response
*/
static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs_removeres *res)
+ void *obj)
{
+ struct nfs_removeres *res = obj;
struct compound_hdr hdr;
int status;

@@ -6058,8 +6113,9 @@ out:
* Decode RENAME response
*/
static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs_renameres *res)
+ void *obj)
{
+ struct nfs_renameres *res = obj;
struct compound_hdr hdr;
int status;

@@ -6087,8 +6143,9 @@ out:
* Decode LINK response
*/
static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs4_link_res *res)
+ void *obj)
{
+ struct nfs4_link_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6126,8 +6183,9 @@ out:
* Decode CREATE response
*/
static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs4_create_res *res)
+ void *obj)
{
+ struct nfs4_create_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6155,8 +6213,9 @@ out:
* Decode SYMLINK response
*/
static int nfs4_xdr_dec_symlink(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs4_create_res *res)
+ void *obj)
{
+ struct nfs4_create_res *res = obj;
return nfs4_xdr_dec_create(rqstp, xdr, res);
}

@@ -6164,8 +6223,9 @@ static int nfs4_xdr_dec_symlink(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
* Decode GETATTR response
*/
static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs4_getattr_res *res)
+ void *obj)
{
+ struct nfs4_getattr_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6187,8 +6247,9 @@ out:
* Encode an SETACL request
*/
static void nfs4_xdr_enc_setacl(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs_setaclargs *args)
+ void *obj)
{
+ struct nfs_setaclargs *args = obj;
struct compound_hdr hdr = {
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
};
@@ -6205,8 +6266,9 @@ static void nfs4_xdr_enc_setacl(struct rpc_rqst *req, struct xdr_stream *xdr,
*/
static int
nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs_setaclres *res)
+ void *obj)
{
+ struct nfs_setaclres *res = obj;
struct compound_hdr hdr;
int status;

@@ -6229,8 +6291,9 @@ out:
*/
static int
nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs_getaclres *res)
+ void *obj)
{
+ struct nfs_getaclres *res = obj;
struct compound_hdr hdr;
int status;

@@ -6257,8 +6320,9 @@ out:
* Decode CLOSE response
*/
static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs_closeres *res)
+ void *obj)
{
+ struct nfs_closeres *res = obj;
struct compound_hdr hdr;
int status;

@@ -6289,8 +6353,9 @@ out:
* Decode OPEN response
*/
static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs_openres *res)
+ void *obj)
{
+ struct nfs_openres *res = obj;
struct compound_hdr hdr;
int status;

@@ -6321,8 +6386,9 @@ out:
*/
static int nfs4_xdr_dec_open_confirm(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs_open_confirmres *res)
+ void *obj)
{
+ struct nfs_open_confirmres *res = obj;
struct compound_hdr hdr;
int status;

@@ -6342,8 +6408,9 @@ out:
*/
static int nfs4_xdr_dec_open_noattr(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs_openres *res)
+ void *obj)
{
+ struct nfs_openres *res = obj;
struct compound_hdr hdr;
int status;

@@ -6371,8 +6438,9 @@ out:
*/
static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs_setattrres *res)
+ void *obj)
{
+ struct nfs_setattrres *res = obj;
struct compound_hdr hdr;
int status;

@@ -6397,8 +6465,9 @@ out:
* Decode LOCK response
*/
static int nfs4_xdr_dec_lock(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs_lock_res *res)
+ void *obj)
{
+ struct nfs_lock_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6420,8 +6489,9 @@ out:
* Decode LOCKT response
*/
static int nfs4_xdr_dec_lockt(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs_lockt_res *res)
+ void *obj)
{
+ struct nfs_lockt_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6443,8 +6513,9 @@ out:
* Decode LOCKU response
*/
static int nfs4_xdr_dec_locku(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs_locku_res *res)
+ void *obj)
{
+ struct nfs_locku_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6479,8 +6550,9 @@ static int nfs4_xdr_dec_release_lockowner(struct rpc_rqst *rqstp,
*/
static int nfs4_xdr_dec_readlink(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs4_readlink_res *res)
+ void *obj)
{
+ struct nfs4_readlink_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6502,8 +6574,9 @@ out:
* Decode READDIR response
*/
static int nfs4_xdr_dec_readdir(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs4_readdir_res *res)
+ void *obj)
{
+ struct nfs4_readdir_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6525,8 +6598,9 @@ out:
* Decode Read response
*/
static int nfs4_xdr_dec_read(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs_pgio_res *res)
+ void *obj)
{
+ struct nfs_pgio_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6551,8 +6625,9 @@ out:
* Decode WRITE response
*/
static int nfs4_xdr_dec_write(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs_pgio_res *res)
+ void *obj)
{
+ struct nfs_pgio_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6581,8 +6656,9 @@ out:
* Decode COMMIT response
*/
static int nfs4_xdr_dec_commit(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
- struct nfs_commitres *res)
+ void *obj)
{
+ struct nfs_commitres *res = obj;
struct compound_hdr hdr;
int status;

@@ -6605,8 +6681,9 @@ out:
* Decode FSINFO response
*/
static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs4_fsinfo_res *res)
+ void *obj)
{
+ struct nfs4_fsinfo_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6624,8 +6701,9 @@ static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, struct xdr_stream *xdr,
* Decode PATHCONF response
*/
static int nfs4_xdr_dec_pathconf(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs4_pathconf_res *res)
+ void *obj)
{
+ struct nfs4_pathconf_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6643,8 +6721,9 @@ static int nfs4_xdr_dec_pathconf(struct rpc_rqst *req, struct xdr_stream *xdr,
* Decode STATFS response
*/
static int nfs4_xdr_dec_statfs(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct nfs4_statfs_res *res)
+ void *obj)
{
+ struct nfs4_statfs_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6663,8 +6742,9 @@ static int nfs4_xdr_dec_statfs(struct rpc_rqst *req, struct xdr_stream *xdr,
*/
static int nfs4_xdr_dec_server_caps(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs4_server_caps_res *res)
+ void *obj)
{
+ struct nfs4_server_caps_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6702,8 +6782,9 @@ static int nfs4_xdr_dec_renew(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
*/
static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs4_setclientid_res *res)
+ void *obj)
{
+ struct nfs4_setclientid_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6733,8 +6814,9 @@ static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req,
*/
static int nfs4_xdr_dec_delegreturn(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs4_delegreturnres *res)
+ void *obj)
{
+ struct nfs4_delegreturnres *res = obj;
struct compound_hdr hdr;
int status;

@@ -6760,8 +6842,9 @@ out:
*/
static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req,
struct xdr_stream *xdr,
- struct nfs4_fs_locations_res *res)
+ void *obj)
{
+ struct nfs4_fs_locations_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6803,8 +6886,9 @@ out:
*/
static int nfs4_xdr_dec_secinfo(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs4_secinfo_res *res)
+ void *obj)
{
+ struct nfs4_secinfo_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6827,8 +6911,9 @@ out:
*/
static int nfs4_xdr_dec_fsid_present(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs4_fsid_present_res *res)
+ void *obj)
{
+ struct nfs4_fsid_present_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6888,8 +6973,9 @@ static int nfs4_xdr_dec_exchange_id(struct rpc_rqst *rqstp,
*/
static int nfs4_xdr_dec_create_session(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs41_create_session_res *res)
+ void *obj)
{
+ struct nfs41_create_session_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6936,8 +7022,9 @@ static int nfs4_xdr_dec_destroy_clientid(struct rpc_rqst *rqstp,
*/
static int nfs4_xdr_dec_sequence(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs4_sequence_res *res)
+ void *obj)
{
+ struct nfs4_sequence_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6952,8 +7039,9 @@ static int nfs4_xdr_dec_sequence(struct rpc_rqst *rqstp,
*/
static int nfs4_xdr_dec_get_lease_time(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs4_get_lease_time_res *res)
+ void *obj)
{
+ struct nfs4_get_lease_time_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6972,8 +7060,9 @@ static int nfs4_xdr_dec_get_lease_time(struct rpc_rqst *rqstp,
*/
static int nfs4_xdr_dec_reclaim_complete(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs41_reclaim_complete_res *res)
+ void *obj)
{
+ struct nfs41_reclaim_complete_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -6990,8 +7079,9 @@ static int nfs4_xdr_dec_reclaim_complete(struct rpc_rqst *rqstp,
*/
static int nfs4_xdr_dec_getdeviceinfo(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs4_getdeviceinfo_res *res)
+ void *obj)
{
+ struct nfs4_getdeviceinfo_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -7011,8 +7101,9 @@ out:
*/
static int nfs4_xdr_dec_layoutget(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs4_layoutget_res *res)
+ void *obj)
{
+ struct nfs4_layoutget_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -7035,8 +7126,9 @@ out:
*/
static int nfs4_xdr_dec_layoutreturn(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs4_layoutreturn_res *res)
+ void *obj)
{
+ struct nfs4_layoutreturn_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -7059,8 +7151,9 @@ out:
*/
static int nfs4_xdr_dec_layoutcommit(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs4_layoutcommit_res *res)
+ void *obj)
{
+ struct nfs4_layoutcommit_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -7086,8 +7179,9 @@ out:
*/
static int nfs4_xdr_dec_secinfo_no_name(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs4_secinfo_res *res)
+ void *obj)
{
+ struct nfs4_secinfo_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -7110,8 +7204,9 @@ out:
*/
static int nfs4_xdr_dec_test_stateid(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs41_test_stateid_res *res)
+ void *obj)
{
+ struct nfs41_test_stateid_res *res = obj;
struct compound_hdr hdr;
int status;

@@ -7131,8 +7226,9 @@ out:
*/
static int nfs4_xdr_dec_free_stateid(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
- struct nfs41_free_stateid_res *res)
+ void *obj)
{
+ struct nfs41_free_stateid_res *res = obj;
struct compound_hdr hdr;
int status;

diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 5827785..a7167b8 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -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, \
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index 70c6b92..7177815 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -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);
diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.c b/net/sunrpc/auth_gss/gss_rpc_upcall.c
index 59eeed4..d06117a 100644
--- a/net/sunrpc/auth_gss/gss_rpc_upcall.c
+++ b/net/sunrpc/auth_gss/gss_rpc_upcall.c
@@ -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, \
diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c
index 1ec19f6..6b9dc37 100644
--- a/net/sunrpc/auth_gss/gss_rpc_xdr.c
+++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c
@@ -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;

diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.h b/net/sunrpc/auth_gss/gss_rpc_xdr.h
index 9d88c62..eb98597 100644
--- a/net/sunrpc/auth_gss/gss_rpc_xdr.h
+++ b/net/sunrpc/auth_gss/gss_rpc_xdr.h
@@ -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
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index e6ce151..47a9a11 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -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;
}
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
index cf5770d..7c71283 100644
--- a/net/sunrpc/rpcb_clnt.c
+++ b/net/sunrpc/rpcb_clnt.c
@@ -845,8 +845,9 @@ static void rpcb_getport_done(struct rpc_task *child, void *data)
*/

static void rpcb_enc_mapping(struct rpc_rqst *req, struct xdr_stream *xdr,
- const struct rpcbind_args *rpcb)
+ void *obj)
{
+ const struct rpcbind_args *rpcb = obj;
__be32 *p;

dprintk("RPC: %5u encoding PMAP_%s call (%u, %u, %d, %u)\n",
@@ -862,8 +863,9 @@ static void rpcb_enc_mapping(struct rpc_rqst *req, struct xdr_stream *xdr,
}

static int rpcb_dec_getport(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct rpcbind_args *rpcb)
+ void *obj)
{
+ struct rpcbind_args *rpcb = obj;
unsigned long port;
__be32 *p;

@@ -884,8 +886,9 @@ static int rpcb_dec_getport(struct rpc_rqst *req, struct xdr_stream *xdr,
}

static int rpcb_dec_set(struct rpc_rqst *req, struct xdr_stream *xdr,
- unsigned int *boolp)
+ void *obj)
{
+ unsigned int *boolp = obj;
__be32 *p;

p = xdr_inline_decode(xdr, 4);
@@ -919,8 +922,9 @@ static void encode_rpcb_string(struct xdr_stream *xdr, const char *string,
}

static void rpcb_enc_getaddr(struct rpc_rqst *req, struct xdr_stream *xdr,
- const struct rpcbind_args *rpcb)
+ void *obj)
{
+ const struct rpcbind_args *rpcb = obj;
__be32 *p;

dprintk("RPC: %5u encoding RPCB_%s call (%u, %u, '%s', '%s')\n",
@@ -939,8 +943,9 @@ static void rpcb_enc_getaddr(struct rpc_rqst *req, struct xdr_stream *xdr,
}

static int rpcb_dec_getaddr(struct rpc_rqst *req, struct xdr_stream *xdr,
- struct rpcbind_args *rpcb)
+ void *obj)
{
+ struct rpcbind_args *rpcb = obj;
struct sockaddr_storage address;
struct sockaddr *sap = (struct sockaddr *)&address;
__be32 *p;
@@ -994,8 +999,8 @@ out_fail:
static struct rpc_procinfo rpcb_procedures2[] = {
[RPCBPROC_SET] = {
.p_proc = RPCBPROC_SET,
- .p_encode = (kxdreproc_t)rpcb_enc_mapping,
- .p_decode = (kxdrdproc_t)rpcb_dec_set,
+ .p_encode = rpcb_enc_mapping,
+ .p_decode = rpcb_dec_set,
.p_arglen = RPCB_mappingargs_sz,
.p_replen = RPCB_setres_sz,
.p_statidx = RPCBPROC_SET,
@@ -1004,8 +1009,8 @@ static struct rpc_procinfo rpcb_procedures2[] = {
},
[RPCBPROC_UNSET] = {
.p_proc = RPCBPROC_UNSET,
- .p_encode = (kxdreproc_t)rpcb_enc_mapping,
- .p_decode = (kxdrdproc_t)rpcb_dec_set,
+ .p_encode = rpcb_enc_mapping,
+ .p_decode = rpcb_dec_set,
.p_arglen = RPCB_mappingargs_sz,
.p_replen = RPCB_setres_sz,
.p_statidx = RPCBPROC_UNSET,
@@ -1014,8 +1019,8 @@ static struct rpc_procinfo rpcb_procedures2[] = {
},
[RPCBPROC_GETPORT] = {
.p_proc = RPCBPROC_GETPORT,
- .p_encode = (kxdreproc_t)rpcb_enc_mapping,
- .p_decode = (kxdrdproc_t)rpcb_dec_getport,
+ .p_encode = rpcb_enc_mapping,
+ .p_decode = rpcb_dec_getport,
.p_arglen = RPCB_mappingargs_sz,
.p_replen = RPCB_getportres_sz,
.p_statidx = RPCBPROC_GETPORT,
@@ -1027,8 +1032,8 @@ static struct rpc_procinfo rpcb_procedures2[] = {
static struct rpc_procinfo rpcb_procedures3[] = {
[RPCBPROC_SET] = {
.p_proc = RPCBPROC_SET,
- .p_encode = (kxdreproc_t)rpcb_enc_getaddr,
- .p_decode = (kxdrdproc_t)rpcb_dec_set,
+ .p_encode = rpcb_enc_getaddr,
+ .p_decode = rpcb_dec_set,
.p_arglen = RPCB_getaddrargs_sz,
.p_replen = RPCB_setres_sz,
.p_statidx = RPCBPROC_SET,
@@ -1037,8 +1042,8 @@ static struct rpc_procinfo rpcb_procedures3[] = {
},
[RPCBPROC_UNSET] = {
.p_proc = RPCBPROC_UNSET,
- .p_encode = (kxdreproc_t)rpcb_enc_getaddr,
- .p_decode = (kxdrdproc_t)rpcb_dec_set,
+ .p_encode = rpcb_enc_getaddr,
+ .p_decode = rpcb_dec_set,
.p_arglen = RPCB_getaddrargs_sz,
.p_replen = RPCB_setres_sz,
.p_statidx = RPCBPROC_UNSET,
@@ -1047,8 +1052,8 @@ static struct rpc_procinfo rpcb_procedures3[] = {
},
[RPCBPROC_GETADDR] = {
.p_proc = RPCBPROC_GETADDR,
- .p_encode = (kxdreproc_t)rpcb_enc_getaddr,
- .p_decode = (kxdrdproc_t)rpcb_dec_getaddr,
+ .p_encode = rpcb_enc_getaddr,
+ .p_decode = rpcb_dec_getaddr,
.p_arglen = RPCB_getaddrargs_sz,
.p_replen = RPCB_getaddrres_sz,
.p_statidx = RPCBPROC_GETADDR,
@@ -1060,8 +1065,8 @@ static struct rpc_procinfo rpcb_procedures3[] = {
static struct rpc_procinfo rpcb_procedures4[] = {
[RPCBPROC_SET] = {
.p_proc = RPCBPROC_SET,
- .p_encode = (kxdreproc_t)rpcb_enc_getaddr,
- .p_decode = (kxdrdproc_t)rpcb_dec_set,
+ .p_encode = rpcb_enc_getaddr,
+ .p_decode = rpcb_dec_set,
.p_arglen = RPCB_getaddrargs_sz,
.p_replen = RPCB_setres_sz,
.p_statidx = RPCBPROC_SET,
@@ -1070,8 +1075,8 @@ static struct rpc_procinfo rpcb_procedures4[] = {
},
[RPCBPROC_UNSET] = {
.p_proc = RPCBPROC_UNSET,
- .p_encode = (kxdreproc_t)rpcb_enc_getaddr,
- .p_decode = (kxdrdproc_t)rpcb_dec_set,
+ .p_encode = rpcb_enc_getaddr,
+ .p_decode = rpcb_dec_set,
.p_arglen = RPCB_getaddrargs_sz,
.p_replen = RPCB_setres_sz,
.p_statidx = RPCBPROC_UNSET,
@@ -1080,8 +1085,8 @@ static struct rpc_procinfo rpcb_procedures4[] = {
},
[RPCBPROC_GETADDR] = {
.p_proc = RPCBPROC_GETADDR,
- .p_encode = (kxdreproc_t)rpcb_enc_getaddr,
- .p_decode = (kxdrdproc_t)rpcb_dec_getaddr,
+ .p_encode = rpcb_enc_getaddr,
+ .p_decode = rpcb_dec_getaddr,
.p_arglen = RPCB_getaddrargs_sz,
.p_replen = RPCB_getaddrres_sz,
.p_statidx = RPCBPROC_GETADDR,
--
2.1.0


2015-04-24 11:05:45

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 38/45] nfs/sunrpc: Return status code from encode functions

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.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
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(-)

diff --git a/fs/lockd/clnt4xdr.c b/fs/lockd/clnt4xdr.c
index 693f71a..61df64c 100644
--- a/fs/lockd/clnt4xdr.c
+++ b/fs/lockd/clnt4xdr.c
@@ -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;
}


diff --git a/fs/lockd/clntxdr.c b/fs/lockd/clntxdr.c
index 6ca581b..e05d3035 100644
--- a/fs/lockd/clntxdr.c
+++ b/fs/lockd/clntxdr.c
@@ -372,9 +372,9 @@ static void encode_nlm_lock(struct xdr_stream *xdr,
* struct nlm_lock alock;
* };
*/
-static void nlm_xdr_enc_testargs(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nlm_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;
@@ -382,6 +382,7 @@ static void nlm_xdr_enc_testargs(struct rpc_rqst *req,
encode_cookie(xdr, &args->cookie);
encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
encode_nlm_lock(xdr, lock);
+ return 0;
}

/*
@@ -394,9 +395,9 @@ static void nlm_xdr_enc_testargs(struct rpc_rqst *req,
* int state;
* };
*/
-static void nlm_xdr_enc_lockargs(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nlm_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;
@@ -407,6 +408,7 @@ static void nlm_xdr_enc_lockargs(struct rpc_rqst *req,
encode_nlm_lock(xdr, lock);
encode_bool(xdr, args->reclaim);
encode_int32(xdr, args->state);
+ return 0;
}

/*
@@ -417,9 +419,9 @@ static void nlm_xdr_enc_lockargs(struct rpc_rqst *req,
* struct nlm_lock alock;
* };
*/
-static void nlm_xdr_enc_cancargs(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nlm_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;
@@ -428,6 +430,7 @@ static void nlm_xdr_enc_cancargs(struct rpc_rqst *req,
encode_bool(xdr, args->block);
encode_bool(xdr, lock->fl.fl_type == F_WRLCK);
encode_nlm_lock(xdr, lock);
+ return 0;
}

/*
@@ -436,15 +439,16 @@ static void nlm_xdr_enc_cancargs(struct rpc_rqst *req,
* struct nlm_lock alock;
* };
*/
-static void nlm_xdr_enc_unlockargs(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nlm_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_nlm_lock(xdr, lock);
+ return 0;
}

/*
@@ -453,13 +457,14 @@ static void nlm_xdr_enc_unlockargs(struct rpc_rqst *req,
* nlm_stat stat;
* };
*/
-static void nlm_xdr_enc_res(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nlm_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_nlm_stat(xdr, result->status);
+ return 0;
}

/*
@@ -482,14 +487,15 @@ static void encode_nlm_testrply(struct xdr_stream *xdr,
encode_nlm_holder(xdr, result);
}

-static void nlm_xdr_enc_testres(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nlm_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_nlm_stat(xdr, result->status);
encode_nlm_testrply(xdr, result);
+ return 0;
}


diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index 395c39f..2308983 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -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,
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
index c02962a..cd1cc75 100644
--- a/fs/nfs/mount_clnt.c
+++ b/fs/nfs/mount_clnt.c
@@ -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;
}

/*
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
index 91a988a..2739a87 100644
--- a/fs/nfs/nfs2xdr.c
+++ b/fs/nfs/nfs2xdr.c
@@ -566,12 +566,13 @@ out_default:
* "NFS: Network File System Protocol Specification".
*/

-static void nfs2_xdr_enc_fhandle(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs2_xdr_enc_fhandle(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_fh *fh = obj;
encode_fhandle(xdr, fh);
+ return 0;
}

/*
@@ -582,31 +583,34 @@ static void nfs2_xdr_enc_fhandle(struct rpc_rqst *req,
* sattr attributes;
* };
*/
-static void nfs2_xdr_enc_sattrargs(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs2_xdr_enc_sattrargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_sattrargs *args = obj;
encode_fhandle(xdr, args->fh);
encode_sattr(xdr, args->sattr);
+ return 0;
}

-static void nfs2_xdr_enc_diropargs(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs2_xdr_enc_diropargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_diropargs *args = obj;
encode_diropargs(xdr, args->fh, args->name, args->len);
+ return 0;
}

-static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_readlinkargs *args = obj;
encode_fhandle(xdr, args->fh);
prepare_reply_buffer(req, args->pages, args->pgbase,
args->pglen, NFS_readlinkres_sz);
+ return 0;
}

/*
@@ -634,15 +638,16 @@ static void encode_readargs(struct xdr_stream *xdr,
*p = cpu_to_be32(count);
}

-static void nfs2_xdr_enc_readargs(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs2_xdr_enc_readargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_pgio_args *args = obj;
encode_readargs(xdr, args);
prepare_reply_buffer(req, args->pages, args->pgbase,
args->count, NFS_readres_sz);
req->rq_rcv_buf.flags |= XDRBUF_READ;
+ return 0;
}

/*
@@ -675,13 +680,14 @@ static void encode_writeargs(struct xdr_stream *xdr,
xdr_write_pages(xdr, args->pages, args->pgbase, count);
}

-static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs2_xdr_enc_writeargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_pgio_args *args = obj;
encode_writeargs(xdr, args);
xdr->buf->flags |= XDRBUF_WRITE;
+ return 0;
}

/*
@@ -692,21 +698,23 @@ static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req,
* sattr attributes;
* };
*/
-static void nfs2_xdr_enc_createargs(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs2_xdr_enc_createargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_createargs *args = obj;
encode_diropargs(xdr, args->fh, args->name, args->len);
encode_sattr(xdr, args->sattr);
+ return 0;
}

-static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs2_xdr_enc_removeargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_removeargs *args = obj;
encode_diropargs(xdr, args->fh, args->name.name, args->name.len);
+ return 0;
}

/*
@@ -717,9 +725,9 @@ static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req,
* diropargs to;
* };
*/
-static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs2_xdr_enc_renameargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_renameargs *args = obj;
const struct qstr *old = args->old_name;
@@ -727,6 +735,7 @@ static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req,

encode_diropargs(xdr, args->old_dir, old->name, old->len);
encode_diropargs(xdr, args->new_dir, new->name, new->len);
+ return 0;
}

/*
@@ -737,13 +746,14 @@ static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req,
* diropargs to;
* };
*/
-static void nfs2_xdr_enc_linkargs(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs2_xdr_enc_linkargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_linkargs *args = obj;
encode_fhandle(xdr, args->fromfh);
encode_diropargs(xdr, args->tofh, args->toname, args->tolen);
+ return 0;
}

/*
@@ -755,14 +765,15 @@ static void nfs2_xdr_enc_linkargs(struct rpc_rqst *req,
* sattr attributes;
* };
*/
-static void nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_symlinkargs *args = obj;
encode_diropargs(xdr, args->fromfh, args->fromname, args->fromlen);
encode_path(xdr, args->pages, args->pathlen);
encode_sattr(xdr, args->sattr);
+ return 0;
}

/*
@@ -786,14 +797,15 @@ static void encode_readdirargs(struct xdr_stream *xdr,
*p = cpu_to_be32(args->count);
}

-static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs2_xdr_enc_readdirargs(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_readdirargs *args = obj;
encode_readdirargs(xdr, args);
prepare_reply_buffer(req, args->pages, 0,
args->count, NFS_readdirres_sz);
+ return 0;
}

/*
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index d8f7744..8b880f7 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -844,12 +844,13 @@ static void encode_diropargs3(struct xdr_stream *xdr, const struct nfs_fh *fh,
* nfs_fh3 object;
* };
*/
-static void nfs3_xdr_enc_getattr3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_getattr3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_fh *fh = obj;
encode_nfs_fh3(xdr, fh);
+ return 0;
}

/*
@@ -883,14 +884,15 @@ static void encode_sattrguard3(struct xdr_stream *xdr,
}
}

-static void nfs3_xdr_enc_setattr3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_setattr3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs3_sattrargs *args = obj;
encode_nfs_fh3(xdr, args->fh);
encode_sattr3(xdr, args->sattr);
encode_sattrguard3(xdr, args);
+ return 0;
}

/*
@@ -900,12 +902,13 @@ static void nfs3_xdr_enc_setattr3args(struct rpc_rqst *req,
* diropargs3 what;
* };
*/
-static void nfs3_xdr_enc_lookup3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_lookup3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs3_diropargs *args = obj;
encode_diropargs3(xdr, args->fh, args->name, args->len);
+ return 0;
}

/*
@@ -923,12 +926,13 @@ static void encode_access3args(struct xdr_stream *xdr,
encode_uint32(xdr, args->access);
}

-static void nfs3_xdr_enc_access3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_access3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs3_accessargs *args = obj;
encode_access3args(xdr, args);
+ return 0;
}

/*
@@ -938,14 +942,15 @@ static void nfs3_xdr_enc_access3args(struct rpc_rqst *req,
* nfs_fh3 symlink;
* };
*/
-static void nfs3_xdr_enc_readlink3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_readlink3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs3_readlinkargs *args = obj;
encode_nfs_fh3(xdr, args->fh);
prepare_reply_buffer(req, args->pages, args->pgbase,
args->pglen, NFS3_readlinkres_sz);
+ return 0;
}

/*
@@ -969,15 +974,16 @@ static void encode_read3args(struct xdr_stream *xdr,
*p = cpu_to_be32(args->count);
}

-static void nfs3_xdr_enc_read3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_read3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_pgio_args *args = obj;
encode_read3args(xdr, args);
prepare_reply_buffer(req, args->pages, args->pgbase,
args->count, NFS3_readres_sz);
req->rq_rcv_buf.flags |= XDRBUF_READ;
+ return 0;
}

/*
@@ -1012,13 +1018,14 @@ static void encode_write3args(struct xdr_stream *xdr,
xdr_write_pages(xdr, args->pages, args->pgbase, args->count);
}

-static void nfs3_xdr_enc_write3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_write3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_pgio_args *args = obj;
encode_write3args(xdr, args);
xdr->buf->flags |= XDRBUF_WRITE;
+ return 0;
}

/*
@@ -1060,13 +1067,14 @@ static void encode_createhow3(struct xdr_stream *xdr,
}
}

-static void nfs3_xdr_enc_create3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_create3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs3_createargs *args = obj;
encode_diropargs3(xdr, args->fh, args->name, args->len);
encode_createhow3(xdr, args);
+ return 0;
}

/*
@@ -1077,13 +1085,14 @@ static void nfs3_xdr_enc_create3args(struct rpc_rqst *req,
* sattr3 attributes;
* };
*/
-static void nfs3_xdr_enc_mkdir3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_mkdir3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs3_mkdirargs *args = obj;
encode_diropargs3(xdr, args->fh, args->name, args->len);
encode_sattr3(xdr, args->sattr);
+ return 0;
}

/*
@@ -1106,13 +1115,14 @@ static void encode_symlinkdata3(struct xdr_stream *xdr,
encode_nfspath3(xdr, args->pages, args->pathlen);
}

-static void nfs3_xdr_enc_symlink3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_symlink3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs3_symlinkargs *args = obj;
encode_diropargs3(xdr, args->fromfh, args->fromname, args->fromlen);
encode_symlinkdata3(xdr, args);
+ return 0;
}

/*
@@ -1167,13 +1177,14 @@ static void encode_mknoddata3(struct xdr_stream *xdr,
}
}

-static void nfs3_xdr_enc_mknod3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_mknod3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs3_mknodargs *args = obj;
encode_diropargs3(xdr, args->fh, args->name, args->len);
encode_mknoddata3(xdr, args);
+ return 0;
}

/*
@@ -1183,12 +1194,13 @@ static void nfs3_xdr_enc_mknod3args(struct rpc_rqst *req,
* diropargs3 object;
* };
*/
-static void nfs3_xdr_enc_remove3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_remove3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_removeargs *args = obj;
encode_diropargs3(xdr, args->fh, args->name.name, args->name.len);
+ return 0;
}

/*
@@ -1199,9 +1211,9 @@ static void nfs3_xdr_enc_remove3args(struct rpc_rqst *req,
* diropargs3 to;
* };
*/
-static void nfs3_xdr_enc_rename3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_rename3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_renameargs *args = obj;
const struct qstr *old = args->old_name;
@@ -1209,6 +1221,7 @@ static void nfs3_xdr_enc_rename3args(struct rpc_rqst *req,

encode_diropargs3(xdr, args->old_dir, old->name, old->len);
encode_diropargs3(xdr, args->new_dir, new->name, new->len);
+ return 0;
}

/*
@@ -1219,13 +1232,14 @@ static void nfs3_xdr_enc_rename3args(struct rpc_rqst *req,
* diropargs3 link;
* };
*/
-static void nfs3_xdr_enc_link3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_link3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs3_linkargs *args = obj;
encode_nfs_fh3(xdr, args->fromfh);
encode_diropargs3(xdr, args->tofh, args->toname, args->tolen);
+ return 0;
}

/*
@@ -1251,14 +1265,15 @@ static void encode_readdir3args(struct xdr_stream *xdr,
*p = cpu_to_be32(args->count);
}

-static void nfs3_xdr_enc_readdir3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_readdir3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs3_readdirargs *args = obj;
encode_readdir3args(xdr, args);
prepare_reply_buffer(req, args->pages, 0,
args->count, NFS3_readdirres_sz);
+ return 0;
}

/*
@@ -1292,14 +1307,15 @@ static void encode_readdirplus3args(struct xdr_stream *xdr,
*p = cpu_to_be32(args->count);
}

-static void nfs3_xdr_enc_readdirplus3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_readdirplus3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs3_readdirargs *args = obj;
encode_readdirplus3args(xdr, args);
prepare_reply_buffer(req, args->pages, 0,
args->count, NFS3_readdirres_sz);
+ return 0;
}

/*
@@ -1323,19 +1339,20 @@ static void encode_commit3args(struct xdr_stream *xdr,
*p = cpu_to_be32(args->count);
}

-static void nfs3_xdr_enc_commit3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_commit3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_commitargs *args = obj;
encode_commit3args(xdr, args);
+ return 0;
}

#ifdef CONFIG_NFS_V3_ACL

-static void nfs3_xdr_enc_getacl3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_getacl3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs3_getaclargs *args = obj;
encode_nfs_fh3(xdr, args->fh);
@@ -1344,11 +1361,12 @@ static void nfs3_xdr_enc_getacl3args(struct rpc_rqst *req,
prepare_reply_buffer(req, args->pages, 0,
NFSACL_MAXPAGES << PAGE_SHIFT,
ACL3_getaclres_sz);
+ return 0;
}

-static void nfs3_xdr_enc_setacl3args(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs3_xdr_enc_setacl3args(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs3_setaclargs *args = obj;
unsigned int base;
@@ -1373,6 +1391,7 @@ static void nfs3_xdr_enc_setacl3args(struct rpc_rqst *req,
args->acl_default : NULL, 1,
NFS_ACL_DEFAULT);
BUG_ON(error < 0);
+ return 0;
}

#endif /* CONFIG_NFS_V3_ACL */
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 245f20e..dd7a800 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -2059,8 +2059,8 @@ static u32 nfs4_xdr_minorversion(const struct nfs4_sequence_args *args)
/*
* Encode an ACCESS request
*/
-static void nfs4_xdr_enc_access(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_access(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs4_accessargs *args = obj;
struct compound_hdr hdr = {
@@ -2073,13 +2073,14 @@ static void nfs4_xdr_enc_access(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_access(xdr, args->access, &hdr);
encode_getfattr(xdr, args->bitmask, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode LOOKUP request
*/
-static void nfs4_xdr_enc_lookup(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_lookup(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs4_lookup_arg *args = obj;
struct compound_hdr hdr = {
@@ -2093,14 +2094,15 @@ static void nfs4_xdr_enc_lookup(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_getfh(xdr, &hdr);
encode_getfattr(xdr, args->bitmask, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode LOOKUP_ROOT request
*/
-static void nfs4_xdr_enc_lookup_root(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_lookup_root(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs4_lookup_root_arg *args = obj;
struct compound_hdr hdr = {
@@ -2113,13 +2115,14 @@ static void nfs4_xdr_enc_lookup_root(struct rpc_rqst *req,
encode_getfh(xdr, &hdr);
encode_getfattr(xdr, args->bitmask, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode REMOVE request
*/
-static void nfs4_xdr_enc_remove(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_remove(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_removeargs *args = obj;
struct compound_hdr hdr = {
@@ -2131,13 +2134,14 @@ static void nfs4_xdr_enc_remove(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_putfh(xdr, args->fh, &hdr);
encode_remove(xdr, &args->name, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode RENAME request
*/
-static void nfs4_xdr_enc_rename(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_rename(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs_renameargs *args = obj;
struct compound_hdr hdr = {
@@ -2151,12 +2155,13 @@ static void nfs4_xdr_enc_rename(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_putfh(xdr, args->new_dir, &hdr);
encode_rename(xdr, args->old_name, args->new_name, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode LINK request
*/
-static void nfs4_xdr_enc_link(struct rpc_rqst *req, struct xdr_stream *xdr,
+static int nfs4_xdr_enc_link(struct rpc_rqst *req, struct xdr_stream *xdr,
void *obj)
{
const struct nfs4_link_arg *args = obj;
@@ -2173,13 +2178,14 @@ static void nfs4_xdr_enc_link(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_restorefh(xdr, &hdr);
encode_getfattr(xdr, args->bitmask, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode CREATE request
*/
-static void nfs4_xdr_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs4_create_arg *args = obj;
struct compound_hdr hdr = {
@@ -2193,22 +2199,23 @@ static void nfs4_xdr_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_getfh(xdr, &hdr);
encode_getfattr(xdr, args->bitmask, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode SYMLINK request
*/
-static void nfs4_xdr_enc_symlink(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_symlink(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
- nfs4_xdr_enc_create(req, xdr, obj);
+ return nfs4_xdr_enc_create(req, xdr, obj);
}

/*
* Encode GETATTR request
*/
-static void nfs4_xdr_enc_getattr(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_getattr(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs4_getattr_arg *args = obj;
struct compound_hdr hdr = {
@@ -2220,13 +2227,14 @@ static void nfs4_xdr_enc_getattr(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_putfh(xdr, args->fh, &hdr);
encode_getfattr(xdr, args->bitmask, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode a CLOSE request
*/
-static void nfs4_xdr_enc_close(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_close(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
struct nfs_closeargs *args = obj;
struct compound_hdr hdr = {
@@ -2239,13 +2247,14 @@ static void nfs4_xdr_enc_close(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_close(xdr, args, &hdr);
encode_getfattr(xdr, args->bitmask, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode an OPEN request
*/
-static void nfs4_xdr_enc_open(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_open(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
struct nfs_openargs *args = obj;
struct compound_hdr hdr = {
@@ -2261,14 +2270,15 @@ static void nfs4_xdr_enc_open(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_access(xdr, args->access, &hdr);
encode_getfattr_open(xdr, args->bitmask, args->open_bitmap, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode an OPEN_CONFIRM request
*/
-static void nfs4_xdr_enc_open_confirm(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_open_confirm(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs_open_confirmargs *args = obj;
struct compound_hdr hdr = {
@@ -2279,14 +2289,15 @@ static void nfs4_xdr_enc_open_confirm(struct rpc_rqst *req,
encode_putfh(xdr, args->fh, &hdr);
encode_open_confirm(xdr, args, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode an OPEN request with no attributes.
*/
-static void nfs4_xdr_enc_open_noattr(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_open_noattr(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs_openargs *args = obj;
struct compound_hdr hdr = {
@@ -2301,14 +2312,15 @@ static void nfs4_xdr_enc_open_noattr(struct rpc_rqst *req,
encode_access(xdr, args->access, &hdr);
encode_getfattr_open(xdr, args->bitmask, args->open_bitmap, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode an OPEN_DOWNGRADE request
*/
-static void nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs_closeargs *args = obj;
struct compound_hdr hdr = {
@@ -2321,13 +2333,14 @@ static void nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req,
encode_open_downgrade(xdr, args, &hdr);
encode_getfattr(xdr, args->bitmask, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode a LOCK request
*/
-static void nfs4_xdr_enc_lock(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_lock(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
struct nfs_lock_args *args = obj;
struct compound_hdr hdr = {
@@ -2339,13 +2352,14 @@ static void nfs4_xdr_enc_lock(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_putfh(xdr, args->fh, &hdr);
encode_lock(xdr, args, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode a LOCKT request
*/
-static void nfs4_xdr_enc_lockt(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_lockt(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
struct nfs_lockt_args *args = obj;
struct compound_hdr hdr = {
@@ -2357,13 +2371,14 @@ static void nfs4_xdr_enc_lockt(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_putfh(xdr, args->fh, &hdr);
encode_lockt(xdr, args, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode a LOCKU request
*/
-static void nfs4_xdr_enc_locku(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_locku(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
struct nfs_locku_args *args = obj;
struct compound_hdr hdr = {
@@ -2375,11 +2390,12 @@ static void nfs4_xdr_enc_locku(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_putfh(xdr, args->fh, &hdr);
encode_locku(xdr, args, &hdr);
encode_nops(&hdr);
+ return 0;
}

-static void nfs4_xdr_enc_release_lockowner(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_release_lockowner(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs_release_lockowner_args *args = obj;
struct compound_hdr hdr = {
@@ -2389,13 +2405,14 @@ static void nfs4_xdr_enc_release_lockowner(struct rpc_rqst *req,
encode_compound_hdr(xdr, req, &hdr);
encode_release_lockowner(xdr, &args->lock_owner, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode a READLINK request
*/
-static void nfs4_xdr_enc_readlink(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs4_readlink *args = obj;
struct compound_hdr hdr = {
@@ -2410,13 +2427,14 @@ static void nfs4_xdr_enc_readlink(struct rpc_rqst *req, struct xdr_stream *xdr,
xdr_inline_pages(&req->rq_rcv_buf, hdr.replen << 2, args->pages,
args->pgbase, args->pglen);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode a READDIR request
*/
-static void nfs4_xdr_enc_readdir(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs4_readdir_arg *args = obj;
struct compound_hdr hdr = {
@@ -2434,13 +2452,14 @@ static void nfs4_xdr_enc_readdir(struct rpc_rqst *req, struct xdr_stream *xdr,
__func__, hdr.replen << 2, args->pages,
args->pgbase, args->count);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode a READ request
*/
-static void nfs4_xdr_enc_read(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_read(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
struct nfs_pgio_args *args = obj;
struct compound_hdr hdr = {
@@ -2456,13 +2475,14 @@ static void nfs4_xdr_enc_read(struct rpc_rqst *req, struct xdr_stream *xdr,
args->pages, args->pgbase, args->count);
req->rq_rcv_buf.flags |= XDRBUF_READ;
encode_nops(&hdr);
+ return 0;
}

/*
* Encode an SETATTR request
*/
-static void nfs4_xdr_enc_setattr(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_setattr(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
struct nfs_setattrargs *args = obj;
struct compound_hdr hdr = {
@@ -2475,13 +2495,14 @@ static void nfs4_xdr_enc_setattr(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_setattr(xdr, args, args->server, &hdr);
encode_getfattr(xdr, args->bitmask, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode a GETACL request
*/
-static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
struct nfs_getaclargs *args = obj;
struct compound_hdr hdr = {
@@ -2499,13 +2520,14 @@ static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,
args->acl_pages, args->acl_pgbase, args->acl_len);

encode_nops(&hdr);
+ return 0;
}

/*
* Encode a WRITE request
*/
-static void nfs4_xdr_enc_write(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_write(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
struct nfs_pgio_args *args = obj;
struct compound_hdr hdr = {
@@ -2520,13 +2542,14 @@ static void nfs4_xdr_enc_write(struct rpc_rqst *req, struct xdr_stream *xdr,
if (args->bitmask)
encode_getfattr(xdr, args->bitmask, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* a COMMIT request
*/
-static void nfs4_xdr_enc_commit(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_commit(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
struct nfs_commitargs *args = obj;
struct compound_hdr hdr = {
@@ -2538,13 +2561,14 @@ static void nfs4_xdr_enc_commit(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_putfh(xdr, args->fh, &hdr);
encode_commit(xdr, args, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* FSINFO request
*/
-static void nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
struct nfs4_fsinfo_arg *args = obj;
struct compound_hdr hdr = {
@@ -2556,13 +2580,14 @@ static void nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_putfh(xdr, args->fh, &hdr);
encode_fsinfo(xdr, args->bitmask, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* a PATHCONF request
*/
-static void nfs4_xdr_enc_pathconf(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_pathconf(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs4_pathconf_arg *args = obj;
struct compound_hdr hdr = {
@@ -2575,13 +2600,14 @@ static void nfs4_xdr_enc_pathconf(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_getattr_one(xdr, args->bitmask[0] & nfs4_pathconf_bitmap[0],
&hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* a STATFS request
*/
-static void nfs4_xdr_enc_statfs(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs4_statfs_arg *args = obj;
struct compound_hdr hdr = {
@@ -2594,14 +2620,15 @@ static void nfs4_xdr_enc_statfs(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_getattr_two(xdr, args->bitmask[0] & nfs4_statfs_bitmap[0],
args->bitmask[1] & nfs4_statfs_bitmap[1], &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* GETATTR_BITMAP request
*/
-static void nfs4_xdr_enc_server_caps(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs4_server_caps_arg *args = obj;
struct compound_hdr hdr = {
@@ -2617,13 +2644,14 @@ static void nfs4_xdr_enc_server_caps(struct rpc_rqst *req,
FATTR4_WORD0_SYMLINK_SUPPORT|
FATTR4_WORD0_ACLSUPPORT, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* a RENEW request
*/
-static void nfs4_xdr_enc_renew(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_renew(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
struct nfs_client *clp = obj;
struct compound_hdr hdr = {
@@ -2633,14 +2661,15 @@ static void nfs4_xdr_enc_renew(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_compound_hdr(xdr, req, &hdr);
encode_renew(xdr, clp->cl_clientid, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* a SETCLIENTID request
*/
-static void nfs4_xdr_enc_setclientid(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_setclientid(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs4_setclientid *sc = obj;
struct compound_hdr hdr = {
@@ -2650,14 +2679,15 @@ static void nfs4_xdr_enc_setclientid(struct rpc_rqst *req,
encode_compound_hdr(xdr, req, &hdr);
encode_setclientid(xdr, sc, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* a SETCLIENTID_CONFIRM request
*/
-static void nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs4_setclientid_res *arg = obj;
struct compound_hdr hdr = {
@@ -2667,14 +2697,15 @@ static void nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req,
encode_compound_hdr(xdr, req, &hdr);
encode_setclientid_confirm(xdr, arg, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* DELEGRETURN request
*/
-static void nfs4_xdr_enc_delegreturn(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_delegreturn(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
const struct nfs4_delegreturnargs *args = obj;
struct compound_hdr hdr = {
@@ -2687,14 +2718,15 @@ static void nfs4_xdr_enc_delegreturn(struct rpc_rqst *req,
encode_getfattr(xdr, args->bitmask, &hdr);
encode_delegreturn(xdr, args->stateid, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode FS_LOCATIONS request
*/
-static void nfs4_xdr_enc_fs_locations(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs4_fs_locations_arg *args = obj;
struct compound_hdr hdr = {
@@ -2721,12 +2753,13 @@ static void nfs4_xdr_enc_fs_locations(struct rpc_rqst *req,
xdr_inline_pages(&req->rq_rcv_buf, replen << 2, &args->page,
0, PAGE_SIZE);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode SECINFO request
*/
-static void nfs4_xdr_enc_secinfo(struct rpc_rqst *req,
+static int nfs4_xdr_enc_secinfo(struct rpc_rqst *req,
struct xdr_stream *xdr,
void *obj)
{
@@ -2740,14 +2773,15 @@ static void nfs4_xdr_enc_secinfo(struct rpc_rqst *req,
encode_putfh(xdr, args->dir_fh, &hdr);
encode_secinfo(xdr, args->name, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode FSID_PRESENT request
*/
-static void nfs4_xdr_enc_fsid_present(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_fsid_present(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs4_fsid_present_arg *args = obj;
struct compound_hdr hdr = {
@@ -2761,15 +2795,16 @@ static void nfs4_xdr_enc_fsid_present(struct rpc_rqst *req,
if (args->renew)
encode_renew(xdr, args->clientid, &hdr);
encode_nops(&hdr);
+ return 0;
}

#if defined(CONFIG_NFS_V4_1)
/*
* BIND_CONN_TO_SESSION request
*/
-static void nfs4_xdr_enc_bind_conn_to_session(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_bind_conn_to_session(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs41_bind_conn_to_session_args *args = obj;
struct compound_hdr hdr = {
@@ -2779,14 +2814,15 @@ static void nfs4_xdr_enc_bind_conn_to_session(struct rpc_rqst *req,
encode_compound_hdr(xdr, req, &hdr);
encode_bind_conn_to_session(xdr, args, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* EXCHANGE_ID request
*/
-static void nfs4_xdr_enc_exchange_id(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_exchange_id(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs41_exchange_id_args *args = obj;
struct compound_hdr hdr = {
@@ -2796,14 +2832,15 @@ static void nfs4_xdr_enc_exchange_id(struct rpc_rqst *req,
encode_compound_hdr(xdr, req, &hdr);
encode_exchange_id(xdr, args, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* a CREATE_SESSION request
*/
-static void nfs4_xdr_enc_create_session(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_create_session(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs41_create_session_args *args = obj;
struct compound_hdr hdr = {
@@ -2813,14 +2850,15 @@ static void nfs4_xdr_enc_create_session(struct rpc_rqst *req,
encode_compound_hdr(xdr, req, &hdr);
encode_create_session(xdr, args, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* a DESTROY_SESSION request
*/
-static void nfs4_xdr_enc_destroy_session(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_destroy_session(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs4_session *session = obj;
struct compound_hdr hdr = {
@@ -2830,12 +2868,13 @@ static void nfs4_xdr_enc_destroy_session(struct rpc_rqst *req,
encode_compound_hdr(xdr, req, &hdr);
encode_destroy_session(xdr, session, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* a DESTROY_CLIENTID request
*/
-static void nfs4_xdr_enc_destroy_clientid(struct rpc_rqst *req,
+static int nfs4_xdr_enc_destroy_clientid(struct rpc_rqst *req,
struct xdr_stream *xdr,
void *obj)
{
@@ -2847,13 +2886,14 @@ static void nfs4_xdr_enc_destroy_clientid(struct rpc_rqst *req,
encode_compound_hdr(xdr, req, &hdr);
encode_destroy_clientid(xdr, clp->cl_clientid, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* a SEQUENCE request
*/
-static void nfs4_xdr_enc_sequence(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_sequence(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
struct nfs4_sequence_args *args = obj;
struct compound_hdr hdr = {
@@ -2863,14 +2903,15 @@ static void nfs4_xdr_enc_sequence(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, args, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* a GET_LEASE_TIME request
*/
-static void nfs4_xdr_enc_get_lease_time(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_get_lease_time(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs4_get_lease_time_args *args = obj;
struct compound_hdr hdr = {
@@ -2883,14 +2924,15 @@ static void nfs4_xdr_enc_get_lease_time(struct rpc_rqst *req,
encode_putrootfh(xdr, &hdr);
encode_fsinfo(xdr, lease_bitmap, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* a RECLAIM_COMPLETE request
*/
-static void nfs4_xdr_enc_reclaim_complete(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_reclaim_complete(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs41_reclaim_complete_args *args = obj;
struct compound_hdr hdr = {
@@ -2901,14 +2943,15 @@ static void nfs4_xdr_enc_reclaim_complete(struct rpc_rqst *req,
encode_sequence(xdr, &args->seq_args, &hdr);
encode_reclaim_complete(xdr, args, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode GETDEVICEINFO request
*/
-static void nfs4_xdr_enc_getdeviceinfo(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_getdeviceinfo(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs4_getdeviceinfo_args *args = obj;
struct compound_hdr hdr = {
@@ -2926,14 +2969,15 @@ static void nfs4_xdr_enc_getdeviceinfo(struct rpc_rqst *req,
args->pdev->pglen);

encode_nops(&hdr);
+ return 0;
}

/*
* Encode LAYOUTGET request
*/
-static void nfs4_xdr_enc_layoutget(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_layoutget(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs4_layoutget_args *args = obj;
struct compound_hdr hdr = {
@@ -2949,14 +2993,15 @@ static void nfs4_xdr_enc_layoutget(struct rpc_rqst *req,
args->layout.pages, 0, args->layout.pglen);

encode_nops(&hdr);
+ return 0;
}

/*
* Encode LAYOUTCOMMIT request
*/
-static void nfs4_xdr_enc_layoutcommit(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_layoutcommit(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs4_layoutcommit_args *args = obj;
struct nfs4_layoutcommit_data *data =
@@ -2971,14 +3016,15 @@ static void nfs4_xdr_enc_layoutcommit(struct rpc_rqst *req,
encode_layoutcommit(xdr, data->args.inode, args, &hdr);
encode_getfattr(xdr, args->bitmask, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode LAYOUTRETURN request
*/
-static void nfs4_xdr_enc_layoutreturn(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_layoutreturn(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs4_layoutreturn_args *args = obj;
struct compound_hdr hdr = {
@@ -2990,6 +3036,7 @@ static void nfs4_xdr_enc_layoutreturn(struct rpc_rqst *req,
encode_putfh(xdr, NFS_FH(args->inode), &hdr);
encode_layoutreturn(xdr, args, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
@@ -3015,9 +3062,9 @@ static int nfs4_xdr_enc_secinfo_no_name(struct rpc_rqst *req,
/*
* Encode TEST_STATEID request
*/
-static void nfs4_xdr_enc_test_stateid(struct rpc_rqst *req,
- struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_test_stateid(struct rpc_rqst *req,
+ struct xdr_stream *xdr,
+ void *obj)
{
struct nfs41_test_stateid_args *args = obj;
struct compound_hdr hdr = {
@@ -3028,12 +3075,13 @@ static void nfs4_xdr_enc_test_stateid(struct rpc_rqst *req,
encode_sequence(xdr, &args->seq_args, &hdr);
encode_test_stateid(xdr, args, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
* Encode FREE_STATEID request
*/
-static void nfs4_xdr_enc_free_stateid(struct rpc_rqst *req,
+static int nfs4_xdr_enc_free_stateid(struct rpc_rqst *req,
struct xdr_stream *xdr,
void *obj)
{
@@ -3046,6 +3094,7 @@ static void nfs4_xdr_enc_free_stateid(struct rpc_rqst *req,
encode_sequence(xdr, &args->seq_args, &hdr);
encode_free_stateid(xdr, args, &hdr);
encode_nops(&hdr);
+ return 0;
}
#endif /* CONFIG_NFS_V4_1 */

@@ -6246,8 +6295,8 @@ out:
/*
* Encode an SETACL request
*/
-static void nfs4_xdr_enc_setacl(struct rpc_rqst *req, struct xdr_stream *xdr,
- void *obj)
+static int nfs4_xdr_enc_setacl(struct rpc_rqst *req, struct xdr_stream *xdr,
+ void *obj)
{
struct nfs_setaclargs *args = obj;
struct compound_hdr hdr = {
@@ -6259,6 +6308,7 @@ static void nfs4_xdr_enc_setacl(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_putfh(xdr, args->fh, &hdr);
encode_setacl(xdr, args, &hdr);
encode_nops(&hdr);
+ return 0;
}

/*
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index a7167b8..f658f6d 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -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,
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index 7177815..d68f71e 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -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);
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c
index 47f38be..11f78ac2 100644
--- a/net/sunrpc/auth.c
+++ b/net/sunrpc/auth.c
@@ -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
diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c
index 6b9dc37..26601cd 100644
--- a/net/sunrpc/auth_gss/gss_rpc_xdr.c
+++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c
@@ -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,
diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.h b/net/sunrpc/auth_gss/gss_rpc_xdr.h
index eb98597..425085c 100644
--- a/net/sunrpc/auth_gss/gss_rpc_xdr.h
+++ b/net/sunrpc/auth_gss/gss_rpc_xdr.h
@@ -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 *);
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 47a9a11..7d864eb 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -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)
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
index 7c71283..1d31943 100644
--- a/net/sunrpc/rpcb_clnt.c
+++ b/net/sunrpc/rpcb_clnt.c
@@ -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,
--
2.1.0


2015-04-24 11:05:43

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 36/45] NFSv4: Fix GETATTR bitmap verification

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.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/nfs/nfs4xdr.c | 186 +++++++++++++++++++------------------------------------
1 file changed, 63 insertions(+), 123 deletions(-)

diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 5c399ec..36b5e95 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -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
--
2.1.0


2015-04-24 11:05:33

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 30/45] richacl: Create richacl from mode values

Create a richacl that corresponds to given file mode permission bits.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/richacl_compat.c | 38 ++++++++++++++++++++++++++++++++++++++
include/linux/richacl.h | 1 +
2 files changed, 39 insertions(+)

diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
index 49af600..8ebe772 100644
--- a/fs/richacl_compat.c
+++ b/fs/richacl_compat.c
@@ -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);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 6a97dca..25ff4df 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -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 */
--
2.1.0


2015-04-24 11:05:40

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 35/45] nfsd: Add richacl support

On file systems with richacls enabled, get and set richacls directly instead of
converting from / to posix acls.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
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(-)

diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h
index 1c5deb5..d73c664 100644
--- a/fs/nfsd/acl.h
+++ b/fs/nfsd/acl.h
@@ -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);

diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c
index 9e65a19..b753d76 100644
--- a/fs/nfsd/nfs4acl.c
+++ b/fs/nfsd/nfs4acl.c
@@ -38,6 +38,8 @@
#include <linux/nfs_fs.h>
#include <linux/richacl_compat.h>
#include <linux/nfs4acl.h>
+#include <linux/xattr.h>
+#include <linux/richacl_xattr.h>
#include "nfsfh.h"
#include "nfsd.h"
#include "idmap.h"
@@ -127,31 +129,28 @@ static short ace2type(struct richace *);
static void _posix_to_richacl_one(struct posix_acl *, struct richacl_alloc *,
unsigned int);

-int
-nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct richacl **acl)
+static struct richacl *
+nfsd4_get_posix_acl(struct svc_rqst *rqstp, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
- int error = 0;
struct posix_acl *pacl = NULL, *dpacl = NULL;
struct richacl_alloc x;
unsigned int flags = 0;
int count;

pacl = get_acl(inode, ACL_TYPE_ACCESS);
- if (!pacl)
- pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
-
- if (IS_ERR(pacl))
- return PTR_ERR(pacl);
+ if (IS_ERR_OR_NULL(pacl))
+ return (void *)pacl;

- /* allocate for worst case: one (deny, allow) pair each: */
+ /* Allocate for worst case: one (deny, allow) pair each. The resulting
+ acl will be released shortly and won't be cached. */
count = 2 * pacl->a_count;

if (S_ISDIR(inode->i_mode)) {
flags = FLAG_DIRECTORY;
dpacl = get_acl(inode, ACL_TYPE_DEFAULT);
if (IS_ERR(dpacl)) {
- error = PTR_ERR(dpacl);
+ x.acl = (void *)dpacl;
goto rel_pacl;
}

@@ -160,7 +159,7 @@ nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct richacl **ac
}

if (!richacl_prepare(&x, count)) {
- error = -ENOMEM;
+ x.acl = ERR_PTR(-ENOMEM);
goto out;
}

@@ -169,13 +168,37 @@ nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct richacl **ac
if (dpacl)
_posix_to_richacl_one(dpacl, &x, flags | FLAG_DEFAULT_ACL);

- *acl = x.acl;
-
out:
posix_acl_release(dpacl);
rel_pacl:
posix_acl_release(pacl);
- return error;
+ return x.acl;
+}
+
+struct richacl *
+nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ struct richacl *acl;
+ int error;
+
+ if (IS_RICHACL(inode))
+ acl = get_richacl(inode);
+ else
+ acl = nfsd4_get_posix_acl(rqstp, dentry);
+ if (IS_ERR(acl))
+ return acl;
+ else if (acl == NULL) {
+ acl = richacl_from_mode_unmasked(inode->i_mode);
+ if (acl == NULL)
+ acl = ERR_PTR(-ENOMEM);
+ }
+ error = richacl_apply_masks(&acl);
+ if (error) {
+ richacl_put(acl);
+ acl = ERR_PTR(error);
+ }
+ return acl;
}

struct posix_acl_summary {
@@ -788,56 +811,84 @@ out_estate:
return ret;
}

-__be32
-nfsd4_set_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, struct richacl *acl)
+static int
+nfsd4_set_posix_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct richacl *acl)
{
- __be32 error;
int host_error;
- struct dentry *dentry;
- struct inode *inode;
+ struct inode *inode = dentry->d_inode;
struct posix_acl *pacl = NULL, *dpacl = NULL;
unsigned int flags = 0;

- /* Get inode */
- error = fh_verify(rqstp, fhp, 0, NFSD_MAY_SATTR);
- if (error)
- return error;
-
- dentry = fhp->fh_dentry;
- inode = dentry->d_inode;
-
if (!inode->i_op->set_acl || !IS_POSIXACL(inode))
- return nfserr_attrnotsupp;
+ return -EOPNOTSUPP;

if (S_ISDIR(inode->i_mode))
flags = FLAG_DIRECTORY;

host_error = nfs4_richacl_to_posix(acl, &pacl, &dpacl, flags);
if (host_error == -EINVAL)
- return nfserr_attrnotsupp;
+ return -EOPNOTSUPP;
if (host_error < 0)
- goto out_nfserr;
+ return host_error;

host_error = inode->i_op->set_acl(inode, pacl, ACL_TYPE_ACCESS);
if (host_error < 0)
goto out_release;

- if (S_ISDIR(inode->i_mode)) {
- host_error = inode->i_op->set_acl(inode, dpacl,
- ACL_TYPE_DEFAULT);
- }
+ if (S_ISDIR(inode->i_mode))
+ host_error = inode->i_op->set_acl(inode, dpacl, ACL_TYPE_DEFAULT);

out_release:
posix_acl_release(pacl);
posix_acl_release(dpacl);
-out_nfserr:
+ return host_error;
+}
+
+static int
+nfsd4_set_richacl(struct svc_rqst *rqstp, struct dentry *dentry, struct richacl *acl)
+{
+ int host_error;
+ struct inode *inode = dentry->d_inode;
+ size_t size = richacl_xattr_size(acl);
+ char *buffer;
+
+ if (!inode->i_op->setxattr || !IS_RICHACL(inode))
+ return -EOPNOTSUPP;
+
+ richacl_compute_max_masks(acl);
+
+ buffer = kmalloc(size, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+ richacl_to_xattr(&init_user_ns, acl, buffer, size);
+ host_error = inode->i_op->setxattr(dentry, RICHACL_XATTR, buffer, size, 0);
+ kfree(buffer);
+ return host_error;
+}
+
+__be32
+nfsd4_set_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, struct richacl *acl)
+{
+ struct dentry *dentry;
+ int host_error;
+ __be32 error;
+
+ error = fh_verify(rqstp, fhp, 0, NFSD_MAY_SATTR);
+ if (error)
+ return error;
+ dentry = fhp->fh_dentry;
+
+ if (IS_RICHACL(dentry->d_inode))
+ host_error = nfsd4_set_richacl(rqstp, dentry, acl);
+ else
+ host_error = nfsd4_set_posix_acl(rqstp, dentry, acl);
+
if (host_error == -EOPNOTSUPP)
return nfserr_attrnotsupp;
else
return nfserrno(host_error);
}

-
static short
ace2type(struct richace *ace)
{
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 658da6e..2117e52 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -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;
}

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index f3c1880..c0e1b3c 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -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) {
--
2.1.0


2015-04-24 11:05:32

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 29/45] richacl: Apply the file masks to a richacl

Put all the pieces of the acl transformation puzzle together for
computing a richacl which has the file masks "applied" so that the
standard nfsv4 access check algorithm can be used on the richacl.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/richacl_compat.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/richacl.h | 3 ++
2 files changed, 106 insertions(+)

diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
index 645917f..49af600 100644
--- a/fs/richacl_compat.c
+++ b/fs/richacl_compat.c
@@ -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);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index bcc2b64..6a97dca 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -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 */
--
2.1.0


2015-04-24 11:05:38

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 33/45] nfsd: Keep list of acls to dispose of in compoundargs

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.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/nfsd/nfs4xdr.c | 26 ++++++++++++++++++++++++++
fs/nfsd/xdr4.h | 6 ++++++
2 files changed, 32 insertions(+)

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index e2d602d..f25d1e7 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -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;

diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 0bda93e..4ed4db0 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -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;

--
2.1.0


2015-04-24 11:05:39

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 34/45] nfsd: Use richacls as internal acl representation

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.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
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

diff --git a/fs/Kconfig b/fs/Kconfig
index 8b84f99..f459f75 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -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"
diff --git a/fs/nfs_common/Makefile b/fs/nfs_common/Makefile
index d153ca3..e055139 100644
--- a/fs/nfs_common/Makefile
+++ b/fs/nfs_common/Makefile
@@ -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
diff --git a/fs/nfs_common/nfs4acl.c b/fs/nfs_common/nfs4acl.c
new file mode 100644
index 0000000..6a1f6b8
--- /dev/null
+++ b/fs/nfs_common/nfs4acl.c
@@ -0,0 +1,41 @@
+#include <linux/fs.h>
+#include <linux/richacl.h>
+#include <linux/nfs4acl.h>
+
+static struct special_id {
+ char *who;
+ int len;
+} special_who_map[] = {
+ [RICHACE_OWNER_SPECIAL_ID] =
+ { .who = "OWNER@", .len = sizeof("OWNER@") - 1 },
+ [RICHACE_GROUP_SPECIAL_ID] =
+ { .who = "GROUP@", .len = sizeof("GROUP@") - 1 },
+ [RICHACE_EVERYONE_SPECIAL_ID] =
+ { .who = "EVERYONE@", .len = sizeof("EVERYONE@") - 1 }
+};
+
+int nfs4acl_who_to_special_id(const char *who, u32 len)
+{
+ int n;
+
+ for (n = 0; n < ARRAY_SIZE(special_who_map); n++) {
+ if (len == special_who_map[n].len &&
+ !memcmp(who, special_who_map[n].who, len))
+ return n;
+ }
+ return -1;
+}
+EXPORT_SYMBOL(nfs4acl_who_to_special_id);
+
+bool nfs4acl_special_id_to_who(unsigned int special_who,
+ const char **who, unsigned int *len)
+{
+ struct special_id *special = &special_who_map[special_who];
+
+ if (special_who > ARRAY_SIZE(special_who_map) || !special->len)
+ return false;
+ *who = special->who;
+ *len = special->len;
+ return true;
+}
+EXPORT_SYMBOL(nfs4acl_special_id_to_who);
diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig
index 683bf71..ff24643 100644
--- a/fs/nfsd/Kconfig
+++ b/fs/nfsd/Kconfig
@@ -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
diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h
index 4cd7c69..1c5deb5 100644
--- a/fs/nfsd/acl.h
+++ b/fs/nfsd/acl.h
@@ -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 */
diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c
index c630651..9e65a19 100644
--- a/fs/nfsd/nfs4acl.c
+++ b/fs/nfsd/nfs4acl.c
@@ -36,45 +36,49 @@

#include <linux/slab.h>
#include <linux/nfs_fs.h>
+#include <linux/richacl_compat.h>
+#include <linux/nfs4acl.h>
#include "nfsfh.h"
#include "nfsd.h"
+#include "idmap.h"
#include "acl.h"
#include "vfs.h"

-#define NFS4_ACL_TYPE_DEFAULT 0x01
-#define NFS4_ACL_DIR 0x02
-#define NFS4_ACL_OWNER 0x04
+#define FLAG_DEFAULT_ACL 0x01
+#define FLAG_DIRECTORY 0x02
+#define FLAG_OWNER 0x04

/* mode bit translations: */
-#define NFS4_READ_MODE (NFS4_ACE_READ_DATA)
-#define NFS4_WRITE_MODE (NFS4_ACE_WRITE_DATA | NFS4_ACE_APPEND_DATA)
-#define NFS4_EXECUTE_MODE NFS4_ACE_EXECUTE
-#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)
+#define RICHACE_READ_MODE (RICHACE_READ_DATA)
+#define RICHACE_WRITE_MODE (RICHACE_WRITE_DATA | RICHACE_APPEND_DATA)
+#define RICHACE_EXECUTE_MODE RICHACE_EXECUTE
+#define RICHACE_ANYONE_MODE (RICHACE_READ_ATTRIBUTES | RICHACE_READ_ACL | RICHACE_SYNCHRONIZE)
+#define RICHACE_OWNER_MODE (RICHACE_WRITE_ATTRIBUTES | RICHACE_WRITE_ACL)

/* flags used to simulate posix default ACLs */
-#define NFS4_INHERITANCE_FLAGS (NFS4_ACE_FILE_INHERIT_ACE \
- | NFS4_ACE_DIRECTORY_INHERIT_ACE)
+#define RICHACE_INHERITANCE_FLAGS (RICHACE_FILE_INHERIT_ACE \
+ | RICHACE_DIRECTORY_INHERIT_ACE)

-#define NFS4_SUPPORTED_FLAGS (NFS4_INHERITANCE_FLAGS \
- | NFS4_ACE_INHERIT_ONLY_ACE \
- | NFS4_ACE_IDENTIFIER_GROUP)
+#define RICHACE_SUPPORTED_FLAGS (RICHACE_INHERITANCE_FLAGS \
+ | RICHACE_INHERIT_ONLY_ACE \
+ | RICHACE_IDENTIFIER_GROUP \
+ | RICHACE_SPECIAL_WHO)

static u32
mask_from_posix(unsigned short perm, unsigned int flags)
{
- int mask = NFS4_ANYONE_MODE;
+ int mask = RICHACE_ANYONE_MODE;

- if (flags & NFS4_ACL_OWNER)
- mask |= NFS4_OWNER_MODE;
+ if (flags & FLAG_OWNER)
+ mask |= RICHACE_OWNER_MODE;
if (perm & ACL_READ)
- mask |= NFS4_READ_MODE;
+ mask |= RICHACE_READ_MODE;
if (perm & ACL_WRITE)
- mask |= NFS4_WRITE_MODE;
- if ((perm & ACL_WRITE) && (flags & NFS4_ACL_DIR))
- mask |= NFS4_ACE_DELETE_CHILD;
+ mask |= RICHACE_WRITE_MODE;
+ if ((perm & ACL_WRITE) && (flags & FLAG_DIRECTORY))
+ mask |= RICHACE_DELETE_CHILD;
if (perm & ACL_EXECUTE)
- mask |= NFS4_EXECUTE_MODE;
+ mask |= RICHACE_EXECUTE_MODE;
return mask;
}

@@ -84,13 +88,13 @@ deny_mask_from_posix(unsigned short perm, u32 flags)
u32 mask = 0;

if (perm & ACL_READ)
- mask |= NFS4_READ_MODE;
+ mask |= RICHACE_READ_MODE;
if (perm & ACL_WRITE)
- mask |= NFS4_WRITE_MODE;
- if ((perm & ACL_WRITE) && (flags & NFS4_ACL_DIR))
- mask |= NFS4_ACE_DELETE_CHILD;
+ mask |= RICHACE_WRITE_MODE;
+ if ((perm & ACL_WRITE) && (flags & FLAG_DIRECTORY))
+ mask |= RICHACE_DELETE_CHILD;
if (perm & ACL_EXECUTE)
- mask |= NFS4_EXECUTE_MODE;
+ mask |= RICHACE_EXECUTE_MODE;
return mask;
}

@@ -106,32 +110,32 @@ deny_mask_from_posix(unsigned short perm, u32 flags)
static void
low_mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags)
{
- u32 write_mode = NFS4_WRITE_MODE;
+ u32 write_mode = RICHACE_WRITE_MODE;

- if (flags & NFS4_ACL_DIR)
- write_mode |= NFS4_ACE_DELETE_CHILD;
+ if (flags & FLAG_DIRECTORY)
+ write_mode |= RICHACE_DELETE_CHILD;
*mode = 0;
- if ((perm & NFS4_READ_MODE) == NFS4_READ_MODE)
+ if ((perm & RICHACE_READ_MODE) == RICHACE_READ_MODE)
*mode |= ACL_READ;
if ((perm & write_mode) == write_mode)
*mode |= ACL_WRITE;
- if ((perm & NFS4_EXECUTE_MODE) == NFS4_EXECUTE_MODE)
+ if ((perm & RICHACE_EXECUTE_MODE) == RICHACE_EXECUTE_MODE)
*mode |= ACL_EXECUTE;
}

-static short ace2type(struct nfs4_ace *);
-static void _posix_to_nfsv4_one(struct posix_acl *, struct nfs4_acl *,
+static short ace2type(struct richace *);
+static void _posix_to_richacl_one(struct posix_acl *, struct richacl_alloc *,
unsigned int);

int
-nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
- struct nfs4_acl **acl)
+nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct richacl **acl)
{
struct inode *inode = dentry->d_inode;
int error = 0;
struct posix_acl *pacl = NULL, *dpacl = NULL;
+ struct richacl_alloc x;
unsigned int flags = 0;
- int size = 0;
+ int count;

pacl = get_acl(inode, ACL_TYPE_ACCESS);
if (!pacl)
@@ -141,10 +145,10 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
return PTR_ERR(pacl);

/* allocate for worst case: one (deny, allow) pair each: */
- size += 2 * pacl->a_count;
+ count = 2 * pacl->a_count;

if (S_ISDIR(inode->i_mode)) {
- flags = NFS4_ACL_DIR;
+ flags = FLAG_DIRECTORY;
dpacl = get_acl(inode, ACL_TYPE_DEFAULT);
if (IS_ERR(dpacl)) {
error = PTR_ERR(dpacl);
@@ -152,20 +156,20 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
}

if (dpacl)
- size += 2 * dpacl->a_count;
+ count += 2 * dpacl->a_count;
}

- *acl = kmalloc(nfs4_acl_bytes(size), GFP_KERNEL);
- if (*acl == NULL) {
+ if (!richacl_prepare(&x, count)) {
error = -ENOMEM;
goto out;
}
- (*acl)->naces = 0;

- _posix_to_nfsv4_one(pacl, *acl, flags & ~NFS4_ACL_TYPE_DEFAULT);
+ _posix_to_richacl_one(pacl, &x, flags);

if (dpacl)
- _posix_to_nfsv4_one(dpacl, *acl, flags | NFS4_ACL_TYPE_DEFAULT);
+ _posix_to_richacl_one(dpacl, &x, flags | FLAG_DEFAULT_ACL);
+
+ *acl = x.acl;

out:
posix_acl_release(dpacl);
@@ -228,21 +232,20 @@ summarize_posix_acl(struct posix_acl *acl, struct posix_acl_summary *pas)

/* We assume the acl has been verified with posix_acl_valid. */
static void
-_posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
- unsigned int flags)
+_posix_to_richacl_one(struct posix_acl *pacl, struct richacl_alloc *x,
+ unsigned int flags)
{
struct posix_acl_entry *pa, *group_owner_entry;
- struct nfs4_ace *ace;
+ struct richace *ace;
struct posix_acl_summary pas;
unsigned short deny;
- int eflag = ((flags & NFS4_ACL_TYPE_DEFAULT) ?
- NFS4_INHERITANCE_FLAGS | NFS4_ACE_INHERIT_ONLY_ACE : 0);
+ int e_flags = ((flags & FLAG_DEFAULT_ACL) ?
+ RICHACE_INHERITANCE_FLAGS | RICHACE_INHERIT_ONLY_ACE : 0);

BUG_ON(pacl->a_count < 3);
summarize_posix_acl(pacl, &pas);

pa = pacl->a_entries;
- ace = acl->aces + acl->naces;

/* We could deny everything not granted by the owner: */
deny = ~pas.owner;
@@ -252,42 +255,35 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
*/
deny &= pas.users | pas.group | pas.groups | pas.other;
if (deny) {
- ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
- ace->flag = eflag;
- ace->access_mask = deny_mask_from_posix(deny, flags);
- ace->whotype = NFS4_ACL_WHO_OWNER;
- ace++;
- acl->naces++;
+ ace = richacl_append_entry(x);
+ ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
+ ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
+ ace->e_mask = deny_mask_from_posix(deny, flags);
+ ace->e_id.special = RICHACE_OWNER_SPECIAL_ID;
}

- ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
- ace->flag = eflag;
- ace->access_mask = mask_from_posix(pa->e_perm, flags | NFS4_ACL_OWNER);
- ace->whotype = NFS4_ACL_WHO_OWNER;
- ace++;
- acl->naces++;
+ ace = richacl_append_entry(x);
+ ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
+ ace->e_mask = mask_from_posix(pa->e_perm, flags | FLAG_OWNER);
+ ace->e_id.special = RICHACE_OWNER_SPECIAL_ID;
pa++;

while (pa->e_tag == ACL_USER) {
deny = ~(pa->e_perm & pas.mask);
deny &= pas.groups | pas.group | pas.other;
if (deny) {
- ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
- ace->flag = eflag;
- ace->access_mask = deny_mask_from_posix(deny, flags);
- ace->whotype = NFS4_ACL_WHO_NAMED;
- ace->who_uid = pa->e_uid;
- ace++;
- acl->naces++;
+ ace = richacl_append_entry(x);
+ ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
+ ace->e_flags = e_flags;
+ ace->e_mask = deny_mask_from_posix(deny, flags);
+ ace->e_id.uid = pa->e_uid;
}
- ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
- ace->flag = eflag;
- ace->access_mask = mask_from_posix(pa->e_perm & pas.mask,
- flags);
- ace->whotype = NFS4_ACL_WHO_NAMED;
- ace->who_uid = pa->e_uid;
- ace++;
- acl->naces++;
+ ace = richacl_append_entry(x);
+ ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = e_flags;
+ ace->e_mask = mask_from_posix(pa->e_perm & pas.mask, flags);
+ ace->e_id.uid = pa->e_uid;
pa++;
}

@@ -298,23 +294,19 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,

group_owner_entry = pa;

- ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
- ace->flag = eflag;
- ace->access_mask = mask_from_posix(pas.group, flags);
- ace->whotype = NFS4_ACL_WHO_GROUP;
- ace++;
- acl->naces++;
+ ace = richacl_append_entry(x);
+ ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
+ ace->e_mask = mask_from_posix(pas.group, flags);
+ ace->e_id.special = RICHACE_GROUP_SPECIAL_ID;
pa++;

while (pa->e_tag == ACL_GROUP) {
- ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
- ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP;
- ace->access_mask = mask_from_posix(pa->e_perm & pas.mask,
- flags);
- ace->whotype = NFS4_ACL_WHO_NAMED;
- ace->who_gid = pa->e_gid;
- ace++;
- acl->naces++;
+ ace = richacl_append_entry(x);
+ ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = e_flags | RICHACE_IDENTIFIER_GROUP;
+ ace->e_mask = mask_from_posix(pa->e_perm & pas.mask, flags);
+ ace->e_id.gid = pa->e_gid;
pa++;
}

@@ -324,12 +316,11 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,

deny = ~pas.group & pas.other;
if (deny) {
- ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
- ace->flag = eflag;
- ace->access_mask = deny_mask_from_posix(deny, flags);
- ace->whotype = NFS4_ACL_WHO_GROUP;
- ace++;
- acl->naces++;
+ ace = richacl_append_entry(x);
+ ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
+ ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
+ ace->e_mask = deny_mask_from_posix(deny, flags);
+ ace->e_id.special = RICHACE_GROUP_SPECIAL_ID;
}
pa++;

@@ -337,24 +328,22 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
deny = ~(pa->e_perm & pas.mask);
deny &= pas.other;
if (deny) {
- ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
- ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP;
- ace->access_mask = deny_mask_from_posix(deny, flags);
- ace->whotype = NFS4_ACL_WHO_NAMED;
- ace->who_gid = pa->e_gid;
- ace++;
- acl->naces++;
+ ace = richacl_append_entry(x);
+ ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
+ ace->e_flags = e_flags | RICHACE_IDENTIFIER_GROUP;
+ ace->e_mask = deny_mask_from_posix(deny, flags);
+ ace->e_id.gid = pa->e_gid;
}
pa++;
}

if (pa->e_tag == ACL_MASK)
pa++;
- ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
- ace->flag = eflag;
- ace->access_mask = mask_from_posix(pa->e_perm, flags);
- ace->whotype = NFS4_ACL_WHO_EVERYONE;
- acl->naces++;
+ ace = richacl_append_entry(x);
+ ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
+ ace->e_mask = mask_from_posix(pa->e_perm, flags);
+ ace->e_id.special = RICHACE_EVERYONE_SPECIAL_ID;
}

static bool
@@ -508,11 +497,11 @@ static inline void add_to_mask(struct posix_acl_state *state, struct posix_ace_s

static inline int check_deny(u32 mask, int isowner)
{
- if (mask & (NFS4_ACE_READ_ATTRIBUTES | NFS4_ACE_READ_ACL))
+ if (mask & (RICHACE_READ_ATTRIBUTES | RICHACE_READ_ACL))
return -EINVAL;
if (!isowner)
return 0;
- if (mask & (NFS4_ACE_WRITE_ATTRIBUTES | NFS4_ACE_WRITE_ACL))
+ if (mask & (RICHACE_WRITE_ATTRIBUTES | RICHACE_WRITE_ACL))
return -EINVAL;
return 0;
}
@@ -530,7 +519,7 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags)
* and effective cases: when there are no inheritable ACEs,
* calls ->set_acl with a NULL ACL structure.
*/
- if (state->empty && (flags & NFS4_ACL_TYPE_DEFAULT))
+ if (state->empty && (flags & FLAG_DEFAULT_ACL))
return NULL;

/*
@@ -667,24 +656,24 @@ static void allow_bits_array(struct posix_ace_state_array *a, u32 mask)
}

static void process_one_v4_ace(struct posix_acl_state *state,
- struct nfs4_ace *ace)
+ struct richace *ace)
{
- u32 mask = ace->access_mask;
+ u32 mask = ace->e_mask;
int i;

state->empty = 0;

switch (ace2type(ace)) {
case ACL_USER_OBJ:
- if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
+ if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
allow_bits(&state->owner, mask);
} else {
deny_bits(&state->owner, mask);
}
break;
case ACL_USER:
- i = find_uid(state, ace->who_uid);
- if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
+ i = find_uid(state, ace->e_id.uid);
+ if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
allow_bits(&state->users->aces[i].perms, mask);
} else {
deny_bits(&state->users->aces[i].perms, mask);
@@ -693,7 +682,7 @@ static void process_one_v4_ace(struct posix_acl_state *state,
}
break;
case ACL_GROUP_OBJ:
- if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
+ if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
allow_bits(&state->group, mask);
} else {
deny_bits(&state->group, mask);
@@ -705,8 +694,8 @@ static void process_one_v4_ace(struct posix_acl_state *state,
}
break;
case ACL_GROUP:
- i = find_gid(state, ace->who_gid);
- if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
+ i = find_gid(state, ace->e_id.gid);
+ if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
allow_bits(&state->groups->aces[i].perms, mask);
} else {
deny_bits(&state->groups->aces[i].perms, mask);
@@ -719,7 +708,7 @@ static void process_one_v4_ace(struct posix_acl_state *state,
}
break;
case ACL_OTHER:
- if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
+ if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
allow_bits(&state->owner, mask);
allow_bits(&state->group, mask);
allow_bits(&state->other, mask);
@@ -737,32 +726,32 @@ static void process_one_v4_ace(struct posix_acl_state *state,
}
}

-static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
+static int nfs4_richacl_to_posix(struct richacl *acl,
struct posix_acl **pacl, struct posix_acl **dpacl,
unsigned int flags)
{
struct posix_acl_state effective_acl_state, default_acl_state;
- struct nfs4_ace *ace;
+ struct richace *ace;
int ret;

- ret = init_state(&effective_acl_state, acl->naces);
+ ret = init_state(&effective_acl_state, acl->a_count);
if (ret)
return ret;
- ret = init_state(&default_acl_state, acl->naces);
+ ret = init_state(&default_acl_state, acl->a_count);
if (ret)
goto out_estate;
ret = -EINVAL;
- for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
- if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE &&
- ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE)
+ richacl_for_each_entry(ace, acl) {
+ if (ace->e_type != RICHACE_ACCESS_ALLOWED_ACE_TYPE &&
+ ace->e_type != RICHACE_ACCESS_DENIED_ACE_TYPE)
goto out_dstate;
- if (ace->flag & ~NFS4_SUPPORTED_FLAGS)
+ if (ace->e_flags & ~RICHACE_SUPPORTED_FLAGS)
goto out_dstate;
- if ((ace->flag & NFS4_INHERITANCE_FLAGS) == 0) {
+ if ((ace->e_flags & RICHACE_INHERITANCE_FLAGS) == 0) {
process_one_v4_ace(&effective_acl_state, ace);
continue;
}
- if (!(flags & NFS4_ACL_DIR))
+ if (!(flags & FLAG_DIRECTORY))
goto out_dstate;
/*
* Note that when only one of FILE_INHERIT or DIRECTORY_INHERIT
@@ -771,7 +760,7 @@ static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
*/
process_one_v4_ace(&default_acl_state, ace);

- if (!(ace->flag & NFS4_ACE_INHERIT_ONLY_ACE))
+ if (!(ace->e_flags & RICHACE_INHERIT_ONLY_ACE))
process_one_v4_ace(&effective_acl_state, ace);
}
*pacl = posix_state_to_acl(&effective_acl_state, flags);
@@ -781,7 +770,7 @@ static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
goto out_dstate;
}
*dpacl = posix_state_to_acl(&default_acl_state,
- flags | NFS4_ACL_TYPE_DEFAULT);
+ flags | FLAG_DEFAULT_ACL);
if (IS_ERR(*dpacl)) {
ret = PTR_ERR(*dpacl);
*dpacl = NULL;
@@ -800,8 +789,7 @@ out_estate:
}

__be32
-nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
- struct nfs4_acl *acl)
+nfsd4_set_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, struct richacl *acl)
{
__be32 error;
int host_error;
@@ -822,9 +810,9 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
return nfserr_attrnotsupp;

if (S_ISDIR(inode->i_mode))
- flags = NFS4_ACL_DIR;
+ flags = FLAG_DIRECTORY;

- host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
+ host_error = nfs4_richacl_to_posix(acl, &pacl, &dpacl, flags);
if (host_error == -EINVAL)
return nfserr_attrnotsupp;
if (host_error < 0)
@@ -851,82 +839,62 @@ out_nfserr:


static short
-ace2type(struct nfs4_ace *ace)
+ace2type(struct richace *ace)
{
- switch (ace->whotype) {
- case NFS4_ACL_WHO_NAMED:
- return (ace->flag & NFS4_ACE_IDENTIFIER_GROUP ?
- ACL_GROUP : ACL_USER);
- case NFS4_ACL_WHO_OWNER:
- return ACL_USER_OBJ;
- case NFS4_ACL_WHO_GROUP:
- return ACL_GROUP_OBJ;
- case NFS4_ACL_WHO_EVERYONE:
- return ACL_OTHER;
+ if (ace->e_flags & RICHACE_SPECIAL_WHO) {
+ switch(ace->e_id.special) {
+ case RICHACE_OWNER_SPECIAL_ID:
+ return ACL_USER_OBJ;
+ case RICHACE_GROUP_SPECIAL_ID:
+ return ACL_GROUP_OBJ;
+ case RICHACE_EVERYONE_SPECIAL_ID:
+ return ACL_OTHER;
+ default:
+ BUG();
+ }
}
- BUG();
- return -1;
+ return ace->e_flags & RICHACE_IDENTIFIER_GROUP ? ACL_GROUP : ACL_USER;
}

-/*
- * return the size of the struct nfs4_acl required to represent an acl
- * with @entries entries.
- */
-int nfs4_acl_bytes(int entries)
+__be32 nfsd4_decode_ace_who(struct richace *ace, struct svc_rqst *rqstp,
+ char *who, u32 len)
{
- return sizeof(struct nfs4_acl) + entries * sizeof(struct nfs4_ace);
-}
-
-static struct {
- char *string;
- int stringlen;
- int type;
-} s2t_map[] = {
- {
- .string = "OWNER@",
- .stringlen = sizeof("OWNER@") - 1,
- .type = NFS4_ACL_WHO_OWNER,
- },
- {
- .string = "GROUP@",
- .stringlen = sizeof("GROUP@") - 1,
- .type = NFS4_ACL_WHO_GROUP,
- },
- {
- .string = "EVERYONE@",
- .stringlen = sizeof("EVERYONE@") - 1,
- .type = NFS4_ACL_WHO_EVERYONE,
- },
-};
-
-int
-nfs4_acl_get_whotype(char *p, u32 len)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(s2t_map); i++) {
- if (s2t_map[i].stringlen == len &&
- 0 == memcmp(s2t_map[i].string, p, len))
- return s2t_map[i].type;
+ int special_id;
+
+ 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 nfs_ok;
}
- return NFS4_ACL_WHO_NAMED;
+ if (ace->e_flags & RICHACE_IDENTIFIER_GROUP)
+ return nfsd_map_name_to_gid(rqstp, who, len, &ace->e_id.gid);
+ else
+ return nfsd_map_name_to_uid(rqstp, who, len, &ace->e_id.uid);
}

-__be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who)
+__be32 nfsd4_encode_ace_who(struct xdr_stream *xdr, struct svc_rqst *rqstp,
+ struct richace *ace)
{
- __be32 *p;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(s2t_map); i++) {
- if (s2t_map[i].type != who)
- continue;
- p = xdr_reserve_space(xdr, s2t_map[i].stringlen + 4);
+ if (ace->e_flags & RICHACE_SPECIAL_WHO) {
+ unsigned int special_id = ace->e_id.special;
+ const char *who;
+ unsigned int len;
+ __be32 *p;
+
+ if (!nfs4acl_special_id_to_who(special_id, &who, &len)) {
+ WARN_ON_ONCE(1);
+ return nfserr_serverfault;
+ }
+ p = xdr_reserve_space(xdr, len + 4);
if (!p)
return nfserr_resource;
- p = xdr_encode_opaque(p, s2t_map[i].string,
- s2t_map[i].stringlen);
+ p = xdr_encode_opaque(p, who, len);
return 0;
}
- WARN_ON_ONCE(1);
- return nfserr_serverfault;
+ if (ace->e_flags & RICHACE_IDENTIFIER_GROUP)
+ return nfsd4_encode_group(xdr, rqstp, ace->e_id.gid);
+ else
+ return nfsd4_encode_user(xdr, rqstp, ace->e_id.uid);
}
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 92b9d97..658da6e 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -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)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index f25d1e7..f3c1880 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -302,7 +302,7 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)

static __be32
nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
- struct iattr *iattr, struct nfs4_acl **acl,
+ struct iattr *iattr, struct richacl **acl,
struct xdr_netobj *label)
{
int expected_len, len = 0;
@@ -325,38 +325,31 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
}
if (bmval[0] & FATTR4_WORD0_ACL) {
u32 nace;
- struct nfs4_ace *ace;
+ struct richace *ace;

READ_BUF(4); len += 4;
nace = be32_to_cpup(p++);

- if (nace > NFS4_ACL_MAX)
+ if (nace > NFSD4_ACL_MAX)
return nfserr_fbig;

- *acl = svcxdr_tmpalloc(argp, nfs4_acl_bytes(nace));
+ *acl = svcxdr_alloc_richacl(argp, nace);
if (*acl == NULL)
return nfserr_jukebox;

- (*acl)->naces = nace;
- for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) {
+ richacl_for_each_entry(ace, *acl) {
READ_BUF(16); len += 16;
- ace->type = be32_to_cpup(p++);
- ace->flag = be32_to_cpup(p++);
- ace->access_mask = be32_to_cpup(p++);
+ 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)
+ return nfserr_inval;
dummy32 = be32_to_cpup(p++);
READ_BUF(dummy32);
len += XDR_QUADLEN(dummy32) << 2;
READMEM(buf, dummy32);
- ace->whotype = nfs4_acl_get_whotype(buf, dummy32);
- status = nfs_ok;
- if (ace->whotype != NFS4_ACL_WHO_NAMED)
- ;
- else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
- status = nfsd_map_name_to_gid(argp->rqstp,
- buf, dummy32, &ace->who_gid);
- else
- status = nfsd_map_name_to_uid(argp->rqstp,
- buf, dummy32, &ace->who_uid);
+ status = nfsd4_decode_ace_who(ace, argp->rqstp,
+ buf, dummy32);
if (status)
return status;
}
@@ -2146,18 +2139,6 @@ static u32 nfs4_file_type(umode_t mode)
};
}

-static inline __be32
-nfsd4_encode_aclname(struct xdr_stream *xdr, struct svc_rqst *rqstp,
- struct nfs4_ace *ace)
-{
- if (ace->whotype != NFS4_ACL_WHO_NAMED)
- return nfs4_acl_write_who(xdr, ace->whotype);
- else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
- return nfsd4_encode_group(xdr, rqstp, ace->who_gid);
- else
- return nfsd4_encode_user(xdr, rqstp, ace->who_uid);
-}
-
#define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
FATTR4_WORD0_RDATTR_ERROR)
#define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
@@ -2246,7 +2227,7 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
u32 rdattr_err = 0;
__be32 status;
int err;
- struct nfs4_acl *acl = NULL;
+ struct richacl *acl = NULL;
void *context = NULL;
int contextlen;
bool contextsupport = false;
@@ -2467,7 +2448,7 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
*p++ = cpu_to_be32(rdattr_err);
}
if (bmval0 & FATTR4_WORD0_ACL) {
- struct nfs4_ace *ace;
+ struct richace *ace;

if (acl == NULL) {
p = xdr_reserve_space(xdr, 4);
@@ -2480,17 +2461,16 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
p = xdr_reserve_space(xdr, 4);
if (!p)
goto out_resource;
- *p++ = cpu_to_be32(acl->naces);
+ *p++ = cpu_to_be32(acl->a_count);

- for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
+ richacl_for_each_entry(ace, acl) {
p = xdr_reserve_space(xdr, 4*3);
if (!p)
goto out_resource;
- *p++ = cpu_to_be32(ace->type);
- *p++ = cpu_to_be32(ace->flag);
- *p++ = cpu_to_be32(ace->access_mask &
- NFS4_ACE_MASK_ALL);
- status = nfsd4_encode_aclname(xdr, rqstp, ace);
+ *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);
+ status = nfsd4_encode_ace_who(xdr, rqstp, ace);
if (status)
goto out;
}
@@ -2753,7 +2733,7 @@ out:
if (context)
security_release_secctx(context, contextlen);
#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
- kfree(acl);
+ richacl_put(acl);
if (tempfh) {
fh_put(tempfh);
kfree(tempfh);
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index 4ed4db0..092c340 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -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;
};

diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 32201c2..095854c 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -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 {
diff --git a/include/linux/nfs4acl.h b/include/linux/nfs4acl.h
new file mode 100644
index 0000000..db9f9a6
--- /dev/null
+++ b/include/linux/nfs4acl.h
@@ -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
--
2.1.0


2015-04-24 11:05:27

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 26/45] richacl: Move everyone@ aces down the acl

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.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/richacl_compat.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)

diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
index 2ea6658..63f4373 100644
--- a/fs/richacl_compat.c
+++ b/fs/richacl_compat.c
@@ -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;
+}
--
2.1.0


2015-04-24 11:05:31

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 28/45] richacl: Isolate the owner and group classes

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

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/richacl_compat.c | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 233 insertions(+)

diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
index f43d007..645917f 100644
--- a/fs/richacl_compat.c
+++ b/fs/richacl_compat.c
@@ -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;
+}
--
2.1.0


2015-04-24 11:05:29

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 27/45] richacl: Propagate everyone@ permissions to other aces

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

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/richacl_compat.c | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 190 insertions(+)

diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
index 63f4373..f43d007 100644
--- a/fs/richacl_compat.c
+++ b/fs/richacl_compat.c
@@ -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;
+}
--
2.1.0


2015-04-24 11:05:27

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 25/45] richacl: acl editing helper functions

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.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
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

diff --git a/fs/Makefile b/fs/Makefile
index 6155cc4..5983276 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -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/

diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
new file mode 100644
index 0000000..2ea6658
--- /dev/null
+++ b/fs/richacl_compat.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2006, 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/module.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/richacl_compat.h>
+
+/**
+ * richacl_prepare - allocate richacl being constructed
+ *
+ * Allocate a richacl which can hold @count entries but which is initially
+ * empty.
+ */
+struct richacl *richacl_prepare(struct richacl_alloc *x, unsigned int count)
+{
+ x->acl = richacl_alloc(count, GFP_KERNEL);
+ if (!x->acl)
+ return NULL;
+ x->acl->a_count = 0;
+ x->count = count;
+ return x->acl;
+}
+EXPORT_SYMBOL_GPL(richacl_prepare);
+
+/**
+ * richacl_delete_entry - delete an entry in an acl
+ * @x: acl and number of allocated entries
+ * @ace: an entry in @x->acl
+ *
+ * Updates @ace so that it points to the entry before the deleted entry
+ * on return. (When deleting the first entry, @ace will point to the
+ * (non-existant) entry before the first entry). This behavior is the
+ * expected behavior when deleting entries while forward iterating over
+ * an acl.
+ */
+void
+richacl_delete_entry(struct richacl_alloc *x, struct richace **ace)
+{
+ void *end = x->acl->a_entries + x->acl->a_count;
+
+ memmove(*ace, *ace + 1, end - (void *)(*ace + 1));
+ (*ace)--;
+ x->acl->a_count--;
+}
+EXPORT_SYMBOL_GPL(richacl_delete_entry);
+
+/**
+ * richacl_insert_entry - insert an entry in an acl
+ * @x: acl and number of allocated entries
+ * @ace: entry before which the new entry shall be inserted
+ *
+ * Insert a new entry in @x->acl at position @ace and zero-initialize
+ * it. This may require reallocating @x->acl.
+ */
+int
+richacl_insert_entry(struct richacl_alloc *x, struct richace **ace)
+{
+ if (x->count == x->acl->a_count) {
+ int n = *ace - x->acl->a_entries;
+ struct richacl *acl2;
+
+ acl2 = richacl_alloc(x->acl->a_count + 1, GFP_KERNEL);
+ if (!acl2)
+ return -1;
+ acl2->a_flags = x->acl->a_flags;
+ acl2->a_owner_mask = x->acl->a_owner_mask;
+ acl2->a_group_mask = x->acl->a_group_mask;
+ acl2->a_other_mask = x->acl->a_other_mask;
+ memcpy(acl2->a_entries, x->acl->a_entries,
+ n * sizeof(struct richace));
+ memcpy(acl2->a_entries + n + 1, *ace,
+ (x->acl->a_count - n) * sizeof(struct richace));
+ kfree(x->acl);
+ x->acl = acl2;
+ x->count = acl2->a_count;
+ *ace = acl2->a_entries + n;
+ } else {
+ void *end = x->acl->a_entries + x->acl->a_count;
+
+ memmove(*ace + 1, *ace, end - (void *)*ace);
+ x->acl->a_count++;
+ }
+ memset(*ace, 0, sizeof(struct richace));
+ return 0;
+}
+EXPORT_SYMBOL_GPL(richacl_insert_entry);
+
+/**
+ * richacl_append_entry - append an entry to an acl
+ * @x: acl and number of allocated entries
+ *
+ * This may require reallocating @x->acl.
+ */
+struct richace *richacl_append_entry(struct richacl_alloc *x)
+{
+ struct richacl *acl = x->acl;
+ struct richace *ace = acl->a_entries + acl->a_count;
+
+ if (x->count > x->acl->a_count) {
+ acl->a_count++;
+ return ace;
+ }
+ return richacl_insert_entry(x, &ace) ? NULL : ace;
+}
+EXPORT_SYMBOL_GPL(richacl_append_entry);
+
+/**
+ * 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
+ * there are any inheritable permissions, and removed otherwise.
+ *
+ * The returned @ace points to the modified or inserted effective-only acl
+ * entry if that entry exists, to the entry that has become inheritable-only,
+ * or else to the previous entry in the acl.
+ */
+static int
+richace_change_mask(struct richacl_alloc *x, struct richace **ace,
+ unsigned int mask)
+{
+ if (mask && (*ace)->e_mask == mask)
+ return 0;
+ if (mask & ~RICHACE_POSIX_ALWAYS_ALLOWED) {
+ if (richace_is_inheritable(*ace)) {
+ if (richacl_insert_entry(x, ace))
+ return -1;
+ memcpy(*ace, *ace + 1, sizeof(struct richace));
+ (*ace)->e_flags |= RICHACE_INHERIT_ONLY_ACE;
+ (*ace)++;
+ richace_clear_inheritance_flags(*ace);
+ }
+ (*ace)->e_mask = mask;
+ } else {
+ if (richace_is_inheritable(*ace))
+ (*ace)->e_flags |= RICHACE_INHERIT_ONLY_ACE;
+ else
+ richacl_delete_entry(x, ace);
+ }
+ return 0;
+}
diff --git a/include/linux/richacl_compat.h b/include/linux/richacl_compat.h
new file mode 100644
index 0000000..a9ff630
--- /dev/null
+++ b/include/linux/richacl_compat.h
@@ -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 */
--
2.1.0


2015-04-24 11:05:15

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 17/45] richacl: Create-time inheritance

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

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

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/richacl_base.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++
fs/richacl_inode.c | 60 ++++++++++++++++++++++++++++++++++++++++++
include/linux/richacl.h | 2 ++
3 files changed, 131 insertions(+)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 5273ccb..5d45539 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -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;
+}
diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
index b421f68..1a5b868 100644
--- a/fs/richacl_inode.c
+++ b/fs/richacl_inode.c
@@ -201,3 +201,63 @@ is_everyone:
return denied ? -EACCES : 0;
}
EXPORT_SYMBOL_GPL(richacl_permission);
+
+/**
+ * richacl_inherit_inode - compute inherited acl and file mode
+ * @dir_acl: acl of the containing directory
+ * @inode: inode of the new file (create mode in i_mode)
+ *
+ * The file permission bits in inode->i_mode must be set to the create mode by
+ * the caller.
+ *
+ * If there is an inheritable acl, the maximum permissions that the acl grants
+ * will be computed and permissions not granted by the acl will be removed from
+ * inode->i_mode. If there is no inheritable acl, the umask will be applied
+ * instead.
+ */
+static struct richacl *
+richacl_inherit_inode(const struct richacl *dir_acl, struct inode *inode)
+{
+ struct richacl *acl;
+ mode_t mask;
+
+ acl = richacl_inherit(dir_acl, S_ISDIR(inode->i_mode));
+ if (acl) {
+
+ richacl_compute_max_masks(acl);
+
+ /*
+ * Ensure that the acl will not grant any permissions beyond
+ * the create mode.
+ */
+ acl->a_flags |= RICHACL_MASKED;
+ acl->a_owner_mask &= richacl_mode_to_mask(inode->i_mode >> 6) |
+ RICHACE_POSIX_OWNER_ALLOWED;
+ acl->a_group_mask &= richacl_mode_to_mask(inode->i_mode >> 3);
+ acl->a_other_mask &= richacl_mode_to_mask(inode->i_mode);
+ mask = ~S_IRWXUGO | richacl_masks_to_mode(acl);
+ } else
+ mask = ~current_umask();
+
+ inode->i_mode &= mask;
+ return acl;
+}
+
+struct richacl *richacl_create(struct inode *inode, struct inode *dir)
+{
+ struct richacl *dir_acl, *acl = NULL;
+
+ if (S_ISLNK(inode->i_mode))
+ return NULL;
+ dir_acl = get_richacl(dir);
+ if (dir_acl) {
+ if (IS_ERR(dir_acl))
+ return dir_acl;
+ acl = richacl_inherit_inode(dir_acl, inode);
+ richacl_put(dir_acl);
+ }
+ if (!acl)
+ inode->i_mode &= ~current_umask();
+ return acl;
+}
+EXPORT_SYMBOL_GPL(richacl_create);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 6d88ee9..ab9fde1 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -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 */
--
2.1.0


2015-04-24 11:05:20

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 21/45] richacl: xattr mapping functions

Map between "system.richacl" xattrs and the in-kernel representation.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
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

diff --git a/fs/Makefile b/fs/Makefile
index bb96ad7..6155cc4 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -48,7 +48,7 @@ obj-$(CONFIG_SYSCTL) += drop_caches.o

obj-$(CONFIG_FHANDLE) += fhandle.o
obj-$(CONFIG_FS_RICHACL) += richacl.o
-richacl-y := richacl_base.o richacl_inode.o
+richacl-y := richacl_base.o richacl_inode.o richacl_xattr.o

obj-y += quota/

diff --git a/fs/richacl_xattr.c b/fs/richacl_xattr.c
new file mode 100644
index 0000000..1fb5b36
--- /dev/null
+++ b/fs/richacl_xattr.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2006, 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/fs.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/richacl_xattr.h>
+
+MODULE_LICENSE("GPL");
+
+/**
+ * richacl_from_xattr - convert a richacl xattr into the in-memory representation
+ */
+struct richacl *
+richacl_from_xattr(struct user_namespace *user_ns,
+ const void *value, size_t size)
+{
+ const struct richacl_xattr *xattr_acl = value;
+ const struct richace_xattr *xattr_ace = (void *)(xattr_acl + 1);
+ struct richacl *acl;
+ struct richace *ace;
+ int count;
+
+ if (size < sizeof(*xattr_acl) ||
+ xattr_acl->a_version != RICHACL_XATTR_VERSION ||
+ (xattr_acl->a_flags & ~RICHACL_VALID_FLAGS) ||
+ xattr_acl->a_unused != 0)
+ return ERR_PTR(-EINVAL);
+ size -= sizeof(*xattr_acl);
+ if (size % sizeof(*xattr_ace))
+ return ERR_PTR(-EINVAL);
+ count = size / sizeof(*xattr_ace);
+ if (count > RICHACL_XATTR_MAX_COUNT)
+ return ERR_PTR(-EINVAL);
+
+ acl = richacl_alloc(count, GFP_NOFS);
+ if (!acl)
+ return ERR_PTR(-ENOMEM);
+
+ acl->a_flags = xattr_acl->a_flags;
+ acl->a_owner_mask = le32_to_cpu(xattr_acl->a_owner_mask);
+ if (acl->a_owner_mask & ~RICHACE_VALID_MASK)
+ goto fail_einval;
+ acl->a_group_mask = le32_to_cpu(xattr_acl->a_group_mask);
+ if (acl->a_group_mask & ~RICHACE_VALID_MASK)
+ goto fail_einval;
+ acl->a_other_mask = le32_to_cpu(xattr_acl->a_other_mask);
+ if (acl->a_other_mask & ~RICHACE_VALID_MASK)
+ goto fail_einval;
+
+ richacl_for_each_entry(ace, acl) {
+ ace->e_type = le16_to_cpu(xattr_ace->e_type);
+ ace->e_flags = le16_to_cpu(xattr_ace->e_flags);
+ ace->e_mask = le32_to_cpu(xattr_ace->e_mask);
+
+ if (ace->e_flags & ~RICHACE_VALID_FLAGS)
+ goto fail_einval;
+ if (ace->e_flags & RICHACE_SPECIAL_WHO) {
+ ace->e_id.special = le32_to_cpu(xattr_ace->e_id);
+ if (ace->e_id.special > RICHACE_EVERYONE_SPECIAL_ID)
+ goto fail_einval;
+ } else if (ace->e_flags & RICHACE_IDENTIFIER_GROUP) {
+ ace->e_id.gid = make_kgid(user_ns, le32_to_cpu(xattr_ace->e_id));
+ if (!gid_valid(ace->e_id.gid))
+ goto fail_einval;
+ } else {
+ ace->e_id.uid = make_kuid(user_ns, le32_to_cpu(xattr_ace->e_id));
+ if (!uid_valid(ace->e_id.uid))
+ goto fail_einval;
+ }
+ if (ace->e_type > RICHACE_ACCESS_DENIED_ACE_TYPE ||
+ (ace->e_mask & ~RICHACE_VALID_MASK))
+ goto fail_einval;
+
+ xattr_ace++;
+ }
+
+ return acl;
+
+fail_einval:
+ richacl_put(acl);
+ return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL_GPL(richacl_from_xattr);
+
+/**
+ * richacl_xattr_size - compute the size of the xattr representation of @acl
+ */
+size_t
+richacl_xattr_size(const struct richacl *acl)
+{
+ size_t size = sizeof(struct richacl_xattr);
+
+ size += sizeof(struct richace_xattr) * acl->a_count;
+ return size;
+}
+EXPORT_SYMBOL_GPL(richacl_xattr_size);
+
+/**
+ * richacl_to_xattr - convert @acl into its xattr representation
+ * @acl: the richacl to convert
+ * @buffer: buffer for the result
+ * @size: size of @buffer
+ */
+int
+richacl_to_xattr(struct user_namespace *user_ns,
+ const struct richacl *acl, void *buffer, size_t size)
+{
+ struct richacl_xattr *xattr_acl = buffer;
+ struct richace_xattr *xattr_ace;
+ const struct richace *ace;
+ size_t real_size;
+
+ real_size = richacl_xattr_size(acl);
+ if (!buffer)
+ return real_size;
+ if (real_size > size)
+ return -ERANGE;
+
+ xattr_acl->a_version = RICHACL_XATTR_VERSION;
+ xattr_acl->a_flags = acl->a_flags;
+ xattr_acl->a_unused = 0;
+
+ xattr_acl->a_owner_mask = cpu_to_le32(acl->a_owner_mask);
+ xattr_acl->a_group_mask = cpu_to_le32(acl->a_group_mask);
+ xattr_acl->a_other_mask = cpu_to_le32(acl->a_other_mask);
+
+ xattr_ace = (void *)(xattr_acl + 1);
+ richacl_for_each_entry(ace, acl) {
+ xattr_ace->e_type = cpu_to_le16(ace->e_type);
+ xattr_ace->e_flags = cpu_to_le16(ace->e_flags);
+ xattr_ace->e_mask = cpu_to_le32(ace->e_mask);
+ if (ace->e_flags & RICHACE_SPECIAL_WHO)
+ xattr_ace->e_id = cpu_to_le32(ace->e_id.special);
+ else if (ace->e_flags & RICHACE_IDENTIFIER_GROUP)
+ xattr_ace->e_id =
+ cpu_to_le32(from_kgid(user_ns, ace->e_id.gid));
+ else
+ xattr_ace->e_id =
+ cpu_to_le32(from_kuid(user_ns, ace->e_id.uid));
+ xattr_ace++;
+ }
+ return real_size;
+}
+EXPORT_SYMBOL_GPL(richacl_to_xattr);
+
+/*
+ * Fix up the uids and gids in richacl extended attributes in place.
+ */
+static void richacl_fix_xattr_userns(
+ struct user_namespace *to, struct user_namespace *from,
+ void *value, size_t size)
+{
+ struct richacl_xattr *xattr_acl = value;
+ struct richace_xattr *xattr_ace =
+ (struct richace_xattr *)(xattr_acl + 1);
+ unsigned int count;
+
+ if (!value)
+ return;
+ if (size < sizeof(*xattr_acl))
+ return;
+ if (xattr_acl->a_version != cpu_to_le32(RICHACL_XATTR_VERSION))
+ return;
+ size -= sizeof(*xattr_acl);
+ if (size % sizeof(*xattr_ace))
+ return;
+ count = size / sizeof(*xattr_ace);
+ for (; count; count--, xattr_ace++) {
+ if (xattr_ace->e_flags & cpu_to_le16(RICHACE_SPECIAL_WHO))
+ continue;
+ if (xattr_ace->e_flags & cpu_to_le16(RICHACE_IDENTIFIER_GROUP)) {
+ kgid_t gid = make_kgid(from, le32_to_cpu(xattr_ace->e_id));
+ xattr_ace->e_id = cpu_to_le32(from_kgid(to, gid));
+ } else {
+ kuid_t uid = make_kuid(from, le32_to_cpu(xattr_ace->e_id));
+ xattr_ace->e_id = cpu_to_le32(from_kuid(to, uid));
+ }
+ }
+}
+
+void richacl_fix_xattr_from_user(void *value, size_t size)
+{
+ struct user_namespace *user_ns = current_user_ns();
+ if (user_ns == &init_user_ns)
+ return;
+ richacl_fix_xattr_userns(&init_user_ns, user_ns, value, size);
+}
+
+void richacl_fix_xattr_to_user(void *value, size_t size)
+{
+ struct user_namespace *user_ns = current_user_ns();
+ if (user_ns == &init_user_ns)
+ return;
+ richacl_fix_xattr_userns(user_ns, &init_user_ns, value, size);
+}
diff --git a/fs/xattr.c b/fs/xattr.c
index 4ef6985..a09e654 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -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) {
diff --git a/include/linux/richacl_xattr.h b/include/linux/richacl_xattr.h
new file mode 100644
index 0000000..13cf746
--- /dev/null
+++ b/include/linux/richacl_xattr.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2006, 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.
+ */
+
+#ifndef __RICHACL_XATTR_H
+#define __RICHACL_XATTR_H
+
+#include <linux/richacl.h>
+
+#define RICHACL_XATTR "system.richacl"
+
+struct richace_xattr {
+ __le16 e_type;
+ __le16 e_flags;
+ __le32 e_mask;
+ __le32 e_id;
+};
+
+struct richacl_xattr {
+ unsigned char a_version;
+ unsigned char a_flags;
+ __le16 a_unused;
+ __le32 a_owner_mask;
+ __le32 a_group_mask;
+ __le32 a_other_mask;
+};
+
+#define RICHACL_XATTR_VERSION 0
+#define RICHACL_XATTR_MAX_COUNT \
+ ((XATTR_SIZE_MAX - sizeof(struct richacl_xattr)) / \
+ sizeof(struct richace_xattr))
+
+extern struct richacl *richacl_from_xattr(struct user_namespace *, const void *, size_t);
+extern size_t richacl_xattr_size(const struct richacl *);
+extern int richacl_to_xattr(struct user_namespace *, const struct richacl *, void *, size_t);
+
+extern void richacl_fix_xattr_from_user(void *, size_t);
+extern void richacl_fix_xattr_to_user(void *, size_t);
+
+#endif /* __RICHACL_XATTR_H */
diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h
index 1590c49..1996903 100644
--- a/include/uapi/linux/xattr.h
+++ b/include/uapi/linux/xattr.h
@@ -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 */
--
2.1.0


2015-04-24 11:05:19

by Andreas Grünbacher

[permalink] [raw]
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.

- When the ACL4_PROTECTED acl flag in the acl of a file is set, AI will
not modify the acl of the file. This does not affect propagation of
permissions from the file to its children (if the file is a
directory).

Linux does not have a way of creating files without setting the file permission
bits, so all files created inside a directory with ACL4_AUTO_INHERIT set will
also have the ACL4_PROTECTED flag set. This effectively disables Automatic
Inheritance.

Protocols which support creating files without specifying permissions can
explicitly clear the ACL4_PROTECTED flag after creating a file and reset the
file masks to "undo" applying the create mode; see richacl_compute_max_masks().
This is a workaround; a mechanism that would allow a process to indicate to the
kernel to ignore the create mode when there are inherited permissions would fix
this problem.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/richacl_base.c | 10 +++++++++-
fs/richacl_inode.c | 7 ++++++-
include/linux/richacl.h | 25 +++++++++++++++++++++++--
3 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 54cb482..572f1b8 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -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;
}
diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
index 1a5b868..dfbb15a 100644
--- a/fs/richacl_inode.c
+++ b/fs/richacl_inode.c
@@ -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.
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 8a92b89..bcc2b64 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -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);
}

/**
--
2.1.0


2015-04-24 11:05:14

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 16/45] vfs: Cache richacl in struct inode

Cache richacls in struct inode so that this doesn't have to be done
individually in each filesystem. This is similar to POSIX ACLs.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
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(-)

diff --git a/fs/inode.c b/fs/inode.c
index 555fe9c..5272412 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -175,8 +175,11 @@ int inode_init_always(struct super_block *sb, struct inode *inode)
inode->i_private = NULL;
inode->i_mapping = mapping;
INIT_HLIST_HEAD(&inode->i_dentry); /* buggered by rcu freeing */
-#ifdef CONFIG_FS_POSIX_ACL
- inode->i_acl = inode->i_default_acl = ACL_NOT_CACHED;
+#if defined(CONFIG_FS_POSIX_ACL) || defined(CONFIG_FS_RICHACL)
+ inode->i_acl = ACL_NOT_CACHED;
+# if defined(CONFIG_FS_POSIX_ACL)
+ inode->i_default_acl = ACL_NOT_CACHED;
+# endif
#endif

#ifdef CONFIG_FSNOTIFY
@@ -231,11 +234,13 @@ void __destroy_inode(struct inode *inode)
atomic_long_dec(&inode->i_sb->s_remove_count);
}

-#ifdef CONFIG_FS_POSIX_ACL
+#if defined(CONFIG_FS_POSIX_ACL) || defined(CONFIG_FS_RICHACL)
if (inode->i_acl && inode->i_acl != ACL_NOT_CACHED)
put_base_acl(inode->i_acl);
+# if defined(CONFIG_FS_POSIX_ACL)
if (inode->i_default_acl && inode->i_default_acl != ACL_NOT_CACHED)
put_base_acl(inode->i_default_acl);
+# endif
#endif
this_cpu_dec(nr_inodes);
}
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 2fbfec8..ebf96b2 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -38,7 +38,7 @@ struct posix_acl *get_cached_acl(struct inode *inode, int type)
{
struct posix_acl **p = acl_by_type(inode, type);
struct posix_acl *acl = ACCESS_ONCE(*p);
- if (acl) {
+ if (acl && IS_POSIXACL(inode)) {
spin_lock(&inode->i_lock);
acl = *p;
if (acl != ACL_NOT_CACHED)
diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 261ddd2..5273ccb 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -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;
}
diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
index 33e591a..b421f68 100644
--- a/fs/richacl_inode.c
+++ b/fs/richacl_inode.c
@@ -20,6 +20,79 @@
#include <linux/slab.h>
#include <linux/richacl.h>

+struct richacl *get_cached_richacl(struct inode *inode)
+{
+ struct richacl *acl;
+
+ acl = (struct richacl *)ACCESS_ONCE(inode->i_acl);
+ if (acl && IS_RICHACL(inode)) {
+ spin_lock(&inode->i_lock);
+ acl = (struct richacl *)inode->i_acl;
+ if (acl != ACL_NOT_CACHED)
+ acl = richacl_get(acl);
+ spin_unlock(&inode->i_lock);
+ }
+ return acl;
+}
+EXPORT_SYMBOL_GPL(get_cached_richacl);
+
+struct richacl *get_cached_richacl_rcu(struct inode *inode)
+{
+ return (struct richacl *)rcu_dereference(inode->i_acl);
+}
+EXPORT_SYMBOL_GPL(get_cached_richacl_rcu);
+
+void set_cached_richacl(struct inode *inode, struct richacl *acl)
+{
+ struct base_acl *old = NULL;
+ spin_lock(&inode->i_lock);
+ old = inode->i_acl;
+ rcu_assign_pointer(inode->i_acl, &richacl_get(acl)->a_base);
+ spin_unlock(&inode->i_lock);
+ if (old != ACL_NOT_CACHED)
+ put_base_acl(old);
+}
+EXPORT_SYMBOL_GPL(set_cached_richacl);
+
+void forget_cached_richacl(struct inode *inode)
+{
+ struct base_acl *old = NULL;
+ spin_lock(&inode->i_lock);
+ old = inode->i_acl;
+ inode->i_acl = ACL_NOT_CACHED;
+ spin_unlock(&inode->i_lock);
+ if (old != ACL_NOT_CACHED)
+ put_base_acl(old);
+}
+EXPORT_SYMBOL_GPL(forget_cached_richacl);
+
+struct richacl *get_richacl(struct inode *inode)
+{
+ struct richacl *acl;
+
+ acl = get_cached_richacl(inode);
+ if (acl != ACL_NOT_CACHED)
+ return acl;
+
+ if (!IS_RICHACL(inode))
+ return NULL;
+
+ /*
+ * A filesystem can force a ACL callback by just never filling the
+ * ACL cache. But normally you'd fill the cache either at inode
+ * instantiation time, or on the first ->get_richacl call.
+ *
+ * If the filesystem doesn't have a get_richacl() function at all,
+ * we'll just create the negative cache entry.
+ */
+ if (!inode->i_op->get_richacl) {
+ set_cached_richacl(inode, NULL);
+ return NULL;
+ }
+ return inode->i_op->get_richacl(inode);
+}
+EXPORT_SYMBOL_GPL(get_richacl);
+
/**
* richacl_permission - richacl permission check algorithm
* @inode: inode to check
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 6e0da10..7584a63 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -551,6 +551,7 @@ struct base_acl {
atomic_t ba_refcount;
};
struct posix_acl;
+struct richacl;
#define ACL_NOT_CACHED ((void *)(-1))

#define IOP_FASTPERM 0x0001
@@ -569,9 +570,11 @@ struct inode {
kgid_t i_gid;
unsigned int i_flags;

-#if defined(CONFIG_FS_POSIX_ACL)
+#if defined(CONFIG_FS_POSIX_ACL) || defined(CONFIG_FS_RICHACL)
struct base_acl *i_acl;
+# if defined(CONFIG_FS_POSIX_ACL)
struct base_acl *i_default_acl;
+# endif
#endif

const struct inode_operations *i_op;
@@ -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 *);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 5eb198d..6d88ee9 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -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
*/
--
2.1.0


2015-04-24 11:05:17

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 18/45] richacl: Check if an acl is equivalent to a file mode

This function is used to avoid storing richacls if the acl can be computed from
the file permission bits.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/richacl_base.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/richacl.h | 1 +
2 files changed, 49 insertions(+)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 5d45539..db27542 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -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);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index ab9fde1..c6fd0a1 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -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);
--
2.1.0


2015-04-24 11:05:17

by Andreas Grünbacher

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

So far, richacl_equiv_mode() is relatively limited in the types of acl it
considers equivalent to a file mode: it only accepts masked acls with a single
everyone@:rwpxd::allow entry.

Change this to consider all acls equivalent to file modes if they only consist
of owner@, group@, and everyone@ entries and the owner@ permissions do not
depend on whether the owner is a member in the owning group.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/richacl_base.c | 150 ++++++++++++++++++++++++++++++++++++++----------
include/linux/richacl.h | 1 +
2 files changed, 122 insertions(+), 29 deletions(-)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index db27542..54cb482 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -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);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index c6fd0a1..8a92b89 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -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 */
--
2.1.0


2015-04-24 11:05:12

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 15/45] vfs: Cache base_acl objects in inodes

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

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
drivers/staging/lustre/lustre/llite/llite_lib.c | 2 +-
fs/f2fs/acl.c | 4 ++--
fs/inode.c | 4 ++--
fs/posix_acl.c | 18 +++++++++---------
include/linux/fs.h | 22 +++++++++++++++++++---
include/linux/posix_acl.h | 9 ++++-----
include/linux/richacl.h | 2 +-
7 files changed, 38 insertions(+), 23 deletions(-)

diff --git a/drivers/staging/lustre/lustre/llite/llite_lib.c b/drivers/staging/lustre/lustre/llite/llite_lib.c
index 0c1b583..c8cae33 100644
--- a/drivers/staging/lustre/lustre/llite/llite_lib.c
+++ b/drivers/staging/lustre/lustre/llite/llite_lib.c
@@ -1145,7 +1145,7 @@ void ll_clear_inode(struct inode *inode)
}
#ifdef CONFIG_FS_POSIX_ACL
else if (lli->lli_posix_acl) {
- LASSERT(atomic_read(&lli->lli_posix_acl->a_refcount) == 1);
+ LASSERT(atomic_read(&lli->lli_posix_acl->a_base.ba_refcount) == 1);
LASSERT(lli->lli_remote_perms == NULL);
posix_acl_release(lli->lli_posix_acl);
lli->lli_posix_acl = NULL;
diff --git a/fs/f2fs/acl.c b/fs/f2fs/acl.c
index 7422027..ccb2c7c 100644
--- a/fs/f2fs/acl.c
+++ b/fs/f2fs/acl.c
@@ -270,7 +270,7 @@ static struct posix_acl *f2fs_acl_clone(const struct posix_acl *acl,
sizeof(struct posix_acl_entry);
clone = kmemdup(acl, size, flags);
if (clone)
- atomic_set(&clone->a_refcount, 1);
+ atomic_set(&clone->a_base.ba_refcount, 1);
}
return clone;
}
@@ -282,7 +282,7 @@ static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
umode_t mode = *mode_p;
int not_equiv = 0;

- /* assert(atomic_read(acl->a_refcount) == 1); */
+ /* assert(atomic_read(acl->a_base.ba_refcount) == 1); */

FOREACH_ACL_ENTRY(pa, acl, pe) {
switch(pa->e_tag) {
diff --git a/fs/inode.c b/fs/inode.c
index f00b16f..555fe9c 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -233,9 +233,9 @@ void __destroy_inode(struct inode *inode)

#ifdef CONFIG_FS_POSIX_ACL
if (inode->i_acl && inode->i_acl != ACL_NOT_CACHED)
- posix_acl_release(inode->i_acl);
+ put_base_acl(inode->i_acl);
if (inode->i_default_acl && inode->i_default_acl != ACL_NOT_CACHED)
- posix_acl_release(inode->i_default_acl);
+ put_base_acl(inode->i_default_acl);
#endif
this_cpu_dec(nr_inodes);
}
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index efe983e..2fbfec8 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -25,9 +25,9 @@ struct posix_acl **acl_by_type(struct inode *inode, int type)
{
switch (type) {
case ACL_TYPE_ACCESS:
- return &inode->i_acl;
+ return (struct posix_acl **)&inode->i_acl;
case ACL_TYPE_DEFAULT:
- return &inode->i_default_acl;
+ return (struct posix_acl **)&inode->i_default_acl;
default:
BUG();
}
@@ -83,16 +83,16 @@ EXPORT_SYMBOL(forget_cached_acl);

void forget_all_cached_acls(struct inode *inode)
{
- struct posix_acl *old_access, *old_default;
+ struct base_acl *old_access, *old_default;
spin_lock(&inode->i_lock);
old_access = inode->i_acl;
old_default = inode->i_default_acl;
inode->i_acl = inode->i_default_acl = ACL_NOT_CACHED;
spin_unlock(&inode->i_lock);
if (old_access != ACL_NOT_CACHED)
- posix_acl_release(old_access);
+ put_base_acl(old_access);
if (old_default != ACL_NOT_CACHED)
- posix_acl_release(old_default);
+ put_base_acl(old_default);
}
EXPORT_SYMBOL(forget_all_cached_acls);

@@ -129,7 +129,7 @@ EXPORT_SYMBOL(get_acl);
void
posix_acl_init(struct posix_acl *acl, int count)
{
- atomic_set(&acl->a_refcount, 1);
+ atomic_set(&acl->a_base.ba_refcount, 1);
acl->a_count = count;
}
EXPORT_SYMBOL(posix_acl_init);
@@ -163,7 +163,7 @@ posix_acl_clone(const struct posix_acl *acl, gfp_t flags)
sizeof(struct posix_acl_entry);
clone = kmemdup(acl, size, flags);
if (clone)
- atomic_set(&clone->a_refcount, 1);
+ atomic_set(&clone->a_base.ba_refcount, 1);
}
return clone;
}
@@ -385,7 +385,7 @@ static int posix_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
umode_t mode = *mode_p;
int not_equiv = 0;

- /* assert(atomic_read(acl->a_refcount) == 1); */
+ /* assert(atomic_read(acl->a_base.ba_refcount) == 1); */

FOREACH_ACL_ENTRY(pa, acl, pe) {
switch(pa->e_tag) {
@@ -440,7 +440,7 @@ static int __posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode)
struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
struct posix_acl_entry *pa, *pe;

- /* assert(atomic_read(acl->a_refcount) == 1); */
+ /* assert(atomic_read(acl->a_base.ba_refcount) == 1); */

FOREACH_ACL_ENTRY(pa, acl, pe) {
switch(pa->e_tag) {
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 0121047..6e0da10 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -547,6 +547,9 @@ static inline void mapping_allow_writable(struct address_space *mapping)
#define i_size_ordered_init(inode) do { } while (0)
#endif

+struct base_acl {
+ atomic_t ba_refcount;
+};
struct posix_acl;
#define ACL_NOT_CACHED ((void *)(-1))

@@ -566,9 +569,9 @@ struct inode {
kgid_t i_gid;
unsigned int i_flags;

-#ifdef CONFIG_FS_POSIX_ACL
- struct posix_acl *i_acl;
- struct posix_acl *i_default_acl;
+#if defined(CONFIG_FS_POSIX_ACL)
+ struct base_acl *i_acl;
+ struct base_acl *i_default_acl;
#endif

const struct inode_operations *i_op;
@@ -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 */
diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h
index 66cf477..2c46441 100644
--- a/include/linux/posix_acl.h
+++ b/include/linux/posix_acl.h
@@ -43,7 +43,7 @@ struct posix_acl_entry {
};

struct posix_acl {
- atomic_t a_refcount;
+ struct base_acl a_base;
unsigned int a_count;
struct posix_acl_entry a_entries[0];
};
@@ -58,8 +58,7 @@ struct posix_acl {
static inline struct posix_acl *
posix_acl_dup(struct posix_acl *acl)
{
- if (acl)
- atomic_inc(&acl->a_refcount);
+ get_base_acl(&acl->a_base);
return acl;
}

@@ -69,8 +68,8 @@ posix_acl_dup(struct posix_acl *acl)
static inline void
posix_acl_release(struct posix_acl *acl)
{
- if (acl && atomic_dec_and_test(&acl->a_refcount))
- __kfree_rcu((struct rcu_head *)acl, 0);
+ BUILD_BUG_ON(offsetof(struct posix_acl, a_base) != 0);
+ put_base_acl(&acl->a_base);
}


diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index d11e8f3..5eb198d 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -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;
}

--
2.1.0


2015-04-24 11:05:11

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 14/45] richacl: Permission check algorithm

A richacl grants a requested access if the NFSv4 acl in the richacl grants the
requested permissions (according to the NFSv4 permission check algorithm) and
the file mask that applies to the process includes the requested permissions.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/Makefile | 2 +-
fs/richacl_inode.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/richacl.h | 3 ++
3 files changed, 134 insertions(+), 1 deletion(-)
create mode 100644 fs/richacl_inode.c

diff --git a/fs/Makefile b/fs/Makefile
index 8f0a59c..bb96ad7 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -48,7 +48,7 @@ obj-$(CONFIG_SYSCTL) += drop_caches.o

obj-$(CONFIG_FHANDLE) += fhandle.o
obj-$(CONFIG_FS_RICHACL) += richacl.o
-richacl-y := richacl_base.o
+richacl-y := richacl_base.o richacl_inode.o

obj-y += quota/

diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
new file mode 100644
index 0000000..33e591a
--- /dev/null
+++ b/fs/richacl_inode.c
@@ -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);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index a27ff10..d11e8f3 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -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 */
--
2.1.0


2015-04-24 11:05:09

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 13/45] richacl: Update the file masks in chmod()

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

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

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

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/richacl_base.c | 40 ++++++++++++++++++++++++++++++++++++++++
include/linux/richacl.h | 1 +
2 files changed, 41 insertions(+)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 1e14c38..261ddd2 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -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);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index cd0cdaa..a27ff10 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -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 */
--
2.1.0


2015-04-24 11:05:06

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 10/45] richacl: In-memory representation and helper functions

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

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

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

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
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

diff --git a/fs/Makefile b/fs/Makefile
index a88ac48..8f0a59c 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -47,6 +47,8 @@ obj-$(CONFIG_COREDUMP) += coredump.o
obj-$(CONFIG_SYSCTL) += drop_caches.o

obj-$(CONFIG_FHANDLE) += fhandle.o
+obj-$(CONFIG_FS_RICHACL) += richacl.o
+richacl-y := richacl_base.o

obj-y += quota/

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
new file mode 100644
index 0000000..164b902
--- /dev/null
+++ b/fs/richacl_base.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2006, 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>
+
+MODULE_LICENSE("GPL");
+
+/**
+ * richacl_alloc - allocate a richacl
+ * @count: number of entries
+ */
+struct richacl *
+richacl_alloc(int count, gfp_t gfp)
+{
+ size_t size = sizeof(struct richacl) + count * sizeof(struct richace);
+ struct richacl *acl = kzalloc(size, gfp);
+
+ if (acl) {
+ atomic_set(&acl->a_refcount, 1);
+ acl->a_count = count;
+ }
+ return acl;
+}
+EXPORT_SYMBOL_GPL(richacl_alloc);
+
+/**
+ * richacl_clone - create a copy of a richacl
+ */
+struct richacl *
+richacl_clone(const struct richacl *acl, gfp_t gfp)
+{
+ int count = acl->a_count;
+ size_t size = sizeof(struct richacl) + count * sizeof(struct richace);
+ struct richacl *dup = kmalloc(size, gfp);
+
+ if (dup) {
+ memcpy(dup, acl, size);
+ atomic_set(&dup->a_refcount, 1);
+ }
+ return dup;
+}
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
new file mode 100644
index 0000000..1499e7d
--- /dev/null
+++ b/include/linux/richacl.h
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2006, 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.
+ */
+
+#ifndef __RICHACL_H
+#define __RICHACL_H
+
+#define RICHACE_OWNER_SPECIAL_ID 0
+#define RICHACE_GROUP_SPECIAL_ID 1
+#define RICHACE_EVERYONE_SPECIAL_ID 2
+
+struct richace {
+ unsigned short e_type;
+ unsigned short e_flags;
+ unsigned int e_mask;
+ union {
+ kuid_t uid;
+ kgid_t gid;
+ unsigned int special;
+ } e_id;
+};
+
+struct richacl {
+ atomic_t a_refcount;
+ unsigned int a_owner_mask;
+ unsigned int a_group_mask;
+ unsigned int a_other_mask;
+ unsigned short a_count;
+ unsigned short a_flags;
+ struct richace a_entries[0];
+};
+
+#define richacl_for_each_entry(_ace, _acl) \
+ for (_ace = (_acl)->a_entries; \
+ _ace != (_acl)->a_entries + (_acl)->a_count; \
+ _ace++)
+
+#define richacl_for_each_entry_reverse(_ace, _acl) \
+ for (_ace = (_acl)->a_entries + (_acl)->a_count - 1; \
+ _ace != (_acl)->a_entries - 1; \
+ _ace--)
+
+/* a_flag values */
+#define RICHACL_MASKED 0x80
+
+#define RICHACL_VALID_FLAGS ( \
+ RICHACL_MASKED)
+
+/* e_type values */
+#define RICHACE_ACCESS_ALLOWED_ACE_TYPE 0x0000
+#define RICHACE_ACCESS_DENIED_ACE_TYPE 0x0001
+
+/* e_flags bitflags */
+#define RICHACE_FILE_INHERIT_ACE 0x0001
+#define RICHACE_DIRECTORY_INHERIT_ACE 0x0002
+#define RICHACE_NO_PROPAGATE_INHERIT_ACE 0x0004
+#define RICHACE_INHERIT_ONLY_ACE 0x0008
+#define RICHACE_IDENTIFIER_GROUP 0x0040
+#define RICHACE_SPECIAL_WHO 0x4000
+
+#define RICHACE_VALID_FLAGS ( \
+ RICHACE_FILE_INHERIT_ACE | \
+ RICHACE_DIRECTORY_INHERIT_ACE | \
+ RICHACE_NO_PROPAGATE_INHERIT_ACE | \
+ RICHACE_INHERIT_ONLY_ACE | \
+ RICHACE_IDENTIFIER_GROUP | \
+ RICHACE_SPECIAL_WHO)
+
+/* e_mask bitflags */
+#define RICHACE_READ_DATA 0x00000001
+#define RICHACE_LIST_DIRECTORY 0x00000001
+#define RICHACE_WRITE_DATA 0x00000002
+#define RICHACE_ADD_FILE 0x00000002
+#define RICHACE_APPEND_DATA 0x00000004
+#define RICHACE_ADD_SUBDIRECTORY 0x00000004
+#define RICHACE_READ_NAMED_ATTRS 0x00000008
+#define RICHACE_WRITE_NAMED_ATTRS 0x00000010
+#define RICHACE_EXECUTE 0x00000020
+#define RICHACE_DELETE_CHILD 0x00000040
+#define RICHACE_READ_ATTRIBUTES 0x00000080
+#define RICHACE_WRITE_ATTRIBUTES 0x00000100
+#define RICHACE_WRITE_RETENTION 0x00000200
+#define RICHACE_WRITE_RETENTION_HOLD 0x00000400
+#define RICHACE_DELETE 0x00010000
+#define RICHACE_READ_ACL 0x00020000
+#define RICHACE_WRITE_ACL 0x00040000
+#define RICHACE_WRITE_OWNER 0x00080000
+#define RICHACE_SYNCHRONIZE 0x00100000
+
+/* Valid RICHACE_* flags for directories and non-directories */
+#define RICHACE_VALID_MASK ( \
+ RICHACE_READ_DATA | RICHACE_LIST_DIRECTORY | \
+ RICHACE_WRITE_DATA | RICHACE_ADD_FILE | \
+ RICHACE_APPEND_DATA | RICHACE_ADD_SUBDIRECTORY | \
+ RICHACE_READ_NAMED_ATTRS | \
+ RICHACE_WRITE_NAMED_ATTRS | \
+ RICHACE_EXECUTE | \
+ RICHACE_DELETE_CHILD | \
+ RICHACE_READ_ATTRIBUTES | \
+ RICHACE_WRITE_ATTRIBUTES | \
+ RICHACE_WRITE_RETENTION | \
+ RICHACE_WRITE_RETENTION_HOLD | \
+ RICHACE_DELETE | \
+ RICHACE_READ_ACL | \
+ RICHACE_WRITE_ACL | \
+ RICHACE_WRITE_OWNER | \
+ RICHACE_SYNCHRONIZE)
+
+/**
+ * richacl_get - grab another reference to a richacl handle
+ */
+static inline struct richacl *
+richacl_get(struct richacl *acl)
+{
+ if (acl)
+ atomic_inc(&acl->a_refcount);
+ return acl;
+}
+
+/**
+ * richacl_put - free a richacl handle
+ */
+static inline void
+richacl_put(struct richacl *acl)
+{
+ if (acl && atomic_dec_and_test(&acl->a_refcount))
+ kfree(acl);
+}
+
+/**
+ * richace_is_owner - check if @ace is an OWNER@ entry
+ */
+static inline bool
+richace_is_owner(const struct richace *ace)
+{
+ return (ace->e_flags & RICHACE_SPECIAL_WHO) &&
+ ace->e_id.special == RICHACE_OWNER_SPECIAL_ID;
+}
+
+/**
+ * richace_is_group - check if @ace is a GROUP@ entry
+ */
+static inline bool
+richace_is_group(const struct richace *ace)
+{
+ return (ace->e_flags & RICHACE_SPECIAL_WHO) &&
+ ace->e_id.special == RICHACE_GROUP_SPECIAL_ID;
+}
+
+/**
+ * richace_is_everyone - check if @ace is an EVERYONE@ entry
+ */
+static inline bool
+richace_is_everyone(const struct richace *ace)
+{
+ return (ace->e_flags & RICHACE_SPECIAL_WHO) &&
+ ace->e_id.special == RICHACE_EVERYONE_SPECIAL_ID;
+}
+
+/**
+ * richace_is_unix_id - check if @ace applies to a specific uid or gid
+ */
+static inline bool
+richace_is_unix_id(const struct richace *ace)
+{
+ return !(ace->e_flags & RICHACE_SPECIAL_WHO);
+}
+
+/**
+ * richace_is_inherit_only - check if @ace is for inheritance only
+ *
+ * ACEs with the %RICHACE_INHERIT_ONLY_ACE flag set have no effect during
+ * permission checking.
+ */
+static inline bool
+richace_is_inherit_only(const struct richace *ace)
+{
+ return ace->e_flags & RICHACE_INHERIT_ONLY_ACE;
+}
+
+/**
+ * richace_is_inheritable - check if @ace is inheritable
+ */
+static inline bool
+richace_is_inheritable(const struct richace *ace)
+{
+ return ace->e_flags & (RICHACE_FILE_INHERIT_ACE |
+ RICHACE_DIRECTORY_INHERIT_ACE);
+}
+
+/**
+ * richace_clear_inheritance_flags - clear all inheritance flags in @ace
+ */
+static inline void
+richace_clear_inheritance_flags(struct richace *ace)
+{
+ ace->e_flags &= ~(RICHACE_FILE_INHERIT_ACE |
+ RICHACE_DIRECTORY_INHERIT_ACE |
+ RICHACE_NO_PROPAGATE_INHERIT_ACE |
+ RICHACE_INHERIT_ONLY_ACE);
+}
+
+/**
+ * richace_is_allow - check if @ace is an %ALLOW type entry
+ */
+static inline bool
+richace_is_allow(const struct richace *ace)
+{
+ return ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+}
+
+/**
+ * richace_is_deny - check if @ace is a %DENY type entry
+ */
+static inline bool
+richace_is_deny(const struct richace *ace)
+{
+ return ace->e_type == RICHACE_ACCESS_DENIED_ACE_TYPE;
+}
+
+/**
+ * richace_is_same_identifier - are both identifiers the same?
+ */
+static inline bool
+richace_is_same_identifier(const struct richace *a, const struct richace *b)
+{
+ return !((a->e_flags ^ b->e_flags) &
+ (RICHACE_SPECIAL_WHO | RICHACE_IDENTIFIER_GROUP)) &&
+ !memcmp(&a->e_id, &b->e_id, sizeof(a->e_id));
+}
+
+extern struct richacl *richacl_alloc(int, gfp_t);
+extern struct richacl *richacl_clone(const struct richacl *, gfp_t);
+
+#endif /* __RICHACL_H */
--
2.1.0


2015-04-24 11:05:07

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 11/45] richacl: Permission mapping functions

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

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/richacl_base.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/richacl.h | 46 +++++++++++++++++++
2 files changed, 162 insertions(+)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 164b902..978f221 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -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);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 1499e7d..d19843b 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -118,6 +118,49 @@ struct richacl {
RICHACE_WRITE_OWNER | \
RICHACE_SYNCHRONIZE)

+/*
+ * The POSIX permissions are supersets of the following NFSv4 permissions:
+ *
+ * - MAY_READ maps to READ_DATA or LIST_DIRECTORY, depending on the type
+ * of the file system object.
+ *
+ * - MAY_WRITE maps to WRITE_DATA or RICHACE_APPEND_DATA for files, and to
+ * ADD_FILE, RICHACE_ADD_SUBDIRECTORY, or RICHACE_DELETE_CHILD for directories.
+ *
+ * - MAY_EXECUTE maps to RICHACE_EXECUTE.
+ *
+ * (Some of these NFSv4 permissions have the same bit values.)
+ */
+#define RICHACE_POSIX_MODE_READ ( \
+ RICHACE_READ_DATA | \
+ RICHACE_LIST_DIRECTORY)
+#define RICHACE_POSIX_MODE_WRITE ( \
+ RICHACE_WRITE_DATA | \
+ RICHACE_ADD_FILE | \
+ RICHACE_APPEND_DATA | \
+ RICHACE_ADD_SUBDIRECTORY | \
+ RICHACE_DELETE_CHILD)
+#define RICHACE_POSIX_MODE_EXEC RICHACE_EXECUTE
+#define RICHACE_POSIX_MODE_ALL ( \
+ RICHACE_POSIX_MODE_READ | \
+ RICHACE_POSIX_MODE_WRITE | \
+ RICHACE_POSIX_MODE_EXEC)
+/*
+ * These permissions are always allowed
+ * no matter what the acl says.
+ */
+#define RICHACE_POSIX_ALWAYS_ALLOWED ( \
+ RICHACE_SYNCHRONIZE | \
+ RICHACE_READ_ATTRIBUTES | \
+ RICHACE_READ_ACL)
+/*
+ * The owner is implicitly granted
+ * these permissions under POSIX.
+ */
+#define RICHACE_POSIX_OWNER_ALLOWED ( \
+ RICHACE_WRITE_ATTRIBUTES | \
+ RICHACE_WRITE_OWNER | \
+ RICHACE_WRITE_ACL)
/**
* richacl_get - grab another reference to a richacl handle
*/
@@ -243,5 +286,8 @@ richace_is_same_identifier(const struct richace *a, const struct richace *b)

extern struct richacl *richacl_alloc(int, gfp_t);
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);

#endif /* __RICHACL_H */
--
2.1.0


2015-04-24 11:05:08

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 12/45] richacl: Compute maximum file masks from an acl

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

Signed-off-by: Andreas Gruenbacher <[email protected]>
Reviewed-by: J. Bruce Fields <[email protected]>
---
fs/richacl_base.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/richacl.h | 1 +
2 files changed, 155 insertions(+)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 978f221..1e14c38 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -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);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index d19843b..cd0cdaa 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -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 */
--
2.1.0


2015-04-24 11:05:00

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 06/45] vfs: Add MAY_CREATE_FILE and MAY_CREATE_DIR permission flags

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

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

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/namei.c | 42 ++++++++++++++++++++++++------------------
include/linux/fs.h | 2 ++
2 files changed, 26 insertions(+), 18 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 0ba4bbc..a8bc030 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -454,7 +454,8 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
* this, letting us set arbitrary permissions for filesystem access without
* changing the "normal" UIDs which are used for other things.
*
- * When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
+ * When checking for MAY_APPEND, MAY_CREATE_FILE, MAY_CREATE_DIR,
+ * MAY_WRITE must also be set in @mask.
*/
int inode_permission(struct inode *inode, int mask)
{
@@ -2447,10 +2448,11 @@ EXPORT_SYMBOL(__check_sticky);
* 10. We don't allow removal of NFS sillyrenamed files; it's handled by
* nfs_async_unlink().
*/
-static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
+static int may_delete(struct inode *dir, struct dentry *victim,
+ bool isdir, bool replace)
{
struct inode *inode = victim->d_inode;
- int error;
+ int error, mask = MAY_WRITE | MAY_EXEC;

if (d_is_negative(victim))
return -ENOENT;
@@ -2459,7 +2461,9 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
BUG_ON(victim->d_parent->d_inode != dir);
audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);

- error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ if (replace)
+ mask |= isdir ? MAY_CREATE_DIR : MAY_CREATE_FILE;
+ error = inode_permission(dir, mask);
if (error)
return error;
if (IS_APPEND(dir))
@@ -2490,14 +2494,16 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
* 3. We should have write and exec permissions on dir
* 4. We can't do it if dir is immutable (done in permission())
*/
-static inline int may_create(struct inode *dir, struct dentry *child)
+static inline int may_create(struct inode *dir, struct dentry *child, bool isdir)
{
+ int mask = isdir ? MAY_CREATE_DIR : MAY_CREATE_FILE;
+
audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
if (child->d_inode)
return -EEXIST;
if (IS_DEADDIR(dir))
return -ENOENT;
- return inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ return inode_permission(dir, MAY_WRITE | MAY_EXEC | mask);
}

/*
@@ -2547,7 +2553,7 @@ EXPORT_SYMBOL(unlock_rename);
int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
bool want_excl)
{
- int error = may_create(dir, dentry);
+ int error = may_create(dir, dentry, false);
if (error)
return error;

@@ -3422,7 +3428,7 @@ EXPORT_SYMBOL(user_path_create);

int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
{
- int error = may_create(dir, dentry);
+ int error = may_create(dir, dentry, false);

if (error)
return error;
@@ -3514,7 +3520,7 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d

int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
- int error = may_create(dir, dentry);
+ int error = may_create(dir, dentry, true);
unsigned max_links = dir->i_sb->s_max_links;

if (error)
@@ -3595,7 +3601,7 @@ EXPORT_SYMBOL(dentry_unhash);

int vfs_rmdir(struct inode *dir, struct dentry *dentry)
{
- int error = may_delete(dir, dentry, 1);
+ int error = may_delete(dir, dentry, true, false);

if (error)
return error;
@@ -3715,7 +3721,7 @@ SYSCALL_DEFINE1(rmdir, const char __user *, pathname)
int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
{
struct inode *target = dentry->d_inode;
- int error = may_delete(dir, dentry, 0);
+ int error = may_delete(dir, dentry, false, false);

if (error)
return error;
@@ -3847,7 +3853,7 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname)

int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
{
- int error = may_create(dir, dentry);
+ int error = may_create(dir, dentry, false);

if (error)
return error;
@@ -3930,7 +3936,7 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
if (!inode)
return -ENOENT;

- error = may_create(dir, new_dentry);
+ error = may_create(dir, new_dentry, false);
if (error)
return error;

@@ -4118,19 +4124,19 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (source == target)
return 0;

- error = may_delete(old_dir, old_dentry, is_dir);
+ error = may_delete(old_dir, old_dentry, is_dir, false);
if (error)
return error;

if (!target) {
- error = may_create(new_dir, new_dentry);
+ error = may_create(new_dir, new_dentry, is_dir);
} else {
new_is_dir = d_is_dir(new_dentry);

if (!(flags & RENAME_EXCHANGE))
- error = may_delete(new_dir, new_dentry, is_dir);
+ error = may_delete(new_dir, new_dentry, is_dir, true);
else
- error = may_delete(new_dir, new_dentry, new_is_dir);
+ error = may_delete(new_dir, new_dentry, new_is_dir, true);
}
if (error)
return error;
@@ -4394,7 +4400,7 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna

int vfs_whiteout(struct inode *dir, struct dentry *dentry)
{
- int error = may_create(dir, dentry);
+ int error = may_create(dir, dentry, false);
if (error)
return error;

diff --git a/include/linux/fs.h b/include/linux/fs.h
index b64a316..2cced7c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -80,6 +80,8 @@ typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
#define MAY_CHDIR 0x00000040
/* called from RCU mode, don't block */
#define MAY_NOT_BLOCK 0x00000080
+#define MAY_CREATE_FILE 0x00000100
+#define MAY_CREATE_DIR 0x00000200

/*
* flags in file.f_mode. Note that FMODE_READ and FMODE_WRITE must correspond
--
2.1.0


2015-04-24 11:05:01

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 07/45] vfs: Add MAY_DELETE_SELF and MAY_DELETE_CHILD permission flags

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

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

The MAY_DELETE_SELF permission does not override the sticky directory check.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/namei.c | 15 +++++++++++----
include/linux/fs.h | 2 ++
2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index a8bc030..a8d1674 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -455,7 +455,7 @@ static int sb_permission(struct super_block *sb, struct inode *inode, int mask)
* changing the "normal" UIDs which are used for other things.
*
* When checking for MAY_APPEND, MAY_CREATE_FILE, MAY_CREATE_DIR,
- * MAY_WRITE must also be set in @mask.
+ * MAY_DELETE_CHILD, MAY_DELETE_SELF, MAY_WRITE must also be set in @mask.
*/
int inode_permission(struct inode *inode, int mask)
{
@@ -2452,7 +2452,7 @@ static int may_delete(struct inode *dir, struct dentry *victim,
bool isdir, bool replace)
{
struct inode *inode = victim->d_inode;
- int error, mask = MAY_WRITE | MAY_EXEC;
+ int error, mask = MAY_EXEC;

if (d_is_negative(victim))
return -ENOENT;
@@ -2462,8 +2462,15 @@ static int may_delete(struct inode *dir, struct dentry *victim,
audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);

if (replace)
- mask |= isdir ? MAY_CREATE_DIR : MAY_CREATE_FILE;
- error = inode_permission(dir, mask);
+ mask |= MAY_WRITE | (isdir ? MAY_CREATE_DIR : MAY_CREATE_FILE);
+ error = inode_permission(dir, mask | MAY_WRITE | MAY_DELETE_CHILD);
+ if (error && IS_RICHACL(inode)) {
+ /* Deleting is also permitted with MAY_EXEC on the directory
+ * and MAY_DELETE_SELF on the inode. */
+ if (!inode_permission(inode, MAY_DELETE_SELF) &&
+ !inode_permission(dir, mask))
+ error = 0;
+ }
if (error)
return error;
if (IS_APPEND(dir))
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 2cced7c..5701f90 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -82,6 +82,8 @@ typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
#define MAY_NOT_BLOCK 0x00000080
#define MAY_CREATE_FILE 0x00000100
#define MAY_CREATE_DIR 0x00000200
+#define MAY_DELETE_CHILD 0x00000400
+#define MAY_DELETE_SELF 0x00000800

/*
* flags in file.f_mode. Note that FMODE_READ and FMODE_WRITE must correspond
--
2.1.0


2015-04-24 11:05:04

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 09/45] vfs: Add permission flags for setting file attributes

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

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/attr.c | 79 +++++++++++++++++++++++++++++++++++++++++++++---------
include/linux/fs.h | 3 +++
2 files changed, 70 insertions(+), 12 deletions(-)

diff --git a/fs/attr.c b/fs/attr.c
index 328be71..85483e0 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -17,6 +17,65 @@
#include <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;
}

diff --git a/include/linux/fs.h b/include/linux/fs.h
index 8347f81..0121047 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -84,6 +84,9 @@ typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
#define MAY_CREATE_DIR 0x00000200
#define MAY_DELETE_CHILD 0x00000400
#define MAY_DELETE_SELF 0x00000800
+#define MAY_TAKE_OWNERSHIP 0x00001000
+#define MAY_CHMOD 0x00002000
+#define MAY_SET_TIMES 0x00004000

/*
* flags in file.f_mode. Note that FMODE_READ and FMODE_WRITE must correspond
--
2.1.0


2015-04-24 11:05:03

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 08/45] vfs: Make the inode passed to inode_change_ok non-const

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

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/attr.c | 2 +-
include/linux/fs.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/attr.c b/fs/attr.c
index 6530ced..328be71 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -28,7 +28,7 @@
* Should be called as the first thing in ->setattr implementations,
* possibly after taking additional locks.
*/
-int inode_change_ok(const struct inode *inode, struct iattr *attr)
+int inode_change_ok(struct inode *inode, struct iattr *attr)
{
unsigned int ia_valid = attr->ia_valid;

diff --git a/include/linux/fs.h b/include/linux/fs.h
index 5701f90..8347f81 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -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);

--
2.1.0


2015-04-24 11:04:59

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 05/45] vfs: Add IS_ACL() and IS_RICHACL() tests

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

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/Kconfig | 3 +++
fs/namei.c | 8 ++++----
include/linux/fs.h | 12 ++++++++++++
include/uapi/linux/fs.h | 3 ++-
4 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/fs/Kconfig b/fs/Kconfig
index ec35851..8b84f99 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -58,6 +58,9 @@ endif # BLOCK
config FS_POSIX_ACL
def_bool n

+config FS_RICHACL
+ def_bool n
+
config EXPORTFS
tristate

diff --git a/fs/namei.c b/fs/namei.c
index c83145a..0ba4bbc 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2696,7 +2696,7 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
}

mode = op->mode;
- if ((open_flag & O_CREAT) && !IS_POSIXACL(dir))
+ if ((open_flag & O_CREAT) && !IS_ACL(dir))
mode &= ~current_umask();

excl = (open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT);
@@ -2880,7 +2880,7 @@ static int lookup_open(struct nameidata *nd, struct path *path,
/* Negative dentry, just create the file */
if (!dentry->d_inode && (op->open_flag & O_CREAT)) {
umode_t mode = op->mode;
- if (!IS_POSIXACL(dir->d_inode))
+ if (!IS_ACL(dir->d_inode))
mode &= ~current_umask();
/*
* This write is needed to ensure that a
@@ -3481,7 +3481,7 @@ retry:
if (IS_ERR(dentry))
return PTR_ERR(dentry);

- if (!IS_POSIXACL(path.dentry->d_inode))
+ if (!IS_ACL(path.dentry->d_inode))
mode &= ~current_umask();
error = security_path_mknod(&path, dentry, mode, dev);
if (error)
@@ -3550,7 +3550,7 @@ retry:
if (IS_ERR(dentry))
return PTR_ERR(dentry);

- if (!IS_POSIXACL(path.dentry->d_inode))
+ if (!IS_ACL(path.dentry->d_inode))
mode &= ~current_umask();
error = security_path_mkdir(&path, dentry, mode);
if (!error)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 52cc449..b64a316 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -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,
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index 9b964a5..6ac6bc9 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -81,7 +81,7 @@ struct inodes_stat_t {
#define MS_VERBOSE 32768 /* War is peace. Verbosity is silence.
MS_VERBOSE is deprecated. */
#define MS_SILENT 32768
-#define MS_POSIXACL (1<<16) /* VFS does not apply the umask */
+#define MS_POSIXACL (1<<16) /* Supports POSIX ACLs */
#define MS_UNBINDABLE (1<<17) /* change to unbindable */
#define MS_PRIVATE (1<<18) /* change to private */
#define MS_SLAVE (1<<19) /* change to slave */
@@ -91,6 +91,7 @@ struct inodes_stat_t {
#define MS_I_VERSION (1<<23) /* Update inode I_version field */
#define MS_STRICTATIME (1<<24) /* Always perform atime updates */
#define MS_LAZYTIME (1<<25) /* Update the on-disk [acm]times lazily */
+#define MS_RICHACL (1<<26) /* Supports richacls */

/* These sb flags are internal to the kernel */
#define MS_NOSEC (1<<28)
--
2.1.0


2015-04-24 11:04:57

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 04/45] vfs: Shrink struct posix_acl

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

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/posix_acl.c | 5 +++--
include/linux/posix_acl.h | 7 ++-----
2 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 3a48bb7..efe983e 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -140,8 +140,9 @@ EXPORT_SYMBOL(posix_acl_init);
struct posix_acl *
posix_acl_alloc(int count, gfp_t flags)
{
- const size_t size = sizeof(struct posix_acl) +
- count * sizeof(struct posix_acl_entry);
+ const size_t size = max(sizeof(struct rcu_head),
+ sizeof(struct posix_acl) +
+ count * sizeof(struct posix_acl_entry));
struct posix_acl *acl = kmalloc(size, flags);
if (acl)
posix_acl_init(acl, count);
diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h
index 3e96a6a..66cf477 100644
--- a/include/linux/posix_acl.h
+++ b/include/linux/posix_acl.h
@@ -43,10 +43,7 @@ struct posix_acl_entry {
};

struct posix_acl {
- union {
- atomic_t a_refcount;
- struct rcu_head a_rcu;
- };
+ atomic_t a_refcount;
unsigned int a_count;
struct posix_acl_entry a_entries[0];
};
@@ -73,7 +70,7 @@ static inline void
posix_acl_release(struct posix_acl *acl)
{
if (acl && atomic_dec_and_test(&acl->a_refcount))
- kfree_rcu(acl, a_rcu);
+ __kfree_rcu((struct rcu_head *)acl, 0);
}


--
2.1.0


2015-04-24 11:04:52

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 01/45] vfs: Minor documentation fix

The check_acl inode operation and the IPERM_FLAG_RCU are long gone.
Document what get_acl does instead.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
Documentation/filesystems/porting | 8 ++++----
Documentation/filesystems/vfs.txt | 3 +++
2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting
index fa2db08..d6f9ab4 100644
--- a/Documentation/filesystems/porting
+++ b/Documentation/filesystems/porting
@@ -379,10 +379,10 @@ may now be called in rcu-walk mode (nd->flags & LOOKUP_RCU). -ECHILD should be
returned if the filesystem cannot handle rcu-walk. See
Documentation/filesystems/vfs.txt for more details.

- permission and check_acl are inode permission checks that are called
-on many or all directory inodes on the way down a path walk (to check for
-exec permission). These must now be rcu-walk aware (flags & IPERM_FLAG_RCU).
-See Documentation/filesystems/vfs.txt for more details.
+ permission is an inode permission check that is called on many or all
+directory inodes on the way down a path walk (to check for exec permission). It
+must now be rcu-walk aware (mask & MAY_NOT_BLOCK). See
+Documentation/filesystems/vfs.txt for more details.

--
[mandatory]
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt
index 966b228..700cdf6 100644
--- a/Documentation/filesystems/vfs.txt
+++ b/Documentation/filesystems/vfs.txt
@@ -457,6 +457,9 @@ otherwise noted.
If a situation is encountered that rcu-walk cannot handle, return
-ECHILD and it will be called again in ref-walk mode.

+ get_acl: called by the VFS to get the posix acl of an inode. Called during
+ permission checks. The returned acl is cached in the inode.
+
setattr: called by the VFS to set attributes for a file. This method
is called by chmod(2) and related system calls.

--
2.1.0


2015-04-24 11:04:55

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 03/45] nfsd: Checking for acl support does not require fetching any acls

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.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
fs/nfsd/nfs4xdr.c | 25 ++++++++++---------------
1 file changed, 10 insertions(+), 15 deletions(-)

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 5fb7e78..e2d602d 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -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) {
--
2.1.0


2015-04-24 11:04:54

by Andreas Grünbacher

[permalink] [raw]
Subject: [RFC v3 02/45] uapi: Remove kernel internal declaration

The enum nfs4_acl_whotype is only used in nfs4d's internal nfs4 acl
representation. No longer expose it to user space.

Signed-off-by: Andreas Gruenbacher <[email protected]>
---
include/linux/nfs4.h | 7 +++++++
include/uapi/linux/nfs4.h | 7 -------
2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index ed43cb7..32201c2 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -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;
diff --git a/include/uapi/linux/nfs4.h b/include/uapi/linux/nfs4.h
index 35f5f4c..adc0aff 100644
--- a/include/uapi/linux/nfs4.h
+++ b/include/uapi/linux/nfs4.h
@@ -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 */

/*
--
2.1.0


2015-04-25 07:56:05

by Rasmus Villemoes

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

On Fri, Apr 24 2015, Andreas Gruenbacher <[email protected]> wrote:

> There is a hole in struct posix_acl because its struct rcu_head member is too
> large; at least on on 64-bit architectures, the hole cannot be closed by
> changing the definition of struct posix_acl. So instead, remove the struct
> rcu_head member from struct posix_acl, make sure that acls are always big
> enough to fit a struct rcu_head, and cast to struct rcu_head * when disposing
> of an acl.
>
> Signed-off-by: Andreas Gruenbacher <[email protected]>
> ---
> fs/posix_acl.c | 5 +++--
> include/linux/posix_acl.h | 7 ++-----
> 2 files changed, 5 insertions(+), 7 deletions(-)
>
> diff --git a/fs/posix_acl.c b/fs/posix_acl.c
> index 3a48bb7..efe983e 100644
> --- a/fs/posix_acl.c
> +++ b/fs/posix_acl.c
> @@ -140,8 +140,9 @@ EXPORT_SYMBOL(posix_acl_init);
> struct posix_acl *
> posix_acl_alloc(int count, gfp_t flags)
> {
> - const size_t size = sizeof(struct posix_acl) +
> - count * sizeof(struct posix_acl_entry);
> + const size_t size = max(sizeof(struct rcu_head),
> + sizeof(struct posix_acl) +
> + count * sizeof(struct posix_acl_entry));
> struct posix_acl *acl = kmalloc(size, flags);
> if (acl)
> posix_acl_init(acl, count);
> diff --git a/include/linux/posix_acl.h b/include/linux/posix_acl.h
> index 3e96a6a..66cf477 100644
> --- a/include/linux/posix_acl.h
> +++ b/include/linux/posix_acl.h
> @@ -43,10 +43,7 @@ struct posix_acl_entry {
> };
>
> struct posix_acl {
> - union {
> - atomic_t a_refcount;
> - struct rcu_head a_rcu;
> - };
> + atomic_t a_refcount;
> unsigned int a_count;
> struct posix_acl_entry a_entries[0];
> };
> @@ -73,7 +70,7 @@ static inline void
> posix_acl_release(struct posix_acl *acl)
> {
> if (acl && atomic_dec_and_test(&acl->a_refcount))
> - kfree_rcu(acl, a_rcu);
> + __kfree_rcu((struct rcu_head *)acl, 0);
> }

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

2015-04-27 10:27:03

by Andreas Grünbacher

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

2015-04-25 9:56 GMT+02:00 Rasmus Villemoes <[email protected]>:
> 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

2015-05-12 21:44:05

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [RFC v3 12/45] richacl: Compute maximum file masks from an acl

On Fri, Apr 24, 2015 at 01:04:09PM +0200, Andreas Gruenbacher wrote:
> 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:

> + /*
> + * @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.

2015-05-13 18:14:04

by Frank Filz

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



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

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

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

> Signed-off-by: Andreas Gruenbacher <[email protected]>
> ---
> fs/richacl_base.c | 10 +++++++++-
> fs/richacl_inode.c | 7 ++++++-
> include/linux/richacl.h | 25 +++++++++++++++++++++++--
> 3 files changed, 38 insertions(+), 4 deletions(-)
>
> diff --git a/fs/richacl_base.c b/fs/richacl_base.c index 54cb482..572f1b8
> 100644
> --- a/fs/richacl_base.c
> +++ b/fs/richacl_base.c
> @@ -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;
> }
> diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c index
1a5b868..dfbb15a
> 100644
> --- a/fs/richacl_inode.c
> +++ b/fs/richacl_inode.c
> @@ -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.
> diff --git a/include/linux/richacl.h b/include/linux/richacl.h index
> 8a92b89..bcc2b64 100644
> --- a/include/linux/richacl.h
> +++ b/include/linux/richacl.h
> @@ -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);
> }
>
> /**
> --
> 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


2015-05-13 20:22:21

by Andreas Grünbacher

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

2015-05-13 20:01 GMT+02:00 Frank Filz <[email protected]>:
> You might want to edit your commit message to use RICHACL_ instead of ACL4_
> constants...

Indeed, thanks.

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

> 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

2015-05-13 20:34:44

by Jeremy Allison

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

On Wed, May 13, 2015 at 10:22:21PM +0200, Andreas Gr?nbacher wrote:
>
> 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 :-).

2015-05-13 20:38:54

by Frank Filz

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



> -----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]>:
> > You might want to edit your commit message to use RICHACL_ instead of
> > ACL4_ constants...
>
> Indeed, thanks.
>
> >> 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.
> >
> > 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.
>
> > 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


2015-05-13 20:47:46

by Andreas Grünbacher

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

2015-05-13 22:28 GMT+02:00 Jeremy Allison <[email protected]>:
> On Wed, May 13, 2015 at 10:22:21PM +0200, Andreas Grünbacher wrote:
>>
>> 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

2015-05-13 20:55:41

by Jeremy Allison

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

On Wed, May 13, 2015 at 10:47:44PM +0200, Andreas Gr?nbacher wrote:
> 2015-05-13 22:28 GMT+02:00 Jeremy Allison <[email protected]>:
> > On Wed, May 13, 2015 at 10:22:21PM +0200, Andreas Gr?nbacher wrote:
> >>
> >> 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?

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.

2015-05-13 21:05:45

by Andreas Grünbacher

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

2015-05-13 22:38 GMT+02:00 Frank Filz <[email protected]>:
> 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-----.

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

> 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

2015-05-13 21:15:50

by Andreas Grünbacher

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

2015-05-13 22:55 GMT+02:00 Jeremy Allison <[email protected]>:
> 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.

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

> 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

2015-05-13 22:56:07

by Frank Filz

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



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


2015-05-13 23:52:53

by Andreas Grünbacher

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

2015-05-14 0:56 GMT+02:00 Frank Filz <[email protected]>:
> 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

2015-05-14 15:09:26

by J. Bruce Fields

[permalink] [raw]

2015-05-14 19:28:07

by J. Bruce Fields

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

On Fri, Apr 24, 2015 at 01:04:16PM +0200, Andreas Gruenbacher wrote:
> So far, richacl_equiv_mode() is relatively limited in the types of acl it
> considers equivalent to a file mode: it only accepts masked acls with a single
> everyone@:rwpxd::allow entry.
>
> Change this to consider all acls equivalent to file modes if they only consist
> of owner@, group@, and everyone@ entries and the owner@ permissions do not
> depend on whether the owner is a member in the owning group.
>
> Signed-off-by: Andreas Gruenbacher <[email protected]>
> ---
> fs/richacl_base.c | 150 ++++++++++++++++++++++++++++++++++++++----------
> include/linux/richacl.h | 1 +
> 2 files changed, 122 insertions(+), 29 deletions(-)
>
> diff --git a/fs/richacl_base.c b/fs/richacl_base.c
> index db27542..54cb482 100644
> --- a/fs/richacl_base.c
> +++ b/fs/richacl_base.c
> @@ -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;
> +

I'm having trouble following this code. Some comments just to see if I
can figure it out:

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

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


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

> + }
> + 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;

This last else clause is redundant with the first RICHACE_SPECIAL_WHO
check, right? Not that I necessarily object.

--b.

> + }
> +
> + 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);
> diff --git a/include/linux/richacl.h b/include/linux/richacl.h
> index c6fd0a1..8a92b89 100644
> --- a/include/linux/richacl.h
> +++ b/include/linux/richacl.h
> @@ -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 */
> --
> 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

2015-05-15 20:04:33

by J. Bruce Fields

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

On Thu, May 14, 2015 at 03:28:06PM -0400, bfields wrote:
> On Fri, Apr 24, 2015 at 01:04:16PM +0200, Andreas Gruenbacher wrote:
> > So far, richacl_equiv_mode() is relatively limited in the types of acl it
> > considers equivalent to a file mode: it only accepts masked acls with a single
> > everyone@:rwpxd::allow entry.
> >
> > Change this to consider all acls equivalent to file modes if they only consist
> > of owner@, group@, and everyone@ entries and the owner@ permissions do not
> > depend on whether the owner is a member in the owning group.
> >
> > Signed-off-by: Andreas Gruenbacher <[email protected]>
> > ---
> > fs/richacl_base.c | 150 ++++++++++++++++++++++++++++++++++++++----------
> > include/linux/richacl.h | 1 +
> > 2 files changed, 122 insertions(+), 29 deletions(-)
> >
> > diff --git a/fs/richacl_base.c b/fs/richacl_base.c
> > index db27542..54cb482 100644
> > --- a/fs/richacl_base.c
> > +++ b/fs/richacl_base.c
> > @@ -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;
> > +
>
> I'm having trouble following this code. Some comments just to see if I
> can figure it out:
>
> > + 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".

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

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.

>
> which is equivalent to 007? But some overkill's OK.)
>
>
> > + 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.

>
> > + }
> > + 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;
>
> This last else clause is redundant with the first RICHACE_SPECIAL_WHO
> check, right? Not that I necessarily object.
>
> --b.
>
> > + }
> > +
> > + 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);
> > diff --git a/include/linux/richacl.h b/include/linux/richacl.h
> > index c6fd0a1..8a92b89 100644
> > --- a/include/linux/richacl.h
> > +++ b/include/linux/richacl.h
> > @@ -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 */
> > --
> > 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

2015-05-15 20:51:41

by J. Bruce Fields

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

On Fri, Apr 24, 2015 at 01:04:16PM +0200, Andreas Gruenbacher wrote:
> So far, richacl_equiv_mode() is relatively limited in the types of acl it
> considers equivalent to a file mode: it only accepts masked acls with a single
> everyone@:rwpxd::allow entry.
>
> Change this to consider all acls equivalent to file modes if they only consist
> of owner@, group@, and everyone@ entries and the owner@ permissions do not
> depend on whether the owner is a member in the owning group.
>
> Signed-off-by: Andreas Gruenbacher <[email protected]>
> ---
> fs/richacl_base.c | 150 ++++++++++++++++++++++++++++++++++++++----------
> include/linux/richacl.h | 1 +
> 2 files changed, 122 insertions(+), 29 deletions(-)
>
> diff --git a/fs/richacl_base.c b/fs/richacl_base.c
> index db27542..54cb482 100644
> --- a/fs/richacl_base.c
> +++ b/fs/richacl_base.c
> @@ -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:

> + * This function does not consider the masks in @acl.

Given that we do this later:

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

> + 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;
> + }

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

2015-05-15 21:33:54

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [RFC v3 25/45] richacl: acl editing helper functions

On Fri, Apr 24, 2015 at 01:04:22PM +0200, Andreas Gruenbacher wrote:
> +/**
> + * 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.

2015-05-18 20:39:39

by Andreas Grünbacher

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

2015-05-14 17:09 GMT+02:00 J. Bruce Fields <[email protected]>:
> 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.

> Do we have wireshark patches?

We do now:

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

> On Fri, Apr 24, 2015 at 01:04:17PM +0200, Andreas Gruenbacher wrote:
> 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

2015-05-18 20:43:49

by J. Bruce Fields

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

On Mon, May 18, 2015 at 10:39:37PM +0200, Andreas Grünbacher wrote:
> 2015-05-14 17:09 GMT+02:00 J. Bruce Fields <[email protected]>:
> > 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.
>
> > Do we have wireshark patches?
>
> We do now:
>
> https://code.wireshark.org/review/#/c/8526/1

Great!--b.

>
> > On Fri, Apr 24, 2015 at 01:04:17PM +0200, Andreas Gruenbacher wrote:
> > 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

2015-05-18 21:20:18

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [RFC v3 26/45] richacl: Move everyone@ aces down the acl

Looks OK.

On Fri, Apr 24, 2015 at 01:04:23PM +0200, Andreas Gruenbacher wrote:
> 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".

> The everyone@ aces can be moved down the acl without changing the
> permissions that the acl grants. This transformation simplifies the
> following algorithms, and eventually allows us to turn the final
> everyone@ allow ace into an entry for the other class.
>
> Signed-off-by: Andreas Gruenbacher <[email protected]>
> ---
> fs/richacl_compat.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 65 insertions(+)
>
> diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
> index 2ea6658..63f4373 100644
> --- a/fs/richacl_compat.c
> +++ b/fs/richacl_compat.c
> @@ -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.

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.

> + */
> +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;
> +}
> --
> 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

2015-05-22 21:08:34

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [RFC v3 14/45] richacl: Permission check algorithm

On Fri, Apr 24, 2015 at 01:04:11PM +0200, Andreas Gruenbacher wrote:
> 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.

>
> Signed-off-by: Andreas Gruenbacher <[email protected]>
> ---
> fs/Makefile | 2 +-
> fs/richacl_inode.c | 130 ++++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/richacl.h | 3 ++
> 3 files changed, 134 insertions(+), 1 deletion(-)
> create mode 100644 fs/richacl_inode.c
>
> diff --git a/fs/Makefile b/fs/Makefile
> index 8f0a59c..bb96ad7 100644
> --- a/fs/Makefile
> +++ b/fs/Makefile
> @@ -48,7 +48,7 @@ obj-$(CONFIG_SYSCTL) += drop_caches.o
>
> obj-$(CONFIG_FHANDLE) += fhandle.o
> obj-$(CONFIG_FS_RICHACL) += richacl.o
> -richacl-y := richacl_base.o
> +richacl-y := richacl_base.o richacl_inode.o
>
> obj-y += quota/
>
> diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
> new file mode 100644
> index 0000000..33e591a
> --- /dev/null
> +++ b/fs/richacl_inode.c
> @@ -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);
> diff --git a/include/linux/richacl.h b/include/linux/richacl.h
> index a27ff10..d11e8f3 100644
> --- a/include/linux/richacl.h
> +++ b/include/linux/richacl.h
> @@ -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 */
> --
> 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

2015-05-23 15:51:01

by Jeremy Allison

[permalink] [raw]
Subject: Re: [RFC v3 00/45] Richacls

On Fri, Apr 24, 2015 at 01:03:57PM +0200, Andreas Gruenbacher wrote:
> 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

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.

2015-05-23 15:56:48

by Andreas Grünbacher

[permalink] [raw]
Subject: Re: [RFC v3 00/45] Richacls

Jeremy,

2015-05-23 17:50 GMT+02:00 Jeremy Allison <[email protected]>:
> 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

2015-05-26 21:56:51

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [RFC v3 29/45] richacl: Apply the file masks to a richacl

On Fri, Apr 24, 2015 at 01:04:26PM +0200, Andreas Gruenbacher wrote:
> Put all the pieces of the acl transformation puzzle together for
> computing a richacl which has the file masks "applied" so that the
> standard nfsv4 access check algorithm can be used on the richacl.
>
> Signed-off-by: Andreas Gruenbacher <[email protected]>
> ---
> fs/richacl_compat.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++
> include/linux/richacl.h | 3 ++
> 2 files changed, 106 insertions(+)
>
> diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
> index 645917f..49af600 100644
> --- a/fs/richacl_compat.c
> +++ b/fs/richacl_compat.c
> @@ -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).

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.

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

> + */
> +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);
> diff --git a/include/linux/richacl.h b/include/linux/richacl.h
> index bcc2b64..6a97dca 100644
> --- a/include/linux/richacl.h
> +++ b/include/linux/richacl.h
> @@ -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 */
> --
> 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

2015-05-27 09:24:52

by Andreas Grünbacher

[permalink] [raw]
Subject: 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]>:
> On Fri, Apr 24, 2015 at 01:04:16PM +0200, Andreas Gruenbacher wrote:
> This comment is a little confusing:
>
>> + * This function does not consider the masks in @acl.
>
> Given that we do this later:
>
>> + 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.

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

>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

2015-05-27 11:25:19

by Andreas Grünbacher

[permalink] [raw]
Subject: Re: [RFC v3 14/45] richacl: Permission check algorithm

2015-05-22 23:08 GMT+02:00 J. Bruce Fields <[email protected]>:
> On Fri, Apr 24, 2015 at 01:04:11PM +0200, Andreas Gruenbacher wrote:
>> 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).

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

> But if I'm right, I'm not sure how to describe the algorithm concisely.

Right, it's all a bit messy.

Thanks,
Andreas

2015-05-27 15:05:25

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [RFC v3 14/45] richacl: Permission check algorithm

On Wed, May 27, 2015 at 01:25:18PM +0200, Andreas Grünbacher wrote:
> 2015-05-22 23:08 GMT+02:00 J. Bruce Fields <[email protected]>:
> > On Fri, Apr 24, 2015 at 01:04:11PM +0200, Andreas Gruenbacher wrote:
> >> 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).
>
> 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:

OK, good, that makes sense, so I think all that's needed is some updates
to documentation; for example:

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

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

> */
>
> 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().
>
> > 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.

2015-05-27 15:06:56

by J. Bruce Fields

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

On Wed, May 27, 2015 at 11:24:49AM +0200, Andreas Grünbacher wrote:
> Bruce,
>
> 2015-05-15 22:51 GMT+02:00 J. Bruce Fields <[email protected]>:
> > On Fri, Apr 24, 2015 at 01:04:16PM +0200, Andreas Gruenbacher wrote:
> > This comment is a little confusing:
> >
> >> + * This function does not consider the masks in @acl.
> >
> > Given that we do this later:
> >
> >> + 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.
>
> > 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.
>
> >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.

2015-05-28 19:24:14

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [RFC v3 30/45] richacl: Create richacl from mode values

On Fri, Apr 24, 2015 at 01:04:27PM +0200, Andreas Gruenbacher wrote:
> Create a richacl that corresponds to given file mode permission bits.
>
> Signed-off-by: Andreas Gruenbacher <[email protected]>
> ---
> fs/richacl_compat.c | 38 ++++++++++++++++++++++++++++++++++++++
> include/linux/richacl.h | 1 +
> 2 files changed, 39 insertions(+)
>
> diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
> index 49af600..8ebe772 100644
> --- a/fs/richacl_compat.c
> +++ b/fs/richacl_compat.c
> @@ -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:

> + *
> + * @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.

> + 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);
> diff --git a/include/linux/richacl.h b/include/linux/richacl.h
> index 6a97dca..25ff4df 100644
> --- a/include/linux/richacl.h
> +++ b/include/linux/richacl.h
> @@ -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 */
> --
> 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

2015-05-28 19:25:53

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [RFC v3 30/45] richacl: Create richacl from mode values

On Thu, May 28, 2015 at 03:24:13PM -0400, bfields wrote:
> On Fri, Apr 24, 2015 at 01:04:27PM +0200, Andreas Gruenbacher wrote:
> > Create a richacl that corresponds to given file mode permission bits.
> >
> > Signed-off-by: Andreas Gruenbacher <[email protected]>
> > ---
> > fs/richacl_compat.c | 38 ++++++++++++++++++++++++++++++++++++++
> > include/linux/richacl.h | 1 +
> > 2 files changed, 39 insertions(+)
> >
> > diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
> > index 49af600..8ebe772 100644
> > --- a/fs/richacl_compat.c
> > +++ b/fs/richacl_compat.c
> > @@ -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:
>
> > + *
> > + * @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.

>
> --b.
>
> > + 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);
> > diff --git a/include/linux/richacl.h b/include/linux/richacl.h
> > index 6a97dca..25ff4df 100644
> > --- a/include/linux/richacl.h
> > +++ b/include/linux/richacl.h
> > @@ -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 */
> > --
> > 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

2015-05-28 19:52:06

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [RFC v3 33/45] nfsd: Keep list of acls to dispose of in compoundargs

On Fri, Apr 24, 2015 at 01:04:30PM +0200, Andreas Gruenbacher wrote:
> 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.

>
> Signed-off-by: Andreas Gruenbacher <[email protected]>
> ---
> fs/nfsd/nfs4xdr.c | 26 ++++++++++++++++++++++++++
> fs/nfsd/xdr4.h | 6 ++++++
> 2 files changed, 32 insertions(+)
>
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index e2d602d..f25d1e7 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -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;
>
> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> index 0bda93e..4ed4db0 100644
> --- a/fs/nfsd/xdr4.h
> +++ b/fs/nfsd/xdr4.h
> @@ -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;
>
> --
> 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

2015-05-28 19:58:57

by Andreas Grünbacher

[permalink] [raw]
Subject: Re: [RFC v3 30/45] richacl: Create richacl from mode values

2015-05-28 21:25 GMT+02:00 J. Bruce Fields <[email protected]>:
> 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

2015-05-28 20:28:14

by Andreas Grünbacher

[permalink] [raw]
Subject: 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]>:
> On Fri, Apr 24, 2015 at 01:04:30PM +0200, Andreas Gruenbacher wrote:
>> 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

2015-05-28 20:33:33

by J. Bruce Fields

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

On Fri, Apr 24, 2015 at 01:04:33PM +0200, Andreas Gruenbacher wrote:
> 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.

Is it important that the client catch that?

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

...
> +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,
> + };

This is the same list as in nfs4-xdr_enc_server_caps. Could we avoid
that duplication?

--b.

2015-05-28 20:50:01

by Trond Myklebust

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

On Thu, May 28, 2015 at 4:33 PM, J. Bruce Fields <[email protected]> wrote:
> On Fri, Apr 24, 2015 at 01:04:33PM +0200, Andreas Gruenbacher wrote:
>> 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.
>
> 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

2015-05-28 21:05:44

by Andreas Grünbacher

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

2015-05-28 22:50 GMT+02:00 Trond Myklebust <[email protected]>:
> On Thu, May 28, 2015 at 4:33 PM, J. Bruce Fields <[email protected]> wrote:
>> On Fri, Apr 24, 2015 at 01:04:33PM +0200, Andreas Gruenbacher wrote:
>>> 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.
>>
>> 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

2015-05-28 21:15:30

by Andreas Grünbacher

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

2015-05-28 22:33 GMT+02:00 J. Bruce Fields <[email protected]>:
> On Fri, Apr 24, 2015 at 01:04:33PM +0200, Andreas Gruenbacher wrote:
>> 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.

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

2015-05-28 21:28:14

by Andreas Grünbacher

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

2015-05-28 22:33 GMT+02:00 J. Bruce Fields <[email protected]>:
> 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

2015-05-28 21:40:37

by Trond Myklebust

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

On Thu, May 28, 2015 at 5:05 PM, Andreas Grünbacher
<[email protected]> wrote:
> 2015-05-28 22:50 GMT+02:00 Trond Myklebust <[email protected]>:
>> On Thu, May 28, 2015 at 4:33 PM, J. Bruce Fields <[email protected]> wrote:
>>> On Fri, Apr 24, 2015 at 01:04:33PM +0200, Andreas Gruenbacher wrote:
>>>> 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.
>>>
>>> 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.
>

We already do this kind of check with the existing code. What's wrong with it?

Trond

2015-05-28 21:55:25

by Trond Myklebust

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

On Thu, May 28, 2015 at 5:40 PM, Trond Myklebust
<[email protected]> wrote:
> On Thu, May 28, 2015 at 5:05 PM, Andreas Grünbacher
> <[email protected]> wrote:
>> 2015-05-28 22:50 GMT+02:00 Trond Myklebust <[email protected]>:
>>> On Thu, May 28, 2015 at 4:33 PM, J. Bruce Fields <[email protected]> wrote:
>>>> On Fri, Apr 24, 2015 at 01:04:33PM +0200, Andreas Gruenbacher wrote:
>>>>> 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.
>>>>
>>>> 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.
>>
>
> 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

2015-05-28 22:09:38

by Andreas Grünbacher

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

2015-05-28 23:55 GMT+02:00 Trond Myklebust <[email protected]>:
>> 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

2015-05-28 22:24:29

by Trond Myklebust

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

On Thu, May 28, 2015 at 6:09 PM, Andreas Grünbacher
<[email protected]> wrote:
> 2015-05-28 23:55 GMT+02:00 Trond Myklebust <[email protected]>:
>>> 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.


...and I'd prefer that we don't keep rewriting code that works. Screw
the textbooks...

Trond

2015-05-28 22:28:52

by Andreas Grünbacher

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

2015-05-29 0:24 GMT+02:00 Trond Myklebust <[email protected]>:
> ...and I'd prefer that we don't keep rewriting code that works.

Sure, go ahead.

Thanks,
Andreas

2015-05-28 23:06:18

by Trond Myklebust

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

On Fri, Apr 24, 2015 at 7:04 AM, Andreas Gruenbacher
<[email protected]> wrote:
> 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

2015-05-28 23:11:15

by Trond Myklebust

[permalink] [raw]
Subject: Re: [RFC v3 37/45] nfs/sunrpc: No more encode and decode function pointer casting

On Fri, Apr 24, 2015 at 7:04 AM, Andreas Gruenbacher
<[email protected]> wrote:
> 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

2015-05-28 23:24:48

by Trond Myklebust

[permalink] [raw]
Subject: Re: [RFC v3 41/45] rpc: Allow to demand-allocate pages to encode into

On Fri, Apr 24, 2015 at 7:04 AM, Andreas Gruenbacher
<[email protected]> wrote:
> When encoding large, variable-length objects such as acls into xdr_bufs, it is
> easier to allocate buffer pages on demand rather than computing the required
> buffer size beforehand.
>
> Signed-off-by: Andreas Gruenbacher <[email protected]>
> ---
> net/sunrpc/xdr.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
> index 4439ac4..062951b 100644
> --- a/net/sunrpc/xdr.c
> +++ b/net/sunrpc/xdr.c
> @@ -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
>

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

2015-05-28 23:29:39

by Andreas Grünbacher

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

2015-05-29 1:06 GMT+02:00 Trond Myklebust <[email protected]>:
> On Fri, Apr 24, 2015 at 7:04 AM, Andreas Gruenbacher
> <[email protected]> wrote:
>> 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.

> 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

2015-05-28 23:39:54

by Trond Myklebust

[permalink] [raw]
Subject: Re: [RFC v3 38/45] nfs/sunrpc: Return status code from encode functions

On Fri, Apr 24, 2015 at 7:04 AM, Andreas Gruenbacher
<[email protected]> wrote:
> 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

2015-05-28 23:40:29

by Andreas Grünbacher

[permalink] [raw]
Subject: 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]>:
> 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

2015-05-29 00:37:31

by Trond Myklebust

[permalink] [raw]
Subject: Re: [RFC v3 37/45] nfs/sunrpc: No more encode and decode function pointer casting

On Thu, May 28, 2015 at 7:40 PM, Andreas Grünbacher
<[email protected]> wrote:
> 2015-05-29 1:11 GMT+02:00 Trond Myklebust <[email protected]>:
>> 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.

> 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

2015-05-29 13:15:08

by Trond Myklebust

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

On Thu, May 28, 2015 at 7:06 PM, Trond Myklebust
<[email protected]> wrote:
> On Fri, Apr 24, 2015 at 7:04 AM, Andreas Gruenbacher
> <[email protected]> wrote:
>> 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

2015-05-29 14:57:34

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [RFC v3 43/45] uapi/nfs: Add NFSv4.1 ACL definitions

On Fri, Apr 24, 2015 at 01:04:40PM +0200, Andreas Gruenbacher wrote:
> 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.

>
> Signed-off-by: Andreas Gruenbacher <[email protected]>
> ---
> include/uapi/linux/nfs4.h | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/include/uapi/linux/nfs4.h b/include/uapi/linux/nfs4.h
> index adc0aff..2119c7c 100644
> --- a/include/uapi/linux/nfs4.h
> +++ b/include/uapi/linux/nfs4.h
> @@ -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
> --
> 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

2015-05-29 15:00:16

by Andreas Grünbacher

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

2015-05-29 15:15 GMT+02:00 Trond Myklebust <[email protected]>:
> [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.

> 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

2015-05-29 15:24:59

by Trond Myklebust

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

Adding linux-api...

On Fri, May 29, 2015 at 11:00 AM, Andreas Grünbacher
<[email protected]> wrote:
> 2015-05-29 15:15 GMT+02:00 Trond Myklebust <[email protected]>:
>> [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.

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.

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

What is the editor supposed to do about an entry it cannot even
interpret, let alone store back to the server?

> 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

2015-05-29 15:30:43

by Andreas Grünbacher

[permalink] [raw]
Subject: 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]>:
> On Thu, May 28, 2015 at 7:40 PM, Andreas Grünbacher
> <[email protected]> wrote:
>> 2015-05-29 1:11 GMT+02:00 Trond Myklebust <[email protected]>:
>>> 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.

I agree, none of that is going to get merged directly.

Andreas

2015-05-29 15:45:15

by Andreas Grünbacher

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

2015-05-29 17:24 GMT+02:00 Trond Myklebust <[email protected]>:
>> 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.

That can be changed if we find a reasonable solution.

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

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

2015-05-29 15:46:14

by Andreas Grünbacher

[permalink] [raw]
Subject: 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]>:
> 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

2015-05-29 15:54:49

by Trond Myklebust

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

On Fri, May 29, 2015 at 11:45 AM, Andreas Grünbacher
<[email protected]> wrote:
> 2015-05-29 17:24 GMT+02:00 Trond Myklebust <[email protected]>:
>>> 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.
>
> That can be changed if we find a reasonable solution.
>
>>>> 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.)
>>
>> 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.
>
>>> 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.

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

2015-05-29 15:54:52

by Frank Filz

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

> On Fri, May 29, 2015 at 11:00 AM, Andreas Grünbacher
> <[email protected]> wrote:
> > 2015-05-29 15:15 GMT+02:00 Trond Myklebust
> <[email protected]>:
> >> [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.
>
> 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



2015-06-25 20:09:35

by Stefan Metzmacher

[permalink] [raw]
Subject: Re: [RFC v3 00/45] Richacls

Hi Andreas,

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


Attachments:
signature.asc (198.00 B)
OpenPGP digital signature

2016-06-23 16:37:04

by Weston Andros Adamson

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


> On Apr 24, 2015, at 7:04 AM, Andreas Gruenbacher <[email protected]> wrote:
>
> Changes nfs to support the "system.richacl" xattr instead of "system.nfs4_acl".
>
> The "system.nfs4_acl" xattr nfs uses directly exposes the on-the-wire format of
> NFSv4's acl attribute to user space. This has at least two downsides: (1) the
> format is different from other file systems, so user-space code needs to be nfs
> filesystem aware; (2) when symbolic user@domain and group@domain names are used
> in the acl, user-space needs to perform ID mapping in the same way as the
> kernel.
>
> Previously, when user-space requested only the length of the "system.nfs4_acl"
> attribute, nfs didn't need to retrieve the entire "system.nfs4_acl" attribute;
> retrieving its length was enough. With the "system.richacl" xattr, the length
> of "system.richacl" cannot be computed from the length of the NFSv4 acl
> attribute, so we always need to retrieve and cache the acl even when user-space
> only asks for the length of the "system.richacl" attribute.
>
> Because the nfs client now knows which kind of acl the user is trying to set,
> it will now no longer sends acls with deny entries to servers which didn't
> declare support for that feature. The maximum supported size of the NFSv4 acl
> attribute is now hard coded in the client code and no longer depends on the
> size of the buffer the user provides to the getxattr system call. When an acl
> exceeds this limit, getxattr fails with errno set to ENOMEM.
>
> Signed-off-by: Andreas Gruenbacher <[email protected]>
> ---
> fs/nfs/inode.c | 3 -
> fs/nfs/nfs4proc.c | 354 ++++++++++++++++++++--------------------------
> fs/nfs/nfs4xdr.c | 224 +++++++++++++++++++++++++----
> fs/nfs/super.c | 4 +-
> include/linux/nfs_fs.h | 1 -
> include/linux/nfs_fs_sb.h | 2 +
> include/linux/nfs_xdr.h | 8 +-
> 7 files changed, 357 insertions(+), 239 deletions(-)
>
> diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
> index d42dff6..e67f72e 100644
> --- a/fs/nfs/inode.c
> +++ b/fs/nfs/inode.c
> @@ -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);
> diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
> index 8c50670..c2ba4f0 100644
> --- a/fs/nfs/nfs4proc.c
> +++ b/fs/nfs/nfs4proc.c
> @@ -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:
> - 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;
> }
>
> -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 = { };
> - 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;
> }
>
> -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);
> + 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;
> }
>
> -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);
> - 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;
> +
> 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

> + } 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
> diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
> index 4372fc4..8ccc2a0 100644
> --- a/fs/nfs/nfs4xdr.c
> +++ b/fs/nfs/nfs4xdr.c
> @@ -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
> @@ -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;
> @@ -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;
> }
> diff --git a/fs/nfs/super.c b/fs/nfs/super.c
> index 322b2de02..fb904ec 100644
> --- a/fs/nfs/super.c
> +++ b/fs/nfs/super.c
> @@ -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);
> diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
> index b01ccf3..4eac89d7 100644
> --- a/include/linux/nfs_fs.h
> +++ b/include/linux/nfs_fs.h
> @@ -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;
> diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
> index 5e1273d..df994aa 100644
> --- a/include/linux/nfs_fs_sb.h
> +++ b/include/linux/nfs_fs_sb.h
> @@ -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
> diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
> index 92134cf..77097ec 100644
> --- a/include/linux/nfs_xdr.h
> +++ b/include/linux/nfs_xdr.h
> @@ -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;
> };
>
> --
> 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


2016-06-23 17:15:57

by Frank Filz

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

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

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


2016-07-15 08:00:52

by Andreas Grünbacher

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

Hi Andy,

2016-06-23 18:37 GMT+02:00 Weston Andros Adamson <[email protected]>:
>> On Apr 24, 2015, at 7:04 AM, Andreas Gruenbacher <[email protected]> wrote:
>> Changes nfs to support the "system.richacl" xattr instead of "system.nfs4_acl".
>>
>> The "system.nfs4_acl" xattr nfs uses directly exposes the on-the-wire format of
>> NFSv4's acl attribute to user space. This has at least two downsides: (1) the
>> format is different from other file systems, so user-space code needs to be nfs
>> filesystem aware; (2) when symbolic user@domain and group@domain names are used
>> in the acl, user-space needs to perform ID mapping in the same way as the
>> kernel.
>>
>> Previously, when user-space requested only the length of the "system.nfs4_acl"
>> attribute, nfs didn't need to retrieve the entire "system.nfs4_acl" attribute;
>> retrieving its length was enough. With the "system.richacl" xattr, the length
>> of "system.richacl" cannot be computed from the length of the NFSv4 acl
>> attribute, so we always need to retrieve and cache the acl even when user-space
>> only asks for the length of the "system.richacl" attribute.
>>
>> Because the nfs client now knows which kind of acl the user is trying to set,
>> it will now no longer sends acls with deny entries to servers which didn't
>> declare support for that feature. The maximum supported size of the NFSv4 acl
>> attribute is now hard coded in the client code and no longer depends on the
>> size of the buffer the user provides to the getxattr system call. When an acl
>> exceeds this limit, getxattr fails with errno set to ENOMEM.
>>
>> Signed-off-by: Andreas Gruenbacher <[email protected]>
>> ---
>> fs/nfs/inode.c | 3 -
>> fs/nfs/nfs4proc.c | 354 ++++++++++++++++++++--------------------------
>> fs/nfs/nfs4xdr.c | 224 +++++++++++++++++++++++++----
>> fs/nfs/super.c | 4 +-
>> include/linux/nfs_fs.h | 1 -
>> include/linux/nfs_fs_sb.h | 2 +
>> include/linux/nfs_xdr.h | 8 +-
>> 7 files changed, 357 insertions(+), 239 deletions(-)
>>
>> diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
>> index d42dff6..e67f72e 100644
>> --- a/fs/nfs/inode.c
>> +++ b/fs/nfs/inode.c
>> @@ -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);
>> diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
>> index 8c50670..c2ba4f0 100644
>> --- a/fs/nfs/nfs4proc.c
>> +++ b/fs/nfs/nfs4proc.c
>> @@ -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:
>> - 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;
>> }
>>
>> -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 = { };
>> - 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;
>> }
>>
>> -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);
>> + 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;
>> }
>>
>> -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);
>> - 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;
>> +
>> 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.

Thanks!

> 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