#include <assert.h>
#include <ctype.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/mnttab.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <libbe.h>
#include <libbe_priv.h>
#include <libzfsbootenv.h>
libzfs_handle_t *g_zfs = NULL;
static int _be_destroy(const char *, be_destroy_data_t *);
static int be_destroy_zones(char *, char *, be_destroy_data_t *);
static int be_destroy_zone_roots(char *, be_destroy_data_t *);
static int be_destroy_zone_roots_callback(zfs_handle_t *, void *);
static int be_copy_zones(char *, char *, char *);
static int be_clone_fs_callback(zfs_handle_t *, void *);
static int be_destroy_callback(zfs_handle_t *, void *);
static int be_send_fs_callback(zfs_handle_t *, void *);
static int be_demote_callback(zfs_handle_t *, void *);
static int be_demote_find_clone_callback(zfs_handle_t *, void *);
static int be_has_snapshot_callback(zfs_handle_t *, void *);
static int be_demote_get_one_clone(zfs_handle_t *, void *);
static int be_get_snap(char *, char **);
static int be_prep_clone_send_fs(zfs_handle_t *, be_transaction_data_t *,
char *, int);
static boolean_t be_create_container_ds(char *);
static char *be_get_zone_be_name(char *root_ds, char *container_ds);
static int be_zone_root_exists_callback(zfs_handle_t *, void *);
int
be_init(nvlist_t *be_attrs)
{
be_transaction_data_t bt = { 0 };
zpool_handle_t *zlp;
nvlist_t *zfs_props = NULL;
char nbe_root_ds[MAXPATHLEN];
char child_fs[MAXPATHLEN];
char **fs_names = NULL;
char **shared_fs_names = NULL;
uint16_t fs_num = 0;
uint16_t shared_fs_num = 0;
int nelem;
int i;
int zret = 0, ret = BE_SUCCESS;
if (!be_zfs_init())
return (BE_ERR_INIT);
if (nvlist_lookup_string(be_attrs, BE_ATTR_NEW_BE_NAME, &bt.nbe_name)
!= 0) {
be_print_err(gettext("be_init: failed to lookup "
"BE_ATTR_NEW_BE_NAME attribute\n"));
return (BE_ERR_INVAL);
}
if (!be_valid_be_name(bt.nbe_name)) {
be_print_err(gettext("be_init: invalid BE name %s\n"),
bt.nbe_name);
return (BE_ERR_INVAL);
}
if (nvlist_lookup_string(be_attrs, BE_ATTR_NEW_BE_POOL, &bt.nbe_zpool)
!= 0) {
be_print_err(gettext("be_init: failed to lookup "
"BE_ATTR_NEW_BE_POOL attribute\n"));
return (BE_ERR_INVAL);
}
nelem = 0;
if (nvlist_lookup_pairs(be_attrs, 0,
BE_ATTR_FS_NUM, DATA_TYPE_UINT16, &fs_num,
BE_ATTR_FS_NAMES, DATA_TYPE_STRING_ARRAY, &fs_names, &nelem,
NULL) != 0) {
be_print_err(gettext("be_init: failed to lookup fs "
"attributes\n"));
return (BE_ERR_INVAL);
}
if (nelem != fs_num) {
be_print_err(gettext("be_init: size of FS_NAMES array (%d) "
"does not match FS_NUM (%d)\n"), nelem, fs_num);
return (BE_ERR_INVAL);
}
nelem = 0;
if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
BE_ATTR_SHARED_FS_NUM, DATA_TYPE_UINT16, &shared_fs_num,
BE_ATTR_SHARED_FS_NAMES, DATA_TYPE_STRING_ARRAY, &shared_fs_names,
&nelem, NULL) != 0) {
be_print_err(gettext("be_init: failed to lookup "
"shared fs attributes\n"));
return (BE_ERR_INVAL);
}
if (nelem != shared_fs_num) {
be_print_err(gettext("be_init: size of SHARED_FS_NAMES "
"array does not match SHARED_FS_NUM\n"));
return (BE_ERR_INVAL);
}
if ((zlp = zpool_open(g_zfs, bt.nbe_zpool)) == NULL) {
be_print_err(gettext("be_init: failed to "
"find existing zpool (%s): %s\n"), bt.nbe_zpool,
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
zpool_close(zlp);
if (!be_create_container_ds(bt.nbe_zpool))
return (BE_ERR_CREATDS);
if ((zret = zpool_iter(g_zfs, be_exists_callback, bt.nbe_name)) > 0) {
be_print_err(gettext("be_init: BE (%s) already exists\n"),
bt.nbe_name);
return (BE_ERR_BE_EXISTS);
} else if (zret < 0) {
be_print_err(gettext("be_init: 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.nbe_zpool, bt.nbe_name, nbe_root_ds,
sizeof (nbe_root_ds))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container dataset "
"for %s/%s\n"), __func__, bt.nbe_zpool, bt.nbe_name);
return (ret);
}
if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
BE_ATTR_ZFS_PROPERTIES, DATA_TYPE_NVLIST, &zfs_props, NULL)
!= 0) {
be_print_err(gettext("be_init: failed to lookup "
"BE_ATTR_ZFS_PROPERTIES attribute\n"));
return (BE_ERR_INVAL);
}
if (zfs_props != NULL) {
if (!(zfs_props->nvl_nvflag & NV_UNIQUE_NAME) &&
!(zfs_props->nvl_nvflag & NV_UNIQUE_NAME_TYPE)) {
be_print_err(gettext("be_init: ZFS property list "
"not unique\n"));
return (BE_ERR_INVAL);
}
if (nvlist_dup(zfs_props, &bt.nbe_zfs_props, 0) != 0) {
be_print_err(gettext("be_init: failed to dup ZFS "
"property list\n"));
return (BE_ERR_NOMEM);
}
} else {
if (nvlist_alloc(&bt.nbe_zfs_props, NV_UNIQUE_NAME, 0) != 0) {
be_print_err(gettext("be_init: internal "
"error: out of memory\n"));
return (BE_ERR_NOMEM);
}
}
if (nvlist_add_string(bt.nbe_zfs_props,
zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), "/") != 0) {
be_print_err(gettext("be_init: internal error "
"out of memory\n"));
ret = BE_ERR_NOMEM;
goto done;
}
if (nvlist_add_string(bt.nbe_zfs_props,
zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto") != 0) {
be_print_err(gettext("be_init: internal error "
"out of memory\n"));
ret = BE_ERR_NOMEM;
goto done;
}
if (zfs_create(g_zfs, nbe_root_ds, ZFS_TYPE_FILESYSTEM,
bt.nbe_zfs_props) != 0) {
be_print_err(gettext("be_init: failed to "
"create BE root dataset (%s): %s\n"), nbe_root_ds,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
if ((ret = be_set_uuid(nbe_root_ds)) != BE_SUCCESS) {
be_print_err(gettext("be_init: failed to "
"set uuid for new BE\n"));
}
(void) nvlist_remove(bt.nbe_zfs_props,
zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), DATA_TYPE_STRING);
for (i = 0; i < fs_num && fs_names[i]; i++) {
if (strcmp(fs_names[i], "/") == 0)
continue;
(void) snprintf(child_fs, sizeof (child_fs), "%s%s",
nbe_root_ds, fs_names[i]);
if (zfs_create(g_zfs, child_fs, ZFS_TYPE_FILESYSTEM,
bt.nbe_zfs_props) != 0) {
be_print_err(gettext("be_init: failed to create "
"BE's child dataset (%s): %s\n"), child_fs,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
}
if (shared_fs_num > 0) {
nvlist_t *props = NULL;
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
be_print_err(gettext("be_init: nvlist_alloc failed\n"));
ret = BE_ERR_NOMEM;
goto done;
}
for (i = 0; i < shared_fs_num; i++) {
(void) snprintf(child_fs, sizeof (child_fs), "%s%s",
bt.nbe_zpool, shared_fs_names[i]);
if (nvlist_add_string(props,
zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
shared_fs_names[i]) != 0) {
be_print_err(gettext("be_init: "
"internal error: out of memory\n"));
nvlist_free(props);
ret = BE_ERR_NOMEM;
goto done;
}
if (zfs_dataset_exists(g_zfs, child_fs,
ZFS_TYPE_FILESYSTEM)) {
continue;
}
if (zfs_create(g_zfs, child_fs, ZFS_TYPE_FILESYSTEM,
props) != 0) {
be_print_err(gettext("be_init: failed to "
"create BE's shared dataset (%s): %s\n"),
child_fs, libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
nvlist_free(props);
goto done;
}
}
nvlist_free(props);
}
done:
nvlist_free(bt.nbe_zfs_props);
be_zfs_fini();
return (ret);
}
int
be_destroy(nvlist_t *be_attrs)
{
zfs_handle_t *zhp = NULL;
be_transaction_data_t bt = { 0 };
be_transaction_data_t cur_bt = { 0 };
be_destroy_data_t dd = { 0 };
int ret = BE_SUCCESS;
uint16_t flags = 0;
boolean_t bs_found = B_FALSE;
int zret;
char obe_root_ds[MAXPATHLEN];
char *mp = NULL;
if (!be_zfs_init())
return (BE_ERR_INIT);
if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &bt.obe_name)
!= 0) {
be_print_err(gettext("be_destroy: failed to lookup "
"BE_ATTR_ORIG_BE_NAME attribute\n"));
return (BE_ERR_INVAL);
}
if (!be_valid_be_name(bt.obe_name)) {
be_print_err(gettext("be_destroy: invalid BE name %s\n"),
bt.obe_name);
return (BE_ERR_INVAL);
} else if (bt.obe_name != NULL) {
if ((ret = be_find_current_be(&cur_bt)) != BE_SUCCESS) {
return (ret);
}
if (strcmp(cur_bt.obe_name, bt.obe_name) == 0) {
return (BE_ERR_DESTROY_CURR_BE);
}
}
if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
BE_ATTR_DESTROY_FLAGS, DATA_TYPE_UINT16, &flags, NULL)
!= 0) {
be_print_err(gettext("be_destroy: failed to lookup "
"BE_ATTR_DESTROY_FLAGS attribute\n"));
return (BE_ERR_INVAL);
}
dd.destroy_snaps = flags & BE_DESTROY_FLAG_SNAPSHOTS;
dd.force_unmount = flags & BE_DESTROY_FLAG_FORCE_UNMOUNT;
if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) {
be_print_err(gettext("be_destroy: 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_destroy: 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 (getzoneid() != GLOBAL_ZONEID) {
if (!be_zone_compare_uuids(bt.obe_root_ds)) {
if (be_is_active_on_boot(bt.obe_name)) {
be_print_err(gettext("be_destroy: destroying "
"active zone root dataset from non-active "
"global BE is not supported\n"));
return (BE_ERR_NOTSUP);
}
}
}
if (be_is_active_on_boot(bt.obe_name)) {
if ((ret = be_activate_current_be()) != BE_SUCCESS) {
be_print_err(gettext("be_destroy: failed to "
"make the current BE 'active on boot'\n"));
return (ret);
}
}
if (getzoneid() == GLOBAL_ZONEID) {
char *nextboot = NULL;
if (lzbe_get_boot_device(bt.obe_zpool, &nextboot) == 0 &&
nextboot != NULL && strcmp(nextboot, bt.obe_root_ds) == 0) {
if (lzbe_set_boot_device(bt.obe_zpool,
lzbe_add, "") != 0) {
be_print_err(gettext("be_destroy: failed to "
"remove temporary activation for "
"dataset %s on pool %s\n"),
bt.obe_root_ds, bt.obe_zpool);
free(nextboot);
return (BE_ERR_UNKNOWN);
}
}
free(nextboot);
}
if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
NULL) {
be_print_err(gettext("be_destroy: 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));
}
(void) zfs_iter_snapshots(zhp, B_FALSE, be_has_snapshot_callback,
&bs_found);
if (!dd.destroy_snaps && bs_found) {
ZFS_CLOSE(zhp);
return (BE_ERR_SS_EXISTS);
}
if (getzoneid() == GLOBAL_ZONEID) {
if (be_get_uuid(zfs_get_name(zhp),
&dd.gz_be_uuid) != BE_SUCCESS) {
be_print_err(gettext("be_destroy: BE has no "
"UUID (%s)\n"), zfs_get_name(zhp));
}
}
if (zfs_is_mounted(zhp, &mp)) {
if (!(dd.force_unmount)) {
be_print_err(gettext("be_destroy: "
"%s is currently mounted at %s, cannot destroy\n"),
bt.obe_name, mp != NULL ? mp : "<unknown>");
free(mp);
ZFS_CLOSE(zhp);
return (BE_ERR_MOUNTED);
}
free(mp);
}
if (getzoneid() == GLOBAL_ZONEID && !uuid_is_null(dd.gz_be_uuid)) {
if ((ret = be_destroy_zones(bt.obe_name, bt.obe_root_ds, &dd))
!= BE_SUCCESS) {
be_print_err(gettext("be_destroy: failed to "
"destroy one or more zones for BE %s\n"),
bt.obe_name);
goto done;
}
}
if (zfs_is_mounted(zhp, NULL)) {
if ((ret = _be_unmount(bt.obe_name, BE_UNMOUNT_FLAG_FORCE))
!= BE_SUCCESS) {
be_print_err(gettext("be_destroy: "
"failed to unmount %s\n"), bt.obe_name);
ZFS_CLOSE(zhp);
return (ret);
}
}
ZFS_CLOSE(zhp);
if ((ret = _be_destroy((const char *)bt.obe_root_ds, &dd))
!= BE_SUCCESS) {
goto done;
}
if (getzoneid() == GLOBAL_ZONEID) {
if ((ret = be_remove_menu(bt.obe_name, bt.obe_zpool, NULL))
!= BE_SUCCESS) {
be_print_err(gettext("be_destroy: failed to "
"remove BE %s from the boot menu\n"),
bt.obe_root_ds);
goto done;
}
}
done:
be_zfs_fini();
return (ret);
}
int
be_copy(nvlist_t *be_attrs)
{
be_transaction_data_t bt = { 0 };
be_fs_list_data_t fld = { 0 };
zfs_handle_t *zhp = NULL;
zpool_handle_t *zphp = NULL;
nvlist_t *zfs_props = NULL;
uuid_t uu = { 0 };
uuid_t parent_uu = { 0 };
char obe_root_ds[MAXPATHLEN];
char nbe_root_ds[MAXPATHLEN];
char obe_root_container[MAXPATHLEN];
char nbe_root_container[MAXPATHLEN];
char ss[MAXPATHLEN];
char *new_mp = NULL;
char *obe_name = NULL;
boolean_t autoname = B_FALSE;
boolean_t be_created = B_FALSE;
int i;
int zret;
int ret = BE_SUCCESS;
struct be_defaults be_defaults;
if (!be_zfs_init())
return (BE_ERR_INIT);
if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
BE_ATTR_ORIG_BE_NAME, DATA_TYPE_STRING, &obe_name, NULL) != 0) {
be_print_err(gettext("be_copy: failed to lookup "
"BE_ATTR_ORIG_BE_NAME attribute\n"));
return (BE_ERR_INVAL);
}
if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) {
return (ret);
}
be_get_defaults(&be_defaults);
if (obe_name != NULL) {
bt.obe_name = obe_name;
if (!be_valid_be_name(bt.obe_name)) {
be_print_err(gettext("be_copy: "
"invalid BE name %s\n"), bt.obe_name);
return (BE_ERR_INVAL);
}
}
if (be_defaults.be_deflt_rpool_container) {
if ((zphp = zpool_open(g_zfs, bt.obe_zpool)) == NULL) {
be_print_err(gettext("be_get_node_data: failed to "
"open rpool (%s): %s\n"), bt.obe_zpool,
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if (be_find_zpool_callback(zphp, &bt) == 0) {
return (BE_ERR_BE_NOENT);
}
} else {
if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) ==
0) {
be_print_err(gettext("be_copy: 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_copy: "
"zpool_iter failed: %s\n"),
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
}
if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
BE_ATTR_SNAP_NAME, DATA_TYPE_STRING, &bt.obe_snap_name, NULL)
!= 0) {
be_print_err(gettext("be_copy: failed to lookup "
"BE_ATTR_SNAP_NAME attribute\n"));
return (BE_ERR_INVAL);
}
if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
BE_ATTR_NEW_BE_NAME, DATA_TYPE_STRING, &bt.nbe_name, NULL)
!= 0) {
be_print_err(gettext("be_copy: failed to lookup "
"BE_ATTR_NEW_BE_NAME attribute\n"));
return (BE_ERR_INVAL);
}
if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
BE_ATTR_NEW_BE_POOL, DATA_TYPE_STRING, &bt.nbe_zpool, NULL) != 0) {
be_print_err(gettext("be_copy: failed to lookup "
"BE_ATTR_NEW_BE_POOL attribute\n"));
return (BE_ERR_INVAL);
}
if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
BE_ATTR_NEW_BE_DESC, DATA_TYPE_STRING, &bt.nbe_desc, NULL) != 0) {
be_print_err(gettext("be_copy: failed to lookup "
"BE_ATTR_NEW_BE_DESC attribute\n"));
return (BE_ERR_INVAL);
}
if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
BE_ATTR_POLICY, DATA_TYPE_STRING, &bt.policy, NULL) != 0) {
be_print_err(gettext("be_copy: failed to lookup "
"BE_ATTR_POLICY attribute\n"));
return (BE_ERR_INVAL);
}
if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
BE_ATTR_ZFS_PROPERTIES, DATA_TYPE_NVLIST, &zfs_props, NULL)
!= 0) {
be_print_err(gettext("be_copy: failed to lookup "
"BE_ATTR_ZFS_PROPERTIES attribute\n"));
return (BE_ERR_INVAL);
}
if (zfs_props != NULL) {
if (!(zfs_props->nvl_nvflag & NV_UNIQUE_NAME) &&
!(zfs_props->nvl_nvflag & NV_UNIQUE_NAME_TYPE)) {
be_print_err(gettext("be_copy: ZFS property list "
"not unique\n"));
return (BE_ERR_INVAL);
}
if (nvlist_dup(zfs_props, &bt.nbe_zfs_props, 0) != 0) {
be_print_err(gettext("be_copy: "
"failed to dup ZFS property list\n"));
return (BE_ERR_NOMEM);
}
} else {
if (nvlist_alloc(&bt.nbe_zfs_props, NV_UNIQUE_NAME, 0) != 0) {
be_print_err(gettext("be_copy: internal "
"error: out of memory\n"));
return (BE_ERR_NOMEM);
}
}
if (bt.nbe_name == NULL && bt.nbe_zpool != NULL) {
be_print_err(gettext("be_copy: cannot specify pool "
"name when creating an auto named BE\n"));
ret = BE_ERR_INVAL;
goto done;
}
if (bt.nbe_zpool == NULL)
bt.nbe_zpool = bt.obe_zpool;
if (bt.nbe_name != NULL) {
if (!be_valid_be_name(bt.nbe_name)) {
be_print_err(gettext("be_copy: "
"invalid BE name %s\n"), bt.nbe_name);
ret = BE_ERR_INVAL;
goto done;
}
if (getzoneid() == GLOBAL_ZONEID) {
if ((zret = zpool_iter(g_zfs, be_exists_callback,
bt.nbe_name)) > 0) {
be_print_err(gettext("be_copy: BE (%s) already "
"exists\n"), bt.nbe_name);
ret = BE_ERR_BE_EXISTS;
goto done;
} else if (zret < 0) {
be_print_err(gettext("be_copy: zpool_iter "
"failed: %s\n"),
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
} else {
if ((ret = be_make_root_ds(bt.nbe_zpool, bt.nbe_name,
nbe_root_ds, sizeof (nbe_root_ds))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE "
"container dataset for %s/%s\n"), __func__,
bt.nbe_zpool, bt.nbe_name);
goto done;
}
if (zfs_dataset_exists(g_zfs, nbe_root_ds,
ZFS_TYPE_FILESYSTEM)) {
be_print_err(gettext("be_copy: BE (%s) already "
"exists\n"), bt.nbe_name);
ret = BE_ERR_BE_EXISTS;
goto done;
}
}
} else {
if ((bt.nbe_name = be_auto_be_name(bt.obe_name))
== NULL) {
be_print_err(gettext("be_copy: "
"failed to generate auto BE name\n"));
ret = BE_ERR_AUTONAME;
goto done;
}
autoname = B_TRUE;
}
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);
goto done;
}
if ((ret = be_make_root_ds(bt.nbe_zpool, bt.nbe_name, nbe_root_ds,
sizeof (nbe_root_ds))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container dataset "
"for %s/%s\n"), __func__, bt.nbe_zpool, bt.nbe_name);
goto done;
}
bt.obe_root_ds = obe_root_ds;
bt.nbe_root_ds = nbe_root_ds;
if (bt.obe_snap_name != NULL) {
(void) snprintf(ss, sizeof (ss), "%s@%s", bt.obe_root_ds,
bt.obe_snap_name);
if (!zfs_dataset_exists(g_zfs, ss, ZFS_TYPE_SNAPSHOT)) {
be_print_err(gettext("be_copy: "
"snapshot does not exist (%s): %s\n"), ss,
libzfs_error_description(g_zfs));
ret = BE_ERR_SS_NOENT;
goto done;
}
} else {
if ((ret = _be_create_snapshot(bt.obe_name,
&bt.obe_snap_name, bt.policy)) != BE_SUCCESS) {
be_print_err(gettext("be_copy: "
"failed to create auto named snapshot\n"));
goto done;
}
if (nvlist_add_string(be_attrs, BE_ATTR_SNAP_NAME,
bt.obe_snap_name) != 0) {
be_print_err(gettext("be_copy: "
"failed to add snap name to be_attrs\n"));
ret = BE_ERR_NOMEM;
goto done;
}
}
if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM))
== NULL) {
be_print_err(gettext("be_copy: 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);
goto done;
}
if (zfs_is_mounted(zhp, &bt.obe_altroot) && bt.obe_altroot == NULL) {
be_print_err(gettext("be_copy: failed to "
"get altroot of mounted BE %s: %s\n"),
bt.obe_name, libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
if (strcmp(bt.obe_zpool, bt.nbe_zpool) == 0) {
if ((ret = be_clone_fs_callback(zhp, &bt)) != 0) {
zhp = NULL;
if (!autoname || ret != BE_ERR_BE_EXISTS) {
be_print_err(gettext("be_copy: "
"failed to clone new BE (%s) from "
"orig BE (%s)\n"),
bt.nbe_name, bt.obe_name);
ret = BE_ERR_CLONE;
goto done;
}
for (i = 1; i < BE_AUTO_NAME_MAX_TRY; i++) {
(void) sleep(1);
free(bt.nbe_name);
if ((bt.nbe_name = be_auto_be_name(bt.obe_name))
== NULL) {
be_print_err(gettext("be_copy: "
"failed to generate auto "
"BE name\n"));
ret = BE_ERR_AUTONAME;
goto done;
}
if ((ret = be_make_root_ds(bt.nbe_zpool,
bt.nbe_name, nbe_root_ds,
sizeof (nbe_root_ds))) != BE_SUCCESS) {
be_print_err(gettext(
"%s: failed to get BE container "
"dataset for %s/%s\n"), __func__,
bt.nbe_zpool, bt.nbe_name);
goto done;
}
bt.nbe_root_ds = nbe_root_ds;
if ((zhp = zfs_open(g_zfs, bt.obe_root_ds,
ZFS_TYPE_FILESYSTEM)) == NULL) {
be_print_err(gettext("be_copy: "
"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);
goto done;
}
ret = be_clone_fs_callback(zhp, &bt);
zhp = NULL;
if (ret == 0) {
break;
} else if (ret != BE_ERR_BE_EXISTS) {
be_print_err(gettext("be_copy: "
"failed to clone new BE "
"(%s) from orig BE (%s)\n"),
bt.nbe_name, bt.obe_name);
ret = BE_ERR_CLONE;
goto done;
}
}
if (i == BE_AUTO_NAME_MAX_TRY) {
be_print_err(gettext("be_copy: failed "
"to create unique auto BE name\n"));
free(bt.nbe_name);
bt.nbe_name = NULL;
ret = BE_ERR_AUTONAME;
goto done;
}
}
zhp = NULL;
} else {
if (!be_create_container_ds(bt.nbe_zpool)) {
ret = BE_ERR_CREATDS;
goto done;
}
if ((ret = be_send_fs_callback(zhp, &bt)) != 0) {
be_print_err(gettext("be_copy: failed to "
"send BE (%s) to pool (%s)\n"), bt.obe_name,
bt.nbe_zpool);
ret = BE_ERR_COPY;
zhp = NULL;
goto done;
}
zhp = NULL;
}
be_created = B_TRUE;
if ((ret = _be_mount(bt.nbe_name, &new_mp, BE_MOUNT_FLAG_NO_ZONES))
!= BE_SUCCESS) {
be_print_err(gettext("be_copy: failed to "
"mount newly created BE\n"));
(void) _be_unmount(bt.nbe_name, 0);
goto done;
}
if (getzoneid() == GLOBAL_ZONEID) {
if (be_set_uuid(bt.nbe_root_ds) != BE_SUCCESS) {
be_print_err(gettext("be_copy: failed to "
"set uuid for new BE\n"));
}
} else {
if ((ret = be_zone_get_parent_uuid(bt.obe_root_ds,
&parent_uu)) != BE_SUCCESS) {
be_print_err(gettext("be_copy: failed to get "
"parentbe uuid from orig BE\n"));
ret = BE_ERR_ZONE_NO_PARENTBE;
goto done;
} else if ((ret = be_zone_set_parent_uuid(bt.nbe_root_ds,
parent_uu)) != BE_SUCCESS) {
be_print_err(gettext("be_copy: failed to set "
"parentbe uuid for newly created BE\n"));
goto done;
}
}
if (getzoneid() == GLOBAL_ZONEID &&
be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) {
if ((ret = be_copy_zones(bt.obe_name, bt.obe_root_ds,
bt.nbe_root_ds)) != BE_SUCCESS) {
be_print_err(gettext("be_copy: failed to process "
"zones\n"));
goto done;
}
}
if ((ret = be_get_legacy_fs(bt.obe_name, bt.obe_root_ds, NULL, NULL,
&fld)) != BE_SUCCESS) {
be_print_err(gettext("be_copy: failed to "
"get legacy mounted file system list for %s\n"),
bt.obe_name);
goto done;
}
if ((ret = be_make_root_container_ds(bt.obe_zpool, obe_root_container,
sizeof (obe_root_container))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container dataset "
"for %s\n"), __func__, bt.obe_zpool);
goto done;
}
if ((ret = be_make_root_container_ds(bt.nbe_zpool, nbe_root_container,
sizeof (nbe_root_container))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container dataset "
"for %s\n"), __func__, bt.nbe_zpool);
goto done;
}
if ((ret = be_update_vfstab(bt.nbe_name, obe_root_container,
nbe_root_container, &fld, new_mp)) != BE_SUCCESS) {
be_print_err(gettext("be_copy: failed to "
"update new BE's vfstab (%s)\n"), bt.nbe_name);
goto done;
}
if ((ret = _be_unmount(bt.nbe_name, 0)) != BE_SUCCESS) {
be_print_err(gettext("be_copy: failed to "
"unmount newly created BE\n"));
goto done;
}
if (getzoneid() == GLOBAL_ZONEID &&
(ret = be_append_menu(bt.nbe_name, bt.nbe_zpool,
NULL, bt.obe_root_ds, bt.nbe_desc)) != BE_SUCCESS) {
be_print_err(gettext("be_copy: failed to "
"add BE (%s) to boot menu\n"), bt.nbe_name);
goto done;
}
if (autoname) {
if ((zhp = zfs_open(g_zfs, bt.nbe_root_ds,
ZFS_TYPE_FILESYSTEM)) == NULL) {
be_print_err(gettext("be_copy: failed to "
"open BE root dataset (%s): %s\n"), bt.nbe_root_ds,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
if (bt.policy == NULL) {
bt.policy = be_default_policy();
}
if (zfs_prop_set(zhp, BE_POLICY_PROPERTY, bt.policy) != 0) {
be_print_err(gettext("be_copy: failed to "
"set BE policy for %s: %s\n"), bt.nbe_name,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
if (bt.nbe_name) {
if (nvlist_add_string(be_attrs, BE_ATTR_NEW_BE_NAME,
bt.nbe_name) != 0) {
be_print_err(gettext("be_copy: failed to "
"add snap name to be_attrs\n"));
}
}
}
done:
ZFS_CLOSE(zhp);
be_free_fs_list(&fld);
nvlist_free(bt.nbe_zfs_props);
free(bt.obe_altroot);
free(new_mp);
if (ret != BE_SUCCESS && be_created) {
be_destroy_data_t cdd = { 0 };
cdd.force_unmount = B_TRUE;
be_print_err(gettext("be_copy: "
"destroying partially created boot environment\n"));
if (getzoneid() == GLOBAL_ZONEID && be_get_uuid(bt.nbe_root_ds,
&cdd.gz_be_uuid) == 0)
(void) be_destroy_zones(bt.nbe_name, bt.nbe_root_ds,
&cdd);
(void) _be_destroy(bt.nbe_root_ds, &cdd);
}
be_zfs_fini();
return (ret);
}
int
be_find_zpool_callback(zpool_handle_t *zlp, void *data)
{
be_transaction_data_t *bt = data;
const char *zpool = zpool_get_name(zlp);
char be_root_ds[MAXPATHLEN];
int ret = 0;
if (be_make_root_ds(zpool, bt->obe_name, be_root_ds,
sizeof (be_root_ds)) != BE_SUCCESS) {
goto out;
}
if (zfs_dataset_exists(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM)) {
bt->obe_zpool = strdup(zpool);
ret = 1;
}
out:
zpool_close(zlp);
return (ret);
}
int
be_exists_callback(zpool_handle_t *zlp, void *data)
{
const char *zpool = zpool_get_name(zlp);
char *be_name = data;
char be_root_ds[MAXPATHLEN];
int ret = 0;
if (be_make_root_ds(zpool, be_name, be_root_ds,
sizeof (be_root_ds)) != BE_SUCCESS) {
goto out;
}
if (zfs_dataset_exists(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM)) {
ret = 1;
}
out:
zpool_close(zlp);
return (ret);
}
static int
be_has_snapshot_callback(zfs_handle_t *zhp, void *data)
{
boolean_t *bs = data;
if (zfs_get_name(zhp) == NULL) {
zfs_close(zhp);
return (1);
}
*bs = B_TRUE;
zfs_close(zhp);
return (0);
}
int
be_set_uuid(char *root_ds)
{
zfs_handle_t *zhp = NULL;
uuid_t uu = { 0 };
char uu_string[UUID_PRINTABLE_STRING_LENGTH] = { 0 };
int ret = BE_SUCCESS;
uuid_generate(uu);
if (uuid_is_null(uu) != 0) {
be_print_err(gettext("be_set_uuid: failed to "
"generate uuid\n"));
return (BE_ERR_GEN_UUID);
}
uuid_unparse(uu, uu_string);
if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
be_print_err(gettext("be_set_uuid: failed to "
"open BE root 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_UUID_PROPERTY, uu_string) != 0) {
be_print_err(gettext("be_set_uuid: failed to "
"set uuid property for BE: %s\n"),
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
}
ZFS_CLOSE(zhp);
return (ret);
}
int
be_get_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_get_uuid: failed to "
"open BE 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_get_uuid: failed to "
"get user properties for BE 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_UUID_PROPERTY, &propname) != 0 ||
nvlist_lookup_string(propname, ZPROP_VALUE, &uu_string) != 0) {
be_print_err(gettext("be_get_uuid: failed to "
"get uuid property from BE root dataset user "
"properties.\n"));
ret = BE_ERR_NO_UUID;
goto done;
}
if (uuid_parse(uu_string, *uu) != 0 || uuid_is_null(*uu)) {
be_print_err(gettext("be_get_uuid: failed to "
"parse uuid\n"));
ret = BE_ERR_PARSE_UUID;
goto done;
}
done:
ZFS_CLOSE(zhp);
return (ret);
}
static int
_be_destroy(const char *root_ds, be_destroy_data_t *dd)
{
zfs_handle_t *zhp = NULL;
char origin[MAXPATHLEN];
char parent[MAXPATHLEN];
char *snap = NULL;
boolean_t has_origin = B_FALSE;
int ret = BE_SUCCESS;
if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) ==
NULL) {
be_print_err(gettext("be_destroy: failed to "
"open BE root dataset (%s): %s\n"), root_ds,
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if (be_demote_callback(zhp, NULL) != 0) {
be_print_err(gettext("be_destroy: "
"failed to demote BE %s\n"), root_ds);
return (BE_ERR_DEMOTE);
}
if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) ==
NULL) {
be_print_err(gettext("be_destroy: failed to "
"open BE root dataset (%s): %s\n"), root_ds,
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL,
NULL, 0, B_FALSE) == 0) {
(void) strlcpy(parent, origin, sizeof (parent));
if (be_get_snap(parent, &snap) != BE_SUCCESS) {
ZFS_CLOSE(zhp);
be_print_err(gettext("be_destroy: failed to "
"get snapshot name from origin %s\n"), origin);
return (BE_ERR_INVAL);
}
has_origin = B_TRUE;
}
if (be_destroy_callback(zhp, dd) != 0) {
be_print_err(gettext("be_destroy: failed to "
"destroy BE %s\n"), root_ds);
ret = zfs_err_to_be_err(g_zfs);
return (ret);
}
if (has_origin) {
if ((zhp = zfs_open(g_zfs, origin, ZFS_TYPE_SNAPSHOT)) ==
NULL) {
be_print_err(gettext("be_destroy: failed to "
"open BE's origin (%s): %s\n"), origin,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
return (ret);
}
if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) != 0) {
ZFS_CLOSE(zhp);
return (ret);
}
ZFS_CLOSE(zhp);
if ((zhp = zfs_open(g_zfs, parent, ZFS_TYPE_FILESYSTEM)) ==
NULL) {
be_print_err(gettext("be_destroy: failed to "
"open BE's parent root dataset (%s): %s\n"), parent,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
return (ret);
}
if (zfs_destroy_snaps(zhp, snap, B_FALSE) != 0) {
be_print_err(gettext("be_destroy: failed to "
"destroy original snapshots used to create "
"BE: %s\n"), libzfs_error_description(g_zfs));
if (libzfs_errno(g_zfs) != EZFS_EXISTS) {
ret = zfs_err_to_be_err(g_zfs);
ZFS_CLOSE(zhp);
return (ret);
}
}
ZFS_CLOSE(zhp);
}
return (ret);
}
static int
be_destroy_zones(char *be_name, char *be_root_ds, be_destroy_data_t *dd)
{
int i;
int ret = BE_SUCCESS;
int force_umnt = BE_UNMOUNT_FLAG_NULL;
char *zonepath = NULL;
char *zonename = NULL;
char *zonepath_ds = NULL;
char *mp = NULL;
zoneList_t zlist = NULL;
zoneBrandList_t *brands = NULL;
zfs_handle_t *zhp = NULL;
if (!z_zones_are_implemented()) {
return (BE_SUCCESS);
}
if ((brands = be_get_supported_brandlist()) == NULL) {
be_print_err(gettext("be_destroy_zones: "
"no supported brands\n"));
return (BE_SUCCESS);
}
if ((zhp = zfs_open(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM)) ==
NULL) {
be_print_err(gettext("be_destroy_zones: failed to "
"open BE root dataset (%s): %s\n"), be_root_ds,
libzfs_error_description(g_zfs));
z_free_brand_list(brands);
return (zfs_err_to_be_err(g_zfs));
}
if (!zfs_is_mounted(zhp, &mp)) {
if ((ret = _be_mount(be_name, &mp,
BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
be_print_err(gettext("be_destroy_zones: failed to "
"mount the BE (%s) for zones processing.\n"),
be_name);
ZFS_CLOSE(zhp);
z_free_brand_list(brands);
return (ret);
}
}
ZFS_CLOSE(zhp);
z_set_zone_root(mp);
free(mp);
if ((zlist = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) {
z_free_brand_list(brands);
return (BE_SUCCESS);
}
if (dd->force_unmount)
force_umnt = BE_UNMOUNT_FLAG_FORCE;
if ((ret = _be_unmount(be_name, force_umnt)) != BE_SUCCESS) {
be_print_err(gettext("be_destroy_zones: failed to "
"unmount the BE (%s)\n"), be_name);
goto done;
}
for (i = 0; (zonename = z_zlist_get_zonename(zlist, i)) != NULL; i++) {
if (z_zlist_get_current_state(zlist, i) < ZONE_STATE_INSTALLED)
continue;
zonepath = z_zlist_get_zonepath(zlist, i);
if ((zonepath_ds = be_get_ds_from_dir(zonepath)) == NULL)
continue;
if (!be_zone_supported(zonepath_ds)) {
free(zonepath_ds);
continue;
}
if ((ret = be_destroy_zone_roots(zonepath_ds, dd))
!= BE_SUCCESS) {
be_print_err(gettext("be_destroy_zones: failed to "
"find and destroy zone roots for zone %s\n"),
zonename);
free(zonepath_ds);
goto done;
}
free(zonepath_ds);
}
done:
z_free_brand_list(brands);
z_free_zone_list(zlist);
return (ret);
}
static int
be_destroy_zone_roots(char *zonepath_ds, be_destroy_data_t *dd)
{
zfs_handle_t *zhp;
char zone_container_ds[MAXPATHLEN];
int ret = BE_SUCCESS;
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_destroy_zone_roots: 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_destroy_zone_roots_callback,
dd)) != 0) {
be_print_err(gettext("be_destroy_zone_roots: failed to "
"destroy zone roots under zonepath dataset %s: %s\n"),
zonepath_ds, libzfs_error_description(g_zfs));
ZFS_CLOSE(zhp);
return (ret);
}
ZFS_CLOSE(zhp);
if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM))
== NULL) {
be_print_err(gettext("be_destroy_zone_roots: 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 (zfs_iter_filesystems(zhp, be_zone_root_exists_callback, NULL)
== 0) {
if (zfs_unmount(zhp, NULL, MS_FORCE) != 0 ||
zfs_destroy(zhp, B_FALSE) != 0) {
be_print_err(gettext("be_destroy_zone_roots: failed to "
"destroy zone root container dataset (%s): %s\n"),
zone_container_ds, libzfs_error_description(g_zfs));
goto done;
}
ZFS_CLOSE(zhp);
if ((zhp = zfs_open(g_zfs, zonepath_ds, ZFS_TYPE_FILESYSTEM))
== NULL) {
be_print_err(gettext("be_destroy_zone_roots: failed to "
"open zonepath dataset (%s): %s\n"),
zonepath_ds, libzfs_error_description(g_zfs));
goto done;
}
if (zfs_unmount(zhp, NULL, MS_FORCE) != 0 ||
zfs_destroy(zhp, B_FALSE) != 0) {
be_print_err(gettext("be_destroy_zone_roots: "
"failed to destroy zonepath dataest %s: %s\n"),
zonepath_ds, libzfs_error_description(g_zfs));
goto done;
}
}
done:
ZFS_CLOSE(zhp);
return (ret);
}
static int
be_destroy_zone_roots_callback(zfs_handle_t *zhp, void *data)
{
be_destroy_data_t *dd = data;
uuid_t parent_uuid = { 0 };
int ret = 0;
if (be_zone_get_parent_uuid(zfs_get_name(zhp), &parent_uuid)
!= BE_SUCCESS) {
be_print_err(gettext("be_destroy_zone_roots_callback: "
"could not get parentuuid for zone root dataset %s\n"),
zfs_get_name(zhp));
ZFS_CLOSE(zhp);
return (0);
}
if (uuid_compare(dd->gz_be_uuid, parent_uuid) == 0) {
if ((ret = _be_destroy(zfs_get_name(zhp), dd)) != BE_SUCCESS) {
be_print_err(gettext("be_destroy_zone_root_callback: "
"failed to destroy zone root %s\n"),
zfs_get_name(zhp));
ZFS_CLOSE(zhp);
return (ret);
}
}
ZFS_CLOSE(zhp);
return (ret);
}
static int
be_copy_zones(char *obe_name, char *obe_root_ds, char *nbe_root_ds)
{
int i, num_retries;
int ret = BE_SUCCESS;
int iret = 0;
char *zonename = NULL;
char *zonepath = NULL;
char *zone_be_name = NULL;
char *temp_mntpt = NULL;
char *new_zone_be_name = NULL;
char zoneroot[MAXPATHLEN];
char zoneroot_ds[MAXPATHLEN];
char zone_container_ds[MAXPATHLEN];
char new_zoneroot_ds[MAXPATHLEN];
char ss[MAXPATHLEN];
uuid_t uu = { 0 };
char uu_string[UUID_PRINTABLE_STRING_LENGTH] = { 0 };
be_transaction_data_t bt = { 0 };
zfs_handle_t *obe_zhp = NULL;
zfs_handle_t *nbe_zhp = NULL;
zfs_handle_t *z_zhp = NULL;
zoneList_t zlist = NULL;
zoneBrandList_t *brands = NULL;
boolean_t mounted_here = B_FALSE;
char *snap_name = NULL;
if (!z_zones_are_implemented()) {
return (BE_SUCCESS);
}
if ((brands = be_get_supported_brandlist()) == NULL) {
be_print_err(gettext("be_copy_zones: "
"no supported brands\n"));
return (BE_SUCCESS);
}
if ((obe_zhp = zfs_open(g_zfs, obe_root_ds, ZFS_TYPE_FILESYSTEM))
== NULL) {
be_print_err(gettext("be_copy_zones: failed to open "
"the origin BE root dataset (%s) for zones processing: "
"%s\n"), obe_root_ds, libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if ((nbe_zhp = zfs_open(g_zfs, nbe_root_ds, ZFS_TYPE_FILESYSTEM))
== NULL) {
be_print_err(gettext("be_copy_zones: failed to open "
"the new BE root dataset (%s): %s\n"), nbe_root_ds,
libzfs_error_description(g_zfs));
ZFS_CLOSE(obe_zhp);
return (zfs_err_to_be_err(g_zfs));
}
if (be_get_uuid(zfs_get_name(nbe_zhp), &uu) != BE_SUCCESS) {
be_print_err(gettext("be_copy_zones: "
"failed to get uuid for BE root "
"dataset %s\n"), zfs_get_name(nbe_zhp));
ZFS_CLOSE(nbe_zhp);
goto done;
}
ZFS_CLOSE(nbe_zhp);
uuid_unparse(uu, uu_string);
if (!zfs_is_mounted(obe_zhp, &temp_mntpt)) {
if ((ret = _be_mount(obe_name, &temp_mntpt,
BE_MOUNT_FLAG_NULL)) != BE_SUCCESS) {
be_print_err(gettext("be_copy_zones: failed to "
"mount the BE (%s) for zones procesing.\n"),
obe_name);
goto done;
}
mounted_here = B_TRUE;
}
z_set_zone_root(temp_mntpt);
if ((zlist = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) {
ret = BE_SUCCESS;
goto done;
}
for (i = 0; (zonename = z_zlist_get_zonename(zlist, i)) != NULL; i++) {
be_fs_list_data_t fld = { 0 };
char zonepath_ds[MAXPATHLEN];
char *ds = NULL;
zonepath = z_zlist_get_zonepath(zlist, i);
if (z_zlist_get_current_state(zlist, i) < ZONE_STATE_INSTALLED)
continue;
if ((ds = be_get_ds_from_dir(zonepath)) == NULL)
continue;
(void) strlcpy(zonepath_ds, ds, sizeof (zonepath_ds));
free(ds);
ds = NULL;
be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot));
if (!be_zone_supported(zonepath_ds)) {
continue;
}
if ((ret = be_find_active_zone_root(obe_zhp, zonepath_ds,
zoneroot_ds, sizeof (zoneroot_ds))) != BE_SUCCESS) {
be_print_err(gettext("be_copy_zones: "
"failed to find active zone root for zone %s "
"in BE %s\n"), zonename, obe_name);
goto done;
}
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);
goto done;
}
if ((z_zhp = zfs_open(g_zfs, zoneroot_ds,
ZFS_TYPE_FILESYSTEM)) == NULL) {
be_print_err(gettext("be_copy_zones: "
"failed to open zone root dataset (%s): %s\n"),
zoneroot_ds, libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
zone_be_name =
be_get_zone_be_name(zoneroot_ds, zone_container_ds);
if ((new_zone_be_name = be_auto_zone_be_name(zone_container_ds,
zone_be_name)) == NULL) {
be_print_err(gettext("be_copy_zones: failed "
"to generate auto name for zone BE.\n"));
ret = BE_ERR_AUTONAME;
goto done;
}
if ((snap_name = be_auto_snap_name()) == NULL) {
be_print_err(gettext("be_copy_zones: failed to "
"generate snapshot name for zone BE.\n"));
ret = BE_ERR_AUTONAME;
goto done;
}
(void) snprintf(ss, sizeof (ss), "%s@%s", zoneroot_ds,
snap_name);
if (zfs_snapshot(g_zfs, ss, B_TRUE, NULL) != 0) {
be_print_err(gettext("be_copy_zones: "
"failed to snapshot zone BE (%s): %s\n"),
ss, libzfs_error_description(g_zfs));
if (libzfs_errno(g_zfs) == EZFS_EXISTS)
ret = BE_ERR_ZONE_SS_EXISTS;
else
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
(void) snprintf(new_zoneroot_ds, sizeof (new_zoneroot_ds),
"%s/%s", zone_container_ds, new_zone_be_name);
bt.obe_name = zone_be_name;
bt.obe_root_ds = zoneroot_ds;
bt.obe_snap_name = snap_name;
bt.obe_altroot = temp_mntpt;
bt.nbe_name = new_zone_be_name;
bt.nbe_root_ds = new_zoneroot_ds;
if (nvlist_alloc(&bt.nbe_zfs_props, NV_UNIQUE_NAME, 0) != 0) {
be_print_err(gettext("be_copy_zones: "
"internal error: out of memory\n"));
ret = BE_ERR_NOMEM;
goto done;
}
if ((iret = be_clone_fs_callback(z_zhp, &bt)) != 0) {
z_zhp = NULL;
if (iret != BE_ERR_BE_EXISTS) {
be_print_err(gettext("be_copy_zones: "
"failed to create zone BE clone for new "
"zone BE %s\n"), new_zone_be_name);
ret = iret;
nvlist_free(bt.nbe_zfs_props);
goto done;
}
for (num_retries = 1;
num_retries < BE_AUTO_NAME_MAX_TRY;
num_retries++) {
(void) sleep(1);
free(new_zone_be_name);
if ((new_zone_be_name = be_auto_zone_be_name(
zone_container_ds,
zone_be_name)) == NULL) {
be_print_err(gettext("be_copy_zones: "
"failed to generate auto name "
"for zone BE.\n"));
ret = BE_ERR_AUTONAME;
nvlist_free(bt.nbe_zfs_props);
goto done;
}
(void) snprintf(new_zoneroot_ds,
sizeof (new_zoneroot_ds),
"%s/%s", zone_container_ds,
new_zone_be_name);
bt.nbe_name = new_zone_be_name;
bt.nbe_root_ds = new_zoneroot_ds;
if ((z_zhp = zfs_open(g_zfs, zoneroot_ds,
ZFS_TYPE_FILESYSTEM)) == NULL) {
be_print_err(gettext("be_copy_zones: "
"failed to open zone root "
"dataset (%s): %s\n"),
zoneroot_ds,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
nvlist_free(bt.nbe_zfs_props);
goto done;
}
iret = be_clone_fs_callback(z_zhp, &bt);
z_zhp = NULL;
if (iret == 0) {
break;
} else if (iret != BE_ERR_BE_EXISTS) {
be_print_err(gettext("be_copy_zones: "
"failed to create zone BE clone "
"for new zone BE %s\n"),
new_zone_be_name);
ret = iret;
nvlist_free(bt.nbe_zfs_props);
goto done;
}
}
if (num_retries == BE_AUTO_NAME_MAX_TRY) {
be_print_err(gettext("be_copy_zones: failed "
"to create a unique auto zone BE name\n"));
free(bt.nbe_name);
bt.nbe_name = NULL;
ret = BE_ERR_AUTONAME;
nvlist_free(bt.nbe_zfs_props);
goto done;
}
}
nvlist_free(bt.nbe_zfs_props);
z_zhp = NULL;
if ((z_zhp = zfs_open(g_zfs, new_zoneroot_ds,
ZFS_TYPE_FILESYSTEM)) == NULL) {
be_print_err(gettext("be_copy_zones: "
"failed to open the new zone BE root dataset "
"(%s): %s\n"), new_zoneroot_ds,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
if (zfs_prop_set(z_zhp, BE_ZONE_PARENTBE_PROPERTY,
uu_string) != 0) {
be_print_err(gettext("be_copy_zones: "
"failed to set parentbe property\n"));
ZFS_CLOSE(z_zhp);
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
if (zfs_prop_set(z_zhp, BE_ZONE_ACTIVE_PROPERTY, "on") != 0) {
be_print_err(gettext("be_copy_zones: "
"failed to set active property\n"));
ZFS_CLOSE(z_zhp);
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
if ((ret = be_get_legacy_fs(obe_name, obe_root_ds,
zoneroot_ds, zoneroot, &fld)) != BE_SUCCESS) {
be_print_err(gettext("be_copy_zones: "
"failed to get legacy mounted file system "
"list for zone %s\n"), zonename);
ZFS_CLOSE(z_zhp);
goto done;
}
if ((ret = be_update_zone_vfstab(z_zhp, bt.nbe_name,
zonepath_ds, zonepath_ds, &fld)) != BE_SUCCESS) {
be_print_err(gettext("be_copy_zones: "
"failed to update new BE's vfstab (%s)\n"),
bt.nbe_name);
ZFS_CLOSE(z_zhp);
be_free_fs_list(&fld);
goto done;
}
be_free_fs_list(&fld);
ZFS_CLOSE(z_zhp);
}
done:
free(snap_name);
if (brands != NULL)
z_free_brand_list(brands);
if (zlist != NULL)
z_free_zone_list(zlist);
if (mounted_here)
(void) _be_unmount(obe_name, 0);
ZFS_CLOSE(obe_zhp);
return (ret);
}
static int
be_clone_fs_callback(zfs_handle_t *zhp, void *data)
{
be_transaction_data_t *bt = data;
zfs_handle_t *zhp_ss = NULL;
char prop_buf[MAXPATHLEN];
char zhp_name[ZFS_MAX_DATASET_NAME_LEN];
char clone_ds[MAXPATHLEN];
char ss[MAXPATHLEN];
int ret = 0;
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, prop_buf,
ZFS_MAXPROPLEN, NULL, NULL, 0, B_FALSE) != 0) {
be_print_err(gettext("be_clone_fs_callback: "
"failed to get dataset mountpoint (%s): %s\n"),
zfs_get_name(zhp), libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
ZFS_CLOSE(zhp);
return (ret);
}
if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) != 0 &&
strcmp(prop_buf, "legacy") != 0) {
goto zoned;
}
(void) strlcpy(zhp_name, zfs_get_name(zhp), sizeof (zhp_name));
if ((ret = be_prep_clone_send_fs(zhp, bt, clone_ds,
sizeof (clone_ds))) != BE_SUCCESS) {
ZFS_CLOSE(zhp);
return (ret);
}
(void) snprintf(ss, sizeof (ss), "%s@%s", zhp_name,
bt->obe_snap_name);
if ((zhp_ss = zfs_open(g_zfs, ss, ZFS_TYPE_SNAPSHOT)) == NULL) {
be_print_err(gettext("be_clone_fs_callback: "
"failed to get handle to snapshot (%s): %s\n"), ss,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
ZFS_CLOSE(zhp);
return (ret);
}
if (zfs_clone(zhp_ss, clone_ds, bt->nbe_zfs_props) != 0) {
be_print_err(gettext("be_clone_fs_callback: "
"failed to create clone dataset (%s): %s\n"),
clone_ds, libzfs_error_description(g_zfs));
ZFS_CLOSE(zhp_ss);
ZFS_CLOSE(zhp);
return (zfs_err_to_be_err(g_zfs));
}
ZFS_CLOSE(zhp_ss);
zoned:
if ((ret = zfs_iter_filesystems(zhp, be_clone_fs_callback, bt)) != 0) {
zfs_handle_t *d_zhp = NULL;
ZFS_CLOSE(zhp);
if ((d_zhp = zfs_open(g_zfs, clone_ds, ZFS_TYPE_FILESYSTEM))
== NULL) {
return (ret);
}
(void) zfs_destroy(d_zhp, B_FALSE);
ZFS_CLOSE(d_zhp);
return (ret);
}
ZFS_CLOSE(zhp);
return (0);
}
static int
be_send_fs_callback(zfs_handle_t *zhp, void *data)
{
be_transaction_data_t *bt = data;
recvflags_t flags = { 0 };
char zhp_name[ZFS_MAX_DATASET_NAME_LEN];
char clone_ds[MAXPATHLEN];
sendflags_t send_flags = { 0 };
int pid, status, retval;
int srpipe[2];
int ret = 0;
(void) strlcpy(zhp_name, zfs_get_name(zhp), sizeof (zhp_name));
if ((ret = be_prep_clone_send_fs(zhp, bt, clone_ds,
sizeof (clone_ds))) != BE_SUCCESS) {
ZFS_CLOSE(zhp);
return (ret);
}
if (zfs_create(g_zfs, clone_ds, ZFS_TYPE_FILESYSTEM, bt->nbe_zfs_props)
!= 0) {
be_print_err(gettext("be_send_fs_callback: "
"failed to create new dataset '%s': %s\n"),
clone_ds, libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
ZFS_CLOSE(zhp);
return (ret);
}
flags.force = B_TRUE;
if (pipe(srpipe) != 0) {
int err = errno;
be_print_err(gettext("be_send_fs_callback: failed to "
"open pipe\n"));
ZFS_CLOSE(zhp);
return (errno_to_be_err(err));
}
if ((pid = fork()) == -1) {
int err = errno;
be_print_err(gettext("be_send_fs_callback: failed to fork\n"));
(void) close(srpipe[0]);
(void) close(srpipe[1]);
ZFS_CLOSE(zhp);
return (errno_to_be_err(err));
} else if (pid == 0) {
(void) close(srpipe[0]);
if (zfs_send(zhp, NULL, bt->obe_snap_name, &send_flags,
srpipe[1], NULL, NULL, NULL) != 0) {
_exit(1);
}
ZFS_CLOSE(zhp);
_exit(0);
}
(void) close(srpipe[1]);
if (zfs_receive(g_zfs, clone_ds, NULL, &flags, srpipe[0], NULL) != 0) {
be_print_err(gettext("be_send_fs_callback: failed to "
"recv dataset (%s)\n"), clone_ds);
}
(void) close(srpipe[0]);
do {
retval = waitpid(pid, &status, 0);
if (retval == -1) {
status = 0;
}
} while (retval != pid);
if (WEXITSTATUS(status) != 0) {
be_print_err(gettext("be_send_fs_callback: failed to "
"send dataset (%s)\n"), zhp_name);
ZFS_CLOSE(zhp);
return (BE_ERR_ZFS);
}
if ((ret = zfs_iter_filesystems(zhp, be_send_fs_callback, bt)) != 0) {
zfs_handle_t *d_zhp = NULL;
ZFS_CLOSE(zhp);
if ((d_zhp = zfs_open(g_zfs, clone_ds, ZFS_TYPE_FILESYSTEM))
== NULL) {
return (ret);
}
(void) zfs_destroy(d_zhp, B_FALSE);
ZFS_CLOSE(d_zhp);
return (ret);
}
ZFS_CLOSE(zhp);
return (0);
}
static int
be_destroy_callback(zfs_handle_t *zhp, void *data)
{
be_destroy_data_t *dd = data;
int ret = 0;
if ((ret = zfs_iter_filesystems(zhp, be_destroy_callback, dd)) != 0) {
ZFS_CLOSE(zhp);
return (ret);
}
if (dd->destroy_snaps) {
if ((ret = zfs_iter_snapshots(zhp, B_FALSE, be_destroy_callback,
dd))
!= 0) {
ZFS_CLOSE(zhp);
return (ret);
}
}
if (dd->force_unmount) {
if ((ret = zfs_unmount(zhp, NULL, MS_FORCE)) != 0) {
be_print_err(gettext("be_destroy_callback: "
"failed to unmount %s: %s\n"), zfs_get_name(zhp),
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
ZFS_CLOSE(zhp);
return (ret);
}
}
if (zfs_destroy(zhp, B_FALSE) != 0) {
be_print_err(gettext("be_destroy_callback: "
"failed to destroy %s: %s\n"), zfs_get_name(zhp),
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
ZFS_CLOSE(zhp);
return (ret);
}
ZFS_CLOSE(zhp);
return (0);
}
static int
be_demote_callback(zfs_handle_t *zhp, void *data)
{
be_demote_data_t dd = { 0 };
int i, ret = 0;
dd.find_in_BE = B_TRUE;
for (i = 0; i < 2; i++) {
if (zfs_iter_snapshots(zhp, B_FALSE,
be_demote_find_clone_callback, &dd) != 0) {
be_print_err(gettext("be_demote_callback: "
"failed to iterate snapshots for %s: %s\n"),
zfs_get_name(zhp), libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
ZFS_CLOSE(zhp);
return (ret);
}
if (dd.clone_zhp != NULL) {
if (zfs_promote(dd.clone_zhp) != 0) {
be_print_err(gettext("be_demote_callback: "
"failed to promote %s: %s\n"),
zfs_get_name(dd.clone_zhp),
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
ZFS_CLOSE(dd.clone_zhp);
ZFS_CLOSE(zhp);
return (ret);
}
ZFS_CLOSE(dd.clone_zhp);
}
dd.clone_zhp = NULL;
dd.origin_creation = 0;
dd.snapshot = NULL;
dd.find_in_BE = B_FALSE;
}
if ((ret = zfs_iter_filesystems(zhp, be_demote_callback, NULL)) != 0) {
ZFS_CLOSE(zhp);
return (ret);
}
ZFS_CLOSE(zhp);
return (0);
}
static int
be_demote_find_clone_callback(zfs_handle_t *zhp, void *data)
{
be_demote_data_t *dd = data;
time_t snap_creation;
int zret = 0;
if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) == 0) {
ZFS_CLOSE(zhp);
return (0);
}
dd->snapshot = zfs_get_name(zhp);
snap_creation = (time_t)zfs_prop_get_int(zhp, ZFS_PROP_CREATION);
if (snap_creation >= dd->origin_creation) {
if ((zret = zfs_iter_dependents(zhp, B_FALSE,
be_demote_get_one_clone, dd)) == -1) {
be_print_err(gettext("be_demote_find_clone_callback: "
"failed to iterate dependents of %s\n"),
zfs_get_name(zhp));
ZFS_CLOSE(zhp);
return (1);
} else if (zret == 1) {
dd->origin_creation = snap_creation;
}
}
ZFS_CLOSE(zhp);
return (0);
}
static int
be_demote_get_one_clone(zfs_handle_t *zhp, void *data)
{
be_demote_data_t *dd = data;
char origin[ZFS_MAX_DATASET_NAME_LEN];
char ds_path[ZFS_MAX_DATASET_NAME_LEN];
if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
ZFS_CLOSE(zhp);
return (0);
}
(void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL,
NULL, 0, B_FALSE) != 0) {
be_print_err(gettext("be_demote_get_one_clone: "
"failed to get origin of %s: %s\n"), ds_path,
libzfs_error_description(g_zfs));
ZFS_CLOSE(zhp);
return (0);
}
if (strcmp(origin, dd->snapshot) != 0) {
ZFS_CLOSE(zhp);
return (0);
}
if (dd->find_in_BE) {
if ((zpool_iter(g_zfs, be_check_be_roots_callback, ds_path))
> 0) {
if (dd->clone_zhp != NULL)
ZFS_CLOSE(dd->clone_zhp);
dd->clone_zhp = zhp;
return (1);
}
ZFS_CLOSE(zhp);
return (0);
}
if (dd->clone_zhp != NULL)
ZFS_CLOSE(dd->clone_zhp);
dd->clone_zhp = zhp;
return (1);
}
static int
be_get_snap(char *origin, char **snap)
{
char *cp;
cp = strrchr(origin, '@');
if (cp != NULL) {
if (cp[1] != '\0') {
cp[0] = '\0';
*snap = cp+1;
} else {
return (1);
}
} else {
return (1);
}
return (BE_SUCCESS);
}
static boolean_t
be_create_container_ds(char *zpool)
{
nvlist_t *props = NULL;
char be_container_ds[MAXPATHLEN];
if (be_make_container_ds(zpool, be_container_ds,
sizeof (be_container_ds)) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container dataset "
"for %s\n"), __func__, zpool);
return (B_FALSE);
}
if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) {
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
be_print_err(gettext("be_create_container_ds: "
"nvlist_alloc failed\n"));
return (B_FALSE);
}
if (nvlist_add_string(props,
zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
ZFS_MOUNTPOINT_LEGACY) != 0) {
be_print_err(gettext("be_create_container_ds: "
"internal error: out of memory\n"));
nvlist_free(props);
return (B_FALSE);
}
if (nvlist_add_string(props,
zfs_prop_to_name(ZFS_PROP_CANMOUNT), "off") != 0) {
be_print_err(gettext("be_create_container_ds: "
"internal error: out of memory\n"));
nvlist_free(props);
return (B_FALSE);
}
if (zfs_create(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM,
props) != 0) {
be_print_err(gettext("be_create_container_ds: "
"failed to create container dataset (%s): %s\n"),
be_container_ds, libzfs_error_description(g_zfs));
nvlist_free(props);
return (B_FALSE);
}
nvlist_free(props);
}
return (B_TRUE);
}
static int
be_prep_clone_send_fs(zfs_handle_t *zhp, be_transaction_data_t *bt,
char *clone_ds, int clone_ds_len)
{
zprop_source_t sourcetype;
char source[ZFS_MAX_DATASET_NAME_LEN];
char zhp_name[ZFS_MAX_DATASET_NAME_LEN];
char mountpoint[MAXPATHLEN];
char *child_fs = NULL;
char *zhp_mountpoint = NULL;
int err = 0;
(void) strlcpy(zhp_name, zfs_get_name(zhp), sizeof (zhp_name));
if (strncmp(zhp_name, bt->obe_root_ds, strlen(bt->obe_root_ds))
== 0) {
child_fs = zhp_name + strlen(bt->obe_root_ds);
if (child_fs == NULL)
child_fs = "";
} else {
return (BE_ERR_INVAL);
}
(void) snprintf(clone_ds, clone_ds_len, "%s%s", bt->nbe_root_ds,
child_fs);
if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
sizeof (mountpoint), &sourcetype, source, sizeof (source),
B_FALSE) != 0) {
be_print_err(gettext("be_prep_clone_send_fs: "
"failed to get mountpoint for (%s): %s\n"),
zhp_name, libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if (strcmp(mountpoint, "") == 0) {
(void) snprintf(mountpoint, sizeof (mountpoint), "/");
}
if (sourcetype & ZPROP_SRC_LOCAL) {
zhp_mountpoint = mountpoint;
if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 &&
bt->obe_altroot != NULL && strcmp(bt->obe_altroot,
"/") != 0 && zfs_is_mounted(zhp, NULL)) {
int altroot_len = strlen(bt->obe_altroot);
if (strncmp(bt->obe_altroot, mountpoint, altroot_len)
== 0) {
if (mountpoint[altroot_len] == '/')
zhp_mountpoint = mountpoint +
altroot_len;
else if (mountpoint[altroot_len] == '\0')
(void) snprintf(mountpoint,
sizeof (mountpoint), "/");
}
}
if (nvlist_add_string(bt->nbe_zfs_props,
zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
zhp_mountpoint) != 0) {
be_print_err(gettext("be_prep_clone_send_fs: "
"internal error: out of memory\n"));
return (BE_ERR_NOMEM);
}
} else {
err = nvlist_remove_all(bt->nbe_zfs_props,
zfs_prop_to_name(ZFS_PROP_MOUNTPOINT));
if (err != 0 && err != ENOENT) {
be_print_err(gettext("be_prep_clone_send_fs: "
"failed to remove mountpoint from "
"nvlist\n"));
return (BE_ERR_INVAL);
}
}
if (nvlist_add_string(bt->nbe_zfs_props,
zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto") != 0) {
be_print_err(gettext("be_prep_clone_send_fs: "
"internal error: out of memory\n"));
return (BE_ERR_NOMEM);
}
return (BE_SUCCESS);
}
static char *
be_get_zone_be_name(char *root_ds, char *container_ds)
{
return (root_ds + (strlen(container_ds) + 1));
}
static int
be_zone_root_exists_callback(zfs_handle_t *zhp, void *data)
{
ZFS_CLOSE(zhp);
return (1);
}