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.
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.
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]
22 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
27 * This module contains functions used for reading and writing the index file.
28 * setzoneent() opens the file. getzoneent() parses the file, doing the usual
29 * skipping of comment lines, etc., and using gettok() to deal with the ":"
30 * delimiters. endzoneent() closes the file. putzoneent() updates the file,
31 * adding, deleting or modifying lines, locking and unlocking appropriately.
37 #include <libzonecfg.h>
42 #include <uuid/uuid.h>
43 #include "zonecfg_impl.h"
46 #define _PATH_TMPFILE ZONE_CONFIG_ROOT "/zonecfg.XXXXXX"
49 * gettok() is a helper function for parsing the index file, used to split
50 * the lines by their ":" delimiters. Note that an entry may contain a ":"
51 * inside double quotes; this should only affect the zonepath, as zone names
52 * do not allow such characters, and zone states do not have them either.
53 * Same with double-quotes themselves: they are not allowed in zone names,
54 * and do not occur in zone states, and in theory should never occur in a
55 * zonepath since zonecfg does not support a method for escaping them.
57 * It never returns NULL.
63 char *cp
= *cpp
, *retv
;
64 boolean_t quoted
= B_FALSE
;
74 while (*cp
!= '\0' && *cp
!= '"')
79 while (*cp
!= '\0' && *cp
!= ':')
91 getzoneent(FILE *cookie
)
96 if ((ze
= getzoneent_private(cookie
)) == NULL
)
98 name
= strdup(ze
->zone_name
);
104 getzoneent_private(FILE *cookie
)
106 char *cp
, buf
[MAX_INDEX_LEN
], *p
;
112 if ((ze
= malloc(sizeof (struct zoneent
))) == NULL
)
116 if (fgets(buf
, sizeof (buf
), cookie
) == NULL
) {
120 if ((cp
= strpbrk(buf
, "\r\n")) == NULL
) {
121 /* this represents a line that's too long */
127 /* skip comment lines */
131 if (*p
== '\0' || strlen(p
) >= ZONENAME_MAX
) {
133 * empty or very long zone names are not allowed
137 (void) strlcpy(ze
->zone_name
, p
, ZONENAME_MAX
);
141 /* state field should not be empty */
145 if (strcmp(p
, ZONE_STATE_STR_CONFIGURED
) == 0) {
146 ze
->zone_state
= ZONE_STATE_CONFIGURED
;
147 } else if (strcmp(p
, ZONE_STATE_STR_INCOMPLETE
) == 0) {
148 ze
->zone_state
= ZONE_STATE_INCOMPLETE
;
149 } else if (strcmp(p
, ZONE_STATE_STR_INSTALLED
) == 0) {
150 ze
->zone_state
= ZONE_STATE_INSTALLED
;
156 if (strlen(p
) >= MAXPATHLEN
) {
157 /* very long paths are not allowed */
160 (void) strlcpy(ze
->zone_path
, p
, MAXPATHLEN
);
163 if (uuid_parse(p
, ze
->zone_uuid
) == -1)
164 uuid_clear(ze
->zone_uuid
);
173 get_index_path(char *path
)
175 return (snprintf(path
, MAXPATHLEN
, "%s%s", zonecfg_root
,
176 ZONE_INDEX_FILE
) < MAXPATHLEN
);
182 char path
[MAXPATHLEN
];
184 if (!get_index_path(path
)) {
188 return (fopen(path
, "r"));
192 endzoneent(FILE *cookie
)
195 (void) fclose(cookie
);
199 lock_index_file(void)
203 char path
[MAXPATHLEN
];
205 if (snprintf(path
, sizeof (path
), "%s%s", zonecfg_root
,
206 ZONE_INDEX_LOCK_DIR
) >= sizeof (path
))
208 if ((mkdir(path
, S_IRWXU
) == -1) && errno
!= EEXIST
)
210 if (strlcat(path
, ZONE_INDEX_LOCK_FILE
, sizeof (path
)) >=
213 lock_fd
= open(path
, O_CREAT
|O_RDWR
, 0644);
217 lock
.l_type
= F_WRLCK
;
218 lock
.l_whence
= SEEK_SET
;
222 if (fcntl(lock_fd
, F_SETLKW
, &lock
) == -1) {
223 (void) close(lock_fd
);
231 unlock_index_file(int lock_fd
)
235 lock
.l_type
= F_UNLCK
;
236 lock
.l_whence
= SEEK_SET
;
240 if (fcntl(lock_fd
, F_SETLK
, &lock
) == -1)
241 return (Z_UNLOCKING_FILE
);
243 if (close(lock_fd
) == -1)
244 return (Z_UNLOCKING_FILE
);
250 * This function adds or removes a zone name et al. to the index file.
252 * If ze->zone_state is < 0, it means leave the
253 * existing value unchanged; this is only meaningful when operation ==
254 * PZE_MODIFY (i.e., it's bad on PZE_ADD and a no-op on PZE_REMOVE).
256 * A zero-length ze->zone_path means leave the existing value
257 * unchanged; this is only meaningful when operation == PZE_MODIFY
258 * (i.e., it's bad on PZE_ADD and a no-op on PZE_REMOVE).
260 * A zero-length ze->zone_newname means leave the existing name
261 * unchanged; otherwise the zone is renamed to zone_newname. This is
262 * only meaningful when operation == PZE_MODIFY.
264 * Locking and unlocking is done via the functions above.
265 * The file itself is not modified in place; rather, a copy is made which
266 * is modified, then the copy is atomically renamed back to the main file.
269 putzoneent(struct zoneent
*ze
, zoneent_op_t operation
)
271 FILE *index_file
, *tmp_file
;
272 char *tmp_file_name
, buf
[MAX_INDEX_LEN
];
273 int tmp_file_desc
, lock_fd
, err
;
274 boolean_t exist
, need_quotes
;
276 char path
[MAXPATHLEN
];
277 char uuidstr
[UUID_PRINTABLE_STRING_LENGTH
];
278 size_t tlen
, namelen
;
279 const char *zone_name
, *zone_state
, *zone_path
, *zone_uuid
;
284 * Don't allow modification of Global Zone entry
287 if ((operation
== PZE_MODIFY
) &&
288 (strcmp(ze
->zone_name
, GLOBAL_ZONENAME
) == 0)) {
292 if (operation
== PZE_ADD
&&
293 (ze
->zone_state
< 0 || strlen(ze
->zone_path
) == 0))
296 if (operation
!= PZE_MODIFY
&& strlen(ze
->zone_newname
) != 0)
299 if ((lock_fd
= lock_index_file()) == -1)
300 return (Z_LOCKING_FILE
);
302 /* using sizeof gives us room for the terminating NUL byte as well */
303 tlen
= sizeof (_PATH_TMPFILE
) + strlen(zonecfg_root
);
304 tmp_file_name
= malloc(tlen
);
305 if (tmp_file_name
== NULL
) {
306 (void) unlock_index_file(lock_fd
);
309 (void) snprintf(tmp_file_name
, tlen
, "%s%s", zonecfg_root
,
312 tmp_file_desc
= mkstemp(tmp_file_name
);
313 if (tmp_file_desc
== -1) {
314 (void) unlink(tmp_file_name
);
316 (void) unlock_index_file(lock_fd
);
317 return (Z_TEMP_FILE
);
319 (void) fchmod(tmp_file_desc
, ZONE_INDEX_MODE
);
320 (void) fchown(tmp_file_desc
, ZONE_INDEX_UID
, ZONE_INDEX_GID
);
321 if ((tmp_file
= fdopen(tmp_file_desc
, "w")) == NULL
) {
322 (void) close(tmp_file_desc
);
326 if (!get_index_path(path
)) {
330 if ((index_file
= fopen(path
, "r")) == NULL
) {
336 zone_name
= ze
->zone_name
;
337 namelen
= strlen(zone_name
);
339 if (fgets(buf
, sizeof (buf
), index_file
) == NULL
) {
340 if (operation
== PZE_ADD
&& !exist
) {
341 zone_state
= zone_state_str(ze
->zone_state
);
342 zone_path
= ze
->zone_path
;
347 * It's not considered an error to delete something
348 * that doesn't exist, but we can't modify a missing
351 if (operation
== PZE_MODIFY
&& !exist
) {
352 err
= Z_UPDATING_INDEX
;
359 /* skip and preserve comment lines */
360 (void) fputs(buf
, tmp_file
);
364 if (strncmp(buf
, zone_name
, namelen
) != 0 ||
365 buf
[namelen
] != ':') {
366 /* skip and preserve non-target lines */
367 (void) fputs(buf
, tmp_file
);
371 if ((cp
= strpbrk(buf
, "\r\n")) == NULL
) {
372 /* this represents a line that's too long; delete */
378 * Skip over the zone name. Because we've already matched the
379 * target zone (above), we know for certain here that the zone
380 * name is present and correctly formed. No need to check.
382 cp
= strchr(buf
, ':') + 1;
384 zone_state
= gettok(&cp
);
385 if (*zone_state
== '\0') {
386 /* state field should not be empty */
387 err
= Z_UPDATING_INDEX
;
390 zone_path
= gettok(&cp
);
391 zone_uuid
= gettok(&cp
);
395 /* can't add same zone */
396 err
= Z_UPDATING_INDEX
;
401 * If the caller specified a new state for the zone,
402 * then use that. Otherwise, use the current state.
404 if (ze
->zone_state
>= 0) {
405 zone_state
= zone_state_str(ze
->zone_state
);
408 * If the caller is uninstalling this zone,
409 * then wipe out the uuid. The zone's contents
410 * are no longer known.
412 if (ze
->zone_state
< ZONE_STATE_INSTALLED
)
416 /* If a new name is supplied, use it. */
417 if (ze
->zone_newname
[0] != '\0')
418 zone_name
= ze
->zone_newname
;
420 if (ze
->zone_path
[0] != '\0')
421 zone_path
= ze
->zone_path
;
431 * If the entry in the file is in greater than configured
432 * state, then we must have a UUID. Make sure that we do.
433 * (Note that the file entry is only tokenized, not fully
434 * parsed, so we need to do a string comparison here.)
436 if (strcmp(zone_state
, ZONE_STATE_STR_CONFIGURED
) != 0 &&
437 *zone_uuid
== '\0') {
438 if (uuid_is_null(ze
->zone_uuid
))
439 uuid_generate(ze
->zone_uuid
);
440 uuid_unparse(ze
->zone_uuid
, uuidstr
);
444 * We need to quote a path that contains a ":"; this should
445 * only affect the zonepath, as zone names do not allow such
446 * characters, and zone states do not have them either. Same
447 * with double-quotes themselves: they are not allowed in zone
448 * names, and do not occur in zone states, and in theory should
449 * never occur in a zonepath since zonecfg does not support a
450 * method for escaping them.
452 need_quotes
= (strchr(zone_path
, ':') != NULL
);
453 (void) fprintf(tmp_file
, "%s:%s:%s%s%s:%s\n", zone_name
,
454 zone_state
, need_quotes
? "\"" : "", zone_path
,
455 need_quotes
? "\"" : "", zone_uuid
);
459 (void) fclose(index_file
);
461 if (fclose(tmp_file
) != 0) {
467 if (rename(tmp_file_name
, path
) == -1) {
468 err
= errno
== EACCES
? Z_ACCES
: Z_MISC_FS
;
472 if (unlock_index_file(lock_fd
) != Z_OK
)
473 return (Z_UNLOCKING_FILE
);
477 if (index_file
!= NULL
)
478 (void) fclose(index_file
);
479 if (tmp_file
!= NULL
)
480 (void) fclose(tmp_file
);
481 (void) unlink(tmp_file_name
);
483 (void) unlock_index_file(lock_fd
);