svn commit: r344084 - head/lib/libbe

Kyle Evans kevans at FreeBSD.org
Wed Feb 13 04:19:10 UTC 2019


Author: kevans
Date: Wed Feb 13 04:19:08 2019
New Revision: 344084
URL: https://svnweb.freebsd.org/changeset/base/344084

Log:
  libbe(3): Fix be_destroy behavior w.r.t. deep BE snapshots and -o
  
  be_destroy is documented to recursively destroy a boot environment.  In the
  case of snapshots, one would take this to mean that these are also
  recursively destroyed.  However, this was previously not the case.
  be_destroy would descend into the be_destroy callback and attempt to
  zfs_iter_children on the top-level snapshot, which is bogus.
  
  Our alternative approach is to take note of the snapshot name and iterate
  through all of fs children of the BE to try destruction in the children.
  
  The -o option is also fixed to work properly with deep BEs.  If the BE was
  created with `bectl create -e otherDeepBE newDeepBE`, for instance, then a
  recursive snapshot of otherDeepBE would have been taken for construction of
  newDeepBE but a subsequent destroy with BE_DESTROY_ORIGIN set would only
  clean up the snapshot at the root of otherDeepBE: ${BEROOT}/otherDeepBE at ...
  
  The most recent iteration instead pretends not to know how these things
  work, verifies that the origin is another BE and then passes that back
  through be_destroy to DTRT when snapshots and deep BEs may be in play.
  
  MFC after:	1 week

Modified:
  head/lib/libbe/be.c
  head/lib/libbe/be.h
  head/lib/libbe/be_error.c
  head/lib/libbe/libbe.3

Modified: head/lib/libbe/be.c
==============================================================================
--- head/lib/libbe/be.c	Wed Feb 13 03:11:12 2019	(r344083)
+++ head/lib/libbe/be.c	Wed Feb 13 04:19:08 2019	(r344084)
@@ -45,6 +45,11 @@ __FBSDID("$FreeBSD$");
 #include "be.h"
 #include "be_impl.h"
 
+struct be_destroy_data {
+	libbe_handle_t		*lbh;
+	char			*snapname;
+};
+
 #if SOON
 static int be_create_child_noent(libbe_handle_t *lbh, const char *active,
     const char *child_path);
@@ -186,12 +191,38 @@ be_nicenum(uint64_t num, char *buf, size_t buflen)
 static int
 be_destroy_cb(zfs_handle_t *zfs_hdl, void *data)
 {
+	char path[BE_MAXPATHLEN];
+	struct be_destroy_data *bdd;
+	zfs_handle_t *snap;
 	int err;
 
-	if ((err = zfs_iter_children(zfs_hdl, be_destroy_cb, data)) != 0)
+	bdd = (struct be_destroy_data *)data;
+	if (bdd->snapname == NULL) {
+		err = zfs_iter_children(zfs_hdl, be_destroy_cb, data);
+		if (err != 0)
+			return (err);
+		return (zfs_destroy(zfs_hdl, false));
+	}
+	/* If we're dealing with snapshots instead, delete that one alone */
+	err = zfs_iter_filesystems(zfs_hdl, be_destroy_cb, data);
+	if (err != 0)
 		return (err);
-	if ((err = zfs_destroy(zfs_hdl, false)) != 0)
-		return (err);
+	/*
+	 * This part is intentionally glossing over any potential errors,
+	 * because there's a lot less potential for errors when we're cleaning
+	 * up snapshots rather than a full deep BE.  The primary error case
+	 * here being if the snapshot doesn't exist in the first place, which
+	 * the caller will likely deem insignificant as long as it doesn't
+	 * exist after the call.  Thus, such a missing snapshot shouldn't jam
+	 * up the destruction.
+	 */
+	snprintf(path, sizeof(path), "%s@%s", zfs_get_name(zfs_hdl),
+	    bdd->snapname);
+	if (!zfs_dataset_exists(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT))
+		return (0);
+	snap = zfs_open(bdd->lbh->lzh, path, ZFS_TYPE_SNAPSHOT);
+	if (snap != NULL)
+		zfs_destroy(snap, false);
 	return (0);
 }
 
@@ -199,22 +230,26 @@ be_destroy_cb(zfs_handle_t *zfs_hdl, void *data)
  * Destroy the boot environment or snapshot specified by the name
  * parameter. Options are or'd together with the possible values:
  * BE_DESTROY_FORCE : forces operation on mounted datasets
+ * BE_DESTROY_ORIGIN: destroy the origin snapshot as well
  */
 int
 be_destroy(libbe_handle_t *lbh, const char *name, int options)
 {
+	struct be_destroy_data bdd;
 	char origin[BE_MAXPATHLEN], path[BE_MAXPATHLEN];
 	zfs_handle_t *fs;
-	char *p;
+	char *snapdelim;
 	int err, force, mounted;
+	size_t rootlen;
 
-	p = path;
+	bdd.lbh = lbh;
+	bdd.snapname = NULL;
 	force = options & BE_DESTROY_FORCE;
 	*origin = '\0';
 
 	be_root_concat(lbh, name, path);
 
-	if (strchr(name, '@') == NULL) {
+	if ((snapdelim = strchr(path, '@')) == NULL) {
 		if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_FILESYSTEM))
 			return (set_error(lbh, BE_ERR_NOENT));
 
@@ -222,9 +257,10 @@ be_destroy(libbe_handle_t *lbh, const char *name, int 
 		    strcmp(path, lbh->bootfs) == 0)
 			return (set_error(lbh, BE_ERR_DESTROYACT));
 
-		fs = zfs_open(lbh->lzh, p, ZFS_TYPE_FILESYSTEM);
+		fs = zfs_open(lbh->lzh, path, ZFS_TYPE_FILESYSTEM);
 		if (fs == NULL)
 			return (set_error(lbh, BE_ERR_ZFSOPEN));
+
 		if ((options & BE_DESTROY_ORIGIN) != 0 &&
 		    zfs_prop_get(fs, ZFS_PROP_ORIGIN, origin, sizeof(origin),
 		    NULL, NULL, 0, 1) != 0)
@@ -233,40 +269,56 @@ be_destroy(libbe_handle_t *lbh, const char *name, int 
 		if (!zfs_dataset_exists(lbh->lzh, path, ZFS_TYPE_SNAPSHOT))
 			return (set_error(lbh, BE_ERR_NOENT));
 
-		fs = zfs_open(lbh->lzh, p, ZFS_TYPE_SNAPSHOT);
-		if (fs == NULL)
+		bdd.snapname = strdup(snapdelim + 1);
+		if (bdd.snapname == NULL)
+			return (set_error(lbh, BE_ERR_NOMEM));
+		*snapdelim = '\0';
+		fs = zfs_open(lbh->lzh, path, ZFS_TYPE_DATASET);
+		if (fs == NULL) {
+			free(bdd.snapname);
 			return (set_error(lbh, BE_ERR_ZFSOPEN));
+		}
 	}
 
 	/* Check if mounted, unmount if force is specified */
 	if ((mounted = zfs_is_mounted(fs, NULL)) != 0) {
-		if (force)
+		if (force) {
 			zfs_unmount(fs, NULL, 0);
-		else
+		} else {
+			free(bdd.snapname);
 			return (set_error(lbh, BE_ERR_DESTROYMNT));
+		}
 	}
 
-	if ((err = be_destroy_cb(fs, NULL)) != 0) {
+	err = be_destroy_cb(fs, &bdd);
+	zfs_close(fs);
+	free(bdd.snapname);
+	if (err != 0) {
 		/* Children are still present or the mount is referenced */
 		if (err == EBUSY)
 			return (set_error(lbh, BE_ERR_DESTROYMNT));
 		return (set_error(lbh, BE_ERR_UNKNOWN));
 	}
 
-	if (*origin != '\0') {
-		fs = zfs_open(lbh->lzh, origin, ZFS_TYPE_SNAPSHOT);
-		if (fs == NULL)
-			return (set_error(lbh, BE_ERR_ZFSOPEN));
-		err = zfs_destroy(fs, false);
-		if (err == EBUSY)
-			return (set_error(lbh, BE_ERR_DESTROYMNT));
-		else if (err != 0)
-			return (set_error(lbh, BE_ERR_UNKNOWN));
-	}
+	if ((options & BE_DESTROY_ORIGIN) == 0)
+		return (0);
 
-	return (0);
-}
+	/* The origin can't possibly be shorter than the BE root */
+	rootlen = strlen(lbh->root);
+	if (*origin == '\0' || strlen(origin) <= rootlen + 1)
+		return (set_error(lbh, BE_ERR_INVORIGIN));
 
+	/*
+	 * We'll be chopping off the BE root and running this back through
+	 * be_destroy, so that we properly handle the origin snapshot whether
+	 * it be that of a deep BE or not.
+	 */
+	if (strncmp(origin, lbh->root, rootlen) != 0 || origin[rootlen] != '/')
+		return (0);
+
+	return (be_destroy(lbh, origin + rootlen + 1,
+	    options & ~BE_DESTROY_ORIGIN));
+}
 
 int
 be_snapshot(libbe_handle_t *lbh, const char *source, const char *snap_name,

Modified: head/lib/libbe/be.h
==============================================================================
--- head/lib/libbe/be.h	Wed Feb 13 03:11:12 2019	(r344083)
+++ head/lib/libbe/be.h	Wed Feb 13 04:19:08 2019	(r344084)
@@ -59,6 +59,7 @@ typedef enum be_error {
 	BE_ERR_NOPOOL,		/* operation not supported on this pool */
 	BE_ERR_NOMEM,		/* insufficient memory */
 	BE_ERR_UNKNOWN,         /* unknown error */
+	BE_ERR_INVORIGIN,       /* invalid origin */
 } be_error_t;
 
 

Modified: head/lib/libbe/be_error.c
==============================================================================
--- head/lib/libbe/be_error.c	Wed Feb 13 03:11:12 2019	(r344083)
+++ head/lib/libbe/be_error.c	Wed Feb 13 04:19:08 2019	(r344084)
@@ -105,6 +105,9 @@ libbe_error_description(libbe_handle_t *lbh)
 	case BE_ERR_UNKNOWN:
 		return ("unknown error");
 
+	case BE_ERR_INVORIGIN:
+		return ("invalid origin");
+
 	default:
 		assert(lbh->error == BE_ERR_SUCCESS);
 		return ("no error");

Modified: head/lib/libbe/libbe.3
==============================================================================
--- head/lib/libbe/libbe.3	Wed Feb 13 03:11:12 2019	(r344083)
+++ head/lib/libbe/libbe.3	Wed Feb 13 04:19:08 2019	(r344084)
@@ -28,7 +28,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd February 11, 2019
+.Dd February 12, 2019
 .Dt LIBBE 3
 .Os
 .Sh NAME
@@ -489,6 +489,8 @@ BE_ERR_NOPOOL
 BE_ERR_NOMEM
 .It
 BE_ERR_UNKNOWN
+.It
+BE_ERR_INVORIGIN
 .El
 .Sh SEE ALSO
 .Xr bectl 8


More information about the svn-src-all mailing list