Skip to content

Commit

Permalink
libbe(3): Change be_mount to mount/unmount child datasets
Browse files Browse the repository at this point in the history
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
  • Loading branch information
kevans91 committed Jan 10, 2019
1 parent aa52c71 commit 0a603a6
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 47 deletions.
171 changes: 134 additions & 37 deletions lib/libbe/be_access.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*
* Copyright (c) 2017 Kyle J. Kneitinger <[email protected]>
* Copyright (c) 2018 Kyle Evans <[email protected]>
* Copyright (c) 2019 Wes Maag <[email protected]>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -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)
{
Expand All @@ -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
*/
Expand Down Expand Up @@ -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));
Expand All @@ -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 */
Expand All @@ -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,
Expand All @@ -155,44 +262,34 @@ 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));

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

Expand Down
6 changes: 4 additions & 2 deletions sbin/bectl/bectl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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:
Expand Down
10 changes: 6 additions & 4 deletions sbin/bectl/bectl_jail.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -415,7 +417,7 @@ bectl_cmd_unjail(int argc, char *argv[])
}

jail_remove(jid);
unmount(path, 0);
be_unmount(be, target, 0);

return (0);
}
22 changes: 18 additions & 4 deletions sbin/bectl/tests/bectl_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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}
Expand All @@ -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
Expand Down

0 comments on commit 0a603a6

Please sign in to comment.