summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authormmusante <none@none>2007-04-11 09:10:08 -0700
committermmusante <none@none>2007-04-11 09:10:08 -0700
commitcdf5b4ca0fa5ca7622b06bcb271be9e8a8245fec (patch)
treec4bec9f51e851f4223cfda3f1e15c9bf2a9b6abc /usr/src
parent69648175ab3df6ed66211c75234243dbdf9ddd35 (diff)
downloadillumos-joyent-cdf5b4ca0fa5ca7622b06bcb271be9e8a8245fec.tar.gz
PSARC 2007/142 zfs rename -r
6479884 want 'zfs rename -r' to recursively rename snapshots
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/zfs/zfs_main.c44
-rw-r--r--usr/src/lib/libzfs/common/libzfs.h2
-rw-r--r--usr/src/lib/libzfs/common/libzfs_dataset.c152
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_dataset.c142
-rw-r--r--usr/src/uts/common/fs/zfs/sys/dmu.h3
-rw-r--r--usr/src/uts/common/fs/zfs/sys/dsl_dataset.h2
-rw-r--r--usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h1
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_ctldir.c2
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_ioctl.c13
9 files changed, 303 insertions, 58 deletions
diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c
index 815b72ea75..73b818abd4 100644
--- a/usr/src/cmd/zfs/zfs_main.c
+++ b/usr/src/cmd/zfs/zfs_main.c
@@ -196,7 +196,8 @@ get_usage(zfs_help_t idx)
"\treceive [-vnF] -d <filesystem>\n"));
case HELP_RENAME:
return (gettext("\trename <filesystem|volume|snapshot> "
- "<filesystem|volume|snapshot>\n"));
+ "<filesystem|volume|snapshot>\n"
+ "\trename -r <snapshot> <snapshot>"));
case HELP_ROLLBACK:
return (gettext("\trollback [-rRf] <snapshot>\n"));
case HELP_SEND:
@@ -1475,7 +1476,7 @@ zfs_do_list(int argc, char **argv)
}
/*
- * zfs rename <fs | snap | vol> <fs | snap | vol>
+ * zfs rename [-r] <fs | snap | vol> <fs | snap | vol>
*
* Renames the given dataset to another of the same type.
*/
@@ -1484,38 +1485,57 @@ static int
zfs_do_rename(int argc, char **argv)
{
zfs_handle_t *zhp;
+ int c;
int ret;
+ int recurse = 0;
/* check options */
- if (argc > 1 && argv[1][0] == '-') {
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- argv[1][1]);
- usage(B_FALSE);
+ while ((c = getopt(argc, argv, "r")) != -1) {
+ switch (c) {
+ case 'r':
+ recurse = 1;
+ break;
+ case '?':
+ default:
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ usage(B_FALSE);
+ }
}
+ argc -= optind;
+ argv += optind;
+
/* check number of arguments */
- if (argc < 2) {
+ if (argc < 1) {
(void) fprintf(stderr, gettext("missing source dataset "
"argument\n"));
usage(B_FALSE);
}
- if (argc < 3) {
+ if (argc < 2) {
(void) fprintf(stderr, gettext("missing target dataset "
"argument\n"));
usage(B_FALSE);
}
- if (argc > 3) {
+ if (argc > 2) {
(void) fprintf(stderr, gettext("too many arguments\n"));
usage(B_FALSE);
}
- if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_ANY)) == NULL)
+ if (recurse && strchr(argv[0], '@') == 0) {
+ (void) fprintf(stderr, gettext("source dataset for recursive "
+ "rename must be a snapshot\n"));
+ usage(B_FALSE);
+ }
+
+ if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL)
return (1);
- ret = (zfs_rename(zhp, argv[2]) != 0);
+ ret = (zfs_rename(zhp, argv[1], recurse) != 0);
if (!ret)
- zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE);
+ zpool_log_history(g_zfs, argc + optind, argv - optind, argv[1],
+ B_FALSE, B_FALSE);
zfs_close(zhp);
return (ret);
diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h
index d4bd3a25c7..2ed975ccd4 100644
--- a/usr/src/lib/libzfs/common/libzfs.h
+++ b/usr/src/lib/libzfs/common/libzfs.h
@@ -335,7 +335,7 @@ extern int zfs_destroy_snaps(zfs_handle_t *, char *);
extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *);
extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t);
extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, int);
-extern int zfs_rename(zfs_handle_t *, const char *);
+extern int zfs_rename(zfs_handle_t *, const char *, int);
extern int zfs_send(zfs_handle_t *, const char *, int);
extern int zfs_receive(libzfs_handle_t *, const char *, int, int, int,
boolean_t, int);
diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c
index c14f0229fd..15141ab19f 100644
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c
@@ -51,6 +51,8 @@
#include "zfs_prop.h"
#include "libzfs_impl.h"
+static int zvol_create_link_common(libzfs_handle_t *, const char *, int);
+
/*
* Given a single type (not a mask of types), return the type in a human
* readable form.
@@ -2512,10 +2514,15 @@ zfs_promote(zfs_handle_t *zhp)
return (ret);
}
+struct createdata {
+ const char *cd_snapname;
+ int cd_ifexists;
+};
+
static int
zfs_create_link_cb(zfs_handle_t *zhp, void *arg)
{
- char *snapname = arg;
+ struct createdata *cd = arg;
int ret;
if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
@@ -2523,8 +2530,9 @@ zfs_create_link_cb(zfs_handle_t *zhp, void *arg)
(void) strlcpy(name, zhp->zfs_name, sizeof (name));
(void) strlcat(name, "@", sizeof (name));
- (void) strlcat(name, snapname, sizeof (name));
- (void) zvol_create_link(zhp->zfs_hdl, 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
@@ -2532,7 +2540,7 @@ zfs_create_link_cb(zfs_handle_t *zhp, void *arg)
*/
}
- ret = zfs_iter_filesystems(zhp, zfs_create_link_cb, snapname);
+ ret = zfs_iter_filesystems(zhp, zfs_create_link_cb, cd);
zfs_close(zhp);
@@ -2584,8 +2592,11 @@ zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive)
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
"cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
if (ret == 0 && recursive) {
- (void) zfs_iter_filesystems(zhp,
- zfs_create_link_cb, (char *)delim+1);
+ 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);
@@ -3181,12 +3192,14 @@ zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
* Renames the given dataset.
*/
int
-zfs_rename(zfs_handle_t *zhp, const char *target)
+zfs_rename(zfs_handle_t *zhp, const char *target, int recursive)
{
int ret;
zfs_cmd_t zc = { 0 };
char *delim;
- prop_changelist_t *cl;
+ prop_changelist_t *cl = NULL;
+ zfs_handle_t *zhrp = NULL;
+ char *parentname = NULL;
char parent[ZFS_MAXNAMELEN];
libzfs_handle_t *hdl = zhp->zfs_hdl;
char errbuf[1024];
@@ -3234,6 +3247,12 @@ zfs_rename(zfs_handle_t *zhp, const char *target)
if (!zfs_validate_name(hdl, target, zhp->zfs_type))
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
} else {
+ if (recursive) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "recursive rename must be a snapshot"));
+ return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
+ }
+
if (!zfs_validate_name(hdl, target, zhp->zfs_type))
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
uint64_t unused;
@@ -3273,19 +3292,41 @@ zfs_rename(zfs_handle_t *zhp, const char *target)
return (zfs_error(hdl, EZFS_ZONED, errbuf));
}
- if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0)) == NULL)
- return (-1);
+ if (recursive) {
+ struct destroydata dd;
- if (changelist_haszonedchild(cl)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "child dataset with inherited mountpoint is used "
- "in a non-global zone"));
- (void) zfs_error(hdl, EZFS_ZONED, errbuf);
- goto error;
- }
+ parentname = strdup(zhp->zfs_name);
+ delim = strchr(parentname, '@');
+ *delim = '\0';
+ zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_ANY);
+ if (zhrp == NULL) {
+ return (-1);
+ }
- if ((ret = changelist_prefix(cl)) != 0)
- goto error;
+ dd.snapname = delim + 1;
+ dd.gotone = B_FALSE;
+ dd.closezhp = B_FALSE;
+
+ /* We remove any zvol links prior to renaming them */
+ ret = zfs_iter_filesystems(zhrp, zfs_remove_link_cb, &dd);
+ if (ret) {
+ goto error;
+ }
+ } else {
+ if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0)) == NULL)
+ return (-1);
+
+ if (changelist_haszonedchild(cl)) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "child dataset with inherited mountpoint is used "
+ "in a non-global zone"));
+ (void) zfs_error(hdl, EZFS_ZONED, errbuf);
+ goto error;
+ }
+
+ if ((ret = changelist_prefix(cl)) != 0)
+ goto error;
+ }
if (ZFS_IS_VOLUME(zhp))
zc.zc_objset_type = DMU_OST_ZVOL;
@@ -3295,22 +3336,65 @@ zfs_rename(zfs_handle_t *zhp, const char *target)
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
(void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value));
+ zc.zc_cookie = recursive;
+
if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_RENAME, &zc)) != 0) {
- (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
+ /*
+ * if it was recursive, the one that actually failed will
+ * be in zc.zc_name
+ */
+ (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+ "cannot rename to '%s'"), zc.zc_name);
+
+ if (recursive && errno == EEXIST) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "a child dataset already has a snapshot "
+ "with the new name"));
+ (void) zfs_error(hdl, EZFS_CROSSTARGET, errbuf);
+ } else {
+ (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
+ }
/*
* On failure, we still want to remount any filesystems that
* were previously mounted, so we don't alter the system state.
*/
- (void) changelist_postfix(cl);
+ 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 {
- changelist_rename(cl, zfs_get_name(zhp), target);
-
- ret = changelist_postfix(cl);
+ 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);
+ }
}
error:
- changelist_free(cl);
+ if (parentname) {
+ free(parentname);
+ }
+ if (zhrp) {
+ zfs_close(zhrp);
+ }
+ if (cl) {
+ changelist_free(cl);
+ }
return (ret);
}
@@ -3321,6 +3405,12 @@ error:
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 };
di_devlink_handle_t dhdl;
@@ -3339,6 +3429,18 @@ zvol_create_link(libzfs_handle_t *hdl, const char *dataset)
*/
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 "
diff --git a/usr/src/uts/common/fs/zfs/dsl_dataset.c b/usr/src/uts/common/fs/zfs/dsl_dataset.c
index af06c316cf..43dba469d0 100644
--- a/usr/src/uts/common/fs/zfs/dsl_dataset.c
+++ b/usr/src/uts/common/fs/zfs/dsl_dataset.c
@@ -37,6 +37,7 @@
#include <sys/zap.h>
#include <sys/unique.h>
#include <sys/zfs_context.h>
+#include <sys/zfs_ioctl.h>
static dsl_checkfunc_t dsl_dataset_destroy_begin_check;
static dsl_syncfunc_t dsl_dataset_destroy_begin_sync;
@@ -639,7 +640,6 @@ dsl_dataset_create_sync(dsl_dir_t *pdd,
struct destroyarg {
dsl_sync_task_group_t *dstg;
char *snapname;
- void *tag;
char *failed;
};
@@ -655,7 +655,7 @@ dsl_snapshot_destroy_one(char *name, void *arg)
(void) strcat(name, da->snapname);
err = dsl_dataset_open(name,
DS_MODE_EXCLUSIVE | DS_MODE_READONLY | DS_MODE_INCONSISTENT,
- da->tag, &ds);
+ da->dstg, &ds);
cp = strchr(name, '@');
*cp = '\0';
if (err == ENOENT)
@@ -666,7 +666,7 @@ dsl_snapshot_destroy_one(char *name, void *arg)
}
dsl_sync_task_create(da->dstg, dsl_dataset_destroy_check,
- dsl_dataset_destroy_sync, ds, da->tag, 0);
+ dsl_dataset_destroy_sync, ds, da->dstg, 0);
return (0);
}
@@ -695,7 +695,6 @@ dsl_snapshots_destroy(char *fsname, char *snapname)
return (err);
da.dstg = dsl_sync_task_group_create(spa_get_dsl(spa));
da.snapname = snapname;
- da.tag = FTAG;
da.failed = fsname;
err = dmu_objset_find(fsname,
@@ -717,7 +716,7 @@ dsl_snapshots_destroy(char *fsname, char *snapname)
* closed the ds
*/
if (err)
- dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
+ dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, da.dstg);
}
dsl_sync_task_group_destroy(da.dstg);
@@ -1546,6 +1545,11 @@ dsl_dataset_snapshot_rename_check(void *arg1, void *arg2, dmu_tx_t *tx)
err = EEXIST;
else if (err == ENOENT)
err = 0;
+
+ /* dataset name + 1 for the "@" + the new snapshot name must fit */
+ if (dsl_dir_namelen(ds->ds_dir) + 1 + strlen(newsnapname) >= MAXNAMELEN)
+ err = ENAMETOOLONG;
+
return (err);
}
@@ -1578,9 +1582,115 @@ dsl_dataset_snapshot_rename_sync(void *arg1, void *arg2, dmu_tx_t *tx)
dsl_dataset_close(hds, DS_MODE_NONE, FTAG);
}
+struct renamearg {
+ dsl_sync_task_group_t *dstg;
+ char failed[MAXPATHLEN];
+ char *oldsnap;
+ char *newsnap;
+};
+
+static int
+dsl_snapshot_rename_one(char *name, void *arg)
+{
+ struct renamearg *ra = arg;
+ dsl_dataset_t *ds = NULL;
+ objset_t *os;
+ char *cp;
+ int err;
+
+ cp = name + strlen(name);
+ *cp = '@';
+ (void) strcpy(cp + 1, ra->oldsnap);
+ err = dsl_dataset_open(name, DS_MODE_READONLY | DS_MODE_STANDARD,
+ ra->dstg, &ds);
+ if (err == ENOENT) {
+ *cp = '\0';
+ return (0);
+ }
+ if (err) {
+ (void) strcpy(ra->failed, name);
+ *cp = '\0';
+ dsl_dataset_close(ds, DS_MODE_STANDARD, ra->dstg);
+ return (err);
+ }
+
+#ifdef _KERNEL
+ /* for all filesystems undergoing rename, we'll need to unmount it */
+ (void) zfs_unmount_snap(name, NULL);
+#endif
+
+ *cp = '\0';
+
+ dsl_sync_task_create(ra->dstg, dsl_dataset_snapshot_rename_check,
+ dsl_dataset_snapshot_rename_sync, ds, ra->newsnap, 0);
+
+ return (0);
+}
+
+static int
+dsl_recursive_rename(char *oldname, const char *newname)
+{
+ int err;
+ struct renamearg *ra;
+ dsl_sync_task_t *dst;
+ spa_t *spa;
+ char *cp, *fsname = spa_strdup(oldname);
+ int len = strlen(oldname);
+
+ /* truncate the snapshot name to get the fsname */
+ cp = strchr(fsname, '@');
+ *cp = '\0';
+
+ cp = strchr(fsname, '/');
+ if (cp) {
+ *cp = '\0';
+ err = spa_open(fsname, &spa, FTAG);
+ *cp = '/';
+ } else {
+ err = spa_open(fsname, &spa, FTAG);
+ }
+ if (err) {
+ kmem_free(fsname, len + 1);
+ return (err);
+ }
+ ra = kmem_alloc(sizeof (struct renamearg), KM_SLEEP);
+ ra->dstg = dsl_sync_task_group_create(spa_get_dsl(spa));
+
+ ra->oldsnap = strchr(oldname, '@') + 1;
+ ra->newsnap = strchr(newname, '@') + 1;
+ *ra->failed = '\0';
+
+ err = dmu_objset_find(fsname, dsl_snapshot_rename_one, ra,
+ DS_FIND_CHILDREN);
+ kmem_free(fsname, len + 1);
+
+ 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)) {
+ dsl_dataset_t *ds = dst->dst_arg1;
+ if (dst->dst_err) {
+ dsl_dir_name(ds->ds_dir, ra->failed);
+ strcat(ra->failed, "@");
+ strcat(ra->failed, ra->newsnap);
+ }
+ dsl_dataset_close(ds, DS_MODE_STANDARD, ra->dstg);
+ }
+
+ (void) strcpy(oldname, ra->failed);
+
+ dsl_sync_task_group_destroy(ra->dstg);
+ kmem_free(ra, sizeof (struct renamearg));
+ spa_close(spa, FTAG);
+ return (err);
+}
+
#pragma weak dmu_objset_rename = dsl_dataset_rename
int
-dsl_dataset_rename(const char *oldname, const char *newname)
+dsl_dataset_rename(char *oldname, const char *newname,
+ boolean_t recursive)
{
dsl_dir_t *dd;
dsl_dataset_t *ds;
@@ -1611,16 +1721,20 @@ dsl_dataset_rename(const char *oldname, const char *newname)
if (strncmp(oldname, newname, tail - newname) != 0)
return (EXDEV);
- err = dsl_dataset_open(oldname,
- DS_MODE_READONLY | DS_MODE_STANDARD, FTAG, &ds);
- if (err)
- return (err);
+ if (recursive) {
+ err = dsl_recursive_rename(oldname, newname);
+ } else {
+ err = dsl_dataset_open(oldname,
+ DS_MODE_READONLY | DS_MODE_STANDARD, FTAG, &ds);
+ if (err)
+ return (err);
- err = dsl_sync_task_do(ds->ds_dir->dd_pool,
- dsl_dataset_snapshot_rename_check,
- dsl_dataset_snapshot_rename_sync, ds, (char *)tail, 1);
+ err = dsl_sync_task_do(ds->ds_dir->dd_pool,
+ dsl_dataset_snapshot_rename_check,
+ dsl_dataset_snapshot_rename_sync, ds, (char *)tail, 1);
- dsl_dataset_close(ds, DS_MODE_STANDARD, FTAG);
+ dsl_dataset_close(ds, DS_MODE_STANDARD, FTAG);
+ }
return (err);
}
diff --git a/usr/src/uts/common/fs/zfs/sys/dmu.h b/usr/src/uts/common/fs/zfs/sys/dmu.h
index e07ca30234..a4eae81543 100644
--- a/usr/src/uts/common/fs/zfs/sys/dmu.h
+++ b/usr/src/uts/common/fs/zfs/sys/dmu.h
@@ -164,7 +164,8 @@ int dmu_objset_destroy(const char *name);
int dmu_snapshots_destroy(char *fsname, char *snapname);
int dmu_objset_rollback(const char *name);
int dmu_objset_snapshot(char *fsname, char *snapname, boolean_t recursive);
-int dmu_objset_rename(const char *name, const char *newname);
+int dmu_objset_rename(const char *name, const char *newname,
+ boolean_t recursive);
int dmu_objset_find(char *name, int func(char *, void *), void *arg,
int flags);
void dmu_objset_byteswap(void *buf, size_t size);
diff --git a/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h b/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h
index 8929dbc875..8cfc1dcc98 100644
--- a/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h
@@ -132,7 +132,7 @@ int dsl_snapshots_destroy(char *fsname, char *snapname);
dsl_checkfunc_t dsl_dataset_snapshot_check;
dsl_syncfunc_t dsl_dataset_snapshot_sync;
int dsl_dataset_rollback(dsl_dataset_t *ds);
-int dsl_dataset_rename(const char *name, const char *newname);
+int dsl_dataset_rename(char *name, const char *newname, boolean_t recursive);
int dsl_dataset_promote(const char *name);
void *dsl_dataset_set_user_ptr(dsl_dataset_t *ds,
diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h
index 016c0c9acc..765804c46e 100644
--- a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h
@@ -151,6 +151,7 @@ extern dev_info_t *zfs_dip;
extern int zfs_secpolicy_write(const char *dataset, cred_t *cr);
extern int zfs_busy(void);
+extern int zfs_unmount_snap(char *, void *);
#endif /* _KERNEL */
diff --git a/usr/src/uts/common/fs/zfs/zfs_ctldir.c b/usr/src/uts/common/fs/zfs/zfs_ctldir.c
index aaae4f7849..f48dd98436 100644
--- a/usr/src/uts/common/fs/zfs/zfs_ctldir.c
+++ b/usr/src/uts/common/fs/zfs/zfs_ctldir.c
@@ -539,7 +539,7 @@ zfsctl_snapdir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
return (ENOENT);
}
- err = dmu_objset_rename(from, to);
+ err = dmu_objset_rename(from, to, B_FALSE);
if (err == 0)
zfsctl_rename_snap(sdp, sep, tnm);
diff --git a/usr/src/uts/common/fs/zfs/zfs_ioctl.c b/usr/src/uts/common/fs/zfs/zfs_ioctl.c
index 987d3b5493..b5f8b0368d 100644
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c
@@ -1333,7 +1333,7 @@ zfs_ioc_snapshot(zfs_cmd_t *zc)
zc->zc_value, zc->zc_cookie));
}
-static int
+int
zfs_unmount_snap(char *name, void *arg)
{
char *snapname = arg;
@@ -1408,18 +1408,25 @@ zfs_ioc_rollback(zfs_cmd_t *zc)
static int
zfs_ioc_rename(zfs_cmd_t *zc)
{
+ int recursive = zc->zc_cookie & 1;
+
zc->zc_value[sizeof (zc->zc_value) - 1] = '\0';
if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0)
return (EINVAL);
- if (strchr(zc->zc_name, '@') != NULL &&
+ /*
+ * Unmount snapshot unless we're doing a recursive rename,
+ * in which case the dataset code figures out which snapshots
+ * to unmount.
+ */
+ if (!recursive && strchr(zc->zc_name, '@') != NULL &&
zc->zc_objset_type == DMU_OST_ZFS) {
int err = zfs_unmount_snap(zc->zc_name, NULL);
if (err)
return (err);
}
- return (dmu_objset_rename(zc->zc_name, zc->zc_value));
+ return (dmu_objset_rename(zc->zc_name, zc->zc_value, recursive));
}
static int