+#if defined(CONFIG_NFSD_V2_ACL) || \
+ defined(CONFIG_NFSD_V3_ACL) || \
+ defined(CONFIG_NFSD_V4)
+static ssize_t nfsd_getxattr(struct dentry *dentry, char *key, void **buf)
+{
+ ssize_t buflen;
+
+ buflen = vfs_getxattr(dentry, key, NULL, 0);
+ if (buflen <= 0)
+ return buflen;
+
+ *buf = kmalloc(buflen, GFP_KERNEL);
+ if (!*buf)
+ return -ENOMEM;
+
+ return vfs_getxattr(dentry, key, *buf, buflen);
+}
+#endif
+
+#if defined(CONFIG_NFSD_V4)
+static int
+set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key)
+{
+ int len;
+ size_t buflen;
+ char *buf = NULL;
+ int error = 0;
+
+ buflen = posix_acl_xattr_size(pacl->a_count);
+ buf = kmalloc(buflen, GFP_KERNEL);
+ error = -ENOMEM;
+ if (buf == NULL)
+ goto out;
+
+ len = posix_acl_to_xattr(pacl, buf, buflen);
+ if (len < 0) {
+ error = len;
+ goto out;
+ }
+
+ error = vfs_setxattr(dentry, key, buf, len, 0);
+out:
+ kfree(buf);
+ return error;
+}
+
+int
+nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ struct nfs4_acl *acl)
+{
+ int error;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct posix_acl *pacl = NULL, *dpacl = NULL;
+ unsigned int flags = 0;
+
+ /* Get inode */
+ error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, MAY_SATTR);
+ if (error)
+ goto out;
+
+ dentry = fhp->fh_dentry;
+ inode = dentry->d_inode;
+ if (S_ISDIR(inode->i_mode))
+ flags = NFS4_ACL_DIR;
+
+ error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
+ if (error == -EINVAL) {
+ error = nfserr_attrnotsupp;
+ goto out;
+ } else if (error < 0)
+ goto out_nfserr;
+
+ if (pacl) {
+ error = set_nfsv4_acl_one(dentry, pacl, POSIX_ACL_XATTR_ACCESS);
+ if (error < 0)
+ goto out_nfserr;
+ }
+
+ if (dpacl) {
+ error = set_nfsv4_acl_one(dentry, dpacl, POSIX_ACL_XATTR_DEFAULT);
+ if (error < 0)
+ goto out_nfserr;
+ }
+
+ error = nfs_ok;
+
+out:
+ posix_acl_release(pacl);
+ posix_acl_release(dpacl);
+ return (error);
+out_nfserr:
+ error = nfserrno(error);
+ goto out;
+}
+
+static struct posix_acl *
+_get_posix_acl(struct dentry *dentry, char *key)
+{
+ void *buf = NULL;
+ struct posix_acl *pacl = NULL;
+ int buflen;
+
+ buflen = nfsd_getxattr(dentry, key, &buf);
+ if (!buflen)
+ buflen = -ENODATA;
+ if (buflen <= 0)
+ return ERR_PTR(buflen);
+
+ pacl = posix_acl_from_xattr(buf, buflen);
+ kfree(buf);
+ return pacl;
+}
+
+int
+nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl)
+{
+ struct inode *inode = dentry->d_inode;
+ int error = 0;
+ struct posix_acl *pacl = NULL, *dpacl = NULL;
+ unsigned int flags = 0;
+
+ pacl = _get_posix_acl(dentry, POSIX_ACL_XATTR_ACCESS);
+ if (IS_ERR(pacl) && PTR_ERR(pacl) == -ENODATA)
+ pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
+ if (IS_ERR(pacl)) {
+ error = PTR_ERR(pacl);
+ pacl = NULL;
+ goto out;
+ }
+
+ if (S_ISDIR(inode->i_mode)) {
+ dpacl = _get_posix_acl(dentry, POSIX_ACL_XATTR_DEFAULT);
+ if (IS_ERR(dpacl) && PTR_ERR(dpacl) == -ENODATA)
+ dpacl = NULL;
+ else if (IS_ERR(dpacl)) {
+ error = PTR_ERR(dpacl);
+ dpacl = NULL;
+ goto out;
+ }
+ flags = NFS4_ACL_DIR;
+ }
+
+ *acl = nfs4_acl_posix_to_nfsv4(pacl, dpacl, flags);
+ if (IS_ERR(*acl)) {
+ error = PTR_ERR(*acl);
+ *acl = NULL;
+ }
+ out:
+ posix_acl_release(pacl);
+ posix_acl_release(dpacl);
+ return error;
+}
+
+#endif /* defined(CONFIG_NFS_V4) */
+