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