xref: /illumos-gate/usr/src/lib/libbe/common/be_zones.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
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
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
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
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
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 *
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
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
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
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
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
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
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