6659 nvlist_free(NULL) is a no-op
[illumos-gate.git] / usr / src / cmd / boot / bootadm / bootadm.c
blobc520301579d62fb7c9da4b33e5176c8974343737
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2012 Milan Jurik. All rights reserved.
27 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
31 * bootadm(1M) is a new utility for managing bootability of
32 * Solaris *Newboot* environments. It has two primary tasks:
33 * - Allow end users to manage bootability of Newboot Solaris instances
34 * - Provide services to other subsystems in Solaris (primarily Install)
37 /* Headers */
38 #include <stdio.h>
39 #include <errno.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <alloca.h>
46 #include <stdarg.h>
47 #include <limits.h>
48 #include <signal.h>
49 #include <sys/wait.h>
50 #include <sys/mnttab.h>
51 #include <sys/mntent.h>
52 #include <sys/statvfs.h>
53 #include <libnvpair.h>
54 #include <ftw.h>
55 #include <fcntl.h>
56 #include <strings.h>
57 #include <utime.h>
58 #include <sys/systeminfo.h>
59 #include <sys/dktp/fdisk.h>
60 #include <sys/param.h>
61 #include <dirent.h>
62 #include <ctype.h>
63 #include <libgen.h>
64 #include <sys/sysmacros.h>
65 #include <sys/elf.h>
66 #include <libscf.h>
67 #include <zlib.h>
68 #include <sys/lockfs.h>
69 #include <sys/filio.h>
70 #include <libbe.h>
71 #ifdef i386
72 #include <libfdisk.h>
73 #endif
75 #if !defined(_OBP)
76 #include <sys/ucode.h>
77 #endif
79 #include <pwd.h>
80 #include <grp.h>
81 #include <device_info.h>
82 #include <sys/vtoc.h>
83 #include <sys/efi_partition.h>
84 #include <regex.h>
85 #include <locale.h>
86 #include <sys/mkdev.h>
88 #include "message.h"
89 #include "bootadm.h"
91 #ifndef TEXT_DOMAIN
92 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
93 #endif /* TEXT_DOMAIN */
95 /* Type definitions */
97 /* Primary subcmds */
98 typedef enum {
99 BAM_MENU = 3,
100 BAM_ARCHIVE,
101 BAM_INSTALL
102 } subcmd_t;
104 typedef enum {
105 OPT_ABSENT = 0, /* No option */
106 OPT_REQ, /* option required */
107 OPT_OPTIONAL /* option may or may not be present */
108 } option_t;
110 typedef struct {
111 char *subcmd;
112 option_t option;
113 error_t (*handler)();
114 int unpriv; /* is this an unprivileged command */
115 } subcmd_defn_t;
117 #define LINE_INIT 0 /* lineNum initial value */
118 #define ENTRY_INIT -1 /* entryNum initial value */
119 #define ALL_ENTRIES -2 /* selects all boot entries */
121 #define PARTNO_NOTFOUND -1 /* Solaris partition not found */
122 #define PARTNO_EFI -2 /* EFI partition table found */
124 #define GRUB_DIR "/boot/grub"
125 #define GRUB_STAGE2 GRUB_DIR "/stage2"
126 #define GRUB_MENU "/boot/grub/menu.lst"
127 #define MENU_TMP "/boot/grub/menu.lst.tmp"
128 #define GRUB_BACKUP_MENU "/etc/lu/GRUB_backup_menu"
129 #define RAMDISK_SPECIAL "/dev/ramdisk/"
130 #define STUBBOOT "/stubboot"
131 #define MULTIBOOT "/platform/i86pc/multiboot"
132 #define GRUBSIGN_DIR "/boot/grub/bootsign"
133 #define GRUBSIGN_BACKUP "/etc/bootsign"
134 #define GRUBSIGN_UFS_PREFIX "rootfs"
135 #define GRUBSIGN_ZFS_PREFIX "pool_"
136 #define GRUBSIGN_LU_PREFIX "BE_"
137 #define UFS_SIGNATURE_LIST "/var/run/grub_ufs_signatures"
138 #define ZFS_LEGACY_MNTPT "/tmp/bootadm_mnt_zfs_legacy"
140 #define BOOTADM_RDONLY_TEST "BOOTADM_RDONLY_TEST"
142 /* lock related */
143 #define BAM_LOCK_FILE "/var/run/bootadm.lock"
144 #define LOCK_FILE_PERMS (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
146 #define CREATE_RAMDISK "boot/solaris/bin/create_ramdisk"
147 #define CREATE_DISKMAP "boot/solaris/bin/create_diskmap"
148 #define EXTRACT_BOOT_FILELIST "boot/solaris/bin/extract_boot_filelist"
149 #define GRUBDISK_MAP "/var/run/solaris_grubdisk.map"
151 #define GRUB_slice "/etc/lu/GRUB_slice"
152 #define GRUB_root "/etc/lu/GRUB_root"
153 #define GRUB_fdisk "/etc/lu/GRUB_fdisk"
154 #define GRUB_fdisk_target "/etc/lu/GRUB_fdisk_target"
155 #define FINDROOT_INSTALLGRUB "/etc/lu/installgrub.findroot"
156 #define LULIB "/usr/lib/lu/lulib"
157 #define LULIB_PROPAGATE_FILE "lulib_propagate_file"
158 #define CKSUM "/usr/bin/cksum"
159 #define LU_MENU_CKSUM "/etc/lu/menu.cksum"
160 #define BOOTADM "/sbin/bootadm"
162 #define INSTALLGRUB "/sbin/installgrub"
163 #define STAGE1 "/boot/grub/stage1"
164 #define STAGE2 "/boot/grub/stage2"
166 typedef enum zfs_mnted {
167 ZFS_MNT_ERROR = -1,
168 LEGACY_MOUNTED = 1,
169 LEGACY_ALREADY,
170 ZFS_MOUNTED,
171 ZFS_ALREADY
172 } zfs_mnted_t;
175 * Default file attributes
177 #define DEFAULT_DEV_MODE 0644 /* default permissions */
178 #define DEFAULT_DEV_UID 0 /* user root */
179 #define DEFAULT_DEV_GID 3 /* group sys */
182 * Menu related
183 * menu_cmd_t and menu_cmds must be kept in sync
185 char *menu_cmds[] = {
186 "default", /* DEFAULT_CMD */
187 "timeout", /* TIMEOUT_CMD */
188 "title", /* TITLE_CMD */
189 "root", /* ROOT_CMD */
190 "kernel", /* KERNEL_CMD */
191 "kernel$", /* KERNEL_DOLLAR_CMD */
192 "module", /* MODULE_CMD */
193 "module$", /* MODULE_DOLLAR_CMD */
194 " ", /* SEP_CMD */
195 "#", /* COMMENT_CMD */
196 "chainloader", /* CHAINLOADER_CMD */
197 "args", /* ARGS_CMD */
198 "findroot", /* FINDROOT_CMD */
199 "bootfs", /* BOOTFS_CMD */
200 NULL
203 #define OPT_ENTRY_NUM "entry"
206 * exec_cmd related
208 typedef struct {
209 line_t *head;
210 line_t *tail;
211 } filelist_t;
213 #define BOOT_FILE_LIST "boot/solaris/filelist.ramdisk"
214 #define ETC_FILE_LIST "etc/boot/solaris/filelist.ramdisk"
216 #define FILE_STAT "boot/solaris/filestat.ramdisk"
217 #define FILE_STAT_TMP "boot/solaris/filestat.ramdisk.tmp"
218 #define DIR_PERMS (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
219 #define FILE_STAT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
221 #define FILE_STAT_TIMESTAMP "boot/solaris/timestamp.cache"
223 /* Globals */
224 int bam_verbose;
225 int bam_force;
226 int bam_debug;
227 static char *prog;
228 static subcmd_t bam_cmd;
229 static char *bam_root;
230 static int bam_rootlen;
231 static int bam_root_readonly;
232 static int bam_alt_root;
233 static int bam_extend = 0;
234 static int bam_purge = 0;
235 static char *bam_subcmd;
236 static char *bam_opt;
237 static char **bam_argv;
238 static char *bam_pool;
239 static int bam_argc;
240 static int bam_check;
241 static int bam_saved_check;
242 static int bam_smf_check;
243 static int bam_lock_fd = -1;
244 static int bam_zfs;
245 static int bam_mbr;
246 static char rootbuf[PATH_MAX] = "/";
247 static int bam_update_all;
248 static int bam_alt_platform;
249 static char *bam_platform;
250 static char *bam_home_env = NULL;
252 /* function prototypes */
253 static void parse_args_internal(int, char *[]);
254 static void parse_args(int, char *argv[]);
255 static error_t bam_menu(char *, char *, int, char *[]);
256 static error_t bam_install(char *, char *);
257 static error_t bam_archive(char *, char *);
259 static void bam_lock(void);
260 static void bam_unlock(void);
262 static int exec_cmd(char *, filelist_t *);
263 static error_t read_globals(menu_t *, char *, char *, int);
264 static int menu_on_bootdisk(char *os_root, char *menu_root);
265 static menu_t *menu_read(char *);
266 static error_t menu_write(char *, menu_t *);
267 static void linelist_free(line_t *);
268 static void menu_free(menu_t *);
269 static void filelist_free(filelist_t *);
270 static error_t list2file(char *, char *, char *, line_t *);
271 static error_t list_entry(menu_t *, char *, char *);
272 static error_t list_setting(menu_t *, char *, char *);
273 static error_t delete_all_entries(menu_t *, char *, char *);
274 static error_t update_entry(menu_t *mp, char *menu_root, char *opt);
275 static error_t update_temp(menu_t *mp, char *dummy, char *opt);
277 static error_t install_bootloader(void);
278 static error_t update_archive(char *, char *);
279 static error_t list_archive(char *, char *);
280 static error_t update_all(char *, char *);
281 static error_t read_list(char *, filelist_t *);
282 static error_t set_option(menu_t *, char *, char *);
283 static error_t set_kernel(menu_t *, menu_cmd_t, char *, char *, size_t);
284 static error_t get_kernel(menu_t *, menu_cmd_t, char *, size_t);
285 static char *expand_path(const char *);
287 static long s_strtol(char *);
288 static int s_fputs(char *, FILE *);
290 static int is_zfs(char *root);
291 static int is_ufs(char *root);
292 static int is_pcfs(char *root);
293 static int is_amd64(void);
294 static char *get_machine(void);
295 static void append_to_flist(filelist_t *, char *);
296 static char *mount_top_dataset(char *pool, zfs_mnted_t *mnted);
297 static int umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt);
298 static int ufs_add_to_sign_list(char *sign);
299 static error_t synchronize_BE_menu(void);
301 #if !defined(_OBP)
302 static void ucode_install();
303 #endif
305 /* Menu related sub commands */
306 static subcmd_defn_t menu_subcmds[] = {
307 "set_option", OPT_ABSENT, set_option, 0, /* PUB */
308 "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */
309 "delete_all_entries", OPT_ABSENT, delete_all_entries, 0, /* PVT */
310 "update_entry", OPT_REQ, update_entry, 0, /* menu */
311 "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */
312 "upgrade", OPT_ABSENT, upgrade_menu, 0, /* menu */
313 "list_setting", OPT_OPTIONAL, list_setting, 1, /* menu */
314 "disable_hypervisor", OPT_ABSENT, cvt_to_metal, 0, /* menu */
315 "enable_hypervisor", OPT_ABSENT, cvt_to_hyper, 0, /* menu */
316 NULL, 0, NULL, 0 /* must be last */
319 /* Archive related sub commands */
320 static subcmd_defn_t arch_subcmds[] = {
321 "update", OPT_ABSENT, update_archive, 0, /* PUB */
322 "update_all", OPT_ABSENT, update_all, 0, /* PVT */
323 "list", OPT_OPTIONAL, list_archive, 1, /* PUB */
324 NULL, 0, NULL, 0 /* must be last */
327 /* Install related sub commands */
328 static subcmd_defn_t inst_subcmds[] = {
329 "install_bootloader", OPT_ABSENT, install_bootloader, 0, /* PUB */
330 NULL, 0, NULL, 0 /* must be last */
333 enum dircache_copy_opt {
334 FILE32 = 0,
335 FILE64,
336 CACHEDIR_NUM
340 * Directory specific flags:
341 * NEED_UPDATE : the specified archive needs to be updated
342 * NO_MULTI : don't extend the specified archive, but recreate it
344 #define NEED_UPDATE 0x00000001
345 #define NO_MULTI 0x00000002
347 #define set_dir_flag(id, f) (walk_arg.dirinfo[id].flags |= f)
348 #define unset_dir_flag(id, f) (walk_arg.dirinfo[id].flags &= ~f)
349 #define is_dir_flag_on(id, f) (walk_arg.dirinfo[id].flags & f ? 1 : 0)
351 #define get_cachedir(id) (walk_arg.dirinfo[id].cdir_path)
352 #define get_updatedir(id) (walk_arg.dirinfo[id].update_path)
353 #define get_count(id) (walk_arg.dirinfo[id].count)
354 #define has_cachedir(id) (walk_arg.dirinfo[id].has_dir)
355 #define set_dir_present(id) (walk_arg.dirinfo[id].has_dir = 1)
358 * dirinfo_t (specific cache directory information):
359 * cdir_path: path to the archive cache directory
360 * update_path: path to the update directory (contains the files that will be
361 * used to extend the archive)
362 * has_dir: the specified cache directory is active
363 * count: the number of files to update
364 * flags: directory specific flags
366 typedef struct _dirinfo {
367 char cdir_path[PATH_MAX];
368 char update_path[PATH_MAX];
369 int has_dir;
370 int count;
371 int flags;
372 } dirinfo_t;
375 * Update flags:
376 * NEED_CACHE_DIR : cache directory is missing and needs to be created
377 * IS_SPARC_TARGET : the target mountpoint is a SPARC environment
378 * UPDATE_ERROR : an error occourred while traversing the list of files
379 * RDONLY_FSCHK : the target filesystem is read-only
380 * RAMDSK_FSCHK : the target filesystem is on a ramdisk
382 #define NEED_CACHE_DIR 0x00000001
383 #define IS_SPARC_TARGET 0x00000002
384 #define UPDATE_ERROR 0x00000004
385 #define RDONLY_FSCHK 0x00000008
386 #define INVALIDATE_CACHE 0x00000010
388 #define is_flag_on(flag) (walk_arg.update_flags & flag ? 1 : 0)
389 #define set_flag(flag) (walk_arg.update_flags |= flag)
390 #define unset_flag(flag) (walk_arg.update_flags &= ~flag)
393 * struct walk_arg :
394 * update_flags: flags related to the current updating process
395 * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs
396 * sparcfile: list of file paths for mkisofs -path-list (SPARC only)
398 static struct {
399 int update_flags;
400 nvlist_t *new_nvlp;
401 nvlist_t *old_nvlp;
402 FILE *sparcfile;
403 dirinfo_t dirinfo[CACHEDIR_NUM];
404 } walk_arg;
406 struct safefile {
407 char *name;
408 struct safefile *next;
411 static struct safefile *safefiles = NULL;
414 * svc:/system/filesystem/usr:default service checks for this file and
415 * does a boot archive update and then reboot the system.
417 #define NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
420 * svc:/system/boot-archive-update:default checks for this file and
421 * updates the boot archive.
423 #define NEED_UPDATE_SAFE_FILE "/etc/svc/volatile/boot_archive_safefile_update"
425 /* Thanks growisofs */
426 #define CD_BLOCK ((off64_t)2048)
427 #define VOLDESC_OFF 16
428 #define DVD_BLOCK (32*1024)
429 #define MAX_IVDs 16
431 struct iso_pdesc {
432 unsigned char type [1];
433 unsigned char id [5];
434 unsigned char void1 [80-5-1];
435 unsigned char volume_space_size [8];
436 unsigned char void2 [2048-80-8];
440 * COUNT_MAX: maximum number of changed files to justify a multisession update
441 * BA_SIZE_MAX: maximum size of the boot_archive to justify a multisession
442 * update
444 #define COUNT_MAX 50
445 #define BA_SIZE_MAX (50 * 1024 * 1024)
447 #define bam_nowrite() (bam_check || bam_smf_check)
449 static int sync_menu = 1; /* whether we need to sync the BE menus */
451 static void
452 usage(void)
454 (void) fprintf(stderr, "USAGE:\n");
456 /* archive usage */
457 (void) fprintf(stderr,
458 "\t%s update-archive [-vn] [-R altroot [-p platform]]\n", prog);
459 (void) fprintf(stderr,
460 "\t%s list-archive [-R altroot [-p platform]]\n", prog);
461 #if defined(_OBP)
462 (void) fprintf(stderr,
463 "\t%s install-bootloader [-fv] [-R altroot] [-P pool]\n", prog);
464 #else
465 (void) fprintf(stderr,
466 "\t%s install-bootloader [-Mfv] [-R altroot] [-P pool]\n", prog);
467 #endif
468 #if !defined(_OBP)
469 /* x86 only */
470 (void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
471 (void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
472 #endif
476 * Best effort attempt to restore the $HOME value.
478 static void
479 restore_env()
481 char home_env[PATH_MAX];
483 if (bam_home_env) {
484 (void) snprintf(home_env, sizeof (home_env), "HOME=%s",
485 bam_home_env);
486 (void) putenv(home_env);
491 #define SLEEP_TIME 5
492 #define MAX_TRIES 4
495 * Sanitize the environment in which bootadm will execute its sub-processes
496 * (ex. mkisofs). This is done to prevent those processes from attempting
497 * to access files (ex. .mkisofsrc) or stat paths that might be on NFS
498 * or, potentially, insecure.
500 static void
501 sanitize_env()
503 int stry = 0;
505 /* don't depend on caller umask */
506 (void) umask(0022);
508 /* move away from a potential unsafe current working directory */
509 while (chdir("/") == -1) {
510 if (errno != EINTR) {
511 bam_print("WARNING: unable to chdir to /");
512 break;
516 bam_home_env = getenv("HOME");
517 while (bam_home_env != NULL && putenv("HOME=/") == -1) {
518 if (errno == ENOMEM) {
519 /* retry no more than MAX_TRIES times */
520 if (++stry > MAX_TRIES) {
521 bam_print("WARNING: unable to recover from "
522 "system memory pressure... aborting \n");
523 bam_exit(EXIT_FAILURE);
525 /* memory is tight, try to sleep */
526 bam_print("Attempting to recover from memory pressure: "
527 "sleeping for %d seconds\n", SLEEP_TIME * stry);
528 (void) sleep(SLEEP_TIME * stry);
529 } else {
530 bam_print("WARNING: unable to sanitize HOME\n");
536 main(int argc, char *argv[])
538 error_t ret;
540 (void) setlocale(LC_ALL, "");
541 (void) textdomain(TEXT_DOMAIN);
543 if ((prog = strrchr(argv[0], '/')) == NULL) {
544 prog = argv[0];
545 } else {
546 prog++;
549 INJECT_ERROR1("ASSERT_ON", assert(0))
551 sanitize_env();
553 parse_args(argc, argv);
555 switch (bam_cmd) {
556 case BAM_MENU:
557 ret = bam_menu(bam_subcmd, bam_opt, bam_argc, bam_argv);
558 break;
559 case BAM_ARCHIVE:
560 ret = bam_archive(bam_subcmd, bam_opt);
561 break;
562 case BAM_INSTALL:
563 ret = bam_install(bam_subcmd, bam_opt);
564 break;
565 default:
566 usage();
567 bam_exit(1);
570 if (ret != BAM_SUCCESS)
571 bam_exit((ret == BAM_NOCHANGE) ? 2 : 1);
573 bam_unlock();
574 return (0);
578 * Equivalence of public and internal commands:
579 * update-archive -- -a update
580 * list-archive -- -a list
581 * set-menu -- -m set_option
582 * list-menu -- -m list_entry
583 * update-menu -- -m update_entry
584 * install-bootloader -- -i install_bootloader
586 static struct cmd_map {
587 char *bam_cmdname;
588 int bam_cmd;
589 char *bam_subcmd;
590 } cmd_map[] = {
591 { "update-archive", BAM_ARCHIVE, "update"},
592 { "list-archive", BAM_ARCHIVE, "list"},
593 { "set-menu", BAM_MENU, "set_option"},
594 { "list-menu", BAM_MENU, "list_entry"},
595 { "update-menu", BAM_MENU, "update_entry"},
596 { "install-bootloader", BAM_INSTALL, "install_bootloader"},
597 { NULL, 0, NULL}
601 * Commands syntax published in bootadm(1M) are parsed here
603 static void
604 parse_args(int argc, char *argv[])
606 struct cmd_map *cmp = cmd_map;
608 /* command conforming to the final spec */
609 if (argc > 1 && argv[1][0] != '-') {
611 * Map commands to internal table.
613 while (cmp->bam_cmdname) {
614 if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
615 bam_cmd = cmp->bam_cmd;
616 bam_subcmd = cmp->bam_subcmd;
617 break;
619 cmp++;
621 if (cmp->bam_cmdname == NULL) {
622 usage();
623 bam_exit(1);
625 argc--;
626 argv++;
629 parse_args_internal(argc, argv);
633 * A combination of public and private commands are parsed here.
634 * The internal syntax and the corresponding functionality are:
635 * -a update -- update-archive
636 * -a list -- list-archive
637 * -a update-all -- (reboot to sync all mnted OS archive)
638 * -i install_bootloader -- install-bootloader
639 * -m update_entry -- update-menu
640 * -m list_entry -- list-menu
641 * -m update_temp -- (reboot -- [boot-args])
642 * -m delete_all_entries -- (called from install)
643 * -m enable_hypervisor [args] -- cvt_to_hyper
644 * -m disable_hypervisor -- cvt_to_metal
645 * -m list_setting [entry] [value] -- list_setting
647 * A set of private flags is there too:
648 * -F -- purge the cache directories and rebuild them
649 * -e -- use the (faster) archive update approach (used by
650 * reboot)
652 static void
653 parse_args_internal(int argc, char *argv[])
655 int c, error;
656 extern char *optarg;
657 extern int optind, opterr;
658 #if defined(_OBP)
659 const char *optstring = "a:d:fi:m:no:veFCR:p:P:XZ";
660 #else
661 const char *optstring = "a:d:fi:m:no:veFCMR:p:P:XZ";
662 #endif
664 /* Suppress error message from getopt */
665 opterr = 0;
667 error = 0;
668 while ((c = getopt(argc, argv, optstring)) != -1) {
669 switch (c) {
670 case 'a':
671 if (bam_cmd) {
672 error = 1;
673 bam_error(MULT_CMDS, c);
675 bam_cmd = BAM_ARCHIVE;
676 bam_subcmd = optarg;
677 break;
678 case 'd':
679 if (bam_debug) {
680 error = 1;
681 bam_error(DUP_OPT, c);
683 bam_debug = s_strtol(optarg);
684 break;
685 case 'f':
686 bam_force = 1;
687 break;
688 case 'F':
689 bam_purge = 1;
690 break;
691 case 'i':
692 if (bam_cmd) {
693 error = 1;
694 bam_error(MULT_CMDS, c);
696 bam_cmd = BAM_INSTALL;
697 bam_subcmd = optarg;
698 break;
699 case 'm':
700 if (bam_cmd) {
701 error = 1;
702 bam_error(MULT_CMDS, c);
704 bam_cmd = BAM_MENU;
705 bam_subcmd = optarg;
706 break;
707 #if !defined(_OBP)
708 case 'M':
709 bam_mbr = 1;
710 break;
711 #endif
712 case 'n':
713 bam_check = 1;
715 * We save the original value of bam_check. The new
716 * approach in case of a read-only filesystem is to
717 * behave as a check, so we need a way to restore the
718 * original value after the evaluation of the read-only
719 * filesystem has been done.
720 * Even if we don't allow at the moment a check with
721 * update_all, this approach is more robust than
722 * simply resetting bam_check to zero.
724 bam_saved_check = 1;
725 break;
726 case 'o':
727 if (bam_opt) {
728 error = 1;
729 bam_error(DUP_OPT, c);
731 bam_opt = optarg;
732 break;
733 case 'v':
734 bam_verbose = 1;
735 break;
736 case 'C':
737 bam_smf_check = 1;
738 break;
739 case 'P':
740 if (bam_pool != NULL) {
741 error = 1;
742 bam_error(DUP_OPT, c);
744 bam_pool = optarg;
745 break;
746 case 'R':
747 if (bam_root) {
748 error = 1;
749 bam_error(DUP_OPT, c);
750 break;
751 } else if (realpath(optarg, rootbuf) == NULL) {
752 error = 1;
753 bam_error(CANT_RESOLVE, optarg,
754 strerror(errno));
755 break;
757 bam_alt_root = 1;
758 bam_root = rootbuf;
759 bam_rootlen = strlen(rootbuf);
760 break;
761 case 'p':
762 bam_alt_platform = 1;
763 bam_platform = optarg;
764 if ((strcmp(bam_platform, "i86pc") != 0) &&
765 (strcmp(bam_platform, "sun4u") != 0) &&
766 (strcmp(bam_platform, "sun4v") != 0)) {
767 error = 1;
768 bam_error(INVALID_PLAT, bam_platform);
770 break;
771 case 'X':
772 bam_is_hv = BAM_HV_PRESENT;
773 break;
774 case 'Z':
775 bam_zfs = 1;
776 break;
777 case 'e':
778 bam_extend = 1;
779 break;
780 case '?':
781 error = 1;
782 bam_error(BAD_OPT, optopt);
783 break;
784 default :
785 error = 1;
786 bam_error(BAD_OPT, c);
787 break;
792 * An alternate platform requires an alternate root
794 if (bam_alt_platform && bam_alt_root == 0) {
795 usage();
796 bam_exit(0);
800 * A command option must be specfied
802 if (!bam_cmd) {
803 if (bam_opt && strcmp(bam_opt, "all") == 0) {
804 usage();
805 bam_exit(0);
807 bam_error(NEED_CMD);
808 error = 1;
811 if (error) {
812 usage();
813 bam_exit(1);
816 if (optind > argc) {
817 bam_error(INT_ERROR, "parse_args");
818 bam_exit(1);
819 } else if (optind < argc) {
820 bam_argv = &argv[optind];
821 bam_argc = argc - optind;
825 * mbr and pool are options for install_bootloader
827 if (bam_cmd != BAM_INSTALL && (bam_mbr || bam_pool != NULL)) {
828 usage();
829 bam_exit(0);
833 * -n implies verbose mode
835 if (bam_check)
836 bam_verbose = 1;
839 static error_t
840 check_subcmd_and_options(
841 char *subcmd,
842 char *opt,
843 subcmd_defn_t *table,
844 error_t (**fp)())
846 int i;
848 if (subcmd == NULL) {
849 bam_error(NEED_SUBCMD);
850 return (BAM_ERROR);
853 if (strcmp(subcmd, "set_option") == 0) {
854 if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) {
855 bam_error(MISSING_ARG);
856 usage();
857 return (BAM_ERROR);
858 } else if (bam_argc > 1 || bam_argv[1] != NULL) {
859 bam_error(TRAILING_ARGS);
860 usage();
861 return (BAM_ERROR);
863 } else if (strcmp(subcmd, "update_all") == 0) {
865 * The only option we accept for the "update_all"
866 * subcmd is "fastboot".
868 if (bam_argc > 1 || (bam_argc == 1 &&
869 strcmp(bam_argv[0], "fastboot") != 0)) {
870 bam_error(TRAILING_ARGS);
871 usage();
872 return (BAM_ERROR);
874 if (bam_argc == 1)
875 sync_menu = 0;
876 } else if (((strcmp(subcmd, "enable_hypervisor") != 0) &&
877 (strcmp(subcmd, "list_setting") != 0)) && (bam_argc || bam_argv)) {
879 * Of the remaining subcommands, only "enable_hypervisor" and
880 * "list_setting" take trailing arguments.
882 bam_error(TRAILING_ARGS);
883 usage();
884 return (BAM_ERROR);
887 if (bam_root == NULL) {
888 bam_root = rootbuf;
889 bam_rootlen = 1;
892 /* verify that subcmd is valid */
893 for (i = 0; table[i].subcmd != NULL; i++) {
894 if (strcmp(table[i].subcmd, subcmd) == 0)
895 break;
898 if (table[i].subcmd == NULL) {
899 bam_error(INVALID_SUBCMD, subcmd);
900 return (BAM_ERROR);
903 if (table[i].unpriv == 0 && geteuid() != 0) {
904 bam_error(MUST_BE_ROOT);
905 return (BAM_ERROR);
909 * Currently only privileged commands need a lock
911 if (table[i].unpriv == 0)
912 bam_lock();
914 /* subcmd verifies that opt is appropriate */
915 if (table[i].option != OPT_OPTIONAL) {
916 if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
917 if (opt)
918 bam_error(NO_OPT_REQ, subcmd);
919 else
920 bam_error(MISS_OPT, subcmd);
921 return (BAM_ERROR);
925 *fp = table[i].handler;
927 return (BAM_SUCCESS);
931 * NOTE: A single "/" is also considered a trailing slash and will
932 * be deleted.
934 static void
935 elide_trailing_slash(const char *src, char *dst, size_t dstsize)
937 size_t dstlen;
939 assert(src);
940 assert(dst);
942 (void) strlcpy(dst, src, dstsize);
944 dstlen = strlen(dst);
945 if (dst[dstlen - 1] == '/') {
946 dst[dstlen - 1] = '\0';
950 static int
951 is_safe_exec(char *path)
953 struct stat sb;
955 if (lstat(path, &sb) != 0) {
956 bam_error(STAT_FAIL, path, strerror(errno));
957 return (BAM_ERROR);
960 if (!S_ISREG(sb.st_mode)) {
961 bam_error(PATH_EXEC_LINK, path);
962 return (BAM_ERROR);
965 if (sb.st_uid != getuid()) {
966 bam_error(PATH_EXEC_OWNER, path, getuid());
967 return (BAM_ERROR);
970 if (sb.st_mode & S_IWOTH || sb.st_mode & S_IWGRP) {
971 bam_error(PATH_EXEC_PERMS, path);
972 return (BAM_ERROR);
975 return (BAM_SUCCESS);
978 static error_t
979 list_setting(menu_t *mp, char *which, char *setting)
981 line_t *lp;
982 entry_t *ent;
984 char *p = which;
985 int entry;
987 int found;
989 assert(which);
990 assert(setting);
992 if (*which != NULL) {
994 * If "which" is not a number, assume it's a setting we want
995 * to look for and so set up the routine to look for "which"
996 * in the default entry.
998 while (*p != NULL)
999 if (!(isdigit((int)*p++))) {
1000 setting = which;
1001 which = mp->curdefault->arg;
1002 break;
1004 } else {
1005 which = mp->curdefault->arg;
1008 entry = atoi(which);
1010 for (ent = mp->entries; ((ent != NULL) && (ent->entryNum != entry));
1011 ent = ent->next)
1014 if (!ent) {
1015 bam_error(NO_MATCH_ENTRY);
1016 return (BAM_ERROR);
1019 found = (*setting == NULL);
1021 for (lp = ent->start; lp != NULL; lp = lp->next) {
1022 if ((*setting == NULL) && (lp->flags != BAM_COMMENT))
1023 bam_print(PRINT, lp->line);
1024 else if (lp->cmd != NULL && strcmp(setting, lp->cmd) == 0) {
1025 bam_print(PRINT, lp->arg);
1026 found = 1;
1029 if (lp == ent->end)
1030 break;
1033 if (!found) {
1034 bam_error(NO_MATCH_ENTRY);
1035 return (BAM_ERROR);
1038 return (BAM_SUCCESS);
1041 static error_t
1042 install_bootloader(void)
1044 nvlist_t *nvl;
1045 uint16_t flags = 0;
1046 int found = 0;
1047 struct extmnttab mnt;
1048 struct stat statbuf = {0};
1049 be_node_list_t *be_nodes, *node;
1050 FILE *fp;
1051 char *root_ds = NULL;
1052 int ret;
1054 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
1055 bam_error(_("out of memory\n"));
1056 return (BAM_ERROR);
1060 * if bam_alt_root is set, the stage files are used from alt root.
1061 * if pool is set, the target devices are pool devices, stage files
1062 * are read from pool bootfs unless alt root is set.
1064 * use arguments as targets, stage files are from alt or current root
1065 * if no arguments and no pool, install on current boot pool.
1068 if (bam_alt_root) {
1069 if (stat(bam_root, &statbuf) != 0) {
1070 bam_error(STAT_FAIL, bam_root, strerror(errno));
1071 ret = BAM_ERROR;
1072 goto done;
1074 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1075 bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
1076 ret = BAM_ERROR;
1077 goto done;
1079 resetmnttab(fp);
1080 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1081 if (mnt.mnt_major == major(statbuf.st_dev) &&
1082 mnt.mnt_minor == minor(statbuf.st_dev)) {
1083 found = 1;
1084 root_ds = strdup(mnt.mnt_special);
1085 break;
1088 (void) fclose(fp);
1090 if (found == 0) {
1091 bam_error(NOT_IN_MNTTAB, bam_root);
1092 ret = BAM_ERROR;
1093 goto done;
1095 if (root_ds == NULL) {
1096 bam_error(_("out of memory\n"));
1097 ret = BAM_ERROR;
1098 goto done;
1101 if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
1102 bam_error(_("No BE's found\n"));
1103 ret = BAM_ERROR;
1104 goto done;
1106 for (node = be_nodes; node != NULL; node = node->be_next_node)
1107 if (strcmp(root_ds, node->be_root_ds) == 0)
1108 break;
1110 if (node == NULL)
1111 bam_error(_("BE (%s) does not exist\n"), root_ds);
1113 free(root_ds);
1114 root_ds = NULL;
1115 if (node == NULL) {
1116 be_free_list(be_nodes);
1117 ret = BAM_ERROR;
1118 goto done;
1120 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1121 node->be_node_name);
1122 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1123 node->be_root_ds);
1124 be_free_list(be_nodes);
1125 if (ret) {
1126 ret = BAM_ERROR;
1127 goto done;
1131 if (bam_force)
1132 flags |= BE_INSTALLBOOT_FLAG_FORCE;
1133 if (bam_mbr)
1134 flags |= BE_INSTALLBOOT_FLAG_MBR;
1135 if (bam_verbose)
1136 flags |= BE_INSTALLBOOT_FLAG_VERBOSE;
1138 if (nvlist_add_uint16(nvl, BE_ATTR_INSTALL_FLAGS, flags) != 0) {
1139 bam_error(_("out of memory\n"));
1140 goto done;
1144 * if altroot was set, we got be name and be root, only need
1145 * to set pool name as target.
1146 * if no altroot, need to find be name and root from pool.
1148 if (bam_pool != NULL) {
1149 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, bam_pool);
1150 if (ret) {
1151 ret = BAM_ERROR;
1152 goto done;
1154 if (found) {
1155 ret = be_installboot(nvl);
1156 if (ret)
1157 ret = BAM_ERROR;
1158 goto done;
1162 if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
1163 bam_error(_("No BE's found\n"));
1164 ret = BAM_ERROR;
1165 goto done;
1168 if (bam_pool != NULL) {
1170 * find active be_node in bam_pool
1172 for (node = be_nodes; node != NULL; node = node->be_next_node) {
1173 if (strcmp(bam_pool, node->be_rpool) != 0)
1174 continue;
1175 if (node->be_active_on_boot)
1176 break;
1178 if (node == NULL) {
1179 bam_error(_("No active BE in %s\n"), bam_pool);
1180 be_free_list(be_nodes);
1181 ret = BAM_ERROR;
1182 goto done;
1184 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1185 node->be_node_name);
1186 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1187 node->be_root_ds);
1188 be_free_list(be_nodes);
1189 if (ret) {
1190 ret = BAM_ERROR;
1191 goto done;
1193 ret = be_installboot(nvl);
1194 if (ret)
1195 ret = BAM_ERROR;
1196 goto done;
1200 * get dataset for "/" and fill up the args.
1202 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1203 bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
1204 ret = BAM_ERROR;
1205 be_free_list(be_nodes);
1206 goto done;
1208 resetmnttab(fp);
1209 found = 0;
1210 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1211 if (strcmp(mnt.mnt_mountp, "/") == 0) {
1212 found = 1;
1213 root_ds = strdup(mnt.mnt_special);
1214 break;
1217 (void) fclose(fp);
1219 if (found == 0) {
1220 bam_error(NOT_IN_MNTTAB, "/");
1221 ret = BAM_ERROR;
1222 be_free_list(be_nodes);
1223 goto done;
1225 if (root_ds == NULL) {
1226 bam_error(_("out of memory\n"));
1227 ret = BAM_ERROR;
1228 be_free_list(be_nodes);
1229 goto done;
1232 for (node = be_nodes; node != NULL; node = node->be_next_node) {
1233 if (strcmp(root_ds, node->be_root_ds) == 0)
1234 break;
1237 if (node == NULL) {
1238 bam_error(_("No such BE: %s\n"), root_ds);
1239 free(root_ds);
1240 be_free_list(be_nodes);
1241 ret = BAM_ERROR;
1242 goto done;
1244 free(root_ds);
1246 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME, node->be_node_name);
1247 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT, node->be_root_ds);
1248 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, node->be_rpool);
1249 be_free_list(be_nodes);
1251 if (ret)
1252 ret = BAM_ERROR;
1253 else
1254 ret = be_installboot(nvl) ? BAM_ERROR : 0;
1255 done:
1256 nvlist_free(nvl);
1258 return (ret);
1261 static error_t
1262 bam_install(char *subcmd, char *opt)
1264 error_t (*f)(void);
1267 * Check arguments
1269 if (check_subcmd_and_options(subcmd, opt, inst_subcmds, &f) ==
1270 BAM_ERROR)
1271 return (BAM_ERROR);
1273 return (f());
1276 static error_t
1277 bam_menu(char *subcmd, char *opt, int largc, char *largv[])
1279 error_t ret;
1280 char menu_path[PATH_MAX];
1281 char clean_menu_root[PATH_MAX];
1282 char path[PATH_MAX];
1283 menu_t *menu;
1284 char menu_root[PATH_MAX];
1285 struct stat sb;
1286 error_t (*f)(menu_t *mp, char *menu_path, char *opt);
1287 char *special;
1288 char *pool = NULL;
1289 zfs_mnted_t zmnted;
1290 char *zmntpt;
1291 char *osdev;
1292 char *osroot;
1293 const char *fcn = "bam_menu()";
1296 * Menu sub-command only applies to GRUB (i.e. x86)
1298 if (!is_grub(bam_alt_root ? bam_root : "/")) {
1299 bam_error(NOT_GRUB_BOOT);
1300 return (BAM_ERROR);
1304 * Check arguments
1306 ret = check_subcmd_and_options(subcmd, opt, menu_subcmds, &f);
1307 if (ret == BAM_ERROR) {
1308 return (BAM_ERROR);
1311 assert(bam_root);
1313 (void) strlcpy(menu_root, bam_root, sizeof (menu_root));
1314 osdev = osroot = NULL;
1316 if (strcmp(subcmd, "update_entry") == 0) {
1317 assert(opt);
1319 osdev = strtok(opt, ",");
1320 assert(osdev);
1321 osroot = strtok(NULL, ",");
1322 if (osroot) {
1323 /* fixup bam_root so that it points at osroot */
1324 if (realpath(osroot, rootbuf) == NULL) {
1325 bam_error(CANT_RESOLVE, osroot,
1326 strerror(errno));
1327 return (BAM_ERROR);
1329 bam_alt_root = 1;
1330 bam_root = rootbuf;
1331 bam_rootlen = strlen(rootbuf);
1336 * We support menu on PCFS (under certain conditions), but
1337 * not the OS root
1339 if (is_pcfs(bam_root)) {
1340 bam_error(PCFS_ROOT_NOTSUP, bam_root);
1341 return (BAM_ERROR);
1344 if (stat(menu_root, &sb) == -1) {
1345 bam_error(CANNOT_LOCATE_GRUB_MENU);
1346 return (BAM_ERROR);
1349 BAM_DPRINTF((D_MENU_ROOT, fcn, menu_root));
1352 * We no longer use the GRUB slice file. If it exists, then
1353 * the user is doing something that is unsupported (such as
1354 * standard upgrading an old Live Upgrade BE). If that
1355 * happens, mimic existing behavior i.e. pretend that it is
1356 * not a BE. Emit a warning though.
1358 if (bam_alt_root) {
1359 (void) snprintf(path, sizeof (path), "%s%s", bam_root,
1360 GRUB_slice);
1361 } else {
1362 (void) snprintf(path, sizeof (path), "%s", GRUB_slice);
1365 if (bam_verbose && stat(path, &sb) == 0)
1366 bam_error(GRUB_SLICE_FILE_EXISTS, path);
1368 if (is_zfs(menu_root)) {
1369 assert(strcmp(menu_root, bam_root) == 0);
1370 special = get_special(menu_root);
1371 INJECT_ERROR1("Z_MENU_GET_SPECIAL", special = NULL);
1372 if (special == NULL) {
1373 bam_error(CANT_FIND_SPECIAL, menu_root);
1374 return (BAM_ERROR);
1376 pool = strtok(special, "/");
1377 INJECT_ERROR1("Z_MENU_GET_POOL", pool = NULL);
1378 if (pool == NULL) {
1379 free(special);
1380 bam_error(CANT_FIND_POOL, menu_root);
1381 return (BAM_ERROR);
1383 BAM_DPRINTF((D_Z_MENU_GET_POOL_FROM_SPECIAL, fcn, pool));
1385 zmntpt = mount_top_dataset(pool, &zmnted);
1386 INJECT_ERROR1("Z_MENU_MOUNT_TOP_DATASET", zmntpt = NULL);
1387 if (zmntpt == NULL) {
1388 bam_error(CANT_MOUNT_POOL_DATASET, pool);
1389 free(special);
1390 return (BAM_ERROR);
1392 BAM_DPRINTF((D_Z_GET_MENU_MOUNT_TOP_DATASET, fcn, zmntpt));
1394 (void) strlcpy(menu_root, zmntpt, sizeof (menu_root));
1395 BAM_DPRINTF((D_Z_GET_MENU_MENU_ROOT, fcn, menu_root));
1398 elide_trailing_slash(menu_root, clean_menu_root,
1399 sizeof (clean_menu_root));
1401 BAM_DPRINTF((D_CLEAN_MENU_ROOT, fcn, clean_menu_root));
1403 (void) strlcpy(menu_path, clean_menu_root, sizeof (menu_path));
1404 (void) strlcat(menu_path, GRUB_MENU, sizeof (menu_path));
1406 BAM_DPRINTF((D_MENU_PATH, fcn, menu_path));
1409 * If listing the menu, display the menu location
1411 if (strcmp(subcmd, "list_entry") == 0)
1412 bam_print(GRUB_MENU_PATH, menu_path);
1414 if ((menu = menu_read(menu_path)) == NULL) {
1415 bam_error(CANNOT_LOCATE_GRUB_MENU_FILE, menu_path);
1416 if (special != NULL)
1417 free(special);
1419 return (BAM_ERROR);
1423 * We already checked the following case in
1424 * check_subcmd_and_suboptions() above. Complete the
1425 * final step now.
1427 if (strcmp(subcmd, "set_option") == 0) {
1428 assert(largc == 1 && largv[0] && largv[1] == NULL);
1429 opt = largv[0];
1430 } else if ((strcmp(subcmd, "enable_hypervisor") != 0) &&
1431 (strcmp(subcmd, "list_setting") != 0)) {
1432 assert(largc == 0 && largv == NULL);
1435 ret = get_boot_cap(bam_root);
1436 if (ret != BAM_SUCCESS) {
1437 BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn));
1438 goto out;
1442 * Once the sub-cmd handler has run
1443 * only the line field is guaranteed to have valid values
1445 if (strcmp(subcmd, "update_entry") == 0) {
1446 ret = f(menu, menu_root, osdev);
1447 } else if (strcmp(subcmd, "upgrade") == 0) {
1448 ret = f(menu, bam_root, menu_root);
1449 } else if (strcmp(subcmd, "list_entry") == 0) {
1450 ret = f(menu, menu_path, opt);
1451 } else if (strcmp(subcmd, "list_setting") == 0) {
1452 ret = f(menu, ((largc > 0) ? largv[0] : ""),
1453 ((largc > 1) ? largv[1] : ""));
1454 } else if (strcmp(subcmd, "disable_hypervisor") == 0) {
1455 if (is_sparc()) {
1456 bam_error(NO_SPARC, subcmd);
1457 ret = BAM_ERROR;
1458 } else {
1459 ret = f(menu, bam_root, NULL);
1461 } else if (strcmp(subcmd, "enable_hypervisor") == 0) {
1462 if (is_sparc()) {
1463 bam_error(NO_SPARC, subcmd);
1464 ret = BAM_ERROR;
1465 } else {
1466 char *extra_args = NULL;
1469 * Compress all arguments passed in the largv[] array
1470 * into one string that can then be appended to the
1471 * end of the kernel$ string the routine to enable the
1472 * hypervisor will build.
1474 * This allows the caller to supply arbitrary unparsed
1475 * arguments, such as dom0 memory settings or APIC
1476 * options.
1478 * This concatenation will be done without ANY syntax
1479 * checking whatsoever, so it's the responsibility of
1480 * the caller to make sure the arguments are valid and
1481 * do not duplicate arguments the conversion routines
1482 * may create.
1484 if (largc > 0) {
1485 int extra_len, i;
1487 for (extra_len = 0, i = 0; i < largc; i++)
1488 extra_len += strlen(largv[i]);
1491 * Allocate space for argument strings,
1492 * intervening spaces and terminating NULL.
1494 extra_args = alloca(extra_len + largc);
1496 (void) strcpy(extra_args, largv[0]);
1498 for (i = 1; i < largc; i++) {
1499 (void) strcat(extra_args, " ");
1500 (void) strcat(extra_args, largv[i]);
1504 ret = f(menu, bam_root, extra_args);
1506 } else
1507 ret = f(menu, NULL, opt);
1509 if (ret == BAM_WRITE) {
1510 BAM_DPRINTF((D_WRITING_MENU_ROOT, fcn, clean_menu_root));
1511 ret = menu_write(clean_menu_root, menu);
1514 out:
1515 INJECT_ERROR1("POOL_SET", pool = "/pooldata");
1516 assert((is_zfs(menu_root)) ^ (pool == NULL));
1517 if (pool) {
1518 (void) umount_top_dataset(pool, zmnted, zmntpt);
1519 free(special);
1521 menu_free(menu);
1522 return (ret);
1526 static error_t
1527 bam_archive(
1528 char *subcmd,
1529 char *opt)
1531 error_t ret;
1532 error_t (*f)(char *root, char *opt);
1533 const char *fcn = "bam_archive()";
1536 * Add trailing / for archive subcommands
1538 if (rootbuf[strlen(rootbuf) - 1] != '/')
1539 (void) strcat(rootbuf, "/");
1540 bam_rootlen = strlen(rootbuf);
1543 * Check arguments
1545 ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
1546 if (ret != BAM_SUCCESS) {
1547 return (BAM_ERROR);
1550 ret = get_boot_cap(rootbuf);
1551 if (ret != BAM_SUCCESS) {
1552 BAM_DPRINTF((D_BOOT_GET_CAP_FAILED, fcn));
1553 return (ret);
1557 * Check archive not supported with update_all
1558 * since it is awkward to display out-of-sync
1559 * information for each BE.
1561 if (bam_check && strcmp(subcmd, "update_all") == 0) {
1562 bam_error(CHECK_NOT_SUPPORTED, subcmd);
1563 return (BAM_ERROR);
1566 if (strcmp(subcmd, "update_all") == 0)
1567 bam_update_all = 1;
1569 #if !defined(_OBP)
1570 ucode_install(bam_root);
1571 #endif
1573 ret = f(bam_root, opt);
1575 bam_update_all = 0;
1577 return (ret);
1580 /*PRINTFLIKE1*/
1581 void
1582 bam_error(char *format, ...)
1584 va_list ap;
1586 va_start(ap, format);
1587 (void) fprintf(stderr, "%s: ", prog);
1588 (void) vfprintf(stderr, format, ap);
1589 va_end(ap);
1592 /*PRINTFLIKE1*/
1593 void
1594 bam_derror(char *format, ...)
1596 va_list ap;
1598 assert(bam_debug);
1600 va_start(ap, format);
1601 (void) fprintf(stderr, "DEBUG: ");
1602 (void) vfprintf(stderr, format, ap);
1603 va_end(ap);
1606 /*PRINTFLIKE1*/
1607 void
1608 bam_print(char *format, ...)
1610 va_list ap;
1612 va_start(ap, format);
1613 (void) vfprintf(stdout, format, ap);
1614 va_end(ap);
1617 /*PRINTFLIKE1*/
1618 void
1619 bam_print_stderr(char *format, ...)
1621 va_list ap;
1623 va_start(ap, format);
1624 (void) vfprintf(stderr, format, ap);
1625 va_end(ap);
1628 void
1629 bam_exit(int excode)
1631 restore_env();
1632 bam_unlock();
1633 exit(excode);
1636 static void
1637 bam_lock(void)
1639 struct flock lock;
1640 pid_t pid;
1642 bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
1643 if (bam_lock_fd < 0) {
1645 * We may be invoked early in boot for archive verification.
1646 * In this case, root is readonly and /var/run may not exist.
1647 * Proceed without the lock
1649 if (errno == EROFS || errno == ENOENT) {
1650 bam_root_readonly = 1;
1651 return;
1654 bam_error(OPEN_FAIL, BAM_LOCK_FILE, strerror(errno));
1655 bam_exit(1);
1658 lock.l_type = F_WRLCK;
1659 lock.l_whence = SEEK_SET;
1660 lock.l_start = 0;
1661 lock.l_len = 0;
1663 if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
1664 if (errno != EACCES && errno != EAGAIN) {
1665 bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
1666 (void) close(bam_lock_fd);
1667 bam_lock_fd = -1;
1668 bam_exit(1);
1670 pid = 0;
1671 (void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
1672 bam_print(FILE_LOCKED, pid);
1674 lock.l_type = F_WRLCK;
1675 lock.l_whence = SEEK_SET;
1676 lock.l_start = 0;
1677 lock.l_len = 0;
1678 if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
1679 bam_error(LOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
1680 (void) close(bam_lock_fd);
1681 bam_lock_fd = -1;
1682 bam_exit(1);
1686 /* We own the lock now */
1687 pid = getpid();
1688 (void) write(bam_lock_fd, &pid, sizeof (pid));
1691 static void
1692 bam_unlock(void)
1694 struct flock unlock;
1697 * NOP if we don't hold the lock
1699 if (bam_lock_fd < 0) {
1700 return;
1703 unlock.l_type = F_UNLCK;
1704 unlock.l_whence = SEEK_SET;
1705 unlock.l_start = 0;
1706 unlock.l_len = 0;
1708 if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
1709 bam_error(UNLOCK_FAIL, BAM_LOCK_FILE, strerror(errno));
1712 if (close(bam_lock_fd) == -1) {
1713 bam_error(CLOSE_FAIL, BAM_LOCK_FILE, strerror(errno));
1715 bam_lock_fd = -1;
1718 static error_t
1719 list_archive(char *root, char *opt)
1721 filelist_t flist;
1722 filelist_t *flistp = &flist;
1723 line_t *lp;
1725 assert(root);
1726 assert(opt == NULL);
1728 flistp->head = flistp->tail = NULL;
1729 if (read_list(root, flistp) != BAM_SUCCESS) {
1730 return (BAM_ERROR);
1732 assert(flistp->head && flistp->tail);
1734 for (lp = flistp->head; lp; lp = lp->next) {
1735 bam_print(PRINT, lp->line);
1738 filelist_free(flistp);
1740 return (BAM_SUCCESS);
1744 * This routine writes a list of lines to a file.
1745 * The list is *not* freed
1747 static error_t
1748 list2file(char *root, char *tmp, char *final, line_t *start)
1750 char tmpfile[PATH_MAX];
1751 char path[PATH_MAX];
1752 FILE *fp;
1753 int ret;
1754 struct stat sb;
1755 mode_t mode;
1756 uid_t root_uid;
1757 gid_t sys_gid;
1758 struct passwd *pw;
1759 struct group *gp;
1760 const char *fcn = "list2file()";
1762 (void) snprintf(path, sizeof (path), "%s%s", root, final);
1764 if (start == NULL) {
1765 /* Empty GRUB menu */
1766 if (stat(path, &sb) != -1) {
1767 bam_print(UNLINK_EMPTY, path);
1768 if (unlink(path) != 0) {
1769 bam_error(UNLINK_FAIL, path, strerror(errno));
1770 return (BAM_ERROR);
1771 } else {
1772 return (BAM_SUCCESS);
1775 return (BAM_SUCCESS);
1779 * Preserve attributes of existing file if possible,
1780 * otherwise ask the system for uid/gid of root/sys.
1781 * If all fails, fall back on hard-coded defaults.
1783 if (stat(path, &sb) != -1) {
1784 mode = sb.st_mode;
1785 root_uid = sb.st_uid;
1786 sys_gid = sb.st_gid;
1787 } else {
1788 mode = DEFAULT_DEV_MODE;
1789 if ((pw = getpwnam(DEFAULT_DEV_USER)) != NULL) {
1790 root_uid = pw->pw_uid;
1791 } else {
1792 bam_error(CANT_FIND_USER,
1793 DEFAULT_DEV_USER, DEFAULT_DEV_UID);
1794 root_uid = (uid_t)DEFAULT_DEV_UID;
1796 if ((gp = getgrnam(DEFAULT_DEV_GROUP)) != NULL) {
1797 sys_gid = gp->gr_gid;
1798 } else {
1799 bam_error(CANT_FIND_GROUP,
1800 DEFAULT_DEV_GROUP, DEFAULT_DEV_GID);
1801 sys_gid = (gid_t)DEFAULT_DEV_GID;
1805 (void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", root, tmp);
1807 /* Truncate tmpfile first */
1808 fp = fopen(tmpfile, "w");
1809 if (fp == NULL) {
1810 bam_error(OPEN_FAIL, tmpfile, strerror(errno));
1811 return (BAM_ERROR);
1813 ret = fclose(fp);
1814 INJECT_ERROR1("LIST2FILE_TRUNC_FCLOSE", ret = EOF);
1815 if (ret == EOF) {
1816 bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
1817 return (BAM_ERROR);
1820 /* Now open it in append mode */
1821 fp = fopen(tmpfile, "a");
1822 if (fp == NULL) {
1823 bam_error(OPEN_FAIL, tmpfile, strerror(errno));
1824 return (BAM_ERROR);
1827 for (; start; start = start->next) {
1828 ret = s_fputs(start->line, fp);
1829 INJECT_ERROR1("LIST2FILE_FPUTS", ret = EOF);
1830 if (ret == EOF) {
1831 bam_error(WRITE_FAIL, tmpfile, strerror(errno));
1832 (void) fclose(fp);
1833 return (BAM_ERROR);
1837 ret = fclose(fp);
1838 INJECT_ERROR1("LIST2FILE_APPEND_FCLOSE", ret = EOF);
1839 if (ret == EOF) {
1840 bam_error(CLOSE_FAIL, tmpfile, strerror(errno));
1841 return (BAM_ERROR);
1845 * Set up desired attributes. Ignore failures on filesystems
1846 * not supporting these operations - pcfs reports unsupported
1847 * operations as EINVAL.
1849 ret = chmod(tmpfile, mode);
1850 if (ret == -1 &&
1851 errno != EINVAL && errno != ENOTSUP) {
1852 bam_error(CHMOD_FAIL, tmpfile, strerror(errno));
1853 return (BAM_ERROR);
1856 ret = chown(tmpfile, root_uid, sys_gid);
1857 if (ret == -1 &&
1858 errno != EINVAL && errno != ENOTSUP) {
1859 bam_error(CHOWN_FAIL, tmpfile, strerror(errno));
1860 return (BAM_ERROR);
1864 * Do an atomic rename
1866 ret = rename(tmpfile, path);
1867 INJECT_ERROR1("LIST2FILE_RENAME", ret = -1);
1868 if (ret != 0) {
1869 bam_error(RENAME_FAIL, path, strerror(errno));
1870 return (BAM_ERROR);
1873 BAM_DPRINTF((D_WROTE_FILE, fcn, path));
1874 return (BAM_SUCCESS);
1878 * Checks if the path specified (without the file name at the end) exists
1879 * and creates it if not. If the path exists and is not a directory, an attempt
1880 * to unlink is made.
1882 static int
1883 setup_path(char *path)
1885 char *p;
1886 int ret;
1887 struct stat sb;
1889 p = strrchr(path, '/');
1890 if (p != NULL) {
1891 *p = '\0';
1892 if (stat(path, &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
1893 /* best effort attempt, mkdirp will catch the error */
1894 (void) unlink(path);
1895 if (bam_verbose)
1896 bam_print(NEED_DIRPATH, path);
1897 ret = mkdirp(path, DIR_PERMS);
1898 if (ret == -1) {
1899 bam_error(MKDIR_FAILED, path, strerror(errno));
1900 *p = '/';
1901 return (BAM_ERROR);
1904 *p = '/';
1905 return (BAM_SUCCESS);
1907 return (BAM_SUCCESS);
1910 typedef union {
1911 gzFile gzfile;
1912 int fdfile;
1913 } outfile;
1915 typedef struct {
1916 char path[PATH_MAX];
1917 outfile out;
1918 } cachefile;
1920 static int
1921 setup_file(char *base, const char *path, cachefile *cf)
1923 int ret;
1924 char *strip;
1926 /* init gzfile or fdfile in case we fail before opening */
1927 if (bam_direct == BAM_DIRECT_DBOOT)
1928 cf->out.gzfile = NULL;
1929 else
1930 cf->out.fdfile = -1;
1932 /* strip the trailing altroot path */
1933 strip = (char *)path + strlen(rootbuf);
1935 ret = snprintf(cf->path, sizeof (cf->path), "%s/%s", base, strip);
1936 if (ret >= sizeof (cf->path)) {
1937 bam_error(PATH_TOO_LONG, rootbuf);
1938 return (BAM_ERROR);
1941 /* Check if path is present in the archive cache directory */
1942 if (setup_path(cf->path) == BAM_ERROR)
1943 return (BAM_ERROR);
1945 if (bam_direct == BAM_DIRECT_DBOOT) {
1946 if ((cf->out.gzfile = gzopen(cf->path, "wb")) == NULL) {
1947 bam_error(OPEN_FAIL, cf->path, strerror(errno));
1948 return (BAM_ERROR);
1950 (void) gzsetparams(cf->out.gzfile, Z_BEST_SPEED,
1951 Z_DEFAULT_STRATEGY);
1952 } else {
1953 if ((cf->out.fdfile = open(cf->path, O_WRONLY | O_CREAT, 0644))
1954 == -1) {
1955 bam_error(OPEN_FAIL, cf->path, strerror(errno));
1956 return (BAM_ERROR);
1960 return (BAM_SUCCESS);
1963 static int
1964 cache_write(cachefile cf, char *buf, int size)
1966 int err;
1968 if (bam_direct == BAM_DIRECT_DBOOT) {
1969 if (gzwrite(cf.out.gzfile, buf, size) < 1) {
1970 bam_error(GZ_WRITE_FAIL, gzerror(cf.out.gzfile, &err));
1971 if (err == Z_ERRNO && bam_verbose) {
1972 bam_error(WRITE_FAIL, cf.path, strerror(errno));
1974 return (BAM_ERROR);
1976 } else {
1977 if (write(cf.out.fdfile, buf, size) < 1) {
1978 bam_error(WRITE_FAIL, cf.path, strerror(errno));
1979 return (BAM_ERROR);
1982 return (BAM_SUCCESS);
1985 static int
1986 cache_close(cachefile cf)
1988 int ret;
1990 if (bam_direct == BAM_DIRECT_DBOOT) {
1991 if (cf.out.gzfile) {
1992 ret = gzclose(cf.out.gzfile);
1993 if (ret != Z_OK) {
1994 bam_error(CLOSE_FAIL, cf.path, strerror(errno));
1995 return (BAM_ERROR);
1998 } else {
1999 if (cf.out.fdfile != -1) {
2000 ret = close(cf.out.fdfile);
2001 if (ret != 0) {
2002 bam_error(CLOSE_FAIL, cf.path, strerror(errno));
2003 return (BAM_ERROR);
2008 return (BAM_SUCCESS);
2011 static int
2012 dircache_updatefile(const char *path, int what)
2014 int ret, exitcode;
2015 char buf[4096 * 4];
2016 FILE *infile;
2017 cachefile outfile, outupdt;
2019 if (bam_nowrite()) {
2020 set_dir_flag(what, NEED_UPDATE);
2021 return (BAM_SUCCESS);
2024 if (!has_cachedir(what))
2025 return (BAM_SUCCESS);
2027 if ((infile = fopen(path, "rb")) == NULL) {
2028 bam_error(OPEN_FAIL, path, strerror(errno));
2029 return (BAM_ERROR);
2032 ret = setup_file(get_cachedir(what), path, &outfile);
2033 if (ret == BAM_ERROR) {
2034 exitcode = BAM_ERROR;
2035 goto out;
2037 if (!is_dir_flag_on(what, NO_MULTI)) {
2038 ret = setup_file(get_updatedir(what), path, &outupdt);
2039 if (ret == BAM_ERROR)
2040 set_dir_flag(what, NO_MULTI);
2043 while ((ret = fread(buf, 1, sizeof (buf), infile)) > 0) {
2044 if (cache_write(outfile, buf, ret) == BAM_ERROR) {
2045 exitcode = BAM_ERROR;
2046 goto out;
2048 if (!is_dir_flag_on(what, NO_MULTI))
2049 if (cache_write(outupdt, buf, ret) == BAM_ERROR)
2050 set_dir_flag(what, NO_MULTI);
2053 set_dir_flag(what, NEED_UPDATE);
2054 get_count(what)++;
2055 if (get_count(what) > COUNT_MAX)
2056 set_dir_flag(what, NO_MULTI);
2057 exitcode = BAM_SUCCESS;
2058 out:
2059 (void) fclose(infile);
2060 if (cache_close(outfile) == BAM_ERROR)
2061 exitcode = BAM_ERROR;
2062 if (!is_dir_flag_on(what, NO_MULTI) &&
2063 cache_close(outupdt) == BAM_ERROR)
2064 exitcode = BAM_ERROR;
2065 if (exitcode == BAM_ERROR)
2066 set_flag(UPDATE_ERROR);
2067 return (exitcode);
2070 static int
2071 dircache_updatedir(const char *path, int what, int updt)
2073 int ret;
2074 char dpath[PATH_MAX];
2075 char *strip;
2076 struct stat sb;
2078 strip = (char *)path + strlen(rootbuf);
2080 ret = snprintf(dpath, sizeof (dpath), "%s/%s", updt ?
2081 get_updatedir(what) : get_cachedir(what), strip);
2083 if (ret >= sizeof (dpath)) {
2084 bam_error(PATH_TOO_LONG, rootbuf);
2085 set_flag(UPDATE_ERROR);
2086 return (BAM_ERROR);
2089 if (stat(dpath, &sb) == 0 && S_ISDIR(sb.st_mode))
2090 return (BAM_SUCCESS);
2092 if (updt) {
2093 if (!is_dir_flag_on(what, NO_MULTI))
2094 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1)
2095 set_dir_flag(what, NO_MULTI);
2096 } else {
2097 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) {
2098 set_flag(UPDATE_ERROR);
2099 return (BAM_ERROR);
2103 set_dir_flag(what, NEED_UPDATE);
2104 return (BAM_SUCCESS);
2107 #define DO_CACHE_DIR 0
2108 #define DO_UPDATE_DIR 1
2110 #if defined(_LP64) || defined(_LONGLONG_TYPE)
2111 typedef Elf64_Ehdr _elfhdr;
2112 #else
2113 typedef Elf32_Ehdr _elfhdr;
2114 #endif
2117 * This routine updates the contents of the cache directory
2119 static int
2120 update_dircache(const char *path, int flags)
2122 int rc = BAM_SUCCESS;
2124 switch (flags) {
2125 case FTW_F:
2127 int fd;
2128 _elfhdr elf;
2130 if ((fd = open(path, O_RDONLY)) < 0) {
2131 bam_error(OPEN_FAIL, path, strerror(errno));
2132 set_flag(UPDATE_ERROR);
2133 rc = BAM_ERROR;
2134 break;
2138 * libelf and gelf would be a cleaner and easier way to handle
2139 * this, but libelf fails compilation if _ILP32 is defined &&
2140 * _FILE_OFFSET_BITS is != 32 ...
2142 if (read(fd, (void *)&elf, sizeof (_elfhdr)) < 0) {
2143 bam_error(READ_FAIL, path, strerror(errno));
2144 set_flag(UPDATE_ERROR);
2145 (void) close(fd);
2146 rc = BAM_ERROR;
2147 break;
2149 (void) close(fd);
2152 * If the file is not an executable and is not inside an amd64
2153 * directory, we copy it in both the cache directories,
2154 * otherwise, we only copy it inside the 64-bit one.
2156 if (memcmp(elf.e_ident, ELFMAG, 4) != 0) {
2157 if (strstr(path, "/amd64")) {
2158 rc = dircache_updatefile(path, FILE64);
2159 } else {
2160 rc = dircache_updatefile(path, FILE32);
2161 if (rc == BAM_SUCCESS)
2162 rc = dircache_updatefile(path, FILE64);
2164 } else {
2166 * Based on the ELF class we copy the file in the 32-bit
2167 * or the 64-bit cache directory.
2169 if (elf.e_ident[EI_CLASS] == ELFCLASS32) {
2170 rc = dircache_updatefile(path, FILE32);
2171 } else if (elf.e_ident[EI_CLASS] == ELFCLASS64) {
2172 rc = dircache_updatefile(path, FILE64);
2173 } else {
2174 bam_print(NO3264ELF, path);
2175 /* paranoid */
2176 rc = dircache_updatefile(path, FILE32);
2177 if (rc == BAM_SUCCESS)
2178 rc = dircache_updatefile(path, FILE64);
2181 break;
2183 case FTW_D:
2184 if (strstr(path, "/amd64") == NULL) {
2185 rc = dircache_updatedir(path, FILE32, DO_UPDATE_DIR);
2186 if (rc == BAM_SUCCESS)
2187 rc = dircache_updatedir(path, FILE32,
2188 DO_CACHE_DIR);
2189 } else {
2190 if (has_cachedir(FILE64)) {
2191 rc = dircache_updatedir(path, FILE64,
2192 DO_UPDATE_DIR);
2193 if (rc == BAM_SUCCESS)
2194 rc = dircache_updatedir(path, FILE64,
2195 DO_CACHE_DIR);
2198 break;
2199 default:
2200 rc = BAM_ERROR;
2201 break;
2204 return (rc);
2207 /*ARGSUSED*/
2208 static int
2209 cmpstat(
2210 const char *file,
2211 const struct stat *st,
2212 int flags,
2213 struct FTW *ftw)
2215 uint_t sz;
2216 uint64_t *value;
2217 uint64_t filestat[2];
2218 int error, ret, status;
2220 struct safefile *safefilep;
2221 FILE *fp;
2222 struct stat sb;
2223 regex_t re;
2226 * On SPARC we create/update links too.
2228 if (flags != FTW_F && flags != FTW_D && (flags == FTW_SL &&
2229 !is_flag_on(IS_SPARC_TARGET)))
2230 return (0);
2233 * Ignore broken links
2235 if (flags == FTW_SL && stat(file, &sb) < 0)
2236 return (0);
2239 * new_nvlp may be NULL if there were errors earlier
2240 * but this is not fatal to update determination.
2242 if (walk_arg.new_nvlp) {
2243 filestat[0] = st->st_size;
2244 filestat[1] = st->st_mtime;
2245 error = nvlist_add_uint64_array(walk_arg.new_nvlp,
2246 file + bam_rootlen, filestat, 2);
2247 if (error)
2248 bam_error(NVADD_FAIL, file, strerror(error));
2252 * If we are invoked as part of system/filesystem/boot-archive, then
2253 * there are a number of things we should not worry about
2255 if (bam_smf_check) {
2256 /* ignore amd64 modules unless we are booted amd64. */
2257 if (!is_amd64() && strstr(file, "/amd64/") != 0)
2258 return (0);
2260 /* read in list of safe files */
2261 if (safefiles == NULL)
2262 if (fp = fopen("/boot/solaris/filelist.safe", "r")) {
2263 safefiles = s_calloc(1,
2264 sizeof (struct safefile));
2265 safefilep = safefiles;
2266 safefilep->name = s_calloc(1, MAXPATHLEN +
2267 MAXNAMELEN);
2268 safefilep->next = NULL;
2269 while (s_fgets(safefilep->name, MAXPATHLEN +
2270 MAXNAMELEN, fp) != NULL) {
2271 safefilep->next = s_calloc(1,
2272 sizeof (struct safefile));
2273 safefilep = safefilep->next;
2274 safefilep->name = s_calloc(1,
2275 MAXPATHLEN + MAXNAMELEN);
2276 safefilep->next = NULL;
2278 (void) fclose(fp);
2283 * On SPARC we create a -path-list file for mkisofs
2285 if (is_flag_on(IS_SPARC_TARGET) && !bam_nowrite()) {
2286 if (flags != FTW_D) {
2287 char *strip;
2289 strip = (char *)file + strlen(rootbuf);
2290 (void) fprintf(walk_arg.sparcfile, "/%s=%s\n", strip,
2291 file);
2296 * We are transitioning from the old model to the dircache or the cache
2297 * directory was removed: create the entry without further checkings.
2299 if (is_flag_on(NEED_CACHE_DIR)) {
2300 if (bam_verbose)
2301 bam_print(PARSEABLE_NEW_FILE, file);
2303 if (is_flag_on(IS_SPARC_TARGET)) {
2304 set_dir_flag(FILE64, NEED_UPDATE);
2305 return (0);
2308 ret = update_dircache(file, flags);
2309 if (ret == BAM_ERROR) {
2310 bam_error(UPDT_CACHE_FAIL, file);
2311 return (-1);
2314 return (0);
2318 * We need an update if file doesn't exist in old archive
2320 if (walk_arg.old_nvlp == NULL ||
2321 nvlist_lookup_uint64_array(walk_arg.old_nvlp,
2322 file + bam_rootlen, &value, &sz) != 0) {
2323 if (bam_smf_check) /* ignore new during smf check */
2324 return (0);
2326 if (is_flag_on(IS_SPARC_TARGET)) {
2327 set_dir_flag(FILE64, NEED_UPDATE);
2328 } else {
2329 ret = update_dircache(file, flags);
2330 if (ret == BAM_ERROR) {
2331 bam_error(UPDT_CACHE_FAIL, file);
2332 return (-1);
2336 if (bam_verbose)
2337 bam_print(PARSEABLE_NEW_FILE, file);
2338 return (0);
2342 * If we got there, the file is already listed as to be included in the
2343 * iso image. We just need to know if we are going to rebuild it or not
2345 if (is_flag_on(IS_SPARC_TARGET) &&
2346 is_dir_flag_on(FILE64, NEED_UPDATE) && !bam_nowrite())
2347 return (0);
2349 * File exists in old archive. Check if file has changed
2351 assert(sz == 2);
2352 bcopy(value, filestat, sizeof (filestat));
2354 if (flags != FTW_D && (filestat[0] != st->st_size ||
2355 filestat[1] != st->st_mtime)) {
2356 if (bam_smf_check) {
2357 safefilep = safefiles;
2358 while (safefilep != NULL &&
2359 safefilep->name[0] != '\0') {
2360 if (regcomp(&re, safefilep->name,
2361 REG_EXTENDED|REG_NOSUB) == 0) {
2362 status = regexec(&re,
2363 file + bam_rootlen, 0, NULL, 0);
2364 regfree(&re);
2365 if (status == 0) {
2366 (void) creat(
2367 NEED_UPDATE_SAFE_FILE,
2368 0644);
2369 return (0);
2372 safefilep = safefilep->next;
2376 if (is_flag_on(IS_SPARC_TARGET)) {
2377 set_dir_flag(FILE64, NEED_UPDATE);
2378 } else {
2379 ret = update_dircache(file, flags);
2380 if (ret == BAM_ERROR) {
2381 bam_error(UPDT_CACHE_FAIL, file);
2382 return (-1);
2386 if (bam_verbose)
2387 if (bam_smf_check)
2388 bam_print(" %s\n", file);
2389 else
2390 bam_print(PARSEABLE_OUT_DATE, file);
2393 return (0);
2397 * Remove a directory path recursively
2399 static int
2400 rmdir_r(char *path)
2402 struct dirent *d = NULL;
2403 DIR *dir = NULL;
2404 char tpath[PATH_MAX];
2405 struct stat sb;
2407 if ((dir = opendir(path)) == NULL)
2408 return (-1);
2410 while (d = readdir(dir)) {
2411 if ((strcmp(d->d_name, ".") != 0) &&
2412 (strcmp(d->d_name, "..") != 0)) {
2413 (void) snprintf(tpath, sizeof (tpath), "%s/%s",
2414 path, d->d_name);
2415 if (stat(tpath, &sb) == 0) {
2416 if (sb.st_mode & S_IFDIR)
2417 (void) rmdir_r(tpath);
2418 else
2419 (void) remove(tpath);
2423 return (remove(path));
2427 * Check if cache directory exists and, if not, create it and update flags
2428 * accordingly. If the path exists, but it's not a directory, a best effort
2429 * attempt to remove and recreate it is made.
2430 * If the user requested a 'purge', always recreate the directory from scratch.
2432 static int
2433 set_cache_dir(char *root, int what)
2435 struct stat sb;
2436 int ret = 0;
2438 ret = snprintf(get_cachedir(what), sizeof (get_cachedir(what)),
2439 "%s%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(), what == FILE64 ?
2440 "/amd64" : "", CACHEDIR_SUFFIX);
2442 if (ret >= sizeof (get_cachedir(what))) {
2443 bam_error(PATH_TOO_LONG, rootbuf);
2444 return (BAM_ERROR);
2447 if (bam_purge || is_flag_on(INVALIDATE_CACHE))
2448 (void) rmdir_r(get_cachedir(what));
2450 if (stat(get_cachedir(what), &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
2451 /* best effort unlink attempt, mkdir will catch errors */
2452 (void) unlink(get_cachedir(what));
2454 if (bam_verbose)
2455 bam_print(UPDATE_CDIR_MISS, get_cachedir(what));
2456 ret = mkdir(get_cachedir(what), DIR_PERMS);
2457 if (ret < 0) {
2458 bam_error(MKDIR_FAILED, get_cachedir(what),
2459 strerror(errno));
2460 get_cachedir(what)[0] = '\0';
2461 return (ret);
2463 set_flag(NEED_CACHE_DIR);
2464 set_dir_flag(what, NO_MULTI);
2467 return (BAM_SUCCESS);
2470 static int
2471 set_update_dir(char *root, int what)
2473 struct stat sb;
2474 int ret;
2476 if (is_dir_flag_on(what, NO_MULTI))
2477 return (BAM_SUCCESS);
2479 if (!bam_extend) {
2480 set_dir_flag(what, NO_MULTI);
2481 return (BAM_SUCCESS);
2484 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
2485 ret = snprintf(get_updatedir(what),
2486 sizeof (get_updatedir(what)), "%s%s%s/amd64%s", root,
2487 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
2488 else
2489 ret = snprintf(get_updatedir(what),
2490 sizeof (get_updatedir(what)), "%s%s%s%s", root,
2491 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
2493 if (ret >= sizeof (get_updatedir(what))) {
2494 bam_error(PATH_TOO_LONG, rootbuf);
2495 return (BAM_ERROR);
2498 if (stat(get_updatedir(what), &sb) == 0) {
2499 if (S_ISDIR(sb.st_mode))
2500 ret = rmdir_r(get_updatedir(what));
2501 else
2502 ret = unlink(get_updatedir(what));
2504 if (ret != 0)
2505 set_dir_flag(what, NO_MULTI);
2508 if (mkdir(get_updatedir(what), DIR_PERMS) < 0)
2509 set_dir_flag(what, NO_MULTI);
2511 return (BAM_SUCCESS);
2514 static int
2515 is_valid_archive(char *root, int what)
2517 char archive_path[PATH_MAX];
2518 char timestamp_path[PATH_MAX];
2519 struct stat sb, timestamp;
2520 int ret;
2522 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
2523 ret = snprintf(archive_path, sizeof (archive_path),
2524 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
2525 ARCHIVE_SUFFIX);
2526 else
2527 ret = snprintf(archive_path, sizeof (archive_path), "%s%s%s%s",
2528 root, ARCHIVE_PREFIX, get_machine(), ARCHIVE_SUFFIX);
2530 if (ret >= sizeof (archive_path)) {
2531 bam_error(PATH_TOO_LONG, rootbuf);
2532 return (BAM_ERROR);
2535 if (stat(archive_path, &sb) != 0) {
2536 if (bam_verbose && !bam_check)
2537 bam_print(UPDATE_ARCH_MISS, archive_path);
2538 set_dir_flag(what, NEED_UPDATE);
2539 set_dir_flag(what, NO_MULTI);
2540 return (BAM_SUCCESS);
2544 * The timestamp file is used to prevent stale files in the archive
2545 * cache.
2546 * Stale files can happen if the system is booted back and forth across
2547 * the transition from bootadm-before-the-cache to
2548 * bootadm-after-the-cache, since older versions of bootadm don't know
2549 * about the existence of the archive cache.
2551 * Since only bootadm-after-the-cache versions know about about this
2552 * file, we require that the boot archive be older than this file.
2554 ret = snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
2555 FILE_STAT_TIMESTAMP);
2557 if (ret >= sizeof (timestamp_path)) {
2558 bam_error(PATH_TOO_LONG, rootbuf);
2559 return (BAM_ERROR);
2562 if (stat(timestamp_path, &timestamp) != 0 ||
2563 sb.st_mtime > timestamp.st_mtime) {
2564 if (bam_verbose && !bam_check)
2565 bam_print(UPDATE_CACHE_OLD);
2567 * Don't generate a false positive for the boot-archive service
2568 * but trigger an update of the archive cache in
2569 * boot-archive-update.
2571 if (bam_smf_check) {
2572 (void) creat(NEED_UPDATE_FILE, 0644);
2573 return (BAM_SUCCESS);
2576 set_flag(INVALIDATE_CACHE);
2577 set_dir_flag(what, NEED_UPDATE);
2578 set_dir_flag(what, NO_MULTI);
2579 return (BAM_SUCCESS);
2582 if (is_flag_on(IS_SPARC_TARGET))
2583 return (BAM_SUCCESS);
2585 if (bam_extend && sb.st_size > BA_SIZE_MAX) {
2586 if (bam_verbose && !bam_check)
2587 bam_print(MULTI_SIZE, archive_path, BA_SIZE_MAX);
2588 set_dir_flag(what, NO_MULTI);
2591 return (BAM_SUCCESS);
2595 * Check flags and presence of required files and directories.
2596 * The force flag and/or absence of files should
2597 * trigger an update.
2598 * Suppress stdout output if check (-n) option is set
2599 * (as -n should only produce parseable output.)
2601 static int
2602 check_flags_and_files(char *root)
2605 struct stat sb;
2606 int ret;
2609 * If archive is missing, create archive
2611 if (is_flag_on(IS_SPARC_TARGET)) {
2612 ret = is_valid_archive(root, FILE64);
2613 if (ret == BAM_ERROR)
2614 return (BAM_ERROR);
2615 } else {
2616 int what = FILE32;
2617 do {
2618 ret = is_valid_archive(root, what);
2619 if (ret == BAM_ERROR)
2620 return (BAM_ERROR);
2621 what++;
2622 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2625 if (bam_nowrite())
2626 return (BAM_SUCCESS);
2630 * check if cache directories exist on x86.
2631 * check (and always open) the cache file on SPARC.
2633 if (is_sparc()) {
2634 ret = snprintf(get_cachedir(FILE64),
2635 sizeof (get_cachedir(FILE64)), "%s%s%s/%s", root,
2636 ARCHIVE_PREFIX, get_machine(), CACHEDIR_SUFFIX);
2638 if (ret >= sizeof (get_cachedir(FILE64))) {
2639 bam_error(PATH_TOO_LONG, rootbuf);
2640 return (BAM_ERROR);
2643 if (stat(get_cachedir(FILE64), &sb) != 0) {
2644 set_flag(NEED_CACHE_DIR);
2645 set_dir_flag(FILE64, NEED_UPDATE);
2648 walk_arg.sparcfile = fopen(get_cachedir(FILE64), "w");
2649 if (walk_arg.sparcfile == NULL) {
2650 bam_error(OPEN_FAIL, get_cachedir(FILE64),
2651 strerror(errno));
2652 return (BAM_ERROR);
2655 set_dir_present(FILE64);
2656 } else {
2657 int what = FILE32;
2659 do {
2660 if (set_cache_dir(root, what) != 0)
2661 return (BAM_ERROR);
2663 set_dir_present(what);
2665 if (set_update_dir(root, what) != 0)
2666 return (BAM_ERROR);
2667 what++;
2668 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2672 * if force, create archive unconditionally
2674 if (bam_force) {
2675 if (!is_sparc())
2676 set_dir_flag(FILE32, NEED_UPDATE);
2677 set_dir_flag(FILE64, NEED_UPDATE);
2678 if (bam_verbose)
2679 bam_print(UPDATE_FORCE);
2680 return (BAM_SUCCESS);
2683 return (BAM_SUCCESS);
2686 static error_t
2687 read_one_list(char *root, filelist_t *flistp, char *filelist)
2689 char path[PATH_MAX];
2690 FILE *fp;
2691 char buf[BAM_MAXLINE];
2692 const char *fcn = "read_one_list()";
2694 (void) snprintf(path, sizeof (path), "%s%s", root, filelist);
2696 fp = fopen(path, "r");
2697 if (fp == NULL) {
2698 BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno)));
2699 return (BAM_ERROR);
2701 while (s_fgets(buf, sizeof (buf), fp) != NULL) {
2702 /* skip blank lines */
2703 if (strspn(buf, " \t") == strlen(buf))
2704 continue;
2705 append_to_flist(flistp, buf);
2707 if (fclose(fp) != 0) {
2708 bam_error(CLOSE_FAIL, path, strerror(errno));
2709 return (BAM_ERROR);
2711 return (BAM_SUCCESS);
2714 static error_t
2715 read_list(char *root, filelist_t *flistp)
2717 char path[PATH_MAX];
2718 char cmd[PATH_MAX];
2719 struct stat sb;
2720 int n, rval;
2721 const char *fcn = "read_list()";
2723 flistp->head = flistp->tail = NULL;
2726 * build and check path to extract_boot_filelist.ksh
2728 n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
2729 if (n >= sizeof (path)) {
2730 bam_error(NO_FLIST);
2731 return (BAM_ERROR);
2734 if (is_safe_exec(path) == BAM_ERROR)
2735 return (BAM_ERROR);
2738 * If extract_boot_filelist is present, exec it, otherwise read
2739 * the filelists directly, for compatibility with older images.
2741 if (stat(path, &sb) == 0) {
2743 * build arguments to exec extract_boot_filelist.ksh
2745 char *rootarg, *platarg;
2746 int platarglen = 1, rootarglen = 1;
2747 if (strlen(root) > 1)
2748 rootarglen += strlen(root) + strlen("-R ");
2749 if (bam_alt_platform)
2750 platarglen += strlen(bam_platform) + strlen("-p ");
2751 platarg = s_calloc(1, platarglen);
2752 rootarg = s_calloc(1, rootarglen);
2753 *platarg = 0;
2754 *rootarg = 0;
2756 if (strlen(root) > 1) {
2757 (void) snprintf(rootarg, rootarglen,
2758 "-R %s", root);
2760 if (bam_alt_platform) {
2761 (void) snprintf(platarg, platarglen,
2762 "-p %s", bam_platform);
2764 n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s",
2765 path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST);
2766 free(platarg);
2767 free(rootarg);
2768 if (n >= sizeof (cmd)) {
2769 bam_error(NO_FLIST);
2770 return (BAM_ERROR);
2772 if (exec_cmd(cmd, flistp) != 0) {
2773 BAM_DPRINTF((D_FLIST_FAIL, fcn, path, strerror(errno)));
2774 return (BAM_ERROR);
2776 } else {
2778 * Read current lists of files - only the first is mandatory
2780 rval = read_one_list(root, flistp, BOOT_FILE_LIST);
2781 if (rval != BAM_SUCCESS)
2782 return (rval);
2783 (void) read_one_list(root, flistp, ETC_FILE_LIST);
2786 if (flistp->head == NULL) {
2787 bam_error(NO_FLIST);
2788 return (BAM_ERROR);
2791 return (BAM_SUCCESS);
2794 static void
2795 getoldstat(char *root)
2797 char path[PATH_MAX];
2798 int fd, error;
2799 struct stat sb;
2800 char *ostat;
2802 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
2803 fd = open(path, O_RDONLY);
2804 if (fd == -1) {
2805 if (bam_verbose)
2806 bam_print(OPEN_FAIL, path, strerror(errno));
2807 goto out_err;
2810 if (fstat(fd, &sb) != 0) {
2811 bam_error(STAT_FAIL, path, strerror(errno));
2812 goto out_err;
2815 ostat = s_calloc(1, sb.st_size);
2817 if (read(fd, ostat, sb.st_size) != sb.st_size) {
2818 bam_error(READ_FAIL, path, strerror(errno));
2819 free(ostat);
2820 goto out_err;
2823 (void) close(fd);
2824 fd = -1;
2826 walk_arg.old_nvlp = NULL;
2827 error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
2829 free(ostat);
2831 if (error) {
2832 bam_error(UNPACK_FAIL, path, strerror(error));
2833 walk_arg.old_nvlp = NULL;
2834 goto out_err;
2835 } else {
2836 return;
2839 out_err:
2840 if (fd != -1)
2841 (void) close(fd);
2842 if (!is_flag_on(IS_SPARC_TARGET))
2843 set_dir_flag(FILE32, NEED_UPDATE);
2844 set_dir_flag(FILE64, NEED_UPDATE);
2847 /* Best effort stale entry removal */
2848 static void
2849 delete_stale(char *file, int what)
2851 char path[PATH_MAX];
2852 struct stat sb;
2854 (void) snprintf(path, sizeof (path), "%s/%s", get_cachedir(what), file);
2855 if (!bam_check && stat(path, &sb) == 0) {
2856 if (sb.st_mode & S_IFDIR)
2857 (void) rmdir_r(path);
2858 else
2859 (void) unlink(path);
2861 set_dir_flag(what, (NEED_UPDATE | NO_MULTI));
2866 * Checks if a file in the current (old) archive has
2867 * been deleted from the root filesystem. This is needed for
2868 * software like Trusted Extensions (TX) that switch early
2869 * in boot based on presence/absence of a kernel module.
2871 static void
2872 check4stale(char *root)
2874 nvpair_t *nvp;
2875 nvlist_t *nvlp;
2876 char *file;
2877 char path[PATH_MAX];
2880 * Skip stale file check during smf check
2882 if (bam_smf_check)
2883 return;
2886 * If we need to (re)create the cache, there's no need to check for
2887 * stale files
2889 if (is_flag_on(NEED_CACHE_DIR))
2890 return;
2892 /* Nothing to do if no old stats */
2893 if ((nvlp = walk_arg.old_nvlp) == NULL)
2894 return;
2896 for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
2897 nvp = nvlist_next_nvpair(nvlp, nvp)) {
2898 file = nvpair_name(nvp);
2899 if (file == NULL)
2900 continue;
2901 (void) snprintf(path, sizeof (path), "%s/%s",
2902 root, file);
2903 if (access(path, F_OK) < 0) {
2904 int what;
2906 if (bam_verbose)
2907 bam_print(PARSEABLE_STALE_FILE, path);
2909 if (is_flag_on(IS_SPARC_TARGET)) {
2910 set_dir_flag(FILE64, NEED_UPDATE);
2911 } else {
2912 for (what = FILE32; what < CACHEDIR_NUM; what++)
2913 if (has_cachedir(what))
2914 delete_stale(file, what);
2920 static void
2921 create_newstat(void)
2923 int error;
2925 error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
2926 if (error) {
2928 * Not fatal - we can still create archive
2930 walk_arg.new_nvlp = NULL;
2931 bam_error(NVALLOC_FAIL, strerror(error));
2935 static int
2936 walk_list(char *root, filelist_t *flistp)
2938 char path[PATH_MAX];
2939 line_t *lp;
2941 for (lp = flistp->head; lp; lp = lp->next) {
2943 * Don't follow symlinks. A symlink must refer to
2944 * a file that would appear in the archive through
2945 * a direct reference. This matches the archive
2946 * construction behavior.
2948 (void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
2949 if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
2950 if (is_flag_on(UPDATE_ERROR))
2951 return (BAM_ERROR);
2953 * Some files may not exist.
2954 * For example: etc/rtc_config on a x86 diskless system
2955 * Emit verbose message only
2957 if (bam_verbose)
2958 bam_print(NFTW_FAIL, path, strerror(errno));
2962 return (BAM_SUCCESS);
2966 * Update the timestamp file.
2968 static void
2969 update_timestamp(char *root)
2971 char timestamp_path[PATH_MAX];
2973 /* this path length has already been checked in check_flags_and_files */
2974 (void) snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
2975 FILE_STAT_TIMESTAMP);
2978 * recreate the timestamp file. Since an outdated or absent timestamp
2979 * file translates in a complete rebuild of the archive cache, notify
2980 * the user of the performance issue.
2982 if (creat(timestamp_path, FILE_STAT_MODE) < 0) {
2983 bam_error(OPEN_FAIL, timestamp_path, strerror(errno));
2984 bam_error(TIMESTAMP_FAIL);
2989 static void
2990 savenew(char *root)
2992 char path[PATH_MAX];
2993 char path2[PATH_MAX];
2994 size_t sz;
2995 char *nstat;
2996 int fd, wrote, error;
2998 nstat = NULL;
2999 sz = 0;
3000 error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
3001 NV_ENCODE_XDR, 0);
3002 if (error) {
3003 bam_error(PACK_FAIL, strerror(error));
3004 return;
3007 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
3008 fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
3009 if (fd == -1) {
3010 bam_error(OPEN_FAIL, path, strerror(errno));
3011 free(nstat);
3012 return;
3014 wrote = write(fd, nstat, sz);
3015 if (wrote != sz) {
3016 bam_error(WRITE_FAIL, path, strerror(errno));
3017 (void) close(fd);
3018 free(nstat);
3019 return;
3021 (void) close(fd);
3022 free(nstat);
3024 (void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
3025 if (rename(path, path2) != 0) {
3026 bam_error(RENAME_FAIL, path2, strerror(errno));
3030 #define init_walk_args() bzero(&walk_arg, sizeof (walk_arg))
3032 static void
3033 clear_walk_args(void)
3035 nvlist_free(walk_arg.old_nvlp);
3036 nvlist_free(walk_arg.new_nvlp);
3037 if (walk_arg.sparcfile)
3038 (void) fclose(walk_arg.sparcfile);
3039 walk_arg.old_nvlp = NULL;
3040 walk_arg.new_nvlp = NULL;
3041 walk_arg.sparcfile = NULL;
3045 * Returns:
3046 * 0 - no update necessary
3047 * 1 - update required.
3048 * BAM_ERROR (-1) - An error occurred
3050 * Special handling for check (-n):
3051 * ================================
3052 * The check (-n) option produces parseable output.
3053 * To do this, we suppress all stdout messages unrelated
3054 * to out of sync files.
3055 * All stderr messages are still printed though.
3058 static int
3059 update_required(char *root)
3061 struct stat sb;
3062 char path[PATH_MAX];
3063 filelist_t flist;
3064 filelist_t *flistp = &flist;
3065 int ret;
3067 flistp->head = flistp->tail = NULL;
3069 if (is_sparc())
3070 set_flag(IS_SPARC_TARGET);
3073 * Check if cache directories and archives are present
3076 ret = check_flags_and_files(root);
3077 if (ret < 0)
3078 return (BAM_ERROR);
3081 * In certain deployment scenarios, filestat may not
3082 * exist. Do not stop the boot process, but trigger an update
3083 * of the archives (which will recreate filestat.ramdisk).
3085 if (bam_smf_check) {
3086 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
3087 if (stat(path, &sb) != 0) {
3088 (void) creat(NEED_UPDATE_FILE, 0644);
3089 return (0);
3093 getoldstat(root);
3096 * Check if the archive contains files that are no longer
3097 * present on the root filesystem.
3099 check4stale(root);
3102 * read list of files
3104 if (read_list(root, flistp) != BAM_SUCCESS) {
3105 clear_walk_args();
3106 return (BAM_ERROR);
3109 assert(flistp->head && flistp->tail);
3112 * At this point either the update is required
3113 * or the decision is pending. In either case
3114 * we need to create new stat nvlist
3116 create_newstat();
3118 * This walk does 2 things:
3119 * - gets new stat data for every file
3120 * - (optional) compare old and new stat data
3122 ret = walk_list(root, &flist);
3124 /* done with the file list */
3125 filelist_free(flistp);
3127 /* something went wrong */
3129 if (ret == BAM_ERROR) {
3130 bam_error(CACHE_FAIL);
3131 return (BAM_ERROR);
3134 if (walk_arg.new_nvlp == NULL) {
3135 if (walk_arg.sparcfile != NULL)
3136 (void) fclose(walk_arg.sparcfile);
3137 bam_error(NO_NEW_STAT);
3140 /* If nothing was updated, discard newstat. */
3142 if (!is_dir_flag_on(FILE32, NEED_UPDATE) &&
3143 !is_dir_flag_on(FILE64, NEED_UPDATE)) {
3144 clear_walk_args();
3145 return (0);
3148 if (walk_arg.sparcfile != NULL)
3149 (void) fclose(walk_arg.sparcfile);
3151 return (1);
3154 static int
3155 flushfs(char *root)
3157 char cmd[PATH_MAX + 30];
3159 (void) snprintf(cmd, sizeof (cmd), "%s -f \"%s\" 2>/dev/null",
3160 LOCKFS_PATH, root);
3162 return (exec_cmd(cmd, NULL));
3165 static int
3166 do_archive_copy(char *source, char *dest)
3169 sync();
3171 /* the equivalent of mv archive-new-$pid boot_archive */
3172 if (rename(source, dest) != 0) {
3173 (void) unlink(source);
3174 return (BAM_ERROR);
3177 if (flushfs(bam_root) != 0)
3178 sync();
3180 return (BAM_SUCCESS);
3183 static int
3184 check_cmdline(filelist_t flist)
3186 line_t *lp;
3188 for (lp = flist.head; lp; lp = lp->next) {
3189 if (strstr(lp->line, "Error:") != NULL ||
3190 strstr(lp->line, "Inode number overflow") != NULL) {
3191 (void) fprintf(stderr, "%s\n", lp->line);
3192 return (BAM_ERROR);
3196 return (BAM_SUCCESS);
3199 static void
3200 dump_errormsg(filelist_t flist)
3202 line_t *lp;
3204 for (lp = flist.head; lp; lp = lp->next)
3205 (void) fprintf(stderr, "%s\n", lp->line);
3208 static int
3209 check_archive(char *dest)
3211 struct stat sb;
3213 if (stat(dest, &sb) != 0 || !S_ISREG(sb.st_mode) ||
3214 sb.st_size < 10000) {
3215 bam_error(ARCHIVE_BAD, dest);
3216 (void) unlink(dest);
3217 return (BAM_ERROR);
3220 return (BAM_SUCCESS);
3223 static boolean_t
3224 is_be(char *root)
3226 zfs_handle_t *zhp;
3227 libzfs_handle_t *hdl;
3228 be_node_list_t *be_nodes = NULL;
3229 be_node_list_t *cur_be;
3230 boolean_t be_exist = B_FALSE;
3231 char ds_path[ZFS_MAXNAMELEN];
3233 if (!is_zfs(root))
3234 return (B_FALSE);
3236 * Get dataset for mountpoint
3238 if ((hdl = libzfs_init()) == NULL)
3239 return (B_FALSE);
3241 if ((zhp = zfs_path_to_zhandle(hdl, root,
3242 ZFS_TYPE_FILESYSTEM)) == NULL) {
3243 libzfs_fini(hdl);
3244 return (B_FALSE);
3247 (void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
3250 * Check if the current dataset is BE
3252 if (be_list(NULL, &be_nodes) == BE_SUCCESS) {
3253 for (cur_be = be_nodes; cur_be != NULL;
3254 cur_be = cur_be->be_next_node) {
3257 * Because we guarantee that cur_be->be_root_ds
3258 * is null-terminated by internal data structure,
3259 * we can safely use strcmp()
3261 if (strcmp(ds_path, cur_be->be_root_ds) == 0) {
3262 be_exist = B_TRUE;
3263 break;
3266 be_free_list(be_nodes);
3268 zfs_close(zhp);
3269 libzfs_fini(hdl);
3271 return (be_exist);
3275 * Returns 1 if mkiso is in the expected PATH, 0 otherwise
3277 static int
3278 is_mkisofs()
3280 if (access(MKISOFS_PATH, X_OK) == 0)
3281 return (1);
3282 return (0);
3285 #define MKISO_PARAMS " -quiet -graft-points -dlrDJN -relaxed-filenames "
3287 static int
3288 create_sparc_archive(char *archive, char *tempname, char *bootblk, char *list)
3290 int ret;
3291 char cmdline[3 * PATH_MAX + 64];
3292 filelist_t flist = {0};
3293 const char *func = "create_sparc_archive()";
3295 if (access(bootblk, R_OK) == 1) {
3296 bam_error(BOOTBLK_FAIL, bootblk);
3297 return (BAM_ERROR);
3301 * Prepare mkisofs command line and execute it
3303 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -G %s -o \"%s\" "
3304 "-path-list \"%s\" 2>&1", MKISOFS_PATH, MKISO_PARAMS, bootblk,
3305 tempname, list);
3307 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3309 ret = exec_cmd(cmdline, &flist);
3310 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3311 dump_errormsg(flist);
3312 goto out_err;
3315 filelist_free(&flist);
3318 * Prepare dd command line to copy the bootblk on the new archive and
3319 * execute it
3321 (void) snprintf(cmdline, sizeof (cmdline), "%s if=\"%s\" of=\"%s\""
3322 " bs=1b oseek=1 count=15 conv=notrunc conv=sync 2>&1", DD_PATH_USR,
3323 bootblk, tempname);
3325 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3327 ret = exec_cmd(cmdline, &flist);
3328 if (ret != 0 || check_cmdline(flist) == BAM_ERROR)
3329 goto out_err;
3331 filelist_free(&flist);
3333 /* Did we get a valid archive ? */
3334 if (check_archive(tempname) == BAM_ERROR)
3335 return (BAM_ERROR);
3337 return (do_archive_copy(tempname, archive));
3339 out_err:
3340 filelist_free(&flist);
3341 bam_error(ARCHIVE_FAIL, cmdline);
3342 (void) unlink(tempname);
3343 return (BAM_ERROR);
3346 static unsigned int
3347 from_733(unsigned char *s)
3349 int i;
3350 unsigned int ret = 0;
3352 for (i = 0; i < 4; i++)
3353 ret |= s[i] << (8 * i);
3355 return (ret);
3358 static void
3359 to_733(unsigned char *s, unsigned int val)
3361 int i;
3363 for (i = 0; i < 4; i++)
3364 s[i] = s[7-i] = (val >> (8 * i)) & 0xFF;
3368 * Extends the current boot archive without recreating it from scratch
3370 static int
3371 extend_iso_archive(char *archive, char *tempname, char *update_dir)
3373 int fd = -1, newfd = -1, ret, i;
3374 int next_session = 0, new_size = 0;
3375 char cmdline[3 * PATH_MAX + 64];
3376 const char *func = "extend_iso_archive()";
3377 filelist_t flist = {0};
3378 struct iso_pdesc saved_desc[MAX_IVDs];
3380 fd = open(archive, O_RDWR);
3381 if (fd == -1) {
3382 if (bam_verbose)
3383 bam_error(OPEN_FAIL, archive, strerror(errno));
3384 goto out_err;
3388 * A partial read is likely due to a corrupted file
3390 ret = pread64(fd, saved_desc, sizeof (saved_desc),
3391 VOLDESC_OFF * CD_BLOCK);
3392 if (ret != sizeof (saved_desc)) {
3393 if (bam_verbose)
3394 bam_error(READ_FAIL, archive, strerror(errno));
3395 goto out_err;
3398 if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3399 if (bam_verbose)
3400 bam_error(SIGN_FAIL, archive);
3401 goto out_err;
3405 * Read primary descriptor and locate next_session offset (it should
3406 * point to the end of the archive)
3408 next_session = P2ROUNDUP(from_733(saved_desc[0].volume_space_size), 16);
3410 (void) snprintf(cmdline, sizeof (cmdline), "%s -C 16,%d -M %s %s -o \""
3411 "%s\" \"%s\" 2>&1", MKISOFS_PATH, next_session, archive,
3412 MKISO_PARAMS, tempname, update_dir);
3414 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3416 ret = exec_cmd(cmdline, &flist);
3417 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3418 if (bam_verbose) {
3419 bam_error(MULTI_FAIL, cmdline);
3420 dump_errormsg(flist);
3422 goto out_flist_err;
3424 filelist_free(&flist);
3426 newfd = open(tempname, O_RDONLY);
3427 if (newfd == -1) {
3428 if (bam_verbose)
3429 bam_error(OPEN_FAIL, archive, strerror(errno));
3430 goto out_err;
3433 ret = pread64(newfd, saved_desc, sizeof (saved_desc),
3434 VOLDESC_OFF * CD_BLOCK);
3435 if (ret != sizeof (saved_desc)) {
3436 if (bam_verbose)
3437 bam_error(READ_FAIL, archive, strerror(errno));
3438 goto out_err;
3441 if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3442 if (bam_verbose)
3443 bam_error(SIGN_FAIL, archive);
3444 goto out_err;
3447 new_size = from_733(saved_desc[0].volume_space_size) + next_session;
3448 to_733(saved_desc[0].volume_space_size, new_size);
3450 for (i = 1; i < MAX_IVDs; i++) {
3451 if (saved_desc[i].type[0] == (unsigned char)255)
3452 break;
3453 if (memcmp(saved_desc[i].id, "CD001", 5))
3454 break;
3456 if (bam_verbose)
3457 bam_print("%s: Updating descriptor entry [%d]\n", func,
3460 to_733(saved_desc[i].volume_space_size, new_size);
3463 ret = pwrite64(fd, saved_desc, DVD_BLOCK, VOLDESC_OFF*CD_BLOCK);
3464 if (ret != DVD_BLOCK) {
3465 if (bam_verbose)
3466 bam_error(WRITE_FAIL, archive, strerror(errno));
3467 goto out_err;
3469 (void) close(newfd);
3470 newfd = -1;
3472 ret = fsync(fd);
3473 if (ret != 0)
3474 sync();
3476 ret = close(fd);
3477 if (ret != 0) {
3478 if (bam_verbose)
3479 bam_error(CLOSE_FAIL, archive, strerror(errno));
3480 return (BAM_ERROR);
3482 fd = -1;
3484 (void) snprintf(cmdline, sizeof (cmdline), "%s if=%s of=%s bs=32k "
3485 "seek=%d conv=sync 2>&1", DD_PATH_USR, tempname, archive,
3486 (next_session/16));
3488 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3490 ret = exec_cmd(cmdline, &flist);
3491 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3492 if (bam_verbose)
3493 bam_error(MULTI_FAIL, cmdline);
3494 goto out_flist_err;
3496 filelist_free(&flist);
3498 (void) unlink(tempname);
3500 if (flushfs(bam_root) != 0)
3501 sync();
3503 if (bam_verbose)
3504 bam_print("boot archive updated successfully\n");
3506 return (BAM_SUCCESS);
3508 out_flist_err:
3509 filelist_free(&flist);
3510 out_err:
3511 if (fd != -1)
3512 (void) close(fd);
3513 if (newfd != -1)
3514 (void) close(newfd);
3515 return (BAM_ERROR);
3518 static int
3519 create_x86_archive(char *archive, char *tempname, char *update_dir)
3521 int ret;
3522 char cmdline[3 * PATH_MAX + 64];
3523 filelist_t flist = {0};
3524 const char *func = "create_x86_archive()";
3526 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -o \"%s\" \"%s\" "
3527 "2>&1", MKISOFS_PATH, MKISO_PARAMS, tempname, update_dir);
3529 BAM_DPRINTF((D_CMDLINE, func, cmdline));
3531 ret = exec_cmd(cmdline, &flist);
3532 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3533 bam_error(ARCHIVE_FAIL, cmdline);
3534 dump_errormsg(flist);
3535 filelist_free(&flist);
3536 (void) unlink(tempname);
3537 return (BAM_ERROR);
3540 filelist_free(&flist);
3542 if (check_archive(tempname) == BAM_ERROR)
3543 return (BAM_ERROR);
3545 return (do_archive_copy(tempname, archive));
3548 static int
3549 mkisofs_archive(char *root, int what)
3551 int ret;
3552 char temp[PATH_MAX];
3553 char bootblk[PATH_MAX];
3554 char boot_archive[PATH_MAX];
3556 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
3557 ret = snprintf(temp, sizeof (temp),
3558 "%s%s%s/amd64/archive-new-%d", root, ARCHIVE_PREFIX,
3559 get_machine(), getpid());
3560 else
3561 ret = snprintf(temp, sizeof (temp), "%s%s%s/archive-new-%d",
3562 root, ARCHIVE_PREFIX, get_machine(), getpid());
3564 if (ret >= sizeof (temp))
3565 goto out_path_err;
3567 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
3568 ret = snprintf(boot_archive, sizeof (boot_archive),
3569 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
3570 ARCHIVE_SUFFIX);
3571 else
3572 ret = snprintf(boot_archive, sizeof (boot_archive),
3573 "%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(),
3574 ARCHIVE_SUFFIX);
3576 if (ret >= sizeof (boot_archive))
3577 goto out_path_err;
3579 bam_print("updating %s\n", boot_archive);
3581 if (is_flag_on(IS_SPARC_TARGET)) {
3582 ret = snprintf(bootblk, sizeof (bootblk),
3583 "%s/platform/%s/lib/fs/hsfs/bootblk", root, get_machine());
3584 if (ret >= sizeof (bootblk))
3585 goto out_path_err;
3587 ret = create_sparc_archive(boot_archive, temp, bootblk,
3588 get_cachedir(what));
3589 } else {
3590 if (!is_dir_flag_on(what, NO_MULTI)) {
3591 if (bam_verbose)
3592 bam_print("Attempting to extend x86 archive: "
3593 "%s\n", boot_archive);
3595 ret = extend_iso_archive(boot_archive, temp,
3596 get_updatedir(what));
3597 if (ret == BAM_SUCCESS) {
3598 if (bam_verbose)
3599 bam_print("Successfully extended %s\n",
3600 boot_archive);
3602 (void) rmdir_r(get_updatedir(what));
3603 return (BAM_SUCCESS);
3607 * The boot archive will be recreated from scratch. We get here
3608 * if at least one of these conditions is true:
3609 * - bootadm was called without the -e switch
3610 * - the archive (or the archive cache) doesn't exist
3611 * - archive size is bigger than BA_SIZE_MAX
3612 * - more than COUNT_MAX files need to be updated
3613 * - an error occourred either populating the /updates directory
3614 * or extend_iso_archive() failed
3616 if (bam_verbose)
3617 bam_print("Unable to extend %s... rebuilding archive\n",
3618 boot_archive);
3620 if (get_updatedir(what)[0] != '\0')
3621 (void) rmdir_r(get_updatedir(what));
3624 ret = create_x86_archive(boot_archive, temp,
3625 get_cachedir(what));
3628 if (ret == BAM_SUCCESS && bam_verbose)
3629 bam_print("Successfully created %s\n", boot_archive);
3631 return (ret);
3633 out_path_err:
3634 bam_error(PATH_TOO_LONG, root);
3635 return (BAM_ERROR);
3638 static error_t
3639 create_ramdisk(char *root)
3641 char *cmdline, path[PATH_MAX];
3642 size_t len;
3643 struct stat sb;
3644 int ret, what, status = BAM_SUCCESS;
3646 /* If there is mkisofs, use it to create the required archives */
3647 if (is_mkisofs()) {
3648 for (what = FILE32; what < CACHEDIR_NUM; what++) {
3649 if (has_cachedir(what) && is_dir_flag_on(what,
3650 NEED_UPDATE)) {
3651 ret = mkisofs_archive(root, what);
3652 if (ret != 0)
3653 status = BAM_ERROR;
3656 return (status);
3660 * Else setup command args for create_ramdisk.ksh for the UFS archives
3662 if (bam_verbose)
3663 bam_print("mkisofs not found, creating UFS archive\n");
3665 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3666 if (stat(path, &sb) != 0) {
3667 bam_error(ARCH_EXEC_MISS, path, strerror(errno));
3668 return (BAM_ERROR);
3671 if (is_safe_exec(path) == BAM_ERROR)
3672 return (BAM_ERROR);
3674 len = strlen(path) + strlen(root) + 10; /* room for space + -R */
3675 if (bam_alt_platform)
3676 len += strlen(bam_platform) + strlen("-p ");
3677 cmdline = s_calloc(1, len);
3679 if (bam_alt_platform) {
3680 assert(strlen(root) > 1);
3681 (void) snprintf(cmdline, len, "%s -p %s -R %s",
3682 path, bam_platform, root);
3683 /* chop off / at the end */
3684 cmdline[strlen(cmdline) - 1] = '\0';
3685 } else if (strlen(root) > 1) {
3686 (void) snprintf(cmdline, len, "%s -R %s", path, root);
3687 /* chop off / at the end */
3688 cmdline[strlen(cmdline) - 1] = '\0';
3689 } else
3690 (void) snprintf(cmdline, len, "%s", path);
3692 if (exec_cmd(cmdline, NULL) != 0) {
3693 bam_error(ARCHIVE_FAIL, cmdline);
3694 free(cmdline);
3695 return (BAM_ERROR);
3697 free(cmdline);
3699 * The existence of the expected archives used to be
3700 * verified here. This check is done in create_ramdisk as
3701 * it needs to be in sync with the altroot operated upon.
3703 return (BAM_SUCCESS);
3707 * Checks if target filesystem is on a ramdisk
3708 * 1 - is miniroot
3709 * 0 - is not
3710 * When in doubt assume it is not a ramdisk.
3712 static int
3713 is_ramdisk(char *root)
3715 struct extmnttab mnt;
3716 FILE *fp;
3717 int found;
3718 char mntpt[PATH_MAX];
3719 char *cp;
3722 * There are 3 situations where creating archive is
3723 * of dubious value:
3724 * - create boot_archive on a lofi-mounted boot_archive
3725 * - create it on a ramdisk which is the root filesystem
3726 * - create it on a ramdisk mounted somewhere else
3727 * The first is not easy to detect and checking for it is not
3728 * worth it.
3729 * The other two conditions are handled here
3731 fp = fopen(MNTTAB, "r");
3732 if (fp == NULL) {
3733 bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
3734 return (0);
3737 resetmnttab(fp);
3740 * Remove any trailing / from the mount point
3742 (void) strlcpy(mntpt, root, sizeof (mntpt));
3743 if (strcmp(root, "/") != 0) {
3744 cp = mntpt + strlen(mntpt) - 1;
3745 if (*cp == '/')
3746 *cp = '\0';
3748 found = 0;
3749 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
3750 if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
3751 found = 1;
3752 break;
3756 if (!found) {
3757 if (bam_verbose)
3758 bam_error(NOT_IN_MNTTAB, mntpt);
3759 (void) fclose(fp);
3760 return (0);
3763 if (strncmp(mnt.mnt_special, RAMDISK_SPECIAL,
3764 strlen(RAMDISK_SPECIAL)) == 0) {
3765 if (bam_verbose)
3766 bam_error(IS_RAMDISK, bam_root);
3767 (void) fclose(fp);
3768 return (1);
3771 (void) fclose(fp);
3773 return (0);
3776 static int
3777 is_boot_archive(char *root)
3779 char path[PATH_MAX];
3780 struct stat sb;
3781 int error;
3782 const char *fcn = "is_boot_archive()";
3785 * We can't create an archive without the create_ramdisk script
3787 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3788 error = stat(path, &sb);
3789 INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1);
3790 if (error == -1) {
3791 if (bam_verbose)
3792 bam_print(FILE_MISS, path);
3793 BAM_DPRINTF((D_NOT_ARCHIVE_BOOT, fcn, root));
3794 return (0);
3797 BAM_DPRINTF((D_IS_ARCHIVE_BOOT, fcn, root));
3798 return (1);
3802 * Need to call this for anything that operates on the GRUB menu
3803 * In the x86 live upgrade case the directory /boot/grub may be present
3804 * even on pre-newboot BEs. The authoritative way to check for a GRUB target
3805 * is to check for the presence of the stage2 binary which is present
3806 * only on GRUB targets (even on x86 boot partitions). Checking for the
3807 * presence of the multiboot binary is not correct as it is not present
3808 * on x86 boot partitions.
3811 is_grub(const char *root)
3813 char path[PATH_MAX];
3814 struct stat sb;
3815 const char *fcn = "is_grub()";
3817 (void) snprintf(path, sizeof (path), "%s%s", root, GRUB_STAGE2);
3818 if (stat(path, &sb) == -1) {
3819 BAM_DPRINTF((D_NO_GRUB_DIR, fcn, path));
3820 return (0);
3823 return (1);
3826 static int
3827 is_zfs(char *root)
3829 struct statvfs vfs;
3830 int ret;
3831 const char *fcn = "is_zfs()";
3833 ret = statvfs(root, &vfs);
3834 INJECT_ERROR1("STATVFS_ZFS", ret = 1);
3835 if (ret != 0) {
3836 bam_error(STATVFS_FAIL, root, strerror(errno));
3837 return (0);
3840 if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
3841 BAM_DPRINTF((D_IS_ZFS, fcn, root));
3842 return (1);
3843 } else {
3844 BAM_DPRINTF((D_IS_NOT_ZFS, fcn, root));
3845 return (0);
3849 static int
3850 is_ufs(char *root)
3852 struct statvfs vfs;
3853 int ret;
3854 const char *fcn = "is_ufs()";
3856 ret = statvfs(root, &vfs);
3857 INJECT_ERROR1("STATVFS_UFS", ret = 1);
3858 if (ret != 0) {
3859 bam_error(STATVFS_FAIL, root, strerror(errno));
3860 return (0);
3863 if (strncmp(vfs.f_basetype, "ufs", strlen("ufs")) == 0) {
3864 BAM_DPRINTF((D_IS_UFS, fcn, root));
3865 return (1);
3866 } else {
3867 BAM_DPRINTF((D_IS_NOT_UFS, fcn, root));
3868 return (0);
3872 static int
3873 is_pcfs(char *root)
3875 struct statvfs vfs;
3876 int ret;
3877 const char *fcn = "is_pcfs()";
3879 ret = statvfs(root, &vfs);
3880 INJECT_ERROR1("STATVFS_PCFS", ret = 1);
3881 if (ret != 0) {
3882 bam_error(STATVFS_FAIL, root, strerror(errno));
3883 return (0);
3886 if (strncmp(vfs.f_basetype, "pcfs", strlen("pcfs")) == 0) {
3887 BAM_DPRINTF((D_IS_PCFS, fcn, root));
3888 return (1);
3889 } else {
3890 BAM_DPRINTF((D_IS_NOT_PCFS, fcn, root));
3891 return (0);
3895 static int
3896 is_readonly(char *root)
3898 int fd;
3899 int error;
3900 char testfile[PATH_MAX];
3901 const char *fcn = "is_readonly()";
3904 * Using statvfs() to check for a read-only filesystem is not
3905 * reliable. The only way to reliably test is to attempt to
3906 * create a file
3908 (void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
3909 root, BOOTADM_RDONLY_TEST, getpid());
3911 (void) unlink(testfile);
3913 errno = 0;
3914 fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
3915 error = errno;
3916 INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES);
3917 if (fd == -1 && error == EROFS) {
3918 BAM_DPRINTF((D_RDONLY_FS, fcn, root));
3919 return (1);
3920 } else if (fd == -1) {
3921 bam_error(RDONLY_TEST_ERROR, root, strerror(error));
3924 (void) close(fd);
3925 (void) unlink(testfile);
3927 BAM_DPRINTF((D_RDWR_FS, fcn, root));
3928 return (0);
3931 static error_t
3932 update_archive(char *root, char *opt)
3934 error_t ret;
3936 assert(root);
3937 assert(opt == NULL);
3939 init_walk_args();
3940 (void) umask(022);
3943 * Never update non-BE root in update_all
3945 if (!is_be(root) && bam_update_all)
3946 return (BAM_SUCCESS);
3948 * root must belong to a boot archive based OS,
3950 if (!is_boot_archive(root)) {
3952 * Emit message only if not in context of update_all.
3953 * If in update_all, emit only if verbose flag is set.
3955 if (!bam_update_all || bam_verbose)
3956 bam_print(NOT_ARCHIVE_BOOT, root);
3957 return (BAM_ERROR);
3961 * If smf check is requested when / is writable (can happen
3962 * on first reboot following an upgrade because service
3963 * dependency is messed up), skip the check.
3965 if (bam_smf_check && !bam_root_readonly && !is_zfs(root))
3966 return (BAM_SUCCESS);
3969 * Don't generate archive on ramdisk.
3971 if (is_ramdisk(root))
3972 return (BAM_SUCCESS);
3975 * root must be writable. This check applies to alternate
3976 * root (-R option); bam_root_readonly applies to '/' only.
3977 * The behaviour translates into being the one of a 'check'.
3979 if (!bam_smf_check && !bam_check && is_readonly(root)) {
3980 set_flag(RDONLY_FSCHK);
3981 bam_check = 1;
3985 * Now check if an update is really needed.
3987 ret = update_required(root);
3990 * The check command (-n) is *not* a dry run.
3991 * It only checks if the archive is in sync.
3992 * A readonly filesystem has to be considered an error only if an update
3993 * is required.
3995 if (bam_nowrite()) {
3996 if (is_flag_on(RDONLY_FSCHK)) {
3997 bam_check = bam_saved_check;
3998 if (ret > 0)
3999 bam_error(RDONLY_FS, root);
4000 if (bam_update_all)
4001 return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
4004 bam_exit((ret != 0) ? 1 : 0);
4007 if (ret == 1) {
4008 /* create the ramdisk */
4009 ret = create_ramdisk(root);
4013 * if the archive is updated, save the new stat data and update the
4014 * timestamp file
4016 if (ret == 0 && walk_arg.new_nvlp != NULL) {
4017 savenew(root);
4018 update_timestamp(root);
4021 clear_walk_args();
4023 return (ret);
4026 static char *
4027 find_root_pool()
4029 char *special = get_special("/");
4030 char *p;
4032 if (special == NULL)
4033 return (NULL);
4035 if (*special == '/') {
4036 free(special);
4037 return (NULL);
4040 if ((p = strchr(special, '/')) != NULL)
4041 *p = '\0';
4043 return (special);
4046 static error_t
4047 synchronize_BE_menu(void)
4049 struct stat sb;
4050 char cmdline[PATH_MAX];
4051 char cksum_line[PATH_MAX];
4052 filelist_t flist = {0};
4053 char *old_cksum_str;
4054 char *old_size_str;
4055 char *old_file;
4056 char *curr_cksum_str;
4057 char *curr_size_str;
4058 char *curr_file;
4059 char *pool = NULL;
4060 char *mntpt = NULL;
4061 zfs_mnted_t mnted;
4062 FILE *cfp;
4063 int found;
4064 int ret;
4065 const char *fcn = "synchronize_BE_menu()";
4067 BAM_DPRINTF((D_FUNC_ENTRY0, fcn));
4069 /* Check if findroot enabled LU BE */
4070 if (stat(FINDROOT_INSTALLGRUB, &sb) != 0) {
4071 BAM_DPRINTF((D_NOT_LU_BE, fcn));
4072 return (BAM_SUCCESS);
4075 if (stat(LU_MENU_CKSUM, &sb) != 0) {
4076 BAM_DPRINTF((D_NO_CKSUM_FILE, fcn, LU_MENU_CKSUM));
4077 goto menu_sync;
4080 cfp = fopen(LU_MENU_CKSUM, "r");
4081 INJECT_ERROR1("CKSUM_FILE_MISSING", cfp = NULL);
4082 if (cfp == NULL) {
4083 bam_error(CANNOT_READ_LU_CKSUM, LU_MENU_CKSUM);
4084 goto menu_sync;
4086 BAM_DPRINTF((D_CKSUM_FILE_OPENED, fcn, LU_MENU_CKSUM));
4088 found = 0;
4089 while (s_fgets(cksum_line, sizeof (cksum_line), cfp) != NULL) {
4090 INJECT_ERROR1("MULTIPLE_CKSUM", found = 1);
4091 if (found) {
4092 bam_error(MULTIPLE_LU_CKSUM, LU_MENU_CKSUM);
4093 (void) fclose(cfp);
4094 goto menu_sync;
4096 found = 1;
4098 BAM_DPRINTF((D_CKSUM_FILE_READ, fcn, LU_MENU_CKSUM));
4101 old_cksum_str = strtok(cksum_line, " \t");
4102 old_size_str = strtok(NULL, " \t");
4103 old_file = strtok(NULL, " \t");
4105 INJECT_ERROR1("OLD_CKSUM_NULL", old_cksum_str = NULL);
4106 INJECT_ERROR1("OLD_SIZE_NULL", old_size_str = NULL);
4107 INJECT_ERROR1("OLD_FILE_NULL", old_file = NULL);
4108 if (old_cksum_str == NULL || old_size_str == NULL || old_file == NULL) {
4109 bam_error(CANNOT_PARSE_LU_CKSUM, LU_MENU_CKSUM);
4110 goto menu_sync;
4112 BAM_DPRINTF((D_CKSUM_FILE_PARSED, fcn, LU_MENU_CKSUM));
4114 /* Get checksum of current menu */
4115 pool = find_root_pool();
4116 if (pool) {
4117 mntpt = mount_top_dataset(pool, &mnted);
4118 if (mntpt == NULL) {
4119 bam_error(FAIL_MNT_TOP_DATASET, pool);
4120 free(pool);
4121 return (BAM_ERROR);
4123 (void) snprintf(cmdline, sizeof (cmdline), "%s %s%s",
4124 CKSUM, mntpt, GRUB_MENU);
4125 } else {
4126 (void) snprintf(cmdline, sizeof (cmdline), "%s %s",
4127 CKSUM, GRUB_MENU);
4129 ret = exec_cmd(cmdline, &flist);
4130 if (pool) {
4131 (void) umount_top_dataset(pool, mnted, mntpt);
4132 free(pool);
4134 INJECT_ERROR1("GET_CURR_CKSUM", ret = 1);
4135 if (ret != 0) {
4136 bam_error(MENU_CKSUM_FAIL);
4137 return (BAM_ERROR);
4139 BAM_DPRINTF((D_CKSUM_GEN_SUCCESS, fcn));
4141 INJECT_ERROR1("GET_CURR_CKSUM_OUTPUT", flist.head = NULL);
4142 if ((flist.head == NULL) || (flist.head != flist.tail)) {
4143 bam_error(BAD_CKSUM);
4144 filelist_free(&flist);
4145 return (BAM_ERROR);
4147 BAM_DPRINTF((D_CKSUM_GEN_OUTPUT_VALID, fcn));
4149 curr_cksum_str = strtok(flist.head->line, " \t");
4150 curr_size_str = strtok(NULL, " \t");
4151 curr_file = strtok(NULL, " \t");
4153 INJECT_ERROR1("CURR_CKSUM_NULL", curr_cksum_str = NULL);
4154 INJECT_ERROR1("CURR_SIZE_NULL", curr_size_str = NULL);
4155 INJECT_ERROR1("CURR_FILE_NULL", curr_file = NULL);
4156 if (curr_cksum_str == NULL || curr_size_str == NULL ||
4157 curr_file == NULL) {
4158 bam_error(BAD_CKSUM_PARSE);
4159 filelist_free(&flist);
4160 return (BAM_ERROR);
4162 BAM_DPRINTF((D_CKSUM_GEN_PARSED, fcn));
4164 if (strcmp(old_cksum_str, curr_cksum_str) == 0 &&
4165 strcmp(old_size_str, curr_size_str) == 0 &&
4166 strcmp(old_file, curr_file) == 0) {
4167 filelist_free(&flist);
4168 BAM_DPRINTF((D_CKSUM_NO_CHANGE, fcn));
4169 return (BAM_SUCCESS);
4172 filelist_free(&flist);
4174 /* cksum doesn't match - the menu has changed */
4175 BAM_DPRINTF((D_CKSUM_HAS_CHANGED, fcn));
4177 menu_sync:
4178 bam_print(PROP_GRUB_MENU);
4180 (void) snprintf(cmdline, sizeof (cmdline),
4181 "/bin/sh -c '. %s > /dev/null; %s %s yes > /dev/null'",
4182 LULIB, LULIB_PROPAGATE_FILE, GRUB_MENU);
4183 ret = exec_cmd(cmdline, NULL);
4184 INJECT_ERROR1("PROPAGATE_MENU", ret = 1);
4185 if (ret != 0) {
4186 bam_error(MENU_PROP_FAIL);
4187 return (BAM_ERROR);
4189 BAM_DPRINTF((D_PROPAGATED_MENU, fcn));
4191 (void) snprintf(cmdline, sizeof (cmdline), "/bin/cp %s %s > /dev/null",
4192 GRUB_MENU, GRUB_BACKUP_MENU);
4193 ret = exec_cmd(cmdline, NULL);
4194 INJECT_ERROR1("CREATE_BACKUP", ret = 1);
4195 if (ret != 0) {
4196 bam_error(MENU_BACKUP_FAIL, GRUB_BACKUP_MENU);
4197 return (BAM_ERROR);
4199 BAM_DPRINTF((D_CREATED_BACKUP, fcn, GRUB_BACKUP_MENU));
4201 (void) snprintf(cmdline, sizeof (cmdline),
4202 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4203 LULIB, LULIB_PROPAGATE_FILE, GRUB_BACKUP_MENU);
4204 ret = exec_cmd(cmdline, NULL);
4205 INJECT_ERROR1("PROPAGATE_BACKUP", ret = 1);
4206 if (ret != 0) {
4207 bam_error(BACKUP_PROP_FAIL, GRUB_BACKUP_MENU);
4208 return (BAM_ERROR);
4210 BAM_DPRINTF((D_PROPAGATED_BACKUP, fcn, GRUB_BACKUP_MENU));
4212 (void) snprintf(cmdline, sizeof (cmdline), "%s %s > %s",
4213 CKSUM, GRUB_MENU, LU_MENU_CKSUM);
4214 ret = exec_cmd(cmdline, NULL);
4215 INJECT_ERROR1("CREATE_CKSUM_FILE", ret = 1);
4216 if (ret != 0) {
4217 bam_error(MENU_CKSUM_WRITE_FAIL, LU_MENU_CKSUM);
4218 return (BAM_ERROR);
4220 BAM_DPRINTF((D_CREATED_CKSUM_FILE, fcn, LU_MENU_CKSUM));
4222 (void) snprintf(cmdline, sizeof (cmdline),
4223 "/bin/sh -c '. %s > /dev/null; %s %s no > /dev/null'",
4224 LULIB, LULIB_PROPAGATE_FILE, LU_MENU_CKSUM);
4225 ret = exec_cmd(cmdline, NULL);
4226 INJECT_ERROR1("PROPAGATE_MENU_CKSUM_FILE", ret = 1);
4227 if (ret != 0) {
4228 bam_error(MENU_CKSUM_PROP_FAIL, LU_MENU_CKSUM);
4229 return (BAM_ERROR);
4231 BAM_DPRINTF((D_PROPAGATED_CKSUM_FILE, fcn, LU_MENU_CKSUM));
4233 return (BAM_SUCCESS);
4236 static error_t
4237 update_all(char *root, char *opt)
4239 struct extmnttab mnt;
4240 struct stat sb;
4241 FILE *fp;
4242 char multibt[PATH_MAX];
4243 char creatram[PATH_MAX];
4244 error_t ret = BAM_SUCCESS;
4246 assert(root);
4247 assert(opt == NULL);
4249 if (bam_rootlen != 1 || *root != '/') {
4250 elide_trailing_slash(root, multibt, sizeof (multibt));
4251 bam_error(ALT_ROOT_INVALID, multibt);
4252 return (BAM_ERROR);
4256 * First update archive for current root
4258 if (update_archive(root, opt) != BAM_SUCCESS)
4259 ret = BAM_ERROR;
4261 if (ret == BAM_ERROR)
4262 goto out;
4265 * Now walk the mount table, performing archive update
4266 * for all mounted Newboot root filesystems
4268 fp = fopen(MNTTAB, "r");
4269 if (fp == NULL) {
4270 bam_error(OPEN_FAIL, MNTTAB, strerror(errno));
4271 ret = BAM_ERROR;
4272 goto out;
4275 resetmnttab(fp);
4277 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
4278 if (mnt.mnt_special == NULL)
4279 continue;
4280 if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) &&
4281 (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0))
4282 continue;
4283 if (strcmp(mnt.mnt_mountp, "/") == 0)
4284 continue;
4286 (void) snprintf(creatram, sizeof (creatram), "%s/%s",
4287 mnt.mnt_mountp, CREATE_RAMDISK);
4289 if (stat(creatram, &sb) == -1)
4290 continue;
4293 * We put a trailing slash to be consistent with root = "/"
4294 * case, such that we don't have to print // in some cases.
4296 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
4297 mnt.mnt_mountp);
4298 bam_rootlen = strlen(rootbuf);
4301 * It's possible that other mounts may be an alternate boot
4302 * architecture, so check it again.
4304 if ((get_boot_cap(rootbuf) != BAM_SUCCESS) ||
4305 (update_archive(rootbuf, opt) != BAM_SUCCESS))
4306 ret = BAM_ERROR;
4309 (void) fclose(fp);
4311 out:
4313 * We no longer use biosdev for Live Upgrade. Hence
4314 * there is no need to defer (to shutdown time) any fdisk
4315 * updates
4317 if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) {
4318 bam_error(FDISK_FILES_FOUND, GRUB_fdisk, GRUB_fdisk_target);
4322 * If user has updated menu in current BE, propagate the
4323 * updates to all BEs.
4325 if (sync_menu && synchronize_BE_menu() != BAM_SUCCESS)
4326 ret = BAM_ERROR;
4328 return (ret);
4331 static void
4332 append_line(menu_t *mp, line_t *lp)
4334 if (mp->start == NULL) {
4335 mp->start = lp;
4336 } else {
4337 mp->end->next = lp;
4338 lp->prev = mp->end;
4340 mp->end = lp;
4343 void
4344 unlink_line(menu_t *mp, line_t *lp)
4346 /* unlink from list */
4347 if (lp->prev)
4348 lp->prev->next = lp->next;
4349 else
4350 mp->start = lp->next;
4351 if (lp->next)
4352 lp->next->prev = lp->prev;
4353 else
4354 mp->end = lp->prev;
4357 static entry_t *
4358 boot_entry_new(menu_t *mp, line_t *start, line_t *end)
4360 entry_t *ent, *prev;
4361 const char *fcn = "boot_entry_new()";
4363 assert(mp);
4364 assert(start);
4365 assert(end);
4367 ent = s_calloc(1, sizeof (entry_t));
4368 BAM_DPRINTF((D_ENTRY_NEW, fcn));
4369 ent->start = start;
4370 ent->end = end;
4372 if (mp->entries == NULL) {
4373 mp->entries = ent;
4374 BAM_DPRINTF((D_ENTRY_NEW_FIRST, fcn));
4375 return (ent);
4378 prev = mp->entries;
4379 while (prev->next)
4380 prev = prev->next;
4381 prev->next = ent;
4382 ent->prev = prev;
4383 BAM_DPRINTF((D_ENTRY_NEW_LINKED, fcn));
4384 return (ent);
4387 static void
4388 boot_entry_addline(entry_t *ent, line_t *lp)
4390 if (ent)
4391 ent->end = lp;
4395 * Check whether cmd matches the one indexed by which, and whether arg matches
4396 * str. which must be either KERNEL_CMD or MODULE_CMD, and a match to the
4397 * respective *_DOLLAR_CMD is also acceptable. The arg is searched using
4398 * strstr(), so it can be a partial match.
4400 static int
4401 check_cmd(const char *cmd, const int which, const char *arg, const char *str)
4403 int ret;
4404 const char *fcn = "check_cmd()";
4406 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, arg, str));
4408 if (cmd != NULL) {
4409 if ((strcmp(cmd, menu_cmds[which]) != 0) &&
4410 (strcmp(cmd, menu_cmds[which + 1]) != 0)) {
4411 BAM_DPRINTF((D_CHECK_CMD_CMD_NOMATCH,
4412 fcn, cmd, menu_cmds[which]));
4413 return (0);
4415 ret = (strstr(arg, str) != NULL);
4416 } else
4417 ret = 0;
4419 if (ret) {
4420 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
4421 } else {
4422 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
4425 return (ret);
4428 static error_t
4429 kernel_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4431 const char *fcn = "kernel_parser()";
4433 assert(entry);
4434 assert(cmd);
4435 assert(arg);
4437 if (strcmp(cmd, menu_cmds[KERNEL_CMD]) != 0 &&
4438 strcmp(cmd, menu_cmds[KERNEL_DOLLAR_CMD]) != 0) {
4439 BAM_DPRINTF((D_NOT_KERNEL_CMD, fcn, cmd));
4440 return (BAM_ERROR);
4443 if (strncmp(arg, DIRECT_BOOT_32, sizeof (DIRECT_BOOT_32) - 1) == 0) {
4444 BAM_DPRINTF((D_SET_DBOOT_32, fcn, arg));
4445 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4446 } else if (strncmp(arg, DIRECT_BOOT_KERNEL,
4447 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0) {
4448 BAM_DPRINTF((D_SET_DBOOT, fcn, arg));
4449 entry->flags |= BAM_ENTRY_DBOOT;
4450 } else if (strncmp(arg, DIRECT_BOOT_64,
4451 sizeof (DIRECT_BOOT_64) - 1) == 0) {
4452 BAM_DPRINTF((D_SET_DBOOT_64, fcn, arg));
4453 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4454 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_KERNEL,
4455 sizeof (DIRECT_BOOT_FAILSAFE_KERNEL) - 1) == 0) {
4456 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE, fcn, arg));
4457 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE;
4458 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_32,
4459 sizeof (DIRECT_BOOT_FAILSAFE_32) - 1) == 0) {
4460 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_32, fcn, arg));
4461 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4462 | BAM_ENTRY_32BIT;
4463 } else if (strncmp(arg, DIRECT_BOOT_FAILSAFE_64,
4464 sizeof (DIRECT_BOOT_FAILSAFE_64) - 1) == 0) {
4465 BAM_DPRINTF((D_SET_DBOOT_FAILSAFE_64, fcn, arg));
4466 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_FAILSAFE
4467 | BAM_ENTRY_64BIT;
4468 } else if (strncmp(arg, MULTI_BOOT, sizeof (MULTI_BOOT) - 1) == 0) {
4469 BAM_DPRINTF((D_SET_MULTIBOOT, fcn, arg));
4470 entry->flags |= BAM_ENTRY_MULTIBOOT;
4471 } else if (strncmp(arg, MULTI_BOOT_FAILSAFE,
4472 sizeof (MULTI_BOOT_FAILSAFE) - 1) == 0) {
4473 BAM_DPRINTF((D_SET_MULTIBOOT_FAILSAFE, fcn, arg));
4474 entry->flags |= BAM_ENTRY_MULTIBOOT | BAM_ENTRY_FAILSAFE;
4475 } else if (strstr(arg, XEN_KERNEL_SUBSTR)) {
4476 BAM_DPRINTF((D_SET_HV, fcn, arg));
4477 entry->flags |= BAM_ENTRY_HV;
4478 } else if (!(entry->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU))) {
4479 BAM_DPRINTF((D_SET_HAND_KERNEL, fcn, arg));
4480 return (BAM_ERROR);
4481 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4482 strstr(arg, UNIX_SPACE)) {
4483 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_32BIT;
4484 } else if (strncmp(arg, KERNEL_PREFIX, strlen(KERNEL_PREFIX)) == 0 &&
4485 strstr(arg, AMD_UNIX_SPACE)) {
4486 entry->flags |= BAM_ENTRY_DBOOT | BAM_ENTRY_64BIT;
4487 } else {
4488 BAM_DPRINTF((D_IS_UNKNOWN_KERNEL, fcn, arg));
4489 bam_error(UNKNOWN_KERNEL_LINE, linenum);
4490 return (BAM_ERROR);
4493 return (BAM_SUCCESS);
4496 static error_t
4497 module_parser(entry_t *entry, char *cmd, char *arg, int linenum)
4499 const char *fcn = "module_parser()";
4501 assert(entry);
4502 assert(cmd);
4503 assert(arg);
4505 if (strcmp(cmd, menu_cmds[MODULE_CMD]) != 0 &&
4506 strcmp(cmd, menu_cmds[MODULE_DOLLAR_CMD]) != 0) {
4507 BAM_DPRINTF((D_NOT_MODULE_CMD, fcn, cmd));
4508 return (BAM_ERROR);
4511 if (strcmp(arg, DIRECT_BOOT_ARCHIVE) == 0 ||
4512 strcmp(arg, DIRECT_BOOT_ARCHIVE_32) == 0 ||
4513 strcmp(arg, DIRECT_BOOT_ARCHIVE_64) == 0 ||
4514 strcmp(arg, MULTIBOOT_ARCHIVE) == 0 ||
4515 strcmp(arg, FAILSAFE_ARCHIVE) == 0 ||
4516 strcmp(arg, FAILSAFE_ARCHIVE_32) == 0 ||
4517 strcmp(arg, FAILSAFE_ARCHIVE_64) == 0 ||
4518 strcmp(arg, XEN_KERNEL_MODULE_LINE) == 0 ||
4519 strcmp(arg, XEN_KERNEL_MODULE_LINE_ZFS) == 0) {
4520 BAM_DPRINTF((D_BOOTADM_LU_MODULE, fcn, arg));
4521 return (BAM_SUCCESS);
4522 } else if (!(entry->flags & BAM_ENTRY_BOOTADM) &&
4523 !(entry->flags & BAM_ENTRY_LU)) {
4524 /* don't emit warning for hand entries */
4525 BAM_DPRINTF((D_IS_HAND_MODULE, fcn, arg));
4526 return (BAM_ERROR);
4527 } else {
4528 BAM_DPRINTF((D_IS_UNKNOWN_MODULE, fcn, arg));
4529 bam_error(UNKNOWN_MODULE_LINE, linenum);
4530 return (BAM_ERROR);
4535 * A line in menu.lst looks like
4536 * [ ]*<cmd>[ \t=]*<arg>*
4538 static void
4539 line_parser(menu_t *mp, char *str, int *lineNum, int *entryNum)
4542 * save state across calls. This is so that
4543 * header gets the right entry# after title has
4544 * been processed
4546 static line_t *prev = NULL;
4547 static entry_t *curr_ent = NULL;
4548 static int in_liveupgrade = 0;
4549 static int is_libbe_ent = 0;
4551 line_t *lp;
4552 char *cmd, *sep, *arg;
4553 char save, *cp, *line;
4554 menu_flag_t flag = BAM_INVALID;
4555 const char *fcn = "line_parser()";
4557 if (str == NULL) {
4558 return;
4562 * First save a copy of the entire line.
4563 * We use this later to set the line field.
4565 line = s_strdup(str);
4567 /* Eat up leading whitespace */
4568 while (*str == ' ' || *str == '\t')
4569 str++;
4571 if (*str == '#') { /* comment */
4572 cmd = s_strdup("#");
4573 sep = NULL;
4574 arg = s_strdup(str + 1);
4575 flag = BAM_COMMENT;
4576 if (strstr(arg, BAM_LU_HDR) != NULL) {
4577 in_liveupgrade = 1;
4578 } else if (strstr(arg, BAM_LU_FTR) != NULL) {
4579 in_liveupgrade = 0;
4580 } else if (strstr(arg, BAM_LIBBE_FTR) != NULL) {
4581 is_libbe_ent = 1;
4583 } else if (*str == '\0') { /* blank line */
4584 cmd = sep = arg = NULL;
4585 flag = BAM_EMPTY;
4586 } else {
4588 * '=' is not a documented separator in grub syntax.
4589 * However various development bits use '=' as a
4590 * separator. In addition, external users also
4591 * use = as a separator. So we will allow that usage.
4593 cp = str;
4594 while (*str != ' ' && *str != '\t' && *str != '=') {
4595 if (*str == '\0') {
4596 cmd = s_strdup(cp);
4597 sep = arg = NULL;
4598 break;
4600 str++;
4603 if (*str != '\0') {
4604 save = *str;
4605 *str = '\0';
4606 cmd = s_strdup(cp);
4607 *str = save;
4609 str++;
4610 save = *str;
4611 *str = '\0';
4612 sep = s_strdup(str - 1);
4613 *str = save;
4615 while (*str == ' ' || *str == '\t')
4616 str++;
4617 if (*str == '\0')
4618 arg = NULL;
4619 else
4620 arg = s_strdup(str);
4624 lp = s_calloc(1, sizeof (line_t));
4626 lp->cmd = cmd;
4627 lp->sep = sep;
4628 lp->arg = arg;
4629 lp->line = line;
4630 lp->lineNum = ++(*lineNum);
4631 if (cmd && strcmp(cmd, menu_cmds[TITLE_CMD]) == 0) {
4632 lp->entryNum = ++(*entryNum);
4633 lp->flags = BAM_TITLE;
4634 if (prev && prev->flags == BAM_COMMENT &&
4635 prev->arg && strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4636 prev->entryNum = lp->entryNum;
4637 curr_ent = boot_entry_new(mp, prev, lp);
4638 curr_ent->flags |= BAM_ENTRY_BOOTADM;
4639 BAM_DPRINTF((D_IS_BOOTADM_ENTRY, fcn, arg));
4640 } else {
4641 curr_ent = boot_entry_new(mp, lp, lp);
4642 if (in_liveupgrade) {
4643 curr_ent->flags |= BAM_ENTRY_LU;
4644 BAM_DPRINTF((D_IS_LU_ENTRY, fcn, arg));
4647 curr_ent->entryNum = *entryNum;
4648 } else if (flag != BAM_INVALID) {
4650 * For header comments, the entry# is "fixed up"
4651 * by the subsequent title
4653 lp->entryNum = *entryNum;
4654 lp->flags = flag;
4655 } else {
4656 lp->entryNum = *entryNum;
4658 if (*entryNum == ENTRY_INIT) {
4659 lp->flags = BAM_GLOBAL;
4660 } else {
4661 lp->flags = BAM_ENTRY;
4663 if (cmd && arg) {
4664 if (strcmp(cmd, menu_cmds[ROOT_CMD]) == 0) {
4665 BAM_DPRINTF((D_IS_ROOT_CMD, fcn, arg));
4666 curr_ent->flags |= BAM_ENTRY_ROOT;
4667 } else if (strcmp(cmd, menu_cmds[FINDROOT_CMD])
4668 == 0) {
4669 BAM_DPRINTF((D_IS_FINDROOT_CMD, fcn,
4670 arg));
4671 curr_ent->flags |= BAM_ENTRY_FINDROOT;
4672 } else if (strcmp(cmd,
4673 menu_cmds[CHAINLOADER_CMD]) == 0) {
4674 BAM_DPRINTF((D_IS_CHAINLOADER_CMD, fcn,
4675 arg));
4676 curr_ent->flags |=
4677 BAM_ENTRY_CHAINLOADER;
4678 } else if (kernel_parser(curr_ent, cmd, arg,
4679 lp->lineNum) != BAM_SUCCESS) {
4680 (void) module_parser(curr_ent, cmd,
4681 arg, lp->lineNum);
4687 /* record default, old default, and entry line ranges */
4688 if (lp->flags == BAM_GLOBAL && lp->cmd != NULL &&
4689 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0) {
4690 mp->curdefault = lp;
4691 } else if (lp->flags == BAM_COMMENT &&
4692 strncmp(lp->arg, BAM_OLDDEF, strlen(BAM_OLDDEF)) == 0) {
4693 mp->olddefault = lp;
4694 } else if (lp->flags == BAM_COMMENT &&
4695 strncmp(lp->arg, BAM_OLD_RC_DEF, strlen(BAM_OLD_RC_DEF)) == 0) {
4696 mp->old_rc_default = lp;
4697 } else if (lp->flags == BAM_ENTRY ||
4698 (lp->flags == BAM_COMMENT &&
4699 ((strcmp(lp->arg, BAM_BOOTADM_FTR) == 0) || is_libbe_ent))) {
4700 if (is_libbe_ent) {
4701 curr_ent->flags |= BAM_ENTRY_LIBBE;
4702 is_libbe_ent = 0;
4705 boot_entry_addline(curr_ent, lp);
4707 append_line(mp, lp);
4709 prev = lp;
4712 void
4713 update_numbering(menu_t *mp)
4715 int lineNum;
4716 int entryNum;
4717 int old_default_value;
4718 line_t *lp, *prev, *default_lp, *default_entry;
4719 char buf[PATH_MAX];
4721 if (mp->start == NULL) {
4722 return;
4725 lineNum = LINE_INIT;
4726 entryNum = ENTRY_INIT;
4727 old_default_value = ENTRY_INIT;
4728 lp = default_lp = default_entry = NULL;
4730 prev = NULL;
4731 for (lp = mp->start; lp; prev = lp, lp = lp->next) {
4732 lp->lineNum = ++lineNum;
4735 * Get the value of the default command
4737 if (lp->entryNum == ENTRY_INIT && lp->cmd != NULL &&
4738 strcmp(lp->cmd, menu_cmds[DEFAULT_CMD]) == 0 &&
4739 lp->arg) {
4740 old_default_value = atoi(lp->arg);
4741 default_lp = lp;
4745 * If not a booting entry, nothing else to fix for this
4746 * entry
4748 if (lp->entryNum == ENTRY_INIT)
4749 continue;
4752 * Record the position of the default entry.
4753 * The following works because global
4754 * commands like default and timeout should precede
4755 * actual boot entries, so old_default_value
4756 * is already known (or default cmd is missing).
4758 if (default_entry == NULL &&
4759 old_default_value != ENTRY_INIT &&
4760 lp->entryNum == old_default_value) {
4761 default_entry = lp;
4765 * Now fixup the entry number
4767 if (lp->cmd != NULL &&
4768 strcmp(lp->cmd, menu_cmds[TITLE_CMD]) == 0) {
4769 lp->entryNum = ++entryNum;
4770 /* fixup the bootadm header */
4771 if (prev && prev->flags == BAM_COMMENT &&
4772 prev->arg &&
4773 strcmp(prev->arg, BAM_BOOTADM_HDR) == 0) {
4774 prev->entryNum = lp->entryNum;
4776 } else {
4777 lp->entryNum = entryNum;
4782 * No default command in menu, simply return
4784 if (default_lp == NULL) {
4785 return;
4788 free(default_lp->arg);
4789 free(default_lp->line);
4791 if (default_entry == NULL) {
4792 default_lp->arg = s_strdup("0");
4793 } else {
4794 (void) snprintf(buf, sizeof (buf), "%d",
4795 default_entry->entryNum);
4796 default_lp->arg = s_strdup(buf);
4800 * The following is required since only the line field gets
4801 * written back to menu.lst
4803 (void) snprintf(buf, sizeof (buf), "%s%s%s",
4804 menu_cmds[DEFAULT_CMD], menu_cmds[SEP_CMD], default_lp->arg);
4805 default_lp->line = s_strdup(buf);
4809 static menu_t *
4810 menu_read(char *menu_path)
4812 FILE *fp;
4813 char buf[BAM_MAXLINE], *cp;
4814 menu_t *mp;
4815 int line, entry, len, n;
4817 mp = s_calloc(1, sizeof (menu_t));
4819 fp = fopen(menu_path, "r");
4820 if (fp == NULL) { /* Let the caller handle this error */
4821 free(mp);
4822 return (NULL);
4825 /* Note: GRUB boot entry number starts with 0 */
4826 line = LINE_INIT;
4827 entry = ENTRY_INIT;
4828 cp = buf;
4829 len = sizeof (buf);
4830 while (s_fgets(cp, len, fp) != NULL) {
4831 n = strlen(cp);
4832 if (cp[n - 1] == '\\') {
4833 len -= n - 1;
4834 assert(len >= 2);
4835 cp += n - 1;
4836 continue;
4838 line_parser(mp, buf, &line, &entry);
4839 cp = buf;
4840 len = sizeof (buf);
4843 if (fclose(fp) == EOF) {
4844 bam_error(CLOSE_FAIL, menu_path, strerror(errno));
4847 return (mp);
4850 static error_t
4851 selector(menu_t *mp, char *opt, int *entry, char **title)
4853 char *eq;
4854 char *opt_dup;
4855 int entryNum;
4857 assert(mp);
4858 assert(mp->start);
4859 assert(opt);
4861 opt_dup = s_strdup(opt);
4863 if (entry)
4864 *entry = ENTRY_INIT;
4865 if (title)
4866 *title = NULL;
4868 eq = strchr(opt_dup, '=');
4869 if (eq == NULL) {
4870 bam_error(INVALID_OPT, opt);
4871 free(opt_dup);
4872 return (BAM_ERROR);
4875 *eq = '\0';
4876 if (entry && strcmp(opt_dup, OPT_ENTRY_NUM) == 0) {
4877 assert(mp->end);
4878 entryNum = s_strtol(eq + 1);
4879 if (entryNum < 0 || entryNum > mp->end->entryNum) {
4880 bam_error(INVALID_ENTRY, eq + 1);
4881 free(opt_dup);
4882 return (BAM_ERROR);
4884 *entry = entryNum;
4885 } else if (title && strcmp(opt_dup, menu_cmds[TITLE_CMD]) == 0) {
4886 *title = opt + (eq - opt_dup) + 1;
4887 } else {
4888 bam_error(INVALID_OPT, opt);
4889 free(opt_dup);
4890 return (BAM_ERROR);
4893 free(opt_dup);
4894 return (BAM_SUCCESS);
4898 * If invoked with no titles/entries (opt == NULL)
4899 * only title lines in file are printed.
4901 * If invoked with a title or entry #, all
4902 * lines in *every* matching entry are listed
4904 static error_t
4905 list_entry(menu_t *mp, char *menu_path, char *opt)
4907 line_t *lp;
4908 int entry = ENTRY_INIT;
4909 int found;
4910 char *title = NULL;
4912 assert(mp);
4913 assert(menu_path);
4915 /* opt is optional */
4916 BAM_DPRINTF((D_FUNC_ENTRY2, "list_entry", menu_path,
4917 opt ? opt : "<NULL>"));
4919 if (mp->start == NULL) {
4920 bam_error(NO_MENU, menu_path);
4921 return (BAM_ERROR);
4924 if (opt != NULL) {
4925 if (selector(mp, opt, &entry, &title) != BAM_SUCCESS) {
4926 return (BAM_ERROR);
4928 assert((entry != ENTRY_INIT) ^ (title != NULL));
4929 } else {
4930 (void) read_globals(mp, menu_path, menu_cmds[DEFAULT_CMD], 0);
4931 (void) read_globals(mp, menu_path, menu_cmds[TIMEOUT_CMD], 0);
4934 found = 0;
4935 for (lp = mp->start; lp; lp = lp->next) {
4936 if (lp->flags == BAM_COMMENT || lp->flags == BAM_EMPTY)
4937 continue;
4938 if (opt == NULL && lp->flags == BAM_TITLE) {
4939 bam_print(PRINT_TITLE, lp->entryNum,
4940 lp->arg);
4941 found = 1;
4942 continue;
4944 if (entry != ENTRY_INIT && lp->entryNum == entry) {
4945 bam_print(PRINT, lp->line);
4946 found = 1;
4947 continue;
4951 * We set the entry value here so that all lines
4952 * in entry get printed. If we subsequently match
4953 * title in other entries, all lines in those
4954 * entries get printed as well.
4956 if (title && lp->flags == BAM_TITLE && lp->arg &&
4957 strncmp(title, lp->arg, strlen(title)) == 0) {
4958 bam_print(PRINT, lp->line);
4959 entry = lp->entryNum;
4960 found = 1;
4961 continue;
4965 if (!found) {
4966 bam_error(NO_MATCH_ENTRY);
4967 return (BAM_ERROR);
4970 return (BAM_SUCCESS);
4974 add_boot_entry(menu_t *mp,
4975 char *title,
4976 char *findroot,
4977 char *kernel,
4978 char *mod_kernel,
4979 char *module,
4980 char *bootfs)
4982 int lineNum;
4983 int entryNum;
4984 char linebuf[BAM_MAXLINE];
4985 menu_cmd_t k_cmd;
4986 menu_cmd_t m_cmd;
4987 const char *fcn = "add_boot_entry()";
4989 assert(mp);
4991 INJECT_ERROR1("ADD_BOOT_ENTRY_FINDROOT_NULL", findroot = NULL);
4992 if (findroot == NULL) {
4993 bam_error(NULL_FINDROOT);
4994 return (BAM_ERROR);
4997 if (title == NULL) {
4998 title = "Solaris"; /* default to Solaris */
5000 if (kernel == NULL) {
5001 bam_error(SUBOPT_MISS, menu_cmds[KERNEL_CMD]);
5002 return (BAM_ERROR);
5004 if (module == NULL) {
5005 if (bam_direct != BAM_DIRECT_DBOOT) {
5006 bam_error(SUBOPT_MISS, menu_cmds[MODULE_CMD]);
5007 return (BAM_ERROR);
5010 /* Figure the commands out from the kernel line */
5011 if (strstr(kernel, "$ISADIR") != NULL) {
5012 module = DIRECT_BOOT_ARCHIVE;
5013 } else if (strstr(kernel, "amd64") != NULL) {
5014 module = DIRECT_BOOT_ARCHIVE_64;
5015 } else {
5016 module = DIRECT_BOOT_ARCHIVE_32;
5020 k_cmd = KERNEL_DOLLAR_CMD;
5021 m_cmd = MODULE_DOLLAR_CMD;
5023 if (mp->start) {
5024 lineNum = mp->end->lineNum;
5025 entryNum = mp->end->entryNum;
5026 } else {
5027 lineNum = LINE_INIT;
5028 entryNum = ENTRY_INIT;
5032 * No separator for comment (HDR/FTR) commands
5033 * The syntax for comments is #<comment>
5035 (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5036 menu_cmds[COMMENT_CMD], BAM_BOOTADM_HDR);
5037 line_parser(mp, linebuf, &lineNum, &entryNum);
5039 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5040 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
5041 line_parser(mp, linebuf, &lineNum, &entryNum);
5043 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5044 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
5045 line_parser(mp, linebuf, &lineNum, &entryNum);
5046 BAM_DPRINTF((D_ADD_FINDROOT_NUM, fcn, lineNum, entryNum));
5048 if (bootfs != NULL) {
5049 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5050 menu_cmds[BOOTFS_CMD], menu_cmds[SEP_CMD], bootfs);
5051 line_parser(mp, linebuf, &lineNum, &entryNum);
5054 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5055 menu_cmds[k_cmd], menu_cmds[SEP_CMD], kernel);
5056 line_parser(mp, linebuf, &lineNum, &entryNum);
5058 if (mod_kernel != NULL) {
5059 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5060 menu_cmds[m_cmd], menu_cmds[SEP_CMD], mod_kernel);
5061 line_parser(mp, linebuf, &lineNum, &entryNum);
5064 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
5065 menu_cmds[m_cmd], menu_cmds[SEP_CMD], module);
5066 line_parser(mp, linebuf, &lineNum, &entryNum);
5068 (void) snprintf(linebuf, sizeof (linebuf), "%s%s",
5069 menu_cmds[COMMENT_CMD], BAM_BOOTADM_FTR);
5070 line_parser(mp, linebuf, &lineNum, &entryNum);
5072 return (entryNum);
5075 error_t
5076 delete_boot_entry(menu_t *mp, int entryNum, int quiet)
5078 line_t *lp;
5079 line_t *freed;
5080 entry_t *ent;
5081 entry_t *tmp;
5082 int deleted = 0;
5083 const char *fcn = "delete_boot_entry()";
5085 assert(entryNum != ENTRY_INIT);
5087 tmp = NULL;
5089 ent = mp->entries;
5090 while (ent) {
5091 lp = ent->start;
5094 * Check entry number and make sure it's a modifiable entry.
5096 * Guidelines:
5097 * + We can modify a bootadm-created entry
5098 * + We can modify a libbe-created entry
5100 if ((lp->flags != BAM_COMMENT &&
5101 (((ent->flags & BAM_ENTRY_LIBBE) == 0) &&
5102 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0)) ||
5103 (entryNum != ALL_ENTRIES && lp->entryNum != entryNum)) {
5104 ent = ent->next;
5105 continue;
5108 /* free the entry content */
5109 do {
5110 freed = lp;
5111 lp = lp->next; /* prev stays the same */
5112 BAM_DPRINTF((D_FREEING_LINE, fcn, freed->lineNum));
5113 unlink_line(mp, freed);
5114 line_free(freed);
5115 } while (freed != ent->end);
5117 /* free the entry_t structure */
5118 assert(tmp == NULL);
5119 tmp = ent;
5120 ent = ent->next;
5121 if (tmp->prev)
5122 tmp->prev->next = ent;
5123 else
5124 mp->entries = ent;
5125 if (ent)
5126 ent->prev = tmp->prev;
5127 BAM_DPRINTF((D_FREEING_ENTRY, fcn, tmp->entryNum));
5128 free(tmp);
5129 tmp = NULL;
5130 deleted = 1;
5133 assert(tmp == NULL);
5135 if (!deleted && entryNum != ALL_ENTRIES) {
5136 if (quiet == DBE_PRINTERR)
5137 bam_error(NO_BOOTADM_MATCH);
5138 return (BAM_ERROR);
5142 * Now that we have deleted an entry, update
5143 * the entry numbering and the default cmd.
5145 update_numbering(mp);
5147 return (BAM_SUCCESS);
5150 static error_t
5151 delete_all_entries(menu_t *mp, char *dummy, char *opt)
5153 assert(mp);
5154 assert(dummy == NULL);
5155 assert(opt == NULL);
5157 BAM_DPRINTF((D_FUNC_ENTRY0, "delete_all_entries"));
5159 if (mp->start == NULL) {
5160 bam_print(EMPTY_MENU);
5161 return (BAM_SUCCESS);
5164 if (delete_boot_entry(mp, ALL_ENTRIES, DBE_PRINTERR) != BAM_SUCCESS) {
5165 return (BAM_ERROR);
5168 return (BAM_WRITE);
5171 static FILE *
5172 create_diskmap(char *osroot)
5174 FILE *fp;
5175 char cmd[PATH_MAX + 16];
5176 char path[PATH_MAX];
5177 const char *fcn = "create_diskmap()";
5179 /* make sure we have a map file */
5180 fp = fopen(GRUBDISK_MAP, "r");
5181 if (fp == NULL) {
5182 int ret;
5184 ret = snprintf(path, sizeof (path), "%s/%s", osroot,
5185 CREATE_DISKMAP);
5186 if (ret >= sizeof (path)) {
5187 bam_error(PATH_TOO_LONG, osroot);
5188 return (NULL);
5190 if (is_safe_exec(path) == BAM_ERROR)
5191 return (NULL);
5193 (void) snprintf(cmd, sizeof (cmd),
5194 "%s/%s > /dev/null", osroot, CREATE_DISKMAP);
5195 if (exec_cmd(cmd, NULL) != 0)
5196 return (NULL);
5197 fp = fopen(GRUBDISK_MAP, "r");
5198 INJECT_ERROR1("DISKMAP_CREATE_FAIL", fp = NULL);
5199 if (fp) {
5200 BAM_DPRINTF((D_CREATED_DISKMAP, fcn, GRUBDISK_MAP));
5201 } else {
5202 BAM_DPRINTF((D_CREATE_DISKMAP_FAIL, fcn, GRUBDISK_MAP));
5205 return (fp);
5208 #define SECTOR_SIZE 512
5210 static int
5211 get_partition(char *device)
5213 int i, fd, is_pcfs, partno = PARTNO_NOTFOUND;
5214 struct mboot *mboot;
5215 char boot_sect[SECTOR_SIZE];
5216 char *wholedisk, *slice;
5217 #ifdef i386
5218 ext_part_t *epp;
5219 uint32_t secnum, numsec;
5220 int rval, pno, ext_partno = PARTNO_NOTFOUND;
5221 #endif
5223 /* form whole disk (p0) */
5224 slice = device + strlen(device) - 2;
5225 is_pcfs = (*slice != 's');
5226 if (!is_pcfs)
5227 *slice = '\0';
5228 wholedisk = s_calloc(1, strlen(device) + 3);
5229 (void) snprintf(wholedisk, strlen(device) + 3, "%sp0", device);
5230 if (!is_pcfs)
5231 *slice = 's';
5233 /* read boot sector */
5234 fd = open(wholedisk, O_RDONLY);
5235 if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) {
5236 return (partno);
5238 (void) close(fd);
5240 #ifdef i386
5241 /* Read/Initialize extended partition information */
5242 if ((rval = libfdisk_init(&epp, wholedisk, NULL, FDISK_READ_DISK))
5243 != FDISK_SUCCESS) {
5244 switch (rval) {
5246 * FDISK_EBADLOGDRIVE and FDISK_ENOLOGDRIVE can
5247 * be considered as soft errors and hence
5248 * we do not return
5250 case FDISK_EBADLOGDRIVE:
5251 break;
5252 case FDISK_ENOLOGDRIVE:
5253 break;
5254 case FDISK_EBADMAGIC:
5255 /*FALLTHROUGH*/
5256 default:
5257 free(wholedisk);
5258 libfdisk_fini(&epp);
5259 return (partno);
5262 #endif
5263 free(wholedisk);
5265 /* parse fdisk table */
5266 mboot = (struct mboot *)((void *)boot_sect);
5267 for (i = 0; i < FD_NUMPART; i++) {
5268 struct ipart *part =
5269 (struct ipart *)(uintptr_t)mboot->parts + i;
5270 if (is_pcfs) { /* looking for solaris boot part */
5271 if (part->systid == 0xbe) {
5272 partno = i;
5273 break;
5275 } else { /* look for solaris partition, old and new */
5276 if (part->systid == EFI_PMBR) {
5277 partno = PARTNO_EFI;
5278 break;
5281 #ifdef i386
5282 if ((part->systid == SUNIXOS &&
5283 (fdisk_is_linux_swap(epp, part->relsect,
5284 NULL) != 0)) || part->systid == SUNIXOS2) {
5285 #else
5286 if (part->systid == SUNIXOS ||
5287 part->systid == SUNIXOS2) {
5288 #endif
5289 partno = i;
5290 break;
5293 #ifdef i386
5294 if (fdisk_is_dos_extended(part->systid))
5295 ext_partno = i;
5296 #endif
5299 #ifdef i386
5300 /* If no primary solaris partition, check extended partition */
5301 if ((partno == PARTNO_NOTFOUND) && (ext_partno != PARTNO_NOTFOUND)) {
5302 rval = fdisk_get_solaris_part(epp, &pno, &secnum, &numsec);
5303 if (rval == FDISK_SUCCESS) {
5304 partno = pno - 1;
5307 libfdisk_fini(&epp);
5308 #endif
5309 return (partno);
5312 char *
5313 get_grubroot(char *osroot, char *osdev, char *menu_root)
5315 char *grubroot; /* (hd#,#,#) */
5316 char *slice;
5317 char *grubhd;
5318 int fdiskpart;
5319 int found = 0;
5320 char *devname;
5321 char *ctdname = strstr(osdev, "dsk/");
5322 char linebuf[PATH_MAX];
5323 FILE *fp;
5325 INJECT_ERROR1("GRUBROOT_INVALID_OSDEV", ctdname = NULL);
5326 if (ctdname == NULL) {
5327 bam_error(INVALID_DEV_DSK, osdev);
5328 return (NULL);
5331 if (menu_root && !menu_on_bootdisk(osroot, menu_root)) {
5332 /* menu bears no resemblance to our reality */
5333 bam_error(CANNOT_GRUBROOT_BOOTDISK, osdev);
5334 return (NULL);
5337 ctdname += strlen("dsk/");
5338 slice = strrchr(ctdname, 's');
5339 if (slice)
5340 *slice = '\0';
5342 fp = create_diskmap(osroot);
5343 if (fp == NULL) {
5344 bam_error(DISKMAP_FAIL, osroot);
5345 return (NULL);
5348 rewind(fp);
5349 while (s_fgets(linebuf, sizeof (linebuf), fp) != NULL) {
5350 grubhd = strtok(linebuf, " \t\n");
5351 if (grubhd)
5352 devname = strtok(NULL, " \t\n");
5353 else
5354 devname = NULL;
5355 if (devname && strcmp(devname, ctdname) == 0) {
5356 found = 1;
5357 break;
5361 if (slice)
5362 *slice = 's';
5364 (void) fclose(fp);
5365 fp = NULL;
5367 INJECT_ERROR1("GRUBROOT_BIOSDEV_FAIL", found = 0);
5368 if (found == 0) {
5369 bam_error(BIOSDEV_SKIP, osdev);
5370 return (NULL);
5373 fdiskpart = get_partition(osdev);
5374 INJECT_ERROR1("GRUBROOT_FDISK_FAIL", fdiskpart = PARTNO_NOTFOUND);
5375 if (fdiskpart == PARTNO_NOTFOUND) {
5376 bam_error(FDISKPART_FAIL, osdev);
5377 return (NULL);
5380 grubroot = s_calloc(1, 10);
5381 if (fdiskpart == PARTNO_EFI) {
5382 fdiskpart = atoi(&slice[1]);
5383 slice = NULL;
5386 if (slice) {
5387 (void) snprintf(grubroot, 10, "(hd%s,%d,%c)",
5388 grubhd, fdiskpart, slice[1] + 'a' - '0');
5389 } else
5390 (void) snprintf(grubroot, 10, "(hd%s,%d)",
5391 grubhd, fdiskpart);
5393 assert(fp == NULL);
5394 assert(strncmp(grubroot, "(hd", strlen("(hd")) == 0);
5395 return (grubroot);
5398 static char *
5399 find_primary_common(char *mntpt, char *fstype)
5401 char signdir[PATH_MAX];
5402 char tmpsign[MAXNAMELEN + 1];
5403 char *lu;
5404 char *ufs;
5405 char *zfs;
5406 DIR *dirp = NULL;
5407 struct dirent *entp;
5408 struct stat sb;
5409 const char *fcn = "find_primary_common()";
5411 (void) snprintf(signdir, sizeof (signdir), "%s/%s",
5412 mntpt, GRUBSIGN_DIR);
5414 if (stat(signdir, &sb) == -1) {
5415 BAM_DPRINTF((D_NO_SIGNDIR, fcn, signdir));
5416 return (NULL);
5419 dirp = opendir(signdir);
5420 INJECT_ERROR1("SIGNDIR_OPENDIR_FAIL", dirp = NULL);
5421 if (dirp == NULL) {
5422 bam_error(OPENDIR_FAILED, signdir, strerror(errno));
5423 return (NULL);
5426 ufs = zfs = lu = NULL;
5428 while (entp = readdir(dirp)) {
5429 if (strcmp(entp->d_name, ".") == 0 ||
5430 strcmp(entp->d_name, "..") == 0)
5431 continue;
5433 (void) snprintf(tmpsign, sizeof (tmpsign), "%s", entp->d_name);
5435 if (lu == NULL &&
5436 strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5437 strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5438 lu = s_strdup(tmpsign);
5441 if (ufs == NULL &&
5442 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5443 strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5444 ufs = s_strdup(tmpsign);
5447 if (zfs == NULL &&
5448 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5449 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5450 zfs = s_strdup(tmpsign);
5454 BAM_DPRINTF((D_EXIST_PRIMARY_SIGNS, fcn,
5455 zfs ? zfs : "NULL",
5456 ufs ? ufs : "NULL",
5457 lu ? lu : "NULL"));
5459 if (dirp) {
5460 (void) closedir(dirp);
5461 dirp = NULL;
5464 if (strcmp(fstype, "ufs") == 0 && zfs) {
5465 bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs");
5466 free(zfs);
5467 zfs = NULL;
5468 } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5469 bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs");
5470 free(ufs);
5471 ufs = NULL;
5474 assert(dirp == NULL);
5476 /* For now, we let Live Upgrade take care of its signature itself */
5477 if (lu) {
5478 BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu));
5479 free(lu);
5480 lu = NULL;
5483 return (zfs ? zfs : ufs);
5486 static char *
5487 find_backup_common(char *mntpt, char *fstype)
5489 FILE *bfp = NULL;
5490 char tmpsign[MAXNAMELEN + 1];
5491 char backup[PATH_MAX];
5492 char *ufs;
5493 char *zfs;
5494 char *lu;
5495 int error;
5496 const char *fcn = "find_backup_common()";
5499 * We didn't find it in the primary directory.
5500 * Look at the backup
5502 (void) snprintf(backup, sizeof (backup), "%s%s",
5503 mntpt, GRUBSIGN_BACKUP);
5505 bfp = fopen(backup, "r");
5506 if (bfp == NULL) {
5507 error = errno;
5508 if (bam_verbose) {
5509 bam_error(OPEN_FAIL, backup, strerror(error));
5511 BAM_DPRINTF((D_OPEN_FAIL, fcn, backup, strerror(error)));
5512 return (NULL);
5515 ufs = zfs = lu = NULL;
5517 while (s_fgets(tmpsign, sizeof (tmpsign), bfp) != NULL) {
5519 if (lu == NULL &&
5520 strncmp(tmpsign, GRUBSIGN_LU_PREFIX,
5521 strlen(GRUBSIGN_LU_PREFIX)) == 0) {
5522 lu = s_strdup(tmpsign);
5525 if (ufs == NULL &&
5526 strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
5527 strlen(GRUBSIGN_UFS_PREFIX)) == 0) {
5528 ufs = s_strdup(tmpsign);
5531 if (zfs == NULL &&
5532 strncmp(tmpsign, GRUBSIGN_ZFS_PREFIX,
5533 strlen(GRUBSIGN_ZFS_PREFIX)) == 0) {
5534 zfs = s_strdup(tmpsign);
5538 BAM_DPRINTF((D_EXIST_BACKUP_SIGNS, fcn,
5539 zfs ? zfs : "NULL",
5540 ufs ? ufs : "NULL",
5541 lu ? lu : "NULL"));
5543 if (bfp) {
5544 (void) fclose(bfp);
5545 bfp = NULL;
5548 if (strcmp(fstype, "ufs") == 0 && zfs) {
5549 bam_error(SIGN_FSTYPE_MISMATCH, zfs, "ufs");
5550 free(zfs);
5551 zfs = NULL;
5552 } else if (strcmp(fstype, "zfs") == 0 && ufs) {
5553 bam_error(SIGN_FSTYPE_MISMATCH, ufs, "zfs");
5554 free(ufs);
5555 ufs = NULL;
5558 assert(bfp == NULL);
5560 /* For now, we let Live Upgrade take care of its signature itself */
5561 if (lu) {
5562 BAM_DPRINTF((D_FREEING_LU_SIGNS, fcn, lu));
5563 free(lu);
5564 lu = NULL;
5567 return (zfs ? zfs : ufs);
5570 static char *
5571 find_ufs_existing(char *osroot)
5573 char *sign;
5574 const char *fcn = "find_ufs_existing()";
5576 sign = find_primary_common(osroot, "ufs");
5577 if (sign == NULL) {
5578 sign = find_backup_common(osroot, "ufs");
5579 BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL"));
5580 } else {
5581 BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign));
5584 return (sign);
5587 char *
5588 get_mountpoint(char *special, char *fstype)
5590 FILE *mntfp;
5591 struct mnttab mp = {0};
5592 struct mnttab mpref = {0};
5593 int error;
5594 int ret;
5595 const char *fcn = "get_mountpoint()";
5597 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, special, fstype));
5599 mntfp = fopen(MNTTAB, "r");
5600 error = errno;
5601 INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL);
5602 if (mntfp == NULL) {
5603 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
5604 return (NULL);
5607 mpref.mnt_special = special;
5608 mpref.mnt_fstype = fstype;
5610 ret = getmntany(mntfp, &mp, &mpref);
5611 INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
5612 if (ret != 0) {
5613 (void) fclose(mntfp);
5614 BAM_DPRINTF((D_NO_MNTPT, fcn, special, fstype));
5615 return (NULL);
5617 (void) fclose(mntfp);
5619 assert(mp.mnt_mountp);
5621 BAM_DPRINTF((D_GET_MOUNTPOINT_RET, fcn, special, mp.mnt_mountp));
5623 return (s_strdup(mp.mnt_mountp));
5627 * Mounts a "legacy" top dataset (if needed)
5628 * Returns: The mountpoint of the legacy top dataset or NULL on error
5629 * mnted returns one of the above values defined for zfs_mnted_t
5631 static char *
5632 mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
5634 char cmd[PATH_MAX];
5635 char tmpmnt[PATH_MAX];
5636 filelist_t flist = {0};
5637 char *is_mounted;
5638 struct stat sb;
5639 int ret;
5640 const char *fcn = "mount_legacy_dataset()";
5642 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool));
5644 *mnted = ZFS_MNT_ERROR;
5646 (void) snprintf(cmd, sizeof (cmd),
5647 "/sbin/zfs get -Ho value mounted %s",
5648 pool);
5650 ret = exec_cmd(cmd, &flist);
5651 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
5652 if (ret != 0) {
5653 bam_error(ZFS_MNTED_FAILED, pool);
5654 return (NULL);
5657 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL);
5658 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5659 bam_error(BAD_ZFS_MNTED, pool);
5660 filelist_free(&flist);
5661 return (NULL);
5664 is_mounted = strtok(flist.head->line, " \t\n");
5665 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes");
5666 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no");
5667 if (strcmp(is_mounted, "no") != 0) {
5668 filelist_free(&flist);
5669 *mnted = LEGACY_ALREADY;
5670 /* get_mountpoint returns a strdup'ed string */
5671 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_ALREADY, fcn, pool));
5672 return (get_mountpoint(pool, "zfs"));
5675 filelist_free(&flist);
5678 * legacy top dataset is not mounted. Mount it now
5679 * First create a mountpoint.
5681 (void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d",
5682 ZFS_LEGACY_MNTPT, getpid());
5684 ret = stat(tmpmnt, &sb);
5685 if (ret == -1) {
5686 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_ABS, fcn, pool, tmpmnt));
5687 ret = mkdirp(tmpmnt, DIR_PERMS);
5688 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1);
5689 if (ret == -1) {
5690 bam_error(MKDIR_FAILED, tmpmnt, strerror(errno));
5691 return (NULL);
5693 } else {
5694 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MNTPT_PRES, fcn, pool, tmpmnt));
5697 (void) snprintf(cmd, sizeof (cmd),
5698 "/sbin/mount -F zfs %s %s",
5699 pool, tmpmnt);
5701 ret = exec_cmd(cmd, NULL);
5702 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
5703 if (ret != 0) {
5704 bam_error(ZFS_MOUNT_FAILED, pool);
5705 (void) rmdir(tmpmnt);
5706 return (NULL);
5709 *mnted = LEGACY_MOUNTED;
5710 BAM_DPRINTF((D_Z_MOUNT_TOP_LEG_MOUNTED, fcn, pool, tmpmnt));
5711 return (s_strdup(tmpmnt));
5715 * Mounts the top dataset (if needed)
5716 * Returns: The mountpoint of the top dataset or NULL on error
5717 * mnted returns one of the above values defined for zfs_mnted_t
5719 static char *
5720 mount_top_dataset(char *pool, zfs_mnted_t *mnted)
5722 char cmd[PATH_MAX];
5723 filelist_t flist = {0};
5724 char *is_mounted;
5725 char *mntpt;
5726 char *zmntpt;
5727 int ret;
5728 const char *fcn = "mount_top_dataset()";
5730 *mnted = ZFS_MNT_ERROR;
5732 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, pool));
5735 * First check if the top dataset is a "legacy" dataset
5737 (void) snprintf(cmd, sizeof (cmd),
5738 "/sbin/zfs get -Ho value mountpoint %s",
5739 pool);
5740 ret = exec_cmd(cmd, &flist);
5741 INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
5742 if (ret != 0) {
5743 bam_error(ZFS_MNTPT_FAILED, pool);
5744 return (NULL);
5747 if (flist.head && (flist.head == flist.tail)) {
5748 char *legacy = strtok(flist.head->line, " \t\n");
5749 if (legacy && strcmp(legacy, "legacy") == 0) {
5750 filelist_free(&flist);
5751 BAM_DPRINTF((D_Z_IS_LEGACY, fcn, pool));
5752 return (mount_legacy_dataset(pool, mnted));
5756 filelist_free(&flist);
5758 BAM_DPRINTF((D_Z_IS_NOT_LEGACY, fcn, pool));
5760 (void) snprintf(cmd, sizeof (cmd),
5761 "/sbin/zfs get -Ho value mounted %s",
5762 pool);
5764 ret = exec_cmd(cmd, &flist);
5765 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
5766 if (ret != 0) {
5767 bam_error(ZFS_MNTED_FAILED, pool);
5768 return (NULL);
5771 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL);
5772 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5773 bam_error(BAD_ZFS_MNTED, pool);
5774 filelist_free(&flist);
5775 return (NULL);
5778 is_mounted = strtok(flist.head->line, " \t\n");
5779 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes");
5780 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no");
5781 if (strcmp(is_mounted, "no") != 0) {
5782 filelist_free(&flist);
5783 *mnted = ZFS_ALREADY;
5784 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_ALREADY, fcn, pool));
5785 goto mounted;
5788 filelist_free(&flist);
5789 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOT_ALREADY, fcn, pool));
5791 /* top dataset is not mounted. Mount it now */
5792 (void) snprintf(cmd, sizeof (cmd),
5793 "/sbin/zfs mount %s", pool);
5794 ret = exec_cmd(cmd, NULL);
5795 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1);
5796 if (ret != 0) {
5797 bam_error(ZFS_MOUNT_FAILED, pool);
5798 return (NULL);
5800 *mnted = ZFS_MOUNTED;
5801 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MOUNTED_NOW, fcn, pool));
5802 /*FALLTHRU*/
5803 mounted:
5805 * Now get the mountpoint
5807 (void) snprintf(cmd, sizeof (cmd),
5808 "/sbin/zfs get -Ho value mountpoint %s",
5809 pool);
5811 ret = exec_cmd(cmd, &flist);
5812 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
5813 if (ret != 0) {
5814 bam_error(ZFS_MNTPT_FAILED, pool);
5815 goto error;
5818 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL);
5819 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5820 bam_error(NULL_ZFS_MNTPT, pool);
5821 goto error;
5824 mntpt = strtok(flist.head->line, " \t\n");
5825 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo");
5826 if (*mntpt != '/') {
5827 bam_error(BAD_ZFS_MNTPT, pool, mntpt);
5828 goto error;
5830 zmntpt = s_strdup(mntpt);
5832 filelist_free(&flist);
5834 BAM_DPRINTF((D_Z_MOUNT_TOP_NONLEG_MNTPT, fcn, pool, zmntpt));
5836 return (zmntpt);
5838 error:
5839 filelist_free(&flist);
5840 (void) umount_top_dataset(pool, *mnted, NULL);
5841 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
5842 return (NULL);
5845 static int
5846 umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
5848 char cmd[PATH_MAX];
5849 int ret;
5850 const char *fcn = "umount_top_dataset()";
5852 INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
5853 switch (mnted) {
5854 case LEGACY_ALREADY:
5855 case ZFS_ALREADY:
5856 /* nothing to do */
5857 BAM_DPRINTF((D_Z_UMOUNT_TOP_ALREADY_NOP, fcn, pool,
5858 mntpt ? mntpt : "NULL"));
5859 free(mntpt);
5860 return (BAM_SUCCESS);
5861 case LEGACY_MOUNTED:
5862 (void) snprintf(cmd, sizeof (cmd),
5863 "/sbin/umount %s", pool);
5864 ret = exec_cmd(cmd, NULL);
5865 INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1);
5866 if (ret != 0) {
5867 bam_error(UMOUNT_FAILED, pool);
5868 free(mntpt);
5869 return (BAM_ERROR);
5871 if (mntpt)
5872 (void) rmdir(mntpt);
5873 free(mntpt);
5874 BAM_DPRINTF((D_Z_UMOUNT_TOP_LEGACY, fcn, pool));
5875 return (BAM_SUCCESS);
5876 case ZFS_MOUNTED:
5877 free(mntpt);
5878 (void) snprintf(cmd, sizeof (cmd),
5879 "/sbin/zfs unmount %s", pool);
5880 ret = exec_cmd(cmd, NULL);
5881 INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1);
5882 if (ret != 0) {
5883 bam_error(UMOUNT_FAILED, pool);
5884 return (BAM_ERROR);
5886 BAM_DPRINTF((D_Z_UMOUNT_TOP_NONLEG, fcn, pool));
5887 return (BAM_SUCCESS);
5888 default:
5889 bam_error(INT_BAD_MNTSTATE, pool);
5890 return (BAM_ERROR);
5892 /*NOTREACHED*/
5896 * For ZFS, osdev can be one of two forms
5897 * It can be a "special" file as seen in mnttab: rpool/ROOT/szboot_0402
5898 * It can be a /dev/[r]dsk special file. We handle both instances
5900 static char *
5901 get_pool(char *osdev)
5903 char cmd[PATH_MAX];
5904 char buf[PATH_MAX];
5905 filelist_t flist = {0};
5906 char *pool;
5907 char *cp;
5908 char *slash;
5909 int ret;
5910 const char *fcn = "get_pool()";
5912 INJECT_ERROR1("GET_POOL_OSDEV", osdev = NULL);
5913 if (osdev == NULL) {
5914 bam_error(GET_POOL_OSDEV_NULL);
5915 return (NULL);
5918 BAM_DPRINTF((D_GET_POOL_OSDEV, fcn, osdev));
5920 if (osdev[0] != '/') {
5921 (void) strlcpy(buf, osdev, sizeof (buf));
5922 slash = strchr(buf, '/');
5923 if (slash)
5924 *slash = '\0';
5925 pool = s_strdup(buf);
5926 BAM_DPRINTF((D_GET_POOL_RET, fcn, pool));
5927 return (pool);
5928 } else if (strncmp(osdev, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
5929 strncmp(osdev, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0) {
5930 bam_error(GET_POOL_BAD_OSDEV, osdev);
5931 return (NULL);
5935 * Call the zfs fstyp directly since this is a zpool. This avoids
5936 * potential pcfs conflicts if the first block wasn't cleared.
5938 (void) snprintf(cmd, sizeof (cmd),
5939 "/usr/lib/fs/zfs/fstyp -a %s 2>/dev/null | /bin/grep '^name:'",
5940 osdev);
5942 ret = exec_cmd(cmd, &flist);
5943 INJECT_ERROR1("GET_POOL_FSTYP", ret = 1);
5944 if (ret != 0) {
5945 bam_error(FSTYP_A_FAILED, osdev);
5946 return (NULL);
5949 INJECT_ERROR1("GET_POOL_FSTYP_OUT", flist.head = NULL);
5950 if ((flist.head == NULL) || (flist.head != flist.tail)) {
5951 bam_error(NULL_FSTYP_A, osdev);
5952 filelist_free(&flist);
5953 return (NULL);
5956 (void) strtok(flist.head->line, "'");
5957 cp = strtok(NULL, "'");
5958 INJECT_ERROR1("GET_POOL_FSTYP_STRTOK", cp = NULL);
5959 if (cp == NULL) {
5960 bam_error(BAD_FSTYP_A, osdev);
5961 filelist_free(&flist);
5962 return (NULL);
5965 pool = s_strdup(cp);
5967 filelist_free(&flist);
5969 BAM_DPRINTF((D_GET_POOL_RET, fcn, pool));
5971 return (pool);
5974 static char *
5975 find_zfs_existing(char *osdev)
5977 char *pool;
5978 zfs_mnted_t mnted;
5979 char *mntpt;
5980 char *sign;
5981 const char *fcn = "find_zfs_existing()";
5983 pool = get_pool(osdev);
5984 INJECT_ERROR1("ZFS_FIND_EXIST_POOL", pool = NULL);
5985 if (pool == NULL) {
5986 bam_error(ZFS_GET_POOL_FAILED, osdev);
5987 return (NULL);
5990 mntpt = mount_top_dataset(pool, &mnted);
5991 INJECT_ERROR1("ZFS_FIND_EXIST_MOUNT_TOP", mntpt = NULL);
5992 if (mntpt == NULL) {
5993 bam_error(ZFS_MOUNT_TOP_DATASET_FAILED, pool);
5994 free(pool);
5995 return (NULL);
5998 sign = find_primary_common(mntpt, "zfs");
5999 if (sign == NULL) {
6000 sign = find_backup_common(mntpt, "zfs");
6001 BAM_DPRINTF((D_EXIST_BACKUP_SIGN, fcn, sign ? sign : "NULL"));
6002 } else {
6003 BAM_DPRINTF((D_EXIST_PRIMARY_SIGN, fcn, sign));
6006 (void) umount_top_dataset(pool, mnted, mntpt);
6008 free(pool);
6010 return (sign);
6013 static char *
6014 find_existing_sign(char *osroot, char *osdev, char *fstype)
6016 const char *fcn = "find_existing_sign()";
6018 INJECT_ERROR1("FIND_EXIST_NOTSUP_FS", fstype = "foofs");
6019 if (strcmp(fstype, "ufs") == 0) {
6020 BAM_DPRINTF((D_CHECK_UFS_EXIST_SIGN, fcn));
6021 return (find_ufs_existing(osroot));
6022 } else if (strcmp(fstype, "zfs") == 0) {
6023 BAM_DPRINTF((D_CHECK_ZFS_EXIST_SIGN, fcn));
6024 return (find_zfs_existing(osdev));
6025 } else {
6026 bam_error(GRUBSIGN_NOTSUP, fstype);
6027 return (NULL);
6031 #define MH_HASH_SZ 16
6033 typedef enum {
6034 MH_ERROR = -1,
6035 MH_NOMATCH,
6036 MH_MATCH
6037 } mh_search_t;
6039 typedef struct mcache {
6040 char *mc_special;
6041 char *mc_mntpt;
6042 char *mc_fstype;
6043 struct mcache *mc_next;
6044 } mcache_t;
6046 typedef struct mhash {
6047 mcache_t *mh_hash[MH_HASH_SZ];
6048 } mhash_t;
6050 static int
6051 mhash_fcn(char *key)
6053 int i;
6054 uint64_t sum = 0;
6056 for (i = 0; key[i] != '\0'; i++) {
6057 sum += (uchar_t)key[i];
6060 sum %= MH_HASH_SZ;
6062 assert(sum < MH_HASH_SZ);
6064 return (sum);
6067 static mhash_t *
6068 cache_mnttab(void)
6070 FILE *mfp;
6071 struct extmnttab mnt;
6072 mcache_t *mcp;
6073 mhash_t *mhp;
6074 char *ctds;
6075 int idx;
6076 int error;
6077 char *special_dup;
6078 const char *fcn = "cache_mnttab()";
6080 mfp = fopen(MNTTAB, "r");
6081 error = errno;
6082 INJECT_ERROR1("CACHE_MNTTAB_MNTTAB_ERR", mfp = NULL);
6083 if (mfp == NULL) {
6084 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
6085 return (NULL);
6088 mhp = s_calloc(1, sizeof (mhash_t));
6090 resetmnttab(mfp);
6092 while (getextmntent(mfp, &mnt, sizeof (mnt)) == 0) {
6093 /* only cache ufs */
6094 if (strcmp(mnt.mnt_fstype, "ufs") != 0)
6095 continue;
6097 /* basename() modifies its arg, so dup it */
6098 special_dup = s_strdup(mnt.mnt_special);
6099 ctds = basename(special_dup);
6101 mcp = s_calloc(1, sizeof (mcache_t));
6102 mcp->mc_special = s_strdup(ctds);
6103 mcp->mc_mntpt = s_strdup(mnt.mnt_mountp);
6104 mcp->mc_fstype = s_strdup(mnt.mnt_fstype);
6105 BAM_DPRINTF((D_CACHE_MNTS, fcn, ctds,
6106 mnt.mnt_mountp, mnt.mnt_fstype));
6107 idx = mhash_fcn(ctds);
6108 mcp->mc_next = mhp->mh_hash[idx];
6109 mhp->mh_hash[idx] = mcp;
6110 free(special_dup);
6113 (void) fclose(mfp);
6115 return (mhp);
6118 static void
6119 free_mnttab(mhash_t *mhp)
6121 mcache_t *mcp;
6122 int i;
6124 for (i = 0; i < MH_HASH_SZ; i++) {
6125 /*LINTED*/
6126 while (mcp = mhp->mh_hash[i]) {
6127 mhp->mh_hash[i] = mcp->mc_next;
6128 free(mcp->mc_special);
6129 free(mcp->mc_mntpt);
6130 free(mcp->mc_fstype);
6131 free(mcp);
6135 for (i = 0; i < MH_HASH_SZ; i++) {
6136 assert(mhp->mh_hash[i] == NULL);
6138 free(mhp);
6141 static mh_search_t
6142 search_hash(mhash_t *mhp, char *special, char **mntpt)
6144 int idx;
6145 mcache_t *mcp;
6146 const char *fcn = "search_hash()";
6148 assert(mntpt);
6150 *mntpt = NULL;
6152 INJECT_ERROR1("SEARCH_HASH_FULL_PATH", special = "/foo");
6153 if (strchr(special, '/')) {
6154 bam_error(INVALID_MHASH_KEY, special);
6155 return (MH_ERROR);
6158 idx = mhash_fcn(special);
6160 for (mcp = mhp->mh_hash[idx]; mcp; mcp = mcp->mc_next) {
6161 if (strcmp(mcp->mc_special, special) == 0)
6162 break;
6165 if (mcp == NULL) {
6166 BAM_DPRINTF((D_MNTTAB_HASH_NOMATCH, fcn, special));
6167 return (MH_NOMATCH);
6170 assert(strcmp(mcp->mc_fstype, "ufs") == 0);
6171 *mntpt = mcp->mc_mntpt;
6172 BAM_DPRINTF((D_MNTTAB_HASH_MATCH, fcn, special));
6173 return (MH_MATCH);
6176 static int
6177 check_add_ufs_sign_to_list(FILE *tfp, char *mntpt)
6179 char *sign;
6180 char *signline;
6181 char signbuf[MAXNAMELEN];
6182 int len;
6183 int error;
6184 const char *fcn = "check_add_ufs_sign_to_list()";
6186 /* safe to specify NULL as "osdev" arg for UFS */
6187 sign = find_existing_sign(mntpt, NULL, "ufs");
6188 if (sign == NULL) {
6189 /* No existing signature, nothing to add to list */
6190 BAM_DPRINTF((D_NO_SIGN_TO_LIST, fcn, mntpt));
6191 return (0);
6194 (void) snprintf(signbuf, sizeof (signbuf), "%s\n", sign);
6195 signline = signbuf;
6197 INJECT_ERROR1("UFS_MNTPT_SIGN_NOTUFS", signline = "pool_rpool10\n");
6198 if (strncmp(signline, GRUBSIGN_UFS_PREFIX,
6199 strlen(GRUBSIGN_UFS_PREFIX))) {
6200 bam_error(INVALID_UFS_SIGNATURE, sign);
6201 free(sign);
6202 /* ignore invalid signatures */
6203 return (0);
6206 len = fputs(signline, tfp);
6207 error = errno;
6208 INJECT_ERROR1("SIGN_LIST_PUTS_ERROR", len = 0);
6209 if (len != strlen(signline)) {
6210 bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error));
6211 free(sign);
6212 return (-1);
6215 free(sign);
6217 BAM_DPRINTF((D_SIGN_LIST_PUTS_DONE, fcn, mntpt));
6218 return (0);
6222 * slice is a basename not a full pathname
6224 static int
6225 process_slice_common(char *slice, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6227 int ret;
6228 char cmd[PATH_MAX];
6229 char path[PATH_MAX];
6230 struct stat sbuf;
6231 char *mntpt;
6232 filelist_t flist = {0};
6233 char *fstype;
6234 char blkslice[PATH_MAX];
6235 const char *fcn = "process_slice_common()";
6238 ret = search_hash(mhp, slice, &mntpt);
6239 switch (ret) {
6240 case MH_MATCH:
6241 if (check_add_ufs_sign_to_list(tfp, mntpt) == -1)
6242 return (-1);
6243 else
6244 return (0);
6245 case MH_NOMATCH:
6246 break;
6247 case MH_ERROR:
6248 default:
6249 return (-1);
6252 (void) snprintf(path, sizeof (path), "/dev/rdsk/%s", slice);
6253 if (stat(path, &sbuf) == -1) {
6254 BAM_DPRINTF((D_SLICE_ENOENT, fcn, path));
6255 return (0);
6258 /* Check if ufs. Call ufs fstyp directly to avoid pcfs conflicts. */
6259 (void) snprintf(cmd, sizeof (cmd),
6260 "/usr/lib/fs/ufs/fstyp /dev/rdsk/%s 2>/dev/null",
6261 slice);
6263 if (exec_cmd(cmd, &flist) != 0) {
6264 if (bam_verbose)
6265 bam_print(FSTYP_FAILED, slice);
6266 return (0);
6269 if ((flist.head == NULL) || (flist.head != flist.tail)) {
6270 if (bam_verbose)
6271 bam_print(FSTYP_BAD, slice);
6272 filelist_free(&flist);
6273 return (0);
6276 fstype = strtok(flist.head->line, " \t\n");
6277 if (fstype == NULL || strcmp(fstype, "ufs") != 0) {
6278 if (bam_verbose)
6279 bam_print(NOT_UFS_SLICE, slice, fstype);
6280 filelist_free(&flist);
6281 return (0);
6284 filelist_free(&flist);
6287 * Since we are mounting the filesystem read-only, the
6288 * the last mount field of the superblock is unchanged
6289 * and does not need to be fixed up post-mount;
6292 (void) snprintf(blkslice, sizeof (blkslice), "/dev/dsk/%s",
6293 slice);
6295 (void) snprintf(cmd, sizeof (cmd),
6296 "/usr/sbin/mount -F ufs -o ro %s %s "
6297 "> /dev/null 2>&1", blkslice, tmpmnt);
6299 if (exec_cmd(cmd, NULL) != 0) {
6300 if (bam_verbose)
6301 bam_print(MOUNT_FAILED, blkslice, "ufs");
6302 return (0);
6305 ret = check_add_ufs_sign_to_list(tfp, tmpmnt);
6307 (void) snprintf(cmd, sizeof (cmd),
6308 "/usr/sbin/umount -f %s > /dev/null 2>&1",
6309 tmpmnt);
6311 if (exec_cmd(cmd, NULL) != 0) {
6312 bam_print(UMOUNT_FAILED, slice);
6313 return (0);
6316 return (ret);
6319 static int
6320 process_vtoc_slices(
6321 char *s0,
6322 struct vtoc *vtoc,
6323 FILE *tfp,
6324 mhash_t *mhp,
6325 char *tmpmnt)
6327 int idx;
6328 char slice[PATH_MAX];
6329 size_t len;
6330 char *cp;
6331 const char *fcn = "process_vtoc_slices()";
6333 len = strlen(s0);
6335 assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6337 s0[len - 1] = '\0';
6339 (void) strlcpy(slice, s0, sizeof (slice));
6341 s0[len - 1] = '0';
6343 cp = slice + len - 1;
6345 for (idx = 0; idx < vtoc->v_nparts; idx++) {
6347 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6349 if (vtoc->v_part[idx].p_size == 0) {
6350 BAM_DPRINTF((D_VTOC_SIZE_ZERO, fcn, slice));
6351 continue;
6354 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6355 switch (vtoc->v_part[idx].p_tag) {
6356 case V_SWAP:
6357 case V_USR:
6358 case V_BACKUP:
6359 case V_VAR:
6360 case V_HOME:
6361 case V_ALTSCTR:
6362 BAM_DPRINTF((D_VTOC_NOT_ROOT_TAG, fcn, slice));
6363 continue;
6364 default:
6365 BAM_DPRINTF((D_VTOC_ROOT_TAG, fcn, slice));
6366 break;
6369 /* skip unmountable and readonly slices */
6370 switch (vtoc->v_part[idx].p_flag) {
6371 case V_UNMNT:
6372 case V_RONLY:
6373 BAM_DPRINTF((D_VTOC_NOT_RDWR_FLAG, fcn, slice));
6374 continue;
6375 default:
6376 BAM_DPRINTF((D_VTOC_RDWR_FLAG, fcn, slice));
6377 break;
6380 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6381 return (-1);
6385 return (0);
6388 static int
6389 process_efi_slices(
6390 char *s0,
6391 struct dk_gpt *efi,
6392 FILE *tfp,
6393 mhash_t *mhp,
6394 char *tmpmnt)
6396 int idx;
6397 char slice[PATH_MAX];
6398 size_t len;
6399 char *cp;
6400 const char *fcn = "process_efi_slices()";
6402 len = strlen(s0);
6404 assert(s0[len - 2] == 's' && s0[len - 1] == '0');
6406 s0[len - 1] = '\0';
6408 (void) strlcpy(slice, s0, sizeof (slice));
6410 s0[len - 1] = '0';
6412 cp = slice + len - 1;
6414 for (idx = 0; idx < efi->efi_nparts; idx++) {
6416 (void) snprintf(cp, sizeof (slice) - (len - 1), "%u", idx);
6418 if (efi->efi_parts[idx].p_size == 0) {
6419 BAM_DPRINTF((D_EFI_SIZE_ZERO, fcn, slice));
6420 continue;
6423 /* Skip "SWAP", "USR", "BACKUP", "VAR", "HOME", "ALTSCTR" */
6424 switch (efi->efi_parts[idx].p_tag) {
6425 case V_SWAP:
6426 case V_USR:
6427 case V_BACKUP:
6428 case V_VAR:
6429 case V_HOME:
6430 case V_ALTSCTR:
6431 BAM_DPRINTF((D_EFI_NOT_ROOT_TAG, fcn, slice));
6432 continue;
6433 default:
6434 BAM_DPRINTF((D_EFI_ROOT_TAG, fcn, slice));
6435 break;
6438 /* skip unmountable and readonly slices */
6439 switch (efi->efi_parts[idx].p_flag) {
6440 case V_UNMNT:
6441 case V_RONLY:
6442 BAM_DPRINTF((D_EFI_NOT_RDWR_FLAG, fcn, slice));
6443 continue;
6444 default:
6445 BAM_DPRINTF((D_EFI_RDWR_FLAG, fcn, slice));
6446 break;
6449 if (process_slice_common(slice, tfp, mhp, tmpmnt) == -1) {
6450 return (-1);
6454 return (0);
6458 * s0 is a basename not a full path
6460 static int
6461 process_slice0(char *s0, FILE *tfp, mhash_t *mhp, char *tmpmnt)
6463 struct vtoc vtoc;
6464 struct dk_gpt *efi;
6465 char s0path[PATH_MAX];
6466 struct stat sbuf;
6467 int e_flag;
6468 int v_flag;
6469 int retval;
6470 int err;
6471 int fd;
6472 const char *fcn = "process_slice0()";
6474 (void) snprintf(s0path, sizeof (s0path), "/dev/rdsk/%s", s0);
6476 if (stat(s0path, &sbuf) == -1) {
6477 BAM_DPRINTF((D_SLICE0_ENOENT, fcn, s0path));
6478 return (0);
6481 fd = open(s0path, O_NONBLOCK|O_RDONLY);
6482 if (fd == -1) {
6483 bam_error(OPEN_FAIL, s0path, strerror(errno));
6484 return (0);
6487 e_flag = v_flag = 0;
6488 retval = ((err = read_vtoc(fd, &vtoc)) >= 0) ? 0 : err;
6489 switch (retval) {
6490 case VT_EIO:
6491 BAM_DPRINTF((D_VTOC_READ_FAIL, fcn, s0path));
6492 break;
6493 case VT_EINVAL:
6494 BAM_DPRINTF((D_VTOC_INVALID, fcn, s0path));
6495 break;
6496 case VT_ERROR:
6497 BAM_DPRINTF((D_VTOC_UNKNOWN_ERR, fcn, s0path));
6498 break;
6499 case VT_ENOTSUP:
6500 e_flag = 1;
6501 BAM_DPRINTF((D_VTOC_NOTSUP, fcn, s0path));
6502 break;
6503 case 0:
6504 v_flag = 1;
6505 BAM_DPRINTF((D_VTOC_READ_SUCCESS, fcn, s0path));
6506 break;
6507 default:
6508 BAM_DPRINTF((D_VTOC_UNKNOWN_RETCODE, fcn, s0path));
6509 break;
6513 if (e_flag) {
6514 e_flag = 0;
6515 retval = ((err = efi_alloc_and_read(fd, &efi)) >= 0) ? 0 : err;
6516 switch (retval) {
6517 case VT_EIO:
6518 BAM_DPRINTF((D_EFI_READ_FAIL, fcn, s0path));
6519 break;
6520 case VT_EINVAL:
6521 BAM_DPRINTF((D_EFI_INVALID, fcn, s0path));
6522 break;
6523 case VT_ERROR:
6524 BAM_DPRINTF((D_EFI_UNKNOWN_ERR, fcn, s0path));
6525 break;
6526 case VT_ENOTSUP:
6527 BAM_DPRINTF((D_EFI_NOTSUP, fcn, s0path));
6528 break;
6529 case 0:
6530 e_flag = 1;
6531 BAM_DPRINTF((D_EFI_READ_SUCCESS, fcn, s0path));
6532 break;
6533 default:
6534 BAM_DPRINTF((D_EFI_UNKNOWN_RETCODE, fcn, s0path));
6535 break;
6539 (void) close(fd);
6541 if (v_flag) {
6542 retval = process_vtoc_slices(s0,
6543 &vtoc, tfp, mhp, tmpmnt);
6544 } else if (e_flag) {
6545 retval = process_efi_slices(s0,
6546 efi, tfp, mhp, tmpmnt);
6547 } else {
6548 BAM_DPRINTF((D_NOT_VTOC_OR_EFI, fcn, s0path));
6549 return (0);
6552 return (retval);
6556 * Find and create a list of all existing UFS boot signatures
6558 static int
6559 FindAllUfsSignatures(void)
6561 mhash_t *mnttab_hash;
6562 DIR *dirp = NULL;
6563 struct dirent *dp;
6564 char tmpmnt[PATH_MAX];
6565 char cmd[PATH_MAX];
6566 struct stat sb;
6567 int fd;
6568 FILE *tfp;
6569 size_t len;
6570 int ret;
6571 int error;
6572 const char *fcn = "FindAllUfsSignatures()";
6574 if (stat(UFS_SIGNATURE_LIST, &sb) != -1) {
6575 bam_print(SIGNATURE_LIST_EXISTS, UFS_SIGNATURE_LIST);
6576 return (0);
6579 fd = open(UFS_SIGNATURE_LIST".tmp",
6580 O_RDWR|O_CREAT|O_TRUNC, 0644);
6581 error = errno;
6582 INJECT_ERROR1("SIGN_LIST_TMP_TRUNC", fd = -1);
6583 if (fd == -1) {
6584 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
6585 return (-1);
6588 ret = close(fd);
6589 error = errno;
6590 INJECT_ERROR1("SIGN_LIST_TMP_CLOSE", ret = -1);
6591 if (ret == -1) {
6592 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
6593 strerror(error));
6594 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6595 return (-1);
6598 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
6599 error = errno;
6600 INJECT_ERROR1("SIGN_LIST_APPEND_FOPEN", tfp = NULL);
6601 if (tfp == NULL) {
6602 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
6603 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6604 return (-1);
6607 mnttab_hash = cache_mnttab();
6608 INJECT_ERROR1("CACHE_MNTTAB_ERROR", mnttab_hash = NULL);
6609 if (mnttab_hash == NULL) {
6610 (void) fclose(tfp);
6611 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6612 bam_error(CACHE_MNTTAB_FAIL, fcn);
6613 return (-1);
6616 (void) snprintf(tmpmnt, sizeof (tmpmnt),
6617 "/tmp/bootadm_ufs_sign_mnt.%d", getpid());
6618 (void) unlink(tmpmnt);
6620 ret = mkdirp(tmpmnt, DIR_PERMS);
6621 error = errno;
6622 INJECT_ERROR1("MKDIRP_SIGN_MNT", ret = -1);
6623 if (ret == -1) {
6624 bam_error(MKDIR_FAILED, tmpmnt, strerror(error));
6625 free_mnttab(mnttab_hash);
6626 (void) fclose(tfp);
6627 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6628 return (-1);
6631 dirp = opendir("/dev/rdsk");
6632 error = errno;
6633 INJECT_ERROR1("OPENDIR_DEV_RDSK", dirp = NULL);
6634 if (dirp == NULL) {
6635 bam_error(OPENDIR_FAILED, "/dev/rdsk", strerror(error));
6636 goto fail;
6639 while (dp = readdir(dirp)) {
6640 if (strcmp(dp->d_name, ".") == 0 ||
6641 strcmp(dp->d_name, "..") == 0)
6642 continue;
6645 * we only look for the s0 slice. This is guranteed to
6646 * have 's' at len - 2.
6648 len = strlen(dp->d_name);
6649 if (dp->d_name[len - 2 ] != 's' || dp->d_name[len - 1] != '0') {
6650 BAM_DPRINTF((D_SKIP_SLICE_NOTZERO, fcn, dp->d_name));
6651 continue;
6654 ret = process_slice0(dp->d_name, tfp, mnttab_hash, tmpmnt);
6655 INJECT_ERROR1("PROCESS_S0_FAIL", ret = -1);
6656 if (ret == -1)
6657 goto fail;
6660 (void) closedir(dirp);
6661 free_mnttab(mnttab_hash);
6662 (void) rmdir(tmpmnt);
6664 ret = fclose(tfp);
6665 error = errno;
6666 INJECT_ERROR1("FCLOSE_SIGNLIST_TMP", ret = EOF);
6667 if (ret == EOF) {
6668 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
6669 strerror(error));
6670 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6671 return (-1);
6674 /* We have a list of existing GRUB signatures. Sort it first */
6675 (void) snprintf(cmd, sizeof (cmd),
6676 "/usr/bin/sort -u %s.tmp > %s.sorted",
6677 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
6679 ret = exec_cmd(cmd, NULL);
6680 INJECT_ERROR1("SORT_SIGN_LIST", ret = 1);
6681 if (ret != 0) {
6682 bam_error(GRUBSIGN_SORT_FAILED);
6683 (void) unlink(UFS_SIGNATURE_LIST".sorted");
6684 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6685 return (-1);
6688 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6690 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
6691 error = errno;
6692 INJECT_ERROR1("RENAME_TMP_SIGNLIST", ret = -1);
6693 if (ret == -1) {
6694 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error));
6695 (void) unlink(UFS_SIGNATURE_LIST".sorted");
6696 return (-1);
6699 if (stat(UFS_SIGNATURE_LIST, &sb) == 0 && sb.st_size == 0) {
6700 BAM_DPRINTF((D_ZERO_LEN_SIGNLIST, fcn, UFS_SIGNATURE_LIST));
6703 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6704 return (0);
6706 fail:
6707 if (dirp)
6708 (void) closedir(dirp);
6709 free_mnttab(mnttab_hash);
6710 (void) rmdir(tmpmnt);
6711 (void) fclose(tfp);
6712 (void) unlink(UFS_SIGNATURE_LIST".tmp");
6713 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
6714 return (-1);
6717 static char *
6718 create_ufs_sign(void)
6720 struct stat sb;
6721 int signnum = -1;
6722 char tmpsign[MAXNAMELEN + 1];
6723 char *numstr;
6724 int i;
6725 FILE *tfp;
6726 int ret;
6727 int error;
6728 const char *fcn = "create_ufs_sign()";
6730 bam_print(SEARCHING_UFS_SIGN);
6732 ret = FindAllUfsSignatures();
6733 INJECT_ERROR1("FIND_ALL_UFS", ret = -1);
6734 if (ret == -1) {
6735 bam_error(ERR_FIND_UFS_SIGN);
6736 return (NULL);
6739 /* Make sure the list exists and is owned by root */
6740 INJECT_ERROR1("SIGNLIST_NOT_CREATED",
6741 (void) unlink(UFS_SIGNATURE_LIST));
6742 if (stat(UFS_SIGNATURE_LIST, &sb) == -1 || sb.st_uid != 0) {
6743 (void) unlink(UFS_SIGNATURE_LIST);
6744 bam_error(UFS_SIGNATURE_LIST_MISS, UFS_SIGNATURE_LIST);
6745 return (NULL);
6748 if (sb.st_size == 0) {
6749 bam_print(GRUBSIGN_UFS_NONE);
6750 i = 0;
6751 goto found;
6754 /* The signature list was sorted when it was created */
6755 tfp = fopen(UFS_SIGNATURE_LIST, "r");
6756 error = errno;
6757 INJECT_ERROR1("FOPEN_SIGN_LIST", tfp = NULL);
6758 if (tfp == NULL) {
6759 bam_error(UFS_SIGNATURE_LIST_OPENERR,
6760 UFS_SIGNATURE_LIST, strerror(error));
6761 (void) unlink(UFS_SIGNATURE_LIST);
6762 return (NULL);
6765 for (i = 0; s_fgets(tmpsign, sizeof (tmpsign), tfp); i++) {
6767 if (strncmp(tmpsign, GRUBSIGN_UFS_PREFIX,
6768 strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
6769 (void) fclose(tfp);
6770 (void) unlink(UFS_SIGNATURE_LIST);
6771 bam_error(UFS_BADSIGN, tmpsign);
6772 return (NULL);
6774 numstr = tmpsign + strlen(GRUBSIGN_UFS_PREFIX);
6776 if (numstr[0] == '\0' || !isdigit(numstr[0])) {
6777 (void) fclose(tfp);
6778 (void) unlink(UFS_SIGNATURE_LIST);
6779 bam_error(UFS_BADSIGN, tmpsign);
6780 return (NULL);
6783 signnum = atoi(numstr);
6784 INJECT_ERROR1("NEGATIVE_SIGN", signnum = -1);
6785 if (signnum < 0) {
6786 (void) fclose(tfp);
6787 (void) unlink(UFS_SIGNATURE_LIST);
6788 bam_error(UFS_BADSIGN, tmpsign);
6789 return (NULL);
6792 if (i != signnum) {
6793 BAM_DPRINTF((D_FOUND_HOLE_SIGNLIST, fcn, i));
6794 break;
6798 (void) fclose(tfp);
6800 found:
6801 (void) snprintf(tmpsign, sizeof (tmpsign), "rootfs%d", i);
6803 /* add the ufs signature to the /var/run list of signatures */
6804 ret = ufs_add_to_sign_list(tmpsign);
6805 INJECT_ERROR1("UFS_ADD_TO_SIGN_LIST", ret = -1);
6806 if (ret == -1) {
6807 (void) unlink(UFS_SIGNATURE_LIST);
6808 bam_error(FAILED_ADD_SIGNLIST, tmpsign);
6809 return (NULL);
6812 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6814 return (s_strdup(tmpsign));
6817 static char *
6818 get_fstype(char *osroot)
6820 FILE *mntfp;
6821 struct mnttab mp = {0};
6822 struct mnttab mpref = {0};
6823 int error;
6824 int ret;
6825 const char *fcn = "get_fstype()";
6827 INJECT_ERROR1("GET_FSTYPE_OSROOT", osroot = NULL);
6828 if (osroot == NULL) {
6829 bam_error(GET_FSTYPE_ARGS);
6830 return (NULL);
6833 mntfp = fopen(MNTTAB, "r");
6834 error = errno;
6835 INJECT_ERROR1("GET_FSTYPE_FOPEN", mntfp = NULL);
6836 if (mntfp == NULL) {
6837 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
6838 return (NULL);
6841 if (*osroot == '\0')
6842 mpref.mnt_mountp = "/";
6843 else
6844 mpref.mnt_mountp = osroot;
6846 ret = getmntany(mntfp, &mp, &mpref);
6847 INJECT_ERROR1("GET_FSTYPE_GETMNTANY", ret = 1);
6848 if (ret != 0) {
6849 bam_error(MNTTAB_MNTPT_NOT_FOUND, osroot, MNTTAB);
6850 (void) fclose(mntfp);
6851 return (NULL);
6853 (void) fclose(mntfp);
6855 INJECT_ERROR1("GET_FSTYPE_NULL", mp.mnt_fstype = NULL);
6856 if (mp.mnt_fstype == NULL) {
6857 bam_error(MNTTAB_FSTYPE_NULL, osroot);
6858 return (NULL);
6861 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6863 return (s_strdup(mp.mnt_fstype));
6866 static char *
6867 create_zfs_sign(char *osdev)
6869 char tmpsign[PATH_MAX];
6870 char *pool;
6871 const char *fcn = "create_zfs_sign()";
6873 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, osdev));
6876 * First find the pool name
6878 pool = get_pool(osdev);
6879 INJECT_ERROR1("CREATE_ZFS_SIGN_GET_POOL", pool = NULL);
6880 if (pool == NULL) {
6881 bam_error(GET_POOL_FAILED, osdev);
6882 return (NULL);
6885 (void) snprintf(tmpsign, sizeof (tmpsign), "pool_%s", pool);
6887 BAM_DPRINTF((D_CREATED_ZFS_SIGN, fcn, tmpsign));
6889 free(pool);
6891 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
6893 return (s_strdup(tmpsign));
6896 static char *
6897 create_new_sign(char *osdev, char *fstype)
6899 char *sign;
6900 const char *fcn = "create_new_sign()";
6902 INJECT_ERROR1("NEW_SIGN_FSTYPE", fstype = "foofs");
6904 if (strcmp(fstype, "zfs") == 0) {
6905 BAM_DPRINTF((D_CREATE_NEW_ZFS, fcn));
6906 sign = create_zfs_sign(osdev);
6907 } else if (strcmp(fstype, "ufs") == 0) {
6908 BAM_DPRINTF((D_CREATE_NEW_UFS, fcn));
6909 sign = create_ufs_sign();
6910 } else {
6911 bam_error(GRUBSIGN_NOTSUP, fstype);
6912 sign = NULL;
6915 BAM_DPRINTF((D_CREATED_NEW_SIGN, fcn, sign ? sign : "<NULL>"));
6916 return (sign);
6919 static int
6920 set_backup_common(char *mntpt, char *sign)
6922 FILE *bfp;
6923 char backup[PATH_MAX];
6924 char tmpsign[PATH_MAX];
6925 int error;
6926 char *bdir;
6927 char *backup_dup;
6928 struct stat sb;
6929 int ret;
6930 const char *fcn = "set_backup_common()";
6932 (void) snprintf(backup, sizeof (backup), "%s%s",
6933 mntpt, GRUBSIGN_BACKUP);
6935 /* First read the backup */
6936 bfp = fopen(backup, "r");
6937 if (bfp != NULL) {
6938 while (s_fgets(tmpsign, sizeof (tmpsign), bfp)) {
6939 if (strcmp(tmpsign, sign) == 0) {
6940 BAM_DPRINTF((D_FOUND_IN_BACKUP, fcn, sign));
6941 (void) fclose(bfp);
6942 return (0);
6945 (void) fclose(bfp);
6946 BAM_DPRINTF((D_NOT_FOUND_IN_EXIST_BACKUP, fcn, sign));
6947 } else {
6948 BAM_DPRINTF((D_BACKUP_NOT_EXIST, fcn, backup));
6952 * Didn't find the correct signature. First create
6953 * the directory if necessary.
6956 /* dirname() modifies its argument so dup it */
6957 backup_dup = s_strdup(backup);
6958 bdir = dirname(backup_dup);
6959 assert(bdir);
6961 ret = stat(bdir, &sb);
6962 INJECT_ERROR1("SET_BACKUP_STAT", ret = -1);
6963 if (ret == -1) {
6964 BAM_DPRINTF((D_BACKUP_DIR_NOEXIST, fcn, bdir));
6965 ret = mkdirp(bdir, DIR_PERMS);
6966 error = errno;
6967 INJECT_ERROR1("SET_BACKUP_MKDIRP", ret = -1);
6968 if (ret == -1) {
6969 bam_error(GRUBSIGN_BACKUP_MKDIRERR,
6970 GRUBSIGN_BACKUP, strerror(error));
6971 free(backup_dup);
6972 return (-1);
6975 free(backup_dup);
6978 * Open the backup in append mode to add the correct
6979 * signature;
6981 bfp = fopen(backup, "a");
6982 error = errno;
6983 INJECT_ERROR1("SET_BACKUP_FOPEN_A", bfp = NULL);
6984 if (bfp == NULL) {
6985 bam_error(GRUBSIGN_BACKUP_OPENERR,
6986 GRUBSIGN_BACKUP, strerror(error));
6987 return (-1);
6990 (void) snprintf(tmpsign, sizeof (tmpsign), "%s\n", sign);
6992 ret = fputs(tmpsign, bfp);
6993 error = errno;
6994 INJECT_ERROR1("SET_BACKUP_FPUTS", ret = 0);
6995 if (ret != strlen(tmpsign)) {
6996 bam_error(GRUBSIGN_BACKUP_WRITEERR,
6997 GRUBSIGN_BACKUP, strerror(error));
6998 (void) fclose(bfp);
6999 return (-1);
7002 (void) fclose(bfp);
7004 if (bam_verbose)
7005 bam_print(GRUBSIGN_BACKUP_UPDATED, GRUBSIGN_BACKUP);
7007 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7009 return (0);
7012 static int
7013 set_backup_ufs(char *osroot, char *sign)
7015 const char *fcn = "set_backup_ufs()";
7017 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign));
7018 return (set_backup_common(osroot, sign));
7021 static int
7022 set_backup_zfs(char *osdev, char *sign)
7024 char *pool;
7025 char *mntpt;
7026 zfs_mnted_t mnted;
7027 int ret;
7028 const char *fcn = "set_backup_zfs()";
7030 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign));
7032 pool = get_pool(osdev);
7033 INJECT_ERROR1("SET_BACKUP_GET_POOL", pool = NULL);
7034 if (pool == NULL) {
7035 bam_error(GET_POOL_FAILED, osdev);
7036 return (-1);
7039 mntpt = mount_top_dataset(pool, &mnted);
7040 INJECT_ERROR1("SET_BACKUP_MOUNT_DATASET", mntpt = NULL);
7041 if (mntpt == NULL) {
7042 bam_error(FAIL_MNT_TOP_DATASET, pool);
7043 free(pool);
7044 return (-1);
7047 ret = set_backup_common(mntpt, sign);
7049 (void) umount_top_dataset(pool, mnted, mntpt);
7051 free(pool);
7053 INJECT_ERROR1("SET_BACKUP_ZFS_FAIL", ret = 1);
7054 if (ret == 0) {
7055 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7056 } else {
7057 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7060 return (ret);
7063 static int
7064 set_backup(char *osroot, char *osdev, char *sign, char *fstype)
7066 const char *fcn = "set_backup()";
7067 int ret;
7069 INJECT_ERROR1("SET_BACKUP_FSTYPE", fstype = "foofs");
7071 if (strcmp(fstype, "ufs") == 0) {
7072 BAM_DPRINTF((D_SET_BACKUP_UFS, fcn));
7073 ret = set_backup_ufs(osroot, sign);
7074 } else if (strcmp(fstype, "zfs") == 0) {
7075 BAM_DPRINTF((D_SET_BACKUP_ZFS, fcn));
7076 ret = set_backup_zfs(osdev, sign);
7077 } else {
7078 bam_error(GRUBSIGN_NOTSUP, fstype);
7079 ret = -1;
7082 if (ret == 0) {
7083 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7084 } else {
7085 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7088 return (ret);
7091 static int
7092 set_primary_common(char *mntpt, char *sign)
7094 char signfile[PATH_MAX];
7095 char signdir[PATH_MAX];
7096 struct stat sb;
7097 int fd;
7098 int error;
7099 int ret;
7100 const char *fcn = "set_primary_common()";
7102 (void) snprintf(signfile, sizeof (signfile), "%s/%s/%s",
7103 mntpt, GRUBSIGN_DIR, sign);
7105 if (stat(signfile, &sb) != -1) {
7106 if (bam_verbose)
7107 bam_print(PRIMARY_SIGN_EXISTS, sign);
7108 return (0);
7109 } else {
7110 BAM_DPRINTF((D_PRIMARY_NOT_EXIST, fcn, signfile));
7113 (void) snprintf(signdir, sizeof (signdir), "%s/%s",
7114 mntpt, GRUBSIGN_DIR);
7116 if (stat(signdir, &sb) == -1) {
7117 BAM_DPRINTF((D_PRIMARY_DIR_NOEXIST, fcn, signdir));
7118 ret = mkdirp(signdir, DIR_PERMS);
7119 error = errno;
7120 INJECT_ERROR1("SET_PRIMARY_MKDIRP", ret = -1);
7121 if (ret == -1) {
7122 bam_error(GRUBSIGN_MKDIR_ERR, signdir, strerror(errno));
7123 return (-1);
7127 fd = open(signfile, O_RDWR|O_CREAT|O_TRUNC, 0444);
7128 error = errno;
7129 INJECT_ERROR1("PRIMARY_SIGN_CREAT", fd = -1);
7130 if (fd == -1) {
7131 bam_error(GRUBSIGN_PRIMARY_CREATERR, signfile, strerror(error));
7132 return (-1);
7135 ret = fsync(fd);
7136 error = errno;
7137 INJECT_ERROR1("PRIMARY_FSYNC", ret = -1);
7138 if (ret != 0) {
7139 bam_error(GRUBSIGN_PRIMARY_SYNCERR, signfile, strerror(error));
7142 (void) close(fd);
7144 if (bam_verbose)
7145 bam_print(GRUBSIGN_CREATED_PRIMARY, signfile);
7147 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7149 return (0);
7152 static int
7153 set_primary_ufs(char *osroot, char *sign)
7155 const char *fcn = "set_primary_ufs()";
7157 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, sign));
7158 return (set_primary_common(osroot, sign));
7161 static int
7162 set_primary_zfs(char *osdev, char *sign)
7164 char *pool;
7165 char *mntpt;
7166 zfs_mnted_t mnted;
7167 int ret;
7168 const char *fcn = "set_primary_zfs()";
7170 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osdev, sign));
7172 pool = get_pool(osdev);
7173 INJECT_ERROR1("SET_PRIMARY_ZFS_GET_POOL", pool = NULL);
7174 if (pool == NULL) {
7175 bam_error(GET_POOL_FAILED, osdev);
7176 return (-1);
7179 /* Pool name must exist in the sign */
7180 ret = (strstr(sign, pool) != NULL);
7181 INJECT_ERROR1("SET_PRIMARY_ZFS_POOL_SIGN_INCOMPAT", ret = 0);
7182 if (ret == 0) {
7183 bam_error(POOL_SIGN_INCOMPAT, pool, sign);
7184 free(pool);
7185 return (-1);
7188 mntpt = mount_top_dataset(pool, &mnted);
7189 INJECT_ERROR1("SET_PRIMARY_ZFS_MOUNT_DATASET", mntpt = NULL);
7190 if (mntpt == NULL) {
7191 bam_error(FAIL_MNT_TOP_DATASET, pool);
7192 free(pool);
7193 return (-1);
7196 ret = set_primary_common(mntpt, sign);
7198 (void) umount_top_dataset(pool, mnted, mntpt);
7200 free(pool);
7202 INJECT_ERROR1("SET_PRIMARY_ZFS_FAIL", ret = 1);
7203 if (ret == 0) {
7204 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7205 } else {
7206 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7209 return (ret);
7212 static int
7213 set_primary(char *osroot, char *osdev, char *sign, char *fstype)
7215 const char *fcn = "set_primary()";
7216 int ret;
7218 INJECT_ERROR1("SET_PRIMARY_FSTYPE", fstype = "foofs");
7219 if (strcmp(fstype, "ufs") == 0) {
7220 BAM_DPRINTF((D_SET_PRIMARY_UFS, fcn));
7221 ret = set_primary_ufs(osroot, sign);
7222 } else if (strcmp(fstype, "zfs") == 0) {
7223 BAM_DPRINTF((D_SET_PRIMARY_ZFS, fcn));
7224 ret = set_primary_zfs(osdev, sign);
7225 } else {
7226 bam_error(GRUBSIGN_NOTSUP, fstype);
7227 ret = -1;
7230 if (ret == 0) {
7231 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7232 } else {
7233 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7236 return (ret);
7239 static int
7240 ufs_add_to_sign_list(char *sign)
7242 FILE *tfp;
7243 char signline[MAXNAMELEN];
7244 char cmd[PATH_MAX];
7245 int ret;
7246 int error;
7247 const char *fcn = "ufs_add_to_sign_list()";
7249 INJECT_ERROR1("ADD_TO_SIGN_LIST_NOT_UFS", sign = "pool_rpool5");
7250 if (strncmp(sign, GRUBSIGN_UFS_PREFIX,
7251 strlen(GRUBSIGN_UFS_PREFIX)) != 0) {
7252 bam_error(INVALID_UFS_SIGN, sign);
7253 (void) unlink(UFS_SIGNATURE_LIST);
7254 return (-1);
7258 * most failures in this routine are not a fatal error
7259 * We simply unlink the /var/run file and continue
7262 ret = rename(UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST".tmp");
7263 error = errno;
7264 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME", ret = -1);
7265 if (ret == -1) {
7266 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST".tmp",
7267 strerror(error));
7268 (void) unlink(UFS_SIGNATURE_LIST);
7269 return (0);
7272 tfp = fopen(UFS_SIGNATURE_LIST".tmp", "a");
7273 error = errno;
7274 INJECT_ERROR1("ADD_TO_SIGN_LIST_FOPEN", tfp = NULL);
7275 if (tfp == NULL) {
7276 bam_error(OPEN_FAIL, UFS_SIGNATURE_LIST".tmp", strerror(error));
7277 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7278 return (0);
7281 (void) snprintf(signline, sizeof (signline), "%s\n", sign);
7283 ret = fputs(signline, tfp);
7284 error = errno;
7285 INJECT_ERROR1("ADD_TO_SIGN_LIST_FPUTS", ret = 0);
7286 if (ret != strlen(signline)) {
7287 bam_error(SIGN_LIST_FPUTS_ERR, sign, strerror(error));
7288 (void) fclose(tfp);
7289 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7290 return (0);
7293 ret = fclose(tfp);
7294 error = errno;
7295 INJECT_ERROR1("ADD_TO_SIGN_LIST_FCLOSE", ret = EOF);
7296 if (ret == EOF) {
7297 bam_error(CLOSE_FAIL, UFS_SIGNATURE_LIST".tmp",
7298 strerror(error));
7299 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7300 return (0);
7303 /* Sort the list again */
7304 (void) snprintf(cmd, sizeof (cmd),
7305 "/usr/bin/sort -u %s.tmp > %s.sorted",
7306 UFS_SIGNATURE_LIST, UFS_SIGNATURE_LIST);
7308 ret = exec_cmd(cmd, NULL);
7309 INJECT_ERROR1("ADD_TO_SIGN_LIST_SORT", ret = 1);
7310 if (ret != 0) {
7311 bam_error(GRUBSIGN_SORT_FAILED);
7312 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7313 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7314 return (0);
7317 (void) unlink(UFS_SIGNATURE_LIST".tmp");
7319 ret = rename(UFS_SIGNATURE_LIST".sorted", UFS_SIGNATURE_LIST);
7320 error = errno;
7321 INJECT_ERROR1("ADD_TO_SIGN_LIST_RENAME2", ret = -1);
7322 if (ret == -1) {
7323 bam_error(RENAME_FAIL, UFS_SIGNATURE_LIST, strerror(error));
7324 (void) unlink(UFS_SIGNATURE_LIST".sorted");
7325 return (0);
7328 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7330 return (0);
7333 static int
7334 set_signature(char *osroot, char *osdev, char *sign, char *fstype)
7336 int ret;
7337 const char *fcn = "set_signature()";
7339 BAM_DPRINTF((D_FUNC_ENTRY4, fcn, osroot, osdev, sign, fstype));
7341 ret = set_backup(osroot, osdev, sign, fstype);
7342 INJECT_ERROR1("SET_SIGNATURE_BACKUP", ret = -1);
7343 if (ret == -1) {
7344 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7345 bam_error(SET_BACKUP_FAILED, sign, osroot, osdev);
7346 return (-1);
7349 ret = set_primary(osroot, osdev, sign, fstype);
7350 INJECT_ERROR1("SET_SIGNATURE_PRIMARY", ret = -1);
7352 if (ret == 0) {
7353 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7354 } else {
7355 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7356 bam_error(SET_PRIMARY_FAILED, sign, osroot, osdev);
7359 return (ret);
7362 char *
7363 get_grubsign(char *osroot, char *osdev)
7365 char *grubsign; /* (<sign>,#,#) */
7366 char *slice;
7367 int fdiskpart;
7368 char *sign;
7369 char *fstype;
7370 int ret;
7371 const char *fcn = "get_grubsign()";
7373 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, osdev));
7374 fstype = get_fstype(osroot);
7375 INJECT_ERROR1("GET_GRUBSIGN_FSTYPE", fstype = NULL);
7376 if (fstype == NULL) {
7377 bam_error(GET_FSTYPE_FAILED, osroot);
7378 return (NULL);
7381 sign = find_existing_sign(osroot, osdev, fstype);
7382 INJECT_ERROR1("FIND_EXISTING_SIGN", sign = NULL);
7383 if (sign == NULL) {
7384 BAM_DPRINTF((D_GET_GRUBSIGN_NO_EXISTING, fcn, osroot, osdev));
7385 sign = create_new_sign(osdev, fstype);
7386 INJECT_ERROR1("CREATE_NEW_SIGN", sign = NULL);
7387 if (sign == NULL) {
7388 bam_error(GRUBSIGN_CREATE_FAIL, osdev);
7389 free(fstype);
7390 return (NULL);
7394 ret = set_signature(osroot, osdev, sign, fstype);
7395 INJECT_ERROR1("SET_SIGNATURE_FAIL", ret = -1);
7396 if (ret == -1) {
7397 bam_error(GRUBSIGN_WRITE_FAIL, osdev);
7398 free(sign);
7399 free(fstype);
7400 (void) unlink(UFS_SIGNATURE_LIST);
7401 return (NULL);
7404 free(fstype);
7406 if (bam_verbose)
7407 bam_print(GRUBSIGN_FOUND_OR_CREATED, sign, osdev);
7409 fdiskpart = get_partition(osdev);
7410 INJECT_ERROR1("GET_GRUBSIGN_FDISK", fdiskpart = PARTNO_NOTFOUND);
7411 if (fdiskpart == PARTNO_NOTFOUND) {
7412 bam_error(FDISKPART_FAIL, osdev);
7413 free(sign);
7414 return (NULL);
7417 slice = strrchr(osdev, 's');
7419 if (fdiskpart == PARTNO_EFI) {
7420 fdiskpart = atoi(&slice[1]);
7421 slice = NULL;
7424 grubsign = s_calloc(1, MAXNAMELEN + 10);
7425 if (slice) {
7426 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d,%c)",
7427 sign, fdiskpart, slice[1] + 'a' - '0');
7428 } else
7429 (void) snprintf(grubsign, MAXNAMELEN + 10, "(%s,%d)",
7430 sign, fdiskpart);
7432 free(sign);
7434 BAM_DPRINTF((D_GET_GRUBSIGN_SUCCESS, fcn, grubsign));
7436 return (grubsign);
7439 static char *
7440 get_title(char *rootdir)
7442 static char title[80];
7443 char *cp = NULL;
7444 char release[PATH_MAX];
7445 FILE *fp;
7446 const char *fcn = "get_title()";
7448 /* open the /etc/release file */
7449 (void) snprintf(release, sizeof (release), "%s/etc/release", rootdir);
7451 fp = fopen(release, "r");
7452 if (fp == NULL) {
7453 bam_error(OPEN_FAIL, release, strerror(errno));
7454 cp = NULL;
7455 goto out;
7458 /* grab first line of /etc/release */
7459 cp = s_fgets(title, sizeof (title), fp);
7460 if (cp) {
7461 while (isspace(*cp)) /* remove leading spaces */
7462 cp++;
7465 (void) fclose(fp);
7467 out:
7468 cp = cp ? cp : "Oracle Solaris";
7470 BAM_DPRINTF((D_GET_TITLE, fcn, cp));
7472 return (cp);
7475 char *
7476 get_special(char *mountp)
7478 FILE *mntfp;
7479 struct mnttab mp = {0};
7480 struct mnttab mpref = {0};
7481 int error;
7482 int ret;
7483 const char *fcn = "get_special()";
7485 INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL);
7486 if (mountp == NULL) {
7487 bam_error(GET_SPECIAL_NULL_MNTPT);
7488 return (NULL);
7491 mntfp = fopen(MNTTAB, "r");
7492 error = errno;
7493 INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL);
7494 if (mntfp == NULL) {
7495 bam_error(OPEN_FAIL, MNTTAB, strerror(error));
7496 return (NULL);
7499 if (*mountp == '\0')
7500 mpref.mnt_mountp = "/";
7501 else
7502 mpref.mnt_mountp = mountp;
7504 ret = getmntany(mntfp, &mp, &mpref);
7505 INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1);
7506 if (ret != 0) {
7507 (void) fclose(mntfp);
7508 BAM_DPRINTF((D_GET_SPECIAL_NOT_IN_MNTTAB, fcn, mountp));
7509 return (NULL);
7511 (void) fclose(mntfp);
7513 BAM_DPRINTF((D_GET_SPECIAL, fcn, mp.mnt_special));
7515 return (s_strdup(mp.mnt_special));
7518 static void
7519 free_physarray(char **physarray, int n)
7521 int i;
7522 const char *fcn = "free_physarray()";
7524 assert(physarray);
7525 assert(n);
7527 BAM_DPRINTF((D_FUNC_ENTRY_N1, fcn, n));
7529 for (i = 0; i < n; i++) {
7530 free(physarray[i]);
7532 free(physarray);
7534 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7537 static int
7538 zfs_get_physical(char *special, char ***physarray, int *n)
7540 char sdup[PATH_MAX];
7541 char cmd[PATH_MAX];
7542 char dsk[PATH_MAX];
7543 char *pool;
7544 filelist_t flist = {0};
7545 line_t *lp;
7546 line_t *startlp;
7547 char *comp1;
7548 int i;
7549 int ret;
7550 const char *fcn = "zfs_get_physical()";
7552 assert(special);
7554 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special));
7556 INJECT_ERROR1("INVALID_ZFS_SPECIAL", special = "/foo");
7557 if (special[0] == '/') {
7558 bam_error(INVALID_ZFS_SPECIAL, special);
7559 return (-1);
7562 (void) strlcpy(sdup, special, sizeof (sdup));
7564 pool = strtok(sdup, "/");
7565 INJECT_ERROR1("ZFS_GET_PHYS_POOL", pool = NULL);
7566 if (pool == NULL) {
7567 bam_error(CANT_FIND_POOL_FROM_SPECIAL, special);
7568 return (-1);
7571 (void) snprintf(cmd, sizeof (cmd), "/sbin/zpool status %s", pool);
7573 ret = exec_cmd(cmd, &flist);
7574 INJECT_ERROR1("ZFS_GET_PHYS_STATUS", ret = 1);
7575 if (ret != 0) {
7576 bam_error(ZFS_GET_POOL_STATUS, pool);
7577 return (-1);
7580 INJECT_ERROR1("ZFS_GET_PHYS_STATUS_OUT", flist.head = NULL);
7581 if (flist.head == NULL) {
7582 bam_error(BAD_ZPOOL_STATUS, pool);
7583 filelist_free(&flist);
7584 return (-1);
7587 for (lp = flist.head; lp; lp = lp->next) {
7588 BAM_DPRINTF((D_STRTOK_ZPOOL_STATUS, fcn, lp->line));
7589 comp1 = strtok(lp->line, " \t");
7590 if (comp1 == NULL) {
7591 free(lp->line);
7592 lp->line = NULL;
7593 } else {
7594 comp1 = s_strdup(comp1);
7595 free(lp->line);
7596 lp->line = comp1;
7600 for (lp = flist.head; lp; lp = lp->next) {
7601 if (lp->line == NULL)
7602 continue;
7603 if (strcmp(lp->line, pool) == 0) {
7604 BAM_DPRINTF((D_FOUND_POOL_IN_ZPOOL_STATUS, fcn, pool));
7605 break;
7609 if (lp == NULL) {
7610 bam_error(NO_POOL_IN_ZPOOL_STATUS, pool);
7611 filelist_free(&flist);
7612 return (-1);
7615 startlp = lp->next;
7616 for (i = 0, lp = startlp; lp; lp = lp->next) {
7617 if (lp->line == NULL)
7618 continue;
7619 if (strcmp(lp->line, "mirror") == 0)
7620 continue;
7621 if (lp->line[0] == '\0' || strcmp(lp->line, "errors:") == 0)
7622 break;
7623 i++;
7624 BAM_DPRINTF((D_COUNTING_ZFS_PHYS, fcn, i));
7627 if (i == 0) {
7628 bam_error(NO_PHYS_IN_ZPOOL_STATUS, pool);
7629 filelist_free(&flist);
7630 return (-1);
7633 *n = i;
7634 *physarray = s_calloc(*n, sizeof (char *));
7635 for (i = 0, lp = startlp; lp; lp = lp->next) {
7636 if (lp->line == NULL)
7637 continue;
7638 if (strcmp(lp->line, "mirror") == 0)
7639 continue;
7640 if (strcmp(lp->line, "errors:") == 0)
7641 break;
7642 if (strncmp(lp->line, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
7643 strncmp(lp->line, "/dev/rdsk/",
7644 strlen("/dev/rdsk/")) != 0) {
7645 (void) snprintf(dsk, sizeof (dsk), "/dev/rdsk/%s",
7646 lp->line);
7647 } else {
7648 (void) strlcpy(dsk, lp->line, sizeof (dsk));
7650 BAM_DPRINTF((D_ADDING_ZFS_PHYS, fcn, dsk, pool));
7651 (*physarray)[i++] = s_strdup(dsk);
7654 assert(i == *n);
7656 filelist_free(&flist);
7658 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7659 return (0);
7663 * Certain services needed to run metastat successfully may not
7664 * be enabled. Enable them now.
7667 * Checks if the specified service is online
7668 * Returns: 1 if the service is online
7669 * 0 if the service is not online
7670 * -1 on error
7672 static int
7673 is_svc_online(char *svc)
7675 char *state;
7676 const char *fcn = "is_svc_online()";
7678 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, svc));
7680 state = smf_get_state(svc);
7681 INJECT_ERROR2("GET_SVC_STATE", free(state), state = NULL);
7682 if (state == NULL) {
7683 bam_error(GET_SVC_STATE_ERR, svc);
7684 return (-1);
7686 BAM_DPRINTF((D_GOT_SVC_STATUS, fcn, svc));
7688 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0) {
7689 BAM_DPRINTF((D_SVC_ONLINE, fcn, svc));
7690 free(state);
7691 return (1);
7694 BAM_DPRINTF((D_SVC_NOT_ONLINE, fcn, state, svc));
7696 free(state);
7698 return (0);
7701 static int
7702 enable_svc(char *svc)
7704 int ret;
7705 int sleeptime;
7706 const char *fcn = "enable_svc()";
7708 ret = is_svc_online(svc);
7709 if (ret == -1) {
7710 bam_error(SVC_IS_ONLINE_FAILED, svc);
7711 return (-1);
7712 } else if (ret == 1) {
7713 BAM_DPRINTF((D_SVC_ALREADY_ONLINE, fcn, svc));
7714 return (0);
7717 /* Service is not enabled. Enable it now. */
7718 ret = smf_enable_instance(svc, 0);
7719 INJECT_ERROR1("ENABLE_SVC_FAILED", ret = -1);
7720 if (ret != 0) {
7721 bam_error(ENABLE_SVC_FAILED, svc);
7722 return (-1);
7725 BAM_DPRINTF((D_SVC_ONLINE_INITIATED, fcn, svc));
7727 sleeptime = 0;
7728 do {
7729 ret = is_svc_online(svc);
7730 INJECT_ERROR1("SVC_ONLINE_SUCCESS", ret = 1);
7731 INJECT_ERROR1("SVC_ONLINE_FAILURE", ret = -1);
7732 INJECT_ERROR1("SVC_ONLINE_NOTYET", ret = 0);
7733 if (ret == -1) {
7734 bam_error(ERR_SVC_GET_ONLINE, svc);
7735 return (-1);
7736 } else if (ret == 1) {
7737 BAM_DPRINTF((D_SVC_NOW_ONLINE, fcn, svc));
7738 return (1);
7740 (void) sleep(1);
7741 } while (++sleeptime < 60);
7743 bam_error(TIMEOUT_ENABLE_SVC, svc);
7745 return (-1);
7748 static int
7749 ufs_get_physical(char *special, char ***physarray, int *n)
7751 char cmd[PATH_MAX];
7752 char *shortname;
7753 filelist_t flist = {0};
7754 char *meta;
7755 char *type;
7756 char *comp1;
7757 char *comp2;
7758 char *comp3;
7759 char *comp4;
7760 int i;
7761 line_t *lp;
7762 int ret;
7763 char *svc;
7764 const char *fcn = "ufs_get_physical()";
7766 assert(special);
7768 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, special));
7770 if (strncmp(special, "/dev/md/", strlen("/dev/md/")) != 0) {
7771 bam_error(UFS_GET_PHYS_NOT_SVM, special);
7772 return (-1);
7775 if (strncmp(special, "/dev/md/dsk/", strlen("/dev/md/dsk/")) == 0) {
7776 shortname = special + strlen("/dev/md/dsk/");
7777 } else if (strncmp(special, "/dev/md/rdsk/",
7778 strlen("/dev/md/rdsk/")) == 0) {
7779 shortname = special + strlen("/dev/md/rdsk");
7780 } else {
7781 bam_error(UFS_GET_PHYS_INVALID_SVM, special);
7782 return (-1);
7785 BAM_DPRINTF((D_UFS_SVM_SHORT, fcn, special, shortname));
7787 svc = "network/rpc/meta:default";
7788 if (enable_svc(svc) == -1) {
7789 bam_error(UFS_SVM_METASTAT_SVC_ERR, svc);
7792 (void) snprintf(cmd, sizeof (cmd), "/sbin/metastat -p %s", shortname);
7794 ret = exec_cmd(cmd, &flist);
7795 INJECT_ERROR1("UFS_SVM_METASTAT", ret = 1);
7796 if (ret != 0) {
7797 bam_error(UFS_SVM_METASTAT_ERR, shortname);
7798 return (-1);
7801 INJECT_ERROR1("UFS_SVM_METASTAT_OUT", flist.head = NULL);
7802 if (flist.head == NULL) {
7803 bam_error(BAD_UFS_SVM_METASTAT, shortname);
7804 filelist_free(&flist);
7805 return (-1);
7809 * Check if not a mirror. We only parse a single metadevice
7810 * if not a mirror
7812 meta = strtok(flist.head->line, " \t");
7813 type = strtok(NULL, " \t");
7814 if (meta == NULL || type == NULL) {
7815 bam_error(ERROR_PARSE_UFS_SVM_METASTAT, shortname);
7816 filelist_free(&flist);
7817 return (-1);
7819 if (strcmp(type, "-m") != 0) {
7820 comp1 = strtok(NULL, " \t");
7821 comp2 = strtok(NULL, " \t");
7822 if (comp1 == NULL || comp2 != NULL) {
7823 bam_error(INVALID_UFS_SVM_METASTAT, shortname);
7824 filelist_free(&flist);
7825 return (-1);
7827 BAM_DPRINTF((D_UFS_SVM_ONE_COMP, fcn, comp1, shortname));
7828 *physarray = s_calloc(1, sizeof (char *));
7829 (*physarray)[0] = s_strdup(comp1);
7830 *n = 1;
7831 filelist_free(&flist);
7832 return (0);
7836 * Okay we have a mirror. Everything after the first line
7837 * is a submirror
7839 for (i = 0, lp = flist.head->next; lp; lp = lp->next) {
7840 if (strstr(lp->line, "/dev/dsk/") == NULL &&
7841 strstr(lp->line, "/dev/rdsk/") == NULL) {
7842 bam_error(CANNOT_PARSE_UFS_SVM_METASTAT, shortname);
7843 filelist_free(&flist);
7844 return (-1);
7846 i++;
7849 *physarray = s_calloc(i, sizeof (char *));
7850 *n = i;
7852 for (i = 0, lp = flist.head->next; lp; lp = lp->next) {
7853 comp1 = strtok(lp->line, " \t");
7854 comp2 = strtok(NULL, " \t");
7855 comp3 = strtok(NULL, " \t");
7856 comp4 = strtok(NULL, " \t");
7858 if (comp3 == NULL || comp4 == NULL ||
7859 (strncmp(comp4, "/dev/dsk/", strlen("/dev/dsk/")) != 0 &&
7860 strncmp(comp4, "/dev/rdsk/", strlen("/dev/rdsk/")) != 0)) {
7861 bam_error(CANNOT_PARSE_UFS_SVM_SUBMIRROR, shortname);
7862 filelist_free(&flist);
7863 free_physarray(*physarray, *n);
7864 return (-1);
7867 (*physarray)[i++] = s_strdup(comp4);
7870 assert(i == *n);
7872 filelist_free(&flist);
7874 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7875 return (0);
7878 static int
7879 get_physical(char *menu_root, char ***physarray, int *n)
7881 char *special;
7882 int ret;
7883 const char *fcn = "get_physical()";
7885 assert(menu_root);
7886 assert(physarray);
7887 assert(n);
7889 *physarray = NULL;
7890 *n = 0;
7892 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_root));
7894 /* First get the device special file from /etc/mnttab */
7895 special = get_special(menu_root);
7896 INJECT_ERROR1("GET_PHYSICAL_SPECIAL", special = NULL);
7897 if (special == NULL) {
7898 bam_error(GET_SPECIAL_NULL, menu_root);
7899 return (-1);
7902 /* If already a physical device nothing to do */
7903 if (strncmp(special, "/dev/dsk/", strlen("/dev/dsk/")) == 0 ||
7904 strncmp(special, "/dev/rdsk/", strlen("/dev/rdsk/")) == 0) {
7905 BAM_DPRINTF((D_GET_PHYSICAL_ALREADY, fcn, menu_root, special));
7906 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
7907 *physarray = s_calloc(1, sizeof (char *));
7908 (*physarray)[0] = special;
7909 *n = 1;
7910 return (0);
7913 if (is_zfs(menu_root)) {
7914 ret = zfs_get_physical(special, physarray, n);
7915 } else if (is_ufs(menu_root)) {
7916 ret = ufs_get_physical(special, physarray, n);
7917 } else {
7918 bam_error(GET_PHYSICAL_NOTSUP_FSTYPE, menu_root, special);
7919 ret = -1;
7922 free(special);
7924 INJECT_ERROR1("GET_PHYSICAL_RET", ret = -1);
7925 if (ret == -1) {
7926 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
7927 } else {
7928 int i;
7929 assert (*n > 0);
7930 for (i = 0; i < *n; i++) {
7931 BAM_DPRINTF((D_GET_PHYSICAL_RET, fcn, (*physarray)[i]));
7935 return (ret);
7938 static int
7939 is_bootdisk(char *osroot, char *physical)
7941 int ret;
7942 char *grubroot;
7943 char *bootp;
7944 const char *fcn = "is_bootdisk()";
7946 assert(osroot);
7947 assert(physical);
7949 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, physical));
7951 bootp = strstr(physical, "p0:boot");
7952 if (bootp)
7953 *bootp = '\0';
7955 * We just want the BIOS mapping for menu disk.
7956 * Don't pass menu_root to get_grubroot() as the
7957 * check that it is used for is not relevant here.
7958 * The osroot is immaterial as well - it is only used to
7959 * to find create_diskmap script. Everything hinges on
7960 * "physical"
7962 grubroot = get_grubroot(osroot, physical, NULL);
7964 INJECT_ERROR1("IS_BOOTDISK_GRUBROOT", grubroot = NULL);
7965 if (grubroot == NULL) {
7966 if (bam_verbose)
7967 bam_error(NO_GRUBROOT_FOR_DISK, physical);
7968 return (0);
7970 ret = grubroot[3] == '0';
7971 free(grubroot);
7973 BAM_DPRINTF((D_RETURN_RET, fcn, ret));
7975 return (ret);
7979 * Check if menu is on the boot device
7980 * Return 0 (false) on error
7982 static int
7983 menu_on_bootdisk(char *osroot, char *menu_root)
7985 char **physarray;
7986 int ret;
7987 int n;
7988 int i;
7989 int on_bootdisk;
7990 const char *fcn = "menu_on_bootdisk()";
7992 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
7994 ret = get_physical(menu_root, &physarray, &n);
7995 INJECT_ERROR1("MENU_ON_BOOTDISK_PHYSICAL", ret = -1);
7996 if (ret != 0) {
7997 bam_error(GET_PHYSICAL_MENU_NULL, menu_root);
7998 return (0);
8001 assert(physarray);
8002 assert(n > 0);
8004 on_bootdisk = 0;
8005 for (i = 0; i < n; i++) {
8006 assert(strncmp(physarray[i], "/dev/dsk/",
8007 strlen("/dev/dsk/")) == 0 ||
8008 strncmp(physarray[i], "/dev/rdsk/",
8009 strlen("/dev/rdsk/")) == 0);
8011 BAM_DPRINTF((D_CHECK_ON_BOOTDISK, fcn, physarray[i]));
8012 if (is_bootdisk(osroot, physarray[i])) {
8013 on_bootdisk = 1;
8014 BAM_DPRINTF((D_IS_ON_BOOTDISK, fcn, physarray[i]));
8018 free_physarray(physarray, n);
8020 INJECT_ERROR1("ON_BOOTDISK_YES", on_bootdisk = 1);
8021 INJECT_ERROR1("ON_BOOTDISK_NO", on_bootdisk = 0);
8022 if (on_bootdisk) {
8023 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8024 } else {
8025 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
8028 return (on_bootdisk);
8031 void
8032 bam_add_line(menu_t *mp, entry_t *entry, line_t *prev, line_t *lp)
8034 const char *fcn = "bam_add_line()";
8036 assert(mp);
8037 assert(entry);
8038 assert(prev);
8039 assert(lp);
8041 lp->next = prev->next;
8042 if (prev->next) {
8043 BAM_DPRINTF((D_ADD_LINE_PREV_NEXT, fcn));
8044 prev->next->prev = lp;
8045 } else {
8046 BAM_DPRINTF((D_ADD_LINE_NOT_PREV_NEXT, fcn));
8048 prev->next = lp;
8049 lp->prev = prev;
8051 if (entry->end == prev) {
8052 BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_ENTRY, fcn));
8053 entry->end = lp;
8055 if (mp->end == prev) {
8056 assert(lp->next == NULL);
8057 mp->end = lp;
8058 BAM_DPRINTF((D_ADD_LINE_LAST_LINE_IN_MENU, fcn));
8063 * look for matching bootadm entry with specified parameters
8064 * Here are the rules (based on existing usage):
8065 * - If title is specified, match on title only
8066 * - Else, match on root/findroot, kernel, and module.
8067 * Note that, if root_opt is non-zero, the absence of
8068 * root line is considered a match.
8070 static entry_t *
8071 find_boot_entry(
8072 menu_t *mp,
8073 char *title,
8074 char *kernel,
8075 char *findroot,
8076 char *root,
8077 char *module,
8078 int root_opt,
8079 int *entry_num)
8081 int i;
8082 line_t *lp;
8083 entry_t *ent;
8084 const char *fcn = "find_boot_entry()";
8086 if (entry_num)
8087 *entry_num = BAM_ERROR;
8089 /* find matching entry */
8090 for (i = 0, ent = mp->entries; ent; i++, ent = ent->next) {
8091 lp = ent->start;
8093 /* first line of entry must be bootadm comment */
8094 lp = ent->start;
8095 if (lp->flags != BAM_COMMENT ||
8096 strcmp(lp->arg, BAM_BOOTADM_HDR) != 0) {
8097 continue;
8100 /* advance to title line */
8101 lp = lp->next;
8102 if (title) {
8103 if (lp->flags == BAM_TITLE && lp->arg &&
8104 strcmp(lp->arg, title) == 0) {
8105 BAM_DPRINTF((D_MATCHED_TITLE, fcn, title));
8106 break;
8108 BAM_DPRINTF((D_NOMATCH_TITLE, fcn, title, lp->arg));
8109 continue; /* check title only */
8112 lp = lp->next; /* advance to root line */
8113 if (lp == NULL) {
8114 continue;
8115 } else if (lp->cmd != NULL &&
8116 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) == 0) {
8117 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_FINDROOT",
8118 findroot = NULL);
8119 if (findroot == NULL) {
8120 BAM_DPRINTF((D_NOMATCH_FINDROOT_NULL,
8121 fcn, lp->arg));
8122 continue;
8124 /* findroot command found, try match */
8125 if (strcmp(lp->arg, findroot) != 0) {
8126 BAM_DPRINTF((D_NOMATCH_FINDROOT,
8127 fcn, findroot, lp->arg));
8128 continue;
8130 BAM_DPRINTF((D_MATCHED_FINDROOT, fcn, findroot));
8131 lp = lp->next; /* advance to kernel line */
8132 } else if (lp->cmd != NULL &&
8133 strcmp(lp->cmd, menu_cmds[ROOT_CMD]) == 0) {
8134 INJECT_ERROR1("FIND_BOOT_ENTRY_NULL_ROOT", root = NULL);
8135 if (root == NULL) {
8136 BAM_DPRINTF((D_NOMATCH_ROOT_NULL,
8137 fcn, lp->arg));
8138 continue;
8140 /* root cmd found, try match */
8141 if (strcmp(lp->arg, root) != 0) {
8142 BAM_DPRINTF((D_NOMATCH_ROOT,
8143 fcn, root, lp->arg));
8144 continue;
8146 BAM_DPRINTF((D_MATCHED_ROOT, fcn, root));
8147 lp = lp->next; /* advance to kernel line */
8148 } else {
8149 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_NO",
8150 root_opt = 0);
8151 INJECT_ERROR1("FIND_BOOT_ENTRY_ROOT_OPT_YES",
8152 root_opt = 1);
8153 /* no root command, see if root is optional */
8154 if (root_opt == 0) {
8155 BAM_DPRINTF((D_NO_ROOT_OPT, fcn));
8156 continue;
8158 BAM_DPRINTF((D_ROOT_OPT, fcn));
8161 if (lp == NULL || lp->next == NULL) {
8162 continue;
8165 if (kernel &&
8166 (!check_cmd(lp->cmd, KERNEL_CMD, lp->arg, kernel))) {
8167 if (!(ent->flags & BAM_ENTRY_FAILSAFE) ||
8168 !(ent->flags & BAM_ENTRY_DBOOT) ||
8169 strcmp(kernel, DIRECT_BOOT_FAILSAFE_LINE) != 0)
8170 continue;
8172 ent->flags |= BAM_ENTRY_UPGFSKERNEL;
8175 BAM_DPRINTF((D_KERNEL_MATCH, fcn, kernel, lp->arg));
8178 * Check for matching module entry (failsafe or normal).
8179 * If it fails to match, we go around the loop again.
8180 * For xpv entries, there are two module lines, so we
8181 * do the check twice.
8183 lp = lp->next; /* advance to module line */
8184 if (check_cmd(lp->cmd, MODULE_CMD, lp->arg, module) ||
8185 (((lp = lp->next) != NULL) &&
8186 check_cmd(lp->cmd, MODULE_CMD, lp->arg, module))) {
8187 /* match found */
8188 BAM_DPRINTF((D_MODULE_MATCH, fcn, module, lp->arg));
8189 break;
8192 if (strcmp(module, FAILSAFE_ARCHIVE) == 0 &&
8193 (strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_32) == 0 ||
8194 strcmp(lp->prev->arg, FAILSAFE_ARCHIVE_64) == 0)) {
8195 ent->flags |= BAM_ENTRY_UPGFSMODULE;
8196 break;
8201 if (ent && entry_num) {
8202 *entry_num = i;
8205 if (ent) {
8206 BAM_DPRINTF((D_RETURN_RET, fcn, i));
8207 } else {
8208 BAM_DPRINTF((D_RETURN_RET, fcn, BAM_ERROR));
8210 return (ent);
8213 static int
8214 update_boot_entry(menu_t *mp, char *title, char *findroot, char *root,
8215 char *kernel, char *mod_kernel, char *module, int root_opt)
8217 int i;
8218 int change_kernel = 0;
8219 entry_t *ent;
8220 line_t *lp;
8221 line_t *tlp;
8222 char linebuf[BAM_MAXLINE];
8223 const char *fcn = "update_boot_entry()";
8225 /* note: don't match on title, it's updated on upgrade */
8226 ent = find_boot_entry(mp, NULL, kernel, findroot, root, module,
8227 root_opt, &i);
8228 if ((ent == NULL) && (bam_direct == BAM_DIRECT_DBOOT)) {
8230 * We may be upgrading a kernel from multiboot to
8231 * directboot. Look for a multiboot entry. A multiboot
8232 * entry will not have a findroot line.
8234 ent = find_boot_entry(mp, NULL, "multiboot", NULL, root,
8235 MULTIBOOT_ARCHIVE, root_opt, &i);
8236 if (ent != NULL) {
8237 BAM_DPRINTF((D_UPGRADE_FROM_MULTIBOOT, fcn, root));
8238 change_kernel = 1;
8240 } else if (ent) {
8241 BAM_DPRINTF((D_FOUND_FINDROOT, fcn, findroot));
8244 if (ent == NULL) {
8245 BAM_DPRINTF((D_ENTRY_NOT_FOUND_CREATING, fcn, findroot));
8246 return (add_boot_entry(mp, title, findroot,
8247 kernel, mod_kernel, module, NULL));
8250 /* replace title of existing entry and update findroot line */
8251 lp = ent->start;
8252 lp = lp->next; /* title line */
8253 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8254 menu_cmds[TITLE_CMD], menu_cmds[SEP_CMD], title);
8255 free(lp->arg);
8256 free(lp->line);
8257 lp->arg = s_strdup(title);
8258 lp->line = s_strdup(linebuf);
8259 BAM_DPRINTF((D_CHANGING_TITLE, fcn, title));
8261 tlp = lp; /* title line */
8262 lp = lp->next; /* root line */
8264 /* if no root or findroot command, create a new line_t */
8265 if ((lp->cmd != NULL) && (strcmp(lp->cmd, menu_cmds[ROOT_CMD]) != 0 &&
8266 strcmp(lp->cmd, menu_cmds[FINDROOT_CMD]) != 0)) {
8267 lp = s_calloc(1, sizeof (line_t));
8268 bam_add_line(mp, ent, tlp, lp);
8269 } else {
8270 if (lp->cmd != NULL)
8271 free(lp->cmd);
8273 free(lp->sep);
8274 free(lp->arg);
8275 free(lp->line);
8278 lp->cmd = s_strdup(menu_cmds[FINDROOT_CMD]);
8279 lp->sep = s_strdup(menu_cmds[SEP_CMD]);
8280 lp->arg = s_strdup(findroot);
8281 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8282 menu_cmds[FINDROOT_CMD], menu_cmds[SEP_CMD], findroot);
8283 lp->line = s_strdup(linebuf);
8284 BAM_DPRINTF((D_ADDING_FINDROOT_LINE, fcn, findroot));
8286 /* kernel line */
8287 lp = lp->next;
8289 if (ent->flags & BAM_ENTRY_UPGFSKERNEL) {
8290 char *params = NULL;
8292 params = strstr(lp->line, "-s");
8293 if (params != NULL)
8294 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s%s",
8295 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8296 kernel, params+2);
8297 else
8298 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8299 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8300 kernel);
8302 if (lp->cmd != NULL)
8303 free(lp->cmd);
8305 free(lp->arg);
8306 free(lp->line);
8307 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8308 lp->arg = s_strdup(strstr(linebuf, "/"));
8309 lp->line = s_strdup(linebuf);
8310 ent->flags &= ~BAM_ENTRY_UPGFSKERNEL;
8311 BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, lp->prev->cmd));
8314 if (change_kernel) {
8316 * We're upgrading from multiboot to directboot.
8318 if (lp->cmd != NULL &&
8319 strcmp(lp->cmd, menu_cmds[KERNEL_CMD]) == 0) {
8320 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8321 menu_cmds[KERNEL_DOLLAR_CMD], menu_cmds[SEP_CMD],
8322 kernel);
8323 free(lp->cmd);
8324 free(lp->arg);
8325 free(lp->line);
8326 lp->cmd = s_strdup(menu_cmds[KERNEL_DOLLAR_CMD]);
8327 lp->arg = s_strdup(kernel);
8328 lp->line = s_strdup(linebuf);
8329 lp = lp->next;
8330 BAM_DPRINTF((D_ADDING_KERNEL_DOLLAR, fcn, kernel));
8332 if (lp->cmd != NULL &&
8333 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8334 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8335 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8336 module);
8337 free(lp->cmd);
8338 free(lp->arg);
8339 free(lp->line);
8340 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8341 lp->arg = s_strdup(module);
8342 lp->line = s_strdup(linebuf);
8343 lp = lp->next;
8344 BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module));
8348 /* module line */
8349 lp = lp->next;
8351 if (ent->flags & BAM_ENTRY_UPGFSMODULE) {
8352 if (lp->cmd != NULL &&
8353 strcmp(lp->cmd, menu_cmds[MODULE_CMD]) == 0) {
8354 (void) snprintf(linebuf, sizeof (linebuf), "%s%s%s",
8355 menu_cmds[MODULE_DOLLAR_CMD], menu_cmds[SEP_CMD],
8356 module);
8357 free(lp->cmd);
8358 free(lp->arg);
8359 free(lp->line);
8360 lp->cmd = s_strdup(menu_cmds[MODULE_DOLLAR_CMD]);
8361 lp->arg = s_strdup(module);
8362 lp->line = s_strdup(linebuf);
8363 lp = lp->next;
8364 ent->flags &= ~BAM_ENTRY_UPGFSMODULE;
8365 BAM_DPRINTF((D_ADDING_MODULE_DOLLAR, fcn, module));
8369 BAM_DPRINTF((D_RETURN_RET, fcn, i));
8370 return (i);
8374 root_optional(char *osroot, char *menu_root)
8376 char *ospecial;
8377 char *mspecial;
8378 char *slash;
8379 int root_opt;
8380 int ret1;
8381 int ret2;
8382 const char *fcn = "root_optional()";
8384 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, osroot, menu_root));
8387 * For all filesystems except ZFS, a straight compare of osroot
8388 * and menu_root will tell us if root is optional.
8389 * For ZFS, the situation is complicated by the fact that
8390 * menu_root and osroot are always different
8392 ret1 = is_zfs(osroot);
8393 ret2 = is_zfs(menu_root);
8394 INJECT_ERROR1("ROOT_OPT_NOT_ZFS", ret1 = 0);
8395 if (!ret1 || !ret2) {
8396 BAM_DPRINTF((D_ROOT_OPT_NOT_ZFS, fcn, osroot, menu_root));
8397 root_opt = (strcmp(osroot, menu_root) == 0);
8398 goto out;
8401 ospecial = get_special(osroot);
8402 INJECT_ERROR1("ROOT_OPTIONAL_OSPECIAL", ospecial = NULL);
8403 if (ospecial == NULL) {
8404 bam_error(GET_OSROOT_SPECIAL_ERR, osroot);
8405 return (0);
8407 BAM_DPRINTF((D_ROOT_OPTIONAL_OSPECIAL, fcn, ospecial, osroot));
8409 mspecial = get_special(menu_root);
8410 INJECT_ERROR1("ROOT_OPTIONAL_MSPECIAL", mspecial = NULL);
8411 if (mspecial == NULL) {
8412 bam_error(GET_MENU_ROOT_SPECIAL_ERR, menu_root);
8413 free(ospecial);
8414 return (0);
8416 BAM_DPRINTF((D_ROOT_OPTIONAL_MSPECIAL, fcn, mspecial, menu_root));
8418 slash = strchr(ospecial, '/');
8419 if (slash)
8420 *slash = '\0';
8421 BAM_DPRINTF((D_ROOT_OPTIONAL_FIXED_OSPECIAL, fcn, ospecial, osroot));
8423 root_opt = (strcmp(ospecial, mspecial) == 0);
8425 free(ospecial);
8426 free(mspecial);
8428 out:
8429 INJECT_ERROR1("ROOT_OPTIONAL_NO", root_opt = 0);
8430 INJECT_ERROR1("ROOT_OPTIONAL_YES", root_opt = 1);
8431 if (root_opt) {
8432 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8433 } else {
8434 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
8437 return (root_opt);
8440 /*ARGSUSED*/
8441 static error_t
8442 update_entry(menu_t *mp, char *menu_root, char *osdev)
8444 int entry;
8445 char *grubsign;
8446 char *grubroot;
8447 char *title;
8448 char osroot[PATH_MAX];
8449 char *failsafe_kernel = NULL;
8450 struct stat sbuf;
8451 char failsafe[256];
8452 char failsafe_64[256];
8453 int ret;
8454 const char *fcn = "update_entry()";
8456 assert(mp);
8457 assert(menu_root);
8458 assert(osdev);
8459 assert(bam_root);
8461 BAM_DPRINTF((D_FUNC_ENTRY3, fcn, menu_root, osdev, bam_root));
8463 (void) strlcpy(osroot, bam_root, sizeof (osroot));
8465 title = get_title(osroot);
8466 assert(title);
8468 grubsign = get_grubsign(osroot, osdev);
8469 INJECT_ERROR1("GET_GRUBSIGN_FAIL", grubsign = NULL);
8470 if (grubsign == NULL) {
8471 bam_error(GET_GRUBSIGN_ERROR, osroot, osdev);
8472 return (BAM_ERROR);
8476 * It is not a fatal error if get_grubroot() fails
8477 * We no longer rely on biosdev to populate the
8478 * menu
8480 grubroot = get_grubroot(osroot, osdev, menu_root);
8481 INJECT_ERROR1("GET_GRUBROOT_FAIL", grubroot = NULL);
8482 if (grubroot) {
8483 BAM_DPRINTF((D_GET_GRUBROOT_SUCCESS,
8484 fcn, osroot, osdev, menu_root));
8485 } else {
8486 BAM_DPRINTF((D_GET_GRUBROOT_FAILURE,
8487 fcn, osroot, osdev, menu_root));
8490 /* add the entry for normal Solaris */
8491 INJECT_ERROR1("UPDATE_ENTRY_MULTIBOOT",
8492 bam_direct = BAM_DIRECT_MULTIBOOT);
8493 if (bam_direct == BAM_DIRECT_DBOOT) {
8494 entry = update_boot_entry(mp, title, grubsign, grubroot,
8495 (bam_zfs ? DIRECT_BOOT_KERNEL_ZFS : DIRECT_BOOT_KERNEL),
8496 NULL, DIRECT_BOOT_ARCHIVE,
8497 root_optional(osroot, menu_root));
8498 BAM_DPRINTF((D_UPDATED_BOOT_ENTRY, fcn, bam_zfs, grubsign));
8499 if ((entry != BAM_ERROR) && (bam_is_hv == BAM_HV_PRESENT)) {
8500 (void) update_boot_entry(mp, NEW_HV_ENTRY, grubsign,
8501 grubroot, XEN_MENU, bam_zfs ?
8502 XEN_KERNEL_MODULE_LINE_ZFS : XEN_KERNEL_MODULE_LINE,
8503 DIRECT_BOOT_ARCHIVE,
8504 root_optional(osroot, menu_root));
8505 BAM_DPRINTF((D_UPDATED_HV_ENTRY,
8506 fcn, bam_zfs, grubsign));
8508 } else {
8509 entry = update_boot_entry(mp, title, grubsign, grubroot,
8510 MULTI_BOOT, NULL, MULTIBOOT_ARCHIVE,
8511 root_optional(osroot, menu_root));
8513 BAM_DPRINTF((D_UPDATED_MULTIBOOT_ENTRY, fcn, grubsign));
8517 * Add the entry for failsafe archive. On a bfu'd system, the
8518 * failsafe may be different than the installed kernel.
8520 (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8521 osroot, FAILSAFE_ARCHIVE_32);
8522 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8523 osroot, FAILSAFE_ARCHIVE_64);
8526 * Check if at least one of the two archives exists
8527 * Using $ISADIR as the default line, we have an entry which works
8528 * for both the cases.
8531 if (stat(failsafe, &sbuf) == 0 || stat(failsafe_64, &sbuf) == 0) {
8533 /* Figure out where the kernel line should point */
8534 (void) snprintf(failsafe, sizeof (failsafe), "%s%s", osroot,
8535 DIRECT_BOOT_FAILSAFE_32);
8536 (void) snprintf(failsafe_64, sizeof (failsafe_64), "%s%s",
8537 osroot, DIRECT_BOOT_FAILSAFE_64);
8538 if (stat(failsafe, &sbuf) == 0 ||
8539 stat(failsafe_64, &sbuf) == 0) {
8540 failsafe_kernel = DIRECT_BOOT_FAILSAFE_LINE;
8541 } else {
8542 (void) snprintf(failsafe, sizeof (failsafe), "%s%s",
8543 osroot, MULTI_BOOT_FAILSAFE);
8544 if (stat(failsafe, &sbuf) == 0) {
8545 failsafe_kernel = MULTI_BOOT_FAILSAFE_LINE;
8548 if (failsafe_kernel != NULL) {
8549 (void) update_boot_entry(mp, FAILSAFE_TITLE, grubsign,
8550 grubroot, failsafe_kernel, NULL, FAILSAFE_ARCHIVE,
8551 root_optional(osroot, menu_root));
8552 BAM_DPRINTF((D_UPDATED_FAILSAFE_ENTRY, fcn,
8553 failsafe_kernel));
8556 free(grubroot);
8558 INJECT_ERROR1("UPDATE_ENTRY_ERROR", entry = BAM_ERROR);
8559 if (entry == BAM_ERROR) {
8560 bam_error(FAILED_TO_ADD_BOOT_ENTRY, title, grubsign);
8561 free(grubsign);
8562 return (BAM_ERROR);
8564 free(grubsign);
8566 update_numbering(mp);
8567 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8568 INJECT_ERROR1("SET_DEFAULT_ERROR", ret = BAM_ERROR);
8569 if (ret == BAM_ERROR) {
8570 bam_error(SET_DEFAULT_FAILED, entry);
8572 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8573 return (BAM_WRITE);
8576 static void
8577 save_default_entry(menu_t *mp, const char *which)
8579 int lineNum;
8580 int entryNum;
8581 int entry = 0; /* default is 0 */
8582 char linebuf[BAM_MAXLINE];
8583 line_t *lp = mp->curdefault;
8584 const char *fcn = "save_default_entry()";
8586 if (mp->start) {
8587 lineNum = mp->end->lineNum;
8588 entryNum = mp->end->entryNum;
8589 } else {
8590 lineNum = LINE_INIT;
8591 entryNum = ENTRY_INIT;
8594 if (lp)
8595 entry = s_strtol(lp->arg);
8597 (void) snprintf(linebuf, sizeof (linebuf), "#%s%d", which, entry);
8598 BAM_DPRINTF((D_SAVING_DEFAULT_TO, fcn, linebuf));
8599 line_parser(mp, linebuf, &lineNum, &entryNum);
8600 BAM_DPRINTF((D_SAVED_DEFAULT_TO, fcn, lineNum, entryNum));
8603 static void
8604 restore_default_entry(menu_t *mp, const char *which, line_t *lp)
8606 int entry;
8607 char *str;
8608 const char *fcn = "restore_default_entry()";
8610 if (lp == NULL) {
8611 BAM_DPRINTF((D_RESTORE_DEFAULT_NULL, fcn));
8612 return; /* nothing to restore */
8615 BAM_DPRINTF((D_RESTORE_DEFAULT_STR, fcn, which));
8617 str = lp->arg + strlen(which);
8618 entry = s_strtol(str);
8619 (void) set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8621 BAM_DPRINTF((D_RESTORED_DEFAULT_TO, fcn, entry));
8623 /* delete saved old default line */
8624 unlink_line(mp, lp);
8625 line_free(lp);
8629 * This function is for supporting reboot with args.
8630 * The opt value can be:
8631 * NULL delete temp entry, if present
8632 * entry=<n> switches default entry to <n>
8633 * else treated as boot-args and setup a temperary menu entry
8634 * and make it the default
8635 * Note that we are always rebooting the current OS instance
8636 * so osroot == / always.
8638 #define REBOOT_TITLE "Solaris_reboot_transient"
8640 /*ARGSUSED*/
8641 static error_t
8642 update_temp(menu_t *mp, char *dummy, char *opt)
8644 int entry;
8645 char *osdev;
8646 char *fstype;
8647 char *sign;
8648 char *opt_ptr;
8649 char *path;
8650 char kernbuf[BUFSIZ];
8651 char args_buf[BUFSIZ];
8652 char signbuf[PATH_MAX];
8653 int ret;
8654 const char *fcn = "update_temp()";
8656 assert(mp);
8657 assert(dummy == NULL);
8659 /* opt can be NULL */
8660 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt ? opt : "<NULL>"));
8661 BAM_DPRINTF((D_BAM_ROOT, fcn, bam_alt_root, bam_root));
8663 if (bam_alt_root || bam_rootlen != 1 ||
8664 strcmp(bam_root, "/") != 0 ||
8665 strcmp(rootbuf, "/") != 0) {
8666 bam_error(ALT_ROOT_INVALID, bam_root);
8667 return (BAM_ERROR);
8670 /* If no option, delete exiting reboot menu entry */
8671 if (opt == NULL) {
8672 entry_t *ent;
8673 BAM_DPRINTF((D_OPT_NULL, fcn));
8674 ent = find_boot_entry(mp, REBOOT_TITLE, NULL, NULL,
8675 NULL, NULL, 0, &entry);
8676 if (ent == NULL) { /* not found is ok */
8677 BAM_DPRINTF((D_TRANSIENT_NOTFOUND, fcn));
8678 return (BAM_SUCCESS);
8680 (void) delete_boot_entry(mp, entry, DBE_PRINTERR);
8681 restore_default_entry(mp, BAM_OLDDEF, mp->olddefault);
8682 mp->olddefault = NULL;
8683 BAM_DPRINTF((D_RESTORED_DEFAULT, fcn));
8684 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8685 return (BAM_WRITE);
8688 /* if entry= is specified, set the default entry */
8689 if (strncmp(opt, "entry=", strlen("entry=")) == 0) {
8690 int entryNum = s_strtol(opt + strlen("entry="));
8691 BAM_DPRINTF((D_ENTRY_EQUALS, fcn, opt));
8692 if (selector(mp, opt, &entry, NULL) == BAM_SUCCESS) {
8693 /* this is entry=# option */
8694 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8695 BAM_DPRINTF((D_ENTRY_SET_IS, fcn, entry, ret));
8696 return (ret);
8697 } else {
8698 bam_error(SET_DEFAULT_FAILED, entryNum);
8699 return (BAM_ERROR);
8704 * add a new menu entry based on opt and make it the default
8707 fstype = get_fstype("/");
8708 INJECT_ERROR1("REBOOT_FSTYPE_NULL", fstype = NULL);
8709 if (fstype == NULL) {
8710 bam_error(REBOOT_FSTYPE_FAILED);
8711 return (BAM_ERROR);
8714 osdev = get_special("/");
8715 INJECT_ERROR1("REBOOT_SPECIAL_NULL", osdev = NULL);
8716 if (osdev == NULL) {
8717 free(fstype);
8718 bam_error(REBOOT_SPECIAL_FAILED);
8719 return (BAM_ERROR);
8722 sign = find_existing_sign("/", osdev, fstype);
8723 INJECT_ERROR1("REBOOT_SIGN_NULL", sign = NULL);
8724 if (sign == NULL) {
8725 free(fstype);
8726 free(osdev);
8727 bam_error(REBOOT_SIGN_FAILED);
8728 return (BAM_ERROR);
8731 free(osdev);
8732 (void) strlcpy(signbuf, sign, sizeof (signbuf));
8733 free(sign);
8735 assert(strchr(signbuf, '(') == NULL && strchr(signbuf, ',') == NULL &&
8736 strchr(signbuf, ')') == NULL);
8739 * There is no alternate root while doing reboot with args
8740 * This version of bootadm is only delivered with a DBOOT
8741 * version of Solaris.
8743 INJECT_ERROR1("REBOOT_NOT_DBOOT", bam_direct = BAM_DIRECT_MULTIBOOT);
8744 if (bam_direct != BAM_DIRECT_DBOOT) {
8745 free(fstype);
8746 bam_error(REBOOT_DIRECT_FAILED);
8747 return (BAM_ERROR);
8750 /* add an entry for Solaris reboot */
8751 if (opt[0] == '-') {
8752 /* It's an option - first see if boot-file is set */
8753 ret = get_kernel(mp, KERNEL_CMD, kernbuf, sizeof (kernbuf));
8754 INJECT_ERROR1("REBOOT_GET_KERNEL", ret = BAM_ERROR);
8755 if (ret != BAM_SUCCESS) {
8756 free(fstype);
8757 bam_error(REBOOT_GET_KERNEL_FAILED);
8758 return (BAM_ERROR);
8760 if (kernbuf[0] == '\0')
8761 (void) strlcpy(kernbuf, DIRECT_BOOT_KERNEL,
8762 sizeof (kernbuf));
8764 * If this is a zfs file system and kernbuf does not
8765 * have "-B $ZFS-BOOTFS" string yet, add it.
8767 if (strcmp(fstype, "zfs") == 0 && !strstr(kernbuf, ZFS_BOOT)) {
8768 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8769 (void) strlcat(kernbuf, ZFS_BOOT, sizeof (kernbuf));
8771 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8772 (void) strlcat(kernbuf, opt, sizeof (kernbuf));
8773 BAM_DPRINTF((D_REBOOT_OPTION, fcn, kernbuf));
8774 } else if (opt[0] == '/') {
8775 /* It's a full path, so write it out. */
8776 (void) strlcpy(kernbuf, opt, sizeof (kernbuf));
8779 * If someone runs:
8781 * # eeprom boot-args='-kd'
8782 * # reboot /platform/i86pc/kernel/unix
8784 * we want to use the boot-args as part of the boot
8785 * line. On the other hand, if someone runs:
8787 * # reboot "/platform/i86pc/kernel/unix -kd"
8789 * we don't need to mess with boot-args. If there's
8790 * no space in the options string, assume we're in the
8791 * first case.
8793 if (strchr(opt, ' ') == NULL) {
8794 ret = get_kernel(mp, ARGS_CMD, args_buf,
8795 sizeof (args_buf));
8796 INJECT_ERROR1("REBOOT_GET_ARGS", ret = BAM_ERROR);
8797 if (ret != BAM_SUCCESS) {
8798 free(fstype);
8799 bam_error(REBOOT_GET_ARGS_FAILED);
8800 return (BAM_ERROR);
8803 if (args_buf[0] != '\0') {
8804 (void) strlcat(kernbuf, " ", sizeof (kernbuf));
8805 (void) strlcat(kernbuf, args_buf,
8806 sizeof (kernbuf));
8809 BAM_DPRINTF((D_REBOOT_ABSPATH, fcn, kernbuf));
8810 } else {
8812 * It may be a partial path, or it may be a partial
8813 * path followed by options. Assume that only options
8814 * follow a space. If someone sends us a kernel path
8815 * that includes a space, they deserve to be broken.
8817 opt_ptr = strchr(opt, ' ');
8818 if (opt_ptr != NULL) {
8819 *opt_ptr = '\0';
8822 path = expand_path(opt);
8823 if (path != NULL) {
8824 (void) strlcpy(kernbuf, path, sizeof (kernbuf));
8825 free(path);
8828 * If there were options given, use those.
8829 * Otherwise, copy over the default options.
8831 if (opt_ptr != NULL) {
8832 /* Restore the space in opt string */
8833 *opt_ptr = ' ';
8834 (void) strlcat(kernbuf, opt_ptr,
8835 sizeof (kernbuf));
8836 } else {
8837 ret = get_kernel(mp, ARGS_CMD, args_buf,
8838 sizeof (args_buf));
8839 INJECT_ERROR1("UPDATE_TEMP_PARTIAL_ARGS",
8840 ret = BAM_ERROR);
8841 if (ret != BAM_SUCCESS) {
8842 free(fstype);
8843 bam_error(REBOOT_GET_ARGS_FAILED);
8844 return (BAM_ERROR);
8847 if (args_buf[0] != '\0') {
8848 (void) strlcat(kernbuf, " ",
8849 sizeof (kernbuf));
8850 (void) strlcat(kernbuf,
8851 args_buf, sizeof (kernbuf));
8854 BAM_DPRINTF((D_REBOOT_RESOLVED_PARTIAL, fcn, kernbuf));
8855 } else {
8856 free(fstype);
8857 bam_error(UNKNOWN_KERNEL, opt);
8858 bam_print_stderr(UNKNOWN_KERNEL_REBOOT);
8859 return (BAM_ERROR);
8862 free(fstype);
8863 entry = add_boot_entry(mp, REBOOT_TITLE, signbuf, kernbuf,
8864 NULL, NULL, NULL);
8865 INJECT_ERROR1("REBOOT_ADD_BOOT_ENTRY", entry = BAM_ERROR);
8866 if (entry == BAM_ERROR) {
8867 bam_error(REBOOT_WITH_ARGS_ADD_ENTRY_FAILED);
8868 return (BAM_ERROR);
8871 save_default_entry(mp, BAM_OLDDEF);
8872 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entry);
8873 INJECT_ERROR1("REBOOT_SET_GLOBAL", ret = BAM_ERROR);
8874 if (ret == BAM_ERROR) {
8875 bam_error(REBOOT_SET_DEFAULT_FAILED, entry);
8877 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8878 return (BAM_WRITE);
8881 error_t
8882 set_global(menu_t *mp, char *globalcmd, int val)
8884 line_t *lp;
8885 line_t *found;
8886 line_t *last;
8887 char *cp;
8888 char *str;
8889 char prefix[BAM_MAXLINE];
8890 size_t len;
8891 const char *fcn = "set_global()";
8893 assert(mp);
8894 assert(globalcmd);
8896 if (strcmp(globalcmd, menu_cmds[DEFAULT_CMD]) == 0) {
8897 INJECT_ERROR1("SET_GLOBAL_VAL_NEG", val = -1);
8898 INJECT_ERROR1("SET_GLOBAL_MENU_EMPTY", mp->end = NULL);
8899 INJECT_ERROR1("SET_GLOBAL_VAL_TOO_BIG", val = 100);
8900 if (val < 0 || mp->end == NULL || val > mp->end->entryNum) {
8901 (void) snprintf(prefix, sizeof (prefix), "%d", val);
8902 bam_error(INVALID_ENTRY, prefix);
8903 return (BAM_ERROR);
8907 found = last = NULL;
8908 for (lp = mp->start; lp; lp = lp->next) {
8909 if (lp->flags != BAM_GLOBAL)
8910 continue;
8912 last = lp; /* track the last global found */
8914 INJECT_ERROR1("SET_GLOBAL_NULL_CMD", lp->cmd = NULL);
8915 if (lp->cmd == NULL) {
8916 bam_error(NO_CMD, lp->lineNum);
8917 continue;
8919 if (strcmp(globalcmd, lp->cmd) != 0)
8920 continue;
8922 BAM_DPRINTF((D_FOUND_GLOBAL, fcn, globalcmd));
8924 if (found) {
8925 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
8927 found = lp;
8930 if (found == NULL) {
8931 lp = s_calloc(1, sizeof (line_t));
8932 if (last == NULL) {
8933 lp->next = mp->start;
8934 mp->start = lp;
8935 mp->end = (mp->end) ? mp->end : lp;
8936 } else {
8937 lp->next = last->next;
8938 last->next = lp;
8939 if (lp->next == NULL)
8940 mp->end = lp;
8942 lp->flags = BAM_GLOBAL; /* other fields not needed for writes */
8943 len = strlen(globalcmd) + strlen(menu_cmds[SEP_CMD]);
8944 len += 10; /* val < 10 digits */
8945 lp->line = s_calloc(1, len);
8946 (void) snprintf(lp->line, len, "%s%s%d",
8947 globalcmd, menu_cmds[SEP_CMD], val);
8948 BAM_DPRINTF((D_SET_GLOBAL_WROTE_NEW, fcn, lp->line));
8949 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8950 return (BAM_WRITE);
8954 * We are changing an existing entry. Retain any prefix whitespace,
8955 * but overwrite everything else. This preserves tabs added for
8956 * readability.
8958 str = found->line;
8959 cp = prefix;
8960 while (*str == ' ' || *str == '\t')
8961 *(cp++) = *(str++);
8962 *cp = '\0'; /* Terminate prefix */
8963 len = strlen(prefix) + strlen(globalcmd);
8964 len += strlen(menu_cmds[SEP_CMD]) + 10;
8966 free(found->line);
8967 found->line = s_calloc(1, len);
8968 (void) snprintf(found->line, len,
8969 "%s%s%s%d", prefix, globalcmd, menu_cmds[SEP_CMD], val);
8971 BAM_DPRINTF((D_SET_GLOBAL_REPLACED, fcn, found->line));
8972 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
8973 return (BAM_WRITE); /* need a write to menu */
8977 * partial_path may be anything like "kernel/unix" or "kmdb". Try to
8978 * expand it to a full unix path. The calling function is expected to
8979 * output a message if an error occurs and NULL is returned.
8981 static char *
8982 expand_path(const char *partial_path)
8984 int new_path_len;
8985 char *new_path;
8986 char new_path2[PATH_MAX];
8987 struct stat sb;
8988 const char *fcn = "expand_path()";
8990 new_path_len = strlen(partial_path) + 64;
8991 new_path = s_calloc(1, new_path_len);
8993 /* First, try the simplest case - something like "kernel/unix" */
8994 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s",
8995 partial_path);
8996 if (stat(new_path, &sb) == 0) {
8997 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
8998 return (new_path);
9001 if (strcmp(partial_path, "kmdb") == 0) {
9002 (void) snprintf(new_path, new_path_len, "%s -k",
9003 DIRECT_BOOT_KERNEL);
9004 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
9005 return (new_path);
9009 * We've quickly reached unsupported usage. Try once more to
9010 * see if we were just given a glom name.
9012 (void) snprintf(new_path, new_path_len, "/platform/i86pc/%s/unix",
9013 partial_path);
9014 (void) snprintf(new_path2, PATH_MAX, "/platform/i86pc/%s/amd64/unix",
9015 partial_path);
9016 if (stat(new_path, &sb) == 0) {
9017 if (stat(new_path2, &sb) == 0) {
9019 * We matched both, so we actually
9020 * want to write the $ISADIR version.
9022 (void) snprintf(new_path, new_path_len,
9023 "/platform/i86pc/kernel/%s/$ISADIR/unix",
9024 partial_path);
9026 BAM_DPRINTF((D_EXPAND_PATH, fcn, new_path));
9027 return (new_path);
9030 free(new_path);
9031 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
9032 return (NULL);
9036 * The kernel cmd and arg have been changed, so
9037 * check whether the archive line needs to change.
9039 static void
9040 set_archive_line(entry_t *entryp, line_t *kernelp)
9042 line_t *lp = entryp->start;
9043 char *new_archive;
9044 menu_cmd_t m_cmd;
9045 const char *fcn = "set_archive_line()";
9047 for (; lp != NULL; lp = lp->next) {
9048 if (lp->cmd != NULL && strncmp(lp->cmd, menu_cmds[MODULE_CMD],
9049 sizeof (menu_cmds[MODULE_CMD]) - 1) == 0) {
9050 break;
9053 INJECT_ERROR1("SET_ARCHIVE_LINE_END_ENTRY", lp = entryp->end);
9054 if (lp == entryp->end) {
9055 BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn,
9056 entryp->entryNum));
9057 return;
9060 INJECT_ERROR1("SET_ARCHIVE_LINE_END_MENU", lp = NULL);
9061 if (lp == NULL) {
9062 BAM_DPRINTF((D_ARCHIVE_LINE_NONE, fcn, entryp->entryNum));
9063 return;
9066 if (strstr(kernelp->arg, "$ISADIR") != NULL) {
9067 new_archive = DIRECT_BOOT_ARCHIVE;
9068 m_cmd = MODULE_DOLLAR_CMD;
9069 } else if (strstr(kernelp->arg, "amd64") != NULL) {
9070 new_archive = DIRECT_BOOT_ARCHIVE_64;
9071 m_cmd = MODULE_CMD;
9072 } else {
9073 new_archive = DIRECT_BOOT_ARCHIVE_32;
9074 m_cmd = MODULE_CMD;
9077 if (strcmp(lp->arg, new_archive) == 0) {
9078 BAM_DPRINTF((D_ARCHIVE_LINE_NOCHANGE, fcn, lp->arg));
9079 return;
9082 if (lp->cmd != NULL && strcmp(lp->cmd, menu_cmds[m_cmd]) != 0) {
9083 free(lp->cmd);
9084 lp->cmd = s_strdup(menu_cmds[m_cmd]);
9087 free(lp->arg);
9088 lp->arg = s_strdup(new_archive);
9089 update_line(lp);
9090 BAM_DPRINTF((D_ARCHIVE_LINE_REPLACED, fcn, lp->line));
9094 * Title for an entry to set properties that once went in bootenv.rc.
9096 #define BOOTENV_RC_TITLE "Solaris bootenv rc"
9099 * If path is NULL, return the kernel (optnum == KERNEL_CMD) or arguments
9100 * (optnum == ARGS_CMD) in the argument buf. If path is a zero-length
9101 * string, reset the value to the default. If path is a non-zero-length
9102 * string, set the kernel or arguments.
9104 static error_t
9105 get_set_kernel(
9106 menu_t *mp,
9107 menu_cmd_t optnum,
9108 char *path,
9109 char *buf,
9110 size_t bufsize)
9112 int entryNum;
9113 int rv = BAM_SUCCESS;
9114 int free_new_path = 0;
9115 entry_t *entryp;
9116 line_t *ptr;
9117 line_t *kernelp;
9118 char *new_arg;
9119 char *old_args;
9120 char *space;
9121 char *new_path;
9122 char old_space;
9123 size_t old_kernel_len;
9124 size_t new_str_len;
9125 char *fstype;
9126 char *osdev;
9127 char *sign;
9128 char signbuf[PATH_MAX];
9129 int ret;
9130 const char *fcn = "get_set_kernel()";
9132 assert(bufsize > 0);
9134 ptr = kernelp = NULL;
9135 new_arg = old_args = space = NULL;
9136 new_path = NULL;
9137 buf[0] = '\0';
9139 INJECT_ERROR1("GET_SET_KERNEL_NOT_DBOOT",
9140 bam_direct = BAM_DIRECT_MULTIBOOT);
9141 if (bam_direct != BAM_DIRECT_DBOOT) {
9142 bam_error(NOT_DBOOT, optnum == KERNEL_CMD ? "kernel" : "args");
9143 return (BAM_ERROR);
9147 * If a user changed the default entry to a non-bootadm controlled
9148 * one, we don't want to mess with it. Just print an error and
9149 * return.
9151 if (mp->curdefault) {
9152 entryNum = s_strtol(mp->curdefault->arg);
9153 for (entryp = mp->entries; entryp; entryp = entryp->next) {
9154 if (entryp->entryNum == entryNum)
9155 break;
9157 if ((entryp != NULL) &&
9158 ((entryp->flags & (BAM_ENTRY_BOOTADM|BAM_ENTRY_LU)) == 0)) {
9159 bam_error(DEFAULT_NOT_BAM);
9160 return (BAM_ERROR);
9164 entryp = find_boot_entry(mp, BOOTENV_RC_TITLE, NULL, NULL, NULL, NULL,
9165 0, &entryNum);
9167 if (entryp != NULL) {
9168 for (ptr = entryp->start; ptr && ptr != entryp->end;
9169 ptr = ptr->next) {
9170 if (strncmp(ptr->cmd, menu_cmds[KERNEL_CMD],
9171 sizeof (menu_cmds[KERNEL_CMD]) - 1) == 0) {
9172 kernelp = ptr;
9173 break;
9176 if (kernelp == NULL) {
9177 bam_error(NO_KERNEL, entryNum);
9178 return (BAM_ERROR);
9181 old_kernel_len = strcspn(kernelp->arg, " \t");
9182 space = old_args = kernelp->arg + old_kernel_len;
9183 while ((*old_args == ' ') || (*old_args == '\t'))
9184 old_args++;
9187 if (path == NULL) {
9188 if (entryp == NULL) {
9189 BAM_DPRINTF((D_GET_SET_KERNEL_NO_RC, fcn));
9190 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9191 return (BAM_SUCCESS);
9193 assert(kernelp);
9194 if (optnum == ARGS_CMD) {
9195 if (old_args[0] != '\0') {
9196 (void) strlcpy(buf, old_args, bufsize);
9197 BAM_DPRINTF((D_GET_SET_KERNEL_ARGS, fcn, buf));
9199 } else {
9201 * We need to print the kernel, so we just turn the
9202 * first space into a '\0' and print the beginning.
9203 * We don't print anything if it's the default kernel.
9205 old_space = *space;
9206 *space = '\0';
9207 if (strcmp(kernelp->arg, DIRECT_BOOT_KERNEL) != 0) {
9208 (void) strlcpy(buf, kernelp->arg, bufsize);
9209 BAM_DPRINTF((D_GET_SET_KERNEL_KERN, fcn, buf));
9211 *space = old_space;
9213 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9214 return (BAM_SUCCESS);
9218 * First, check if we're resetting an entry to the default.
9220 if ((path[0] == '\0') ||
9221 ((optnum == KERNEL_CMD) &&
9222 (strcmp(path, DIRECT_BOOT_KERNEL) == 0))) {
9223 if ((entryp == NULL) || (kernelp == NULL)) {
9224 /* No previous entry, it's already the default */
9225 BAM_DPRINTF((D_GET_SET_KERNEL_ALREADY, fcn));
9226 return (BAM_SUCCESS);
9230 * Check if we can delete the entry. If we're resetting the
9231 * kernel command, and the args is already empty, or if we're
9232 * resetting the args command, and the kernel is already the
9233 * default, we can restore the old default and delete the entry.
9235 if (((optnum == KERNEL_CMD) &&
9236 ((old_args == NULL) || (old_args[0] == '\0'))) ||
9237 ((optnum == ARGS_CMD) &&
9238 (strncmp(kernelp->arg, DIRECT_BOOT_KERNEL,
9239 sizeof (DIRECT_BOOT_KERNEL) - 1) == 0))) {
9240 kernelp = NULL;
9241 (void) delete_boot_entry(mp, entryNum, DBE_PRINTERR);
9242 restore_default_entry(mp, BAM_OLD_RC_DEF,
9243 mp->old_rc_default);
9244 mp->old_rc_default = NULL;
9245 rv = BAM_WRITE;
9246 BAM_DPRINTF((D_GET_SET_KERNEL_RESTORE_DEFAULT, fcn));
9247 goto done;
9250 if (optnum == KERNEL_CMD) {
9252 * At this point, we've already checked that old_args
9253 * and entryp are valid pointers. The "+ 2" is for
9254 * a space a the string termination character.
9256 new_str_len = (sizeof (DIRECT_BOOT_KERNEL) - 1) +
9257 strlen(old_args) + 2;
9258 new_arg = s_calloc(1, new_str_len);
9259 (void) snprintf(new_arg, new_str_len, "%s %s",
9260 DIRECT_BOOT_KERNEL, old_args);
9261 free(kernelp->arg);
9262 kernelp->arg = new_arg;
9265 * We have changed the kernel line, so we may need
9266 * to update the archive line as well.
9268 set_archive_line(entryp, kernelp);
9269 BAM_DPRINTF((D_GET_SET_KERNEL_RESET_KERNEL_SET_ARG,
9270 fcn, kernelp->arg));
9271 } else {
9273 * We're resetting the boot args to nothing, so
9274 * we only need to copy the kernel. We've already
9275 * checked that the kernel is not the default.
9277 new_arg = s_calloc(1, old_kernel_len + 1);
9278 (void) snprintf(new_arg, old_kernel_len + 1, "%s",
9279 kernelp->arg);
9280 free(kernelp->arg);
9281 kernelp->arg = new_arg;
9282 BAM_DPRINTF((D_GET_SET_KERNEL_RESET_ARG_SET_KERNEL,
9283 fcn, kernelp->arg));
9285 rv = BAM_WRITE;
9286 goto done;
9290 * Expand the kernel file to a full path, if necessary
9292 if ((optnum == KERNEL_CMD) && (path[0] != '/')) {
9293 new_path = expand_path(path);
9294 if (new_path == NULL) {
9295 bam_error(UNKNOWN_KERNEL, path);
9296 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
9297 return (BAM_ERROR);
9299 free_new_path = 1;
9300 } else {
9301 new_path = path;
9302 free_new_path = 0;
9306 * At this point, we know we're setting a new value. First, take care
9307 * of the case where there was no previous entry.
9309 if (entryp == NULL) {
9311 /* Similar to code in update_temp */
9312 fstype = get_fstype("/");
9313 INJECT_ERROR1("GET_SET_KERNEL_FSTYPE", fstype = NULL);
9314 if (fstype == NULL) {
9315 bam_error(BOOTENV_FSTYPE_FAILED);
9316 rv = BAM_ERROR;
9317 goto done;
9320 osdev = get_special("/");
9321 INJECT_ERROR1("GET_SET_KERNEL_SPECIAL", osdev = NULL);
9322 if (osdev == NULL) {
9323 free(fstype);
9324 bam_error(BOOTENV_SPECIAL_FAILED);
9325 rv = BAM_ERROR;
9326 goto done;
9329 sign = find_existing_sign("/", osdev, fstype);
9330 INJECT_ERROR1("GET_SET_KERNEL_SIGN", sign = NULL);
9331 if (sign == NULL) {
9332 free(fstype);
9333 free(osdev);
9334 bam_error(BOOTENV_SIGN_FAILED);
9335 rv = BAM_ERROR;
9336 goto done;
9339 free(osdev);
9340 (void) strlcpy(signbuf, sign, sizeof (signbuf));
9341 free(sign);
9342 assert(strchr(signbuf, '(') == NULL &&
9343 strchr(signbuf, ',') == NULL &&
9344 strchr(signbuf, ')') == NULL);
9346 if (optnum == KERNEL_CMD) {
9347 if (strcmp(fstype, "zfs") == 0) {
9348 new_str_len = strlen(new_path) +
9349 strlen(ZFS_BOOT) + 8;
9350 new_arg = s_calloc(1, new_str_len);
9351 (void) snprintf(new_arg, new_str_len, "%s %s",
9352 new_path, ZFS_BOOT);
9353 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn,
9354 new_arg));
9355 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9356 signbuf, new_arg, NULL, NULL, NULL);
9357 free(new_arg);
9358 } else {
9359 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_KERN, fcn,
9360 new_path));
9361 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9362 signbuf, new_path, NULL, NULL, NULL);
9364 } else {
9365 new_str_len = strlen(path) + 8;
9366 if (strcmp(fstype, "zfs") == 0) {
9367 new_str_len += strlen(DIRECT_BOOT_KERNEL_ZFS);
9368 new_arg = s_calloc(1, new_str_len);
9369 (void) snprintf(new_arg, new_str_len, "%s %s",
9370 DIRECT_BOOT_KERNEL_ZFS, path);
9371 } else {
9372 new_str_len += strlen(DIRECT_BOOT_KERNEL);
9373 new_arg = s_calloc(1, new_str_len);
9374 (void) snprintf(new_arg, new_str_len, "%s %s",
9375 DIRECT_BOOT_KERNEL, path);
9378 BAM_DPRINTF((D_GET_SET_KERNEL_NEW_ARG, fcn, new_arg));
9379 entryNum = add_boot_entry(mp, BOOTENV_RC_TITLE,
9380 signbuf, new_arg, NULL, DIRECT_BOOT_ARCHIVE, NULL);
9381 free(new_arg);
9383 free(fstype);
9384 INJECT_ERROR1("GET_SET_KERNEL_ADD_BOOT_ENTRY",
9385 entryNum = BAM_ERROR);
9386 if (entryNum == BAM_ERROR) {
9387 bam_error(GET_SET_KERNEL_ADD_BOOT_ENTRY,
9388 BOOTENV_RC_TITLE);
9389 rv = BAM_ERROR;
9390 goto done;
9392 save_default_entry(mp, BAM_OLD_RC_DEF);
9393 ret = set_global(mp, menu_cmds[DEFAULT_CMD], entryNum);
9394 INJECT_ERROR1("GET_SET_KERNEL_SET_GLOBAL", ret = BAM_ERROR);
9395 if (ret == BAM_ERROR) {
9396 bam_error(GET_SET_KERNEL_SET_GLOBAL, entryNum);
9398 rv = BAM_WRITE;
9399 goto done;
9403 * There was already an bootenv entry which we need to edit.
9405 if (optnum == KERNEL_CMD) {
9406 new_str_len = strlen(new_path) + strlen(old_args) + 2;
9407 new_arg = s_calloc(1, new_str_len);
9408 (void) snprintf(new_arg, new_str_len, "%s %s", new_path,
9409 old_args);
9410 free(kernelp->arg);
9411 kernelp->arg = new_arg;
9414 * If we have changed the kernel line, we may need to update
9415 * the archive line as well.
9417 set_archive_line(entryp, kernelp);
9418 BAM_DPRINTF((D_GET_SET_KERNEL_REPLACED_KERNEL_SAME_ARG, fcn,
9419 kernelp->arg));
9420 } else {
9421 new_str_len = old_kernel_len + strlen(path) + 8;
9422 new_arg = s_calloc(1, new_str_len);
9423 (void) strncpy(new_arg, kernelp->arg, old_kernel_len);
9424 (void) strlcat(new_arg, " ", new_str_len);
9425 (void) strlcat(new_arg, path, new_str_len);
9426 free(kernelp->arg);
9427 kernelp->arg = new_arg;
9428 BAM_DPRINTF((D_GET_SET_KERNEL_SAME_KERNEL_REPLACED_ARG, fcn,
9429 kernelp->arg));
9431 rv = BAM_WRITE;
9433 done:
9434 if ((rv == BAM_WRITE) && kernelp)
9435 update_line(kernelp);
9436 if (free_new_path)
9437 free(new_path);
9438 if (rv == BAM_WRITE) {
9439 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9440 } else {
9441 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
9443 return (rv);
9446 static error_t
9447 get_kernel(menu_t *mp, menu_cmd_t optnum, char *buf, size_t bufsize)
9449 const char *fcn = "get_kernel()";
9450 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, menu_cmds[optnum]));
9451 return (get_set_kernel(mp, optnum, NULL, buf, bufsize));
9454 static error_t
9455 set_kernel(menu_t *mp, menu_cmd_t optnum, char *path, char *buf, size_t bufsize)
9457 const char *fcn = "set_kernel()";
9458 assert(path != NULL);
9459 BAM_DPRINTF((D_FUNC_ENTRY2, fcn, menu_cmds[optnum], path));
9460 return (get_set_kernel(mp, optnum, path, buf, bufsize));
9463 /*ARGSUSED*/
9464 static error_t
9465 set_option(menu_t *mp, char *dummy, char *opt)
9467 int optnum;
9468 int optval;
9469 char *val;
9470 char buf[BUFSIZ] = "";
9471 error_t rv;
9472 const char *fcn = "set_option()";
9474 assert(mp);
9475 assert(opt);
9476 assert(dummy == NULL);
9478 /* opt is set from bam_argv[0] and is always non-NULL */
9479 BAM_DPRINTF((D_FUNC_ENTRY1, fcn, opt));
9481 val = strchr(opt, '=');
9482 if (val != NULL) {
9483 *val = '\0';
9486 if (strcmp(opt, "default") == 0) {
9487 optnum = DEFAULT_CMD;
9488 } else if (strcmp(opt, "timeout") == 0) {
9489 optnum = TIMEOUT_CMD;
9490 } else if (strcmp(opt, menu_cmds[KERNEL_CMD]) == 0) {
9491 optnum = KERNEL_CMD;
9492 } else if (strcmp(opt, menu_cmds[ARGS_CMD]) == 0) {
9493 optnum = ARGS_CMD;
9494 } else {
9495 bam_error(INVALID_OPTION, opt);
9496 return (BAM_ERROR);
9500 * kernel and args are allowed without "=new_value" strings. All
9501 * others cause errors
9503 if ((val == NULL) && (optnum != KERNEL_CMD) && (optnum != ARGS_CMD)) {
9504 bam_error(NO_OPTION_ARG, opt);
9505 return (BAM_ERROR);
9506 } else if (val != NULL) {
9507 *val = '=';
9510 if ((optnum == KERNEL_CMD) || (optnum == ARGS_CMD)) {
9511 BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum],
9512 val ? val + 1 : "NULL"));
9514 if (val)
9515 rv = set_kernel(mp, optnum, val + 1, buf, sizeof (buf));
9516 else
9517 rv = get_kernel(mp, optnum, buf, sizeof (buf));
9518 if ((rv == BAM_SUCCESS) && (buf[0] != '\0'))
9519 (void) printf("%s\n", buf);
9520 } else {
9521 optval = s_strtol(val + 1);
9522 BAM_DPRINTF((D_SET_OPTION, fcn, menu_cmds[optnum], val + 1));
9523 rv = set_global(mp, menu_cmds[optnum], optval);
9526 if (rv == BAM_WRITE || rv == BAM_SUCCESS) {
9527 BAM_DPRINTF((D_RETURN_SUCCESS, fcn));
9528 } else {
9529 BAM_DPRINTF((D_RETURN_FAILURE, fcn));
9532 return (rv);
9536 * The quiet argument suppresses messages. This is used
9537 * when invoked in the context of other commands (e.g. list_entry)
9539 static error_t
9540 read_globals(menu_t *mp, char *menu_path, char *globalcmd, int quiet)
9542 line_t *lp;
9543 char *arg;
9544 int done, ret = BAM_SUCCESS;
9546 assert(mp);
9547 assert(menu_path);
9548 assert(globalcmd);
9550 if (mp->start == NULL) {
9551 if (!quiet)
9552 bam_error(NO_MENU, menu_path);
9553 return (BAM_ERROR);
9556 done = 0;
9557 for (lp = mp->start; lp; lp = lp->next) {
9558 if (lp->flags != BAM_GLOBAL)
9559 continue;
9561 if (lp->cmd == NULL) {
9562 if (!quiet)
9563 bam_error(NO_CMD, lp->lineNum);
9564 continue;
9567 if (strcmp(globalcmd, lp->cmd) != 0)
9568 continue;
9570 /* Found global. Check for duplicates */
9571 if (done && !quiet) {
9572 bam_error(DUP_CMD, globalcmd, lp->lineNum, bam_root);
9573 ret = BAM_ERROR;
9576 arg = lp->arg ? lp->arg : "";
9577 bam_print(GLOBAL_CMD, globalcmd, arg);
9578 done = 1;
9581 if (!done && bam_verbose)
9582 bam_print(NO_ENTRY, globalcmd);
9584 return (ret);
9587 static error_t
9588 menu_write(char *root, menu_t *mp)
9590 const char *fcn = "menu_write()";
9592 BAM_DPRINTF((D_MENU_WRITE_ENTER, fcn, root));
9593 return (list2file(root, MENU_TMP, GRUB_MENU, mp->start));
9596 void
9597 line_free(line_t *lp)
9599 if (lp == NULL)
9600 return;
9602 if (lp->cmd != NULL)
9603 free(lp->cmd);
9604 if (lp->sep)
9605 free(lp->sep);
9606 if (lp->arg)
9607 free(lp->arg);
9608 if (lp->line)
9609 free(lp->line);
9610 free(lp);
9613 static void
9614 linelist_free(line_t *start)
9616 line_t *lp;
9618 while (start) {
9619 lp = start;
9620 start = start->next;
9621 line_free(lp);
9625 static void
9626 filelist_free(filelist_t *flistp)
9628 linelist_free(flistp->head);
9629 flistp->head = NULL;
9630 flistp->tail = NULL;
9633 static void
9634 menu_free(menu_t *mp)
9636 entry_t *ent, *tmp;
9637 assert(mp);
9639 if (mp->start)
9640 linelist_free(mp->start);
9641 ent = mp->entries;
9642 while (ent) {
9643 tmp = ent;
9644 ent = tmp->next;
9645 free(tmp);
9648 free(mp);
9652 * Utility routines
9657 * Returns 0 on success
9658 * Any other value indicates an error
9660 static int
9661 exec_cmd(char *cmdline, filelist_t *flistp)
9663 char buf[BUFSIZ];
9664 int ret;
9665 FILE *ptr;
9666 sigset_t set;
9667 void (*disp)(int);
9670 * For security
9671 * - only absolute paths are allowed
9672 * - set IFS to space and tab
9674 if (*cmdline != '/') {
9675 bam_error(ABS_PATH_REQ, cmdline);
9676 return (-1);
9678 (void) putenv("IFS= \t");
9681 * We may have been exec'ed with SIGCHLD blocked
9682 * unblock it here
9684 (void) sigemptyset(&set);
9685 (void) sigaddset(&set, SIGCHLD);
9686 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
9687 bam_error(CANT_UNBLOCK_SIGCHLD, strerror(errno));
9688 return (-1);
9692 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
9694 disp = sigset(SIGCHLD, SIG_DFL);
9695 if (disp == SIG_ERR) {
9696 bam_error(FAILED_SIG, strerror(errno));
9697 return (-1);
9699 if (disp == SIG_HOLD) {
9700 bam_error(BLOCKED_SIG, cmdline);
9701 return (-1);
9704 ptr = popen(cmdline, "r");
9705 if (ptr == NULL) {
9706 bam_error(POPEN_FAIL, cmdline, strerror(errno));
9707 return (-1);
9711 * If we simply do a pclose() following a popen(), pclose()
9712 * will close the reader end of the pipe immediately even
9713 * if the child process has not started/exited. pclose()
9714 * does wait for cmd to terminate before returning though.
9715 * When the executed command writes its output to the pipe
9716 * there is no reader process and the command dies with
9717 * SIGPIPE. To avoid this we read repeatedly until read
9718 * terminates with EOF. This indicates that the command
9719 * (writer) has closed the pipe and we can safely do a
9720 * pclose().
9722 * Since pclose() does wait for the command to exit,
9723 * we can safely reap the exit status of the command
9724 * from the value returned by pclose()
9726 while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
9727 if (flistp == NULL) {
9728 /* s_fgets strips newlines, so insert them at the end */
9729 bam_print(PRINT, buf);
9730 } else {
9731 append_to_flist(flistp, buf);
9735 ret = pclose(ptr);
9736 if (ret == -1) {
9737 bam_error(PCLOSE_FAIL, cmdline, strerror(errno));
9738 return (-1);
9741 if (WIFEXITED(ret)) {
9742 return (WEXITSTATUS(ret));
9743 } else {
9744 bam_error(EXEC_FAIL, cmdline, ret);
9745 return (-1);
9750 * Since this function returns -1 on error
9751 * it cannot be used to convert -1. However,
9752 * that is sufficient for what we need.
9754 static long
9755 s_strtol(char *str)
9757 long l;
9758 char *res = NULL;
9760 if (str == NULL) {
9761 return (-1);
9764 errno = 0;
9765 l = strtol(str, &res, 10);
9766 if (errno || *res != '\0') {
9767 return (-1);
9770 return (l);
9774 * Wrapper around fputs, that adds a newline (since fputs doesn't)
9776 static int
9777 s_fputs(char *str, FILE *fp)
9779 char linebuf[BAM_MAXLINE];
9781 (void) snprintf(linebuf, sizeof (linebuf), "%s\n", str);
9782 return (fputs(linebuf, fp));
9786 * Wrapper around fgets, that strips newlines returned by fgets
9788 char *
9789 s_fgets(char *buf, int buflen, FILE *fp)
9791 int n;
9793 buf = fgets(buf, buflen, fp);
9794 if (buf) {
9795 n = strlen(buf);
9796 if (n == buflen - 1 && buf[n-1] != '\n')
9797 bam_error(TOO_LONG, buflen - 1, buf);
9798 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
9801 return (buf);
9804 void *
9805 s_calloc(size_t nelem, size_t sz)
9807 void *ptr;
9809 ptr = calloc(nelem, sz);
9810 if (ptr == NULL) {
9811 bam_error(NO_MEM, nelem*sz);
9812 bam_exit(1);
9814 return (ptr);
9817 void *
9818 s_realloc(void *ptr, size_t sz)
9820 ptr = realloc(ptr, sz);
9821 if (ptr == NULL) {
9822 bam_error(NO_MEM, sz);
9823 bam_exit(1);
9825 return (ptr);
9828 char *
9829 s_strdup(char *str)
9831 char *ptr;
9833 if (str == NULL)
9834 return (NULL);
9836 ptr = strdup(str);
9837 if (ptr == NULL) {
9838 bam_error(NO_MEM, strlen(str) + 1);
9839 bam_exit(1);
9841 return (ptr);
9845 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
9846 * Returns 0 otherwise
9848 static int
9849 is_amd64(void)
9851 static int amd64 = -1;
9852 char isabuf[257]; /* from sysinfo(2) manpage */
9854 if (amd64 != -1)
9855 return (amd64);
9857 if (bam_alt_platform) {
9858 if (strcmp(bam_platform, "i86pc") == 0) {
9859 amd64 = 1; /* diskless server */
9861 } else {
9862 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
9863 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
9864 amd64 = 1;
9865 } else if (strstr(isabuf, "i386") == NULL) {
9866 amd64 = 1; /* diskless server */
9869 if (amd64 == -1)
9870 amd64 = 0;
9872 return (amd64);
9875 static char *
9876 get_machine(void)
9878 static int cached = -1;
9879 static char mbuf[257]; /* from sysinfo(2) manpage */
9881 if (cached == 0)
9882 return (mbuf);
9884 if (bam_alt_platform) {
9885 return (bam_platform);
9886 } else {
9887 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) {
9888 cached = 1;
9891 if (cached == -1) {
9892 mbuf[0] = '\0';
9893 cached = 0;
9896 return (mbuf);
9900 is_sparc(void)
9902 static int issparc = -1;
9903 char mbuf[257]; /* from sysinfo(2) manpage */
9905 if (issparc != -1)
9906 return (issparc);
9908 if (bam_alt_platform) {
9909 if (strncmp(bam_platform, "sun4", 4) == 0) {
9910 issparc = 1;
9912 } else {
9913 if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 &&
9914 strcmp(mbuf, "sparc") == 0) {
9915 issparc = 1;
9918 if (issparc == -1)
9919 issparc = 0;
9921 return (issparc);
9924 static void
9925 append_to_flist(filelist_t *flistp, char *s)
9927 line_t *lp;
9929 lp = s_calloc(1, sizeof (line_t));
9930 lp->line = s_strdup(s);
9931 if (flistp->head == NULL)
9932 flistp->head = lp;
9933 else
9934 flistp->tail->next = lp;
9935 flistp->tail = lp;
9938 #if !defined(_OBP)
9940 UCODE_VENDORS;
9942 /*ARGSUSED*/
9943 static void
9944 ucode_install(char *root)
9946 int i;
9948 for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
9949 int cmd_len = PATH_MAX + 256;
9950 char cmd[PATH_MAX + 256];
9951 char file[PATH_MAX];
9952 char timestamp[PATH_MAX];
9953 struct stat fstatus, tstatus;
9954 struct utimbuf u_times;
9956 (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s",
9957 bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr,
9958 ucode_vendors[i].extstr);
9960 if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
9961 continue;
9963 (void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
9965 if (stat(timestamp, &tstatus) == 0 &&
9966 fstatus.st_mtime <= tstatus.st_mtime)
9967 continue;
9969 (void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
9970 "%s/%s/%s %s > /dev/null 2>&1", bam_root,
9971 UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
9972 if (system(cmd) != 0)
9973 return;
9975 if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
9976 return;
9978 u_times.actime = fstatus.st_atime;
9979 u_times.modtime = fstatus.st_mtime;
9980 (void) utime(timestamp, &u_times);
9983 #endif