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)