usr/src/lib/libzfs/common/libzfs_dataset.c
changeset 2885 c0259887ebbc
parent 2856 6f4d5ee1906a
child 3126 4f4fb617fdd0
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c	Sun Oct 08 21:26:45 2006 -0700
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c	Mon Oct 09 10:56:01 2006 -0700
@@ -257,8 +257,7 @@
 		}
 	}
 
-	bcopy(&zc.zc_objset_stats, &zhp->zfs_dmustats,
-	    sizeof (zc.zc_objset_stats));
+	zhp->zfs_dmustats = zc.zc_objset_stats; /* structure assignment */
 
 	(void) strlcpy(zhp->zfs_root, zc.zc_value, sizeof (zhp->zfs_root));
 
@@ -274,8 +273,6 @@
 
 	zcmd_free_nvlists(&zc);
 
-	zhp->zfs_volstats = zc.zc_vol_stats;
-
 	if (process_user_props(zhp) != 0)
 		return (-1);
 
@@ -333,7 +330,7 @@
 
 		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
-		if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
+		if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) {
 			(void) zvol_remove_link(hdl, zhp->zfs_name);
 			zc.zc_objset_type = DMU_OST_ZVOL;
 		} else {
@@ -358,6 +355,13 @@
 	 * We've managed to open the dataset and gather statistics.  Determine
 	 * the high-level type.
 	 */
+	if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
+		zhp->zfs_head_type = ZFS_TYPE_VOLUME;
+	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
+		zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM;
+	else
+		abort();
+
 	if (zhp->zfs_dmustats.dds_is_snapshot)
 		zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
 	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
@@ -1235,10 +1239,11 @@
 	nvlist_t *nv;
 	uint64_t value;
 
+	*source = NULL;
 	if (nvlist_lookup_nvlist(zhp->zfs_props,
 	    zfs_prop_to_name(prop), &nv) == 0) {
 		verify(nvlist_lookup_uint64(nv, ZFS_PROP_VALUE, &value) == 0);
-		verify(nvlist_lookup_string(nv, ZFS_PROP_SOURCE, source) == 0);
+		(void) nvlist_lookup_string(nv, ZFS_PROP_SOURCE, source);
 	} else {
 		value = zfs_prop_default_numeric(prop);
 		*source = "";
@@ -1253,10 +1258,11 @@
 	nvlist_t *nv;
 	char *value;
 
+	*source = NULL;
 	if (nvlist_lookup_nvlist(zhp->zfs_props,
 	    zfs_prop_to_name(prop), &nv) == 0) {
 		verify(nvlist_lookup_string(nv, ZFS_PROP_VALUE, &value) == 0);
-		verify(nvlist_lookup_string(nv, ZFS_PROP_SOURCE, source) == 0);
+		(void) nvlist_lookup_string(nv, ZFS_PROP_SOURCE, source);
 	} else {
 		if ((value = (char *)zfs_prop_default_string(prop)) == NULL)
 			value = "";
@@ -1330,10 +1336,6 @@
 		}
 		break;
 
-	case ZFS_PROP_AVAILABLE:
-		*val = zhp->zfs_dmustats.dds_available;
-		break;
-
 	case ZFS_PROP_DEVICES:
 		*val = getprop_uint64(zhp, prop, source);
 
@@ -1365,6 +1367,14 @@
 	case ZFS_PROP_RECORDSIZE:
 	case ZFS_PROP_COMPRESSION:
 	case ZFS_PROP_ZONED:
+	case ZFS_PROP_CREATION:
+	case ZFS_PROP_COMPRESSRATIO:
+	case ZFS_PROP_REFERENCED:
+	case ZFS_PROP_USED:
+	case ZFS_PROP_CREATETXG:
+	case ZFS_PROP_AVAILABLE:
+	case ZFS_PROP_VOLSIZE:
+	case ZFS_PROP_VOLBLOCKSIZE:
 		*val = getprop_uint64(zhp, prop, source);
 		break;
 
@@ -1382,46 +1392,13 @@
 		}
 		break;
 
-	case ZFS_PROP_CREATION:
-		*val = zhp->zfs_dmustats.dds_creation_time;
-		break;
-
 	case ZFS_PROP_QUOTA:
-		if (zhp->zfs_dmustats.dds_quota == 0)
-			*source = "";	/* default */
-		else
-			*source = zhp->zfs_name;
-		*val = zhp->zfs_dmustats.dds_quota;
-		break;
-
 	case ZFS_PROP_RESERVATION:
-		if (zhp->zfs_dmustats.dds_reserved == 0)
+		*val = getprop_uint64(zhp, prop, source);
+		if (*val == 0)
 			*source = "";	/* default */
 		else
 			*source = zhp->zfs_name;
-		*val = zhp->zfs_dmustats.dds_reserved;
-		break;
-
-	case ZFS_PROP_COMPRESSRATIO:
-		/*
-		 * Using physical space and logical space, calculate the
-		 * compression ratio.  We return the number as a multiple of
-		 * 100, so '2.5x' would be returned as 250.
-		 */
-		if (zhp->zfs_dmustats.dds_compressed_bytes == 0)
-			*val = 100ULL;
-		else
-			*val =
-			    (zhp->zfs_dmustats.dds_uncompressed_bytes * 100 /
-			    zhp->zfs_dmustats.dds_compressed_bytes);
-		break;
-
-	case ZFS_PROP_REFERENCED:
-		/*
-		 * 'referenced' refers to the amount of physical space
-		 * referenced (possibly shared) by this object.
-		 */
-		*val = zhp->zfs_dmustats.dds_space_refd;
 		break;
 
 	case ZFS_PROP_SETUID:
@@ -1438,22 +1415,6 @@
 		}
 		break;
 
-	case ZFS_PROP_VOLSIZE:
-		*val = zhp->zfs_volstats.zv_volsize;
-		break;
-
-	case ZFS_PROP_VOLBLOCKSIZE:
-		*val = zhp->zfs_volstats.zv_volblocksize;
-		break;
-
-	case ZFS_PROP_USED:
-		*val = zhp->zfs_dmustats.dds_space_used;
-		break;
-
-	case ZFS_PROP_CREATETXG:
-		*val = zhp->zfs_dmustats.dds_creation_txg;
-		break;
-
 	case ZFS_PROP_MOUNTED:
 		*val = (zhp->zfs_mntopts != NULL);
 		break;
@@ -1577,17 +1538,15 @@
 		 * this into a string unless 'literal' is specified.
 		 */
 		{
-			time_t time = (time_t)
-			    zhp->zfs_dmustats.dds_creation_time;
+			val = getprop_uint64(zhp, prop, &source);
+			time_t time = (time_t)val;
 			struct tm t;
 
 			if (literal ||
 			    localtime_r(&time, &t) == NULL ||
 			    strftime(propbuf, proplen, "%a %b %e %k:%M %Y",
 			    &t) == 0)
-				(void) snprintf(propbuf, proplen, "%llu",
-				    (u_longlong_t)
-				    zhp->zfs_dmustats.dds_creation_time);
+				(void) snprintf(propbuf, proplen, "%llu", val);
 		}
 		break;
 
@@ -1637,7 +1596,7 @@
 		break;
 
 	case ZFS_PROP_ORIGIN:
-		(void) strlcpy(propbuf, zhp->zfs_dmustats.dds_clone_of,
+		(void) strlcpy(propbuf, getprop_string(zhp, prop, &source),
 		    proplen);
 		/*
 		 * If there is no parent at all, return failure to indicate that
@@ -2561,27 +2520,23 @@
  * Dumps a backup of tosnap, incremental from fromsnap if it isn't NULL.
  */
 int
-zfs_send(zfs_handle_t *zhp_to, zfs_handle_t *zhp_from)
+zfs_send(zfs_handle_t *zhp, const char *fromsnap)
 {
 	zfs_cmd_t zc = { 0 };
 	int ret;
 	char errbuf[1024];
-	libzfs_handle_t *hdl = zhp_to->zfs_hdl;
+	libzfs_handle_t *hdl = zhp->zfs_hdl;
 
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
-	    "cannot send '%s'"), zhp_to->zfs_name);
+	    "cannot send '%s'"), zhp->zfs_name);
 
 	/* do the ioctl() */
-	(void) strlcpy(zc.zc_name, zhp_to->zfs_name, sizeof (zc.zc_name));
-	if (zhp_from) {
-		(void) strlcpy(zc.zc_value, zhp_from->zfs_name,
-		    sizeof (zc.zc_name));
-	} else {
-		zc.zc_value[0] = '\0';
-	}
+	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+	if (fromsnap)
+		(void) strlcpy(zc.zc_value, fromsnap, sizeof (zc.zc_name));
 	zc.zc_cookie = STDOUT_FILENO;
 
-	ret = ioctl(zhp_to->zfs_hdl->libzfs_fd, ZFS_IOC_SENDBACKUP, &zc);
+	ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SENDBACKUP, &zc);
 	if (ret != 0) {
 		switch (errno) {
 
@@ -2613,6 +2568,73 @@
 }
 
 /*
+ * Create ancestors of 'target', but not target itself, and not
+ * ancestors whose names are shorter than prefixlen.  Die if
+ * prefixlen-ancestor does not exist.
+ */
+static int
+create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
+{
+	zfs_handle_t *h;
+	char *cp;
+
+	/* make sure prefix exists */
+	cp = strchr(target + prefixlen, '/');
+	*cp = '\0';
+	h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
+	*cp = '/';
+	if (h == NULL)
+		return (-1);
+	zfs_close(h);
+
+	/*
+	 * Attempt to create, mount, and share any ancestor filesystems,
+	 * up to the prefixlen-long one.
+	 */
+	for (cp = target + prefixlen + 1;
+	    cp = strchr(cp, '/'); *cp = '/', cp++) {
+		const char *opname;
+
+		*cp = '\0';
+
+		h = make_dataset_handle(hdl, target);
+		if (h) {
+			/* it already exists, nothing to do here */
+			zfs_close(h);
+			continue;
+		}
+
+		opname = dgettext(TEXT_DOMAIN, "create");
+		if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
+		    NULL) != 0)
+			goto ancestorerr;
+
+		opname = dgettext(TEXT_DOMAIN, "open");
+		h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
+		if (h == NULL)
+			goto ancestorerr;
+
+		opname = dgettext(TEXT_DOMAIN, "mount");
+		if (zfs_mount(h, NULL, 0) != 0)
+			goto ancestorerr;
+
+		opname = dgettext(TEXT_DOMAIN, "share");
+		if (zfs_share(h) != 0)
+			goto ancestorerr;
+
+		zfs_close(h);
+
+		continue;
+ancestorerr:
+		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+		    "failed to %s ancestor '%s'"), opname, target);
+		return (-1);
+	}
+
+	return (0);
+}
+
+/*
  * Restores a backup of tosnap from stdin.
  */
 int
@@ -2621,24 +2643,19 @@
 {
 	zfs_cmd_t zc = { 0 };
 	time_t begin_time;
-	int ioctl_err, err, bytes, size;
+	int ioctl_err, err, bytes, size, choplen;
 	char *cp;
 	dmu_replay_record_t drr;
 	struct drr_begin *drrb = &zc.zc_begin_record;
 	char errbuf[1024];
 	prop_changelist_t *clp;
+	char chopprefix[ZFS_MAXNAMELEN];
 
 	begin_time = time(NULL);
 
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 	    "cannot receive"));
 
-	/* trim off snapname, if any */
-	(void) strlcpy(zc.zc_name, tosnap, sizeof (zc.zc_name));
-	cp = strchr(zc.zc_name, '@');
-	if (cp)
-		*cp = '\0';
-
 	/* read in the BEGIN record */
 	cp = (char *)&drr;
 	bytes = 0;
@@ -2671,44 +2688,59 @@
 		return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
 	}
 
+	if (strchr(drr.drr_u.drr_begin.drr_toname, '@') == NULL) {
+		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
+			    "stream (bad snapshot name)"));
+		return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
+	}
 	/*
-	 * Determine name of destination snapshot.
+	 * Determine how much of the snapshot name stored in the stream
+	 * we are going to tack on to the name they specified on the
+	 * command line, and how much we are going to chop off.
+	 *
+	 * If they specified a snapshot, chop the entire name stored in
+	 * the stream.
 	 */
-	(void) strlcpy(zc.zc_value, tosnap, sizeof (zc.zc_value));
+	(void) strcpy(chopprefix, drr.drr_u.drr_begin.drr_toname);
 	if (isprefix) {
-		if (strchr(tosnap, '@') != NULL) {
-			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-			    "destination must be a filesystem"));
-			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
+		/*
+		 * They specified a fs with -d, we want to tack on
+		 * everything but the pool name stored in the stream
+		 */
+		if (strchr(tosnap, '@')) {
+			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
+			    "argument - snapshot not allowed with -d"));
+			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 		}
-
-		cp = strchr(drr.drr_u.drr_begin.drr_toname, '/');
+		cp = strchr(chopprefix, '/');
 		if (cp == NULL)
-			cp = drr.drr_u.drr_begin.drr_toname;
-		else
-			cp++;
-
-		(void) strcat(zc.zc_value, "/");
-		(void) strcat(zc.zc_value, cp);
+			cp = strchr(chopprefix, '@');
+		*cp = '\0';
 	} else if (strchr(tosnap, '@') == NULL) {
 		/*
-		 * they specified just a filesystem; tack on the
-		 * snapname from the backup.
+		 * If they specified a filesystem without -d, we want to
+		 * tack on everything after the fs specified in the
+		 * first name from the stream.
 		 */
-		cp = strchr(drr.drr_u.drr_begin.drr_toname, '@');
-		if (cp == NULL || strlen(tosnap) + strlen(cp) >= MAXNAMELEN)
-			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
-		(void) strcat(zc.zc_value, cp);
+		cp = strchr(chopprefix, '@');
+		*cp = '\0';
 	}
-
+	choplen = strlen(chopprefix);
+
+	/*
+	 * Determine name of destination snapshot, store in zc_value.
+	 */
+	(void) strcpy(zc.zc_value, tosnap);
+	(void) strncat(zc.zc_value, drr.drr_u.drr_begin.drr_toname+choplen,
+	    sizeof (zc.zc_value));
+
+	(void) strcpy(zc.zc_name, zc.zc_value);
 	if (drrb->drr_fromguid) {
-		zfs_handle_t *h;
 		/* incremental backup stream */
-
-		/* do the ioctl to the containing fs */
-		(void) strlcpy(zc.zc_name, zc.zc_value, sizeof (zc.zc_name));
-		cp = strchr(zc.zc_name, '@');
-		*cp = '\0';
+		zfs_handle_t *h;
+
+		/* do the recvbackup ioctl to the containing fs */
+		*strchr(zc.zc_name, '@') = '\0';
 
 		/* make sure destination fs exists */
 		h = zfs_open(hdl, zc.zc_name,
@@ -2737,87 +2769,32 @@
 	} else {
 		/* full backup stream */
 
-		(void) strlcpy(zc.zc_name, zc.zc_value, sizeof (zc.zc_name));
-
-		/* make sure they aren't trying to receive into the root */
-		if (strchr(zc.zc_name, '/') == NULL) {
-			cp = strchr(zc.zc_name, '@');
-			if (cp)
-				*cp = '\0';
-			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-			    "destination '%s' already exists"), zc.zc_name);
-			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
-		}
-
-		if (isprefix) {
-			zfs_handle_t *h;
-
-			/* make sure prefix exists */
-			h = zfs_open(hdl, tosnap, ZFS_TYPE_FILESYSTEM);
-			if (h == NULL)
-				return (-1);
-			zfs_close(h);
-
-			/* create any necessary ancestors up to prefix */
-			zc.zc_objset_type = DMU_OST_ZFS;
-
-			/*
-			 * zc.zc_name is now the full name of the snap
-			 * we're restoring into.  Attempt to create,
-			 * mount, and share any ancestor filesystems, up
-			 * to the one that was named.
-			 */
-			for (cp = zc.zc_name + strlen(tosnap) + 1;
-			    cp = strchr(cp, '/'); *cp = '/', cp++) {
-				const char *opname;
-				*cp = '\0';
-
-				opname = dgettext(TEXT_DOMAIN, "create");
-				if (zfs_create(hdl, zc.zc_name,
-				    ZFS_TYPE_FILESYSTEM, NULL) != 0) {
-					if (errno == EEXIST)
-						continue;
-					goto ancestorerr;
-				}
-
-				opname = dgettext(TEXT_DOMAIN, "open");
-				h = zfs_open(hdl, zc.zc_name,
-				    ZFS_TYPE_FILESYSTEM);
-				if (h == NULL)
-					goto ancestorerr;
-
-				opname = dgettext(TEXT_DOMAIN, "mount");
-				if (zfs_mount(h, NULL, 0) != 0)
-					goto ancestorerr;
-
-				opname = dgettext(TEXT_DOMAIN, "share");
-				if (zfs_share(h) != 0)
-					goto ancestorerr;
-
-				zfs_close(h);
-
-				continue;
-ancestorerr:
-				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-				    "failed to %s ancestor '%s'"), opname,
-				    zc.zc_name);
-				return (zfs_error(hdl, EZFS_BADRESTORE,
-				    errbuf));
-			}
-		}
-
 		/* Make sure destination fs does not exist */
-		cp = strchr(zc.zc_name, '@');
-		*cp = '\0';
+		*strchr(zc.zc_name, '@') = '\0';
 		if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "destination '%s' exists"), zc.zc_name);
 			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
 		}
 
+		if (strchr(zc.zc_name, '/') == NULL) {
+			/*
+			 * they're trying to do a recv into a
+			 * nonexistant topmost filesystem.
+			 */
+			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+			    "destination does not exist"), zc.zc_name);
+			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
+		}
+
 		/* Do the recvbackup ioctl to the fs's parent. */
-		cp = strrchr(zc.zc_name, '/');
-		*cp = '\0';
+		*strrchr(zc.zc_name, '/') = '\0';
+
+		if (isprefix && (err = create_parents(hdl,
+		    zc.zc_value, strlen(tosnap))) != 0) {
+			return (zfs_error(hdl, EZFS_BADRESTORE, errbuf));
+		}
+
 	}
 
 	zc.zc_cookie = STDIN_FILENO;