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 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * This module contains functions used for reading and writing the index file. 30 * setzoneent() opens the file. getzoneent() parses the file, doing the usual 31 * skipping of comment lines, etc., and using gettok() to deal with the ":" 32 * delimiters. endzoneent() closes the file. putzoneent() updates the file, 33 * adding, deleting or modifying lines, locking and unlocking appropriately. 34 */ 35 36 #include <stdlib.h> 37 #include <string.h> 38 #include <errno.h> 39 #include <libzonecfg.h> 40 #include <unistd.h> 41 #include <fcntl.h> 42 #include <sys/stat.h> 43 #include <assert.h> 44 #include <uuid/uuid.h> 45 #include "zonecfg_impl.h" 46 47 48 #define _PATH_TMPFILE ZONE_CONFIG_ROOT "/zonecfg.XXXXXX" 49 50 /* 51 * gettok() is a helper function for parsing the index file, used to split 52 * the lines by their ":" delimiters. Note that an entry may contain a ":" 53 * inside double quotes; this should only affect the zonepath, as zone names 54 * do not allow such characters, and zone states do not have them either. 55 * Same with double-quotes themselves: they are not allowed in zone names, 56 * and do not occur in zone states, and in theory should never occur in a 57 * zonepath since zonecfg does not support a method for escaping them. 58 * 59 * It never returns NULL. 60 */ 61 62 static char * 63 gettok(char **cpp) 64 { 65 char *cp = *cpp, *retv; 66 boolean_t quoted = B_FALSE; 67 68 if (cp == NULL) 69 return (""); 70 if (*cp == '"') { 71 quoted = B_TRUE; 72 cp++; 73 } 74 retv = cp; 75 if (quoted) { 76 while (*cp != '\0' && *cp != '"') 77 cp++; 78 if (*cp == '"') 79 *cp++ = '\0'; 80 } 81 while (*cp != '\0' && *cp != ':') 82 cp++; 83 if (*cp == '\0') { 84 *cpp = NULL; 85 } else { 86 *cp++ = '\0'; 87 *cpp = cp; 88 } 89 return (retv); 90 } 91 92 char * 93 getzoneent(FILE *cookie) 94 { 95 struct zoneent *ze; 96 char *name; 97 98 if ((ze = getzoneent_private(cookie)) == NULL) 99 return (NULL); 100 name = strdup(ze->zone_name); 101 free(ze); 102 return (name); 103 } 104 105 struct zoneent * 106 getzoneent_private(FILE *cookie) 107 { 108 char *cp, buf[MAX_INDEX_LEN], *p; 109 struct zoneent *ze; 110 111 if (cookie == NULL) 112 return (NULL); 113 114 if ((ze = malloc(sizeof (struct zoneent))) == NULL) 115 return (NULL); 116 117 for (;;) { 118 if (fgets(buf, sizeof (buf), cookie) == NULL) { 119 free(ze); 120 return (NULL); 121 } 122 if ((cp = strpbrk(buf, "\r\n")) == NULL) { 123 /* this represents a line that's too long */ 124 continue; 125 } 126 *cp = '\0'; 127 cp = buf; 128 if (*cp == '#') { 129 /* skip comment lines */ 130 continue; 131 } 132 p = gettok(&cp); 133 if (*p == '\0' || strlen(p) >= ZONENAME_MAX) { 134 /* 135 * empty or very long zone names are not allowed 136 */ 137 continue; 138 } 139 (void) strlcpy(ze->zone_name, p, ZONENAME_MAX); 140 141 p = gettok(&cp); 142 if (*p == '\0') { 143 /* state field should not be empty */ 144 continue; 145 } 146 errno = 0; 147 if (strcmp(p, ZONE_STATE_STR_CONFIGURED) == 0) { 148 ze->zone_state = ZONE_STATE_CONFIGURED; 149 } else if (strcmp(p, ZONE_STATE_STR_INCOMPLETE) == 0) { 150 ze->zone_state = ZONE_STATE_INCOMPLETE; 151 } else if (strcmp(p, ZONE_STATE_STR_INSTALLED) == 0) { 152 ze->zone_state = ZONE_STATE_INSTALLED; 153 } else { 154 continue; 155 } 156 157 p = gettok(&cp); 158 if (strlen(p) >= MAXPATHLEN) { 159 /* very long paths are not allowed */ 160 continue; 161 } 162 (void) strlcpy(ze->zone_path, p, MAXPATHLEN); 163 164 p = gettok(&cp); 165 if (uuid_parse(p, ze->zone_uuid) == -1) 166 uuid_clear(ze->zone_uuid); 167 168 break; 169 } 170 171 return (ze); 172 } 173 174 static boolean_t 175 get_index_path(char *path) 176 { 177 return (snprintf(path, MAXPATHLEN, "%s%s", zonecfg_root, 178 ZONE_INDEX_FILE) < MAXPATHLEN); 179 } 180 181 FILE * 182 setzoneent(void) 183 { 184 char path[MAXPATHLEN]; 185 186 if (!get_index_path(path)) { 187 errno = EINVAL; 188 return (NULL); 189 } 190 return (fopen(path, "r")); 191 } 192 193 void 194 endzoneent(FILE *cookie) 195 { 196 if (cookie != NULL) 197 (void) fclose(cookie); 198 } 199 200 static int 201 lock_index_file(void) 202 { 203 int lock_fd; 204 struct flock lock; 205 char path[MAXPATHLEN]; 206 207 if (snprintf(path, sizeof (path), "%s%s", zonecfg_root, 208 ZONE_INDEX_LOCK_DIR) >= sizeof (path)) 209 return (-1); 210 if ((mkdir(path, S_IRWXU) == -1) && errno != EEXIST) 211 return (-1); 212 if (strlcat(path, ZONE_INDEX_LOCK_FILE, sizeof (path)) >= 213 sizeof (path)) 214 return (-1); 215 lock_fd = open(path, O_CREAT|O_RDWR, 0644); 216 if (lock_fd == -1) 217 return (-1); 218 219 lock.l_type = F_WRLCK; 220 lock.l_whence = SEEK_SET; 221 lock.l_start = 0; 222 lock.l_len = 0; 223 224 if (fcntl(lock_fd, F_SETLKW, &lock) == -1) { 225 (void) close(lock_fd); 226 return (-1); 227 } 228 229 return (lock_fd); 230 } 231 232 static int 233 unlock_index_file(int lock_fd) 234 { 235 struct flock lock; 236 237 lock.l_type = F_UNLCK; 238 lock.l_whence = SEEK_SET; 239 lock.l_start = 0; 240 lock.l_len = 0; 241 242 if (fcntl(lock_fd, F_SETLK, &lock) == -1) 243 return (Z_UNLOCKING_FILE); 244 245 if (close(lock_fd) == -1) 246 return (Z_UNLOCKING_FILE); 247 248 return (Z_OK); 249 } 250 251 /* 252 * This function adds or removes a zone name et al. to the index file. 253 * 254 * If ze->zone_state is < 0, it means leave the 255 * existing value unchanged; this is only meaningful when operation == 256 * PZE_MODIFY (i.e., it's bad on PZE_ADD and a no-op on PZE_REMOVE). 257 * 258 * A zero-length ze->zone_path means leave the existing value 259 * unchanged; this is only meaningful when operation == PZE_MODIFY 260 * (i.e., it's bad on PZE_ADD and a no-op on PZE_REMOVE). 261 * 262 * A zero-length ze->zone_newname means leave the existing name 263 * unchanged; otherwise the zone is renamed to zone_newname. This is 264 * only meaningful when operation == PZE_MODIFY. 265 * 266 * Locking and unlocking is done via the functions above. 267 * The file itself is not modified in place; rather, a copy is made which 268 * is modified, then the copy is atomically renamed back to the main file. 269 */ 270 int 271 putzoneent(struct zoneent *ze, zoneent_op_t operation) 272 { 273 FILE *index_file, *tmp_file; 274 char *tmp_file_name, buf[MAX_INDEX_LEN]; 275 int tmp_file_desc, lock_fd, err; 276 boolean_t exist, need_quotes; 277 char *cp; 278 char path[MAXPATHLEN]; 279 char uuidstr[UUID_PRINTABLE_STRING_LENGTH]; 280 size_t tlen, namelen; 281 const char *zone_name, *zone_state, *zone_path, *zone_uuid; 282 283 assert(ze != NULL); 284 if (operation == PZE_ADD && 285 (ze->zone_state < 0 || strlen(ze->zone_path) == 0)) 286 return (Z_INVAL); 287 288 if (operation != PZE_MODIFY && strlen(ze->zone_newname) != 0) 289 return (Z_INVAL); 290 291 if ((lock_fd = lock_index_file()) == -1) 292 return (Z_LOCKING_FILE); 293 294 /* using sizeof gives us room for the terminating NUL byte as well */ 295 tlen = sizeof (_PATH_TMPFILE) + strlen(zonecfg_root); 296 tmp_file_name = malloc(tlen); 297 if (tmp_file_name == NULL) { 298 (void) unlock_index_file(lock_fd); 299 return (Z_NOMEM); 300 } 301 (void) snprintf(tmp_file_name, tlen, "%s%s", zonecfg_root, 302 _PATH_TMPFILE); 303 304 tmp_file_desc = mkstemp(tmp_file_name); 305 if (tmp_file_desc == -1) { 306 (void) unlink(tmp_file_name); 307 free(tmp_file_name); 308 (void) unlock_index_file(lock_fd); 309 return (Z_TEMP_FILE); 310 } 311 (void) fchmod(tmp_file_desc, ZONE_INDEX_MODE); 312 (void) fchown(tmp_file_desc, ZONE_INDEX_UID, ZONE_INDEX_GID); 313 if ((tmp_file = fdopen(tmp_file_desc, "w")) == NULL) { 314 (void) close(tmp_file_desc); 315 err = Z_MISC_FS; 316 goto error; 317 } 318 if (!get_index_path(path)) { 319 err = Z_MISC_FS; 320 goto error; 321 } 322 if ((index_file = fopen(path, "r")) == NULL) { 323 err = Z_MISC_FS; 324 goto error; 325 } 326 327 exist = B_FALSE; 328 zone_name = ze->zone_name; 329 namelen = strlen(zone_name); 330 for (;;) { 331 if (fgets(buf, sizeof (buf), index_file) == NULL) { 332 if (operation == PZE_ADD && !exist) { 333 zone_state = zone_state_str(ze->zone_state); 334 zone_path = ze->zone_path; 335 zone_uuid = ""; 336 goto add_entry; 337 } 338 /* 339 * It's not considered an error to delete something 340 * that doesn't exist, but we can't modify a missing 341 * record. 342 */ 343 if (operation == PZE_MODIFY && !exist) { 344 err = Z_UPDATING_INDEX; 345 goto error; 346 } 347 break; 348 } 349 350 if (buf[0] == '#') { 351 /* skip and preserve comment lines */ 352 (void) fputs(buf, tmp_file); 353 continue; 354 } 355 356 if (strncmp(buf, zone_name, namelen) != 0 || 357 buf[namelen] != ':') { 358 /* skip and preserve non-target lines */ 359 (void) fputs(buf, tmp_file); 360 continue; 361 } 362 363 if ((cp = strpbrk(buf, "\r\n")) == NULL) { 364 /* this represents a line that's too long; delete */ 365 continue; 366 } 367 *cp = '\0'; 368 369 /* 370 * Skip over the zone name. Because we've already matched the 371 * target zone (above), we know for certain here that the zone 372 * name is present and correctly formed. No need to check. 373 */ 374 cp = strchr(buf, ':') + 1; 375 376 zone_state = gettok(&cp); 377 if (*zone_state == '\0') { 378 /* state field should not be empty */ 379 err = Z_UPDATING_INDEX; 380 goto error; 381 } 382 zone_path = gettok(&cp); 383 zone_uuid = gettok(&cp); 384 385 switch (operation) { 386 case PZE_ADD: 387 /* can't add same zone */ 388 err = Z_UPDATING_INDEX; 389 goto error; 390 391 case PZE_MODIFY: 392 /* 393 * If the caller specified a new state for the zone, 394 * then use that. Otherwise, use the current state. 395 */ 396 if (ze->zone_state >= 0) { 397 zone_state = zone_state_str(ze->zone_state); 398 399 /* 400 * If the caller is uninstalling this zone, 401 * then wipe out the uuid. The zone's contents 402 * are no longer known. 403 */ 404 if (ze->zone_state < ZONE_STATE_INSTALLED) 405 zone_uuid = ""; 406 } 407 408 /* If a new name is supplied, use it. */ 409 if (ze->zone_newname[0] != '\0') 410 zone_name = ze->zone_newname; 411 412 if (ze->zone_path[0] != '\0') 413 zone_path = ze->zone_path; 414 break; 415 416 case PZE_REMOVE: 417 default: 418 continue; 419 } 420 421 add_entry: 422 /* 423 * If the entry in the file is in greater than configured 424 * state, then we must have a UUID. Make sure that we do. 425 * (Note that the file entry is only tokenized, not fully 426 * parsed, so we need to do a string comparison here.) 427 */ 428 if (strcmp(zone_state, ZONE_STATE_STR_CONFIGURED) != 0 && 429 *zone_uuid == '\0') { 430 if (uuid_is_null(ze->zone_uuid)) 431 uuid_generate(ze->zone_uuid); 432 uuid_unparse(ze->zone_uuid, uuidstr); 433 zone_uuid = uuidstr; 434 } 435 /* 436 * We need to quote a path that contains a ":"; this should 437 * only affect the zonepath, as zone names do not allow such 438 * characters, and zone states do not have them either. Same 439 * with double-quotes themselves: they are not allowed in zone 440 * names, and do not occur in zone states, and in theory should 441 * never occur in a zonepath since zonecfg does not support a 442 * method for escaping them. 443 */ 444 need_quotes = (strchr(zone_path, ':') != NULL); 445 (void) fprintf(tmp_file, "%s:%s:%s%s%s:%s\n", zone_name, 446 zone_state, need_quotes ? "\"" : "", zone_path, 447 need_quotes ? "\"" : "", zone_uuid); 448 exist = B_TRUE; 449 } 450 451 (void) fclose(index_file); 452 index_file = NULL; 453 if (fclose(tmp_file) != 0) { 454 tmp_file = NULL; 455 err = Z_MISC_FS; 456 goto error; 457 } 458 tmp_file = NULL; 459 if (rename(tmp_file_name, path) == -1) { 460 err = errno == EACCES ? Z_ACCES : Z_MISC_FS; 461 goto error; 462 } 463 free(tmp_file_name); 464 if (unlock_index_file(lock_fd) != Z_OK) 465 return (Z_UNLOCKING_FILE); 466 return (Z_OK); 467 468 error: 469 if (index_file != NULL) 470 (void) fclose(index_file); 471 if (tmp_file != NULL) 472 (void) fclose(tmp_file); 473 (void) unlink(tmp_file_name); 474 free(tmp_file_name); 475 (void) unlock_index_file(lock_fd); 476 return (err); 477 } 478