From b10494af4989d2d20679d0e3b7d1a45c2f8f8f1a Mon Sep 17 00:00:00 2001
From: Joe Gorse <jhgorse@gmail.com>
Date: Thu, 25 Apr 2019 14:26:52 +0100
Subject: [PATCH] afs: implement acl setting

Implements the setting of ACLs in AFS by means of setting the
afs.acl extended attribute on the file.

Signed-off-by: Joe Gorse <jhgorse@gmail.com>
Signed-off-by: David Howells <dhowells@redhat.com>
---
 fs/afs/afs_fs.h            |  1 +
 fs/afs/fsclient.c          | 61 +++++++++++++++++++++++++++++++++++---
 fs/afs/internal.h          |  1 +
 fs/afs/xattr.c             | 52 ++++++++++++++++++++++++++++++--
 include/trace/events/afs.h |  1 +
 5 files changed, 110 insertions(+), 6 deletions(-)

diff --git a/fs/afs/afs_fs.h b/fs/afs/afs_fs.h
index 4df1f1eec0ab..18a54ca422f8 100644
--- a/fs/afs/afs_fs.h
+++ b/fs/afs/afs_fs.h
@@ -20,6 +20,7 @@ enum AFS_FS_Operations {
 	FSFETCHACL		= 131,	/* AFS Fetch file ACL */
 	FSFETCHSTATUS		= 132,	/* AFS Fetch file status */
 	FSSTOREDATA		= 133,	/* AFS Store file data */
+	FSSTOREACL		= 134,	/* AFS Store file ACL */
 	FSSTORESTATUS		= 135,	/* AFS Store file status */
 	FSREMOVEFILE		= 136,	/* AFS Remove a file */
 	FSCREATEFILE		= 137,	/* AFS Create a file */
diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c
index 283f486c59f4..7f1722b9e432 100644
--- a/fs/afs/fsclient.c
+++ b/fs/afs/fsclient.c
@@ -836,9 +836,10 @@ int afs_fs_create(struct afs_fs_cursor *fc,
 }
 
 /*
- * deliver reply data to an FS.RemoveFile or FS.RemoveDir
+ * Deliver reply data to any operation that returns file status and volume
+ * sync.
  */
-static int afs_deliver_fs_remove(struct afs_call *call)
+static int afs_deliver_fs_status_and_vol(struct afs_call *call)
 {
 	struct afs_vnode *vnode = call->reply[0];
 	const __be32 *bp;
@@ -868,14 +869,14 @@ static int afs_deliver_fs_remove(struct afs_call *call)
 static const struct afs_call_type afs_RXFSRemoveFile = {
 	.name		= "FS.RemoveFile",
 	.op		= afs_FS_RemoveFile,
-	.deliver	= afs_deliver_fs_remove,
+	.deliver	= afs_deliver_fs_status_and_vol,
 	.destructor	= afs_flat_call_destructor,
 };
 
 static const struct afs_call_type afs_RXFSRemoveDir = {
 	.name		= "FS.RemoveDir",
 	.op		= afs_FS_RemoveDir,
-	.deliver	= afs_deliver_fs_remove,
+	.deliver	= afs_deliver_fs_status_and_vol,
 	.destructor	= afs_flat_call_destructor,
 };
 
@@ -2513,3 +2514,55 @@ struct afs_acl *afs_fs_fetch_acl(struct afs_fs_cursor *fc)
 	afs_make_call(&fc->ac, call, GFP_KERNEL);
 	return (struct afs_acl *)afs_wait_for_call_to_complete(call, &fc->ac);
 }
+
+/*
+ * FS.StoreACL operation type
+ */
+static const struct afs_call_type afs_RXFSStoreACL = {
+	.name		= "FS.StoreACL",
+	.op		= afs_FS_StoreACL,
+	.deliver	= afs_deliver_fs_status_and_vol,
+	.destructor	= afs_flat_call_destructor,
+};
+
+/*
+ * Fetch the ACL for a file.
+ */
+int afs_fs_store_acl(struct afs_fs_cursor *fc, const struct afs_acl *acl)
+{
+	struct afs_vnode *vnode = fc->vnode;
+	struct afs_call *call;
+	struct afs_net *net = afs_v2net(vnode);
+	size_t size;
+	__be32 *bp;
+
+	_enter(",%x,{%llx:%llu},,",
+	       key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
+
+	size = round_up(acl->size, 4);
+	call = afs_alloc_flat_call(net, &afs_RXFSStoreACL,
+				   5 * 4 + size, (21 + 6) * 4);
+	if (!call) {
+		fc->ac.error = -ENOMEM;
+		return -ENOMEM;
+	}
+
+	call->key = fc->key;
+	call->reply[0] = vnode;
+	call->reply[2] = NULL; /* volsync */
+
+	/* marshall the parameters */
+	bp = call->request;
+	bp[0] = htonl(FSSTOREACL);
+	bp[1] = htonl(vnode->fid.vid);
+	bp[2] = htonl(vnode->fid.vnode);
+	bp[3] = htonl(vnode->fid.unique);
+	bp[4] = htonl(acl->size);
+	memcpy(&bp[5], acl->data, acl->size);
+	if (acl->size != size)
+		memset((void *)&bp[5] + acl->size, 0, size - acl->size);
+
+	trace_afs_make_fs_call(call, &vnode->fid);
+	afs_make_call(&fc->ac, call, GFP_KERNEL);
+	return afs_wait_for_call_to_complete(call, &fc->ac);
+}
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 683b802c20ea..5269824244c6 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -983,6 +983,7 @@ struct afs_acl {
 };
 
 extern struct afs_acl *afs_fs_fetch_acl(struct afs_fs_cursor *);
+extern int afs_fs_store_acl(struct afs_fs_cursor *, const struct afs_acl *);
 
 /*
  * fs_probe.c
diff --git a/fs/afs/xattr.c b/fs/afs/xattr.c
index b7d3d714d8ff..31db360947a6 100644
--- a/fs/afs/xattr.c
+++ b/fs/afs/xattr.c
@@ -80,9 +80,57 @@ static int afs_xattr_get_acl(const struct xattr_handler *handler,
 	return ret;
 }
 
+/*
+ * Set a file's AFS3 ACL.
+ */
+static int afs_xattr_set_acl(const struct xattr_handler *handler,
+                             struct dentry *dentry,
+                             struct inode *inode, const char *name,
+                             const void *buffer, size_t size, int flags)
+{
+	struct afs_fs_cursor fc;
+	struct afs_vnode *vnode = AFS_FS_I(inode);
+	struct afs_acl *acl = NULL;
+	struct key *key;
+	int ret;
+
+	if (flags == XATTR_CREATE)
+		return -EINVAL;
+
+	key = afs_request_key(vnode->volume->cell);
+	if (IS_ERR(key))
+		return PTR_ERR(key);
+
+	acl = kmalloc(sizeof(*acl) + size, GFP_KERNEL);
+	if (!acl) {
+		key_put(key);
+		return -ENOMEM;
+	}
+
+	acl->size = size;
+	memcpy(acl->data, buffer, size);
+
+	ret = -ERESTARTSYS;
+	if (afs_begin_vnode_operation(&fc, vnode, key)) {
+		while (afs_select_fileserver(&fc)) {
+			fc.cb_break = afs_calc_vnode_cb_break(vnode);
+			afs_fs_store_acl(&fc, acl);
+		}
+
+		afs_check_for_remote_deletion(&fc, fc.vnode);
+		afs_vnode_commit_status(&fc, vnode, fc.cb_break);
+		ret = afs_end_vnode_operation(&fc);
+	}
+
+	kfree(acl);
+	key_put(key);
+	return ret;
+}
+
 static const struct xattr_handler afs_xattr_afs_acl_handler = {
-	.name	= "afs.acl",
-	.get	= afs_xattr_get_acl,
+	.name   = "afs.acl",
+	.get    = afs_xattr_get_acl,
+	.set    = afs_xattr_set_acl,
 };
 
 /*
diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h
index 25c2e089c6ea..562f854ac4bf 100644
--- a/include/trace/events/afs.h
+++ b/include/trace/events/afs.h
@@ -36,6 +36,7 @@ enum afs_fs_operation {
 	afs_FS_FetchACL			= 131,	/* AFS Fetch file ACL */
 	afs_FS_FetchStatus		= 132,	/* AFS Fetch file status */
 	afs_FS_StoreData		= 133,	/* AFS Store file data */
+	afs_FS_StoreACL			= 134,	/* AFS Store file ACL */
 	afs_FS_StoreStatus		= 135,	/* AFS Store file status */
 	afs_FS_RemoveFile		= 136,	/* AFS Remove a file */
 	afs_FS_CreateFile		= 137,	/* AFS Create a file */