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