xref: /onnv-gate/usr/src/uts/common/fs/dev/sdev_zvolops.c (revision 10763:f1a11aaa04fc)
110588SEric.Taylor@Sun.COM /*
210588SEric.Taylor@Sun.COM  * CDDL HEADER START
310588SEric.Taylor@Sun.COM  *
410588SEric.Taylor@Sun.COM  * The contents of this file are subject to the terms of the
510588SEric.Taylor@Sun.COM  * Common Development and Distribution License (the "License").
610588SEric.Taylor@Sun.COM  * You may not use this file except in compliance with the License.
710588SEric.Taylor@Sun.COM  *
810588SEric.Taylor@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910588SEric.Taylor@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010588SEric.Taylor@Sun.COM  * See the License for the specific language governing permissions
1110588SEric.Taylor@Sun.COM  * and limitations under the License.
1210588SEric.Taylor@Sun.COM  *
1310588SEric.Taylor@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410588SEric.Taylor@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510588SEric.Taylor@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610588SEric.Taylor@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710588SEric.Taylor@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810588SEric.Taylor@Sun.COM  *
1910588SEric.Taylor@Sun.COM  * CDDL HEADER END
2010588SEric.Taylor@Sun.COM  */
2110588SEric.Taylor@Sun.COM /*
2210588SEric.Taylor@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2310588SEric.Taylor@Sun.COM  * Use is subject to license terms.
2410588SEric.Taylor@Sun.COM  */
2510588SEric.Taylor@Sun.COM 
2610588SEric.Taylor@Sun.COM /* vnode ops for the /dev/zvol directory */
2710588SEric.Taylor@Sun.COM 
2810588SEric.Taylor@Sun.COM #include <sys/types.h>
2910588SEric.Taylor@Sun.COM #include <sys/param.h>
3010588SEric.Taylor@Sun.COM #include <sys/sysmacros.h>
3110588SEric.Taylor@Sun.COM #include <sys/ddi.h>
3210588SEric.Taylor@Sun.COM #include <sys/sunndi.h>
3310588SEric.Taylor@Sun.COM #include <sys/sunldi.h>
3410588SEric.Taylor@Sun.COM #include <fs/fs_subr.h>
3510588SEric.Taylor@Sun.COM #include <sys/fs/dv_node.h>
3610588SEric.Taylor@Sun.COM #include <sys/fs/sdev_impl.h>
3710588SEric.Taylor@Sun.COM #include <sys/zfs_ioctl.h>
3810588SEric.Taylor@Sun.COM #include <sys/policy.h>
3910588SEric.Taylor@Sun.COM #include <sys/stat.h>
4010588SEric.Taylor@Sun.COM #include <sys/vfs_opreg.h>
4110588SEric.Taylor@Sun.COM 
4210588SEric.Taylor@Sun.COM struct vnodeops	*devzvol_vnodeops;
4310588SEric.Taylor@Sun.COM static uint64_t devzvol_gen = 0;
4410588SEric.Taylor@Sun.COM static uint64_t devzvol_zclist;
4510588SEric.Taylor@Sun.COM static size_t devzvol_zclist_size;
4610588SEric.Taylor@Sun.COM static ldi_ident_t devzvol_li;
4710588SEric.Taylor@Sun.COM static ldi_handle_t devzvol_lh;
4810588SEric.Taylor@Sun.COM static kmutex_t devzvol_mtx;
49*10763SEric.Taylor@Sun.COM static boolean_t devzvol_isopen;
5010588SEric.Taylor@Sun.COM 
5110588SEric.Taylor@Sun.COM /*
5210588SEric.Taylor@Sun.COM  * we need to use ddi_mod* since fs/dev gets loaded early on in
5310588SEric.Taylor@Sun.COM  * startup(), and linking fs/dev to fs/zfs would drag in a lot of
5410588SEric.Taylor@Sun.COM  * other stuff (like drv/random) before the rest of the system is
5510588SEric.Taylor@Sun.COM  * ready to go
5610588SEric.Taylor@Sun.COM  */
5710588SEric.Taylor@Sun.COM ddi_modhandle_t zfs_mod;
5810588SEric.Taylor@Sun.COM int (*szcm)(char *);
5910588SEric.Taylor@Sun.COM int (*szn2m)(char *, minor_t *);
6010588SEric.Taylor@Sun.COM 
6110588SEric.Taylor@Sun.COM int
sdev_zvol_create_minor(char * dsname)6210588SEric.Taylor@Sun.COM sdev_zvol_create_minor(char *dsname)
6310588SEric.Taylor@Sun.COM {
6410588SEric.Taylor@Sun.COM 	return ((*szcm)(dsname));
6510588SEric.Taylor@Sun.COM }
6610588SEric.Taylor@Sun.COM 
6710588SEric.Taylor@Sun.COM int
sdev_zvol_name2minor(char * dsname,minor_t * minor)6810588SEric.Taylor@Sun.COM sdev_zvol_name2minor(char *dsname, minor_t *minor)
6910588SEric.Taylor@Sun.COM {
7010588SEric.Taylor@Sun.COM 	return ((*szn2m)(dsname, minor));
7110588SEric.Taylor@Sun.COM }
7210588SEric.Taylor@Sun.COM 
7310588SEric.Taylor@Sun.COM int
devzvol_open_zfs()7410588SEric.Taylor@Sun.COM devzvol_open_zfs()
7510588SEric.Taylor@Sun.COM {
7610588SEric.Taylor@Sun.COM 	int rc;
7710588SEric.Taylor@Sun.COM 
7810588SEric.Taylor@Sun.COM 	devzvol_li = ldi_ident_from_anon();
7910588SEric.Taylor@Sun.COM 	if (ldi_open_by_name("/dev/zfs", FREAD | FWRITE, kcred,
8010588SEric.Taylor@Sun.COM 	    &devzvol_lh, devzvol_li))
8110588SEric.Taylor@Sun.COM 		return (-1);
8210588SEric.Taylor@Sun.COM 	if (zfs_mod == NULL && ((zfs_mod = ddi_modopen("fs/zfs",
8310588SEric.Taylor@Sun.COM 	    KRTLD_MODE_FIRST, &rc)) == NULL)) {
8410588SEric.Taylor@Sun.COM 		return (rc);
8510588SEric.Taylor@Sun.COM 	}
8610588SEric.Taylor@Sun.COM 	ASSERT(szcm == NULL && szn2m == NULL);
8710588SEric.Taylor@Sun.COM 	if ((szcm = (int (*)(char *))
8810588SEric.Taylor@Sun.COM 	    ddi_modsym(zfs_mod, "zvol_create_minor", &rc)) == NULL) {
8910588SEric.Taylor@Sun.COM 		cmn_err(CE_WARN, "couldn't resolve zvol_create_minor");
9010588SEric.Taylor@Sun.COM 		return (rc);
9110588SEric.Taylor@Sun.COM 	}
9210588SEric.Taylor@Sun.COM 	if ((szn2m = (int(*)(char *, minor_t *))
9310588SEric.Taylor@Sun.COM 	    ddi_modsym(zfs_mod, "zvol_name2minor", &rc)) == NULL) {
9410588SEric.Taylor@Sun.COM 		cmn_err(CE_WARN, "couldn't resolve zvol_name2minor");
9510588SEric.Taylor@Sun.COM 		return (rc);
9610588SEric.Taylor@Sun.COM 	}
9710588SEric.Taylor@Sun.COM 	return (0);
9810588SEric.Taylor@Sun.COM }
9910588SEric.Taylor@Sun.COM 
10010588SEric.Taylor@Sun.COM void
devzvol_close_zfs()10110588SEric.Taylor@Sun.COM devzvol_close_zfs()
10210588SEric.Taylor@Sun.COM {
10310588SEric.Taylor@Sun.COM 	szcm = NULL;
10410588SEric.Taylor@Sun.COM 	szn2m = NULL;
10510588SEric.Taylor@Sun.COM 	(void) ldi_close(devzvol_lh, FREAD|FWRITE, kcred);
10610588SEric.Taylor@Sun.COM 	ldi_ident_release(devzvol_li);
10710588SEric.Taylor@Sun.COM 	if (zfs_mod != NULL) {
10810588SEric.Taylor@Sun.COM 		(void) ddi_modclose(zfs_mod);
10910588SEric.Taylor@Sun.COM 		zfs_mod = NULL;
11010588SEric.Taylor@Sun.COM 	}
11110588SEric.Taylor@Sun.COM }
11210588SEric.Taylor@Sun.COM 
11310588SEric.Taylor@Sun.COM int
devzvol_handle_ioctl(int cmd,zfs_cmd_t * zc,size_t * alloc_size)11410588SEric.Taylor@Sun.COM devzvol_handle_ioctl(int cmd, zfs_cmd_t *zc, size_t *alloc_size)
11510588SEric.Taylor@Sun.COM {
11610588SEric.Taylor@Sun.COM 	uint64_t cookie;
11710588SEric.Taylor@Sun.COM 	int size = 8000;
11810588SEric.Taylor@Sun.COM 	int unused;
11910588SEric.Taylor@Sun.COM 	int rc;
12010588SEric.Taylor@Sun.COM 
12110588SEric.Taylor@Sun.COM 	if (cmd != ZFS_IOC_POOL_CONFIGS)
12210588SEric.Taylor@Sun.COM 		mutex_enter(&devzvol_mtx);
123*10763SEric.Taylor@Sun.COM 	if (!devzvol_isopen) {
12410588SEric.Taylor@Sun.COM 		if ((rc = devzvol_open_zfs()) == 0) {
125*10763SEric.Taylor@Sun.COM 			devzvol_isopen = B_TRUE;
12610588SEric.Taylor@Sun.COM 		} else {
12710588SEric.Taylor@Sun.COM 			if (cmd != ZFS_IOC_POOL_CONFIGS)
12810588SEric.Taylor@Sun.COM 				mutex_exit(&devzvol_mtx);
12910588SEric.Taylor@Sun.COM 			return (ENXIO);
13010588SEric.Taylor@Sun.COM 		}
13110588SEric.Taylor@Sun.COM 	}
13210588SEric.Taylor@Sun.COM 	cookie = zc->zc_cookie;
13310588SEric.Taylor@Sun.COM again:
13410588SEric.Taylor@Sun.COM 	zc->zc_nvlist_dst = (uint64_t)(intptr_t)kmem_alloc(size,
13510588SEric.Taylor@Sun.COM 	    KM_SLEEP);
13610588SEric.Taylor@Sun.COM 	zc->zc_nvlist_dst_size = size;
13710588SEric.Taylor@Sun.COM 	rc = ldi_ioctl(devzvol_lh, cmd, (intptr_t)zc, FKIOCTL, kcred,
13810588SEric.Taylor@Sun.COM 	    &unused);
13910588SEric.Taylor@Sun.COM 	if (rc == ENOMEM) {
14010588SEric.Taylor@Sun.COM 		int newsize;
14110588SEric.Taylor@Sun.COM 		newsize = zc->zc_nvlist_dst_size;
14210588SEric.Taylor@Sun.COM 		ASSERT(newsize > size);
14310588SEric.Taylor@Sun.COM 		kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
14410588SEric.Taylor@Sun.COM 		size = newsize;
14510588SEric.Taylor@Sun.COM 		zc->zc_cookie = cookie;
14610588SEric.Taylor@Sun.COM 		goto again;
14710588SEric.Taylor@Sun.COM 	}
14810588SEric.Taylor@Sun.COM 	if (alloc_size == NULL)
14910588SEric.Taylor@Sun.COM 		kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
15010588SEric.Taylor@Sun.COM 	else
15110588SEric.Taylor@Sun.COM 		*alloc_size = size;
15210588SEric.Taylor@Sun.COM 	if (cmd != ZFS_IOC_POOL_CONFIGS)
15310588SEric.Taylor@Sun.COM 		mutex_exit(&devzvol_mtx);
15410588SEric.Taylor@Sun.COM 	return (rc);
15510588SEric.Taylor@Sun.COM }
15610588SEric.Taylor@Sun.COM 
15710588SEric.Taylor@Sun.COM /* figures out if the objset exists and returns its type */
15810588SEric.Taylor@Sun.COM int
devzvol_objset_check(char * dsname,dmu_objset_type_t * type)15910588SEric.Taylor@Sun.COM devzvol_objset_check(char *dsname, dmu_objset_type_t *type)
16010588SEric.Taylor@Sun.COM {
16110588SEric.Taylor@Sun.COM 	boolean_t	ispool;
16210588SEric.Taylor@Sun.COM 	zfs_cmd_t	*zc;
16310588SEric.Taylor@Sun.COM 	int rc;
16410588SEric.Taylor@Sun.COM 
16510588SEric.Taylor@Sun.COM 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
16610588SEric.Taylor@Sun.COM 	(void) strlcpy(zc->zc_name, dsname, MAXPATHLEN);
16710588SEric.Taylor@Sun.COM 
16810588SEric.Taylor@Sun.COM 	ispool = (strchr(dsname, '/') == NULL) ? B_TRUE : B_FALSE;
16910588SEric.Taylor@Sun.COM 	if (!ispool && sdev_zvol_name2minor(dsname, NULL) == 0) {
17010588SEric.Taylor@Sun.COM 		sdcmn_err13(("found cached minor node"));
17110588SEric.Taylor@Sun.COM 		if (type)
17210588SEric.Taylor@Sun.COM 			*type = DMU_OST_ZVOL;
17310588SEric.Taylor@Sun.COM 		kmem_free(zc, sizeof (zfs_cmd_t));
17410588SEric.Taylor@Sun.COM 		return (0);
17510588SEric.Taylor@Sun.COM 	}
17610588SEric.Taylor@Sun.COM 	rc = devzvol_handle_ioctl(ispool ? ZFS_IOC_POOL_STATS :
17710588SEric.Taylor@Sun.COM 	    ZFS_IOC_OBJSET_STATS, zc, NULL);
17810588SEric.Taylor@Sun.COM 	if (type && rc == 0)
17910588SEric.Taylor@Sun.COM 		*type = (ispool) ? DMU_OST_ZFS :
18010588SEric.Taylor@Sun.COM 		    zc->zc_objset_stats.dds_type;
18110588SEric.Taylor@Sun.COM 	kmem_free(zc, sizeof (zfs_cmd_t));
18210588SEric.Taylor@Sun.COM 	return (rc);
18310588SEric.Taylor@Sun.COM }
18410588SEric.Taylor@Sun.COM 
18510588SEric.Taylor@Sun.COM /*
18610588SEric.Taylor@Sun.COM  * returns what the zfs dataset name should be, given the /dev/zvol
18710588SEric.Taylor@Sun.COM  * path and an optional name; otherwise NULL
18810588SEric.Taylor@Sun.COM  */
18910588SEric.Taylor@Sun.COM char *
devzvol_make_dsname(const char * path,const char * name)19010588SEric.Taylor@Sun.COM devzvol_make_dsname(const char *path, const char *name)
19110588SEric.Taylor@Sun.COM {
19210588SEric.Taylor@Sun.COM 	char *dsname;
19310588SEric.Taylor@Sun.COM 	const char *ptr;
19410588SEric.Taylor@Sun.COM 	int dslen;
19510588SEric.Taylor@Sun.COM 
19610588SEric.Taylor@Sun.COM 	if (strcmp(path, ZVOL_DIR) == 0)
19710588SEric.Taylor@Sun.COM 		return (NULL);
19810588SEric.Taylor@Sun.COM 	if (name && (strcmp(name, ".") == 0 || strcmp(name, "..") == 0))
19910588SEric.Taylor@Sun.COM 		return (NULL);
20010588SEric.Taylor@Sun.COM 	ptr = path + strlen(ZVOL_DIR);
20110588SEric.Taylor@Sun.COM 	if (strncmp(ptr, "/dsk", 4) == 0)
20210588SEric.Taylor@Sun.COM 		ptr += strlen("/dsk");
20310588SEric.Taylor@Sun.COM 	else if (strncmp(ptr, "/rdsk", 5) == 0)
20410588SEric.Taylor@Sun.COM 		ptr += strlen("/rdsk");
20510588SEric.Taylor@Sun.COM 	else
20610588SEric.Taylor@Sun.COM 		return (NULL);
20710588SEric.Taylor@Sun.COM 	if (*ptr == '/')
20810588SEric.Taylor@Sun.COM 		ptr++;
20910588SEric.Taylor@Sun.COM 
21010588SEric.Taylor@Sun.COM 	dslen = strlen(ptr);
21110588SEric.Taylor@Sun.COM 	if (dslen)
21210588SEric.Taylor@Sun.COM 		dslen++;			/* plus null */
21310588SEric.Taylor@Sun.COM 	if (name)
21410588SEric.Taylor@Sun.COM 		dslen += strlen(name) + 1;	/* plus slash */
21510588SEric.Taylor@Sun.COM 	dsname = kmem_zalloc(dslen, KM_SLEEP);
21610588SEric.Taylor@Sun.COM 	if (*ptr) {
21710588SEric.Taylor@Sun.COM 		(void) strlcpy(dsname, ptr, dslen);
21810588SEric.Taylor@Sun.COM 		if (name)
21910588SEric.Taylor@Sun.COM 			(void) strlcat(dsname, "/", dslen);
22010588SEric.Taylor@Sun.COM 	}
22110588SEric.Taylor@Sun.COM 	if (name)
22210588SEric.Taylor@Sun.COM 		(void) strlcat(dsname, name, dslen);
22310588SEric.Taylor@Sun.COM 	return (dsname);
22410588SEric.Taylor@Sun.COM }
22510588SEric.Taylor@Sun.COM 
22610588SEric.Taylor@Sun.COM /*
22710588SEric.Taylor@Sun.COM  * check if the zvol's sdev_node is still valid, which means make
22810588SEric.Taylor@Sun.COM  * sure the zvol is still valid.  zvol minors aren't proactively
22910588SEric.Taylor@Sun.COM  * destroyed when the zvol is destroyed, so we use a validator to clean
23010588SEric.Taylor@Sun.COM  * these up (in other words, when such nodes are encountered during
23110588SEric.Taylor@Sun.COM  * subsequent lookup() and readdir() operations) so that only valid
23210588SEric.Taylor@Sun.COM  * nodes are returned.  The ordering between devname_lookup_func and
23310588SEric.Taylor@Sun.COM  * devzvol_validate is a little inefficient in the case of invalid
23410588SEric.Taylor@Sun.COM  * or stale nodes because devname_lookup_func calls
23510588SEric.Taylor@Sun.COM  * devzvol_create_{dir, link}, then the validator says it's invalid,
23610588SEric.Taylor@Sun.COM  * and then the node gets cleaned up.
23710588SEric.Taylor@Sun.COM  */
23810588SEric.Taylor@Sun.COM int
devzvol_validate(struct sdev_node * dv)23910588SEric.Taylor@Sun.COM devzvol_validate(struct sdev_node *dv)
24010588SEric.Taylor@Sun.COM {
24110588SEric.Taylor@Sun.COM 	dmu_objset_type_t do_type;
24210588SEric.Taylor@Sun.COM 	char *dsname;
24310588SEric.Taylor@Sun.COM 	char *nm = dv->sdev_name;
24410588SEric.Taylor@Sun.COM 	int rc;
24510588SEric.Taylor@Sun.COM 
24610588SEric.Taylor@Sun.COM 	sdcmn_err13(("validating ('%s' '%s')", dv->sdev_path, nm));
24710588SEric.Taylor@Sun.COM 	/*
24810588SEric.Taylor@Sun.COM 	 * validate only READY nodes; if someone is sitting on the
24910588SEric.Taylor@Sun.COM 	 * directory of a dataset that just got destroyed we could
25010588SEric.Taylor@Sun.COM 	 * get a zombie node which we just skip.
25110588SEric.Taylor@Sun.COM 	 */
25210588SEric.Taylor@Sun.COM 	if (dv->sdev_state != SDEV_READY) {
25310588SEric.Taylor@Sun.COM 		sdcmn_err13(("skipping '%s'", nm));
25410588SEric.Taylor@Sun.COM 		return (SDEV_VTOR_SKIP);
25510588SEric.Taylor@Sun.COM 	}
25610588SEric.Taylor@Sun.COM 
25710588SEric.Taylor@Sun.COM 	if ((strcmp(dv->sdev_path, ZVOL_DIR "/dsk") == 0) ||
25810588SEric.Taylor@Sun.COM 	    (strcmp(dv->sdev_path, ZVOL_DIR "/rdsk") == 0))
25910588SEric.Taylor@Sun.COM 		return (SDEV_VTOR_VALID);
26010588SEric.Taylor@Sun.COM 	dsname = devzvol_make_dsname(dv->sdev_path, NULL);
26110588SEric.Taylor@Sun.COM 	if (dsname == NULL)
26210588SEric.Taylor@Sun.COM 		return (SDEV_VTOR_INVALID);
26310588SEric.Taylor@Sun.COM 
26410588SEric.Taylor@Sun.COM 	rc = devzvol_objset_check(dsname, &do_type);
26510588SEric.Taylor@Sun.COM 	sdcmn_err13(("  '%s' rc %d", dsname, rc));
26610588SEric.Taylor@Sun.COM 	if (rc != 0) {
26710588SEric.Taylor@Sun.COM 		kmem_free(dsname, strlen(dsname) + 1);
26810588SEric.Taylor@Sun.COM 		return (SDEV_VTOR_INVALID);
26910588SEric.Taylor@Sun.COM 	}
27010588SEric.Taylor@Sun.COM 	sdcmn_err13(("  v_type %d do_type %d",
27110588SEric.Taylor@Sun.COM 	    SDEVTOV(dv)->v_type, do_type));
27210588SEric.Taylor@Sun.COM 	if ((SDEVTOV(dv)->v_type == VLNK && do_type != DMU_OST_ZVOL) ||
27310588SEric.Taylor@Sun.COM 	    (SDEVTOV(dv)->v_type == VDIR && do_type == DMU_OST_ZVOL)) {
27410588SEric.Taylor@Sun.COM 		kmem_free(dsname, strlen(dsname) + 1);
27510588SEric.Taylor@Sun.COM 		return (SDEV_VTOR_STALE);
27610588SEric.Taylor@Sun.COM 	}
27710588SEric.Taylor@Sun.COM 	if (SDEVTOV(dv)->v_type == VLNK) {
27810588SEric.Taylor@Sun.COM 		char *ptr, *link;
27910588SEric.Taylor@Sun.COM 		long val = 0;
28010588SEric.Taylor@Sun.COM 		minor_t lminor, ominor;
28110588SEric.Taylor@Sun.COM 
28210588SEric.Taylor@Sun.COM 		rc = sdev_getlink(SDEVTOV(dv), &link);
28310588SEric.Taylor@Sun.COM 		ASSERT(rc == 0);
28410588SEric.Taylor@Sun.COM 
28510588SEric.Taylor@Sun.COM 		ptr = strrchr(link, ':') + 1;
28610588SEric.Taylor@Sun.COM 		rc = ddi_strtol(ptr, NULL, 10, &val);
28710588SEric.Taylor@Sun.COM 		kmem_free(link, strlen(link) + 1);
28810588SEric.Taylor@Sun.COM 		ASSERT(rc == 0 && val != 0);
28910588SEric.Taylor@Sun.COM 		lminor = (minor_t)val;
29010588SEric.Taylor@Sun.COM 		if (sdev_zvol_name2minor(dsname, &ominor) < 0 ||
29110588SEric.Taylor@Sun.COM 		    ominor != lminor) {
29210588SEric.Taylor@Sun.COM 			kmem_free(dsname, strlen(dsname) + 1);
29310588SEric.Taylor@Sun.COM 			return (SDEV_VTOR_STALE);
29410588SEric.Taylor@Sun.COM 		}
29510588SEric.Taylor@Sun.COM 	}
29610588SEric.Taylor@Sun.COM 	kmem_free(dsname, strlen(dsname) + 1);
29710588SEric.Taylor@Sun.COM 	return (SDEV_VTOR_VALID);
29810588SEric.Taylor@Sun.COM }
29910588SEric.Taylor@Sun.COM 
30010588SEric.Taylor@Sun.COM /*
30110588SEric.Taylor@Sun.COM  * creates directories as needed in response to a readdir
30210588SEric.Taylor@Sun.COM  */
30310588SEric.Taylor@Sun.COM void
devzvol_create_pool_dirs(struct vnode * dvp)30410588SEric.Taylor@Sun.COM devzvol_create_pool_dirs(struct vnode *dvp)
30510588SEric.Taylor@Sun.COM {
30610588SEric.Taylor@Sun.COM 	zfs_cmd_t	*zc;
30710588SEric.Taylor@Sun.COM 	nvlist_t *nv = NULL;
30810588SEric.Taylor@Sun.COM 	nvpair_t *elem = NULL;
30910588SEric.Taylor@Sun.COM 	size_t size;
31010588SEric.Taylor@Sun.COM 	int pools = 0;
31110588SEric.Taylor@Sun.COM 	int rc;
31210588SEric.Taylor@Sun.COM 
31310588SEric.Taylor@Sun.COM 	sdcmn_err13(("devzvol_create_pool_dirs"));
31410588SEric.Taylor@Sun.COM 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
31510588SEric.Taylor@Sun.COM 	mutex_enter(&devzvol_mtx);
31610588SEric.Taylor@Sun.COM 	zc->zc_cookie = devzvol_gen;
31710588SEric.Taylor@Sun.COM 
31810588SEric.Taylor@Sun.COM 	rc = devzvol_handle_ioctl(ZFS_IOC_POOL_CONFIGS, zc, &size);
31910588SEric.Taylor@Sun.COM 	switch (rc) {
32010588SEric.Taylor@Sun.COM 		case 0:
32110588SEric.Taylor@Sun.COM 			/* new generation */
32210588SEric.Taylor@Sun.COM 			ASSERT(devzvol_gen != zc->zc_cookie);
32310588SEric.Taylor@Sun.COM 			devzvol_gen = zc->zc_cookie;
32410588SEric.Taylor@Sun.COM 			if (devzvol_zclist)
32510588SEric.Taylor@Sun.COM 				kmem_free((void *)(uintptr_t)devzvol_zclist,
32610588SEric.Taylor@Sun.COM 				    devzvol_zclist_size);
32710588SEric.Taylor@Sun.COM 			devzvol_zclist = zc->zc_nvlist_dst;
32810588SEric.Taylor@Sun.COM 			devzvol_zclist_size = size;
32910588SEric.Taylor@Sun.COM 			break;
33010588SEric.Taylor@Sun.COM 		case EEXIST:
33110588SEric.Taylor@Sun.COM 			/*
33210588SEric.Taylor@Sun.COM 			 * no change in the configuration; still need
33310588SEric.Taylor@Sun.COM 			 * to do lookups in case we did a lookup in
33410588SEric.Taylor@Sun.COM 			 * zvol/rdsk but not zvol/dsk (or vice versa)
33510588SEric.Taylor@Sun.COM 			 */
33610588SEric.Taylor@Sun.COM 			kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst,
33710588SEric.Taylor@Sun.COM 			    size);
33810588SEric.Taylor@Sun.COM 			break;
33910588SEric.Taylor@Sun.COM 		default:
34010588SEric.Taylor@Sun.COM 			kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst,
34110588SEric.Taylor@Sun.COM 			    size);
34210588SEric.Taylor@Sun.COM 			goto out;
34310588SEric.Taylor@Sun.COM 	}
34410588SEric.Taylor@Sun.COM 	rc = nvlist_unpack((char *)(uintptr_t)devzvol_zclist,
34510588SEric.Taylor@Sun.COM 	    devzvol_zclist_size, &nv, 0);
34610588SEric.Taylor@Sun.COM 	if (rc) {
34710588SEric.Taylor@Sun.COM 		ASSERT(rc == 0);
34810588SEric.Taylor@Sun.COM 		kmem_free((void *)(uintptr_t)devzvol_zclist,
34910588SEric.Taylor@Sun.COM 		    devzvol_zclist_size);
35010588SEric.Taylor@Sun.COM 		devzvol_gen = 0;
35110588SEric.Taylor@Sun.COM 		devzvol_zclist = NULL;
35210588SEric.Taylor@Sun.COM 		devzvol_zclist_size = 0;
35310588SEric.Taylor@Sun.COM 		goto out;
35410588SEric.Taylor@Sun.COM 	}
35510588SEric.Taylor@Sun.COM 	mutex_exit(&devzvol_mtx);
35610588SEric.Taylor@Sun.COM 	while ((elem = nvlist_next_nvpair(nv, elem)) != NULL) {
35710588SEric.Taylor@Sun.COM 		struct vnode *vp;
35810588SEric.Taylor@Sun.COM 		ASSERT(dvp->v_count > 0);
35910588SEric.Taylor@Sun.COM 		rc = VOP_LOOKUP(dvp, nvpair_name(elem), &vp, NULL, 0,
36010588SEric.Taylor@Sun.COM 		    NULL, kcred, NULL, 0, NULL);
36110588SEric.Taylor@Sun.COM 		/* should either work, or not be visible from a zone */
36210588SEric.Taylor@Sun.COM 		ASSERT(rc == 0 || rc == ENOENT);
36310588SEric.Taylor@Sun.COM 		if (rc == 0)
36410588SEric.Taylor@Sun.COM 			VN_RELE(vp);
36510588SEric.Taylor@Sun.COM 		pools++;
36610588SEric.Taylor@Sun.COM 	}
36710588SEric.Taylor@Sun.COM 	nvlist_free(nv);
36810588SEric.Taylor@Sun.COM 	mutex_enter(&devzvol_mtx);
369*10763SEric.Taylor@Sun.COM 	if (devzvol_isopen && pools == 0) {
37010588SEric.Taylor@Sun.COM 		/* clean up so zfs can be unloaded */
37110588SEric.Taylor@Sun.COM 		devzvol_close_zfs();
372*10763SEric.Taylor@Sun.COM 		devzvol_isopen = B_FALSE;
37310588SEric.Taylor@Sun.COM 	}
37410588SEric.Taylor@Sun.COM out:
37510588SEric.Taylor@Sun.COM 	mutex_exit(&devzvol_mtx);
37610588SEric.Taylor@Sun.COM 	kmem_free(zc, sizeof (zfs_cmd_t));
37710588SEric.Taylor@Sun.COM }
37810588SEric.Taylor@Sun.COM 
37910588SEric.Taylor@Sun.COM /*ARGSUSED3*/
38010588SEric.Taylor@Sun.COM static int
devzvol_create_dir(struct sdev_node * ddv,char * nm,void ** arg,cred_t * cred,void * whatever,char * whichever)38110588SEric.Taylor@Sun.COM devzvol_create_dir(struct sdev_node *ddv, char *nm, void **arg,
38210588SEric.Taylor@Sun.COM     cred_t *cred, void *whatever, char *whichever)
38310588SEric.Taylor@Sun.COM {
38410588SEric.Taylor@Sun.COM 	timestruc_t now;
38510588SEric.Taylor@Sun.COM 	struct vattr *vap = (struct vattr *)arg;
38610588SEric.Taylor@Sun.COM 
38710588SEric.Taylor@Sun.COM 	sdcmn_err13(("create_dir (%s) (%s) '%s'", ddv->sdev_name,
38810588SEric.Taylor@Sun.COM 	    ddv->sdev_path, nm));
38910588SEric.Taylor@Sun.COM 	ASSERT(strncmp(ddv->sdev_path, ZVOL_DIR,
39010588SEric.Taylor@Sun.COM 	    strlen(ZVOL_DIR)) == 0);
39110588SEric.Taylor@Sun.COM 	*vap = *sdev_getdefault_attr(VDIR);
39210588SEric.Taylor@Sun.COM 	gethrestime(&now);
39310588SEric.Taylor@Sun.COM 	vap->va_atime = now;
39410588SEric.Taylor@Sun.COM 	vap->va_mtime = now;
39510588SEric.Taylor@Sun.COM 	vap->va_ctime = now;
39610588SEric.Taylor@Sun.COM 	return (0);
39710588SEric.Taylor@Sun.COM }
39810588SEric.Taylor@Sun.COM 
39910588SEric.Taylor@Sun.COM /*ARGSUSED3*/
40010588SEric.Taylor@Sun.COM static int
devzvol_create_link(struct sdev_node * ddv,char * nm,void ** arg,cred_t * cred,void * whatever,char * whichever)40110588SEric.Taylor@Sun.COM devzvol_create_link(struct sdev_node *ddv, char *nm,
40210588SEric.Taylor@Sun.COM     void **arg, cred_t *cred, void *whatever, char *whichever)
40310588SEric.Taylor@Sun.COM {
40410588SEric.Taylor@Sun.COM 	minor_t minor;
40510588SEric.Taylor@Sun.COM 	char *pathname = (char *)*arg;
40610588SEric.Taylor@Sun.COM 	int rc;
40710588SEric.Taylor@Sun.COM 	char *dsname;
40810588SEric.Taylor@Sun.COM 	char *x;
40910588SEric.Taylor@Sun.COM 	char str[MAXNAMELEN];
41010588SEric.Taylor@Sun.COM 	sdcmn_err13(("create_link (%s) (%s) '%s'", ddv->sdev_name,
41110588SEric.Taylor@Sun.COM 	    ddv->sdev_path, nm));
41210588SEric.Taylor@Sun.COM 	dsname = devzvol_make_dsname(ddv->sdev_path, nm);
41310588SEric.Taylor@Sun.COM 	rc = sdev_zvol_create_minor(dsname);
41410588SEric.Taylor@Sun.COM 	if ((rc != 0 && rc != EEXIST && rc != EBUSY) ||
41510588SEric.Taylor@Sun.COM 	    sdev_zvol_name2minor(dsname, &minor)) {
41610588SEric.Taylor@Sun.COM 		sdcmn_err13(("devzvol_create_link %d", rc));
41710588SEric.Taylor@Sun.COM 		kmem_free(dsname, strlen(dsname) + 1);
41810588SEric.Taylor@Sun.COM 		return (-1);
41910588SEric.Taylor@Sun.COM 	}
42010588SEric.Taylor@Sun.COM 	kmem_free(dsname, strlen(dsname) + 1);
42110588SEric.Taylor@Sun.COM 
42210588SEric.Taylor@Sun.COM 	/*
42310588SEric.Taylor@Sun.COM 	 * This is a valid zvol; create a symlink that points to the
42410588SEric.Taylor@Sun.COM 	 * minor which was created under /devices/pseudo/zfs@0
42510588SEric.Taylor@Sun.COM 	 */
42610588SEric.Taylor@Sun.COM 	*pathname = '\0';
42710588SEric.Taylor@Sun.COM 	for (x = ddv->sdev_path; x = strchr(x, '/'); x++)
42810588SEric.Taylor@Sun.COM 		(void) strcat(pathname, "../");
42910588SEric.Taylor@Sun.COM 	(void) snprintf(str, sizeof (str), ZVOL_PSEUDO_DEV "%u", minor);
43010588SEric.Taylor@Sun.COM 	(void) strncat(pathname, str, MAXPATHLEN);
43110588SEric.Taylor@Sun.COM 	if (strncmp(ddv->sdev_path, ZVOL_FULL_RDEV_DIR,
43210588SEric.Taylor@Sun.COM 	    strlen(ZVOL_FULL_RDEV_DIR)) == 0)
43310588SEric.Taylor@Sun.COM 		(void) strcat(pathname, ",raw");
43410588SEric.Taylor@Sun.COM 	return (0);
43510588SEric.Taylor@Sun.COM }
43610588SEric.Taylor@Sun.COM 
43710588SEric.Taylor@Sun.COM /* Clean zvol sdev_nodes that are no longer valid.  */
43810588SEric.Taylor@Sun.COM static void
devzvol_prunedir(struct sdev_node * ddv)43910588SEric.Taylor@Sun.COM devzvol_prunedir(struct sdev_node *ddv)
44010588SEric.Taylor@Sun.COM {
44110588SEric.Taylor@Sun.COM 	struct sdev_node *dv;
44210588SEric.Taylor@Sun.COM 
44310588SEric.Taylor@Sun.COM 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
44410588SEric.Taylor@Sun.COM 
44510588SEric.Taylor@Sun.COM 	sdcmn_err13(("prunedir '%s'", ddv->sdev_name));
44610588SEric.Taylor@Sun.COM 	ASSERT(strncmp(ddv->sdev_path, ZVOL_DIR, strlen(ZVOL_DIR)) == 0);
44710588SEric.Taylor@Sun.COM 	if (rw_tryupgrade(&ddv->sdev_contents) == 0) {
44810588SEric.Taylor@Sun.COM 		rw_exit(&ddv->sdev_contents);
44910588SEric.Taylor@Sun.COM 		rw_enter(&ddv->sdev_contents, RW_WRITER);
45010588SEric.Taylor@Sun.COM 	}
45110588SEric.Taylor@Sun.COM 
45210588SEric.Taylor@Sun.COM 	dv = SDEV_FIRST_ENTRY(ddv);
45310588SEric.Taylor@Sun.COM 	while (dv) {
45410588SEric.Taylor@Sun.COM 		sdcmn_err13(("sdev_name '%s'", dv->sdev_name));
45510588SEric.Taylor@Sun.COM 		/* skip stale nodes */
45610588SEric.Taylor@Sun.COM 		if (dv->sdev_flags & SDEV_STALE) {
45710588SEric.Taylor@Sun.COM 			sdcmn_err13(("  stale"));
45810588SEric.Taylor@Sun.COM 			dv = SDEV_NEXT_ENTRY(ddv, dv);
45910588SEric.Taylor@Sun.COM 			continue;
46010588SEric.Taylor@Sun.COM 		}
46110588SEric.Taylor@Sun.COM 
46210588SEric.Taylor@Sun.COM 		switch (devzvol_validate(dv)) {
46310588SEric.Taylor@Sun.COM 		case SDEV_VTOR_VALID:
46410588SEric.Taylor@Sun.COM 		case SDEV_VTOR_SKIP:
46510588SEric.Taylor@Sun.COM 			dv = SDEV_NEXT_ENTRY(ddv, dv);
46610588SEric.Taylor@Sun.COM 			continue;
46710588SEric.Taylor@Sun.COM 		case SDEV_VTOR_INVALID:
46810588SEric.Taylor@Sun.COM 			sdcmn_err7(("prunedir: destroy invalid "
46910588SEric.Taylor@Sun.COM 			    "node: %s\n", dv->sdev_name));
47010588SEric.Taylor@Sun.COM 			break;
47110588SEric.Taylor@Sun.COM 		}
47210588SEric.Taylor@Sun.COM 
47310588SEric.Taylor@Sun.COM 		if ((SDEVTOV(dv)->v_type == VDIR) &&
47410588SEric.Taylor@Sun.COM 		    (sdev_cleandir(dv, NULL, 0) != 0)) {
47510588SEric.Taylor@Sun.COM 			dv = SDEV_NEXT_ENTRY(ddv, dv);
47610588SEric.Taylor@Sun.COM 			continue;
47710588SEric.Taylor@Sun.COM 		}
47810588SEric.Taylor@Sun.COM 		SDEV_HOLD(dv);
47910588SEric.Taylor@Sun.COM 		/* remove the cache node */
48010588SEric.Taylor@Sun.COM 		if (sdev_cache_update(ddv, &dv, dv->sdev_name,
48110588SEric.Taylor@Sun.COM 		    SDEV_CACHE_DELETE) == 0)
48210588SEric.Taylor@Sun.COM 			dv = SDEV_FIRST_ENTRY(ddv);
48310588SEric.Taylor@Sun.COM 		else
48410588SEric.Taylor@Sun.COM 			dv = SDEV_NEXT_ENTRY(ddv, dv);
48510588SEric.Taylor@Sun.COM 	}
48610588SEric.Taylor@Sun.COM 	rw_downgrade(&ddv->sdev_contents);
48710588SEric.Taylor@Sun.COM }
48810588SEric.Taylor@Sun.COM 
48910588SEric.Taylor@Sun.COM /*ARGSUSED*/
49010588SEric.Taylor@Sun.COM static int
devzvol_lookup(struct vnode * dvp,char * nm,struct vnode ** vpp,struct pathname * pnp,int flags,struct vnode * rdir,struct cred * cred,caller_context_t * ct,int * direntflags,pathname_t * realpnp)49110588SEric.Taylor@Sun.COM devzvol_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
49210588SEric.Taylor@Sun.COM     struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
49310588SEric.Taylor@Sun.COM     caller_context_t *ct, int *direntflags, pathname_t *realpnp)
49410588SEric.Taylor@Sun.COM {
49510588SEric.Taylor@Sun.COM 	enum vtype expected_type = VDIR;
49610588SEric.Taylor@Sun.COM 	struct sdev_node *parent = VTOSDEV(dvp);
49710588SEric.Taylor@Sun.COM 	char *dsname;
49810588SEric.Taylor@Sun.COM 	dmu_objset_type_t do_type;
49910588SEric.Taylor@Sun.COM 	int error;
50010588SEric.Taylor@Sun.COM 
50110588SEric.Taylor@Sun.COM 	sdcmn_err13(("devzvol_lookup '%s' '%s'", parent->sdev_path, nm));
50210588SEric.Taylor@Sun.COM 	*vpp = NULL;
50310588SEric.Taylor@Sun.COM 	/* execute access is required to search the directory */
50410588SEric.Taylor@Sun.COM 	if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0)
50510588SEric.Taylor@Sun.COM 		return (error);
50610588SEric.Taylor@Sun.COM 
50710588SEric.Taylor@Sun.COM 	rw_enter(&parent->sdev_contents, RW_READER);
50810588SEric.Taylor@Sun.COM 	if (!SDEV_IS_GLOBAL(parent)) {
50910588SEric.Taylor@Sun.COM 		rw_exit(&parent->sdev_contents);
51010588SEric.Taylor@Sun.COM 		return (prof_lookup(dvp, nm, vpp, cred));
51110588SEric.Taylor@Sun.COM 	}
51210588SEric.Taylor@Sun.COM 
51310588SEric.Taylor@Sun.COM 	dsname = devzvol_make_dsname(parent->sdev_path, nm);
51410588SEric.Taylor@Sun.COM 	rw_exit(&parent->sdev_contents);
51510588SEric.Taylor@Sun.COM 	sdcmn_err13(("rvp dsname %s", dsname ? dsname : "(null)"));
51610588SEric.Taylor@Sun.COM 	if (dsname) {
51710588SEric.Taylor@Sun.COM 		error = devzvol_objset_check(dsname, &do_type);
51810588SEric.Taylor@Sun.COM 		if (error != 0) {
51910588SEric.Taylor@Sun.COM 			error = ENOENT;
52010588SEric.Taylor@Sun.COM 			goto out;
52110588SEric.Taylor@Sun.COM 		}
52210588SEric.Taylor@Sun.COM 		if (do_type == DMU_OST_ZVOL)
52310588SEric.Taylor@Sun.COM 			expected_type = VLNK;
52410588SEric.Taylor@Sun.COM 	}
52510588SEric.Taylor@Sun.COM 	/*
52610588SEric.Taylor@Sun.COM 	 * the callbacks expect:
52710588SEric.Taylor@Sun.COM 	 *
52810588SEric.Taylor@Sun.COM 	 * parent->sdev_path		   nm
52910588SEric.Taylor@Sun.COM 	 * /dev/zvol			   {r}dsk
53010588SEric.Taylor@Sun.COM 	 * /dev/zvol/{r}dsk		   <pool name>
53110588SEric.Taylor@Sun.COM 	 * /dev/zvol/{r}dsk/<dataset name> <last ds component>
53210588SEric.Taylor@Sun.COM 	 *
53310588SEric.Taylor@Sun.COM 	 * sdev_name is always last path component of sdev_path
53410588SEric.Taylor@Sun.COM 	 */
53510588SEric.Taylor@Sun.COM 	if (expected_type == VDIR) {
53610588SEric.Taylor@Sun.COM 		error = devname_lookup_func(parent, nm, vpp, cred,
53710588SEric.Taylor@Sun.COM 		    devzvol_create_dir, SDEV_VATTR);
53810588SEric.Taylor@Sun.COM 	} else {
53910588SEric.Taylor@Sun.COM 		error = devname_lookup_func(parent, nm, vpp, cred,
54010588SEric.Taylor@Sun.COM 		    devzvol_create_link, SDEV_VLINK);
54110588SEric.Taylor@Sun.COM 	}
54210588SEric.Taylor@Sun.COM 	sdcmn_err13(("devzvol_lookup %d %d", expected_type, error));
54310588SEric.Taylor@Sun.COM 	ASSERT(error || ((*vpp)->v_type == expected_type));
54410588SEric.Taylor@Sun.COM out:
54510588SEric.Taylor@Sun.COM 	if (dsname)
54610588SEric.Taylor@Sun.COM 		kmem_free(dsname, strlen(dsname) + 1);
54710588SEric.Taylor@Sun.COM 	sdcmn_err13(("devzvol_lookup %d", error));
54810588SEric.Taylor@Sun.COM 	return (error);
54910588SEric.Taylor@Sun.COM }
55010588SEric.Taylor@Sun.COM 
55110588SEric.Taylor@Sun.COM /*
55210588SEric.Taylor@Sun.COM  * We allow create to find existing nodes
55310588SEric.Taylor@Sun.COM  *	- if the node doesn't exist - EROFS
55410588SEric.Taylor@Sun.COM  *	- creating an existing dir read-only succeeds, otherwise EISDIR
55510588SEric.Taylor@Sun.COM  *	- exclusive creates fail - EEXIST
55610588SEric.Taylor@Sun.COM  */
55710588SEric.Taylor@Sun.COM /*ARGSUSED2*/
55810588SEric.Taylor@Sun.COM static int
devzvol_create(struct vnode * dvp,char * nm,struct vattr * vap,vcexcl_t excl,int mode,struct vnode ** vpp,struct cred * cred,int flag,caller_context_t * ct,vsecattr_t * vsecp)55910588SEric.Taylor@Sun.COM devzvol_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
56010588SEric.Taylor@Sun.COM     int mode, struct vnode **vpp, struct cred *cred, int flag,
56110588SEric.Taylor@Sun.COM     caller_context_t *ct, vsecattr_t *vsecp)
56210588SEric.Taylor@Sun.COM {
56310588SEric.Taylor@Sun.COM 	int error;
56410588SEric.Taylor@Sun.COM 	struct vnode *vp;
56510588SEric.Taylor@Sun.COM 
56610588SEric.Taylor@Sun.COM 	*vpp = NULL;
56710588SEric.Taylor@Sun.COM 
56810588SEric.Taylor@Sun.COM 	error = devzvol_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL,
56910588SEric.Taylor@Sun.COM 	    NULL);
57010588SEric.Taylor@Sun.COM 	if (error == 0) {
57110588SEric.Taylor@Sun.COM 		if (excl == EXCL)
57210588SEric.Taylor@Sun.COM 			error = EEXIST;
57310588SEric.Taylor@Sun.COM 		else if (vp->v_type == VDIR && (mode & VWRITE))
57410588SEric.Taylor@Sun.COM 			error = EISDIR;
57510588SEric.Taylor@Sun.COM 		else
57610588SEric.Taylor@Sun.COM 			error = VOP_ACCESS(vp, mode, 0, cred, ct);
57710588SEric.Taylor@Sun.COM 
57810588SEric.Taylor@Sun.COM 		if (error) {
57910588SEric.Taylor@Sun.COM 			VN_RELE(vp);
58010588SEric.Taylor@Sun.COM 		} else
58110588SEric.Taylor@Sun.COM 			*vpp = vp;
58210588SEric.Taylor@Sun.COM 	} else if (error == ENOENT) {
58310588SEric.Taylor@Sun.COM 		error = EROFS;
58410588SEric.Taylor@Sun.COM 	}
58510588SEric.Taylor@Sun.COM 
58610588SEric.Taylor@Sun.COM 	return (error);
58710588SEric.Taylor@Sun.COM }
58810588SEric.Taylor@Sun.COM 
58910588SEric.Taylor@Sun.COM void sdev_iter_snapshots(struct vnode *dvp, char *name);
59010588SEric.Taylor@Sun.COM 
59110588SEric.Taylor@Sun.COM void
sdev_iter_datasets(struct vnode * dvp,int arg,char * name)59210588SEric.Taylor@Sun.COM sdev_iter_datasets(struct vnode *dvp, int arg, char *name)
59310588SEric.Taylor@Sun.COM {
59410588SEric.Taylor@Sun.COM 	zfs_cmd_t	*zc;
59510588SEric.Taylor@Sun.COM 	int rc;
59610588SEric.Taylor@Sun.COM 
59710588SEric.Taylor@Sun.COM 	sdcmn_err13(("iter name is '%s' (arg %x)", name, arg));
59810588SEric.Taylor@Sun.COM 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
59910588SEric.Taylor@Sun.COM 	(void) strcpy(zc->zc_name, name);
60010588SEric.Taylor@Sun.COM 
60110588SEric.Taylor@Sun.COM 	while ((rc = devzvol_handle_ioctl(arg, zc, B_FALSE)) == 0) {
60210588SEric.Taylor@Sun.COM 		struct vnode *vpp;
60310588SEric.Taylor@Sun.COM 		char *ptr;
60410588SEric.Taylor@Sun.COM 
60510588SEric.Taylor@Sun.COM 		sdcmn_err13(("  name %s", zc->zc_name));
60610588SEric.Taylor@Sun.COM 		if (strchr(zc->zc_name, '$') || strchr(zc->zc_name, '%'))
60710588SEric.Taylor@Sun.COM 			goto skip;
60810588SEric.Taylor@Sun.COM 		ptr = strrchr(zc->zc_name, '/') + 1;
60910588SEric.Taylor@Sun.COM 		rc = devzvol_lookup(dvp, ptr, &vpp, NULL, 0, NULL,
61010588SEric.Taylor@Sun.COM 		    kcred, NULL, NULL, NULL);
61110588SEric.Taylor@Sun.COM 		if (rc == 0) {
61210588SEric.Taylor@Sun.COM 			VN_RELE(vpp);
61310588SEric.Taylor@Sun.COM 		} else if (rc == ENOENT) {
61410588SEric.Taylor@Sun.COM 			goto skip;
61510588SEric.Taylor@Sun.COM 		} else {
61610588SEric.Taylor@Sun.COM 			/* EBUSY == problem with zvols's dmu holds? */
61710588SEric.Taylor@Sun.COM 			ASSERT(0);
61810588SEric.Taylor@Sun.COM 			goto skip;
61910588SEric.Taylor@Sun.COM 		}
62010588SEric.Taylor@Sun.COM 		if (arg == ZFS_IOC_DATASET_LIST_NEXT &&
62110588SEric.Taylor@Sun.COM 		    zc->zc_objset_stats.dds_type != DMU_OST_ZFS)
62210588SEric.Taylor@Sun.COM 			sdev_iter_snapshots(dvp, zc->zc_name);
62310588SEric.Taylor@Sun.COM skip:
62410588SEric.Taylor@Sun.COM 		(void) strcpy(zc->zc_name, name);
62510588SEric.Taylor@Sun.COM 	}
62610588SEric.Taylor@Sun.COM 	kmem_free(zc, sizeof (zfs_cmd_t));
62710588SEric.Taylor@Sun.COM }
62810588SEric.Taylor@Sun.COM 
62910588SEric.Taylor@Sun.COM void
sdev_iter_snapshots(struct vnode * dvp,char * name)63010588SEric.Taylor@Sun.COM sdev_iter_snapshots(struct vnode *dvp, char *name)
63110588SEric.Taylor@Sun.COM {
63210588SEric.Taylor@Sun.COM 	sdev_iter_datasets(dvp, ZFS_IOC_SNAPSHOT_LIST_NEXT, name);
63310588SEric.Taylor@Sun.COM }
63410588SEric.Taylor@Sun.COM 
63510588SEric.Taylor@Sun.COM /*ARGSUSED4*/
63610588SEric.Taylor@Sun.COM static int
devzvol_readdir(struct vnode * dvp,struct uio * uiop,struct cred * cred,int * eofp,caller_context_t * ct_unused,int flags_unused)63710588SEric.Taylor@Sun.COM devzvol_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
63810588SEric.Taylor@Sun.COM     int *eofp, caller_context_t *ct_unused, int flags_unused)
63910588SEric.Taylor@Sun.COM {
64010588SEric.Taylor@Sun.COM 	struct sdev_node *sdvp = VTOSDEV(dvp);
64110588SEric.Taylor@Sun.COM 	char *ptr;
64210588SEric.Taylor@Sun.COM 
64310588SEric.Taylor@Sun.COM 	sdcmn_err13(("zv readdir of '%s' %s'", sdvp->sdev_path,
64410588SEric.Taylor@Sun.COM 	    sdvp->sdev_name));
64510588SEric.Taylor@Sun.COM 
64610588SEric.Taylor@Sun.COM 	if (strcmp(sdvp->sdev_path, ZVOL_DIR) == 0) {
64710588SEric.Taylor@Sun.COM 		struct vnode *vp;
64810588SEric.Taylor@Sun.COM 
64910588SEric.Taylor@Sun.COM 		rw_exit(&sdvp->sdev_contents);
65010588SEric.Taylor@Sun.COM 		(void) devname_lookup_func(sdvp, "dsk", &vp, cred,
65110588SEric.Taylor@Sun.COM 		    devzvol_create_dir, SDEV_VATTR);
65210588SEric.Taylor@Sun.COM 		VN_RELE(vp);
65310588SEric.Taylor@Sun.COM 		(void) devname_lookup_func(sdvp, "rdsk", &vp, cred,
65410588SEric.Taylor@Sun.COM 		    devzvol_create_dir, SDEV_VATTR);
65510588SEric.Taylor@Sun.COM 		VN_RELE(vp);
65610588SEric.Taylor@Sun.COM 		rw_enter(&sdvp->sdev_contents, RW_READER);
65710588SEric.Taylor@Sun.COM 		return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
65810588SEric.Taylor@Sun.COM 	}
65910588SEric.Taylor@Sun.COM 	if (uiop->uio_offset == 0)
66010588SEric.Taylor@Sun.COM 		devzvol_prunedir(sdvp);
66110588SEric.Taylor@Sun.COM 	ptr = sdvp->sdev_path + strlen(ZVOL_DIR);
66210588SEric.Taylor@Sun.COM 	if ((strcmp(ptr, "/dsk") == 0) || (strcmp(ptr, "/rdsk") == 0)) {
66310588SEric.Taylor@Sun.COM 		rw_exit(&sdvp->sdev_contents);
66410588SEric.Taylor@Sun.COM 		devzvol_create_pool_dirs(dvp);
66510588SEric.Taylor@Sun.COM 		rw_enter(&sdvp->sdev_contents, RW_READER);
66610588SEric.Taylor@Sun.COM 		return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
66710588SEric.Taylor@Sun.COM 	}
66810588SEric.Taylor@Sun.COM 
66910588SEric.Taylor@Sun.COM 	ptr = strchr(ptr + 1, '/') + 1;
67010588SEric.Taylor@Sun.COM 	rw_exit(&sdvp->sdev_contents);
67110588SEric.Taylor@Sun.COM 	sdev_iter_datasets(dvp, ZFS_IOC_DATASET_LIST_NEXT, ptr);
67210588SEric.Taylor@Sun.COM 	rw_enter(&sdvp->sdev_contents, RW_READER);
67310588SEric.Taylor@Sun.COM 	return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
67410588SEric.Taylor@Sun.COM }
67510588SEric.Taylor@Sun.COM 
67610588SEric.Taylor@Sun.COM const fs_operation_def_t devzvol_vnodeops_tbl[] = {
67710588SEric.Taylor@Sun.COM 	VOPNAME_READDIR,	{ .vop_readdir = devzvol_readdir },
67810588SEric.Taylor@Sun.COM 	VOPNAME_LOOKUP,		{ .vop_lookup = devzvol_lookup },
67910588SEric.Taylor@Sun.COM 	VOPNAME_CREATE,		{ .vop_create = devzvol_create },
68010588SEric.Taylor@Sun.COM 	VOPNAME_RENAME,		{ .error = fs_nosys },
68110588SEric.Taylor@Sun.COM 	VOPNAME_MKDIR,		{ .error = fs_nosys },
68210588SEric.Taylor@Sun.COM 	VOPNAME_RMDIR,		{ .error = fs_nosys },
68310588SEric.Taylor@Sun.COM 	VOPNAME_REMOVE,		{ .error = fs_nosys },
68410588SEric.Taylor@Sun.COM 	VOPNAME_SYMLINK,	{ .error = fs_nosys },
68510588SEric.Taylor@Sun.COM 	NULL,			NULL
68610588SEric.Taylor@Sun.COM };
687