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