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