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