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