Commit 8edc0648 authored by Chuck Lever's avatar Chuck Lever
Browse files

NFSD: Add an xdr_stream-based encoder for NFSv2/3 ACLs

parent 8a2cf9f5
Loading
Loading
Loading
Loading
+71 −0
Original line number Diff line number Diff line
@@ -136,6 +136,77 @@ int nfsacl_encode(struct xdr_buf *buf, unsigned int base, struct inode *inode,
}
EXPORT_SYMBOL_GPL(nfsacl_encode);

/**
 * nfs_stream_encode_acl - Encode an NFSv3 ACL
 *
 * @xdr: an xdr_stream positioned to receive an encoded ACL
 * @inode: inode of file whose ACL this is
 * @acl: posix_acl to encode
 * @encode_entries: whether to encode ACEs as well
 * @typeflag: ACL type: NFS_ACL_DEFAULT or zero
 *
 * Return values:
 *   %false: The ACL could not be encoded
 *   %true: @xdr is advanced to the next available position
 */
bool nfs_stream_encode_acl(struct xdr_stream *xdr, struct inode *inode,
			   struct posix_acl *acl, int encode_entries,
			   int typeflag)
{
	const size_t elem_size = XDR_UNIT * 3;
	u32 entries = (acl && acl->a_count) ? max_t(int, acl->a_count, 4) : 0;
	struct nfsacl_encode_desc nfsacl_desc = {
		.desc = {
			.elem_size = elem_size,
			.array_len = encode_entries ? entries : 0,
			.xcode = xdr_nfsace_encode,
		},
		.acl = acl,
		.typeflag = typeflag,
		.uid = inode->i_uid,
		.gid = inode->i_gid,
	};
	struct nfsacl_simple_acl aclbuf;
	unsigned int base;
	int err;

	if (entries > NFS_ACL_MAX_ENTRIES)
		return false;
	if (xdr_stream_encode_u32(xdr, entries) < 0)
		return false;

	if (encode_entries && acl && acl->a_count == 3) {
		struct posix_acl *acl2 = &aclbuf.acl;

		/* Avoid the use of posix_acl_alloc().  nfsacl_encode() is
		 * invoked in contexts where a memory allocation failure is
		 * fatal.  Fortunately this fake ACL is small enough to
		 * construct on the stack. */
		posix_acl_init(acl2, 4);

		/* Insert entries in canonical order: other orders seem
		 to confuse Solaris VxFS. */
		acl2->a_entries[0] = acl->a_entries[0];  /* ACL_USER_OBJ */
		acl2->a_entries[1] = acl->a_entries[1];  /* ACL_GROUP_OBJ */
		acl2->a_entries[2] = acl->a_entries[1];  /* ACL_MASK */
		acl2->a_entries[2].e_tag = ACL_MASK;
		acl2->a_entries[3] = acl->a_entries[2];  /* ACL_OTHER */
		nfsacl_desc.acl = acl2;
	}

	base = xdr_stream_pos(xdr);
	if (!xdr_reserve_space(xdr, XDR_UNIT +
			       elem_size * nfsacl_desc.desc.array_len))
		return false;
	err = xdr_encode_array2(xdr->buf, base, &nfsacl_desc.desc);
	if (err)
		return false;

	return true;
}
EXPORT_SYMBOL_GPL(nfs_stream_encode_acl);


struct nfsacl_decode_desc {
	struct xdr_array2_desc desc;
	unsigned int count;
+3 −0
Original line number Diff line number Diff line
@@ -41,5 +41,8 @@ nfsacl_decode(struct xdr_buf *buf, unsigned int base, unsigned int *aclcnt,
extern bool
nfs_stream_decode_acl(struct xdr_stream *xdr, unsigned int *aclcnt,
		      struct posix_acl **pacl);
extern bool
nfs_stream_encode_acl(struct xdr_stream *xdr, struct inode *inode,
		      struct posix_acl *acl, int encode_entries, int typeflag);

#endif  /* __LINUX_NFSACL_H */