--- cddl/contrib/opensolaris/cmd/zfs/zfs_main.c +++ cddl/contrib/opensolaris/cmd/zfs/zfs_main.c @@ -3110,6 +3110,7 @@ int ret = 0; int types; boolean_t parents = B_FALSE; + char *snapshot = NULL; /* check options */ while ((c = getopt(argc, argv, "pru")) != -1) { @@ -3175,6 +3176,19 @@ else types = ZFS_TYPE_DATASET; + if (flags.recurse) { + /* + * When we do recursive rename we are fine when the given + * snapshot for the given dataset doesn't exist - it can + * still exists below. + */ + + snapshot = strchr(argv[0], '@'); + assert(snapshot != NULL); + *snapshot = '\0'; + snapshot++; + } + if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) return (1); @@ -3185,7 +3199,7 @@ return (1); } - ret = (zfs_rename(zhp, argv[1], flags) != 0); + ret = (zfs_rename(zhp, snapshot, argv[1], flags) != 0); zfs_close(zhp); return (ret); --- cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h +++ cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h @@ -543,7 +543,8 @@ int nounmount : 1; } renameflags_t; -extern int zfs_rename(zfs_handle_t *, const char *, renameflags_t flags); +extern int zfs_rename(zfs_handle_t *, const char *, const char *, + renameflags_t flags); typedef struct sendflags { /* print informational messages (ie, -v was specified) */ --- cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c +++ cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c @@ -609,6 +609,22 @@ return (NULL); } + if (zhp == NULL) { + char *at = strchr(path, '@'); + + if (at != NULL) + *at = '\0'; + errno = 0; + if ((zhp = make_dataset_handle(hdl, path)) == NULL) { + (void) zfs_standard_error(hdl, errno, errbuf); + return (NULL); + } + if (at != NULL) + *at = '@'; + (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name)); + zhp->zfs_type = ZFS_TYPE_SNAPSHOT; + } + if (!(types & zhp->zfs_type)) { (void) zfs_error(hdl, EZFS_BADTYPE, errbuf); zfs_close(zhp); @@ -3594,7 +3610,8 @@ * Renames the given dataset. */ int -zfs_rename(zfs_handle_t *zhp, const char *target, renameflags_t flags) +zfs_rename(zfs_handle_t *zhp, const char *source, const char *target, + renameflags_t flags) { int ret; zfs_cmd_t zc = { 0 }; @@ -3614,6 +3631,18 @@ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, "cannot rename to '%s'"), target); + if (source != NULL) { + /* + * This is recursive snapshots rename, put snapshot name + * (that might not exist) into zfs_name. + */ + assert(flags.recurse); + + (void) strlcat(zhp->zfs_name, "@", sizeof(zhp->zfs_name)); + (void) strlcat(zhp->zfs_name, source, sizeof(zhp->zfs_name)); + zhp->zfs_type = ZFS_TYPE_SNAPSHOT; + } + /* * Make sure the target name is valid */ --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c @@ -2453,6 +2453,7 @@ char failed[MAXPATHLEN]; char *oldsnap; char *newsnap; + int error; }; static int @@ -2490,6 +2491,9 @@ dsl_sync_task_create(ra->dstg, dsl_dataset_snapshot_rename_check, dsl_dataset_snapshot_rename_sync, ds, ra->newsnap, 0); + /* First successful rename clears the error. */ + ra->error = 0; + return (0); } @@ -2518,14 +2522,16 @@ ra->oldsnap = strchr(oldname, '@') + 1; ra->newsnap = strchr(newname, '@') + 1; *ra->failed = '\0'; + ra->error = ENOENT; err = dmu_objset_find(fsname, dsl_snapshot_rename_one, ra, DS_FIND_CHILDREN); kmem_free(fsname, len); + if (err == 0) + err = ra->error; - if (err == 0) { + if (err == 0) err = dsl_sync_task_group_wait(ra->dstg); - } for (dst = list_head(&ra->dstg->dstg_tasks); dst; dst = list_next(&ra->dstg->dstg_tasks, dst)) { --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c @@ -763,7 +763,26 @@ static int zfs_secpolicy_rename(zfs_cmd_t *zc, cred_t *cr) { - return (zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr)); + char *at = NULL; + int error; + + if ((zc->zc_cookie & 1) != 0) { + /* + * This is recursive rename, so the starting snapshot might + * not exist. Check file system or volume permission instead. + */ + at = strchr(zc->zc_name, '@'); + if (at == NULL) + return (EINVAL); + *at = '\0'; + } + + error = zfs_secpolicy_rename_perms(zc->zc_name, zc->zc_value, cr); + + if (at != NULL) + *at = '@'; + + return (error); } static int