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 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * bootadm(1M) is a new utility for managing bootability of 28 * Solaris *Newboot* environments. It has two primary tasks: 29 * - Allow end users to manage bootability of Newboot Solaris instances 30 * - Provide services to other subsystems in Solaris (primarily Install) 31 */ 32 33 /* Headers */ 34 #include <stdio.h> 35 #include <errno.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 #include <sys/types.h> 40 #include <sys/stat.h> 41 #include <alloca.h> 42 #include <stdarg.h> 43 #include <limits.h> 44 #include <signal.h> 45 #include <sys/wait.h> 46 #include <sys/mnttab.h> 47 #include <sys/mntent.h> 48 #include <sys/statvfs.h> 49 #include <libnvpair.h> 50 #include <ftw.h> 51 #include <fcntl.h> 52 #include <strings.h> 53 #include <utime.h> 54 #include <sys/systeminfo.h> 55 #include <sys/dktp/fdisk.h> 56 #include <sys/param.h> 57 #include <dirent.h> 58 #include <ctype.h> 59 #include <libgen.h> 60 #include <sys/sysmacros.h> 61 #include <sys/elf.h> 62 #include <libscf.h> 63 #include <zlib.h> 64 #include <sys/lockfs.h> 65 #include <sys/filio.h> 66 #ifdef i386 67 #include <libfdisk.h> 68 #endif 69 70 #if !defined(_OPB) 71 #include <sys/ucode.h> 72 #endif 73 74 #include <pwd.h> 75 #include <grp.h> 76 #include <device_info.h> 77 #include <sys/vtoc.h> 78 #include <sys/efi_partition.h> 79 #include <regex.h> 80 #include <locale.h> 81 82 #include "message.h" 83 #include "bootadm.h" 84 85 #ifndef TEXT_DOMAIN 86 #define TEXT_DOMAIN "SUNW_OST_OSCMD" 87 #endif /* TEXT_DOMAIN */ 88 89 /* Type definitions */ 90 91 /* Primary subcmds */ 92 typedef enum { 93 BAM_MENU = 3, 94 BAM_ARCHIVE 95 } subcmd_t; 96 97 typedef enum { 98 OPT_ABSENT = 0, /* No option */ 99 OPT_REQ, /* option required */ 100 OPT_OPTIONAL /* option may or may not be present */ 101 } option_t; 102 103 typedef struct { 104 char *subcmd; 105 option_t option; 106 error_t (*handler)(); 107 int unpriv; /* is this an unprivileged command */ 108 } subcmd_defn_t; 109 110 #define LINE_INIT 0 /* lineNum initial value */ 111 #define ENTRY_INIT -1 /* entryNum initial value */ 112 #define ALL_ENTRIES -2 /* selects all boot entries */ 113 114 #define GRUB_DIR "/boot/grub" 115 #define GRUB_STAGE2 GRUB_DIR "/stage2" 116 #define GRUB_MENU "/boot/grub/menu.lst" 117 #define MENU_TMP "/boot/grub/menu.lst.tmp" 118 #define GRUB_BACKUP_MENU "/etc/lu/GRUB_backup_menu" 119 #define RAMDISK_SPECIAL "/ramdisk" 120 #define STUBBOOT "/stubboot" 121 #define MULTIBOOT "/platform/i86pc/multiboot" 122 #define GRUBSIGN_DIR "/boot/grub/bootsign" 123 #define GRUBSIGN_BACKUP "/etc/bootsign" 124 #define GRUBSIGN_UFS_PREFIX "rootfs" 125 #define GRUBSIGN_ZFS_PREFIX "pool_" 126 #define GRUBSIGN_LU_PREFIX "BE_" 127 #define UFS_SIGNATURE_LIST "/var/run/grub_ufs_signatures" 128 #define ZFS_LEGACY_MNTPT "/tmp/bootadm_mnt_zfs_legacy" 129 130 #define BOOTADM_RDONLY_TEST "BOOTADM_RDONLY_TEST" 131 132 /* lock related */ 133 #define BAM_LOCK_FILE "/var/run/bootadm.lock" 134 #define LOCK_FILE_PERMS (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) 135 136 #define CREATE_RAMDISK "boot/solaris/bin/create_ramdisk" 137 #define CREATE_DISKMAP "boot/solaris/bin/create_diskmap" 138 #define EXTRACT_BOOT_FILELIST "boot/solaris/bin/extract_boot_filelist" 139 #define GRUBDISK_MAP "/var/run/solaris_grubdisk.map" 140 141 #define GRUB_slice "/etc/lu/GRUB_slice" 142 #define GRUB_root "/etc/lu/GRUB_root" 143 #define GRUB_fdisk "/etc/lu/GRUB_fdisk" 144 #define GRUB_fdisk_target "/etc/lu/GRUB_fdisk_target" 145 #define FINDROOT_INSTALLGRUB "/etc/lu/installgrub.findroot" 146 #define LULIB "/usr/lib/lu/lulib" 147 #define LULIB_PROPAGATE_FILE "lulib_propagate_file" 148 #define CKSUM "/usr/bin/cksum" 149 #define LU_MENU_CKSUM "/etc/lu/menu.cksum" 150 #define BOOTADM "/sbin/bootadm" 151 152 #define INSTALLGRUB "/sbin/installgrub" 153 #define STAGE1 "/boot/grub/stage1" 154 #define STAGE2 "/boot/grub/stage2" 155 156 typedef enum zfs_mnted { 157 ZFS_MNT_ERROR = -1, 158 LEGACY_MOUNTED = 1, 159 LEGACY_ALREADY, 160 ZFS_MOUNTED, 161 ZFS_ALREADY 162 } zfs_mnted_t; 163 164 /* 165 * The following two defines are used to detect and create the correct 166 * boot archive when safemode patching is underway. LOFS_PATCH_FILE is a 167 * contracted private interface between bootadm and the install 168 * consolidation. It is set by pdo.c when a patch with SUNW_PATCH_SAFEMODE 169 * is applied. 170 */ 171 #define LOFS_PATCH_FILE "/var/run/.patch_loopback_mode" 172 #define LOFS_PATCH_MNT "/var/run/.patch_root_loopbackmnt" 173 174 /* 175 * Default file attributes 176 */ 177 #define DEFAULT_DEV_MODE 0644 /* default permissions */ 178 #define DEFAULT_DEV_UID 0 /* user root */ 179 #define DEFAULT_DEV_GID 3 /* group sys */ 180 181 /* 182 * Menu related 183 * menu_cmd_t and menu_cmds must be kept in sync 184 */ 185 char *menu_cmds[] = { 186 "default", /* DEFAULT_CMD */ 187 "timeout", /* TIMEOUT_CMD */ 188 "title", /* TITLE_CMD */ 189 "root", /* ROOT_CMD */ 190 "kernel", /* KERNEL_CMD */ 191 "kernel$", /* KERNEL_DOLLAR_CMD */ 192 "module", /* MODULE_CMD */ 193 "module$", /* MODULE_DOLLAR_CMD */ 194 " ", /* SEP_CMD */ 195 "#", /* COMMENT_CMD */ 196 "chainloader", /* CHAINLOADER_CMD */ 197 "args", /* ARGS_CMD */ 198 "findroot", /* FINDROOT_CMD */ 199 "bootfs", /* BOOTFS_CMD */ 200 NULL 201 }; 202 203 #define OPT_ENTRY_NUM "entry" 204 205 /* 206 * exec_cmd related 207 */ 208 typedef struct { 209 line_t *head; 210 line_t *tail; 211 } filelist_t; 212 213 #define BOOT_FILE_LIST "boot/solaris/filelist.ramdisk" 214 #define ETC_FILE_LIST "etc/boot/solaris/filelist.ramdisk" 215 216 #define FILE_STAT "boot/solaris/filestat.ramdisk" 217 #define FILE_STAT_TMP "boot/solaris/filestat.ramdisk.tmp" 218 #define DIR_PERMS (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) 219 #define FILE_STAT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) 220 221 #define FILE_STAT_TIMESTAMP "boot/solaris/timestamp.cache" 222 223 /* Globals */ 224 int bam_verbose; 225 int bam_force; 226 int bam_debug; 227 static char *prog; 228 static subcmd_t bam_cmd; 229 static char *bam_root; 230 static int bam_rootlen; 231 static int bam_root_readonly; 232 static int bam_alt_root; 233 static int bam_extend = 0; 234 static int bam_purge = 0; 235 static char *bam_subcmd; 236 static char *bam_opt; 237 static char **bam_argv; 238 static int bam_argc; 239 static int bam_check; 240 static int bam_saved_check; 241 static int bam_smf_check; 242 static int bam_lock_fd = -1; 243 static int bam_zfs; 244 static char rootbuf[PATH_MAX] = "/"; 245 static int bam_update_all; 246 static int bam_alt_platform; 247 static char *bam_platform; 248 static char *bam_home_env = NULL; 249 250 /* function prototypes */ 251 static void parse_args_internal(int, char *[]); 252 static void parse_args(int, char *argv[]); 253 static error_t bam_menu(char *, char *, int, char *[]); 254 static error_t bam_archive(char *, char *); 255 256 static void bam_lock(void); 257 static void bam_unlock(void); 258 259 static int exec_cmd(char *, filelist_t *); 260 static error_t read_globals(menu_t *, char *, char *, int); 261 static int menu_on_bootdisk(char *os_root, char *menu_root); 262 static menu_t *menu_read(char *); 263 static error_t menu_write(char *, menu_t *); 264 static void linelist_free(line_t *); 265 static void menu_free(menu_t *); 266 static void filelist_free(filelist_t *); 267 static error_t list2file(char *, char *, char *, line_t *); 268 static error_t list_entry(menu_t *, char *, char *); 269 static error_t list_setting(menu_t *, char *, char *); 270 static error_t delete_all_entries(menu_t *, char *, char *); 271 static error_t update_entry(menu_t *mp, char *menu_root, char *opt); 272 static error_t update_temp(menu_t *mp, char *dummy, char *opt); 273 274 static error_t update_archive(char *, char *); 275 static error_t list_archive(char *, char *); 276 static error_t update_all(char *, char *); 277 static error_t read_list(char *, filelist_t *); 278 static error_t set_option(menu_t *, char *, char *); 279 static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t); 280 static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t); 281 static char *expand_path(const char *); 282 283 static long s_strtol(char *); 284 static int s_fputs(char *, FILE *); 285 286 static int is_zfs(char *root); 287 static int is_ufs(char *root); 288 static int is_pcfs(char *root); 289 static int is_amd64(void); 290 static char *get_machine(void); 291 static void append_to_flist(filelist_t *, char *); 292 static char *mount_top_dataset(char *pool, zfs_mnted_t *mnted); 293 static int umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt); 294 static int ufs_add_to_sign_list(char *sign); 295 static error_t synchronize_BE_menu(void); 296 297 #if !defined(_OPB) 298 static void ucode_install(); 299 #endif 300 301 /* Menu related sub commands */ 302 static subcmd_defn_t menu_subcmds[] = { 303 "set_option", OPT_ABSENT, set_option, 0, /* PUB */ 304 "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */ 305 "delete_all_entries", OPT_ABSENT, delete_all_entries, 0, /* PVT */ 306 "update_entry", OPT_REQ, update_entry, 0, /* menu */ 307 "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */ 308 "upgrade", OPT_ABSENT, upgrade_menu, 0, /* menu */ 309 "list_setting", OPT_OPTIONAL, list_setting, 1, /* menu */ 310 "disable_hypervisor", OPT_ABSENT, cvt_to_metal, 0, /* menu */ 311 "enable_hypervisor", OPT_ABSENT, cvt_to_hyper, 0, /* menu */ 312 NULL, 0, NULL, 0 /* must be last */ 313 }; 314 315 /* Archive related sub commands */ 316 static subcmd_defn_t arch_subcmds[] = { 317 "update", OPT_ABSENT, update_archive, 0, /* PUB */ 318 "update_all", OPT_ABSENT, update_all, 0, /* PVT */ 319 "list", OPT_OPTIONAL, list_archive, 1, /* PUB */ 320 NULL, 0, NULL, 0 /* must be last */ 321 }; 322 323 enum dircache_copy_opt { 324 FILE32 = 0, 325 FILE64, 326 CACHEDIR_NUM 327 }; 328 329 /* 330 * Directory specific flags: 331 * NEED_UPDATE : the specified archive needs to be updated 332 * NO_MULTI : don't extend the specified archive, but recreate it 333 */ 334 #define NEED_UPDATE 0x00000001 335 #define NO_MULTI 0x00000002 336 337 #define set_dir_flag(id, f) (walk_arg.dirinfo[id].flags |= f) 338 #define unset_dir_flag(id, f) (walk_arg.dirinfo[id].flags &= ~f) 339 #define is_dir_flag_on(id, f) (walk_arg.dirinfo[id].flags & f ? 1 : 0) 340 341 #define get_cachedir(id) (walk_arg.dirinfo[id].cdir_path) 342 #define get_updatedir(id) (walk_arg.dirinfo[id].update_path) 343 #define get_count(id) (walk_arg.dirinfo[id].count) 344 #define has_cachedir(id) (walk_arg.dirinfo[id].has_dir) 345 #define set_dir_present(id) (walk_arg.dirinfo[id].has_dir = 1) 346 347 /* 348 * dirinfo_t (specific cache directory information): 349 * cdir_path: path to the archive cache directory 350 * update_path: path to the update directory (contains the files that will be 351 * used to extend the archive) 352 * has_dir: the specified cache directory is active 353 * count: the number of files to update 354 * flags: directory specific flags 355 */ 356 typedef struct _dirinfo { 357 char cdir_path[PATH_MAX]; 358 char update_path[PATH_MAX]; 359 int has_dir; 360 int count; 361 int flags; 362 } dirinfo_t; 363 364 /* 365 * Update flags: 366 * NEED_CACHE_DIR : cache directory is missing and needs to be created 367 * IS_SPARC_TARGET : the target mountpoint is a SPARC environment 368 * UPDATE_ERROR : an error occourred while traversing the list of files 369 * RDONLY_FSCHK : the target filesystem is read-only 370 * RAMDSK_FSCHK : the target filesystem is on a ramdisk 371 */ 372 #define NEED_CACHE_DIR 0x00000001 373 #define IS_SPARC_TARGET 0x00000002 374 #define UPDATE_ERROR 0x00000004 375 #define RDONLY_FSCHK 0x00000008 376 #define INVALIDATE_CACHE 0x00000010 377 378 #define is_flag_on(flag) (walk_arg.update_flags & flag ? 1 : 0) 379 #define set_flag(flag) (walk_arg.update_flags |= flag) 380 #define unset_flag(flag) (walk_arg.update_flags &= ~flag) 381 382 /* 383 * struct walk_arg : 384 * update_flags: flags related to the current updating process 385 * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs 386 * sparcfile: list of file paths for mkisofs -path-list (SPARC only) 387 */ 388 static struct { 389 int update_flags; 390 nvlist_t *new_nvlp; 391 nvlist_t *old_nvlp; 392 FILE *sparcfile; 393 dirinfo_t dirinfo[CACHEDIR_NUM]; 394 } walk_arg; 395 396 struct safefile { 397 char *name; 398 struct safefile *next; 399 }; 400 401 static struct safefile *safefiles = NULL; 402 #define NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update" 403 404 /* Thanks growisofs */ 405 #define CD_BLOCK ((off64_t)2048) 406 #define VOLDESC_OFF 16 407 #define DVD_BLOCK (32*1024) 408 #define MAX_IVDs 16 409 410 struct iso_pdesc { 411 unsigned char type [1]; 412 unsigned char id [5]; 413 unsigned char void1 [80-5-1]; 414 unsigned char volume_space_size [8]; 415 unsigned char void2 [2048-80-8]; 416 }; 417 418 /* 419 * COUNT_MAX: maximum number of changed files to justify a multisession update 420 * BA_SIZE_MAX: maximum size of the boot_archive to justify a multisession 421 * update 422 */ 423 #define COUNT_MAX 50 424 #define BA_SIZE_MAX (50 * 1024 * 1024) 425 426 #define bam_nowrite() (bam_check || bam_smf_check) 427 428 static int sync_menu = 1; /* whether we need to sync the BE menus */ 429 430 static void 431 usage(void) 432 { 433 (void) fprintf(stderr, "USAGE:\n"); 434 435 /* archive usage */ 436 (void) fprintf(stderr, 437 "\t%s update-archive [-vn] [-R altroot [-p platform>]]\n", prog); 438 (void) fprintf(stderr, 439 "\t%s list-archive [-R altroot [-p platform>]]\n", prog); 440 #if !defined(_OPB) 441 /* x86 only */ 442 (void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog); 443 (void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog); 444 #endif 445 } 446 447 /* 448 * Best effort attempt to restore the $HOME value. 449 */ 450 static void 451 restore_env() 452 { 453 char home_env[PATH_MAX]; 454 455 if (bam_home_env) { 456 (void) snprintf(home_env, sizeof (home_env), "HOME=%s", 457 bam_home_env); 458 (void) putenv(home_env); 459 } 460 } 461 462 463 #define SLEEP_TIME 5 464 #define MAX_TRIES 4 465 466 /* 467 * Sanitize the environment in which bootadm will execute its sub-processes 468 * (ex. mkisofs). This is done to prevent those processes from attempting 469 * to access files (ex. .mkisofsrc) or stat paths that might be on NFS 470 * or, potentially, insecure. 471 */ 472 static void 473 sanitize_env() 474 { 475 int stry = 0; 476 477 /* don't depend on caller umask */ 478 (void) umask(0022); 479 480 /* move away from a potential unsafe current working directory */ 481 while (chdir("/") == -1) { 482 if (errno != EINTR) { 483 bam_print("WARNING: unable to chdir to /"); 484 break; 485 } 486 } 487 488 bam_home_env = getenv("HOME"); 489 while (bam_home_env != NULL && putenv("HOME=/") == -1) { 490 if (errno == ENOMEM) { 491 /* retry no more than MAX_TRIES times */ 492 if (++stry > MAX_TRIES) { 493 bam_print("WARNING: unable to recover from " 494 "system memory pressure... aborting \n"); 495 bam_exit(EXIT_FAILURE); 496 } 497 /* memory is tight, try to sleep */ 498 bam_print("Attempting to recover from memory pressure: " 499 "sleeping for %d seconds\n", SLEEP_TIME * stry); 500 (void) sleep(SLEEP_TIME * stry); 501 } else { 502 bam_print("WARNING: unable to sanitize HOME\n"); 503 } 504 } 505 } 506 507 int 508 main(int argc, char *argv[]) 509 { 510 error_t ret; 511 512 (void) setlocale(LC_ALL, ""); 513 (void) textdomain(TEXT_DOMAIN); 514 515 if ((prog = strrchr(argv[0], '/')) == NULL) { 516 prog = argv[0]; 517 } else { 518 prog++; 519 } 520 521 INJECT_ERROR1("ASSERT_ON", assert(0)) 522 523 sanitize_env(); 524 525 parse_args(argc, argv); 526 527 switch (bam_cmd) { 528 case BAM_MENU: 529 ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv); 530 break; 531 case BAM_ARCHIVE: 532 ret = bam_archive(bam_subcmd, bam_opt); 533 break; 534 default: 535 usage(); 536 bam_exit(1); 537 } 538 539 if (ret != BAM_SUCCESS) 540 bam_exit((ret == BAM_NOCHANGE) ? 2 : 1); 541 542 bam_unlock(); 543 return (0); 544 } 545 546 /* 547 * Equivalence of public and internal commands: 548 * update-archive -- -a update 549 * list-archive -- -a list 550 * set-menu -- -m set_option 551 * list-menu -- -m list_entry 552 * update-menu -- -m update_entry 553 */ 554 static struct cmd_map { 555 char *bam_cmdname; 556 int bam_cmd; 557 char *bam_subcmd; 558 } cmd_map[] = { 559 { "update-archive", BAM_ARCHIVE, "update"}, 560 { "list-archive", BAM_ARCHIVE, "list"}, 561 { "set-menu", BAM_MENU, "set_option"}, 562 { "list-menu", BAM_MENU, "list_entry"}, 563 { "update-menu", BAM_MENU, "update_entry"}, 564 { NULL, 0, NULL} 565 }; 566 567 /* 568 * Commands syntax published in bootadm(1M) are parsed here 569 */ 570 static void 571 parse_args(int argc, char *argv[]) 572 { 573 struct cmd_map *cmp = cmd_map; 574 575 /* command conforming to the final spec */ 576 if (argc > 1 && argv[1][0] != '-') { 577 /* 578 * Map commands to internal table. 579 */ 580 while (cmp->bam_cmdname) { 581 if (strcmp(argv[1], cmp->bam_cmdname) == 0) { 582 bam_cmd = cmp->bam_cmd; 583 bam_subcmd = cmp->bam_subcmd; 584 break; 585 } 586 cmp++; 587 } 588 if (cmp->bam_cmdname == NULL) { 589 usage(); 590 bam_exit(1); 591 } 592 argc--; 593 argv++; 594 } 595 596 parse_args_internal(argc, argv); 597 } 598 599 /* 600 * A combination of public and private commands are parsed here. 601 * The internal syntax and the corresponding functionality are: 602 * -a update -- update-archive 603 * -a list -- list-archive 604 * -a update-all -- (reboot to sync all mnted OS archive) 605 * -m update_entry -- update-menu 606 * -m list_entry -- list-menu 607 * -m update_temp -- (reboot -- [boot-args]) 608 * -m delete_all_entries -- (called from install) 609 * -m enable_hypervisor [args] -- cvt_to_hyper 610 * -m disable_hypervisor -- cvt_to_metal 611 * -m list_setting [entry] [value] -- list_setting 612 * 613 * A set of private flags is there too: 614 * -F -- purge the cache directories and rebuild them 615 * -e -- use the (faster) archive update approach (used by 616 * reboot) 617 */ 618 static void 619 parse_args_internal(int argc, char *argv[]) 620 { 621 int c, error; 622 extern char *optarg; 623 extern int optind, opterr; 624 625 /* Suppress error message from getopt */ 626 opterr = 0; 627 628 error = 0; 629 while ((c = getopt(argc, argv, "a:d:fm:no:veFCR:p:XZ")) != -1) { 630 switch (c) { 631 case 'a': 632 if (bam_cmd) { 633 error = 1; 634 bam_error(MULT_CMDS, c); 635 } 636 bam_cmd = BAM_ARCHIVE; 637 bam_subcmd = optarg; 638 break; 639 case 'd': 640 if (bam_debug) { 641 error = 1; 642 bam_error(DUP_OPT, c); 643 } 644 bam_debug = s_strtol(optarg); 645 break; 646 case 'f': 647 bam_force = 1; 648 break; 649 case 'F': 650 bam_purge = 1; 651 break; 652 case 'm': 653 if (bam_cmd) { 654 error = 1; 655 bam_error(MULT_CMDS, c); 656 } 657 bam_cmd = BAM_MENU; 658 bam_subcmd = optarg; 659 break; 660 case 'n': 661 bam_check = 1; 662 /* 663 * We save the original value of bam_check. The new 664 * approach in case of a read-only filesystem is to 665 * behave as a check, so we need a way to restore the 666 * original value after the evaluation of the read-only 667 * filesystem has been done. 668 * Even if we don't allow at the moment a check with 669 * update_all, this approach is more robust than 670 * simply resetting bam_check to zero. 671 */ 672 bam_saved_check = 1; 673 break; 674 case 'o': 675 if (bam_opt) { 676 error = 1; 677 bam_error(DUP_OPT, c); 678 } 679 bam_opt = optarg; 680 break; 681 case 'v': 682 bam_verbose = 1; 683 break; 684 case 'C': 685 bam_smf_check = 1; 686 break; 687 case 'R': 688 if (bam_root) { 689 error = 1; 690 bam_error(DUP_OPT, c); 691 break; 692 } else if (realpath(optarg, rootbuf) == NULL) { 693 error = 1; 694 bam_error(CANT_RESOLVE, optarg, 695 strerror(errno)); 696 break; 697 } 698 bam_alt_root = 1; 699 bam_root = rootbuf; 700 bam_rootlen = strlen(rootbuf); 701 break; 702 case 'p': 703 bam_alt_platform = 1; 704 bam_platform = optarg; 705 if ((strcmp(bam_platform, "i86pc") != 0) && 706 (strcmp(bam_platform, "sun4u") != 0) && 707 (strcmp(bam_platform, "sun4v") != 0)) { 708 error = 1; 709 bam_error(INVALID_PLAT, bam_platform); 710 } 711 break; 712 case 'X': 713 bam_is_hv = BAM_HV_PRESENT; 714 break; 715 case 'Z': 716 bam_zfs = 1; 717 break; 718 case 'e': 719 bam_extend = 1; 720 break; 721 case '?': 722 error = 1; 723 bam_error(BAD_OPT, optopt); 724 break; 725 default : 726 error = 1; 727 bam_error(BAD_OPT, c); 728 break; 729 } 730 } 731 732 /* 733 * An alternate platform requires an alternate root 734 */ 735 if (bam_alt_platform && bam_alt_root == 0) { 736 usage(); 737 bam_exit(0); 738 } 739 740 /* 741 * A command option must be specfied 742 */ 743 if (!bam_cmd) { 744 if (bam_opt && strcmp(bam_opt, "all") == 0) { 745 usage(); 746 bam_exit(0); 747 } 748 bam_error(NEED_CMD); 749 error = 1; 750 } 751 752 if (error) { 753 usage(); 754 bam_exit(1); 755 } 756 757 if (optind > argc) { 758 bam_error(INT_ERROR, "parse_args"); 759 bam_exit(1); 760 } else if (optind < argc) { 761 bam_argv = &argv[optind]; 762 bam_argc = argc - optind; 763 } 764 765 /* 766 * -n implies verbose mode 767 */ 768 if (bam_check) 769 bam_verbose = 1; 770 } 771 772 static error_t 773 check_subcmd_and_options( 774 char *subcmd, 775 char *opt, 776 subcmd_defn_t *table, 777 error_t (**fp)()) 778 { 779 int i; 780 781 if (subcmd == NULL) { 782 bam_error(NEED_SUBCMD); 783 return (BAM_ERROR); 784 } 785 786 if (strcmp(subcmd, "set_option") == 0) { 787 if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) { 788 bam_error(MISSING_ARG); 789 usage(); 790 return (BAM_ERROR); 791 } else if (bam_argc > 1 || bam_argv[1] != NULL) { 792 bam_error(TRAILING_ARGS); 793 usage(); 794 return (BAM_ERROR); 795 } 796 } else if (strcmp(subcmd, "update_all") == 0) { 797 /* 798 * The only option we accept for the "update_all" 799 * subcmd is "fastboot". 800 */ 801 if (bam_argc > 1 || (bam_argc == 1 && 802 strcmp(bam_argv[0], "fastboot") != 0)) { 803 bam_error(TRAILING_ARGS); 804 usage(); 805 return (BAM_ERROR); 806 } 807 if (bam_argc == 1) 808 sync_menu = 0; 809 } else if (((strcmp(subcmd, "enable_hypervisor") != 0) && 810 (strcmp(subcmd, "list_setting") != 0)) && (bam_argc || bam_argv)) { 811 /* 812 * Of the remaining subcommands, only "enable_hypervisor" and 813 * "list_setting" take trailing arguments. 814 */ 815 bam_error(TRAILING_ARGS); 816 usage(); 817 return (BAM_ERROR); 818 } 819 820 if (bam_root == NULL) { 821 bam_root = rootbuf; 822 bam_rootlen = 1; 823 } 824 825 /* verify that subcmd is valid */ 826 for (i = 0; table[i].subcmd != NULL; i++) { 827 if (strcmp(table[i].subcmd, subcmd) == 0) 828 break; 829 } 830 831 if (table[i].subcmd == NULL) { 832 bam_error(INVALID_SUBCMD, subcmd); 833 return (BAM_ERROR); 834 } 835 836 if (table[i].unpriv == 0 && geteuid() != 0) { 837 bam_error(MUST_BE_ROOT); 838 return (BAM_ERROR); 839 } 840 841 /* 842 * Currently only privileged commands need a lock 843 */ 844 if (table[i].unpriv == 0) 845 bam_lock(); 846 847 /* subcmd verifies that opt is appropriate */ 848 if (table[i].option != OPT_OPTIONAL) { 849 if ((table[i].option == OPT_REQ) ^ (opt != NULL)) { 850 if (opt) 851 bam_error(NO_OPT_REQ, subcmd); 852 else 853 bam_error(MISS_OPT, subcmd); 854 return (BAM_ERROR); 855 } 856 } 857 858 *fp = table[i].handler; 859 860 return (BAM_SUCCESS); 861 } 862 863 /* 864 * NOTE: A single "/" is also considered a trailing slash and will 865 * be deleted. 866 */ 867 static void 868 elide_trailing_slash(const char *src, char *dst, size_t dstsize) 869 { 870 size_t dstlen; 871 872 assert(src); 873 assert(dst); 874 875 (void) strlcpy(dst, src, dstsize); 876 877 dstlen = strlen(dst); 878 if (dst[dstlen - 1] == '/') { 879 dst[dstlen - 1] = '\0'; 880 } 881 } 882 883 static int 884 is_safe_exec(char *path) 885 { 886 struct stat sb; 887 888 if (lstat(path, &sb) != 0) { 889 bam_error(STAT_FAIL, path, strerror(errno)); 890 return (BAM_ERROR); 891 } 892 893 if (!S_ISREG(sb.st_mode)) { 894 bam_error(PATH_EXEC_LINK, path); 895 return (BAM_ERROR); 896 } 897 898 if (sb.st_uid != getuid()) { 899 bam_error(PATH_EXEC_OWNER, path, getuid()); 900 return (BAM_ERROR); 901 } 902 903 if (sb.st_mode & S_IWOTH || sb.st_mode & S_IWGRP) { 904 bam_error(PATH_EXEC_PERMS, path); 905 return (BAM_ERROR); 906 } 907 908 return (BAM_SUCCESS); 909 } 910 911 static error_t 912 list_setting(menu_t *mp, char *which, char *setting) 913 { 914 line_t *lp; 915 entry_t *ent; 916 917 char *p = which; 918 int entry; 919 920 int found; 921 922 assert(which); 923 assert(setting); 924 925 if (*which != NULL) { 926 /* 927 * If "which" is not a number, assume it's a setting we want 928 * to look for and so set up the routine to look for "which" 929 * in the default entry. 930 */ 931 while (*p != NULL) 932 if (!(isdigit((int)*p++))) { 933 setting = which; 934 which = mp->curdefault->arg; 935 break; 936 } 937 } else { 938 which = mp->curdefault->arg; 939 } 940 941 entry = atoi(which); 942 943 for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != entry)); 944 ent = ent->next) 945 ; 946 947 if (!ent) { 948 bam_error(NO_MATCH_ENTRY); 949 return (BAM_ERROR); 950 } 951 952 found = (*setting == NULL); 953 954 for (lp = ent->start; lp != NULL; lp = lp->next) { 955 if ((*setting == NULL) && (lp->flags != BAM_COMMENT)) 956 bam_print(PRINT, lp->line); 957 else if (lp->cmd != NULL && strcmp(setting, lp->cmd) == 0) { 958 bam_print(PRINT, lp->arg); 959 found = 1; 960 } 961 962 if (lp == ent->end) 963 break; 964 } 965 966 if (!found) { 967 bam_error(NO_MATCH_ENTRY); 968 return (BAM_ERROR); 969 } 970 971 return (BAM_SUCCESS); 972 } 973 974 static error_t 975 bam_menu(char *subcmd, char *opt, int largc, char *largv[]) 976 { 977 error_t ret; 978 char menu_path[PATH_MAX]; 979 char clean_menu_root[PATH_MAX]; 980 char path[PATH_MAX]; 981 menu_t *menu; 982 char menu_root[PATH_MAX]; 983 struct stat sb; 984 error_t (*f)(menu_t *mp, char *menu_path, char *opt); 985 char *special; 986 char *pool = NULL; 987 zfs_mnted_t zmnted; 988 char *zmntpt; 989 char *osdev; 990 char *osroot; 991 const char *fcn = "bam_menu()"; 992 993 /* 994 * Menu sub-command only applies to GRUB (i.e. x86) 995 */ 996 if (!is_grub(bam_alt_root ? bam_root : "/")) { 997 bam_error(NOT_GRUB_BOOT); 998 return (BAM_ERROR); 999 } 1000 1001 /* 1002 * Check arguments 1003 */ 1004 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f); 1005 if (ret == BAM_ERROR) { 1006 return (BAM_ERROR); 1007 } 1008 1009 assert(bam_root); 1010 1011 (void) strlcpy(menu_root, bam_root, sizeof (menu_root)); 1012 osdev = osroot = NULL; 1013 1014 if (strcmp(subcmd, "update_entry") == 0) { 1015 assert(opt); 1016 1017 osdev = strtok(opt, ","); 1018 assert(osdev); 1019 osroot = strtok(NULL, ","); 1020 if (osroot) { 1021 /* fixup bam_root so that it points at osroot */ 1022 if (realpath(osroot, rootbuf) == NULL) { 1023 bam_error(CANT_RESOLVE, osroot, 1024 strerror(errno)); 1025 return (BAM_ERROR); 1026 } 1027 bam_alt_root = 1; 1028 bam_root = rootbuf; 1029 bam_rootlen = strlen(rootbuf); 1030 } 1031 } 1032 1033 /* 1034 * We support menu on PCFS (under certain conditions), but 1035 * not the OS root 1036 */ 1037 if (is_pcfs(bam_root)) { 1038 bam_error(PCFS_ROOT_NOTSUP, bam_root); 1039 return (BAM_ERROR); 1040 } 1041 1042 if (stat(menu_root, &sb) == -1) { 1043 bam_error(CANNOT_LOCATE_GRUB_MENU); 1044 return (BAM_ERROR); 1045 } 1046 1047 BAM_DPRINTF((D_MENU_ROOT, fcn, menu_root)); 1048 1049 /* 1050 * We no longer use the GRUB slice file. If it exists, then 1051 * the user is doing something that is unsupported (such as 1052 * standard upgrading an old Live Upgrade BE). If that 1053 * happens, mimic existing behavior i.e. pretend that it is 1054 * not a BE. Emit a warning though. 1055 */ 1056 if (bam_alt_root) { 1057 (void) snprintf(path, sizeof (path), "%s%s", bam_root, 1058 GRUB_slice); 1059 } else { 1060 (void) snprintf(path, sizeof (path), "%s", GRUB_slice); 1061 } 1062 1063 if (bam_verbose && stat(path, &sb) == 0) 1064 bam_error(GRUB_SLICE_FILE_EXISTS, path); 1065 1066 if (is_zfs(menu_root)) { 1067 assert(strcmp(menu_root, bam_root) == 0); 1068 special = get_special(menu_root); 1069 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL); 1070 if (special == NULL) { 1071 bam_error(CANT_FIND_SPECIAL, menu_root); 1072 return (BAM_ERROR); 1073 } 1074 pool = strtok(special, "/"); 1075 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL); 1076 if (pool == NULL) { 1077 free(special); 1078 bam_error(CANT_FIND_POOL, menu_root); 1079 return (BAM_ERROR); 1080 } 1081 BAM_DPRINTF((D_Z_MENU_GET_POOL_FROM_SPECIAL, fcn, pool)); 1082 1083 zmntpt = mount_top_dataset(pool, &zmnted); 1084 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL); 1085 if (zmntpt == NULL) { 1086 bam_error(CANT_MOUNT_POOL_DATASET, pool); 1087 free(special); 1088 return (BAM_ERROR); 1089 } 1090 BAM_DPRINTF((D_Z_GET_MENU_MOUNT_TOP_DATASET, fcn, zmntpt)); 1091 1092 (void) strlcpy(menu_root, zmntpt, sizeof (menu_root)); 1093 BAM_DPRINTF((D_Z_GET_MENU_MENU_ROOT, fcn, menu_root)); 1094 } 1095 1096 elide_trailing_slash(menu_root, clean_menu_root, 1097 sizeof (clean_menu_root)); 1098 1099 BAM_DPRINTF((D_CLEAN_MENU_ROOT, fcn, clean_menu_root)); 1100 1101 (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path)); 1102 (void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path)); 1103 1104 BAM_DPRINTF((D_MENU_PATH, fcn, menu_path)); 1105 1106 /* 1107 * If listing the menu, display the menu location 1108 */ 1109 if (strcmp(subcmd, "list_entry") == 0) 1110 bam_print(GRUB_MENU_PATH, menu_path); 1111 1112 if ((menu = menu_read(menu_path)) == NULL) { 1113 bam_error(CANNOT_LOCATE_GRUB_MENU_FILE, menu_path); 1114 if (special != NULL) 1115 free(special); 1116 1117 return (BAM_ERROR); 1118 } 1119 1120 /* 1121 * We already checked the following case in 1122 * check_subcmd_and_suboptions() above. Complete the 1123 * final step now. 1124 */ 1125 if (strcmp(subcmd, "set_option") == 0) { 1126 assert(largc == 1 && largv[0] && largv[1] == NULL); 1127 opt = largv[0]; 1128 } else if ((strcmp(subcmd, "enable_hypervisor") != 0) && 1129 (strcmp(subcmd, "list_setting") != 0)) { 1130 assert(largc == 0 && largv == NULL); 1131 } 1132 1133 ret = get_boot_cap(bam_root); 1134 if (ret != BAM_SUCCESS) { 1135 BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn)); 1136 goto out; 1137 } 1138 1139 /* 1140 * Once the sub-cmd handler has run 1141 * only the line field is guaranteed to have valid values 1142 */ 1143 if (strcmp(subcmd, "update_entry") == 0) { 1144 ret = f(menu, menu_root, osdev); 1145 } else if (strcmp(subcmd, "upgrade") == 0) { 1146 ret = f(menu, bam_root, menu_root); 1147 } else if (strcmp(subcmd, "list_entry") == 0) { 1148 ret = f(menu, menu_path, opt); 1149 } else if (strcmp(subcmd, "list_setting") == 0) { 1150 ret = f(menu, ((largc > 0) ? largv[0] : ""), 1151 ((largc > 1) ? largv[1] : "")); 1152 } else if (strcmp(subcmd, "disable_hypervisor") == 0) { 1153 if (is_sparc()) { 1154 bam_error(NO_SPARC, subcmd); 1155 ret = BAM_ERROR; 1156 } else { 1157 ret = f(menu, bam_root, NULL); 1158 } 1159 } else if (strcmp(subcmd, "enable_hypervisor") == 0) { 1160 if (is_sparc()) { 1161 bam_error(NO_SPARC, subcmd); 1162 ret = BAM_ERROR; 1163 } else { 1164 char *extra_args = NULL; 1165 1166 /* 1167 * Compress all arguments passed in the largv[] array 1168 * into one string that can then be appended to the 1169 * end of the kernel$ string the routine to enable the 1170 * hypervisor will build. 1171 * 1172 * This allows the caller to supply arbitrary unparsed 1173 * arguments, such as dom0 memory settings or APIC 1174 * options. 1175 * 1176 * This concatenation will be done without ANY syntax 1177 * checking whatsoever, so it's the responsibility of 1178 * the caller to make sure the arguments are valid and 1179 * do not duplicate arguments the conversion routines 1180 * may create. 1181 */ 1182 if (largc > 0) { 1183 int extra_len, i; 1184 1185 for (extra_len = 0, i = 0; i < largc; i++) 1186 extra_len += strlen(largv[i]); 1187 1188 /* 1189 * Allocate space for argument strings, 1190 * intervening spaces and terminating NULL. 1191 */ 1192 extra_args = alloca(extra_len + largc); 1193 1194 (void) strcpy(extra_args, largv[0]); 1195 1196 for (i = 1; i < largc; i++) { 1197 (void) strcat(extra_args, " "); 1198 (void) strcat(extra_args, largv[i]); 1199 } 1200 } 1201 1202 ret = f(menu, bam_root, extra_args); 1203 } 1204 } else 1205 ret = f(menu, NULL, opt); 1206 1207 if (ret == BAM_WRITE) { 1208 BAM_DPRINTF((D_WRITING_MENU_ROOT, fcn, clean_menu_root)); 1209 ret = menu_write(clean_menu_root, menu); 1210 } 1211 1212 out: 1213 INJECT_ERROR1("POOL_SET", pool = "/pooldata"); 1214 assert((is_zfs(menu_root)) ^ (pool == NULL)); 1215 if (pool) { 1216 (void) umount_top_dataset(pool, zmnted, zmntpt); 1217 free(special); 1218 } 1219 menu_free(menu); 1220 return (ret); 1221 } 1222 1223 1224 static error_t 1225 bam_archive( 1226 char *subcmd, 1227 char *opt) 1228 { 1229 error_t ret; 1230 error_t (*f)(char *root, char *opt); 1231 const char *fcn = "bam_archive()"; 1232 1233 /* 1234 * Add trailing / for archive subcommands 1235 */ 1236 if (rootbuf[strlen(rootbuf) - 1] != '/') 1237 (void) strcat(rootbuf, "/"); 1238 bam_rootlen = strlen(rootbuf); 1239 1240 /* 1241 * Check arguments 1242 */ 1243 ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f); 1244 if (ret != BAM_SUCCESS) { 1245 return (BAM_ERROR); 1246 } 1247 1248 ret = get_boot_cap(rootbuf); 1249 if (ret != BAM_SUCCESS) { 1250 BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn)); 1251 return (ret); 1252 } 1253 1254 /* 1255 * Check archive not supported with update_all 1256 * since it is awkward to display out-of-sync 1257 * information for each BE. 1258 */ 1259 if (bam_check && strcmp(subcmd, "update_all") == 0) { 1260 bam_error(CHECK_NOT_SUPPORTED, subcmd); 1261 return (BAM_ERROR); 1262 } 1263 1264 if (strcmp(subcmd, "update_all") == 0) 1265 bam_update_all = 1; 1266 1267 #if !defined(_OPB) 1268 ucode_install(bam_root); 1269 #endif 1270 1271 ret = f(bam_root, opt); 1272 1273 bam_update_all = 0; 1274 1275 return (ret); 1276 } 1277 1278 /*PRINTFLIKE1*/ 1279 void 1280 bam_error(char *format, ...) 1281 { 1282 va_list ap; 1283 1284 va_start(ap, format); 1285 (void) fprintf(stderr, "%s: ", prog); 1286 (void) vfprintf(stderr, format, ap); 1287 va_end(ap); 1288 } 1289 1290 /*PRINTFLIKE1*/ 1291 void 1292 bam_derror(char *format, ...) 1293 { 1294 va_list ap; 1295 1296 assert(bam_debug); 1297 1298 va_start(ap, format); 1299 (void) fprintf(stderr, "DEBUG: "); 1300 (void) vfprintf(stderr, format, ap); 1301 va_end(ap); 1302 } 1303 1304 /*PRINTFLIKE1*/ 1305 void 1306 bam_print(char *format, ...) 1307 { 1308 va_list ap; 1309 1310 va_start(ap, format); 1311 (void) vfprintf(stdout, format, ap); 1312 va_end(ap); 1313 } 1314 1315 /*PRINTFLIKE1*/ 1316 void 1317 bam_print_stderr(char *format, ...) 1318 { 1319 va_list ap; 1320 1321 va_start(ap, format); 1322 (void) vfprintf(stderr, format, ap); 1323 va_end(ap); 1324 } 1325 1326 void 1327 bam_exit(int excode) 1328 { 1329 restore_env(); 1330 bam_unlock(); 1331 exit(excode); 1332 } 1333 1334 static void 1335 bam_lock(void) 1336 { 1337 struct flock lock; 1338 pid_t pid; 1339 1340 bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS); 1341 if (bam_lock_fd < 0) { 1342 /* 1343 * We may be invoked early in boot for archive verification. 1344 * In this case, root is readonly and /var/run may not exist. 1345 * Proceed without the lock 1346 */ 1347 if (errno == EROFS || errno == ENOENT) { 1348 bam_root_readonly = 1; 1349 return; 1350 } 1351 1352 bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno)); 1353 bam_exit(1); 1354 } 1355 1356 lock.l_type = F_WRLCK; 1357 lock.l_whence = SEEK_SET; 1358 lock.l_start = 0; 1359 lock.l_len = 0; 1360 1361 if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) { 1362 if (errno != EACCES && errno != EAGAIN) { 1363 bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno)); 1364 (void) close(bam_lock_fd); 1365 bam_lock_fd = -1; 1366 bam_exit(1); 1367 } 1368 pid = 0; 1369 (void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0); 1370 bam_print(FILE_LOCKED, pid); 1371 1372 lock.l_type = F_WRLCK; 1373 lock.l_whence = SEEK_SET; 1374 lock.l_start = 0; 1375 lock.l_len = 0; 1376 if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) { 1377 bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno)); 1378 (void) close(bam_lock_fd); 1379 bam_lock_fd = -1; 1380 bam_exit(1); 1381 } 1382 } 1383 1384 /* We own the lock now */ 1385 pid = getpid(); 1386 (void) write(bam_lock_fd, &pid, sizeof (pid)); 1387 } 1388 1389 static void 1390 bam_unlock(void) 1391 { 1392 struct flock unlock; 1393 1394 /* 1395 * NOP if we don't hold the lock 1396 */ 1397 if (bam_lock_fd < 0) { 1398 return; 1399 } 1400 1401 unlock.l_type = F_UNLCK; 1402 unlock.l_whence = SEEK_SET; 1403 unlock.l_start = 0; 1404 unlock.l_len = 0; 1405 1406 if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) { 1407 bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno)); 1408 } 1409 1410 if (close(bam_lock_fd) == -1) { 1411 bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno)); 1412 } 1413 bam_lock_fd = -1; 1414 } 1415 1416 static error_t 1417 list_archive(char *root, char *opt) 1418 { 1419 filelist_t flist; 1420 filelist_t *flistp = &flist; 1421 line_t *lp; 1422 1423 assert(root); 1424 assert(opt == NULL); 1425 1426 flistp->head = flistp->tail = NULL; 1427 if (read_list(root, flistp) != BAM_SUCCESS) { 1428 return (BAM_ERROR); 1429 } 1430 assert(flistp->head && flistp->tail); 1431 1432 for (lp = flistp->head; lp; lp = lp->next) { 1433 bam_print(PRINT, lp->line); 1434 } 1435 1436 filelist_free(flistp); 1437 1438 return (BAM_SUCCESS); 1439 } 1440 1441 /* 1442 * This routine writes a list of lines to a file. 1443 * The list is *not* freed 1444 */ 1445 static error_t 1446 list2file(char *root, char *tmp, char *final, line_t *start) 1447 { 1448 char tmpfile[PATH_MAX]; 1449 char path[PATH_MAX]; 1450 FILE *fp; 1451 int ret; 1452 struct stat sb; 1453 mode_t mode; 1454 uid_t root_uid; 1455 gid_t sys_gid; 1456 struct passwd *pw; 1457 struct group *gp; 1458 const char *fcn = "list2file()"; 1459 1460 (void) snprintf(path, sizeof (path), "%s%s", root, final); 1461 1462 if (start == NULL) { 1463 /* Empty GRUB menu */ 1464 if (stat(path, &sb) != -1) { 1465 bam_print(UNLINK_EMPTY, path); 1466 if (unlink(path) != 0) { 1467 bam_error(UNLINK_FAIL, path, strerror(errno)); 1468 return (BAM_ERROR); 1469 } else { 1470 return (BAM_SUCCESS); 1471 } 1472 } 1473 return (BAM_SUCCESS); 1474 } 1475 1476 /* 1477 * Preserve attributes of existing file if possible, 1478 * otherwise ask the system for uid/gid of root/sys. 1479 * If all fails, fall back on hard-coded defaults. 1480 */ 1481 if (stat(path, &sb) != -1) { 1482 mode = sb.st_mode; 1483 root_uid = sb.st_uid; 1484 sys_gid = sb.st_gid; 1485 } else { 1486 mode = DEFAULT_DEV_MODE; 1487 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) { 1488 root_uid = pw->pw_uid; 1489 } else { 1490 bam_error(CANT_FIND_USER, 1491 DEFAULT_DEV_USER, DEFAULT_DEV_UID); 1492 root_uid = (uid_t)DEFAULT_DEV_UID; 1493 } 1494 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) { 1495 sys_gid = gp->gr_gid; 1496 } else { 1497 bam_error(CANT_FIND_GROUP, 1498 DEFAULT_DEV_GROUP, DEFAULT_DEV_GID); 1499 sys_gid = (gid_t)DEFAULT_DEV_GID; 1500 } 1501 } 1502 1503 (void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp); 1504 1505 /* Truncate tmpfile first */ 1506 fp = fopen(tmpfile, "w"); 1507 if (fp == NULL) { 1508 bam_error(OPEN_FAIL, tmpfile, strerror(errno)); 1509 return (BAM_ERROR); 1510 } 1511 ret = fclose(fp); 1512 INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret = EOF); 1513 if (ret == EOF) { 1514 bam_error(CLOSE_FAIL, tmpfile, strerror(errno)); 1515 return (BAM_ERROR); 1516 } 1517 1518 /* Now open it in append mode */ 1519 fp = fopen(tmpfile, "a"); 1520 if (fp == NULL) { 1521 bam_error(OPEN_FAIL, tmpfile, strerror(errno)); 1522 return (BAM_ERROR); 1523 } 1524 1525 for (; start; start = start->next) { 1526 ret = s_fputs(start->line, fp); 1527 INJECT_ERROR1("LIST2FILE_FPUTS", ret = EOF); 1528 if (ret == EOF) { 1529 bam_error(WRITE_FAIL, tmpfile, strerror(errno)); 1530 (void) fclose(fp); 1531 return (BAM_ERROR); 1532 } 1533 } 1534 1535 ret = fclose(fp); 1536 INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret = EOF); 1537 if (ret == EOF) { 1538 bam_error(CLOSE_FAIL, tmpfile, strerror(errno)); 1539 return (BAM_ERROR); 1540 } 1541 1542 /* 1543 * Set up desired attributes. Ignore failures on filesystems 1544 * not supporting these operations - pcfs reports unsupported 1545 * operations as EINVAL. 1546 */ 1547 ret = chmod(tmpfile, mode); 1548 if (ret == -1 && 1549 errno != EINVAL && errno != ENOTSUP) { 1550 bam_error(CHMOD_FAIL, tmpfile, strerror(errno)); 1551 return (BAM_ERROR); 1552 } 1553 1554 ret = chown(tmpfile, root_uid, sys_gid); 1555 if (ret == -1 && 1556 errno != EINVAL && errno != ENOTSUP) { 1557 bam_error(CHOWN_FAIL, tmpfile, strerror(errno)); 1558 return (BAM_ERROR); 1559 } 1560 1561 /* 1562 * Do an atomic rename 1563 */ 1564 ret = rename(tmpfile, path); 1565 INJECT_ERROR1("LIST2FILE_RENAME", ret = -1); 1566 if (ret != 0) { 1567 bam_error(RENAME_FAIL, path, strerror(errno)); 1568 return (BAM_ERROR); 1569 } 1570 1571 BAM_DPRINTF((D_WROTE_FILE, fcn, path)); 1572 return (BAM_SUCCESS); 1573 } 1574 1575 /* 1576 * Checks if the path specified (without the file name at the end) exists 1577 * and creates it if not. If the path exists and is not a directory, an attempt 1578 * to unlink is made. 1579 */ 1580 static int 1581 setup_path(char *path) 1582 { 1583 char *p; 1584 int ret; 1585 struct stat sb; 1586 1587 p = strrchr(path, '/'); 1588 if (p != NULL) { 1589 *p = '\0'; 1590 if (stat(path, &sb) != 0 || !(S_ISDIR(sb.st_mode))) { 1591 /* best effort attempt, mkdirp will catch the error */ 1592 (void) unlink(path); 1593 if (bam_verbose) 1594 bam_print(NEED_DIRPATH, path); 1595 ret = mkdirp(path, DIR_PERMS); 1596 if (ret == -1) { 1597 bam_error(MKDIR_FAILED, path, strerror(errno)); 1598 *p = '/'; 1599 return (BAM_ERROR); 1600 } 1601 } 1602 *p = '/'; 1603 return (BAM_SUCCESS); 1604 } 1605 return (BAM_SUCCESS); 1606 } 1607 1608 typedef union { 1609 gzFile gzfile; 1610 int fdfile; 1611 } outfile; 1612 1613 typedef struct { 1614 char path[PATH_MAX]; 1615 outfile out; 1616 } cachefile; 1617 1618 static int 1619 setup_file(char *base, const char *path, cachefile *cf) 1620 { 1621 int ret; 1622 char *strip; 1623 1624 /* init gzfile or fdfile in case we fail before opening */ 1625 if (bam_direct == BAM_DIRECT_DBOOT) 1626 cf->out.gzfile = NULL; 1627 else 1628 cf->out.fdfile = -1; 1629 1630 /* strip the trailing altroot path */ 1631 strip = (char *)path + strlen(rootbuf); 1632 1633 ret = snprintf(cf->path, sizeof (cf->path), "%s/%s", base, strip); 1634 if (ret >= sizeof (cf->path)) { 1635 bam_error(PATH_TOO_LONG, rootbuf); 1636 return (BAM_ERROR); 1637 } 1638 1639 /* Check if path is present in the archive cache directory */ 1640 if (setup_path(cf->path) == BAM_ERROR) 1641 return (BAM_ERROR); 1642 1643 if (bam_direct == BAM_DIRECT_DBOOT) { 1644 if ((cf->out.gzfile = gzopen(cf->path, "wb")) == NULL) { 1645 bam_error(OPEN_FAIL, cf->path, strerror(errno)); 1646 return (BAM_ERROR); 1647 } 1648 (void) gzsetparams(cf->out.gzfile, Z_BEST_SPEED, 1649 Z_DEFAULT_STRATEGY); 1650 } else { 1651 if ((cf->out.fdfile = open(cf->path, O_WRONLY | O_CREAT, 0644)) 1652 == -1) { 1653 bam_error(OPEN_FAIL, cf->path, strerror(errno)); 1654 return (BAM_ERROR); 1655 } 1656 } 1657 1658 return (BAM_SUCCESS); 1659 } 1660 1661 static int 1662 cache_write(cachefile cf, char *buf, int size) 1663 { 1664 int err; 1665 1666 if (bam_direct == BAM_DIRECT_DBOOT) { 1667 if (gzwrite(cf.out.gzfile, buf, size) < 1) { 1668 bam_error(GZ_WRITE_FAIL, gzerror(cf.out.gzfile, &err)); 1669 if (err == Z_ERRNO && bam_verbose) { 1670 bam_error(WRITE_FAIL, cf.path, strerror(errno)); 1671 } 1672 return (BAM_ERROR); 1673 } 1674 } else { 1675 if (write(cf.out.fdfile, buf, size) < 1) { 1676 bam_error(WRITE_FAIL, cf.path, strerror(errno)); 1677 return (BAM_ERROR); 1678 } 1679 } 1680 return (BAM_SUCCESS); 1681 } 1682 1683 static int 1684 cache_close(cachefile cf) 1685 { 1686 int ret; 1687 1688 if (bam_direct == BAM_DIRECT_DBOOT) { 1689 if (cf.out.gzfile) { 1690 ret = gzclose(cf.out.gzfile); 1691 if (ret != Z_OK) { 1692 bam_error(CLOSE_FAIL, cf.path, strerror(errno)); 1693 return (BAM_ERROR); 1694 } 1695 } 1696 } else { 1697 if (cf.out.fdfile != -1) { 1698 ret = close(cf.out.fdfile); 1699 if (ret != 0) { 1700 bam_error(CLOSE_FAIL, cf.path, strerror(errno)); 1701 return (BAM_ERROR); 1702 } 1703 } 1704 } 1705 1706 return (BAM_SUCCESS); 1707 } 1708 1709 static int 1710 dircache_updatefile(const char *path, int what) 1711 { 1712 int ret, exitcode; 1713 char buf[4096 * 4]; 1714 FILE *infile; 1715 cachefile outfile, outupdt; 1716 1717 if (bam_nowrite()) { 1718 set_dir_flag(what, NEED_UPDATE); 1719 return (BAM_SUCCESS); 1720 } 1721 1722 if (!has_cachedir(what)) 1723 return (BAM_SUCCESS); 1724 1725 if ((infile = fopen(path, "rb")) == NULL) { 1726 bam_error(OPEN_FAIL, path, strerror(errno)); 1727 return (BAM_ERROR); 1728 } 1729 1730 ret = setup_file(get_cachedir(what), path, &outfile); 1731 if (ret == BAM_ERROR) { 1732 exitcode = BAM_ERROR; 1733 goto out; 1734 } 1735 if (!is_dir_flag_on(what, NO_MULTI)) { 1736 ret = setup_file(get_updatedir(what), path, &outupdt); 1737 if (ret == BAM_ERROR) 1738 set_dir_flag(what, NO_MULTI); 1739 } 1740 1741 while ((ret = fread(buf, 1, sizeof (buf), infile)) > 0) { 1742 if (cache_write(outfile, buf, ret) == BAM_ERROR) { 1743 exitcode = BAM_ERROR; 1744 goto out; 1745 } 1746 if (!is_dir_flag_on(what, NO_MULTI)) 1747 if (cache_write(outupdt, buf, ret) == BAM_ERROR) 1748 set_dir_flag(what, NO_MULTI); 1749 } 1750 1751 set_dir_flag(what, NEED_UPDATE); 1752 get_count(what)++; 1753 if (get_count(what) > COUNT_MAX) 1754 set_dir_flag(what, NO_MULTI); 1755 exitcode = BAM_SUCCESS; 1756 out: 1757 (void) fclose(infile); 1758 if (cache_close(outfile) == BAM_ERROR) 1759 exitcode = BAM_ERROR; 1760 if (!is_dir_flag_on(what, NO_MULTI) && 1761 cache_close(outupdt) == BAM_ERROR) 1762 exitcode = BAM_ERROR; 1763 if (exitcode == BAM_ERROR) 1764 set_flag(UPDATE_ERROR); 1765 return (exitcode); 1766 } 1767 1768 static int 1769 dircache_updatedir(const char *path, int what, int updt) 1770 { 1771 int ret; 1772 char dpath[PATH_MAX]; 1773 char *strip; 1774 struct stat sb; 1775 1776 strip = (char *)path + strlen(rootbuf); 1777 1778 ret = snprintf(dpath, sizeof (dpath), "%s/%s", updt ? 1779 get_updatedir(what) : get_cachedir(what), strip); 1780 1781 if (ret >= sizeof (dpath)) { 1782 bam_error(PATH_TOO_LONG, rootbuf); 1783 set_flag(UPDATE_ERROR); 1784 return (BAM_ERROR); 1785 } 1786 1787 if (stat(dpath, &sb) == 0 && S_ISDIR(sb.st_mode)) 1788 return (BAM_SUCCESS); 1789 1790 if (updt) { 1791 if (!is_dir_flag_on(what, NO_MULTI)) 1792 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) 1793 set_dir_flag(what, NO_MULTI); 1794 } else { 1795 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) { 1796 set_flag(UPDATE_ERROR); 1797 return (BAM_ERROR); 1798 } 1799 } 1800 1801 set_dir_flag(what, NEED_UPDATE); 1802 return (BAM_SUCCESS); 1803 } 1804 1805 #define DO_CACHE_DIR 0 1806 #define DO_UPDATE_DIR 1 1807 1808 #if defined(_LP64) || defined(_LONGLONG_TYPE) 1809 typedef Elf64_Ehdr _elfhdr; 1810 #else 1811 typedef Elf32_Ehdr _elfhdr; 1812 #endif 1813 1814 /* 1815 * This routine updates the contents of the cache directory 1816 */ 1817 static int 1818 update_dircache(const char *path, int flags) 1819 { 1820 int rc = BAM_SUCCESS; 1821 1822 switch (flags) { 1823 case FTW_F: 1824 { 1825 int fd; 1826 _elfhdr elf; 1827 1828 if ((fd = open(path, O_RDONLY)) < 0) { 1829 bam_error(OPEN_FAIL, path, strerror(errno)); 1830 set_flag(UPDATE_ERROR); 1831 rc = BAM_ERROR; 1832 break; 1833 } 1834 1835 /* 1836 * libelf and gelf would be a cleaner and easier way to handle 1837 * this, but libelf fails compilation if _ILP32 is defined && 1838 * _FILE_OFFSET_BITS is != 32 ... 1839 */ 1840 if (read(fd, (void *)&elf, sizeof (_elfhdr)) < 0) { 1841 bam_error(READ_FAIL, path, strerror(errno)); 1842 set_flag(UPDATE_ERROR); 1843 (void) close(fd); 1844 rc = BAM_ERROR; 1845 break; 1846 } 1847 (void) close(fd); 1848 1849 /* 1850 * If the file is not an executable and is not inside an amd64 1851 * directory, we copy it in both the cache directories, 1852 * otherwise, we only copy it inside the 64-bit one. 1853 */ 1854 if (memcmp(elf.e_ident, ELFMAG, 4) != 0) { 1855 if (strstr(path, "/amd64")) { 1856 rc = dircache_updatefile(path, FILE64); 1857 } else { 1858 rc = dircache_updatefile(path, FILE32); 1859 if (rc == BAM_SUCCESS) 1860 rc = dircache_updatefile(path, FILE64); 1861 } 1862 } else { 1863 /* 1864 * Based on the ELF class we copy the file in the 32-bit 1865 * or the 64-bit cache directory. 1866 */ 1867 if (elf.e_ident[EI_CLASS] == ELFCLASS32) { 1868 rc = dircache_updatefile(path, FILE32); 1869 } else if (elf.e_ident[EI_CLASS] == ELFCLASS64) { 1870 rc = dircache_updatefile(path, FILE64); 1871 } else { 1872 bam_print(NO3264ELF, path); 1873 /* paranoid */ 1874 rc = dircache_updatefile(path, FILE32); 1875 if (rc == BAM_SUCCESS) 1876 rc = dircache_updatefile(path, FILE64); 1877 } 1878 } 1879 break; 1880 } 1881 case FTW_D: 1882 if (strstr(path, "/amd64") == NULL) { 1883 rc = dircache_updatedir(path, FILE32, DO_UPDATE_DIR); 1884 if (rc == BAM_SUCCESS) 1885 rc = dircache_updatedir(path, FILE32, 1886 DO_CACHE_DIR); 1887 } else { 1888 if (has_cachedir(FILE64)) { 1889 rc = dircache_updatedir(path, FILE64, 1890 DO_UPDATE_DIR); 1891 if (rc == BAM_SUCCESS) 1892 rc = dircache_updatedir(path, FILE64, 1893 DO_CACHE_DIR); 1894 } 1895 } 1896 break; 1897 default: 1898 rc = BAM_ERROR; 1899 break; 1900 } 1901 1902 return (rc); 1903 } 1904 1905 /*ARGSUSED*/ 1906 static int 1907 cmpstat( 1908 const char *file, 1909 const struct stat *st, 1910 int flags, 1911 struct FTW *ftw) 1912 { 1913 uint_t sz; 1914 uint64_t *value; 1915 uint64_t filestat[2]; 1916 int error, ret, status; 1917 1918 struct safefile *safefilep; 1919 FILE *fp; 1920 struct stat sb; 1921 regex_t re; 1922 1923 /* 1924 * On SPARC we create/update links too. 1925 */ 1926 if (flags != FTW_F && flags != FTW_D && (flags == FTW_SL && 1927 !is_flag_on(IS_SPARC_TARGET))) 1928 return (0); 1929 1930 /* 1931 * Ignore broken links 1932 */ 1933 if (flags == FTW_SL && stat(file, &sb) < 0) 1934 return (0); 1935 1936 /* 1937 * new_nvlp may be NULL if there were errors earlier 1938 * but this is not fatal to update determination. 1939 */ 1940 if (walk_arg.new_nvlp) { 1941 filestat[0] = st->st_size; 1942 filestat[1] = st->st_mtime; 1943 error = nvlist_add_uint64_array(walk_arg.new_nvlp, 1944 file + bam_rootlen, filestat, 2); 1945 if (error) 1946 bam_error(NVADD_FAIL, file, strerror(error)); 1947 } 1948 1949 /* 1950 * If we are invoked as part of system/filesystem/boot-archive, then 1951 * there are a number of things we should not worry about 1952 */ 1953 if (bam_smf_check) { 1954 /* ignore amd64 modules unless we are booted amd64. */ 1955 if (!is_amd64() && strstr(file, "/amd64/") != 0) 1956 return (0); 1957 1958 /* read in list of safe files */ 1959 if (safefiles == NULL) 1960 if (fp = fopen("/boot/solaris/filelist.safe", "r")) { 1961 safefiles = s_calloc(1, 1962 sizeof (struct safefile)); 1963 safefilep = safefiles; 1964 safefilep->name = s_calloc(1, MAXPATHLEN + 1965 MAXNAMELEN); 1966 safefilep->next = NULL; 1967 while (s_fgets(safefilep->name, MAXPATHLEN + 1968 MAXNAMELEN, fp) != NULL) { 1969 safefilep->next = s_calloc(1, 1970 sizeof (struct safefile)); 1971 safefilep = safefilep->next; 1972 safefilep->name = s_calloc(1, 1973 MAXPATHLEN + MAXNAMELEN); 1974 safefilep->next = NULL; 1975 } 1976 (void) fclose(fp); 1977 } 1978 } 1979 1980 /* 1981 * On SPARC we create a -path-list file for mkisofs 1982 */ 1983 if (is_flag_on(IS_SPARC_TARGET) && !bam_nowrite()) { 1984 if (flags != FTW_D) { 1985 char *strip; 1986 1987 strip = (char *)file + strlen(rootbuf); 1988 (void) fprintf(walk_arg.sparcfile, "/%s=%s\n", strip, 1989 file); 1990 } 1991 } 1992 1993 /* 1994 * We are transitioning from the old model to the dircache or the cache 1995 * directory was removed: create the entry without further checkings. 1996 */ 1997 if (is_flag_on(NEED_CACHE_DIR)) { 1998 if (bam_verbose) 1999 bam_print(PARSEABLE_NEW_FILE, file); 2000 2001 if (is_flag_on(IS_SPARC_TARGET)) { 2002 set_dir_flag(FILE64, NEED_UPDATE); 2003 return (0); 2004 } 2005 2006 ret = update_dircache(file, flags); 2007 if (ret == BAM_ERROR) { 2008 bam_error(UPDT_CACHE_FAIL, file); 2009 return (-1); 2010 } 2011 2012 return (0); 2013 } 2014 2015 /* 2016 * We need an update if file doesn't exist in old archive 2017 */ 2018 if (walk_arg.old_nvlp == NULL || 2019 nvlist_lookup_uint64_array(walk_arg.old_nvlp, 2020 file + bam_rootlen, &value, &sz) != 0) { 2021 if (bam_smf_check) /* ignore new during smf check */ 2022 return (0); 2023 2024 if (is_flag_on(IS_SPARC_TARGET)) { 2025 set_dir_flag(FILE64, NEED_UPDATE); 2026 } else { 2027 ret = update_dircache(file, flags); 2028 if (ret == BAM_ERROR) { 2029 bam_error(UPDT_CACHE_FAIL, file); 2030 return (-1); 2031 } 2032 } 2033 2034 if (bam_verbose) 2035 bam_print(PARSEABLE_NEW_FILE, file); 2036 return (0); 2037 } 2038 2039 /* 2040 * If we got there, the file is already listed as to be included in the 2041 * iso image. We just need to know if we are going to rebuild it or not 2042 */ 2043 if (is_flag_on(IS_SPARC_TARGET) && 2044 is_dir_flag_on(FILE64, NEED_UPDATE) && !bam_nowrite()) 2045 return (0); 2046 /* 2047 * File exists in old archive. Check if file has changed 2048 */ 2049 assert(sz == 2); 2050 bcopy(value, filestat, sizeof (filestat)); 2051 2052 if (flags != FTW_D && (filestat[0] != st->st_size || 2053 filestat[1] != st->st_mtime)) { 2054 if (bam_smf_check) { 2055 safefilep = safefiles; 2056 while (safefilep != NULL && 2057 safefilep->name[0] != '\0') { 2058 if (regcomp(&re, safefilep->name, 2059 REG_EXTENDED|REG_NOSUB) == 0) { 2060 status = regexec(&re, 2061 file + bam_rootlen, 0, NULL, 0); 2062 regfree(&re); 2063 if (status == 0) { 2064 (void) creat(NEED_UPDATE_FILE, 2065 0644); 2066 return (0); 2067 } 2068 } 2069 safefilep = safefilep->next; 2070 } 2071 } 2072 2073 if (is_flag_on(IS_SPARC_TARGET)) { 2074 set_dir_flag(FILE64, NEED_UPDATE); 2075 } else { 2076 ret = update_dircache(file, flags); 2077 if (ret == BAM_ERROR) { 2078 bam_error(UPDT_CACHE_FAIL, file); 2079 return (-1); 2080 } 2081 } 2082 2083 if (bam_verbose) 2084 if (bam_smf_check) 2085 bam_print(" %s\n", file); 2086 else 2087 bam_print(PARSEABLE_OUT_DATE, file); 2088 } 2089 2090 return (0); 2091 } 2092 2093 /* 2094 * Remove a directory path recursively 2095 */ 2096 static int 2097 rmdir_r(char *path) 2098 { 2099 struct dirent *d = NULL; 2100 DIR *dir = NULL; 2101 char tpath[PATH_MAX]; 2102 struct stat sb; 2103 2104 if ((dir = opendir(path)) == NULL) 2105 return (-1); 2106 2107 while (d = readdir(dir)) { 2108 if ((strcmp(d->d_name, ".") != 0) && 2109 (strcmp(d->d_name, "..") != 0)) { 2110 (void) snprintf(tpath, sizeof (tpath), "%s/%s", 2111 path, d->d_name); 2112 if (stat(tpath, &sb) == 0) { 2113 if (sb.st_mode & S_IFDIR) 2114 (void) rmdir_r(tpath); 2115 else 2116 (void) remove(tpath); 2117 } 2118 } 2119 } 2120 return (remove(path)); 2121 } 2122 2123 /* 2124 * Check if cache directory exists and, if not, create it and update flags 2125 * accordingly. If the path exists, but it's not a directory, a best effort 2126 * attempt to remove and recreate it is made. 2127 * If the user requested a 'purge', always recreate the directory from scratch. 2128 */ 2129 static int 2130 set_cache_dir(char *root, int what) 2131 { 2132 struct stat sb; 2133 int ret = 0; 2134 2135 ret = snprintf(get_cachedir(what), sizeof (get_cachedir(what)), 2136 "%s%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(), what == FILE64 ? 2137 "/amd64" : "", CACHEDIR_SUFFIX); 2138 2139 if (ret >= sizeof (get_cachedir(what))) { 2140 bam_error(PATH_TOO_LONG, rootbuf); 2141 return (BAM_ERROR); 2142 } 2143 2144 if (bam_purge || is_flag_on(INVALIDATE_CACHE)) 2145 (void) rmdir_r(get_cachedir(what)); 2146 2147 if (stat(get_cachedir(what), &sb) != 0 || !(S_ISDIR(sb.st_mode))) { 2148 /* best effort unlink attempt, mkdir will catch errors */ 2149 (void) unlink(get_cachedir(what)); 2150 2151 if (bam_verbose) 2152 bam_print(UPDATE_CDIR_MISS, get_cachedir(what)); 2153 ret = mkdir(get_cachedir(what), DIR_PERMS); 2154 if (ret < 0) { 2155 bam_error(MKDIR_FAILED, get_cachedir(what), 2156 strerror(errno)); 2157 get_cachedir(what)[0] = '\0'; 2158 return (ret); 2159 } 2160 set_flag(NEED_CACHE_DIR); 2161 set_dir_flag(what, NO_MULTI); 2162 } 2163 2164 return (BAM_SUCCESS); 2165 } 2166 2167 static int 2168 set_update_dir(char *root, int what) 2169 { 2170 struct stat sb; 2171 int ret; 2172 2173 if (is_dir_flag_on(what, NO_MULTI)) 2174 return (BAM_SUCCESS); 2175 2176 if (!bam_extend) { 2177 set_dir_flag(what, NO_MULTI); 2178 return (BAM_SUCCESS); 2179 } 2180 2181 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET)) 2182 ret = snprintf(get_updatedir(what), 2183 sizeof (get_updatedir(what)), "%s%s%s/amd64%s", root, 2184 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX); 2185 else 2186 ret = snprintf(get_updatedir(what), 2187 sizeof (get_updatedir(what)), "%s%s%s%s", root, 2188 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX); 2189 2190 if (ret >= sizeof (get_updatedir(what))) { 2191 bam_error(PATH_TOO_LONG, rootbuf); 2192 return (BAM_ERROR); 2193 } 2194 2195 if (stat(get_updatedir(what), &sb) == 0) { 2196 if (S_ISDIR(sb.st_mode)) 2197 ret = rmdir_r(get_updatedir(what)); 2198 else 2199 ret = unlink(get_updatedir(what)); 2200 2201 if (ret != 0) 2202 set_dir_flag(what, NO_MULTI); 2203 } 2204 2205 if (mkdir(get_updatedir(what), DIR_PERMS) < 0) 2206 set_dir_flag(what, NO_MULTI); 2207 2208 return (BAM_SUCCESS); 2209 } 2210 2211 static int 2212 is_valid_archive(char *root, int what) 2213 { 2214 char archive_path[PATH_MAX]; 2215 char timestamp_path[PATH_MAX]; 2216 struct stat sb, timestamp; 2217 int ret; 2218 2219 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET)) 2220 ret = snprintf(archive_path, sizeof (archive_path), 2221 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(), 2222 ARCHIVE_SUFFIX); 2223 else 2224 ret = snprintf(archive_path, sizeof (archive_path), "%s%s%s%s", 2225 root, ARCHIVE_PREFIX, get_machine(), ARCHIVE_SUFFIX); 2226 2227 if (ret >= sizeof (archive_path)) { 2228 bam_error(PATH_TOO_LONG, rootbuf); 2229 return (BAM_ERROR); 2230 } 2231 2232 if (stat(archive_path, &sb) != 0) { 2233 if (bam_verbose && !bam_check) 2234 bam_print(UPDATE_ARCH_MISS, archive_path); 2235 set_dir_flag(what, NEED_UPDATE); 2236 set_dir_flag(what, NO_MULTI); 2237 return (BAM_SUCCESS); 2238 } 2239 2240 /* 2241 * The timestamp file is used to prevent stale files in the archive 2242 * cache. 2243 * Stale files can happen if the system is booted back and forth across 2244 * the transition from bootadm-before-the-cache to 2245 * bootadm-after-the-cache, since older versions of bootadm don't know 2246 * about the existence of the archive cache. 2247 * 2248 * Since only bootadm-after-the-cache versions know about about this 2249 * file, we require that the boot archive be older than this file. 2250 */ 2251 ret = snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root, 2252 FILE_STAT_TIMESTAMP); 2253 2254 if (ret >= sizeof (timestamp_path)) { 2255 bam_error(PATH_TOO_LONG, rootbuf); 2256 return (BAM_ERROR); 2257 } 2258 2259 if (stat(timestamp_path, ×tamp) != 0 || 2260 sb.st_mtime > timestamp.st_mtime) { 2261 if (bam_verbose && !bam_check) 2262 bam_print(UPDATE_CACHE_OLD, timestamp); 2263 /* 2264 * Don't generate a false positive for the boot-archive service 2265 * but trigger an update of the archive cache in 2266 * boot-archive-update. 2267 */ 2268 if (bam_smf_check) { 2269 (void) creat(NEED_UPDATE_FILE, 0644); 2270 return (BAM_SUCCESS); 2271 } 2272 2273 set_flag(INVALIDATE_CACHE); 2274 set_dir_flag(what, NEED_UPDATE); 2275 set_dir_flag(what, NO_MULTI); 2276 return (BAM_SUCCESS); 2277 } 2278 2279 if (is_flag_on(IS_SPARC_TARGET)) 2280 return (BAM_SUCCESS); 2281 2282 if (bam_extend && sb.st_size > BA_SIZE_MAX) { 2283 if (bam_verbose && !bam_check) 2284 bam_print(MULTI_SIZE, archive_path, BA_SIZE_MAX); 2285 set_dir_flag(what, NO_MULTI); 2286 } 2287 2288 return (BAM_SUCCESS); 2289 } 2290 2291 /* 2292 * Check flags and presence of required files and directories. 2293 * The force flag and/or absence of files should 2294 * trigger an update. 2295 * Suppress stdout output if check (-n) option is set 2296 * (as -n should only produce parseable output.) 2297 */ 2298 static int 2299 check_flags_and_files(char *root) 2300 { 2301 2302 struct stat sb; 2303 int ret; 2304 2305 /* 2306 * If archive is missing, create archive 2307 */ 2308 if (is_flag_on(IS_SPARC_TARGET)) { 2309 ret = is_valid_archive(root, FILE64); 2310 if (ret == BAM_ERROR) 2311 return (BAM_ERROR); 2312 } else { 2313 int what = FILE32; 2314 do { 2315 ret = is_valid_archive(root, what); 2316 if (ret == BAM_ERROR) 2317 return (BAM_ERROR); 2318 what++; 2319 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM); 2320 } 2321 2322 if (bam_nowrite()) 2323 return (BAM_SUCCESS); 2324 2325 2326 /* 2327 * check if cache directories exist on x86. 2328 * check (and always open) the cache file on SPARC. 2329 */ 2330 if (is_sparc()) { 2331 ret = snprintf(get_cachedir(FILE64), 2332 sizeof (get_cachedir(FILE64)), "%s%s%s/%s", root, 2333 ARCHIVE_PREFIX, get_machine(), CACHEDIR_SUFFIX); 2334 2335 if (ret >= sizeof (get_cachedir(FILE64))) { 2336 bam_error(PATH_TOO_LONG, rootbuf); 2337 return (BAM_ERROR); 2338 } 2339 2340 if (stat(get_cachedir(FILE64), &sb) != 0) { 2341 set_flag(NEED_CACHE_DIR); 2342 set_dir_flag(FILE64, NEED_UPDATE); 2343 } 2344 2345 walk_arg.sparcfile = fopen(get_cachedir(FILE64), "w"); 2346 if (walk_arg.sparcfile == NULL) { 2347 bam_error(OPEN_FAIL, get_cachedir(FILE64), 2348 strerror(errno)); 2349 return (BAM_ERROR); 2350 } 2351 2352 set_dir_present(FILE64); 2353 } else { 2354 int what = FILE32; 2355 2356 do { 2357 if (set_cache_dir(root, what) != 0) 2358 return (BAM_ERROR); 2359 2360 set_dir_present(what); 2361 2362 if (set_update_dir(root, what) != 0) 2363 return (BAM_ERROR); 2364 what++; 2365 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM); 2366 } 2367 2368 /* 2369 * if force, create archive unconditionally 2370 */ 2371 if (bam_force) { 2372 if (!is_sparc()) 2373 set_dir_flag(FILE32, NEED_UPDATE); 2374 set_dir_flag(FILE64, NEED_UPDATE); 2375 if (bam_verbose) 2376 bam_print(UPDATE_FORCE); 2377 return (BAM_SUCCESS); 2378 } 2379 2380 return (BAM_SUCCESS); 2381 } 2382 2383 static error_t 2384 read_one_list(char *root, filelist_t *flistp, char *filelist) 2385 { 2386 char path[PATH_MAX]; 2387 FILE *fp; 2388 char buf[BAM_MAXLINE]; 2389 const char *fcn = "read_one_list()"; 2390 2391 (void) snprintf(path, sizeof (path), "%s%s", root, filelist); 2392 2393 fp = fopen(path, "r"); 2394 if (fp == NULL) { 2395 BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno))); 2396 return (BAM_ERROR); 2397 } 2398 while (s_fgets(buf, sizeof (buf), fp) != NULL) { 2399 /* skip blank lines */ 2400 if (strspn(buf, " \t") == strlen(buf)) 2401 continue; 2402 append_to_flist(flistp, buf); 2403 } 2404 if (fclose(fp) != 0) { 2405 bam_error(CLOSE_FAIL, path, strerror(errno)); 2406 return (BAM_ERROR); 2407 } 2408 return (BAM_SUCCESS); 2409 } 2410 2411 static error_t 2412 read_list(char *root, filelist_t *flistp) 2413 { 2414 char path[PATH_MAX]; 2415 char cmd[PATH_MAX]; 2416 struct stat sb; 2417 int n, rval; 2418 const char *fcn = "read_list()"; 2419 2420 flistp->head = flistp->tail = NULL; 2421 2422 /* 2423 * build and check path to extract_boot_filelist.ksh 2424 */ 2425 n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST); 2426 if (n >= sizeof (path)) { 2427 bam_error(NO_FLIST); 2428 return (BAM_ERROR); 2429 } 2430 2431 if (is_safe_exec(path) == BAM_ERROR) 2432 return (BAM_ERROR); 2433 2434 /* 2435 * If extract_boot_filelist is present, exec it, otherwise read 2436 * the filelists directly, for compatibility with older images. 2437 */ 2438 if (stat(path, &sb) == 0) { 2439 /* 2440 * build arguments to exec extract_boot_filelist.ksh 2441 */ 2442 char *rootarg, *platarg; 2443 int platarglen = 1, rootarglen = 1; 2444 if (strlen(root) > 1) 2445 rootarglen += strlen(root) + strlen("-R "); 2446 if (bam_alt_platform) 2447 platarglen += strlen(bam_platform) + strlen("-p "); 2448 platarg = s_calloc(1, platarglen); 2449 rootarg = s_calloc(1, rootarglen); 2450 *platarg = 0; 2451 *rootarg = 0; 2452 2453 if (strlen(root) > 1) { 2454 (void) snprintf(rootarg, rootarglen, 2455 "-R %s", root); 2456 } 2457 if (bam_alt_platform) { 2458 (void) snprintf(platarg, platarglen, 2459 "-p %s", bam_platform); 2460 } 2461 n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s", 2462 path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST); 2463 free(platarg); 2464 free(rootarg); 2465 if (n >= sizeof (cmd)) { 2466 bam_error(NO_FLIST); 2467 return (BAM_ERROR); 2468 } 2469 if (exec_cmd(cmd, flistp) != 0) { 2470 BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno))); 2471 return (BAM_ERROR); 2472 } 2473 } else { 2474 /* 2475 * Read current lists of files - only the first is mandatory 2476 */ 2477 rval = read_one_list(root, flistp, BOOT_FILE_LIST); 2478 if (rval != BAM_SUCCESS) 2479 return (rval); 2480 (void) read_one_list(root, flistp, ETC_FILE_LIST); 2481 } 2482 2483 if (flistp->head == NULL) { 2484 bam_error(NO_FLIST); 2485 return (BAM_ERROR); 2486 } 2487 2488 return (BAM_SUCCESS); 2489 } 2490 2491 static void 2492 getoldstat(char *root) 2493 { 2494 char path[PATH_MAX]; 2495 int fd, error; 2496 struct stat sb; 2497 char *ostat; 2498 2499 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT); 2500 fd = open(path, O_RDONLY); 2501 if (fd == -1) { 2502 if (bam_verbose) 2503 bam_print(OPEN_FAIL, path, strerror(errno)); 2504 goto out_err; 2505 } 2506 2507 if (fstat(fd, &sb) != 0) { 2508 bam_error(STAT_FAIL, path, strerror(errno)); 2509 goto out_err; 2510 } 2511 2512 ostat = s_calloc(1, sb.st_size); 2513 2514 if (read(fd, ostat, sb.st_size) != sb.st_size) { 2515 bam_error(READ_FAIL, path, strerror(errno)); 2516 free(ostat); 2517 goto out_err; 2518 } 2519 2520 (void) close(fd); 2521 fd = -1; 2522 2523 walk_arg.old_nvlp = NULL; 2524 error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0); 2525 2526 free(ostat); 2527 2528 if (error) { 2529 bam_error(UNPACK_FAIL, path, strerror(error)); 2530 walk_arg.old_nvlp = NULL; 2531 goto out_err; 2532 } else { 2533 return; 2534 } 2535 2536 out_err: 2537 if (fd != -1) 2538 (void) close(fd); 2539 if (!is_flag_on(IS_SPARC_TARGET)) 2540 set_dir_flag(FILE32, NEED_UPDATE); 2541 set_dir_flag(FILE64, NEED_UPDATE); 2542 } 2543 2544 /* Best effort stale entry removal */ 2545 static void 2546 delete_stale(char *file, int what) 2547 { 2548 char path[PATH_MAX]; 2549 struct stat sb; 2550 2551 (void) snprintf(path, sizeof (path), "%s/%s", get_cachedir(what), file); 2552 if (!bam_check && stat(path, &sb) == 0) { 2553 if (sb.st_mode & S_IFDIR) 2554 (void) rmdir_r(path); 2555 else 2556 (void) unlink(path); 2557 2558 set_dir_flag(what, (NEED_UPDATE | NO_MULTI)); 2559 } 2560 } 2561 2562 /* 2563 * Checks if a file in the current (old) archive has 2564 * been deleted from the root filesystem. This is needed for 2565 * software like Trusted Extensions (TX) that switch early 2566 * in boot based on presence/absence of a kernel module. 2567 */ 2568 static void 2569 check4stale(char *root) 2570 { 2571 nvpair_t *nvp; 2572 nvlist_t *nvlp; 2573 char *file; 2574 char path[PATH_MAX]; 2575 2576 /* 2577 * Skip stale file check during smf check 2578 */ 2579 if (bam_smf_check) 2580 return; 2581 2582 /* 2583 * If we need to (re)create the cache, there's no need to check for 2584 * stale files 2585 */ 2586 if (is_flag_on(NEED_CACHE_DIR)) 2587 return; 2588 2589 /* Nothing to do if no old stats */ 2590 if ((nvlp = walk_arg.old_nvlp) == NULL) 2591 return; 2592 2593 for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp; 2594 nvp = nvlist_next_nvpair(nvlp, nvp)) { 2595 file = nvpair_name(nvp); 2596 if (file == NULL) 2597 continue; 2598 (void) snprintf(path, sizeof (path), "%s/%s", 2599 root, file); 2600 if (access(path, F_OK) < 0) { 2601 int what; 2602 2603 if (bam_verbose) 2604 bam_print(PARSEABLE_STALE_FILE, path); 2605 2606 if (is_flag_on(IS_SPARC_TARGET)) { 2607 set_dir_flag(FILE64, NEED_UPDATE); 2608 } else { 2609 for (what = FILE32; what < CACHEDIR_NUM; what++) 2610 if (has_cachedir(what)) 2611 delete_stale(file, what); 2612 } 2613 } 2614 } 2615 } 2616 2617 static void 2618 create_newstat(void) 2619 { 2620 int error; 2621 2622 error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0); 2623 if (error) { 2624 /* 2625 * Not fatal - we can still create archive 2626 */ 2627 walk_arg.new_nvlp = NULL; 2628 bam_error(NVALLOC_FAIL, strerror(error)); 2629 } 2630 } 2631 2632 static int 2633 walk_list(char *root, filelist_t *flistp) 2634 { 2635 char path[PATH_MAX]; 2636 line_t *lp; 2637 2638 for (lp = flistp->head; lp; lp = lp->next) { 2639 /* 2640 * Don't follow symlinks. A symlink must refer to 2641 * a file that would appear in the archive through 2642 * a direct reference. This matches the archive 2643 * construction behavior. 2644 */ 2645 (void) snprintf(path, sizeof (path), "%s%s", root, lp->line); 2646 if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) { 2647 if (is_flag_on(UPDATE_ERROR)) 2648 return (BAM_ERROR); 2649 /* 2650 * Some files may not exist. 2651 * For example: etc/rtc_config on a x86 diskless system 2652 * Emit verbose message only 2653 */ 2654 if (bam_verbose) 2655 bam_print(NFTW_FAIL, path, strerror(errno)); 2656 } 2657 } 2658 2659 return (BAM_SUCCESS); 2660 } 2661 2662 /* 2663 * Update the timestamp file. 2664 */ 2665 static void 2666 update_timestamp(char *root) 2667 { 2668 char timestamp_path[PATH_MAX]; 2669 2670 /* this path length has already been checked in check_flags_and_files */ 2671 (void) snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root, 2672 FILE_STAT_TIMESTAMP); 2673 2674 /* 2675 * recreate the timestamp file. Since an outdated or absent timestamp 2676 * file translates in a complete rebuild of the archive cache, notify 2677 * the user of the performance issue. 2678 */ 2679 if (creat(timestamp_path, FILE_STAT_MODE) < 0) { 2680 bam_error(OPEN_FAIL, timestamp_path, strerror(errno)); 2681 bam_error(TIMESTAMP_FAIL, rootbuf); 2682 } 2683 } 2684 2685 2686 static void 2687 savenew(char *root) 2688 { 2689 char path[PATH_MAX]; 2690 char path2[PATH_MAX]; 2691 size_t sz; 2692 char *nstat; 2693 int fd, wrote, error; 2694 2695 nstat = NULL; 2696 sz = 0; 2697 error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz, 2698 NV_ENCODE_XDR, 0); 2699 if (error) { 2700 bam_error(PACK_FAIL, strerror(error)); 2701 return; 2702 } 2703 2704 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP); 2705 fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE); 2706 if (fd == -1) { 2707 bam_error(OPEN_FAIL, path, strerror(errno)); 2708 free(nstat); 2709 return; 2710 } 2711 wrote = write(fd, nstat, sz); 2712 if (wrote != sz) { 2713 bam_error(WRITE_FAIL, path, strerror(errno)); 2714 (void) close(fd); 2715 free(nstat); 2716 return; 2717 } 2718 (void) close(fd); 2719 free(nstat); 2720 2721 (void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT); 2722 if (rename(path, path2) != 0) { 2723 bam_error(RENAME_FAIL, path2, strerror(errno)); 2724 } 2725 } 2726 2727 #define init_walk_args() bzero(&walk_arg, sizeof (walk_arg)) 2728 2729 static void 2730 clear_walk_args(void) 2731 { 2732 if (walk_arg.old_nvlp) 2733 nvlist_free(walk_arg.old_nvlp); 2734 if (walk_arg.new_nvlp) 2735 nvlist_free(walk_arg.new_nvlp); 2736 if (walk_arg.sparcfile) 2737 (void) fclose(walk_arg.sparcfile); 2738 walk_arg.old_nvlp = NULL; 2739 walk_arg.new_nvlp = NULL; 2740 walk_arg.sparcfile = NULL; 2741 } 2742 2743 /* 2744 * Returns: 2745 * 0 - no update necessary 2746 * 1 - update required. 2747 * BAM_ERROR (-1) - An error occurred 2748 * 2749 * Special handling for check (-n): 2750 * ================================ 2751 * The check (-n) option produces parseable output. 2752 * To do this, we suppress all stdout messages unrelated 2753 * to out of sync files. 2754 * All stderr messages are still printed though. 2755 * 2756 */ 2757 static int 2758 update_required(char *root) 2759 { 2760 struct stat sb; 2761 char path[PATH_MAX]; 2762 filelist_t flist; 2763 filelist_t *flistp = &flist; 2764 int ret; 2765 2766 flistp->head = flistp->tail = NULL; 2767 2768 if (is_sparc()) 2769 set_flag(IS_SPARC_TARGET); 2770 2771 /* 2772 * Check if cache directories and archives are present 2773 */ 2774 2775 ret = check_flags_and_files(root); 2776 if (ret < 0) 2777 return (BAM_ERROR); 2778 2779 /* 2780 * In certain deployment scenarios, filestat may not 2781 * exist. Do not stop the boot process, but trigger an update 2782 * of the archives (which will recreate filestat.ramdisk). 2783 */ 2784 if (bam_smf_check) { 2785 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT); 2786 if (stat(path, &sb) != 0) { 2787 (void) creat(NEED_UPDATE_FILE, 0644); 2788 return (0); 2789 } 2790 } 2791 2792 getoldstat(root); 2793 2794 /* 2795 * Check if the archive contains files that are no longer 2796 * present on the root filesystem. 2797 */ 2798 check4stale(root); 2799 2800 /* 2801 * read list of files 2802 */ 2803 if (read_list(root, flistp) != BAM_SUCCESS) { 2804 clear_walk_args(); 2805 return (BAM_ERROR); 2806 } 2807 2808 assert(flistp->head && flistp->tail); 2809 2810 /* 2811 * At this point either the update is required 2812 * or the decision is pending. In either case 2813 * we need to create new stat nvlist 2814 */ 2815 create_newstat(); 2816 /* 2817 * This walk does 2 things: 2818 * - gets new stat data for every file 2819 * - (optional) compare old and new stat data 2820 */ 2821 ret = walk_list(root, &flist); 2822 2823 /* done with the file list */ 2824 filelist_free(flistp); 2825 2826 /* something went wrong */ 2827 2828 if (ret == BAM_ERROR) { 2829 bam_error(CACHE_FAIL); 2830 return (BAM_ERROR); 2831 } 2832 2833 if (walk_arg.new_nvlp == NULL) { 2834 if (walk_arg.sparcfile != NULL) 2835 (void) fclose(walk_arg.sparcfile); 2836 bam_error(NO_NEW_STAT); 2837 } 2838 2839 /* If nothing was updated, discard newstat. */ 2840 2841 if (!is_dir_flag_on(FILE32, NEED_UPDATE) && 2842 !is_dir_flag_on(FILE64, NEED_UPDATE)) { 2843 clear_walk_args(); 2844 return (0); 2845 } 2846 2847 if (walk_arg.sparcfile != NULL) 2848 (void) fclose(walk_arg.sparcfile); 2849 2850 return (1); 2851 } 2852 2853 static int 2854 flushfs(char *root) 2855 { 2856 char cmd[PATH_MAX + 30]; 2857 2858 (void) snprintf(cmd, sizeof (cmd), "%s -f \"%s\" 2>/dev/null", 2859 LOCKFS_PATH, root); 2860 2861 return (exec_cmd(cmd, NULL)); 2862 } 2863 2864 static int 2865 do_archive_copy(char *source, char *dest) 2866 { 2867 2868 sync(); 2869 2870 /* the equivalent of mv archive-new-$pid boot_archive */ 2871 if (rename(source, dest) != 0) { 2872 (void) unlink(source); 2873 return (BAM_ERROR); 2874 } 2875 2876 if (flushfs(bam_root) != 0) 2877 sync(); 2878 2879 return (BAM_SUCCESS); 2880 } 2881 2882 static int 2883 check_cmdline(filelist_t flist) 2884 { 2885 line_t *lp; 2886 2887 for (lp = flist.head; lp; lp = lp->next) { 2888 if (strstr(lp->line, "Error:") != NULL || 2889 strstr(lp->line, "Inode number overflow") != NULL) { 2890 (void) fprintf(stderr, "%s\n", lp->line); 2891 return (BAM_ERROR); 2892 } 2893 } 2894 2895 return (BAM_SUCCESS); 2896 } 2897 2898 static void 2899 dump_errormsg(filelist_t flist) 2900 { 2901 line_t *lp; 2902 2903 for (lp = flist.head; lp; lp = lp->next) 2904 (void) fprintf(stderr, "%s\n", lp->line); 2905 } 2906 2907 static int 2908 check_archive(char *dest) 2909 { 2910 struct stat sb; 2911 2912 if (stat(dest, &sb) != 0 || !S_ISREG(sb.st_mode) || 2913 sb.st_size < 10000) { 2914 bam_error(ARCHIVE_BAD, dest); 2915 (void) unlink(dest); 2916 return (BAM_ERROR); 2917 } 2918 2919 return (BAM_SUCCESS); 2920 } 2921 2922 /* 2923 * Returns 1 if mkiso is in the expected PATH, 0 otherwise 2924 */ 2925 static int 2926 is_mkisofs() 2927 { 2928 if (access(MKISOFS_PATH, X_OK) == 0) 2929 return (1); 2930 return (0); 2931 } 2932 2933 #define MKISO_PARAMS " -quiet -graft-points -dlrDJN -relaxed-filenames " 2934 2935 static int 2936 create_sparc_archive(char *archive, char *tempname, char *bootblk, char *list) 2937 { 2938 int ret; 2939 char cmdline[3 * PATH_MAX + 64]; 2940 filelist_t flist = {0}; 2941 const char *func = "create_sparc_archive()"; 2942 2943 if (access(bootblk, R_OK) == 1) { 2944 bam_error(BOOTBLK_FAIL, bootblk); 2945 return (BAM_ERROR); 2946 } 2947 2948 /* 2949 * Prepare mkisofs command line and execute it 2950 */ 2951 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -G %s -o \"%s\" " 2952 "-path-list \"%s\" 2>&1", MKISOFS_PATH, MKISO_PARAMS, bootblk, 2953 tempname, list); 2954 2955 BAM_DPRINTF((D_CMDLINE, func, cmdline)); 2956 2957 ret = exec_cmd(cmdline, &flist); 2958 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) { 2959 dump_errormsg(flist); 2960 goto out_err; 2961 } 2962 2963 filelist_free(&flist); 2964 2965 /* 2966 * Prepare dd command line to copy the bootblk on the new archive and 2967 * execute it 2968 */ 2969 (void) snprintf(cmdline, sizeof (cmdline), "%s if=\"%s\" of=\"%s\"" 2970 " bs=1b oseek=1 count=15 conv=notrunc conv=sync 2>&1", DD_PATH_USR, 2971 bootblk, tempname); 2972 2973 BAM_DPRINTF((D_CMDLINE, func, cmdline)); 2974 2975 ret = exec_cmd(cmdline, &flist); 2976 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) 2977 goto out_err; 2978 2979 filelist_free(&flist); 2980 2981 /* Did we get a valid archive ? */ 2982 if (check_archive(tempname) == BAM_ERROR) 2983 return (BAM_ERROR); 2984 2985 return (do_archive_copy(tempname, archive)); 2986 2987 out_err: 2988 filelist_free(&flist); 2989 bam_error(ARCHIVE_FAIL, cmdline); 2990 (void) unlink(tempname); 2991 return (BAM_ERROR); 2992 } 2993 2994 static unsigned int 2995 from_733(unsigned char *s) 2996 { 2997 int i; 2998 unsigned int ret = 0; 2999 3000 for (i = 0; i < 4; i++) 3001 ret |= s[i] << (8 * i); 3002 3003 return (ret); 3004 } 3005 3006 static void 3007 to_733(unsigned char *s, unsigned int val) 3008 { 3009 int i; 3010 3011 for (i = 0; i < 4; i++) 3012 s[i] = s[7-i] = (val >> (8 * i)) & 0xFF; 3013 } 3014 3015 /* 3016 * Extends the current boot archive without recreating it from scratch 3017 */ 3018 static int 3019 extend_iso_archive(char *archive, char *tempname, char *update_dir) 3020 { 3021 int fd = -1, newfd = -1, ret, i; 3022 int next_session = 0, new_size = 0; 3023 char cmdline[3 * PATH_MAX + 64]; 3024 const char *func = "extend_iso_archive()"; 3025 filelist_t flist = {0}; 3026 struct iso_pdesc saved_desc[MAX_IVDs]; 3027 3028 fd = open(archive, O_RDWR); 3029 if (fd == -1) { 3030 if (bam_verbose) 3031 bam_error(OPEN_FAIL, archive, strerror(errno)); 3032 goto out_err; 3033 } 3034 3035 /* 3036 * A partial read is likely due to a corrupted file 3037 */ 3038 ret = pread64(fd, saved_desc, sizeof (saved_desc), 3039 VOLDESC_OFF * CD_BLOCK); 3040 if (ret != sizeof (saved_desc)) { 3041 if (bam_verbose) 3042 bam_error(READ_FAIL, archive, strerror(errno)); 3043 goto out_err; 3044 } 3045 3046 if (memcmp(saved_desc[0].type, "\1CD001", 6)) { 3047 if (bam_verbose) 3048 bam_error(SIGN_FAIL, archive); 3049 goto out_err; 3050 } 3051 3052 /* 3053 * Read primary descriptor and locate next_session offset (it should 3054 * point to the end of the archive) 3055 */ 3056 next_session = P2ROUNDUP(from_733(saved_desc[0].volume_space_size), 16); 3057 3058 (void) snprintf(cmdline, sizeof (cmdline), "%s -C 16,%d -M %s %s -o \"" 3059 "%s\" \"%s\" 2>&1", MKISOFS_PATH, next_session, archive, 3060 MKISO_PARAMS, tempname, update_dir); 3061 3062 BAM_DPRINTF((D_CMDLINE, func, cmdline)); 3063 3064 ret = exec_cmd(cmdline, &flist); 3065 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) { 3066 if (bam_verbose) { 3067 bam_error(MULTI_FAIL, cmdline); 3068 dump_errormsg(flist); 3069 } 3070 goto out_flist_err; 3071 } 3072 filelist_free(&flist); 3073 3074 newfd = open(tempname, O_RDONLY); 3075 if (newfd == -1) { 3076 if (bam_verbose) 3077 bam_error(OPEN_FAIL, archive, strerror(errno)); 3078 goto out_err; 3079 } 3080 3081 ret = pread64(newfd, saved_desc, sizeof (saved_desc), 3082 VOLDESC_OFF * CD_BLOCK); 3083 if (ret != sizeof (saved_desc)) { 3084 if (bam_verbose) 3085 bam_error(READ_FAIL, archive, strerror(errno)); 3086 goto out_err; 3087 } 3088 3089 if (memcmp(saved_desc[0].type, "\1CD001", 6)) { 3090 if (bam_verbose) 3091 bam_error(SIGN_FAIL, archive); 3092 goto out_err; 3093 } 3094 3095 new_size = from_733(saved_desc[0].volume_space_size) + next_session; 3096 to_733(saved_desc[0].volume_space_size, new_size); 3097 3098 for (i = 1; i < MAX_IVDs; i++) { 3099 if (saved_desc[i].type[0] == (unsigned char)255) 3100 break; 3101 if (memcmp(saved_desc[i].id, "CD001", 5)) 3102 break; 3103 3104 if (bam_verbose) 3105 bam_print("%s: Updating descriptor entry [%d]\n", func, 3106 i); 3107 3108 to_733(saved_desc[i].volume_space_size, new_size); 3109 } 3110 3111 ret = pwrite64(fd, saved_desc, DVD_BLOCK, VOLDESC_OFF*CD_BLOCK); 3112 if (ret != DVD_BLOCK) { 3113 if (bam_verbose) 3114 bam_error(WRITE_FAIL, archive, strerror(errno)); 3115 goto out_err; 3116 } 3117 (void) close(newfd); 3118 newfd = -1; 3119 3120 ret = fsync(fd); 3121 if (ret != 0) 3122 sync(); 3123 3124 ret = close(fd); 3125 if (ret != 0) { 3126 if (bam_verbose) 3127 bam_error(CLOSE_FAIL, archive, strerror(errno)); 3128 return (BAM_ERROR); 3129 } 3130 fd = -1; 3131 3132 (void) snprintf(cmdline, sizeof (cmdline), "%s if=%s of=%s bs=32k " 3133 "seek=%d conv=sync 2>&1", DD_PATH_USR, tempname, archive, 3134 (next_session/16)); 3135 3136 BAM_DPRINTF((D_CMDLINE, func, cmdline)); 3137 3138 ret = exec_cmd(cmdline, &flist); 3139 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) { 3140 if (bam_verbose) 3141 bam_error(MULTI_FAIL, cmdline); 3142 goto out_flist_err; 3143 } 3144 filelist_free(&flist); 3145 3146 (void) unlink(tempname); 3147 3148 if (flushfs(bam_root) != 0) 3149 sync(); 3150 3151 if (bam_verbose) 3152 bam_print("boot archive updated successfully\n"); 3153 3154 return (BAM_SUCCESS); 3155 3156 out_flist_err: 3157 filelist_free(&flist); 3158 out_err: 3159 if (fd != -1) 3160 (void) close(fd); 3161 if (newfd != -1) 3162 (void) close(newfd); 3163 return (BAM_ERROR); 3164 } 3165 3166 static int 3167 create_x86_archive(char *archive, char *tempname, char *update_dir) 3168 { 3169 int ret; 3170 char cmdline[3 * PATH_MAX + 64]; 3171 filelist_t flist = {0}; 3172 const char *func = "create_x86_archive()"; 3173 3174 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -o \"%s\" \"%s\" " 3175 "2>&1", MKISOFS_PATH, MKISO_PARAMS, tempname, update_dir); 3176 3177 BAM_DPRINTF((D_CMDLINE, func, cmdline)); 3178 3179 ret = exec_cmd(cmdline, &flist); 3180 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) { 3181 bam_error(ARCHIVE_FAIL, cmdline); 3182 dump_errormsg(flist); 3183 filelist_free(&flist); 3184 (void) unlink(tempname); 3185 return (BAM_ERROR); 3186 } 3187 3188 filelist_free(&flist); 3189 3190 if (check_archive(tempname) == BAM_ERROR) 3191 return (BAM_ERROR); 3192 3193 return (do_archive_copy(tempname, archive)); 3194 } 3195 3196 static int 3197 mkisofs_archive(char *root, int what) 3198 { 3199 int ret; 3200 char temp[PATH_MAX]; 3201 char bootblk[PATH_MAX]; 3202 char boot_archive[PATH_MAX]; 3203 3204 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET)) 3205 ret = snprintf(temp, sizeof (temp), 3206 "%s%s%s/amd64/archive-new-%d", root, ARCHIVE_PREFIX, 3207 get_machine(), getpid()); 3208 else 3209 ret = snprintf(temp, sizeof (temp), "%s%s%s/archive-new-%d", 3210 root, ARCHIVE_PREFIX, get_machine(), getpid()); 3211 3212 if (ret >= sizeof (temp)) 3213 goto out_path_err; 3214 3215 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET)) 3216 ret = snprintf(boot_archive, sizeof (boot_archive), 3217 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(), 3218 ARCHIVE_SUFFIX); 3219 else 3220 ret = snprintf(boot_archive, sizeof (boot_archive), 3221 "%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(), 3222 ARCHIVE_SUFFIX); 3223 3224 if (ret >= sizeof (boot_archive)) 3225 goto out_path_err; 3226 3227 bam_print("updating %s\n", boot_archive); 3228 3229 if (is_flag_on(IS_SPARC_TARGET)) { 3230 ret = snprintf(bootblk, sizeof (bootblk), 3231 "%s/platform/%s/lib/fs/hsfs/bootblk", root, get_machine()); 3232 if (ret >= sizeof (bootblk)) 3233 goto out_path_err; 3234 3235 ret = create_sparc_archive(boot_archive, temp, bootblk, 3236 get_cachedir(what)); 3237 } else { 3238 if (!is_dir_flag_on(what, NO_MULTI)) { 3239 if (bam_verbose) 3240 bam_print("Attempting to extend x86 archive: " 3241 "%s\n", boot_archive); 3242 3243 ret = extend_iso_archive(boot_archive, temp, 3244 get_updatedir(what)); 3245 if (ret == BAM_SUCCESS) { 3246 if (bam_verbose) 3247 bam_print("Successfully extended %s\n", 3248 boot_archive); 3249 3250 (void) rmdir_r(get_updatedir(what)); 3251 return (BAM_SUCCESS); 3252 } 3253 } 3254 /* 3255 * The boot archive will be recreated from scratch. We get here 3256 * if at least one of these conditions is true: 3257 * - bootadm was called without the -e switch 3258 * - the archive (or the archive cache) doesn't exist 3259 * - archive size is bigger than BA_SIZE_MAX 3260 * - more than COUNT_MAX files need to be updated 3261 * - an error occourred either populating the /updates directory 3262 * or extend_iso_archive() failed 3263 */ 3264 if (bam_verbose) 3265 bam_print("Unable to extend %s... rebuilding archive\n", 3266 boot_archive); 3267 3268 if (get_updatedir(what)[0] != '\0') 3269 (void) rmdir_r(get_updatedir(what)); 3270 3271 3272 ret = create_x86_archive(boot_archive, temp, 3273 get_cachedir(what)); 3274 } 3275 3276 if (ret == BAM_SUCCESS && bam_verbose) 3277 bam_print("Successfully created %s\n", boot_archive); 3278 3279 return (ret); 3280 3281 out_path_err: 3282 bam_error(PATH_TOO_LONG, root); 3283 return (BAM_ERROR); 3284 } 3285 3286 static error_t 3287 create_ramdisk(char *root) 3288 { 3289 char *cmdline, path[PATH_MAX]; 3290 size_t len; 3291 struct stat sb; 3292 int ret, what, status = BAM_SUCCESS; 3293 3294 /* If there is mkisofs, use it to create the required archives */ 3295 if (is_mkisofs()) { 3296 for (what = FILE32; what < CACHEDIR_NUM; what++) { 3297 if (has_cachedir(what) && is_dir_flag_on(what, 3298 NEED_UPDATE)) { 3299 ret = mkisofs_archive(root, what); 3300 if (ret != 0) 3301 status = BAM_ERROR; 3302 } 3303 } 3304 return (status); 3305 } 3306 3307 /* 3308 * Else setup command args for create_ramdisk.ksh for the UFS archives 3309 */ 3310 if (bam_verbose) 3311 bam_print("mkisofs not found, creating UFS archive\n"); 3312 3313 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK); 3314 if (stat(path, &sb) != 0) { 3315 bam_error(ARCH_EXEC_MISS, path, strerror(errno)); 3316 return (BAM_ERROR); 3317 } 3318 3319 if (is_safe_exec(path) == BAM_ERROR) 3320 return (BAM_ERROR); 3321 3322 len = strlen(path) + strlen(root) + 10; /* room for space + -R */ 3323 if (bam_alt_platform) 3324 len += strlen(bam_platform) + strlen("-p "); 3325 cmdline = s_calloc(1, len); 3326 3327 if (bam_alt_platform) { 3328 assert(strlen(root) > 1); 3329 (void) snprintf(cmdline, len, "%s -p %s -R %s", 3330 path, bam_platform, root); 3331 /* chop off / at the end */ 3332 cmdline[strlen(cmdline) - 1] = '\0'; 3333 } else if (strlen(root) > 1) { 3334 (void) snprintf(cmdline, len, "%s -R %s", path, root); 3335 /* chop off / at the end */ 3336 cmdline[strlen(cmdline) - 1] = '\0'; 3337 } else 3338 (void) snprintf(cmdline, len, "%s", path); 3339 3340 if (exec_cmd(cmdline, NULL) != 0) { 3341 bam_error(ARCHIVE_FAIL, cmdline); 3342 free(cmdline); 3343 return (BAM_ERROR); 3344 } 3345 free(cmdline); 3346 /* 3347 * The existence of the expected archives used to be 3348 * verified here. This check is done in create_ramdisk as 3349 * it needs to be in sync with the altroot operated upon. 3350 */ 3351 return (BAM_SUCCESS); 3352 } 3353 3354 /* 3355 * Checks if target filesystem is on a ramdisk 3356 * 1 - is miniroot 3357 * 0 - is not 3358 * When in doubt assume it is not a ramdisk. 3359 */ 3360 static int 3361 is_ramdisk(char *root) 3362 { 3363 struct extmnttab mnt; 3364 FILE *fp; 3365 int found; 3366 char mntpt[PATH_MAX]; 3367 char *cp; 3368 3369 /* 3370 * There are 3 situations where creating archive is 3371 * of dubious value: 3372 * - create boot_archive on a lofi-mounted boot_archive 3373 * - create it on a ramdisk which is the root filesystem 3374 * - create it on a ramdisk mounted somewhere else 3375 * The first is not easy to detect and checking for it is not 3376 * worth it. 3377 * The other two conditions are handled here 3378 */ 3379 fp = fopen(MNTTAB, "r"); 3380 if (fp == NULL) { 3381 bam_error(OPEN_FAIL, MNTTAB, strerror(errno)); 3382 return (0); 3383 } 3384 3385 resetmnttab(fp); 3386 3387 /* 3388 * Remove any trailing / from the mount point 3389 */ 3390 (void) strlcpy(mntpt, root, sizeof (mntpt)); 3391 if (strcmp(root, "/") != 0) { 3392 cp = mntpt + strlen(mntpt) - 1; 3393 if (*cp == '/') 3394 *cp = '\0'; 3395 } 3396 found = 0; 3397 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) { 3398 if (strcmp(mnt.mnt_mountp, mntpt) == 0) { 3399 found = 1; 3400 break; 3401 } 3402 } 3403 3404 if (!found) { 3405 if (bam_verbose) 3406 bam_error(NOT_IN_MNTTAB, mntpt); 3407 (void) fclose(fp); 3408 return (0); 3409 } 3410 3411 if (strstr(mnt.mnt_special, RAMDISK_SPECIAL) != NULL) { 3412 if (bam_verbose) 3413 bam_error(IS_RAMDISK, bam_root); 3414 (void) fclose(fp); 3415 return (1); 3416 } 3417 3418 (void) fclose(fp); 3419 3420 return (0); 3421 } 3422 3423 static int 3424 is_boot_archive(char *root) 3425 { 3426 char path[PATH_MAX]; 3427 struct stat sb; 3428 int error; 3429 const char *fcn = "is_boot_archive()"; 3430 3431 /* 3432 * We can't create an archive without the create_ramdisk script 3433 */ 3434 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK); 3435 error = stat(path, &sb); 3436 INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1); 3437 if (error == -1) { 3438 if (bam_verbose) 3439 bam_print(FILE_MISS, path); 3440 BAM_DPRINTF((D_NOT_ARCHIVE_BOOT, fcn, root)); 3441 return (0); 3442 } 3443 3444 BAM_DPRINTF((D_IS_ARCHIVE_BOOT, fcn, root)); 3445 return (1); 3446 } 3447 3448 /* 3449 * Need to call this for anything that operates on the GRUB menu 3450 * In the x86 live upgrade case the directory /boot/grub may be present 3451 * even on pre-newboot BEs. The authoritative way to check for a GRUB target 3452 * is to check for the presence of the stage2 binary which is present 3453 * only on GRUB targets (even on x86 boot partitions). Checking for the 3454 * presence of the multiboot binary is not correct as it is not present 3455 * on x86 boot partitions. 3456 */ 3457 int 3458 is_grub(const char *root) 3459 { 3460 char path[PATH_MAX]; 3461 struct stat sb; 3462 const char *fcn = "is_grub()"; 3463 3464 (void) snprintf(path, sizeof (path), "%s%s", root, GRUB_STAGE2); 3465 if (stat(path, &sb) == -1) { 3466 BAM_DPRINTF((D_NO_GRUB_DIR, fcn, path)); 3467 return (0); 3468 } 3469 3470 return (1); 3471 } 3472 3473 static int 3474 is_zfs(char *root) 3475 { 3476 struct statvfs vfs; 3477 int ret; 3478 const char *fcn = "is_zfs()"; 3479 3480 ret = statvfs(root, &vfs); 3481 INJECT_ERROR1("STATVFS_ZFS", ret = 1); 3482 if (ret != 0) { 3483 bam_error(STATVFS_FAIL, root, strerror(errno)); 3484 return (0); 3485 } 3486 3487 if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) { 3488 BAM_DPRINTF((D_IS_ZFS, fcn, root)); 3489 return (1); 3490 } else { 3491 BAM_DPRINTF((D_IS_NOT_ZFS, fcn, root)); 3492 return (0); 3493 } 3494 } 3495 3496 static int 3497 is_ufs(char *root) 3498 { 3499 struct statvfs vfs; 3500 int ret; 3501 const char *fcn = "is_ufs()"; 3502 3503 ret = statvfs(root, &vfs); 3504 INJECT_ERROR1("STATVFS_UFS", ret = 1); 3505 if (ret != 0) { 3506 bam_error(STATVFS_FAIL, root, strerror(errno)); 3507 return (0); 3508 } 3509 3510 if (strncmp(vfs.f_basetype, "ufs", strlen("ufs")) == 0) { 3511 BAM_DPRINTF((D_IS_UFS, fcn, root)); 3512 return (1); 3513 } else { 3514 BAM_DPRINTF((D_IS_NOT_UFS, fcn, root)); 3515 return (0); 3516 } 3517 } 3518 3519 static int 3520 is_pcfs(char *root) 3521 { 3522 struct statvfs vfs; 3523 int ret; 3524 const char *fcn = "is_pcfs()"; 3525 3526 ret = statvfs(root, &vfs); 3527 INJECT_ERROR1("STATVFS_PCFS", ret = 1); 3528 if (ret != 0) { 3529 bam_error(STATVFS_FAIL, root, strerror(errno)); 3530 return (0); 3531 } 3532 3533 if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) { 3534 BAM_DPRINTF((D_IS_PCFS, fcn, root)); 3535 return (1); 3536 } else { 3537 BAM_DPRINTF((D_IS_NOT_PCFS, fcn, root)); 3538 return (0); 3539 } 3540 } 3541 3542 static int 3543 is_readonly(char *root) 3544 { 3545 int fd; 3546 int error; 3547 char testfile[PATH_MAX]; 3548 const char *fcn = "is_readonly()"; 3549 3550 /* 3551 * Using statvfs() to check for a read-only filesystem is not 3552 * reliable. The only way to reliably test is to attempt to 3553 * create a file 3554 */ 3555 (void) snprintf(testfile, sizeof (testfile), "%s/%s.%d", 3556 root, BOOTADM_RDONLY_TEST, getpid()); 3557 3558 (void) unlink(testfile); 3559 3560 errno = 0; 3561 fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644); 3562 error = errno; 3563 INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES); 3564 if (fd == -1 && error == EROFS) { 3565 BAM_DPRINTF((D_RDONLY_FS, fcn, root)); 3566 return (1); 3567 } else if (fd == -1) { 3568 bam_error(RDONLY_TEST_ERROR, root, strerror(error)); 3569 } 3570 3571 (void) close(fd); 3572 (void) unlink(testfile); 3573 3574 BAM_DPRINTF((D_RDWR_FS, fcn, root)); 3575 return (0); 3576 } 3577 3578 static error_t 3579 update_archive(char *root, char *opt) 3580 { 3581 error_t ret; 3582 3583 assert(root); 3584 assert(opt == NULL); 3585 3586 init_walk_args(); 3587 (void) umask(022); 3588 3589 /* 3590 * root must belong to a boot archive based OS, 3591 */ 3592 if (!is_boot_archive(root)) { 3593 /* 3594 * Emit message only if not in context of update_all. 3595 * If in update_all, emit only if verbose flag is set. 3596 */ 3597 if (!bam_update_all || bam_verbose) 3598 bam_print(NOT_ARCHIVE_BOOT, root); 3599 return (BAM_ERROR); 3600 } 3601 3602 /* 3603 * If smf check is requested when / is writable (can happen 3604 * on first reboot following an upgrade because service 3605 * dependency is messed up), skip the check. 3606 */ 3607 if (bam_smf_check && !bam_root_readonly && !is_zfs(root)) 3608 return (BAM_SUCCESS); 3609 3610 /* 3611 * Don't generate archive on ramdisk. 3612 */ 3613 if (is_ramdisk(root)) 3614 return (BAM_SUCCESS); 3615 3616 /* 3617 * root must be writable. This check applies to alternate 3618 * root (-R option); bam_root_readonly applies to '/' only. 3619 * The behaviour translates into being the one of a 'check'. 3620 */ 3621 if (!bam_smf_check && !bam_check && is_readonly(root)) { 3622 set_flag(RDONLY_FSCHK); 3623 bam_check = 1; 3624 } 3625 3626 /* 3627 * Now check if an update is really needed. 3628 */ 3629 ret = update_required(root); 3630 3631 /* 3632 * The check command (-n) is *not* a dry run. 3633 * It only checks if the archive is in sync. 3634 * A readonly filesystem has to be considered an error only if an update 3635 * is required. 3636 */ 3637 if (bam_nowrite()) { 3638 if (is_flag_on(RDONLY_FSCHK)) { 3639 bam_check = bam_saved_check; 3640 if (ret > 0) 3641 bam_error(RDONLY_FS, root); 3642 if (bam_update_all) 3643 return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS); 3644 } 3645 3646 bam_exit((ret != 0) ? 1 : 0); 3647 } 3648 3649 if (ret == 1) { 3650 /* create the ramdisk */ 3651 ret = create_ramdisk(root); 3652 } 3653 3654 /* 3655 * if the archive is updated, save the new stat data and update the 3656 * timestamp file 3657 */ 3658 if (ret == 0 && walk_arg.new_nvlp != NULL) { 3659 savenew(root); 3660 update_timestamp(root); 3661 } 3662 3663 clear_walk_args(); 3664 3665 return (ret); 3666 } 3667 3668 static char * 3669 find_root_pool() 3670 { 3671 char *special = get_special("/"); 3672 char *p; 3673 3674 if (special == NULL) 3675 return (NULL); 3676 3677 if (*special == '/') { 3678 free(special); 3679 return (NULL); 3680 } 3681 3682 if ((p = strchr(special, '/')) != NULL) 3683 *p = '\0'; 3684 3685 return (special); 3686 } 3687 3688 static error_t 3689 synchronize_BE_menu(void) 3690 { 3691 struct stat sb; 3692 char cmdline[PATH_MAX]; 3693 char cksum_line[PATH_MAX]; 3694 filelist_t flist = {0}; 3695 char *old_cksum_str; 3696 char *old_size_str; 3697 char *old_file; 3698 char *curr_cksum_str; 3699 char *curr_size_str; 3700 char *curr_file; 3701 char *pool = NULL; 3702 char *mntpt = NULL; 3703 zfs_mnted_t mnted; 3704 FILE *cfp; 3705 int found; 3706 int ret; 3707 const char *fcn = "synchronize_BE_menu()"; 3708 3709 BAM_DPRINTF((D_FUNC_ENTRY0, fcn)); 3710 3711 /* Check if findroot enabled LU BE */ 3712 if (stat(FINDROOT_INSTALLGRUB, &sb) != 0) { 3713 BAM_DPRINTF((D_NOT_LU_BE, fcn)); 3714 return (BAM_SUCCESS); 3715 } 3716 3717 if (stat(LU_MENU_CKSUM, &sb) != 0) { 3718 BAM_DPRINTF((D_NO_CKSUM_FILE, fcn, LU_MENU_CKSUM)); 3719 goto menu_sync; 3720 } 3721 3722 cfp = fopen(LU_MENU_CKSUM, "r"); 3723 INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL); 3724 if (cfp == NULL) { 3725 bam_error(CANNOT_READ_LU_CKSUM, LU_MENU_CKSUM); 3726 goto menu_sync; 3727 } 3728 BAM_DPRINTF((D_CKSUM_FILE_OPENED, fcn, LU_MENU_CKSUM)); 3729 3730 found = 0; 3731 while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) { 3732 INJECT_ERROR1("MULTIPLE_CKSUM", found = 1); 3733 if (found) { 3734 bam_error(MULTIPLE_LU_CKSUM, LU_MENU_CKSUM); 3735 (void) fclose(cfp); 3736 goto menu_sync; 3737 } 3738 found = 1; 3739 } 3740 BAM_DPRINTF((D_CKSUM_FILE_READ, fcn, LU_MENU_CKSUM)); 3741 3742 3743 old_cksum_str = strtok(cksum_line, " \t"); 3744 old_size_str = strtok(NULL, " \t"); 3745 old_file = strtok(NULL, " \t"); 3746 3747 INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str = NULL); 3748 INJECT_ERROR1("OLD_SIZE_NULL", old_size_str = NULL); 3749 INJECT_ERROR1("OLD_FILE_NULL", old_file = NULL); 3750 if (old_cksum_str == NULL || old_size_str == NULL || old_file == NULL) { 3751 bam_error(CANNOT_PARSE_LU_CKSUM, LU_MENU_CKSUM); 3752 goto menu_sync; 3753 } 3754 BAM_DPRINTF((D_CKSUM_FILE_PARSED, fcn, LU_MENU_CKSUM)); 3755 3756 /* Get checksum of current menu */ 3757 pool = find_root_pool(); 3758 if (pool) { 3759 mntpt = mount_top_dataset(pool, &mnted); 3760 if (mntpt == NULL) { 3761 bam_error(FAIL_MNT_TOP_DATASET, pool); 3762 free(pool); 3763 return (BAM_ERROR); 3764 } 3765 (void) snprintf(cmdline, sizeof (cmdline), "%s %s%s", 3766 CKSUM, mntpt, GRUB_MENU); 3767 } else { 3768 (void) snprintf(cmdline, sizeof (cmdline), "%s %s", 3769 CKSUM, GRUB_MENU); 3770 } 3771 ret = exec_cmd(cmdline, &flist); 3772 if (pool) { 3773 (void) umount_top_dataset(pool, mnted, mntpt); 3774 free(pool); 3775 } 3776 INJECT_ERROR1("GET_CURR_CKSUM", ret = 1); 3777 if (ret != 0) { 3778 bam_error(MENU_CKSUM_FAIL); 3779 return (BAM_ERROR); 3780 } 3781 BAM_DPRINTF((D_CKSUM_GEN_SUCCESS, fcn)); 3782 3783 INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist.head = NULL); 3784 if ((flist.head == NULL) || (flist.head != flist.tail)) { 3785 bam_error(BAD_CKSUM); 3786 filelist_free(&flist); 3787 return (BAM_ERROR); 3788 } 3789 BAM_DPRINTF((D_CKSUM_GEN_OUTPUT_VALID, fcn)); 3790 3791 curr_cksum_str = strtok(flist.head->line, " \t"); 3792 curr_size_str = strtok(NULL, " \t"); 3793 curr_file = strtok(NULL, " \t"); 3794 3795 INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str = NULL); 3796 INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str = NULL); 3797 INJECT_ERROR1("CURR_FILE_NULL", curr_file = NULL); 3798 if (curr_cksum_str == NULL || curr_size_str == NULL || 3799 curr_file == NULL) { 3800 bam_error(BAD_CKSUM_PARSE); 3801 filelist_free(&flist); 3802 return (BAM_ERROR); 3803 } 3804 BAM_DPRINTF((D_CKSUM_GEN_PARSED, fcn)); 3805 3806 if (strcmp(old_cksum_str, curr_cksum_str) == 0 && 3807 strcmp(old_size_str, curr_size_str) == 0 && 3808 strcmp(old_file, curr_file) == 0) { 3809 filelist_free(&flist); 3810 BAM_DPRINTF((D_CKSUM_NO_CHANGE, fcn)); 3811 return (BAM_SUCCESS); 3812 } 3813 3814 filelist_free(&flist); 3815 3816 /* cksum doesn't match - the menu has changed */ 3817 BAM_DPRINTF((D_CKSUM_HAS_CHANGED, fcn)); 3818 3819 menu_sync: 3820 bam_print(PROP_GRUB_MENU); 3821 3822 (void) snprintf(cmdline, sizeof (cmdline), 3823 "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'", 3824 LULIB, LULIB_PROPAGATE_FILE, GRUB_MENU); 3825 ret = exec_cmd(cmdline, NULL); 3826 INJECT_ERROR1("PROPAGATE_MENU", ret = 1); 3827 if (ret != 0) { 3828 bam_error(MENU_PROP_FAIL); 3829 return (BAM_ERROR); 3830 } 3831 BAM_DPRINTF((D_PROPAGATED_MENU, fcn)); 3832 3833 (void) snprintf(cmdline, sizeof (cmdline), "/bin/cp %s %s > /dev/null", 3834 GRUB_MENU, GRUB_BACKUP_MENU); 3835 ret = exec_cmd(cmdline, NULL); 3836 INJECT_ERROR1("CREATE_BACKUP", ret = 1); 3837 if (ret != 0) { 3838 bam_error(MENU_BACKUP_FAIL, GRUB_BACKUP_MENU); 3839 return (BAM_ERROR); 3840 } 3841 BAM_DPRINTF((D_CREATED_BACKUP, fcn, GRUB_BACKUP_MENU)); 3842 3843 (void) snprintf(cmdline, sizeof (cmdline), 3844 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'", 3845 LULIB, LULIB_PROPAGATE_FILE, GRUB_BACKUP_MENU); 3846 ret = exec_cmd(cmdline, NULL); 3847 INJECT_ERROR1("PROPAGATE_BACKUP", ret = 1); 3848 if (ret != 0) { 3849 bam_error(BACKUP_PROP_FAIL, GRUB_BACKUP_MENU); 3850 return (BAM_ERROR); 3851 } 3852 BAM_DPRINTF((D_PROPAGATED_BACKUP, fcn, GRUB_BACKUP_MENU)); 3853 3854 (void) snprintf(cmdline, sizeof (cmdline), "%s %s > %s", 3855 CKSUM, GRUB_MENU, LU_MENU_CKSUM); 3856 ret = exec_cmd(cmdline, NULL); 3857 INJECT_ERROR1("CREATE_CKSUM_FILE", ret = 1); 3858 if (ret != 0) { 3859 bam_error(MENU_CKSUM_WRITE_FAIL, LU_MENU_CKSUM); 3860 return (BAM_ERROR); 3861 } 3862 BAM_DPRINTF((D_CREATED_CKSUM_FILE, fcn, LU_MENU_CKSUM)); 3863 3864 (void) snprintf(cmdline, sizeof (cmdline), 3865 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'", 3866 LULIB, LULIB_PROPAGATE_FILE, LU_MENU_CKSUM); 3867 ret = exec_cmd(cmdline, NULL); 3868 INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret = 1); 3869 if (ret != 0) { 3870 bam_error(MENU_CKSUM_PROP_FAIL, LU_MENU_CKSUM); 3871 return (BAM_ERROR); 3872 } 3873 BAM_DPRINTF((D_PROPAGATED_CKSUM_FILE, fcn, LU_MENU_CKSUM)); 3874 3875 return (BAM_SUCCESS); 3876 } 3877 3878 static error_t 3879 update_all(char *root, char *opt) 3880 { 3881 struct extmnttab mnt; 3882 struct stat sb; 3883 FILE *fp; 3884 char multibt[PATH_MAX]; 3885 char creatram[PATH_MAX]; 3886 error_t ret = BAM_SUCCESS; 3887 3888 assert(root); 3889 assert(opt == NULL); 3890 3891 if (bam_rootlen != 1 || *root != '/') { 3892 elide_trailing_slash(root, multibt, sizeof (multibt)); 3893 bam_error(ALT_ROOT_INVALID, multibt); 3894 return (BAM_ERROR); 3895 } 3896 3897 /* 3898 * Check to see if we are in the midst of safemode patching 3899 * If so skip building the archive for /. Instead build it 3900 * against the latest bits obtained by creating a fresh lofs 3901 * mount of root. 3902 */ 3903 if (stat(LOFS_PATCH_FILE, &sb) == 0) { 3904 if (mkdir(LOFS_PATCH_MNT, DIR_PERMS) == -1 && 3905 errno != EEXIST) { 3906 bam_error(MKDIR_FAILED, "%s", LOFS_PATCH_MNT, 3907 strerror(errno)); 3908 ret = BAM_ERROR; 3909 goto out; 3910 } 3911 (void) snprintf(multibt, sizeof (multibt), 3912 "/sbin/mount -F lofs -o nosub / %s", LOFS_PATCH_MNT); 3913 if (exec_cmd(multibt, NULL) != 0) { 3914 bam_error(MOUNT_FAILED, LOFS_PATCH_MNT, "lofs"); 3915 ret = BAM_ERROR; 3916 } 3917 if (ret != BAM_ERROR) { 3918 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/", 3919 LOFS_PATCH_MNT); 3920 bam_rootlen = strlen(rootbuf); 3921 if (update_archive(rootbuf, opt) != BAM_SUCCESS) 3922 ret = BAM_ERROR; 3923 /* 3924 * unmount the lofs mount since there could be 3925 * multiple invocations of bootadm -a update_all 3926 */ 3927 (void) snprintf(multibt, sizeof (multibt), 3928 "/sbin/umount %s", LOFS_PATCH_MNT); 3929 if (exec_cmd(multibt, NULL) != 0) { 3930 bam_error(UMOUNT_FAILED, LOFS_PATCH_MNT); 3931 ret = BAM_ERROR; 3932 } 3933 } 3934 } else { 3935 /* 3936 * First update archive for current root 3937 */ 3938 if (update_archive(root, opt) != BAM_SUCCESS) 3939 ret = BAM_ERROR; 3940 } 3941 3942 if (ret == BAM_ERROR) 3943 goto out; 3944 3945 /* 3946 * Now walk the mount table, performing archive update 3947 * for all mounted Newboot root filesystems 3948 */ 3949 fp = fopen(MNTTAB, "r"); 3950 if (fp == NULL) { 3951 bam_error(OPEN_FAIL, MNTTAB, strerror(errno)); 3952 ret = BAM_ERROR; 3953 goto out; 3954 } 3955 3956 resetmnttab(fp); 3957 3958 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) { 3959 if (mnt.mnt_special == NULL) 3960 continue; 3961 if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) && 3962 (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0)) 3963 continue; 3964 if (strcmp(mnt.mnt_mountp, "/") == 0) 3965 continue; 3966 3967 (void) snprintf(creatram, sizeof (creatram), "%s/%s", 3968 mnt.mnt_mountp, CREATE_RAMDISK); 3969 3970 if (stat(creatram, &sb) == -1) 3971 continue; 3972 3973 /* 3974 * We put a trailing slash to be consistent with root = "/" 3975 * case, such that we don't have to print // in some cases. 3976 */ 3977 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/", 3978 mnt.mnt_mountp); 3979 bam_rootlen = strlen(rootbuf); 3980 3981 /* 3982 * It's possible that other mounts may be an alternate boot 3983 * architecture, so check it again. 3984 */ 3985 if ((get_boot_cap(rootbuf) != BAM_SUCCESS) || 3986 (update_archive(rootbuf, opt) != BAM_SUCCESS)) 3987 ret = BAM_ERROR; 3988 } 3989 3990 (void) fclose(fp); 3991 3992 out: 3993 /* 3994 * We no longer use biosdev for Live Upgrade. Hence 3995 * there is no need to defer (to shutdown time) any fdisk 3996 * updates 3997 */ 3998 if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) { 3999 bam_error(FDISK_FILES_FOUND, GRUB_fdisk, GRUB_fdisk_target); 4000 } 4001 4002 /* 4003 * If user has updated menu in current BE, propagate the 4004 * updates to all BEs. 4005 */ 4006 if (sync_menu && synchronize_BE_menu() != BAM_SUCCESS) 4007 ret = BAM_ERROR; 4008 4009 return (ret); 4010 } 4011 4012 static void 4013 append_line(menu_t *mp, line_t *lp) 4014 { 4015 if (mp->start == NULL) { 4016 mp->start = lp; 4017 } else { 4018 mp->end->next = lp; 4019 lp->prev = mp->end; 4020 } 4021 mp->end = lp; 4022 } 4023 4024 void 4025 unlink_line(menu_t *mp, line_t *lp) 4026 { 4027 /* unlink from list */ 4028 if (lp->prev) 4029 lp->prev->next = lp->next; 4030 else 4031 mp->start = lp->next; 4032 if (lp->next) 4033 lp->next->prev = lp->prev; 4034 else 4035 mp->end = lp->prev; 4036 } 4037 4038 static entry_t * 4039 boot_entry_new(menu_t *mp, line_t *start, line_t *end) 4040 { 4041 entry_t *ent, *prev; 4042 const char *fcn = "boot_entry_new()"; 4043 4044 assert(mp); 4045 assert(start); 4046 assert(end); 4047 4048 ent = s_calloc(1, sizeof (entry_t)); 4049 BAM_DPRINTF((D_ENTRY_NEW, fcn)); 4050 ent->start = start; 4051 ent->end = end; 4052 4053 if (mp->entries == NULL) { 4054 mp->entries = ent; 4055 BAM_DPRINTF((D_ENTRY_NEW_FIRST, fcn)); 4056 return (ent); 4057 } 4058 4059 prev = mp->entries; 4060 while (prev->next) 4061 prev = prev->next; 4062 prev->next = ent; 4063 ent->prev = prev; 4064 BAM_DPRINTF((D_ENTRY_NEW_LINKED, fcn)); 4065 return (ent); 4066 } 4067 4068 static void 4069 boot_entry_addline(entry_t *ent, line_t *lp) 4070 { 4071 if (ent) 4072 ent->end = lp; 4073 } 4074 4075 /* 4076 * Check whether cmd matches the one indexed by which, and whether arg matches 4077 * str. which must be either KERNEL_CMD or MODULE_CMD, and a match to the 4078 * respective *_DOLLAR_CMD is also acceptable. The arg is searched using 4079 * strstr(), so it can be a partial match. 4080 */ 4081 static int 4082 check_cmd(const char *cmd, const int which, const char *arg, const char *str) 4083 { 4084 int ret; 4085 const char *fcn = "check_cmd()"; 4086 4087 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, arg, str)); 4088 4089 if (cmd != NULL) { 4090 if ((strcmp(cmd, menu_cmds[which]) != 0) && 4091 (strcmp(cmd, menu_cmds[which + 1]) != 0)) { 4092 BAM_DPRINTF((D_CHECK_CMD_CMD_NOMATCH, 4093 fcn, cmd, menu_cmds[which])); 4094 return (0); 4095 } 4096 ret = (strstr(arg, str) != NULL); 4097 } else 4098 ret = 0; 4099 4100 if (ret) { 4101 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 4102 } else { 4103 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 4104 } 4105 4106 return (ret); 4107 } 4108 4109 static error_t 4110 kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum) 4111 { 4112 const char *fcn = "kernel_parser()"; 4113 4114 assert(entry); 4115 assert(cmd); 4116 assert(arg); 4117 4118 if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 && 4119 strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) { 4120 BAM_DPRINTF((D_NOT_KERNEL_CMD, fcn, cmd)); 4121 return (BAM_ERROR); 4122 } 4123 4124 if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) { 4125 BAM_DPRINTF((D_SET_DBOOT_32, fcn, arg)); 4126 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT; 4127 } else if (strncmp(arg, DIRECT_BOOT_KERNEL, 4128 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) { 4129 BAM_DPRINTF((D_SET_DBOOT, fcn, arg)); 4130 entry->flags |= BAM_ENTRY_DBOOT; 4131 } else if (strncmp(arg, DIRECT_BOOT_64, 4132 sizeof (DIRECT_BOOT_64) - 1) == 0) { 4133 BAM_DPRINTF((D_SET_DBOOT_64, fcn, arg)); 4134 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT; 4135 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL, 4136 sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) { 4137 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE, fcn, arg)); 4138 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE; 4139 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_32, 4140 sizeof (DIRECT_BOOT_FAILSAFE_32) - 1) == 0) { 4141 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_32, fcn, arg)); 4142 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE 4143 | BAM_ENTRY_32BIT; 4144 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_64, 4145 sizeof (DIRECT_BOOT_FAILSAFE_64) - 1) == 0) { 4146 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_64, fcn, arg)); 4147 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE 4148 | BAM_ENTRY_64BIT; 4149 } else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) { 4150 BAM_DPRINTF((D_SET_MULTIBOOT, fcn, arg)); 4151 entry->flags |= BAM_ENTRY_MULTIBOOT; 4152 } else if (strncmp(arg, MULTI_BOOT_FAILSAFE, 4153 sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) { 4154 BAM_DPRINTF((D_SET_MULTIBOOT_FAILSAFE, fcn, arg)); 4155 entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE; 4156 } else if (strstr(arg, XEN_KERNEL_SUBSTR)) { 4157 BAM_DPRINTF((D_SET_HV, fcn, arg)); 4158 entry->flags |= BAM_ENTRY_HV; 4159 } else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) { 4160 BAM_DPRINTF((D_SET_HAND_KERNEL, fcn, arg)); 4161 return (BAM_ERROR); 4162 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 && 4163 strstr(arg, UNIX_SPACE)) { 4164 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT; 4165 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 && 4166 strstr(arg, AMD_UNIX_SPACE)) { 4167 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT; 4168 } else { 4169 BAM_DPRINTF((D_IS_UNKNOWN_KERNEL, fcn, arg)); 4170 bam_error(UNKNOWN_KERNEL_LINE, linenum); 4171 return (BAM_ERROR); 4172 } 4173 4174 return (BAM_SUCCESS); 4175 } 4176 4177 static error_t 4178 module_parser(entry_t *entry, char *cmd, char *arg, int linenum) 4179 { 4180 const char *fcn = "module_parser()"; 4181 4182 assert(entry); 4183 assert(cmd); 4184 assert(arg); 4185 4186 if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 && 4187 strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) { 4188 BAM_DPRINTF((D_NOT_MODULE_CMD, fcn, cmd)); 4189 return (BAM_ERROR); 4190 } 4191 4192 if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 || 4193 strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 || 4194 strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 || 4195 strcmp(arg, MULTIBOOT_ARCHIVE) == 0 || 4196 strcmp(arg, FAILSAFE_ARCHIVE) == 0 || 4197 strcmp(arg, FAILSAFE_ARCHIVE_32) == 0 || 4198 strcmp(arg, FAILSAFE_ARCHIVE_64) == 0 || 4199 strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 || 4200 strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) { 4201 BAM_DPRINTF((D_BOOTADM_LU_MODULE, fcn, arg)); 4202 return (BAM_SUCCESS); 4203 } else if (!(entry->flags & BAM_ENTRY_BOOTADM) && 4204 !(entry->flags & BAM_ENTRY_LU)) { 4205 /* don't emit warning for hand entries */ 4206 BAM_DPRINTF((D_IS_HAND_MODULE, fcn, arg)); 4207 return (BAM_ERROR); 4208 } else { 4209 BAM_DPRINTF((D_IS_UNKNOWN_MODULE, fcn, arg)); 4210 bam_error(UNKNOWN_MODULE_LINE, linenum); 4211 return (BAM_ERROR); 4212 } 4213 } 4214 4215 /* 4216 * A line in menu.lst looks like 4217 * [ ]*<cmd>[ \t=]*<arg>* 4218 */ 4219 static void 4220 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum) 4221 { 4222 /* 4223 * save state across calls. This is so that 4224 * header gets the right entry# after title has 4225 * been processed 4226 */ 4227 static line_t *prev = NULL; 4228 static entry_t *curr_ent = NULL; 4229 static int in_liveupgrade = 0; 4230 static int is_libbe_ent = 0; 4231 4232 line_t *lp; 4233 char *cmd, *sep, *arg; 4234 char save, *cp, *line; 4235 menu_flag_t flag = BAM_INVALID; 4236 const char *fcn = "line_parser()"; 4237 4238 if (str == NULL) { 4239 return; 4240 } 4241 4242 /* 4243 * First save a copy of the entire line. 4244 * We use this later to set the line field. 4245 */ 4246 line = s_strdup(str); 4247 4248 /* Eat up leading whitespace */ 4249 while (*str == ' ' || *str == '\t') 4250 str++; 4251 4252 if (*str == '#') { /* comment */ 4253 cmd = s_strdup("#"); 4254 sep = NULL; 4255 arg = s_strdup(str + 1); 4256 flag = BAM_COMMENT; 4257 if (strstr(arg, BAM_LU_HDR) != NULL) { 4258 in_liveupgrade = 1; 4259 } else if (strstr(arg, BAM_LU_FTR) != NULL) { 4260 in_liveupgrade = 0; 4261 } else if (strstr(arg, BAM_LIBBE_FTR) != NULL) { 4262 is_libbe_ent = 1; 4263 } 4264 } else if (*str == '\0') { /* blank line */ 4265 cmd = sep = arg = NULL; 4266 flag = BAM_EMPTY; 4267 } else { 4268 /* 4269 * '=' is not a documented separator in grub syntax. 4270 * However various development bits use '=' as a 4271 * separator. In addition, external users also 4272 * use = as a separator. So we will allow that usage. 4273 */ 4274 cp = str; 4275 while (*str != ' ' && *str != '\t' && *str != '=') { 4276 if (*str == '\0') { 4277 cmd = s_strdup(cp); 4278 sep = arg = NULL; 4279 break; 4280 } 4281 str++; 4282 } 4283 4284 if (*str != '\0') { 4285 save = *str; 4286 *str = '\0'; 4287 cmd = s_strdup(cp); 4288 *str = save; 4289 4290 str++; 4291 save = *str; 4292 *str = '\0'; 4293 sep = s_strdup(str - 1); 4294 *str = save; 4295 4296 while (*str == ' ' || *str == '\t') 4297 str++; 4298 if (*str == '\0') 4299 arg = NULL; 4300 else 4301 arg = s_strdup(str); 4302 } 4303 } 4304 4305 lp = s_calloc(1, sizeof (line_t)); 4306 4307 lp->cmd = cmd; 4308 lp->sep = sep; 4309 lp->arg = arg; 4310 lp->line = line; 4311 lp->lineNum = ++(*lineNum); 4312 if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) { 4313 lp->entryNum = ++(*entryNum); 4314 lp->flags = BAM_TITLE; 4315 if (prev && prev->flags == BAM_COMMENT && 4316 prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) { 4317 prev->entryNum = lp->entryNum; 4318 curr_ent = boot_entry_new(mp, prev, lp); 4319 curr_ent->flags |= BAM_ENTRY_BOOTADM; 4320 BAM_DPRINTF((D_IS_BOOTADM_ENTRY, fcn, arg)); 4321 } else { 4322 curr_ent = boot_entry_new(mp, lp, lp); 4323 if (in_liveupgrade) { 4324 curr_ent->flags |= BAM_ENTRY_LU; 4325 BAM_DPRINTF((D_IS_LU_ENTRY, fcn, arg)); 4326 } 4327 } 4328 curr_ent->entryNum = *entryNum; 4329 } else if (flag != BAM_INVALID) { 4330 /* 4331 * For header comments, the entry# is "fixed up" 4332 * by the subsequent title 4333 */ 4334 lp->entryNum = *entryNum; 4335 lp->flags = flag; 4336 } else { 4337 lp->entryNum = *entryNum; 4338 4339 if (*entryNum == ENTRY_INIT) { 4340 lp->flags = BAM_GLOBAL; 4341 } else { 4342 lp->flags = BAM_ENTRY; 4343 4344 if (cmd && arg) { 4345 if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) { 4346 BAM_DPRINTF((D_IS_ROOT_CMD, fcn, arg)); 4347 curr_ent->flags |= BAM_ENTRY_ROOT; 4348 } else if (strcmp(cmd, menu_cmds[FINDROOT_CMD]) 4349 == 0) { 4350 BAM_DPRINTF((D_IS_FINDROOT_CMD, fcn, 4351 arg)); 4352 curr_ent->flags |= BAM_ENTRY_FINDROOT; 4353 } else if (strcmp(cmd, 4354 menu_cmds[CHAINLOADER_CMD]) == 0) { 4355 BAM_DPRINTF((D_IS_CHAINLOADER_CMD, fcn, 4356 arg)); 4357 curr_ent->flags |= 4358 BAM_ENTRY_CHAINLOADER; 4359 } else if (kernel_parser(curr_ent, cmd, arg, 4360 lp->lineNum) != BAM_SUCCESS) { 4361 (void) module_parser(curr_ent, cmd, 4362 arg, lp->lineNum); 4363 } 4364 } 4365 } 4366 } 4367 4368 /* record default, old default, and entry line ranges */ 4369 if (lp->flags == BAM_GLOBAL && lp->cmd != NULL && 4370 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) { 4371 mp->curdefault = lp; 4372 } else if (lp->flags == BAM_COMMENT && 4373 strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) { 4374 mp->olddefault = lp; 4375 } else if (lp->flags == BAM_COMMENT && 4376 strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) { 4377 mp->old_rc_default = lp; 4378 } else if (lp->flags == BAM_ENTRY || 4379 (lp->flags == BAM_COMMENT && 4380 ((strcmp(lp->arg, BAM_BOOTADM_FTR) == 0) || is_libbe_ent))) { 4381 if (is_libbe_ent) { 4382 curr_ent->flags |= BAM_ENTRY_LIBBE; 4383 is_libbe_ent = 0; 4384 } 4385 4386 boot_entry_addline(curr_ent, lp); 4387 } 4388 append_line(mp, lp); 4389 4390 prev = lp; 4391 } 4392 4393 void 4394 update_numbering(menu_t *mp) 4395 { 4396 int lineNum; 4397 int entryNum; 4398 int old_default_value; 4399 line_t *lp, *prev, *default_lp, *default_entry; 4400 char buf[PATH_MAX]; 4401 4402 if (mp->start == NULL) { 4403 return; 4404 } 4405 4406 lineNum = LINE_INIT; 4407 entryNum = ENTRY_INIT; 4408 old_default_value = ENTRY_INIT; 4409 lp = default_lp = default_entry = NULL; 4410 4411 prev = NULL; 4412 for (lp = mp->start; lp; prev = lp, lp = lp->next) { 4413 lp->lineNum = ++lineNum; 4414 4415 /* 4416 * Get the value of the default command 4417 */ 4418 if (lp->entryNum == ENTRY_INIT && lp->cmd != NULL && 4419 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 && 4420 lp->arg) { 4421 old_default_value = atoi(lp->arg); 4422 default_lp = lp; 4423 } 4424 4425 /* 4426 * If not a booting entry, nothing else to fix for this 4427 * entry 4428 */ 4429 if (lp->entryNum == ENTRY_INIT) 4430 continue; 4431 4432 /* 4433 * Record the position of the default entry. 4434 * The following works because global 4435 * commands like default and timeout should precede 4436 * actual boot entries, so old_default_value 4437 * is already known (or default cmd is missing). 4438 */ 4439 if (default_entry == NULL && 4440 old_default_value != ENTRY_INIT && 4441 lp->entryNum == old_default_value) { 4442 default_entry = lp; 4443 } 4444 4445 /* 4446 * Now fixup the entry number 4447 */ 4448 if (lp->cmd != NULL && 4449 strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) { 4450 lp->entryNum = ++entryNum; 4451 /* fixup the bootadm header */ 4452 if (prev && prev->flags == BAM_COMMENT && 4453 prev->arg && 4454 strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) { 4455 prev->entryNum = lp->entryNum; 4456 } 4457 } else { 4458 lp->entryNum = entryNum; 4459 } 4460 } 4461 4462 /* 4463 * No default command in menu, simply return 4464 */ 4465 if (default_lp == NULL) { 4466 return; 4467 } 4468 4469 free(default_lp->arg); 4470 free(default_lp->line); 4471 4472 if (default_entry == NULL) { 4473 default_lp->arg = s_strdup("0"); 4474 } else { 4475 (void) snprintf(buf, sizeof (buf), "%d", 4476 default_entry->entryNum); 4477 default_lp->arg = s_strdup(buf); 4478 } 4479 4480 /* 4481 * The following is required since only the line field gets 4482 * written back to menu.lst 4483 */ 4484 (void) snprintf(buf, sizeof (buf), "%s%s%s", 4485 menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg); 4486 default_lp->line = s_strdup(buf); 4487 } 4488 4489 4490 static menu_t * 4491 menu_read(char *menu_path) 4492 { 4493 FILE *fp; 4494 char buf[BAM_MAXLINE], *cp; 4495 menu_t *mp; 4496 int line, entry, len, n; 4497 4498 mp = s_calloc(1, sizeof (menu_t)); 4499 4500 fp = fopen(menu_path, "r"); 4501 if (fp == NULL) { /* Let the caller handle this error */ 4502 free(mp); 4503 return (NULL); 4504 } 4505 4506 /* Note: GRUB boot entry number starts with 0 */ 4507 line = LINE_INIT; 4508 entry = ENTRY_INIT; 4509 cp = buf; 4510 len = sizeof (buf); 4511 while (s_fgets(cp, len, fp) != NULL) { 4512 n = strlen(cp); 4513 if (cp[n - 1] == '\\') { 4514 len -= n - 1; 4515 assert(len >= 2); 4516 cp += n - 1; 4517 continue; 4518 } 4519 line_parser(mp, buf, &line, &entry); 4520 cp = buf; 4521 len = sizeof (buf); 4522 } 4523 4524 if (fclose(fp) == EOF) { 4525 bam_error(CLOSE_FAIL, menu_path, strerror(errno)); 4526 } 4527 4528 return (mp); 4529 } 4530 4531 static error_t 4532 selector(menu_t *mp, char *opt, int *entry, char **title) 4533 { 4534 char *eq; 4535 char *opt_dup; 4536 int entryNum; 4537 4538 assert(mp); 4539 assert(mp->start); 4540 assert(opt); 4541 4542 opt_dup = s_strdup(opt); 4543 4544 if (entry) 4545 *entry = ENTRY_INIT; 4546 if (title) 4547 *title = NULL; 4548 4549 eq = strchr(opt_dup, '='); 4550 if (eq == NULL) { 4551 bam_error(INVALID_OPT, opt); 4552 free(opt_dup); 4553 return (BAM_ERROR); 4554 } 4555 4556 *eq = '\0'; 4557 if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) { 4558 assert(mp->end); 4559 entryNum = s_strtol(eq + 1); 4560 if (entryNum < 0 || entryNum > mp->end->entryNum) { 4561 bam_error(INVALID_ENTRY, eq + 1); 4562 free(opt_dup); 4563 return (BAM_ERROR); 4564 } 4565 *entry = entryNum; 4566 } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) { 4567 *title = opt + (eq - opt_dup) + 1; 4568 } else { 4569 bam_error(INVALID_OPT, opt); 4570 free(opt_dup); 4571 return (BAM_ERROR); 4572 } 4573 4574 free(opt_dup); 4575 return (BAM_SUCCESS); 4576 } 4577 4578 /* 4579 * If invoked with no titles/entries (opt == NULL) 4580 * only title lines in file are printed. 4581 * 4582 * If invoked with a title or entry #, all 4583 * lines in *every* matching entry are listed 4584 */ 4585 static error_t 4586 list_entry(menu_t *mp, char *menu_path, char *opt) 4587 { 4588 line_t *lp; 4589 int entry = ENTRY_INIT; 4590 int found; 4591 char *title = NULL; 4592 4593 assert(mp); 4594 assert(menu_path); 4595 4596 /* opt is optional */ 4597 BAM_DPRINTF((D_FUNC_ENTRY2, "list_entry", menu_path, 4598 opt ? opt : "<NULL>")); 4599 4600 if (mp->start == NULL) { 4601 bam_error(NO_MENU, menu_path); 4602 return (BAM_ERROR); 4603 } 4604 4605 if (opt != NULL) { 4606 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) { 4607 return (BAM_ERROR); 4608 } 4609 assert((entry != ENTRY_INIT) ^ (title != NULL)); 4610 } else { 4611 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0); 4612 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0); 4613 } 4614 4615 found = 0; 4616 for (lp = mp->start; lp; lp = lp->next) { 4617 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY) 4618 continue; 4619 if (opt == NULL && lp->flags == BAM_TITLE) { 4620 bam_print(PRINT_TITLE, lp->entryNum, 4621 lp->arg); 4622 found = 1; 4623 continue; 4624 } 4625 if (entry != ENTRY_INIT && lp->entryNum == entry) { 4626 bam_print(PRINT, lp->line); 4627 found = 1; 4628 continue; 4629 } 4630 4631 /* 4632 * We set the entry value here so that all lines 4633 * in entry get printed. If we subsequently match 4634 * title in other entries, all lines in those 4635 * entries get printed as well. 4636 */ 4637 if (title && lp->flags == BAM_TITLE && lp->arg && 4638 strncmp(title, lp->arg, strlen(title)) == 0) { 4639 bam_print(PRINT, lp->line); 4640 entry = lp->entryNum; 4641 found = 1; 4642 continue; 4643 } 4644 } 4645 4646 if (!found) { 4647 bam_error(NO_MATCH_ENTRY); 4648 return (BAM_ERROR); 4649 } 4650 4651 return (BAM_SUCCESS); 4652 } 4653 4654 int 4655 add_boot_entry(menu_t *mp, 4656 char *title, 4657 char *findroot, 4658 char *kernel, 4659 char *mod_kernel, 4660 char *module, 4661 char *bootfs) 4662 { 4663 int lineNum; 4664 int entryNum; 4665 char linebuf[BAM_MAXLINE]; 4666 menu_cmd_t k_cmd; 4667 menu_cmd_t m_cmd; 4668 const char *fcn = "add_boot_entry()"; 4669 4670 assert(mp); 4671 4672 INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL); 4673 if (findroot == NULL) { 4674 bam_error(NULL_FINDROOT); 4675 return (BAM_ERROR); 4676 } 4677 4678 if (title == NULL) { 4679 title = "Solaris"; /* default to Solaris */ 4680 } 4681 if (kernel == NULL) { 4682 bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]); 4683 return (BAM_ERROR); 4684 } 4685 if (module == NULL) { 4686 if (bam_direct != BAM_DIRECT_DBOOT) { 4687 bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]); 4688 return (BAM_ERROR); 4689 } 4690 4691 /* Figure the commands out from the kernel line */ 4692 if (strstr(kernel, "$ISADIR") != NULL) { 4693 module = DIRECT_BOOT_ARCHIVE; 4694 k_cmd = KERNEL_DOLLAR_CMD; 4695 m_cmd = MODULE_DOLLAR_CMD; 4696 } else if (strstr(kernel, "amd64") != NULL) { 4697 module = DIRECT_BOOT_ARCHIVE_64; 4698 k_cmd = KERNEL_CMD; 4699 m_cmd = MODULE_CMD; 4700 } else { 4701 module = DIRECT_BOOT_ARCHIVE_32; 4702 k_cmd = KERNEL_CMD; 4703 m_cmd = MODULE_CMD; 4704 } 4705 } else if ((bam_direct == BAM_DIRECT_DBOOT) && 4706 (strstr(kernel, "$ISADIR") != NULL)) { 4707 /* 4708 * If it's a non-failsafe dboot kernel, use the "kernel$" 4709 * command. Otherwise, use "kernel". 4710 */ 4711 k_cmd = KERNEL_DOLLAR_CMD; 4712 m_cmd = MODULE_DOLLAR_CMD; 4713 } else { 4714 k_cmd = KERNEL_CMD; 4715 m_cmd = MODULE_CMD; 4716 } 4717 4718 if (mp->start) { 4719 lineNum = mp->end->lineNum; 4720 entryNum = mp->end->entryNum; 4721 } else { 4722 lineNum = LINE_INIT; 4723 entryNum = ENTRY_INIT; 4724 } 4725 4726 /* 4727 * No separator for comment (HDR/FTR) commands 4728 * The syntax for comments is #<comment> 4729 */ 4730 (void) snprintf(linebuf, sizeof (linebuf), "%s%s", 4731 menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR); 4732 line_parser(mp, linebuf, &lineNum, &entryNum); 4733 4734 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 4735 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title); 4736 line_parser(mp, linebuf, &lineNum, &entryNum); 4737 4738 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 4739 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot); 4740 line_parser(mp, linebuf, &lineNum, &entryNum); 4741 BAM_DPRINTF((D_ADD_FINDROOT_NUM, fcn, lineNum, entryNum)); 4742 4743 if (bootfs != NULL) { 4744 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 4745 menu_cmds[BOOTFS_CMD], menu_cmds[SEP_CMD], bootfs); 4746 line_parser(mp, linebuf, &lineNum, &entryNum); 4747 } 4748 4749 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 4750 menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel); 4751 line_parser(mp, linebuf, &lineNum, &entryNum); 4752 4753 if (mod_kernel != NULL) { 4754 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 4755 menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel); 4756 line_parser(mp, linebuf, &lineNum, &entryNum); 4757 } 4758 4759 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 4760 menu_cmds[m_cmd], menu_cmds[SEP_CMD], module); 4761 line_parser(mp, linebuf, &lineNum, &entryNum); 4762 4763 (void) snprintf(linebuf, sizeof (linebuf), "%s%s", 4764 menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR); 4765 line_parser(mp, linebuf, &lineNum, &entryNum); 4766 4767 return (entryNum); 4768 } 4769 4770 error_t 4771 delete_boot_entry(menu_t *mp, int entryNum, int quiet) 4772 { 4773 line_t *lp; 4774 line_t *freed; 4775 entry_t *ent; 4776 entry_t *tmp; 4777 int deleted = 0; 4778 const char *fcn = "delete_boot_entry()"; 4779 4780 assert(entryNum != ENTRY_INIT); 4781 4782 tmp = NULL; 4783 4784 ent = mp->entries; 4785 while (ent) { 4786 lp = ent->start; 4787 4788 /* 4789 * Check entry number and make sure it's a modifiable entry. 4790 * 4791 * Guidelines: 4792 * + We can modify a bootadm-created entry 4793 * + We can modify a libbe-created entry 4794 */ 4795 if ((lp->flags != BAM_COMMENT && 4796 (((ent->flags & BAM_ENTRY_LIBBE) == 0) && 4797 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0)) || 4798 (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) { 4799 ent = ent->next; 4800 continue; 4801 } 4802 4803 /* free the entry content */ 4804 do { 4805 freed = lp; 4806 lp = lp->next; /* prev stays the same */ 4807 BAM_DPRINTF((D_FREEING_LINE, fcn, freed->lineNum)); 4808 unlink_line(mp, freed); 4809 line_free(freed); 4810 } while (freed != ent->end); 4811 4812 /* free the entry_t structure */ 4813 assert(tmp == NULL); 4814 tmp = ent; 4815 ent = ent->next; 4816 if (tmp->prev) 4817 tmp->prev->next = ent; 4818 else 4819 mp->entries = ent; 4820 if (ent) 4821 ent->prev = tmp->prev; 4822 BAM_DPRINTF((D_FREEING_ENTRY, fcn, tmp->entryNum)); 4823 free(tmp); 4824 tmp = NULL; 4825 deleted = 1; 4826 } 4827 4828 assert(tmp == NULL); 4829 4830 if (!deleted && entryNum != ALL_ENTRIES) { 4831 if (quiet == DBE_PRINTERR) 4832 bam_error(NO_BOOTADM_MATCH); 4833 return (BAM_ERROR); 4834 } 4835 4836 /* 4837 * Now that we have deleted an entry, update 4838 * the entry numbering and the default cmd. 4839 */ 4840 update_numbering(mp); 4841 4842 return (BAM_SUCCESS); 4843 } 4844 4845 static error_t 4846 delete_all_entries(menu_t *mp, char *dummy, char *opt) 4847 { 4848 assert(mp); 4849 assert(dummy == NULL); 4850 assert(opt == NULL); 4851 4852 BAM_DPRINTF((D_FUNC_ENTRY0, "delete_all_entries")); 4853 4854 if (mp->start == NULL) { 4855 bam_print(EMPTY_MENU); 4856 return (BAM_SUCCESS); 4857 } 4858 4859 if (delete_boot_entry(mp, ALL_ENTRIES, DBE_PRINTERR) != BAM_SUCCESS) { 4860 return (BAM_ERROR); 4861 } 4862 4863 return (BAM_WRITE); 4864 } 4865 4866 static FILE * 4867 create_diskmap(char *osroot) 4868 { 4869 FILE *fp; 4870 char cmd[PATH_MAX + 16]; 4871 char path[PATH_MAX]; 4872 const char *fcn = "create_diskmap()"; 4873 4874 /* make sure we have a map file */ 4875 fp = fopen(GRUBDISK_MAP, "r"); 4876 if (fp == NULL) { 4877 int ret; 4878 4879 ret = snprintf(path, sizeof (path), "%s/%s", osroot, 4880 CREATE_DISKMAP); 4881 if (ret >= sizeof (path)) { 4882 bam_error(PATH_TOO_LONG, osroot); 4883 return (NULL); 4884 } 4885 if (is_safe_exec(path) == BAM_ERROR) 4886 return (NULL); 4887 4888 (void) snprintf(cmd, sizeof (cmd), 4889 "%s/%s > /dev/null", osroot, CREATE_DISKMAP); 4890 if (exec_cmd(cmd, NULL) != 0) 4891 return (NULL); 4892 fp = fopen(GRUBDISK_MAP, "r"); 4893 INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL); 4894 if (fp) { 4895 BAM_DPRINTF((D_CREATED_DISKMAP, fcn, GRUBDISK_MAP)); 4896 } else { 4897 BAM_DPRINTF((D_CREATE_DISKMAP_FAIL, fcn, GRUBDISK_MAP)); 4898 } 4899 } 4900 return (fp); 4901 } 4902 4903 #define SECTOR_SIZE 512 4904 4905 static int 4906 get_partition(char *device) 4907 { 4908 int i, fd, is_pcfs, partno = -1; 4909 struct mboot *mboot; 4910 char boot_sect[SECTOR_SIZE]; 4911 char *wholedisk, *slice; 4912 #ifdef i386 4913 ext_part_t *epp; 4914 uint32_t secnum, numsec; 4915 int rval, pno, ext_partno = -1; 4916 #endif 4917 4918 /* form whole disk (p0) */ 4919 slice = device + strlen(device) - 2; 4920 is_pcfs = (*slice != 's'); 4921 if (!is_pcfs) 4922 *slice = '\0'; 4923 wholedisk = s_calloc(1, strlen(device) + 3); 4924 (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device); 4925 if (!is_pcfs) 4926 *slice = 's'; 4927 4928 /* read boot sector */ 4929 fd = open(wholedisk, O_RDONLY); 4930 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) { 4931 return (partno); 4932 } 4933 (void) close(fd); 4934 4935 #ifdef i386 4936 /* Read/Initialize extended partition information */ 4937 if ((rval = libfdisk_init(&epp, wholedisk, NULL, FDISK_READ_DISK)) 4938 != FDISK_SUCCESS) { 4939 switch (rval) { 4940 /* 4941 * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can 4942 * be considered as soft errors and hence 4943 * we do not return 4944 */ 4945 case FDISK_EBADLOGDRIVE: 4946 break; 4947 case FDISK_ENOLOGDRIVE: 4948 break; 4949 case FDISK_EBADMAGIC: 4950 /*FALLTHROUGH*/ 4951 default: 4952 free(wholedisk); 4953 libfdisk_fini(&epp); 4954 return (partno); 4955 break; 4956 } 4957 } 4958 #endif 4959 free(wholedisk); 4960 4961 /* parse fdisk table */ 4962 mboot = (struct mboot *)((void *)boot_sect); 4963 for (i = 0; i < FD_NUMPART; i++) { 4964 struct ipart *part = 4965 (struct ipart *)(uintptr_t)mboot->parts + i; 4966 if (is_pcfs) { /* looking for solaris boot part */ 4967 if (part->systid == 0xbe) { 4968 partno = i; 4969 break; 4970 } 4971 } else { /* look for solaris partition, old and new */ 4972 #ifdef i386 4973 if ((part->systid == SUNIXOS && 4974 (fdisk_is_linux_swap(epp, part->relsect, 4975 NULL) != 0)) || part->systid == SUNIXOS2) { 4976 #else 4977 if (part->systid == SUNIXOS || 4978 part->systid == SUNIXOS2) { 4979 #endif 4980 partno = i; 4981 break; 4982 } 4983 4984 #ifdef i386 4985 if (fdisk_is_dos_extended(part->systid)) 4986 ext_partno = i; 4987 #endif 4988 } 4989 } 4990 #ifdef i386 4991 /* If no primary solaris partition, check extended partition */ 4992 if ((partno == -1) && (ext_partno != -1)) { 4993 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec); 4994 if (rval == FDISK_SUCCESS) { 4995 partno = pno - 1; 4996 } 4997 } 4998 libfdisk_fini(&epp); 4999 #endif 5000 return (partno); 5001 } 5002 5003 char * 5004 get_grubroot(char *osroot, char *osdev, char *menu_root) 5005 { 5006 char *grubroot; /* (hd#,#,#) */ 5007 char *slice; 5008 char *grubhd; 5009 int fdiskpart; 5010 int found = 0; 5011 char *devname; 5012 char *ctdname = strstr(osdev, "dsk/"); 5013 char linebuf[PATH_MAX]; 5014 FILE *fp; 5015 5016 INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL); 5017 if (ctdname == NULL) { 5018 bam_error(INVALID_DEV_DSK, osdev); 5019 return (NULL); 5020 } 5021 5022 if (menu_root && !menu_on_bootdisk(osroot, menu_root)) { 5023 /* menu bears no resemblance to our reality */ 5024 bam_error(CANNOT_GRUBROOT_BOOTDISK, osdev); 5025 return (NULL); 5026 } 5027 5028 ctdname += strlen("dsk/"); 5029 slice = strrchr(ctdname, 's'); 5030 if (slice) 5031 *slice = '\0'; 5032 5033 fp = create_diskmap(osroot); 5034 if (fp == NULL) { 5035 bam_error(DISKMAP_FAIL, osroot); 5036 return (NULL); 5037 } 5038 5039 rewind(fp); 5040 while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) { 5041 grubhd = strtok(linebuf, " \t\n"); 5042 if (grubhd) 5043 devname = strtok(NULL, " \t\n"); 5044 else 5045 devname = NULL; 5046 if (devname && strcmp(devname, ctdname) == 0) { 5047 found = 1; 5048 break; 5049 } 5050 } 5051 5052 if (slice) 5053 *slice = 's'; 5054 5055 (void) fclose(fp); 5056 fp = NULL; 5057 5058 INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0); 5059 if (found == 0) { 5060 bam_error(BIOSDEV_SKIP, osdev); 5061 return (NULL); 5062 } 5063 5064 fdiskpart = get_partition(osdev); 5065 INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = -1); 5066 if (fdiskpart == -1) { 5067 bam_error(FDISKPART_FAIL, osdev); 5068 return (NULL); 5069 } 5070 5071 grubroot = s_calloc(1, 10); 5072 if (slice) { 5073 (void) snprintf(grubroot, 10, "(hd%s,%d,%c)", 5074 grubhd, fdiskpart, slice[1] + 'a' - '0'); 5075 } else 5076 (void) snprintf(grubroot, 10, "(hd%s,%d)", 5077 grubhd, fdiskpart); 5078 5079 assert(fp == NULL); 5080 assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0); 5081 return (grubroot); 5082 } 5083 5084 static char * 5085 find_primary_common(char *mntpt, char *fstype) 5086 { 5087 char signdir[PATH_MAX]; 5088 char tmpsign[MAXNAMELEN + 1]; 5089 char *lu; 5090 char *ufs; 5091 char *zfs; 5092 DIR *dirp = NULL; 5093 struct dirent *entp; 5094 struct stat sb; 5095 const char *fcn = "find_primary_common()"; 5096 5097 (void) snprintf(signdir, sizeof (signdir), "%s/%s", 5098 mntpt, GRUBSIGN_DIR); 5099 5100 if (stat(signdir, &sb) == -1) { 5101 BAM_DPRINTF((D_NO_SIGNDIR, fcn, signdir)); 5102 return (NULL); 5103 } 5104 5105 dirp = opendir(signdir); 5106 INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL); 5107 if (dirp == NULL) { 5108 bam_error(OPENDIR_FAILED, signdir, strerror(errno)); 5109 return (NULL); 5110 } 5111 5112 ufs = zfs = lu = NULL; 5113 5114 while (entp = readdir(dirp)) { 5115 if (strcmp(entp->d_name, ".") == 0 || 5116 strcmp(entp->d_name, "..") == 0) 5117 continue; 5118 5119 (void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name); 5120 5121 if (lu == NULL && 5122 strncmp(tmpsign, GRUBSIGN_LU_PREFIX, 5123 strlen(GRUBSIGN_LU_PREFIX)) == 0) { 5124 lu = s_strdup(tmpsign); 5125 } 5126 5127 if (ufs == NULL && 5128 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX, 5129 strlen(GRUBSIGN_UFS_PREFIX)) == 0) { 5130 ufs = s_strdup(tmpsign); 5131 } 5132 5133 if (zfs == NULL && 5134 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX, 5135 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) { 5136 zfs = s_strdup(tmpsign); 5137 } 5138 } 5139 5140 BAM_DPRINTF((D_EXIST_PRIMARY_SIGNS, fcn, 5141 zfs ? zfs : "NULL", 5142 ufs ? ufs : "NULL", 5143 lu ? lu : "NULL")); 5144 5145 if (dirp) { 5146 (void) closedir(dirp); 5147 dirp = NULL; 5148 } 5149 5150 if (strcmp(fstype, "ufs") == 0 && zfs) { 5151 bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs"); 5152 free(zfs); 5153 zfs = NULL; 5154 } else if (strcmp(fstype, "zfs") == 0 && ufs) { 5155 bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs"); 5156 free(ufs); 5157 ufs = NULL; 5158 } 5159 5160 assert(dirp == NULL); 5161 5162 /* For now, we let Live Upgrade take care of its signature itself */ 5163 if (lu) { 5164 BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu)); 5165 free(lu); 5166 lu = NULL; 5167 } 5168 5169 return (zfs ? zfs : ufs); 5170 } 5171 5172 static char * 5173 find_backup_common(char *mntpt, char *fstype) 5174 { 5175 FILE *bfp = NULL; 5176 char tmpsign[MAXNAMELEN + 1]; 5177 char backup[PATH_MAX]; 5178 char *ufs; 5179 char *zfs; 5180 char *lu; 5181 int error; 5182 const char *fcn = "find_backup_common()"; 5183 5184 /* 5185 * We didn't find it in the primary directory. 5186 * Look at the backup 5187 */ 5188 (void) snprintf(backup, sizeof (backup), "%s%s", 5189 mntpt, GRUBSIGN_BACKUP); 5190 5191 bfp = fopen(backup, "r"); 5192 if (bfp == NULL) { 5193 error = errno; 5194 if (bam_verbose) { 5195 bam_error(OPEN_FAIL, backup, strerror(error)); 5196 } 5197 BAM_DPRINTF((D_OPEN_FAIL, fcn, backup, strerror(error))); 5198 return (NULL); 5199 } 5200 5201 ufs = zfs = lu = NULL; 5202 5203 while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) { 5204 5205 if (lu == NULL && 5206 strncmp(tmpsign, GRUBSIGN_LU_PREFIX, 5207 strlen(GRUBSIGN_LU_PREFIX)) == 0) { 5208 lu = s_strdup(tmpsign); 5209 } 5210 5211 if (ufs == NULL && 5212 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX, 5213 strlen(GRUBSIGN_UFS_PREFIX)) == 0) { 5214 ufs = s_strdup(tmpsign); 5215 } 5216 5217 if (zfs == NULL && 5218 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX, 5219 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) { 5220 zfs = s_strdup(tmpsign); 5221 } 5222 } 5223 5224 BAM_DPRINTF((D_EXIST_BACKUP_SIGNS, fcn, 5225 zfs ? zfs : "NULL", 5226 ufs ? ufs : "NULL", 5227 lu ? lu : "NULL")); 5228 5229 if (bfp) { 5230 (void) fclose(bfp); 5231 bfp = NULL; 5232 } 5233 5234 if (strcmp(fstype, "ufs") == 0 && zfs) { 5235 bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs"); 5236 free(zfs); 5237 zfs = NULL; 5238 } else if (strcmp(fstype, "zfs") == 0 && ufs) { 5239 bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs"); 5240 free(ufs); 5241 ufs = NULL; 5242 } 5243 5244 assert(bfp == NULL); 5245 5246 /* For now, we let Live Upgrade take care of its signature itself */ 5247 if (lu) { 5248 BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu)); 5249 free(lu); 5250 lu = NULL; 5251 } 5252 5253 return (zfs ? zfs : ufs); 5254 } 5255 5256 static char * 5257 find_ufs_existing(char *osroot) 5258 { 5259 char *sign; 5260 const char *fcn = "find_ufs_existing()"; 5261 5262 sign = find_primary_common(osroot, "ufs"); 5263 if (sign == NULL) { 5264 sign = find_backup_common(osroot, "ufs"); 5265 BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL")); 5266 } else { 5267 BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign)); 5268 } 5269 5270 return (sign); 5271 } 5272 5273 char * 5274 get_mountpoint(char *special, char *fstype) 5275 { 5276 FILE *mntfp; 5277 struct mnttab mp = {0}; 5278 struct mnttab mpref = {0}; 5279 int error; 5280 int ret; 5281 const char *fcn = "get_mountpoint()"; 5282 5283 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, special, fstype)); 5284 5285 mntfp = fopen(MNTTAB, "r"); 5286 error = errno; 5287 INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL); 5288 if (mntfp == NULL) { 5289 bam_error(OPEN_FAIL, MNTTAB, strerror(error)); 5290 return (NULL); 5291 } 5292 5293 mpref.mnt_special = special; 5294 mpref.mnt_fstype = fstype; 5295 5296 ret = getmntany(mntfp, &mp, &mpref); 5297 INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1); 5298 if (ret != 0) { 5299 (void) fclose(mntfp); 5300 BAM_DPRINTF((D_NO_MNTPT, fcn, special, fstype)); 5301 return (NULL); 5302 } 5303 (void) fclose(mntfp); 5304 5305 assert(mp.mnt_mountp); 5306 5307 BAM_DPRINTF((D_GET_MOUNTPOINT_RET, fcn, special, mp.mnt_mountp)); 5308 5309 return (s_strdup(mp.mnt_mountp)); 5310 } 5311 5312 /* 5313 * Mounts a "legacy" top dataset (if needed) 5314 * Returns: The mountpoint of the legacy top dataset or NULL on error 5315 * mnted returns one of the above values defined for zfs_mnted_t 5316 */ 5317 static char * 5318 mount_legacy_dataset(char *pool, zfs_mnted_t *mnted) 5319 { 5320 char cmd[PATH_MAX]; 5321 char tmpmnt[PATH_MAX]; 5322 filelist_t flist = {0}; 5323 char *is_mounted; 5324 struct stat sb; 5325 int ret; 5326 const char *fcn = "mount_legacy_dataset()"; 5327 5328 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool)); 5329 5330 *mnted = ZFS_MNT_ERROR; 5331 5332 (void) snprintf(cmd, sizeof (cmd), 5333 "/sbin/zfs get -Ho value mounted %s", 5334 pool); 5335 5336 ret = exec_cmd(cmd, &flist); 5337 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1); 5338 if (ret != 0) { 5339 bam_error(ZFS_MNTED_FAILED, pool); 5340 return (NULL); 5341 } 5342 5343 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL); 5344 if ((flist.head == NULL) || (flist.head != flist.tail)) { 5345 bam_error(BAD_ZFS_MNTED, pool); 5346 filelist_free(&flist); 5347 return (NULL); 5348 } 5349 5350 is_mounted = strtok(flist.head->line, " \t\n"); 5351 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes"); 5352 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no"); 5353 if (strcmp(is_mounted, "no") != 0) { 5354 filelist_free(&flist); 5355 *mnted = LEGACY_ALREADY; 5356 /* get_mountpoint returns a strdup'ed string */ 5357 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_ALREADY, fcn, pool)); 5358 return (get_mountpoint(pool, "zfs")); 5359 } 5360 5361 filelist_free(&flist); 5362 5363 /* 5364 * legacy top dataset is not mounted. Mount it now 5365 * First create a mountpoint. 5366 */ 5367 (void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d", 5368 ZFS_LEGACY_MNTPT, getpid()); 5369 5370 ret = stat(tmpmnt, &sb); 5371 if (ret == -1) { 5372 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_ABS, fcn, pool, tmpmnt)); 5373 ret = mkdirp(tmpmnt, DIR_PERMS); 5374 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1); 5375 if (ret == -1) { 5376 bam_error(MKDIR_FAILED, tmpmnt, strerror(errno)); 5377 return (NULL); 5378 } 5379 } else { 5380 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_PRES, fcn, pool, tmpmnt)); 5381 } 5382 5383 (void) snprintf(cmd, sizeof (cmd), 5384 "/sbin/mount -F zfs %s %s", 5385 pool, tmpmnt); 5386 5387 ret = exec_cmd(cmd, NULL); 5388 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1); 5389 if (ret != 0) { 5390 bam_error(ZFS_MOUNT_FAILED, pool); 5391 (void) rmdir(tmpmnt); 5392 return (NULL); 5393 } 5394 5395 *mnted = LEGACY_MOUNTED; 5396 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MOUNTED, fcn, pool, tmpmnt)); 5397 return (s_strdup(tmpmnt)); 5398 } 5399 5400 /* 5401 * Mounts the top dataset (if needed) 5402 * Returns: The mountpoint of the top dataset or NULL on error 5403 * mnted returns one of the above values defined for zfs_mnted_t 5404 */ 5405 static char * 5406 mount_top_dataset(char *pool, zfs_mnted_t *mnted) 5407 { 5408 char cmd[PATH_MAX]; 5409 filelist_t flist = {0}; 5410 char *is_mounted; 5411 char *mntpt; 5412 char *zmntpt; 5413 int ret; 5414 const char *fcn = "mount_top_dataset()"; 5415 5416 *mnted = ZFS_MNT_ERROR; 5417 5418 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool)); 5419 5420 /* 5421 * First check if the top dataset is a "legacy" dataset 5422 */ 5423 (void) snprintf(cmd, sizeof (cmd), 5424 "/sbin/zfs get -Ho value mountpoint %s", 5425 pool); 5426 ret = exec_cmd(cmd, &flist); 5427 INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1); 5428 if (ret != 0) { 5429 bam_error(ZFS_MNTPT_FAILED, pool); 5430 return (NULL); 5431 } 5432 5433 if (flist.head && (flist.head == flist.tail)) { 5434 char *legacy = strtok(flist.head->line, " \t\n"); 5435 if (legacy && strcmp(legacy, "legacy") == 0) { 5436 filelist_free(&flist); 5437 BAM_DPRINTF((D_Z_IS_LEGACY, fcn, pool)); 5438 return (mount_legacy_dataset(pool, mnted)); 5439 } 5440 } 5441 5442 filelist_free(&flist); 5443 5444 BAM_DPRINTF((D_Z_IS_NOT_LEGACY, fcn, pool)); 5445 5446 (void) snprintf(cmd, sizeof (cmd), 5447 "/sbin/zfs get -Ho value mounted %s", 5448 pool); 5449 5450 ret = exec_cmd(cmd, &flist); 5451 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1); 5452 if (ret != 0) { 5453 bam_error(ZFS_MNTED_FAILED, pool); 5454 return (NULL); 5455 } 5456 5457 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL); 5458 if ((flist.head == NULL) || (flist.head != flist.tail)) { 5459 bam_error(BAD_ZFS_MNTED, pool); 5460 filelist_free(&flist); 5461 return (NULL); 5462 } 5463 5464 is_mounted = strtok(flist.head->line, " \t\n"); 5465 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes"); 5466 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no"); 5467 if (strcmp(is_mounted, "no") != 0) { 5468 filelist_free(&flist); 5469 *mnted = ZFS_ALREADY; 5470 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_ALREADY, fcn, pool)); 5471 goto mounted; 5472 } 5473 5474 filelist_free(&flist); 5475 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOT_ALREADY, fcn, pool)); 5476 5477 /* top dataset is not mounted. Mount it now */ 5478 (void) snprintf(cmd, sizeof (cmd), 5479 "/sbin/zfs mount %s", pool); 5480 ret = exec_cmd(cmd, NULL); 5481 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1); 5482 if (ret != 0) { 5483 bam_error(ZFS_MOUNT_FAILED, pool); 5484 return (NULL); 5485 } 5486 *mnted = ZFS_MOUNTED; 5487 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOW, fcn, pool)); 5488 /*FALLTHRU*/ 5489 mounted: 5490 /* 5491 * Now get the mountpoint 5492 */ 5493 (void) snprintf(cmd, sizeof (cmd), 5494 "/sbin/zfs get -Ho value mountpoint %s", 5495 pool); 5496 5497 ret = exec_cmd(cmd, &flist); 5498 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1); 5499 if (ret != 0) { 5500 bam_error(ZFS_MNTPT_FAILED, pool); 5501 goto error; 5502 } 5503 5504 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL); 5505 if ((flist.head == NULL) || (flist.head != flist.tail)) { 5506 bam_error(NULL_ZFS_MNTPT, pool); 5507 goto error; 5508 } 5509 5510 mntpt = strtok(flist.head->line, " \t\n"); 5511 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo"); 5512 if (*mntpt != '/') { 5513 bam_error(BAD_ZFS_MNTPT, pool, mntpt); 5514 goto error; 5515 } 5516 zmntpt = s_strdup(mntpt); 5517 5518 filelist_free(&flist); 5519 5520 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MNTPT, fcn, pool, zmntpt)); 5521 5522 return (zmntpt); 5523 5524 error: 5525 filelist_free(&flist); 5526 (void) umount_top_dataset(pool, *mnted, NULL); 5527 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 5528 return (NULL); 5529 } 5530 5531 static int 5532 umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt) 5533 { 5534 char cmd[PATH_MAX]; 5535 int ret; 5536 const char *fcn = "umount_top_dataset()"; 5537 5538 INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR); 5539 switch (mnted) { 5540 case LEGACY_ALREADY: 5541 case ZFS_ALREADY: 5542 /* nothing to do */ 5543 BAM_DPRINTF((D_Z_UMOUNT_TOP_ALREADY_NOP, fcn, pool, 5544 mntpt ? mntpt : "NULL")); 5545 free(mntpt); 5546 return (BAM_SUCCESS); 5547 case LEGACY_MOUNTED: 5548 (void) snprintf(cmd, sizeof (cmd), 5549 "/sbin/umount %s", pool); 5550 ret = exec_cmd(cmd, NULL); 5551 INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1); 5552 if (ret != 0) { 5553 bam_error(UMOUNT_FAILED, pool); 5554 free(mntpt); 5555 return (BAM_ERROR); 5556 } 5557 if (mntpt) 5558 (void) rmdir(mntpt); 5559 free(mntpt); 5560 BAM_DPRINTF((D_Z_UMOUNT_TOP_LEGACY, fcn, pool)); 5561 return (BAM_SUCCESS); 5562 case ZFS_MOUNTED: 5563 free(mntpt); 5564 (void) snprintf(cmd, sizeof (cmd), 5565 "/sbin/zfs unmount %s", pool); 5566 ret = exec_cmd(cmd, NULL); 5567 INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1); 5568 if (ret != 0) { 5569 bam_error(UMOUNT_FAILED, pool); 5570 return (BAM_ERROR); 5571 } 5572 BAM_DPRINTF((D_Z_UMOUNT_TOP_NONLEG, fcn, pool)); 5573 return (BAM_SUCCESS); 5574 default: 5575 bam_error(INT_BAD_MNTSTATE, pool); 5576 return (BAM_ERROR); 5577 } 5578 /*NOTREACHED*/ 5579 } 5580 5581 /* 5582 * For ZFS, osdev can be one of two forms 5583 * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402 5584 * It can be a /dev/[r]dsk special file. We handle both instances 5585 */ 5586 static char * 5587 get_pool(char *osdev) 5588 { 5589 char cmd[PATH_MAX]; 5590 char buf[PATH_MAX]; 5591 filelist_t flist = {0}; 5592 char *pool; 5593 char *cp; 5594 char *slash; 5595 int ret; 5596 const char *fcn = "get_pool()"; 5597 5598 INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL); 5599 if (osdev == NULL) { 5600 bam_error(GET_POOL_OSDEV_NULL); 5601 return (NULL); 5602 } 5603 5604 BAM_DPRINTF((D_GET_POOL_OSDEV, fcn, osdev)); 5605 5606 if (osdev[0] != '/') { 5607 (void) strlcpy(buf, osdev, sizeof (buf)); 5608 slash = strchr(buf, '/'); 5609 if (slash) 5610 *slash = '\0'; 5611 pool = s_strdup(buf); 5612 BAM_DPRINTF((D_GET_POOL_RET, fcn, pool)); 5613 return (pool); 5614 } else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 && 5615 strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) { 5616 bam_error(GET_POOL_BAD_OSDEV, osdev); 5617 return (NULL); 5618 } 5619 5620 /* 5621 * Call the zfs fstyp directly since this is a zpool. This avoids 5622 * potential pcfs conflicts if the first block wasn't cleared. 5623 */ 5624 (void) snprintf(cmd, sizeof (cmd), 5625 "/usr/lib/fs/zfs/fstyp -a %s 2>/dev/null | /bin/grep '^name:'", 5626 osdev); 5627 5628 ret = exec_cmd(cmd, &flist); 5629 INJECT_ERROR1("GET_POOL_FSTYP", ret = 1); 5630 if (ret != 0) { 5631 bam_error(FSTYP_A_FAILED, osdev); 5632 return (NULL); 5633 } 5634 5635 INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL); 5636 if ((flist.head == NULL) || (flist.head != flist.tail)) { 5637 bam_error(NULL_FSTYP_A, osdev); 5638 filelist_free(&flist); 5639 return (NULL); 5640 } 5641 5642 (void) strtok(flist.head->line, "'"); 5643 cp = strtok(NULL, "'"); 5644 INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL); 5645 if (cp == NULL) { 5646 bam_error(BAD_FSTYP_A, osdev); 5647 filelist_free(&flist); 5648 return (NULL); 5649 } 5650 5651 pool = s_strdup(cp); 5652 5653 filelist_free(&flist); 5654 5655 BAM_DPRINTF((D_GET_POOL_RET, fcn, pool)); 5656 5657 return (pool); 5658 } 5659 5660 static char * 5661 find_zfs_existing(char *osdev) 5662 { 5663 char *pool; 5664 zfs_mnted_t mnted; 5665 char *mntpt; 5666 char *sign; 5667 const char *fcn = "find_zfs_existing()"; 5668 5669 pool = get_pool(osdev); 5670 INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL); 5671 if (pool == NULL) { 5672 bam_error(ZFS_GET_POOL_FAILED, osdev); 5673 return (NULL); 5674 } 5675 5676 mntpt = mount_top_dataset(pool, &mnted); 5677 INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL); 5678 if (mntpt == NULL) { 5679 bam_error(ZFS_MOUNT_TOP_DATASET_FAILED, pool); 5680 free(pool); 5681 return (NULL); 5682 } 5683 5684 sign = find_primary_common(mntpt, "zfs"); 5685 if (sign == NULL) { 5686 sign = find_backup_common(mntpt, "zfs"); 5687 BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL")); 5688 } else { 5689 BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign)); 5690 } 5691 5692 (void) umount_top_dataset(pool, mnted, mntpt); 5693 5694 free(pool); 5695 5696 return (sign); 5697 } 5698 5699 static char * 5700 find_existing_sign(char *osroot, char *osdev, char *fstype) 5701 { 5702 const char *fcn = "find_existing_sign()"; 5703 5704 INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs"); 5705 if (strcmp(fstype, "ufs") == 0) { 5706 BAM_DPRINTF((D_CHECK_UFS_EXIST_SIGN, fcn)); 5707 return (find_ufs_existing(osroot)); 5708 } else if (strcmp(fstype, "zfs") == 0) { 5709 BAM_DPRINTF((D_CHECK_ZFS_EXIST_SIGN, fcn)); 5710 return (find_zfs_existing(osdev)); 5711 } else { 5712 bam_error(GRUBSIGN_NOTSUP, fstype); 5713 return (NULL); 5714 } 5715 } 5716 5717 #define MH_HASH_SZ 16 5718 5719 typedef enum { 5720 MH_ERROR = -1, 5721 MH_NOMATCH, 5722 MH_MATCH 5723 } mh_search_t; 5724 5725 typedef struct mcache { 5726 char *mc_special; 5727 char *mc_mntpt; 5728 char *mc_fstype; 5729 struct mcache *mc_next; 5730 } mcache_t; 5731 5732 typedef struct mhash { 5733 mcache_t *mh_hash[MH_HASH_SZ]; 5734 } mhash_t; 5735 5736 static int 5737 mhash_fcn(char *key) 5738 { 5739 int i; 5740 uint64_t sum = 0; 5741 5742 for (i = 0; key[i] != '\0'; i++) { 5743 sum += (uchar_t)key[i]; 5744 } 5745 5746 sum %= MH_HASH_SZ; 5747 5748 assert(sum < MH_HASH_SZ); 5749 5750 return (sum); 5751 } 5752 5753 static mhash_t * 5754 cache_mnttab(void) 5755 { 5756 FILE *mfp; 5757 struct extmnttab mnt; 5758 mcache_t *mcp; 5759 mhash_t *mhp; 5760 char *ctds; 5761 int idx; 5762 int error; 5763 char *special_dup; 5764 const char *fcn = "cache_mnttab()"; 5765 5766 mfp = fopen(MNTTAB, "r"); 5767 error = errno; 5768 INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL); 5769 if (mfp == NULL) { 5770 bam_error(OPEN_FAIL, MNTTAB, strerror(error)); 5771 return (NULL); 5772 } 5773 5774 mhp = s_calloc(1, sizeof (mhash_t)); 5775 5776 resetmnttab(mfp); 5777 5778 while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) { 5779 /* only cache ufs */ 5780 if (strcmp(mnt.mnt_fstype, "ufs") != 0) 5781 continue; 5782 5783 /* basename() modifies its arg, so dup it */ 5784 special_dup = s_strdup(mnt.mnt_special); 5785 ctds = basename(special_dup); 5786 5787 mcp = s_calloc(1, sizeof (mcache_t)); 5788 mcp->mc_special = s_strdup(ctds); 5789 mcp->mc_mntpt = s_strdup(mnt.mnt_mountp); 5790 mcp->mc_fstype = s_strdup(mnt.mnt_fstype); 5791 BAM_DPRINTF((D_CACHE_MNTS, fcn, ctds, 5792 mnt.mnt_mountp, mnt.mnt_fstype)); 5793 idx = mhash_fcn(ctds); 5794 mcp->mc_next = mhp->mh_hash[idx]; 5795 mhp->mh_hash[idx] = mcp; 5796 free(special_dup); 5797 } 5798 5799 (void) fclose(mfp); 5800 5801 return (mhp); 5802 } 5803 5804 static void 5805 free_mnttab(mhash_t *mhp) 5806 { 5807 mcache_t *mcp; 5808 int i; 5809 5810 for (i = 0; i < MH_HASH_SZ; i++) { 5811 /*LINTED*/ 5812 while (mcp = mhp->mh_hash[i]) { 5813 mhp->mh_hash[i] = mcp->mc_next; 5814 free(mcp->mc_special); 5815 free(mcp->mc_mntpt); 5816 free(mcp->mc_fstype); 5817 free(mcp); 5818 } 5819 } 5820 5821 for (i = 0; i < MH_HASH_SZ; i++) { 5822 assert(mhp->mh_hash[i] == NULL); 5823 } 5824 free(mhp); 5825 } 5826 5827 static mh_search_t 5828 search_hash(mhash_t *mhp, char *special, char **mntpt) 5829 { 5830 int idx; 5831 mcache_t *mcp; 5832 const char *fcn = "search_hash()"; 5833 5834 assert(mntpt); 5835 5836 *mntpt = NULL; 5837 5838 INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo"); 5839 if (strchr(special, '/')) { 5840 bam_error(INVALID_MHASH_KEY, special); 5841 return (MH_ERROR); 5842 } 5843 5844 idx = mhash_fcn(special); 5845 5846 for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) { 5847 if (strcmp(mcp->mc_special, special) == 0) 5848 break; 5849 } 5850 5851 if (mcp == NULL) { 5852 BAM_DPRINTF((D_MNTTAB_HASH_NOMATCH, fcn, special)); 5853 return (MH_NOMATCH); 5854 } 5855 5856 assert(strcmp(mcp->mc_fstype, "ufs") == 0); 5857 *mntpt = mcp->mc_mntpt; 5858 BAM_DPRINTF((D_MNTTAB_HASH_MATCH, fcn, special)); 5859 return (MH_MATCH); 5860 } 5861 5862 static int 5863 check_add_ufs_sign_to_list(FILE *tfp, char *mntpt) 5864 { 5865 char *sign; 5866 char *signline; 5867 char signbuf[MAXNAMELEN]; 5868 int len; 5869 int error; 5870 const char *fcn = "check_add_ufs_sign_to_list()"; 5871 5872 /* safe to specify NULL as "osdev" arg for UFS */ 5873 sign = find_existing_sign(mntpt, NULL, "ufs"); 5874 if (sign == NULL) { 5875 /* No existing signature, nothing to add to list */ 5876 BAM_DPRINTF((D_NO_SIGN_TO_LIST, fcn, mntpt)); 5877 return (0); 5878 } 5879 5880 (void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign); 5881 signline = signbuf; 5882 5883 INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n"); 5884 if (strncmp(signline, GRUBSIGN_UFS_PREFIX, 5885 strlen(GRUBSIGN_UFS_PREFIX))) { 5886 bam_error(INVALID_UFS_SIGNATURE, sign); 5887 free(sign); 5888 /* ignore invalid signatures */ 5889 return (0); 5890 } 5891 5892 len = fputs(signline, tfp); 5893 error = errno; 5894 INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0); 5895 if (len != strlen(signline)) { 5896 bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error)); 5897 free(sign); 5898 return (-1); 5899 } 5900 5901 free(sign); 5902 5903 BAM_DPRINTF((D_SIGN_LIST_PUTS_DONE, fcn, mntpt)); 5904 return (0); 5905 } 5906 5907 /* 5908 * slice is a basename not a full pathname 5909 */ 5910 static int 5911 process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt) 5912 { 5913 int ret; 5914 char cmd[PATH_MAX]; 5915 char path[PATH_MAX]; 5916 struct stat sbuf; 5917 char *mntpt; 5918 filelist_t flist = {0}; 5919 char *fstype; 5920 char blkslice[PATH_MAX]; 5921 const char *fcn = "process_slice_common()"; 5922 5923 5924 ret = search_hash(mhp, slice, &mntpt); 5925 switch (ret) { 5926 case MH_MATCH: 5927 if (check_add_ufs_sign_to_list(tfp, mntpt) == -1) 5928 return (-1); 5929 else 5930 return (0); 5931 case MH_NOMATCH: 5932 break; 5933 case MH_ERROR: 5934 default: 5935 return (-1); 5936 } 5937 5938 (void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice); 5939 if (stat(path, &sbuf) == -1) { 5940 BAM_DPRINTF((D_SLICE_ENOENT, fcn, path)); 5941 return (0); 5942 } 5943 5944 /* Check if ufs. Call ufs fstyp directly to avoid pcfs conflicts. */ 5945 (void) snprintf(cmd, sizeof (cmd), 5946 "/usr/lib/fs/ufs/fstyp /dev/rdsk/%s 2>/dev/null", 5947 slice); 5948 5949 if (exec_cmd(cmd, &flist) != 0) { 5950 if (bam_verbose) 5951 bam_print(FSTYP_FAILED, slice); 5952 return (0); 5953 } 5954 5955 if ((flist.head == NULL) || (flist.head != flist.tail)) { 5956 if (bam_verbose) 5957 bam_print(FSTYP_BAD, slice); 5958 filelist_free(&flist); 5959 return (0); 5960 } 5961 5962 fstype = strtok(flist.head->line, " \t\n"); 5963 if (fstype == NULL || strcmp(fstype, "ufs") != 0) { 5964 if (bam_verbose) 5965 bam_print(NOT_UFS_SLICE, slice, fstype); 5966 filelist_free(&flist); 5967 return (0); 5968 } 5969 5970 filelist_free(&flist); 5971 5972 /* 5973 * Since we are mounting the filesystem read-only, the 5974 * the last mount field of the superblock is unchanged 5975 * and does not need to be fixed up post-mount; 5976 */ 5977 5978 (void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s", 5979 slice); 5980 5981 (void) snprintf(cmd, sizeof (cmd), 5982 "/usr/sbin/mount -F ufs -o ro %s %s " 5983 "> /dev/null 2>&1", blkslice, tmpmnt); 5984 5985 if (exec_cmd(cmd, NULL) != 0) { 5986 if (bam_verbose) 5987 bam_print(MOUNT_FAILED, blkslice, "ufs"); 5988 return (0); 5989 } 5990 5991 ret = check_add_ufs_sign_to_list(tfp, tmpmnt); 5992 5993 (void) snprintf(cmd, sizeof (cmd), 5994 "/usr/sbin/umount -f %s > /dev/null 2>&1", 5995 tmpmnt); 5996 5997 if (exec_cmd(cmd, NULL) != 0) { 5998 bam_print(UMOUNT_FAILED, slice); 5999 return (0); 6000 } 6001 6002 return (ret); 6003 } 6004 6005 static int 6006 process_vtoc_slices( 6007 char *s0, 6008 struct vtoc *vtoc, 6009 FILE *tfp, 6010 mhash_t *mhp, 6011 char *tmpmnt) 6012 { 6013 int idx; 6014 char slice[PATH_MAX]; 6015 size_t len; 6016 char *cp; 6017 const char *fcn = "process_vtoc_slices()"; 6018 6019 len = strlen(s0); 6020 6021 assert(s0[len - 2] == 's' && s0[len - 1] == '0'); 6022 6023 s0[len - 1] = '\0'; 6024 6025 (void) strlcpy(slice, s0, sizeof (slice)); 6026 6027 s0[len - 1] = '0'; 6028 6029 cp = slice + len - 1; 6030 6031 for (idx = 0; idx < vtoc->v_nparts; idx++) { 6032 6033 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx); 6034 6035 if (vtoc->v_part[idx].p_size == 0) { 6036 BAM_DPRINTF((D_VTOC_SIZE_ZERO, fcn, slice)); 6037 continue; 6038 } 6039 6040 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */ 6041 switch (vtoc->v_part[idx].p_tag) { 6042 case V_SWAP: 6043 case V_USR: 6044 case V_BACKUP: 6045 case V_VAR: 6046 case V_HOME: 6047 case V_ALTSCTR: 6048 BAM_DPRINTF((D_VTOC_NOT_ROOT_TAG, fcn, slice)); 6049 continue; 6050 default: 6051 BAM_DPRINTF((D_VTOC_ROOT_TAG, fcn, slice)); 6052 break; 6053 } 6054 6055 /* skip unmountable and readonly slices */ 6056 switch (vtoc->v_part[idx].p_flag) { 6057 case V_UNMNT: 6058 case V_RONLY: 6059 BAM_DPRINTF((D_VTOC_NOT_RDWR_FLAG, fcn, slice)); 6060 continue; 6061 default: 6062 BAM_DPRINTF((D_VTOC_RDWR_FLAG, fcn, slice)); 6063 break; 6064 } 6065 6066 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) { 6067 return (-1); 6068 } 6069 } 6070 6071 return (0); 6072 } 6073 6074 static int 6075 process_efi_slices( 6076 char *s0, 6077 struct dk_gpt *efi, 6078 FILE *tfp, 6079 mhash_t *mhp, 6080 char *tmpmnt) 6081 { 6082 int idx; 6083 char slice[PATH_MAX]; 6084 size_t len; 6085 char *cp; 6086 const char *fcn = "process_efi_slices()"; 6087 6088 len = strlen(s0); 6089 6090 assert(s0[len - 2] == 's' && s0[len - 1] == '0'); 6091 6092 s0[len - 1] = '\0'; 6093 6094 (void) strlcpy(slice, s0, sizeof (slice)); 6095 6096 s0[len - 1] = '0'; 6097 6098 cp = slice + len - 1; 6099 6100 for (idx = 0; idx < efi->efi_nparts; idx++) { 6101 6102 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx); 6103 6104 if (efi->efi_parts[idx].p_size == 0) { 6105 BAM_DPRINTF((D_EFI_SIZE_ZERO, fcn, slice)); 6106 continue; 6107 } 6108 6109 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */ 6110 switch (efi->efi_parts[idx].p_tag) { 6111 case V_SWAP: 6112 case V_USR: 6113 case V_BACKUP: 6114 case V_VAR: 6115 case V_HOME: 6116 case V_ALTSCTR: 6117 BAM_DPRINTF((D_EFI_NOT_ROOT_TAG, fcn, slice)); 6118 continue; 6119 default: 6120 BAM_DPRINTF((D_EFI_ROOT_TAG, fcn, slice)); 6121 break; 6122 } 6123 6124 /* skip unmountable and readonly slices */ 6125 switch (efi->efi_parts[idx].p_flag) { 6126 case V_UNMNT: 6127 case V_RONLY: 6128 BAM_DPRINTF((D_EFI_NOT_RDWR_FLAG, fcn, slice)); 6129 continue; 6130 default: 6131 BAM_DPRINTF((D_EFI_RDWR_FLAG, fcn, slice)); 6132 break; 6133 } 6134 6135 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) { 6136 return (-1); 6137 } 6138 } 6139 6140 return (0); 6141 } 6142 6143 /* 6144 * s0 is a basename not a full path 6145 */ 6146 static int 6147 process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt) 6148 { 6149 struct vtoc vtoc; 6150 struct dk_gpt *efi; 6151 char s0path[PATH_MAX]; 6152 struct stat sbuf; 6153 int e_flag; 6154 int v_flag; 6155 int retval; 6156 int err; 6157 int fd; 6158 const char *fcn = "process_slice0()"; 6159 6160 (void) snprintf(s0path, sizeof (s0path), "/dev/rdsk/%s", s0); 6161 6162 if (stat(s0path, &sbuf) == -1) { 6163 BAM_DPRINTF((D_SLICE0_ENOENT, fcn, s0path)); 6164 return (0); 6165 } 6166 6167 fd = open(s0path, O_NONBLOCK|O_RDONLY); 6168 if (fd == -1) { 6169 bam_error(OPEN_FAIL, s0path, strerror(errno)); 6170 return (0); 6171 } 6172 6173 e_flag = v_flag = 0; 6174 retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err; 6175 switch (retval) { 6176 case VT_EIO: 6177 BAM_DPRINTF((D_VTOC_READ_FAIL, fcn, s0path)); 6178 break; 6179 case VT_EINVAL: 6180 BAM_DPRINTF((D_VTOC_INVALID, fcn, s0path)); 6181 break; 6182 case VT_ERROR: 6183 BAM_DPRINTF((D_VTOC_UNKNOWN_ERR, fcn, s0path)); 6184 break; 6185 case VT_ENOTSUP: 6186 e_flag = 1; 6187 BAM_DPRINTF((D_VTOC_NOTSUP, fcn, s0path)); 6188 break; 6189 case 0: 6190 v_flag = 1; 6191 BAM_DPRINTF((D_VTOC_READ_SUCCESS, fcn, s0path)); 6192 break; 6193 default: 6194 BAM_DPRINTF((D_VTOC_UNKNOWN_RETCODE, fcn, s0path)); 6195 break; 6196 } 6197 6198 6199 if (e_flag) { 6200 e_flag = 0; 6201 retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err; 6202 switch (retval) { 6203 case VT_EIO: 6204 BAM_DPRINTF((D_EFI_READ_FAIL, fcn, s0path)); 6205 break; 6206 case VT_EINVAL: 6207 BAM_DPRINTF((D_EFI_INVALID, fcn, s0path)); 6208 break; 6209 case VT_ERROR: 6210 BAM_DPRINTF((D_EFI_UNKNOWN_ERR, fcn, s0path)); 6211 break; 6212 case VT_ENOTSUP: 6213 BAM_DPRINTF((D_EFI_NOTSUP, fcn, s0path)); 6214 break; 6215 case 0: 6216 e_flag = 1; 6217 BAM_DPRINTF((D_EFI_READ_SUCCESS, fcn, s0path)); 6218 break; 6219 default: 6220 BAM_DPRINTF((D_EFI_UNKNOWN_RETCODE, fcn, s0path)); 6221 break; 6222 } 6223 } 6224 6225 (void) close(fd); 6226 6227 if (v_flag) { 6228 retval = process_vtoc_slices(s0, 6229 &vtoc, tfp, mhp, tmpmnt); 6230 } else if (e_flag) { 6231 retval = process_efi_slices(s0, 6232 efi, tfp, mhp, tmpmnt); 6233 } else { 6234 BAM_DPRINTF((D_NOT_VTOC_OR_EFI, fcn, s0path)); 6235 return (0); 6236 } 6237 6238 return (retval); 6239 } 6240 6241 /* 6242 * Find and create a list of all existing UFS boot signatures 6243 */ 6244 static int 6245 FindAllUfsSignatures(void) 6246 { 6247 mhash_t *mnttab_hash; 6248 DIR *dirp = NULL; 6249 struct dirent *dp; 6250 char tmpmnt[PATH_MAX]; 6251 char cmd[PATH_MAX]; 6252 struct stat sb; 6253 int fd; 6254 FILE *tfp; 6255 size_t len; 6256 int ret; 6257 int error; 6258 const char *fcn = "FindAllUfsSignatures()"; 6259 6260 if (stat(UFS_SIGNATURE_LIST, &sb) != -1) { 6261 bam_print(SIGNATURE_LIST_EXISTS, UFS_SIGNATURE_LIST); 6262 return (0); 6263 } 6264 6265 fd = open(UFS_SIGNATURE_LIST".tmp", 6266 O_RDWR|O_CREAT|O_TRUNC, 0644); 6267 error = errno; 6268 INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1); 6269 if (fd == -1) { 6270 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error)); 6271 return (-1); 6272 } 6273 6274 ret = close(fd); 6275 error = errno; 6276 INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1); 6277 if (ret == -1) { 6278 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp", 6279 strerror(error)); 6280 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6281 return (-1); 6282 } 6283 6284 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a"); 6285 error = errno; 6286 INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL); 6287 if (tfp == NULL) { 6288 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error)); 6289 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6290 return (-1); 6291 } 6292 6293 mnttab_hash = cache_mnttab(); 6294 INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL); 6295 if (mnttab_hash == NULL) { 6296 (void) fclose(tfp); 6297 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6298 bam_error(CACHE_MNTTAB_FAIL, fcn); 6299 return (-1); 6300 } 6301 6302 (void) snprintf(tmpmnt, sizeof (tmpmnt), 6303 "/tmp/bootadm_ufs_sign_mnt.%d", getpid()); 6304 (void) unlink(tmpmnt); 6305 6306 ret = mkdirp(tmpmnt, DIR_PERMS); 6307 error = errno; 6308 INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1); 6309 if (ret == -1) { 6310 bam_error(MKDIR_FAILED, tmpmnt, strerror(error)); 6311 free_mnttab(mnttab_hash); 6312 (void) fclose(tfp); 6313 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6314 return (-1); 6315 } 6316 6317 dirp = opendir("/dev/rdsk"); 6318 error = errno; 6319 INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL); 6320 if (dirp == NULL) { 6321 bam_error(OPENDIR_FAILED, "/dev/rdsk", strerror(error)); 6322 goto fail; 6323 } 6324 6325 while (dp = readdir(dirp)) { 6326 if (strcmp(dp->d_name, ".") == 0 || 6327 strcmp(dp->d_name, "..") == 0) 6328 continue; 6329 6330 /* 6331 * we only look for the s0 slice. This is guranteed to 6332 * have 's' at len - 2. 6333 */ 6334 len = strlen(dp->d_name); 6335 if (dp->d_name[len - 2 ] != 's' || dp->d_name[len - 1] != '0') { 6336 BAM_DPRINTF((D_SKIP_SLICE_NOTZERO, fcn, dp->d_name)); 6337 continue; 6338 } 6339 6340 ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt); 6341 INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1); 6342 if (ret == -1) 6343 goto fail; 6344 } 6345 6346 (void) closedir(dirp); 6347 free_mnttab(mnttab_hash); 6348 (void) rmdir(tmpmnt); 6349 6350 ret = fclose(tfp); 6351 error = errno; 6352 INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF); 6353 if (ret == EOF) { 6354 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp", 6355 strerror(error)); 6356 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6357 return (-1); 6358 } 6359 6360 /* We have a list of existing GRUB signatures. Sort it first */ 6361 (void) snprintf(cmd, sizeof (cmd), 6362 "/usr/bin/sort -u %s.tmp > %s.sorted", 6363 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST); 6364 6365 ret = exec_cmd(cmd, NULL); 6366 INJECT_ERROR1("SORT_SIGN_LIST", ret = 1); 6367 if (ret != 0) { 6368 bam_error(GRUBSIGN_SORT_FAILED); 6369 (void) unlink(UFS_SIGNATURE_LIST".sorted"); 6370 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6371 return (-1); 6372 } 6373 6374 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6375 6376 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST); 6377 error = errno; 6378 INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1); 6379 if (ret == -1) { 6380 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error)); 6381 (void) unlink(UFS_SIGNATURE_LIST".sorted"); 6382 return (-1); 6383 } 6384 6385 if (stat(UFS_SIGNATURE_LIST, &sb) == 0 && sb.st_size == 0) { 6386 BAM_DPRINTF((D_ZERO_LEN_SIGNLIST, fcn, UFS_SIGNATURE_LIST)); 6387 } 6388 6389 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6390 return (0); 6391 6392 fail: 6393 if (dirp) 6394 (void) closedir(dirp); 6395 free_mnttab(mnttab_hash); 6396 (void) rmdir(tmpmnt); 6397 (void) fclose(tfp); 6398 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6399 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 6400 return (-1); 6401 } 6402 6403 static char * 6404 create_ufs_sign(void) 6405 { 6406 struct stat sb; 6407 int signnum = -1; 6408 char tmpsign[MAXNAMELEN + 1]; 6409 char *numstr; 6410 int i; 6411 FILE *tfp; 6412 int ret; 6413 int error; 6414 const char *fcn = "create_ufs_sign()"; 6415 6416 bam_print(SEARCHING_UFS_SIGN); 6417 6418 ret = FindAllUfsSignatures(); 6419 INJECT_ERROR1("FIND_ALL_UFS", ret = -1); 6420 if (ret == -1) { 6421 bam_error(ERR_FIND_UFS_SIGN); 6422 return (NULL); 6423 } 6424 6425 /* Make sure the list exists and is owned by root */ 6426 INJECT_ERROR1("SIGNLIST_NOT_CREATED", 6427 (void) unlink(UFS_SIGNATURE_LIST)); 6428 if (stat(UFS_SIGNATURE_LIST, &sb) == -1 || sb.st_uid != 0) { 6429 (void) unlink(UFS_SIGNATURE_LIST); 6430 bam_error(UFS_SIGNATURE_LIST_MISS, UFS_SIGNATURE_LIST); 6431 return (NULL); 6432 } 6433 6434 if (sb.st_size == 0) { 6435 bam_print(GRUBSIGN_UFS_NONE); 6436 i = 0; 6437 goto found; 6438 } 6439 6440 /* The signature list was sorted when it was created */ 6441 tfp = fopen(UFS_SIGNATURE_LIST, "r"); 6442 error = errno; 6443 INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL); 6444 if (tfp == NULL) { 6445 bam_error(UFS_SIGNATURE_LIST_OPENERR, 6446 UFS_SIGNATURE_LIST, strerror(error)); 6447 (void) unlink(UFS_SIGNATURE_LIST); 6448 return (NULL); 6449 } 6450 6451 for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) { 6452 6453 if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX, 6454 strlen(GRUBSIGN_UFS_PREFIX)) != 0) { 6455 (void) fclose(tfp); 6456 (void) unlink(UFS_SIGNATURE_LIST); 6457 bam_error(UFS_BADSIGN, tmpsign); 6458 return (NULL); 6459 } 6460 numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX); 6461 6462 if (numstr[0] == '\0' || !isdigit(numstr[0])) { 6463 (void) fclose(tfp); 6464 (void) unlink(UFS_SIGNATURE_LIST); 6465 bam_error(UFS_BADSIGN, tmpsign); 6466 return (NULL); 6467 } 6468 6469 signnum = atoi(numstr); 6470 INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1); 6471 if (signnum < 0) { 6472 (void) fclose(tfp); 6473 (void) unlink(UFS_SIGNATURE_LIST); 6474 bam_error(UFS_BADSIGN, tmpsign); 6475 return (NULL); 6476 } 6477 6478 if (i != signnum) { 6479 BAM_DPRINTF((D_FOUND_HOLE_SIGNLIST, fcn, i)); 6480 break; 6481 } 6482 } 6483 6484 (void) fclose(tfp); 6485 6486 found: 6487 (void) snprintf(tmpsign, sizeof (tmpsign), "rootfs%d", i); 6488 6489 /* add the ufs signature to the /var/run list of signatures */ 6490 ret = ufs_add_to_sign_list(tmpsign); 6491 INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret = -1); 6492 if (ret == -1) { 6493 (void) unlink(UFS_SIGNATURE_LIST); 6494 bam_error(FAILED_ADD_SIGNLIST, tmpsign); 6495 return (NULL); 6496 } 6497 6498 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6499 6500 return (s_strdup(tmpsign)); 6501 } 6502 6503 static char * 6504 get_fstype(char *osroot) 6505 { 6506 FILE *mntfp; 6507 struct mnttab mp = {0}; 6508 struct mnttab mpref = {0}; 6509 int error; 6510 int ret; 6511 const char *fcn = "get_fstype()"; 6512 6513 INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot = NULL); 6514 if (osroot == NULL) { 6515 bam_error(GET_FSTYPE_ARGS); 6516 return (NULL); 6517 } 6518 6519 mntfp = fopen(MNTTAB, "r"); 6520 error = errno; 6521 INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL); 6522 if (mntfp == NULL) { 6523 bam_error(OPEN_FAIL, MNTTAB, strerror(error)); 6524 return (NULL); 6525 } 6526 6527 if (*osroot == '\0') 6528 mpref.mnt_mountp = "/"; 6529 else 6530 mpref.mnt_mountp = osroot; 6531 6532 ret = getmntany(mntfp, &mp, &mpref); 6533 INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1); 6534 if (ret != 0) { 6535 bam_error(MNTTAB_MNTPT_NOT_FOUND, osroot, MNTTAB); 6536 (void) fclose(mntfp); 6537 return (NULL); 6538 } 6539 (void) fclose(mntfp); 6540 6541 INJECT_ERROR1("GET_FSTYPE_NULL", mp.mnt_fstype = NULL); 6542 if (mp.mnt_fstype == NULL) { 6543 bam_error(MNTTAB_FSTYPE_NULL, osroot); 6544 return (NULL); 6545 } 6546 6547 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6548 6549 return (s_strdup(mp.mnt_fstype)); 6550 } 6551 6552 static char * 6553 create_zfs_sign(char *osdev) 6554 { 6555 char tmpsign[PATH_MAX]; 6556 char *pool; 6557 const char *fcn = "create_zfs_sign()"; 6558 6559 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, osdev)); 6560 6561 /* 6562 * First find the pool name 6563 */ 6564 pool = get_pool(osdev); 6565 INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool = NULL); 6566 if (pool == NULL) { 6567 bam_error(GET_POOL_FAILED, osdev); 6568 return (NULL); 6569 } 6570 6571 (void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool); 6572 6573 BAM_DPRINTF((D_CREATED_ZFS_SIGN, fcn, tmpsign)); 6574 6575 free(pool); 6576 6577 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6578 6579 return (s_strdup(tmpsign)); 6580 } 6581 6582 static char * 6583 create_new_sign(char *osdev, char *fstype) 6584 { 6585 char *sign; 6586 const char *fcn = "create_new_sign()"; 6587 6588 INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype = "foofs"); 6589 6590 if (strcmp(fstype, "zfs") == 0) { 6591 BAM_DPRINTF((D_CREATE_NEW_ZFS, fcn)); 6592 sign = create_zfs_sign(osdev); 6593 } else if (strcmp(fstype, "ufs") == 0) { 6594 BAM_DPRINTF((D_CREATE_NEW_UFS, fcn)); 6595 sign = create_ufs_sign(); 6596 } else { 6597 bam_error(GRUBSIGN_NOTSUP, fstype); 6598 sign = NULL; 6599 } 6600 6601 BAM_DPRINTF((D_CREATED_NEW_SIGN, fcn, sign ? sign : "<NULL>")); 6602 return (sign); 6603 } 6604 6605 static int 6606 set_backup_common(char *mntpt, char *sign) 6607 { 6608 FILE *bfp; 6609 char backup[PATH_MAX]; 6610 char tmpsign[PATH_MAX]; 6611 int error; 6612 char *bdir; 6613 char *backup_dup; 6614 struct stat sb; 6615 int ret; 6616 const char *fcn = "set_backup_common()"; 6617 6618 (void) snprintf(backup, sizeof (backup), "%s%s", 6619 mntpt, GRUBSIGN_BACKUP); 6620 6621 /* First read the backup */ 6622 bfp = fopen(backup, "r"); 6623 if (bfp != NULL) { 6624 while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) { 6625 if (strcmp(tmpsign, sign) == 0) { 6626 BAM_DPRINTF((D_FOUND_IN_BACKUP, fcn, sign)); 6627 (void) fclose(bfp); 6628 return (0); 6629 } 6630 } 6631 (void) fclose(bfp); 6632 BAM_DPRINTF((D_NOT_FOUND_IN_EXIST_BACKUP, fcn, sign)); 6633 } else { 6634 BAM_DPRINTF((D_BACKUP_NOT_EXIST, fcn, backup)); 6635 } 6636 6637 /* 6638 * Didn't find the correct signature. First create 6639 * the directory if necessary. 6640 */ 6641 6642 /* dirname() modifies its argument so dup it */ 6643 backup_dup = s_strdup(backup); 6644 bdir = dirname(backup_dup); 6645 assert(bdir); 6646 6647 ret = stat(bdir, &sb); 6648 INJECT_ERROR1("SET_BACKUP_STAT", ret = -1); 6649 if (ret == -1) { 6650 BAM_DPRINTF((D_BACKUP_DIR_NOEXIST, fcn, bdir)); 6651 ret = mkdirp(bdir, DIR_PERMS); 6652 error = errno; 6653 INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1); 6654 if (ret == -1) { 6655 bam_error(GRUBSIGN_BACKUP_MKDIRERR, 6656 GRUBSIGN_BACKUP, strerror(error)); 6657 free(backup_dup); 6658 return (-1); 6659 } 6660 } 6661 free(backup_dup); 6662 6663 /* 6664 * Open the backup in append mode to add the correct 6665 * signature; 6666 */ 6667 bfp = fopen(backup, "a"); 6668 error = errno; 6669 INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL); 6670 if (bfp == NULL) { 6671 bam_error(GRUBSIGN_BACKUP_OPENERR, 6672 GRUBSIGN_BACKUP, strerror(error)); 6673 return (-1); 6674 } 6675 6676 (void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign); 6677 6678 ret = fputs(tmpsign, bfp); 6679 error = errno; 6680 INJECT_ERROR1("SET_BACKUP_FPUTS", ret = 0); 6681 if (ret != strlen(tmpsign)) { 6682 bam_error(GRUBSIGN_BACKUP_WRITEERR, 6683 GRUBSIGN_BACKUP, strerror(error)); 6684 (void) fclose(bfp); 6685 return (-1); 6686 } 6687 6688 (void) fclose(bfp); 6689 6690 if (bam_verbose) 6691 bam_print(GRUBSIGN_BACKUP_UPDATED, GRUBSIGN_BACKUP); 6692 6693 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6694 6695 return (0); 6696 } 6697 6698 static int 6699 set_backup_ufs(char *osroot, char *sign) 6700 { 6701 const char *fcn = "set_backup_ufs()"; 6702 6703 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign)); 6704 return (set_backup_common(osroot, sign)); 6705 } 6706 6707 static int 6708 set_backup_zfs(char *osdev, char *sign) 6709 { 6710 char *pool; 6711 char *mntpt; 6712 zfs_mnted_t mnted; 6713 int ret; 6714 const char *fcn = "set_backup_zfs()"; 6715 6716 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign)); 6717 6718 pool = get_pool(osdev); 6719 INJECT_ERROR1("SET_BACKUP_GET_POOL", pool = NULL); 6720 if (pool == NULL) { 6721 bam_error(GET_POOL_FAILED, osdev); 6722 return (-1); 6723 } 6724 6725 mntpt = mount_top_dataset(pool, &mnted); 6726 INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt = NULL); 6727 if (mntpt == NULL) { 6728 bam_error(FAIL_MNT_TOP_DATASET, pool); 6729 free(pool); 6730 return (-1); 6731 } 6732 6733 ret = set_backup_common(mntpt, sign); 6734 6735 (void) umount_top_dataset(pool, mnted, mntpt); 6736 6737 free(pool); 6738 6739 INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1); 6740 if (ret == 0) { 6741 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6742 } else { 6743 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 6744 } 6745 6746 return (ret); 6747 } 6748 6749 static int 6750 set_backup(char *osroot, char *osdev, char *sign, char *fstype) 6751 { 6752 const char *fcn = "set_backup()"; 6753 int ret; 6754 6755 INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype = "foofs"); 6756 6757 if (strcmp(fstype, "ufs") == 0) { 6758 BAM_DPRINTF((D_SET_BACKUP_UFS, fcn)); 6759 ret = set_backup_ufs(osroot, sign); 6760 } else if (strcmp(fstype, "zfs") == 0) { 6761 BAM_DPRINTF((D_SET_BACKUP_ZFS, fcn)); 6762 ret = set_backup_zfs(osdev, sign); 6763 } else { 6764 bam_error(GRUBSIGN_NOTSUP, fstype); 6765 ret = -1; 6766 } 6767 6768 if (ret == 0) { 6769 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6770 } else { 6771 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 6772 } 6773 6774 return (ret); 6775 } 6776 6777 static int 6778 set_primary_common(char *mntpt, char *sign) 6779 { 6780 char signfile[PATH_MAX]; 6781 char signdir[PATH_MAX]; 6782 struct stat sb; 6783 int fd; 6784 int error; 6785 int ret; 6786 const char *fcn = "set_primary_common()"; 6787 6788 (void) snprintf(signfile, sizeof (signfile), "%s/%s/%s", 6789 mntpt, GRUBSIGN_DIR, sign); 6790 6791 if (stat(signfile, &sb) != -1) { 6792 if (bam_verbose) 6793 bam_print(PRIMARY_SIGN_EXISTS, sign); 6794 return (0); 6795 } else { 6796 BAM_DPRINTF((D_PRIMARY_NOT_EXIST, fcn, signfile)); 6797 } 6798 6799 (void) snprintf(signdir, sizeof (signdir), "%s/%s", 6800 mntpt, GRUBSIGN_DIR); 6801 6802 if (stat(signdir, &sb) == -1) { 6803 BAM_DPRINTF((D_PRIMARY_DIR_NOEXIST, fcn, signdir)); 6804 ret = mkdirp(signdir, DIR_PERMS); 6805 error = errno; 6806 INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1); 6807 if (ret == -1) { 6808 bam_error(GRUBSIGN_MKDIR_ERR, signdir, strerror(errno)); 6809 return (-1); 6810 } 6811 } 6812 6813 fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444); 6814 error = errno; 6815 INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1); 6816 if (fd == -1) { 6817 bam_error(GRUBSIGN_PRIMARY_CREATERR, signfile, strerror(error)); 6818 return (-1); 6819 } 6820 6821 ret = fsync(fd); 6822 error = errno; 6823 INJECT_ERROR1("PRIMARY_FSYNC", ret = -1); 6824 if (ret != 0) { 6825 bam_error(GRUBSIGN_PRIMARY_SYNCERR, signfile, strerror(error)); 6826 } 6827 6828 (void) close(fd); 6829 6830 if (bam_verbose) 6831 bam_print(GRUBSIGN_CREATED_PRIMARY, signfile); 6832 6833 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6834 6835 return (0); 6836 } 6837 6838 static int 6839 set_primary_ufs(char *osroot, char *sign) 6840 { 6841 const char *fcn = "set_primary_ufs()"; 6842 6843 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign)); 6844 return (set_primary_common(osroot, sign)); 6845 } 6846 6847 static int 6848 set_primary_zfs(char *osdev, char *sign) 6849 { 6850 char *pool; 6851 char *mntpt; 6852 zfs_mnted_t mnted; 6853 int ret; 6854 const char *fcn = "set_primary_zfs()"; 6855 6856 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign)); 6857 6858 pool = get_pool(osdev); 6859 INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool = NULL); 6860 if (pool == NULL) { 6861 bam_error(GET_POOL_FAILED, osdev); 6862 return (-1); 6863 } 6864 6865 /* Pool name must exist in the sign */ 6866 ret = (strstr(sign, pool) != NULL); 6867 INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret = 0); 6868 if (ret == 0) { 6869 bam_error(POOL_SIGN_INCOMPAT, pool, sign); 6870 free(pool); 6871 return (-1); 6872 } 6873 6874 mntpt = mount_top_dataset(pool, &mnted); 6875 INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt = NULL); 6876 if (mntpt == NULL) { 6877 bam_error(FAIL_MNT_TOP_DATASET, pool); 6878 free(pool); 6879 return (-1); 6880 } 6881 6882 ret = set_primary_common(mntpt, sign); 6883 6884 (void) umount_top_dataset(pool, mnted, mntpt); 6885 6886 free(pool); 6887 6888 INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1); 6889 if (ret == 0) { 6890 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6891 } else { 6892 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 6893 } 6894 6895 return (ret); 6896 } 6897 6898 static int 6899 set_primary(char *osroot, char *osdev, char *sign, char *fstype) 6900 { 6901 const char *fcn = "set_primary()"; 6902 int ret; 6903 6904 INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype = "foofs"); 6905 if (strcmp(fstype, "ufs") == 0) { 6906 BAM_DPRINTF((D_SET_PRIMARY_UFS, fcn)); 6907 ret = set_primary_ufs(osroot, sign); 6908 } else if (strcmp(fstype, "zfs") == 0) { 6909 BAM_DPRINTF((D_SET_PRIMARY_ZFS, fcn)); 6910 ret = set_primary_zfs(osdev, sign); 6911 } else { 6912 bam_error(GRUBSIGN_NOTSUP, fstype); 6913 ret = -1; 6914 } 6915 6916 if (ret == 0) { 6917 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 6918 } else { 6919 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 6920 } 6921 6922 return (ret); 6923 } 6924 6925 static int 6926 ufs_add_to_sign_list(char *sign) 6927 { 6928 FILE *tfp; 6929 char signline[MAXNAMELEN]; 6930 char cmd[PATH_MAX]; 6931 int ret; 6932 int error; 6933 const char *fcn = "ufs_add_to_sign_list()"; 6934 6935 INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign = "pool_rpool5"); 6936 if (strncmp(sign, GRUBSIGN_UFS_PREFIX, 6937 strlen(GRUBSIGN_UFS_PREFIX)) != 0) { 6938 bam_error(INVALID_UFS_SIGN, sign); 6939 (void) unlink(UFS_SIGNATURE_LIST); 6940 return (-1); 6941 } 6942 6943 /* 6944 * most failures in this routine are not a fatal error 6945 * We simply unlink the /var/run file and continue 6946 */ 6947 6948 ret = rename(UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST".tmp"); 6949 error = errno; 6950 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1); 6951 if (ret == -1) { 6952 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST".tmp", 6953 strerror(error)); 6954 (void) unlink(UFS_SIGNATURE_LIST); 6955 return (0); 6956 } 6957 6958 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a"); 6959 error = errno; 6960 INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL); 6961 if (tfp == NULL) { 6962 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error)); 6963 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6964 return (0); 6965 } 6966 6967 (void) snprintf(signline, sizeof (signline), "%s\n", sign); 6968 6969 ret = fputs(signline, tfp); 6970 error = errno; 6971 INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret = 0); 6972 if (ret != strlen(signline)) { 6973 bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error)); 6974 (void) fclose(tfp); 6975 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6976 return (0); 6977 } 6978 6979 ret = fclose(tfp); 6980 error = errno; 6981 INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF); 6982 if (ret == EOF) { 6983 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp", 6984 strerror(error)); 6985 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 6986 return (0); 6987 } 6988 6989 /* Sort the list again */ 6990 (void) snprintf(cmd, sizeof (cmd), 6991 "/usr/bin/sort -u %s.tmp > %s.sorted", 6992 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST); 6993 6994 ret = exec_cmd(cmd, NULL); 6995 INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret = 1); 6996 if (ret != 0) { 6997 bam_error(GRUBSIGN_SORT_FAILED); 6998 (void) unlink(UFS_SIGNATURE_LIST".sorted"); 6999 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 7000 return (0); 7001 } 7002 7003 (void) unlink(UFS_SIGNATURE_LIST".tmp"); 7004 7005 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST); 7006 error = errno; 7007 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1); 7008 if (ret == -1) { 7009 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error)); 7010 (void) unlink(UFS_SIGNATURE_LIST".sorted"); 7011 return (0); 7012 } 7013 7014 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 7015 7016 return (0); 7017 } 7018 7019 static int 7020 set_signature(char *osroot, char *osdev, char *sign, char *fstype) 7021 { 7022 int ret; 7023 const char *fcn = "set_signature()"; 7024 7025 BAM_DPRINTF((D_FUNC_ENTRY4, fcn, osroot, osdev, sign, fstype)); 7026 7027 ret = set_backup(osroot, osdev, sign, fstype); 7028 INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret = -1); 7029 if (ret == -1) { 7030 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 7031 bam_error(SET_BACKUP_FAILED, sign, osroot, osdev); 7032 return (-1); 7033 } 7034 7035 ret = set_primary(osroot, osdev, sign, fstype); 7036 INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1); 7037 7038 if (ret == 0) { 7039 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 7040 } else { 7041 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 7042 bam_error(SET_PRIMARY_FAILED, sign, osroot, osdev); 7043 7044 } 7045 return (ret); 7046 } 7047 7048 char * 7049 get_grubsign(char *osroot, char *osdev) 7050 { 7051 char *grubsign; /* (<sign>,#,#) */ 7052 char *slice; 7053 int fdiskpart; 7054 char *sign; 7055 char *fstype; 7056 int ret; 7057 const char *fcn = "get_grubsign()"; 7058 7059 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, osdev)); 7060 fstype = get_fstype(osroot); 7061 INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL); 7062 if (fstype == NULL) { 7063 bam_error(GET_FSTYPE_FAILED, osroot); 7064 return (NULL); 7065 } 7066 7067 sign = find_existing_sign(osroot, osdev, fstype); 7068 INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL); 7069 if (sign == NULL) { 7070 BAM_DPRINTF((D_GET_GRUBSIGN_NO_EXISTING, fcn, osroot, osdev)); 7071 sign = create_new_sign(osdev, fstype); 7072 INJECT_ERROR1("CREATE_NEW_SIGN", sign = NULL); 7073 if (sign == NULL) { 7074 bam_error(GRUBSIGN_CREATE_FAIL, osdev); 7075 free(fstype); 7076 return (NULL); 7077 } 7078 } 7079 7080 ret = set_signature(osroot, osdev, sign, fstype); 7081 INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1); 7082 if (ret == -1) { 7083 bam_error(GRUBSIGN_WRITE_FAIL, osdev); 7084 free(sign); 7085 free(fstype); 7086 (void) unlink(UFS_SIGNATURE_LIST); 7087 return (NULL); 7088 } 7089 7090 free(fstype); 7091 7092 if (bam_verbose) 7093 bam_print(GRUBSIGN_FOUND_OR_CREATED, sign, osdev); 7094 7095 fdiskpart = get_partition(osdev); 7096 INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart = -1); 7097 if (fdiskpart == -1) { 7098 bam_error(FDISKPART_FAIL, osdev); 7099 free(sign); 7100 return (NULL); 7101 } 7102 7103 slice = strrchr(osdev, 's'); 7104 7105 grubsign = s_calloc(1, MAXNAMELEN + 10); 7106 if (slice) { 7107 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)", 7108 sign, fdiskpart, slice[1] + 'a' - '0'); 7109 } else 7110 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)", 7111 sign, fdiskpart); 7112 7113 free(sign); 7114 7115 BAM_DPRINTF((D_GET_GRUBSIGN_SUCCESS, fcn, grubsign)); 7116 7117 return (grubsign); 7118 } 7119 7120 static char * 7121 get_title(char *rootdir) 7122 { 7123 static char title[80]; 7124 char *cp = NULL; 7125 char release[PATH_MAX]; 7126 FILE *fp; 7127 const char *fcn = "get_title()"; 7128 7129 /* open the /etc/release file */ 7130 (void) snprintf(release, sizeof (release), "%s/etc/release", rootdir); 7131 7132 fp = fopen(release, "r"); 7133 if (fp == NULL) { 7134 bam_error(OPEN_FAIL, release, strerror(errno)); 7135 cp = NULL; 7136 goto out; 7137 } 7138 7139 while (s_fgets(title, sizeof (title), fp) != NULL) { 7140 cp = strstr(title, "Solaris"); 7141 if (cp) 7142 break; 7143 } 7144 (void) fclose(fp); 7145 7146 out: 7147 cp = cp ? cp : "Solaris"; 7148 7149 BAM_DPRINTF((D_GET_TITLE, fcn, cp)); 7150 7151 return (cp); 7152 } 7153 7154 char * 7155 get_special(char *mountp) 7156 { 7157 FILE *mntfp; 7158 struct mnttab mp = {0}; 7159 struct mnttab mpref = {0}; 7160 int error; 7161 int ret; 7162 const char *fcn = "get_special()"; 7163 7164 INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL); 7165 if (mountp == NULL) { 7166 bam_error(GET_SPECIAL_NULL_MNTPT); 7167 return (NULL); 7168 } 7169 7170 mntfp = fopen(MNTTAB, "r"); 7171 error = errno; 7172 INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL); 7173 if (mntfp == NULL) { 7174 bam_error(OPEN_FAIL, MNTTAB, strerror(error)); 7175 return (NULL); 7176 } 7177 7178 if (*mountp == '\0') 7179 mpref.mnt_mountp = "/"; 7180 else 7181 mpref.mnt_mountp = mountp; 7182 7183 ret = getmntany(mntfp, &mp, &mpref); 7184 INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1); 7185 if (ret != 0) { 7186 (void) fclose(mntfp); 7187 BAM_DPRINTF((D_GET_SPECIAL_NOT_IN_MNTTAB, fcn, mountp)); 7188 return (NULL); 7189 } 7190 (void) fclose(mntfp); 7191 7192 BAM_DPRINTF((D_GET_SPECIAL, fcn, mp.mnt_special)); 7193 7194 return (s_strdup(mp.mnt_special)); 7195 } 7196 7197 static void 7198 free_physarray(char **physarray, int n) 7199 { 7200 int i; 7201 const char *fcn = "free_physarray()"; 7202 7203 assert(physarray); 7204 assert(n); 7205 7206 BAM_DPRINTF((D_FUNC_ENTRY_N1, fcn, n)); 7207 7208 for (i = 0; i < n; i++) { 7209 free(physarray[i]); 7210 } 7211 free(physarray); 7212 7213 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 7214 } 7215 7216 static int 7217 zfs_get_physical(char *special, char ***physarray, int *n) 7218 { 7219 char sdup[PATH_MAX]; 7220 char cmd[PATH_MAX]; 7221 char dsk[PATH_MAX]; 7222 char *pool; 7223 filelist_t flist = {0}; 7224 line_t *lp; 7225 line_t *startlp; 7226 char *comp1; 7227 int i; 7228 int ret; 7229 const char *fcn = "zfs_get_physical()"; 7230 7231 assert(special); 7232 7233 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special)); 7234 7235 INJECT_ERROR1("INVALID_ZFS_SPECIAL", special = "/foo"); 7236 if (special[0] == '/') { 7237 bam_error(INVALID_ZFS_SPECIAL, special); 7238 return (-1); 7239 } 7240 7241 (void) strlcpy(sdup, special, sizeof (sdup)); 7242 7243 pool = strtok(sdup, "/"); 7244 INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL); 7245 if (pool == NULL) { 7246 bam_error(CANT_FIND_POOL_FROM_SPECIAL, special); 7247 return (-1); 7248 } 7249 7250 (void) snprintf(cmd, sizeof (cmd), "/sbin/zpool status %s", pool); 7251 7252 ret = exec_cmd(cmd, &flist); 7253 INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret = 1); 7254 if (ret != 0) { 7255 bam_error(ZFS_GET_POOL_STATUS, pool); 7256 return (-1); 7257 } 7258 7259 INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist.head = NULL); 7260 if (flist.head == NULL) { 7261 bam_error(BAD_ZPOOL_STATUS, pool); 7262 filelist_free(&flist); 7263 return (-1); 7264 } 7265 7266 for (lp = flist.head; lp; lp = lp->next) { 7267 BAM_DPRINTF((D_STRTOK_ZPOOL_STATUS, fcn, lp->line)); 7268 comp1 = strtok(lp->line, " \t"); 7269 if (comp1 == NULL) { 7270 free(lp->line); 7271 lp->line = NULL; 7272 } else { 7273 comp1 = s_strdup(comp1); 7274 free(lp->line); 7275 lp->line = comp1; 7276 } 7277 } 7278 7279 for (lp = flist.head; lp; lp = lp->next) { 7280 if (lp->line == NULL) 7281 continue; 7282 if (strcmp(lp->line, pool) == 0) { 7283 BAM_DPRINTF((D_FOUND_POOL_IN_ZPOOL_STATUS, fcn, pool)); 7284 break; 7285 } 7286 } 7287 7288 if (lp == NULL) { 7289 bam_error(NO_POOL_IN_ZPOOL_STATUS, pool); 7290 filelist_free(&flist); 7291 return (-1); 7292 } 7293 7294 startlp = lp->next; 7295 for (i = 0, lp = startlp; lp; lp = lp->next) { 7296 if (lp->line == NULL) 7297 continue; 7298 if (strcmp(lp->line, "mirror") == 0) 7299 continue; 7300 if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0) 7301 break; 7302 i++; 7303 BAM_DPRINTF((D_COUNTING_ZFS_PHYS, fcn, i)); 7304 } 7305 7306 if (i == 0) { 7307 bam_error(NO_PHYS_IN_ZPOOL_STATUS, pool); 7308 filelist_free(&flist); 7309 return (-1); 7310 } 7311 7312 *n = i; 7313 *physarray = s_calloc(*n, sizeof (char *)); 7314 for (i = 0, lp = startlp; lp; lp = lp->next) { 7315 if (lp->line == NULL) 7316 continue; 7317 if (strcmp(lp->line, "mirror") == 0) 7318 continue; 7319 if (strcmp(lp->line, "errors:") == 0) 7320 break; 7321 if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 && 7322 strncmp(lp->line, "/dev/rdsk/", 7323 strlen("/dev/rdsk/")) != 0) { 7324 (void) snprintf(dsk, sizeof (dsk), "/dev/rdsk/%s", 7325 lp->line); 7326 } else { 7327 (void) strlcpy(dsk, lp->line, sizeof (dsk)); 7328 } 7329 BAM_DPRINTF((D_ADDING_ZFS_PHYS, fcn, dsk, pool)); 7330 (*physarray)[i++] = s_strdup(dsk); 7331 } 7332 7333 assert(i == *n); 7334 7335 filelist_free(&flist); 7336 7337 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 7338 return (0); 7339 } 7340 7341 /* 7342 * Certain services needed to run metastat successfully may not 7343 * be enabled. Enable them now. 7344 */ 7345 /* 7346 * Checks if the specified service is online 7347 * Returns: 1 if the service is online 7348 * 0 if the service is not online 7349 * -1 on error 7350 */ 7351 static int 7352 is_svc_online(char *svc) 7353 { 7354 char *state; 7355 const char *fcn = "is_svc_online()"; 7356 7357 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, svc)); 7358 7359 state = smf_get_state(svc); 7360 INJECT_ERROR2("GET_SVC_STATE", free(state), state = NULL); 7361 if (state == NULL) { 7362 bam_error(GET_SVC_STATE_ERR, svc); 7363 return (-1); 7364 } 7365 BAM_DPRINTF((D_GOT_SVC_STATUS, fcn, svc)); 7366 7367 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) { 7368 BAM_DPRINTF((D_SVC_ONLINE, fcn, svc)); 7369 free(state); 7370 return (1); 7371 } 7372 7373 BAM_DPRINTF((D_SVC_NOT_ONLINE, fcn, state, svc)); 7374 7375 free(state); 7376 7377 return (0); 7378 } 7379 7380 static int 7381 enable_svc(char *svc) 7382 { 7383 int ret; 7384 int sleeptime; 7385 const char *fcn = "enable_svc()"; 7386 7387 ret = is_svc_online(svc); 7388 if (ret == -1) { 7389 bam_error(SVC_IS_ONLINE_FAILED, svc); 7390 return (-1); 7391 } else if (ret == 1) { 7392 BAM_DPRINTF((D_SVC_ALREADY_ONLINE, fcn, svc)); 7393 return (0); 7394 } 7395 7396 /* Service is not enabled. Enable it now. */ 7397 ret = smf_enable_instance(svc, 0); 7398 INJECT_ERROR1("ENABLE_SVC_FAILED", ret = -1); 7399 if (ret != 0) { 7400 bam_error(ENABLE_SVC_FAILED, svc); 7401 return (-1); 7402 } 7403 7404 BAM_DPRINTF((D_SVC_ONLINE_INITIATED, fcn, svc)); 7405 7406 sleeptime = 0; 7407 do { 7408 ret = is_svc_online(svc); 7409 INJECT_ERROR1("SVC_ONLINE_SUCCESS", ret = 1); 7410 INJECT_ERROR1("SVC_ONLINE_FAILURE", ret = -1); 7411 INJECT_ERROR1("SVC_ONLINE_NOTYET", ret = 0); 7412 if (ret == -1) { 7413 bam_error(ERR_SVC_GET_ONLINE, svc); 7414 return (-1); 7415 } else if (ret == 1) { 7416 BAM_DPRINTF((D_SVC_NOW_ONLINE, fcn, svc)); 7417 return (1); 7418 } 7419 (void) sleep(1); 7420 } while (++sleeptime < 60); 7421 7422 bam_error(TIMEOUT_ENABLE_SVC, svc); 7423 7424 return (-1); 7425 } 7426 7427 static int 7428 ufs_get_physical(char *special, char ***physarray, int *n) 7429 { 7430 char cmd[PATH_MAX]; 7431 char *shortname; 7432 filelist_t flist = {0}; 7433 char *meta; 7434 char *type; 7435 char *comp1; 7436 char *comp2; 7437 char *comp3; 7438 char *comp4; 7439 int i; 7440 line_t *lp; 7441 int ret; 7442 char *svc; 7443 const char *fcn = "ufs_get_physical()"; 7444 7445 assert(special); 7446 7447 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special)); 7448 7449 if (strncmp(special, "/dev/md/", strlen("/dev/md/")) != 0) { 7450 bam_error(UFS_GET_PHYS_NOT_SVM, special); 7451 return (-1); 7452 } 7453 7454 if (strncmp(special, "/dev/md/dsk/", strlen("/dev/md/dsk/")) == 0) { 7455 shortname = special + strlen("/dev/md/dsk/"); 7456 } else if (strncmp(special, "/dev/md/rdsk/", 7457 strlen("/dev/md/rdsk/")) == 0) { 7458 shortname = special + strlen("/dev/md/rdsk"); 7459 } else { 7460 bam_error(UFS_GET_PHYS_INVALID_SVM, special); 7461 return (-1); 7462 } 7463 7464 BAM_DPRINTF((D_UFS_SVM_SHORT, fcn, special, shortname)); 7465 7466 svc = "network/rpc/meta:default"; 7467 if (enable_svc(svc) == -1) { 7468 bam_error(UFS_SVM_METASTAT_SVC_ERR, svc); 7469 } 7470 7471 (void) snprintf(cmd, sizeof (cmd), "/sbin/metastat -p %s", shortname); 7472 7473 ret = exec_cmd(cmd, &flist); 7474 INJECT_ERROR1("UFS_SVM_METASTAT", ret = 1); 7475 if (ret != 0) { 7476 bam_error(UFS_SVM_METASTAT_ERR, shortname); 7477 return (-1); 7478 } 7479 7480 INJECT_ERROR1("UFS_SVM_METASTAT_OUT", flist.head = NULL); 7481 if (flist.head == NULL) { 7482 bam_error(BAD_UFS_SVM_METASTAT, shortname); 7483 filelist_free(&flist); 7484 return (-1); 7485 } 7486 7487 /* 7488 * Check if not a mirror. We only parse a single metadevice 7489 * if not a mirror 7490 */ 7491 meta = strtok(flist.head->line, " \t"); 7492 type = strtok(NULL, " \t"); 7493 if (meta == NULL || type == NULL) { 7494 bam_error(ERROR_PARSE_UFS_SVM_METASTAT, shortname); 7495 filelist_free(&flist); 7496 return (-1); 7497 } 7498 if (strcmp(type, "-m") != 0) { 7499 comp1 = strtok(NULL, " \t"); 7500 comp2 = strtok(NULL, " \t"); 7501 if (comp1 == NULL || comp2 != NULL) { 7502 bam_error(INVALID_UFS_SVM_METASTAT, shortname); 7503 filelist_free(&flist); 7504 return (-1); 7505 } 7506 BAM_DPRINTF((D_UFS_SVM_ONE_COMP, fcn, comp1, shortname)); 7507 *physarray = s_calloc(1, sizeof (char *)); 7508 (*physarray)[0] = s_strdup(comp1); 7509 *n = 1; 7510 filelist_free(&flist); 7511 return (0); 7512 } 7513 7514 /* 7515 * Okay we have a mirror. Everything after the first line 7516 * is a submirror 7517 */ 7518 for (i = 0, lp = flist.head->next; lp; lp = lp->next) { 7519 if (strstr(lp->line, "/dev/dsk/") == NULL && 7520 strstr(lp->line, "/dev/rdsk/") == NULL) { 7521 bam_error(CANNOT_PARSE_UFS_SVM_METASTAT, shortname); 7522 filelist_free(&flist); 7523 return (-1); 7524 } 7525 i++; 7526 } 7527 7528 *physarray = s_calloc(i, sizeof (char *)); 7529 *n = i; 7530 7531 for (i = 0, lp = flist.head->next; lp; lp = lp->next) { 7532 comp1 = strtok(lp->line, " \t"); 7533 comp2 = strtok(NULL, " \t"); 7534 comp3 = strtok(NULL, " \t"); 7535 comp4 = strtok(NULL, " \t"); 7536 7537 if (comp3 == NULL || comp4 == NULL || 7538 (strncmp(comp4, "/dev/dsk/", strlen("/dev/dsk/")) != 0 && 7539 strncmp(comp4, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0)) { 7540 bam_error(CANNOT_PARSE_UFS_SVM_SUBMIRROR, shortname); 7541 filelist_free(&flist); 7542 free_physarray(*physarray, *n); 7543 return (-1); 7544 } 7545 7546 (*physarray)[i++] = s_strdup(comp4); 7547 } 7548 7549 assert(i == *n); 7550 7551 filelist_free(&flist); 7552 7553 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 7554 return (0); 7555 } 7556 7557 static int 7558 get_physical(char *menu_root, char ***physarray, int *n) 7559 { 7560 char *special; 7561 int ret; 7562 const char *fcn = "get_physical()"; 7563 7564 assert(menu_root); 7565 assert(physarray); 7566 assert(n); 7567 7568 *physarray = NULL; 7569 *n = 0; 7570 7571 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_root)); 7572 7573 /* First get the device special file from /etc/mnttab */ 7574 special = get_special(menu_root); 7575 INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special = NULL); 7576 if (special == NULL) { 7577 bam_error(GET_SPECIAL_NULL, menu_root); 7578 return (-1); 7579 } 7580 7581 /* If already a physical device nothing to do */ 7582 if (strncmp(special, "/dev/dsk/", strlen("/dev/dsk/")) == 0 || 7583 strncmp(special, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) { 7584 BAM_DPRINTF((D_GET_PHYSICAL_ALREADY, fcn, menu_root, special)); 7585 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 7586 *physarray = s_calloc(1, sizeof (char *)); 7587 (*physarray)[0] = special; 7588 *n = 1; 7589 return (0); 7590 } 7591 7592 if (is_zfs(menu_root)) { 7593 ret = zfs_get_physical(special, physarray, n); 7594 } else if (is_ufs(menu_root)) { 7595 ret = ufs_get_physical(special, physarray, n); 7596 } else { 7597 bam_error(GET_PHYSICAL_NOTSUP_FSTYPE, menu_root, special); 7598 ret = -1; 7599 } 7600 7601 free(special); 7602 7603 INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1); 7604 if (ret == -1) { 7605 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 7606 } else { 7607 int i; 7608 assert (*n > 0); 7609 for (i = 0; i < *n; i++) { 7610 BAM_DPRINTF((D_GET_PHYSICAL_RET, fcn, (*physarray)[i])); 7611 } 7612 } 7613 7614 return (ret); 7615 } 7616 7617 static int 7618 is_bootdisk(char *osroot, char *physical) 7619 { 7620 int ret; 7621 char *grubroot; 7622 char *bootp; 7623 const char *fcn = "is_bootdisk()"; 7624 7625 assert(osroot); 7626 assert(physical); 7627 7628 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, physical)); 7629 7630 bootp = strstr(physical, "p0:boot"); 7631 if (bootp) 7632 *bootp = '\0'; 7633 /* 7634 * We just want the BIOS mapping for menu disk. 7635 * Don't pass menu_root to get_grubroot() as the 7636 * check that it is used for is not relevant here. 7637 * The osroot is immaterial as well - it is only used to 7638 * to find create_diskmap script. Everything hinges on 7639 * "physical" 7640 */ 7641 grubroot = get_grubroot(osroot, physical, NULL); 7642 7643 INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL); 7644 if (grubroot == NULL) { 7645 if (bam_verbose) 7646 bam_error(NO_GRUBROOT_FOR_DISK, physical); 7647 return (0); 7648 } 7649 ret = grubroot[3] == '0'; 7650 free(grubroot); 7651 7652 BAM_DPRINTF((D_RETURN_RET, fcn, ret)); 7653 7654 return (ret); 7655 } 7656 7657 /* 7658 * Check if menu is on the boot device 7659 * Return 0 (false) on error 7660 */ 7661 static int 7662 menu_on_bootdisk(char *osroot, char *menu_root) 7663 { 7664 char **physarray; 7665 int ret; 7666 int n; 7667 int i; 7668 int on_bootdisk; 7669 const char *fcn = "menu_on_bootdisk()"; 7670 7671 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root)); 7672 7673 ret = get_physical(menu_root, &physarray, &n); 7674 INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret = -1); 7675 if (ret != 0) { 7676 bam_error(GET_PHYSICAL_MENU_NULL, menu_root); 7677 return (0); 7678 } 7679 7680 assert(physarray); 7681 assert(n > 0); 7682 7683 on_bootdisk = 0; 7684 for (i = 0; i < n; i++) { 7685 assert(strncmp(physarray[i], "/dev/dsk/", 7686 strlen("/dev/dsk/")) == 0 || 7687 strncmp(physarray[i], "/dev/rdsk/", 7688 strlen("/dev/rdsk/")) == 0); 7689 7690 BAM_DPRINTF((D_CHECK_ON_BOOTDISK, fcn, physarray[i])); 7691 if (is_bootdisk(osroot, physarray[i])) { 7692 on_bootdisk = 1; 7693 BAM_DPRINTF((D_IS_ON_BOOTDISK, fcn, physarray[i])); 7694 } 7695 } 7696 7697 free_physarray(physarray, n); 7698 7699 INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk = 1); 7700 INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk = 0); 7701 if (on_bootdisk) { 7702 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 7703 } else { 7704 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 7705 } 7706 7707 return (on_bootdisk); 7708 } 7709 7710 void 7711 bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp) 7712 { 7713 const char *fcn = "bam_add_line()"; 7714 7715 assert(mp); 7716 assert(entry); 7717 assert(prev); 7718 assert(lp); 7719 7720 lp->next = prev->next; 7721 if (prev->next) { 7722 BAM_DPRINTF((D_ADD_LINE_PREV_NEXT, fcn)); 7723 prev->next->prev = lp; 7724 } else { 7725 BAM_DPRINTF((D_ADD_LINE_NOT_PREV_NEXT, fcn)); 7726 } 7727 prev->next = lp; 7728 lp->prev = prev; 7729 7730 if (entry->end == prev) { 7731 BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_ENTRY, fcn)); 7732 entry->end = lp; 7733 } 7734 if (mp->end == prev) { 7735 assert(lp->next == NULL); 7736 mp->end = lp; 7737 BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_MENU, fcn)); 7738 } 7739 } 7740 7741 /* 7742 * look for matching bootadm entry with specified parameters 7743 * Here are the rules (based on existing usage): 7744 * - If title is specified, match on title only 7745 * - Else, match on root/findroot, kernel, and module. 7746 * Note that, if root_opt is non-zero, the absence of 7747 * root line is considered a match. 7748 */ 7749 static entry_t * 7750 find_boot_entry( 7751 menu_t *mp, 7752 char *title, 7753 char *kernel, 7754 char *findroot, 7755 char *root, 7756 char *module, 7757 int root_opt, 7758 int *entry_num) 7759 { 7760 int i; 7761 line_t *lp; 7762 entry_t *ent; 7763 const char *fcn = "find_boot_entry()"; 7764 7765 if (entry_num) 7766 *entry_num = BAM_ERROR; 7767 7768 /* find matching entry */ 7769 for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) { 7770 lp = ent->start; 7771 7772 /* first line of entry must be bootadm comment */ 7773 lp = ent->start; 7774 if (lp->flags != BAM_COMMENT || 7775 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) { 7776 continue; 7777 } 7778 7779 /* advance to title line */ 7780 lp = lp->next; 7781 if (title) { 7782 if (lp->flags == BAM_TITLE && lp->arg && 7783 strcmp(lp->arg, title) == 0) { 7784 BAM_DPRINTF((D_MATCHED_TITLE, fcn, title)); 7785 break; 7786 } 7787 BAM_DPRINTF((D_NOMATCH_TITLE, fcn, title, lp->arg)); 7788 continue; /* check title only */ 7789 } 7790 7791 lp = lp->next; /* advance to root line */ 7792 if (lp == NULL) { 7793 continue; 7794 } else if (lp->cmd != NULL && 7795 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) { 7796 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT", 7797 findroot = NULL); 7798 if (findroot == NULL) { 7799 BAM_DPRINTF((D_NOMATCH_FINDROOT_NULL, 7800 fcn, lp->arg)); 7801 continue; 7802 } 7803 /* findroot command found, try match */ 7804 if (strcmp(lp->arg, findroot) != 0) { 7805 BAM_DPRINTF((D_NOMATCH_FINDROOT, 7806 fcn, findroot, lp->arg)); 7807 continue; 7808 } 7809 BAM_DPRINTF((D_MATCHED_FINDROOT, fcn, findroot)); 7810 lp = lp->next; /* advance to kernel line */ 7811 } else if (lp->cmd != NULL && 7812 strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) { 7813 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root = NULL); 7814 if (root == NULL) { 7815 BAM_DPRINTF((D_NOMATCH_ROOT_NULL, 7816 fcn, lp->arg)); 7817 continue; 7818 } 7819 /* root cmd found, try match */ 7820 if (strcmp(lp->arg, root) != 0) { 7821 BAM_DPRINTF((D_NOMATCH_ROOT, 7822 fcn, root, lp->arg)); 7823 continue; 7824 } 7825 BAM_DPRINTF((D_MATCHED_ROOT, fcn, root)); 7826 lp = lp->next; /* advance to kernel line */ 7827 } else { 7828 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO", 7829 root_opt = 0); 7830 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES", 7831 root_opt = 1); 7832 /* no root command, see if root is optional */ 7833 if (root_opt == 0) { 7834 BAM_DPRINTF((D_NO_ROOT_OPT, fcn)); 7835 continue; 7836 } 7837 BAM_DPRINTF((D_ROOT_OPT, fcn)); 7838 } 7839 7840 if (lp == NULL || lp->next == NULL) { 7841 continue; 7842 } 7843 7844 if (kernel && 7845 (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) { 7846 if (!(ent->flags & BAM_ENTRY_FAILSAFE) || 7847 !(ent->flags & BAM_ENTRY_DBOOT) || 7848 strcmp(kernel, DIRECT_BOOT_FAILSAFE_LINE) != 0) 7849 continue; 7850 7851 ent->flags |= BAM_ENTRY_UPGFSKERNEL; 7852 7853 } 7854 BAM_DPRINTF((D_KERNEL_MATCH, fcn, kernel, lp->arg)); 7855 7856 /* 7857 * Check for matching module entry (failsafe or normal). 7858 * If it fails to match, we go around the loop again. 7859 * For xpv entries, there are two module lines, so we 7860 * do the check twice. 7861 */ 7862 lp = lp->next; /* advance to module line */ 7863 if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) || 7864 (((lp = lp->next) != NULL) && 7865 check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) { 7866 /* match found */ 7867 BAM_DPRINTF((D_MODULE_MATCH, fcn, module, lp->arg)); 7868 break; 7869 } 7870 7871 if (strcmp(module, FAILSAFE_ARCHIVE) == 0 && 7872 (strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_32) == 0 || 7873 strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_64) == 0)) { 7874 ent->flags |= BAM_ENTRY_UPGFSMODULE; 7875 break; 7876 } 7877 7878 } 7879 7880 if (ent && entry_num) { 7881 *entry_num = i; 7882 } 7883 7884 if (ent) { 7885 BAM_DPRINTF((D_RETURN_RET, fcn, i)); 7886 } else { 7887 BAM_DPRINTF((D_RETURN_RET, fcn, BAM_ERROR)); 7888 } 7889 return (ent); 7890 } 7891 7892 static int 7893 update_boot_entry(menu_t *mp, char *title, char *findroot, char *root, 7894 char *kernel, char *mod_kernel, char *module, int root_opt) 7895 { 7896 int i; 7897 int change_kernel = 0; 7898 entry_t *ent; 7899 line_t *lp; 7900 line_t *tlp; 7901 char linebuf[BAM_MAXLINE]; 7902 const char *fcn = "update_boot_entry()"; 7903 7904 /* note: don't match on title, it's updated on upgrade */ 7905 ent = find_boot_entry(mp, NULL, kernel, findroot, root, module, 7906 root_opt, &i); 7907 if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) { 7908 /* 7909 * We may be upgrading a kernel from multiboot to 7910 * directboot. Look for a multiboot entry. A multiboot 7911 * entry will not have a findroot line. 7912 */ 7913 ent = find_boot_entry(mp, NULL, "multiboot", NULL, root, 7914 MULTIBOOT_ARCHIVE, root_opt, &i); 7915 if (ent != NULL) { 7916 BAM_DPRINTF((D_UPGRADE_FROM_MULTIBOOT, fcn, root)); 7917 change_kernel = 1; 7918 } 7919 } else if (ent) { 7920 BAM_DPRINTF((D_FOUND_FINDROOT, fcn, findroot)); 7921 } 7922 7923 if (ent == NULL) { 7924 BAM_DPRINTF((D_ENTRY_NOT_FOUND_CREATING, fcn, findroot)); 7925 return (add_boot_entry(mp, title, findroot, 7926 kernel, mod_kernel, module, NULL)); 7927 } 7928 7929 /* replace title of existing entry and update findroot line */ 7930 lp = ent->start; 7931 lp = lp->next; /* title line */ 7932 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 7933 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title); 7934 free(lp->arg); 7935 free(lp->line); 7936 lp->arg = s_strdup(title); 7937 lp->line = s_strdup(linebuf); 7938 BAM_DPRINTF((D_CHANGING_TITLE, fcn, title)); 7939 7940 tlp = lp; /* title line */ 7941 lp = lp->next; /* root line */ 7942 7943 /* if no root or findroot command, create a new line_t */ 7944 if ((lp->cmd != NULL) && (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) != 0 && 7945 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) != 0)) { 7946 lp = s_calloc(1, sizeof (line_t)); 7947 bam_add_line(mp, ent, tlp, lp); 7948 } else { 7949 if (lp->cmd != NULL) 7950 free(lp->cmd); 7951 7952 free(lp->sep); 7953 free(lp->arg); 7954 free(lp->line); 7955 } 7956 7957 lp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]); 7958 lp->sep = s_strdup(menu_cmds[SEP_CMD]); 7959 lp->arg = s_strdup(findroot); 7960 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 7961 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot); 7962 lp->line = s_strdup(linebuf); 7963 BAM_DPRINTF((D_ADDING_FINDROOT_LINE, fcn, findroot)); 7964 7965 /* kernel line */ 7966 lp = lp->next; 7967 7968 if (ent->flags & BAM_ENTRY_UPGFSKERNEL) { 7969 char *params = NULL; 7970 7971 params = strstr(lp->line, "-s"); 7972 if (params != NULL) 7973 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s%s", 7974 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD], 7975 kernel, params+2); 7976 else 7977 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 7978 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD], 7979 kernel); 7980 7981 if (lp->cmd != NULL) 7982 free(lp->cmd); 7983 7984 free(lp->arg); 7985 free(lp->line); 7986 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]); 7987 lp->arg = s_strdup(strstr(linebuf, "/")); 7988 lp->line = s_strdup(linebuf); 7989 ent->flags &= ~BAM_ENTRY_UPGFSKERNEL; 7990 BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, lp->prev->cmd)); 7991 } 7992 7993 if (change_kernel) { 7994 /* 7995 * We're upgrading from multiboot to directboot. 7996 */ 7997 if (lp->cmd != NULL && 7998 strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) { 7999 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 8000 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD], 8001 kernel); 8002 free(lp->cmd); 8003 free(lp->arg); 8004 free(lp->line); 8005 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]); 8006 lp->arg = s_strdup(kernel); 8007 lp->line = s_strdup(linebuf); 8008 lp = lp->next; 8009 BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, kernel)); 8010 } 8011 if (lp->cmd != NULL && 8012 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) { 8013 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 8014 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD], 8015 module); 8016 free(lp->cmd); 8017 free(lp->arg); 8018 free(lp->line); 8019 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]); 8020 lp->arg = s_strdup(module); 8021 lp->line = s_strdup(linebuf); 8022 lp = lp->next; 8023 BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module)); 8024 } 8025 } 8026 8027 /* module line */ 8028 lp = lp->next; 8029 8030 if (ent->flags & BAM_ENTRY_UPGFSMODULE) { 8031 if (lp->cmd != NULL && 8032 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) { 8033 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s", 8034 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD], 8035 module); 8036 free(lp->cmd); 8037 free(lp->arg); 8038 free(lp->line); 8039 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]); 8040 lp->arg = s_strdup(module); 8041 lp->line = s_strdup(linebuf); 8042 lp = lp->next; 8043 ent->flags &= ~BAM_ENTRY_UPGFSMODULE; 8044 BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module)); 8045 } 8046 } 8047 8048 BAM_DPRINTF((D_RETURN_RET, fcn, i)); 8049 return (i); 8050 } 8051 8052 int 8053 root_optional(char *osroot, char *menu_root) 8054 { 8055 char *ospecial; 8056 char *mspecial; 8057 char *slash; 8058 int root_opt; 8059 int ret1; 8060 int ret2; 8061 const char *fcn = "root_optional()"; 8062 8063 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root)); 8064 8065 /* 8066 * For all filesystems except ZFS, a straight compare of osroot 8067 * and menu_root will tell us if root is optional. 8068 * For ZFS, the situation is complicated by the fact that 8069 * menu_root and osroot are always different 8070 */ 8071 ret1 = is_zfs(osroot); 8072 ret2 = is_zfs(menu_root); 8073 INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1 = 0); 8074 if (!ret1 || !ret2) { 8075 BAM_DPRINTF((D_ROOT_OPT_NOT_ZFS, fcn, osroot, menu_root)); 8076 root_opt = (strcmp(osroot, menu_root) == 0); 8077 goto out; 8078 } 8079 8080 ospecial = get_special(osroot); 8081 INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial = NULL); 8082 if (ospecial == NULL) { 8083 bam_error(GET_OSROOT_SPECIAL_ERR, osroot); 8084 return (0); 8085 } 8086 BAM_DPRINTF((D_ROOT_OPTIONAL_OSPECIAL, fcn, ospecial, osroot)); 8087 8088 mspecial = get_special(menu_root); 8089 INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial = NULL); 8090 if (mspecial == NULL) { 8091 bam_error(GET_MENU_ROOT_SPECIAL_ERR, menu_root); 8092 free(ospecial); 8093 return (0); 8094 } 8095 BAM_DPRINTF((D_ROOT_OPTIONAL_MSPECIAL, fcn, mspecial, menu_root)); 8096 8097 slash = strchr(ospecial, '/'); 8098 if (slash) 8099 *slash = '\0'; 8100 BAM_DPRINTF((D_ROOT_OPTIONAL_FIXED_OSPECIAL, fcn, ospecial, osroot)); 8101 8102 root_opt = (strcmp(ospecial, mspecial) == 0); 8103 8104 free(ospecial); 8105 free(mspecial); 8106 8107 out: 8108 INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0); 8109 INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1); 8110 if (root_opt) { 8111 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 8112 } else { 8113 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 8114 } 8115 8116 return (root_opt); 8117 } 8118 8119 /*ARGSUSED*/ 8120 static error_t 8121 update_entry(menu_t *mp, char *menu_root, char *osdev) 8122 { 8123 int entry; 8124 char *grubsign; 8125 char *grubroot; 8126 char *title; 8127 char osroot[PATH_MAX]; 8128 char *failsafe_kernel = NULL; 8129 struct stat sbuf; 8130 char failsafe[256]; 8131 char failsafe_64[256]; 8132 int ret; 8133 const char *fcn = "update_entry()"; 8134 8135 assert(mp); 8136 assert(menu_root); 8137 assert(osdev); 8138 assert(bam_root); 8139 8140 BAM_DPRINTF((D_FUNC_ENTRY3, fcn, menu_root, osdev, bam_root)); 8141 8142 (void) strlcpy(osroot, bam_root, sizeof (osroot)); 8143 8144 title = get_title(osroot); 8145 assert(title); 8146 8147 grubsign = get_grubsign(osroot, osdev); 8148 INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign = NULL); 8149 if (grubsign == NULL) { 8150 bam_error(GET_GRUBSIGN_ERROR, osroot, osdev); 8151 return (BAM_ERROR); 8152 } 8153 8154 /* 8155 * It is not a fatal error if get_grubroot() fails 8156 * We no longer rely on biosdev to populate the 8157 * menu 8158 */ 8159 grubroot = get_grubroot(osroot, osdev, menu_root); 8160 INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL); 8161 if (grubroot) { 8162 BAM_DPRINTF((D_GET_GRUBROOT_SUCCESS, 8163 fcn, osroot, osdev, menu_root)); 8164 } else { 8165 BAM_DPRINTF((D_GET_GRUBROOT_FAILURE, 8166 fcn, osroot, osdev, menu_root)); 8167 } 8168 8169 /* add the entry for normal Solaris */ 8170 INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT", 8171 bam_direct = BAM_DIRECT_MULTIBOOT); 8172 if (bam_direct == BAM_DIRECT_DBOOT) { 8173 entry = update_boot_entry(mp, title, grubsign, grubroot, 8174 (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL), 8175 NULL, DIRECT_BOOT_ARCHIVE, 8176 root_optional(osroot, menu_root)); 8177 BAM_DPRINTF((D_UPDATED_BOOT_ENTRY, fcn, bam_zfs, grubsign)); 8178 if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) { 8179 (void) update_boot_entry(mp, NEW_HV_ENTRY, grubsign, 8180 grubroot, XEN_MENU, bam_zfs ? 8181 XEN_KERNEL_MODULE_LINE_ZFS : XEN_KERNEL_MODULE_LINE, 8182 DIRECT_BOOT_ARCHIVE, 8183 root_optional(osroot, menu_root)); 8184 BAM_DPRINTF((D_UPDATED_HV_ENTRY, 8185 fcn, bam_zfs, grubsign)); 8186 } 8187 } else { 8188 entry = update_boot_entry(mp, title, grubsign, grubroot, 8189 MULTI_BOOT, NULL, MULTIBOOT_ARCHIVE, 8190 root_optional(osroot, menu_root)); 8191 8192 BAM_DPRINTF((D_UPDATED_MULTIBOOT_ENTRY, fcn, grubsign)); 8193 } 8194 8195 /* 8196 * Add the entry for failsafe archive. On a bfu'd system, the 8197 * failsafe may be different than the installed kernel. 8198 */ 8199 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", 8200 osroot, FAILSAFE_ARCHIVE_32); 8201 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s", 8202 osroot, FAILSAFE_ARCHIVE_64); 8203 8204 /* 8205 * Check if at least one of the two archives exists 8206 * Using $ISADIR as the default line, we have an entry which works 8207 * for both the cases. 8208 */ 8209 8210 if (stat(failsafe, &sbuf) == 0 || stat(failsafe_64, &sbuf) == 0) { 8211 8212 /* Figure out where the kernel line should point */ 8213 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot, 8214 DIRECT_BOOT_FAILSAFE_32); 8215 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s", 8216 osroot, DIRECT_BOOT_FAILSAFE_64); 8217 if (stat(failsafe, &sbuf) == 0 || 8218 stat(failsafe_64, &sbuf) == 0) { 8219 failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE; 8220 } else { 8221 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", 8222 osroot, MULTI_BOOT_FAILSAFE); 8223 if (stat(failsafe, &sbuf) == 0) { 8224 failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE; 8225 } 8226 } 8227 if (failsafe_kernel != NULL) { 8228 (void) update_boot_entry(mp, FAILSAFE_TITLE, grubsign, 8229 grubroot, failsafe_kernel, NULL, FAILSAFE_ARCHIVE, 8230 root_optional(osroot, menu_root)); 8231 BAM_DPRINTF((D_UPDATED_FAILSAFE_ENTRY, fcn, 8232 failsafe_kernel)); 8233 } 8234 } 8235 free(grubroot); 8236 8237 INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry = BAM_ERROR); 8238 if (entry == BAM_ERROR) { 8239 bam_error(FAILED_TO_ADD_BOOT_ENTRY, title, grubsign); 8240 free(grubsign); 8241 return (BAM_ERROR); 8242 } 8243 free(grubsign); 8244 8245 update_numbering(mp); 8246 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry); 8247 INJECT_ERROR1("SET_DEFAULT_ERROR", ret = BAM_ERROR); 8248 if (ret == BAM_ERROR) { 8249 bam_error(SET_DEFAULT_FAILED, entry); 8250 } 8251 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 8252 return (BAM_WRITE); 8253 } 8254 8255 static void 8256 save_default_entry(menu_t *mp, const char *which) 8257 { 8258 int lineNum; 8259 int entryNum; 8260 int entry = 0; /* default is 0 */ 8261 char linebuf[BAM_MAXLINE]; 8262 line_t *lp = mp->curdefault; 8263 const char *fcn = "save_default_entry()"; 8264 8265 if (mp->start) { 8266 lineNum = mp->end->lineNum; 8267 entryNum = mp->end->entryNum; 8268 } else { 8269 lineNum = LINE_INIT; 8270 entryNum = ENTRY_INIT; 8271 } 8272 8273 if (lp) 8274 entry = s_strtol(lp->arg); 8275 8276 (void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry); 8277 BAM_DPRINTF((D_SAVING_DEFAULT_TO, fcn, linebuf)); 8278 line_parser(mp, linebuf, &lineNum, &entryNum); 8279 BAM_DPRINTF((D_SAVED_DEFAULT_TO, fcn, lineNum, entryNum)); 8280 } 8281 8282 static void 8283 restore_default_entry(menu_t *mp, const char *which, line_t *lp) 8284 { 8285 int entry; 8286 char *str; 8287 const char *fcn = "restore_default_entry()"; 8288 8289 if (lp == NULL) { 8290 BAM_DPRINTF((D_RESTORE_DEFAULT_NULL, fcn)); 8291 return; /* nothing to restore */ 8292 } 8293 8294 BAM_DPRINTF((D_RESTORE_DEFAULT_STR, fcn, which)); 8295 8296 str = lp->arg + strlen(which); 8297 entry = s_strtol(str); 8298 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry); 8299 8300 BAM_DPRINTF((D_RESTORED_DEFAULT_TO, fcn, entry)); 8301 8302 /* delete saved old default line */ 8303 unlink_line(mp, lp); 8304 line_free(lp); 8305 } 8306 8307 /* 8308 * This function is for supporting reboot with args. 8309 * The opt value can be: 8310 * NULL delete temp entry, if present 8311 * entry=<n> switches default entry to <n> 8312 * else treated as boot-args and setup a temperary menu entry 8313 * and make it the default 8314 * Note that we are always rebooting the current OS instance 8315 * so osroot == / always. 8316 */ 8317 #define REBOOT_TITLE "Solaris_reboot_transient" 8318 8319 /*ARGSUSED*/ 8320 static error_t 8321 update_temp(menu_t *mp, char *dummy, char *opt) 8322 { 8323 int entry; 8324 char *osdev; 8325 char *fstype; 8326 char *sign; 8327 char *opt_ptr; 8328 char *path; 8329 char kernbuf[BUFSIZ]; 8330 char args_buf[BUFSIZ]; 8331 char signbuf[PATH_MAX]; 8332 int ret; 8333 const char *fcn = "update_temp()"; 8334 8335 assert(mp); 8336 assert(dummy == NULL); 8337 8338 /* opt can be NULL */ 8339 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt ? opt : "<NULL>")); 8340 BAM_DPRINTF((D_BAM_ROOT, fcn, bam_alt_root, bam_root)); 8341 8342 if (bam_alt_root || bam_rootlen != 1 || 8343 strcmp(bam_root, "/") != 0 || 8344 strcmp(rootbuf, "/") != 0) { 8345 bam_error(ALT_ROOT_INVALID, bam_root); 8346 return (BAM_ERROR); 8347 } 8348 8349 /* If no option, delete exiting reboot menu entry */ 8350 if (opt == NULL) { 8351 entry_t *ent; 8352 BAM_DPRINTF((D_OPT_NULL, fcn)); 8353 ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL, 8354 NULL, NULL, 0, &entry); 8355 if (ent == NULL) { /* not found is ok */ 8356 BAM_DPRINTF((D_TRANSIENT_NOTFOUND, fcn)); 8357 return (BAM_SUCCESS); 8358 } 8359 (void) delete_boot_entry(mp, entry, DBE_PRINTERR); 8360 restore_default_entry(mp, BAM_OLDDEF, mp->olddefault); 8361 mp->olddefault = NULL; 8362 BAM_DPRINTF((D_RESTORED_DEFAULT, fcn)); 8363 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 8364 return (BAM_WRITE); 8365 } 8366 8367 /* if entry= is specified, set the default entry */ 8368 if (strncmp(opt, "entry=", strlen("entry=")) == 0) { 8369 int entryNum = s_strtol(opt + strlen("entry=")); 8370 BAM_DPRINTF((D_ENTRY_EQUALS, fcn, opt)); 8371 if (selector(mp, opt, &entry, NULL) == BAM_SUCCESS) { 8372 /* this is entry=# option */ 8373 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry); 8374 BAM_DPRINTF((D_ENTRY_SET_IS, fcn, entry, ret)); 8375 return (ret); 8376 } else { 8377 bam_error(SET_DEFAULT_FAILED, entryNum); 8378 return (BAM_ERROR); 8379 } 8380 } 8381 8382 /* 8383 * add a new menu entry based on opt and make it the default 8384 */ 8385 8386 fstype = get_fstype("/"); 8387 INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype = NULL); 8388 if (fstype == NULL) { 8389 bam_error(REBOOT_FSTYPE_FAILED); 8390 return (BAM_ERROR); 8391 } 8392 8393 osdev = get_special("/"); 8394 INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL); 8395 if (osdev == NULL) { 8396 free(fstype); 8397 bam_error(REBOOT_SPECIAL_FAILED); 8398 return (BAM_ERROR); 8399 } 8400 8401 sign = find_existing_sign("/", osdev, fstype); 8402 INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL); 8403 if (sign == NULL) { 8404 free(fstype); 8405 free(osdev); 8406 bam_error(REBOOT_SIGN_FAILED); 8407 return (BAM_ERROR); 8408 } 8409 8410 free(osdev); 8411 (void) strlcpy(signbuf, sign, sizeof (signbuf)); 8412 free(sign); 8413 8414 assert(strchr(signbuf, '(') == NULL && strchr(signbuf, ',') == NULL && 8415 strchr(signbuf, ')') == NULL); 8416 8417 /* 8418 * There is no alternate root while doing reboot with args 8419 * This version of bootadm is only delivered with a DBOOT 8420 * version of Solaris. 8421 */ 8422 INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct = BAM_DIRECT_MULTIBOOT); 8423 if (bam_direct != BAM_DIRECT_DBOOT) { 8424 free(fstype); 8425 bam_error(REBOOT_DIRECT_FAILED); 8426 return (BAM_ERROR); 8427 } 8428 8429 /* add an entry for Solaris reboot */ 8430 if (opt[0] == '-') { 8431 /* It's an option - first see if boot-file is set */ 8432 ret = get_kernel(mp, KERNEL_CMD, kernbuf, sizeof (kernbuf)); 8433 INJECT_ERROR1("REBOOT_GET_KERNEL", ret = BAM_ERROR); 8434 if (ret != BAM_SUCCESS) { 8435 free(fstype); 8436 bam_error(REBOOT_GET_KERNEL_FAILED); 8437 return (BAM_ERROR); 8438 } 8439 if (kernbuf[0] == '\0') 8440 (void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL, 8441 sizeof (kernbuf)); 8442 /* 8443 * If this is a zfs file system and kernbuf does not 8444 * have "-B $ZFS-BOOTFS" string yet, add it. 8445 */ 8446 if (strcmp(fstype, "zfs") == 0 && !strstr(kernbuf, ZFS_BOOT)) { 8447 (void) strlcat(kernbuf, " ", sizeof (kernbuf)); 8448 (void) strlcat(kernbuf, ZFS_BOOT, sizeof (kernbuf)); 8449 } 8450 (void) strlcat(kernbuf, " ", sizeof (kernbuf)); 8451 (void) strlcat(kernbuf, opt, sizeof (kernbuf)); 8452 BAM_DPRINTF((D_REBOOT_OPTION, fcn, kernbuf)); 8453 } else if (opt[0] == '/') { 8454 /* It's a full path, so write it out. */ 8455 (void) strlcpy(kernbuf, opt, sizeof (kernbuf)); 8456 8457 /* 8458 * If someone runs: 8459 * 8460 * # eeprom boot-args='-kd' 8461 * # reboot /platform/i86pc/kernel/unix 8462 * 8463 * we want to use the boot-args as part of the boot 8464 * line. On the other hand, if someone runs: 8465 * 8466 * # reboot "/platform/i86pc/kernel/unix -kd" 8467 * 8468 * we don't need to mess with boot-args. If there's 8469 * no space in the options string, assume we're in the 8470 * first case. 8471 */ 8472 if (strchr(opt, ' ') == NULL) { 8473 ret = get_kernel(mp, ARGS_CMD, args_buf, 8474 sizeof (args_buf)); 8475 INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR); 8476 if (ret != BAM_SUCCESS) { 8477 free(fstype); 8478 bam_error(REBOOT_GET_ARGS_FAILED); 8479 return (BAM_ERROR); 8480 } 8481 8482 if (args_buf[0] != '\0') { 8483 (void) strlcat(kernbuf, " ", sizeof (kernbuf)); 8484 (void) strlcat(kernbuf, args_buf, 8485 sizeof (kernbuf)); 8486 } 8487 } 8488 BAM_DPRINTF((D_REBOOT_ABSPATH, fcn, kernbuf)); 8489 } else { 8490 /* 8491 * It may be a partial path, or it may be a partial 8492 * path followed by options. Assume that only options 8493 * follow a space. If someone sends us a kernel path 8494 * that includes a space, they deserve to be broken. 8495 */ 8496 opt_ptr = strchr(opt, ' '); 8497 if (opt_ptr != NULL) { 8498 *opt_ptr = '\0'; 8499 } 8500 8501 path = expand_path(opt); 8502 if (path != NULL) { 8503 (void) strlcpy(kernbuf, path, sizeof (kernbuf)); 8504 free(path); 8505 8506 /* 8507 * If there were options given, use those. 8508 * Otherwise, copy over the default options. 8509 */ 8510 if (opt_ptr != NULL) { 8511 /* Restore the space in opt string */ 8512 *opt_ptr = ' '; 8513 (void) strlcat(kernbuf, opt_ptr, 8514 sizeof (kernbuf)); 8515 } else { 8516 ret = get_kernel(mp, ARGS_CMD, args_buf, 8517 sizeof (args_buf)); 8518 INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS", 8519 ret = BAM_ERROR); 8520 if (ret != BAM_SUCCESS) { 8521 free(fstype); 8522 bam_error(REBOOT_GET_ARGS_FAILED); 8523 return (BAM_ERROR); 8524 } 8525 8526 if (args_buf[0] != '\0') { 8527 (void) strlcat(kernbuf, " ", 8528 sizeof (kernbuf)); 8529 (void) strlcat(kernbuf, 8530 args_buf, sizeof (kernbuf)); 8531 } 8532 } 8533 BAM_DPRINTF((D_REBOOT_RESOLVED_PARTIAL, fcn, kernbuf)); 8534 } else { 8535 free(fstype); 8536 bam_error(UNKNOWN_KERNEL, opt); 8537 bam_print_stderr(UNKNOWN_KERNEL_REBOOT); 8538 return (BAM_ERROR); 8539 } 8540 } 8541 free(fstype); 8542 entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf, 8543 NULL, NULL, NULL); 8544 INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR); 8545 if (entry == BAM_ERROR) { 8546 bam_error(REBOOT_WITH_ARGS_ADD_ENTRY_FAILED); 8547 return (BAM_ERROR); 8548 } 8549 8550 save_default_entry(mp, BAM_OLDDEF); 8551 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry); 8552 INJECT_ERROR1("REBOOT_SET_GLOBAL", ret = BAM_ERROR); 8553 if (ret == BAM_ERROR) { 8554 bam_error(REBOOT_SET_DEFAULT_FAILED, entry); 8555 } 8556 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 8557 return (BAM_WRITE); 8558 } 8559 8560 error_t 8561 set_global(menu_t *mp, char *globalcmd, int val) 8562 { 8563 line_t *lp; 8564 line_t *found; 8565 line_t *last; 8566 char *cp; 8567 char *str; 8568 char prefix[BAM_MAXLINE]; 8569 size_t len; 8570 const char *fcn = "set_global()"; 8571 8572 assert(mp); 8573 assert(globalcmd); 8574 8575 if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) { 8576 INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val = -1); 8577 INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp->end = NULL); 8578 INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val = 100); 8579 if (val < 0 || mp->end == NULL || val > mp->end->entryNum) { 8580 (void) snprintf(prefix, sizeof (prefix), "%d", val); 8581 bam_error(INVALID_ENTRY, prefix); 8582 return (BAM_ERROR); 8583 } 8584 } 8585 8586 found = last = NULL; 8587 for (lp = mp->start; lp; lp = lp->next) { 8588 if (lp->flags != BAM_GLOBAL) 8589 continue; 8590 8591 last = lp; /* track the last global found */ 8592 8593 INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp->cmd = NULL); 8594 if (lp->cmd == NULL) { 8595 bam_error(NO_CMD, lp->lineNum); 8596 continue; 8597 } 8598 if (strcmp(globalcmd, lp->cmd) != 0) 8599 continue; 8600 8601 BAM_DPRINTF((D_FOUND_GLOBAL, fcn, globalcmd)); 8602 8603 if (found) { 8604 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root); 8605 } 8606 found = lp; 8607 } 8608 8609 if (found == NULL) { 8610 lp = s_calloc(1, sizeof (line_t)); 8611 if (last == NULL) { 8612 lp->next = mp->start; 8613 mp->start = lp; 8614 mp->end = (mp->end) ? mp->end : lp; 8615 } else { 8616 lp->next = last->next; 8617 last->next = lp; 8618 if (lp->next == NULL) 8619 mp->end = lp; 8620 } 8621 lp->flags = BAM_GLOBAL; /* other fields not needed for writes */ 8622 len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]); 8623 len += 10; /* val < 10 digits */ 8624 lp->line = s_calloc(1, len); 8625 (void) snprintf(lp->line, len, "%s%s%d", 8626 globalcmd, menu_cmds[SEP_CMD], val); 8627 BAM_DPRINTF((D_SET_GLOBAL_WROTE_NEW, fcn, lp->line)); 8628 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 8629 return (BAM_WRITE); 8630 } 8631 8632 /* 8633 * We are changing an existing entry. Retain any prefix whitespace, 8634 * but overwrite everything else. This preserves tabs added for 8635 * readability. 8636 */ 8637 str = found->line; 8638 cp = prefix; 8639 while (*str == ' ' || *str == '\t') 8640 *(cp++) = *(str++); 8641 *cp = '\0'; /* Terminate prefix */ 8642 len = strlen(prefix) + strlen(globalcmd); 8643 len += strlen(menu_cmds[SEP_CMD]) + 10; 8644 8645 free(found->line); 8646 found->line = s_calloc(1, len); 8647 (void) snprintf(found->line, len, 8648 "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val); 8649 8650 BAM_DPRINTF((D_SET_GLOBAL_REPLACED, fcn, found->line)); 8651 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 8652 return (BAM_WRITE); /* need a write to menu */ 8653 } 8654 8655 /* 8656 * partial_path may be anything like "kernel/unix" or "kmdb". Try to 8657 * expand it to a full unix path. The calling function is expected to 8658 * output a message if an error occurs and NULL is returned. 8659 */ 8660 static char * 8661 expand_path(const char *partial_path) 8662 { 8663 int new_path_len; 8664 char *new_path; 8665 char new_path2[PATH_MAX]; 8666 struct stat sb; 8667 const char *fcn = "expand_path()"; 8668 8669 new_path_len = strlen(partial_path) + 64; 8670 new_path = s_calloc(1, new_path_len); 8671 8672 /* First, try the simplest case - something like "kernel/unix" */ 8673 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s", 8674 partial_path); 8675 if (stat(new_path, &sb) == 0) { 8676 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path)); 8677 return (new_path); 8678 } 8679 8680 if (strcmp(partial_path, "kmdb") == 0) { 8681 (void) snprintf(new_path, new_path_len, "%s -k", 8682 DIRECT_BOOT_KERNEL); 8683 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path)); 8684 return (new_path); 8685 } 8686 8687 /* 8688 * We've quickly reached unsupported usage. Try once more to 8689 * see if we were just given a glom name. 8690 */ 8691 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix", 8692 partial_path); 8693 (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix", 8694 partial_path); 8695 if (stat(new_path, &sb) == 0) { 8696 if (stat(new_path2, &sb) == 0) { 8697 /* 8698 * We matched both, so we actually 8699 * want to write the $ISADIR version. 8700 */ 8701 (void) snprintf(new_path, new_path_len, 8702 "/platform/i86pc/kernel/%s/$ISADIR/unix", 8703 partial_path); 8704 } 8705 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path)); 8706 return (new_path); 8707 } 8708 8709 free(new_path); 8710 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 8711 return (NULL); 8712 } 8713 8714 /* 8715 * The kernel cmd and arg have been changed, so 8716 * check whether the archive line needs to change. 8717 */ 8718 static void 8719 set_archive_line(entry_t *entryp, line_t *kernelp) 8720 { 8721 line_t *lp = entryp->start; 8722 char *new_archive; 8723 menu_cmd_t m_cmd; 8724 const char *fcn = "set_archive_line()"; 8725 8726 for (; lp != NULL; lp = lp->next) { 8727 if (lp->cmd != NULL && strncmp(lp->cmd, menu_cmds[MODULE_CMD], 8728 sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) { 8729 break; 8730 } 8731 8732 INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp = entryp->end); 8733 if (lp == entryp->end) { 8734 BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn, 8735 entryp->entryNum)); 8736 return; 8737 } 8738 } 8739 INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL); 8740 if (lp == NULL) { 8741 BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn, entryp->entryNum)); 8742 return; 8743 } 8744 8745 if (strstr(kernelp->arg, "$ISADIR") != NULL) { 8746 new_archive = DIRECT_BOOT_ARCHIVE; 8747 m_cmd = MODULE_DOLLAR_CMD; 8748 } else if (strstr(kernelp->arg, "amd64") != NULL) { 8749 new_archive = DIRECT_BOOT_ARCHIVE_64; 8750 m_cmd = MODULE_CMD; 8751 } else { 8752 new_archive = DIRECT_BOOT_ARCHIVE_32; 8753 m_cmd = MODULE_CMD; 8754 } 8755 8756 if (strcmp(lp->arg, new_archive) == 0) { 8757 BAM_DPRINTF((D_ARCHIVE_LINE_NOCHANGE, fcn, lp->arg)); 8758 return; 8759 } 8760 8761 if (lp->cmd != NULL && strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) { 8762 free(lp->cmd); 8763 lp->cmd = s_strdup(menu_cmds[m_cmd]); 8764 } 8765 8766 free(lp->arg); 8767 lp->arg = s_strdup(new_archive); 8768 update_line(lp); 8769 BAM_DPRINTF((D_ARCHIVE_LINE_REPLACED, fcn, lp->line)); 8770 } 8771 8772 /* 8773 * Title for an entry to set properties that once went in bootenv.rc. 8774 */ 8775 #define BOOTENV_RC_TITLE "Solaris bootenv rc" 8776 8777 /* 8778 * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments 8779 * (optnum == ARGS_CMD) in the argument buf. If path is a zero-length 8780 * string, reset the value to the default. If path is a non-zero-length 8781 * string, set the kernel or arguments. 8782 */ 8783 static error_t 8784 get_set_kernel( 8785 menu_t *mp, 8786 menu_cmd_t optnum, 8787 char *path, 8788 char *buf, 8789 size_t bufsize) 8790 { 8791 int entryNum; 8792 int rv = BAM_SUCCESS; 8793 int free_new_path = 0; 8794 entry_t *entryp; 8795 line_t *ptr; 8796 line_t *kernelp; 8797 char *new_arg; 8798 char *old_args; 8799 char *space; 8800 char *new_path; 8801 char old_space; 8802 size_t old_kernel_len; 8803 size_t new_str_len; 8804 char *fstype; 8805 char *osdev; 8806 char *sign; 8807 char signbuf[PATH_MAX]; 8808 int ret; 8809 const char *fcn = "get_set_kernel()"; 8810 8811 assert(bufsize > 0); 8812 8813 ptr = kernelp = NULL; 8814 new_arg = old_args = space = NULL; 8815 new_path = NULL; 8816 buf[0] = '\0'; 8817 8818 INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT", 8819 bam_direct = BAM_DIRECT_MULTIBOOT); 8820 if (bam_direct != BAM_DIRECT_DBOOT) { 8821 bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args"); 8822 return (BAM_ERROR); 8823 } 8824 8825 /* 8826 * If a user changed the default entry to a non-bootadm controlled 8827 * one, we don't want to mess with it. Just print an error and 8828 * return. 8829 */ 8830 if (mp->curdefault) { 8831 entryNum = s_strtol(mp->curdefault->arg); 8832 for (entryp = mp->entries; entryp; entryp = entryp->next) { 8833 if (entryp->entryNum == entryNum) 8834 break; 8835 } 8836 if ((entryp != NULL) && 8837 ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) { 8838 bam_error(DEFAULT_NOT_BAM); 8839 return (BAM_ERROR); 8840 } 8841 } 8842 8843 entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL, 8844 0, &entryNum); 8845 8846 if (entryp != NULL) { 8847 for (ptr = entryp->start; ptr && ptr != entryp->end; 8848 ptr = ptr->next) { 8849 if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD], 8850 sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) { 8851 kernelp = ptr; 8852 break; 8853 } 8854 } 8855 if (kernelp == NULL) { 8856 bam_error(NO_KERNEL, entryNum); 8857 return (BAM_ERROR); 8858 } 8859 8860 old_kernel_len = strcspn(kernelp->arg, " \t"); 8861 space = old_args = kernelp->arg + old_kernel_len; 8862 while ((*old_args == ' ') || (*old_args == '\t')) 8863 old_args++; 8864 } 8865 8866 if (path == NULL) { 8867 if (entryp == NULL) { 8868 BAM_DPRINTF((D_GET_SET_KERNEL_NO_RC, fcn)); 8869 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 8870 return (BAM_SUCCESS); 8871 } 8872 assert(kernelp); 8873 if (optnum == ARGS_CMD) { 8874 if (old_args[0] != '\0') { 8875 (void) strlcpy(buf, old_args, bufsize); 8876 BAM_DPRINTF((D_GET_SET_KERNEL_ARGS, fcn, buf)); 8877 } 8878 } else { 8879 /* 8880 * We need to print the kernel, so we just turn the 8881 * first space into a '\0' and print the beginning. 8882 * We don't print anything if it's the default kernel. 8883 */ 8884 old_space = *space; 8885 *space = '\0'; 8886 if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) { 8887 (void) strlcpy(buf, kernelp->arg, bufsize); 8888 BAM_DPRINTF((D_GET_SET_KERNEL_KERN, fcn, buf)); 8889 } 8890 *space = old_space; 8891 } 8892 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 8893 return (BAM_SUCCESS); 8894 } 8895 8896 /* 8897 * First, check if we're resetting an entry to the default. 8898 */ 8899 if ((path[0] == '\0') || 8900 ((optnum == KERNEL_CMD) && 8901 (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) { 8902 if ((entryp == NULL) || (kernelp == NULL)) { 8903 /* No previous entry, it's already the default */ 8904 BAM_DPRINTF((D_GET_SET_KERNEL_ALREADY, fcn)); 8905 return (BAM_SUCCESS); 8906 } 8907 8908 /* 8909 * Check if we can delete the entry. If we're resetting the 8910 * kernel command, and the args is already empty, or if we're 8911 * resetting the args command, and the kernel is already the 8912 * default, we can restore the old default and delete the entry. 8913 */ 8914 if (((optnum == KERNEL_CMD) && 8915 ((old_args == NULL) || (old_args[0] == '\0'))) || 8916 ((optnum == ARGS_CMD) && 8917 (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL, 8918 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) { 8919 kernelp = NULL; 8920 (void) delete_boot_entry(mp, entryNum, DBE_PRINTERR); 8921 restore_default_entry(mp, BAM_OLD_RC_DEF, 8922 mp->old_rc_default); 8923 mp->old_rc_default = NULL; 8924 rv = BAM_WRITE; 8925 BAM_DPRINTF((D_GET_SET_KERNEL_RESTORE_DEFAULT, fcn)); 8926 goto done; 8927 } 8928 8929 if (optnum == KERNEL_CMD) { 8930 /* 8931 * At this point, we've already checked that old_args 8932 * and entryp are valid pointers. The "+ 2" is for 8933 * a space a the string termination character. 8934 */ 8935 new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) + 8936 strlen(old_args) + 2; 8937 new_arg = s_calloc(1, new_str_len); 8938 (void) snprintf(new_arg, new_str_len, "%s %s", 8939 DIRECT_BOOT_KERNEL, old_args); 8940 free(kernelp->arg); 8941 kernelp->arg = new_arg; 8942 8943 /* 8944 * We have changed the kernel line, so we may need 8945 * to update the archive line as well. 8946 */ 8947 set_archive_line(entryp, kernelp); 8948 BAM_DPRINTF((D_GET_SET_KERNEL_RESET_KERNEL_SET_ARG, 8949 fcn, kernelp->arg)); 8950 } else { 8951 /* 8952 * We're resetting the boot args to nothing, so 8953 * we only need to copy the kernel. We've already 8954 * checked that the kernel is not the default. 8955 */ 8956 new_arg = s_calloc(1, old_kernel_len + 1); 8957 (void) snprintf(new_arg, old_kernel_len + 1, "%s", 8958 kernelp->arg); 8959 free(kernelp->arg); 8960 kernelp->arg = new_arg; 8961 BAM_DPRINTF((D_GET_SET_KERNEL_RESET_ARG_SET_KERNEL, 8962 fcn, kernelp->arg)); 8963 } 8964 rv = BAM_WRITE; 8965 goto done; 8966 } 8967 8968 /* 8969 * Expand the kernel file to a full path, if necessary 8970 */ 8971 if ((optnum == KERNEL_CMD) && (path[0] != '/')) { 8972 new_path = expand_path(path); 8973 if (new_path == NULL) { 8974 bam_error(UNKNOWN_KERNEL, path); 8975 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 8976 return (BAM_ERROR); 8977 } 8978 free_new_path = 1; 8979 } else { 8980 new_path = path; 8981 free_new_path = 0; 8982 } 8983 8984 /* 8985 * At this point, we know we're setting a new value. First, take care 8986 * of the case where there was no previous entry. 8987 */ 8988 if (entryp == NULL) { 8989 8990 /* Similar to code in update_temp */ 8991 fstype = get_fstype("/"); 8992 INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype = NULL); 8993 if (fstype == NULL) { 8994 bam_error(BOOTENV_FSTYPE_FAILED); 8995 rv = BAM_ERROR; 8996 goto done; 8997 } 8998 8999 osdev = get_special("/"); 9000 INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL); 9001 if (osdev == NULL) { 9002 free(fstype); 9003 bam_error(BOOTENV_SPECIAL_FAILED); 9004 rv = BAM_ERROR; 9005 goto done; 9006 } 9007 9008 sign = find_existing_sign("/", osdev, fstype); 9009 INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL); 9010 if (sign == NULL) { 9011 free(fstype); 9012 free(osdev); 9013 bam_error(BOOTENV_SIGN_FAILED); 9014 rv = BAM_ERROR; 9015 goto done; 9016 } 9017 9018 free(osdev); 9019 (void) strlcpy(signbuf, sign, sizeof (signbuf)); 9020 free(sign); 9021 assert(strchr(signbuf, '(') == NULL && 9022 strchr(signbuf, ',') == NULL && 9023 strchr(signbuf, ')') == NULL); 9024 9025 if (optnum == KERNEL_CMD) { 9026 if (strcmp(fstype, "zfs") == 0) { 9027 new_str_len = strlen(new_path) + 9028 strlen(ZFS_BOOT) + 8; 9029 new_arg = s_calloc(1, new_str_len); 9030 (void) snprintf(new_arg, new_str_len, "%s %s", 9031 new_path, ZFS_BOOT); 9032 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn, 9033 new_arg)); 9034 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE, 9035 signbuf, new_arg, NULL, NULL, NULL); 9036 free(new_arg); 9037 } else { 9038 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn, 9039 new_path)); 9040 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE, 9041 signbuf, new_path, NULL, NULL, NULL); 9042 } 9043 } else { 9044 new_str_len = strlen(path) + 8; 9045 if (strcmp(fstype, "zfs") == 0) { 9046 new_str_len += strlen(DIRECT_BOOT_KERNEL_ZFS); 9047 new_arg = s_calloc(1, new_str_len); 9048 (void) snprintf(new_arg, new_str_len, "%s %s", 9049 DIRECT_BOOT_KERNEL_ZFS, path); 9050 } else { 9051 new_str_len += strlen(DIRECT_BOOT_KERNEL); 9052 new_arg = s_calloc(1, new_str_len); 9053 (void) snprintf(new_arg, new_str_len, "%s %s", 9054 DIRECT_BOOT_KERNEL, path); 9055 } 9056 9057 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_ARG, fcn, new_arg)); 9058 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE, 9059 signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE, NULL); 9060 free(new_arg); 9061 } 9062 free(fstype); 9063 INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY", 9064 entryNum = BAM_ERROR); 9065 if (entryNum == BAM_ERROR) { 9066 bam_error(GET_SET_KERNEL_ADD_BOOT_ENTRY, 9067 BOOTENV_RC_TITLE); 9068 rv = BAM_ERROR; 9069 goto done; 9070 } 9071 save_default_entry(mp, BAM_OLD_RC_DEF); 9072 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entryNum); 9073 INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret = BAM_ERROR); 9074 if (ret == BAM_ERROR) { 9075 bam_error(GET_SET_KERNEL_SET_GLOBAL, entryNum); 9076 } 9077 rv = BAM_WRITE; 9078 goto done; 9079 } 9080 9081 /* 9082 * There was already an bootenv entry which we need to edit. 9083 */ 9084 if (optnum == KERNEL_CMD) { 9085 new_str_len = strlen(new_path) + strlen(old_args) + 2; 9086 new_arg = s_calloc(1, new_str_len); 9087 (void) snprintf(new_arg, new_str_len, "%s %s", new_path, 9088 old_args); 9089 free(kernelp->arg); 9090 kernelp->arg = new_arg; 9091 9092 /* 9093 * If we have changed the kernel line, we may need to update 9094 * the archive line as well. 9095 */ 9096 set_archive_line(entryp, kernelp); 9097 BAM_DPRINTF((D_GET_SET_KERNEL_REPLACED_KERNEL_SAME_ARG, fcn, 9098 kernelp->arg)); 9099 } else { 9100 new_str_len = old_kernel_len + strlen(path) + 8; 9101 new_arg = s_calloc(1, new_str_len); 9102 (void) strncpy(new_arg, kernelp->arg, old_kernel_len); 9103 (void) strlcat(new_arg, " ", new_str_len); 9104 (void) strlcat(new_arg, path, new_str_len); 9105 free(kernelp->arg); 9106 kernelp->arg = new_arg; 9107 BAM_DPRINTF((D_GET_SET_KERNEL_SAME_KERNEL_REPLACED_ARG, fcn, 9108 kernelp->arg)); 9109 } 9110 rv = BAM_WRITE; 9111 9112 done: 9113 if ((rv == BAM_WRITE) && kernelp) 9114 update_line(kernelp); 9115 if (free_new_path) 9116 free(new_path); 9117 if (rv == BAM_WRITE) { 9118 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 9119 } else { 9120 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 9121 } 9122 return (rv); 9123 } 9124 9125 static error_t 9126 get_kernel(menu_t *mp, menu_cmd_t optnum, char *buf, size_t bufsize) 9127 { 9128 const char *fcn = "get_kernel()"; 9129 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_cmds[optnum])); 9130 return (get_set_kernel(mp, optnum, NULL, buf, bufsize)); 9131 } 9132 9133 static error_t 9134 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize) 9135 { 9136 const char *fcn = "set_kernel()"; 9137 assert(path != NULL); 9138 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, menu_cmds[optnum], path)); 9139 return (get_set_kernel(mp, optnum, path, buf, bufsize)); 9140 } 9141 9142 /*ARGSUSED*/ 9143 static error_t 9144 set_option(menu_t *mp, char *dummy, char *opt) 9145 { 9146 int optnum; 9147 int optval; 9148 char *val; 9149 char buf[BUFSIZ] = ""; 9150 error_t rv; 9151 const char *fcn = "set_option()"; 9152 9153 assert(mp); 9154 assert(opt); 9155 assert(dummy == NULL); 9156 9157 /* opt is set from bam_argv[0] and is always non-NULL */ 9158 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt)); 9159 9160 val = strchr(opt, '='); 9161 if (val != NULL) { 9162 *val = '\0'; 9163 } 9164 9165 if (strcmp(opt, "default") == 0) { 9166 optnum = DEFAULT_CMD; 9167 } else if (strcmp(opt, "timeout") == 0) { 9168 optnum = TIMEOUT_CMD; 9169 } else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) { 9170 optnum = KERNEL_CMD; 9171 } else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) { 9172 optnum = ARGS_CMD; 9173 } else { 9174 bam_error(INVALID_OPTION, opt); 9175 return (BAM_ERROR); 9176 } 9177 9178 /* 9179 * kernel and args are allowed without "=new_value" strings. All 9180 * others cause errors 9181 */ 9182 if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) { 9183 bam_error(NO_OPTION_ARG, opt); 9184 return (BAM_ERROR); 9185 } else if (val != NULL) { 9186 *val = '='; 9187 } 9188 9189 if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) { 9190 BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum], 9191 val ? val + 1 : "NULL")); 9192 9193 if (val) 9194 rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf)); 9195 else 9196 rv = get_kernel(mp, optnum, buf, sizeof (buf)); 9197 if ((rv == BAM_SUCCESS) && (buf[0] != '\0')) 9198 (void) printf("%s\n", buf); 9199 } else { 9200 optval = s_strtol(val + 1); 9201 BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum], val + 1)); 9202 rv = set_global(mp, menu_cmds[optnum], optval); 9203 } 9204 9205 if (rv == BAM_WRITE || rv == BAM_SUCCESS) { 9206 BAM_DPRINTF((D_RETURN_SUCCESS, fcn)); 9207 } else { 9208 BAM_DPRINTF((D_RETURN_FAILURE, fcn)); 9209 } 9210 9211 return (rv); 9212 } 9213 9214 /* 9215 * The quiet argument suppresses messages. This is used 9216 * when invoked in the context of other commands (e.g. list_entry) 9217 */ 9218 static error_t 9219 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet) 9220 { 9221 line_t *lp; 9222 char *arg; 9223 int done, ret = BAM_SUCCESS; 9224 9225 assert(mp); 9226 assert(menu_path); 9227 assert(globalcmd); 9228 9229 if (mp->start == NULL) { 9230 if (!quiet) 9231 bam_error(NO_MENU, menu_path); 9232 return (BAM_ERROR); 9233 } 9234 9235 done = 0; 9236 for (lp = mp->start; lp; lp = lp->next) { 9237 if (lp->flags != BAM_GLOBAL) 9238 continue; 9239 9240 if (lp->cmd == NULL) { 9241 if (!quiet) 9242 bam_error(NO_CMD, lp->lineNum); 9243 continue; 9244 } 9245 9246 if (strcmp(globalcmd, lp->cmd) != 0) 9247 continue; 9248 9249 /* Found global. Check for duplicates */ 9250 if (done && !quiet) { 9251 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root); 9252 ret = BAM_ERROR; 9253 } 9254 9255 arg = lp->arg ? lp->arg : ""; 9256 bam_print(GLOBAL_CMD, globalcmd, arg); 9257 done = 1; 9258 } 9259 9260 if (!done && bam_verbose) 9261 bam_print(NO_ENTRY, globalcmd); 9262 9263 return (ret); 9264 } 9265 9266 static error_t 9267 menu_write(char *root, menu_t *mp) 9268 { 9269 const char *fcn = "menu_write()"; 9270 9271 BAM_DPRINTF((D_MENU_WRITE_ENTER, fcn, root)); 9272 return (list2file(root, MENU_TMP, GRUB_MENU, mp->start)); 9273 } 9274 9275 void 9276 line_free(line_t *lp) 9277 { 9278 if (lp == NULL) 9279 return; 9280 9281 if (lp->cmd != NULL) 9282 free(lp->cmd); 9283 if (lp->sep) 9284 free(lp->sep); 9285 if (lp->arg) 9286 free(lp->arg); 9287 if (lp->line) 9288 free(lp->line); 9289 free(lp); 9290 } 9291 9292 static void 9293 linelist_free(line_t *start) 9294 { 9295 line_t *lp; 9296 9297 while (start) { 9298 lp = start; 9299 start = start->next; 9300 line_free(lp); 9301 } 9302 } 9303 9304 static void 9305 filelist_free(filelist_t *flistp) 9306 { 9307 linelist_free(flistp->head); 9308 flistp->head = NULL; 9309 flistp->tail = NULL; 9310 } 9311 9312 static void 9313 menu_free(menu_t *mp) 9314 { 9315 entry_t *ent, *tmp; 9316 assert(mp); 9317 9318 if (mp->start) 9319 linelist_free(mp->start); 9320 ent = mp->entries; 9321 while (ent) { 9322 tmp = ent; 9323 ent = tmp->next; 9324 free(tmp); 9325 } 9326 9327 free(mp); 9328 } 9329 9330 /* 9331 * Utility routines 9332 */ 9333 9334 9335 /* 9336 * Returns 0 on success 9337 * Any other value indicates an error 9338 */ 9339 static int 9340 exec_cmd(char *cmdline, filelist_t *flistp) 9341 { 9342 char buf[BUFSIZ]; 9343 int ret; 9344 FILE *ptr; 9345 sigset_t set; 9346 void (*disp)(int); 9347 9348 /* 9349 * For security 9350 * - only absolute paths are allowed 9351 * - set IFS to space and tab 9352 */ 9353 if (*cmdline != '/') { 9354 bam_error(ABS_PATH_REQ, cmdline); 9355 return (-1); 9356 } 9357 (void) putenv("IFS= \t"); 9358 9359 /* 9360 * We may have been exec'ed with SIGCHLD blocked 9361 * unblock it here 9362 */ 9363 (void) sigemptyset(&set); 9364 (void) sigaddset(&set, SIGCHLD); 9365 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) { 9366 bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno)); 9367 return (-1); 9368 } 9369 9370 /* 9371 * Set SIGCHLD disposition to SIG_DFL for popen/pclose 9372 */ 9373 disp = sigset(SIGCHLD, SIG_DFL); 9374 if (disp == SIG_ERR) { 9375 bam_error(FAILED_SIG, strerror(errno)); 9376 return (-1); 9377 } 9378 if (disp == SIG_HOLD) { 9379 bam_error(BLOCKED_SIG, cmdline); 9380 return (-1); 9381 } 9382 9383 ptr = popen(cmdline, "r"); 9384 if (ptr == NULL) { 9385 bam_error(POPEN_FAIL, cmdline, strerror(errno)); 9386 return (-1); 9387 } 9388 9389 /* 9390 * If we simply do a pclose() following a popen(), pclose() 9391 * will close the reader end of the pipe immediately even 9392 * if the child process has not started/exited. pclose() 9393 * does wait for cmd to terminate before returning though. 9394 * When the executed command writes its output to the pipe 9395 * there is no reader process and the command dies with 9396 * SIGPIPE. To avoid this we read repeatedly until read 9397 * terminates with EOF. This indicates that the command 9398 * (writer) has closed the pipe and we can safely do a 9399 * pclose(). 9400 * 9401 * Since pclose() does wait for the command to exit, 9402 * we can safely reap the exit status of the command 9403 * from the value returned by pclose() 9404 */ 9405 while (s_fgets(buf, sizeof (buf), ptr) != NULL) { 9406 if (flistp == NULL) { 9407 /* s_fgets strips newlines, so insert them at the end */ 9408 bam_print(PRINT, buf); 9409 } else { 9410 append_to_flist(flistp, buf); 9411 } 9412 } 9413 9414 ret = pclose(ptr); 9415 if (ret == -1) { 9416 bam_error(PCLOSE_FAIL, cmdline, strerror(errno)); 9417 return (-1); 9418 } 9419 9420 if (WIFEXITED(ret)) { 9421 return (WEXITSTATUS(ret)); 9422 } else { 9423 bam_error(EXEC_FAIL, cmdline, ret); 9424 return (-1); 9425 } 9426 } 9427 9428 /* 9429 * Since this function returns -1 on error 9430 * it cannot be used to convert -1. However, 9431 * that is sufficient for what we need. 9432 */ 9433 static long 9434 s_strtol(char *str) 9435 { 9436 long l; 9437 char *res = NULL; 9438 9439 if (str == NULL) { 9440 return (-1); 9441 } 9442 9443 errno = 0; 9444 l = strtol(str, &res, 10); 9445 if (errno || *res != '\0') { 9446 return (-1); 9447 } 9448 9449 return (l); 9450 } 9451 9452 /* 9453 * Wrapper around fputs, that adds a newline (since fputs doesn't) 9454 */ 9455 static int 9456 s_fputs(char *str, FILE *fp) 9457 { 9458 char linebuf[BAM_MAXLINE]; 9459 9460 (void) snprintf(linebuf, sizeof (linebuf), "%s\n", str); 9461 return (fputs(linebuf, fp)); 9462 } 9463 9464 /* 9465 * Wrapper around fgets, that strips newlines returned by fgets 9466 */ 9467 char * 9468 s_fgets(char *buf, int buflen, FILE *fp) 9469 { 9470 int n; 9471 9472 buf = fgets(buf, buflen, fp); 9473 if (buf) { 9474 n = strlen(buf); 9475 if (n == buflen - 1 && buf[n-1] != '\n') 9476 bam_error(TOO_LONG, buflen - 1, buf); 9477 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1]; 9478 } 9479 9480 return (buf); 9481 } 9482 9483 void * 9484 s_calloc(size_t nelem, size_t sz) 9485 { 9486 void *ptr; 9487 9488 ptr = calloc(nelem, sz); 9489 if (ptr == NULL) { 9490 bam_error(NO_MEM, nelem*sz); 9491 bam_exit(1); 9492 } 9493 return (ptr); 9494 } 9495 9496 void * 9497 s_realloc(void *ptr, size_t sz) 9498 { 9499 ptr = realloc(ptr, sz); 9500 if (ptr == NULL) { 9501 bam_error(NO_MEM, sz); 9502 bam_exit(1); 9503 } 9504 return (ptr); 9505 } 9506 9507 char * 9508 s_strdup(char *str) 9509 { 9510 char *ptr; 9511 9512 if (str == NULL) 9513 return (NULL); 9514 9515 ptr = strdup(str); 9516 if (ptr == NULL) { 9517 bam_error(NO_MEM, strlen(str) + 1); 9518 bam_exit(1); 9519 } 9520 return (ptr); 9521 } 9522 9523 /* 9524 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients) 9525 * Returns 0 otherwise 9526 */ 9527 static int 9528 is_amd64(void) 9529 { 9530 static int amd64 = -1; 9531 char isabuf[257]; /* from sysinfo(2) manpage */ 9532 9533 if (amd64 != -1) 9534 return (amd64); 9535 9536 if (bam_alt_platform) { 9537 if (strcmp(bam_platform, "i86pc") == 0) { 9538 amd64 = 1; /* diskless server */ 9539 } 9540 } else { 9541 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 && 9542 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) { 9543 amd64 = 1; 9544 } else if (strstr(isabuf, "i386") == NULL) { 9545 amd64 = 1; /* diskless server */ 9546 } 9547 } 9548 if (amd64 == -1) 9549 amd64 = 0; 9550 9551 return (amd64); 9552 } 9553 9554 static char * 9555 get_machine(void) 9556 { 9557 static int cached = -1; 9558 static char mbuf[257]; /* from sysinfo(2) manpage */ 9559 9560 if (cached == 0) 9561 return (mbuf); 9562 9563 if (bam_alt_platform) { 9564 return (bam_platform); 9565 } else { 9566 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) { 9567 cached = 1; 9568 } 9569 } 9570 if (cached == -1) { 9571 mbuf[0] = '\0'; 9572 cached = 0; 9573 } 9574 9575 return (mbuf); 9576 } 9577 9578 int 9579 is_sparc(void) 9580 { 9581 static int issparc = -1; 9582 char mbuf[257]; /* from sysinfo(2) manpage */ 9583 9584 if (issparc != -1) 9585 return (issparc); 9586 9587 if (bam_alt_platform) { 9588 if (strncmp(bam_platform, "sun4", 4) == 0) { 9589 issparc = 1; 9590 } 9591 } else { 9592 if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 && 9593 strcmp(mbuf, "sparc") == 0) { 9594 issparc = 1; 9595 } 9596 } 9597 if (issparc == -1) 9598 issparc = 0; 9599 9600 return (issparc); 9601 } 9602 9603 static void 9604 append_to_flist(filelist_t *flistp, char *s) 9605 { 9606 line_t *lp; 9607 9608 lp = s_calloc(1, sizeof (line_t)); 9609 lp->line = s_strdup(s); 9610 if (flistp->head == NULL) 9611 flistp->head = lp; 9612 else 9613 flistp->tail->next = lp; 9614 flistp->tail = lp; 9615 } 9616 9617 #if !defined(_OPB) 9618 9619 UCODE_VENDORS; 9620 9621 /*ARGSUSED*/ 9622 static void 9623 ucode_install(char *root) 9624 { 9625 int i; 9626 9627 for (i = 0; ucode_vendors[i].filestr != NULL; i++) { 9628 int cmd_len = PATH_MAX + 256; 9629 char cmd[PATH_MAX + 256]; 9630 char file[PATH_MAX]; 9631 char timestamp[PATH_MAX]; 9632 struct stat fstatus, tstatus; 9633 struct utimbuf u_times; 9634 9635 (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s", 9636 bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr, 9637 ucode_vendors[i].extstr); 9638 9639 if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode))) 9640 continue; 9641 9642 (void) snprintf(timestamp, PATH_MAX, "%s.ts", file); 9643 9644 if (stat(timestamp, &tstatus) == 0 && 9645 fstatus.st_mtime <= tstatus.st_mtime) 9646 continue; 9647 9648 (void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R " 9649 "%s/%s/%s %s > /dev/null 2>&1", bam_root, 9650 UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file); 9651 if (system(cmd) != 0) 9652 return; 9653 9654 if (creat(timestamp, S_IRUSR | S_IWUSR) == -1) 9655 return; 9656 9657 u_times.actime = fstatus.st_atime; 9658 u_times.modtime = fstatus.st_mtime; 9659 (void) utime(timestamp, &u_times); 9660 } 9661 } 9662 #endif 9663