#include <assert.h>
#include <errno.h>
#include <libgen.h>
#include <libintl.h>
#include <libnvpair.h>
#include <libzfs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mntent.h>
#include <sys/mnttab.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/vfstab.h>
#include <sys/zone.h>
#include <sys/mkdev.h>
#include <unistd.h>
#include <libbe.h>
#include <libbe_priv.h>
#define BE_TMP_MNTPNT "/tmp/.be.XXXXXX"
typedef struct dir_data {
char *dir;
char *ds;
} dir_data_t;
static int be_mount_callback(zfs_handle_t *, void *);
static int be_unmount_callback(zfs_handle_t *, void *);
static int be_get_legacy_fs_callback(zfs_handle_t *, void *);
static int fix_mountpoint(zfs_handle_t *);
static int fix_mountpoint_callback(zfs_handle_t *, void *);
static int get_mountpoint_from_vfstab(char *, const char *, char *, size_t,
boolean_t);
static int loopback_mount_shared_fs(zfs_handle_t *, be_mount_data_t *);
static int loopback_mount_zonepath(const char *, be_mount_data_t *);
static int iter_shared_fs_callback(zfs_handle_t *, void *);
static int zpool_shared_fs_callback(zpool_handle_t *, void *);
static int unmount_shared_fs(be_unmount_data_t *);
static int add_to_fs_list(be_fs_list_data_t *, const char *);
static int be_mount_root(zfs_handle_t *, char *);
static int be_unmount_root(zfs_handle_t *, be_unmount_data_t *);
static int be_mount_zones(zfs_handle_t *, be_mount_data_t *);
static int be_unmount_zones(be_unmount_data_t *);
static int be_mount_one_zone(zfs_handle_t *, be_mount_data_t *, char *, char *,
char *);
static int be_unmount_one_zone(be_unmount_data_t *, char *, char *, char *);
static int be_get_ds_from_dir_callback(zfs_handle_t *, void *);
static int mount_zfs(zfs_handle_t *, char *);
int
be_mount(nvlist_t *be_attrs)
{
char *be_name = NULL;
char *mountpoint = NULL;
uint16_t flags = 0;
int ret = BE_SUCCESS;
if (!be_zfs_init())
return (BE_ERR_INIT);
if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
!= 0) {
be_print_err(gettext("be_mount: failed to lookup "
"BE_ATTR_ORIG_BE_NAME attribute\n"));
return (BE_ERR_INVAL);
}
if (!be_valid_be_name(be_name)) {
be_print_err(gettext("be_mount: invalid BE name %s\n"),
be_name);
return (BE_ERR_INVAL);
}
if (nvlist_lookup_string(be_attrs, BE_ATTR_MOUNTPOINT, &mountpoint)
!= 0) {
be_print_err(gettext("be_mount: failed to lookup "
"BE_ATTR_MOUNTPOINT attribute\n"));
return (BE_ERR_INVAL);
}
if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
BE_ATTR_MOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) {
be_print_err(gettext("be_mount: failed to lookup "
"BE_ATTR_MOUNT_FLAGS attribute\n"));
return (BE_ERR_INVAL);
}
ret = _be_mount(be_name, &mountpoint, flags);
be_zfs_fini();
return (ret);
}
int
be_unmount(nvlist_t *be_attrs)
{
char *be_name = NULL;
char *be_name_mnt = NULL;
char *ds = NULL;
uint16_t flags = 0;
int ret = BE_SUCCESS;
if (!be_zfs_init())
return (BE_ERR_INIT);
if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
!= 0) {
be_print_err(gettext("be_unmount: failed to lookup "
"BE_ATTR_ORIG_BE_NAME attribute\n"));
return (BE_ERR_INVAL);
}
if (be_name[0] == '/') {
if ((ds = be_get_ds_from_dir(be_name)) != NULL) {
if ((be_name_mnt = strrchr(ds, '/')) != NULL) {
be_name = be_name_mnt + 1;
}
} else {
be_print_err(gettext("be_unmount: no datasets mounted "
"at '%s'\n"), be_name);
return (BE_ERR_INVAL);
}
}
if (!be_valid_be_name(be_name)) {
be_print_err(gettext("be_unmount: invalid BE name %s\n"),
be_name);
return (BE_ERR_INVAL);
}
if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
BE_ATTR_UNMOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) {
be_print_err(gettext("be_unmount: failed to loookup "
"BE_ATTR_UNMOUNT_FLAGS attribute\n"));
return (BE_ERR_INVAL);
}
ret = _be_unmount(be_name, flags);
be_zfs_fini();
return (ret);
}
int
_be_mount(char *be_name, char **altroot, int flags)
{
be_transaction_data_t bt = { 0 };
be_mount_data_t md = { 0 };
zfs_handle_t *zhp;
char obe_root_ds[MAXPATHLEN];
char *mp = NULL;
char *tmp_altroot = NULL;
int ret = BE_SUCCESS, err = 0;
uuid_t uu = { 0 };
boolean_t gen_tmp_altroot = B_FALSE;
if (be_name == NULL || altroot == NULL)
return (BE_ERR_INVAL);
bt.obe_name = be_name;
if ((err = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
be_print_err(gettext("be_mount: failed to "
"find zpool for BE (%s)\n"), bt.obe_name);
return (BE_ERR_BE_NOENT);
} else if (err < 0) {
be_print_err(gettext("be_mount: zpool_iter failed: %s\n"),
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if ((ret = be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
sizeof (obe_root_ds))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container dataset "
"for %s/%s\n"), __func__, bt.obe_zpool, bt.obe_name);
return (ret);
}
bt.obe_root_ds = obe_root_ds;
if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
NULL) {
be_print_err(gettext("be_mount: failed to "
"open BE root dataset (%s): %s\n"), bt.obe_root_ds,
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if (zfs_is_mounted(zhp, &mp)) {
ZFS_CLOSE(zhp);
be_print_err(gettext("be_mount: %s is already mounted "
"at %s\n"), bt.obe_name, mp != NULL ? mp : "");
free(mp);
return (BE_ERR_MOUNTED);
}
if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) {
be_print_err(gettext("be_mount: mountpoint check "
"failed for %s\n"), bt.obe_root_ds);
ZFS_CLOSE(zhp);
return (ret);
}
if (*altroot == NULL) {
if ((ret = be_make_tmp_mountpoint(&tmp_altroot))
!= BE_SUCCESS) {
be_print_err(gettext("be_mount: failed to "
"make temporary mountpoint\n"));
ZFS_CLOSE(zhp);
return (ret);
}
gen_tmp_altroot = B_TRUE;
} else {
tmp_altroot = *altroot;
}
md.altroot = tmp_altroot;
md.shared_fs = flags & BE_MOUNT_FLAG_SHARED_FS;
md.shared_rw = flags & BE_MOUNT_FLAG_SHARED_RW;
if (getzoneid() == GLOBAL_ZONEID) {
if ((ret = be_mount_root(zhp, tmp_altroot)) != BE_SUCCESS) {
be_print_err(gettext("be_mount: failed to "
"mount BE root file system\n"));
if (gen_tmp_altroot)
free(tmp_altroot);
ZFS_CLOSE(zhp);
return (ret);
}
} else {
if ((ret = be_mount_zone_root(zhp, &md)) != BE_SUCCESS) {
be_print_err(gettext("be_mount: failed to "
"mount BE zone root file system\n"));
free(md.altroot);
ZFS_CLOSE(zhp);
return (ret);
}
}
if ((err = zfs_iter_filesystems(zhp, be_mount_callback,
tmp_altroot)) != 0) {
be_print_err(gettext("be_mount: failed to "
"mount BE (%s) on %s\n"), bt.obe_name, tmp_altroot);
if (gen_tmp_altroot)
free(tmp_altroot);
ZFS_CLOSE(zhp);
return (err);
}
if (md.shared_fs) {
(void) zpool_iter(g_zfs, zpool_shared_fs_callback, &md);
}
if (getzoneid() == GLOBAL_ZONEID &&
!(flags & BE_MOUNT_FLAG_NO_ZONES) &&
be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) {
if (be_mount_zones(zhp, &md) != BE_SUCCESS) {
ret = BE_ERR_NO_MOUNTED_ZONE;
}
}
ZFS_CLOSE(zhp);
if (gen_tmp_altroot) {
if (ret == BE_SUCCESS || ret == BE_ERR_NO_MOUNTED_ZONE)
*altroot = tmp_altroot;
else
free(tmp_altroot);
}
return (ret);
}
int
_be_unmount(char *be_name, int flags)
{
be_transaction_data_t bt = { 0 };
be_unmount_data_t ud = { 0 };
zfs_handle_t *zhp;
uuid_t uu = { 0 };
char obe_root_ds[MAXPATHLEN];
char mountpoint[MAXPATHLEN];
char *mp = NULL;
int ret = BE_SUCCESS;
int zret = 0;
if (be_name == NULL)
return (BE_ERR_INVAL);
bt.obe_name = be_name;
if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
be_print_err(gettext("be_unmount: failed to "
"find zpool for BE (%s)\n"), bt.obe_name);
return (BE_ERR_BE_NOENT);
} else if (zret < 0) {
be_print_err(gettext("be_unmount: "
"zpool_iter failed: %s\n"),
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
return (ret);
}
if ((ret = be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds,
sizeof (obe_root_ds))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container dataset "
"for %s/%s\n"), __func__, bt.obe_zpool, bt.obe_name);
return (ret);
}
bt.obe_root_ds = obe_root_ds;
if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
NULL) {
be_print_err(gettext("be_unmount: failed to "
"open BE root dataset (%s): %s\n"), bt.obe_root_ds,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
return (ret);
}
if (!zfs_is_mounted(zhp, &mp)) {
be_print_err(gettext("be_unmount: "
"(%s) not mounted\n"), bt.obe_name);
if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) {
be_print_err(gettext("be_unmount: mountpoint check "
"failed for %s\n"), bt.obe_root_ds);
ZFS_CLOSE(zhp);
return (ret);
}
ZFS_CLOSE(zhp);
return (BE_ERR_NOTMOUNTED);
}
if (mp == NULL) {
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
be_print_err(gettext("be_unmount: failed to "
"get mountpoint of (%s)\n"), bt.obe_name);
ZFS_CLOSE(zhp);
return (BE_ERR_ZFS);
}
} else {
(void) strlcpy(mountpoint, mp, sizeof (mountpoint));
free(mp);
}
if (strcmp(mountpoint, "/") == 0) {
be_print_err(gettext("be_unmount: "
"cannot unmount currently running BE\n"));
ZFS_CLOSE(zhp);
return (BE_ERR_UMOUNT_CURR_BE);
}
ud.altroot = mountpoint;
ud.force = flags & BE_UNMOUNT_FLAG_FORCE;
if (getzoneid() == GLOBAL_ZONEID &&
be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) {
if ((ret = be_unmount_zones(&ud)) != BE_SUCCESS) {
ZFS_CLOSE(zhp);
return (ret);
}
}
if ((ret = unmount_shared_fs(&ud)) != BE_SUCCESS) {
be_print_err(gettext("be_unmount: failed to "
"unmount shared file systems\n"));
ZFS_CLOSE(zhp);
return (ret);
}
if ((zret = zfs_iter_filesystems(zhp, be_unmount_callback,
&ud)) != 0) {
be_print_err(gettext("be_unmount: failed to "
"unmount BE (%s)\n"), bt.obe_name);
ZFS_CLOSE(zhp);
return (zret);
}
if (getzoneid() == GLOBAL_ZONEID) {
if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) {
ZFS_CLOSE(zhp);
return (ret);
}
} else {
if ((ret = be_unmount_zone_root(zhp, &ud)) != BE_SUCCESS) {
ZFS_CLOSE(zhp);
return (ret);
}
}
ZFS_CLOSE(zhp);
return (BE_SUCCESS);
}
int
be_mount_zone_root(zfs_handle_t *zhp, be_mount_data_t *md)
{
struct stat buf;
char mountpoint[MAXPATHLEN];
int err = 0;
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
be_print_err(gettext("be_mount_zone_root: failed to "
"get mountpoint property for %s: %s\n"), zfs_get_name(zhp),
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
be_print_err(gettext("be_mount_zone_root: "
"zone root dataset mountpoint is not 'legacy'\n"));
return (BE_ERR_ZONE_ROOT_NOT_LEGACY);
}
if (lstat(md->altroot, &buf) != 0) {
if (mkdirp(md->altroot, 0755) != 0) {
err = errno;
be_print_err(gettext("be_mount_zone_root: failed "
"to create mountpoint %s\n"), md->altroot);
return (errno_to_be_err(err));
}
}
if (mount(zfs_get_name(zhp), md->altroot, MS_OVERLAY, MNTTYPE_ZFS,
NULL, 0, NULL, 0) != 0) {
err = errno;
be_print_err(gettext("be_mount_zone_root: failed to "
"legacy mount zone root dataset (%s) at %s\n"),
zfs_get_name(zhp), md->altroot);
return (errno_to_be_err(err));
}
return (BE_SUCCESS);
}
int
be_unmount_zone_root(zfs_handle_t *zhp, be_unmount_data_t *ud)
{
char mountpoint[MAXPATHLEN];
if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
be_print_err(gettext("be_unmount_zone_root: failed to "
"unmount zone root dataset %s: %s\n"), zfs_get_name(zhp),
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
be_print_err(gettext("be_unmount_zone_root: failed to "
"get mountpoint property for zone root dataset (%s): %s\n"),
zfs_get_name(zhp), libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
ZFS_MOUNTPOINT_LEGACY) != 0) {
be_print_err(gettext("be_unmount_zone_root: "
"failed to set mountpoint of zone root dataset "
"%s to 'legacy': %s\n"), zfs_get_name(zhp),
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
}
return (BE_SUCCESS);
}
int
be_get_legacy_fs(char *be_name, char *be_root_ds, char *zoneroot_ds,
char *zoneroot, be_fs_list_data_t *fld)
{
zfs_handle_t *zhp = NULL;
char mountpoint[MAXPATHLEN];
boolean_t mounted_here = B_FALSE;
boolean_t zone_mounted_here = B_FALSE;
int ret = BE_SUCCESS, err = 0;
if (be_name == NULL || be_root_ds == NULL || fld == NULL)
return (BE_ERR_INVAL);
if ((zhp = zfs_open(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM))
== NULL) {
be_print_err(gettext("be_get_legacy_fs: failed to "
"open BE root dataset (%s): %s\n"), be_root_ds,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
return (ret);
}
if (!zfs_is_mounted(zhp, &fld->altroot)) {
if ((ret = _be_mount(be_name, &fld->altroot,
zoneroot_ds ? BE_MOUNT_FLAG_NULL :
BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
be_print_err(gettext("be_get_legacy_fs: "
"failed to mount BE %s\n"), be_name);
goto cleanup;
}
mounted_here = B_TRUE;
} else if (fld->altroot == NULL) {
be_print_err(gettext("be_get_legacy_fs: failed to "
"get altroot of mounted BE %s: %s\n"),
be_name, libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto cleanup;
}
if (zoneroot_ds != NULL) {
be_mount_data_t zone_md = { 0 };
ZFS_CLOSE(zhp);
if ((zhp = zfs_open(g_zfs, zoneroot_ds,
ZFS_TYPE_FILESYSTEM)) == NULL) {
be_print_err(gettext("be_get_legacy_fs: failed to "
"open zone BE root dataset (%s): %s\n"),
zoneroot_ds, libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto cleanup;
}
if (!zfs_is_mounted(zhp, &zone_md.altroot)) {
char zone_altroot[MAXPATHLEN];
(void) snprintf(zone_altroot, sizeof (zone_altroot),
"%s%s", fld->altroot, zoneroot);
if ((zone_md.altroot = strdup(zone_altroot)) == NULL) {
be_print_err(gettext("be_get_legacy_fs: "
"memory allocation failed\n"));
ret = BE_ERR_NOMEM;
goto cleanup;
}
if ((ret = be_mount_zone_root(zhp, &zone_md))
!= BE_SUCCESS) {
be_print_err(gettext("be_get_legacy_fs: "
"failed to mount zone root %s\n"),
zoneroot_ds);
free(zone_md.altroot);
zone_md.altroot = NULL;
goto cleanup;
}
zone_mounted_here = B_TRUE;
}
free(fld->altroot);
fld->altroot = zone_md.altroot;
}
if (get_mountpoint_from_vfstab(fld->altroot, zfs_get_name(zhp),
mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS) {
if (strcmp(mountpoint, "/") == 0) {
if (add_to_fs_list(fld, zfs_get_name(zhp))
!= BE_SUCCESS) {
be_print_err(gettext("be_get_legacy_fs: "
"failed to add %s to fs list\n"),
zfs_get_name(zhp));
ret = BE_ERR_INVAL;
goto cleanup;
}
}
}
if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback,
fld)) != 0) {
be_print_err(gettext("be_get_legacy_fs: "
"failed to iterate %s to get legacy mounts\n"),
zfs_get_name(zhp));
}
cleanup:
if (zone_mounted_here) {
be_unmount_data_t zone_ud = { 0 };
zone_ud.altroot = fld->altroot;
zone_ud.force = B_TRUE;
if ((err = be_unmount_zone_root(zhp, &zone_ud)) != BE_SUCCESS) {
be_print_err(gettext("be_get_legacy_fs: "
"failed to unmount zone root %s\n"),
zoneroot_ds);
if (ret == BE_SUCCESS)
ret = err;
}
}
if (mounted_here) {
if ((err = _be_unmount(be_name, 0)) != BE_SUCCESS) {
be_print_err(gettext("be_get_legacy_fs: "
"failed to unmount %s\n"), be_name);
if (ret == BE_SUCCESS)
ret = err;
}
}
ZFS_CLOSE(zhp);
free(fld->altroot);
fld->altroot = NULL;
return (ret);
}
void
be_free_fs_list(be_fs_list_data_t *fld)
{
int i;
if (fld == NULL)
return;
free(fld->altroot);
if (fld->fs_list == NULL)
return;
for (i = 0; i < fld->fs_num; i++)
free(fld->fs_list[i]);
free(fld->fs_list);
}
char *
be_get_ds_from_dir(char *dir)
{
dir_data_t dd = { 0 };
char resolved_dir[MAXPATHLEN];
if (dir == NULL || strlen(dir) >= MAXPATHLEN)
return (NULL);
(void) strlcpy(resolved_dir, dir, sizeof (resolved_dir));
z_resolve_lofs(resolved_dir, sizeof (resolved_dir));
dd.dir = resolved_dir;
(void) zfs_iter_root(g_zfs, be_get_ds_from_dir_callback, &dd);
return (dd.ds);
}
int
be_make_tmp_mountpoint(char **tmp_mp)
{
int err = 0;
if ((*tmp_mp = (char *)calloc(1, sizeof (BE_TMP_MNTPNT) + 1)) == NULL) {
be_print_err(gettext("be_make_tmp_mountpoint: "
"malloc failed\n"));
return (BE_ERR_NOMEM);
}
(void) strlcpy(*tmp_mp, BE_TMP_MNTPNT, sizeof (BE_TMP_MNTPNT) + 1);
if (mkdtemp(*tmp_mp) == NULL) {
err = errno;
be_print_err(gettext("be_make_tmp_mountpoint: mkdtemp() failed "
"for %s: %s\n"), *tmp_mp, strerror(err));
free(*tmp_mp);
*tmp_mp = NULL;
return (errno_to_be_err(err));
}
return (BE_SUCCESS);
}
int
be_mount_pool(
zfs_handle_t *zhp,
char **tmp_mntpnt,
char **orig_mntpnt,
boolean_t *pool_mounted)
{
char mountpoint[MAXPATHLEN];
int ret = 0;
*tmp_mntpnt = NULL;
*orig_mntpnt = NULL;
*pool_mounted = B_FALSE;
if (!zfs_is_mounted(zhp, NULL)) {
if (zfs_mount(zhp, NULL, 0) != 0) {
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
be_print_err(gettext("be_mount_pool: failed to "
"get mountpoint of (%s): %s\n"),
zfs_get_name(zhp),
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if ((*orig_mntpnt = strdup(mountpoint)) == NULL) {
be_print_err(gettext("be_mount_pool: memory "
"allocation failed\n"));
return (BE_ERR_NOMEM);
}
if ((ret = be_make_tmp_mountpoint(tmp_mntpnt))
!= BE_SUCCESS) {
be_print_err(gettext("be_mount_pool: failed "
"to make temporary mountpoint\n"));
free(*orig_mntpnt);
*orig_mntpnt = NULL;
return (ret);
}
if (zfs_prop_set(zhp,
zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
*tmp_mntpnt) != 0) {
be_print_err(gettext("be_mount_pool: failed "
"to set mountpoint of pool dataset %s to "
"%s: %s\n"), zfs_get_name(zhp),
*orig_mntpnt,
libzfs_error_description(g_zfs));
free(*tmp_mntpnt);
free(*orig_mntpnt);
*orig_mntpnt = NULL;
*tmp_mntpnt = NULL;
return (zfs_err_to_be_err(g_zfs));
}
if (zfs_mount(zhp, NULL, 0) != 0) {
be_print_err(gettext("be_mount_pool: failed "
"to mount dataset %s at %s: %s\n"),
zfs_get_name(zhp), *tmp_mntpnt,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
if (zfs_prop_set(zhp,
zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
mountpoint) != 0) {
be_print_err(gettext("be_mount_pool: "
"failed to set mountpoint of pool "
"dataset %s to %s: %s\n"),
zfs_get_name(zhp), *tmp_mntpnt,
libzfs_error_description(g_zfs));
}
free(*tmp_mntpnt);
free(*orig_mntpnt);
*orig_mntpnt = NULL;
*tmp_mntpnt = NULL;
return (ret);
}
}
*pool_mounted = B_TRUE;
}
return (BE_SUCCESS);
}
int
be_unmount_pool(
zfs_handle_t *zhp,
char *tmp_mntpnt,
char *orig_mntpnt)
{
if (zfs_unmount(zhp, NULL, 0) != 0) {
be_print_err(gettext("be_unmount_pool: failed to "
"unmount pool (%s): %s\n"), zfs_get_name(zhp),
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if (orig_mntpnt != NULL) {
if (tmp_mntpnt != NULL &&
strcmp(orig_mntpnt, tmp_mntpnt) != 0) {
(void) rmdir(tmp_mntpnt);
}
if (zfs_prop_set(zhp,
zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
orig_mntpnt) != 0) {
be_print_err(gettext("be_unmount_pool: failed "
"to set the mountpoint for dataset (%s) to "
"%s: %s\n"), zfs_get_name(zhp), orig_mntpnt,
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
}
return (BE_SUCCESS);
}
static int
be_mount_callback(zfs_handle_t *zhp, void *data)
{
zprop_source_t sourcetype;
const char *fs_name = zfs_get_name(zhp);
char source[ZFS_MAX_DATASET_NAME_LEN];
char *altroot = data;
char zhp_mountpoint[MAXPATHLEN];
char mountpoint[MAXPATHLEN];
int ret = 0;
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint,
sizeof (zhp_mountpoint), &sourcetype, source, sizeof (source),
B_FALSE) != 0) {
be_print_err(gettext("be_mount_callback: failed to "
"get mountpoint and sourcetype for %s\n"),
fs_name);
ZFS_CLOSE(zhp);
return (BE_ERR_ZFS);
}
if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) {
be_print_err(gettext("be_mount_callback: failed to "
"set canmount to 'noauto' (%s)\n"), fs_name);
ZFS_CLOSE(zhp);
return (BE_ERR_ZFS);
}
if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_NONE) == 0) {
goto next;
} else if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
if (get_mountpoint_from_vfstab(altroot, fs_name,
mountpoint, sizeof (mountpoint), B_TRUE) == BE_SUCCESS) {
if (mount(fs_name, mountpoint, MS_DATA,
MNTTYPE_ZFS, NULL, 0, NULL, 0) != 0) {
be_print_err(
gettext("be_mount_callback: "
"failed to mount %s on %s\n"),
fs_name, mountpoint);
}
} else {
be_print_err(
gettext("be_mount_callback: "
"no entry for %s in vfstab, "
"skipping ...\n"), fs_name);
}
goto next;
} else if ((sourcetype & (ZPROP_SRC_INHERITED|ZPROP_SRC_LOCAL)) == 0) {
be_print_err(gettext("be_mount_callback: "
"mountpoint sourcetype of %s is %d, skipping ...\n"),
fs_name, sourcetype);
goto next;
}
if (mount_zfs(zhp, altroot) != 0) {
be_print_err(gettext("be_mount_callback: failed to "
"mount dataset %s at %s: %s\n"), fs_name, mountpoint,
strerror(errno));
ZFS_CLOSE(zhp);
return (BE_ERR_MOUNT);
}
next:
if ((ret = zfs_iter_filesystems(zhp, be_mount_callback,
altroot)) != 0) {
ZFS_CLOSE(zhp);
return (ret);
}
ZFS_CLOSE(zhp);
return (0);
}
static int
be_unmount_callback(zfs_handle_t *zhp, void *data)
{
be_unmount_data_t *ud = data;
zprop_source_t sourcetype;
const char *fs_name = zfs_get_name(zhp);
char source[ZFS_MAX_DATASET_NAME_LEN];
char mountpoint[MAXPATHLEN];
char *zhp_mountpoint;
int ret = 0;
if (zfs_iter_filesystems(zhp, be_unmount_callback, ud)) {
ret = BE_ERR_UMOUNT;
goto done;
}
if (!zfs_is_mounted(zhp, NULL))
goto done;
if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
be_print_err(gettext("be_unmount_callback: "
"failed to unmount %s: %s\n"), fs_name,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
sizeof (mountpoint), &sourcetype, source, sizeof (source),
B_FALSE) != 0) {
be_print_err(gettext("be_unmount_callback: "
"failed to get mountpoint and sourcetype for %s: %s\n"),
fs_name, libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
if (sourcetype & ZPROP_SRC_INHERITED) {
goto done;
} else if (sourcetype & ZPROP_SRC_LOCAL) {
if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
goto done;
} else {
if ((strncmp(mountpoint, ud->altroot,
strlen(ud->altroot)) == 0) &&
(mountpoint[strlen(ud->altroot)] == '/')) {
zhp_mountpoint = mountpoint +
strlen(ud->altroot);
if (zfs_prop_set(zhp,
zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
zhp_mountpoint)) {
be_print_err(
gettext("be_unmount_callback: "
"failed to set mountpoint for "
"%s to %s: %s\n"), fs_name,
zhp_mountpoint,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
}
} else {
goto done;
}
}
} else {
be_print_err(gettext("be_unmount_callback: "
"mountpoint sourcetype of %s is %d, skipping ...\n"),
fs_name, sourcetype);
ret = BE_ERR_ZFS;
}
done:
if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) {
be_print_err(gettext("be_unmount_callback: "
"failed to set canmount to 'noauto' (%s)\n"), fs_name);
if (ret == 0)
ret = BE_ERR_ZFS;
}
ZFS_CLOSE(zhp);
return (ret);
}
static int
be_get_legacy_fs_callback(zfs_handle_t *zhp, void *data)
{
be_fs_list_data_t *fld = data;
const char *fs_name = zfs_get_name(zhp);
char zhp_mountpoint[MAXPATHLEN];
char mountpoint[MAXPATHLEN];
int ret = 0;
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint,
sizeof (zhp_mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
be_print_err(gettext("be_get_legacy_fs_callback: "
"failed to get mountpoint for %s: %s\n"),
fs_name, libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
ZFS_CLOSE(zhp);
return (ret);
}
if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
if (get_mountpoint_from_vfstab(fld->altroot, fs_name,
mountpoint, sizeof (mountpoint), B_FALSE) != BE_SUCCESS) {
be_print_err(gettext("be_get_legacy_fs_callback: "
"no entry for %s in vfstab, "
"skipping ...\n"), fs_name);
goto next;
}
if (add_to_fs_list(fld, zfs_get_name(zhp)) != BE_SUCCESS) {
be_print_err(gettext("be_get_legacy_fs_callback: "
"failed to add %s to fs list\n"), mountpoint);
ZFS_CLOSE(zhp);
return (BE_ERR_NOMEM);
}
}
next:
if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback,
fld)) != 0) {
ZFS_CLOSE(zhp);
return (ret);
}
ZFS_CLOSE(zhp);
return (0);
}
static int
add_to_fs_list(be_fs_list_data_t *fld, const char *fs)
{
if (fld == NULL || fs == NULL)
return (1);
if ((fld->fs_list = (char **)realloc(fld->fs_list,
sizeof (char *)*(fld->fs_num + 1))) == NULL) {
be_print_err(gettext("add_to_fs_list: "
"memory allocation failed\n"));
return (1);
}
if ((fld->fs_list[fld->fs_num++] = strdup(fs)) == NULL) {
be_print_err(gettext("add_to_fs_list: "
"memory allocation failed\n"));
return (1);
}
return (BE_SUCCESS);
}
static int
zpool_shared_fs_callback(zpool_handle_t *zlp, void *data)
{
be_mount_data_t *md = data;
zfs_handle_t *zhp = NULL;
const char *zpool = zpool_get_name(zlp);
int ret = 0;
if ((zhp = zfs_open(g_zfs, zpool, ZFS_TYPE_FILESYSTEM)) == NULL) {
be_print_err(gettext("zpool_shared_fs: "
"failed to open pool dataset %s: %s\n"), zpool,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
zpool_close(zlp);
return (ret);
}
(void) loopback_mount_shared_fs(zhp, md);
(void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md);
ZFS_CLOSE(zhp);
zpool_close(zlp);
return (0);
}
static int
iter_shared_fs_callback(zfs_handle_t *zhp, void *data)
{
be_mount_data_t *md = data;
const char *name = zfs_get_name(zhp);
char container_ds[MAXPATHLEN];
char tmp_name[MAXPATHLEN];
char *pool;
(void) strlcpy(tmp_name, name, sizeof (tmp_name));
pool = strtok(tmp_name, "/");
if (pool) {
if (be_make_container_ds(pool, container_ds,
sizeof (container_ds)) == BE_SUCCESS &&
strcmp(name, container_ds) == 0) {
ZFS_CLOSE(zhp);
return (0);
}
} else {
be_print_err(gettext("iter_shared_fs_callback: "
"failed to get pool name from %s\n"), name);
ZFS_CLOSE(zhp);
return (BE_ERR_POOL_NOENT);
}
(void) loopback_mount_shared_fs(zhp, md);
(void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md);
ZFS_CLOSE(zhp);
return (0);
}
static int
loopback_mount_shared_fs(zfs_handle_t *zhp, be_mount_data_t *md)
{
char zhp_mountpoint[MAXPATHLEN];
char mountpoint[MAXPATHLEN];
char *mp = NULL;
char optstr[MAX_MNTOPT_STR];
int mflag = MS_OPTIONSTR;
int err;
if (zfs_is_mounted(zhp, &mp) && (getzoneid() != GLOBAL_ZONEID ||
!zfs_prop_get_int(zhp, ZFS_PROP_ZONED))) {
if (mp == NULL) {
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
zhp_mountpoint, sizeof (zhp_mountpoint), NULL,
NULL, 0, B_FALSE) != 0) {
be_print_err(
gettext("loopback_mount_shared_fs: "
"failed to get mountpoint property\n"));
return (BE_ERR_ZFS);
}
} else {
(void) strlcpy(zhp_mountpoint, mp,
sizeof (zhp_mountpoint));
free(mp);
}
(void) snprintf(mountpoint, sizeof (mountpoint), "%s%s",
md->altroot, zhp_mountpoint);
if (!md->shared_rw) {
mflag |= MS_RDONLY;
}
(void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr));
if (mount(zhp_mountpoint, mountpoint, mflag, MNTTYPE_LOFS,
NULL, 0, optstr, sizeof (optstr)) != 0) {
err = errno;
be_print_err(gettext("loopback_mount_shared_fs: "
"failed to loopback mount %s at %s: %s\n"),
zhp_mountpoint, mountpoint, strerror(err));
return (BE_ERR_MOUNT);
}
}
return (BE_SUCCESS);
}
static int
loopback_mount_zonepath(const char *zonepath, be_mount_data_t *md)
{
FILE *fp = (FILE *)NULL;
struct stat st;
char *p;
char *p1;
char *parent_dir;
struct extmnttab extmtab;
dev_t dev = NODEV;
char *parentmnt;
char alt_parentmnt[MAXPATHLEN];
struct mnttab mntref;
char altzonepath[MAXPATHLEN];
char optstr[MAX_MNTOPT_STR];
int mflag = MS_OPTIONSTR;
int ret;
int err;
fp = fopen(MNTTAB, "r");
if (fp == NULL) {
err = errno;
be_print_err(gettext("loopback_mount_zonepath: "
"failed to open /etc/mnttab\n"));
return (errno_to_be_err(err));
}
p = strrchr(zonepath, '/');
if (p != NULL && p != zonepath) {
if ((parent_dir = (char *)calloc(sizeof (char),
p - zonepath + 1)) == NULL) {
ret = BE_ERR_NOMEM;
goto done;
}
(void) strlcpy(parent_dir, zonepath, p - zonepath + 1);
if (stat(parent_dir, &st) < 0) {
ret = errno_to_be_err(errno);
be_print_err(gettext("loopback_mount_zonepath: "
"failed to stat %s"),
parent_dir);
free(parent_dir);
goto done;
}
free(parent_dir);
resetmnttab(fp);
while (getextmntent(fp, &extmtab, sizeof (extmtab)) == 0) {
dev = makedev(extmtab.mnt_major, extmtab.mnt_minor);
if (st.st_dev == dev && strcmp(extmtab.mnt_fstype,
MNTTYPE_ZFS) == 0) {
p1 = strchr(extmtab.mnt_special, '/');
if (p1 == NULL || strncmp(p1 + 1,
BE_CONTAINER_DS_NAME, 4) != 0 ||
(*(p1 + 5) != '/' && *(p1 + 5) != '\0')) {
parentmnt = strdup(extmtab.mnt_mountp);
(void) snprintf(alt_parentmnt,
sizeof (alt_parentmnt), "%s%s",
md->altroot, parentmnt);
mntref.mnt_mountp = alt_parentmnt;
mntref.mnt_special = parentmnt;
mntref.mnt_fstype = MNTTYPE_LOFS;
mntref.mnt_mntopts = NULL;
mntref.mnt_time = NULL;
resetmnttab(fp);
if (getmntany(fp, (struct mnttab *)
&extmtab, &mntref) != 0) {
ret = loopback_mount_zonepath(
parentmnt, md);
if (ret != BE_SUCCESS) {
free(parentmnt);
goto done;
}
}
free(parentmnt);
}
break;
}
}
}
if (!md->shared_rw) {
mflag |= MS_RDONLY;
}
(void) snprintf(altzonepath, sizeof (altzonepath), "%s%s",
md->altroot, zonepath);
(void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr));
if (mount(zonepath, altzonepath, mflag, MNTTYPE_LOFS,
NULL, 0, optstr, sizeof (optstr)) != 0) {
err = errno;
be_print_err(gettext("loopback_mount_zonepath: "
"failed to loopback mount %s at %s: %s\n"),
zonepath, altzonepath, strerror(err));
ret = BE_ERR_MOUNT;
goto done;
}
ret = BE_SUCCESS;
done :
(void) fclose(fp);
return (ret);
}
static int
unmount_shared_fs(be_unmount_data_t *ud)
{
FILE *fp = NULL;
struct mnttab *table = NULL;
struct mnttab ent;
struct mnttab *entp = NULL;
size_t size = 0;
int read_chunk = 32;
int i;
int altroot_len;
int err = 0;
errno = 0;
if ((fp = fopen(MNTTAB, "r")) == NULL) {
err = errno;
be_print_err(gettext("unmount_shared_fs: "
"failed to open mnttab\n"));
return (errno_to_be_err(err));
}
while (getmntent(fp, &ent) == 0) {
if (size % read_chunk == 0) {
table = (struct mnttab *)realloc(table,
(size + read_chunk) * sizeof (ent));
}
entp = &table[size++];
(void) memset(entp, 0, sizeof (*entp));
if ((entp->mnt_mountp = strdup(ent.mnt_mountp)) == NULL ||
(entp->mnt_fstype = strdup(ent.mnt_fstype)) == NULL) {
be_print_err(gettext("unmount_shared_fs: "
"memory allocation failed\n"));
return (BE_ERR_NOMEM);
}
}
(void) fclose(fp);
altroot_len = strlen(ud->altroot);
for (i = size; i > 0; i--) {
entp = &table[i - 1];
if (strcmp(entp->mnt_fstype, MNTTYPE_LOFS) != 0)
continue;
if (strncmp(entp->mnt_mountp, ud->altroot, altroot_len) == 0 &&
entp->mnt_mountp[altroot_len] == '/') {
if (umount(entp->mnt_mountp) != 0) {
err = errno;
if (err == EBUSY) {
(void) sleep(1);
err = errno = 0;
if (umount(entp->mnt_mountp) != 0)
err = errno;
}
if (err != 0) {
be_print_err(gettext(
"unmount_shared_fs: "
"failed to unmount shared file "
"system %s: %s\n"),
entp->mnt_mountp, strerror(err));
return (errno_to_be_err(err));
}
}
}
}
return (BE_SUCCESS);
}
static int
get_mountpoint_from_vfstab(char *altroot, const char *fs, char *mountpoint,
size_t size_mp, boolean_t get_alt_mountpoint)
{
struct vfstab vp;
FILE *fp = NULL;
char alt_vfstab[MAXPATHLEN];
(void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
altroot);
if ((fp = fopen(alt_vfstab, "r")) == NULL) {
be_print_err(gettext("get_mountpoint_from_vfstab: "
"failed to open vfstab (%s)\n"), alt_vfstab);
return (1);
}
if (getvfsspec(fp, &vp, (char *)fs) == 0) {
if (get_alt_mountpoint) {
(void) snprintf(mountpoint, size_mp, "%s%s", altroot,
vp.vfs_mountp);
} else {
(void) strlcpy(mountpoint, vp.vfs_mountp, size_mp);
}
} else {
(void) fclose(fp);
return (1);
}
(void) fclose(fp);
return (BE_SUCCESS);
}
static int
fix_mountpoint_callback(zfs_handle_t *zhp, void *data)
{
zprop_source_t sourcetype;
char source[ZFS_MAX_DATASET_NAME_LEN];
char mountpoint[MAXPATHLEN];
char *zhp_mountpoint = NULL;
char *altroot = data;
int ret = 0;
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
sizeof (mountpoint), &sourcetype, source, sizeof (source),
B_FALSE) != 0) {
be_print_err(gettext("fix_mountpoint_callback: "
"failed to get mountpoint and sourcetype for %s\n"),
zfs_get_name(zhp));
ZFS_CLOSE(zhp);
return (BE_ERR_ZFS);
}
if (!(sourcetype & ZPROP_SRC_INHERITED) &&
strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) {
if (strncmp(mountpoint, altroot, strlen(altroot)) == 0 &&
mountpoint[strlen(altroot)] == '/') {
zhp_mountpoint = mountpoint + strlen(altroot);
if (zfs_prop_set(zhp,
zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
zhp_mountpoint)) {
be_print_err(gettext("fix_mountpoint_callback: "
"failed to set mountpoint for %s to "
"%s: %s\n"), zfs_get_name(zhp),
zhp_mountpoint,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
ZFS_CLOSE(zhp);
return (ret);
}
}
}
if ((ret = zfs_iter_filesystems(zhp, fix_mountpoint_callback,
altroot)) != 0) {
ZFS_CLOSE(zhp);
return (ret);
}
ZFS_CLOSE(zhp);
return (0);
}
static int
be_mount_root(zfs_handle_t *zhp, char *altroot)
{
char mountpoint[MAXPATHLEN];
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
be_print_err(gettext("be_mount_root: failed to "
"get mountpoint property for %s: %s\n"), zfs_get_name(zhp),
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")
!= 0) {
be_print_err(gettext("be_mount_root: failed to "
"set canmount property to 'noauto' (%s): %s\n"),
zfs_get_name(zhp), libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if (mount_zfs(zhp, altroot) != 0) {
be_print_err(gettext("be_mount_root: failed to "
"mount dataset %s at %s: %s\n"), zfs_get_name(zhp),
altroot, strerror(errno));
return (BE_ERR_ZFS);
}
return (BE_SUCCESS);
}
static int
be_unmount_root(zfs_handle_t *zhp, be_unmount_data_t *ud)
{
char mountpoint[MAXPATHLEN];
boolean_t is_legacy = B_FALSE;
if (get_mountpoint_from_vfstab(ud->altroot, zfs_get_name(zhp),
mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS &&
strcmp(mountpoint, "/") == 0) {
is_legacy = B_TRUE;
}
if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) {
be_print_err(gettext("be_unmount_root: failed to "
"unmount BE root dataset %s: %s\n"), zfs_get_name(zhp),
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")
!= 0) {
be_print_err(gettext("be_unmount_root: failed to "
"set canmount property for %s to 'noauto': %s\n"),
zfs_get_name(zhp), libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
is_legacy ? ZFS_MOUNTPOINT_LEGACY : "/") != 0) {
be_print_err(gettext("be_unmount_root: failed to "
"set mountpoint of %s to %s\n"), zfs_get_name(zhp),
is_legacy ? ZFS_MOUNTPOINT_LEGACY : "/");
return (zfs_err_to_be_err(g_zfs));
}
return (BE_SUCCESS);
}
static int
fix_mountpoint(zfs_handle_t *zhp)
{
be_unmount_data_t ud = { 0 };
char *altroot = NULL;
char mountpoint[MAXPATHLEN];
int ret = BE_SUCCESS;
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
be_print_err(gettext("fix_mountpoint: failed to get "
"mountpoint property of (%s): %s\n"), zfs_get_name(zhp),
libzfs_error_description(g_zfs));
return (BE_ERR_ZFS);
}
if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0 ||
strcmp(mountpoint, "/") == 0) {
return (BE_SUCCESS);
}
if (zfs_iter_filesystems(zhp, fix_mountpoint_callback, mountpoint)
!= 0) {
return (BE_ERR_ZFS);
}
if ((ret = be_make_tmp_mountpoint(&altroot)) != BE_SUCCESS) {
be_print_err(gettext("fix_mountpoint: failed to "
"make temporary mountpoint\n"));
return (ret);
}
if ((ret = be_mount_root(zhp, altroot)) != BE_SUCCESS) {
be_print_err(gettext("fix_mountpoint: failed to "
"mount BE root file system\n"));
goto cleanup;
}
ud.altroot = altroot;
if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) {
be_print_err(gettext("fix_mountpoint: failed to "
"unmount BE root file system\n"));
goto cleanup;
}
cleanup:
free(altroot);
return (ret);
}
static int
be_mount_zones(zfs_handle_t *be_zhp, be_mount_data_t *md)
{
zoneBrandList_t *brands = NULL;
zoneList_t zlst = NULL;
char *zonename = NULL;
char *zonepath = NULL;
char *zonepath_ds = NULL;
int k;
int ret = BE_SUCCESS;
z_set_zone_root(md->altroot);
if ((brands = be_get_supported_brandlist()) == NULL) {
be_print_err(gettext("be_mount_zones: "
"no supported brands\n"));
return (BE_SUCCESS);
}
zlst = z_get_nonglobal_zone_list_by_brand(brands);
if (zlst == NULL) {
z_free_brand_list(brands);
return (BE_SUCCESS);
}
for (k = 0; (zonename = z_zlist_get_zonename(zlst, k)) != NULL; k++) {
if (z_zlist_get_current_state(zlst, k) ==
ZONE_STATE_INSTALLED) {
zonepath = z_zlist_get_zonepath(zlst, k);
if ((zonepath_ds = be_get_ds_from_dir(zonepath))
== NULL)
continue;
if (!be_zone_supported(zonepath_ds)) {
free(zonepath_ds);
zonepath_ds = NULL;
continue;
}
if (!md->shared_fs) {
ret = loopback_mount_zonepath(zonepath, md);
if (ret != BE_SUCCESS)
goto done;
}
ret = be_mount_one_zone(be_zhp, md, zonename,
zonepath, zonepath_ds);
free(zonepath_ds);
zonepath_ds = NULL;
if (ret != BE_SUCCESS) {
be_print_err(gettext("be_mount_zones: "
"failed to mount zone %s under "
"altroot %s\n"), zonename, md->altroot);
goto done;
}
}
}
done:
z_free_brand_list(brands);
z_free_zone_list(zlst);
z_destroyMountTable();
return (ret);
}
static int
be_unmount_zones(be_unmount_data_t *ud)
{
zoneBrandList_t *brands = NULL;
zoneList_t zlst = NULL;
char *zonename = NULL;
char *zonepath = NULL;
char alt_zonepath[MAXPATHLEN];
char *zonepath_ds = NULL;
int k;
int ret = BE_SUCCESS;
z_set_zone_root(ud->altroot);
if ((brands = be_get_supported_brandlist()) == NULL) {
be_print_err(gettext("be_unmount_zones: "
"no supported brands\n"));
return (BE_SUCCESS);
}
zlst = z_get_nonglobal_zone_list_by_brand(brands);
if (zlst == NULL) {
z_free_brand_list(brands);
return (BE_SUCCESS);
}
for (k = 0; (zonename = z_zlist_get_zonename(zlst, k)) != NULL; k++) {
if (z_zlist_get_current_state(zlst, k) ==
ZONE_STATE_INSTALLED) {
zonepath = z_zlist_get_zonepath(zlst, k);
(void) snprintf(alt_zonepath, sizeof (alt_zonepath),
"%s%s", ud->altroot, zonepath);
if ((zonepath_ds = be_get_ds_from_dir(alt_zonepath))
== NULL)
continue;
if (!be_zone_supported(zonepath_ds)) {
free(zonepath_ds);
zonepath_ds = NULL;
continue;
}
ret = be_unmount_one_zone(ud, zonename, zonepath,
zonepath_ds);
free(zonepath_ds);
zonepath_ds = NULL;
if (ret != BE_SUCCESS) {
be_print_err(gettext("be_unmount_zones:"
" failed to unmount zone %s from "
"altroot %s\n"), zonename, ud->altroot);
goto done;
}
}
}
done:
z_free_brand_list(brands);
z_free_zone_list(zlst);
return (ret);
}
static int
be_mount_one_zone(zfs_handle_t *be_zhp, be_mount_data_t *md, char *zonename,
char *zonepath, char *zonepath_ds)
{
be_mount_data_t zone_md = { 0 };
zfs_handle_t *zone_zhp = NULL;
char zone_altroot[MAXPATHLEN];
char zoneroot[MAXPATHLEN];
char zoneroot_ds[MAXPATHLEN];
int ret = BE_SUCCESS;
if ((ret = be_find_active_zone_root(be_zhp, zonepath_ds, zoneroot_ds,
sizeof (zoneroot_ds))) == BE_ERR_ZONE_NO_ACTIVE_ROOT) {
be_print_err(gettext("be_mount_one_zone: did not "
"find active zone root for zone %s, skipping ...\n"),
zonename);
return (BE_SUCCESS);
} else if (ret != BE_SUCCESS) {
be_print_err(gettext("be_mount_one_zone: failed to "
"find active zone root for zone %s\n"), zonename);
return (ret);
}
if ((zone_zhp = zfs_open(g_zfs, zoneroot_ds, ZFS_TYPE_FILESYSTEM))
== NULL) {
be_print_err(gettext("be_mount_one_zone: failed to "
"open zone root dataset (%s): %s\n"), zoneroot_ds,
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot));
(void) strlcpy(zone_altroot, md->altroot, sizeof (zone_altroot));
(void) strlcat(zone_altroot, zoneroot, sizeof (zone_altroot));
zone_md.altroot = zone_altroot;
zone_md.shared_fs = md->shared_fs;
zone_md.shared_rw = md->shared_rw;
if ((ret = be_mount_zone_root(zone_zhp, &zone_md)) != BE_SUCCESS) {
be_print_err(gettext("be_mount_one_zone: failed to "
"mount zone root file system at %s\n"), zone_altroot);
goto done;
}
if ((ret = zfs_iter_filesystems(zone_zhp, be_mount_callback,
zone_altroot)) != 0) {
be_print_err(gettext("be_mount_one_zone: failed to "
"mount zone subordinate file systems at %s\n"),
zone_altroot);
goto done;
}
done:
ZFS_CLOSE(zone_zhp);
return (ret);
}
static int
be_unmount_one_zone(be_unmount_data_t *ud, char *zonename, char *zonepath,
char *zonepath_ds)
{
be_unmount_data_t zone_ud = { 0 };
zfs_handle_t *zone_zhp = NULL;
char zone_altroot[MAXPATHLEN];
char zoneroot[MAXPATHLEN];
char zoneroot_ds[MAXPATHLEN];
int ret = BE_SUCCESS;
be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot));
(void) strlcpy(zone_altroot, ud->altroot, sizeof (zone_altroot));
(void) strlcat(zone_altroot, zoneroot, sizeof (zone_altroot));
zone_ud.altroot = zone_altroot;
zone_ud.force = ud->force;
if ((ret = be_find_mounted_zone_root(zone_altroot, zonepath_ds,
zoneroot_ds, sizeof (zoneroot_ds))) == BE_ERR_NO_MOUNTED_ZONE) {
be_print_err(gettext("be_unmount_one_zone: did not "
"find any zone root mounted for zone %s\n"), zonename);
return (BE_SUCCESS);
} else if (ret != BE_SUCCESS) {
be_print_err(gettext("be_unmount_one_zone: failed to "
"find mounted zone root for zone %s\n"), zonename);
return (ret);
}
if ((zone_zhp = zfs_open(g_zfs, zoneroot_ds, ZFS_TYPE_FILESYSTEM))
== NULL) {
be_print_err(gettext("be_unmount_one_zone: failed to "
"open mounted zone root dataset (%s): %s\n"), zoneroot_ds,
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if ((ret = zfs_iter_filesystems(zone_zhp, be_unmount_callback,
&zone_ud)) != 0) {
be_print_err(gettext("be_unmount_one_zone: failed to "
"unmount zone subordinate file systems at %s\n"),
zone_altroot);
goto done;
}
if ((ret = be_unmount_zone_root(zone_zhp, &zone_ud)) != BE_SUCCESS) {
be_print_err(gettext("be_unmount_one_zone: failed to "
"unmount zone root file system at %s\n"), zone_altroot);
goto done;
}
done:
ZFS_CLOSE(zone_zhp);
return (ret);
}
static int
be_get_ds_from_dir_callback(zfs_handle_t *zhp, void *data)
{
dir_data_t *dd = data;
char *mp = NULL;
int zret = 0;
if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
ZFS_CLOSE(zhp);
return (0);
}
if (zfs_is_mounted(zhp, &mp) && mp != NULL &&
strcmp(mp, dd->dir) == 0) {
if ((dd->ds = strdup(zfs_get_name(zhp))) == NULL) {
be_print_err(gettext("be_get_ds_from_dir_callback: "
"memory allocation failed\n"));
ZFS_CLOSE(zhp);
return (0);
}
ZFS_CLOSE(zhp);
return (1);
}
zret = zfs_iter_filesystems(zhp, be_get_ds_from_dir_callback, dd);
ZFS_CLOSE(zhp);
return (zret);
}
static int
mount_zfs(zfs_handle_t *zhp, char *altroot)
{
int flags = 0;
char mountpoint[MAXPATHLEN];
char real_mountpoint[MAXPATHLEN];
char source[MAXNAMELEN];
char optstr[MAX_MNTOPT_STR];
zprop_source_t sourcetype;
struct stat buf;
optstr[0] = '\0';
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
sizeof (mountpoint), &sourcetype, source, sizeof (source),
B_FALSE) != 0) {
be_print_err(gettext("mount_zfs: "
"failed to get mountpoint and sourcetype for %s\n"),
zfs_get_name(zhp));
ZFS_CLOSE(zhp);
return (BE_ERR_ZFS);
}
if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0 ||
strcmp(mountpoint, "/") == 0) {
mountpoint[0] = '\0';
}
(void) snprintf(real_mountpoint, MAXPATHLEN, "%s%s", altroot,
mountpoint);
if (zpool_get_prop_int(zfs_get_pool_handle(zhp), ZPOOL_PROP_READONLY,
NULL))
flags |= MS_RDONLY;
if (lstat(real_mountpoint, &buf) != 0) {
if (mkdirp(real_mountpoint, 0755) != 0) {
be_print_err(gettext("mount_zfs: "
"failed to create mountpoint for %s\n"),
zfs_get_name(zhp));
ZFS_CLOSE(zhp);
return (BE_ERR_ZFS);
}
}
if (mount(zfs_get_name(zhp), real_mountpoint, MS_OPTIONSTR | flags,
MNTTYPE_ZFS, NULL, 0, optstr, sizeof (optstr))) {
be_print_err(gettext("mount_zfs: failed to "
"mount dataset %s at %s\n"), zfs_get_name(zhp),
real_mountpoint);
return (BE_ERR_ZFS);
}
return (BE_SUCCESS);
}