xref: /onnv-gate/usr/src/uts/common/fs/dev/sdev_zvolops.c (revision 10588:dc03f981ea18)
1*10588SEric.Taylor@Sun.COM /*
2*10588SEric.Taylor@Sun.COM  * CDDL HEADER START
3*10588SEric.Taylor@Sun.COM  *
4*10588SEric.Taylor@Sun.COM  * The contents of this file are subject to the terms of the
5*10588SEric.Taylor@Sun.COM  * Common Development and Distribution License (the "License").
6*10588SEric.Taylor@Sun.COM  * You may not use this file except in compliance with the License.
7*10588SEric.Taylor@Sun.COM  *
8*10588SEric.Taylor@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*10588SEric.Taylor@Sun.COM  * or http://www.opensolaris.org/os/licensing.
10*10588SEric.Taylor@Sun.COM  * See the License for the specific language governing permissions
11*10588SEric.Taylor@Sun.COM  * and limitations under the License.
12*10588SEric.Taylor@Sun.COM  *
13*10588SEric.Taylor@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
14*10588SEric.Taylor@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*10588SEric.Taylor@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
16*10588SEric.Taylor@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
17*10588SEric.Taylor@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
18*10588SEric.Taylor@Sun.COM  *
19*10588SEric.Taylor@Sun.COM  * CDDL HEADER END
20*10588SEric.Taylor@Sun.COM  */
21*10588SEric.Taylor@Sun.COM /*
22*10588SEric.Taylor@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23*10588SEric.Taylor@Sun.COM  * Use is subject to license terms.
24*10588SEric.Taylor@Sun.COM  */
25*10588SEric.Taylor@Sun.COM 
26*10588SEric.Taylor@Sun.COM /* vnode ops for the /dev/zvol directory */
27*10588SEric.Taylor@Sun.COM 
28*10588SEric.Taylor@Sun.COM #include <sys/types.h>
29*10588SEric.Taylor@Sun.COM #include <sys/param.h>
30*10588SEric.Taylor@Sun.COM #include <sys/sysmacros.h>
31*10588SEric.Taylor@Sun.COM #include <sys/ddi.h>
32*10588SEric.Taylor@Sun.COM #include <sys/sunndi.h>
33*10588SEric.Taylor@Sun.COM #include <sys/sunldi.h>
34*10588SEric.Taylor@Sun.COM #include <fs/fs_subr.h>
35*10588SEric.Taylor@Sun.COM #include <sys/fs/dv_node.h>
36*10588SEric.Taylor@Sun.COM #include <sys/fs/sdev_impl.h>
37*10588SEric.Taylor@Sun.COM #include <sys/zfs_ioctl.h>
38*10588SEric.Taylor@Sun.COM #include <sys/policy.h>
39*10588SEric.Taylor@Sun.COM #include <sys/stat.h>
40*10588SEric.Taylor@Sun.COM #include <sys/vfs_opreg.h>
41*10588SEric.Taylor@Sun.COM 
42*10588SEric.Taylor@Sun.COM struct vnodeops	*devzvol_vnodeops;
43*10588SEric.Taylor@Sun.COM static uint64_t devzvol_gen = 0;
44*10588SEric.Taylor@Sun.COM static uint64_t devzvol_zclist;
45*10588SEric.Taylor@Sun.COM static size_t devzvol_zclist_size;
46*10588SEric.Taylor@Sun.COM static ldi_ident_t devzvol_li;
47*10588SEric.Taylor@Sun.COM static ldi_handle_t devzvol_lh;
48*10588SEric.Taylor@Sun.COM static kmutex_t devzvol_mtx;
49*10588SEric.Taylor@Sun.COM static int devzvol_isopen;
50*10588SEric.Taylor@Sun.COM 
51*10588SEric.Taylor@Sun.COM /*
52*10588SEric.Taylor@Sun.COM  * we need to use ddi_mod* since fs/dev gets loaded early on in
53*10588SEric.Taylor@Sun.COM  * startup(), and linking fs/dev to fs/zfs would drag in a lot of
54*10588SEric.Taylor@Sun.COM  * other stuff (like drv/random) before the rest of the system is
55*10588SEric.Taylor@Sun.COM  * ready to go
56*10588SEric.Taylor@Sun.COM  */
57*10588SEric.Taylor@Sun.COM ddi_modhandle_t zfs_mod;
58*10588SEric.Taylor@Sun.COM int (*szcm)(char *);
59*10588SEric.Taylor@Sun.COM int (*szn2m)(char *, minor_t *);
60*10588SEric.Taylor@Sun.COM 
61*10588SEric.Taylor@Sun.COM int
62*10588SEric.Taylor@Sun.COM sdev_zvol_create_minor(char *dsname)
63*10588SEric.Taylor@Sun.COM {
64*10588SEric.Taylor@Sun.COM 	return ((*szcm)(dsname));
65*10588SEric.Taylor@Sun.COM }
66*10588SEric.Taylor@Sun.COM 
67*10588SEric.Taylor@Sun.COM int
68*10588SEric.Taylor@Sun.COM sdev_zvol_name2minor(char *dsname, minor_t *minor)
69*10588SEric.Taylor@Sun.COM {
70*10588SEric.Taylor@Sun.COM 	return ((*szn2m)(dsname, minor));
71*10588SEric.Taylor@Sun.COM }
72*10588SEric.Taylor@Sun.COM 
73*10588SEric.Taylor@Sun.COM int
74*10588SEric.Taylor@Sun.COM devzvol_open_zfs()
75*10588SEric.Taylor@Sun.COM {
76*10588SEric.Taylor@Sun.COM 	int rc;
77*10588SEric.Taylor@Sun.COM 
78*10588SEric.Taylor@Sun.COM 	devzvol_li = ldi_ident_from_anon();
79*10588SEric.Taylor@Sun.COM 	if (ldi_open_by_name("/dev/zfs", FREAD | FWRITE, kcred,
80*10588SEric.Taylor@Sun.COM 	    &devzvol_lh, devzvol_li))
81*10588SEric.Taylor@Sun.COM 		return (-1);
82*10588SEric.Taylor@Sun.COM 	if (zfs_mod == NULL && ((zfs_mod = ddi_modopen("fs/zfs",
83*10588SEric.Taylor@Sun.COM 	    KRTLD_MODE_FIRST, &rc)) == NULL)) {
84*10588SEric.Taylor@Sun.COM 		return (rc);
85*10588SEric.Taylor@Sun.COM 	}
86*10588SEric.Taylor@Sun.COM 	ASSERT(szcm == NULL && szn2m == NULL);
87*10588SEric.Taylor@Sun.COM 	if ((szcm = (int (*)(char *))
88*10588SEric.Taylor@Sun.COM 	    ddi_modsym(zfs_mod, "zvol_create_minor", &rc)) == NULL) {
89*10588SEric.Taylor@Sun.COM 		cmn_err(CE_WARN, "couldn't resolve zvol_create_minor");
90*10588SEric.Taylor@Sun.COM 		return (rc);
91*10588SEric.Taylor@Sun.COM 	}
92*10588SEric.Taylor@Sun.COM 	if ((szn2m = (int(*)(char *, minor_t *))
93*10588SEric.Taylor@Sun.COM 	    ddi_modsym(zfs_mod, "zvol_name2minor", &rc)) == NULL) {
94*10588SEric.Taylor@Sun.COM 		cmn_err(CE_WARN, "couldn't resolve zvol_name2minor");
95*10588SEric.Taylor@Sun.COM 		return (rc);
96*10588SEric.Taylor@Sun.COM 	}
97*10588SEric.Taylor@Sun.COM 	return (0);
98*10588SEric.Taylor@Sun.COM }
99*10588SEric.Taylor@Sun.COM 
100*10588SEric.Taylor@Sun.COM void
101*10588SEric.Taylor@Sun.COM devzvol_close_zfs()
102*10588SEric.Taylor@Sun.COM {
103*10588SEric.Taylor@Sun.COM 	szcm = NULL;
104*10588SEric.Taylor@Sun.COM 	szn2m = NULL;
105*10588SEric.Taylor@Sun.COM 	(void) ldi_close(devzvol_lh, FREAD|FWRITE, kcred);
106*10588SEric.Taylor@Sun.COM 	ldi_ident_release(devzvol_li);
107*10588SEric.Taylor@Sun.COM 	if (zfs_mod != NULL) {
108*10588SEric.Taylor@Sun.COM 		(void) ddi_modclose(zfs_mod);
109*10588SEric.Taylor@Sun.COM 		zfs_mod = NULL;
110*10588SEric.Taylor@Sun.COM 	}
111*10588SEric.Taylor@Sun.COM }
112*10588SEric.Taylor@Sun.COM 
113*10588SEric.Taylor@Sun.COM int
114*10588SEric.Taylor@Sun.COM devzvol_handle_ioctl(int cmd, zfs_cmd_t *zc, size_t *alloc_size)
115*10588SEric.Taylor@Sun.COM {
116*10588SEric.Taylor@Sun.COM 	uint64_t cookie;
117*10588SEric.Taylor@Sun.COM 	int size = 8000;
118*10588SEric.Taylor@Sun.COM 	int unused;
119*10588SEric.Taylor@Sun.COM 	int rc;
120*10588SEric.Taylor@Sun.COM 
121*10588SEric.Taylor@Sun.COM 	if (cmd != ZFS_IOC_POOL_CONFIGS)
122*10588SEric.Taylor@Sun.COM 		mutex_enter(&devzvol_mtx);
123*10588SEric.Taylor@Sun.COM 	if (devzvol_isopen == 0) {
124*10588SEric.Taylor@Sun.COM 		if ((rc = devzvol_open_zfs()) == 0) {
125*10588SEric.Taylor@Sun.COM 			devzvol_isopen++;
126*10588SEric.Taylor@Sun.COM 		} else {
127*10588SEric.Taylor@Sun.COM 			if (cmd != ZFS_IOC_POOL_CONFIGS)
128*10588SEric.Taylor@Sun.COM 				mutex_exit(&devzvol_mtx);
129*10588SEric.Taylor@Sun.COM 			return (ENXIO);
130*10588SEric.Taylor@Sun.COM 		}
131*10588SEric.Taylor@Sun.COM 	}
132*10588SEric.Taylor@Sun.COM 	cookie = zc->zc_cookie;
133*10588SEric.Taylor@Sun.COM again:
134*10588SEric.Taylor@Sun.COM 	zc->zc_nvlist_dst = (uint64_t)(intptr_t)kmem_alloc(size,
135*10588SEric.Taylor@Sun.COM 	    KM_SLEEP);
136*10588SEric.Taylor@Sun.COM 	zc->zc_nvlist_dst_size = size;
137*10588SEric.Taylor@Sun.COM 	rc = ldi_ioctl(devzvol_lh, cmd, (intptr_t)zc, FKIOCTL, kcred,
138*10588SEric.Taylor@Sun.COM 	    &unused);
139*10588SEric.Taylor@Sun.COM 	if (rc == ENOMEM) {
140*10588SEric.Taylor@Sun.COM 		int newsize;
141*10588SEric.Taylor@Sun.COM 		newsize = zc->zc_nvlist_dst_size;
142*10588SEric.Taylor@Sun.COM 		ASSERT(newsize > size);
143*10588SEric.Taylor@Sun.COM 		kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
144*10588SEric.Taylor@Sun.COM 		size = newsize;
145*10588SEric.Taylor@Sun.COM 		zc->zc_cookie = cookie;
146*10588SEric.Taylor@Sun.COM 		goto again;
147*10588SEric.Taylor@Sun.COM 	}
148*10588SEric.Taylor@Sun.COM 	if (alloc_size == NULL)
149*10588SEric.Taylor@Sun.COM 		kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
150*10588SEric.Taylor@Sun.COM 	else
151*10588SEric.Taylor@Sun.COM 		*alloc_size = size;
152*10588SEric.Taylor@Sun.COM 	if (cmd != ZFS_IOC_POOL_CONFIGS)
153*10588SEric.Taylor@Sun.COM 		mutex_exit(&devzvol_mtx);
154*10588SEric.Taylor@Sun.COM 	return (rc);
155*10588SEric.Taylor@Sun.COM }
156*10588SEric.Taylor@Sun.COM 
157*10588SEric.Taylor@Sun.COM /* figures out if the objset exists and returns its type */
158*10588SEric.Taylor@Sun.COM int
159*10588SEric.Taylor@Sun.COM devzvol_objset_check(char *dsname, dmu_objset_type_t *type)
160*10588SEric.Taylor@Sun.COM {
161*10588SEric.Taylor@Sun.COM 	boolean_t	ispool;
162*10588SEric.Taylor@Sun.COM 	zfs_cmd_t	*zc;
163*10588SEric.Taylor@Sun.COM 	int rc;
164*10588SEric.Taylor@Sun.COM 
165*10588SEric.Taylor@Sun.COM 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
166*10588SEric.Taylor@Sun.COM 	(void) strlcpy(zc->zc_name, dsname, MAXPATHLEN);
167*10588SEric.Taylor@Sun.COM 
168*10588SEric.Taylor@Sun.COM 	ispool = (strchr(dsname, '/') == NULL) ? B_TRUE : B_FALSE;
169*10588SEric.Taylor@Sun.COM 	if (!ispool && sdev_zvol_name2minor(dsname, NULL) == 0) {
170*10588SEric.Taylor@Sun.COM 		sdcmn_err13(("found cached minor node"));
171*10588SEric.Taylor@Sun.COM 		if (type)
172*10588SEric.Taylor@Sun.COM 			*type = DMU_OST_ZVOL;
173*10588SEric.Taylor@Sun.COM 		kmem_free(zc, sizeof (zfs_cmd_t));
174*10588SEric.Taylor@Sun.COM 		return (0);
175*10588SEric.Taylor@Sun.COM 	}
176*10588SEric.Taylor@Sun.COM 	rc = devzvol_handle_ioctl(ispool ? ZFS_IOC_POOL_STATS :
177*10588SEric.Taylor@Sun.COM 	    ZFS_IOC_OBJSET_STATS, zc, NULL);
178*10588SEric.Taylor@Sun.COM 	if (type && rc == 0)
179*10588SEric.Taylor@Sun.COM 		*type = (ispool) ? DMU_OST_ZFS :
180*10588SEric.Taylor@Sun.COM 		    zc->zc_objset_stats.dds_type;
181*10588SEric.Taylor@Sun.COM 	kmem_free(zc, sizeof (zfs_cmd_t));
182*10588SEric.Taylor@Sun.COM 	return (rc);
183*10588SEric.Taylor@Sun.COM }
184*10588SEric.Taylor@Sun.COM 
185*10588SEric.Taylor@Sun.COM /*
186*10588SEric.Taylor@Sun.COM  * returns what the zfs dataset name should be, given the /dev/zvol
187*10588SEric.Taylor@Sun.COM  * path and an optional name; otherwise NULL
188*10588SEric.Taylor@Sun.COM  */
189*10588SEric.Taylor@Sun.COM char *
190*10588SEric.Taylor@Sun.COM devzvol_make_dsname(const char *path, const char *name)
191*10588SEric.Taylor@Sun.COM {
192*10588SEric.Taylor@Sun.COM 	char *dsname;
193*10588SEric.Taylor@Sun.COM 	const char *ptr;
194*10588SEric.Taylor@Sun.COM 	int dslen;
195*10588SEric.Taylor@Sun.COM 
196*10588SEric.Taylor@Sun.COM 	if (strcmp(path, ZVOL_DIR) == 0)
197*10588SEric.Taylor@Sun.COM 		return (NULL);
198*10588SEric.Taylor@Sun.COM 	if (name && (strcmp(name, ".") == 0 || strcmp(name, "..") == 0))
199*10588SEric.Taylor@Sun.COM 		return (NULL);
200*10588SEric.Taylor@Sun.COM 	ptr = path + strlen(ZVOL_DIR);
201*10588SEric.Taylor@Sun.COM 	if (strncmp(ptr, "/dsk", 4) == 0)
202*10588SEric.Taylor@Sun.COM 		ptr += strlen("/dsk");
203*10588SEric.Taylor@Sun.COM 	else if (strncmp(ptr, "/rdsk", 5) == 0)
204*10588SEric.Taylor@Sun.COM 		ptr += strlen("/rdsk");
205*10588SEric.Taylor@Sun.COM 	else
206*10588SEric.Taylor@Sun.COM 		return (NULL);
207*10588SEric.Taylor@Sun.COM 	if (*ptr == '/')
208*10588SEric.Taylor@Sun.COM 		ptr++;
209*10588SEric.Taylor@Sun.COM 
210*10588SEric.Taylor@Sun.COM 	dslen = strlen(ptr);
211*10588SEric.Taylor@Sun.COM 	if (dslen)
212*10588SEric.Taylor@Sun.COM 		dslen++;			/* plus null */
213*10588SEric.Taylor@Sun.COM 	if (name)
214*10588SEric.Taylor@Sun.COM 		dslen += strlen(name) + 1;	/* plus slash */
215*10588SEric.Taylor@Sun.COM 	dsname = kmem_zalloc(dslen, KM_SLEEP);
216*10588SEric.Taylor@Sun.COM 	if (*ptr) {
217*10588SEric.Taylor@Sun.COM 		(void) strlcpy(dsname, ptr, dslen);
218*10588SEric.Taylor@Sun.COM 		if (name)
219*10588SEric.Taylor@Sun.COM 			(void) strlcat(dsname, "/", dslen);
220*10588SEric.Taylor@Sun.COM 	}
221*10588SEric.Taylor@Sun.COM 	if (name)
222*10588SEric.Taylor@Sun.COM 		(void) strlcat(dsname, name, dslen);
223*10588SEric.Taylor@Sun.COM 	return (dsname);
224*10588SEric.Taylor@Sun.COM }
225*10588SEric.Taylor@Sun.COM 
226*10588SEric.Taylor@Sun.COM /*
227*10588SEric.Taylor@Sun.COM  * check if the zvol's sdev_node is still valid, which means make
228*10588SEric.Taylor@Sun.COM  * sure the zvol is still valid.  zvol minors aren't proactively
229*10588SEric.Taylor@Sun.COM  * destroyed when the zvol is destroyed, so we use a validator to clean
230*10588SEric.Taylor@Sun.COM  * these up (in other words, when such nodes are encountered during
231*10588SEric.Taylor@Sun.COM  * subsequent lookup() and readdir() operations) so that only valid
232*10588SEric.Taylor@Sun.COM  * nodes are returned.  The ordering between devname_lookup_func and
233*10588SEric.Taylor@Sun.COM  * devzvol_validate is a little inefficient in the case of invalid
234*10588SEric.Taylor@Sun.COM  * or stale nodes because devname_lookup_func calls
235*10588SEric.Taylor@Sun.COM  * devzvol_create_{dir, link}, then the validator says it's invalid,
236*10588SEric.Taylor@Sun.COM  * and then the node gets cleaned up.
237*10588SEric.Taylor@Sun.COM  */
238*10588SEric.Taylor@Sun.COM int
239*10588SEric.Taylor@Sun.COM devzvol_validate(struct sdev_node *dv)
240*10588SEric.Taylor@Sun.COM {
241*10588SEric.Taylor@Sun.COM 	dmu_objset_type_t do_type;
242*10588SEric.Taylor@Sun.COM 	char *dsname;
243*10588SEric.Taylor@Sun.COM 	char *nm = dv->sdev_name;
244*10588SEric.Taylor@Sun.COM 	int rc;
245*10588SEric.Taylor@Sun.COM 
246*10588SEric.Taylor@Sun.COM 	sdcmn_err13(("validating ('%s' '%s')", dv->sdev_path, nm));
247*10588SEric.Taylor@Sun.COM 	/*
248*10588SEric.Taylor@Sun.COM 	 * validate only READY nodes; if someone is sitting on the
249*10588SEric.Taylor@Sun.COM 	 * directory of a dataset that just got destroyed we could
250*10588SEric.Taylor@Sun.COM 	 * get a zombie node which we just skip.
251*10588SEric.Taylor@Sun.COM 	 */
252*10588SEric.Taylor@Sun.COM 	if (dv->sdev_state != SDEV_READY) {
253*10588SEric.Taylor@Sun.COM 		sdcmn_err13(("skipping '%s'", nm));
254*10588SEric.Taylor@Sun.COM 		return (SDEV_VTOR_SKIP);
255*10588SEric.Taylor@Sun.COM 	}
256*10588SEric.Taylor@Sun.COM 
257*10588SEric.Taylor@Sun.COM 	if ((strcmp(dv->sdev_path, ZVOL_DIR "/dsk") == 0) ||
258*10588SEric.Taylor@Sun.COM 	    (strcmp(dv->sdev_path, ZVOL_DIR "/rdsk") == 0))
259*10588SEric.Taylor@Sun.COM 		return (SDEV_VTOR_VALID);
260*10588SEric.Taylor@Sun.COM 	dsname = devzvol_make_dsname(dv->sdev_path, NULL);
261*10588SEric.Taylor@Sun.COM 	if (dsname == NULL)
262*10588SEric.Taylor@Sun.COM 		return (SDEV_VTOR_INVALID);
263*10588SEric.Taylor@Sun.COM 
264*10588SEric.Taylor@Sun.COM 	rc = devzvol_objset_check(dsname, &do_type);
265*10588SEric.Taylor@Sun.COM 	sdcmn_err13(("  '%s' rc %d", dsname, rc));
266*10588SEric.Taylor@Sun.COM 	if (rc != 0) {
267*10588SEric.Taylor@Sun.COM 		kmem_free(dsname, strlen(dsname) + 1);
268*10588SEric.Taylor@Sun.COM 		return (SDEV_VTOR_INVALID);
269*10588SEric.Taylor@Sun.COM 	}
270*10588SEric.Taylor@Sun.COM 	sdcmn_err13(("  v_type %d do_type %d",
271*10588SEric.Taylor@Sun.COM 	    SDEVTOV(dv)->v_type, do_type));
272*10588SEric.Taylor@Sun.COM 	if ((SDEVTOV(dv)->v_type == VLNK && do_type != DMU_OST_ZVOL) ||
273*10588SEric.Taylor@Sun.COM 	    (SDEVTOV(dv)->v_type == VDIR && do_type == DMU_OST_ZVOL)) {
274*10588SEric.Taylor@Sun.COM 		kmem_free(dsname, strlen(dsname) + 1);
275*10588SEric.Taylor@Sun.COM 		return (SDEV_VTOR_STALE);
276*10588SEric.Taylor@Sun.COM 	}
277*10588SEric.Taylor@Sun.COM 	if (SDEVTOV(dv)->v_type == VLNK) {
278*10588SEric.Taylor@Sun.COM 		char *ptr, *link;
279*10588SEric.Taylor@Sun.COM 		long val = 0;
280*10588SEric.Taylor@Sun.COM 		minor_t lminor, ominor;
281*10588SEric.Taylor@Sun.COM 
282*10588SEric.Taylor@Sun.COM 		rc = sdev_getlink(SDEVTOV(dv), &link);
283*10588SEric.Taylor@Sun.COM 		ASSERT(rc == 0);
284*10588SEric.Taylor@Sun.COM 
285*10588SEric.Taylor@Sun.COM 		ptr = strrchr(link, ':') + 1;
286*10588SEric.Taylor@Sun.COM 		rc = ddi_strtol(ptr, NULL, 10, &val);
287*10588SEric.Taylor@Sun.COM 		kmem_free(link, strlen(link) + 1);
288*10588SEric.Taylor@Sun.COM 		ASSERT(rc == 0 && val != 0);
289*10588SEric.Taylor@Sun.COM 		lminor = (minor_t)val;
290*10588SEric.Taylor@Sun.COM 		if (sdev_zvol_name2minor(dsname, &ominor) < 0 ||
291*10588SEric.Taylor@Sun.COM 		    ominor != lminor) {
292*10588SEric.Taylor@Sun.COM 			kmem_free(dsname, strlen(dsname) + 1);
293*10588SEric.Taylor@Sun.COM 			return (SDEV_VTOR_STALE);
294*10588SEric.Taylor@Sun.COM 		}
295*10588SEric.Taylor@Sun.COM 	}
296*10588SEric.Taylor@Sun.COM 	kmem_free(dsname, strlen(dsname) + 1);
297*10588SEric.Taylor@Sun.COM 	return (SDEV_VTOR_VALID);
298*10588SEric.Taylor@Sun.COM }
299*10588SEric.Taylor@Sun.COM 
300*10588SEric.Taylor@Sun.COM /*
301*10588SEric.Taylor@Sun.COM  * creates directories as needed in response to a readdir
302*10588SEric.Taylor@Sun.COM  */
303*10588SEric.Taylor@Sun.COM void
304*10588SEric.Taylor@Sun.COM devzvol_create_pool_dirs(struct vnode *dvp)
305*10588SEric.Taylor@Sun.COM {
306*10588SEric.Taylor@Sun.COM 	zfs_cmd_t	*zc;
307*10588SEric.Taylor@Sun.COM 	nvlist_t *nv = NULL;
308*10588SEric.Taylor@Sun.COM 	nvpair_t *elem = NULL;
309*10588SEric.Taylor@Sun.COM 	size_t size;
310*10588SEric.Taylor@Sun.COM 	int pools = 0;
311*10588SEric.Taylor@Sun.COM 	int rc;
312*10588SEric.Taylor@Sun.COM 
313*10588SEric.Taylor@Sun.COM 	sdcmn_err13(("devzvol_create_pool_dirs"));
314*10588SEric.Taylor@Sun.COM 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
315*10588SEric.Taylor@Sun.COM 	mutex_enter(&devzvol_mtx);
316*10588SEric.Taylor@Sun.COM 	zc->zc_cookie = devzvol_gen;
317*10588SEric.Taylor@Sun.COM 
318*10588SEric.Taylor@Sun.COM 	rc = devzvol_handle_ioctl(ZFS_IOC_POOL_CONFIGS, zc, &size);
319*10588SEric.Taylor@Sun.COM 	switch (rc) {
320*10588SEric.Taylor@Sun.COM 		case 0:
321*10588SEric.Taylor@Sun.COM 			/* new generation */
322*10588SEric.Taylor@Sun.COM 			ASSERT(devzvol_gen != zc->zc_cookie);
323*10588SEric.Taylor@Sun.COM 			devzvol_gen = zc->zc_cookie;
324*10588SEric.Taylor@Sun.COM 			if (devzvol_zclist)
325*10588SEric.Taylor@Sun.COM 				kmem_free((void *)(uintptr_t)devzvol_zclist,
326*10588SEric.Taylor@Sun.COM 				    devzvol_zclist_size);
327*10588SEric.Taylor@Sun.COM 			devzvol_zclist = zc->zc_nvlist_dst;
328*10588SEric.Taylor@Sun.COM 			devzvol_zclist_size = size;
329*10588SEric.Taylor@Sun.COM 			break;
330*10588SEric.Taylor@Sun.COM 		case EEXIST:
331*10588SEric.Taylor@Sun.COM 			/*
332*10588SEric.Taylor@Sun.COM 			 * no change in the configuration; still need
333*10588SEric.Taylor@Sun.COM 			 * to do lookups in case we did a lookup in
334*10588SEric.Taylor@Sun.COM 			 * zvol/rdsk but not zvol/dsk (or vice versa)
335*10588SEric.Taylor@Sun.COM 			 */
336*10588SEric.Taylor@Sun.COM 			kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst,
337*10588SEric.Taylor@Sun.COM 			    size);
338*10588SEric.Taylor@Sun.COM 			break;
339*10588SEric.Taylor@Sun.COM 		default:
340*10588SEric.Taylor@Sun.COM 			kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst,
341*10588SEric.Taylor@Sun.COM 			    size);
342*10588SEric.Taylor@Sun.COM 			goto out;
343*10588SEric.Taylor@Sun.COM 	}
344*10588SEric.Taylor@Sun.COM 	rc = nvlist_unpack((char *)(uintptr_t)devzvol_zclist,
345*10588SEric.Taylor@Sun.COM 	    devzvol_zclist_size, &nv, 0);
346*10588SEric.Taylor@Sun.COM 	if (rc) {
347*10588SEric.Taylor@Sun.COM 		ASSERT(rc == 0);
348*10588SEric.Taylor@Sun.COM 		kmem_free((void *)(uintptr_t)devzvol_zclist,
349*10588SEric.Taylor@Sun.COM 		    devzvol_zclist_size);
350*10588SEric.Taylor@Sun.COM 		devzvol_gen = 0;
351*10588SEric.Taylor@Sun.COM 		devzvol_zclist = NULL;
352*10588SEric.Taylor@Sun.COM 		devzvol_zclist_size = 0;
353*10588SEric.Taylor@Sun.COM 		goto out;
354*10588SEric.Taylor@Sun.COM 	}
355*10588SEric.Taylor@Sun.COM 	mutex_exit(&devzvol_mtx);
356*10588SEric.Taylor@Sun.COM 	while ((elem = nvlist_next_nvpair(nv, elem)) != NULL) {
357*10588SEric.Taylor@Sun.COM 		struct vnode *vp;
358*10588SEric.Taylor@Sun.COM 		ASSERT(dvp->v_count > 0);
359*10588SEric.Taylor@Sun.COM 		rc = VOP_LOOKUP(dvp, nvpair_name(elem), &vp, NULL, 0,
360*10588SEric.Taylor@Sun.COM 		    NULL, kcred, NULL, 0, NULL);
361*10588SEric.Taylor@Sun.COM 		/* should either work, or not be visible from a zone */
362*10588SEric.Taylor@Sun.COM 		ASSERT(rc == 0 || rc == ENOENT);
363*10588SEric.Taylor@Sun.COM 		if (rc == 0)
364*10588SEric.Taylor@Sun.COM 			VN_RELE(vp);
365*10588SEric.Taylor@Sun.COM 		pools++;
366*10588SEric.Taylor@Sun.COM 	}
367*10588SEric.Taylor@Sun.COM 	nvlist_free(nv);
368*10588SEric.Taylor@Sun.COM 	mutex_enter(&devzvol_mtx);
369*10588SEric.Taylor@Sun.COM 	if (pools == 0) {
370*10588SEric.Taylor@Sun.COM 		/* clean up so zfs can be unloaded */
371*10588SEric.Taylor@Sun.COM 		devzvol_close_zfs();
372*10588SEric.Taylor@Sun.COM 		devzvol_isopen--;
373*10588SEric.Taylor@Sun.COM 	}
374*10588SEric.Taylor@Sun.COM out:
375*10588SEric.Taylor@Sun.COM 	mutex_exit(&devzvol_mtx);
376*10588SEric.Taylor@Sun.COM 	kmem_free(zc, sizeof (zfs_cmd_t));
377*10588SEric.Taylor@Sun.COM }
378*10588SEric.Taylor@Sun.COM 
379*10588SEric.Taylor@Sun.COM /*ARGSUSED3*/
380*10588SEric.Taylor@Sun.COM static int
381*10588SEric.Taylor@Sun.COM devzvol_create_dir(struct sdev_node *ddv, char *nm, void **arg,
382*10588SEric.Taylor@Sun.COM     cred_t *cred, void *whatever, char *whichever)
383*10588SEric.Taylor@Sun.COM {
384*10588SEric.Taylor@Sun.COM 	timestruc_t now;
385*10588SEric.Taylor@Sun.COM 	struct vattr *vap = (struct vattr *)arg;
386*10588SEric.Taylor@Sun.COM 
387*10588SEric.Taylor@Sun.COM 	sdcmn_err13(("create_dir (%s) (%s) '%s'", ddv->sdev_name,
388*10588SEric.Taylor@Sun.COM 	    ddv->sdev_path, nm));
389*10588SEric.Taylor@Sun.COM 	ASSERT(strncmp(ddv->sdev_path, ZVOL_DIR,
390*10588SEric.Taylor@Sun.COM 	    strlen(ZVOL_DIR)) == 0);
391*10588SEric.Taylor@Sun.COM 	*vap = *sdev_getdefault_attr(VDIR);
392*10588SEric.Taylor@Sun.COM 	gethrestime(&now);
393*10588SEric.Taylor@Sun.COM 	vap->va_atime = now;
394*10588SEric.Taylor@Sun.COM 	vap->va_mtime = now;
395*10588SEric.Taylor@Sun.COM 	vap->va_ctime = now;
396*10588SEric.Taylor@Sun.COM 	return (0);
397*10588SEric.Taylor@Sun.COM }
398*10588SEric.Taylor@Sun.COM 
399*10588SEric.Taylor@Sun.COM /*ARGSUSED3*/
400*10588SEric.Taylor@Sun.COM static int
401*10588SEric.Taylor@Sun.COM devzvol_create_link(struct sdev_node *ddv, char *nm,
402*10588SEric.Taylor@Sun.COM     void **arg, cred_t *cred, void *whatever, char *whichever)
403*10588SEric.Taylor@Sun.COM {
404*10588SEric.Taylor@Sun.COM 	minor_t minor;
405*10588SEric.Taylor@Sun.COM 	char *pathname = (char *)*arg;
406*10588SEric.Taylor@Sun.COM 	int rc;
407*10588SEric.Taylor@Sun.COM 	char *dsname;
408*10588SEric.Taylor@Sun.COM 	char *x;
409*10588SEric.Taylor@Sun.COM 	char str[MAXNAMELEN];
410*10588SEric.Taylor@Sun.COM 	sdcmn_err13(("create_link (%s) (%s) '%s'", ddv->sdev_name,
411*10588SEric.Taylor@Sun.COM 	    ddv->sdev_path, nm));
412*10588SEric.Taylor@Sun.COM 	dsname = devzvol_make_dsname(ddv->sdev_path, nm);
413*10588SEric.Taylor@Sun.COM 	rc = sdev_zvol_create_minor(dsname);
414*10588SEric.Taylor@Sun.COM 	if ((rc != 0 && rc != EEXIST && rc != EBUSY) ||
415*10588SEric.Taylor@Sun.COM 	    sdev_zvol_name2minor(dsname, &minor)) {
416*10588SEric.Taylor@Sun.COM 		sdcmn_err13(("devzvol_create_link %d", rc));
417*10588SEric.Taylor@Sun.COM 		kmem_free(dsname, strlen(dsname) + 1);
418*10588SEric.Taylor@Sun.COM 		return (-1);
419*10588SEric.Taylor@Sun.COM 	}
420*10588SEric.Taylor@Sun.COM 	kmem_free(dsname, strlen(dsname) + 1);
421*10588SEric.Taylor@Sun.COM 
422*10588SEric.Taylor@Sun.COM 	/*
423*10588SEric.Taylor@Sun.COM 	 * This is a valid zvol; create a symlink that points to the
424*10588SEric.Taylor@Sun.COM 	 * minor which was created under /devices/pseudo/zfs@0
425*10588SEric.Taylor@Sun.COM 	 */
426*10588SEric.Taylor@Sun.COM 	*pathname = '\0';
427*10588SEric.Taylor@Sun.COM 	for (x = ddv->sdev_path; x = strchr(x, '/'); x++)
428*10588SEric.Taylor@Sun.COM 		(void) strcat(pathname, "../");
429*10588SEric.Taylor@Sun.COM 	(void) snprintf(str, sizeof (str), ZVOL_PSEUDO_DEV "%u", minor);
430*10588SEric.Taylor@Sun.COM 	(void) strncat(pathname, str, MAXPATHLEN);
431*10588SEric.Taylor@Sun.COM 	if (strncmp(ddv->sdev_path, ZVOL_FULL_RDEV_DIR,
432*10588SEric.Taylor@Sun.COM 	    strlen(ZVOL_FULL_RDEV_DIR)) == 0)
433*10588SEric.Taylor@Sun.COM 		(void) strcat(pathname, ",raw");
434*10588SEric.Taylor@Sun.COM 	return (0);
435*10588SEric.Taylor@Sun.COM }
436*10588SEric.Taylor@Sun.COM 
437*10588SEric.Taylor@Sun.COM /* Clean zvol sdev_nodes that are no longer valid.  */
438*10588SEric.Taylor@Sun.COM static void
439*10588SEric.Taylor@Sun.COM devzvol_prunedir(struct sdev_node *ddv)
440*10588SEric.Taylor@Sun.COM {
441*10588SEric.Taylor@Sun.COM 	struct sdev_node *dv;
442*10588SEric.Taylor@Sun.COM 
443*10588SEric.Taylor@Sun.COM 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
444*10588SEric.Taylor@Sun.COM 
445*10588SEric.Taylor@Sun.COM 	sdcmn_err13(("prunedir '%s'", ddv->sdev_name));
446*10588SEric.Taylor@Sun.COM 	ASSERT(strncmp(ddv->sdev_path, ZVOL_DIR, strlen(ZVOL_DIR)) == 0);
447*10588SEric.Taylor@Sun.COM 	if (rw_tryupgrade(&ddv->sdev_contents) == 0) {
448*10588SEric.Taylor@Sun.COM 		rw_exit(&ddv->sdev_contents);
449*10588SEric.Taylor@Sun.COM 		rw_enter(&ddv->sdev_contents, RW_WRITER);
450*10588SEric.Taylor@Sun.COM 	}
451*10588SEric.Taylor@Sun.COM 
452*10588SEric.Taylor@Sun.COM 	dv = SDEV_FIRST_ENTRY(ddv);
453*10588SEric.Taylor@Sun.COM 	while (dv) {
454*10588SEric.Taylor@Sun.COM 		sdcmn_err13(("sdev_name '%s'", dv->sdev_name));
455*10588SEric.Taylor@Sun.COM 		/* skip stale nodes */
456*10588SEric.Taylor@Sun.COM 		if (dv->sdev_flags & SDEV_STALE) {
457*10588SEric.Taylor@Sun.COM 			sdcmn_err13(("  stale"));
458*10588SEric.Taylor@Sun.COM 			dv = SDEV_NEXT_ENTRY(ddv, dv);
459*10588SEric.Taylor@Sun.COM 			continue;
460*10588SEric.Taylor@Sun.COM 		}
461*10588SEric.Taylor@Sun.COM 
462*10588SEric.Taylor@Sun.COM 		switch (devzvol_validate(dv)) {
463*10588SEric.Taylor@Sun.COM 		case SDEV_VTOR_VALID:
464*10588SEric.Taylor@Sun.COM 		case SDEV_VTOR_SKIP:
465*10588SEric.Taylor@Sun.COM 			dv = SDEV_NEXT_ENTRY(ddv, dv);
466*10588SEric.Taylor@Sun.COM 			continue;
467*10588SEric.Taylor@Sun.COM 		case SDEV_VTOR_INVALID:
468*10588SEric.Taylor@Sun.COM 			sdcmn_err7(("prunedir: destroy invalid "
469*10588SEric.Taylor@Sun.COM 			    "node: %s\n", dv->sdev_name));
470*10588SEric.Taylor@Sun.COM 			break;
471*10588SEric.Taylor@Sun.COM 		}
472*10588SEric.Taylor@Sun.COM 
473*10588SEric.Taylor@Sun.COM 		if ((SDEVTOV(dv)->v_type == VDIR) &&
474*10588SEric.Taylor@Sun.COM 		    (sdev_cleandir(dv, NULL, 0) != 0)) {
475*10588SEric.Taylor@Sun.COM 			dv = SDEV_NEXT_ENTRY(ddv, dv);
476*10588SEric.Taylor@Sun.COM 			continue;
477*10588SEric.Taylor@Sun.COM 		}
478*10588SEric.Taylor@Sun.COM 		SDEV_HOLD(dv);
479*10588SEric.Taylor@Sun.COM 		/* remove the cache node */
480*10588SEric.Taylor@Sun.COM 		if (sdev_cache_update(ddv, &dv, dv->sdev_name,
481*10588SEric.Taylor@Sun.COM 		    SDEV_CACHE_DELETE) == 0)
482*10588SEric.Taylor@Sun.COM 			dv = SDEV_FIRST_ENTRY(ddv);
483*10588SEric.Taylor@Sun.COM 		else
484*10588SEric.Taylor@Sun.COM 			dv = SDEV_NEXT_ENTRY(ddv, dv);
485*10588SEric.Taylor@Sun.COM 	}
486*10588SEric.Taylor@Sun.COM 	rw_downgrade(&ddv->sdev_contents);
487*10588SEric.Taylor@Sun.COM }
488*10588SEric.Taylor@Sun.COM 
489*10588SEric.Taylor@Sun.COM /*ARGSUSED*/
490*10588SEric.Taylor@Sun.COM static int
491*10588SEric.Taylor@Sun.COM devzvol_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
492*10588SEric.Taylor@Sun.COM     struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
493*10588SEric.Taylor@Sun.COM     caller_context_t *ct, int *direntflags, pathname_t *realpnp)
494*10588SEric.Taylor@Sun.COM {
495*10588SEric.Taylor@Sun.COM 	enum vtype expected_type = VDIR;
496*10588SEric.Taylor@Sun.COM 	struct sdev_node *parent = VTOSDEV(dvp);
497*10588SEric.Taylor@Sun.COM 	char *dsname;
498*10588SEric.Taylor@Sun.COM 	dmu_objset_type_t do_type;
499*10588SEric.Taylor@Sun.COM 	int error;
500*10588SEric.Taylor@Sun.COM 
501*10588SEric.Taylor@Sun.COM 	sdcmn_err13(("devzvol_lookup '%s' '%s'", parent->sdev_path, nm));
502*10588SEric.Taylor@Sun.COM 	*vpp = NULL;
503*10588SEric.Taylor@Sun.COM 	/* execute access is required to search the directory */
504*10588SEric.Taylor@Sun.COM 	if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0)
505*10588SEric.Taylor@Sun.COM 		return (error);
506*10588SEric.Taylor@Sun.COM 
507*10588SEric.Taylor@Sun.COM 	rw_enter(&parent->sdev_contents, RW_READER);
508*10588SEric.Taylor@Sun.COM 	if (!SDEV_IS_GLOBAL(parent)) {
509*10588SEric.Taylor@Sun.COM 		rw_exit(&parent->sdev_contents);
510*10588SEric.Taylor@Sun.COM 		return (prof_lookup(dvp, nm, vpp, cred));
511*10588SEric.Taylor@Sun.COM 	}
512*10588SEric.Taylor@Sun.COM 
513*10588SEric.Taylor@Sun.COM 	dsname = devzvol_make_dsname(parent->sdev_path, nm);
514*10588SEric.Taylor@Sun.COM 	rw_exit(&parent->sdev_contents);
515*10588SEric.Taylor@Sun.COM 	sdcmn_err13(("rvp dsname %s", dsname ? dsname : "(null)"));
516*10588SEric.Taylor@Sun.COM 	if (dsname) {
517*10588SEric.Taylor@Sun.COM 		error = devzvol_objset_check(dsname, &do_type);
518*10588SEric.Taylor@Sun.COM 		if (error != 0) {
519*10588SEric.Taylor@Sun.COM 			error = ENOENT;
520*10588SEric.Taylor@Sun.COM 			goto out;
521*10588SEric.Taylor@Sun.COM 		}
522*10588SEric.Taylor@Sun.COM 		if (do_type == DMU_OST_ZVOL)
523*10588SEric.Taylor@Sun.COM 			expected_type = VLNK;
524*10588SEric.Taylor@Sun.COM 	}
525*10588SEric.Taylor@Sun.COM 	/*
526*10588SEric.Taylor@Sun.COM 	 * the callbacks expect:
527*10588SEric.Taylor@Sun.COM 	 *
528*10588SEric.Taylor@Sun.COM 	 * parent->sdev_path		   nm
529*10588SEric.Taylor@Sun.COM 	 * /dev/zvol			   {r}dsk
530*10588SEric.Taylor@Sun.COM 	 * /dev/zvol/{r}dsk		   <pool name>
531*10588SEric.Taylor@Sun.COM 	 * /dev/zvol/{r}dsk/<dataset name> <last ds component>
532*10588SEric.Taylor@Sun.COM 	 *
533*10588SEric.Taylor@Sun.COM 	 * sdev_name is always last path component of sdev_path
534*10588SEric.Taylor@Sun.COM 	 */
535*10588SEric.Taylor@Sun.COM 	if (expected_type == VDIR) {
536*10588SEric.Taylor@Sun.COM 		error = devname_lookup_func(parent, nm, vpp, cred,
537*10588SEric.Taylor@Sun.COM 		    devzvol_create_dir, SDEV_VATTR);
538*10588SEric.Taylor@Sun.COM 	} else {
539*10588SEric.Taylor@Sun.COM 		error = devname_lookup_func(parent, nm, vpp, cred,
540*10588SEric.Taylor@Sun.COM 		    devzvol_create_link, SDEV_VLINK);
541*10588SEric.Taylor@Sun.COM 	}
542*10588SEric.Taylor@Sun.COM 	sdcmn_err13(("devzvol_lookup %d %d", expected_type, error));
543*10588SEric.Taylor@Sun.COM 	ASSERT(error || ((*vpp)->v_type == expected_type));
544*10588SEric.Taylor@Sun.COM out:
545*10588SEric.Taylor@Sun.COM 	if (dsname)
546*10588SEric.Taylor@Sun.COM 		kmem_free(dsname, strlen(dsname) + 1);
547*10588SEric.Taylor@Sun.COM 	sdcmn_err13(("devzvol_lookup %d", error));
548*10588SEric.Taylor@Sun.COM 	return (error);
549*10588SEric.Taylor@Sun.COM }
550*10588SEric.Taylor@Sun.COM 
551*10588SEric.Taylor@Sun.COM /*
552*10588SEric.Taylor@Sun.COM  * We allow create to find existing nodes
553*10588SEric.Taylor@Sun.COM  *	- if the node doesn't exist - EROFS
554*10588SEric.Taylor@Sun.COM  *	- creating an existing dir read-only succeeds, otherwise EISDIR
555*10588SEric.Taylor@Sun.COM  *	- exclusive creates fail - EEXIST
556*10588SEric.Taylor@Sun.COM  */
557*10588SEric.Taylor@Sun.COM /*ARGSUSED2*/
558*10588SEric.Taylor@Sun.COM static int
559*10588SEric.Taylor@Sun.COM devzvol_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
560*10588SEric.Taylor@Sun.COM     int mode, struct vnode **vpp, struct cred *cred, int flag,
561*10588SEric.Taylor@Sun.COM     caller_context_t *ct, vsecattr_t *vsecp)
562*10588SEric.Taylor@Sun.COM {
563*10588SEric.Taylor@Sun.COM 	int error;
564*10588SEric.Taylor@Sun.COM 	struct vnode *vp;
565*10588SEric.Taylor@Sun.COM 
566*10588SEric.Taylor@Sun.COM 	*vpp = NULL;
567*10588SEric.Taylor@Sun.COM 
568*10588SEric.Taylor@Sun.COM 	error = devzvol_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL,
569*10588SEric.Taylor@Sun.COM 	    NULL);
570*10588SEric.Taylor@Sun.COM 	if (error == 0) {
571*10588SEric.Taylor@Sun.COM 		if (excl == EXCL)
572*10588SEric.Taylor@Sun.COM 			error = EEXIST;
573*10588SEric.Taylor@Sun.COM 		else if (vp->v_type == VDIR && (mode & VWRITE))
574*10588SEric.Taylor@Sun.COM 			error = EISDIR;
575*10588SEric.Taylor@Sun.COM 		else
576*10588SEric.Taylor@Sun.COM 			error = VOP_ACCESS(vp, mode, 0, cred, ct);
577*10588SEric.Taylor@Sun.COM 
578*10588SEric.Taylor@Sun.COM 		if (error) {
579*10588SEric.Taylor@Sun.COM 			VN_RELE(vp);
580*10588SEric.Taylor@Sun.COM 		} else
581*10588SEric.Taylor@Sun.COM 			*vpp = vp;
582*10588SEric.Taylor@Sun.COM 	} else if (error == ENOENT) {
583*10588SEric.Taylor@Sun.COM 		error = EROFS;
584*10588SEric.Taylor@Sun.COM 	}
585*10588SEric.Taylor@Sun.COM 
586*10588SEric.Taylor@Sun.COM 	return (error);
587*10588SEric.Taylor@Sun.COM }
588*10588SEric.Taylor@Sun.COM 
589*10588SEric.Taylor@Sun.COM void sdev_iter_snapshots(struct vnode *dvp, char *name);
590*10588SEric.Taylor@Sun.COM 
591*10588SEric.Taylor@Sun.COM void
592*10588SEric.Taylor@Sun.COM sdev_iter_datasets(struct vnode *dvp, int arg, char *name)
593*10588SEric.Taylor@Sun.COM {
594*10588SEric.Taylor@Sun.COM 	zfs_cmd_t	*zc;
595*10588SEric.Taylor@Sun.COM 	int rc;
596*10588SEric.Taylor@Sun.COM 
597*10588SEric.Taylor@Sun.COM 	sdcmn_err13(("iter name is '%s' (arg %x)", name, arg));
598*10588SEric.Taylor@Sun.COM 	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
599*10588SEric.Taylor@Sun.COM 	(void) strcpy(zc->zc_name, name);
600*10588SEric.Taylor@Sun.COM 
601*10588SEric.Taylor@Sun.COM 	while ((rc = devzvol_handle_ioctl(arg, zc, B_FALSE)) == 0) {
602*10588SEric.Taylor@Sun.COM 		struct vnode *vpp;
603*10588SEric.Taylor@Sun.COM 		char *ptr;
604*10588SEric.Taylor@Sun.COM 
605*10588SEric.Taylor@Sun.COM 		sdcmn_err13(("  name %s", zc->zc_name));
606*10588SEric.Taylor@Sun.COM 		if (strchr(zc->zc_name, '$') || strchr(zc->zc_name, '%'))
607*10588SEric.Taylor@Sun.COM 			goto skip;
608*10588SEric.Taylor@Sun.COM 		ptr = strrchr(zc->zc_name, '/') + 1;
609*10588SEric.Taylor@Sun.COM 		rc = devzvol_lookup(dvp, ptr, &vpp, NULL, 0, NULL,
610*10588SEric.Taylor@Sun.COM 		    kcred, NULL, NULL, NULL);
611*10588SEric.Taylor@Sun.COM 		if (rc == 0) {
612*10588SEric.Taylor@Sun.COM 			VN_RELE(vpp);
613*10588SEric.Taylor@Sun.COM 		} else if (rc == ENOENT) {
614*10588SEric.Taylor@Sun.COM 			goto skip;
615*10588SEric.Taylor@Sun.COM 		} else {
616*10588SEric.Taylor@Sun.COM 			/* EBUSY == problem with zvols's dmu holds? */
617*10588SEric.Taylor@Sun.COM 			ASSERT(0);
618*10588SEric.Taylor@Sun.COM 			goto skip;
619*10588SEric.Taylor@Sun.COM 		}
620*10588SEric.Taylor@Sun.COM 		if (arg == ZFS_IOC_DATASET_LIST_NEXT &&
621*10588SEric.Taylor@Sun.COM 		    zc->zc_objset_stats.dds_type != DMU_OST_ZFS)
622*10588SEric.Taylor@Sun.COM 			sdev_iter_snapshots(dvp, zc->zc_name);
623*10588SEric.Taylor@Sun.COM skip:
624*10588SEric.Taylor@Sun.COM 		(void) strcpy(zc->zc_name, name);
625*10588SEric.Taylor@Sun.COM 	}
626*10588SEric.Taylor@Sun.COM 	kmem_free(zc, sizeof (zfs_cmd_t));
627*10588SEric.Taylor@Sun.COM }
628*10588SEric.Taylor@Sun.COM 
629*10588SEric.Taylor@Sun.COM void
630*10588SEric.Taylor@Sun.COM sdev_iter_snapshots(struct vnode *dvp, char *name)
631*10588SEric.Taylor@Sun.COM {
632*10588SEric.Taylor@Sun.COM 	sdev_iter_datasets(dvp, ZFS_IOC_SNAPSHOT_LIST_NEXT, name);
633*10588SEric.Taylor@Sun.COM }
634*10588SEric.Taylor@Sun.COM 
635*10588SEric.Taylor@Sun.COM /*ARGSUSED4*/
636*10588SEric.Taylor@Sun.COM static int
637*10588SEric.Taylor@Sun.COM devzvol_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
638*10588SEric.Taylor@Sun.COM     int *eofp, caller_context_t *ct_unused, int flags_unused)
639*10588SEric.Taylor@Sun.COM {
640*10588SEric.Taylor@Sun.COM 	struct sdev_node *sdvp = VTOSDEV(dvp);
641*10588SEric.Taylor@Sun.COM 	char *ptr;
642*10588SEric.Taylor@Sun.COM 
643*10588SEric.Taylor@Sun.COM 	sdcmn_err13(("zv readdir of '%s' %s'", sdvp->sdev_path,
644*10588SEric.Taylor@Sun.COM 	    sdvp->sdev_name));
645*10588SEric.Taylor@Sun.COM 
646*10588SEric.Taylor@Sun.COM 	if (strcmp(sdvp->sdev_path, ZVOL_DIR) == 0) {
647*10588SEric.Taylor@Sun.COM 		struct vnode *vp;
648*10588SEric.Taylor@Sun.COM 
649*10588SEric.Taylor@Sun.COM 		rw_exit(&sdvp->sdev_contents);
650*10588SEric.Taylor@Sun.COM 		(void) devname_lookup_func(sdvp, "dsk", &vp, cred,
651*10588SEric.Taylor@Sun.COM 		    devzvol_create_dir, SDEV_VATTR);
652*10588SEric.Taylor@Sun.COM 		VN_RELE(vp);
653*10588SEric.Taylor@Sun.COM 		(void) devname_lookup_func(sdvp, "rdsk", &vp, cred,
654*10588SEric.Taylor@Sun.COM 		    devzvol_create_dir, SDEV_VATTR);
655*10588SEric.Taylor@Sun.COM 		VN_RELE(vp);
656*10588SEric.Taylor@Sun.COM 		rw_enter(&sdvp->sdev_contents, RW_READER);
657*10588SEric.Taylor@Sun.COM 		return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
658*10588SEric.Taylor@Sun.COM 	}
659*10588SEric.Taylor@Sun.COM 	if (uiop->uio_offset == 0)
660*10588SEric.Taylor@Sun.COM 		devzvol_prunedir(sdvp);
661*10588SEric.Taylor@Sun.COM 	ptr = sdvp->sdev_path + strlen(ZVOL_DIR);
662*10588SEric.Taylor@Sun.COM 	if ((strcmp(ptr, "/dsk") == 0) || (strcmp(ptr, "/rdsk") == 0)) {
663*10588SEric.Taylor@Sun.COM 		rw_exit(&sdvp->sdev_contents);
664*10588SEric.Taylor@Sun.COM 		devzvol_create_pool_dirs(dvp);
665*10588SEric.Taylor@Sun.COM 		rw_enter(&sdvp->sdev_contents, RW_READER);
666*10588SEric.Taylor@Sun.COM 		return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
667*10588SEric.Taylor@Sun.COM 	}
668*10588SEric.Taylor@Sun.COM 
669*10588SEric.Taylor@Sun.COM 	ptr = strchr(ptr + 1, '/') + 1;
670*10588SEric.Taylor@Sun.COM 	rw_exit(&sdvp->sdev_contents);
671*10588SEric.Taylor@Sun.COM 	sdev_iter_datasets(dvp, ZFS_IOC_DATASET_LIST_NEXT, ptr);
672*10588SEric.Taylor@Sun.COM 	rw_enter(&sdvp->sdev_contents, RW_READER);
673*10588SEric.Taylor@Sun.COM 	return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
674*10588SEric.Taylor@Sun.COM }
675*10588SEric.Taylor@Sun.COM 
676*10588SEric.Taylor@Sun.COM const fs_operation_def_t devzvol_vnodeops_tbl[] = {
677*10588SEric.Taylor@Sun.COM 	VOPNAME_READDIR,	{ .vop_readdir = devzvol_readdir },
678*10588SEric.Taylor@Sun.COM 	VOPNAME_LOOKUP,		{ .vop_lookup = devzvol_lookup },
679*10588SEric.Taylor@Sun.COM 	VOPNAME_CREATE,		{ .vop_create = devzvol_create },
680*10588SEric.Taylor@Sun.COM 	VOPNAME_RENAME,		{ .error = fs_nosys },
681*10588SEric.Taylor@Sun.COM 	VOPNAME_MKDIR,		{ .error = fs_nosys },
682*10588SEric.Taylor@Sun.COM 	VOPNAME_RMDIR,		{ .error = fs_nosys },
683*10588SEric.Taylor@Sun.COM 	VOPNAME_REMOVE,		{ .error = fs_nosys },
684*10588SEric.Taylor@Sun.COM 	VOPNAME_SYMLINK,	{ .error = fs_nosys },
685*10588SEric.Taylor@Sun.COM 	NULL,			NULL
686*10588SEric.Taylor@Sun.COM };
687