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