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