1a257cdd0SAndreas Gruenbacher /* 2a257cdd0SAndreas Gruenbacher * Process version 3 NFSACL requests. 3a257cdd0SAndreas Gruenbacher * 4a257cdd0SAndreas Gruenbacher * Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de> 5a257cdd0SAndreas Gruenbacher */ 6a257cdd0SAndreas Gruenbacher 79a74af21SBoaz Harrosh #include "nfsd.h" 89a74af21SBoaz Harrosh /* FIXME: nfsacl.h is a broken header */ 9a257cdd0SAndreas Gruenbacher #include <linux/nfsacl.h> 105a0e3ad6STejun Heo #include <linux/gfp.h> 119a74af21SBoaz Harrosh #include "cache.h" 129a74af21SBoaz Harrosh #include "xdr3.h" 130a3adadeSJ. Bruce Fields #include "vfs.h" 14a257cdd0SAndreas Gruenbacher 15a257cdd0SAndreas Gruenbacher #define RETURN_STATUS(st) { resp->status = (st); return (st); } 16a257cdd0SAndreas Gruenbacher 17a257cdd0SAndreas Gruenbacher /* 18a257cdd0SAndreas Gruenbacher * NULL call. 19a257cdd0SAndreas Gruenbacher */ 207111c66eSAl Viro static __be32 21a257cdd0SAndreas Gruenbacher nfsd3_proc_null(struct svc_rqst *rqstp, void *argp, void *resp) 22a257cdd0SAndreas Gruenbacher { 23a257cdd0SAndreas Gruenbacher return nfs_ok; 24a257cdd0SAndreas Gruenbacher } 25a257cdd0SAndreas Gruenbacher 26a257cdd0SAndreas Gruenbacher /* 27a257cdd0SAndreas Gruenbacher * Get the Access and/or Default ACL of a file. 28a257cdd0SAndreas Gruenbacher */ 297111c66eSAl Viro static __be32 nfsd3_proc_getacl(struct svc_rqst * rqstp, 30a257cdd0SAndreas Gruenbacher struct nfsd3_getaclargs *argp, struct nfsd3_getaclres *resp) 31a257cdd0SAndreas Gruenbacher { 32a257cdd0SAndreas Gruenbacher struct posix_acl *acl; 334ac7249eSChristoph Hellwig struct inode *inode; 344ac7249eSChristoph Hellwig svc_fh *fh; 35c4d987baSAl Viro __be32 nfserr = 0; 36a257cdd0SAndreas Gruenbacher 37a257cdd0SAndreas Gruenbacher fh = fh_copy(&resp->fh, &argp->fh); 388837abcaSMiklos Szeredi nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); 398837abcaSMiklos Szeredi if (nfserr) 40ac8587dcSJ. Bruce Fields RETURN_STATUS(nfserr); 41a257cdd0SAndreas Gruenbacher 424ac7249eSChristoph Hellwig inode = fh->fh_dentry->d_inode; 434ac7249eSChristoph Hellwig 44a257cdd0SAndreas Gruenbacher if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT)) 45a257cdd0SAndreas Gruenbacher RETURN_STATUS(nfserr_inval); 46a257cdd0SAndreas Gruenbacher resp->mask = argp->mask; 47a257cdd0SAndreas Gruenbacher 48a257cdd0SAndreas Gruenbacher if (resp->mask & (NFS_ACL|NFS_ACLCNT)) { 494ac7249eSChristoph Hellwig acl = get_acl(inode, ACL_TYPE_ACCESS); 50a257cdd0SAndreas Gruenbacher if (IS_ERR(acl)) { 514ac7249eSChristoph Hellwig nfserr = nfserrno(PTR_ERR(acl)); 52a257cdd0SAndreas Gruenbacher goto fail; 53a257cdd0SAndreas Gruenbacher } 54a257cdd0SAndreas Gruenbacher if (acl == NULL) { 55a257cdd0SAndreas Gruenbacher /* Solaris returns the inode's minimum ACL. */ 56a257cdd0SAndreas Gruenbacher acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); 57a257cdd0SAndreas Gruenbacher } 58a257cdd0SAndreas Gruenbacher resp->acl_access = acl; 59a257cdd0SAndreas Gruenbacher } 60a257cdd0SAndreas Gruenbacher if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) { 61a257cdd0SAndreas Gruenbacher /* Check how Solaris handles requests for the Default ACL 62a257cdd0SAndreas Gruenbacher of a non-directory! */ 634ac7249eSChristoph Hellwig acl = get_acl(inode, ACL_TYPE_DEFAULT); 64a257cdd0SAndreas Gruenbacher if (IS_ERR(acl)) { 654ac7249eSChristoph Hellwig nfserr = nfserrno(PTR_ERR(acl)); 66a257cdd0SAndreas Gruenbacher goto fail; 67a257cdd0SAndreas Gruenbacher } 68a257cdd0SAndreas Gruenbacher resp->acl_default = acl; 69a257cdd0SAndreas Gruenbacher } 70a257cdd0SAndreas Gruenbacher 71a257cdd0SAndreas Gruenbacher /* resp->acl_{access,default} are released in nfs3svc_release_getacl. */ 72a257cdd0SAndreas Gruenbacher RETURN_STATUS(0); 73a257cdd0SAndreas Gruenbacher 74a257cdd0SAndreas Gruenbacher fail: 75a257cdd0SAndreas Gruenbacher posix_acl_release(resp->acl_access); 76a257cdd0SAndreas Gruenbacher posix_acl_release(resp->acl_default); 77a257cdd0SAndreas Gruenbacher RETURN_STATUS(nfserr); 78a257cdd0SAndreas Gruenbacher } 79a257cdd0SAndreas Gruenbacher 80a257cdd0SAndreas Gruenbacher /* 81a257cdd0SAndreas Gruenbacher * Set the Access and/or Default ACL of a file. 82a257cdd0SAndreas Gruenbacher */ 837111c66eSAl Viro static __be32 nfsd3_proc_setacl(struct svc_rqst * rqstp, 84a257cdd0SAndreas Gruenbacher struct nfsd3_setaclargs *argp, 85a257cdd0SAndreas Gruenbacher struct nfsd3_attrstat *resp) 86a257cdd0SAndreas Gruenbacher { 874ac7249eSChristoph Hellwig struct inode *inode; 88a257cdd0SAndreas Gruenbacher svc_fh *fh; 89c4d987baSAl Viro __be32 nfserr = 0; 904ac7249eSChristoph Hellwig int error; 91a257cdd0SAndreas Gruenbacher 92a257cdd0SAndreas Gruenbacher fh = fh_copy(&resp->fh, &argp->fh); 938837abcaSMiklos Szeredi nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR); 944ac7249eSChristoph Hellwig if (nfserr) 954ac7249eSChristoph Hellwig goto out; 96a257cdd0SAndreas Gruenbacher 974ac7249eSChristoph Hellwig inode = fh->fh_dentry->d_inode; 984ac7249eSChristoph Hellwig if (!IS_POSIXACL(inode) || !inode->i_op->set_acl) { 994ac7249eSChristoph Hellwig error = -EOPNOTSUPP; 1004ac7249eSChristoph Hellwig goto out_errno; 101a257cdd0SAndreas Gruenbacher } 102a257cdd0SAndreas Gruenbacher 1034ac7249eSChristoph Hellwig error = fh_want_write(fh); 1044ac7249eSChristoph Hellwig if (error) 1054ac7249eSChristoph Hellwig goto out_errno; 1064ac7249eSChristoph Hellwig 1074ac7249eSChristoph Hellwig error = inode->i_op->set_acl(inode, argp->acl_access, ACL_TYPE_ACCESS); 1084ac7249eSChristoph Hellwig if (error) 1094ac7249eSChristoph Hellwig goto out_drop_write; 1104ac7249eSChristoph Hellwig error = inode->i_op->set_acl(inode, argp->acl_default, 1114ac7249eSChristoph Hellwig ACL_TYPE_DEFAULT); 1124ac7249eSChristoph Hellwig 1134ac7249eSChristoph Hellwig out_drop_write: 1144ac7249eSChristoph Hellwig fh_drop_write(fh); 1154ac7249eSChristoph Hellwig out_errno: 1164ac7249eSChristoph Hellwig nfserr = nfserrno(error); 1174ac7249eSChristoph Hellwig out: 118a257cdd0SAndreas Gruenbacher /* argp->acl_{access,default} may have been allocated in 119a257cdd0SAndreas Gruenbacher nfs3svc_decode_setaclargs. */ 120a257cdd0SAndreas Gruenbacher posix_acl_release(argp->acl_access); 121a257cdd0SAndreas Gruenbacher posix_acl_release(argp->acl_default); 122a257cdd0SAndreas Gruenbacher RETURN_STATUS(nfserr); 123a257cdd0SAndreas Gruenbacher } 124a257cdd0SAndreas Gruenbacher 125a257cdd0SAndreas Gruenbacher /* 126a257cdd0SAndreas Gruenbacher * XDR decode functions 127a257cdd0SAndreas Gruenbacher */ 12891f07168SAl Viro static int nfs3svc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p, 129a257cdd0SAndreas Gruenbacher struct nfsd3_getaclargs *args) 130a257cdd0SAndreas Gruenbacher { 131a257cdd0SAndreas Gruenbacher if (!(p = nfs3svc_decode_fh(p, &args->fh))) 132a257cdd0SAndreas Gruenbacher return 0; 133a257cdd0SAndreas Gruenbacher args->mask = ntohl(*p); p++; 134a257cdd0SAndreas Gruenbacher 135a257cdd0SAndreas Gruenbacher return xdr_argsize_check(rqstp, p); 136a257cdd0SAndreas Gruenbacher } 137a257cdd0SAndreas Gruenbacher 138a257cdd0SAndreas Gruenbacher 13991f07168SAl Viro static int nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p, 140a257cdd0SAndreas Gruenbacher struct nfsd3_setaclargs *args) 141a257cdd0SAndreas Gruenbacher { 142a257cdd0SAndreas Gruenbacher struct kvec *head = rqstp->rq_arg.head; 143a257cdd0SAndreas Gruenbacher unsigned int base; 144a257cdd0SAndreas Gruenbacher int n; 145a257cdd0SAndreas Gruenbacher 146a257cdd0SAndreas Gruenbacher if (!(p = nfs3svc_decode_fh(p, &args->fh))) 147a257cdd0SAndreas Gruenbacher return 0; 148a257cdd0SAndreas Gruenbacher args->mask = ntohl(*p++); 149a257cdd0SAndreas Gruenbacher if (args->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT) || 150a257cdd0SAndreas Gruenbacher !xdr_argsize_check(rqstp, p)) 151a257cdd0SAndreas Gruenbacher return 0; 152a257cdd0SAndreas Gruenbacher 153a257cdd0SAndreas Gruenbacher base = (char *)p - (char *)head->iov_base; 154a257cdd0SAndreas Gruenbacher n = nfsacl_decode(&rqstp->rq_arg, base, NULL, 155a257cdd0SAndreas Gruenbacher (args->mask & NFS_ACL) ? 156a257cdd0SAndreas Gruenbacher &args->acl_access : NULL); 157a257cdd0SAndreas Gruenbacher if (n > 0) 158a257cdd0SAndreas Gruenbacher n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL, 159a257cdd0SAndreas Gruenbacher (args->mask & NFS_DFACL) ? 160a257cdd0SAndreas Gruenbacher &args->acl_default : NULL); 161a257cdd0SAndreas Gruenbacher return (n > 0); 162a257cdd0SAndreas Gruenbacher } 163a257cdd0SAndreas Gruenbacher 164a257cdd0SAndreas Gruenbacher /* 165a257cdd0SAndreas Gruenbacher * XDR encode functions 166a257cdd0SAndreas Gruenbacher */ 167a257cdd0SAndreas Gruenbacher 168a257cdd0SAndreas Gruenbacher /* GETACL */ 16991f07168SAl Viro static int nfs3svc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p, 170a257cdd0SAndreas Gruenbacher struct nfsd3_getaclres *resp) 171a257cdd0SAndreas Gruenbacher { 172a257cdd0SAndreas Gruenbacher struct dentry *dentry = resp->fh.fh_dentry; 173a257cdd0SAndreas Gruenbacher 174a257cdd0SAndreas Gruenbacher p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh); 175a257cdd0SAndreas Gruenbacher if (resp->status == 0 && dentry && dentry->d_inode) { 176a257cdd0SAndreas Gruenbacher struct inode *inode = dentry->d_inode; 177a257cdd0SAndreas Gruenbacher struct kvec *head = rqstp->rq_res.head; 178a257cdd0SAndreas Gruenbacher unsigned int base; 179a257cdd0SAndreas Gruenbacher int n; 18014d2b59eSJesper Juhl int w; 181a257cdd0SAndreas Gruenbacher 182a257cdd0SAndreas Gruenbacher *p++ = htonl(resp->mask); 183a257cdd0SAndreas Gruenbacher if (!xdr_ressize_check(rqstp, p)) 184a257cdd0SAndreas Gruenbacher return 0; 185a257cdd0SAndreas Gruenbacher base = (char *)p - (char *)head->iov_base; 186a257cdd0SAndreas Gruenbacher 18714d2b59eSJesper Juhl rqstp->rq_res.page_len = w = nfsacl_size( 18814d2b59eSJesper Juhl (resp->mask & NFS_ACL) ? resp->acl_access : NULL, 18914d2b59eSJesper Juhl (resp->mask & NFS_DFACL) ? resp->acl_default : NULL); 190a257cdd0SAndreas Gruenbacher while (w > 0) { 191afc59400SJ. Bruce Fields if (!*(rqstp->rq_next_page++)) 192a257cdd0SAndreas Gruenbacher return 0; 193a257cdd0SAndreas Gruenbacher w -= PAGE_SIZE; 194a257cdd0SAndreas Gruenbacher } 195a257cdd0SAndreas Gruenbacher 196a257cdd0SAndreas Gruenbacher n = nfsacl_encode(&rqstp->rq_res, base, inode, 197a257cdd0SAndreas Gruenbacher resp->acl_access, 198a257cdd0SAndreas Gruenbacher resp->mask & NFS_ACL, 0); 199a257cdd0SAndreas Gruenbacher if (n > 0) 200a257cdd0SAndreas Gruenbacher n = nfsacl_encode(&rqstp->rq_res, base + n, inode, 201a257cdd0SAndreas Gruenbacher resp->acl_default, 202a257cdd0SAndreas Gruenbacher resp->mask & NFS_DFACL, 203a257cdd0SAndreas Gruenbacher NFS_ACL_DEFAULT); 204a257cdd0SAndreas Gruenbacher if (n <= 0) 205a257cdd0SAndreas Gruenbacher return 0; 206a257cdd0SAndreas Gruenbacher } else 207a257cdd0SAndreas Gruenbacher if (!xdr_ressize_check(rqstp, p)) 208a257cdd0SAndreas Gruenbacher return 0; 209a257cdd0SAndreas Gruenbacher 210a257cdd0SAndreas Gruenbacher return 1; 211a257cdd0SAndreas Gruenbacher } 212a257cdd0SAndreas Gruenbacher 213a257cdd0SAndreas Gruenbacher /* SETACL */ 21491f07168SAl Viro static int nfs3svc_encode_setaclres(struct svc_rqst *rqstp, __be32 *p, 215a257cdd0SAndreas Gruenbacher struct nfsd3_attrstat *resp) 216a257cdd0SAndreas Gruenbacher { 217a257cdd0SAndreas Gruenbacher p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh); 218a257cdd0SAndreas Gruenbacher 219a257cdd0SAndreas Gruenbacher return xdr_ressize_check(rqstp, p); 220a257cdd0SAndreas Gruenbacher } 221a257cdd0SAndreas Gruenbacher 222a257cdd0SAndreas Gruenbacher /* 223a257cdd0SAndreas Gruenbacher * XDR release functions 224a257cdd0SAndreas Gruenbacher */ 22591f07168SAl Viro static int nfs3svc_release_getacl(struct svc_rqst *rqstp, __be32 *p, 226a257cdd0SAndreas Gruenbacher struct nfsd3_getaclres *resp) 227a257cdd0SAndreas Gruenbacher { 228a257cdd0SAndreas Gruenbacher fh_put(&resp->fh); 229a257cdd0SAndreas Gruenbacher posix_acl_release(resp->acl_access); 230a257cdd0SAndreas Gruenbacher posix_acl_release(resp->acl_default); 231a257cdd0SAndreas Gruenbacher return 1; 232a257cdd0SAndreas Gruenbacher } 233a257cdd0SAndreas Gruenbacher 234a257cdd0SAndreas Gruenbacher #define nfs3svc_decode_voidargs NULL 235a257cdd0SAndreas Gruenbacher #define nfs3svc_release_void NULL 236a257cdd0SAndreas Gruenbacher #define nfsd3_setaclres nfsd3_attrstat 237a257cdd0SAndreas Gruenbacher #define nfsd3_voidres nfsd3_voidargs 238a257cdd0SAndreas Gruenbacher struct nfsd3_voidargs { int dummy; }; 239a257cdd0SAndreas Gruenbacher 240a257cdd0SAndreas Gruenbacher #define PROC(name, argt, rest, relt, cache, respsize) \ 241a257cdd0SAndreas Gruenbacher { (svc_procfunc) nfsd3_proc_##name, \ 242a257cdd0SAndreas Gruenbacher (kxdrproc_t) nfs3svc_decode_##argt##args, \ 243a257cdd0SAndreas Gruenbacher (kxdrproc_t) nfs3svc_encode_##rest##res, \ 244a257cdd0SAndreas Gruenbacher (kxdrproc_t) nfs3svc_release_##relt, \ 245a257cdd0SAndreas Gruenbacher sizeof(struct nfsd3_##argt##args), \ 246a257cdd0SAndreas Gruenbacher sizeof(struct nfsd3_##rest##res), \ 247a257cdd0SAndreas Gruenbacher 0, \ 248a257cdd0SAndreas Gruenbacher cache, \ 249a257cdd0SAndreas Gruenbacher respsize, \ 250a257cdd0SAndreas Gruenbacher } 251a257cdd0SAndreas Gruenbacher 252a257cdd0SAndreas Gruenbacher #define ST 1 /* status*/ 253a257cdd0SAndreas Gruenbacher #define AT 21 /* attributes */ 254a257cdd0SAndreas Gruenbacher #define pAT (1+AT) /* post attributes - conditional */ 255a257cdd0SAndreas Gruenbacher #define ACL (1+NFS_ACL_MAX_ENTRIES*3) /* Access Control List */ 256a257cdd0SAndreas Gruenbacher 257a257cdd0SAndreas Gruenbacher static struct svc_procedure nfsd_acl_procedures3[] = { 258a257cdd0SAndreas Gruenbacher PROC(null, void, void, void, RC_NOCACHE, ST), 259a257cdd0SAndreas Gruenbacher PROC(getacl, getacl, getacl, getacl, RC_NOCACHE, ST+1+2*(1+ACL)), 260a257cdd0SAndreas Gruenbacher PROC(setacl, setacl, setacl, fhandle, RC_NOCACHE, ST+pAT), 261a257cdd0SAndreas Gruenbacher }; 262a257cdd0SAndreas Gruenbacher 263a257cdd0SAndreas Gruenbacher struct svc_version nfsd_acl_version3 = { 264a257cdd0SAndreas Gruenbacher .vs_vers = 3, 265a257cdd0SAndreas Gruenbacher .vs_nproc = 3, 266a257cdd0SAndreas Gruenbacher .vs_proc = nfsd_acl_procedures3, 267a257cdd0SAndreas Gruenbacher .vs_dispatch = nfsd_dispatch, 268a257cdd0SAndreas Gruenbacher .vs_xdrsize = NFS3_SVC_XDRSIZE, 2691b7e0403SPeter Staubach .vs_hidden = 0, 270a257cdd0SAndreas Gruenbacher }; 271a257cdd0SAndreas Gruenbacher 272