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
132 /* zonename */
133 p = gettok(&cp);
134 if (*p == '\0' || strlen(p) >= ZONENAME_MAX) {
135 /*
136 * empty or very long zone names are not allowed
137 */
138 continue;
139 }
140 (void) strlcpy(ze->zone_name, p, ZONENAME_MAX);
141
142 /* state */
143 p = gettok(&cp);
144 if (*p == '\0') {
145 /* state field should not be empty */
146 continue;
147 }
148 errno = 0;
149 if (strcmp(p, ZONE_STATE_STR_CONFIGURED) == 0) {
150 ze->zone_state = ZONE_STATE_CONFIGURED;
151 } else if (strcmp(p, ZONE_STATE_STR_INCOMPLETE) == 0) {
152 ze->zone_state = ZONE_STATE_INCOMPLETE;
153 } else if (strcmp(p, ZONE_STATE_STR_INSTALLED) == 0) {
154 ze->zone_state = ZONE_STATE_INSTALLED;
155 } else {
156 continue;
157 }
158
159 /* zonepath */
160 p = gettok(&cp);
161 if (strlen(p) >= MAXPATHLEN) {
162 /* very long paths are not allowed */
163 continue;
164 }
165 (void) strlcpy(ze->zone_path, p, MAXPATHLEN);
166
167 /* uuid */
168 p = gettok(&cp);
169 if (uuid_parse(p, ze->zone_uuid) == -1)
170 uuid_clear(ze->zone_uuid);
171
172 /* brand [optional] */
173 p = gettok(&cp);
174 if (strlen(p) >= MAXNAMELEN) {
175 /* very long names are not allowed */
176 continue;
177 }
178 (void) strlcpy(ze->zone_brand, p, MAXNAMELEN);
179
180 /* IP type [optional] */
181 p = gettok(&cp);
182 if (strlen(p) >= MAXNAMELEN) {
183 /* very long names are not allowed */
184 continue;
185 }
186 ze->zone_iptype = ZS_SHARED;
187 if (*p == 'e') {
188 ze->zone_iptype = ZS_EXCLUSIVE;
189 }
190
191 /* debug ID [optional] */
192 p = gettok(&cp);
193 if (*p != '\0')
194 ze->zone_did = atoi(p);
195
196 break;
197 }
198
199 return (ze);
200 }
201
202 static boolean_t
203 path_common(char *path, size_t path_size, const char *stem)
204 {
205 const char *native_root = zone_get_nroot();
206
207 if (native_root == NULL || zonecfg_in_alt_root()) {
208 /*
209 * Do not prepend the native system root (e.g. "/native") if an
210 * alternative configuration root has been selected.
211 */
212 native_root = "";
213 }
214
215 return (snprintf(path, path_size, "%s%s%s", native_root, zonecfg_root,
306 *
307 * A zero-length ze->zone_path means leave the existing value
308 * unchanged; this is only meaningful when operation == PZE_MODIFY
309 * (i.e., it's bad on PZE_ADD and a no-op on PZE_REMOVE).
310 *
311 * A zero-length ze->zone_newname means leave the existing name
312 * unchanged; otherwise the zone is renamed to zone_newname. This is
313 * only meaningful when operation == PZE_MODIFY.
314 *
315 * Locking and unlocking is done via the functions above.
316 * The file itself is not modified in place; rather, a copy is made which
317 * is modified, then the copy is atomically renamed back to the main file.
318 */
319 int
320 putzoneent(struct zoneent *ze, zoneent_op_t operation)
321 {
322 FILE *index_file, *tmp_file;
323 char buf[MAX_INDEX_LEN];
324 int tmp_file_desc, lock_fd, err;
325 boolean_t exist, need_quotes;
326 char *cp, *tmpp;
327 char tmp_path[MAXPATHLEN];
328 char path[MAXPATHLEN];
329 char uuidstr[UUID_PRINTABLE_STRING_LENGTH];
330 size_t namelen;
331 const char *zone_name, *zone_state, *zone_path, *zone_uuid,
332 *zone_brand = "", *zone_iptype;
333 zoneid_t zone_did;
334
335 assert(ze != NULL);
336
337 /*
338 * Don't allow modification of Global Zone entry
339 * in index file
340 */
341 if ((operation == PZE_MODIFY) &&
342 (strcmp(ze->zone_name, GLOBAL_ZONENAME) == 0)) {
343 return (Z_OK);
344 }
345
346 if (operation == PZE_ADD &&
347 (ze->zone_state < 0 || strlen(ze->zone_path) == 0))
348 return (Z_INVAL);
349
350 if (operation != PZE_MODIFY && strlen(ze->zone_newname) != 0)
351 return (Z_INVAL);
352
353 if ((lock_fd = lock_index_file()) == -1)
366 }
367 (void) fchmod(tmp_file_desc, ZONE_INDEX_MODE);
368 (void) fchown(tmp_file_desc, ZONE_INDEX_UID, ZONE_INDEX_GID);
369 if ((tmp_file = fdopen(tmp_file_desc, "w")) == NULL) {
370 (void) close(tmp_file_desc);
371 err = Z_MISC_FS;
372 goto error;
373 }
374 if (!get_index_path(path, sizeof (path))) {
375 err = Z_MISC_FS;
376 goto error;
377 }
378 if ((index_file = fopen(path, "r")) == NULL) {
379 err = Z_MISC_FS;
380 goto error;
381 }
382
383 exist = B_FALSE;
384 zone_name = ze->zone_name;
385 namelen = strlen(zone_name);
386 zone_brand = ze->zone_brand;
387 zone_iptype = (ze->zone_iptype == ZS_SHARED ? "sh" : "ex");
388 zone_did = ze->zone_did;
389 for (;;) {
390 if (fgets(buf, sizeof (buf), index_file) == NULL) {
391 if (operation == PZE_ADD && !exist) {
392 zone_state = zone_state_str(ze->zone_state);
393 zone_path = ze->zone_path;
394 zone_uuid = "";
395 goto add_entry;
396 }
397 /*
398 * It's not considered an error to delete something
399 * that doesn't exist, but we can't modify a missing
400 * record.
401 */
402 if (operation == PZE_MODIFY && !exist) {
403 err = Z_UPDATING_INDEX;
404 goto error;
405 }
406 break;
407 }
408
423 /* this represents a line that's too long; delete */
424 continue;
425 }
426 *cp = '\0';
427
428 /*
429 * Skip over the zone name. Because we've already matched the
430 * target zone (above), we know for certain here that the zone
431 * name is present and correctly formed. No need to check.
432 */
433 cp = strchr(buf, ':') + 1;
434
435 zone_state = gettok(&cp);
436 if (*zone_state == '\0') {
437 /* state field should not be empty */
438 err = Z_UPDATING_INDEX;
439 goto error;
440 }
441 zone_path = gettok(&cp);
442 zone_uuid = gettok(&cp);
443 zone_brand = gettok(&cp);
444 zone_iptype = gettok(&cp);
445 tmpp = gettok(&cp);
446 if (*tmpp != '\0')
447 zone_did = atoi(tmpp);
448
449 switch (operation) {
450 case PZE_ADD:
451 /* can't add same zone */
452 err = Z_UPDATING_INDEX;
453 goto error;
454
455 case PZE_MODIFY:
456 /*
457 * If the caller specified a new state for the zone,
458 * then use that. Otherwise, use the current state.
459 */
460 if (ze->zone_state >= 0) {
461 zone_state = zone_state_str(ze->zone_state);
462 }
463
464 /* If a new name is supplied, use it. */
465 if (ze->zone_newname[0] != '\0')
466 zone_name = ze->zone_newname;
467
468 if (ze->zone_path[0] != '\0')
469 zone_path = ze->zone_path;
470
471 /* If new UUID provided, replace it */
472 if (!uuid_is_null(ze->zone_uuid)) {
473 uuid_unparse(ze->zone_uuid, uuidstr);
474 zone_uuid = uuidstr;
475 }
476
477 /* If a brand is supplied, use it. */
478 if (ze->zone_brand[0] != '\0') {
479 zone_brand = ze->zone_brand;
480
481 /*
482 * Since the brand, iptype and did are optional,
483 * we we only reset the iptype and did if the
484 * brand is provided.
485 */
486 zone_iptype = (ze->zone_iptype == ZS_SHARED ?
487 "sh" : "ex");
488 zone_did = ze->zone_did;
489 }
490
491 break;
492
493 case PZE_REMOVE:
494 default:
495 continue;
496 }
497
498 add_entry:
499 /*
500 * If the entry in the file is in greater than configured
501 * state, then we must have a UUID. Make sure that we do.
502 * (Note that the file entry is only tokenized, not fully
503 * parsed, so we need to do a string comparison here.)
504 */
505 if (strcmp(zone_state, ZONE_STATE_STR_CONFIGURED) != 0 &&
506 *zone_uuid == '\0') {
507 if (uuid_is_null(ze->zone_uuid))
508 uuid_generate(ze->zone_uuid);
509 uuid_unparse(ze->zone_uuid, uuidstr);
510 zone_uuid = uuidstr;
511 }
512 /*
513 * We need to quote a path that contains a ":"; this should
514 * only affect the zonepath, as zone names do not allow such
515 * characters, and zone states do not have them either. Same
516 * with double-quotes themselves: they are not allowed in zone
517 * names, and do not occur in zone states, and in theory should
518 * never occur in a zonepath since zonecfg does not support a
519 * method for escaping them.
520 */
521 need_quotes = (strchr(zone_path, ':') != NULL);
522
523 if (*zone_brand != '\0') {
524 (void) fprintf(tmp_file, "%s:%s:%s%s%s:%s:%s:%s:%d\n",
525 zone_name, zone_state, need_quotes ? "\"" : "",
526 zone_path, need_quotes ? "\"" : "", zone_uuid,
527 zone_brand, zone_iptype, zone_did);
528 } else {
529 (void) fprintf(tmp_file, "%s:%s:%s%s%s:%s\n", zone_name,
530 zone_state, need_quotes ? "\"" : "", zone_path,
531 need_quotes ? "\"" : "", zone_uuid);
532 }
533 exist = B_TRUE;
534 }
535
536 (void) fclose(index_file);
537 index_file = NULL;
538 if (fclose(tmp_file) != 0) {
539 tmp_file = NULL;
540 err = Z_MISC_FS;
541 goto error;
542 }
543 tmp_file = NULL;
544 if (rename(tmp_path, path) == -1) {
545 err = errno == EACCES ? Z_ACCES : Z_MISC_FS;
546 goto error;
547 }
548 if (unlock_index_file(lock_fd) != Z_OK)
549 return (Z_UNLOCKING_FILE);
550 return (Z_OK);
551
552 error:
|
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 == '\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 == '\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
156 p = gettok(&cp);
157 if (strlen(p) >= MAXPATHLEN) {
158 /* very long paths are not allowed */
159 continue;
160 }
161 (void) strlcpy(ze->zone_path, p, MAXPATHLEN);
162
163 p = gettok(&cp);
164 if (uuid_parse(p, ze->zone_uuid) == -1)
165 uuid_clear(ze->zone_uuid);
166
167 break;
168 }
169
170 return (ze);
171 }
172
173 static boolean_t
174 path_common(char *path, size_t path_size, const char *stem)
175 {
176 const char *native_root = zone_get_nroot();
177
178 if (native_root == NULL || zonecfg_in_alt_root()) {
179 /*
180 * Do not prepend the native system root (e.g. "/native") if an
181 * alternative configuration root has been selected.
182 */
183 native_root = "";
184 }
185
186 return (snprintf(path, path_size, "%s%s%s", native_root, zonecfg_root,
277 *
278 * A zero-length ze->zone_path means leave the existing value
279 * unchanged; this is only meaningful when operation == PZE_MODIFY
280 * (i.e., it's bad on PZE_ADD and a no-op on PZE_REMOVE).
281 *
282 * A zero-length ze->zone_newname means leave the existing name
283 * unchanged; otherwise the zone is renamed to zone_newname. This is
284 * only meaningful when operation == PZE_MODIFY.
285 *
286 * Locking and unlocking is done via the functions above.
287 * The file itself is not modified in place; rather, a copy is made which
288 * is modified, then the copy is atomically renamed back to the main file.
289 */
290 int
291 putzoneent(struct zoneent *ze, zoneent_op_t operation)
292 {
293 FILE *index_file, *tmp_file;
294 char buf[MAX_INDEX_LEN];
295 int tmp_file_desc, lock_fd, err;
296 boolean_t exist, need_quotes;
297 char *cp;
298 char tmp_path[MAXPATHLEN];
299 char path[MAXPATHLEN];
300 char uuidstr[UUID_PRINTABLE_STRING_LENGTH];
301 size_t namelen;
302 const char *zone_name, *zone_state, *zone_path, *zone_uuid;
303
304 assert(ze != NULL);
305
306 /*
307 * Don't allow modification of Global Zone entry
308 * in index file
309 */
310 if ((operation == PZE_MODIFY) &&
311 (strcmp(ze->zone_name, GLOBAL_ZONENAME) == 0)) {
312 return (Z_OK);
313 }
314
315 if (operation == PZE_ADD &&
316 (ze->zone_state < 0 || strlen(ze->zone_path) == 0))
317 return (Z_INVAL);
318
319 if (operation != PZE_MODIFY && strlen(ze->zone_newname) != 0)
320 return (Z_INVAL);
321
322 if ((lock_fd = lock_index_file()) == -1)
335 }
336 (void) fchmod(tmp_file_desc, ZONE_INDEX_MODE);
337 (void) fchown(tmp_file_desc, ZONE_INDEX_UID, ZONE_INDEX_GID);
338 if ((tmp_file = fdopen(tmp_file_desc, "w")) == NULL) {
339 (void) close(tmp_file_desc);
340 err = Z_MISC_FS;
341 goto error;
342 }
343 if (!get_index_path(path, sizeof (path))) {
344 err = Z_MISC_FS;
345 goto error;
346 }
347 if ((index_file = fopen(path, "r")) == NULL) {
348 err = Z_MISC_FS;
349 goto error;
350 }
351
352 exist = B_FALSE;
353 zone_name = ze->zone_name;
354 namelen = strlen(zone_name);
355 for (;;) {
356 if (fgets(buf, sizeof (buf), index_file) == NULL) {
357 if (operation == PZE_ADD && !exist) {
358 zone_state = zone_state_str(ze->zone_state);
359 zone_path = ze->zone_path;
360 zone_uuid = "";
361 goto add_entry;
362 }
363 /*
364 * It's not considered an error to delete something
365 * that doesn't exist, but we can't modify a missing
366 * record.
367 */
368 if (operation == PZE_MODIFY && !exist) {
369 err = Z_UPDATING_INDEX;
370 goto error;
371 }
372 break;
373 }
374
389 /* this represents a line that's too long; delete */
390 continue;
391 }
392 *cp = '\0';
393
394 /*
395 * Skip over the zone name. Because we've already matched the
396 * target zone (above), we know for certain here that the zone
397 * name is present and correctly formed. No need to check.
398 */
399 cp = strchr(buf, ':') + 1;
400
401 zone_state = gettok(&cp);
402 if (*zone_state == '\0') {
403 /* state field should not be empty */
404 err = Z_UPDATING_INDEX;
405 goto error;
406 }
407 zone_path = gettok(&cp);
408 zone_uuid = gettok(&cp);
409
410 switch (operation) {
411 case PZE_ADD:
412 /* can't add same zone */
413 err = Z_UPDATING_INDEX;
414 goto error;
415
416 case PZE_MODIFY:
417 /*
418 * If the caller specified a new state for the zone,
419 * then use that. Otherwise, use the current state.
420 */
421 if (ze->zone_state >= 0) {
422 zone_state = zone_state_str(ze->zone_state);
423
424 /*
425 * If the caller is uninstalling this zone,
426 * then wipe out the uuid. The zone's contents
427 * are no longer known.
428 */
429 if (ze->zone_state < ZONE_STATE_INSTALLED)
430 zone_uuid = "";
431 }
432
433 /* If a new name is supplied, use it. */
434 if (ze->zone_newname[0] != '\0')
435 zone_name = ze->zone_newname;
436
437 if (ze->zone_path[0] != '\0')
438 zone_path = ze->zone_path;
439 break;
440
441 case PZE_REMOVE:
442 default:
443 continue;
444 }
445
446 add_entry:
447 /*
448 * If the entry in the file is in greater than configured
449 * state, then we must have a UUID. Make sure that we do.
450 * (Note that the file entry is only tokenized, not fully
451 * parsed, so we need to do a string comparison here.)
452 */
453 if (strcmp(zone_state, ZONE_STATE_STR_CONFIGURED) != 0 &&
454 *zone_uuid == '\0') {
455 if (uuid_is_null(ze->zone_uuid))
456 uuid_generate(ze->zone_uuid);
457 uuid_unparse(ze->zone_uuid, uuidstr);
458 zone_uuid = uuidstr;
459 }
460 /*
461 * We need to quote a path that contains a ":"; this should
462 * only affect the zonepath, as zone names do not allow such
463 * characters, and zone states do not have them either. Same
464 * with double-quotes themselves: they are not allowed in zone
465 * names, and do not occur in zone states, and in theory should
466 * never occur in a zonepath since zonecfg does not support a
467 * method for escaping them.
468 */
469 need_quotes = (strchr(zone_path, ':') != NULL);
470 (void) fprintf(tmp_file, "%s:%s:%s%s%s:%s\n", zone_name,
471 zone_state, need_quotes ? "\"" : "", zone_path,
472 need_quotes ? "\"" : "", zone_uuid);
473 exist = B_TRUE;
474 }
475
476 (void) fclose(index_file);
477 index_file = NULL;
478 if (fclose(tmp_file) != 0) {
479 tmp_file = NULL;
480 err = Z_MISC_FS;
481 goto error;
482 }
483 tmp_file = NULL;
484 if (rename(tmp_path, path) == -1) {
485 err = errno == EACCES ? Z_ACCES : Z_MISC_FS;
486 goto error;
487 }
488 if (unlock_index_file(lock_fd) != Z_OK)
489 return (Z_UNLOCKING_FILE);
490 return (Z_OK);
491
492 error:
|