xref: /onnv-gate/usr/src/lib/libzonecfg/common/getzoneent.c (revision 12706:29969138b2f7)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51422Scarlsonj  * Common Development and Distribution License (the "License").
61422Scarlsonj  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*12706SPhaniram.Krishnamurthy@Sun.COM  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate  * This module contains functions used for reading and writing the index file.
280Sstevel@tonic-gate  * setzoneent() opens the file.  getzoneent() parses the file, doing the usual
290Sstevel@tonic-gate  * skipping of comment lines, etc., and using gettok() to deal with the ":"
300Sstevel@tonic-gate  * delimiters.  endzoneent() closes the file.  putzoneent() updates the file,
310Sstevel@tonic-gate  * adding, deleting or modifying lines, locking and unlocking appropriately.
320Sstevel@tonic-gate  */
330Sstevel@tonic-gate 
340Sstevel@tonic-gate #include <stdlib.h>
350Sstevel@tonic-gate #include <string.h>
360Sstevel@tonic-gate #include <errno.h>
370Sstevel@tonic-gate #include <libzonecfg.h>
380Sstevel@tonic-gate #include <unistd.h>
390Sstevel@tonic-gate #include <fcntl.h>
400Sstevel@tonic-gate #include <sys/stat.h>
410Sstevel@tonic-gate #include <assert.h>
42766Scarlsonj #include <uuid/uuid.h>
430Sstevel@tonic-gate #include "zonecfg_impl.h"
440Sstevel@tonic-gate 
450Sstevel@tonic-gate 
460Sstevel@tonic-gate #define	_PATH_TMPFILE	ZONE_CONFIG_ROOT "/zonecfg.XXXXXX"
470Sstevel@tonic-gate 
480Sstevel@tonic-gate /*
490Sstevel@tonic-gate  * gettok() is a helper function for parsing the index file, used to split
500Sstevel@tonic-gate  * the lines by their ":" delimiters.  Note that an entry may contain a ":"
510Sstevel@tonic-gate  * inside double quotes; this should only affect the zonepath, as zone names
520Sstevel@tonic-gate  * do not allow such characters, and zone states do not have them either.
530Sstevel@tonic-gate  * Same with double-quotes themselves: they are not allowed in zone names,
540Sstevel@tonic-gate  * and do not occur in zone states, and in theory should never occur in a
550Sstevel@tonic-gate  * zonepath since zonecfg does not support a method for escaping them.
56766Scarlsonj  *
57766Scarlsonj  * It never returns NULL.
580Sstevel@tonic-gate  */
590Sstevel@tonic-gate 
600Sstevel@tonic-gate static char *
gettok(char ** cpp)610Sstevel@tonic-gate gettok(char **cpp)
620Sstevel@tonic-gate {
630Sstevel@tonic-gate 	char *cp = *cpp, *retv;
640Sstevel@tonic-gate 	boolean_t quoted = B_FALSE;
650Sstevel@tonic-gate 
660Sstevel@tonic-gate 	if (cp == NULL)
670Sstevel@tonic-gate 		return ("");
680Sstevel@tonic-gate 	if (*cp == '"') {
690Sstevel@tonic-gate 		quoted = B_TRUE;
700Sstevel@tonic-gate 		cp++;
710Sstevel@tonic-gate 	}
720Sstevel@tonic-gate 	retv = cp;
730Sstevel@tonic-gate 	if (quoted) {
740Sstevel@tonic-gate 		while (*cp != '\0' && *cp != '"')
750Sstevel@tonic-gate 			cp++;
760Sstevel@tonic-gate 		if (*cp == '"')
770Sstevel@tonic-gate 			*cp++ = '\0';
780Sstevel@tonic-gate 	}
790Sstevel@tonic-gate 	while (*cp != '\0' && *cp != ':')
800Sstevel@tonic-gate 		cp++;
810Sstevel@tonic-gate 	if (*cp == '\0') {
820Sstevel@tonic-gate 		*cpp = NULL;
830Sstevel@tonic-gate 	} else {
840Sstevel@tonic-gate 		*cp++ = '\0';
850Sstevel@tonic-gate 		*cpp = cp;
860Sstevel@tonic-gate 	}
870Sstevel@tonic-gate 	return (retv);
880Sstevel@tonic-gate }
890Sstevel@tonic-gate 
900Sstevel@tonic-gate char *
getzoneent(FILE * cookie)910Sstevel@tonic-gate getzoneent(FILE *cookie)
920Sstevel@tonic-gate {
930Sstevel@tonic-gate 	struct zoneent *ze;
940Sstevel@tonic-gate 	char *name;
950Sstevel@tonic-gate 
960Sstevel@tonic-gate 	if ((ze = getzoneent_private(cookie)) == NULL)
970Sstevel@tonic-gate 		return (NULL);
980Sstevel@tonic-gate 	name = strdup(ze->zone_name);
990Sstevel@tonic-gate 	free(ze);
1000Sstevel@tonic-gate 	return (name);
1010Sstevel@tonic-gate }
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate struct zoneent *
getzoneent_private(FILE * cookie)1040Sstevel@tonic-gate getzoneent_private(FILE *cookie)
1050Sstevel@tonic-gate {
1060Sstevel@tonic-gate 	char *cp, buf[MAX_INDEX_LEN], *p;
1070Sstevel@tonic-gate 	struct zoneent *ze;
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate 	if (cookie == NULL)
1100Sstevel@tonic-gate 		return (NULL);
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate 	if ((ze = malloc(sizeof (struct zoneent))) == NULL)
1130Sstevel@tonic-gate 		return (NULL);
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate 	for (;;) {
1160Sstevel@tonic-gate 		if (fgets(buf, sizeof (buf), cookie) == NULL) {
1170Sstevel@tonic-gate 			free(ze);
1180Sstevel@tonic-gate 			return (NULL);
1190Sstevel@tonic-gate 		}
1200Sstevel@tonic-gate 		if ((cp = strpbrk(buf, "\r\n")) == NULL) {
1210Sstevel@tonic-gate 			/* this represents a line that's too long */
1220Sstevel@tonic-gate 			continue;
1230Sstevel@tonic-gate 		}
1240Sstevel@tonic-gate 		*cp = '\0';
1250Sstevel@tonic-gate 		cp = buf;
1260Sstevel@tonic-gate 		if (*cp == '#') {
1270Sstevel@tonic-gate 			/* skip comment lines */
1280Sstevel@tonic-gate 			continue;
1290Sstevel@tonic-gate 		}
1300Sstevel@tonic-gate 		p = gettok(&cp);
131766Scarlsonj 		if (*p == '\0' || strlen(p) >= ZONENAME_MAX) {
1320Sstevel@tonic-gate 			/*
1330Sstevel@tonic-gate 			 * empty or very long zone names are not allowed
1340Sstevel@tonic-gate 			 */
1350Sstevel@tonic-gate 			continue;
1360Sstevel@tonic-gate 		}
1370Sstevel@tonic-gate 		(void) strlcpy(ze->zone_name, p, ZONENAME_MAX);
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate 		p = gettok(&cp);
140766Scarlsonj 		if (*p == '\0') {
1410Sstevel@tonic-gate 			/* state field should not be empty */
1420Sstevel@tonic-gate 			continue;
1430Sstevel@tonic-gate 		}
1440Sstevel@tonic-gate 		errno = 0;
1450Sstevel@tonic-gate 		if (strcmp(p, ZONE_STATE_STR_CONFIGURED) == 0) {
1460Sstevel@tonic-gate 			ze->zone_state = ZONE_STATE_CONFIGURED;
1470Sstevel@tonic-gate 		} else if (strcmp(p, ZONE_STATE_STR_INCOMPLETE) == 0) {
1480Sstevel@tonic-gate 			ze->zone_state = ZONE_STATE_INCOMPLETE;
1490Sstevel@tonic-gate 		} else if (strcmp(p, ZONE_STATE_STR_INSTALLED) == 0) {
1500Sstevel@tonic-gate 			ze->zone_state = ZONE_STATE_INSTALLED;
151766Scarlsonj 		} else {
1520Sstevel@tonic-gate 			continue;
153766Scarlsonj 		}
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate 		p = gettok(&cp);
156766Scarlsonj 		if (strlen(p) >= MAXPATHLEN) {
1570Sstevel@tonic-gate 			/* very long paths are not allowed */
1580Sstevel@tonic-gate 			continue;
1590Sstevel@tonic-gate 		}
1600Sstevel@tonic-gate 		(void) strlcpy(ze->zone_path, p, MAXPATHLEN);
1610Sstevel@tonic-gate 
162766Scarlsonj 		p = gettok(&cp);
163766Scarlsonj 		if (uuid_parse(p, ze->zone_uuid) == -1)
164766Scarlsonj 			uuid_clear(ze->zone_uuid);
165766Scarlsonj 
1660Sstevel@tonic-gate 		break;
1670Sstevel@tonic-gate 	}
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate 	return (ze);
1700Sstevel@tonic-gate }
1710Sstevel@tonic-gate 
172766Scarlsonj static boolean_t
get_index_path(char * path)173766Scarlsonj get_index_path(char *path)
174766Scarlsonj {
175766Scarlsonj 	return (snprintf(path, MAXPATHLEN, "%s%s", zonecfg_root,
176766Scarlsonj 	    ZONE_INDEX_FILE) < MAXPATHLEN);
177766Scarlsonj }
178766Scarlsonj 
1790Sstevel@tonic-gate FILE *
setzoneent(void)1800Sstevel@tonic-gate setzoneent(void)
1810Sstevel@tonic-gate {
182766Scarlsonj 	char path[MAXPATHLEN];
183766Scarlsonj 
184766Scarlsonj 	if (!get_index_path(path)) {
185766Scarlsonj 		errno = EINVAL;
186766Scarlsonj 		return (NULL);
187766Scarlsonj 	}
188766Scarlsonj 	return (fopen(path, "r"));
1890Sstevel@tonic-gate }
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate void
endzoneent(FILE * cookie)1920Sstevel@tonic-gate endzoneent(FILE *cookie)
1930Sstevel@tonic-gate {
1940Sstevel@tonic-gate 	if (cookie != NULL)
1950Sstevel@tonic-gate 		(void) fclose(cookie);
1960Sstevel@tonic-gate }
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate static int
lock_index_file(void)199766Scarlsonj lock_index_file(void)
2000Sstevel@tonic-gate {
201766Scarlsonj 	int lock_fd;
2020Sstevel@tonic-gate 	struct flock lock;
203766Scarlsonj 	char path[MAXPATHLEN];
2040Sstevel@tonic-gate 
205766Scarlsonj 	if (snprintf(path, sizeof (path), "%s%s", zonecfg_root,
206766Scarlsonj 	    ZONE_INDEX_LOCK_DIR) >= sizeof (path))
207766Scarlsonj 		return (-1);
208766Scarlsonj 	if ((mkdir(path, S_IRWXU) == -1) && errno != EEXIST)
209766Scarlsonj 		return (-1);
210766Scarlsonj 	if (strlcat(path, ZONE_INDEX_LOCK_FILE, sizeof (path)) >=
211766Scarlsonj 	    sizeof (path))
212766Scarlsonj 		return (-1);
213766Scarlsonj 	lock_fd = open(path, O_CREAT|O_RDWR, 0644);
214766Scarlsonj 	if (lock_fd == -1)
215766Scarlsonj 		return (-1);
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	lock.l_type = F_WRLCK;
2180Sstevel@tonic-gate 	lock.l_whence = SEEK_SET;
2190Sstevel@tonic-gate 	lock.l_start = 0;
2200Sstevel@tonic-gate 	lock.l_len = 0;
2210Sstevel@tonic-gate 
222766Scarlsonj 	if (fcntl(lock_fd, F_SETLKW, &lock) == -1) {
223766Scarlsonj 		(void) close(lock_fd);
224766Scarlsonj 		return (-1);
225766Scarlsonj 	}
2260Sstevel@tonic-gate 
227766Scarlsonj 	return (lock_fd);
2280Sstevel@tonic-gate }
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate static int
unlock_index_file(int lock_fd)2310Sstevel@tonic-gate unlock_index_file(int lock_fd)
2320Sstevel@tonic-gate {
2330Sstevel@tonic-gate 	struct flock lock;
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 	lock.l_type = F_UNLCK;
2360Sstevel@tonic-gate 	lock.l_whence = SEEK_SET;
2370Sstevel@tonic-gate 	lock.l_start = 0;
2380Sstevel@tonic-gate 	lock.l_len = 0;
2390Sstevel@tonic-gate 
2400Sstevel@tonic-gate 	if (fcntl(lock_fd, F_SETLK, &lock) == -1)
2410Sstevel@tonic-gate 		return (Z_UNLOCKING_FILE);
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate 	if (close(lock_fd) == -1)
2440Sstevel@tonic-gate 		return (Z_UNLOCKING_FILE);
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate 	return (Z_OK);
2470Sstevel@tonic-gate }
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate /*
2500Sstevel@tonic-gate  * This function adds or removes a zone name et al. to the index file.
2510Sstevel@tonic-gate  *
2520Sstevel@tonic-gate  * If ze->zone_state is < 0, it means leave the
2530Sstevel@tonic-gate  * existing value unchanged; this is only meaningful when operation ==
254565Sdp  * PZE_MODIFY (i.e., it's bad on PZE_ADD and a no-op on PZE_REMOVE).
2550Sstevel@tonic-gate  *
256565Sdp  * A zero-length ze->zone_path means leave the existing value
2570Sstevel@tonic-gate  * unchanged; this is only meaningful when operation == PZE_MODIFY
258565Sdp  * (i.e., it's bad on PZE_ADD and a no-op on PZE_REMOVE).
259565Sdp  *
260565Sdp  * A zero-length ze->zone_newname means leave the existing name
261565Sdp  * unchanged; otherwise the zone is renamed to zone_newname.  This is
262565Sdp  * only meaningful when operation == PZE_MODIFY.
2630Sstevel@tonic-gate  *
2640Sstevel@tonic-gate  * Locking and unlocking is done via the functions above.
2650Sstevel@tonic-gate  * The file itself is not modified in place; rather, a copy is made which
2660Sstevel@tonic-gate  * is modified, then the copy is atomically renamed back to the main file.
2670Sstevel@tonic-gate  */
2680Sstevel@tonic-gate int
putzoneent(struct zoneent * ze,zoneent_op_t operation)2690Sstevel@tonic-gate putzoneent(struct zoneent *ze, zoneent_op_t operation)
2700Sstevel@tonic-gate {
2710Sstevel@tonic-gate 	FILE *index_file, *tmp_file;
2721422Scarlsonj 	char *tmp_file_name, buf[MAX_INDEX_LEN];
2730Sstevel@tonic-gate 	int tmp_file_desc, lock_fd, err;
2741422Scarlsonj 	boolean_t exist, need_quotes;
2751422Scarlsonj 	char *cp;
276766Scarlsonj 	char path[MAXPATHLEN];
2771388Sgz161490 	char uuidstr[UUID_PRINTABLE_STRING_LENGTH];
2781422Scarlsonj 	size_t tlen, namelen;
2791422Scarlsonj 	const char *zone_name, *zone_state, *zone_path, *zone_uuid;
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 	assert(ze != NULL);
282*12706SPhaniram.Krishnamurthy@Sun.COM 
283*12706SPhaniram.Krishnamurthy@Sun.COM 	/*
284*12706SPhaniram.Krishnamurthy@Sun.COM 	 * Don't allow modification of Global Zone entry
285*12706SPhaniram.Krishnamurthy@Sun.COM 	 * in index file
286*12706SPhaniram.Krishnamurthy@Sun.COM 	 */
287*12706SPhaniram.Krishnamurthy@Sun.COM 	if ((operation == PZE_MODIFY) &&
288*12706SPhaniram.Krishnamurthy@Sun.COM 	    (strcmp(ze->zone_name, GLOBAL_ZONENAME) == 0)) {
289*12706SPhaniram.Krishnamurthy@Sun.COM 		return (Z_OK);
290*12706SPhaniram.Krishnamurthy@Sun.COM 	}
291*12706SPhaniram.Krishnamurthy@Sun.COM 
2920Sstevel@tonic-gate 	if (operation == PZE_ADD &&
2930Sstevel@tonic-gate 	    (ze->zone_state < 0 || strlen(ze->zone_path) == 0))
2940Sstevel@tonic-gate 		return (Z_INVAL);
295565Sdp 
296565Sdp 	if (operation != PZE_MODIFY && strlen(ze->zone_newname) != 0)
297565Sdp 		return (Z_INVAL);
298565Sdp 
299766Scarlsonj 	if ((lock_fd = lock_index_file()) == -1)
300766Scarlsonj 		return (Z_LOCKING_FILE);
301766Scarlsonj 
302766Scarlsonj 	/* using sizeof gives us room for the terminating NUL byte as well */
303766Scarlsonj 	tlen = sizeof (_PATH_TMPFILE) + strlen(zonecfg_root);
304766Scarlsonj 	tmp_file_name = malloc(tlen);
3050Sstevel@tonic-gate 	if (tmp_file_name == NULL) {
3060Sstevel@tonic-gate 		(void) unlock_index_file(lock_fd);
3070Sstevel@tonic-gate 		return (Z_NOMEM);
3080Sstevel@tonic-gate 	}
309766Scarlsonj 	(void) snprintf(tmp_file_name, tlen, "%s%s", zonecfg_root,
310766Scarlsonj 	    _PATH_TMPFILE);
311766Scarlsonj 
3120Sstevel@tonic-gate 	tmp_file_desc = mkstemp(tmp_file_name);
3130Sstevel@tonic-gate 	if (tmp_file_desc == -1) {
3140Sstevel@tonic-gate 		(void) unlink(tmp_file_name);
3150Sstevel@tonic-gate 		free(tmp_file_name);
3160Sstevel@tonic-gate 		(void) unlock_index_file(lock_fd);
3170Sstevel@tonic-gate 		return (Z_TEMP_FILE);
3180Sstevel@tonic-gate 	}
3192303Scarlsonj 	(void) fchmod(tmp_file_desc, ZONE_INDEX_MODE);
3202303Scarlsonj 	(void) fchown(tmp_file_desc, ZONE_INDEX_UID, ZONE_INDEX_GID);
3210Sstevel@tonic-gate 	if ((tmp_file = fdopen(tmp_file_desc, "w")) == NULL) {
3220Sstevel@tonic-gate 		(void) close(tmp_file_desc);
323766Scarlsonj 		err = Z_MISC_FS;
324766Scarlsonj 		goto error;
3250Sstevel@tonic-gate 	}
326766Scarlsonj 	if (!get_index_path(path)) {
327766Scarlsonj 		err = Z_MISC_FS;
328766Scarlsonj 		goto error;
329766Scarlsonj 	}
330766Scarlsonj 	if ((index_file = fopen(path, "r")) == NULL) {
331766Scarlsonj 		err = Z_MISC_FS;
332766Scarlsonj 		goto error;
3330Sstevel@tonic-gate 	}
3340Sstevel@tonic-gate 
3351422Scarlsonj 	exist = B_FALSE;
3361422Scarlsonj 	zone_name = ze->zone_name;
3371422Scarlsonj 	namelen = strlen(zone_name);
3380Sstevel@tonic-gate 	for (;;) {
3390Sstevel@tonic-gate 		if (fgets(buf, sizeof (buf), index_file) == NULL) {
3401422Scarlsonj 			if (operation == PZE_ADD && !exist) {
3411422Scarlsonj 				zone_state = zone_state_str(ze->zone_state);
3421422Scarlsonj 				zone_path = ze->zone_path;
3431422Scarlsonj 				zone_uuid = "";
3441422Scarlsonj 				goto add_entry;
3451422Scarlsonj 			}
3461422Scarlsonj 			/*
3471422Scarlsonj 			 * It's not considered an error to delete something
3481422Scarlsonj 			 * that doesn't exist, but we can't modify a missing
3491422Scarlsonj 			 * record.
3501422Scarlsonj 			 */
3511422Scarlsonj 			if (operation == PZE_MODIFY && !exist) {
3521422Scarlsonj 				err = Z_UPDATING_INDEX;
3531422Scarlsonj 				goto error;
3541422Scarlsonj 			}
3550Sstevel@tonic-gate 			break;
3560Sstevel@tonic-gate 		}
3571422Scarlsonj 
3581422Scarlsonj 		if (buf[0] == '#') {
3591422Scarlsonj 			/* skip and preserve comment lines */
3601422Scarlsonj 			(void) fputs(buf, tmp_file);
3611422Scarlsonj 			continue;
3621422Scarlsonj 		}
3631422Scarlsonj 
3641422Scarlsonj 		if (strncmp(buf, zone_name, namelen) != 0 ||
3651422Scarlsonj 		    buf[namelen] != ':') {
3661422Scarlsonj 			/* skip and preserve non-target lines */
3671422Scarlsonj 			(void) fputs(buf, tmp_file);
3681422Scarlsonj 			continue;
3691422Scarlsonj 		}
3700Sstevel@tonic-gate 
3710Sstevel@tonic-gate 		if ((cp = strpbrk(buf, "\r\n")) == NULL) {
3721422Scarlsonj 			/* this represents a line that's too long; delete */
3730Sstevel@tonic-gate 			continue;
3740Sstevel@tonic-gate 		}
3750Sstevel@tonic-gate 		*cp = '\0';
3761422Scarlsonj 
3771422Scarlsonj 		/*
3781422Scarlsonj 		 * Skip over the zone name.  Because we've already matched the
3791422Scarlsonj 		 * target zone (above), we know for certain here that the zone
3801422Scarlsonj 		 * name is present and correctly formed.  No need to check.
3811422Scarlsonj 		 */
3821422Scarlsonj 		cp = strchr(buf, ':') + 1;
3831422Scarlsonj 
3841422Scarlsonj 		zone_state = gettok(&cp);
3851422Scarlsonj 		if (*zone_state == '\0') {
3861422Scarlsonj 			/* state field should not be empty */
3871422Scarlsonj 			err = Z_UPDATING_INDEX;
3881422Scarlsonj 			goto error;
3890Sstevel@tonic-gate 		}
3901422Scarlsonj 		zone_path = gettok(&cp);
3911422Scarlsonj 		zone_uuid = gettok(&cp);
3921422Scarlsonj 
3931422Scarlsonj 		switch (operation) {
3941422Scarlsonj 		case PZE_ADD:
3951422Scarlsonj 			/* can't add same zone */
3961422Scarlsonj 			err = Z_UPDATING_INDEX;
3971422Scarlsonj 			goto error;
3981422Scarlsonj 
3991422Scarlsonj 		case PZE_MODIFY:
4000Sstevel@tonic-gate 			/*
4011422Scarlsonj 			 * If the caller specified a new state for the zone,
4021422Scarlsonj 			 * then use that.  Otherwise, use the current state.
4030Sstevel@tonic-gate 			 */
4041422Scarlsonj 			if (ze->zone_state >= 0) {
4051422Scarlsonj 				zone_state = zone_state_str(ze->zone_state);
4061422Scarlsonj 
4071422Scarlsonj 				/*
4081422Scarlsonj 				 * If the caller is uninstalling this zone,
4091422Scarlsonj 				 * then wipe out the uuid.  The zone's contents
4101422Scarlsonj 				 * are no longer known.
4111422Scarlsonj 				 */
4121422Scarlsonj 				if (ze->zone_state < ZONE_STATE_INSTALLED)
4131422Scarlsonj 					zone_uuid = "";
4141422Scarlsonj 			}
4151422Scarlsonj 
4161422Scarlsonj 			/* If a new name is supplied, use it. */
4171422Scarlsonj 			if (ze->zone_newname[0] != '\0')
4181422Scarlsonj 				zone_name = ze->zone_newname;
4191422Scarlsonj 
4201422Scarlsonj 			if (ze->zone_path[0] != '\0')
4211422Scarlsonj 				zone_path = ze->zone_path;
4221422Scarlsonj 			break;
4231422Scarlsonj 
4241422Scarlsonj 		case PZE_REMOVE:
4251422Scarlsonj 		default:
4260Sstevel@tonic-gate 			continue;
4270Sstevel@tonic-gate 		}
4280Sstevel@tonic-gate 
4291422Scarlsonj 	add_entry:
4301422Scarlsonj 		/*
4311422Scarlsonj 		 * If the entry in the file is in greater than configured
4321422Scarlsonj 		 * state, then we must have a UUID.  Make sure that we do.
4331422Scarlsonj 		 * (Note that the file entry is only tokenized, not fully
4341422Scarlsonj 		 * parsed, so we need to do a string comparison here.)
4351422Scarlsonj 		 */
4361422Scarlsonj 		if (strcmp(zone_state, ZONE_STATE_STR_CONFIGURED) != 0 &&
4371422Scarlsonj 		    *zone_uuid == '\0') {
4381422Scarlsonj 			if (uuid_is_null(ze->zone_uuid))
4391422Scarlsonj 				uuid_generate(ze->zone_uuid);
4401422Scarlsonj 			uuid_unparse(ze->zone_uuid, uuidstr);
4411422Scarlsonj 			zone_uuid = uuidstr;
4420Sstevel@tonic-gate 		}
4431422Scarlsonj 		/*
4441422Scarlsonj 		 * We need to quote a path that contains a ":"; this should
4451422Scarlsonj 		 * only affect the zonepath, as zone names do not allow such
4461422Scarlsonj 		 * characters, and zone states do not have them either.  Same
4471422Scarlsonj 		 * with double-quotes themselves: they are not allowed in zone
4481422Scarlsonj 		 * names, and do not occur in zone states, and in theory should
4491422Scarlsonj 		 * never occur in a zonepath since zonecfg does not support a
4501422Scarlsonj 		 * method for escaping them.
4511422Scarlsonj 		 */
4521422Scarlsonj 		need_quotes = (strchr(zone_path, ':') != NULL);
4531422Scarlsonj 		(void) fprintf(tmp_file, "%s:%s:%s%s%s:%s\n", zone_name,
4541422Scarlsonj 		    zone_state, need_quotes ? "\"" : "", zone_path,
4551422Scarlsonj 		    need_quotes ? "\"" : "", zone_uuid);
4561422Scarlsonj 		exist = B_TRUE;
4570Sstevel@tonic-gate 	}
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 	(void) fclose(index_file);
460766Scarlsonj 	index_file = NULL;
4610Sstevel@tonic-gate 	if (fclose(tmp_file) != 0) {
462766Scarlsonj 		tmp_file = NULL;
463766Scarlsonj 		err = Z_MISC_FS;
464766Scarlsonj 		goto error;
4650Sstevel@tonic-gate 	}
466766Scarlsonj 	tmp_file = NULL;
467766Scarlsonj 	if (rename(tmp_file_name, path) == -1) {
468766Scarlsonj 		err = errno == EACCES ? Z_ACCES : Z_MISC_FS;
469766Scarlsonj 		goto error;
4700Sstevel@tonic-gate 	}
4710Sstevel@tonic-gate 	free(tmp_file_name);
4720Sstevel@tonic-gate 	if (unlock_index_file(lock_fd) != Z_OK)
4730Sstevel@tonic-gate 		return (Z_UNLOCKING_FILE);
4740Sstevel@tonic-gate 	return (Z_OK);
475766Scarlsonj 
4760Sstevel@tonic-gate error:
477766Scarlsonj 	if (index_file != NULL)
478766Scarlsonj 		(void) fclose(index_file);
479766Scarlsonj 	if (tmp_file != NULL)
480766Scarlsonj 		(void) fclose(tmp_file);
4810Sstevel@tonic-gate 	(void) unlink(tmp_file_name);
4820Sstevel@tonic-gate 	free(tmp_file_name);
483766Scarlsonj 	(void) unlock_index_file(lock_fd);
484766Scarlsonj 	return (err);
4850Sstevel@tonic-gate }
486