xref: /illumos-gate/usr/src/lib/libzonecfg/common/getzoneent.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 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 /*
30  * This module contains functions used for reading and writing the index file.
31  * setzoneent() opens the file.  getzoneent() parses the file, doing the usual
32  * skipping of comment lines, etc., and using gettok() to deal with the ":"
33  * delimiters.  endzoneent() closes the file.  putzoneent() updates the file,
34  * adding, deleting or modifying lines, locking and unlocking appropriately.
35  */
36 
37 #include <stdlib.h>
38 #include <ctype.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <libzonecfg.h>
42 #include <unistd.h>
43 #include <fcntl.h>
44 #include <sys/stat.h>
45 #include <assert.h>
46 #include "zonecfg_impl.h"
47 
48 
49 #define	_PATH_TMPFILE	ZONE_CONFIG_ROOT "/zonecfg.XXXXXX"
50 
51 /*
52  * gettok() is a helper function for parsing the index file, used to split
53  * the lines by their ":" delimiters.  Note that an entry may contain a ":"
54  * inside double quotes; this should only affect the zonepath, as zone names
55  * do not allow such characters, and zone states do not have them either.
56  * Same with double-quotes themselves: they are not allowed in zone names,
57  * and do not occur in zone states, and in theory should never occur in a
58  * zonepath since zonecfg does not support a method for escaping them.
59  */
60 
61 static char *
62 gettok(char **cpp)
63 {
64 	char *cp = *cpp, *retv;
65 	boolean_t quoted = B_FALSE;
66 
67 	if (cp == NULL)
68 		return ("");
69 	if (*cp == '"') {
70 		quoted = B_TRUE;
71 		cp++;
72 	}
73 	retv = cp;
74 	if (quoted) {
75 		while (*cp != '\0' && *cp != '"')
76 			cp++;
77 		if (*cp == '"')
78 			*cp++ = '\0';
79 	}
80 	while (*cp != '\0' && *cp != ':')
81 		cp++;
82 	if (*cp == '\0') {
83 		*cpp = NULL;
84 	} else {
85 		*cp++ = '\0';
86 		*cpp = cp;
87 	}
88 	return (retv);
89 }
90 
91 char *
92 getzoneent(FILE *cookie)
93 {
94 	struct zoneent *ze;
95 	char *name;
96 
97 	if ((ze = getzoneent_private(cookie)) == NULL)
98 		return (NULL);
99 	name = strdup(ze->zone_name);
100 	free(ze);
101 	return (name);
102 }
103 
104 struct zoneent *
105 getzoneent_private(FILE *cookie)
106 {
107 	char *cp, buf[MAX_INDEX_LEN], *p;
108 	struct zoneent *ze;
109 
110 	if (cookie == NULL)
111 		return (NULL);
112 
113 	if ((ze = malloc(sizeof (struct zoneent))) == NULL)
114 		return (NULL);
115 
116 	for (;;) {
117 		if (fgets(buf, sizeof (buf), cookie) == NULL) {
118 			free(ze);
119 			return (NULL);
120 		}
121 		if ((cp = strpbrk(buf, "\r\n")) == NULL) {
122 			/* this represents a line that's too long */
123 			continue;
124 		}
125 		*cp = '\0';
126 		cp = buf;
127 		if (*cp == '#') {
128 			/* skip comment lines */
129 			continue;
130 		}
131 		p = gettok(&cp);
132 		if (p == NULL || *p == '\0' || strlen(p) > ZONENAME_MAX) {
133 			/*
134 			 * empty or very long zone names are not allowed
135 			 */
136 			continue;
137 		}
138 		(void) strlcpy(ze->zone_name, p, ZONENAME_MAX);
139 
140 		p = gettok(&cp);
141 		if (p == NULL || *p == '\0') {
142 			/* state field should not be empty */
143 			continue;
144 		}
145 		errno = 0;
146 		if (strcmp(p, ZONE_STATE_STR_CONFIGURED) == 0) {
147 			ze->zone_state = ZONE_STATE_CONFIGURED;
148 		} else if (strcmp(p, ZONE_STATE_STR_INCOMPLETE) == 0) {
149 			ze->zone_state = ZONE_STATE_INCOMPLETE;
150 		} else if (strcmp(p, ZONE_STATE_STR_INSTALLED) == 0) {
151 			ze->zone_state = ZONE_STATE_INSTALLED;
152 		} else
153 			continue;
154 
155 		p = gettok(&cp);
156 		if (strlen(p) > MAXPATHLEN) {
157 			/* very long paths are not allowed */
158 			continue;
159 		}
160 		if (p == NULL) {
161 			/* empty paths accepted for backwards compatibility */
162 			p = "";
163 		}
164 		(void) strlcpy(ze->zone_path, p, MAXPATHLEN);
165 
166 		break;
167 	}
168 
169 	return (ze);
170 }
171 
172 FILE *
173 setzoneent(void)
174 {
175 	return (fopen(ZONE_INDEX_FILE, "r"));
176 }
177 
178 void
179 endzoneent(FILE *cookie)
180 {
181 	if (cookie != NULL)
182 		(void) fclose(cookie);
183 }
184 
185 static int
186 lock_index_file(int *lock_fd)
187 {
188 	struct flock lock;
189 
190 	if ((mkdir(ZONE_SNAPSHOT_ROOT, S_IRWXU) == -1) && errno != EEXIST)
191 		return (Z_LOCKING_FILE);
192 	*lock_fd = open(ZONE_INDEX_LOCK_FILE, O_CREAT|O_RDWR, 0644);
193 	if (*lock_fd < 0)
194 		return (Z_LOCKING_FILE);
195 
196 	lock.l_type = F_WRLCK;
197 	lock.l_whence = SEEK_SET;
198 	lock.l_start = 0;
199 	lock.l_len = 0;
200 
201 	if (fcntl(*lock_fd, F_SETLKW, &lock) == -1)
202 		return (Z_LOCKING_FILE);
203 
204 	return (Z_OK);
205 }
206 
207 static int
208 unlock_index_file(int lock_fd)
209 {
210 	struct flock lock;
211 
212 	lock.l_type = F_UNLCK;
213 	lock.l_whence = SEEK_SET;
214 	lock.l_start = 0;
215 	lock.l_len = 0;
216 
217 	if (fcntl(lock_fd, F_SETLK, &lock) == -1)
218 		return (Z_UNLOCKING_FILE);
219 
220 	if (close(lock_fd) == -1)
221 		return (Z_UNLOCKING_FILE);
222 
223 	return (Z_OK);
224 }
225 
226 /*
227  * This function adds or removes a zone name et al. to the index file.
228  *
229  * If ze->zone_state is < 0, it means leave the
230  * existing value unchanged; this is only meaningful when operation ==
231  * PZE_MODIFY (i.e., it's bad on PZE_ADD and a no-op on PZE_DELETE).
232  *
233  * Likewise, a zero-length ze->zone_path means leave the existing value
234  * unchanged; this is only meaningful when operation == PZE_MODIFY
235  * (i.e., it's bad on PZE_ADD and a no-op on PZE_DELETE).
236  *
237  * Locking and unlocking is done via the functions above.
238  * The file itself is not modified in place; rather, a copy is made which
239  * is modified, then the copy is atomically renamed back to the main file.
240  */
241 
242 int
243 putzoneent(struct zoneent *ze, zoneent_op_t operation)
244 {
245 	FILE *index_file, *tmp_file;
246 	char *tmp_file_name, buf[MAX_INDEX_LEN], orig_buf[MAX_INDEX_LEN];
247 	char zone[ZONENAME_MAX + 1];		/* name plus newline */
248 	char line[MAX_INDEX_LEN];
249 	int tmp_file_desc, lock_fd, err;
250 	boolean_t exists = B_FALSE, need_quotes;
251 	char *cp, *p;
252 
253 	assert(ze != NULL);
254 	if (operation == PZE_ADD &&
255 	    (ze->zone_state < 0 || strlen(ze->zone_path) == 0))
256 		return (Z_INVAL);
257 	if ((err = lock_index_file(&lock_fd)) != Z_OK)
258 		return (err);
259 	tmp_file_name = strdup(_PATH_TMPFILE);
260 	if (tmp_file_name == NULL) {
261 		(void) unlock_index_file(lock_fd);
262 		return (Z_NOMEM);
263 	}
264 	tmp_file_desc = mkstemp(tmp_file_name);
265 	if (tmp_file_desc == -1) {
266 		(void) unlink(tmp_file_name);
267 		free(tmp_file_name);
268 		(void) unlock_index_file(lock_fd);
269 		return (Z_TEMP_FILE);
270 	}
271 	if ((tmp_file = fdopen(tmp_file_desc, "w")) == NULL) {
272 		(void) close(tmp_file_desc);
273 		(void) unlink(tmp_file_name);
274 		free(tmp_file_name);
275 		(void) unlock_index_file(lock_fd);
276 		return (Z_MISC_FS);
277 	}
278 	if ((index_file = fopen(ZONE_INDEX_FILE, "r")) == NULL) {
279 		(void) fclose(tmp_file);
280 		(void) unlink(tmp_file_name);
281 		free(tmp_file_name);
282 		(void) unlock_index_file(lock_fd);
283 		return (Z_MISC_FS);
284 	}
285 
286 	/*
287 	 * We need to quote a path which contains a ":"; this should only
288 	 * affect the zonepath, as zone names do not allow such characters,
289 	 * and zone states do not have them either.  Same with double-quotes
290 	 * themselves: they are not allowed in zone names, and do not occur
291 	 * in zone states, and in theory should never occur in a zonepath
292 	 * since zonecfg does not support a method for escaping them.
293 	 */
294 	need_quotes = (strchr(ze->zone_path, ':') != NULL);
295 
296 	(void) snprintf(line, sizeof (line), "%s:%s:%s%s%s\n", ze->zone_name,
297 	    zone_state_str(ze->zone_state), need_quotes ? "\"" : "",
298 	    ze->zone_path, need_quotes ? "\"" : "");
299 	for (;;) {
300 		if (fgets(buf, sizeof (buf), index_file) == NULL) {
301 			if (operation == PZE_ADD && !exists)
302 				(void) fputs(line, tmp_file);
303 			break;
304 		}
305 		(void) strlcpy(orig_buf, buf, sizeof (orig_buf));
306 
307 		if ((cp = strpbrk(buf, "\r\n")) == NULL) {
308 			/* this represents a line that's too long */
309 			continue;
310 		}
311 		*cp = '\0';
312 		cp = buf;
313 		if (*cp == '#') {
314 			/* skip comment lines */
315 			(void) fputs(orig_buf, tmp_file);
316 			continue;
317 		}
318 		p = gettok(&cp);
319 		if (p == NULL || *p == '\0' || strlen(p) > ZONENAME_MAX) {
320 			/*
321 			 * empty or very long zone names are not allowed
322 			 */
323 			continue;
324 		}
325 		(void) strlcpy(zone, p, ZONENAME_MAX);
326 
327 		if (strcmp(zone, ze->zone_name) == 0) {
328 			exists = B_TRUE;		/* already there */
329 			if (operation == PZE_ADD) {
330 				/* can't add same zone */
331 				goto error;
332 			} else if (operation == PZE_MODIFY) {
333 				char tmp_state[ZONE_STATE_MAXSTRLEN + 1];
334 
335 				if (ze->zone_state >= 0 &&
336 				    strlen(ze->zone_path) > 0) {
337 					/* use specified values */
338 					(void) fputs(line, tmp_file);
339 					continue;
340 				}
341 				/* use existing value for state */
342 				p = gettok(&cp);
343 				if (p == NULL || *p == '\0') {
344 					/* state field should not be empty */
345 					goto error;
346 				}
347 				(void) strlcpy(tmp_state,
348 				    (ze->zone_state < 0) ? p :
349 				    zone_state_str(ze->zone_state),
350 				    sizeof (tmp_state));
351 
352 				p = gettok(&cp);
353 
354 				(void) fprintf(tmp_file, "%s:%s:%s%s%s\n",
355 				    ze->zone_name, tmp_state,
356 				    need_quotes ? "\"" : "",
357 				    (strlen(ze->zone_path) == 0) ? p :
358 				    ze->zone_path, need_quotes ? "\"" : "");
359 			}
360 		} else {
361 			(void) fputs(orig_buf, tmp_file);
362 		}
363 	}
364 
365 	(void) fclose(index_file);
366 	if (fclose(tmp_file) != 0) {
367 		(void) unlink(tmp_file_name);
368 		free(tmp_file_name);
369 		(void) unlock_index_file(lock_fd);
370 		return (Z_MISC_FS);
371 	}
372 	(void) chmod(tmp_file_name, 0644);
373 	if (rename(tmp_file_name, ZONE_INDEX_FILE) == -1) {
374 		(void) unlink(tmp_file_name);
375 		free(tmp_file_name);
376 		(void) unlock_index_file(lock_fd);
377 		if (errno == EACCES)
378 			return (Z_ACCES);
379 		return (Z_MISC_FS);
380 	}
381 	free(tmp_file_name);
382 	if (unlock_index_file(lock_fd) != Z_OK)
383 		return (Z_UNLOCKING_FILE);
384 	return (Z_OK);
385 error:
386 	(void) fclose(index_file);
387 	(void) fclose(tmp_file);
388 	(void) unlink(tmp_file_name);
389 	free(tmp_file_name);
390 	if (unlock_index_file(lock_fd) != Z_OK)
391 		return (Z_UNLOCKING_FILE);
392 	return (Z_UPDATING_INDEX);
393 }
394