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 2005 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_REMOVE). 232 * 233 * 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_REMOVE). 236 * 237 * A zero-length ze->zone_newname means leave the existing name 238 * unchanged; otherwise the zone is renamed to zone_newname. This is 239 * only meaningful when operation == PZE_MODIFY. 240 * 241 * Locking and unlocking is done via the functions above. 242 * The file itself is not modified in place; rather, a copy is made which 243 * is modified, then the copy is atomically renamed back to the main file. 244 */ 245 int 246 putzoneent(struct zoneent *ze, zoneent_op_t operation) 247 { 248 FILE *index_file, *tmp_file; 249 char *tmp_file_name, buf[MAX_INDEX_LEN], orig_buf[MAX_INDEX_LEN]; 250 char zone[ZONENAME_MAX + 1]; /* name plus newline */ 251 char line[MAX_INDEX_LEN]; 252 int tmp_file_desc, lock_fd, err; 253 boolean_t exists = B_FALSE, need_quotes; 254 char *cp, *p; 255 256 assert(ze != NULL); 257 if (operation == PZE_ADD && 258 (ze->zone_state < 0 || strlen(ze->zone_path) == 0)) 259 return (Z_INVAL); 260 261 if (operation != PZE_MODIFY && strlen(ze->zone_newname) != 0) 262 return (Z_INVAL); 263 264 if ((err = lock_index_file(&lock_fd)) != Z_OK) 265 return (err); 266 tmp_file_name = strdup(_PATH_TMPFILE); 267 if (tmp_file_name == NULL) { 268 (void) unlock_index_file(lock_fd); 269 return (Z_NOMEM); 270 } 271 tmp_file_desc = mkstemp(tmp_file_name); 272 if (tmp_file_desc == -1) { 273 (void) unlink(tmp_file_name); 274 free(tmp_file_name); 275 (void) unlock_index_file(lock_fd); 276 return (Z_TEMP_FILE); 277 } 278 if ((tmp_file = fdopen(tmp_file_desc, "w")) == NULL) { 279 (void) close(tmp_file_desc); 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 if ((index_file = fopen(ZONE_INDEX_FILE, "r")) == NULL) { 286 (void) fclose(tmp_file); 287 (void) unlink(tmp_file_name); 288 free(tmp_file_name); 289 (void) unlock_index_file(lock_fd); 290 return (Z_MISC_FS); 291 } 292 293 /* 294 * We need to quote a path which contains a ":"; this should only 295 * affect the zonepath, as zone names do not allow such characters, 296 * and zone states do not have them either. Same with double-quotes 297 * themselves: they are not allowed in zone names, and do not occur 298 * in zone states, and in theory should never occur in a zonepath 299 * since zonecfg does not support a method for escaping them. 300 */ 301 need_quotes = (strchr(ze->zone_path, ':') != NULL); 302 303 (void) snprintf(line, sizeof (line), "%s:%s:%s%s%s\n", ze->zone_name, 304 zone_state_str(ze->zone_state), need_quotes ? "\"" : "", 305 ze->zone_path, need_quotes ? "\"" : ""); 306 for (;;) { 307 if (fgets(buf, sizeof (buf), index_file) == NULL) { 308 if (operation == PZE_ADD && !exists) 309 (void) fputs(line, tmp_file); 310 break; 311 } 312 (void) strlcpy(orig_buf, buf, sizeof (orig_buf)); 313 314 if ((cp = strpbrk(buf, "\r\n")) == NULL) { 315 /* this represents a line that's too long */ 316 continue; 317 } 318 *cp = '\0'; 319 cp = buf; 320 if (*cp == '#') { 321 /* skip comment lines */ 322 (void) fputs(orig_buf, tmp_file); 323 continue; 324 } 325 p = gettok(&cp); 326 if (p == NULL || *p == '\0' || strlen(p) > ZONENAME_MAX) { 327 /* 328 * empty or very long zone names are not allowed 329 */ 330 continue; 331 } 332 (void) strlcpy(zone, p, ZONENAME_MAX); 333 334 if (strcmp(zone, ze->zone_name) == 0) { 335 exists = B_TRUE; /* already there */ 336 if (operation == PZE_ADD) { 337 /* can't add same zone */ 338 goto error; 339 } else if (operation == PZE_MODIFY) { 340 char tmp_state[ZONE_STATE_MAXSTRLEN + 1]; 341 char *tmp_name; 342 343 if (ze->zone_state >= 0 && 344 strlen(ze->zone_path) > 0) { 345 /* use specified values */ 346 (void) fputs(line, tmp_file); 347 continue; 348 } 349 /* use existing value for state */ 350 p = gettok(&cp); 351 if (p == NULL || *p == '\0') { 352 /* state field should not be empty */ 353 goto error; 354 } 355 (void) strlcpy(tmp_state, 356 (ze->zone_state < 0) ? p : 357 zone_state_str(ze->zone_state), 358 sizeof (tmp_state)); 359 360 p = gettok(&cp); 361 362 /* 363 * If a new name is supplied, use it. 364 */ 365 if (strlen(ze->zone_newname) != 0) 366 tmp_name = ze->zone_newname; 367 else 368 tmp_name = ze->zone_name; 369 370 (void) fprintf(tmp_file, "%s:%s:%s%s%s\n", 371 tmp_name, tmp_state, 372 need_quotes ? "\"" : "", 373 (strlen(ze->zone_path) == 0) ? p : 374 ze->zone_path, need_quotes ? "\"" : ""); 375 } 376 /* else if (operation == PZE_REMOVE) { no-op } */ 377 } else { 378 (void) fputs(orig_buf, tmp_file); 379 } 380 } 381 382 (void) fclose(index_file); 383 if (fclose(tmp_file) != 0) { 384 (void) unlink(tmp_file_name); 385 free(tmp_file_name); 386 (void) unlock_index_file(lock_fd); 387 return (Z_MISC_FS); 388 } 389 (void) chmod(tmp_file_name, 0644); 390 if (rename(tmp_file_name, ZONE_INDEX_FILE) == -1) { 391 (void) unlink(tmp_file_name); 392 free(tmp_file_name); 393 (void) unlock_index_file(lock_fd); 394 if (errno == EACCES) 395 return (Z_ACCES); 396 return (Z_MISC_FS); 397 } 398 free(tmp_file_name); 399 if (unlock_index_file(lock_fd) != Z_OK) 400 return (Z_UNLOCKING_FILE); 401 return (Z_OK); 402 error: 403 (void) fclose(index_file); 404 (void) fclose(tmp_file); 405 (void) unlink(tmp_file_name); 406 free(tmp_file_name); 407 if (unlock_index_file(lock_fd) != Z_OK) 408 return (Z_UNLOCKING_FILE); 409 return (Z_UPDATING_INDEX); 410 } 411