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