#include <assert.h>
#include <errno.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 <unistd.h>
#include <libbe.h>
#include <libbe_priv.h>
typedef struct active_zone_root_data {
uuid_t parent_uuid;
char *zoneroot_ds;
} active_zone_root_data_t;
typedef struct mounted_zone_root_data {
char *zone_altroot;
char *zoneroot_ds;
} mounted_zone_root_data_t;
static int be_find_active_zone_root_callback(zfs_handle_t *, void *);
static int be_find_mounted_zone_root_callback(zfs_handle_t *, void *);
static boolean_t be_zone_get_active(zfs_handle_t *);
void
be_make_zoneroot(char *zonepath, char *zoneroot, int zoneroot_size)
{
(void) snprintf(zoneroot, zoneroot_size, "%s/root", zonepath);
}
int
be_find_active_zone_root(zfs_handle_t *be_zhp, char *zonepath_ds,
char *zoneroot_ds, int zoneroot_ds_size)
{
active_zone_root_data_t azr_data = { { 0 }, NULL };
zfs_handle_t *zhp;
char zone_container_ds[MAXPATHLEN];
int ret = BE_SUCCESS;
if (getzoneid() == GLOBAL_ZONEID) {
if ((ret = be_get_uuid(zfs_get_name(be_zhp),
&azr_data.parent_uuid)) != BE_SUCCESS) {
be_print_err(gettext("be_find_active_zone_root: failed "
"to get uuid for BE root dataset %s\n"),
zfs_get_name(be_zhp));
return (ret);
}
} else {
if ((ret = be_zone_get_parent_uuid(zfs_get_name(be_zhp),
&azr_data.parent_uuid)) != BE_SUCCESS) {
be_print_err(gettext("be_find_active_zone_root: failed "
"to get parentbe uuid for zone root dataset %s\n"),
zfs_get_name(be_zhp));
return (ret);
}
}
if ((ret = be_make_container_ds(zonepath_ds, zone_container_ds,
sizeof (zone_container_ds))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container dataset "
"for %s\n"), __func__, zonepath_ds);
return (ret);
}
if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM))
== NULL) {
be_print_err(gettext("be_find_active_zone_root: failed to "
"open zone root container dataset (%s): %s\n"),
zone_container_ds, libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if ((ret = zfs_iter_filesystems(zhp, be_find_active_zone_root_callback,
&azr_data)) != 0) {
be_print_err(gettext("be_find_active_zone_root: failed to "
"find active zone root in zonepath dataset %s: %s\n"),
zonepath_ds, be_err_to_str(ret));
goto done;
}
if (azr_data.zoneroot_ds != NULL) {
(void) strlcpy(zoneroot_ds, azr_data.zoneroot_ds,
zoneroot_ds_size);
free(azr_data.zoneroot_ds);
} else {
be_print_err(gettext("be_find_active_zone_root: failed to "
"find active zone root in zonepath dataset %s\n"),
zonepath_ds);
ret = BE_ERR_ZONE_NO_ACTIVE_ROOT;
}
done:
ZFS_CLOSE(zhp);
return (ret);
}
int
be_find_mounted_zone_root(char *zone_altroot, char *zonepath_ds,
char *zoneroot_ds, int zoneroot_ds_size)
{
mounted_zone_root_data_t mzr_data = { 0 };
zfs_handle_t *zhp = NULL;
char zone_container_ds[MAXPATHLEN];
int ret = BE_SUCCESS;
int zret = 0;
if ((ret = be_make_container_ds(zonepath_ds, zone_container_ds,
sizeof (zone_container_ds))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container dataset "
"for %s\n"), __func__, zonepath_ds);
return (ret);
}
if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM))
== NULL) {
be_print_err(gettext("be_find_mounted_zone_root: failed to "
"open zone root container dataset (%s): %s\n"),
zone_container_ds, libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
mzr_data.zone_altroot = zone_altroot;
if ((zret = zfs_iter_filesystems(zhp,
be_find_mounted_zone_root_callback, &mzr_data)) == 0) {
be_print_err(gettext("be_find_mounted_zone_root: did not "
"find mounted zone under altroot zonepath %s\n"),
zonepath_ds);
ret = BE_ERR_NO_MOUNTED_ZONE;
goto done;
} else if (zret < 0) {
be_print_err(gettext("be_find_mounted_zone_root: "
"zfs_iter_filesystems failed: %s\n"),
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
if (mzr_data.zoneroot_ds != NULL) {
(void) strlcpy(zoneroot_ds, mzr_data.zoneroot_ds,
zoneroot_ds_size);
free(mzr_data.zoneroot_ds);
}
done:
ZFS_CLOSE(zhp);
return (ret);
}
boolean_t
be_zone_supported(char *zonepath_ds)
{
char zone_container_ds[MAXPATHLEN];
int ret = 0;
if ((ret = zpool_iter(g_zfs, be_check_be_roots_callback,
zonepath_ds)) > 0) {
be_print_err(gettext("be_zone_supported: "
"zonepath dataset %s not supported\n"), zonepath_ds);
return (B_FALSE);
} else if (ret < 0) {
be_print_err(gettext("be_zone_supported: "
"zpool_iter failed: %s\n"),
libzfs_error_description(g_zfs));
return (B_FALSE);
}
if ((ret = be_make_container_ds(zonepath_ds, zone_container_ds,
sizeof (zone_container_ds))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container dataset "
"for %s\n"), __func__, zonepath_ds);
return (B_FALSE);
}
if (!zfs_dataset_exists(g_zfs, zone_container_ds,
ZFS_TYPE_FILESYSTEM)) {
be_print_err(gettext("be_zone_supported: "
"zonepath dataset (%s) does not have a zone root container "
"dataset, zone is not supported, skipping ...\n"),
zonepath_ds);
return (B_FALSE);
}
return (B_TRUE);
}
zoneBrandList_t *
be_get_supported_brandlist(void)
{
return (z_make_brand_list(BE_ZONE_SUPPORTED_BRANDS,
BE_ZONE_SUPPORTED_BRANDS_DELIM));
}
int
be_zone_get_parent_uuid(const char *root_ds, uuid_t *uu)
{
zfs_handle_t *zhp = NULL;
nvlist_t *userprops = NULL;
nvlist_t *propname = NULL;
char *uu_string = NULL;
int ret = BE_SUCCESS;
if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
be_print_err(gettext("be_zone_get_parent_uuid: failed to "
"open zone root dataset (%s): %s\n"), root_ds,
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if ((userprops = zfs_get_user_props(zhp)) == NULL) {
be_print_err(gettext("be_zone_get_parent_uuid: "
"failed to get user properties for zone root "
"dataset (%s): %s\n"), root_ds,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
if (nvlist_lookup_nvlist(userprops, BE_ZONE_PARENTBE_PROPERTY,
&propname) != 0 || nvlist_lookup_string(propname, ZPROP_VALUE,
&uu_string) != 0) {
be_print_err(gettext("be_zone_get_parent_uuid: failed to "
"get parent uuid property from zone root dataset user "
"properties.\n"));
ret = BE_ERR_ZONE_NO_PARENTBE;
goto done;
}
if (uuid_parse(uu_string, *uu) != 0 || uuid_is_null(*uu)) {
be_print_err(gettext("be_zone_get_parent_uuid: failed to "
"parse parentuuid\n"));
ret = BE_ERR_PARSE_UUID;
}
done:
ZFS_CLOSE(zhp);
return (ret);
}
int
be_zone_set_parent_uuid(char *root_ds, uuid_t uu)
{
zfs_handle_t *zhp = NULL;
char uu_string[UUID_PRINTABLE_STRING_LENGTH];
int ret = BE_SUCCESS;
uuid_unparse(uu, uu_string);
if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
be_print_err(gettext("be_zone_set_parent_uuid: failed to "
"open root zone dataset (%s): %s\n"), root_ds,
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if (zfs_prop_set(zhp, BE_ZONE_PARENTBE_PROPERTY, uu_string) != 0) {
be_print_err(gettext("be_zone_set_parent_uuid: failed to "
"set parentbe uuid property for root zone dataset: %s\n"),
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
}
ZFS_CLOSE(zhp);
return (ret);
}
boolean_t
be_zone_compare_uuids(char *root_ds)
{
char *active_ds;
uuid_t parent_uuid = {0};
uuid_t cur_parent_uuid = {0};
if ((be_zone_get_parent_uuid(root_ds,
&parent_uuid)) != BE_SUCCESS) {
be_print_err(gettext("be_zone_compare_uuids: failed to get "
"parentbe uuid from the given BE\n"));
return (B_FALSE);
}
if ((active_ds = be_get_ds_from_dir("/")) != NULL) {
if ((be_zone_get_parent_uuid(active_ds,
&cur_parent_uuid)) != BE_SUCCESS) {
be_print_err(gettext("be_zone_compare_uuids: failed "
"to get parentbe uuid from the current running zone "
"root dataset\n"));
return (B_FALSE);
}
} else {
be_print_err(gettext("be_zone_compare_uuids: zone root dataset "
"is not mounted\n"));
return (B_FALSE);
}
if (uuid_compare(parent_uuid, cur_parent_uuid) != 0) {
return (B_FALSE);
}
return (B_TRUE);
}
static int
be_find_active_zone_root_callback(zfs_handle_t *zhp, void *data)
{
active_zone_root_data_t *azr_data = data;
uuid_t parent_uuid = { 0 };
int iret = 0;
int ret = 0;
if ((iret = be_zone_get_parent_uuid(zfs_get_name(zhp), &parent_uuid))
!= BE_SUCCESS) {
be_print_err(gettext("be_find_active_zone_root_callback: "
"skipping zone root dataset (%s): %s\n"),
zfs_get_name(zhp), be_err_to_str(iret));
goto done;
}
if (uuid_compare(azr_data->parent_uuid, parent_uuid) == 0) {
if (be_zone_get_active(zhp)) {
if (azr_data->zoneroot_ds != NULL) {
ret = BE_ERR_ZONE_MULTIPLE_ACTIVE;
goto done;
}
azr_data->zoneroot_ds = strdup(zfs_get_name(zhp));
if (azr_data->zoneroot_ds == NULL) {
ret = BE_ERR_NOMEM;
}
}
}
done:
ZFS_CLOSE(zhp);
return (ret);
}
static int
be_find_mounted_zone_root_callback(zfs_handle_t *zhp, void *data)
{
mounted_zone_root_data_t *mzr_data = data;
char *mp = NULL;
if (zfs_is_mounted(zhp, &mp) && mp != NULL &&
strcmp(mp, mzr_data->zone_altroot) == 0) {
mzr_data->zoneroot_ds = strdup(zfs_get_name(zhp));
free(mp);
return (1);
}
free(mp);
return (0);
}
static boolean_t
be_zone_get_active(zfs_handle_t *zhp)
{
nvlist_t *userprops = NULL;
nvlist_t *propname = NULL;
char *active_str = NULL;
if ((userprops = zfs_get_user_props(zhp)) == NULL) {
be_print_err(gettext("be_zone_get_active: "
"failed to get user properties for zone root "
"dataset (%s): %s\n"), zfs_get_name(zhp),
libzfs_error_description(g_zfs));
return (B_FALSE);
}
if (nvlist_lookup_nvlist(userprops, BE_ZONE_ACTIVE_PROPERTY, &propname)
!= 0 || nvlist_lookup_string(propname, ZPROP_VALUE, &active_str)
!= 0) {
return (B_FALSE);
}
if (strcmp(active_str, "on") == 0)
return (B_TRUE);
return (B_FALSE);
}