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