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