1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
28 * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
29 */
30
31 /*
32 * System includes
33 */
34 #include <assert.h>
35 #include <errno.h>
36 #include <libintl.h>
37 #include <libnvpair.h>
38 #include <libzfs.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/mntent.h>
43 #include <sys/mnttab.h>
44 #include <sys/mount.h>
45 #include <sys/stat.h>
46 #include <sys/types.h>
47 #include <sys/vfstab.h>
48 #include <unistd.h>
49
50 #include <libbe.h>
51 #include <libbe_priv.h>
52
53 typedef struct active_zone_root_data {
54 uuid_t parent_uuid;
55 char *zoneroot_ds;
56 } active_zone_root_data_t;
57
58 typedef struct mounted_zone_root_data {
59 char *zone_altroot;
60 char *zoneroot_ds;
61 } mounted_zone_root_data_t;
62
63 /* Private function prototypes */
64 static int be_find_active_zone_root_callback(zfs_handle_t *, void *);
65 static int be_find_mounted_zone_root_callback(zfs_handle_t *, void *);
66 static boolean_t be_zone_get_active(zfs_handle_t *);
67
68
69 /* ******************************************************************** */
70 /* Semi-Private Functions */
71 /* ******************************************************************** */
72
73 /*
74 * Function: be_make_zoneroot
75 * Description: Generate a string for a zone's zoneroot given the
76 * zone's zonepath.
77 * Parameters:
78 * zonepath - pointer to zonepath
79 * zoneroot - pointer to buffer to retrn zoneroot in.
80 * zoneroot_size - size of zoneroot
81 * Returns:
82 * None
83 * Scope:
84 * Semi-private (library wise use only)
85 */
86 void
be_make_zoneroot(char * zonepath,char * zoneroot,int zoneroot_size)87 be_make_zoneroot(char *zonepath, char *zoneroot, int zoneroot_size)
88 {
89 (void) snprintf(zoneroot, zoneroot_size, "%s/root", zonepath);
90 }
91
92 /*
93 * Function: be_find_active_zone_root
94 * Description: This function will find the active zone root of a zone for
95 * a given global BE. It will iterate all of the zone roots
96 * under a zonepath, find the zone roots that belong to the
97 * specified global BE, and return the one that is active.
98 * Parameters:
99 * be_zhp - zfs handle to global BE root dataset.
100 * zonepath_ds - pointer to zone's zonepath dataset.
101 * zoneroot_ds - pointer to a buffer to store the dataset name of
102 * the zone's zoneroot that's currently active for this
103 * given global BE..
104 * zoneroot-ds_size - size of zoneroot_ds.
105 * Returns:
106 * BE_SUCCESS - Success
107 * be_errno_t - Failure
108 * Scope:
109 * Semi-private (library wide use only)
110 */
111 int
be_find_active_zone_root(zfs_handle_t * be_zhp,char * zonepath_ds,char * zoneroot_ds,int zoneroot_ds_size)112 be_find_active_zone_root(zfs_handle_t *be_zhp, char *zonepath_ds,
113 char *zoneroot_ds, int zoneroot_ds_size)
114 {
115 active_zone_root_data_t azr_data = { { 0 }, NULL };
116 zfs_handle_t *zhp;
117 char zone_container_ds[MAXPATHLEN];
118 int ret = BE_SUCCESS;
119
120 /* Get the uuid of the parent global BE */
121 if (getzoneid() == GLOBAL_ZONEID) {
122 if ((ret = be_get_uuid(zfs_get_name(be_zhp),
123 &azr_data.parent_uuid)) != BE_SUCCESS) {
124 be_print_err(gettext("be_find_active_zone_root: failed "
125 "to get uuid for BE root dataset %s\n"),
126 zfs_get_name(be_zhp));
127 return (ret);
128 }
129 } else {
130 if ((ret = be_zone_get_parent_uuid(zfs_get_name(be_zhp),
131 &azr_data.parent_uuid)) != BE_SUCCESS) {
132 be_print_err(gettext("be_find_active_zone_root: failed "
133 "to get parentbe uuid for zone root dataset %s\n"),
134 zfs_get_name(be_zhp));
135 return (ret);
136 }
137 }
138
139 /* Generate string for the root container dataset for this zone. */
140 if ((ret = be_make_container_ds(zonepath_ds, zone_container_ds,
141 sizeof (zone_container_ds))) != BE_SUCCESS) {
142 be_print_err(gettext("%s: failed to get BE container dataset "
143 "for %s\n"), __func__, zonepath_ds);
144 return (ret);
145 }
146
147 /* Get handle to this zone's root container dataset */
148 if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM))
149 == NULL) {
150 be_print_err(gettext("be_find_active_zone_root: failed to "
151 "open zone root container dataset (%s): %s\n"),
152 zone_container_ds, libzfs_error_description(g_zfs));
153 return (zfs_err_to_be_err(g_zfs));
154 }
155
156 /*
157 * Iterate through all of this zone's BEs, looking for ones
158 * that belong to the parent global BE, and finding the one
159 * that is marked active.
160 */
161 if ((ret = zfs_iter_filesystems(zhp, be_find_active_zone_root_callback,
162 &azr_data)) != 0) {
163 be_print_err(gettext("be_find_active_zone_root: failed to "
164 "find active zone root in zonepath dataset %s: %s\n"),
165 zonepath_ds, be_err_to_str(ret));
166 goto done;
167 }
168
169 if (azr_data.zoneroot_ds != NULL) {
170 (void) strlcpy(zoneroot_ds, azr_data.zoneroot_ds,
171 zoneroot_ds_size);
172 free(azr_data.zoneroot_ds);
173 } else {
174 be_print_err(gettext("be_find_active_zone_root: failed to "
175 "find active zone root in zonepath dataset %s\n"),
176 zonepath_ds);
177 ret = BE_ERR_ZONE_NO_ACTIVE_ROOT;
178 }
179
180 done:
181 ZFS_CLOSE(zhp);
182 return (ret);
183 }
184
185 /*
186 * Function: be_find_mounted_zone_root
187 * Description: This function will find the dataset mounted as the zoneroot
188 * of a zone for a given mounted global BE.
189 * Parameters:
190 * zone_altroot - path of zoneroot wrt the mounted global BE.
191 * zonepath_ds - dataset of the zone's zonepath.
192 * zoneroot_ds - pointer to a buffer to store the dataset of
193 * the zoneroot that currently mounted for this zone
194 * in the mounted global BE.
195 * zoneroot_ds_size - size of zoneroot_ds
196 * Returns:
197 * BE_SUCCESS - Success
198 * be_errno_t - Failure
199 * Scope:
200 * Semi-private (library wide use only)
201 */
202 int
be_find_mounted_zone_root(char * zone_altroot,char * zonepath_ds,char * zoneroot_ds,int zoneroot_ds_size)203 be_find_mounted_zone_root(char *zone_altroot, char *zonepath_ds,
204 char *zoneroot_ds, int zoneroot_ds_size)
205 {
206 mounted_zone_root_data_t mzr_data = { 0 };
207 zfs_handle_t *zhp = NULL;
208 char zone_container_ds[MAXPATHLEN];
209 int ret = BE_SUCCESS;
210 int zret = 0;
211
212 /* Generate string for the root container dataset for this zone. */
213 if ((ret = be_make_container_ds(zonepath_ds, zone_container_ds,
214 sizeof (zone_container_ds))) != BE_SUCCESS) {
215 be_print_err(gettext("%s: failed to get BE container dataset "
216 "for %s\n"), __func__, zonepath_ds);
217 return (ret);
218 }
219
220 /* Get handle to this zone's root container dataset. */
221 if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM))
222 == NULL) {
223 be_print_err(gettext("be_find_mounted_zone_root: failed to "
224 "open zone root container dataset (%s): %s\n"),
225 zone_container_ds, libzfs_error_description(g_zfs));
226 return (zfs_err_to_be_err(g_zfs));
227 }
228
229 mzr_data.zone_altroot = zone_altroot;
230
231 /*
232 * Iterate through all of the zone's BEs, looking for the one
233 * that is currently mounted at the zone altroot in the mounted
234 * global BE.
235 */
236 if ((zret = zfs_iter_filesystems(zhp,
237 be_find_mounted_zone_root_callback, &mzr_data)) == 0) {
238 be_print_err(gettext("be_find_mounted_zone_root: did not "
239 "find mounted zone under altroot zonepath %s\n"),
240 zonepath_ds);
241 ret = BE_ERR_NO_MOUNTED_ZONE;
242 goto done;
243 } else if (zret < 0) {
244 be_print_err(gettext("be_find_mounted_zone_root: "
245 "zfs_iter_filesystems failed: %s\n"),
246 libzfs_error_description(g_zfs));
247 ret = zfs_err_to_be_err(g_zfs);
248 goto done;
249 }
250
251 if (mzr_data.zoneroot_ds != NULL) {
252 (void) strlcpy(zoneroot_ds, mzr_data.zoneroot_ds,
253 zoneroot_ds_size);
254 free(mzr_data.zoneroot_ds);
255 }
256
257 done:
258 ZFS_CLOSE(zhp);
259 return (ret);
260 }
261
262 /*
263 * Function: be_zone_supported
264 * Description: This function will determine if a zone is supported
265 * based on its zonepath dataset. The zonepath dataset
266 * must:
267 * - not be under any global BE root dataset.
268 * - have a root container dataset underneath it.
269 *
270 * Parameters:
271 * zonepath_ds - name of dataset of the zonepath of the
272 * zone to check.
273 * Returns:
274 * B_TRUE - zone is supported
275 * B_FALSE - zone is not supported
276 * Scope:
277 * Semi-private (library wide use only)
278 */
279 boolean_t
be_zone_supported(char * zonepath_ds)280 be_zone_supported(char *zonepath_ds)
281 {
282 char zone_container_ds[MAXPATHLEN];
283 int ret = 0;
284
285 /*
286 * Make sure the dataset for the zonepath is not hierarchically
287 * under any reserved BE root container dataset of any pool.
288 */
289 if ((ret = zpool_iter(g_zfs, be_check_be_roots_callback,
290 zonepath_ds)) > 0) {
291 be_print_err(gettext("be_zone_supported: "
292 "zonepath dataset %s not supported\n"), zonepath_ds);
293 return (B_FALSE);
294 } else if (ret < 0) {
295 be_print_err(gettext("be_zone_supported: "
296 "zpool_iter failed: %s\n"),
297 libzfs_error_description(g_zfs));
298 return (B_FALSE);
299 }
300
301 /*
302 * Make sure the zonepath has a zone root container dataset
303 * underneath it.
304 */
305 if ((ret = be_make_container_ds(zonepath_ds, zone_container_ds,
306 sizeof (zone_container_ds))) != BE_SUCCESS) {
307 be_print_err(gettext("%s: failed to get BE container dataset "
308 "for %s\n"), __func__, zonepath_ds);
309 return (B_FALSE);
310 }
311
312 if (!zfs_dataset_exists(g_zfs, zone_container_ds,
313 ZFS_TYPE_FILESYSTEM)) {
314 be_print_err(gettext("be_zone_supported: "
315 "zonepath dataset (%s) does not have a zone root container "
316 "dataset, zone is not supported, skipping ...\n"),
317 zonepath_ds);
318 return (B_FALSE);
319 }
320
321 return (B_TRUE);
322 }
323
324 /*
325 * Function: be_get_supported_brandlist
326 * Desciption: This functions retuns a list of supported brands in
327 * a zoneBrandList_t object.
328 * Parameters:
329 * None
330 * Returns:
331 * Failure - NULL if no supported brands found.
332 * Success - pointer to zoneBrandList structure.
333 * Scope:
334 * Semi-private (library wide use only)
335 */
336 zoneBrandList_t *
be_get_supported_brandlist(void)337 be_get_supported_brandlist(void)
338 {
339 return (z_make_brand_list(BE_ZONE_SUPPORTED_BRANDS,
340 BE_ZONE_SUPPORTED_BRANDS_DELIM));
341 }
342
343 /*
344 * Function: be_zone_get_parent_uuid
345 * Description: This function gets the parentbe property of a zone root
346 * dataset, parsed it into internal uuid format, and returns
347 * it in the uuid_t reference pointer passed in.
348 * Parameters:
349 * root_ds - dataset name of a zone root dataset
350 * uu - pointer to a uuid_t to return the parentbe uuid in
351 * Returns:
352 * BE_SUCCESS - Success
353 * be_errno_t - Failure
354 * Scope:
355 * Private
356 */
357 int
be_zone_get_parent_uuid(const char * root_ds,uuid_t * uu)358 be_zone_get_parent_uuid(const char *root_ds, uuid_t *uu)
359 {
360 zfs_handle_t *zhp = NULL;
361 nvlist_t *userprops = NULL;
362 nvlist_t *propname = NULL;
363 char *uu_string = NULL;
364 int ret = BE_SUCCESS;
365
366 /* Get handle to zone root dataset */
367 if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
368 be_print_err(gettext("be_zone_get_parent_uuid: failed to "
369 "open zone root dataset (%s): %s\n"), root_ds,
370 libzfs_error_description(g_zfs));
371 return (zfs_err_to_be_err(g_zfs));
372 }
373
374 /* Get user properties for zone root dataset */
375 if ((userprops = zfs_get_user_props(zhp)) == NULL) {
376 be_print_err(gettext("be_zone_get_parent_uuid: "
377 "failed to get user properties for zone root "
378 "dataset (%s): %s\n"), root_ds,
379 libzfs_error_description(g_zfs));
380 ret = zfs_err_to_be_err(g_zfs);
381 goto done;
382 }
383
384 /* Get UUID string from zone's root dataset user properties */
385 if (nvlist_lookup_nvlist(userprops, BE_ZONE_PARENTBE_PROPERTY,
386 &propname) != 0 || nvlist_lookup_string(propname, ZPROP_VALUE,
387 &uu_string) != 0) {
388 be_print_err(gettext("be_zone_get_parent_uuid: failed to "
389 "get parent uuid property from zone root dataset user "
390 "properties.\n"));
391 ret = BE_ERR_ZONE_NO_PARENTBE;
392 goto done;
393 }
394
395 /* Parse the uuid string into internal format */
396 if (uuid_parse(uu_string, *uu) != 0 || uuid_is_null(*uu)) {
397 be_print_err(gettext("be_zone_get_parent_uuid: failed to "
398 "parse parentuuid\n"));
399 ret = BE_ERR_PARSE_UUID;
400 }
401
402 done:
403 ZFS_CLOSE(zhp);
404 return (ret);
405 }
406
407 /*
408 * Function: be_zone_set_parent_uuid
409 * Description: This function sets parentbe uuid into
410 * a zfs user property for a root zone dataset.
411 * Parameters:
412 * root_ds - Root zone dataset of the BE to set a uuid on.
413 * Return:
414 * be_errno_t - Failure
415 * BE_SUCCESS - Success
416 * Scope:
417 * Semi-private (library wide uses only)
418 */
419 int
be_zone_set_parent_uuid(char * root_ds,uuid_t uu)420 be_zone_set_parent_uuid(char *root_ds, uuid_t uu)
421 {
422 zfs_handle_t *zhp = NULL;
423 char uu_string[UUID_PRINTABLE_STRING_LENGTH];
424 int ret = BE_SUCCESS;
425
426 uuid_unparse(uu, uu_string);
427
428 /* Get handle to the root zone dataset. */
429 if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
430 be_print_err(gettext("be_zone_set_parent_uuid: failed to "
431 "open root zone dataset (%s): %s\n"), root_ds,
432 libzfs_error_description(g_zfs));
433 return (zfs_err_to_be_err(g_zfs));
434 }
435
436 /* Set parentbe uuid property for the root zone dataset */
437 if (zfs_prop_set(zhp, BE_ZONE_PARENTBE_PROPERTY, uu_string) != 0) {
438 be_print_err(gettext("be_zone_set_parent_uuid: failed to "
439 "set parentbe uuid property for root zone dataset: %s\n"),
440 libzfs_error_description(g_zfs));
441 ret = zfs_err_to_be_err(g_zfs);
442 }
443
444 ZFS_CLOSE(zhp);
445 return (ret);
446 }
447
448 /*
449 * Function: be_zone_compare_uuids
450 * Description: This function compare the parentbe uuid of the
451 * current running root zone dataset with the parentbe
452 * uuid of the given root zone dataset.
453 * Parameters:
454 * root_ds - Root zone dataset of the BE to compare.
455 * Return:
456 * B_TRUE - root dataset has right parentbe uuid
457 * B_FALSE - root dataset has wrong parentbe uuid
458 * Scope:
459 * Semi-private (library wide uses only)
460 */
461 boolean_t
be_zone_compare_uuids(char * root_ds)462 be_zone_compare_uuids(char *root_ds)
463 {
464 char *active_ds;
465 uuid_t parent_uuid = {0};
466 uuid_t cur_parent_uuid = {0};
467
468 /* Get parentbe uuid from given zone root dataset */
469 if ((be_zone_get_parent_uuid(root_ds,
470 &parent_uuid)) != BE_SUCCESS) {
471 be_print_err(gettext("be_zone_compare_uuids: failed to get "
472 "parentbe uuid from the given BE\n"));
473 return (B_FALSE);
474 }
475
476 /*
477 * Find current running zone root dataset and get it's parentbe
478 * uuid property.
479 */
480 if ((active_ds = be_get_ds_from_dir("/")) != NULL) {
481 if ((be_zone_get_parent_uuid(active_ds,
482 &cur_parent_uuid)) != BE_SUCCESS) {
483 be_print_err(gettext("be_zone_compare_uuids: failed "
484 "to get parentbe uuid from the current running zone "
485 "root dataset\n"));
486 return (B_FALSE);
487 }
488 } else {
489 be_print_err(gettext("be_zone_compare_uuids: zone root dataset "
490 "is not mounted\n"));
491 return (B_FALSE);
492 }
493
494 if (uuid_compare(parent_uuid, cur_parent_uuid) != 0) {
495 return (B_FALSE);
496 }
497
498 return (B_TRUE);
499 }
500
501 /* ******************************************************************** */
502 /* Private Functions */
503 /* ******************************************************************** */
504
505 /*
506 * Function: be_find_active_zone_root_callback
507 * Description: This function is used as a callback to iterate over all of
508 * a zone's root datasets, finding the one that is marked active
509 * for the parent BE specified in the data passed in. The name
510 * of the zone's active root dataset is returned in heap storage
511 * in the active_zone_root_data_t structure passed in, so the
512 * caller is responsible for freeing it.
513 * Parameters:
514 * zhp - zfs_handle_t pointer to current dataset being processed
515 * data - active_zone_root_data_t pointer
516 * Returns:
517 * 0 - Success
518 * >0 - Failure
519 * Scope:
520 * Private
521 */
522 static int
be_find_active_zone_root_callback(zfs_handle_t * zhp,void * data)523 be_find_active_zone_root_callback(zfs_handle_t *zhp, void *data)
524 {
525 active_zone_root_data_t *azr_data = data;
526 uuid_t parent_uuid = { 0 };
527 int iret = 0;
528 int ret = 0;
529
530 if ((iret = be_zone_get_parent_uuid(zfs_get_name(zhp), &parent_uuid))
531 != BE_SUCCESS) {
532 be_print_err(gettext("be_find_active_zone_root_callback: "
533 "skipping zone root dataset (%s): %s\n"),
534 zfs_get_name(zhp), be_err_to_str(iret));
535 goto done;
536 }
537
538 if (uuid_compare(azr_data->parent_uuid, parent_uuid) == 0) {
539 /*
540 * Found a zone root dataset belonging to the right parent,
541 * check if its active.
542 */
543 if (be_zone_get_active(zhp)) {
544 /*
545 * Found active zone root dataset, if its already
546 * set in the callback data, that means this
547 * is the second one we've found. Return error.
548 */
549 if (azr_data->zoneroot_ds != NULL) {
550 ret = BE_ERR_ZONE_MULTIPLE_ACTIVE;
551 goto done;
552 }
553
554 azr_data->zoneroot_ds = strdup(zfs_get_name(zhp));
555 if (azr_data->zoneroot_ds == NULL) {
556 ret = BE_ERR_NOMEM;
557 }
558 }
559 }
560
561 done:
562 ZFS_CLOSE(zhp);
563 return (ret);
564 }
565
566 /*
567 * Function: be_find_mounted_zone_root_callback
568 * Description: This function is used as a callback to iterate over all of
569 * a zone's root datasets, find the one that is currently
570 * mounted for the parent BE specified in the data passed in.
571 * The name of the zone's mounted root dataset is returned in
572 * heap storage the mounted_zone_data_t structure passed in,
573 * so the caller is responsible for freeing it.
574 * Parameters:
575 * zhp - zfs_handle_t pointer to the current dataset being
576 * processed
577 * data - mounted_zone_data_t pointer
578 * Returns:
579 * 0 - not mounted as zone's root
580 * 1 - this dataset is mounted as zone's root
581 * Scope:
582 * Private
583 */
584 static int
be_find_mounted_zone_root_callback(zfs_handle_t * zhp,void * data)585 be_find_mounted_zone_root_callback(zfs_handle_t *zhp, void *data)
586 {
587 mounted_zone_root_data_t *mzr_data = data;
588 char *mp = NULL;
589
590 if (zfs_is_mounted(zhp, &mp) && mp != NULL &&
591 strcmp(mp, mzr_data->zone_altroot) == 0) {
592 mzr_data->zoneroot_ds = strdup(zfs_get_name(zhp));
593 free(mp);
594 return (1);
595 }
596
597 free(mp);
598 return (0);
599 }
600
601 /*
602 * Function: be_zone_get_active
603 * Description: This function gets the active property of a zone root
604 * dataset, and returns true if active property is on.
605 * Parameters:
606 * zfs - zfs_handle_t pointer to zone root dataset to check
607 * Returns:
608 * B_TRUE - zone root dataset is active
609 * B_FALSE - zone root dataset is not active
610 * Scope:
611 * Private
612 */
613 static boolean_t
be_zone_get_active(zfs_handle_t * zhp)614 be_zone_get_active(zfs_handle_t *zhp)
615 {
616 nvlist_t *userprops = NULL;
617 nvlist_t *propname = NULL;
618 char *active_str = NULL;
619
620 /* Get user properties for the zone root dataset */
621 if ((userprops = zfs_get_user_props(zhp)) == NULL) {
622 be_print_err(gettext("be_zone_get_active: "
623 "failed to get user properties for zone root "
624 "dataset (%s): %s\n"), zfs_get_name(zhp),
625 libzfs_error_description(g_zfs));
626 return (B_FALSE);
627 }
628
629 /* Get active property from the zone root dataset user properties */
630 if (nvlist_lookup_nvlist(userprops, BE_ZONE_ACTIVE_PROPERTY, &propname)
631 != 0 || nvlist_lookup_string(propname, ZPROP_VALUE, &active_str)
632 != 0) {
633 return (B_FALSE);
634 }
635
636 if (strcmp(active_str, "on") == 0)
637 return (B_TRUE);
638
639 return (B_FALSE);
640 }
641