xref: /illumos-gate/usr/src/uts/common/fs/zut/zut.c (revision d616ad8ecd9216bbe9e7c0d0b9fb3f00d4cd5505)
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