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