1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
28 */
29
30
31 /*
32 * System includes
33 */
34 #include <assert.h>
35 #include <errno.h>
36 #include <libgen.h>
37 #include <libintl.h>
38 #include <libnvpair.h>
39 #include <libzfs.h>
40 #include <libgen.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sys/stat.h>
45 #include <sys/types.h>
46 #include <sys/vfstab.h>
47 #include <sys/param.h>
48 #include <sys/systeminfo.h>
49 #include <ctype.h>
50 #include <time.h>
51 #include <unistd.h>
52 #include <fcntl.h>
53 #include <deflt.h>
54 #include <wait.h>
55 #include <libdevinfo.h>
56
57 #include <libbe.h>
58 #include <libbe_priv.h>
59
60 /* Private function prototypes */
61 static int update_dataset(char *, int, char *, char *, char *);
62 static int _update_vfstab(char *, char *, char *, char *, be_fs_list_data_t *);
63 static int be_open_menu(char *, char *, FILE **, char *, boolean_t);
64 static int be_create_menu(char *, char *, FILE **, char *);
65 static char *be_get_auto_name(char *, char *, boolean_t);
66
67 /*
68 * Global error printing
69 */
70 boolean_t do_print = B_FALSE;
71
72 /*
73 * Private datatypes
74 */
75 typedef struct zone_be_name_cb_data {
76 char *base_be_name;
77 int num;
78 } zone_be_name_cb_data_t;
79
80 /* ******************************************************************** */
81 /* Public Functions */
82 /* ******************************************************************** */
83
84 /*
85 * Function: be_max_avail
86 * Description: Returns the available size for the zfs dataset passed in.
87 * Parameters:
88 * dataset - The dataset we want to get the available space for.
89 * ret - The available size will be returned in this.
90 * Returns:
91 * The error returned by the zfs get property function.
92 * Scope:
93 * Public
94 */
95 int
96 be_max_avail(char *dataset, uint64_t *ret)
97 {
98 zfs_handle_t *zhp;
99 int err = 0;
100
101 /* Initialize libzfs handle */
102 if (!be_zfs_init())
103 return (BE_ERR_INIT);
104
105 zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET);
106 if (zhp == NULL) {
107 /*
108 * The zfs_open failed return an error
109 */
110 err = zfs_err_to_be_err(g_zfs);
111 } else {
112 err = be_maxsize_avail(zhp, ret);
113 }
114 ZFS_CLOSE(zhp);
115 be_zfs_fini();
116 return (err);
117 }
118
119 /*
120 * Function: libbe_print_errors
121 * Description: Turns on/off error output for the library.
122 * Parameter:
123 * set_do_print - Boolean that turns library error
124 * printing on or off.
125 * Returns:
126 * None
127 * Scope:
128 * Public;
129 */
130 void
131 libbe_print_errors(boolean_t set_do_print)
132 {
133 do_print = set_do_print;
134 }
135
136 /* ******************************************************************** */
137 /* Semi-Private Functions */
138 /* ******************************************************************** */
139
140 /*
141 * Function: be_zfs_init
142 * Description: Initializes the libary global libzfs handle.
143 * Parameters:
144 * None
145 * Returns:
146 * B_TRUE - Success
147 * B_FALSE - Failure
148 * Scope:
149 * Semi-private (library wide use only)
150 */
151 boolean_t
152 be_zfs_init(void)
153 {
154 be_zfs_fini();
155
156 if ((g_zfs = libzfs_init()) == NULL) {
157 be_print_err(gettext("be_zfs_init: failed to initialize ZFS "
158 "library\n"));
159 return (B_FALSE);
160 }
161
162 return (B_TRUE);
163 }
164
165 /*
166 * Function: be_zfs_fini
167 * Description: Closes the library global libzfs handle if it currently open.
168 * Parameter:
169 * None
170 * Returns:
171 * None
172 * Scope:
173 * Semi-private (library wide use only)
174 */
175 void
176 be_zfs_fini(void)
177 {
178 if (g_zfs)
179 libzfs_fini(g_zfs);
180
181 g_zfs = NULL;
182 }
183
184 /*
185 * Function: be_get_defaults
186 * Description: Open defaults and gets be default paramets
187 * Parameters:
188 * defaults - be defaults struct
189 * Returns:
190 * None
191 * Scope:
192 * Semi-private (library wide use only)
193 */
194 void
195 be_get_defaults(struct be_defaults *defaults)
196 {
197 void *defp;
198
199 defaults->be_deflt_rpool_container = B_FALSE;
200 defaults->be_deflt_bename_starts_with[0] = '\0';
201
202 if ((defp = defopen_r(BE_DEFAULTS)) != NULL) {
203 const char *res = defread_r(BE_DFLT_BENAME_STARTS, defp);
204 if (res != NULL && res[0] != NULL) {
205 (void) strlcpy(defaults->be_deflt_bename_starts_with,
206 res, ZFS_MAXNAMELEN);
207 defaults->be_deflt_rpool_container = B_TRUE;
208 }
209 defclose_r(defp);
210 }
211 }
212
213 /*
214 * Function: be_make_root_ds
215 * Description: Generate string for BE's root dataset given the pool
216 * it lives in and the BE name.
217 * Parameters:
218 * zpool - pointer zpool name.
219 * be_name - pointer to BE name.
220 * be_root_ds - pointer to buffer to return BE root dataset in.
221 * be_root_ds_size - size of be_root_ds
222 * Returns:
223 * None
224 * Scope:
225 * Semi-private (library wide use only)
226 */
227 void
228 be_make_root_ds(const char *zpool, const char *be_name, char *be_root_ds,
229 int be_root_ds_size)
230 {
231 struct be_defaults be_defaults;
232 be_get_defaults(&be_defaults);
233
234 if (be_defaults.be_deflt_rpool_container)
235 (void) snprintf(be_root_ds, be_root_ds_size, "%s/%s", zpool,
236 be_name);
237 else
238 (void) snprintf(be_root_ds, be_root_ds_size, "%s/%s/%s", zpool,
239 BE_CONTAINER_DS_NAME, be_name);
240 }
241
242 /*
243 * Function: be_make_container_ds
244 * Description: Generate string for the BE container dataset given a pool name.
245 * Parameters:
246 * zpool - pointer zpool name.
247 * container_ds - pointer to buffer to return BE container
248 * dataset in.
249 * container_ds_size - size of container_ds
250 * Returns:
251 * None
252 * Scope:
253 * Semi-private (library wide use only)
254 */
255 void
256 be_make_container_ds(const char *zpool, char *container_ds,
257 int container_ds_size)
258 {
259 struct be_defaults be_defaults;
260 be_get_defaults(&be_defaults);
261
262 if (be_defaults.be_deflt_rpool_container)
263 (void) snprintf(container_ds, container_ds_size, "%s", zpool);
264 else
265 (void) snprintf(container_ds, container_ds_size, "%s/%s", zpool,
266 BE_CONTAINER_DS_NAME);
267 }
268
269 /*
270 * Function: be_make_name_from_ds
271 * Description: This function takes a dataset name and strips off the
272 * BE container dataset portion from the beginning. The
273 * returned name is allocated in heap storage, so the caller
274 * is responsible for freeing it.
275 * Parameters:
276 * dataset - dataset to get name from.
277 * rc_loc - dataset underwhich the root container dataset lives.
278 * Returns:
279 * name of dataset relative to BE container dataset.
280 * NULL if dataset is not under a BE root dataset.
281 * Scope:
282 * Semi-primate (library wide use only)
283 */
284 char *
285 be_make_name_from_ds(const char *dataset, char *rc_loc)
286 {
287 char ds[ZFS_MAXNAMELEN];
288 char *tok = NULL;
289 char *name = NULL;
290 struct be_defaults be_defaults;
291 int rlen = strlen(rc_loc);
292
293 be_get_defaults(&be_defaults);
294
295 /*
296 * First token is the location of where the root container dataset
297 * lives; it must match rc_loc.
298 */
299 if (strncmp(dataset, rc_loc, rlen) == 0 && dataset[rlen] == '/')
300 (void) strlcpy(ds, dataset + rlen + 1, sizeof (ds));
301 else
302 return (NULL);
303
304 if (be_defaults.be_deflt_rpool_container) {
305 if ((name = strdup(ds)) == NULL) {
306 be_print_err(gettext("be_make_name_from_ds: "
307 "memory allocation failed\n"));
308 return (NULL);
309 }
310 } else {
311 /* Second token must be BE container dataset name */
312 if ((tok = strtok(ds, "/")) == NULL ||
313 strcmp(tok, BE_CONTAINER_DS_NAME) != 0)
314 return (NULL);
315
316 /* Return the remaining token if one exists */
317 if ((tok = strtok(NULL, "")) == NULL)
318 return (NULL);
319
320 if ((name = strdup(tok)) == NULL) {
321 be_print_err(gettext("be_make_name_from_ds: "
322 "memory allocation failed\n"));
323 return (NULL);
324 }
325 }
326
327 return (name);
328 }
329
330 /*
331 * Function: be_maxsize_avail
332 * Description: Returns the available size for the zfs handle passed in.
333 * Parameters:
334 * zhp - A pointer to the open zfs handle.
335 * ret - The available size will be returned in this.
336 * Returns:
337 * The error returned by the zfs get property function.
338 * Scope:
339 * Semi-private (library wide use only)
340 */
341 int
342 be_maxsize_avail(zfs_handle_t *zhp, uint64_t *ret)
343 {
344 return ((*ret = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE)));
345 }
346
347 /*
348 * Function: be_append_menu
349 * Description: Appends an entry for a BE into the menu.lst.
350 * Parameters:
351 * be_name - pointer to name of BE to add boot menu entry for.
352 * be_root_pool - pointer to name of pool BE lives in.
353 * boot_pool - Used if the pool containing the grub menu is
354 * different than the one contaiing the BE. This
355 * will normally be NULL.
356 * be_orig_root_ds - The root dataset for the BE. This is
357 * used to check to see if an entry already exists
358 * for this BE.
359 * description - pointer to description of BE to be added in
360 * the title line for this BEs entry.
361 * Returns:
362 * BE_SUCCESS - Success
363 * be_errno_t - Failure
364 * Scope:
365 * Semi-private (library wide use only)
366 */
367 int
368 be_append_menu(char *be_name, char *be_root_pool, char *boot_pool,
369 char *be_orig_root_ds, char *description)
370 {
371 zfs_handle_t *zhp = NULL;
372 char menu_file[MAXPATHLEN];
373 char be_root_ds[MAXPATHLEN];
374 char line[BUFSIZ];
375 char temp_line[BUFSIZ];
376 char title[MAXPATHLEN];
377 char *entries[BUFSIZ];
378 char *tmp_entries[BUFSIZ];
379 char *pool_mntpnt = NULL;
380 char *ptmp_mntpnt = NULL;
381 char *orig_mntpnt = NULL;
382 boolean_t found_be = B_FALSE;
383 boolean_t found_orig_be = B_FALSE;
384 boolean_t found_title = B_FALSE;
385 boolean_t pool_mounted = B_FALSE;
386 boolean_t collect_lines = B_FALSE;
387 FILE *menu_fp = NULL;
388 int err = 0, ret = BE_SUCCESS;
389 int i, num_tmp_lines = 0, num_lines = 0;
390
391 if (be_name == NULL || be_root_pool == NULL)
392 return (BE_ERR_INVAL);
393
394 if (boot_pool == NULL)
395 boot_pool = be_root_pool;
396
397 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
398 be_print_err(gettext("be_append_menu: failed to open "
399 "pool dataset for %s: %s\n"), be_root_pool,
400 libzfs_error_description(g_zfs));
401 return (zfs_err_to_be_err(g_zfs));
402 }
403
404 /*
405 * Check to see if the pool's dataset is mounted. If it isn't we'll
406 * attempt to mount it.
407 */
408 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
409 &pool_mounted)) != BE_SUCCESS) {
410 be_print_err(gettext("be_append_menu: pool dataset "
411 "(%s) could not be mounted\n"), be_root_pool);
412 ZFS_CLOSE(zhp);
413 return (ret);
414 }
415
416 /*
417 * Get the mountpoint for the root pool dataset.
418 */
419 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
420 be_print_err(gettext("be_append_menu: pool "
421 "dataset (%s) is not mounted. Can't set "
422 "the default BE in the grub menu.\n"), be_root_pool);
423 ret = BE_ERR_NO_MENU;
424 goto cleanup;
425 }
426
427 /*
428 * Check to see if this system supports grub
429 */
430 if (be_has_grub()) {
431 (void) snprintf(menu_file, sizeof (menu_file),
432 "%s%s", pool_mntpnt, BE_GRUB_MENU);
433 } else {
434 (void) snprintf(menu_file, sizeof (menu_file),
435 "%s%s", pool_mntpnt, BE_SPARC_MENU);
436 }
437
438 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
439
440 /*
441 * Iterate through menu first to make sure the BE doesn't already
442 * have an entry in the menu.
443 *
444 * Additionally while iterating through the menu, if we have an
445 * original root dataset for a BE we're cloning from, we need to keep
446 * track of that BE's menu entry. We will then use the lines from
447 * that entry to create the entry for the new BE.
448 */
449 if ((ret = be_open_menu(be_root_pool, menu_file,
450 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
451 goto cleanup;
452 } else if (menu_fp == NULL) {
453 ret = BE_ERR_NO_MENU;
454 goto cleanup;
455 }
456
457 free(pool_mntpnt);
458 pool_mntpnt = NULL;
459
460 while (fgets(line, BUFSIZ, menu_fp)) {
461 char *tok = NULL;
462
463 (void) strlcpy(temp_line, line, BUFSIZ);
464 tok = strtok(line, "=\n");
465
466 if (tok == NULL || tok[0] == '#') {
467 continue;
468 } else if (strcmp(tok, "entry_name") == 0) {
469 collect_lines = B_FALSE;
470 if ((tok = strtok(NULL, "\n")) == NULL)
471 (void) strlcpy(title, "", sizeof (title));
472 else
473 (void) strlcpy(title, tok, sizeof (title));
474 found_title = B_TRUE;
475
476 if (num_tmp_lines != 0) {
477 for (i = 0; i < num_tmp_lines; i++) {
478 free(tmp_entries[i]);
479 tmp_entries[i] = NULL;
480 }
481 num_tmp_lines = 0;
482 }
483 } else if (strcmp(tok, "data_set") == 0) {
484 char *bootfs = strtok(NULL, BE_WHITE_SPACE);
485 found_title = B_FALSE;
486 if (bootfs == NULL)
487 continue;
488
489 if (strcmp(bootfs, be_root_ds) == 0) {
490 found_be = B_TRUE;
491 break;
492 }
493
494 if (be_orig_root_ds != NULL &&
495 strcmp(bootfs, be_orig_root_ds) == 0 &&
496 !found_orig_be) {
497 char str[BUFSIZ];
498 found_orig_be = B_TRUE;
499 num_lines = 0;
500 /*
501 * Store the new title line
502 */
503 (void) snprintf(str, BUFSIZ, "entry_name=%s\n",
504 description ? description : be_name);
505 entries[num_lines] = strdup(str);
506 num_lines++;
507 /*
508 * If there are any lines between the title
509 * and the bootfs line store these. Also
510 * free the temporary lines.
511 */
512 for (i = 0; i < num_tmp_lines; i++) {
513 entries[num_lines] = tmp_entries[i];
514 tmp_entries[i] = NULL;
515 num_lines++;
516 }
517
518 zprop_get_cbdata_t cb = { 0 };
519 zprop_source_t src;
520 char * bc = strchr(be_root_ds, '/');
521 char sguid[] = "guid";
522
523 *bc = '\0';
524 cb.cb_first = B_TRUE;
525 cb.cb_sources = ZPROP_SRC_ALL;
526 cb.cb_type = ZFS_TYPE_POOL;
527 zprop_get_list(g_zfs, sguid, &cb.cb_proplist, ZFS_TYPE_POOL);
528 zpool_handle_t * z_hndl = zpool_open(g_zfs, be_root_ds);
529 *bc = '/';
530 if (z_hndl) {
531 uint64_t guid = zpool_get_prop_int(z_hndl, cb.cb_proplist->pl_prop, &src);
532 (void) snprintf(str, BUFSIZ, "pool_uuid=%llx\n", guid);
533 entries[num_lines] = strdup(str);
534 num_lines++;
535 zpool_close(z_hndl);
536 }
537 num_tmp_lines = 0;
538 /*
539 * Store the new bootfs line.
540 */
541 (void) snprintf(str, BUFSIZ, "data_set=%s\n",
542 be_root_ds);
543 entries[num_lines] = strdup(str);
544 num_lines++;
545 collect_lines = B_TRUE;
546 }
547 } else if (found_orig_be && collect_lines) {
548 /*
549 * get the rest of the lines for the original BE and
550 * store them.
551 */
552 if (strstr(line, BE_GRUB_COMMENT) != NULL ||
553 strstr(line, "BOOTADM") != NULL)
554 continue;
555 if (strcmp(tok, "splashimage") == 0) {
556 entries[num_lines] =
557 strdup("splashimage "
558 "/boot/splashimage.xpm\n");
559 } else if ((strcmp(tok, "kernel_path") == 0) ||
560 (strcmp(tok, "module") == 0))
561 {
562 char * path;
563 char * st_path;
564
565 st_path = strtok(NULL, "\n");
566 path = (char *) malloc(512);
567 strcpy(path, tok);
568 strcat(path, "=");
569 strcat(path, st_path);
570 strcat(path, "\n");
571 entries[num_lines] = path;
572 } else {
573 entries[num_lines] = strdup(temp_line);
574 }
575 num_lines++;
576 } else if (found_title && !found_orig_be) {
577 tmp_entries[num_tmp_lines] = strdup(temp_line);
578 num_tmp_lines++;
579 }
580 }
581
582 (void) fclose(menu_fp);
583
584 if (found_be) {
585 /*
586 * If an entry for this BE was already in the menu, then if
587 * that entry's title matches what we would have put in
588 * return success. Otherwise return failure.
589 */
590 char *new_title = description ? description : be_name;
591
592 if (strcmp(title, new_title) == 0) {
593 ret = BE_SUCCESS;
594 goto cleanup;
595 } else {
596 if (be_remove_menu(be_name, be_root_pool,
597 boot_pool) != BE_SUCCESS) {
598 be_print_err(gettext("be_append_menu: "
599 "Failed to remove existing unusable "
600 "entry '%s' in boot menu.\n"), be_name);
601 ret = BE_ERR_BE_EXISTS;
602 goto cleanup;
603 }
604 }
605 }
606
607 /* Append BE entry to the end of the file */
608 menu_fp = fopen(menu_file, "a+");
609 err = errno;
610 if (menu_fp == NULL) {
611 be_print_err(gettext("be_append_menu: failed "
612 "to open menu.lst file %s\n"), menu_file);
613 ret = errno_to_be_err(err);
614 goto cleanup;
615 }
616
617 if (found_orig_be) {
618 /*
619 * write out all the stored lines
620 */
621 for (i = 0; i < num_lines; i++) {
622 (void) fprintf(menu_fp, "%s", entries[i]);
623 free(entries[i]);
624 }
625 num_lines = 0;
626
627 /*
628 * Check to see if this system supports grub
629 */
630 if (be_has_grub())
631 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
632 ret = BE_SUCCESS;
633 } else {
634 zprop_get_cbdata_t cb = { 0 };
635 zprop_source_t src;
636 char * bc = strchr(be_root_ds, '/');
637 char sguid[] = "guid";
638
639 (void) fprintf(menu_fp, "entry_name=%s\n",
640 description ? description : be_name);
641 *bc = '\0';
642 cb.cb_first = B_TRUE;
643 cb.cb_sources = ZPROP_SRC_ALL;
644 cb.cb_type = ZFS_TYPE_POOL;
645 zprop_get_list(g_zfs, sguid, &cb.cb_proplist, ZFS_TYPE_POOL);
646 zpool_handle_t * z_hndl = zpool_open(g_zfs, be_root_ds);
647 *bc = '/';
648 if (z_hndl) {
649 uint64_t guid = zpool_get_prop_int(z_hndl, cb.cb_proplist->pl_prop, &src);
650 (void) fprintf(menu_fp, "pool_uuid=%llx\n", guid);
651 zpool_close(z_hndl);
652 }
653 (void) fprintf(menu_fp, "data_set=%s\n", be_root_ds);
654
655 /*
656 * Check to see if this system supports grub
657 */
658 if (be_has_grub()) {
659 (void) fprintf(menu_fp, "kernel_path="
660 "/platform/i86pc/kernel/$ISADIR/unix\n");
661 (void) fprintf(menu_fp, "kernel_options="
662 "-B $ZFS_BOOTFS,console=graphic\n");
663 (void) fprintf(menu_fp, "module="
664 "/platform/i86pc/$ISADIR/boot_archive\n");
665 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
666 }
667 ret = BE_SUCCESS;
668 }
669 (void) fclose(menu_fp);
670 cleanup:
671 if (pool_mounted) {
672 int err = BE_SUCCESS;
673 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
674 if (ret == BE_SUCCESS)
675 ret = err;
676 free(orig_mntpnt);
677 free(ptmp_mntpnt);
678 }
679 ZFS_CLOSE(zhp);
680 if (num_tmp_lines > 0) {
681 for (i = 0; i < num_tmp_lines; i++) {
682 free(tmp_entries[i]);
683 tmp_entries[i] = NULL;
684 }
685 }
686 if (num_lines > 0) {
687 for (i = 0; i < num_lines; i++) {
688 free(entries[i]);
689 entries[i] = NULL;
690 }
691 }
692 return (ret);
693 }
694
695 /*
696 * Function: be_remove_menu
697 * Description: Removes a BE's entry from a menu.lst file.
698 * Parameters:
699 * be_name - the name of BE whose entry is to be removed from
700 * the menu.lst file.
701 * be_root_pool - the pool that be_name lives in.
702 * boot_pool - the pool where the BE is, if different than
703 * the pool containing the boot menu. If this is
704 * NULL it will be set to be_root_pool.
705 * Returns:
706 * BE_SUCCESS - Success
707 * be_errno_t - Failure
708 * Scope:
709 * Semi-private (library wide use only)
710 */
711 int
712 be_remove_menu(char *be_name, char *be_root_pool, char *boot_pool)
713 {
714 zfs_handle_t *zhp = NULL;
715 char be_root_ds[MAXPATHLEN];
716 char **buffer = NULL;
717 char menu_buf[BUFSIZ];
718 char menu[MAXPATHLEN];
719 char *pool_mntpnt = NULL;
720 char *ptmp_mntpnt = NULL;
721 char *orig_mntpnt = NULL;
722 char *tmp_menu = NULL;
723 FILE *menu_fp = NULL;
724 FILE *tmp_menu_fp = NULL;
725 struct stat sb;
726 int ret = BE_SUCCESS;
727 int i;
728 int fd;
729 int err = 0;
730 int nlines = 0;
731 int default_entry = 0;
732 int entry_cnt = 0;
733 int entry_del = 0;
734 int num_entry_del = 0;
735 int tmp_menu_len = 0;
736 boolean_t write = B_TRUE;
737 boolean_t do_buffer = B_FALSE;
738 boolean_t pool_mounted = B_FALSE;
739
740 if (boot_pool == NULL)
741 boot_pool = be_root_pool;
742
743 /* Get name of BE's root dataset */
744 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
745
746 /* Get handle to pool dataset */
747 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
748 be_print_err(gettext("be_remove_menu: "
749 "failed to open pool dataset for %s: %s"),
750 be_root_pool, libzfs_error_description(g_zfs));
751 return (zfs_err_to_be_err(g_zfs));
752 }
753
754 /*
755 * Check to see if the pool's dataset is mounted. If it isn't we'll
756 * attempt to mount it.
757 */
758 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
759 &pool_mounted)) != BE_SUCCESS) {
760 be_print_err(gettext("be_remove_menu: pool dataset "
761 "(%s) could not be mounted\n"), be_root_pool);
762 ZFS_CLOSE(zhp);
763 return (ret);
764 }
765
766 /*
767 * Get the mountpoint for the root pool dataset.
768 */
769 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
770 be_print_err(gettext("be_remove_menu: pool "
771 "dataset (%s) is not mounted. Can't set "
772 "the default BE in the grub menu.\n"), be_root_pool);
773 ret = BE_ERR_NO_MENU;
774 goto cleanup;
775 }
776
777 /* Get path to boot menu */
778 (void) strlcpy(menu, pool_mntpnt, sizeof (menu));
779
780 /*
781 * Check to see if this system supports grub
782 */
783 if (be_has_grub())
784 (void) strlcat(menu, BE_GRUB_MENU, sizeof (menu));
785 else
786 (void) strlcat(menu, BE_SPARC_MENU, sizeof (menu));
787
788 /* Get handle to boot menu file */
789 if ((ret = be_open_menu(be_root_pool, menu, &menu_fp, "r",
790 B_TRUE)) != BE_SUCCESS) {
791 goto cleanup;
792 } else if (menu_fp == NULL) {
793 ret = BE_ERR_NO_MENU;
794 goto cleanup;
795 }
796
797 free(pool_mntpnt);
798 pool_mntpnt = NULL;
799
800 /* Grab the stats of the original menu file */
801 if (stat(menu, &sb) != 0) {
802 err = errno;
803 be_print_err(gettext("be_remove_menu: "
804 "failed to stat file %s: %s\n"), menu, strerror(err));
805 ret = errno_to_be_err(err);
806 goto cleanup;
807 }
808
809 /* Create a tmp file for the modified menu.lst */
810 tmp_menu_len = strlen(menu) + 7;
811 if ((tmp_menu = (char *)malloc(tmp_menu_len)) == NULL) {
812 be_print_err(gettext("be_remove_menu: malloc failed\n"));
813 ret = BE_ERR_NOMEM;
814 goto cleanup;
815 }
816 (void) memset(tmp_menu, 0, tmp_menu_len);
817 (void) strlcpy(tmp_menu, menu, tmp_menu_len);
818 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
819 if ((fd = mkstemp(tmp_menu)) == -1) {
820 err = errno;
821 be_print_err(gettext("be_remove_menu: mkstemp failed\n"));
822 ret = errno_to_be_err(err);
823 free(tmp_menu);
824 tmp_menu = NULL;
825 goto cleanup;
826 }
827 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
828 err = errno;
829 be_print_err(gettext("be_remove_menu: "
830 "could not open tmp file for write: %s\n"), strerror(err));
831 (void) close(fd);
832 ret = errno_to_be_err(err);
833 goto cleanup;
834 }
835
836 while (fgets(menu_buf, BUFSIZ, menu_fp)) {
837 char tline [BUFSIZ];
838 char *tok = NULL;
839
840 (void) strlcpy(tline, menu_buf, sizeof (tline));
841
842 /* Tokenize line */
843 tok = strtok(tline, "=\n");
844
845 if (tok == NULL || tok[0] == '#') {
846 /* Found empty line or comment line */
847 if (do_buffer) {
848 /* Buffer this line */
849 if ((buffer = (char **)realloc(buffer,
850 sizeof (char *)*(nlines + 1))) == NULL) {
851 ret = BE_ERR_NOMEM;
852 goto cleanup;
853 }
854 if ((buffer[nlines++] = strdup(menu_buf))
855 == NULL) {
856 ret = BE_ERR_NOMEM;
857 goto cleanup;
858 }
859
860 } else if (write || strncmp(menu_buf, BE_GRUB_COMMENT,
861 strlen(BE_GRUB_COMMENT)) != 0) {
862 /* Write this line out */
863 (void) fputs(menu_buf, tmp_menu_fp);
864 }
865 } else if (strcmp(tok, "default_entry") == 0) {
866 /*
867 * Record what 'default' is set to because we might
868 * need to adjust this upon deleting an entry.
869 */
870 tok = strtok(NULL, BE_WHITE_SPACE);
871
872 if (tok != NULL) {
873 default_entry = atoi(tok);
874 }
875
876 (void) fputs(menu_buf, tmp_menu_fp);
877 } else if (strcmp(tok, "entry_name") == 0) {
878 /*
879 * If we've reached a 'title' line and do_buffer is
880 * is true, that means we've just buffered an entire
881 * entry without finding a 'bootfs' directive. We
882 * need to write that entry out and keep searching.
883 */
884 if (do_buffer) {
885 for (i = 0; i < nlines; i++) {
886 (void) fputs(buffer[i], tmp_menu_fp);
887 free(buffer[i]);
888 }
889 free(buffer);
890 buffer = NULL;
891 nlines = 0;
892 }
893
894 /*
895 * Turn writing off and buffering on, and increment
896 * our entry counter.
897 */
898 write = B_FALSE;
899 do_buffer = B_TRUE;
900 entry_cnt++;
901
902 /* Buffer this 'title' line */
903 if ((buffer = (char **)realloc(buffer,
904 sizeof (char *)*(nlines + 1))) == NULL) {
905 ret = BE_ERR_NOMEM;
906 goto cleanup;
907 }
908 if ((buffer[nlines++] = strdup(menu_buf)) == NULL) {
909 ret = BE_ERR_NOMEM;
910 goto cleanup;
911 }
912
913 } else if (strcmp(tok, "data_set") == 0) {
914 char *bootfs = NULL;
915
916 /*
917 * Found a 'bootfs' line. See if it matches the
918 * BE we're looking for.
919 */
920 if ((bootfs = strtok(NULL, BE_WHITE_SPACE)) == NULL ||
921 strcmp(bootfs, be_root_ds) != 0) {
922 /*
923 * Either there's nothing after the 'bootfs'
924 * or this is not the BE we're looking for,
925 * write out the line(s) we've buffered since
926 * finding the title.
927 */
928 for (i = 0; i < nlines; i++) {
929 (void) fputs(buffer[i], tmp_menu_fp);
930 free(buffer[i]);
931 }
932 free(buffer);
933 buffer = NULL;
934 nlines = 0;
935
936 /*
937 * Turn writing back on, and turn off buffering
938 * since this isn't the entry we're looking
939 * for.
940 */
941 write = B_TRUE;
942 do_buffer = B_FALSE;
943
944 /* Write this 'bootfs' line out. */
945 (void) fputs(menu_buf, tmp_menu_fp);
946 } else {
947 /*
948 * Found the entry we're looking for.
949 * Record its entry number, increment the
950 * number of entries we've deleted, and turn
951 * writing off. Also, throw away the lines
952 * we've buffered for this entry so far, we
953 * don't need them.
954 */
955 entry_del = entry_cnt - 1;
956 num_entry_del++;
957 write = B_FALSE;
958 do_buffer = B_FALSE;
959
960 for (i = 0; i < nlines; i++) {
961 free(buffer[i]);
962 }
963 free(buffer);
964 buffer = NULL;
965 nlines = 0;
966 }
967 } else {
968 if (do_buffer) {
969 /* Buffer this line */
970 if ((buffer = (char **)realloc(buffer,
971 sizeof (char *)*(nlines + 1))) == NULL) {
972 ret = BE_ERR_NOMEM;
973 goto cleanup;
974 }
975 if ((buffer[nlines++] = strdup(menu_buf))
976 == NULL) {
977 ret = BE_ERR_NOMEM;
978 goto cleanup;
979 }
980 } else if (write) {
981 /* Write this line out */
982 (void) fputs(menu_buf, tmp_menu_fp);
983 }
984 }
985 }
986
987 (void) fclose(menu_fp);
988 menu_fp = NULL;
989 (void) fclose(tmp_menu_fp);
990 tmp_menu_fp = NULL;
991
992 /* Copy the modified menu.lst into place */
993 if (rename(tmp_menu, menu) != 0) {
994 err = errno;
995 be_print_err(gettext("be_remove_menu: "
996 "failed to rename file %s to %s: %s\n"),
997 tmp_menu, menu, strerror(err));
998 ret = errno_to_be_err(err);
999 goto cleanup;
1000 }
1001 free(tmp_menu);
1002 tmp_menu = NULL;
1003
1004 /*
1005 * If we've removed an entry, see if we need to
1006 * adjust the default value in the menu.lst. If the
1007 * entry we've deleted comes before the default entry
1008 * we need to adjust the default value accordingly.
1009 *
1010 * be_has_grub is used here to check to see if this system
1011 * supports grub.
1012 */
1013 if (be_has_grub() && num_entry_del > 0) {
1014 if (entry_del <= default_entry) {
1015 default_entry = default_entry - num_entry_del;
1016 if (default_entry < 0)
1017 default_entry = 0;
1018
1019 /*
1020 * Adjust the default value by rewriting the
1021 * menu.lst file. This may be overkill, but to
1022 * preserve the location of the 'default' entry
1023 * in the file, we need to do this.
1024 */
1025
1026 /* Get handle to boot menu file */
1027 if ((menu_fp = fopen(menu, "r")) == NULL) {
1028 err = errno;
1029 be_print_err(gettext("be_remove_menu: "
1030 "failed to open menu.lst (%s): %s\n"),
1031 menu, strerror(err));
1032 ret = errno_to_be_err(err);
1033 goto cleanup;
1034 }
1035
1036 /* Create a tmp file for the modified menu.lst */
1037 tmp_menu_len = strlen(menu) + 7;
1038 if ((tmp_menu = (char *)malloc(tmp_menu_len))
1039 == NULL) {
1040 be_print_err(gettext("be_remove_menu: "
1041 "malloc failed\n"));
1042 ret = BE_ERR_NOMEM;
1043 goto cleanup;
1044 }
1045 (void) memset(tmp_menu, 0, tmp_menu_len);
1046 (void) strlcpy(tmp_menu, menu, tmp_menu_len);
1047 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
1048 if ((fd = mkstemp(tmp_menu)) == -1) {
1049 err = errno;
1050 be_print_err(gettext("be_remove_menu: "
1051 "mkstemp failed: %s\n"), strerror(err));
1052 ret = errno_to_be_err(err);
1053 free(tmp_menu);
1054 tmp_menu = NULL;
1055 goto cleanup;
1056 }
1057 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
1058 err = errno;
1059 be_print_err(gettext("be_remove_menu: "
1060 "could not open tmp file for write: %s\n"),
1061 strerror(err));
1062 (void) close(fd);
1063 ret = errno_to_be_err(err);
1064 goto cleanup;
1065 }
1066
1067 while (fgets(menu_buf, BUFSIZ, menu_fp)) {
1068 char tline [BUFSIZ];
1069 char *tok = NULL;
1070
1071 (void) strlcpy(tline, menu_buf, sizeof (tline));
1072
1073 /* Tokenize line */
1074 tok = strtok(tline, "=\n");
1075
1076 if (tok == NULL) {
1077 /* Found empty line, write it out */
1078 (void) fputs(menu_buf, tmp_menu_fp);
1079 } else if (strcmp(tok, "default_entry") == 0) {
1080 /* Found the default line, adjust it */
1081 (void) snprintf(tline, sizeof (tline),
1082 "default_entry=%d\n", default_entry);
1083
1084 (void) fputs(tline, tmp_menu_fp);
1085 } else {
1086 /* Pass through all other lines */
1087 (void) fputs(menu_buf, tmp_menu_fp);
1088 }
1089 }
1090
1091 (void) fclose(menu_fp);
1092 menu_fp = NULL;
1093 (void) fclose(tmp_menu_fp);
1094 tmp_menu_fp = NULL;
1095
1096 /* Copy the modified menu.lst into place */
1097 if (rename(tmp_menu, menu) != 0) {
1098 err = errno;
1099 be_print_err(gettext("be_remove_menu: "
1100 "failed to rename file %s to %s: %s\n"),
1101 tmp_menu, menu, strerror(err));
1102 ret = errno_to_be_err(err);
1103 goto cleanup;
1104 }
1105
1106 free(tmp_menu);
1107 tmp_menu = NULL;
1108 }
1109 }
1110
1111 /* Set the perms and ownership of the updated file */
1112 if (chmod(menu, sb.st_mode) != 0) {
1113 err = errno;
1114 be_print_err(gettext("be_remove_menu: "
1115 "failed to chmod %s: %s\n"), menu, strerror(err));
1116 ret = errno_to_be_err(err);
1117 goto cleanup;
1118 }
1119 if (chown(menu, sb.st_uid, sb.st_gid) != 0) {
1120 err = errno;
1121 be_print_err(gettext("be_remove_menu: "
1122 "failed to chown %s: %s\n"), menu, strerror(err));
1123 ret = errno_to_be_err(err);
1124 goto cleanup;
1125 }
1126
1127 cleanup:
1128 if (pool_mounted) {
1129 int err = BE_SUCCESS;
1130 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1131 if (ret == BE_SUCCESS)
1132 ret = err;
1133 free(orig_mntpnt);
1134 free(ptmp_mntpnt);
1135 }
1136 ZFS_CLOSE(zhp);
1137
1138 free(buffer);
1139 if (menu_fp != NULL)
1140 (void) fclose(menu_fp);
1141 if (tmp_menu_fp != NULL)
1142 (void) fclose(tmp_menu_fp);
1143 if (tmp_menu != NULL) {
1144 (void) unlink(tmp_menu);
1145 free(tmp_menu);
1146 }
1147
1148 return (ret);
1149 }
1150
1151 /*
1152 * Function: be_default_grub_bootfs
1153 * Description: This function returns the dataset in the default entry of
1154 * the grub menu. If no default entry is found with a valid bootfs
1155 * entry NULL is returned.
1156 * Parameters:
1157 * be_root_pool - This is the name of the root pool where the
1158 * grub menu can be found.
1159 * def_bootfs - This is used to pass back the bootfs string. On
1160 * error NULL is returned here.
1161 * Returns:
1162 * Success - BE_SUCCESS is returned.
1163 * Failure - a be_errno_t is returned.
1164 * Scope:
1165 * Semi-private (library wide use only)
1166 */
1167 int
1168 be_default_grub_bootfs(const char *be_root_pool, char **def_bootfs)
1169 {
1170 zfs_handle_t *zhp = NULL;
1171 char grub_file[MAXPATHLEN];
1172 FILE *menu_fp;
1173 char line[BUFSIZ];
1174 char *pool_mntpnt = NULL;
1175 char *ptmp_mntpnt = NULL;
1176 char *orig_mntpnt = NULL;
1177 int default_entry = 0, entries = 0;
1178 int found_default = 0;
1179 int ret = BE_SUCCESS;
1180 boolean_t pool_mounted = B_FALSE;
1181
1182 errno = 0;
1183
1184 /*
1185 * Check to see if this system supports grub
1186 */
1187 if (!be_has_grub()) {
1188 be_print_err(gettext("be_default_grub_bootfs: operation "
1189 "not supported on this architecture\n"));
1190 return (BE_ERR_NOTSUP);
1191 }
1192
1193 *def_bootfs = NULL;
1194
1195 /* Get handle to pool dataset */
1196 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1197 be_print_err(gettext("be_default_grub_bootfs: "
1198 "failed to open pool dataset for %s: %s"),
1199 be_root_pool, libzfs_error_description(g_zfs));
1200 return (zfs_err_to_be_err(g_zfs));
1201 }
1202
1203 /*
1204 * Check to see if the pool's dataset is mounted. If it isn't we'll
1205 * attempt to mount it.
1206 */
1207 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1208 &pool_mounted)) != BE_SUCCESS) {
1209 be_print_err(gettext("be_default_grub_bootfs: pool dataset "
1210 "(%s) could not be mounted\n"), be_root_pool);
1211 ZFS_CLOSE(zhp);
1212 return (ret);
1213 }
1214
1215 /*
1216 * Get the mountpoint for the root pool dataset.
1217 */
1218 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1219 be_print_err(gettext("be_default_grub_bootfs: failed "
1220 "to get mount point for the root pool. Can't set "
1221 "the default BE in the grub menu.\n"));
1222 ret = BE_ERR_NO_MENU;
1223 goto cleanup;
1224 }
1225
1226 (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1227 pool_mntpnt, BE_GRUB_MENU);
1228
1229 if ((ret = be_open_menu((char *)be_root_pool, grub_file,
1230 &menu_fp, "r", B_FALSE)) != BE_SUCCESS) {
1231 goto cleanup;
1232 } else if (menu_fp == NULL) {
1233 ret = BE_ERR_NO_MENU;
1234 goto cleanup;
1235 }
1236
1237 free(pool_mntpnt);
1238 pool_mntpnt = NULL;
1239
1240 while (fgets(line, BUFSIZ, menu_fp)) {
1241 char *tok = strtok(line, "=\n");
1242
1243 if (tok != NULL && tok[0] != '#') {
1244 if (!found_default) {
1245 if (strcmp(tok, "default_entry") == 0) {
1246 tok = strtok(NULL, BE_WHITE_SPACE);
1247 if (tok != NULL) {
1248 default_entry = atoi(tok);
1249 rewind(menu_fp);
1250 found_default = 1;
1251 }
1252 }
1253 continue;
1254 }
1255 if (strcmp(tok, "entry_name") == 0) {
1256 entries++;
1257 } else if (default_entry == entries - 1) {
1258 if (strcmp(tok, "data_set") == 0) {
1259 tok = strtok(NULL, BE_WHITE_SPACE);
1260 (void) fclose(menu_fp);
1261
1262 if (tok == NULL) {
1263 ret = BE_SUCCESS;
1264 goto cleanup;
1265 }
1266
1267 if ((*def_bootfs = strdup(tok)) !=
1268 NULL) {
1269 ret = BE_SUCCESS;
1270 goto cleanup;
1271 }
1272 be_print_err(gettext(
1273 "be_default_grub_bootfs: "
1274 "memory allocation failed\n"));
1275 ret = BE_ERR_NOMEM;
1276 goto cleanup;
1277 }
1278 } else if (default_entry < entries - 1) {
1279 /*
1280 * no bootfs entry for the default entry.
1281 */
1282 break;
1283 }
1284 }
1285 }
1286 (void) fclose(menu_fp);
1287
1288 cleanup:
1289 if (pool_mounted) {
1290 int err = BE_SUCCESS;
1291 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1292 if (ret == BE_SUCCESS)
1293 ret = err;
1294 free(orig_mntpnt);
1295 free(ptmp_mntpnt);
1296 }
1297 ZFS_CLOSE(zhp);
1298 return (ret);
1299 }
1300
1301 /*
1302 * Function: be_change_grub_default
1303 * Description: This function takes two parameters. These are the name of
1304 * the BE we want to have as the default booted in the grub
1305 * menu and the root pool where the path to the grub menu exists.
1306 * The code takes this and finds the BE's entry in the grub menu
1307 * and changes the default entry to point to that entry in the
1308 * list.
1309 * Parameters:
1310 * be_name - This is the name of the BE wanted as the default
1311 * for the next boot.
1312 * be_root_pool - This is the name of the root pool where the
1313 * grub menu can be found.
1314 * Returns:
1315 * BE_SUCCESS - Success
1316 * be_errno_t - Failure
1317 * Scope:
1318 * Semi-private (library wide use only)
1319 */
1320 int
1321 be_change_grub_default(char *be_name, char *be_root_pool)
1322 {
1323 zfs_handle_t *zhp = NULL;
1324 char grub_file[MAXPATHLEN];
1325 char *temp_grub;
1326 char *pool_mntpnt = NULL;
1327 char *ptmp_mntpnt = NULL;
1328 char *orig_mntpnt = NULL;
1329 char line[BUFSIZ];
1330 char temp_line[BUFSIZ];
1331 char be_root_ds[MAXPATHLEN];
1332 FILE *grub_fp = NULL;
1333 FILE *temp_fp = NULL;
1334 struct stat sb;
1335 int temp_grub_len = 0;
1336 int fd, entries = 0;
1337 int err = 0;
1338 int ret = BE_SUCCESS;
1339 boolean_t found_default = B_FALSE;
1340 boolean_t pool_mounted = B_FALSE;
1341
1342 errno = 0;
1343
1344 /*
1345 * Check to see if this system supports grub
1346 */
1347 if (!be_has_grub()) {
1348 be_print_err(gettext("be_change_grub_default: operation "
1349 "not supported on this architecture\n"));
1350 return (BE_ERR_NOTSUP);
1351 }
1352
1353 /* Generate string for BE's root dataset */
1354 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
1355
1356 /* Get handle to pool dataset */
1357 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1358 be_print_err(gettext("be_change_grub_default: "
1359 "failed to open pool dataset for %s: %s"),
1360 be_root_pool, libzfs_error_description(g_zfs));
1361 return (zfs_err_to_be_err(g_zfs));
1362 }
1363
1364 /*
1365 * Check to see if the pool's dataset is mounted. If it isn't we'll
1366 * attempt to mount it.
1367 */
1368 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1369 &pool_mounted)) != BE_SUCCESS) {
1370 be_print_err(gettext("be_change_grub_default: pool dataset "
1371 "(%s) could not be mounted\n"), be_root_pool);
1372 ZFS_CLOSE(zhp);
1373 return (ret);
1374 }
1375
1376 /*
1377 * Get the mountpoint for the root pool dataset.
1378 */
1379 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1380 be_print_err(gettext("be_change_grub_default: pool "
1381 "dataset (%s) is not mounted. Can't set "
1382 "the default BE in the grub menu.\n"), be_root_pool);
1383 ret = BE_ERR_NO_MENU;
1384 goto cleanup;
1385 }
1386
1387 (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1388 pool_mntpnt, BE_GRUB_MENU);
1389
1390 if ((ret = be_open_menu(be_root_pool, grub_file,
1391 &grub_fp, "r+", B_TRUE)) != BE_SUCCESS) {
1392 goto cleanup;
1393 } else if (grub_fp == NULL) {
1394 ret = BE_ERR_NO_MENU;
1395 goto cleanup;
1396 }
1397
1398 free(pool_mntpnt);
1399 pool_mntpnt = NULL;
1400
1401 /* Grab the stats of the original menu file */
1402 if (stat(grub_file, &sb) != 0) {
1403 err = errno;
1404 be_print_err(gettext("be_change_grub_default: "
1405 "failed to stat file %s: %s\n"), grub_file, strerror(err));
1406 ret = errno_to_be_err(err);
1407 goto cleanup;
1408 }
1409
1410 /* Create a tmp file for the modified menu.lst */
1411 temp_grub_len = strlen(grub_file) + 7;
1412 if ((temp_grub = (char *)malloc(temp_grub_len)) == NULL) {
1413 be_print_err(gettext("be_change_grub_default: "
1414 "malloc failed\n"));
1415 ret = BE_ERR_NOMEM;
1416 goto cleanup;
1417 }
1418 (void) memset(temp_grub, 0, temp_grub_len);
1419 (void) strlcpy(temp_grub, grub_file, temp_grub_len);
1420 (void) strlcat(temp_grub, "XXXXXX", temp_grub_len);
1421 if ((fd = mkstemp(temp_grub)) == -1) {
1422 err = errno;
1423 be_print_err(gettext("be_change_grub_default: "
1424 "mkstemp failed: %s\n"), strerror(err));
1425 ret = errno_to_be_err(err);
1426 free(temp_grub);
1427 temp_grub = NULL;
1428 goto cleanup;
1429 }
1430 if ((temp_fp = fdopen(fd, "w")) == NULL) {
1431 err = errno;
1432 be_print_err(gettext("be_change_grub_default: "
1433 "failed to open %s file: %s\n"),
1434 temp_grub, strerror(err));
1435 (void) close(fd);
1436 ret = errno_to_be_err(err);
1437 goto cleanup;
1438 }
1439
1440 while (fgets(line, BUFSIZ, grub_fp)) {
1441 char *tok = strtok(line, "=\n");
1442
1443 if (tok == NULL || tok[0] == '#') {
1444 continue;
1445 } else if (strcmp(tok, "entry_name") == 0) {
1446 entries++;
1447 continue;
1448 } else if (strcmp(tok, "data_set") == 0) {
1449 char *bootfs = strtok(NULL, BE_WHITE_SPACE);
1450 if (bootfs == NULL)
1451 continue;
1452
1453 if (strcmp(bootfs, be_root_ds) == 0) {
1454 found_default = B_TRUE;
1455 break;
1456 }
1457 }
1458 }
1459
1460 if (!found_default) {
1461 be_print_err(gettext("be_change_grub_default: failed "
1462 "to find entry for %s in the grub menu\n"),
1463 be_name);
1464 ret = BE_ERR_BE_NOENT;
1465 goto cleanup;
1466 }
1467
1468 rewind(grub_fp);
1469
1470 (void) snprintf(temp_line, BUFSIZ, "default_entry=%d\n",
1471 entries - 1 >= 0 ? entries - 1 : 0);
1472 (void) fputs(temp_line, temp_fp);
1473 while (fgets(line, BUFSIZ, grub_fp)) {
1474 char *tok = NULL;
1475
1476 (void) strncpy(temp_line, line, BUFSIZ);
1477
1478 if ((tok = strtok(temp_line, "=\n")) != NULL &&
1479 strcmp(tok, "default_entry") == 0) {
1480 } else {
1481 (void) fputs(line, temp_fp);
1482 }
1483 }
1484
1485 (void) fclose(grub_fp);
1486 grub_fp = NULL;
1487 (void) fclose(temp_fp);
1488 temp_fp = NULL;
1489
1490 if (rename(temp_grub, grub_file) != 0) {
1491 err = errno;
1492 be_print_err(gettext("be_change_grub_default: "
1493 "failed to rename file %s to %s: %s\n"),
1494 temp_grub, grub_file, strerror(err));
1495 ret = errno_to_be_err(err);
1496 goto cleanup;
1497 }
1498 free(temp_grub);
1499 temp_grub = NULL;
1500
1501 /* Set the perms and ownership of the updated file */
1502 if (chmod(grub_file, sb.st_mode) != 0) {
1503 err = errno;
1504 be_print_err(gettext("be_change_grub_default: "
1505 "failed to chmod %s: %s\n"), grub_file, strerror(err));
1506 ret = errno_to_be_err(err);
1507 goto cleanup;
1508 }
1509 if (chown(grub_file, sb.st_uid, sb.st_gid) != 0) {
1510 err = errno;
1511 be_print_err(gettext("be_change_grub_default: "
1512 "failed to chown %s: %s\n"), grub_file, strerror(err));
1513 ret = errno_to_be_err(err);
1514 }
1515
1516 cleanup:
1517 if (pool_mounted) {
1518 int err = BE_SUCCESS;
1519 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1520 if (ret == BE_SUCCESS)
1521 ret = err;
1522 free(orig_mntpnt);
1523 free(ptmp_mntpnt);
1524 }
1525 ZFS_CLOSE(zhp);
1526 if (grub_fp != NULL)
1527 (void) fclose(grub_fp);
1528 if (temp_fp != NULL)
1529 (void) fclose(temp_fp);
1530 if (temp_grub != NULL) {
1531 (void) unlink(temp_grub);
1532 free(temp_grub);
1533 }
1534
1535 return (ret);
1536 }
1537
1538 /*
1539 * Function: be_update_menu
1540 * Description: This function is used by be_rename to change the BE name in
1541 * an existing entry in the grub menu to the new name of the BE.
1542 * Parameters:
1543 * be_orig_name - the original name of the BE
1544 * be_new_name - the new name the BE is being renameed to.
1545 * be_root_pool - The pool which contains the grub menu
1546 * boot_pool - the pool where the BE is, if different than
1547 * the pool containing the boot menu. If this is
1548 * NULL it will be set to be_root_pool.
1549 * Returns:
1550 * BE_SUCCESS - Success
1551 * be_errno_t - Failure
1552 * Scope:
1553 * Semi-private (library wide use only)
1554 */
1555 int
1556 be_update_menu(char *be_orig_name, char *be_new_name, char *be_root_pool,
1557 char *boot_pool)
1558 {
1559 zfs_handle_t *zhp = NULL;
1560 char menu_file[MAXPATHLEN];
1561 char be_root_ds[MAXPATHLEN];
1562 char be_new_root_ds[MAXPATHLEN];
1563 char line[BUFSIZ];
1564 char *pool_mntpnt = NULL;
1565 char *ptmp_mntpnt = NULL;
1566 char *orig_mntpnt = NULL;
1567 char *temp_menu = NULL;
1568 FILE *menu_fp = NULL;
1569 FILE *new_fp = NULL;
1570 struct stat sb;
1571 int temp_menu_len = 0;
1572 int tmp_fd;
1573 int ret = BE_SUCCESS;
1574 int flag = 0;
1575 int err = 0;
1576 boolean_t pool_mounted = B_FALSE;
1577
1578 errno = 0;
1579
1580 if (boot_pool == NULL)
1581 boot_pool = be_root_pool;
1582
1583 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1584 be_print_err(gettext("be_update_menu: failed to open "
1585 "pool dataset for %s: %s\n"), be_root_pool,
1586 libzfs_error_description(g_zfs));
1587 return (zfs_err_to_be_err(g_zfs));
1588 }
1589
1590 /*
1591 * Check to see if the pool's dataset is mounted. If it isn't we'll
1592 * attempt to mount it.
1593 */
1594 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1595 &pool_mounted)) != BE_SUCCESS) {
1596 be_print_err(gettext("be_update_menu: pool dataset "
1597 "(%s) could not be mounted\n"), be_root_pool);
1598 ZFS_CLOSE(zhp);
1599 return (ret);
1600 }
1601
1602 /*
1603 * Get the mountpoint for the root pool dataset.
1604 */
1605 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1606 be_print_err(gettext("be_update_menu: failed "
1607 "to get mount point for the root pool. Can't set "
1608 "the default BE in the grub menu.\n"));
1609 ret = BE_ERR_NO_MENU;
1610 goto cleanup;
1611 }
1612
1613 /*
1614 * Check to see if this system supports grub
1615 */
1616 if (be_has_grub()) {
1617 (void) snprintf(menu_file, sizeof (menu_file),
1618 "%s%s", pool_mntpnt, BE_GRUB_MENU);
1619 } else {
1620 (void) snprintf(menu_file, sizeof (menu_file),
1621 "%s%s", pool_mntpnt, BE_SPARC_MENU);
1622 }
1623
1624 be_make_root_ds(be_root_pool, be_orig_name, be_root_ds,
1625 sizeof (be_root_ds));
1626 be_make_root_ds(be_root_pool, be_new_name, be_new_root_ds,
1627 sizeof (be_new_root_ds));
1628
1629 if ((ret = be_open_menu(be_root_pool, menu_file,
1630 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
1631 goto cleanup;
1632 } else if (menu_fp == NULL) {
1633 ret = BE_ERR_NO_MENU;
1634 goto cleanup;
1635 }
1636
1637 free(pool_mntpnt);
1638 pool_mntpnt = NULL;
1639
1640 /* Grab the stat of the original menu file */
1641 if (stat(menu_file, &sb) != 0) {
1642 err = errno;
1643 be_print_err(gettext("be_update_menu: "
1644 "failed to stat file %s: %s\n"), menu_file, strerror(err));
1645 (void) fclose(menu_fp);
1646 ret = errno_to_be_err(err);
1647 goto cleanup;
1648 }
1649
1650 /* Create tmp file for modified menu.lst */
1651 temp_menu_len = strlen(menu_file) + 7;
1652 if ((temp_menu = (char *)malloc(temp_menu_len))
1653 == NULL) {
1654 be_print_err(gettext("be_update_menu: "
1655 "malloc failed\n"));
1656 (void) fclose(menu_fp);
1657 ret = BE_ERR_NOMEM;
1658 goto cleanup;
1659 }
1660 (void) memset(temp_menu, 0, temp_menu_len);
1661 (void) strlcpy(temp_menu, menu_file, temp_menu_len);
1662 (void) strlcat(temp_menu, "XXXXXX", temp_menu_len);
1663 if ((tmp_fd = mkstemp(temp_menu)) == -1) {
1664 err = errno;
1665 be_print_err(gettext("be_update_menu: "
1666 "mkstemp failed: %s\n"), strerror(err));
1667 (void) fclose(menu_fp);
1668 free(temp_menu);
1669 ret = errno_to_be_err(err);
1670 goto cleanup;
1671 }
1672 if ((new_fp = fdopen(tmp_fd, "w")) == NULL) {
1673 err = errno;
1674 be_print_err(gettext("be_update_menu: "
1675 "fdopen failed: %s\n"), strerror(err));
1676 (void) close(tmp_fd);
1677 (void) fclose(menu_fp);
1678 free(temp_menu);
1679 ret = errno_to_be_err(err);
1680 goto cleanup;
1681 }
1682
1683 while (fgets(line, BUFSIZ, menu_fp)) {
1684 char tline[BUFSIZ];
1685 char new_line[BUFSIZ];
1686 char *c = NULL;
1687
1688 (void) strlcpy(tline, line, sizeof (tline));
1689
1690 /* Tokenize line */
1691 c = strtok(tline, "=\n");
1692
1693 if (c == NULL) {
1694 /* Found empty line, write it out. */
1695 (void) fputs(line, new_fp);
1696 } else if (c[0] == '#') {
1697 /* Found a comment line, write it out. */
1698 (void) fputs(line, new_fp);
1699 } else if (strcmp(c, "entry_name") == 0) {
1700 char *name = NULL;
1701 char *desc = NULL;
1702
1703 /*
1704 * Found a 'title' line, parse out BE name or
1705 * the description.
1706 */
1707 flag = 0;
1708 name = strtok(NULL, BE_WHITE_SPACE);
1709
1710 if (name == NULL) {
1711 /*
1712 * Nothing after 'title', just push
1713 * this line through
1714 */
1715 (void) fputs(line, new_fp);
1716 } else {
1717 /*
1718 * Grab the remainder of the title which
1719 * could be a multi worded description
1720 */
1721 desc = strtok(NULL, "\n");
1722
1723 if (strcmp(name, be_orig_name) == 0) {
1724 /*
1725 * The first token of the title is
1726 * the old BE name, replace it with
1727 * the new one, and write it out
1728 * along with the remainder of
1729 * description if there is one.
1730 */
1731 ++flag;
1732 if (desc) {
1733 (void) snprintf(new_line,
1734 sizeof (new_line),
1735 "entry_name=%s %s\n",
1736 be_new_name, desc);
1737 } else {
1738 (void) snprintf(new_line,
1739 sizeof (new_line),
1740 "entry_name=%s\n", be_new_name);
1741 }
1742
1743 (void) fputs(new_line, new_fp);
1744 } else {
1745 (void) fputs(line, new_fp);
1746 }
1747 }
1748 } else if (strcmp(c, "data_set") == 0) {
1749 /*
1750 * Found a 'bootfs' line, parse out the BE root
1751 * dataset value.
1752 */
1753 char *root_ds = strtok(NULL, BE_WHITE_SPACE);
1754
1755 if (root_ds == NULL) {
1756 /*
1757 * Nothing after 'bootfs', just push
1758 * this line through
1759 */
1760 (void) fputs(line, new_fp);
1761 } else {
1762 /*
1763 * If this bootfs is the one we're renaming,
1764 * write out the new root dataset value
1765 */
1766 if (strcmp(root_ds, be_root_ds) == 0) {
1767 ++flag;
1768 (void) snprintf(new_line,
1769 sizeof (new_line), "data_set=%s\n",
1770 be_new_root_ds);
1771
1772 (void) fputs(new_line, new_fp);
1773 } else {
1774 (void) fputs(line, new_fp);
1775 }
1776 }
1777 } else {
1778 /*
1779 * Found some other line we don't care
1780 * about, write it out.
1781 */
1782 (void) fputs(line, new_fp);
1783 }
1784 }
1785
1786 (void) fclose(menu_fp);
1787 (void) fclose(new_fp);
1788 (void) close(tmp_fd);
1789
1790 if (rename(temp_menu, menu_file) != 0) {
1791 err = errno;
1792 be_print_err(gettext("be_update_menu: "
1793 "failed to rename file %s to %s: %s\n"),
1794 temp_menu, menu_file, strerror(err));
1795 ret = errno_to_be_err(err);
1796 }
1797 free(temp_menu);
1798
1799 /* Set the perms and ownership of the updated file */
1800 if (chmod(menu_file, sb.st_mode) != 0) {
1801 err = errno;
1802 be_print_err(gettext("be_update_menu: "
1803 "failed to chmod %s: %s\n"), menu_file, strerror(err));
1804 ret = errno_to_be_err(err);
1805 goto cleanup;
1806 }
1807 if (chown(menu_file, sb.st_uid, sb.st_gid) != 0) {
1808 err = errno;
1809 be_print_err(gettext("be_update_menu: "
1810 "failed to chown %s: %s\n"), menu_file, strerror(err));
1811 ret = errno_to_be_err(err);
1812 }
1813
1814 cleanup:
1815 if (pool_mounted) {
1816 int err = BE_SUCCESS;
1817 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1818 if (ret == BE_SUCCESS)
1819 ret = err;
1820 free(orig_mntpnt);
1821 free(ptmp_mntpnt);
1822 }
1823 ZFS_CLOSE(zhp);
1824 return (ret);
1825 }
1826
1827 /*
1828 * Function: be_has_menu_entry
1829 * Description: Checks to see if the BEs root dataset has an entry in the grub
1830 * menu.
1831 * Parameters:
1832 * be_dataset - The root dataset of the BE
1833 * be_root_pool - The pool which contains the boot menu
1834 * entry - A pointer the the entry number of the BE if found.
1835 * Returns:
1836 * B_TRUE - Success
1837 * B_FALSE - Failure
1838 * Scope:
1839 * Semi-private (library wide use only)
1840 */
1841 boolean_t
1842 be_has_menu_entry(char *be_dataset, char *be_root_pool, int *entry)
1843 {
1844 zfs_handle_t *zhp = NULL;
1845 char menu_file[MAXPATHLEN];
1846 FILE *menu_fp;
1847 char line[BUFSIZ];
1848 char *last;
1849 char *rpool_mntpnt = NULL;
1850 char *ptmp_mntpnt = NULL;
1851 char *orig_mntpnt = NULL;
1852 int ent_num = 0;
1853 boolean_t ret = 0;
1854 boolean_t pool_mounted = B_FALSE;
1855
1856
1857 /*
1858 * Check to see if this system supports grub
1859 */
1860 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1861 be_print_err(gettext("be_has_menu_entry: failed to open "
1862 "pool dataset for %s: %s\n"), be_root_pool,
1863 libzfs_error_description(g_zfs));
1864 return (B_FALSE);
1865 }
1866
1867 /*
1868 * Check to see if the pool's dataset is mounted. If it isn't we'll
1869 * attempt to mount it.
1870 */
1871 if (be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1872 &pool_mounted) != 0) {
1873 be_print_err(gettext("be_has_menu_entry: pool dataset "
1874 "(%s) could not be mounted\n"), be_root_pool);
1875 ZFS_CLOSE(zhp);
1876 return (B_FALSE);
1877 }
1878
1879 /*
1880 * Get the mountpoint for the root pool dataset.
1881 */
1882 if (!zfs_is_mounted(zhp, &rpool_mntpnt)) {
1883 be_print_err(gettext("be_has_menu_entry: pool "
1884 "dataset (%s) is not mounted. Can't set "
1885 "the default BE in the grub menu.\n"), be_root_pool);
1886 ret = B_FALSE;
1887 goto cleanup;
1888 }
1889
1890 if (be_has_grub()) {
1891 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
1892 rpool_mntpnt, BE_GRUB_MENU);
1893 } else {
1894 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
1895 rpool_mntpnt, BE_SPARC_MENU);
1896 }
1897
1898 if (be_open_menu(be_root_pool, menu_file, &menu_fp, "r",
1899 B_FALSE) != 0) {
1900 ret = B_FALSE;
1901 goto cleanup;
1902 } else if (menu_fp == NULL) {
1903 ret = B_FALSE;
1904 goto cleanup;
1905 }
1906
1907 free(rpool_mntpnt);
1908 rpool_mntpnt = NULL;
1909
1910 while (fgets(line, BUFSIZ, menu_fp)) {
1911 char *tok = strtok_r(line, "=\n", &last);
1912
1913 if (tok != NULL && tok[0] != '#') {
1914 if (strcmp(tok, "data_set") == 0) {
1915 tok = strtok_r(last, BE_WHITE_SPACE, &last);
1916 if (tok != NULL && strcmp(tok,
1917 be_dataset) == 0) {
1918 (void) fclose(menu_fp);
1919 /*
1920 * The entry number needs to be
1921 * decremented here because the title
1922 * will always be the first line for
1923 * an entry. Because of this we'll
1924 * always be off by one entry when we
1925 * check for bootfs.
1926 */
1927 *entry = ent_num - 1;
1928 ret = B_TRUE;
1929 goto cleanup;
1930 }
1931 } else if (strcmp(tok, "entry_name") == 0)
1932 ent_num++;
1933 }
1934 }
1935
1936 cleanup:
1937 if (pool_mounted) {
1938 (void) be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1939 free(orig_mntpnt);
1940 free(ptmp_mntpnt);
1941 }
1942 ZFS_CLOSE(zhp);
1943 (void) fclose(menu_fp);
1944 return (ret);
1945 }
1946
1947 /*
1948 * Function: be_update_vfstab
1949 * Description: This function digs into a BE's vfstab and updates all
1950 * entries with file systems listed in be_fs_list_data_t.
1951 * The entry's root container dataset and be_name will be
1952 * updated with the parameters passed in.
1953 * Parameters:
1954 * be_name - name of BE to update
1955 * old_rc_loc - dataset under which the root container dataset
1956 * of the old BE resides in.
1957 * new_rc_loc - dataset under which the root container dataset
1958 * of the new BE resides in.
1959 * fld - be_fs_list_data_t pointer providing the list of
1960 * file systems to look for in vfstab.
1961 * mountpoint - directory of where BE is currently mounted.
1962 * If NULL, then BE is not currently mounted.
1963 * Returns:
1964 * BE_SUCCESS - Success
1965 * be_errno_t - Failure
1966 * Scope:
1967 * Semi-private (library wide use only)
1968 */
1969 int
1970 be_update_vfstab(char *be_name, char *old_rc_loc, char *new_rc_loc,
1971 be_fs_list_data_t *fld, char *mountpoint)
1972 {
1973 char *tmp_mountpoint = NULL;
1974 char alt_vfstab[MAXPATHLEN];
1975 int ret = BE_SUCCESS, err = BE_SUCCESS;
1976
1977 if (fld == NULL || fld->fs_list == NULL || fld->fs_num == 0)
1978 return (BE_SUCCESS);
1979
1980 /* If BE not already mounted, mount the BE */
1981 if (mountpoint == NULL) {
1982 if ((ret = _be_mount(be_name, &tmp_mountpoint,
1983 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
1984 be_print_err(gettext("be_update_vfstab: "
1985 "failed to mount BE (%s)\n"), be_name);
1986 return (ret);
1987 }
1988 } else {
1989 tmp_mountpoint = mountpoint;
1990 }
1991
1992 /* Get string for vfstab in the mounted BE. */
1993 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
1994 tmp_mountpoint);
1995
1996 /* Update the vfstab */
1997 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
1998 fld);
1999
2000 /* Unmount BE if we mounted it */
2001 if (mountpoint == NULL) {
2002 if ((err = _be_unmount(be_name, 0)) == BE_SUCCESS) {
2003 /* Remove temporary mountpoint */
2004 (void) rmdir(tmp_mountpoint);
2005 } else {
2006 be_print_err(gettext("be_update_vfstab: "
2007 "failed to unmount BE %s mounted at %s\n"),
2008 be_name, tmp_mountpoint);
2009 if (ret == BE_SUCCESS)
2010 ret = err;
2011 }
2012
2013 free(tmp_mountpoint);
2014 }
2015
2016 return (ret);
2017 }
2018
2019 /*
2020 * Function: be_update_zone_vfstab
2021 * Description: This function digs into a zone BE's vfstab and updates all
2022 * entries with file systems listed in be_fs_list_data_t.
2023 * The entry's root container dataset and be_name will be
2024 * updated with the parameters passed in.
2025 * Parameters:
2026 * zhp - zfs_handle_t pointer to zone root dataset.
2027 * be_name - name of zone BE to update
2028 * old_rc_loc - dataset under which the root container dataset
2029 * of the old zone BE resides in.
2030 * new_rc_loc - dataset under which the root container dataset
2031 * of the new zone BE resides in.
2032 * fld - be_fs_list_data_t pointer providing the list of
2033 * file systems to look for in vfstab.
2034 * Returns:
2035 * BE_SUCCESS - Success
2036 * be_errno_t - Failure
2037 * Scope:
2038 * Semi-private (library wide use only)
2039 */
2040 int
2041 be_update_zone_vfstab(zfs_handle_t *zhp, char *be_name, char *old_rc_loc,
2042 char *new_rc_loc, be_fs_list_data_t *fld)
2043 {
2044 be_mount_data_t md = { 0 };
2045 be_unmount_data_t ud = { 0 };
2046 char alt_vfstab[MAXPATHLEN];
2047 boolean_t mounted_here = B_FALSE;
2048 int ret = BE_SUCCESS;
2049
2050 /*
2051 * If zone root not already mounted, mount it at a
2052 * temporary location.
2053 */
2054 if (!zfs_is_mounted(zhp, &md.altroot)) {
2055 /* Generate temporary mountpoint to mount zone root */
2056 if ((ret = be_make_tmp_mountpoint(&md.altroot)) != BE_SUCCESS) {
2057 be_print_err(gettext("be_update_zone_vfstab: "
2058 "failed to make temporary mountpoint to "
2059 "mount zone root\n"));
2060 return (ret);
2061 }
2062
2063 if (be_mount_zone_root(zhp, &md) != BE_SUCCESS) {
2064 be_print_err(gettext("be_update_zone_vfstab: "
2065 "failed to mount zone root %s\n"),
2066 zfs_get_name(zhp));
2067 free(md.altroot);
2068 return (BE_ERR_MOUNT_ZONEROOT);
2069 }
2070 mounted_here = B_TRUE;
2071 }
2072
2073 /* Get string from vfstab in the mounted zone BE */
2074 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
2075 md.altroot);
2076
2077 /* Update the vfstab */
2078 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
2079 fld);
2080
2081 /* Unmount zone root if we mounted it */
2082 if (mounted_here) {
2083 ud.force = B_TRUE;
2084
2085 if (be_unmount_zone_root(zhp, &ud) == BE_SUCCESS) {
2086 /* Remove the temporary mountpoint */
2087 (void) rmdir(md.altroot);
2088 } else {
2089 be_print_err(gettext("be_update_zone_vfstab: "
2090 "failed to unmount zone root %s from %s\n"),
2091 zfs_get_name(zhp), md.altroot);
2092 if (ret == 0)
2093 ret = BE_ERR_UMOUNT_ZONEROOT;
2094 }
2095 }
2096
2097 free(md.altroot);
2098 return (ret);
2099 }
2100
2101 /*
2102 * Function: be_auto_snap_name
2103 * Description: Generate an auto snapshot name constructed based on the
2104 * current date and time. The auto snapshot name is of the form:
2105 *
2106 * <date>-<time>
2107 *
2108 * where <date> is in ISO standard format, so the resultant name
2109 * is of the form:
2110 *
2111 * %Y-%m-%d-%H:%M:%S
2112 *
2113 * Parameters:
2114 * None
2115 * Returns:
2116 * Success - pointer to auto generated snapshot name. The name
2117 * is allocated in heap storage so the caller is
2118 * responsible for free'ing the name.
2119 * Failure - NULL
2120 * Scope:
2121 * Semi-private (library wide use only)
2122 */
2123 char *
2124 be_auto_snap_name(void)
2125 {
2126 time_t utc_tm = NULL;
2127 struct tm *gmt_tm = NULL;
2128 char gmt_time_str[64];
2129 char *auto_snap_name = NULL;
2130
2131 if (time(&utc_tm) == -1) {
2132 be_print_err(gettext("be_auto_snap_name: time() failed\n"));
2133 return (NULL);
2134 }
2135
2136 if ((gmt_tm = gmtime(&utc_tm)) == NULL) {
2137 be_print_err(gettext("be_auto_snap_name: gmtime() failed\n"));
2138 return (NULL);
2139 }
2140
2141 (void) strftime(gmt_time_str, sizeof (gmt_time_str), "%F-%T", gmt_tm);
2142
2143 if ((auto_snap_name = strdup(gmt_time_str)) == NULL) {
2144 be_print_err(gettext("be_auto_snap_name: "
2145 "memory allocation failed\n"));
2146 return (NULL);
2147 }
2148
2149 return (auto_snap_name);
2150 }
2151
2152 /*
2153 * Function: be_auto_be_name
2154 * Description: Generate an auto BE name constructed based on the BE name
2155 * of the original BE being cloned.
2156 * Parameters:
2157 * obe_name - name of the original BE being cloned.
2158 * Returns:
2159 * Success - pointer to auto generated BE name. The name
2160 * is allocated in heap storage so the caller is
2161 * responsible for free'ing the name.
2162 * Failure - NULL
2163 * Scope:
2164 * Semi-private (library wide use only)
2165 */
2166 char *
2167 be_auto_be_name(char *obe_name)
2168 {
2169 return (be_get_auto_name(obe_name, NULL, B_FALSE));
2170 }
2171
2172 /*
2173 * Function: be_auto_zone_be_name
2174 * Description: Generate an auto BE name for a zone constructed based on
2175 * the BE name of the original zone BE being cloned.
2176 * Parameters:
2177 * container_ds - container dataset for the zone.
2178 * zbe_name - name of the original zone BE being cloned.
2179 * Returns:
2180 * Success - pointer to auto generated BE name. The name
2181 * is allocated in heap storage so the caller is
2182 * responsible for free'ing the name.
2183 * Failure - NULL
2184 * Scope:
2185 * Semi-private (library wide use only)
2186 */
2187 char *
2188 be_auto_zone_be_name(char *container_ds, char *zbe_name)
2189 {
2190 return (be_get_auto_name(zbe_name, container_ds, B_TRUE));
2191 }
2192
2193 /*
2194 * Function: be_valid_be_name
2195 * Description: Validates a BE name.
2196 * Parameters:
2197 * be_name - name of BE to validate
2198 * Returns:
2199 * B_TRUE - be_name is valid
2200 * B_FALSE - be_name is invalid
2201 * Scope:
2202 * Semi-private (library wide use only)
2203 */
2204
2205 boolean_t
2206 be_valid_be_name(const char *be_name)
2207 {
2208 const char *c = NULL;
2209 struct be_defaults be_defaults;
2210
2211 if (be_name == NULL)
2212 return (B_FALSE);
2213
2214 be_get_defaults(&be_defaults);
2215
2216 /*
2217 * A BE name must not be a multi-level dataset name. We also check
2218 * that it does not contain the ' ' and '%' characters. The ' ' is
2219 * a valid character for datasets, however we don't allow that in a
2220 * BE name. The '%' is invalid, but zfs_name_valid() allows it for
2221 * internal reasons, so we explicitly check for it here.
2222 */
2223 c = be_name;
2224 while (*c != '\0' && *c != '/' && *c != ' ' && *c != '%')
2225 c++;
2226
2227 if (*c != '\0')
2228 return (B_FALSE);
2229
2230 /*
2231 * The BE name must comply with a zfs dataset filesystem. We also
2232 * verify its length to be < BE_NAME_MAX_LEN.
2233 */
2234 if (!zfs_name_valid(be_name, ZFS_TYPE_FILESYSTEM) ||
2235 strlen(be_name) > BE_NAME_MAX_LEN)
2236 return (B_FALSE);
2237
2238 if (be_defaults.be_deflt_bename_starts_with[0] != '\0' &&
2239 strstr(be_name, be_defaults.be_deflt_bename_starts_with) == NULL) {
2240 return (B_FALSE);
2241 }
2242
2243 return (B_TRUE);
2244 }
2245
2246 /*
2247 * Function: be_valid_auto_snap_name
2248 * Description: This function checks that a snapshot name is a valid auto
2249 * generated snapshot name. A valid auto generated snapshot
2250 * name is of the form:
2251 *
2252 * %Y-%m-%d-%H:%M:%S
2253 *
2254 * An older form of the auto generated snapshot name also
2255 * included the snapshot's BE cleanup policy and a reserved
2256 * field. Those names will also be verified by this function.
2257 *
2258 * Examples of valid auto snapshot names are:
2259 *
2260 * 2008-03-31-18:41:30
2261 * 2008-03-31-22:17:24
2262 * <policy>:-:2008:04-05-09:12:55
2263 * <policy>:-:2008:04-06-15:34:12
2264 *
2265 * Parameters:
2266 * name - name of the snapshot to be validated.
2267 * Returns:
2268 * B_TRUE - the name is a valid auto snapshot name.
2269 * B_FALSE - the name is not a valid auto snapshot name.
2270 * Scope:
2271 * Semi-private (library wide use only)
2272 */
2273 boolean_t
2274 be_valid_auto_snap_name(char *name)
2275 {
2276 struct tm gmt_tm;
2277
2278 char *policy = NULL;
2279 char *reserved = NULL;
2280 char *date = NULL;
2281 char *c = NULL;
2282
2283 /* Validate the snapshot name by converting it into utc time */
2284 if (strptime(name, "%Y-%m-%d-%T", &gmt_tm) != NULL &&
2285 (mktime(&gmt_tm) != -1)) {
2286 return (B_TRUE);
2287 }
2288
2289 /*
2290 * Validate the snapshot name against the older form of an
2291 * auto generated snapshot name.
2292 */
2293 policy = strdup(name);
2294
2295 /*
2296 * Get the first field from the snapshot name,
2297 * which is the BE policy
2298 */
2299 c = strchr(policy, ':');
2300 if (c == NULL) {
2301 free(policy);
2302 return (B_FALSE);
2303 }
2304 c[0] = '\0';
2305
2306 /* Validate the policy name */
2307 if (!valid_be_policy(policy)) {
2308 free(policy);
2309 return (B_FALSE);
2310 }
2311
2312 /* Get the next field, which is the reserved field. */
2313 if (c[1] == NULL || c[1] == '\0') {
2314 free(policy);
2315 return (B_FALSE);
2316 }
2317 reserved = c+1;
2318 c = strchr(reserved, ':');
2319 if (c == NULL) {
2320 free(policy);
2321 return (B_FALSE);
2322 }
2323 c[0] = '\0';
2324
2325 /* Validate the reserved field */
2326 if (strcmp(reserved, "-") != 0) {
2327 free(policy);
2328 return (B_FALSE);
2329 }
2330
2331 /* The remaining string should be the date field */
2332 if (c[1] == NULL || c[1] == '\0') {
2333 free(policy);
2334 return (B_FALSE);
2335 }
2336 date = c+1;
2337
2338 /* Validate the date string by converting it into utc time */
2339 if (strptime(date, "%Y-%m-%d-%T", &gmt_tm) == NULL ||
2340 (mktime(&gmt_tm) == -1)) {
2341 be_print_err(gettext("be_valid_auto_snap_name: "
2342 "invalid auto snapshot name\n"));
2343 free(policy);
2344 return (B_FALSE);
2345 }
2346
2347 free(policy);
2348 return (B_TRUE);
2349 }
2350
2351 /*
2352 * Function: be_default_policy
2353 * Description: Temporary hardcoded policy support. This function returns
2354 * the default policy type to be used to create a BE or a BE
2355 * snapshot.
2356 * Parameters:
2357 * None
2358 * Returns:
2359 * Name of default BE policy.
2360 * Scope:
2361 * Semi-private (library wide use only)
2362 */
2363 char *
2364 be_default_policy(void)
2365 {
2366 return (BE_PLCY_STATIC);
2367 }
2368
2369 /*
2370 * Function: valid_be_policy
2371 * Description: Temporary hardcoded policy support. This function valids
2372 * whether a policy is a valid known policy or not.
2373 * Paramters:
2374 * policy - name of policy to validate.
2375 * Returns:
2376 * B_TRUE - policy is a valid.
2377 * B_FALSE - policy is invalid.
2378 * Scope:
2379 * Semi-private (library wide use only)
2380 */
2381 boolean_t
2382 valid_be_policy(char *policy)
2383 {
2384 if (policy == NULL)
2385 return (B_FALSE);
2386
2387 if (strcmp(policy, BE_PLCY_STATIC) == 0 ||
2388 strcmp(policy, BE_PLCY_VOLATILE) == 0) {
2389 return (B_TRUE);
2390 }
2391
2392 return (B_FALSE);
2393 }
2394
2395 /*
2396 * Function: be_print_err
2397 * Description: This function prints out error messages if do_print is
2398 * set to B_TRUE or if the BE_PRINT_ERR environment variable
2399 * is set to true.
2400 * Paramters:
2401 * prnt_str - the string we wish to print and any arguments
2402 * for the format of that string.
2403 * Returns:
2404 * void
2405 * Scope:
2406 * Semi-private (library wide use only)
2407 */
2408 void
2409 be_print_err(char *prnt_str, ...)
2410 {
2411 va_list ap;
2412 char buf[BUFSIZ];
2413 char *env_buf;
2414 static boolean_t env_checked = B_FALSE;
2415
2416 if (!env_checked) {
2417 if ((env_buf = getenv("BE_PRINT_ERR")) != NULL) {
2418 if (strcasecmp(env_buf, "true") == 0) {
2419 do_print = B_TRUE;
2420 }
2421 }
2422 env_checked = B_TRUE;
2423 }
2424
2425 if (do_print) {
2426 va_start(ap, prnt_str);
2427 /* LINTED variable format specifier */
2428 (void) vsnprintf(buf, BUFSIZ, prnt_str, ap);
2429 (void) fputs(buf, stderr);
2430 va_end(ap);
2431 }
2432 }
2433
2434 /*
2435 * Function: be_find_current_be
2436 * Description: Find the currently "active" BE. Fill in the
2437 * passed in be_transaction_data_t reference with the
2438 * active BE's data.
2439 * Paramters:
2440 * none
2441 * Returns:
2442 * BE_SUCCESS - Success
2443 * be_errnot_t - Failure
2444 * Scope:
2445 * Semi-private (library wide use only)
2446 * Notes:
2447 * The caller is responsible for initializing the libzfs handle
2448 * and freeing the memory used by the active be_name.
2449 */
2450 int
2451 be_find_current_be(be_transaction_data_t *bt)
2452 {
2453 int zret;
2454
2455 if ((zret = zpool_iter(g_zfs, be_zpool_find_current_be_callback,
2456 bt)) == 0) {
2457 be_print_err(gettext("be_find_current_be: failed to "
2458 "find current BE name\n"));
2459 return (BE_ERR_BE_NOENT);
2460 } else if (zret < 0) {
2461 be_print_err(gettext("be_find_current_be: "
2462 "zpool_iter failed: %s\n"),
2463 libzfs_error_description(g_zfs));
2464 return (zfs_err_to_be_err(g_zfs));
2465 }
2466
2467 return (BE_SUCCESS);
2468 }
2469
2470 /*
2471 * Function: be_zpool_find_current_be_callback
2472 * Description: Callback function used to iterate through all existing pools
2473 * to find the BE that is the currently booted BE.
2474 * Parameters:
2475 * zlp - zpool_handle_t pointer to the current pool being
2476 * looked at.
2477 * data - be_transaction_data_t pointer.
2478 * Upon successfully finding the current BE, the
2479 * obe_zpool member of this parameter is set to the
2480 * pool it is found in.
2481 * Return:
2482 * 1 - Found current BE in this pool.
2483 * 0 - Did not find current BE in this pool.
2484 * Scope:
2485 * Semi-private (library wide use only)
2486 */
2487 int
2488 be_zpool_find_current_be_callback(zpool_handle_t *zlp, void *data)
2489 {
2490 be_transaction_data_t *bt = data;
2491 zfs_handle_t *zhp = NULL;
2492 const char *zpool = zpool_get_name(zlp);
2493 char be_container_ds[MAXPATHLEN];
2494
2495 /*
2496 * Generate string for BE container dataset
2497 */
2498 be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds));
2499
2500 /*
2501 * Check if a BE container dataset exists in this pool.
2502 */
2503 if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) {
2504 zpool_close(zlp);
2505 return (0);
2506 }
2507
2508 /*
2509 * Get handle to this zpool's BE container dataset.
2510 */
2511 if ((zhp = zfs_open(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) ==
2512 NULL) {
2513 be_print_err(gettext("be_zpool_find_current_be_callback: "
2514 "failed to open BE container dataset (%s)\n"),
2515 be_container_ds);
2516 zpool_close(zlp);
2517 return (0);
2518 }
2519
2520 /*
2521 * Iterate through all potential BEs in this zpool
2522 */
2523 if (zfs_iter_filesystems(zhp, be_zfs_find_current_be_callback, bt)) {
2524 /*
2525 * Found current BE dataset; set obe_zpool
2526 */
2527 if ((bt->obe_zpool = strdup(zpool)) == NULL) {
2528 be_print_err(gettext(
2529 "be_zpool_find_current_be_callback: "
2530 "memory allocation failed\n"));
2531 ZFS_CLOSE(zhp);
2532 zpool_close(zlp);
2533 return (0);
2534 }
2535
2536 ZFS_CLOSE(zhp);
2537 zpool_close(zlp);
2538 return (1);
2539 }
2540
2541 ZFS_CLOSE(zhp);
2542 zpool_close(zlp);
2543
2544 return (0);
2545 }
2546
2547 /*
2548 * Function: be_zfs_find_current_be_callback
2549 * Description: Callback function used to iterate through all BEs in a
2550 * pool to find the BE that is the currently booted BE.
2551 * Parameters:
2552 * zhp - zfs_handle_t pointer to current filesystem being checked.
2553 * data - be_transaction-data_t pointer
2554 * Upon successfully finding the current BE, the
2555 * obe_name and obe_root_ds members of this parameter
2556 * are set to the BE name and BE's root dataset
2557 * respectively.
2558 * Return:
2559 * 1 - Found current BE.
2560 * 0 - Did not find current BE.
2561 * Scope:
2562 * Semi-private (library wide use only)
2563 */
2564 int
2565 be_zfs_find_current_be_callback(zfs_handle_t *zhp, void *data)
2566 {
2567 be_transaction_data_t *bt = data;
2568 char *mp = NULL;
2569
2570 /*
2571 * Check if dataset is mounted, and if so where.
2572 */
2573 if (zfs_is_mounted(zhp, &mp)) {
2574 /*
2575 * If mounted at root, set obe_root_ds and obe_name
2576 */
2577 if (mp != NULL && strcmp(mp, "/") == 0) {
2578 free(mp);
2579
2580 if ((bt->obe_root_ds = strdup(zfs_get_name(zhp)))
2581 == NULL) {
2582 be_print_err(gettext(
2583 "be_zfs_find_current_be_callback: "
2584 "memory allocation failed\n"));
2585 ZFS_CLOSE(zhp);
2586 return (0);
2587 }
2588
2589 if ((bt->obe_name = strdup(basename(bt->obe_root_ds)))
2590 == NULL) {
2591 be_print_err(gettext(
2592 "be_zfs_find_current_be_callback: "
2593 "memory allocation failed\n"));
2594 ZFS_CLOSE(zhp);
2595 return (0);
2596 }
2597
2598 ZFS_CLOSE(zhp);
2599 return (1);
2600 }
2601
2602 free(mp);
2603 }
2604 ZFS_CLOSE(zhp);
2605
2606 return (0);
2607 }
2608
2609 /*
2610 * Function: be_check_be_roots_callback
2611 * Description: This function checks whether or not the dataset name passed
2612 * is hierachically located under the BE root container dataset
2613 * for this pool.
2614 * Parameters:
2615 * zlp - zpool_handle_t pointer to current pool being processed.
2616 * data - name of dataset to check
2617 * Returns:
2618 * 0 - dataset is not in this pool's BE root container dataset
2619 * 1 - dataset is in this pool's BE root container dataset
2620 * Scope:
2621 * Semi-private (library wide use only)
2622 */
2623 int
2624 be_check_be_roots_callback(zpool_handle_t *zlp, void *data)
2625 {
2626 const char *zpool = zpool_get_name(zlp);
2627 char *ds = data;
2628 char be_container_ds[MAXPATHLEN];
2629
2630 /* Generate string for this pool's BE root container dataset */
2631 be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds));
2632
2633 /*
2634 * If dataset lives under the BE root container dataset
2635 * of this pool, return failure.
2636 */
2637 if (strncmp(be_container_ds, ds, strlen(be_container_ds)) == 0 &&
2638 ds[strlen(be_container_ds)] == '/') {
2639 zpool_close(zlp);
2640 return (1);
2641 }
2642
2643 zpool_close(zlp);
2644 return (0);
2645 }
2646
2647 /*
2648 * Function: zfs_err_to_be_err
2649 * Description: This function takes the error stored in the libzfs handle
2650 * and maps it to an be_errno_t. If there are no matching
2651 * be_errno_t's then BE_ERR_ZFS is returned.
2652 * Paramters:
2653 * zfsh - The libzfs handle containing the error we're looking up.
2654 * Returns:
2655 * be_errno_t
2656 * Scope:
2657 * Semi-private (library wide use only)
2658 */
2659 int
2660 zfs_err_to_be_err(libzfs_handle_t *zfsh)
2661 {
2662 int err = libzfs_errno(zfsh);
2663
2664 switch (err) {
2665 case 0:
2666 return (BE_SUCCESS);
2667 case EZFS_PERM:
2668 return (BE_ERR_PERM);
2669 case EZFS_INTR:
2670 return (BE_ERR_INTR);
2671 case EZFS_NOENT:
2672 return (BE_ERR_NOENT);
2673 case EZFS_NOSPC:
2674 return (BE_ERR_NOSPC);
2675 case EZFS_MOUNTFAILED:
2676 return (BE_ERR_MOUNT);
2677 case EZFS_UMOUNTFAILED:
2678 return (BE_ERR_UMOUNT);
2679 case EZFS_EXISTS:
2680 return (BE_ERR_BE_EXISTS);
2681 case EZFS_BUSY:
2682 return (BE_ERR_DEV_BUSY);
2683 case EZFS_POOLREADONLY:
2684 return (BE_ERR_ROFS);
2685 case EZFS_NAMETOOLONG:
2686 return (BE_ERR_NAMETOOLONG);
2687 case EZFS_NODEVICE:
2688 return (BE_ERR_NODEV);
2689 case EZFS_POOL_INVALARG:
2690 return (BE_ERR_INVAL);
2691 case EZFS_PROPTYPE:
2692 return (BE_ERR_INVALPROP);
2693 case EZFS_BADTYPE:
2694 return (BE_ERR_DSTYPE);
2695 case EZFS_PROPNONINHERIT:
2696 return (BE_ERR_NONINHERIT);
2697 case EZFS_PROPREADONLY:
2698 return (BE_ERR_READONLYPROP);
2699 case EZFS_RESILVERING:
2700 case EZFS_POOLUNAVAIL:
2701 return (BE_ERR_UNAVAIL);
2702 case EZFS_DSREADONLY:
2703 return (BE_ERR_READONLYDS);
2704 default:
2705 return (BE_ERR_ZFS);
2706 }
2707 }
2708
2709 /*
2710 * Function: errno_to_be_err
2711 * Description: This function takes an errno and maps it to an be_errno_t.
2712 * If there are no matching be_errno_t's then BE_ERR_UNKNOWN is
2713 * returned.
2714 * Paramters:
2715 * err - The errno we're compairing against.
2716 * Returns:
2717 * be_errno_t
2718 * Scope:
2719 * Semi-private (library wide use only)
2720 */
2721 int
2722 errno_to_be_err(int err)
2723 {
2724 switch (err) {
2725 case EPERM:
2726 return (BE_ERR_PERM);
2727 case EACCES:
2728 return (BE_ERR_ACCESS);
2729 case ECANCELED:
2730 return (BE_ERR_CANCELED);
2731 case EINTR:
2732 return (BE_ERR_INTR);
2733 case ENOENT:
2734 return (BE_ERR_NOENT);
2735 case ENOSPC:
2736 case EDQUOT:
2737 return (BE_ERR_NOSPC);
2738 case EEXIST:
2739 return (BE_ERR_BE_EXISTS);
2740 case EBUSY:
2741 return (BE_ERR_BUSY);
2742 case EROFS:
2743 return (BE_ERR_ROFS);
2744 case ENAMETOOLONG:
2745 return (BE_ERR_NAMETOOLONG);
2746 case ENXIO:
2747 return (BE_ERR_NXIO);
2748 case EINVAL:
2749 return (BE_ERR_INVAL);
2750 case EFAULT:
2751 return (BE_ERR_FAULT);
2752 default:
2753 return (BE_ERR_UNKNOWN);
2754 }
2755 }
2756
2757 /*
2758 * Function: be_err_to_str
2759 * Description: This function takes a be_errno_t and maps it to a message.
2760 * If there are no matching be_errno_t's then NULL is returned.
2761 * Paramters:
2762 * be_errno_t - The be_errno_t we're mapping.
2763 * Returns:
2764 * string or NULL if the error code is not known.
2765 * Scope:
2766 * Semi-private (library wide use only)
2767 */
2768 char *
2769 be_err_to_str(int err)
2770 {
2771 switch (err) {
2772 case BE_ERR_ACCESS:
2773 return (gettext("Permission denied."));
2774 case BE_ERR_ACTIVATE_CURR:
2775 return (gettext("Activation of current BE failed."));
2776 case BE_ERR_AUTONAME:
2777 return (gettext("Auto naming failed."));
2778 case BE_ERR_BE_NOENT:
2779 return (gettext("No such BE."));
2780 case BE_ERR_BUSY:
2781 return (gettext("Mount busy."));
2782 case BE_ERR_DEV_BUSY:
2783 return (gettext("Device busy."));
2784 case BE_ERR_CANCELED:
2785 return (gettext("Operation canceled."));
2786 case BE_ERR_CLONE:
2787 return (gettext("BE clone failed."));
2788 case BE_ERR_COPY:
2789 return (gettext("BE copy failed."));
2790 case BE_ERR_CREATDS:
2791 return (gettext("Dataset creation failed."));
2792 case BE_ERR_CURR_BE_NOT_FOUND:
2793 return (gettext("Can't find current BE."));
2794 case BE_ERR_DESTROY:
2795 return (gettext("Failed to destroy BE or snapshot."));
2796 case BE_ERR_DESTROY_CURR_BE:
2797 return (gettext("Cannot destroy current BE."));
2798 case BE_ERR_DEMOTE:
2799 return (gettext("BE demotion failed."));
2800 case BE_ERR_DSTYPE:
2801 return (gettext("Invalid dataset type."));
2802 case BE_ERR_BE_EXISTS:
2803 return (gettext("BE exists."));
2804 case BE_ERR_INIT:
2805 return (gettext("be_zfs_init failed."));
2806 case BE_ERR_INTR:
2807 return (gettext("Interupted system call."));
2808 case BE_ERR_INVAL:
2809 return (gettext("Invalid argument."));
2810 case BE_ERR_INVALPROP:
2811 return (gettext("Invalid property for dataset."));
2812 case BE_ERR_INVALMOUNTPOINT:
2813 return (gettext("Unexpected mountpoint."));
2814 case BE_ERR_MOUNT:
2815 return (gettext("Mount failed."));
2816 case BE_ERR_MOUNTED:
2817 return (gettext("Already mounted."));
2818 case BE_ERR_NAMETOOLONG:
2819 return (gettext("name > BUFSIZ."));
2820 case BE_ERR_NOENT:
2821 return (gettext("Doesn't exist."));
2822 case BE_ERR_POOL_NOENT:
2823 return (gettext("No such pool."));
2824 case BE_ERR_NODEV:
2825 return (gettext("No such device."));
2826 case BE_ERR_NOTMOUNTED:
2827 return (gettext("File system not mounted."));
2828 case BE_ERR_NOMEM:
2829 return (gettext("Not enough memory."));
2830 case BE_ERR_NONINHERIT:
2831 return (gettext(
2832 "Property is not inheritable for the BE dataset."));
2833 case BE_ERR_NXIO:
2834 return (gettext("No such device or address."));
2835 case BE_ERR_NOSPC:
2836 return (gettext("No space on device."));
2837 case BE_ERR_NOTSUP:
2838 return (gettext("Operation not supported."));
2839 case BE_ERR_OPEN:
2840 return (gettext("Open failed."));
2841 case BE_ERR_PERM:
2842 return (gettext("Not owner."));
2843 case BE_ERR_UNAVAIL:
2844 return (gettext("The BE is currently unavailable."));
2845 case BE_ERR_PROMOTE:
2846 return (gettext("BE promotion failed."));
2847 case BE_ERR_ROFS:
2848 return (gettext("Read only file system."));
2849 case BE_ERR_READONLYDS:
2850 return (gettext("Read only dataset."));
2851 case BE_ERR_READONLYPROP:
2852 return (gettext("Read only property."));
2853 case BE_ERR_RENAME_ACTIVE:
2854 return (gettext("Renaming the active BE is not supported."));
2855 case BE_ERR_SS_EXISTS:
2856 return (gettext("Snapshot exists."));
2857 case BE_ERR_SS_NOENT:
2858 return (gettext("No such snapshot."));
2859 case BE_ERR_UMOUNT:
2860 return (gettext("Unmount failed."));
2861 case BE_ERR_UMOUNT_CURR_BE:
2862 return (gettext("Can't unmount the current BE."));
2863 case BE_ERR_UMOUNT_SHARED:
2864 return (gettext("Unmount of a shared File System failed."));
2865 case BE_ERR_FAULT:
2866 return (gettext("Bad address."));
2867 case BE_ERR_UNKNOWN:
2868 return (gettext("Unknown error."));
2869 case BE_ERR_ZFS:
2870 return (gettext("ZFS returned an error."));
2871 case BE_ERR_GEN_UUID:
2872 return (gettext("Failed to generate uuid."));
2873 case BE_ERR_PARSE_UUID:
2874 return (gettext("Failed to parse uuid."));
2875 case BE_ERR_NO_UUID:
2876 return (gettext("No uuid"));
2877 case BE_ERR_ZONE_NO_PARENTBE:
2878 return (gettext("No parent uuid"));
2879 case BE_ERR_ZONE_MULTIPLE_ACTIVE:
2880 return (gettext("Multiple active zone roots"));
2881 case BE_ERR_ZONE_NO_ACTIVE_ROOT:
2882 return (gettext("No active zone root"));
2883 case BE_ERR_ZONE_ROOT_NOT_LEGACY:
2884 return (gettext("Zone root not legacy"));
2885 case BE_ERR_MOUNT_ZONEROOT:
2886 return (gettext("Failed to mount a zone root."));
2887 case BE_ERR_UMOUNT_ZONEROOT:
2888 return (gettext("Failed to unmount a zone root."));
2889 case BE_ERR_NO_MOUNTED_ZONE:
2890 return (gettext("Zone is not mounted"));
2891 case BE_ERR_ZONES_UNMOUNT:
2892 return (gettext("Unable to unmount a zone BE."));
2893 case BE_ERR_NO_MENU:
2894 return (gettext("Missing boot menu file."));
2895 case BE_ERR_BAD_MENU_PATH:
2896 return (gettext("Invalid path for menu.lst file"));
2897 case BE_ERR_ZONE_SS_EXISTS:
2898 return (gettext("Zone snapshot exists."));
2899 case BE_ERR_BOOTFILE_INST:
2900 return (gettext("Error installing boot files."));
2901 case BE_ERR_EXTCMD:
2902 return (gettext("Error running an external command."));
2903 default:
2904 return (NULL);
2905 }
2906 }
2907
2908 /*
2909 * Function: be_has_grub
2910 * Description: Boolean function indicating whether the current system
2911 * uses grub.
2912 * Return: B_FALSE - the system does not have grub
2913 * B_TRUE - the system does have grub.
2914 * Scope:
2915 * Semi-private (library wide use only)
2916 */
2917 boolean_t
2918 be_has_grub(void)
2919 {
2920 /*
2921 * TODO: This will need to be expanded to check for the existence of
2922 * grub if and when there is grub support for SPARC.
2923 */
2924 return (be_is_isa("i386"));
2925 }
2926
2927 /*
2928 * Function: be_is_isa
2929 * Description: Boolean function indicating whether the instruction set
2930 * architecture of the executing system matches the name provided.
2931 * The string must match a system defined architecture (e.g.
2932 * "i386", "sparc") and is case sensitive.
2933 * Parameters: name - string representing the name of instruction set
2934 * architecture being tested
2935 * Returns: B_FALSE - the system instruction set architecture is different
2936 * from the one specified
2937 * B_TRUE - the system instruction set architecture is the same
2938 * as the one specified
2939 * Scope:
2940 * Semi-private (library wide use only)
2941 */
2942 boolean_t
2943 be_is_isa(char *name)
2944 {
2945 return ((strcmp((char *)be_get_default_isa(), name) == 0));
2946 }
2947
2948 /*
2949 * Function: be_get_default_isa
2950 * Description:
2951 * Returns the default instruction set architecture of the
2952 * machine it is executed on. (eg. sparc, i386, ...)
2953 * NOTE: SYS_INST environment variable may override default
2954 * return value
2955 * Parameters:
2956 * none
2957 * Returns:
2958 * NULL - the architecture returned by sysinfo() was too
2959 * long for local variables
2960 * char * - pointer to a string containing the default
2961 * implementation
2962 * Scope:
2963 * Semi-private (library wide use only)
2964 */
2965 char *
2966 be_get_default_isa(void)
2967 {
2968 int i;
2969 char *envp;
2970 static char default_inst[ARCH_LENGTH] = "";
2971
2972 if (default_inst[0] == '\0') {
2973 if ((envp = getenv("SYS_INST")) != NULL) {
2974 if ((int)strlen(envp) >= ARCH_LENGTH)
2975 return (NULL);
2976 else
2977 (void) strcpy(default_inst, envp);
2978 } else {
2979 i = sysinfo(SI_ARCHITECTURE, default_inst, ARCH_LENGTH);
2980 if (i < 0 || i > ARCH_LENGTH)
2981 return (NULL);
2982 }
2983 }
2984 return (default_inst);
2985 }
2986
2987 /*
2988 * Function: be_run_cmd
2989 * Description:
2990 * Runs a command in a separate subprocess. Splits out stdout from stderr
2991 * and sends each to its own buffer. Buffers must be pre-allocated and
2992 * passed in as arguments. Buffer sizes are also passed in as arguments.
2993 *
2994 * Notes / caveats:
2995 * - Command being run is assumed to not have any stdout or stderr
2996 * redirection.
2997 * - Commands which emit total stderr output of greater than PIPE_BUF
2998 * bytes can hang. For such commands, a different implementation
2999 * which uses poll(2) must be used.
3000 * - stdout_buf can be NULL. In this case, stdout_bufsize is ignored, and
3001 * the stream which would have gone to it is sent to the bit
3002 * bucket.
3003 * - stderr_buf cannot be NULL.
3004 * - Only subprocess errors are appended to the stderr_buf. Errors
3005 * running the command are reported through be_print_err().
3006 * - Data which would overflow its respective buffer is sent to the bit
3007 * bucket.
3008 *
3009 * Parameters:
3010 * command: command to run. Assumed not to have embedded stdout
3011 * or stderr redirection. May have stdin redirection,
3012 * however.
3013 * stderr_buf: buffer returning subprocess stderr data. Errors
3014 * reported by this function are reported through
3015 * be_print_err().
3016 * stderr_bufsize: size of stderr_buf
3017 * stdout_buf: buffer returning subprocess stdout data.
3018 * stdout_bufsize: size of stdout_buf
3019 * Returns:
3020 * BE_SUCCESS - The command ran successfully without returning
3021 * errors.
3022 * BE_ERR_EXTCMD
3023 * - The command could not be run.
3024 * - The command terminated with error status.
3025 * - There were errors extracting or returning subprocess
3026 * data.
3027 * BE_ERR_NOMEM - The command exceeds the command buffer size.
3028 * BE_ERR_INVAL - An invalid argument was specified.
3029 * Scope:
3030 * Semi-private (library wide use only)
3031 */
3032 int
3033 be_run_cmd(char *command, char *stderr_buf, int stderr_bufsize,
3034 char *stdout_buf, int stdout_bufsize)
3035 {
3036 char *temp_filename = strdup(tmpnam(NULL));
3037 FILE *stdout_str = NULL;
3038 FILE *stderr_str = NULL;
3039 char cmdline[BUFSIZ];
3040 char oneline[BUFSIZ];
3041 int exit_status;
3042 int rval = BE_SUCCESS;
3043
3044 if ((command == NULL) || (stderr_buf == NULL) ||
3045 (stderr_bufsize <= 0) || (stdout_bufsize < 0) ||
3046 ((stdout_buf != NULL) ^ (stdout_bufsize != 0))) {
3047 return (BE_ERR_INVAL);
3048 }
3049
3050 /* Set up command so popen returns stderr, not stdout */
3051 if (snprintf(cmdline, BUFSIZ, "%s 2> %s", command,
3052 temp_filename) >= BUFSIZ) {
3053 rval = BE_ERR_NOMEM;
3054 goto cleanup;
3055 }
3056
3057 /* Set up the fifo that will make stderr available. */
3058 if (mkfifo(temp_filename, 0600) != 0) {
3059 (void) be_print_err(gettext("be_run_cmd: mkfifo: %s\n"),
3060 strerror(errno));
3061 rval = BE_ERR_EXTCMD;
3062 goto cleanup;
3063 }
3064
3065 if ((stdout_str = popen(cmdline, "r")) == NULL) {
3066 (void) be_print_err(gettext("be_run_cmd: popen: %s\n"),
3067 strerror(errno));
3068 rval = BE_ERR_EXTCMD;
3069 goto cleanup;
3070 }
3071
3072 if ((stderr_str = fopen(temp_filename, "r")) == NULL) {
3073 (void) be_print_err(gettext("be_run_cmd: fopen: %s\n"),
3074 strerror(errno));
3075 (void) pclose(stdout_str);
3076 rval = BE_ERR_EXTCMD;
3077 goto cleanup;
3078 }
3079
3080 /* Read stdout first, as it usually outputs more than stderr. */
3081 oneline[BUFSIZ-1] = '\0';
3082 while (fgets(oneline, BUFSIZ-1, stdout_str) != NULL) {
3083 if (stdout_str != NULL) {
3084 (void) strlcat(stdout_buf, oneline, stdout_bufsize);
3085 }
3086 }
3087
3088 while (fgets(oneline, BUFSIZ-1, stderr_str) != NULL) {
3089 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3090 }
3091
3092 /* Close pipe, get exit status. */
3093 if ((exit_status = pclose(stdout_str)) == -1) {
3094 (void) be_print_err(gettext("be_run_cmd: pclose: %s\n"),
3095 strerror(errno));
3096 rval = BE_ERR_EXTCMD;
3097 } else if (WIFEXITED(exit_status)) {
3098 exit_status = (int)((char)WEXITSTATUS(exit_status));
3099 if (exit_status != 0) {
3100 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: "
3101 "command terminated with error status: %d\n"),
3102 exit_status);
3103 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3104 rval = BE_ERR_EXTCMD;
3105 }
3106 } else {
3107 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: command "
3108 "terminated on signal: %s\n"),
3109 strsignal(WTERMSIG(exit_status)));
3110 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3111 rval = BE_ERR_EXTCMD;
3112 }
3113
3114 cleanup:
3115 (void) unlink(temp_filename);
3116 (void) free(temp_filename);
3117
3118 return (rval);
3119 }
3120
3121 /* ******************************************************************** */
3122 /* Private Functions */
3123 /* ******************************************************************** */
3124
3125 /*
3126 * Function: update_dataset
3127 * Description: This function takes a dataset name and replaces the zpool
3128 * and be_name components of the dataset with the new be_name
3129 * zpool passed in.
3130 * Parameters:
3131 * dataset - name of dataset
3132 * dataset_len - lenth of buffer in which dataset is passed in.
3133 * be_name - name of new BE name to update to.
3134 * old_rc_loc - dataset under which the root container dataset
3135 * for the old BE lives.
3136 * new_rc_loc - dataset under which the root container dataset
3137 * for the new BE lives.
3138 * Returns:
3139 * BE_SUCCESS - Success
3140 * be_errno_t - Failure
3141 * Scope:
3142 * Private
3143 */
3144 static int
3145 update_dataset(char *dataset, int dataset_len, char *be_name,
3146 char *old_rc_loc, char *new_rc_loc)
3147 {
3148 char *ds = NULL;
3149 char *sub_ds = NULL;
3150
3151 /* Tear off the BE container dataset */
3152 if ((ds = be_make_name_from_ds(dataset, old_rc_loc)) == NULL) {
3153 return (BE_ERR_INVAL);
3154 }
3155
3156 /* Get dataset name relative to BE root, if there is one */
3157 sub_ds = strchr(ds, '/');
3158
3159 /* Generate the BE root dataset name */
3160 be_make_root_ds(new_rc_loc, be_name, dataset, dataset_len);
3161
3162 /* If a subordinate dataset name was found, append it */
3163 if (sub_ds != NULL)
3164 (void) strlcat(dataset, sub_ds, dataset_len);
3165
3166 free(ds);
3167 return (BE_SUCCESS);
3168 }
3169
3170 /*
3171 * Function: _update_vfstab
3172 * Description: This function updates a vfstab file to reflect the new
3173 * root container dataset location and be_name for all
3174 * entries listed in the be_fs_list_data_t structure passed in.
3175 * Parameters:
3176 * vfstab - vfstab file to modify
3177 * be_name - name of BE to update.
3178 * old_rc_loc - dataset under which the root container dataset
3179 * of the old BE resides in.
3180 * new_rc_loc - dataset under which the root container dataset
3181 * of the new BE resides in.
3182 * fld - be_fs_list_data_t pointer providing the list of
3183 * file systems to look for in vfstab.
3184 * Returns:
3185 * BE_SUCCESS - Success
3186 * be_errno_t - Failure
3187 * Scope:
3188 * Private
3189 */
3190 static int
3191 _update_vfstab(char *vfstab, char *be_name, char *old_rc_loc,
3192 char *new_rc_loc, be_fs_list_data_t *fld)
3193 {
3194 struct vfstab vp;
3195 char *tmp_vfstab = NULL;
3196 char comments_buf[BUFSIZ];
3197 FILE *comments = NULL;
3198 FILE *vfs_ents = NULL;
3199 FILE *tfile = NULL;
3200 struct stat sb;
3201 char dev[MAXPATHLEN];
3202 char *c;
3203 int fd;
3204 int ret = BE_SUCCESS, err = 0;
3205 int i;
3206 int tmp_vfstab_len = 0;
3207
3208 errno = 0;
3209
3210 /*
3211 * Open vfstab for reading twice. First is for comments,
3212 * second is for actual entries.
3213 */
3214 if ((comments = fopen(vfstab, "r")) == NULL ||
3215 (vfs_ents = fopen(vfstab, "r")) == NULL) {
3216 err = errno;
3217 be_print_err(gettext("_update_vfstab: "
3218 "failed to open vfstab (%s): %s\n"), vfstab,
3219 strerror(err));
3220 ret = errno_to_be_err(err);
3221 goto cleanup;
3222 }
3223
3224 /* Grab the stats of the original vfstab file */
3225 if (stat(vfstab, &sb) != 0) {
3226 err = errno;
3227 be_print_err(gettext("_update_vfstab: "
3228 "failed to stat file %s: %s\n"), vfstab,
3229 strerror(err));
3230 ret = errno_to_be_err(err);
3231 goto cleanup;
3232 }
3233
3234 /* Create tmp file for modified vfstab */
3235 if ((tmp_vfstab = (char *)malloc(strlen(vfstab) + 7))
3236 == NULL) {
3237 be_print_err(gettext("_update_vfstab: "
3238 "malloc failed\n"));
3239 ret = BE_ERR_NOMEM;
3240 goto cleanup;
3241 }
3242 tmp_vfstab_len = strlen(vfstab) + 7;
3243 (void) memset(tmp_vfstab, 0, tmp_vfstab_len);
3244 (void) strlcpy(tmp_vfstab, vfstab, tmp_vfstab_len);
3245 (void) strlcat(tmp_vfstab, "XXXXXX", tmp_vfstab_len);
3246 if ((fd = mkstemp(tmp_vfstab)) == -1) {
3247 err = errno;
3248 be_print_err(gettext("_update_vfstab: "
3249 "mkstemp failed: %s\n"), strerror(err));
3250 ret = errno_to_be_err(err);
3251 goto cleanup;
3252 }
3253 if ((tfile = fdopen(fd, "w")) == NULL) {
3254 err = errno;
3255 be_print_err(gettext("_update_vfstab: "
3256 "could not open file for write\n"));
3257 (void) close(fd);
3258 ret = errno_to_be_err(err);
3259 goto cleanup;
3260 }
3261
3262 while (fgets(comments_buf, BUFSIZ, comments)) {
3263 for (c = comments_buf; *c != '\0' && isspace(*c); c++)
3264 ;
3265 if (*c == '\0') {
3266 continue;
3267 } else if (*c == '#') {
3268 /*
3269 * If line is a comment line, just put
3270 * it through to the tmp vfstab.
3271 */
3272 (void) fputs(comments_buf, tfile);
3273 } else {
3274 /*
3275 * Else line is a vfstab entry, grab it
3276 * into a vfstab struct.
3277 */
3278 if (getvfsent(vfs_ents, &vp) != 0) {
3279 err = errno;
3280 be_print_err(gettext("_update_vfstab: "
3281 "getvfsent failed: %s\n"), strerror(err));
3282 ret = errno_to_be_err(err);
3283 goto cleanup;
3284 }
3285
3286 if (vp.vfs_special == NULL || vp.vfs_mountp == NULL) {
3287 (void) putvfsent(tfile, &vp);
3288 continue;
3289 }
3290
3291 /*
3292 * If the entry is one of the entries in the list
3293 * of file systems to update, modify it's device
3294 * field to be correct for this BE.
3295 */
3296 for (i = 0; i < fld->fs_num; i++) {
3297 if (strcmp(vp.vfs_special, fld->fs_list[i])
3298 == 0) {
3299 /*
3300 * Found entry that needs an update.
3301 * Replace the root container dataset
3302 * location and be_name in the
3303 * entry's device.
3304 */
3305 (void) strlcpy(dev, vp.vfs_special,
3306 sizeof (dev));
3307
3308 if ((ret = update_dataset(dev,
3309 sizeof (dev), be_name, old_rc_loc,
3310 new_rc_loc)) != 0) {
3311 be_print_err(
3312 gettext("_update_vfstab: "
3313 "Failed to update device "
3314 "field for vfstab entry "
3315 "%s\n"), fld->fs_list[i]);
3316 goto cleanup;
3317 }
3318
3319 vp.vfs_special = dev;
3320 break;
3321 }
3322 }
3323
3324 /* Put entry through to tmp vfstab */
3325 (void) putvfsent(tfile, &vp);
3326 }
3327 }
3328
3329 (void) fclose(comments);
3330 comments = NULL;
3331 (void) fclose(vfs_ents);
3332 vfs_ents = NULL;
3333 (void) fclose(tfile);
3334 tfile = NULL;
3335
3336 /* Copy tmp vfstab into place */
3337 if (rename(tmp_vfstab, vfstab) != 0) {
3338 err = errno;
3339 be_print_err(gettext("_update_vfstab: "
3340 "failed to rename file %s to %s: %s\n"), tmp_vfstab,
3341 vfstab, strerror(err));
3342 ret = errno_to_be_err(err);
3343 goto cleanup;
3344 }
3345
3346 /* Set the perms and ownership of the updated file */
3347 if (chmod(vfstab, sb.st_mode) != 0) {
3348 err = errno;
3349 be_print_err(gettext("_update_vfstab: "
3350 "failed to chmod %s: %s\n"), vfstab, strerror(err));
3351 ret = errno_to_be_err(err);
3352 goto cleanup;
3353 }
3354 if (chown(vfstab, sb.st_uid, sb.st_gid) != 0) {
3355 err = errno;
3356 be_print_err(gettext("_update_vfstab: "
3357 "failed to chown %s: %s\n"), vfstab, strerror(err));
3358 ret = errno_to_be_err(err);
3359 goto cleanup;
3360 }
3361
3362 cleanup:
3363 if (comments != NULL)
3364 (void) fclose(comments);
3365 if (vfs_ents != NULL)
3366 (void) fclose(vfs_ents);
3367 (void) unlink(tmp_vfstab);
3368 (void) free(tmp_vfstab);
3369 if (tfile != NULL)
3370 (void) fclose(tfile);
3371
3372 return (ret);
3373 }
3374
3375
3376 /*
3377 * Function: be_get_auto_name
3378 * Description: Generate an auto name constructed based on the BE name
3379 * of the original BE or zone BE being cloned.
3380 * Parameters:
3381 * obe_name - name of the original BE or zone BE being cloned.
3382 * container_ds - container dataset for the zone.
3383 * Note: if zone_be is false this should be
3384 * NULL.
3385 * zone_be - flag that indicates if we are operating on a zone BE.
3386 * Returns:
3387 * Success - pointer to auto generated BE name. The name
3388 * is allocated in heap storage so the caller is
3389 * responsible for free'ing the name.
3390 * Failure - NULL
3391 * Scope:
3392 * Private
3393 */
3394 static char *
3395 be_get_auto_name(char *obe_name, char *be_container_ds, boolean_t zone_be)
3396 {
3397 be_node_list_t *be_nodes = NULL;
3398 be_node_list_t *cur_be = NULL;
3399 char auto_be_name[MAXPATHLEN];
3400 char base_be_name[MAXPATHLEN];
3401 char cur_be_name[MAXPATHLEN];
3402 char *num_str = NULL;
3403 char *c = NULL;
3404 int num = 0;
3405 int cur_num = 0;
3406
3407 errno = 0;
3408
3409 /*
3410 * Check if obe_name is already in an auto BE name format.
3411 * If it is, then strip off the increment number to get the
3412 * base name.
3413 */
3414 (void) strlcpy(base_be_name, obe_name, sizeof (base_be_name));
3415
3416 if ((num_str = strrchr(base_be_name, BE_AUTO_NAME_DELIM))
3417 != NULL) {
3418 /* Make sure remaining string is all digits */
3419 c = num_str + 1;
3420 while (c[0] != '\0' && isdigit(c[0]))
3421 c++;
3422 /*
3423 * If we're now at the end of the string strip off the
3424 * increment number.
3425 */
3426 if (c[0] == '\0')
3427 num_str[0] = '\0';
3428 }
3429
3430 if (zone_be) {
3431 if (be_container_ds == NULL)
3432 return (NULL);
3433 if (be_get_zone_be_list(obe_name, be_container_ds,
3434 &be_nodes) != BE_SUCCESS) {
3435 be_print_err(gettext("be_get_auto_name: "
3436 "be_get_zone_be_list failed\n"));
3437 return (NULL);
3438 }
3439 } else if (_be_list(NULL, &be_nodes) != BE_SUCCESS) {
3440 be_print_err(gettext("be_get_auto_name: be_list failed\n"));
3441 return (NULL);
3442 }
3443
3444 for (cur_be = be_nodes; cur_be != NULL; cur_be = cur_be->be_next_node) {
3445 (void) strlcpy(cur_be_name, cur_be->be_node_name,
3446 sizeof (cur_be_name));
3447
3448 /* If cur_be_name doesn't match at least base be name, skip. */
3449 if (strncmp(cur_be_name, base_be_name, strlen(base_be_name))
3450 != 0)
3451 continue;
3452
3453 /* Get the string following the base be name */
3454 num_str = cur_be_name + strlen(base_be_name);
3455
3456 /*
3457 * If nothing follows the base be name, this cur_be_name
3458 * is the BE named with the base be name, skip.
3459 */
3460 if (num_str == NULL || num_str[0] == '\0')
3461 continue;
3462
3463 /*
3464 * Remove the name delimiter. If its not there,
3465 * cur_be_name isn't part of this BE name stream, skip.
3466 */
3467 if (num_str[0] == BE_AUTO_NAME_DELIM)
3468 num_str++;
3469 else
3470 continue;
3471
3472 /* Make sure remaining string is all digits */
3473 c = num_str;
3474 while (c[0] != '\0' && isdigit(c[0]))
3475 c++;
3476 if (c[0] != '\0')
3477 continue;
3478
3479 /* Convert the number string to an int */
3480 cur_num = atoi(num_str);
3481
3482 /*
3483 * If failed to convert the string, skip it. If its too
3484 * long to be converted to an int, we wouldn't auto generate
3485 * this number anyway so there couldn't be a conflict.
3486 * We treat it as a manually created BE name.
3487 */
3488 if (cur_num == 0 && errno == EINVAL)
3489 continue;
3490
3491 /*
3492 * Compare current number to current max number,
3493 * take higher of the two.
3494 */
3495 if (cur_num > num)
3496 num = cur_num;
3497 }
3498
3499 /*
3500 * Store off a copy of 'num' incase we need it later. If incrementing
3501 * 'num' causes it to roll over, this means 'num' is the largest
3502 * positive int possible; we'll need it later in the loop to determine
3503 * if we've exhausted all possible increment numbers. We store it in
3504 * 'cur_num'.
3505 */
3506 cur_num = num;
3507
3508 /* Increment 'num' to get new auto BE name number */
3509 if (++num <= 0) {
3510 int ret = 0;
3511
3512 /*
3513 * Since incrementing 'num' caused it to rollover, start
3514 * over at 0 and find the first available number.
3515 */
3516 for (num = 0; num < cur_num; num++) {
3517
3518 (void) snprintf(cur_be_name, sizeof (cur_be_name),
3519 "%s%c%d", base_be_name, BE_AUTO_NAME_DELIM, num);
3520
3521 ret = zpool_iter(g_zfs, be_exists_callback,
3522 cur_be_name);
3523
3524 if (ret == 0) {
3525 /*
3526 * BE name doesn't exist, break out
3527 * to use 'num'.
3528 */
3529 break;
3530 } else if (ret == 1) {
3531 /* BE name exists, continue looking */
3532 continue;
3533 } else {
3534 be_print_err(gettext("be_get_auto_name: "
3535 "zpool_iter failed: %s\n"),
3536 libzfs_error_description(g_zfs));
3537 be_free_list(be_nodes);
3538 return (NULL);
3539 }
3540 }
3541
3542 /*
3543 * If 'num' equals 'cur_num', we've exhausted all possible
3544 * auto BE names for this base BE name.
3545 */
3546 if (num == cur_num) {
3547 be_print_err(gettext("be_get_auto_name: "
3548 "No more available auto BE names for base "
3549 "BE name %s\n"), base_be_name);
3550 be_free_list(be_nodes);
3551 return (NULL);
3552 }
3553 }
3554
3555 be_free_list(be_nodes);
3556
3557 /*
3558 * Generate string for auto BE name.
3559 */
3560 (void) snprintf(auto_be_name, sizeof (auto_be_name), "%s%c%d",
3561 base_be_name, BE_AUTO_NAME_DELIM, num);
3562
3563 if ((c = strdup(auto_be_name)) == NULL) {
3564 be_print_err(gettext("be_get_auto_name: "
3565 "memory allocation failed\n"));
3566 return (NULL);
3567 }
3568
3569 return (c);
3570 }
3571
3572 /*
3573 * Function: be_get_console_prop
3574 * Description: Determine console device.
3575 * Returns:
3576 * Success - pointer to console setting.
3577 * Failure - NULL
3578 * Scope:
3579 * Private
3580 */
3581 static char *
3582 be_get_console_prop(void)
3583 {
3584 di_node_t dn;
3585 char *console = NULL;
3586
3587 if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) {
3588 be_print_err(gettext("be_get_console_prop: "
3589 "di_init() failed\n"));
3590 return (NULL);
3591 }
3592
3593 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3594 "console", &console) != -1) {
3595 di_fini(dn);
3596 return (console);
3597 }
3598
3599 if (console == NULL) {
3600 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3601 "output-device", &console) != -1) {
3602 di_fini(dn);
3603 if (strncmp(console, "screen", strlen("screen")) == 0)
3604 console = BE_DEFAULT_CONSOLE;
3605 }
3606 }
3607
3608 /*
3609 * Default console to text
3610 */
3611 if (console == NULL) {
3612 console = BE_DEFAULT_CONSOLE;
3613 }
3614
3615 return (console);
3616 }
3617
3618 /*
3619 * Function: be_create_menu
3620 * Description:
3621 * This function is used if no menu.lst file exists. In
3622 * this case a new file is created and if needed default
3623 * lines are added to the file.
3624 * Parameters:
3625 * pool - The name of the pool the menu.lst file is on
3626 * menu_file - The name of the file we're creating.
3627 * menu_fp - A pointer to the file pointer of the file we
3628 * created. This is also used to pass back the file
3629 * pointer to the newly created file.
3630 * mode - the original mode used for the failed attempt to
3631 * non-existent file.
3632 * Returns:
3633 * BE_SUCCESS - Success
3634 * be_errno_t - Failure
3635 * Scope:
3636 * Private
3637 */
3638 static int
3639 be_create_menu(
3640 char *pool,
3641 char *menu_file,
3642 FILE **menu_fp,
3643 char *mode)
3644 {
3645 be_node_list_t *be_nodes = NULL;
3646 char *menu_path = NULL;
3647 char *be_rpool = NULL;
3648 char *be_name = NULL;
3649 char *console = NULL;
3650 errno = 0;
3651
3652 if (menu_file == NULL || menu_fp == NULL || mode == NULL)
3653 return (BE_ERR_INVAL);
3654
3655 menu_path = strdup(menu_file);
3656 if (menu_path == NULL)
3657 return (BE_ERR_NOMEM);
3658
3659 (void) dirname(menu_path);
3660 if (*menu_path == '.') {
3661 free(menu_path);
3662 return (BE_ERR_BAD_MENU_PATH);
3663 }
3664 if (mkdirp(menu_path,
3665 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
3666 errno != EEXIST) {
3667 free(menu_path);
3668 be_print_err(gettext("be_create_menu: Failed to create the %s "
3669 "directory: %s\n"), menu_path, strerror(errno));
3670 return (errno_to_be_err(errno));
3671 }
3672 free(menu_path);
3673
3674 /*
3675 * Check to see if this system supports grub
3676 */
3677 if (be_has_grub()) {
3678 /*
3679 * The grub menu is missing so we need to create it
3680 * and fill in the first few lines.
3681 */
3682 FILE *temp_fp = fopen(menu_file, "a+");
3683 if (temp_fp == NULL) {
3684 *menu_fp = NULL;
3685 return (errno_to_be_err(errno));
3686 }
3687
3688 if ((console = be_get_console_prop()) != NULL) {
3689
3690 /*
3691 * If console is redirected to serial line,
3692 * GRUB splash screen will not be enabled.
3693 */
3694 if (strncmp(console, "text", strlen("text")) == 0 ||
3695 strncmp(console, "graphics",
3696 strlen("graphics")) == 0) {
3697 /*
3698 (void) fprintf(temp_fp, "%s\n", BE_GRUB_SPLASH);
3699 (void) fprintf(temp_fp, "%s\n",
3700 BE_GRUB_FOREGROUND);
3701 (void) fprintf(temp_fp, "%s\n",
3702 BE_GRUB_BACKGROUND);
3703 (void) fprintf(temp_fp, "%s\n",
3704 BE_GRUB_DEFAULT);*/
3705 } else {
3706 be_print_err(gettext("be_create_menu: "
3707 "console on serial line, "
3708 "GRUB splash image will be disabled\n"));
3709 }
3710 }
3711
3712 (void) fprintf(temp_fp, "timeout=30\n");
3713 (void) fclose(temp_fp);
3714
3715 } else {
3716 /*
3717 * The menu file doesn't exist so we need to create a
3718 * blank file.
3719 */
3720 FILE *temp_fp = fopen(menu_file, "w+");
3721 if (temp_fp == NULL) {
3722 *menu_fp = NULL;
3723 return (errno_to_be_err(errno));
3724 }
3725 (void) fclose(temp_fp);
3726 }
3727
3728 /*
3729 * Now we need to add all the BE's back into the the file.
3730 */
3731 if (_be_list(NULL, &be_nodes) == BE_SUCCESS) {
3732 while (be_nodes != NULL) {
3733 if (strcmp(pool, be_nodes->be_rpool) == 0) {
3734 (void) be_append_menu(be_nodes->be_node_name,
3735 be_nodes->be_rpool, NULL, NULL, NULL);
3736 }
3737 if (be_nodes->be_active_on_boot) {
3738 be_rpool = strdup(be_nodes->be_rpool);
3739 be_name = strdup(be_nodes->be_node_name);
3740 }
3741
3742 be_nodes = be_nodes->be_next_node;
3743 }
3744 }
3745 be_free_list(be_nodes);
3746
3747 /*
3748 * Check to see if this system supports grub
3749 */
3750 if (be_has_grub()) {
3751 int err = be_change_grub_default(be_name, be_rpool);
3752 if (err != BE_SUCCESS)
3753 return (err);
3754 }
3755 *menu_fp = fopen(menu_file, mode);
3756 if (*menu_fp == NULL)
3757 return (errno_to_be_err(errno));
3758
3759 return (BE_SUCCESS);
3760 }
3761
3762 /*
3763 * Function: be_open_menu
3764 * Description:
3765 * This function is used it open the menu.lst file. If this
3766 * file does not exist be_create_menu is called to create it
3767 * and the open file pointer is returned. If the file does
3768 * exist it is simply opened using the mode passed in.
3769 * Parameters:
3770 * pool - The name of the pool the menu.lst file is on
3771 * menu_file - The name of the file we're opening.
3772 * menu_fp - A pointer to the file pointer of the file we're
3773 * opening. This is also used to pass back the file
3774 * pointer.
3775 * mode - the original mode to be used for opening the menu.lst
3776 * file.
3777 * create_menu - If this is true and the menu.lst file does not
3778 * exist we will attempt to re-create it. However
3779 * if it's false the error returned from the fopen
3780 * will be returned.
3781 * Returns:
3782 * BE_SUCCESS - Success
3783 * be_errno_t - Failure
3784 * Scope:
3785 * Private
3786 */
3787 static int
3788 be_open_menu(
3789 char *pool,
3790 char *menu_file,
3791 FILE **menu_fp,
3792 char *mode,
3793 boolean_t create_menu)
3794 {
3795 int err = 0;
3796 boolean_t set_print = B_FALSE;
3797
3798 *menu_fp = fopen(menu_file, mode);
3799 err = errno;
3800 if (*menu_fp == NULL) {
3801 if (err == ENOENT && create_menu) {
3802 be_print_err(gettext("be_open_menu: menu.lst "
3803 "file %s does not exist,\n"), menu_file);
3804 if (!do_print) {
3805 set_print = B_TRUE;
3806 do_print = B_TRUE;
3807 }
3808 be_print_err(gettext("WARNING: menu.lst "
3809 "file %s does not exist,\n generating "
3810 "a new menu.lst file\n"), menu_file);
3811 if (set_print)
3812 do_print = B_FALSE;
3813 err = 0;
3814 if ((err = be_create_menu(pool, menu_file,
3815 menu_fp, mode)) == ENOENT)
3816 return (BE_ERR_NO_MENU);
3817 else if (err != BE_SUCCESS)
3818 return (err);
3819 else if (*menu_fp == NULL)
3820 return (BE_ERR_NO_MENU);
3821 } else {
3822 be_print_err(gettext("be_open_menu: failed "
3823 "to open menu.lst file %s\n"), menu_file);
3824 if (err == ENOENT)
3825 return (BE_ERR_NO_MENU);
3826 else
3827 return (errno_to_be_err(err));
3828 }
3829 }
3830 return (BE_SUCCESS);
3831 }