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