xref: /illumos-gate/usr/src/lib/libzonecfg/common/libzonecfg.c (revision 2caf0dcd2abc26b477e317999994020212790d38)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <libsysevent.h>
29 #include <pthread.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <fnmatch.h>
33 #include <strings.h>
34 #include <unistd.h>
35 #include <sys/stat.h>
36 #include <assert.h>
37 #include <libgen.h>
38 #include <libintl.h>
39 #include <alloca.h>
40 #include <ctype.h>
41 #include <sys/mntio.h>
42 #include <sys/mnttab.h>
43 #include <sys/types.h>
44 #include <sys/nvpair.h>
45 #include <sys/acl.h>
46 #include <ftw.h>
47 
48 #include <arpa/inet.h>
49 #include <netdb.h>
50 
51 #include <priv.h>
52 
53 #include <libxml/xmlmemory.h>
54 #include <libxml/parser.h>
55 
56 #include <libdevinfo.h>
57 #include <uuid/uuid.h>
58 
59 #include <dirent.h>
60 
61 #include <libzonecfg.h>
62 #include "zonecfg_impl.h"
63 
64 
65 #define	_PATH_TMPFILE	"/zonecfg.XXXXXX"
66 #define	ZONE_CB_RETRY_COUNT		10
67 #define	ZONE_EVENT_PING_SUBCLASS	"ping"
68 #define	ZONE_EVENT_PING_PUBLISHER	"solaris"
69 
70 /* Hard-code the DTD element/attribute/entity names just once, here. */
71 #define	DTD_ELEM_ATTR		(const xmlChar *) "attr"
72 #define	DTD_ELEM_COMMENT	(const xmlChar *) "comment"
73 #define	DTD_ELEM_DEVICE		(const xmlChar *) "device"
74 #define	DTD_ELEM_FS		(const xmlChar *) "filesystem"
75 #define	DTD_ELEM_FSOPTION	(const xmlChar *) "fsoption"
76 #define	DTD_ELEM_IPD		(const xmlChar *) "inherited-pkg-dir"
77 #define	DTD_ELEM_NET		(const xmlChar *) "network"
78 #define	DTD_ELEM_RCTL		(const xmlChar *) "rctl"
79 #define	DTD_ELEM_RCTLVALUE	(const xmlChar *) "rctl-value"
80 #define	DTD_ELEM_ZONE		(const xmlChar *) "zone"
81 #define	DTD_ELEM_DATASET	(const xmlChar *) "dataset"
82 #define	DTD_ELEM_PACKAGE	(const xmlChar *) "package"
83 #define	DTD_ELEM_PATCH		(const xmlChar *) "patch"
84 #define	DTD_ELEM_OBSOLETES	(const xmlChar *) "obsoletes"
85 #define	DTD_ELEM_INCOMPATIBLE	(const xmlChar *) "incompatible"
86 #define	DTD_ELEM_DEV_PERM	(const xmlChar *) "dev-perm"
87 
88 #define	DTD_ATTR_ACTION		(const xmlChar *) "action"
89 #define	DTD_ATTR_ADDRESS	(const xmlChar *) "address"
90 #define	DTD_ATTR_AUTOBOOT	(const xmlChar *) "autoboot"
91 #define	DTD_ATTR_DIR		(const xmlChar *) "directory"
92 #define	DTD_ATTR_LIMIT		(const xmlChar *) "limit"
93 #define	DTD_ATTR_MATCH		(const xmlChar *) "match"
94 #define	DTD_ATTR_NAME		(const xmlChar *) "name"
95 #define	DTD_ATTR_PHYSICAL	(const xmlChar *) "physical"
96 #define	DTD_ATTR_POOL		(const xmlChar *) "pool"
97 #define	DTD_ATTR_PRIV		(const xmlChar *) "priv"
98 #define	DTD_ATTR_RAW		(const xmlChar *) "raw"
99 #define	DTD_ATTR_SPECIAL	(const xmlChar *) "special"
100 #define	DTD_ATTR_TYPE		(const xmlChar *) "type"
101 #define	DTD_ATTR_VALUE		(const xmlChar *) "value"
102 #define	DTD_ATTR_ZONEPATH	(const xmlChar *) "zonepath"
103 #define	DTD_ATTR_VERSION	(const xmlChar *) "version"
104 #define	DTD_ATTR_ID		(const xmlChar *) "id"
105 #define	DTD_ATTR_UID		(const xmlChar *) "uid"
106 #define	DTD_ATTR_GID		(const xmlChar *) "gid"
107 #define	DTD_ATTR_MODE		(const xmlChar *) "mode"
108 #define	DTD_ATTR_ACL		(const xmlChar *) "acl"
109 
110 #define	DTD_ENTITY_BOOLEAN	"boolean"
111 #define	DTD_ENTITY_DEVPATH	"devpath"
112 #define	DTD_ENTITY_DRIVER	"driver"
113 #define	DTD_ENTITY_DRVMIN	"drv_min"
114 #define	DTD_ENTITY_FALSE	"false"
115 #define	DTD_ENTITY_INT		"int"
116 #define	DTD_ENTITY_STRING	"string"
117 #define	DTD_ENTITY_TRUE		"true"
118 #define	DTD_ENTITY_UINT		"uint"
119 
120 #define	DTD_ENTITY_BOOL_LEN	6	/* "false" */
121 
122 #define	DETACHED	"SUNWdetached.xml"
123 #define	ATTACH_FORCED	"SUNWattached.xml"
124 #define	PKG_PATH	"/var/sadm/pkg"
125 #define	CONTENTS_FILE	"/var/sadm/install/contents"
126 #define	ALL_ZONES	"SUNW_PKG_ALLZONES=true\n"
127 #define	THIS_ZONE	"SUNW_PKG_THISZONE=true\n"
128 #define	VERSION		"VERSION="
129 #define	PATCHLIST	"PATCHLIST="
130 #define	PATCHINFO	"PATCH_INFO_"
131 #define	PKGINFO_RD_LEN	128
132 
133 struct zone_dochandle {
134 	char		*zone_dh_rootdir;
135 	xmlDocPtr	zone_dh_doc;
136 	xmlNodePtr	zone_dh_cur;
137 	xmlNodePtr	zone_dh_top;
138 	boolean_t	zone_dh_newzone;
139 	boolean_t	zone_dh_snapshot;
140 	boolean_t	zone_dh_sw_inv;
141 	char		zone_dh_delete_name[ZONENAME_MAX];
142 };
143 
144 struct znotify {
145 	void * zn_private;
146 	evchan_t *zn_eventchan;
147 	int (*zn_callback)(const  char *zonename, zoneid_t zid,
148 	    const char *newstate, const char *oldstate, hrtime_t when, void *p);
149 	pthread_mutex_t zn_mutex;
150 	pthread_cond_t zn_cond;
151 	pthread_mutex_t zn_bigmutex;
152 	volatile enum {ZN_UNLOCKED, ZN_LOCKED, ZN_PING_INFLIGHT,
153 	    ZN_PING_RECEIVED} zn_state;
154 	char zn_subscriber_id[MAX_SUBID_LEN];
155 	volatile boolean_t zn_failed;
156 	int zn_failure_count;
157 };
158 
159 struct zone_pkginfo {
160 	boolean_t	zpi_all_zones;
161 	boolean_t	zpi_this_zone;
162 	int		zpi_patch_cnt;
163 	char		*zpi_version;
164 	char		**zpi_patchinfo;
165 };
166 
167 char *zonecfg_root = "";
168 
169 /*
170  * For functions which return int, which is most of the functions herein,
171  * the return values should be from the Z_foo set defined in <libzonecfg.h>.
172  * In some instances, we take pains mapping some libc errno values to Z_foo
173  * values from this set.
174  */
175 
176 /*
177  * Set the root (/) path for all zonecfg configuration files.  This is a
178  * private interface used by Live Upgrade extensions to access zone
179  * configuration inside mounted alternate boot environments.
180  */
181 void
182 zonecfg_set_root(const char *rootpath)
183 {
184 	if (*zonecfg_root != '\0')
185 		free(zonecfg_root);
186 	if (rootpath == NULL || rootpath[0] == '\0' || rootpath[1] == '\0' ||
187 	    (zonecfg_root = strdup(rootpath)) == NULL)
188 		zonecfg_root = "";
189 }
190 
191 const char *
192 zonecfg_get_root(void)
193 {
194 	return (zonecfg_root);
195 }
196 
197 boolean_t
198 zonecfg_in_alt_root(void)
199 {
200 	return (*zonecfg_root != '\0');
201 }
202 
203 /*
204  * Callers of the _file_path() functions are expected to have the second
205  * parameter be a (char foo[MAXPATHLEN]).
206  */
207 
208 static boolean_t
209 config_file_path(const char *zonename, char *answer)
210 {
211 	return (snprintf(answer, MAXPATHLEN, "%s%s/%s.xml", zonecfg_root,
212 	    ZONE_CONFIG_ROOT, zonename) < MAXPATHLEN);
213 }
214 
215 static boolean_t
216 snap_file_path(const char *zonename, char *answer)
217 {
218 	return (snprintf(answer, MAXPATHLEN, "%s%s/%s.snapshot.xml",
219 	    zonecfg_root, ZONE_SNAPSHOT_ROOT, zonename) < MAXPATHLEN);
220 }
221 
222 /*ARGSUSED*/
223 static void
224 zonecfg_error_func(void *ctx, const char *msg, ...)
225 {
226 	/*
227 	 * This function does nothing by design.  Its purpose is to prevent
228 	 * libxml from dumping unwanted messages to stdout/stderr.
229 	 */
230 }
231 
232 zone_dochandle_t
233 zonecfg_init_handle(void)
234 {
235 	zone_dochandle_t handle = calloc(1, sizeof (struct zone_dochandle));
236 	if (handle == NULL) {
237 		errno = Z_NOMEM;
238 		return (NULL);
239 	}
240 
241 	/* generic libxml initialization */
242 	xmlLineNumbersDefault(1);
243 	xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS;
244 	xmlDoValidityCheckingDefaultValue = 1;
245 	(void) xmlKeepBlanksDefault(0);
246 	xmlGetWarningsDefaultValue = 0;
247 	xmlSetGenericErrorFunc(NULL, zonecfg_error_func);
248 
249 	return (handle);
250 }
251 
252 int
253 zonecfg_check_handle(zone_dochandle_t handle)
254 {
255 	if (handle == NULL || handle->zone_dh_doc == NULL)
256 		return (Z_BAD_HANDLE);
257 	return (Z_OK);
258 }
259 
260 void
261 zonecfg_fini_handle(zone_dochandle_t handle)
262 {
263 	if (zonecfg_check_handle(handle) == Z_OK)
264 		xmlFreeDoc(handle->zone_dh_doc);
265 	if (handle != NULL)
266 		free(handle);
267 }
268 
269 static int
270 zonecfg_destroy_impl(char *filename)
271 {
272 	if (unlink(filename) == -1) {
273 		if (errno == EACCES)
274 			return (Z_ACCES);
275 		if (errno == ENOENT)
276 			return (Z_NO_ZONE);
277 		return (Z_MISC_FS);
278 	}
279 	return (Z_OK);
280 }
281 
282 int
283 zonecfg_destroy(const char *zonename, boolean_t force)
284 {
285 	char path[MAXPATHLEN];
286 	struct zoneent ze;
287 	int err, state_err;
288 	zone_state_t state;
289 
290 	if (!config_file_path(zonename, path))
291 		return (Z_MISC_FS);
292 
293 	state_err = zone_get_state((char *)zonename, &state);
294 	err = access(path, W_OK);
295 
296 	/*
297 	 * If there is no file, and no index entry, reliably indicate that no
298 	 * such zone exists.
299 	 */
300 	if ((state_err == Z_NO_ZONE) && (err == -1) && (errno == ENOENT))
301 		return (Z_NO_ZONE);
302 
303 	/*
304 	 * Handle any other filesystem related errors (except if the XML
305 	 * file is missing, which we treat silently), unless we're forcing,
306 	 * in which case we plow on.
307 	 */
308 	if (err == -1 && errno != ENOENT) {
309 		if (errno == EACCES)
310 			return (Z_ACCES);
311 		else if (!force)
312 			return (Z_MISC_FS);
313 	}
314 
315 	if (state > ZONE_STATE_INSTALLED)
316 		return (Z_BAD_ZONE_STATE);
317 
318 	if (!force && state > ZONE_STATE_CONFIGURED)
319 		return (Z_BAD_ZONE_STATE);
320 
321 	/*
322 	 * Index deletion succeeds even if the entry doesn't exist.  So this
323 	 * will fail only if we've had some more severe problem.
324 	 */
325 	bzero(&ze, sizeof (ze));
326 	(void) strlcpy(ze.zone_name, zonename, sizeof (ze.zone_name));
327 	if ((err = putzoneent(&ze, PZE_REMOVE)) != Z_OK)
328 		if (!force)
329 			return (err);
330 
331 	err = zonecfg_destroy_impl(path);
332 
333 	/*
334 	 * Treat failure to find the XML file silently, since, well, it's
335 	 * gone, and with the index file cleaned up, we're done.
336 	 */
337 	if (err == Z_OK || err == Z_NO_ZONE)
338 		return (Z_OK);
339 	return (err);
340 }
341 
342 int
343 zonecfg_destroy_snapshot(const char *zonename)
344 {
345 	char path[MAXPATHLEN];
346 
347 	if (!snap_file_path(zonename, path))
348 		return (Z_MISC_FS);
349 	return (zonecfg_destroy_impl(path));
350 }
351 
352 static int
353 getroot(zone_dochandle_t handle, xmlNodePtr *root)
354 {
355 	if (zonecfg_check_handle(handle) == Z_BAD_HANDLE)
356 		return (Z_BAD_HANDLE);
357 
358 	*root = xmlDocGetRootElement(handle->zone_dh_doc);
359 
360 	if (*root == NULL)
361 		return (Z_EMPTY_DOCUMENT);
362 
363 	if (xmlStrcmp((*root)->name, DTD_ELEM_ZONE))
364 		return (Z_WRONG_DOC_TYPE);
365 
366 	return (Z_OK);
367 }
368 
369 static int
370 operation_prep(zone_dochandle_t handle)
371 {
372 	xmlNodePtr root;
373 	int err;
374 
375 	if ((err = getroot(handle, &root)) != 0)
376 		return (err);
377 
378 	handle->zone_dh_cur = root;
379 	handle->zone_dh_top = root;
380 	return (Z_OK);
381 }
382 
383 static int
384 getrootattr(zone_dochandle_t handle, const xmlChar *propname,
385     char *propval, size_t propsize)
386 {
387 	xmlNodePtr root;
388 	xmlChar *property;
389 	size_t srcsize;
390 	int err;
391 
392 	if ((err = getroot(handle, &root)) != 0)
393 		return (err);
394 
395 	if ((property = xmlGetProp(root, propname)) == NULL)
396 		return (Z_BAD_PROPERTY);
397 	srcsize = strlcpy(propval, (char *)property, propsize);
398 	xmlFree(property);
399 	if (srcsize >= propsize)
400 		return (Z_TOO_BIG);
401 	return (Z_OK);
402 }
403 
404 static int
405 setrootattr(zone_dochandle_t handle, const xmlChar *propname,
406     const char *propval)
407 {
408 	int err;
409 	xmlNodePtr root;
410 
411 	if (propval == NULL)
412 		return (Z_INVAL);
413 
414 	if ((err = getroot(handle, &root)) != Z_OK)
415 		return (err);
416 
417 	if (xmlSetProp(root, propname, (const xmlChar *) propval) == NULL)
418 		return (Z_INVAL);
419 	return (Z_OK);
420 }
421 
422 static void
423 addcomment(zone_dochandle_t handle, const char *comment)
424 {
425 	xmlNodePtr node;
426 	node = xmlNewComment((xmlChar *) comment);
427 
428 	if (node != NULL)
429 		(void) xmlAddPrevSibling(handle->zone_dh_top, node);
430 }
431 
432 static void
433 stripcomments(zone_dochandle_t handle)
434 {
435 	xmlDocPtr top;
436 	xmlNodePtr child, next;
437 
438 	top = handle->zone_dh_doc;
439 	for (child = top->xmlChildrenNode; child != NULL; child = next) {
440 		next = child->next;
441 		if (child->name == NULL)
442 			continue;
443 		if (xmlStrcmp(child->name, DTD_ELEM_COMMENT) == 0) {
444 			next = child->next;
445 			xmlUnlinkNode(child);
446 			xmlFreeNode(child);
447 		}
448 	}
449 }
450 
451 static void
452 strip_sw_inv(zone_dochandle_t handle)
453 {
454 	xmlNodePtr root, child, next;
455 
456 	root = xmlDocGetRootElement(handle->zone_dh_doc);
457 	for (child = root->xmlChildrenNode; child != NULL; child = next) {
458 		next = child->next;
459 		if (child->name == NULL)
460 			continue;
461 		if (xmlStrcmp(child->name, DTD_ELEM_PACKAGE) == 0 ||
462 		    xmlStrcmp(child->name, DTD_ELEM_PATCH) == 0) {
463 			next = child->next;
464 			xmlUnlinkNode(child);
465 			xmlFreeNode(child);
466 		}
467 	}
468 }
469 
470 static int
471 zonecfg_get_handle_impl(const char *zonename, const char *filename,
472     zone_dochandle_t handle)
473 {
474 	xmlValidCtxtPtr cvp;
475 	struct stat statbuf;
476 	int valid;
477 
478 	if (zonename == NULL)
479 		return (Z_NO_ZONE);
480 	if ((handle->zone_dh_doc = xmlParseFile(filename)) == NULL) {
481 		/* distinguish file not found vs. found but not parsed */
482 		if (stat(filename, &statbuf) == 0)
483 			return (Z_INVALID_DOCUMENT);
484 		return (Z_NO_ZONE);
485 	}
486 	if ((cvp = xmlNewValidCtxt()) == NULL)
487 		return (Z_NOMEM);
488 	cvp->error = zonecfg_error_func;
489 	cvp->warning = zonecfg_error_func;
490 	valid = xmlValidateDocument(cvp, handle->zone_dh_doc);
491 	xmlFreeValidCtxt(cvp);
492 	if (valid == 0)
493 		return (Z_INVALID_DOCUMENT);
494 
495 	/* delete any comments such as inherited Sun copyright / ident str */
496 	stripcomments(handle);
497 	return (Z_OK);
498 }
499 
500 int
501 zonecfg_get_handle(const char *zonename, zone_dochandle_t handle)
502 {
503 	char path[MAXPATHLEN];
504 
505 	if (!config_file_path(zonename, path))
506 		return (Z_MISC_FS);
507 	handle->zone_dh_newzone = B_FALSE;
508 
509 	return (zonecfg_get_handle_impl(zonename, path, handle));
510 }
511 
512 int
513 zonecfg_get_attach_handle(const char *path, const char *zonename,
514     boolean_t preserve_sw, zone_dochandle_t handle)
515 {
516 	char		migpath[MAXPATHLEN];
517 	int		err;
518 	struct stat	buf;
519 
520 	if (snprintf(migpath, sizeof (migpath), "%s/root", path) >=
521 	    sizeof (migpath))
522 		return (Z_NOMEM);
523 
524 	if (stat(migpath, &buf) == -1 || !S_ISDIR(buf.st_mode))
525 		return (Z_NO_ZONE);
526 
527 	if (snprintf(migpath, sizeof (migpath), "%s/%s", path, DETACHED) >=
528 	    sizeof (migpath))
529 		return (Z_NOMEM);
530 
531 	if ((err = zonecfg_get_handle_impl(zonename, migpath, handle)) != Z_OK)
532 		return (err);
533 
534 	if (!preserve_sw)
535 		strip_sw_inv(handle);
536 
537 	handle->zone_dh_newzone = B_TRUE;
538 	if ((err = setrootattr(handle, DTD_ATTR_ZONEPATH, path)) != Z_OK)
539 		return (err);
540 
541 	return (setrootattr(handle, DTD_ATTR_NAME, zonename));
542 }
543 
544 int
545 zonecfg_get_snapshot_handle(const char *zonename, zone_dochandle_t handle)
546 {
547 	char path[MAXPATHLEN];
548 
549 	if (!snap_file_path(zonename, path))
550 		return (Z_MISC_FS);
551 	handle->zone_dh_newzone = B_FALSE;
552 	return (zonecfg_get_handle_impl(zonename, path, handle));
553 }
554 
555 int
556 zonecfg_get_template_handle(const char *template, const char *zonename,
557     zone_dochandle_t handle)
558 {
559 	char path[MAXPATHLEN];
560 	int err;
561 
562 	if (!config_file_path(template, path))
563 		return (Z_MISC_FS);
564 
565 	if ((err = zonecfg_get_handle_impl(template, path, handle)) != Z_OK)
566 		return (err);
567 	handle->zone_dh_newzone = B_TRUE;
568 	return (setrootattr(handle, DTD_ATTR_NAME, zonename));
569 }
570 
571 static boolean_t
572 is_renaming(zone_dochandle_t handle)
573 {
574 	if (handle->zone_dh_newzone)
575 		return (B_FALSE);
576 	if (strlen(handle->zone_dh_delete_name) > 0)
577 		return (B_TRUE);
578 	return (B_FALSE);
579 }
580 
581 static boolean_t
582 is_new(zone_dochandle_t handle)
583 {
584 	return (handle->zone_dh_newzone || handle->zone_dh_snapshot);
585 }
586 
587 static boolean_t
588 is_snapshot(zone_dochandle_t handle)
589 {
590 	return (handle->zone_dh_snapshot);
591 }
592 
593 /*
594  * It would be great to be able to use libc's ctype(3c) macros, but we
595  * can't, as they are locale sensitive, and it would break our limited thread
596  * safety if this routine had to change the app locale on the fly.
597  */
598 int
599 zonecfg_validate_zonename(const char *zone)
600 {
601 	int i;
602 
603 	if (strcmp(zone, GLOBAL_ZONENAME) == 0)
604 		return (Z_BOGUS_ZONE_NAME);
605 
606 	if (strlen(zone) >= ZONENAME_MAX)
607 		return (Z_BOGUS_ZONE_NAME);
608 
609 	if (!((zone[0] >= 'a' && zone[0] <= 'z') ||
610 	    (zone[0] >= 'A' && zone[0] <= 'Z') ||
611 	    (zone[0] >= '0' && zone[0] <= '9')))
612 		return (Z_BOGUS_ZONE_NAME);
613 
614 	for (i = 1; zone[i] != '\0'; i++) {
615 		if (!((zone[i] >= 'a' && zone[i] <= 'z') ||
616 		    (zone[i] >= 'A' && zone[i] <= 'Z') ||
617 		    (zone[i] >= '0' && zone[i] <= '9') ||
618 		    (zone[i] == '-') || (zone[i] == '_') || (zone[i] == '.')))
619 			return (Z_BOGUS_ZONE_NAME);
620 	}
621 
622 	return (Z_OK);
623 }
624 
625 /*
626  * Changing the zone name requires us to track both the old and new
627  * name of the zone until commit time.
628  */
629 int
630 zonecfg_get_name(zone_dochandle_t handle, char *name, size_t namesize)
631 {
632 	return (getrootattr(handle, DTD_ATTR_NAME, name, namesize));
633 }
634 
635 int
636 zonecfg_set_name(zone_dochandle_t handle, char *name)
637 {
638 	zone_state_t state;
639 	char curname[ZONENAME_MAX], old_delname[ZONENAME_MAX];
640 	int err;
641 
642 	if ((err = getrootattr(handle, DTD_ATTR_NAME, curname,
643 	    sizeof (curname))) != Z_OK)
644 		return (err);
645 
646 	if (strcmp(name, curname) == 0)
647 		return (Z_OK);
648 
649 	/*
650 	 * Switching zone names to one beginning with SUNW is not permitted.
651 	 */
652 	if (strncmp(name, "SUNW", 4) == 0)
653 		return (Z_BOGUS_ZONE_NAME);
654 
655 	if ((err = zonecfg_validate_zonename(name)) != Z_OK)
656 		return (err);
657 
658 	/*
659 	 * Setting the name back to the original name (effectively a revert of
660 	 * the name) is fine.  But if we carry on, we'll falsely identify the
661 	 * name as "in use," so special case here.
662 	 */
663 	if (strcmp(name, handle->zone_dh_delete_name) == 0) {
664 		err = setrootattr(handle, DTD_ATTR_NAME, name);
665 		handle->zone_dh_delete_name[0] = '\0';
666 		return (err);
667 	}
668 
669 	/* Check to see if new name chosen is already in use */
670 	if (zone_get_state(name, &state) != Z_NO_ZONE)
671 		return (Z_NAME_IN_USE);
672 
673 	/*
674 	 * If this isn't already "new" or in a renaming transition, then
675 	 * we're initiating a rename here; so stash the "delete name"
676 	 * (i.e. the name of the zone we'll be removing) for the rename.
677 	 */
678 	(void) strlcpy(old_delname, handle->zone_dh_delete_name,
679 	    sizeof (old_delname));
680 	if (!is_new(handle) && !is_renaming(handle)) {
681 		/*
682 		 * Name change is allowed only when the zone we're altering
683 		 * is not ready or running.
684 		 */
685 		err = zone_get_state(curname, &state);
686 		if (err == Z_OK) {
687 			if (state > ZONE_STATE_INSTALLED)
688 				return (Z_BAD_ZONE_STATE);
689 		} else if (err != Z_NO_ZONE) {
690 			return (err);
691 		}
692 
693 		(void) strlcpy(handle->zone_dh_delete_name, curname,
694 		    sizeof (handle->zone_dh_delete_name));
695 		assert(is_renaming(handle));
696 	} else if (is_renaming(handle)) {
697 		err = zone_get_state(handle->zone_dh_delete_name, &state);
698 		if (err == Z_OK) {
699 			if (state > ZONE_STATE_INSTALLED)
700 				return (Z_BAD_ZONE_STATE);
701 		} else if (err != Z_NO_ZONE) {
702 			return (err);
703 		}
704 	}
705 
706 	if ((err = setrootattr(handle, DTD_ATTR_NAME, name)) != Z_OK) {
707 		/*
708 		 * Restore the deletename to whatever it was at the
709 		 * top of the routine, since we've had a failure.
710 		 */
711 		(void) strlcpy(handle->zone_dh_delete_name, old_delname,
712 		    sizeof (handle->zone_dh_delete_name));
713 		return (err);
714 	}
715 
716 	return (Z_OK);
717 }
718 
719 int
720 zonecfg_get_zonepath(zone_dochandle_t handle, char *path, size_t pathsize)
721 {
722 	size_t len;
723 
724 	if ((len = strlcpy(path, zonecfg_root, pathsize)) >= pathsize)
725 		return (Z_TOO_BIG);
726 	return (getrootattr(handle, DTD_ATTR_ZONEPATH, path + len,
727 	    pathsize - len));
728 }
729 
730 int
731 zonecfg_set_zonepath(zone_dochandle_t handle, char *zonepath)
732 {
733 	return (setrootattr(handle, DTD_ATTR_ZONEPATH, zonepath));
734 }
735 
736 int
737 zonecfg_get_autoboot(zone_dochandle_t handle, boolean_t *autoboot)
738 {
739 	char autobootstr[DTD_ENTITY_BOOL_LEN];
740 	int ret;
741 
742 	if ((ret = getrootattr(handle, DTD_ATTR_AUTOBOOT, autobootstr,
743 	    sizeof (autobootstr))) != Z_OK)
744 		return (ret);
745 
746 	if (strcmp(autobootstr, DTD_ENTITY_TRUE) == 0)
747 		*autoboot = B_TRUE;
748 	else if (strcmp(autobootstr, DTD_ENTITY_FALSE) == 0)
749 		*autoboot = B_FALSE;
750 	else
751 		ret = Z_BAD_PROPERTY;
752 	return (ret);
753 }
754 
755 int
756 zonecfg_set_autoboot(zone_dochandle_t handle, boolean_t autoboot)
757 {
758 	return (setrootattr(handle, DTD_ATTR_AUTOBOOT,
759 	    autoboot ? DTD_ENTITY_TRUE : DTD_ENTITY_FALSE));
760 }
761 
762 int
763 zonecfg_get_pool(zone_dochandle_t handle, char *pool, size_t poolsize)
764 {
765 	return (getrootattr(handle, DTD_ATTR_POOL, pool, poolsize));
766 }
767 
768 int
769 zonecfg_set_pool(zone_dochandle_t handle, char *pool)
770 {
771 	return (setrootattr(handle, DTD_ATTR_POOL, pool));
772 }
773 
774 /*
775  * /etc/zones/index caches a vital piece of information which is also
776  * in the <zonename>.xml file: the path to the zone.  This is for performance,
777  * since we need to walk all zonepath's in order to be able to detect conflicts
778  * (see crosscheck_zonepaths() in the zoneadm command).
779  *
780  * An additional complexity is that when doing a rename, we'd like the entire
781  * index update operation (rename, and potential state changes) to be atomic.
782  * In general, the operation of this function should succeed or fail as
783  * a unit.
784  */
785 int
786 zonecfg_refresh_index_file(zone_dochandle_t handle)
787 {
788 	char name[ZONENAME_MAX], zonepath[MAXPATHLEN];
789 	struct zoneent ze;
790 	int err;
791 	int opcode;
792 	char *zn;
793 
794 	bzero(&ze, sizeof (ze));
795 	ze.zone_state = -1;	/* Preserve existing state in index */
796 
797 	if ((err = zonecfg_get_name(handle, name, sizeof (name))) != Z_OK)
798 		return (err);
799 	(void) strlcpy(ze.zone_name, name, sizeof (ze.zone_name));
800 
801 	if ((err = zonecfg_get_zonepath(handle, zonepath,
802 	    sizeof (zonepath))) != Z_OK)
803 		return (err);
804 	(void) strlcpy(ze.zone_path, zonepath, sizeof (ze.zone_path));
805 
806 	if (is_renaming(handle)) {
807 		opcode = PZE_MODIFY;
808 		(void) strlcpy(ze.zone_name, handle->zone_dh_delete_name,
809 		    sizeof (ze.zone_name));
810 		(void) strlcpy(ze.zone_newname, name, sizeof (ze.zone_newname));
811 	} else if (is_new(handle)) {
812 		FILE *cookie;
813 		/*
814 		 * Be tolerant of the zone already existing in the index file,
815 		 * since we might be forcibly overwriting an existing
816 		 * configuration with a new one (for example 'create -F'
817 		 * in zonecfg).
818 		 */
819 		opcode = PZE_ADD;
820 		cookie = setzoneent();
821 		while ((zn = getzoneent(cookie)) != NULL) {
822 			if (strcmp(zn, name) == 0) {
823 				opcode = PZE_MODIFY;
824 				free(zn);
825 				break;
826 			}
827 			free(zn);
828 		}
829 		endzoneent(cookie);
830 		ze.zone_state = ZONE_STATE_CONFIGURED;
831 	} else {
832 		opcode = PZE_MODIFY;
833 	}
834 
835 	if ((err = putzoneent(&ze, opcode)) != Z_OK)
836 		return (err);
837 
838 	return (Z_OK);
839 }
840 
841 /*
842  * The goal of this routine is to cause the index file update and the
843  * document save to happen as an atomic operation.  We do the document
844  * first, saving a backup copy using a hard link; if that succeeds, we go
845  * on to the index.  If that fails, we roll the document back into place.
846  *
847  * Strategy:
848  *
849  * New zone 'foo' configuration:
850  * 	Create tmpfile (zonecfg.xxxxxx)
851  * 	Write XML to tmpfile
852  * 	Rename tmpfile to xmlfile (zonecfg.xxxxxx -> foo.xml)
853  * 	Add entry to index file
854  * 	If it fails, delete foo.xml, leaving nothing behind.
855  *
856  * Save existing zone 'foo':
857  * 	Make backup of foo.xml -> .backup
858  * 	Create tmpfile (zonecfg.xxxxxx)
859  * 	Write XML to tmpfile
860  * 	Rename tmpfile to xmlfile (zonecfg.xxxxxx -> foo.xml)
861  * 	Modify index file as needed
862  * 	If it fails, recover from .backup -> foo.xml
863  *
864  * Rename 'foo' to 'bar':
865  * 	Create tmpfile (zonecfg.xxxxxx)
866  * 	Write XML to tmpfile
867  * 	Rename tmpfile to xmlfile (zonecfg.xxxxxx -> bar.xml)
868  * 	Add entry for 'bar' to index file, Remove entry for 'foo' (refresh)
869  * 	If it fails, delete bar.xml; foo.xml is left behind.
870  */
871 static int
872 zonecfg_save_impl(zone_dochandle_t handle, char *filename)
873 {
874 	char tmpfile[MAXPATHLEN];
875 	char bakdir[MAXPATHLEN], bakbase[MAXPATHLEN], bakfile[MAXPATHLEN];
876 	int tmpfd, err;
877 	xmlValidCtxt cvp = { NULL };
878 	boolean_t backup;
879 
880 	(void) strlcpy(tmpfile, filename, sizeof (tmpfile));
881 	(void) dirname(tmpfile);
882 	(void) strlcat(tmpfile, _PATH_TMPFILE, sizeof (tmpfile));
883 
884 	tmpfd = mkstemp(tmpfile);
885 	if (tmpfd == -1) {
886 		(void) unlink(tmpfile);
887 		return (Z_TEMP_FILE);
888 	}
889 	(void) close(tmpfd);
890 
891 	cvp.error = zonecfg_error_func;
892 	cvp.warning = zonecfg_error_func;
893 
894 	/*
895 	 * We do a final validation of the document-- but the library has
896 	 * malfunctioned if it fails to validate, so it's an assert.
897 	 */
898 	assert(xmlValidateDocument(&cvp, handle->zone_dh_doc) != 0);
899 
900 	if (xmlSaveFormatFile(tmpfile, handle->zone_dh_doc, 1) <= 0)
901 		goto err;
902 
903 	(void) chmod(tmpfile, 0644);
904 
905 	/*
906 	 * In the event we are doing a standard save, hard link a copy of the
907 	 * original file in .backup.<pid>.filename so we can restore it if
908 	 * something goes wrong.
909 	 */
910 	if (!is_new(handle) && !is_renaming(handle)) {
911 		backup = B_TRUE;
912 
913 		(void) strlcpy(bakdir, filename, sizeof (bakdir));
914 		(void) strlcpy(bakbase, filename, sizeof (bakbase));
915 		(void) snprintf(bakfile, sizeof (bakfile), "%s/.backup.%d.%s",
916 		    dirname(bakdir), getpid(), basename(bakbase));
917 
918 		if (link(filename, bakfile) == -1) {
919 			err = errno;
920 			(void) unlink(tmpfile);
921 			if (errno == EACCES)
922 				return (Z_ACCES);
923 			return (Z_MISC_FS);
924 		}
925 	}
926 
927 	/*
928 	 * Move the new document over top of the old.
929 	 * i.e.:   zonecfg.XXXXXX  ->  myzone.xml
930 	 */
931 	if (rename(tmpfile, filename) == -1) {
932 		err = errno;
933 		(void) unlink(tmpfile);
934 		if (backup)
935 			(void) unlink(bakfile);
936 		if (err == EACCES)
937 			return (Z_ACCES);
938 		return (Z_MISC_FS);
939 	}
940 
941 	/*
942 	 * If this is a snapshot, we're done-- don't add an index entry.
943 	 */
944 	if (is_snapshot(handle))
945 		return (Z_OK);
946 
947 	/* now update the index file to reflect whatever we just did */
948 	if ((err = zonecfg_refresh_index_file(handle)) != Z_OK) {
949 		if (backup) {
950 			/*
951 			 * Try to restore from our backup.
952 			 */
953 			(void) unlink(filename);
954 			(void) rename(bakfile, filename);
955 		} else {
956 			/*
957 			 * Either the zone is new, in which case we can delete
958 			 * new.xml, or we're doing a rename, so ditto.
959 			 */
960 			assert(is_new(handle) || is_renaming(handle));
961 			(void) unlink(filename);
962 		}
963 		return (Z_UPDATING_INDEX);
964 	}
965 
966 	if (backup)
967 		(void) unlink(bakfile);
968 
969 	return (Z_OK);
970 
971 err:
972 	(void) unlink(tmpfile);
973 	return (Z_SAVING_FILE);
974 }
975 
976 int
977 zonecfg_save(zone_dochandle_t handle)
978 {
979 	char zname[ZONENAME_MAX], path[MAXPATHLEN];
980 	char delpath[MAXPATHLEN];
981 	int err = Z_SAVING_FILE;
982 
983 	if (zonecfg_check_handle(handle) != Z_OK)
984 		return (Z_BAD_HANDLE);
985 
986 	/*
987 	 * We don't support saving snapshots or a tree containing a sw
988 	 * inventory at this time.
989 	 */
990 	if (handle->zone_dh_snapshot || handle->zone_dh_sw_inv)
991 		return (Z_INVAL);
992 
993 	if ((err = zonecfg_get_name(handle, zname, sizeof (zname))) != Z_OK)
994 		return (err);
995 
996 	if (!config_file_path(zname, path))
997 		return (Z_MISC_FS);
998 
999 	addcomment(handle, "\n    DO NOT EDIT THIS "
1000 	    "FILE.  Use zonecfg(1M) instead.\n");
1001 
1002 	err = zonecfg_save_impl(handle, path);
1003 
1004 	stripcomments(handle);
1005 
1006 	if (err != Z_OK)
1007 		return (err);
1008 
1009 	handle->zone_dh_newzone = B_FALSE;
1010 
1011 	if (is_renaming(handle)) {
1012 		if (config_file_path(handle->zone_dh_delete_name, delpath))
1013 			(void) unlink(delpath);
1014 		handle->zone_dh_delete_name[0] = '\0';
1015 	}
1016 
1017 	return (Z_OK);
1018 }
1019 
1020 int
1021 zonecfg_detach_save(zone_dochandle_t handle)
1022 {
1023 	char zname[ZONENAME_MAX];
1024 	char path[MAXPATHLEN];
1025 	char migpath[MAXPATHLEN];
1026 	xmlValidCtxt cvp = { NULL };
1027 	int err = Z_SAVING_FILE;
1028 
1029 	if (zonecfg_check_handle(handle) != Z_OK)
1030 		return (Z_BAD_HANDLE);
1031 
1032 	/*
1033 	 * We can only detach if we have taken a sw inventory.
1034 	 */
1035 	if (!handle->zone_dh_sw_inv)
1036 		return (Z_INVAL);
1037 
1038 	if ((err = zonecfg_get_name(handle, zname, sizeof (zname))) != Z_OK)
1039 		return (err);
1040 
1041 	if ((err = zone_get_zonepath(zname, path, sizeof (path))) != Z_OK)
1042 		return (err);
1043 
1044 	if (snprintf(migpath, sizeof (migpath), "%s/%s", path, DETACHED) >=
1045 	    sizeof (migpath))
1046 		return (Z_NOMEM);
1047 
1048 	if ((err = operation_prep(handle)) != Z_OK)
1049 		return (err);
1050 
1051 	addcomment(handle, "\n    DO NOT EDIT THIS FILE.  "
1052 	    "Use zonecfg(1M) and zoneadm(1M) attach.\n");
1053 
1054 	cvp.error = zonecfg_error_func;
1055 	cvp.warning = zonecfg_error_func;
1056 
1057 	/*
1058 	 * We do a final validation of the document-- but the library has
1059 	 * malfunctioned if it fails to validate, so it's an assert.
1060 	 */
1061 	assert(xmlValidateDocument(&cvp, handle->zone_dh_doc) != 0);
1062 
1063 	if (xmlSaveFormatFile(migpath, handle->zone_dh_doc, 1) <= 0)
1064 		return (Z_SAVING_FILE);
1065 
1066 	(void) chmod(migpath, 0644);
1067 
1068 	stripcomments(handle);
1069 
1070 	handle->zone_dh_newzone = B_FALSE;
1071 
1072 	return (Z_OK);
1073 }
1074 
1075 boolean_t
1076 zonecfg_detached(const char *path)
1077 {
1078 	char		migpath[MAXPATHLEN];
1079 	struct stat	buf;
1080 
1081 	if (snprintf(migpath, sizeof (migpath), "%s/%s", path, DETACHED) >=
1082 	    sizeof (migpath))
1083 		return (B_FALSE);
1084 
1085 	if (stat(migpath, &buf) != -1)
1086 		return (B_TRUE);
1087 
1088 	return (B_FALSE);
1089 }
1090 
1091 void
1092 zonecfg_rm_detached(zone_dochandle_t handle, boolean_t forced)
1093 {
1094 	char zname[ZONENAME_MAX];
1095 	char path[MAXPATHLEN];
1096 	char detached[MAXPATHLEN];
1097 	char attached[MAXPATHLEN];
1098 
1099 	if (zonecfg_check_handle(handle) != Z_OK)
1100 		return;
1101 
1102 	if (zonecfg_get_name(handle, zname, sizeof (zname)) != Z_OK)
1103 		return;
1104 
1105 	if (zone_get_zonepath(zname, path, sizeof (path)) != Z_OK)
1106 		return;
1107 
1108 	(void) snprintf(detached, sizeof (detached), "%s/%s", path, DETACHED);
1109 	(void) snprintf(attached, sizeof (attached), "%s/%s", path,
1110 	    ATTACH_FORCED);
1111 
1112 	if (forced) {
1113 		(void) rename(detached, attached);
1114 	} else {
1115 		(void) unlink(attached);
1116 		(void) unlink(detached);
1117 	}
1118 }
1119 
1120 /*
1121  * Special case: if access(2) fails with ENOENT, then try again using
1122  * ZONE_CONFIG_ROOT instead of config_file_path(zonename).  This is how we
1123  * work around the case of a config file which has not been created yet:
1124  * the user will need access to the directory so use that as a heuristic.
1125  */
1126 
1127 int
1128 zonecfg_access(const char *zonename, int amode)
1129 {
1130 	char path[MAXPATHLEN];
1131 
1132 	if (!config_file_path(zonename, path))
1133 		return (Z_INVAL);
1134 	if (access(path, amode) == 0)
1135 		return (Z_OK);
1136 	if (errno == ENOENT) {
1137 		if (snprintf(path, sizeof (path), "%s%s", zonecfg_root,
1138 		    ZONE_CONFIG_ROOT) >= sizeof (path))
1139 			return (Z_INVAL);
1140 		if (access(path, amode) == 0)
1141 			return (Z_OK);
1142 	}
1143 	if (errno == EACCES)
1144 		return (Z_ACCES);
1145 	if (errno == EINVAL)
1146 		return (Z_INVAL);
1147 	return (Z_MISC_FS);
1148 }
1149 
1150 int
1151 zonecfg_create_snapshot(const char *zonename)
1152 {
1153 	zone_dochandle_t handle;
1154 	char path[MAXPATHLEN], zonepath[MAXPATHLEN], rpath[MAXPATHLEN];
1155 	int error = Z_OK, res;
1156 
1157 	if ((handle = zonecfg_init_handle()) == NULL) {
1158 		return (Z_NOMEM);
1159 	}
1160 
1161 	handle->zone_dh_newzone = B_TRUE;
1162 	handle->zone_dh_snapshot = B_TRUE;
1163 
1164 	if ((error = zonecfg_get_handle(zonename, handle)) != Z_OK)
1165 		goto out;
1166 	if ((error = operation_prep(handle)) != Z_OK)
1167 		goto out;
1168 	error = zonecfg_get_zonepath(handle, zonepath, sizeof (zonepath));
1169 	if (error != Z_OK)
1170 		goto out;
1171 	if ((res = resolvepath(zonepath, rpath, sizeof (rpath))) == -1) {
1172 		error = Z_RESOLVED_PATH;
1173 		goto out;
1174 	}
1175 	/*
1176 	 * If the resolved path is not the same as the original path, then
1177 	 * save the resolved path in the snapshot, thus preventing any
1178 	 * potential problems down the line when zoneadmd goes to unmount
1179 	 * file systems and depends on initial string matches with resolved
1180 	 * paths.
1181 	 */
1182 	rpath[res] = '\0';
1183 	if (strcmp(zonepath, rpath) != 0) {
1184 		if ((error = zonecfg_set_zonepath(handle, rpath)) != Z_OK)
1185 			goto out;
1186 	}
1187 	if (snprintf(path, sizeof (path), "%s%s", zonecfg_root,
1188 	    ZONE_SNAPSHOT_ROOT) >= sizeof (path)) {
1189 		error = Z_MISC_FS;
1190 		goto out;
1191 	}
1192 	if ((mkdir(path, S_IRWXU) == -1) && (errno != EEXIST)) {
1193 		error = Z_MISC_FS;
1194 		goto out;
1195 	}
1196 
1197 	if (!snap_file_path(zonename, path)) {
1198 		error = Z_MISC_FS;
1199 		goto out;
1200 	}
1201 
1202 	addcomment(handle, "\n    DO NOT EDIT THIS FILE.  "
1203 	    "It is a snapshot of running zone state.\n");
1204 
1205 	error = zonecfg_save_impl(handle, path);
1206 
1207 	stripcomments(handle);
1208 
1209 out:
1210 	zonecfg_fini_handle(handle);
1211 	return (error);
1212 }
1213 
1214 static int
1215 newprop(xmlNodePtr node, const xmlChar *attrname, char *src)
1216 {
1217 	xmlAttrPtr newattr;
1218 
1219 	newattr = xmlNewProp(node, attrname, (xmlChar *)src);
1220 	if (newattr == NULL) {
1221 		xmlUnlinkNode(node);
1222 		xmlFreeNode(node);
1223 		return (Z_BAD_PROPERTY);
1224 	}
1225 	return (Z_OK);
1226 }
1227 
1228 static int
1229 zonecfg_add_filesystem_core(zone_dochandle_t handle, struct zone_fstab *tabptr)
1230 {
1231 	xmlNodePtr newnode, cur = handle->zone_dh_cur, options_node;
1232 	zone_fsopt_t *ptr;
1233 	int err;
1234 
1235 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_FS, NULL);
1236 	if ((err = newprop(newnode, DTD_ATTR_SPECIAL,
1237 	    tabptr->zone_fs_special)) != Z_OK)
1238 		return (err);
1239 	if (tabptr->zone_fs_raw[0] != '\0' &&
1240 	    (err = newprop(newnode, DTD_ATTR_RAW, tabptr->zone_fs_raw)) != Z_OK)
1241 		return (err);
1242 	if ((err = newprop(newnode, DTD_ATTR_DIR, tabptr->zone_fs_dir)) != Z_OK)
1243 		return (err);
1244 	if ((err = newprop(newnode, DTD_ATTR_TYPE,
1245 	    tabptr->zone_fs_type)) != Z_OK)
1246 		return (err);
1247 	if (tabptr->zone_fs_options != NULL) {
1248 		for (ptr = tabptr->zone_fs_options; ptr != NULL;
1249 		    ptr = ptr->zone_fsopt_next) {
1250 			options_node = xmlNewTextChild(newnode, NULL,
1251 			    DTD_ELEM_FSOPTION, NULL);
1252 			if ((err = newprop(options_node, DTD_ATTR_NAME,
1253 			    ptr->zone_fsopt_opt)) != Z_OK)
1254 				return (err);
1255 		}
1256 	}
1257 	return (Z_OK);
1258 }
1259 
1260 int
1261 zonecfg_add_filesystem(zone_dochandle_t handle, struct zone_fstab *tabptr)
1262 {
1263 	int err;
1264 
1265 	if (tabptr == NULL)
1266 		return (Z_INVAL);
1267 
1268 	if ((err = operation_prep(handle)) != Z_OK)
1269 		return (err);
1270 
1271 	if ((err = zonecfg_add_filesystem_core(handle, tabptr)) != Z_OK)
1272 		return (err);
1273 
1274 	return (Z_OK);
1275 }
1276 
1277 static int
1278 zonecfg_add_ipd_core(zone_dochandle_t handle, struct zone_fstab *tabptr)
1279 {
1280 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
1281 	int err;
1282 
1283 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_IPD, NULL);
1284 	if ((err = newprop(newnode, DTD_ATTR_DIR, tabptr->zone_fs_dir)) != Z_OK)
1285 		return (err);
1286 	return (Z_OK);
1287 }
1288 
1289 int
1290 zonecfg_add_ipd(zone_dochandle_t handle, struct zone_fstab *tabptr)
1291 {
1292 	int err;
1293 
1294 	if (tabptr == NULL)
1295 		return (Z_INVAL);
1296 
1297 	if ((err = operation_prep(handle)) != Z_OK)
1298 		return (err);
1299 
1300 	if ((err = zonecfg_add_ipd_core(handle, tabptr)) != Z_OK)
1301 		return (err);
1302 
1303 	return (Z_OK);
1304 }
1305 
1306 int
1307 zonecfg_add_fs_option(struct zone_fstab *tabptr, char *option)
1308 {
1309 	zone_fsopt_t *last, *old, *new;
1310 
1311 	last = tabptr->zone_fs_options;
1312 	for (old = last; old != NULL; old = old->zone_fsopt_next)
1313 		last = old;	/* walk to the end of the list */
1314 	new = (zone_fsopt_t *)malloc(sizeof (zone_fsopt_t));
1315 	if (new == NULL)
1316 		return (Z_NOMEM);
1317 	(void) strlcpy(new->zone_fsopt_opt, option,
1318 	    sizeof (new->zone_fsopt_opt));
1319 	new->zone_fsopt_next = NULL;
1320 	if (last == NULL)
1321 		tabptr->zone_fs_options = new;
1322 	else
1323 		last->zone_fsopt_next = new;
1324 	return (Z_OK);
1325 }
1326 
1327 int
1328 zonecfg_remove_fs_option(struct zone_fstab *tabptr, char *option)
1329 {
1330 	zone_fsopt_t *last, *this, *next;
1331 
1332 	last = tabptr->zone_fs_options;
1333 	for (this = last; this != NULL; this = this->zone_fsopt_next) {
1334 		if (strcmp(this->zone_fsopt_opt, option) == 0) {
1335 			next = this->zone_fsopt_next;
1336 			if (this == tabptr->zone_fs_options)
1337 				tabptr->zone_fs_options = next;
1338 			else
1339 				last->zone_fsopt_next = next;
1340 			free(this);
1341 			return (Z_OK);
1342 		} else
1343 			last = this;
1344 	}
1345 	return (Z_NO_PROPERTY_ID);
1346 }
1347 
1348 void
1349 zonecfg_free_fs_option_list(zone_fsopt_t *list)
1350 {
1351 	zone_fsopt_t *this, *next;
1352 
1353 	for (this = list; this != NULL; this = next) {
1354 		next = this->zone_fsopt_next;
1355 		free(this);
1356 	}
1357 }
1358 
1359 void
1360 zonecfg_free_rctl_value_list(struct zone_rctlvaltab *valtab)
1361 {
1362 	if (valtab == NULL)
1363 		return;
1364 	zonecfg_free_rctl_value_list(valtab->zone_rctlval_next);
1365 	free(valtab);
1366 }
1367 
1368 static boolean_t
1369 match_prop(xmlNodePtr cur, const xmlChar *attr, char *user_prop)
1370 {
1371 	xmlChar *gotten_prop;
1372 	int prop_result;
1373 
1374 	gotten_prop = xmlGetProp(cur, attr);
1375 	if (gotten_prop == NULL)	/* shouldn't happen */
1376 		return (B_FALSE);
1377 	prop_result = xmlStrcmp(gotten_prop, (const xmlChar *) user_prop);
1378 	xmlFree(gotten_prop);
1379 	return ((prop_result == 0));
1380 }
1381 
1382 static int
1383 zonecfg_delete_filesystem_core(zone_dochandle_t handle,
1384     struct zone_fstab *tabptr)
1385 {
1386 	xmlNodePtr cur = handle->zone_dh_cur;
1387 	boolean_t dir_match, spec_match, raw_match, type_match;
1388 
1389 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1390 		if (xmlStrcmp(cur->name, DTD_ELEM_FS))
1391 			continue;
1392 		dir_match = match_prop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir);
1393 		spec_match = match_prop(cur, DTD_ATTR_SPECIAL,
1394 		    tabptr->zone_fs_special);
1395 		raw_match = match_prop(cur, DTD_ATTR_RAW,
1396 		    tabptr->zone_fs_raw);
1397 		type_match = match_prop(cur, DTD_ATTR_TYPE,
1398 		    tabptr->zone_fs_type);
1399 		if (dir_match && spec_match && raw_match && type_match) {
1400 			xmlUnlinkNode(cur);
1401 			xmlFreeNode(cur);
1402 			return (Z_OK);
1403 		}
1404 	}
1405 	return (Z_NO_RESOURCE_ID);
1406 }
1407 
1408 int
1409 zonecfg_delete_filesystem(zone_dochandle_t handle, struct zone_fstab *tabptr)
1410 {
1411 	int err;
1412 
1413 	if (tabptr == NULL)
1414 		return (Z_INVAL);
1415 
1416 	if ((err = operation_prep(handle)) != Z_OK)
1417 		return (err);
1418 
1419 	if ((err = zonecfg_delete_filesystem_core(handle, tabptr)) != Z_OK)
1420 		return (err);
1421 
1422 	return (Z_OK);
1423 }
1424 
1425 int
1426 zonecfg_modify_filesystem(
1427 	zone_dochandle_t handle,
1428 	struct zone_fstab *oldtabptr,
1429 	struct zone_fstab *newtabptr)
1430 {
1431 	int err;
1432 
1433 	if (oldtabptr == NULL || newtabptr == NULL)
1434 		return (Z_INVAL);
1435 
1436 	if ((err = operation_prep(handle)) != Z_OK)
1437 		return (err);
1438 
1439 	if ((err = zonecfg_delete_filesystem_core(handle, oldtabptr)) != Z_OK)
1440 		return (err);
1441 
1442 	if ((err = zonecfg_add_filesystem_core(handle, newtabptr)) != Z_OK)
1443 		return (err);
1444 
1445 	return (Z_OK);
1446 }
1447 
1448 static int
1449 zonecfg_delete_ipd_core(zone_dochandle_t handle, struct zone_fstab *tabptr)
1450 {
1451 	xmlNodePtr cur = handle->zone_dh_cur;
1452 
1453 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1454 		if (xmlStrcmp(cur->name, DTD_ELEM_IPD))
1455 			continue;
1456 		if (match_prop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir)) {
1457 			xmlUnlinkNode(cur);
1458 			xmlFreeNode(cur);
1459 			return (Z_OK);
1460 		}
1461 	}
1462 	return (Z_NO_RESOURCE_ID);
1463 }
1464 
1465 int
1466 zonecfg_delete_ipd(zone_dochandle_t handle, struct zone_fstab *tabptr)
1467 {
1468 	int err;
1469 
1470 	if (tabptr == NULL)
1471 		return (Z_INVAL);
1472 
1473 	if ((err = operation_prep(handle)) != Z_OK)
1474 		return (err);
1475 
1476 	if ((err = zonecfg_delete_ipd_core(handle, tabptr)) != Z_OK)
1477 		return (err);
1478 
1479 	return (Z_OK);
1480 }
1481 
1482 int
1483 zonecfg_modify_ipd(zone_dochandle_t handle, struct zone_fstab *oldtabptr,
1484     struct zone_fstab *newtabptr)
1485 {
1486 	int err;
1487 
1488 	if (oldtabptr == NULL || newtabptr == NULL)
1489 		return (Z_INVAL);
1490 
1491 	if ((err = operation_prep(handle)) != Z_OK)
1492 		return (err);
1493 
1494 	if ((err = zonecfg_delete_ipd_core(handle, oldtabptr)) != Z_OK)
1495 		return (err);
1496 
1497 	if ((err = zonecfg_add_ipd_core(handle, newtabptr)) != Z_OK)
1498 		return (err);
1499 
1500 	return (Z_OK);
1501 }
1502 
1503 static int
1504 fetchprop(xmlNodePtr cur, const xmlChar *propname, char *dst, size_t dstsize)
1505 {
1506 	xmlChar *property;
1507 	size_t srcsize;
1508 
1509 	if ((property = xmlGetProp(cur, propname)) == NULL)
1510 		return (Z_BAD_PROPERTY);
1511 	srcsize = strlcpy(dst, (char *)property, dstsize);
1512 	xmlFree(property);
1513 	if (srcsize >= dstsize)
1514 		return (Z_TOO_BIG);
1515 	return (Z_OK);
1516 }
1517 
1518 static int
1519 fetch_alloc_prop(xmlNodePtr cur, const xmlChar *propname, char **dst)
1520 {
1521 	xmlChar *property;
1522 
1523 	if ((property = xmlGetProp(cur, propname)) == NULL)
1524 		return (Z_BAD_PROPERTY);
1525 	if ((*dst = strdup((char *)property)) == NULL) {
1526 		xmlFree(property);
1527 		return (Z_NOMEM);
1528 	}
1529 	xmlFree(property);
1530 	return (Z_OK);
1531 }
1532 
1533 int
1534 zonecfg_lookup_filesystem(
1535 	zone_dochandle_t handle,
1536 	struct zone_fstab *tabptr)
1537 {
1538 	xmlNodePtr cur, options, firstmatch;
1539 	int err;
1540 	char dirname[MAXPATHLEN], special[MAXPATHLEN], raw[MAXPATHLEN];
1541 	char type[FSTYPSZ];
1542 	char options_str[MAX_MNTOPT_STR];
1543 
1544 	if (tabptr == NULL)
1545 		return (Z_INVAL);
1546 
1547 	if ((err = operation_prep(handle)) != Z_OK)
1548 		return (err);
1549 
1550 	/*
1551 	 * Walk the list of children looking for matches on any properties
1552 	 * specified in the fstab parameter.  If more than one resource
1553 	 * matches, we return Z_INSUFFICIENT_SPEC; if none match, we return
1554 	 * Z_NO_RESOURCE_ID.
1555 	 */
1556 	cur = handle->zone_dh_cur;
1557 	firstmatch = NULL;
1558 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1559 		if (xmlStrcmp(cur->name, DTD_ELEM_FS))
1560 			continue;
1561 		if (strlen(tabptr->zone_fs_dir) > 0) {
1562 			if ((fetchprop(cur, DTD_ATTR_DIR, dirname,
1563 			    sizeof (dirname)) == Z_OK) &&
1564 			    (strcmp(tabptr->zone_fs_dir, dirname) == 0)) {
1565 				if (firstmatch == NULL)
1566 					firstmatch = cur;
1567 				else
1568 					return (Z_INSUFFICIENT_SPEC);
1569 			}
1570 		}
1571 		if (strlen(tabptr->zone_fs_special) > 0) {
1572 			if ((fetchprop(cur, DTD_ATTR_SPECIAL, special,
1573 			    sizeof (special)) == Z_OK)) {
1574 				if (strcmp(tabptr->zone_fs_special,
1575 				    special) == 0) {
1576 					if (firstmatch == NULL)
1577 						firstmatch = cur;
1578 					else if (firstmatch != cur)
1579 						return (Z_INSUFFICIENT_SPEC);
1580 				} else {
1581 					/*
1582 					 * If another property matched but this
1583 					 * one doesn't then reset firstmatch.
1584 					 */
1585 					if (firstmatch == cur)
1586 						firstmatch = NULL;
1587 				}
1588 			}
1589 		}
1590 		if (strlen(tabptr->zone_fs_raw) > 0) {
1591 			if ((fetchprop(cur, DTD_ATTR_RAW, raw,
1592 			    sizeof (raw)) == Z_OK)) {
1593 				if (strcmp(tabptr->zone_fs_raw, raw) == 0) {
1594 					if (firstmatch == NULL)
1595 						firstmatch = cur;
1596 					else if (firstmatch != cur)
1597 						return (Z_INSUFFICIENT_SPEC);
1598 				} else {
1599 					/*
1600 					 * If another property matched but this
1601 					 * one doesn't then reset firstmatch.
1602 					 */
1603 					if (firstmatch == cur)
1604 						firstmatch = NULL;
1605 				}
1606 			}
1607 		}
1608 		if (strlen(tabptr->zone_fs_type) > 0) {
1609 			if ((fetchprop(cur, DTD_ATTR_TYPE, type,
1610 			    sizeof (type)) == Z_OK)) {
1611 				if (strcmp(tabptr->zone_fs_type, type) == 0) {
1612 					if (firstmatch == NULL)
1613 						firstmatch = cur;
1614 					else if (firstmatch != cur)
1615 						return (Z_INSUFFICIENT_SPEC);
1616 				} else {
1617 					/*
1618 					 * If another property matched but this
1619 					 * one doesn't then reset firstmatch.
1620 					 */
1621 					if (firstmatch == cur)
1622 						firstmatch = NULL;
1623 				}
1624 			}
1625 		}
1626 	}
1627 
1628 	if (firstmatch == NULL)
1629 		return (Z_NO_RESOURCE_ID);
1630 
1631 	cur = firstmatch;
1632 
1633 	if ((err = fetchprop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir,
1634 	    sizeof (tabptr->zone_fs_dir))) != Z_OK)
1635 		return (err);
1636 
1637 	if ((err = fetchprop(cur, DTD_ATTR_SPECIAL, tabptr->zone_fs_special,
1638 	    sizeof (tabptr->zone_fs_special))) != Z_OK)
1639 		return (err);
1640 
1641 	if ((err = fetchprop(cur, DTD_ATTR_RAW, tabptr->zone_fs_raw,
1642 	    sizeof (tabptr->zone_fs_raw))) != Z_OK)
1643 		return (err);
1644 
1645 	if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_fs_type,
1646 	    sizeof (tabptr->zone_fs_type))) != Z_OK)
1647 		return (err);
1648 
1649 	/* options are optional */
1650 	tabptr->zone_fs_options = NULL;
1651 	for (options = cur->xmlChildrenNode; options != NULL;
1652 	    options = options->next) {
1653 		if ((fetchprop(options, DTD_ATTR_NAME, options_str,
1654 		    sizeof (options_str)) != Z_OK))
1655 			break;
1656 		if (zonecfg_add_fs_option(tabptr, options_str) != Z_OK)
1657 			break;
1658 	}
1659 	return (Z_OK);
1660 }
1661 
1662 int
1663 zonecfg_lookup_ipd(zone_dochandle_t handle, struct zone_fstab *tabptr)
1664 {
1665 	xmlNodePtr cur, match;
1666 	int err;
1667 	char dirname[MAXPATHLEN];
1668 
1669 	if (tabptr == NULL)
1670 		return (Z_INVAL);
1671 
1672 	if ((err = operation_prep(handle)) != Z_OK)
1673 		return (err);
1674 
1675 	/*
1676 	 * General algorithm:
1677 	 * Walk the list of children looking for matches on any properties
1678 	 * specified in the fstab parameter.  If more than one resource
1679 	 * matches, we return Z_INSUFFICIENT_SPEC; if none match, we return
1680 	 * Z_NO_RESOURCE_ID.
1681 	 */
1682 	cur = handle->zone_dh_cur;
1683 	match = NULL;
1684 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1685 		if (xmlStrcmp(cur->name, DTD_ELEM_IPD))
1686 			continue;
1687 		if (strlen(tabptr->zone_fs_dir) > 0) {
1688 			if ((fetchprop(cur, DTD_ATTR_DIR, dirname,
1689 			    sizeof (dirname)) == Z_OK) &&
1690 			    (strcmp(tabptr->zone_fs_dir, dirname) == 0)) {
1691 				if (match == NULL)
1692 					match = cur;
1693 				else
1694 					return (Z_INSUFFICIENT_SPEC);
1695 			}
1696 		}
1697 	}
1698 
1699 	if (match == NULL)
1700 		return (Z_NO_RESOURCE_ID);
1701 
1702 	cur = match;
1703 
1704 	if ((err = fetchprop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir,
1705 	    sizeof (tabptr->zone_fs_dir))) != Z_OK)
1706 		return (err);
1707 
1708 	return (Z_OK);
1709 }
1710 
1711 /*
1712  * Compare two IP addresses in string form.  Allow for the possibility that
1713  * one might have "/<prefix-length>" at the end: allow a match on just the
1714  * IP address (or host name) part.
1715  */
1716 
1717 boolean_t
1718 zonecfg_same_net_address(char *a1, char *a2)
1719 {
1720 	char *slashp, *slashp1, *slashp2;
1721 	int result;
1722 
1723 	if (strcmp(a1, a2) == 0)
1724 		return (B_TRUE);
1725 
1726 	/*
1727 	 * If neither has a slash or both do, they need to match to be
1728 	 * considered the same, but they did not match above, so fail.
1729 	 */
1730 	slashp1 = strchr(a1, '/');
1731 	slashp2 = strchr(a2, '/');
1732 	if ((slashp1 == NULL && slashp2 == NULL) ||
1733 	    (slashp1 != NULL && slashp2 != NULL))
1734 		return (B_FALSE);
1735 
1736 	/*
1737 	 * Only one had a slash: pick that one, zero out the slash, compare
1738 	 * the "address only" strings, restore the slash, and return the
1739 	 * result of the comparison.
1740 	 */
1741 	slashp = (slashp1 == NULL) ? slashp2 : slashp1;
1742 	*slashp = '\0';
1743 	result = strcmp(a1, a2);
1744 	*slashp = '/';
1745 	return ((result == 0));
1746 }
1747 
1748 int
1749 zonecfg_valid_net_address(char *address, struct lifreq *lifr)
1750 {
1751 	struct sockaddr_in *sin4;
1752 	struct sockaddr_in6 *sin6;
1753 	struct addrinfo hints, *result;
1754 	char *slashp = strchr(address, '/');
1755 
1756 	bzero(lifr, sizeof (struct lifreq));
1757 	sin4 = (struct sockaddr_in *)&lifr->lifr_addr;
1758 	sin6 = (struct sockaddr_in6 *)&lifr->lifr_addr;
1759 	if (slashp != NULL)
1760 		*slashp = '\0';
1761 	if (inet_pton(AF_INET, address, &sin4->sin_addr) == 1) {
1762 		sin4->sin_family = AF_INET;
1763 	} else if (inet_pton(AF_INET6, address, &sin6->sin6_addr) == 1) {
1764 		if (slashp == NULL)
1765 			return (Z_IPV6_ADDR_PREFIX_LEN);
1766 		sin6->sin6_family = AF_INET6;
1767 	} else {
1768 		/* "address" may be a host name */
1769 		(void) memset(&hints, 0, sizeof (hints));
1770 		hints.ai_family = PF_INET;
1771 		if (getaddrinfo(address, NULL, &hints, &result) != 0)
1772 			return (Z_BOGUS_ADDRESS);
1773 		sin4->sin_family = result->ai_family;
1774 
1775 		(void) memcpy(&sin4->sin_addr,
1776 		    /* LINTED E_BAD_PTR_CAST_ALIGN */
1777 		    &((struct sockaddr_in *)result->ai_addr)->sin_addr,
1778 		    sizeof (struct in_addr));
1779 
1780 		freeaddrinfo(result);
1781 	}
1782 	return (Z_OK);
1783 }
1784 
1785 int
1786 zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
1787 {
1788 	xmlNodePtr cur, firstmatch;
1789 	int err;
1790 	char address[INET6_ADDRSTRLEN], physical[LIFNAMSIZ];
1791 
1792 	if (tabptr == NULL)
1793 		return (Z_INVAL);
1794 
1795 	if ((err = operation_prep(handle)) != Z_OK)
1796 		return (err);
1797 
1798 	cur = handle->zone_dh_cur;
1799 	firstmatch = NULL;
1800 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1801 		if (xmlStrcmp(cur->name, DTD_ELEM_NET))
1802 			continue;
1803 		if (strlen(tabptr->zone_nwif_physical) > 0) {
1804 			if ((fetchprop(cur, DTD_ATTR_PHYSICAL, physical,
1805 			    sizeof (physical)) == Z_OK) &&
1806 			    (strcmp(tabptr->zone_nwif_physical,
1807 			    physical) == 0)) {
1808 				if (firstmatch == NULL)
1809 					firstmatch = cur;
1810 				else
1811 					return (Z_INSUFFICIENT_SPEC);
1812 			}
1813 		}
1814 		if (strlen(tabptr->zone_nwif_address) > 0) {
1815 			if ((fetchprop(cur, DTD_ATTR_ADDRESS, address,
1816 			    sizeof (address)) == Z_OK)) {
1817 				if (zonecfg_same_net_address(
1818 				    tabptr->zone_nwif_address, address)) {
1819 					if (firstmatch == NULL)
1820 						firstmatch = cur;
1821 					else if (firstmatch != cur)
1822 						return (Z_INSUFFICIENT_SPEC);
1823 				} else {
1824 					/*
1825 					 * If another property matched but this
1826 					 * one doesn't then reset firstmatch.
1827 					 */
1828 					if (firstmatch == cur)
1829 						firstmatch = NULL;
1830 				}
1831 			}
1832 		}
1833 	}
1834 	if (firstmatch == NULL)
1835 		return (Z_NO_RESOURCE_ID);
1836 
1837 	cur = firstmatch;
1838 
1839 	if ((err = fetchprop(cur, DTD_ATTR_PHYSICAL, tabptr->zone_nwif_physical,
1840 	    sizeof (tabptr->zone_nwif_physical))) != Z_OK)
1841 		return (err);
1842 
1843 	if ((err = fetchprop(cur, DTD_ATTR_ADDRESS, tabptr->zone_nwif_address,
1844 	    sizeof (tabptr->zone_nwif_address))) != Z_OK)
1845 		return (err);
1846 
1847 	return (Z_OK);
1848 }
1849 
1850 static int
1851 zonecfg_add_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
1852 {
1853 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
1854 	int err;
1855 
1856 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_NET, NULL);
1857 	if ((err = newprop(newnode, DTD_ATTR_ADDRESS,
1858 	    tabptr->zone_nwif_address)) != Z_OK)
1859 		return (err);
1860 	if ((err = newprop(newnode, DTD_ATTR_PHYSICAL,
1861 	    tabptr->zone_nwif_physical)) != Z_OK)
1862 		return (err);
1863 	return (Z_OK);
1864 }
1865 
1866 int
1867 zonecfg_add_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
1868 {
1869 	int err;
1870 
1871 	if (tabptr == NULL)
1872 		return (Z_INVAL);
1873 
1874 	if ((err = operation_prep(handle)) != Z_OK)
1875 		return (err);
1876 
1877 	if ((err = zonecfg_add_nwif_core(handle, tabptr)) != Z_OK)
1878 		return (err);
1879 
1880 	return (Z_OK);
1881 }
1882 
1883 static int
1884 zonecfg_delete_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
1885 {
1886 	xmlNodePtr cur = handle->zone_dh_cur;
1887 	boolean_t addr_match, phys_match;
1888 
1889 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1890 		if (xmlStrcmp(cur->name, DTD_ELEM_NET))
1891 			continue;
1892 
1893 		addr_match = match_prop(cur, DTD_ATTR_ADDRESS,
1894 		    tabptr->zone_nwif_address);
1895 		phys_match = match_prop(cur, DTD_ATTR_PHYSICAL,
1896 		    tabptr->zone_nwif_physical);
1897 
1898 		if (addr_match && phys_match) {
1899 			xmlUnlinkNode(cur);
1900 			xmlFreeNode(cur);
1901 			return (Z_OK);
1902 		}
1903 	}
1904 	return (Z_NO_RESOURCE_ID);
1905 }
1906 
1907 int
1908 zonecfg_delete_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
1909 {
1910 	int err;
1911 
1912 	if (tabptr == NULL)
1913 		return (Z_INVAL);
1914 
1915 	if ((err = operation_prep(handle)) != Z_OK)
1916 		return (err);
1917 
1918 	if ((err = zonecfg_delete_nwif_core(handle, tabptr)) != Z_OK)
1919 		return (err);
1920 
1921 	return (Z_OK);
1922 }
1923 
1924 int
1925 zonecfg_modify_nwif(
1926 	zone_dochandle_t handle,
1927 	struct zone_nwiftab *oldtabptr,
1928 	struct zone_nwiftab *newtabptr)
1929 {
1930 	int err;
1931 
1932 	if (oldtabptr == NULL || newtabptr == NULL)
1933 		return (Z_INVAL);
1934 
1935 	if ((err = operation_prep(handle)) != Z_OK)
1936 		return (err);
1937 
1938 	if ((err = zonecfg_delete_nwif_core(handle, oldtabptr)) != Z_OK)
1939 		return (err);
1940 
1941 	if ((err = zonecfg_add_nwif_core(handle, newtabptr)) != Z_OK)
1942 		return (err);
1943 
1944 	return (Z_OK);
1945 }
1946 
1947 int
1948 zonecfg_lookup_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
1949 {
1950 	xmlNodePtr cur, firstmatch;
1951 	int err;
1952 	char match[MAXPATHLEN];
1953 
1954 	if (tabptr == NULL)
1955 		return (Z_INVAL);
1956 
1957 	if ((err = operation_prep(handle)) != Z_OK)
1958 		return (err);
1959 
1960 	cur = handle->zone_dh_cur;
1961 	firstmatch = NULL;
1962 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
1963 		if (xmlStrcmp(cur->name, DTD_ELEM_DEVICE))
1964 			continue;
1965 		if (strlen(tabptr->zone_dev_match) == 0)
1966 			continue;
1967 
1968 		if ((fetchprop(cur, DTD_ATTR_MATCH, match,
1969 		    sizeof (match)) == Z_OK)) {
1970 			if (strcmp(tabptr->zone_dev_match,
1971 			    match) == 0) {
1972 				if (firstmatch == NULL)
1973 					firstmatch = cur;
1974 				else if (firstmatch != cur)
1975 					return (Z_INSUFFICIENT_SPEC);
1976 			} else {
1977 				/*
1978 				 * If another property matched but this
1979 				 * one doesn't then reset firstmatch.
1980 				 */
1981 				if (firstmatch == cur)
1982 					firstmatch = NULL;
1983 			}
1984 		}
1985 	}
1986 	if (firstmatch == NULL)
1987 		return (Z_NO_RESOURCE_ID);
1988 
1989 	cur = firstmatch;
1990 
1991 	if ((err = fetchprop(cur, DTD_ATTR_MATCH, tabptr->zone_dev_match,
1992 	    sizeof (tabptr->zone_dev_match))) != Z_OK)
1993 		return (err);
1994 
1995 	return (Z_OK);
1996 }
1997 
1998 static int
1999 zonecfg_add_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr)
2000 {
2001 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
2002 	int err;
2003 
2004 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DEVICE, NULL);
2005 
2006 	if ((err = newprop(newnode, DTD_ATTR_MATCH,
2007 	    tabptr->zone_dev_match)) != Z_OK)
2008 		return (err);
2009 
2010 	return (Z_OK);
2011 }
2012 
2013 int
2014 zonecfg_add_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
2015 {
2016 	int err;
2017 
2018 	if (tabptr == NULL)
2019 		return (Z_INVAL);
2020 
2021 	if ((err = operation_prep(handle)) != Z_OK)
2022 		return (err);
2023 
2024 	if ((err = zonecfg_add_dev_core(handle, tabptr)) != Z_OK)
2025 		return (err);
2026 
2027 	return (Z_OK);
2028 }
2029 
2030 static int
2031 zonecfg_delete_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr)
2032 {
2033 	xmlNodePtr cur = handle->zone_dh_cur;
2034 	int match_match;
2035 
2036 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2037 		if (xmlStrcmp(cur->name, DTD_ELEM_DEVICE))
2038 			continue;
2039 
2040 		match_match = match_prop(cur, DTD_ATTR_MATCH,
2041 		    tabptr->zone_dev_match);
2042 
2043 		if (match_match) {
2044 			xmlUnlinkNode(cur);
2045 			xmlFreeNode(cur);
2046 			return (Z_OK);
2047 		}
2048 	}
2049 	return (Z_NO_RESOURCE_ID);
2050 }
2051 
2052 int
2053 zonecfg_delete_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
2054 {
2055 	int err;
2056 
2057 	if (tabptr == NULL)
2058 		return (Z_INVAL);
2059 
2060 	if ((err = operation_prep(handle)) != Z_OK)
2061 		return (err);
2062 
2063 	if ((err = zonecfg_delete_dev_core(handle, tabptr)) != Z_OK)
2064 		return (err);
2065 
2066 	return (Z_OK);
2067 }
2068 
2069 int
2070 zonecfg_modify_dev(
2071 	zone_dochandle_t handle,
2072 	struct zone_devtab *oldtabptr,
2073 	struct zone_devtab *newtabptr)
2074 {
2075 	int err;
2076 
2077 	if (oldtabptr == NULL || newtabptr == NULL)
2078 		return (Z_INVAL);
2079 
2080 	if ((err = operation_prep(handle)) != Z_OK)
2081 		return (err);
2082 
2083 	if ((err = zonecfg_delete_dev_core(handle, oldtabptr)) != Z_OK)
2084 		return (err);
2085 
2086 	if ((err = zonecfg_add_dev_core(handle, newtabptr)) != Z_OK)
2087 		return (err);
2088 
2089 	return (Z_OK);
2090 }
2091 
2092 /* Lock to serialize all zonecfg_devwalks */
2093 static pthread_mutex_t zonecfg_devwalk_lock = PTHREAD_MUTEX_INITIALIZER;
2094 /*
2095  * Global variables used to pass data from zonecfg_devwalk to the nftw
2096  * call-back (zonecfg_devwalk_cb).  g_devwalk_data is really the void*
2097  * parameter and g_devwalk_cb is really the *cb parameter from zonecfg_devwalk.
2098  */
2099 static void *g_devwalk_data;
2100 static int (*g_devwalk_cb)(const char *, uid_t, gid_t, mode_t, const char *,
2101     void *);
2102 static size_t g_devwalk_skip_prefix;
2103 
2104 /*
2105  * This is the nftw call-back function used by zonecfg_devwalk.  It is
2106  * responsible for calling the actual call-back that is passed in to
2107  * zonecfg_devwalk as the *cb argument.
2108  */
2109 /* ARGSUSED2 */
2110 static int
2111 zonecfg_devwalk_cb(const char *path, const struct stat *st, int f,
2112     struct FTW *ftw)
2113 {
2114 	acl_t *acl;
2115 	char *acl_txt = NULL;
2116 
2117 	/* skip all but character and block devices */
2118 	if (!S_ISBLK(st->st_mode) && !S_ISCHR(st->st_mode))
2119 		return (0);
2120 
2121 	if ((acl_get(path, ACL_NO_TRIVIAL, &acl) == 0) &&
2122 	    acl != NULL) {
2123 		acl_txt = acl_totext(acl, ACL_NORESOLVE);
2124 		acl_free(acl);
2125 	}
2126 
2127 	if (strlen(path) <= g_devwalk_skip_prefix)
2128 		return (0);
2129 
2130 	g_devwalk_cb(path + g_devwalk_skip_prefix, st->st_uid, st->st_gid,
2131 	    st->st_mode & S_IAMB, acl_txt != NULL ? acl_txt : "",
2132 	    g_devwalk_data);
2133 	free(acl_txt);
2134 	return (0);
2135 }
2136 
2137 /*
2138  * Walk the dev tree for the zone specified by hdl and call the call-back (cb)
2139  * function for each entry in the tree.  The call-back will be passed the
2140  * name, uid, gid, mode, acl string and the void *data input parameter
2141  * for each dev entry.
2142  *
2143  * Data is passed to the zonecfg_devwalk_cb through the global variables
2144  * g_devwalk_data, *g_devwalk_cb, and g_devwalk_skip_prefix.  The
2145  * zonecfg_devwalk_cb function will actually call *cb.
2146  */
2147 int
2148 zonecfg_devwalk(zone_dochandle_t hdl,
2149     int (*cb)(const char *, uid_t, gid_t, mode_t, const char *, void *),
2150     void *data)
2151 {
2152 	char path[MAXPATHLEN];
2153 	int ret;
2154 
2155 	if ((ret = zonecfg_get_zonepath(hdl, path, sizeof (path))) != Z_OK)
2156 		return (ret);
2157 
2158 	if (strlcat(path, "/dev", sizeof (path)) >= sizeof (path))
2159 		return (Z_TOO_BIG);
2160 	g_devwalk_skip_prefix = strlen(path) + 1;
2161 
2162 	/*
2163 	 * We have to serialize all zonecfg_devwalks in the same process
2164 	 * (which should be fine), since nftw() is so badly designed.
2165 	 */
2166 	(void) pthread_mutex_lock(&zonecfg_devwalk_lock);
2167 
2168 	g_devwalk_data = data;
2169 	g_devwalk_cb = cb;
2170 	(void) nftw(path, zonecfg_devwalk_cb, 0, FTW_PHYS);
2171 
2172 	(void) pthread_mutex_unlock(&zonecfg_devwalk_lock);
2173 	return (Z_OK);
2174 }
2175 
2176 /*
2177  * Update the owner, group, mode and acl on the specified dev (inpath) for
2178  * the zone (hdl).  This function can be used to fix up the dev tree after
2179  * attaching a migrated zone.
2180  */
2181 int
2182 zonecfg_devperms_apply(zone_dochandle_t hdl, const char *inpath, uid_t owner,
2183     gid_t group, mode_t mode, const char *acltxt)
2184 {
2185 	int ret;
2186 	char path[MAXPATHLEN];
2187 	struct stat st;
2188 	acl_t *aclp;
2189 
2190 	if ((ret = zonecfg_get_zonepath(hdl, path, sizeof (path))) != Z_OK)
2191 		return (ret);
2192 
2193 	if (strlcat(path, "/dev/", sizeof (path)) >= sizeof (path))
2194 		return (Z_TOO_BIG);
2195 	if (strlcat(path, inpath, sizeof (path)) >= sizeof (path))
2196 		return (Z_TOO_BIG);
2197 
2198 	if (stat(path, &st) == -1)
2199 		return (Z_INVAL);
2200 
2201 	/* make sure we're only touching device nodes */
2202 	if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode))
2203 		return (Z_INVAL);
2204 
2205 	if (chown(path, owner, group) == -1)
2206 		return (Z_SYSTEM);
2207 
2208 	if (chmod(path, mode) == -1)
2209 		return (Z_SYSTEM);
2210 
2211 	if ((acltxt == NULL) || (strcmp(acltxt, "") == 0))
2212 		return (Z_OK);
2213 
2214 	if (acl_fromtext(acltxt, &aclp) != 0)
2215 		return (Z_SYSTEM);
2216 
2217 	errno = 0;
2218 	if (acl_set(path, aclp) == -1) {
2219 		free(aclp);
2220 		return (Z_SYSTEM);
2221 	}
2222 
2223 	free(aclp);
2224 	return (Z_OK);
2225 }
2226 
2227 /*
2228  * This is the set of devices which must be present in every zone.  Users
2229  * can augment this list with additional device rules in their zone
2230  * configuration, but at present cannot remove any of the this set of
2231  * standard devices.  All matching is done by /dev pathname (the "/dev"
2232  * part is implicit.  Try to keep rules which match a large number of
2233  * devices (like the pts rule) first.
2234  */
2235 static const char *standard_devs[] = {
2236 	"pts/*",
2237 	"ptmx",
2238 	"random",
2239 	"urandom",
2240 	"poll",
2241 	"pool",
2242 	"kstat",
2243 	"zero",
2244 	"null",
2245 	"crypto",
2246 	"cryptoadm",
2247 	"ticots",
2248 	"ticotsord",
2249 	"ticlts",
2250 	"lo0",
2251 	"lo1",
2252 	"lo2",
2253 	"lo3",
2254 	"sad/user",
2255 	"tty",
2256 	"logindmux",
2257 	"log",
2258 	"conslog",
2259 	"arp",
2260 	"tcp",
2261 	"tcp6",
2262 	"udp",
2263 	"udp6",
2264 	"sysevent",
2265 #ifdef __sparc
2266 	"openprom",
2267 #endif
2268 	"cpu/self/cpuid",
2269 	"dtrace/helper",
2270 	"zfs",
2271 	NULL
2272 };
2273 
2274 /*
2275  * This function finds everything mounted under a zone's rootpath.
2276  * This returns the number of mounts under rootpath, or -1 on error.
2277  * callback is called once per mount found with the first argument
2278  * pointing to the  mount point.
2279  *
2280  * If the callback function returns non-zero zonecfg_find_mounts
2281  * aborts with an error.
2282  */
2283 
2284 int
2285 zonecfg_find_mounts(char *rootpath, int (*callback)(const char *, void *),
2286     void *priv) {
2287 	FILE *mnttab;
2288 	struct mnttab m;
2289 	size_t l;
2290 	int rv = 0;
2291 
2292 	assert(rootpath != NULL);
2293 
2294 	l = strlen(rootpath);
2295 
2296 	mnttab = fopen("/etc/mnttab", "r");
2297 
2298 	if (mnttab == NULL)
2299 		return (-1);
2300 
2301 	if (ioctl(fileno(mnttab), MNTIOC_SHOWHIDDEN, NULL) < 0)  {
2302 		rv = -1;
2303 		goto out;
2304 	}
2305 
2306 	while (!getmntent(mnttab, &m)) {
2307 		if ((strncmp(rootpath, m.mnt_mountp, l) == 0) &&
2308 		    (m.mnt_mountp[l] == '/')) {
2309 			rv++;
2310 			if (callback == NULL)
2311 				continue;
2312 			if (callback(m.mnt_mountp, priv)) {
2313 				rv = -1;
2314 				goto out;
2315 
2316 			}
2317 		}
2318 	}
2319 
2320 out:
2321 	(void) fclose(mnttab);
2322 	return (rv);
2323 }
2324 
2325 /*
2326  * This routine is used to determine if a given device should appear in the
2327  * zone represented by 'handle'.  First it consults the list of "standard"
2328  * zone devices.  Then it scans the user-supplied device entries.
2329  */
2330 int
2331 zonecfg_match_dev(zone_dochandle_t handle, char *devpath,
2332     struct zone_devtab *out_match)
2333 {
2334 	int err;
2335 	boolean_t found = B_FALSE;
2336 	char match[MAXPATHLEN];
2337 	const char **stdmatch;
2338 	xmlNodePtr cur;
2339 
2340 	if (handle == NULL || devpath == NULL)
2341 		return (Z_INVAL);
2342 
2343 	/*
2344 	 * Check the "standard" devices which we require to be present.
2345 	 */
2346 	for (stdmatch = &standard_devs[0]; *stdmatch != NULL; stdmatch++) {
2347 		/*
2348 		 * fnmatch gives us simple but powerful shell-style matching.
2349 		 */
2350 		if (fnmatch(*stdmatch, devpath, FNM_PATHNAME) == 0) {
2351 			if (!out_match)
2352 				return (Z_OK);
2353 			(void) snprintf(out_match->zone_dev_match,
2354 			    sizeof (out_match->zone_dev_match),
2355 			    "/dev/%s", *stdmatch);
2356 			return (Z_OK);
2357 		}
2358 	}
2359 
2360 	/*
2361 	 * We got no hits in the set of standard devices.  On to the user
2362 	 * supplied ones.
2363 	 */
2364 	if ((err = operation_prep(handle)) != Z_OK) {
2365 		handle->zone_dh_cur = NULL;
2366 		return (err);
2367 	}
2368 
2369 	cur = handle->zone_dh_cur;
2370 	cur = cur->xmlChildrenNode;
2371 	if (cur == NULL)
2372 		return (Z_NO_ENTRY);
2373 	handle->zone_dh_cur = cur;
2374 
2375 	for (; cur != NULL; cur = cur->next) {
2376 		char *m;
2377 		if (xmlStrcmp(cur->name, DTD_ELEM_DEVICE) != 0)
2378 			continue;
2379 		if ((err = fetchprop(cur, DTD_ATTR_MATCH, match,
2380 		    sizeof (match))) != Z_OK) {
2381 			handle->zone_dh_cur = handle->zone_dh_top;
2382 			return (err);
2383 		}
2384 		m = match;
2385 		/*
2386 		 * fnmatch gives us simple but powerful shell-style matching;
2387 		 * but first, we need to strip out /dev/ from the matching rule.
2388 		 */
2389 		if (strncmp(m, "/dev/", 5) == 0)
2390 			m += 5;
2391 
2392 		if (fnmatch(m, devpath, FNM_PATHNAME) == 0) {
2393 			found = B_TRUE;
2394 			break;
2395 		}
2396 	}
2397 
2398 	if (!found)
2399 		return (Z_NO_ENTRY);
2400 
2401 	if (!out_match)
2402 		return (Z_OK);
2403 
2404 	(void) strlcpy(out_match->zone_dev_match, match,
2405 	    sizeof (out_match->zone_dev_match));
2406 	return (Z_OK);
2407 }
2408 
2409 int
2410 zonecfg_lookup_attr(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2411 {
2412 	xmlNodePtr cur, firstmatch;
2413 	int err;
2414 	char name[MAXNAMELEN], type[MAXNAMELEN], value[MAXNAMELEN];
2415 
2416 	if (tabptr == NULL)
2417 		return (Z_INVAL);
2418 
2419 	if ((err = operation_prep(handle)) != Z_OK)
2420 		return (err);
2421 
2422 	cur = handle->zone_dh_cur;
2423 	firstmatch = NULL;
2424 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2425 		if (xmlStrcmp(cur->name, DTD_ELEM_ATTR))
2426 			continue;
2427 		if (strlen(tabptr->zone_attr_name) > 0) {
2428 			if ((fetchprop(cur, DTD_ATTR_NAME, name,
2429 			    sizeof (name)) == Z_OK) &&
2430 			    (strcmp(tabptr->zone_attr_name, name) == 0)) {
2431 				if (firstmatch == NULL)
2432 					firstmatch = cur;
2433 				else
2434 					return (Z_INSUFFICIENT_SPEC);
2435 			}
2436 		}
2437 		if (strlen(tabptr->zone_attr_type) > 0) {
2438 			if ((fetchprop(cur, DTD_ATTR_TYPE, type,
2439 			    sizeof (type)) == Z_OK)) {
2440 				if (strcmp(tabptr->zone_attr_type, type) == 0) {
2441 					if (firstmatch == NULL)
2442 						firstmatch = cur;
2443 					else if (firstmatch != cur)
2444 						return (Z_INSUFFICIENT_SPEC);
2445 				} else {
2446 					/*
2447 					 * If another property matched but this
2448 					 * one doesn't then reset firstmatch.
2449 					 */
2450 					if (firstmatch == cur)
2451 						firstmatch = NULL;
2452 				}
2453 			}
2454 		}
2455 		if (strlen(tabptr->zone_attr_value) > 0) {
2456 			if ((fetchprop(cur, DTD_ATTR_VALUE, value,
2457 			    sizeof (value)) == Z_OK)) {
2458 				if (strcmp(tabptr->zone_attr_value, value) ==
2459 				    0) {
2460 					if (firstmatch == NULL)
2461 						firstmatch = cur;
2462 					else if (firstmatch != cur)
2463 						return (Z_INSUFFICIENT_SPEC);
2464 				} else {
2465 					/*
2466 					 * If another property matched but this
2467 					 * one doesn't then reset firstmatch.
2468 					 */
2469 					if (firstmatch == cur)
2470 						firstmatch = NULL;
2471 				}
2472 			}
2473 		}
2474 	}
2475 	if (firstmatch == NULL)
2476 		return (Z_NO_RESOURCE_ID);
2477 
2478 	cur = firstmatch;
2479 
2480 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_attr_name,
2481 	    sizeof (tabptr->zone_attr_name))) != Z_OK)
2482 		return (err);
2483 
2484 	if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_attr_type,
2485 	    sizeof (tabptr->zone_attr_type))) != Z_OK)
2486 		return (err);
2487 
2488 	if ((err = fetchprop(cur, DTD_ATTR_VALUE, tabptr->zone_attr_value,
2489 	    sizeof (tabptr->zone_attr_value))) != Z_OK)
2490 		return (err);
2491 
2492 	return (Z_OK);
2493 }
2494 
2495 static int
2496 zonecfg_add_attr_core(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2497 {
2498 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
2499 	int err;
2500 
2501 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_ATTR, NULL);
2502 	err = newprop(newnode, DTD_ATTR_NAME, tabptr->zone_attr_name);
2503 	if (err != Z_OK)
2504 		return (err);
2505 	err = newprop(newnode, DTD_ATTR_TYPE, tabptr->zone_attr_type);
2506 	if (err != Z_OK)
2507 		return (err);
2508 	err = newprop(newnode, DTD_ATTR_VALUE, tabptr->zone_attr_value);
2509 	if (err != Z_OK)
2510 		return (err);
2511 	return (Z_OK);
2512 }
2513 
2514 int
2515 zonecfg_add_attr(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2516 {
2517 	int err;
2518 
2519 	if (tabptr == NULL)
2520 		return (Z_INVAL);
2521 
2522 	if ((err = operation_prep(handle)) != Z_OK)
2523 		return (err);
2524 
2525 	if ((err = zonecfg_add_attr_core(handle, tabptr)) != Z_OK)
2526 		return (err);
2527 
2528 	return (Z_OK);
2529 }
2530 
2531 static int
2532 zonecfg_delete_attr_core(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2533 {
2534 	xmlNodePtr cur = handle->zone_dh_cur;
2535 	int name_match, type_match, value_match;
2536 
2537 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2538 		if (xmlStrcmp(cur->name, DTD_ELEM_ATTR))
2539 			continue;
2540 
2541 		name_match = match_prop(cur, DTD_ATTR_NAME,
2542 		    tabptr->zone_attr_name);
2543 		type_match = match_prop(cur, DTD_ATTR_TYPE,
2544 		    tabptr->zone_attr_type);
2545 		value_match = match_prop(cur, DTD_ATTR_VALUE,
2546 		    tabptr->zone_attr_value);
2547 
2548 		if (name_match && type_match && value_match) {
2549 			xmlUnlinkNode(cur);
2550 			xmlFreeNode(cur);
2551 			return (Z_OK);
2552 		}
2553 	}
2554 	return (Z_NO_RESOURCE_ID);
2555 }
2556 
2557 int
2558 zonecfg_delete_attr(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2559 {
2560 	int err;
2561 
2562 	if (tabptr == NULL)
2563 		return (Z_INVAL);
2564 
2565 	if ((err = operation_prep(handle)) != Z_OK)
2566 		return (err);
2567 
2568 	if ((err = zonecfg_delete_attr_core(handle, tabptr)) != Z_OK)
2569 		return (err);
2570 
2571 	return (Z_OK);
2572 }
2573 
2574 int
2575 zonecfg_modify_attr(
2576 	zone_dochandle_t handle,
2577 	struct zone_attrtab *oldtabptr,
2578 	struct zone_attrtab *newtabptr)
2579 {
2580 	int err;
2581 
2582 	if (oldtabptr == NULL || newtabptr == NULL)
2583 		return (Z_INVAL);
2584 
2585 	if ((err = operation_prep(handle)) != Z_OK)
2586 		return (err);
2587 
2588 	if ((err = zonecfg_delete_attr_core(handle, oldtabptr)) != Z_OK)
2589 		return (err);
2590 
2591 	if ((err = zonecfg_add_attr_core(handle, newtabptr)) != Z_OK)
2592 		return (err);
2593 
2594 	return (Z_OK);
2595 }
2596 
2597 int
2598 zonecfg_get_attr_boolean(const struct zone_attrtab *attr, boolean_t *value)
2599 {
2600 	if (attr == NULL)
2601 		return (Z_INVAL);
2602 
2603 	if (strcmp(attr->zone_attr_type, DTD_ENTITY_BOOLEAN) != 0)
2604 		return (Z_INVAL);
2605 
2606 	if (strcmp(attr->zone_attr_value, DTD_ENTITY_TRUE) == 0) {
2607 		*value = B_TRUE;
2608 		return (Z_OK);
2609 	}
2610 	if (strcmp(attr->zone_attr_value, DTD_ENTITY_FALSE) == 0) {
2611 		*value = B_FALSE;
2612 		return (Z_OK);
2613 	}
2614 	return (Z_INVAL);
2615 }
2616 
2617 int
2618 zonecfg_get_attr_int(const struct zone_attrtab *attr, int64_t *value)
2619 {
2620 	long long result;
2621 	char *endptr;
2622 
2623 	if (attr == NULL)
2624 		return (Z_INVAL);
2625 
2626 	if (strcmp(attr->zone_attr_type, DTD_ENTITY_INT) != 0)
2627 		return (Z_INVAL);
2628 
2629 	errno = 0;
2630 	result = strtoll(attr->zone_attr_value, &endptr, 10);
2631 	if (errno != 0 || *endptr != '\0')
2632 		return (Z_INVAL);
2633 	*value = result;
2634 	return (Z_OK);
2635 }
2636 
2637 int
2638 zonecfg_get_attr_string(const struct zone_attrtab *attr, char *value,
2639     size_t val_sz)
2640 {
2641 	if (attr == NULL)
2642 		return (Z_INVAL);
2643 
2644 	if (strcmp(attr->zone_attr_type, DTD_ENTITY_STRING) != 0)
2645 		return (Z_INVAL);
2646 
2647 	if (strlcpy(value, attr->zone_attr_value, val_sz) >= val_sz)
2648 		return (Z_TOO_BIG);
2649 	return (Z_OK);
2650 }
2651 
2652 int
2653 zonecfg_get_attr_uint(const struct zone_attrtab *attr, uint64_t *value)
2654 {
2655 	unsigned long long result;
2656 	long long neg_result;
2657 	char *endptr;
2658 
2659 	if (attr == NULL)
2660 		return (Z_INVAL);
2661 
2662 	if (strcmp(attr->zone_attr_type, DTD_ENTITY_UINT) != 0)
2663 		return (Z_INVAL);
2664 
2665 	errno = 0;
2666 	result = strtoull(attr->zone_attr_value, &endptr, 10);
2667 	if (errno != 0 || *endptr != '\0')
2668 		return (Z_INVAL);
2669 	errno = 0;
2670 	neg_result = strtoll(attr->zone_attr_value, &endptr, 10);
2671 	/*
2672 	 * Incredibly, strtoull("<negative number>", ...) will not fail but
2673 	 * return whatever (negative) number cast as a u_longlong_t, so we
2674 	 * need to look for this here.
2675 	 */
2676 	if (errno == 0 && neg_result < 0)
2677 		return (Z_INVAL);
2678 	*value = result;
2679 	return (Z_OK);
2680 }
2681 
2682 int
2683 zonecfg_lookup_rctl(zone_dochandle_t handle, struct zone_rctltab *tabptr)
2684 {
2685 	xmlNodePtr cur, val;
2686 	char savedname[MAXNAMELEN];
2687 	struct zone_rctlvaltab *valptr;
2688 	int err;
2689 
2690 	if (tabptr->zone_rctl_name == NULL ||
2691 	    strlen(tabptr->zone_rctl_name) == 0)
2692 		return (Z_INVAL);
2693 
2694 	if ((err = operation_prep(handle)) != Z_OK)
2695 		return (err);
2696 
2697 	cur = handle->zone_dh_cur;
2698 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2699 		if (xmlStrcmp(cur->name, DTD_ELEM_RCTL))
2700 			continue;
2701 		if ((fetchprop(cur, DTD_ATTR_NAME, savedname,
2702 		    sizeof (savedname)) == Z_OK) &&
2703 		    (strcmp(savedname, tabptr->zone_rctl_name) == 0)) {
2704 			tabptr->zone_rctl_valptr = NULL;
2705 			for (val = cur->xmlChildrenNode; val != NULL;
2706 			    val = val->next) {
2707 				valptr = (struct zone_rctlvaltab *)malloc(
2708 				    sizeof (struct zone_rctlvaltab));
2709 				if (valptr == NULL)
2710 					return (Z_NOMEM);
2711 				if ((fetchprop(val, DTD_ATTR_PRIV,
2712 				    valptr->zone_rctlval_priv,
2713 				    sizeof (valptr->zone_rctlval_priv)) !=
2714 				    Z_OK))
2715 					break;
2716 				if ((fetchprop(val, DTD_ATTR_LIMIT,
2717 				    valptr->zone_rctlval_limit,
2718 				    sizeof (valptr->zone_rctlval_limit)) !=
2719 				    Z_OK))
2720 					break;
2721 				if ((fetchprop(val, DTD_ATTR_ACTION,
2722 				    valptr->zone_rctlval_action,
2723 				    sizeof (valptr->zone_rctlval_action)) !=
2724 				    Z_OK))
2725 					break;
2726 				if (zonecfg_add_rctl_value(tabptr, valptr) !=
2727 				    Z_OK)
2728 					break;
2729 			}
2730 			return (Z_OK);
2731 		}
2732 	}
2733 	return (Z_NO_RESOURCE_ID);
2734 }
2735 
2736 static int
2737 zonecfg_add_rctl_core(zone_dochandle_t handle, struct zone_rctltab *tabptr)
2738 {
2739 	xmlNodePtr newnode, cur = handle->zone_dh_cur, valnode;
2740 	struct zone_rctlvaltab *valptr;
2741 	int err;
2742 
2743 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_RCTL, NULL);
2744 	err = newprop(newnode, DTD_ATTR_NAME, tabptr->zone_rctl_name);
2745 	if (err != Z_OK)
2746 		return (err);
2747 	for (valptr = tabptr->zone_rctl_valptr; valptr != NULL;
2748 	    valptr = valptr->zone_rctlval_next) {
2749 		valnode = xmlNewTextChild(newnode, NULL,
2750 		    DTD_ELEM_RCTLVALUE, NULL);
2751 		err = newprop(valnode, DTD_ATTR_PRIV,
2752 		    valptr->zone_rctlval_priv);
2753 		if (err != Z_OK)
2754 			return (err);
2755 		err = newprop(valnode, DTD_ATTR_LIMIT,
2756 		    valptr->zone_rctlval_limit);
2757 		if (err != Z_OK)
2758 			return (err);
2759 		err = newprop(valnode, DTD_ATTR_ACTION,
2760 		    valptr->zone_rctlval_action);
2761 		if (err != Z_OK)
2762 			return (err);
2763 	}
2764 	return (Z_OK);
2765 }
2766 
2767 int
2768 zonecfg_add_rctl(zone_dochandle_t handle, struct zone_rctltab *tabptr)
2769 {
2770 	int err;
2771 
2772 	if (tabptr == NULL || tabptr->zone_rctl_name == NULL)
2773 		return (Z_INVAL);
2774 
2775 	if ((err = operation_prep(handle)) != Z_OK)
2776 		return (err);
2777 
2778 	if ((err = zonecfg_add_rctl_core(handle, tabptr)) != Z_OK)
2779 		return (err);
2780 
2781 	return (Z_OK);
2782 }
2783 
2784 static int
2785 zonecfg_delete_rctl_core(zone_dochandle_t handle, struct zone_rctltab *tabptr)
2786 {
2787 	xmlNodePtr cur = handle->zone_dh_cur;
2788 	xmlChar *savedname;
2789 	int name_result;
2790 
2791 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2792 		if (xmlStrcmp(cur->name, DTD_ELEM_RCTL))
2793 			continue;
2794 
2795 		savedname = xmlGetProp(cur, DTD_ATTR_NAME);
2796 		if (savedname == NULL)	/* shouldn't happen */
2797 			continue;
2798 		name_result = xmlStrcmp(savedname,
2799 		    (const xmlChar *) tabptr->zone_rctl_name);
2800 		xmlFree(savedname);
2801 
2802 		if (name_result == 0) {
2803 			xmlUnlinkNode(cur);
2804 			xmlFreeNode(cur);
2805 			return (Z_OK);
2806 		}
2807 	}
2808 	return (Z_NO_RESOURCE_ID);
2809 }
2810 
2811 int
2812 zonecfg_delete_rctl(zone_dochandle_t handle, struct zone_rctltab *tabptr)
2813 {
2814 	int err;
2815 
2816 	if (tabptr == NULL || tabptr->zone_rctl_name == NULL)
2817 		return (Z_INVAL);
2818 
2819 	if ((err = operation_prep(handle)) != Z_OK)
2820 		return (err);
2821 
2822 	if ((err = zonecfg_delete_rctl_core(handle, tabptr)) != Z_OK)
2823 		return (err);
2824 
2825 	return (Z_OK);
2826 }
2827 
2828 int
2829 zonecfg_modify_rctl(
2830 	zone_dochandle_t handle,
2831 	struct zone_rctltab *oldtabptr,
2832 	struct zone_rctltab *newtabptr)
2833 {
2834 	int err;
2835 
2836 	if (oldtabptr == NULL || oldtabptr->zone_rctl_name == NULL ||
2837 	    newtabptr == NULL || newtabptr->zone_rctl_name == NULL)
2838 		return (Z_INVAL);
2839 
2840 	if ((err = operation_prep(handle)) != Z_OK)
2841 		return (err);
2842 
2843 	if ((err = zonecfg_delete_rctl_core(handle, oldtabptr)) != Z_OK)
2844 		return (err);
2845 
2846 	if ((err = zonecfg_add_rctl_core(handle, newtabptr)) != Z_OK)
2847 		return (err);
2848 
2849 	return (Z_OK);
2850 }
2851 
2852 int
2853 zonecfg_add_rctl_value(
2854 	struct zone_rctltab *tabptr,
2855 	struct zone_rctlvaltab *valtabptr)
2856 {
2857 	struct zone_rctlvaltab *last, *old, *new;
2858 	rctlblk_t *rctlblk = alloca(rctlblk_size());
2859 
2860 	last = tabptr->zone_rctl_valptr;
2861 	for (old = last; old != NULL; old = old->zone_rctlval_next)
2862 		last = old;	/* walk to the end of the list */
2863 	new = valtabptr;	/* alloc'd by caller */
2864 	new->zone_rctlval_next = NULL;
2865 	if (zonecfg_construct_rctlblk(valtabptr, rctlblk) != Z_OK)
2866 		return (Z_INVAL);
2867 	if (!zonecfg_valid_rctlblk(rctlblk))
2868 		return (Z_INVAL);
2869 	if (last == NULL)
2870 		tabptr->zone_rctl_valptr = new;
2871 	else
2872 		last->zone_rctlval_next = new;
2873 	return (Z_OK);
2874 }
2875 
2876 int
2877 zonecfg_remove_rctl_value(
2878 	struct zone_rctltab *tabptr,
2879 	struct zone_rctlvaltab *valtabptr)
2880 {
2881 	struct zone_rctlvaltab *last, *this, *next;
2882 
2883 	last = tabptr->zone_rctl_valptr;
2884 	for (this = last; this != NULL; this = this->zone_rctlval_next) {
2885 		if (strcmp(this->zone_rctlval_priv,
2886 		    valtabptr->zone_rctlval_priv) == 0 &&
2887 		    strcmp(this->zone_rctlval_limit,
2888 		    valtabptr->zone_rctlval_limit) == 0 &&
2889 		    strcmp(this->zone_rctlval_action,
2890 		    valtabptr->zone_rctlval_action) == 0) {
2891 			next = this->zone_rctlval_next;
2892 			if (this == tabptr->zone_rctl_valptr)
2893 				tabptr->zone_rctl_valptr = next;
2894 			else
2895 				last->zone_rctlval_next = next;
2896 			free(this);
2897 			return (Z_OK);
2898 		} else
2899 			last = this;
2900 	}
2901 	return (Z_NO_PROPERTY_ID);
2902 }
2903 
2904 char *
2905 zonecfg_strerror(int errnum)
2906 {
2907 	switch (errnum) {
2908 	case Z_OK:
2909 		return (dgettext(TEXT_DOMAIN, "OK"));
2910 	case Z_EMPTY_DOCUMENT:
2911 		return (dgettext(TEXT_DOMAIN, "Empty document"));
2912 	case Z_WRONG_DOC_TYPE:
2913 		return (dgettext(TEXT_DOMAIN, "Wrong document type"));
2914 	case Z_BAD_PROPERTY:
2915 		return (dgettext(TEXT_DOMAIN, "Bad document property"));
2916 	case Z_TEMP_FILE:
2917 		return (dgettext(TEXT_DOMAIN,
2918 		    "Problem creating temporary file"));
2919 	case Z_SAVING_FILE:
2920 		return (dgettext(TEXT_DOMAIN, "Problem saving file"));
2921 	case Z_NO_ENTRY:
2922 		return (dgettext(TEXT_DOMAIN, "No such entry"));
2923 	case Z_BOGUS_ZONE_NAME:
2924 		return (dgettext(TEXT_DOMAIN, "Bogus zone name"));
2925 	case Z_REQD_RESOURCE_MISSING:
2926 		return (dgettext(TEXT_DOMAIN, "Required resource missing"));
2927 	case Z_REQD_PROPERTY_MISSING:
2928 		return (dgettext(TEXT_DOMAIN, "Required property missing"));
2929 	case Z_BAD_HANDLE:
2930 		return (dgettext(TEXT_DOMAIN, "Bad handle"));
2931 	case Z_NOMEM:
2932 		return (dgettext(TEXT_DOMAIN, "Out of memory"));
2933 	case Z_INVAL:
2934 		return (dgettext(TEXT_DOMAIN, "Invalid argument"));
2935 	case Z_ACCES:
2936 		return (dgettext(TEXT_DOMAIN, "Permission denied"));
2937 	case Z_TOO_BIG:
2938 		return (dgettext(TEXT_DOMAIN, "Argument list too long"));
2939 	case Z_MISC_FS:
2940 		return (dgettext(TEXT_DOMAIN,
2941 		    "Miscellaneous file system error"));
2942 	case Z_NO_ZONE:
2943 		return (dgettext(TEXT_DOMAIN, "No such zone configured"));
2944 	case Z_NO_RESOURCE_TYPE:
2945 		return (dgettext(TEXT_DOMAIN, "No such resource type"));
2946 	case Z_NO_RESOURCE_ID:
2947 		return (dgettext(TEXT_DOMAIN, "No such resource with that id"));
2948 	case Z_NO_PROPERTY_TYPE:
2949 		return (dgettext(TEXT_DOMAIN, "No such property type"));
2950 	case Z_NO_PROPERTY_ID:
2951 		return (dgettext(TEXT_DOMAIN, "No such property with that id"));
2952 	case Z_BAD_ZONE_STATE:
2953 		return (dgettext(TEXT_DOMAIN,
2954 		    "Zone state is invalid for the requested operation"));
2955 	case Z_INVALID_DOCUMENT:
2956 		return (dgettext(TEXT_DOMAIN, "Invalid document"));
2957 	case Z_NAME_IN_USE:
2958 		return (dgettext(TEXT_DOMAIN, "Zone name already in use"));
2959 	case Z_NO_SUCH_ID:
2960 		return (dgettext(TEXT_DOMAIN, "No such zone ID"));
2961 	case Z_UPDATING_INDEX:
2962 		return (dgettext(TEXT_DOMAIN, "Problem updating index file"));
2963 	case Z_LOCKING_FILE:
2964 		return (dgettext(TEXT_DOMAIN, "Locking index file"));
2965 	case Z_UNLOCKING_FILE:
2966 		return (dgettext(TEXT_DOMAIN, "Unlocking index file"));
2967 	case Z_INSUFFICIENT_SPEC:
2968 		return (dgettext(TEXT_DOMAIN, "Insufficient specification"));
2969 	case Z_RESOLVED_PATH:
2970 		return (dgettext(TEXT_DOMAIN, "Resolved path mismatch"));
2971 	case Z_IPV6_ADDR_PREFIX_LEN:
2972 		return (dgettext(TEXT_DOMAIN,
2973 		    "IPv6 address missing required prefix length"));
2974 	case Z_BOGUS_ADDRESS:
2975 		return (dgettext(TEXT_DOMAIN,
2976 		    "Neither an IPv4 nor an IPv6 address nor a host name"));
2977 	default:
2978 		return (dgettext(TEXT_DOMAIN, "Unknown error"));
2979 	}
2980 }
2981 
2982 /*
2983  * Note that the zonecfg_setXent() and zonecfg_endXent() calls are all the
2984  * same, as they just turn around and call zonecfg_setent() / zonecfg_endent().
2985  */
2986 
2987 static int
2988 zonecfg_setent(zone_dochandle_t handle)
2989 {
2990 	xmlNodePtr cur;
2991 	int err;
2992 
2993 	if (handle == NULL)
2994 		return (Z_INVAL);
2995 
2996 	if ((err = operation_prep(handle)) != Z_OK) {
2997 		handle->zone_dh_cur = NULL;
2998 		return (err);
2999 	}
3000 	cur = handle->zone_dh_cur;
3001 	cur = cur->xmlChildrenNode;
3002 	handle->zone_dh_cur = cur;
3003 	return (Z_OK);
3004 }
3005 
3006 static int
3007 zonecfg_endent(zone_dochandle_t handle)
3008 {
3009 	if (handle == NULL)
3010 		return (Z_INVAL);
3011 
3012 	handle->zone_dh_cur = handle->zone_dh_top;
3013 	return (Z_OK);
3014 }
3015 
3016 int
3017 zonecfg_setfsent(zone_dochandle_t handle)
3018 {
3019 	return (zonecfg_setent(handle));
3020 }
3021 
3022 int
3023 zonecfg_getfsent(zone_dochandle_t handle, struct zone_fstab *tabptr)
3024 {
3025 	xmlNodePtr cur, options;
3026 	char options_str[MAX_MNTOPT_STR];
3027 	int err;
3028 
3029 	if (handle == NULL)
3030 		return (Z_INVAL);
3031 
3032 	if ((cur = handle->zone_dh_cur) == NULL)
3033 		return (Z_NO_ENTRY);
3034 
3035 	for (; cur != NULL; cur = cur->next)
3036 		if (!xmlStrcmp(cur->name, DTD_ELEM_FS))
3037 			break;
3038 	if (cur == NULL) {
3039 		handle->zone_dh_cur = handle->zone_dh_top;
3040 		return (Z_NO_ENTRY);
3041 	}
3042 
3043 	if ((err = fetchprop(cur, DTD_ATTR_SPECIAL, tabptr->zone_fs_special,
3044 	    sizeof (tabptr->zone_fs_special))) != Z_OK) {
3045 		handle->zone_dh_cur = handle->zone_dh_top;
3046 		return (err);
3047 	}
3048 
3049 	if ((err = fetchprop(cur, DTD_ATTR_RAW, tabptr->zone_fs_raw,
3050 	    sizeof (tabptr->zone_fs_raw))) != Z_OK) {
3051 		handle->zone_dh_cur = handle->zone_dh_top;
3052 		return (err);
3053 	}
3054 
3055 	if ((err = fetchprop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir,
3056 	    sizeof (tabptr->zone_fs_dir))) != Z_OK) {
3057 		handle->zone_dh_cur = handle->zone_dh_top;
3058 		return (err);
3059 	}
3060 
3061 	if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_fs_type,
3062 	    sizeof (tabptr->zone_fs_type))) != Z_OK) {
3063 		handle->zone_dh_cur = handle->zone_dh_top;
3064 		return (err);
3065 	}
3066 
3067 	/* OK for options to be NULL */
3068 	tabptr->zone_fs_options = NULL;
3069 	for (options = cur->xmlChildrenNode; options != NULL;
3070 	    options = options->next) {
3071 		if (fetchprop(options, DTD_ATTR_NAME, options_str,
3072 		    sizeof (options_str)) != Z_OK)
3073 			break;
3074 		if (zonecfg_add_fs_option(tabptr, options_str) != Z_OK)
3075 			break;
3076 	}
3077 
3078 	handle->zone_dh_cur = cur->next;
3079 	return (Z_OK);
3080 }
3081 
3082 int
3083 zonecfg_endfsent(zone_dochandle_t handle)
3084 {
3085 	return (zonecfg_endent(handle));
3086 }
3087 
3088 int
3089 zonecfg_setipdent(zone_dochandle_t handle)
3090 {
3091 	return (zonecfg_setent(handle));
3092 }
3093 
3094 int
3095 zonecfg_getipdent(zone_dochandle_t handle, struct zone_fstab *tabptr)
3096 {
3097 	xmlNodePtr cur;
3098 	int err;
3099 
3100 	if (handle == NULL)
3101 		return (Z_INVAL);
3102 
3103 	if ((cur = handle->zone_dh_cur) == NULL)
3104 		return (Z_NO_ENTRY);
3105 
3106 	for (; cur != NULL; cur = cur->next)
3107 		if (!xmlStrcmp(cur->name, DTD_ELEM_IPD))
3108 			break;
3109 	if (cur == NULL) {
3110 		handle->zone_dh_cur = handle->zone_dh_top;
3111 		return (Z_NO_ENTRY);
3112 	}
3113 
3114 	if ((err = fetchprop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir,
3115 	    sizeof (tabptr->zone_fs_dir))) != Z_OK) {
3116 		handle->zone_dh_cur = handle->zone_dh_top;
3117 		return (err);
3118 	}
3119 
3120 	handle->zone_dh_cur = cur->next;
3121 	return (Z_OK);
3122 }
3123 
3124 int
3125 zonecfg_endipdent(zone_dochandle_t handle)
3126 {
3127 	return (zonecfg_endent(handle));
3128 }
3129 
3130 int
3131 zonecfg_setnwifent(zone_dochandle_t handle)
3132 {
3133 	return (zonecfg_setent(handle));
3134 }
3135 
3136 int
3137 zonecfg_getnwifent(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
3138 {
3139 	xmlNodePtr cur;
3140 	int err;
3141 
3142 	if (handle == NULL)
3143 		return (Z_INVAL);
3144 
3145 	if ((cur = handle->zone_dh_cur) == NULL)
3146 		return (Z_NO_ENTRY);
3147 
3148 	for (; cur != NULL; cur = cur->next)
3149 		if (!xmlStrcmp(cur->name, DTD_ELEM_NET))
3150 			break;
3151 	if (cur == NULL) {
3152 		handle->zone_dh_cur = handle->zone_dh_top;
3153 		return (Z_NO_ENTRY);
3154 	}
3155 
3156 	if ((err = fetchprop(cur, DTD_ATTR_ADDRESS, tabptr->zone_nwif_address,
3157 	    sizeof (tabptr->zone_nwif_address))) != Z_OK) {
3158 		handle->zone_dh_cur = handle->zone_dh_top;
3159 		return (err);
3160 	}
3161 
3162 	if ((err = fetchprop(cur, DTD_ATTR_PHYSICAL, tabptr->zone_nwif_physical,
3163 	    sizeof (tabptr->zone_nwif_physical))) != Z_OK) {
3164 		handle->zone_dh_cur = handle->zone_dh_top;
3165 		return (err);
3166 	}
3167 
3168 	handle->zone_dh_cur = cur->next;
3169 	return (Z_OK);
3170 }
3171 
3172 int
3173 zonecfg_endnwifent(zone_dochandle_t handle)
3174 {
3175 	return (zonecfg_endent(handle));
3176 }
3177 
3178 int
3179 zonecfg_setdevent(zone_dochandle_t handle)
3180 {
3181 	return (zonecfg_setent(handle));
3182 }
3183 
3184 int
3185 zonecfg_getdevent(zone_dochandle_t handle, struct zone_devtab *tabptr)
3186 {
3187 	xmlNodePtr cur;
3188 	int err;
3189 
3190 	if (handle == NULL)
3191 		return (Z_INVAL);
3192 
3193 	if ((cur = handle->zone_dh_cur) == NULL)
3194 		return (Z_NO_ENTRY);
3195 
3196 	for (; cur != NULL; cur = cur->next)
3197 		if (!xmlStrcmp(cur->name, DTD_ELEM_DEVICE))
3198 			break;
3199 	if (cur == NULL) {
3200 		handle->zone_dh_cur = handle->zone_dh_top;
3201 		return (Z_NO_ENTRY);
3202 	}
3203 
3204 	if ((err = fetchprop(cur, DTD_ATTR_MATCH, tabptr->zone_dev_match,
3205 	    sizeof (tabptr->zone_dev_match))) != Z_OK) {
3206 		handle->zone_dh_cur = handle->zone_dh_top;
3207 		return (err);
3208 	}
3209 
3210 	handle->zone_dh_cur = cur->next;
3211 	return (Z_OK);
3212 }
3213 
3214 int
3215 zonecfg_enddevent(zone_dochandle_t handle)
3216 {
3217 	return (zonecfg_endent(handle));
3218 }
3219 
3220 int
3221 zonecfg_setrctlent(zone_dochandle_t handle)
3222 {
3223 	return (zonecfg_setent(handle));
3224 }
3225 
3226 int
3227 zonecfg_getrctlent(zone_dochandle_t handle, struct zone_rctltab *tabptr)
3228 {
3229 	xmlNodePtr cur, val;
3230 	struct zone_rctlvaltab *valptr;
3231 	int err;
3232 
3233 	if (handle == NULL)
3234 		return (Z_INVAL);
3235 
3236 	if ((cur = handle->zone_dh_cur) == NULL)
3237 		return (Z_NO_ENTRY);
3238 
3239 	for (; cur != NULL; cur = cur->next)
3240 		if (!xmlStrcmp(cur->name, DTD_ELEM_RCTL))
3241 			break;
3242 	if (cur == NULL) {
3243 		handle->zone_dh_cur = handle->zone_dh_top;
3244 		return (Z_NO_ENTRY);
3245 	}
3246 
3247 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_rctl_name,
3248 	    sizeof (tabptr->zone_rctl_name))) != Z_OK) {
3249 		handle->zone_dh_cur = handle->zone_dh_top;
3250 		return (err);
3251 	}
3252 
3253 	tabptr->zone_rctl_valptr = NULL;
3254 	for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
3255 		valptr = (struct zone_rctlvaltab *)malloc(
3256 		    sizeof (struct zone_rctlvaltab));
3257 		if (valptr == NULL)
3258 			return (Z_NOMEM);
3259 		if (fetchprop(val, DTD_ATTR_PRIV, valptr->zone_rctlval_priv,
3260 		    sizeof (valptr->zone_rctlval_priv)) != Z_OK)
3261 			break;
3262 		if (fetchprop(val, DTD_ATTR_LIMIT, valptr->zone_rctlval_limit,
3263 		    sizeof (valptr->zone_rctlval_limit)) != Z_OK)
3264 			break;
3265 		if (fetchprop(val, DTD_ATTR_ACTION, valptr->zone_rctlval_action,
3266 		    sizeof (valptr->zone_rctlval_action)) != Z_OK)
3267 			break;
3268 		if (zonecfg_add_rctl_value(tabptr, valptr) != Z_OK)
3269 			break;
3270 	}
3271 
3272 	handle->zone_dh_cur = cur->next;
3273 	return (Z_OK);
3274 }
3275 
3276 int
3277 zonecfg_endrctlent(zone_dochandle_t handle)
3278 {
3279 	return (zonecfg_endent(handle));
3280 }
3281 
3282 int
3283 zonecfg_setattrent(zone_dochandle_t handle)
3284 {
3285 	return (zonecfg_setent(handle));
3286 }
3287 
3288 int
3289 zonecfg_getattrent(zone_dochandle_t handle, struct zone_attrtab *tabptr)
3290 {
3291 	xmlNodePtr cur;
3292 	int err;
3293 
3294 	if (handle == NULL)
3295 		return (Z_INVAL);
3296 
3297 	if ((cur = handle->zone_dh_cur) == NULL)
3298 		return (Z_NO_ENTRY);
3299 
3300 	for (; cur != NULL; cur = cur->next)
3301 		if (!xmlStrcmp(cur->name, DTD_ELEM_ATTR))
3302 			break;
3303 	if (cur == NULL) {
3304 		handle->zone_dh_cur = handle->zone_dh_top;
3305 		return (Z_NO_ENTRY);
3306 	}
3307 
3308 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_attr_name,
3309 	    sizeof (tabptr->zone_attr_name))) != Z_OK) {
3310 		handle->zone_dh_cur = handle->zone_dh_top;
3311 		return (err);
3312 	}
3313 
3314 	if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_attr_type,
3315 	    sizeof (tabptr->zone_attr_type))) != Z_OK) {
3316 		handle->zone_dh_cur = handle->zone_dh_top;
3317 		return (err);
3318 	}
3319 
3320 	if ((err = fetchprop(cur, DTD_ATTR_VALUE, tabptr->zone_attr_value,
3321 	    sizeof (tabptr->zone_attr_value))) != Z_OK) {
3322 		handle->zone_dh_cur = handle->zone_dh_top;
3323 		return (err);
3324 	}
3325 
3326 	handle->zone_dh_cur = cur->next;
3327 	return (Z_OK);
3328 }
3329 
3330 int
3331 zonecfg_endattrent(zone_dochandle_t handle)
3332 {
3333 	return (zonecfg_endent(handle));
3334 }
3335 
3336 /* This will ultimately be configurable. */
3337 static const char *priv_list[] = {
3338 	PRIV_FILE_CHOWN,
3339 	PRIV_FILE_CHOWN_SELF,
3340 	PRIV_FILE_DAC_EXECUTE,
3341 	PRIV_FILE_DAC_READ,
3342 	PRIV_FILE_DAC_SEARCH,
3343 	PRIV_FILE_DAC_WRITE,
3344 	PRIV_FILE_OWNER,
3345 	PRIV_FILE_SETID,
3346 	PRIV_IPC_DAC_READ,
3347 	PRIV_IPC_DAC_WRITE,
3348 	PRIV_IPC_OWNER,
3349 	PRIV_NET_ICMPACCESS,
3350 	PRIV_NET_PRIVADDR,
3351 	PRIV_PROC_CHROOT,
3352 	PRIV_SYS_AUDIT,
3353 	PRIV_PROC_AUDIT,
3354 	PRIV_PROC_OWNER,
3355 	PRIV_PROC_SETID,
3356 	PRIV_PROC_TASKID,
3357 	PRIV_SYS_ACCT,
3358 	PRIV_SYS_ADMIN,
3359 	PRIV_SYS_MOUNT,
3360 	PRIV_SYS_NFS,
3361 	PRIV_SYS_RESOURCE,
3362 	PRIV_CONTRACT_EVENT,
3363 	PRIV_CONTRACT_OBSERVER,
3364 	NULL
3365 };
3366 
3367 int
3368 zonecfg_get_privset(priv_set_t *privs)
3369 {
3370 	const char **strp;
3371 	priv_set_t *basic = priv_str_to_set("basic", ",", NULL);
3372 
3373 	if (basic == NULL)
3374 		return (Z_INVAL);
3375 
3376 	priv_union(basic, privs);
3377 	priv_freeset(basic);
3378 
3379 	for (strp = priv_list; *strp != NULL; strp++) {
3380 		if (priv_addset(privs, *strp) != 0) {
3381 			return (Z_INVAL);
3382 		}
3383 	}
3384 	return (Z_OK);
3385 }
3386 
3387 int
3388 zone_get_zonepath(char *zone_name, char *zonepath, size_t rp_sz)
3389 {
3390 	zone_dochandle_t handle;
3391 	boolean_t found = B_FALSE;
3392 	struct zoneent *ze;
3393 	FILE *cookie;
3394 	int err;
3395 	char *cp;
3396 
3397 	if (zone_name == NULL)
3398 		return (Z_INVAL);
3399 
3400 	(void) strlcpy(zonepath, zonecfg_root, rp_sz);
3401 	cp = zonepath + strlen(zonepath);
3402 	while (cp > zonepath && cp[-1] == '/')
3403 		*--cp = '\0';
3404 
3405 	if (strcmp(zone_name, GLOBAL_ZONENAME) == 0) {
3406 		if (zonepath[0] == '\0')
3407 			(void) strlcpy(zonepath, "/", rp_sz);
3408 		return (Z_OK);
3409 	}
3410 
3411 	/*
3412 	 * First check the index file.  Because older versions did not have
3413 	 * a copy of the zone path, allow for it to be zero length, in which
3414 	 * case we ignore this result and fall back to the XML files.
3415 	 */
3416 	cookie = setzoneent();
3417 	while ((ze = getzoneent_private(cookie)) != NULL) {
3418 		if (strcmp(ze->zone_name, zone_name) == 0) {
3419 			found = B_TRUE;
3420 			if (ze->zone_path[0] != '\0')
3421 				(void) strlcpy(cp, ze->zone_path,
3422 				    rp_sz - (cp - zonepath));
3423 		}
3424 		free(ze);
3425 		if (found)
3426 			break;
3427 	}
3428 	endzoneent(cookie);
3429 	if (found && *cp != '\0')
3430 		return (Z_OK);
3431 
3432 	/* Fall back to the XML files. */
3433 	if ((handle = zonecfg_init_handle()) == NULL)
3434 		return (Z_NOMEM);
3435 
3436 	/*
3437 	 * Check the snapshot first: if a zone is running, its zonepath
3438 	 * may have changed.
3439 	 */
3440 	if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) {
3441 		if ((err = zonecfg_get_handle(zone_name, handle)) != Z_OK)
3442 			return (err);
3443 	}
3444 	err = zonecfg_get_zonepath(handle, zonepath, rp_sz);
3445 	zonecfg_fini_handle(handle);
3446 	return (err);
3447 }
3448 
3449 int
3450 zone_get_rootpath(char *zone_name, char *rootpath, size_t rp_sz)
3451 {
3452 	int err;
3453 
3454 	/* This function makes sense for non-global zones only. */
3455 	if (strcmp(zone_name, GLOBAL_ZONENAME) == 0)
3456 		return (Z_BOGUS_ZONE_NAME);
3457 	if ((err = zone_get_zonepath(zone_name, rootpath, rp_sz)) != Z_OK)
3458 		return (err);
3459 	if (strlcat(rootpath, "/root", rp_sz) >= rp_sz)
3460 		return (Z_TOO_BIG);
3461 	return (Z_OK);
3462 }
3463 
3464 static zone_state_t
3465 kernel_state_to_user_state(zoneid_t zoneid, zone_status_t kernel_state)
3466 {
3467 	char zoneroot[MAXPATHLEN];
3468 	size_t zlen;
3469 
3470 	assert(kernel_state <= ZONE_MAX_STATE);
3471 	switch (kernel_state) {
3472 		case ZONE_IS_UNINITIALIZED:
3473 			return (ZONE_STATE_READY);
3474 		case ZONE_IS_READY:
3475 			/*
3476 			 * If the zone's root is mounted on $ZONEPATH/lu, then
3477 			 * it's a mounted scratch zone.
3478 			 */
3479 			if (zone_getattr(zoneid, ZONE_ATTR_ROOT, zoneroot,
3480 			    sizeof (zoneroot)) >= 0) {
3481 				zlen = strlen(zoneroot);
3482 				if (zlen > 3 &&
3483 				    strcmp(zoneroot + zlen - 3, "/lu") == 0)
3484 					return (ZONE_STATE_MOUNTED);
3485 			}
3486 			return (ZONE_STATE_READY);
3487 		case ZONE_IS_BOOTING:
3488 		case ZONE_IS_RUNNING:
3489 			return (ZONE_STATE_RUNNING);
3490 		case ZONE_IS_SHUTTING_DOWN:
3491 		case ZONE_IS_EMPTY:
3492 			return (ZONE_STATE_SHUTTING_DOWN);
3493 		case ZONE_IS_DOWN:
3494 		case ZONE_IS_DYING:
3495 		case ZONE_IS_DEAD:
3496 		default:
3497 			return (ZONE_STATE_DOWN);
3498 	}
3499 	/* NOTREACHED */
3500 }
3501 
3502 int
3503 zone_get_state(char *zone_name, zone_state_t *state_num)
3504 {
3505 	zone_status_t status;
3506 	zoneid_t zone_id;
3507 	struct zoneent *ze;
3508 	boolean_t found = B_FALSE;
3509 	FILE *cookie;
3510 	char kernzone[ZONENAME_MAX];
3511 	FILE *fp;
3512 
3513 	if (zone_name == NULL)
3514 		return (Z_INVAL);
3515 
3516 	/*
3517 	 * If we're looking at an alternate root, then we need to query the
3518 	 * kernel using the scratch zone name.
3519 	 */
3520 	zone_id = -1;
3521 	if (*zonecfg_root != '\0' && !zonecfg_is_scratch(zone_name)) {
3522 		if ((fp = zonecfg_open_scratch("", B_FALSE)) != NULL) {
3523 			if (zonecfg_find_scratch(fp, zone_name, zonecfg_root,
3524 			    kernzone, sizeof (kernzone)) == 0)
3525 				zone_id = getzoneidbyname(kernzone);
3526 			zonecfg_close_scratch(fp);
3527 		}
3528 	} else {
3529 		zone_id = getzoneidbyname(zone_name);
3530 	}
3531 
3532 	/* check to see if zone is running */
3533 	if (zone_id != -1 &&
3534 	    zone_getattr(zone_id, ZONE_ATTR_STATUS, &status,
3535 	    sizeof (status)) >= 0) {
3536 		*state_num = kernel_state_to_user_state(zone_id, status);
3537 		return (Z_OK);
3538 	}
3539 
3540 	cookie = setzoneent();
3541 	while ((ze = getzoneent_private(cookie)) != NULL) {
3542 		if (strcmp(ze->zone_name, zone_name) == 0) {
3543 			found = B_TRUE;
3544 			*state_num = ze->zone_state;
3545 		}
3546 		free(ze);
3547 		if (found)
3548 			break;
3549 	}
3550 	endzoneent(cookie);
3551 	return ((found) ? Z_OK : Z_NO_ZONE);
3552 }
3553 
3554 int
3555 zone_set_state(char *zone, zone_state_t state)
3556 {
3557 	struct zoneent ze;
3558 
3559 	if (state != ZONE_STATE_CONFIGURED && state != ZONE_STATE_INSTALLED &&
3560 	    state != ZONE_STATE_INCOMPLETE)
3561 		return (Z_INVAL);
3562 
3563 	bzero(&ze, sizeof (ze));
3564 	(void) strlcpy(ze.zone_name, zone, sizeof (ze.zone_name));
3565 	ze.zone_state = state;
3566 	(void) strlcpy(ze.zone_path, "", sizeof (ze.zone_path));
3567 	return (putzoneent(&ze, PZE_MODIFY));
3568 }
3569 
3570 /*
3571  * Get id (if any) for specified zone.  There are four possible outcomes:
3572  * - If the string corresponds to the numeric id of an active (booted)
3573  *   zone, sets *zip to the zone id and returns 0.
3574  * - If the string corresponds to the name of an active (booted) zone,
3575  *   sets *zip to the zone id and returns 0.
3576  * - If the string is a name in the configuration but is not booted,
3577  *   sets *zip to ZONE_ID_UNDEFINED and returns 0.
3578  * - Otherwise, leaves *zip unchanged and returns -1.
3579  *
3580  * This function acts as an auxiliary filter on the function of the same
3581  * name in libc; the linker binds to this version if libzonecfg exists,
3582  * and the libc version if it doesn't.  Any changes to this version of
3583  * the function should probably be reflected in the libc version as well.
3584  */
3585 int
3586 zone_get_id(const char *str, zoneid_t *zip)
3587 {
3588 	zone_dochandle_t hdl;
3589 	zoneid_t zoneid;
3590 	char *cp;
3591 	int err;
3592 
3593 	/* first try looking for active zone by id */
3594 	errno = 0;
3595 	zoneid = (zoneid_t)strtol(str, &cp, 0);
3596 	if (errno == 0 && cp != str && *cp == '\0' &&
3597 	    getzonenamebyid(zoneid, NULL, 0) != -1) {
3598 		*zip = zoneid;
3599 		return (0);
3600 	}
3601 
3602 	/* then look for active zone by name */
3603 	if ((zoneid = getzoneidbyname(str)) != -1) {
3604 		*zip = zoneid;
3605 		return (0);
3606 	}
3607 
3608 	/* if in global zone, try looking up name in configuration database */
3609 	if (getzoneid() != GLOBAL_ZONEID ||
3610 	    (hdl = zonecfg_init_handle()) == NULL)
3611 		return (-1);
3612 
3613 	if (zonecfg_get_handle(str, hdl) == Z_OK) {
3614 		/* zone exists but isn't active */
3615 		*zip = ZONE_ID_UNDEFINED;
3616 		err = 0;
3617 	} else {
3618 		err = -1;
3619 	}
3620 
3621 	zonecfg_fini_handle(hdl);
3622 	return (err);
3623 }
3624 
3625 char *
3626 zone_state_str(zone_state_t state_num)
3627 {
3628 	switch (state_num) {
3629 	case ZONE_STATE_CONFIGURED:
3630 		return (ZONE_STATE_STR_CONFIGURED);
3631 	case ZONE_STATE_INCOMPLETE:
3632 		return (ZONE_STATE_STR_INCOMPLETE);
3633 	case ZONE_STATE_INSTALLED:
3634 		return (ZONE_STATE_STR_INSTALLED);
3635 	case ZONE_STATE_READY:
3636 		return (ZONE_STATE_STR_READY);
3637 	case ZONE_STATE_MOUNTED:
3638 		return (ZONE_STATE_STR_MOUNTED);
3639 	case ZONE_STATE_RUNNING:
3640 		return (ZONE_STATE_STR_RUNNING);
3641 	case ZONE_STATE_SHUTTING_DOWN:
3642 		return (ZONE_STATE_STR_SHUTTING_DOWN);
3643 	case ZONE_STATE_DOWN:
3644 		return (ZONE_STATE_STR_DOWN);
3645 	default:
3646 		return ("unknown");
3647 	}
3648 }
3649 
3650 /*
3651  * Given a UUID value, find an associated zone name.  This is intended to be
3652  * used by callers who set up some 'default' name (corresponding to the
3653  * expected name for the zone) in the zonename buffer, and thus the function
3654  * doesn't touch this buffer on failure.
3655  */
3656 int
3657 zonecfg_get_name_by_uuid(const uuid_t uuid, char *zonename, size_t namelen)
3658 {
3659 	FILE *fp;
3660 	struct zoneent *ze;
3661 
3662 	/*
3663 	 * A small amount of subterfuge via casts is necessary here because
3664 	 * libuuid doesn't use const correctly, but we don't want to export
3665 	 * this brokenness to our clients.
3666 	 */
3667 	if (uuid_is_null(*(uuid_t *)&uuid))
3668 		return (Z_NO_ZONE);
3669 	if ((fp = setzoneent()) == NULL)
3670 		return (Z_NO_ZONE);
3671 	while ((ze = getzoneent_private(fp)) != NULL) {
3672 		if (uuid_compare(*(uuid_t *)&uuid, ze->zone_uuid) == 0)
3673 			break;
3674 		free(ze);
3675 	}
3676 	endzoneent(fp);
3677 	if (ze != NULL) {
3678 		(void) strlcpy(zonename, ze->zone_name, namelen);
3679 		free(ze);
3680 		return (Z_OK);
3681 	} else {
3682 		return (Z_NO_ZONE);
3683 	}
3684 }
3685 
3686 /*
3687  * Given a zone name, get its UUID.  Returns a "NULL" UUID value if the zone
3688  * exists but the file doesn't have a value set yet.  Returns an error if the
3689  * zone cannot be located.
3690  */
3691 int
3692 zonecfg_get_uuid(const char *zonename, uuid_t uuid)
3693 {
3694 	FILE *fp;
3695 	struct zoneent *ze;
3696 
3697 	if ((fp = setzoneent()) == NULL)
3698 		return (Z_NO_ZONE);
3699 	while ((ze = getzoneent_private(fp)) != NULL) {
3700 		if (strcmp(ze->zone_name, zonename) == 0)
3701 			break;
3702 		free(ze);
3703 	}
3704 	endzoneent(fp);
3705 	if (ze != NULL) {
3706 		uuid_copy(uuid, ze->zone_uuid);
3707 		free(ze);
3708 		return (Z_OK);
3709 	} else {
3710 		return (Z_NO_ZONE);
3711 	}
3712 }
3713 
3714 /*
3715  * File-system convenience functions.
3716  */
3717 boolean_t
3718 zonecfg_valid_fs_type(const char *type)
3719 {
3720 	/*
3721 	 * We already know which FS types don't work.
3722 	 */
3723 	if (strcmp(type, "proc") == 0 ||
3724 	    strcmp(type, "mntfs") == 0 ||
3725 	    strcmp(type, "autofs") == 0 ||
3726 	    strncmp(type, "nfs", sizeof ("nfs") - 1) == 0 ||
3727 	    strcmp(type, "cachefs") == 0)
3728 		return (B_FALSE);
3729 	/*
3730 	 * The caller may do more detailed verification to make sure other
3731 	 * aspects of this filesystem type make sense.
3732 	 */
3733 	return (B_TRUE);
3734 }
3735 
3736 /*
3737  * Generally uninteresting rctl convenience functions.
3738  */
3739 
3740 int
3741 zonecfg_construct_rctlblk(const struct zone_rctlvaltab *rctlval,
3742     rctlblk_t *rctlblk)
3743 {
3744 	unsigned long long ull;
3745 	char *endp;
3746 	rctl_priv_t priv;
3747 	rctl_qty_t limit;
3748 	uint_t action;
3749 
3750 	/* Get the privilege */
3751 	if (strcmp(rctlval->zone_rctlval_priv, "basic") == 0) {
3752 		priv = RCPRIV_BASIC;
3753 	} else if (strcmp(rctlval->zone_rctlval_priv, "privileged") == 0) {
3754 		priv = RCPRIV_PRIVILEGED;
3755 	} else {
3756 		/* Invalid privilege */
3757 		return (Z_INVAL);
3758 	}
3759 
3760 	/* deal with negative input; strtoull(3c) doesn't do what we want */
3761 	if (rctlval->zone_rctlval_limit[0] == '-')
3762 		return (Z_INVAL);
3763 	/* Get the limit */
3764 	errno = 0;
3765 	ull = strtoull(rctlval->zone_rctlval_limit, &endp, 0);
3766 	if (errno != 0 || *endp != '\0') {
3767 		/* parse failed */
3768 		return (Z_INVAL);
3769 	}
3770 	limit = (rctl_qty_t)ull;
3771 
3772 	/* Get the action */
3773 	if (strcmp(rctlval->zone_rctlval_action, "none") == 0) {
3774 		action = RCTL_LOCAL_NOACTION;
3775 	} else if (strcmp(rctlval->zone_rctlval_action, "signal") == 0) {
3776 		action = RCTL_LOCAL_SIGNAL;
3777 	} else if (strcmp(rctlval->zone_rctlval_action, "deny") == 0) {
3778 		action = RCTL_LOCAL_DENY;
3779 	} else {
3780 		/* Invalid Action */
3781 		return (Z_INVAL);
3782 	}
3783 	rctlblk_set_local_action(rctlblk, action, 0);
3784 	rctlblk_set_privilege(rctlblk, priv);
3785 	rctlblk_set_value(rctlblk, limit);
3786 	return (Z_OK);
3787 }
3788 
3789 static int
3790 rctl_check(const char *rctlname, void *arg)
3791 {
3792 	const char *attrname = arg;
3793 
3794 	/*
3795 	 * Returning 1 here is our signal to zonecfg_is_rctl() that it is
3796 	 * indeed an rctl name recognized by the system.
3797 	 */
3798 	return (strcmp(rctlname, attrname) == 0 ? 1 : 0);
3799 }
3800 
3801 boolean_t
3802 zonecfg_is_rctl(const char *name)
3803 {
3804 	return (rctl_walk(rctl_check, (void *)name) == 1);
3805 }
3806 
3807 boolean_t
3808 zonecfg_valid_rctlname(const char *name)
3809 {
3810 	const char *c;
3811 
3812 	if (strncmp(name, "zone.", sizeof ("zone.") - 1) != 0)
3813 		return (B_FALSE);
3814 	if (strlen(name) == sizeof ("zone.") - 1)
3815 		return (B_FALSE);
3816 	for (c = name + sizeof ("zone.") - 1; *c != '\0'; c++) {
3817 		if (!isalpha(*c) && *c != '-')
3818 			return (B_FALSE);
3819 	}
3820 	return (B_TRUE);
3821 }
3822 
3823 boolean_t
3824 zonecfg_valid_rctlblk(const rctlblk_t *rctlblk)
3825 {
3826 	rctl_priv_t priv = rctlblk_get_privilege((rctlblk_t *)rctlblk);
3827 	uint_t action = rctlblk_get_local_action((rctlblk_t *)rctlblk, NULL);
3828 
3829 	if (priv != RCPRIV_PRIVILEGED)
3830 		return (B_FALSE);
3831 	if (action != RCTL_LOCAL_NOACTION && action != RCTL_LOCAL_DENY)
3832 		return (B_FALSE);
3833 	return (B_TRUE);
3834 }
3835 
3836 boolean_t
3837 zonecfg_valid_rctl(const char *name, const rctlblk_t *rctlblk)
3838 {
3839 	rctlblk_t *current, *next;
3840 	rctl_qty_t limit = rctlblk_get_value((rctlblk_t *)rctlblk);
3841 	uint_t action = rctlblk_get_local_action((rctlblk_t *)rctlblk, NULL);
3842 	uint_t global_flags;
3843 
3844 	if (!zonecfg_valid_rctlblk(rctlblk))
3845 		return (B_FALSE);
3846 	if (!zonecfg_valid_rctlname(name))
3847 		return (B_FALSE);
3848 
3849 	current = alloca(rctlblk_size());
3850 	if (getrctl(name, NULL, current, RCTL_FIRST) != 0)
3851 		return (B_TRUE);	/* not an rctl on this system */
3852 	/*
3853 	 * Make sure the proposed value isn't greater than the current system
3854 	 * value.
3855 	 */
3856 	next = alloca(rctlblk_size());
3857 	while (rctlblk_get_privilege(current) != RCPRIV_SYSTEM) {
3858 		rctlblk_t *tmp;
3859 
3860 		if (getrctl(name, current, next, RCTL_NEXT) != 0)
3861 			return (B_FALSE);	/* shouldn't happen */
3862 		tmp = current;
3863 		current = next;
3864 		next = tmp;
3865 	}
3866 	if (limit > rctlblk_get_value(current))
3867 		return (B_FALSE);
3868 
3869 	/*
3870 	 * Make sure the proposed action is allowed.
3871 	 */
3872 	global_flags = rctlblk_get_global_flags(current);
3873 	if ((global_flags & RCTL_GLOBAL_DENY_NEVER) &&
3874 	    action == RCTL_LOCAL_DENY)
3875 		return (B_FALSE);
3876 	if ((global_flags & RCTL_GLOBAL_DENY_ALWAYS) &&
3877 	    action == RCTL_LOCAL_NOACTION)
3878 		return (B_FALSE);
3879 
3880 	return (B_TRUE);
3881 }
3882 
3883 /*
3884  * There is always a race condition between reading the initial copy of
3885  * a zones state and its state changing.  We address this by providing
3886  * zonecfg_notify_critical_enter and zonecfg_noticy_critical_exit functions.
3887  * When zonecfg_critical_enter is called, sets the state field to LOCKED
3888  * and aquires biglock. Biglock protects against other threads executing
3889  * critical_enter and the state field protects against state changes during
3890  * the critical period.
3891  *
3892  * If any state changes occur, zn_cb will set the failed field of the znotify
3893  * structure.  This will cause the critical_exit function to re-lock the
3894  * channel and return an error. Since evsnts may be delayed, the critical_exit
3895  * function "flushes" the queue by putting an event on the queue and waiting for
3896  * zn_cb to notify critical_exit that it received the ping event.
3897  */
3898 static const char *
3899 string_get_tok(const char *in, char delim, int num)
3900 {
3901 	int i = 0;
3902 
3903 	for (; i < num; in++) {
3904 		if (*in == delim)
3905 			i++;
3906 		if (*in == 0)
3907 			return (NULL);
3908 	}
3909 	return (in);
3910 }
3911 
3912 static boolean_t
3913 is_ping(sysevent_t *ev)
3914 {
3915 	if (strcmp(sysevent_get_subclass_name(ev),
3916 		ZONE_EVENT_PING_SUBCLASS) == 0) {
3917 		return (B_TRUE);
3918 	} else {
3919 		return (B_FALSE);
3920 	}
3921 }
3922 
3923 static boolean_t
3924 is_my_ping(sysevent_t *ev)
3925 {
3926 	const char *sender;
3927 	char mypid[sizeof (pid_t) * 3 + 1];
3928 
3929 	(void) snprintf(mypid, sizeof (mypid), "%i", getpid());
3930 	sender = string_get_tok(sysevent_get_pub(ev), ':', 3);
3931 	if (sender == NULL)
3932 		return (B_FALSE);
3933 	if (strcmp(sender, mypid) != 0)
3934 		return (B_FALSE);
3935 	return (B_TRUE);
3936 }
3937 
3938 static int
3939 do_callback(struct znotify *zevtchan, sysevent_t *ev)
3940 {
3941 	nvlist_t *l;
3942 	int zid;
3943 	char *zonename;
3944 	char *newstate;
3945 	char *oldstate;
3946 	int ret;
3947 	hrtime_t when;
3948 
3949 	if (strcmp(sysevent_get_subclass_name(ev),
3950 	    ZONE_EVENT_STATUS_SUBCLASS) == 0) {
3951 
3952 		if (sysevent_get_attr_list(ev, &l) != 0) {
3953 			if (errno == ENOMEM) {
3954 				zevtchan->zn_failure_count++;
3955 				return (EAGAIN);
3956 			}
3957 			return (0);
3958 		}
3959 		ret = 0;
3960 
3961 		if ((nvlist_lookup_string(l, ZONE_CB_NAME, &zonename) == 0) &&
3962 		    (nvlist_lookup_string(l, ZONE_CB_NEWSTATE, &newstate)
3963 			== 0) &&
3964 		    (nvlist_lookup_string(l, ZONE_CB_OLDSTATE, &oldstate)
3965 			== 0) &&
3966 		    (nvlist_lookup_uint64(l, ZONE_CB_TIMESTAMP,
3967 			    (uint64_t *)&when) == 0) &&
3968 		    (nvlist_lookup_int32(l, ZONE_CB_ZONEID, &zid) == 0)) {
3969 			ret = zevtchan->zn_callback(zonename, zid, newstate,
3970 			    oldstate, when, zevtchan->zn_private);
3971 		}
3972 
3973 		zevtchan->zn_failure_count = 0;
3974 		nvlist_free(l);
3975 		return (ret);
3976 	} else {
3977 		/*
3978 		 * We have received an event in an unknown subclass. Ignore.
3979 		 */
3980 		zevtchan->zn_failure_count = 0;
3981 		return (0);
3982 	}
3983 }
3984 
3985 static int
3986 zn_cb(sysevent_t *ev, void *p)
3987 {
3988 	struct znotify *zevtchan = p;
3989 	int error;
3990 
3991 	(void) pthread_mutex_lock(&(zevtchan->zn_mutex));
3992 
3993 	if (is_ping(ev) && !is_my_ping(ev)) {
3994 		(void) pthread_mutex_unlock((&zevtchan->zn_mutex));
3995 		return (0);
3996 	}
3997 
3998 	if (zevtchan->zn_state == ZN_LOCKED) {
3999 		assert(!is_ping(ev));
4000 		zevtchan->zn_failed = B_TRUE;
4001 		(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
4002 		return (0);
4003 	}
4004 
4005 	if (zevtchan->zn_state == ZN_PING_INFLIGHT) {
4006 		if (is_ping(ev)) {
4007 			zevtchan->zn_state = ZN_PING_RECEIVED;
4008 			(void) pthread_cond_signal(&(zevtchan->zn_cond));
4009 			(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
4010 			return (0);
4011 		} else {
4012 			zevtchan->zn_failed = B_TRUE;
4013 			(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
4014 			return (0);
4015 		}
4016 	}
4017 
4018 	if (zevtchan->zn_state == ZN_UNLOCKED) {
4019 
4020 		error = do_callback(zevtchan, ev);
4021 		(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
4022 		/*
4023 		 * Every ENOMEM failure causes do_callback to increment
4024 		 * zn_failure_count and every success causes it to
4025 		 * set zn_failure_count to zero.  If we got EAGAIN,
4026 		 * we will sleep for zn_failure_count seconds and return
4027 		 * EAGAIN to gpec to try again.
4028 		 *
4029 		 * After 55 seconds, or 10 try's we give up and drop the
4030 		 * event.
4031 		 */
4032 		if (error == EAGAIN) {
4033 			if (zevtchan->zn_failure_count > ZONE_CB_RETRY_COUNT) {
4034 				return (0);
4035 			}
4036 			(void) sleep(zevtchan->zn_failure_count);
4037 		}
4038 		return (error);
4039 	}
4040 
4041 	if (zevtchan->zn_state == ZN_PING_RECEIVED) {
4042 		(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
4043 		return (0);
4044 	}
4045 
4046 	abort();
4047 	return (0);
4048 }
4049 
4050 void
4051 zonecfg_notify_critical_enter(void *h)
4052 {
4053 	struct znotify *zevtchan = h;
4054 
4055 	(void) pthread_mutex_lock(&(zevtchan->zn_bigmutex));
4056 	zevtchan->zn_state = ZN_LOCKED;
4057 }
4058 
4059 int
4060 zonecfg_notify_critical_exit(void * h)
4061 {
4062 
4063 	struct znotify *zevtchan = h;
4064 
4065 	if (zevtchan->zn_state == ZN_UNLOCKED)
4066 		return (0);
4067 
4068 	(void) pthread_mutex_lock(&(zevtchan->zn_mutex));
4069 	zevtchan->zn_state = ZN_PING_INFLIGHT;
4070 
4071 	(void) sysevent_evc_publish(zevtchan->zn_eventchan,
4072 	    ZONE_EVENT_STATUS_CLASS,
4073 	    ZONE_EVENT_PING_SUBCLASS, ZONE_EVENT_PING_PUBLISHER,
4074 	    zevtchan->zn_subscriber_id, NULL, EVCH_SLEEP);
4075 
4076 	while (zevtchan->zn_state != ZN_PING_RECEIVED) {
4077 		(void) pthread_cond_wait(&(zevtchan->zn_cond),
4078 		    &(zevtchan->zn_mutex));
4079 	}
4080 
4081 	if (zevtchan->zn_failed == B_TRUE) {
4082 		zevtchan->zn_state = ZN_LOCKED;
4083 		zevtchan->zn_failed = B_FALSE;
4084 		(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
4085 		return (1);
4086 	}
4087 
4088 	zevtchan->zn_state = ZN_UNLOCKED;
4089 	(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
4090 	(void) pthread_mutex_unlock(&(zevtchan->zn_bigmutex));
4091 	return (0);
4092 }
4093 
4094 void
4095 zonecfg_notify_critical_abort(void *h)
4096 {
4097 	struct znotify *zevtchan = h;
4098 
4099 	zevtchan->zn_state = ZN_UNLOCKED;
4100 	zevtchan->zn_failed = B_FALSE;
4101 	/*
4102 	 * Don't do anything about zn_lock. If it is held, it could only be
4103 	 * held by zn_cb and it will be unlocked soon.
4104 	 */
4105 	(void) pthread_mutex_unlock(&(zevtchan->zn_bigmutex));
4106 }
4107 
4108 void *
4109 zonecfg_notify_bind(int(*func)(const char *zonename, zoneid_t zid,
4110     const char *newstate, const char *oldstate, hrtime_t when, void *p),
4111     void *p)
4112 {
4113 	struct znotify *zevtchan;
4114 	int i = 1;
4115 	int r;
4116 
4117 	zevtchan = malloc(sizeof (struct znotify));
4118 
4119 	if (zevtchan == NULL)
4120 		return (NULL);
4121 
4122 	zevtchan->zn_private = p;
4123 	zevtchan->zn_callback = func;
4124 	zevtchan->zn_state = ZN_UNLOCKED;
4125 	zevtchan->zn_failed = B_FALSE;
4126 
4127 	if (pthread_mutex_init(&(zevtchan->zn_mutex), NULL))
4128 		goto out3;
4129 	if (pthread_cond_init(&(zevtchan->zn_cond), NULL)) {
4130 		(void) pthread_mutex_destroy(&(zevtchan->zn_mutex));
4131 		goto out3;
4132 	}
4133 	if (pthread_mutex_init(&(zevtchan->zn_bigmutex), NULL)) {
4134 		(void) pthread_mutex_destroy(&(zevtchan->zn_mutex));
4135 		(void) pthread_cond_destroy(&(zevtchan->zn_cond));
4136 		goto out3;
4137 	}
4138 
4139 	if (sysevent_evc_bind(ZONE_EVENT_CHANNEL, &(zevtchan->zn_eventchan),
4140 		0) != 0)
4141 		goto out2;
4142 
4143 	do {
4144 		/*
4145 		 * At 4 digits the subscriber ID gets too long and we have
4146 		 * no chance of successfully registering.
4147 		 */
4148 		if (i > 999)
4149 			goto out1;
4150 
4151 		(void) sprintf(zevtchan->zn_subscriber_id, "zone_%li_%i",
4152 		    getpid() % 999999l, i);
4153 
4154 		r = sysevent_evc_subscribe(zevtchan->zn_eventchan,
4155 		    zevtchan->zn_subscriber_id, ZONE_EVENT_STATUS_CLASS, zn_cb,
4156 		    zevtchan, 0);
4157 
4158 		i++;
4159 
4160 	} while (r);
4161 
4162 	return (zevtchan);
4163 out1:
4164 	sysevent_evc_unbind(zevtchan->zn_eventchan);
4165 out2:
4166 	(void) pthread_mutex_destroy(&zevtchan->zn_mutex);
4167 	(void) pthread_cond_destroy(&zevtchan->zn_cond);
4168 	(void) pthread_mutex_destroy(&(zevtchan->zn_bigmutex));
4169 out3:
4170 	free(zevtchan);
4171 
4172 	return (NULL);
4173 }
4174 
4175 void
4176 zonecfg_notify_unbind(void *handle)
4177 {
4178 
4179 	int ret;
4180 
4181 	sysevent_evc_unbind(((struct znotify *)handle)->zn_eventchan);
4182 	/*
4183 	 * Check that all evc threads have gone away. This should be
4184 	 * enforced by sysevent_evc_unbind.
4185 	 */
4186 	ret = pthread_mutex_trylock(&((struct znotify *)handle)->zn_mutex);
4187 
4188 	if (ret)
4189 		abort();
4190 
4191 	(void) pthread_mutex_unlock(&((struct znotify *)handle)->zn_mutex);
4192 	(void) pthread_mutex_destroy(&((struct znotify *)handle)->zn_mutex);
4193 	(void) pthread_cond_destroy(&((struct znotify *)handle)->zn_cond);
4194 	(void) pthread_mutex_destroy(&((struct znotify *)handle)->zn_bigmutex);
4195 
4196 	free(handle);
4197 }
4198 
4199 static int
4200 zonecfg_add_ds_core(zone_dochandle_t handle, struct zone_dstab *tabptr)
4201 {
4202 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
4203 	int err;
4204 
4205 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DATASET, NULL);
4206 	if ((err = newprop(newnode, DTD_ATTR_NAME,
4207 	    tabptr->zone_dataset_name)) != Z_OK)
4208 		return (err);
4209 	return (Z_OK);
4210 }
4211 
4212 int
4213 zonecfg_add_ds(zone_dochandle_t handle, struct zone_dstab *tabptr)
4214 {
4215 	int err;
4216 
4217 	if (tabptr == NULL)
4218 		return (Z_INVAL);
4219 
4220 	if ((err = operation_prep(handle)) != Z_OK)
4221 		return (err);
4222 
4223 	if ((err = zonecfg_add_ds_core(handle, tabptr)) != Z_OK)
4224 		return (err);
4225 
4226 	return (Z_OK);
4227 }
4228 
4229 static int
4230 zonecfg_delete_ds_core(zone_dochandle_t handle, struct zone_dstab *tabptr)
4231 {
4232 	xmlNodePtr cur = handle->zone_dh_cur;
4233 
4234 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
4235 		if (xmlStrcmp(cur->name, DTD_ELEM_DATASET))
4236 			continue;
4237 
4238 		if (match_prop(cur, DTD_ATTR_NAME,
4239 		    tabptr->zone_dataset_name)) {
4240 			xmlUnlinkNode(cur);
4241 			xmlFreeNode(cur);
4242 			return (Z_OK);
4243 		}
4244 	}
4245 	return (Z_NO_RESOURCE_ID);
4246 }
4247 
4248 int
4249 zonecfg_delete_ds(zone_dochandle_t handle, struct zone_dstab *tabptr)
4250 {
4251 	int err;
4252 
4253 	if (tabptr == NULL)
4254 		return (Z_INVAL);
4255 
4256 	if ((err = operation_prep(handle)) != Z_OK)
4257 		return (err);
4258 
4259 	if ((err = zonecfg_delete_ds_core(handle, tabptr)) != Z_OK)
4260 		return (err);
4261 
4262 	return (Z_OK);
4263 }
4264 
4265 int
4266 zonecfg_modify_ds(
4267 	zone_dochandle_t handle,
4268 	struct zone_dstab *oldtabptr,
4269 	struct zone_dstab *newtabptr)
4270 {
4271 	int err;
4272 
4273 	if (oldtabptr == NULL || newtabptr == NULL)
4274 		return (Z_INVAL);
4275 
4276 	if ((err = operation_prep(handle)) != Z_OK)
4277 		return (err);
4278 
4279 	if ((err = zonecfg_delete_ds_core(handle, oldtabptr)) != Z_OK)
4280 		return (err);
4281 
4282 	if ((err = zonecfg_add_ds_core(handle, newtabptr)) != Z_OK)
4283 		return (err);
4284 
4285 	return (Z_OK);
4286 }
4287 
4288 int
4289 zonecfg_lookup_ds(zone_dochandle_t handle, struct zone_dstab *tabptr)
4290 {
4291 	xmlNodePtr cur, firstmatch;
4292 	int err;
4293 	char dataset[MAXNAMELEN];
4294 
4295 	if (tabptr == NULL)
4296 		return (Z_INVAL);
4297 
4298 	if ((err = operation_prep(handle)) != Z_OK)
4299 		return (err);
4300 
4301 	cur = handle->zone_dh_cur;
4302 	firstmatch = NULL;
4303 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
4304 		if (xmlStrcmp(cur->name, DTD_ELEM_DATASET))
4305 			continue;
4306 		if (strlen(tabptr->zone_dataset_name) > 0) {
4307 			if ((fetchprop(cur, DTD_ATTR_NAME, dataset,
4308 			    sizeof (dataset)) == Z_OK) &&
4309 			    (strcmp(tabptr->zone_dataset_name,
4310 			    dataset) == 0)) {
4311 				if (firstmatch == NULL)
4312 					firstmatch = cur;
4313 				else
4314 					return (Z_INSUFFICIENT_SPEC);
4315 			}
4316 		}
4317 	}
4318 	if (firstmatch == NULL)
4319 		return (Z_NO_RESOURCE_ID);
4320 
4321 	cur = firstmatch;
4322 
4323 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_dataset_name,
4324 	    sizeof (tabptr->zone_dataset_name))) != Z_OK)
4325 		return (err);
4326 
4327 	return (Z_OK);
4328 }
4329 
4330 int
4331 zonecfg_setdsent(zone_dochandle_t handle)
4332 {
4333 	return (zonecfg_setent(handle));
4334 }
4335 
4336 int
4337 zonecfg_getdsent(zone_dochandle_t handle, struct zone_dstab *tabptr)
4338 {
4339 	xmlNodePtr cur;
4340 	int err;
4341 
4342 	if (handle == NULL)
4343 		return (Z_INVAL);
4344 
4345 	if ((cur = handle->zone_dh_cur) == NULL)
4346 		return (Z_NO_ENTRY);
4347 
4348 	for (; cur != NULL; cur = cur->next)
4349 		if (!xmlStrcmp(cur->name, DTD_ELEM_DATASET))
4350 			break;
4351 	if (cur == NULL) {
4352 		handle->zone_dh_cur = handle->zone_dh_top;
4353 		return (Z_NO_ENTRY);
4354 	}
4355 
4356 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_dataset_name,
4357 	    sizeof (tabptr->zone_dataset_name))) != Z_OK) {
4358 		handle->zone_dh_cur = handle->zone_dh_top;
4359 		return (err);
4360 	}
4361 
4362 	handle->zone_dh_cur = cur->next;
4363 	return (Z_OK);
4364 }
4365 
4366 int
4367 zonecfg_enddsent(zone_dochandle_t handle)
4368 {
4369 	return (zonecfg_endent(handle));
4370 }
4371 
4372 int
4373 zonecfg_setpkgent(zone_dochandle_t handle)
4374 {
4375 	return (zonecfg_setent(handle));
4376 }
4377 
4378 int
4379 zonecfg_getpkgent(zone_dochandle_t handle, struct zone_pkgtab *tabptr)
4380 {
4381 	xmlNodePtr cur;
4382 	int err;
4383 
4384 	if (handle == NULL)
4385 		return (Z_INVAL);
4386 
4387 	if ((cur = handle->zone_dh_cur) == NULL)
4388 		return (Z_NO_ENTRY);
4389 
4390 	for (; cur != NULL; cur = cur->next)
4391 		if (!xmlStrcmp(cur->name, DTD_ELEM_PACKAGE))
4392 			break;
4393 	if (cur == NULL) {
4394 		handle->zone_dh_cur = handle->zone_dh_top;
4395 		return (Z_NO_ENTRY);
4396 	}
4397 
4398 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_pkg_name,
4399 	    sizeof (tabptr->zone_pkg_name))) != Z_OK) {
4400 		handle->zone_dh_cur = handle->zone_dh_top;
4401 		return (err);
4402 	}
4403 
4404 	if ((err = fetchprop(cur, DTD_ATTR_VERSION, tabptr->zone_pkg_version,
4405 	    sizeof (tabptr->zone_pkg_version))) != Z_OK) {
4406 		handle->zone_dh_cur = handle->zone_dh_top;
4407 		return (err);
4408 	}
4409 
4410 	handle->zone_dh_cur = cur->next;
4411 	return (Z_OK);
4412 }
4413 
4414 int
4415 zonecfg_endpkgent(zone_dochandle_t handle)
4416 {
4417 	return (zonecfg_endent(handle));
4418 }
4419 
4420 int
4421 zonecfg_setpatchent(zone_dochandle_t handle)
4422 {
4423 	return (zonecfg_setent(handle));
4424 }
4425 
4426 int
4427 zonecfg_getpatchent(zone_dochandle_t handle, struct zone_patchtab *tabptr)
4428 {
4429 	xmlNodePtr cur;
4430 	int err;
4431 
4432 	if (handle == NULL)
4433 		return (Z_INVAL);
4434 
4435 	if ((cur = handle->zone_dh_cur) == NULL)
4436 		return (Z_NO_ENTRY);
4437 
4438 	for (; cur != NULL; cur = cur->next)
4439 		if (!xmlStrcmp(cur->name, DTD_ELEM_PATCH))
4440 			break;
4441 	if (cur == NULL) {
4442 		handle->zone_dh_cur = handle->zone_dh_top;
4443 		return (Z_NO_ENTRY);
4444 	}
4445 
4446 	if ((err = fetchprop(cur, DTD_ATTR_ID, tabptr->zone_patch_id,
4447 	    sizeof (tabptr->zone_patch_id))) != Z_OK) {
4448 		handle->zone_dh_cur = handle->zone_dh_top;
4449 		return (err);
4450 	}
4451 
4452 	handle->zone_dh_cur = cur->next;
4453 	return (Z_OK);
4454 }
4455 
4456 int
4457 zonecfg_endpatchent(zone_dochandle_t handle)
4458 {
4459 	return (zonecfg_endent(handle));
4460 }
4461 
4462 int
4463 zonecfg_setdevperment(zone_dochandle_t handle)
4464 {
4465 	return (zonecfg_setent(handle));
4466 }
4467 
4468 int
4469 zonecfg_getdevperment(zone_dochandle_t handle, struct zone_devpermtab *tabptr)
4470 {
4471 	xmlNodePtr cur;
4472 	int err;
4473 	char buf[128];
4474 
4475 	tabptr->zone_devperm_acl = NULL;
4476 
4477 	if (handle == NULL)
4478 		return (Z_INVAL);
4479 
4480 	if ((cur = handle->zone_dh_cur) == NULL)
4481 		return (Z_NO_ENTRY);
4482 
4483 	for (; cur != NULL; cur = cur->next)
4484 		if (!xmlStrcmp(cur->name, DTD_ELEM_DEV_PERM))
4485 			break;
4486 	if (cur == NULL) {
4487 		handle->zone_dh_cur = handle->zone_dh_top;
4488 		return (Z_NO_ENTRY);
4489 	}
4490 
4491 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_devperm_name,
4492 	    sizeof (tabptr->zone_devperm_name))) != Z_OK) {
4493 		handle->zone_dh_cur = handle->zone_dh_top;
4494 		return (err);
4495 	}
4496 
4497 	if ((err = fetchprop(cur, DTD_ATTR_UID, buf, sizeof (buf))) != Z_OK) {
4498 		handle->zone_dh_cur = handle->zone_dh_top;
4499 		return (err);
4500 	}
4501 	tabptr->zone_devperm_uid = (uid_t)atol(buf);
4502 
4503 	if ((err = fetchprop(cur, DTD_ATTR_GID, buf, sizeof (buf))) != Z_OK) {
4504 		handle->zone_dh_cur = handle->zone_dh_top;
4505 		return (err);
4506 	}
4507 	tabptr->zone_devperm_gid = (gid_t)atol(buf);
4508 
4509 	if ((err = fetchprop(cur, DTD_ATTR_MODE, buf, sizeof (buf))) != Z_OK) {
4510 		handle->zone_dh_cur = handle->zone_dh_top;
4511 		return (err);
4512 	}
4513 	tabptr->zone_devperm_mode = (mode_t)strtol(buf, (char **)NULL, 8);
4514 
4515 	if ((err = fetch_alloc_prop(cur, DTD_ATTR_ACL,
4516 	    &(tabptr->zone_devperm_acl))) != Z_OK) {
4517 		handle->zone_dh_cur = handle->zone_dh_top;
4518 		return (err);
4519 	}
4520 
4521 	handle->zone_dh_cur = cur->next;
4522 	return (Z_OK);
4523 }
4524 
4525 int
4526 zonecfg_enddevperment(zone_dochandle_t handle)
4527 {
4528 	return (zonecfg_endent(handle));
4529 }
4530 
4531 /*
4532  * Process a list of pkgs from an entry in the contents file, adding each pkg
4533  * name to the list of pkgs.
4534  *
4535  * It is possible for the pkg name to be preceeded by a special character
4536  * which indicates some bookkeeping information for pkging.  Check if the
4537  * first char is not an Alpha char.  If so, skip over it.
4538  */
4539 static int
4540 add_pkg_list(char *lastp, char ***plist, int *pcnt)
4541 {
4542 	char	*p;
4543 	int	pkg_cnt = *pcnt;
4544 	char	**pkgs = *plist;
4545 	int	res = Z_OK;
4546 
4547 	while ((p = strtok_r(NULL, " ", &lastp)) != NULL) {
4548 		char	**tmpp;
4549 		int	i;
4550 
4551 		/* skip over any special pkg bookkeeping char */
4552 		if (!isalpha(*p))
4553 			p++;
4554 
4555 		/* Check if the pkg is already in the list */
4556 		for (i = 0; i < pkg_cnt; i++) {
4557 			if (strcmp(p, pkgs[i]) == 0)
4558 				break;
4559 		}
4560 
4561 		if (i < pkg_cnt)
4562 			continue;
4563 
4564 		/* The pkg is not in the list; add it. */
4565 		if ((tmpp = (char **)realloc(pkgs,
4566 		    sizeof (char *) * (pkg_cnt + 1))) == NULL) {
4567 			res = Z_NOMEM;
4568 			break;
4569 		}
4570 		pkgs = tmpp;
4571 
4572 		if ((pkgs[pkg_cnt] = strdup(p)) == NULL) {
4573 			res = Z_NOMEM;
4574 			break;
4575 		}
4576 		pkg_cnt++;
4577 	}
4578 
4579 	*plist = pkgs;
4580 	*pcnt = pkg_cnt;
4581 
4582 	return (res);
4583 }
4584 
4585 /*
4586  * Process an entry from the contents file (type "directory") and if the
4587  * directory path is in the list of paths, add the associated list of pkgs
4588  * to the pkg list.  The input parameter "entry" will be broken up by
4589  * the parser within this function so its value will be modified when this
4590  * function exits.
4591  *
4592  * The entries we are looking for will look something like:
4593  *	/usr d none 0755 root sys SUNWctpls SUNWidnl SUNWlibCf ....
4594  */
4595 static int
4596 get_path_pkgs(char *entry, char **paths, int cnt, char ***pkgs, int *pkg_cnt)
4597 {
4598 	char	*f1;
4599 	char	*f2;
4600 	char	*lastp;
4601 	int	i;
4602 	int	res = Z_OK;
4603 
4604 	if ((f1 = strtok_r(entry, " ", &lastp)) == NULL ||
4605 	    (f2 = strtok_r(NULL, " ", &lastp)) == NULL || strcmp(f2, "d") != 0)
4606 		return (Z_OK);
4607 
4608 	/* Check if this directory entry is in the list of paths. */
4609 	for (i = 0; i < cnt; i++) {
4610 		if (fnmatch(paths[i], f1, FNM_PATHNAME) == 0) {
4611 			/*
4612 			 * We do want the pkgs for this path.  First, skip
4613 			 * over the next 4 fields in the entry so that we call
4614 			 * add_pkg_list starting with the pkg names.
4615 			 */
4616 			int j;
4617 
4618 			for (j = 0; j < 4 &&
4619 			    strtok_r(NULL, " ", &lastp) != NULL; j++);
4620 			/*
4621 			 * If there are < 4 fields this entry is corrupt,
4622 			 * just skip it.
4623 			 */
4624 			if (j < 4)
4625 				return (Z_OK);
4626 
4627 			res = add_pkg_list(lastp, pkgs, pkg_cnt);
4628 			break;
4629 		}
4630 	}
4631 
4632 	return (res);
4633 }
4634 
4635 /*
4636  * Read an entry from a pkginfo or contents file.  Some of these lines can
4637  * either be arbitrarily long or be continued by a backslash at the end of
4638  * the line.  This function coalesces lines that are longer than the read
4639  * buffer, and lines that are continued, into one buffer which is returned.
4640  * The caller must free this memory.  NULL is returned when we hit EOF or
4641  * if we run out of memory (errno is set to ENOMEM).
4642  */
4643 static char *
4644 read_pkg_data(FILE *fp)
4645 {
4646 	char *start;
4647 	char *inp;
4648 	char *p;
4649 	int char_cnt = 0;
4650 
4651 	errno = 0;
4652 	if ((start = (char *)malloc(PKGINFO_RD_LEN)) == NULL) {
4653 		errno = ENOMEM;
4654 		return (NULL);
4655 	}
4656 
4657 	inp = start;
4658 	while ((p = fgets(inp, PKGINFO_RD_LEN, fp)) != NULL) {
4659 		int len;
4660 
4661 		len = strlen(inp);
4662 		if (inp[len - 1] == '\n' &&
4663 		    (len == 1 || inp[len - 2] != '\\')) {
4664 			char_cnt = len;
4665 			break;
4666 		}
4667 
4668 		if (inp[len - 2] == '\\')
4669 			char_cnt += len - 2;
4670 		else
4671 			char_cnt += PKGINFO_RD_LEN - 1;
4672 
4673 		if ((p = realloc(start, char_cnt + PKGINFO_RD_LEN)) == NULL) {
4674 			errno = ENOMEM;
4675 			break;
4676 		}
4677 
4678 		start = p;
4679 		inp = start + char_cnt;
4680 	}
4681 
4682 	if (errno == ENOMEM || (p == NULL && char_cnt == 0)) {
4683 		free(start);
4684 		start = NULL;
4685 	}
4686 
4687 	return (start);
4688 }
4689 
4690 static void
4691 free_ipd_pkgs(char **pkgs, int cnt)
4692 {
4693 	int i;
4694 
4695 	for (i = 0; i < cnt; i++)
4696 		free(pkgs[i]);
4697 	free(pkgs);
4698 }
4699 
4700 /*
4701  * Get the list of inherited-pkg-dirs (ipd) for the zone and then get the
4702  * list of pkgs that deliver into those dirs.
4703  */
4704 static int
4705 get_ipd_pkgs(zone_dochandle_t handle, char ***pkg_list, int *cnt)
4706 {
4707 	int	res;
4708 	struct zone_fstab fstab;
4709 	int	ipd_cnt = 0;
4710 	char	**ipds = NULL;
4711 	int	pkg_cnt = 0;
4712 	char	**pkgs = NULL;
4713 	int	i;
4714 
4715 	if ((res = zonecfg_setipdent(handle)) != Z_OK)
4716 		return (res);
4717 
4718 	while (zonecfg_getipdent(handle, &fstab) == Z_OK) {
4719 		char	**p;
4720 		int	len;
4721 
4722 		if ((p = (char **)realloc(ipds,
4723 		    sizeof (char *) * (ipd_cnt + 2))) == NULL) {
4724 			res = Z_NOMEM;
4725 			break;
4726 		}
4727 		ipds = p;
4728 
4729 		if ((ipds[ipd_cnt] = strdup(fstab.zone_fs_dir)) == NULL) {
4730 			res = Z_NOMEM;
4731 			break;
4732 		}
4733 		ipd_cnt++;
4734 
4735 		len = strlen(fstab.zone_fs_dir) + 3;
4736 		if ((ipds[ipd_cnt] = malloc(len)) == NULL) {
4737 			res = Z_NOMEM;
4738 			break;
4739 		}
4740 
4741 		(void) snprintf(ipds[ipd_cnt], len, "%s/*", fstab.zone_fs_dir);
4742 		ipd_cnt++;
4743 	}
4744 
4745 	(void) zonecfg_endipdent(handle);
4746 
4747 	if (res != Z_OK) {
4748 		for (i = 0; i < ipd_cnt; i++)
4749 			free(ipds[i]);
4750 		free(ipds);
4751 		return (res);
4752 	}
4753 
4754 	/* We only have to process the contents file if we have ipds. */
4755 	if (ipd_cnt > 0) {
4756 		FILE	*fp;
4757 
4758 		if ((fp = fopen(CONTENTS_FILE, "r")) != NULL) {
4759 			char	*buf;
4760 
4761 			while ((buf = read_pkg_data(fp)) != NULL) {
4762 				res = get_path_pkgs(buf, ipds, ipd_cnt, &pkgs,
4763 				    &pkg_cnt);
4764 				free(buf);
4765 				if (res != Z_OK)
4766 					break;
4767 			}
4768 
4769 			(void) fclose(fp);
4770 		}
4771 	}
4772 
4773 	for (i = 0; i < ipd_cnt; i++)
4774 		free(ipds[i]);
4775 	free(ipds);
4776 
4777 	if (res != Z_OK) {
4778 		free_ipd_pkgs(pkgs, pkg_cnt);
4779 	} else {
4780 		*pkg_list = pkgs;
4781 		*cnt = pkg_cnt;
4782 	}
4783 
4784 	return (res);
4785 }
4786 
4787 /*
4788  * Return true if pkg_name is in the list of pkgs that deliver into an
4789  * inherited pkg directory for the zone.
4790  */
4791 static boolean_t
4792 dir_pkg(char *pkg_name, char **pkg_list, int cnt)
4793 {
4794 	int i;
4795 
4796 	for (i = 0; i < cnt; i++) {
4797 		if (strcmp(pkg_name, pkg_list[i]) == 0)
4798 			return (B_TRUE);
4799 	}
4800 
4801 	return (B_FALSE);
4802 }
4803 
4804 /*
4805  * Start by adding the patch to the sw inventory on the handle.
4806  *
4807  * The info parameter will be the portion of the PATCH_INFO_ entry following
4808  * the '='.  For example:
4809  * Installed: Wed Dec  7 07:13:51 PST 2005 From: mum Obsoletes: 120777-03 \
4810  *	121087-02 119108-07 Requires: 119575-02 119255-06 Incompatibles:
4811  *
4812  * We also want to add the Obsolete and Incompatible patches to the
4813  * sw inventory description of this patch.
4814  */
4815 static int
4816 add_patch(zone_dochandle_t handle, char *patch, char *info)
4817 {
4818 	xmlNodePtr	node;
4819 	xmlNodePtr	cur;
4820 	int		err;
4821 	char		*p;
4822 	char		*lastp;
4823 	boolean_t	add_info = B_FALSE;
4824 	boolean_t	obsolete;
4825 
4826 	if ((err = operation_prep(handle)) != Z_OK)
4827 		return (err);
4828 
4829 	cur = handle->zone_dh_cur;
4830 	node = xmlNewTextChild(cur, NULL, DTD_ELEM_PATCH, NULL);
4831 	if ((err = newprop(node, DTD_ATTR_ID, patch)) != Z_OK)
4832 		return (err);
4833 
4834 	/*
4835 	 * Start with the first token.  This will probably be "Installed:".
4836 	 * If we can't tokenize this entry, just return.
4837 	 */
4838 	if ((p = strtok_r(info, " ", &lastp)) == NULL)
4839 		return (Z_OK);
4840 
4841 	do {
4842 		xmlNodePtr new_node;
4843 		char	*nlp;
4844 
4845 		if (strcmp(p, "Installed:") == 0 ||
4846 		    strcmp(p, "Requires:") == 0 ||
4847 		    strcmp(p, "From:") == 0) {
4848 			add_info = B_FALSE;
4849 			continue;
4850 		} else if (strcmp(p, "Obsoletes:") == 0) {
4851 			obsolete = B_TRUE;
4852 			add_info = B_TRUE;
4853 			continue;
4854 		} else if (strcmp(p, "Incompatibles:") == 0) {
4855 			obsolete = B_FALSE;
4856 			add_info = B_TRUE;
4857 			continue;
4858 		}
4859 
4860 		if (!add_info)
4861 			continue;
4862 
4863 		/* strip newline from last patch in the line */
4864 		nlp = (p + strlen(p) - 1);
4865 		if (*nlp == '\n')
4866 			*nlp = '\0';
4867 
4868 		if (obsolete)
4869 			new_node = xmlNewTextChild(node, NULL,
4870 			    DTD_ELEM_OBSOLETES, NULL);
4871 		else
4872 			new_node = xmlNewTextChild(node, NULL,
4873 			    DTD_ELEM_INCOMPATIBLE, NULL);
4874 
4875 		if ((err = newprop(new_node, DTD_ATTR_ID, p)) != Z_OK)
4876 			return (err);
4877 
4878 	} while ((p = strtok_r(NULL, " ", &lastp)) != NULL);
4879 
4880 	return (Z_OK);
4881 }
4882 
4883 static boolean_t
4884 unique_patch(zone_dochandle_t handle, char *patch)
4885 {
4886 	xmlNodePtr	cur;
4887 	char		id[MAXNAMELEN];
4888 
4889 	cur = xmlDocGetRootElement(handle->zone_dh_doc);
4890 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
4891 		if (xmlStrcmp(cur->name, DTD_ELEM_PATCH) == 0) {
4892 			if (fetchprop(cur, DTD_ATTR_ID, id, sizeof (id))
4893 			    != Z_OK)
4894 				continue;
4895 
4896 			if (strcmp(patch, id) == 0)
4897 				return (B_FALSE);
4898 		}
4899 	}
4900 
4901 	return (B_TRUE);
4902 }
4903 
4904 /*
4905  * Add the unique patches associated with this pkg to the sw inventory on the
4906  * handle.
4907  *
4908  * We are processing entries of the form:
4909  * PATCH_INFO_121454-02=Installed: Wed Dec  7 07:13:51 PST 2005 From: mum \
4910  *	Obsoletes: 120777-03 121087-02 119108-07 Requires: 119575-02 \
4911  *	119255-06 Incompatibles:
4912  *
4913  */
4914 static int
4915 add_patches(zone_dochandle_t handle, struct zone_pkginfo *infop)
4916 {
4917 	int i;
4918 	int res = Z_OK;
4919 
4920 	for (i = 0; i < infop->zpi_patch_cnt; i++) {
4921 		char *p, *ep;
4922 
4923 		if (strlen(infop->zpi_patchinfo[i]) < (sizeof (PATCHINFO) - 1))
4924 			continue;
4925 
4926 		/* Skip over "PATCH_INFO_" to get the patch id. */
4927 		p = infop->zpi_patchinfo[i] + sizeof (PATCHINFO) - 1;
4928 		if ((ep = strchr(p, '=')) == NULL)
4929 			continue;
4930 
4931 		*ep = '\0';
4932 		if (unique_patch(handle, p))
4933 			res = add_patch(handle, p, ep + 1);
4934 	}
4935 
4936 	return (res);
4937 }
4938 
4939 /*
4940  * Add the pkg to the sw inventory on the handle.
4941  */
4942 static int
4943 add_pkg(zone_dochandle_t handle, char *name, char *version)
4944 {
4945 	xmlNodePtr newnode;
4946 	xmlNodePtr cur;
4947 	int err;
4948 
4949 	if ((err = operation_prep(handle)) != Z_OK)
4950 		return (err);
4951 
4952 	cur = handle->zone_dh_cur;
4953 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_PACKAGE, NULL);
4954 	if ((err = newprop(newnode, DTD_ATTR_NAME, name)) != Z_OK)
4955 		return (err);
4956 	if ((err = newprop(newnode, DTD_ATTR_VERSION, version)) != Z_OK)
4957 		return (err);
4958 	return (Z_OK);
4959 }
4960 
4961 static void
4962 free_pkginfo(struct zone_pkginfo *infop)
4963 {
4964 	free(infop->zpi_version);
4965 	if (infop->zpi_patch_cnt > 0) {
4966 		int i;
4967 
4968 		for (i = 0; i < infop->zpi_patch_cnt; i++)
4969 			free(infop->zpi_patchinfo[i]);
4970 		free(infop->zpi_patchinfo);
4971 	}
4972 }
4973 
4974 /*
4975  * Read the pkginfo file and populate the structure with the data we need
4976  * from this pkg for a sw inventory.
4977  */
4978 static int
4979 get_pkginfo(char *pkginfo, struct zone_pkginfo *infop)
4980 {
4981 	FILE	*fp;
4982 	char	*buf;
4983 	int	err = 0;
4984 
4985 	infop->zpi_all_zones = B_FALSE;
4986 	infop->zpi_this_zone = B_FALSE;
4987 	infop->zpi_version = NULL;
4988 	infop->zpi_patch_cnt = 0;
4989 	infop->zpi_patchinfo = NULL;
4990 
4991 	if ((fp = fopen(pkginfo, "r")) == NULL)
4992 		return (errno);
4993 
4994 	while ((buf = read_pkg_data(fp)) != NULL) {
4995 		if (strncmp(buf, VERSION, sizeof (VERSION) - 1) == 0) {
4996 			int len;
4997 
4998 			if ((infop->zpi_version =
4999 			    strdup(buf + sizeof (VERSION) - 1)) == NULL) {
5000 				err = ENOMEM;
5001 				break;
5002 			}
5003 
5004 			/* remove trailing newline */
5005 			len = strlen(infop->zpi_version);
5006 			*(infop->zpi_version + len - 1) = 0;
5007 
5008 		} else if (strcmp(buf, ALL_ZONES) == 0) {
5009 			infop->zpi_all_zones = B_TRUE;
5010 
5011 		} else if (strcmp(buf, THIS_ZONE) == 0) {
5012 			infop->zpi_this_zone = B_TRUE;
5013 
5014 		} else if (strncmp(buf, PATCHINFO, sizeof (PATCHINFO) - 1)
5015 		    == 0) {
5016 			char **p;
5017 
5018 			if ((p = (char **)realloc(infop->zpi_patchinfo,
5019 			    sizeof (char *) * (infop->zpi_patch_cnt + 1)))
5020 			    == NULL) {
5021 				err = ENOMEM;
5022 				break;
5023 			}
5024 			infop->zpi_patchinfo = p;
5025 
5026 			if ((infop->zpi_patchinfo[infop->zpi_patch_cnt] =
5027 			    strdup(buf)) == NULL) {
5028 				err = ENOMEM;
5029 				break;
5030 			}
5031 			infop->zpi_patch_cnt++;
5032 		}
5033 
5034 		free(buf);
5035 	}
5036 
5037 	free(buf);
5038 
5039 	if (errno == ENOMEM) {
5040 		err = ENOMEM;
5041 		/* Clean up anything we did manage to allocate. */
5042 		free_pkginfo(infop);
5043 	}
5044 
5045 	(void) fclose(fp);
5046 
5047 	return (err);
5048 }
5049 
5050 /*
5051  * Take a software inventory of the global zone.  We need to get the set of
5052  * packages and patches that are on the global zone that the specified
5053  * non-global zone depends on.  The packages we need in the inventory are:
5054  *
5055  * - skip the package if SUNW_PKG_THISZONE is 'true'
5056  * otherwise,
5057  * - add the package if
5058  * a) SUNW_PKG_ALLZONES is 'true',
5059  * or
5060  * b) any file delivered by the package is in a file system that is inherited
5061  * from the global zone.
5062  * If the zone does not inherit any file systems (whole root)
5063  * then (b) will be skipped.
5064  *
5065  * For each of the packages that is being added to the inventory, we will also
5066  * add all of the associated, unique patches to the inventory.
5067  */
5068 static int
5069 zonecfg_sw_inventory(zone_dochandle_t handle)
5070 {
5071 	char		pkginfo[MAXPATHLEN];
5072 	int		res;
5073 	struct dirent	*dp;
5074 	DIR		*dirp;
5075 	struct stat	buf;
5076 	struct zone_pkginfo	info;
5077 	int		pkg_cnt = 0;
5078 	char		**pkgs = NULL;
5079 
5080 	if ((res = get_ipd_pkgs(handle, &pkgs, &pkg_cnt)) != Z_OK)
5081 		return (res);
5082 
5083 	if ((dirp = opendir(PKG_PATH)) == NULL) {
5084 		free_ipd_pkgs(pkgs, pkg_cnt);
5085 		return (Z_OK);
5086 	}
5087 
5088 	while ((dp = readdir(dirp)) != (struct dirent *)0) {
5089 		if (strcmp(dp->d_name, ".") == 0 ||
5090 		    strcmp(dp->d_name, "..") == 0)
5091 			continue;
5092 
5093 		(void) snprintf(pkginfo, sizeof (pkginfo), "%s/%s/pkginfo",
5094 		    PKG_PATH, dp->d_name);
5095 
5096 		if (stat(pkginfo, &buf) == -1 || !S_ISREG(buf.st_mode))
5097 			continue;
5098 
5099 		if (get_pkginfo(pkginfo, &info) != 0) {
5100 			res = Z_NOMEM;
5101 			break;
5102 		}
5103 
5104 		if (!info.zpi_this_zone &&
5105 		    (info.zpi_all_zones ||
5106 		    dir_pkg(dp->d_name, pkgs, pkg_cnt))) {
5107 			if ((res = add_pkg(handle, dp->d_name,
5108 			    info.zpi_version)) == Z_OK) {
5109 				if (info.zpi_patch_cnt > 0)
5110 					res = add_patches(handle, &info);
5111 			}
5112 		}
5113 
5114 		free_pkginfo(&info);
5115 
5116 		if (res != Z_OK)
5117 			break;
5118 	}
5119 
5120 	(void) closedir(dirp);
5121 
5122 	free_ipd_pkgs(pkgs, pkg_cnt);
5123 
5124 	if (res == Z_OK)
5125 		handle->zone_dh_sw_inv = B_TRUE;
5126 
5127 	return (res);
5128 }
5129 
5130 /*
5131  * zonecfg_devwalk call-back function used during detach to generate the
5132  * dev info in the manifest.
5133  */
5134 static int
5135 get_detach_dev_entry(const char *name, uid_t uid, gid_t gid, mode_t mode,
5136     const char *acl, void *hdl)
5137 {
5138 	zone_dochandle_t handle = (zone_dochandle_t)hdl;
5139 	xmlNodePtr newnode;
5140 	xmlNodePtr cur;
5141 	int err;
5142 	char buf[128];
5143 
5144 	if ((err = operation_prep(handle)) != Z_OK)
5145 		return (err);
5146 
5147 	cur = handle->zone_dh_cur;
5148 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DEV_PERM, NULL);
5149 	if ((err = newprop(newnode, DTD_ATTR_NAME, (char *)name)) != Z_OK)
5150 		return (err);
5151 	(void) snprintf(buf, sizeof (buf), "%lu", uid);
5152 	if ((err = newprop(newnode, DTD_ATTR_UID, buf)) != Z_OK)
5153 		return (err);
5154 	(void) snprintf(buf, sizeof (buf), "%lu", gid);
5155 	if ((err = newprop(newnode, DTD_ATTR_GID, buf)) != Z_OK)
5156 		return (err);
5157 	(void) snprintf(buf, sizeof (buf), "%o", mode);
5158 	if ((err = newprop(newnode, DTD_ATTR_MODE, buf)) != Z_OK)
5159 		return (err);
5160 	if ((err = newprop(newnode, DTD_ATTR_ACL, (char *)acl)) != Z_OK)
5161 		return (err);
5162 	return (Z_OK);
5163 }
5164 
5165 /*
5166  * Get the information required to support detaching a zone.  This is
5167  * called on the source system when detaching (the detaching parameter should
5168  * be set to true) and on the destination system before attaching (the
5169  * detaching parameter should be false).
5170  *
5171  * For native Solaris zones, the detach/attach process involves validating
5172  * that the software on the global zone can support the zone when we attach.
5173  * To do this we take a software inventory of the global zone.  We also
5174  * have to keep track of the device configuration so that we can properly
5175  * recreate it on the destination.
5176  */
5177 int
5178 zonecfg_get_detach_info(zone_dochandle_t handle, boolean_t detaching)
5179 {
5180 	int		res;
5181 
5182 	if ((res = zonecfg_sw_inventory(handle)) != Z_OK)
5183 		return (res);
5184 
5185 	if (detaching)
5186 		res = zonecfg_devwalk(handle, get_detach_dev_entry, handle);
5187 
5188 	return (res);
5189 }
5190