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
52typedef struct active_zone_root_data {
53	uuid_t	parent_uuid;
54	char	*zoneroot_ds;
55} active_zone_root_data_t;
56
57typedef struct mounted_zone_root_data {
58	char	*zone_altroot;
59	char	*zoneroot_ds;
60} mounted_zone_root_data_t;
61
62/* Private function prototypes */
63static int be_find_active_zone_root_callback(zfs_handle_t *, void *);
64static int be_find_mounted_zone_root_callback(zfs_handle_t *, void *);
65static 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 */
85void
86be_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 */
110int
111be_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 }, NULL };
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
175done:
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 */
197int
198be_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
248done:
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 */
270boolean_t
271be_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 */
323zoneBrandList_t *
324be_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 */
344int
345be_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
389done:
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 */
406int
407be_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 */
448boolean_t
449be_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 */
509static int
510be_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
548done:
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 */
571static int
572be_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 */
600static boolean_t
601be_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