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