diff --git a/lib/libbe/be_access.c b/lib/libbe/be_access.c index e0d71a60b87f..af46bd900b71 100644 --- a/lib/libbe/be_access.c +++ b/lib/libbe/be_access.c @@ -3,6 +3,7 @@ * * Copyright (c) 2017 Kyle J. Kneitinger * Copyright (c) 2018 Kyle Evans + * Copyright (c) 2019 Wes Maag * 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); } diff --git a/sbin/bectl/bectl.c b/sbin/bectl/bectl.c index 92c13956f8c7..06e64cdda9d0 100644 --- a/sbin/bectl/bectl.c +++ b/sbin/bectl/bectl.c @@ -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: diff --git a/sbin/bectl/bectl_jail.c b/sbin/bectl/bectl_jail.c index a68855ca749d..fbab03b6b24a 100644 --- a/sbin/bectl/bectl_jail.c +++ b/sbin/bectl/bectl_jail.c @@ -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); } diff --git a/sbin/bectl/tests/bectl_test.sh b/sbin/bectl/tests/bectl_test.sh index 3ebbc84731a0..ecad748f5c0f 100755 --- a/sbin/bectl/tests/bectl_test.sh +++ b/sbin/bectl/tests/bectl_test.sh @@ -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