Home | History | Annotate | Download | only in zut
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <sys/conf.h>
     27 #include <sys/stat.h>
     28 #include <sys/file.h>
     29 #include <sys/types.h>
     30 #include <sys/pathname.h>
     31 #include <sys/proc.h>
     32 #include <sys/mode.h>
     33 #include <sys/vnode.h>
     34 #include <sys/ddi.h>
     35 #include <sys/sunddi.h>
     36 #include <sys/sunldi.h>
     37 #include <sys/uio.h>
     38 #include <sys/attr.h>
     39 #include <sys/acl.h>
     40 #include <sys/fs/zut.h>
     41 
     42 ldi_ident_t zut_li = NULL;
     43 dev_info_t *zut_dip;
     44 
     45 static int
     46 zut_open_dir(char *path, vnode_t *startvp, cred_t *cr, int flags,
     47     pathname_t *realpn, vnode_t **dvn)
     48 {
     49 	pathname_t pn;
     50 	vnode_t *vp;
     51 	vnode_t *rootvp;
     52 	proc_t *p = curproc;
     53 	int error;
     54 
     55 	pn_alloc(&pn);
     56 	(void) strlcpy(pn.pn_buf, path, MAXPATHLEN);
     57 	pn.pn_pathlen = strlen(path);
     58 
     59 	mutex_enter(&p->p_lock);	/* for u_rdir and u_cdir */
     60 	if ((rootvp = PTOU(p)->u_rdir) == NULL)
     61 		rootvp = rootdir;
     62 	else if (rootvp != rootdir)	/* no need to VN_HOLD rootdir */
     63 		VN_HOLD(rootvp);
     64 
     65 	if (pn.pn_path[0] == '/') {
     66 		vp = rootvp;
     67 	} else {
     68 		vp = (startvp == NULL) ? PTOU(p)->u_cdir : startvp;
     69 	}
     70 	VN_HOLD(vp);
     71 	mutex_exit(&p->p_lock);
     72 
     73 	/*
     74 	 * Skip over leading slashes
     75 	 */
     76 	while (pn.pn_path[0] == '/') {
     77 		pn.pn_path++;
     78 		pn.pn_pathlen--;
     79 	}
     80 
     81 	error = lookuppnvp(&pn, realpn, flags | FOLLOW, NULL,
     82 	    dvn, rootvp, vp, cr);
     83 
     84 	/*
     85 	 * If we lack read access to the directory, we should error out.
     86 	 */
     87 	if (!error) {
     88 		if (vfs_has_feature((*dvn)->v_vfsp, VFSFT_ACEMASKONACCESS)) {
     89 			error = VOP_ACCESS(*dvn, ACE_LIST_DIRECTORY,
     90 			    V_ACE_MASK, cr, NULL);
     91 		} else {
     92 			error = VOP_ACCESS(*dvn, VREAD, 0, cr, NULL);
     93 		}
     94 	}
     95 
     96 	pn_free(&pn);
     97 
     98 	return (error);
     99 }
    100 
    101 static int
    102 zut_readdir(intptr_t arg, cred_t *cr, int iflag, int *rvalp)
    103 {
    104 	zut_readdir_t *zr;
    105 	struct iovec aiov;
    106 	struct uio auio;
    107 	vnode_t *dvn = NULL;
    108 	vnode_t *fvn = NULL;
    109 	char *kbuf;
    110 	int flags = 0;
    111 	int error, rc;
    112 
    113 	zr = kmem_zalloc(sizeof (zut_readdir_t), KM_SLEEP);
    114 	error = ddi_copyin((void *)arg, zr, sizeof (zut_readdir_t), iflag);
    115 	if (error)
    116 		goto zutr_bail;
    117 
    118 	kbuf = kmem_zalloc(zr->zr_buflen, KM_SLEEP);
    119 
    120 	zr->zr_retcode = zut_open_dir(zr->zr_dir, NULL, cr, flags, NULL, &dvn);
    121 	if (zr->zr_retcode)
    122 		goto zutr_done;
    123 
    124 	if (zr->zr_reqflags & ZUT_XATTR) {
    125 		vattr_t vattr;
    126 
    127 		zr->zr_retcode = VOP_LOOKUP(dvn, zr->zr_file, &fvn,
    128 		    NULL, flags, NULL, cr, NULL, NULL, NULL);
    129 		VN_RELE(dvn);
    130 		dvn = NULL;
    131 		if (zr->zr_retcode)
    132 			goto zutr_done;
    133 
    134 		/*
    135 		 * In order to access hidden attribute directory the
    136 		 * user must have appropriate read access and be able
    137 		 * to stat() the file
    138 		 */
    139 		if (vfs_has_feature(fvn->v_vfsp, VFSFT_ACEMASKONACCESS)) {
    140 			zr->zr_retcode = VOP_ACCESS(fvn, ACE_READ_NAMED_ATTRS,
    141 			    V_ACE_MASK, cr, NULL);
    142 		} else {
    143 			zr->zr_retcode = VOP_ACCESS(fvn, VREAD, 0, cr, NULL);
    144 		}
    145 		if (zr->zr_retcode)
    146 			goto zutr_done;
    147 
    148 		vattr.va_mask = AT_ALL;
    149 		zr->zr_retcode = VOP_GETATTR(fvn, &vattr, 0, cr, NULL);
    150 		if (zr->zr_retcode)
    151 			goto zutr_done;
    152 
    153 		zr->zr_retcode = VOP_LOOKUP(fvn, "", &dvn, NULL,
    154 		    flags | LOOKUP_XATTR, NULL, cr, NULL, NULL, NULL);
    155 		VN_RELE(fvn);
    156 		if (zr->zr_retcode)
    157 			goto zutr_done;
    158 	}
    159 
    160 	aiov.iov_base = kbuf;
    161 	aiov.iov_len = zr->zr_buflen;
    162 	auio.uio_iov = &aiov;
    163 	auio.uio_iovcnt = 1;
    164 	auio.uio_loffset = zr->zr_loffset;
    165 	auio.uio_segflg = UIO_SYSSPACE;
    166 	auio.uio_resid = zr->zr_buflen;
    167 	auio.uio_fmode = 0;
    168 	auio.uio_extflg = UIO_COPY_CACHED;
    169 
    170 	if (zr->zr_reqflags & ZUT_EXTRDDIR)
    171 		flags |= V_RDDIR_ENTFLAGS;
    172 	if (zr->zr_reqflags & ZUT_ACCFILTER)
    173 		flags |= V_RDDIR_ACCFILTER;
    174 
    175 	(void) VOP_RWLOCK(dvn, V_WRITELOCK_FALSE, NULL);
    176 	zr->zr_retcode = VOP_READDIR(dvn, &auio, cr, &zr->zr_eof,
    177 	    NULL, flags);
    178 	VOP_RWUNLOCK(dvn, V_WRITELOCK_FALSE, NULL);
    179 	VN_RELE(dvn);
    180 
    181 	zr->zr_bytes = aiov.iov_base - kbuf;
    182 	zr->zr_loffset = auio.uio_loffset;
    183 
    184 	error = ddi_copyout(kbuf, (void *)(uintptr_t)zr->zr_buf,
    185 	    zr->zr_buflen, iflag);
    186 
    187 zutr_done:
    188 	kmem_free(kbuf, zr->zr_buflen);
    189 	rc = ddi_copyout(zr, (void *)arg, sizeof (zut_readdir_t), iflag);
    190 	if (error == 0)
    191 		error = rc;
    192 
    193 zutr_bail:
    194 	kmem_free(zr, sizeof (zut_readdir_t));
    195 	if (rvalp)
    196 		*rvalp = error;
    197 	return (error);
    198 }
    199 
    200 static int
    201 zut_stat64(vnode_t *vp, struct stat64 *sb, uint64_t *xvs, int flag, cred_t *cr)
    202 {
    203 	xoptattr_t *xoap = NULL;
    204 	xvattr_t xv = { 0 };
    205 	int error;
    206 
    207 	xva_init(&xv);
    208 
    209 	XVA_SET_REQ(&xv, XAT_ARCHIVE);
    210 	XVA_SET_REQ(&xv, XAT_SYSTEM);
    211 	XVA_SET_REQ(&xv, XAT_READONLY);
    212 	XVA_SET_REQ(&xv, XAT_HIDDEN);
    213 	XVA_SET_REQ(&xv, XAT_NOUNLINK);
    214 	XVA_SET_REQ(&xv, XAT_IMMUTABLE);
    215 	XVA_SET_REQ(&xv, XAT_APPENDONLY);
    216 	XVA_SET_REQ(&xv, XAT_NODUMP);
    217 	XVA_SET_REQ(&xv, XAT_OPAQUE);
    218 	XVA_SET_REQ(&xv, XAT_AV_QUARANTINED);
    219 	XVA_SET_REQ(&xv, XAT_AV_MODIFIED);
    220 	XVA_SET_REQ(&xv, XAT_REPARSE);
    221 
    222 	xv.xva_vattr.va_mask |= AT_STAT | AT_NBLOCKS | AT_BLKSIZE | AT_SIZE;
    223 	if (error = VOP_GETATTR(vp, &xv.xva_vattr, flag, cr, NULL))
    224 		return (error);
    225 
    226 	bzero(sb, sizeof (sb));
    227 	sb->st_dev = xv.xva_vattr.va_fsid;
    228 	sb->st_ino = xv.xva_vattr.va_nodeid;
    229 	sb->st_mode = VTTOIF(xv.xva_vattr.va_type) | xv.xva_vattr.va_mode;
    230 	sb->st_nlink = xv.xva_vattr.va_nlink;
    231 	sb->st_uid = xv.xva_vattr.va_uid;
    232 	sb->st_gid = xv.xva_vattr.va_gid;
    233 	sb->st_rdev = xv.xva_vattr.va_rdev;
    234 	sb->st_size = xv.xva_vattr.va_size;
    235 	sb->st_atim = xv.xva_vattr.va_atime;
    236 	sb->st_mtim = xv.xva_vattr.va_mtime;
    237 	sb->st_ctim = xv.xva_vattr.va_ctime;
    238 	sb->st_blksize = xv.xva_vattr.va_blksize;
    239 	sb->st_blocks = xv.xva_vattr.va_nblocks;
    240 	sb->st_fstype[0] = 0;
    241 
    242 	if ((xoap = xva_getxoptattr(&xv)) == NULL)
    243 		return (0);
    244 
    245 	if (XVA_ISSET_RTN(&xv, XAT_ARCHIVE) && xoap->xoa_archive)
    246 		*xvs |= (1 << F_ARCHIVE);
    247 	if (XVA_ISSET_RTN(&xv, XAT_SYSTEM) && xoap->xoa_system)
    248 		*xvs |= (1 << F_SYSTEM);
    249 	if (XVA_ISSET_RTN(&xv, XAT_READONLY) && xoap->xoa_readonly)
    250 		*xvs |= (1 << F_READONLY);
    251 	if (XVA_ISSET_RTN(&xv, XAT_HIDDEN) && xoap->xoa_hidden)
    252 		*xvs |= (1 << F_HIDDEN);
    253 	if (XVA_ISSET_RTN(&xv, XAT_NOUNLINK) && xoap->xoa_nounlink)
    254 		*xvs |= (1 << F_NOUNLINK);
    255 	if (XVA_ISSET_RTN(&xv, XAT_IMMUTABLE) && xoap->xoa_immutable)
    256 		*xvs |= (1 << F_IMMUTABLE);
    257 	if (XVA_ISSET_RTN(&xv, XAT_APPENDONLY) && xoap->xoa_appendonly)
    258 		*xvs |= (1 << F_APPENDONLY);
    259 	if (XVA_ISSET_RTN(&xv, XAT_NODUMP) && xoap->xoa_nodump)
    260 		*xvs |= (1 << F_NODUMP);
    261 	if (XVA_ISSET_RTN(&xv, XAT_OPAQUE) && xoap->xoa_opaque)
    262 		*xvs |= (1 << F_OPAQUE);
    263 	if (XVA_ISSET_RTN(&xv, XAT_AV_QUARANTINED) && xoap->xoa_av_quarantined)
    264 		*xvs |= (1 << F_AV_QUARANTINED);
    265 	if (XVA_ISSET_RTN(&xv, XAT_AV_MODIFIED) && xoap->xoa_av_modified)
    266 		*xvs |= (1 << F_AV_MODIFIED);
    267 	if (XVA_ISSET_RTN(&xv, XAT_REPARSE) && xoap->xoa_reparse)
    268 		*xvs |= (1 << F_REPARSE);
    269 
    270 	return (0);
    271 }
    272 
    273 /*ARGSUSED*/
    274 static int
    275 zut_lookup(intptr_t arg, cred_t *cr, int iflag, int *rvalp)
    276 {
    277 	zut_lookup_t *zl;
    278 	pathname_t rpn;
    279 	vnode_t *dvn = NULL;
    280 	vnode_t *fvn = NULL;
    281 	vnode_t *xdvn = NULL;
    282 	vnode_t *xfvn = NULL;
    283 	vnode_t *release = NULL;
    284 	int flags = 0;
    285 	int error, rc;
    286 
    287 	zl = kmem_zalloc(sizeof (zut_lookup_t), KM_SLEEP);
    288 
    289 	error = ddi_copyin((void *)arg, zl, sizeof (zut_lookup_t), iflag);
    290 	if (error)
    291 		goto zutl_bail;
    292 
    293 	pn_alloc(&rpn);
    294 	bzero(rpn.pn_buf, MAXPATHLEN);
    295 
    296 	zl->zl_retcode = zut_open_dir(zl->zl_dir, NULL, cr, flags, &rpn, &dvn);
    297 	if (zl->zl_retcode)
    298 		goto zutl_done;
    299 
    300 	if (zl->zl_reqflags & ZUT_IGNORECASE)
    301 		flags |= FIGNORECASE;
    302 
    303 	zl->zl_retcode = VOP_LOOKUP(dvn, zl->zl_file, &fvn, NULL, flags, NULL,
    304 	    cr, NULL, &zl->zl_deflags, &rpn);
    305 	if (zl->zl_retcode)
    306 		goto zutl_done;
    307 
    308 	release = fvn;
    309 
    310 	if (zl->zl_reqflags & ZUT_XATTR) {
    311 		vattr_t vattr;
    312 
    313 		/*
    314 		 * In order to access hidden attribute directory the
    315 		 * user must have appropriate read access and be able
    316 		 * to stat() the file
    317 		 */
    318 		if (vfs_has_feature(fvn->v_vfsp, VFSFT_ACEMASKONACCESS)) {
    319 			zl->zl_retcode = VOP_ACCESS(fvn, ACE_READ_NAMED_ATTRS,
    320 			    V_ACE_MASK, cr, NULL);
    321 		} else {
    322 			zl->zl_retcode = VOP_ACCESS(fvn, VREAD, 0, cr, NULL);
    323 		}
    324 		if (zl->zl_retcode)
    325 			goto zutl_done;
    326 
    327 		vattr.va_mask = AT_ALL;
    328 		zl->zl_retcode = VOP_GETATTR(fvn, &vattr, 0, cr, NULL);
    329 		if (zl->zl_retcode)
    330 			goto zutl_done;
    331 
    332 		zl->zl_retcode = VOP_LOOKUP(fvn, "", &xdvn, NULL,
    333 		    flags | LOOKUP_XATTR, NULL, cr, NULL, NULL, NULL);
    334 		if (zl->zl_retcode)
    335 			goto zutl_done;
    336 		VN_RELE(fvn);
    337 		release = xdvn;
    338 
    339 		zl->zl_retcode = VOP_LOOKUP(xdvn, zl->zl_xfile, &xfvn,
    340 		    NULL, flags, NULL, cr, NULL, &zl->zl_deflags, &rpn);
    341 		if (zl->zl_retcode)
    342 			goto zutl_done;
    343 		VN_RELE(xdvn);
    344 		release = xfvn;
    345 	}
    346 
    347 	if (zl->zl_reqflags & ZUT_GETSTAT) {
    348 		zl->zl_retcode = zut_stat64(release,
    349 		    &zl->zl_statbuf, &zl->zl_xvattrs, 0, cr);
    350 	}
    351 
    352 zutl_done:
    353 	(void) strlcpy(zl->zl_real, rpn.pn_path, MAXPATHLEN);
    354 
    355 	rc = ddi_copyout(zl, (void *)arg, sizeof (zut_lookup_t), iflag);
    356 	if (error == 0)
    357 		error = rc;
    358 
    359 	if (release)
    360 		VN_RELE(release);
    361 	if (dvn)
    362 		VN_RELE(dvn);
    363 	pn_free(&rpn);
    364 
    365 zutl_bail:
    366 	kmem_free(zl, sizeof (zut_lookup_t));
    367 	if (rvalp)
    368 		*rvalp = error;
    369 	return (error);
    370 }
    371 
    372 /*ARGSUSED*/
    373 static int
    374 zut_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
    375 {
    376 	int error;
    377 
    378 	if (getminor(dev) != 0)
    379 		return (ENXIO);
    380 
    381 	if (cmd <= ZUT_IOC_MIN_CMD || cmd >= ZUT_IOC_MAX_CMD)
    382 		return (EINVAL);
    383 
    384 	switch (cmd) {
    385 	case ZUT_IOC_LOOKUP:
    386 		error = zut_lookup(arg, cr, flag, rvalp);
    387 		break;
    388 	case ZUT_IOC_READDIR:
    389 		error = zut_readdir(arg, cr, flag, rvalp);
    390 	default:
    391 		break;
    392 	}
    393 
    394 	return (error);
    395 }
    396 
    397 static int
    398 zut_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
    399 {
    400 	if (cmd != DDI_ATTACH)
    401 		return (DDI_FAILURE);
    402 
    403 	if (ddi_create_minor_node(dip, "zut", S_IFCHR, 0,
    404 	    DDI_PSEUDO, 0) == DDI_FAILURE)
    405 		return (DDI_FAILURE);
    406 
    407 	zut_dip = dip;
    408 
    409 	ddi_report_dev(dip);
    410 
    411 	return (DDI_SUCCESS);
    412 }
    413 
    414 static int
    415 zut_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
    416 {
    417 	if (cmd != DDI_DETACH)
    418 		return (DDI_FAILURE);
    419 
    420 	zut_dip = NULL;
    421 
    422 	ddi_prop_remove_all(dip);
    423 	ddi_remove_minor_node(dip, NULL);
    424 
    425 	return (DDI_SUCCESS);
    426 }
    427 
    428 /*ARGSUSED*/
    429 static int
    430 zut_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
    431 {
    432 	switch (infocmd) {
    433 	case DDI_INFO_DEVT2DEVINFO:
    434 		*result = zut_dip;
    435 		return (DDI_SUCCESS);
    436 
    437 	case DDI_INFO_DEVT2INSTANCE:
    438 		*result = (void *)0;
    439 		return (DDI_SUCCESS);
    440 	}
    441 
    442 	return (DDI_FAILURE);
    443 }
    444 
    445 /*ARGSUSED*/
    446 int
    447 zut_open(dev_t *devp, int flag, int otyp, cred_t *cr)
    448 {
    449 	minor_t minor = getminor(*devp);
    450 
    451 	if (minor == 0)			/* This is the control device */
    452 		return (0);
    453 
    454 	return (ENXIO);
    455 }
    456 
    457 /*ARGSUSED*/
    458 int
    459 zut_close(dev_t dev, int flag, int otyp, cred_t *cr)
    460 {
    461 	minor_t minor = getminor(dev);
    462 
    463 	if (minor == 0)		/* This is the control device */
    464 		return (0);
    465 
    466 	return (ENXIO);
    467 }
    468 
    469 /*
    470  * /dev/zut is the control node, i.e. minor 0.
    471  *
    472  * There are no other minor nodes, and /dev/zut basically does nothing
    473  * other than serve up ioctls.
    474  */
    475 static struct cb_ops zut_cb_ops = {
    476 	zut_open,	/* open */
    477 	zut_close,	/* close */
    478 	nodev,		/* strategy */
    479 	nodev,		/* print */
    480 	nodev,		/* dump */
    481 	nodev,		/* read */
    482 	nodev,		/* write */
    483 	zut_ioctl,	/* ioctl */
    484 	nodev,		/* devmap */
    485 	nodev,		/* mmap */
    486 	nodev,		/* segmap */
    487 	nochpoll,	/* poll */
    488 	ddi_prop_op,	/* prop_op */
    489 	NULL,		/* streamtab */
    490 	D_NEW | D_MP | D_64BIT,		/* Driver compatibility flag */
    491 	CB_REV,		/* version */
    492 	nodev,		/* async read */
    493 	nodev,		/* async write */
    494 };
    495 
    496 static struct dev_ops zut_dev_ops = {
    497 	DEVO_REV,	/* version */
    498 	0,		/* refcnt */
    499 	zut_info,	/* info */
    500 	nulldev,	/* identify */
    501 	nulldev,	/* probe */
    502 	zut_attach,	/* attach */
    503 	zut_detach,	/* detach */
    504 	nodev,		/* reset */
    505 	&zut_cb_ops,	/* driver operations */
    506 	NULL		/* no bus operations */
    507 };
    508 
    509 static struct modldrv zut_modldrv = {
    510 	&mod_driverops, "ZFS unit test " ZUT_VERSION_STRING,
    511 	    &zut_dev_ops
    512 };
    513 
    514 static struct modlinkage modlinkage = {
    515 	MODREV_1,
    516 	(void *)&zut_modldrv,
    517 	NULL
    518 };
    519 
    520 int
    521 _init(void)
    522 {
    523 	int error;
    524 
    525 	if ((error = mod_install(&modlinkage)) != 0) {
    526 		return (error);
    527 	}
    528 
    529 	error = ldi_ident_from_mod(&modlinkage, &zut_li);
    530 	ASSERT(error == 0);
    531 
    532 	return (0);
    533 }
    534 
    535 int
    536 _fini(void)
    537 {
    538 	int error;
    539 
    540 	if ((error = mod_remove(&modlinkage)) != 0)
    541 		return (error);
    542 
    543 	ldi_ident_release(zut_li);
    544 	zut_li = NULL;
    545 
    546 	return (error);
    547 }
    548 
    549 int
    550 _info(struct modinfo *modinfop)
    551 {
    552 	return (mod_info(&modlinkage, modinfop));
    553 }
    554