afs: Implement YFS ACL setting

Implement the setting of YFS ACLs in AFS through the interface of setting
the afs.yfs.acl extended attribute on the file.

Signed-off-by: David Howells <dhowells@redhat.com>
diff --git a/fs/afs/xattr.c b/fs/afs/xattr.c
index a5c82b0..c81f850 100644
--- a/fs/afs/xattr.c
+++ b/fs/afs/xattr.c
@@ -226,9 +226,58 @@ static int afs_xattr_get_yfs(const struct xattr_handler *handler,
 	return ret;
 }
 
+/*
+ * Set a file's YFS ACL.
+ */
+static int afs_xattr_set_yfs(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 ||
+	    strcmp(name, "acl") != 0)
+		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);
+			yfs_fs_store_opaque_acl2(&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_yfs_handler = {
 	.prefix	= "afs.yfs.",
 	.get	= afs_xattr_get_yfs,
+	.set	= afs_xattr_set_yfs,
 };
 
 /*