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