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