Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1244246 > unrolled thread
| Started by | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| First post | 2015-10-12 01:00 +0200 |
| Last post | 2015-10-12 01:50 +0200 |
| Articles | 20 on this page of 58 — 6 participants |
Back to article view | Back to linux.kernel
[PATCH v10 00/46] Richacls Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:00 +0200
[PATCH v10 37/46] nfsd: Add support for the MAY_CREATE_{FILE,DIR} permissions Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 35/46] nfsd: Add richacl support Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 27/46] richacl: Propagate everyone@ permissions to other aces Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 17/46] richacl: xattr mapping functions Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 23/46] xfs: Make xfs_set_mode non-static Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
Re: [PATCH v10 23/46] xfs: Make xfs_set_mode non-static Dave Chinner <david@fromorbit.com> - 2015-10-12 01:50 +0200
[PATCH v10 43/46] nfs: Remove unused xdr page offsets in getacl/setacl arguments Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 24/46] xfs: Add richacl support Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
Re: [PATCH v10 24/46] xfs: Add richacl support Dave Chinner <david@fromorbit.com> - 2015-10-12 02:20 +0200
Re: [PATCH v10 24/46] xfs: Add richacl support Andreas Grünbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 04:00 +0200
Re: [PATCH v10 24/46] xfs: Add richacl support Dave Chinner <david@fromorbit.com> - 2015-10-12 06:10 +0200
Re: [PATCH v10 24/46] xfs: Add richacl support Andreas Gruenbacher <agruenba@redhat.com> - 2015-10-12 08:00 +0200
Re: [PATCH v10 24/46] xfs: Add richacl support Austin S Hemmelgarn <ahferroin7@gmail.com> - 2015-10-13 21:30 +0200
Re: [PATCH v10 24/46] xfs: Add richacl support Andreas Gruenbacher <agruenba@redhat.com> - 2015-10-13 15:40 +0200
[PATCH v10 28/46] richacl: Set the owner permissions to the owner mask Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 30/46] richacl: Isolate the owner and group classes Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 19/46] vfs: Add richacl permission checking Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 26/46] richacl: Move everyone@ aces down the acl Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 18/46] richacl: Add richacl xattr handler Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 42/46] nfs: Fix GETATTR bitmap verification Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 36/46] nfsd: Add support for the v4.1 dacl attribute Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 13/46] richacl: Update the file masks in chmod() Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 32/46] richacl: Create richacl from mode values Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 22/46] xfs: Fix error path in xfs_get_acl Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 20/46] ext4: Add richacl support Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 11/46] vfs: Add get_richacl and set_richacl inode operations Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 14/46] richacl: Check if an acl is equivalent to a file mode Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 33/46] nfsd: Keep list of acls to dispose of in compoundargs Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 38/46] richacl: Add support for unmapped identifiers Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
Re: [PATCH v10 38/46] richacl: Add support for unmapped identifiers Dave Chinner <david@fromorbit.com> - 2015-10-12 02:30 +0200
Re: [PATCH v10 38/46] richacl: Add support for unmapped identifiers Andreas Grünbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 04:00 +0200
[PATCH v10 29/46] richacl: Set the other permissions to the other mask Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 45/46] nfs: Add support for the v4.1 dacl attribute Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 41/46] sunrpc: Add xdr_init_encode_pages Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 46/46] richacl: uapi header split Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 44/46] nfs: Add richacl support Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
Re: [PATCH v10 44/46] nfs: Add richacl support Anna Schumaker <Anna.Schumaker@netapp.com> - 2015-10-12 16:40 +0200
Re: [PATCH v10 44/46] nfs: Add richacl support Andreas Gruenbacher <agruenba@redhat.com> - 2015-10-12 21:50 +0200
[PATCH v10 21/46] ext4: Add richacl feature flag Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 25/46] richacl: acl editing helper functions Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 15/46] richacl: Create-time inheritance Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 31/46] richacl: Apply the file masks to a richacl Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:10 +0200
[PATCH v10 40/46] sunrpc: Allow to demand-allocate pages to encode into Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:20 +0200
[PATCH v10 39/46] ext4: Don't allow unmapped identifiers in richacls Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:20 +0200
[PATCH v10 34/46] nfsd: Use richacls as internal acl representation Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:20 +0200
[PATCH v10 16/46] richacl: Automatic Inheritance Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:30 +0200
[PATCH v10 03/46] vfs: Add MAY_DELETE_SELF and MAY_DELETE_CHILD permission flags Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:40 +0200
[PATCH v10 12/46] vfs: Cache richacl in struct inode Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:40 +0200
[PATCH v10 09/46] richacl: Permission check algorithm Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:40 +0200
[PATCH v10 10/46] vfs: Cache base_acl objects in inodes Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:40 +0200
[PATCH v10 08/46] richacl: Compute maximum file masks from an acl Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:40 +0200
[PATCH v10 04/46] vfs: Make the inode passed to inode_change_ok non-const Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:40 +0200
[PATCH v10 05/46] vfs: Add permission flags for setting file attributes Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:40 +0200
[PATCH v10 02/46] vfs: Add MAY_CREATE_FILE and MAY_CREATE_DIR permission flags Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:40 +0200
[PATCH v10 01/46] vfs: Add IS_ACL() and IS_RICHACL() tests Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:50 +0200
[PATCH v10 07/46] richacl: Permission mapping functions Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:50 +0200
[PATCH v10 06/46] richacl: In-memory representation and helper functions Andreas Gruenbacher <andreas.gruenbacher@gmail.com> - 2015-10-12 01:50 +0200
Page 2 of 3 — ← Prev page 1 [2] 3 Next page →
| From | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 01:10 +0200 |
| Subject | [PATCH v10 42/46] nfs: Fix GETATTR bitmap verification |
| Message-ID | <qixUD-61y-39@gated-at.bofh.it> |
| In reply to | #1244246 |
From: Andreas Gruenbacher <agruenba@redhat.com> When decoding GETATTR replies, the client checks the attribute bitmap for which attributes the server has sent. It misses bits at the word boundaries, though; fix that. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> --- fs/nfs/nfs4xdr.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 788adf3..6f6d921 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -4375,6 +4375,11 @@ static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat) goto xdr_error; if ((status = decode_attr_files_total(xdr, bitmap, &fsstat->tfiles)) != 0) goto xdr_error; + + status = -EIO; + if (unlikely(bitmap[0])) + goto xdr_error; + if ((status = decode_attr_space_avail(xdr, bitmap, &fsstat->abytes)) != 0) goto xdr_error; if ((status = decode_attr_space_free(xdr, bitmap, &fsstat->fbytes)) != 0) @@ -4574,6 +4579,10 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap, goto xdr_error; fattr->valid |= status; + status = -EIO; + if (unlikely(bitmap[0])) + goto xdr_error; + status = decode_attr_mode(xdr, bitmap, &fmode); if (status < 0) goto xdr_error; @@ -4627,6 +4636,10 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap, goto xdr_error; fattr->valid |= status; + status = -EIO; + if (unlikely(bitmap[1])) + goto xdr_error; + status = decode_attr_mdsthreshold(xdr, bitmap, fattr->mdsthreshold); if (status < 0) goto xdr_error; @@ -4789,12 +4802,22 @@ static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo) if ((status = decode_attr_maxwrite(xdr, bitmap, &fsinfo->wtmax)) != 0) goto xdr_error; fsinfo->wtpref = fsinfo->wtmax; + + status = -EIO; + if (unlikely(bitmap[0])) + goto xdr_error; + status = decode_attr_time_delta(xdr, bitmap, &fsinfo->time_delta); if (status != 0) goto xdr_error; status = decode_attr_pnfstype(xdr, bitmap, &fsinfo->layouttype); if (status != 0) goto xdr_error; + + status = -EIO; + if (unlikely(bitmap[1])) + goto xdr_error; + status = decode_attr_layout_blksize(xdr, bitmap, &fsinfo->blksize); if (status) goto xdr_error; -- 2.5.0 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 01:10 +0200 |
| Subject | [PATCH v10 36/46] nfsd: Add support for the v4.1 dacl attribute |
| Message-ID | <qixUD-61y-41@gated-at.bofh.it> |
| In reply to | #1244246 |
From: Andreas Gruenbacher <agruenba@redhat.com>
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 <agruenba@redhat.com>
Acked-by: J. Bruce Fields <bfields@redhat.com>
---
fs/nfsd/nfs4proc.c | 3 +-
fs/nfsd/nfs4xdr.c | 219 ++++++++++++++++++++++++++++++----------------
fs/nfsd/nfsd.h | 6 +-
include/linux/nfs4.h | 1 +
include/uapi/linux/nfs4.h | 3 +-
5 files changed, 155 insertions(+), 77 deletions(-)
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 1bcfda2..a053e78 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1781,7 +1781,8 @@ static inline u32 nfsd4_getattr_rsize(struct svc_rqst *rqstp,
u32 bmap0 = bmap[0], bmap1 = bmap[1], bmap2 = bmap[2];
u32 ret = 0;
- if (bmap0 & FATTR4_WORD0_ACL)
+ if (bmap0 & FATTR4_WORD0_ACL ||
+ bmap1 & FATTR4_WORD1_DACL)
return svc_max_payload(rqstp);
if (bmap0 & FATTR4_WORD0_FS_LOCATIONS)
return svc_max_payload(rqstp);
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 682a7d8..33d028c 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -301,6 +301,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,
@@ -312,6 +374,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;
@@ -325,50 +388,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;
@@ -436,6 +467,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
@@ -2272,6 +2319,42 @@ out_resource:
return nfserr_resource;
}
+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.
@@ -2342,15 +2425,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)
@@ -2389,6 +2473,8 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
if (!IS_ACL(d_inode(dentry)))
word0 &= ~FATTR4_WORD0_ACL;
+ if (!IS_RICHACL(d_inode(dentry)))
+ word1 &= ~FATTR4_WORD1_DACL;
if (!contextsupport)
word2 &= ~FATTR4_WORD2_SECURITY_LABEL;
if (!word2) {
@@ -2502,35 +2588,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)
@@ -2746,6 +2809,16 @@ out_acl:
}
p = xdr_encode_hyper(p, 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) {
status = nfsd4_encode_layout_type(xdr, exp->ex_layout_type);
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index cf98052..cb5c3ed 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 1422fc6..682ced3 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 2b871e0..b850ffd 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.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 01:10 +0200 |
| Subject | [PATCH v10 13/46] richacl: Update the file masks in chmod() |
| Message-ID | <qixUD-61y-43@gated-at.bofh.it> |
| In reply to | #1244246 |
From: Andreas Gruenbacher <agruenba@redhat.com>
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
RICHACE_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 <agruenba@redhat.com>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
---
fs/richacl_base.c | 42 ++++++++++++++++++++++++++++++++++++++++++
fs/richacl_inode.c | 30 ++++++++++++++++++++++++++++++
include/linux/richacl.h | 2 ++
3 files changed, 74 insertions(+)
diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 78e81df..764d73a 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -340,3 +340,45 @@ restart:
acl->a_flags &= ~(RICHACL_WRITE_THROUGH | RICHACL_MASKED);
}
EXPORT_SYMBOL_GPL(richacl_compute_max_masks);
+
+/**
+ * __richacl_chmod - update the file masks to reflect the new mode
+ * @acl: access control list
+ * @mode: new file permission bits including the file type
+ *
+ * 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, umode_t mode)
+{
+ unsigned int x = S_ISDIR(mode) ? 0 : RICHACE_DELETE_CHILD;
+ unsigned int owner_mask, group_mask, other_mask;
+ struct richacl *clone;
+
+ owner_mask = richacl_mode_to_mask(mode >> 6) & ~x;
+ group_mask = richacl_mode_to_mask(mode >> 3) & ~x;
+ other_mask = richacl_mode_to_mask(mode) & ~x;
+
+ 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_WRITE_THROUGH))
+ return acl;
+
+ clone = richacl_clone(acl, GFP_KERNEL);
+ richacl_put(acl);
+ if (!clone)
+ return ERR_PTR(-ENOMEM);
+
+ clone->a_flags |= (RICHACL_WRITE_THROUGH | 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/fs/richacl_inode.c b/fs/richacl_inode.c
index c0458db..be11832 100644
--- a/fs/richacl_inode.c
+++ b/fs/richacl_inode.c
@@ -222,3 +222,33 @@ out:
return denied ? -EACCES : 0;
}
EXPORT_SYMBOL_GPL(richacl_permission);
+
+/**
+ * richacl_chmod - filesystem chmod helper
+ * @inode: inode whose file permission bits to change
+ * @mode: new file permission bits including the file type
+ *
+ * Helper for filesystems to use to perform a chmod on the richacl of an inode.
+ */
+int
+richacl_chmod(struct inode *inode, umode_t mode)
+{
+ struct richacl *acl;
+ int retval;
+
+ if (S_ISLNK(mode))
+ return -EOPNOTSUPP;
+ if (!inode->i_op->set_richacl)
+ return -EOPNOTSUPP;
+ acl = get_richacl(inode);
+ if (IS_ERR_OR_NULL(acl))
+ return PTR_ERR(acl);
+ acl = __richacl_chmod(acl, mode);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ retval = inode->i_op->set_richacl(inode, acl);
+ richacl_put(acl);
+
+ return retval;
+}
+EXPORT_SYMBOL(richacl_chmod);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 3f9dd10..152000c 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -303,8 +303,10 @@ extern int richacl_masks_to_mode(const struct richacl *);
extern unsigned int richacl_mode_to_mask(umode_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 *, umode_t);
/* richacl_inode.c */
extern int richacl_permission(struct inode *, const struct richacl *, int);
+extern int richacl_chmod(struct inode *, umode_t);
#endif /* __RICHACL_H */
--
2.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 01:10 +0200 |
| Subject | [PATCH v10 32/46] richacl: Create richacl from mode values |
| Message-ID | <qixUD-61y-45@gated-at.bofh.it> |
| In reply to | #1244246 |
From: Andreas Gruenbacher <agruenba@redhat.com>
A file can have "no acl" in the sense that only the file mode permission
bits determine access. In that case, the getxattr system call fails with
errno == ENODATA (No such attribute).
Over the NFSv4 protocol, a file always has an acl, and we convert the file
mode permission bits into an equivalent acl with richacl_from_mode. Such
"trivial" acls can be converted back to a file mode with
richacl_equiv_mode.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
---
fs/richacl_compat.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/richacl.h | 1 +
2 files changed, 89 insertions(+)
diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
index c99274f..e513958 100644
--- a/fs/richacl_compat.c
+++ b/fs/richacl_compat.c
@@ -827,3 +827,91 @@ richacl_apply_masks(struct richacl **acl, kuid_t owner)
return 0;
}
EXPORT_SYMBOL_GPL(richacl_apply_masks);
+
+/**
+ * richacl_from_mode - 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(umode_t mode)
+{
+ unsigned int owner_mask = richacl_mode_to_mask(mode >> 6);
+ 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;
+
+ /* 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;
+ }
+
+ denied = ~owner_mask & (group_mask | other_mask);
+ if (denied)
+ entries++; /* owner@ deny entry needed */
+ if (owner_mask & ~(group_mask & other_mask))
+ entries++; /* owner@ allow entry needed */
+ denied = ~group_mask & other_mask;
+ if (denied)
+ entries++; /* group@ deny entry needed */
+ if (group_mask & ~other_mask)
+ entries++; /* group@ allow entry needed */
+ if (other_mask)
+ entries++; /* everyone@ allow entry needed */
+
+ acl = richacl_alloc(entries, GFP_KERNEL);
+ if (!acl)
+ return NULL;
+ acl->a_owner_mask = owner_mask;
+ acl->a_group_mask = group_mask;
+ acl->a_other_mask = other_mask;
+ ace = acl->a_entries;
+
+ 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 (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 = ~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 (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++;
+ }
+ 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);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 77b6779..708926f 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -335,5 +335,6 @@ extern struct richacl *richacl_create(umode_t *, struct inode *);
/* richacl_compat.c */
extern int richacl_apply_masks(struct richacl **, kuid_t);
+extern struct richacl *richacl_from_mode(umode_t);
#endif /* __RICHACL_H */
--
2.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 01:10 +0200 |
| Subject | [PATCH v10 22/46] xfs: Fix error path in xfs_get_acl |
| Message-ID | <qixUD-61y-47@gated-at.bofh.it> |
| In reply to | #1244246 |
From: Andreas Gruenbacher <agruenba@redhat.com>
Error codes from xfs_attr_get other than -ENOATTR were not properly
reported. Fix that, and clean the code up somewhat.
In addition, the declaration of struct xfs_inode in xfs_acl.h isn't needed.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/xfs/xfs_acl.c | 19 +++++++------------
fs/xfs/xfs_acl.h | 1 -
2 files changed, 7 insertions(+), 13 deletions(-)
diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
index 4b64167..0f4ee92 100644
--- a/fs/xfs/xfs_acl.c
+++ b/fs/xfs/xfs_acl.c
@@ -122,7 +122,7 @@ struct posix_acl *
xfs_get_acl(struct inode *inode, int type)
{
struct xfs_inode *ip = XFS_I(inode);
- struct posix_acl *acl = NULL;
+ struct posix_acl *acl;
struct xfs_acl *xfs_acl;
unsigned char *ea_name;
int error;
@@ -158,18 +158,13 @@ xfs_get_acl(struct inode *inode, int type)
* cache entry, for any other error assume it is transient and
* leave the cache entry as ACL_NOT_CACHED.
*/
- if (error == -ENOATTR)
- goto out_update_cache;
- goto out;
- }
+ acl = (error == -ENOATTR) ? NULL : ERR_PTR(error);
+ } else
+ acl = xfs_acl_from_disk(xfs_acl,
+ XFS_ACL_MAX_ENTRIES(ip->i_mount));
- acl = xfs_acl_from_disk(xfs_acl, XFS_ACL_MAX_ENTRIES(ip->i_mount));
- if (IS_ERR(acl))
- goto out;
-
-out_update_cache:
- set_cached_acl(inode, type, acl);
-out:
+ if (!IS_ERR(acl))
+ set_cached_acl(inode, type, acl);
kmem_free(xfs_acl);
return acl;
}
diff --git a/fs/xfs/xfs_acl.h b/fs/xfs/xfs_acl.h
index 3841b07..9ee0a0d 100644
--- a/fs/xfs/xfs_acl.h
+++ b/fs/xfs/xfs_acl.h
@@ -20,7 +20,6 @@
struct inode;
struct posix_acl;
-struct xfs_inode;
#ifdef CONFIG_XFS_POSIX_ACL
extern struct posix_acl *xfs_get_acl(struct inode *inode, int type);
--
2.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 01:10 +0200 |
| Subject | [PATCH v10 20/46] ext4: Add richacl support |
| Message-ID | <qixUD-61y-49@gated-at.bofh.it> |
| In reply to | #1244246 |
From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
Support the richacl permission model in ext4. The richacls are stored
in "system.richacl" xattrs. Richacls need to be enabled by tune2fs or
at file system create time.
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/ext4/Kconfig | 11 +++++
fs/ext4/Makefile | 1 +
fs/ext4/file.c | 3 ++
fs/ext4/ialloc.c | 7 ++-
fs/ext4/inode.c | 10 +++--
fs/ext4/namei.c | 5 +++
fs/ext4/richacl.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/ext4/richacl.h | 43 ++++++++++++++++++
fs/ext4/xattr.c | 7 +++
9 files changed, 212 insertions(+), 4 deletions(-)
create mode 100644 fs/ext4/richacl.c
create mode 100644 fs/ext4/richacl.h
diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
index 47728da..3ec0843 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -22,6 +22,17 @@ config EXT3_FS_POSIX_ACL
This config option is here only for backward compatibility. ext3
filesystem is now handled by the ext4 driver.
+config EXT4_FS_RICHACL
+ bool "Ext4 Rich Access Control Lists (EXPERIMENTAL)"
+ depends on EXT4_FS
+ select FS_RICHACL
+ help
+ Richacls are an implementation of NFSv4 ACLs, extended by file masks
+ to cleanly integrate into the POSIX file permission model. To learn
+ more about them, see http://www.bestbits.at/richacl/.
+
+ If you don't know what Richacls are, say N.
+
config EXT3_FS_SECURITY
bool "Ext3 Security Labels"
depends on EXT3_FS
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 75285ea..ea0d539 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -14,3 +14,4 @@ ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o
ext4-$(CONFIG_EXT4_FS_ENCRYPTION) += crypto_policy.o crypto.o \
crypto_key.o crypto_fname.o
+ext4-$(CONFIG_EXT4_FS_RICHACL) += richacl.o
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 113837e..a03b4a5 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -30,6 +30,7 @@
#include "ext4_jbd2.h"
#include "xattr.h"
#include "acl.h"
+#include "richacl.h"
/*
* Called when an inode is released. Note that this is different
@@ -719,6 +720,8 @@ const struct inode_operations ext4_file_inode_operations = {
.removexattr = generic_removexattr,
.get_acl = ext4_get_acl,
.set_acl = ext4_set_acl,
+ .get_richacl = ext4_get_richacl,
+ .set_richacl = ext4_set_richacl,
.fiemap = ext4_fiemap,
};
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 619bfc1..f279725 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -27,6 +27,7 @@
#include "ext4_jbd2.h"
#include "xattr.h"
#include "acl.h"
+#include "richacl.h"
#include <trace/events/ext4.h>
@@ -1052,7 +1053,11 @@ got:
if (err)
goto fail_drop;
- err = ext4_init_acl(handle, inode, dir);
+ if (EXT4_IS_RICHACL(dir))
+ err = ext4_init_richacl(handle, inode, dir);
+ else
+ err = ext4_init_acl(handle, inode, dir);
+
if (err)
goto fail_free_drop;
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 612fbcf..d8a48a3 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -42,6 +42,7 @@
#include "xattr.h"
#include "acl.h"
#include "truncate.h"
+#include "richacl.h"
#include <trace/events/ext4.h>
@@ -4805,9 +4806,12 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
if (orphan && inode->i_nlink)
ext4_orphan_del(NULL, inode);
- if (!rc && (ia_valid & ATTR_MODE))
- rc = posix_acl_chmod(inode, inode->i_mode);
-
+ if (!rc && (ia_valid & ATTR_MODE)) {
+ if (EXT4_IS_RICHACL(inode))
+ rc = richacl_chmod(inode, inode->i_mode);
+ else
+ rc = posix_acl_chmod(inode, inode->i_mode);
+ }
err_out:
ext4_std_error(inode->i_sb, error);
if (!error)
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 9f61e76..9b6e8b9 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -38,6 +38,7 @@
#include "xattr.h"
#include "acl.h"
+#include "richacl.h"
#include <trace/events/ext4.h>
/*
@@ -3854,6 +3855,8 @@ const struct inode_operations ext4_dir_inode_operations = {
.removexattr = generic_removexattr,
.get_acl = ext4_get_acl,
.set_acl = ext4_set_acl,
+ .get_richacl = ext4_get_richacl,
+ .set_richacl = ext4_set_richacl,
.fiemap = ext4_fiemap,
};
@@ -3865,4 +3868,6 @@ const struct inode_operations ext4_special_inode_operations = {
.removexattr = generic_removexattr,
.get_acl = ext4_get_acl,
.set_acl = ext4_set_acl,
+ .get_richacl = ext4_get_richacl,
+ .set_richacl = ext4_set_richacl,
};
diff --git a/fs/ext4/richacl.c b/fs/ext4/richacl.c
new file mode 100644
index 0000000..b46ac60
--- /dev/null
+++ b/fs/ext4/richacl.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright IBM Corporation, 2010
+ * Copyright (C) 2015 Red Hat, Inc.
+ * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/richacl_xattr.h>
+
+#include "ext4.h"
+#include "ext4_jbd2.h"
+#include "xattr.h"
+#include "acl.h"
+#include "richacl.h"
+
+struct richacl *
+ext4_get_richacl(struct inode *inode)
+{
+ const int name_index = EXT4_XATTR_INDEX_RICHACL;
+ void *value = NULL;
+ struct richacl *acl;
+ int retval;
+
+ retval = ext4_xattr_get(inode, name_index, "", NULL, 0);
+ if (retval > 0) {
+ value = kmalloc(retval, GFP_NOFS);
+ if (!value)
+ return ERR_PTR(-ENOMEM);
+ retval = ext4_xattr_get(inode, name_index, "", value, retval);
+ }
+ if (retval > 0) {
+ acl = richacl_from_xattr(&init_user_ns, value, retval);
+ if (acl == ERR_PTR(-EINVAL))
+ acl = ERR_PTR(-EIO);
+ } else if (retval == -ENODATA || retval == -ENOSYS)
+ acl = NULL;
+ else
+ acl = ERR_PTR(retval);
+ kfree(value);
+
+ if (!IS_ERR(acl))
+ set_cached_richacl(inode, acl);
+
+ return acl;
+}
+
+static int
+__ext4_set_richacl(handle_t *handle, struct inode *inode, struct richacl *acl)
+{
+ const int name_index = EXT4_XATTR_INDEX_RICHACL;
+ umode_t mode = inode->i_mode;
+ int retval;
+
+ if (acl) {
+ if (richacl_equiv_mode(acl, &mode) == 0) {
+ inode->i_ctime = ext4_current_time(inode);
+ inode->i_mode = mode;
+ ext4_mark_inode_dirty(handle, inode);
+ acl = NULL;
+ }
+ }
+ if (acl) {
+ void *value;
+ size_t size = richacl_xattr_size(acl);
+
+ mode &= ~S_IRWXUGO;
+ mode |= richacl_masks_to_mode(acl);
+
+ value = kmalloc(size, GFP_NOFS);
+ if (!value)
+ return -ENOMEM;
+ richacl_to_xattr(&init_user_ns, acl, value, size);
+ inode->i_mode = mode;
+ retval = ext4_xattr_set_handle(handle, inode, name_index, "",
+ value, size, 0);
+ kfree(value);
+ } else
+ retval = ext4_xattr_set_handle(handle, inode, name_index, "",
+ NULL, 0, 0);
+ if (!retval)
+ set_cached_richacl(inode, acl);
+
+ return retval;
+}
+
+int
+ext4_set_richacl(struct inode *inode, struct richacl *acl)
+{
+ handle_t *handle;
+ int retval, retries = 0;
+
+retry:
+ handle = ext4_journal_start(inode, EXT4_HT_XATTR,
+ ext4_jbd2_credits_xattr(inode));
+ if (IS_ERR(handle))
+ return PTR_ERR(handle);
+
+ retval = __ext4_set_richacl(handle, inode, acl);
+ ext4_journal_stop(handle);
+ if (retval == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
+ goto retry;
+ return retval;
+}
+
+int
+ext4_init_richacl(handle_t *handle, struct inode *inode, struct inode *dir)
+{
+ struct richacl *acl = richacl_create(&inode->i_mode, dir);
+ int error;
+
+ error = PTR_ERR(acl);
+ if (IS_ERR(acl))
+ return error;
+ if (acl) {
+ error = __ext4_set_richacl(handle, inode, acl);
+ richacl_put(acl);
+ }
+ return error;
+}
diff --git a/fs/ext4/richacl.h b/fs/ext4/richacl.h
new file mode 100644
index 0000000..fc7826f
--- /dev/null
+++ b/fs/ext4/richacl.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright IBM Corporation, 2010
+ * Copyright (C) 2015 Red Hat, Inc.
+ * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef __FS_EXT4_RICHACL_H
+#define __FS_EXT4_RICHACL_H
+
+#include <linux/richacl.h>
+
+#ifdef CONFIG_EXT4_FS_RICHACL
+
+#define EXT4_IS_RICHACL(inode) IS_RICHACL(inode)
+
+extern struct richacl *ext4_get_richacl(struct inode *);
+extern int ext4_set_richacl(struct inode *, struct richacl *);
+
+extern int ext4_init_richacl(handle_t *, struct inode *, struct inode *);
+
+#else /* CONFIG_EXT4_FS_RICHACL */
+
+#define EXT4_IS_RICHACL(inode) (0)
+#define ext4_get_richacl NULL
+#define ext4_set_richacl NULL
+
+static inline int
+ext4_init_richacl(handle_t *handle, struct inode *inode, struct inode *dir)
+{
+ return 0;
+}
+
+#endif /* CONFIG_EXT4_FS_RICHACL */
+#endif /* __FS_EXT4_RICHACL_H */
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 16e28c0..4d79adb 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -55,6 +55,7 @@
#include <linux/slab.h>
#include <linux/mbcache.h>
#include <linux/quotaops.h>
+#include <linux/richacl_xattr.h>
#include "ext4_jbd2.h"
#include "ext4.h"
#include "xattr.h"
@@ -99,6 +100,9 @@ static const struct xattr_handler *ext4_xattr_handler_map[] = {
#ifdef CONFIG_EXT4_FS_SECURITY
[EXT4_XATTR_INDEX_SECURITY] = &ext4_xattr_security_handler,
#endif
+#ifdef CONFIG_EXT4_FS_RICHACL
+ [EXT4_XATTR_INDEX_RICHACL] = &richacl_xattr_handler,
+#endif
};
const struct xattr_handler *ext4_xattr_handlers[] = {
@@ -111,6 +115,9 @@ const struct xattr_handler *ext4_xattr_handlers[] = {
#ifdef CONFIG_EXT4_FS_SECURITY
&ext4_xattr_security_handler,
#endif
+#ifdef CONFIG_EXT4_FS_RICHACL
+ &richacl_xattr_handler,
+#endif
NULL
};
--
2.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 01:10 +0200 |
| Subject | [PATCH v10 11/46] vfs: Add get_richacl and set_richacl inode operations |
| Message-ID | <qixUD-61y-51@gated-at.bofh.it> |
| In reply to | #1244246 |
From: Andreas Gruenbacher <agruenba@redhat.com>
These operations are similar to the get_acl and set_acl operations for
POSIX ACLs. The distinction between access and default ACLs doesn't exist
for richacls.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
include/linux/fs.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 3c22c92..08fde42 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1662,6 +1662,7 @@ struct inode_operations {
const char * (*follow_link) (struct dentry *, void **);
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 inode *, void *);
@@ -1691,6 +1692,7 @@ struct inode_operations {
umode_t create_mode, int *opened);
int (*tmpfile) (struct inode *, struct dentry *, umode_t);
int (*set_acl)(struct inode *, struct posix_acl *, int);
+ int (*set_richacl)(struct inode *, struct richacl *);
/* WARNING: probably going away soon, do not use! */
} ____cacheline_aligned;
--
2.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 01:10 +0200 |
| Subject | [PATCH v10 14/46] richacl: Check if an acl is equivalent to a file mode |
| Message-ID | <qixUD-61y-53@gated-at.bofh.it> |
| In reply to | #1244246 |
From: Andreas Gruenbacher <agruenba@redhat.com>
ACLs are considered equivalent to file modes if they only consist of
owner@, group@, and everyone@ entries, the owner@ permissions do not
depend on whether the owner is a member in the owning group, and no
inheritance flags are set. This test is used to avoid storing richacls
if the acl can be computed from the file permission bits.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
---
fs/richacl_base.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/richacl.h | 1 +
2 files changed, 105 insertions(+)
diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 764d73a..f4b8d3c 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -382,3 +382,107 @@ __richacl_chmod(struct richacl *acl, umode_t mode)
return clone;
}
EXPORT_SYMBOL_GPL(__richacl_chmod);
+
+/**
+ * richacl_equiv_mode - compute the mode equivalent of @acl
+ *
+ * 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, umode_t *mode_p)
+{
+ umode_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 = {
+ .defined = RICHACE_POSIX_ALWAYS_ALLOWED |
+ RICHACE_POSIX_OWNER_ALLOWED | x,
+ }, group = {
+ .defined = RICHACE_POSIX_ALWAYS_ALLOWED | x,
+ }, everyone = {
+ .defined = RICHACE_POSIX_ALWAYS_ALLOWED | x,
+ };
+ const struct richace *ace;
+
+ if (acl->a_flags & ~(RICHACL_WRITE_THROUGH | RICHACL_MASKED))
+ return -1;
+
+ 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;
+
+ if (acl->a_flags & RICHACL_MASKED) {
+ if (acl->a_flags & RICHACL_WRITE_THROUGH) {
+ owner.allowed = acl->a_owner_mask;
+ everyone.allowed = acl->a_other_mask;
+ } else {
+ owner.allowed &= acl->a_owner_mask;
+ everyone.allowed &= acl->a_other_mask;
+ }
+ group.allowed &= acl->a_group_mask;
+ }
+
+ 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;
+
+ *mode_p = mode;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(richacl_equiv_mode);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 152000c..9561db0 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -304,6 +304,7 @@ extern unsigned int richacl_mode_to_mask(umode_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 *, umode_t);
+extern int richacl_equiv_mode(const struct richacl *, umode_t *);
/* richacl_inode.c */
extern int richacl_permission(struct inode *, const struct richacl *, int);
--
2.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 01:10 +0200 |
| Subject | [PATCH v10 33/46] nfsd: Keep list of acls to dispose of in compoundargs |
| Message-ID | <qixUD-61y-57@gated-at.bofh.it> |
| In reply to | #1244246 |
From: Andreas Gruenbacher <agruenba@redhat.com>
We will decode acls in requests into richacls. Even if unlikely, there
can be more than one acl in a single request; those richacls need to be
richacl_put() at the end of the request instead of kfree()d, so keep a
list of acls in compoundargs for that.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Acked-by: J. Bruce Fields <bfields@redhat.com>
---
fs/nfsd/nfs4xdr.c | 27 +++++++++++++++++++++++++++
fs/nfsd/xdr4.h | 6 ++++++
2 files changed, 33 insertions(+)
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 51c9e9c..b8db5a7 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -40,6 +40,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"
@@ -196,6 +197,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.
@@ -4437,6 +4456,13 @@ 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;
}
@@ -4455,6 +4481,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 9f99100..b698585 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -570,6 +570,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;
@@ -579,6 +584,7 @@ struct nfsd4_compoundargs {
__be32 tmp[8];
__be32 * tmpp;
struct svcxdr_tmpbuf *to_free;
+ struct svcxdr_richacl *acls;
struct svc_rqst *rqstp;
--
2.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 01:10 +0200 |
| Subject | [PATCH v10 38/46] richacl: Add support for unmapped identifiers |
| Message-ID | <qixUE-61y-61@gated-at.bofh.it> |
| In reply to | #1244246 |
From: Andreas Gruenbacher <agruenba@redhat.com>
Some remote file systems like nfs may return user or group identifiers
that cannot be mapped to local uids / gids. Allow to represent such
unmapped identifiers in richacls. (We still cannot represent unmapped
owners and owning groups, however.)
In the in-memory representation, the richacl is followed by a list of
NUL-terminated strings, with no padding. Entries with an unmapped
identifier have the RICHACE_UNMAPPED_WHO flag set, and ace->e_id.offs
specifies the offset into this list. Multiple entries can refer to the
same offset.
The xattr representation is similar, but ace->e_id is ignored, and the
list of unmapped identifier strings contains a string for each acl entry
whose RICHACE_UNMAPPED_WHO flag is set.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/richacl_base.c | 139 ++++++++++++++++++++++++++++++++++++++++++++----
fs/richacl_compat.c | 18 +++----
fs/richacl_inode.c | 4 +-
fs/richacl_xattr.c | 69 ++++++++++++++++++++----
include/linux/richacl.h | 33 ++++++++++--
5 files changed, 227 insertions(+), 36 deletions(-)
diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 3a97a82..f88d19b 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -23,22 +23,25 @@
MODULE_LICENSE("GPL");
/**
- * richacl_alloc - allocate a richacl
+ * __richacl_alloc - allocate a richacl
* @count: number of entries
+ * @unmapped_size: size to reserve for unmapped identifiers
*/
struct richacl *
-richacl_alloc(int count, gfp_t gfp)
+__richacl_alloc(unsigned int count, size_t unmapped_size, gfp_t gfp)
{
- size_t size = sizeof(struct richacl) + count * sizeof(struct richace);
+ size_t size = sizeof(struct richacl) + count * sizeof(struct richace) +
+ unmapped_size;
struct richacl *acl = kzalloc(size, gfp);
if (acl) {
atomic_set(&acl->a_base.ba_refcount, 1);
acl->a_count = count;
+ acl->a_unmapped_size = unmapped_size;
}
return acl;
}
-EXPORT_SYMBOL_GPL(richacl_alloc);
+EXPORT_SYMBOL_GPL(__richacl_alloc);
/**
* richacl_clone - create a copy of a richacl
@@ -47,7 +50,8 @@ 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);
+ size_t size = sizeof(struct richacl) + count * sizeof(struct richace) +
+ acl->a_unmapped_size;
struct richacl *dup = kmalloc(size, gfp);
if (dup) {
@@ -59,6 +63,9 @@ richacl_clone(const struct richacl *acl, gfp_t gfp)
/**
* richace_copy - copy an acl entry
+ *
+ * If @from has an unmapped who value (from->e_flags & RICHACE_UNMAPPED_WHO),
+ * it can only be copied within the same acl!
*/
void
richace_copy(struct richace *to, const struct richace *from)
@@ -66,6 +73,82 @@ richace_copy(struct richace *to, const struct richace *from)
memcpy(to, from, sizeof(struct richace));
}
+/**
+ * richacl_add_unmapped_identifier
+ * @pacl: Pointer to an acl
+ * @pace: acl entry within @acl
+ * @who: unmapped identifier
+ * @len: length of @who
+ * @gfp: memory allocation flags
+ *
+ * Add an unmapped identifier to an acl, possibly reallocating the acl.
+ */
+int richacl_add_unmapped_identifier(struct richacl **pacl,
+ struct richace **pace,
+ const char *who,
+ unsigned int len, gfp_t gfp)
+{
+ struct richacl *acl = *pacl;
+ size_t size = sizeof(struct richacl) +
+ acl->a_count * sizeof(struct richace) +
+ acl->a_unmapped_size + len + 1;
+ unsigned int index = *pace - acl->a_entries;
+
+ acl = krealloc(*pacl, size, gfp);
+ if (acl) {
+ char *unmapped = (char *)(acl->a_entries + acl->a_count);
+ struct richace *ace = acl->a_entries + index;
+
+ ace->e_flags |= RICHACE_UNMAPPED_WHO;
+ ace->e_flags &= ~RICHACE_SPECIAL_WHO;
+ ace->e_id.offs = acl->a_unmapped_size;
+ memcpy(unmapped + ace->e_id.offs, who, len);
+ unmapped[ace->e_id.offs + len] = 0;
+ acl->a_unmapped_size += len + 1;
+ *pace = ace;
+ *pacl = acl;
+ return 0;
+ }
+ return -1;
+}
+EXPORT_SYMBOL_GPL(richacl_add_unmapped_identifier);
+
+/**
+ * richace_unmapped_identifier - get unmapped identifier
+ * @acl: acl containing @ace
+ * @ace: acl entry
+ *
+ * Get the unmapped identifier of @ace as a NUL-terminated string, or NULL if
+ * @ace doesn't have an unmapped identifier.
+ */
+const char *richace_unmapped_identifier(const struct richace *ace,
+ const struct richacl *acl)
+{
+ const char *unmapped = (char *)(acl->a_entries + acl->a_count);
+
+ if (!(ace->e_flags & RICHACE_UNMAPPED_WHO))
+ return NULL;
+ return unmapped + ace->e_id.offs;
+}
+EXPORT_SYMBOL(richace_unmapped_identifier);
+
+/**
+ * richacl_has_unmapped_identifiers
+ *
+ * Check if an acl has unmapped identifiers.
+ */
+bool richacl_has_unmapped_identifiers(struct richacl *acl)
+{
+ struct richace *ace;
+
+ richacl_for_each_entry(ace, acl) {
+ if (ace->e_flags & RICHACE_UNMAPPED_WHO)
+ return true;
+ }
+ return false;
+}
+EXPORT_SYMBOL_GPL(richacl_has_unmapped_identifiers);
+
/*
* richacl_mask_to_mode - compute the file permission bits from mask
* @mask: %RICHACE_* permission mask
@@ -214,7 +297,7 @@ static unsigned int richacl_allowed_to_who(struct richacl *acl,
richacl_for_each_entry_reverse(ace, acl) {
if (richace_is_inherit_only(ace))
continue;
- if (richace_is_same_identifier(ace, who) ||
+ if (richace_is_same_identifier(acl, ace, who) ||
richace_is_everyone(ace)) {
if (richace_is_allow(ace))
allowed |= ace->e_mask;
@@ -505,45 +588,72 @@ richacl_inherit(const struct richacl *dir_acl, int isdir)
const struct richace *dir_ace;
struct richacl *acl = NULL;
struct richace *ace;
- int count = 0;
+ unsigned int count = 0, unmapped_size = 0, offset = 0;
+ const char *dir_unmapped;
+ char *unmapped;
if (isdir) {
richacl_for_each_entry(dir_ace, dir_acl) {
if (!richace_is_inheritable(dir_ace))
continue;
+
count++;
+ dir_unmapped =
+ richace_unmapped_identifier(dir_ace, dir_acl);
+ if (dir_unmapped)
+ unmapped_size += strlen(dir_unmapped) + 1;
}
if (!count)
return NULL;
- acl = richacl_alloc(count, GFP_KERNEL);
+ acl = __richacl_alloc(count, unmapped_size, GFP_KERNEL);
if (!acl)
return ERR_PTR(-ENOMEM);
ace = acl->a_entries;
+ unmapped = (char *)(acl->a_entries + acl->a_count);
richacl_for_each_entry(dir_ace, dir_acl) {
if (!richace_is_inheritable(dir_ace))
continue;
+
richace_copy(ace, dir_ace);
if (dir_ace->e_flags & RICHACE_NO_PROPAGATE_INHERIT_ACE)
ace->e_flags &= ~RICHACE_INHERITANCE_FLAGS;
else if (!(dir_ace->e_flags & RICHACE_DIRECTORY_INHERIT_ACE))
ace->e_flags |= RICHACE_INHERIT_ONLY_ACE;
+
+ dir_unmapped =
+ richace_unmapped_identifier(dir_ace, dir_acl);
+ if (dir_unmapped) {
+ size_t sz = strlen(dir_unmapped) + 1;
+
+ ace->e_id.offs = offset;
+ memcpy(unmapped, dir_unmapped, sz);
+ unmapped += sz;
+ offset += sz;
+ }
ace++;
}
} else {
richacl_for_each_entry(dir_ace, dir_acl) {
if (!(dir_ace->e_flags & RICHACE_FILE_INHERIT_ACE))
continue;
+
count++;
+ dir_unmapped =
+ richace_unmapped_identifier(dir_ace, dir_acl);
+ if (dir_unmapped)
+ unmapped_size += strlen(dir_unmapped) + 1;
}
if (!count)
return NULL;
- acl = richacl_alloc(count, GFP_KERNEL);
+ acl = __richacl_alloc(count, unmapped_size, GFP_KERNEL);
if (!acl)
return ERR_PTR(-ENOMEM);
ace = acl->a_entries;
+ unmapped = (char *)(acl->a_entries + acl->a_count);
richacl_for_each_entry(dir_ace, dir_acl) {
if (!(dir_ace->e_flags & RICHACE_FILE_INHERIT_ACE))
continue;
+
richace_copy(ace, dir_ace);
ace->e_flags &= ~RICHACE_INHERITANCE_FLAGS;
/*
@@ -551,6 +661,17 @@ richacl_inherit(const struct richacl *dir_acl, int isdir)
* non-directories, so clear it.
*/
ace->e_mask &= ~RICHACE_DELETE_CHILD;
+
+ dir_unmapped =
+ richace_unmapped_identifier(dir_ace, dir_acl);
+ if (dir_unmapped) {
+ size_t sz = strlen(dir_unmapped) + 1;
+
+ ace->e_id.offs = offset;
+ memcpy(unmapped, dir_unmapped, sz);
+ unmapped += sz;
+ offset += sz;
+ }
ace++;
}
}
diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
index e513958..c5c670e 100644
--- a/fs/richacl_compat.c
+++ b/fs/richacl_compat.c
@@ -71,11 +71,13 @@ richacl_insert_entry(struct richacl_alloc *alloc, struct richace **ace)
{
struct richacl *acl = alloc->acl;
unsigned int index = *ace - acl->a_entries;
- size_t tail_size = (acl->a_count - index) * sizeof(struct richace);
+ size_t tail_size = (acl->a_count - index) * sizeof(struct richace) +
+ acl->a_unmapped_size;
if (alloc->count == acl->a_count) {
size_t new_size = sizeof(struct richacl) +
- (acl->a_count + 1) * sizeof(struct richace);
+ (acl->a_count + 1) * sizeof(struct richace) +
+ acl->a_unmapped_size;
acl = krealloc(acl, new_size, GFP_KERNEL);
if (!acl)
@@ -103,10 +105,6 @@ struct richace *richacl_append_entry(struct richacl_alloc *alloc)
struct richacl *acl = alloc->acl;
struct richace *ace = acl->a_entries + acl->a_count;
- if (alloc->count > alloc->acl->a_count) {
- acl->a_count++;
- return ace;
- }
return richacl_insert_entry(alloc, &ace) ? NULL : ace;
}
EXPORT_SYMBOL_GPL(richacl_append_entry);
@@ -261,12 +259,12 @@ __richacl_propagate_everyone(struct richacl_alloc *alloc, struct richace *who,
if (richace_is_inherit_only(ace))
continue;
if (richace_is_allow(ace)) {
- if (richace_is_same_identifier(ace, who)) {
+ if (richace_is_same_identifier(acl, ace, who)) {
allow &= ~ace->e_mask;
allow_last = ace;
}
} else if (richace_is_deny(ace)) {
- if (richace_is_same_identifier(ace, who))
+ if (richace_is_same_identifier(acl, ace, who))
allow &= ~ace->e_mask;
else if (allow & ace->e_mask)
allow_last = NULL;
@@ -613,7 +611,7 @@ __richacl_isolate_who(struct richacl_alloc *alloc, struct richace *who,
richacl_for_each_entry(ace, acl) {
if (richace_is_inherit_only(ace))
continue;
- if (richace_is_same_identifier(ace, who))
+ if (richace_is_same_identifier(acl, ace, who))
deny &= ~ace->e_mask;
}
if (!deny)
@@ -629,7 +627,7 @@ __richacl_isolate_who(struct richacl_alloc *alloc, struct richace *who,
if (richace_is_inherit_only(ace))
continue;
if (richace_is_deny(ace)) {
- if (richace_is_same_identifier(ace, who))
+ if (richace_is_same_identifier(acl, ace, who))
return richace_change_mask(alloc, &ace,
ace->e_mask | deny);
} else if (richace_is_allow(ace) &&
diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
index b88a2f1..2f50389 100644
--- a/fs/richacl_inode.c
+++ b/fs/richacl_inode.c
@@ -161,8 +161,10 @@ richacl_permission(struct inode *inode, const struct richacl *acl,
} else if (richace_is_unix_group(ace)) {
if (!in_group_p(ace->e_id.gid))
continue;
- } else
+ } else if (richace_is_everyone(ace))
goto entry_matches_everyone;
+ else
+ continue;
/*
* Apply the group file mask to entries other than owner@ and
diff --git a/fs/richacl_xattr.c b/fs/richacl_xattr.c
index dc529dc..7ae5348 100644
--- a/fs/richacl_xattr.c
+++ b/fs/richacl_xattr.c
@@ -35,7 +35,8 @@ richacl_from_xattr(struct user_namespace *user_ns,
const struct richace_xattr *xattr_ace = (void *)(xattr_acl + 1);
struct richacl *acl;
struct richace *ace;
- int count;
+ unsigned int count, offset;
+ char *unmapped;
if (size < sizeof(*xattr_acl) ||
xattr_acl->a_version != RICHACL_XATTR_VERSION ||
@@ -45,10 +46,11 @@ richacl_from_xattr(struct user_namespace *user_ns,
count = le16_to_cpu(xattr_acl->a_count);
if (count > RICHACL_XATTR_MAX_COUNT)
return ERR_PTR(-EINVAL);
- if (size != count * sizeof(*xattr_ace))
+ if (size < count * sizeof(*xattr_ace))
return ERR_PTR(-EINVAL);
+ size -= count * sizeof(*xattr_ace);
- acl = richacl_alloc(count, GFP_NOFS);
+ acl = __richacl_alloc(count, size, GFP_NOFS);
if (!acl)
return ERR_PTR(-ENOMEM);
@@ -63,6 +65,16 @@ richacl_from_xattr(struct user_namespace *user_ns,
if (acl->a_other_mask & ~RICHACE_VALID_MASK)
goto fail_einval;
+ unmapped = (char *)(acl->a_entries + count);
+ if (size) {
+ char *xattr_unmapped = (char *)(xattr_ace + count);
+
+ if (xattr_unmapped[size - 1] != 0)
+ goto fail_einval;
+ memcpy(unmapped, xattr_unmapped, size);
+ }
+ offset = 0;
+
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);
@@ -74,6 +86,15 @@ richacl_from_xattr(struct user_namespace *user_ns,
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_UNMAPPED_WHO) {
+ size_t sz;
+
+ if (offset == size)
+ goto fail_einval;
+ ace->e_id.offs = offset;
+ sz = strlen(unmapped) + 1;
+ unmapped += sz;
+ offset += sz;
} else if (ace->e_flags & RICHACE_IDENTIFIER_GROUP) {
u32 id = le32_to_cpu(xattr_ace->e_id);
@@ -90,10 +111,12 @@ richacl_from_xattr(struct user_namespace *user_ns,
if (ace->e_type > RICHACE_ACCESS_DENIED_ACE_TYPE ||
(ace->e_mask & ~RICHACE_VALID_MASK))
goto fail_einval;
-
xattr_ace++;
}
+ if (offset != size)
+ goto fail_einval;
+
return acl;
fail_einval:
@@ -109,8 +132,15 @@ size_t
richacl_xattr_size(const struct richacl *acl)
{
size_t size = sizeof(struct richacl_xattr);
+ const struct richace *ace;
size += sizeof(struct richace_xattr) * acl->a_count;
+ richacl_for_each_entry(ace, acl) {
+ const char *unmapped = richace_unmapped_identifier(ace, acl);
+
+ if (unmapped)
+ size += strlen(unmapped) + 1;
+ }
return size;
}
EXPORT_SYMBOL_GPL(richacl_xattr_size);
@@ -129,6 +159,7 @@ richacl_to_xattr(struct user_namespace *user_ns,
struct richace_xattr *xattr_ace;
const struct richace *ace;
size_t real_size;
+ char *xattr_unmapped;
real_size = richacl_xattr_size(acl);
if (!buffer)
@@ -145,18 +176,33 @@ richacl_to_xattr(struct user_namespace *user_ns,
xattr_acl->a_other_mask = cpu_to_le32(acl->a_other_mask);
xattr_ace = (void *)(xattr_acl + 1);
+ xattr_unmapped = (char *)(xattr_ace + acl->a_count);
richacl_for_each_entry(ace, acl) {
+ const char *who;
+
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));
+ else {
+ who = richace_unmapped_identifier(ace, acl);
+ if (who) {
+ size_t sz = strlen(who) + 1;
+
+ xattr_ace->e_id = 0;
+ memcpy(xattr_unmapped, who, sz);
+ xattr_unmapped += sz;
+ } else if (ace->e_flags & RICHACE_IDENTIFIER_GROUP) {
+ u32 id = from_kgid(user_ns, ace->e_id.gid);
+
+ xattr_ace->e_id = cpu_to_le32(id);
+ } else {
+ u32 id = from_kuid(user_ns, ace->e_id.uid);
+
+ xattr_ace->e_id = cpu_to_le32(id);
+ }
+ }
xattr_ace++;
}
return real_size;
@@ -262,7 +308,8 @@ static void richacl_fix_xattr_userns(
return;
count = size / sizeof(*xattr_ace);
for (; count; count--, xattr_ace++) {
- if (xattr_ace->e_flags & cpu_to_le16(RICHACE_SPECIAL_WHO))
+ if (xattr_ace->e_flags & cpu_to_le16(RICHACE_SPECIAL_WHO |
+ RICHACE_UNMAPPED_WHO))
continue;
if (xattr_ace->e_flags &
cpu_to_le16(RICHACE_IDENTIFIER_GROUP)) {
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 708926f..95f22e5 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -29,6 +29,7 @@ struct richace {
kuid_t uid;
kgid_t gid;
unsigned int special;
+ unsigned short offs; /* unmapped offset */
} e_id;
};
@@ -39,6 +40,7 @@ struct richacl {
unsigned int a_other_mask;
unsigned short a_count;
unsigned short a_flags;
+ unsigned short a_unmapped_size;
struct richace a_entries[0];
};
@@ -77,6 +79,7 @@ struct richacl {
#define RICHACE_INHERIT_ONLY_ACE 0x0008
#define RICHACE_IDENTIFIER_GROUP 0x0040
#define RICHACE_INHERITED_ACE 0x0080
+#define RICHACE_UNMAPPED_WHO 0x2000
#define RICHACE_SPECIAL_WHO 0x4000
#define RICHACE_VALID_FLAGS ( \
@@ -86,6 +89,7 @@ struct richacl {
RICHACE_INHERIT_ONLY_ACE | \
RICHACE_IDENTIFIER_GROUP | \
RICHACE_INHERITED_ACE | \
+ RICHACE_UNMAPPED_WHO | \
RICHACE_SPECIAL_WHO)
#define RICHACE_INHERITANCE_FLAGS ( \
@@ -310,14 +314,28 @@ richace_is_deny(const struct richace *ace)
* richace_is_same_identifier - are both identifiers the same?
*/
static inline bool
-richace_is_same_identifier(const struct richace *a, const struct richace *b)
+richace_is_same_identifier(const struct richacl *acl,
+ const struct richace *ace1,
+ const struct richace *ace2)
{
- return !((a->e_flags ^ b->e_flags) &
- (RICHACE_SPECIAL_WHO | RICHACE_IDENTIFIER_GROUP)) &&
- !memcmp(&a->e_id, &b->e_id, sizeof(a->e_id));
+ const char *unmapped = (char *)(acl->a_entries + acl->a_count);
+
+ return !((ace1->e_flags ^ ace2->e_flags) &
+ (RICHACE_SPECIAL_WHO |
+ RICHACE_IDENTIFIER_GROUP |
+ RICHACE_UNMAPPED_WHO)) &&
+ ((ace1->e_flags & RICHACE_UNMAPPED_WHO) ?
+ !strcmp(unmapped + ace1->e_id.offs,
+ unmapped + ace2->e_id.offs) :
+ !memcmp(&ace1->e_id, &ace2->e_id, sizeof(ace1->e_id)));
+}
+
+extern struct richacl *__richacl_alloc(unsigned int, size_t, gfp_t);
+static inline struct richacl *richacl_alloc(unsigned int count, gfp_t gfp)
+{
+ return __richacl_alloc(count, 0, gfp);
}
-extern struct richacl *richacl_alloc(int, gfp_t);
extern struct richacl *richacl_clone(const struct richacl *, gfp_t);
extern void richace_copy(struct richace *, const struct richace *);
extern int richacl_masks_to_mode(const struct richacl *);
@@ -327,6 +345,11 @@ extern void richacl_compute_max_masks(struct richacl *);
extern struct richacl *__richacl_chmod(struct richacl *, umode_t);
extern int richacl_equiv_mode(const struct richacl *, umode_t *);
extern struct richacl *richacl_inherit(const struct richacl *, int);
+extern int richacl_add_unmapped_identifier(struct richacl **, struct richace **,
+ const char *, unsigned int, gfp_t);
+extern const char *richace_unmapped_identifier(const struct richace *,
+ const struct richacl *);
+extern bool richacl_has_unmapped_identifiers(struct richacl *);
/* richacl_inode.c */
extern int richacl_permission(struct inode *, const struct richacl *, int);
--
2.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Dave Chinner <david@fromorbit.com> |
|---|---|
| Date | 2015-10-12 02:30 +0200 |
| Subject | Re: [PATCH v10 38/46] richacl: Add support for unmapped identifiers |
| Message-ID | <qiza2-7Jb-1@gated-at.bofh.it> |
| In reply to | #1244273 |
On Mon, Oct 12, 2015 at 12:58:49AM +0200, Andreas Gruenbacher wrote:
> From: Andreas Gruenbacher <agruenba@redhat.com>
>
> Some remote file systems like nfs may return user or group identifiers
> that cannot be mapped to local uids / gids. Allow to represent such
> unmapped identifiers in richacls. (We still cannot represent unmapped
> owners and owning groups, however.)
>
> In the in-memory representation, the richacl is followed by a list of
> NUL-terminated strings, with no padding. Entries with an unmapped
> identifier have the RICHACE_UNMAPPED_WHO flag set, and ace->e_id.offs
> specifies the offset into this list. Multiple entries can refer to the
> same offset.
>
> The xattr representation is similar, but ace->e_id is ignored, and the
> list of unmapped identifier strings contains a string for each acl entry
> whose RICHACE_UNMAPPED_WHO flag is set.
>
> Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
....
> +/**
> + * richacl_has_unmapped_identifiers
> + *
> + * Check if an acl has unmapped identifiers.
> + */
> +bool richacl_has_unmapped_identifiers(struct richacl *acl)
> +{
> + struct richace *ace;
> +
> + richacl_for_each_entry(ace, acl) {
> + if (ace->e_flags & RICHACE_UNMAPPED_WHO)
> + return true;
> + }
> + return false;
> +}
> +EXPORT_SYMBOL_GPL(richacl_has_unmapped_identifiers);
This was used by the XFS support patch earlier in the series. Bisect
problem here...
Cheers,
Dave.
--
Dave Chinner
david@fromorbit.com
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Grünbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 04:00 +0200 |
| Subject | Re: [PATCH v10 38/46] richacl: Add support for unmapped identifiers |
| Message-ID | <qiAz7-1bO-7@gated-at.bofh.it> |
| In reply to | #1244313 |
2015-10-12 2:22 GMT+02:00 Dave Chinner <david@fromorbit.com>: > This was used by the XFS support patch earlier in the series. Bisect > problem here... Yes, I'll fix that up as well. Thanks, Andreas -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 01:10 +0200 |
| Subject | [PATCH v10 29/46] richacl: Set the other permissions to the other mask |
| Message-ID | <qixUE-61y-67@gated-at.bofh.it> |
| In reply to | #1244246 |
From: Andreas Gruenbacher <agruenba@redhat.com>
Change the acl so that everyone@ is granted the permissions set in the
other mask.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: J. Bruce Fields <bfields@redhat.com>
---
fs/richacl_compat.c | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
index 16deeb4..21ee31e 100644
--- a/fs/richacl_compat.c
+++ b/fs/richacl_compat.c
@@ -462,3 +462,44 @@ richacl_set_owner_permissions(struct richacl_alloc *alloc)
}
return 0;
}
+
+/**
+ * richacl_set_other_permissions - set the other permissions to the other mask
+ * @alloc: acl and number of allocated entries
+ * @added: permissions added for everyone@
+ *
+ * Change the acl so that everyone@ is granted the permissions set in the other
+ * mask. This leaves at most one efective everyone@ allow entry at the end of
+ * the acl. If everyone@ end up being granted additional permissions, these
+ * permissions are returned in @added.
+ */
+static int
+richacl_set_other_permissions(struct richacl_alloc *alloc, unsigned int *added)
+{
+ struct richacl *acl = alloc->acl;
+ unsigned int x = RICHACE_POSIX_ALWAYS_ALLOWED;
+ unsigned int other_mask = acl->a_other_mask & ~x;
+ struct richace *ace;
+
+ if (!(other_mask &&
+ (acl->a_flags & RICHACL_WRITE_THROUGH)))
+ return 0;
+
+ *added = other_mask;
+ ace = acl->a_entries + acl->a_count - 1;
+ if (acl->a_count == 0 ||
+ !richace_is_everyone(ace) ||
+ richace_is_inherit_only(ace)) {
+ ace++;
+ if (richacl_insert_entry(alloc, &ace))
+ return -1;
+ 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;
+ } else {
+ *added &= ~ace->e_mask;
+ richace_change_mask(alloc, &ace, other_mask);
+ }
+ return 0;
+}
--
2.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 01:10 +0200 |
| Subject | [PATCH v10 45/46] nfs: Add support for the v4.1 dacl attribute |
| Message-ID | <qixUE-61y-59@gated-at.bofh.it> |
| In reply to | #1244246 |
From: Andreas Gruenbacher <agruenba@redhat.com>
The dacl attribute includes Automatic Inheritance flags not supported by
the acl attribute. it is only supported in NFS version 4.1 and higher.
On systems where NFS version 4.0 is still the default, an additional
mount option is needed:
mount -t nfs4 -o vers=4.1 [...]
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/nfs/nfs4proc.c | 2 +-
fs/nfs/nfs4xdr.c | 55 ++++++++++++++++++++++++++++++++++++++++++-------
include/linux/nfs_xdr.h | 2 +-
3 files changed, 50 insertions(+), 9 deletions(-)
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index a686251..6af222c 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -4556,7 +4556,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 f2507d7..d855c6b 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -1661,9 +1661,16 @@ 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);
+ }
attrlen_offset = xdr->buf->len;
xdr_reserve_space(xdr, 4); /* to be backfilled later */
@@ -2498,9 +2505,12 @@ static void 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);
@@ -5408,14 +5418,28 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U)))
return -EIO;
- if (likely(bitmap[0] & FATTR4_WORD0_ACL)) {
+
+ if (bitmap[0] & FATTR4_WORD0_ACL) {
+ struct richace *ace;
+
+ if (bitmap[1] & FATTR4_WORD1_DACL)
+ return -EIO;
+
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;
+ }
bitmap[0] &= ~FATTR4_WORD0_ACL;
- } else
+ } else if (!(bitmap[1] & FATTR4_WORD1_DACL)) {
status = -EOPNOTSUPP;
+ goto out;
+ }
status = -EIO;
if (unlikely(bitmap[0]))
@@ -5424,6 +5448,23 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
status = decode_attr_mode(xdr, bitmap, &res->mode);
if (status < 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;
+ bitmap[1] &= ~FATTR4_WORD1_DACL;
+ }
status = 0;
out:
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 337c341..9c2a078 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -695,7 +695,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.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 01:10 +0200 |
| Subject | [PATCH v10 41/46] sunrpc: Add xdr_init_encode_pages |
| Message-ID | <qixUE-61y-73@gated-at.bofh.it> |
| In reply to | #1244246 |
From: Andreas Gruenbacher <agruenba@redhat.com>
Initialize xdr_stream and xdr_buf from a pages array, for encoding into
the pages.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
include/linux/sunrpc/xdr.h | 2 ++
net/sunrpc/xdr.c | 25 +++++++++++++++++++++++++
2 files changed, 27 insertions(+)
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index 70c6b92..2c99cff 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -214,6 +214,8 @@ typedef void (*kxdreproc_t)(void *rqstp, struct xdr_stream *xdr, void *obj);
typedef int (*kxdrdproc_t)(void *rqstp, struct xdr_stream *xdr, void *obj);
extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
+extern void xdr_init_encode_pages(struct xdr_stream *xdr, struct xdr_buf *buf,
+ struct page **pages, unsigned int len);
extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes);
extern void xdr_commit_encode(struct xdr_stream *xdr);
extern void xdr_truncate_encode(struct xdr_stream *xdr, size_t len);
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index 63c1c36..f97b96b 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -483,6 +483,31 @@ void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
EXPORT_SYMBOL_GPL(xdr_init_encode);
/**
+ * xdr_init_encode_pages - Initialize struct xdr_stream for encoding into pages
+ * @xdr: pointer to xdr_stream struct
+ * @buf: pointer to XDR buffer in which to encode data
+ * @pages: pages array in which to encode
+ * @len: maximum length of @buf
+ *
+ * Initialize @xdr and @buf for encoding into the @pages array. If a
+ * page in @pages is NULL, it will be allocated on demand.
+ */
+void xdr_init_encode_pages(struct xdr_stream *xdr, struct xdr_buf *buf,
+ struct page **pages, unsigned int len)
+{
+ memset(buf, 0, sizeof(*buf));
+ buf->pages = pages;
+ buf->page_len = len;
+ buf->buflen = len;
+
+ memset(xdr, 0, sizeof(*xdr));
+ xdr->buf = buf;
+ xdr->iov = buf->head;
+ xdr->page_ptr = pages - 1;
+}
+EXPORT_SYMBOL_GPL(xdr_init_encode_pages);
+
+/**
* xdr_commit_encode - Ensure all data is written to buffer
* @xdr: pointer to xdr_stream
*
--
2.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 01:10 +0200 |
| Subject | [PATCH v10 46/46] richacl: uapi header split |
| Message-ID | <qixUE-61y-75@gated-at.bofh.it> |
| In reply to | #1244246 |
From: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
include/linux/richacl.h | 119 +++++--------------------------------
include/linux/richacl_xattr.h | 17 +-----
include/uapi/linux/Kbuild | 2 +
include/uapi/linux/richacl.h | 111 ++++++++++++++++++++++++++++++++++
include/uapi/linux/richacl_xattr.h | 43 ++++++++++++++
5 files changed, 173 insertions(+), 119 deletions(-)
create mode 100644 include/uapi/linux/richacl.h
create mode 100644 include/uapi/linux/richacl_xattr.h
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 95f22e5..de0b327 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -17,9 +17,7 @@
#ifndef __RICHACL_H
#define __RICHACL_H
-#define RICHACE_OWNER_SPECIAL_ID 0
-#define RICHACE_GROUP_SPECIAL_ID 1
-#define RICHACE_EVERYONE_SPECIAL_ID 2
+#include <uapi/linux/richacl.h>
struct richace {
unsigned short e_type;
@@ -44,43 +42,12 @@ struct richacl {
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_flags values */
-#define RICHACL_AUTO_INHERIT 0x01
-#define RICHACL_PROTECTED 0x02
-#define RICHACL_DEFAULTED 0x04
-#define RICHACL_WRITE_THROUGH 0x40
-#define RICHACL_MASKED 0x80
-
#define RICHACL_VALID_FLAGS ( \
- RICHACL_AUTO_INHERIT | \
- RICHACL_PROTECTED | \
- RICHACL_DEFAULTED | \
- RICHACL_WRITE_THROUGH | \
- 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_INHERITED_ACE 0x0080
-#define RICHACE_UNMAPPED_WHO 0x2000
-#define RICHACE_SPECIAL_WHO 0x4000
+ RICHACL_AUTO_INHERIT | \
+ RICHACL_PROTECTED | \
+ RICHACL_DEFAULTED | \
+ RICHACL_WRITE_THROUGH | \
+ RICHACL_MASKED)
#define RICHACE_VALID_FLAGS ( \
RICHACE_FILE_INHERIT_ACE | \
@@ -99,27 +66,6 @@ struct richacl {
RICHACE_INHERIT_ONLY_ACE | \
RICHACE_INHERITED_ACE )
-/* 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 | \
@@ -139,49 +85,16 @@ 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)
+#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--)
+
/**
* richacl_get - grab another reference to a richacl handle
*/
diff --git a/include/linux/richacl_xattr.h b/include/linux/richacl_xattr.h
index dac4e32..4ba54bd 100644
--- a/include/linux/richacl_xattr.h
+++ b/include/linux/richacl_xattr.h
@@ -17,24 +17,9 @@
#ifndef __RICHACL_XATTR_H
#define __RICHACL_XATTR_H
+#include <uapi/linux/richacl_xattr.h>
#include <linux/richacl.h>
-struct richace_xattr {
- __le16 e_type;
- __le16 e_flags;
- __le32 e_mask;
- __le32 e_id;
-};
-
-struct richacl_xattr {
- unsigned char a_version;
- unsigned char a_flags;
- __le16 a_count;
- __le32 a_owner_mask;
- __le32 a_group_mask;
- __le32 a_other_mask;
-};
-
#define RICHACL_XATTR_VERSION 0
#define RICHACL_XATTR_MAX_COUNT \
((XATTR_SIZE_MAX - sizeof(struct richacl_xattr)) / \
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index f7b2db4..18ad070 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -348,6 +348,8 @@ header-y += reboot.h
header-y += reiserfs_fs.h
header-y += reiserfs_xattr.h
header-y += resource.h
+header-y += richacl.h
+header-y += richacl_xattr.h
header-y += rfkill.h
header-y += romfs_fs.h
header-y += rose.h
diff --git a/include/uapi/linux/richacl.h b/include/uapi/linux/richacl.h
new file mode 100644
index 0000000..6887f88
--- /dev/null
+++ b/include/uapi/linux/richacl.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2006, 2010 Novell, Inc.
+ * Copyright (C) 2015 Red Hat, Inc.
+ * Written by Andreas Gruenbacher <agruen@kernel.org>
+ *
+ * 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 __UAPI_RICHACL_H
+#define __UAPI_RICHACL_H
+
+/* a_flags values */
+#define RICHACL_AUTO_INHERIT 0x01
+#define RICHACL_PROTECTED 0x02
+#define RICHACL_DEFAULTED 0x04
+#define RICHACL_WRITE_THROUGH 0x40
+#define RICHACL_MASKED 0x80
+
+/* 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_INHERITED_ACE 0x0080
+#define RICHACE_UNMAPPED_WHO 0x2000
+#define RICHACE_SPECIAL_WHO 0x4000
+
+/* 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
+
+/* e_id values */
+#define RICHACE_OWNER_SPECIAL_ID 0
+#define RICHACE_GROUP_SPECIAL_ID 1
+#define RICHACE_EVERYONE_SPECIAL_ID 2
+
+/*
+ * The POSIX permissions are supersets of the following richacl 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 richacl 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)
+
+#endif /* __UAPI_RICHACL_H */
diff --git a/include/uapi/linux/richacl_xattr.h b/include/uapi/linux/richacl_xattr.h
new file mode 100644
index 0000000..6f96bc0
--- /dev/null
+++ b/include/uapi/linux/richacl_xattr.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2006, 2010 Novell, Inc.
+ * Copyright (C) 2015 Red Hat, Inc.
+ * Written by Andreas Gruenbacher <agruen@kernel.org>
+ *
+ * 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 __UAPI_RICHACL_XATTR_H
+#define __UAPI_RICHACL_XATTR_H
+
+#include <linux/types.h>
+#include <linux/xattr.h>
+
+struct richace_xattr {
+ __le16 e_type;
+ __le16 e_flags;
+ __le32 e_mask;
+ __le32 e_id;
+};
+
+struct richacl_xattr {
+ unsigned char a_version;
+ unsigned char a_flags;
+ __le16 a_count;
+ __le32 a_owner_mask;
+ __le32 a_group_mask;
+ __le32 a_other_mask;
+};
+
+#define RICHACL_XATTR_MAX_COUNT \
+ ((XATTR_SIZE_MAX - sizeof(struct richacl_xattr)) / \
+ sizeof(struct richace_xattr))
+
+#endif /* __UAPI_RICHACL_XATTR_H */
--
2.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 01:10 +0200 |
| Subject | [PATCH v10 44/46] nfs: Add richacl support |
| Message-ID | <qixUE-61y-63@gated-at.bofh.it> |
| In reply to | #1244246 |
From: Andreas Gruenbacher <agruenba@redhat.com>
Add support for the "system.richacl" xattr in nfs. The existing
"system.nfs4_acl" xattr on nfs doesn't map user and group names to uids
and gids; the "system.richacl" xattr does, and only keeps the
on-the-wire names when there is no mapping. This allows to copy
permissions across different file systems.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/nfs/inode.c | 3 -
fs/nfs/nfs4proc.c | 698 +++++++++++++++++++++++++++++++++-------------
fs/nfs/nfs4xdr.c | 179 ++++++++++--
fs/nfs/super.c | 4 +-
include/linux/nfs_fs.h | 1 -
include/linux/nfs_fs_sb.h | 2 +
include/linux/nfs_xdr.h | 9 +-
7 files changed, 673 insertions(+), 223 deletions(-)
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 326d9e1..843d15d 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1852,9 +1852,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 eec5c4c..a686251 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -55,6 +55,9 @@
#include <linux/xattr.h>
#include <linux/utsname.h>
#include <linux/freezer.h>
+#include <linux/richacl.h>
+#include <linux/richacl_xattr.h>
+#include <linux/nfs4acl.h>
#include "nfs4_fs.h"
#include "delegation.h"
@@ -2982,15 +2985,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)
@@ -4518,45 +4524,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;
@@ -4564,66 +4536,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);
}
/*
@@ -4636,121 +4551,269 @@ 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;
+ 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;
+ 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_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 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, struct richacl *acl)
+{
+ const char *who;
+ __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;
+ xdr_encode_opaque(p, who, len);
+ return 0;
+ } else {
+ who = richace_unmapped_identifier(ace, acl);
+ if (who) {
+ unsigned int len = strlen(who);
+
+ p = xdr_reserve_space(xdr, 4 + len);
+ if (!p)
+ return -EIO;
+ xdr_encode_opaque(p, who, len);
+ return 0;
+ } else 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
+nfs4_encode_acl(struct page **pages, unsigned int len, struct richacl *acl,
+ const struct nfs_server *server)
+{
+ int minorversion = server->nfs_client->cl_minorversion;
+ unsigned int ace_mask = nfs4_ace_mask(minorversion);
+ struct xdr_stream xdr;
+ struct xdr_buf buf;
+ __be32 *p;
+ struct richace *ace;
+
+ /* Reject acls not understood by the server */
+ if (server->attr_bitmask[1] & FATTR4_WORD1_DACL) {
+ BUILD_BUG_ON(NFS4_ACE_MASK_ALL != RICHACE_VALID_MASK);
+ } else {
+ if (acl->a_flags)
+ return -EINVAL;
+ richacl_for_each_entry(ace, acl) {
+ if (ace->e_flags & RICHACE_INHERITED_ACE)
+ return -EINVAL;
+ }
+ }
+ richacl_for_each_entry(ace, acl) {
+ if (ace->e_mask & ~ace_mask)
+ return -EINVAL;
+ }
+
+ xdr_init_encode_pages(&xdr, &buf, pages, len);
+
+ if (server->attr_bitmask[1] & FATTR4_WORD1_DACL) {
+ p = xdr_reserve_space(&xdr, 4);
+ if (!p)
+ goto fail;
+ *p = cpu_to_be32(acl ? acl->a_flags : 0);
+ }
+
+ p = xdr_reserve_space(&xdr, 4);
+ if (!p)
+ goto fail;
+ if (!acl) {
+ *p++ = cpu_to_be32(0);
+ return buf.len;
+ }
+ *p++ = cpu_to_be32(acl->a_count);
+
+ richacl_for_each_entry(ace, acl) {
+ 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 | RICHACE_UNMAPPED_WHO));
+ *p++ = cpu_to_be32(ace->e_mask & NFS4_ACE_MASK_ALL);
+ if (nfs4_encode_ace_who(&xdr, server, ace, acl) != 0)
+ goto fail;
+ }
+
+ return buf.len;
+
+fail:
+ return -ENOMEM;
+}
+
+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_pages = pages,
- .acl_len = buflen,
};
struct nfs_setaclres res;
struct rpc_message msg = {
@@ -4758,16 +4821,20 @@ 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;
+
+ ret = nfs4_encode_acl(pages, NFS4ACL_SIZE_MAX, acl, server);
+ if (ret < 0) {
+ for (i = 0; i < ARRAY_SIZE(pages) && pages[i]; i++)
+ put_page(pages[i]);
+ return ret;
+ }
+ arg.acl_len = ret;
+
nfs4_inode_return_delegation(inode);
ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
@@ -4775,8 +4842,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; i < ARRAY_SIZE(pages) && pages[i]; i++)
+ put_page(pages[i]);
/*
* Acl update can result in inode attribute update.
@@ -4790,12 +4857,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);
@@ -6257,34 +6324,283 @@ nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_state *lsp)
rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, data);
}
+static int nfs4_xattr_set_richacl(struct dentry *dentry, const char *key,
+ const void *buf, size_t buflen,
+ int flags, int handler_flags)
+{
+ struct inode *inode = d_inode(dentry);
+ struct richacl *acl;
+ int error;
+
+ if (strcmp(key, "") != 0)
+ return -EINVAL;
+
+ if (buf) {
+ acl = richacl_from_xattr(&init_user_ns, buf, buflen);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ error = richacl_apply_masks(&acl, inode->i_uid);
+ } 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(inode->i_mode);
+ error = 0;
+ }
+
+ if (!error)
+ error = nfs4_proc_set_acl(inode, acl);
+ richacl_put(acl);
+ return error;
+}
+
+static int nfs4_xattr_get_richacl(struct dentry *dentry, const char *key,
+ void *buf, size_t buflen, int handler_flags)
+{
+ struct inode *inode = d_inode(dentry);
+ struct richacl *acl;
+ int error;
+ umode_t mode = inode->i_mode & S_IFMT;
+
+ if (strcmp(key, "") != 0)
+ return -EINVAL;
+
+ acl = nfs4_proc_get_acl(inode);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ if (acl == NULL)
+ return -ENODATA;
+ error = -ENODATA;
+ if (richacl_equiv_mode(acl, &mode) == 0 &&
+ ((mode ^ inode->i_mode) & S_IRWXUGO) == 0)
+ goto out;
+ error = richacl_to_xattr(&init_user_ns, acl, buf, buflen);
+out:
+ richacl_put(acl);
+ return error;
+}
+
+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)
+{
+ struct nfs_server *server = NFS_SERVER(d_inode(dentry));
+ size_t len = sizeof(XATTR_NAME_RICHACL);
+
+ if (!(server->caps & (NFS_CAP_ALLOW_ACLS | NFS_CAP_DENY_ACLS)))
+ return 0;
+
+ if (list && len <= list_len)
+ memcpy(list, XATTR_NAME_RICHACL, len);
+ return len;
+}
+
#define XATTR_NAME_NFSV4_ACL "system.nfs4_acl"
+static int richacl_to_nfs4_acl(struct nfs_server *server,
+ const struct richacl *acl,
+ void *buf, size_t buflen)
+{
+ const struct richace *ace;
+ __be32 *p = buf;
+ size_t size = 0;
+
+ size += sizeof(*p);
+ if (buflen >= size)
+ *p++ = cpu_to_be32(acl->a_count);
+
+ richacl_for_each_entry(ace, acl) {
+ char who_buf[IDMAP_NAMESZ];
+ const char *who = who_buf;
+ int who_len;
+
+ size += 3 * sizeof(*p);
+ if (buflen >= size) {
+ *p++ = cpu_to_be32(ace->e_type);
+ *p++ = cpu_to_be32(ace->e_flags &
+ ~(RICHACE_INHERITED_ACE |
+ RICHACE_UNMAPPED_WHO |
+ RICHACE_SPECIAL_WHO));
+ *p++ = cpu_to_be32(ace->e_mask);
+ }
+
+ if (richace_is_unix_user(ace)) {
+ who_len = nfs_map_uid_to_name(server, ace->e_id.uid,
+ who_buf, sizeof(who_buf));
+ if (who_len < 0)
+ return -EIO;
+ } else if (richace_is_unix_group(ace)) {
+ who_len = nfs_map_gid_to_group(server, ace->e_id.gid,
+ who_buf, sizeof(who_buf));
+ if (who_len < 0)
+ return -EIO;
+ } else if (ace->e_flags & RICHACE_SPECIAL_WHO) {
+ if (!nfs4acl_special_id_to_who(ace->e_id.special,
+ &who, &who_len))
+ return -EIO;
+ } else {
+ who = richace_unmapped_identifier(ace, acl);
+ if (who)
+ who_len = strlen(who);
+ else
+ return -EIO;
+ }
+
+ size += sizeof(*p) + ALIGN(who_len, sizeof(*p));
+ if (buflen >= size) {
+ unsigned int padding = -who_len & (sizeof(*p) - 1);
+
+ *p++ = cpu_to_be32(who_len);
+ memcpy(p, who, who_len);
+ memset((char *)p + who_len, 0, padding);
+ p += DIV_ROUND_UP(who_len, sizeof(*p));
+ }
+ }
+ if (buflen && buflen < size)
+ return -ERANGE;
+ return size;
+}
+
+static struct richacl *richacl_from_nfs4_acl(struct nfs_server *server,
+ const void *buf, size_t buflen)
+{
+ struct richacl *acl = NULL;
+ struct richace *ace;
+ const __be32 *p = buf;
+ int count, err;
+
+ if (buflen < sizeof(*p))
+ return ERR_PTR(-EINVAL);
+ count = be32_to_cpu(*p++);
+ if (count > RICHACL_XATTR_MAX_COUNT)
+ return ERR_PTR(-EINVAL);
+ buflen -= sizeof(*p);
+ acl = richacl_alloc(count, GFP_NOFS);
+ if (!acl)
+ return ERR_PTR(-ENOMEM);
+ richacl_for_each_entry(ace, acl) {
+ u32 who_len, size;
+ int special_id;
+ char *who;
+
+ err = -EINVAL;
+ if (buflen < 4 * sizeof(*p))
+ goto out;
+ ace->e_type = be32_to_cpu(*p++);
+ ace->e_flags = be32_to_cpu(*p++);
+ if (ace->e_flags & (RICHACE_SPECIAL_WHO | RICHACE_UNMAPPED_WHO))
+ goto out;
+ ace->e_mask = be32_to_cpu(*p++);
+ who_len = be32_to_cpu(*p++);
+ buflen -= 4 * sizeof(*p);
+ size = ALIGN(who_len, 4);
+ if (buflen < size || size == 0)
+ goto out;
+ who = (char *)p;
+ special_id = nfs4acl_who_to_special_id(who, who_len);
+ if (special_id >= 0) {
+ ace->e_flags |= RICHACE_SPECIAL_WHO;
+ ace->e_id.special = special_id;
+ } else {
+ bool unmappable;
+
+ if (ace->e_flags & RICHACE_IDENTIFIER_GROUP) {
+ err = nfs_map_group_to_gid(server, who, who_len,
+ &ace->e_id.gid);
+ if (err) {
+ dprintk("%s: nfs_map_group_to_gid "
+ "failed!\n", __func__);
+ goto out;
+ }
+ /* FIXME: nfsidmap doesn't distinguish between
+ group nobody and unmappable groups! */
+ unmappable = gid_eq(ace->e_id.gid,
+ make_kgid(&init_user_ns, 99));
+ } else {
+ err = nfs_map_name_to_uid(server, who, who_len,
+ &ace->e_id.uid);
+ if (err) {
+ dprintk("%s: nfs_map_name_to_gid "
+ "failed!\n", __func__);
+ goto out;
+ }
+ /* FIXME: nfsidmap doesn't distinguish between
+ user nobody and unmappable users! */
+ unmappable = uid_eq(ace->e_id.uid,
+ make_kuid(&init_user_ns, 99));
+ }
+ if (unmappable) {
+ err = -ENOMEM;
+ if (richacl_add_unmapped_identifier(&acl, &ace,
+ who, who_len, GFP_NOFS))
+ goto out;
+ }
+ }
+ p += size / sizeof(*p);
+ buflen -= size;
+ }
+ err = -EINVAL;
+ if (buflen != 0)
+ goto out;
+ err = 0;
+
+out:
+ if (err) {
+ richacl_put(acl);
+ acl = ERR_PTR(err);
+ }
+ return acl;
+}
+
static int nfs4_xattr_set_nfs4_acl(struct dentry *dentry, const char *key,
const void *buf, size_t buflen,
int flags, int type)
{
- if (strcmp(key, "") != 0)
+ struct inode *inode = d_inode(dentry);
+ struct richacl *acl;
+ int error;
+
+ if (!buf || strcmp(key, "") != 0)
return -EINVAL;
- return nfs4_proc_set_acl(d_inode(dentry), buf, buflen);
+ acl = richacl_from_nfs4_acl(NFS_SERVER(inode), (void *)buf, buflen);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ error = nfs4_proc_set_acl(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)
{
+ struct inode *inode = d_inode(dentry);
+ struct richacl *acl;
+ int error;
+
if (strcmp(key, "") != 0)
return -EINVAL;
-
- return nfs4_proc_get_acl(d_inode(dentry), buf, buflen);
+ acl = nfs4_proc_get_acl(inode);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ if (acl == NULL)
+ return -ENODATA;
+ error = richacl_to_nfs4_acl(NFS_SERVER(inode), 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)
{
+ struct nfs_server *server = NFS_SERVER(d_inode(dentry));
size_t len = sizeof(XATTR_NAME_NFSV4_ACL);
- if (!nfs4_server_supports_acls(NFS_SERVER(d_inode(dentry))))
+ if (!(server->caps & (NFS_CAP_ALLOW_ACLS | NFS_CAP_DENY_ACLS)))
return 0;
if (list && len <= list_len)
@@ -8837,6 +9153,13 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
.clone_server = nfs_clone_server,
};
+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,
+};
+
static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
.prefix = XATTR_NAME_NFSV4_ACL,
.list = nfs4_xattr_list_nfs4_acl,
@@ -8845,6 +9168,7 @@ static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
};
const struct xattr_handler *nfs4_xattr_handlers[] = {
+ &nfs4_xattr_richacl_handler,
&nfs4_xattr_nfs4_acl_handler,
#ifdef CONFIG_NFS_V4_SECURITY_LABEL
&nfs4_xattr_nfs4_label_handler,
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index eefed15..f2507d7 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -52,6 +52,10 @@
#include <linux/nfs.h>
#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"
@@ -1650,16 +1654,24 @@ encode_restorefh(struct xdr_stream *xdr, struct compound_hdr *hdr)
static void
encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compound_hdr *hdr)
{
- __be32 *p;
+ int attrlen_offset;
+ __be32 attrlen, *p;
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);
+
+ attrlen_offset = xdr->buf->len;
+ xdr_reserve_space(xdr, 4); /* to be backfilled later */
+
xdr_write_pages(xdr, arg->acl_pages, 0, arg->acl_len);
+
+ attrlen = htonl(xdr->buf->len - attrlen_offset - 4);
+ write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, 4);
}
static void
@@ -2488,7 +2500,7 @@ static void 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);
@@ -5260,24 +5272,135 @@ decode_restorefh(struct xdr_stream *xdr)
return decode_op_hdr(xdr, OP_RESTOREFH);
}
+static int
+nfs4_decode_ace_who(struct richace *ace,
+ const char **unmapped, unsigned int *unmapped_len,
+ 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__);
+ return error;
+ }
+ /* FIXME: nfsidmap doesn't distinguish between group nobody and
+ unmappable groups! */
+ if (gid_eq(ace->e_id.gid, make_kgid(&init_user_ns, 99))) {
+ *unmapped = who;
+ *unmapped_len = len;
+ }
+ } 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;
+ }
+ /* FIXME: nfsidmap doesn't distinguish between user nobody and
+ unmappable users! */
+ if (uid_eq(ace->e_id.uid, make_kuid(&init_user_ns, 99))) {
+ *unmapped = who;
+ *unmapped_len = len;
+ }
+ }
+ return 0;
+}
+
+static struct richacl *
+decode_acl_entries(struct xdr_stream *xdr, const struct nfs_server *server)
+{
+ struct richacl *acl;
+ struct richace *ace;
+ uint32_t count;
+ __be32 *p;
+ int status;
+
+ p = xdr_inline_decode(xdr, 4);
+ if (unlikely(!p))
+ return ERR_PTR(-ENOMEM); /* acl truncated */
+ count = be32_to_cpup(p);
+ if (count > RICHACL_XATTR_MAX_COUNT)
+ return ERR_PTR(-EIO);
+ acl = richacl_alloc(count, GFP_NOFS);
+ if (!acl)
+ return ERR_PTR(-ENOMEM);
+ richacl_for_each_entry(ace, acl) {
+ const char *unmapped = NULL;
+ unsigned int unmapped_len;
+
+ 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 | RICHACE_UNMAPPED_WHO))
+ goto out;
+ ace->e_mask = be32_to_cpup(p++);
+ status = nfs4_decode_ace_who(ace, &unmapped,
+ &unmapped_len, server,
+ xdr);
+ if (status)
+ goto out;
+ if (unmapped) {
+ status = -ENOMEM;
+ if (richacl_add_unmapped_identifier(&acl, &ace,
+ unmapped, unmapped_len,
+ GFP_NOFS))
+ goto out;
+ }
+ }
+ status = 0;
+
+out:
+ if (status) {
+ 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)
{
unsigned int savep;
uint32_t attrlen,
bitmap[3] = {0};
+ struct richacl *acl = NULL;
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 = decode_attr_length(xdr, &attrlen, &savep)) != 0)
@@ -5286,24 +5409,28 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
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
- * 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);
- }
+ acl = decode_acl_entries(xdr, res->server);
+ status = PTR_ERR(acl);
+ if (IS_ERR(acl))
+ goto out;
+ bitmap[0] &= ~FATTR4_WORD0_ACL;
} else
status = -EOPNOTSUPP;
+ status = -EIO;
+ if (unlikely(bitmap[0]))
+ goto out;
+
+ status = decode_attr_mode(xdr, bitmap, &res->mode);
+ if (status < 0)
+ goto out;
+ status = 0;
+
out:
+ if (status == 0)
+ res->acl = acl;
+ else
+ richacl_put(acl);
return status;
}
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 383a027..8ced33d 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -2319,7 +2319,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;
}
@@ -2346,7 +2346,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 c0e9614..b84e194 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 570a7df..6c41668 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -243,5 +243,7 @@ struct nfs_server {
#define NFS_CAP_ALLOCATE (1U << 20)
#define NFS_CAP_DEALLOCATE (1U << 21)
#define NFS_CAP_LAYOUTSTATS (1U << 22)
+#define NFS_CAP_ALLOW_ACLS (1U << 23)
+#define NFS_CAP_DENY_ACLS (1U << 24)
#endif
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 090ade4..337c341 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -683,9 +683,10 @@ struct nfs_setattrargs {
struct nfs_setaclargs {
struct nfs4_sequence_args seq_args;
+ const struct nfs_server * server;
struct nfs_fh * fh;
- size_t acl_len;
struct page ** acl_pages;
+ size_t acl_len;
};
struct nfs_setaclres {
@@ -703,9 +704,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.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Anna Schumaker <Anna.Schumaker@netapp.com> |
|---|---|
| Date | 2015-10-12 16:40 +0200 |
| Subject | Re: [PATCH v10 44/46] nfs: Add richacl support |
| Message-ID | <qiMqB-1Ld-3@gated-at.bofh.it> |
| In reply to | #1244280 |
Hi Andreas,
On 10/11/2015 06:58 PM, Andreas Gruenbacher wrote:
> From: Andreas Gruenbacher <agruenba@redhat.com>
>
> Add support for the "system.richacl" xattr in nfs. The existing
> "system.nfs4_acl" xattr on nfs doesn't map user and group names to uids
> and gids; the "system.richacl" xattr does, and only keeps the
> on-the-wire names when there is no mapping. This allows to copy
> permissions across different file systems.
>
> Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
> ---
> fs/nfs/inode.c | 3 -
> fs/nfs/nfs4proc.c | 698 +++++++++++++++++++++++++++++++++-------------
> fs/nfs/nfs4xdr.c | 179 ++++++++++--
> fs/nfs/super.c | 4 +-
> include/linux/nfs_fs.h | 1 -
> include/linux/nfs_fs_sb.h | 2 +
> include/linux/nfs_xdr.h | 9 +-
> 7 files changed, 673 insertions(+), 223 deletions(-)
>
> diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
> index 326d9e1..843d15d 100644
> --- a/fs/nfs/inode.c
> +++ b/fs/nfs/inode.c
> @@ -1852,9 +1852,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 eec5c4c..a686251 100644
> --- a/fs/nfs/nfs4proc.c
> +++ b/fs/nfs/nfs4proc.c
> @@ -55,6 +55,9 @@
> #include <linux/xattr.h>
> #include <linux/utsname.h>
> #include <linux/freezer.h>
> +#include <linux/richacl.h>
> +#include <linux/richacl_xattr.h>
> +#include <linux/nfs4acl.h>
>
> #include "nfs4_fs.h"
> #include "delegation.h"
> @@ -2982,15 +2985,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)
> @@ -4518,45 +4524,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;
> @@ -4564,66 +4536,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);
> }
>
> /*
> @@ -4636,121 +4551,269 @@ 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;
> + 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;
> + 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_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 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, struct richacl *acl)
> +{
> + const char *who;
> + __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;
> + xdr_encode_opaque(p, who, len);
> + return 0;
> + } else {
> + who = richace_unmapped_identifier(ace, acl);
> + if (who) {
> + unsigned int len = strlen(who);
> +
> + p = xdr_reserve_space(xdr, 4 + len);
> + if (!p)
> + return -EIO;
> + xdr_encode_opaque(p, who, len);
> + return 0;
> + } else 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
> +nfs4_encode_acl(struct page **pages, unsigned int len, struct richacl *acl,
> + const struct nfs_server *server)
> +{
> + int minorversion = server->nfs_client->cl_minorversion;
> + unsigned int ace_mask = nfs4_ace_mask(minorversion);
> + struct xdr_stream xdr;
> + struct xdr_buf buf;
> + __be32 *p;
> + struct richace *ace;
> +
> + /* Reject acls not understood by the server */
> + if (server->attr_bitmask[1] & FATTR4_WORD1_DACL) {
> + BUILD_BUG_ON(NFS4_ACE_MASK_ALL != RICHACE_VALID_MASK);
> + } else {
> + if (acl->a_flags)
> + return -EINVAL;
> + richacl_for_each_entry(ace, acl) {
> + if (ace->e_flags & RICHACE_INHERITED_ACE)
> + return -EINVAL;
> + }
> + }
> + richacl_for_each_entry(ace, acl) {
> + if (ace->e_mask & ~ace_mask)
> + return -EINVAL;
> + }
> +
> + xdr_init_encode_pages(&xdr, &buf, pages, len);
> +
> + if (server->attr_bitmask[1] & FATTR4_WORD1_DACL) {
> + p = xdr_reserve_space(&xdr, 4);
> + if (!p)
> + goto fail;
> + *p = cpu_to_be32(acl ? acl->a_flags : 0);
> + }
> +
> + p = xdr_reserve_space(&xdr, 4);
> + if (!p)
> + goto fail;
> + if (!acl) {
> + *p++ = cpu_to_be32(0);
> + return buf.len;
> + }
> + *p++ = cpu_to_be32(acl->a_count);
> +
> + richacl_for_each_entry(ace, acl) {
> + 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 | RICHACE_UNMAPPED_WHO));
> + *p++ = cpu_to_be32(ace->e_mask & NFS4_ACE_MASK_ALL);
> + if (nfs4_encode_ace_who(&xdr, server, ace, acl) != 0)
> + goto fail;
> + }
> +
> + return buf.len;
> +
> +fail:
> + return -ENOMEM;
> +}
> +
> +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_pages = pages,
> - .acl_len = buflen,
> };
> struct nfs_setaclres res;
> struct rpc_message msg = {
> @@ -4758,16 +4821,20 @@ 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;
> +
> + ret = nfs4_encode_acl(pages, NFS4ACL_SIZE_MAX, acl, server);
> + if (ret < 0) {
> + for (i = 0; i < ARRAY_SIZE(pages) && pages[i]; i++)
> + put_page(pages[i]);
> + return ret;
> + }
> + arg.acl_len = ret;
> +
> nfs4_inode_return_delegation(inode);
> ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
>
> @@ -4775,8 +4842,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; i < ARRAY_SIZE(pages) && pages[i]; i++)
> + put_page(pages[i]);
>
> /*
> * Acl update can result in inode attribute update.
> @@ -4790,12 +4857,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);
> @@ -6257,34 +6324,283 @@ nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_state *lsp)
> rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, data);
> }
>
> +static int nfs4_xattr_set_richacl(struct dentry *dentry, const char *key,
> + const void *buf, size_t buflen,
> + int flags, int handler_flags)
> +{
> + struct inode *inode = d_inode(dentry);
> + struct richacl *acl;
> + int error;
> +
> + if (strcmp(key, "") != 0)
> + return -EINVAL;
> +
> + if (buf) {
> + acl = richacl_from_xattr(&init_user_ns, buf, buflen);
> + if (IS_ERR(acl))
> + return PTR_ERR(acl);
> + error = richacl_apply_masks(&acl, inode->i_uid);
> + } 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(inode->i_mode);
> + error = 0;
> + }
> +
> + if (!error)
> + error = nfs4_proc_set_acl(inode, acl);
> + richacl_put(acl);
> + return error;
> +}
> +
> +static int nfs4_xattr_get_richacl(struct dentry *dentry, const char *key,
> + void *buf, size_t buflen, int handler_flags)
> +{
> + struct inode *inode = d_inode(dentry);
> + struct richacl *acl;
> + int error;
> + umode_t mode = inode->i_mode & S_IFMT;
> +
> + if (strcmp(key, "") != 0)
> + return -EINVAL;
> +
> + acl = nfs4_proc_get_acl(inode);
> + if (IS_ERR(acl))
> + return PTR_ERR(acl);
> + if (acl == NULL)
> + return -ENODATA;
> + error = -ENODATA;
> + if (richacl_equiv_mode(acl, &mode) == 0 &&
> + ((mode ^ inode->i_mode) & S_IRWXUGO) == 0)
> + goto out;
> + error = richacl_to_xattr(&init_user_ns, acl, buf, buflen);
> +out:
> + richacl_put(acl);
> + return error;
> +}
> +
> +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)
> +{
> + struct nfs_server *server = NFS_SERVER(d_inode(dentry));
> + size_t len = sizeof(XATTR_NAME_RICHACL);
> +
> + if (!(server->caps & (NFS_CAP_ALLOW_ACLS | NFS_CAP_DENY_ACLS)))
> + return 0;
> +
> + if (list && len <= list_len)
> + memcpy(list, XATTR_NAME_RICHACL, len);
> + return len;
> +}
> +
> #define XATTR_NAME_NFSV4_ACL "system.nfs4_acl"
>
> +static int richacl_to_nfs4_acl(struct nfs_server *server,
> + const struct richacl *acl,
> + void *buf, size_t buflen)
> +{
> + const struct richace *ace;
> + __be32 *p = buf;
> + size_t size = 0;
> +
> + size += sizeof(*p);
> + if (buflen >= size)
> + *p++ = cpu_to_be32(acl->a_count);
> +
> + richacl_for_each_entry(ace, acl) {
> + char who_buf[IDMAP_NAMESZ];
> + const char *who = who_buf;
> + int who_len;
> +
> + size += 3 * sizeof(*p);
> + if (buflen >= size) {
> + *p++ = cpu_to_be32(ace->e_type);
> + *p++ = cpu_to_be32(ace->e_flags &
> + ~(RICHACE_INHERITED_ACE |
> + RICHACE_UNMAPPED_WHO |
> + RICHACE_SPECIAL_WHO));
> + *p++ = cpu_to_be32(ace->e_mask);
> + }
> +
> + if (richace_is_unix_user(ace)) {
> + who_len = nfs_map_uid_to_name(server, ace->e_id.uid,
> + who_buf, sizeof(who_buf));
> + if (who_len < 0)
> + return -EIO;
> + } else if (richace_is_unix_group(ace)) {
> + who_len = nfs_map_gid_to_group(server, ace->e_id.gid,
> + who_buf, sizeof(who_buf));
> + if (who_len < 0)
> + return -EIO;
> + } else if (ace->e_flags & RICHACE_SPECIAL_WHO) {
> + if (!nfs4acl_special_id_to_who(ace->e_id.special,
> + &who, &who_len))
> + return -EIO;
> + } else {
> + who = richace_unmapped_identifier(ace, acl);
> + if (who)
> + who_len = strlen(who);
> + else
> + return -EIO;
> + }
> +
> + size += sizeof(*p) + ALIGN(who_len, sizeof(*p));
> + if (buflen >= size) {
> + unsigned int padding = -who_len & (sizeof(*p) - 1);
> +
> + *p++ = cpu_to_be32(who_len);
> + memcpy(p, who, who_len);
> + memset((char *)p + who_len, 0, padding);
> + p += DIV_ROUND_UP(who_len, sizeof(*p));
> + }
> + }
> + if (buflen && buflen < size)
> + return -ERANGE;
> + return size;
> +}
> +
> +static struct richacl *richacl_from_nfs4_acl(struct nfs_server *server,
> + const void *buf, size_t buflen)
> +{
> + struct richacl *acl = NULL;
> + struct richace *ace;
> + const __be32 *p = buf;
> + int count, err;
> +
> + if (buflen < sizeof(*p))
> + return ERR_PTR(-EINVAL);
> + count = be32_to_cpu(*p++);
> + if (count > RICHACL_XATTR_MAX_COUNT)
> + return ERR_PTR(-EINVAL);
> + buflen -= sizeof(*p);
> + acl = richacl_alloc(count, GFP_NOFS);
> + if (!acl)
> + return ERR_PTR(-ENOMEM);
> + richacl_for_each_entry(ace, acl) {
> + u32 who_len, size;
> + int special_id;
> + char *who;
> +
> + err = -EINVAL;
> + if (buflen < 4 * sizeof(*p))
> + goto out;
> + ace->e_type = be32_to_cpu(*p++);
> + ace->e_flags = be32_to_cpu(*p++);
> + if (ace->e_flags & (RICHACE_SPECIAL_WHO | RICHACE_UNMAPPED_WHO))
> + goto out;
> + ace->e_mask = be32_to_cpu(*p++);
> + who_len = be32_to_cpu(*p++);
> + buflen -= 4 * sizeof(*p);
> + size = ALIGN(who_len, 4);
> + if (buflen < size || size == 0)
> + goto out;
> + who = (char *)p;
> + special_id = nfs4acl_who_to_special_id(who, who_len);
> + if (special_id >= 0) {
> + ace->e_flags |= RICHACE_SPECIAL_WHO;
> + ace->e_id.special = special_id;
> + } else {
> + bool unmappable;
> +
> + if (ace->e_flags & RICHACE_IDENTIFIER_GROUP) {
> + err = nfs_map_group_to_gid(server, who, who_len,
> + &ace->e_id.gid);
> + if (err) {
> + dprintk("%s: nfs_map_group_to_gid "
> + "failed!\n", __func__);
> + goto out;
> + }
> + /* FIXME: nfsidmap doesn't distinguish between
> + group nobody and unmappable groups! */
> + unmappable = gid_eq(ace->e_id.gid,
> + make_kgid(&init_user_ns, 99));
> + } else {
> + err = nfs_map_name_to_uid(server, who, who_len,
> + &ace->e_id.uid);
> + if (err) {
> + dprintk("%s: nfs_map_name_to_gid "
> + "failed!\n", __func__);
> + goto out;
> + }
> + /* FIXME: nfsidmap doesn't distinguish between
> + user nobody and unmappable users! */
> + unmappable = uid_eq(ace->e_id.uid,
> + make_kuid(&init_user_ns, 99));
> + }
> + if (unmappable) {
> + err = -ENOMEM;
> + if (richacl_add_unmapped_identifier(&acl, &ace,
> + who, who_len, GFP_NOFS))
> + goto out;
> + }
> + }
> + p += size / sizeof(*p);
> + buflen -= size;
> + }
> + err = -EINVAL;
> + if (buflen != 0)
> + goto out;
> + err = 0;
> +
> +out:
> + if (err) {
> + richacl_put(acl);
> + acl = ERR_PTR(err);
> + }
> + return acl;
> +}
I'm not a fan of the "one giant function" approach. Is there any way to split richacl_from_nfs4_acl() into several smaller functions?
Thanks,
Anna
> +
> static int nfs4_xattr_set_nfs4_acl(struct dentry *dentry, const char *key,
> const void *buf, size_t buflen,
> int flags, int type)
> {
> - if (strcmp(key, "") != 0)
> + struct inode *inode = d_inode(dentry);
> + struct richacl *acl;
> + int error;
> +
> + if (!buf || strcmp(key, "") != 0)
> return -EINVAL;
>
> - return nfs4_proc_set_acl(d_inode(dentry), buf, buflen);
> + acl = richacl_from_nfs4_acl(NFS_SERVER(inode), (void *)buf, buflen);
> + if (IS_ERR(acl))
> + return PTR_ERR(acl);
> + error = nfs4_proc_set_acl(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)
> {
> + struct inode *inode = d_inode(dentry);
> + struct richacl *acl;
> + int error;
> +
> if (strcmp(key, "") != 0)
> return -EINVAL;
> -
> - return nfs4_proc_get_acl(d_inode(dentry), buf, buflen);
> + acl = nfs4_proc_get_acl(inode);
> + if (IS_ERR(acl))
> + return PTR_ERR(acl);
> + if (acl == NULL)
> + return -ENODATA;
> + error = richacl_to_nfs4_acl(NFS_SERVER(inode), 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)
> {
> + struct nfs_server *server = NFS_SERVER(d_inode(dentry));
> size_t len = sizeof(XATTR_NAME_NFSV4_ACL);
>
> - if (!nfs4_server_supports_acls(NFS_SERVER(d_inode(dentry))))
> + if (!(server->caps & (NFS_CAP_ALLOW_ACLS | NFS_CAP_DENY_ACLS)))
> return 0;
>
> if (list && len <= list_len)
> @@ -8837,6 +9153,13 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
> .clone_server = nfs_clone_server,
> };
>
> +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,
> +};
> +
> static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
> .prefix = XATTR_NAME_NFSV4_ACL,
> .list = nfs4_xattr_list_nfs4_acl,
> @@ -8845,6 +9168,7 @@ static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
> };
>
> const struct xattr_handler *nfs4_xattr_handlers[] = {
> + &nfs4_xattr_richacl_handler,
> &nfs4_xattr_nfs4_acl_handler,
> #ifdef CONFIG_NFS_V4_SECURITY_LABEL
> &nfs4_xattr_nfs4_label_handler,
> diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
> index eefed15..f2507d7 100644
> --- a/fs/nfs/nfs4xdr.c
> +++ b/fs/nfs/nfs4xdr.c
> @@ -52,6 +52,10 @@
> #include <linux/nfs.h>
> #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"
> @@ -1650,16 +1654,24 @@ encode_restorefh(struct xdr_stream *xdr, struct compound_hdr *hdr)
> static void
> encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compound_hdr *hdr)
> {
> - __be32 *p;
> + int attrlen_offset;
> + __be32 attrlen, *p;
>
> 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);
> +
> + attrlen_offset = xdr->buf->len;
> + xdr_reserve_space(xdr, 4); /* to be backfilled later */
> +
> xdr_write_pages(xdr, arg->acl_pages, 0, arg->acl_len);
> +
> + attrlen = htonl(xdr->buf->len - attrlen_offset - 4);
> + write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, 4);
> }
>
> static void
> @@ -2488,7 +2500,7 @@ static void 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);
> @@ -5260,24 +5272,135 @@ decode_restorefh(struct xdr_stream *xdr)
> return decode_op_hdr(xdr, OP_RESTOREFH);
> }
>
> +static int
> +nfs4_decode_ace_who(struct richace *ace,
> + const char **unmapped, unsigned int *unmapped_len,
> + 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__);
> + return error;
> + }
> + /* FIXME: nfsidmap doesn't distinguish between group nobody and
> + unmappable groups! */
> + if (gid_eq(ace->e_id.gid, make_kgid(&init_user_ns, 99))) {
> + *unmapped = who;
> + *unmapped_len = len;
> + }
> + } 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;
> + }
> + /* FIXME: nfsidmap doesn't distinguish between user nobody and
> + unmappable users! */
> + if (uid_eq(ace->e_id.uid, make_kuid(&init_user_ns, 99))) {
> + *unmapped = who;
> + *unmapped_len = len;
> + }
> + }
> + return 0;
> +}
> +
> +static struct richacl *
> +decode_acl_entries(struct xdr_stream *xdr, const struct nfs_server *server)
> +{
> + struct richacl *acl;
> + struct richace *ace;
> + uint32_t count;
> + __be32 *p;
> + int status;
> +
> + p = xdr_inline_decode(xdr, 4);
> + if (unlikely(!p))
> + return ERR_PTR(-ENOMEM); /* acl truncated */
> + count = be32_to_cpup(p);
> + if (count > RICHACL_XATTR_MAX_COUNT)
> + return ERR_PTR(-EIO);
> + acl = richacl_alloc(count, GFP_NOFS);
> + if (!acl)
> + return ERR_PTR(-ENOMEM);
> + richacl_for_each_entry(ace, acl) {
> + const char *unmapped = NULL;
> + unsigned int unmapped_len;
> +
> + 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 | RICHACE_UNMAPPED_WHO))
> + goto out;
> + ace->e_mask = be32_to_cpup(p++);
> + status = nfs4_decode_ace_who(ace, &unmapped,
> + &unmapped_len, server,
> + xdr);
> + if (status)
> + goto out;
> + if (unmapped) {
> + status = -ENOMEM;
> + if (richacl_add_unmapped_identifier(&acl, &ace,
> + unmapped, unmapped_len,
> + GFP_NOFS))
> + goto out;
> + }
> + }
> + status = 0;
> +
> +out:
> + if (status) {
> + 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)
> {
> unsigned int savep;
> uint32_t attrlen,
> bitmap[3] = {0};
> + struct richacl *acl = NULL;
> 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 = decode_attr_length(xdr, &attrlen, &savep)) != 0)
> @@ -5286,24 +5409,28 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
> 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
> - * 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);
> - }
> + acl = decode_acl_entries(xdr, res->server);
> + status = PTR_ERR(acl);
> + if (IS_ERR(acl))
> + goto out;
> + bitmap[0] &= ~FATTR4_WORD0_ACL;
> } else
> status = -EOPNOTSUPP;
>
> + status = -EIO;
> + if (unlikely(bitmap[0]))
> + goto out;
> +
> + status = decode_attr_mode(xdr, bitmap, &res->mode);
> + if (status < 0)
> + goto out;
> + status = 0;
> +
> out:
> + if (status == 0)
> + res->acl = acl;
> + else
> + richacl_put(acl);
> return status;
> }
>
> diff --git a/fs/nfs/super.c b/fs/nfs/super.c
> index 383a027..8ced33d 100644
> --- a/fs/nfs/super.c
> +++ b/fs/nfs/super.c
> @@ -2319,7 +2319,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;
> }
>
> @@ -2346,7 +2346,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 c0e9614..b84e194 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 570a7df..6c41668 100644
> --- a/include/linux/nfs_fs_sb.h
> +++ b/include/linux/nfs_fs_sb.h
> @@ -243,5 +243,7 @@ struct nfs_server {
> #define NFS_CAP_ALLOCATE (1U << 20)
> #define NFS_CAP_DEALLOCATE (1U << 21)
> #define NFS_CAP_LAYOUTSTATS (1U << 22)
> +#define NFS_CAP_ALLOW_ACLS (1U << 23)
> +#define NFS_CAP_DENY_ACLS (1U << 24)
>
> #endif
> diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
> index 090ade4..337c341 100644
> --- a/include/linux/nfs_xdr.h
> +++ b/include/linux/nfs_xdr.h
> @@ -683,9 +683,10 @@ struct nfs_setattrargs {
>
> struct nfs_setaclargs {
> struct nfs4_sequence_args seq_args;
> + const struct nfs_server * server;
> struct nfs_fh * fh;
> - size_t acl_len;
> struct page ** acl_pages;
> + size_t acl_len;
> };
>
> struct nfs_setaclres {
> @@ -703,9 +704,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;
> };
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Gruenbacher <agruenba@redhat.com> |
|---|---|
| Date | 2015-10-12 21:50 +0200 |
| Subject | Re: [PATCH v10 44/46] nfs: Add richacl support |
| Message-ID | <qiRgB-nX-17@gated-at.bofh.it> |
| In reply to | #1244756 |
Anna, On Mon, Oct 12, 2015 at 4:39 PM, Anna Schumaker <Anna.Schumaker@netapp.com> wrote: > I'm not a fan of the "one giant function" approach. Is there any way to split richacl_from_nfs4_acl() into several smaller functions? sure, parsing the usr/group identifier could be put in a separate function, for example. That function cannot stay as it is right now anyway --- we really need idmapper to tell us when an identifier string cannot be mapped to a uid or gid so that we can preserve that identifier. Thanks, Andreas -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Andreas Gruenbacher <andreas.gruenbacher@gmail.com> |
|---|---|
| Date | 2015-10-12 01:10 +0200 |
| Subject | [PATCH v10 21/46] ext4: Add richacl feature flag |
| Message-ID | <qixUE-61y-77@gated-at.bofh.it> |
| In reply to | #1244246 |
From: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com>
This feature flag selects richacl instead of posix acl support on the
file system. In addition, the "acl" mount option is needed for enabling
either of the two kinds of acls.
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
fs/ext4/ext4.h | 6 ++++--
fs/ext4/super.c | 42 +++++++++++++++++++++++++++++++++---------
2 files changed, 37 insertions(+), 11 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index fd1f28b..b97a3b1 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -991,7 +991,7 @@ struct ext4_inode_info {
#define EXT4_MOUNT_UPDATE_JOURNAL 0x01000 /* Update the journal format */
#define EXT4_MOUNT_NO_UID32 0x02000 /* Disable 32-bit UIDs */
#define EXT4_MOUNT_XATTR_USER 0x04000 /* Extended user attributes */
-#define EXT4_MOUNT_POSIX_ACL 0x08000 /* POSIX Access Control Lists */
+#define EXT4_MOUNT_ACL 0x08000 /* Access Control Lists */
#define EXT4_MOUNT_NO_AUTO_DA_ALLOC 0x10000 /* No auto delalloc mapping */
#define EXT4_MOUNT_BARRIER 0x20000 /* Use block barriers */
#define EXT4_MOUNT_QUOTA 0x80000 /* Some quota option set */
@@ -1582,6 +1582,7 @@ static inline int ext4_encrypted_inode(struct inode *inode)
#define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */
#define EXT4_FEATURE_INCOMPAT_INLINE_DATA 0x8000 /* data in inode */
#define EXT4_FEATURE_INCOMPAT_ENCRYPT 0x10000
+#define EXT4_FEATURE_INCOMPAT_RICHACL 0x20000
#define EXT2_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR
#define EXT2_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \
@@ -1607,7 +1608,8 @@ static inline int ext4_encrypted_inode(struct inode *inode)
EXT4_FEATURE_INCOMPAT_FLEX_BG| \
EXT4_FEATURE_INCOMPAT_MMP | \
EXT4_FEATURE_INCOMPAT_INLINE_DATA | \
- EXT4_FEATURE_INCOMPAT_ENCRYPT)
+ EXT4_FEATURE_INCOMPAT_ENCRYPT | \
+ EXT4_FEATURE_INCOMPAT_RICHACL)
#define EXT4_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \
EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \
EXT4_FEATURE_RO_COMPAT_GDT_CSUM| \
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index a63c7b0..84d1a5d 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1270,6 +1270,28 @@ static ext4_fsblk_t get_sb_block(void **data)
return sb_block;
}
+static int enable_acl(struct super_block *sb)
+{
+ sb->s_flags &= ~(MS_POSIXACL | MS_RICHACL);
+ if (test_opt(sb, ACL)) {
+ if (EXT4_HAS_INCOMPAT_FEATURE(sb,
+ EXT4_FEATURE_INCOMPAT_RICHACL)) {
+#ifdef CONFIG_EXT4_FS_RICHACL
+ sb->s_flags |= MS_RICHACL;
+#else
+ return -EOPNOTSUPP;
+#endif
+ } else {
+#ifdef CONFIG_EXT4_FS_POSIX_ACL
+ sb->s_flags |= MS_POSIXACL;
+#else
+ return -EOPNOTSUPP;
+#endif
+ }
+ }
+ return 0;
+}
+
#define DEFAULT_JOURNAL_IOPRIO (IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 3))
static char deprecated_msg[] = "Mount option \"%s\" will be removed by %s\n"
"Contact linux-ext4@vger.kernel.org if you think we should keep it.\n";
@@ -1416,9 +1438,9 @@ static const struct mount_opts {
MOPT_NO_EXT2 | MOPT_DATAJ},
{Opt_user_xattr, EXT4_MOUNT_XATTR_USER, MOPT_SET},
{Opt_nouser_xattr, EXT4_MOUNT_XATTR_USER, MOPT_CLEAR},
-#ifdef CONFIG_EXT4_FS_POSIX_ACL
- {Opt_acl, EXT4_MOUNT_POSIX_ACL, MOPT_SET},
- {Opt_noacl, EXT4_MOUNT_POSIX_ACL, MOPT_CLEAR},
+#if defined(CONFIG_EXT4_FS_POSIX_ACL) || defined(CONFIG_EXT4_FS_RICHACL)
+ {Opt_acl, EXT4_MOUNT_ACL, MOPT_SET},
+ {Opt_noacl, EXT4_MOUNT_ACL, MOPT_CLEAR},
#else
{Opt_acl, 0, MOPT_NOSUPPORT},
{Opt_noacl, 0, MOPT_NOSUPPORT},
@@ -3576,8 +3598,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
set_opt(sb, NO_UID32);
/* xattr user namespace & acls are now defaulted on */
set_opt(sb, XATTR_USER);
-#ifdef CONFIG_EXT4_FS_POSIX_ACL
- set_opt(sb, POSIX_ACL);
+#if defined(CONFIG_EXT4_FS_POSIX_ACL) || defined(CONFIG_EXT4_FS_RICHACL)
+ set_opt(sb, ACL);
#endif
/* don't forget to enable journal_csum when metadata_csum is enabled. */
if (ext4_has_metadata_csum(sb))
@@ -3660,8 +3682,9 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
sb->s_iflags |= SB_I_CGROUPWB;
}
- sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
- (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);
+ err = enable_acl(sb);
+ if (err)
+ goto failed_mount;
if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV &&
(EXT4_HAS_COMPAT_FEATURE(sb, ~0U) ||
@@ -4981,8 +5004,9 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
if (sbi->s_mount_flags & EXT4_MF_FS_ABORTED)
ext4_abort(sb, "Abort forced by user");
- sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |
- (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0);
+ err = enable_acl(sb);
+ if (err)
+ goto restore_opts;
es = sbi->s_es;
--
2.5.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
Page 2 of 3 — ← Prev page 1 [2] 3 Next page →
Back to top | Article view | linux.kernel
csiph-web