dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libbe / common / be_utils.c
blob678e0bd5af05c1ced4bc441d7f35a738a42025c5
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
25 * Copyright 2016 Toomas Soome <tsoome@me.com>
26 * Copyright (c) 2015 by Delphix. All rights reserved.
31 * System includes
33 #include <assert.h>
34 #include <errno.h>
35 #include <libgen.h>
36 #include <libintl.h>
37 #include <libnvpair.h>
38 #include <libzfs.h>
39 #include <libgen.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sys/stat.h>
44 #include <sys/types.h>
45 #include <sys/vfstab.h>
46 #include <sys/param.h>
47 #include <sys/systeminfo.h>
48 #include <ctype.h>
49 #include <time.h>
50 #include <unistd.h>
51 #include <fcntl.h>
52 #include <deflt.h>
53 #include <wait.h>
54 #include <libdevinfo.h>
55 #include <libgen.h>
57 #include <libbe.h>
58 #include <libbe_priv.h>
59 #include <boot_utils.h>
60 #include <ficl.h>
61 #include <ficlplatform/emu.h>
63 /* Private function prototypes */
64 static int update_dataset(char *, int, char *, char *, char *);
65 static int _update_vfstab(char *, char *, char *, char *, be_fs_list_data_t *);
66 static int be_open_menu(char *, char *, FILE **, char *, boolean_t);
67 static int be_create_menu(char *, char *, FILE **, char *);
68 static char *be_get_auto_name(char *, char *, boolean_t);
71 * Global error printing
73 boolean_t do_print = B_FALSE;
76 * Private datatypes
78 typedef struct zone_be_name_cb_data {
79 char *base_be_name;
80 int num;
81 } zone_be_name_cb_data_t;
83 /* ******************************************************************** */
84 /* Public Functions */
85 /* ******************************************************************** */
88 * Callback for ficl to suppress all output from ficl, as we do not
89 * want to confuse user with messages from ficl, and we are only
90 * checking results from function calls.
92 /*ARGSUSED*/
93 static void
94 ficlSuppressTextOutput(ficlCallback *cb, char *text)
96 /* This function is intentionally doing nothing. */
100 * Function: be_get_boot_args
101 * Description: Returns the fast boot argument string for enumerated BE.
102 * Parameters:
103 * fbarg - pointer to argument string.
104 * entry - index of BE.
105 * Returns:
106 * fast boot argument string.
107 * Scope:
108 * Public
111 be_get_boot_args(char **fbarg, int entry)
113 be_node_list_t *node, *be_nodes = NULL;
114 be_transaction_data_t bt = {0};
115 char *mountpoint = NULL;
116 boolean_t be_mounted = B_FALSE;
117 int ret = BE_SUCCESS;
118 int index;
119 ficlVm *vm;
121 *fbarg = NULL;
122 if (!be_zfs_init())
123 return (BE_ERR_INIT);
126 * need pool name, menu.lst has entries from our pool only
128 ret = be_find_current_be(&bt);
129 if (ret != BE_SUCCESS) {
130 be_zfs_fini();
131 return (ret);
134 ret = _be_list(NULL, &be_nodes);
135 if (ret != BE_SUCCESS)
136 goto done;
139 * iterate through be_nodes,
140 * if entry == -1, stop if be_active_on_boot,
141 * else stop if index == entry.
143 index = 0;
144 for (node = be_nodes; node != NULL; node = node->be_next_node) {
145 if (strcmp(node->be_rpool, bt.obe_zpool) != 0)
146 continue;
147 if (entry == BE_ENTRY_DEFAULT &&
148 node->be_active_on_boot == B_TRUE)
149 break;
150 if (index == entry)
151 break;
152 index++;
154 if (node == NULL) {
155 be_free_list(be_nodes);
156 ret = BE_ERR_NOENT;
157 goto done;
160 /* try to mount inactive be */
161 if (node->be_active == B_FALSE) {
162 ret = _be_mount(node->be_node_name, &mountpoint,
163 BE_MOUNT_FLAG_NO_ZONES);
164 if (ret != BE_SUCCESS && ret != BE_ERR_MOUNTED) {
165 be_free_list(be_nodes);
166 goto done;
167 } else
168 be_mounted = B_TRUE;
171 vm = bf_init("", ficlSuppressTextOutput);
172 if (vm != NULL) {
174 * zfs MAXNAMELEN is 256, so we need to pick buf large enough
175 * to contain such names.
177 char buf[MAXNAMELEN * 2];
178 char *kernel_options = NULL;
179 char *kernel = NULL;
180 char *tmp;
181 zpool_handle_t *zph;
184 * just try to interpret following words. on error
185 * we will be missing kernelname, and will get out.
187 (void) snprintf(buf, sizeof (buf), "set currdev=zfs:%s:",
188 node->be_root_ds);
189 ret = ficlVmEvaluate(vm, buf);
190 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
191 be_print_err(gettext("be_get_boot_args: error "
192 "interpreting boot config: %d\n"), ret);
193 bf_fini();
194 ret = BE_ERR_NO_MENU;
195 goto cleanup;
197 (void) snprintf(buf, sizeof (buf),
198 "include /boot/forth/loader.4th");
199 ret = ficlVmEvaluate(vm, buf);
200 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
201 be_print_err(gettext("be_get_boot_args: error "
202 "interpreting boot config: %d\n"), ret);
203 bf_fini();
204 ret = BE_ERR_NO_MENU;
205 goto cleanup;
207 (void) snprintf(buf, sizeof (buf), "start");
208 ret = ficlVmEvaluate(vm, buf);
209 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
210 be_print_err(gettext("be_get_boot_args: error "
211 "interpreting boot config: %d\n"), ret);
212 bf_fini();
213 ret = BE_ERR_NO_MENU;
214 goto cleanup;
216 (void) snprintf(buf, sizeof (buf), "boot");
217 ret = ficlVmEvaluate(vm, buf);
218 bf_fini();
219 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
220 be_print_err(gettext("be_get_boot_args: error "
221 "interpreting boot config: %d\n"), ret);
222 ret = BE_ERR_NO_MENU;
223 goto cleanup;
226 kernel_options = getenv("boot-args");
227 kernel = getenv("kernelname");
229 if (kernel == NULL) {
230 be_print_err(gettext("be_get_boot_args: no kernel\n"));
231 ret = BE_ERR_NOENT;
232 goto cleanup;
235 if ((zph = zpool_open(g_zfs, node->be_rpool)) == NULL) {
236 be_print_err(gettext("be_get_boot_args: failed to "
237 "open root pool (%s): %s\n"), node->be_rpool,
238 libzfs_error_description(g_zfs));
239 ret = zfs_err_to_be_err(g_zfs);
240 goto cleanup;
242 ret = zpool_get_physpath(zph, buf, sizeof (buf));
243 zpool_close(zph);
244 if (ret != 0) {
245 be_print_err(gettext("be_get_boot_args: failed to "
246 "get physpath\n"));
247 goto cleanup;
250 /* zpool_get_physpath() can return space separated list */
251 tmp = buf;
252 tmp = strsep(&tmp, " ");
254 if (kernel_options == NULL || *kernel_options == '\0')
255 (void) asprintf(fbarg, "/ %s "
256 "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel,
257 node->be_root_ds, tmp);
258 else
259 (void) asprintf(fbarg, "/ %s %s "
260 "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel,
261 kernel_options, node->be_root_ds, tmp);
263 if (fbarg == NULL)
264 ret = BE_ERR_NOMEM;
265 else
266 ret = 0;
267 } else
268 ret = BE_ERR_NOMEM;
269 cleanup:
270 if (be_mounted == B_TRUE)
271 (void) _be_unmount(node->be_node_name, BE_UNMOUNT_FLAG_FORCE);
272 be_free_list(be_nodes);
273 done:
274 free(mountpoint);
275 free(bt.obe_name);
276 free(bt.obe_root_ds);
277 free(bt.obe_zpool);
278 free(bt.obe_snap_name);
279 free(bt.obe_altroot);
280 be_zfs_fini();
281 return (ret);
285 * Function: be_max_avail
286 * Description: Returns the available size for the zfs dataset passed in.
287 * Parameters:
288 * dataset - The dataset we want to get the available space for.
289 * ret - The available size will be returned in this.
290 * Returns:
291 * The error returned by the zfs get property function.
292 * Scope:
293 * Public
296 be_max_avail(char *dataset, uint64_t *ret)
298 zfs_handle_t *zhp;
299 int err = 0;
301 /* Initialize libzfs handle */
302 if (!be_zfs_init())
303 return (BE_ERR_INIT);
305 zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET);
306 if (zhp == NULL) {
308 * The zfs_open failed return an error
310 err = zfs_err_to_be_err(g_zfs);
311 } else {
312 err = be_maxsize_avail(zhp, ret);
314 ZFS_CLOSE(zhp);
315 be_zfs_fini();
316 return (err);
320 * Function: libbe_print_errors
321 * Description: Turns on/off error output for the library.
322 * Parameter:
323 * set_do_print - Boolean that turns library error
324 * printing on or off.
325 * Returns:
326 * None
327 * Scope:
328 * Public;
330 void
331 libbe_print_errors(boolean_t set_do_print)
333 do_print = set_do_print;
336 /* ******************************************************************** */
337 /* Semi-Private Functions */
338 /* ******************************************************************** */
341 * Function: be_zfs_init
342 * Description: Initializes the libary global libzfs handle.
343 * Parameters:
344 * None
345 * Returns:
346 * B_TRUE - Success
347 * B_FALSE - Failure
348 * Scope:
349 * Semi-private (library wide use only)
351 boolean_t
352 be_zfs_init(void)
354 be_zfs_fini();
356 if ((g_zfs = libzfs_init()) == NULL) {
357 be_print_err(gettext("be_zfs_init: failed to initialize ZFS "
358 "library\n"));
359 return (B_FALSE);
362 return (B_TRUE);
366 * Function: be_zfs_fini
367 * Description: Closes the library global libzfs handle if it currently open.
368 * Parameter:
369 * None
370 * Returns:
371 * None
372 * Scope:
373 * Semi-private (library wide use only)
375 void
376 be_zfs_fini(void)
378 if (g_zfs)
379 libzfs_fini(g_zfs);
381 g_zfs = NULL;
385 * Function: be_get_defaults
386 * Description: Open defaults and gets be default paramets
387 * Parameters:
388 * defaults - be defaults struct
389 * Returns:
390 * None
391 * Scope:
392 * Semi-private (library wide use only)
394 void
395 be_get_defaults(struct be_defaults *defaults)
397 void *defp;
399 defaults->be_deflt_rpool_container = B_FALSE;
400 defaults->be_deflt_bename_starts_with[0] = '\0';
402 if ((defp = defopen_r(BE_DEFAULTS)) != NULL) {
403 const char *res = defread_r(BE_DFLT_BENAME_STARTS, defp);
404 if (res != NULL && res[0] != '\0') {
405 (void) strlcpy(defaults->be_deflt_bename_starts_with,
406 res, ZFS_MAX_DATASET_NAME_LEN);
407 defaults->be_deflt_rpool_container = B_TRUE;
409 defclose_r(defp);
414 * Function: be_make_root_ds
415 * Description: Generate string for BE's root dataset given the pool
416 * it lives in and the BE name.
417 * Parameters:
418 * zpool - pointer zpool name.
419 * be_name - pointer to BE name.
420 * be_root_ds - pointer to buffer to return BE root dataset in.
421 * be_root_ds_size - size of be_root_ds
422 * Returns:
423 * None
424 * Scope:
425 * Semi-private (library wide use only)
427 void
428 be_make_root_ds(const char *zpool, const char *be_name, char *be_root_ds,
429 int be_root_ds_size)
431 struct be_defaults be_defaults;
432 be_get_defaults(&be_defaults);
433 char *root_ds = NULL;
435 if (getzoneid() == GLOBAL_ZONEID) {
436 if (be_defaults.be_deflt_rpool_container) {
437 (void) snprintf(be_root_ds, be_root_ds_size,
438 "%s/%s", zpool, be_name);
439 } else {
440 (void) snprintf(be_root_ds, be_root_ds_size,
441 "%s/%s/%s", zpool, BE_CONTAINER_DS_NAME, be_name);
443 } else {
445 * In non-global zone we can use path from mounted root dataset
446 * to generate BE's root dataset string.
448 if ((root_ds = be_get_ds_from_dir("/")) != NULL) {
449 (void) snprintf(be_root_ds, be_root_ds_size, "%s/%s",
450 dirname(root_ds), be_name);
451 } else {
452 be_print_err(gettext("be_make_root_ds: zone root "
453 "dataset is not mounted\n"));
454 return;
460 * Function: be_make_container_ds
461 * Description: Generate string for the BE container dataset given a pool name.
462 * Parameters:
463 * zpool - pointer zpool name.
464 * container_ds - pointer to buffer to return BE container
465 * dataset in.
466 * container_ds_size - size of container_ds
467 * Returns:
468 * None
469 * Scope:
470 * Semi-private (library wide use only)
472 void
473 be_make_container_ds(const char *zpool, char *container_ds,
474 int container_ds_size)
476 struct be_defaults be_defaults;
477 be_get_defaults(&be_defaults);
478 char *root_ds = NULL;
480 if (getzoneid() == GLOBAL_ZONEID) {
481 if (be_defaults.be_deflt_rpool_container) {
482 (void) snprintf(container_ds, container_ds_size,
483 "%s", zpool);
484 } else {
485 (void) snprintf(container_ds, container_ds_size,
486 "%s/%s", zpool, BE_CONTAINER_DS_NAME);
488 } else {
489 if ((root_ds = be_get_ds_from_dir("/")) != NULL) {
490 (void) strlcpy(container_ds, dirname(root_ds),
491 container_ds_size);
492 } else {
493 be_print_err(gettext("be_make_container_ds: zone root "
494 "dataset is not mounted\n"));
495 return;
501 * Function: be_make_name_from_ds
502 * Description: This function takes a dataset name and strips off the
503 * BE container dataset portion from the beginning. The
504 * returned name is allocated in heap storage, so the caller
505 * is responsible for freeing it.
506 * Parameters:
507 * dataset - dataset to get name from.
508 * rc_loc - dataset underwhich the root container dataset lives.
509 * Returns:
510 * name of dataset relative to BE container dataset.
511 * NULL if dataset is not under a BE root dataset.
512 * Scope:
513 * Semi-primate (library wide use only)
515 char *
516 be_make_name_from_ds(const char *dataset, char *rc_loc)
518 char ds[ZFS_MAX_DATASET_NAME_LEN];
519 char *tok = NULL;
520 char *name = NULL;
521 struct be_defaults be_defaults;
522 int rlen = strlen(rc_loc);
524 be_get_defaults(&be_defaults);
527 * First token is the location of where the root container dataset
528 * lives; it must match rc_loc.
530 if (strncmp(dataset, rc_loc, rlen) == 0 && dataset[rlen] == '/')
531 (void) strlcpy(ds, dataset + rlen + 1, sizeof (ds));
532 else
533 return (NULL);
535 if (be_defaults.be_deflt_rpool_container) {
536 if ((name = strdup(ds)) == NULL) {
537 be_print_err(gettext("be_make_name_from_ds: "
538 "memory allocation failed\n"));
539 return (NULL);
541 } else {
542 /* Second token must be BE container dataset name */
543 if ((tok = strtok(ds, "/")) == NULL ||
544 strcmp(tok, BE_CONTAINER_DS_NAME) != 0)
545 return (NULL);
547 /* Return the remaining token if one exists */
548 if ((tok = strtok(NULL, "")) == NULL)
549 return (NULL);
551 if ((name = strdup(tok)) == NULL) {
552 be_print_err(gettext("be_make_name_from_ds: "
553 "memory allocation failed\n"));
554 return (NULL);
558 return (name);
562 * Function: be_maxsize_avail
563 * Description: Returns the available size for the zfs handle passed in.
564 * Parameters:
565 * zhp - A pointer to the open zfs handle.
566 * ret - The available size will be returned in this.
567 * Returns:
568 * The error returned by the zfs get property function.
569 * Scope:
570 * Semi-private (library wide use only)
573 be_maxsize_avail(zfs_handle_t *zhp, uint64_t *ret)
575 return ((*ret = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE)));
579 * Function: be_append_menu
580 * Description: Appends an entry for a BE into the menu.lst.
581 * Parameters:
582 * be_name - pointer to name of BE to add boot menu entry for.
583 * be_root_pool - pointer to name of pool BE lives in.
584 * boot_pool - Used if the pool containing the menu is
585 * different than the one containing the BE. This
586 * will normally be NULL.
587 * be_orig_root_ds - The root dataset for the BE. This is
588 * used to check to see if an entry already exists
589 * for this BE.
590 * description - pointer to description of BE to be added in
591 * the title line for this BEs entry.
592 * Returns:
593 * BE_SUCCESS - Success
594 * be_errno_t - Failure
595 * Scope:
596 * Semi-private (library wide use only)
599 be_append_menu(char *be_name, char *be_root_pool, char *boot_pool,
600 char *be_orig_root_ds, char *description)
602 zfs_handle_t *zhp = NULL;
603 char menu_file[MAXPATHLEN];
604 char be_root_ds[MAXPATHLEN];
605 char line[BUFSIZ];
606 char temp_line[BUFSIZ];
607 char title[MAXPATHLEN];
608 char *entries[BUFSIZ];
609 char *tmp_entries[BUFSIZ];
610 char *pool_mntpnt = NULL;
611 char *ptmp_mntpnt = NULL;
612 char *orig_mntpnt = NULL;
613 boolean_t found_be = B_FALSE;
614 boolean_t found_orig_be = B_FALSE;
615 boolean_t found_title = B_FALSE;
616 boolean_t pool_mounted = B_FALSE;
617 boolean_t collect_lines = B_FALSE;
618 FILE *menu_fp = NULL;
619 int err = 0, ret = BE_SUCCESS;
620 int i, num_tmp_lines = 0, num_lines = 0;
622 if (be_name == NULL || be_root_pool == NULL)
623 return (BE_ERR_INVAL);
625 if (boot_pool == NULL)
626 boot_pool = be_root_pool;
628 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
629 be_print_err(gettext("be_append_menu: failed to open "
630 "pool dataset for %s: %s\n"), be_root_pool,
631 libzfs_error_description(g_zfs));
632 return (zfs_err_to_be_err(g_zfs));
636 * Check to see if the pool's dataset is mounted. If it isn't we'll
637 * attempt to mount it.
639 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
640 &pool_mounted)) != BE_SUCCESS) {
641 be_print_err(gettext("be_append_menu: pool dataset "
642 "(%s) could not be mounted\n"), be_root_pool);
643 ZFS_CLOSE(zhp);
644 return (ret);
648 * Get the mountpoint for the root pool dataset.
650 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
651 be_print_err(gettext("be_append_menu: pool "
652 "dataset (%s) is not mounted. Can't append "
653 "the BE into the boot menu.\n"), be_root_pool);
654 ret = BE_ERR_NO_MENU;
655 goto cleanup;
658 (void) snprintf(menu_file, sizeof (menu_file),
659 "%s%s", pool_mntpnt, BE_SPARC_MENU);
661 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
664 * Iterate through menu first to make sure the BE doesn't already
665 * have an entry in the menu.
667 * Additionally while iterating through the menu, if we have an
668 * original root dataset for a BE we're cloning from, we need to keep
669 * track of that BE's menu entry. We will then use the lines from
670 * that entry to create the entry for the new BE.
672 if ((ret = be_open_menu(be_root_pool, menu_file,
673 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
674 goto cleanup;
675 } else if (menu_fp == NULL) {
676 ret = BE_ERR_NO_MENU;
677 goto cleanup;
680 free(pool_mntpnt);
681 pool_mntpnt = NULL;
683 while (fgets(line, BUFSIZ, menu_fp)) {
684 char *tok = NULL;
686 (void) strlcpy(temp_line, line, BUFSIZ);
687 tok = strtok(line, BE_WHITE_SPACE);
689 if (tok == NULL || tok[0] == '#') {
690 continue;
691 } else if (strcmp(tok, "title") == 0) {
692 collect_lines = B_FALSE;
693 if ((tok = strtok(NULL, "\n")) == NULL)
694 (void) strlcpy(title, "", sizeof (title));
695 else
696 (void) strlcpy(title, tok, sizeof (title));
697 found_title = B_TRUE;
699 if (num_tmp_lines != 0) {
700 for (i = 0; i < num_tmp_lines; i++) {
701 free(tmp_entries[i]);
702 tmp_entries[i] = NULL;
704 num_tmp_lines = 0;
706 } else if (strcmp(tok, "bootfs") == 0) {
707 char *bootfs = strtok(NULL, BE_WHITE_SPACE);
708 found_title = B_FALSE;
709 if (bootfs == NULL)
710 continue;
712 if (strcmp(bootfs, be_root_ds) == 0) {
713 found_be = B_TRUE;
714 break;
717 if (be_orig_root_ds != NULL &&
718 strcmp(bootfs, be_orig_root_ds) == 0 &&
719 !found_orig_be) {
720 char str[BUFSIZ];
721 found_orig_be = B_TRUE;
722 num_lines = 0;
724 * Store the new title line
726 (void) snprintf(str, BUFSIZ, "title %s\n",
727 description ? description : be_name);
728 entries[num_lines] = strdup(str);
729 num_lines++;
731 * If there are any lines between the title
732 * and the bootfs line store these. Also
733 * free the temporary lines.
735 for (i = 0; i < num_tmp_lines; i++) {
736 entries[num_lines] = tmp_entries[i];
737 tmp_entries[i] = NULL;
738 num_lines++;
740 num_tmp_lines = 0;
742 * Store the new bootfs line.
744 (void) snprintf(str, BUFSIZ, "bootfs %s\n",
745 be_root_ds);
746 entries[num_lines] = strdup(str);
747 num_lines++;
748 collect_lines = B_TRUE;
750 } else if (found_orig_be && collect_lines) {
752 * get the rest of the lines for the original BE and
753 * store them.
755 entries[num_lines] = strdup(temp_line);
756 num_lines++;
757 } else if (found_title && !found_orig_be) {
758 tmp_entries[num_tmp_lines] = strdup(temp_line);
759 num_tmp_lines++;
763 (void) fclose(menu_fp);
765 if (found_be) {
767 * If an entry for this BE was already in the menu, then if
768 * that entry's title matches what we would have put in
769 * return success. Otherwise return failure.
771 char *new_title = description ? description : be_name;
773 if (strcmp(title, new_title) == 0) {
774 ret = BE_SUCCESS;
775 goto cleanup;
776 } else {
777 if (be_remove_menu(be_name, be_root_pool,
778 boot_pool) != BE_SUCCESS) {
779 be_print_err(gettext("be_append_menu: "
780 "Failed to remove existing unusable "
781 "entry '%s' in boot menu.\n"), be_name);
782 ret = BE_ERR_BE_EXISTS;
783 goto cleanup;
788 /* Append BE entry to the end of the file */
789 menu_fp = fopen(menu_file, "a+");
790 err = errno;
791 if (menu_fp == NULL) {
792 be_print_err(gettext("be_append_menu: failed "
793 "to open menu.lst file %s\n"), menu_file);
794 ret = errno_to_be_err(err);
795 goto cleanup;
798 if (found_orig_be) {
800 * write out all the stored lines
802 for (i = 0; i < num_lines; i++) {
803 (void) fprintf(menu_fp, "%s", entries[i]);
804 free(entries[i]);
806 num_lines = 0;
807 ret = BE_SUCCESS;
808 } else {
809 (void) fprintf(menu_fp, "title %s\n",
810 description ? description : be_name);
811 (void) fprintf(menu_fp, "bootfs %s\n", be_root_ds);
812 ret = BE_SUCCESS;
814 (void) fclose(menu_fp);
815 cleanup:
816 if (pool_mounted) {
817 int err = BE_SUCCESS;
818 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
819 if (ret == BE_SUCCESS)
820 ret = err;
821 free(orig_mntpnt);
822 free(ptmp_mntpnt);
824 ZFS_CLOSE(zhp);
825 if (num_tmp_lines > 0) {
826 for (i = 0; i < num_tmp_lines; i++) {
827 free(tmp_entries[i]);
828 tmp_entries[i] = NULL;
831 if (num_lines > 0) {
832 for (i = 0; i < num_lines; i++) {
833 free(entries[i]);
834 entries[i] = NULL;
837 return (ret);
841 * Function: be_remove_menu
842 * Description: Removes a BE's entry from a menu.lst file.
843 * Parameters:
844 * be_name - the name of BE whose entry is to be removed from
845 * the menu.lst file.
846 * be_root_pool - the pool that be_name lives in.
847 * boot_pool - the pool where the BE is, if different than
848 * the pool containing the boot menu. If this is
849 * NULL it will be set to be_root_pool.
850 * Returns:
851 * BE_SUCCESS - Success
852 * be_errno_t - Failure
853 * Scope:
854 * Semi-private (library wide use only)
857 be_remove_menu(char *be_name, char *be_root_pool, char *boot_pool)
859 zfs_handle_t *zhp = NULL;
860 char be_root_ds[MAXPATHLEN];
861 char **buffer = NULL;
862 char menu_buf[BUFSIZ];
863 char menu[MAXPATHLEN];
864 char *pool_mntpnt = NULL;
865 char *ptmp_mntpnt = NULL;
866 char *orig_mntpnt = NULL;
867 char *tmp_menu = NULL;
868 FILE *menu_fp = NULL;
869 FILE *tmp_menu_fp = NULL;
870 struct stat sb;
871 int ret = BE_SUCCESS;
872 int i;
873 int fd;
874 int err = 0;
875 int nlines = 0;
876 int entry_cnt = 0;
877 int entry_del = 0;
878 int num_entry_del = 0;
879 int tmp_menu_len = 0;
880 boolean_t write = B_TRUE;
881 boolean_t do_buffer = B_FALSE;
882 boolean_t pool_mounted = B_FALSE;
884 if (boot_pool == NULL)
885 boot_pool = be_root_pool;
887 /* Get name of BE's root dataset */
888 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
890 /* Get handle to pool dataset */
891 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
892 be_print_err(gettext("be_remove_menu: "
893 "failed to open pool dataset for %s: %s"),
894 be_root_pool, libzfs_error_description(g_zfs));
895 return (zfs_err_to_be_err(g_zfs));
899 * Check to see if the pool's dataset is mounted. If it isn't we'll
900 * attempt to mount it.
902 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
903 &pool_mounted)) != BE_SUCCESS) {
904 be_print_err(gettext("be_remove_menu: pool dataset "
905 "(%s) could not be mounted\n"), be_root_pool);
906 ZFS_CLOSE(zhp);
907 return (ret);
911 * Get the mountpoint for the root pool dataset.
913 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
914 be_print_err(gettext("be_remove_menu: pool "
915 "dataset (%s) is not mounted. Can't remove "
916 "the BE from the boot menu.\n"), be_root_pool);
917 ret = BE_ERR_NO_MENU;
918 goto cleanup;
921 /* Get path to boot menu */
922 (void) strlcpy(menu, pool_mntpnt, sizeof (menu));
923 (void) strlcat(menu, BE_SPARC_MENU, sizeof (menu));
925 /* Get handle to boot menu file */
926 if ((ret = be_open_menu(be_root_pool, menu, &menu_fp, "r",
927 B_TRUE)) != BE_SUCCESS) {
928 goto cleanup;
929 } else if (menu_fp == NULL) {
930 ret = BE_ERR_NO_MENU;
931 goto cleanup;
934 free(pool_mntpnt);
935 pool_mntpnt = NULL;
937 /* Grab the stats of the original menu file */
938 if (stat(menu, &sb) != 0) {
939 err = errno;
940 be_print_err(gettext("be_remove_menu: "
941 "failed to stat file %s: %s\n"), menu, strerror(err));
942 ret = errno_to_be_err(err);
943 goto cleanup;
946 /* Create a tmp file for the modified menu.lst */
947 tmp_menu_len = strlen(menu) + 7;
948 if ((tmp_menu = (char *)malloc(tmp_menu_len)) == NULL) {
949 be_print_err(gettext("be_remove_menu: malloc failed\n"));
950 ret = BE_ERR_NOMEM;
951 goto cleanup;
953 (void) memset(tmp_menu, 0, tmp_menu_len);
954 (void) strlcpy(tmp_menu, menu, tmp_menu_len);
955 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
956 if ((fd = mkstemp(tmp_menu)) == -1) {
957 err = errno;
958 be_print_err(gettext("be_remove_menu: mkstemp failed\n"));
959 ret = errno_to_be_err(err);
960 free(tmp_menu);
961 tmp_menu = NULL;
962 goto cleanup;
964 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
965 err = errno;
966 be_print_err(gettext("be_remove_menu: "
967 "could not open tmp file for write: %s\n"), strerror(err));
968 (void) close(fd);
969 ret = errno_to_be_err(err);
970 goto cleanup;
973 while (fgets(menu_buf, BUFSIZ, menu_fp)) {
974 char tline [BUFSIZ];
975 char *tok = NULL;
977 (void) strlcpy(tline, menu_buf, sizeof (tline));
979 /* Tokenize line */
980 tok = strtok(tline, BE_WHITE_SPACE);
982 if (tok == NULL || tok[0] == '#') {
983 /* Found empty line or comment line */
984 if (do_buffer) {
985 /* Buffer this line */
986 if ((buffer = reallocarray(buffer, nlines + 1,
987 sizeof (char *))) == NULL) {
988 ret = BE_ERR_NOMEM;
989 goto cleanup;
991 if ((buffer[nlines++] = strdup(menu_buf))
992 == NULL) {
993 ret = BE_ERR_NOMEM;
994 goto cleanup;
997 } else if (write != 0) {
998 /* Write this line out */
999 (void) fputs(menu_buf, tmp_menu_fp);
1001 } else if (strcmp(tok, "title") == 0) {
1003 * If we've reached a 'title' line and do_buffer is
1004 * is true, that means we've just buffered an entire
1005 * entry without finding a 'bootfs' directive. We
1006 * need to write that entry out and keep searching.
1008 if (do_buffer) {
1009 for (i = 0; i < nlines; i++) {
1010 (void) fputs(buffer[i], tmp_menu_fp);
1011 free(buffer[i]);
1013 free(buffer);
1014 buffer = NULL;
1015 nlines = 0;
1019 * Turn writing off and buffering on, and increment
1020 * our entry counter.
1022 write = B_FALSE;
1023 do_buffer = B_TRUE;
1024 entry_cnt++;
1026 /* Buffer this 'title' line */
1027 if ((buffer = reallocarray(buffer, nlines + 1,
1028 sizeof (char *))) == NULL) {
1029 ret = BE_ERR_NOMEM;
1030 goto cleanup;
1032 if ((buffer[nlines++] = strdup(menu_buf)) == NULL) {
1033 ret = BE_ERR_NOMEM;
1034 goto cleanup;
1037 } else if (strcmp(tok, "bootfs") == 0) {
1038 char *bootfs = NULL;
1041 * Found a 'bootfs' line. See if it matches the
1042 * BE we're looking for.
1044 if ((bootfs = strtok(NULL, BE_WHITE_SPACE)) == NULL ||
1045 strcmp(bootfs, be_root_ds) != 0) {
1047 * Either there's nothing after the 'bootfs'
1048 * or this is not the BE we're looking for,
1049 * write out the line(s) we've buffered since
1050 * finding the title.
1052 for (i = 0; i < nlines; i++) {
1053 (void) fputs(buffer[i], tmp_menu_fp);
1054 free(buffer[i]);
1056 free(buffer);
1057 buffer = NULL;
1058 nlines = 0;
1061 * Turn writing back on, and turn off buffering
1062 * since this isn't the entry we're looking
1063 * for.
1065 write = B_TRUE;
1066 do_buffer = B_FALSE;
1068 /* Write this 'bootfs' line out. */
1069 (void) fputs(menu_buf, tmp_menu_fp);
1070 } else {
1072 * Found the entry we're looking for.
1073 * Record its entry number, increment the
1074 * number of entries we've deleted, and turn
1075 * writing off. Also, throw away the lines
1076 * we've buffered for this entry so far, we
1077 * don't need them.
1079 entry_del = entry_cnt - 1;
1080 num_entry_del++;
1081 write = B_FALSE;
1082 do_buffer = B_FALSE;
1084 for (i = 0; i < nlines; i++) {
1085 free(buffer[i]);
1087 free(buffer);
1088 buffer = NULL;
1089 nlines = 0;
1091 } else {
1092 if (do_buffer) {
1093 /* Buffer this line */
1094 if ((buffer = reallocarray(buffer, nlines + 1,
1095 sizeof (char *))) == NULL) {
1096 ret = BE_ERR_NOMEM;
1097 goto cleanup;
1099 if ((buffer[nlines++] = strdup(menu_buf))
1100 == NULL) {
1101 ret = BE_ERR_NOMEM;
1102 goto cleanup;
1104 } else if (write) {
1105 /* Write this line out */
1106 (void) fputs(menu_buf, tmp_menu_fp);
1111 (void) fclose(menu_fp);
1112 menu_fp = NULL;
1113 (void) fclose(tmp_menu_fp);
1114 tmp_menu_fp = NULL;
1116 /* Copy the modified menu.lst into place */
1117 if (rename(tmp_menu, menu) != 0) {
1118 err = errno;
1119 be_print_err(gettext("be_remove_menu: "
1120 "failed to rename file %s to %s: %s\n"),
1121 tmp_menu, menu, strerror(err));
1122 ret = errno_to_be_err(err);
1123 goto cleanup;
1125 free(tmp_menu);
1126 tmp_menu = NULL;
1128 /* Set the perms and ownership of the updated file */
1129 if (chmod(menu, sb.st_mode) != 0) {
1130 err = errno;
1131 be_print_err(gettext("be_remove_menu: "
1132 "failed to chmod %s: %s\n"), menu, strerror(err));
1133 ret = errno_to_be_err(err);
1134 goto cleanup;
1136 if (chown(menu, sb.st_uid, sb.st_gid) != 0) {
1137 err = errno;
1138 be_print_err(gettext("be_remove_menu: "
1139 "failed to chown %s: %s\n"), menu, strerror(err));
1140 ret = errno_to_be_err(err);
1141 goto cleanup;
1144 cleanup:
1145 if (pool_mounted) {
1146 int err = BE_SUCCESS;
1147 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1148 if (ret == BE_SUCCESS)
1149 ret = err;
1150 free(orig_mntpnt);
1151 free(ptmp_mntpnt);
1153 ZFS_CLOSE(zhp);
1155 free(buffer);
1156 if (menu_fp != NULL)
1157 (void) fclose(menu_fp);
1158 if (tmp_menu_fp != NULL)
1159 (void) fclose(tmp_menu_fp);
1160 if (tmp_menu != NULL) {
1161 (void) unlink(tmp_menu);
1162 free(tmp_menu);
1165 return (ret);
1169 * Function: be_update_menu
1170 * Description: This function is used by be_rename to change the BE name in
1171 * an existing entry in the menu to the new name of the BE.
1172 * Parameters:
1173 * be_orig_name - the original name of the BE
1174 * be_new_name - the new name the BE is being renameed to.
1175 * be_root_pool - The pool which contains the boot menu
1176 * boot_pool - the pool where the BE is, if different than
1177 * the pool containing the boot menu. If this is
1178 * NULL it will be set to be_root_pool.
1179 * Returns:
1180 * BE_SUCCESS - Success
1181 * be_errno_t - Failure
1182 * Scope:
1183 * Semi-private (library wide use only)
1186 be_update_menu(char *be_orig_name, char *be_new_name, char *be_root_pool,
1187 char *boot_pool)
1189 zfs_handle_t *zhp = NULL;
1190 char menu_file[MAXPATHLEN];
1191 char be_root_ds[MAXPATHLEN];
1192 char be_new_root_ds[MAXPATHLEN];
1193 char line[BUFSIZ];
1194 char *pool_mntpnt = NULL;
1195 char *ptmp_mntpnt = NULL;
1196 char *orig_mntpnt = NULL;
1197 char *temp_menu = NULL;
1198 FILE *menu_fp = NULL;
1199 FILE *new_fp = NULL;
1200 struct stat sb;
1201 int temp_menu_len = 0;
1202 int tmp_fd;
1203 int ret = BE_SUCCESS;
1204 int err = 0;
1205 boolean_t pool_mounted = B_FALSE;
1207 errno = 0;
1209 if (boot_pool == NULL)
1210 boot_pool = be_root_pool;
1212 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1213 be_print_err(gettext("be_update_menu: failed to open "
1214 "pool dataset for %s: %s\n"), be_root_pool,
1215 libzfs_error_description(g_zfs));
1216 return (zfs_err_to_be_err(g_zfs));
1220 * Check to see if the pool's dataset is mounted. If it isn't we'll
1221 * attempt to mount it.
1223 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1224 &pool_mounted)) != BE_SUCCESS) {
1225 be_print_err(gettext("be_update_menu: pool dataset "
1226 "(%s) could not be mounted\n"), be_root_pool);
1227 ZFS_CLOSE(zhp);
1228 return (ret);
1232 * Get the mountpoint for the root pool dataset.
1234 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1235 be_print_err(gettext("be_update_menu: failed "
1236 "to get mount point for the root pool. Can't update "
1237 "the BE in the boot menu.\n"));
1238 ret = BE_ERR_NO_MENU;
1239 goto cleanup;
1242 (void) snprintf(menu_file, sizeof (menu_file),
1243 "%s%s", pool_mntpnt, BE_SPARC_MENU);
1245 be_make_root_ds(be_root_pool, be_orig_name, be_root_ds,
1246 sizeof (be_root_ds));
1247 be_make_root_ds(be_root_pool, be_new_name, be_new_root_ds,
1248 sizeof (be_new_root_ds));
1250 if ((ret = be_open_menu(be_root_pool, menu_file,
1251 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
1252 goto cleanup;
1253 } else if (menu_fp == NULL) {
1254 ret = BE_ERR_NO_MENU;
1255 goto cleanup;
1258 free(pool_mntpnt);
1259 pool_mntpnt = NULL;
1261 /* Grab the stat of the original menu file */
1262 if (stat(menu_file, &sb) != 0) {
1263 err = errno;
1264 be_print_err(gettext("be_update_menu: "
1265 "failed to stat file %s: %s\n"), menu_file, strerror(err));
1266 (void) fclose(menu_fp);
1267 ret = errno_to_be_err(err);
1268 goto cleanup;
1271 /* Create tmp file for modified menu.lst */
1272 temp_menu_len = strlen(menu_file) + 7;
1273 if ((temp_menu = (char *)malloc(temp_menu_len))
1274 == NULL) {
1275 be_print_err(gettext("be_update_menu: "
1276 "malloc failed\n"));
1277 (void) fclose(menu_fp);
1278 ret = BE_ERR_NOMEM;
1279 goto cleanup;
1281 (void) memset(temp_menu, 0, temp_menu_len);
1282 (void) strlcpy(temp_menu, menu_file, temp_menu_len);
1283 (void) strlcat(temp_menu, "XXXXXX", temp_menu_len);
1284 if ((tmp_fd = mkstemp(temp_menu)) == -1) {
1285 err = errno;
1286 be_print_err(gettext("be_update_menu: "
1287 "mkstemp failed: %s\n"), strerror(err));
1288 (void) fclose(menu_fp);
1289 free(temp_menu);
1290 ret = errno_to_be_err(err);
1291 goto cleanup;
1293 if ((new_fp = fdopen(tmp_fd, "w")) == NULL) {
1294 err = errno;
1295 be_print_err(gettext("be_update_menu: "
1296 "fdopen failed: %s\n"), strerror(err));
1297 (void) close(tmp_fd);
1298 (void) fclose(menu_fp);
1299 free(temp_menu);
1300 ret = errno_to_be_err(err);
1301 goto cleanup;
1304 while (fgets(line, BUFSIZ, menu_fp)) {
1305 char tline[BUFSIZ];
1306 char new_line[BUFSIZ];
1307 char *c = NULL;
1309 (void) strlcpy(tline, line, sizeof (tline));
1311 /* Tokenize line */
1312 c = strtok(tline, BE_WHITE_SPACE);
1314 if (c == NULL) {
1315 /* Found empty line, write it out. */
1316 (void) fputs(line, new_fp);
1317 } else if (c[0] == '#') {
1318 /* Found a comment line, write it out. */
1319 (void) fputs(line, new_fp);
1320 } else if (strcmp(c, "title") == 0) {
1321 char *name = NULL;
1322 char *desc = NULL;
1325 * Found a 'title' line, parse out BE name or
1326 * the description.
1328 name = strtok(NULL, BE_WHITE_SPACE);
1330 if (name == NULL) {
1332 * Nothing after 'title', just push
1333 * this line through
1335 (void) fputs(line, new_fp);
1336 } else {
1338 * Grab the remainder of the title which
1339 * could be a multi worded description
1341 desc = strtok(NULL, "\n");
1343 if (strcmp(name, be_orig_name) == 0) {
1345 * The first token of the title is
1346 * the old BE name, replace it with
1347 * the new one, and write it out
1348 * along with the remainder of
1349 * description if there is one.
1351 if (desc) {
1352 (void) snprintf(new_line,
1353 sizeof (new_line),
1354 "title %s %s\n",
1355 be_new_name, desc);
1356 } else {
1357 (void) snprintf(new_line,
1358 sizeof (new_line),
1359 "title %s\n", be_new_name);
1362 (void) fputs(new_line, new_fp);
1363 } else {
1364 (void) fputs(line, new_fp);
1367 } else if (strcmp(c, "bootfs") == 0) {
1369 * Found a 'bootfs' line, parse out the BE root
1370 * dataset value.
1372 char *root_ds = strtok(NULL, BE_WHITE_SPACE);
1374 if (root_ds == NULL) {
1376 * Nothing after 'bootfs', just push
1377 * this line through
1379 (void) fputs(line, new_fp);
1380 } else {
1382 * If this bootfs is the one we're renaming,
1383 * write out the new root dataset value
1385 if (strcmp(root_ds, be_root_ds) == 0) {
1386 (void) snprintf(new_line,
1387 sizeof (new_line), "bootfs %s\n",
1388 be_new_root_ds);
1390 (void) fputs(new_line, new_fp);
1391 } else {
1392 (void) fputs(line, new_fp);
1395 } else {
1397 * Found some other line we don't care
1398 * about, write it out.
1400 (void) fputs(line, new_fp);
1404 (void) fclose(menu_fp);
1405 (void) fclose(new_fp);
1406 (void) close(tmp_fd);
1408 if (rename(temp_menu, menu_file) != 0) {
1409 err = errno;
1410 be_print_err(gettext("be_update_menu: "
1411 "failed to rename file %s to %s: %s\n"),
1412 temp_menu, menu_file, strerror(err));
1413 ret = errno_to_be_err(err);
1415 free(temp_menu);
1417 /* Set the perms and ownership of the updated file */
1418 if (chmod(menu_file, sb.st_mode) != 0) {
1419 err = errno;
1420 be_print_err(gettext("be_update_menu: "
1421 "failed to chmod %s: %s\n"), menu_file, strerror(err));
1422 ret = errno_to_be_err(err);
1423 goto cleanup;
1425 if (chown(menu_file, sb.st_uid, sb.st_gid) != 0) {
1426 err = errno;
1427 be_print_err(gettext("be_update_menu: "
1428 "failed to chown %s: %s\n"), menu_file, strerror(err));
1429 ret = errno_to_be_err(err);
1432 cleanup:
1433 if (pool_mounted) {
1434 int err = BE_SUCCESS;
1435 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1436 if (ret == BE_SUCCESS)
1437 ret = err;
1438 free(orig_mntpnt);
1439 free(ptmp_mntpnt);
1441 ZFS_CLOSE(zhp);
1442 return (ret);
1446 * Function: be_has_menu_entry
1447 * Description: Checks to see if the BEs root dataset has an entry in the menu.
1448 * Parameters:
1449 * be_dataset - The root dataset of the BE
1450 * be_root_pool - The pool which contains the boot menu
1451 * entry - A pointer the the entry number of the BE if found.
1452 * Returns:
1453 * B_TRUE - Success
1454 * B_FALSE - Failure
1455 * Scope:
1456 * Semi-private (library wide use only)
1458 boolean_t
1459 be_has_menu_entry(char *be_dataset, char *be_root_pool, int *entry)
1461 zfs_handle_t *zhp = NULL;
1462 char menu_file[MAXPATHLEN];
1463 FILE *menu_fp;
1464 char line[BUFSIZ];
1465 char *last;
1466 char *rpool_mntpnt = NULL;
1467 char *ptmp_mntpnt = NULL;
1468 char *orig_mntpnt = NULL;
1469 int ent_num = 0;
1470 boolean_t ret = 0;
1471 boolean_t pool_mounted = B_FALSE;
1474 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1475 be_print_err(gettext("be_has_menu_entry: failed to open "
1476 "pool dataset for %s: %s\n"), be_root_pool,
1477 libzfs_error_description(g_zfs));
1478 return (B_FALSE);
1482 * Check to see if the pool's dataset is mounted. If it isn't we'll
1483 * attempt to mount it.
1485 if (be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1486 &pool_mounted) != 0) {
1487 be_print_err(gettext("be_has_menu_entry: pool dataset "
1488 "(%s) could not be mounted\n"), be_root_pool);
1489 ZFS_CLOSE(zhp);
1490 return (B_FALSE);
1494 * Get the mountpoint for the root pool dataset.
1496 if (!zfs_is_mounted(zhp, &rpool_mntpnt)) {
1497 be_print_err(gettext("be_has_menu_entry: pool "
1498 "dataset (%s) is not mounted. Can't set "
1499 "the BE in the boot menu.\n"), be_root_pool);
1500 ret = B_FALSE;
1501 goto cleanup;
1504 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
1505 rpool_mntpnt, BE_SPARC_MENU);
1507 if (be_open_menu(be_root_pool, menu_file, &menu_fp, "r",
1508 B_FALSE) != 0) {
1509 ret = B_FALSE;
1510 goto cleanup;
1511 } else if (menu_fp == NULL) {
1512 ret = B_FALSE;
1513 goto cleanup;
1516 free(rpool_mntpnt);
1517 rpool_mntpnt = NULL;
1519 while (fgets(line, BUFSIZ, menu_fp)) {
1520 char *tok = strtok_r(line, BE_WHITE_SPACE, &last);
1522 if (tok != NULL && tok[0] != '#') {
1523 if (strcmp(tok, "bootfs") == 0) {
1524 tok = strtok_r(last, BE_WHITE_SPACE, &last);
1525 if (tok != NULL && strcmp(tok,
1526 be_dataset) == 0) {
1527 (void) fclose(menu_fp);
1529 * The entry number needs to be
1530 * decremented here because the title
1531 * will always be the first line for
1532 * an entry. Because of this we'll
1533 * always be off by one entry when we
1534 * check for bootfs.
1536 *entry = ent_num - 1;
1537 ret = B_TRUE;
1538 goto cleanup;
1540 } else if (strcmp(tok, "title") == 0)
1541 ent_num++;
1545 cleanup:
1546 if (pool_mounted) {
1547 (void) be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1548 free(orig_mntpnt);
1549 free(ptmp_mntpnt);
1551 ZFS_CLOSE(zhp);
1552 (void) fclose(menu_fp);
1553 return (ret);
1557 * Function: be_update_vfstab
1558 * Description: This function digs into a BE's vfstab and updates all
1559 * entries with file systems listed in be_fs_list_data_t.
1560 * The entry's root container dataset and be_name will be
1561 * updated with the parameters passed in.
1562 * Parameters:
1563 * be_name - name of BE to update
1564 * old_rc_loc - dataset under which the root container dataset
1565 * of the old BE resides in.
1566 * new_rc_loc - dataset under which the root container dataset
1567 * of the new BE resides in.
1568 * fld - be_fs_list_data_t pointer providing the list of
1569 * file systems to look for in vfstab.
1570 * mountpoint - directory of where BE is currently mounted.
1571 * If NULL, then BE is not currently mounted.
1572 * Returns:
1573 * BE_SUCCESS - Success
1574 * be_errno_t - Failure
1575 * Scope:
1576 * Semi-private (library wide use only)
1579 be_update_vfstab(char *be_name, char *old_rc_loc, char *new_rc_loc,
1580 be_fs_list_data_t *fld, char *mountpoint)
1582 char *tmp_mountpoint = NULL;
1583 char alt_vfstab[MAXPATHLEN];
1584 int ret = BE_SUCCESS, err = BE_SUCCESS;
1586 if (fld == NULL || fld->fs_list == NULL || fld->fs_num == 0)
1587 return (BE_SUCCESS);
1589 /* If BE not already mounted, mount the BE */
1590 if (mountpoint == NULL) {
1591 if ((ret = _be_mount(be_name, &tmp_mountpoint,
1592 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
1593 be_print_err(gettext("be_update_vfstab: "
1594 "failed to mount BE (%s)\n"), be_name);
1595 return (ret);
1597 } else {
1598 tmp_mountpoint = mountpoint;
1601 /* Get string for vfstab in the mounted BE. */
1602 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
1603 tmp_mountpoint);
1605 /* Update the vfstab */
1606 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
1607 fld);
1609 /* Unmount BE if we mounted it */
1610 if (mountpoint == NULL) {
1611 if ((err = _be_unmount(be_name, 0)) == BE_SUCCESS) {
1612 /* Remove temporary mountpoint */
1613 (void) rmdir(tmp_mountpoint);
1614 } else {
1615 be_print_err(gettext("be_update_vfstab: "
1616 "failed to unmount BE %s mounted at %s\n"),
1617 be_name, tmp_mountpoint);
1618 if (ret == BE_SUCCESS)
1619 ret = err;
1622 free(tmp_mountpoint);
1625 return (ret);
1629 * Function: be_update_zone_vfstab
1630 * Description: This function digs into a zone BE's vfstab and updates all
1631 * entries with file systems listed in be_fs_list_data_t.
1632 * The entry's root container dataset and be_name will be
1633 * updated with the parameters passed in.
1634 * Parameters:
1635 * zhp - zfs_handle_t pointer to zone root dataset.
1636 * be_name - name of zone BE to update
1637 * old_rc_loc - dataset under which the root container dataset
1638 * of the old zone BE resides in.
1639 * new_rc_loc - dataset under which the root container dataset
1640 * of the new zone BE resides in.
1641 * fld - be_fs_list_data_t pointer providing the list of
1642 * file systems to look for in vfstab.
1643 * Returns:
1644 * BE_SUCCESS - Success
1645 * be_errno_t - Failure
1646 * Scope:
1647 * Semi-private (library wide use only)
1650 be_update_zone_vfstab(zfs_handle_t *zhp, char *be_name, char *old_rc_loc,
1651 char *new_rc_loc, be_fs_list_data_t *fld)
1653 be_mount_data_t md = { 0 };
1654 be_unmount_data_t ud = { 0 };
1655 char alt_vfstab[MAXPATHLEN];
1656 boolean_t mounted_here = B_FALSE;
1657 int ret = BE_SUCCESS;
1660 * If zone root not already mounted, mount it at a
1661 * temporary location.
1663 if (!zfs_is_mounted(zhp, &md.altroot)) {
1664 /* Generate temporary mountpoint to mount zone root */
1665 if ((ret = be_make_tmp_mountpoint(&md.altroot)) != BE_SUCCESS) {
1666 be_print_err(gettext("be_update_zone_vfstab: "
1667 "failed to make temporary mountpoint to "
1668 "mount zone root\n"));
1669 return (ret);
1672 if (be_mount_zone_root(zhp, &md) != BE_SUCCESS) {
1673 be_print_err(gettext("be_update_zone_vfstab: "
1674 "failed to mount zone root %s\n"),
1675 zfs_get_name(zhp));
1676 free(md.altroot);
1677 return (BE_ERR_MOUNT_ZONEROOT);
1679 mounted_here = B_TRUE;
1682 /* Get string from vfstab in the mounted zone BE */
1683 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
1684 md.altroot);
1686 /* Update the vfstab */
1687 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
1688 fld);
1690 /* Unmount zone root if we mounted it */
1691 if (mounted_here) {
1692 ud.force = B_TRUE;
1694 if (be_unmount_zone_root(zhp, &ud) == BE_SUCCESS) {
1695 /* Remove the temporary mountpoint */
1696 (void) rmdir(md.altroot);
1697 } else {
1698 be_print_err(gettext("be_update_zone_vfstab: "
1699 "failed to unmount zone root %s from %s\n"),
1700 zfs_get_name(zhp), md.altroot);
1701 if (ret == 0)
1702 ret = BE_ERR_UMOUNT_ZONEROOT;
1706 free(md.altroot);
1707 return (ret);
1711 * Function: be_auto_snap_name
1712 * Description: Generate an auto snapshot name constructed based on the
1713 * current date and time. The auto snapshot name is of the form:
1715 * <date>-<time>
1717 * where <date> is in ISO standard format, so the resultant name
1718 * is of the form:
1720 * %Y-%m-%d-%H:%M:%S
1722 * Parameters:
1723 * None
1724 * Returns:
1725 * Success - pointer to auto generated snapshot name. The name
1726 * is allocated in heap storage so the caller is
1727 * responsible for free'ing the name.
1728 * Failure - NULL
1729 * Scope:
1730 * Semi-private (library wide use only)
1732 char *
1733 be_auto_snap_name(void)
1735 time_t utc_tm = 0;
1736 struct tm *gmt_tm = NULL;
1737 char gmt_time_str[64];
1738 char *auto_snap_name = NULL;
1740 if (time(&utc_tm) == -1) {
1741 be_print_err(gettext("be_auto_snap_name: time() failed\n"));
1742 return (NULL);
1745 if ((gmt_tm = gmtime(&utc_tm)) == NULL) {
1746 be_print_err(gettext("be_auto_snap_name: gmtime() failed\n"));
1747 return (NULL);
1750 (void) strftime(gmt_time_str, sizeof (gmt_time_str), "%F-%T", gmt_tm);
1752 if ((auto_snap_name = strdup(gmt_time_str)) == NULL) {
1753 be_print_err(gettext("be_auto_snap_name: "
1754 "memory allocation failed\n"));
1755 return (NULL);
1758 return (auto_snap_name);
1762 * Function: be_auto_be_name
1763 * Description: Generate an auto BE name constructed based on the BE name
1764 * of the original BE being cloned.
1765 * Parameters:
1766 * obe_name - name of the original BE being cloned.
1767 * Returns:
1768 * Success - pointer to auto generated BE name. The name
1769 * is allocated in heap storage so the caller is
1770 * responsible for free'ing the name.
1771 * Failure - NULL
1772 * Scope:
1773 * Semi-private (library wide use only)
1775 char *
1776 be_auto_be_name(char *obe_name)
1778 return (be_get_auto_name(obe_name, NULL, B_FALSE));
1782 * Function: be_auto_zone_be_name
1783 * Description: Generate an auto BE name for a zone constructed based on
1784 * the BE name of the original zone BE being cloned.
1785 * Parameters:
1786 * container_ds - container dataset for the zone.
1787 * zbe_name - name of the original zone BE being cloned.
1788 * Returns:
1789 * Success - pointer to auto generated BE name. The name
1790 * is allocated in heap storage so the caller is
1791 * responsible for free'ing the name.
1792 * Failure - NULL
1793 * Scope:
1794 * Semi-private (library wide use only)
1796 char *
1797 be_auto_zone_be_name(char *container_ds, char *zbe_name)
1799 return (be_get_auto_name(zbe_name, container_ds, B_TRUE));
1803 * Function: be_valid_be_name
1804 * Description: Validates a BE name.
1805 * Parameters:
1806 * be_name - name of BE to validate
1807 * Returns:
1808 * B_TRUE - be_name is valid
1809 * B_FALSE - be_name is invalid
1810 * Scope:
1811 * Semi-private (library wide use only)
1814 boolean_t
1815 be_valid_be_name(const char *be_name)
1817 const char *c = NULL;
1818 struct be_defaults be_defaults;
1820 if (be_name == NULL)
1821 return (B_FALSE);
1823 be_get_defaults(&be_defaults);
1826 * A BE name must not be a multi-level dataset name. We also check
1827 * that it does not contain the ' ' and '%' characters. The ' ' is
1828 * a valid character for datasets, however we don't allow that in a
1829 * BE name. The '%' is invalid, but zfs_name_valid() allows it for
1830 * internal reasons, so we explicitly check for it here.
1832 c = be_name;
1833 while (*c != '\0' && *c != '/' && *c != ' ' && *c != '%')
1834 c++;
1836 if (*c != '\0')
1837 return (B_FALSE);
1840 * The BE name must comply with a zfs dataset filesystem. We also
1841 * verify its length to be < BE_NAME_MAX_LEN.
1843 if (!zfs_name_valid(be_name, ZFS_TYPE_FILESYSTEM) ||
1844 strlen(be_name) > BE_NAME_MAX_LEN)
1845 return (B_FALSE);
1847 if (be_defaults.be_deflt_bename_starts_with[0] != '\0' &&
1848 strstr(be_name, be_defaults.be_deflt_bename_starts_with) == NULL) {
1849 return (B_FALSE);
1852 return (B_TRUE);
1856 * Function: be_valid_auto_snap_name
1857 * Description: This function checks that a snapshot name is a valid auto
1858 * generated snapshot name. A valid auto generated snapshot
1859 * name is of the form:
1861 * %Y-%m-%d-%H:%M:%S
1863 * An older form of the auto generated snapshot name also
1864 * included the snapshot's BE cleanup policy and a reserved
1865 * field. Those names will also be verified by this function.
1867 * Examples of valid auto snapshot names are:
1869 * 2008-03-31-18:41:30
1870 * 2008-03-31-22:17:24
1871 * <policy>:-:2008:04-05-09:12:55
1872 * <policy>:-:2008:04-06-15:34:12
1874 * Parameters:
1875 * name - name of the snapshot to be validated.
1876 * Returns:
1877 * B_TRUE - the name is a valid auto snapshot name.
1878 * B_FALSE - the name is not a valid auto snapshot name.
1879 * Scope:
1880 * Semi-private (library wide use only)
1882 boolean_t
1883 be_valid_auto_snap_name(char *name)
1885 struct tm gmt_tm;
1887 char *policy = NULL;
1888 char *reserved = NULL;
1889 char *date = NULL;
1890 char *c = NULL;
1892 /* Validate the snapshot name by converting it into utc time */
1893 if (strptime(name, "%Y-%m-%d-%T", &gmt_tm) != NULL &&
1894 (mktime(&gmt_tm) != -1)) {
1895 return (B_TRUE);
1899 * Validate the snapshot name against the older form of an
1900 * auto generated snapshot name.
1902 policy = strdup(name);
1905 * Get the first field from the snapshot name,
1906 * which is the BE policy
1908 c = strchr(policy, ':');
1909 if (c == NULL) {
1910 free(policy);
1911 return (B_FALSE);
1913 c[0] = '\0';
1915 /* Validate the policy name */
1916 if (!valid_be_policy(policy)) {
1917 free(policy);
1918 return (B_FALSE);
1921 /* Get the next field, which is the reserved field. */
1922 if (c[1] == '\0') {
1923 free(policy);
1924 return (B_FALSE);
1926 reserved = c+1;
1927 c = strchr(reserved, ':');
1928 if (c == NULL) {
1929 free(policy);
1930 return (B_FALSE);
1932 c[0] = '\0';
1934 /* Validate the reserved field */
1935 if (strcmp(reserved, "-") != 0) {
1936 free(policy);
1937 return (B_FALSE);
1940 /* The remaining string should be the date field */
1941 if (c[1] == '\0') {
1942 free(policy);
1943 return (B_FALSE);
1945 date = c+1;
1947 /* Validate the date string by converting it into utc time */
1948 if (strptime(date, "%Y-%m-%d-%T", &gmt_tm) == NULL ||
1949 (mktime(&gmt_tm) == -1)) {
1950 be_print_err(gettext("be_valid_auto_snap_name: "
1951 "invalid auto snapshot name\n"));
1952 free(policy);
1953 return (B_FALSE);
1956 free(policy);
1957 return (B_TRUE);
1961 * Function: be_default_policy
1962 * Description: Temporary hardcoded policy support. This function returns
1963 * the default policy type to be used to create a BE or a BE
1964 * snapshot.
1965 * Parameters:
1966 * None
1967 * Returns:
1968 * Name of default BE policy.
1969 * Scope:
1970 * Semi-private (library wide use only)
1972 char *
1973 be_default_policy(void)
1975 return (BE_PLCY_STATIC);
1979 * Function: valid_be_policy
1980 * Description: Temporary hardcoded policy support. This function valids
1981 * whether a policy is a valid known policy or not.
1982 * Paramters:
1983 * policy - name of policy to validate.
1984 * Returns:
1985 * B_TRUE - policy is a valid.
1986 * B_FALSE - policy is invalid.
1987 * Scope:
1988 * Semi-private (library wide use only)
1990 boolean_t
1991 valid_be_policy(char *policy)
1993 if (policy == NULL)
1994 return (B_FALSE);
1996 if (strcmp(policy, BE_PLCY_STATIC) == 0 ||
1997 strcmp(policy, BE_PLCY_VOLATILE) == 0) {
1998 return (B_TRUE);
2001 return (B_FALSE);
2005 * Function: be_print_err
2006 * Description: This function prints out error messages if do_print is
2007 * set to B_TRUE or if the BE_PRINT_ERR environment variable
2008 * is set to true.
2009 * Paramters:
2010 * prnt_str - the string we wish to print and any arguments
2011 * for the format of that string.
2012 * Returns:
2013 * void
2014 * Scope:
2015 * Semi-private (library wide use only)
2017 void
2018 be_print_err(char *prnt_str, ...)
2020 va_list ap;
2021 char buf[BUFSIZ];
2022 char *env_buf;
2023 static boolean_t env_checked = B_FALSE;
2025 if (!env_checked) {
2026 if ((env_buf = getenv("BE_PRINT_ERR")) != NULL) {
2027 if (strcasecmp(env_buf, "true") == 0) {
2028 do_print = B_TRUE;
2031 env_checked = B_TRUE;
2034 if (do_print) {
2035 va_start(ap, prnt_str);
2036 /* LINTED variable format specifier */
2037 (void) vsnprintf(buf, BUFSIZ, prnt_str, ap);
2038 (void) fputs(buf, stderr);
2039 va_end(ap);
2044 * Function: be_find_current_be
2045 * Description: Find the currently "active" BE. Fill in the
2046 * passed in be_transaction_data_t reference with the
2047 * active BE's data.
2048 * Paramters:
2049 * none
2050 * Returns:
2051 * BE_SUCCESS - Success
2052 * be_errnot_t - Failure
2053 * Scope:
2054 * Semi-private (library wide use only)
2055 * Notes:
2056 * The caller is responsible for initializing the libzfs handle
2057 * and freeing the memory used by the active be_name.
2060 be_find_current_be(be_transaction_data_t *bt)
2062 int zret;
2064 if ((zret = zpool_iter(g_zfs, be_zpool_find_current_be_callback,
2065 bt)) == 0) {
2066 be_print_err(gettext("be_find_current_be: failed to "
2067 "find current BE name\n"));
2068 return (BE_ERR_BE_NOENT);
2069 } else if (zret < 0) {
2070 be_print_err(gettext("be_find_current_be: "
2071 "zpool_iter failed: %s\n"),
2072 libzfs_error_description(g_zfs));
2073 return (zfs_err_to_be_err(g_zfs));
2076 return (BE_SUCCESS);
2080 * Function: be_zpool_find_current_be_callback
2081 * Description: Callback function used to iterate through all existing pools
2082 * to find the BE that is the currently booted BE.
2083 * Parameters:
2084 * zlp - zpool_handle_t pointer to the current pool being
2085 * looked at.
2086 * data - be_transaction_data_t pointer.
2087 * Upon successfully finding the current BE, the
2088 * obe_zpool member of this parameter is set to the
2089 * pool it is found in.
2090 * Return:
2091 * 1 - Found current BE in this pool.
2092 * 0 - Did not find current BE in this pool.
2093 * Scope:
2094 * Semi-private (library wide use only)
2097 be_zpool_find_current_be_callback(zpool_handle_t *zlp, void *data)
2099 be_transaction_data_t *bt = data;
2100 zfs_handle_t *zhp = NULL;
2101 const char *zpool = zpool_get_name(zlp);
2102 char be_container_ds[MAXPATHLEN];
2103 char *zpath = NULL;
2106 * Generate string for BE container dataset
2108 if (getzoneid() != GLOBAL_ZONEID) {
2109 if ((zpath = be_get_ds_from_dir("/")) != NULL) {
2110 (void) strlcpy(be_container_ds, dirname(zpath),
2111 sizeof (be_container_ds));
2112 } else {
2113 be_print_err(gettext(
2114 "be_zpool_find_current_be_callback: "
2115 "zone root dataset is not mounted\n"));
2116 return (0);
2118 } else {
2119 be_make_container_ds(zpool, be_container_ds,
2120 sizeof (be_container_ds));
2124 * Check if a BE container dataset exists in this pool.
2126 if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) {
2127 zpool_close(zlp);
2128 return (0);
2132 * Get handle to this zpool's BE container dataset.
2134 if ((zhp = zfs_open(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) ==
2135 NULL) {
2136 be_print_err(gettext("be_zpool_find_current_be_callback: "
2137 "failed to open BE container dataset (%s)\n"),
2138 be_container_ds);
2139 zpool_close(zlp);
2140 return (0);
2144 * Iterate through all potential BEs in this zpool
2146 if (zfs_iter_filesystems(zhp, be_zfs_find_current_be_callback, bt)) {
2148 * Found current BE dataset; set obe_zpool
2150 if ((bt->obe_zpool = strdup(zpool)) == NULL) {
2151 be_print_err(gettext(
2152 "be_zpool_find_current_be_callback: "
2153 "memory allocation failed\n"));
2154 ZFS_CLOSE(zhp);
2155 zpool_close(zlp);
2156 return (0);
2159 ZFS_CLOSE(zhp);
2160 zpool_close(zlp);
2161 return (1);
2164 ZFS_CLOSE(zhp);
2165 zpool_close(zlp);
2167 return (0);
2171 * Function: be_zfs_find_current_be_callback
2172 * Description: Callback function used to iterate through all BEs in a
2173 * pool to find the BE that is the currently booted BE.
2174 * Parameters:
2175 * zhp - zfs_handle_t pointer to current filesystem being checked.
2176 * data - be_transaction-data_t pointer
2177 * Upon successfully finding the current BE, the
2178 * obe_name and obe_root_ds members of this parameter
2179 * are set to the BE name and BE's root dataset
2180 * respectively.
2181 * Return:
2182 * 1 - Found current BE.
2183 * 0 - Did not find current BE.
2184 * Scope:
2185 * Semi-private (library wide use only)
2188 be_zfs_find_current_be_callback(zfs_handle_t *zhp, void *data)
2190 be_transaction_data_t *bt = data;
2191 char *mp = NULL;
2194 * Check if dataset is mounted, and if so where.
2196 if (zfs_is_mounted(zhp, &mp)) {
2198 * If mounted at root, set obe_root_ds and obe_name
2200 if (mp != NULL && strcmp(mp, "/") == 0) {
2201 free(mp);
2203 if ((bt->obe_root_ds = strdup(zfs_get_name(zhp)))
2204 == NULL) {
2205 be_print_err(gettext(
2206 "be_zfs_find_current_be_callback: "
2207 "memory allocation failed\n"));
2208 ZFS_CLOSE(zhp);
2209 return (0);
2212 if ((bt->obe_name = strdup(basename(bt->obe_root_ds)))
2213 == NULL) {
2214 be_print_err(gettext(
2215 "be_zfs_find_current_be_callback: "
2216 "memory allocation failed\n"));
2217 ZFS_CLOSE(zhp);
2218 return (0);
2221 ZFS_CLOSE(zhp);
2222 return (1);
2225 free(mp);
2227 ZFS_CLOSE(zhp);
2229 return (0);
2233 * Function: be_check_be_roots_callback
2234 * Description: This function checks whether or not the dataset name passed
2235 * is hierachically located under the BE root container dataset
2236 * for this pool.
2237 * Parameters:
2238 * zlp - zpool_handle_t pointer to current pool being processed.
2239 * data - name of dataset to check
2240 * Returns:
2241 * 0 - dataset is not in this pool's BE root container dataset
2242 * 1 - dataset is in this pool's BE root container dataset
2243 * Scope:
2244 * Semi-private (library wide use only)
2247 be_check_be_roots_callback(zpool_handle_t *zlp, void *data)
2249 const char *zpool = zpool_get_name(zlp);
2250 char *ds = data;
2251 char be_container_ds[MAXPATHLEN];
2253 /* Generate string for this pool's BE root container dataset */
2254 be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds));
2257 * If dataset lives under the BE root container dataset
2258 * of this pool, return failure.
2260 if (strncmp(be_container_ds, ds, strlen(be_container_ds)) == 0 &&
2261 ds[strlen(be_container_ds)] == '/') {
2262 zpool_close(zlp);
2263 return (1);
2266 zpool_close(zlp);
2267 return (0);
2271 * Function: zfs_err_to_be_err
2272 * Description: This function takes the error stored in the libzfs handle
2273 * and maps it to an be_errno_t. If there are no matching
2274 * be_errno_t's then BE_ERR_ZFS is returned.
2275 * Paramters:
2276 * zfsh - The libzfs handle containing the error we're looking up.
2277 * Returns:
2278 * be_errno_t
2279 * Scope:
2280 * Semi-private (library wide use only)
2283 zfs_err_to_be_err(libzfs_handle_t *zfsh)
2285 int err = libzfs_errno(zfsh);
2287 switch (err) {
2288 case 0:
2289 return (BE_SUCCESS);
2290 case EZFS_PERM:
2291 return (BE_ERR_PERM);
2292 case EZFS_INTR:
2293 return (BE_ERR_INTR);
2294 case EZFS_NOENT:
2295 return (BE_ERR_NOENT);
2296 case EZFS_NOSPC:
2297 return (BE_ERR_NOSPC);
2298 case EZFS_MOUNTFAILED:
2299 return (BE_ERR_MOUNT);
2300 case EZFS_UMOUNTFAILED:
2301 return (BE_ERR_UMOUNT);
2302 case EZFS_EXISTS:
2303 return (BE_ERR_BE_EXISTS);
2304 case EZFS_BUSY:
2305 return (BE_ERR_DEV_BUSY);
2306 case EZFS_POOLREADONLY:
2307 return (BE_ERR_ROFS);
2308 case EZFS_NAMETOOLONG:
2309 return (BE_ERR_NAMETOOLONG);
2310 case EZFS_NODEVICE:
2311 return (BE_ERR_NODEV);
2312 case EZFS_POOL_INVALARG:
2313 return (BE_ERR_INVAL);
2314 case EZFS_PROPTYPE:
2315 return (BE_ERR_INVALPROP);
2316 case EZFS_BADTYPE:
2317 return (BE_ERR_DSTYPE);
2318 case EZFS_PROPNONINHERIT:
2319 return (BE_ERR_NONINHERIT);
2320 case EZFS_PROPREADONLY:
2321 return (BE_ERR_READONLYPROP);
2322 case EZFS_RESILVERING:
2323 case EZFS_POOLUNAVAIL:
2324 return (BE_ERR_UNAVAIL);
2325 case EZFS_DSREADONLY:
2326 return (BE_ERR_READONLYDS);
2327 default:
2328 return (BE_ERR_ZFS);
2333 * Function: errno_to_be_err
2334 * Description: This function takes an errno and maps it to an be_errno_t.
2335 * If there are no matching be_errno_t's then BE_ERR_UNKNOWN is
2336 * returned.
2337 * Paramters:
2338 * err - The errno we're compairing against.
2339 * Returns:
2340 * be_errno_t
2341 * Scope:
2342 * Semi-private (library wide use only)
2345 errno_to_be_err(int err)
2347 switch (err) {
2348 case EPERM:
2349 return (BE_ERR_PERM);
2350 case EACCES:
2351 return (BE_ERR_ACCESS);
2352 case ECANCELED:
2353 return (BE_ERR_CANCELED);
2354 case EINTR:
2355 return (BE_ERR_INTR);
2356 case ENOENT:
2357 return (BE_ERR_NOENT);
2358 case ENOSPC:
2359 case EDQUOT:
2360 return (BE_ERR_NOSPC);
2361 case EEXIST:
2362 return (BE_ERR_BE_EXISTS);
2363 case EBUSY:
2364 return (BE_ERR_BUSY);
2365 case EROFS:
2366 return (BE_ERR_ROFS);
2367 case ENAMETOOLONG:
2368 return (BE_ERR_NAMETOOLONG);
2369 case ENXIO:
2370 return (BE_ERR_NXIO);
2371 case EINVAL:
2372 return (BE_ERR_INVAL);
2373 case EFAULT:
2374 return (BE_ERR_FAULT);
2375 default:
2376 return (BE_ERR_UNKNOWN);
2381 * Function: be_err_to_str
2382 * Description: This function takes a be_errno_t and maps it to a message.
2383 * If there are no matching be_errno_t's then NULL is returned.
2384 * Paramters:
2385 * be_errno_t - The be_errno_t we're mapping.
2386 * Returns:
2387 * string or NULL if the error code is not known.
2388 * Scope:
2389 * Semi-private (library wide use only)
2391 char *
2392 be_err_to_str(int err)
2394 switch (err) {
2395 case BE_ERR_ACCESS:
2396 return (gettext("Permission denied."));
2397 case BE_ERR_ACTIVATE_CURR:
2398 return (gettext("Activation of current BE failed."));
2399 case BE_ERR_AUTONAME:
2400 return (gettext("Auto naming failed."));
2401 case BE_ERR_BE_NOENT:
2402 return (gettext("No such BE."));
2403 case BE_ERR_BUSY:
2404 return (gettext("Mount busy."));
2405 case BE_ERR_DEV_BUSY:
2406 return (gettext("Device busy."));
2407 case BE_ERR_CANCELED:
2408 return (gettext("Operation canceled."));
2409 case BE_ERR_CLONE:
2410 return (gettext("BE clone failed."));
2411 case BE_ERR_COPY:
2412 return (gettext("BE copy failed."));
2413 case BE_ERR_CREATDS:
2414 return (gettext("Dataset creation failed."));
2415 case BE_ERR_CURR_BE_NOT_FOUND:
2416 return (gettext("Can't find current BE."));
2417 case BE_ERR_DESTROY:
2418 return (gettext("Failed to destroy BE or snapshot."));
2419 case BE_ERR_DESTROY_CURR_BE:
2420 return (gettext("Cannot destroy current BE."));
2421 case BE_ERR_DEMOTE:
2422 return (gettext("BE demotion failed."));
2423 case BE_ERR_DSTYPE:
2424 return (gettext("Invalid dataset type."));
2425 case BE_ERR_BE_EXISTS:
2426 return (gettext("BE exists."));
2427 case BE_ERR_INIT:
2428 return (gettext("be_zfs_init failed."));
2429 case BE_ERR_INTR:
2430 return (gettext("Interupted system call."));
2431 case BE_ERR_INVAL:
2432 return (gettext("Invalid argument."));
2433 case BE_ERR_INVALPROP:
2434 return (gettext("Invalid property for dataset."));
2435 case BE_ERR_INVALMOUNTPOINT:
2436 return (gettext("Unexpected mountpoint."));
2437 case BE_ERR_MOUNT:
2438 return (gettext("Mount failed."));
2439 case BE_ERR_MOUNTED:
2440 return (gettext("Already mounted."));
2441 case BE_ERR_NAMETOOLONG:
2442 return (gettext("name > BUFSIZ."));
2443 case BE_ERR_NOENT:
2444 return (gettext("Doesn't exist."));
2445 case BE_ERR_POOL_NOENT:
2446 return (gettext("No such pool."));
2447 case BE_ERR_NODEV:
2448 return (gettext("No such device."));
2449 case BE_ERR_NOTMOUNTED:
2450 return (gettext("File system not mounted."));
2451 case BE_ERR_NOMEM:
2452 return (gettext("Not enough memory."));
2453 case BE_ERR_NONINHERIT:
2454 return (gettext(
2455 "Property is not inheritable for the BE dataset."));
2456 case BE_ERR_NXIO:
2457 return (gettext("No such device or address."));
2458 case BE_ERR_NOSPC:
2459 return (gettext("No space on device."));
2460 case BE_ERR_NOTSUP:
2461 return (gettext("Operation not supported."));
2462 case BE_ERR_OPEN:
2463 return (gettext("Open failed."));
2464 case BE_ERR_PERM:
2465 return (gettext("Not owner."));
2466 case BE_ERR_UNAVAIL:
2467 return (gettext("The BE is currently unavailable."));
2468 case BE_ERR_PROMOTE:
2469 return (gettext("BE promotion failed."));
2470 case BE_ERR_ROFS:
2471 return (gettext("Read only file system."));
2472 case BE_ERR_READONLYDS:
2473 return (gettext("Read only dataset."));
2474 case BE_ERR_READONLYPROP:
2475 return (gettext("Read only property."));
2476 case BE_ERR_RENAME_ACTIVE:
2477 return (gettext("Renaming the active BE is not supported."));
2478 case BE_ERR_SS_EXISTS:
2479 return (gettext("Snapshot exists."));
2480 case BE_ERR_SS_NOENT:
2481 return (gettext("No such snapshot."));
2482 case BE_ERR_UMOUNT:
2483 return (gettext("Unmount failed."));
2484 case BE_ERR_UMOUNT_CURR_BE:
2485 return (gettext("Can't unmount the current BE."));
2486 case BE_ERR_UMOUNT_SHARED:
2487 return (gettext("Unmount of a shared File System failed."));
2488 case BE_ERR_FAULT:
2489 return (gettext("Bad address."));
2490 case BE_ERR_UNKNOWN:
2491 return (gettext("Unknown error."));
2492 case BE_ERR_ZFS:
2493 return (gettext("ZFS returned an error."));
2494 case BE_ERR_GEN_UUID:
2495 return (gettext("Failed to generate uuid."));
2496 case BE_ERR_PARSE_UUID:
2497 return (gettext("Failed to parse uuid."));
2498 case BE_ERR_NO_UUID:
2499 return (gettext("No uuid"));
2500 case BE_ERR_ZONE_NO_PARENTBE:
2501 return (gettext("No parent uuid"));
2502 case BE_ERR_ZONE_MULTIPLE_ACTIVE:
2503 return (gettext("Multiple active zone roots"));
2504 case BE_ERR_ZONE_NO_ACTIVE_ROOT:
2505 return (gettext("No active zone root"));
2506 case BE_ERR_ZONE_ROOT_NOT_LEGACY:
2507 return (gettext("Zone root not legacy"));
2508 case BE_ERR_MOUNT_ZONEROOT:
2509 return (gettext("Failed to mount a zone root."));
2510 case BE_ERR_UMOUNT_ZONEROOT:
2511 return (gettext("Failed to unmount a zone root."));
2512 case BE_ERR_NO_MOUNTED_ZONE:
2513 return (gettext("Zone is not mounted"));
2514 case BE_ERR_ZONES_UNMOUNT:
2515 return (gettext("Unable to unmount a zone BE."));
2516 case BE_ERR_NO_MENU:
2517 return (gettext("Missing boot menu file."));
2518 case BE_ERR_BAD_MENU_PATH:
2519 return (gettext("Invalid path for menu.lst file"));
2520 case BE_ERR_ZONE_SS_EXISTS:
2521 return (gettext("Zone snapshot exists."));
2522 case BE_ERR_BOOTFILE_INST:
2523 return (gettext("Error installing boot files."));
2524 case BE_ERR_EXTCMD:
2525 return (gettext("Error running an external command."));
2526 default:
2527 return (NULL);
2532 * Function: be_is_isa
2533 * Description: Boolean function indicating whether the instruction set
2534 * architecture of the executing system matches the name provided.
2535 * The string must match a system defined architecture (e.g.
2536 * "i386", "sparc") and is case sensitive.
2537 * Parameters: name - string representing the name of instruction set
2538 * architecture being tested
2539 * Returns: B_FALSE - the system instruction set architecture is different
2540 * from the one specified
2541 * B_TRUE - the system instruction set architecture is the same
2542 * as the one specified
2543 * Scope:
2544 * Semi-private (library wide use only)
2546 boolean_t
2547 be_is_isa(char *name)
2549 return ((strcmp((char *)be_get_default_isa(), name) == 0));
2553 * Function: be_get_default_isa
2554 * Description:
2555 * Returns the default instruction set architecture of the
2556 * machine it is executed on. (eg. sparc, i386, ...)
2557 * NOTE: SYS_INST environment variable may override default
2558 * return value
2559 * Parameters:
2560 * none
2561 * Returns:
2562 * NULL - the architecture returned by sysinfo() was too
2563 * long for local variables
2564 * char * - pointer to a string containing the default
2565 * implementation
2566 * Scope:
2567 * Semi-private (library wide use only)
2569 char *
2570 be_get_default_isa(void)
2572 int i;
2573 char *envp;
2574 static char default_inst[ARCH_LENGTH] = "";
2576 if (default_inst[0] == '\0') {
2577 if ((envp = getenv("SYS_INST")) != NULL) {
2578 if ((int)strlen(envp) >= ARCH_LENGTH)
2579 return (NULL);
2580 else
2581 (void) strcpy(default_inst, envp);
2582 } else {
2583 i = sysinfo(SI_ARCHITECTURE, default_inst, ARCH_LENGTH);
2584 if (i < 0 || i > ARCH_LENGTH)
2585 return (NULL);
2588 return (default_inst);
2592 * Function: be_get_platform
2593 * Description:
2594 * Returns the platfom name
2595 * Parameters:
2596 * none
2597 * Returns:
2598 * NULL - the platform name returned by sysinfo() was too
2599 * long for local variables
2600 * char * - pointer to a string containing the platform name
2601 * Scope:
2602 * Semi-private (library wide use only)
2604 char *
2605 be_get_platform(void)
2607 int i;
2608 static char default_inst[ARCH_LENGTH] = "";
2610 if (default_inst[0] == '\0') {
2611 i = sysinfo(SI_PLATFORM, default_inst, ARCH_LENGTH);
2612 if (i < 0 || i > ARCH_LENGTH)
2613 return (NULL);
2615 return (default_inst);
2619 * Function: be_run_cmd
2620 * Description:
2621 * Runs a command in a separate subprocess. Splits out stdout from stderr
2622 * and sends each to its own buffer. Buffers must be pre-allocated and
2623 * passed in as arguments. Buffer sizes are also passed in as arguments.
2625 * Notes / caveats:
2626 * - Command being run is assumed to not have any stdout or stderr
2627 * redirection.
2628 * - Commands which emit total stderr output of greater than PIPE_BUF
2629 * bytes can hang. For such commands, a different implementation
2630 * which uses poll(2) must be used.
2631 * - stdout_buf can be NULL. In this case, stdout_bufsize is ignored, and
2632 * the stream which would have gone to it is sent to the bit
2633 * bucket.
2634 * - stderr_buf cannot be NULL.
2635 * - Only subprocess errors are appended to the stderr_buf. Errors
2636 * running the command are reported through be_print_err().
2637 * - Data which would overflow its respective buffer is sent to the bit
2638 * bucket.
2640 * Parameters:
2641 * command: command to run. Assumed not to have embedded stdout
2642 * or stderr redirection. May have stdin redirection,
2643 * however.
2644 * stderr_buf: buffer returning subprocess stderr data. Errors
2645 * reported by this function are reported through
2646 * be_print_err().
2647 * stderr_bufsize: size of stderr_buf
2648 * stdout_buf: buffer returning subprocess stdout data.
2649 * stdout_bufsize: size of stdout_buf
2650 * Returns:
2651 * BE_SUCCESS - The command ran successfully without returning
2652 * errors.
2653 * BE_ERR_EXTCMD
2654 * - The command could not be run.
2655 * - The command terminated with error status.
2656 * - There were errors extracting or returning subprocess
2657 * data.
2658 * BE_ERR_NOMEM - The command exceeds the command buffer size.
2659 * BE_ERR_INVAL - An invalid argument was specified.
2660 * Scope:
2661 * Semi-private (library wide use only)
2664 be_run_cmd(char *command, char *stderr_buf, int stderr_bufsize,
2665 char *stdout_buf, int stdout_bufsize)
2667 char *temp_filename = strdup(tmpnam(NULL));
2668 FILE *stdout_str = NULL;
2669 FILE *stderr_str = NULL;
2670 char cmdline[BUFSIZ];
2671 char oneline[BUFSIZ];
2672 int exit_status;
2673 int rval = BE_SUCCESS;
2675 if ((command == NULL) || (stderr_buf == NULL) ||
2676 (stderr_bufsize <= 0) || (stdout_bufsize < 0) ||
2677 ((stdout_buf != NULL) ^ (stdout_bufsize != 0))) {
2678 return (BE_ERR_INVAL);
2681 /* Set up command so popen returns stderr, not stdout */
2682 if (snprintf(cmdline, BUFSIZ, "%s 2> %s", command,
2683 temp_filename) >= BUFSIZ) {
2684 rval = BE_ERR_NOMEM;
2685 goto cleanup;
2688 /* Set up the fifo that will make stderr available. */
2689 if (mkfifo(temp_filename, 0600) != 0) {
2690 (void) be_print_err(gettext("be_run_cmd: mkfifo: %s\n"),
2691 strerror(errno));
2692 rval = BE_ERR_EXTCMD;
2693 goto cleanup;
2696 if ((stdout_str = popen(cmdline, "r")) == NULL) {
2697 (void) be_print_err(gettext("be_run_cmd: popen: %s\n"),
2698 strerror(errno));
2699 rval = BE_ERR_EXTCMD;
2700 goto cleanup;
2703 if ((stderr_str = fopen(temp_filename, "r")) == NULL) {
2704 (void) be_print_err(gettext("be_run_cmd: fopen: %s\n"),
2705 strerror(errno));
2706 (void) pclose(stdout_str);
2707 rval = BE_ERR_EXTCMD;
2708 goto cleanup;
2711 /* Read stdout first, as it usually outputs more than stderr. */
2712 oneline[BUFSIZ-1] = '\0';
2713 while (fgets(oneline, BUFSIZ-1, stdout_str) != NULL) {
2714 if (stdout_str != NULL) {
2715 (void) strlcat(stdout_buf, oneline, stdout_bufsize);
2719 while (fgets(oneline, BUFSIZ-1, stderr_str) != NULL) {
2720 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
2723 /* Close pipe, get exit status. */
2724 if ((exit_status = pclose(stdout_str)) == -1) {
2725 (void) be_print_err(gettext("be_run_cmd: pclose: %s\n"),
2726 strerror(errno));
2727 rval = BE_ERR_EXTCMD;
2728 } else if (WIFEXITED(exit_status)) {
2729 exit_status = (int)((char)WEXITSTATUS(exit_status));
2731 * error code BC_NOUPDT means more recent version
2732 * is installed
2734 if (exit_status != BC_SUCCESS && exit_status != BC_NOUPDT) {
2735 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: "
2736 "command terminated with error status: %d\n"),
2737 exit_status);
2738 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
2739 rval = BE_ERR_EXTCMD;
2741 } else {
2742 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: command "
2743 "terminated on signal: %s\n"),
2744 strsignal(WTERMSIG(exit_status)));
2745 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
2746 rval = BE_ERR_EXTCMD;
2749 cleanup:
2750 (void) unlink(temp_filename);
2751 (void) free(temp_filename);
2753 return (rval);
2756 /* ******************************************************************** */
2757 /* Private Functions */
2758 /* ******************************************************************** */
2761 * Function: update_dataset
2762 * Description: This function takes a dataset name and replaces the zpool
2763 * and be_name components of the dataset with the new be_name
2764 * zpool passed in.
2765 * Parameters:
2766 * dataset - name of dataset
2767 * dataset_len - lenth of buffer in which dataset is passed in.
2768 * be_name - name of new BE name to update to.
2769 * old_rc_loc - dataset under which the root container dataset
2770 * for the old BE lives.
2771 * new_rc_loc - dataset under which the root container dataset
2772 * for the new BE lives.
2773 * Returns:
2774 * BE_SUCCESS - Success
2775 * be_errno_t - Failure
2776 * Scope:
2777 * Private
2779 static int
2780 update_dataset(char *dataset, int dataset_len, char *be_name,
2781 char *old_rc_loc, char *new_rc_loc)
2783 char *ds = NULL;
2784 char *sub_ds = NULL;
2786 /* Tear off the BE container dataset */
2787 if ((ds = be_make_name_from_ds(dataset, old_rc_loc)) == NULL) {
2788 return (BE_ERR_INVAL);
2791 /* Get dataset name relative to BE root, if there is one */
2792 sub_ds = strchr(ds, '/');
2794 /* Generate the BE root dataset name */
2795 be_make_root_ds(new_rc_loc, be_name, dataset, dataset_len);
2797 /* If a subordinate dataset name was found, append it */
2798 if (sub_ds != NULL)
2799 (void) strlcat(dataset, sub_ds, dataset_len);
2801 free(ds);
2802 return (BE_SUCCESS);
2806 * Function: _update_vfstab
2807 * Description: This function updates a vfstab file to reflect the new
2808 * root container dataset location and be_name for all
2809 * entries listed in the be_fs_list_data_t structure passed in.
2810 * Parameters:
2811 * vfstab - vfstab file to modify
2812 * be_name - name of BE to update.
2813 * old_rc_loc - dataset under which the root container dataset
2814 * of the old BE resides in.
2815 * new_rc_loc - dataset under which the root container dataset
2816 * of the new BE resides in.
2817 * fld - be_fs_list_data_t pointer providing the list of
2818 * file systems to look for in vfstab.
2819 * Returns:
2820 * BE_SUCCESS - Success
2821 * be_errno_t - Failure
2822 * Scope:
2823 * Private
2825 static int
2826 _update_vfstab(char *vfstab, char *be_name, char *old_rc_loc,
2827 char *new_rc_loc, be_fs_list_data_t *fld)
2829 struct vfstab vp;
2830 char *tmp_vfstab = NULL;
2831 char comments_buf[BUFSIZ];
2832 FILE *comments = NULL;
2833 FILE *vfs_ents = NULL;
2834 FILE *tfile = NULL;
2835 struct stat sb;
2836 char dev[MAXPATHLEN];
2837 char *c;
2838 int fd;
2839 int ret = BE_SUCCESS, err = 0;
2840 int i;
2841 int tmp_vfstab_len = 0;
2843 errno = 0;
2846 * Open vfstab for reading twice. First is for comments,
2847 * second is for actual entries.
2849 if ((comments = fopen(vfstab, "r")) == NULL ||
2850 (vfs_ents = fopen(vfstab, "r")) == NULL) {
2851 err = errno;
2852 be_print_err(gettext("_update_vfstab: "
2853 "failed to open vfstab (%s): %s\n"), vfstab,
2854 strerror(err));
2855 ret = errno_to_be_err(err);
2856 goto cleanup;
2859 /* Grab the stats of the original vfstab file */
2860 if (stat(vfstab, &sb) != 0) {
2861 err = errno;
2862 be_print_err(gettext("_update_vfstab: "
2863 "failed to stat file %s: %s\n"), vfstab,
2864 strerror(err));
2865 ret = errno_to_be_err(err);
2866 goto cleanup;
2869 /* Create tmp file for modified vfstab */
2870 if ((tmp_vfstab = (char *)malloc(strlen(vfstab) + 7))
2871 == NULL) {
2872 be_print_err(gettext("_update_vfstab: "
2873 "malloc failed\n"));
2874 ret = BE_ERR_NOMEM;
2875 goto cleanup;
2877 tmp_vfstab_len = strlen(vfstab) + 7;
2878 (void) memset(tmp_vfstab, 0, tmp_vfstab_len);
2879 (void) strlcpy(tmp_vfstab, vfstab, tmp_vfstab_len);
2880 (void) strlcat(tmp_vfstab, "XXXXXX", tmp_vfstab_len);
2881 if ((fd = mkstemp(tmp_vfstab)) == -1) {
2882 err = errno;
2883 be_print_err(gettext("_update_vfstab: "
2884 "mkstemp failed: %s\n"), strerror(err));
2885 ret = errno_to_be_err(err);
2886 goto cleanup;
2888 if ((tfile = fdopen(fd, "w")) == NULL) {
2889 err = errno;
2890 be_print_err(gettext("_update_vfstab: "
2891 "could not open file for write\n"));
2892 (void) close(fd);
2893 ret = errno_to_be_err(err);
2894 goto cleanup;
2897 while (fgets(comments_buf, BUFSIZ, comments)) {
2898 for (c = comments_buf; *c != '\0' && isspace(*c); c++)
2900 if (*c == '\0') {
2901 continue;
2902 } else if (*c == '#') {
2904 * If line is a comment line, just put
2905 * it through to the tmp vfstab.
2907 (void) fputs(comments_buf, tfile);
2908 } else {
2910 * Else line is a vfstab entry, grab it
2911 * into a vfstab struct.
2913 if (getvfsent(vfs_ents, &vp) != 0) {
2914 err = errno;
2915 be_print_err(gettext("_update_vfstab: "
2916 "getvfsent failed: %s\n"), strerror(err));
2917 ret = errno_to_be_err(err);
2918 goto cleanup;
2921 if (vp.vfs_special == NULL || vp.vfs_mountp == NULL) {
2922 (void) putvfsent(tfile, &vp);
2923 continue;
2927 * If the entry is one of the entries in the list
2928 * of file systems to update, modify it's device
2929 * field to be correct for this BE.
2931 for (i = 0; i < fld->fs_num; i++) {
2932 if (strcmp(vp.vfs_special, fld->fs_list[i])
2933 == 0) {
2935 * Found entry that needs an update.
2936 * Replace the root container dataset
2937 * location and be_name in the
2938 * entry's device.
2940 (void) strlcpy(dev, vp.vfs_special,
2941 sizeof (dev));
2943 if ((ret = update_dataset(dev,
2944 sizeof (dev), be_name, old_rc_loc,
2945 new_rc_loc)) != 0) {
2946 be_print_err(
2947 gettext("_update_vfstab: "
2948 "Failed to update device "
2949 "field for vfstab entry "
2950 "%s\n"), fld->fs_list[i]);
2951 goto cleanup;
2954 vp.vfs_special = dev;
2955 break;
2959 /* Put entry through to tmp vfstab */
2960 (void) putvfsent(tfile, &vp);
2964 (void) fclose(comments);
2965 comments = NULL;
2966 (void) fclose(vfs_ents);
2967 vfs_ents = NULL;
2968 (void) fclose(tfile);
2969 tfile = NULL;
2971 /* Copy tmp vfstab into place */
2972 if (rename(tmp_vfstab, vfstab) != 0) {
2973 err = errno;
2974 be_print_err(gettext("_update_vfstab: "
2975 "failed to rename file %s to %s: %s\n"), tmp_vfstab,
2976 vfstab, strerror(err));
2977 ret = errno_to_be_err(err);
2978 goto cleanup;
2981 /* Set the perms and ownership of the updated file */
2982 if (chmod(vfstab, sb.st_mode) != 0) {
2983 err = errno;
2984 be_print_err(gettext("_update_vfstab: "
2985 "failed to chmod %s: %s\n"), vfstab, strerror(err));
2986 ret = errno_to_be_err(err);
2987 goto cleanup;
2989 if (chown(vfstab, sb.st_uid, sb.st_gid) != 0) {
2990 err = errno;
2991 be_print_err(gettext("_update_vfstab: "
2992 "failed to chown %s: %s\n"), vfstab, strerror(err));
2993 ret = errno_to_be_err(err);
2994 goto cleanup;
2997 cleanup:
2998 if (comments != NULL)
2999 (void) fclose(comments);
3000 if (vfs_ents != NULL)
3001 (void) fclose(vfs_ents);
3002 (void) unlink(tmp_vfstab);
3003 (void) free(tmp_vfstab);
3004 if (tfile != NULL)
3005 (void) fclose(tfile);
3007 return (ret);
3012 * Function: be_get_auto_name
3013 * Description: Generate an auto name constructed based on the BE name
3014 * of the original BE or zone BE being cloned.
3015 * Parameters:
3016 * obe_name - name of the original BE or zone BE being cloned.
3017 * container_ds - container dataset for the zone.
3018 * Note: if zone_be is false this should be
3019 * NULL.
3020 * zone_be - flag that indicates if we are operating on a zone BE.
3021 * Returns:
3022 * Success - pointer to auto generated BE name. The name
3023 * is allocated in heap storage so the caller is
3024 * responsible for free'ing the name.
3025 * Failure - NULL
3026 * Scope:
3027 * Private
3029 static char *
3030 be_get_auto_name(char *obe_name, char *be_container_ds, boolean_t zone_be)
3032 be_node_list_t *be_nodes = NULL;
3033 be_node_list_t *cur_be = NULL;
3034 char auto_be_name[MAXPATHLEN];
3035 char base_be_name[MAXPATHLEN];
3036 char cur_be_name[MAXPATHLEN];
3037 char *num_str = NULL;
3038 char *c = NULL;
3039 int num = 0;
3040 int cur_num = 0;
3042 errno = 0;
3045 * Check if obe_name is already in an auto BE name format.
3046 * If it is, then strip off the increment number to get the
3047 * base name.
3049 (void) strlcpy(base_be_name, obe_name, sizeof (base_be_name));
3051 if ((num_str = strrchr(base_be_name, BE_AUTO_NAME_DELIM))
3052 != NULL) {
3053 /* Make sure remaining string is all digits */
3054 c = num_str + 1;
3055 while (c[0] != '\0' && isdigit(c[0]))
3056 c++;
3058 * If we're now at the end of the string strip off the
3059 * increment number.
3061 if (c[0] == '\0')
3062 num_str[0] = '\0';
3065 if (zone_be) {
3066 if (be_container_ds == NULL)
3067 return (NULL);
3068 if (be_get_zone_be_list(obe_name, be_container_ds,
3069 &be_nodes) != BE_SUCCESS) {
3070 be_print_err(gettext("be_get_auto_name: "
3071 "be_get_zone_be_list failed\n"));
3072 return (NULL);
3074 } else if (_be_list(NULL, &be_nodes) != BE_SUCCESS) {
3075 be_print_err(gettext("be_get_auto_name: be_list failed\n"));
3076 return (NULL);
3079 for (cur_be = be_nodes; cur_be != NULL; cur_be = cur_be->be_next_node) {
3080 (void) strlcpy(cur_be_name, cur_be->be_node_name,
3081 sizeof (cur_be_name));
3083 /* If cur_be_name doesn't match at least base be name, skip. */
3084 if (strncmp(cur_be_name, base_be_name, strlen(base_be_name))
3085 != 0)
3086 continue;
3088 /* Get the string following the base be name */
3089 num_str = cur_be_name + strlen(base_be_name);
3092 * If nothing follows the base be name, this cur_be_name
3093 * is the BE named with the base be name, skip.
3095 if (num_str == NULL || num_str[0] == '\0')
3096 continue;
3099 * Remove the name delimiter. If its not there,
3100 * cur_be_name isn't part of this BE name stream, skip.
3102 if (num_str[0] == BE_AUTO_NAME_DELIM)
3103 num_str++;
3104 else
3105 continue;
3107 /* Make sure remaining string is all digits */
3108 c = num_str;
3109 while (c[0] != '\0' && isdigit(c[0]))
3110 c++;
3111 if (c[0] != '\0')
3112 continue;
3114 /* Convert the number string to an int */
3115 cur_num = atoi(num_str);
3118 * If failed to convert the string, skip it. If its too
3119 * long to be converted to an int, we wouldn't auto generate
3120 * this number anyway so there couldn't be a conflict.
3121 * We treat it as a manually created BE name.
3123 if (cur_num == 0 && errno == EINVAL)
3124 continue;
3127 * Compare current number to current max number,
3128 * take higher of the two.
3130 if (cur_num > num)
3131 num = cur_num;
3135 * Store off a copy of 'num' incase we need it later. If incrementing
3136 * 'num' causes it to roll over, this means 'num' is the largest
3137 * positive int possible; we'll need it later in the loop to determine
3138 * if we've exhausted all possible increment numbers. We store it in
3139 * 'cur_num'.
3141 cur_num = num;
3143 /* Increment 'num' to get new auto BE name number */
3144 if (++num <= 0) {
3145 int ret = 0;
3148 * Since incrementing 'num' caused it to rollover, start
3149 * over at 0 and find the first available number.
3151 for (num = 0; num < cur_num; num++) {
3153 (void) snprintf(cur_be_name, sizeof (cur_be_name),
3154 "%s%c%d", base_be_name, BE_AUTO_NAME_DELIM, num);
3156 ret = zpool_iter(g_zfs, be_exists_callback,
3157 cur_be_name);
3159 if (ret == 0) {
3161 * BE name doesn't exist, break out
3162 * to use 'num'.
3164 break;
3165 } else if (ret == 1) {
3166 /* BE name exists, continue looking */
3167 continue;
3168 } else {
3169 be_print_err(gettext("be_get_auto_name: "
3170 "zpool_iter failed: %s\n"),
3171 libzfs_error_description(g_zfs));
3172 be_free_list(be_nodes);
3173 return (NULL);
3178 * If 'num' equals 'cur_num', we've exhausted all possible
3179 * auto BE names for this base BE name.
3181 if (num == cur_num) {
3182 be_print_err(gettext("be_get_auto_name: "
3183 "No more available auto BE names for base "
3184 "BE name %s\n"), base_be_name);
3185 be_free_list(be_nodes);
3186 return (NULL);
3190 be_free_list(be_nodes);
3193 * Generate string for auto BE name.
3195 (void) snprintf(auto_be_name, sizeof (auto_be_name), "%s%c%d",
3196 base_be_name, BE_AUTO_NAME_DELIM, num);
3198 if ((c = strdup(auto_be_name)) == NULL) {
3199 be_print_err(gettext("be_get_auto_name: "
3200 "memory allocation failed\n"));
3201 return (NULL);
3204 return (c);
3208 * Function: be_create_menu
3209 * Description:
3210 * This function is used if no menu.lst file exists. In
3211 * this case a new file is created and if needed default
3212 * lines are added to the file.
3213 * Parameters:
3214 * pool - The name of the pool the menu.lst file is on
3215 * menu_file - The name of the file we're creating.
3216 * menu_fp - A pointer to the file pointer of the file we
3217 * created. This is also used to pass back the file
3218 * pointer to the newly created file.
3219 * mode - the original mode used for the failed attempt to
3220 * non-existent file.
3221 * Returns:
3222 * BE_SUCCESS - Success
3223 * be_errno_t - Failure
3224 * Scope:
3225 * Private
3227 static int
3228 be_create_menu(
3229 char *pool,
3230 char *menu_file,
3231 FILE **menu_fp,
3232 char *mode)
3234 be_node_list_t *be_nodes = NULL;
3235 char *menu_path = NULL;
3236 char *be_rpool = NULL;
3237 char *be_name = NULL;
3238 errno = 0;
3240 if (menu_file == NULL || menu_fp == NULL || mode == NULL)
3241 return (BE_ERR_INVAL);
3243 menu_path = strdup(menu_file);
3244 if (menu_path == NULL)
3245 return (BE_ERR_NOMEM);
3247 (void) dirname(menu_path);
3248 if (*menu_path == '.') {
3249 free(menu_path);
3250 return (BE_ERR_BAD_MENU_PATH);
3252 if (mkdirp(menu_path,
3253 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
3254 errno != EEXIST) {
3255 free(menu_path);
3256 be_print_err(gettext("be_create_menu: Failed to create the %s "
3257 "directory: %s\n"), menu_path, strerror(errno));
3258 return (errno_to_be_err(errno));
3260 free(menu_path);
3263 * The menu file doesn't exist so we need to create a
3264 * blank file.
3266 FILE *temp_fp = fopen(menu_file, "w+");
3267 if (temp_fp == NULL) {
3268 *menu_fp = NULL;
3269 return (errno_to_be_err(errno));
3271 (void) fclose(temp_fp);
3274 * Now we need to add all the BE's back into the the file.
3276 if (_be_list(NULL, &be_nodes) == BE_SUCCESS) {
3277 while (be_nodes != NULL) {
3278 if (strcmp(pool, be_nodes->be_rpool) == 0) {
3279 (void) be_append_menu(be_nodes->be_node_name,
3280 be_nodes->be_rpool, NULL, NULL, NULL);
3282 if (be_nodes->be_active_on_boot) {
3283 be_rpool = strdup(be_nodes->be_rpool);
3284 be_name = strdup(be_nodes->be_node_name);
3287 be_nodes = be_nodes->be_next_node;
3290 be_free_list(be_nodes);
3292 *menu_fp = fopen(menu_file, mode);
3293 if (*menu_fp == NULL)
3294 return (errno_to_be_err(errno));
3296 return (BE_SUCCESS);
3300 * Function: be_open_menu
3301 * Description:
3302 * This function is used it open the menu.lst file. If this
3303 * file does not exist be_create_menu is called to create it
3304 * and the open file pointer is returned. If the file does
3305 * exist it is simply opened using the mode passed in.
3306 * Parameters:
3307 * pool - The name of the pool the menu.lst file is on
3308 * menu_file - The name of the file we're opening.
3309 * menu_fp - A pointer to the file pointer of the file we're
3310 * opening. This is also used to pass back the file
3311 * pointer.
3312 * mode - the original mode to be used for opening the menu.lst
3313 * file.
3314 * create_menu - If this is true and the menu.lst file does not
3315 * exist we will attempt to re-create it. However
3316 * if it's false the error returned from the fopen
3317 * will be returned.
3318 * Returns:
3319 * BE_SUCCESS - Success
3320 * be_errno_t - Failure
3321 * Scope:
3322 * Private
3324 static int
3325 be_open_menu(
3326 char *pool,
3327 char *menu_file,
3328 FILE **menu_fp,
3329 char *mode,
3330 boolean_t create_menu)
3332 int err = 0;
3333 boolean_t set_print = B_FALSE;
3335 *menu_fp = fopen(menu_file, mode);
3336 err = errno;
3337 if (*menu_fp == NULL) {
3338 if (err == ENOENT && create_menu) {
3339 be_print_err(gettext("be_open_menu: menu.lst "
3340 "file %s does not exist,\n"), menu_file);
3341 if (!do_print) {
3342 set_print = B_TRUE;
3343 do_print = B_TRUE;
3345 be_print_err(gettext("WARNING: menu.lst "
3346 "file %s does not exist,\n generating "
3347 "a new menu.lst file\n"), menu_file);
3348 if (set_print)
3349 do_print = B_FALSE;
3350 err = 0;
3351 if ((err = be_create_menu(pool, menu_file,
3352 menu_fp, mode)) == ENOENT)
3353 return (BE_ERR_NO_MENU);
3354 else if (err != BE_SUCCESS)
3355 return (err);
3356 else if (*menu_fp == NULL)
3357 return (BE_ERR_NO_MENU);
3358 } else {
3359 be_print_err(gettext("be_open_menu: failed "
3360 "to open menu.lst file %s\n"), menu_file);
3361 if (err == ENOENT)
3362 return (BE_ERR_NO_MENU);
3363 else
3364 return (errno_to_be_err(err));
3367 return (BE_SUCCESS);