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