Mercurial > illumos > illumos-gate
changeset 4490:abf035049f7f
PSARC 2007/199 zfs {create,clone,rename} -p
6290249 zfs {create,clone,rename} -p to create parents
author | vb160487 |
---|---|
date | Tue, 19 Jun 2007 01:43:55 -0700 |
parents | 6b71f54fac33 |
children | efc4ff7c0669 |
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/lib/libzfs/common/mapfile-vers usr/src/uts/common/fs/zfs/zfs_ioctl.c |
diffstat | 5 files changed, 181 insertions(+), 42 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/cmd/zfs/zfs_main.c Tue Jun 19 01:36:56 2007 -0700 +++ b/usr/src/cmd/zfs/zfs_main.c Tue Jun 19 01:43:55 2007 -0700 @@ -161,11 +161,13 @@ { switch (idx) { case HELP_CLONE: - return (gettext("\tclone <snapshot> <filesystem|volume>\n")); + return (gettext("\tclone [-p] <snapshot> " + "<filesystem|volume>\n")); case HELP_CREATE: - return (gettext("\tcreate [[-o property=value] ... ] " + return (gettext("\tcreate [-p] [[-o property=value] ... ] " "<filesystem>\n" - "\tcreate [-s] [-b blocksize] [[-o property=value] ...]\n" + "\tcreate [-ps] [-b blocksize] [[-o property=value] " + "...]\n" "\t -V <size> <volume>\n")); case HELP_DESTROY: return (gettext("\tdestroy [-rRf] " @@ -197,6 +199,7 @@ case HELP_RENAME: return (gettext("\trename <filesystem|volume|snapshot> " "<filesystem|volume|snapshot>\n" + "\trename -p <filesystem|volume> <filesystem|volume>\n" "\trename -r <snapshot> <snapshot>")); case HELP_ROLLBACK: return (gettext("\trollback [-rRf] <snapshot>\n")); @@ -345,58 +348,86 @@ } /* - * zfs clone <fs, snap, vol> fs + * zfs clone [-p] <snap> <fs | vol> * * Given an existing dataset, create a writable copy whose initial contents * are the same as the source. The newly created dataset maintains a * dependency on the original; the original cannot be destroyed so long as * the clone exists. + * + * The '-p' flag creates all the non-existing ancestors of the target first. */ static int zfs_do_clone(int argc, char **argv) { zfs_handle_t *zhp; + boolean_t parents = B_FALSE; int ret; + int c; /* 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, "p")) != -1) { + switch (c) { + case 'p': + parents = B_TRUE; + break; + case '?': + (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); } /* open the source dataset */ - if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_SNAPSHOT)) == NULL) + if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) return (1); + if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM | + ZFS_TYPE_VOLUME)) { + /* + * Now create the ancestors of the target dataset. If the + * target already exists and '-p' option was used we should not + * complain. + */ + if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | + ZFS_TYPE_VOLUME)) + return (0); + if (zfs_create_ancestors(g_zfs, argv[1]) != 0) + return (1); + } + /* pass to libzfs */ - ret = zfs_clone(zhp, argv[2], NULL); + ret = zfs_clone(zhp, argv[1], NULL); /* create the mountpoint if necessary */ if (ret == 0) { - zfs_handle_t *clone = zfs_open(g_zfs, argv[2], ZFS_TYPE_ANY); + zfs_handle_t *clone = zfs_open(g_zfs, argv[1], ZFS_TYPE_ANY); if (clone != NULL) { if ((ret = zfs_mount(clone, NULL, 0)) == 0) ret = zfs_share(clone); zfs_close(clone); } - 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); @@ -405,8 +436,8 @@ } /* - * zfs create [-o prop=value] ... fs - * zfs create [-s] [-b blocksize] [-o prop=value] ... -V vol size + * zfs create [-p] [-o prop=value] ... fs + * zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size * * Create a new dataset. This command can be used to create filesystems * and volumes. Snapshot creation is handled by 'zfs snapshot'. @@ -415,6 +446,8 @@ * The '-s' flag applies only to volumes, and indicates that we should not try * to set the reservation for this volume. By default we set a reservation * equal to the size for any volume. + * + * The '-p' flag creates all the non-existing ancestors of the target first. */ static int zfs_do_create(int argc, char **argv) @@ -424,6 +457,7 @@ uint64_t volsize; int c; boolean_t noreserve = B_FALSE; + boolean_t parents = B_FALSE; int ret = 1; nvlist_t *props = NULL; uint64_t intval; @@ -438,7 +472,7 @@ } /* check options */ - while ((c = getopt(argc, argv, ":V:b:so:")) != -1) { + while ((c = getopt(argc, argv, ":V:b:so:p")) != -1) { switch (c) { case 'V': type = ZFS_TYPE_VOLUME; @@ -458,6 +492,9 @@ } volsize = intval; break; + case 'p': + parents = B_TRUE; + break; case 'b': if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { (void) fprintf(stderr, gettext("bad volume " @@ -543,6 +580,20 @@ } } + if (parents && zfs_name_valid(argv[0], type)) { + /* + * Now create the ancestors of target dataset. If the target + * already exists and '-p' option was used we should not + * complain. + */ + if (zfs_dataset_exists(g_zfs, argv[0], type)) { + ret = 0; + goto error; + } + if (zfs_create_ancestors(g_zfs, argv[0]) != 0) + goto error; + } + /* pass to libzfs */ if (zfs_create(g_zfs, argv[0], type, props) != 0) goto error; @@ -1476,9 +1527,13 @@ } /* - * zfs rename [-r] <fs | snap | vol> <fs | snap | vol> + * zfs rename <fs | snap | vol> <fs | snap | vol> + * zfs rename -p <fs | vol> <fs | vol> + * zfs rename -r <snap> <snap> * * Renames the given dataset to another of the same type. + * + * The '-p' flag creates all the non-existing ancestors of the target first. */ /* ARGSUSED */ static int @@ -1487,13 +1542,17 @@ zfs_handle_t *zhp; int c; int ret; - int recurse = 0; + boolean_t recurse = B_FALSE; + boolean_t parents = B_FALSE; /* check options */ - while ((c = getopt(argc, argv, "r")) != -1) { + while ((c = getopt(argc, argv, "pr")) != -1) { switch (c) { + case 'p': + parents = B_TRUE; + break; case 'r': - recurse = 1; + recurse = B_TRUE; break; case '?': default: @@ -1522,15 +1581,29 @@ usage(B_FALSE); } + if (recurse && parents) { + (void) fprintf(stderr, gettext("-p and -r options are mutually " + "exclusive\n")); + usage(B_FALSE); + } + 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) + if ((zhp = zfs_open(g_zfs, argv[0], parents ? ZFS_TYPE_FILESYSTEM | + ZFS_TYPE_VOLUME : ZFS_TYPE_ANY)) == NULL) return (1); + /* If we were asked and the name looks good, try to create ancestors. */ + if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) && + zfs_create_ancestors(g_zfs, argv[1]) != 0) { + zfs_close(zhp); + return (1); + } + ret = (zfs_rename(zhp, argv[1], recurse) != 0); if (!ret) @@ -1857,7 +1930,7 @@ static int zfs_do_snapshot(int argc, char **argv) { - int recursive = B_FALSE; + boolean_t recursive = B_FALSE; int ret; char c;
--- a/usr/src/lib/libzfs/common/libzfs.h Tue Jun 19 01:36:56 2007 -0700 +++ b/usr/src/lib/libzfs/common/libzfs.h Tue Jun 19 01:43:55 2007 -0700 @@ -343,12 +343,13 @@ */ extern int zfs_create(libzfs_handle_t *, const char *, zfs_type_t, nvlist_t *); +extern int zfs_create_ancestors(libzfs_handle_t *, const char *); extern int zfs_destroy(zfs_handle_t *); 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 *, int); +extern int zfs_rename(zfs_handle_t *, const char *, boolean_t); 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); @@ -363,6 +364,8 @@ extern int zfs_disable(zfs_handle_t *); extern int zfs_enable(zfs_handle_t *); extern zfs_handle_t *zfs_path_to_zhandle(libzfs_handle_t *, char *, zfs_type_t); +extern boolean_t zfs_dataset_exists(libzfs_handle_t *, const char *, + zfs_type_t); /* * Mount support functions.
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c Tue Jun 19 01:36:56 2007 -0700 +++ b/usr/src/lib/libzfs/common/libzfs_dataset.c Tue Jun 19 01:43:55 2007 -0700 @@ -51,6 +51,7 @@ #include "zfs_prop.h" #include "libzfs_impl.h" +static int create_parents(libzfs_handle_t *, char *, int); static int zvol_create_link_common(libzfs_handle_t *, const char *, int); /* @@ -1990,12 +1991,16 @@ } /* - * Checks to make sure that the given path has a parent, and that it exists. We - * also fetch the 'zoned' property, which is used to validate property settings - * when creating new datasets. + * If accept_ancestor is false, then check to make sure that the given path has + * a parent, and that it exists. If accept_ancestor is true, then find the + * closest existing ancestor for the given path. In prefixlen return the + * length of already existing prefix of the given path. We also fetch the + * 'zoned' property, which is used to validate property settings when creating + * new datasets. */ static int -check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned) +check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned, + boolean_t accept_ancestor, int *prefixlen) { zfs_cmd_t zc = { 0 }; char parent[ZFS_MAXNAMELEN]; @@ -2026,16 +2031,22 @@ } /* check to see if the parent dataset exists */ - if ((zhp = make_dataset_handle(hdl, parent)) == NULL) { - switch (errno) { - case ENOENT: + while ((zhp = make_dataset_handle(hdl, parent)) == NULL) { + if (errno == ENOENT && accept_ancestor) { + /* + * Go deeper to find an ancestor, give up on top level. + */ + if (parent_name(parent, parent, sizeof (parent)) != 0) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "no such pool '%s'"), zc.zc_name); + return (zfs_error(hdl, EZFS_NOENT, errbuf)); + } + } else if (errno == ENOENT) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "parent does not exist")); return (zfs_error(hdl, EZFS_NOENT, errbuf)); - - default: + } else return (zfs_standard_error(hdl, errno, errbuf)); - } } *zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); @@ -2056,6 +2067,56 @@ } zfs_close(zhp); + if (prefixlen != NULL) + *prefixlen = strlen(parent); + return (0); +} + +/* + * Finds whether the dataset of the given type(s) exists. + */ +boolean_t +zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types) +{ + zfs_handle_t *zhp; + + if (!zfs_validate_name(hdl, path, types)) + return (B_FALSE); + + /* + * Try to get stats for the dataset, which will tell us if it exists. + */ + if ((zhp = make_dataset_handle(hdl, path)) != NULL) { + int ds_type = zhp->zfs_type; + + zfs_close(zhp); + if (types & ds_type) + return (B_TRUE); + } + return (B_FALSE); +} + +/* + * Creates non-existing ancestors of the given path. + */ +int +zfs_create_ancestors(libzfs_handle_t *hdl, const char *path) +{ + int prefix; + uint64_t zoned; + char *path_copy; + int rc; + + if (check_parents(hdl, path, &zoned, B_TRUE, &prefix) != 0) + return (-1); + + if ((path_copy = strdup(path)) != NULL) { + rc = create_parents(hdl, path_copy, prefix); + free(path_copy); + } + if (path_copy == NULL || rc != 0) + return (-1); + return (0); } @@ -2081,7 +2142,7 @@ return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); /* validate parents exist */ - if (check_parents(hdl, path, &zoned) != 0) + if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0) return (-1); /* @@ -2092,7 +2153,7 @@ * first try to see if the dataset exists. */ (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name)); - if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0) { + if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_ANY)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "dataset already exists")); return (zfs_error(hdl, EZFS_EXISTS, errbuf)); @@ -2364,7 +2425,7 @@ return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); /* validate parents exist */ - if (check_parents(hdl, target, &zoned) != 0) + if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0) return (-1); (void) parent_name(target, parent, sizeof (parent)); @@ -2933,7 +2994,7 @@ /* Make sure destination fs does not exist */ *strchr(zc.zc_name, '@') = '\0'; - if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0) { + if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_ANY)) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "destination '%s' exists"), zc.zc_name); return (zfs_error(hdl, EZFS_EXISTS, errbuf)); @@ -3253,7 +3314,7 @@ * Renames the given dataset. */ int -zfs_rename(zfs_handle_t *zhp, const char *target, int recursive) +zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive) { int ret; zfs_cmd_t zc = { 0 }; @@ -3319,7 +3380,7 @@ uint64_t unused; /* validate parents */ - if (check_parents(hdl, target, &unused) != 0) + if (check_parents(hdl, target, &unused, B_FALSE, NULL) != 0) return (-1); (void) parent_name(target, parent, sizeof (parent));
--- a/usr/src/lib/libzfs/common/mapfile-vers Tue Jun 19 01:36:56 2007 -0700 +++ b/usr/src/lib/libzfs/common/mapfile-vers Tue Jun 19 01:43:55 2007 -0700 @@ -37,6 +37,8 @@ zfs_clone; zfs_close; zfs_create; + zfs_create_ancestors; + zfs_dataset_exists; zfs_destroy; zfs_destroy_snaps; zfs_expand_proplist;
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c Tue Jun 19 01:36:56 2007 -0700 +++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c Tue Jun 19 01:43:55 2007 -0700 @@ -1415,7 +1415,7 @@ static int zfs_ioc_rename(zfs_cmd_t *zc) { - int recursive = zc->zc_cookie & 1; + boolean_t recursive = zc->zc_cookie & 1; zc->zc_value[sizeof (zc->zc_value) - 1] = '\0'; if (dataset_namecheck(zc->zc_value, NULL, NULL) != 0)