diff --git a/lib/libzfs/include/libzfs_impl.h b/lib/libzfs/include/libzfs_impl.h index 3f540e37d2..9625855429 100644 --- a/lib/libzfs/include/libzfs_impl.h +++ b/lib/libzfs/include/libzfs_impl.h @@ -190,6 +190,8 @@ zfs_handle_t *make_dataset_handle(libzfs_handle_t *, const char *); int zpool_open_silent(libzfs_handle_t *, const char *, zpool_handle_t **); +int zvol_create_link(libzfs_handle_t *, const char *); +int zvol_remove_link(libzfs_handle_t *, const char *); boolean_t zpool_name_valid(libzfs_handle_t *, boolean_t, const char *); void namespace_clear(libzfs_handle_t *); diff --git a/lib/libzfs/libzfs_dataset.c b/lib/libzfs/libzfs_dataset.c index da05277b3a..55b646afa2 100644 --- a/lib/libzfs/libzfs_dataset.c +++ b/lib/libzfs/libzfs_dataset.c @@ -58,6 +58,7 @@ #include "libzfs_impl.h" #include "zfs_deleg.h" +static int zvol_create_link_common(libzfs_handle_t *, const char *, int); static int userquota_propname_decode(const char *propname, boolean_t zoned, zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp); @@ -2868,6 +2869,18 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, /* create the dataset */ ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc); + if (ret == 0 && type == ZFS_TYPE_VOLUME) { + ret = zvol_create_link(hdl, path); + if (ret) { + (void) zfs_standard_error(hdl, errno, + dgettext(TEXT_DOMAIN, + "Volume successfully created, but device links " + "were not created")); + zcmd_free_nvlists(&zc); + return (-1); + } + } + zcmd_free_nvlists(&zc); /* check for failure */ @@ -2930,6 +2943,9 @@ zfs_destroy(zfs_handle_t *zhp, boolean_t defer) (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); if (ZFS_IS_VOLUME(zhp)) { + if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) + return (-1); + zc.zc_objset_type = DMU_OST_ZVOL; } else { zc.zc_objset_type = DMU_OST_ZFS; @@ -2972,9 +2988,17 @@ zfs_check_snap_cb(zfs_handle_t *zhp, void *arg) zfs_close(szhp); } + if (zhp->zfs_type == ZFS_TYPE_VOLUME) { + (void) zvol_remove_link(zhp->zfs_hdl, name); + /* + * NB: this is simply a best-effort. We don't want to + * return an error, because then we wouldn't visit all + * the volumes. + */ + } + dd->closezhp = B_TRUE; - if (!dd->gotone) - rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg); + rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg); if (closezhp) zfs_close(zhp); return (rv); @@ -3109,11 +3133,70 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props) return (zfs_standard_error(zhp->zfs_hdl, errno, errbuf)); } + } else if (ZFS_IS_VOLUME(zhp)) { + ret = zvol_create_link(zhp->zfs_hdl, target); } return (ret); } +typedef struct promote_data { + char cb_mountpoint[MAXPATHLEN]; + const char *cb_target; + const char *cb_errbuf; + uint64_t cb_pivot_txg; +} promote_data_t; + +static int +promote_snap_cb(zfs_handle_t *zhp, void *data) +{ + promote_data_t *pd = data; + zfs_handle_t *szhp; + char snapname[MAXPATHLEN]; + int rv = 0; + + /* We don't care about snapshots after the pivot point */ + if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > pd->cb_pivot_txg) { + zfs_close(zhp); + return (0); + } + + /* Remove the device link if it's a zvol. */ + if (ZFS_IS_VOLUME(zhp)) + (void) zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name); + + /* Check for conflicting names */ + (void) strlcpy(snapname, pd->cb_target, sizeof (snapname)); + (void) strlcat(snapname, strchr(zhp->zfs_name, '@'), sizeof (snapname)); + szhp = make_dataset_handle(zhp->zfs_hdl, snapname); + if (szhp != NULL) { + zfs_close(szhp); + zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN, + "snapshot name '%s' from origin \n" + "conflicts with '%s' from target"), + zhp->zfs_name, snapname); + rv = zfs_error(zhp->zfs_hdl, EZFS_EXISTS, pd->cb_errbuf); + } + zfs_close(zhp); + return (rv); +} + +static int +promote_snap_done_cb(zfs_handle_t *zhp, void *data) +{ + promote_data_t *pd = data; + + /* We don't care about snapshots after the pivot point */ + if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) <= pd->cb_pivot_txg) { + /* Create the device link if it's a zvol. */ + if (ZFS_IS_VOLUME(zhp)) + (void) zvol_create_link(zhp->zfs_hdl, zhp->zfs_name); + } + + zfs_close(zhp); + return (0); +} + /* * Promotes the given clone fs to be the clone parent. */ @@ -3123,7 +3206,10 @@ zfs_promote(zfs_handle_t *zhp) libzfs_handle_t *hdl = zhp->zfs_hdl; zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 }; char parent[MAXPATHLEN]; + char *cp; int ret; + zfs_handle_t *pzhp; + promote_data_t pd; char errbuf[1024]; (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, @@ -3141,7 +3227,29 @@ zfs_promote(zfs_handle_t *zhp) "not a cloned filesystem")); return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); } + cp = strchr(parent, '@'); + *cp = '\0'; + /* Walk the snapshots we will be moving */ + pzhp = zfs_open(hdl, zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT); + if (pzhp == NULL) + return (-1); + pd.cb_pivot_txg = zfs_prop_get_int(pzhp, ZFS_PROP_CREATETXG); + zfs_close(pzhp); + pd.cb_target = zhp->zfs_name; + pd.cb_errbuf = errbuf; + pzhp = zfs_open(hdl, parent, ZFS_TYPE_DATASET); + if (pzhp == NULL) + return (-1); + (void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint, + sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE); + ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd); + if (ret != 0) { + zfs_close(pzhp); + return (-1); + } + + /* issue the ioctl */ (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin, sizeof (zc.zc_value)); (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); @@ -3150,9 +3258,16 @@ zfs_promote(zfs_handle_t *zhp) if (ret != 0) { int save_errno = errno; + (void) zfs_iter_snapshots(pzhp, promote_snap_done_cb, &pd); + zfs_close(pzhp); + switch (save_errno) { case EEXIST: - /* There is a conflicting snapshot name. */ + /* + * There is a conflicting snapshot name. We + * should have caught this above, but they could + * have renamed something in the mean time. + */ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "conflicting snapshot '%s' from parent '%s'"), zc.zc_string, parent); @@ -3161,7 +3276,44 @@ zfs_promote(zfs_handle_t *zhp) default: return (zfs_standard_error(hdl, save_errno, errbuf)); } + } else { + (void) zfs_iter_snapshots(zhp, promote_snap_done_cb, &pd); } + + zfs_close(pzhp); + return (ret); +} + +struct createdata { + const char *cd_snapname; + int cd_ifexists; +}; + +static int +zfs_create_link_cb(zfs_handle_t *zhp, void *arg) +{ + struct createdata *cd = arg; + int ret; + + if (zhp->zfs_type == ZFS_TYPE_VOLUME) { + char name[MAXPATHLEN]; + + (void) strlcpy(name, zhp->zfs_name, sizeof (name)); + (void) strlcat(name, "@", sizeof (name)); + (void) strlcat(name, cd->cd_snapname, sizeof (name)); + (void) zvol_create_link_common(zhp->zfs_hdl, name, + cd->cd_ifexists); + /* + * NB: this is simply a best-effort. We don't want to + * return an error, because then we wouldn't visit all + * the volumes. + */ + } + + ret = zfs_iter_filesystems(zhp, zfs_create_link_cb, cd); + + zfs_close(zhp); + return (ret); } @@ -3225,11 +3377,31 @@ zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive, * if it was recursive, the one that actually failed will be in * zc.zc_name. */ - if (ret != 0) { + if (ret != 0) (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value); - (void) zfs_standard_error(hdl, errno, errbuf); + + if (ret == 0 && recursive) { + struct createdata cd; + + cd.cd_snapname = delim + 1; + cd.cd_ifexists = B_FALSE; + (void) zfs_iter_filesystems(zhp, zfs_create_link_cb, &cd); } + if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) { + ret = zvol_create_link(zhp->zfs_hdl, path); + if (ret != 0) { + (void) zfs_standard_error(hdl, errno, + dgettext(TEXT_DOMAIN, + "Volume successfully snapshotted, but device links " + "were not created")); + zfs_close(zhp); + return (-1); + } + } + + if (ret != 0) + (void) zfs_standard_error(hdl, errno, errbuf); zfs_close(zhp); @@ -3332,6 +3504,8 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) */ if (zhp->zfs_type == ZFS_TYPE_VOLUME) { + if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0) + return (-1); if (zfs_which_resv_prop(zhp, &resv_prop) < 0) return (-1); old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); @@ -3369,6 +3543,10 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force) */ if ((zhp->zfs_type == ZFS_TYPE_VOLUME) && (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) { + if ((err = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name))) { + zfs_close(zhp); + return (err); + } if (restore_resv) { new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); if (old_volsize != new_volsize) @@ -3517,6 +3695,7 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive) } if (recursive) { + struct destroydata dd; parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name); if (parentname == NULL) { @@ -3531,6 +3710,15 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive) goto error; } + dd.snapname = delim + 1; + dd.gotone = B_FALSE; + dd.closezhp = B_TRUE; + + /* We remove any zvol links prior to renaming them */ + ret = zfs_iter_filesystems(zhrp, zfs_check_snap_cb, &dd); + if (ret) { + goto error; + } } else { if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL) return (-1); @@ -3579,10 +3767,27 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive) * On failure, we still want to remount any filesystems that * were previously mounted, so we don't alter the system state. */ - if (!recursive) + if (recursive) { + struct createdata cd; + + /* only create links for datasets that had existed */ + cd.cd_snapname = delim + 1; + cd.cd_ifexists = B_TRUE; + (void) zfs_iter_filesystems(zhrp, zfs_create_link_cb, + &cd); + } else { (void) changelist_postfix(cl); + } } else { - if (!recursive) { + if (recursive) { + struct createdata cd; + + /* only create links for datasets that had existed */ + cd.cd_snapname = strchr(target, '@') + 1; + cd.cd_ifexists = B_TRUE; + ret = zfs_iter_filesystems(zhrp, zfs_create_link_cb, + &cd); + } else { changelist_rename(cl, zfs_get_name(zhp), target); ret = changelist_postfix(cl); } @@ -3601,21 +3806,105 @@ error: return (ret); } +/* + * Given a zvol dataset, issue the ioctl to create the appropriate minor node, + * and wait briefly for udev to create the /dev link. + */ +int +zvol_create_link(libzfs_handle_t *hdl, const char *dataset) +{ + return (zvol_create_link_common(hdl, dataset, B_FALSE)); +} + +static int +zvol_create_link_common(libzfs_handle_t *hdl, const char *dataset, int ifexists) +{ + zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 }; + char path[MAXPATHLEN]; + int error; + + (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); + + /* + * Issue the appropriate ioctl. + */ + if (ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE_MINOR, &zc) != 0) { + switch (errno) { + case EEXIST: + /* + * Silently ignore the case where the link already + * exists. This allows 'zfs volinit' to be run multiple + * times without errors. + */ + return (0); + + case ENOENT: + /* + * Dataset does not exist in the kernel. If we + * don't care (see zfs_rename), then ignore the + * error quietly. + */ + if (ifexists) { + return (0); + } + + /* FALLTHROUGH */ + + default: + return (zfs_standard_error_fmt(hdl, errno, + dgettext(TEXT_DOMAIN, "cannot create device links " + "for '%s'"), dataset)); + } + } + + /* + * Wait up to 10 seconds for udev to create the device. + */ + (void) snprintf(path, sizeof (path), "%s/%s", ZVOL_DIR, dataset); + error = zpool_label_disk_wait(path, 10000); + if (error) + (void) printf(gettext("%s may not be immediately " + "available\n"), path); + + return (0); +} + +/* + * Remove a minor node for the given zvol and the associated /dev links. + */ +int +zvol_remove_link(libzfs_handle_t *hdl, const char *dataset) +{ + zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 }; + + (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); + + if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) { + switch (errno) { + case ENXIO: + /* + * Silently ignore the case where the link no longer + * exists, so that 'zfs volfini' can be run multiple + * times without errors. + */ + return (0); + + default: + return (zfs_standard_error_fmt(hdl, errno, + dgettext(TEXT_DOMAIN, "cannot remove device " + "links for '%s'"), dataset)); + } + } + + return (0); +} + nvlist_t * zfs_get_user_props(zfs_handle_t *zhp) { return (zhp->zfs_user_props); } -nvlist_t * -zfs_get_recvd_props(zfs_handle_t *zhp) -{ - if (zhp->zfs_recvd_props == NULL) - if (get_recvd_props_ioctl(zhp) != 0) - return (NULL); - return (zhp->zfs_recvd_props); -} - /* * This function is used by 'zfs list' to determine the exact set of columns to * display, and their maximum widths. This does two main things: diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index 5e74fb6514..030fb97e86 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -3663,7 +3663,7 @@ zpool_label_disk_check(char *path) (void) close(fd); return EIDRM; } - + efi_free(vtoc); (void) close(fd); return 0; diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c index 8b5f26a8c0..a1b014d548 100644 --- a/lib/libzfs/libzfs_sendrecv.c +++ b/lib/libzfs/libzfs_sendrecv.c @@ -2562,6 +2562,12 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, return (-1); } } + if (!flags.dryrun && zhp->zfs_type == ZFS_TYPE_VOLUME && + zvol_remove_link(hdl, zhp->zfs_name) != 0) { + zfs_close(zhp); + zcmd_free_nvlists(&zc); + return (-1); + } zfs_close(zhp); } else { /* @@ -2755,7 +2761,6 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, * children of the target filesystem if we did a replication * receive (indicated by stream_avl being non-NULL). */ -#ifdef HAVE_ZPL cp = strchr(zc.zc_value, '@'); if (cp && (ioctl_err == 0 || !newfs)) { zfs_handle_t *h; @@ -2766,6 +2771,10 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, if (h != NULL) { if (h->zfs_type == ZFS_TYPE_VOLUME) { *cp = '@'; + err = zvol_create_link(hdl, h->zfs_name); + if (err == 0 && ioctl_err == 0) + err = zvol_create_link(hdl, + zc.zc_value); } else if (newfs || stream_avl) { /* * Track the first/top of hierarchy fs, @@ -2778,7 +2787,6 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap, } *cp = '@'; } -#endif /* HAVE_ZPL */ if (clp) { err |= changelist_postfix(clp); diff --git a/module/zcommon/include/sys/fs/zfs.h b/module/zcommon/include/sys/fs/zfs.h index 7de30b94a2..45b21e13c3 100644 --- a/module/zcommon/include/sys/fs/zfs.h +++ b/module/zcommon/include/sys/fs/zfs.h @@ -686,13 +686,17 @@ typedef struct ddt_histogram { ddt_stat_t ddh_stat[64]; /* power-of-two histogram buckets */ } ddt_histogram_t; -#define ZVOL_DRIVER "zvol" -#define ZFS_DRIVER "zfs" -#define ZFS_DEV "/dev/zfs" -#define ZVOL_MAJOR 230 -#define ZVOL_MINOR_BITS 4 -#define ZVOL_MINOR_MASK ((1U << ZVOL_MINOR_BITS) - 1) -#define ZVOL_MINORS (1 << 4) +#define ZVOL_DRIVER "zvol" +#define ZFS_DRIVER "zfs" +#define ZFS_DEV "/dev/zfs" + +/* general zvol path */ +#define ZVOL_DIR "/dev/zvol" + +#define ZVOL_MAJOR 230 +#define ZVOL_MINOR_BITS 4 +#define ZVOL_MINOR_MASK ((1U << ZVOL_MINOR_BITS) - 1) +#define ZVOL_MINORS (1 << 4) #define ZVOL_PROP_NAME "name" #define ZVOL_DEFAULT_BLOCKSIZE 8192 @@ -726,6 +730,8 @@ typedef enum zfs_ioc { ZFS_IOC_DATASET_LIST_NEXT, ZFS_IOC_SNAPSHOT_LIST_NEXT, ZFS_IOC_SET_PROP, + ZFS_IOC_CREATE_MINOR, + ZFS_IOC_REMOVE_MINOR, ZFS_IOC_CREATE, ZFS_IOC_DESTROY, ZFS_IOC_ROLLBACK, diff --git a/module/zfs/include/sys/zvol.h b/module/zfs/include/sys/zvol.h index ed12c923de..c8b9d65071 100644 --- a/module/zfs/include/sys/zvol.h +++ b/module/zfs/include/sys/zvol.h @@ -42,7 +42,7 @@ extern void zvol_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); extern int zvol_create_minor(const char *); extern int zvol_create_minors(const char *); extern int zvol_remove_minor(const char *); -extern int zvol_remove_minors(const char *); +extern void zvol_remove_minors(const char *); extern int zvol_set_volsize(const char *, uint64_t); extern int zvol_set_volblocksize(const char *, uint64_t); diff --git a/module/zfs/vdev.c b/module/zfs/vdev.c index 72745bff37..1d980eb52f 100644 --- a/module/zfs/vdev.c +++ b/module/zfs/vdev.c @@ -1072,12 +1072,6 @@ vdev_open_child(void *arg) boolean_t vdev_uses_zvols(vdev_t *vd) { -/* - * NOTE: Disabled because under Linux I've choosen not to put all the zvols - * in their own directory. This could be changed or this code can be updated - * to perhap run an ioctl() on the vdev path to determine if it is a zvol. - */ -#if 0 int c; if (vd->vdev_path && strncmp(vd->vdev_path, ZVOL_DIR, @@ -1086,7 +1080,6 @@ vdev_uses_zvols(vdev_t *vd) for (c = 0; c < vd->vdev_children; c++) if (vdev_uses_zvols(vd->vdev_child[c])) return (B_TRUE); -#endif return (B_FALSE); } diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index a29b7aa7d0..8064edd6cc 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -1135,8 +1135,9 @@ zfs_ioc_pool_destroy(struct file *filp, zfs_cmd_t *zc) { int error; zfs_log_history(zc); - (void) zvol_remove_minors(zc->zc_name); error = spa_destroy(zc->zc_name); + if (error == 0) + zvol_remove_minors(zc->zc_name); return (error); } @@ -1167,7 +1168,7 @@ zfs_ioc_pool_import(struct file *filp, zfs_cmd_t *zc) error = spa_import(zc->zc_name, config, props); if (error == 0) - error = zvol_create_minors(zc->zc_name); + zvol_create_minors(zc->zc_name); if (zc->zc_nvlist_dst != 0) (void) put_nvlist(zc, config); @@ -1188,10 +1189,9 @@ zfs_ioc_pool_export(struct file *filp, zfs_cmd_t *zc) boolean_t hardforce = (boolean_t)zc->zc_guid; zfs_log_history(zc); - error = zvol_remove_minors(zc->zc_name); + error = spa_export(zc->zc_name, NULL, force, hardforce); if (error == 0) - error = spa_export(zc->zc_name, NULL, force, hardforce); - + zvol_remove_minors(zc->zc_name); return (error); } @@ -2467,6 +2467,30 @@ zfs_ioc_pool_get_props(struct file *filp, zfs_cmd_t *zc) return (error); } +/* + * inputs: + * zc_name name of volume + * + * outputs: none + */ +static int +zfs_ioc_create_minor(struct file *filp, zfs_cmd_t *zc) +{ + return (zvol_create_minor(zc->zc_name)); +} + +/* + * inputs: + * zc_name name of volume + * + * outputs: none + */ +static int +zfs_ioc_remove_minor(struct file *filp, zfs_cmd_t *zc) +{ + return (zvol_remove_minor(zc->zc_name)); +} + /* * inputs: * zc_name name of filesystem @@ -2865,18 +2889,9 @@ zfs_ioc_create(struct file *filp, zfs_cmd_t *zc) if (error == 0) { error = zfs_set_prop_nvlist(filp, zc->zc_name, ZPROP_SRC_LOCAL, nvprops, NULL); - if (error != 0) { + if (error != 0) (void) dmu_objset_destroy(zc->zc_name, B_FALSE); - goto out; - } - - if (type == DMU_OST_ZVOL) { - error = zvol_create_minor(zc->zc_name); - if (error != 0) - (void) dmu_objset_destroy(zc->zc_name, B_FALSE); - } } -out: nvlist_free(nvprops); return (error); } @@ -3001,7 +3016,7 @@ zfs_ioc_destroy(struct file *filp, zfs_cmd_t *zc) err = dmu_objset_destroy(zc->zc_name, zc->zc_defer_destroy); if (zc->zc_objset_type == DMU_OST_ZVOL && err == 0) - err = zvol_remove_minor(zc->zc_name); + (void) zvol_remove_minor(zc->zc_name); return (err); } @@ -3105,7 +3120,6 @@ static int zfs_ioc_rename(struct file *filp, zfs_cmd_t *zc) { boolean_t recursive = zc->zc_cookie & 1; - int err; zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0 || @@ -3119,16 +3133,12 @@ zfs_ioc_rename(struct file *filp, zfs_cmd_t *zc) */ if (!recursive && strchr(zc->zc_name, '@') != NULL && zc->zc_objset_type == DMU_OST_ZFS) { - err = zfs_unmount_snap(zc->zc_name, NULL); + int err = zfs_unmount_snap(zc->zc_name, NULL); if (err) return (err); } - if (zc->zc_objset_type == DMU_OST_ZVOL) { - err = zvol_remove_minor(zc->zc_name); - if (err) - return (err); - } - + if (zc->zc_objset_type == DMU_OST_ZVOL) + (void) zvol_remove_minor(zc->zc_name); return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive)); } @@ -4426,6 +4436,10 @@ static zfs_ioc_vec_t zfs_ioc_vec[] = { { zfs_ioc_snapshot_list_next, zfs_secpolicy_read, DATASET_NAME, B_FALSE, B_TRUE }, { zfs_ioc_set_prop, zfs_secpolicy_none, DATASET_NAME, B_TRUE, B_TRUE }, + { zfs_ioc_create_minor, zfs_secpolicy_config, DATASET_NAME, B_FALSE, + B_FALSE }, + { zfs_ioc_remove_minor, zfs_secpolicy_config, DATASET_NAME, B_FALSE, + B_FALSE }, { zfs_ioc_create, zfs_secpolicy_create, DATASET_NAME, B_TRUE, B_TRUE }, { zfs_ioc_destroy, zfs_secpolicy_destroy, DATASET_NAME, B_TRUE, B_TRUE}, diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c index 2e16e60c23..e93d94897e 100644 --- a/module/zfs/zvol.c +++ b/module/zfs/zvol.c @@ -57,6 +57,7 @@ static char *zvol_tag = "zvol_tag"; * The in-core state of each volume. */ typedef struct zvol_state { + char zv_name[DISK_NAME_LEN]; /* name */ uint64_t zv_volsize; /* advertised space */ uint64_t zv_volblocksize;/* volume block size */ objset_t *zv_objset; /* objset handle */ @@ -130,7 +131,7 @@ zvol_find_by_name(const char *name) ASSERT(MUTEX_HELD(&zvol_state_lock)); for (zv = list_head(&zvol_state_list); zv != NULL; zv = list_next(&zvol_state_list, zv)) { - if (!strncmp(zv->zv_disk->disk_name, name, DISK_NAME_LEN)) + if (!strncmp(zv->zv_name, name, DISK_NAME_LEN)) return zv; } @@ -182,7 +183,7 @@ int zvol_get_stats(objset_t *os, nvlist_t *nv) { int error; - dmu_object_info_t doi; + dmu_object_info_t *doi; uint64_t val; error = zap_lookup(os, ZVOL_ZAP_OBJ, "size", 8, 1, &val); @@ -190,14 +191,16 @@ zvol_get_stats(objset_t *os, nvlist_t *nv) return (error); dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_VOLSIZE, val); - - error = dmu_object_info(os, ZVOL_OBJ, &doi); + doi = kmem_alloc(sizeof(dmu_object_info_t), KM_SLEEP); + error = dmu_object_info(os, ZVOL_OBJ, doi); if (error == 0) { dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_VOLBLOCKSIZE, - doi.doi_data_block_size); + doi->doi_data_block_size); } + kmem_free(doi, sizeof(dmu_object_info_t)); + return (error); } @@ -273,7 +276,7 @@ int zvol_set_volsize(const char *name, uint64_t volsize) { zvol_state_t *zv; - dmu_object_info_t doi; + dmu_object_info_t *doi; objset_t *os = NULL; uint64_t readonly; int error; @@ -286,26 +289,30 @@ zvol_set_volsize(const char *name, uint64_t volsize) goto out; } + doi = kmem_alloc(sizeof(dmu_object_info_t), KM_SLEEP); + error = dmu_objset_hold(name, FTAG, &os); if (error) - goto out; + goto out_doi; - if ((error = dmu_object_info(os, ZVOL_OBJ, &doi)) != 0 || - (error = zvol_check_volsize(volsize,doi.doi_data_block_size)) != 0) - goto out; + if ((error = dmu_object_info(os, ZVOL_OBJ, doi)) != 0 || + (error = zvol_check_volsize(volsize,doi->doi_data_block_size)) != 0) + goto out_doi; VERIFY(dsl_prop_get_integer(name, "readonly", &readonly, NULL) == 0); if (readonly) { error = EROFS; - goto out; + goto out_doi; } if (get_disk_ro(zv->zv_disk) || (zv->zv_flags & ZVOL_RDONLY)) { error = EROFS; - goto out; + goto out_doi; } error = zvol_update_volsize(zv, volsize); +out_doi: + kmem_free(doi, sizeof(dmu_object_info_t)); out: if (os) dmu_objset_rele(os, FTAG); @@ -757,11 +764,10 @@ zvol_first_open(zvol_state_t *zv) objset_t *os; uint64_t volsize; int error; - uint64_t readonly; + uint64_t ro; /* lie and say we're read-only */ - error = dmu_objset_own(zv->zv_disk->disk_name, - DMU_OST_ZVOL, B_TRUE, zvol_tag, &os); + error = dmu_objset_own(zv->zv_name, DMU_OST_ZVOL, 1, zvol_tag, &os); if (error) return (-error); @@ -782,9 +788,8 @@ zvol_first_open(zvol_state_t *zv) zv->zv_volsize = volsize; zv->zv_zilog = zil_open(os, zvol_get_data); - VERIFY(dsl_prop_get_integer(zv->zv_disk->disk_name, - "readonly", &readonly, NULL) == 0); - if (readonly || dmu_objset_is_snapshot(os)) { + VERIFY(dsl_prop_get_integer(zv->zv_name, "readonly", &ro, NULL) == 0); + if (ro || dmu_objset_is_snapshot(os)) { set_disk_ro(zv->zv_disk, 1); zv->zv_flags |= ZVOL_RDONLY; } else { @@ -1031,6 +1036,7 @@ zvol_alloc(dev_t dev, const char *name) zv->zv_queue->queuedata = zv; zv->zv_dev = dev; zv->zv_open_count = 0; + strlcpy(zv->zv_name, name, DISK_NAME_LEN); mutex_init(&zv->zv_znode.z_range_lock, NULL, MUTEX_DEFAULT, NULL); avl_create(&zv->zv_znode.z_range_avl, zfs_range_compare, @@ -1043,7 +1049,7 @@ zvol_alloc(dev_t dev, const char *name) zv->zv_disk->fops = &zvol_ops; zv->zv_disk->private_data = zv; zv->zv_disk->queue = zv->zv_queue; - strlcpy(zv->zv_disk->disk_name, name, DISK_NAME_LEN); + snprintf(zv->zv_disk->disk_name, DISK_NAME_LEN, "zvol/%s", name); return zv; @@ -1071,21 +1077,16 @@ zvol_free(zvol_state_t *zv) kmem_free(zv, sizeof (zvol_state_t)); } -/* - * Create a block device minor node and setup the linkage between it - * and the specified volume. Once this function returns the block - * device is live and ready for use. - */ -int -zvol_create_minor(const char *name) +static int +__zvol_create_minor(const char *name) { zvol_state_t *zv; objset_t *os; - dmu_object_info_t doi; + dmu_object_info_t *doi; unsigned minor = 0; int error = 0; - mutex_enter(&zvol_state_lock); + ASSERT(MUTEX_HELD(&zvol_state_lock)); zv = zvol_find_by_name(name); if (zv) { @@ -1093,11 +1094,13 @@ zvol_create_minor(const char *name) goto out; } + doi = kmem_alloc(sizeof(dmu_object_info_t), KM_SLEEP); + error = dmu_objset_own(name, DMU_OST_ZVOL, B_TRUE, zvol_tag, &os); if (error) - goto out; + goto out_doi; - error = dmu_object_info(os, ZVOL_OBJ, &doi); + error = dmu_object_info(os, ZVOL_OBJ, doi); if (error) goto out_dmu_objset_disown; @@ -1114,7 +1117,7 @@ zvol_create_minor(const char *name) if (dmu_objset_is_snapshot(os)) zv->zv_flags |= ZVOL_RDONLY; - zv->zv_volblocksize = doi.doi_data_block_size; + zv->zv_volblocksize = doi->doi_data_block_size; if (zil_replay_disable) zil_destroy(dmu_objset_zil(os), B_FALSE); @@ -1127,10 +1130,47 @@ zvol_create_minor(const char *name) out_dmu_objset_disown: dmu_objset_disown(os, zvol_tag); +out_doi: + kmem_free(doi, sizeof(dmu_object_info_t)); out: + return (error); +} + +/* + * Create a block device minor node and setup the linkage between it + * and the specified volume. Once this function returns the block + * device is live and ready for use. + */ +int +zvol_create_minor(const char *name) +{ + int error; + + mutex_enter(&zvol_state_lock); + error = __zvol_create_minor(name); mutex_exit(&zvol_state_lock); - return (-error); + return (error); +} + +static int +__zvol_remove_minor(const char *name) +{ + zvol_state_t *zv; + + ASSERT(MUTEX_HELD(&zvol_state_lock)); + + zv = zvol_find_by_name(name); + if (zv == NULL) + return (ENXIO); + + if (zv->zv_open_count > 0) + return (EBUSY); + + zvol_remove(zv); + zvol_free(zv); + + return (0); } /* @@ -1139,25 +1179,10 @@ out: int zvol_remove_minor(const char *name) { - zvol_state_t *zv; - int error = 0; + int error; mutex_enter(&zvol_state_lock); - - zv = zvol_find_by_name(name); - if (zv == NULL) { - error = ENXIO; - goto out; - } - - if (zv->zv_open_count > 0) { - error = EBUSY; - goto out; - } - - zvol_remove(zv); - zvol_free(zv); -out: + error = __zvol_remove_minor(name); mutex_exit(&zvol_state_lock); return (error); @@ -1170,59 +1195,65 @@ zvol_create_minors_cb(spa_t *spa, uint64_t dsobj, if (strchr(dsname, '/') == NULL) return 0; - return zvol_create_minor(dsname); + return __zvol_create_minor(dsname); } -static int -zvol_remove_minors_cb(spa_t *spa, uint64_t dsobj, - const char *dsname, void *arg) -{ - if (strchr(dsname, '/') == NULL) - return 0; - - return zvol_remove_minor(dsname); -} - -static int -zvol_cr_minors_common(const char *pool, - int func(spa_t *, uint64_t, const char *, void *), void *arg) +/* + * Create minors for specified pool, if pool is NULL create minors + * for all available pools. + */ +int +zvol_create_minors(const char *pool) { spa_t *spa = NULL; int error = 0; + mutex_enter(&zvol_state_lock); if (pool) { - error = dmu_objset_find_spa(NULL, pool, func, arg, - DS_FIND_CHILDREN | DS_FIND_SNAPSHOTS); + error = dmu_objset_find_spa(NULL, pool, zvol_create_minors_cb, + NULL, DS_FIND_CHILDREN | DS_FIND_SNAPSHOTS); } else { mutex_enter(&spa_namespace_lock); while ((spa = spa_next(spa)) != NULL) { - error = dmu_objset_find_spa(NULL, spa_name(spa), - func, arg, DS_FIND_CHILDREN | DS_FIND_SNAPSHOTS); + error = dmu_objset_find_spa(NULL, + spa_name(spa), zvol_create_minors_cb, NULL, + DS_FIND_CHILDREN | DS_FIND_SNAPSHOTS); if (error) break; } mutex_exit(&spa_namespace_lock); } + mutex_exit(&zvol_state_lock); return error; } /* - * Create minors for specified pool, or if NULL create all minors. + * Remove minors for specified pool, if pool is NULL remove all minors. */ -int -zvol_create_minors(const char *pool) -{ - return zvol_cr_minors_common(pool, zvol_create_minors_cb, NULL); -} - -/* - * Remove minors for specified pool, or if NULL remove all minors. - */ -int +void zvol_remove_minors(const char *pool) { - return zvol_cr_minors_common(pool, zvol_remove_minors_cb, NULL); + zvol_state_t *zv, *zv_next; + char *str; + + str = kmem_zalloc(DISK_NAME_LEN, KM_SLEEP); + if (pool) { + (void) strncpy(str, pool, strlen(pool)); + (void) strcat(str, "/"); + } + + mutex_enter(&zvol_state_lock); + for (zv = list_head(&zvol_state_list); zv != NULL; zv = zv_next) { + zv_next = list_next(&zvol_state_list, zv); + + if (pool == NULL || !strncmp(str, zv->zv_name, strlen(str))) { + zvol_remove(zv); + zvol_free(zv); + } + } + mutex_exit(&zvol_state_lock); + kmem_free(str, DISK_NAME_LEN); } int @@ -1262,12 +1293,10 @@ zvol_init(void) void zvol_fini(void) { - (void) zvol_remove_minors(NULL); - + zvol_remove_minors(NULL); blk_unregister_region(MKDEV(zvol_major, 0), 1UL << MINORBITS); unregister_blkdev(zvol_major, ZVOL_DRIVER); taskq_destroy(zvol_taskq); - mutex_destroy(&zvol_state_lock); list_destroy(&zvol_state_list); } diff --git a/scripts/zconfig.sh b/scripts/zconfig.sh index 5123d25ad5..b74c57087a 100755 --- a/scripts/zconfig.sh +++ b/scripts/zconfig.sh @@ -159,52 +159,4 @@ EOF } zconfig_test3 -# zpool import/export check -zconfig_test4() { - POOL_NAME=test4 - ZVOL_NAME1=fish1 - ZVOL_NAME2=fish2 - FULL_NAME1=${POOL_NAME}/${ZVOL_NAME1} - FULL_NAME2=${POOL_NAME}/${ZVOL_NAME2} - TMP_CACHE=`mktemp -p /tmp zpool.cache.XXXXXXXX` - - echo -n "test 4 - zpool import/export: " - - # Create a pool and volume. - ${ZFS_SH} zfs="spa_config_path=${TMP_CACHE}" || fail 1 - ${ZPOOL_CREATE_SH} -p ${POOL_NAME} -c lo-raidz2 || fail 2 - ${ZFS} create -V 100M ${FULL_NAME1} || fail 3 - ${ZFS} create -V 100M ${FULL_NAME2} || fail 4 - - # Verify the devices were created - stat /dev/${FULL_NAME1} &>/dev/null || fail 5 - stat /dev/${FULL_NAME2} &>/dev/null || fail 6 - - # Export the pool - ${ZPOOL} export ${POOL_NAME} || fail 7 - - # Verify the devices were removed - stat /dev/${FULL_NAME1} &>/dev/null && fail 8 - stat /dev/${FULL_NAME2} &>/dev/null && fail 9 - - # Import the pool - ${ZPOOL} import ${POOL_NAME} || fail 10 - - # Verify the devices were created - stat /dev/${FULL_NAME1} &>/dev/null || fail 11 - stat /dev/${FULL_NAME2} &>/dev/null || fail 12 - - # Destroy the pool and consequently the devices - ${ZPOOL_CREATE_SH} -p ${POOL_NAME} -c lo-raidz2 -d || fail 15 - - # Verify the devices were removed - stat /dev/${FULL_NAME1} &>/dev/null && fail 16 - stat /dev/${FULL_NAME2} &>/dev/null && fail 17 - - ${ZFS_SH} -u || fail 18 - - pass -} -zconfig_test4 - exit 0