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:
parent
aa52c719ea
commit
0a603a6ece
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user