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