FreeBSD/sys/fs/nfsclient/nfs_clvnops.c
/*
 * nfs access vnode op.
 * For nfs version 2, just return ok. File accesses may fail later.
 * For nfs version 3, use the access rpc to check accessibility. If file modes
 * are changed on the server, accesses might still fail later.
 */
nfs access vnode op.
For nfs version 2, just return ok. File accesses may fail later.
For nfs version 3, use the access rpc to check accessibility. If file modes
are changed on the server, accesses might still fail later.
nfs access vnode op . For nfs version 2 just return ok . File accesses may fail later . For nfs version 3 use the access rpc to check accessibility . If file modes are changed on the server accesses might still fail later .
static int
nfs_access(struct vop_access_args *ap)
{
	struct vnode *vp = ap->a_vp;
	int error = 0, i, gotahit;
	u_int32_t mode, wmode, rmode;
	int v34 = NFS_ISV34(vp);
	struct nfsnode *np = VTONFS(vp);
	if ((ap->a_accmode & (VWRITE | VAPPEND | VWRITE_NAMED_ATTRS |
	    VDELETE_CHILD | VWRITE_ATTRIBUTES | VDELETE | VWRITE_ACL |
	    VWRITE_OWNER)) != 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) != 0) {
		switch (vp->v_type) {
		case VREG:
		case VDIR:
		case VLNK:
			return (EROFS);
		default:
			break;
		}
	}
	if (v34) {
		if (ap->a_accmode & VREAD)
			mode = NFSACCESS_READ;
		else
			mode = 0;
		if (vp->v_type != VDIR) {
			if (ap->a_accmode & VWRITE)
				mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND);
			if (ap->a_accmode & VAPPEND)
				mode |= NFSACCESS_EXTEND;
			if (ap->a_accmode & VEXEC)
				mode |= NFSACCESS_EXECUTE;
			if (ap->a_accmode & VDELETE)
				mode |= NFSACCESS_DELETE;
		} else {
			if (ap->a_accmode & VWRITE)
				mode |= (NFSACCESS_MODIFY | NFSACCESS_EXTEND);
			if (ap->a_accmode & VAPPEND)
				mode |= NFSACCESS_EXTEND;
			if (ap->a_accmode & VEXEC)
				mode |= NFSACCESS_LOOKUP;
			if (ap->a_accmode & VDELETE)
				mode |= NFSACCESS_DELETE;
			if (ap->a_accmode & VDELETE_CHILD)
				mode |= NFSACCESS_MODIFY;
		}
		if (nfsaccess_cache_timeout > 0) {
			wmode = NFSACCESS_READ | NFSACCESS_MODIFY |
				NFSACCESS_EXTEND | NFSACCESS_EXECUTE |
				NFSACCESS_DELETE | NFSACCESS_LOOKUP;
		} else {
			wmode = mode;
		}
		gotahit = 0;
		mtx_lock(&np->n_mtx);
		for (i = 0; i < NFS_ACCESSCACHESIZE; i++) {
			if (ap->a_cred->cr_uid == np->n_accesscache[i].uid) {
			    if (time_second < (np->n_accesscache[i].stamp
				+ nfsaccess_cache_timeout) &&
				(np->n_accesscache[i].mode & mode) == mode) {
				NFSINCRGLOBAL(newnfsstats.accesscache_hits);
				gotahit = 1;
			    }
			    break;
			}
		}
		mtx_unlock(&np->n_mtx);
#ifdef KDTRACE_HOOKS
		if (gotahit != 0)
			KDTRACE_NFS_ACCESSCACHE_GET_HIT(vp,
			    ap->a_cred->cr_uid, mode);
		else
			KDTRACE_NFS_ACCESSCACHE_GET_MISS(vp,
			    ap->a_cred->cr_uid, mode);
#endif
		if (gotahit == 0) {
			NFSINCRGLOBAL(newnfsstats.accesscache_misses);
		        error = nfs34_access_otw(vp, wmode, ap->a_td,
			    ap->a_cred, &rmode);
			if (!error &&
			    (rmode & mode) != mode)
				error = EACCES;
		}
		return (error);
	} else {
		if ((error = nfsspec_access(ap)) != 0) {
			return (error);
		}
		mtx_lock(&np->n_mtx);
		if (ap->a_cred->cr_uid == 0 && (ap->a_accmode & VREAD)
		    && VTONFS(vp)->n_size > 0) {
			struct iovec aiov;
			struct uio auio;
			char buf[1];
			mtx_unlock(&np->n_mtx);
			aiov.iov_base = buf;
			aiov.iov_len = 1;
			auio.uio_iov = &aiov;
			auio.uio_iovcnt = 1;
			auio.uio_offset = 0;
			auio.uio_resid = 1;
			auio.uio_segflg = UIO_SYSSPACE;
			auio.uio_rw = UIO_READ;
			auio.uio_td = ap->a_td;
			if (vp->v_type == VREG)
				error = ncl_readrpc(vp, &auio, ap->a_cred);
			else if (vp->v_type == VDIR) {
				char* bp;
				bp = malloc(NFS_DIRBLKSIZ, M_TEMP, M_WAITOK);
				aiov.iov_base = bp;
				aiov.iov_len = auio.uio_resid = NFS_DIRBLKSIZ;
				error = ncl_readdirrpc(vp, &auio, ap->a_cred,
				    ap->a_td);
				free(bp, M_TEMP);
			} else if (vp->v_type == VLNK)
				error = ncl_readlinkrpc(vp, &auio, ap->a_cred);
			else
				error = EACCES;
		} else
			mtx_unlock(&np->n_mtx);
		return (error);
	}
}
static int nfs_access ( struct vop_access_args * ap ) { struct vnode * vp = ap -> a_vp ; int error = 0 , i , gotahit ; u_int32_t mode , wmode , rmode ; int v34 = NFS_ISV34 ( vp ) ; struct nfsnode * np = VTONFS ( vp ) ; if ( ( ap -> a_accmode & ( VWRITE | VAPPEND | VWRITE_NAMED_ATTRS | VDELETE_CHILD | VWRITE_ATTRIBUTES | VDELETE | VWRITE_ACL | VWRITE_OWNER ) ) != 0 && ( vp -> v_mount -> mnt_flag & MNT_RDONLY ) != 0 ) { switch ( vp -> v_type ) { case VREG : case VDIR : case VLNK : return ( EROFS ) ; default : break ; } } if ( v34 ) { if ( ap -> a_accmode & VREAD ) mode = NFSACCESS_READ ; else mode = 0 ; if ( vp -> v_type != VDIR ) { if ( ap -> a_accmode & VWRITE ) mode |= ( NFSACCESS_MODIFY | NFSACCESS_EXTEND ) ; if ( ap -> a_accmode & VAPPEND ) mode |= NFSACCESS_EXTEND ; if ( ap -> a_accmode & VEXEC ) mode |= NFSACCESS_EXECUTE ; if ( ap -> a_accmode & VDELETE ) mode |= NFSACCESS_DELETE ; } else { if ( ap -> a_accmode & VWRITE ) mode |= ( NFSACCESS_MODIFY | NFSACCESS_EXTEND ) ; if ( ap -> a_accmode & VAPPEND ) mode |= NFSACCESS_EXTEND ; if ( ap -> a_accmode & VEXEC ) mode |= NFSACCESS_LOOKUP ; if ( ap -> a_accmode & VDELETE ) mode |= NFSACCESS_DELETE ; if ( ap -> a_accmode & VDELETE_CHILD ) mode |= NFSACCESS_MODIFY ; } if ( nfsaccess_cache_timeout > 0 ) { wmode = NFSACCESS_READ | NFSACCESS_MODIFY | NFSACCESS_EXTEND | NFSACCESS_EXECUTE | NFSACCESS_DELETE | NFSACCESS_LOOKUP ; } else { wmode = mode ; } gotahit = 0 ; mtx_lock ( & np -> n_mtx ) ; for ( i = 0 ; i < NFS_ACCESSCACHESIZE ; i ++ ) { if ( ap -> a_cred -> cr_uid == np -> n_accesscache [ i ] . uid ) { if ( time_second < ( np -> n_accesscache [ i ] . stamp + nfsaccess_cache_timeout ) && ( np -> n_accesscache [ i ] . mode & mode ) == mode ) { NFSINCRGLOBAL ( newnfsstats . accesscache_hits ) ; gotahit = 1 ; } break ; } } mtx_unlock ( & np -> n_mtx ) ; #ifdef KDTRACE_HOOKS if ( gotahit != 0 ) KDTRACE_NFS_ACCESSCACHE_GET_HIT ( vp , ap -> a_cred -> cr_uid , mode ) ; else KDTRACE_NFS_ACCESSCACHE_GET_MISS ( vp , ap -> a_cred -> cr_uid , mode ) ; #endif if ( gotahit == 0 ) { NFSINCRGLOBAL ( newnfsstats . accesscache_misses ) ; error = nfs34_access_otw ( vp , wmode , ap -> a_td , ap -> a_cred , & rmode ) ; if ( ! error && ( rmode & mode ) != mode ) error = EACCES ; } return ( error ) ; } else { if ( ( error = nfsspec_access ( ap ) ) != 0 ) { return ( error ) ; } mtx_lock ( & np -> n_mtx ) ; if ( ap -> a_cred -> cr_uid == 0 && ( ap -> a_accmode & VREAD ) && VTONFS ( vp ) -> n_size > 0 ) { struct iovec aiov ; struct uio auio ; char buf [ 1 ] ; mtx_unlock ( & np -> n_mtx ) ; aiov . iov_base = buf ; aiov . iov_len = 1 ; auio . uio_iov = & aiov ; auio . uio_iovcnt = 1 ; auio . uio_offset = 0 ; auio . uio_resid = 1 ; auio . uio_segflg = UIO_SYSSPACE ; auio . uio_rw = UIO_READ ; auio . uio_td = ap -> a_td ; if ( vp -> v_type == VREG ) error = ncl_readrpc ( vp , & auio , ap -> a_cred ) ; else if ( vp -> v_type == VDIR ) { char * bp ; bp = malloc ( NFS_DIRBLKSIZ , M_TEMP , M_WAITOK ) ; aiov . iov_base = bp ; aiov . iov_len = auio . uio_resid = NFS_DIRBLKSIZ ; error = ncl_readdirrpc ( vp , & auio , ap -> a_cred , ap -> a_td ) ; free ( bp , M_TEMP ) ; } else if ( vp -> v_type == VLNK ) error = ncl_readlinkrpc ( vp , & auio , ap -> a_cred ) ; else error = EACCES ; } else mtx_unlock ( & np -> n_mtx ) ; return ( error ) ; } }
nfs access vnode op.
nfs access vnode op .
/*
	 * Disallow write attempts on filesystems mounted read-only;
	 * unless the file is a socket, fifo, or a block or character
	 * device resident on the filesystem.
	 */ /*
	 * For nfs v3 or v4, check to see if we have done this recently, and if
	 * so return our cached result instead of making an ACCESS call.
	 * If not, do an access rpc, otherwise you are stuck emulating
	 * ufs_access() locally using the vattr. This may not be correct,
	 * since the server may apply other access criteria such as
	 * client uid-->server uid mapping that we do not know about.
	 */ /* XXX safety belt, only make blanket request if caching */ /*
		 * Does our cached result allow us to give a definite yes to
		 * this request?
		 */ /*
			 * Either a no, or a don't know.  Go to the wire.
			 */ /*
		 * Attempt to prevent a mapped root from accessing a file
		 * which it shouldn't.  We try to read a byte from the file
		 * if the user is root and the file is not zero length.
		 * After calling nfsspec_access, we should have the correct
		 * file size cached.
		 */