Mercurial > illumos > illumos-gate
changeset 4007:c6f5c6753018
PSARC 2007/142 zfs rename -r
6479884 want 'zfs rename -r' to recursively rename snapshots
author | mmusante |
---|---|
date | Wed, 11 Apr 2007 09:10:08 -0700 |
parents | ae098b2a8faa |
children | a95c00c4179a |
files | usr/src/cmd/zfs/zfs_main.c usr/src/lib/libzfs/common/libzfs.h usr/src/lib/libzfs/common/libzfs_dataset.c usr/src/uts/common/fs/zfs/dsl_dataset.c usr/src/uts/common/fs/zfs/sys/dmu.h usr/src/uts/common/fs/zfs/sys/dsl_dataset.h usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h usr/src/uts/common/fs/zfs/zfs_ctldir.c usr/src/uts/common/fs/zfs/zfs_ioctl.c |
diffstat | 9 files changed, 304 insertions(+), 59 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/cmd/zfs/zfs_main.c Tue Apr 10 16:14:57 2007 -0700 +++ b/usr/src/cmd/zfs/zfs_main.c Wed Apr 11 09:10:08 2007 -0700 @@ -196,7 +196,8 @@ "\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 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 @@ 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);
--- a/usr/src/lib/libzfs/common/libzfs.h Tue Apr 10 16:14:57 2007 -0700 +++ b/usr/src/lib/libzfs/common/libzfs.h Wed Apr 11 09:10:08 2007 -0700 @@ -335,7 +335,7 @@ 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);
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c Tue Apr 10 16:14:57 2007 -0700 +++ b/usr/src/lib/libzfs/common/libzfs_dataset.c Wed Apr 11 09:10:08 2007 -0700 @@ -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 @@ 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 @@ (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 @@ */ } - 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 @@ (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 @@ * 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 @@ 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,20 +3292,42 @@ return (zfs_error(hdl, EZFS_ZONED, errbuf)); } - 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 (recursive) { + struct destroydata dd; + + 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); + } + + 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 ((ret = changelist_prefix(cl)) != 0) - goto error; - if (ZFS_IS_VOLUME(zhp)) zc.zc_objset_type = DMU_OST_ZVOL; else @@ -3295,22 +3336,65 @@ (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 @@ 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 @@ */ 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 "
--- a/usr/src/uts/common/fs/zfs/dsl_dataset.c Tue Apr 10 16:14:57 2007 -0700 +++ b/usr/src/uts/common/fs/zfs/dsl_dataset.c Wed Apr 11 09:10:08 2007 -0700 @@ -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 @@ struct destroyarg { dsl_sync_task_group_t *dstg; char *snapname; - void *tag; char *failed; }; @@ -655,7 +655,7 @@ (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_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 @@ 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 @@ * 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 @@ 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_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 @@ 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); }
--- a/usr/src/uts/common/fs/zfs/sys/dmu.h Tue Apr 10 16:14:57 2007 -0700 +++ b/usr/src/uts/common/fs/zfs/sys/dmu.h Wed Apr 11 09:10:08 2007 -0700 @@ -164,7 +164,8 @@ 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);
--- a/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h Tue Apr 10 16:14:57 2007 -0700 +++ b/usr/src/uts/common/fs/zfs/sys/dsl_dataset.h Wed Apr 11 09:10:08 2007 -0700 @@ -132,7 +132,7 @@ 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,
--- a/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h Tue Apr 10 16:14:57 2007 -0700 +++ b/usr/src/uts/common/fs/zfs/sys/zfs_ioctl.h Wed Apr 11 09:10:08 2007 -0700 @@ -151,6 +151,7 @@ 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 */
--- a/usr/src/uts/common/fs/zfs/zfs_ctldir.c Tue Apr 10 16:14:57 2007 -0700 +++ b/usr/src/uts/common/fs/zfs/zfs_ctldir.c Wed Apr 11 09:10:08 2007 -0700 @@ -539,7 +539,7 @@ 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);
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c Tue Apr 10 16:14:57 2007 -0700 +++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c Wed Apr 11 09:10:08 2007 -0700 @@ -1333,7 +1333,7 @@ zc->zc_value, zc->zc_cookie)); } -static int +int zfs_unmount_snap(char *name, void *arg) { char *snapname = arg; @@ -1408,18 +1408,25 @@ 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