From dc1c630b8abf2db4ce78b583b441bd0c74f42936 Mon Sep 17 00:00:00 2001 From: Andriy Gapon Date: Thu, 28 Jun 2018 00:37:54 +0300 Subject: [PATCH] OpenZFS 9630 - add lzc_rename and lzc_destroy to libzfs_core Porting Notes: * Additional changes to recv_rename_impl() were required due to encryption code not being merged in OpenZFS yet. * libzfs_core python bindings (pyzfs) were updated to fully support both lzc_rename() and lzc_destroy() Authored by: Andriy Gapon Reviewed by: Andy Stormont Reviewed by: Matt Ahrens Reviewed by: Serapheim Dimitropoulos Reviewed by: Brian Behlendorf Approved by: Dan McDonald Ported-by: loli10K OpenZFS-issue: https://www.illumos.org/issues/9630 OpenZFS-commit: https://github.com/openzfs/openzfs/commit/049ba63 Closes #8207 --- contrib/pyzfs/libzfs_core/_libzfs_core.py | 14 ++----- .../pyzfs/libzfs_core/bindings/libzfs_core.py | 4 +- include/libzfs_core.h | 3 ++ lib/libzfs/libzfs_dataset.c | 24 ++++++----- lib/libzfs/libzfs_sendrecv.c | 40 ++++++++----------- lib/libzfs_core/libzfs_core.c | 24 +++++++++++ module/zfs/zfs_ioctl.c | 22 ++++++++-- .../libzfs_input_check/libzfs_input_check.c | 1 - 8 files changed, 81 insertions(+), 51 deletions(-) diff --git a/contrib/pyzfs/libzfs_core/_libzfs_core.py b/contrib/pyzfs/libzfs_core/_libzfs_core.py index db207bf71e..157dc16e9a 100644 --- a/contrib/pyzfs/libzfs_core/_libzfs_core.py +++ b/contrib/pyzfs/libzfs_core/_libzfs_core.py @@ -1605,7 +1605,6 @@ def lzc_pool_checkpoint_discard(name): errors.lzc_pool_checkpoint_discard_translate_error(ret, name) -@_uncommitted() def lzc_rename(source, target): ''' Rename the ZFS dataset. @@ -1621,12 +1620,11 @@ def lzc_rename(source, target): :raises FilesystemExists: if the target already exists. :raises PoolsDiffer: if the source and target belong to different pools. ''' - ret = _lib.lzc_rename(source, target, _ffi.NULL, _ffi.NULL) + ret = _lib.lzc_rename(source, target) errors.lzc_rename_translate_error(ret, source, target) -@_uncommitted() -def lzc_destroy_one(name): +def lzc_destroy(name): ''' Destroy the ZFS dataset. @@ -1635,16 +1633,10 @@ def lzc_destroy_one(name): :raises NameTooLong: if the dataset name is too long. :raises FilesystemNotFound: if the dataset does not exist. ''' - ret = _lib.lzc_destroy_one(name, _ffi.NULL) + ret = _lib.lzc_destroy(name) errors.lzc_destroy_translate_error(ret, name) -# As the extended API is not committed yet, the names of the new interfaces -# are not settled down yet. -# lzc_destroy() might make more sense as we do not have lzc_create_one(). -lzc_destroy = lzc_destroy_one - - @_uncommitted() def lzc_inherit(name, prop): ''' diff --git a/contrib/pyzfs/libzfs_core/bindings/libzfs_core.py b/contrib/pyzfs/libzfs_core/bindings/libzfs_core.py index b69c8d779a..658394a3fe 100644 --- a/contrib/pyzfs/libzfs_core/bindings/libzfs_core.py +++ b/contrib/pyzfs/libzfs_core/bindings/libzfs_core.py @@ -129,9 +129,9 @@ CDEF = """ int lzc_remap(const char *); int lzc_pool_checkpoint(const char *); int lzc_pool_checkpoint_discard(const char *); + int lzc_rename(const char *, const char *); + int lzc_destroy(const char *fsname); - int lzc_rename(const char *, const char *, nvlist_t *, char **); - int lzc_destroy_one(const char *fsname, nvlist_t *); int lzc_inherit(const char *fsname, const char *name, nvlist_t *); int lzc_set_props(const char *, nvlist_t *, nvlist_t *, nvlist_t *); int lzc_list (const char *, nvlist_t *); diff --git a/include/libzfs_core.h b/include/libzfs_core.h index c22cbf18e2..f84270d7eb 100644 --- a/include/libzfs_core.h +++ b/include/libzfs_core.h @@ -102,6 +102,9 @@ boolean_t lzc_exists(const char *); int lzc_rollback(const char *, char *, int); int lzc_rollback_to(const char *, const char *); +int lzc_rename(const char *, const char *); +int lzc_destroy(const char *); + int lzc_channel_program(const char *, const char *, uint64_t, uint64_t, nvlist_t *, nvlist_t **); int lzc_channel_program_nosync(const char *, const char *, uint64_t, diff --git a/lib/libzfs/libzfs_dataset.c b/lib/libzfs/libzfs_dataset.c index 237933c371..6445c9d7ac 100644 --- a/lib/libzfs/libzfs_dataset.c +++ b/lib/libzfs/libzfs_dataset.c @@ -3856,32 +3856,34 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type, int zfs_destroy(zfs_handle_t *zhp, boolean_t defer) { - zfs_cmd_t zc = {"\0"}; + int error; + + if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT && defer) + return (EINVAL); if (zhp->zfs_type == ZFS_TYPE_BOOKMARK) { nvlist_t *nv = fnvlist_alloc(); fnvlist_add_boolean(nv, zhp->zfs_name); - int error = lzc_destroy_bookmarks(nv, NULL); + error = lzc_destroy_bookmarks(nv, NULL); fnvlist_free(nv); if (error != 0) { - return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, + return (zfs_standard_error_fmt(zhp->zfs_hdl, error, dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), zhp->zfs_name)); } return (0); } - (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); - - if (ZFS_IS_VOLUME(zhp)) { - zc.zc_objset_type = DMU_OST_ZVOL; + if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { + nvlist_t *nv = fnvlist_alloc(); + fnvlist_add_boolean(nv, zhp->zfs_name); + error = lzc_destroy_snaps(nv, defer, NULL); + fnvlist_free(nv); } else { - zc.zc_objset_type = DMU_OST_ZFS; + error = lzc_destroy(zhp->zfs_name); } - zc.zc_defer_destroy = defer; - if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0 && - errno != ENOENT) { + if (error != 0 && error != ENOENT) { return (zfs_standard_error_fmt(zhp->zfs_hdl, errno, dgettext(TEXT_DOMAIN, "cannot destroy '%s'"), zhp->zfs_name)); diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c index a05ccbe309..ae24454d7c 100644 --- a/lib/libzfs/libzfs_sendrecv.c +++ b/lib/libzfs/libzfs_sendrecv.c @@ -2294,7 +2294,7 @@ recv_open_grand_origin(zfs_handle_t *zhp) } static int -recv_rename_impl(zfs_handle_t *zhp, zfs_cmd_t *zc) +recv_rename_impl(zfs_handle_t *zhp, const char *name, const char *newname) { int err; zfs_handle_t *ozhp = NULL; @@ -2304,7 +2304,7 @@ recv_rename_impl(zfs_handle_t *zhp, zfs_cmd_t *zc) * attempted to rename the dataset outside of its encryption root. * Force the dataset to become an encryption root and try again. */ - err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_RENAME, &zc); + err = lzc_rename(name, newname); if (err == EACCES) { ozhp = recv_open_grand_origin(zhp); if (ozhp == NULL) { @@ -2317,7 +2317,7 @@ recv_rename_impl(zfs_handle_t *zhp, zfs_cmd_t *zc) if (err != 0) goto out; - err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_RENAME, &zc); + err = lzc_rename(name, newname); } out: @@ -2331,7 +2331,6 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname, int baselen, char *newname, recvflags_t *flags) { static int seq; - zfs_cmd_t zc = {"\0"}; int err; prop_changelist_t *clp = NULL; zfs_handle_t *zhp = NULL; @@ -2351,19 +2350,13 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname, if (err) goto out; - zc.zc_objset_type = DMU_OST_ZFS; - (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); - if (tryname) { (void) strcpy(newname, tryname); - - (void) strlcpy(zc.zc_value, tryname, sizeof (zc.zc_value)); - if (flags->verbose) { (void) printf("attempting rename %s to %s\n", - zc.zc_name, zc.zc_value); + name, newname); } - err = recv_rename_impl(zhp, &zc); + err = recv_rename_impl(zhp, name, newname); if (err == 0) changelist_rename(clp, name, tryname); } else { @@ -2375,13 +2368,12 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname, (void) snprintf(newname, ZFS_MAX_DATASET_NAME_LEN, "%.*srecv-%u-%u", baselen, name, getpid(), seq); - (void) strlcpy(zc.zc_value, newname, sizeof (zc.zc_value)); if (flags->verbose) { (void) printf("failed - trying rename %s to %s\n", - zc.zc_name, zc.zc_value); + name, newname); } - err = recv_rename_impl(zhp, &zc); + err = recv_rename_impl(zhp, name, newname); if (err == 0) changelist_rename(clp, name, newname); if (err && flags->verbose) { @@ -2461,7 +2453,6 @@ static int recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen, char *newname, recvflags_t *flags) { - zfs_cmd_t zc = {"\0"}; int err = 0; prop_changelist_t *clp; zfs_handle_t *zhp; @@ -2484,17 +2475,20 @@ recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen, if (err) return (err); - zc.zc_objset_type = DMU_OST_ZFS; - zc.zc_defer_destroy = defer; - (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); - if (flags->verbose) - (void) printf("attempting destroy %s\n", zc.zc_name); - err = ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc); + (void) printf("attempting destroy %s\n", name); + if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) { + nvlist_t *nv = fnvlist_alloc(); + fnvlist_add_boolean(nv, name); + err = lzc_destroy_snaps(nv, defer, NULL); + fnvlist_free(nv); + } else { + err = lzc_destroy(name); + } if (err == 0) { if (flags->verbose) (void) printf("success\n"); - changelist_remove(clp, zc.zc_name); + changelist_remove(clp, name); } (void) changelist_postfix(clp); diff --git a/lib/libzfs_core/libzfs_core.c b/lib/libzfs_core/libzfs_core.c index f24581db83..1baec04a40 100644 --- a/lib/libzfs_core/libzfs_core.c +++ b/lib/libzfs_core/libzfs_core.c @@ -316,6 +316,30 @@ lzc_remap(const char *fsname) return (error); } +int +lzc_rename(const char *source, const char *target) +{ + zfs_cmd_t zc = { "\0" }; + int error; + ASSERT3S(g_refcount, >, 0); + VERIFY3S(g_fd, !=, -1); + (void) strlcpy(zc.zc_name, source, sizeof (zc.zc_name)); + (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value)); + error = ioctl(g_fd, ZFS_IOC_RENAME, &zc); + if (error != 0) + error = errno; + return (error); +} +int +lzc_destroy(const char *fsname) +{ + int error; + nvlist_t *args = fnvlist_alloc(); + error = lzc_ioctl(ZFS_IOC_DESTROY, fsname, args, NULL); + nvlist_free(args); + return (error); +} + /* * Creates snapshots. * diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index 7c469246d9..a71da28374 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -3785,7 +3785,6 @@ zfs_ioc_pool_discard_checkpoint(const char *poolname, nvlist_t *innvl, /* * inputs: * zc_name name of dataset to destroy - * zc_objset_type type of objset * zc_defer_destroy mark for deferred destroy * * outputs: none @@ -3793,9 +3792,17 @@ zfs_ioc_pool_discard_checkpoint(const char *poolname, nvlist_t *innvl, static int zfs_ioc_destroy(zfs_cmd_t *zc) { + objset_t *os; + dmu_objset_type_t ost; int err; - if (zc->zc_objset_type == DMU_OST_ZFS) + err = dmu_objset_hold(zc->zc_name, FTAG, &os); + if (err != 0) + return (err); + ost = dmu_objset_type(os); + dmu_objset_rele(os, FTAG); + + if (ost == DMU_OST_ZFS) zfs_unmount_snap(zc->zc_name); if (strchr(zc->zc_name, '@')) { @@ -3917,8 +3924,11 @@ recursive_unmount(const char *fsname, void *arg) static int zfs_ioc_rename(zfs_cmd_t *zc) { + objset_t *os; + dmu_objset_type_t ost; boolean_t recursive = zc->zc_cookie & 1; char *at; + int err; /* "zfs rename" from and to ...%recv datasets should both fail */ zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; @@ -3928,6 +3938,12 @@ zfs_ioc_rename(zfs_cmd_t *zc) strchr(zc->zc_name, '%') || strchr(zc->zc_value, '%')) return (SET_ERROR(EINVAL)); + err = dmu_objset_hold(zc->zc_name, FTAG, &os); + if (err != 0) + return (err); + ost = dmu_objset_type(os); + dmu_objset_rele(os, FTAG); + at = strchr(zc->zc_name, '@'); if (at != NULL) { /* snaps must be in same fs */ @@ -3936,7 +3952,7 @@ zfs_ioc_rename(zfs_cmd_t *zc) if (strncmp(zc->zc_name, zc->zc_value, at - zc->zc_name + 1)) return (SET_ERROR(EXDEV)); *at = '\0'; - if (zc->zc_objset_type == DMU_OST_ZFS) { + if (ost == DMU_OST_ZFS) { error = dmu_objset_find(zc->zc_name, recursive_unmount, at + 1, recursive ? DS_FIND_CHILDREN : 0); diff --git a/tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c b/tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c index 3d5c7b64a2..88a18d8f0f 100644 --- a/tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c +++ b/tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c @@ -650,7 +650,6 @@ zfs_destroy(const char *dataset) (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name)); zc.zc_name[sizeof (zc.zc_name) - 1] = '\0'; - zc.zc_objset_type = DMU_OST_ZFS; err = ioctl(zfs_fd, ZFS_IOC_DESTROY, &zc); return (err == 0 ? 0 : errno);