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