"afs.acl\0"
        "afs.cell\0"
        "afs.fid\0"
-       "afs.volume";
+       "afs.volume\0"
+       "afs.yfs.acl\0"
+       "afs.yfs.acl_inherited\0"
+       "afs.yfs.acl_num_cleaned\0"
+       "afs.yfs.vol_acl";
 
 /*
  * Retrieve a list of the supported xattrs.
        .set    = afs_xattr_set_acl,
 };
 
+/*
+ * Get a file's YFS ACL.
+ */
+static int afs_xattr_get_yfs(const struct xattr_handler *handler,
+                            struct dentry *dentry,
+                            struct inode *inode, const char *name,
+                            void *buffer, size_t size)
+{
+       struct afs_fs_cursor fc;
+       struct afs_vnode *vnode = AFS_FS_I(inode);
+       struct yfs_acl *yacl = NULL;
+       struct key *key;
+       unsigned int flags = 0;
+       char buf[16], *data;
+       int which = 0, dsize, ret;
+
+       if (strcmp(name, "acl") == 0)
+               which = 0;
+       else if (strcmp(name, "acl_inherited") == 0)
+               which = 1;
+       else if (strcmp(name, "acl_num_cleaned") == 0)
+               which = 2;
+       else if (strcmp(name, "vol_acl") == 0)
+               which = 3;
+       else
+               return -EOPNOTSUPP;
+
+       if (which == 0)
+               flags |= YFS_ACL_WANT_ACL;
+       else if (which == 3)
+               flags |= YFS_ACL_WANT_VOL_ACL;
+
+       key = afs_request_key(vnode->volume->cell);
+       if (IS_ERR(key))
+               return PTR_ERR(key);
+
+       ret = -ERESTARTSYS;
+       if (afs_begin_vnode_operation(&fc, vnode, key)) {
+               while (afs_select_fileserver(&fc)) {
+                       fc.cb_break = afs_calc_vnode_cb_break(vnode);
+                       yacl = yfs_fs_fetch_opaque_acl(&fc, flags);
+               }
+
+               afs_check_for_remote_deletion(&fc, fc.vnode);
+               afs_vnode_commit_status(&fc, vnode, fc.cb_break);
+               ret = afs_end_vnode_operation(&fc);
+       }
+
+       if (ret == 0) {
+               switch (which) {
+               case 0:
+                       data = yacl->acl->data;
+                       dsize = yacl->acl->size;
+                       break;
+               case 1:
+                       data = buf;
+                       dsize = snprintf(buf, sizeof(buf), "%u",
+                                        yacl->inherit_flag);
+                       break;
+               case 2:
+                       data = buf;
+                       dsize = snprintf(buf, sizeof(buf), "%u",
+                                        yacl->num_cleaned);
+                       break;
+               case 3:
+                       data = yacl->vol_acl->data;
+                       dsize = yacl->vol_acl->size;
+                       break;
+               default:
+                       ret = -EOPNOTSUPP;
+                       goto out;
+               }
+
+               ret = dsize;
+               if (size > 0) {
+                       if (dsize > size) {
+                               ret = -ERANGE;
+                               goto out;
+                       }
+                       memcpy(buffer, data, dsize);
+               }
+       }
+
+out:
+       yfs_free_opaque_acl(yacl);
+       key_put(key);
+       return ret;
+}
+
+static const struct xattr_handler afs_xattr_yfs_handler = {
+       .prefix = "afs.yfs.",
+       .get    = afs_xattr_get_yfs,
+};
+
 /*
  * Get the name of the cell on which a file resides.
  */
        &afs_xattr_afs_cell_handler,
        &afs_xattr_afs_fid_handler,
        &afs_xattr_afs_volume_handler,
+       &afs_xattr_yfs_handler,         /* afs.yfs. prefix */
        NULL
 };
 
        afs_make_call(&fc->ac, call, GFP_NOFS);
        return afs_wait_for_call_to_complete(call, &fc->ac);
 }
+
+/*
+ * Deliver reply data to an YFS.FetchOpaqueACL.
+ */
+static int yfs_deliver_fs_fetch_opaque_acl(struct afs_call *call)
+{
+       struct afs_volsync *volsync = call->reply[2];
+       struct afs_vnode *vnode = call->reply[1];
+       struct yfs_acl *yacl =  call->reply[0];
+       struct afs_acl *acl;
+       const __be32 *bp;
+       unsigned int size;
+       int ret;
+
+       _enter("{%u}", call->unmarshall);
+
+       switch (call->unmarshall) {
+       case 0:
+               afs_extract_to_tmp(call);
+               call->unmarshall++;
+
+               /* Extract the file ACL length */
+       case 1:
+               ret = afs_extract_data(call, true);
+               if (ret < 0)
+                       return ret;
+
+               size = call->count2 = ntohl(call->tmp);
+               size = round_up(size, 4);
+
+               if (yacl->flags & YFS_ACL_WANT_ACL) {
+                       acl = kmalloc(struct_size(acl, data, size), GFP_KERNEL);
+                       if (!acl)
+                               return -ENOMEM;
+                       yacl->acl = acl;
+                       acl->size = call->count2;
+                       afs_extract_begin(call, acl->data, size);
+               } else {
+                       iov_iter_discard(&call->iter, READ, size);
+               }
+               call->unmarshall++;
+
+               /* Extract the file ACL */
+       case 2:
+               ret = afs_extract_data(call, true);
+               if (ret < 0)
+                       return ret;
+
+               afs_extract_to_tmp(call);
+               call->unmarshall++;
+
+               /* Extract the volume ACL length */
+       case 3:
+               ret = afs_extract_data(call, true);
+               if (ret < 0)
+                       return ret;
+
+               size = call->count2 = ntohl(call->tmp);
+               size = round_up(size, 4);
+
+               if (yacl->flags & YFS_ACL_WANT_VOL_ACL) {
+                       acl = kmalloc(struct_size(acl, data, size), GFP_KERNEL);
+                       if (!acl)
+                               return -ENOMEM;
+                       yacl->vol_acl = acl;
+                       acl->size = call->count2;
+                       afs_extract_begin(call, acl->data, size);
+               } else {
+                       iov_iter_discard(&call->iter, READ, size);
+               }
+               call->unmarshall++;
+
+               /* Extract the volume ACL */
+       case 4:
+               ret = afs_extract_data(call, true);
+               if (ret < 0)
+                       return ret;
+
+               afs_extract_to_buf(call,
+                                  sizeof(__be32) * 2 +
+                                  sizeof(struct yfs_xdr_YFSFetchStatus) +
+                                  sizeof(struct yfs_xdr_YFSVolSync));
+               call->unmarshall++;
+
+               /* extract the metadata */
+       case 5:
+               ret = afs_extract_data(call, false);
+               if (ret < 0)
+                       return ret;
+
+               bp = call->buffer;
+               yacl->inherit_flag = ntohl(*bp++);
+               yacl->num_cleaned = ntohl(*bp++);
+               ret = yfs_decode_status(call, &bp, &vnode->status, vnode,
+                                       &call->expected_version, NULL);
+               if (ret < 0)
+                       return ret;
+               xdr_decode_YFSVolSync(&bp, volsync);
+
+               call->unmarshall++;
+
+       case 6:
+               break;
+       }
+
+       _leave(" = 0 [done]");
+       return 0;
+}
+
+void yfs_free_opaque_acl(struct yfs_acl *yacl)
+{
+       if (yacl) {
+               kfree(yacl->acl);
+               kfree(yacl->vol_acl);
+               kfree(yacl);
+       }
+}
+
+static void yfs_destroy_fs_fetch_opaque_acl(struct afs_call *call)
+{
+       yfs_free_opaque_acl(call->reply[0]);
+       afs_flat_call_destructor(call);
+}
+
+/*
+ * YFS.FetchOpaqueACL operation type
+ */
+static const struct afs_call_type yfs_RXYFSFetchOpaqueACL = {
+       .name           = "YFS.FetchOpaqueACL",
+       .op             = yfs_FS_FetchOpaqueACL,
+       .deliver        = yfs_deliver_fs_fetch_opaque_acl,
+       .destructor     = yfs_destroy_fs_fetch_opaque_acl,
+};
+
+/*
+ * Fetch the YFS advanced ACLs for a file.
+ */
+struct yfs_acl *yfs_fs_fetch_opaque_acl(struct afs_fs_cursor *fc,
+                                       unsigned int flags)
+{
+       struct afs_vnode *vnode = fc->vnode;
+       struct afs_call *call;
+       struct yfs_acl *yacl;
+       struct afs_net *net = afs_v2net(vnode);
+       __be32 *bp;
+
+       _enter(",%x,{%llx:%llu},,",
+              key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
+
+       call = afs_alloc_flat_call(net, &yfs_RXYFSFetchOpaqueACL,
+                                  sizeof(__be32) * 2 +
+                                  sizeof(struct yfs_xdr_YFSFid),
+                                  sizeof(__be32) * 2 +
+                                  sizeof(struct yfs_xdr_YFSFetchStatus) +
+                                  sizeof(struct yfs_xdr_YFSVolSync));
+       if (!call)
+               goto nomem;
+
+       yacl = kzalloc(sizeof(struct yfs_acl), GFP_KERNEL);
+       if (!yacl)
+               goto nomem_call;
+
+       yacl->flags = flags;
+       call->key = fc->key;
+       call->reply[0] = yacl;
+       call->reply[1] = vnode;
+       call->reply[2] = NULL; /* volsync */
+       call->ret_reply0 = true;
+
+       /* marshall the parameters */
+       bp = call->request;
+       bp = xdr_encode_u32(bp, YFSFETCHOPAQUEACL);
+       bp = xdr_encode_u32(bp, 0); /* RPC flags */
+       bp = xdr_encode_YFSFid(bp, &vnode->fid);
+       yfs_check_req(call, bp);
+
+       call->cb_break = fc->cb_break;
+       afs_use_fs_server(call, fc->cbi);
+       trace_afs_make_fs_call(call, &vnode->fid);
+       afs_make_call(&fc->ac, call, GFP_KERNEL);
+       return (struct yfs_acl *)afs_wait_for_call_to_complete(call, &fc->ac);
+
+nomem_call:
+       afs_put_call(call);
+nomem:
+       fc->ac.error = -ENOMEM;
+       return ERR_PTR(-ENOMEM);
+}