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