xref: /titanic_51/usr/src/uts/common/fs/dev/sdev_zvolops.c (revision e3c6427a8c15992042ea477cf18805764b7e41e0)
1681d9761SEric Taylor /*
2681d9761SEric Taylor  * CDDL HEADER START
3681d9761SEric Taylor  *
4681d9761SEric Taylor  * The contents of this file are subject to the terms of the
5681d9761SEric Taylor  * Common Development and Distribution License (the "License").
6681d9761SEric Taylor  * You may not use this file except in compliance with the License.
7681d9761SEric Taylor  *
8681d9761SEric Taylor  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9681d9761SEric Taylor  * or http://www.opensolaris.org/os/licensing.
10681d9761SEric Taylor  * See the License for the specific language governing permissions
11681d9761SEric Taylor  * and limitations under the License.
12681d9761SEric Taylor  *
13681d9761SEric Taylor  * When distributing Covered Code, include this CDDL HEADER in each
14681d9761SEric Taylor  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15681d9761SEric Taylor  * If applicable, add the following below this CDDL HEADER, with the
16681d9761SEric Taylor  * fields enclosed by brackets "[]" replaced with your own identifying
17681d9761SEric Taylor  * information: Portions Copyright [yyyy] [name of copyright owner]
18681d9761SEric Taylor  *
19681d9761SEric Taylor  * CDDL HEADER END
20681d9761SEric Taylor  */
21681d9761SEric Taylor /*
22681d9761SEric Taylor  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23681d9761SEric Taylor  * Use is subject to license terms.
24dd9c3b29SJerry Jelinek  * Copyright 2013 Joyent, Inc.  All rights reserved.
25681d9761SEric Taylor  */
26681d9761SEric Taylor 
27681d9761SEric Taylor /* vnode ops for the /dev/zvol directory */
28681d9761SEric Taylor 
29681d9761SEric Taylor #include <sys/types.h>
30681d9761SEric Taylor #include <sys/param.h>
31681d9761SEric Taylor #include <sys/sysmacros.h>
32681d9761SEric Taylor #include <sys/ddi.h>
33681d9761SEric Taylor #include <sys/sunndi.h>
34681d9761SEric Taylor #include <sys/sunldi.h>
35681d9761SEric Taylor #include <fs/fs_subr.h>
36681d9761SEric Taylor #include <sys/fs/dv_node.h>
37681d9761SEric Taylor #include <sys/fs/sdev_impl.h>
38681d9761SEric Taylor #include <sys/zfs_ioctl.h>
39681d9761SEric Taylor #include <sys/policy.h>
40681d9761SEric Taylor #include <sys/stat.h>
41681d9761SEric Taylor #include <sys/vfs_opreg.h>
42681d9761SEric Taylor 
43681d9761SEric Taylor struct vnodeops	*devzvol_vnodeops;
440ad555adSAlex Wilson static major_t devzvol_major;
450ad555adSAlex Wilson static taskq_ent_t devzvol_zclist_task;
460ad555adSAlex Wilson 
470ad555adSAlex Wilson static kmutex_t devzvol_mtx;
480ad555adSAlex Wilson /* Below are protected by devzvol_mtx */
490ad555adSAlex Wilson static boolean_t devzvol_isopen;
500ad555adSAlex Wilson static boolean_t devzvol_zclist_task_running = B_FALSE;
51681d9761SEric Taylor static uint64_t devzvol_gen = 0;
52681d9761SEric Taylor static uint64_t devzvol_zclist;
53681d9761SEric Taylor static size_t devzvol_zclist_size;
54681d9761SEric Taylor static ldi_ident_t devzvol_li;
55681d9761SEric Taylor static ldi_handle_t devzvol_lh;
56681d9761SEric Taylor 
57681d9761SEric Taylor /*
58681d9761SEric Taylor  * we need to use ddi_mod* since fs/dev gets loaded early on in
59681d9761SEric Taylor  * startup(), and linking fs/dev to fs/zfs would drag in a lot of
60681d9761SEric Taylor  * other stuff (like drv/random) before the rest of the system is
61681d9761SEric Taylor  * ready to go
62681d9761SEric Taylor  */
63681d9761SEric Taylor ddi_modhandle_t zfs_mod;
64681d9761SEric Taylor int (*szcm)(char *);
65681d9761SEric Taylor int (*szn2m)(char *, minor_t *);
66681d9761SEric Taylor 
67681d9761SEric Taylor int
68681d9761SEric Taylor sdev_zvol_create_minor(char *dsname)
69681d9761SEric Taylor {
70dd9c3b29SJerry Jelinek 	if (szcm == NULL)
71dd9c3b29SJerry Jelinek 		return (-1);
72681d9761SEric Taylor 	return ((*szcm)(dsname));
73681d9761SEric Taylor }
74681d9761SEric Taylor 
75681d9761SEric Taylor int
76681d9761SEric Taylor sdev_zvol_name2minor(char *dsname, minor_t *minor)
77681d9761SEric Taylor {
78dd9c3b29SJerry Jelinek 	if (szn2m == NULL)
79dd9c3b29SJerry Jelinek 		return (-1);
80681d9761SEric Taylor 	return ((*szn2m)(dsname, minor));
81681d9761SEric Taylor }
82681d9761SEric Taylor 
83681d9761SEric Taylor int
84681d9761SEric Taylor devzvol_open_zfs()
85681d9761SEric Taylor {
86681d9761SEric Taylor 	int rc;
87dd9c3b29SJerry Jelinek 	dev_t dv;
88681d9761SEric Taylor 
89681d9761SEric Taylor 	devzvol_li = ldi_ident_from_anon();
90681d9761SEric Taylor 	if (ldi_open_by_name("/dev/zfs", FREAD | FWRITE, kcred,
91681d9761SEric Taylor 	    &devzvol_lh, devzvol_li))
92681d9761SEric Taylor 		return (-1);
93681d9761SEric Taylor 	if (zfs_mod == NULL && ((zfs_mod = ddi_modopen("fs/zfs",
94681d9761SEric Taylor 	    KRTLD_MODE_FIRST, &rc)) == NULL)) {
95681d9761SEric Taylor 		return (rc);
96681d9761SEric Taylor 	}
97681d9761SEric Taylor 	ASSERT(szcm == NULL && szn2m == NULL);
98681d9761SEric Taylor 	if ((szcm = (int (*)(char *))
99681d9761SEric Taylor 	    ddi_modsym(zfs_mod, "zvol_create_minor", &rc)) == NULL) {
100681d9761SEric Taylor 		cmn_err(CE_WARN, "couldn't resolve zvol_create_minor");
101681d9761SEric Taylor 		return (rc);
102681d9761SEric Taylor 	}
103681d9761SEric Taylor 	if ((szn2m = (int(*)(char *, minor_t *))
104681d9761SEric Taylor 	    ddi_modsym(zfs_mod, "zvol_name2minor", &rc)) == NULL) {
105681d9761SEric Taylor 		cmn_err(CE_WARN, "couldn't resolve zvol_name2minor");
106681d9761SEric Taylor 		return (rc);
107681d9761SEric Taylor 	}
108dd9c3b29SJerry Jelinek 	if (ldi_get_dev(devzvol_lh, &dv))
109dd9c3b29SJerry Jelinek 		return (-1);
110dd9c3b29SJerry Jelinek 	devzvol_major = getmajor(dv);
111681d9761SEric Taylor 	return (0);
112681d9761SEric Taylor }
113681d9761SEric Taylor 
114681d9761SEric Taylor void
115681d9761SEric Taylor devzvol_close_zfs()
116681d9761SEric Taylor {
117681d9761SEric Taylor 	szcm = NULL;
118681d9761SEric Taylor 	szn2m = NULL;
119681d9761SEric Taylor 	(void) ldi_close(devzvol_lh, FREAD|FWRITE, kcred);
120681d9761SEric Taylor 	ldi_ident_release(devzvol_li);
121681d9761SEric Taylor 	if (zfs_mod != NULL) {
122681d9761SEric Taylor 		(void) ddi_modclose(zfs_mod);
123681d9761SEric Taylor 		zfs_mod = NULL;
124681d9761SEric Taylor 	}
125681d9761SEric Taylor }
126681d9761SEric Taylor 
127681d9761SEric Taylor int
128681d9761SEric Taylor devzvol_handle_ioctl(int cmd, zfs_cmd_t *zc, size_t *alloc_size)
129681d9761SEric Taylor {
130681d9761SEric Taylor 	uint64_t cookie;
131681d9761SEric Taylor 	int size = 8000;
132681d9761SEric Taylor 	int unused;
133681d9761SEric Taylor 	int rc;
134681d9761SEric Taylor 
135681d9761SEric Taylor 	if (cmd != ZFS_IOC_POOL_CONFIGS)
136681d9761SEric Taylor 		mutex_enter(&devzvol_mtx);
137ff060bd8SEric Taylor 	if (!devzvol_isopen) {
138681d9761SEric Taylor 		if ((rc = devzvol_open_zfs()) == 0) {
139ff060bd8SEric Taylor 			devzvol_isopen = B_TRUE;
140681d9761SEric Taylor 		} else {
141681d9761SEric Taylor 			if (cmd != ZFS_IOC_POOL_CONFIGS)
142681d9761SEric Taylor 				mutex_exit(&devzvol_mtx);
143681d9761SEric Taylor 			return (ENXIO);
144681d9761SEric Taylor 		}
145681d9761SEric Taylor 	}
146681d9761SEric Taylor 	cookie = zc->zc_cookie;
147681d9761SEric Taylor again:
148681d9761SEric Taylor 	zc->zc_nvlist_dst = (uint64_t)(intptr_t)kmem_alloc(size,
149681d9761SEric Taylor 	    KM_SLEEP);
150681d9761SEric Taylor 	zc->zc_nvlist_dst_size = size;
151681d9761SEric Taylor 	rc = ldi_ioctl(devzvol_lh, cmd, (intptr_t)zc, FKIOCTL, kcred,
152681d9761SEric Taylor 	    &unused);
153681d9761SEric Taylor 	if (rc == ENOMEM) {
154681d9761SEric Taylor 		int newsize;
155681d9761SEric Taylor 		newsize = zc->zc_nvlist_dst_size;
156681d9761SEric Taylor 		ASSERT(newsize > size);
157681d9761SEric Taylor 		kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
158681d9761SEric Taylor 		size = newsize;
159681d9761SEric Taylor 		zc->zc_cookie = cookie;
160681d9761SEric Taylor 		goto again;
161681d9761SEric Taylor 	}
162681d9761SEric Taylor 	if (alloc_size == NULL)
163681d9761SEric Taylor 		kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
164681d9761SEric Taylor 	else
165681d9761SEric Taylor 		*alloc_size = size;
166681d9761SEric Taylor 	if (cmd != ZFS_IOC_POOL_CONFIGS)
167681d9761SEric Taylor 		mutex_exit(&devzvol_mtx);
168681d9761SEric Taylor 	return (rc);
169681d9761SEric Taylor }
170681d9761SEric Taylor 
171681d9761SEric Taylor /* figures out if the objset exists and returns its type */
172681d9761SEric Taylor int
173681d9761SEric Taylor devzvol_objset_check(char *dsname, dmu_objset_type_t *type)
174681d9761SEric Taylor {
175681d9761SEric Taylor 	boolean_t	ispool;
176681d9761SEric Taylor 	zfs_cmd_t	*zc;
177681d9761SEric Taylor 	int rc;
178681d9761SEric Taylor 
179681d9761SEric Taylor 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
180681d9761SEric Taylor 	(void) strlcpy(zc->zc_name, dsname, MAXPATHLEN);
181681d9761SEric Taylor 
182681d9761SEric Taylor 	ispool = (strchr(dsname, '/') == NULL) ? B_TRUE : B_FALSE;
183681d9761SEric Taylor 	if (!ispool && sdev_zvol_name2minor(dsname, NULL) == 0) {
184681d9761SEric Taylor 		sdcmn_err13(("found cached minor node"));
185681d9761SEric Taylor 		if (type)
186681d9761SEric Taylor 			*type = DMU_OST_ZVOL;
187681d9761SEric Taylor 		kmem_free(zc, sizeof (zfs_cmd_t));
188681d9761SEric Taylor 		return (0);
189681d9761SEric Taylor 	}
190681d9761SEric Taylor 	rc = devzvol_handle_ioctl(ispool ? ZFS_IOC_POOL_STATS :
191681d9761SEric Taylor 	    ZFS_IOC_OBJSET_STATS, zc, NULL);
192681d9761SEric Taylor 	if (type && rc == 0)
193681d9761SEric Taylor 		*type = (ispool) ? DMU_OST_ZFS :
194681d9761SEric Taylor 		    zc->zc_objset_stats.dds_type;
195681d9761SEric Taylor 	kmem_free(zc, sizeof (zfs_cmd_t));
196681d9761SEric Taylor 	return (rc);
197681d9761SEric Taylor }
198681d9761SEric Taylor 
199681d9761SEric Taylor /*
200*e3c6427aSAlex Wilson  * Returns what the zfs dataset name should be, given the /dev/zvol
201*e3c6427aSAlex Wilson  * path and an optional name (can be NULL).
202*e3c6427aSAlex Wilson  *
203*e3c6427aSAlex Wilson  * Note that if the name param is NULL, then path must be an
204*e3c6427aSAlex Wilson  * actual dataset's directory and not one of the top-level
205*e3c6427aSAlex Wilson  * /dev/zvol/{dsk,rdsk} dirs, as these do not correspond to a
206*e3c6427aSAlex Wilson  * specific dataset.
207681d9761SEric Taylor  */
208681d9761SEric Taylor char *
209681d9761SEric Taylor devzvol_make_dsname(const char *path, const char *name)
210681d9761SEric Taylor {
211681d9761SEric Taylor 	char *dsname;
212681d9761SEric Taylor 	const char *ptr;
213681d9761SEric Taylor 	int dslen;
214681d9761SEric Taylor 
215681d9761SEric Taylor 	if (strcmp(path, ZVOL_DIR) == 0)
216681d9761SEric Taylor 		return (NULL);
217681d9761SEric Taylor 	if (name && (strcmp(name, ".") == 0 || strcmp(name, "..") == 0))
218681d9761SEric Taylor 		return (NULL);
219681d9761SEric Taylor 	ptr = path + strlen(ZVOL_DIR);
220681d9761SEric Taylor 	if (strncmp(ptr, "/dsk", 4) == 0)
221681d9761SEric Taylor 		ptr += strlen("/dsk");
222681d9761SEric Taylor 	else if (strncmp(ptr, "/rdsk", 5) == 0)
223681d9761SEric Taylor 		ptr += strlen("/rdsk");
224681d9761SEric Taylor 	else
225681d9761SEric Taylor 		return (NULL);
226*e3c6427aSAlex Wilson 
227681d9761SEric Taylor 	if (*ptr == '/')
228681d9761SEric Taylor 		ptr++;
229*e3c6427aSAlex Wilson 	else if (name == NULL)
230*e3c6427aSAlex Wilson 		return (NULL);
231681d9761SEric Taylor 
232681d9761SEric Taylor 	dslen = strlen(ptr);
233681d9761SEric Taylor 	if (dslen)
234681d9761SEric Taylor 		dslen++;			/* plus null */
235681d9761SEric Taylor 	if (name)
236681d9761SEric Taylor 		dslen += strlen(name) + 1;	/* plus slash */
237681d9761SEric Taylor 	dsname = kmem_zalloc(dslen, KM_SLEEP);
238681d9761SEric Taylor 	if (*ptr) {
239681d9761SEric Taylor 		(void) strlcpy(dsname, ptr, dslen);
240681d9761SEric Taylor 		if (name)
241681d9761SEric Taylor 			(void) strlcat(dsname, "/", dslen);
242681d9761SEric Taylor 	}
243681d9761SEric Taylor 	if (name)
244681d9761SEric Taylor 		(void) strlcat(dsname, name, dslen);
245681d9761SEric Taylor 	return (dsname);
246681d9761SEric Taylor }
247681d9761SEric Taylor 
248681d9761SEric Taylor /*
249681d9761SEric Taylor  * check if the zvol's sdev_node is still valid, which means make
250681d9761SEric Taylor  * sure the zvol is still valid.  zvol minors aren't proactively
251681d9761SEric Taylor  * destroyed when the zvol is destroyed, so we use a validator to clean
252681d9761SEric Taylor  * these up (in other words, when such nodes are encountered during
253681d9761SEric Taylor  * subsequent lookup() and readdir() operations) so that only valid
254681d9761SEric Taylor  * nodes are returned.  The ordering between devname_lookup_func and
255681d9761SEric Taylor  * devzvol_validate is a little inefficient in the case of invalid
256681d9761SEric Taylor  * or stale nodes because devname_lookup_func calls
257681d9761SEric Taylor  * devzvol_create_{dir, link}, then the validator says it's invalid,
258681d9761SEric Taylor  * and then the node gets cleaned up.
259681d9761SEric Taylor  */
260681d9761SEric Taylor int
261681d9761SEric Taylor devzvol_validate(struct sdev_node *dv)
262681d9761SEric Taylor {
263681d9761SEric Taylor 	dmu_objset_type_t do_type;
264681d9761SEric Taylor 	char *dsname;
265681d9761SEric Taylor 	char *nm = dv->sdev_name;
266681d9761SEric Taylor 	int rc;
267681d9761SEric Taylor 
268681d9761SEric Taylor 	sdcmn_err13(("validating ('%s' '%s')", dv->sdev_path, nm));
269681d9761SEric Taylor 	/*
270681d9761SEric Taylor 	 * validate only READY nodes; if someone is sitting on the
271681d9761SEric Taylor 	 * directory of a dataset that just got destroyed we could
272681d9761SEric Taylor 	 * get a zombie node which we just skip.
273681d9761SEric Taylor 	 */
274681d9761SEric Taylor 	if (dv->sdev_state != SDEV_READY) {
275681d9761SEric Taylor 		sdcmn_err13(("skipping '%s'", nm));
276681d9761SEric Taylor 		return (SDEV_VTOR_SKIP);
277681d9761SEric Taylor 	}
278681d9761SEric Taylor 
279681d9761SEric Taylor 	if ((strcmp(dv->sdev_path, ZVOL_DIR "/dsk") == 0) ||
280681d9761SEric Taylor 	    (strcmp(dv->sdev_path, ZVOL_DIR "/rdsk") == 0))
281681d9761SEric Taylor 		return (SDEV_VTOR_VALID);
282681d9761SEric Taylor 	dsname = devzvol_make_dsname(dv->sdev_path, NULL);
283681d9761SEric Taylor 	if (dsname == NULL)
284681d9761SEric Taylor 		return (SDEV_VTOR_INVALID);
285681d9761SEric Taylor 
286681d9761SEric Taylor 	rc = devzvol_objset_check(dsname, &do_type);
287681d9761SEric Taylor 	sdcmn_err13(("  '%s' rc %d", dsname, rc));
288681d9761SEric Taylor 	if (rc != 0) {
289681d9761SEric Taylor 		kmem_free(dsname, strlen(dsname) + 1);
290681d9761SEric Taylor 		return (SDEV_VTOR_INVALID);
291681d9761SEric Taylor 	}
292681d9761SEric Taylor 	sdcmn_err13(("  v_type %d do_type %d",
293681d9761SEric Taylor 	    SDEVTOV(dv)->v_type, do_type));
294681d9761SEric Taylor 	if ((SDEVTOV(dv)->v_type == VLNK && do_type != DMU_OST_ZVOL) ||
295dd9c3b29SJerry Jelinek 	    ((SDEVTOV(dv)->v_type == VBLK || SDEVTOV(dv)->v_type == VCHR) &&
296dd9c3b29SJerry Jelinek 	    do_type != DMU_OST_ZVOL) ||
297681d9761SEric Taylor 	    (SDEVTOV(dv)->v_type == VDIR && do_type == DMU_OST_ZVOL)) {
298681d9761SEric Taylor 		kmem_free(dsname, strlen(dsname) + 1);
299681d9761SEric Taylor 		return (SDEV_VTOR_STALE);
300681d9761SEric Taylor 	}
301681d9761SEric Taylor 	if (SDEVTOV(dv)->v_type == VLNK) {
302681d9761SEric Taylor 		char *ptr, *link;
303681d9761SEric Taylor 		long val = 0;
304681d9761SEric Taylor 		minor_t lminor, ominor;
305681d9761SEric Taylor 
306681d9761SEric Taylor 		rc = sdev_getlink(SDEVTOV(dv), &link);
307681d9761SEric Taylor 		ASSERT(rc == 0);
308681d9761SEric Taylor 
309681d9761SEric Taylor 		ptr = strrchr(link, ':') + 1;
310681d9761SEric Taylor 		rc = ddi_strtol(ptr, NULL, 10, &val);
311681d9761SEric Taylor 		kmem_free(link, strlen(link) + 1);
312681d9761SEric Taylor 		ASSERT(rc == 0 && val != 0);
313681d9761SEric Taylor 		lminor = (minor_t)val;
314681d9761SEric Taylor 		if (sdev_zvol_name2minor(dsname, &ominor) < 0 ||
315681d9761SEric Taylor 		    ominor != lminor) {
316681d9761SEric Taylor 			kmem_free(dsname, strlen(dsname) + 1);
317681d9761SEric Taylor 			return (SDEV_VTOR_STALE);
318681d9761SEric Taylor 		}
319681d9761SEric Taylor 	}
320681d9761SEric Taylor 	kmem_free(dsname, strlen(dsname) + 1);
321681d9761SEric Taylor 	return (SDEV_VTOR_VALID);
322681d9761SEric Taylor }
323681d9761SEric Taylor 
324681d9761SEric Taylor /*
3250ad555adSAlex Wilson  * Taskq callback to update the devzvol_zclist.
3260ad555adSAlex Wilson  *
3270ad555adSAlex Wilson  * We need to defer this to the taskq to avoid it running with a user
3280ad555adSAlex Wilson  * context that might be associated with some non-global zone, and thus
3290ad555adSAlex Wilson  * not being able to list all of the pools on the entire system.
330681d9761SEric Taylor  */
3310ad555adSAlex Wilson /*ARGSUSED*/
3320ad555adSAlex Wilson static void
3330ad555adSAlex Wilson devzvol_update_zclist_cb(void *arg)
334681d9761SEric Taylor {
335681d9761SEric Taylor 	zfs_cmd_t	*zc;
336681d9761SEric Taylor 	int		rc;
3370ad555adSAlex Wilson 	size_t		size;
338681d9761SEric Taylor 
339681d9761SEric Taylor 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
340681d9761SEric Taylor 	mutex_enter(&devzvol_mtx);
341681d9761SEric Taylor 	zc->zc_cookie = devzvol_gen;
342681d9761SEric Taylor 
343681d9761SEric Taylor 	rc = devzvol_handle_ioctl(ZFS_IOC_POOL_CONFIGS, zc, &size);
344681d9761SEric Taylor 	switch (rc) {
345681d9761SEric Taylor 		case 0:
346681d9761SEric Taylor 			/* new generation */
347681d9761SEric Taylor 			ASSERT(devzvol_gen != zc->zc_cookie);
348681d9761SEric Taylor 			devzvol_gen = zc->zc_cookie;
349681d9761SEric Taylor 			if (devzvol_zclist)
350681d9761SEric Taylor 				kmem_free((void *)(uintptr_t)devzvol_zclist,
351681d9761SEric Taylor 				    devzvol_zclist_size);
352681d9761SEric Taylor 			devzvol_zclist = zc->zc_nvlist_dst;
3530ad555adSAlex Wilson 			/* Keep the alloc'd size, not the nvlist size. */
354681d9761SEric Taylor 			devzvol_zclist_size = size;
355681d9761SEric Taylor 			break;
3560ad555adSAlex Wilson 		default:
357681d9761SEric Taylor 			/*
3580ad555adSAlex Wilson 			 * Either there was no change in pool configuration
3590ad555adSAlex Wilson 			 * since we last asked (rc == EEXIST) or we got a
3600ad555adSAlex Wilson 			 * catastrophic error.
3610ad555adSAlex Wilson 			 *
3620ad555adSAlex Wilson 			 * Give up memory and exit.
363681d9761SEric Taylor 			 */
364681d9761SEric Taylor 			kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst,
365681d9761SEric Taylor 			    size);
366681d9761SEric Taylor 			break;
367681d9761SEric Taylor 	}
3680ad555adSAlex Wilson 
3690ad555adSAlex Wilson 	VERIFY(devzvol_zclist_task_running == B_TRUE);
3700ad555adSAlex Wilson 	devzvol_zclist_task_running = B_FALSE;
3710ad555adSAlex Wilson 	mutex_exit(&devzvol_mtx);
3720ad555adSAlex Wilson 
3730ad555adSAlex Wilson 	kmem_free(zc, sizeof (zfs_cmd_t));
3740ad555adSAlex Wilson }
3750ad555adSAlex Wilson 
3760ad555adSAlex Wilson static void
3770ad555adSAlex Wilson devzvol_update_zclist(void)
3780ad555adSAlex Wilson {
3790ad555adSAlex Wilson 	mutex_enter(&devzvol_mtx);
3800ad555adSAlex Wilson 	if (devzvol_zclist_task_running == B_TRUE) {
3810ad555adSAlex Wilson 		mutex_exit(&devzvol_mtx);
3820ad555adSAlex Wilson 		goto wait;
3830ad555adSAlex Wilson 	}
3840ad555adSAlex Wilson 
3850ad555adSAlex Wilson 	devzvol_zclist_task_running = B_TRUE;
3860ad555adSAlex Wilson 
3870ad555adSAlex Wilson 	taskq_dispatch_ent(sdev_taskq, devzvol_update_zclist_cb, NULL, 0,
3880ad555adSAlex Wilson 	    &devzvol_zclist_task);
3890ad555adSAlex Wilson 
3900ad555adSAlex Wilson 	mutex_exit(&devzvol_mtx);
3910ad555adSAlex Wilson 
3920ad555adSAlex Wilson wait:
3930ad555adSAlex Wilson 	taskq_wait(sdev_taskq);
3940ad555adSAlex Wilson }
3950ad555adSAlex Wilson 
3960ad555adSAlex Wilson /*
3970ad555adSAlex Wilson  * Creates sub-directories for each zpool as needed in response to a
3980ad555adSAlex Wilson  * readdir on one of the /dev/zvol/{dsk,rdsk} directories.
3990ad555adSAlex Wilson  */
4000ad555adSAlex Wilson void
4010ad555adSAlex Wilson devzvol_create_pool_dirs(struct vnode *dvp)
4020ad555adSAlex Wilson {
4030ad555adSAlex Wilson 	nvlist_t *nv = NULL;
4040ad555adSAlex Wilson 	nvpair_t *elem = NULL;
4050ad555adSAlex Wilson 	int pools = 0;
4060ad555adSAlex Wilson 	int rc;
4070ad555adSAlex Wilson 
4080ad555adSAlex Wilson 	sdcmn_err13(("devzvol_create_pool_dirs"));
4090ad555adSAlex Wilson 
4100ad555adSAlex Wilson 	devzvol_update_zclist();
4110ad555adSAlex Wilson 
4120ad555adSAlex Wilson 	mutex_enter(&devzvol_mtx);
4130ad555adSAlex Wilson 
414681d9761SEric Taylor 	rc = nvlist_unpack((char *)(uintptr_t)devzvol_zclist,
415681d9761SEric Taylor 	    devzvol_zclist_size, &nv, 0);
416681d9761SEric Taylor 	if (rc) {
417681d9761SEric Taylor 		ASSERT(rc == 0);
418681d9761SEric Taylor 		kmem_free((void *)(uintptr_t)devzvol_zclist,
419681d9761SEric Taylor 		    devzvol_zclist_size);
420681d9761SEric Taylor 		devzvol_gen = 0;
421681d9761SEric Taylor 		devzvol_zclist = NULL;
422681d9761SEric Taylor 		devzvol_zclist_size = 0;
423681d9761SEric Taylor 		goto out;
424681d9761SEric Taylor 	}
425681d9761SEric Taylor 	mutex_exit(&devzvol_mtx);
426681d9761SEric Taylor 	while ((elem = nvlist_next_nvpair(nv, elem)) != NULL) {
427681d9761SEric Taylor 		struct vnode *vp;
428681d9761SEric Taylor 		ASSERT(dvp->v_count > 0);
429681d9761SEric Taylor 		rc = VOP_LOOKUP(dvp, nvpair_name(elem), &vp, NULL, 0,
430681d9761SEric Taylor 		    NULL, kcred, NULL, 0, NULL);
431681d9761SEric Taylor 		/* should either work, or not be visible from a zone */
432681d9761SEric Taylor 		ASSERT(rc == 0 || rc == ENOENT);
433681d9761SEric Taylor 		if (rc == 0)
434681d9761SEric Taylor 			VN_RELE(vp);
435681d9761SEric Taylor 		pools++;
436681d9761SEric Taylor 	}
437681d9761SEric Taylor 	nvlist_free(nv);
438681d9761SEric Taylor 	mutex_enter(&devzvol_mtx);
439ff060bd8SEric Taylor 	if (devzvol_isopen && pools == 0) {
440681d9761SEric Taylor 		/* clean up so zfs can be unloaded */
441681d9761SEric Taylor 		devzvol_close_zfs();
442ff060bd8SEric Taylor 		devzvol_isopen = B_FALSE;
443681d9761SEric Taylor 	}
444681d9761SEric Taylor out:
445681d9761SEric Taylor 	mutex_exit(&devzvol_mtx);
446681d9761SEric Taylor }
447681d9761SEric Taylor 
448681d9761SEric Taylor /*ARGSUSED3*/
449681d9761SEric Taylor static int
450681d9761SEric Taylor devzvol_create_dir(struct sdev_node *ddv, char *nm, void **arg,
451681d9761SEric Taylor     cred_t *cred, void *whatever, char *whichever)
452681d9761SEric Taylor {
453681d9761SEric Taylor 	timestruc_t now;
454681d9761SEric Taylor 	struct vattr *vap = (struct vattr *)arg;
455681d9761SEric Taylor 
456681d9761SEric Taylor 	sdcmn_err13(("create_dir (%s) (%s) '%s'", ddv->sdev_name,
457681d9761SEric Taylor 	    ddv->sdev_path, nm));
458681d9761SEric Taylor 	ASSERT(strncmp(ddv->sdev_path, ZVOL_DIR,
459681d9761SEric Taylor 	    strlen(ZVOL_DIR)) == 0);
460681d9761SEric Taylor 	*vap = *sdev_getdefault_attr(VDIR);
461681d9761SEric Taylor 	gethrestime(&now);
462681d9761SEric Taylor 	vap->va_atime = now;
463681d9761SEric Taylor 	vap->va_mtime = now;
464681d9761SEric Taylor 	vap->va_ctime = now;
465681d9761SEric Taylor 	return (0);
466681d9761SEric Taylor }
467681d9761SEric Taylor 
468681d9761SEric Taylor /*ARGSUSED3*/
469681d9761SEric Taylor static int
470681d9761SEric Taylor devzvol_create_link(struct sdev_node *ddv, char *nm,
471681d9761SEric Taylor     void **arg, cred_t *cred, void *whatever, char *whichever)
472681d9761SEric Taylor {
473681d9761SEric Taylor 	minor_t minor;
474681d9761SEric Taylor 	char *pathname = (char *)*arg;
475681d9761SEric Taylor 	int rc;
476681d9761SEric Taylor 	char *dsname;
477681d9761SEric Taylor 	char *x;
478681d9761SEric Taylor 	char str[MAXNAMELEN];
479681d9761SEric Taylor 	sdcmn_err13(("create_link (%s) (%s) '%s'", ddv->sdev_name,
480681d9761SEric Taylor 	    ddv->sdev_path, nm));
481681d9761SEric Taylor 	dsname = devzvol_make_dsname(ddv->sdev_path, nm);
482681d9761SEric Taylor 	rc = sdev_zvol_create_minor(dsname);
483681d9761SEric Taylor 	if ((rc != 0 && rc != EEXIST && rc != EBUSY) ||
484681d9761SEric Taylor 	    sdev_zvol_name2minor(dsname, &minor)) {
485681d9761SEric Taylor 		sdcmn_err13(("devzvol_create_link %d", rc));
486681d9761SEric Taylor 		kmem_free(dsname, strlen(dsname) + 1);
487681d9761SEric Taylor 		return (-1);
488681d9761SEric Taylor 	}
489681d9761SEric Taylor 	kmem_free(dsname, strlen(dsname) + 1);
490681d9761SEric Taylor 
491681d9761SEric Taylor 	/*
492681d9761SEric Taylor 	 * This is a valid zvol; create a symlink that points to the
493681d9761SEric Taylor 	 * minor which was created under /devices/pseudo/zfs@0
494681d9761SEric Taylor 	 */
495681d9761SEric Taylor 	*pathname = '\0';
496681d9761SEric Taylor 	for (x = ddv->sdev_path; x = strchr(x, '/'); x++)
497681d9761SEric Taylor 		(void) strcat(pathname, "../");
498681d9761SEric Taylor 	(void) snprintf(str, sizeof (str), ZVOL_PSEUDO_DEV "%u", minor);
499681d9761SEric Taylor 	(void) strncat(pathname, str, MAXPATHLEN);
500681d9761SEric Taylor 	if (strncmp(ddv->sdev_path, ZVOL_FULL_RDEV_DIR,
501681d9761SEric Taylor 	    strlen(ZVOL_FULL_RDEV_DIR)) == 0)
502681d9761SEric Taylor 		(void) strcat(pathname, ",raw");
503681d9761SEric Taylor 	return (0);
504681d9761SEric Taylor }
505681d9761SEric Taylor 
506681d9761SEric Taylor /* Clean zvol sdev_nodes that are no longer valid.  */
507681d9761SEric Taylor static void
508681d9761SEric Taylor devzvol_prunedir(struct sdev_node *ddv)
509681d9761SEric Taylor {
510681d9761SEric Taylor 	struct sdev_node *dv;
511681d9761SEric Taylor 
512681d9761SEric Taylor 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
513681d9761SEric Taylor 
514681d9761SEric Taylor 	sdcmn_err13(("prunedir '%s'", ddv->sdev_name));
515681d9761SEric Taylor 	ASSERT(strncmp(ddv->sdev_path, ZVOL_DIR, strlen(ZVOL_DIR)) == 0);
516681d9761SEric Taylor 	if (rw_tryupgrade(&ddv->sdev_contents) == 0) {
517681d9761SEric Taylor 		rw_exit(&ddv->sdev_contents);
518681d9761SEric Taylor 		rw_enter(&ddv->sdev_contents, RW_WRITER);
519681d9761SEric Taylor 	}
520681d9761SEric Taylor 
521681d9761SEric Taylor 	dv = SDEV_FIRST_ENTRY(ddv);
522681d9761SEric Taylor 	while (dv) {
523681d9761SEric Taylor 		sdcmn_err13(("sdev_name '%s'", dv->sdev_name));
524681d9761SEric Taylor 
525681d9761SEric Taylor 		switch (devzvol_validate(dv)) {
526681d9761SEric Taylor 		case SDEV_VTOR_VALID:
527681d9761SEric Taylor 		case SDEV_VTOR_SKIP:
528681d9761SEric Taylor 			dv = SDEV_NEXT_ENTRY(ddv, dv);
529681d9761SEric Taylor 			continue;
530681d9761SEric Taylor 		case SDEV_VTOR_INVALID:
531681d9761SEric Taylor 			sdcmn_err7(("prunedir: destroy invalid "
532681d9761SEric Taylor 			    "node: %s\n", dv->sdev_name));
533681d9761SEric Taylor 			break;
534681d9761SEric Taylor 		}
535681d9761SEric Taylor 
536681d9761SEric Taylor 		if ((SDEVTOV(dv)->v_type == VDIR) &&
537681d9761SEric Taylor 		    (sdev_cleandir(dv, NULL, 0) != 0)) {
538681d9761SEric Taylor 			dv = SDEV_NEXT_ENTRY(ddv, dv);
539681d9761SEric Taylor 			continue;
540681d9761SEric Taylor 		}
541681d9761SEric Taylor 		SDEV_HOLD(dv);
542681d9761SEric Taylor 		/* remove the cache node */
5439e5aa9d8SRobert Mustacchi 		sdev_cache_update(ddv, &dv, dv->sdev_name,
5449e5aa9d8SRobert Mustacchi 		    SDEV_CACHE_DELETE);
5459e5aa9d8SRobert Mustacchi 		SDEV_RELE(dv);
546681d9761SEric Taylor 		dv = SDEV_FIRST_ENTRY(ddv);
547681d9761SEric Taylor 	}
548681d9761SEric Taylor 	rw_downgrade(&ddv->sdev_contents);
549681d9761SEric Taylor }
550681d9761SEric Taylor 
551dd9c3b29SJerry Jelinek /*
552dd9c3b29SJerry Jelinek  * This function is used to create a dir or dev inside a zone's /dev when the
553dd9c3b29SJerry Jelinek  * zone has a zvol that is dynamically created within the zone (i.e. inside
554dd9c3b29SJerry Jelinek  * of a delegated dataset.  Since there is no /devices tree within a zone,
555dd9c3b29SJerry Jelinek  * we create the chr/blk devices directly inside the zone's /dev instead of
556dd9c3b29SJerry Jelinek  * making symlinks.
557dd9c3b29SJerry Jelinek  */
558dd9c3b29SJerry Jelinek static int
559dd9c3b29SJerry Jelinek devzvol_mk_ngz_node(struct sdev_node *parent, char *nm)
560dd9c3b29SJerry Jelinek {
561dd9c3b29SJerry Jelinek 	struct vattr vattr;
562dd9c3b29SJerry Jelinek 	timestruc_t now;
563dd9c3b29SJerry Jelinek 	enum vtype expected_type = VDIR;
564dd9c3b29SJerry Jelinek 	dmu_objset_type_t do_type;
565dd9c3b29SJerry Jelinek 	struct sdev_node *dv = NULL;
566dd9c3b29SJerry Jelinek 	int res;
567dd9c3b29SJerry Jelinek 	char *dsname;
568dd9c3b29SJerry Jelinek 
569dd9c3b29SJerry Jelinek 	bzero(&vattr, sizeof (vattr));
570dd9c3b29SJerry Jelinek 	gethrestime(&now);
571dd9c3b29SJerry Jelinek 	vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID;
572dd9c3b29SJerry Jelinek 	vattr.va_uid = SDEV_UID_DEFAULT;
573dd9c3b29SJerry Jelinek 	vattr.va_gid = SDEV_GID_DEFAULT;
574dd9c3b29SJerry Jelinek 	vattr.va_type = VNON;
575dd9c3b29SJerry Jelinek 	vattr.va_atime = now;
576dd9c3b29SJerry Jelinek 	vattr.va_mtime = now;
577dd9c3b29SJerry Jelinek 	vattr.va_ctime = now;
578dd9c3b29SJerry Jelinek 
579dd9c3b29SJerry Jelinek 	if ((dsname = devzvol_make_dsname(parent->sdev_path, nm)) == NULL)
580dd9c3b29SJerry Jelinek 		return (ENOENT);
581dd9c3b29SJerry Jelinek 
582dd9c3b29SJerry Jelinek 	if (devzvol_objset_check(dsname, &do_type) != 0) {
583dd9c3b29SJerry Jelinek 		kmem_free(dsname, strlen(dsname) + 1);
584dd9c3b29SJerry Jelinek 		return (ENOENT);
585dd9c3b29SJerry Jelinek 	}
586dd9c3b29SJerry Jelinek 	if (do_type == DMU_OST_ZVOL)
587dd9c3b29SJerry Jelinek 		expected_type = VBLK;
588dd9c3b29SJerry Jelinek 
589dd9c3b29SJerry Jelinek 	if (expected_type == VDIR) {
590dd9c3b29SJerry Jelinek 		vattr.va_type = VDIR;
591dd9c3b29SJerry Jelinek 		vattr.va_mode = SDEV_DIRMODE_DEFAULT;
592dd9c3b29SJerry Jelinek 	} else {
593dd9c3b29SJerry Jelinek 		minor_t minor;
594dd9c3b29SJerry Jelinek 		dev_t devnum;
595dd9c3b29SJerry Jelinek 		int rc;
596dd9c3b29SJerry Jelinek 
597dd9c3b29SJerry Jelinek 		rc = sdev_zvol_create_minor(dsname);
598dd9c3b29SJerry Jelinek 		if ((rc != 0 && rc != EEXIST && rc != EBUSY) ||
599dd9c3b29SJerry Jelinek 		    sdev_zvol_name2minor(dsname, &minor)) {
600dd9c3b29SJerry Jelinek 			kmem_free(dsname, strlen(dsname) + 1);
601dd9c3b29SJerry Jelinek 			return (ENOENT);
602dd9c3b29SJerry Jelinek 		}
603dd9c3b29SJerry Jelinek 
604dd9c3b29SJerry Jelinek 		devnum = makedevice(devzvol_major, minor);
605dd9c3b29SJerry Jelinek 		vattr.va_rdev = devnum;
606dd9c3b29SJerry Jelinek 
607dd9c3b29SJerry Jelinek 		if (strstr(parent->sdev_path, "/rdsk/") != NULL)
608dd9c3b29SJerry Jelinek 			vattr.va_type = VCHR;
609dd9c3b29SJerry Jelinek 		else
610dd9c3b29SJerry Jelinek 			vattr.va_type = VBLK;
611dd9c3b29SJerry Jelinek 		vattr.va_mode = SDEV_DEVMODE_DEFAULT;
612dd9c3b29SJerry Jelinek 	}
613dd9c3b29SJerry Jelinek 	kmem_free(dsname, strlen(dsname) + 1);
614dd9c3b29SJerry Jelinek 
615dd9c3b29SJerry Jelinek 	rw_enter(&parent->sdev_contents, RW_WRITER);
616dd9c3b29SJerry Jelinek 
617dd9c3b29SJerry Jelinek 	res = sdev_mknode(parent, nm, &dv, &vattr,
618dd9c3b29SJerry Jelinek 	    NULL, NULL, kcred, SDEV_READY);
619dd9c3b29SJerry Jelinek 	rw_exit(&parent->sdev_contents);
620dd9c3b29SJerry Jelinek 	if (res != 0)
621dd9c3b29SJerry Jelinek 		return (ENOENT);
622dd9c3b29SJerry Jelinek 
623dd9c3b29SJerry Jelinek 	SDEV_RELE(dv);
624dd9c3b29SJerry Jelinek 	return (0);
625dd9c3b29SJerry Jelinek }
626dd9c3b29SJerry Jelinek 
627681d9761SEric Taylor /*ARGSUSED*/
628681d9761SEric Taylor static int
629681d9761SEric Taylor devzvol_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
630681d9761SEric Taylor     struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
631681d9761SEric Taylor     caller_context_t *ct, int *direntflags, pathname_t *realpnp)
632681d9761SEric Taylor {
633681d9761SEric Taylor 	enum vtype expected_type = VDIR;
634681d9761SEric Taylor 	struct sdev_node *parent = VTOSDEV(dvp);
635681d9761SEric Taylor 	char *dsname;
636681d9761SEric Taylor 	dmu_objset_type_t do_type;
637681d9761SEric Taylor 	int error;
638681d9761SEric Taylor 
639681d9761SEric Taylor 	sdcmn_err13(("devzvol_lookup '%s' '%s'", parent->sdev_path, nm));
640681d9761SEric Taylor 	*vpp = NULL;
641681d9761SEric Taylor 	/* execute access is required to search the directory */
642681d9761SEric Taylor 	if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0)
643681d9761SEric Taylor 		return (error);
644681d9761SEric Taylor 
645681d9761SEric Taylor 	rw_enter(&parent->sdev_contents, RW_READER);
646dd9c3b29SJerry Jelinek 	if (SDEV_IS_GLOBAL(parent)) {
647dd9c3b29SJerry Jelinek 		/*
648dd9c3b29SJerry Jelinek 		 * During iter_datasets, don't create GZ dev when running in
649dd9c3b29SJerry Jelinek 		 * NGZ.  We can't return ENOENT here since that could
650dd9c3b29SJerry Jelinek 		 * incorrectly trigger the creation of the dev from the
651dd9c3b29SJerry Jelinek 		 * recursive call through prof_filldir during iter_datasets.
652dd9c3b29SJerry Jelinek 		 */
653dd9c3b29SJerry Jelinek 		if (getzoneid() != GLOBAL_ZONEID) {
654681d9761SEric Taylor 			rw_exit(&parent->sdev_contents);
655dd9c3b29SJerry Jelinek 			return (EPERM);
656dd9c3b29SJerry Jelinek 		}
657dd9c3b29SJerry Jelinek 	} else {
658dd9c3b29SJerry Jelinek 		int res;
659dd9c3b29SJerry Jelinek 
660dd9c3b29SJerry Jelinek 		rw_exit(&parent->sdev_contents);
661dd9c3b29SJerry Jelinek 
662dd9c3b29SJerry Jelinek 		/*
663dd9c3b29SJerry Jelinek 		 * If we're in the global zone and reach down into a non-global
664dd9c3b29SJerry Jelinek 		 * zone's /dev/zvol then this action could trigger the creation
665dd9c3b29SJerry Jelinek 		 * of all of the zvol devices for every zone into the non-global
666dd9c3b29SJerry Jelinek 		 * zone's /dev tree. This could be a big security hole. To
667dd9c3b29SJerry Jelinek 		 * prevent this, disallow the global zone from looking inside
668dd9c3b29SJerry Jelinek 		 * a non-global zones /dev/zvol. This behavior is similar to
669dd9c3b29SJerry Jelinek 		 * delegated datasets, which cannot be used by the global zone.
670dd9c3b29SJerry Jelinek 		 */
671dd9c3b29SJerry Jelinek 		if (getzoneid() == GLOBAL_ZONEID)
672dd9c3b29SJerry Jelinek 			return (EPERM);
673dd9c3b29SJerry Jelinek 
674dd9c3b29SJerry Jelinek 		res = prof_lookup(dvp, nm, vpp, cred);
675dd9c3b29SJerry Jelinek 
676dd9c3b29SJerry Jelinek 		/*
677dd9c3b29SJerry Jelinek 		 * We won't find a zvol that was dynamically created inside
678dd9c3b29SJerry Jelinek 		 * a NGZ, within a delegated dataset, in the zone's dev profile
679dd9c3b29SJerry Jelinek 		 * but prof_lookup will also find it via sdev_cache_lookup.
680dd9c3b29SJerry Jelinek 		 */
681dd9c3b29SJerry Jelinek 		if (res == ENOENT) {
682dd9c3b29SJerry Jelinek 			/*
683dd9c3b29SJerry Jelinek 			 * We have to create the sdev node for the dymamically
684dd9c3b29SJerry Jelinek 			 * created zvol.
685dd9c3b29SJerry Jelinek 			 */
686dd9c3b29SJerry Jelinek 			if (devzvol_mk_ngz_node(parent, nm) != 0)
687dd9c3b29SJerry Jelinek 				return (ENOENT);
688dd9c3b29SJerry Jelinek 			res = prof_lookup(dvp, nm, vpp, cred);
689dd9c3b29SJerry Jelinek 		}
690dd9c3b29SJerry Jelinek 
691dd9c3b29SJerry Jelinek 		return (res);
692681d9761SEric Taylor 	}
693681d9761SEric Taylor 
694681d9761SEric Taylor 	dsname = devzvol_make_dsname(parent->sdev_path, nm);
695681d9761SEric Taylor 	rw_exit(&parent->sdev_contents);
696681d9761SEric Taylor 	sdcmn_err13(("rvp dsname %s", dsname ? dsname : "(null)"));
697681d9761SEric Taylor 	if (dsname) {
698681d9761SEric Taylor 		error = devzvol_objset_check(dsname, &do_type);
699681d9761SEric Taylor 		if (error != 0) {
700681d9761SEric Taylor 			error = ENOENT;
701681d9761SEric Taylor 			goto out;
702681d9761SEric Taylor 		}
703681d9761SEric Taylor 		if (do_type == DMU_OST_ZVOL)
704681d9761SEric Taylor 			expected_type = VLNK;
705681d9761SEric Taylor 	}
706681d9761SEric Taylor 	/*
707681d9761SEric Taylor 	 * the callbacks expect:
708681d9761SEric Taylor 	 *
709681d9761SEric Taylor 	 * parent->sdev_path		   nm
710681d9761SEric Taylor 	 * /dev/zvol			   {r}dsk
711681d9761SEric Taylor 	 * /dev/zvol/{r}dsk		   <pool name>
712681d9761SEric Taylor 	 * /dev/zvol/{r}dsk/<dataset name> <last ds component>
713681d9761SEric Taylor 	 *
714681d9761SEric Taylor 	 * sdev_name is always last path component of sdev_path
715681d9761SEric Taylor 	 */
716681d9761SEric Taylor 	if (expected_type == VDIR) {
717681d9761SEric Taylor 		error = devname_lookup_func(parent, nm, vpp, cred,
718681d9761SEric Taylor 		    devzvol_create_dir, SDEV_VATTR);
719681d9761SEric Taylor 	} else {
720681d9761SEric Taylor 		error = devname_lookup_func(parent, nm, vpp, cred,
721681d9761SEric Taylor 		    devzvol_create_link, SDEV_VLINK);
722681d9761SEric Taylor 	}
723681d9761SEric Taylor 	sdcmn_err13(("devzvol_lookup %d %d", expected_type, error));
724681d9761SEric Taylor 	ASSERT(error || ((*vpp)->v_type == expected_type));
725681d9761SEric Taylor out:
726681d9761SEric Taylor 	if (dsname)
727681d9761SEric Taylor 		kmem_free(dsname, strlen(dsname) + 1);
728681d9761SEric Taylor 	sdcmn_err13(("devzvol_lookup %d", error));
729681d9761SEric Taylor 	return (error);
730681d9761SEric Taylor }
731681d9761SEric Taylor 
732681d9761SEric Taylor /*
733681d9761SEric Taylor  * We allow create to find existing nodes
734681d9761SEric Taylor  *	- if the node doesn't exist - EROFS
735681d9761SEric Taylor  *	- creating an existing dir read-only succeeds, otherwise EISDIR
736681d9761SEric Taylor  *	- exclusive creates fail - EEXIST
737681d9761SEric Taylor  */
738681d9761SEric Taylor /*ARGSUSED2*/
739681d9761SEric Taylor static int
740681d9761SEric Taylor devzvol_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
741681d9761SEric Taylor     int mode, struct vnode **vpp, struct cred *cred, int flag,
742681d9761SEric Taylor     caller_context_t *ct, vsecattr_t *vsecp)
743681d9761SEric Taylor {
744681d9761SEric Taylor 	int error;
745681d9761SEric Taylor 	struct vnode *vp;
746681d9761SEric Taylor 
747681d9761SEric Taylor 	*vpp = NULL;
748681d9761SEric Taylor 
749681d9761SEric Taylor 	error = devzvol_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL,
750681d9761SEric Taylor 	    NULL);
751681d9761SEric Taylor 	if (error == 0) {
752681d9761SEric Taylor 		if (excl == EXCL)
753681d9761SEric Taylor 			error = EEXIST;
754681d9761SEric Taylor 		else if (vp->v_type == VDIR && (mode & VWRITE))
755681d9761SEric Taylor 			error = EISDIR;
756681d9761SEric Taylor 		else
757681d9761SEric Taylor 			error = VOP_ACCESS(vp, mode, 0, cred, ct);
758681d9761SEric Taylor 
759681d9761SEric Taylor 		if (error) {
760681d9761SEric Taylor 			VN_RELE(vp);
761681d9761SEric Taylor 		} else
762681d9761SEric Taylor 			*vpp = vp;
763681d9761SEric Taylor 	} else if (error == ENOENT) {
764681d9761SEric Taylor 		error = EROFS;
765681d9761SEric Taylor 	}
766681d9761SEric Taylor 
767681d9761SEric Taylor 	return (error);
768681d9761SEric Taylor }
769681d9761SEric Taylor 
770681d9761SEric Taylor void sdev_iter_snapshots(struct vnode *dvp, char *name);
771681d9761SEric Taylor 
772681d9761SEric Taylor void
773681d9761SEric Taylor sdev_iter_datasets(struct vnode *dvp, int arg, char *name)
774681d9761SEric Taylor {
775681d9761SEric Taylor 	zfs_cmd_t	*zc;
776681d9761SEric Taylor 	int rc;
777681d9761SEric Taylor 
778681d9761SEric Taylor 	sdcmn_err13(("iter name is '%s' (arg %x)", name, arg));
779681d9761SEric Taylor 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
780681d9761SEric Taylor 	(void) strcpy(zc->zc_name, name);
781681d9761SEric Taylor 
782681d9761SEric Taylor 	while ((rc = devzvol_handle_ioctl(arg, zc, B_FALSE)) == 0) {
783681d9761SEric Taylor 		struct vnode *vpp;
784681d9761SEric Taylor 		char *ptr;
785681d9761SEric Taylor 
786681d9761SEric Taylor 		sdcmn_err13(("  name %s", zc->zc_name));
787681d9761SEric Taylor 		if (strchr(zc->zc_name, '$') || strchr(zc->zc_name, '%'))
788681d9761SEric Taylor 			goto skip;
789681d9761SEric Taylor 		ptr = strrchr(zc->zc_name, '/') + 1;
790681d9761SEric Taylor 		rc = devzvol_lookup(dvp, ptr, &vpp, NULL, 0, NULL,
791681d9761SEric Taylor 		    kcred, NULL, NULL, NULL);
792681d9761SEric Taylor 		if (rc == 0) {
793681d9761SEric Taylor 			VN_RELE(vpp);
794681d9761SEric Taylor 		} else if (rc == ENOENT) {
795681d9761SEric Taylor 			goto skip;
796681d9761SEric Taylor 		} else {
797dd9c3b29SJerry Jelinek 			/*
798dd9c3b29SJerry Jelinek 			 * EBUSY == problem with zvols's dmu holds?
799dd9c3b29SJerry Jelinek 			 * EPERM when in a NGZ and traversing up and out.
800dd9c3b29SJerry Jelinek 			 */
801681d9761SEric Taylor 			goto skip;
802681d9761SEric Taylor 		}
803681d9761SEric Taylor 		if (arg == ZFS_IOC_DATASET_LIST_NEXT &&
804681d9761SEric Taylor 		    zc->zc_objset_stats.dds_type != DMU_OST_ZFS)
805681d9761SEric Taylor 			sdev_iter_snapshots(dvp, zc->zc_name);
806681d9761SEric Taylor skip:
807681d9761SEric Taylor 		(void) strcpy(zc->zc_name, name);
808681d9761SEric Taylor 	}
809681d9761SEric Taylor 	kmem_free(zc, sizeof (zfs_cmd_t));
810681d9761SEric Taylor }
811681d9761SEric Taylor 
812681d9761SEric Taylor void
813681d9761SEric Taylor sdev_iter_snapshots(struct vnode *dvp, char *name)
814681d9761SEric Taylor {
815681d9761SEric Taylor 	sdev_iter_datasets(dvp, ZFS_IOC_SNAPSHOT_LIST_NEXT, name);
816681d9761SEric Taylor }
817681d9761SEric Taylor 
818681d9761SEric Taylor /*ARGSUSED4*/
819681d9761SEric Taylor static int
820681d9761SEric Taylor devzvol_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
821681d9761SEric Taylor     int *eofp, caller_context_t *ct_unused, int flags_unused)
822681d9761SEric Taylor {
823681d9761SEric Taylor 	struct sdev_node *sdvp = VTOSDEV(dvp);
824681d9761SEric Taylor 	char *ptr;
825681d9761SEric Taylor 
826681d9761SEric Taylor 	sdcmn_err13(("zv readdir of '%s' %s'", sdvp->sdev_path,
827681d9761SEric Taylor 	    sdvp->sdev_name));
828681d9761SEric Taylor 
829681d9761SEric Taylor 	if (strcmp(sdvp->sdev_path, ZVOL_DIR) == 0) {
830681d9761SEric Taylor 		struct vnode *vp;
831681d9761SEric Taylor 
832681d9761SEric Taylor 		rw_exit(&sdvp->sdev_contents);
833681d9761SEric Taylor 		(void) devname_lookup_func(sdvp, "dsk", &vp, cred,
834681d9761SEric Taylor 		    devzvol_create_dir, SDEV_VATTR);
835681d9761SEric Taylor 		VN_RELE(vp);
836681d9761SEric Taylor 		(void) devname_lookup_func(sdvp, "rdsk", &vp, cred,
837681d9761SEric Taylor 		    devzvol_create_dir, SDEV_VATTR);
838681d9761SEric Taylor 		VN_RELE(vp);
839681d9761SEric Taylor 		rw_enter(&sdvp->sdev_contents, RW_READER);
840681d9761SEric Taylor 		return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
841681d9761SEric Taylor 	}
842681d9761SEric Taylor 	if (uiop->uio_offset == 0)
843681d9761SEric Taylor 		devzvol_prunedir(sdvp);
844681d9761SEric Taylor 	ptr = sdvp->sdev_path + strlen(ZVOL_DIR);
845681d9761SEric Taylor 	if ((strcmp(ptr, "/dsk") == 0) || (strcmp(ptr, "/rdsk") == 0)) {
846681d9761SEric Taylor 		rw_exit(&sdvp->sdev_contents);
847681d9761SEric Taylor 		devzvol_create_pool_dirs(dvp);
848681d9761SEric Taylor 		rw_enter(&sdvp->sdev_contents, RW_READER);
849681d9761SEric Taylor 		return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
850681d9761SEric Taylor 	}
851681d9761SEric Taylor 
852d6568684SRobert Mustacchi 	ptr = strchr(ptr + 1, '/');
853d6568684SRobert Mustacchi 	if (ptr == NULL)
854d6568684SRobert Mustacchi 		return (ENOENT);
855d6568684SRobert Mustacchi 	ptr++;
856681d9761SEric Taylor 	rw_exit(&sdvp->sdev_contents);
857681d9761SEric Taylor 	sdev_iter_datasets(dvp, ZFS_IOC_DATASET_LIST_NEXT, ptr);
858681d9761SEric Taylor 	rw_enter(&sdvp->sdev_contents, RW_READER);
859681d9761SEric Taylor 	return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
860681d9761SEric Taylor }
861681d9761SEric Taylor 
862681d9761SEric Taylor const fs_operation_def_t devzvol_vnodeops_tbl[] = {
863681d9761SEric Taylor 	VOPNAME_READDIR,	{ .vop_readdir = devzvol_readdir },
864681d9761SEric Taylor 	VOPNAME_LOOKUP,		{ .vop_lookup = devzvol_lookup },
865681d9761SEric Taylor 	VOPNAME_CREATE,		{ .vop_create = devzvol_create },
866681d9761SEric Taylor 	VOPNAME_RENAME,		{ .error = fs_nosys },
867681d9761SEric Taylor 	VOPNAME_MKDIR,		{ .error = fs_nosys },
868681d9761SEric Taylor 	VOPNAME_RMDIR,		{ .error = fs_nosys },
869681d9761SEric Taylor 	VOPNAME_REMOVE,		{ .error = fs_nosys },
870681d9761SEric Taylor 	VOPNAME_SYMLINK,	{ .error = fs_nosys },
871681d9761SEric Taylor 	NULL,			NULL
872681d9761SEric Taylor };
873