xref: /titanic_41/usr/src/lib/libzonecfg/common/libzonecfg.c (revision 505d05c73a6e56769f263d4803b22eddd168ee24)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 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 <errno.h>
30 #include <fnmatch.h>
31 #include <strings.h>
32 #include <unistd.h>
33 #include <sys/stat.h>
34 #include <assert.h>
35 #include <libgen.h>
36 #include <libintl.h>
37 #include <alloca.h>
38 #include <ctype.h>
39 #include <sys/mntio.h>
40 #include <sys/mnttab.h>
41 #include <sys/types.h>
42 
43 #include <arpa/inet.h>
44 #include <netdb.h>
45 
46 #include <priv.h>
47 
48 #include <libxml/xmlmemory.h>
49 #include <libxml/parser.h>
50 
51 #include <libdevinfo.h>
52 #include <uuid/uuid.h>
53 
54 #include <libzonecfg.h>
55 #include "zonecfg_impl.h"
56 
57 #define	_PATH_TMPFILE	"/zonecfg.XXXXXX"
58 
59 /* Hard-code the DTD element/attribute/entity names just once, here. */
60 #define	DTD_ELEM_ATTR		(const xmlChar *) "attr"
61 #define	DTD_ELEM_COMMENT	(const xmlChar *) "comment"
62 #define	DTD_ELEM_DEVICE		(const xmlChar *) "device"
63 #define	DTD_ELEM_FS		(const xmlChar *) "filesystem"
64 #define	DTD_ELEM_FSOPTION	(const xmlChar *) "fsoption"
65 #define	DTD_ELEM_IPD		(const xmlChar *) "inherited-pkg-dir"
66 #define	DTD_ELEM_NET		(const xmlChar *) "network"
67 #define	DTD_ELEM_RCTL		(const xmlChar *) "rctl"
68 #define	DTD_ELEM_RCTLVALUE	(const xmlChar *) "rctl-value"
69 #define	DTD_ELEM_ZONE		(const xmlChar *) "zone"
70 
71 #define	DTD_ATTR_ACTION		(const xmlChar *) "action"
72 #define	DTD_ATTR_ADDRESS	(const xmlChar *) "address"
73 #define	DTD_ATTR_AUTOBOOT	(const xmlChar *) "autoboot"
74 #define	DTD_ATTR_DIR		(const xmlChar *) "directory"
75 #define	DTD_ATTR_LIMIT		(const xmlChar *) "limit"
76 #define	DTD_ATTR_MATCH		(const xmlChar *) "match"
77 #define	DTD_ATTR_NAME		(const xmlChar *) "name"
78 #define	DTD_ATTR_PHYSICAL	(const xmlChar *) "physical"
79 #define	DTD_ATTR_POOL		(const xmlChar *) "pool"
80 #define	DTD_ATTR_PRIV		(const xmlChar *) "priv"
81 #define	DTD_ATTR_RAW		(const xmlChar *) "raw"
82 #define	DTD_ATTR_SPECIAL	(const xmlChar *) "special"
83 #define	DTD_ATTR_TYPE		(const xmlChar *) "type"
84 #define	DTD_ATTR_VALUE		(const xmlChar *) "value"
85 #define	DTD_ATTR_ZONEPATH	(const xmlChar *) "zonepath"
86 
87 #define	DTD_ENTITY_BOOLEAN	"boolean"
88 #define	DTD_ENTITY_DEVPATH	"devpath"
89 #define	DTD_ENTITY_DRIVER	"driver"
90 #define	DTD_ENTITY_DRVMIN	"drv_min"
91 #define	DTD_ENTITY_FALSE	"false"
92 #define	DTD_ENTITY_INT		"int"
93 #define	DTD_ENTITY_STRING	"string"
94 #define	DTD_ENTITY_TRUE		"true"
95 #define	DTD_ENTITY_UINT		"uint"
96 
97 #define	DTD_ENTITY_BOOL_LEN	6	/* "false" */
98 
99 struct zone_dochandle {
100 	char		*zone_dh_rootdir;
101 	xmlDocPtr	zone_dh_doc;
102 	xmlNodePtr	zone_dh_cur;
103 	xmlNodePtr	zone_dh_top;
104 	boolean_t	zone_dh_newzone;
105 	boolean_t	zone_dh_snapshot;
106 	char		zone_dh_delete_name[ZONENAME_MAX];
107 };
108 
109 char *zonecfg_root = "";
110 
111 /*
112  * For functions which return int, which is most of the functions herein,
113  * the return values should be from the Z_foo set defined in <libzonecfg.h>.
114  * In some instances, we take pains mapping some libc errno values to Z_foo
115  * values from this set.
116  */
117 
118 /*
119  * Set the root (/) path for all zonecfg configuration files.  This is a
120  * private interface used by Live Upgrade extensions to access zone
121  * configuration inside mounted alternate boot environments.
122  */
123 void
124 zonecfg_set_root(const char *rootpath)
125 {
126 	if (*zonecfg_root != '\0')
127 		free(zonecfg_root);
128 	if (rootpath == NULL || rootpath[0] == '\0' || rootpath[1] == '\0' ||
129 	    (zonecfg_root = strdup(rootpath)) == NULL)
130 		zonecfg_root = "";
131 }
132 
133 const char *
134 zonecfg_get_root(void)
135 {
136 	return (zonecfg_root);
137 }
138 
139 boolean_t
140 zonecfg_in_alt_root(void)
141 {
142 	return (*zonecfg_root != '\0');
143 }
144 
145 /*
146  * Callers of the _file_path() functions are expected to have the second
147  * parameter be a (char foo[MAXPATHLEN]).
148  */
149 
150 static boolean_t
151 config_file_path(const char *zonename, char *answer)
152 {
153 	return (snprintf(answer, MAXPATHLEN, "%s%s/%s.xml", zonecfg_root,
154 	    ZONE_CONFIG_ROOT, zonename) < MAXPATHLEN);
155 }
156 
157 static boolean_t
158 snap_file_path(const char *zonename, char *answer)
159 {
160 	return (snprintf(answer, MAXPATHLEN, "%s%s/%s.snapshot.xml",
161 	    zonecfg_root, ZONE_SNAPSHOT_ROOT, zonename) < MAXPATHLEN);
162 }
163 
164 /*ARGSUSED*/
165 static void
166 zonecfg_error_func(void *ctx, const char *msg, ...)
167 {
168 	/*
169 	 * This function does nothing by design.  Its purpose is to prevent
170 	 * libxml from dumping unwanted messages to stdout/stderr.
171 	 */
172 }
173 
174 zone_dochandle_t
175 zonecfg_init_handle(void)
176 {
177 	zone_dochandle_t handle = calloc(1, sizeof (struct zone_dochandle));
178 	if (handle == NULL) {
179 		errno = Z_NOMEM;
180 		return (NULL);
181 	}
182 
183 	/* generic libxml initialization */
184 	xmlLineNumbersDefault(1);
185 	xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS;
186 	xmlDoValidityCheckingDefaultValue = 1;
187 	(void) xmlKeepBlanksDefault(0);
188 	xmlGetWarningsDefaultValue = 0;
189 	xmlSetGenericErrorFunc(NULL, zonecfg_error_func);
190 
191 	return (handle);
192 }
193 
194 int
195 zonecfg_check_handle(zone_dochandle_t handle)
196 {
197 	if (handle == NULL || handle->zone_dh_doc == NULL)
198 		return (Z_BAD_HANDLE);
199 	return (Z_OK);
200 }
201 
202 void
203 zonecfg_fini_handle(zone_dochandle_t handle)
204 {
205 	if (zonecfg_check_handle(handle) == Z_OK)
206 		xmlFreeDoc(handle->zone_dh_doc);
207 	if (handle != NULL)
208 		free(handle);
209 }
210 
211 static int
212 zonecfg_destroy_impl(char *filename)
213 {
214 	if (unlink(filename) == -1) {
215 		if (errno == EACCES)
216 			return (Z_ACCES);
217 		if (errno == ENOENT)
218 			return (Z_NO_ZONE);
219 		return (Z_MISC_FS);
220 	}
221 	return (Z_OK);
222 }
223 
224 int
225 zonecfg_destroy(const char *zonename, boolean_t force)
226 {
227 	char path[MAXPATHLEN];
228 	struct zoneent ze;
229 	int err, state_err;
230 	zone_state_t state;
231 
232 	if (!config_file_path(zonename, path))
233 		return (Z_MISC_FS);
234 
235 	state_err = zone_get_state((char *)zonename, &state);
236 	err = access(path, W_OK);
237 
238 	/*
239 	 * If there is no file, and no index entry, reliably indicate that no
240 	 * such zone exists.
241 	 */
242 	if ((state_err == Z_NO_ZONE) && (err == -1) && (errno == ENOENT))
243 		return (Z_NO_ZONE);
244 
245 	/*
246 	 * Handle any other filesystem related errors (except if the XML
247 	 * file is missing, which we treat silently), unless we're forcing,
248 	 * in which case we plow on.
249 	 */
250 	if (err == -1 && errno != ENOENT) {
251 		if (errno == EACCES)
252 			return (Z_ACCES);
253 		else if (!force)
254 			return (Z_MISC_FS);
255 	}
256 
257 	if (state > ZONE_STATE_INSTALLED)
258 		return (Z_BAD_ZONE_STATE);
259 
260 	if (!force && state > ZONE_STATE_CONFIGURED)
261 		return (Z_BAD_ZONE_STATE);
262 
263 	/*
264 	 * Index deletion succeeds even if the entry doesn't exist.  So this
265 	 * will fail only if we've had some more severe problem.
266 	 */
267 	bzero(&ze, sizeof (ze));
268 	(void) strlcpy(ze.zone_name, zonename, sizeof (ze.zone_name));
269 	if ((err = putzoneent(&ze, PZE_REMOVE)) != Z_OK)
270 		if (!force)
271 			return (err);
272 
273 	err = zonecfg_destroy_impl(path);
274 
275 	/*
276 	 * Treat failure to find the XML file silently, since, well, it's
277 	 * gone, and with the index file cleaned up, we're done.
278 	 */
279 	if (err == Z_OK || err == Z_NO_ZONE)
280 		return (Z_OK);
281 	return (err);
282 }
283 
284 int
285 zonecfg_destroy_snapshot(const char *zonename)
286 {
287 	char path[MAXPATHLEN];
288 
289 	if (!snap_file_path(zonename, path))
290 		return (Z_MISC_FS);
291 	return (zonecfg_destroy_impl(path));
292 }
293 
294 static int
295 getroot(zone_dochandle_t handle, xmlNodePtr *root)
296 {
297 	if (zonecfg_check_handle(handle) == Z_BAD_HANDLE)
298 		return (Z_BAD_HANDLE);
299 
300 	*root = xmlDocGetRootElement(handle->zone_dh_doc);
301 
302 	if (*root == NULL)
303 		return (Z_EMPTY_DOCUMENT);
304 
305 	if (xmlStrcmp((*root)->name, DTD_ELEM_ZONE))
306 		return (Z_WRONG_DOC_TYPE);
307 
308 	return (Z_OK);
309 }
310 
311 static int
312 operation_prep(zone_dochandle_t handle)
313 {
314 	xmlNodePtr root;
315 	int err;
316 
317 	if ((err = getroot(handle, &root)) != 0)
318 		return (err);
319 
320 	handle->zone_dh_cur = root;
321 	handle->zone_dh_top = root;
322 	return (Z_OK);
323 }
324 
325 static int
326 getrootattr(zone_dochandle_t handle, const xmlChar *propname,
327     char *propval, size_t propsize)
328 {
329 	xmlNodePtr root;
330 	xmlChar *property;
331 	size_t srcsize;
332 	int err;
333 
334 	if ((err = getroot(handle, &root)) != 0)
335 		return (err);
336 
337 	if ((property = xmlGetProp(root, propname)) == NULL)
338 		return (Z_BAD_PROPERTY);
339 	srcsize = strlcpy(propval, (char *)property, propsize);
340 	xmlFree(property);
341 	if (srcsize >= propsize)
342 		return (Z_TOO_BIG);
343 	return (Z_OK);
344 }
345 
346 static int
347 setrootattr(zone_dochandle_t handle, const xmlChar *propname,
348     const char *propval)
349 {
350 	int err;
351 	xmlNodePtr root;
352 
353 	if (propval == NULL)
354 		return (Z_INVAL);
355 
356 	if ((err = getroot(handle, &root)) != Z_OK)
357 		return (err);
358 
359 	if (xmlSetProp(root, propname, (const xmlChar *) propval) == NULL)
360 		return (Z_INVAL);
361 	return (Z_OK);
362 }
363 
364 static void
365 addcomment(zone_dochandle_t handle, const char *comment)
366 {
367 	xmlNodePtr node;
368 	node = xmlNewComment((xmlChar *) comment);
369 
370 	if (node != NULL)
371 		(void) xmlAddPrevSibling(handle->zone_dh_top, node);
372 }
373 
374 static void
375 stripcomments(zone_dochandle_t handle)
376 {
377 	xmlDocPtr top;
378 	xmlNodePtr child, next;
379 
380 	top = handle->zone_dh_doc;
381 	for (child = top->xmlChildrenNode; child != NULL; child = next) {
382 		next = child->next;
383 		if (child->name == NULL)
384 			continue;
385 		if (xmlStrcmp(child->name, DTD_ELEM_COMMENT) == 0) {
386 			next = child->next;
387 			xmlUnlinkNode(child);
388 			xmlFreeNode(child);
389 		}
390 	}
391 }
392 
393 static int
394 zonecfg_get_handle_impl(const char *zonename, const char *filename,
395     zone_dochandle_t handle)
396 {
397 	xmlValidCtxtPtr cvp;
398 	struct stat statbuf;
399 	int valid;
400 
401 	if (zonename == NULL)
402 		return (Z_NO_ZONE);
403 	if ((handle->zone_dh_doc = xmlParseFile(filename)) == NULL) {
404 		/* distinguish file not found vs. found but not parsed */
405 		if (stat(filename, &statbuf) == 0)
406 			return (Z_INVALID_DOCUMENT);
407 		return (Z_NO_ZONE);
408 	}
409 	if ((cvp = xmlNewValidCtxt()) == NULL)
410 		return (Z_NOMEM);
411 	cvp->error = zonecfg_error_func;
412 	cvp->warning = zonecfg_error_func;
413 	valid = xmlValidateDocument(cvp, handle->zone_dh_doc);
414 	xmlFreeValidCtxt(cvp);
415 	if (valid == 0)
416 		return (Z_INVALID_DOCUMENT);
417 
418 	/* delete any comments such as inherited Sun copyright / ident str */
419 	stripcomments(handle);
420 	return (Z_OK);
421 }
422 
423 int
424 zonecfg_get_handle(const char *zonename, zone_dochandle_t handle)
425 {
426 	char path[MAXPATHLEN];
427 
428 	if (!config_file_path(zonename, path))
429 		return (Z_MISC_FS);
430 	handle->zone_dh_newzone = B_FALSE;
431 
432 	return (zonecfg_get_handle_impl(zonename, path, handle));
433 }
434 
435 int
436 zonecfg_get_snapshot_handle(const char *zonename, zone_dochandle_t handle)
437 {
438 	char path[MAXPATHLEN];
439 
440 	if (!snap_file_path(zonename, path))
441 		return (Z_MISC_FS);
442 	handle->zone_dh_newzone = B_FALSE;
443 	return (zonecfg_get_handle_impl(zonename, path, handle));
444 }
445 
446 int
447 zonecfg_get_template_handle(const char *template, const char *zonename,
448     zone_dochandle_t handle)
449 {
450 	char path[MAXPATHLEN];
451 	int err;
452 
453 	if (!config_file_path(template, path))
454 		return (Z_MISC_FS);
455 
456 	if ((err = zonecfg_get_handle_impl(template, path, handle)) != Z_OK)
457 		return (err);
458 	handle->zone_dh_newzone = B_TRUE;
459 	return (setrootattr(handle, DTD_ATTR_NAME, zonename));
460 }
461 
462 static boolean_t
463 is_renaming(zone_dochandle_t handle)
464 {
465 	if (handle->zone_dh_newzone)
466 		return (B_FALSE);
467 	if (strlen(handle->zone_dh_delete_name) > 0)
468 		return (B_TRUE);
469 	return (B_FALSE);
470 }
471 
472 static boolean_t
473 is_new(zone_dochandle_t handle)
474 {
475 	return (handle->zone_dh_newzone || handle->zone_dh_snapshot);
476 }
477 
478 static boolean_t
479 is_snapshot(zone_dochandle_t handle)
480 {
481 	return (handle->zone_dh_snapshot);
482 }
483 
484 /*
485  * It would be great to be able to use libc's ctype(3c) macros, but we
486  * can't, as they are locale sensitive, and it would break our limited thread
487  * safety if this routine had to change the app locale on the fly.
488  */
489 int
490 zonecfg_validate_zonename(const char *zone)
491 {
492 	int i;
493 
494 	if (strcmp(zone, GLOBAL_ZONENAME) == 0)
495 		return (Z_BOGUS_ZONE_NAME);
496 
497 	if (strlen(zone) >= ZONENAME_MAX)
498 		return (Z_BOGUS_ZONE_NAME);
499 
500 	if (!((zone[0] >= 'a' && zone[0] <= 'z') ||
501 	    (zone[0] >= 'A' && zone[0] <= 'Z') ||
502 	    (zone[0] >= '0' && zone[0] <= '9')))
503 		return (Z_BOGUS_ZONE_NAME);
504 
505 	for (i = 1; zone[i] != '\0'; i++) {
506 		if (!((zone[i] >= 'a' && zone[i] <= 'z') ||
507 		    (zone[i] >= 'A' && zone[i] <= 'Z') ||
508 		    (zone[i] >= '0' && zone[i] <= '9') ||
509 		    (zone[i] == '-') || (zone[i] == '_') || (zone[i] == '.')))
510 			return (Z_BOGUS_ZONE_NAME);
511 	}
512 
513 	return (Z_OK);
514 }
515 
516 /*
517  * Changing the zone name requires us to track both the old and new
518  * name of the zone until commit time.
519  */
520 int
521 zonecfg_get_name(zone_dochandle_t handle, char *name, size_t namesize)
522 {
523 	return (getrootattr(handle, DTD_ATTR_NAME, name, namesize));
524 }
525 
526 int
527 zonecfg_set_name(zone_dochandle_t handle, char *name)
528 {
529 	zone_state_t state;
530 	char curname[ZONENAME_MAX], old_delname[ZONENAME_MAX];
531 	int err;
532 
533 	if ((err = getrootattr(handle, DTD_ATTR_NAME, curname,
534 	    sizeof (curname))) != Z_OK)
535 		return (err);
536 
537 	if (strcmp(name, curname) == 0)
538 		return (Z_OK);
539 
540 	/*
541 	 * Switching zone names to one beginning with SUNW is not permitted.
542 	 */
543 	if (strncmp(name, "SUNW", 4) == 0)
544 		return (Z_BOGUS_ZONE_NAME);
545 
546 	if ((err = zonecfg_validate_zonename(name)) != Z_OK)
547 		return (err);
548 
549 	/*
550 	 * Setting the name back to the original name (effectively a revert of
551 	 * the name) is fine.  But if we carry on, we'll falsely identify the
552 	 * name as "in use," so special case here.
553 	 */
554 	if (strcmp(name, handle->zone_dh_delete_name) == 0) {
555 		err = setrootattr(handle, DTD_ATTR_NAME, name);
556 		handle->zone_dh_delete_name[0] = '\0';
557 		return (err);
558 	}
559 
560 	/* Check to see if new name chosen is already in use */
561 	if (zone_get_state(name, &state) != Z_NO_ZONE)
562 		return (Z_NAME_IN_USE);
563 
564 	/*
565 	 * If this isn't already "new" or in a renaming transition, then
566 	 * we're initiating a rename here; so stash the "delete name"
567 	 * (i.e. the name of the zone we'll be removing) for the rename.
568 	 */
569 	(void) strlcpy(old_delname, handle->zone_dh_delete_name,
570 	    sizeof (old_delname));
571 	if (!is_new(handle) && !is_renaming(handle)) {
572 		/*
573 		 * Name change is allowed only when the zone we're altering
574 		 * is not ready or running.
575 		 */
576 		err = zone_get_state(curname, &state);
577 		if (err == Z_OK) {
578 			if (state > ZONE_STATE_INSTALLED)
579 				return (Z_BAD_ZONE_STATE);
580 		} else if (err != Z_NO_ZONE) {
581 			return (err);
582 		}
583 
584 		(void) strlcpy(handle->zone_dh_delete_name, curname,
585 		    sizeof (handle->zone_dh_delete_name));
586 		assert(is_renaming(handle));
587 	} else if (is_renaming(handle)) {
588 		err = zone_get_state(handle->zone_dh_delete_name, &state);
589 		if (err == Z_OK) {
590 			if (state > ZONE_STATE_INSTALLED)
591 				return (Z_BAD_ZONE_STATE);
592 		} else if (err != Z_NO_ZONE) {
593 			return (err);
594 		}
595 	}
596 
597 	if ((err = setrootattr(handle, DTD_ATTR_NAME, name)) != Z_OK) {
598 		/*
599 		 * Restore the deletename to whatever it was at the
600 		 * top of the routine, since we've had a failure.
601 		 */
602 		(void) strlcpy(handle->zone_dh_delete_name, old_delname,
603 		    sizeof (handle->zone_dh_delete_name));
604 		return (err);
605 	}
606 
607 	return (Z_OK);
608 }
609 
610 int
611 zonecfg_get_zonepath(zone_dochandle_t handle, char *path, size_t pathsize)
612 {
613 	size_t len;
614 
615 	if ((len = strlcpy(path, zonecfg_root, pathsize)) >= pathsize)
616 		return (Z_TOO_BIG);
617 	return (getrootattr(handle, DTD_ATTR_ZONEPATH, path + len,
618 	    pathsize - len));
619 }
620 
621 int
622 zonecfg_set_zonepath(zone_dochandle_t handle, char *zonepath)
623 {
624 	return (setrootattr(handle, DTD_ATTR_ZONEPATH, zonepath));
625 }
626 
627 int
628 zonecfg_get_autoboot(zone_dochandle_t handle, boolean_t *autoboot)
629 {
630 	char autobootstr[DTD_ENTITY_BOOL_LEN];
631 	int ret;
632 
633 	if ((ret = getrootattr(handle, DTD_ATTR_AUTOBOOT, autobootstr,
634 	    sizeof (autobootstr))) != Z_OK)
635 		return (ret);
636 
637 	if (strcmp(autobootstr, DTD_ENTITY_TRUE) == 0)
638 		*autoboot = B_TRUE;
639 	else if (strcmp(autobootstr, DTD_ENTITY_FALSE) == 0)
640 		*autoboot = B_FALSE;
641 	else
642 		ret = Z_BAD_PROPERTY;
643 	return (ret);
644 }
645 
646 int
647 zonecfg_set_autoboot(zone_dochandle_t handle, boolean_t autoboot)
648 {
649 	return (setrootattr(handle, DTD_ATTR_AUTOBOOT,
650 	    autoboot ? DTD_ENTITY_TRUE : DTD_ENTITY_FALSE));
651 }
652 
653 int
654 zonecfg_get_pool(zone_dochandle_t handle, char *pool, size_t poolsize)
655 {
656 	return (getrootattr(handle, DTD_ATTR_POOL, pool, poolsize));
657 }
658 
659 int
660 zonecfg_set_pool(zone_dochandle_t handle, char *pool)
661 {
662 	return (setrootattr(handle, DTD_ATTR_POOL, pool));
663 }
664 
665 /*
666  * /etc/zones/index caches a vital piece of information which is also
667  * in the <zonename>.xml file: the path to the zone.  This is for performance,
668  * since we need to walk all zonepath's in order to be able to detect conflicts
669  * (see crosscheck_zonepaths() in the zoneadm command).
670  *
671  * An additional complexity is that when doing a rename, we'd like the entire
672  * index update operation (rename, and potential state changes) to be atomic.
673  * In general, the operation of this function should succeed or fail as
674  * a unit.
675  */
676 int
677 zonecfg_refresh_index_file(zone_dochandle_t handle)
678 {
679 	char name[ZONENAME_MAX], zonepath[MAXPATHLEN];
680 	struct zoneent ze;
681 	int err;
682 	int opcode;
683 	char *zn;
684 
685 	bzero(&ze, sizeof (ze));
686 	ze.zone_state = -1;	/* Preserve existing state in index */
687 
688 	if ((err = zonecfg_get_name(handle, name, sizeof (name))) != Z_OK)
689 		return (err);
690 	(void) strlcpy(ze.zone_name, name, sizeof (ze.zone_name));
691 
692 	if ((err = zonecfg_get_zonepath(handle, zonepath,
693 	    sizeof (zonepath))) != Z_OK)
694 		return (err);
695 	(void) strlcpy(ze.zone_path, zonepath, sizeof (ze.zone_path));
696 
697 	if (is_renaming(handle)) {
698 		opcode = PZE_MODIFY;
699 		(void) strlcpy(ze.zone_name, handle->zone_dh_delete_name,
700 		    sizeof (ze.zone_name));
701 		(void) strlcpy(ze.zone_newname, name, sizeof (ze.zone_newname));
702 	} else if (is_new(handle)) {
703 		FILE *cookie;
704 		/*
705 		 * Be tolerant of the zone already existing in the index file,
706 		 * since we might be forcibly overwriting an existing
707 		 * configuration with a new one (for example 'create -F'
708 		 * in zonecfg).
709 		 */
710 		opcode = PZE_ADD;
711 		cookie = setzoneent();
712 		while ((zn = getzoneent(cookie)) != NULL) {
713 			if (strcmp(zn, name) == 0) {
714 				opcode = PZE_MODIFY;
715 				free(zn);
716 				break;
717 			}
718 			free(zn);
719 		}
720 		endzoneent(cookie);
721 		ze.zone_state = ZONE_STATE_CONFIGURED;
722 	} else {
723 		opcode = PZE_MODIFY;
724 	}
725 
726 	if ((err = putzoneent(&ze, opcode)) != Z_OK)
727 		return (err);
728 
729 	return (Z_OK);
730 }
731 
732 /*
733  * The goal of this routine is to cause the index file update and the
734  * document save to happen as an atomic operation.  We do the document
735  * first, saving a backup copy using a hard link; if that succeeds, we go
736  * on to the index.  If that fails, we roll the document back into place.
737  *
738  * Strategy:
739  *
740  * New zone 'foo' configuration:
741  * 	Create tmpfile (zonecfg.xxxxxx)
742  * 	Write XML to tmpfile
743  * 	Rename tmpfile to xmlfile (zonecfg.xxxxxx -> foo.xml)
744  * 	Add entry to index file
745  * 	If it fails, delete foo.xml, leaving nothing behind.
746  *
747  * Save existing zone 'foo':
748  * 	Make backup of foo.xml -> .backup
749  * 	Create tmpfile (zonecfg.xxxxxx)
750  * 	Write XML to tmpfile
751  * 	Rename tmpfile to xmlfile (zonecfg.xxxxxx -> foo.xml)
752  * 	Modify index file as needed
753  * 	If it fails, recover from .backup -> foo.xml
754  *
755  * Rename 'foo' to 'bar':
756  * 	Create tmpfile (zonecfg.xxxxxx)
757  * 	Write XML to tmpfile
758  * 	Rename tmpfile to xmlfile (zonecfg.xxxxxx -> bar.xml)
759  * 	Add entry for 'bar' to index file, Remove entry for 'foo' (refresh)
760  * 	If it fails, delete bar.xml; foo.xml is left behind.
761  */
762 static int
763 zonecfg_save_impl(zone_dochandle_t handle, char *filename)
764 {
765 	char tmpfile[MAXPATHLEN];
766 	char bakdir[MAXPATHLEN], bakbase[MAXPATHLEN], bakfile[MAXPATHLEN];
767 	int tmpfd, err;
768 	xmlValidCtxt cvp = { NULL };
769 	boolean_t backup;
770 
771 	(void) strlcpy(tmpfile, filename, sizeof (tmpfile));
772 	(void) dirname(tmpfile);
773 	(void) strlcat(tmpfile, _PATH_TMPFILE, sizeof (tmpfile));
774 
775 	tmpfd = mkstemp(tmpfile);
776 	if (tmpfd == -1) {
777 		(void) unlink(tmpfile);
778 		return (Z_TEMP_FILE);
779 	}
780 	(void) close(tmpfd);
781 
782 	cvp.error = zonecfg_error_func;
783 	cvp.warning = zonecfg_error_func;
784 
785 	/*
786 	 * We do a final validation of the document-- but the library has
787 	 * malfunctioned if it fails to validate, so it's an assert.
788 	 */
789 	assert(xmlValidateDocument(&cvp, handle->zone_dh_doc) != 0);
790 
791 	if (xmlSaveFormatFile(tmpfile, handle->zone_dh_doc, 1) <= 0)
792 		goto err;
793 
794 	(void) chmod(tmpfile, 0644);
795 
796 	/*
797 	 * In the event we are doing a standard save, hard link a copy of the
798 	 * original file in .backup.<pid>.filename so we can restore it if
799 	 * something goes wrong.
800 	 */
801 	if (!is_new(handle) && !is_renaming(handle)) {
802 		backup = B_TRUE;
803 
804 		(void) strlcpy(bakdir, filename, sizeof (bakdir));
805 		(void) strlcpy(bakbase, filename, sizeof (bakbase));
806 		(void) snprintf(bakfile, sizeof (bakfile), "%s/.backup.%d.%s",
807 		    dirname(bakdir), getpid(), basename(bakbase));
808 
809 		if (link(filename, bakfile) == -1) {
810 			err = errno;
811 			(void) unlink(tmpfile);
812 			if (errno == EACCES)
813 				return (Z_ACCES);
814 			return (Z_MISC_FS);
815 		}
816 	}
817 
818 	/*
819 	 * Move the new document over top of the old.
820 	 * i.e.:   zonecfg.XXXXXX  ->  myzone.xml
821 	 */
822 	if (rename(tmpfile, filename) == -1) {
823 		err = errno;
824 		(void) unlink(tmpfile);
825 		if (backup)
826 			(void) unlink(bakfile);
827 		if (err == EACCES)
828 			return (Z_ACCES);
829 		return (Z_MISC_FS);
830 	}
831 
832 	/*
833 	 * If this is a snapshot, we're done-- don't add an index entry.
834 	 */
835 	if (is_snapshot(handle))
836 		return (Z_OK);
837 
838 	/* now update the index file to reflect whatever we just did */
839 	if ((err = zonecfg_refresh_index_file(handle)) != Z_OK) {
840 		if (backup) {
841 			/*
842 			 * Try to restore from our backup.
843 			 */
844 			(void) unlink(filename);
845 			(void) rename(bakfile, filename);
846 		} else {
847 			/*
848 			 * Either the zone is new, in which case we can delete
849 			 * new.xml, or we're doing a rename, so ditto.
850 			 */
851 			assert(is_new(handle) || is_renaming(handle));
852 			(void) unlink(filename);
853 		}
854 		return (Z_UPDATING_INDEX);
855 	}
856 
857 	if (backup)
858 		(void) unlink(bakfile);
859 
860 	return (Z_OK);
861 
862 err:
863 	(void) unlink(tmpfile);
864 	return (Z_SAVING_FILE);
865 }
866 
867 int
868 zonecfg_save(zone_dochandle_t handle)
869 {
870 	char zname[ZONENAME_MAX], path[MAXPATHLEN];
871 	char delpath[MAXPATHLEN];
872 	int err = Z_SAVING_FILE;
873 
874 	if (zonecfg_check_handle(handle) != Z_OK)
875 		return (Z_BAD_HANDLE);
876 
877 	/*
878 	 * We don't support saving snapshots at this time.
879 	 */
880 	if (handle->zone_dh_snapshot)
881 		return (Z_INVAL);
882 
883 	if ((err = zonecfg_get_name(handle, zname, sizeof (zname))) != Z_OK)
884 		return (err);
885 
886 	if (!config_file_path(zname, path))
887 		return (Z_MISC_FS);
888 
889 	addcomment(handle, "\n    DO NOT EDIT THIS "
890 	    "FILE.  Use zonecfg(1M) instead.\n");
891 
892 	err = zonecfg_save_impl(handle, path);
893 
894 	stripcomments(handle);
895 
896 	if (err != Z_OK)
897 		return (err);
898 
899 	handle->zone_dh_newzone = B_FALSE;
900 
901 	if (is_renaming(handle)) {
902 		if (config_file_path(handle->zone_dh_delete_name, delpath))
903 			(void) unlink(delpath);
904 		handle->zone_dh_delete_name[0] = '\0';
905 	}
906 
907 	return (Z_OK);
908 }
909 
910 /*
911  * Special case: if access(2) fails with ENOENT, then try again using
912  * ZONE_CONFIG_ROOT instead of config_file_path(zonename).  This is how we
913  * work around the case of a config file which has not been created yet:
914  * the user will need access to the directory so use that as a heuristic.
915  */
916 
917 int
918 zonecfg_access(const char *zonename, int amode)
919 {
920 	char path[MAXPATHLEN];
921 
922 	if (!config_file_path(zonename, path))
923 		return (Z_INVAL);
924 	if (access(path, amode) == 0)
925 		return (Z_OK);
926 	if (errno == ENOENT) {
927 		if (snprintf(path, sizeof (path), "%s%s", zonecfg_root,
928 		    ZONE_CONFIG_ROOT) >= sizeof (path))
929 			return (Z_INVAL);
930 		if (access(path, amode) == 0)
931 			return (Z_OK);
932 	}
933 	if (errno == EACCES)
934 		return (Z_ACCES);
935 	if (errno == EINVAL)
936 		return (Z_INVAL);
937 	return (Z_MISC_FS);
938 }
939 
940 int
941 zonecfg_create_snapshot(const char *zonename)
942 {
943 	zone_dochandle_t handle;
944 	char path[MAXPATHLEN], zonepath[MAXPATHLEN], rpath[MAXPATHLEN];
945 	int error = Z_OK, res;
946 
947 	if ((handle = zonecfg_init_handle()) == NULL) {
948 		return (Z_NOMEM);
949 	}
950 
951 	handle->zone_dh_newzone = B_TRUE;
952 	handle->zone_dh_snapshot = B_TRUE;
953 
954 	if ((error = zonecfg_get_handle(zonename, handle)) != Z_OK)
955 		goto out;
956 	if ((error = operation_prep(handle)) != Z_OK)
957 		goto out;
958 	error = zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath));
959 	if (error != Z_OK)
960 		goto out;
961 	if ((res = resolvepath(zonepath, rpath, sizeof (rpath))) == -1) {
962 		error = Z_RESOLVED_PATH;
963 		goto out;
964 	}
965 	/*
966 	 * If the resolved path is not the same as the original path, then
967 	 * save the resolved path in the snapshot, thus preventing any
968 	 * potential problems down the line when zoneadmd goes to unmount
969 	 * file systems and depends on initial string matches with resolved
970 	 * paths.
971 	 */
972 	rpath[res] = '\0';
973 	if (strcmp(zonepath, rpath) != 0) {
974 		if ((error = zonecfg_set_zonepath(handle, rpath)) != Z_OK)
975 			goto out;
976 	}
977 	if (snprintf(path, sizeof (path), "%s%s", zonecfg_root,
978 	    ZONE_SNAPSHOT_ROOT) >= sizeof (path)) {
979 		error = Z_MISC_FS;
980 		goto out;
981 	}
982 	if ((mkdir(path, S_IRWXU) == -1) && (errno != EEXIST)) {
983 		error = Z_MISC_FS;
984 		goto out;
985 	}
986 
987 	if (!snap_file_path(zonename, path)) {
988 		error = Z_MISC_FS;
989 		goto out;
990 	}
991 
992 	addcomment(handle, "\n    DO NOT EDIT THIS FILE.  "
993 	    "It is a snapshot of running zone state.\n");
994 
995 	error = zonecfg_save_impl(handle, path);
996 
997 	stripcomments(handle);
998 
999 out:
1000 	zonecfg_fini_handle(handle);
1001 	return (error);
1002 }
1003 
1004 static int
1005 newprop(xmlNodePtr node, const xmlChar *attrname, char *src)
1006 {
1007 	xmlAttrPtr newattr;
1008 
1009 	newattr = xmlNewProp(node, attrname, (xmlChar *)src);
1010 	if (newattr == NULL) {
1011 		xmlUnlinkNode(node);
1012 		xmlFreeNode(node);
1013 		return (Z_BAD_PROPERTY);
1014 	}
1015 	return (Z_OK);
1016 }
1017 
1018 static int
1019 zonecfg_add_filesystem_core(zone_dochandle_t handle, struct zone_fstab *tabptr)
1020 {
1021 	xmlNodePtr newnode, cur = handle->zone_dh_cur, options_node;
1022 	zone_fsopt_t *ptr;
1023 	int err;
1024 
1025 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_FS, NULL);
1026 	if ((err = newprop(newnode, DTD_ATTR_SPECIAL,
1027 	    tabptr->zone_fs_special)) != Z_OK)
1028 		return (err);
1029 	if (tabptr->zone_fs_raw[0] != '\0' &&
1030 	    (err = newprop(newnode, DTD_ATTR_RAW, tabptr->zone_fs_raw)) != Z_OK)
1031 		return (err);
1032 	if ((err = newprop(newnode, DTD_ATTR_DIR, tabptr->zone_fs_dir)) != Z_OK)
1033 		return (err);
1034 	if ((err = newprop(newnode, DTD_ATTR_TYPE,
1035 	    tabptr->zone_fs_type)) != Z_OK)
1036 		return (err);
1037 	if (tabptr->zone_fs_options != NULL) {
1038 		for (ptr = tabptr->zone_fs_options; ptr != NULL;
1039 		    ptr = ptr->zone_fsopt_next) {
1040 			options_node = xmlNewTextChild(newnode, NULL,
1041 			    DTD_ELEM_FSOPTION, NULL);
1042 			if ((err = newprop(options_node, DTD_ATTR_NAME,
1043 			    ptr->zone_fsopt_opt)) != Z_OK)
1044 				return (err);
1045 		}
1046 	}
1047 	return (Z_OK);
1048 }
1049 
1050 int
1051 zonecfg_add_filesystem(zone_dochandle_t handle, struct zone_fstab *tabptr)
1052 {
1053 	int err;
1054 
1055 	if (tabptr == NULL)
1056 		return (Z_INVAL);
1057 
1058 	if ((err = operation_prep(handle)) != Z_OK)
1059 		return (err);
1060 
1061 	if ((err = zonecfg_add_filesystem_core(handle, tabptr)) != Z_OK)
1062 		return (err);
1063 
1064 	return (Z_OK);
1065 }
1066 
1067 static int
1068 zonecfg_add_ipd_core(zone_dochandle_t handle, struct zone_fstab *tabptr)
1069 {
1070 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
1071 	int err;
1072 
1073 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_IPD, NULL);
1074 	if ((err = newprop(newnode, DTD_ATTR_DIR, tabptr->zone_fs_dir)) != Z_OK)
1075 		return (err);
1076 	return (Z_OK);
1077 }
1078 
1079 int
1080 zonecfg_add_ipd(zone_dochandle_t handle, struct zone_fstab *tabptr)
1081 {
1082 	int err;
1083 
1084 	if (tabptr == NULL)
1085 		return (Z_INVAL);
1086 
1087 	if ((err = operation_prep(handle)) != Z_OK)
1088 		return (err);
1089 
1090 	if ((err = zonecfg_add_ipd_core(handle, tabptr)) != Z_OK)
1091 		return (err);
1092 
1093 	return (Z_OK);
1094 }
1095 
1096 int
1097 zonecfg_add_fs_option(struct zone_fstab *tabptr, char *option)
1098 {
1099 	zone_fsopt_t *last, *old, *new;
1100 
1101 	last = tabptr->zone_fs_options;
1102 	for (old = last; old != NULL; old = old->zone_fsopt_next)
1103 		last = old;	/* walk to the end of the list */
1104 	new = (zone_fsopt_t *)malloc(sizeof (zone_fsopt_t));
1105 	if (new == NULL)
1106 		return (Z_NOMEM);
1107 	(void) strlcpy(new->zone_fsopt_opt, option,
1108 	    sizeof (new->zone_fsopt_opt));
1109 	new->zone_fsopt_next = NULL;
1110 	if (last == NULL)
1111 		tabptr->zone_fs_options = new;
1112 	else
1113 		last->zone_fsopt_next = new;
1114 	return (Z_OK);
1115 }
1116 
1117 int
1118 zonecfg_remove_fs_option(struct zone_fstab *tabptr, char *option)
1119 {
1120 	zone_fsopt_t *last, *this, *next;
1121 
1122 	last = tabptr->zone_fs_options;
1123 	for (this = last; this != NULL; this = this->zone_fsopt_next) {
1124 		if (strcmp(this->zone_fsopt_opt, option) == 0) {
1125 			next = this->zone_fsopt_next;
1126 			if (this == tabptr->zone_fs_options)
1127 				tabptr->zone_fs_options = next;
1128 			else
1129 				last->zone_fsopt_next = next;
1130 			free(this);
1131 			return (Z_OK);
1132 		} else
1133 			last = this;
1134 	}
1135 	return (Z_NO_PROPERTY_ID);
1136 }
1137 
1138 void
1139 zonecfg_free_fs_option_list(zone_fsopt_t *list)
1140 {
1141 	zone_fsopt_t *this, *next;
1142 
1143 	for (this = list; this != NULL; this = next) {
1144 		next = this->zone_fsopt_next;
1145 		free(this);
1146 	}
1147 }
1148 
1149 void
1150 zonecfg_free_rctl_value_list(struct zone_rctlvaltab *valtab)
1151 {
1152 	if (valtab == NULL)
1153 		return;
1154 	zonecfg_free_rctl_value_list(valtab->zone_rctlval_next);
1155 	free(valtab);
1156 }
1157 
1158 static boolean_t
1159 match_prop(xmlNodePtr cur, const xmlChar *attr, char *user_prop)
1160 {
1161 	xmlChar *gotten_prop;
1162 	int prop_result;
1163 
1164 	gotten_prop = xmlGetProp(cur, attr);
1165 	if (gotten_prop == NULL)	/* shouldn't happen */
1166 		return (B_FALSE);
1167 	prop_result = xmlStrcmp(gotten_prop, (const xmlChar *) user_prop);
1168 	xmlFree(gotten_prop);
1169 	return ((prop_result == 0));
1170 }
1171 
1172 static int
1173 zonecfg_delete_filesystem_core(zone_dochandle_t handle,
1174     struct zone_fstab *tabptr)
1175 {
1176 	xmlNodePtr cur = handle->zone_dh_cur;
1177 	boolean_t dir_match, spec_match, raw_match, type_match;
1178 
1179 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1180 		if (xmlStrcmp(cur->name, DTD_ELEM_FS))
1181 			continue;
1182 		dir_match = match_prop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir);
1183 		spec_match = match_prop(cur, DTD_ATTR_SPECIAL,
1184 		    tabptr->zone_fs_special);
1185 		raw_match = match_prop(cur, DTD_ATTR_RAW,
1186 		    tabptr->zone_fs_raw);
1187 		type_match = match_prop(cur, DTD_ATTR_TYPE,
1188 		    tabptr->zone_fs_type);
1189 		if (dir_match && spec_match && raw_match && type_match) {
1190 			xmlUnlinkNode(cur);
1191 			xmlFreeNode(cur);
1192 			return (Z_OK);
1193 		}
1194 	}
1195 	return (Z_NO_RESOURCE_ID);
1196 }
1197 
1198 int
1199 zonecfg_delete_filesystem(zone_dochandle_t handle, struct zone_fstab *tabptr)
1200 {
1201 	int err;
1202 
1203 	if (tabptr == NULL)
1204 		return (Z_INVAL);
1205 
1206 	if ((err = operation_prep(handle)) != Z_OK)
1207 		return (err);
1208 
1209 	if ((err = zonecfg_delete_filesystem_core(handle, tabptr)) != Z_OK)
1210 		return (err);
1211 
1212 	return (Z_OK);
1213 }
1214 
1215 int
1216 zonecfg_modify_filesystem(
1217 	zone_dochandle_t handle,
1218 	struct zone_fstab *oldtabptr,
1219 	struct zone_fstab *newtabptr)
1220 {
1221 	int err;
1222 
1223 	if (oldtabptr == NULL || newtabptr == NULL)
1224 		return (Z_INVAL);
1225 
1226 	if ((err = operation_prep(handle)) != Z_OK)
1227 		return (err);
1228 
1229 	if ((err = zonecfg_delete_filesystem_core(handle, oldtabptr)) != Z_OK)
1230 		return (err);
1231 
1232 	if ((err = zonecfg_add_filesystem_core(handle, newtabptr)) != Z_OK)
1233 		return (err);
1234 
1235 	return (Z_OK);
1236 }
1237 
1238 static int
1239 zonecfg_delete_ipd_core(zone_dochandle_t handle, struct zone_fstab *tabptr)
1240 {
1241 	xmlNodePtr cur = handle->zone_dh_cur;
1242 
1243 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1244 		if (xmlStrcmp(cur->name, DTD_ELEM_IPD))
1245 			continue;
1246 		if (match_prop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir)) {
1247 			xmlUnlinkNode(cur);
1248 			xmlFreeNode(cur);
1249 			return (Z_OK);
1250 		}
1251 	}
1252 	return (Z_NO_RESOURCE_ID);
1253 }
1254 
1255 int
1256 zonecfg_delete_ipd(zone_dochandle_t handle, struct zone_fstab *tabptr)
1257 {
1258 	int err;
1259 
1260 	if (tabptr == NULL)
1261 		return (Z_INVAL);
1262 
1263 	if ((err = operation_prep(handle)) != Z_OK)
1264 		return (err);
1265 
1266 	if ((err = zonecfg_delete_ipd_core(handle, tabptr)) != Z_OK)
1267 		return (err);
1268 
1269 	return (Z_OK);
1270 }
1271 
1272 int
1273 zonecfg_modify_ipd(zone_dochandle_t handle, struct zone_fstab *oldtabptr,
1274     struct zone_fstab *newtabptr)
1275 {
1276 	int err;
1277 
1278 	if (oldtabptr == NULL || newtabptr == NULL)
1279 		return (Z_INVAL);
1280 
1281 	if ((err = operation_prep(handle)) != Z_OK)
1282 		return (err);
1283 
1284 	if ((err = zonecfg_delete_ipd_core(handle, oldtabptr)) != Z_OK)
1285 		return (err);
1286 
1287 	if ((err = zonecfg_add_ipd_core(handle, newtabptr)) != Z_OK)
1288 		return (err);
1289 
1290 	return (Z_OK);
1291 }
1292 
1293 static int
1294 fetchprop(xmlNodePtr cur, const xmlChar *propname, char *dst, size_t dstsize)
1295 {
1296 	xmlChar *property;
1297 	size_t srcsize;
1298 
1299 	if ((property = xmlGetProp(cur, propname)) == NULL)
1300 		return (Z_BAD_PROPERTY);
1301 	srcsize = strlcpy(dst, (char *)property, dstsize);
1302 	xmlFree(property);
1303 	if (srcsize >= dstsize)
1304 		return (Z_TOO_BIG);
1305 	return (Z_OK);
1306 }
1307 
1308 int
1309 zonecfg_lookup_filesystem(
1310 	zone_dochandle_t handle,
1311 	struct zone_fstab *tabptr)
1312 {
1313 	xmlNodePtr cur, options, firstmatch;
1314 	int err;
1315 	char dirname[MAXPATHLEN], special[MAXPATHLEN], raw[MAXPATHLEN];
1316 	char type[FSTYPSZ];
1317 	char options_str[MAX_MNTOPT_STR];
1318 
1319 	if (tabptr == NULL)
1320 		return (Z_INVAL);
1321 
1322 	if ((err = operation_prep(handle)) != Z_OK)
1323 		return (err);
1324 
1325 	/*
1326 	 * Walk the list of children looking for matches on any properties
1327 	 * specified in the fstab parameter.  If more than one resource
1328 	 * matches, we return Z_INSUFFICIENT_SPEC; if none match, we return
1329 	 * Z_NO_RESOURCE_ID.
1330 	 */
1331 	cur = handle->zone_dh_cur;
1332 	firstmatch = NULL;
1333 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1334 		if (xmlStrcmp(cur->name, DTD_ELEM_FS))
1335 			continue;
1336 		if (strlen(tabptr->zone_fs_dir) > 0) {
1337 			if ((fetchprop(cur, DTD_ATTR_DIR, dirname,
1338 			    sizeof (dirname)) == Z_OK) &&
1339 			    (strcmp(tabptr->zone_fs_dir, dirname) == 0)) {
1340 				if (firstmatch == NULL)
1341 					firstmatch = cur;
1342 				else
1343 					return (Z_INSUFFICIENT_SPEC);
1344 			}
1345 		}
1346 		if (strlen(tabptr->zone_fs_special) > 0) {
1347 			if ((fetchprop(cur, DTD_ATTR_SPECIAL, special,
1348 			    sizeof (special)) == Z_OK)) {
1349 				if (strcmp(tabptr->zone_fs_special,
1350 				    special) == 0) {
1351 					if (firstmatch == NULL)
1352 						firstmatch = cur;
1353 					else if (firstmatch != cur)
1354 						return (Z_INSUFFICIENT_SPEC);
1355 				} else {
1356 					/*
1357 					 * If another property matched but this
1358 					 * one doesn't then reset firstmatch.
1359 					 */
1360 					if (firstmatch == cur)
1361 						firstmatch = NULL;
1362 				}
1363 			}
1364 		}
1365 		if (strlen(tabptr->zone_fs_raw) > 0) {
1366 			if ((fetchprop(cur, DTD_ATTR_RAW, raw,
1367 			    sizeof (raw)) == Z_OK)) {
1368 				if (strcmp(tabptr->zone_fs_raw, raw) == 0) {
1369 					if (firstmatch == NULL)
1370 						firstmatch = cur;
1371 					else if (firstmatch != cur)
1372 						return (Z_INSUFFICIENT_SPEC);
1373 				} else {
1374 					/*
1375 					 * If another property matched but this
1376 					 * one doesn't then reset firstmatch.
1377 					 */
1378 					if (firstmatch == cur)
1379 						firstmatch = NULL;
1380 				}
1381 			}
1382 		}
1383 		if (strlen(tabptr->zone_fs_type) > 0) {
1384 			if ((fetchprop(cur, DTD_ATTR_TYPE, type,
1385 			    sizeof (type)) == Z_OK)) {
1386 				if (strcmp(tabptr->zone_fs_type, type) == 0) {
1387 					if (firstmatch == NULL)
1388 						firstmatch = cur;
1389 					else if (firstmatch != cur)
1390 						return (Z_INSUFFICIENT_SPEC);
1391 				} else {
1392 					/*
1393 					 * If another property matched but this
1394 					 * one doesn't then reset firstmatch.
1395 					 */
1396 					if (firstmatch == cur)
1397 						firstmatch = NULL;
1398 				}
1399 			}
1400 		}
1401 	}
1402 
1403 	if (firstmatch == NULL)
1404 		return (Z_NO_RESOURCE_ID);
1405 
1406 	cur = firstmatch;
1407 
1408 	if ((err = fetchprop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir,
1409 	    sizeof (tabptr->zone_fs_dir))) != Z_OK)
1410 		return (err);
1411 
1412 	if ((err = fetchprop(cur, DTD_ATTR_SPECIAL, tabptr->zone_fs_special,
1413 	    sizeof (tabptr->zone_fs_special))) != Z_OK)
1414 		return (err);
1415 
1416 	if ((err = fetchprop(cur, DTD_ATTR_RAW, tabptr->zone_fs_raw,
1417 	    sizeof (tabptr->zone_fs_raw))) != Z_OK)
1418 		return (err);
1419 
1420 	if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_fs_type,
1421 	    sizeof (tabptr->zone_fs_type))) != Z_OK)
1422 		return (err);
1423 
1424 	/* options are optional */
1425 	tabptr->zone_fs_options = NULL;
1426 	for (options = cur->xmlChildrenNode; options != NULL;
1427 	    options = options->next) {
1428 		if ((fetchprop(options, DTD_ATTR_NAME, options_str,
1429 		    sizeof (options_str)) != Z_OK))
1430 			break;
1431 		if (zonecfg_add_fs_option(tabptr, options_str) != Z_OK)
1432 			break;
1433 	}
1434 	return (Z_OK);
1435 }
1436 
1437 int
1438 zonecfg_lookup_ipd(zone_dochandle_t handle, struct zone_fstab *tabptr)
1439 {
1440 	xmlNodePtr cur, match;
1441 	int err;
1442 	char dirname[MAXPATHLEN];
1443 
1444 	if (tabptr == NULL)
1445 		return (Z_INVAL);
1446 
1447 	if ((err = operation_prep(handle)) != Z_OK)
1448 		return (err);
1449 
1450 	/*
1451 	 * General algorithm:
1452 	 * Walk the list of children looking for matches on any properties
1453 	 * specified in the fstab parameter.  If more than one resource
1454 	 * matches, we return Z_INSUFFICIENT_SPEC; if none match, we return
1455 	 * Z_NO_RESOURCE_ID.
1456 	 */
1457 	cur = handle->zone_dh_cur;
1458 	match = NULL;
1459 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1460 		if (xmlStrcmp(cur->name, DTD_ELEM_IPD))
1461 			continue;
1462 		if (strlen(tabptr->zone_fs_dir) > 0) {
1463 			if ((fetchprop(cur, DTD_ATTR_DIR, dirname,
1464 			    sizeof (dirname)) == Z_OK) &&
1465 			    (strcmp(tabptr->zone_fs_dir, dirname) == 0)) {
1466 				if (match == NULL)
1467 					match = cur;
1468 				else
1469 					return (Z_INSUFFICIENT_SPEC);
1470 			}
1471 		}
1472 	}
1473 
1474 	if (match == NULL)
1475 		return (Z_NO_RESOURCE_ID);
1476 
1477 	cur = match;
1478 
1479 	if ((err = fetchprop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir,
1480 	    sizeof (tabptr->zone_fs_dir))) != Z_OK)
1481 		return (err);
1482 
1483 	return (Z_OK);
1484 }
1485 
1486 /*
1487  * Compare two IP addresses in string form.  Allow for the possibility that
1488  * one might have "/<prefix-length>" at the end: allow a match on just the
1489  * IP address (or host name) part.
1490  */
1491 
1492 boolean_t
1493 zonecfg_same_net_address(char *a1, char *a2)
1494 {
1495 	char *slashp, *slashp1, *slashp2;
1496 	int result;
1497 
1498 	if (strcmp(a1, a2) == 0)
1499 		return (B_TRUE);
1500 
1501 	/*
1502 	 * If neither has a slash or both do, they need to match to be
1503 	 * considered the same, but they did not match above, so fail.
1504 	 */
1505 	slashp1 = strchr(a1, '/');
1506 	slashp2 = strchr(a2, '/');
1507 	if ((slashp1 == NULL && slashp2 == NULL) ||
1508 	    (slashp1 != NULL && slashp2 != NULL))
1509 		return (B_FALSE);
1510 
1511 	/*
1512 	 * Only one had a slash: pick that one, zero out the slash, compare
1513 	 * the "address only" strings, restore the slash, and return the
1514 	 * result of the comparison.
1515 	 */
1516 	slashp = (slashp1 == NULL) ? slashp2 : slashp1;
1517 	*slashp = '\0';
1518 	result = strcmp(a1, a2);
1519 	*slashp = '/';
1520 	return ((result == 0));
1521 }
1522 
1523 int
1524 zonecfg_valid_net_address(char *address, struct lifreq *lifr)
1525 {
1526 	struct sockaddr_in *sin4;
1527 	struct sockaddr_in6 *sin6;
1528 	struct addrinfo hints, *result;
1529 	char *slashp = strchr(address, '/');
1530 
1531 	bzero(lifr, sizeof (struct lifreq));
1532 	sin4 = (struct sockaddr_in *)&lifr->lifr_addr;
1533 	sin6 = (struct sockaddr_in6 *)&lifr->lifr_addr;
1534 	if (slashp != NULL)
1535 		*slashp = '\0';
1536 	if (inet_pton(AF_INET, address, &sin4->sin_addr) == 1) {
1537 		sin4->sin_family = AF_INET;
1538 	} else if (inet_pton(AF_INET6, address, &sin6->sin6_addr) == 1) {
1539 		if (slashp == NULL)
1540 			return (Z_IPV6_ADDR_PREFIX_LEN);
1541 		sin6->sin6_family = AF_INET6;
1542 	} else {
1543 		/* "address" may be a host name */
1544 		(void) memset(&hints, 0, sizeof (hints));
1545 		hints.ai_family = PF_INET;
1546 		if (getaddrinfo(address, NULL, &hints, &result) != 0)
1547 			return (Z_BOGUS_ADDRESS);
1548 		sin4->sin_family = result->ai_family;
1549 
1550 		(void) memcpy(&sin4->sin_addr,
1551 		    /* LINTED E_BAD_PTR_CAST_ALIGN */
1552 		    &((struct sockaddr_in *)result->ai_addr)->sin_addr,
1553 		    sizeof (struct in_addr));
1554 
1555 		freeaddrinfo(result);
1556 	}
1557 	return (Z_OK);
1558 }
1559 
1560 int
1561 zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
1562 {
1563 	xmlNodePtr cur, firstmatch;
1564 	int err;
1565 	char address[INET6_ADDRSTRLEN], physical[LIFNAMSIZ];
1566 
1567 	if (tabptr == NULL)
1568 		return (Z_INVAL);
1569 
1570 	if ((err = operation_prep(handle)) != Z_OK)
1571 		return (err);
1572 
1573 	cur = handle->zone_dh_cur;
1574 	firstmatch = NULL;
1575 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1576 		if (xmlStrcmp(cur->name, DTD_ELEM_NET))
1577 			continue;
1578 		if (strlen(tabptr->zone_nwif_physical) > 0) {
1579 			if ((fetchprop(cur, DTD_ATTR_PHYSICAL, physical,
1580 			    sizeof (physical)) == Z_OK) &&
1581 			    (strcmp(tabptr->zone_nwif_physical,
1582 			    physical) == 0)) {
1583 				if (firstmatch == NULL)
1584 					firstmatch = cur;
1585 				else
1586 					return (Z_INSUFFICIENT_SPEC);
1587 			}
1588 		}
1589 		if (strlen(tabptr->zone_nwif_address) > 0) {
1590 			if ((fetchprop(cur, DTD_ATTR_ADDRESS, address,
1591 			    sizeof (address)) == Z_OK)) {
1592 				if (zonecfg_same_net_address(
1593 				    tabptr->zone_nwif_address, address)) {
1594 					if (firstmatch == NULL)
1595 						firstmatch = cur;
1596 					else if (firstmatch != cur)
1597 						return (Z_INSUFFICIENT_SPEC);
1598 				} else {
1599 					/*
1600 					 * If another property matched but this
1601 					 * one doesn't then reset firstmatch.
1602 					 */
1603 					if (firstmatch == cur)
1604 						firstmatch = NULL;
1605 				}
1606 			}
1607 		}
1608 	}
1609 	if (firstmatch == NULL)
1610 		return (Z_NO_RESOURCE_ID);
1611 
1612 	cur = firstmatch;
1613 
1614 	if ((err = fetchprop(cur, DTD_ATTR_PHYSICAL, tabptr->zone_nwif_physical,
1615 	    sizeof (tabptr->zone_nwif_physical))) != Z_OK)
1616 		return (err);
1617 
1618 	if ((err = fetchprop(cur, DTD_ATTR_ADDRESS, tabptr->zone_nwif_address,
1619 	    sizeof (tabptr->zone_nwif_address))) != Z_OK)
1620 		return (err);
1621 
1622 	return (Z_OK);
1623 }
1624 
1625 static int
1626 zonecfg_add_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
1627 {
1628 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
1629 	int err;
1630 
1631 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_NET, NULL);
1632 	if ((err = newprop(newnode, DTD_ATTR_ADDRESS,
1633 	    tabptr->zone_nwif_address)) != Z_OK)
1634 		return (err);
1635 	if ((err = newprop(newnode, DTD_ATTR_PHYSICAL,
1636 	    tabptr->zone_nwif_physical)) != Z_OK)
1637 		return (err);
1638 	return (Z_OK);
1639 }
1640 
1641 int
1642 zonecfg_add_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
1643 {
1644 	int err;
1645 
1646 	if (tabptr == NULL)
1647 		return (Z_INVAL);
1648 
1649 	if ((err = operation_prep(handle)) != Z_OK)
1650 		return (err);
1651 
1652 	if ((err = zonecfg_add_nwif_core(handle, tabptr)) != Z_OK)
1653 		return (err);
1654 
1655 	return (Z_OK);
1656 }
1657 
1658 static int
1659 zonecfg_delete_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
1660 {
1661 	xmlNodePtr cur = handle->zone_dh_cur;
1662 	boolean_t addr_match, phys_match;
1663 
1664 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1665 		if (xmlStrcmp(cur->name, DTD_ELEM_NET))
1666 			continue;
1667 
1668 		addr_match = match_prop(cur, DTD_ATTR_ADDRESS,
1669 		    tabptr->zone_nwif_address);
1670 		phys_match = match_prop(cur, DTD_ATTR_PHYSICAL,
1671 		    tabptr->zone_nwif_physical);
1672 
1673 		if (addr_match && phys_match) {
1674 			xmlUnlinkNode(cur);
1675 			xmlFreeNode(cur);
1676 			return (Z_OK);
1677 		}
1678 	}
1679 	return (Z_NO_RESOURCE_ID);
1680 }
1681 
1682 int
1683 zonecfg_delete_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
1684 {
1685 	int err;
1686 
1687 	if (tabptr == NULL)
1688 		return (Z_INVAL);
1689 
1690 	if ((err = operation_prep(handle)) != Z_OK)
1691 		return (err);
1692 
1693 	if ((err = zonecfg_delete_nwif_core(handle, tabptr)) != Z_OK)
1694 		return (err);
1695 
1696 	return (Z_OK);
1697 }
1698 
1699 int
1700 zonecfg_modify_nwif(
1701 	zone_dochandle_t handle,
1702 	struct zone_nwiftab *oldtabptr,
1703 	struct zone_nwiftab *newtabptr)
1704 {
1705 	int err;
1706 
1707 	if (oldtabptr == NULL || newtabptr == NULL)
1708 		return (Z_INVAL);
1709 
1710 	if ((err = operation_prep(handle)) != Z_OK)
1711 		return (err);
1712 
1713 	if ((err = zonecfg_delete_nwif_core(handle, oldtabptr)) != Z_OK)
1714 		return (err);
1715 
1716 	if ((err = zonecfg_add_nwif_core(handle, newtabptr)) != Z_OK)
1717 		return (err);
1718 
1719 	return (Z_OK);
1720 }
1721 
1722 int
1723 zonecfg_lookup_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
1724 {
1725 	xmlNodePtr cur, firstmatch;
1726 	int err;
1727 	char match[MAXPATHLEN];
1728 
1729 	if (tabptr == NULL)
1730 		return (Z_INVAL);
1731 
1732 	if ((err = operation_prep(handle)) != Z_OK)
1733 		return (err);
1734 
1735 	cur = handle->zone_dh_cur;
1736 	firstmatch = NULL;
1737 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1738 		if (xmlStrcmp(cur->name, DTD_ELEM_DEVICE))
1739 			continue;
1740 		if (strlen(tabptr->zone_dev_match) == 0)
1741 			continue;
1742 
1743 		if ((fetchprop(cur, DTD_ATTR_MATCH, match,
1744 		    sizeof (match)) == Z_OK)) {
1745 			if (strcmp(tabptr->zone_dev_match,
1746 			    match) == 0) {
1747 				if (firstmatch == NULL)
1748 					firstmatch = cur;
1749 				else if (firstmatch != cur)
1750 					return (Z_INSUFFICIENT_SPEC);
1751 			} else {
1752 				/*
1753 				 * If another property matched but this
1754 				 * one doesn't then reset firstmatch.
1755 				 */
1756 				if (firstmatch == cur)
1757 					firstmatch = NULL;
1758 			}
1759 		}
1760 	}
1761 	if (firstmatch == NULL)
1762 		return (Z_NO_RESOURCE_ID);
1763 
1764 	cur = firstmatch;
1765 
1766 	if ((err = fetchprop(cur, DTD_ATTR_MATCH, tabptr->zone_dev_match,
1767 	    sizeof (tabptr->zone_dev_match))) != Z_OK)
1768 		return (err);
1769 
1770 	return (Z_OK);
1771 }
1772 
1773 static int
1774 zonecfg_add_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr)
1775 {
1776 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
1777 	int err;
1778 
1779 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DEVICE, NULL);
1780 
1781 	if ((err = newprop(newnode, DTD_ATTR_MATCH,
1782 	    tabptr->zone_dev_match)) != Z_OK)
1783 		return (err);
1784 
1785 	return (Z_OK);
1786 }
1787 
1788 int
1789 zonecfg_add_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
1790 {
1791 	int err;
1792 
1793 	if (tabptr == NULL)
1794 		return (Z_INVAL);
1795 
1796 	if ((err = operation_prep(handle)) != Z_OK)
1797 		return (err);
1798 
1799 	if ((err = zonecfg_add_dev_core(handle, tabptr)) != Z_OK)
1800 		return (err);
1801 
1802 	return (Z_OK);
1803 }
1804 
1805 static int
1806 zonecfg_delete_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr)
1807 {
1808 	xmlNodePtr cur = handle->zone_dh_cur;
1809 	int match_match;
1810 
1811 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1812 		if (xmlStrcmp(cur->name, DTD_ELEM_DEVICE))
1813 			continue;
1814 
1815 		match_match = match_prop(cur, DTD_ATTR_MATCH,
1816 		    tabptr->zone_dev_match);
1817 
1818 		if (match_match) {
1819 			xmlUnlinkNode(cur);
1820 			xmlFreeNode(cur);
1821 			return (Z_OK);
1822 		}
1823 	}
1824 	return (Z_NO_RESOURCE_ID);
1825 }
1826 
1827 int
1828 zonecfg_delete_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
1829 {
1830 	int err;
1831 
1832 	if (tabptr == NULL)
1833 		return (Z_INVAL);
1834 
1835 	if ((err = operation_prep(handle)) != Z_OK)
1836 		return (err);
1837 
1838 	if ((err = zonecfg_delete_dev_core(handle, tabptr)) != Z_OK)
1839 		return (err);
1840 
1841 	return (Z_OK);
1842 }
1843 
1844 int
1845 zonecfg_modify_dev(
1846 	zone_dochandle_t handle,
1847 	struct zone_devtab *oldtabptr,
1848 	struct zone_devtab *newtabptr)
1849 {
1850 	int err;
1851 
1852 	if (oldtabptr == NULL || newtabptr == NULL)
1853 		return (Z_INVAL);
1854 
1855 	if ((err = operation_prep(handle)) != Z_OK)
1856 		return (err);
1857 
1858 	if ((err = zonecfg_delete_dev_core(handle, oldtabptr)) != Z_OK)
1859 		return (err);
1860 
1861 	if ((err = zonecfg_add_dev_core(handle, newtabptr)) != Z_OK)
1862 		return (err);
1863 
1864 	return (Z_OK);
1865 }
1866 
1867 /*
1868  * This is the set of devices which must be present in every zone.  Users
1869  * can augment this list with additional device rules in their zone
1870  * configuration, but at present cannot remove any of the this set of
1871  * standard devices.  All matching is done by /dev pathname (the "/dev"
1872  * part is implicit.  Try to keep rules which match a large number of
1873  * devices (like the pts rule) first.
1874  */
1875 static const char *standard_devs[] = {
1876 	"pts/*",
1877 	"ptmx",
1878 	"random",
1879 	"urandom",
1880 	"poll",
1881 	"pool",
1882 	"kstat",
1883 	"zero",
1884 	"null",
1885 	"crypto",
1886 	"cryptoadm",
1887 	"ticots",
1888 	"ticotsord",
1889 	"ticlts",
1890 	"lo0",
1891 	"lo1",
1892 	"lo2",
1893 	"lo3",
1894 	"sad/user",
1895 	"tty",
1896 	"logindmux",
1897 	"log",
1898 	"conslog",
1899 	"arp",
1900 	"tcp",
1901 	"tcp6",
1902 	"udp",
1903 	"udp6",
1904 	"sysevent",
1905 #ifdef __sparc
1906 	"openprom",
1907 #endif
1908 	"cpu/self/cpuid",
1909 	"dtrace/helper",
1910 	NULL
1911 };
1912 
1913 /*
1914  * This function finds everything mounted under a zone's rootpath.
1915  * This returns the number of mounts under rootpath, or -1 on error.
1916  * callback is called once per mount found with the first argument
1917  * pointing to the  mount point.
1918  *
1919  * If the callback function returns non-zero zonecfg_find_mounts
1920  * aborts with an error.
1921  */
1922 
1923 int
1924 zonecfg_find_mounts(char *rootpath, int (*callback)(const char *, void *),
1925     void *priv) {
1926 	FILE *mnttab;
1927 	struct mnttab m;
1928 	size_t l;
1929 	int rv = 0;
1930 
1931 	assert(rootpath != NULL);
1932 
1933 	l = strlen(rootpath);
1934 
1935 	mnttab = fopen("/etc/mnttab", "r");
1936 
1937 	if (mnttab == NULL)
1938 		return (-1);
1939 
1940 	if (ioctl(fileno(mnttab), MNTIOC_SHOWHIDDEN, NULL) < 0)  {
1941 		rv = -1;
1942 		goto out;
1943 	}
1944 
1945 	while (!getmntent(mnttab, &m)) {
1946 		if ((strncmp(rootpath, m.mnt_mountp, l) == 0) &&
1947 		    (m.mnt_mountp[l] == '/')) {
1948 			rv++;
1949 			if (callback == NULL)
1950 				continue;
1951 			if (callback(m.mnt_mountp, priv)) {
1952 				rv = -1;
1953 				goto out;
1954 
1955 			}
1956 		}
1957 	}
1958 
1959 out:
1960 	(void) fclose(mnttab);
1961 	return (rv);
1962 }
1963 
1964 /*
1965  * This routine is used to determine if a given device should appear in the
1966  * zone represented by 'handle'.  First it consults the list of "standard"
1967  * zone devices.  Then it scans the user-supplied device entries.
1968  */
1969 int
1970 zonecfg_match_dev(zone_dochandle_t handle, char *devpath,
1971     struct zone_devtab *out_match)
1972 {
1973 	int err;
1974 	boolean_t found = B_FALSE;
1975 	char match[MAXPATHLEN];
1976 	const char **stdmatch;
1977 	xmlNodePtr cur;
1978 
1979 	if (handle == NULL || devpath == NULL)
1980 		return (Z_INVAL);
1981 
1982 	/*
1983 	 * Check the "standard" devices which we require to be present.
1984 	 */
1985 	for (stdmatch = &standard_devs[0]; *stdmatch != NULL; stdmatch++) {
1986 		/*
1987 		 * fnmatch gives us simple but powerful shell-style matching.
1988 		 */
1989 		if (fnmatch(*stdmatch, devpath, FNM_PATHNAME) == 0) {
1990 			if (!out_match)
1991 				return (Z_OK);
1992 			(void) snprintf(out_match->zone_dev_match,
1993 			    sizeof (out_match->zone_dev_match),
1994 			    "/dev/%s", *stdmatch);
1995 			return (Z_OK);
1996 		}
1997 	}
1998 
1999 	/*
2000 	 * We got no hits in the set of standard devices.  On to the user
2001 	 * supplied ones.
2002 	 */
2003 	if ((err = operation_prep(handle)) != Z_OK) {
2004 		handle->zone_dh_cur = NULL;
2005 		return (err);
2006 	}
2007 
2008 	cur = handle->zone_dh_cur;
2009 	cur = cur->xmlChildrenNode;
2010 	if (cur == NULL)
2011 		return (Z_NO_ENTRY);
2012 	handle->zone_dh_cur = cur;
2013 
2014 	for (; cur != NULL; cur = cur->next) {
2015 		char *m;
2016 		if (xmlStrcmp(cur->name, DTD_ELEM_DEVICE) != 0)
2017 			continue;
2018 		if ((err = fetchprop(cur, DTD_ATTR_MATCH, match,
2019 		    sizeof (match))) != Z_OK) {
2020 			handle->zone_dh_cur = handle->zone_dh_top;
2021 			return (err);
2022 		}
2023 		m = match;
2024 		/*
2025 		 * fnmatch gives us simple but powerful shell-style matching;
2026 		 * but first, we need to strip out /dev/ from the matching rule.
2027 		 */
2028 		if (strncmp(m, "/dev/", 5) == 0)
2029 			m += 5;
2030 
2031 		if (fnmatch(m, devpath, FNM_PATHNAME) == 0) {
2032 			found = B_TRUE;
2033 			break;
2034 		}
2035 	}
2036 
2037 	if (!found)
2038 		return (Z_NO_ENTRY);
2039 
2040 	if (!out_match)
2041 		return (Z_OK);
2042 
2043 	(void) strlcpy(out_match->zone_dev_match, match,
2044 	    sizeof (out_match->zone_dev_match));
2045 	return (Z_OK);
2046 }
2047 
2048 int
2049 zonecfg_lookup_attr(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2050 {
2051 	xmlNodePtr cur, firstmatch;
2052 	int err;
2053 	char name[MAXNAMELEN], type[MAXNAMELEN], value[MAXNAMELEN];
2054 
2055 	if (tabptr == NULL)
2056 		return (Z_INVAL);
2057 
2058 	if ((err = operation_prep(handle)) != Z_OK)
2059 		return (err);
2060 
2061 	cur = handle->zone_dh_cur;
2062 	firstmatch = NULL;
2063 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2064 		if (xmlStrcmp(cur->name, DTD_ELEM_ATTR))
2065 			continue;
2066 		if (strlen(tabptr->zone_attr_name) > 0) {
2067 			if ((fetchprop(cur, DTD_ATTR_NAME, name,
2068 			    sizeof (name)) == Z_OK) &&
2069 			    (strcmp(tabptr->zone_attr_name, name) == 0)) {
2070 				if (firstmatch == NULL)
2071 					firstmatch = cur;
2072 				else
2073 					return (Z_INSUFFICIENT_SPEC);
2074 			}
2075 		}
2076 		if (strlen(tabptr->zone_attr_type) > 0) {
2077 			if ((fetchprop(cur, DTD_ATTR_TYPE, type,
2078 			    sizeof (type)) == Z_OK)) {
2079 				if (strcmp(tabptr->zone_attr_type, type) == 0) {
2080 					if (firstmatch == NULL)
2081 						firstmatch = cur;
2082 					else if (firstmatch != cur)
2083 						return (Z_INSUFFICIENT_SPEC);
2084 				} else {
2085 					/*
2086 					 * If another property matched but this
2087 					 * one doesn't then reset firstmatch.
2088 					 */
2089 					if (firstmatch == cur)
2090 						firstmatch = NULL;
2091 				}
2092 			}
2093 		}
2094 		if (strlen(tabptr->zone_attr_value) > 0) {
2095 			if ((fetchprop(cur, DTD_ATTR_VALUE, value,
2096 			    sizeof (value)) == Z_OK)) {
2097 				if (strcmp(tabptr->zone_attr_value, value) ==
2098 				    0) {
2099 					if (firstmatch == NULL)
2100 						firstmatch = cur;
2101 					else if (firstmatch != cur)
2102 						return (Z_INSUFFICIENT_SPEC);
2103 				} else {
2104 					/*
2105 					 * If another property matched but this
2106 					 * one doesn't then reset firstmatch.
2107 					 */
2108 					if (firstmatch == cur)
2109 						firstmatch = NULL;
2110 				}
2111 			}
2112 		}
2113 	}
2114 	if (firstmatch == NULL)
2115 		return (Z_NO_RESOURCE_ID);
2116 
2117 	cur = firstmatch;
2118 
2119 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_attr_name,
2120 	    sizeof (tabptr->zone_attr_name))) != Z_OK)
2121 		return (err);
2122 
2123 	if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_attr_type,
2124 	    sizeof (tabptr->zone_attr_type))) != Z_OK)
2125 		return (err);
2126 
2127 	if ((err = fetchprop(cur, DTD_ATTR_VALUE, tabptr->zone_attr_value,
2128 	    sizeof (tabptr->zone_attr_value))) != Z_OK)
2129 		return (err);
2130 
2131 	return (Z_OK);
2132 }
2133 
2134 static int
2135 zonecfg_add_attr_core(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2136 {
2137 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
2138 	int err;
2139 
2140 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_ATTR, NULL);
2141 	err = newprop(newnode, DTD_ATTR_NAME, tabptr->zone_attr_name);
2142 	if (err != Z_OK)
2143 		return (err);
2144 	err = newprop(newnode, DTD_ATTR_TYPE, tabptr->zone_attr_type);
2145 	if (err != Z_OK)
2146 		return (err);
2147 	err = newprop(newnode, DTD_ATTR_VALUE, tabptr->zone_attr_value);
2148 	if (err != Z_OK)
2149 		return (err);
2150 	return (Z_OK);
2151 }
2152 
2153 int
2154 zonecfg_add_attr(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2155 {
2156 	int err;
2157 
2158 	if (tabptr == NULL)
2159 		return (Z_INVAL);
2160 
2161 	if ((err = operation_prep(handle)) != Z_OK)
2162 		return (err);
2163 
2164 	if ((err = zonecfg_add_attr_core(handle, tabptr)) != Z_OK)
2165 		return (err);
2166 
2167 	return (Z_OK);
2168 }
2169 
2170 static int
2171 zonecfg_delete_attr_core(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2172 {
2173 	xmlNodePtr cur = handle->zone_dh_cur;
2174 	int name_match, type_match, value_match;
2175 
2176 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2177 		if (xmlStrcmp(cur->name, DTD_ELEM_ATTR))
2178 			continue;
2179 
2180 		name_match = match_prop(cur, DTD_ATTR_NAME,
2181 		    tabptr->zone_attr_name);
2182 		type_match = match_prop(cur, DTD_ATTR_TYPE,
2183 		    tabptr->zone_attr_type);
2184 		value_match = match_prop(cur, DTD_ATTR_VALUE,
2185 		    tabptr->zone_attr_value);
2186 
2187 		if (name_match && type_match && value_match) {
2188 			xmlUnlinkNode(cur);
2189 			xmlFreeNode(cur);
2190 			return (Z_OK);
2191 		}
2192 	}
2193 	return (Z_NO_RESOURCE_ID);
2194 }
2195 
2196 int
2197 zonecfg_delete_attr(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2198 {
2199 	int err;
2200 
2201 	if (tabptr == NULL)
2202 		return (Z_INVAL);
2203 
2204 	if ((err = operation_prep(handle)) != Z_OK)
2205 		return (err);
2206 
2207 	if ((err = zonecfg_delete_attr_core(handle, tabptr)) != Z_OK)
2208 		return (err);
2209 
2210 	return (Z_OK);
2211 }
2212 
2213 int
2214 zonecfg_modify_attr(
2215 	zone_dochandle_t handle,
2216 	struct zone_attrtab *oldtabptr,
2217 	struct zone_attrtab *newtabptr)
2218 {
2219 	int err;
2220 
2221 	if (oldtabptr == NULL || newtabptr == NULL)
2222 		return (Z_INVAL);
2223 
2224 	if ((err = operation_prep(handle)) != Z_OK)
2225 		return (err);
2226 
2227 	if ((err = zonecfg_delete_attr_core(handle, oldtabptr)) != Z_OK)
2228 		return (err);
2229 
2230 	if ((err = zonecfg_add_attr_core(handle, newtabptr)) != Z_OK)
2231 		return (err);
2232 
2233 	return (Z_OK);
2234 }
2235 
2236 int
2237 zonecfg_get_attr_boolean(const struct zone_attrtab *attr, boolean_t *value)
2238 {
2239 	if (attr == NULL)
2240 		return (Z_INVAL);
2241 
2242 	if (strcmp(attr->zone_attr_type, DTD_ENTITY_BOOLEAN) != 0)
2243 		return (Z_INVAL);
2244 
2245 	if (strcmp(attr->zone_attr_value, DTD_ENTITY_TRUE) == 0) {
2246 		*value = B_TRUE;
2247 		return (Z_OK);
2248 	}
2249 	if (strcmp(attr->zone_attr_value, DTD_ENTITY_FALSE) == 0) {
2250 		*value = B_FALSE;
2251 		return (Z_OK);
2252 	}
2253 	return (Z_INVAL);
2254 }
2255 
2256 int
2257 zonecfg_get_attr_int(const struct zone_attrtab *attr, int64_t *value)
2258 {
2259 	long long result;
2260 	char *endptr;
2261 
2262 	if (attr == NULL)
2263 		return (Z_INVAL);
2264 
2265 	if (strcmp(attr->zone_attr_type, DTD_ENTITY_INT) != 0)
2266 		return (Z_INVAL);
2267 
2268 	errno = 0;
2269 	result = strtoll(attr->zone_attr_value, &endptr, 10);
2270 	if (errno != 0 || *endptr != '\0')
2271 		return (Z_INVAL);
2272 	*value = result;
2273 	return (Z_OK);
2274 }
2275 
2276 int
2277 zonecfg_get_attr_string(const struct zone_attrtab *attr, char *value,
2278     size_t val_sz)
2279 {
2280 	if (attr == NULL)
2281 		return (Z_INVAL);
2282 
2283 	if (strcmp(attr->zone_attr_type, DTD_ENTITY_STRING) != 0)
2284 		return (Z_INVAL);
2285 
2286 	if (strlcpy(value, attr->zone_attr_value, val_sz) >= val_sz)
2287 		return (Z_TOO_BIG);
2288 	return (Z_OK);
2289 }
2290 
2291 int
2292 zonecfg_get_attr_uint(const struct zone_attrtab *attr, uint64_t *value)
2293 {
2294 	unsigned long long result;
2295 	long long neg_result;
2296 	char *endptr;
2297 
2298 	if (attr == NULL)
2299 		return (Z_INVAL);
2300 
2301 	if (strcmp(attr->zone_attr_type, DTD_ENTITY_UINT) != 0)
2302 		return (Z_INVAL);
2303 
2304 	errno = 0;
2305 	result = strtoull(attr->zone_attr_value, &endptr, 10);
2306 	if (errno != 0 || *endptr != '\0')
2307 		return (Z_INVAL);
2308 	errno = 0;
2309 	neg_result = strtoll(attr->zone_attr_value, &endptr, 10);
2310 	/*
2311 	 * Incredibly, strtoull("<negative number>", ...) will not fail but
2312 	 * return whatever (negative) number cast as a u_longlong_t, so we
2313 	 * need to look for this here.
2314 	 */
2315 	if (errno == 0 && neg_result < 0)
2316 		return (Z_INVAL);
2317 	*value = result;
2318 	return (Z_OK);
2319 }
2320 
2321 int
2322 zonecfg_lookup_rctl(zone_dochandle_t handle, struct zone_rctltab *tabptr)
2323 {
2324 	xmlNodePtr cur, val;
2325 	char savedname[MAXNAMELEN];
2326 	struct zone_rctlvaltab *valptr;
2327 	int err;
2328 
2329 	if (tabptr->zone_rctl_name == NULL ||
2330 	    strlen(tabptr->zone_rctl_name) == 0)
2331 		return (Z_INVAL);
2332 
2333 	if ((err = operation_prep(handle)) != Z_OK)
2334 		return (err);
2335 
2336 	cur = handle->zone_dh_cur;
2337 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2338 		if (xmlStrcmp(cur->name, DTD_ELEM_RCTL))
2339 			continue;
2340 		if ((fetchprop(cur, DTD_ATTR_NAME, savedname,
2341 		    sizeof (savedname)) == Z_OK) &&
2342 		    (strcmp(savedname, tabptr->zone_rctl_name) == 0)) {
2343 			tabptr->zone_rctl_valptr = NULL;
2344 			for (val = cur->xmlChildrenNode; val != NULL;
2345 			    val = val->next) {
2346 				valptr = (struct zone_rctlvaltab *)malloc(
2347 				    sizeof (struct zone_rctlvaltab));
2348 				if (valptr == NULL)
2349 					return (Z_NOMEM);
2350 				if ((fetchprop(val, DTD_ATTR_PRIV,
2351 				    valptr->zone_rctlval_priv,
2352 				    sizeof (valptr->zone_rctlval_priv)) !=
2353 				    Z_OK))
2354 					break;
2355 				if ((fetchprop(val, DTD_ATTR_LIMIT,
2356 				    valptr->zone_rctlval_limit,
2357 				    sizeof (valptr->zone_rctlval_limit)) !=
2358 				    Z_OK))
2359 					break;
2360 				if ((fetchprop(val, DTD_ATTR_ACTION,
2361 				    valptr->zone_rctlval_action,
2362 				    sizeof (valptr->zone_rctlval_action)) !=
2363 				    Z_OK))
2364 					break;
2365 				if (zonecfg_add_rctl_value(tabptr, valptr) !=
2366 				    Z_OK)
2367 					break;
2368 			}
2369 			return (Z_OK);
2370 		}
2371 	}
2372 	return (Z_NO_RESOURCE_ID);
2373 }
2374 
2375 static int
2376 zonecfg_add_rctl_core(zone_dochandle_t handle, struct zone_rctltab *tabptr)
2377 {
2378 	xmlNodePtr newnode, cur = handle->zone_dh_cur, valnode;
2379 	struct zone_rctlvaltab *valptr;
2380 	int err;
2381 
2382 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_RCTL, NULL);
2383 	err = newprop(newnode, DTD_ATTR_NAME, tabptr->zone_rctl_name);
2384 	if (err != Z_OK)
2385 		return (err);
2386 	for (valptr = tabptr->zone_rctl_valptr; valptr != NULL;
2387 	    valptr = valptr->zone_rctlval_next) {
2388 		valnode = xmlNewTextChild(newnode, NULL,
2389 		    DTD_ELEM_RCTLVALUE, NULL);
2390 		err = newprop(valnode, DTD_ATTR_PRIV,
2391 		    valptr->zone_rctlval_priv);
2392 		if (err != Z_OK)
2393 			return (err);
2394 		err = newprop(valnode, DTD_ATTR_LIMIT,
2395 		    valptr->zone_rctlval_limit);
2396 		if (err != Z_OK)
2397 			return (err);
2398 		err = newprop(valnode, DTD_ATTR_ACTION,
2399 		    valptr->zone_rctlval_action);
2400 		if (err != Z_OK)
2401 			return (err);
2402 	}
2403 	return (Z_OK);
2404 }
2405 
2406 int
2407 zonecfg_add_rctl(zone_dochandle_t handle, struct zone_rctltab *tabptr)
2408 {
2409 	int err;
2410 
2411 	if (tabptr == NULL || tabptr->zone_rctl_name == NULL)
2412 		return (Z_INVAL);
2413 
2414 	if ((err = operation_prep(handle)) != Z_OK)
2415 		return (err);
2416 
2417 	if ((err = zonecfg_add_rctl_core(handle, tabptr)) != Z_OK)
2418 		return (err);
2419 
2420 	return (Z_OK);
2421 }
2422 
2423 static int
2424 zonecfg_delete_rctl_core(zone_dochandle_t handle, struct zone_rctltab *tabptr)
2425 {
2426 	xmlNodePtr cur = handle->zone_dh_cur;
2427 	xmlChar *savedname;
2428 	int name_result;
2429 
2430 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2431 		if (xmlStrcmp(cur->name, DTD_ELEM_RCTL))
2432 			continue;
2433 
2434 		savedname = xmlGetProp(cur, DTD_ATTR_NAME);
2435 		if (savedname == NULL)	/* shouldn't happen */
2436 			continue;
2437 		name_result = xmlStrcmp(savedname,
2438 		    (const xmlChar *) tabptr->zone_rctl_name);
2439 		xmlFree(savedname);
2440 
2441 		if (name_result == 0) {
2442 			xmlUnlinkNode(cur);
2443 			xmlFreeNode(cur);
2444 			return (Z_OK);
2445 		}
2446 	}
2447 	return (Z_NO_RESOURCE_ID);
2448 }
2449 
2450 int
2451 zonecfg_delete_rctl(zone_dochandle_t handle, struct zone_rctltab *tabptr)
2452 {
2453 	int err;
2454 
2455 	if (tabptr == NULL || tabptr->zone_rctl_name == NULL)
2456 		return (Z_INVAL);
2457 
2458 	if ((err = operation_prep(handle)) != Z_OK)
2459 		return (err);
2460 
2461 	if ((err = zonecfg_delete_rctl_core(handle, tabptr)) != Z_OK)
2462 		return (err);
2463 
2464 	return (Z_OK);
2465 }
2466 
2467 int
2468 zonecfg_modify_rctl(
2469 	zone_dochandle_t handle,
2470 	struct zone_rctltab *oldtabptr,
2471 	struct zone_rctltab *newtabptr)
2472 {
2473 	int err;
2474 
2475 	if (oldtabptr == NULL || oldtabptr->zone_rctl_name == NULL ||
2476 	    newtabptr == NULL || newtabptr->zone_rctl_name == NULL)
2477 		return (Z_INVAL);
2478 
2479 	if ((err = operation_prep(handle)) != Z_OK)
2480 		return (err);
2481 
2482 	if ((err = zonecfg_delete_rctl_core(handle, oldtabptr)) != Z_OK)
2483 		return (err);
2484 
2485 	if ((err = zonecfg_add_rctl_core(handle, newtabptr)) != Z_OK)
2486 		return (err);
2487 
2488 	return (Z_OK);
2489 }
2490 
2491 int
2492 zonecfg_add_rctl_value(
2493 	struct zone_rctltab *tabptr,
2494 	struct zone_rctlvaltab *valtabptr)
2495 {
2496 	struct zone_rctlvaltab *last, *old, *new;
2497 	rctlblk_t *rctlblk = alloca(rctlblk_size());
2498 
2499 	last = tabptr->zone_rctl_valptr;
2500 	for (old = last; old != NULL; old = old->zone_rctlval_next)
2501 		last = old;	/* walk to the end of the list */
2502 	new = valtabptr;	/* alloc'd by caller */
2503 	new->zone_rctlval_next = NULL;
2504 	if (zonecfg_construct_rctlblk(valtabptr, rctlblk) != Z_OK)
2505 		return (Z_INVAL);
2506 	if (!zonecfg_valid_rctlblk(rctlblk))
2507 		return (Z_INVAL);
2508 	if (last == NULL)
2509 		tabptr->zone_rctl_valptr = new;
2510 	else
2511 		last->zone_rctlval_next = new;
2512 	return (Z_OK);
2513 }
2514 
2515 int
2516 zonecfg_remove_rctl_value(
2517 	struct zone_rctltab *tabptr,
2518 	struct zone_rctlvaltab *valtabptr)
2519 {
2520 	struct zone_rctlvaltab *last, *this, *next;
2521 
2522 	last = tabptr->zone_rctl_valptr;
2523 	for (this = last; this != NULL; this = this->zone_rctlval_next) {
2524 		if (strcmp(this->zone_rctlval_priv,
2525 		    valtabptr->zone_rctlval_priv) == 0 &&
2526 		    strcmp(this->zone_rctlval_limit,
2527 		    valtabptr->zone_rctlval_limit) == 0 &&
2528 		    strcmp(this->zone_rctlval_action,
2529 		    valtabptr->zone_rctlval_action) == 0) {
2530 			next = this->zone_rctlval_next;
2531 			if (this == tabptr->zone_rctl_valptr)
2532 				tabptr->zone_rctl_valptr = next;
2533 			else
2534 				last->zone_rctlval_next = next;
2535 			free(this);
2536 			return (Z_OK);
2537 		} else
2538 			last = this;
2539 	}
2540 	return (Z_NO_PROPERTY_ID);
2541 }
2542 
2543 char *
2544 zonecfg_strerror(int errnum)
2545 {
2546 	switch (errnum) {
2547 	case Z_OK:
2548 		return (dgettext(TEXT_DOMAIN, "OK"));
2549 	case Z_EMPTY_DOCUMENT:
2550 		return (dgettext(TEXT_DOMAIN, "Empty document"));
2551 	case Z_WRONG_DOC_TYPE:
2552 		return (dgettext(TEXT_DOMAIN, "Wrong document type"));
2553 	case Z_BAD_PROPERTY:
2554 		return (dgettext(TEXT_DOMAIN, "Bad document property"));
2555 	case Z_TEMP_FILE:
2556 		return (dgettext(TEXT_DOMAIN,
2557 		    "Problem creating temporary file"));
2558 	case Z_SAVING_FILE:
2559 		return (dgettext(TEXT_DOMAIN, "Problem saving file"));
2560 	case Z_NO_ENTRY:
2561 		return (dgettext(TEXT_DOMAIN, "No such entry"));
2562 	case Z_BOGUS_ZONE_NAME:
2563 		return (dgettext(TEXT_DOMAIN, "Bogus zone name"));
2564 	case Z_REQD_RESOURCE_MISSING:
2565 		return (dgettext(TEXT_DOMAIN, "Required resource missing"));
2566 	case Z_REQD_PROPERTY_MISSING:
2567 		return (dgettext(TEXT_DOMAIN, "Required property missing"));
2568 	case Z_BAD_HANDLE:
2569 		return (dgettext(TEXT_DOMAIN, "Bad handle"));
2570 	case Z_NOMEM:
2571 		return (dgettext(TEXT_DOMAIN, "Out of memory"));
2572 	case Z_INVAL:
2573 		return (dgettext(TEXT_DOMAIN, "Invalid argument"));
2574 	case Z_ACCES:
2575 		return (dgettext(TEXT_DOMAIN, "Permission denied"));
2576 	case Z_TOO_BIG:
2577 		return (dgettext(TEXT_DOMAIN, "Argument list too long"));
2578 	case Z_MISC_FS:
2579 		return (dgettext(TEXT_DOMAIN,
2580 		    "Miscellaneous file system error"));
2581 	case Z_NO_ZONE:
2582 		return (dgettext(TEXT_DOMAIN, "No such zone configured"));
2583 	case Z_NO_RESOURCE_TYPE:
2584 		return (dgettext(TEXT_DOMAIN, "No such resource type"));
2585 	case Z_NO_RESOURCE_ID:
2586 		return (dgettext(TEXT_DOMAIN, "No such resource with that id"));
2587 	case Z_NO_PROPERTY_TYPE:
2588 		return (dgettext(TEXT_DOMAIN, "No such property type"));
2589 	case Z_NO_PROPERTY_ID:
2590 		return (dgettext(TEXT_DOMAIN, "No such property with that id"));
2591 	case Z_BAD_ZONE_STATE:
2592 		return (dgettext(TEXT_DOMAIN,
2593 		    "Zone state is invalid for the requested operation"));
2594 	case Z_INVALID_DOCUMENT:
2595 		return (dgettext(TEXT_DOMAIN, "Invalid document"));
2596 	case Z_NAME_IN_USE:
2597 		return (dgettext(TEXT_DOMAIN, "Zone name already in use"));
2598 	case Z_NO_SUCH_ID:
2599 		return (dgettext(TEXT_DOMAIN, "No such zone ID"));
2600 	case Z_UPDATING_INDEX:
2601 		return (dgettext(TEXT_DOMAIN, "Problem updating index file"));
2602 	case Z_LOCKING_FILE:
2603 		return (dgettext(TEXT_DOMAIN, "Locking index file"));
2604 	case Z_UNLOCKING_FILE:
2605 		return (dgettext(TEXT_DOMAIN, "Unlocking index file"));
2606 	case Z_INSUFFICIENT_SPEC:
2607 		return (dgettext(TEXT_DOMAIN, "Insufficient specification"));
2608 	case Z_RESOLVED_PATH:
2609 		return (dgettext(TEXT_DOMAIN, "Resolved path mismatch"));
2610 	case Z_IPV6_ADDR_PREFIX_LEN:
2611 		return (dgettext(TEXT_DOMAIN,
2612 		    "IPv6 address missing required prefix length"));
2613 	case Z_BOGUS_ADDRESS:
2614 		return (dgettext(TEXT_DOMAIN,
2615 		    "Neither an IPv4 nor an IPv6 address nor a host name"));
2616 	default:
2617 		return (dgettext(TEXT_DOMAIN, "Unknown error"));
2618 	}
2619 }
2620 
2621 /*
2622  * Note that the zonecfg_setXent() and zonecfg_endXent() calls are all the
2623  * same, as they just turn around and call zonecfg_setent() / zonecfg_endent().
2624  */
2625 
2626 static int
2627 zonecfg_setent(zone_dochandle_t handle)
2628 {
2629 	xmlNodePtr cur;
2630 	int err;
2631 
2632 	if (handle == NULL)
2633 		return (Z_INVAL);
2634 
2635 	if ((err = operation_prep(handle)) != Z_OK) {
2636 		handle->zone_dh_cur = NULL;
2637 		return (err);
2638 	}
2639 	cur = handle->zone_dh_cur;
2640 	cur = cur->xmlChildrenNode;
2641 	handle->zone_dh_cur = cur;
2642 	return (Z_OK);
2643 }
2644 
2645 static int
2646 zonecfg_endent(zone_dochandle_t handle)
2647 {
2648 	if (handle == NULL)
2649 		return (Z_INVAL);
2650 
2651 	handle->zone_dh_cur = handle->zone_dh_top;
2652 	return (Z_OK);
2653 }
2654 
2655 int
2656 zonecfg_setfsent(zone_dochandle_t handle)
2657 {
2658 	return (zonecfg_setent(handle));
2659 }
2660 
2661 int
2662 zonecfg_getfsent(zone_dochandle_t handle, struct zone_fstab *tabptr)
2663 {
2664 	xmlNodePtr cur, options;
2665 	char options_str[MAX_MNTOPT_STR];
2666 	int err;
2667 
2668 	if (handle == NULL)
2669 		return (Z_INVAL);
2670 
2671 	if ((cur = handle->zone_dh_cur) == NULL)
2672 		return (Z_NO_ENTRY);
2673 
2674 	for (; cur != NULL; cur = cur->next)
2675 		if (!xmlStrcmp(cur->name, DTD_ELEM_FS))
2676 			break;
2677 	if (cur == NULL) {
2678 		handle->zone_dh_cur = handle->zone_dh_top;
2679 		return (Z_NO_ENTRY);
2680 	}
2681 
2682 	if ((err = fetchprop(cur, DTD_ATTR_SPECIAL, tabptr->zone_fs_special,
2683 	    sizeof (tabptr->zone_fs_special))) != Z_OK) {
2684 		handle->zone_dh_cur = handle->zone_dh_top;
2685 		return (err);
2686 	}
2687 
2688 	if ((err = fetchprop(cur, DTD_ATTR_RAW, tabptr->zone_fs_raw,
2689 	    sizeof (tabptr->zone_fs_raw))) != Z_OK) {
2690 		handle->zone_dh_cur = handle->zone_dh_top;
2691 		return (err);
2692 	}
2693 
2694 	if ((err = fetchprop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir,
2695 	    sizeof (tabptr->zone_fs_dir))) != Z_OK) {
2696 		handle->zone_dh_cur = handle->zone_dh_top;
2697 		return (err);
2698 	}
2699 
2700 	if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_fs_type,
2701 	    sizeof (tabptr->zone_fs_type))) != Z_OK) {
2702 		handle->zone_dh_cur = handle->zone_dh_top;
2703 		return (err);
2704 	}
2705 
2706 	/* OK for options to be NULL */
2707 	tabptr->zone_fs_options = NULL;
2708 	for (options = cur->xmlChildrenNode; options != NULL;
2709 	    options = options->next) {
2710 		if (fetchprop(options, DTD_ATTR_NAME, options_str,
2711 		    sizeof (options_str)) != Z_OK)
2712 			break;
2713 		if (zonecfg_add_fs_option(tabptr, options_str) != Z_OK)
2714 			break;
2715 	}
2716 
2717 	handle->zone_dh_cur = cur->next;
2718 	return (Z_OK);
2719 }
2720 
2721 int
2722 zonecfg_endfsent(zone_dochandle_t handle)
2723 {
2724 	return (zonecfg_endent(handle));
2725 }
2726 
2727 int
2728 zonecfg_setipdent(zone_dochandle_t handle)
2729 {
2730 	return (zonecfg_setent(handle));
2731 }
2732 
2733 int
2734 zonecfg_getipdent(zone_dochandle_t handle, struct zone_fstab *tabptr)
2735 {
2736 	xmlNodePtr cur;
2737 	int err;
2738 
2739 	if (handle == NULL)
2740 		return (Z_INVAL);
2741 
2742 	if ((cur = handle->zone_dh_cur) == NULL)
2743 		return (Z_NO_ENTRY);
2744 
2745 	for (; cur != NULL; cur = cur->next)
2746 		if (!xmlStrcmp(cur->name, DTD_ELEM_IPD))
2747 			break;
2748 	if (cur == NULL) {
2749 		handle->zone_dh_cur = handle->zone_dh_top;
2750 		return (Z_NO_ENTRY);
2751 	}
2752 
2753 	if ((err = fetchprop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir,
2754 	    sizeof (tabptr->zone_fs_dir))) != Z_OK) {
2755 		handle->zone_dh_cur = handle->zone_dh_top;
2756 		return (err);
2757 	}
2758 
2759 	handle->zone_dh_cur = cur->next;
2760 	return (Z_OK);
2761 }
2762 
2763 int
2764 zonecfg_endipdent(zone_dochandle_t handle)
2765 {
2766 	return (zonecfg_endent(handle));
2767 }
2768 
2769 int
2770 zonecfg_setnwifent(zone_dochandle_t handle)
2771 {
2772 	return (zonecfg_setent(handle));
2773 }
2774 
2775 int
2776 zonecfg_getnwifent(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
2777 {
2778 	xmlNodePtr cur;
2779 	int err;
2780 
2781 	if (handle == NULL)
2782 		return (Z_INVAL);
2783 
2784 	if ((cur = handle->zone_dh_cur) == NULL)
2785 		return (Z_NO_ENTRY);
2786 
2787 	for (; cur != NULL; cur = cur->next)
2788 		if (!xmlStrcmp(cur->name, DTD_ELEM_NET))
2789 			break;
2790 	if (cur == NULL) {
2791 		handle->zone_dh_cur = handle->zone_dh_top;
2792 		return (Z_NO_ENTRY);
2793 	}
2794 
2795 	if ((err = fetchprop(cur, DTD_ATTR_ADDRESS, tabptr->zone_nwif_address,
2796 	    sizeof (tabptr->zone_nwif_address))) != Z_OK) {
2797 		handle->zone_dh_cur = handle->zone_dh_top;
2798 		return (err);
2799 	}
2800 
2801 	if ((err = fetchprop(cur, DTD_ATTR_PHYSICAL, tabptr->zone_nwif_physical,
2802 	    sizeof (tabptr->zone_nwif_physical))) != Z_OK) {
2803 		handle->zone_dh_cur = handle->zone_dh_top;
2804 		return (err);
2805 	}
2806 
2807 	handle->zone_dh_cur = cur->next;
2808 	return (Z_OK);
2809 }
2810 
2811 int
2812 zonecfg_endnwifent(zone_dochandle_t handle)
2813 {
2814 	return (zonecfg_endent(handle));
2815 }
2816 
2817 int
2818 zonecfg_setdevent(zone_dochandle_t handle)
2819 {
2820 	return (zonecfg_setent(handle));
2821 }
2822 
2823 int
2824 zonecfg_getdevent(zone_dochandle_t handle, struct zone_devtab *tabptr)
2825 {
2826 	xmlNodePtr cur;
2827 	int err;
2828 
2829 	if (handle == NULL)
2830 		return (Z_INVAL);
2831 
2832 	if ((cur = handle->zone_dh_cur) == NULL)
2833 		return (Z_NO_ENTRY);
2834 
2835 	for (; cur != NULL; cur = cur->next)
2836 		if (!xmlStrcmp(cur->name, DTD_ELEM_DEVICE))
2837 			break;
2838 	if (cur == NULL) {
2839 		handle->zone_dh_cur = handle->zone_dh_top;
2840 		return (Z_NO_ENTRY);
2841 	}
2842 
2843 	if ((err = fetchprop(cur, DTD_ATTR_MATCH, tabptr->zone_dev_match,
2844 	    sizeof (tabptr->zone_dev_match))) != Z_OK) {
2845 		handle->zone_dh_cur = handle->zone_dh_top;
2846 		return (err);
2847 	}
2848 
2849 	handle->zone_dh_cur = cur->next;
2850 	return (Z_OK);
2851 }
2852 
2853 int
2854 zonecfg_enddevent(zone_dochandle_t handle)
2855 {
2856 	return (zonecfg_endent(handle));
2857 }
2858 
2859 int
2860 zonecfg_setrctlent(zone_dochandle_t handle)
2861 {
2862 	return (zonecfg_setent(handle));
2863 }
2864 
2865 int
2866 zonecfg_getrctlent(zone_dochandle_t handle, struct zone_rctltab *tabptr)
2867 {
2868 	xmlNodePtr cur, val;
2869 	struct zone_rctlvaltab *valptr;
2870 	int err;
2871 
2872 	if (handle == NULL)
2873 		return (Z_INVAL);
2874 
2875 	if ((cur = handle->zone_dh_cur) == NULL)
2876 		return (Z_NO_ENTRY);
2877 
2878 	for (; cur != NULL; cur = cur->next)
2879 		if (!xmlStrcmp(cur->name, DTD_ELEM_RCTL))
2880 			break;
2881 	if (cur == NULL) {
2882 		handle->zone_dh_cur = handle->zone_dh_top;
2883 		return (Z_NO_ENTRY);
2884 	}
2885 
2886 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_rctl_name,
2887 	    sizeof (tabptr->zone_rctl_name))) != Z_OK) {
2888 		handle->zone_dh_cur = handle->zone_dh_top;
2889 		return (err);
2890 	}
2891 
2892 	tabptr->zone_rctl_valptr = NULL;
2893 	for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
2894 		valptr = (struct zone_rctlvaltab *)malloc(
2895 		    sizeof (struct zone_rctlvaltab));
2896 		if (valptr == NULL)
2897 			return (Z_NOMEM);
2898 		if (fetchprop(val, DTD_ATTR_PRIV, valptr->zone_rctlval_priv,
2899 		    sizeof (valptr->zone_rctlval_priv)) != Z_OK)
2900 			break;
2901 		if (fetchprop(val, DTD_ATTR_LIMIT, valptr->zone_rctlval_limit,
2902 		    sizeof (valptr->zone_rctlval_limit)) != Z_OK)
2903 			break;
2904 		if (fetchprop(val, DTD_ATTR_ACTION, valptr->zone_rctlval_action,
2905 		    sizeof (valptr->zone_rctlval_action)) != Z_OK)
2906 			break;
2907 		if (zonecfg_add_rctl_value(tabptr, valptr) != Z_OK)
2908 			break;
2909 	}
2910 
2911 	handle->zone_dh_cur = cur->next;
2912 	return (Z_OK);
2913 }
2914 
2915 int
2916 zonecfg_endrctlent(zone_dochandle_t handle)
2917 {
2918 	return (zonecfg_endent(handle));
2919 }
2920 
2921 int
2922 zonecfg_setattrent(zone_dochandle_t handle)
2923 {
2924 	return (zonecfg_setent(handle));
2925 }
2926 
2927 int
2928 zonecfg_getattrent(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2929 {
2930 	xmlNodePtr cur;
2931 	int err;
2932 
2933 	if (handle == NULL)
2934 		return (Z_INVAL);
2935 
2936 	if ((cur = handle->zone_dh_cur) == NULL)
2937 		return (Z_NO_ENTRY);
2938 
2939 	for (; cur != NULL; cur = cur->next)
2940 		if (!xmlStrcmp(cur->name, DTD_ELEM_ATTR))
2941 			break;
2942 	if (cur == NULL) {
2943 		handle->zone_dh_cur = handle->zone_dh_top;
2944 		return (Z_NO_ENTRY);
2945 	}
2946 
2947 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_attr_name,
2948 	    sizeof (tabptr->zone_attr_name))) != Z_OK) {
2949 		handle->zone_dh_cur = handle->zone_dh_top;
2950 		return (err);
2951 	}
2952 
2953 	if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_attr_type,
2954 	    sizeof (tabptr->zone_attr_type))) != Z_OK) {
2955 		handle->zone_dh_cur = handle->zone_dh_top;
2956 		return (err);
2957 	}
2958 
2959 	if ((err = fetchprop(cur, DTD_ATTR_VALUE, tabptr->zone_attr_value,
2960 	    sizeof (tabptr->zone_attr_value))) != Z_OK) {
2961 		handle->zone_dh_cur = handle->zone_dh_top;
2962 		return (err);
2963 	}
2964 
2965 	handle->zone_dh_cur = cur->next;
2966 	return (Z_OK);
2967 }
2968 
2969 int
2970 zonecfg_endattrent(zone_dochandle_t handle)
2971 {
2972 	return (zonecfg_endent(handle));
2973 }
2974 
2975 /* This will ultimately be configurable. */
2976 static const char *priv_list[] = {
2977 	PRIV_FILE_CHOWN,
2978 	PRIV_FILE_CHOWN_SELF,
2979 	PRIV_FILE_DAC_EXECUTE,
2980 	PRIV_FILE_DAC_READ,
2981 	PRIV_FILE_DAC_SEARCH,
2982 	PRIV_FILE_DAC_WRITE,
2983 	PRIV_FILE_OWNER,
2984 	PRIV_FILE_SETID,
2985 	PRIV_IPC_DAC_READ,
2986 	PRIV_IPC_DAC_WRITE,
2987 	PRIV_IPC_OWNER,
2988 	PRIV_NET_ICMPACCESS,
2989 	PRIV_NET_PRIVADDR,
2990 	PRIV_PROC_CHROOT,
2991 	PRIV_SYS_AUDIT,
2992 	PRIV_PROC_AUDIT,
2993 	PRIV_PROC_OWNER,
2994 	PRIV_PROC_SETID,
2995 	PRIV_PROC_TASKID,
2996 	PRIV_SYS_ACCT,
2997 	PRIV_SYS_ADMIN,
2998 	PRIV_SYS_MOUNT,
2999 	PRIV_SYS_NFS,
3000 	PRIV_SYS_RESOURCE,
3001 	PRIV_CONTRACT_EVENT,
3002 	PRIV_CONTRACT_OBSERVER,
3003 	NULL
3004 };
3005 
3006 int
3007 zonecfg_get_privset(priv_set_t *privs)
3008 {
3009 	const char **strp;
3010 	priv_set_t *basic = priv_str_to_set("basic", ",", NULL);
3011 
3012 	if (basic == NULL)
3013 		return (Z_INVAL);
3014 
3015 	priv_union(basic, privs);
3016 	priv_freeset(basic);
3017 
3018 	for (strp = priv_list; *strp != NULL; strp++) {
3019 		if (priv_addset(privs, *strp) != 0) {
3020 			return (Z_INVAL);
3021 		}
3022 	}
3023 	return (Z_OK);
3024 }
3025 
3026 int
3027 zone_get_zonepath(char *zone_name, char *zonepath, size_t rp_sz)
3028 {
3029 	zone_dochandle_t handle;
3030 	boolean_t found = B_FALSE;
3031 	struct zoneent *ze;
3032 	FILE *cookie;
3033 	int err;
3034 	char *cp;
3035 
3036 	if (zone_name == NULL)
3037 		return (Z_INVAL);
3038 
3039 	(void) strlcpy(zonepath, zonecfg_root, rp_sz);
3040 	cp = zonepath + strlen(zonepath);
3041 	while (cp > zonepath && cp[-1] == '/')
3042 		*--cp = '\0';
3043 
3044 	if (strcmp(zone_name, GLOBAL_ZONENAME) == 0) {
3045 		if (zonepath[0] == '\0')
3046 			(void) strlcpy(zonepath, "/", rp_sz);
3047 		return (Z_OK);
3048 	}
3049 
3050 	/*
3051 	 * First check the index file.  Because older versions did not have
3052 	 * a copy of the zone path, allow for it to be zero length, in which
3053 	 * case we ignore this result and fall back to the XML files.
3054 	 */
3055 	cookie = setzoneent();
3056 	while ((ze = getzoneent_private(cookie)) != NULL) {
3057 		if (strcmp(ze->zone_name, zone_name) == 0) {
3058 			found = B_TRUE;
3059 			if (ze->zone_path[0] != '\0')
3060 				(void) strlcpy(cp, ze->zone_path,
3061 				    rp_sz - (cp - zonepath));
3062 		}
3063 		free(ze);
3064 		if (found)
3065 			break;
3066 	}
3067 	endzoneent(cookie);
3068 	if (found && *cp != '\0')
3069 		return (Z_OK);
3070 
3071 	/* Fall back to the XML files. */
3072 	if ((handle = zonecfg_init_handle()) == NULL)
3073 		return (Z_NOMEM);
3074 
3075 	/*
3076 	 * Check the snapshot first: if a zone is running, its zonepath
3077 	 * may have changed.
3078 	 */
3079 	if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) {
3080 		if ((err = zonecfg_get_handle(zone_name, handle)) != Z_OK)
3081 			return (err);
3082 	}
3083 	err = zonecfg_get_zonepath(handle, zonepath, rp_sz);
3084 	zonecfg_fini_handle(handle);
3085 	return (err);
3086 }
3087 
3088 int
3089 zone_get_rootpath(char *zone_name, char *rootpath, size_t rp_sz)
3090 {
3091 	int err;
3092 
3093 	/* This function makes sense for non-global zones only. */
3094 	if (strcmp(zone_name, GLOBAL_ZONENAME) == 0)
3095 		return (Z_BOGUS_ZONE_NAME);
3096 	if ((err = zone_get_zonepath(zone_name, rootpath, rp_sz)) != Z_OK)
3097 		return (err);
3098 	if (strlcat(rootpath, "/root", rp_sz) >= rp_sz)
3099 		return (Z_TOO_BIG);
3100 	return (Z_OK);
3101 }
3102 
3103 static zone_state_t
3104 kernel_state_to_user_state(zoneid_t zoneid, zone_status_t kernel_state)
3105 {
3106 	char zoneroot[MAXPATHLEN];
3107 	size_t zlen;
3108 
3109 	assert(kernel_state <= ZONE_MAX_STATE);
3110 	switch (kernel_state) {
3111 		case ZONE_IS_UNINITIALIZED:
3112 			return (ZONE_STATE_READY);
3113 		case ZONE_IS_READY:
3114 			/*
3115 			 * If the zone's root is mounted on $ZONEPATH/lu, then
3116 			 * it's a mounted scratch zone.
3117 			 */
3118 			if (zone_getattr(zoneid, ZONE_ATTR_ROOT, zoneroot,
3119 			    sizeof (zoneroot)) >= 0) {
3120 				zlen = strlen(zoneroot);
3121 				if (zlen > 3 &&
3122 				    strcmp(zoneroot + zlen - 3, "/lu") == 0)
3123 					return (ZONE_STATE_MOUNTED);
3124 			}
3125 			return (ZONE_STATE_READY);
3126 		case ZONE_IS_BOOTING:
3127 		case ZONE_IS_RUNNING:
3128 			return (ZONE_STATE_RUNNING);
3129 		case ZONE_IS_SHUTTING_DOWN:
3130 		case ZONE_IS_EMPTY:
3131 			return (ZONE_STATE_SHUTTING_DOWN);
3132 		case ZONE_IS_DOWN:
3133 		case ZONE_IS_DYING:
3134 		case ZONE_IS_DEAD:
3135 		default:
3136 			return (ZONE_STATE_DOWN);
3137 	}
3138 	/* NOTREACHED */
3139 }
3140 
3141 int
3142 zone_get_state(char *zone_name, zone_state_t *state_num)
3143 {
3144 	zone_status_t status;
3145 	zoneid_t zone_id;
3146 	struct zoneent *ze;
3147 	boolean_t found = B_FALSE;
3148 	FILE *cookie;
3149 	char kernzone[ZONENAME_MAX];
3150 	FILE *fp;
3151 
3152 	if (zone_name == NULL)
3153 		return (Z_INVAL);
3154 
3155 	/*
3156 	 * If we're looking at an alternate root, then we need to query the
3157 	 * kernel using the scratch zone name.
3158 	 */
3159 	zone_id = -1;
3160 	if (*zonecfg_root != '\0' && !zonecfg_is_scratch(zone_name)) {
3161 		if ((fp = zonecfg_open_scratch("", B_FALSE)) != NULL) {
3162 			if (zonecfg_find_scratch(fp, zone_name, zonecfg_root,
3163 			    kernzone, sizeof (kernzone)) == 0)
3164 				zone_id = getzoneidbyname(kernzone);
3165 			zonecfg_close_scratch(fp);
3166 		}
3167 	} else {
3168 		zone_id = getzoneidbyname(zone_name);
3169 	}
3170 
3171 	/* check to see if zone is running */
3172 	if (zone_id != -1 &&
3173 	    zone_getattr(zone_id, ZONE_ATTR_STATUS, &status,
3174 	    sizeof (status)) >= 0) {
3175 		*state_num = kernel_state_to_user_state(zone_id, status);
3176 		return (Z_OK);
3177 	}
3178 
3179 	cookie = setzoneent();
3180 	while ((ze = getzoneent_private(cookie)) != NULL) {
3181 		if (strcmp(ze->zone_name, zone_name) == 0) {
3182 			found = B_TRUE;
3183 			*state_num = ze->zone_state;
3184 		}
3185 		free(ze);
3186 		if (found)
3187 			break;
3188 	}
3189 	endzoneent(cookie);
3190 	return ((found) ? Z_OK : Z_NO_ZONE);
3191 }
3192 
3193 int
3194 zone_set_state(char *zone, zone_state_t state)
3195 {
3196 	struct zoneent ze;
3197 
3198 	if (state != ZONE_STATE_CONFIGURED && state != ZONE_STATE_INSTALLED &&
3199 	    state != ZONE_STATE_INCOMPLETE)
3200 		return (Z_INVAL);
3201 
3202 	bzero(&ze, sizeof (ze));
3203 	(void) strlcpy(ze.zone_name, zone, sizeof (ze.zone_name));
3204 	ze.zone_state = state;
3205 	(void) strlcpy(ze.zone_path, "", sizeof (ze.zone_path));
3206 	return (putzoneent(&ze, PZE_MODIFY));
3207 }
3208 
3209 /*
3210  * Get id (if any) for specified zone.  There are four possible outcomes:
3211  * - If the string corresponds to the numeric id of an active (booted)
3212  *   zone, sets *zip to the zone id and returns 0.
3213  * - If the string corresponds to the name of an active (booted) zone,
3214  *   sets *zip to the zone id and returns 0.
3215  * - If the string is a name in the configuration but is not booted,
3216  *   sets *zip to ZONE_ID_UNDEFINED and returns 0.
3217  * - Otherwise, leaves *zip unchanged and returns -1.
3218  *
3219  * This function acts as an auxiliary filter on the function of the same
3220  * name in libc; the linker binds to this version if libzonecfg exists,
3221  * and the libc version if it doesn't.  Any changes to this version of
3222  * the function should probably be reflected in the libc version as well.
3223  */
3224 int
3225 zone_get_id(const char *str, zoneid_t *zip)
3226 {
3227 	zone_dochandle_t hdl;
3228 	zoneid_t zoneid;
3229 	char *cp;
3230 	int err;
3231 
3232 	/* first try looking for active zone by id */
3233 	errno = 0;
3234 	zoneid = (zoneid_t)strtol(str, &cp, 0);
3235 	if (errno == 0 && cp != str && *cp == '\0' &&
3236 	    getzonenamebyid(zoneid, NULL, 0) != -1) {
3237 		*zip = zoneid;
3238 		return (0);
3239 	}
3240 
3241 	/* then look for active zone by name */
3242 	if ((zoneid = getzoneidbyname(str)) != -1) {
3243 		*zip = zoneid;
3244 		return (0);
3245 	}
3246 
3247 	/* if in global zone, try looking up name in configuration database */
3248 	if (getzoneid() != GLOBAL_ZONEID ||
3249 	    (hdl = zonecfg_init_handle()) == NULL)
3250 		return (-1);
3251 
3252 	if (zonecfg_get_handle(str, hdl) == Z_OK) {
3253 		/* zone exists but isn't active */
3254 		*zip = ZONE_ID_UNDEFINED;
3255 		err = 0;
3256 	} else {
3257 		err = -1;
3258 	}
3259 
3260 	zonecfg_fini_handle(hdl);
3261 	return (err);
3262 }
3263 
3264 char *
3265 zone_state_str(zone_state_t state_num)
3266 {
3267 	switch (state_num) {
3268 	case ZONE_STATE_CONFIGURED:
3269 		return (ZONE_STATE_STR_CONFIGURED);
3270 	case ZONE_STATE_INCOMPLETE:
3271 		return (ZONE_STATE_STR_INCOMPLETE);
3272 	case ZONE_STATE_INSTALLED:
3273 		return (ZONE_STATE_STR_INSTALLED);
3274 	case ZONE_STATE_READY:
3275 		return (ZONE_STATE_STR_READY);
3276 	case ZONE_STATE_MOUNTED:
3277 		return (ZONE_STATE_STR_MOUNTED);
3278 	case ZONE_STATE_RUNNING:
3279 		return (ZONE_STATE_STR_RUNNING);
3280 	case ZONE_STATE_SHUTTING_DOWN:
3281 		return (ZONE_STATE_STR_SHUTTING_DOWN);
3282 	case ZONE_STATE_DOWN:
3283 		return (ZONE_STATE_STR_DOWN);
3284 	default:
3285 		return ("unknown");
3286 	}
3287 }
3288 
3289 /*
3290  * Given a UUID value, find an associated zone name.  This is intended to be
3291  * used by callers who set up some 'default' name (corresponding to the
3292  * expected name for the zone) in the zonename buffer, and thus the function
3293  * doesn't touch this buffer on failure.
3294  */
3295 int
3296 zonecfg_get_name_by_uuid(const uuid_t uuid, char *zonename, size_t namelen)
3297 {
3298 	FILE *fp;
3299 	struct zoneent *ze;
3300 
3301 	/*
3302 	 * A small amount of subterfuge via casts is necessary here because
3303 	 * libuuid doesn't use const correctly, but we don't want to export
3304 	 * this brokenness to our clients.
3305 	 */
3306 	if (uuid_is_null(*(uuid_t *)&uuid))
3307 		return (Z_NO_ZONE);
3308 	if ((fp = setzoneent()) == NULL)
3309 		return (Z_NO_ZONE);
3310 	while ((ze = getzoneent_private(fp)) != NULL) {
3311 		if (uuid_compare(*(uuid_t *)&uuid, ze->zone_uuid) == 0)
3312 			break;
3313 		free(ze);
3314 	}
3315 	endzoneent(fp);
3316 	if (ze != NULL) {
3317 		(void) strlcpy(zonename, ze->zone_name, namelen);
3318 		free(ze);
3319 		return (Z_OK);
3320 	} else {
3321 		return (Z_NO_ZONE);
3322 	}
3323 }
3324 
3325 /*
3326  * Given a zone name, get its UUID.  Returns a "NULL" UUID value if the zone
3327  * exists but the file doesn't have a value set yet.  Returns an error if the
3328  * zone cannot be located.
3329  */
3330 int
3331 zonecfg_get_uuid(const char *zonename, uuid_t uuid)
3332 {
3333 	FILE *fp;
3334 	struct zoneent *ze;
3335 
3336 	if ((fp = setzoneent()) == NULL)
3337 		return (Z_NO_ZONE);
3338 	while ((ze = getzoneent_private(fp)) != NULL) {
3339 		if (strcmp(ze->zone_name, zonename) == 0)
3340 			break;
3341 		free(ze);
3342 	}
3343 	endzoneent(fp);
3344 	if (ze != NULL) {
3345 		uuid_copy(uuid, ze->zone_uuid);
3346 		free(ze);
3347 		return (Z_OK);
3348 	} else {
3349 		return (Z_NO_ZONE);
3350 	}
3351 }
3352 
3353 /*
3354  * File-system convenience functions.
3355  */
3356 boolean_t
3357 zonecfg_valid_fs_type(const char *type)
3358 {
3359 	/*
3360 	 * We already know which FS types don't work.
3361 	 */
3362 	if (strcmp(type, "proc") == 0 ||
3363 	    strcmp(type, "mntfs") == 0 ||
3364 	    strcmp(type, "autofs") == 0 ||
3365 	    strncmp(type, "nfs", sizeof ("nfs") - 1) == 0 ||
3366 	    strcmp(type, "cachefs") == 0)
3367 		return (B_FALSE);
3368 	/*
3369 	 * The caller may do more detailed verification to make sure other
3370 	 * aspects of this filesystem type make sense.
3371 	 */
3372 	return (B_TRUE);
3373 }
3374 
3375 /*
3376  * Generally uninteresting rctl convenience functions.
3377  */
3378 
3379 int
3380 zonecfg_construct_rctlblk(const struct zone_rctlvaltab *rctlval,
3381     rctlblk_t *rctlblk)
3382 {
3383 	unsigned long long ull;
3384 	char *endp;
3385 	rctl_priv_t priv;
3386 	rctl_qty_t limit;
3387 	uint_t action;
3388 
3389 	/* Get the privilege */
3390 	if (strcmp(rctlval->zone_rctlval_priv, "basic") == 0) {
3391 		priv = RCPRIV_BASIC;
3392 	} else if (strcmp(rctlval->zone_rctlval_priv, "privileged") == 0) {
3393 		priv = RCPRIV_PRIVILEGED;
3394 	} else {
3395 		/* Invalid privilege */
3396 		return (Z_INVAL);
3397 	}
3398 
3399 	/* deal with negative input; strtoull(3c) doesn't do what we want */
3400 	if (rctlval->zone_rctlval_limit[0] == '-')
3401 		return (Z_INVAL);
3402 	/* Get the limit */
3403 	errno = 0;
3404 	ull = strtoull(rctlval->zone_rctlval_limit, &endp, 0);
3405 	if (errno != 0 || *endp != '\0') {
3406 		/* parse failed */
3407 		return (Z_INVAL);
3408 	}
3409 	limit = (rctl_qty_t)ull;
3410 
3411 	/* Get the action */
3412 	if (strcmp(rctlval->zone_rctlval_action, "none") == 0) {
3413 		action = RCTL_LOCAL_NOACTION;
3414 	} else if (strcmp(rctlval->zone_rctlval_action, "signal") == 0) {
3415 		action = RCTL_LOCAL_SIGNAL;
3416 	} else if (strcmp(rctlval->zone_rctlval_action, "deny") == 0) {
3417 		action = RCTL_LOCAL_DENY;
3418 	} else {
3419 		/* Invalid Action */
3420 		return (Z_INVAL);
3421 	}
3422 	rctlblk_set_local_action(rctlblk, action, 0);
3423 	rctlblk_set_privilege(rctlblk, priv);
3424 	rctlblk_set_value(rctlblk, limit);
3425 	return (Z_OK);
3426 }
3427 
3428 static int
3429 rctl_check(const char *rctlname, void *arg)
3430 {
3431 	const char *attrname = arg;
3432 
3433 	/*
3434 	 * Returning 1 here is our signal to zonecfg_is_rctl() that it is
3435 	 * indeed an rctl name recognized by the system.
3436 	 */
3437 	return (strcmp(rctlname, attrname) == 0 ? 1 : 0);
3438 }
3439 
3440 boolean_t
3441 zonecfg_is_rctl(const char *name)
3442 {
3443 	return (rctl_walk(rctl_check, (void *)name) == 1);
3444 }
3445 
3446 boolean_t
3447 zonecfg_valid_rctlname(const char *name)
3448 {
3449 	const char *c;
3450 
3451 	if (strncmp(name, "zone.", sizeof ("zone.") - 1) != 0)
3452 		return (B_FALSE);
3453 	if (strlen(name) == sizeof ("zone.") - 1)
3454 		return (B_FALSE);
3455 	for (c = name + sizeof ("zone.") - 1; *c != '\0'; c++) {
3456 		if (!isalpha(*c) && *c != '-')
3457 			return (B_FALSE);
3458 	}
3459 	return (B_TRUE);
3460 }
3461 
3462 boolean_t
3463 zonecfg_valid_rctlblk(const rctlblk_t *rctlblk)
3464 {
3465 	rctl_priv_t priv = rctlblk_get_privilege((rctlblk_t *)rctlblk);
3466 	uint_t action = rctlblk_get_local_action((rctlblk_t *)rctlblk, NULL);
3467 
3468 	if (priv != RCPRIV_PRIVILEGED)
3469 		return (B_FALSE);
3470 	if (action != RCTL_LOCAL_NOACTION && action != RCTL_LOCAL_DENY)
3471 		return (B_FALSE);
3472 	return (B_TRUE);
3473 }
3474 
3475 boolean_t
3476 zonecfg_valid_rctl(const char *name, const rctlblk_t *rctlblk)
3477 {
3478 	rctlblk_t *current, *next;
3479 	rctl_qty_t limit = rctlblk_get_value((rctlblk_t *)rctlblk);
3480 	uint_t action = rctlblk_get_local_action((rctlblk_t *)rctlblk, NULL);
3481 	uint_t global_flags;
3482 
3483 	if (!zonecfg_valid_rctlblk(rctlblk))
3484 		return (B_FALSE);
3485 	if (!zonecfg_valid_rctlname(name))
3486 		return (B_FALSE);
3487 
3488 	current = alloca(rctlblk_size());
3489 	if (getrctl(name, NULL, current, RCTL_FIRST) != 0)
3490 		return (B_TRUE);	/* not an rctl on this system */
3491 	/*
3492 	 * Make sure the proposed value isn't greater than the current system
3493 	 * value.
3494 	 */
3495 	next = alloca(rctlblk_size());
3496 	while (rctlblk_get_privilege(current) != RCPRIV_SYSTEM) {
3497 		rctlblk_t *tmp;
3498 
3499 		if (getrctl(name, current, next, RCTL_NEXT) != 0)
3500 			return (B_FALSE);	/* shouldn't happen */
3501 		tmp = current;
3502 		current = next;
3503 		next = tmp;
3504 	}
3505 	if (limit > rctlblk_get_value(current))
3506 		return (B_FALSE);
3507 
3508 	/*
3509 	 * Make sure the proposed action is allowed.
3510 	 */
3511 	global_flags = rctlblk_get_global_flags(current);
3512 	if ((global_flags & RCTL_GLOBAL_DENY_NEVER) &&
3513 	    action == RCTL_LOCAL_DENY)
3514 		return (B_FALSE);
3515 	if ((global_flags & RCTL_GLOBAL_DENY_ALWAYS) &&
3516 	    action == RCTL_LOCAL_NOACTION)
3517 		return (B_FALSE);
3518 
3519 	return (B_TRUE);
3520 }
3521