1/* 2 * Process version 2 NFSACL requests. 3 * 4 * Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de> 5 */ 6 7#include "nfsd.h" 8#include <linux/nfsacl.h> 9#include <linux/gfp.h> 10#include "cache.h" 11#include "xdr3.h" 12#include "vfs.h" 13 14#define NFSDDBG_FACILITY NFSDDBG_PROC 15#define RETURN_STATUS(st) { resp->status = (st); return (st); } 16 17/* 18 * NULL call. 19 */ 20static __be32 21nfsacld_proc_null(struct svc_rqst *rqstp, void *argp, void *resp) 22{ 23 return nfs_ok; 24} 25 26/* 27 * Get the Access and/or Default ACL of a file. 28 */ 29static __be32 nfsacld_proc_getacl(struct svc_rqst * rqstp, 30 struct nfsd3_getaclargs *argp, struct nfsd3_getaclres *resp) 31{ 32 svc_fh *fh; 33 struct posix_acl *acl; 34 __be32 nfserr = 0; 35 36 dprintk("nfsd: GETACL(2acl) %s\n", SVCFH_fmt(&argp->fh)); 37 38 fh = fh_copy(&resp->fh, &argp->fh); 39 nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); 40 if (nfserr) 41 RETURN_STATUS(nfserr); 42 43 if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT)) 44 RETURN_STATUS(nfserr_inval); 45 resp->mask = argp->mask; 46 47 if (resp->mask & (NFS_ACL|NFS_ACLCNT)) { 48 acl = nfsd_get_posix_acl(fh, ACL_TYPE_ACCESS); 49 if (IS_ERR(acl)) { 50 int err = PTR_ERR(acl); 51 52 if (err == -ENODATA || err == -EOPNOTSUPP) 53 acl = NULL; 54 else { 55 nfserr = nfserrno(err); 56 goto fail; 57 } 58 } 59 if (acl == NULL) { 60 /* Solaris returns the inode's minimum ACL. */ 61 62 struct inode *inode = fh->fh_dentry->d_inode; 63 acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); 64 } 65 resp->acl_access = acl; 66 } 67 if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) { 68 /* Check how Solaris handles requests for the Default ACL 69 of a non-directory! */ 70 71 acl = nfsd_get_posix_acl(fh, ACL_TYPE_DEFAULT); 72 if (IS_ERR(acl)) { 73 int err = PTR_ERR(acl); 74 75 if (err == -ENODATA || err == -EOPNOTSUPP) 76 acl = NULL; 77 else { 78 nfserr = nfserrno(err); 79 goto fail; 80 } 81 } 82 resp->acl_default = acl; 83 } 84 85 /* resp->acl_{access,default} are released in nfssvc_release_getacl. */ 86 RETURN_STATUS(0); 87 88fail: 89 posix_acl_release(resp->acl_access); 90 posix_acl_release(resp->acl_default); 91 RETURN_STATUS(nfserr); 92} 93 94/* 95 * Set the Access and/or Default ACL of a file. 96 */ 97static __be32 nfsacld_proc_setacl(struct svc_rqst * rqstp, 98 struct nfsd3_setaclargs *argp, 99 struct nfsd_attrstat *resp) 100{ 101 svc_fh *fh; 102 __be32 nfserr = 0; 103 104 dprintk("nfsd: SETACL(2acl) %s\n", SVCFH_fmt(&argp->fh)); 105 106 fh = fh_copy(&resp->fh, &argp->fh); 107 nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR); 108 109 if (!nfserr) { 110 nfserr = nfserrno( nfsd_set_posix_acl( 111 fh, ACL_TYPE_ACCESS, argp->acl_access) ); 112 } 113 if (!nfserr) { 114 nfserr = nfserrno( nfsd_set_posix_acl( 115 fh, ACL_TYPE_DEFAULT, argp->acl_default) ); 116 } 117 118 /* argp->acl_{access,default} may have been allocated in 119 nfssvc_decode_setaclargs. */ 120 posix_acl_release(argp->acl_access); 121 posix_acl_release(argp->acl_default); 122 return nfserr; 123} 124 125/* 126 * Check file attributes 127 */ 128static __be32 nfsacld_proc_getattr(struct svc_rqst * rqstp, 129 struct nfsd_fhandle *argp, struct nfsd_attrstat *resp) 130{ 131 dprintk("nfsd: GETATTR %s\n", SVCFH_fmt(&argp->fh)); 132 133 fh_copy(&resp->fh, &argp->fh); 134 return fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); 135} 136 137/* 138 * Check file access 139 */ 140static __be32 nfsacld_proc_access(struct svc_rqst *rqstp, struct nfsd3_accessargs *argp, 141 struct nfsd3_accessres *resp) 142{ 143 __be32 nfserr; 144 145 dprintk("nfsd: ACCESS(2acl) %s 0x%x\n", 146 SVCFH_fmt(&argp->fh), 147 argp->access); 148 149 fh_copy(&resp->fh, &argp->fh); 150 resp->access = argp->access; 151 nfserr = nfsd_access(rqstp, &resp->fh, &resp->access, NULL); 152 return nfserr; 153} 154 155/* 156 * XDR decode functions 157 */ 158static int nfsaclsvc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p, 159 struct nfsd3_getaclargs *argp) 160{ 161 if (!(p = nfs2svc_decode_fh(p, &argp->fh))) 162 return 0; 163 argp->mask = ntohl(*p); p++; 164 165 return xdr_argsize_check(rqstp, p); 166} 167 168 169static int nfsaclsvc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p, 170 struct nfsd3_setaclargs *argp) 171{ 172 struct kvec *head = rqstp->rq_arg.head; 173 unsigned int base; 174 int n; 175 176 if (!(p = nfs2svc_decode_fh(p, &argp->fh))) 177 return 0; 178 argp->mask = ntohl(*p++); 179 if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT) || 180 !xdr_argsize_check(rqstp, p)) 181 return 0; 182 183 base = (char *)p - (char *)head->iov_base; 184 n = nfsacl_decode(&rqstp->rq_arg, base, NULL, 185 (argp->mask & NFS_ACL) ? 186 &argp->acl_access : NULL); 187 if (n > 0) 188 n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL, 189 (argp->mask & NFS_DFACL) ? 190 &argp->acl_default : NULL); 191 return (n > 0); 192} 193 194static int nfsaclsvc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p, 195 struct nfsd_fhandle *argp) 196{ 197 if (!(p = nfs2svc_decode_fh(p, &argp->fh))) 198 return 0; 199 return xdr_argsize_check(rqstp, p); 200} 201 202static int nfsaclsvc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p, 203 struct nfsd3_accessargs *argp) 204{ 205 if (!(p = nfs2svc_decode_fh(p, &argp->fh))) 206 return 0; 207 argp->access = ntohl(*p++); 208 209 return xdr_argsize_check(rqstp, p); 210} 211 212/* 213 * XDR encode functions 214 */ 215 216/* 217 * There must be an encoding function for void results so svc_process 218 * will work properly. 219 */ 220int 221nfsaclsvc_encode_voidres(struct svc_rqst *rqstp, __be32 *p, void *dummy) 222{ 223 return xdr_ressize_check(rqstp, p); 224} 225 226/* GETACL */ 227static int nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p, 228 struct nfsd3_getaclres *resp) 229{ 230 struct dentry *dentry = resp->fh.fh_dentry; 231 struct inode *inode; 232 struct kvec *head = rqstp->rq_res.head; 233 unsigned int base; 234 int n; 235 int w; 236 237 /* 238 * Since this is version 2, the check for nfserr in 239 * nfsd_dispatch actually ensures the following cannot happen. 240 * However, it seems fragile to depend on that. 241 */ 242 if (dentry == NULL || dentry->d_inode == NULL) 243 return 0; 244 inode = dentry->d_inode; 245 246 p = nfs2svc_encode_fattr(rqstp, p, &resp->fh); 247 *p++ = htonl(resp->mask); 248 if (!xdr_ressize_check(rqstp, p)) 249 return 0; 250 base = (char *)p - (char *)head->iov_base; 251 252 rqstp->rq_res.page_len = w = nfsacl_size( 253 (resp->mask & NFS_ACL) ? resp->acl_access : NULL, 254 (resp->mask & NFS_DFACL) ? resp->acl_default : NULL); 255 while (w > 0) { 256 if (!rqstp->rq_respages[rqstp->rq_resused++]) 257 return 0; 258 w -= PAGE_SIZE; 259 } 260 261 n = nfsacl_encode(&rqstp->rq_res, base, inode, 262 resp->acl_access, 263 resp->mask & NFS_ACL, 0); 264 if (n > 0) 265 n = nfsacl_encode(&rqstp->rq_res, base + n, inode, 266 resp->acl_default, 267 resp->mask & NFS_DFACL, 268 NFS_ACL_DEFAULT); 269 if (n <= 0) 270 return 0; 271 return 1; 272} 273 274static int nfsaclsvc_encode_attrstatres(struct svc_rqst *rqstp, __be32 *p, 275 struct nfsd_attrstat *resp) 276{ 277 p = nfs2svc_encode_fattr(rqstp, p, &resp->fh); 278 return xdr_ressize_check(rqstp, p); 279} 280 281/* ACCESS */ 282static int nfsaclsvc_encode_accessres(struct svc_rqst *rqstp, __be32 *p, 283 struct nfsd3_accessres *resp) 284{ 285 p = nfs2svc_encode_fattr(rqstp, p, &resp->fh); 286 *p++ = htonl(resp->access); 287 return xdr_ressize_check(rqstp, p); 288} 289 290/* 291 * XDR release functions 292 */ 293static int nfsaclsvc_release_getacl(struct svc_rqst *rqstp, __be32 *p, 294 struct nfsd3_getaclres *resp) 295{ 296 fh_put(&resp->fh); 297 posix_acl_release(resp->acl_access); 298 posix_acl_release(resp->acl_default); 299 return 1; 300} 301 302static int nfsaclsvc_release_attrstat(struct svc_rqst *rqstp, __be32 *p, 303 struct nfsd_attrstat *resp) 304{ 305 fh_put(&resp->fh); 306 return 1; 307} 308 309static int nfsaclsvc_release_access(struct svc_rqst *rqstp, __be32 *p, 310 struct nfsd3_accessres *resp) 311{ 312 fh_put(&resp->fh); 313 return 1; 314} 315 316#define nfsaclsvc_decode_voidargs NULL 317#define nfsaclsvc_release_void NULL 318#define nfsd3_fhandleargs nfsd_fhandle 319#define nfsd3_attrstatres nfsd_attrstat 320#define nfsd3_voidres nfsd3_voidargs 321struct nfsd3_voidargs { int dummy; }; 322 323#define PROC(name, argt, rest, relt, cache, respsize) \ 324 { (svc_procfunc) nfsacld_proc_##name, \ 325 (kxdrproc_t) nfsaclsvc_decode_##argt##args, \ 326 (kxdrproc_t) nfsaclsvc_encode_##rest##res, \ 327 (kxdrproc_t) nfsaclsvc_release_##relt, \ 328 sizeof(struct nfsd3_##argt##args), \ 329 sizeof(struct nfsd3_##rest##res), \ 330 0, \ 331 cache, \ 332 respsize, \ 333 } 334 335#define ST 1 /* status*/ 336#define AT 21 /* attributes */ 337#define pAT (1+AT) /* post attributes - conditional */ 338#define ACL (1+NFS_ACL_MAX_ENTRIES*3) /* Access Control List */ 339 340static struct svc_procedure nfsd_acl_procedures2[] = { 341 PROC(null, void, void, void, RC_NOCACHE, ST), 342 PROC(getacl, getacl, getacl, getacl, RC_NOCACHE, ST+1+2*(1+ACL)), 343 PROC(setacl, setacl, attrstat, attrstat, RC_NOCACHE, ST+AT), 344 PROC(getattr, fhandle, attrstat, attrstat, RC_NOCACHE, ST+AT), 345 PROC(access, access, access, access, RC_NOCACHE, ST+AT+1), 346}; 347 348struct svc_version nfsd_acl_version2 = { 349 .vs_vers = 2, 350 .vs_nproc = 5, 351 .vs_proc = nfsd_acl_procedures2, 352 .vs_dispatch = nfsd_dispatch, 353 .vs_xdrsize = NFS3_SVC_XDRSIZE, 354 .vs_hidden = 0, 355}; 356