xref: /illumos-gate/usr/src/lib/libbrand/common/libbrand.c (revision cb6207858a9fcc2feaee22e626912fba281ac969)
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 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <assert.h>
30 #include <dirent.h>
31 #include <errno.h>
32 #include <fnmatch.h>
33 #include <signal.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <strings.h>
37 #include <synch.h>
38 #include <sys/brand.h>
39 #include <sys/fcntl.h>
40 #include <sys/param.h>
41 #include <sys/stat.h>
42 #include <sys/systeminfo.h>
43 #include <sys/types.h>
44 #include <thread.h>
45 #include <zone.h>
46 
47 #include <libbrand_impl.h>
48 #include <libbrand.h>
49 
50 #define	DTD_ELEM_BOOT		((const xmlChar *) "boot")
51 #define	DTD_ELEM_BRAND		((const xmlChar *) "brand")
52 #define	DTD_ELEM_COMMENT	((const xmlChar *) "comment")
53 #define	DTD_ELEM_DEVICE		((const xmlChar *) "device")
54 #define	DTD_ELEM_GLOBAL_MOUNT	((const xmlChar *) "global_mount")
55 #define	DTD_ELEM_HALT		((const xmlChar *) "halt")
56 #define	DTD_ELEM_INITNAME	((const xmlChar *) "initname")
57 #define	DTD_ELEM_INSTALL	((const xmlChar *) "install")
58 #define	DTD_ELEM_INSTALLOPTS	((const xmlChar *) "installopts")
59 #define	DTD_ELEM_LOGIN_CMD	((const xmlChar *) "login_cmd")
60 #define	DTD_ELEM_MODNAME	((const xmlChar *) "modname")
61 #define	DTD_ELEM_MOUNT		((const xmlChar *) "mount")
62 #define	DTD_ELEM_POSTCLONE	((const xmlChar *) "postclone")
63 #define	DTD_ELEM_PRIVILEGE	((const xmlChar *) "privilege")
64 #define	DTD_ELEM_SYMLINK	((const xmlChar *) "symlink")
65 #define	DTD_ELEM_VERIFY_CFG	((const xmlChar *) "verify_cfg")
66 #define	DTD_ELEM_VERIFY_ADM	((const xmlChar *) "verify_adm")
67 
68 #define	DTD_ATTR_ALLOWEXCL	((const xmlChar *) "allow-exclusive-ip")
69 #define	DTD_ATTR_ARCH		((const xmlChar *) "arch")
70 #define	DTD_ATTR_DIRECTORY	((const xmlChar *) "directory")
71 #define	DTD_ATTR_IPTYPE		((const xmlChar *) "ip-type")
72 #define	DTD_ATTR_MATCH		((const xmlChar *) "match")
73 #define	DTD_ATTR_MODE		((const xmlChar *) "mode")
74 #define	DTD_ATTR_NAME		((const xmlChar *) "name")
75 #define	DTD_ATTR_OPT		((const xmlChar *) "opt")
76 #define	DTD_ATTR_PATH		((const xmlChar *) "path")
77 #define	DTD_ATTR_SET		((const xmlChar *) "set")
78 #define	DTD_ATTR_SOURCE		((const xmlChar *) "source")
79 #define	DTD_ATTR_SPECIAL	((const xmlChar *) "special")
80 #define	DTD_ATTR_TARGET		((const xmlChar *) "target")
81 #define	DTD_ATTR_TYPE		((const xmlChar *) "type")
82 
83 #define	DTD_ENTITY_TRUE		"true"
84 
85 static volatile boolean_t	libbrand_initialized = B_FALSE;
86 static char			i_curr_arch[MAXNAMELEN];
87 static char			i_curr_zone[ZONENAME_MAX];
88 
89 /*ARGSUSED*/
90 static void
91 brand_error_func(void *ctx, const char *msg, ...)
92 {
93 	/*
94 	 * Ignore error messages from libxml
95 	 */
96 }
97 
98 static boolean_t
99 libbrand_initialize()
100 {
101 	static mutex_t initialize_lock = DEFAULTMUTEX;
102 
103 	(void) mutex_lock(&initialize_lock);
104 
105 	if (libbrand_initialized) {
106 		(void) mutex_unlock(&initialize_lock);
107 		return (B_TRUE);
108 	}
109 
110 	if (sysinfo(SI_ARCHITECTURE, i_curr_arch, sizeof (i_curr_arch)) < 0) {
111 		(void) mutex_unlock(&initialize_lock);
112 		return (B_FALSE);
113 	}
114 
115 	if (getzonenamebyid(getzoneid(), i_curr_zone,
116 	    sizeof (i_curr_zone)) < 0) {
117 		(void) mutex_unlock(&initialize_lock);
118 		return (B_FALSE);
119 	}
120 
121 	/*
122 	 * Note that here we're initializing per-process libxml2
123 	 * state.  By doing so we're implicitly assuming that
124 	 * no other code in this process is also trying to
125 	 * use libxml2.  But in most case we know this not to
126 	 * be true since we're almost always used in conjunction
127 	 * with libzonecfg, which also uses libxml2.  Lucky for
128 	 * us, libzonecfg initializes libxml2 to essentially
129 	 * the same defaults as we're using below.
130 	 */
131 	xmlLineNumbersDefault(1);
132 	xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS;
133 	xmlDoValidityCheckingDefaultValue = 1;
134 	(void) xmlKeepBlanksDefault(0);
135 	xmlGetWarningsDefaultValue = 0;
136 	xmlSetGenericErrorFunc(NULL, brand_error_func);
137 
138 	libbrand_initialized = B_TRUE;
139 	(void) mutex_unlock(&initialize_lock);
140 	return (B_TRUE);
141 }
142 
143 static const char *
144 get_curr_arch(void)
145 {
146 	if (!libbrand_initialize())
147 		return (NULL);
148 
149 	return (i_curr_arch);
150 }
151 
152 static const char *
153 get_curr_zone(void)
154 {
155 	if (!libbrand_initialize())
156 		return (NULL);
157 
158 	return (i_curr_zone);
159 }
160 
161 /*
162  * Internal function to open an XML file
163  *
164  * Returns the XML doc pointer, or NULL on failure.  It will validate the
165  * document, as well as removing any comments from the document structure.
166  */
167 static xmlDocPtr
168 open_xml_file(const char *file)
169 {
170 	xmlDocPtr doc;
171 	xmlValidCtxtPtr cvp;
172 	int valid;
173 
174 	if (!libbrand_initialize())
175 		return (NULL);
176 
177 	/*
178 	 * Parse the file
179 	 */
180 	if ((doc = xmlParseFile(file)) == NULL)
181 		return (NULL);
182 
183 	/*
184 	 * Validate the file
185 	 */
186 	if ((cvp = xmlNewValidCtxt()) == NULL) {
187 		xmlFreeDoc(doc);
188 		return (NULL);
189 	}
190 	cvp->error = brand_error_func;
191 	cvp->warning = brand_error_func;
192 	valid = xmlValidateDocument(cvp, doc);
193 	xmlFreeValidCtxt(cvp);
194 	if (valid == 0) {
195 		xmlFreeDoc(doc);
196 		return (NULL);
197 	}
198 
199 	return (doc);
200 }
201 /*
202  * Open a handle to the named brand.
203  *
204  * Returns a handle to the named brand, which is used for all subsequent brand
205  * interaction, or NULL if unable to open or initialize the brand.
206  */
207 brand_handle_t
208 brand_open(const char *name)
209 {
210 	struct brand_handle *bhp;
211 	char path[MAXPATHLEN];
212 	xmlNodePtr node;
213 	xmlChar *property;
214 	struct stat statbuf;
215 
216 	/*
217 	 * Make sure brand name isn't too long
218 	 */
219 	if (strlen(name) >= MAXNAMELEN)
220 		return (NULL);
221 
222 	/*
223 	 * Check that the brand exists
224 	 */
225 	(void) snprintf(path, sizeof (path), "%s/%s", BRAND_DIR, name);
226 
227 	if (stat(path, &statbuf) != 0)
228 		return (NULL);
229 
230 	/*
231 	 * Allocate brand handle
232 	 */
233 	if ((bhp = malloc(sizeof (struct brand_handle))) == NULL)
234 		return (NULL);
235 	bzero(bhp, sizeof (struct brand_handle));
236 
237 	(void) strcpy(bhp->bh_name, name);
238 
239 	/*
240 	 * Open the configuration file
241 	 */
242 	(void) snprintf(path, sizeof (path), "%s/%s/%s", BRAND_DIR, name,
243 	    BRAND_CONFIG);
244 	if ((bhp->bh_config = open_xml_file(path)) == NULL) {
245 		brand_close((brand_handle_t)bhp);
246 		return (NULL);
247 	}
248 
249 	/*
250 	 * Verify that the name of the brand matches the directory in which it
251 	 * is installed.
252 	 */
253 	if ((node = xmlDocGetRootElement(bhp->bh_config)) == NULL) {
254 		brand_close((brand_handle_t)bhp);
255 		return (NULL);
256 	}
257 
258 	if (xmlStrcmp(node->name, DTD_ELEM_BRAND) != 0) {
259 		brand_close((brand_handle_t)bhp);
260 		return (NULL);
261 	}
262 
263 	if ((property = xmlGetProp(node, DTD_ATTR_NAME)) == NULL) {
264 		brand_close((brand_handle_t)bhp);
265 		return (NULL);
266 	}
267 
268 	if (strcmp((char *)property, name) != 0) {
269 		xmlFree(property);
270 		brand_close((brand_handle_t)bhp);
271 		return (NULL);
272 	}
273 	xmlFree(property);
274 
275 	/*
276 	 * Open handle to platform configuration file.
277 	 */
278 	(void) snprintf(path, sizeof (path), "%s/%s/%s", BRAND_DIR, name,
279 	    BRAND_PLATFORM);
280 	if ((bhp->bh_platform = open_xml_file(path)) == NULL) {
281 		brand_close((brand_handle_t)bhp);
282 		return (NULL);
283 	}
284 
285 	return ((brand_handle_t)bhp);
286 }
287 
288 /*
289  * Closes the given brand handle
290  */
291 void
292 brand_close(brand_handle_t bh)
293 {
294 	struct brand_handle *bhp = (struct brand_handle *)bh;
295 	if (bhp->bh_platform != NULL)
296 		xmlFreeDoc(bhp->bh_platform);
297 	if (bhp->bh_config != NULL)
298 		xmlFreeDoc(bhp->bh_config);
299 	free(bhp);
300 }
301 
302 static int
303 i_substitute_tokens(const char *sbuf, char *dbuf, int dbuf_size,
304     const char *zonename, const char *zoneroot, const char *username,
305     const char *curr_zone, int argc, char **argv)
306 {
307 	int dst, src, i;
308 
309 	assert(argc >= 0);
310 	assert((argc == 0) || (argv != NULL));
311 
312 	/*
313 	 * Walk through the characters, substituting values as needed.
314 	 */
315 	dbuf[0] = '\0';
316 	dst = 0;
317 	for (src = 0; src < strlen((char *)sbuf) && dst < dbuf_size; src++) {
318 		if (sbuf[src] != '%') {
319 			dbuf[dst++] = sbuf[src];
320 			continue;
321 		}
322 
323 		switch (sbuf[++src]) {
324 		case '%':
325 			dst += strlcpy(dbuf + dst, "%", dbuf_size - dst);
326 			break;
327 		case 'R':
328 			if (zoneroot == NULL)
329 				break;
330 			dst += strlcpy(dbuf + dst, zoneroot, dbuf_size - dst);
331 			break;
332 		case 'u':
333 			if (username == NULL)
334 				break;
335 			dst += strlcpy(dbuf + dst, username, dbuf_size - dst);
336 			break;
337 		case 'Z':
338 			if (curr_zone == NULL)
339 				break;
340 			/* name of the zone we're running in */
341 			dst += strlcpy(dbuf + dst, curr_zone, dbuf_size - dst);
342 			break;
343 		case 'z':
344 			/* name of the zone we're operating on */
345 			if (zonename == NULL)
346 				break;
347 			dst += strlcpy(dbuf + dst, zonename, dbuf_size - dst);
348 			break;
349 		case '*':
350 			if (argv == NULL)
351 				break;
352 			for (i = 0; i < argc; i++)
353 				dst += snprintf(dbuf + dst, dbuf_size - dst,
354 				    " \"%s\"", argv[i]);
355 			break;
356 		}
357 	}
358 
359 	if (dst >= dbuf_size)
360 		return (-1);
361 
362 	dbuf[dst] = '\0';
363 	return (0);
364 }
365 
366 /*
367  * Retrieve the given tag from the brand.
368  * Perform the following substitutions as necessary:
369  *
370  *	%%	%
371  *	%u	Username
372  *	%z	Name of target zone
373  *	%Z	Name of current zone
374  *	%R	Root of zone
375  *	%*	Additional arguments (argc, argv)
376  *
377  * Returns 0 on success, -1 on failure.
378  */
379 static int
380 brand_get_value(struct brand_handle *bhp, const char *zonename,
381     const char *zoneroot, const char *username, const char *curr_zone,
382     char *buf, size_t len, int argc, char **argv, const xmlChar *tagname,
383     boolean_t substitute, boolean_t optional)
384 {
385 	xmlNodePtr node;
386 	xmlChar *content;
387 	int err = 0;
388 
389 	/*
390 	 * Retrieve the specified value from the XML doc
391 	 */
392 	if ((node = xmlDocGetRootElement(bhp->bh_config)) == NULL)
393 		return (-1);
394 
395 	if (xmlStrcmp(node->name, DTD_ELEM_BRAND) != 0)
396 		return (-1);
397 
398 	for (node = node->xmlChildrenNode; node != NULL;
399 	    node = node->next) {
400 		if (xmlStrcmp(node->name, tagname) == 0)
401 			break;
402 	}
403 
404 	if (node == NULL)
405 		return (-1);
406 
407 	if ((content = xmlNodeGetContent(node)) == NULL)
408 		return (-1);
409 
410 	if (strlen((char *)content) == 0) {
411 		/*
412 		 * If the entry in the config file is empty, check to see
413 		 * whether this is an optional field.  If so, we return the
414 		 * empty buffer.  If not, we return an error.
415 		 */
416 		if (optional) {
417 			buf[0] = '\0';
418 		} else {
419 			err = -1;
420 		}
421 	} else {
422 		/* Substitute token values as needed. */
423 		if (substitute) {
424 			if (i_substitute_tokens((char *)content, buf, len,
425 			    zonename, zoneroot, username, curr_zone,
426 			    argc, argv) != 0)
427 				err = -1;
428 		} else {
429 			if (strlcpy(buf, (char *)content, len) >= len)
430 				err = -1;
431 		}
432 	}
433 
434 	xmlFree(content);
435 
436 	return (err);
437 }
438 
439 int
440 brand_get_boot(brand_handle_t bh, const char *zonename,
441     const char *zoneroot, char *buf, size_t len, int argc, char **argv)
442 {
443 	struct brand_handle *bhp = (struct brand_handle *)bh;
444 	return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
445 	    buf, len, argc, argv, DTD_ELEM_BOOT, B_TRUE, B_TRUE));
446 }
447 
448 int
449 brand_get_brandname(brand_handle_t bh, char *buf, size_t len)
450 {
451 	struct brand_handle *bhp = (struct brand_handle *)bh;
452 	if (len <= strlen(bhp->bh_name))
453 		return (-1);
454 
455 	(void) strcpy(buf, bhp->bh_name);
456 
457 	return (0);
458 }
459 
460 int
461 brand_get_halt(brand_handle_t bh, const char *zonename,
462     const char *zoneroot, char *buf, size_t len, int argc, char **argv)
463 {
464 	struct brand_handle *bhp = (struct brand_handle *)bh;
465 	return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
466 	    buf, len, argc, argv, DTD_ELEM_HALT, B_TRUE, B_TRUE));
467 }
468 
469 int
470 brand_get_initname(brand_handle_t bh, char *buf, size_t len)
471 {
472 	struct brand_handle *bhp = (struct brand_handle *)bh;
473 	return (brand_get_value(bhp, NULL, NULL, NULL, NULL,
474 	    buf, len, 0, NULL, DTD_ELEM_INITNAME, B_FALSE, B_FALSE));
475 }
476 
477 int
478 brand_get_login_cmd(brand_handle_t bh, const char *username,
479     char *buf, size_t len)
480 {
481 	struct brand_handle *bhp = (struct brand_handle *)bh;
482 	const char *curr_zone = get_curr_zone();
483 	return (brand_get_value(bhp, NULL, NULL, username, curr_zone,
484 	    buf, len, 0, NULL, DTD_ELEM_LOGIN_CMD, B_TRUE, B_FALSE));
485 }
486 
487 int
488 brand_get_install(brand_handle_t bh, const char *zonename,
489     const char *zoneroot, char *buf, size_t len, int argc, char **argv)
490 {
491 	struct brand_handle *bhp = (struct brand_handle *)bh;
492 	return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
493 	    buf, len, argc, argv, DTD_ELEM_INSTALL, B_TRUE, B_FALSE));
494 }
495 
496 int
497 brand_get_installopts(brand_handle_t bh, char *buf, size_t len)
498 {
499 	struct brand_handle *bhp = (struct brand_handle *)bh;
500 	return (brand_get_value(bhp, NULL, NULL, NULL, NULL,
501 	    buf, len, 0, NULL, DTD_ELEM_INSTALLOPTS, B_FALSE, B_TRUE));
502 }
503 
504 int
505 brand_get_modname(brand_handle_t bh, char *buf, size_t len)
506 {
507 	struct brand_handle *bhp = (struct brand_handle *)bh;
508 	return (brand_get_value(bhp, NULL, NULL, NULL, NULL,
509 	    buf, len, 0, NULL, DTD_ELEM_MODNAME, B_FALSE, B_TRUE));
510 }
511 
512 int
513 brand_get_postclone(brand_handle_t bh, const char *zonename,
514     const char *zoneroot, char *buf, size_t len, int argc, char **argv)
515 {
516 	struct brand_handle *bhp = (struct brand_handle *)bh;
517 	return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
518 	    buf, len, argc, argv, DTD_ELEM_POSTCLONE, B_TRUE, B_TRUE));
519 }
520 
521 int
522 brand_get_verify_cfg(brand_handle_t bh, char *buf, size_t len)
523 {
524 	struct brand_handle *bhp = (struct brand_handle *)bh;
525 	return (brand_get_value(bhp, NULL, NULL, NULL, NULL,
526 	    buf, len, 0, NULL, DTD_ELEM_VERIFY_CFG, B_FALSE, B_TRUE));
527 }
528 
529 int
530 brand_get_verify_adm(brand_handle_t bh, const char *zonename,
531     const char *zoneroot, char *buf, size_t len, int argc, char **argv)
532 {
533 	struct brand_handle *bhp = (struct brand_handle *)bh;
534 	return (brand_get_value(bhp, zonename, zoneroot, NULL, NULL,
535 	    buf, len, argc, argv, DTD_ELEM_VERIFY_ADM, B_TRUE, B_TRUE));
536 }
537 
538 int
539 brand_is_native(brand_handle_t bh)
540 {
541 	struct brand_handle *bhp = (struct brand_handle *)bh;
542 	return ((strcmp(bhp->bh_name, NATIVE_BRAND_NAME) == 0) ? 1 : 0);
543 }
544 
545 boolean_t
546 brand_allow_exclusive_ip(brand_handle_t bh)
547 {
548 	struct brand_handle	*bhp = (struct brand_handle *)bh;
549 	xmlNodePtr		node;
550 	xmlChar			*allow_excl;
551 	boolean_t		ret;
552 
553 	assert(bhp != NULL);
554 
555 	if ((node = xmlDocGetRootElement(bhp->bh_platform)) == NULL)
556 		return (B_FALSE);
557 
558 	allow_excl = xmlGetProp(node, DTD_ATTR_ALLOWEXCL);
559 	if (allow_excl == NULL)
560 		return (B_FALSE);
561 
562 	/* Note: only return B_TRUE if it's "true" */
563 	if (strcmp((char *)allow_excl, DTD_ENTITY_TRUE) == 0)
564 		ret = B_TRUE;
565 	else
566 		ret = B_FALSE;
567 
568 	xmlFree(allow_excl);
569 
570 	return (ret);
571 }
572 
573 /*
574  * Iterate over brand privileges
575  *
576  * Walks the brand config, searching for <privilege> elements, calling the
577  * specified callback for each.  Returns 0 on success, or -1 on failure.
578  */
579 int
580 brand_config_iter_privilege(brand_handle_t bh,
581     int (*func)(void *, priv_iter_t *), void *data)
582 {
583 	struct brand_handle	*bhp = (struct brand_handle *)bh;
584 	xmlNodePtr		node;
585 	xmlChar			*name, *set, *iptype;
586 	priv_iter_t		priv_iter;
587 	int			ret;
588 
589 	if ((node = xmlDocGetRootElement(bhp->bh_config)) == NULL)
590 		return (-1);
591 
592 	for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
593 
594 		if (xmlStrcmp(node->name, DTD_ELEM_PRIVILEGE) != 0)
595 			continue;
596 
597 		name = xmlGetProp(node, DTD_ATTR_NAME);
598 		set = xmlGetProp(node, DTD_ATTR_SET);
599 		iptype = xmlGetProp(node, DTD_ATTR_IPTYPE);
600 
601 		if (name == NULL || set == NULL || iptype == NULL) {
602 			if (name != NULL)
603 				xmlFree(name);
604 			if (set != NULL)
605 				xmlFree(set);
606 			if (iptype != NULL)
607 				xmlFree(iptype);
608 			return (-1);
609 		}
610 
611 		priv_iter.pi_name = (char *)name;
612 		priv_iter.pi_set = (char *)set;
613 		priv_iter.pi_iptype = (char *)iptype;
614 
615 		ret = func(data, &priv_iter);
616 
617 		xmlFree(name);
618 		xmlFree(set);
619 		xmlFree(iptype);
620 
621 		if (ret != 0)
622 			return (-1);
623 	}
624 
625 	return (0);
626 }
627 
628 static int
629 i_brand_platform_iter_mounts(struct brand_handle *bhp, const char *zoneroot,
630     int (*func)(void *, const char *, const char *, const char *,
631     const char *), void *data, const xmlChar *mount_type)
632 {
633 	xmlNodePtr node;
634 	xmlChar *special, *dir, *type, *opt;
635 	char special_exp[MAXPATHLEN];
636 	char opt_exp[MAXPATHLEN];
637 	int ret;
638 
639 	if ((node = xmlDocGetRootElement(bhp->bh_platform)) == NULL)
640 		return (-1);
641 
642 	for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
643 
644 		if (xmlStrcmp(node->name, mount_type) != 0)
645 			continue;
646 
647 		special = xmlGetProp(node, DTD_ATTR_SPECIAL);
648 		dir = xmlGetProp(node, DTD_ATTR_DIRECTORY);
649 		type = xmlGetProp(node, DTD_ATTR_TYPE);
650 		opt = xmlGetProp(node, DTD_ATTR_OPT);
651 		if ((special == NULL) || (dir == NULL) || (type == NULL) ||
652 		    (opt == NULL)) {
653 			ret = -1;
654 			goto next;
655 		}
656 
657 		/* Substitute token values as needed. */
658 		if ((ret = i_substitute_tokens((char *)special,
659 		    special_exp, sizeof (special_exp),
660 		    NULL, zoneroot, NULL, NULL, 0, NULL)) != 0)
661 			goto next;
662 
663 		/* opt might not be defined */
664 		if (strlen((const char *)opt) == 0) {
665 			xmlFree(opt);
666 			opt = NULL;
667 		} else {
668 			if ((ret = i_substitute_tokens((char *)opt,
669 			    opt_exp, sizeof (opt_exp),
670 			    NULL, zoneroot, NULL, NULL, 0, NULL)) != 0)
671 				goto next;
672 		}
673 
674 		ret = func(data, (char *)special_exp, (char *)dir,
675 		    (char *)type, ((opt != NULL) ? opt_exp : NULL));
676 
677 next:
678 		if (special != NULL)
679 			xmlFree(special);
680 		if (dir != NULL)
681 			xmlFree(dir);
682 		if (type != NULL)
683 			xmlFree(type);
684 		if (opt != NULL)
685 			xmlFree(opt);
686 		if (ret != 0)
687 			return (-1);
688 	}
689 	return (0);
690 }
691 
692 
693 /*
694  * Iterate over global platform filesystems
695  *
696  * Walks the platform, searching for <global_mount> elements, calling the
697  * specified callback for each.  Returns 0 on success, or -1 on failure.
698  *
699  * Perform the following substitutions as necessary:
700  *
701  *	%R	Root of zone
702  */
703 int
704 brand_platform_iter_gmounts(brand_handle_t bh, const char *zoneroot,
705     int (*func)(void *, const char *, const char *, const char *,
706     const char *), void *data)
707 {
708 	struct brand_handle *bhp = (struct brand_handle *)bh;
709 	return (i_brand_platform_iter_mounts(bhp, zoneroot, func, data,
710 	    DTD_ELEM_GLOBAL_MOUNT));
711 }
712 
713 /*
714  * Iterate over non-global zone platform filesystems
715  *
716  * Walks the platform, searching for <mount> elements, calling the
717  * specified callback for each.  Returns 0 on success, or -1 on failure.
718  */
719 int
720 brand_platform_iter_mounts(brand_handle_t bh, int (*func)(void *,
721     const char *, const char *, const char *, const char *), void *data)
722 {
723 	struct brand_handle *bhp = (struct brand_handle *)bh;
724 	return (i_brand_platform_iter_mounts(bhp, NULL, func, data,
725 	    DTD_ELEM_MOUNT));
726 }
727 
728 /*
729  * Iterate over platform symlinks
730  *
731  * Walks the platform, searching for <symlink> elements, calling the
732  * specified callback for each.  Returns 0 on success, or -1 on failure.
733  */
734 int
735 brand_platform_iter_link(brand_handle_t bh,
736     int (*func)(void *, const char *, const char *), void *data)
737 {
738 	struct brand_handle *bhp = (struct brand_handle *)bh;
739 	xmlNodePtr node;
740 	xmlChar *source, *target;
741 	int ret;
742 
743 	if ((node = xmlDocGetRootElement(bhp->bh_platform)) == NULL)
744 		return (-1);
745 
746 	for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
747 
748 		if (xmlStrcmp(node->name, DTD_ELEM_SYMLINK) != 0)
749 			continue;
750 
751 		source = xmlGetProp(node, DTD_ATTR_SOURCE);
752 		target = xmlGetProp(node, DTD_ATTR_TARGET);
753 
754 		if (source == NULL || target == NULL) {
755 			if (source != NULL)
756 				xmlFree(source);
757 			if (target != NULL)
758 				xmlFree(target);
759 			return (-1);
760 		}
761 
762 		ret = func(data, (char *)source, (char *)target);
763 
764 		xmlFree(source);
765 		xmlFree(target);
766 
767 		if (ret != 0)
768 			return (-1);
769 	}
770 
771 	return (0);
772 }
773 
774 /*
775  * Iterate over platform devices
776  *
777  * Walks the platform, searching for <device> elements, calling the
778  * specified callback for each.  Returns 0 on success, or -1 on failure.
779  */
780 int
781 brand_platform_iter_devices(brand_handle_t bh, const char *zonename,
782     int (*func)(void *, const char *, const char *), void *data,
783     const char *curr_iptype)
784 {
785 	struct brand_handle	*bhp = (struct brand_handle *)bh;
786 	const char		*curr_arch = get_curr_arch();
787 	xmlNodePtr		node;
788 	xmlChar			*match, *name, *arch, *iptype;
789 	char			match_exp[MAXPATHLEN];
790 	boolean_t		err = B_FALSE;
791 	int			ret = 0;
792 
793 
794 	assert(bhp != NULL);
795 	assert(zonename != NULL);
796 	assert(func != NULL);
797 	assert(curr_iptype != NULL);
798 
799 	if ((node = xmlDocGetRootElement(bhp->bh_platform)) == NULL)
800 		return (-1);
801 
802 	for (node = node->xmlChildrenNode; node != NULL; node = node->next) {
803 
804 		if (xmlStrcmp(node->name, DTD_ELEM_DEVICE) != 0)
805 			continue;
806 
807 		match = xmlGetProp(node, DTD_ATTR_MATCH);
808 		name = xmlGetProp(node, DTD_ATTR_NAME);
809 		arch = xmlGetProp(node, DTD_ATTR_ARCH);
810 		iptype = xmlGetProp(node, DTD_ATTR_IPTYPE);
811 		if ((match == NULL) || (name == NULL) || (arch == NULL) ||
812 		    (iptype == NULL)) {
813 			err = B_TRUE;
814 			goto next;
815 		}
816 
817 		/* check if the arch matches */
818 		if ((strcmp((char *)arch, "all") != 0) &&
819 		    (strcmp((char *)arch, curr_arch) != 0))
820 			goto next;
821 
822 		/* check if the iptype matches */
823 		if ((strcmp((char *)iptype, "all") != 0) &&
824 		    (strcmp((char *)iptype, curr_iptype) != 0))
825 			goto next;
826 
827 		/* Substitute token values as needed. */
828 		if ((ret = i_substitute_tokens((char *)match,
829 		    match_exp, sizeof (match_exp),
830 		    zonename, NULL, NULL, NULL, 0, NULL)) != 0) {
831 			err = B_TRUE;
832 			goto next;
833 		}
834 
835 		/* name might not be defined */
836 		if (strlen((const char *)name) == 0) {
837 			xmlFree(name);
838 			name = NULL;
839 		}
840 
841 		/* invoke the callback */
842 		ret = func(data, (const char *)match_exp, (const char *)name);
843 
844 next:
845 		if (match != NULL)
846 			xmlFree(match);
847 		if (name != NULL)
848 			xmlFree(name);
849 		if (arch != NULL)
850 			xmlFree(arch);
851 		if (iptype != NULL)
852 			xmlFree(iptype);
853 		if (err)
854 			return (-1);
855 		if (ret != 0)
856 			return (-1);
857 	}
858 
859 	return (0);
860 }
861