xref: /titanic_44/usr/src/lib/libzonecfg/common/libzonecfg.c (revision 9df12a23948bd40cbe37ce88d84e272c3894e675)
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 zfsl;
2291 	int rv = 0;
2292 	char zfs_path[MAXPATHLEN];
2293 
2294 	assert(rootpath != NULL);
2295 
2296 	if ((zfsl = snprintf(zfs_path, sizeof (zfs_path), "%s/.zfs/", rootpath))
2297 	    >= sizeof (zfs_path))
2298 		return (-1);
2299 
2300 	l = strlen(rootpath);
2301 
2302 	mnttab = fopen("/etc/mnttab", "r");
2303 
2304 	if (mnttab == NULL)
2305 		return (-1);
2306 
2307 	if (ioctl(fileno(mnttab), MNTIOC_SHOWHIDDEN, NULL) < 0)  {
2308 		rv = -1;
2309 		goto out;
2310 	}
2311 
2312 	while (!getmntent(mnttab, &m)) {
2313 		if ((strncmp(rootpath, m.mnt_mountp, l) == 0) &&
2314 		    (m.mnt_mountp[l] == '/') &&
2315 		    (strncmp(zfs_path, m.mnt_mountp, zfsl) != 0)) {
2316 			rv++;
2317 			if (callback == NULL)
2318 				continue;
2319 			if (callback(m.mnt_mountp, priv)) {
2320 				rv = -1;
2321 				goto out;
2322 
2323 			}
2324 		}
2325 	}
2326 
2327 out:
2328 	(void) fclose(mnttab);
2329 	return (rv);
2330 }
2331 
2332 /*
2333  * This routine is used to determine if a given device should appear in the
2334  * zone represented by 'handle'.  First it consults the list of "standard"
2335  * zone devices.  Then it scans the user-supplied device entries.
2336  */
2337 int
2338 zonecfg_match_dev(zone_dochandle_t handle, char *devpath,
2339     struct zone_devtab *out_match)
2340 {
2341 	int err;
2342 	boolean_t found = B_FALSE;
2343 	char match[MAXPATHLEN];
2344 	const char **stdmatch;
2345 	xmlNodePtr cur;
2346 
2347 	if (handle == NULL || devpath == NULL)
2348 		return (Z_INVAL);
2349 
2350 	/*
2351 	 * Check the "standard" devices which we require to be present.
2352 	 */
2353 	for (stdmatch = &standard_devs[0]; *stdmatch != NULL; stdmatch++) {
2354 		/*
2355 		 * fnmatch gives us simple but powerful shell-style matching.
2356 		 */
2357 		if (fnmatch(*stdmatch, devpath, FNM_PATHNAME) == 0) {
2358 			if (!out_match)
2359 				return (Z_OK);
2360 			(void) snprintf(out_match->zone_dev_match,
2361 			    sizeof (out_match->zone_dev_match),
2362 			    "/dev/%s", *stdmatch);
2363 			return (Z_OK);
2364 		}
2365 	}
2366 
2367 	/*
2368 	 * We got no hits in the set of standard devices.  On to the user
2369 	 * supplied ones.
2370 	 */
2371 	if ((err = operation_prep(handle)) != Z_OK) {
2372 		handle->zone_dh_cur = NULL;
2373 		return (err);
2374 	}
2375 
2376 	cur = handle->zone_dh_cur;
2377 	cur = cur->xmlChildrenNode;
2378 	if (cur == NULL)
2379 		return (Z_NO_ENTRY);
2380 	handle->zone_dh_cur = cur;
2381 
2382 	for (; cur != NULL; cur = cur->next) {
2383 		char *m;
2384 		if (xmlStrcmp(cur->name, DTD_ELEM_DEVICE) != 0)
2385 			continue;
2386 		if ((err = fetchprop(cur, DTD_ATTR_MATCH, match,
2387 		    sizeof (match))) != Z_OK) {
2388 			handle->zone_dh_cur = handle->zone_dh_top;
2389 			return (err);
2390 		}
2391 		m = match;
2392 		/*
2393 		 * fnmatch gives us simple but powerful shell-style matching;
2394 		 * but first, we need to strip out /dev/ from the matching rule.
2395 		 */
2396 		if (strncmp(m, "/dev/", 5) == 0)
2397 			m += 5;
2398 
2399 		if (fnmatch(m, devpath, FNM_PATHNAME) == 0) {
2400 			found = B_TRUE;
2401 			break;
2402 		}
2403 	}
2404 
2405 	if (!found)
2406 		return (Z_NO_ENTRY);
2407 
2408 	if (!out_match)
2409 		return (Z_OK);
2410 
2411 	(void) strlcpy(out_match->zone_dev_match, match,
2412 	    sizeof (out_match->zone_dev_match));
2413 	return (Z_OK);
2414 }
2415 
2416 int
2417 zonecfg_lookup_attr(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2418 {
2419 	xmlNodePtr cur, firstmatch;
2420 	int err;
2421 	char name[MAXNAMELEN], type[MAXNAMELEN], value[MAXNAMELEN];
2422 
2423 	if (tabptr == NULL)
2424 		return (Z_INVAL);
2425 
2426 	if ((err = operation_prep(handle)) != Z_OK)
2427 		return (err);
2428 
2429 	cur = handle->zone_dh_cur;
2430 	firstmatch = NULL;
2431 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2432 		if (xmlStrcmp(cur->name, DTD_ELEM_ATTR))
2433 			continue;
2434 		if (strlen(tabptr->zone_attr_name) > 0) {
2435 			if ((fetchprop(cur, DTD_ATTR_NAME, name,
2436 			    sizeof (name)) == Z_OK) &&
2437 			    (strcmp(tabptr->zone_attr_name, name) == 0)) {
2438 				if (firstmatch == NULL)
2439 					firstmatch = cur;
2440 				else
2441 					return (Z_INSUFFICIENT_SPEC);
2442 			}
2443 		}
2444 		if (strlen(tabptr->zone_attr_type) > 0) {
2445 			if ((fetchprop(cur, DTD_ATTR_TYPE, type,
2446 			    sizeof (type)) == Z_OK)) {
2447 				if (strcmp(tabptr->zone_attr_type, type) == 0) {
2448 					if (firstmatch == NULL)
2449 						firstmatch = cur;
2450 					else if (firstmatch != cur)
2451 						return (Z_INSUFFICIENT_SPEC);
2452 				} else {
2453 					/*
2454 					 * If another property matched but this
2455 					 * one doesn't then reset firstmatch.
2456 					 */
2457 					if (firstmatch == cur)
2458 						firstmatch = NULL;
2459 				}
2460 			}
2461 		}
2462 		if (strlen(tabptr->zone_attr_value) > 0) {
2463 			if ((fetchprop(cur, DTD_ATTR_VALUE, value,
2464 			    sizeof (value)) == Z_OK)) {
2465 				if (strcmp(tabptr->zone_attr_value, value) ==
2466 				    0) {
2467 					if (firstmatch == NULL)
2468 						firstmatch = cur;
2469 					else if (firstmatch != cur)
2470 						return (Z_INSUFFICIENT_SPEC);
2471 				} else {
2472 					/*
2473 					 * If another property matched but this
2474 					 * one doesn't then reset firstmatch.
2475 					 */
2476 					if (firstmatch == cur)
2477 						firstmatch = NULL;
2478 				}
2479 			}
2480 		}
2481 	}
2482 	if (firstmatch == NULL)
2483 		return (Z_NO_RESOURCE_ID);
2484 
2485 	cur = firstmatch;
2486 
2487 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_attr_name,
2488 	    sizeof (tabptr->zone_attr_name))) != Z_OK)
2489 		return (err);
2490 
2491 	if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_attr_type,
2492 	    sizeof (tabptr->zone_attr_type))) != Z_OK)
2493 		return (err);
2494 
2495 	if ((err = fetchprop(cur, DTD_ATTR_VALUE, tabptr->zone_attr_value,
2496 	    sizeof (tabptr->zone_attr_value))) != Z_OK)
2497 		return (err);
2498 
2499 	return (Z_OK);
2500 }
2501 
2502 static int
2503 zonecfg_add_attr_core(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2504 {
2505 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
2506 	int err;
2507 
2508 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_ATTR, NULL);
2509 	err = newprop(newnode, DTD_ATTR_NAME, tabptr->zone_attr_name);
2510 	if (err != Z_OK)
2511 		return (err);
2512 	err = newprop(newnode, DTD_ATTR_TYPE, tabptr->zone_attr_type);
2513 	if (err != Z_OK)
2514 		return (err);
2515 	err = newprop(newnode, DTD_ATTR_VALUE, tabptr->zone_attr_value);
2516 	if (err != Z_OK)
2517 		return (err);
2518 	return (Z_OK);
2519 }
2520 
2521 int
2522 zonecfg_add_attr(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2523 {
2524 	int err;
2525 
2526 	if (tabptr == NULL)
2527 		return (Z_INVAL);
2528 
2529 	if ((err = operation_prep(handle)) != Z_OK)
2530 		return (err);
2531 
2532 	if ((err = zonecfg_add_attr_core(handle, tabptr)) != Z_OK)
2533 		return (err);
2534 
2535 	return (Z_OK);
2536 }
2537 
2538 static int
2539 zonecfg_delete_attr_core(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2540 {
2541 	xmlNodePtr cur = handle->zone_dh_cur;
2542 	int name_match, type_match, value_match;
2543 
2544 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2545 		if (xmlStrcmp(cur->name, DTD_ELEM_ATTR))
2546 			continue;
2547 
2548 		name_match = match_prop(cur, DTD_ATTR_NAME,
2549 		    tabptr->zone_attr_name);
2550 		type_match = match_prop(cur, DTD_ATTR_TYPE,
2551 		    tabptr->zone_attr_type);
2552 		value_match = match_prop(cur, DTD_ATTR_VALUE,
2553 		    tabptr->zone_attr_value);
2554 
2555 		if (name_match && type_match && value_match) {
2556 			xmlUnlinkNode(cur);
2557 			xmlFreeNode(cur);
2558 			return (Z_OK);
2559 		}
2560 	}
2561 	return (Z_NO_RESOURCE_ID);
2562 }
2563 
2564 int
2565 zonecfg_delete_attr(zone_dochandle_t handle, struct zone_attrtab *tabptr)
2566 {
2567 	int err;
2568 
2569 	if (tabptr == NULL)
2570 		return (Z_INVAL);
2571 
2572 	if ((err = operation_prep(handle)) != Z_OK)
2573 		return (err);
2574 
2575 	if ((err = zonecfg_delete_attr_core(handle, tabptr)) != Z_OK)
2576 		return (err);
2577 
2578 	return (Z_OK);
2579 }
2580 
2581 int
2582 zonecfg_modify_attr(
2583 	zone_dochandle_t handle,
2584 	struct zone_attrtab *oldtabptr,
2585 	struct zone_attrtab *newtabptr)
2586 {
2587 	int err;
2588 
2589 	if (oldtabptr == NULL || newtabptr == NULL)
2590 		return (Z_INVAL);
2591 
2592 	if ((err = operation_prep(handle)) != Z_OK)
2593 		return (err);
2594 
2595 	if ((err = zonecfg_delete_attr_core(handle, oldtabptr)) != Z_OK)
2596 		return (err);
2597 
2598 	if ((err = zonecfg_add_attr_core(handle, newtabptr)) != Z_OK)
2599 		return (err);
2600 
2601 	return (Z_OK);
2602 }
2603 
2604 int
2605 zonecfg_get_attr_boolean(const struct zone_attrtab *attr, boolean_t *value)
2606 {
2607 	if (attr == NULL)
2608 		return (Z_INVAL);
2609 
2610 	if (strcmp(attr->zone_attr_type, DTD_ENTITY_BOOLEAN) != 0)
2611 		return (Z_INVAL);
2612 
2613 	if (strcmp(attr->zone_attr_value, DTD_ENTITY_TRUE) == 0) {
2614 		*value = B_TRUE;
2615 		return (Z_OK);
2616 	}
2617 	if (strcmp(attr->zone_attr_value, DTD_ENTITY_FALSE) == 0) {
2618 		*value = B_FALSE;
2619 		return (Z_OK);
2620 	}
2621 	return (Z_INVAL);
2622 }
2623 
2624 int
2625 zonecfg_get_attr_int(const struct zone_attrtab *attr, int64_t *value)
2626 {
2627 	long long result;
2628 	char *endptr;
2629 
2630 	if (attr == NULL)
2631 		return (Z_INVAL);
2632 
2633 	if (strcmp(attr->zone_attr_type, DTD_ENTITY_INT) != 0)
2634 		return (Z_INVAL);
2635 
2636 	errno = 0;
2637 	result = strtoll(attr->zone_attr_value, &endptr, 10);
2638 	if (errno != 0 || *endptr != '\0')
2639 		return (Z_INVAL);
2640 	*value = result;
2641 	return (Z_OK);
2642 }
2643 
2644 int
2645 zonecfg_get_attr_string(const struct zone_attrtab *attr, char *value,
2646     size_t val_sz)
2647 {
2648 	if (attr == NULL)
2649 		return (Z_INVAL);
2650 
2651 	if (strcmp(attr->zone_attr_type, DTD_ENTITY_STRING) != 0)
2652 		return (Z_INVAL);
2653 
2654 	if (strlcpy(value, attr->zone_attr_value, val_sz) >= val_sz)
2655 		return (Z_TOO_BIG);
2656 	return (Z_OK);
2657 }
2658 
2659 int
2660 zonecfg_get_attr_uint(const struct zone_attrtab *attr, uint64_t *value)
2661 {
2662 	unsigned long long result;
2663 	long long neg_result;
2664 	char *endptr;
2665 
2666 	if (attr == NULL)
2667 		return (Z_INVAL);
2668 
2669 	if (strcmp(attr->zone_attr_type, DTD_ENTITY_UINT) != 0)
2670 		return (Z_INVAL);
2671 
2672 	errno = 0;
2673 	result = strtoull(attr->zone_attr_value, &endptr, 10);
2674 	if (errno != 0 || *endptr != '\0')
2675 		return (Z_INVAL);
2676 	errno = 0;
2677 	neg_result = strtoll(attr->zone_attr_value, &endptr, 10);
2678 	/*
2679 	 * Incredibly, strtoull("<negative number>", ...) will not fail but
2680 	 * return whatever (negative) number cast as a u_longlong_t, so we
2681 	 * need to look for this here.
2682 	 */
2683 	if (errno == 0 && neg_result < 0)
2684 		return (Z_INVAL);
2685 	*value = result;
2686 	return (Z_OK);
2687 }
2688 
2689 int
2690 zonecfg_lookup_rctl(zone_dochandle_t handle, struct zone_rctltab *tabptr)
2691 {
2692 	xmlNodePtr cur, val;
2693 	char savedname[MAXNAMELEN];
2694 	struct zone_rctlvaltab *valptr;
2695 	int err;
2696 
2697 	if (tabptr->zone_rctl_name == NULL ||
2698 	    strlen(tabptr->zone_rctl_name) == 0)
2699 		return (Z_INVAL);
2700 
2701 	if ((err = operation_prep(handle)) != Z_OK)
2702 		return (err);
2703 
2704 	cur = handle->zone_dh_cur;
2705 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2706 		if (xmlStrcmp(cur->name, DTD_ELEM_RCTL))
2707 			continue;
2708 		if ((fetchprop(cur, DTD_ATTR_NAME, savedname,
2709 		    sizeof (savedname)) == Z_OK) &&
2710 		    (strcmp(savedname, tabptr->zone_rctl_name) == 0)) {
2711 			tabptr->zone_rctl_valptr = NULL;
2712 			for (val = cur->xmlChildrenNode; val != NULL;
2713 			    val = val->next) {
2714 				valptr = (struct zone_rctlvaltab *)malloc(
2715 				    sizeof (struct zone_rctlvaltab));
2716 				if (valptr == NULL)
2717 					return (Z_NOMEM);
2718 				if ((fetchprop(val, DTD_ATTR_PRIV,
2719 				    valptr->zone_rctlval_priv,
2720 				    sizeof (valptr->zone_rctlval_priv)) !=
2721 				    Z_OK))
2722 					break;
2723 				if ((fetchprop(val, DTD_ATTR_LIMIT,
2724 				    valptr->zone_rctlval_limit,
2725 				    sizeof (valptr->zone_rctlval_limit)) !=
2726 				    Z_OK))
2727 					break;
2728 				if ((fetchprop(val, DTD_ATTR_ACTION,
2729 				    valptr->zone_rctlval_action,
2730 				    sizeof (valptr->zone_rctlval_action)) !=
2731 				    Z_OK))
2732 					break;
2733 				if (zonecfg_add_rctl_value(tabptr, valptr) !=
2734 				    Z_OK)
2735 					break;
2736 			}
2737 			return (Z_OK);
2738 		}
2739 	}
2740 	return (Z_NO_RESOURCE_ID);
2741 }
2742 
2743 static int
2744 zonecfg_add_rctl_core(zone_dochandle_t handle, struct zone_rctltab *tabptr)
2745 {
2746 	xmlNodePtr newnode, cur = handle->zone_dh_cur, valnode;
2747 	struct zone_rctlvaltab *valptr;
2748 	int err;
2749 
2750 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_RCTL, NULL);
2751 	err = newprop(newnode, DTD_ATTR_NAME, tabptr->zone_rctl_name);
2752 	if (err != Z_OK)
2753 		return (err);
2754 	for (valptr = tabptr->zone_rctl_valptr; valptr != NULL;
2755 	    valptr = valptr->zone_rctlval_next) {
2756 		valnode = xmlNewTextChild(newnode, NULL,
2757 		    DTD_ELEM_RCTLVALUE, NULL);
2758 		err = newprop(valnode, DTD_ATTR_PRIV,
2759 		    valptr->zone_rctlval_priv);
2760 		if (err != Z_OK)
2761 			return (err);
2762 		err = newprop(valnode, DTD_ATTR_LIMIT,
2763 		    valptr->zone_rctlval_limit);
2764 		if (err != Z_OK)
2765 			return (err);
2766 		err = newprop(valnode, DTD_ATTR_ACTION,
2767 		    valptr->zone_rctlval_action);
2768 		if (err != Z_OK)
2769 			return (err);
2770 	}
2771 	return (Z_OK);
2772 }
2773 
2774 int
2775 zonecfg_add_rctl(zone_dochandle_t handle, struct zone_rctltab *tabptr)
2776 {
2777 	int err;
2778 
2779 	if (tabptr == NULL || tabptr->zone_rctl_name == NULL)
2780 		return (Z_INVAL);
2781 
2782 	if ((err = operation_prep(handle)) != Z_OK)
2783 		return (err);
2784 
2785 	if ((err = zonecfg_add_rctl_core(handle, tabptr)) != Z_OK)
2786 		return (err);
2787 
2788 	return (Z_OK);
2789 }
2790 
2791 static int
2792 zonecfg_delete_rctl_core(zone_dochandle_t handle, struct zone_rctltab *tabptr)
2793 {
2794 	xmlNodePtr cur = handle->zone_dh_cur;
2795 	xmlChar *savedname;
2796 	int name_result;
2797 
2798 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
2799 		if (xmlStrcmp(cur->name, DTD_ELEM_RCTL))
2800 			continue;
2801 
2802 		savedname = xmlGetProp(cur, DTD_ATTR_NAME);
2803 		if (savedname == NULL)	/* shouldn't happen */
2804 			continue;
2805 		name_result = xmlStrcmp(savedname,
2806 		    (const xmlChar *) tabptr->zone_rctl_name);
2807 		xmlFree(savedname);
2808 
2809 		if (name_result == 0) {
2810 			xmlUnlinkNode(cur);
2811 			xmlFreeNode(cur);
2812 			return (Z_OK);
2813 		}
2814 	}
2815 	return (Z_NO_RESOURCE_ID);
2816 }
2817 
2818 int
2819 zonecfg_delete_rctl(zone_dochandle_t handle, struct zone_rctltab *tabptr)
2820 {
2821 	int err;
2822 
2823 	if (tabptr == NULL || tabptr->zone_rctl_name == NULL)
2824 		return (Z_INVAL);
2825 
2826 	if ((err = operation_prep(handle)) != Z_OK)
2827 		return (err);
2828 
2829 	if ((err = zonecfg_delete_rctl_core(handle, tabptr)) != Z_OK)
2830 		return (err);
2831 
2832 	return (Z_OK);
2833 }
2834 
2835 int
2836 zonecfg_modify_rctl(
2837 	zone_dochandle_t handle,
2838 	struct zone_rctltab *oldtabptr,
2839 	struct zone_rctltab *newtabptr)
2840 {
2841 	int err;
2842 
2843 	if (oldtabptr == NULL || oldtabptr->zone_rctl_name == NULL ||
2844 	    newtabptr == NULL || newtabptr->zone_rctl_name == NULL)
2845 		return (Z_INVAL);
2846 
2847 	if ((err = operation_prep(handle)) != Z_OK)
2848 		return (err);
2849 
2850 	if ((err = zonecfg_delete_rctl_core(handle, oldtabptr)) != Z_OK)
2851 		return (err);
2852 
2853 	if ((err = zonecfg_add_rctl_core(handle, newtabptr)) != Z_OK)
2854 		return (err);
2855 
2856 	return (Z_OK);
2857 }
2858 
2859 int
2860 zonecfg_add_rctl_value(
2861 	struct zone_rctltab *tabptr,
2862 	struct zone_rctlvaltab *valtabptr)
2863 {
2864 	struct zone_rctlvaltab *last, *old, *new;
2865 	rctlblk_t *rctlblk = alloca(rctlblk_size());
2866 
2867 	last = tabptr->zone_rctl_valptr;
2868 	for (old = last; old != NULL; old = old->zone_rctlval_next)
2869 		last = old;	/* walk to the end of the list */
2870 	new = valtabptr;	/* alloc'd by caller */
2871 	new->zone_rctlval_next = NULL;
2872 	if (zonecfg_construct_rctlblk(valtabptr, rctlblk) != Z_OK)
2873 		return (Z_INVAL);
2874 	if (!zonecfg_valid_rctlblk(rctlblk))
2875 		return (Z_INVAL);
2876 	if (last == NULL)
2877 		tabptr->zone_rctl_valptr = new;
2878 	else
2879 		last->zone_rctlval_next = new;
2880 	return (Z_OK);
2881 }
2882 
2883 int
2884 zonecfg_remove_rctl_value(
2885 	struct zone_rctltab *tabptr,
2886 	struct zone_rctlvaltab *valtabptr)
2887 {
2888 	struct zone_rctlvaltab *last, *this, *next;
2889 
2890 	last = tabptr->zone_rctl_valptr;
2891 	for (this = last; this != NULL; this = this->zone_rctlval_next) {
2892 		if (strcmp(this->zone_rctlval_priv,
2893 		    valtabptr->zone_rctlval_priv) == 0 &&
2894 		    strcmp(this->zone_rctlval_limit,
2895 		    valtabptr->zone_rctlval_limit) == 0 &&
2896 		    strcmp(this->zone_rctlval_action,
2897 		    valtabptr->zone_rctlval_action) == 0) {
2898 			next = this->zone_rctlval_next;
2899 			if (this == tabptr->zone_rctl_valptr)
2900 				tabptr->zone_rctl_valptr = next;
2901 			else
2902 				last->zone_rctlval_next = next;
2903 			free(this);
2904 			return (Z_OK);
2905 		} else
2906 			last = this;
2907 	}
2908 	return (Z_NO_PROPERTY_ID);
2909 }
2910 
2911 char *
2912 zonecfg_strerror(int errnum)
2913 {
2914 	switch (errnum) {
2915 	case Z_OK:
2916 		return (dgettext(TEXT_DOMAIN, "OK"));
2917 	case Z_EMPTY_DOCUMENT:
2918 		return (dgettext(TEXT_DOMAIN, "Empty document"));
2919 	case Z_WRONG_DOC_TYPE:
2920 		return (dgettext(TEXT_DOMAIN, "Wrong document type"));
2921 	case Z_BAD_PROPERTY:
2922 		return (dgettext(TEXT_DOMAIN, "Bad document property"));
2923 	case Z_TEMP_FILE:
2924 		return (dgettext(TEXT_DOMAIN,
2925 		    "Problem creating temporary file"));
2926 	case Z_SAVING_FILE:
2927 		return (dgettext(TEXT_DOMAIN, "Problem saving file"));
2928 	case Z_NO_ENTRY:
2929 		return (dgettext(TEXT_DOMAIN, "No such entry"));
2930 	case Z_BOGUS_ZONE_NAME:
2931 		return (dgettext(TEXT_DOMAIN, "Bogus zone name"));
2932 	case Z_REQD_RESOURCE_MISSING:
2933 		return (dgettext(TEXT_DOMAIN, "Required resource missing"));
2934 	case Z_REQD_PROPERTY_MISSING:
2935 		return (dgettext(TEXT_DOMAIN, "Required property missing"));
2936 	case Z_BAD_HANDLE:
2937 		return (dgettext(TEXT_DOMAIN, "Bad handle"));
2938 	case Z_NOMEM:
2939 		return (dgettext(TEXT_DOMAIN, "Out of memory"));
2940 	case Z_INVAL:
2941 		return (dgettext(TEXT_DOMAIN, "Invalid argument"));
2942 	case Z_ACCES:
2943 		return (dgettext(TEXT_DOMAIN, "Permission denied"));
2944 	case Z_TOO_BIG:
2945 		return (dgettext(TEXT_DOMAIN, "Argument list too long"));
2946 	case Z_MISC_FS:
2947 		return (dgettext(TEXT_DOMAIN,
2948 		    "Miscellaneous file system error"));
2949 	case Z_NO_ZONE:
2950 		return (dgettext(TEXT_DOMAIN, "No such zone configured"));
2951 	case Z_NO_RESOURCE_TYPE:
2952 		return (dgettext(TEXT_DOMAIN, "No such resource type"));
2953 	case Z_NO_RESOURCE_ID:
2954 		return (dgettext(TEXT_DOMAIN, "No such resource with that id"));
2955 	case Z_NO_PROPERTY_TYPE:
2956 		return (dgettext(TEXT_DOMAIN, "No such property type"));
2957 	case Z_NO_PROPERTY_ID:
2958 		return (dgettext(TEXT_DOMAIN, "No such property with that id"));
2959 	case Z_BAD_ZONE_STATE:
2960 		return (dgettext(TEXT_DOMAIN,
2961 		    "Zone state is invalid for the requested operation"));
2962 	case Z_INVALID_DOCUMENT:
2963 		return (dgettext(TEXT_DOMAIN, "Invalid document"));
2964 	case Z_NAME_IN_USE:
2965 		return (dgettext(TEXT_DOMAIN, "Zone name already in use"));
2966 	case Z_NO_SUCH_ID:
2967 		return (dgettext(TEXT_DOMAIN, "No such zone ID"));
2968 	case Z_UPDATING_INDEX:
2969 		return (dgettext(TEXT_DOMAIN, "Problem updating index file"));
2970 	case Z_LOCKING_FILE:
2971 		return (dgettext(TEXT_DOMAIN, "Locking index file"));
2972 	case Z_UNLOCKING_FILE:
2973 		return (dgettext(TEXT_DOMAIN, "Unlocking index file"));
2974 	case Z_INSUFFICIENT_SPEC:
2975 		return (dgettext(TEXT_DOMAIN, "Insufficient specification"));
2976 	case Z_RESOLVED_PATH:
2977 		return (dgettext(TEXT_DOMAIN, "Resolved path mismatch"));
2978 	case Z_IPV6_ADDR_PREFIX_LEN:
2979 		return (dgettext(TEXT_DOMAIN,
2980 		    "IPv6 address missing required prefix length"));
2981 	case Z_BOGUS_ADDRESS:
2982 		return (dgettext(TEXT_DOMAIN,
2983 		    "Neither an IPv4 nor an IPv6 address nor a host name"));
2984 	default:
2985 		return (dgettext(TEXT_DOMAIN, "Unknown error"));
2986 	}
2987 }
2988 
2989 /*
2990  * Note that the zonecfg_setXent() and zonecfg_endXent() calls are all the
2991  * same, as they just turn around and call zonecfg_setent() / zonecfg_endent().
2992  */
2993 
2994 static int
2995 zonecfg_setent(zone_dochandle_t handle)
2996 {
2997 	xmlNodePtr cur;
2998 	int err;
2999 
3000 	if (handle == NULL)
3001 		return (Z_INVAL);
3002 
3003 	if ((err = operation_prep(handle)) != Z_OK) {
3004 		handle->zone_dh_cur = NULL;
3005 		return (err);
3006 	}
3007 	cur = handle->zone_dh_cur;
3008 	cur = cur->xmlChildrenNode;
3009 	handle->zone_dh_cur = cur;
3010 	return (Z_OK);
3011 }
3012 
3013 static int
3014 zonecfg_endent(zone_dochandle_t handle)
3015 {
3016 	if (handle == NULL)
3017 		return (Z_INVAL);
3018 
3019 	handle->zone_dh_cur = handle->zone_dh_top;
3020 	return (Z_OK);
3021 }
3022 
3023 int
3024 zonecfg_setfsent(zone_dochandle_t handle)
3025 {
3026 	return (zonecfg_setent(handle));
3027 }
3028 
3029 int
3030 zonecfg_getfsent(zone_dochandle_t handle, struct zone_fstab *tabptr)
3031 {
3032 	xmlNodePtr cur, options;
3033 	char options_str[MAX_MNTOPT_STR];
3034 	int err;
3035 
3036 	if (handle == NULL)
3037 		return (Z_INVAL);
3038 
3039 	if ((cur = handle->zone_dh_cur) == NULL)
3040 		return (Z_NO_ENTRY);
3041 
3042 	for (; cur != NULL; cur = cur->next)
3043 		if (!xmlStrcmp(cur->name, DTD_ELEM_FS))
3044 			break;
3045 	if (cur == NULL) {
3046 		handle->zone_dh_cur = handle->zone_dh_top;
3047 		return (Z_NO_ENTRY);
3048 	}
3049 
3050 	if ((err = fetchprop(cur, DTD_ATTR_SPECIAL, tabptr->zone_fs_special,
3051 	    sizeof (tabptr->zone_fs_special))) != Z_OK) {
3052 		handle->zone_dh_cur = handle->zone_dh_top;
3053 		return (err);
3054 	}
3055 
3056 	if ((err = fetchprop(cur, DTD_ATTR_RAW, tabptr->zone_fs_raw,
3057 	    sizeof (tabptr->zone_fs_raw))) != Z_OK) {
3058 		handle->zone_dh_cur = handle->zone_dh_top;
3059 		return (err);
3060 	}
3061 
3062 	if ((err = fetchprop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir,
3063 	    sizeof (tabptr->zone_fs_dir))) != Z_OK) {
3064 		handle->zone_dh_cur = handle->zone_dh_top;
3065 		return (err);
3066 	}
3067 
3068 	if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_fs_type,
3069 	    sizeof (tabptr->zone_fs_type))) != Z_OK) {
3070 		handle->zone_dh_cur = handle->zone_dh_top;
3071 		return (err);
3072 	}
3073 
3074 	/* OK for options to be NULL */
3075 	tabptr->zone_fs_options = NULL;
3076 	for (options = cur->xmlChildrenNode; options != NULL;
3077 	    options = options->next) {
3078 		if (fetchprop(options, DTD_ATTR_NAME, options_str,
3079 		    sizeof (options_str)) != Z_OK)
3080 			break;
3081 		if (zonecfg_add_fs_option(tabptr, options_str) != Z_OK)
3082 			break;
3083 	}
3084 
3085 	handle->zone_dh_cur = cur->next;
3086 	return (Z_OK);
3087 }
3088 
3089 int
3090 zonecfg_endfsent(zone_dochandle_t handle)
3091 {
3092 	return (zonecfg_endent(handle));
3093 }
3094 
3095 int
3096 zonecfg_setipdent(zone_dochandle_t handle)
3097 {
3098 	return (zonecfg_setent(handle));
3099 }
3100 
3101 int
3102 zonecfg_getipdent(zone_dochandle_t handle, struct zone_fstab *tabptr)
3103 {
3104 	xmlNodePtr cur;
3105 	int err;
3106 
3107 	if (handle == NULL)
3108 		return (Z_INVAL);
3109 
3110 	if ((cur = handle->zone_dh_cur) == NULL)
3111 		return (Z_NO_ENTRY);
3112 
3113 	for (; cur != NULL; cur = cur->next)
3114 		if (!xmlStrcmp(cur->name, DTD_ELEM_IPD))
3115 			break;
3116 	if (cur == NULL) {
3117 		handle->zone_dh_cur = handle->zone_dh_top;
3118 		return (Z_NO_ENTRY);
3119 	}
3120 
3121 	if ((err = fetchprop(cur, DTD_ATTR_DIR, tabptr->zone_fs_dir,
3122 	    sizeof (tabptr->zone_fs_dir))) != Z_OK) {
3123 		handle->zone_dh_cur = handle->zone_dh_top;
3124 		return (err);
3125 	}
3126 
3127 	handle->zone_dh_cur = cur->next;
3128 	return (Z_OK);
3129 }
3130 
3131 int
3132 zonecfg_endipdent(zone_dochandle_t handle)
3133 {
3134 	return (zonecfg_endent(handle));
3135 }
3136 
3137 int
3138 zonecfg_setnwifent(zone_dochandle_t handle)
3139 {
3140 	return (zonecfg_setent(handle));
3141 }
3142 
3143 int
3144 zonecfg_getnwifent(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
3145 {
3146 	xmlNodePtr cur;
3147 	int err;
3148 
3149 	if (handle == NULL)
3150 		return (Z_INVAL);
3151 
3152 	if ((cur = handle->zone_dh_cur) == NULL)
3153 		return (Z_NO_ENTRY);
3154 
3155 	for (; cur != NULL; cur = cur->next)
3156 		if (!xmlStrcmp(cur->name, DTD_ELEM_NET))
3157 			break;
3158 	if (cur == NULL) {
3159 		handle->zone_dh_cur = handle->zone_dh_top;
3160 		return (Z_NO_ENTRY);
3161 	}
3162 
3163 	if ((err = fetchprop(cur, DTD_ATTR_ADDRESS, tabptr->zone_nwif_address,
3164 	    sizeof (tabptr->zone_nwif_address))) != Z_OK) {
3165 		handle->zone_dh_cur = handle->zone_dh_top;
3166 		return (err);
3167 	}
3168 
3169 	if ((err = fetchprop(cur, DTD_ATTR_PHYSICAL, tabptr->zone_nwif_physical,
3170 	    sizeof (tabptr->zone_nwif_physical))) != Z_OK) {
3171 		handle->zone_dh_cur = handle->zone_dh_top;
3172 		return (err);
3173 	}
3174 
3175 	handle->zone_dh_cur = cur->next;
3176 	return (Z_OK);
3177 }
3178 
3179 int
3180 zonecfg_endnwifent(zone_dochandle_t handle)
3181 {
3182 	return (zonecfg_endent(handle));
3183 }
3184 
3185 int
3186 zonecfg_setdevent(zone_dochandle_t handle)
3187 {
3188 	return (zonecfg_setent(handle));
3189 }
3190 
3191 int
3192 zonecfg_getdevent(zone_dochandle_t handle, struct zone_devtab *tabptr)
3193 {
3194 	xmlNodePtr cur;
3195 	int err;
3196 
3197 	if (handle == NULL)
3198 		return (Z_INVAL);
3199 
3200 	if ((cur = handle->zone_dh_cur) == NULL)
3201 		return (Z_NO_ENTRY);
3202 
3203 	for (; cur != NULL; cur = cur->next)
3204 		if (!xmlStrcmp(cur->name, DTD_ELEM_DEVICE))
3205 			break;
3206 	if (cur == NULL) {
3207 		handle->zone_dh_cur = handle->zone_dh_top;
3208 		return (Z_NO_ENTRY);
3209 	}
3210 
3211 	if ((err = fetchprop(cur, DTD_ATTR_MATCH, tabptr->zone_dev_match,
3212 	    sizeof (tabptr->zone_dev_match))) != Z_OK) {
3213 		handle->zone_dh_cur = handle->zone_dh_top;
3214 		return (err);
3215 	}
3216 
3217 	handle->zone_dh_cur = cur->next;
3218 	return (Z_OK);
3219 }
3220 
3221 int
3222 zonecfg_enddevent(zone_dochandle_t handle)
3223 {
3224 	return (zonecfg_endent(handle));
3225 }
3226 
3227 int
3228 zonecfg_setrctlent(zone_dochandle_t handle)
3229 {
3230 	return (zonecfg_setent(handle));
3231 }
3232 
3233 int
3234 zonecfg_getrctlent(zone_dochandle_t handle, struct zone_rctltab *tabptr)
3235 {
3236 	xmlNodePtr cur, val;
3237 	struct zone_rctlvaltab *valptr;
3238 	int err;
3239 
3240 	if (handle == NULL)
3241 		return (Z_INVAL);
3242 
3243 	if ((cur = handle->zone_dh_cur) == NULL)
3244 		return (Z_NO_ENTRY);
3245 
3246 	for (; cur != NULL; cur = cur->next)
3247 		if (!xmlStrcmp(cur->name, DTD_ELEM_RCTL))
3248 			break;
3249 	if (cur == NULL) {
3250 		handle->zone_dh_cur = handle->zone_dh_top;
3251 		return (Z_NO_ENTRY);
3252 	}
3253 
3254 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_rctl_name,
3255 	    sizeof (tabptr->zone_rctl_name))) != Z_OK) {
3256 		handle->zone_dh_cur = handle->zone_dh_top;
3257 		return (err);
3258 	}
3259 
3260 	tabptr->zone_rctl_valptr = NULL;
3261 	for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
3262 		valptr = (struct zone_rctlvaltab *)malloc(
3263 		    sizeof (struct zone_rctlvaltab));
3264 		if (valptr == NULL)
3265 			return (Z_NOMEM);
3266 		if (fetchprop(val, DTD_ATTR_PRIV, valptr->zone_rctlval_priv,
3267 		    sizeof (valptr->zone_rctlval_priv)) != Z_OK)
3268 			break;
3269 		if (fetchprop(val, DTD_ATTR_LIMIT, valptr->zone_rctlval_limit,
3270 		    sizeof (valptr->zone_rctlval_limit)) != Z_OK)
3271 			break;
3272 		if (fetchprop(val, DTD_ATTR_ACTION, valptr->zone_rctlval_action,
3273 		    sizeof (valptr->zone_rctlval_action)) != Z_OK)
3274 			break;
3275 		if (zonecfg_add_rctl_value(tabptr, valptr) != Z_OK)
3276 			break;
3277 	}
3278 
3279 	handle->zone_dh_cur = cur->next;
3280 	return (Z_OK);
3281 }
3282 
3283 int
3284 zonecfg_endrctlent(zone_dochandle_t handle)
3285 {
3286 	return (zonecfg_endent(handle));
3287 }
3288 
3289 int
3290 zonecfg_setattrent(zone_dochandle_t handle)
3291 {
3292 	return (zonecfg_setent(handle));
3293 }
3294 
3295 int
3296 zonecfg_getattrent(zone_dochandle_t handle, struct zone_attrtab *tabptr)
3297 {
3298 	xmlNodePtr cur;
3299 	int err;
3300 
3301 	if (handle == NULL)
3302 		return (Z_INVAL);
3303 
3304 	if ((cur = handle->zone_dh_cur) == NULL)
3305 		return (Z_NO_ENTRY);
3306 
3307 	for (; cur != NULL; cur = cur->next)
3308 		if (!xmlStrcmp(cur->name, DTD_ELEM_ATTR))
3309 			break;
3310 	if (cur == NULL) {
3311 		handle->zone_dh_cur = handle->zone_dh_top;
3312 		return (Z_NO_ENTRY);
3313 	}
3314 
3315 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_attr_name,
3316 	    sizeof (tabptr->zone_attr_name))) != Z_OK) {
3317 		handle->zone_dh_cur = handle->zone_dh_top;
3318 		return (err);
3319 	}
3320 
3321 	if ((err = fetchprop(cur, DTD_ATTR_TYPE, tabptr->zone_attr_type,
3322 	    sizeof (tabptr->zone_attr_type))) != Z_OK) {
3323 		handle->zone_dh_cur = handle->zone_dh_top;
3324 		return (err);
3325 	}
3326 
3327 	if ((err = fetchprop(cur, DTD_ATTR_VALUE, tabptr->zone_attr_value,
3328 	    sizeof (tabptr->zone_attr_value))) != Z_OK) {
3329 		handle->zone_dh_cur = handle->zone_dh_top;
3330 		return (err);
3331 	}
3332 
3333 	handle->zone_dh_cur = cur->next;
3334 	return (Z_OK);
3335 }
3336 
3337 int
3338 zonecfg_endattrent(zone_dochandle_t handle)
3339 {
3340 	return (zonecfg_endent(handle));
3341 }
3342 
3343 /* This will ultimately be configurable. */
3344 static const char *priv_list[] = {
3345 	PRIV_FILE_CHOWN,
3346 	PRIV_FILE_CHOWN_SELF,
3347 	PRIV_FILE_DAC_EXECUTE,
3348 	PRIV_FILE_DAC_READ,
3349 	PRIV_FILE_DAC_SEARCH,
3350 	PRIV_FILE_DAC_WRITE,
3351 	PRIV_FILE_OWNER,
3352 	PRIV_FILE_SETID,
3353 	PRIV_IPC_DAC_READ,
3354 	PRIV_IPC_DAC_WRITE,
3355 	PRIV_IPC_OWNER,
3356 	PRIV_NET_ICMPACCESS,
3357 	PRIV_NET_PRIVADDR,
3358 	PRIV_PROC_CHROOT,
3359 	PRIV_SYS_AUDIT,
3360 	PRIV_PROC_AUDIT,
3361 	PRIV_PROC_OWNER,
3362 	PRIV_PROC_SETID,
3363 	PRIV_PROC_TASKID,
3364 	PRIV_SYS_ACCT,
3365 	PRIV_SYS_ADMIN,
3366 	PRIV_SYS_MOUNT,
3367 	PRIV_SYS_NFS,
3368 	PRIV_SYS_RESOURCE,
3369 	PRIV_CONTRACT_EVENT,
3370 	PRIV_CONTRACT_OBSERVER,
3371 	NULL
3372 };
3373 
3374 int
3375 zonecfg_get_privset(priv_set_t *privs)
3376 {
3377 	const char **strp;
3378 	priv_set_t *basic = priv_str_to_set("basic", ",", NULL);
3379 
3380 	if (basic == NULL)
3381 		return (Z_INVAL);
3382 
3383 	priv_union(basic, privs);
3384 	priv_freeset(basic);
3385 
3386 	for (strp = priv_list; *strp != NULL; strp++) {
3387 		if (priv_addset(privs, *strp) != 0) {
3388 			return (Z_INVAL);
3389 		}
3390 	}
3391 	return (Z_OK);
3392 }
3393 
3394 int
3395 zone_get_zonepath(char *zone_name, char *zonepath, size_t rp_sz)
3396 {
3397 	zone_dochandle_t handle;
3398 	boolean_t found = B_FALSE;
3399 	struct zoneent *ze;
3400 	FILE *cookie;
3401 	int err;
3402 	char *cp;
3403 
3404 	if (zone_name == NULL)
3405 		return (Z_INVAL);
3406 
3407 	(void) strlcpy(zonepath, zonecfg_root, rp_sz);
3408 	cp = zonepath + strlen(zonepath);
3409 	while (cp > zonepath && cp[-1] == '/')
3410 		*--cp = '\0';
3411 
3412 	if (strcmp(zone_name, GLOBAL_ZONENAME) == 0) {
3413 		if (zonepath[0] == '\0')
3414 			(void) strlcpy(zonepath, "/", rp_sz);
3415 		return (Z_OK);
3416 	}
3417 
3418 	/*
3419 	 * First check the index file.  Because older versions did not have
3420 	 * a copy of the zone path, allow for it to be zero length, in which
3421 	 * case we ignore this result and fall back to the XML files.
3422 	 */
3423 	cookie = setzoneent();
3424 	while ((ze = getzoneent_private(cookie)) != NULL) {
3425 		if (strcmp(ze->zone_name, zone_name) == 0) {
3426 			found = B_TRUE;
3427 			if (ze->zone_path[0] != '\0')
3428 				(void) strlcpy(cp, ze->zone_path,
3429 				    rp_sz - (cp - zonepath));
3430 		}
3431 		free(ze);
3432 		if (found)
3433 			break;
3434 	}
3435 	endzoneent(cookie);
3436 	if (found && *cp != '\0')
3437 		return (Z_OK);
3438 
3439 	/* Fall back to the XML files. */
3440 	if ((handle = zonecfg_init_handle()) == NULL)
3441 		return (Z_NOMEM);
3442 
3443 	/*
3444 	 * Check the snapshot first: if a zone is running, its zonepath
3445 	 * may have changed.
3446 	 */
3447 	if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) {
3448 		if ((err = zonecfg_get_handle(zone_name, handle)) != Z_OK)
3449 			return (err);
3450 	}
3451 	err = zonecfg_get_zonepath(handle, zonepath, rp_sz);
3452 	zonecfg_fini_handle(handle);
3453 	return (err);
3454 }
3455 
3456 int
3457 zone_get_rootpath(char *zone_name, char *rootpath, size_t rp_sz)
3458 {
3459 	int err;
3460 
3461 	/* This function makes sense for non-global zones only. */
3462 	if (strcmp(zone_name, GLOBAL_ZONENAME) == 0)
3463 		return (Z_BOGUS_ZONE_NAME);
3464 	if ((err = zone_get_zonepath(zone_name, rootpath, rp_sz)) != Z_OK)
3465 		return (err);
3466 	if (strlcat(rootpath, "/root", rp_sz) >= rp_sz)
3467 		return (Z_TOO_BIG);
3468 	return (Z_OK);
3469 }
3470 
3471 static zone_state_t
3472 kernel_state_to_user_state(zoneid_t zoneid, zone_status_t kernel_state)
3473 {
3474 	char zoneroot[MAXPATHLEN];
3475 	size_t zlen;
3476 
3477 	assert(kernel_state <= ZONE_MAX_STATE);
3478 	switch (kernel_state) {
3479 		case ZONE_IS_UNINITIALIZED:
3480 			return (ZONE_STATE_READY);
3481 		case ZONE_IS_READY:
3482 			/*
3483 			 * If the zone's root is mounted on $ZONEPATH/lu, then
3484 			 * it's a mounted scratch zone.
3485 			 */
3486 			if (zone_getattr(zoneid, ZONE_ATTR_ROOT, zoneroot,
3487 			    sizeof (zoneroot)) >= 0) {
3488 				zlen = strlen(zoneroot);
3489 				if (zlen > 3 &&
3490 				    strcmp(zoneroot + zlen - 3, "/lu") == 0)
3491 					return (ZONE_STATE_MOUNTED);
3492 			}
3493 			return (ZONE_STATE_READY);
3494 		case ZONE_IS_BOOTING:
3495 		case ZONE_IS_RUNNING:
3496 			return (ZONE_STATE_RUNNING);
3497 		case ZONE_IS_SHUTTING_DOWN:
3498 		case ZONE_IS_EMPTY:
3499 			return (ZONE_STATE_SHUTTING_DOWN);
3500 		case ZONE_IS_DOWN:
3501 		case ZONE_IS_DYING:
3502 		case ZONE_IS_DEAD:
3503 		default:
3504 			return (ZONE_STATE_DOWN);
3505 	}
3506 	/* NOTREACHED */
3507 }
3508 
3509 int
3510 zone_get_state(char *zone_name, zone_state_t *state_num)
3511 {
3512 	zone_status_t status;
3513 	zoneid_t zone_id;
3514 	struct zoneent *ze;
3515 	boolean_t found = B_FALSE;
3516 	FILE *cookie;
3517 	char kernzone[ZONENAME_MAX];
3518 	FILE *fp;
3519 
3520 	if (zone_name == NULL)
3521 		return (Z_INVAL);
3522 
3523 	/*
3524 	 * If we're looking at an alternate root, then we need to query the
3525 	 * kernel using the scratch zone name.
3526 	 */
3527 	zone_id = -1;
3528 	if (*zonecfg_root != '\0' && !zonecfg_is_scratch(zone_name)) {
3529 		if ((fp = zonecfg_open_scratch("", B_FALSE)) != NULL) {
3530 			if (zonecfg_find_scratch(fp, zone_name, zonecfg_root,
3531 			    kernzone, sizeof (kernzone)) == 0)
3532 				zone_id = getzoneidbyname(kernzone);
3533 			zonecfg_close_scratch(fp);
3534 		}
3535 	} else {
3536 		zone_id = getzoneidbyname(zone_name);
3537 	}
3538 
3539 	/* check to see if zone is running */
3540 	if (zone_id != -1 &&
3541 	    zone_getattr(zone_id, ZONE_ATTR_STATUS, &status,
3542 	    sizeof (status)) >= 0) {
3543 		*state_num = kernel_state_to_user_state(zone_id, status);
3544 		return (Z_OK);
3545 	}
3546 
3547 	cookie = setzoneent();
3548 	while ((ze = getzoneent_private(cookie)) != NULL) {
3549 		if (strcmp(ze->zone_name, zone_name) == 0) {
3550 			found = B_TRUE;
3551 			*state_num = ze->zone_state;
3552 		}
3553 		free(ze);
3554 		if (found)
3555 			break;
3556 	}
3557 	endzoneent(cookie);
3558 	return ((found) ? Z_OK : Z_NO_ZONE);
3559 }
3560 
3561 int
3562 zone_set_state(char *zone, zone_state_t state)
3563 {
3564 	struct zoneent ze;
3565 
3566 	if (state != ZONE_STATE_CONFIGURED && state != ZONE_STATE_INSTALLED &&
3567 	    state != ZONE_STATE_INCOMPLETE)
3568 		return (Z_INVAL);
3569 
3570 	bzero(&ze, sizeof (ze));
3571 	(void) strlcpy(ze.zone_name, zone, sizeof (ze.zone_name));
3572 	ze.zone_state = state;
3573 	(void) strlcpy(ze.zone_path, "", sizeof (ze.zone_path));
3574 	return (putzoneent(&ze, PZE_MODIFY));
3575 }
3576 
3577 /*
3578  * Get id (if any) for specified zone.  There are four possible outcomes:
3579  * - If the string corresponds to the numeric id of an active (booted)
3580  *   zone, sets *zip to the zone id and returns 0.
3581  * - If the string corresponds to the name of an active (booted) zone,
3582  *   sets *zip to the zone id and returns 0.
3583  * - If the string is a name in the configuration but is not booted,
3584  *   sets *zip to ZONE_ID_UNDEFINED and returns 0.
3585  * - Otherwise, leaves *zip unchanged and returns -1.
3586  *
3587  * This function acts as an auxiliary filter on the function of the same
3588  * name in libc; the linker binds to this version if libzonecfg exists,
3589  * and the libc version if it doesn't.  Any changes to this version of
3590  * the function should probably be reflected in the libc version as well.
3591  */
3592 int
3593 zone_get_id(const char *str, zoneid_t *zip)
3594 {
3595 	zone_dochandle_t hdl;
3596 	zoneid_t zoneid;
3597 	char *cp;
3598 	int err;
3599 
3600 	/* first try looking for active zone by id */
3601 	errno = 0;
3602 	zoneid = (zoneid_t)strtol(str, &cp, 0);
3603 	if (errno == 0 && cp != str && *cp == '\0' &&
3604 	    getzonenamebyid(zoneid, NULL, 0) != -1) {
3605 		*zip = zoneid;
3606 		return (0);
3607 	}
3608 
3609 	/* then look for active zone by name */
3610 	if ((zoneid = getzoneidbyname(str)) != -1) {
3611 		*zip = zoneid;
3612 		return (0);
3613 	}
3614 
3615 	/* if in global zone, try looking up name in configuration database */
3616 	if (getzoneid() != GLOBAL_ZONEID ||
3617 	    (hdl = zonecfg_init_handle()) == NULL)
3618 		return (-1);
3619 
3620 	if (zonecfg_get_handle(str, hdl) == Z_OK) {
3621 		/* zone exists but isn't active */
3622 		*zip = ZONE_ID_UNDEFINED;
3623 		err = 0;
3624 	} else {
3625 		err = -1;
3626 	}
3627 
3628 	zonecfg_fini_handle(hdl);
3629 	return (err);
3630 }
3631 
3632 char *
3633 zone_state_str(zone_state_t state_num)
3634 {
3635 	switch (state_num) {
3636 	case ZONE_STATE_CONFIGURED:
3637 		return (ZONE_STATE_STR_CONFIGURED);
3638 	case ZONE_STATE_INCOMPLETE:
3639 		return (ZONE_STATE_STR_INCOMPLETE);
3640 	case ZONE_STATE_INSTALLED:
3641 		return (ZONE_STATE_STR_INSTALLED);
3642 	case ZONE_STATE_READY:
3643 		return (ZONE_STATE_STR_READY);
3644 	case ZONE_STATE_MOUNTED:
3645 		return (ZONE_STATE_STR_MOUNTED);
3646 	case ZONE_STATE_RUNNING:
3647 		return (ZONE_STATE_STR_RUNNING);
3648 	case ZONE_STATE_SHUTTING_DOWN:
3649 		return (ZONE_STATE_STR_SHUTTING_DOWN);
3650 	case ZONE_STATE_DOWN:
3651 		return (ZONE_STATE_STR_DOWN);
3652 	default:
3653 		return ("unknown");
3654 	}
3655 }
3656 
3657 /*
3658  * Given a UUID value, find an associated zone name.  This is intended to be
3659  * used by callers who set up some 'default' name (corresponding to the
3660  * expected name for the zone) in the zonename buffer, and thus the function
3661  * doesn't touch this buffer on failure.
3662  */
3663 int
3664 zonecfg_get_name_by_uuid(const uuid_t uuid, char *zonename, size_t namelen)
3665 {
3666 	FILE *fp;
3667 	struct zoneent *ze;
3668 
3669 	/*
3670 	 * A small amount of subterfuge via casts is necessary here because
3671 	 * libuuid doesn't use const correctly, but we don't want to export
3672 	 * this brokenness to our clients.
3673 	 */
3674 	if (uuid_is_null(*(uuid_t *)&uuid))
3675 		return (Z_NO_ZONE);
3676 	if ((fp = setzoneent()) == NULL)
3677 		return (Z_NO_ZONE);
3678 	while ((ze = getzoneent_private(fp)) != NULL) {
3679 		if (uuid_compare(*(uuid_t *)&uuid, ze->zone_uuid) == 0)
3680 			break;
3681 		free(ze);
3682 	}
3683 	endzoneent(fp);
3684 	if (ze != NULL) {
3685 		(void) strlcpy(zonename, ze->zone_name, namelen);
3686 		free(ze);
3687 		return (Z_OK);
3688 	} else {
3689 		return (Z_NO_ZONE);
3690 	}
3691 }
3692 
3693 /*
3694  * Given a zone name, get its UUID.  Returns a "NULL" UUID value if the zone
3695  * exists but the file doesn't have a value set yet.  Returns an error if the
3696  * zone cannot be located.
3697  */
3698 int
3699 zonecfg_get_uuid(const char *zonename, uuid_t uuid)
3700 {
3701 	FILE *fp;
3702 	struct zoneent *ze;
3703 
3704 	if ((fp = setzoneent()) == NULL)
3705 		return (Z_NO_ZONE);
3706 	while ((ze = getzoneent_private(fp)) != NULL) {
3707 		if (strcmp(ze->zone_name, zonename) == 0)
3708 			break;
3709 		free(ze);
3710 	}
3711 	endzoneent(fp);
3712 	if (ze != NULL) {
3713 		uuid_copy(uuid, ze->zone_uuid);
3714 		free(ze);
3715 		return (Z_OK);
3716 	} else {
3717 		return (Z_NO_ZONE);
3718 	}
3719 }
3720 
3721 /*
3722  * File-system convenience functions.
3723  */
3724 boolean_t
3725 zonecfg_valid_fs_type(const char *type)
3726 {
3727 	/*
3728 	 * We already know which FS types don't work.
3729 	 */
3730 	if (strcmp(type, "proc") == 0 ||
3731 	    strcmp(type, "mntfs") == 0 ||
3732 	    strcmp(type, "autofs") == 0 ||
3733 	    strncmp(type, "nfs", sizeof ("nfs") - 1) == 0 ||
3734 	    strcmp(type, "cachefs") == 0)
3735 		return (B_FALSE);
3736 	/*
3737 	 * The caller may do more detailed verification to make sure other
3738 	 * aspects of this filesystem type make sense.
3739 	 */
3740 	return (B_TRUE);
3741 }
3742 
3743 /*
3744  * Generally uninteresting rctl convenience functions.
3745  */
3746 
3747 int
3748 zonecfg_construct_rctlblk(const struct zone_rctlvaltab *rctlval,
3749     rctlblk_t *rctlblk)
3750 {
3751 	unsigned long long ull;
3752 	char *endp;
3753 	rctl_priv_t priv;
3754 	rctl_qty_t limit;
3755 	uint_t action;
3756 
3757 	/* Get the privilege */
3758 	if (strcmp(rctlval->zone_rctlval_priv, "basic") == 0) {
3759 		priv = RCPRIV_BASIC;
3760 	} else if (strcmp(rctlval->zone_rctlval_priv, "privileged") == 0) {
3761 		priv = RCPRIV_PRIVILEGED;
3762 	} else {
3763 		/* Invalid privilege */
3764 		return (Z_INVAL);
3765 	}
3766 
3767 	/* deal with negative input; strtoull(3c) doesn't do what we want */
3768 	if (rctlval->zone_rctlval_limit[0] == '-')
3769 		return (Z_INVAL);
3770 	/* Get the limit */
3771 	errno = 0;
3772 	ull = strtoull(rctlval->zone_rctlval_limit, &endp, 0);
3773 	if (errno != 0 || *endp != '\0') {
3774 		/* parse failed */
3775 		return (Z_INVAL);
3776 	}
3777 	limit = (rctl_qty_t)ull;
3778 
3779 	/* Get the action */
3780 	if (strcmp(rctlval->zone_rctlval_action, "none") == 0) {
3781 		action = RCTL_LOCAL_NOACTION;
3782 	} else if (strcmp(rctlval->zone_rctlval_action, "signal") == 0) {
3783 		action = RCTL_LOCAL_SIGNAL;
3784 	} else if (strcmp(rctlval->zone_rctlval_action, "deny") == 0) {
3785 		action = RCTL_LOCAL_DENY;
3786 	} else {
3787 		/* Invalid Action */
3788 		return (Z_INVAL);
3789 	}
3790 	rctlblk_set_local_action(rctlblk, action, 0);
3791 	rctlblk_set_privilege(rctlblk, priv);
3792 	rctlblk_set_value(rctlblk, limit);
3793 	return (Z_OK);
3794 }
3795 
3796 static int
3797 rctl_check(const char *rctlname, void *arg)
3798 {
3799 	const char *attrname = arg;
3800 
3801 	/*
3802 	 * Returning 1 here is our signal to zonecfg_is_rctl() that it is
3803 	 * indeed an rctl name recognized by the system.
3804 	 */
3805 	return (strcmp(rctlname, attrname) == 0 ? 1 : 0);
3806 }
3807 
3808 boolean_t
3809 zonecfg_is_rctl(const char *name)
3810 {
3811 	return (rctl_walk(rctl_check, (void *)name) == 1);
3812 }
3813 
3814 boolean_t
3815 zonecfg_valid_rctlname(const char *name)
3816 {
3817 	const char *c;
3818 
3819 	if (strncmp(name, "zone.", sizeof ("zone.") - 1) != 0)
3820 		return (B_FALSE);
3821 	if (strlen(name) == sizeof ("zone.") - 1)
3822 		return (B_FALSE);
3823 	for (c = name + sizeof ("zone.") - 1; *c != '\0'; c++) {
3824 		if (!isalpha(*c) && *c != '-')
3825 			return (B_FALSE);
3826 	}
3827 	return (B_TRUE);
3828 }
3829 
3830 boolean_t
3831 zonecfg_valid_rctlblk(const rctlblk_t *rctlblk)
3832 {
3833 	rctl_priv_t priv = rctlblk_get_privilege((rctlblk_t *)rctlblk);
3834 	uint_t action = rctlblk_get_local_action((rctlblk_t *)rctlblk, NULL);
3835 
3836 	if (priv != RCPRIV_PRIVILEGED)
3837 		return (B_FALSE);
3838 	if (action != RCTL_LOCAL_NOACTION && action != RCTL_LOCAL_DENY)
3839 		return (B_FALSE);
3840 	return (B_TRUE);
3841 }
3842 
3843 boolean_t
3844 zonecfg_valid_rctl(const char *name, const rctlblk_t *rctlblk)
3845 {
3846 	rctlblk_t *current, *next;
3847 	rctl_qty_t limit = rctlblk_get_value((rctlblk_t *)rctlblk);
3848 	uint_t action = rctlblk_get_local_action((rctlblk_t *)rctlblk, NULL);
3849 	uint_t global_flags;
3850 
3851 	if (!zonecfg_valid_rctlblk(rctlblk))
3852 		return (B_FALSE);
3853 	if (!zonecfg_valid_rctlname(name))
3854 		return (B_FALSE);
3855 
3856 	current = alloca(rctlblk_size());
3857 	if (getrctl(name, NULL, current, RCTL_FIRST) != 0)
3858 		return (B_TRUE);	/* not an rctl on this system */
3859 	/*
3860 	 * Make sure the proposed value isn't greater than the current system
3861 	 * value.
3862 	 */
3863 	next = alloca(rctlblk_size());
3864 	while (rctlblk_get_privilege(current) != RCPRIV_SYSTEM) {
3865 		rctlblk_t *tmp;
3866 
3867 		if (getrctl(name, current, next, RCTL_NEXT) != 0)
3868 			return (B_FALSE);	/* shouldn't happen */
3869 		tmp = current;
3870 		current = next;
3871 		next = tmp;
3872 	}
3873 	if (limit > rctlblk_get_value(current))
3874 		return (B_FALSE);
3875 
3876 	/*
3877 	 * Make sure the proposed action is allowed.
3878 	 */
3879 	global_flags = rctlblk_get_global_flags(current);
3880 	if ((global_flags & RCTL_GLOBAL_DENY_NEVER) &&
3881 	    action == RCTL_LOCAL_DENY)
3882 		return (B_FALSE);
3883 	if ((global_flags & RCTL_GLOBAL_DENY_ALWAYS) &&
3884 	    action == RCTL_LOCAL_NOACTION)
3885 		return (B_FALSE);
3886 
3887 	return (B_TRUE);
3888 }
3889 
3890 /*
3891  * There is always a race condition between reading the initial copy of
3892  * a zones state and its state changing.  We address this by providing
3893  * zonecfg_notify_critical_enter and zonecfg_noticy_critical_exit functions.
3894  * When zonecfg_critical_enter is called, sets the state field to LOCKED
3895  * and aquires biglock. Biglock protects against other threads executing
3896  * critical_enter and the state field protects against state changes during
3897  * the critical period.
3898  *
3899  * If any state changes occur, zn_cb will set the failed field of the znotify
3900  * structure.  This will cause the critical_exit function to re-lock the
3901  * channel and return an error. Since evsnts may be delayed, the critical_exit
3902  * function "flushes" the queue by putting an event on the queue and waiting for
3903  * zn_cb to notify critical_exit that it received the ping event.
3904  */
3905 static const char *
3906 string_get_tok(const char *in, char delim, int num)
3907 {
3908 	int i = 0;
3909 
3910 	for (; i < num; in++) {
3911 		if (*in == delim)
3912 			i++;
3913 		if (*in == 0)
3914 			return (NULL);
3915 	}
3916 	return (in);
3917 }
3918 
3919 static boolean_t
3920 is_ping(sysevent_t *ev)
3921 {
3922 	if (strcmp(sysevent_get_subclass_name(ev),
3923 		ZONE_EVENT_PING_SUBCLASS) == 0) {
3924 		return (B_TRUE);
3925 	} else {
3926 		return (B_FALSE);
3927 	}
3928 }
3929 
3930 static boolean_t
3931 is_my_ping(sysevent_t *ev)
3932 {
3933 	const char *sender;
3934 	char mypid[sizeof (pid_t) * 3 + 1];
3935 
3936 	(void) snprintf(mypid, sizeof (mypid), "%i", getpid());
3937 	sender = string_get_tok(sysevent_get_pub(ev), ':', 3);
3938 	if (sender == NULL)
3939 		return (B_FALSE);
3940 	if (strcmp(sender, mypid) != 0)
3941 		return (B_FALSE);
3942 	return (B_TRUE);
3943 }
3944 
3945 static int
3946 do_callback(struct znotify *zevtchan, sysevent_t *ev)
3947 {
3948 	nvlist_t *l;
3949 	int zid;
3950 	char *zonename;
3951 	char *newstate;
3952 	char *oldstate;
3953 	int ret;
3954 	hrtime_t when;
3955 
3956 	if (strcmp(sysevent_get_subclass_name(ev),
3957 	    ZONE_EVENT_STATUS_SUBCLASS) == 0) {
3958 
3959 		if (sysevent_get_attr_list(ev, &l) != 0) {
3960 			if (errno == ENOMEM) {
3961 				zevtchan->zn_failure_count++;
3962 				return (EAGAIN);
3963 			}
3964 			return (0);
3965 		}
3966 		ret = 0;
3967 
3968 		if ((nvlist_lookup_string(l, ZONE_CB_NAME, &zonename) == 0) &&
3969 		    (nvlist_lookup_string(l, ZONE_CB_NEWSTATE, &newstate)
3970 			== 0) &&
3971 		    (nvlist_lookup_string(l, ZONE_CB_OLDSTATE, &oldstate)
3972 			== 0) &&
3973 		    (nvlist_lookup_uint64(l, ZONE_CB_TIMESTAMP,
3974 			    (uint64_t *)&when) == 0) &&
3975 		    (nvlist_lookup_int32(l, ZONE_CB_ZONEID, &zid) == 0)) {
3976 			ret = zevtchan->zn_callback(zonename, zid, newstate,
3977 			    oldstate, when, zevtchan->zn_private);
3978 		}
3979 
3980 		zevtchan->zn_failure_count = 0;
3981 		nvlist_free(l);
3982 		return (ret);
3983 	} else {
3984 		/*
3985 		 * We have received an event in an unknown subclass. Ignore.
3986 		 */
3987 		zevtchan->zn_failure_count = 0;
3988 		return (0);
3989 	}
3990 }
3991 
3992 static int
3993 zn_cb(sysevent_t *ev, void *p)
3994 {
3995 	struct znotify *zevtchan = p;
3996 	int error;
3997 
3998 	(void) pthread_mutex_lock(&(zevtchan->zn_mutex));
3999 
4000 	if (is_ping(ev) && !is_my_ping(ev)) {
4001 		(void) pthread_mutex_unlock((&zevtchan->zn_mutex));
4002 		return (0);
4003 	}
4004 
4005 	if (zevtchan->zn_state == ZN_LOCKED) {
4006 		assert(!is_ping(ev));
4007 		zevtchan->zn_failed = B_TRUE;
4008 		(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
4009 		return (0);
4010 	}
4011 
4012 	if (zevtchan->zn_state == ZN_PING_INFLIGHT) {
4013 		if (is_ping(ev)) {
4014 			zevtchan->zn_state = ZN_PING_RECEIVED;
4015 			(void) pthread_cond_signal(&(zevtchan->zn_cond));
4016 			(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
4017 			return (0);
4018 		} else {
4019 			zevtchan->zn_failed = B_TRUE;
4020 			(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
4021 			return (0);
4022 		}
4023 	}
4024 
4025 	if (zevtchan->zn_state == ZN_UNLOCKED) {
4026 
4027 		error = do_callback(zevtchan, ev);
4028 		(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
4029 		/*
4030 		 * Every ENOMEM failure causes do_callback to increment
4031 		 * zn_failure_count and every success causes it to
4032 		 * set zn_failure_count to zero.  If we got EAGAIN,
4033 		 * we will sleep for zn_failure_count seconds and return
4034 		 * EAGAIN to gpec to try again.
4035 		 *
4036 		 * After 55 seconds, or 10 try's we give up and drop the
4037 		 * event.
4038 		 */
4039 		if (error == EAGAIN) {
4040 			if (zevtchan->zn_failure_count > ZONE_CB_RETRY_COUNT) {
4041 				return (0);
4042 			}
4043 			(void) sleep(zevtchan->zn_failure_count);
4044 		}
4045 		return (error);
4046 	}
4047 
4048 	if (zevtchan->zn_state == ZN_PING_RECEIVED) {
4049 		(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
4050 		return (0);
4051 	}
4052 
4053 	abort();
4054 	return (0);
4055 }
4056 
4057 void
4058 zonecfg_notify_critical_enter(void *h)
4059 {
4060 	struct znotify *zevtchan = h;
4061 
4062 	(void) pthread_mutex_lock(&(zevtchan->zn_bigmutex));
4063 	zevtchan->zn_state = ZN_LOCKED;
4064 }
4065 
4066 int
4067 zonecfg_notify_critical_exit(void * h)
4068 {
4069 
4070 	struct znotify *zevtchan = h;
4071 
4072 	if (zevtchan->zn_state == ZN_UNLOCKED)
4073 		return (0);
4074 
4075 	(void) pthread_mutex_lock(&(zevtchan->zn_mutex));
4076 	zevtchan->zn_state = ZN_PING_INFLIGHT;
4077 
4078 	(void) sysevent_evc_publish(zevtchan->zn_eventchan,
4079 	    ZONE_EVENT_STATUS_CLASS,
4080 	    ZONE_EVENT_PING_SUBCLASS, ZONE_EVENT_PING_PUBLISHER,
4081 	    zevtchan->zn_subscriber_id, NULL, EVCH_SLEEP);
4082 
4083 	while (zevtchan->zn_state != ZN_PING_RECEIVED) {
4084 		(void) pthread_cond_wait(&(zevtchan->zn_cond),
4085 		    &(zevtchan->zn_mutex));
4086 	}
4087 
4088 	if (zevtchan->zn_failed == B_TRUE) {
4089 		zevtchan->zn_state = ZN_LOCKED;
4090 		zevtchan->zn_failed = B_FALSE;
4091 		(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
4092 		return (1);
4093 	}
4094 
4095 	zevtchan->zn_state = ZN_UNLOCKED;
4096 	(void) pthread_mutex_unlock(&(zevtchan->zn_mutex));
4097 	(void) pthread_mutex_unlock(&(zevtchan->zn_bigmutex));
4098 	return (0);
4099 }
4100 
4101 void
4102 zonecfg_notify_critical_abort(void *h)
4103 {
4104 	struct znotify *zevtchan = h;
4105 
4106 	zevtchan->zn_state = ZN_UNLOCKED;
4107 	zevtchan->zn_failed = B_FALSE;
4108 	/*
4109 	 * Don't do anything about zn_lock. If it is held, it could only be
4110 	 * held by zn_cb and it will be unlocked soon.
4111 	 */
4112 	(void) pthread_mutex_unlock(&(zevtchan->zn_bigmutex));
4113 }
4114 
4115 void *
4116 zonecfg_notify_bind(int(*func)(const char *zonename, zoneid_t zid,
4117     const char *newstate, const char *oldstate, hrtime_t when, void *p),
4118     void *p)
4119 {
4120 	struct znotify *zevtchan;
4121 	int i = 1;
4122 	int r;
4123 
4124 	zevtchan = malloc(sizeof (struct znotify));
4125 
4126 	if (zevtchan == NULL)
4127 		return (NULL);
4128 
4129 	zevtchan->zn_private = p;
4130 	zevtchan->zn_callback = func;
4131 	zevtchan->zn_state = ZN_UNLOCKED;
4132 	zevtchan->zn_failed = B_FALSE;
4133 
4134 	if (pthread_mutex_init(&(zevtchan->zn_mutex), NULL))
4135 		goto out3;
4136 	if (pthread_cond_init(&(zevtchan->zn_cond), NULL)) {
4137 		(void) pthread_mutex_destroy(&(zevtchan->zn_mutex));
4138 		goto out3;
4139 	}
4140 	if (pthread_mutex_init(&(zevtchan->zn_bigmutex), NULL)) {
4141 		(void) pthread_mutex_destroy(&(zevtchan->zn_mutex));
4142 		(void) pthread_cond_destroy(&(zevtchan->zn_cond));
4143 		goto out3;
4144 	}
4145 
4146 	if (sysevent_evc_bind(ZONE_EVENT_CHANNEL, &(zevtchan->zn_eventchan),
4147 		0) != 0)
4148 		goto out2;
4149 
4150 	do {
4151 		/*
4152 		 * At 4 digits the subscriber ID gets too long and we have
4153 		 * no chance of successfully registering.
4154 		 */
4155 		if (i > 999)
4156 			goto out1;
4157 
4158 		(void) sprintf(zevtchan->zn_subscriber_id, "zone_%li_%i",
4159 		    getpid() % 999999l, i);
4160 
4161 		r = sysevent_evc_subscribe(zevtchan->zn_eventchan,
4162 		    zevtchan->zn_subscriber_id, ZONE_EVENT_STATUS_CLASS, zn_cb,
4163 		    zevtchan, 0);
4164 
4165 		i++;
4166 
4167 	} while (r);
4168 
4169 	return (zevtchan);
4170 out1:
4171 	sysevent_evc_unbind(zevtchan->zn_eventchan);
4172 out2:
4173 	(void) pthread_mutex_destroy(&zevtchan->zn_mutex);
4174 	(void) pthread_cond_destroy(&zevtchan->zn_cond);
4175 	(void) pthread_mutex_destroy(&(zevtchan->zn_bigmutex));
4176 out3:
4177 	free(zevtchan);
4178 
4179 	return (NULL);
4180 }
4181 
4182 void
4183 zonecfg_notify_unbind(void *handle)
4184 {
4185 
4186 	int ret;
4187 
4188 	sysevent_evc_unbind(((struct znotify *)handle)->zn_eventchan);
4189 	/*
4190 	 * Check that all evc threads have gone away. This should be
4191 	 * enforced by sysevent_evc_unbind.
4192 	 */
4193 	ret = pthread_mutex_trylock(&((struct znotify *)handle)->zn_mutex);
4194 
4195 	if (ret)
4196 		abort();
4197 
4198 	(void) pthread_mutex_unlock(&((struct znotify *)handle)->zn_mutex);
4199 	(void) pthread_mutex_destroy(&((struct znotify *)handle)->zn_mutex);
4200 	(void) pthread_cond_destroy(&((struct znotify *)handle)->zn_cond);
4201 	(void) pthread_mutex_destroy(&((struct znotify *)handle)->zn_bigmutex);
4202 
4203 	free(handle);
4204 }
4205 
4206 static int
4207 zonecfg_add_ds_core(zone_dochandle_t handle, struct zone_dstab *tabptr)
4208 {
4209 	xmlNodePtr newnode, cur = handle->zone_dh_cur;
4210 	int err;
4211 
4212 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DATASET, NULL);
4213 	if ((err = newprop(newnode, DTD_ATTR_NAME,
4214 	    tabptr->zone_dataset_name)) != Z_OK)
4215 		return (err);
4216 	return (Z_OK);
4217 }
4218 
4219 int
4220 zonecfg_add_ds(zone_dochandle_t handle, struct zone_dstab *tabptr)
4221 {
4222 	int err;
4223 
4224 	if (tabptr == NULL)
4225 		return (Z_INVAL);
4226 
4227 	if ((err = operation_prep(handle)) != Z_OK)
4228 		return (err);
4229 
4230 	if ((err = zonecfg_add_ds_core(handle, tabptr)) != Z_OK)
4231 		return (err);
4232 
4233 	return (Z_OK);
4234 }
4235 
4236 static int
4237 zonecfg_delete_ds_core(zone_dochandle_t handle, struct zone_dstab *tabptr)
4238 {
4239 	xmlNodePtr cur = handle->zone_dh_cur;
4240 
4241 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
4242 		if (xmlStrcmp(cur->name, DTD_ELEM_DATASET))
4243 			continue;
4244 
4245 		if (match_prop(cur, DTD_ATTR_NAME,
4246 		    tabptr->zone_dataset_name)) {
4247 			xmlUnlinkNode(cur);
4248 			xmlFreeNode(cur);
4249 			return (Z_OK);
4250 		}
4251 	}
4252 	return (Z_NO_RESOURCE_ID);
4253 }
4254 
4255 int
4256 zonecfg_delete_ds(zone_dochandle_t handle, struct zone_dstab *tabptr)
4257 {
4258 	int err;
4259 
4260 	if (tabptr == NULL)
4261 		return (Z_INVAL);
4262 
4263 	if ((err = operation_prep(handle)) != Z_OK)
4264 		return (err);
4265 
4266 	if ((err = zonecfg_delete_ds_core(handle, tabptr)) != Z_OK)
4267 		return (err);
4268 
4269 	return (Z_OK);
4270 }
4271 
4272 int
4273 zonecfg_modify_ds(
4274 	zone_dochandle_t handle,
4275 	struct zone_dstab *oldtabptr,
4276 	struct zone_dstab *newtabptr)
4277 {
4278 	int err;
4279 
4280 	if (oldtabptr == NULL || newtabptr == NULL)
4281 		return (Z_INVAL);
4282 
4283 	if ((err = operation_prep(handle)) != Z_OK)
4284 		return (err);
4285 
4286 	if ((err = zonecfg_delete_ds_core(handle, oldtabptr)) != Z_OK)
4287 		return (err);
4288 
4289 	if ((err = zonecfg_add_ds_core(handle, newtabptr)) != Z_OK)
4290 		return (err);
4291 
4292 	return (Z_OK);
4293 }
4294 
4295 int
4296 zonecfg_lookup_ds(zone_dochandle_t handle, struct zone_dstab *tabptr)
4297 {
4298 	xmlNodePtr cur, firstmatch;
4299 	int err;
4300 	char dataset[MAXNAMELEN];
4301 
4302 	if (tabptr == NULL)
4303 		return (Z_INVAL);
4304 
4305 	if ((err = operation_prep(handle)) != Z_OK)
4306 		return (err);
4307 
4308 	cur = handle->zone_dh_cur;
4309 	firstmatch = NULL;
4310 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
4311 		if (xmlStrcmp(cur->name, DTD_ELEM_DATASET))
4312 			continue;
4313 		if (strlen(tabptr->zone_dataset_name) > 0) {
4314 			if ((fetchprop(cur, DTD_ATTR_NAME, dataset,
4315 			    sizeof (dataset)) == Z_OK) &&
4316 			    (strcmp(tabptr->zone_dataset_name,
4317 			    dataset) == 0)) {
4318 				if (firstmatch == NULL)
4319 					firstmatch = cur;
4320 				else
4321 					return (Z_INSUFFICIENT_SPEC);
4322 			}
4323 		}
4324 	}
4325 	if (firstmatch == NULL)
4326 		return (Z_NO_RESOURCE_ID);
4327 
4328 	cur = firstmatch;
4329 
4330 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_dataset_name,
4331 	    sizeof (tabptr->zone_dataset_name))) != Z_OK)
4332 		return (err);
4333 
4334 	return (Z_OK);
4335 }
4336 
4337 int
4338 zonecfg_setdsent(zone_dochandle_t handle)
4339 {
4340 	return (zonecfg_setent(handle));
4341 }
4342 
4343 int
4344 zonecfg_getdsent(zone_dochandle_t handle, struct zone_dstab *tabptr)
4345 {
4346 	xmlNodePtr cur;
4347 	int err;
4348 
4349 	if (handle == NULL)
4350 		return (Z_INVAL);
4351 
4352 	if ((cur = handle->zone_dh_cur) == NULL)
4353 		return (Z_NO_ENTRY);
4354 
4355 	for (; cur != NULL; cur = cur->next)
4356 		if (!xmlStrcmp(cur->name, DTD_ELEM_DATASET))
4357 			break;
4358 	if (cur == NULL) {
4359 		handle->zone_dh_cur = handle->zone_dh_top;
4360 		return (Z_NO_ENTRY);
4361 	}
4362 
4363 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_dataset_name,
4364 	    sizeof (tabptr->zone_dataset_name))) != Z_OK) {
4365 		handle->zone_dh_cur = handle->zone_dh_top;
4366 		return (err);
4367 	}
4368 
4369 	handle->zone_dh_cur = cur->next;
4370 	return (Z_OK);
4371 }
4372 
4373 int
4374 zonecfg_enddsent(zone_dochandle_t handle)
4375 {
4376 	return (zonecfg_endent(handle));
4377 }
4378 
4379 int
4380 zonecfg_setpkgent(zone_dochandle_t handle)
4381 {
4382 	return (zonecfg_setent(handle));
4383 }
4384 
4385 int
4386 zonecfg_getpkgent(zone_dochandle_t handle, struct zone_pkgtab *tabptr)
4387 {
4388 	xmlNodePtr cur;
4389 	int err;
4390 
4391 	if (handle == NULL)
4392 		return (Z_INVAL);
4393 
4394 	if ((cur = handle->zone_dh_cur) == NULL)
4395 		return (Z_NO_ENTRY);
4396 
4397 	for (; cur != NULL; cur = cur->next)
4398 		if (!xmlStrcmp(cur->name, DTD_ELEM_PACKAGE))
4399 			break;
4400 	if (cur == NULL) {
4401 		handle->zone_dh_cur = handle->zone_dh_top;
4402 		return (Z_NO_ENTRY);
4403 	}
4404 
4405 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_pkg_name,
4406 	    sizeof (tabptr->zone_pkg_name))) != Z_OK) {
4407 		handle->zone_dh_cur = handle->zone_dh_top;
4408 		return (err);
4409 	}
4410 
4411 	if ((err = fetchprop(cur, DTD_ATTR_VERSION, tabptr->zone_pkg_version,
4412 	    sizeof (tabptr->zone_pkg_version))) != Z_OK) {
4413 		handle->zone_dh_cur = handle->zone_dh_top;
4414 		return (err);
4415 	}
4416 
4417 	handle->zone_dh_cur = cur->next;
4418 	return (Z_OK);
4419 }
4420 
4421 int
4422 zonecfg_endpkgent(zone_dochandle_t handle)
4423 {
4424 	return (zonecfg_endent(handle));
4425 }
4426 
4427 int
4428 zonecfg_setpatchent(zone_dochandle_t handle)
4429 {
4430 	return (zonecfg_setent(handle));
4431 }
4432 
4433 int
4434 zonecfg_getpatchent(zone_dochandle_t handle, struct zone_patchtab *tabptr)
4435 {
4436 	xmlNodePtr cur;
4437 	int err;
4438 
4439 	if (handle == NULL)
4440 		return (Z_INVAL);
4441 
4442 	if ((cur = handle->zone_dh_cur) == NULL)
4443 		return (Z_NO_ENTRY);
4444 
4445 	for (; cur != NULL; cur = cur->next)
4446 		if (!xmlStrcmp(cur->name, DTD_ELEM_PATCH))
4447 			break;
4448 	if (cur == NULL) {
4449 		handle->zone_dh_cur = handle->zone_dh_top;
4450 		return (Z_NO_ENTRY);
4451 	}
4452 
4453 	if ((err = fetchprop(cur, DTD_ATTR_ID, tabptr->zone_patch_id,
4454 	    sizeof (tabptr->zone_patch_id))) != Z_OK) {
4455 		handle->zone_dh_cur = handle->zone_dh_top;
4456 		return (err);
4457 	}
4458 
4459 	handle->zone_dh_cur = cur->next;
4460 	return (Z_OK);
4461 }
4462 
4463 int
4464 zonecfg_endpatchent(zone_dochandle_t handle)
4465 {
4466 	return (zonecfg_endent(handle));
4467 }
4468 
4469 int
4470 zonecfg_setdevperment(zone_dochandle_t handle)
4471 {
4472 	return (zonecfg_setent(handle));
4473 }
4474 
4475 int
4476 zonecfg_getdevperment(zone_dochandle_t handle, struct zone_devpermtab *tabptr)
4477 {
4478 	xmlNodePtr cur;
4479 	int err;
4480 	char buf[128];
4481 
4482 	tabptr->zone_devperm_acl = NULL;
4483 
4484 	if (handle == NULL)
4485 		return (Z_INVAL);
4486 
4487 	if ((cur = handle->zone_dh_cur) == NULL)
4488 		return (Z_NO_ENTRY);
4489 
4490 	for (; cur != NULL; cur = cur->next)
4491 		if (!xmlStrcmp(cur->name, DTD_ELEM_DEV_PERM))
4492 			break;
4493 	if (cur == NULL) {
4494 		handle->zone_dh_cur = handle->zone_dh_top;
4495 		return (Z_NO_ENTRY);
4496 	}
4497 
4498 	if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_devperm_name,
4499 	    sizeof (tabptr->zone_devperm_name))) != Z_OK) {
4500 		handle->zone_dh_cur = handle->zone_dh_top;
4501 		return (err);
4502 	}
4503 
4504 	if ((err = fetchprop(cur, DTD_ATTR_UID, buf, sizeof (buf))) != Z_OK) {
4505 		handle->zone_dh_cur = handle->zone_dh_top;
4506 		return (err);
4507 	}
4508 	tabptr->zone_devperm_uid = (uid_t)atol(buf);
4509 
4510 	if ((err = fetchprop(cur, DTD_ATTR_GID, buf, sizeof (buf))) != Z_OK) {
4511 		handle->zone_dh_cur = handle->zone_dh_top;
4512 		return (err);
4513 	}
4514 	tabptr->zone_devperm_gid = (gid_t)atol(buf);
4515 
4516 	if ((err = fetchprop(cur, DTD_ATTR_MODE, buf, sizeof (buf))) != Z_OK) {
4517 		handle->zone_dh_cur = handle->zone_dh_top;
4518 		return (err);
4519 	}
4520 	tabptr->zone_devperm_mode = (mode_t)strtol(buf, (char **)NULL, 8);
4521 
4522 	if ((err = fetch_alloc_prop(cur, DTD_ATTR_ACL,
4523 	    &(tabptr->zone_devperm_acl))) != Z_OK) {
4524 		handle->zone_dh_cur = handle->zone_dh_top;
4525 		return (err);
4526 	}
4527 
4528 	handle->zone_dh_cur = cur->next;
4529 	return (Z_OK);
4530 }
4531 
4532 int
4533 zonecfg_enddevperment(zone_dochandle_t handle)
4534 {
4535 	return (zonecfg_endent(handle));
4536 }
4537 
4538 /*
4539  * Process a list of pkgs from an entry in the contents file, adding each pkg
4540  * name to the list of pkgs.
4541  *
4542  * It is possible for the pkg name to be preceeded by a special character
4543  * which indicates some bookkeeping information for pkging.  Check if the
4544  * first char is not an Alpha char.  If so, skip over it.
4545  */
4546 static int
4547 add_pkg_list(char *lastp, char ***plist, int *pcnt)
4548 {
4549 	char	*p;
4550 	int	pkg_cnt = *pcnt;
4551 	char	**pkgs = *plist;
4552 	int	res = Z_OK;
4553 
4554 	while ((p = strtok_r(NULL, " ", &lastp)) != NULL) {
4555 		char	**tmpp;
4556 		int	i;
4557 
4558 		/* skip over any special pkg bookkeeping char */
4559 		if (!isalpha(*p))
4560 			p++;
4561 
4562 		/* Check if the pkg is already in the list */
4563 		for (i = 0; i < pkg_cnt; i++) {
4564 			if (strcmp(p, pkgs[i]) == 0)
4565 				break;
4566 		}
4567 
4568 		if (i < pkg_cnt)
4569 			continue;
4570 
4571 		/* The pkg is not in the list; add it. */
4572 		if ((tmpp = (char **)realloc(pkgs,
4573 		    sizeof (char *) * (pkg_cnt + 1))) == NULL) {
4574 			res = Z_NOMEM;
4575 			break;
4576 		}
4577 		pkgs = tmpp;
4578 
4579 		if ((pkgs[pkg_cnt] = strdup(p)) == NULL) {
4580 			res = Z_NOMEM;
4581 			break;
4582 		}
4583 		pkg_cnt++;
4584 	}
4585 
4586 	*plist = pkgs;
4587 	*pcnt = pkg_cnt;
4588 
4589 	return (res);
4590 }
4591 
4592 /*
4593  * Process an entry from the contents file (type "directory") and if the
4594  * directory path is in the list of paths, add the associated list of pkgs
4595  * to the pkg list.  The input parameter "entry" will be broken up by
4596  * the parser within this function so its value will be modified when this
4597  * function exits.
4598  *
4599  * The entries we are looking for will look something like:
4600  *	/usr d none 0755 root sys SUNWctpls SUNWidnl SUNWlibCf ....
4601  */
4602 static int
4603 get_path_pkgs(char *entry, char **paths, int cnt, char ***pkgs, int *pkg_cnt)
4604 {
4605 	char	*f1;
4606 	char	*f2;
4607 	char	*lastp;
4608 	int	i;
4609 	int	res = Z_OK;
4610 
4611 	if ((f1 = strtok_r(entry, " ", &lastp)) == NULL ||
4612 	    (f2 = strtok_r(NULL, " ", &lastp)) == NULL || strcmp(f2, "d") != 0)
4613 		return (Z_OK);
4614 
4615 	/* Check if this directory entry is in the list of paths. */
4616 	for (i = 0; i < cnt; i++) {
4617 		if (fnmatch(paths[i], f1, FNM_PATHNAME) == 0) {
4618 			/*
4619 			 * We do want the pkgs for this path.  First, skip
4620 			 * over the next 4 fields in the entry so that we call
4621 			 * add_pkg_list starting with the pkg names.
4622 			 */
4623 			int j;
4624 			char	*nlp;
4625 
4626 			for (j = 0; j < 4 &&
4627 			    strtok_r(NULL, " ", &lastp) != NULL; j++);
4628 			/*
4629 			 * If there are < 4 fields this entry is corrupt,
4630 			 * just skip it.
4631 			 */
4632 			if (j < 4)
4633 				return (Z_OK);
4634 
4635 			/* strip newline from the line */
4636 			nlp = (lastp + strlen(lastp) - 1);
4637 			if (*nlp == '\n')
4638 				*nlp = '\0';
4639 
4640 			res = add_pkg_list(lastp, pkgs, pkg_cnt);
4641 			break;
4642 		}
4643 	}
4644 
4645 	return (res);
4646 }
4647 
4648 /*
4649  * Read an entry from a pkginfo or contents file.  Some of these lines can
4650  * either be arbitrarily long or be continued by a backslash at the end of
4651  * the line.  This function coalesces lines that are longer than the read
4652  * buffer, and lines that are continued, into one buffer which is returned.
4653  * The caller must free this memory.  NULL is returned when we hit EOF or
4654  * if we run out of memory (errno is set to ENOMEM).
4655  */
4656 static char *
4657 read_pkg_data(FILE *fp)
4658 {
4659 	char *start;
4660 	char *inp;
4661 	char *p;
4662 	int char_cnt = 0;
4663 
4664 	errno = 0;
4665 	if ((start = (char *)malloc(PKGINFO_RD_LEN)) == NULL) {
4666 		errno = ENOMEM;
4667 		return (NULL);
4668 	}
4669 
4670 	inp = start;
4671 	while ((p = fgets(inp, PKGINFO_RD_LEN, fp)) != NULL) {
4672 		int len;
4673 
4674 		len = strlen(inp);
4675 		if (inp[len - 1] == '\n' &&
4676 		    (len == 1 || inp[len - 2] != '\\')) {
4677 			char_cnt = len;
4678 			break;
4679 		}
4680 
4681 		if (inp[len - 2] == '\\')
4682 			char_cnt += len - 2;
4683 		else
4684 			char_cnt += PKGINFO_RD_LEN - 1;
4685 
4686 		if ((p = realloc(start, char_cnt + PKGINFO_RD_LEN)) == NULL) {
4687 			errno = ENOMEM;
4688 			break;
4689 		}
4690 
4691 		start = p;
4692 		inp = start + char_cnt;
4693 	}
4694 
4695 	if (errno == ENOMEM || (p == NULL && char_cnt == 0)) {
4696 		free(start);
4697 		start = NULL;
4698 	}
4699 
4700 	return (start);
4701 }
4702 
4703 static void
4704 free_ipd_pkgs(char **pkgs, int cnt)
4705 {
4706 	int i;
4707 
4708 	for (i = 0; i < cnt; i++)
4709 		free(pkgs[i]);
4710 	free(pkgs);
4711 }
4712 
4713 /*
4714  * Get the list of inherited-pkg-dirs (ipd) for the zone and then get the
4715  * list of pkgs that deliver into those dirs.
4716  */
4717 static int
4718 get_ipd_pkgs(zone_dochandle_t handle, char ***pkg_list, int *cnt)
4719 {
4720 	int	res;
4721 	struct zone_fstab fstab;
4722 	int	ipd_cnt = 0;
4723 	char	**ipds = NULL;
4724 	int	pkg_cnt = 0;
4725 	char	**pkgs = NULL;
4726 	int	i;
4727 
4728 	if ((res = zonecfg_setipdent(handle)) != Z_OK)
4729 		return (res);
4730 
4731 	while (zonecfg_getipdent(handle, &fstab) == Z_OK) {
4732 		char	**p;
4733 		int	len;
4734 
4735 		if ((p = (char **)realloc(ipds,
4736 		    sizeof (char *) * (ipd_cnt + 2))) == NULL) {
4737 			res = Z_NOMEM;
4738 			break;
4739 		}
4740 		ipds = p;
4741 
4742 		if ((ipds[ipd_cnt] = strdup(fstab.zone_fs_dir)) == NULL) {
4743 			res = Z_NOMEM;
4744 			break;
4745 		}
4746 		ipd_cnt++;
4747 
4748 		len = strlen(fstab.zone_fs_dir) + 3;
4749 		if ((ipds[ipd_cnt] = malloc(len)) == NULL) {
4750 			res = Z_NOMEM;
4751 			break;
4752 		}
4753 
4754 		(void) snprintf(ipds[ipd_cnt], len, "%s/*", fstab.zone_fs_dir);
4755 		ipd_cnt++;
4756 	}
4757 
4758 	(void) zonecfg_endipdent(handle);
4759 
4760 	if (res != Z_OK) {
4761 		for (i = 0; i < ipd_cnt; i++)
4762 			free(ipds[i]);
4763 		free(ipds);
4764 		return (res);
4765 	}
4766 
4767 	/* We only have to process the contents file if we have ipds. */
4768 	if (ipd_cnt > 0) {
4769 		FILE	*fp;
4770 
4771 		if ((fp = fopen(CONTENTS_FILE, "r")) != NULL) {
4772 			char	*buf;
4773 
4774 			while ((buf = read_pkg_data(fp)) != NULL) {
4775 				res = get_path_pkgs(buf, ipds, ipd_cnt, &pkgs,
4776 				    &pkg_cnt);
4777 				free(buf);
4778 				if (res != Z_OK)
4779 					break;
4780 			}
4781 
4782 			(void) fclose(fp);
4783 		}
4784 	}
4785 
4786 	for (i = 0; i < ipd_cnt; i++)
4787 		free(ipds[i]);
4788 	free(ipds);
4789 
4790 	if (res != Z_OK) {
4791 		free_ipd_pkgs(pkgs, pkg_cnt);
4792 	} else {
4793 		*pkg_list = pkgs;
4794 		*cnt = pkg_cnt;
4795 	}
4796 
4797 	return (res);
4798 }
4799 
4800 /*
4801  * Return true if pkg_name is in the list of pkgs that deliver into an
4802  * inherited pkg directory for the zone.
4803  */
4804 static boolean_t
4805 dir_pkg(char *pkg_name, char **pkg_list, int cnt)
4806 {
4807 	int i;
4808 
4809 	for (i = 0; i < cnt; i++) {
4810 		if (strcmp(pkg_name, pkg_list[i]) == 0)
4811 			return (B_TRUE);
4812 	}
4813 
4814 	return (B_FALSE);
4815 }
4816 
4817 /*
4818  * Start by adding the patch to the sw inventory on the handle.
4819  *
4820  * The info parameter will be the portion of the PATCH_INFO_ entry following
4821  * the '='.  For example:
4822  * Installed: Wed Dec  7 07:13:51 PST 2005 From: mum Obsoletes: 120777-03 \
4823  *	121087-02 119108-07 Requires: 119575-02 119255-06 Incompatibles:
4824  *
4825  * A backed out patch will have an info line of "backed out\n".  We should
4826  * skip these patches.
4827  *
4828  * We also want to add the Obsolete and Incompatible patches to the
4829  * sw inventory description of this patch.
4830  */
4831 static int
4832 add_patch(zone_dochandle_t handle, char *patch, char *info)
4833 {
4834 	xmlNodePtr	node;
4835 	xmlNodePtr	cur;
4836 	int		err;
4837 	char		*p;
4838 	char		*lastp;
4839 	boolean_t	add_info = B_FALSE;
4840 	boolean_t	obsolete;
4841 
4842 	if (strcmp(info, "backed out\n") == 0)
4843 		return (Z_OK);
4844 
4845 	if ((err = operation_prep(handle)) != Z_OK)
4846 		return (err);
4847 
4848 	cur = handle->zone_dh_cur;
4849 	node = xmlNewTextChild(cur, NULL, DTD_ELEM_PATCH, NULL);
4850 	if ((err = newprop(node, DTD_ATTR_ID, patch)) != Z_OK)
4851 		return (err);
4852 
4853 	/*
4854 	 * Start with the first token.  This will probably be "Installed:".
4855 	 * If we can't tokenize this entry, just return.
4856 	 */
4857 	if ((p = strtok_r(info, " ", &lastp)) == NULL)
4858 		return (Z_OK);
4859 
4860 	do {
4861 		xmlNodePtr new_node;
4862 		char	*nlp;
4863 
4864 		if (strcmp(p, "Installed:") == 0 ||
4865 		    strcmp(p, "Requires:") == 0 ||
4866 		    strcmp(p, "From:") == 0) {
4867 			add_info = B_FALSE;
4868 			continue;
4869 		} else if (strcmp(p, "Obsoletes:") == 0) {
4870 			obsolete = B_TRUE;
4871 			add_info = B_TRUE;
4872 			continue;
4873 		} else if (strcmp(p, "Incompatibles:") == 0) {
4874 			obsolete = B_FALSE;
4875 			add_info = B_TRUE;
4876 			continue;
4877 		}
4878 
4879 		if (!add_info)
4880 			continue;
4881 
4882 		/* strip newline from last patch in the line */
4883 		nlp = (p + strlen(p) - 1);
4884 		if (*nlp == '\n')
4885 			*nlp = '\0';
4886 
4887 		if (obsolete)
4888 			new_node = xmlNewTextChild(node, NULL,
4889 			    DTD_ELEM_OBSOLETES, NULL);
4890 		else
4891 			new_node = xmlNewTextChild(node, NULL,
4892 			    DTD_ELEM_INCOMPATIBLE, NULL);
4893 
4894 		if ((err = newprop(new_node, DTD_ATTR_ID, p)) != Z_OK)
4895 			return (err);
4896 
4897 	} while ((p = strtok_r(NULL, " ", &lastp)) != NULL);
4898 
4899 	return (Z_OK);
4900 }
4901 
4902 static boolean_t
4903 unique_patch(zone_dochandle_t handle, char *patch)
4904 {
4905 	xmlNodePtr	cur;
4906 	char		id[MAXNAMELEN];
4907 
4908 	cur = xmlDocGetRootElement(handle->zone_dh_doc);
4909 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
4910 		if (xmlStrcmp(cur->name, DTD_ELEM_PATCH) == 0) {
4911 			if (fetchprop(cur, DTD_ATTR_ID, id, sizeof (id))
4912 			    != Z_OK)
4913 				continue;
4914 
4915 			if (strcmp(patch, id) == 0)
4916 				return (B_FALSE);
4917 		}
4918 	}
4919 
4920 	return (B_TRUE);
4921 }
4922 
4923 /*
4924  * Add the unique patches associated with this pkg to the sw inventory on the
4925  * handle.
4926  *
4927  * We are processing entries of the form:
4928  * PATCH_INFO_121454-02=Installed: Wed Dec  7 07:13:51 PST 2005 From: mum \
4929  *	Obsoletes: 120777-03 121087-02 119108-07 Requires: 119575-02 \
4930  *	119255-06 Incompatibles:
4931  *
4932  */
4933 static int
4934 add_patches(zone_dochandle_t handle, struct zone_pkginfo *infop)
4935 {
4936 	int i;
4937 	int res = Z_OK;
4938 
4939 	for (i = 0; i < infop->zpi_patch_cnt; i++) {
4940 		char *p, *ep;
4941 
4942 		if (strlen(infop->zpi_patchinfo[i]) < (sizeof (PATCHINFO) - 1))
4943 			continue;
4944 
4945 		/* Skip over "PATCH_INFO_" to get the patch id. */
4946 		p = infop->zpi_patchinfo[i] + sizeof (PATCHINFO) - 1;
4947 		if ((ep = strchr(p, '=')) == NULL)
4948 			continue;
4949 
4950 		*ep = '\0';
4951 		if (unique_patch(handle, p))
4952 			if ((res = add_patch(handle, p, ep + 1)) != Z_OK)
4953 				break;
4954 	}
4955 
4956 	return (res);
4957 }
4958 
4959 /*
4960  * Add the pkg to the sw inventory on the handle.
4961  */
4962 static int
4963 add_pkg(zone_dochandle_t handle, char *name, char *version)
4964 {
4965 	xmlNodePtr newnode;
4966 	xmlNodePtr cur;
4967 	int err;
4968 
4969 	if ((err = operation_prep(handle)) != Z_OK)
4970 		return (err);
4971 
4972 	cur = handle->zone_dh_cur;
4973 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_PACKAGE, NULL);
4974 	if ((err = newprop(newnode, DTD_ATTR_NAME, name)) != Z_OK)
4975 		return (err);
4976 	if ((err = newprop(newnode, DTD_ATTR_VERSION, version)) != Z_OK)
4977 		return (err);
4978 	return (Z_OK);
4979 }
4980 
4981 static void
4982 free_pkginfo(struct zone_pkginfo *infop)
4983 {
4984 	free(infop->zpi_version);
4985 	if (infop->zpi_patch_cnt > 0) {
4986 		int i;
4987 
4988 		for (i = 0; i < infop->zpi_patch_cnt; i++)
4989 			free(infop->zpi_patchinfo[i]);
4990 		free(infop->zpi_patchinfo);
4991 	}
4992 }
4993 
4994 /*
4995  * Read the pkginfo file and populate the structure with the data we need
4996  * from this pkg for a sw inventory.
4997  */
4998 static int
4999 get_pkginfo(char *pkginfo, struct zone_pkginfo *infop)
5000 {
5001 	FILE	*fp;
5002 	char	*buf;
5003 	int	err = 0;
5004 
5005 	infop->zpi_all_zones = B_FALSE;
5006 	infop->zpi_this_zone = B_FALSE;
5007 	infop->zpi_version = NULL;
5008 	infop->zpi_patch_cnt = 0;
5009 	infop->zpi_patchinfo = NULL;
5010 
5011 	if ((fp = fopen(pkginfo, "r")) == NULL)
5012 		return (errno);
5013 
5014 	while ((buf = read_pkg_data(fp)) != NULL) {
5015 		if (strncmp(buf, VERSION, sizeof (VERSION) - 1) == 0) {
5016 			int len;
5017 
5018 			if ((infop->zpi_version =
5019 			    strdup(buf + sizeof (VERSION) - 1)) == NULL) {
5020 				err = ENOMEM;
5021 				break;
5022 			}
5023 
5024 			/* remove trailing newline */
5025 			len = strlen(infop->zpi_version);
5026 			*(infop->zpi_version + len - 1) = 0;
5027 
5028 		} else if (strcmp(buf, ALL_ZONES) == 0) {
5029 			infop->zpi_all_zones = B_TRUE;
5030 
5031 		} else if (strcmp(buf, THIS_ZONE) == 0) {
5032 			infop->zpi_this_zone = B_TRUE;
5033 
5034 		} else if (strncmp(buf, PATCHINFO, sizeof (PATCHINFO) - 1)
5035 		    == 0) {
5036 			char **p;
5037 
5038 			if ((p = (char **)realloc(infop->zpi_patchinfo,
5039 			    sizeof (char *) * (infop->zpi_patch_cnt + 1)))
5040 			    == NULL) {
5041 				err = ENOMEM;
5042 				break;
5043 			}
5044 			infop->zpi_patchinfo = p;
5045 
5046 			if ((infop->zpi_patchinfo[infop->zpi_patch_cnt] =
5047 			    strdup(buf)) == NULL) {
5048 				err = ENOMEM;
5049 				break;
5050 			}
5051 			infop->zpi_patch_cnt++;
5052 		}
5053 
5054 		free(buf);
5055 	}
5056 
5057 	free(buf);
5058 
5059 	if (errno == ENOMEM) {
5060 		err = ENOMEM;
5061 		/* Clean up anything we did manage to allocate. */
5062 		free_pkginfo(infop);
5063 	}
5064 
5065 	(void) fclose(fp);
5066 
5067 	return (err);
5068 }
5069 
5070 /*
5071  * Take a software inventory of the global zone.  We need to get the set of
5072  * packages and patches that are on the global zone that the specified
5073  * non-global zone depends on.  The packages we need in the inventory are:
5074  *
5075  * - skip the package if SUNW_PKG_THISZONE is 'true'
5076  * otherwise,
5077  * - add the package if
5078  * a) SUNW_PKG_ALLZONES is 'true',
5079  * or
5080  * b) any file delivered by the package is in a file system that is inherited
5081  * from the global zone.
5082  * If the zone does not inherit any file systems (whole root)
5083  * then (b) will be skipped.
5084  *
5085  * For each of the packages that is being added to the inventory, we will also
5086  * add all of the associated, unique patches to the inventory.
5087  */
5088 static int
5089 zonecfg_sw_inventory(zone_dochandle_t handle)
5090 {
5091 	char		pkginfo[MAXPATHLEN];
5092 	int		res;
5093 	struct dirent	*dp;
5094 	DIR		*dirp;
5095 	struct stat	buf;
5096 	struct zone_pkginfo	info;
5097 	int		pkg_cnt = 0;
5098 	char		**pkgs = NULL;
5099 
5100 	if ((res = get_ipd_pkgs(handle, &pkgs, &pkg_cnt)) != Z_OK)
5101 		return (res);
5102 
5103 	if ((dirp = opendir(PKG_PATH)) == NULL) {
5104 		free_ipd_pkgs(pkgs, pkg_cnt);
5105 		return (Z_OK);
5106 	}
5107 
5108 	while ((dp = readdir(dirp)) != (struct dirent *)0) {
5109 		if (strcmp(dp->d_name, ".") == 0 ||
5110 		    strcmp(dp->d_name, "..") == 0)
5111 			continue;
5112 
5113 		(void) snprintf(pkginfo, sizeof (pkginfo), "%s/%s/pkginfo",
5114 		    PKG_PATH, dp->d_name);
5115 
5116 		if (stat(pkginfo, &buf) == -1 || !S_ISREG(buf.st_mode))
5117 			continue;
5118 
5119 		if (get_pkginfo(pkginfo, &info) != 0) {
5120 			res = Z_NOMEM;
5121 			break;
5122 		}
5123 
5124 		if (!info.zpi_this_zone &&
5125 		    (info.zpi_all_zones ||
5126 		    dir_pkg(dp->d_name, pkgs, pkg_cnt))) {
5127 			if ((res = add_pkg(handle, dp->d_name,
5128 			    info.zpi_version)) == Z_OK) {
5129 				if (info.zpi_patch_cnt > 0)
5130 					res = add_patches(handle, &info);
5131 			}
5132 		}
5133 
5134 		free_pkginfo(&info);
5135 
5136 		if (res != Z_OK)
5137 			break;
5138 	}
5139 
5140 	(void) closedir(dirp);
5141 
5142 	free_ipd_pkgs(pkgs, pkg_cnt);
5143 
5144 	if (res == Z_OK)
5145 		handle->zone_dh_sw_inv = B_TRUE;
5146 
5147 	return (res);
5148 }
5149 
5150 /*
5151  * zonecfg_devwalk call-back function used during detach to generate the
5152  * dev info in the manifest.
5153  */
5154 static int
5155 get_detach_dev_entry(const char *name, uid_t uid, gid_t gid, mode_t mode,
5156     const char *acl, void *hdl)
5157 {
5158 	zone_dochandle_t handle = (zone_dochandle_t)hdl;
5159 	xmlNodePtr newnode;
5160 	xmlNodePtr cur;
5161 	int err;
5162 	char buf[128];
5163 
5164 	if ((err = operation_prep(handle)) != Z_OK)
5165 		return (err);
5166 
5167 	cur = handle->zone_dh_cur;
5168 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DEV_PERM, NULL);
5169 	if ((err = newprop(newnode, DTD_ATTR_NAME, (char *)name)) != Z_OK)
5170 		return (err);
5171 	(void) snprintf(buf, sizeof (buf), "%lu", uid);
5172 	if ((err = newprop(newnode, DTD_ATTR_UID, buf)) != Z_OK)
5173 		return (err);
5174 	(void) snprintf(buf, sizeof (buf), "%lu", gid);
5175 	if ((err = newprop(newnode, DTD_ATTR_GID, buf)) != Z_OK)
5176 		return (err);
5177 	(void) snprintf(buf, sizeof (buf), "%o", mode);
5178 	if ((err = newprop(newnode, DTD_ATTR_MODE, buf)) != Z_OK)
5179 		return (err);
5180 	if ((err = newprop(newnode, DTD_ATTR_ACL, (char *)acl)) != Z_OK)
5181 		return (err);
5182 	return (Z_OK);
5183 }
5184 
5185 /*
5186  * Get the information required to support detaching a zone.  This is
5187  * called on the source system when detaching (the detaching parameter should
5188  * be set to true) and on the destination system before attaching (the
5189  * detaching parameter should be false).
5190  *
5191  * For native Solaris zones, the detach/attach process involves validating
5192  * that the software on the global zone can support the zone when we attach.
5193  * To do this we take a software inventory of the global zone.  We also
5194  * have to keep track of the device configuration so that we can properly
5195  * recreate it on the destination.
5196  */
5197 int
5198 zonecfg_get_detach_info(zone_dochandle_t handle, boolean_t detaching)
5199 {
5200 	int		res;
5201 
5202 	if ((res = zonecfg_sw_inventory(handle)) != Z_OK)
5203 		return (res);
5204 
5205 	if (detaching)
5206 		res = zonecfg_devwalk(handle, get_detach_dev_entry, handle);
5207 
5208 	return (res);
5209 }
5210