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