Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 21 Jan 2019 02:57:58 +0000 (UTC)
From:      Kyle Evans <kevans@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org
Subject:   svn commit: r343241 - in stable/12: lib/libbe sbin/bectl sbin/bectl/tests
Message-ID:  <201901210257.x0L2vwv0019892@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kevans
Date: Mon Jan 21 02:57:57 2019
New Revision: 343241
URL: https://svnweb.freebsd.org/changeset/base/343241

Log:
  MFC r342903, r342911: libbe(3)/bectl(8) refactor and fix mount for deep BEs
  
  r342903: libbe(3): move altroot augmentation bits around a little bit
  
  We could perhaps have a method that does this given a dataset, but it's yet
  clear that we'll always want to bypass the altroot when we grab the
  mountpoint. For now, we'll refactor things a bit so we grab the altroot
  length when libbe is initialized and have a common method that does the
  necessary augmentation (replace with / if it's the root, return a pointer to
  later in the string if not).
  
  This will be used in some upcoming work to make be_mount work properly for
  deep BEs.
  
  r342911: libbe(3): Change be_mount to mount/unmount child datasets
  
  This set of changes is geared towards making bectl respect deep boot
  environments when they exist and are mounted. The deep BE composition
  functionality (`bectl add`) remains disabled for the time being. This set of
  changes has no effect for the average user. but allows deep BE users to
  upgrade properly with their current setup.
  
  libbe(3): Open the target boot environment and get a zfs handle, then pass
  that with the target mountpoint to be_mount_iter; If the BE_MNT_DEEP flag is
  set call zfs_iter_filesystems and mount the child datasets.
  
  Similar logic is employed when unmounting the datasets, save for children
  are unmounted first.
  
  bectl(8): Change bectl_cmd_jail to pass the BE_MNT_DEEP flag when
  calling be_mount as well as call be_unmount when cleaning up after the
  jail has exited instead of umount(2) directly.
  
  PR:		234795

Modified:
  stable/12/lib/libbe/be.c
  stable/12/lib/libbe/be_access.c
  stable/12/lib/libbe/be_impl.h
  stable/12/sbin/bectl/bectl.c
  stable/12/sbin/bectl/bectl_jail.c
  stable/12/sbin/bectl/tests/bectl_test.sh
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/lib/libbe/be.c
==============================================================================
--- stable/12/lib/libbe/be.c	Mon Jan 21 01:45:35 2019	(r343240)
+++ stable/12/lib/libbe/be.c	Mon Jan 21 02:57:57 2019	(r343241)
@@ -90,6 +90,7 @@ be_locate_rootfs(libbe_handle_t *lbh)
 libbe_handle_t *
 libbe_init(const char *root)
 {
+	char altroot[MAXPATHLEN];
 	libbe_handle_t *lbh;
 	char *poolname, *pos;
 	int pnamelen;
@@ -140,6 +141,11 @@ libbe_init(const char *root)
 	    sizeof(lbh->bootfs), NULL, true) != 0)
 		goto err;
 
+	if (zpool_get_prop(lbh->active_phandle, ZPOOL_PROP_ALTROOT,
+	    altroot, sizeof(altroot), NULL, true) == 0 &&
+	    strcmp(altroot, "-") != 0)
+		lbh->altroot_len = strlen(altroot);
+
 	return (lbh);
 err:
 	if (lbh != NULL) {
@@ -314,7 +320,6 @@ be_create(libbe_handle_t *lbh, const char *name)
 	return (set_error(lbh, err));
 }
 
-
 static int
 be_deep_clone_prop(int prop, void *cb)
 {
@@ -345,12 +350,9 @@ be_deep_clone_prop(int prop, void *cb)
 
 	/* Augment mountpoint with altroot, if needed */
 	val = pval;
-	if (prop == ZFS_PROP_MOUNTPOINT && *dccb->altroot != '\0') {
-		if (pval[strlen(dccb->altroot)] == '\0')
-			strlcpy(pval, "/", sizeof(pval));
-		else
-			val = pval + strlen(dccb->altroot);
-	}
+	if (prop == ZFS_PROP_MOUNTPOINT)
+		val = be_mountpoint_augmented(dccb->lbh, val);
+
 	nvlist_add_string(dccb->props, zfs_prop_to_name(prop), val);
 
 	return (ZPROP_CONT);
@@ -392,12 +394,9 @@ be_deep_clone(zfs_handle_t *ds, void *data)
 	nvlist_alloc(&props, NV_UNIQUE_NAME, KM_SLEEP);
 	nvlist_add_string(props, "canmount", "noauto");
 
+	dccb.lbh = isdc->lbh;
 	dccb.zhp = ds;
 	dccb.props = props;
-	if (zpool_get_prop(isdc->lbh->active_phandle, ZPOOL_PROP_ALTROOT,
-	    dccb.altroot, sizeof(dccb.altroot), NULL, true) != 0 ||
-	    strcmp(dccb.altroot, "-") == 0)
-		*dccb.altroot = '\0';
 	if (zprop_iter(be_deep_clone_prop, &dccb, B_FALSE, B_FALSE,
 	    ZFS_TYPE_FILESYSTEM) == ZPROP_INVAL)
 		return (-1);

Modified: stable/12/lib/libbe/be_access.c
==============================================================================
--- stable/12/lib/libbe/be_access.c	Mon Jan 21 01:45:35 2019	(r343240)
+++ stable/12/lib/libbe/be_access.c	Mon Jan 21 02:57:57 2019	(r343241)
@@ -3,6 +3,7 @@
  *
  * Copyright (c) 2017 Kyle J. Kneitinger <kyle@kneit.in>
  * Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
+ * Copyright (c) 2019 Wes Maag <wes@jwmaag.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -38,6 +39,14 @@ struct be_mountcheck_info {
 	char *name;
 };
 
+struct be_mount_info {
+	libbe_handle_t *lbh;
+	const char *be;
+	const char *mountpoint;
+	int mntflags;
+	int deepmount;
+};
+
 static int
 be_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data)
 {
@@ -59,6 +68,105 @@ be_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data)
 }
 
 /*
+ * Called from be_mount, uses the given zfs_handle and attempts to
+ * mount it at the passed mountpoint. If the deepmount flag is set, continue
+ * calling the function for each child dataset.
+ */
+static int
+be_mount_iter(zfs_handle_t *zfs_hdl, void *data)
+{
+	int err;
+	char *mountpoint;
+	char tmp[BE_MAXPATHLEN], zfs_mnt[BE_MAXPATHLEN];
+	struct be_mount_info *info;
+
+	info = (struct be_mount_info *)data;
+
+	if (zfs_is_mounted(zfs_hdl, &mountpoint)) {
+		free(mountpoint);
+		return (0);
+	}
+
+	if (zfs_prop_get_int(zfs_hdl, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_OFF)
+		return (0);
+
+	if (zfs_prop_get(zfs_hdl, ZFS_PROP_MOUNTPOINT, zfs_mnt, BE_MAXPATHLEN,
+	    NULL, NULL, 0, 1))
+		return (1);
+
+	if (strcmp("none", zfs_mnt) != 0) {
+		char opt = '\0';
+
+		mountpoint = be_mountpoint_augmented(info->lbh, zfs_mnt);
+
+		snprintf(tmp, BE_MAXPATHLEN, "%s%s", info->mountpoint,
+		    mountpoint);
+
+		if ((err = zmount(zfs_get_name(zfs_hdl), tmp, info->mntflags,
+		 __DECONST(char *, MNTTYPE_ZFS), NULL, 0, &opt, 1)) != 0) {
+			switch (errno) {
+			case ENAMETOOLONG:
+				return (set_error(info->lbh, BE_ERR_PATHLEN));
+			case ELOOP:
+			case ENOENT:
+			case ENOTDIR:
+				return (set_error(info->lbh, BE_ERR_BADPATH));
+			case EPERM:
+				return (set_error(info->lbh, BE_ERR_PERMS));
+			case EBUSY:
+				return (set_error(info->lbh, BE_ERR_PATHBUSY));
+			default:
+				return (set_error(info->lbh, BE_ERR_UNKNOWN));
+			}
+		}
+	}
+
+	if (!info->deepmount)
+		return (0);
+
+	return (zfs_iter_filesystems(zfs_hdl, be_mount_iter, info));
+}
+
+
+static int
+be_umount_iter(zfs_handle_t *zfs_hdl, void *data)
+{
+
+	int err;
+	char *mountpoint;
+	struct be_mount_info *info;
+
+	info = (struct be_mount_info *)data;
+
+	if((err = zfs_iter_filesystems(zfs_hdl, be_umount_iter, info)) != 0) {
+		return (err);
+	}
+
+	if (!zfs_is_mounted(zfs_hdl, &mountpoint)) {
+		return (0);
+	}
+	free(mountpoint);
+
+	if (zfs_unmount(zfs_hdl, NULL, info->mntflags) != 0) {
+		switch (errno) {
+		case ENAMETOOLONG:
+			return (set_error(info->lbh, BE_ERR_PATHLEN));
+		case ELOOP:
+		case ENOENT:
+		case ENOTDIR:
+			return (set_error(info->lbh, BE_ERR_BADPATH));
+		case EPERM:
+			return (set_error(info->lbh, BE_ERR_PERMS));
+		case EBUSY:
+			return (set_error(info->lbh, BE_ERR_PATHBUSY));
+		default:
+			return (set_error(info->lbh, BE_ERR_UNKNOWN));
+		}
+	}
+	return (0);
+}
+
+/*
  * usage
  */
 int
@@ -108,8 +216,10 @@ be_mount(libbe_handle_t *lbh, char *bootenv, char *mou
 {
 	char be[BE_MAXPATHLEN];
 	char mnt_temp[BE_MAXPATHLEN];
-	int mntflags;
+	int mntflags, mntdeep;
 	int err;
+	struct be_mount_info info;
+	zfs_handle_t *zhdl;
 
 	if ((err = be_root_concat(lbh, bootenv, be)) != 0)
 		return (set_error(lbh, err));
@@ -120,6 +230,7 @@ be_mount(libbe_handle_t *lbh, char *bootenv, char *mou
 	if (is_mounted(lbh->lzh, be, NULL))
 		return (set_error(lbh, BE_ERR_MOUNTED));
 
+	mntdeep = (flags & BE_MNT_DEEP) ? 1 : 0;
 	mntflags = (flags & BE_MNT_FORCE) ? MNT_FORCE : 0;
 
 	/* Create mountpoint if it is not specified */
@@ -129,24 +240,20 @@ be_mount(libbe_handle_t *lbh, char *bootenv, char *mou
 			return (set_error(lbh, BE_ERR_IO));
 	}
 
-	char opt = '\0';
-	if ((err = zmount(be, (mountpoint == NULL) ? mnt_temp : mountpoint,
-	    mntflags, __DECONST(char *, MNTTYPE_ZFS), NULL, 0, &opt, 1)) != 0) {
-		switch (errno) {
-		case ENAMETOOLONG:
-			return (set_error(lbh, BE_ERR_PATHLEN));
-		case ELOOP:
-		case ENOENT:
-		case ENOTDIR:
-			return (set_error(lbh, BE_ERR_BADPATH));
-		case EPERM:
-			return (set_error(lbh, BE_ERR_PERMS));
-		case EBUSY:
-			return (set_error(lbh, BE_ERR_PATHBUSY));
-		default:
-			return (set_error(lbh, BE_ERR_UNKNOWN));
-		}
+	if ((zhdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL)
+		return (set_error(lbh, BE_ERR_ZFSOPEN));
+
+	info.lbh = lbh;
+	info.be = be;
+	info.mountpoint = (mountpoint == NULL) ? mnt_temp : mountpoint;
+	info.mntflags = mntflags;
+	info.deepmount = mntdeep;
+
+	if((err = be_mount_iter(zhdl, &info) != 0)) {
+		zfs_close(zhdl);
+		return (err);
 	}
+	zfs_close(zhdl);
 
 	if (result_loc != NULL)
 		strlcpy(result_loc, mountpoint == NULL ? mnt_temp : mountpoint,
@@ -155,16 +262,16 @@ be_mount(libbe_handle_t *lbh, char *bootenv, char *mou
 	return (BE_ERR_SUCCESS);
 }
 
-
 /*
  * usage
  */
 int
 be_unmount(libbe_handle_t *lbh, char *bootenv, int flags)
 {
-	int err, mntflags;
+	int err;
 	char be[BE_MAXPATHLEN];
 	zfs_handle_t *root_hdl;
+	struct be_mount_info info;
 
 	if ((err = be_root_concat(lbh, bootenv, be)) != 0)
 		return (set_error(lbh, err));
@@ -172,26 +279,38 @@ be_unmount(libbe_handle_t *lbh, char *bootenv, int fla
 	if ((root_hdl = zfs_open(lbh->lzh, be, ZFS_TYPE_FILESYSTEM)) == NULL)
 		return (set_error(lbh, BE_ERR_ZFSOPEN));
 
-	mntflags = (flags & BE_MNT_FORCE) ? MS_FORCE : 0;
+	info.lbh = lbh;
+	info.be = be;
+	info.mountpoint = NULL;
+	info.mntflags = (flags & BE_MNT_FORCE) ? MS_FORCE : 0;
 
-	if (zfs_unmount(root_hdl, NULL, mntflags) != 0) {
+	if ((err = be_umount_iter(root_hdl, &info)) != 0) {
 		zfs_close(root_hdl);
-		switch (errno) {
-		case ENAMETOOLONG:
-			return (set_error(lbh, BE_ERR_PATHLEN));
-		case ELOOP:
-		case ENOENT:
-		case ENOTDIR:
-			return (set_error(lbh, BE_ERR_BADPATH));
-		case EPERM:
-			return (set_error(lbh, BE_ERR_PERMS));
-		case EBUSY:
-			return (set_error(lbh, BE_ERR_PATHBUSY));
-		default:
-			return (set_error(lbh, BE_ERR_UNKNOWN));
-		}
+		return (err);
 	}
-	zfs_close(root_hdl);
 
+	zfs_close(root_hdl);
 	return (BE_ERR_SUCCESS);
+}
+
+/*
+ * This function will blow away the input buffer as needed if we're discovered
+ * to be looking at a root-mount.  If the mountpoint is naturally beyond the
+ * root, however, the buffer may be left intact and a pointer to the section
+ * past altroot will be returned instead for the caller's perusal.
+ */
+char *
+be_mountpoint_augmented(libbe_handle_t *lbh, char *mountpoint)
+{
+
+	if (lbh->altroot_len == 0)
+		return (mountpoint);
+	if (mountpoint == NULL || *mountpoint == '\0')
+		return (mountpoint);
+
+	if (mountpoint[lbh->altroot_len] == '\0') {
+		*(mountpoint + 1) = '\0';
+		return (mountpoint);
+	} else
+		return (mountpoint + lbh->altroot_len);
 }

Modified: stable/12/lib/libbe/be_impl.h
==============================================================================
--- stable/12/lib/libbe/be_impl.h	Mon Jan 21 01:45:35 2019	(r343240)
+++ stable/12/lib/libbe/be_impl.h	Mon Jan 21 02:57:57 2019	(r343241)
@@ -36,11 +36,12 @@
 #include "be.h"
 
 struct libbe_handle {
-	libzfs_handle_t *lzh;
-	zpool_handle_t *active_phandle;
 	char root[BE_MAXPATHLEN];
 	char rootfs[BE_MAXPATHLEN];
 	char bootfs[BE_MAXPATHLEN];
+	size_t altroot_len;
+	zpool_handle_t *active_phandle;
+	libzfs_handle_t *lzh;
 	be_error_t error;
 	bool print_on_err;
 };
@@ -53,9 +54,9 @@ struct libbe_deep_clone {
 };
 
 struct libbe_dccb {
+	libbe_handle_t *lbh;
 	zfs_handle_t *zhp;
 	nvlist_t *props;
-	char altroot[MAXPATHLEN];
 };
 
 typedef struct prop_data {
@@ -66,6 +67,8 @@ typedef struct prop_data {
 
 int prop_list_builder_cb(zfs_handle_t *, void *);
 int be_proplist_update(prop_data_t *);
+
+char *be_mountpoint_augmented(libbe_handle_t *lbh, char *mountpoint);
 
 /* Clobbers any previous errors */
 int set_error(libbe_handle_t *, be_error_t);

Modified: stable/12/sbin/bectl/bectl.c
==============================================================================
--- stable/12/sbin/bectl/bectl.c	Mon Jan 21 01:45:35 2019	(r343240)
+++ stable/12/sbin/bectl/bectl.c	Mon Jan 21 02:57:57 2019	(r343241)
@@ -378,8 +378,10 @@ bectl_cmd_mount(int argc, char *argv[])
 {
 	char result_loc[BE_MAXPATHLEN];
 	char *bootenv, *mountpoint;
-	int err;
+	int err, mntflags;
 
+	/* XXX TODO: Allow shallow */
+	mntflags = BE_MNT_DEEP;
 	if (argc < 2) {
 		fprintf(stderr, "bectl mount: missing argument(s)\n");
 		return (usage(false));
@@ -393,7 +395,7 @@ bectl_cmd_mount(int argc, char *argv[])
 	bootenv = argv[1];
 	mountpoint = ((argc == 3) ? argv[2] : NULL);
 
-	err = be_mount(be, bootenv, mountpoint, 0, result_loc);
+	err = be_mount(be, bootenv, mountpoint, mntflags, result_loc);
 
 	switch (err) {
 	case BE_ERR_SUCCESS:

Modified: stable/12/sbin/bectl/bectl_jail.c
==============================================================================
--- stable/12/sbin/bectl/bectl_jail.c	Mon Jan 21 01:45:35 2019	(r343240)
+++ stable/12/sbin/bectl/bectl_jail.c	Mon Jan 21 02:57:57 2019	(r343241)
@@ -180,10 +180,12 @@ int
 bectl_cmd_jail(int argc, char *argv[])
 {
 	char *bootenv, *mountpoint;
-	int jid, opt, ret;
+	int jid, mntflags, opt, ret;
 	bool default_hostname, interactive, unjail;
 	pid_t pid;
 
+	/* XXX TODO: Allow shallow */
+	mntflags = BE_MNT_DEEP;
 	default_hostname = interactive = unjail = true;
 	jpcnt = INIT_PARAMCOUNT;
 	jp = malloc(jpcnt * sizeof(*jp));
@@ -250,7 +252,7 @@ bectl_cmd_jail(int argc, char *argv[])
 		mountpoint = NULL;
 	else
 		mountpoint = mnt_loc;
-	if (be_mount(be, bootenv, mountpoint, 0, mnt_loc) != BE_ERR_SUCCESS) {
+	if (be_mount(be, bootenv, mountpoint, mntflags, mnt_loc) != BE_ERR_SUCCESS) {
 		fprintf(stderr, "could not mount bootenv\n");
 		return (1);
 	}
@@ -301,7 +303,7 @@ bectl_cmd_jail(int argc, char *argv[])
 
 	if (unjail) {
 		jail_remove(jid);
-		unmount(mnt_loc, 0);
+		be_unmount(be, bootenv, 0);
 	}
 
 	return (0);
@@ -415,7 +417,7 @@ bectl_cmd_unjail(int argc, char *argv[])
 	}
 
 	jail_remove(jid);
-	unmount(path, 0);
+	be_unmount(be, target, 0);
 
 	return (0);
 }

Modified: stable/12/sbin/bectl/tests/bectl_test.sh
==============================================================================
--- stable/12/sbin/bectl/tests/bectl_test.sh	Mon Jan 21 01:45:35 2019	(r343240)
+++ stable/12/sbin/bectl/tests/bectl_test.sh	Mon Jan 21 02:57:57 2019	(r343241)
@@ -42,7 +42,21 @@ bectl_create_setup()
 	atf_check zfs create -o mountpoint=/ -o canmount=noauto \
 	    ${zpool}/ROOT/default
 }
+bectl_create_deep_setup()
+{
+	zpool=$1
+	disk=$2
+	mnt=$3
 
+	bectl_create_setup ${zpool} ${disk} ${mnt}
+	atf_check mkdir -p ${root}
+	atf_check -o ignore bectl -r ${zpool}/ROOT mount default ${root}
+	atf_check mkdir -p ${root}/usr
+	atf_check zfs create -o mountpoint=/usr -o canmount=noauto \
+	    ${zpool}/ROOT/default/usr
+	atf_check -o ignore bectl -r ${zpool}/ROOT umount default
+}
+
 bectl_cleanup()
 {
 	zpool=$1
@@ -183,7 +197,7 @@ bectl_mount_body()
 	mount=${cwd}/mnt
 	root=${mount}/root
 
-	bectl_create_setup ${zpool} ${disk} ${mount}
+	bectl_create_deep_setup ${zpool} ${disk} ${mount}
 	atf_check mkdir -p ${root}
 	# Test unmount first...
 	atf_check -o not-empty bectl -r ${zpool}/ROOT mount default ${root}
@@ -246,7 +260,7 @@ bectl_jail_body()
 	if [ ! -f /rescue/rescue ]; then
 		atf_skip "This test requires a rescue binary"
 	fi
-	bectl_create_setup ${zpool} ${disk} ${mount}
+	bectl_create_deep_setup ${zpool} ${disk} ${mount}
 	# Prepare our minimal BE... plop a rescue binary into it
 	atf_check mkdir -p ${root}
 	atf_check -o ignore bectl -r ${zpool}/ROOT mount default ${root}
@@ -263,9 +277,9 @@ bectl_jail_body()
 	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail default
 
 	# Basic command-mode tests, with and without jail cleanup
-	atf_check -o inline:"rescue\n" bectl -r ${zpool}/ROOT \
+	atf_check -o inline:"rescue\nusr\n" bectl -r ${zpool}/ROOT \
 	    jail default /rescue/rescue ls -1
-	atf_check -o inline:"rescue\n" bectl -r ${zpool}/ROOT \
+	atf_check -o inline:"rescue\nusr\n" bectl -r ${zpool}/ROOT \
 	    jail -Uo path=${root} default /rescue/rescue ls -1
 	atf_check [ -f ${root}/rescue/rescue ]
 	atf_check bectl -r ${zpool}/ROOT ujail default



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201901210257.x0L2vwv0019892>