1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2a257cdd0SAndreas Gruenbacher /* 3a257cdd0SAndreas Gruenbacher * Process version 3 NFSACL requests. 4a257cdd0SAndreas Gruenbacher * 5a257cdd0SAndreas Gruenbacher * Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de> 6a257cdd0SAndreas Gruenbacher */ 7a257cdd0SAndreas Gruenbacher 89a74af21SBoaz Harrosh #include "nfsd.h" 99a74af21SBoaz Harrosh /* FIXME: nfsacl.h is a broken header */ 10a257cdd0SAndreas Gruenbacher #include <linux/nfsacl.h> 115a0e3ad6STejun Heo #include <linux/gfp.h> 129a74af21SBoaz Harrosh #include "cache.h" 139a74af21SBoaz Harrosh #include "xdr3.h" 140a3adadeSJ. Bruce Fields #include "vfs.h" 15a257cdd0SAndreas Gruenbacher 16a257cdd0SAndreas Gruenbacher /* 17a257cdd0SAndreas Gruenbacher * NULL call. 18a257cdd0SAndreas Gruenbacher */ 197111c66eSAl Viro static __be32 20a6beb732SChristoph Hellwig nfsd3_proc_null(struct svc_rqst *rqstp) 21a257cdd0SAndreas Gruenbacher { 22cc028a10SChuck Lever return rpc_success; 23a257cdd0SAndreas Gruenbacher } 24a257cdd0SAndreas Gruenbacher 25a257cdd0SAndreas Gruenbacher /* 26a257cdd0SAndreas Gruenbacher * Get the Access and/or Default ACL of a file. 27a257cdd0SAndreas Gruenbacher */ 28a6beb732SChristoph Hellwig static __be32 nfsd3_proc_getacl(struct svc_rqst *rqstp) 29a257cdd0SAndreas Gruenbacher { 30a6beb732SChristoph Hellwig struct nfsd3_getaclargs *argp = rqstp->rq_argp; 31a6beb732SChristoph Hellwig struct nfsd3_getaclres *resp = rqstp->rq_resp; 32a257cdd0SAndreas Gruenbacher struct posix_acl *acl; 334ac7249eSChristoph Hellwig struct inode *inode; 344ac7249eSChristoph Hellwig svc_fh *fh; 35a257cdd0SAndreas Gruenbacher 36a257cdd0SAndreas Gruenbacher fh = fh_copy(&resp->fh, &argp->fh); 3714168d67SChuck Lever resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); 3814168d67SChuck Lever if (resp->status != nfs_ok) 3914168d67SChuck Lever goto out; 40a257cdd0SAndreas Gruenbacher 412b0143b5SDavid Howells inode = d_inode(fh->fh_dentry); 424ac7249eSChristoph Hellwig 4314168d67SChuck Lever if (argp->mask & ~NFS_ACL_MASK) { 4414168d67SChuck Lever resp->status = nfserr_inval; 4514168d67SChuck Lever goto out; 4614168d67SChuck Lever } 47a257cdd0SAndreas Gruenbacher resp->mask = argp->mask; 48a257cdd0SAndreas Gruenbacher 49a257cdd0SAndreas Gruenbacher if (resp->mask & (NFS_ACL|NFS_ACLCNT)) { 504ac7249eSChristoph Hellwig acl = get_acl(inode, ACL_TYPE_ACCESS); 51a257cdd0SAndreas Gruenbacher if (acl == NULL) { 52a257cdd0SAndreas Gruenbacher /* Solaris returns the inode's minimum ACL. */ 53a257cdd0SAndreas Gruenbacher acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); 54a257cdd0SAndreas Gruenbacher } 5535e634b8SKinglong Mee if (IS_ERR(acl)) { 5614168d67SChuck Lever resp->status = nfserrno(PTR_ERR(acl)); 5735e634b8SKinglong Mee goto fail; 5835e634b8SKinglong Mee } 59a257cdd0SAndreas Gruenbacher resp->acl_access = acl; 60a257cdd0SAndreas Gruenbacher } 61a257cdd0SAndreas Gruenbacher if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) { 62a257cdd0SAndreas Gruenbacher /* Check how Solaris handles requests for the Default ACL 63a257cdd0SAndreas Gruenbacher of a non-directory! */ 644ac7249eSChristoph Hellwig acl = get_acl(inode, ACL_TYPE_DEFAULT); 65a257cdd0SAndreas Gruenbacher if (IS_ERR(acl)) { 6614168d67SChuck Lever resp->status = nfserrno(PTR_ERR(acl)); 67a257cdd0SAndreas Gruenbacher goto fail; 68a257cdd0SAndreas Gruenbacher } 69a257cdd0SAndreas Gruenbacher resp->acl_default = acl; 70a257cdd0SAndreas Gruenbacher } 71a257cdd0SAndreas Gruenbacher 72a257cdd0SAndreas Gruenbacher /* resp->acl_{access,default} are released in nfs3svc_release_getacl. */ 7314168d67SChuck Lever out: 74cc028a10SChuck Lever return rpc_success; 75a257cdd0SAndreas Gruenbacher 76a257cdd0SAndreas Gruenbacher fail: 77a257cdd0SAndreas Gruenbacher posix_acl_release(resp->acl_access); 78a257cdd0SAndreas Gruenbacher posix_acl_release(resp->acl_default); 7914168d67SChuck Lever goto out; 80a257cdd0SAndreas Gruenbacher } 81a257cdd0SAndreas Gruenbacher 82a257cdd0SAndreas Gruenbacher /* 83a257cdd0SAndreas Gruenbacher * Set the Access and/or Default ACL of a file. 84a257cdd0SAndreas Gruenbacher */ 85a6beb732SChristoph Hellwig static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp) 86a257cdd0SAndreas Gruenbacher { 87a6beb732SChristoph Hellwig struct nfsd3_setaclargs *argp = rqstp->rq_argp; 88a6beb732SChristoph Hellwig struct nfsd3_attrstat *resp = rqstp->rq_resp; 894ac7249eSChristoph Hellwig struct inode *inode; 90a257cdd0SAndreas Gruenbacher svc_fh *fh; 914ac7249eSChristoph Hellwig int error; 92a257cdd0SAndreas Gruenbacher 93a257cdd0SAndreas Gruenbacher fh = fh_copy(&resp->fh, &argp->fh); 9414168d67SChuck Lever resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR); 9514168d67SChuck Lever if (resp->status != nfs_ok) 964ac7249eSChristoph Hellwig goto out; 97a257cdd0SAndreas Gruenbacher 982b0143b5SDavid Howells inode = d_inode(fh->fh_dentry); 99a257cdd0SAndreas Gruenbacher 1004ac7249eSChristoph Hellwig error = fh_want_write(fh); 1014ac7249eSChristoph Hellwig if (error) 1024ac7249eSChristoph Hellwig goto out_errno; 1034ac7249eSChristoph Hellwig 104bb4d53d6SNeilBrown inode_lock(inode); 1054ac7249eSChristoph Hellwig 106*138060baSChristian Brauner error = set_posix_acl(&init_user_ns, fh->fh_dentry, ACL_TYPE_ACCESS, 107e65ce2a5SChristian Brauner argp->acl_access); 10899965378SBen Hutchings if (error) 10999965378SBen Hutchings goto out_drop_lock; 110*138060baSChristian Brauner error = set_posix_acl(&init_user_ns, fh->fh_dentry, ACL_TYPE_DEFAULT, 111e65ce2a5SChristian Brauner argp->acl_default); 11299965378SBen Hutchings 11399965378SBen Hutchings out_drop_lock: 114bb4d53d6SNeilBrown inode_unlock(inode); 1154ac7249eSChristoph Hellwig fh_drop_write(fh); 1164ac7249eSChristoph Hellwig out_errno: 11714168d67SChuck Lever resp->status = nfserrno(error); 1184ac7249eSChristoph Hellwig out: 119a257cdd0SAndreas Gruenbacher /* argp->acl_{access,default} may have been allocated in 120a257cdd0SAndreas Gruenbacher nfs3svc_decode_setaclargs. */ 121a257cdd0SAndreas Gruenbacher posix_acl_release(argp->acl_access); 122a257cdd0SAndreas Gruenbacher posix_acl_release(argp->acl_default); 123cc028a10SChuck Lever return rpc_success; 124a257cdd0SAndreas Gruenbacher } 125a257cdd0SAndreas Gruenbacher 126a257cdd0SAndreas Gruenbacher /* 127a257cdd0SAndreas Gruenbacher * XDR decode functions 128a257cdd0SAndreas Gruenbacher */ 12905027eafSChuck Lever 130c44b31c2SChuck Lever static bool 13116c66364SChuck Lever nfs3svc_decode_getaclargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) 132a257cdd0SAndreas Gruenbacher { 133026fec7eSChristoph Hellwig struct nfsd3_getaclargs *args = rqstp->rq_argp; 134026fec7eSChristoph Hellwig 13505027eafSChuck Lever if (!svcxdr_decode_nfs_fh3(xdr, &args->fh)) 136c44b31c2SChuck Lever return false; 13705027eafSChuck Lever if (xdr_stream_decode_u32(xdr, &args->mask) < 0) 138c44b31c2SChuck Lever return false; 139a257cdd0SAndreas Gruenbacher 140c44b31c2SChuck Lever return true; 141a257cdd0SAndreas Gruenbacher } 142a257cdd0SAndreas Gruenbacher 143c44b31c2SChuck Lever static bool 14416c66364SChuck Lever nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) 145a257cdd0SAndreas Gruenbacher { 14668519ff2SChuck Lever struct nfsd3_setaclargs *argp = rqstp->rq_argp; 147a257cdd0SAndreas Gruenbacher 14868519ff2SChuck Lever if (!svcxdr_decode_nfs_fh3(xdr, &argp->fh)) 149c44b31c2SChuck Lever return false; 15068519ff2SChuck Lever if (xdr_stream_decode_u32(xdr, &argp->mask) < 0) 151c44b31c2SChuck Lever return false; 15268519ff2SChuck Lever if (argp->mask & ~NFS_ACL_MASK) 153c44b31c2SChuck Lever return false; 15468519ff2SChuck Lever if (!nfs_stream_decode_acl(xdr, NULL, (argp->mask & NFS_ACL) ? 15568519ff2SChuck Lever &argp->acl_access : NULL)) 156c44b31c2SChuck Lever return false; 15768519ff2SChuck Lever if (!nfs_stream_decode_acl(xdr, NULL, (argp->mask & NFS_DFACL) ? 15868519ff2SChuck Lever &argp->acl_default : NULL)) 159c44b31c2SChuck Lever return false; 160a257cdd0SAndreas Gruenbacher 161c44b31c2SChuck Lever return true; 162a257cdd0SAndreas Gruenbacher } 163a257cdd0SAndreas Gruenbacher 164a257cdd0SAndreas Gruenbacher /* 165a257cdd0SAndreas Gruenbacher * XDR encode functions 166a257cdd0SAndreas Gruenbacher */ 167a257cdd0SAndreas Gruenbacher 168a257cdd0SAndreas Gruenbacher /* GETACL */ 169130e2054SChuck Lever static bool 170fda49441SChuck Lever nfs3svc_encode_getaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr) 171a257cdd0SAndreas Gruenbacher { 17263f8de37SChristoph Hellwig struct nfsd3_getaclres *resp = rqstp->rq_resp; 173a257cdd0SAndreas Gruenbacher struct dentry *dentry = resp->fh.fh_dentry; 174a257cdd0SAndreas Gruenbacher struct kvec *head = rqstp->rq_res.head; 175ab1016d3SJ. Bruce Fields struct inode *inode; 176a257cdd0SAndreas Gruenbacher unsigned int base; 177a257cdd0SAndreas Gruenbacher int n; 17814d2b59eSJesper Juhl int w; 179a257cdd0SAndreas Gruenbacher 18020798dfeSChuck Lever if (!svcxdr_encode_nfsstat3(xdr, resp->status)) 181130e2054SChuck Lever return false; 18220798dfeSChuck Lever switch (resp->status) { 18320798dfeSChuck Lever case nfs_ok: 184ab1016d3SJ. Bruce Fields inode = d_inode(dentry); 18520798dfeSChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh)) 186130e2054SChuck Lever return false; 18720798dfeSChuck Lever if (xdr_stream_encode_u32(xdr, resp->mask) < 0) 188130e2054SChuck Lever return false; 18920798dfeSChuck Lever 19020798dfeSChuck Lever base = (char *)xdr->p - (char *)head->iov_base; 191a257cdd0SAndreas Gruenbacher 19214d2b59eSJesper Juhl rqstp->rq_res.page_len = w = nfsacl_size( 19314d2b59eSJesper Juhl (resp->mask & NFS_ACL) ? resp->acl_access : NULL, 19414d2b59eSJesper Juhl (resp->mask & NFS_DFACL) ? resp->acl_default : NULL); 195a257cdd0SAndreas Gruenbacher while (w > 0) { 196afc59400SJ. Bruce Fields if (!*(rqstp->rq_next_page++)) 197130e2054SChuck Lever return false; 198a257cdd0SAndreas Gruenbacher w -= PAGE_SIZE; 199a257cdd0SAndreas Gruenbacher } 200a257cdd0SAndreas Gruenbacher 201a257cdd0SAndreas Gruenbacher n = nfsacl_encode(&rqstp->rq_res, base, inode, 202a257cdd0SAndreas Gruenbacher resp->acl_access, 203a257cdd0SAndreas Gruenbacher resp->mask & NFS_ACL, 0); 204a257cdd0SAndreas Gruenbacher if (n > 0) 205a257cdd0SAndreas Gruenbacher n = nfsacl_encode(&rqstp->rq_res, base + n, inode, 206a257cdd0SAndreas Gruenbacher resp->acl_default, 207a257cdd0SAndreas Gruenbacher resp->mask & NFS_DFACL, 208a257cdd0SAndreas Gruenbacher NFS_ACL_DEFAULT); 209a257cdd0SAndreas Gruenbacher if (n <= 0) 210130e2054SChuck Lever return false; 21120798dfeSChuck Lever break; 21220798dfeSChuck Lever default: 21320798dfeSChuck Lever if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh)) 214130e2054SChuck Lever return false; 21520798dfeSChuck Lever } 216a257cdd0SAndreas Gruenbacher 217130e2054SChuck Lever return true; 218a257cdd0SAndreas Gruenbacher } 219a257cdd0SAndreas Gruenbacher 220a257cdd0SAndreas Gruenbacher /* SETACL */ 221130e2054SChuck Lever static bool 222fda49441SChuck Lever nfs3svc_encode_setaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr) 223a257cdd0SAndreas Gruenbacher { 22463f8de37SChristoph Hellwig struct nfsd3_attrstat *resp = rqstp->rq_resp; 22563f8de37SChristoph Hellwig 22615e432bfSChuck Lever return svcxdr_encode_nfsstat3(xdr, resp->status) && 22715e432bfSChuck Lever svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh); 228a257cdd0SAndreas Gruenbacher } 229a257cdd0SAndreas Gruenbacher 230a257cdd0SAndreas Gruenbacher /* 231a257cdd0SAndreas Gruenbacher * XDR release functions 232a257cdd0SAndreas Gruenbacher */ 2338537488bSChristoph Hellwig static void nfs3svc_release_getacl(struct svc_rqst *rqstp) 234a257cdd0SAndreas Gruenbacher { 2358537488bSChristoph Hellwig struct nfsd3_getaclres *resp = rqstp->rq_resp; 2368537488bSChristoph Hellwig 237a257cdd0SAndreas Gruenbacher fh_put(&resp->fh); 238a257cdd0SAndreas Gruenbacher posix_acl_release(resp->acl_access); 239a257cdd0SAndreas Gruenbacher posix_acl_release(resp->acl_default); 240a257cdd0SAndreas Gruenbacher } 241a257cdd0SAndreas Gruenbacher 242a257cdd0SAndreas Gruenbacher struct nfsd3_voidargs { int dummy; }; 243a257cdd0SAndreas Gruenbacher 244a257cdd0SAndreas Gruenbacher #define ST 1 /* status*/ 245a257cdd0SAndreas Gruenbacher #define AT 21 /* attributes */ 246a257cdd0SAndreas Gruenbacher #define pAT (1+AT) /* post attributes - conditional */ 247a257cdd0SAndreas Gruenbacher #define ACL (1+NFS_ACL_MAX_ENTRIES*3) /* Access Control List */ 248a257cdd0SAndreas Gruenbacher 249ba1df797SChuck Lever static const struct svc_procedure nfsd_acl_procedures3[3] = { 250ba1df797SChuck Lever [ACLPROC3_NULL] = { 251ba1df797SChuck Lever .pc_func = nfsd3_proc_null, 252788f7183SChuck Lever .pc_decode = nfssvc_decode_voidarg, 253788f7183SChuck Lever .pc_encode = nfssvc_encode_voidres, 254788f7183SChuck Lever .pc_argsize = sizeof(struct nfsd_voidargs), 255103cc1faSChuck Lever .pc_argzero = sizeof(struct nfsd_voidargs), 256788f7183SChuck Lever .pc_ressize = sizeof(struct nfsd_voidres), 257ba1df797SChuck Lever .pc_cachetype = RC_NOCACHE, 258ba1df797SChuck Lever .pc_xdrressize = ST, 2592289e87bSChuck Lever .pc_name = "NULL", 260ba1df797SChuck Lever }, 261ba1df797SChuck Lever [ACLPROC3_GETACL] = { 262ba1df797SChuck Lever .pc_func = nfsd3_proc_getacl, 263ba1df797SChuck Lever .pc_decode = nfs3svc_decode_getaclargs, 264ba1df797SChuck Lever .pc_encode = nfs3svc_encode_getaclres, 265ba1df797SChuck Lever .pc_release = nfs3svc_release_getacl, 266ba1df797SChuck Lever .pc_argsize = sizeof(struct nfsd3_getaclargs), 267103cc1faSChuck Lever .pc_argzero = sizeof(struct nfsd3_getaclargs), 268ba1df797SChuck Lever .pc_ressize = sizeof(struct nfsd3_getaclres), 269ba1df797SChuck Lever .pc_cachetype = RC_NOCACHE, 270ba1df797SChuck Lever .pc_xdrressize = ST+1+2*(1+ACL), 2712289e87bSChuck Lever .pc_name = "GETACL", 272ba1df797SChuck Lever }, 273ba1df797SChuck Lever [ACLPROC3_SETACL] = { 274ba1df797SChuck Lever .pc_func = nfsd3_proc_setacl, 275ba1df797SChuck Lever .pc_decode = nfs3svc_decode_setaclargs, 276ba1df797SChuck Lever .pc_encode = nfs3svc_encode_setaclres, 277ba1df797SChuck Lever .pc_release = nfs3svc_release_fhandle, 278ba1df797SChuck Lever .pc_argsize = sizeof(struct nfsd3_setaclargs), 279103cc1faSChuck Lever .pc_argzero = sizeof(struct nfsd3_setaclargs), 280ba1df797SChuck Lever .pc_ressize = sizeof(struct nfsd3_attrstat), 281ba1df797SChuck Lever .pc_cachetype = RC_NOCACHE, 282ba1df797SChuck Lever .pc_xdrressize = ST+pAT, 2832289e87bSChuck Lever .pc_name = "SETACL", 284ba1df797SChuck Lever }, 285a257cdd0SAndreas Gruenbacher }; 286a257cdd0SAndreas Gruenbacher 2877fd38af9SChristoph Hellwig static unsigned int nfsd_acl_count3[ARRAY_SIZE(nfsd_acl_procedures3)]; 288e9679189SChristoph Hellwig const struct svc_version nfsd_acl_version3 = { 289a257cdd0SAndreas Gruenbacher .vs_vers = 3, 290a257cdd0SAndreas Gruenbacher .vs_nproc = 3, 291a257cdd0SAndreas Gruenbacher .vs_proc = nfsd_acl_procedures3, 2927fd38af9SChristoph Hellwig .vs_count = nfsd_acl_count3, 293a257cdd0SAndreas Gruenbacher .vs_dispatch = nfsd_dispatch, 294a257cdd0SAndreas Gruenbacher .vs_xdrsize = NFS3_SVC_XDRSIZE, 295a257cdd0SAndreas Gruenbacher }; 296a257cdd0SAndreas Gruenbacher 297