nfs_nfsdserv.c revision 245613
1292915Sdim/*- 2292915Sdim * Copyright (c) 1989, 1993 3292915Sdim * The Regents of the University of California. All rights reserved. 4292915Sdim * 5292915Sdim * This code is derived from software contributed to Berkeley by 6292915Sdim * Rick Macklem at The University of Guelph. 7292915Sdim * 8292915Sdim * Redistribution and use in source and binary forms, with or without 9292915Sdim * modification, are permitted provided that the following conditions 10292915Sdim * are met: 11292915Sdim * 1. Redistributions of source code must retain the above copyright 12292915Sdim * notice, this list of conditions and the following disclaimer. 13292915Sdim * 2. Redistributions in binary form must reproduce the above copyright 14292915Sdim * notice, this list of conditions and the following disclaimer in the 15292915Sdim * documentation and/or other materials provided with the distribution. 16292915Sdim * 4. Neither the name of the University nor the names of its contributors 17292915Sdim * may be used to endorse or promote products derived from this software 18292915Sdim * without specific prior written permission. 19292915Sdim * 20292915Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21292915Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22292915Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23292915Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24292915Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25292915Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26292915Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27292915Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28292915Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29292915Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30292915Sdim * SUCH DAMAGE. 31292915Sdim * 32292915Sdim */ 33292915Sdim 34292915Sdim#include <sys/cdefs.h> 35292915Sdim__FBSDID("$FreeBSD: head/sys/fs/nfsserver/nfs_nfsdserv.c 245613 2013-01-18 19:42:08Z delphij $"); 36292915Sdim 37292915Sdim/* 38292915Sdim * nfs version 2, 3 and 4 server calls to vnode ops 39292915Sdim * - these routines generally have 3 phases 40292915Sdim * 1 - break down and validate rpc request in mbuf list 41292915Sdim * 2 - do the vnode ops for the request, usually by calling a nfsvno_XXX() 42292915Sdim * function in nfsd_port.c 43292915Sdim * 3 - build the rpc reply in an mbuf list 44292915Sdim * For nfsv4, these functions are called for each Op within the Compound RPC. 45292915Sdim */ 46292915Sdim 47292915Sdim#ifndef APPLEKEXT 48292915Sdim#include <fs/nfs/nfsport.h> 49292915Sdim 50292915Sdim/* Global vars */ 51292915Sdimextern u_int32_t newnfs_false, newnfs_true; 52292915Sdimextern enum vtype nv34tov_type[8]; 53292915Sdimextern struct timeval nfsboottime; 54292915Sdimextern int nfs_rootfhset; 55292915Sdimextern int nfsrv_enable_crossmntpt; 56292915Sdim#endif /* !APPLEKEXT */ 57292915Sdim 58292915Sdimstatic int nfs_async = 0; 59292915SdimSYSCTL_DECL(_vfs_nfsd); 60292915SdimSYSCTL_INT(_vfs_nfsd, OID_AUTO, async, CTLFLAG_RW, &nfs_async, 0, 61292915Sdim "Tell client that writes were synced even though they were not"); 62292915Sdim 63292915Sdim/* 64292915Sdim * This list defines the GSS mechanisms supported. 65292915Sdim * (Don't ask me how you get these strings from the RFC stuff like 66292915Sdim * iso(1), org(3)... but someone did it, so I don't need to know.) 67292915Sdim */ 68292915Sdimstatic struct nfsgss_mechlist nfsgss_mechlist[] = { 69292915Sdim { 9, "\052\206\110\206\367\022\001\002\002", 11 }, 70292915Sdim { 0, "", 0 }, 71292915Sdim}; 72292915Sdim 73292915Sdim/* local functions */ 74292915Sdimstatic void nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp, 75292915Sdim struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp, 76292915Sdim vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp, 77292915Sdim int *diraft_retp, nfsattrbit_t *attrbitp, 78292915Sdim NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp, 79292915Sdim int pathlen); 80292915Sdimstatic void nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp, 81292915Sdim struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp, 82292915Sdim vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp, 83292915Sdim int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp, 84292915Sdim NFSPROC_T *p, struct nfsexstuff *exp); 85292915Sdim 86292915Sdim/* 87292915Sdim * nfs access service (not a part of NFS V2) 88292915Sdim */ 89292915SdimAPPLESTATIC int 90292915Sdimnfsrvd_access(struct nfsrv_descript *nd, __unused int isdgram, 91292915Sdim vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp) 92292915Sdim{ 93292915Sdim u_int32_t *tl; 94292915Sdim int getret, error = 0; 95292915Sdim struct nfsvattr nva; 96292915Sdim u_int32_t testmode, nfsmode, supported = 0; 97292915Sdim accmode_t deletebit; 98292915Sdim 99292915Sdim if (nd->nd_repstat) { 100292915Sdim nfsrv_postopattr(nd, 1, &nva); 101292915Sdim goto out; 102292915Sdim } 103292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 104292915Sdim nfsmode = fxdr_unsigned(u_int32_t, *tl); 105292915Sdim if ((nd->nd_flag & ND_NFSV4) && 106292915Sdim (nfsmode & ~(NFSACCESS_READ | NFSACCESS_LOOKUP | 107292915Sdim NFSACCESS_MODIFY | NFSACCESS_EXTEND | NFSACCESS_DELETE | 108292915Sdim NFSACCESS_EXECUTE))) { 109292915Sdim nd->nd_repstat = NFSERR_INVAL; 110292915Sdim vput(vp); 111292915Sdim goto out; 112292915Sdim } 113292915Sdim if (nfsmode & NFSACCESS_READ) { 114292915Sdim supported |= NFSACCESS_READ; 115292915Sdim if (nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, p, 116292915Sdim NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported)) 117292915Sdim nfsmode &= ~NFSACCESS_READ; 118292915Sdim } 119292915Sdim if (nfsmode & NFSACCESS_MODIFY) { 120292915Sdim supported |= NFSACCESS_MODIFY; 121292915Sdim if (nfsvno_accchk(vp, VWRITE, nd->nd_cred, exp, p, 122292915Sdim NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported)) 123292915Sdim nfsmode &= ~NFSACCESS_MODIFY; 124292915Sdim } 125292915Sdim if (nfsmode & NFSACCESS_EXTEND) { 126292915Sdim supported |= NFSACCESS_EXTEND; 127292915Sdim if (nfsvno_accchk(vp, VWRITE | VAPPEND, nd->nd_cred, exp, p, 128292915Sdim NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported)) 129292915Sdim nfsmode &= ~NFSACCESS_EXTEND; 130292915Sdim } 131292915Sdim if (nfsmode & NFSACCESS_DELETE) { 132292915Sdim supported |= NFSACCESS_DELETE; 133292915Sdim if (vp->v_type == VDIR) 134292915Sdim deletebit = VDELETE_CHILD; 135292915Sdim else 136292915Sdim deletebit = VDELETE; 137292915Sdim if (nfsvno_accchk(vp, deletebit, nd->nd_cred, exp, p, 138292915Sdim NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported)) 139292915Sdim nfsmode &= ~NFSACCESS_DELETE; 140292915Sdim } 141292915Sdim if (vnode_vtype(vp) == VDIR) 142292915Sdim testmode = NFSACCESS_LOOKUP; 143292915Sdim else 144292915Sdim testmode = NFSACCESS_EXECUTE; 145292915Sdim if (nfsmode & testmode) { 146292915Sdim supported |= (nfsmode & testmode); 147292915Sdim if (nfsvno_accchk(vp, VEXEC, nd->nd_cred, exp, p, 148292915Sdim NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED, &supported)) 149292915Sdim nfsmode &= ~testmode; 150292915Sdim } 151292915Sdim nfsmode &= supported; 152292915Sdim if (nd->nd_flag & ND_NFSV3) { 153292915Sdim getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1); 154292915Sdim nfsrv_postopattr(nd, getret, &nva); 155292915Sdim } 156292915Sdim vput(vp); 157292915Sdim if (nd->nd_flag & ND_NFSV4) { 158292915Sdim NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED); 159292915Sdim *tl++ = txdr_unsigned(supported); 160292915Sdim } else 161292915Sdim NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); 162292915Sdim *tl = txdr_unsigned(nfsmode); 163292915Sdim 164292915Sdimout: 165292915Sdim NFSEXITCODE2(0, nd); 166292915Sdim return (0); 167292915Sdimnfsmout: 168292915Sdim vput(vp); 169292915Sdim NFSEXITCODE2(error, nd); 170292915Sdim return (error); 171292915Sdim} 172292915Sdim 173292915Sdim/* 174292915Sdim * nfs getattr service 175292915Sdim */ 176292915SdimAPPLESTATIC int 177292915Sdimnfsrvd_getattr(struct nfsrv_descript *nd, int isdgram, 178292915Sdim vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) 179292915Sdim{ 180292915Sdim struct nfsvattr nva; 181292915Sdim fhandle_t fh; 182292915Sdim int at_root = 0, error = 0, supports_nfsv4acls; 183292915Sdim struct nfsreferral *refp; 184292915Sdim nfsattrbit_t attrbits, tmpbits; 185292915Sdim struct mount *mp; 186292915Sdim struct vnode *tvp = NULL; 187292915Sdim struct vattr va; 188292915Sdim uint64_t mounted_on_fileno = 0; 189292915Sdim accmode_t accmode; 190292915Sdim 191292915Sdim if (nd->nd_repstat) 192292915Sdim goto out; 193292915Sdim if (nd->nd_flag & ND_NFSV4) { 194292915Sdim error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL); 195292915Sdim if (error) { 196292915Sdim vput(vp); 197292915Sdim goto out; 198292915Sdim } 199292915Sdim 200292915Sdim /* 201292915Sdim * Check for a referral. 202292915Sdim */ 203292915Sdim refp = nfsv4root_getreferral(vp, NULL, 0); 204292915Sdim if (refp != NULL) { 205292915Sdim (void) nfsrv_putreferralattr(nd, &attrbits, refp, 1, 206292915Sdim &nd->nd_repstat); 207292915Sdim vput(vp); 208292915Sdim goto out; 209292915Sdim } 210292915Sdim if (nd->nd_repstat == 0) { 211292915Sdim accmode = 0; 212292915Sdim NFSSET_ATTRBIT(&tmpbits, &attrbits); 213292915Sdim if (NFSISSET_ATTRBIT(&tmpbits, NFSATTRBIT_ACL)) { 214292915Sdim NFSCLRBIT_ATTRBIT(&tmpbits, NFSATTRBIT_ACL); 215292915Sdim accmode |= VREAD_ACL; 216292915Sdim } 217292915Sdim if (NFSNONZERO_ATTRBIT(&tmpbits)) 218292915Sdim accmode |= VREAD_ATTRIBUTES; 219292915Sdim if (accmode != 0) 220292915Sdim nd->nd_repstat = nfsvno_accchk(vp, accmode, 221292915Sdim nd->nd_cred, exp, p, NFSACCCHK_NOOVERRIDE, 222292915Sdim NFSACCCHK_VPISLOCKED, NULL); 223292915Sdim } 224292915Sdim } 225292915Sdim if (!nd->nd_repstat) 226292915Sdim nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1); 227292915Sdim if (!nd->nd_repstat) { 228292915Sdim if (nd->nd_flag & ND_NFSV4) { 229292915Sdim if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_FILEHANDLE)) 230292915Sdim nd->nd_repstat = nfsvno_getfh(vp, &fh, p); 231292915Sdim if (!nd->nd_repstat) 232292915Sdim nd->nd_repstat = nfsrv_checkgetattr(nd, vp, 233292915Sdim &nva, &attrbits, nd->nd_cred, p); 234292915Sdim if (nd->nd_repstat == 0) { 235292915Sdim supports_nfsv4acls = nfs_supportsnfsv4acls(vp); 236292915Sdim mp = vp->v_mount; 237292915Sdim if (nfsrv_enable_crossmntpt != 0 && 238292915Sdim vp->v_type == VDIR && 239292915Sdim (vp->v_vflag & VV_ROOT) != 0 && 240292915Sdim vp != rootvnode) { 241292915Sdim tvp = mp->mnt_vnodecovered; 242292915Sdim VREF(tvp); 243292915Sdim at_root = 1; 244292915Sdim } else 245292915Sdim at_root = 0; 246292915Sdim vfs_ref(mp); 247292915Sdim NFSVOPUNLOCK(vp, 0); 248292915Sdim if (at_root != 0) { 249292915Sdim if ((nd->nd_repstat = 250292915Sdim NFSVOPLOCK(tvp, LK_SHARED)) == 0) { 251292915Sdim nd->nd_repstat = VOP_GETATTR( 252292915Sdim tvp, &va, nd->nd_cred); 253292915Sdim vput(tvp); 254292915Sdim } else 255292915Sdim vrele(tvp); 256292915Sdim if (nd->nd_repstat == 0) 257292915Sdim mounted_on_fileno = (uint64_t) 258292915Sdim va.va_fileid; 259292915Sdim else 260292915Sdim at_root = 0; 261292915Sdim } 262292915Sdim if (nd->nd_repstat == 0) 263292915Sdim nd->nd_repstat = vfs_busy(mp, 0); 264292915Sdim vfs_rel(mp); 265292915Sdim if (nd->nd_repstat == 0) { 266292915Sdim (void)nfsvno_fillattr(nd, mp, vp, &nva, 267292915Sdim &fh, 0, &attrbits, nd->nd_cred, p, 268292915Sdim isdgram, 1, supports_nfsv4acls, 269292915Sdim at_root, mounted_on_fileno); 270292915Sdim vfs_unbusy(mp); 271292915Sdim } 272292915Sdim vrele(vp); 273292915Sdim } else 274292915Sdim vput(vp); 275292915Sdim } else { 276292915Sdim nfsrv_fillattr(nd, &nva); 277292915Sdim vput(vp); 278292915Sdim } 279292915Sdim } else { 280292915Sdim vput(vp); 281292915Sdim } 282292915Sdim 283292915Sdimout: 284292915Sdim NFSEXITCODE2(error, nd); 285292915Sdim return (error); 286292915Sdim} 287292915Sdim 288292915Sdim/* 289292915Sdim * nfs setattr service 290292915Sdim */ 291292915SdimAPPLESTATIC int 292292915Sdimnfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram, 293292915Sdim vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp) 294292915Sdim{ 295292915Sdim struct nfsvattr nva, nva2; 296292915Sdim u_int32_t *tl; 297292915Sdim int preat_ret = 1, postat_ret = 1, gcheck = 0, error = 0; 298292915Sdim struct timespec guard = { 0, 0 }; 299292915Sdim nfsattrbit_t attrbits, retbits; 300292915Sdim nfsv4stateid_t stateid; 301292915Sdim NFSACL_T *aclp = NULL; 302292915Sdim 303292915Sdim if (nd->nd_repstat) { 304292915Sdim nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva); 305292915Sdim goto out; 306292915Sdim } 307292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME 308292915Sdim aclp = acl_alloc(M_WAITOK); 309292915Sdim aclp->acl_cnt = 0; 310292915Sdim#endif 311292915Sdim NFSVNO_ATTRINIT(&nva); 312292915Sdim NFSZERO_ATTRBIT(&retbits); 313292915Sdim if (nd->nd_flag & ND_NFSV4) { 314292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID); 315292915Sdim stateid.seqid = fxdr_unsigned(u_int32_t, *tl++); 316292915Sdim NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER); 317292915Sdim } 318292915Sdim error = nfsrv_sattr(nd, &nva, &attrbits, aclp, p); 319292915Sdim if (error) 320292915Sdim goto nfsmout; 321292915Sdim preat_ret = nfsvno_getattr(vp, &nva2, nd->nd_cred, p, 1); 322292915Sdim if (!nd->nd_repstat) 323292915Sdim nd->nd_repstat = preat_ret; 324292915Sdim if (nd->nd_flag & ND_NFSV3) { 325292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 326292915Sdim gcheck = fxdr_unsigned(int, *tl); 327292915Sdim if (gcheck) { 328292915Sdim NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED); 329292915Sdim fxdr_nfsv3time(tl, &guard); 330292915Sdim } 331292915Sdim if (!nd->nd_repstat && gcheck && 332292915Sdim (nva2.na_ctime.tv_sec != guard.tv_sec || 333292915Sdim nva2.na_ctime.tv_nsec != guard.tv_nsec)) 334292915Sdim nd->nd_repstat = NFSERR_NOT_SYNC; 335292915Sdim if (nd->nd_repstat) { 336292915Sdim vput(vp); 337292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME 338292915Sdim acl_free(aclp); 339292915Sdim#endif 340292915Sdim nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva); 341292915Sdim goto out; 342292915Sdim } 343292915Sdim } else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) 344292915Sdim nd->nd_repstat = nfsrv_checkuidgid(nd, &nva); 345292915Sdim 346292915Sdim /* 347292915Sdim * Now that we have all the fields, lets do it. 348292915Sdim * If the size is being changed write access is required, otherwise 349292915Sdim * just check for a read only file system. 350292915Sdim */ 351292915Sdim if (!nd->nd_repstat) { 352292915Sdim if (NFSVNO_NOTSETSIZE(&nva)) { 353292915Sdim if (NFSVNO_EXRDONLY(exp) || 354292915Sdim (vfs_flags(vnode_mount(vp)) & MNT_RDONLY)) 355292915Sdim nd->nd_repstat = EROFS; 356292915Sdim } else { 357292915Sdim if (vnode_vtype(vp) != VREG) 358292915Sdim nd->nd_repstat = EINVAL; 359292915Sdim else if (nva2.na_uid != nd->nd_cred->cr_uid || 360292915Sdim NFSVNO_EXSTRICTACCESS(exp)) 361292915Sdim nd->nd_repstat = nfsvno_accchk(vp, 362292915Sdim VWRITE, nd->nd_cred, exp, p, 363292915Sdim NFSACCCHK_NOOVERRIDE, 364292915Sdim NFSACCCHK_VPISLOCKED, NULL); 365292915Sdim } 366292915Sdim } 367292915Sdim if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) 368292915Sdim nd->nd_repstat = nfsrv_checksetattr(vp, nd, &stateid, 369292915Sdim &nva, &attrbits, exp, p); 370292915Sdim 371292915Sdim if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) { 372292915Sdim /* 373292915Sdim * For V4, try setting the attrbutes in sets, so that the 374292915Sdim * reply bitmap will be correct for an error case. 375292915Sdim */ 376292915Sdim if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER) || 377292915Sdim NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP)) { 378292915Sdim NFSVNO_ATTRINIT(&nva2); 379292915Sdim NFSVNO_SETATTRVAL(&nva2, uid, nva.na_uid); 380292915Sdim NFSVNO_SETATTRVAL(&nva2, gid, nva.na_gid); 381292915Sdim nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p, 382292915Sdim exp); 383292915Sdim if (!nd->nd_repstat) { 384292915Sdim if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER)) 385292915Sdim NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER); 386292915Sdim if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP)) 387292915Sdim NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNERGROUP); 388292915Sdim } 389292915Sdim } 390292915Sdim if (!nd->nd_repstat && 391292915Sdim NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SIZE)) { 392292915Sdim NFSVNO_ATTRINIT(&nva2); 393292915Sdim NFSVNO_SETATTRVAL(&nva2, size, nva.na_size); 394292915Sdim nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p, 395292915Sdim exp); 396292915Sdim if (!nd->nd_repstat) 397292915Sdim NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SIZE); 398292915Sdim } 399292915Sdim if (!nd->nd_repstat && 400292915Sdim (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET) || 401292915Sdim NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))) { 402292915Sdim NFSVNO_ATTRINIT(&nva2); 403292915Sdim NFSVNO_SETATTRVAL(&nva2, atime, nva.na_atime); 404292915Sdim NFSVNO_SETATTRVAL(&nva2, mtime, nva.na_mtime); 405292915Sdim if (nva.na_vaflags & VA_UTIMES_NULL) { 406292915Sdim nva2.na_vaflags |= VA_UTIMES_NULL; 407292915Sdim NFSVNO_SETACTIVE(&nva2, vaflags); 408292915Sdim } 409292915Sdim nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p, 410292915Sdim exp); 411292915Sdim if (!nd->nd_repstat) { 412292915Sdim if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET)) 413292915Sdim NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEACCESSSET); 414292915Sdim if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET)) 415292915Sdim NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEMODIFYSET); 416292915Sdim } 417292915Sdim } 418292915Sdim if (!nd->nd_repstat && 419292915Sdim NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE)) { 420292915Sdim NFSVNO_ATTRINIT(&nva2); 421292915Sdim NFSVNO_SETATTRVAL(&nva2, mode, nva.na_mode); 422292915Sdim nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p, 423292915Sdim exp); 424292915Sdim if (!nd->nd_repstat) 425292915Sdim NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODE); 426292915Sdim } 427292915Sdim 428292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME 429292915Sdim if (!nd->nd_repstat && aclp->acl_cnt > 0 && 430292915Sdim NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ACL)) { 431292915Sdim nd->nd_repstat = nfsrv_setacl(vp, aclp, nd->nd_cred, p); 432292915Sdim if (!nd->nd_repstat) 433292915Sdim NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_ACL); 434292915Sdim } 435292915Sdim#endif 436292915Sdim } else if (!nd->nd_repstat) { 437292915Sdim nd->nd_repstat = nfsvno_setattr(vp, &nva, nd->nd_cred, p, 438292915Sdim exp); 439292915Sdim } 440292915Sdim if (nd->nd_flag & (ND_NFSV2 | ND_NFSV3)) { 441292915Sdim postat_ret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1); 442292915Sdim if (!nd->nd_repstat) 443292915Sdim nd->nd_repstat = postat_ret; 444292915Sdim } 445292915Sdim vput(vp); 446292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME 447292915Sdim acl_free(aclp); 448292915Sdim#endif 449292915Sdim if (nd->nd_flag & ND_NFSV3) 450292915Sdim nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva); 451292915Sdim else if (nd->nd_flag & ND_NFSV4) 452292915Sdim (void) nfsrv_putattrbit(nd, &retbits); 453292915Sdim else if (!nd->nd_repstat) 454292915Sdim nfsrv_fillattr(nd, &nva); 455292915Sdim 456292915Sdimout: 457292915Sdim NFSEXITCODE2(0, nd); 458292915Sdim return (0); 459292915Sdimnfsmout: 460292915Sdim vput(vp); 461292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME 462292915Sdim acl_free(aclp); 463292915Sdim#endif 464292915Sdim if (nd->nd_flag & ND_NFSV4) { 465292915Sdim /* 466292915Sdim * For all nd_repstat, the V4 reply includes a bitmap, 467292915Sdim * even NFSERR_BADXDR, which is what this will end up 468292915Sdim * returning. 469292915Sdim */ 470292915Sdim (void) nfsrv_putattrbit(nd, &retbits); 471292915Sdim } 472292915Sdim NFSEXITCODE2(error, nd); 473292915Sdim return (error); 474292915Sdim} 475292915Sdim 476292915Sdim/* 477292915Sdim * nfs lookup rpc 478292915Sdim * (Also performs lookup parent for v4) 479292915Sdim */ 480292915SdimAPPLESTATIC int 481292915Sdimnfsrvd_lookup(struct nfsrv_descript *nd, __unused int isdgram, 482292915Sdim vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p, 483292915Sdim struct nfsexstuff *exp) 484292915Sdim{ 485292915Sdim struct nameidata named; 486292915Sdim vnode_t vp, dirp = NULL; 487292915Sdim int error = 0, dattr_ret = 1; 488292915Sdim struct nfsvattr nva, dattr; 489292915Sdim char *bufp; 490292915Sdim u_long *hashp; 491292915Sdim 492292915Sdim if (nd->nd_repstat) { 493292915Sdim nfsrv_postopattr(nd, dattr_ret, &dattr); 494292915Sdim goto out; 495292915Sdim } 496292915Sdim 497292915Sdim /* 498292915Sdim * For some reason, if dp is a symlink, the error 499292915Sdim * returned is supposed to be NFSERR_SYMLINK and not NFSERR_NOTDIR. 500292915Sdim */ 501292915Sdim if (dp->v_type == VLNK && (nd->nd_flag & ND_NFSV4)) { 502292915Sdim nd->nd_repstat = NFSERR_SYMLINK; 503292915Sdim vrele(dp); 504292915Sdim goto out; 505292915Sdim } 506292915Sdim 507292915Sdim NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP, 508292915Sdim LOCKLEAF | SAVESTART); 509292915Sdim nfsvno_setpathbuf(&named, &bufp, &hashp); 510292915Sdim error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); 511292915Sdim if (error) { 512292915Sdim vrele(dp); 513292915Sdim nfsvno_relpathbuf(&named); 514292915Sdim goto out; 515292915Sdim } 516292915Sdim if (!nd->nd_repstat) { 517292915Sdim nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp); 518292915Sdim } else { 519292915Sdim vrele(dp); 520292915Sdim nfsvno_relpathbuf(&named); 521292915Sdim } 522292915Sdim if (nd->nd_repstat) { 523292915Sdim if (dirp) { 524292915Sdim if (nd->nd_flag & ND_NFSV3) 525292915Sdim dattr_ret = nfsvno_getattr(dirp, &dattr, 526292915Sdim nd->nd_cred, p, 0); 527292915Sdim vrele(dirp); 528292915Sdim } 529292915Sdim if (nd->nd_flag & ND_NFSV3) 530292915Sdim nfsrv_postopattr(nd, dattr_ret, &dattr); 531292915Sdim goto out; 532292915Sdim } 533292915Sdim if (named.ni_startdir) 534292915Sdim vrele(named.ni_startdir); 535292915Sdim nfsvno_relpathbuf(&named); 536292915Sdim vp = named.ni_vp; 537292915Sdim if ((nd->nd_flag & ND_NFSV4) != 0 && !NFSVNO_EXPORTED(exp) && 538292915Sdim vp->v_type != VDIR && vp->v_type != VLNK) 539292915Sdim /* 540292915Sdim * Only allow lookup of VDIR and VLNK for traversal of 541292915Sdim * non-exported volumes during NFSv4 mounting. 542292915Sdim */ 543292915Sdim nd->nd_repstat = ENOENT; 544292915Sdim if (nd->nd_repstat == 0) 545292915Sdim nd->nd_repstat = nfsvno_getfh(vp, fhp, p); 546292915Sdim if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) 547292915Sdim nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1); 548292915Sdim if (vpp != NULL && nd->nd_repstat == 0) 549292915Sdim *vpp = vp; 550292915Sdim else 551292915Sdim vput(vp); 552292915Sdim if (dirp) { 553292915Sdim if (nd->nd_flag & ND_NFSV3) 554292915Sdim dattr_ret = nfsvno_getattr(dirp, &dattr, nd->nd_cred, 555292915Sdim p, 0); 556292915Sdim vrele(dirp); 557292915Sdim } 558292915Sdim if (nd->nd_repstat) { 559292915Sdim if (nd->nd_flag & ND_NFSV3) 560292915Sdim nfsrv_postopattr(nd, dattr_ret, &dattr); 561292915Sdim goto out; 562292915Sdim } 563292915Sdim if (nd->nd_flag & ND_NFSV2) { 564292915Sdim (void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0); 565292915Sdim nfsrv_fillattr(nd, &nva); 566292915Sdim } else if (nd->nd_flag & ND_NFSV3) { 567292915Sdim (void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0); 568292915Sdim nfsrv_postopattr(nd, 0, &nva); 569292915Sdim nfsrv_postopattr(nd, dattr_ret, &dattr); 570292915Sdim } 571292915Sdim 572292915Sdimout: 573292915Sdim NFSEXITCODE2(error, nd); 574292915Sdim return (error); 575292915Sdim} 576292915Sdim 577292915Sdim/* 578292915Sdim * nfs readlink service 579292915Sdim */ 580292915SdimAPPLESTATIC int 581292915Sdimnfsrvd_readlink(struct nfsrv_descript *nd, __unused int isdgram, 582292915Sdim vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) 583292915Sdim{ 584292915Sdim u_int32_t *tl; 585292915Sdim mbuf_t mp = NULL, mpend = NULL; 586292915Sdim int getret = 1, len; 587292915Sdim struct nfsvattr nva; 588292915Sdim 589292915Sdim if (nd->nd_repstat) { 590292915Sdim nfsrv_postopattr(nd, getret, &nva); 591292915Sdim goto out; 592292915Sdim } 593292915Sdim if (vnode_vtype(vp) != VLNK) { 594292915Sdim if (nd->nd_flag & ND_NFSV2) 595292915Sdim nd->nd_repstat = ENXIO; 596292915Sdim else 597292915Sdim nd->nd_repstat = EINVAL; 598292915Sdim } 599292915Sdim if (!nd->nd_repstat) 600292915Sdim nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred, p, 601292915Sdim &mp, &mpend, &len); 602292915Sdim if (nd->nd_flag & ND_NFSV3) 603292915Sdim getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1); 604292915Sdim vput(vp); 605292915Sdim if (nd->nd_flag & ND_NFSV3) 606292915Sdim nfsrv_postopattr(nd, getret, &nva); 607292915Sdim if (nd->nd_repstat) 608292915Sdim goto out; 609292915Sdim NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); 610292915Sdim *tl = txdr_unsigned(len); 611292915Sdim mbuf_setnext(nd->nd_mb, mp); 612292915Sdim nd->nd_mb = mpend; 613292915Sdim nd->nd_bpos = NFSMTOD(mpend, caddr_t) + mbuf_len(mpend); 614292915Sdim 615292915Sdimout: 616292915Sdim NFSEXITCODE2(0, nd); 617292915Sdim return (0); 618292915Sdim} 619292915Sdim 620292915Sdim/* 621292915Sdim * nfs read service 622292915Sdim */ 623292915SdimAPPLESTATIC int 624292915Sdimnfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram, 625292915Sdim vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp) 626292915Sdim{ 627292915Sdim u_int32_t *tl; 628292915Sdim int error = 0, cnt, getret = 1, reqlen, eof = 0; 629292915Sdim mbuf_t m2, m3; 630292915Sdim struct nfsvattr nva; 631292915Sdim off_t off = 0x0; 632292915Sdim struct nfsstate st, *stp = &st; 633292915Sdim struct nfslock lo, *lop = &lo; 634292915Sdim nfsv4stateid_t stateid; 635292915Sdim nfsquad_t clientid; 636292915Sdim 637292915Sdim if (nd->nd_repstat) { 638292915Sdim nfsrv_postopattr(nd, getret, &nva); 639292915Sdim goto out; 640292915Sdim } 641292915Sdim if (nd->nd_flag & ND_NFSV2) { 642292915Sdim NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED); 643292915Sdim off = (off_t)fxdr_unsigned(u_int32_t, *tl++); 644292915Sdim reqlen = fxdr_unsigned(int, *tl); 645292915Sdim } else if (nd->nd_flag & ND_NFSV3) { 646292915Sdim NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED); 647292915Sdim off = fxdr_hyper(tl); 648292915Sdim tl += 2; 649292915Sdim reqlen = fxdr_unsigned(int, *tl); 650292915Sdim } else { 651292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3*NFSX_UNSIGNED); 652292915Sdim reqlen = fxdr_unsigned(int, *(tl + 6)); 653292915Sdim } 654292915Sdim if (reqlen > NFS_SRVMAXDATA(nd)) { 655292915Sdim reqlen = NFS_SRVMAXDATA(nd); 656292915Sdim } else if (reqlen < 0) { 657292915Sdim error = EBADRPC; 658292915Sdim goto nfsmout; 659292915Sdim } 660292915Sdim if (nd->nd_flag & ND_NFSV4) { 661292915Sdim stp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS); 662292915Sdim lop->lo_flags = NFSLCK_READ; 663292915Sdim stp->ls_ownerlen = 0; 664292915Sdim stp->ls_op = NULL; 665292915Sdim stp->ls_uid = nd->nd_cred->cr_uid; 666292915Sdim stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++); 667292915Sdim clientid.lval[0] = stp->ls_stateid.other[0] = *tl++; 668292915Sdim clientid.lval[1] = stp->ls_stateid.other[1] = *tl++; 669292915Sdim if (nd->nd_flag & ND_IMPLIEDCLID) { 670292915Sdim if (nd->nd_clientid.qval != clientid.qval) 671292915Sdim printf("EEK! multiple clids\n"); 672292915Sdim } else { 673292915Sdim nd->nd_flag |= ND_IMPLIEDCLID; 674292915Sdim nd->nd_clientid.qval = clientid.qval; 675292915Sdim } 676292915Sdim stp->ls_stateid.other[2] = *tl++; 677292915Sdim off = fxdr_hyper(tl); 678292915Sdim lop->lo_first = off; 679292915Sdim tl += 2; 680292915Sdim lop->lo_end = off + reqlen; 681292915Sdim /* 682292915Sdim * Paranoia, just in case it wraps around. 683292915Sdim */ 684292915Sdim if (lop->lo_end < off) 685292915Sdim lop->lo_end = NFS64BITSSET; 686292915Sdim } 687292915Sdim if (vnode_vtype(vp) != VREG) { 688292915Sdim if (nd->nd_flag & ND_NFSV3) 689292915Sdim nd->nd_repstat = EINVAL; 690292915Sdim else 691292915Sdim nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR : 692292915Sdim EINVAL; 693292915Sdim } 694292915Sdim getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1); 695292915Sdim if (!nd->nd_repstat) 696292915Sdim nd->nd_repstat = getret; 697292915Sdim if (!nd->nd_repstat && 698292915Sdim (nva.na_uid != nd->nd_cred->cr_uid || 699292915Sdim NFSVNO_EXSTRICTACCESS(exp))) { 700292915Sdim nd->nd_repstat = nfsvno_accchk(vp, VREAD, 701292915Sdim nd->nd_cred, exp, p, 702292915Sdim NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL); 703292915Sdim if (nd->nd_repstat) 704292915Sdim nd->nd_repstat = nfsvno_accchk(vp, VEXEC, 705292915Sdim nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER, 706292915Sdim NFSACCCHK_VPISLOCKED, NULL); 707292915Sdim } 708292915Sdim if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) 709292915Sdim nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid, 710292915Sdim &stateid, exp, nd, p); 711292915Sdim if (nd->nd_repstat) { 712292915Sdim vput(vp); 713292915Sdim if (nd->nd_flag & ND_NFSV3) 714292915Sdim nfsrv_postopattr(nd, getret, &nva); 715292915Sdim goto out; 716292915Sdim } 717292915Sdim if (off >= nva.na_size) { 718292915Sdim cnt = 0; 719292915Sdim eof = 1; 720292915Sdim } else if (reqlen == 0) 721292915Sdim cnt = 0; 722292915Sdim else if ((off + reqlen) >= nva.na_size) { 723292915Sdim cnt = nva.na_size - off; 724292915Sdim eof = 1; 725292915Sdim } else 726292915Sdim cnt = reqlen; 727292915Sdim m3 = NULL; 728292915Sdim if (cnt > 0) { 729292915Sdim nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred, p, 730292915Sdim &m3, &m2); 731292915Sdim if (!(nd->nd_flag & ND_NFSV4)) { 732292915Sdim getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1); 733292915Sdim if (!nd->nd_repstat) 734292915Sdim nd->nd_repstat = getret; 735292915Sdim } 736292915Sdim if (nd->nd_repstat) { 737292915Sdim vput(vp); 738292915Sdim if (m3) 739292915Sdim mbuf_freem(m3); 740292915Sdim if (nd->nd_flag & ND_NFSV3) 741292915Sdim nfsrv_postopattr(nd, getret, &nva); 742292915Sdim goto out; 743292915Sdim } 744292915Sdim } 745292915Sdim vput(vp); 746292915Sdim if (nd->nd_flag & ND_NFSV2) { 747292915Sdim nfsrv_fillattr(nd, &nva); 748292915Sdim NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); 749292915Sdim } else { 750292915Sdim if (nd->nd_flag & ND_NFSV3) { 751292915Sdim nfsrv_postopattr(nd, getret, &nva); 752292915Sdim NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED); 753292915Sdim *tl++ = txdr_unsigned(cnt); 754292915Sdim } else 755292915Sdim NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED); 756292915Sdim if (eof) 757292915Sdim *tl++ = newnfs_true; 758292915Sdim else 759292915Sdim *tl++ = newnfs_false; 760292915Sdim } 761292915Sdim *tl = txdr_unsigned(cnt); 762292915Sdim if (m3) { 763292915Sdim mbuf_setnext(nd->nd_mb, m3); 764292915Sdim nd->nd_mb = m2; 765292915Sdim nd->nd_bpos = NFSMTOD(m2, caddr_t) + mbuf_len(m2); 766292915Sdim } 767292915Sdim 768292915Sdimout: 769292915Sdim NFSEXITCODE2(0, nd); 770292915Sdim return (0); 771292915Sdimnfsmout: 772292915Sdim vput(vp); 773292915Sdim NFSEXITCODE2(error, nd); 774292915Sdim return (error); 775292915Sdim} 776292915Sdim 777292915Sdim/* 778292915Sdim * nfs write service 779292915Sdim */ 780292915SdimAPPLESTATIC int 781292915Sdimnfsrvd_write(struct nfsrv_descript *nd, __unused int isdgram, 782292915Sdim vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp) 783292915Sdim{ 784292915Sdim int i, cnt; 785292915Sdim u_int32_t *tl; 786292915Sdim mbuf_t mp; 787292915Sdim struct nfsvattr nva, forat; 788292915Sdim int aftat_ret = 1, retlen, len, error = 0, forat_ret = 1; 789292915Sdim int stable = NFSWRITE_FILESYNC; 790292915Sdim off_t off; 791292915Sdim struct nfsstate st, *stp = &st; 792292915Sdim struct nfslock lo, *lop = &lo; 793292915Sdim nfsv4stateid_t stateid; 794292915Sdim nfsquad_t clientid; 795292915Sdim 796292915Sdim if (nd->nd_repstat) { 797292915Sdim nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva); 798292915Sdim goto out; 799292915Sdim } 800292915Sdim if (nd->nd_flag & ND_NFSV2) { 801292915Sdim NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED); 802292915Sdim off = (off_t)fxdr_unsigned(u_int32_t, *++tl); 803292915Sdim tl += 2; 804292915Sdim retlen = len = fxdr_unsigned(int32_t, *tl); 805292915Sdim } else if (nd->nd_flag & ND_NFSV3) { 806292915Sdim NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED); 807292915Sdim off = fxdr_hyper(tl); 808292915Sdim tl += 3; 809292915Sdim stable = fxdr_unsigned(int, *tl++); 810292915Sdim retlen = len = fxdr_unsigned(int32_t, *tl); 811292915Sdim } else { 812292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 4 * NFSX_UNSIGNED); 813292915Sdim stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS); 814292915Sdim lop->lo_flags = NFSLCK_WRITE; 815292915Sdim stp->ls_ownerlen = 0; 816292915Sdim stp->ls_op = NULL; 817292915Sdim stp->ls_uid = nd->nd_cred->cr_uid; 818292915Sdim stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++); 819292915Sdim clientid.lval[0] = stp->ls_stateid.other[0] = *tl++; 820292915Sdim clientid.lval[1] = stp->ls_stateid.other[1] = *tl++; 821292915Sdim if (nd->nd_flag & ND_IMPLIEDCLID) { 822292915Sdim if (nd->nd_clientid.qval != clientid.qval) 823292915Sdim printf("EEK! multiple clids\n"); 824292915Sdim } else { 825292915Sdim nd->nd_flag |= ND_IMPLIEDCLID; 826292915Sdim nd->nd_clientid.qval = clientid.qval; 827292915Sdim } 828292915Sdim stp->ls_stateid.other[2] = *tl++; 829292915Sdim off = fxdr_hyper(tl); 830292915Sdim lop->lo_first = off; 831292915Sdim tl += 2; 832292915Sdim stable = fxdr_unsigned(int, *tl++); 833292915Sdim retlen = len = fxdr_unsigned(int32_t, *tl); 834292915Sdim lop->lo_end = off + len; 835292915Sdim /* 836292915Sdim * Paranoia, just in case it wraps around, which shouldn't 837292915Sdim * ever happen anyhow. 838292915Sdim */ 839292915Sdim if (lop->lo_end < lop->lo_first) 840292915Sdim lop->lo_end = NFS64BITSSET; 841292915Sdim } 842292915Sdim 843292915Sdim /* 844292915Sdim * Loop through the mbuf chain, counting how many mbufs are a 845292915Sdim * part of this write operation, so the iovec size is known. 846292915Sdim */ 847292915Sdim cnt = 0; 848292915Sdim mp = nd->nd_md; 849292915Sdim i = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - nd->nd_dpos; 850292915Sdim while (len > 0) { 851292915Sdim if (i > 0) { 852292915Sdim len -= i; 853292915Sdim cnt++; 854292915Sdim } 855292915Sdim mp = mbuf_next(mp); 856292915Sdim if (!mp) { 857292915Sdim if (len > 0) { 858292915Sdim error = EBADRPC; 859292915Sdim goto nfsmout; 860292915Sdim } 861292915Sdim } else 862292915Sdim i = mbuf_len(mp); 863292915Sdim } 864292915Sdim 865292915Sdim if (retlen > NFS_MAXDATA || retlen < 0) 866292915Sdim nd->nd_repstat = EIO; 867292915Sdim if (vnode_vtype(vp) != VREG && !nd->nd_repstat) { 868292915Sdim if (nd->nd_flag & ND_NFSV3) 869292915Sdim nd->nd_repstat = EINVAL; 870292915Sdim else 871292915Sdim nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR : 872292915Sdim EINVAL; 873292915Sdim } 874292915Sdim forat_ret = nfsvno_getattr(vp, &forat, nd->nd_cred, p, 1); 875292915Sdim if (!nd->nd_repstat) 876292915Sdim nd->nd_repstat = forat_ret; 877292915Sdim if (!nd->nd_repstat && 878292915Sdim (forat.na_uid != nd->nd_cred->cr_uid || 879292915Sdim NFSVNO_EXSTRICTACCESS(exp))) 880292915Sdim nd->nd_repstat = nfsvno_accchk(vp, VWRITE, 881292915Sdim nd->nd_cred, exp, p, 882292915Sdim NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL); 883292915Sdim if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) { 884292915Sdim nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid, 885292915Sdim &stateid, exp, nd, p); 886292915Sdim } 887292915Sdim if (nd->nd_repstat) { 888292915Sdim vput(vp); 889292915Sdim if (nd->nd_flag & ND_NFSV3) 890292915Sdim nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva); 891292915Sdim goto out; 892292915Sdim } 893292915Sdim 894292915Sdim /* 895292915Sdim * For NFS Version 2, it is not obvious what a write of zero length 896292915Sdim * should do, but I might as well be consistent with Version 3, 897292915Sdim * which is to return ok so long as there are no permission problems. 898292915Sdim */ 899292915Sdim if (retlen > 0) { 900292915Sdim nd->nd_repstat = nfsvno_write(vp, off, retlen, cnt, stable, 901292915Sdim nd->nd_md, nd->nd_dpos, nd->nd_cred, p); 902292915Sdim error = nfsm_advance(nd, NFSM_RNDUP(retlen), -1); 903292915Sdim if (error) 904292915Sdim panic("nfsrv_write mbuf"); 905292915Sdim } 906292915Sdim if (nd->nd_flag & ND_NFSV4) 907292915Sdim aftat_ret = 0; 908292915Sdim else 909292915Sdim aftat_ret = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1); 910292915Sdim vput(vp); 911292915Sdim if (!nd->nd_repstat) 912292915Sdim nd->nd_repstat = aftat_ret; 913292915Sdim if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) { 914292915Sdim if (nd->nd_flag & ND_NFSV3) 915292915Sdim nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva); 916292915Sdim if (nd->nd_repstat) 917292915Sdim goto out; 918292915Sdim NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED); 919292915Sdim *tl++ = txdr_unsigned(retlen); 920292915Sdim /* 921292915Sdim * If nfs_async is set, then pretend the write was FILESYNC. 922292915Sdim * Warning: Doing this violates RFC1813 and runs a risk 923292915Sdim * of data written by a client being lost when the server 924292915Sdim * crashes/reboots. 925292915Sdim */ 926292915Sdim if (stable == NFSWRITE_UNSTABLE && nfs_async == 0) 927292915Sdim *tl++ = txdr_unsigned(stable); 928292915Sdim else 929292915Sdim *tl++ = txdr_unsigned(NFSWRITE_FILESYNC); 930292915Sdim /* 931292915Sdim * Actually, there is no need to txdr these fields, 932292915Sdim * but it may make the values more human readable, 933292915Sdim * for debugging purposes. 934292915Sdim */ 935292915Sdim *tl++ = txdr_unsigned(nfsboottime.tv_sec); 936292915Sdim *tl = txdr_unsigned(nfsboottime.tv_usec); 937292915Sdim } else if (!nd->nd_repstat) 938292915Sdim nfsrv_fillattr(nd, &nva); 939292915Sdim 940292915Sdimout: 941292915Sdim NFSEXITCODE2(0, nd); 942292915Sdim return (0); 943292915Sdimnfsmout: 944292915Sdim vput(vp); 945292915Sdim NFSEXITCODE2(error, nd); 946292915Sdim return (error); 947292915Sdim} 948292915Sdim 949292915Sdim/* 950292915Sdim * nfs create service (creates regular files for V2 and V3. Spec. files for V2.) 951292915Sdim * now does a truncate to 0 length via. setattr if it already exists 952292915Sdim * The core creation routine has been extracted out into nfsrv_creatsub(), 953292915Sdim * so it can also be used by nfsrv_open() for V4. 954292915Sdim */ 955292915SdimAPPLESTATIC int 956292915Sdimnfsrvd_create(struct nfsrv_descript *nd, __unused int isdgram, 957292915Sdim vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp) 958292915Sdim{ 959292915Sdim struct nfsvattr nva, dirfor, diraft; 960292915Sdim struct nfsv2_sattr *sp; 961292915Sdim struct nameidata named; 962292915Sdim u_int32_t *tl; 963292915Sdim int error = 0, tsize, dirfor_ret = 1, diraft_ret = 1; 964292915Sdim int how = NFSCREATE_UNCHECKED, exclusive_flag = 0; 965292915Sdim NFSDEV_T rdev = 0; 966292915Sdim vnode_t vp = NULL, dirp = NULL; 967292915Sdim fhandle_t fh; 968292915Sdim char *bufp; 969292915Sdim u_long *hashp; 970292915Sdim enum vtype vtyp; 971292915Sdim int32_t cverf[2], tverf[2] = { 0, 0 }; 972292915Sdim 973292915Sdim if (nd->nd_repstat) { 974292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft); 975292915Sdim goto out; 976292915Sdim } 977292915Sdim NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, 978292915Sdim LOCKPARENT | LOCKLEAF | SAVESTART); 979292915Sdim nfsvno_setpathbuf(&named, &bufp, &hashp); 980292915Sdim error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); 981292915Sdim if (error) 982292915Sdim goto nfsmout; 983292915Sdim if (!nd->nd_repstat) { 984292915Sdim NFSVNO_ATTRINIT(&nva); 985292915Sdim if (nd->nd_flag & ND_NFSV2) { 986292915Sdim NFSM_DISSECT(sp, struct nfsv2_sattr *, NFSX_V2SATTR); 987292915Sdim vtyp = IFTOVT(fxdr_unsigned(u_int32_t, sp->sa_mode)); 988292915Sdim if (vtyp == VNON) 989292915Sdim vtyp = VREG; 990292915Sdim NFSVNO_SETATTRVAL(&nva, type, vtyp); 991292915Sdim NFSVNO_SETATTRVAL(&nva, mode, 992292915Sdim nfstov_mode(sp->sa_mode)); 993292915Sdim switch (nva.na_type) { 994292915Sdim case VREG: 995292915Sdim tsize = fxdr_unsigned(int32_t, sp->sa_size); 996292915Sdim if (tsize != -1) 997292915Sdim NFSVNO_SETATTRVAL(&nva, size, 998292915Sdim (u_quad_t)tsize); 999292915Sdim break; 1000292915Sdim case VCHR: 1001292915Sdim case VBLK: 1002292915Sdim case VFIFO: 1003292915Sdim rdev = fxdr_unsigned(NFSDEV_T, sp->sa_size); 1004292915Sdim break; 1005292915Sdim default: 1006292915Sdim break; 1007292915Sdim }; 1008292915Sdim } else { 1009292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 1010292915Sdim how = fxdr_unsigned(int, *tl); 1011292915Sdim switch (how) { 1012292915Sdim case NFSCREATE_GUARDED: 1013292915Sdim case NFSCREATE_UNCHECKED: 1014292915Sdim error = nfsrv_sattr(nd, &nva, NULL, NULL, p); 1015292915Sdim if (error) 1016292915Sdim goto nfsmout; 1017292915Sdim break; 1018292915Sdim case NFSCREATE_EXCLUSIVE: 1019292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF); 1020292915Sdim cverf[0] = *tl++; 1021292915Sdim cverf[1] = *tl; 1022292915Sdim exclusive_flag = 1; 1023292915Sdim break; 1024292915Sdim }; 1025292915Sdim NFSVNO_SETATTRVAL(&nva, type, VREG); 1026292915Sdim } 1027292915Sdim } 1028292915Sdim if (nd->nd_repstat) { 1029292915Sdim nfsvno_relpathbuf(&named); 1030292915Sdim if (nd->nd_flag & ND_NFSV3) { 1031292915Sdim dirfor_ret = nfsvno_getattr(dp, &dirfor, nd->nd_cred, 1032292915Sdim p, 1); 1033292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, 1034292915Sdim &diraft); 1035292915Sdim } 1036292915Sdim vput(dp); 1037292915Sdim goto out; 1038292915Sdim } 1039292915Sdim 1040292915Sdim nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp); 1041292915Sdim if (dirp) { 1042292915Sdim if (nd->nd_flag & ND_NFSV2) { 1043292915Sdim vrele(dirp); 1044292915Sdim dirp = NULL; 1045292915Sdim } else { 1046292915Sdim dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, 1047292915Sdim p, 0); 1048292915Sdim } 1049292915Sdim } 1050292915Sdim if (nd->nd_repstat) { 1051292915Sdim if (nd->nd_flag & ND_NFSV3) 1052292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, 1053292915Sdim &diraft); 1054292915Sdim if (dirp) 1055292915Sdim vrele(dirp); 1056292915Sdim goto out; 1057292915Sdim } 1058292915Sdim 1059292915Sdim if (!(nd->nd_flag & ND_NFSV2)) { 1060292915Sdim switch (how) { 1061292915Sdim case NFSCREATE_GUARDED: 1062292915Sdim if (named.ni_vp) 1063292915Sdim nd->nd_repstat = EEXIST; 1064292915Sdim break; 1065292915Sdim case NFSCREATE_UNCHECKED: 1066292915Sdim break; 1067292915Sdim case NFSCREATE_EXCLUSIVE: 1068292915Sdim if (named.ni_vp == NULL) 1069292915Sdim NFSVNO_SETATTRVAL(&nva, mode, 0); 1070292915Sdim break; 1071292915Sdim }; 1072292915Sdim } 1073292915Sdim 1074292915Sdim /* 1075292915Sdim * Iff doesn't exist, create it 1076292915Sdim * otherwise just truncate to 0 length 1077292915Sdim * should I set the mode too ? 1078292915Sdim */ 1079292915Sdim nd->nd_repstat = nfsvno_createsub(nd, &named, &vp, &nva, 1080292915Sdim &exclusive_flag, cverf, rdev, p, exp); 1081292915Sdim 1082292915Sdim if (!nd->nd_repstat) { 1083292915Sdim nd->nd_repstat = nfsvno_getfh(vp, &fh, p); 1084292915Sdim if (!nd->nd_repstat) 1085292915Sdim nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, 1086292915Sdim p, 1); 1087292915Sdim vput(vp); 1088292915Sdim if (!nd->nd_repstat) { 1089292915Sdim tverf[0] = nva.na_atime.tv_sec; 1090292915Sdim tverf[1] = nva.na_atime.tv_nsec; 1091292915Sdim } 1092292915Sdim } 1093292915Sdim if (nd->nd_flag & ND_NFSV2) { 1094292915Sdim if (!nd->nd_repstat) { 1095292915Sdim (void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0); 1096292915Sdim nfsrv_fillattr(nd, &nva); 1097292915Sdim } 1098292915Sdim } else { 1099292915Sdim if (exclusive_flag && !nd->nd_repstat && (cverf[0] != tverf[0] 1100292915Sdim || cverf[1] != tverf[1])) 1101292915Sdim nd->nd_repstat = EEXIST; 1102292915Sdim diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0); 1103292915Sdim vrele(dirp); 1104292915Sdim if (!nd->nd_repstat) { 1105292915Sdim (void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 1); 1106292915Sdim nfsrv_postopattr(nd, 0, &nva); 1107292915Sdim } 1108292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft); 1109292915Sdim } 1110292915Sdim 1111292915Sdimout: 1112292915Sdim NFSEXITCODE2(0, nd); 1113292915Sdim return (0); 1114292915Sdimnfsmout: 1115292915Sdim vput(dp); 1116292915Sdim nfsvno_relpathbuf(&named); 1117292915Sdim NFSEXITCODE2(error, nd); 1118292915Sdim return (error); 1119292915Sdim} 1120292915Sdim 1121292915Sdim/* 1122292915Sdim * nfs v3 mknod service (and v4 create) 1123292915Sdim */ 1124292915SdimAPPLESTATIC int 1125292915Sdimnfsrvd_mknod(struct nfsrv_descript *nd, __unused int isdgram, 1126292915Sdim vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p, 1127292915Sdim struct nfsexstuff *exp) 1128292915Sdim{ 1129292915Sdim struct nfsvattr nva, dirfor, diraft; 1130292915Sdim u_int32_t *tl; 1131292915Sdim struct nameidata named; 1132292915Sdim int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen; 1133292915Sdim u_int32_t major, minor; 1134292915Sdim enum vtype vtyp = VNON; 1135292915Sdim nfstype nfs4type = NFNON; 1136292915Sdim vnode_t vp, dirp = NULL; 1137292915Sdim nfsattrbit_t attrbits; 1138292915Sdim char *bufp = NULL, *pathcp = NULL; 1139292915Sdim u_long *hashp, cnflags; 1140292915Sdim NFSACL_T *aclp = NULL; 1141292915Sdim 1142292915Sdim NFSVNO_ATTRINIT(&nva); 1143292915Sdim cnflags = (LOCKPARENT | SAVESTART); 1144292915Sdim if (nd->nd_repstat) { 1145292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft); 1146292915Sdim goto out; 1147292915Sdim } 1148292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME 1149292915Sdim aclp = acl_alloc(M_WAITOK); 1150292915Sdim aclp->acl_cnt = 0; 1151292915Sdim#endif 1152292915Sdim 1153292915Sdim /* 1154292915Sdim * For V4, the creation stuff is here, Yuck! 1155292915Sdim */ 1156292915Sdim if (nd->nd_flag & ND_NFSV4) { 1157292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 1158292915Sdim vtyp = nfsv34tov_type(*tl); 1159292915Sdim nfs4type = fxdr_unsigned(nfstype, *tl); 1160292915Sdim switch (nfs4type) { 1161292915Sdim case NFLNK: 1162292915Sdim error = nfsvno_getsymlink(nd, &nva, p, &pathcp, 1163292915Sdim &pathlen); 1164292915Sdim if (error) 1165292915Sdim goto nfsmout; 1166292915Sdim break; 1167292915Sdim case NFCHR: 1168292915Sdim case NFBLK: 1169292915Sdim NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED); 1170292915Sdim major = fxdr_unsigned(u_int32_t, *tl++); 1171292915Sdim minor = fxdr_unsigned(u_int32_t, *tl); 1172292915Sdim nva.na_rdev = NFSMAKEDEV(major, minor); 1173292915Sdim break; 1174292915Sdim case NFSOCK: 1175292915Sdim case NFFIFO: 1176292915Sdim break; 1177292915Sdim case NFDIR: 1178292915Sdim cnflags = (LOCKPARENT | SAVENAME); 1179292915Sdim break; 1180292915Sdim default: 1181292915Sdim nd->nd_repstat = NFSERR_BADTYPE; 1182292915Sdim vrele(dp); 1183292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME 1184292915Sdim acl_free(aclp); 1185292915Sdim#endif 1186292915Sdim goto out; 1187292915Sdim } 1188292915Sdim } 1189292915Sdim NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, cnflags); 1190292915Sdim nfsvno_setpathbuf(&named, &bufp, &hashp); 1191292915Sdim error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); 1192292915Sdim if (error) 1193292915Sdim goto nfsmout; 1194292915Sdim if (!nd->nd_repstat) { 1195292915Sdim if (nd->nd_flag & ND_NFSV3) { 1196292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 1197292915Sdim vtyp = nfsv34tov_type(*tl); 1198292915Sdim } 1199292915Sdim error = nfsrv_sattr(nd, &nva, &attrbits, aclp, p); 1200292915Sdim if (error) 1201292915Sdim goto nfsmout; 1202292915Sdim nva.na_type = vtyp; 1203292915Sdim if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV3) && 1204292915Sdim (vtyp == VCHR || vtyp == VBLK)) { 1205292915Sdim NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED); 1206292915Sdim major = fxdr_unsigned(u_int32_t, *tl++); 1207292915Sdim minor = fxdr_unsigned(u_int32_t, *tl); 1208292915Sdim nva.na_rdev = NFSMAKEDEV(major, minor); 1209292915Sdim } 1210292915Sdim } 1211292915Sdim 1212292915Sdim dirfor_ret = nfsvno_getattr(dp, &dirfor, nd->nd_cred, p, 0); 1213292915Sdim if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) { 1214292915Sdim if (!dirfor_ret && NFSVNO_ISSETGID(&nva) && 1215292915Sdim dirfor.na_gid == nva.na_gid) 1216292915Sdim NFSVNO_UNSET(&nva, gid); 1217292915Sdim nd->nd_repstat = nfsrv_checkuidgid(nd, &nva); 1218292915Sdim } 1219292915Sdim if (nd->nd_repstat) { 1220292915Sdim vrele(dp); 1221292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME 1222292915Sdim acl_free(aclp); 1223292915Sdim#endif 1224292915Sdim nfsvno_relpathbuf(&named); 1225292915Sdim if (pathcp) 1226292915Sdim FREE(pathcp, M_TEMP); 1227292915Sdim if (nd->nd_flag & ND_NFSV3) 1228292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, 1229292915Sdim &diraft); 1230292915Sdim goto out; 1231292915Sdim } 1232292915Sdim 1233292915Sdim /* 1234292915Sdim * Yuck! For V4, mkdir and link are here and some V4 clients don't fill 1235292915Sdim * in va_mode, so we'll have to set a default here. 1236292915Sdim */ 1237292915Sdim if (NFSVNO_NOTSETMODE(&nva)) { 1238292915Sdim if (vtyp == VLNK) 1239292915Sdim nva.na_mode = 0755; 1240292915Sdim else 1241292915Sdim nva.na_mode = 0400; 1242292915Sdim } 1243292915Sdim 1244292915Sdim if (vtyp == VDIR) 1245292915Sdim named.ni_cnd.cn_flags |= WILLBEDIR; 1246292915Sdim nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp); 1247292915Sdim if (nd->nd_repstat) { 1248292915Sdim if (dirp) { 1249292915Sdim if (nd->nd_flag & ND_NFSV3) 1250292915Sdim dirfor_ret = nfsvno_getattr(dirp, &dirfor, 1251292915Sdim nd->nd_cred, p, 0); 1252292915Sdim vrele(dirp); 1253292915Sdim } 1254292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME 1255292915Sdim acl_free(aclp); 1256292915Sdim#endif 1257292915Sdim if (nd->nd_flag & ND_NFSV3) 1258292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, 1259292915Sdim &diraft); 1260292915Sdim goto out; 1261292915Sdim } 1262292915Sdim if (dirp) 1263292915Sdim dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0); 1264292915Sdim 1265292915Sdim if ((nd->nd_flag & ND_NFSV4) && (vtyp == VDIR || vtyp == VLNK)) { 1266292915Sdim if (vtyp == VDIR) { 1267292915Sdim nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp, 1268292915Sdim &dirfor, &diraft, &diraft_ret, &attrbits, aclp, p, 1269292915Sdim exp); 1270292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME 1271292915Sdim acl_free(aclp); 1272292915Sdim#endif 1273292915Sdim goto out; 1274292915Sdim } else if (vtyp == VLNK) { 1275292915Sdim nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp, 1276292915Sdim &dirfor, &diraft, &diraft_ret, &attrbits, 1277292915Sdim aclp, p, exp, pathcp, pathlen); 1278294024Sdim#ifdef NFS4_ACL_EXTATTR_NAME 1279294024Sdim acl_free(aclp); 1280292915Sdim#endif 1281292915Sdim FREE(pathcp, M_TEMP); 1282292915Sdim goto out; 1283292915Sdim } 1284292915Sdim } 1285292915Sdim 1286292915Sdim nd->nd_repstat = nfsvno_mknod(&named, &nva, nd->nd_cred, p); 1287292915Sdim if (!nd->nd_repstat) { 1288292915Sdim vp = named.ni_vp; 1289292915Sdim nfsrv_fixattr(nd, vp, &nva, aclp, p, &attrbits, exp); 1290292915Sdim nd->nd_repstat = nfsvno_getfh(vp, fhp, p); 1291292915Sdim if ((nd->nd_flag & ND_NFSV3) && !nd->nd_repstat) 1292292915Sdim nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, 1293292915Sdim p, 1); 1294292915Sdim if (vpp != NULL && nd->nd_repstat == 0) { 1295292915Sdim NFSVOPUNLOCK(vp, 0); 1296292915Sdim *vpp = vp; 1297292915Sdim } else 1298292915Sdim vput(vp); 1299292915Sdim } 1300292915Sdim 1301292915Sdim diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0); 1302292915Sdim vrele(dirp); 1303294024Sdim if (!nd->nd_repstat) { 1304292915Sdim if (nd->nd_flag & ND_NFSV3) { 1305292915Sdim (void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1); 1306292915Sdim nfsrv_postopattr(nd, 0, &nva); 1307292915Sdim } else { 1308292915Sdim NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED); 1309292915Sdim *tl++ = newnfs_false; 1310292915Sdim txdr_hyper(dirfor.na_filerev, tl); 1311292915Sdim tl += 2; 1312292915Sdim txdr_hyper(diraft.na_filerev, tl); 1313292915Sdim (void) nfsrv_putattrbit(nd, &attrbits); 1314292915Sdim } 1315292915Sdim } 1316292915Sdim if (nd->nd_flag & ND_NFSV3) 1317292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft); 1318292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME 1319292915Sdim acl_free(aclp); 1320292915Sdim#endif 1321292915Sdim 1322292915Sdimout: 1323292915Sdim NFSEXITCODE2(0, nd); 1324292915Sdim return (0); 1325292915Sdimnfsmout: 1326292915Sdim vrele(dp); 1327292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME 1328292915Sdim acl_free(aclp); 1329292915Sdim#endif 1330292915Sdim if (bufp) 1331292915Sdim nfsvno_relpathbuf(&named); 1332292915Sdim if (pathcp) 1333292915Sdim FREE(pathcp, M_TEMP); 1334292915Sdim 1335292915Sdim NFSEXITCODE2(error, nd); 1336292915Sdim return (error); 1337292915Sdim} 1338292915Sdim 1339292915Sdim/* 1340292915Sdim * nfs remove service 1341292915Sdim */ 1342292915SdimAPPLESTATIC int 1343292915Sdimnfsrvd_remove(struct nfsrv_descript *nd, __unused int isdgram, 1344292915Sdim vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp) 1345292915Sdim{ 1346292915Sdim struct nameidata named; 1347292915Sdim u_int32_t *tl; 1348292915Sdim int error = 0, dirfor_ret = 1, diraft_ret = 1; 1349292915Sdim vnode_t dirp = NULL; 1350292915Sdim struct nfsvattr dirfor, diraft; 1351292915Sdim char *bufp; 1352292915Sdim u_long *hashp; 1353292915Sdim 1354292915Sdim if (nd->nd_repstat) { 1355292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft); 1356292915Sdim goto out; 1357292915Sdim } 1358292915Sdim NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, DELETE, 1359292915Sdim LOCKPARENT | LOCKLEAF); 1360292915Sdim nfsvno_setpathbuf(&named, &bufp, &hashp); 1361292915Sdim error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); 1362292915Sdim if (error) { 1363292915Sdim vput(dp); 1364292915Sdim nfsvno_relpathbuf(&named); 1365292915Sdim goto out; 1366292915Sdim } 1367292915Sdim if (!nd->nd_repstat) { 1368292915Sdim nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp); 1369292915Sdim } else { 1370292915Sdim vput(dp); 1371292915Sdim nfsvno_relpathbuf(&named); 1372292915Sdim } 1373292915Sdim if (dirp) { 1374292915Sdim if (!(nd->nd_flag & ND_NFSV2)) { 1375292915Sdim dirfor_ret = nfsvno_getattr(dirp, &dirfor, 1376292915Sdim nd->nd_cred, p, 0); 1377292915Sdim } else { 1378292915Sdim vrele(dirp); 1379292915Sdim dirp = NULL; 1380292915Sdim } 1381292915Sdim } 1382292915Sdim if (!nd->nd_repstat) { 1383292915Sdim if (nd->nd_flag & ND_NFSV4) { 1384292915Sdim if (vnode_vtype(named.ni_vp) == VDIR) 1385292915Sdim nd->nd_repstat = nfsvno_rmdirsub(&named, 1, 1386292915Sdim nd->nd_cred, p, exp); 1387292915Sdim else 1388292915Sdim nd->nd_repstat = nfsvno_removesub(&named, 1, 1389292915Sdim nd->nd_cred, p, exp); 1390292915Sdim } else if (nd->nd_procnum == NFSPROC_RMDIR) { 1391292915Sdim nd->nd_repstat = nfsvno_rmdirsub(&named, 0, 1392292915Sdim nd->nd_cred, p, exp); 1393292915Sdim } else { 1394292915Sdim nd->nd_repstat = nfsvno_removesub(&named, 0, 1395292915Sdim nd->nd_cred, p, exp); 1396292915Sdim } 1397292915Sdim } 1398292915Sdim if (!(nd->nd_flag & ND_NFSV2)) { 1399292915Sdim if (dirp) { 1400292915Sdim diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, 1401292915Sdim p, 0); 1402292915Sdim vrele(dirp); 1403292915Sdim } 1404292915Sdim if (nd->nd_flag & ND_NFSV3) { 1405292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, 1406292915Sdim &diraft); 1407292915Sdim } else if (!nd->nd_repstat) { 1408292915Sdim NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED); 1409292915Sdim *tl++ = newnfs_false; 1410292915Sdim txdr_hyper(dirfor.na_filerev, tl); 1411292915Sdim tl += 2; 1412292915Sdim txdr_hyper(diraft.na_filerev, tl); 1413292915Sdim } 1414292915Sdim } 1415292915Sdim 1416292915Sdimout: 1417292915Sdim NFSEXITCODE2(error, nd); 1418292915Sdim return (error); 1419292915Sdim} 1420292915Sdim 1421292915Sdim/* 1422292915Sdim * nfs rename service 1423292915Sdim */ 1424292915SdimAPPLESTATIC int 1425292915Sdimnfsrvd_rename(struct nfsrv_descript *nd, int isdgram, 1426292915Sdim vnode_t dp, vnode_t todp, NFSPROC_T *p, struct nfsexstuff *exp, 1427292915Sdim struct nfsexstuff *toexp) 1428292915Sdim{ 1429292915Sdim u_int32_t *tl; 1430292915Sdim int error = 0, fdirfor_ret = 1, fdiraft_ret = 1; 1431292915Sdim int tdirfor_ret = 1, tdiraft_ret = 1; 1432292915Sdim struct nameidata fromnd, tond; 1433292915Sdim vnode_t fdirp = NULL, tdirp = NULL, tdp = NULL; 1434292915Sdim struct nfsvattr fdirfor, fdiraft, tdirfor, tdiraft; 1435292915Sdim struct nfsexstuff tnes; 1436292915Sdim struct nfsrvfh tfh; 1437292915Sdim char *bufp, *tbufp = NULL; 1438292915Sdim u_long *hashp; 1439292915Sdim fhandle_t fh; 1440292915Sdim 1441292915Sdim if (nd->nd_repstat) { 1442292915Sdim nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft); 1443292915Sdim nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft); 1444292915Sdim goto out; 1445292915Sdim } 1446292915Sdim if (!(nd->nd_flag & ND_NFSV2)) 1447292915Sdim fdirfor_ret = nfsvno_getattr(dp, &fdirfor, nd->nd_cred, p, 1); 1448292915Sdim tond.ni_cnd.cn_nameiop = 0; 1449292915Sdim tond.ni_startdir = NULL; 1450292915Sdim NFSNAMEICNDSET(&fromnd.ni_cnd, nd->nd_cred, DELETE, WANTPARENT | SAVESTART); 1451292915Sdim nfsvno_setpathbuf(&fromnd, &bufp, &hashp); 1452292915Sdim error = nfsrv_parsename(nd, bufp, hashp, &fromnd.ni_pathlen); 1453292915Sdim if (error) { 1454292915Sdim vput(dp); 1455292915Sdim if (todp) 1456292915Sdim vrele(todp); 1457292915Sdim nfsvno_relpathbuf(&fromnd); 1458292915Sdim goto out; 1459292915Sdim } 1460292915Sdim if (nd->nd_flag & ND_NFSV4) { 1461292915Sdim tdp = todp; 1462292915Sdim tnes = *toexp; 1463292915Sdim tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred, p, 0); 1464292915Sdim } else { 1465292915Sdim tfh.nfsrvfh_len = 0; 1466292915Sdim error = nfsrv_mtofh(nd, &tfh); 1467292915Sdim if (error == 0) 1468292915Sdim error = nfsvno_getfh(dp, &fh, p); 1469292915Sdim if (error) { 1470292915Sdim vput(dp); 1471292915Sdim /* todp is always NULL except NFSv4 */ 1472292915Sdim nfsvno_relpathbuf(&fromnd); 1473292915Sdim goto out; 1474292915Sdim } 1475292915Sdim 1476292915Sdim /* If this is the same file handle, just VREF() the vnode. */ 1477292915Sdim if (tfh.nfsrvfh_len == NFSX_MYFH && 1478292915Sdim !NFSBCMP(tfh.nfsrvfh_data, &fh, NFSX_MYFH)) { 1479292915Sdim VREF(dp); 1480292915Sdim tdp = dp; 1481292915Sdim tnes = *exp; 1482292915Sdim tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred, 1483292915Sdim p, 1); 1484292915Sdim } else { 1485292915Sdim nd->nd_cred->cr_uid = nd->nd_saveduid; 1486292915Sdim nfsd_fhtovp(nd, &tfh, LK_EXCLUSIVE, &tdp, &tnes, NULL, 1487292915Sdim 0, p); 1488292915Sdim if (tdp) { 1489292915Sdim tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, 1490292915Sdim nd->nd_cred, p, 1); 1491292915Sdim NFSVOPUNLOCK(tdp, 0); 1492292915Sdim } 1493292915Sdim } 1494292915Sdim } 1495292915Sdim NFSNAMEICNDSET(&tond.ni_cnd, nd->nd_cred, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART); 1496292915Sdim nfsvno_setpathbuf(&tond, &tbufp, &hashp); 1497292915Sdim if (!nd->nd_repstat) { 1498292915Sdim error = nfsrv_parsename(nd, tbufp, hashp, &tond.ni_pathlen); 1499292915Sdim if (error) { 1500292915Sdim if (tdp) 1501292915Sdim vrele(tdp); 1502292915Sdim vput(dp); 1503292915Sdim nfsvno_relpathbuf(&fromnd); 1504292915Sdim nfsvno_relpathbuf(&tond); 1505292915Sdim goto out; 1506292915Sdim } 1507292915Sdim } 1508292915Sdim if (nd->nd_repstat) { 1509292915Sdim if (nd->nd_flag & ND_NFSV3) { 1510292915Sdim nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, 1511292915Sdim &fdiraft); 1512292915Sdim nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, 1513292915Sdim &tdiraft); 1514292915Sdim } 1515292915Sdim if (tdp) 1516292915Sdim vrele(tdp); 1517292915Sdim vput(dp); 1518292915Sdim nfsvno_relpathbuf(&fromnd); 1519292915Sdim nfsvno_relpathbuf(&tond); 1520292915Sdim goto out; 1521292915Sdim } 1522292915Sdim 1523292915Sdim /* 1524292915Sdim * Done parsing, now down to business. 1525292915Sdim */ 1526292915Sdim nd->nd_repstat = nfsvno_namei(nd, &fromnd, dp, 1, exp, p, &fdirp); 1527292915Sdim if (nd->nd_repstat) { 1528292915Sdim if (nd->nd_flag & ND_NFSV3) { 1529292915Sdim nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, 1530292915Sdim &fdiraft); 1531292915Sdim nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, 1532292915Sdim &tdiraft); 1533292915Sdim } 1534292915Sdim if (fdirp) 1535292915Sdim vrele(fdirp); 1536292915Sdim if (tdp) 1537292915Sdim vrele(tdp); 1538292915Sdim nfsvno_relpathbuf(&tond); 1539292915Sdim goto out; 1540292915Sdim } 1541292915Sdim if (vnode_vtype(fromnd.ni_vp) == VDIR) 1542292915Sdim tond.ni_cnd.cn_flags |= WILLBEDIR; 1543292915Sdim nd->nd_repstat = nfsvno_namei(nd, &tond, tdp, 0, &tnes, p, &tdirp); 1544292915Sdim nd->nd_repstat = nfsvno_rename(&fromnd, &tond, nd->nd_repstat, 1545292915Sdim nd->nd_flag, nd->nd_cred, p); 1546292915Sdim if (fdirp) 1547292915Sdim fdiraft_ret = nfsvno_getattr(fdirp, &fdiraft, nd->nd_cred, p, 1548292915Sdim 0); 1549292915Sdim if (tdirp) 1550292915Sdim tdiraft_ret = nfsvno_getattr(tdirp, &tdiraft, nd->nd_cred, p, 1551292915Sdim 0); 1552292915Sdim if (fdirp) 1553292915Sdim vrele(fdirp); 1554292915Sdim if (tdirp) 1555292915Sdim vrele(tdirp); 1556292915Sdim if (nd->nd_flag & ND_NFSV3) { 1557292915Sdim nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft); 1558292915Sdim nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft); 1559292915Sdim } else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) { 1560292915Sdim NFSM_BUILD(tl, u_int32_t *, 10 * NFSX_UNSIGNED); 1561292915Sdim *tl++ = newnfs_false; 1562292915Sdim txdr_hyper(fdirfor.na_filerev, tl); 1563292915Sdim tl += 2; 1564292915Sdim txdr_hyper(fdiraft.na_filerev, tl); 1565292915Sdim tl += 2; 1566292915Sdim *tl++ = newnfs_false; 1567292915Sdim txdr_hyper(tdirfor.na_filerev, tl); 1568292915Sdim tl += 2; 1569292915Sdim txdr_hyper(tdiraft.na_filerev, tl); 1570292915Sdim } 1571292915Sdim 1572292915Sdimout: 1573292915Sdim NFSEXITCODE2(error, nd); 1574292915Sdim return (error); 1575292915Sdim} 1576292915Sdim 1577292915Sdim/* 1578292915Sdim * nfs link service 1579292915Sdim */ 1580292915SdimAPPLESTATIC int 1581292915Sdimnfsrvd_link(struct nfsrv_descript *nd, int isdgram, 1582292915Sdim vnode_t vp, vnode_t tovp, NFSPROC_T *p, struct nfsexstuff *exp, 1583292915Sdim struct nfsexstuff *toexp) 1584292915Sdim{ 1585292915Sdim struct nameidata named; 1586292915Sdim u_int32_t *tl; 1587292915Sdim int error = 0, dirfor_ret = 1, diraft_ret = 1, getret = 1; 1588292915Sdim vnode_t dirp = NULL, dp = NULL; 1589292915Sdim struct nfsvattr dirfor, diraft, at; 1590292915Sdim struct nfsexstuff tnes; 1591292915Sdim struct nfsrvfh dfh; 1592292915Sdim char *bufp; 1593292915Sdim u_long *hashp; 1594292915Sdim 1595292915Sdim if (nd->nd_repstat) { 1596292915Sdim nfsrv_postopattr(nd, getret, &at); 1597292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft); 1598292915Sdim goto out; 1599292915Sdim } 1600292915Sdim NFSVOPUNLOCK(vp, 0); 1601292915Sdim if (vnode_vtype(vp) == VDIR) { 1602292915Sdim if (nd->nd_flag & ND_NFSV4) 1603292915Sdim nd->nd_repstat = NFSERR_ISDIR; 1604292915Sdim else 1605292915Sdim nd->nd_repstat = NFSERR_INVAL; 1606292915Sdim if (tovp) 1607292915Sdim vrele(tovp); 1608292915Sdim } else if (vnode_vtype(vp) == VLNK) { 1609292915Sdim if (nd->nd_flag & ND_NFSV2) 1610292915Sdim nd->nd_repstat = NFSERR_INVAL; 1611292915Sdim else 1612292915Sdim nd->nd_repstat = NFSERR_NOTSUPP; 1613292915Sdim if (tovp) 1614292915Sdim vrele(tovp); 1615292915Sdim } 1616292915Sdim if (!nd->nd_repstat) { 1617292915Sdim if (nd->nd_flag & ND_NFSV4) { 1618292915Sdim dp = tovp; 1619292915Sdim tnes = *toexp; 1620292915Sdim } else { 1621292915Sdim error = nfsrv_mtofh(nd, &dfh); 1622292915Sdim if (error) { 1623292915Sdim vrele(vp); 1624292915Sdim /* tovp is always NULL unless NFSv4 */ 1625292915Sdim goto out; 1626292915Sdim } 1627292915Sdim nfsd_fhtovp(nd, &dfh, LK_EXCLUSIVE, &dp, &tnes, NULL, 0, 1628292915Sdim p); 1629292915Sdim if (dp) 1630292915Sdim NFSVOPUNLOCK(dp, 0); 1631292915Sdim } 1632292915Sdim } 1633292915Sdim NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, 1634292915Sdim LOCKPARENT | SAVENAME); 1635292915Sdim if (!nd->nd_repstat) { 1636292915Sdim nfsvno_setpathbuf(&named, &bufp, &hashp); 1637292915Sdim error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); 1638292915Sdim if (error) { 1639292915Sdim vrele(vp); 1640292915Sdim if (dp) 1641292915Sdim vrele(dp); 1642292915Sdim nfsvno_relpathbuf(&named); 1643292915Sdim goto out; 1644292915Sdim } 1645292915Sdim if (!nd->nd_repstat) { 1646292915Sdim nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, &tnes, 1647292915Sdim p, &dirp); 1648292915Sdim } else { 1649292915Sdim if (dp) 1650292915Sdim vrele(dp); 1651292915Sdim nfsvno_relpathbuf(&named); 1652292915Sdim } 1653292915Sdim } 1654292915Sdim if (dirp) { 1655292915Sdim if (nd->nd_flag & ND_NFSV2) { 1656292915Sdim vrele(dirp); 1657292915Sdim dirp = NULL; 1658292915Sdim } else { 1659292915Sdim dirfor_ret = nfsvno_getattr(dirp, &dirfor, 1660292915Sdim nd->nd_cred, p, 0); 1661292915Sdim } 1662292915Sdim } 1663292915Sdim if (!nd->nd_repstat) 1664292915Sdim nd->nd_repstat = nfsvno_link(&named, vp, nd->nd_cred, p, exp); 1665292915Sdim if (nd->nd_flag & ND_NFSV3) 1666292915Sdim getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 0); 1667292915Sdim if (dirp) { 1668292915Sdim diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 0); 1669292915Sdim vrele(dirp); 1670292915Sdim } 1671292915Sdim vrele(vp); 1672292915Sdim if (nd->nd_flag & ND_NFSV3) { 1673292915Sdim nfsrv_postopattr(nd, getret, &at); 1674292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft); 1675292915Sdim } else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) { 1676292915Sdim NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED); 1677292915Sdim *tl++ = newnfs_false; 1678292915Sdim txdr_hyper(dirfor.na_filerev, tl); 1679292915Sdim tl += 2; 1680292915Sdim txdr_hyper(diraft.na_filerev, tl); 1681292915Sdim } 1682292915Sdim 1683292915Sdimout: 1684292915Sdim NFSEXITCODE2(error, nd); 1685292915Sdim return (error); 1686292915Sdim} 1687292915Sdim 1688292915Sdim/* 1689292915Sdim * nfs symbolic link service 1690292915Sdim */ 1691292915SdimAPPLESTATIC int 1692292915Sdimnfsrvd_symlink(struct nfsrv_descript *nd, __unused int isdgram, 1693292915Sdim vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p, 1694292915Sdim struct nfsexstuff *exp) 1695292915Sdim{ 1696292915Sdim struct nfsvattr nva, dirfor, diraft; 1697292915Sdim struct nameidata named; 1698292915Sdim int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen; 1699292915Sdim vnode_t dirp = NULL; 1700292915Sdim char *bufp, *pathcp = NULL; 1701292915Sdim u_long *hashp; 1702292915Sdim 1703292915Sdim if (nd->nd_repstat) { 1704292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft); 1705292915Sdim goto out; 1706292915Sdim } 1707292915Sdim if (vpp) 1708292915Sdim *vpp = NULL; 1709292915Sdim NFSVNO_ATTRINIT(&nva); 1710292915Sdim NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, 1711292915Sdim LOCKPARENT | SAVESTART); 1712292915Sdim nfsvno_setpathbuf(&named, &bufp, &hashp); 1713292915Sdim error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); 1714292915Sdim if (!error && !nd->nd_repstat) 1715292915Sdim error = nfsvno_getsymlink(nd, &nva, p, &pathcp, &pathlen); 1716292915Sdim if (error) { 1717292915Sdim vrele(dp); 1718292915Sdim nfsvno_relpathbuf(&named); 1719292915Sdim goto out; 1720292915Sdim } 1721292915Sdim if (!nd->nd_repstat) { 1722292915Sdim nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp); 1723292915Sdim } else { 1724292915Sdim vrele(dp); 1725292915Sdim nfsvno_relpathbuf(&named); 1726292915Sdim } 1727292915Sdim if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) { 1728292915Sdim vrele(dirp); 1729292915Sdim dirp = NULL; 1730292915Sdim } 1731292915Sdim 1732292915Sdim /* 1733292915Sdim * And call nfsrvd_symlinksub() to do the common code. It will 1734292915Sdim * return EBADRPC upon a parsing error, 0 otherwise. 1735292915Sdim */ 1736292915Sdim if (!nd->nd_repstat) { 1737292915Sdim if (dirp != NULL) 1738292915Sdim dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, 1739292915Sdim p, 0); 1740292915Sdim nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp, 1741292915Sdim &dirfor, &diraft, &diraft_ret, NULL, NULL, p, exp, 1742292915Sdim pathcp, pathlen); 1743292915Sdim } else if (dirp != NULL) { 1744292915Sdim dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0); 1745292915Sdim vrele(dirp); 1746292915Sdim } 1747292915Sdim if (pathcp) 1748292915Sdim FREE(pathcp, M_TEMP); 1749292915Sdim 1750292915Sdim if (nd->nd_flag & ND_NFSV3) { 1751292915Sdim if (!nd->nd_repstat) { 1752292915Sdim (void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1); 1753292915Sdim nfsrv_postopattr(nd, 0, &nva); 1754292915Sdim } 1755292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft); 1756292915Sdim } 1757292915Sdim 1758292915Sdimout: 1759292915Sdim NFSEXITCODE2(error, nd); 1760292915Sdim return (error); 1761292915Sdim} 1762292915Sdim 1763292915Sdim/* 1764292915Sdim * Common code for creating a symbolic link. 1765292915Sdim */ 1766292915Sdimstatic void 1767292915Sdimnfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp, 1768292915Sdim struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp, 1769292915Sdim vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp, 1770292915Sdim int *diraft_retp, nfsattrbit_t *attrbitp, 1771292915Sdim NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp, 1772292915Sdim int pathlen) 1773292915Sdim{ 1774292915Sdim u_int32_t *tl; 1775292915Sdim 1776292915Sdim nd->nd_repstat = nfsvno_symlink(ndp, nvap, pathcp, pathlen, 1777292915Sdim !(nd->nd_flag & ND_NFSV2), nd->nd_saveduid, nd->nd_cred, p, exp); 1778292915Sdim if (!nd->nd_repstat && !(nd->nd_flag & ND_NFSV2)) { 1779292915Sdim nfsrv_fixattr(nd, ndp->ni_vp, nvap, aclp, p, attrbitp, exp); 1780292915Sdim if (nd->nd_flag & ND_NFSV3) { 1781292915Sdim nd->nd_repstat = nfsvno_getfh(ndp->ni_vp, fhp, p); 1782292915Sdim if (!nd->nd_repstat) 1783292915Sdim nd->nd_repstat = nfsvno_getattr(ndp->ni_vp, 1784292915Sdim nvap, nd->nd_cred, p, 1); 1785292915Sdim } 1786292915Sdim if (vpp != NULL && nd->nd_repstat == 0) { 1787292915Sdim NFSVOPUNLOCK(ndp->ni_vp, 0); 1788292915Sdim *vpp = ndp->ni_vp; 1789292915Sdim } else 1790292915Sdim vput(ndp->ni_vp); 1791292915Sdim } 1792292915Sdim if (dirp) { 1793292915Sdim *diraft_retp = nfsvno_getattr(dirp, diraftp, nd->nd_cred, p, 0); 1794292915Sdim vrele(dirp); 1795292915Sdim } 1796292915Sdim if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) { 1797292915Sdim NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED); 1798292915Sdim *tl++ = newnfs_false; 1799292915Sdim txdr_hyper(dirforp->na_filerev, tl); 1800292915Sdim tl += 2; 1801292915Sdim txdr_hyper(diraftp->na_filerev, tl); 1802292915Sdim (void) nfsrv_putattrbit(nd, attrbitp); 1803292915Sdim } 1804292915Sdim 1805292915Sdim NFSEXITCODE2(0, nd); 1806292915Sdim} 1807292915Sdim 1808292915Sdim/* 1809292915Sdim * nfs mkdir service 1810292915Sdim */ 1811292915SdimAPPLESTATIC int 1812292915Sdimnfsrvd_mkdir(struct nfsrv_descript *nd, __unused int isdgram, 1813292915Sdim vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p, 1814292915Sdim struct nfsexstuff *exp) 1815292915Sdim{ 1816292915Sdim struct nfsvattr nva, dirfor, diraft; 1817292915Sdim struct nameidata named; 1818292915Sdim u_int32_t *tl; 1819292915Sdim int error = 0, dirfor_ret = 1, diraft_ret = 1; 1820292915Sdim vnode_t dirp = NULL; 1821292915Sdim char *bufp; 1822292915Sdim u_long *hashp; 1823292915Sdim 1824292915Sdim if (nd->nd_repstat) { 1825292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft); 1826292915Sdim goto out; 1827292915Sdim } 1828292915Sdim NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, 1829292915Sdim LOCKPARENT | SAVENAME); 1830292915Sdim nfsvno_setpathbuf(&named, &bufp, &hashp); 1831292915Sdim error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); 1832292915Sdim if (error) 1833292915Sdim goto nfsmout; 1834292915Sdim if (!nd->nd_repstat) { 1835292915Sdim NFSVNO_ATTRINIT(&nva); 1836292915Sdim if (nd->nd_flag & ND_NFSV3) { 1837292915Sdim error = nfsrv_sattr(nd, &nva, NULL, NULL, p); 1838292915Sdim if (error) 1839292915Sdim goto nfsmout; 1840292915Sdim } else { 1841292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 1842292915Sdim nva.na_mode = nfstov_mode(*tl++); 1843292915Sdim } 1844292915Sdim } 1845292915Sdim if (!nd->nd_repstat) { 1846292915Sdim nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp); 1847292915Sdim } else { 1848292915Sdim vrele(dp); 1849292915Sdim nfsvno_relpathbuf(&named); 1850292915Sdim } 1851292915Sdim if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) { 1852292915Sdim vrele(dirp); 1853292915Sdim dirp = NULL; 1854292915Sdim } 1855292915Sdim if (nd->nd_repstat) { 1856292915Sdim if (dirp != NULL) { 1857292915Sdim dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, 1858292915Sdim p, 0); 1859292915Sdim vrele(dirp); 1860292915Sdim } 1861292915Sdim if (nd->nd_flag & ND_NFSV3) 1862292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, 1863292915Sdim &diraft); 1864292915Sdim goto out; 1865292915Sdim } 1866292915Sdim if (dirp != NULL) 1867292915Sdim dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p, 0); 1868292915Sdim 1869292915Sdim /* 1870292915Sdim * Call nfsrvd_mkdirsub() for the code common to V4 as well. 1871292915Sdim */ 1872292915Sdim nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp, &dirfor, &diraft, 1873292915Sdim &diraft_ret, NULL, NULL, p, exp); 1874292915Sdim 1875292915Sdim if (nd->nd_flag & ND_NFSV3) { 1876292915Sdim if (!nd->nd_repstat) { 1877292915Sdim (void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1); 1878292915Sdim nfsrv_postopattr(nd, 0, &nva); 1879292915Sdim } 1880292915Sdim nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft); 1881292915Sdim } else if (!nd->nd_repstat) { 1882292915Sdim (void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0); 1883292915Sdim nfsrv_fillattr(nd, &nva); 1884292915Sdim } 1885292915Sdim 1886292915Sdimout: 1887292915Sdim NFSEXITCODE2(0, nd); 1888292915Sdim return (0); 1889292915Sdimnfsmout: 1890292915Sdim vrele(dp); 1891292915Sdim nfsvno_relpathbuf(&named); 1892292915Sdim NFSEXITCODE2(error, nd); 1893292915Sdim return (error); 1894292915Sdim} 1895292915Sdim 1896292915Sdim/* 1897292915Sdim * Code common to mkdir for V2,3 and 4. 1898292915Sdim */ 1899292915Sdimstatic void 1900292915Sdimnfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp, 1901292915Sdim struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp, 1902292915Sdim vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp, 1903292915Sdim int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp, 1904292915Sdim NFSPROC_T *p, struct nfsexstuff *exp) 1905292915Sdim{ 1906292915Sdim vnode_t vp; 1907292915Sdim u_int32_t *tl; 1908292915Sdim 1909292915Sdim NFSVNO_SETATTRVAL(nvap, type, VDIR); 1910292915Sdim nd->nd_repstat = nfsvno_mkdir(ndp, nvap, nd->nd_saveduid, 1911292915Sdim nd->nd_cred, p, exp); 1912292915Sdim if (!nd->nd_repstat) { 1913292915Sdim vp = ndp->ni_vp; 1914292915Sdim nfsrv_fixattr(nd, vp, nvap, aclp, p, attrbitp, exp); 1915292915Sdim nd->nd_repstat = nfsvno_getfh(vp, fhp, p); 1916292915Sdim if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) 1917292915Sdim nd->nd_repstat = nfsvno_getattr(vp, nvap, nd->nd_cred, 1918292915Sdim p, 1); 1919292915Sdim if (vpp && !nd->nd_repstat) { 1920292915Sdim NFSVOPUNLOCK(vp, 0); 1921292915Sdim *vpp = vp; 1922292915Sdim } else { 1923292915Sdim vput(vp); 1924292915Sdim } 1925292915Sdim } 1926292915Sdim if (dirp) { 1927292915Sdim *diraft_retp = nfsvno_getattr(dirp, diraftp, nd->nd_cred, p, 0); 1928292915Sdim vrele(dirp); 1929292915Sdim } 1930294024Sdim if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) { 1931294024Sdim NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED); 1932294024Sdim *tl++ = newnfs_false; 1933292915Sdim txdr_hyper(dirforp->na_filerev, tl); 1934292915Sdim tl += 2; 1935292915Sdim txdr_hyper(diraftp->na_filerev, tl); 1936292915Sdim (void) nfsrv_putattrbit(nd, attrbitp); 1937292915Sdim } 1938292915Sdim 1939292915Sdim NFSEXITCODE2(0, nd); 1940292915Sdim} 1941292915Sdim 1942292915Sdim/* 1943292915Sdim * nfs commit service 1944292915Sdim */ 1945292915SdimAPPLESTATIC int 1946292915Sdimnfsrvd_commit(struct nfsrv_descript *nd, __unused int isdgram, 1947292915Sdim vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) 1948292915Sdim{ 1949292915Sdim struct nfsvattr bfor, aft; 1950292915Sdim u_int32_t *tl; 1951292915Sdim int error = 0, for_ret = 1, aft_ret = 1, cnt; 1952292915Sdim u_int64_t off; 1953292915Sdim 1954292915Sdim if (nd->nd_repstat) { 1955292915Sdim nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft); 1956292915Sdim goto out; 1957294024Sdim } 1958294024Sdim NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED); 1959292915Sdim /* 1960292915Sdim * XXX At this time VOP_FSYNC() does not accept offset and byte 1961294024Sdim * count parameters, so these arguments are useless (someday maybe). 1962292915Sdim */ 1963292915Sdim off = fxdr_hyper(tl); 1964292915Sdim tl += 2; 1965292915Sdim cnt = fxdr_unsigned(int, *tl); 1966292915Sdim if (nd->nd_flag & ND_NFSV3) 1967292915Sdim for_ret = nfsvno_getattr(vp, &bfor, nd->nd_cred, p, 1); 1968292915Sdim nd->nd_repstat = nfsvno_fsync(vp, off, cnt, nd->nd_cred, p); 1969292915Sdim if (nd->nd_flag & ND_NFSV3) { 1970292915Sdim aft_ret = nfsvno_getattr(vp, &aft, nd->nd_cred, p, 1); 1971292915Sdim nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft); 1972292915Sdim } 1973292915Sdim vput(vp); 1974292915Sdim if (!nd->nd_repstat) { 1975292915Sdim NFSM_BUILD(tl, u_int32_t *, NFSX_VERF); 1976292915Sdim *tl++ = txdr_unsigned(nfsboottime.tv_sec); 1977292915Sdim *tl = txdr_unsigned(nfsboottime.tv_usec); 1978292915Sdim } 1979292915Sdim 1980292915Sdimout: 1981292915Sdim NFSEXITCODE2(0, nd); 1982292915Sdim return (0); 1983292915Sdimnfsmout: 1984292915Sdim vput(vp); 1985292915Sdim NFSEXITCODE2(error, nd); 1986292915Sdim return (error); 1987292915Sdim} 1988292915Sdim 1989292915Sdim/* 1990292915Sdim * nfs statfs service 1991292915Sdim */ 1992292915SdimAPPLESTATIC int 1993292915Sdimnfsrvd_statfs(struct nfsrv_descript *nd, __unused int isdgram, 1994292915Sdim vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) 1995292915Sdim{ 1996292915Sdim struct statfs *sf; 1997292915Sdim u_int32_t *tl; 1998294024Sdim int getret = 1; 1999294024Sdim struct nfsvattr at; 2000294024Sdim struct statfs sfs; 2001292915Sdim u_quad_t tval; 2002292915Sdim 2003292915Sdim if (nd->nd_repstat) { 2004292915Sdim nfsrv_postopattr(nd, getret, &at); 2005292915Sdim goto out; 2006292915Sdim } 2007292915Sdim sf = &sfs; 2008292915Sdim nd->nd_repstat = nfsvno_statfs(vp, sf); 2009292915Sdim getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1); 2010292915Sdim vput(vp); 2011292915Sdim if (nd->nd_flag & ND_NFSV3) 2012292915Sdim nfsrv_postopattr(nd, getret, &at); 2013292915Sdim if (nd->nd_repstat) 2014292915Sdim goto out; 2015292915Sdim if (nd->nd_flag & ND_NFSV2) { 2016292915Sdim NFSM_BUILD(tl, u_int32_t *, NFSX_V2STATFS); 2017292915Sdim *tl++ = txdr_unsigned(NFS_V2MAXDATA); 2018292915Sdim *tl++ = txdr_unsigned(sf->f_bsize); 2019292915Sdim *tl++ = txdr_unsigned(sf->f_blocks); 2020292915Sdim *tl++ = txdr_unsigned(sf->f_bfree); 2021292915Sdim *tl = txdr_unsigned(sf->f_bavail); 2022292915Sdim } else { 2023292915Sdim NFSM_BUILD(tl, u_int32_t *, NFSX_V3STATFS); 2024292915Sdim tval = (u_quad_t)sf->f_blocks; 2025292915Sdim tval *= (u_quad_t)sf->f_bsize; 2026292915Sdim txdr_hyper(tval, tl); tl += 2; 2027292915Sdim tval = (u_quad_t)sf->f_bfree; 2028292915Sdim tval *= (u_quad_t)sf->f_bsize; 2029292915Sdim txdr_hyper(tval, tl); tl += 2; 2030292915Sdim tval = (u_quad_t)sf->f_bavail; 2031292915Sdim tval *= (u_quad_t)sf->f_bsize; 2032292915Sdim txdr_hyper(tval, tl); tl += 2; 2033292915Sdim tval = (u_quad_t)sf->f_files; 2034292915Sdim txdr_hyper(tval, tl); tl += 2; 2035292915Sdim tval = (u_quad_t)sf->f_ffree; 2036292915Sdim txdr_hyper(tval, tl); tl += 2; 2037292915Sdim tval = (u_quad_t)sf->f_ffree; 2038292915Sdim txdr_hyper(tval, tl); tl += 2; 2039292915Sdim *tl = 0; 2040292915Sdim } 2041292915Sdim 2042292915Sdimout: 2043292915Sdim NFSEXITCODE2(0, nd); 2044292915Sdim return (0); 2045292915Sdim} 2046292915Sdim 2047292915Sdim/* 2048292915Sdim * nfs fsinfo service 2049292915Sdim */ 2050292915SdimAPPLESTATIC int 2051292915Sdimnfsrvd_fsinfo(struct nfsrv_descript *nd, int isdgram, 2052292915Sdim vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) 2053292915Sdim{ 2054294024Sdim u_int32_t *tl; 2055294024Sdim struct nfsfsinfo fs; 2056294024Sdim int getret = 1; 2057292915Sdim struct nfsvattr at; 2058292915Sdim 2059292915Sdim if (nd->nd_repstat) { 2060292915Sdim nfsrv_postopattr(nd, getret, &at); 2061292915Sdim goto out; 2062292915Sdim } 2063292915Sdim getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1); 2064292915Sdim nfsvno_getfs(&fs, isdgram); 2065292915Sdim vput(vp); 2066292915Sdim nfsrv_postopattr(nd, getret, &at); 2067292915Sdim NFSM_BUILD(tl, u_int32_t *, NFSX_V3FSINFO); 2068292915Sdim *tl++ = txdr_unsigned(fs.fs_rtmax); 2069292915Sdim *tl++ = txdr_unsigned(fs.fs_rtpref); 2070292915Sdim *tl++ = txdr_unsigned(fs.fs_rtmult); 2071292915Sdim *tl++ = txdr_unsigned(fs.fs_wtmax); 2072292915Sdim *tl++ = txdr_unsigned(fs.fs_wtpref); 2073292915Sdim *tl++ = txdr_unsigned(fs.fs_wtmult); 2074292915Sdim *tl++ = txdr_unsigned(fs.fs_dtpref); 2075292915Sdim txdr_hyper(fs.fs_maxfilesize, tl); 2076292915Sdim tl += 2; 2077292915Sdim txdr_nfsv3time(&fs.fs_timedelta, tl); 2078292915Sdim tl += 2; 2079292915Sdim *tl = txdr_unsigned(fs.fs_properties); 2080292915Sdim 2081292915Sdimout: 2082292915Sdim NFSEXITCODE2(0, nd); 2083292915Sdim return (0); 2084292915Sdim} 2085292915Sdim 2086292915Sdim/* 2087292915Sdim * nfs pathconf service 2088292915Sdim */ 2089294024SdimAPPLESTATIC int 2090294024Sdimnfsrvd_pathconf(struct nfsrv_descript *nd, __unused int isdgram, 2091292915Sdim vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) 2092292915Sdim{ 2093292915Sdim struct nfsv3_pathconf *pc; 2094292915Sdim int getret = 1; 2095292915Sdim register_t linkmax, namemax, chownres, notrunc; 2096292915Sdim struct nfsvattr at; 2097292915Sdim 2098292915Sdim if (nd->nd_repstat) { 2099292915Sdim nfsrv_postopattr(nd, getret, &at); 2100292915Sdim goto out; 2101292915Sdim } 2102292915Sdim nd->nd_repstat = nfsvno_pathconf(vp, _PC_LINK_MAX, &linkmax, 2103292915Sdim nd->nd_cred, p); 2104292915Sdim if (!nd->nd_repstat) 2105292915Sdim nd->nd_repstat = nfsvno_pathconf(vp, _PC_NAME_MAX, &namemax, 2106292915Sdim nd->nd_cred, p); 2107292915Sdim if (!nd->nd_repstat) 2108292915Sdim nd->nd_repstat=nfsvno_pathconf(vp, _PC_CHOWN_RESTRICTED, 2109292915Sdim &chownres, nd->nd_cred, p); 2110292915Sdim if (!nd->nd_repstat) 2111292915Sdim nd->nd_repstat = nfsvno_pathconf(vp, _PC_NO_TRUNC, ¬runc, 2112292915Sdim nd->nd_cred, p); 2113294024Sdim getret = nfsvno_getattr(vp, &at, nd->nd_cred, p, 1); 2114292915Sdim vput(vp); 2115292915Sdim nfsrv_postopattr(nd, getret, &at); 2116292915Sdim if (!nd->nd_repstat) { 2117292915Sdim NFSM_BUILD(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF); 2118292915Sdim pc->pc_linkmax = txdr_unsigned(linkmax); 2119292915Sdim pc->pc_namemax = txdr_unsigned(namemax); 2120292915Sdim pc->pc_notrunc = txdr_unsigned(notrunc); 2121292915Sdim pc->pc_chownrestricted = txdr_unsigned(chownres); 2122292915Sdim 2123294024Sdim /* 2124292915Sdim * These should probably be supported by VOP_PATHCONF(), but 2125292915Sdim * until msdosfs is exportable (why would you want to?), the 2126292915Sdim * Unix defaults should be ok. 2127292915Sdim */ 2128292915Sdim pc->pc_caseinsensitive = newnfs_false; 2129292915Sdim pc->pc_casepreserving = newnfs_true; 2130292915Sdim } 2131292915Sdim 2132292915Sdimout: 2133292915Sdim NFSEXITCODE2(0, nd); 2134292915Sdim return (0); 2135292915Sdim} 2136292915Sdim 2137292915Sdim/* 2138292915Sdim * nfsv4 lock service 2139292915Sdim */ 2140292915SdimAPPLESTATIC int 2141292915Sdimnfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram, 2142292915Sdim vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp) 2143292915Sdim{ 2144292915Sdim u_int32_t *tl; 2145292915Sdim int i; 2146292915Sdim struct nfsstate *stp = NULL; 2147292915Sdim struct nfslock *lop; 2148292915Sdim struct nfslockconflict cf; 2149292915Sdim int error = 0; 2150292915Sdim u_short flags = NFSLCK_LOCK, lflags; 2151292915Sdim u_int64_t offset, len; 2152292915Sdim nfsv4stateid_t stateid; 2153292915Sdim nfsquad_t clientid; 2154292915Sdim 2155292915Sdim NFSM_DISSECT(tl, u_int32_t *, 7 * NFSX_UNSIGNED); 2156292915Sdim i = fxdr_unsigned(int, *tl++); 2157292915Sdim switch (i) { 2158292915Sdim case NFSV4LOCKT_READW: 2159292915Sdim flags |= NFSLCK_BLOCKING; 2160292915Sdim case NFSV4LOCKT_READ: 2161292915Sdim lflags = NFSLCK_READ; 2162292915Sdim break; 2163292915Sdim case NFSV4LOCKT_WRITEW: 2164292915Sdim flags |= NFSLCK_BLOCKING; 2165292915Sdim case NFSV4LOCKT_WRITE: 2166292915Sdim lflags = NFSLCK_WRITE; 2167292915Sdim break; 2168292915Sdim default: 2169292915Sdim nd->nd_repstat = NFSERR_BADXDR; 2170292915Sdim goto nfsmout; 2171292915Sdim }; 2172292915Sdim if (*tl++ == newnfs_true) 2173292915Sdim flags |= NFSLCK_RECLAIM; 2174292915Sdim offset = fxdr_hyper(tl); 2175292915Sdim tl += 2; 2176292915Sdim len = fxdr_hyper(tl); 2177292915Sdim tl += 2; 2178292915Sdim if (*tl == newnfs_true) 2179292915Sdim flags |= NFSLCK_OPENTOLOCK; 2180292915Sdim if (flags & NFSLCK_OPENTOLOCK) { 2181292915Sdim NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED + NFSX_STATEID); 2182292915Sdim i = fxdr_unsigned(int, *(tl+4+(NFSX_STATEID / NFSX_UNSIGNED))); 2183292915Sdim if (i <= 0 || i > NFSV4_OPAQUELIMIT) { 2184292915Sdim nd->nd_repstat = NFSERR_BADXDR; 2185292915Sdim goto nfsmout; 2186292915Sdim } 2187292915Sdim MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i, 2188292915Sdim M_NFSDSTATE, M_WAITOK); 2189292915Sdim stp->ls_ownerlen = i; 2190292915Sdim stp->ls_op = nd->nd_rp; 2191292915Sdim stp->ls_seq = fxdr_unsigned(int, *tl++); 2192292915Sdim stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++); 2193292915Sdim NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other, 2194292915Sdim NFSX_STATEIDOTHER); 2195292915Sdim tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED); 2196292915Sdim stp->ls_opentolockseq = fxdr_unsigned(int, *tl++); 2197292915Sdim clientid.lval[0] = *tl++; 2198292915Sdim clientid.lval[1] = *tl++; 2199292915Sdim if (nd->nd_flag & ND_IMPLIEDCLID) { 2200292915Sdim if (nd->nd_clientid.qval != clientid.qval) 2201292915Sdim printf("EEK! multiple clids\n"); 2202292915Sdim } else { 2203292915Sdim nd->nd_flag |= ND_IMPLIEDCLID; 2204292915Sdim nd->nd_clientid.qval = clientid.qval; 2205292915Sdim } 2206292915Sdim error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen); 2207292915Sdim if (error) 2208292915Sdim goto nfsmout; 2209292915Sdim } else { 2210292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED); 2211292915Sdim MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate), 2212292915Sdim M_NFSDSTATE, M_WAITOK); 2213292915Sdim stp->ls_ownerlen = 0; 2214292915Sdim stp->ls_op = nd->nd_rp; 2215292915Sdim stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++); 2216292915Sdim NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other, 2217292915Sdim NFSX_STATEIDOTHER); 2218292915Sdim tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED); 2219292915Sdim stp->ls_seq = fxdr_unsigned(int, *tl); 2220292915Sdim clientid.lval[0] = stp->ls_stateid.other[0]; 2221292915Sdim clientid.lval[1] = stp->ls_stateid.other[1]; 2222292915Sdim if (nd->nd_flag & ND_IMPLIEDCLID) { 2223292915Sdim if (nd->nd_clientid.qval != clientid.qval) 2224292915Sdim printf("EEK! multiple clids\n"); 2225292915Sdim } else { 2226292915Sdim nd->nd_flag |= ND_IMPLIEDCLID; 2227292915Sdim nd->nd_clientid.qval = clientid.qval; 2228292915Sdim } 2229292915Sdim } 2230292915Sdim MALLOC(lop, struct nfslock *, sizeof (struct nfslock), 2231292915Sdim M_NFSDLOCK, M_WAITOK); 2232292915Sdim lop->lo_first = offset; 2233292915Sdim if (len == NFS64BITSSET) { 2234292915Sdim lop->lo_end = NFS64BITSSET; 2235292915Sdim } else { 2236292915Sdim lop->lo_end = offset + len; 2237292915Sdim if (lop->lo_end <= lop->lo_first) 2238292915Sdim nd->nd_repstat = NFSERR_INVAL; 2239292915Sdim } 2240292915Sdim lop->lo_flags = lflags; 2241292915Sdim stp->ls_flags = flags; 2242292915Sdim stp->ls_uid = nd->nd_cred->cr_uid; 2243292915Sdim 2244292915Sdim /* 2245292915Sdim * Do basic access checking. 2246292915Sdim */ 2247292915Sdim if (!nd->nd_repstat && vnode_vtype(vp) != VREG) { 2248292915Sdim if (vnode_vtype(vp) == VDIR) 2249292915Sdim nd->nd_repstat = NFSERR_ISDIR; 2250292915Sdim else 2251292915Sdim nd->nd_repstat = NFSERR_INVAL; 2252292915Sdim } 2253292915Sdim if (!nd->nd_repstat) { 2254292915Sdim if (lflags & NFSLCK_WRITE) { 2255292915Sdim nd->nd_repstat = nfsvno_accchk(vp, VWRITE, 2256292915Sdim nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER, 2257292915Sdim NFSACCCHK_VPISLOCKED, NULL); 2258292915Sdim } else { 2259292915Sdim nd->nd_repstat = nfsvno_accchk(vp, VREAD, 2260292915Sdim nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER, 2261292915Sdim NFSACCCHK_VPISLOCKED, NULL); 2262292915Sdim if (nd->nd_repstat) 2263292915Sdim nd->nd_repstat = nfsvno_accchk(vp, VEXEC, 2264292915Sdim nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER, 2265292915Sdim NFSACCCHK_VPISLOCKED, NULL); 2266292915Sdim } 2267292915Sdim } 2268292915Sdim 2269292915Sdim /* 2270292915Sdim * We call nfsrv_lockctrl() even if nd_repstat set, so that the 2271292915Sdim * seqid# gets updated. nfsrv_lockctrl() will return the value 2272292915Sdim * of nd_repstat, if it gets that far. 2273292915Sdim */ 2274292915Sdim nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid, 2275292915Sdim &stateid, exp, nd, p); 2276292915Sdim if (lop) 2277292915Sdim FREE((caddr_t)lop, M_NFSDLOCK); 2278292915Sdim if (stp) 2279292915Sdim FREE((caddr_t)stp, M_NFSDSTATE); 2280292915Sdim if (!nd->nd_repstat) { 2281292915Sdim NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID); 2282292915Sdim *tl++ = txdr_unsigned(stateid.seqid); 2283292915Sdim NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER); 2284292915Sdim } else if (nd->nd_repstat == NFSERR_DENIED) { 2285292915Sdim NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED); 2286292915Sdim txdr_hyper(cf.cl_first, tl); 2287292915Sdim tl += 2; 2288292915Sdim if (cf.cl_end == NFS64BITSSET) 2289292915Sdim len = NFS64BITSSET; 2290292915Sdim else 2291292915Sdim len = cf.cl_end - cf.cl_first; 2292292915Sdim txdr_hyper(len, tl); 2293292915Sdim tl += 2; 2294292915Sdim if (cf.cl_flags == NFSLCK_WRITE) 2295292915Sdim *tl++ = txdr_unsigned(NFSV4LOCKT_WRITE); 2296292915Sdim else 2297292915Sdim *tl++ = txdr_unsigned(NFSV4LOCKT_READ); 2298292915Sdim *tl++ = stateid.other[0]; 2299292915Sdim *tl = stateid.other[1]; 2300292915Sdim (void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen); 2301292915Sdim } 2302292915Sdim vput(vp); 2303292915Sdim NFSEXITCODE2(0, nd); 2304292915Sdim return (0); 2305292915Sdimnfsmout: 2306292915Sdim vput(vp); 2307292915Sdim if (stp) 2308292915Sdim free((caddr_t)stp, M_NFSDSTATE); 2309292915Sdim NFSEXITCODE2(error, nd); 2310292915Sdim return (error); 2311292915Sdim} 2312292915Sdim 2313292915Sdim/* 2314292915Sdim * nfsv4 lock test service 2315292915Sdim */ 2316292915SdimAPPLESTATIC int 2317292915Sdimnfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram, 2318292915Sdim vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp) 2319292915Sdim{ 2320292915Sdim u_int32_t *tl; 2321292915Sdim int i; 2322292915Sdim struct nfsstate *stp = NULL; 2323292915Sdim struct nfslock lo, *lop = &lo; 2324292915Sdim struct nfslockconflict cf; 2325292915Sdim int error = 0; 2326292915Sdim nfsv4stateid_t stateid; 2327292915Sdim nfsquad_t clientid; 2328292915Sdim u_int64_t len; 2329292915Sdim 2330292915Sdim NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED); 2331292915Sdim i = fxdr_unsigned(int, *(tl + 7)); 2332292915Sdim if (i <= 0 || i > NFSV4_OPAQUELIMIT) { 2333292915Sdim nd->nd_repstat = NFSERR_BADXDR; 2334292915Sdim goto nfsmout; 2335292915Sdim } 2336292915Sdim MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i, 2337292915Sdim M_NFSDSTATE, M_WAITOK); 2338292915Sdim stp->ls_ownerlen = i; 2339292915Sdim stp->ls_op = NULL; 2340292915Sdim stp->ls_flags = NFSLCK_TEST; 2341292915Sdim stp->ls_uid = nd->nd_cred->cr_uid; 2342292915Sdim i = fxdr_unsigned(int, *tl++); 2343292915Sdim switch (i) { 2344292915Sdim case NFSV4LOCKT_READW: 2345292915Sdim stp->ls_flags |= NFSLCK_BLOCKING; 2346292915Sdim case NFSV4LOCKT_READ: 2347292915Sdim lo.lo_flags = NFSLCK_READ; 2348292915Sdim break; 2349292915Sdim case NFSV4LOCKT_WRITEW: 2350292915Sdim stp->ls_flags |= NFSLCK_BLOCKING; 2351292915Sdim case NFSV4LOCKT_WRITE: 2352292915Sdim lo.lo_flags = NFSLCK_WRITE; 2353292915Sdim break; 2354292915Sdim default: 2355292915Sdim nd->nd_repstat = NFSERR_BADXDR; 2356292915Sdim goto nfsmout; 2357292915Sdim }; 2358292915Sdim lo.lo_first = fxdr_hyper(tl); 2359292915Sdim tl += 2; 2360292915Sdim len = fxdr_hyper(tl); 2361292915Sdim if (len == NFS64BITSSET) { 2362292915Sdim lo.lo_end = NFS64BITSSET; 2363292915Sdim } else { 2364292915Sdim lo.lo_end = lo.lo_first + len; 2365292915Sdim if (lo.lo_end <= lo.lo_first) 2366292915Sdim nd->nd_repstat = NFSERR_INVAL; 2367292915Sdim } 2368292915Sdim tl += 2; 2369292915Sdim clientid.lval[0] = *tl++; 2370292915Sdim clientid.lval[1] = *tl; 2371292915Sdim if (nd->nd_flag & ND_IMPLIEDCLID) { 2372292915Sdim if (nd->nd_clientid.qval != clientid.qval) 2373292915Sdim printf("EEK! multiple clids\n"); 2374292915Sdim } else { 2375292915Sdim nd->nd_flag |= ND_IMPLIEDCLID; 2376292915Sdim nd->nd_clientid.qval = clientid.qval; 2377292915Sdim } 2378292915Sdim error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen); 2379292915Sdim if (error) 2380292915Sdim goto nfsmout; 2381292915Sdim if (!nd->nd_repstat && vnode_vtype(vp) != VREG) { 2382292915Sdim if (vnode_vtype(vp) == VDIR) 2383292915Sdim nd->nd_repstat = NFSERR_ISDIR; 2384292915Sdim else 2385292915Sdim nd->nd_repstat = NFSERR_INVAL; 2386292915Sdim } 2387292915Sdim if (!nd->nd_repstat) 2388292915Sdim nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid, 2389292915Sdim &stateid, exp, nd, p); 2390292915Sdim if (stp) 2391292915Sdim FREE((caddr_t)stp, M_NFSDSTATE); 2392292915Sdim if (nd->nd_repstat) { 2393292915Sdim if (nd->nd_repstat == NFSERR_DENIED) { 2394292915Sdim NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED); 2395292915Sdim txdr_hyper(cf.cl_first, tl); 2396292915Sdim tl += 2; 2397292915Sdim if (cf.cl_end == NFS64BITSSET) 2398292915Sdim len = NFS64BITSSET; 2399292915Sdim else 2400292915Sdim len = cf.cl_end - cf.cl_first; 2401292915Sdim txdr_hyper(len, tl); 2402292915Sdim tl += 2; 2403292915Sdim if (cf.cl_flags == NFSLCK_WRITE) 2404292915Sdim *tl++ = txdr_unsigned(NFSV4LOCKT_WRITE); 2405292915Sdim else 2406292915Sdim *tl++ = txdr_unsigned(NFSV4LOCKT_READ); 2407292915Sdim *tl++ = stp->ls_stateid.other[0]; 2408292915Sdim *tl = stp->ls_stateid.other[1]; 2409292915Sdim (void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen); 2410292915Sdim } 2411292915Sdim } 2412292915Sdim vput(vp); 2413292915Sdim NFSEXITCODE2(0, nd); 2414292915Sdim return (0); 2415292915Sdimnfsmout: 2416292915Sdim vput(vp); 2417292915Sdim if (stp) 2418292915Sdim free((caddr_t)stp, M_NFSDSTATE); 2419292915Sdim NFSEXITCODE2(error, nd); 2420292915Sdim return (error); 2421292915Sdim} 2422292915Sdim 2423292915Sdim/* 2424292915Sdim * nfsv4 unlock service 2425292915Sdim */ 2426292915SdimAPPLESTATIC int 2427292915Sdimnfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram, 2428292915Sdim vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp) 2429292915Sdim{ 2430292915Sdim u_int32_t *tl; 2431292915Sdim int i; 2432292915Sdim struct nfsstate *stp; 2433292915Sdim struct nfslock *lop; 2434292915Sdim int error = 0; 2435292915Sdim nfsv4stateid_t stateid; 2436292915Sdim nfsquad_t clientid; 2437292915Sdim u_int64_t len; 2438292915Sdim 2439292915Sdim NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED + NFSX_STATEID); 2440292915Sdim MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate), 2441292915Sdim M_NFSDSTATE, M_WAITOK); 2442292915Sdim MALLOC(lop, struct nfslock *, sizeof (struct nfslock), 2443292915Sdim M_NFSDLOCK, M_WAITOK); 2444292915Sdim stp->ls_flags = NFSLCK_UNLOCK; 2445292915Sdim lop->lo_flags = NFSLCK_UNLOCK; 2446292915Sdim stp->ls_op = nd->nd_rp; 2447292915Sdim i = fxdr_unsigned(int, *tl++); 2448292915Sdim switch (i) { 2449292915Sdim case NFSV4LOCKT_READW: 2450292915Sdim stp->ls_flags |= NFSLCK_BLOCKING; 2451292915Sdim case NFSV4LOCKT_READ: 2452292915Sdim break; 2453292915Sdim case NFSV4LOCKT_WRITEW: 2454292915Sdim stp->ls_flags |= NFSLCK_BLOCKING; 2455292915Sdim case NFSV4LOCKT_WRITE: 2456292915Sdim break; 2457292915Sdim default: 2458292915Sdim nd->nd_repstat = NFSERR_BADXDR; 2459292915Sdim free(stp, M_NFSDSTATE); 2460292915Sdim free(lop, M_NFSDLOCK); 2461292915Sdim goto nfsmout; 2462292915Sdim }; 2463292915Sdim stp->ls_ownerlen = 0; 2464292915Sdim stp->ls_uid = nd->nd_cred->cr_uid; 2465292915Sdim stp->ls_seq = fxdr_unsigned(int, *tl++); 2466292915Sdim stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++); 2467292915Sdim NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other, 2468292915Sdim NFSX_STATEIDOTHER); 2469292915Sdim tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED); 2470292915Sdim lop->lo_first = fxdr_hyper(tl); 2471292915Sdim tl += 2; 2472292915Sdim len = fxdr_hyper(tl); 2473292915Sdim if (len == NFS64BITSSET) { 2474292915Sdim lop->lo_end = NFS64BITSSET; 2475292915Sdim } else { 2476292915Sdim lop->lo_end = lop->lo_first + len; 2477292915Sdim if (lop->lo_end <= lop->lo_first) 2478292915Sdim nd->nd_repstat = NFSERR_INVAL; 2479292915Sdim } 2480292915Sdim clientid.lval[0] = stp->ls_stateid.other[0]; 2481292915Sdim clientid.lval[1] = stp->ls_stateid.other[1]; 2482292915Sdim if (nd->nd_flag & ND_IMPLIEDCLID) { 2483292915Sdim if (nd->nd_clientid.qval != clientid.qval) 2484292915Sdim printf("EEK! multiple clids\n"); 2485292915Sdim } else { 2486292915Sdim nd->nd_flag |= ND_IMPLIEDCLID; 2487292915Sdim nd->nd_clientid.qval = clientid.qval; 2488292915Sdim } 2489292915Sdim if (!nd->nd_repstat && vnode_vtype(vp) != VREG) { 2490292915Sdim if (vnode_vtype(vp) == VDIR) 2491292915Sdim nd->nd_repstat = NFSERR_ISDIR; 2492292915Sdim else 2493292915Sdim nd->nd_repstat = NFSERR_INVAL; 2494292915Sdim } 2495292915Sdim /* 2496292915Sdim * Call nfsrv_lockctrl() even if nd_repstat is set, so that the 2497292915Sdim * seqid# gets incremented. nfsrv_lockctrl() will return the 2498292915Sdim * value of nd_repstat, if it gets that far. 2499292915Sdim */ 2500292915Sdim nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid, 2501292915Sdim &stateid, exp, nd, p); 2502292915Sdim if (stp) 2503292915Sdim FREE((caddr_t)stp, M_NFSDSTATE); 2504292915Sdim if (lop) 2505292915Sdim free((caddr_t)lop, M_NFSDLOCK); 2506292915Sdim if (!nd->nd_repstat) { 2507292915Sdim NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID); 2508292915Sdim *tl++ = txdr_unsigned(stateid.seqid); 2509292915Sdim NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER); 2510292915Sdim } 2511292915Sdimnfsmout: 2512292915Sdim vput(vp); 2513292915Sdim NFSEXITCODE2(error, nd); 2514292915Sdim return (error); 2515292915Sdim} 2516292915Sdim 2517292915Sdim/* 2518292915Sdim * nfsv4 open service 2519292915Sdim */ 2520292915SdimAPPLESTATIC int 2521292915Sdimnfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram, 2522292915Sdim vnode_t dp, vnode_t *vpp, __unused fhandle_t *fhp, NFSPROC_T *p, 2523292915Sdim struct nfsexstuff *exp) 2524292915Sdim{ 2525292915Sdim u_int32_t *tl; 2526292915Sdim int i; 2527292915Sdim struct nfsstate *stp = NULL; 2528292915Sdim int error = 0, create, claim, exclusive_flag = 0; 2529292915Sdim u_int32_t rflags = NFSV4OPEN_LOCKTYPEPOSIX, acemask; 2530292915Sdim int how = NFSCREATE_UNCHECKED; 2531292915Sdim int32_t cverf[2], tverf[2] = { 0, 0 }; 2532292915Sdim vnode_t vp = NULL, dirp = NULL; 2533292915Sdim struct nfsvattr nva, dirfor, diraft; 2534292915Sdim struct nameidata named; 2535292915Sdim nfsv4stateid_t stateid, delegstateid; 2536292915Sdim nfsattrbit_t attrbits; 2537292915Sdim nfsquad_t clientid; 2538292915Sdim char *bufp = NULL; 2539292915Sdim u_long *hashp; 2540292915Sdim NFSACL_T *aclp = NULL; 2541292915Sdim 2542292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME 2543292915Sdim aclp = acl_alloc(M_WAITOK); 2544292915Sdim aclp->acl_cnt = 0; 2545292915Sdim#endif 2546292915Sdim NFSZERO_ATTRBIT(&attrbits); 2547292915Sdim named.ni_startdir = NULL; 2548292915Sdim named.ni_cnd.cn_nameiop = 0; 2549292915Sdim NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED); 2550292915Sdim i = fxdr_unsigned(int, *(tl + 5)); 2551292915Sdim if (i <= 0 || i > NFSV4_OPAQUELIMIT) { 2552292915Sdim nd->nd_repstat = NFSERR_BADXDR; 2553292915Sdim goto nfsmout; 2554292915Sdim } 2555292915Sdim MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i, 2556292915Sdim M_NFSDSTATE, M_WAITOK); 2557292915Sdim stp->ls_ownerlen = i; 2558292915Sdim stp->ls_op = nd->nd_rp; 2559292915Sdim stp->ls_flags = NFSLCK_OPEN; 2560292915Sdim stp->ls_uid = nd->nd_cred->cr_uid; 2561292915Sdim stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++); 2562292915Sdim i = fxdr_unsigned(int, *tl++); 2563292915Sdim switch (i) { 2564292915Sdim case NFSV4OPEN_ACCESSREAD: 2565292915Sdim stp->ls_flags |= NFSLCK_READACCESS; 2566292915Sdim break; 2567292915Sdim case NFSV4OPEN_ACCESSWRITE: 2568292915Sdim stp->ls_flags |= NFSLCK_WRITEACCESS; 2569292915Sdim break; 2570292915Sdim case NFSV4OPEN_ACCESSBOTH: 2571292915Sdim stp->ls_flags |= (NFSLCK_READACCESS | NFSLCK_WRITEACCESS); 2572292915Sdim break; 2573292915Sdim default: 2574292915Sdim nd->nd_repstat = NFSERR_INVAL; 2575292915Sdim }; 2576292915Sdim i = fxdr_unsigned(int, *tl++); 2577292915Sdim switch (i) { 2578292915Sdim case NFSV4OPEN_DENYNONE: 2579292915Sdim break; 2580292915Sdim case NFSV4OPEN_DENYREAD: 2581292915Sdim stp->ls_flags |= NFSLCK_READDENY; 2582292915Sdim break; 2583292915Sdim case NFSV4OPEN_DENYWRITE: 2584292915Sdim stp->ls_flags |= NFSLCK_WRITEDENY; 2585292915Sdim break; 2586292915Sdim case NFSV4OPEN_DENYBOTH: 2587292915Sdim stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY); 2588292915Sdim break; 2589292915Sdim default: 2590292915Sdim nd->nd_repstat = NFSERR_INVAL; 2591292915Sdim }; 2592292915Sdim clientid.lval[0] = *tl++; 2593292915Sdim clientid.lval[1] = *tl; 2594292915Sdim if (nd->nd_flag & ND_IMPLIEDCLID) { 2595292915Sdim if (nd->nd_clientid.qval != clientid.qval) 2596292915Sdim printf("EEK! multiple clids\n"); 2597292915Sdim } else { 2598292915Sdim nd->nd_flag |= ND_IMPLIEDCLID; 2599292915Sdim nd->nd_clientid.qval = clientid.qval; 2600292915Sdim } 2601292915Sdim error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen); 2602292915Sdim if (error) 2603292915Sdim goto nfsmout; 2604292915Sdim NFSVNO_ATTRINIT(&nva); 2605292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 2606292915Sdim create = fxdr_unsigned(int, *tl); 2607292915Sdim if (!nd->nd_repstat) 2608292915Sdim nd->nd_repstat = nfsvno_getattr(dp, &dirfor, nd->nd_cred, p, 0); 2609292915Sdim if (create == NFSV4OPEN_CREATE) { 2610292915Sdim nva.na_type = VREG; 2611292915Sdim nva.na_mode = 0; 2612292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 2613292915Sdim how = fxdr_unsigned(int, *tl); 2614292915Sdim switch (how) { 2615292915Sdim case NFSCREATE_UNCHECKED: 2616292915Sdim case NFSCREATE_GUARDED: 2617292915Sdim error = nfsv4_sattr(nd, &nva, &attrbits, aclp, p); 2618292915Sdim if (error) 2619292915Sdim goto nfsmout; 2620292915Sdim /* 2621292915Sdim * If the na_gid being set is the same as that of 2622292915Sdim * the directory it is going in, clear it, since 2623292915Sdim * that is what will be set by default. This allows 2624292915Sdim * a user that isn't in that group to do the create. 2625292915Sdim */ 2626292915Sdim if (!nd->nd_repstat && NFSVNO_ISSETGID(&nva) && 2627292915Sdim nva.na_gid == dirfor.na_gid) 2628292915Sdim NFSVNO_UNSET(&nva, gid); 2629292915Sdim if (!nd->nd_repstat) 2630292915Sdim nd->nd_repstat = nfsrv_checkuidgid(nd, &nva); 2631292915Sdim break; 2632292915Sdim case NFSCREATE_EXCLUSIVE: 2633292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF); 2634292915Sdim cverf[0] = *tl++; 2635292915Sdim cverf[1] = *tl; 2636292915Sdim break; 2637292915Sdim default: 2638292915Sdim nd->nd_repstat = NFSERR_BADXDR; 2639292915Sdim goto nfsmout; 2640292915Sdim }; 2641292915Sdim } else if (create != NFSV4OPEN_NOCREATE) { 2642292915Sdim nd->nd_repstat = NFSERR_BADXDR; 2643292915Sdim goto nfsmout; 2644292915Sdim } 2645292915Sdim 2646292915Sdim /* 2647292915Sdim * Now, handle the claim, which usually includes looking up a 2648292915Sdim * name in the directory referenced by dp. The exception is 2649292915Sdim * NFSV4OPEN_CLAIMPREVIOUS. 2650292915Sdim */ 2651292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 2652292915Sdim claim = fxdr_unsigned(int, *tl); 2653292915Sdim if (claim == NFSV4OPEN_CLAIMDELEGATECUR) { 2654292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID); 2655292915Sdim stateid.seqid = fxdr_unsigned(u_int32_t, *tl++); 2656292915Sdim NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER); 2657292915Sdim stp->ls_flags |= NFSLCK_DELEGCUR; 2658292915Sdim } else if (claim == NFSV4OPEN_CLAIMDELEGATEPREV) { 2659292915Sdim stp->ls_flags |= NFSLCK_DELEGPREV; 2660292915Sdim } 2661292915Sdim if (claim == NFSV4OPEN_CLAIMNULL || claim == NFSV4OPEN_CLAIMDELEGATECUR 2662292915Sdim || claim == NFSV4OPEN_CLAIMDELEGATEPREV) { 2663292915Sdim if (!nd->nd_repstat && create == NFSV4OPEN_CREATE && 2664292915Sdim claim != NFSV4OPEN_CLAIMNULL) 2665292915Sdim nd->nd_repstat = NFSERR_INVAL; 2666292915Sdim if (nd->nd_repstat) { 2667292915Sdim nd->nd_repstat = nfsrv_opencheck(clientid, 2668292915Sdim &stateid, stp, NULL, nd, p, nd->nd_repstat); 2669292915Sdim goto nfsmout; 2670292915Sdim } 2671292915Sdim if (create == NFSV4OPEN_CREATE) 2672292915Sdim NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, 2673292915Sdim LOCKPARENT | LOCKLEAF | SAVESTART); 2674292915Sdim else 2675292915Sdim NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP, 2676292915Sdim LOCKLEAF | SAVESTART); 2677292915Sdim nfsvno_setpathbuf(&named, &bufp, &hashp); 2678292915Sdim error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); 2679292915Sdim if (error) { 2680292915Sdim vrele(dp); 2681292915Sdim#ifdef NFS4_ACL_EXTATTR_NAME 2682292915Sdim acl_free(aclp); 2683292915Sdim#endif 2684292915Sdim FREE((caddr_t)stp, M_NFSDSTATE); 2685292915Sdim nfsvno_relpathbuf(&named); 2686292915Sdim NFSEXITCODE2(error, nd); 2687292915Sdim return (error); 2688292915Sdim } 2689292915Sdim if (!nd->nd_repstat) { 2690292915Sdim nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, 2691292915Sdim p, &dirp); 2692292915Sdim } else { 2693292915Sdim vrele(dp); 2694292915Sdim nfsvno_relpathbuf(&named); 2695292915Sdim } 2696292915Sdim if (create == NFSV4OPEN_CREATE) { 2697292915Sdim switch (how) { 2698292915Sdim case NFSCREATE_UNCHECKED: 2699292915Sdim if (named.ni_vp) { 2700292915Sdim /* 2701292915Sdim * Clear the setable attribute bits, except 2702292915Sdim * for Size, if it is being truncated. 2703292915Sdim */ 2704292915Sdim NFSZERO_ATTRBIT(&attrbits); 2705292915Sdim if (NFSVNO_ISSETSIZE(&nva)) 2706292915Sdim NFSSETBIT_ATTRBIT(&attrbits, 2707292915Sdim NFSATTRBIT_SIZE); 2708292915Sdim } 2709292915Sdim break; 2710292915Sdim case NFSCREATE_GUARDED: 2711292915Sdim if (named.ni_vp && !nd->nd_repstat) 2712292915Sdim nd->nd_repstat = EEXIST; 2713292915Sdim break; 2714292915Sdim case NFSCREATE_EXCLUSIVE: 2715292915Sdim exclusive_flag = 1; 2716292915Sdim if (!named.ni_vp) 2717292915Sdim nva.na_mode = 0; 2718292915Sdim }; 2719292915Sdim } 2720292915Sdim nfsvno_open(nd, &named, clientid, &stateid, stp, 2721292915Sdim &exclusive_flag, &nva, cverf, create, aclp, &attrbits, 2722292915Sdim nd->nd_cred, p, exp, &vp); 2723292915Sdim } else if (claim == NFSV4OPEN_CLAIMPREVIOUS) { 2724292915Sdim NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 2725292915Sdim i = fxdr_unsigned(int, *tl); 2726292915Sdim switch (i) { 2727292915Sdim case NFSV4OPEN_DELEGATEREAD: 2728292915Sdim stp->ls_flags |= NFSLCK_DELEGREAD; 2729292915Sdim break; 2730292915Sdim case NFSV4OPEN_DELEGATEWRITE: 2731292915Sdim stp->ls_flags |= NFSLCK_DELEGWRITE; 2732292915Sdim case NFSV4OPEN_DELEGATENONE: 2733292915Sdim break; 2734292915Sdim default: 2735292915Sdim nd->nd_repstat = NFSERR_BADXDR; 2736292915Sdim goto nfsmout; 2737292915Sdim }; 2738292915Sdim stp->ls_flags |= NFSLCK_RECLAIM; 2739292915Sdim vp = dp; 2740292915Sdim NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY); 2741292915Sdim if ((vp->v_iflag & VI_DOOMED) == 0) 2742292915Sdim nd->nd_repstat = nfsrv_opencheck(clientid, &stateid, 2743292915Sdim stp, vp, nd, p, nd->nd_repstat); 2744292915Sdim else 2745292915Sdim nd->nd_repstat = NFSERR_PERM; 2746292915Sdim } else { 2747292915Sdim nd->nd_repstat = NFSERR_BADXDR; 2748292915Sdim goto nfsmout; 2749292915Sdim } 2750292915Sdim 2751292915Sdim /* 2752292915Sdim * Do basic access checking. 2753292915Sdim */ 2754292915Sdim if (!nd->nd_repstat && vnode_vtype(vp) != VREG) { 2755292915Sdim /* 2756292915Sdim * The IETF working group decided that this is the correct 2757292915Sdim * error return for all non-regular files. 2758292915Sdim */ 2759292915Sdim nd->nd_repstat = NFSERR_SYMLINK; 2760292915Sdim } 2761292915Sdim if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_WRITEACCESS)) 2762292915Sdim nd->nd_repstat = nfsvno_accchk(vp, VWRITE, nd->nd_cred, 2763292915Sdim exp, p, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL); 2764292915Sdim if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_READACCESS)) { 2765292915Sdim nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred, 2766292915Sdim exp, p, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, NULL); 2767292915Sdim if (nd->nd_repstat) 2768292915Sdim nd->nd_repstat = nfsvno_accchk(vp, VEXEC, 2769292915Sdim nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER, 2770292915Sdim NFSACCCHK_VPISLOCKED, NULL); 2771292915Sdim } 2772292915Sdim 2773292915Sdim if (!nd->nd_repstat) { 2774292915Sdim nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1); 2775292915Sdim if (!nd->nd_repstat) { 2776292915Sdim tverf[0] = nva.na_atime.tv_sec; 2777292915Sdim tverf[1] = nva.na_atime.tv_nsec; 2778292915Sdim } 2779292915Sdim } 2780292915Sdim if (!nd->nd_repstat && exclusive_flag && (cverf[0] != tverf[0] || 2781292915Sdim cverf[1] != tverf[1])) 2782292915Sdim nd->nd_repstat = EEXIST; 2783292915Sdim /* 2784292915Sdim * Do the open locking/delegation stuff. 2785292915Sdim */ 2786292915Sdim if (!nd->nd_repstat) 2787292915Sdim nd->nd_repstat = nfsrv_openctrl(nd, vp, &stp, clientid, &stateid, 2788292915Sdim &delegstateid, &rflags, exp, p, nva.na_filerev); 2789292915Sdim 2790292915Sdim /* 2791 * vp must be unlocked before the call to nfsvno_getattr(dirp,...) 2792 * below, to avoid a deadlock with the lookup in nfsvno_namei() above. 2793 * (ie: Leave the NFSVOPUNLOCK() about here.) 2794 */ 2795 if (vp) 2796 NFSVOPUNLOCK(vp, 0); 2797 if (stp) 2798 FREE((caddr_t)stp, M_NFSDSTATE); 2799 if (!nd->nd_repstat && dirp) 2800 nd->nd_repstat = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p, 2801 0); 2802 if (!nd->nd_repstat) { 2803 NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED); 2804 *tl++ = txdr_unsigned(stateid.seqid); 2805 NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER); 2806 tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED); 2807 if (claim == NFSV4OPEN_CLAIMPREVIOUS) { 2808 *tl++ = newnfs_true; 2809 *tl++ = 0; 2810 *tl++ = 0; 2811 *tl++ = 0; 2812 *tl++ = 0; 2813 } else { 2814 *tl++ = newnfs_false; /* Since dirp is not locked */ 2815 txdr_hyper(dirfor.na_filerev, tl); 2816 tl += 2; 2817 txdr_hyper(diraft.na_filerev, tl); 2818 tl += 2; 2819 } 2820 *tl = txdr_unsigned(rflags & NFSV4OPEN_RFLAGS); 2821 (void) nfsrv_putattrbit(nd, &attrbits); 2822 NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); 2823 if (rflags & NFSV4OPEN_READDELEGATE) 2824 *tl = txdr_unsigned(NFSV4OPEN_DELEGATEREAD); 2825 else if (rflags & NFSV4OPEN_WRITEDELEGATE) 2826 *tl = txdr_unsigned(NFSV4OPEN_DELEGATEWRITE); 2827 else 2828 *tl = txdr_unsigned(NFSV4OPEN_DELEGATENONE); 2829 if (rflags & (NFSV4OPEN_READDELEGATE|NFSV4OPEN_WRITEDELEGATE)) { 2830 NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID+NFSX_UNSIGNED); 2831 *tl++ = txdr_unsigned(delegstateid.seqid); 2832 NFSBCOPY((caddr_t)delegstateid.other, (caddr_t)tl, 2833 NFSX_STATEIDOTHER); 2834 tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED); 2835 if (rflags & NFSV4OPEN_RECALL) 2836 *tl = newnfs_true; 2837 else 2838 *tl = newnfs_false; 2839 if (rflags & NFSV4OPEN_WRITEDELEGATE) { 2840 NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED); 2841 *tl++ = txdr_unsigned(NFSV4OPEN_LIMITSIZE); 2842 txdr_hyper(nva.na_size, tl); 2843 } 2844 NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED); 2845 *tl++ = txdr_unsigned(NFSV4ACE_ALLOWEDTYPE); 2846 *tl++ = txdr_unsigned(0x0); 2847 acemask = NFSV4ACE_ALLFILESMASK; 2848 if (nva.na_mode & S_IRUSR) 2849 acemask |= NFSV4ACE_READMASK; 2850 if (nva.na_mode & S_IWUSR) 2851 acemask |= NFSV4ACE_WRITEMASK; 2852 if (nva.na_mode & S_IXUSR) 2853 acemask |= NFSV4ACE_EXECUTEMASK; 2854 *tl = txdr_unsigned(acemask); 2855 (void) nfsm_strtom(nd, "OWNER@", 6); 2856 } 2857 *vpp = vp; 2858 } else if (vp) { 2859 vrele(vp); 2860 } 2861 if (dirp) 2862 vrele(dirp); 2863#ifdef NFS4_ACL_EXTATTR_NAME 2864 acl_free(aclp); 2865#endif 2866 NFSEXITCODE2(0, nd); 2867 return (0); 2868nfsmout: 2869 vrele(dp); 2870#ifdef NFS4_ACL_EXTATTR_NAME 2871 acl_free(aclp); 2872#endif 2873 if (stp) 2874 FREE((caddr_t)stp, M_NFSDSTATE); 2875 NFSEXITCODE2(error, nd); 2876 return (error); 2877} 2878 2879/* 2880 * nfsv4 close service 2881 */ 2882APPLESTATIC int 2883nfsrvd_close(struct nfsrv_descript *nd, __unused int isdgram, 2884 vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) 2885{ 2886 u_int32_t *tl; 2887 struct nfsstate st, *stp = &st; 2888 int error = 0; 2889 nfsv4stateid_t stateid; 2890 nfsquad_t clientid; 2891 2892 NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID); 2893 stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++); 2894 stp->ls_ownerlen = 0; 2895 stp->ls_op = nd->nd_rp; 2896 stp->ls_uid = nd->nd_cred->cr_uid; 2897 stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++); 2898 NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other, 2899 NFSX_STATEIDOTHER); 2900 stp->ls_flags = NFSLCK_CLOSE; 2901 clientid.lval[0] = stp->ls_stateid.other[0]; 2902 clientid.lval[1] = stp->ls_stateid.other[1]; 2903 if (nd->nd_flag & ND_IMPLIEDCLID) { 2904 if (nd->nd_clientid.qval != clientid.qval) 2905 printf("EEK! multiple clids\n"); 2906 } else { 2907 nd->nd_flag |= ND_IMPLIEDCLID; 2908 nd->nd_clientid.qval = clientid.qval; 2909 } 2910 nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p); 2911 vput(vp); 2912 if (!nd->nd_repstat) { 2913 NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID); 2914 *tl++ = txdr_unsigned(stateid.seqid); 2915 NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER); 2916 } 2917 NFSEXITCODE2(0, nd); 2918 return (0); 2919nfsmout: 2920 vput(vp); 2921 NFSEXITCODE2(error, nd); 2922 return (error); 2923} 2924 2925/* 2926 * nfsv4 delegpurge service 2927 */ 2928APPLESTATIC int 2929nfsrvd_delegpurge(struct nfsrv_descript *nd, __unused int isdgram, 2930 __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp) 2931{ 2932 u_int32_t *tl; 2933 int error = 0; 2934 nfsquad_t clientid; 2935 2936 if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { 2937 nd->nd_repstat = NFSERR_WRONGSEC; 2938 goto nfsmout; 2939 } 2940 NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED); 2941 clientid.lval[0] = *tl++; 2942 clientid.lval[1] = *tl; 2943 if (nd->nd_flag & ND_IMPLIEDCLID) { 2944 if (nd->nd_clientid.qval != clientid.qval) 2945 printf("EEK! multiple clids\n"); 2946 } else { 2947 nd->nd_flag |= ND_IMPLIEDCLID; 2948 nd->nd_clientid.qval = clientid.qval; 2949 } 2950 nd->nd_repstat = nfsrv_delegupdate(clientid, NULL, NULL, 2951 NFSV4OP_DELEGPURGE, nd->nd_cred, p); 2952nfsmout: 2953 NFSEXITCODE2(error, nd); 2954 return (error); 2955} 2956 2957/* 2958 * nfsv4 delegreturn service 2959 */ 2960APPLESTATIC int 2961nfsrvd_delegreturn(struct nfsrv_descript *nd, __unused int isdgram, 2962 vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) 2963{ 2964 u_int32_t *tl; 2965 int error = 0; 2966 nfsv4stateid_t stateid; 2967 nfsquad_t clientid; 2968 2969 NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID); 2970 stateid.seqid = fxdr_unsigned(u_int32_t, *tl++); 2971 NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, NFSX_STATEIDOTHER); 2972 clientid.lval[0] = stateid.other[0]; 2973 clientid.lval[1] = stateid.other[1]; 2974 if (nd->nd_flag & ND_IMPLIEDCLID) { 2975 if (nd->nd_clientid.qval != clientid.qval) 2976 printf("EEK! multiple clids\n"); 2977 } else { 2978 nd->nd_flag |= ND_IMPLIEDCLID; 2979 nd->nd_clientid.qval = clientid.qval; 2980 } 2981 nd->nd_repstat = nfsrv_delegupdate(clientid, &stateid, vp, 2982 NFSV4OP_DELEGRETURN, nd->nd_cred, p); 2983nfsmout: 2984 vput(vp); 2985 NFSEXITCODE2(error, nd); 2986 return (error); 2987} 2988 2989/* 2990 * nfsv4 get file handle service 2991 */ 2992APPLESTATIC int 2993nfsrvd_getfh(struct nfsrv_descript *nd, __unused int isdgram, 2994 vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) 2995{ 2996 fhandle_t fh; 2997 2998 nd->nd_repstat = nfsvno_getfh(vp, &fh, p); 2999 vput(vp); 3000 if (!nd->nd_repstat) 3001 (void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0); 3002 NFSEXITCODE2(0, nd); 3003 return (0); 3004} 3005 3006/* 3007 * nfsv4 open confirm service 3008 */ 3009APPLESTATIC int 3010nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram, 3011 vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) 3012{ 3013 u_int32_t *tl; 3014 struct nfsstate st, *stp = &st; 3015 int error = 0; 3016 nfsv4stateid_t stateid; 3017 nfsquad_t clientid; 3018 3019 NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED); 3020 stp->ls_ownerlen = 0; 3021 stp->ls_op = nd->nd_rp; 3022 stp->ls_uid = nd->nd_cred->cr_uid; 3023 stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++); 3024 NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other, 3025 NFSX_STATEIDOTHER); 3026 tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED); 3027 stp->ls_seq = fxdr_unsigned(u_int32_t, *tl); 3028 stp->ls_flags = NFSLCK_CONFIRM; 3029 clientid.lval[0] = stp->ls_stateid.other[0]; 3030 clientid.lval[1] = stp->ls_stateid.other[1]; 3031 if (nd->nd_flag & ND_IMPLIEDCLID) { 3032 if (nd->nd_clientid.qval != clientid.qval) 3033 printf("EEK! multiple clids\n"); 3034 } else { 3035 nd->nd_flag |= ND_IMPLIEDCLID; 3036 nd->nd_clientid.qval = clientid.qval; 3037 } 3038 nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p); 3039 if (!nd->nd_repstat) { 3040 NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID); 3041 *tl++ = txdr_unsigned(stateid.seqid); 3042 NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER); 3043 } 3044nfsmout: 3045 vput(vp); 3046 NFSEXITCODE2(error, nd); 3047 return (error); 3048} 3049 3050/* 3051 * nfsv4 open downgrade service 3052 */ 3053APPLESTATIC int 3054nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram, 3055 vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) 3056{ 3057 u_int32_t *tl; 3058 int i; 3059 struct nfsstate st, *stp = &st; 3060 int error = 0; 3061 nfsv4stateid_t stateid; 3062 nfsquad_t clientid; 3063 3064 NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED); 3065 stp->ls_ownerlen = 0; 3066 stp->ls_op = nd->nd_rp; 3067 stp->ls_uid = nd->nd_cred->cr_uid; 3068 stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++); 3069 NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other, 3070 NFSX_STATEIDOTHER); 3071 tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED); 3072 stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++); 3073 i = fxdr_unsigned(int, *tl++); 3074 switch (i) { 3075 case NFSV4OPEN_ACCESSREAD: 3076 stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_DOWNGRADE); 3077 break; 3078 case NFSV4OPEN_ACCESSWRITE: 3079 stp->ls_flags = (NFSLCK_WRITEACCESS | NFSLCK_DOWNGRADE); 3080 break; 3081 case NFSV4OPEN_ACCESSBOTH: 3082 stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_WRITEACCESS | 3083 NFSLCK_DOWNGRADE); 3084 break; 3085 default: 3086 nd->nd_repstat = NFSERR_BADXDR; 3087 }; 3088 i = fxdr_unsigned(int, *tl); 3089 switch (i) { 3090 case NFSV4OPEN_DENYNONE: 3091 break; 3092 case NFSV4OPEN_DENYREAD: 3093 stp->ls_flags |= NFSLCK_READDENY; 3094 break; 3095 case NFSV4OPEN_DENYWRITE: 3096 stp->ls_flags |= NFSLCK_WRITEDENY; 3097 break; 3098 case NFSV4OPEN_DENYBOTH: 3099 stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY); 3100 break; 3101 default: 3102 nd->nd_repstat = NFSERR_BADXDR; 3103 }; 3104 3105 clientid.lval[0] = stp->ls_stateid.other[0]; 3106 clientid.lval[1] = stp->ls_stateid.other[1]; 3107 if (nd->nd_flag & ND_IMPLIEDCLID) { 3108 if (nd->nd_clientid.qval != clientid.qval) 3109 printf("EEK! multiple clids\n"); 3110 } else { 3111 nd->nd_flag |= ND_IMPLIEDCLID; 3112 nd->nd_clientid.qval = clientid.qval; 3113 } 3114 if (!nd->nd_repstat) 3115 nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, 3116 nd, p); 3117 if (!nd->nd_repstat) { 3118 NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID); 3119 *tl++ = txdr_unsigned(stateid.seqid); 3120 NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER); 3121 } 3122nfsmout: 3123 vput(vp); 3124 NFSEXITCODE2(error, nd); 3125 return (error); 3126} 3127 3128/* 3129 * nfsv4 renew lease service 3130 */ 3131APPLESTATIC int 3132nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram, 3133 __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) 3134{ 3135 u_int32_t *tl; 3136 int error = 0; 3137 nfsquad_t clientid; 3138 3139 if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { 3140 nd->nd_repstat = NFSERR_WRONGSEC; 3141 goto nfsmout; 3142 } 3143 NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER); 3144 clientid.lval[0] = *tl++; 3145 clientid.lval[1] = *tl; 3146 if (nd->nd_flag & ND_IMPLIEDCLID) { 3147 if (nd->nd_clientid.qval != clientid.qval) 3148 printf("EEK! multiple clids\n"); 3149 } else { 3150 nd->nd_flag |= ND_IMPLIEDCLID; 3151 nd->nd_clientid.qval = clientid.qval; 3152 } 3153 nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_RENEWOP|CLOPS_RENEW), 3154 NULL, (nfsquad_t)((u_quad_t)0), nd, p); 3155nfsmout: 3156 NFSEXITCODE2(error, nd); 3157 return (error); 3158} 3159 3160/* 3161 * nfsv4 security info service 3162 */ 3163APPLESTATIC int 3164nfsrvd_secinfo(struct nfsrv_descript *nd, int isdgram, 3165 vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp) 3166{ 3167 u_int32_t *tl; 3168 int len; 3169 struct nameidata named; 3170 vnode_t dirp = NULL, vp; 3171 struct nfsrvfh fh; 3172 struct nfsexstuff retnes; 3173 u_int32_t *sizp; 3174 int error = 0, savflag, i; 3175 char *bufp; 3176 u_long *hashp; 3177 3178 /* 3179 * All this just to get the export flags for the name. 3180 */ 3181 NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP, 3182 LOCKLEAF | SAVESTART); 3183 nfsvno_setpathbuf(&named, &bufp, &hashp); 3184 error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen); 3185 if (error) { 3186 vput(dp); 3187 nfsvno_relpathbuf(&named); 3188 goto out; 3189 } 3190 if (!nd->nd_repstat) { 3191 nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp); 3192 } else { 3193 vput(dp); 3194 nfsvno_relpathbuf(&named); 3195 } 3196 if (dirp) 3197 vrele(dirp); 3198 if (nd->nd_repstat) 3199 goto out; 3200 vrele(named.ni_startdir); 3201 nfsvno_relpathbuf(&named); 3202 fh.nfsrvfh_len = NFSX_MYFH; 3203 vp = named.ni_vp; 3204 nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p); 3205 vput(vp); 3206 savflag = nd->nd_flag; 3207 if (!nd->nd_repstat) { 3208 nfsd_fhtovp(nd, &fh, LK_SHARED, &vp, &retnes, NULL, 0, p); 3209 if (vp) 3210 vput(vp); 3211 } 3212 nd->nd_flag = savflag; 3213 if (nd->nd_repstat) 3214 goto out; 3215 3216 /* 3217 * Finally have the export flags for name, so we can create 3218 * the security info. 3219 */ 3220 len = 0; 3221 NFSM_BUILD(sizp, u_int32_t *, NFSX_UNSIGNED); 3222 for (i = 0; i < retnes.nes_numsecflavor; i++) { 3223 if (retnes.nes_secflavors[i] == AUTH_SYS) { 3224 NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); 3225 *tl = txdr_unsigned(RPCAUTH_UNIX); 3226 len++; 3227 } else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5) { 3228 NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); 3229 *tl++ = txdr_unsigned(RPCAUTH_GSS); 3230 (void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str, 3231 nfsgss_mechlist[KERBV_MECH].len); 3232 NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED); 3233 *tl++ = txdr_unsigned(GSS_KERBV_QOP); 3234 *tl = txdr_unsigned(RPCAUTHGSS_SVCNONE); 3235 len++; 3236 } else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5I) { 3237 NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); 3238 *tl++ = txdr_unsigned(RPCAUTH_GSS); 3239 (void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str, 3240 nfsgss_mechlist[KERBV_MECH].len); 3241 NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED); 3242 *tl++ = txdr_unsigned(GSS_KERBV_QOP); 3243 *tl = txdr_unsigned(RPCAUTHGSS_SVCINTEGRITY); 3244 len++; 3245 } else if (retnes.nes_secflavors[i] == RPCSEC_GSS_KRB5P) { 3246 NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED); 3247 *tl++ = txdr_unsigned(RPCAUTH_GSS); 3248 (void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str, 3249 nfsgss_mechlist[KERBV_MECH].len); 3250 NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED); 3251 *tl++ = txdr_unsigned(GSS_KERBV_QOP); 3252 *tl = txdr_unsigned(RPCAUTHGSS_SVCPRIVACY); 3253 len++; 3254 } 3255 } 3256 *sizp = txdr_unsigned(len); 3257 3258out: 3259 NFSEXITCODE2(error, nd); 3260 return (error); 3261} 3262 3263/* 3264 * nfsv4 set client id service 3265 */ 3266APPLESTATIC int 3267nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram, 3268 __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) 3269{ 3270 u_int32_t *tl; 3271 int i; 3272 int error = 0, idlen; 3273 struct nfsclient *clp = NULL; 3274 struct sockaddr_in *rad; 3275 u_char *verf, *ucp, *ucp2, addrbuf[24]; 3276 nfsquad_t clientid, confirm; 3277 3278 if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { 3279 nd->nd_repstat = NFSERR_WRONGSEC; 3280 goto out; 3281 } 3282 NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED); 3283 verf = (u_char *)tl; 3284 tl += (NFSX_VERF / NFSX_UNSIGNED); 3285 i = fxdr_unsigned(int, *tl); 3286 if (i > NFSV4_OPAQUELIMIT || i <= 0) { 3287 nd->nd_repstat = NFSERR_BADXDR; 3288 goto nfsmout; 3289 } 3290 idlen = i; 3291 if (nd->nd_flag & ND_GSS) 3292 i += nd->nd_princlen; 3293 MALLOC(clp, struct nfsclient *, sizeof (struct nfsclient) + i, 3294 M_NFSDCLIENT, M_WAITOK); 3295 NFSBZERO((caddr_t)clp, sizeof (struct nfsclient) + i); 3296 NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx); 3297 NFSSOCKADDRALLOC(clp->lc_req.nr_nam); 3298 NFSSOCKADDRSIZE(clp->lc_req.nr_nam, sizeof (struct sockaddr_in)); 3299 clp->lc_req.nr_cred = NULL; 3300 NFSBCOPY(verf, clp->lc_verf, NFSX_VERF); 3301 clp->lc_idlen = idlen; 3302 error = nfsrv_mtostr(nd, clp->lc_id, idlen); 3303 if (error) 3304 goto nfsmout; 3305 if (nd->nd_flag & ND_GSS) { 3306 clp->lc_flags = LCL_GSS; 3307 if (nd->nd_flag & ND_GSSINTEGRITY) 3308 clp->lc_flags |= LCL_GSSINTEGRITY; 3309 else if (nd->nd_flag & ND_GSSPRIVACY) 3310 clp->lc_flags |= LCL_GSSPRIVACY; 3311 } else { 3312 clp->lc_flags = 0; 3313 } 3314 if ((nd->nd_flag & ND_GSS) && nd->nd_princlen > 0) { 3315 clp->lc_flags |= LCL_NAME; 3316 clp->lc_namelen = nd->nd_princlen; 3317 clp->lc_name = &clp->lc_id[idlen]; 3318 NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen); 3319 } else { 3320 clp->lc_uid = nd->nd_cred->cr_uid; 3321 clp->lc_gid = nd->nd_cred->cr_gid; 3322 } 3323 NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 3324 clp->lc_program = fxdr_unsigned(u_int32_t, *tl); 3325 error = nfsrv_getclientipaddr(nd, clp); 3326 if (error) 3327 goto nfsmout; 3328 NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 3329 clp->lc_callback = fxdr_unsigned(u_int32_t, *tl); 3330 3331 /* 3332 * nfsrv_setclient() does the actual work of adding it to the 3333 * client list. If there is no error, the structure has been 3334 * linked into the client list and clp should no longer be used 3335 * here. When an error is returned, it has not been linked in, 3336 * so it should be free'd. 3337 */ 3338 nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p); 3339 if (nd->nd_repstat == NFSERR_CLIDINUSE) { 3340 if (clp->lc_flags & LCL_TCPCALLBACK) 3341 (void) nfsm_strtom(nd, "tcp", 3); 3342 else 3343 (void) nfsm_strtom(nd, "udp", 3); 3344 rad = NFSSOCKADDR(clp->lc_req.nr_nam, struct sockaddr_in *); 3345 ucp = (u_char *)&rad->sin_addr.s_addr; 3346 ucp2 = (u_char *)&rad->sin_port; 3347 sprintf(addrbuf, "%d.%d.%d.%d.%d.%d", ucp[0] & 0xff, 3348 ucp[1] & 0xff, ucp[2] & 0xff, ucp[3] & 0xff, 3349 ucp2[0] & 0xff, ucp2[1] & 0xff); 3350 (void) nfsm_strtom(nd, addrbuf, strlen(addrbuf)); 3351 } 3352 if (clp) { 3353 NFSSOCKADDRFREE(clp->lc_req.nr_nam); 3354 NFSFREEMUTEX(&clp->lc_req.nr_mtx); 3355 free((caddr_t)clp, M_NFSDCLIENT); 3356 } 3357 if (!nd->nd_repstat) { 3358 NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_HYPER); 3359 *tl++ = clientid.lval[0]; 3360 *tl++ = clientid.lval[1]; 3361 *tl++ = confirm.lval[0]; 3362 *tl = confirm.lval[1]; 3363 } 3364 3365out: 3366 NFSEXITCODE2(0, nd); 3367 return (0); 3368nfsmout: 3369 if (clp) { 3370 NFSSOCKADDRFREE(clp->lc_req.nr_nam); 3371 NFSFREEMUTEX(&clp->lc_req.nr_mtx); 3372 free((caddr_t)clp, M_NFSDCLIENT); 3373 } 3374 NFSEXITCODE2(error, nd); 3375 return (error); 3376} 3377 3378/* 3379 * nfsv4 set client id confirm service 3380 */ 3381APPLESTATIC int 3382nfsrvd_setclientidcfrm(struct nfsrv_descript *nd, 3383 __unused int isdgram, __unused vnode_t vp, NFSPROC_T *p, 3384 __unused struct nfsexstuff *exp) 3385{ 3386 u_int32_t *tl; 3387 int error = 0; 3388 nfsquad_t clientid, confirm; 3389 3390 if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { 3391 nd->nd_repstat = NFSERR_WRONGSEC; 3392 goto nfsmout; 3393 } 3394 NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER); 3395 clientid.lval[0] = *tl++; 3396 clientid.lval[1] = *tl++; 3397 confirm.lval[0] = *tl++; 3398 confirm.lval[1] = *tl; 3399 3400 /* 3401 * nfsrv_getclient() searches the client list for a match and 3402 * returns the appropriate NFSERR status. 3403 */ 3404 nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_CONFIRM|CLOPS_RENEW), 3405 NULL, confirm, nd, p); 3406nfsmout: 3407 NFSEXITCODE2(error, nd); 3408 return (error); 3409} 3410 3411/* 3412 * nfsv4 verify service 3413 */ 3414APPLESTATIC int 3415nfsrvd_verify(struct nfsrv_descript *nd, int isdgram, 3416 vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) 3417{ 3418 int error = 0, ret, fhsize = NFSX_MYFH; 3419 struct nfsvattr nva; 3420 struct statfs sf; 3421 struct nfsfsinfo fs; 3422 fhandle_t fh; 3423 3424 nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p, 1); 3425 if (!nd->nd_repstat) 3426 nd->nd_repstat = nfsvno_statfs(vp, &sf); 3427 if (!nd->nd_repstat) 3428 nd->nd_repstat = nfsvno_getfh(vp, &fh, p); 3429 if (!nd->nd_repstat) { 3430 nfsvno_getfs(&fs, isdgram); 3431 error = nfsv4_loadattr(nd, vp, &nva, NULL, &fh, fhsize, NULL, 3432 &sf, NULL, &fs, NULL, 1, &ret, NULL, NULL, p, nd->nd_cred); 3433 if (!error) { 3434 if (nd->nd_procnum == NFSV4OP_NVERIFY) { 3435 if (ret == 0) 3436 nd->nd_repstat = NFSERR_SAME; 3437 else if (ret != NFSERR_NOTSAME) 3438 nd->nd_repstat = ret; 3439 } else if (ret) 3440 nd->nd_repstat = ret; 3441 } 3442 } 3443 vput(vp); 3444 NFSEXITCODE2(error, nd); 3445 return (error); 3446} 3447 3448/* 3449 * nfs openattr rpc 3450 */ 3451APPLESTATIC int 3452nfsrvd_openattr(struct nfsrv_descript *nd, __unused int isdgram, 3453 vnode_t dp, __unused vnode_t *vpp, __unused fhandle_t *fhp, 3454 __unused NFSPROC_T *p, __unused struct nfsexstuff *exp) 3455{ 3456 u_int32_t *tl; 3457 int error = 0, createdir; 3458 3459 NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); 3460 createdir = fxdr_unsigned(int, *tl); 3461 nd->nd_repstat = NFSERR_NOTSUPP; 3462nfsmout: 3463 vrele(dp); 3464 NFSEXITCODE2(error, nd); 3465 return (error); 3466} 3467 3468/* 3469 * nfsv4 release lock owner service 3470 */ 3471APPLESTATIC int 3472nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram, 3473 __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp) 3474{ 3475 u_int32_t *tl; 3476 struct nfsstate *stp = NULL; 3477 int error = 0, len; 3478 nfsquad_t clientid; 3479 3480 if (nfs_rootfhset == 0 || nfsd_checkrootexp(nd) != 0) { 3481 nd->nd_repstat = NFSERR_WRONGSEC; 3482 goto nfsmout; 3483 } 3484 NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED); 3485 len = fxdr_unsigned(int, *(tl + 2)); 3486 if (len <= 0 || len > NFSV4_OPAQUELIMIT) { 3487 nd->nd_repstat = NFSERR_BADXDR; 3488 goto nfsmout; 3489 } 3490 MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + len, 3491 M_NFSDSTATE, M_WAITOK); 3492 stp->ls_ownerlen = len; 3493 stp->ls_op = NULL; 3494 stp->ls_flags = NFSLCK_RELEASE; 3495 stp->ls_uid = nd->nd_cred->cr_uid; 3496 clientid.lval[0] = *tl++; 3497 clientid.lval[1] = *tl; 3498 if (nd->nd_flag & ND_IMPLIEDCLID) { 3499 if (nd->nd_clientid.qval != clientid.qval) 3500 printf("EEK! multiple clids\n"); 3501 } else { 3502 nd->nd_flag |= ND_IMPLIEDCLID; 3503 nd->nd_clientid.qval = clientid.qval; 3504 } 3505 error = nfsrv_mtostr(nd, stp->ls_owner, len); 3506 if (error) 3507 goto nfsmout; 3508 nd->nd_repstat = nfsrv_releaselckown(stp, clientid, p); 3509 FREE((caddr_t)stp, M_NFSDSTATE); 3510 3511 NFSEXITCODE2(0, nd); 3512 return (0); 3513nfsmout: 3514 if (stp) 3515 free((caddr_t)stp, M_NFSDSTATE); 3516 NFSEXITCODE2(error, nd); 3517 return (error); 3518} 3519