xref: /onnv-gate/usr/src/uts/common/fs/nfs/nfs_srv.c (revision 13002:ce7d2a030660)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51610Sthurlow  * Common Development and Distribution License (the "License").
61610Sthurlow  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*13002SKaren.Rochford@Sun.COM  * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate /*
260Sstevel@tonic-gate  *	Copyright (c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
270Sstevel@tonic-gate  *	All rights reserved.
280Sstevel@tonic-gate  */
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #include <sys/param.h>
310Sstevel@tonic-gate #include <sys/types.h>
320Sstevel@tonic-gate #include <sys/systm.h>
330Sstevel@tonic-gate #include <sys/cred.h>
340Sstevel@tonic-gate #include <sys/buf.h>
350Sstevel@tonic-gate #include <sys/vfs.h>
360Sstevel@tonic-gate #include <sys/vnode.h>
370Sstevel@tonic-gate #include <sys/uio.h>
380Sstevel@tonic-gate #include <sys/stat.h>
390Sstevel@tonic-gate #include <sys/errno.h>
400Sstevel@tonic-gate #include <sys/sysmacros.h>
410Sstevel@tonic-gate #include <sys/statvfs.h>
420Sstevel@tonic-gate #include <sys/kmem.h>
430Sstevel@tonic-gate #include <sys/kstat.h>
440Sstevel@tonic-gate #include <sys/dirent.h>
450Sstevel@tonic-gate #include <sys/cmn_err.h>
460Sstevel@tonic-gate #include <sys/debug.h>
470Sstevel@tonic-gate #include <sys/vtrace.h>
480Sstevel@tonic-gate #include <sys/mode.h>
490Sstevel@tonic-gate #include <sys/acl.h>
500Sstevel@tonic-gate #include <sys/nbmlock.h>
510Sstevel@tonic-gate #include <sys/policy.h>
527387SRobert.Gordon@Sun.COM #include <sys/sdt.h>
530Sstevel@tonic-gate 
540Sstevel@tonic-gate #include <rpc/types.h>
550Sstevel@tonic-gate #include <rpc/auth.h>
560Sstevel@tonic-gate #include <rpc/svc.h>
570Sstevel@tonic-gate 
580Sstevel@tonic-gate #include <nfs/nfs.h>
590Sstevel@tonic-gate #include <nfs/export.h>
607961SNatalie.Li@Sun.COM #include <nfs/nfs_cmd.h>
610Sstevel@tonic-gate 
620Sstevel@tonic-gate #include <vm/hat.h>
630Sstevel@tonic-gate #include <vm/as.h>
640Sstevel@tonic-gate #include <vm/seg.h>
650Sstevel@tonic-gate #include <vm/seg_map.h>
660Sstevel@tonic-gate #include <vm/seg_kmem.h>
670Sstevel@tonic-gate 
680Sstevel@tonic-gate #include <sys/strsubr.h>
690Sstevel@tonic-gate 
700Sstevel@tonic-gate /*
710Sstevel@tonic-gate  * These are the interface routines for the server side of the
720Sstevel@tonic-gate  * Network File System.  See the NFS version 2 protocol specification
730Sstevel@tonic-gate  * for a description of this interface.
740Sstevel@tonic-gate  */
750Sstevel@tonic-gate 
760Sstevel@tonic-gate static int	sattr_to_vattr(struct nfssattr *, struct vattr *);
770Sstevel@tonic-gate static void	acl_perm(struct vnode *, struct exportinfo *, struct vattr *,
780Sstevel@tonic-gate 			cred_t *);
790Sstevel@tonic-gate 
800Sstevel@tonic-gate /*
810Sstevel@tonic-gate  * Some "over the wire" UNIX file types.  These are encoded
820Sstevel@tonic-gate  * into the mode.  This needs to be fixed in the next rev.
830Sstevel@tonic-gate  */
840Sstevel@tonic-gate #define	IFMT		0170000		/* type of file */
850Sstevel@tonic-gate #define	IFCHR		0020000		/* character special */
860Sstevel@tonic-gate #define	IFBLK		0060000		/* block special */
870Sstevel@tonic-gate #define	IFSOCK		0140000		/* socket */
880Sstevel@tonic-gate 
895599Sjwahlig u_longlong_t nfs2_srv_caller_id;
905599Sjwahlig 
910Sstevel@tonic-gate /*
920Sstevel@tonic-gate  * Get file attributes.
930Sstevel@tonic-gate  * Returns the current attributes of the file with the given fhandle.
940Sstevel@tonic-gate  */
950Sstevel@tonic-gate /* ARGSUSED */
960Sstevel@tonic-gate void
rfs_getattr(fhandle_t * fhp,struct nfsattrstat * ns,struct exportinfo * exi,struct svc_req * req,cred_t * cr)970Sstevel@tonic-gate rfs_getattr(fhandle_t *fhp, struct nfsattrstat *ns, struct exportinfo *exi,
980Sstevel@tonic-gate 	struct svc_req *req, cred_t *cr)
990Sstevel@tonic-gate {
1000Sstevel@tonic-gate 	int error;
1010Sstevel@tonic-gate 	vnode_t *vp;
1020Sstevel@tonic-gate 	struct vattr va;
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate 	vp = nfs_fhtovp(fhp, exi);
1050Sstevel@tonic-gate 	if (vp == NULL) {
1060Sstevel@tonic-gate 		ns->ns_status = NFSERR_STALE;
1070Sstevel@tonic-gate 		return;
1080Sstevel@tonic-gate 	}
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 	/*
1110Sstevel@tonic-gate 	 * Do the getattr.
1120Sstevel@tonic-gate 	 */
1130Sstevel@tonic-gate 	va.va_mask = AT_ALL;	/* we want all the attributes */
1147387SRobert.Gordon@Sun.COM 
1150Sstevel@tonic-gate 	error = rfs4_delegated_getattr(vp, &va, 0, cr);
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate 	/* check for overflows */
1180Sstevel@tonic-gate 	if (!error) {
11911291SRobert.Thurlow@Sun.COM 		/* Lie about the object type for a referral */
12011291SRobert.Thurlow@Sun.COM 		if (vn_is_nfs_reparse(vp, cr))
12111291SRobert.Thurlow@Sun.COM 			va.va_type = VLNK;
12211291SRobert.Thurlow@Sun.COM 
1230Sstevel@tonic-gate 		acl_perm(vp, exi, &va, cr);
1240Sstevel@tonic-gate 		error = vattr_to_nattr(&va, &ns->ns_attr);
1250Sstevel@tonic-gate 	}
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate 	VN_RELE(vp);
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate 	ns->ns_status = puterrno(error);
1300Sstevel@tonic-gate }
1311610Sthurlow void *
rfs_getattr_getfh(fhandle_t * fhp)1320Sstevel@tonic-gate rfs_getattr_getfh(fhandle_t *fhp)
1330Sstevel@tonic-gate {
1340Sstevel@tonic-gate 	return (fhp);
1350Sstevel@tonic-gate }
1360Sstevel@tonic-gate 
1370Sstevel@tonic-gate /*
1380Sstevel@tonic-gate  * Set file attributes.
1390Sstevel@tonic-gate  * Sets the attributes of the file with the given fhandle.  Returns
1400Sstevel@tonic-gate  * the new attributes.
1410Sstevel@tonic-gate  */
1420Sstevel@tonic-gate void
rfs_setattr(struct nfssaargs * args,struct nfsattrstat * ns,struct exportinfo * exi,struct svc_req * req,cred_t * cr)1430Sstevel@tonic-gate rfs_setattr(struct nfssaargs *args, struct nfsattrstat *ns,
1440Sstevel@tonic-gate 	struct exportinfo *exi, struct svc_req *req, cred_t *cr)
1450Sstevel@tonic-gate {
1460Sstevel@tonic-gate 	int error;
1470Sstevel@tonic-gate 	int flag;
1480Sstevel@tonic-gate 	int in_crit = 0;
1490Sstevel@tonic-gate 	vnode_t *vp;
1500Sstevel@tonic-gate 	struct vattr va;
1510Sstevel@tonic-gate 	struct vattr bva;
1520Sstevel@tonic-gate 	struct flock64 bf;
1535599Sjwahlig 	caller_context_t ct;
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate 	vp = nfs_fhtovp(&args->saa_fh, exi);
1570Sstevel@tonic-gate 	if (vp == NULL) {
1580Sstevel@tonic-gate 		ns->ns_status = NFSERR_STALE;
1590Sstevel@tonic-gate 		return;
1600Sstevel@tonic-gate 	}
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate 	if (rdonly(exi, req) || vn_is_readonly(vp)) {
1630Sstevel@tonic-gate 		VN_RELE(vp);
1640Sstevel@tonic-gate 		ns->ns_status = NFSERR_ROFS;
1650Sstevel@tonic-gate 		return;
1660Sstevel@tonic-gate 	}
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate 	error = sattr_to_vattr(&args->saa_sa, &va);
1690Sstevel@tonic-gate 	if (error) {
1700Sstevel@tonic-gate 		VN_RELE(vp);
1710Sstevel@tonic-gate 		ns->ns_status = puterrno(error);
1720Sstevel@tonic-gate 		return;
1730Sstevel@tonic-gate 	}
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate 	/*
1760Sstevel@tonic-gate 	 * If the client is requesting a change to the mtime,
1770Sstevel@tonic-gate 	 * but the nanosecond field is set to 1 billion, then
1780Sstevel@tonic-gate 	 * this is a flag to the server that it should set the
1790Sstevel@tonic-gate 	 * atime and mtime fields to the server's current time.
1800Sstevel@tonic-gate 	 * The 1 billion number actually came from the client
1810Sstevel@tonic-gate 	 * as 1 million, but the units in the over the wire
1820Sstevel@tonic-gate 	 * request are microseconds instead of nanoseconds.
1830Sstevel@tonic-gate 	 *
1840Sstevel@tonic-gate 	 * This is an overload of the protocol and should be
1850Sstevel@tonic-gate 	 * documented in the NFS Version 2 protocol specification.
1860Sstevel@tonic-gate 	 */
1870Sstevel@tonic-gate 	if (va.va_mask & AT_MTIME) {
1880Sstevel@tonic-gate 		if (va.va_mtime.tv_nsec == 1000000000) {
1890Sstevel@tonic-gate 			gethrestime(&va.va_mtime);
1900Sstevel@tonic-gate 			va.va_atime = va.va_mtime;
1910Sstevel@tonic-gate 			va.va_mask |= AT_ATIME;
1920Sstevel@tonic-gate 			flag = 0;
1930Sstevel@tonic-gate 		} else
1940Sstevel@tonic-gate 			flag = ATTR_UTIME;
1950Sstevel@tonic-gate 	} else
1960Sstevel@tonic-gate 		flag = 0;
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate 	/*
1990Sstevel@tonic-gate 	 * If the filesystem is exported with nosuid, then mask off
2000Sstevel@tonic-gate 	 * the setuid and setgid bits.
2010Sstevel@tonic-gate 	 */
2020Sstevel@tonic-gate 	if ((va.va_mask & AT_MODE) && vp->v_type == VREG &&
2030Sstevel@tonic-gate 	    (exi->exi_export.ex_flags & EX_NOSUID))
2040Sstevel@tonic-gate 		va.va_mode &= ~(VSUID | VSGID);
2050Sstevel@tonic-gate 
2065599Sjwahlig 	ct.cc_sysid = 0;
2075599Sjwahlig 	ct.cc_pid = 0;
2085599Sjwahlig 	ct.cc_caller_id = nfs2_srv_caller_id;
2095599Sjwahlig 	ct.cc_flags = CC_DONTBLOCK;
2105599Sjwahlig 
2110Sstevel@tonic-gate 	/*
2120Sstevel@tonic-gate 	 * We need to specially handle size changes because it is
2130Sstevel@tonic-gate 	 * possible for the client to create a file with modes
2140Sstevel@tonic-gate 	 * which indicate read-only, but with the file opened for
2150Sstevel@tonic-gate 	 * writing.  If the client then tries to set the size of
2160Sstevel@tonic-gate 	 * the file, then the normal access checking done in
2170Sstevel@tonic-gate 	 * VOP_SETATTR would prevent the client from doing so,
2180Sstevel@tonic-gate 	 * although it should be legal for it to do so.  To get
2190Sstevel@tonic-gate 	 * around this, we do the access checking for ourselves
2200Sstevel@tonic-gate 	 * and then use VOP_SPACE which doesn't do the access
2210Sstevel@tonic-gate 	 * checking which VOP_SETATTR does. VOP_SPACE can only
2220Sstevel@tonic-gate 	 * operate on VREG files, let VOP_SETATTR handle the other
2230Sstevel@tonic-gate 	 * extremely rare cases.
2240Sstevel@tonic-gate 	 * Also the client should not be allowed to change the
2250Sstevel@tonic-gate 	 * size of the file if there is a conflicting non-blocking
2260Sstevel@tonic-gate 	 * mandatory lock in the region of change.
2270Sstevel@tonic-gate 	 */
2280Sstevel@tonic-gate 	if (vp->v_type == VREG && va.va_mask & AT_SIZE) {
2290Sstevel@tonic-gate 		if (nbl_need_check(vp)) {
2300Sstevel@tonic-gate 			nbl_start_crit(vp, RW_READER);
2310Sstevel@tonic-gate 			in_crit = 1;
2320Sstevel@tonic-gate 		}
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 		bva.va_mask = AT_UID | AT_SIZE;
2357387SRobert.Gordon@Sun.COM 
2365599Sjwahlig 		error = VOP_GETATTR(vp, &bva, 0, cr, &ct);
2377387SRobert.Gordon@Sun.COM 
2380Sstevel@tonic-gate 		if (error) {
2390Sstevel@tonic-gate 			if (in_crit)
2400Sstevel@tonic-gate 				nbl_end_crit(vp);
2410Sstevel@tonic-gate 			VN_RELE(vp);
2420Sstevel@tonic-gate 			ns->ns_status = puterrno(error);
2430Sstevel@tonic-gate 			return;
2440Sstevel@tonic-gate 		}
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate 		if (in_crit) {
2470Sstevel@tonic-gate 			u_offset_t offset;
2480Sstevel@tonic-gate 			ssize_t length;
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate 			if (va.va_size < bva.va_size) {
2510Sstevel@tonic-gate 				offset = va.va_size;
2520Sstevel@tonic-gate 				length = bva.va_size - va.va_size;
2530Sstevel@tonic-gate 			} else {
2540Sstevel@tonic-gate 				offset = bva.va_size;
2550Sstevel@tonic-gate 				length = va.va_size - bva.va_size;
2560Sstevel@tonic-gate 			}
2575331Samw 			if (nbl_conflict(vp, NBL_WRITE, offset, length, 0,
2585331Samw 			    NULL)) {
2590Sstevel@tonic-gate 				error = EACCES;
2600Sstevel@tonic-gate 			}
2610Sstevel@tonic-gate 		}
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate 		if (crgetuid(cr) == bva.va_uid && !error &&
2645050Sjwahlig 		    va.va_size != bva.va_size) {
2650Sstevel@tonic-gate 			va.va_mask &= ~AT_SIZE;
2660Sstevel@tonic-gate 			bf.l_type = F_WRLCK;
2670Sstevel@tonic-gate 			bf.l_whence = 0;
2680Sstevel@tonic-gate 			bf.l_start = (off64_t)va.va_size;
2690Sstevel@tonic-gate 			bf.l_len = 0;
2700Sstevel@tonic-gate 			bf.l_sysid = 0;
2710Sstevel@tonic-gate 			bf.l_pid = 0;
2727387SRobert.Gordon@Sun.COM 
2730Sstevel@tonic-gate 			error = VOP_SPACE(vp, F_FREESP, &bf, FWRITE,
2745599Sjwahlig 			    (offset_t)va.va_size, cr, &ct);
2750Sstevel@tonic-gate 		}
2760Sstevel@tonic-gate 		if (in_crit)
2770Sstevel@tonic-gate 			nbl_end_crit(vp);
2780Sstevel@tonic-gate 	} else
2790Sstevel@tonic-gate 		error = 0;
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 	/*
2820Sstevel@tonic-gate 	 * Do the setattr.
2830Sstevel@tonic-gate 	 */
2840Sstevel@tonic-gate 	if (!error && va.va_mask) {
2855599Sjwahlig 		error = VOP_SETATTR(vp, &va, flag, cr, &ct);
2860Sstevel@tonic-gate 	}
2870Sstevel@tonic-gate 
2885599Sjwahlig 	/*
2895599Sjwahlig 	 * check if the monitor on either vop_space or vop_setattr detected
2905599Sjwahlig 	 * a delegation conflict and if so, mark the thread flag as
2915599Sjwahlig 	 * wouldblock so that the response is dropped and the client will
2925599Sjwahlig 	 * try again.
2935599Sjwahlig 	 */
2945599Sjwahlig 	if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK)) {
2955599Sjwahlig 		VN_RELE(vp);
2965599Sjwahlig 		curthread->t_flag |= T_WOULDBLOCK;
2975599Sjwahlig 		return;
2985599Sjwahlig 	}
2995599Sjwahlig 
3000Sstevel@tonic-gate 	if (!error) {
3010Sstevel@tonic-gate 		va.va_mask = AT_ALL;	/* get everything */
3027387SRobert.Gordon@Sun.COM 
3030Sstevel@tonic-gate 		error = rfs4_delegated_getattr(vp, &va, 0, cr);
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate 		/* check for overflows */
3060Sstevel@tonic-gate 		if (!error) {
3070Sstevel@tonic-gate 			acl_perm(vp, exi, &va, cr);
3080Sstevel@tonic-gate 			error = vattr_to_nattr(&va, &ns->ns_attr);
3090Sstevel@tonic-gate 		}
3100Sstevel@tonic-gate 	}
3110Sstevel@tonic-gate 
3125599Sjwahlig 	ct.cc_flags = 0;
3135599Sjwahlig 
3140Sstevel@tonic-gate 	/*
3150Sstevel@tonic-gate 	 * Force modified metadata out to stable storage.
3160Sstevel@tonic-gate 	 */
3175599Sjwahlig 	(void) VOP_FSYNC(vp, FNODSYNC, cr, &ct);
3180Sstevel@tonic-gate 
3190Sstevel@tonic-gate 	VN_RELE(vp);
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	ns->ns_status = puterrno(error);
3220Sstevel@tonic-gate }
3231610Sthurlow void *
rfs_setattr_getfh(struct nfssaargs * args)3240Sstevel@tonic-gate rfs_setattr_getfh(struct nfssaargs *args)
3250Sstevel@tonic-gate {
3260Sstevel@tonic-gate 	return (&args->saa_fh);
3270Sstevel@tonic-gate }
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate /*
3300Sstevel@tonic-gate  * Directory lookup.
3310Sstevel@tonic-gate  * Returns an fhandle and file attributes for file name in a directory.
3320Sstevel@tonic-gate  */
3330Sstevel@tonic-gate /* ARGSUSED */
3340Sstevel@tonic-gate void
rfs_lookup(struct nfsdiropargs * da,struct nfsdiropres * dr,struct exportinfo * exi,struct svc_req * req,cred_t * cr)3350Sstevel@tonic-gate rfs_lookup(struct nfsdiropargs *da, struct nfsdiropres *dr,
3360Sstevel@tonic-gate 	struct exportinfo *exi, struct svc_req *req, cred_t *cr)
3370Sstevel@tonic-gate {
3380Sstevel@tonic-gate 	int error;
3390Sstevel@tonic-gate 	vnode_t *dvp;
3400Sstevel@tonic-gate 	vnode_t *vp;
3410Sstevel@tonic-gate 	struct vattr va;
3420Sstevel@tonic-gate 	fhandle_t *fhp = da->da_fhandle;
3430Sstevel@tonic-gate 	struct sec_ol sec = {0, 0};
3440Sstevel@tonic-gate 	bool_t publicfh_flag = FALSE, auth_weak = FALSE;
3457961SNatalie.Li@Sun.COM 	char *name;
3467961SNatalie.Li@Sun.COM 	struct sockaddr *ca;
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 	/*
3494971Sjarrett 	 * Trusted Extension doesn't support NFSv2. MOUNT
3504971Sjarrett 	 * will reject v2 clients. Need to prevent v2 client
3514971Sjarrett 	 * access via WebNFS here.
3524971Sjarrett 	 */
3534971Sjarrett 	if (is_system_labeled() && req->rq_vers == 2) {
3544971Sjarrett 		dr->dr_status = NFSERR_ACCES;
3554971Sjarrett 		return;
3564971Sjarrett 	}
3574971Sjarrett 
3584971Sjarrett 	/*
3590Sstevel@tonic-gate 	 * Disallow NULL paths
3600Sstevel@tonic-gate 	 */
3610Sstevel@tonic-gate 	if (da->da_name == NULL || *da->da_name == '\0') {
3620Sstevel@tonic-gate 		dr->dr_status = NFSERR_ACCES;
3630Sstevel@tonic-gate 		return;
3640Sstevel@tonic-gate 	}
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate 	/*
3670Sstevel@tonic-gate 	 * Allow lookups from the root - the default
3680Sstevel@tonic-gate 	 * location of the public filehandle.
3690Sstevel@tonic-gate 	 */
3700Sstevel@tonic-gate 	if (exi != NULL && (exi->exi_export.ex_flags & EX_PUBLIC)) {
3710Sstevel@tonic-gate 		dvp = rootdir;
3720Sstevel@tonic-gate 		VN_HOLD(dvp);
3730Sstevel@tonic-gate 	} else {
3740Sstevel@tonic-gate 		dvp = nfs_fhtovp(fhp, exi);
3750Sstevel@tonic-gate 		if (dvp == NULL) {
3760Sstevel@tonic-gate 			dr->dr_status = NFSERR_STALE;
3770Sstevel@tonic-gate 			return;
3780Sstevel@tonic-gate 		}
3790Sstevel@tonic-gate 	}
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate 	/*
3820Sstevel@tonic-gate 	 * Not allow lookup beyond root.
3830Sstevel@tonic-gate 	 * If the filehandle matches a filehandle of the exi,
3840Sstevel@tonic-gate 	 * then the ".." refers beyond the root of an exported filesystem.
3850Sstevel@tonic-gate 	 */
3860Sstevel@tonic-gate 	if (strcmp(da->da_name, "..") == 0 &&
3870Sstevel@tonic-gate 	    EQFID(&exi->exi_fid, (fid_t *)&fhp->fh_len)) {
3880Sstevel@tonic-gate 		VN_RELE(dvp);
3890Sstevel@tonic-gate 		dr->dr_status = NFSERR_NOENT;
3900Sstevel@tonic-gate 		return;
3910Sstevel@tonic-gate 	}
3920Sstevel@tonic-gate 
3937961SNatalie.Li@Sun.COM 	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
3947961SNatalie.Li@Sun.COM 	name = nfscmd_convname(ca, exi, da->da_name, NFSCMD_CONV_INBOUND,
3957961SNatalie.Li@Sun.COM 	    MAXPATHLEN);
3967961SNatalie.Li@Sun.COM 
3977961SNatalie.Li@Sun.COM 	if (name == NULL) {
3987961SNatalie.Li@Sun.COM 		dr->dr_status = NFSERR_ACCES;
3997961SNatalie.Li@Sun.COM 		return;
4007961SNatalie.Li@Sun.COM 	}
4017961SNatalie.Li@Sun.COM 
4020Sstevel@tonic-gate 	/*
4030Sstevel@tonic-gate 	 * If the public filehandle is used then allow
4040Sstevel@tonic-gate 	 * a multi-component lookup, i.e. evaluate
4050Sstevel@tonic-gate 	 * a pathname and follow symbolic links if
4060Sstevel@tonic-gate 	 * necessary.
4070Sstevel@tonic-gate 	 *
4080Sstevel@tonic-gate 	 * This may result in a vnode in another filesystem
4090Sstevel@tonic-gate 	 * which is OK as long as the filesystem is exported.
4100Sstevel@tonic-gate 	 */
4110Sstevel@tonic-gate 	if (PUBLIC_FH2(fhp)) {
4120Sstevel@tonic-gate 		publicfh_flag = TRUE;
4137961SNatalie.Li@Sun.COM 		error = rfs_publicfh_mclookup(name, dvp, cr, &vp, &exi,
4145050Sjwahlig 		    &sec);
4150Sstevel@tonic-gate 	} else {
4160Sstevel@tonic-gate 		/*
4170Sstevel@tonic-gate 		 * Do a normal single component lookup.
4180Sstevel@tonic-gate 		 */
4197961SNatalie.Li@Sun.COM 		error = VOP_LOOKUP(dvp, name, &vp, NULL, 0, NULL, cr,
4205331Samw 		    NULL, NULL, NULL);
4210Sstevel@tonic-gate 	}
4220Sstevel@tonic-gate 
4237961SNatalie.Li@Sun.COM 	if (name != da->da_name)
4247961SNatalie.Li@Sun.COM 		kmem_free(name, MAXPATHLEN);
4257961SNatalie.Li@Sun.COM 
4267961SNatalie.Li@Sun.COM 
4270Sstevel@tonic-gate 	if (!error) {
4280Sstevel@tonic-gate 		va.va_mask = AT_ALL;	/* we want everything */
4297387SRobert.Gordon@Sun.COM 
4300Sstevel@tonic-gate 		error = rfs4_delegated_getattr(vp, &va, 0, cr);
4317387SRobert.Gordon@Sun.COM 
4320Sstevel@tonic-gate 		/* check for overflows */
4330Sstevel@tonic-gate 		if (!error) {
4340Sstevel@tonic-gate 			acl_perm(vp, exi, &va, cr);
4350Sstevel@tonic-gate 			error = vattr_to_nattr(&va, &dr->dr_attr);
4360Sstevel@tonic-gate 			if (!error) {
4370Sstevel@tonic-gate 				if (sec.sec_flags & SEC_QUERY)
4380Sstevel@tonic-gate 					error = makefh_ol(&dr->dr_fhandle, exi,
4395050Sjwahlig 					    sec.sec_index);
4400Sstevel@tonic-gate 				else {
4410Sstevel@tonic-gate 					error = makefh(&dr->dr_fhandle, vp,
4425050Sjwahlig 					    exi);
4430Sstevel@tonic-gate 					if (!error && publicfh_flag &&
4445050Sjwahlig 					    !chk_clnt_sec(exi, req))
4450Sstevel@tonic-gate 						auth_weak = TRUE;
4460Sstevel@tonic-gate 				}
4470Sstevel@tonic-gate 			}
4480Sstevel@tonic-gate 		}
4490Sstevel@tonic-gate 		VN_RELE(vp);
4500Sstevel@tonic-gate 	}
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 	VN_RELE(dvp);
4530Sstevel@tonic-gate 
4540Sstevel@tonic-gate 	/*
4550Sstevel@tonic-gate 	 * If publicfh_flag is true then we have called rfs_publicfh_mclookup
4560Sstevel@tonic-gate 	 * and have obtained a new exportinfo in exi which needs to be
4570Sstevel@tonic-gate 	 * released. Note the the original exportinfo pointed to by exi
4580Sstevel@tonic-gate 	 * will be released by the caller, comon_dispatch.
4590Sstevel@tonic-gate 	 */
4600Sstevel@tonic-gate 	if (publicfh_flag && exi != NULL)
4610Sstevel@tonic-gate 		exi_rele(exi);
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate 	/*
4640Sstevel@tonic-gate 	 * If it's public fh, no 0x81, and client's flavor is
4650Sstevel@tonic-gate 	 * invalid, set WebNFS status to WNFSERR_CLNT_FLAVOR now.
4660Sstevel@tonic-gate 	 * Then set RPC status to AUTH_TOOWEAK in common_dispatch.
4670Sstevel@tonic-gate 	 */
4680Sstevel@tonic-gate 	if (auth_weak)
4690Sstevel@tonic-gate 		dr->dr_status = (enum nfsstat)WNFSERR_CLNT_FLAVOR;
4700Sstevel@tonic-gate 	else
4710Sstevel@tonic-gate 		dr->dr_status = puterrno(error);
4720Sstevel@tonic-gate }
4731610Sthurlow void *
rfs_lookup_getfh(struct nfsdiropargs * da)4740Sstevel@tonic-gate rfs_lookup_getfh(struct nfsdiropargs *da)
4750Sstevel@tonic-gate {
4760Sstevel@tonic-gate 	return (da->da_fhandle);
4770Sstevel@tonic-gate }
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate /*
4800Sstevel@tonic-gate  * Read symbolic link.
4810Sstevel@tonic-gate  * Returns the string in the symbolic link at the given fhandle.
4820Sstevel@tonic-gate  */
4830Sstevel@tonic-gate /* ARGSUSED */
4840Sstevel@tonic-gate void
rfs_readlink(fhandle_t * fhp,struct nfsrdlnres * rl,struct exportinfo * exi,struct svc_req * req,cred_t * cr)4850Sstevel@tonic-gate rfs_readlink(fhandle_t *fhp, struct nfsrdlnres *rl, struct exportinfo *exi,
4860Sstevel@tonic-gate 	struct svc_req *req, cred_t *cr)
4870Sstevel@tonic-gate {
4880Sstevel@tonic-gate 	int error;
4890Sstevel@tonic-gate 	struct iovec iov;
4900Sstevel@tonic-gate 	struct uio uio;
4910Sstevel@tonic-gate 	vnode_t *vp;
4920Sstevel@tonic-gate 	struct vattr va;
4937961SNatalie.Li@Sun.COM 	struct sockaddr *ca;
4947961SNatalie.Li@Sun.COM 	char *name = NULL;
49511291SRobert.Thurlow@Sun.COM 	int is_referral = 0;
4960Sstevel@tonic-gate 
4970Sstevel@tonic-gate 	vp = nfs_fhtovp(fhp, exi);
4980Sstevel@tonic-gate 	if (vp == NULL) {
4990Sstevel@tonic-gate 		rl->rl_data = NULL;
5000Sstevel@tonic-gate 		rl->rl_status = NFSERR_STALE;
5010Sstevel@tonic-gate 		return;
5020Sstevel@tonic-gate 	}
5030Sstevel@tonic-gate 
5040Sstevel@tonic-gate 	va.va_mask = AT_MODE;
5057387SRobert.Gordon@Sun.COM 
5065331Samw 	error = VOP_GETATTR(vp, &va, 0, cr, NULL);
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	if (error) {
5090Sstevel@tonic-gate 		VN_RELE(vp);
5100Sstevel@tonic-gate 		rl->rl_data = NULL;
5110Sstevel@tonic-gate 		rl->rl_status = puterrno(error);
5120Sstevel@tonic-gate 		return;
5130Sstevel@tonic-gate 	}
5140Sstevel@tonic-gate 
5150Sstevel@tonic-gate 	if (MANDLOCK(vp, va.va_mode)) {
5160Sstevel@tonic-gate 		VN_RELE(vp);
5170Sstevel@tonic-gate 		rl->rl_data = NULL;
5180Sstevel@tonic-gate 		rl->rl_status = NFSERR_ACCES;
5190Sstevel@tonic-gate 		return;
5200Sstevel@tonic-gate 	}
5210Sstevel@tonic-gate 
52211291SRobert.Thurlow@Sun.COM 	/* We lied about the object type for a referral */
52311291SRobert.Thurlow@Sun.COM 	if (vn_is_nfs_reparse(vp, cr))
52411291SRobert.Thurlow@Sun.COM 		is_referral = 1;
52511291SRobert.Thurlow@Sun.COM 
5260Sstevel@tonic-gate 	/*
5270Sstevel@tonic-gate 	 * XNFS and RFC1094 require us to return ENXIO if argument
5280Sstevel@tonic-gate 	 * is not a link. BUGID 1138002.
5290Sstevel@tonic-gate 	 */
53011291SRobert.Thurlow@Sun.COM 	if (vp->v_type != VLNK && !is_referral) {
5310Sstevel@tonic-gate 		VN_RELE(vp);
5320Sstevel@tonic-gate 		rl->rl_data = NULL;
5330Sstevel@tonic-gate 		rl->rl_status = NFSERR_NXIO;
5340Sstevel@tonic-gate 		return;
5350Sstevel@tonic-gate 	}
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate 	/*
5380Sstevel@tonic-gate 	 * Allocate data for pathname.  This will be freed by rfs_rlfree.
5390Sstevel@tonic-gate 	 */
5400Sstevel@tonic-gate 	rl->rl_data = kmem_alloc(NFS_MAXPATHLEN, KM_SLEEP);
5410Sstevel@tonic-gate 
54211291SRobert.Thurlow@Sun.COM 	if (is_referral) {
54311291SRobert.Thurlow@Sun.COM 		char *s;
54411291SRobert.Thurlow@Sun.COM 		size_t strsz;
54511291SRobert.Thurlow@Sun.COM 
54611291SRobert.Thurlow@Sun.COM 		/* Get an artificial symlink based on a referral */
54711291SRobert.Thurlow@Sun.COM 		s = build_symlink(vp, cr, &strsz);
54811291SRobert.Thurlow@Sun.COM 		global_svstat_ptr[2][NFS_REFERLINKS].value.ui64++;
54911291SRobert.Thurlow@Sun.COM 		DTRACE_PROBE2(nfs2serv__func__referral__reflink,
55011291SRobert.Thurlow@Sun.COM 		    vnode_t *, vp, char *, s);
55111291SRobert.Thurlow@Sun.COM 		if (s == NULL)
55211291SRobert.Thurlow@Sun.COM 			error = EINVAL;
55311291SRobert.Thurlow@Sun.COM 		else {
55411291SRobert.Thurlow@Sun.COM 			error = 0;
55511291SRobert.Thurlow@Sun.COM 			(void) strlcpy(rl->rl_data, s, NFS_MAXPATHLEN);
55611291SRobert.Thurlow@Sun.COM 			rl->rl_count = (uint32_t)MIN(strsz, NFS_MAXPATHLEN);
55711291SRobert.Thurlow@Sun.COM 			kmem_free(s, strsz);
55811291SRobert.Thurlow@Sun.COM 		}
55911291SRobert.Thurlow@Sun.COM 
56011291SRobert.Thurlow@Sun.COM 	} else {
56111291SRobert.Thurlow@Sun.COM 
56211291SRobert.Thurlow@Sun.COM 		/*
56311291SRobert.Thurlow@Sun.COM 		 * Set up io vector to read sym link data
56411291SRobert.Thurlow@Sun.COM 		 */
56511291SRobert.Thurlow@Sun.COM 		iov.iov_base = rl->rl_data;
56611291SRobert.Thurlow@Sun.COM 		iov.iov_len = NFS_MAXPATHLEN;
56711291SRobert.Thurlow@Sun.COM 		uio.uio_iov = &iov;
56811291SRobert.Thurlow@Sun.COM 		uio.uio_iovcnt = 1;
56911291SRobert.Thurlow@Sun.COM 		uio.uio_segflg = UIO_SYSSPACE;
57011291SRobert.Thurlow@Sun.COM 		uio.uio_extflg = UIO_COPY_CACHED;
57111291SRobert.Thurlow@Sun.COM 		uio.uio_loffset = (offset_t)0;
57211291SRobert.Thurlow@Sun.COM 		uio.uio_resid = NFS_MAXPATHLEN;
57311291SRobert.Thurlow@Sun.COM 
57411291SRobert.Thurlow@Sun.COM 		/*
57511291SRobert.Thurlow@Sun.COM 		 * Do the readlink.
57611291SRobert.Thurlow@Sun.COM 		 */
57711291SRobert.Thurlow@Sun.COM 		error = VOP_READLINK(vp, &uio, cr, NULL);
57811291SRobert.Thurlow@Sun.COM 
57911291SRobert.Thurlow@Sun.COM 		rl->rl_count = (uint32_t)(NFS_MAXPATHLEN - uio.uio_resid);
58011291SRobert.Thurlow@Sun.COM 
58111291SRobert.Thurlow@Sun.COM 		if (!error)
58211291SRobert.Thurlow@Sun.COM 			rl->rl_data[rl->rl_count] = '\0';
58311291SRobert.Thurlow@Sun.COM 
58411291SRobert.Thurlow@Sun.COM 	}
58511291SRobert.Thurlow@Sun.COM 
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 	VN_RELE(vp);
5880Sstevel@tonic-gate 
5897961SNatalie.Li@Sun.COM 	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
5907961SNatalie.Li@Sun.COM 	name = nfscmd_convname(ca, exi, rl->rl_data,
5917961SNatalie.Li@Sun.COM 	    NFSCMD_CONV_OUTBOUND, MAXPATHLEN);
5927961SNatalie.Li@Sun.COM 
5937961SNatalie.Li@Sun.COM 	if (name != NULL && name != rl->rl_data) {
5947961SNatalie.Li@Sun.COM 		kmem_free(rl->rl_data, NFS_MAXPATHLEN);
5957961SNatalie.Li@Sun.COM 		rl->rl_data = name;
5967961SNatalie.Li@Sun.COM 	}
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 	/*
5990Sstevel@tonic-gate 	 * XNFS and RFC1094 require us to return ENXIO if argument
6000Sstevel@tonic-gate 	 * is not a link. UFS returns EINVAL if this is the case,
6010Sstevel@tonic-gate 	 * so we do the mapping here. BUGID 1138002.
6020Sstevel@tonic-gate 	 */
6030Sstevel@tonic-gate 	if (error == EINVAL)
6040Sstevel@tonic-gate 		rl->rl_status = NFSERR_NXIO;
6050Sstevel@tonic-gate 	else
6060Sstevel@tonic-gate 		rl->rl_status = puterrno(error);
6070Sstevel@tonic-gate 
6080Sstevel@tonic-gate }
6091610Sthurlow void *
rfs_readlink_getfh(fhandle_t * fhp)6100Sstevel@tonic-gate rfs_readlink_getfh(fhandle_t *fhp)
6110Sstevel@tonic-gate {
6120Sstevel@tonic-gate 	return (fhp);
6130Sstevel@tonic-gate }
6140Sstevel@tonic-gate /*
6150Sstevel@tonic-gate  * Free data allocated by rfs_readlink
6160Sstevel@tonic-gate  */
6170Sstevel@tonic-gate void
rfs_rlfree(struct nfsrdlnres * rl)6180Sstevel@tonic-gate rfs_rlfree(struct nfsrdlnres *rl)
6190Sstevel@tonic-gate {
6200Sstevel@tonic-gate 	if (rl->rl_data != NULL)
6210Sstevel@tonic-gate 		kmem_free(rl->rl_data, NFS_MAXPATHLEN);
6220Sstevel@tonic-gate }
6230Sstevel@tonic-gate 
6247387SRobert.Gordon@Sun.COM static int rdma_setup_read_data2(struct nfsreadargs *, struct nfsrdresult *);
6257387SRobert.Gordon@Sun.COM 
6260Sstevel@tonic-gate /*
6270Sstevel@tonic-gate  * Read data.
6280Sstevel@tonic-gate  * Returns some data read from the file at the given fhandle.
6290Sstevel@tonic-gate  */
6300Sstevel@tonic-gate /* ARGSUSED */
6310Sstevel@tonic-gate void
rfs_read(struct nfsreadargs * ra,struct nfsrdresult * rr,struct exportinfo * exi,struct svc_req * req,cred_t * cr)6320Sstevel@tonic-gate rfs_read(struct nfsreadargs *ra, struct nfsrdresult *rr,
6330Sstevel@tonic-gate 	struct exportinfo *exi, struct svc_req *req, cred_t *cr)
6340Sstevel@tonic-gate {
6350Sstevel@tonic-gate 	vnode_t *vp;
6360Sstevel@tonic-gate 	int error;
6370Sstevel@tonic-gate 	struct vattr va;
6380Sstevel@tonic-gate 	struct iovec iov;
6390Sstevel@tonic-gate 	struct uio uio;
6400Sstevel@tonic-gate 	mblk_t *mp;
6410Sstevel@tonic-gate 	int alloc_err = 0;
6420Sstevel@tonic-gate 	int in_crit = 0;
6435599Sjwahlig 	caller_context_t ct;
6440Sstevel@tonic-gate 
6450Sstevel@tonic-gate 	vp = nfs_fhtovp(&ra->ra_fhandle, exi);
6460Sstevel@tonic-gate 	if (vp == NULL) {
6470Sstevel@tonic-gate 		rr->rr_data = NULL;
6480Sstevel@tonic-gate 		rr->rr_status = NFSERR_STALE;
6490Sstevel@tonic-gate 		return;
6500Sstevel@tonic-gate 	}
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate 	if (vp->v_type != VREG) {
6530Sstevel@tonic-gate 		VN_RELE(vp);
6540Sstevel@tonic-gate 		rr->rr_data = NULL;
6550Sstevel@tonic-gate 		rr->rr_status = NFSERR_ISDIR;
6560Sstevel@tonic-gate 		return;
6570Sstevel@tonic-gate 	}
6580Sstevel@tonic-gate 
6595599Sjwahlig 	ct.cc_sysid = 0;
6605599Sjwahlig 	ct.cc_pid = 0;
6615599Sjwahlig 	ct.cc_caller_id = nfs2_srv_caller_id;
6625599Sjwahlig 	ct.cc_flags = CC_DONTBLOCK;
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate 	/*
6650Sstevel@tonic-gate 	 * Enter the critical region before calling VOP_RWLOCK
6660Sstevel@tonic-gate 	 * to avoid a deadlock with write requests.
6670Sstevel@tonic-gate 	 */
6680Sstevel@tonic-gate 	if (nbl_need_check(vp)) {
6690Sstevel@tonic-gate 		nbl_start_crit(vp, RW_READER);
6700Sstevel@tonic-gate 		if (nbl_conflict(vp, NBL_READ, ra->ra_offset, ra->ra_count,
6715331Samw 		    0, NULL)) {
6720Sstevel@tonic-gate 			nbl_end_crit(vp);
6730Sstevel@tonic-gate 			VN_RELE(vp);
6740Sstevel@tonic-gate 			rr->rr_data = NULL;
6750Sstevel@tonic-gate 			rr->rr_status = NFSERR_ACCES;
6760Sstevel@tonic-gate 			return;
6770Sstevel@tonic-gate 		}
6780Sstevel@tonic-gate 		in_crit = 1;
6790Sstevel@tonic-gate 	}
6800Sstevel@tonic-gate 
6815599Sjwahlig 	error = VOP_RWLOCK(vp, V_WRITELOCK_FALSE, &ct);
6820Sstevel@tonic-gate 
6835599Sjwahlig 	/* check if a monitor detected a delegation conflict */
6845599Sjwahlig 	if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK)) {
6855599Sjwahlig 		VN_RELE(vp);
6865599Sjwahlig 		/* mark as wouldblock so response is dropped */
6875599Sjwahlig 		curthread->t_flag |= T_WOULDBLOCK;
6887387SRobert.Gordon@Sun.COM 
6895599Sjwahlig 		rr->rr_data = NULL;
6905599Sjwahlig 		return;
6915599Sjwahlig 	}
6925599Sjwahlig 
6930Sstevel@tonic-gate 	va.va_mask = AT_ALL;
6947387SRobert.Gordon@Sun.COM 
6955599Sjwahlig 	error = VOP_GETATTR(vp, &va, 0, cr, &ct);
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate 	if (error) {
6985599Sjwahlig 		VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &ct);
6990Sstevel@tonic-gate 		if (in_crit)
7000Sstevel@tonic-gate 			nbl_end_crit(vp);
7017387SRobert.Gordon@Sun.COM 
7020Sstevel@tonic-gate 		VN_RELE(vp);
7030Sstevel@tonic-gate 		rr->rr_data = NULL;
7040Sstevel@tonic-gate 		rr->rr_status = puterrno(error);
7057387SRobert.Gordon@Sun.COM 
7060Sstevel@tonic-gate 		return;
7070Sstevel@tonic-gate 	}
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate 	/*
7100Sstevel@tonic-gate 	 * This is a kludge to allow reading of files created
7110Sstevel@tonic-gate 	 * with no read permission.  The owner of the file
7120Sstevel@tonic-gate 	 * is always allowed to read it.
7130Sstevel@tonic-gate 	 */
7140Sstevel@tonic-gate 	if (crgetuid(cr) != va.va_uid) {
7155599Sjwahlig 		error = VOP_ACCESS(vp, VREAD, 0, cr, &ct);
7167387SRobert.Gordon@Sun.COM 
7170Sstevel@tonic-gate 		if (error) {
7180Sstevel@tonic-gate 			/*
7190Sstevel@tonic-gate 			 * Exec is the same as read over the net because
7200Sstevel@tonic-gate 			 * of demand loading.
7210Sstevel@tonic-gate 			 */
7225599Sjwahlig 			error = VOP_ACCESS(vp, VEXEC, 0, cr, &ct);
7230Sstevel@tonic-gate 		}
7240Sstevel@tonic-gate 		if (error) {
7255599Sjwahlig 			VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &ct);
7260Sstevel@tonic-gate 			if (in_crit)
7270Sstevel@tonic-gate 				nbl_end_crit(vp);
7280Sstevel@tonic-gate 			VN_RELE(vp);
7290Sstevel@tonic-gate 			rr->rr_data = NULL;
7300Sstevel@tonic-gate 			rr->rr_status = puterrno(error);
7317387SRobert.Gordon@Sun.COM 
7320Sstevel@tonic-gate 			return;
7330Sstevel@tonic-gate 		}
7340Sstevel@tonic-gate 	}
7350Sstevel@tonic-gate 
7360Sstevel@tonic-gate 	if (MANDLOCK(vp, va.va_mode)) {
7375599Sjwahlig 		VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &ct);
7380Sstevel@tonic-gate 		if (in_crit)
7390Sstevel@tonic-gate 			nbl_end_crit(vp);
7407387SRobert.Gordon@Sun.COM 
7410Sstevel@tonic-gate 		VN_RELE(vp);
7420Sstevel@tonic-gate 		rr->rr_data = NULL;
7430Sstevel@tonic-gate 		rr->rr_status = NFSERR_ACCES;
7447387SRobert.Gordon@Sun.COM 
7450Sstevel@tonic-gate 		return;
7460Sstevel@tonic-gate 	}
7470Sstevel@tonic-gate 
7487387SRobert.Gordon@Sun.COM 	rr->rr_ok.rrok_wlist_len = 0;
7497387SRobert.Gordon@Sun.COM 	rr->rr_ok.rrok_wlist = NULL;
7507387SRobert.Gordon@Sun.COM 
7510Sstevel@tonic-gate 	if ((u_offset_t)ra->ra_offset >= va.va_size) {
7520Sstevel@tonic-gate 		rr->rr_count = 0;
7530Sstevel@tonic-gate 		rr->rr_data = NULL;
7540Sstevel@tonic-gate 		/*
7550Sstevel@tonic-gate 		 * In this case, status is NFS_OK, but there is no data
7560Sstevel@tonic-gate 		 * to encode. So set rr_mp to NULL.
7570Sstevel@tonic-gate 		 */
7580Sstevel@tonic-gate 		rr->rr_mp = NULL;
7599348SSiddheshwar.Mahesh@Sun.COM 		rr->rr_ok.rrok_wlist = ra->ra_wlist;
7609348SSiddheshwar.Mahesh@Sun.COM 		if (rr->rr_ok.rrok_wlist)
7619348SSiddheshwar.Mahesh@Sun.COM 			clist_zero_len(rr->rr_ok.rrok_wlist);
7620Sstevel@tonic-gate 		goto done;
7630Sstevel@tonic-gate 	}
7640Sstevel@tonic-gate 
7657387SRobert.Gordon@Sun.COM 	if (ra->ra_wlist) {
7667387SRobert.Gordon@Sun.COM 		mp = NULL;
7677387SRobert.Gordon@Sun.COM 		rr->rr_mp = NULL;
7687387SRobert.Gordon@Sun.COM 		(void) rdma_get_wchunk(req, &iov, ra->ra_wlist);
769*13002SKaren.Rochford@Sun.COM 		if (ra->ra_count > iov.iov_len) {
770*13002SKaren.Rochford@Sun.COM 			rr->rr_data = NULL;
771*13002SKaren.Rochford@Sun.COM 			rr->rr_status = NFSERR_INVAL;
772*13002SKaren.Rochford@Sun.COM 			goto done;
773*13002SKaren.Rochford@Sun.COM 		}
7747387SRobert.Gordon@Sun.COM 	} else {
7757387SRobert.Gordon@Sun.COM 		/*
7767387SRobert.Gordon@Sun.COM 		 * mp will contain the data to be sent out in the read reply.
7777387SRobert.Gordon@Sun.COM 		 * This will be freed after the reply has been sent out (by the
7787387SRobert.Gordon@Sun.COM 		 * driver).
7797387SRobert.Gordon@Sun.COM 		 * Let's roundup the data to a BYTES_PER_XDR_UNIT multiple, so
7807387SRobert.Gordon@Sun.COM 		 * that the call to xdrmblk_putmblk() never fails.
7817387SRobert.Gordon@Sun.COM 		 */
7827387SRobert.Gordon@Sun.COM 		mp = allocb_wait(RNDUP(ra->ra_count), BPRI_MED, STR_NOSIG,
7837387SRobert.Gordon@Sun.COM 		    &alloc_err);
7847387SRobert.Gordon@Sun.COM 		ASSERT(mp != NULL);
7857387SRobert.Gordon@Sun.COM 		ASSERT(alloc_err == 0);
7867387SRobert.Gordon@Sun.COM 
7877387SRobert.Gordon@Sun.COM 		rr->rr_mp = mp;
7887387SRobert.Gordon@Sun.COM 
7897387SRobert.Gordon@Sun.COM 		/*
7907387SRobert.Gordon@Sun.COM 		 * Set up io vector
7917387SRobert.Gordon@Sun.COM 		 */
7927387SRobert.Gordon@Sun.COM 		iov.iov_base = (caddr_t)mp->b_datap->db_base;
7937387SRobert.Gordon@Sun.COM 		iov.iov_len = ra->ra_count;
7947387SRobert.Gordon@Sun.COM 	}
7957387SRobert.Gordon@Sun.COM 
7960Sstevel@tonic-gate 	uio.uio_iov = &iov;
7970Sstevel@tonic-gate 	uio.uio_iovcnt = 1;
7980Sstevel@tonic-gate 	uio.uio_segflg = UIO_SYSSPACE;
7990Sstevel@tonic-gate 	uio.uio_extflg = UIO_COPY_CACHED;
8000Sstevel@tonic-gate 	uio.uio_loffset = (offset_t)ra->ra_offset;
8010Sstevel@tonic-gate 	uio.uio_resid = ra->ra_count;
8020Sstevel@tonic-gate 
8035599Sjwahlig 	error = VOP_READ(vp, &uio, 0, cr, &ct);
8040Sstevel@tonic-gate 
8050Sstevel@tonic-gate 	if (error) {
8067387SRobert.Gordon@Sun.COM 		if (mp)
8077387SRobert.Gordon@Sun.COM 			freeb(mp);
8085599Sjwahlig 
8095599Sjwahlig 		/*
8105599Sjwahlig 		 * check if a monitor detected a delegation conflict and
8115599Sjwahlig 		 * mark as wouldblock so response is dropped
8125599Sjwahlig 		 */
8135599Sjwahlig 		if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK))
8145599Sjwahlig 			curthread->t_flag |= T_WOULDBLOCK;
8155599Sjwahlig 		else
8165599Sjwahlig 			rr->rr_status = puterrno(error);
8175599Sjwahlig 
8185599Sjwahlig 		VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &ct);
8190Sstevel@tonic-gate 		if (in_crit)
8200Sstevel@tonic-gate 			nbl_end_crit(vp);
8217387SRobert.Gordon@Sun.COM 
8220Sstevel@tonic-gate 		VN_RELE(vp);
8230Sstevel@tonic-gate 		rr->rr_data = NULL;
8247387SRobert.Gordon@Sun.COM 
8250Sstevel@tonic-gate 		return;
8260Sstevel@tonic-gate 	}
8270Sstevel@tonic-gate 
8280Sstevel@tonic-gate 	/*
8290Sstevel@tonic-gate 	 * Get attributes again so we can send the latest access
8300Sstevel@tonic-gate 	 * time to the client side for his cache.
8310Sstevel@tonic-gate 	 */
8320Sstevel@tonic-gate 	va.va_mask = AT_ALL;
8337387SRobert.Gordon@Sun.COM 
8345599Sjwahlig 	error = VOP_GETATTR(vp, &va, 0, cr, &ct);
8357387SRobert.Gordon@Sun.COM 
8360Sstevel@tonic-gate 	if (error) {
8377387SRobert.Gordon@Sun.COM 		if (mp)
8387387SRobert.Gordon@Sun.COM 			freeb(mp);
8397387SRobert.Gordon@Sun.COM 
8405599Sjwahlig 		VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &ct);
8410Sstevel@tonic-gate 		if (in_crit)
8420Sstevel@tonic-gate 			nbl_end_crit(vp);
8437387SRobert.Gordon@Sun.COM 
8440Sstevel@tonic-gate 		VN_RELE(vp);
8450Sstevel@tonic-gate 		rr->rr_data = NULL;
8460Sstevel@tonic-gate 		rr->rr_status = puterrno(error);
8477387SRobert.Gordon@Sun.COM 
8480Sstevel@tonic-gate 		return;
8490Sstevel@tonic-gate 	}
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 	rr->rr_count = (uint32_t)(ra->ra_count - uio.uio_resid);
8520Sstevel@tonic-gate 
8537387SRobert.Gordon@Sun.COM 	if (mp) {
8547387SRobert.Gordon@Sun.COM 		rr->rr_data = (char *)mp->b_datap->db_base;
8557387SRobert.Gordon@Sun.COM 	} else {
8567387SRobert.Gordon@Sun.COM 		if (ra->ra_wlist) {
8577387SRobert.Gordon@Sun.COM 			rr->rr_data = (caddr_t)iov.iov_base;
8587387SRobert.Gordon@Sun.COM 			if (!rdma_setup_read_data2(ra, rr)) {
8597387SRobert.Gordon@Sun.COM 				rr->rr_data = NULL;
8607387SRobert.Gordon@Sun.COM 				rr->rr_status = puterrno(NFSERR_INVAL);
8617387SRobert.Gordon@Sun.COM 			}
8627387SRobert.Gordon@Sun.COM 		}
8637387SRobert.Gordon@Sun.COM 	}
8640Sstevel@tonic-gate done:
8655599Sjwahlig 	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, &ct);
8660Sstevel@tonic-gate 	if (in_crit)
8670Sstevel@tonic-gate 		nbl_end_crit(vp);
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate 	acl_perm(vp, exi, &va, cr);
8700Sstevel@tonic-gate 
8710Sstevel@tonic-gate 	/* check for overflows */
8720Sstevel@tonic-gate 	error = vattr_to_nattr(&va, &rr->rr_attr);
8730Sstevel@tonic-gate 
8740Sstevel@tonic-gate 	VN_RELE(vp);
8750Sstevel@tonic-gate 
8760Sstevel@tonic-gate 	rr->rr_status = puterrno(error);
8770Sstevel@tonic-gate }
8780Sstevel@tonic-gate 
8790Sstevel@tonic-gate /*
8800Sstevel@tonic-gate  * Free data allocated by rfs_read
8810Sstevel@tonic-gate  */
8820Sstevel@tonic-gate void
rfs_rdfree(struct nfsrdresult * rr)8830Sstevel@tonic-gate rfs_rdfree(struct nfsrdresult *rr)
8840Sstevel@tonic-gate {
8850Sstevel@tonic-gate 	mblk_t *mp;
8860Sstevel@tonic-gate 
8870Sstevel@tonic-gate 	if (rr->rr_status == NFS_OK) {
8880Sstevel@tonic-gate 		mp = rr->rr_mp;
8890Sstevel@tonic-gate 		if (mp != NULL)
8900Sstevel@tonic-gate 			freeb(mp);
8910Sstevel@tonic-gate 	}
8920Sstevel@tonic-gate }
8930Sstevel@tonic-gate 
8941610Sthurlow void *
rfs_read_getfh(struct nfsreadargs * ra)8950Sstevel@tonic-gate rfs_read_getfh(struct nfsreadargs *ra)
8960Sstevel@tonic-gate {
8970Sstevel@tonic-gate 	return (&ra->ra_fhandle);
8980Sstevel@tonic-gate }
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate #define	MAX_IOVECS	12
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate #ifdef DEBUG
9030Sstevel@tonic-gate static int rfs_write_sync_hits = 0;
9040Sstevel@tonic-gate static int rfs_write_sync_misses = 0;
9050Sstevel@tonic-gate #endif
9060Sstevel@tonic-gate 
9070Sstevel@tonic-gate /*
9080Sstevel@tonic-gate  * Write data to file.
9090Sstevel@tonic-gate  * Returns attributes of a file after writing some data to it.
9100Sstevel@tonic-gate  *
9110Sstevel@tonic-gate  * Any changes made here, especially in error handling might have
9120Sstevel@tonic-gate  * to also be done in rfs_write (which clusters write requests).
9130Sstevel@tonic-gate  */
9140Sstevel@tonic-gate void
rfs_write_sync(struct nfswriteargs * wa,struct nfsattrstat * ns,struct exportinfo * exi,struct svc_req * req,cred_t * cr)9150Sstevel@tonic-gate rfs_write_sync(struct nfswriteargs *wa, struct nfsattrstat *ns,
9160Sstevel@tonic-gate 	struct exportinfo *exi, struct svc_req *req, cred_t *cr)
9170Sstevel@tonic-gate {
9180Sstevel@tonic-gate 	int error;
9190Sstevel@tonic-gate 	vnode_t *vp;
9200Sstevel@tonic-gate 	rlim64_t rlimit;
9210Sstevel@tonic-gate 	struct vattr va;
9220Sstevel@tonic-gate 	struct uio uio;
9230Sstevel@tonic-gate 	struct iovec iov[MAX_IOVECS];
9240Sstevel@tonic-gate 	mblk_t *m;
9250Sstevel@tonic-gate 	struct iovec *iovp;
9260Sstevel@tonic-gate 	int iovcnt;
9270Sstevel@tonic-gate 	cred_t *savecred;
9280Sstevel@tonic-gate 	int in_crit = 0;
9295599Sjwahlig 	caller_context_t ct;
9300Sstevel@tonic-gate 
9310Sstevel@tonic-gate 	vp = nfs_fhtovp(&wa->wa_fhandle, exi);
9320Sstevel@tonic-gate 	if (vp == NULL) {
9330Sstevel@tonic-gate 		ns->ns_status = NFSERR_STALE;
9340Sstevel@tonic-gate 		return;
9350Sstevel@tonic-gate 	}
9360Sstevel@tonic-gate 
9370Sstevel@tonic-gate 	if (rdonly(exi, req)) {
9380Sstevel@tonic-gate 		VN_RELE(vp);
9390Sstevel@tonic-gate 		ns->ns_status = NFSERR_ROFS;
9400Sstevel@tonic-gate 		return;
9410Sstevel@tonic-gate 	}
9420Sstevel@tonic-gate 
9430Sstevel@tonic-gate 	if (vp->v_type != VREG) {
9440Sstevel@tonic-gate 		VN_RELE(vp);
9450Sstevel@tonic-gate 		ns->ns_status = NFSERR_ISDIR;
9460Sstevel@tonic-gate 		return;
9470Sstevel@tonic-gate 	}
9480Sstevel@tonic-gate 
9495599Sjwahlig 	ct.cc_sysid = 0;
9505599Sjwahlig 	ct.cc_pid = 0;
9515599Sjwahlig 	ct.cc_caller_id = nfs2_srv_caller_id;
9525599Sjwahlig 	ct.cc_flags = CC_DONTBLOCK;
9530Sstevel@tonic-gate 
9540Sstevel@tonic-gate 	va.va_mask = AT_UID|AT_MODE;
9557387SRobert.Gordon@Sun.COM 
9565599Sjwahlig 	error = VOP_GETATTR(vp, &va, 0, cr, &ct);
9570Sstevel@tonic-gate 
9580Sstevel@tonic-gate 	if (error) {
9590Sstevel@tonic-gate 		VN_RELE(vp);
9600Sstevel@tonic-gate 		ns->ns_status = puterrno(error);
9617387SRobert.Gordon@Sun.COM 
9620Sstevel@tonic-gate 		return;
9630Sstevel@tonic-gate 	}
9640Sstevel@tonic-gate 
9650Sstevel@tonic-gate 	if (crgetuid(cr) != va.va_uid) {
9660Sstevel@tonic-gate 		/*
9670Sstevel@tonic-gate 		 * This is a kludge to allow writes of files created
9680Sstevel@tonic-gate 		 * with read only permission.  The owner of the file
9690Sstevel@tonic-gate 		 * is always allowed to write it.
9700Sstevel@tonic-gate 		 */
9715599Sjwahlig 		error = VOP_ACCESS(vp, VWRITE, 0, cr, &ct);
9727387SRobert.Gordon@Sun.COM 
9730Sstevel@tonic-gate 		if (error) {
9740Sstevel@tonic-gate 			VN_RELE(vp);
9750Sstevel@tonic-gate 			ns->ns_status = puterrno(error);
9760Sstevel@tonic-gate 			return;
9770Sstevel@tonic-gate 		}
9780Sstevel@tonic-gate 	}
9790Sstevel@tonic-gate 
9800Sstevel@tonic-gate 	/*
9810Sstevel@tonic-gate 	 * Can't access a mandatory lock file.  This might cause
9820Sstevel@tonic-gate 	 * the NFS service thread to block forever waiting for a
9830Sstevel@tonic-gate 	 * lock to be released that will never be released.
9840Sstevel@tonic-gate 	 */
9850Sstevel@tonic-gate 	if (MANDLOCK(vp, va.va_mode)) {
9860Sstevel@tonic-gate 		VN_RELE(vp);
9870Sstevel@tonic-gate 		ns->ns_status = NFSERR_ACCES;
9880Sstevel@tonic-gate 		return;
9890Sstevel@tonic-gate 	}
9900Sstevel@tonic-gate 
9910Sstevel@tonic-gate 	/*
9920Sstevel@tonic-gate 	 * We have to enter the critical region before calling VOP_RWLOCK
9930Sstevel@tonic-gate 	 * to avoid a deadlock with ufs.
9940Sstevel@tonic-gate 	 */
9950Sstevel@tonic-gate 	if (nbl_need_check(vp)) {
9960Sstevel@tonic-gate 		nbl_start_crit(vp, RW_READER);
9970Sstevel@tonic-gate 		in_crit = 1;
9980Sstevel@tonic-gate 		if (nbl_conflict(vp, NBL_WRITE, wa->wa_offset,
9995331Samw 		    wa->wa_count, 0, NULL)) {
10000Sstevel@tonic-gate 			error = EACCES;
10010Sstevel@tonic-gate 			goto out;
10020Sstevel@tonic-gate 		}
10030Sstevel@tonic-gate 	}
10040Sstevel@tonic-gate 
10055599Sjwahlig 	error = VOP_RWLOCK(vp, V_WRITELOCK_TRUE, &ct);
10060Sstevel@tonic-gate 
10075599Sjwahlig 	/* check if a monitor detected a delegation conflict */
10085599Sjwahlig 	if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK)) {
10095599Sjwahlig 		VN_RELE(vp);
10105599Sjwahlig 		/* mark as wouldblock so response is dropped */
10115599Sjwahlig 		curthread->t_flag |= T_WOULDBLOCK;
10125599Sjwahlig 		return;
10135599Sjwahlig 	}
10145599Sjwahlig 
10157387SRobert.Gordon@Sun.COM 	if (wa->wa_data || wa->wa_rlist) {
10167387SRobert.Gordon@Sun.COM 		/* Do the RDMA thing if necessary */
10177387SRobert.Gordon@Sun.COM 		if (wa->wa_rlist) {
10187387SRobert.Gordon@Sun.COM 			iov[0].iov_base = (char *)((wa->wa_rlist)->u.c_daddr3);
10197387SRobert.Gordon@Sun.COM 			iov[0].iov_len = wa->wa_count;
10207387SRobert.Gordon@Sun.COM 		} else  {
10217387SRobert.Gordon@Sun.COM 			iov[0].iov_base = wa->wa_data;
10227387SRobert.Gordon@Sun.COM 			iov[0].iov_len = wa->wa_count;
10237387SRobert.Gordon@Sun.COM 		}
10240Sstevel@tonic-gate 		uio.uio_iov = iov;
10250Sstevel@tonic-gate 		uio.uio_iovcnt = 1;
10260Sstevel@tonic-gate 		uio.uio_segflg = UIO_SYSSPACE;
10270Sstevel@tonic-gate 		uio.uio_extflg = UIO_COPY_DEFAULT;
10280Sstevel@tonic-gate 		uio.uio_loffset = (offset_t)wa->wa_offset;
10290Sstevel@tonic-gate 		uio.uio_resid = wa->wa_count;
10300Sstevel@tonic-gate 		/*
10310Sstevel@tonic-gate 		 * The limit is checked on the client. We
10320Sstevel@tonic-gate 		 * should allow any size writes here.
10330Sstevel@tonic-gate 		 */
10340Sstevel@tonic-gate 		uio.uio_llimit = curproc->p_fsz_ctl;
10350Sstevel@tonic-gate 		rlimit = uio.uio_llimit - wa->wa_offset;
10360Sstevel@tonic-gate 		if (rlimit < (rlim64_t)uio.uio_resid)
10370Sstevel@tonic-gate 			uio.uio_resid = (uint_t)rlimit;
10380Sstevel@tonic-gate 
10390Sstevel@tonic-gate 		/*
10400Sstevel@tonic-gate 		 * for now we assume no append mode
10410Sstevel@tonic-gate 		 */
10420Sstevel@tonic-gate 		/*
10430Sstevel@tonic-gate 		 * We're changing creds because VM may fault and we need
10440Sstevel@tonic-gate 		 * the cred of the current thread to be used if quota
10450Sstevel@tonic-gate 		 * checking is enabled.
10460Sstevel@tonic-gate 		 */
10470Sstevel@tonic-gate 		savecred = curthread->t_cred;
10480Sstevel@tonic-gate 		curthread->t_cred = cr;
10495599Sjwahlig 		error = VOP_WRITE(vp, &uio, FSYNC, cr, &ct);
10500Sstevel@tonic-gate 		curthread->t_cred = savecred;
10510Sstevel@tonic-gate 	} else {
10520Sstevel@tonic-gate 		iovcnt = 0;
10530Sstevel@tonic-gate 		for (m = wa->wa_mblk; m != NULL; m = m->b_cont)
10540Sstevel@tonic-gate 			iovcnt++;
10550Sstevel@tonic-gate 		if (iovcnt <= MAX_IOVECS) {
10560Sstevel@tonic-gate #ifdef DEBUG
10570Sstevel@tonic-gate 			rfs_write_sync_hits++;
10580Sstevel@tonic-gate #endif
10590Sstevel@tonic-gate 			iovp = iov;
10600Sstevel@tonic-gate 		} else {
10610Sstevel@tonic-gate #ifdef DEBUG
10620Sstevel@tonic-gate 			rfs_write_sync_misses++;
10630Sstevel@tonic-gate #endif
10640Sstevel@tonic-gate 			iovp = kmem_alloc(sizeof (*iovp) * iovcnt, KM_SLEEP);
10650Sstevel@tonic-gate 		}
10660Sstevel@tonic-gate 		mblk_to_iov(wa->wa_mblk, iovcnt, iovp);
10670Sstevel@tonic-gate 		uio.uio_iov = iovp;
10680Sstevel@tonic-gate 		uio.uio_iovcnt = iovcnt;
10690Sstevel@tonic-gate 		uio.uio_segflg = UIO_SYSSPACE;
10700Sstevel@tonic-gate 		uio.uio_extflg = UIO_COPY_DEFAULT;
10710Sstevel@tonic-gate 		uio.uio_loffset = (offset_t)wa->wa_offset;
10720Sstevel@tonic-gate 		uio.uio_resid = wa->wa_count;
10730Sstevel@tonic-gate 		/*
10740Sstevel@tonic-gate 		 * The limit is checked on the client. We
10750Sstevel@tonic-gate 		 * should allow any size writes here.
10760Sstevel@tonic-gate 		 */
10770Sstevel@tonic-gate 		uio.uio_llimit = curproc->p_fsz_ctl;
10780Sstevel@tonic-gate 		rlimit = uio.uio_llimit - wa->wa_offset;
10790Sstevel@tonic-gate 		if (rlimit < (rlim64_t)uio.uio_resid)
10800Sstevel@tonic-gate 			uio.uio_resid = (uint_t)rlimit;
10810Sstevel@tonic-gate 
10820Sstevel@tonic-gate 		/*
10830Sstevel@tonic-gate 		 * For now we assume no append mode.
10840Sstevel@tonic-gate 		 */
10850Sstevel@tonic-gate 		/*
10860Sstevel@tonic-gate 		 * We're changing creds because VM may fault and we need
10870Sstevel@tonic-gate 		 * the cred of the current thread to be used if quota
10880Sstevel@tonic-gate 		 * checking is enabled.
10890Sstevel@tonic-gate 		 */
10900Sstevel@tonic-gate 		savecred = curthread->t_cred;
10910Sstevel@tonic-gate 		curthread->t_cred = cr;
10925599Sjwahlig 		error = VOP_WRITE(vp, &uio, FSYNC, cr, &ct);
10930Sstevel@tonic-gate 		curthread->t_cred = savecred;
10940Sstevel@tonic-gate 
10950Sstevel@tonic-gate 		if (iovp != iov)
10960Sstevel@tonic-gate 			kmem_free(iovp, sizeof (*iovp) * iovcnt);
10970Sstevel@tonic-gate 	}
10980Sstevel@tonic-gate 
10995599Sjwahlig 	VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, &ct);
11000Sstevel@tonic-gate 
11010Sstevel@tonic-gate 	if (!error) {
11020Sstevel@tonic-gate 		/*
11030Sstevel@tonic-gate 		 * Get attributes again so we send the latest mod
11040Sstevel@tonic-gate 		 * time to the client side for his cache.
11050Sstevel@tonic-gate 		 */
11060Sstevel@tonic-gate 		va.va_mask = AT_ALL;	/* now we want everything */
11077387SRobert.Gordon@Sun.COM 
11085599Sjwahlig 		error = VOP_GETATTR(vp, &va, 0, cr, &ct);
11097387SRobert.Gordon@Sun.COM 
11100Sstevel@tonic-gate 		/* check for overflows */
11110Sstevel@tonic-gate 		if (!error) {
11120Sstevel@tonic-gate 			acl_perm(vp, exi, &va, cr);
11130Sstevel@tonic-gate 			error = vattr_to_nattr(&va, &ns->ns_attr);
11140Sstevel@tonic-gate 		}
11150Sstevel@tonic-gate 	}
11160Sstevel@tonic-gate 
11170Sstevel@tonic-gate out:
11180Sstevel@tonic-gate 	if (in_crit)
11190Sstevel@tonic-gate 		nbl_end_crit(vp);
11200Sstevel@tonic-gate 	VN_RELE(vp);
11210Sstevel@tonic-gate 
11225599Sjwahlig 	/* check if a monitor detected a delegation conflict */
11235599Sjwahlig 	if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK))
11245599Sjwahlig 		/* mark as wouldblock so response is dropped */
11255599Sjwahlig 		curthread->t_flag |= T_WOULDBLOCK;
11265599Sjwahlig 	else
11275599Sjwahlig 		ns->ns_status = puterrno(error);
11280Sstevel@tonic-gate 
11290Sstevel@tonic-gate }
11300Sstevel@tonic-gate 
11310Sstevel@tonic-gate struct rfs_async_write {
11320Sstevel@tonic-gate 	struct nfswriteargs *wa;
11330Sstevel@tonic-gate 	struct nfsattrstat *ns;
11340Sstevel@tonic-gate 	struct svc_req *req;
11350Sstevel@tonic-gate 	cred_t *cr;
11360Sstevel@tonic-gate 	kthread_t *thread;
11370Sstevel@tonic-gate 	struct rfs_async_write *list;
11380Sstevel@tonic-gate };
11390Sstevel@tonic-gate 
11400Sstevel@tonic-gate struct rfs_async_write_list {
11410Sstevel@tonic-gate 	fhandle_t *fhp;
11420Sstevel@tonic-gate 	kcondvar_t cv;
11430Sstevel@tonic-gate 	struct rfs_async_write *list;
11440Sstevel@tonic-gate 	struct rfs_async_write_list *next;
11450Sstevel@tonic-gate };
11460Sstevel@tonic-gate 
11470Sstevel@tonic-gate static struct rfs_async_write_list *rfs_async_write_head = NULL;
11480Sstevel@tonic-gate static kmutex_t rfs_async_write_lock;
11490Sstevel@tonic-gate static int rfs_write_async = 1;	/* enables write clustering if == 1 */
11500Sstevel@tonic-gate 
11510Sstevel@tonic-gate #define	MAXCLIOVECS	42
11520Sstevel@tonic-gate #define	RFSWRITE_INITVAL (enum nfsstat) -1
11530Sstevel@tonic-gate 
11540Sstevel@tonic-gate #ifdef DEBUG
11550Sstevel@tonic-gate static int rfs_write_hits = 0;
11560Sstevel@tonic-gate static int rfs_write_misses = 0;
11570Sstevel@tonic-gate #endif
11580Sstevel@tonic-gate 
11590Sstevel@tonic-gate /*
11600Sstevel@tonic-gate  * Write data to file.
11610Sstevel@tonic-gate  * Returns attributes of a file after writing some data to it.
11620Sstevel@tonic-gate  */
11630Sstevel@tonic-gate void
rfs_write(struct nfswriteargs * wa,struct nfsattrstat * ns,struct exportinfo * exi,struct svc_req * req,cred_t * cr)11640Sstevel@tonic-gate rfs_write(struct nfswriteargs *wa, struct nfsattrstat *ns,
11650Sstevel@tonic-gate 	struct exportinfo *exi, struct svc_req *req, cred_t *cr)
11660Sstevel@tonic-gate {
11670Sstevel@tonic-gate 	int error;
11680Sstevel@tonic-gate 	vnode_t *vp;
11690Sstevel@tonic-gate 	rlim64_t rlimit;
11700Sstevel@tonic-gate 	struct vattr va;
11710Sstevel@tonic-gate 	struct uio uio;
11720Sstevel@tonic-gate 	struct rfs_async_write_list *lp;
11730Sstevel@tonic-gate 	struct rfs_async_write_list *nlp;
11740Sstevel@tonic-gate 	struct rfs_async_write *rp;
11750Sstevel@tonic-gate 	struct rfs_async_write *nrp;
11760Sstevel@tonic-gate 	struct rfs_async_write *trp;
11770Sstevel@tonic-gate 	struct rfs_async_write *lrp;
11780Sstevel@tonic-gate 	int data_written;
11790Sstevel@tonic-gate 	int iovcnt;
11800Sstevel@tonic-gate 	mblk_t *m;
11810Sstevel@tonic-gate 	struct iovec *iovp;
11820Sstevel@tonic-gate 	struct iovec *niovp;
11830Sstevel@tonic-gate 	struct iovec iov[MAXCLIOVECS];
11840Sstevel@tonic-gate 	int count;
11850Sstevel@tonic-gate 	int rcount;
11860Sstevel@tonic-gate 	uint_t off;
11870Sstevel@tonic-gate 	uint_t len;
11880Sstevel@tonic-gate 	struct rfs_async_write nrpsp;
11890Sstevel@tonic-gate 	struct rfs_async_write_list nlpsp;
11900Sstevel@tonic-gate 	ushort_t t_flag;
11910Sstevel@tonic-gate 	cred_t *savecred;
11920Sstevel@tonic-gate 	int in_crit = 0;
11935599Sjwahlig 	caller_context_t ct;
11940Sstevel@tonic-gate 
11950Sstevel@tonic-gate 	if (!rfs_write_async) {
11960Sstevel@tonic-gate 		rfs_write_sync(wa, ns, exi, req, cr);
11970Sstevel@tonic-gate 		return;
11980Sstevel@tonic-gate 	}
11990Sstevel@tonic-gate 
12000Sstevel@tonic-gate 	/*
12010Sstevel@tonic-gate 	 * Initialize status to RFSWRITE_INITVAL instead of 0, since value of 0
12020Sstevel@tonic-gate 	 * is considered an OK.
12030Sstevel@tonic-gate 	 */
12040Sstevel@tonic-gate 	ns->ns_status = RFSWRITE_INITVAL;
12050Sstevel@tonic-gate 
12060Sstevel@tonic-gate 	nrp = &nrpsp;
12070Sstevel@tonic-gate 	nrp->wa = wa;
12080Sstevel@tonic-gate 	nrp->ns = ns;
12090Sstevel@tonic-gate 	nrp->req = req;
12100Sstevel@tonic-gate 	nrp->cr = cr;
12110Sstevel@tonic-gate 	nrp->thread = curthread;
12120Sstevel@tonic-gate 
12130Sstevel@tonic-gate 	ASSERT(curthread->t_schedflag & TS_DONT_SWAP);
12140Sstevel@tonic-gate 
12150Sstevel@tonic-gate 	/*
12160Sstevel@tonic-gate 	 * Look to see if there is already a cluster started
12170Sstevel@tonic-gate 	 * for this file.
12180Sstevel@tonic-gate 	 */
12190Sstevel@tonic-gate 	mutex_enter(&rfs_async_write_lock);
12200Sstevel@tonic-gate 	for (lp = rfs_async_write_head; lp != NULL; lp = lp->next) {
12210Sstevel@tonic-gate 		if (bcmp(&wa->wa_fhandle, lp->fhp,
12220Sstevel@tonic-gate 		    sizeof (fhandle_t)) == 0)
12230Sstevel@tonic-gate 			break;
12240Sstevel@tonic-gate 	}
12250Sstevel@tonic-gate 
12260Sstevel@tonic-gate 	/*
12270Sstevel@tonic-gate 	 * If lp is non-NULL, then there is already a cluster
12280Sstevel@tonic-gate 	 * started.  We need to place ourselves in the cluster
12290Sstevel@tonic-gate 	 * list in the right place as determined by starting
12300Sstevel@tonic-gate 	 * offset.  Conflicts with non-blocking mandatory locked
12310Sstevel@tonic-gate 	 * regions will be checked when the cluster is processed.
12320Sstevel@tonic-gate 	 */
12330Sstevel@tonic-gate 	if (lp != NULL) {
12340Sstevel@tonic-gate 		rp = lp->list;
12350Sstevel@tonic-gate 		trp = NULL;
12360Sstevel@tonic-gate 		while (rp != NULL && rp->wa->wa_offset < wa->wa_offset) {
12370Sstevel@tonic-gate 			trp = rp;
12380Sstevel@tonic-gate 			rp = rp->list;
12390Sstevel@tonic-gate 		}
12400Sstevel@tonic-gate 		nrp->list = rp;
12410Sstevel@tonic-gate 		if (trp == NULL)
12420Sstevel@tonic-gate 			lp->list = nrp;
12430Sstevel@tonic-gate 		else
12440Sstevel@tonic-gate 			trp->list = nrp;
12450Sstevel@tonic-gate 		while (nrp->ns->ns_status == RFSWRITE_INITVAL)
12460Sstevel@tonic-gate 			cv_wait(&lp->cv, &rfs_async_write_lock);
12470Sstevel@tonic-gate 		mutex_exit(&rfs_async_write_lock);
12487387SRobert.Gordon@Sun.COM 
12490Sstevel@tonic-gate 		return;
12500Sstevel@tonic-gate 	}
12510Sstevel@tonic-gate 
12520Sstevel@tonic-gate 	/*
12530Sstevel@tonic-gate 	 * No cluster started yet, start one and add ourselves
12540Sstevel@tonic-gate 	 * to the list of clusters.
12550Sstevel@tonic-gate 	 */
12560Sstevel@tonic-gate 	nrp->list = NULL;
12570Sstevel@tonic-gate 
12580Sstevel@tonic-gate 	nlp = &nlpsp;
12590Sstevel@tonic-gate 	nlp->fhp = &wa->wa_fhandle;
12600Sstevel@tonic-gate 	cv_init(&nlp->cv, NULL, CV_DEFAULT, NULL);
12610Sstevel@tonic-gate 	nlp->list = nrp;
12620Sstevel@tonic-gate 	nlp->next = NULL;
12630Sstevel@tonic-gate 
12640Sstevel@tonic-gate 	if (rfs_async_write_head == NULL) {
12650Sstevel@tonic-gate 		rfs_async_write_head = nlp;
12660Sstevel@tonic-gate 	} else {
12670Sstevel@tonic-gate 		lp = rfs_async_write_head;
12680Sstevel@tonic-gate 		while (lp->next != NULL)
12690Sstevel@tonic-gate 			lp = lp->next;
12700Sstevel@tonic-gate 		lp->next = nlp;
12710Sstevel@tonic-gate 	}
12720Sstevel@tonic-gate 	mutex_exit(&rfs_async_write_lock);
12730Sstevel@tonic-gate 
12740Sstevel@tonic-gate 	/*
12750Sstevel@tonic-gate 	 * Convert the file handle common to all of the requests
12760Sstevel@tonic-gate 	 * in this cluster to a vnode.
12770Sstevel@tonic-gate 	 */
12780Sstevel@tonic-gate 	vp = nfs_fhtovp(&wa->wa_fhandle, exi);
12790Sstevel@tonic-gate 	if (vp == NULL) {
12800Sstevel@tonic-gate 		mutex_enter(&rfs_async_write_lock);
12810Sstevel@tonic-gate 		if (rfs_async_write_head == nlp)
12820Sstevel@tonic-gate 			rfs_async_write_head = nlp->next;
12830Sstevel@tonic-gate 		else {
12840Sstevel@tonic-gate 			lp = rfs_async_write_head;
12850Sstevel@tonic-gate 			while (lp->next != nlp)
12860Sstevel@tonic-gate 				lp = lp->next;
12870Sstevel@tonic-gate 			lp->next = nlp->next;
12880Sstevel@tonic-gate 		}
12890Sstevel@tonic-gate 		t_flag = curthread->t_flag & T_WOULDBLOCK;
12900Sstevel@tonic-gate 		for (rp = nlp->list; rp != NULL; rp = rp->list) {
12910Sstevel@tonic-gate 			rp->ns->ns_status = NFSERR_STALE;
12920Sstevel@tonic-gate 			rp->thread->t_flag |= t_flag;
12930Sstevel@tonic-gate 		}
12940Sstevel@tonic-gate 		cv_broadcast(&nlp->cv);
12950Sstevel@tonic-gate 		mutex_exit(&rfs_async_write_lock);
12967387SRobert.Gordon@Sun.COM 
12970Sstevel@tonic-gate 		return;
12980Sstevel@tonic-gate 	}
12990Sstevel@tonic-gate 
13000Sstevel@tonic-gate 	/*
13010Sstevel@tonic-gate 	 * Can only write regular files.  Attempts to write any
13020Sstevel@tonic-gate 	 * other file types fail with EISDIR.
13030Sstevel@tonic-gate 	 */
13040Sstevel@tonic-gate 	if (vp->v_type != VREG) {
13050Sstevel@tonic-gate 		VN_RELE(vp);
13060Sstevel@tonic-gate 		mutex_enter(&rfs_async_write_lock);
13070Sstevel@tonic-gate 		if (rfs_async_write_head == nlp)
13080Sstevel@tonic-gate 			rfs_async_write_head = nlp->next;
13090Sstevel@tonic-gate 		else {
13100Sstevel@tonic-gate 			lp = rfs_async_write_head;
13110Sstevel@tonic-gate 			while (lp->next != nlp)
13120Sstevel@tonic-gate 				lp = lp->next;
13130Sstevel@tonic-gate 			lp->next = nlp->next;
13140Sstevel@tonic-gate 		}
13150Sstevel@tonic-gate 		t_flag = curthread->t_flag & T_WOULDBLOCK;
13160Sstevel@tonic-gate 		for (rp = nlp->list; rp != NULL; rp = rp->list) {
13170Sstevel@tonic-gate 			rp->ns->ns_status = NFSERR_ISDIR;
13180Sstevel@tonic-gate 			rp->thread->t_flag |= t_flag;
13190Sstevel@tonic-gate 		}
13200Sstevel@tonic-gate 		cv_broadcast(&nlp->cv);
13210Sstevel@tonic-gate 		mutex_exit(&rfs_async_write_lock);
13227387SRobert.Gordon@Sun.COM 
13230Sstevel@tonic-gate 		return;
13240Sstevel@tonic-gate 	}
13250Sstevel@tonic-gate 
13260Sstevel@tonic-gate 	/*
13270Sstevel@tonic-gate 	 * Enter the critical region before calling VOP_RWLOCK, to avoid a
13280Sstevel@tonic-gate 	 * deadlock with ufs.
13290Sstevel@tonic-gate 	 */
13300Sstevel@tonic-gate 	if (nbl_need_check(vp)) {
13310Sstevel@tonic-gate 		nbl_start_crit(vp, RW_READER);
13320Sstevel@tonic-gate 		in_crit = 1;
13330Sstevel@tonic-gate 	}
13340Sstevel@tonic-gate 
13355599Sjwahlig 	ct.cc_sysid = 0;
13365599Sjwahlig 	ct.cc_pid = 0;
13375599Sjwahlig 	ct.cc_caller_id = nfs2_srv_caller_id;
13385599Sjwahlig 	ct.cc_flags = CC_DONTBLOCK;
13395599Sjwahlig 
13400Sstevel@tonic-gate 	/*
13410Sstevel@tonic-gate 	 * Lock the file for writing.  This operation provides
13420Sstevel@tonic-gate 	 * the delay which allows clusters to grow.
13430Sstevel@tonic-gate 	 */
13445599Sjwahlig 	error = VOP_RWLOCK(vp, V_WRITELOCK_TRUE, &ct);
13450Sstevel@tonic-gate 
13465599Sjwahlig 	/* check if a monitor detected a delegation conflict */
13475599Sjwahlig 	if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK)) {
13489075SNagakiran.Rajashekar@Sun.COM 		if (in_crit)
13499075SNagakiran.Rajashekar@Sun.COM 			nbl_end_crit(vp);
13505599Sjwahlig 		VN_RELE(vp);
13515599Sjwahlig 		/* mark as wouldblock so response is dropped */
13525599Sjwahlig 		curthread->t_flag |= T_WOULDBLOCK;
13535599Sjwahlig 		mutex_enter(&rfs_async_write_lock);
13549075SNagakiran.Rajashekar@Sun.COM 		if (rfs_async_write_head == nlp)
13559075SNagakiran.Rajashekar@Sun.COM 			rfs_async_write_head = nlp->next;
13569075SNagakiran.Rajashekar@Sun.COM 		else {
13579075SNagakiran.Rajashekar@Sun.COM 			lp = rfs_async_write_head;
13589075SNagakiran.Rajashekar@Sun.COM 			while (lp->next != nlp)
13599075SNagakiran.Rajashekar@Sun.COM 				lp = lp->next;
13609075SNagakiran.Rajashekar@Sun.COM 			lp->next = nlp->next;
13619075SNagakiran.Rajashekar@Sun.COM 		}
13625599Sjwahlig 		for (rp = nlp->list; rp != NULL; rp = rp->list) {
13635599Sjwahlig 			if (rp->ns->ns_status == RFSWRITE_INITVAL) {
13645599Sjwahlig 				rp->ns->ns_status = puterrno(error);
13655599Sjwahlig 				rp->thread->t_flag |= T_WOULDBLOCK;
13665599Sjwahlig 			}
13675599Sjwahlig 		}
13685599Sjwahlig 		cv_broadcast(&nlp->cv);
13695599Sjwahlig 		mutex_exit(&rfs_async_write_lock);
13707387SRobert.Gordon@Sun.COM 
13715599Sjwahlig 		return;
13725599Sjwahlig 	}
13735599Sjwahlig 
13740Sstevel@tonic-gate 	/*
13750Sstevel@tonic-gate 	 * Disconnect this cluster from the list of clusters.
13760Sstevel@tonic-gate 	 * The cluster that is being dealt with must be fixed
13770Sstevel@tonic-gate 	 * in size after this point, so there is no reason
13780Sstevel@tonic-gate 	 * to leave it on the list so that new requests can
13790Sstevel@tonic-gate 	 * find it.
13800Sstevel@tonic-gate 	 *
13810Sstevel@tonic-gate 	 * The algorithm is that the first write request will
13820Sstevel@tonic-gate 	 * create a cluster, convert the file handle to a
13830Sstevel@tonic-gate 	 * vnode pointer, and then lock the file for writing.
13840Sstevel@tonic-gate 	 * This request is not likely to be clustered with
13850Sstevel@tonic-gate 	 * any others.  However, the next request will create
13860Sstevel@tonic-gate 	 * a new cluster and be blocked in VOP_RWLOCK while
13870Sstevel@tonic-gate 	 * the first request is being processed.  This delay
13880Sstevel@tonic-gate 	 * will allow more requests to be clustered in this
13890Sstevel@tonic-gate 	 * second cluster.
13900Sstevel@tonic-gate 	 */
13910Sstevel@tonic-gate 	mutex_enter(&rfs_async_write_lock);
13920Sstevel@tonic-gate 	if (rfs_async_write_head == nlp)
13930Sstevel@tonic-gate 		rfs_async_write_head = nlp->next;
13940Sstevel@tonic-gate 	else {
13950Sstevel@tonic-gate 		lp = rfs_async_write_head;
13960Sstevel@tonic-gate 		while (lp->next != nlp)
13970Sstevel@tonic-gate 			lp = lp->next;
13980Sstevel@tonic-gate 		lp->next = nlp->next;
13990Sstevel@tonic-gate 	}
14000Sstevel@tonic-gate 	mutex_exit(&rfs_async_write_lock);
14010Sstevel@tonic-gate 
14020Sstevel@tonic-gate 	/*
14030Sstevel@tonic-gate 	 * Step through the list of requests in this cluster.
14040Sstevel@tonic-gate 	 * We need to check permissions to make sure that all
14050Sstevel@tonic-gate 	 * of the requests have sufficient permission to write
14060Sstevel@tonic-gate 	 * the file.  A cluster can be composed of requests
14070Sstevel@tonic-gate 	 * from different clients and different users on each
14080Sstevel@tonic-gate 	 * client.
14090Sstevel@tonic-gate 	 *
14100Sstevel@tonic-gate 	 * As a side effect, we also calculate the size of the
14110Sstevel@tonic-gate 	 * byte range that this cluster encompasses.
14120Sstevel@tonic-gate 	 */
14130Sstevel@tonic-gate 	rp = nlp->list;
14140Sstevel@tonic-gate 	off = rp->wa->wa_offset;
14150Sstevel@tonic-gate 	len = (uint_t)0;
14160Sstevel@tonic-gate 	do {
14170Sstevel@tonic-gate 		if (rdonly(exi, rp->req)) {
14180Sstevel@tonic-gate 			rp->ns->ns_status = NFSERR_ROFS;
14190Sstevel@tonic-gate 			t_flag = curthread->t_flag & T_WOULDBLOCK;
14200Sstevel@tonic-gate 			rp->thread->t_flag |= t_flag;
14210Sstevel@tonic-gate 			continue;
14220Sstevel@tonic-gate 		}
14230Sstevel@tonic-gate 
14240Sstevel@tonic-gate 		va.va_mask = AT_UID|AT_MODE;
14257387SRobert.Gordon@Sun.COM 
14265599Sjwahlig 		error = VOP_GETATTR(vp, &va, 0, rp->cr, &ct);
14277387SRobert.Gordon@Sun.COM 
14280Sstevel@tonic-gate 		if (!error) {
14290Sstevel@tonic-gate 			if (crgetuid(rp->cr) != va.va_uid) {
14300Sstevel@tonic-gate 				/*
14310Sstevel@tonic-gate 				 * This is a kludge to allow writes of files
14320Sstevel@tonic-gate 				 * created with read only permission.  The
14330Sstevel@tonic-gate 				 * owner of the file is always allowed to
14340Sstevel@tonic-gate 				 * write it.
14350Sstevel@tonic-gate 				 */
14365599Sjwahlig 				error = VOP_ACCESS(vp, VWRITE, 0, rp->cr, &ct);
14370Sstevel@tonic-gate 			}
14380Sstevel@tonic-gate 			if (!error && MANDLOCK(vp, va.va_mode))
14390Sstevel@tonic-gate 				error = EACCES;
14400Sstevel@tonic-gate 		}
14410Sstevel@tonic-gate 
14420Sstevel@tonic-gate 		/*
14430Sstevel@tonic-gate 		 * Check for a conflict with a nbmand-locked region.
14440Sstevel@tonic-gate 		 */
14450Sstevel@tonic-gate 		if (in_crit && nbl_conflict(vp, NBL_WRITE, rp->wa->wa_offset,
14465331Samw 		    rp->wa->wa_count, 0, NULL)) {
14470Sstevel@tonic-gate 			error = EACCES;
14480Sstevel@tonic-gate 		}
14490Sstevel@tonic-gate 
14500Sstevel@tonic-gate 		if (error) {
14510Sstevel@tonic-gate 			rp->ns->ns_status = puterrno(error);
14520Sstevel@tonic-gate 			t_flag = curthread->t_flag & T_WOULDBLOCK;
14530Sstevel@tonic-gate 			rp->thread->t_flag |= t_flag;
14540Sstevel@tonic-gate 			continue;
14550Sstevel@tonic-gate 		}
14560Sstevel@tonic-gate 		if (len < rp->wa->wa_offset + rp->wa->wa_count - off)
14570Sstevel@tonic-gate 			len = rp->wa->wa_offset + rp->wa->wa_count - off;
14580Sstevel@tonic-gate 	} while ((rp = rp->list) != NULL);
14590Sstevel@tonic-gate 
14600Sstevel@tonic-gate 	/*
14610Sstevel@tonic-gate 	 * Step through the cluster attempting to gather as many
14620Sstevel@tonic-gate 	 * requests which are contiguous as possible.  These
14630Sstevel@tonic-gate 	 * contiguous requests are handled via one call to VOP_WRITE
14640Sstevel@tonic-gate 	 * instead of different calls to VOP_WRITE.  We also keep
14650Sstevel@tonic-gate 	 * track of the fact that any data was written.
14660Sstevel@tonic-gate 	 */
14670Sstevel@tonic-gate 	rp = nlp->list;
14680Sstevel@tonic-gate 	data_written = 0;
14690Sstevel@tonic-gate 	do {
14700Sstevel@tonic-gate 		/*
14710Sstevel@tonic-gate 		 * Skip any requests which are already marked as having an
14720Sstevel@tonic-gate 		 * error.
14730Sstevel@tonic-gate 		 */
14740Sstevel@tonic-gate 		if (rp->ns->ns_status != RFSWRITE_INITVAL) {
14750Sstevel@tonic-gate 			rp = rp->list;
14760Sstevel@tonic-gate 			continue;
14770Sstevel@tonic-gate 		}
14780Sstevel@tonic-gate 
14790Sstevel@tonic-gate 		/*
14800Sstevel@tonic-gate 		 * Count the number of iovec's which are required
14810Sstevel@tonic-gate 		 * to handle this set of requests.  One iovec is
14820Sstevel@tonic-gate 		 * needed for each data buffer, whether addressed
14830Sstevel@tonic-gate 		 * by wa_data or by the b_rptr pointers in the
14840Sstevel@tonic-gate 		 * mblk chains.
14850Sstevel@tonic-gate 		 */
14860Sstevel@tonic-gate 		iovcnt = 0;
14870Sstevel@tonic-gate 		lrp = rp;
14880Sstevel@tonic-gate 		for (;;) {
14897387SRobert.Gordon@Sun.COM 			if (lrp->wa->wa_data || lrp->wa->wa_rlist)
14900Sstevel@tonic-gate 				iovcnt++;
14910Sstevel@tonic-gate 			else {
14920Sstevel@tonic-gate 				m = lrp->wa->wa_mblk;
14930Sstevel@tonic-gate 				while (m != NULL) {
14940Sstevel@tonic-gate 					iovcnt++;
14950Sstevel@tonic-gate 					m = m->b_cont;
14960Sstevel@tonic-gate 				}
14970Sstevel@tonic-gate 			}
14980Sstevel@tonic-gate 			if (lrp->list == NULL ||
14990Sstevel@tonic-gate 			    lrp->list->ns->ns_status != RFSWRITE_INITVAL ||
15000Sstevel@tonic-gate 			    lrp->wa->wa_offset + lrp->wa->wa_count !=
15010Sstevel@tonic-gate 			    lrp->list->wa->wa_offset) {
15020Sstevel@tonic-gate 				lrp = lrp->list;
15030Sstevel@tonic-gate 				break;
15040Sstevel@tonic-gate 			}
15050Sstevel@tonic-gate 			lrp = lrp->list;
15060Sstevel@tonic-gate 		}
15070Sstevel@tonic-gate 
15080Sstevel@tonic-gate 		if (iovcnt <= MAXCLIOVECS) {
15090Sstevel@tonic-gate #ifdef DEBUG
15100Sstevel@tonic-gate 			rfs_write_hits++;
15110Sstevel@tonic-gate #endif
15120Sstevel@tonic-gate 			niovp = iov;
15130Sstevel@tonic-gate 		} else {
15140Sstevel@tonic-gate #ifdef DEBUG
15150Sstevel@tonic-gate 			rfs_write_misses++;
15160Sstevel@tonic-gate #endif
15170Sstevel@tonic-gate 			niovp = kmem_alloc(sizeof (*niovp) * iovcnt, KM_SLEEP);
15180Sstevel@tonic-gate 		}
15190Sstevel@tonic-gate 		/*
15200Sstevel@tonic-gate 		 * Put together the scatter/gather iovecs.
15210Sstevel@tonic-gate 		 */
15220Sstevel@tonic-gate 		iovp = niovp;
15230Sstevel@tonic-gate 		trp = rp;
15240Sstevel@tonic-gate 		count = 0;
15250Sstevel@tonic-gate 		do {
15267387SRobert.Gordon@Sun.COM 			if (trp->wa->wa_data || trp->wa->wa_rlist) {
15277387SRobert.Gordon@Sun.COM 				if (trp->wa->wa_rlist) {
15287387SRobert.Gordon@Sun.COM 					iovp->iov_base =
15297387SRobert.Gordon@Sun.COM 					    (char *)((trp->wa->wa_rlist)->
15307387SRobert.Gordon@Sun.COM 					    u.c_daddr3);
15317387SRobert.Gordon@Sun.COM 					iovp->iov_len = trp->wa->wa_count;
15327387SRobert.Gordon@Sun.COM 				} else  {
15337387SRobert.Gordon@Sun.COM 					iovp->iov_base = trp->wa->wa_data;
15347387SRobert.Gordon@Sun.COM 					iovp->iov_len = trp->wa->wa_count;
15357387SRobert.Gordon@Sun.COM 				}
15360Sstevel@tonic-gate 				iovp++;
15370Sstevel@tonic-gate 			} else {
15380Sstevel@tonic-gate 				m = trp->wa->wa_mblk;
15390Sstevel@tonic-gate 				rcount = trp->wa->wa_count;
15400Sstevel@tonic-gate 				while (m != NULL) {
15410Sstevel@tonic-gate 					iovp->iov_base = (caddr_t)m->b_rptr;
15420Sstevel@tonic-gate 					iovp->iov_len = (m->b_wptr - m->b_rptr);
15430Sstevel@tonic-gate 					rcount -= iovp->iov_len;
15440Sstevel@tonic-gate 					if (rcount < 0)
15450Sstevel@tonic-gate 						iovp->iov_len += rcount;
15460Sstevel@tonic-gate 					iovp++;
15470Sstevel@tonic-gate 					if (rcount <= 0)
15480Sstevel@tonic-gate 						break;
15490Sstevel@tonic-gate 					m = m->b_cont;
15500Sstevel@tonic-gate 				}
15510Sstevel@tonic-gate 			}
15520Sstevel@tonic-gate 			count += trp->wa->wa_count;
15530Sstevel@tonic-gate 			trp = trp->list;
15540Sstevel@tonic-gate 		} while (trp != lrp);
15550Sstevel@tonic-gate 
15560Sstevel@tonic-gate 		uio.uio_iov = niovp;
15570Sstevel@tonic-gate 		uio.uio_iovcnt = iovcnt;
15580Sstevel@tonic-gate 		uio.uio_segflg = UIO_SYSSPACE;
15590Sstevel@tonic-gate 		uio.uio_extflg = UIO_COPY_DEFAULT;
15600Sstevel@tonic-gate 		uio.uio_loffset = (offset_t)rp->wa->wa_offset;
15610Sstevel@tonic-gate 		uio.uio_resid = count;
15620Sstevel@tonic-gate 		/*
15630Sstevel@tonic-gate 		 * The limit is checked on the client. We
15640Sstevel@tonic-gate 		 * should allow any size writes here.
15650Sstevel@tonic-gate 		 */
15660Sstevel@tonic-gate 		uio.uio_llimit = curproc->p_fsz_ctl;
15670Sstevel@tonic-gate 		rlimit = uio.uio_llimit - rp->wa->wa_offset;
15680Sstevel@tonic-gate 		if (rlimit < (rlim64_t)uio.uio_resid)
15690Sstevel@tonic-gate 			uio.uio_resid = (uint_t)rlimit;
15700Sstevel@tonic-gate 
15710Sstevel@tonic-gate 		/*
15720Sstevel@tonic-gate 		 * For now we assume no append mode.
15730Sstevel@tonic-gate 		 */
15740Sstevel@tonic-gate 
15750Sstevel@tonic-gate 		/*
15765599Sjwahlig 		 * We're changing creds because VM may fault
15775599Sjwahlig 		 * and we need the cred of the current
15785599Sjwahlig 		 * thread to be used if quota * checking is
15795599Sjwahlig 		 * enabled.
15800Sstevel@tonic-gate 		 */
15815599Sjwahlig 		savecred = curthread->t_cred;
15825599Sjwahlig 		curthread->t_cred = cr;
15835599Sjwahlig 		error = VOP_WRITE(vp, &uio, 0, rp->cr, &ct);
15845599Sjwahlig 		curthread->t_cred = savecred;
15855599Sjwahlig 
15865599Sjwahlig 		/* check if a monitor detected a delegation conflict */
15875599Sjwahlig 		if (error == EAGAIN && (ct.cc_flags & CC_WOULDBLOCK))
15885599Sjwahlig 			/* mark as wouldblock so response is dropped */
15890Sstevel@tonic-gate 			curthread->t_flag |= T_WOULDBLOCK;
15900Sstevel@tonic-gate 
15910Sstevel@tonic-gate 		if (niovp != iov)
15920Sstevel@tonic-gate 			kmem_free(niovp, sizeof (*niovp) * iovcnt);
15930Sstevel@tonic-gate 
15940Sstevel@tonic-gate 		if (!error) {
15950Sstevel@tonic-gate 			data_written = 1;
15960Sstevel@tonic-gate 			/*
15970Sstevel@tonic-gate 			 * Get attributes again so we send the latest mod
15980Sstevel@tonic-gate 			 * time to the client side for his cache.
15990Sstevel@tonic-gate 			 */
16000Sstevel@tonic-gate 			va.va_mask = AT_ALL;	/* now we want everything */
16017387SRobert.Gordon@Sun.COM 
16025599Sjwahlig 			error = VOP_GETATTR(vp, &va, 0, rp->cr, &ct);
16037387SRobert.Gordon@Sun.COM 
16040Sstevel@tonic-gate 			if (!error)
16050Sstevel@tonic-gate 				acl_perm(vp, exi, &va, rp->cr);
16060Sstevel@tonic-gate 		}
16070Sstevel@tonic-gate 
16080Sstevel@tonic-gate 		/*
16090Sstevel@tonic-gate 		 * Fill in the status responses for each request
16100Sstevel@tonic-gate 		 * which was just handled.  Also, copy the latest
16110Sstevel@tonic-gate 		 * attributes in to the attribute responses if
16120Sstevel@tonic-gate 		 * appropriate.
16130Sstevel@tonic-gate 		 */
16140Sstevel@tonic-gate 		t_flag = curthread->t_flag & T_WOULDBLOCK;
16150Sstevel@tonic-gate 		do {
16160Sstevel@tonic-gate 			rp->thread->t_flag |= t_flag;
16170Sstevel@tonic-gate 			/* check for overflows */
16180Sstevel@tonic-gate 			if (!error) {
16190Sstevel@tonic-gate 				error  = vattr_to_nattr(&va, &rp->ns->ns_attr);
16200Sstevel@tonic-gate 			}
16210Sstevel@tonic-gate 			rp->ns->ns_status = puterrno(error);
16220Sstevel@tonic-gate 			rp = rp->list;
16230Sstevel@tonic-gate 		} while (rp != lrp);
16240Sstevel@tonic-gate 	} while (rp != NULL);
16250Sstevel@tonic-gate 
16260Sstevel@tonic-gate 	/*
16270Sstevel@tonic-gate 	 * If any data was written at all, then we need to flush
16280Sstevel@tonic-gate 	 * the data and metadata to stable storage.
16290Sstevel@tonic-gate 	 */
16300Sstevel@tonic-gate 	if (data_written) {
16315599Sjwahlig 		error = VOP_PUTPAGE(vp, (u_offset_t)off, len, 0, cr, &ct);
16327387SRobert.Gordon@Sun.COM 
16330Sstevel@tonic-gate 		if (!error) {
16345599Sjwahlig 			error = VOP_FSYNC(vp, FNODSYNC, cr, &ct);
16350Sstevel@tonic-gate 		}
16360Sstevel@tonic-gate 	}
16370Sstevel@tonic-gate 
16385599Sjwahlig 	VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, &ct);
16390Sstevel@tonic-gate 
16400Sstevel@tonic-gate 	if (in_crit)
16410Sstevel@tonic-gate 		nbl_end_crit(vp);
16420Sstevel@tonic-gate 	VN_RELE(vp);
16430Sstevel@tonic-gate 
16440Sstevel@tonic-gate 	t_flag = curthread->t_flag & T_WOULDBLOCK;
16450Sstevel@tonic-gate 	mutex_enter(&rfs_async_write_lock);
16460Sstevel@tonic-gate 	for (rp = nlp->list; rp != NULL; rp = rp->list) {
16470Sstevel@tonic-gate 		if (rp->ns->ns_status == RFSWRITE_INITVAL) {
16480Sstevel@tonic-gate 			rp->ns->ns_status = puterrno(error);
16490Sstevel@tonic-gate 			rp->thread->t_flag |= t_flag;
16500Sstevel@tonic-gate 		}
16510Sstevel@tonic-gate 	}
16520Sstevel@tonic-gate 	cv_broadcast(&nlp->cv);
16530Sstevel@tonic-gate 	mutex_exit(&rfs_async_write_lock);
16540Sstevel@tonic-gate 
16550Sstevel@tonic-gate }
16560Sstevel@tonic-gate 
16571610Sthurlow void *
rfs_write_getfh(struct nfswriteargs * wa)16580Sstevel@tonic-gate rfs_write_getfh(struct nfswriteargs *wa)
16590Sstevel@tonic-gate {
16600Sstevel@tonic-gate 	return (&wa->wa_fhandle);
16610Sstevel@tonic-gate }
16620Sstevel@tonic-gate 
16630Sstevel@tonic-gate /*
16640Sstevel@tonic-gate  * Create a file.
16650Sstevel@tonic-gate  * Creates a file with given attributes and returns those attributes
16660Sstevel@tonic-gate  * and an fhandle for the new file.
16670Sstevel@tonic-gate  */
16680Sstevel@tonic-gate void
rfs_create(struct nfscreatargs * args,struct nfsdiropres * dr,struct exportinfo * exi,struct svc_req * req,cred_t * cr)16690Sstevel@tonic-gate rfs_create(struct nfscreatargs *args, struct nfsdiropres *dr,
16700Sstevel@tonic-gate 	struct exportinfo *exi, struct svc_req *req, cred_t *cr)
16710Sstevel@tonic-gate {
16720Sstevel@tonic-gate 	int error;
16730Sstevel@tonic-gate 	int lookuperr;
16740Sstevel@tonic-gate 	int in_crit = 0;
16750Sstevel@tonic-gate 	struct vattr va;
16760Sstevel@tonic-gate 	vnode_t *vp;
16776402Sgt29601 	vnode_t *realvp;
16780Sstevel@tonic-gate 	vnode_t *dvp;
16790Sstevel@tonic-gate 	char *name = args->ca_da.da_name;
16800Sstevel@tonic-gate 	vnode_t *tvp = NULL;
16810Sstevel@tonic-gate 	int mode;
16820Sstevel@tonic-gate 	int lookup_ok;
16830Sstevel@tonic-gate 	bool_t trunc;
16847961SNatalie.Li@Sun.COM 	struct sockaddr *ca;
16850Sstevel@tonic-gate 
16860Sstevel@tonic-gate 	/*
16870Sstevel@tonic-gate 	 * Disallow NULL paths
16880Sstevel@tonic-gate 	 */
16890Sstevel@tonic-gate 	if (name == NULL || *name == '\0') {
16900Sstevel@tonic-gate 		dr->dr_status = NFSERR_ACCES;
16910Sstevel@tonic-gate 		return;
16920Sstevel@tonic-gate 	}
16930Sstevel@tonic-gate 
16940Sstevel@tonic-gate 	dvp = nfs_fhtovp(args->ca_da.da_fhandle, exi);
16950Sstevel@tonic-gate 	if (dvp == NULL) {
16960Sstevel@tonic-gate 		dr->dr_status = NFSERR_STALE;
16970Sstevel@tonic-gate 		return;
16980Sstevel@tonic-gate 	}
16990Sstevel@tonic-gate 
17000Sstevel@tonic-gate 	error = sattr_to_vattr(args->ca_sa, &va);
17010Sstevel@tonic-gate 	if (error) {
17020Sstevel@tonic-gate 		dr->dr_status = puterrno(error);
17030Sstevel@tonic-gate 		return;
17040Sstevel@tonic-gate 	}
17050Sstevel@tonic-gate 
17060Sstevel@tonic-gate 	/*
17070Sstevel@tonic-gate 	 * Must specify the mode.
17080Sstevel@tonic-gate 	 */
17090Sstevel@tonic-gate 	if (!(va.va_mask & AT_MODE)) {
17100Sstevel@tonic-gate 		VN_RELE(dvp);
17110Sstevel@tonic-gate 		dr->dr_status = NFSERR_INVAL;
17120Sstevel@tonic-gate 		return;
17130Sstevel@tonic-gate 	}
17140Sstevel@tonic-gate 
17150Sstevel@tonic-gate 	/*
17160Sstevel@tonic-gate 	 * This is a completely gross hack to make mknod
17170Sstevel@tonic-gate 	 * work over the wire until we can wack the protocol
17180Sstevel@tonic-gate 	 */
17190Sstevel@tonic-gate 	if ((va.va_mode & IFMT) == IFCHR) {
17200Sstevel@tonic-gate 		if (args->ca_sa->sa_size == (uint_t)NFS_FIFO_DEV)
17210Sstevel@tonic-gate 			va.va_type = VFIFO;	/* xtra kludge for named pipe */
17220Sstevel@tonic-gate 		else {
17230Sstevel@tonic-gate 			va.va_type = VCHR;
17240Sstevel@tonic-gate 			/*
17250Sstevel@tonic-gate 			 * uncompress the received dev_t
17260Sstevel@tonic-gate 			 * if the top half is zero indicating a request
17270Sstevel@tonic-gate 			 * from an `older style' OS.
17280Sstevel@tonic-gate 			 */
17290Sstevel@tonic-gate 			if ((va.va_size & 0xffff0000) == 0)
17300Sstevel@tonic-gate 				va.va_rdev = nfsv2_expdev(va.va_size);
17310Sstevel@tonic-gate 			else
17320Sstevel@tonic-gate 				va.va_rdev = (dev_t)va.va_size;
17330Sstevel@tonic-gate 		}
17340Sstevel@tonic-gate 		va.va_mask &= ~AT_SIZE;
17350Sstevel@tonic-gate 	} else if ((va.va_mode & IFMT) == IFBLK) {
17360Sstevel@tonic-gate 		va.va_type = VBLK;
17370Sstevel@tonic-gate 		/*
17380Sstevel@tonic-gate 		 * uncompress the received dev_t
17390Sstevel@tonic-gate 		 * if the top half is zero indicating a request
17400Sstevel@tonic-gate 		 * from an `older style' OS.
17410Sstevel@tonic-gate 		 */
17420Sstevel@tonic-gate 		if ((va.va_size & 0xffff0000) == 0)
17430Sstevel@tonic-gate 			va.va_rdev = nfsv2_expdev(va.va_size);
17440Sstevel@tonic-gate 		else
17450Sstevel@tonic-gate 			va.va_rdev = (dev_t)va.va_size;
17460Sstevel@tonic-gate 		va.va_mask &= ~AT_SIZE;
17470Sstevel@tonic-gate 	} else if ((va.va_mode & IFMT) == IFSOCK) {
17480Sstevel@tonic-gate 		va.va_type = VSOCK;
17497961SNatalie.Li@Sun.COM 	} else {
17500Sstevel@tonic-gate 		va.va_type = VREG;
17517961SNatalie.Li@Sun.COM 	}
17520Sstevel@tonic-gate 	va.va_mode &= ~IFMT;
17530Sstevel@tonic-gate 	va.va_mask |= AT_TYPE;
17540Sstevel@tonic-gate 
17557961SNatalie.Li@Sun.COM 	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
17567961SNatalie.Li@Sun.COM 	name = nfscmd_convname(ca, exi, name, NFSCMD_CONV_INBOUND,
17577961SNatalie.Li@Sun.COM 	    MAXPATHLEN);
17587961SNatalie.Li@Sun.COM 	if (name == NULL) {
17597961SNatalie.Li@Sun.COM 		dr->dr_status = puterrno(EINVAL);
17607961SNatalie.Li@Sun.COM 		return;
17617961SNatalie.Li@Sun.COM 	}
17627961SNatalie.Li@Sun.COM 
17630Sstevel@tonic-gate 	/*
17640Sstevel@tonic-gate 	 * Why was the choice made to use VWRITE as the mode to the
17650Sstevel@tonic-gate 	 * call to VOP_CREATE ? This results in a bug.  When a client
17660Sstevel@tonic-gate 	 * opens a file that already exists and is RDONLY, the second
17670Sstevel@tonic-gate 	 * open fails with an EACESS because of the mode.
17680Sstevel@tonic-gate 	 * bug ID 1054648.
17690Sstevel@tonic-gate 	 */
17700Sstevel@tonic-gate 	lookup_ok = 0;
17710Sstevel@tonic-gate 	mode = VWRITE;
17720Sstevel@tonic-gate 	if (!(va.va_mask & AT_SIZE) || va.va_type != VREG) {
17735331Samw 		error = VOP_LOOKUP(dvp, name, &tvp, NULL, 0, NULL, cr,
17745331Samw 		    NULL, NULL, NULL);
17750Sstevel@tonic-gate 		if (!error) {
17760Sstevel@tonic-gate 			struct vattr at;
17770Sstevel@tonic-gate 
17780Sstevel@tonic-gate 			lookup_ok = 1;
17790Sstevel@tonic-gate 			at.va_mask = AT_MODE;
17805331Samw 			error = VOP_GETATTR(tvp, &at, 0, cr, NULL);
17810Sstevel@tonic-gate 			if (!error)
17820Sstevel@tonic-gate 				mode = (at.va_mode & S_IWUSR) ? VWRITE : VREAD;
17830Sstevel@tonic-gate 			VN_RELE(tvp);
17840Sstevel@tonic-gate 			tvp = NULL;
17850Sstevel@tonic-gate 		}
17860Sstevel@tonic-gate 	}
17870Sstevel@tonic-gate 
17880Sstevel@tonic-gate 	if (!lookup_ok) {
17890Sstevel@tonic-gate 		if (rdonly(exi, req)) {
17900Sstevel@tonic-gate 			error = EROFS;
17910Sstevel@tonic-gate 		} else if (va.va_type != VREG && va.va_type != VFIFO &&
17920Sstevel@tonic-gate 		    va.va_type != VSOCK && secpolicy_sys_devices(cr) != 0) {
17930Sstevel@tonic-gate 			error = EPERM;
17940Sstevel@tonic-gate 		} else {
17950Sstevel@tonic-gate 			error = 0;
17960Sstevel@tonic-gate 		}
17970Sstevel@tonic-gate 	}
17980Sstevel@tonic-gate 
17990Sstevel@tonic-gate 	/*
18000Sstevel@tonic-gate 	 * If file size is being modified on an already existing file
18010Sstevel@tonic-gate 	 * make sure that there are no conflicting non-blocking mandatory
18020Sstevel@tonic-gate 	 * locks in the region being manipulated. Return EACCES if there
18030Sstevel@tonic-gate 	 * are conflicting locks.
18040Sstevel@tonic-gate 	 */
18050Sstevel@tonic-gate 	if (!error && (va.va_type == VREG) && (va.va_mask & AT_SIZE)) {
18065331Samw 		lookuperr = VOP_LOOKUP(dvp, name, &tvp, NULL, 0, NULL, cr,
18075331Samw 		    NULL, NULL, NULL);
18080Sstevel@tonic-gate 
18090Sstevel@tonic-gate 		if (!lookuperr &&
18100Sstevel@tonic-gate 		    rfs4_check_delegated(FWRITE, tvp, va.va_size == 0)) {
18110Sstevel@tonic-gate 			VN_RELE(tvp);
18120Sstevel@tonic-gate 			curthread->t_flag |= T_WOULDBLOCK;
18130Sstevel@tonic-gate 			goto out;
18140Sstevel@tonic-gate 		}
18150Sstevel@tonic-gate 
18160Sstevel@tonic-gate 		if (!lookuperr && nbl_need_check(tvp)) {
18170Sstevel@tonic-gate 			/*
18180Sstevel@tonic-gate 			 * The file exists. Now check if it has any
18190Sstevel@tonic-gate 			 * conflicting non-blocking mandatory locks
18200Sstevel@tonic-gate 			 * in the region being changed.
18210Sstevel@tonic-gate 			 */
18220Sstevel@tonic-gate 			struct vattr bva;
18230Sstevel@tonic-gate 			u_offset_t offset;
18240Sstevel@tonic-gate 			ssize_t length;
18250Sstevel@tonic-gate 
18260Sstevel@tonic-gate 			nbl_start_crit(tvp, RW_READER);
18270Sstevel@tonic-gate 			in_crit = 1;
18280Sstevel@tonic-gate 
18290Sstevel@tonic-gate 			bva.va_mask = AT_SIZE;
18305331Samw 			error = VOP_GETATTR(tvp, &bva, 0, cr, NULL);
18310Sstevel@tonic-gate 			if (!error) {
18320Sstevel@tonic-gate 				if (va.va_size < bva.va_size) {
18330Sstevel@tonic-gate 					offset = va.va_size;
18340Sstevel@tonic-gate 					length = bva.va_size - va.va_size;
18350Sstevel@tonic-gate 				} else {
18360Sstevel@tonic-gate 					offset = bva.va_size;
18370Sstevel@tonic-gate 					length = va.va_size - bva.va_size;
18380Sstevel@tonic-gate 				}
18390Sstevel@tonic-gate 				if (length) {
18400Sstevel@tonic-gate 					if (nbl_conflict(tvp, NBL_WRITE,
18415331Samw 					    offset, length, 0, NULL)) {
18420Sstevel@tonic-gate 						error = EACCES;
18430Sstevel@tonic-gate 					}
18440Sstevel@tonic-gate 				}
18450Sstevel@tonic-gate 			}
18460Sstevel@tonic-gate 			if (error) {
18470Sstevel@tonic-gate 				nbl_end_crit(tvp);
18480Sstevel@tonic-gate 				VN_RELE(tvp);
18490Sstevel@tonic-gate 				in_crit = 0;
18500Sstevel@tonic-gate 			}
18510Sstevel@tonic-gate 		} else if (tvp != NULL) {
18520Sstevel@tonic-gate 			VN_RELE(tvp);
18530Sstevel@tonic-gate 		}
18540Sstevel@tonic-gate 	}
18550Sstevel@tonic-gate 
18560Sstevel@tonic-gate 	if (!error) {
18570Sstevel@tonic-gate 		/*
18580Sstevel@tonic-gate 		 * If filesystem is shared with nosuid the remove any
18590Sstevel@tonic-gate 		 * setuid/setgid bits on create.
18600Sstevel@tonic-gate 		 */
18610Sstevel@tonic-gate 		if (va.va_type == VREG &&
18620Sstevel@tonic-gate 		    exi->exi_export.ex_flags & EX_NOSUID)
18630Sstevel@tonic-gate 			va.va_mode &= ~(VSUID | VSGID);
18640Sstevel@tonic-gate 
18655331Samw 		error = VOP_CREATE(dvp, name, &va, NONEXCL, mode, &vp, cr, 0,
18665331Samw 		    NULL, NULL);
18670Sstevel@tonic-gate 
18680Sstevel@tonic-gate 		if (!error) {
18690Sstevel@tonic-gate 
18700Sstevel@tonic-gate 			if ((va.va_mask & AT_SIZE) && (va.va_size == 0))
18710Sstevel@tonic-gate 				trunc = TRUE;
18720Sstevel@tonic-gate 			else
18730Sstevel@tonic-gate 				trunc = FALSE;
18740Sstevel@tonic-gate 
18755050Sjwahlig 			if (rfs4_check_delegated(FWRITE, vp, trunc)) {
18765050Sjwahlig 				VN_RELE(vp);
18770Sstevel@tonic-gate 				curthread->t_flag |= T_WOULDBLOCK;
18780Sstevel@tonic-gate 				goto out;
18790Sstevel@tonic-gate 			}
18800Sstevel@tonic-gate 			va.va_mask = AT_ALL;
18817387SRobert.Gordon@Sun.COM 
18825331Samw 			error = VOP_GETATTR(vp, &va, 0, cr, NULL);
18837387SRobert.Gordon@Sun.COM 
18840Sstevel@tonic-gate 			/* check for overflows */
18850Sstevel@tonic-gate 			if (!error) {
18860Sstevel@tonic-gate 				acl_perm(vp, exi, &va, cr);
18870Sstevel@tonic-gate 				error = vattr_to_nattr(&va, &dr->dr_attr);
18880Sstevel@tonic-gate 				if (!error) {
18890Sstevel@tonic-gate 					error = makefh(&dr->dr_fhandle, vp,
18905050Sjwahlig 					    exi);
18910Sstevel@tonic-gate 				}
18920Sstevel@tonic-gate 			}
18930Sstevel@tonic-gate 			/*
18940Sstevel@tonic-gate 			 * Force modified metadata out to stable storage.
18956402Sgt29601 			 *
18966402Sgt29601 			 * if a underlying vp exists, pass it to VOP_FSYNC
18970Sstevel@tonic-gate 			 */
18986402Sgt29601 			if (VOP_REALVP(vp, &realvp, NULL) == 0)
18996402Sgt29601 				(void) VOP_FSYNC(realvp, FNODSYNC, cr, NULL);
19006402Sgt29601 			else
19016402Sgt29601 				(void) VOP_FSYNC(vp, FNODSYNC, cr, NULL);
19020Sstevel@tonic-gate 			VN_RELE(vp);
19030Sstevel@tonic-gate 		}
19040Sstevel@tonic-gate 
19050Sstevel@tonic-gate 		if (in_crit) {
19060Sstevel@tonic-gate 			nbl_end_crit(tvp);
19070Sstevel@tonic-gate 			VN_RELE(tvp);
19080Sstevel@tonic-gate 		}
19090Sstevel@tonic-gate 	}
19100Sstevel@tonic-gate 
19110Sstevel@tonic-gate 	/*
19120Sstevel@tonic-gate 	 * Force modified data and metadata out to stable storage.
19130Sstevel@tonic-gate 	 */
19145331Samw 	(void) VOP_FSYNC(dvp, 0, cr, NULL);
19150Sstevel@tonic-gate 
19160Sstevel@tonic-gate out:
19170Sstevel@tonic-gate 
19180Sstevel@tonic-gate 	VN_RELE(dvp);
19190Sstevel@tonic-gate 
19200Sstevel@tonic-gate 	dr->dr_status = puterrno(error);
19210Sstevel@tonic-gate 
19227961SNatalie.Li@Sun.COM 	if (name != args->ca_da.da_name)
19237961SNatalie.Li@Sun.COM 		kmem_free(name, MAXPATHLEN);
19240Sstevel@tonic-gate }
19251610Sthurlow void *
rfs_create_getfh(struct nfscreatargs * args)19260Sstevel@tonic-gate rfs_create_getfh(struct nfscreatargs *args)
19270Sstevel@tonic-gate {
19280Sstevel@tonic-gate 	return (args->ca_da.da_fhandle);
19290Sstevel@tonic-gate }
19300Sstevel@tonic-gate 
19310Sstevel@tonic-gate /*
19320Sstevel@tonic-gate  * Remove a file.
19330Sstevel@tonic-gate  * Remove named file from parent directory.
19340Sstevel@tonic-gate  */
19350Sstevel@tonic-gate void
rfs_remove(struct nfsdiropargs * da,enum nfsstat * status,struct exportinfo * exi,struct svc_req * req,cred_t * cr)19360Sstevel@tonic-gate rfs_remove(struct nfsdiropargs *da, enum nfsstat *status,
19370Sstevel@tonic-gate 	struct exportinfo *exi, struct svc_req *req, cred_t *cr)
19380Sstevel@tonic-gate {
19390Sstevel@tonic-gate 	int error = 0;
19400Sstevel@tonic-gate 	vnode_t *vp;
19410Sstevel@tonic-gate 	vnode_t *targvp;
19420Sstevel@tonic-gate 	int in_crit = 0;
19430Sstevel@tonic-gate 
19440Sstevel@tonic-gate 	/*
19450Sstevel@tonic-gate 	 * Disallow NULL paths
19460Sstevel@tonic-gate 	 */
19470Sstevel@tonic-gate 	if (da->da_name == NULL || *da->da_name == '\0') {
19480Sstevel@tonic-gate 		*status = NFSERR_ACCES;
19490Sstevel@tonic-gate 		return;
19500Sstevel@tonic-gate 	}
19510Sstevel@tonic-gate 
19520Sstevel@tonic-gate 	vp = nfs_fhtovp(da->da_fhandle, exi);
19530Sstevel@tonic-gate 	if (vp == NULL) {
19540Sstevel@tonic-gate 		*status = NFSERR_STALE;
19550Sstevel@tonic-gate 		return;
19560Sstevel@tonic-gate 	}
19570Sstevel@tonic-gate 
19580Sstevel@tonic-gate 	if (rdonly(exi, req)) {
19590Sstevel@tonic-gate 		VN_RELE(vp);
19600Sstevel@tonic-gate 		*status = NFSERR_ROFS;
19610Sstevel@tonic-gate 		return;
19620Sstevel@tonic-gate 	}
19630Sstevel@tonic-gate 
19640Sstevel@tonic-gate 	/*
19650Sstevel@tonic-gate 	 * Check for a conflict with a non-blocking mandatory share reservation.
19660Sstevel@tonic-gate 	 */
19675331Samw 	error = VOP_LOOKUP(vp, da->da_name, &targvp, NULL, 0,
19685331Samw 	    NULL, cr, NULL, NULL, NULL);
19690Sstevel@tonic-gate 	if (error != 0) {
19700Sstevel@tonic-gate 		VN_RELE(vp);
19710Sstevel@tonic-gate 		*status = puterrno(error);
19720Sstevel@tonic-gate 		return;
19730Sstevel@tonic-gate 	}
19740Sstevel@tonic-gate 
19750Sstevel@tonic-gate 	/*
19760Sstevel@tonic-gate 	 * If the file is delegated to an v4 client, then initiate
19770Sstevel@tonic-gate 	 * recall and drop this request (by setting T_WOULDBLOCK).
19780Sstevel@tonic-gate 	 * The client will eventually re-transmit the request and
19790Sstevel@tonic-gate 	 * (hopefully), by then, the v4 client will have returned
19800Sstevel@tonic-gate 	 * the delegation.
19810Sstevel@tonic-gate 	 */
19820Sstevel@tonic-gate 
19830Sstevel@tonic-gate 	if (rfs4_check_delegated(FWRITE, targvp, TRUE)) {
19840Sstevel@tonic-gate 		VN_RELE(vp);
19850Sstevel@tonic-gate 		VN_RELE(targvp);
19860Sstevel@tonic-gate 		curthread->t_flag |= T_WOULDBLOCK;
19870Sstevel@tonic-gate 		return;
19880Sstevel@tonic-gate 	}
19890Sstevel@tonic-gate 
19900Sstevel@tonic-gate 	if (nbl_need_check(targvp)) {
19910Sstevel@tonic-gate 		nbl_start_crit(targvp, RW_READER);
19920Sstevel@tonic-gate 		in_crit = 1;
19935331Samw 		if (nbl_conflict(targvp, NBL_REMOVE, 0, 0, 0, NULL)) {
19940Sstevel@tonic-gate 			error = EACCES;
19950Sstevel@tonic-gate 			goto out;
19960Sstevel@tonic-gate 		}
19970Sstevel@tonic-gate 	}
19980Sstevel@tonic-gate 
19995331Samw 	error = VOP_REMOVE(vp, da->da_name, cr, NULL, 0);
20000Sstevel@tonic-gate 
20010Sstevel@tonic-gate 	/*
20020Sstevel@tonic-gate 	 * Force modified data and metadata out to stable storage.
20030Sstevel@tonic-gate 	 */
20045331Samw 	(void) VOP_FSYNC(vp, 0, cr, NULL);
20050Sstevel@tonic-gate 
20060Sstevel@tonic-gate out:
20070Sstevel@tonic-gate 	if (in_crit)
20080Sstevel@tonic-gate 		nbl_end_crit(targvp);
20090Sstevel@tonic-gate 	VN_RELE(targvp);
20100Sstevel@tonic-gate 	VN_RELE(vp);
20110Sstevel@tonic-gate 
20120Sstevel@tonic-gate 	*status = puterrno(error);
20130Sstevel@tonic-gate 
20140Sstevel@tonic-gate }
20150Sstevel@tonic-gate 
20161610Sthurlow void *
rfs_remove_getfh(struct nfsdiropargs * da)20170Sstevel@tonic-gate rfs_remove_getfh(struct nfsdiropargs *da)
20180Sstevel@tonic-gate {
20190Sstevel@tonic-gate 	return (da->da_fhandle);
20200Sstevel@tonic-gate }
20210Sstevel@tonic-gate 
20220Sstevel@tonic-gate /*
20230Sstevel@tonic-gate  * rename a file
20240Sstevel@tonic-gate  * Give a file (from) a new name (to).
20250Sstevel@tonic-gate  */
20260Sstevel@tonic-gate void
rfs_rename(struct nfsrnmargs * args,enum nfsstat * status,struct exportinfo * exi,struct svc_req * req,cred_t * cr)20270Sstevel@tonic-gate rfs_rename(struct nfsrnmargs *args, enum nfsstat *status,
20280Sstevel@tonic-gate 	struct exportinfo *exi, struct svc_req *req, cred_t *cr)
20290Sstevel@tonic-gate {
20300Sstevel@tonic-gate 	int error = 0;
20310Sstevel@tonic-gate 	vnode_t *fromvp;
20320Sstevel@tonic-gate 	vnode_t *tovp;
20330Sstevel@tonic-gate 	struct exportinfo *to_exi;
20340Sstevel@tonic-gate 	fhandle_t *fh;
20350Sstevel@tonic-gate 	vnode_t *srcvp;
20360Sstevel@tonic-gate 	vnode_t *targvp;
20370Sstevel@tonic-gate 	int in_crit = 0;
20380Sstevel@tonic-gate 
20390Sstevel@tonic-gate 	fromvp = nfs_fhtovp(args->rna_from.da_fhandle, exi);
20400Sstevel@tonic-gate 	if (fromvp == NULL) {
20410Sstevel@tonic-gate 		*status = NFSERR_STALE;
20420Sstevel@tonic-gate 		return;
20430Sstevel@tonic-gate 	}
20440Sstevel@tonic-gate 
20450Sstevel@tonic-gate 	fh = args->rna_to.da_fhandle;
20460Sstevel@tonic-gate 	to_exi = checkexport(&fh->fh_fsid, (fid_t *)&fh->fh_xlen);
20470Sstevel@tonic-gate 	if (to_exi == NULL) {
20480Sstevel@tonic-gate 		VN_RELE(fromvp);
20490Sstevel@tonic-gate 		*status = NFSERR_ACCES;
20500Sstevel@tonic-gate 		return;
20510Sstevel@tonic-gate 	}
20520Sstevel@tonic-gate 	exi_rele(to_exi);
20530Sstevel@tonic-gate 
20540Sstevel@tonic-gate 	if (to_exi != exi) {
20550Sstevel@tonic-gate 		VN_RELE(fromvp);
20560Sstevel@tonic-gate 		*status = NFSERR_XDEV;
20570Sstevel@tonic-gate 		return;
20580Sstevel@tonic-gate 	}
20590Sstevel@tonic-gate 
20600Sstevel@tonic-gate 	tovp = nfs_fhtovp(args->rna_to.da_fhandle, exi);
20610Sstevel@tonic-gate 	if (tovp == NULL) {
20620Sstevel@tonic-gate 		VN_RELE(fromvp);
20630Sstevel@tonic-gate 		*status = NFSERR_STALE;
20640Sstevel@tonic-gate 		return;
20650Sstevel@tonic-gate 	}
20660Sstevel@tonic-gate 
20670Sstevel@tonic-gate 	if (fromvp->v_type != VDIR || tovp->v_type != VDIR) {
20680Sstevel@tonic-gate 		VN_RELE(tovp);
20690Sstevel@tonic-gate 		VN_RELE(fromvp);
20700Sstevel@tonic-gate 		*status = NFSERR_NOTDIR;
20710Sstevel@tonic-gate 		return;
20720Sstevel@tonic-gate 	}
20730Sstevel@tonic-gate 
20740Sstevel@tonic-gate 	/*
20750Sstevel@tonic-gate 	 * Disallow NULL paths
20760Sstevel@tonic-gate 	 */
20770Sstevel@tonic-gate 	if (args->rna_from.da_name == NULL || *args->rna_from.da_name == '\0' ||
20780Sstevel@tonic-gate 	    args->rna_to.da_name == NULL || *args->rna_to.da_name == '\0') {
20790Sstevel@tonic-gate 		VN_RELE(tovp);
20800Sstevel@tonic-gate 		VN_RELE(fromvp);
20810Sstevel@tonic-gate 		*status = NFSERR_ACCES;
20820Sstevel@tonic-gate 		return;
20830Sstevel@tonic-gate 	}
20840Sstevel@tonic-gate 
20850Sstevel@tonic-gate 	if (rdonly(exi, req)) {
20860Sstevel@tonic-gate 		VN_RELE(tovp);
20870Sstevel@tonic-gate 		VN_RELE(fromvp);
20880Sstevel@tonic-gate 		*status = NFSERR_ROFS;
20890Sstevel@tonic-gate 		return;
20900Sstevel@tonic-gate 	}
20910Sstevel@tonic-gate 
20920Sstevel@tonic-gate 	/*
20930Sstevel@tonic-gate 	 * Check for a conflict with a non-blocking mandatory share reservation.
20940Sstevel@tonic-gate 	 */
20950Sstevel@tonic-gate 	error = VOP_LOOKUP(fromvp, args->rna_from.da_name, &srcvp, NULL, 0,
20965331Samw 	    NULL, cr, NULL, NULL, NULL);
20970Sstevel@tonic-gate 	if (error != 0) {
20980Sstevel@tonic-gate 		VN_RELE(tovp);
20990Sstevel@tonic-gate 		VN_RELE(fromvp);
21000Sstevel@tonic-gate 		*status = puterrno(error);
21010Sstevel@tonic-gate 		return;
21020Sstevel@tonic-gate 	}
21030Sstevel@tonic-gate 
21040Sstevel@tonic-gate 	/* Check for delegations on the source file */
21050Sstevel@tonic-gate 
21060Sstevel@tonic-gate 	if (rfs4_check_delegated(FWRITE, srcvp, FALSE)) {
21070Sstevel@tonic-gate 		VN_RELE(tovp);
21080Sstevel@tonic-gate 		VN_RELE(fromvp);
21090Sstevel@tonic-gate 		VN_RELE(srcvp);
21100Sstevel@tonic-gate 		curthread->t_flag |= T_WOULDBLOCK;
21110Sstevel@tonic-gate 		return;
21120Sstevel@tonic-gate 	}
21130Sstevel@tonic-gate 
21140Sstevel@tonic-gate 	/* Check for delegation on the file being renamed over, if it exists */
21150Sstevel@tonic-gate 
21160Sstevel@tonic-gate 	if (rfs4_deleg_policy != SRV_NEVER_DELEGATE &&
21175331Samw 	    VOP_LOOKUP(tovp, args->rna_to.da_name, &targvp, NULL, 0, NULL, cr,
21185331Samw 	    NULL, NULL, NULL) == 0) {
21190Sstevel@tonic-gate 
21200Sstevel@tonic-gate 		if (rfs4_check_delegated(FWRITE, targvp, TRUE)) {
21210Sstevel@tonic-gate 			VN_RELE(tovp);
21220Sstevel@tonic-gate 			VN_RELE(fromvp);
21230Sstevel@tonic-gate 			VN_RELE(srcvp);
21240Sstevel@tonic-gate 			VN_RELE(targvp);
21250Sstevel@tonic-gate 			curthread->t_flag |= T_WOULDBLOCK;
21260Sstevel@tonic-gate 			return;
21270Sstevel@tonic-gate 		}
21280Sstevel@tonic-gate 		VN_RELE(targvp);
21290Sstevel@tonic-gate 	}
21300Sstevel@tonic-gate 
21310Sstevel@tonic-gate 
21320Sstevel@tonic-gate 	if (nbl_need_check(srcvp)) {
21330Sstevel@tonic-gate 		nbl_start_crit(srcvp, RW_READER);
21340Sstevel@tonic-gate 		in_crit = 1;
21355331Samw 		if (nbl_conflict(srcvp, NBL_RENAME, 0, 0, 0, NULL)) {
21360Sstevel@tonic-gate 			error = EACCES;
21370Sstevel@tonic-gate 			goto out;
21380Sstevel@tonic-gate 		}
21390Sstevel@tonic-gate 	}
21400Sstevel@tonic-gate 
21410Sstevel@tonic-gate 	error = VOP_RENAME(fromvp, args->rna_from.da_name,
21425331Samw 	    tovp, args->rna_to.da_name, cr, NULL, 0);
21430Sstevel@tonic-gate 
21446976Seschrock 	if (error == 0)
21456976Seschrock 		vn_renamepath(tovp, srcvp, args->rna_to.da_name,
21465050Sjwahlig 		    strlen(args->rna_to.da_name));
21471146Sjwahlig 
21480Sstevel@tonic-gate 	/*
21490Sstevel@tonic-gate 	 * Force modified data and metadata out to stable storage.
21500Sstevel@tonic-gate 	 */
21515331Samw 	(void) VOP_FSYNC(tovp, 0, cr, NULL);
21525331Samw 	(void) VOP_FSYNC(fromvp, 0, cr, NULL);
21530Sstevel@tonic-gate 
21540Sstevel@tonic-gate out:
21550Sstevel@tonic-gate 	if (in_crit)
21560Sstevel@tonic-gate 		nbl_end_crit(srcvp);
21570Sstevel@tonic-gate 	VN_RELE(srcvp);
21580Sstevel@tonic-gate 	VN_RELE(tovp);
21590Sstevel@tonic-gate 	VN_RELE(fromvp);
21600Sstevel@tonic-gate 
21610Sstevel@tonic-gate 	*status = puterrno(error);
21620Sstevel@tonic-gate 
21630Sstevel@tonic-gate }
21641610Sthurlow void *
rfs_rename_getfh(struct nfsrnmargs * args)21650Sstevel@tonic-gate rfs_rename_getfh(struct nfsrnmargs *args)
21660Sstevel@tonic-gate {
21670Sstevel@tonic-gate 	return (args->rna_from.da_fhandle);
21680Sstevel@tonic-gate }
21690Sstevel@tonic-gate 
21700Sstevel@tonic-gate /*
21710Sstevel@tonic-gate  * Link to a file.
21720Sstevel@tonic-gate  * Create a file (to) which is a hard link to the given file (from).
21730Sstevel@tonic-gate  */
21740Sstevel@tonic-gate void
rfs_link(struct nfslinkargs * args,enum nfsstat * status,struct exportinfo * exi,struct svc_req * req,cred_t * cr)21750Sstevel@tonic-gate rfs_link(struct nfslinkargs *args, enum nfsstat *status,
21760Sstevel@tonic-gate 	struct exportinfo *exi, struct svc_req *req, cred_t *cr)
21770Sstevel@tonic-gate {
21780Sstevel@tonic-gate 	int error;
21790Sstevel@tonic-gate 	vnode_t *fromvp;
21800Sstevel@tonic-gate 	vnode_t *tovp;
21810Sstevel@tonic-gate 	struct exportinfo *to_exi;
21820Sstevel@tonic-gate 	fhandle_t *fh;
21830Sstevel@tonic-gate 
21840Sstevel@tonic-gate 	fromvp = nfs_fhtovp(args->la_from, exi);
21850Sstevel@tonic-gate 	if (fromvp == NULL) {
21860Sstevel@tonic-gate 		*status = NFSERR_STALE;
21870Sstevel@tonic-gate 		return;
21880Sstevel@tonic-gate 	}
21890Sstevel@tonic-gate 
21900Sstevel@tonic-gate 	fh = args->la_to.da_fhandle;
21910Sstevel@tonic-gate 	to_exi = checkexport(&fh->fh_fsid, (fid_t *)&fh->fh_xlen);
21920Sstevel@tonic-gate 	if (to_exi == NULL) {
21930Sstevel@tonic-gate 		VN_RELE(fromvp);
21940Sstevel@tonic-gate 		*status = NFSERR_ACCES;
21950Sstevel@tonic-gate 		return;
21960Sstevel@tonic-gate 	}
21970Sstevel@tonic-gate 	exi_rele(to_exi);
21980Sstevel@tonic-gate 
21990Sstevel@tonic-gate 	if (to_exi != exi) {
22000Sstevel@tonic-gate 		VN_RELE(fromvp);
22010Sstevel@tonic-gate 		*status = NFSERR_XDEV;
22020Sstevel@tonic-gate 		return;
22030Sstevel@tonic-gate 	}
22040Sstevel@tonic-gate 
22050Sstevel@tonic-gate 	tovp = nfs_fhtovp(args->la_to.da_fhandle, exi);
22060Sstevel@tonic-gate 	if (tovp == NULL) {
22070Sstevel@tonic-gate 		VN_RELE(fromvp);
22080Sstevel@tonic-gate 		*status = NFSERR_STALE;
22090Sstevel@tonic-gate 		return;
22100Sstevel@tonic-gate 	}
22110Sstevel@tonic-gate 
22120Sstevel@tonic-gate 	if (tovp->v_type != VDIR) {
22130Sstevel@tonic-gate 		VN_RELE(tovp);
22140Sstevel@tonic-gate 		VN_RELE(fromvp);
22150Sstevel@tonic-gate 		*status = NFSERR_NOTDIR;
22160Sstevel@tonic-gate 		return;
22170Sstevel@tonic-gate 	}
22180Sstevel@tonic-gate 	/*
22190Sstevel@tonic-gate 	 * Disallow NULL paths
22200Sstevel@tonic-gate 	 */
22210Sstevel@tonic-gate 	if (args->la_to.da_name == NULL || *args->la_to.da_name == '\0') {
22220Sstevel@tonic-gate 		VN_RELE(tovp);
22230Sstevel@tonic-gate 		VN_RELE(fromvp);
22240Sstevel@tonic-gate 		*status = NFSERR_ACCES;
22250Sstevel@tonic-gate 		return;
22260Sstevel@tonic-gate 	}
22270Sstevel@tonic-gate 
22280Sstevel@tonic-gate 	if (rdonly(exi, req)) {
22290Sstevel@tonic-gate 		VN_RELE(tovp);
22300Sstevel@tonic-gate 		VN_RELE(fromvp);
22310Sstevel@tonic-gate 		*status = NFSERR_ROFS;
22320Sstevel@tonic-gate 		return;
22330Sstevel@tonic-gate 	}
22340Sstevel@tonic-gate 
22355331Samw 	error = VOP_LINK(tovp, fromvp, args->la_to.da_name, cr, NULL, 0);
22360Sstevel@tonic-gate 
22370Sstevel@tonic-gate 	/*
22380Sstevel@tonic-gate 	 * Force modified data and metadata out to stable storage.
22390Sstevel@tonic-gate 	 */
22405331Samw 	(void) VOP_FSYNC(tovp, 0, cr, NULL);
22415331Samw 	(void) VOP_FSYNC(fromvp, FNODSYNC, cr, NULL);
22420Sstevel@tonic-gate 
22430Sstevel@tonic-gate 	VN_RELE(tovp);
22440Sstevel@tonic-gate 	VN_RELE(fromvp);
22450Sstevel@tonic-gate 
22460Sstevel@tonic-gate 	*status = puterrno(error);
22470Sstevel@tonic-gate 
22480Sstevel@tonic-gate }
22491610Sthurlow void *
rfs_link_getfh(struct nfslinkargs * args)22500Sstevel@tonic-gate rfs_link_getfh(struct nfslinkargs *args)
22510Sstevel@tonic-gate {
22520Sstevel@tonic-gate 	return (args->la_from);
22530Sstevel@tonic-gate }
22540Sstevel@tonic-gate 
22550Sstevel@tonic-gate /*
22560Sstevel@tonic-gate  * Symbolicly link to a file.
22570Sstevel@tonic-gate  * Create a file (to) with the given attributes which is a symbolic link
22580Sstevel@tonic-gate  * to the given path name (to).
22590Sstevel@tonic-gate  */
22600Sstevel@tonic-gate void
rfs_symlink(struct nfsslargs * args,enum nfsstat * status,struct exportinfo * exi,struct svc_req * req,cred_t * cr)22610Sstevel@tonic-gate rfs_symlink(struct nfsslargs *args, enum nfsstat *status,
22620Sstevel@tonic-gate 	struct exportinfo *exi, struct svc_req *req, cred_t *cr)
22630Sstevel@tonic-gate {
22640Sstevel@tonic-gate 	int error;
22650Sstevel@tonic-gate 	struct vattr va;
22660Sstevel@tonic-gate 	vnode_t *vp;
22670Sstevel@tonic-gate 	vnode_t *svp;
22680Sstevel@tonic-gate 	int lerror;
22697961SNatalie.Li@Sun.COM 	struct sockaddr *ca;
22707961SNatalie.Li@Sun.COM 	char *name = NULL;
22710Sstevel@tonic-gate 
22720Sstevel@tonic-gate 	/*
22730Sstevel@tonic-gate 	 * Disallow NULL paths
22740Sstevel@tonic-gate 	 */
22750Sstevel@tonic-gate 	if (args->sla_from.da_name == NULL || *args->sla_from.da_name == '\0') {
22760Sstevel@tonic-gate 		*status = NFSERR_ACCES;
22770Sstevel@tonic-gate 		return;
22780Sstevel@tonic-gate 	}
22790Sstevel@tonic-gate 
22800Sstevel@tonic-gate 	vp = nfs_fhtovp(args->sla_from.da_fhandle, exi);
22810Sstevel@tonic-gate 	if (vp == NULL) {
22820Sstevel@tonic-gate 		*status = NFSERR_STALE;
22830Sstevel@tonic-gate 		return;
22840Sstevel@tonic-gate 	}
22850Sstevel@tonic-gate 
22860Sstevel@tonic-gate 	if (rdonly(exi, req)) {
22870Sstevel@tonic-gate 		VN_RELE(vp);
22880Sstevel@tonic-gate 		*status = NFSERR_ROFS;
22890Sstevel@tonic-gate 		return;
22900Sstevel@tonic-gate 	}
22910Sstevel@tonic-gate 
22920Sstevel@tonic-gate 	error = sattr_to_vattr(args->sla_sa, &va);
22930Sstevel@tonic-gate 	if (error) {
22940Sstevel@tonic-gate 		VN_RELE(vp);
22950Sstevel@tonic-gate 		*status = puterrno(error);
22960Sstevel@tonic-gate 		return;
22970Sstevel@tonic-gate 	}
22980Sstevel@tonic-gate 
22990Sstevel@tonic-gate 	if (!(va.va_mask & AT_MODE)) {
23000Sstevel@tonic-gate 		VN_RELE(vp);
23010Sstevel@tonic-gate 		*status = NFSERR_INVAL;
23020Sstevel@tonic-gate 		return;
23030Sstevel@tonic-gate 	}
23040Sstevel@tonic-gate 
23057961SNatalie.Li@Sun.COM 	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
23067961SNatalie.Li@Sun.COM 	name = nfscmd_convname(ca, exi, args->sla_tnm,
23077961SNatalie.Li@Sun.COM 	    NFSCMD_CONV_INBOUND, MAXPATHLEN);
23087961SNatalie.Li@Sun.COM 
23097961SNatalie.Li@Sun.COM 	if (name == NULL) {
23107961SNatalie.Li@Sun.COM 		*status = NFSERR_ACCES;
23117961SNatalie.Li@Sun.COM 		return;
23127961SNatalie.Li@Sun.COM 	}
23137961SNatalie.Li@Sun.COM 
23140Sstevel@tonic-gate 	va.va_type = VLNK;
23150Sstevel@tonic-gate 	va.va_mask |= AT_TYPE;
23160Sstevel@tonic-gate 
23177961SNatalie.Li@Sun.COM 	error = VOP_SYMLINK(vp, args->sla_from.da_name, &va, name, cr, NULL, 0);
23180Sstevel@tonic-gate 
23190Sstevel@tonic-gate 	/*
23200Sstevel@tonic-gate 	 * Force new data and metadata out to stable storage.
23210Sstevel@tonic-gate 	 */
23227961SNatalie.Li@Sun.COM 	lerror = VOP_LOOKUP(vp, args->sla_from.da_name, &svp, NULL, 0,
23237961SNatalie.Li@Sun.COM 	    NULL, cr, NULL, NULL, NULL);
23247387SRobert.Gordon@Sun.COM 
23250Sstevel@tonic-gate 	if (!lerror) {
23265331Samw 		(void) VOP_FSYNC(svp, 0, cr, NULL);
23270Sstevel@tonic-gate 		VN_RELE(svp);
23280Sstevel@tonic-gate 	}
23290Sstevel@tonic-gate 
23300Sstevel@tonic-gate 	/*
23310Sstevel@tonic-gate 	 * Force modified data and metadata out to stable storage.
23320Sstevel@tonic-gate 	 */
23335331Samw 	(void) VOP_FSYNC(vp, 0, cr, NULL);
23340Sstevel@tonic-gate 
23350Sstevel@tonic-gate 	VN_RELE(vp);
23360Sstevel@tonic-gate 
23370Sstevel@tonic-gate 	*status = puterrno(error);
23387961SNatalie.Li@Sun.COM 	if (name != args->sla_tnm)
23397961SNatalie.Li@Sun.COM 		kmem_free(name, MAXPATHLEN);
23400Sstevel@tonic-gate 
23410Sstevel@tonic-gate }
23421610Sthurlow void *
rfs_symlink_getfh(struct nfsslargs * args)23430Sstevel@tonic-gate rfs_symlink_getfh(struct nfsslargs *args)
23440Sstevel@tonic-gate {
23450Sstevel@tonic-gate 	return (args->sla_from.da_fhandle);
23460Sstevel@tonic-gate }
23470Sstevel@tonic-gate 
23480Sstevel@tonic-gate /*
23490Sstevel@tonic-gate  * Make a directory.
23500Sstevel@tonic-gate  * Create a directory with the given name, parent directory, and attributes.
23510Sstevel@tonic-gate  * Returns a file handle and attributes for the new directory.
23520Sstevel@tonic-gate  */
23530Sstevel@tonic-gate void
rfs_mkdir(struct nfscreatargs * args,struct nfsdiropres * dr,struct exportinfo * exi,struct svc_req * req,cred_t * cr)23540Sstevel@tonic-gate rfs_mkdir(struct nfscreatargs *args, struct nfsdiropres *dr,
23550Sstevel@tonic-gate 	struct exportinfo *exi, struct svc_req *req, cred_t *cr)
23560Sstevel@tonic-gate {
23570Sstevel@tonic-gate 	int error;
23580Sstevel@tonic-gate 	struct vattr va;
23590Sstevel@tonic-gate 	vnode_t *dvp = NULL;
23600Sstevel@tonic-gate 	vnode_t *vp;
23610Sstevel@tonic-gate 	char *name = args->ca_da.da_name;
23620Sstevel@tonic-gate 
23630Sstevel@tonic-gate 	/*
23640Sstevel@tonic-gate 	 * Disallow NULL paths
23650Sstevel@tonic-gate 	 */
23660Sstevel@tonic-gate 	if (name == NULL || *name == '\0') {
23670Sstevel@tonic-gate 		dr->dr_status = NFSERR_ACCES;
23680Sstevel@tonic-gate 		return;
23690Sstevel@tonic-gate 	}
23700Sstevel@tonic-gate 
23710Sstevel@tonic-gate 	vp = nfs_fhtovp(args->ca_da.da_fhandle, exi);
23720Sstevel@tonic-gate 	if (vp == NULL) {
23730Sstevel@tonic-gate 		dr->dr_status = NFSERR_STALE;
23740Sstevel@tonic-gate 		return;
23750Sstevel@tonic-gate 	}
23760Sstevel@tonic-gate 
23770Sstevel@tonic-gate 	if (rdonly(exi, req)) {
23780Sstevel@tonic-gate 		VN_RELE(vp);
23790Sstevel@tonic-gate 		dr->dr_status = NFSERR_ROFS;
23800Sstevel@tonic-gate 		return;
23810Sstevel@tonic-gate 	}
23820Sstevel@tonic-gate 
23830Sstevel@tonic-gate 	error = sattr_to_vattr(args->ca_sa, &va);
23840Sstevel@tonic-gate 	if (error) {
23850Sstevel@tonic-gate 		VN_RELE(vp);
23860Sstevel@tonic-gate 		dr->dr_status = puterrno(error);
23870Sstevel@tonic-gate 		return;
23880Sstevel@tonic-gate 	}
23890Sstevel@tonic-gate 
23900Sstevel@tonic-gate 	if (!(va.va_mask & AT_MODE)) {
23910Sstevel@tonic-gate 		VN_RELE(vp);
23920Sstevel@tonic-gate 		dr->dr_status = NFSERR_INVAL;
23930Sstevel@tonic-gate 		return;
23940Sstevel@tonic-gate 	}
23950Sstevel@tonic-gate 
23960Sstevel@tonic-gate 	va.va_type = VDIR;
23970Sstevel@tonic-gate 	va.va_mask |= AT_TYPE;
23980Sstevel@tonic-gate 
23995331Samw 	error = VOP_MKDIR(vp, name, &va, &dvp, cr, NULL, 0, NULL);
24000Sstevel@tonic-gate 
24010Sstevel@tonic-gate 	if (!error) {
24020Sstevel@tonic-gate 		/*
24030Sstevel@tonic-gate 		 * Attribtutes of the newly created directory should
24040Sstevel@tonic-gate 		 * be returned to the client.
24050Sstevel@tonic-gate 		 */
24060Sstevel@tonic-gate 		va.va_mask = AT_ALL; /* We want everything */
24075331Samw 		error = VOP_GETATTR(dvp, &va, 0, cr, NULL);
24087387SRobert.Gordon@Sun.COM 
24090Sstevel@tonic-gate 		/* check for overflows */
24100Sstevel@tonic-gate 		if (!error) {
24110Sstevel@tonic-gate 			acl_perm(vp, exi, &va, cr);
24120Sstevel@tonic-gate 			error = vattr_to_nattr(&va, &dr->dr_attr);
24130Sstevel@tonic-gate 			if (!error) {
24140Sstevel@tonic-gate 				error = makefh(&dr->dr_fhandle, dvp, exi);
24150Sstevel@tonic-gate 			}
24160Sstevel@tonic-gate 		}
24170Sstevel@tonic-gate 		/*
24180Sstevel@tonic-gate 		 * Force new data and metadata out to stable storage.
24190Sstevel@tonic-gate 		 */
24205331Samw 		(void) VOP_FSYNC(dvp, 0, cr, NULL);
24210Sstevel@tonic-gate 		VN_RELE(dvp);
24220Sstevel@tonic-gate 	}
24230Sstevel@tonic-gate 
24240Sstevel@tonic-gate 	/*
24250Sstevel@tonic-gate 	 * Force modified data and metadata out to stable storage.
24260Sstevel@tonic-gate 	 */
24275331Samw 	(void) VOP_FSYNC(vp, 0, cr, NULL);
24280Sstevel@tonic-gate 
24290Sstevel@tonic-gate 	VN_RELE(vp);
24300Sstevel@tonic-gate 
24310Sstevel@tonic-gate 	dr->dr_status = puterrno(error);
24320Sstevel@tonic-gate 
24330Sstevel@tonic-gate }
24341610Sthurlow void *
rfs_mkdir_getfh(struct nfscreatargs * args)24350Sstevel@tonic-gate rfs_mkdir_getfh(struct nfscreatargs *args)
24360Sstevel@tonic-gate {
24370Sstevel@tonic-gate 	return (args->ca_da.da_fhandle);
24380Sstevel@tonic-gate }
24390Sstevel@tonic-gate 
24400Sstevel@tonic-gate /*
24410Sstevel@tonic-gate  * Remove a directory.
24420Sstevel@tonic-gate  * Remove the given directory name from the given parent directory.
24430Sstevel@tonic-gate  */
24440Sstevel@tonic-gate void
rfs_rmdir(struct nfsdiropargs * da,enum nfsstat * status,struct exportinfo * exi,struct svc_req * req,cred_t * cr)24450Sstevel@tonic-gate rfs_rmdir(struct nfsdiropargs *da, enum nfsstat *status,
24460Sstevel@tonic-gate 	struct exportinfo *exi, struct svc_req *req, cred_t *cr)
24470Sstevel@tonic-gate {
24480Sstevel@tonic-gate 	int error;
24490Sstevel@tonic-gate 	vnode_t *vp;
24500Sstevel@tonic-gate 
24510Sstevel@tonic-gate 
24520Sstevel@tonic-gate 	/*
24530Sstevel@tonic-gate 	 * Disallow NULL paths
24540Sstevel@tonic-gate 	 */
24550Sstevel@tonic-gate 	if (da->da_name == NULL || *da->da_name == '\0') {
24560Sstevel@tonic-gate 		*status = NFSERR_ACCES;
24570Sstevel@tonic-gate 		return;
24580Sstevel@tonic-gate 	}
24590Sstevel@tonic-gate 
24600Sstevel@tonic-gate 	vp = nfs_fhtovp(da->da_fhandle, exi);
24610Sstevel@tonic-gate 	if (vp == NULL) {
24620Sstevel@tonic-gate 		*status = NFSERR_STALE;
24630Sstevel@tonic-gate 		return;
24640Sstevel@tonic-gate 	}
24650Sstevel@tonic-gate 
24660Sstevel@tonic-gate 	if (rdonly(exi, req)) {
24670Sstevel@tonic-gate 		VN_RELE(vp);
24680Sstevel@tonic-gate 		*status = NFSERR_ROFS;
24690Sstevel@tonic-gate 		return;
24700Sstevel@tonic-gate 	}
24710Sstevel@tonic-gate 
24720Sstevel@tonic-gate 	/*
24730Sstevel@tonic-gate 	 * VOP_RMDIR now takes a new third argument (the current
24740Sstevel@tonic-gate 	 * directory of the process).  That's because someone
24750Sstevel@tonic-gate 	 * wants to return EINVAL if one tries to remove ".".
24760Sstevel@tonic-gate 	 * Of course, NFS servers have no idea what their
24770Sstevel@tonic-gate 	 * clients' current directories are.  We fake it by
24780Sstevel@tonic-gate 	 * supplying a vnode known to exist and illegal to
24790Sstevel@tonic-gate 	 * remove.
24800Sstevel@tonic-gate 	 */
24815331Samw 	error = VOP_RMDIR(vp, da->da_name, rootdir, cr, NULL, 0);
24820Sstevel@tonic-gate 
24830Sstevel@tonic-gate 	/*
24840Sstevel@tonic-gate 	 * Force modified data and metadata out to stable storage.
24850Sstevel@tonic-gate 	 */
24865331Samw 	(void) VOP_FSYNC(vp, 0, cr, NULL);
24870Sstevel@tonic-gate 
24880Sstevel@tonic-gate 	VN_RELE(vp);
24890Sstevel@tonic-gate 
24900Sstevel@tonic-gate 	/*
24910Sstevel@tonic-gate 	 * System V defines rmdir to return EEXIST, not ENOTEMPTY,
24920Sstevel@tonic-gate 	 * if the directory is not empty.  A System V NFS server
24930Sstevel@tonic-gate 	 * needs to map NFSERR_EXIST to NFSERR_NOTEMPTY to transmit
24940Sstevel@tonic-gate 	 * over the wire.
24950Sstevel@tonic-gate 	 */
24960Sstevel@tonic-gate 	if (error == EEXIST)
24970Sstevel@tonic-gate 		*status = NFSERR_NOTEMPTY;
24980Sstevel@tonic-gate 	else
24990Sstevel@tonic-gate 		*status = puterrno(error);
25000Sstevel@tonic-gate 
25010Sstevel@tonic-gate }
25021610Sthurlow void *
rfs_rmdir_getfh(struct nfsdiropargs * da)25030Sstevel@tonic-gate rfs_rmdir_getfh(struct nfsdiropargs *da)
25040Sstevel@tonic-gate {
25050Sstevel@tonic-gate 	return (da->da_fhandle);
25060Sstevel@tonic-gate }
25070Sstevel@tonic-gate 
25080Sstevel@tonic-gate /* ARGSUSED */
25090Sstevel@tonic-gate void
rfs_readdir(struct nfsrddirargs * rda,struct nfsrddirres * rd,struct exportinfo * exi,struct svc_req * req,cred_t * cr)25100Sstevel@tonic-gate rfs_readdir(struct nfsrddirargs *rda, struct nfsrddirres *rd,
25110Sstevel@tonic-gate 	struct exportinfo *exi, struct svc_req *req, cred_t *cr)
25120Sstevel@tonic-gate {
25130Sstevel@tonic-gate 	int error;
25140Sstevel@tonic-gate 	int iseof;
25150Sstevel@tonic-gate 	struct iovec iov;
25160Sstevel@tonic-gate 	struct uio uio;
25170Sstevel@tonic-gate 	vnode_t *vp;
25187961SNatalie.Li@Sun.COM 	char *ndata = NULL;
25197961SNatalie.Li@Sun.COM 	struct sockaddr *ca;
25207961SNatalie.Li@Sun.COM 	size_t nents;
25217961SNatalie.Li@Sun.COM 	int ret;
25220Sstevel@tonic-gate 
25230Sstevel@tonic-gate 	vp = nfs_fhtovp(&rda->rda_fh, exi);
25240Sstevel@tonic-gate 	if (vp == NULL) {
25250Sstevel@tonic-gate 		rd->rd_entries = NULL;
25260Sstevel@tonic-gate 		rd->rd_status = NFSERR_STALE;
25270Sstevel@tonic-gate 		return;
25280Sstevel@tonic-gate 	}
25290Sstevel@tonic-gate 
25300Sstevel@tonic-gate 	if (vp->v_type != VDIR) {
25310Sstevel@tonic-gate 		VN_RELE(vp);
25320Sstevel@tonic-gate 		rd->rd_entries = NULL;
25330Sstevel@tonic-gate 		rd->rd_status = NFSERR_NOTDIR;
25340Sstevel@tonic-gate 		return;
25350Sstevel@tonic-gate 	}
25360Sstevel@tonic-gate 
25370Sstevel@tonic-gate 	(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
25387387SRobert.Gordon@Sun.COM 
25395331Samw 	error = VOP_ACCESS(vp, VREAD, 0, cr, NULL);
25407387SRobert.Gordon@Sun.COM 
25410Sstevel@tonic-gate 	if (error) {
25420Sstevel@tonic-gate 		rd->rd_entries = NULL;
25430Sstevel@tonic-gate 		goto bad;
25440Sstevel@tonic-gate 	}
25450Sstevel@tonic-gate 
25460Sstevel@tonic-gate 	if (rda->rda_count == 0) {
25470Sstevel@tonic-gate 		rd->rd_entries = NULL;
25480Sstevel@tonic-gate 		rd->rd_size = 0;
25490Sstevel@tonic-gate 		rd->rd_eof = FALSE;
25500Sstevel@tonic-gate 		goto bad;
25510Sstevel@tonic-gate 	}
25520Sstevel@tonic-gate 
25530Sstevel@tonic-gate 	rda->rda_count = MIN(rda->rda_count, NFS_MAXDATA);
25540Sstevel@tonic-gate 
25550Sstevel@tonic-gate 	/*
25560Sstevel@tonic-gate 	 * Allocate data for entries.  This will be freed by rfs_rddirfree.
25570Sstevel@tonic-gate 	 */
25580Sstevel@tonic-gate 	rd->rd_bufsize = (uint_t)rda->rda_count;
25590Sstevel@tonic-gate 	rd->rd_entries = kmem_alloc(rd->rd_bufsize, KM_SLEEP);
25600Sstevel@tonic-gate 
25610Sstevel@tonic-gate 	/*
25620Sstevel@tonic-gate 	 * Set up io vector to read directory data
25630Sstevel@tonic-gate 	 */
25640Sstevel@tonic-gate 	iov.iov_base = (caddr_t)rd->rd_entries;
25650Sstevel@tonic-gate 	iov.iov_len = rda->rda_count;
25660Sstevel@tonic-gate 	uio.uio_iov = &iov;
25670Sstevel@tonic-gate 	uio.uio_iovcnt = 1;
25680Sstevel@tonic-gate 	uio.uio_segflg = UIO_SYSSPACE;
25690Sstevel@tonic-gate 	uio.uio_extflg = UIO_COPY_CACHED;
25700Sstevel@tonic-gate 	uio.uio_loffset = (offset_t)rda->rda_offset;
25710Sstevel@tonic-gate 	uio.uio_resid = rda->rda_count;
25720Sstevel@tonic-gate 
25730Sstevel@tonic-gate 	/*
25740Sstevel@tonic-gate 	 * read directory
25750Sstevel@tonic-gate 	 */
25765331Samw 	error = VOP_READDIR(vp, &uio, cr, &iseof, NULL, 0);
25770Sstevel@tonic-gate 
25780Sstevel@tonic-gate 	/*
25790Sstevel@tonic-gate 	 * Clean up
25800Sstevel@tonic-gate 	 */
25810Sstevel@tonic-gate 	if (!error) {
25820Sstevel@tonic-gate 		/*
25830Sstevel@tonic-gate 		 * set size and eof
25840Sstevel@tonic-gate 		 */
25850Sstevel@tonic-gate 		if (uio.uio_resid == rda->rda_count) {
25860Sstevel@tonic-gate 			rd->rd_size = 0;
25870Sstevel@tonic-gate 			rd->rd_eof = TRUE;
25880Sstevel@tonic-gate 		} else {
25890Sstevel@tonic-gate 			rd->rd_size = (uint32_t)(rda->rda_count -
25900Sstevel@tonic-gate 			    uio.uio_resid);
25910Sstevel@tonic-gate 			rd->rd_eof = iseof ? TRUE : FALSE;
25920Sstevel@tonic-gate 		}
25930Sstevel@tonic-gate 	}
25940Sstevel@tonic-gate 
25957961SNatalie.Li@Sun.COM 	ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
25967961SNatalie.Li@Sun.COM 	nents = nfscmd_countents((char *)rd->rd_entries, rd->rd_size);
25977961SNatalie.Li@Sun.COM 	ret = nfscmd_convdirplus(ca, exi, (char *)rd->rd_entries, nents,
25987961SNatalie.Li@Sun.COM 	    rda->rda_count, &ndata);
25997961SNatalie.Li@Sun.COM 
26007961SNatalie.Li@Sun.COM 	if (ret != 0) {
26017961SNatalie.Li@Sun.COM 		size_t dropbytes;
26027961SNatalie.Li@Sun.COM 		/*
26037961SNatalie.Li@Sun.COM 		 * We had to drop one or more entries in order to fit
26047961SNatalie.Li@Sun.COM 		 * during the character conversion.  We need to patch
26057961SNatalie.Li@Sun.COM 		 * up the size and eof info.
26067961SNatalie.Li@Sun.COM 		 */
26077961SNatalie.Li@Sun.COM 		if (rd->rd_eof)
26087961SNatalie.Li@Sun.COM 			rd->rd_eof = FALSE;
26097961SNatalie.Li@Sun.COM 		dropbytes = nfscmd_dropped_entrysize(
26107961SNatalie.Li@Sun.COM 		    (struct dirent64 *)rd->rd_entries, nents, ret);
26117961SNatalie.Li@Sun.COM 		rd->rd_size -= dropbytes;
26127961SNatalie.Li@Sun.COM 	}
26137961SNatalie.Li@Sun.COM 	if (ndata == NULL) {
26147961SNatalie.Li@Sun.COM 		ndata = (char *)rd->rd_entries;
26157961SNatalie.Li@Sun.COM 	} else if (ndata != (char *)rd->rd_entries) {
26167961SNatalie.Li@Sun.COM 		kmem_free(rd->rd_entries, rd->rd_bufsize);
26177961SNatalie.Li@Sun.COM 		rd->rd_entries = (void *)ndata;
26187961SNatalie.Li@Sun.COM 		rd->rd_bufsize = rda->rda_count;
26197961SNatalie.Li@Sun.COM 	}
26207961SNatalie.Li@Sun.COM 
26210Sstevel@tonic-gate bad:
26220Sstevel@tonic-gate 	VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
26230Sstevel@tonic-gate 
26240Sstevel@tonic-gate #if 0 /* notyet */
26250Sstevel@tonic-gate 	/*
26260Sstevel@tonic-gate 	 * Don't do this.  It causes local disk writes when just
26270Sstevel@tonic-gate 	 * reading the file and the overhead is deemed larger
26280Sstevel@tonic-gate 	 * than the benefit.
26290Sstevel@tonic-gate 	 */
26300Sstevel@tonic-gate 	/*
26310Sstevel@tonic-gate 	 * Force modified metadata out to stable storage.
26320Sstevel@tonic-gate 	 */
26335331Samw 	(void) VOP_FSYNC(vp, FNODSYNC, cr, NULL);
26340Sstevel@tonic-gate #endif
26350Sstevel@tonic-gate 
26360Sstevel@tonic-gate 	VN_RELE(vp);
26370Sstevel@tonic-gate 
26380Sstevel@tonic-gate 	rd->rd_status = puterrno(error);
26390Sstevel@tonic-gate 
26400Sstevel@tonic-gate }
26411610Sthurlow void *
rfs_readdir_getfh(struct nfsrddirargs * rda)26420Sstevel@tonic-gate rfs_readdir_getfh(struct nfsrddirargs *rda)
26430Sstevel@tonic-gate {
26440Sstevel@tonic-gate 	return (&rda->rda_fh);
26450Sstevel@tonic-gate }
26460Sstevel@tonic-gate void
rfs_rddirfree(struct nfsrddirres * rd)26470Sstevel@tonic-gate rfs_rddirfree(struct nfsrddirres *rd)
26480Sstevel@tonic-gate {
26490Sstevel@tonic-gate 	if (rd->rd_entries != NULL)
26500Sstevel@tonic-gate 		kmem_free(rd->rd_entries, rd->rd_bufsize);
26510Sstevel@tonic-gate }
26520Sstevel@tonic-gate 
26530Sstevel@tonic-gate /* ARGSUSED */
26540Sstevel@tonic-gate void
rfs_statfs(fhandle_t * fh,struct nfsstatfs * fs,struct exportinfo * exi,struct svc_req * req,cred_t * cr)26550Sstevel@tonic-gate rfs_statfs(fhandle_t *fh, struct nfsstatfs *fs, struct exportinfo *exi,
26560Sstevel@tonic-gate 	struct svc_req *req, cred_t *cr)
26570Sstevel@tonic-gate {
26580Sstevel@tonic-gate 	int error;
26590Sstevel@tonic-gate 	struct statvfs64 sb;
26600Sstevel@tonic-gate 	vnode_t *vp;
26610Sstevel@tonic-gate 
26620Sstevel@tonic-gate 	vp = nfs_fhtovp(fh, exi);
26630Sstevel@tonic-gate 	if (vp == NULL) {
26640Sstevel@tonic-gate 		fs->fs_status = NFSERR_STALE;
26650Sstevel@tonic-gate 		return;
26660Sstevel@tonic-gate 	}
26670Sstevel@tonic-gate 
26680Sstevel@tonic-gate 	error = VFS_STATVFS(vp->v_vfsp, &sb);
26690Sstevel@tonic-gate 
26700Sstevel@tonic-gate 	if (!error) {
26710Sstevel@tonic-gate 		fs->fs_tsize = nfstsize();
26720Sstevel@tonic-gate 		fs->fs_bsize = sb.f_frsize;
26730Sstevel@tonic-gate 		fs->fs_blocks = sb.f_blocks;
26740Sstevel@tonic-gate 		fs->fs_bfree = sb.f_bfree;
26750Sstevel@tonic-gate 		fs->fs_bavail = sb.f_bavail;
26760Sstevel@tonic-gate 	}
26770Sstevel@tonic-gate 
26780Sstevel@tonic-gate 	VN_RELE(vp);
26790Sstevel@tonic-gate 
26800Sstevel@tonic-gate 	fs->fs_status = puterrno(error);
26810Sstevel@tonic-gate 
26820Sstevel@tonic-gate }
26831610Sthurlow void *
rfs_statfs_getfh(fhandle_t * fh)26840Sstevel@tonic-gate rfs_statfs_getfh(fhandle_t *fh)
26850Sstevel@tonic-gate {
26860Sstevel@tonic-gate 	return (fh);
26870Sstevel@tonic-gate }
26880Sstevel@tonic-gate 
26890Sstevel@tonic-gate static int
sattr_to_vattr(struct nfssattr * sa,struct vattr * vap)26900Sstevel@tonic-gate sattr_to_vattr(struct nfssattr *sa, struct vattr *vap)
26910Sstevel@tonic-gate {
26920Sstevel@tonic-gate 	vap->va_mask = 0;
26930Sstevel@tonic-gate 
26940Sstevel@tonic-gate 	/*
26950Sstevel@tonic-gate 	 * There was a sign extension bug in some VFS based systems
26960Sstevel@tonic-gate 	 * which stored the mode as a short.  When it would get
26970Sstevel@tonic-gate 	 * assigned to a u_long, no sign extension would occur.
26980Sstevel@tonic-gate 	 * It needed to, but this wasn't noticed because sa_mode
26990Sstevel@tonic-gate 	 * would then get assigned back to the short, thus ignoring
27000Sstevel@tonic-gate 	 * the upper 16 bits of sa_mode.
27010Sstevel@tonic-gate 	 *
27020Sstevel@tonic-gate 	 * To make this implementation work for both broken
27030Sstevel@tonic-gate 	 * clients and good clients, we check for both versions
27040Sstevel@tonic-gate 	 * of the mode.
27050Sstevel@tonic-gate 	 */
27060Sstevel@tonic-gate 	if (sa->sa_mode != (uint32_t)((ushort_t)-1) &&
27070Sstevel@tonic-gate 	    sa->sa_mode != (uint32_t)-1) {
27080Sstevel@tonic-gate 		vap->va_mask |= AT_MODE;
27090Sstevel@tonic-gate 		vap->va_mode = sa->sa_mode;
27100Sstevel@tonic-gate 	}
27110Sstevel@tonic-gate 	if (sa->sa_uid != (uint32_t)-1) {
27120Sstevel@tonic-gate 		vap->va_mask |= AT_UID;
27130Sstevel@tonic-gate 		vap->va_uid = sa->sa_uid;
27140Sstevel@tonic-gate 	}
27150Sstevel@tonic-gate 	if (sa->sa_gid != (uint32_t)-1) {
27160Sstevel@tonic-gate 		vap->va_mask |= AT_GID;
27170Sstevel@tonic-gate 		vap->va_gid = sa->sa_gid;
27180Sstevel@tonic-gate 	}
27190Sstevel@tonic-gate 	if (sa->sa_size != (uint32_t)-1) {
27200Sstevel@tonic-gate 		vap->va_mask |= AT_SIZE;
27210Sstevel@tonic-gate 		vap->va_size = sa->sa_size;
27220Sstevel@tonic-gate 	}
27230Sstevel@tonic-gate 	if (sa->sa_atime.tv_sec != (int32_t)-1 &&
27240Sstevel@tonic-gate 	    sa->sa_atime.tv_usec != (int32_t)-1) {
27250Sstevel@tonic-gate #ifndef _LP64
27260Sstevel@tonic-gate 		/* return error if time overflow */
27270Sstevel@tonic-gate 		if (!NFS2_TIME_OK(sa->sa_atime.tv_sec))
27280Sstevel@tonic-gate 			return (EOVERFLOW);
27290Sstevel@tonic-gate #endif
27300Sstevel@tonic-gate 		vap->va_mask |= AT_ATIME;
27310Sstevel@tonic-gate 		/*
27320Sstevel@tonic-gate 		 * nfs protocol defines times as unsigned so don't extend sign,
27330Sstevel@tonic-gate 		 * unless sysadmin set nfs_allow_preepoch_time.
27340Sstevel@tonic-gate 		 */
27350Sstevel@tonic-gate 		NFS_TIME_T_CONVERT(vap->va_atime.tv_sec, sa->sa_atime.tv_sec);
27360Sstevel@tonic-gate 		vap->va_atime.tv_nsec = (uint32_t)(sa->sa_atime.tv_usec * 1000);
27370Sstevel@tonic-gate 	}
27380Sstevel@tonic-gate 	if (sa->sa_mtime.tv_sec != (int32_t)-1 &&
27390Sstevel@tonic-gate 	    sa->sa_mtime.tv_usec != (int32_t)-1) {
27400Sstevel@tonic-gate #ifndef _LP64
27410Sstevel@tonic-gate 		/* return error if time overflow */
27420Sstevel@tonic-gate 		if (!NFS2_TIME_OK(sa->sa_mtime.tv_sec))
27430Sstevel@tonic-gate 			return (EOVERFLOW);
27440Sstevel@tonic-gate #endif
27450Sstevel@tonic-gate 		vap->va_mask |= AT_MTIME;
27460Sstevel@tonic-gate 		/*
27470Sstevel@tonic-gate 		 * nfs protocol defines times as unsigned so don't extend sign,
27480Sstevel@tonic-gate 		 * unless sysadmin set nfs_allow_preepoch_time.
27490Sstevel@tonic-gate 		 */
27500Sstevel@tonic-gate 		NFS_TIME_T_CONVERT(vap->va_mtime.tv_sec, sa->sa_mtime.tv_sec);
27510Sstevel@tonic-gate 		vap->va_mtime.tv_nsec = (uint32_t)(sa->sa_mtime.tv_usec * 1000);
27520Sstevel@tonic-gate 	}
27530Sstevel@tonic-gate 	return (0);
27540Sstevel@tonic-gate }
27550Sstevel@tonic-gate 
27560Sstevel@tonic-gate static enum nfsftype vt_to_nf[] = {
27570Sstevel@tonic-gate 	0, NFREG, NFDIR, NFBLK, NFCHR, NFLNK, 0, 0, 0, NFSOC, 0
27580Sstevel@tonic-gate };
27590Sstevel@tonic-gate 
27600Sstevel@tonic-gate /*
27610Sstevel@tonic-gate  * check the following fields for overflow: nodeid, size, and time.
27620Sstevel@tonic-gate  * There could be a problem when converting 64-bit LP64 fields
27630Sstevel@tonic-gate  * into 32-bit ones.  Return an error if there is an overflow.
27640Sstevel@tonic-gate  */
27650Sstevel@tonic-gate int
vattr_to_nattr(struct vattr * vap,struct nfsfattr * na)27660Sstevel@tonic-gate vattr_to_nattr(struct vattr *vap, struct nfsfattr *na)
27670Sstevel@tonic-gate {
27680Sstevel@tonic-gate 	ASSERT(vap->va_type >= VNON && vap->va_type <= VBAD);
27690Sstevel@tonic-gate 	na->na_type = vt_to_nf[vap->va_type];
27700Sstevel@tonic-gate 
27710Sstevel@tonic-gate 	if (vap->va_mode == (unsigned short) -1)
27720Sstevel@tonic-gate 		na->na_mode = (uint32_t)-1;
27730Sstevel@tonic-gate 	else
27740Sstevel@tonic-gate 		na->na_mode = VTTOIF(vap->va_type) | vap->va_mode;
27750Sstevel@tonic-gate 
27760Sstevel@tonic-gate 	if (vap->va_uid == (unsigned short)(-1))
27770Sstevel@tonic-gate 		na->na_uid = (uint32_t)(-1);
27780Sstevel@tonic-gate 	else if (vap->va_uid == UID_NOBODY)
27790Sstevel@tonic-gate 		na->na_uid = (uint32_t)NFS_UID_NOBODY;
27800Sstevel@tonic-gate 	else
27810Sstevel@tonic-gate 		na->na_uid = vap->va_uid;
27820Sstevel@tonic-gate 
27830Sstevel@tonic-gate 	if (vap->va_gid == (unsigned short)(-1))
27840Sstevel@tonic-gate 		na->na_gid = (uint32_t)-1;
27850Sstevel@tonic-gate 	else if (vap->va_gid == GID_NOBODY)
27860Sstevel@tonic-gate 		na->na_gid = (uint32_t)NFS_GID_NOBODY;
27870Sstevel@tonic-gate 	else
27880Sstevel@tonic-gate 		na->na_gid = vap->va_gid;
27890Sstevel@tonic-gate 
27900Sstevel@tonic-gate 	/*
27910Sstevel@tonic-gate 	 * Do we need to check fsid for overflow?  It is 64-bit in the
27920Sstevel@tonic-gate 	 * vattr, but are bigger than 32 bit values supported?
27930Sstevel@tonic-gate 	 */
27940Sstevel@tonic-gate 	na->na_fsid = vap->va_fsid;
27950Sstevel@tonic-gate 
27960Sstevel@tonic-gate 	na->na_nodeid = vap->va_nodeid;
27970Sstevel@tonic-gate 
27980Sstevel@tonic-gate 	/*
27990Sstevel@tonic-gate 	 * Check to make sure that the nodeid is representable over the
28000Sstevel@tonic-gate 	 * wire without losing bits.
28010Sstevel@tonic-gate 	 */
28020Sstevel@tonic-gate 	if (vap->va_nodeid != (u_longlong_t)na->na_nodeid)
28030Sstevel@tonic-gate 		return (EFBIG);
28040Sstevel@tonic-gate 	na->na_nlink = vap->va_nlink;
28050Sstevel@tonic-gate 
28060Sstevel@tonic-gate 	/*
28070Sstevel@tonic-gate 	 * Check for big files here, instead of at the caller.  See
28080Sstevel@tonic-gate 	 * comments in cstat for large special file explanation.
28090Sstevel@tonic-gate 	 */
28100Sstevel@tonic-gate 	if (vap->va_size > (u_longlong_t)MAXOFF32_T) {
28110Sstevel@tonic-gate 		if ((vap->va_type == VREG) || (vap->va_type == VDIR))
28120Sstevel@tonic-gate 			return (EFBIG);
28130Sstevel@tonic-gate 		if ((vap->va_type == VBLK) || (vap->va_type == VCHR)) {
28140Sstevel@tonic-gate 			/* UNKNOWN_SIZE | OVERFLOW */
28150Sstevel@tonic-gate 			na->na_size = MAXOFF32_T;
28160Sstevel@tonic-gate 		} else
28170Sstevel@tonic-gate 			na->na_size = vap->va_size;
28180Sstevel@tonic-gate 	} else
28190Sstevel@tonic-gate 		na->na_size = vap->va_size;
28200Sstevel@tonic-gate 
28210Sstevel@tonic-gate 	/*
28220Sstevel@tonic-gate 	 * If the vnode times overflow the 32-bit times that NFS2
28230Sstevel@tonic-gate 	 * uses on the wire then return an error.
28240Sstevel@tonic-gate 	 */
28250Sstevel@tonic-gate 	if (!NFS_VAP_TIME_OK(vap)) {
28260Sstevel@tonic-gate 		return (EOVERFLOW);
28270Sstevel@tonic-gate 	}
28280Sstevel@tonic-gate 	na->na_atime.tv_sec = vap->va_atime.tv_sec;
28290Sstevel@tonic-gate 	na->na_atime.tv_usec = vap->va_atime.tv_nsec / 1000;
28300Sstevel@tonic-gate 
28310Sstevel@tonic-gate 	na->na_mtime.tv_sec = vap->va_mtime.tv_sec;
28320Sstevel@tonic-gate 	na->na_mtime.tv_usec = vap->va_mtime.tv_nsec / 1000;
28330Sstevel@tonic-gate 
28340Sstevel@tonic-gate 	na->na_ctime.tv_sec = vap->va_ctime.tv_sec;
28350Sstevel@tonic-gate 	na->na_ctime.tv_usec = vap->va_ctime.tv_nsec / 1000;
28360Sstevel@tonic-gate 
28370Sstevel@tonic-gate 	/*
28380Sstevel@tonic-gate 	 * If the dev_t will fit into 16 bits then compress
28390Sstevel@tonic-gate 	 * it, otherwise leave it alone. See comments in
28400Sstevel@tonic-gate 	 * nfs_client.c.
28410Sstevel@tonic-gate 	 */
28420Sstevel@tonic-gate 	if (getminor(vap->va_rdev) <= SO4_MAXMIN &&
28430Sstevel@tonic-gate 	    getmajor(vap->va_rdev) <= SO4_MAXMAJ)
28440Sstevel@tonic-gate 		na->na_rdev = nfsv2_cmpdev(vap->va_rdev);
28450Sstevel@tonic-gate 	else
28460Sstevel@tonic-gate 		(void) cmpldev(&na->na_rdev, vap->va_rdev);
28470Sstevel@tonic-gate 
28480Sstevel@tonic-gate 	na->na_blocks = vap->va_nblocks;
28490Sstevel@tonic-gate 	na->na_blocksize = vap->va_blksize;
28500Sstevel@tonic-gate 
28510Sstevel@tonic-gate 	/*
28520Sstevel@tonic-gate 	 * This bit of ugliness is a *TEMPORARY* hack to preserve the
28530Sstevel@tonic-gate 	 * over-the-wire protocols for named-pipe vnodes.  It remaps the
28540Sstevel@tonic-gate 	 * VFIFO type to the special over-the-wire type. (see note in nfs.h)
28550Sstevel@tonic-gate 	 *
28560Sstevel@tonic-gate 	 * BUYER BEWARE:
28570Sstevel@tonic-gate 	 *  If you are porting the NFS to a non-Sun server, you probably
28580Sstevel@tonic-gate 	 *  don't want to include the following block of code.  The
28590Sstevel@tonic-gate 	 *  over-the-wire special file types will be changing with the
28600Sstevel@tonic-gate 	 *  NFS Protocol Revision.
28610Sstevel@tonic-gate 	 */
28620Sstevel@tonic-gate 	if (vap->va_type == VFIFO)
28630Sstevel@tonic-gate 		NA_SETFIFO(na);
28640Sstevel@tonic-gate 	return (0);
28650Sstevel@tonic-gate }
28660Sstevel@tonic-gate 
28670Sstevel@tonic-gate /*
28680Sstevel@tonic-gate  * acl v2 support: returns approximate permission.
28690Sstevel@tonic-gate  *	default: returns minimal permission (more restrictive)
28700Sstevel@tonic-gate  *	aclok: returns maximal permission (less restrictive)
28710Sstevel@tonic-gate  *	This routine changes the permissions that are alaredy in *va.
28720Sstevel@tonic-gate  *	If a file has minimal ACL, i.e. aclcnt == MIN_ACL_ENTRIES,
28730Sstevel@tonic-gate  *	CLASS_OBJ is always the same as GROUP_OBJ entry.
28740Sstevel@tonic-gate  */
28750Sstevel@tonic-gate static void
acl_perm(struct vnode * vp,struct exportinfo * exi,struct vattr * va,cred_t * cr)28760Sstevel@tonic-gate acl_perm(struct vnode *vp, struct exportinfo *exi, struct vattr *va, cred_t *cr)
28770Sstevel@tonic-gate {
28780Sstevel@tonic-gate 	vsecattr_t	vsa;
28790Sstevel@tonic-gate 	int		aclcnt;
28800Sstevel@tonic-gate 	aclent_t	*aclentp;
28810Sstevel@tonic-gate 	mode_t		mask_perm;
28820Sstevel@tonic-gate 	mode_t		grp_perm;
28830Sstevel@tonic-gate 	mode_t		other_perm;
28840Sstevel@tonic-gate 	mode_t		other_orig;
28850Sstevel@tonic-gate 	int		error;
28860Sstevel@tonic-gate 
28870Sstevel@tonic-gate 	/* dont care default acl */
28880Sstevel@tonic-gate 	vsa.vsa_mask = (VSA_ACL | VSA_ACLCNT);
28895331Samw 	error = VOP_GETSECATTR(vp, &vsa, 0, cr, NULL);
28900Sstevel@tonic-gate 
28910Sstevel@tonic-gate 	if (!error) {
28920Sstevel@tonic-gate 		aclcnt = vsa.vsa_aclcnt;
28930Sstevel@tonic-gate 		if (aclcnt > MIN_ACL_ENTRIES) {
28940Sstevel@tonic-gate 			/* non-trivial ACL */
28950Sstevel@tonic-gate 			aclentp = vsa.vsa_aclentp;
28960Sstevel@tonic-gate 			if (exi->exi_export.ex_flags & EX_ACLOK) {
28970Sstevel@tonic-gate 				/* maximal permissions */
28980Sstevel@tonic-gate 				grp_perm = 0;
28990Sstevel@tonic-gate 				other_perm = 0;
29000Sstevel@tonic-gate 				for (; aclcnt > 0; aclcnt--, aclentp++) {
29010Sstevel@tonic-gate 					switch (aclentp->a_type) {
29020Sstevel@tonic-gate 					case USER_OBJ:
29030Sstevel@tonic-gate 						break;
29040Sstevel@tonic-gate 					case USER:
29050Sstevel@tonic-gate 						grp_perm |=
29060Sstevel@tonic-gate 						    aclentp->a_perm << 3;
29070Sstevel@tonic-gate 						other_perm |= aclentp->a_perm;
29080Sstevel@tonic-gate 						break;
29090Sstevel@tonic-gate 					case GROUP_OBJ:
29100Sstevel@tonic-gate 						grp_perm |=
29110Sstevel@tonic-gate 						    aclentp->a_perm << 3;
29120Sstevel@tonic-gate 						break;
29130Sstevel@tonic-gate 					case GROUP:
29140Sstevel@tonic-gate 						other_perm |= aclentp->a_perm;
29150Sstevel@tonic-gate 						break;
29160Sstevel@tonic-gate 					case OTHER_OBJ:
29170Sstevel@tonic-gate 						other_orig = aclentp->a_perm;
29180Sstevel@tonic-gate 						break;
29190Sstevel@tonic-gate 					case CLASS_OBJ:
29200Sstevel@tonic-gate 						mask_perm = aclentp->a_perm;
29210Sstevel@tonic-gate 						break;
29220Sstevel@tonic-gate 					default:
29230Sstevel@tonic-gate 						break;
29240Sstevel@tonic-gate 					}
29250Sstevel@tonic-gate 				}
29260Sstevel@tonic-gate 				grp_perm &= mask_perm << 3;
29270Sstevel@tonic-gate 				other_perm &= mask_perm;
29280Sstevel@tonic-gate 				other_perm |= other_orig;
29290Sstevel@tonic-gate 
29300Sstevel@tonic-gate 			} else {
29310Sstevel@tonic-gate 				/* minimal permissions */
29320Sstevel@tonic-gate 				grp_perm = 070;
29330Sstevel@tonic-gate 				other_perm = 07;
29340Sstevel@tonic-gate 				for (; aclcnt > 0; aclcnt--, aclentp++) {
29350Sstevel@tonic-gate 					switch (aclentp->a_type) {
29360Sstevel@tonic-gate 					case USER_OBJ:
29370Sstevel@tonic-gate 						break;
29380Sstevel@tonic-gate 					case USER:
29390Sstevel@tonic-gate 					case CLASS_OBJ:
29400Sstevel@tonic-gate 						grp_perm &=
29410Sstevel@tonic-gate 						    aclentp->a_perm << 3;
29420Sstevel@tonic-gate 						other_perm &=
29430Sstevel@tonic-gate 						    aclentp->a_perm;
29440Sstevel@tonic-gate 						break;
29450Sstevel@tonic-gate 					case GROUP_OBJ:
29460Sstevel@tonic-gate 						grp_perm &=
29470Sstevel@tonic-gate 						    aclentp->a_perm << 3;
29480Sstevel@tonic-gate 						break;
29490Sstevel@tonic-gate 					case GROUP:
29500Sstevel@tonic-gate 						other_perm &=
29510Sstevel@tonic-gate 						    aclentp->a_perm;
29520Sstevel@tonic-gate 						break;
29530Sstevel@tonic-gate 					case OTHER_OBJ:
29540Sstevel@tonic-gate 						other_perm &=
29550Sstevel@tonic-gate 						    aclentp->a_perm;
29560Sstevel@tonic-gate 						break;
29570Sstevel@tonic-gate 					default:
29580Sstevel@tonic-gate 						break;
29590Sstevel@tonic-gate 					}
29600Sstevel@tonic-gate 				}
29610Sstevel@tonic-gate 			}
29620Sstevel@tonic-gate 			/* copy to va */
29630Sstevel@tonic-gate 			va->va_mode &= ~077;
29640Sstevel@tonic-gate 			va->va_mode |= grp_perm | other_perm;
29650Sstevel@tonic-gate 		}
29660Sstevel@tonic-gate 		if (vsa.vsa_aclcnt)
29670Sstevel@tonic-gate 			kmem_free(vsa.vsa_aclentp,
29680Sstevel@tonic-gate 			    vsa.vsa_aclcnt * sizeof (aclent_t));
29690Sstevel@tonic-gate 	}
29700Sstevel@tonic-gate }
29710Sstevel@tonic-gate 
29720Sstevel@tonic-gate void
rfs_srvrinit(void)29730Sstevel@tonic-gate rfs_srvrinit(void)
29740Sstevel@tonic-gate {
29750Sstevel@tonic-gate 	mutex_init(&rfs_async_write_lock, NULL, MUTEX_DEFAULT, NULL);
29765599Sjwahlig 	nfs2_srv_caller_id = fs_new_caller_id();
29770Sstevel@tonic-gate }
29780Sstevel@tonic-gate 
29790Sstevel@tonic-gate void
rfs_srvrfini(void)29800Sstevel@tonic-gate rfs_srvrfini(void)
29810Sstevel@tonic-gate {
29820Sstevel@tonic-gate 	mutex_destroy(&rfs_async_write_lock);
29830Sstevel@tonic-gate }
29847387SRobert.Gordon@Sun.COM 
29857387SRobert.Gordon@Sun.COM static int
rdma_setup_read_data2(struct nfsreadargs * ra,struct nfsrdresult * rr)29867387SRobert.Gordon@Sun.COM rdma_setup_read_data2(struct nfsreadargs *ra, struct nfsrdresult *rr)
29877387SRobert.Gordon@Sun.COM {
29887387SRobert.Gordon@Sun.COM 	struct clist	*wcl;
29899348SSiddheshwar.Mahesh@Sun.COM 	int		wlist_len;
29907387SRobert.Gordon@Sun.COM 	uint32_t	count = rr->rr_count;
29917387SRobert.Gordon@Sun.COM 
29927387SRobert.Gordon@Sun.COM 	wcl = ra->ra_wlist;
29939348SSiddheshwar.Mahesh@Sun.COM 
29949348SSiddheshwar.Mahesh@Sun.COM 	if (rdma_setup_read_chunks(wcl, count, &wlist_len) == FALSE) {
29957387SRobert.Gordon@Sun.COM 		return (FALSE);
29967387SRobert.Gordon@Sun.COM 	}
29977387SRobert.Gordon@Sun.COM 
29987387SRobert.Gordon@Sun.COM 	wcl = ra->ra_wlist;
29999348SSiddheshwar.Mahesh@Sun.COM 	rr->rr_ok.rrok_wlist_len = wlist_len;
30007387SRobert.Gordon@Sun.COM 	rr->rr_ok.rrok_wlist = wcl;
30017387SRobert.Gordon@Sun.COM 
30027387SRobert.Gordon@Sun.COM 	return (TRUE);
30037387SRobert.Gordon@Sun.COM }
3004