4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
25 * Copyright (c) 2014, 2015 by Delphix. All rights reserved.
26 * Copyright (c) 2016 Martin Matuska. All rights reserved.
38 #include <libnvpair.h>
43 #include <sys/mnttab.h>
44 #include <sys/mount.h>
46 #include <sys/types.h>
51 #include <libbe_priv.h>
53 /* Library wide variables */
54 libzfs_handle_t
*g_zfs
= NULL
;
56 /* Private function prototypes */
57 static int _be_destroy(const char *, be_destroy_data_t
*);
58 static int be_destroy_zones(char *, char *, be_destroy_data_t
*);
59 static int be_destroy_zone_roots(char *, be_destroy_data_t
*);
60 static int be_destroy_zone_roots_callback(zfs_handle_t
*, void *);
61 static int be_copy_zones(char *, char *, char *);
62 static int be_clone_fs_callback(zfs_handle_t
*, void *);
63 static int be_destroy_callback(zfs_handle_t
*, void *);
64 static int be_send_fs_callback(zfs_handle_t
*, void *);
65 static int be_demote_callback(zfs_handle_t
*, void *);
66 static int be_demote_find_clone_callback(zfs_handle_t
*, void *);
67 static int be_has_snapshot_callback(zfs_handle_t
*, void *);
68 static int be_demote_get_one_clone(zfs_handle_t
*, void *);
69 static int be_get_snap(char *, char **);
70 static int be_prep_clone_send_fs(zfs_handle_t
*, be_transaction_data_t
*,
72 static boolean_t
be_create_container_ds(char *);
73 static char *be_get_zone_be_name(char *root_ds
, char *container_ds
);
74 static int be_zone_root_exists_callback(zfs_handle_t
*, void *);
76 /* ******************************************************************** */
77 /* Public Functions */
78 /* ******************************************************************** */
82 * Description: Creates the initial datasets for a BE and leaves them
83 * unpopulated. The resultant BE can be mounted but can't
84 * yet be activated or booted.
86 * be_attrs - pointer to nvlist_t of attributes being passed in.
87 * The following attributes are used by this function:
89 * BE_ATTR_NEW_BE_NAME *required
90 * BE_ATTR_NEW_BE_POOL *required
91 * BE_ATTR_ZFS_PROPERTIES *optional
92 * BE_ATTR_FS_NAMES *optional
93 * BE_ATTR_FS_NUM *optional
94 * BE_ATTR_SHARED_FS_NAMES *optional
95 * BE_ATTR_SHARED_FS_NUM *optional
97 * BE_SUCCESS - Success
98 * be_errno_t - Failure
103 be_init(nvlist_t
*be_attrs
)
105 be_transaction_data_t bt
= { 0 };
107 nvlist_t
*zfs_props
= NULL
;
108 char nbe_root_ds
[MAXPATHLEN
];
109 char child_fs
[MAXPATHLEN
];
110 char **fs_names
= NULL
;
111 char **shared_fs_names
= NULL
;
113 uint16_t shared_fs_num
= 0;
116 int zret
= 0, ret
= BE_SUCCESS
;
118 /* Initialize libzfs handle */
120 return (BE_ERR_INIT
);
122 /* Get new BE name */
123 if (nvlist_lookup_string(be_attrs
, BE_ATTR_NEW_BE_NAME
, &bt
.nbe_name
)
125 be_print_err(gettext("be_init: failed to lookup "
126 "BE_ATTR_NEW_BE_NAME attribute\n"));
127 return (BE_ERR_INVAL
);
130 /* Validate new BE name */
131 if (!be_valid_be_name(bt
.nbe_name
)) {
132 be_print_err(gettext("be_init: invalid BE name %s\n"),
134 return (BE_ERR_INVAL
);
138 if (nvlist_lookup_string(be_attrs
, BE_ATTR_NEW_BE_POOL
, &bt
.nbe_zpool
)
140 be_print_err(gettext("be_init: failed to lookup "
141 "BE_ATTR_NEW_BE_POOL attribute\n"));
142 return (BE_ERR_INVAL
);
145 /* Get file system attributes */
147 if (nvlist_lookup_pairs(be_attrs
, 0,
148 BE_ATTR_FS_NUM
, DATA_TYPE_UINT16
, &fs_num
,
149 BE_ATTR_FS_NAMES
, DATA_TYPE_STRING_ARRAY
, &fs_names
, &nelem
,
151 be_print_err(gettext("be_init: failed to lookup fs "
153 return (BE_ERR_INVAL
);
155 if (nelem
!= fs_num
) {
156 be_print_err(gettext("be_init: size of FS_NAMES array (%d) "
157 "does not match FS_NUM (%d)\n"), nelem
, fs_num
);
158 return (BE_ERR_INVAL
);
161 /* Get shared file system attributes */
163 if (nvlist_lookup_pairs(be_attrs
, NV_FLAG_NOENTOK
,
164 BE_ATTR_SHARED_FS_NUM
, DATA_TYPE_UINT16
, &shared_fs_num
,
165 BE_ATTR_SHARED_FS_NAMES
, DATA_TYPE_STRING_ARRAY
, &shared_fs_names
,
166 &nelem
, NULL
) != 0) {
167 be_print_err(gettext("be_init: failed to lookup "
168 "shared fs attributes\n"));
169 return (BE_ERR_INVAL
);
171 if (nelem
!= shared_fs_num
) {
172 be_print_err(gettext("be_init: size of SHARED_FS_NAMES "
173 "array does not match SHARED_FS_NUM\n"));
174 return (BE_ERR_INVAL
);
177 /* Verify that nbe_zpool exists */
178 if ((zlp
= zpool_open(g_zfs
, bt
.nbe_zpool
)) == NULL
) {
179 be_print_err(gettext("be_init: failed to "
180 "find existing zpool (%s): %s\n"), bt
.nbe_zpool
,
181 libzfs_error_description(g_zfs
));
182 return (zfs_err_to_be_err(g_zfs
));
187 * Verify BE container dataset in nbe_zpool exists.
190 if (!be_create_container_ds(bt
.nbe_zpool
))
191 return (BE_ERR_CREATDS
);
194 * Verify that nbe_name doesn't already exist in some pool.
196 if ((zret
= zpool_iter(g_zfs
, be_exists_callback
, bt
.nbe_name
)) > 0) {
197 be_print_err(gettext("be_init: BE (%s) already exists\n"),
199 return (BE_ERR_BE_EXISTS
);
200 } else if (zret
< 0) {
201 be_print_err(gettext("be_init: zpool_iter failed: %s\n"),
202 libzfs_error_description(g_zfs
));
203 return (zfs_err_to_be_err(g_zfs
));
206 /* Generate string for BE's root dataset */
207 be_make_root_ds(bt
.nbe_zpool
, bt
.nbe_name
, nbe_root_ds
,
208 sizeof (nbe_root_ds
));
211 * Create property list for new BE root dataset. If some
212 * zfs properties were already provided by the caller, dup
213 * that list. Otherwise initialize a new property list.
215 if (nvlist_lookup_pairs(be_attrs
, NV_FLAG_NOENTOK
,
216 BE_ATTR_ZFS_PROPERTIES
, DATA_TYPE_NVLIST
, &zfs_props
, NULL
)
218 be_print_err(gettext("be_init: failed to lookup "
219 "BE_ATTR_ZFS_PROPERTIES attribute\n"));
220 return (BE_ERR_INVAL
);
222 if (zfs_props
!= NULL
) {
223 /* Make sure its a unique nvlist */
224 if (!(zfs_props
->nvl_nvflag
& NV_UNIQUE_NAME
) &&
225 !(zfs_props
->nvl_nvflag
& NV_UNIQUE_NAME_TYPE
)) {
226 be_print_err(gettext("be_init: ZFS property list "
228 return (BE_ERR_INVAL
);
232 if (nvlist_dup(zfs_props
, &bt
.nbe_zfs_props
, 0) != 0) {
233 be_print_err(gettext("be_init: failed to dup ZFS "
235 return (BE_ERR_NOMEM
);
238 /* Initialize new nvlist */
239 if (nvlist_alloc(&bt
.nbe_zfs_props
, NV_UNIQUE_NAME
, 0) != 0) {
240 be_print_err(gettext("be_init: internal "
241 "error: out of memory\n"));
242 return (BE_ERR_NOMEM
);
246 /* Set the mountpoint property for the root dataset */
247 if (nvlist_add_string(bt
.nbe_zfs_props
,
248 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT
), "/") != 0) {
249 be_print_err(gettext("be_init: internal error "
255 /* Set the 'canmount' property */
256 if (nvlist_add_string(bt
.nbe_zfs_props
,
257 zfs_prop_to_name(ZFS_PROP_CANMOUNT
), "noauto") != 0) {
258 be_print_err(gettext("be_init: internal error "
264 /* Create BE root dataset for the new BE */
265 if (zfs_create(g_zfs
, nbe_root_ds
, ZFS_TYPE_FILESYSTEM
,
266 bt
.nbe_zfs_props
) != 0) {
267 be_print_err(gettext("be_init: failed to "
268 "create BE root dataset (%s): %s\n"), nbe_root_ds
,
269 libzfs_error_description(g_zfs
));
270 ret
= zfs_err_to_be_err(g_zfs
);
274 /* Set UUID for new BE */
275 if ((ret
= be_set_uuid(nbe_root_ds
)) != BE_SUCCESS
) {
276 be_print_err(gettext("be_init: failed to "
277 "set uuid for new BE\n"));
281 * Clear the mountpoint property so that the non-shared
282 * file systems created below inherit their mountpoints.
284 (void) nvlist_remove(bt
.nbe_zfs_props
,
285 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT
), DATA_TYPE_STRING
);
287 /* Create the new BE's non-shared file systems */
288 for (i
= 0; i
< fs_num
&& fs_names
[i
]; i
++) {
290 * If fs == "/", skip it;
291 * we already created the root dataset
293 if (strcmp(fs_names
[i
], "/") == 0)
296 /* Generate string for file system */
297 (void) snprintf(child_fs
, sizeof (child_fs
), "%s%s",
298 nbe_root_ds
, fs_names
[i
]);
300 /* Create file system */
301 if (zfs_create(g_zfs
, child_fs
, ZFS_TYPE_FILESYSTEM
,
302 bt
.nbe_zfs_props
) != 0) {
303 be_print_err(gettext("be_init: failed to create "
304 "BE's child dataset (%s): %s\n"), child_fs
,
305 libzfs_error_description(g_zfs
));
306 ret
= zfs_err_to_be_err(g_zfs
);
311 /* Create the new BE's shared file systems */
312 if (shared_fs_num
> 0) {
313 nvlist_t
*props
= NULL
;
315 if (nvlist_alloc(&props
, NV_UNIQUE_NAME
, 0) != 0) {
316 be_print_err(gettext("be_init: nvlist_alloc failed\n"));
321 for (i
= 0; i
< shared_fs_num
; i
++) {
322 /* Generate string for shared file system */
323 (void) snprintf(child_fs
, sizeof (child_fs
), "%s%s",
324 bt
.nbe_zpool
, shared_fs_names
[i
]);
326 if (nvlist_add_string(props
,
327 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT
),
328 shared_fs_names
[i
]) != 0) {
329 be_print_err(gettext("be_init: "
330 "internal error: out of memory\n"));
336 /* Create file system if it doesn't already exist */
337 if (zfs_dataset_exists(g_zfs
, child_fs
,
338 ZFS_TYPE_FILESYSTEM
)) {
341 if (zfs_create(g_zfs
, child_fs
, ZFS_TYPE_FILESYSTEM
,
343 be_print_err(gettext("be_init: failed to "
344 "create BE's shared dataset (%s): %s\n"),
345 child_fs
, libzfs_error_description(g_zfs
));
346 ret
= zfs_err_to_be_err(g_zfs
);
356 nvlist_free(bt
.nbe_zfs_props
);
364 * Function: be_destroy
365 * Description: Destroy a BE and all of its children datasets, snapshots and
366 * zones that belong to the parent BE.
368 * be_attrs - pointer to nvlist_t of attributes being passed in.
369 * The following attributes are used by this function:
371 * BE_ATTR_ORIG_BE_NAME *required
372 * BE_ATTR_DESTROY_FLAGS *optional
374 * BE_SUCCESS - Success
375 * be_errno_t - Failure
380 be_destroy(nvlist_t
*be_attrs
)
382 zfs_handle_t
*zhp
= NULL
;
383 be_transaction_data_t bt
= { 0 };
384 be_transaction_data_t cur_bt
= { 0 };
385 be_destroy_data_t dd
= { 0 };
386 int ret
= BE_SUCCESS
;
388 boolean_t bs_found
= B_FALSE
;
390 char obe_root_ds
[MAXPATHLEN
];
393 /* Initialize libzfs handle */
395 return (BE_ERR_INIT
);
397 /* Get name of BE to delete */
398 if (nvlist_lookup_string(be_attrs
, BE_ATTR_ORIG_BE_NAME
, &bt
.obe_name
)
400 be_print_err(gettext("be_destroy: failed to lookup "
401 "BE_ATTR_ORIG_BE_NAME attribute\n"));
402 return (BE_ERR_INVAL
);
406 * Validate BE name. If valid, then check that the original BE is not
407 * the active BE. If it is the 'active' BE then return an error code
408 * since we can't destroy the active BE.
410 if (!be_valid_be_name(bt
.obe_name
)) {
411 be_print_err(gettext("be_destroy: invalid BE name %s\n"),
413 return (BE_ERR_INVAL
);
414 } else if (bt
.obe_name
!= NULL
) {
415 if ((ret
= be_find_current_be(&cur_bt
)) != BE_SUCCESS
) {
418 if (strcmp(cur_bt
.obe_name
, bt
.obe_name
) == 0) {
419 return (BE_ERR_DESTROY_CURR_BE
);
423 /* Get destroy flags if provided */
424 if (nvlist_lookup_pairs(be_attrs
, NV_FLAG_NOENTOK
,
425 BE_ATTR_DESTROY_FLAGS
, DATA_TYPE_UINT16
, &flags
, NULL
)
427 be_print_err(gettext("be_destroy: failed to lookup "
428 "BE_ATTR_DESTROY_FLAGS attribute\n"));
429 return (BE_ERR_INVAL
);
432 dd
.destroy_snaps
= flags
& BE_DESTROY_FLAG_SNAPSHOTS
;
433 dd
.force_unmount
= flags
& BE_DESTROY_FLAG_FORCE_UNMOUNT
;
435 /* Find which zpool obe_name lives in */
436 if ((zret
= zpool_iter(g_zfs
, be_find_zpool_callback
, &bt
)) == 0) {
437 be_print_err(gettext("be_destroy: failed to find zpool "
438 "for BE (%s)\n"), bt
.obe_name
);
439 return (BE_ERR_BE_NOENT
);
440 } else if (zret
< 0) {
441 be_print_err(gettext("be_destroy: zpool_iter failed: %s\n"),
442 libzfs_error_description(g_zfs
));
443 return (zfs_err_to_be_err(g_zfs
));
446 /* Generate string for obe_name's root dataset */
447 be_make_root_ds(bt
.obe_zpool
, bt
.obe_name
, obe_root_ds
,
448 sizeof (obe_root_ds
));
449 bt
.obe_root_ds
= obe_root_ds
;
451 if (getzoneid() != GLOBAL_ZONEID
) {
452 if (!be_zone_compare_uuids(bt
.obe_root_ds
)) {
453 if (be_is_active_on_boot(bt
.obe_name
)) {
454 be_print_err(gettext("be_destroy: destroying "
455 "active zone root dataset from non-active "
456 "global BE is not supported\n"));
457 return (BE_ERR_NOTSUP
);
463 * Detect if the BE to destroy has the 'active on boot' property set.
464 * If so, set the 'active on boot' property on the the 'active' BE.
466 if (be_is_active_on_boot(bt
.obe_name
)) {
467 if ((ret
= be_activate_current_be()) != BE_SUCCESS
) {
468 be_print_err(gettext("be_destroy: failed to "
469 "make the current BE 'active on boot'\n"));
474 /* Get handle to BE's root dataset */
475 if ((zhp
= zfs_open(g_zfs
, bt
.obe_root_ds
, ZFS_TYPE_FILESYSTEM
)) ==
477 be_print_err(gettext("be_destroy: failed to "
478 "open BE root dataset (%s): %s\n"), bt
.obe_root_ds
,
479 libzfs_error_description(g_zfs
));
480 return (zfs_err_to_be_err(g_zfs
));
484 * Check if BE has snapshots and BE_DESTROY_FLAG_SNAPSHOTS
487 (void) zfs_iter_snapshots(zhp
, B_FALSE
, be_has_snapshot_callback
,
489 if (!dd
.destroy_snaps
&& bs_found
) {
491 return (BE_ERR_SS_EXISTS
);
494 /* Get the UUID of the global BE */
495 if (getzoneid() == GLOBAL_ZONEID
) {
496 if (be_get_uuid(zfs_get_name(zhp
),
497 &dd
.gz_be_uuid
) != BE_SUCCESS
) {
498 be_print_err(gettext("be_destroy: BE has no "
499 "UUID (%s)\n"), zfs_get_name(zhp
));
504 * If the global BE is mounted, make sure we've been given the
505 * flag to forcibly unmount it.
507 if (zfs_is_mounted(zhp
, &mp
)) {
508 if (!(dd
.force_unmount
)) {
509 be_print_err(gettext("be_destroy: "
510 "%s is currently mounted at %s, cannot destroy\n"),
511 bt
.obe_name
, mp
!= NULL
? mp
: "<unknown>");
515 return (BE_ERR_MOUNTED
);
521 * Destroy the non-global zone BE's if we are in the global zone
522 * and there is a UUID associated with the global zone BE
524 if (getzoneid() == GLOBAL_ZONEID
&& !uuid_is_null(dd
.gz_be_uuid
)) {
525 if ((ret
= be_destroy_zones(bt
.obe_name
, bt
.obe_root_ds
, &dd
))
527 be_print_err(gettext("be_destroy: failed to "
528 "destroy one or more zones for BE %s\n"),
534 /* Unmount the BE if it was mounted */
535 if (zfs_is_mounted(zhp
, NULL
)) {
536 if ((ret
= _be_unmount(bt
.obe_name
, BE_UNMOUNT_FLAG_FORCE
))
538 be_print_err(gettext("be_destroy: "
539 "failed to unmount %s\n"), bt
.obe_name
);
546 /* Destroy this BE */
547 if ((ret
= _be_destroy((const char *)bt
.obe_root_ds
, &dd
))
552 /* Remove BE's entry from the boot menu */
553 if (getzoneid() == GLOBAL_ZONEID
) {
554 if ((ret
= be_remove_menu(bt
.obe_name
, bt
.obe_zpool
, NULL
))
556 be_print_err(gettext("be_destroy: failed to "
557 "remove BE %s from the boot menu\n"),
571 * Description: This function makes a copy of an existing BE. If the original
572 * BE and the new BE are in the same pool, it uses zfs cloning to
573 * create the new BE, otherwise it does a physical copy.
574 * If the original BE name isn't provided, it uses the currently
575 * booted BE. If the new BE name isn't provided, it creates an
576 * auto named BE and returns that name to the caller.
578 * be_attrs - pointer to nvlist_t of attributes being passed in.
579 * The following attributes are used by this function:
581 * BE_ATTR_ORIG_BE_NAME *optional
582 * BE_ATTR_SNAP_NAME *optional
583 * BE_ATTR_NEW_BE_NAME *optional
584 * BE_ATTR_NEW_BE_POOL *optional
585 * BE_ATTR_NEW_BE_DESC *optional
586 * BE_ATTR_ZFS_PROPERTIES *optional
587 * BE_ATTR_POLICY *optional
589 * If the BE_ATTR_NEW_BE_NAME was not passed in, upon
590 * successful BE creation, the following attribute values
591 * will be returned to the caller by setting them in the
592 * be_attrs parameter passed in:
595 * BE_ATTR_NEW_BE_NAME
597 * BE_SUCCESS - Success
598 * be_errno_t - Failure
603 be_copy(nvlist_t
*be_attrs
)
605 be_transaction_data_t bt
= { 0 };
606 be_fs_list_data_t fld
= { 0 };
607 zfs_handle_t
*zhp
= NULL
;
608 zpool_handle_t
*zphp
= NULL
;
609 nvlist_t
*zfs_props
= NULL
;
611 uuid_t parent_uu
= { 0 };
612 char obe_root_ds
[MAXPATHLEN
];
613 char nbe_root_ds
[MAXPATHLEN
];
616 char *obe_name
= NULL
;
617 boolean_t autoname
= B_FALSE
;
618 boolean_t be_created
= B_FALSE
;
621 int ret
= BE_SUCCESS
;
622 struct be_defaults be_defaults
;
624 /* Initialize libzfs handle */
626 return (BE_ERR_INIT
);
628 /* Get original BE name */
629 if (nvlist_lookup_pairs(be_attrs
, NV_FLAG_NOENTOK
,
630 BE_ATTR_ORIG_BE_NAME
, DATA_TYPE_STRING
, &obe_name
, NULL
) != 0) {
631 be_print_err(gettext("be_copy: failed to lookup "
632 "BE_ATTR_ORIG_BE_NAME attribute\n"));
633 return (BE_ERR_INVAL
);
636 if ((ret
= be_find_current_be(&bt
)) != BE_SUCCESS
) {
640 be_get_defaults(&be_defaults
);
642 /* If original BE name not provided, use current BE */
643 if (obe_name
!= NULL
) {
644 bt
.obe_name
= obe_name
;
645 /* Validate original BE name */
646 if (!be_valid_be_name(bt
.obe_name
)) {
647 be_print_err(gettext("be_copy: "
648 "invalid BE name %s\n"), bt
.obe_name
);
649 return (BE_ERR_INVAL
);
653 if (be_defaults
.be_deflt_rpool_container
) {
654 if ((zphp
= zpool_open(g_zfs
, bt
.obe_zpool
)) == NULL
) {
655 be_print_err(gettext("be_get_node_data: failed to "
656 "open rpool (%s): %s\n"), bt
.obe_zpool
,
657 libzfs_error_description(g_zfs
));
658 return (zfs_err_to_be_err(g_zfs
));
660 if (be_find_zpool_callback(zphp
, &bt
) == 0) {
661 return (BE_ERR_BE_NOENT
);
664 /* Find which zpool obe_name lives in */
665 if ((zret
= zpool_iter(g_zfs
, be_find_zpool_callback
, &bt
)) ==
667 be_print_err(gettext("be_copy: failed to "
668 "find zpool for BE (%s)\n"), bt
.obe_name
);
669 return (BE_ERR_BE_NOENT
);
670 } else if (zret
< 0) {
671 be_print_err(gettext("be_copy: "
672 "zpool_iter failed: %s\n"),
673 libzfs_error_description(g_zfs
));
674 return (zfs_err_to_be_err(g_zfs
));
678 /* Get snapshot name of original BE if one was provided */
679 if (nvlist_lookup_pairs(be_attrs
, NV_FLAG_NOENTOK
,
680 BE_ATTR_SNAP_NAME
, DATA_TYPE_STRING
, &bt
.obe_snap_name
, NULL
)
682 be_print_err(gettext("be_copy: failed to lookup "
683 "BE_ATTR_SNAP_NAME attribute\n"));
684 return (BE_ERR_INVAL
);
687 /* Get new BE name */
688 if (nvlist_lookup_pairs(be_attrs
, NV_FLAG_NOENTOK
,
689 BE_ATTR_NEW_BE_NAME
, DATA_TYPE_STRING
, &bt
.nbe_name
, NULL
)
691 be_print_err(gettext("be_copy: failed to lookup "
692 "BE_ATTR_NEW_BE_NAME attribute\n"));
693 return (BE_ERR_INVAL
);
696 /* Get zpool name to create new BE in */
697 if (nvlist_lookup_pairs(be_attrs
, NV_FLAG_NOENTOK
,
698 BE_ATTR_NEW_BE_POOL
, DATA_TYPE_STRING
, &bt
.nbe_zpool
, NULL
) != 0) {
699 be_print_err(gettext("be_copy: failed to lookup "
700 "BE_ATTR_NEW_BE_POOL attribute\n"));
701 return (BE_ERR_INVAL
);
704 /* Get new BE's description if one was provided */
705 if (nvlist_lookup_pairs(be_attrs
, NV_FLAG_NOENTOK
,
706 BE_ATTR_NEW_BE_DESC
, DATA_TYPE_STRING
, &bt
.nbe_desc
, NULL
) != 0) {
707 be_print_err(gettext("be_copy: failed to lookup "
708 "BE_ATTR_NEW_BE_DESC attribute\n"));
709 return (BE_ERR_INVAL
);
712 /* Get BE policy to create this snapshot under */
713 if (nvlist_lookup_pairs(be_attrs
, NV_FLAG_NOENTOK
,
714 BE_ATTR_POLICY
, DATA_TYPE_STRING
, &bt
.policy
, NULL
) != 0) {
715 be_print_err(gettext("be_copy: failed to lookup "
716 "BE_ATTR_POLICY attribute\n"));
717 return (BE_ERR_INVAL
);
721 * Create property list for new BE root dataset. If some
722 * zfs properties were already provided by the caller, dup
723 * that list. Otherwise initialize a new property list.
725 if (nvlist_lookup_pairs(be_attrs
, NV_FLAG_NOENTOK
,
726 BE_ATTR_ZFS_PROPERTIES
, DATA_TYPE_NVLIST
, &zfs_props
, NULL
)
728 be_print_err(gettext("be_copy: failed to lookup "
729 "BE_ATTR_ZFS_PROPERTIES attribute\n"));
730 return (BE_ERR_INVAL
);
732 if (zfs_props
!= NULL
) {
733 /* Make sure its a unique nvlist */
734 if (!(zfs_props
->nvl_nvflag
& NV_UNIQUE_NAME
) &&
735 !(zfs_props
->nvl_nvflag
& NV_UNIQUE_NAME_TYPE
)) {
736 be_print_err(gettext("be_copy: ZFS property list "
738 return (BE_ERR_INVAL
);
742 if (nvlist_dup(zfs_props
, &bt
.nbe_zfs_props
, 0) != 0) {
743 be_print_err(gettext("be_copy: "
744 "failed to dup ZFS property list\n"));
745 return (BE_ERR_NOMEM
);
748 /* Initialize new nvlist */
749 if (nvlist_alloc(&bt
.nbe_zfs_props
, NV_UNIQUE_NAME
, 0) != 0) {
750 be_print_err(gettext("be_copy: internal "
751 "error: out of memory\n"));
752 return (BE_ERR_NOMEM
);
757 * If new BE name provided, validate the BE name and then verify
758 * that new BE name doesn't already exist in some pool.
761 /* Validate original BE name */
762 if (!be_valid_be_name(bt
.nbe_name
)) {
763 be_print_err(gettext("be_copy: "
764 "invalid BE name %s\n"), bt
.nbe_name
);
769 /* Verify it doesn't already exist */
770 if (getzoneid() == GLOBAL_ZONEID
) {
771 if ((zret
= zpool_iter(g_zfs
, be_exists_callback
,
773 be_print_err(gettext("be_copy: BE (%s) already "
774 "exists\n"), bt
.nbe_name
);
775 ret
= BE_ERR_BE_EXISTS
;
777 } else if (zret
< 0) {
778 be_print_err(gettext("be_copy: zpool_iter "
780 libzfs_error_description(g_zfs
));
781 ret
= zfs_err_to_be_err(g_zfs
);
785 be_make_root_ds(bt
.nbe_zpool
, bt
.nbe_name
, nbe_root_ds
,
786 sizeof (nbe_root_ds
));
787 if (zfs_dataset_exists(g_zfs
, nbe_root_ds
,
788 ZFS_TYPE_FILESYSTEM
)) {
789 be_print_err(gettext("be_copy: BE (%s) already "
790 "exists\n"), bt
.nbe_name
);
791 ret
= BE_ERR_BE_EXISTS
;
797 * If an auto named BE is desired, it must be in the same
798 * pool is the original BE.
800 if (bt
.nbe_zpool
!= NULL
) {
801 be_print_err(gettext("be_copy: cannot specify pool "
802 "name when creating an auto named BE\n"));
808 * Generate auto named BE
810 if ((bt
.nbe_name
= be_auto_be_name(bt
.obe_name
))
812 be_print_err(gettext("be_copy: "
813 "failed to generate auto BE name\n"));
814 ret
= BE_ERR_AUTONAME
;
822 * If zpool name to create new BE in is not provided,
823 * create new BE in original BE's pool.
825 if (bt
.nbe_zpool
== NULL
) {
826 bt
.nbe_zpool
= bt
.obe_zpool
;
829 /* Get root dataset names for obe_name and nbe_name */
830 be_make_root_ds(bt
.obe_zpool
, bt
.obe_name
, obe_root_ds
,
831 sizeof (obe_root_ds
));
832 be_make_root_ds(bt
.nbe_zpool
, bt
.nbe_name
, nbe_root_ds
,
833 sizeof (nbe_root_ds
));
835 bt
.obe_root_ds
= obe_root_ds
;
836 bt
.nbe_root_ds
= nbe_root_ds
;
839 * If an existing snapshot name has been provided to create from,
840 * verify that it exists for the original BE's root dataset.
842 if (bt
.obe_snap_name
!= NULL
) {
844 /* Generate dataset name for snapshot to use. */
845 (void) snprintf(ss
, sizeof (ss
), "%s@%s", bt
.obe_root_ds
,
848 /* Verify snapshot exists */
849 if (!zfs_dataset_exists(g_zfs
, ss
, ZFS_TYPE_SNAPSHOT
)) {
850 be_print_err(gettext("be_copy: "
851 "snapshot does not exist (%s): %s\n"), ss
,
852 libzfs_error_description(g_zfs
));
853 ret
= BE_ERR_SS_NOENT
;
858 * Else snapshot name was not provided, generate an
859 * auto named snapshot to use as its origin.
861 if ((ret
= _be_create_snapshot(bt
.obe_name
,
862 &bt
.obe_snap_name
, bt
.policy
)) != BE_SUCCESS
) {
863 be_print_err(gettext("be_copy: "
864 "failed to create auto named snapshot\n"));
868 if (nvlist_add_string(be_attrs
, BE_ATTR_SNAP_NAME
,
869 bt
.obe_snap_name
) != 0) {
870 be_print_err(gettext("be_copy: "
871 "failed to add snap name to be_attrs\n"));
877 /* Get handle to original BE's root dataset. */
878 if ((zhp
= zfs_open(g_zfs
, bt
.obe_root_ds
, ZFS_TYPE_FILESYSTEM
))
880 be_print_err(gettext("be_copy: failed to "
881 "open BE root dataset (%s): %s\n"), bt
.obe_root_ds
,
882 libzfs_error_description(g_zfs
));
883 ret
= zfs_err_to_be_err(g_zfs
);
887 /* If original BE is currently mounted, record its altroot. */
888 if (zfs_is_mounted(zhp
, &bt
.obe_altroot
) && bt
.obe_altroot
== NULL
) {
889 be_print_err(gettext("be_copy: failed to "
890 "get altroot of mounted BE %s: %s\n"),
891 bt
.obe_name
, libzfs_error_description(g_zfs
));
892 ret
= zfs_err_to_be_err(g_zfs
);
896 if (strcmp(bt
.obe_zpool
, bt
.nbe_zpool
) == 0) {
901 * Iterate through original BE's datasets and clone
902 * them to create new BE. This call will end up closing
903 * the zfs handle passed in whether it succeeds for fails.
905 if ((ret
= be_clone_fs_callback(zhp
, &bt
)) != 0) {
907 /* Creating clone BE failed */
908 if (!autoname
|| ret
!= BE_ERR_BE_EXISTS
) {
909 be_print_err(gettext("be_copy: "
910 "failed to clone new BE (%s) from "
912 bt
.nbe_name
, bt
.obe_name
);
918 * We failed to create the new BE because a BE with
919 * the auto-name we generated above has since come
920 * into existence. Regenerate a new auto-name
923 for (i
= 1; i
< BE_AUTO_NAME_MAX_TRY
; i
++) {
925 /* Sleep 1 before retrying */
928 /* Generate new auto BE name */
930 if ((bt
.nbe_name
= be_auto_be_name(bt
.obe_name
))
932 be_print_err(gettext("be_copy: "
933 "failed to generate auto "
935 ret
= BE_ERR_AUTONAME
;
940 * Regenerate string for new BE's
943 be_make_root_ds(bt
.nbe_zpool
, bt
.nbe_name
,
944 nbe_root_ds
, sizeof (nbe_root_ds
));
945 bt
.nbe_root_ds
= nbe_root_ds
;
948 * Get handle to original BE's root dataset.
950 if ((zhp
= zfs_open(g_zfs
, bt
.obe_root_ds
,
951 ZFS_TYPE_FILESYSTEM
)) == NULL
) {
952 be_print_err(gettext("be_copy: "
953 "failed to open BE root dataset "
954 "(%s): %s\n"), bt
.obe_root_ds
,
955 libzfs_error_description(g_zfs
));
956 ret
= zfs_err_to_be_err(g_zfs
);
961 * Try to clone the BE again. This
962 * call will end up closing the zfs
963 * handle passed in whether it
966 ret
= be_clone_fs_callback(zhp
, &bt
);
970 } else if (ret
!= BE_ERR_BE_EXISTS
) {
971 be_print_err(gettext("be_copy: "
972 "failed to clone new BE "
973 "(%s) from orig BE (%s)\n"),
974 bt
.nbe_name
, bt
.obe_name
);
981 * If we've exhausted the maximum number of
982 * tries, free the auto BE name and return
985 if (i
== BE_AUTO_NAME_MAX_TRY
) {
986 be_print_err(gettext("be_copy: failed "
987 "to create unique auto BE name\n"));
990 ret
= BE_ERR_AUTONAME
;
998 /* Do copy (i.e. send BE datasets via zfs_send/recv) */
1001 * Verify BE container dataset in nbe_zpool exists.
1002 * If not, create it.
1004 if (!be_create_container_ds(bt
.nbe_zpool
)) {
1005 ret
= BE_ERR_CREATDS
;
1010 * Iterate through original BE's datasets and send
1011 * them to the other pool. This call will end up closing
1012 * the zfs handle passed in whether it succeeds or fails.
1014 if ((ret
= be_send_fs_callback(zhp
, &bt
)) != 0) {
1015 be_print_err(gettext("be_copy: failed to "
1016 "send BE (%s) to pool (%s)\n"), bt
.obe_name
,
1026 * Set flag to note that the dataset(s) for the new BE have been
1027 * successfully created so that if a failure happens from this point
1028 * on, we know to cleanup these datasets.
1030 be_created
= B_TRUE
;
1033 * Validate that the new BE is mountable.
1034 * Do not attempt to mount non-global zone datasets
1035 * since they are not cloned yet.
1037 if ((ret
= _be_mount(bt
.nbe_name
, &new_mp
, BE_MOUNT_FLAG_NO_ZONES
))
1039 be_print_err(gettext("be_copy: failed to "
1040 "mount newly created BE\n"));
1041 (void) _be_unmount(bt
.nbe_name
, 0);
1045 /* Set UUID for new BE */
1046 if (getzoneid() == GLOBAL_ZONEID
) {
1047 if (be_set_uuid(bt
.nbe_root_ds
) != BE_SUCCESS
) {
1048 be_print_err(gettext("be_copy: failed to "
1049 "set uuid for new BE\n"));
1052 if ((ret
= be_zone_get_parent_uuid(bt
.obe_root_ds
,
1053 &parent_uu
)) != BE_SUCCESS
) {
1054 be_print_err(gettext("be_copy: failed to get "
1055 "parentbe uuid from orig BE\n"));
1056 ret
= BE_ERR_ZONE_NO_PARENTBE
;
1058 } else if ((ret
= be_zone_set_parent_uuid(bt
.nbe_root_ds
,
1059 parent_uu
)) != BE_SUCCESS
) {
1060 be_print_err(gettext("be_copy: failed to set "
1061 "parentbe uuid for newly created BE\n"));
1067 * Process zones outside of the private BE namespace.
1068 * This has to be done here because we need the uuid set in the
1069 * root dataset of the new BE. The uuid is use to set the parentbe
1070 * property for the new zones datasets.
1072 if (getzoneid() == GLOBAL_ZONEID
&&
1073 be_get_uuid(bt
.obe_root_ds
, &uu
) == BE_SUCCESS
) {
1074 if ((ret
= be_copy_zones(bt
.obe_name
, bt
.obe_root_ds
,
1075 bt
.nbe_root_ds
)) != BE_SUCCESS
) {
1076 be_print_err(gettext("be_copy: failed to process "
1083 * Generate a list of file systems from the original BE that are
1084 * legacy mounted. We use this list to determine which entries in
1085 * vfstab we need to update for the new BE we've just created.
1087 if ((ret
= be_get_legacy_fs(bt
.obe_name
, bt
.obe_root_ds
, NULL
, NULL
,
1088 &fld
)) != BE_SUCCESS
) {
1089 be_print_err(gettext("be_copy: failed to "
1090 "get legacy mounted file system list for %s\n"),
1096 * Update new BE's vfstab.
1098 if ((ret
= be_update_vfstab(bt
.nbe_name
, bt
.obe_zpool
, bt
.nbe_zpool
,
1099 &fld
, new_mp
)) != BE_SUCCESS
) {
1100 be_print_err(gettext("be_copy: failed to "
1101 "update new BE's vfstab (%s)\n"), bt
.nbe_name
);
1105 /* Unmount the new BE */
1106 if ((ret
= _be_unmount(bt
.nbe_name
, 0)) != BE_SUCCESS
) {
1107 be_print_err(gettext("be_copy: failed to "
1108 "unmount newly created BE\n"));
1113 * Add boot menu entry for newly created clone
1115 if (getzoneid() == GLOBAL_ZONEID
&&
1116 (ret
= be_append_menu(bt
.nbe_name
, bt
.nbe_zpool
,
1117 NULL
, bt
.obe_root_ds
, bt
.nbe_desc
)) != BE_SUCCESS
) {
1118 be_print_err(gettext("be_copy: failed to "
1119 "add BE (%s) to boot menu\n"), bt
.nbe_name
);
1124 * If we succeeded in creating an auto named BE, set its policy
1125 * type and return the auto generated name to the caller by storing
1126 * it in the nvlist passed in by the caller.
1129 /* Get handle to new BE's root dataset. */
1130 if ((zhp
= zfs_open(g_zfs
, bt
.nbe_root_ds
,
1131 ZFS_TYPE_FILESYSTEM
)) == NULL
) {
1132 be_print_err(gettext("be_copy: failed to "
1133 "open BE root dataset (%s): %s\n"), bt
.nbe_root_ds
,
1134 libzfs_error_description(g_zfs
));
1135 ret
= zfs_err_to_be_err(g_zfs
);
1140 * Set the policy type property into the new BE's root dataset
1142 if (bt
.policy
== NULL
) {
1143 /* If no policy type provided, use default type */
1144 bt
.policy
= be_default_policy();
1147 if (zfs_prop_set(zhp
, BE_POLICY_PROPERTY
, bt
.policy
) != 0) {
1148 be_print_err(gettext("be_copy: failed to "
1149 "set BE policy for %s: %s\n"), bt
.nbe_name
,
1150 libzfs_error_description(g_zfs
));
1151 ret
= zfs_err_to_be_err(g_zfs
);
1156 * Return the auto generated name to the caller
1159 if (nvlist_add_string(be_attrs
, BE_ATTR_NEW_BE_NAME
,
1160 bt
.nbe_name
) != 0) {
1161 be_print_err(gettext("be_copy: failed to "
1162 "add snap name to be_attrs\n"));
1169 be_free_fs_list(&fld
);
1171 nvlist_free(bt
.nbe_zfs_props
);
1173 free(bt
.obe_altroot
);
1177 * If a failure occurred and we already created the datasets for
1178 * the new boot environment, destroy them.
1180 if (ret
!= BE_SUCCESS
&& be_created
) {
1181 be_destroy_data_t cdd
= { 0 };
1183 cdd
.force_unmount
= B_TRUE
;
1185 be_print_err(gettext("be_copy: "
1186 "destroying partially created boot environment\n"));
1188 if (getzoneid() == GLOBAL_ZONEID
&& be_get_uuid(bt
.nbe_root_ds
,
1189 &cdd
.gz_be_uuid
) == 0)
1190 (void) be_destroy_zones(bt
.nbe_name
, bt
.nbe_root_ds
,
1193 (void) _be_destroy(bt
.nbe_root_ds
, &cdd
);
1201 /* ******************************************************************** */
1202 /* Semi-Private Functions */
1203 /* ******************************************************************** */
1206 * Function: be_find_zpool_callback
1207 * Description: Callback function used to find the pool that a BE lives in.
1209 * zlp - zpool_handle_t pointer for the current pool being
1211 * data - be_transaction_data_t pointer providing information
1212 * about the BE that's being searched for.
1213 * This function uses the obe_name member of this
1214 * parameter to use as the BE name to search for.
1215 * Upon successfully locating the BE, it populates
1216 * obe_zpool with the pool name that the BE is found in.
1218 * 1 - BE exists in this pool.
1219 * 0 - BE does not exist in this pool.
1221 * Semi-private (library wide use only)
1224 be_find_zpool_callback(zpool_handle_t
*zlp
, void *data
)
1226 be_transaction_data_t
*bt
= data
;
1227 const char *zpool
= zpool_get_name(zlp
);
1228 char be_root_ds
[MAXPATHLEN
];
1231 * Generate string for the BE's root dataset
1233 be_make_root_ds(zpool
, bt
->obe_name
, be_root_ds
, sizeof (be_root_ds
));
1236 * Check if dataset exists
1238 if (zfs_dataset_exists(g_zfs
, be_root_ds
, ZFS_TYPE_FILESYSTEM
)) {
1239 /* BE's root dataset exists in zpool */
1240 bt
->obe_zpool
= strdup(zpool
);
1250 * Function: be_exists_callback
1251 * Description: Callback function used to find out if a BE exists.
1253 * zlp - zpool_handle_t pointer to the current pool being
1255 * data - BE name to look for.
1257 * 1 - BE exists in this pool.
1258 * 0 - BE does not exist in this pool.
1260 * Semi-private (library wide use only)
1263 be_exists_callback(zpool_handle_t
*zlp
, void *data
)
1265 const char *zpool
= zpool_get_name(zlp
);
1266 char *be_name
= data
;
1267 char be_root_ds
[MAXPATHLEN
];
1270 * Generate string for the BE's root dataset
1272 be_make_root_ds(zpool
, be_name
, be_root_ds
, sizeof (be_root_ds
));
1275 * Check if dataset exists
1277 if (zfs_dataset_exists(g_zfs
, be_root_ds
, ZFS_TYPE_FILESYSTEM
)) {
1278 /* BE's root dataset exists in zpool */
1288 * Function: be_has_snapshots_callback
1289 * Description: Callback function used to find out if a BE has snapshots.
1291 * zlp - zpool_handle_t pointer to the current pool being
1293 * data - be_snap_found_t pointer.
1295 * 1 - BE has no snapshots.
1296 * 0 - BE has snapshots.
1301 be_has_snapshot_callback(zfs_handle_t
*zhp
, void *data
)
1303 boolean_t
*bs
= data
;
1304 if (zfs_get_name(zhp
) == NULL
) {
1314 * Function: be_set_uuid
1315 * Description: This function generates a uuid, unparses it into
1316 * string representation, and sets that string into
1317 * a zfs user property for a root dataset of a BE.
1318 * The name of the user property used to store the
1319 * uuid is org.opensolaris.libbe:uuid
1322 * root_ds - Root dataset of the BE to set a uuid on.
1324 * be_errno_t - Failure
1325 * BE_SUCCESS - Success
1327 * Semi-private (library wide ues only)
1330 be_set_uuid(char *root_ds
)
1332 zfs_handle_t
*zhp
= NULL
;
1334 char uu_string
[UUID_PRINTABLE_STRING_LENGTH
] = { 0 };
1335 int ret
= BE_SUCCESS
;
1337 /* Generate a UUID and unparse it into string form */
1339 if (uuid_is_null(uu
) != 0) {
1340 be_print_err(gettext("be_set_uuid: failed to "
1341 "generate uuid\n"));
1342 return (BE_ERR_GEN_UUID
);
1344 uuid_unparse(uu
, uu_string
);
1346 /* Get handle to the BE's root dataset. */
1347 if ((zhp
= zfs_open(g_zfs
, root_ds
, ZFS_TYPE_FILESYSTEM
)) == NULL
) {
1348 be_print_err(gettext("be_set_uuid: failed to "
1349 "open BE root dataset (%s): %s\n"), root_ds
,
1350 libzfs_error_description(g_zfs
));
1351 return (zfs_err_to_be_err(g_zfs
));
1354 /* Set uuid property for the BE */
1355 if (zfs_prop_set(zhp
, BE_UUID_PROPERTY
, uu_string
) != 0) {
1356 be_print_err(gettext("be_set_uuid: failed to "
1357 "set uuid property for BE: %s\n"),
1358 libzfs_error_description(g_zfs
));
1359 ret
= zfs_err_to_be_err(g_zfs
);
1368 * Function: be_get_uuid
1369 * Description: This function gets the uuid string from a BE root
1370 * dataset, parses it into internal format, and returns
1371 * it the caller via a reference pointer passed in.
1374 * rootds - Root dataset of the BE to get the uuid from.
1375 * uu - reference pointer to a uuid_t to return uuid in.
1377 * be_errno_t - Failure
1378 * BE_SUCCESS - Success
1380 * Semi-private (library wide use only)
1383 be_get_uuid(const char *root_ds
, uuid_t
*uu
)
1385 zfs_handle_t
*zhp
= NULL
;
1386 nvlist_t
*userprops
= NULL
;
1387 nvlist_t
*propname
= NULL
;
1388 char *uu_string
= NULL
;
1389 int ret
= BE_SUCCESS
;
1391 /* Get handle to the BE's root dataset. */
1392 if ((zhp
= zfs_open(g_zfs
, root_ds
, ZFS_TYPE_FILESYSTEM
)) == NULL
) {
1393 be_print_err(gettext("be_get_uuid: failed to "
1394 "open BE root dataset (%s): %s\n"), root_ds
,
1395 libzfs_error_description(g_zfs
));
1396 return (zfs_err_to_be_err(g_zfs
));
1399 /* Get user properties for BE's root dataset */
1400 if ((userprops
= zfs_get_user_props(zhp
)) == NULL
) {
1401 be_print_err(gettext("be_get_uuid: failed to "
1402 "get user properties for BE root dataset (%s): %s\n"),
1403 root_ds
, libzfs_error_description(g_zfs
));
1404 ret
= zfs_err_to_be_err(g_zfs
);
1408 /* Get UUID string from BE's root dataset user properties */
1409 if (nvlist_lookup_nvlist(userprops
, BE_UUID_PROPERTY
, &propname
) != 0 ||
1410 nvlist_lookup_string(propname
, ZPROP_VALUE
, &uu_string
) != 0) {
1412 * This probably just means that the BE is simply too old
1413 * to have a uuid or that we haven't created a uuid for
1416 be_print_err(gettext("be_get_uuid: failed to "
1417 "get uuid property from BE root dataset user "
1419 ret
= BE_ERR_NO_UUID
;
1422 /* Parse uuid string into internal format */
1423 if (uuid_parse(uu_string
, *uu
) != 0 || uuid_is_null(*uu
)) {
1424 be_print_err(gettext("be_get_uuid: failed to "
1426 ret
= BE_ERR_PARSE_UUID
;
1435 /* ******************************************************************** */
1436 /* Private Functions */
1437 /* ******************************************************************** */
1440 * Function: _be_destroy
1441 * Description: Destroy a BE and all of its children datasets and snapshots.
1442 * This function is called for both global BEs and non-global BEs.
1443 * The root dataset of either the global BE or non-global BE to be
1444 * destroyed is passed in.
1446 * root_ds - pointer to the name of the root dataset of the
1448 * dd - pointer to a be_destroy_data_t structure.
1451 * BE_SUCCESS - Success
1452 * be_errno_t - Failure
1457 _be_destroy(const char *root_ds
, be_destroy_data_t
*dd
)
1459 zfs_handle_t
*zhp
= NULL
;
1460 char origin
[MAXPATHLEN
];
1461 char parent
[MAXPATHLEN
];
1463 boolean_t has_origin
= B_FALSE
;
1464 int ret
= BE_SUCCESS
;
1466 /* Get handle to BE's root dataset */
1467 if ((zhp
= zfs_open(g_zfs
, root_ds
, ZFS_TYPE_FILESYSTEM
)) ==
1469 be_print_err(gettext("be_destroy: failed to "
1470 "open BE root dataset (%s): %s\n"), root_ds
,
1471 libzfs_error_description(g_zfs
));
1472 return (zfs_err_to_be_err(g_zfs
));
1476 * Demote this BE in case it has dependent clones. This call
1477 * will end up closing the zfs handle passed in whether it
1478 * succeeds or fails.
1480 if (be_demote_callback(zhp
, NULL
) != 0) {
1481 be_print_err(gettext("be_destroy: "
1482 "failed to demote BE %s\n"), root_ds
);
1483 return (BE_ERR_DEMOTE
);
1486 /* Get handle to BE's root dataset */
1487 if ((zhp
= zfs_open(g_zfs
, root_ds
, ZFS_TYPE_FILESYSTEM
)) ==
1489 be_print_err(gettext("be_destroy: failed to "
1490 "open BE root dataset (%s): %s\n"), root_ds
,
1491 libzfs_error_description(g_zfs
));
1492 return (zfs_err_to_be_err(g_zfs
));
1496 * Get the origin of this BE's root dataset. This will be used
1497 * later to destroy the snapshots originally used to create this BE.
1499 if (zfs_prop_get(zhp
, ZFS_PROP_ORIGIN
, origin
, sizeof (origin
), NULL
,
1500 NULL
, 0, B_FALSE
) == 0) {
1501 (void) strlcpy(parent
, origin
, sizeof (parent
));
1502 if (be_get_snap(parent
, &snap
) != BE_SUCCESS
) {
1504 be_print_err(gettext("be_destroy: failed to "
1505 "get snapshot name from origin %s\n"), origin
);
1506 return (BE_ERR_INVAL
);
1508 has_origin
= B_TRUE
;
1512 * Destroy the BE's root and its hierarchical children. This call
1513 * will end up closing the zfs handle passed in whether it succeeds
1516 if (be_destroy_callback(zhp
, dd
) != 0) {
1517 be_print_err(gettext("be_destroy: failed to "
1518 "destroy BE %s\n"), root_ds
);
1519 ret
= zfs_err_to_be_err(g_zfs
);
1523 /* If BE has an origin */
1527 * If origin snapshot doesn't have any other
1528 * dependents, delete the origin.
1530 if ((zhp
= zfs_open(g_zfs
, origin
, ZFS_TYPE_SNAPSHOT
)) ==
1532 be_print_err(gettext("be_destroy: failed to "
1533 "open BE's origin (%s): %s\n"), origin
,
1534 libzfs_error_description(g_zfs
));
1535 ret
= zfs_err_to_be_err(g_zfs
);
1539 /* If origin has dependents, don't delete it. */
1540 if (zfs_prop_get_int(zhp
, ZFS_PROP_NUMCLONES
) != 0) {
1546 /* Get handle to BE's parent's root dataset */
1547 if ((zhp
= zfs_open(g_zfs
, parent
, ZFS_TYPE_FILESYSTEM
)) ==
1549 be_print_err(gettext("be_destroy: failed to "
1550 "open BE's parent root dataset (%s): %s\n"), parent
,
1551 libzfs_error_description(g_zfs
));
1552 ret
= zfs_err_to_be_err(g_zfs
);
1556 /* Destroy the snapshot origin used to create this BE. */
1558 * The boolean set to B_FALSE and passed to zfs_destroy_snaps()
1559 * tells zfs to process and destroy the snapshots now.
1560 * Otherwise the call will potentially return where the
1561 * snapshot isn't actually destroyed yet, and ZFS is waiting
1562 * until all the references to the snapshot have been
1563 * released before actually destroying the snapshot.
1565 if (zfs_destroy_snaps(zhp
, snap
, B_FALSE
) != 0) {
1566 be_print_err(gettext("be_destroy: failed to "
1567 "destroy original snapshots used to create "
1568 "BE: %s\n"), libzfs_error_description(g_zfs
));
1571 * If a failure happened because a clone exists,
1572 * don't return a failure to the user. Above, we're
1573 * only checking that the root dataset's origin
1574 * snapshot doesn't have dependent clones, but its
1575 * possible that a subordinate dataset origin snapshot
1576 * has a clone. We really need to check for that
1577 * before trying to destroy the origin snapshot.
1579 if (libzfs_errno(g_zfs
) != EZFS_EXISTS
) {
1580 ret
= zfs_err_to_be_err(g_zfs
);
1592 * Function: be_destroy_zones
1593 * Description: Find valid zone's and call be_destroy_zone_roots to destroy its
1594 * corresponding dataset and all of its children datasets
1597 * be_name - name of global boot environment being destroyed
1598 * be_root_ds - root dataset of global boot environment being
1600 * dd - be_destroy_data_t pointer
1602 * BE_SUCCESS - Success
1603 * be_errno_t - Failure
1607 * NOTES - Requires that the BE being deleted has no dependent BEs. If it
1608 * does, the destroy will fail.
1611 be_destroy_zones(char *be_name
, char *be_root_ds
, be_destroy_data_t
*dd
)
1614 int ret
= BE_SUCCESS
;
1615 int force_umnt
= BE_UNMOUNT_FLAG_NULL
;
1616 char *zonepath
= NULL
;
1617 char *zonename
= NULL
;
1618 char *zonepath_ds
= NULL
;
1620 zoneList_t zlist
= NULL
;
1621 zoneBrandList_t
*brands
= NULL
;
1622 zfs_handle_t
*zhp
= NULL
;
1624 /* If zones are not implemented, then get out. */
1625 if (!z_zones_are_implemented()) {
1626 return (BE_SUCCESS
);
1629 /* Get list of supported brands */
1630 if ((brands
= be_get_supported_brandlist()) == NULL
) {
1631 be_print_err(gettext("be_destroy_zones: "
1632 "no supported brands\n"));
1633 return (BE_SUCCESS
);
1636 /* Get handle to BE's root dataset */
1637 if ((zhp
= zfs_open(g_zfs
, be_root_ds
, ZFS_TYPE_FILESYSTEM
)) ==
1639 be_print_err(gettext("be_destroy_zones: failed to "
1640 "open BE root dataset (%s): %s\n"), be_root_ds
,
1641 libzfs_error_description(g_zfs
));
1642 z_free_brand_list(brands
);
1643 return (zfs_err_to_be_err(g_zfs
));
1647 * If the global BE is not mounted, we must mount it here to
1648 * gather data about the non-global zones in it.
1650 if (!zfs_is_mounted(zhp
, &mp
)) {
1651 if ((ret
= _be_mount(be_name
, &mp
,
1652 BE_MOUNT_FLAG_NO_ZONES
)) != BE_SUCCESS
) {
1653 be_print_err(gettext("be_destroy_zones: failed to "
1654 "mount the BE (%s) for zones processing.\n"),
1657 z_free_brand_list(brands
);
1663 z_set_zone_root(mp
);
1666 /* Get list of supported zones. */
1667 if ((zlist
= z_get_nonglobal_zone_list_by_brand(brands
)) == NULL
) {
1668 z_free_brand_list(brands
);
1669 return (BE_SUCCESS
);
1672 /* Unmount the BE before destroying the zones in it. */
1673 if (dd
->force_unmount
)
1674 force_umnt
= BE_UNMOUNT_FLAG_FORCE
;
1675 if ((ret
= _be_unmount(be_name
, force_umnt
)) != BE_SUCCESS
) {
1676 be_print_err(gettext("be_destroy_zones: failed to "
1677 "unmount the BE (%s)\n"), be_name
);
1681 /* Iterate through the zones and destroy them. */
1682 for (i
= 0; (zonename
= z_zlist_get_zonename(zlist
, i
)) != NULL
; i
++) {
1684 /* Skip zones that aren't at least installed */
1685 if (z_zlist_get_current_state(zlist
, i
) < ZONE_STATE_INSTALLED
)
1688 zonepath
= z_zlist_get_zonepath(zlist
, i
);
1691 * Get the dataset of this zonepath. If its not
1692 * a dataset, skip it.
1694 if ((zonepath_ds
= be_get_ds_from_dir(zonepath
)) == NULL
)
1698 * Check if this zone is supported based on the
1699 * dataset of its zonepath.
1701 if (!be_zone_supported(zonepath_ds
)) {
1706 /* Find the zone BE root datasets for this zone. */
1707 if ((ret
= be_destroy_zone_roots(zonepath_ds
, dd
))
1709 be_print_err(gettext("be_destroy_zones: failed to "
1710 "find and destroy zone roots for zone %s\n"),
1719 z_free_brand_list(brands
);
1720 z_free_zone_list(zlist
);
1726 * Function: be_destroy_zone_roots
1727 * Description: This function will open the zone's root container dataset
1728 * and iterate the datasets within, looking for roots that
1729 * belong to the given global BE and destroying them.
1730 * If no other zone roots remain in the zone's root container
1731 * dataset, the function will destroy it and the zone's
1732 * zonepath dataset as well.
1734 * zonepath_ds - pointer to zone's zonepath dataset.
1735 * dd - pointer to a linked destroy data.
1737 * BE_SUCCESS - Success
1738 * be_errno_t - Failure
1743 be_destroy_zone_roots(char *zonepath_ds
, be_destroy_data_t
*dd
)
1746 char zone_container_ds
[MAXPATHLEN
];
1747 int ret
= BE_SUCCESS
;
1749 /* Generate string for the root container dataset for this zone. */
1750 be_make_container_ds(zonepath_ds
, zone_container_ds
,
1751 sizeof (zone_container_ds
));
1753 /* Get handle to this zone's root container dataset. */
1754 if ((zhp
= zfs_open(g_zfs
, zone_container_ds
, ZFS_TYPE_FILESYSTEM
))
1756 be_print_err(gettext("be_destroy_zone_roots: failed to "
1757 "open zone root container dataset (%s): %s\n"),
1758 zone_container_ds
, libzfs_error_description(g_zfs
));
1759 return (zfs_err_to_be_err(g_zfs
));
1763 * Iterate through all of this zone's BEs, destroying the ones
1764 * that belong to the parent global BE.
1766 if ((ret
= zfs_iter_filesystems(zhp
, be_destroy_zone_roots_callback
,
1768 be_print_err(gettext("be_destroy_zone_roots: failed to "
1769 "destroy zone roots under zonepath dataset %s: %s\n"),
1770 zonepath_ds
, libzfs_error_description(g_zfs
));
1776 /* Get handle to this zone's root container dataset. */
1777 if ((zhp
= zfs_open(g_zfs
, zone_container_ds
, ZFS_TYPE_FILESYSTEM
))
1779 be_print_err(gettext("be_destroy_zone_roots: failed to "
1780 "open zone root container dataset (%s): %s\n"),
1781 zone_container_ds
, libzfs_error_description(g_zfs
));
1782 return (zfs_err_to_be_err(g_zfs
));
1786 * If there are no more zone roots in this zone's root container,
1787 * dataset, destroy it and the zonepath dataset as well.
1789 if (zfs_iter_filesystems(zhp
, be_zone_root_exists_callback
, NULL
)
1791 /* Destroy the zone root container dataset */
1792 if (zfs_unmount(zhp
, NULL
, MS_FORCE
) != 0 ||
1793 zfs_destroy(zhp
, B_FALSE
) != 0) {
1794 be_print_err(gettext("be_destroy_zone_roots: failed to "
1795 "destroy zone root container dataset (%s): %s\n"),
1796 zone_container_ds
, libzfs_error_description(g_zfs
));
1801 /* Get handle to zonepath dataset */
1802 if ((zhp
= zfs_open(g_zfs
, zonepath_ds
, ZFS_TYPE_FILESYSTEM
))
1804 be_print_err(gettext("be_destroy_zone_roots: failed to "
1805 "open zonepath dataset (%s): %s\n"),
1806 zonepath_ds
, libzfs_error_description(g_zfs
));
1810 /* Destroy zonepath dataset */
1811 if (zfs_unmount(zhp
, NULL
, MS_FORCE
) != 0 ||
1812 zfs_destroy(zhp
, B_FALSE
) != 0) {
1813 be_print_err(gettext("be_destroy_zone_roots: "
1814 "failed to destroy zonepath dataest %s: %s\n"),
1815 zonepath_ds
, libzfs_error_description(g_zfs
));
1826 * Function: be_destroy_zone_roots_callback
1827 * Description: This function is used as a callback to iterate over all of
1828 * a zone's root datasets, finding the one's that
1829 * correspond to the current BE. The name's
1830 * of the zone root datasets are then destroyed by _be_destroy().
1832 * zhp - zfs_handle_t pointer to current dataset being processed
1833 * data - be_destroy_data_t pointer
1836 * be_errno_t - Failure
1841 be_destroy_zone_roots_callback(zfs_handle_t
*zhp
, void *data
)
1843 be_destroy_data_t
*dd
= data
;
1844 uuid_t parent_uuid
= { 0 };
1847 if (be_zone_get_parent_uuid(zfs_get_name(zhp
), &parent_uuid
)
1849 be_print_err(gettext("be_destroy_zone_roots_callback: "
1850 "could not get parentuuid for zone root dataset %s\n"),
1856 if (uuid_compare(dd
->gz_be_uuid
, parent_uuid
) == 0) {
1858 * Found a zone root dataset belonging to the parent
1859 * BE being destroyed. Destroy this zone BE.
1861 if ((ret
= _be_destroy(zfs_get_name(zhp
), dd
)) != BE_SUCCESS
) {
1862 be_print_err(gettext("be_destroy_zone_root_callback: "
1863 "failed to destroy zone root %s\n"),
1875 * Function: be_copy_zones
1876 * Description: Find valid zones and clone them to create their
1877 * corresponding datasets for the BE being created.
1879 * obe_name - name of source global BE being copied.
1880 * obe_root_ds - root dataset of source global BE being copied.
1881 * nbe_root_ds - root dataset of target global BE.
1883 * BE_SUCCESS - Success
1884 * be_errno_t - Failure
1889 be_copy_zones(char *obe_name
, char *obe_root_ds
, char *nbe_root_ds
)
1892 int ret
= BE_SUCCESS
;
1894 char *zonename
= NULL
;
1895 char *zonepath
= NULL
;
1896 char *zone_be_name
= NULL
;
1897 char *temp_mntpt
= NULL
;
1898 char *new_zone_be_name
= NULL
;
1899 char zoneroot
[MAXPATHLEN
];
1900 char zoneroot_ds
[MAXPATHLEN
];
1901 char zone_container_ds
[MAXPATHLEN
];
1902 char new_zoneroot_ds
[MAXPATHLEN
];
1903 char ss
[MAXPATHLEN
];
1905 char uu_string
[UUID_PRINTABLE_STRING_LENGTH
] = { 0 };
1906 be_transaction_data_t bt
= { 0 };
1907 zfs_handle_t
*obe_zhp
= NULL
;
1908 zfs_handle_t
*nbe_zhp
= NULL
;
1909 zfs_handle_t
*z_zhp
= NULL
;
1910 zoneList_t zlist
= NULL
;
1911 zoneBrandList_t
*brands
= NULL
;
1912 boolean_t mounted_here
= B_FALSE
;
1913 char *snap_name
= NULL
;
1915 /* If zones are not implemented, then get out. */
1916 if (!z_zones_are_implemented()) {
1917 return (BE_SUCCESS
);
1920 /* Get list of supported brands */
1921 if ((brands
= be_get_supported_brandlist()) == NULL
) {
1922 be_print_err(gettext("be_copy_zones: "
1923 "no supported brands\n"));
1924 return (BE_SUCCESS
);
1927 /* Get handle to origin BE's root dataset */
1928 if ((obe_zhp
= zfs_open(g_zfs
, obe_root_ds
, ZFS_TYPE_FILESYSTEM
))
1930 be_print_err(gettext("be_copy_zones: failed to open "
1931 "the origin BE root dataset (%s) for zones processing: "
1932 "%s\n"), obe_root_ds
, libzfs_error_description(g_zfs
));
1933 return (zfs_err_to_be_err(g_zfs
));
1936 /* Get handle to newly cloned BE's root dataset */
1937 if ((nbe_zhp
= zfs_open(g_zfs
, nbe_root_ds
, ZFS_TYPE_FILESYSTEM
))
1939 be_print_err(gettext("be_copy_zones: failed to open "
1940 "the new BE root dataset (%s): %s\n"), nbe_root_ds
,
1941 libzfs_error_description(g_zfs
));
1943 return (zfs_err_to_be_err(g_zfs
));
1946 /* Get the uuid of the newly cloned parent BE. */
1947 if (be_get_uuid(zfs_get_name(nbe_zhp
), &uu
) != BE_SUCCESS
) {
1948 be_print_err(gettext("be_copy_zones: "
1949 "failed to get uuid for BE root "
1950 "dataset %s\n"), zfs_get_name(nbe_zhp
));
1955 uuid_unparse(uu
, uu_string
);
1958 * If the origin BE is not mounted, we must mount it here to
1959 * gather data about the non-global zones in it.
1961 if (!zfs_is_mounted(obe_zhp
, &temp_mntpt
)) {
1962 if ((ret
= _be_mount(obe_name
, &temp_mntpt
,
1963 BE_MOUNT_FLAG_NULL
)) != BE_SUCCESS
) {
1964 be_print_err(gettext("be_copy_zones: failed to "
1965 "mount the BE (%s) for zones procesing.\n"),
1969 mounted_here
= B_TRUE
;
1972 z_set_zone_root(temp_mntpt
);
1974 /* Get list of supported zones. */
1975 if ((zlist
= z_get_nonglobal_zone_list_by_brand(brands
)) == NULL
) {
1980 for (i
= 0; (zonename
= z_zlist_get_zonename(zlist
, i
)) != NULL
; i
++) {
1982 be_fs_list_data_t fld
= { 0 };
1983 char zonepath_ds
[MAXPATHLEN
];
1986 /* Get zonepath of zone */
1987 zonepath
= z_zlist_get_zonepath(zlist
, i
);
1989 /* Skip zones that aren't at least installed */
1990 if (z_zlist_get_current_state(zlist
, i
) < ZONE_STATE_INSTALLED
)
1994 * Get the dataset of this zonepath. If its not
1995 * a dataset, skip it.
1997 if ((ds
= be_get_ds_from_dir(zonepath
)) == NULL
)
2000 (void) strlcpy(zonepath_ds
, ds
, sizeof (zonepath_ds
));
2004 /* Get zoneroot directory */
2005 be_make_zoneroot(zonepath
, zoneroot
, sizeof (zoneroot
));
2007 /* If zonepath dataset not supported, skip it. */
2008 if (!be_zone_supported(zonepath_ds
)) {
2012 if ((ret
= be_find_active_zone_root(obe_zhp
, zonepath_ds
,
2013 zoneroot_ds
, sizeof (zoneroot_ds
))) != BE_SUCCESS
) {
2014 be_print_err(gettext("be_copy_zones: "
2015 "failed to find active zone root for zone %s "
2016 "in BE %s\n"), zonename
, obe_name
);
2020 be_make_container_ds(zonepath_ds
, zone_container_ds
,
2021 sizeof (zone_container_ds
));
2023 if ((z_zhp
= zfs_open(g_zfs
, zoneroot_ds
,
2024 ZFS_TYPE_FILESYSTEM
)) == NULL
) {
2025 be_print_err(gettext("be_copy_zones: "
2026 "failed to open zone root dataset (%s): %s\n"),
2027 zoneroot_ds
, libzfs_error_description(g_zfs
));
2028 ret
= zfs_err_to_be_err(g_zfs
);
2033 be_get_zone_be_name(zoneroot_ds
, zone_container_ds
);
2035 if ((new_zone_be_name
= be_auto_zone_be_name(zone_container_ds
,
2036 zone_be_name
)) == NULL
) {
2037 be_print_err(gettext("be_copy_zones: failed "
2038 "to generate auto name for zone BE.\n"));
2039 ret
= BE_ERR_AUTONAME
;
2043 if ((snap_name
= be_auto_snap_name()) == NULL
) {
2044 be_print_err(gettext("be_copy_zones: failed to "
2045 "generate snapshot name for zone BE.\n"));
2046 ret
= BE_ERR_AUTONAME
;
2050 (void) snprintf(ss
, sizeof (ss
), "%s@%s", zoneroot_ds
,
2053 if (zfs_snapshot(g_zfs
, ss
, B_TRUE
, NULL
) != 0) {
2054 be_print_err(gettext("be_copy_zones: "
2055 "failed to snapshot zone BE (%s): %s\n"),
2056 ss
, libzfs_error_description(g_zfs
));
2057 if (libzfs_errno(g_zfs
) == EZFS_EXISTS
)
2058 ret
= BE_ERR_ZONE_SS_EXISTS
;
2060 ret
= zfs_err_to_be_err(g_zfs
);
2065 (void) snprintf(new_zoneroot_ds
, sizeof (new_zoneroot_ds
),
2066 "%s/%s", zone_container_ds
, new_zone_be_name
);
2068 bt
.obe_name
= zone_be_name
;
2069 bt
.obe_root_ds
= zoneroot_ds
;
2070 bt
.obe_snap_name
= snap_name
;
2071 bt
.obe_altroot
= temp_mntpt
;
2072 bt
.nbe_name
= new_zone_be_name
;
2073 bt
.nbe_root_ds
= new_zoneroot_ds
;
2075 if (nvlist_alloc(&bt
.nbe_zfs_props
, NV_UNIQUE_NAME
, 0) != 0) {
2076 be_print_err(gettext("be_copy_zones: "
2077 "internal error: out of memory\n"));
2083 * The call to be_clone_fs_callback always closes the
2084 * zfs_handle so there's no need to close z_zhp.
2086 if ((iret
= be_clone_fs_callback(z_zhp
, &bt
)) != 0) {
2088 if (iret
!= BE_ERR_BE_EXISTS
) {
2089 be_print_err(gettext("be_copy_zones: "
2090 "failed to create zone BE clone for new "
2091 "zone BE %s\n"), new_zone_be_name
);
2093 nvlist_free(bt
.nbe_zfs_props
);
2097 * We failed to create the new zone BE because a zone
2098 * BE with the auto-name we generated above has since
2099 * come into existence. Regenerate a new auto-name
2102 for (num_retries
= 1;
2103 num_retries
< BE_AUTO_NAME_MAX_TRY
;
2106 /* Sleep 1 before retrying */
2109 /* Generate new auto zone BE name */
2110 free(new_zone_be_name
);
2111 if ((new_zone_be_name
= be_auto_zone_be_name(
2113 zone_be_name
)) == NULL
) {
2114 be_print_err(gettext("be_copy_zones: "
2115 "failed to generate auto name "
2117 ret
= BE_ERR_AUTONAME
;
2118 nvlist_free(bt
.nbe_zfs_props
);
2122 (void) snprintf(new_zoneroot_ds
,
2123 sizeof (new_zoneroot_ds
),
2124 "%s/%s", zone_container_ds
,
2126 bt
.nbe_name
= new_zone_be_name
;
2127 bt
.nbe_root_ds
= new_zoneroot_ds
;
2130 * Get handle to original zone BE's root
2133 if ((z_zhp
= zfs_open(g_zfs
, zoneroot_ds
,
2134 ZFS_TYPE_FILESYSTEM
)) == NULL
) {
2135 be_print_err(gettext("be_copy_zones: "
2136 "failed to open zone root "
2137 "dataset (%s): %s\n"),
2139 libzfs_error_description(g_zfs
));
2140 ret
= zfs_err_to_be_err(g_zfs
);
2141 nvlist_free(bt
.nbe_zfs_props
);
2146 * Try to clone the zone BE again. This
2147 * call will end up closing the zfs
2148 * handle passed in whether it
2149 * succeeds or fails.
2151 iret
= be_clone_fs_callback(z_zhp
, &bt
);
2155 } else if (iret
!= BE_ERR_BE_EXISTS
) {
2156 be_print_err(gettext("be_copy_zones: "
2157 "failed to create zone BE clone "
2158 "for new zone BE %s\n"),
2161 nvlist_free(bt
.nbe_zfs_props
);
2166 * If we've exhausted the maximum number of
2167 * tries, free the auto zone BE name and return
2170 if (num_retries
== BE_AUTO_NAME_MAX_TRY
) {
2171 be_print_err(gettext("be_copy_zones: failed "
2172 "to create a unique auto zone BE name\n"));
2175 ret
= BE_ERR_AUTONAME
;
2176 nvlist_free(bt
.nbe_zfs_props
);
2181 nvlist_free(bt
.nbe_zfs_props
);
2185 if ((z_zhp
= zfs_open(g_zfs
, new_zoneroot_ds
,
2186 ZFS_TYPE_FILESYSTEM
)) == NULL
) {
2187 be_print_err(gettext("be_copy_zones: "
2188 "failed to open the new zone BE root dataset "
2189 "(%s): %s\n"), new_zoneroot_ds
,
2190 libzfs_error_description(g_zfs
));
2191 ret
= zfs_err_to_be_err(g_zfs
);
2195 if (zfs_prop_set(z_zhp
, BE_ZONE_PARENTBE_PROPERTY
,
2197 be_print_err(gettext("be_copy_zones: "
2198 "failed to set parentbe property\n"));
2200 ret
= zfs_err_to_be_err(g_zfs
);
2204 if (zfs_prop_set(z_zhp
, BE_ZONE_ACTIVE_PROPERTY
, "on") != 0) {
2205 be_print_err(gettext("be_copy_zones: "
2206 "failed to set active property\n"));
2208 ret
= zfs_err_to_be_err(g_zfs
);
2213 * Generate a list of file systems from the original
2214 * zone BE that are legacy mounted. We use this list
2215 * to determine which entries in the vfstab we need to
2216 * update for the new zone BE we've just created.
2218 if ((ret
= be_get_legacy_fs(obe_name
, obe_root_ds
,
2219 zoneroot_ds
, zoneroot
, &fld
)) != BE_SUCCESS
) {
2220 be_print_err(gettext("be_copy_zones: "
2221 "failed to get legacy mounted file system "
2222 "list for zone %s\n"), zonename
);
2228 * Update new zone BE's vfstab.
2230 if ((ret
= be_update_zone_vfstab(z_zhp
, bt
.nbe_name
,
2231 zonepath_ds
, zonepath_ds
, &fld
)) != BE_SUCCESS
) {
2232 be_print_err(gettext("be_copy_zones: "
2233 "failed to update new BE's vfstab (%s)\n"),
2236 be_free_fs_list(&fld
);
2240 be_free_fs_list(&fld
);
2247 z_free_brand_list(brands
);
2249 z_free_zone_list(zlist
);
2252 (void) _be_unmount(obe_name
, 0);
2259 * Function: be_clone_fs_callback
2260 * Description: Callback function used to iterate through a BE's filesystems
2261 * to clone them for the new BE.
2263 * zhp - zfs_handle_t pointer for the filesystem being processed.
2264 * data - be_transaction_data_t pointer providing information
2265 * about original BE and new BE.
2268 * be_errno_t - Failure
2273 be_clone_fs_callback(zfs_handle_t
*zhp
, void *data
)
2275 be_transaction_data_t
*bt
= data
;
2276 zfs_handle_t
*zhp_ss
= NULL
;
2277 char prop_buf
[MAXPATHLEN
];
2278 char zhp_name
[ZFS_MAX_DATASET_NAME_LEN
];
2279 char clone_ds
[MAXPATHLEN
];
2280 char ss
[MAXPATHLEN
];
2283 if (zfs_prop_get(zhp
, ZFS_PROP_MOUNTPOINT
, prop_buf
,
2284 ZFS_MAXPROPLEN
, NULL
, NULL
, 0, B_FALSE
) != 0) {
2285 be_print_err(gettext("be_clone_fs_callback: "
2286 "failed to get dataset mountpoint (%s): %s\n"),
2287 zfs_get_name(zhp
), libzfs_error_description(g_zfs
));
2288 ret
= zfs_err_to_be_err(g_zfs
);
2293 if (zfs_prop_get_int(zhp
, ZFS_PROP_ZONED
) != 0 &&
2294 strcmp(prop_buf
, "legacy") != 0) {
2296 * Since zfs can't currently handle setting the
2297 * mountpoint for a zoned dataset we'll have to skip
2298 * this dataset. This is because the mountpoint is not
2304 * Get a copy of the dataset name from the zfs handle
2306 (void) strlcpy(zhp_name
, zfs_get_name(zhp
), sizeof (zhp_name
));
2309 * Get the clone dataset name and prepare the zfs properties for it.
2311 if ((ret
= be_prep_clone_send_fs(zhp
, bt
, clone_ds
,
2312 sizeof (clone_ds
))) != BE_SUCCESS
) {
2318 * Generate the name of the snapshot to use.
2320 (void) snprintf(ss
, sizeof (ss
), "%s@%s", zhp_name
,
2324 * Get handle to snapshot.
2326 if ((zhp_ss
= zfs_open(g_zfs
, ss
, ZFS_TYPE_SNAPSHOT
)) == NULL
) {
2327 be_print_err(gettext("be_clone_fs_callback: "
2328 "failed to get handle to snapshot (%s): %s\n"), ss
,
2329 libzfs_error_description(g_zfs
));
2330 ret
= zfs_err_to_be_err(g_zfs
);
2336 * Clone the dataset.
2338 if (zfs_clone(zhp_ss
, clone_ds
, bt
->nbe_zfs_props
) != 0) {
2339 be_print_err(gettext("be_clone_fs_callback: "
2340 "failed to create clone dataset (%s): %s\n"),
2341 clone_ds
, libzfs_error_description(g_zfs
));
2346 return (zfs_err_to_be_err(g_zfs
));
2353 * Iterate through zhp's children datasets (if any)
2354 * and clone them accordingly.
2356 if ((ret
= zfs_iter_filesystems(zhp
, be_clone_fs_callback
, bt
)) != 0) {
2358 * Error occurred while processing a child dataset.
2359 * Destroy this dataset and return error.
2361 zfs_handle_t
*d_zhp
= NULL
;
2365 if ((d_zhp
= zfs_open(g_zfs
, clone_ds
, ZFS_TYPE_FILESYSTEM
))
2370 (void) zfs_destroy(d_zhp
, B_FALSE
);
2380 * Function: be_send_fs_callback
2381 * Description: Callback function used to iterate through a BE's filesystems
2382 * to copy them for the new BE.
2384 * zhp - zfs_handle_t pointer for the filesystem being processed.
2385 * data - be_transaction_data_t pointer providing information
2386 * about original BE and new BE.
2389 * be_errnot_t - Failure
2394 be_send_fs_callback(zfs_handle_t
*zhp
, void *data
)
2396 be_transaction_data_t
*bt
= data
;
2397 recvflags_t flags
= { 0 };
2398 char zhp_name
[ZFS_MAX_DATASET_NAME_LEN
];
2399 char clone_ds
[MAXPATHLEN
];
2400 sendflags_t send_flags
= { 0 };
2401 int pid
, status
, retval
;
2406 * Get a copy of the dataset name from the zfs handle
2408 (void) strlcpy(zhp_name
, zfs_get_name(zhp
), sizeof (zhp_name
));
2411 * Get the clone dataset name and prepare the zfs properties for it.
2413 if ((ret
= be_prep_clone_send_fs(zhp
, bt
, clone_ds
,
2414 sizeof (clone_ds
))) != BE_SUCCESS
) {
2420 * Create the new dataset.
2422 if (zfs_create(g_zfs
, clone_ds
, ZFS_TYPE_FILESYSTEM
, bt
->nbe_zfs_props
)
2424 be_print_err(gettext("be_send_fs_callback: "
2425 "failed to create new dataset '%s': %s\n"),
2426 clone_ds
, libzfs_error_description(g_zfs
));
2427 ret
= zfs_err_to_be_err(g_zfs
);
2433 * Destination file system is already created
2434 * hence we need to set the force flag on
2436 flags
.force
= B_TRUE
;
2439 * Initiate the pipe to be used for the send and recv
2441 if (pipe(srpipe
) != 0) {
2443 be_print_err(gettext("be_send_fs_callback: failed to "
2446 return (errno_to_be_err(err
));
2450 * Fork off a child to send the dataset
2452 if ((pid
= fork()) == -1) {
2454 be_print_err(gettext("be_send_fs_callback: failed to fork\n"));
2455 (void) close(srpipe
[0]);
2456 (void) close(srpipe
[1]);
2458 return (errno_to_be_err(err
));
2459 } else if (pid
== 0) { /* child process */
2460 (void) close(srpipe
[0]);
2463 if (zfs_send(zhp
, NULL
, bt
->obe_snap_name
, &send_flags
,
2464 srpipe
[1], NULL
, NULL
, NULL
) != 0) {
2472 (void) close(srpipe
[1]);
2474 /* Receive dataset */
2475 if (zfs_receive(g_zfs
, clone_ds
, NULL
, &flags
, srpipe
[0], NULL
) != 0) {
2476 be_print_err(gettext("be_send_fs_callback: failed to "
2477 "recv dataset (%s)\n"), clone_ds
);
2479 (void) close(srpipe
[0]);
2481 /* wait for child to exit */
2483 retval
= waitpid(pid
, &status
, 0);
2487 } while (retval
!= pid
);
2489 if (WEXITSTATUS(status
) != 0) {
2490 be_print_err(gettext("be_send_fs_callback: failed to "
2491 "send dataset (%s)\n"), zhp_name
);
2493 return (BE_ERR_ZFS
);
2498 * Iterate through zhp's children datasets (if any)
2499 * and send them accordingly.
2501 if ((ret
= zfs_iter_filesystems(zhp
, be_send_fs_callback
, bt
)) != 0) {
2503 * Error occurred while processing a child dataset.
2504 * Destroy this dataset and return error.
2506 zfs_handle_t
*d_zhp
= NULL
;
2510 if ((d_zhp
= zfs_open(g_zfs
, clone_ds
, ZFS_TYPE_FILESYSTEM
))
2515 (void) zfs_destroy(d_zhp
, B_FALSE
);
2525 * Function: be_destroy_callback
2526 * Description: Callback function used to destroy a BEs children datasets
2529 * zhp - zfs_handle_t pointer to the filesystem being processed.
2533 * be_errno_t - Failure
2538 be_destroy_callback(zfs_handle_t
*zhp
, void *data
)
2540 be_destroy_data_t
*dd
= data
;
2544 * Iterate down this file system's hierarchical children
2545 * and destroy them first.
2547 if ((ret
= zfs_iter_filesystems(zhp
, be_destroy_callback
, dd
)) != 0) {
2552 if (dd
->destroy_snaps
) {
2554 * Iterate through this file system's snapshots and
2555 * destroy them before destroying the file system itself.
2557 if ((ret
= zfs_iter_snapshots(zhp
, B_FALSE
, be_destroy_callback
,
2565 /* Attempt to unmount the dataset before destroying it */
2566 if (dd
->force_unmount
) {
2567 if ((ret
= zfs_unmount(zhp
, NULL
, MS_FORCE
)) != 0) {
2568 be_print_err(gettext("be_destroy_callback: "
2569 "failed to unmount %s: %s\n"), zfs_get_name(zhp
),
2570 libzfs_error_description(g_zfs
));
2571 ret
= zfs_err_to_be_err(g_zfs
);
2577 if (zfs_destroy(zhp
, B_FALSE
) != 0) {
2578 be_print_err(gettext("be_destroy_callback: "
2579 "failed to destroy %s: %s\n"), zfs_get_name(zhp
),
2580 libzfs_error_description(g_zfs
));
2581 ret
= zfs_err_to_be_err(g_zfs
);
2591 * Function: be_demote_callback
2592 * Description: This callback function is used to iterate through the file
2593 * systems of a BE, looking for the right clone to promote such
2594 * that this file system is left without any dependent clones.
2595 * If the file system has no dependent clones, it doesn't need
2596 * to get demoted, and the function will return success.
2598 * The demotion will be done in two passes. The first pass
2599 * will attempt to find the youngest snapshot that has a clone
2600 * that is part of some other BE. The second pass will attempt
2601 * to find the youngest snapshot that has a clone that is not
2602 * part of a BE. Doing this helps ensure the aggregated set of
2603 * file systems that compose a BE stay coordinated wrt BE
2604 * snapshots and BE dependents. It also prevents a random user
2605 * generated clone of a BE dataset to become the parent of other
2606 * BE datasets after demoting this dataset.
2609 * zhp - zfs_handle_t pointer to the current file system being
2614 * be_errno_t - Failure
2620 be_demote_callback(zfs_handle_t
*zhp
, void *data
)
2622 be_demote_data_t dd
= { 0 };
2626 * Initialize be_demote_data for the first pass - this will find a
2627 * clone in another BE, if one exists.
2629 dd
.find_in_BE
= B_TRUE
;
2631 for (i
= 0; i
< 2; i
++) {
2633 if (zfs_iter_snapshots(zhp
, B_FALSE
,
2634 be_demote_find_clone_callback
, &dd
) != 0) {
2635 be_print_err(gettext("be_demote_callback: "
2636 "failed to iterate snapshots for %s: %s\n"),
2637 zfs_get_name(zhp
), libzfs_error_description(g_zfs
));
2638 ret
= zfs_err_to_be_err(g_zfs
);
2642 if (dd
.clone_zhp
!= NULL
) {
2643 /* Found the clone to promote. Promote it. */
2644 if (zfs_promote(dd
.clone_zhp
) != 0) {
2645 be_print_err(gettext("be_demote_callback: "
2646 "failed to promote %s: %s\n"),
2647 zfs_get_name(dd
.clone_zhp
),
2648 libzfs_error_description(g_zfs
));
2649 ret
= zfs_err_to_be_err(g_zfs
);
2650 ZFS_CLOSE(dd
.clone_zhp
);
2655 ZFS_CLOSE(dd
.clone_zhp
);
2659 * Reinitialize be_demote_data for the second pass.
2660 * This will find a user created clone outside of any BE
2661 * namespace, if one exists.
2663 dd
.clone_zhp
= NULL
;
2664 dd
.origin_creation
= 0;
2666 dd
.find_in_BE
= B_FALSE
;
2669 /* Iterate down this file system's children and demote them */
2670 if ((ret
= zfs_iter_filesystems(zhp
, be_demote_callback
, NULL
)) != 0) {
2680 * Function: be_demote_find_clone_callback
2681 * Description: This callback function is used to iterate through the
2682 * snapshots of a dataset, looking for the youngest snapshot
2683 * that has a clone. If found, it returns a reference to the
2684 * clone back to the caller in the callback data.
2686 * zhp - zfs_handle_t pointer to current snapshot being looked at
2687 * data - be_demote_data_t pointer used to store the clone that
2690 * 0 - Successfully iterated through all snapshots.
2691 * 1 - Failed to iterate through all snapshots.
2696 be_demote_find_clone_callback(zfs_handle_t
*zhp
, void *data
)
2698 be_demote_data_t
*dd
= data
;
2699 time_t snap_creation
;
2702 /* If snapshot has no clones, no need to look at it */
2703 if (zfs_prop_get_int(zhp
, ZFS_PROP_NUMCLONES
) == 0) {
2708 dd
->snapshot
= zfs_get_name(zhp
);
2710 /* Get the creation time of this snapshot */
2711 snap_creation
= (time_t)zfs_prop_get_int(zhp
, ZFS_PROP_CREATION
);
2714 * If this snapshot's creation time is greater than (or younger than)
2715 * the current youngest snapshot found, iterate this snapshot to
2716 * check if it has a clone that we're looking for.
2718 if (snap_creation
>= dd
->origin_creation
) {
2720 * Iterate the dependents of this snapshot to find a
2721 * a clone that's a direct dependent.
2723 if ((zret
= zfs_iter_dependents(zhp
, B_FALSE
,
2724 be_demote_get_one_clone
, dd
)) == -1) {
2725 be_print_err(gettext("be_demote_find_clone_callback: "
2726 "failed to iterate dependents of %s\n"),
2730 } else if (zret
== 1) {
2732 * Found a clone, update the origin_creation time
2733 * in the callback data.
2735 dd
->origin_creation
= snap_creation
;
2744 * Function: be_demote_get_one_clone
2745 * Description: This callback function is used to iterate through a
2746 * snapshot's dependencies to find a filesystem that is a
2747 * direct clone of the snapshot being iterated.
2749 * zhp - zfs_handle_t pointer to current dataset being looked at
2750 * data - be_demote_data_t pointer used to store the clone
2751 * that is found, and also provides flag to note
2752 * whether or not the clone filesystem being searched
2753 * for needs to be found in a BE dataset hierarchy.
2755 * 1 - Success, found clone and its also a BE's root dataset.
2756 * 0 - Failure, clone not found.
2761 be_demote_get_one_clone(zfs_handle_t
*zhp
, void *data
)
2763 be_demote_data_t
*dd
= data
;
2764 char origin
[ZFS_MAX_DATASET_NAME_LEN
];
2765 char ds_path
[ZFS_MAX_DATASET_NAME_LEN
];
2767 if (zfs_get_type(zhp
) != ZFS_TYPE_FILESYSTEM
) {
2772 (void) strlcpy(ds_path
, zfs_get_name(zhp
), sizeof (ds_path
));
2775 * Make sure this is a direct clone of the snapshot
2778 if (zfs_prop_get(zhp
, ZFS_PROP_ORIGIN
, origin
, sizeof (origin
), NULL
,
2779 NULL
, 0, B_FALSE
) != 0) {
2780 be_print_err(gettext("be_demote_get_one_clone: "
2781 "failed to get origin of %s: %s\n"), ds_path
,
2782 libzfs_error_description(g_zfs
));
2786 if (strcmp(origin
, dd
->snapshot
) != 0) {
2791 if (dd
->find_in_BE
) {
2792 if ((zpool_iter(g_zfs
, be_check_be_roots_callback
, ds_path
))
2794 if (dd
->clone_zhp
!= NULL
)
2795 ZFS_CLOSE(dd
->clone_zhp
);
2796 dd
->clone_zhp
= zhp
;
2804 if (dd
->clone_zhp
!= NULL
)
2805 ZFS_CLOSE(dd
->clone_zhp
);
2807 dd
->clone_zhp
= zhp
;
2812 * Function: be_get_snap
2813 * Description: This function takes a snapshot dataset name and separates
2814 * out the parent dataset portion from the snapshot name.
2815 * I.e. it finds the '@' in the snapshot dataset name and
2816 * replaces it with a '\0'.
2818 * origin - char pointer to a snapshot dataset name. Its
2819 * contents will be modified by this function.
2820 * *snap - pointer to a char pointer. Will be set to the
2821 * snapshot name portion upon success.
2823 * BE_SUCCESS - Success
2829 be_get_snap(char *origin
, char **snap
)
2834 * Separate out the origin's dataset and snapshot portions by
2835 * replacing the @ with a '\0'
2837 cp
= strrchr(origin
, '@');
2839 if (cp
[1] != NULL
&& cp
[1] != '\0') {
2849 return (BE_SUCCESS
);
2853 * Function: be_create_container_ds
2854 * Description: This function checks that the zpool passed has the BE
2855 * container dataset, and if not, then creates it.
2857 * zpool - name of pool to create BE container dataset in.
2859 * B_TRUE - Successfully created BE container dataset, or it
2861 * B_FALSE - Failed to create container dataset.
2866 be_create_container_ds(char *zpool
)
2868 nvlist_t
*props
= NULL
;
2869 char be_container_ds
[MAXPATHLEN
];
2871 /* Generate string for BE container dataset for this pool */
2872 be_make_container_ds(zpool
, be_container_ds
,
2873 sizeof (be_container_ds
));
2875 if (!zfs_dataset_exists(g_zfs
, be_container_ds
, ZFS_TYPE_FILESYSTEM
)) {
2877 if (nvlist_alloc(&props
, NV_UNIQUE_NAME
, 0) != 0) {
2878 be_print_err(gettext("be_create_container_ds: "
2879 "nvlist_alloc failed\n"));
2883 if (nvlist_add_string(props
,
2884 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT
),
2885 ZFS_MOUNTPOINT_LEGACY
) != 0) {
2886 be_print_err(gettext("be_create_container_ds: "
2887 "internal error: out of memory\n"));
2892 if (nvlist_add_string(props
,
2893 zfs_prop_to_name(ZFS_PROP_CANMOUNT
), "off") != 0) {
2894 be_print_err(gettext("be_create_container_ds: "
2895 "internal error: out of memory\n"));
2900 if (zfs_create(g_zfs
, be_container_ds
, ZFS_TYPE_FILESYSTEM
,
2902 be_print_err(gettext("be_create_container_ds: "
2903 "failed to create container dataset (%s): %s\n"),
2904 be_container_ds
, libzfs_error_description(g_zfs
));
2916 * Function: be_prep_clone_send_fs
2917 * Description: This function takes a zfs handle to a dataset from the
2918 * original BE, and generates the name of the clone dataset
2919 * to create for the new BE. It also prepares the zfs
2920 * properties to be used for the new BE.
2922 * zhp - pointer to zfs_handle_t of the file system being
2924 * bt - be_transaction_data pointer providing information
2925 * about the original BE and new BE.
2926 * clone_ds - buffer to store the name of the dataset
2928 * clone_ds_len - length of clone_ds buffer
2930 * BE_SUCCESS - Success
2931 * be_errno_t - Failure
2936 be_prep_clone_send_fs(zfs_handle_t
*zhp
, be_transaction_data_t
*bt
,
2937 char *clone_ds
, int clone_ds_len
)
2939 zprop_source_t sourcetype
;
2940 char source
[ZFS_MAX_DATASET_NAME_LEN
];
2941 char zhp_name
[ZFS_MAX_DATASET_NAME_LEN
];
2942 char mountpoint
[MAXPATHLEN
];
2943 char *child_fs
= NULL
;
2944 char *zhp_mountpoint
= NULL
;
2948 * Get a copy of the dataset name zfs_name from zhp
2950 (void) strlcpy(zhp_name
, zfs_get_name(zhp
), sizeof (zhp_name
));
2953 * Get file system name relative to the root.
2955 if (strncmp(zhp_name
, bt
->obe_root_ds
, strlen(bt
->obe_root_ds
))
2957 child_fs
= zhp_name
+ strlen(bt
->obe_root_ds
);
2960 * if child_fs is NULL, this means we're processing the
2961 * root dataset itself; set child_fs to the empty string.
2963 if (child_fs
== NULL
)
2966 return (BE_ERR_INVAL
);
2970 * Generate the name of the clone file system.
2972 (void) snprintf(clone_ds
, clone_ds_len
, "%s%s", bt
->nbe_root_ds
,
2975 /* Get the mountpoint and source properties of the existing dataset */
2976 if (zfs_prop_get(zhp
, ZFS_PROP_MOUNTPOINT
, mountpoint
,
2977 sizeof (mountpoint
), &sourcetype
, source
, sizeof (source
),
2979 be_print_err(gettext("be_prep_clone_send_fs: "
2980 "failed to get mountpoint for (%s): %s\n"),
2981 zhp_name
, libzfs_error_description(g_zfs
));
2982 return (zfs_err_to_be_err(g_zfs
));
2986 * Workaround for 6668667 where a mountpoint property of "/" comes
2989 if (strcmp(mountpoint
, "") == 0) {
2990 (void) snprintf(mountpoint
, sizeof (mountpoint
), "/");
2994 * Figure out what to set as the mountpoint for the new dataset.
2995 * If the source of the mountpoint property is local, use the
2996 * mountpoint value itself. Otherwise, remove it from the
2997 * zfs properties list so that it gets inherited.
2999 if (sourcetype
& ZPROP_SRC_LOCAL
) {
3001 * If the BE that this file system is a part of is
3002 * currently mounted, strip off the BE altroot portion
3003 * from the mountpoint.
3005 zhp_mountpoint
= mountpoint
;
3007 if (strcmp(mountpoint
, ZFS_MOUNTPOINT_LEGACY
) != 0 &&
3008 bt
->obe_altroot
!= NULL
&& strcmp(bt
->obe_altroot
,
3009 "/") != 0 && zfs_is_mounted(zhp
, NULL
)) {
3011 int altroot_len
= strlen(bt
->obe_altroot
);
3013 if (strncmp(bt
->obe_altroot
, mountpoint
, altroot_len
)
3015 if (mountpoint
[altroot_len
] == '/')
3016 zhp_mountpoint
= mountpoint
+
3018 else if (mountpoint
[altroot_len
] == '\0')
3019 (void) snprintf(mountpoint
,
3020 sizeof (mountpoint
), "/");
3024 if (nvlist_add_string(bt
->nbe_zfs_props
,
3025 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT
),
3026 zhp_mountpoint
) != 0) {
3027 be_print_err(gettext("be_prep_clone_send_fs: "
3028 "internal error: out of memory\n"));
3029 return (BE_ERR_NOMEM
);
3032 err
= nvlist_remove_all(bt
->nbe_zfs_props
,
3033 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT
));
3034 if (err
!= 0 && err
!= ENOENT
) {
3035 be_print_err(gettext("be_prep_clone_send_fs: "
3036 "failed to remove mountpoint from "
3038 return (BE_ERR_INVAL
);
3043 * Set the 'canmount' property
3045 if (nvlist_add_string(bt
->nbe_zfs_props
,
3046 zfs_prop_to_name(ZFS_PROP_CANMOUNT
), "noauto") != 0) {
3047 be_print_err(gettext("be_prep_clone_send_fs: "
3048 "internal error: out of memory\n"));
3049 return (BE_ERR_NOMEM
);
3052 return (BE_SUCCESS
);
3056 * Function: be_get_zone_be_name
3057 * Description: This function takes the zones root dataset, the container
3058 * dataset and returns the zones BE name based on the zone
3061 * root_ds - the zones root dataset.
3062 * container_ds - the container dataset for the zone.
3064 * char * - the BE name of this zone based on the root dataset.
3067 be_get_zone_be_name(char *root_ds
, char *container_ds
)
3069 return (root_ds
+ (strlen(container_ds
) + 1));
3073 * Function: be_zone_root_exists_callback
3074 * Description: This callback function is used to determine if a
3075 * zone root container dataset has any children. It always
3076 * returns 1, signifying a hierarchical child of the zone
3077 * root container dataset has been traversed and therefore
3080 * zhp - zfs_handle_t pointer to current dataset being processed.
3083 * 1 - dataset exists
3089 be_zone_root_exists_callback(zfs_handle_t
*zhp
, void *data
)