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
Submitted by:	Wes Maag <jwmaag_gmail.com> (test additions by kevans)
MFC after:	1 week
Differential Revision:	https://reviews.freebsd.org/D18796
This commit is contained in:
Kyle Evans 2019-01-10 03:27:20 +00:00
parent aa52c719ea
commit 0a603a6ece
4 changed files with 162 additions and 47 deletions

View File

@ -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)
{
@ -58,6 +67,105 @@ be_mountcheck_cb(zfs_handle_t *zfs_hdl, void *data)
return (0);
}
/*
* 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
*/
@ -108,8 +216,10 @@ be_mount(libbe_handle_t *lbh, char *bootenv, char *mountpoint, int flags,
{
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 *mountpoint, int flags,
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 *mountpoint, int flags,
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 *mountpoint, int flags,
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,27 +279,17 @@ be_unmount(libbe_handle_t *lbh, char *bootenv, int flags)
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);
}

View File

@ -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:

View File

@ -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);
}

View File

@ -42,6 +42,20 @@ 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()
{
@ -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