xref: /onnv-gate/usr/src/uts/common/fs/zut/zut.c (revision 13082:81ec56bf6147)
19749STim.Haley@Sun.COM /*
29749STim.Haley@Sun.COM  * CDDL HEADER START
39749STim.Haley@Sun.COM  *
49749STim.Haley@Sun.COM  * The contents of this file are subject to the terms of the
59749STim.Haley@Sun.COM  * Common Development and Distribution License (the "License").
69749STim.Haley@Sun.COM  * You may not use this file except in compliance with the License.
79749STim.Haley@Sun.COM  *
89749STim.Haley@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99749STim.Haley@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109749STim.Haley@Sun.COM  * See the License for the specific language governing permissions
119749STim.Haley@Sun.COM  * and limitations under the License.
129749STim.Haley@Sun.COM  *
139749STim.Haley@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149749STim.Haley@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159749STim.Haley@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169749STim.Haley@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179749STim.Haley@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189749STim.Haley@Sun.COM  *
199749STim.Haley@Sun.COM  * CDDL HEADER END
209749STim.Haley@Sun.COM  */
219749STim.Haley@Sun.COM /*
22*13082SJoyce.McIntosh@Sun.COM  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
239749STim.Haley@Sun.COM  */
249749STim.Haley@Sun.COM 
259749STim.Haley@Sun.COM #include <sys/conf.h>
269749STim.Haley@Sun.COM #include <sys/stat.h>
279749STim.Haley@Sun.COM #include <sys/file.h>
289749STim.Haley@Sun.COM #include <sys/types.h>
299749STim.Haley@Sun.COM #include <sys/pathname.h>
309749STim.Haley@Sun.COM #include <sys/proc.h>
319749STim.Haley@Sun.COM #include <sys/mode.h>
329749STim.Haley@Sun.COM #include <sys/vnode.h>
339749STim.Haley@Sun.COM #include <sys/ddi.h>
349749STim.Haley@Sun.COM #include <sys/sunddi.h>
359749STim.Haley@Sun.COM #include <sys/sunldi.h>
369749STim.Haley@Sun.COM #include <sys/uio.h>
379749STim.Haley@Sun.COM #include <sys/attr.h>
389749STim.Haley@Sun.COM #include <sys/acl.h>
399749STim.Haley@Sun.COM #include <sys/fs/zut.h>
409749STim.Haley@Sun.COM 
419749STim.Haley@Sun.COM ldi_ident_t zut_li = NULL;
429749STim.Haley@Sun.COM dev_info_t *zut_dip;
439749STim.Haley@Sun.COM 
449749STim.Haley@Sun.COM static int
zut_open_dir(char * path,vnode_t * startvp,cred_t * cr,int flags,pathname_t * realpn,vnode_t ** dvn)459749STim.Haley@Sun.COM zut_open_dir(char *path, vnode_t *startvp, cred_t *cr, int flags,
469749STim.Haley@Sun.COM     pathname_t *realpn, vnode_t **dvn)
479749STim.Haley@Sun.COM {
489749STim.Haley@Sun.COM 	pathname_t pn;
499749STim.Haley@Sun.COM 	vnode_t *vp;
509749STim.Haley@Sun.COM 	vnode_t *rootvp;
519749STim.Haley@Sun.COM 	proc_t *p = curproc;
529749STim.Haley@Sun.COM 	int error;
539749STim.Haley@Sun.COM 
549749STim.Haley@Sun.COM 	pn_alloc(&pn);
559749STim.Haley@Sun.COM 	(void) strlcpy(pn.pn_buf, path, MAXPATHLEN);
569749STim.Haley@Sun.COM 	pn.pn_pathlen = strlen(path);
579749STim.Haley@Sun.COM 
589749STim.Haley@Sun.COM 	mutex_enter(&p->p_lock);	/* for u_rdir and u_cdir */
599749STim.Haley@Sun.COM 	if ((rootvp = PTOU(p)->u_rdir) == NULL)
609749STim.Haley@Sun.COM 		rootvp = rootdir;
619749STim.Haley@Sun.COM 	else if (rootvp != rootdir)	/* no need to VN_HOLD rootdir */
629749STim.Haley@Sun.COM 		VN_HOLD(rootvp);
639749STim.Haley@Sun.COM 
649749STim.Haley@Sun.COM 	if (pn.pn_path[0] == '/') {
659749STim.Haley@Sun.COM 		vp = rootvp;
669749STim.Haley@Sun.COM 	} else {
679749STim.Haley@Sun.COM 		vp = (startvp == NULL) ? PTOU(p)->u_cdir : startvp;
689749STim.Haley@Sun.COM 	}
699749STim.Haley@Sun.COM 	VN_HOLD(vp);
709749STim.Haley@Sun.COM 	mutex_exit(&p->p_lock);
719749STim.Haley@Sun.COM 
729749STim.Haley@Sun.COM 	/*
739749STim.Haley@Sun.COM 	 * Skip over leading slashes
749749STim.Haley@Sun.COM 	 */
759749STim.Haley@Sun.COM 	while (pn.pn_path[0] == '/') {
769749STim.Haley@Sun.COM 		pn.pn_path++;
779749STim.Haley@Sun.COM 		pn.pn_pathlen--;
789749STim.Haley@Sun.COM 	}
799749STim.Haley@Sun.COM 
809749STim.Haley@Sun.COM 	error = lookuppnvp(&pn, realpn, flags | FOLLOW, NULL,
819749STim.Haley@Sun.COM 	    dvn, rootvp, vp, cr);
829749STim.Haley@Sun.COM 
839749STim.Haley@Sun.COM 	/*
849749STim.Haley@Sun.COM 	 * If we lack read access to the directory, we should error out.
859749STim.Haley@Sun.COM 	 */
869749STim.Haley@Sun.COM 	if (!error) {
879749STim.Haley@Sun.COM 		if (vfs_has_feature((*dvn)->v_vfsp, VFSFT_ACEMASKONACCESS)) {
889749STim.Haley@Sun.COM 			error = VOP_ACCESS(*dvn, ACE_LIST_DIRECTORY,
899749STim.Haley@Sun.COM 			    V_ACE_MASK, cr, NULL);
909749STim.Haley@Sun.COM 		} else {
919749STim.Haley@Sun.COM 			error = VOP_ACCESS(*dvn, VREAD, 0, cr, NULL);
929749STim.Haley@Sun.COM 		}
939749STim.Haley@Sun.COM 	}
949749STim.Haley@Sun.COM 
959749STim.Haley@Sun.COM 	pn_free(&pn);
969749STim.Haley@Sun.COM 
979749STim.Haley@Sun.COM 	return (error);
989749STim.Haley@Sun.COM }
999749STim.Haley@Sun.COM 
1009749STim.Haley@Sun.COM static int
zut_readdir(intptr_t arg,cred_t * cr,int iflag,int * rvalp)1019749STim.Haley@Sun.COM zut_readdir(intptr_t arg, cred_t *cr, int iflag, int *rvalp)
1029749STim.Haley@Sun.COM {
1039749STim.Haley@Sun.COM 	zut_readdir_t *zr;
1049749STim.Haley@Sun.COM 	struct iovec aiov;
1059749STim.Haley@Sun.COM 	struct uio auio;
1069749STim.Haley@Sun.COM 	vnode_t *dvn = NULL;
1079749STim.Haley@Sun.COM 	vnode_t *fvn = NULL;
1089749STim.Haley@Sun.COM 	char *kbuf;
1099749STim.Haley@Sun.COM 	int flags = 0;
1109749STim.Haley@Sun.COM 	int error, rc;
1119749STim.Haley@Sun.COM 
1129749STim.Haley@Sun.COM 	zr = kmem_zalloc(sizeof (zut_readdir_t), KM_SLEEP);
1139749STim.Haley@Sun.COM 	error = ddi_copyin((void *)arg, zr, sizeof (zut_readdir_t), iflag);
1149749STim.Haley@Sun.COM 	if (error)
1159749STim.Haley@Sun.COM 		goto zutr_bail;
1169749STim.Haley@Sun.COM 
1179749STim.Haley@Sun.COM 	kbuf = kmem_zalloc(zr->zr_buflen, KM_SLEEP);
1189749STim.Haley@Sun.COM 
1199749STim.Haley@Sun.COM 	zr->zr_retcode = zut_open_dir(zr->zr_dir, NULL, cr, flags, NULL, &dvn);
1209749STim.Haley@Sun.COM 	if (zr->zr_retcode)
1219749STim.Haley@Sun.COM 		goto zutr_done;
1229749STim.Haley@Sun.COM 
1239749STim.Haley@Sun.COM 	if (zr->zr_reqflags & ZUT_XATTR) {
1249749STim.Haley@Sun.COM 		vattr_t vattr;
1259749STim.Haley@Sun.COM 
1269749STim.Haley@Sun.COM 		zr->zr_retcode = VOP_LOOKUP(dvn, zr->zr_file, &fvn,
1279749STim.Haley@Sun.COM 		    NULL, flags, NULL, cr, NULL, NULL, NULL);
1289749STim.Haley@Sun.COM 		VN_RELE(dvn);
1299749STim.Haley@Sun.COM 		dvn = NULL;
1309749STim.Haley@Sun.COM 		if (zr->zr_retcode)
1319749STim.Haley@Sun.COM 			goto zutr_done;
1329749STim.Haley@Sun.COM 
1339749STim.Haley@Sun.COM 		/*
1349749STim.Haley@Sun.COM 		 * In order to access hidden attribute directory the
1359749STim.Haley@Sun.COM 		 * user must have appropriate read access and be able
1369749STim.Haley@Sun.COM 		 * to stat() the file
1379749STim.Haley@Sun.COM 		 */
1389749STim.Haley@Sun.COM 		if (vfs_has_feature(fvn->v_vfsp, VFSFT_ACEMASKONACCESS)) {
1399749STim.Haley@Sun.COM 			zr->zr_retcode = VOP_ACCESS(fvn, ACE_READ_NAMED_ATTRS,
1409749STim.Haley@Sun.COM 			    V_ACE_MASK, cr, NULL);
1419749STim.Haley@Sun.COM 		} else {
1429749STim.Haley@Sun.COM 			zr->zr_retcode = VOP_ACCESS(fvn, VREAD, 0, cr, NULL);
1439749STim.Haley@Sun.COM 		}
1449749STim.Haley@Sun.COM 		if (zr->zr_retcode)
1459749STim.Haley@Sun.COM 			goto zutr_done;
1469749STim.Haley@Sun.COM 
1479749STim.Haley@Sun.COM 		vattr.va_mask = AT_ALL;
1489749STim.Haley@Sun.COM 		zr->zr_retcode = VOP_GETATTR(fvn, &vattr, 0, cr, NULL);
1499749STim.Haley@Sun.COM 		if (zr->zr_retcode)
1509749STim.Haley@Sun.COM 			goto zutr_done;
1519749STim.Haley@Sun.COM 
1529749STim.Haley@Sun.COM 		zr->zr_retcode = VOP_LOOKUP(fvn, "", &dvn, NULL,
1539749STim.Haley@Sun.COM 		    flags | LOOKUP_XATTR, NULL, cr, NULL, NULL, NULL);
1549749STim.Haley@Sun.COM 		VN_RELE(fvn);
1559749STim.Haley@Sun.COM 		if (zr->zr_retcode)
1569749STim.Haley@Sun.COM 			goto zutr_done;
1579749STim.Haley@Sun.COM 	}
1589749STim.Haley@Sun.COM 
1599749STim.Haley@Sun.COM 	aiov.iov_base = kbuf;
1609749STim.Haley@Sun.COM 	aiov.iov_len = zr->zr_buflen;
1619749STim.Haley@Sun.COM 	auio.uio_iov = &aiov;
1629749STim.Haley@Sun.COM 	auio.uio_iovcnt = 1;
1639749STim.Haley@Sun.COM 	auio.uio_loffset = zr->zr_loffset;
1649749STim.Haley@Sun.COM 	auio.uio_segflg = UIO_SYSSPACE;
1659749STim.Haley@Sun.COM 	auio.uio_resid = zr->zr_buflen;
1669749STim.Haley@Sun.COM 	auio.uio_fmode = 0;
1679749STim.Haley@Sun.COM 	auio.uio_extflg = UIO_COPY_CACHED;
1689749STim.Haley@Sun.COM 
1699749STim.Haley@Sun.COM 	if (zr->zr_reqflags & ZUT_EXTRDDIR)
1709749STim.Haley@Sun.COM 		flags |= V_RDDIR_ENTFLAGS;
1719749STim.Haley@Sun.COM 	if (zr->zr_reqflags & ZUT_ACCFILTER)
1729749STim.Haley@Sun.COM 		flags |= V_RDDIR_ACCFILTER;
1739749STim.Haley@Sun.COM 
1749749STim.Haley@Sun.COM 	(void) VOP_RWLOCK(dvn, V_WRITELOCK_FALSE, NULL);
1759749STim.Haley@Sun.COM 	zr->zr_retcode = VOP_READDIR(dvn, &auio, cr, &zr->zr_eof,
1769749STim.Haley@Sun.COM 	    NULL, flags);
1779749STim.Haley@Sun.COM 	VOP_RWUNLOCK(dvn, V_WRITELOCK_FALSE, NULL);
1789749STim.Haley@Sun.COM 	VN_RELE(dvn);
1799749STim.Haley@Sun.COM 
1809749STim.Haley@Sun.COM 	zr->zr_bytes = aiov.iov_base - kbuf;
1819749STim.Haley@Sun.COM 	zr->zr_loffset = auio.uio_loffset;
1829749STim.Haley@Sun.COM 
1839749STim.Haley@Sun.COM 	error = ddi_copyout(kbuf, (void *)(uintptr_t)zr->zr_buf,
1849749STim.Haley@Sun.COM 	    zr->zr_buflen, iflag);
1859749STim.Haley@Sun.COM 
1869749STim.Haley@Sun.COM zutr_done:
1879749STim.Haley@Sun.COM 	kmem_free(kbuf, zr->zr_buflen);
1889749STim.Haley@Sun.COM 	rc = ddi_copyout(zr, (void *)arg, sizeof (zut_readdir_t), iflag);
1899749STim.Haley@Sun.COM 	if (error == 0)
1909749STim.Haley@Sun.COM 		error = rc;
1919749STim.Haley@Sun.COM 
1929749STim.Haley@Sun.COM zutr_bail:
1939749STim.Haley@Sun.COM 	kmem_free(zr, sizeof (zut_readdir_t));
1949749STim.Haley@Sun.COM 	if (rvalp)
1959749STim.Haley@Sun.COM 		*rvalp = error;
1969749STim.Haley@Sun.COM 	return (error);
1979749STim.Haley@Sun.COM }
1989749STim.Haley@Sun.COM 
1999749STim.Haley@Sun.COM static int
zut_stat64(vnode_t * vp,struct stat64 * sb,uint64_t * xvs,int flag,cred_t * cr)2009749STim.Haley@Sun.COM zut_stat64(vnode_t *vp, struct stat64 *sb, uint64_t *xvs, int flag, cred_t *cr)
2019749STim.Haley@Sun.COM {
2029749STim.Haley@Sun.COM 	xoptattr_t *xoap = NULL;
2039749STim.Haley@Sun.COM 	xvattr_t xv = { 0 };
2049749STim.Haley@Sun.COM 	int error;
2059749STim.Haley@Sun.COM 
2069749STim.Haley@Sun.COM 	xva_init(&xv);
2079749STim.Haley@Sun.COM 
2089749STim.Haley@Sun.COM 	XVA_SET_REQ(&xv, XAT_ARCHIVE);
2099749STim.Haley@Sun.COM 	XVA_SET_REQ(&xv, XAT_SYSTEM);
2109749STim.Haley@Sun.COM 	XVA_SET_REQ(&xv, XAT_READONLY);
2119749STim.Haley@Sun.COM 	XVA_SET_REQ(&xv, XAT_HIDDEN);
2129749STim.Haley@Sun.COM 	XVA_SET_REQ(&xv, XAT_NOUNLINK);
2139749STim.Haley@Sun.COM 	XVA_SET_REQ(&xv, XAT_IMMUTABLE);
2149749STim.Haley@Sun.COM 	XVA_SET_REQ(&xv, XAT_APPENDONLY);
2159749STim.Haley@Sun.COM 	XVA_SET_REQ(&xv, XAT_NODUMP);
2169749STim.Haley@Sun.COM 	XVA_SET_REQ(&xv, XAT_OPAQUE);
2179749STim.Haley@Sun.COM 	XVA_SET_REQ(&xv, XAT_AV_QUARANTINED);
2189749STim.Haley@Sun.COM 	XVA_SET_REQ(&xv, XAT_AV_MODIFIED);
21910793Sdai.ngo@sun.com 	XVA_SET_REQ(&xv, XAT_REPARSE);
220*13082SJoyce.McIntosh@Sun.COM 	XVA_SET_REQ(&xv, XAT_OFFLINE);
221*13082SJoyce.McIntosh@Sun.COM 	XVA_SET_REQ(&xv, XAT_SPARSE);
2229749STim.Haley@Sun.COM 
2239749STim.Haley@Sun.COM 	xv.xva_vattr.va_mask |= AT_STAT | AT_NBLOCKS | AT_BLKSIZE | AT_SIZE;
2249749STim.Haley@Sun.COM 	if (error = VOP_GETATTR(vp, &xv.xva_vattr, flag, cr, NULL))
2259749STim.Haley@Sun.COM 		return (error);
2269749STim.Haley@Sun.COM 
2279749STim.Haley@Sun.COM 	bzero(sb, sizeof (sb));
2289749STim.Haley@Sun.COM 	sb->st_dev = xv.xva_vattr.va_fsid;
2299749STim.Haley@Sun.COM 	sb->st_ino = xv.xva_vattr.va_nodeid;
2309749STim.Haley@Sun.COM 	sb->st_mode = VTTOIF(xv.xva_vattr.va_type) | xv.xva_vattr.va_mode;
2319749STim.Haley@Sun.COM 	sb->st_nlink = xv.xva_vattr.va_nlink;
2329749STim.Haley@Sun.COM 	sb->st_uid = xv.xva_vattr.va_uid;
2339749STim.Haley@Sun.COM 	sb->st_gid = xv.xva_vattr.va_gid;
2349749STim.Haley@Sun.COM 	sb->st_rdev = xv.xva_vattr.va_rdev;
2359749STim.Haley@Sun.COM 	sb->st_size = xv.xva_vattr.va_size;
2369749STim.Haley@Sun.COM 	sb->st_atim = xv.xva_vattr.va_atime;
2379749STim.Haley@Sun.COM 	sb->st_mtim = xv.xva_vattr.va_mtime;
2389749STim.Haley@Sun.COM 	sb->st_ctim = xv.xva_vattr.va_ctime;
2399749STim.Haley@Sun.COM 	sb->st_blksize = xv.xva_vattr.va_blksize;
2409749STim.Haley@Sun.COM 	sb->st_blocks = xv.xva_vattr.va_nblocks;
2419749STim.Haley@Sun.COM 	sb->st_fstype[0] = 0;
2429749STim.Haley@Sun.COM 
2439749STim.Haley@Sun.COM 	if ((xoap = xva_getxoptattr(&xv)) == NULL)
2449749STim.Haley@Sun.COM 		return (0);
2459749STim.Haley@Sun.COM 
2469749STim.Haley@Sun.COM 	if (XVA_ISSET_RTN(&xv, XAT_ARCHIVE) && xoap->xoa_archive)
2479749STim.Haley@Sun.COM 		*xvs |= (1 << F_ARCHIVE);
2489749STim.Haley@Sun.COM 	if (XVA_ISSET_RTN(&xv, XAT_SYSTEM) && xoap->xoa_system)
2499749STim.Haley@Sun.COM 		*xvs |= (1 << F_SYSTEM);
2509749STim.Haley@Sun.COM 	if (XVA_ISSET_RTN(&xv, XAT_READONLY) && xoap->xoa_readonly)
2519749STim.Haley@Sun.COM 		*xvs |= (1 << F_READONLY);
2529749STim.Haley@Sun.COM 	if (XVA_ISSET_RTN(&xv, XAT_HIDDEN) && xoap->xoa_hidden)
2539749STim.Haley@Sun.COM 		*xvs |= (1 << F_HIDDEN);
2549749STim.Haley@Sun.COM 	if (XVA_ISSET_RTN(&xv, XAT_NOUNLINK) && xoap->xoa_nounlink)
2559749STim.Haley@Sun.COM 		*xvs |= (1 << F_NOUNLINK);
2569749STim.Haley@Sun.COM 	if (XVA_ISSET_RTN(&xv, XAT_IMMUTABLE) && xoap->xoa_immutable)
2579749STim.Haley@Sun.COM 		*xvs |= (1 << F_IMMUTABLE);
2589749STim.Haley@Sun.COM 	if (XVA_ISSET_RTN(&xv, XAT_APPENDONLY) && xoap->xoa_appendonly)
2599749STim.Haley@Sun.COM 		*xvs |= (1 << F_APPENDONLY);
2609749STim.Haley@Sun.COM 	if (XVA_ISSET_RTN(&xv, XAT_NODUMP) && xoap->xoa_nodump)
2619749STim.Haley@Sun.COM 		*xvs |= (1 << F_NODUMP);
2629749STim.Haley@Sun.COM 	if (XVA_ISSET_RTN(&xv, XAT_OPAQUE) && xoap->xoa_opaque)
2639749STim.Haley@Sun.COM 		*xvs |= (1 << F_OPAQUE);
2649749STim.Haley@Sun.COM 	if (XVA_ISSET_RTN(&xv, XAT_AV_QUARANTINED) && xoap->xoa_av_quarantined)
2659749STim.Haley@Sun.COM 		*xvs |= (1 << F_AV_QUARANTINED);
2669749STim.Haley@Sun.COM 	if (XVA_ISSET_RTN(&xv, XAT_AV_MODIFIED) && xoap->xoa_av_modified)
2679749STim.Haley@Sun.COM 		*xvs |= (1 << F_AV_MODIFIED);
26810793Sdai.ngo@sun.com 	if (XVA_ISSET_RTN(&xv, XAT_REPARSE) && xoap->xoa_reparse)
26910793Sdai.ngo@sun.com 		*xvs |= (1 << F_REPARSE);
270*13082SJoyce.McIntosh@Sun.COM 	if (XVA_ISSET_RTN(&xv, XAT_OFFLINE) && xoap->xoa_offline)
271*13082SJoyce.McIntosh@Sun.COM 		*xvs |= (1 << F_OFFLINE);
272*13082SJoyce.McIntosh@Sun.COM 	if (XVA_ISSET_RTN(&xv, XAT_SPARSE) && xoap->xoa_sparse)
273*13082SJoyce.McIntosh@Sun.COM 		*xvs |= (1 << F_SPARSE);
2749749STim.Haley@Sun.COM 
2759749STim.Haley@Sun.COM 	return (0);
2769749STim.Haley@Sun.COM }
2779749STim.Haley@Sun.COM 
2789749STim.Haley@Sun.COM /*ARGSUSED*/
2799749STim.Haley@Sun.COM static int
zut_lookup(intptr_t arg,cred_t * cr,int iflag,int * rvalp)2809749STim.Haley@Sun.COM zut_lookup(intptr_t arg, cred_t *cr, int iflag, int *rvalp)
2819749STim.Haley@Sun.COM {
2829749STim.Haley@Sun.COM 	zut_lookup_t *zl;
2839749STim.Haley@Sun.COM 	pathname_t rpn;
2849749STim.Haley@Sun.COM 	vnode_t *dvn = NULL;
2859749STim.Haley@Sun.COM 	vnode_t *fvn = NULL;
2869749STim.Haley@Sun.COM 	vnode_t *xdvn = NULL;
2879749STim.Haley@Sun.COM 	vnode_t *xfvn = NULL;
2889749STim.Haley@Sun.COM 	vnode_t *release = NULL;
2899749STim.Haley@Sun.COM 	int flags = 0;
2909749STim.Haley@Sun.COM 	int error, rc;
2919749STim.Haley@Sun.COM 
2929749STim.Haley@Sun.COM 	zl = kmem_zalloc(sizeof (zut_lookup_t), KM_SLEEP);
2939749STim.Haley@Sun.COM 
2949749STim.Haley@Sun.COM 	error = ddi_copyin((void *)arg, zl, sizeof (zut_lookup_t), iflag);
2959749STim.Haley@Sun.COM 	if (error)
2969749STim.Haley@Sun.COM 		goto zutl_bail;
2979749STim.Haley@Sun.COM 
2989749STim.Haley@Sun.COM 	pn_alloc(&rpn);
2999749STim.Haley@Sun.COM 	bzero(rpn.pn_buf, MAXPATHLEN);
3009749STim.Haley@Sun.COM 
3019749STim.Haley@Sun.COM 	zl->zl_retcode = zut_open_dir(zl->zl_dir, NULL, cr, flags, &rpn, &dvn);
3029749STim.Haley@Sun.COM 	if (zl->zl_retcode)
3039749STim.Haley@Sun.COM 		goto zutl_done;
3049749STim.Haley@Sun.COM 
3059749STim.Haley@Sun.COM 	if (zl->zl_reqflags & ZUT_IGNORECASE)
3069749STim.Haley@Sun.COM 		flags |= FIGNORECASE;
3079749STim.Haley@Sun.COM 
3089749STim.Haley@Sun.COM 	zl->zl_retcode = VOP_LOOKUP(dvn, zl->zl_file, &fvn, NULL, flags, NULL,
3099749STim.Haley@Sun.COM 	    cr, NULL, &zl->zl_deflags, &rpn);
3109749STim.Haley@Sun.COM 	if (zl->zl_retcode)
3119749STim.Haley@Sun.COM 		goto zutl_done;
3129749STim.Haley@Sun.COM 
3139749STim.Haley@Sun.COM 	release = fvn;
3149749STim.Haley@Sun.COM 
3159749STim.Haley@Sun.COM 	if (zl->zl_reqflags & ZUT_XATTR) {
3169749STim.Haley@Sun.COM 		vattr_t vattr;
3179749STim.Haley@Sun.COM 
3189749STim.Haley@Sun.COM 		/*
3199749STim.Haley@Sun.COM 		 * In order to access hidden attribute directory the
3209749STim.Haley@Sun.COM 		 * user must have appropriate read access and be able
3219749STim.Haley@Sun.COM 		 * to stat() the file
3229749STim.Haley@Sun.COM 		 */
3239749STim.Haley@Sun.COM 		if (vfs_has_feature(fvn->v_vfsp, VFSFT_ACEMASKONACCESS)) {
3249749STim.Haley@Sun.COM 			zl->zl_retcode = VOP_ACCESS(fvn, ACE_READ_NAMED_ATTRS,
3259749STim.Haley@Sun.COM 			    V_ACE_MASK, cr, NULL);
3269749STim.Haley@Sun.COM 		} else {
3279749STim.Haley@Sun.COM 			zl->zl_retcode = VOP_ACCESS(fvn, VREAD, 0, cr, NULL);
3289749STim.Haley@Sun.COM 		}
3299749STim.Haley@Sun.COM 		if (zl->zl_retcode)
3309749STim.Haley@Sun.COM 			goto zutl_done;
3319749STim.Haley@Sun.COM 
3329749STim.Haley@Sun.COM 		vattr.va_mask = AT_ALL;
3339749STim.Haley@Sun.COM 		zl->zl_retcode = VOP_GETATTR(fvn, &vattr, 0, cr, NULL);
3349749STim.Haley@Sun.COM 		if (zl->zl_retcode)
3359749STim.Haley@Sun.COM 			goto zutl_done;
3369749STim.Haley@Sun.COM 
3379749STim.Haley@Sun.COM 		zl->zl_retcode = VOP_LOOKUP(fvn, "", &xdvn, NULL,
3389749STim.Haley@Sun.COM 		    flags | LOOKUP_XATTR, NULL, cr, NULL, NULL, NULL);
3399749STim.Haley@Sun.COM 		if (zl->zl_retcode)
3409749STim.Haley@Sun.COM 			goto zutl_done;
3419749STim.Haley@Sun.COM 		VN_RELE(fvn);
3429749STim.Haley@Sun.COM 		release = xdvn;
3439749STim.Haley@Sun.COM 
3449749STim.Haley@Sun.COM 		zl->zl_retcode = VOP_LOOKUP(xdvn, zl->zl_xfile, &xfvn,
3459749STim.Haley@Sun.COM 		    NULL, flags, NULL, cr, NULL, &zl->zl_deflags, &rpn);
3469749STim.Haley@Sun.COM 		if (zl->zl_retcode)
3479749STim.Haley@Sun.COM 			goto zutl_done;
3489749STim.Haley@Sun.COM 		VN_RELE(xdvn);
3499749STim.Haley@Sun.COM 		release = xfvn;
3509749STim.Haley@Sun.COM 	}
3519749STim.Haley@Sun.COM 
3529749STim.Haley@Sun.COM 	if (zl->zl_reqflags & ZUT_GETSTAT) {
3539749STim.Haley@Sun.COM 		zl->zl_retcode = zut_stat64(release,
3549749STim.Haley@Sun.COM 		    &zl->zl_statbuf, &zl->zl_xvattrs, 0, cr);
3559749STim.Haley@Sun.COM 	}
3569749STim.Haley@Sun.COM 
3579749STim.Haley@Sun.COM zutl_done:
3589749STim.Haley@Sun.COM 	(void) strlcpy(zl->zl_real, rpn.pn_path, MAXPATHLEN);
3599749STim.Haley@Sun.COM 
3609749STim.Haley@Sun.COM 	rc = ddi_copyout(zl, (void *)arg, sizeof (zut_lookup_t), iflag);
3619749STim.Haley@Sun.COM 	if (error == 0)
3629749STim.Haley@Sun.COM 		error = rc;
3639749STim.Haley@Sun.COM 
3649749STim.Haley@Sun.COM 	if (release)
3659749STim.Haley@Sun.COM 		VN_RELE(release);
3669749STim.Haley@Sun.COM 	if (dvn)
3679749STim.Haley@Sun.COM 		VN_RELE(dvn);
3689749STim.Haley@Sun.COM 	pn_free(&rpn);
3699749STim.Haley@Sun.COM 
3709749STim.Haley@Sun.COM zutl_bail:
3719749STim.Haley@Sun.COM 	kmem_free(zl, sizeof (zut_lookup_t));
3729749STim.Haley@Sun.COM 	if (rvalp)
3739749STim.Haley@Sun.COM 		*rvalp = error;
3749749STim.Haley@Sun.COM 	return (error);
3759749STim.Haley@Sun.COM }
3769749STim.Haley@Sun.COM 
3779749STim.Haley@Sun.COM /*ARGSUSED*/
3789749STim.Haley@Sun.COM static int
zut_ioctl(dev_t dev,int cmd,intptr_t arg,int flag,cred_t * cr,int * rvalp)3799749STim.Haley@Sun.COM zut_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
3809749STim.Haley@Sun.COM {
3819749STim.Haley@Sun.COM 	int error;
3829749STim.Haley@Sun.COM 
3839749STim.Haley@Sun.COM 	if (getminor(dev) != 0)
3849749STim.Haley@Sun.COM 		return (ENXIO);
3859749STim.Haley@Sun.COM 
3869749STim.Haley@Sun.COM 	if (cmd <= ZUT_IOC_MIN_CMD || cmd >= ZUT_IOC_MAX_CMD)
3879749STim.Haley@Sun.COM 		return (EINVAL);
3889749STim.Haley@Sun.COM 
3899749STim.Haley@Sun.COM 	switch (cmd) {
3909749STim.Haley@Sun.COM 	case ZUT_IOC_LOOKUP:
3919749STim.Haley@Sun.COM 		error = zut_lookup(arg, cr, flag, rvalp);
3929749STim.Haley@Sun.COM 		break;
3939749STim.Haley@Sun.COM 	case ZUT_IOC_READDIR:
3949749STim.Haley@Sun.COM 		error = zut_readdir(arg, cr, flag, rvalp);
3959749STim.Haley@Sun.COM 	default:
3969749STim.Haley@Sun.COM 		break;
3979749STim.Haley@Sun.COM 	}
3989749STim.Haley@Sun.COM 
3999749STim.Haley@Sun.COM 	return (error);
4009749STim.Haley@Sun.COM }
4019749STim.Haley@Sun.COM 
4029749STim.Haley@Sun.COM static int
zut_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)4039749STim.Haley@Sun.COM zut_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
4049749STim.Haley@Sun.COM {
4059749STim.Haley@Sun.COM 	if (cmd != DDI_ATTACH)
4069749STim.Haley@Sun.COM 		return (DDI_FAILURE);
4079749STim.Haley@Sun.COM 
4089749STim.Haley@Sun.COM 	if (ddi_create_minor_node(dip, "zut", S_IFCHR, 0,
4099749STim.Haley@Sun.COM 	    DDI_PSEUDO, 0) == DDI_FAILURE)
4109749STim.Haley@Sun.COM 		return (DDI_FAILURE);
4119749STim.Haley@Sun.COM 
4129749STim.Haley@Sun.COM 	zut_dip = dip;
4139749STim.Haley@Sun.COM 
4149749STim.Haley@Sun.COM 	ddi_report_dev(dip);
4159749STim.Haley@Sun.COM 
4169749STim.Haley@Sun.COM 	return (DDI_SUCCESS);
4179749STim.Haley@Sun.COM }
4189749STim.Haley@Sun.COM 
4199749STim.Haley@Sun.COM static int
zut_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)4209749STim.Haley@Sun.COM zut_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4219749STim.Haley@Sun.COM {
4229749STim.Haley@Sun.COM 	if (cmd != DDI_DETACH)
4239749STim.Haley@Sun.COM 		return (DDI_FAILURE);
4249749STim.Haley@Sun.COM 
4259749STim.Haley@Sun.COM 	zut_dip = NULL;
4269749STim.Haley@Sun.COM 
4279749STim.Haley@Sun.COM 	ddi_prop_remove_all(dip);
4289749STim.Haley@Sun.COM 	ddi_remove_minor_node(dip, NULL);
4299749STim.Haley@Sun.COM 
4309749STim.Haley@Sun.COM 	return (DDI_SUCCESS);
4319749STim.Haley@Sun.COM }
4329749STim.Haley@Sun.COM 
4339749STim.Haley@Sun.COM /*ARGSUSED*/
4349749STim.Haley@Sun.COM static int
zut_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)4359749STim.Haley@Sun.COM zut_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
4369749STim.Haley@Sun.COM {
4379749STim.Haley@Sun.COM 	switch (infocmd) {
4389749STim.Haley@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
4399749STim.Haley@Sun.COM 		*result = zut_dip;
4409749STim.Haley@Sun.COM 		return (DDI_SUCCESS);
4419749STim.Haley@Sun.COM 
4429749STim.Haley@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
4439749STim.Haley@Sun.COM 		*result = (void *)0;
4449749STim.Haley@Sun.COM 		return (DDI_SUCCESS);
4459749STim.Haley@Sun.COM 	}
4469749STim.Haley@Sun.COM 
4479749STim.Haley@Sun.COM 	return (DDI_FAILURE);
4489749STim.Haley@Sun.COM }
4499749STim.Haley@Sun.COM 
4509749STim.Haley@Sun.COM /*ARGSUSED*/
4519749STim.Haley@Sun.COM int
zut_open(dev_t * devp,int flag,int otyp,cred_t * cr)4529749STim.Haley@Sun.COM zut_open(dev_t *devp, int flag, int otyp, cred_t *cr)
4539749STim.Haley@Sun.COM {
4549749STim.Haley@Sun.COM 	minor_t minor = getminor(*devp);
4559749STim.Haley@Sun.COM 
4569749STim.Haley@Sun.COM 	if (minor == 0)			/* This is the control device */
4579749STim.Haley@Sun.COM 		return (0);
4589749STim.Haley@Sun.COM 
4599749STim.Haley@Sun.COM 	return (ENXIO);
4609749STim.Haley@Sun.COM }
4619749STim.Haley@Sun.COM 
4629749STim.Haley@Sun.COM /*ARGSUSED*/
4639749STim.Haley@Sun.COM int
zut_close(dev_t dev,int flag,int otyp,cred_t * cr)4649749STim.Haley@Sun.COM zut_close(dev_t dev, int flag, int otyp, cred_t *cr)
4659749STim.Haley@Sun.COM {
4669749STim.Haley@Sun.COM 	minor_t minor = getminor(dev);
4679749STim.Haley@Sun.COM 
4689749STim.Haley@Sun.COM 	if (minor == 0)		/* This is the control device */
4699749STim.Haley@Sun.COM 		return (0);
4709749STim.Haley@Sun.COM 
4719749STim.Haley@Sun.COM 	return (ENXIO);
4729749STim.Haley@Sun.COM }
4739749STim.Haley@Sun.COM 
4749749STim.Haley@Sun.COM /*
4759749STim.Haley@Sun.COM  * /dev/zut is the control node, i.e. minor 0.
4769749STim.Haley@Sun.COM  *
4779749STim.Haley@Sun.COM  * There are no other minor nodes, and /dev/zut basically does nothing
4789749STim.Haley@Sun.COM  * other than serve up ioctls.
4799749STim.Haley@Sun.COM  */
4809749STim.Haley@Sun.COM static struct cb_ops zut_cb_ops = {
4819749STim.Haley@Sun.COM 	zut_open,	/* open */
4829749STim.Haley@Sun.COM 	zut_close,	/* close */
4839749STim.Haley@Sun.COM 	nodev,		/* strategy */
4849749STim.Haley@Sun.COM 	nodev,		/* print */
4859749STim.Haley@Sun.COM 	nodev,		/* dump */
4869749STim.Haley@Sun.COM 	nodev,		/* read */
4879749STim.Haley@Sun.COM 	nodev,		/* write */
4889749STim.Haley@Sun.COM 	zut_ioctl,	/* ioctl */
4899749STim.Haley@Sun.COM 	nodev,		/* devmap */
4909749STim.Haley@Sun.COM 	nodev,		/* mmap */
4919749STim.Haley@Sun.COM 	nodev,		/* segmap */
4929749STim.Haley@Sun.COM 	nochpoll,	/* poll */
4939749STim.Haley@Sun.COM 	ddi_prop_op,	/* prop_op */
4949749STim.Haley@Sun.COM 	NULL,		/* streamtab */
4959749STim.Haley@Sun.COM 	D_NEW | D_MP | D_64BIT,		/* Driver compatibility flag */
4969749STim.Haley@Sun.COM 	CB_REV,		/* version */
4979749STim.Haley@Sun.COM 	nodev,		/* async read */
4989749STim.Haley@Sun.COM 	nodev,		/* async write */
4999749STim.Haley@Sun.COM };
5009749STim.Haley@Sun.COM 
5019749STim.Haley@Sun.COM static struct dev_ops zut_dev_ops = {
5029749STim.Haley@Sun.COM 	DEVO_REV,	/* version */
5039749STim.Haley@Sun.COM 	0,		/* refcnt */
5049749STim.Haley@Sun.COM 	zut_info,	/* info */
5059749STim.Haley@Sun.COM 	nulldev,	/* identify */
5069749STim.Haley@Sun.COM 	nulldev,	/* probe */
5079749STim.Haley@Sun.COM 	zut_attach,	/* attach */
5089749STim.Haley@Sun.COM 	zut_detach,	/* detach */
5099749STim.Haley@Sun.COM 	nodev,		/* reset */
5109749STim.Haley@Sun.COM 	&zut_cb_ops,	/* driver operations */
5119749STim.Haley@Sun.COM 	NULL		/* no bus operations */
5129749STim.Haley@Sun.COM };
5139749STim.Haley@Sun.COM 
5149749STim.Haley@Sun.COM static struct modldrv zut_modldrv = {
5159749STim.Haley@Sun.COM 	&mod_driverops, "ZFS unit test " ZUT_VERSION_STRING,
5169749STim.Haley@Sun.COM 	    &zut_dev_ops
5179749STim.Haley@Sun.COM };
5189749STim.Haley@Sun.COM 
5199749STim.Haley@Sun.COM static struct modlinkage modlinkage = {
5209749STim.Haley@Sun.COM 	MODREV_1,
5219749STim.Haley@Sun.COM 	(void *)&zut_modldrv,
5229749STim.Haley@Sun.COM 	NULL
5239749STim.Haley@Sun.COM };
5249749STim.Haley@Sun.COM 
5259749STim.Haley@Sun.COM int
_init(void)5269749STim.Haley@Sun.COM _init(void)
5279749STim.Haley@Sun.COM {
5289749STim.Haley@Sun.COM 	int error;
5299749STim.Haley@Sun.COM 
5309749STim.Haley@Sun.COM 	if ((error = mod_install(&modlinkage)) != 0) {
5319749STim.Haley@Sun.COM 		return (error);
5329749STim.Haley@Sun.COM 	}
5339749STim.Haley@Sun.COM 
5349749STim.Haley@Sun.COM 	error = ldi_ident_from_mod(&modlinkage, &zut_li);
5359749STim.Haley@Sun.COM 	ASSERT(error == 0);
5369749STim.Haley@Sun.COM 
5379749STim.Haley@Sun.COM 	return (0);
5389749STim.Haley@Sun.COM }
5399749STim.Haley@Sun.COM 
5409749STim.Haley@Sun.COM int
_fini(void)5419749STim.Haley@Sun.COM _fini(void)
5429749STim.Haley@Sun.COM {
5439749STim.Haley@Sun.COM 	int error;
5449749STim.Haley@Sun.COM 
5459749STim.Haley@Sun.COM 	if ((error = mod_remove(&modlinkage)) != 0)
5469749STim.Haley@Sun.COM 		return (error);
5479749STim.Haley@Sun.COM 
5489749STim.Haley@Sun.COM 	ldi_ident_release(zut_li);
5499749STim.Haley@Sun.COM 	zut_li = NULL;
5509749STim.Haley@Sun.COM 
5519749STim.Haley@Sun.COM 	return (error);
5529749STim.Haley@Sun.COM }
5539749STim.Haley@Sun.COM 
5549749STim.Haley@Sun.COM int
_info(struct modinfo * modinfop)5559749STim.Haley@Sun.COM _info(struct modinfo *modinfop)
5569749STim.Haley@Sun.COM {
5579749STim.Haley@Sun.COM 	return (mod_info(&modlinkage, modinfop));
5589749STim.Haley@Sun.COM }
559