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;
44681d9761SEric Taylor static uint64_t devzvol_gen = 0;
45681d9761SEric Taylor static uint64_t devzvol_zclist;
46681d9761SEric Taylor static size_t devzvol_zclist_size;
47681d9761SEric Taylor static ldi_ident_t devzvol_li;
48681d9761SEric Taylor static ldi_handle_t devzvol_lh;
49681d9761SEric Taylor static kmutex_t devzvol_mtx;
50ff060bd8SEric Taylor static boolean_t devzvol_isopen;
51dd9c3b29SJerry Jelinek static major_t devzvol_major;
52681d9761SEric Taylor
53681d9761SEric Taylor /*
54681d9761SEric Taylor * we need to use ddi_mod* since fs/dev gets loaded early on in
55681d9761SEric Taylor * startup(), and linking fs/dev to fs/zfs would drag in a lot of
56681d9761SEric Taylor * other stuff (like drv/random) before the rest of the system is
57681d9761SEric Taylor * ready to go
58681d9761SEric Taylor */
59681d9761SEric Taylor ddi_modhandle_t zfs_mod;
60681d9761SEric Taylor int (*szcm)(char *);
61681d9761SEric Taylor int (*szn2m)(char *, minor_t *);
62681d9761SEric Taylor
63681d9761SEric Taylor int
sdev_zvol_create_minor(char * dsname)64681d9761SEric Taylor sdev_zvol_create_minor(char *dsname)
65681d9761SEric Taylor {
66dd9c3b29SJerry Jelinek if (szcm == NULL)
67dd9c3b29SJerry Jelinek return (-1);
68681d9761SEric Taylor return ((*szcm)(dsname));
69681d9761SEric Taylor }
70681d9761SEric Taylor
71681d9761SEric Taylor int
sdev_zvol_name2minor(char * dsname,minor_t * minor)72681d9761SEric Taylor sdev_zvol_name2minor(char *dsname, minor_t *minor)
73681d9761SEric Taylor {
74dd9c3b29SJerry Jelinek if (szn2m == NULL)
75dd9c3b29SJerry Jelinek return (-1);
76681d9761SEric Taylor return ((*szn2m)(dsname, minor));
77681d9761SEric Taylor }
78681d9761SEric Taylor
79681d9761SEric Taylor int
devzvol_open_zfs()80681d9761SEric Taylor devzvol_open_zfs()
81681d9761SEric Taylor {
82681d9761SEric Taylor int rc;
83dd9c3b29SJerry Jelinek dev_t dv;
84681d9761SEric Taylor
85681d9761SEric Taylor devzvol_li = ldi_ident_from_anon();
86681d9761SEric Taylor if (ldi_open_by_name("/dev/zfs", FREAD | FWRITE, kcred,
87681d9761SEric Taylor &devzvol_lh, devzvol_li))
88681d9761SEric Taylor return (-1);
89681d9761SEric Taylor if (zfs_mod == NULL && ((zfs_mod = ddi_modopen("fs/zfs",
90681d9761SEric Taylor KRTLD_MODE_FIRST, &rc)) == NULL)) {
91681d9761SEric Taylor return (rc);
92681d9761SEric Taylor }
93681d9761SEric Taylor ASSERT(szcm == NULL && szn2m == NULL);
94681d9761SEric Taylor if ((szcm = (int (*)(char *))
95681d9761SEric Taylor ddi_modsym(zfs_mod, "zvol_create_minor", &rc)) == NULL) {
96681d9761SEric Taylor cmn_err(CE_WARN, "couldn't resolve zvol_create_minor");
97681d9761SEric Taylor return (rc);
98681d9761SEric Taylor }
99681d9761SEric Taylor if ((szn2m = (int(*)(char *, minor_t *))
100681d9761SEric Taylor ddi_modsym(zfs_mod, "zvol_name2minor", &rc)) == NULL) {
101681d9761SEric Taylor cmn_err(CE_WARN, "couldn't resolve zvol_name2minor");
102681d9761SEric Taylor return (rc);
103681d9761SEric Taylor }
104dd9c3b29SJerry Jelinek if (ldi_get_dev(devzvol_lh, &dv))
105dd9c3b29SJerry Jelinek return (-1);
106dd9c3b29SJerry Jelinek devzvol_major = getmajor(dv);
107681d9761SEric Taylor return (0);
108681d9761SEric Taylor }
109681d9761SEric Taylor
110681d9761SEric Taylor void
devzvol_close_zfs()111681d9761SEric Taylor devzvol_close_zfs()
112681d9761SEric Taylor {
113681d9761SEric Taylor szcm = NULL;
114681d9761SEric Taylor szn2m = NULL;
115681d9761SEric Taylor (void) ldi_close(devzvol_lh, FREAD|FWRITE, kcred);
116681d9761SEric Taylor ldi_ident_release(devzvol_li);
117681d9761SEric Taylor if (zfs_mod != NULL) {
118681d9761SEric Taylor (void) ddi_modclose(zfs_mod);
119681d9761SEric Taylor zfs_mod = NULL;
120681d9761SEric Taylor }
121681d9761SEric Taylor }
122681d9761SEric Taylor
123681d9761SEric Taylor int
devzvol_handle_ioctl(int cmd,zfs_cmd_t * zc,size_t * alloc_size)124681d9761SEric Taylor devzvol_handle_ioctl(int cmd, zfs_cmd_t *zc, size_t *alloc_size)
125681d9761SEric Taylor {
126681d9761SEric Taylor uint64_t cookie;
127681d9761SEric Taylor int size = 8000;
128681d9761SEric Taylor int unused;
129681d9761SEric Taylor int rc;
130681d9761SEric Taylor
131681d9761SEric Taylor if (cmd != ZFS_IOC_POOL_CONFIGS)
132681d9761SEric Taylor mutex_enter(&devzvol_mtx);
133ff060bd8SEric Taylor if (!devzvol_isopen) {
134681d9761SEric Taylor if ((rc = devzvol_open_zfs()) == 0) {
135ff060bd8SEric Taylor devzvol_isopen = B_TRUE;
136681d9761SEric Taylor } else {
137681d9761SEric Taylor if (cmd != ZFS_IOC_POOL_CONFIGS)
138681d9761SEric Taylor mutex_exit(&devzvol_mtx);
139681d9761SEric Taylor return (ENXIO);
140681d9761SEric Taylor }
141681d9761SEric Taylor }
142681d9761SEric Taylor cookie = zc->zc_cookie;
143681d9761SEric Taylor again:
144681d9761SEric Taylor zc->zc_nvlist_dst = (uint64_t)(intptr_t)kmem_alloc(size,
145681d9761SEric Taylor KM_SLEEP);
146681d9761SEric Taylor zc->zc_nvlist_dst_size = size;
147681d9761SEric Taylor rc = ldi_ioctl(devzvol_lh, cmd, (intptr_t)zc, FKIOCTL, kcred,
148681d9761SEric Taylor &unused);
149681d9761SEric Taylor if (rc == ENOMEM) {
150681d9761SEric Taylor int newsize;
151681d9761SEric Taylor newsize = zc->zc_nvlist_dst_size;
152681d9761SEric Taylor ASSERT(newsize > size);
153681d9761SEric Taylor kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
154681d9761SEric Taylor size = newsize;
155681d9761SEric Taylor zc->zc_cookie = cookie;
156681d9761SEric Taylor goto again;
157681d9761SEric Taylor }
158681d9761SEric Taylor if (alloc_size == NULL)
159681d9761SEric Taylor kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
160681d9761SEric Taylor else
161681d9761SEric Taylor *alloc_size = size;
162681d9761SEric Taylor if (cmd != ZFS_IOC_POOL_CONFIGS)
163681d9761SEric Taylor mutex_exit(&devzvol_mtx);
164681d9761SEric Taylor return (rc);
165681d9761SEric Taylor }
166681d9761SEric Taylor
167681d9761SEric Taylor /* figures out if the objset exists and returns its type */
168681d9761SEric Taylor int
devzvol_objset_check(char * dsname,dmu_objset_type_t * type)169681d9761SEric Taylor devzvol_objset_check(char *dsname, dmu_objset_type_t *type)
170681d9761SEric Taylor {
171681d9761SEric Taylor boolean_t ispool;
172681d9761SEric Taylor zfs_cmd_t *zc;
173681d9761SEric Taylor int rc;
174681d9761SEric Taylor
175681d9761SEric Taylor zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
176681d9761SEric Taylor (void) strlcpy(zc->zc_name, dsname, MAXPATHLEN);
177681d9761SEric Taylor
178681d9761SEric Taylor ispool = (strchr(dsname, '/') == NULL) ? B_TRUE : B_FALSE;
179681d9761SEric Taylor if (!ispool && sdev_zvol_name2minor(dsname, NULL) == 0) {
180681d9761SEric Taylor sdcmn_err13(("found cached minor node"));
181681d9761SEric Taylor if (type)
182681d9761SEric Taylor *type = DMU_OST_ZVOL;
183681d9761SEric Taylor kmem_free(zc, sizeof (zfs_cmd_t));
184681d9761SEric Taylor return (0);
185681d9761SEric Taylor }
186681d9761SEric Taylor rc = devzvol_handle_ioctl(ispool ? ZFS_IOC_POOL_STATS :
187681d9761SEric Taylor ZFS_IOC_OBJSET_STATS, zc, NULL);
188681d9761SEric Taylor if (type && rc == 0)
189681d9761SEric Taylor *type = (ispool) ? DMU_OST_ZFS :
190681d9761SEric Taylor zc->zc_objset_stats.dds_type;
191681d9761SEric Taylor kmem_free(zc, sizeof (zfs_cmd_t));
192681d9761SEric Taylor return (rc);
193681d9761SEric Taylor }
194681d9761SEric Taylor
195681d9761SEric Taylor /*
196681d9761SEric Taylor * returns what the zfs dataset name should be, given the /dev/zvol
197681d9761SEric Taylor * path and an optional name; otherwise NULL
198681d9761SEric Taylor */
199681d9761SEric Taylor char *
devzvol_make_dsname(const char * path,const char * name)200681d9761SEric Taylor devzvol_make_dsname(const char *path, const char *name)
201681d9761SEric Taylor {
202681d9761SEric Taylor char *dsname;
203681d9761SEric Taylor const char *ptr;
204681d9761SEric Taylor int dslen;
205681d9761SEric Taylor
206681d9761SEric Taylor if (strcmp(path, ZVOL_DIR) == 0)
207681d9761SEric Taylor return (NULL);
208681d9761SEric Taylor if (name && (strcmp(name, ".") == 0 || strcmp(name, "..") == 0))
209681d9761SEric Taylor return (NULL);
210681d9761SEric Taylor ptr = path + strlen(ZVOL_DIR);
211681d9761SEric Taylor if (strncmp(ptr, "/dsk", 4) == 0)
212681d9761SEric Taylor ptr += strlen("/dsk");
213681d9761SEric Taylor else if (strncmp(ptr, "/rdsk", 5) == 0)
214681d9761SEric Taylor ptr += strlen("/rdsk");
215681d9761SEric Taylor else
216681d9761SEric Taylor return (NULL);
217681d9761SEric Taylor if (*ptr == '/')
218681d9761SEric Taylor ptr++;
219681d9761SEric Taylor
220681d9761SEric Taylor dslen = strlen(ptr);
221681d9761SEric Taylor if (dslen)
222681d9761SEric Taylor dslen++; /* plus null */
223681d9761SEric Taylor if (name)
224681d9761SEric Taylor dslen += strlen(name) + 1; /* plus slash */
225681d9761SEric Taylor dsname = kmem_zalloc(dslen, KM_SLEEP);
226681d9761SEric Taylor if (*ptr) {
227681d9761SEric Taylor (void) strlcpy(dsname, ptr, dslen);
228681d9761SEric Taylor if (name)
229681d9761SEric Taylor (void) strlcat(dsname, "/", dslen);
230681d9761SEric Taylor }
231681d9761SEric Taylor if (name)
232681d9761SEric Taylor (void) strlcat(dsname, name, dslen);
233681d9761SEric Taylor return (dsname);
234681d9761SEric Taylor }
235681d9761SEric Taylor
236681d9761SEric Taylor /*
237681d9761SEric Taylor * check if the zvol's sdev_node is still valid, which means make
238681d9761SEric Taylor * sure the zvol is still valid. zvol minors aren't proactively
239681d9761SEric Taylor * destroyed when the zvol is destroyed, so we use a validator to clean
240681d9761SEric Taylor * these up (in other words, when such nodes are encountered during
241681d9761SEric Taylor * subsequent lookup() and readdir() operations) so that only valid
242681d9761SEric Taylor * nodes are returned. The ordering between devname_lookup_func and
243681d9761SEric Taylor * devzvol_validate is a little inefficient in the case of invalid
244681d9761SEric Taylor * or stale nodes because devname_lookup_func calls
245681d9761SEric Taylor * devzvol_create_{dir, link}, then the validator says it's invalid,
246681d9761SEric Taylor * and then the node gets cleaned up.
247681d9761SEric Taylor */
248681d9761SEric Taylor int
devzvol_validate(struct sdev_node * dv)249681d9761SEric Taylor devzvol_validate(struct sdev_node *dv)
250681d9761SEric Taylor {
251681d9761SEric Taylor dmu_objset_type_t do_type;
252681d9761SEric Taylor char *dsname;
253681d9761SEric Taylor char *nm = dv->sdev_name;
254681d9761SEric Taylor int rc;
255681d9761SEric Taylor
256681d9761SEric Taylor sdcmn_err13(("validating ('%s' '%s')", dv->sdev_path, nm));
257681d9761SEric Taylor /*
258681d9761SEric Taylor * validate only READY nodes; if someone is sitting on the
259681d9761SEric Taylor * directory of a dataset that just got destroyed we could
260681d9761SEric Taylor * get a zombie node which we just skip.
261681d9761SEric Taylor */
262681d9761SEric Taylor if (dv->sdev_state != SDEV_READY) {
263681d9761SEric Taylor sdcmn_err13(("skipping '%s'", nm));
264681d9761SEric Taylor return (SDEV_VTOR_SKIP);
265681d9761SEric Taylor }
266681d9761SEric Taylor
267681d9761SEric Taylor if ((strcmp(dv->sdev_path, ZVOL_DIR "/dsk") == 0) ||
268681d9761SEric Taylor (strcmp(dv->sdev_path, ZVOL_DIR "/rdsk") == 0))
269681d9761SEric Taylor return (SDEV_VTOR_VALID);
270681d9761SEric Taylor dsname = devzvol_make_dsname(dv->sdev_path, NULL);
271681d9761SEric Taylor if (dsname == NULL)
272681d9761SEric Taylor return (SDEV_VTOR_INVALID);
273681d9761SEric Taylor
274681d9761SEric Taylor rc = devzvol_objset_check(dsname, &do_type);
275681d9761SEric Taylor sdcmn_err13((" '%s' rc %d", dsname, rc));
276681d9761SEric Taylor if (rc != 0) {
277681d9761SEric Taylor kmem_free(dsname, strlen(dsname) + 1);
278681d9761SEric Taylor return (SDEV_VTOR_INVALID);
279681d9761SEric Taylor }
280681d9761SEric Taylor sdcmn_err13((" v_type %d do_type %d",
281681d9761SEric Taylor SDEVTOV(dv)->v_type, do_type));
282681d9761SEric Taylor if ((SDEVTOV(dv)->v_type == VLNK && do_type != DMU_OST_ZVOL) ||
283dd9c3b29SJerry Jelinek ((SDEVTOV(dv)->v_type == VBLK || SDEVTOV(dv)->v_type == VCHR) &&
284dd9c3b29SJerry Jelinek do_type != DMU_OST_ZVOL) ||
285681d9761SEric Taylor (SDEVTOV(dv)->v_type == VDIR && do_type == DMU_OST_ZVOL)) {
286681d9761SEric Taylor kmem_free(dsname, strlen(dsname) + 1);
287681d9761SEric Taylor return (SDEV_VTOR_STALE);
288681d9761SEric Taylor }
289681d9761SEric Taylor if (SDEVTOV(dv)->v_type == VLNK) {
290681d9761SEric Taylor char *ptr, *link;
291681d9761SEric Taylor long val = 0;
292681d9761SEric Taylor minor_t lminor, ominor;
293681d9761SEric Taylor
294681d9761SEric Taylor rc = sdev_getlink(SDEVTOV(dv), &link);
295681d9761SEric Taylor ASSERT(rc == 0);
296681d9761SEric Taylor
297681d9761SEric Taylor ptr = strrchr(link, ':') + 1;
298681d9761SEric Taylor rc = ddi_strtol(ptr, NULL, 10, &val);
299681d9761SEric Taylor kmem_free(link, strlen(link) + 1);
300681d9761SEric Taylor ASSERT(rc == 0 && val != 0);
301681d9761SEric Taylor lminor = (minor_t)val;
302681d9761SEric Taylor if (sdev_zvol_name2minor(dsname, &ominor) < 0 ||
303681d9761SEric Taylor ominor != lminor) {
304681d9761SEric Taylor kmem_free(dsname, strlen(dsname) + 1);
305681d9761SEric Taylor return (SDEV_VTOR_STALE);
306681d9761SEric Taylor }
307681d9761SEric Taylor }
308681d9761SEric Taylor kmem_free(dsname, strlen(dsname) + 1);
309681d9761SEric Taylor return (SDEV_VTOR_VALID);
310681d9761SEric Taylor }
311681d9761SEric Taylor
312681d9761SEric Taylor /*
313681d9761SEric Taylor * creates directories as needed in response to a readdir
314681d9761SEric Taylor */
315681d9761SEric Taylor void
devzvol_create_pool_dirs(struct vnode * dvp)316681d9761SEric Taylor devzvol_create_pool_dirs(struct vnode *dvp)
317681d9761SEric Taylor {
318681d9761SEric Taylor zfs_cmd_t *zc;
319681d9761SEric Taylor nvlist_t *nv = NULL;
320681d9761SEric Taylor nvpair_t *elem = NULL;
321681d9761SEric Taylor size_t size;
322681d9761SEric Taylor int pools = 0;
323681d9761SEric Taylor int rc;
324681d9761SEric Taylor
325681d9761SEric Taylor sdcmn_err13(("devzvol_create_pool_dirs"));
326681d9761SEric Taylor zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
327681d9761SEric Taylor mutex_enter(&devzvol_mtx);
328681d9761SEric Taylor zc->zc_cookie = devzvol_gen;
329681d9761SEric Taylor
330681d9761SEric Taylor rc = devzvol_handle_ioctl(ZFS_IOC_POOL_CONFIGS, zc, &size);
331681d9761SEric Taylor switch (rc) {
332681d9761SEric Taylor case 0:
333681d9761SEric Taylor /* new generation */
334681d9761SEric Taylor ASSERT(devzvol_gen != zc->zc_cookie);
335681d9761SEric Taylor devzvol_gen = zc->zc_cookie;
336681d9761SEric Taylor if (devzvol_zclist)
337681d9761SEric Taylor kmem_free((void *)(uintptr_t)devzvol_zclist,
338681d9761SEric Taylor devzvol_zclist_size);
339681d9761SEric Taylor devzvol_zclist = zc->zc_nvlist_dst;
340681d9761SEric Taylor devzvol_zclist_size = size;
341681d9761SEric Taylor break;
342681d9761SEric Taylor case EEXIST:
343681d9761SEric Taylor /*
344681d9761SEric Taylor * no change in the configuration; still need
345681d9761SEric Taylor * to do lookups in case we did a lookup in
346681d9761SEric Taylor * zvol/rdsk but not zvol/dsk (or vice versa)
347681d9761SEric Taylor */
348681d9761SEric Taylor kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst,
349681d9761SEric Taylor size);
350681d9761SEric Taylor break;
351681d9761SEric Taylor default:
352681d9761SEric Taylor kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst,
353681d9761SEric Taylor size);
354681d9761SEric Taylor goto out;
355681d9761SEric Taylor }
356681d9761SEric Taylor rc = nvlist_unpack((char *)(uintptr_t)devzvol_zclist,
357681d9761SEric Taylor devzvol_zclist_size, &nv, 0);
358681d9761SEric Taylor if (rc) {
359681d9761SEric Taylor ASSERT(rc == 0);
360681d9761SEric Taylor kmem_free((void *)(uintptr_t)devzvol_zclist,
361681d9761SEric Taylor devzvol_zclist_size);
362681d9761SEric Taylor devzvol_gen = 0;
363681d9761SEric Taylor devzvol_zclist = NULL;
364681d9761SEric Taylor devzvol_zclist_size = 0;
365681d9761SEric Taylor goto out;
366681d9761SEric Taylor }
367681d9761SEric Taylor mutex_exit(&devzvol_mtx);
368681d9761SEric Taylor while ((elem = nvlist_next_nvpair(nv, elem)) != NULL) {
369681d9761SEric Taylor struct vnode *vp;
370681d9761SEric Taylor ASSERT(dvp->v_count > 0);
371681d9761SEric Taylor rc = VOP_LOOKUP(dvp, nvpair_name(elem), &vp, NULL, 0,
372681d9761SEric Taylor NULL, kcred, NULL, 0, NULL);
373681d9761SEric Taylor /* should either work, or not be visible from a zone */
374681d9761SEric Taylor ASSERT(rc == 0 || rc == ENOENT);
375681d9761SEric Taylor if (rc == 0)
376681d9761SEric Taylor VN_RELE(vp);
377681d9761SEric Taylor pools++;
378681d9761SEric Taylor }
379681d9761SEric Taylor nvlist_free(nv);
380681d9761SEric Taylor mutex_enter(&devzvol_mtx);
381ff060bd8SEric Taylor if (devzvol_isopen && pools == 0) {
382681d9761SEric Taylor /* clean up so zfs can be unloaded */
383681d9761SEric Taylor devzvol_close_zfs();
384ff060bd8SEric Taylor devzvol_isopen = B_FALSE;
385681d9761SEric Taylor }
386681d9761SEric Taylor out:
387681d9761SEric Taylor mutex_exit(&devzvol_mtx);
388681d9761SEric Taylor kmem_free(zc, sizeof (zfs_cmd_t));
389681d9761SEric Taylor }
390681d9761SEric Taylor
391681d9761SEric Taylor /*ARGSUSED3*/
392681d9761SEric Taylor static int
devzvol_create_dir(struct sdev_node * ddv,char * nm,void ** arg,cred_t * cred,void * whatever,char * whichever)393681d9761SEric Taylor devzvol_create_dir(struct sdev_node *ddv, char *nm, void **arg,
394681d9761SEric Taylor cred_t *cred, void *whatever, char *whichever)
395681d9761SEric Taylor {
396681d9761SEric Taylor timestruc_t now;
397681d9761SEric Taylor struct vattr *vap = (struct vattr *)arg;
398681d9761SEric Taylor
399681d9761SEric Taylor sdcmn_err13(("create_dir (%s) (%s) '%s'", ddv->sdev_name,
400681d9761SEric Taylor ddv->sdev_path, nm));
401681d9761SEric Taylor ASSERT(strncmp(ddv->sdev_path, ZVOL_DIR,
402681d9761SEric Taylor strlen(ZVOL_DIR)) == 0);
403681d9761SEric Taylor *vap = *sdev_getdefault_attr(VDIR);
404681d9761SEric Taylor gethrestime(&now);
405681d9761SEric Taylor vap->va_atime = now;
406681d9761SEric Taylor vap->va_mtime = now;
407681d9761SEric Taylor vap->va_ctime = now;
408681d9761SEric Taylor return (0);
409681d9761SEric Taylor }
410681d9761SEric Taylor
411681d9761SEric Taylor /*ARGSUSED3*/
412681d9761SEric Taylor static int
devzvol_create_link(struct sdev_node * ddv,char * nm,void ** arg,cred_t * cred,void * whatever,char * whichever)413681d9761SEric Taylor devzvol_create_link(struct sdev_node *ddv, char *nm,
414681d9761SEric Taylor void **arg, cred_t *cred, void *whatever, char *whichever)
415681d9761SEric Taylor {
416681d9761SEric Taylor minor_t minor;
417681d9761SEric Taylor char *pathname = (char *)*arg;
418681d9761SEric Taylor int rc;
419681d9761SEric Taylor char *dsname;
420681d9761SEric Taylor char *x;
421681d9761SEric Taylor char str[MAXNAMELEN];
422681d9761SEric Taylor sdcmn_err13(("create_link (%s) (%s) '%s'", ddv->sdev_name,
423681d9761SEric Taylor ddv->sdev_path, nm));
424681d9761SEric Taylor dsname = devzvol_make_dsname(ddv->sdev_path, nm);
425681d9761SEric Taylor rc = sdev_zvol_create_minor(dsname);
426681d9761SEric Taylor if ((rc != 0 && rc != EEXIST && rc != EBUSY) ||
427681d9761SEric Taylor sdev_zvol_name2minor(dsname, &minor)) {
428681d9761SEric Taylor sdcmn_err13(("devzvol_create_link %d", rc));
429681d9761SEric Taylor kmem_free(dsname, strlen(dsname) + 1);
430681d9761SEric Taylor return (-1);
431681d9761SEric Taylor }
432681d9761SEric Taylor kmem_free(dsname, strlen(dsname) + 1);
433681d9761SEric Taylor
434681d9761SEric Taylor /*
435681d9761SEric Taylor * This is a valid zvol; create a symlink that points to the
436681d9761SEric Taylor * minor which was created under /devices/pseudo/zfs@0
437681d9761SEric Taylor */
438681d9761SEric Taylor *pathname = '\0';
439681d9761SEric Taylor for (x = ddv->sdev_path; x = strchr(x, '/'); x++)
440681d9761SEric Taylor (void) strcat(pathname, "../");
441681d9761SEric Taylor (void) snprintf(str, sizeof (str), ZVOL_PSEUDO_DEV "%u", minor);
442681d9761SEric Taylor (void) strncat(pathname, str, MAXPATHLEN);
443681d9761SEric Taylor if (strncmp(ddv->sdev_path, ZVOL_FULL_RDEV_DIR,
444681d9761SEric Taylor strlen(ZVOL_FULL_RDEV_DIR)) == 0)
445681d9761SEric Taylor (void) strcat(pathname, ",raw");
446681d9761SEric Taylor return (0);
447681d9761SEric Taylor }
448681d9761SEric Taylor
449681d9761SEric Taylor /* Clean zvol sdev_nodes that are no longer valid. */
450681d9761SEric Taylor static void
devzvol_prunedir(struct sdev_node * ddv)451681d9761SEric Taylor devzvol_prunedir(struct sdev_node *ddv)
452681d9761SEric Taylor {
453681d9761SEric Taylor struct sdev_node *dv;
454681d9761SEric Taylor
455681d9761SEric Taylor ASSERT(RW_READ_HELD(&ddv->sdev_contents));
456681d9761SEric Taylor
457681d9761SEric Taylor sdcmn_err13(("prunedir '%s'", ddv->sdev_name));
458681d9761SEric Taylor ASSERT(strncmp(ddv->sdev_path, ZVOL_DIR, strlen(ZVOL_DIR)) == 0);
459681d9761SEric Taylor if (rw_tryupgrade(&ddv->sdev_contents) == 0) {
460681d9761SEric Taylor rw_exit(&ddv->sdev_contents);
461681d9761SEric Taylor rw_enter(&ddv->sdev_contents, RW_WRITER);
462681d9761SEric Taylor }
463681d9761SEric Taylor
464681d9761SEric Taylor dv = SDEV_FIRST_ENTRY(ddv);
465681d9761SEric Taylor while (dv) {
466681d9761SEric Taylor sdcmn_err13(("sdev_name '%s'", dv->sdev_name));
467681d9761SEric Taylor
468681d9761SEric Taylor switch (devzvol_validate(dv)) {
469681d9761SEric Taylor case SDEV_VTOR_VALID:
470681d9761SEric Taylor case SDEV_VTOR_SKIP:
471681d9761SEric Taylor dv = SDEV_NEXT_ENTRY(ddv, dv);
472681d9761SEric Taylor continue;
473681d9761SEric Taylor case SDEV_VTOR_INVALID:
474681d9761SEric Taylor sdcmn_err7(("prunedir: destroy invalid "
475681d9761SEric Taylor "node: %s\n", dv->sdev_name));
476681d9761SEric Taylor break;
477681d9761SEric Taylor }
478681d9761SEric Taylor
479681d9761SEric Taylor if ((SDEVTOV(dv)->v_type == VDIR) &&
480681d9761SEric Taylor (sdev_cleandir(dv, NULL, 0) != 0)) {
481681d9761SEric Taylor dv = SDEV_NEXT_ENTRY(ddv, dv);
482681d9761SEric Taylor continue;
483681d9761SEric Taylor }
484681d9761SEric Taylor SDEV_HOLD(dv);
485681d9761SEric Taylor /* remove the cache node */
4869e5aa9d8SRobert Mustacchi sdev_cache_update(ddv, &dv, dv->sdev_name,
4879e5aa9d8SRobert Mustacchi SDEV_CACHE_DELETE);
4889e5aa9d8SRobert Mustacchi SDEV_RELE(dv);
489681d9761SEric Taylor dv = SDEV_FIRST_ENTRY(ddv);
490681d9761SEric Taylor }
491681d9761SEric Taylor rw_downgrade(&ddv->sdev_contents);
492681d9761SEric Taylor }
493681d9761SEric Taylor
494dd9c3b29SJerry Jelinek /*
495dd9c3b29SJerry Jelinek * This function is used to create a dir or dev inside a zone's /dev when the
496dd9c3b29SJerry Jelinek * zone has a zvol that is dynamically created within the zone (i.e. inside
497dd9c3b29SJerry Jelinek * of a delegated dataset. Since there is no /devices tree within a zone,
498dd9c3b29SJerry Jelinek * we create the chr/blk devices directly inside the zone's /dev instead of
499dd9c3b29SJerry Jelinek * making symlinks.
500dd9c3b29SJerry Jelinek */
501dd9c3b29SJerry Jelinek static int
devzvol_mk_ngz_node(struct sdev_node * parent,char * nm)502dd9c3b29SJerry Jelinek devzvol_mk_ngz_node(struct sdev_node *parent, char *nm)
503dd9c3b29SJerry Jelinek {
504dd9c3b29SJerry Jelinek struct vattr vattr;
505dd9c3b29SJerry Jelinek timestruc_t now;
506dd9c3b29SJerry Jelinek enum vtype expected_type = VDIR;
507dd9c3b29SJerry Jelinek dmu_objset_type_t do_type;
508dd9c3b29SJerry Jelinek struct sdev_node *dv = NULL;
509dd9c3b29SJerry Jelinek int res;
510dd9c3b29SJerry Jelinek char *dsname;
511dd9c3b29SJerry Jelinek
512dd9c3b29SJerry Jelinek bzero(&vattr, sizeof (vattr));
513dd9c3b29SJerry Jelinek gethrestime(&now);
514dd9c3b29SJerry Jelinek vattr.va_mask = AT_TYPE|AT_MODE|AT_UID|AT_GID;
515dd9c3b29SJerry Jelinek vattr.va_uid = SDEV_UID_DEFAULT;
516dd9c3b29SJerry Jelinek vattr.va_gid = SDEV_GID_DEFAULT;
517dd9c3b29SJerry Jelinek vattr.va_type = VNON;
518dd9c3b29SJerry Jelinek vattr.va_atime = now;
519dd9c3b29SJerry Jelinek vattr.va_mtime = now;
520dd9c3b29SJerry Jelinek vattr.va_ctime = now;
521dd9c3b29SJerry Jelinek
522dd9c3b29SJerry Jelinek if ((dsname = devzvol_make_dsname(parent->sdev_path, nm)) == NULL)
523dd9c3b29SJerry Jelinek return (ENOENT);
524dd9c3b29SJerry Jelinek
525dd9c3b29SJerry Jelinek if (devzvol_objset_check(dsname, &do_type) != 0) {
526dd9c3b29SJerry Jelinek kmem_free(dsname, strlen(dsname) + 1);
527dd9c3b29SJerry Jelinek return (ENOENT);
528dd9c3b29SJerry Jelinek }
529dd9c3b29SJerry Jelinek if (do_type == DMU_OST_ZVOL)
530dd9c3b29SJerry Jelinek expected_type = VBLK;
531dd9c3b29SJerry Jelinek
532dd9c3b29SJerry Jelinek if (expected_type == VDIR) {
533dd9c3b29SJerry Jelinek vattr.va_type = VDIR;
534dd9c3b29SJerry Jelinek vattr.va_mode = SDEV_DIRMODE_DEFAULT;
535dd9c3b29SJerry Jelinek } else {
536dd9c3b29SJerry Jelinek minor_t minor;
537dd9c3b29SJerry Jelinek dev_t devnum;
538dd9c3b29SJerry Jelinek int rc;
539dd9c3b29SJerry Jelinek
540dd9c3b29SJerry Jelinek rc = sdev_zvol_create_minor(dsname);
541dd9c3b29SJerry Jelinek if ((rc != 0 && rc != EEXIST && rc != EBUSY) ||
542dd9c3b29SJerry Jelinek sdev_zvol_name2minor(dsname, &minor)) {
543dd9c3b29SJerry Jelinek kmem_free(dsname, strlen(dsname) + 1);
544dd9c3b29SJerry Jelinek return (ENOENT);
545dd9c3b29SJerry Jelinek }
546dd9c3b29SJerry Jelinek
547dd9c3b29SJerry Jelinek devnum = makedevice(devzvol_major, minor);
548dd9c3b29SJerry Jelinek vattr.va_rdev = devnum;
549dd9c3b29SJerry Jelinek
550dd9c3b29SJerry Jelinek if (strstr(parent->sdev_path, "/rdsk/") != NULL)
551dd9c3b29SJerry Jelinek vattr.va_type = VCHR;
552dd9c3b29SJerry Jelinek else
553dd9c3b29SJerry Jelinek vattr.va_type = VBLK;
554dd9c3b29SJerry Jelinek vattr.va_mode = SDEV_DEVMODE_DEFAULT;
555dd9c3b29SJerry Jelinek }
556dd9c3b29SJerry Jelinek kmem_free(dsname, strlen(dsname) + 1);
557dd9c3b29SJerry Jelinek
558dd9c3b29SJerry Jelinek rw_enter(&parent->sdev_contents, RW_WRITER);
559dd9c3b29SJerry Jelinek
560dd9c3b29SJerry Jelinek res = sdev_mknode(parent, nm, &dv, &vattr,
561dd9c3b29SJerry Jelinek NULL, NULL, kcred, SDEV_READY);
562dd9c3b29SJerry Jelinek rw_exit(&parent->sdev_contents);
563dd9c3b29SJerry Jelinek if (res != 0)
564dd9c3b29SJerry Jelinek return (ENOENT);
565dd9c3b29SJerry Jelinek
566dd9c3b29SJerry Jelinek SDEV_RELE(dv);
567dd9c3b29SJerry Jelinek return (0);
568dd9c3b29SJerry Jelinek }
569dd9c3b29SJerry Jelinek
570681d9761SEric Taylor /*ARGSUSED*/
571681d9761SEric Taylor 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)572681d9761SEric Taylor devzvol_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
573681d9761SEric Taylor struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
574681d9761SEric Taylor caller_context_t *ct, int *direntflags, pathname_t *realpnp)
575681d9761SEric Taylor {
576681d9761SEric Taylor enum vtype expected_type = VDIR;
577681d9761SEric Taylor struct sdev_node *parent = VTOSDEV(dvp);
578681d9761SEric Taylor char *dsname;
579681d9761SEric Taylor dmu_objset_type_t do_type;
580681d9761SEric Taylor int error;
581681d9761SEric Taylor
582681d9761SEric Taylor sdcmn_err13(("devzvol_lookup '%s' '%s'", parent->sdev_path, nm));
583681d9761SEric Taylor *vpp = NULL;
584681d9761SEric Taylor /* execute access is required to search the directory */
585681d9761SEric Taylor if ((error = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0)
586681d9761SEric Taylor return (error);
587681d9761SEric Taylor
588681d9761SEric Taylor rw_enter(&parent->sdev_contents, RW_READER);
589dd9c3b29SJerry Jelinek if (SDEV_IS_GLOBAL(parent)) {
590dd9c3b29SJerry Jelinek /*
591dd9c3b29SJerry Jelinek * During iter_datasets, don't create GZ dev when running in
592dd9c3b29SJerry Jelinek * NGZ. We can't return ENOENT here since that could
593dd9c3b29SJerry Jelinek * incorrectly trigger the creation of the dev from the
594dd9c3b29SJerry Jelinek * recursive call through prof_filldir during iter_datasets.
595dd9c3b29SJerry Jelinek */
596dd9c3b29SJerry Jelinek if (getzoneid() != GLOBAL_ZONEID) {
597681d9761SEric Taylor rw_exit(&parent->sdev_contents);
598dd9c3b29SJerry Jelinek return (EPERM);
599dd9c3b29SJerry Jelinek }
600dd9c3b29SJerry Jelinek } else {
601dd9c3b29SJerry Jelinek int res;
602dd9c3b29SJerry Jelinek
603dd9c3b29SJerry Jelinek rw_exit(&parent->sdev_contents);
604dd9c3b29SJerry Jelinek
605dd9c3b29SJerry Jelinek /*
606dd9c3b29SJerry Jelinek * If we're in the global zone and reach down into a non-global
607dd9c3b29SJerry Jelinek * zone's /dev/zvol then this action could trigger the creation
608dd9c3b29SJerry Jelinek * of all of the zvol devices for every zone into the non-global
609dd9c3b29SJerry Jelinek * zone's /dev tree. This could be a big security hole. To
610dd9c3b29SJerry Jelinek * prevent this, disallow the global zone from looking inside
611dd9c3b29SJerry Jelinek * a non-global zones /dev/zvol. This behavior is similar to
612dd9c3b29SJerry Jelinek * delegated datasets, which cannot be used by the global zone.
613dd9c3b29SJerry Jelinek */
614dd9c3b29SJerry Jelinek if (getzoneid() == GLOBAL_ZONEID)
615dd9c3b29SJerry Jelinek return (EPERM);
616dd9c3b29SJerry Jelinek
617dd9c3b29SJerry Jelinek res = prof_lookup(dvp, nm, vpp, cred);
618dd9c3b29SJerry Jelinek
619dd9c3b29SJerry Jelinek /*
620dd9c3b29SJerry Jelinek * We won't find a zvol that was dynamically created inside
621dd9c3b29SJerry Jelinek * a NGZ, within a delegated dataset, in the zone's dev profile
622dd9c3b29SJerry Jelinek * but prof_lookup will also find it via sdev_cache_lookup.
623dd9c3b29SJerry Jelinek */
624dd9c3b29SJerry Jelinek if (res == ENOENT) {
625dd9c3b29SJerry Jelinek /*
626dd9c3b29SJerry Jelinek * We have to create the sdev node for the dymamically
627dd9c3b29SJerry Jelinek * created zvol.
628dd9c3b29SJerry Jelinek */
629dd9c3b29SJerry Jelinek if (devzvol_mk_ngz_node(parent, nm) != 0)
630dd9c3b29SJerry Jelinek return (ENOENT);
631dd9c3b29SJerry Jelinek res = prof_lookup(dvp, nm, vpp, cred);
632dd9c3b29SJerry Jelinek }
633dd9c3b29SJerry Jelinek
634dd9c3b29SJerry Jelinek return (res);
635681d9761SEric Taylor }
636681d9761SEric Taylor
637681d9761SEric Taylor dsname = devzvol_make_dsname(parent->sdev_path, nm);
638681d9761SEric Taylor rw_exit(&parent->sdev_contents);
639681d9761SEric Taylor sdcmn_err13(("rvp dsname %s", dsname ? dsname : "(null)"));
640681d9761SEric Taylor if (dsname) {
641681d9761SEric Taylor error = devzvol_objset_check(dsname, &do_type);
642681d9761SEric Taylor if (error != 0) {
643681d9761SEric Taylor error = ENOENT;
644681d9761SEric Taylor goto out;
645681d9761SEric Taylor }
646681d9761SEric Taylor if (do_type == DMU_OST_ZVOL)
647681d9761SEric Taylor expected_type = VLNK;
648681d9761SEric Taylor }
649681d9761SEric Taylor /*
650681d9761SEric Taylor * the callbacks expect:
651681d9761SEric Taylor *
652681d9761SEric Taylor * parent->sdev_path nm
653681d9761SEric Taylor * /dev/zvol {r}dsk
654681d9761SEric Taylor * /dev/zvol/{r}dsk <pool name>
655681d9761SEric Taylor * /dev/zvol/{r}dsk/<dataset name> <last ds component>
656681d9761SEric Taylor *
657681d9761SEric Taylor * sdev_name is always last path component of sdev_path
658681d9761SEric Taylor */
659681d9761SEric Taylor if (expected_type == VDIR) {
660681d9761SEric Taylor error = devname_lookup_func(parent, nm, vpp, cred,
661681d9761SEric Taylor devzvol_create_dir, SDEV_VATTR);
662681d9761SEric Taylor } else {
663681d9761SEric Taylor error = devname_lookup_func(parent, nm, vpp, cred,
664681d9761SEric Taylor devzvol_create_link, SDEV_VLINK);
665681d9761SEric Taylor }
666681d9761SEric Taylor sdcmn_err13(("devzvol_lookup %d %d", expected_type, error));
667681d9761SEric Taylor ASSERT(error || ((*vpp)->v_type == expected_type));
668681d9761SEric Taylor out:
669681d9761SEric Taylor if (dsname)
670681d9761SEric Taylor kmem_free(dsname, strlen(dsname) + 1);
671681d9761SEric Taylor sdcmn_err13(("devzvol_lookup %d", error));
672681d9761SEric Taylor return (error);
673681d9761SEric Taylor }
674681d9761SEric Taylor
675681d9761SEric Taylor /*
676681d9761SEric Taylor * We allow create to find existing nodes
677681d9761SEric Taylor * - if the node doesn't exist - EROFS
678681d9761SEric Taylor * - creating an existing dir read-only succeeds, otherwise EISDIR
679681d9761SEric Taylor * - exclusive creates fail - EEXIST
680681d9761SEric Taylor */
681681d9761SEric Taylor /*ARGSUSED2*/
682681d9761SEric Taylor 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)683681d9761SEric Taylor devzvol_create(struct vnode *dvp, char *nm, struct vattr *vap, vcexcl_t excl,
684681d9761SEric Taylor int mode, struct vnode **vpp, struct cred *cred, int flag,
685681d9761SEric Taylor caller_context_t *ct, vsecattr_t *vsecp)
686681d9761SEric Taylor {
687681d9761SEric Taylor int error;
688681d9761SEric Taylor struct vnode *vp;
689681d9761SEric Taylor
690681d9761SEric Taylor *vpp = NULL;
691681d9761SEric Taylor
692681d9761SEric Taylor error = devzvol_lookup(dvp, nm, &vp, NULL, 0, NULL, cred, ct, NULL,
693681d9761SEric Taylor NULL);
694681d9761SEric Taylor if (error == 0) {
695681d9761SEric Taylor if (excl == EXCL)
696681d9761SEric Taylor error = EEXIST;
697681d9761SEric Taylor else if (vp->v_type == VDIR && (mode & VWRITE))
698681d9761SEric Taylor error = EISDIR;
699681d9761SEric Taylor else
700681d9761SEric Taylor error = VOP_ACCESS(vp, mode, 0, cred, ct);
701681d9761SEric Taylor
702681d9761SEric Taylor if (error) {
703681d9761SEric Taylor VN_RELE(vp);
704681d9761SEric Taylor } else
705681d9761SEric Taylor *vpp = vp;
706681d9761SEric Taylor } else if (error == ENOENT) {
707681d9761SEric Taylor error = EROFS;
708681d9761SEric Taylor }
709681d9761SEric Taylor
710681d9761SEric Taylor return (error);
711681d9761SEric Taylor }
712681d9761SEric Taylor
713681d9761SEric Taylor void sdev_iter_snapshots(struct vnode *dvp, char *name);
714681d9761SEric Taylor
715681d9761SEric Taylor void
sdev_iter_datasets(struct vnode * dvp,int arg,char * name)716681d9761SEric Taylor sdev_iter_datasets(struct vnode *dvp, int arg, char *name)
717681d9761SEric Taylor {
718681d9761SEric Taylor zfs_cmd_t *zc;
719681d9761SEric Taylor int rc;
720681d9761SEric Taylor
721681d9761SEric Taylor sdcmn_err13(("iter name is '%s' (arg %x)", name, arg));
722681d9761SEric Taylor zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
723681d9761SEric Taylor (void) strcpy(zc->zc_name, name);
724681d9761SEric Taylor
725681d9761SEric Taylor while ((rc = devzvol_handle_ioctl(arg, zc, B_FALSE)) == 0) {
726681d9761SEric Taylor struct vnode *vpp;
727681d9761SEric Taylor char *ptr;
728681d9761SEric Taylor
729681d9761SEric Taylor sdcmn_err13((" name %s", zc->zc_name));
730681d9761SEric Taylor if (strchr(zc->zc_name, '$') || strchr(zc->zc_name, '%'))
731681d9761SEric Taylor goto skip;
732681d9761SEric Taylor ptr = strrchr(zc->zc_name, '/') + 1;
733681d9761SEric Taylor rc = devzvol_lookup(dvp, ptr, &vpp, NULL, 0, NULL,
734681d9761SEric Taylor kcred, NULL, NULL, NULL);
735681d9761SEric Taylor if (rc == 0) {
736681d9761SEric Taylor VN_RELE(vpp);
737681d9761SEric Taylor } else if (rc == ENOENT) {
738681d9761SEric Taylor goto skip;
739681d9761SEric Taylor } else {
740dd9c3b29SJerry Jelinek /*
741dd9c3b29SJerry Jelinek * EBUSY == problem with zvols's dmu holds?
742dd9c3b29SJerry Jelinek * EPERM when in a NGZ and traversing up and out.
743dd9c3b29SJerry Jelinek */
744681d9761SEric Taylor goto skip;
745681d9761SEric Taylor }
746681d9761SEric Taylor if (arg == ZFS_IOC_DATASET_LIST_NEXT &&
747681d9761SEric Taylor zc->zc_objset_stats.dds_type != DMU_OST_ZFS)
748681d9761SEric Taylor sdev_iter_snapshots(dvp, zc->zc_name);
749681d9761SEric Taylor skip:
750681d9761SEric Taylor (void) strcpy(zc->zc_name, name);
751681d9761SEric Taylor }
752681d9761SEric Taylor kmem_free(zc, sizeof (zfs_cmd_t));
753681d9761SEric Taylor }
754681d9761SEric Taylor
755681d9761SEric Taylor void
sdev_iter_snapshots(struct vnode * dvp,char * name)756681d9761SEric Taylor sdev_iter_snapshots(struct vnode *dvp, char *name)
757681d9761SEric Taylor {
758681d9761SEric Taylor sdev_iter_datasets(dvp, ZFS_IOC_SNAPSHOT_LIST_NEXT, name);
759681d9761SEric Taylor }
760681d9761SEric Taylor
761681d9761SEric Taylor /*ARGSUSED4*/
762681d9761SEric Taylor static int
devzvol_readdir(struct vnode * dvp,struct uio * uiop,struct cred * cred,int * eofp,caller_context_t * ct_unused,int flags_unused)763681d9761SEric Taylor devzvol_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
764681d9761SEric Taylor int *eofp, caller_context_t *ct_unused, int flags_unused)
765681d9761SEric Taylor {
766681d9761SEric Taylor struct sdev_node *sdvp = VTOSDEV(dvp);
767681d9761SEric Taylor char *ptr;
768681d9761SEric Taylor
769681d9761SEric Taylor sdcmn_err13(("zv readdir of '%s' %s'", sdvp->sdev_path,
770681d9761SEric Taylor sdvp->sdev_name));
771681d9761SEric Taylor
772681d9761SEric Taylor if (strcmp(sdvp->sdev_path, ZVOL_DIR) == 0) {
773681d9761SEric Taylor struct vnode *vp;
774681d9761SEric Taylor
775681d9761SEric Taylor rw_exit(&sdvp->sdev_contents);
776681d9761SEric Taylor (void) devname_lookup_func(sdvp, "dsk", &vp, cred,
777681d9761SEric Taylor devzvol_create_dir, SDEV_VATTR);
778681d9761SEric Taylor VN_RELE(vp);
779681d9761SEric Taylor (void) devname_lookup_func(sdvp, "rdsk", &vp, cred,
780681d9761SEric Taylor devzvol_create_dir, SDEV_VATTR);
781681d9761SEric Taylor VN_RELE(vp);
782681d9761SEric Taylor rw_enter(&sdvp->sdev_contents, RW_READER);
783681d9761SEric Taylor return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
784681d9761SEric Taylor }
785681d9761SEric Taylor if (uiop->uio_offset == 0)
786681d9761SEric Taylor devzvol_prunedir(sdvp);
787681d9761SEric Taylor ptr = sdvp->sdev_path + strlen(ZVOL_DIR);
788681d9761SEric Taylor if ((strcmp(ptr, "/dsk") == 0) || (strcmp(ptr, "/rdsk") == 0)) {
789681d9761SEric Taylor rw_exit(&sdvp->sdev_contents);
790681d9761SEric Taylor devzvol_create_pool_dirs(dvp);
791681d9761SEric Taylor rw_enter(&sdvp->sdev_contents, RW_READER);
792681d9761SEric Taylor return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
793681d9761SEric Taylor }
794681d9761SEric Taylor
795*d6568684SRobert Mustacchi ptr = strchr(ptr + 1, '/');
796*d6568684SRobert Mustacchi if (ptr == NULL)
797*d6568684SRobert Mustacchi return (ENOENT);
798*d6568684SRobert Mustacchi ptr++;
799681d9761SEric Taylor rw_exit(&sdvp->sdev_contents);
800681d9761SEric Taylor sdev_iter_datasets(dvp, ZFS_IOC_DATASET_LIST_NEXT, ptr);
801681d9761SEric Taylor rw_enter(&sdvp->sdev_contents, RW_READER);
802681d9761SEric Taylor return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
803681d9761SEric Taylor }
804681d9761SEric Taylor
805681d9761SEric Taylor const fs_operation_def_t devzvol_vnodeops_tbl[] = {
806681d9761SEric Taylor VOPNAME_READDIR, { .vop_readdir = devzvol_readdir },
807681d9761SEric Taylor VOPNAME_LOOKUP, { .vop_lookup = devzvol_lookup },
808681d9761SEric Taylor VOPNAME_CREATE, { .vop_create = devzvol_create },
809681d9761SEric Taylor VOPNAME_RENAME, { .error = fs_nosys },
810681d9761SEric Taylor VOPNAME_MKDIR, { .error = fs_nosys },
811681d9761SEric Taylor VOPNAME_RMDIR, { .error = fs_nosys },
812681d9761SEric Taylor VOPNAME_REMOVE, { .error = fs_nosys },
813681d9761SEric Taylor VOPNAME_SYMLINK, { .error = fs_nosys },
814681d9761SEric Taylor NULL, NULL
815681d9761SEric Taylor };
816