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