bootadm: remove C code for hsfs boot archive generation
[unleashed/tickless.git] / usr / src / cmd / boot / bootadm / bootadm.c
blob42366046e6bed9a5308e4f89de227fdc485406ad
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2012 Milan Jurik. All rights reserved.
25 * Copyright (c) 2015 by Delphix. All rights reserved.
26 * Copyright 2016 Toomas Soome <tsoome@me.com>
27 * Copyright 2016 Nexenta Systems, Inc.
31 * bootadm(1M) is a new utility for managing bootability of
32 * Solaris *Newboot* environments. It has two primary tasks:
33 * - Allow end users to manage bootability of Newboot Solaris instances
34 * - Provide services to other subsystems in Solaris (primarily Install)
37 /* Headers */
38 #include <stdio.h>
39 #include <errno.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <alloca.h>
46 #include <stdarg.h>
47 #include <limits.h>
48 #include <signal.h>
49 #include <sys/wait.h>
50 #include <sys/mnttab.h>
51 #include <sys/mntent.h>
52 #include <sys/statvfs.h>
53 #include <libnvpair.h>
54 #include <ftw.h>
55 #include <fcntl.h>
56 #include <strings.h>
57 #include <utime.h>
58 #include <sys/systeminfo.h>
59 #include <sys/dktp/fdisk.h>
60 #include <sys/param.h>
61 #include <dirent.h>
62 #include <ctype.h>
63 #include <libgen.h>
64 #include <sys/sysmacros.h>
65 #include <sys/elf.h>
66 #include <libscf.h>
67 #include <zlib.h>
68 #include <sys/lockfs.h>
69 #include <sys/filio.h>
70 #include <libbe.h>
71 #include <deflt.h>
72 #ifdef i386
73 #include <libfdisk.h>
74 #endif
76 #if !defined(_OBP)
77 #include <sys/ucode.h>
78 #endif
80 #include <pwd.h>
81 #include <grp.h>
82 #include <device_info.h>
83 #include <sys/vtoc.h>
84 #include <sys/efi_partition.h>
85 #include <regex.h>
86 #include <locale.h>
87 #include <sys/mkdev.h>
89 #include "bootadm.h"
91 #ifndef TEXT_DOMAIN
92 #define TEXT_DOMAIN "SUNW_OST_OSCMD"
93 #endif /* TEXT_DOMAIN */
95 /* Type definitions */
97 /* Primary subcmds */
98 typedef enum {
99 BAM_MENU = 3,
100 BAM_ARCHIVE,
101 BAM_INSTALL
102 } subcmd_t;
104 #define LINE_INIT 0 /* lineNum initial value */
105 #define ENTRY_INIT -1 /* entryNum initial value */
106 #define ALL_ENTRIES -2 /* selects all boot entries */
108 #define PARTNO_NOTFOUND -1 /* Solaris partition not found */
109 #define PARTNO_EFI -2 /* EFI partition table found */
111 #define RAMDISK_SPECIAL "/devices/ramdisk"
112 #define ZFS_LEGACY_MNTPT "/tmp/bootadm_mnt_zfs_legacy"
114 #define BOOTADM_RDONLY_TEST "BOOTADM_RDONLY_TEST"
116 /* lock related */
117 #define BAM_LOCK_FILE "/var/run/bootadm.lock"
118 #define LOCK_FILE_PERMS (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
120 #define CREATE_RAMDISK "boot/solaris/bin/create_ramdisk"
121 #define EXTRACT_BOOT_FILELIST "boot/solaris/bin/extract_boot_filelist"
123 #define GRUB_fdisk "/etc/lu/GRUB_fdisk"
124 #define GRUB_fdisk_target "/etc/lu/GRUB_fdisk_target"
127 * exec_cmd related
129 typedef struct {
130 line_t *head;
131 line_t *tail;
132 } filelist_t;
134 #define BOOT_FILE_LIST "boot/solaris/filelist.ramdisk"
135 #define ETC_FILE_LIST "etc/boot/solaris/filelist.ramdisk"
137 #define FILE_STAT "boot/solaris/filestat.ramdisk"
138 #define FILE_STAT_TMP "boot/solaris/filestat.ramdisk.tmp"
139 #define DIR_PERMS (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
140 #define FILE_STAT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
142 #define FILE_STAT_TIMESTAMP "boot/solaris/timestamp.cache"
144 /* Globals */
145 int bam_verbose;
146 int bam_force;
147 int bam_debug;
148 static char *prog;
149 static subcmd_t bam_cmd;
150 char *bam_root;
151 int bam_rootlen;
152 static int bam_root_readonly;
153 int bam_alt_root;
154 static int bam_purge = 0;
155 static char *bam_subcmd;
156 static char *bam_opt;
157 static char **bam_argv;
158 static char *bam_pool;
159 static int bam_argc;
160 static int bam_check;
161 static int bam_saved_check;
162 static int bam_smf_check;
163 static int bam_lock_fd = -1;
164 static int bam_zfs;
165 static int bam_mbr;
166 char rootbuf[PATH_MAX] = "/";
167 static int bam_update_all;
168 static int bam_alt_platform;
169 static char *bam_platform;
170 static char *bam_home_env = NULL;
172 /* function prototypes */
173 static void parse_args_internal(int, char *[]);
174 static void parse_args(int, char *argv[]);
175 static error_t bam_install(char *, char *);
176 static error_t bam_archive(char *, char *);
178 static void bam_lock(void);
179 static void bam_unlock(void);
181 static int exec_cmd(char *, filelist_t *);
182 static void linelist_free(line_t *);
183 static void filelist_free(filelist_t *);
185 static error_t install_bootloader(void);
186 static error_t update_archive(char *, char *);
187 static error_t list_archive(char *, char *);
188 static error_t update_all(char *, char *);
189 static error_t read_list(char *, filelist_t *);
191 static long s_strtol(char *);
193 static int is_amd64(void);
194 static char *get_machine(void);
195 static void append_to_flist(filelist_t *, char *);
197 #if !defined(_OBP)
198 static void ucode_install();
199 #endif
201 #if 0
202 /* Menu related sub commands */
203 static subcmd_defn_t menu_subcmds[] = {
204 "set_option", OPT_ABSENT, set_option, 0, /* PUB */
205 "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */
206 "delete_all_entries", OPT_ABSENT, delete_all_entries, 0, /* PVT */
207 "update_entry", OPT_REQ, update_entry, 0, /* menu */
208 "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */
209 "upgrade", OPT_ABSENT, upgrade_menu, 0, /* menu */
210 "list_setting", OPT_OPTIONAL, list_setting, 1, /* menu */
211 "disable_hypervisor", OPT_ABSENT, cvt_to_metal, 0, /* menu */
212 "enable_hypervisor", OPT_ABSENT, cvt_to_hyper, 0, /* menu */
213 NULL, 0, NULL, 0 /* must be last */
215 #endif
217 /* Archive related sub commands */
218 static subcmd_defn_t arch_subcmds[] = {
219 { "update", OPT_ABSENT, update_archive, 0 }, /* PUB */
220 { "update_all", OPT_ABSENT, update_all, 0 }, /* PVT */
221 { "list", OPT_OPTIONAL, list_archive, 1 }, /* PUB */
222 { NULL, 0, NULL, 0 } /* must be last */
225 /* Install related sub commands */
226 static subcmd_defn_t inst_subcmds[] = {
227 { "install_bootloader", OPT_ABSENT, install_bootloader, 0 }, /* PUB */
228 { NULL, 0, NULL, 0 } /* must be last */
231 enum dircache_copy_opt {
232 FILE32 = 0,
233 FILE64,
234 CACHEDIR_NUM
238 * Directory specific flags:
239 * NEED_UPDATE : the specified archive needs to be updated
241 #define NEED_UPDATE 0x00000001
243 #define set_dir_flag(id, f) (walk_arg.dirinfo[id].flags |= f)
244 #define unset_dir_flag(id, f) (walk_arg.dirinfo[id].flags &= ~f)
245 #define is_dir_flag_on(id, f) (walk_arg.dirinfo[id].flags & f ? 1 : 0)
247 #define get_cachedir(id) (walk_arg.dirinfo[id].cdir_path)
248 #define get_count(id) (walk_arg.dirinfo[id].count)
249 #define has_cachedir(id) (walk_arg.dirinfo[id].has_dir)
250 #define set_dir_present(id) (walk_arg.dirinfo[id].has_dir = 1)
253 * dirinfo_t (specific cache directory information):
254 * cdir_path: path to the archive cache directory
255 * has_dir: the specified cache directory is active
256 * count: the number of files to update
257 * flags: directory specific flags
259 typedef struct _dirinfo {
260 char cdir_path[PATH_MAX];
261 int has_dir;
262 int count;
263 int flags;
264 } dirinfo_t;
267 * Update flags:
268 * NEED_CACHE_DIR : cache directory is missing and needs to be created
269 * IS_SPARC_TARGET : the target mountpoint is a SPARC environment
270 * UPDATE_ERROR : an error occourred while traversing the list of files
271 * RDONLY_FSCHK : the target filesystem is read-only
272 * RAMDSK_FSCHK : the target filesystem is on a ramdisk
274 #define NEED_CACHE_DIR 0x00000001
275 #define IS_SPARC_TARGET 0x00000002
276 #define UPDATE_ERROR 0x00000004
277 #define RDONLY_FSCHK 0x00000008
278 #define INVALIDATE_CACHE 0x00000010
280 #define is_flag_on(flag) (walk_arg.update_flags & flag ? 1 : 0)
281 #define set_flag(flag) (walk_arg.update_flags |= flag)
282 #define unset_flag(flag) (walk_arg.update_flags &= ~flag)
285 * struct walk_arg :
286 * update_flags: flags related to the current updating process
287 * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs
288 * sparcfile: list of file paths for mkisofs -path-list (SPARC only)
290 static struct {
291 int update_flags;
292 nvlist_t *new_nvlp;
293 nvlist_t *old_nvlp;
294 FILE *sparcfile;
295 dirinfo_t dirinfo[CACHEDIR_NUM];
296 } walk_arg;
298 struct safefile {
299 char *name;
300 struct safefile *next;
303 static struct safefile *safefiles = NULL;
306 * svc:/system/filesystem/usr:default service checks for this file and
307 * does a boot archive update and then reboot the system.
309 #define NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
312 * svc:/system/boot-archive-update:default checks for this file and
313 * updates the boot archive.
315 #define NEED_UPDATE_SAFE_FILE "/etc/svc/volatile/boot_archive_safefile_update"
317 /* Thanks growisofs */
318 #define CD_BLOCK ((off64_t)2048)
319 #define VOLDESC_OFF 16
320 #define DVD_BLOCK (32*1024)
321 #define MAX_IVDs 16
323 struct iso_pdesc {
324 unsigned char type [1];
325 unsigned char id [5];
326 unsigned char void1 [80-5-1];
327 unsigned char volume_space_size [8];
328 unsigned char void2 [2048-80-8];
331 #define bam_nowrite() (bam_check || bam_smf_check)
333 static void
334 usage(void)
336 (void) fprintf(stderr, "USAGE:\n");
338 /* archive usage */
339 (void) fprintf(stderr,
340 "\t%s update-archive [-vn] [-R altroot [-p platform]]\n", prog);
341 (void) fprintf(stderr,
342 "\t%s list-archive [-R altroot [-p platform]]\n", prog);
343 #if defined(_OBP)
344 (void) fprintf(stderr,
345 "\t%s install-bootloader [-fv] [-R altroot] [-P pool]\n", prog);
346 #else
347 (void) fprintf(stderr,
348 "\t%s install-bootloader [-Mfv] [-R altroot] [-P pool]\n", prog);
349 #endif
350 #if !defined(_OBP)
351 /* x86 only */
352 (void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
353 (void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
354 #endif
358 * Best effort attempt to restore the $HOME value.
360 static void
361 restore_env()
363 char home_env[PATH_MAX];
365 if (bam_home_env) {
366 (void) snprintf(home_env, sizeof (home_env), "HOME=%s",
367 bam_home_env);
368 (void) putenv(home_env);
373 #define SLEEP_TIME 5
374 #define MAX_TRIES 4
377 * Sanitize the environment in which bootadm will execute its sub-processes
378 * (ex. mkisofs). This is done to prevent those processes from attempting
379 * to access files (ex. .mkisofsrc) or stat paths that might be on NFS
380 * or, potentially, insecure.
382 static void
383 sanitize_env()
385 int stry = 0;
387 /* don't depend on caller umask */
388 (void) umask(0022);
390 /* move away from a potential unsafe current working directory */
391 while (chdir("/") == -1) {
392 if (errno != EINTR) {
393 bam_print("WARNING: unable to chdir to /");
394 break;
398 bam_home_env = getenv("HOME");
399 while (bam_home_env != NULL && putenv("HOME=/") == -1) {
400 if (errno == ENOMEM) {
401 /* retry no more than MAX_TRIES times */
402 if (++stry > MAX_TRIES) {
403 bam_print("WARNING: unable to recover from "
404 "system memory pressure... aborting \n");
405 bam_exit(EXIT_FAILURE);
407 /* memory is tight, try to sleep */
408 bam_print("Attempting to recover from memory pressure: "
409 "sleeping for %d seconds\n", SLEEP_TIME * stry);
410 (void) sleep(SLEEP_TIME * stry);
411 } else {
412 bam_print("WARNING: unable to sanitize HOME\n");
418 main(int argc, char *argv[])
420 error_t ret = BAM_SUCCESS;
422 (void) setlocale(LC_ALL, "");
423 (void) textdomain(TEXT_DOMAIN);
425 if ((prog = strrchr(argv[0], '/')) == NULL) {
426 prog = argv[0];
427 } else {
428 prog++;
431 INJECT_ERROR1("ASSERT_ON", assert(0))
433 sanitize_env();
435 parse_args(argc, argv);
437 switch (bam_cmd) {
438 case BAM_MENU:
439 ret = bam_loader_menu(bam_subcmd, bam_opt,
440 bam_argc, bam_argv);
441 break;
442 case BAM_ARCHIVE:
443 ret = bam_archive(bam_subcmd, bam_opt);
444 break;
445 case BAM_INSTALL:
446 ret = bam_install(bam_subcmd, bam_opt);
447 break;
448 default:
449 usage();
450 bam_exit(1);
453 if (ret != BAM_SUCCESS)
454 bam_exit((ret == BAM_NOCHANGE) ? 2 : 1);
456 bam_unlock();
457 return (0);
461 * Equivalence of public and internal commands:
462 * update-archive -- -a update
463 * list-archive -- -a list
464 * set-menu -- -m set_option
465 * list-menu -- -m list_entry
466 * update-menu -- -m update_entry
467 * install-bootloader -- -i install_bootloader
469 static struct cmd_map {
470 char *bam_cmdname;
471 int bam_cmd;
472 char *bam_subcmd;
473 } cmd_map[] = {
474 { "update-archive", BAM_ARCHIVE, "update"},
475 { "list-archive", BAM_ARCHIVE, "list"},
476 { "set-menu", BAM_MENU, "set_option"},
477 { "list-menu", BAM_MENU, "list_entry"},
478 { "update-menu", BAM_MENU, "update_entry"},
479 { "install-bootloader", BAM_INSTALL, "install_bootloader"},
480 { NULL, 0, NULL}
484 * Commands syntax published in bootadm(1M) are parsed here
486 static void
487 parse_args(int argc, char *argv[])
489 struct cmd_map *cmp = cmd_map;
491 /* command conforming to the final spec */
492 if (argc > 1 && argv[1][0] != '-') {
494 * Map commands to internal table.
496 while (cmp->bam_cmdname) {
497 if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
498 bam_cmd = cmp->bam_cmd;
499 bam_subcmd = cmp->bam_subcmd;
500 break;
502 cmp++;
504 if (cmp->bam_cmdname == NULL) {
505 usage();
506 bam_exit(1);
508 argc--;
509 argv++;
512 parse_args_internal(argc, argv);
516 * A combination of public and private commands are parsed here.
517 * The internal syntax and the corresponding functionality are:
518 * -a update -- update-archive
519 * -a list -- list-archive
520 * -a update-all -- (reboot to sync all mnted OS archive)
521 * -i install_bootloader -- install-bootloader
522 * -m update_entry -- update-menu
523 * -m list_entry -- list-menu
524 * -m update_temp -- (reboot -- [boot-args])
525 * -m delete_all_entries -- (called from install)
526 * -m enable_hypervisor [args] -- cvt_to_hyper
527 * -m disable_hypervisor -- cvt_to_metal
528 * -m list_setting [entry] [value] -- list_setting
530 * A set of private flags is there too:
531 * -F -- purge the cache directories and rebuild them
533 static void
534 parse_args_internal(int argc, char *argv[])
536 int c, error;
537 extern char *optarg;
538 extern int optind, opterr;
539 #if defined(_OBP)
540 const char *optstring = "a:d:fi:m:no:vFCR:p:P:XZ";
541 #else
542 const char *optstring = "a:d:fi:m:no:vFCMR:p:P:XZ";
543 #endif
545 /* Suppress error message from getopt */
546 opterr = 0;
548 error = 0;
549 while ((c = getopt(argc, argv, optstring)) != -1) {
550 switch (c) {
551 case 'a':
552 if (bam_cmd) {
553 error = 1;
554 bam_error(
555 _("multiple commands specified: -%c\n"), c);
557 bam_cmd = BAM_ARCHIVE;
558 bam_subcmd = optarg;
559 break;
560 case 'd':
561 if (bam_debug) {
562 error = 1;
563 bam_error(
564 _("duplicate options specified: -%c\n"), c);
566 bam_debug = s_strtol(optarg);
567 break;
568 case 'f':
569 bam_force = 1;
570 break;
571 case 'F':
572 bam_purge = 1;
573 break;
574 case 'i':
575 if (bam_cmd) {
576 error = 1;
577 bam_error(
578 _("multiple commands specified: -%c\n"), c);
580 bam_cmd = BAM_INSTALL;
581 bam_subcmd = optarg;
582 break;
583 case 'm':
584 if (bam_cmd) {
585 error = 1;
586 bam_error(
587 _("multiple commands specified: -%c\n"), c);
589 bam_cmd = BAM_MENU;
590 bam_subcmd = optarg;
591 break;
592 #if !defined(_OBP)
593 case 'M':
594 bam_mbr = 1;
595 break;
596 #endif
597 case 'n':
598 bam_check = 1;
600 * We save the original value of bam_check. The new
601 * approach in case of a read-only filesystem is to
602 * behave as a check, so we need a way to restore the
603 * original value after the evaluation of the read-only
604 * filesystem has been done.
605 * Even if we don't allow at the moment a check with
606 * update_all, this approach is more robust than
607 * simply resetting bam_check to zero.
609 bam_saved_check = 1;
610 break;
611 case 'o':
612 if (bam_opt) {
613 error = 1;
614 bam_error(
615 _("duplicate options specified: -%c\n"), c);
617 bam_opt = optarg;
618 break;
619 case 'v':
620 bam_verbose = 1;
621 break;
622 case 'C':
623 bam_smf_check = 1;
624 break;
625 case 'P':
626 if (bam_pool != NULL) {
627 error = 1;
628 bam_error(
629 _("duplicate options specified: -%c\n"), c);
631 bam_pool = optarg;
632 break;
633 case 'R':
634 if (bam_root) {
635 error = 1;
636 bam_error(
637 _("duplicate options specified: -%c\n"), c);
638 break;
639 } else if (realpath(optarg, rootbuf) == NULL) {
640 error = 1;
641 bam_error(_("cannot resolve path %s: %s\n"),
642 optarg, strerror(errno));
643 break;
645 bam_alt_root = 1;
646 bam_root = rootbuf;
647 bam_rootlen = strlen(rootbuf);
648 break;
649 case 'p':
650 bam_alt_platform = 1;
651 bam_platform = optarg;
652 if ((strcmp(bam_platform, "i86pc") != 0) &&
653 (strcmp(bam_platform, "sun4u") != 0) &&
654 (strcmp(bam_platform, "sun4v") != 0)) {
655 error = 1;
656 bam_error(_("invalid platform %s - must be "
657 "one of sun4u, sun4v or i86pc\n"),
658 bam_platform);
660 break;
661 case 'X':
662 bam_is_hv = BAM_HV_PRESENT;
663 break;
664 case 'Z':
665 bam_zfs = 1;
666 break;
667 case '?':
668 error = 1;
669 bam_error(_("invalid option or missing option "
670 "argument: -%c\n"), optopt);
671 break;
672 default :
673 error = 1;
674 bam_error(_("invalid option or missing option "
675 "argument: -%c\n"), c);
676 break;
681 * An alternate platform requires an alternate root
683 if (bam_alt_platform && bam_alt_root == 0) {
684 usage();
685 bam_exit(0);
689 * A command option must be specfied
691 if (!bam_cmd) {
692 if (bam_opt && strcmp(bam_opt, "all") == 0) {
693 usage();
694 bam_exit(0);
696 bam_error(_("a command option must be specified\n"));
697 error = 1;
700 if (error) {
701 usage();
702 bam_exit(1);
705 if (optind > argc) {
706 bam_error(_("Internal error: %s\n"), "parse_args");
707 bam_exit(1);
708 } else if (optind < argc) {
709 bam_argv = &argv[optind];
710 bam_argc = argc - optind;
714 * mbr and pool are options for install_bootloader
716 if (bam_cmd != BAM_INSTALL && (bam_mbr || bam_pool != NULL)) {
717 usage();
718 bam_exit(0);
722 * -n implies verbose mode
724 if (bam_check)
725 bam_verbose = 1;
728 error_t
729 check_subcmd_and_options(
730 char *subcmd,
731 char *opt,
732 subcmd_defn_t *table,
733 error_t (**fp)())
735 int i;
737 if (subcmd == NULL) {
738 bam_error(_("this command requires a sub-command\n"));
739 return (BAM_ERROR);
742 if (strcmp(subcmd, "set_option") == 0) {
743 if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) {
744 bam_error(_("missing argument for sub-command\n"));
745 usage();
746 return (BAM_ERROR);
747 } else if (bam_argc > 1 || bam_argv[1] != NULL) {
748 bam_error(_("invalid trailing arguments\n"));
749 usage();
750 return (BAM_ERROR);
752 } else if (strcmp(subcmd, "update_all") == 0) {
754 * The only option we accept for the "update_all"
755 * subcmd is "fastboot".
757 if (bam_argc > 1 || (bam_argc == 1 &&
758 strcmp(bam_argv[0], "fastboot") != 0)) {
759 bam_error(_("invalid trailing arguments\n"));
760 usage();
761 return (BAM_ERROR);
763 } else if (((strcmp(subcmd, "enable_hypervisor") != 0) &&
764 (strcmp(subcmd, "list_setting") != 0)) && (bam_argc || bam_argv)) {
766 * Of the remaining subcommands, only "enable_hypervisor" and
767 * "list_setting" take trailing arguments.
769 bam_error(_("invalid trailing arguments\n"));
770 usage();
771 return (BAM_ERROR);
774 if (bam_root == NULL) {
775 bam_root = rootbuf;
776 bam_rootlen = 1;
779 /* verify that subcmd is valid */
780 for (i = 0; table[i].subcmd != NULL; i++) {
781 if (strcmp(table[i].subcmd, subcmd) == 0)
782 break;
785 if (table[i].subcmd == NULL) {
786 bam_error(_("invalid sub-command specified: %s\n"), subcmd);
787 return (BAM_ERROR);
790 if (table[i].unpriv == 0 && geteuid() != 0) {
791 bam_error(_("you must be root to run this command\n"));
792 return (BAM_ERROR);
796 * Currently only privileged commands need a lock
798 if (table[i].unpriv == 0)
799 bam_lock();
801 /* subcmd verifies that opt is appropriate */
802 if (table[i].option != OPT_OPTIONAL) {
803 if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
804 if (opt)
805 bam_error(_("this sub-command (%s) does not "
806 "take options\n"), subcmd);
807 else
808 bam_error(_("an option is required for this "
809 "sub-command: %s\n"), subcmd);
810 return (BAM_ERROR);
814 *fp = table[i].handler;
816 return (BAM_SUCCESS);
820 * NOTE: A single "/" is also considered a trailing slash and will
821 * be deleted.
823 void
824 elide_trailing_slash(const char *src, char *dst, size_t dstsize)
826 size_t dstlen;
828 assert(src);
829 assert(dst);
831 (void) strlcpy(dst, src, dstsize);
833 dstlen = strlen(dst);
834 if (dst[dstlen - 1] == '/') {
835 dst[dstlen - 1] = '\0';
839 static int
840 is_safe_exec(char *path)
842 struct stat sb;
844 if (lstat(path, &sb) != 0) {
845 bam_error(_("stat of file failed: %s: %s\n"), path,
846 strerror(errno));
847 return (BAM_ERROR);
850 if (!S_ISREG(sb.st_mode)) {
851 bam_error(_("%s is not a regular file, skipping\n"), path);
852 return (BAM_ERROR);
855 if (sb.st_uid != getuid()) {
856 bam_error(_("%s is not owned by %d, skipping\n"),
857 path, getuid());
858 return (BAM_ERROR);
861 if (sb.st_mode & S_IWOTH || sb.st_mode & S_IWGRP) {
862 bam_error(_("%s is others or group writable, skipping\n"),
863 path);
864 return (BAM_ERROR);
867 return (BAM_SUCCESS);
870 static error_t
871 install_bootloader(void)
873 nvlist_t *nvl;
874 uint16_t flags = 0;
875 int found = 0;
876 struct extmnttab mnt;
877 struct stat statbuf = {0};
878 be_node_list_t *be_nodes, *node;
879 FILE *fp;
880 char *root_ds = NULL;
881 int ret = BAM_ERROR;
883 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
884 bam_error(_("out of memory\n"));
885 return (ret);
889 * if bam_alt_root is set, the stage files are used from alt root.
890 * if pool is set, the target devices are pool devices, stage files
891 * are read from pool bootfs unless alt root is set.
893 * use arguments as targets, stage files are from alt or current root
894 * if no arguments and no pool, install on current boot pool.
897 if (bam_alt_root) {
898 if (stat(bam_root, &statbuf) != 0) {
899 bam_error(_("stat of file failed: %s: %s\n"), bam_root,
900 strerror(errno));
901 goto done;
903 if ((fp = fopen(MNTTAB, "r")) == NULL) {
904 bam_error(_("failed to open file: %s: %s\n"),
905 MNTTAB, strerror(errno));
906 goto done;
908 resetmnttab(fp);
909 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
910 if (mnt.mnt_major == major(statbuf.st_dev) &&
911 mnt.mnt_minor == minor(statbuf.st_dev)) {
912 found = 1;
913 root_ds = strdup(mnt.mnt_special);
914 break;
917 (void) fclose(fp);
919 if (found == 0) {
920 bam_error(_("alternate root %s not in mnttab\n"),
921 bam_root);
922 goto done;
924 if (root_ds == NULL) {
925 bam_error(_("out of memory\n"));
926 goto done;
929 if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
930 bam_error(_("No BE's found\n"));
931 goto done;
933 for (node = be_nodes; node != NULL; node = node->be_next_node)
934 if (strcmp(root_ds, node->be_root_ds) == 0)
935 break;
937 if (node == NULL)
938 bam_error(_("BE (%s) does not exist\n"), root_ds);
940 free(root_ds);
941 root_ds = NULL;
942 if (node == NULL) {
943 be_free_list(be_nodes);
944 goto done;
946 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
947 node->be_node_name);
948 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
949 node->be_root_ds);
950 be_free_list(be_nodes);
951 if (ret != 0) {
952 ret = BAM_ERROR;
953 goto done;
957 if (bam_force)
958 flags |= BE_INSTALLBOOT_FLAG_FORCE;
959 if (bam_mbr)
960 flags |= BE_INSTALLBOOT_FLAG_MBR;
961 if (bam_verbose)
962 flags |= BE_INSTALLBOOT_FLAG_VERBOSE;
964 if (nvlist_add_uint16(nvl, BE_ATTR_INSTALL_FLAGS, flags) != 0) {
965 bam_error(_("out of memory\n"));
966 ret = BAM_ERROR;
967 goto done;
971 * if altroot was set, we got be name and be root, only need
972 * to set pool name as target.
973 * if no altroot, need to find be name and root from pool.
975 if (bam_pool != NULL) {
976 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, bam_pool);
977 if (ret != 0) {
978 ret = BAM_ERROR;
979 goto done;
981 if (found) {
982 ret = be_installboot(nvl);
983 if (ret != 0)
984 ret = BAM_ERROR;
985 goto done;
989 if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
990 bam_error(_("No BE's found\n"));
991 ret = BAM_ERROR;
992 goto done;
995 if (bam_pool != NULL) {
997 * find active be_node in bam_pool
999 for (node = be_nodes; node != NULL; node = node->be_next_node) {
1000 if (strcmp(bam_pool, node->be_rpool) != 0)
1001 continue;
1002 if (node->be_active_on_boot)
1003 break;
1005 if (node == NULL) {
1006 bam_error(_("No active BE in %s\n"), bam_pool);
1007 be_free_list(be_nodes);
1008 ret = BAM_ERROR;
1009 goto done;
1011 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1012 node->be_node_name);
1013 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1014 node->be_root_ds);
1015 be_free_list(be_nodes);
1016 if (ret != 0) {
1017 ret = BAM_ERROR;
1018 goto done;
1020 ret = be_installboot(nvl);
1021 if (ret != 0)
1022 ret = BAM_ERROR;
1023 goto done;
1027 * get dataset for "/" and fill up the args.
1029 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1030 bam_error(_("failed to open file: %s: %s\n"),
1031 MNTTAB, strerror(errno));
1032 ret = BAM_ERROR;
1033 be_free_list(be_nodes);
1034 goto done;
1036 resetmnttab(fp);
1037 found = 0;
1038 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1039 if (strcmp(mnt.mnt_mountp, "/") == 0) {
1040 found = 1;
1041 root_ds = strdup(mnt.mnt_special);
1042 break;
1045 (void) fclose(fp);
1047 if (found == 0) {
1048 bam_error(_("alternate root %s not in mnttab\n"), "/");
1049 ret = BAM_ERROR;
1050 be_free_list(be_nodes);
1051 goto done;
1053 if (root_ds == NULL) {
1054 bam_error(_("out of memory\n"));
1055 ret = BAM_ERROR;
1056 be_free_list(be_nodes);
1057 goto done;
1060 for (node = be_nodes; node != NULL; node = node->be_next_node) {
1061 if (strcmp(root_ds, node->be_root_ds) == 0)
1062 break;
1065 if (node == NULL) {
1066 bam_error(_("No such BE: %s\n"), root_ds);
1067 free(root_ds);
1068 be_free_list(be_nodes);
1069 ret = BAM_ERROR;
1070 goto done;
1072 free(root_ds);
1074 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME, node->be_node_name);
1075 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT, node->be_root_ds);
1076 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, node->be_rpool);
1077 be_free_list(be_nodes);
1079 if (ret != 0)
1080 ret = BAM_ERROR;
1081 else
1082 ret = be_installboot(nvl) ? BAM_ERROR : 0;
1083 done:
1084 nvlist_free(nvl);
1086 return (ret);
1089 static error_t
1090 bam_install(char *subcmd, char *opt)
1092 error_t (*f)(void);
1095 * Check arguments
1097 if (check_subcmd_and_options(subcmd, opt, inst_subcmds, &f) ==
1098 BAM_ERROR)
1099 return (BAM_ERROR);
1101 return (f());
1104 static error_t
1105 bam_archive(
1106 char *subcmd,
1107 char *opt)
1109 error_t ret;
1110 error_t (*f)(char *root, char *opt);
1111 const char *fcn = "bam_archive()";
1114 * Add trailing / for archive subcommands
1116 if (rootbuf[strlen(rootbuf) - 1] != '/')
1117 (void) strcat(rootbuf, "/");
1118 bam_rootlen = strlen(rootbuf);
1121 * Check arguments
1123 ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
1124 if (ret != BAM_SUCCESS) {
1125 return (BAM_ERROR);
1128 ret = get_boot_cap(rootbuf);
1129 if (ret != BAM_SUCCESS) {
1130 BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn));
1131 return (ret);
1135 * Check archive not supported with update_all
1136 * since it is awkward to display out-of-sync
1137 * information for each BE.
1139 if (bam_check && strcmp(subcmd, "update_all") == 0) {
1140 bam_error(_("the check option is not supported with "
1141 "subcmd: %s\n"), subcmd);
1142 return (BAM_ERROR);
1145 if (strcmp(subcmd, "update_all") == 0)
1146 bam_update_all = 1;
1148 #if !defined(_OBP)
1149 ucode_install(bam_root);
1150 #endif
1152 ret = f(bam_root, opt);
1154 bam_update_all = 0;
1156 return (ret);
1159 /*PRINTFLIKE1*/
1160 void
1161 bam_error(char *format, ...)
1163 va_list ap;
1165 va_start(ap, format);
1166 (void) fprintf(stderr, "%s: ", prog);
1167 (void) vfprintf(stderr, format, ap);
1168 va_end(ap);
1171 /*PRINTFLIKE1*/
1172 void
1173 bam_derror(char *format, ...)
1175 va_list ap;
1177 assert(bam_debug);
1179 va_start(ap, format);
1180 (void) fprintf(stderr, "DEBUG: ");
1181 (void) vfprintf(stderr, format, ap);
1182 va_end(ap);
1185 /*PRINTFLIKE1*/
1186 void
1187 bam_print(char *format, ...)
1189 va_list ap;
1191 va_start(ap, format);
1192 (void) vfprintf(stdout, format, ap);
1193 va_end(ap);
1196 /*PRINTFLIKE1*/
1197 void
1198 bam_print_stderr(char *format, ...)
1200 va_list ap;
1202 va_start(ap, format);
1203 (void) vfprintf(stderr, format, ap);
1204 va_end(ap);
1207 void
1208 bam_exit(int excode)
1210 restore_env();
1211 bam_unlock();
1212 exit(excode);
1215 static void
1216 bam_lock(void)
1218 struct flock lock;
1219 pid_t pid;
1221 bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
1222 if (bam_lock_fd < 0) {
1224 * We may be invoked early in boot for archive verification.
1225 * In this case, root is readonly and /var/run may not exist.
1226 * Proceed without the lock
1228 if (errno == EROFS || errno == ENOENT) {
1229 bam_root_readonly = 1;
1230 return;
1233 bam_error(_("failed to open file: %s: %s\n"),
1234 BAM_LOCK_FILE, strerror(errno));
1235 bam_exit(1);
1238 lock.l_type = F_WRLCK;
1239 lock.l_whence = SEEK_SET;
1240 lock.l_start = 0;
1241 lock.l_len = 0;
1243 if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
1244 if (errno != EACCES && errno != EAGAIN) {
1245 bam_error(_("failed to lock file: %s: %s\n"),
1246 BAM_LOCK_FILE, strerror(errno));
1247 (void) close(bam_lock_fd);
1248 bam_lock_fd = -1;
1249 bam_exit(1);
1251 pid = 0;
1252 (void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
1253 bam_print(
1254 _("another instance of bootadm (pid %lu) is running\n"),
1255 pid);
1257 lock.l_type = F_WRLCK;
1258 lock.l_whence = SEEK_SET;
1259 lock.l_start = 0;
1260 lock.l_len = 0;
1261 if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
1262 bam_error(_("failed to lock file: %s: %s\n"),
1263 BAM_LOCK_FILE, strerror(errno));
1264 (void) close(bam_lock_fd);
1265 bam_lock_fd = -1;
1266 bam_exit(1);
1270 /* We own the lock now */
1271 pid = getpid();
1272 (void) write(bam_lock_fd, &pid, sizeof (pid));
1275 static void
1276 bam_unlock(void)
1278 struct flock unlock;
1281 * NOP if we don't hold the lock
1283 if (bam_lock_fd < 0) {
1284 return;
1287 unlock.l_type = F_UNLCK;
1288 unlock.l_whence = SEEK_SET;
1289 unlock.l_start = 0;
1290 unlock.l_len = 0;
1292 if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
1293 bam_error(_("failed to unlock file: %s: %s\n"),
1294 BAM_LOCK_FILE, strerror(errno));
1297 if (close(bam_lock_fd) == -1) {
1298 bam_error(_("failed to close file: %s: %s\n"),
1299 BAM_LOCK_FILE, strerror(errno));
1301 bam_lock_fd = -1;
1304 static error_t
1305 list_archive(char *root, char *opt)
1307 filelist_t flist;
1308 filelist_t *flistp = &flist;
1309 line_t *lp;
1311 assert(root);
1312 assert(opt == NULL);
1314 flistp->head = flistp->tail = NULL;
1315 if (read_list(root, flistp) != BAM_SUCCESS) {
1316 return (BAM_ERROR);
1318 assert(flistp->head && flistp->tail);
1320 for (lp = flistp->head; lp; lp = lp->next) {
1321 bam_print(_("%s\n"), lp->line);
1324 filelist_free(flistp);
1326 return (BAM_SUCCESS);
1330 * Checks if the path specified (without the file name at the end) exists
1331 * and creates it if not. If the path exists and is not a directory, an attempt
1332 * to unlink is made.
1334 static int
1335 setup_path(char *path)
1337 char *p;
1338 int ret;
1339 struct stat sb;
1341 p = strrchr(path, '/');
1342 if (p != NULL) {
1343 *p = '\0';
1344 if (stat(path, &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
1345 /* best effort attempt, mkdirp will catch the error */
1346 (void) unlink(path);
1347 if (bam_verbose)
1348 bam_print(_("need to create directory "
1349 "path for %s\n"), path);
1350 ret = mkdirp(path, DIR_PERMS);
1351 if (ret == -1) {
1352 bam_error(_("mkdir of %s failed: %s\n"),
1353 path, strerror(errno));
1354 *p = '/';
1355 return (BAM_ERROR);
1358 *p = '/';
1359 return (BAM_SUCCESS);
1361 return (BAM_SUCCESS);
1364 typedef union {
1365 gzFile gzfile;
1366 int fdfile;
1367 } outfile;
1369 typedef struct {
1370 char path[PATH_MAX];
1371 outfile out;
1372 } cachefile;
1374 static int
1375 setup_file(char *base, const char *path, cachefile *cf)
1377 int ret;
1378 char *strip;
1380 /* init gzfile or fdfile in case we fail before opening */
1381 if (bam_direct == BAM_DIRECT_DBOOT)
1382 cf->out.gzfile = NULL;
1383 else
1384 cf->out.fdfile = -1;
1386 /* strip the trailing altroot path */
1387 strip = (char *)path + strlen(rootbuf);
1389 ret = snprintf(cf->path, sizeof (cf->path), "%s/%s", base, strip);
1390 if (ret >= sizeof (cf->path)) {
1391 bam_error(_("unable to create path on mountpoint %s, "
1392 "path too long\n"), rootbuf);
1393 return (BAM_ERROR);
1396 /* Check if path is present in the archive cache directory */
1397 if (setup_path(cf->path) == BAM_ERROR)
1398 return (BAM_ERROR);
1400 if (bam_direct == BAM_DIRECT_DBOOT) {
1401 if ((cf->out.gzfile = gzopen(cf->path, "wb")) == NULL) {
1402 bam_error(_("failed to open file: %s: %s\n"),
1403 cf->path, strerror(errno));
1404 return (BAM_ERROR);
1406 (void) gzsetparams(cf->out.gzfile, Z_BEST_SPEED,
1407 Z_DEFAULT_STRATEGY);
1408 } else {
1409 if ((cf->out.fdfile = open(cf->path, O_WRONLY | O_CREAT, 0644))
1410 == -1) {
1411 bam_error(_("failed to open file: %s: %s\n"),
1412 cf->path, strerror(errno));
1413 return (BAM_ERROR);
1417 return (BAM_SUCCESS);
1420 static int
1421 cache_write(cachefile cf, char *buf, int size)
1423 int err;
1425 if (bam_direct == BAM_DIRECT_DBOOT) {
1426 if (gzwrite(cf.out.gzfile, buf, size) < 1) {
1427 bam_error(_("failed to write to %s\n"),
1428 gzerror(cf.out.gzfile, &err));
1429 if (err == Z_ERRNO && bam_verbose) {
1430 bam_error(_("write to file failed: %s: %s\n"),
1431 cf.path, strerror(errno));
1433 return (BAM_ERROR);
1435 } else {
1436 if (write(cf.out.fdfile, buf, size) < 1) {
1437 bam_error(_("write to file failed: %s: %s\n"),
1438 cf.path, strerror(errno));
1439 return (BAM_ERROR);
1442 return (BAM_SUCCESS);
1445 static int
1446 cache_close(cachefile cf)
1448 int ret;
1450 if (bam_direct == BAM_DIRECT_DBOOT) {
1451 if (cf.out.gzfile) {
1452 ret = gzclose(cf.out.gzfile);
1453 if (ret != Z_OK) {
1454 bam_error(_("failed to close file: %s: %s\n"),
1455 cf.path, strerror(errno));
1456 return (BAM_ERROR);
1459 } else {
1460 if (cf.out.fdfile != -1) {
1461 ret = close(cf.out.fdfile);
1462 if (ret != 0) {
1463 bam_error(_("failed to close file: %s: %s\n"),
1464 cf.path, strerror(errno));
1465 return (BAM_ERROR);
1470 return (BAM_SUCCESS);
1473 static int
1474 dircache_updatefile(const char *path, int what)
1476 int ret, exitcode;
1477 char buf[4096 * 4];
1478 FILE *infile;
1479 cachefile outfile;
1481 if (bam_nowrite()) {
1482 set_dir_flag(what, NEED_UPDATE);
1483 return (BAM_SUCCESS);
1486 if (!has_cachedir(what))
1487 return (BAM_SUCCESS);
1489 if ((infile = fopen(path, "rb")) == NULL) {
1490 bam_error(_("failed to open file: %s: %s\n"), path,
1491 strerror(errno));
1492 return (BAM_ERROR);
1495 ret = setup_file(get_cachedir(what), path, &outfile);
1496 if (ret == BAM_ERROR) {
1497 exitcode = BAM_ERROR;
1498 goto out;
1501 while ((ret = fread(buf, 1, sizeof (buf), infile)) > 0) {
1502 if (cache_write(outfile, buf, ret) == BAM_ERROR) {
1503 exitcode = BAM_ERROR;
1504 goto out;
1508 set_dir_flag(what, NEED_UPDATE);
1509 get_count(what)++;
1510 exitcode = BAM_SUCCESS;
1511 out:
1512 (void) fclose(infile);
1513 if (cache_close(outfile) == BAM_ERROR)
1514 exitcode = BAM_ERROR;
1515 if (exitcode == BAM_ERROR)
1516 set_flag(UPDATE_ERROR);
1517 return (exitcode);
1520 static int
1521 dircache_updatedir(const char *path, int what)
1523 int ret;
1524 char dpath[PATH_MAX];
1525 char *strip;
1526 struct stat sb;
1528 strip = (char *)path + strlen(rootbuf);
1530 ret = snprintf(dpath, sizeof (dpath), "%s/%s", get_cachedir(what), strip);
1532 if (ret >= sizeof (dpath)) {
1533 bam_error(_("unable to create path on mountpoint %s, "
1534 "path too long\n"), rootbuf);
1535 set_flag(UPDATE_ERROR);
1536 return (BAM_ERROR);
1539 if (stat(dpath, &sb) == 0 && S_ISDIR(sb.st_mode))
1540 return (BAM_SUCCESS);
1542 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) {
1543 set_flag(UPDATE_ERROR);
1544 return (BAM_ERROR);
1547 set_dir_flag(what, NEED_UPDATE);
1548 return (BAM_SUCCESS);
1551 #define DO_CACHE_DIR 0
1552 #define DO_UPDATE_DIR 1
1554 #if defined(_LP64) || defined(_LONGLONG_TYPE)
1555 typedef Elf64_Ehdr _elfhdr;
1556 #else
1557 typedef Elf32_Ehdr _elfhdr;
1558 #endif
1561 * This routine updates the contents of the cache directory
1563 static int
1564 update_dircache(const char *path, int flags)
1566 int rc = BAM_SUCCESS;
1568 switch (flags) {
1569 case FTW_F:
1571 int fd;
1572 _elfhdr elf;
1574 if ((fd = open(path, O_RDONLY)) < 0) {
1575 bam_error(_("failed to open file: %s: %s\n"),
1576 path, strerror(errno));
1577 set_flag(UPDATE_ERROR);
1578 rc = BAM_ERROR;
1579 break;
1583 * libelf and gelf would be a cleaner and easier way to handle
1584 * this, but libelf fails compilation if _ILP32 is defined &&
1585 * _FILE_OFFSET_BITS is != 32 ...
1587 if (read(fd, (void *)&elf, sizeof (_elfhdr)) < 0) {
1588 bam_error(_("read failed for file: %s: %s\n"),
1589 path, strerror(errno));
1590 set_flag(UPDATE_ERROR);
1591 (void) close(fd);
1592 rc = BAM_ERROR;
1593 break;
1595 (void) close(fd);
1598 * If the file is not an executable and is not inside an amd64
1599 * directory, we copy it in both the cache directories,
1600 * otherwise, we only copy it inside the 64-bit one.
1602 if (memcmp(elf.e_ident, ELFMAG, 4) != 0) {
1603 if (strstr(path, "/amd64")) {
1604 rc = dircache_updatefile(path, FILE64);
1605 } else {
1606 rc = dircache_updatefile(path, FILE32);
1607 if (rc == BAM_SUCCESS)
1608 rc = dircache_updatefile(path, FILE64);
1610 } else {
1612 * Based on the ELF class we copy the file in the 32-bit
1613 * or the 64-bit cache directory.
1615 if (elf.e_ident[EI_CLASS] == ELFCLASS32) {
1616 rc = dircache_updatefile(path, FILE32);
1617 } else if (elf.e_ident[EI_CLASS] == ELFCLASS64) {
1618 rc = dircache_updatefile(path, FILE64);
1619 } else {
1620 bam_print(_("WARNING: file %s is neither a "
1621 "32-bit nor a 64-bit ELF\n"), path);
1622 /* paranoid */
1623 rc = dircache_updatefile(path, FILE32);
1624 if (rc == BAM_SUCCESS)
1625 rc = dircache_updatefile(path, FILE64);
1628 break;
1630 case FTW_D:
1631 if (strstr(path, "/amd64") == NULL) {
1632 rc = dircache_updatedir(path, FILE32);
1633 } else {
1634 if (has_cachedir(FILE64)) {
1635 rc = dircache_updatedir(path, FILE64);
1638 break;
1639 default:
1640 rc = BAM_ERROR;
1641 break;
1644 return (rc);
1647 /*ARGSUSED*/
1648 static int
1649 cmpstat(
1650 const char *file,
1651 const struct stat *st,
1652 int flags,
1653 struct FTW *ftw)
1655 uint_t sz;
1656 uint64_t *value;
1657 uint64_t filestat[2];
1658 int error, ret, status;
1660 struct safefile *safefilep;
1661 FILE *fp;
1662 struct stat sb;
1663 regex_t re;
1666 * On SPARC we create/update links too.
1668 if (flags != FTW_F && flags != FTW_D && (flags == FTW_SL &&
1669 !is_flag_on(IS_SPARC_TARGET)))
1670 return (0);
1673 * Ignore broken links
1675 if (flags == FTW_SL && stat(file, &sb) < 0)
1676 return (0);
1679 * new_nvlp may be NULL if there were errors earlier
1680 * but this is not fatal to update determination.
1682 if (walk_arg.new_nvlp) {
1683 filestat[0] = st->st_size;
1684 filestat[1] = st->st_mtime;
1685 error = nvlist_add_uint64_array(walk_arg.new_nvlp,
1686 file + bam_rootlen, filestat, 2);
1687 if (error)
1688 bam_error(_("failed to update stat data for: %s: %s\n"),
1689 file, strerror(error));
1693 * If we are invoked as part of system/filesystem/boot-archive, then
1694 * there are a number of things we should not worry about
1696 if (bam_smf_check) {
1697 /* ignore amd64 modules unless we are booted amd64. */
1698 if (!is_amd64() && strstr(file, "/amd64/") != 0)
1699 return (0);
1701 /* read in list of safe files */
1702 if (safefiles == NULL) {
1703 fp = fopen("/boot/solaris/filelist.safe", "r");
1704 if (fp != NULL) {
1705 safefiles = s_calloc(1,
1706 sizeof (struct safefile));
1707 safefilep = safefiles;
1708 safefilep->name = s_calloc(1, MAXPATHLEN +
1709 MAXNAMELEN);
1710 safefilep->next = NULL;
1711 while (s_fgets(safefilep->name, MAXPATHLEN +
1712 MAXNAMELEN, fp) != NULL) {
1713 safefilep->next = s_calloc(1,
1714 sizeof (struct safefile));
1715 safefilep = safefilep->next;
1716 safefilep->name = s_calloc(1,
1717 MAXPATHLEN + MAXNAMELEN);
1718 safefilep->next = NULL;
1720 (void) fclose(fp);
1726 * On SPARC we create a -path-list file for mkisofs
1728 if (is_flag_on(IS_SPARC_TARGET) && !bam_nowrite()) {
1729 if (flags != FTW_D) {
1730 char *strip;
1732 strip = (char *)file + strlen(rootbuf);
1733 (void) fprintf(walk_arg.sparcfile, "/%s=%s\n", strip,
1734 file);
1739 * We are transitioning from the old model to the dircache or the cache
1740 * directory was removed: create the entry without further checkings.
1742 if (is_flag_on(NEED_CACHE_DIR)) {
1743 if (bam_verbose)
1744 bam_print(_(" new %s\n"), file);
1746 if (is_flag_on(IS_SPARC_TARGET)) {
1747 set_dir_flag(FILE64, NEED_UPDATE);
1748 return (0);
1751 ret = update_dircache(file, flags);
1752 if (ret == BAM_ERROR) {
1753 bam_error(_("directory cache update failed for %s\n"),
1754 file);
1755 return (-1);
1758 return (0);
1762 * We need an update if file doesn't exist in old archive
1764 if (walk_arg.old_nvlp == NULL ||
1765 nvlist_lookup_uint64_array(walk_arg.old_nvlp,
1766 file + bam_rootlen, &value, &sz) != 0) {
1767 if (bam_smf_check) /* ignore new during smf check */
1768 return (0);
1770 if (is_flag_on(IS_SPARC_TARGET)) {
1771 set_dir_flag(FILE64, NEED_UPDATE);
1772 } else {
1773 ret = update_dircache(file, flags);
1774 if (ret == BAM_ERROR) {
1775 bam_error(_("directory cache update "
1776 "failed for %s\n"), file);
1777 return (-1);
1781 if (bam_verbose)
1782 bam_print(_(" new %s\n"), file);
1783 return (0);
1787 * If we got there, the file is already listed as to be included in the
1788 * iso image. We just need to know if we are going to rebuild it or not
1790 if (is_flag_on(IS_SPARC_TARGET) &&
1791 is_dir_flag_on(FILE64, NEED_UPDATE) && !bam_nowrite())
1792 return (0);
1794 * File exists in old archive. Check if file has changed
1796 assert(sz == 2);
1797 bcopy(value, filestat, sizeof (filestat));
1799 if (flags != FTW_D && (filestat[0] != st->st_size ||
1800 filestat[1] != st->st_mtime)) {
1801 if (bam_smf_check) {
1802 safefilep = safefiles;
1803 while (safefilep != NULL &&
1804 safefilep->name[0] != '\0') {
1805 if (regcomp(&re, safefilep->name,
1806 REG_EXTENDED|REG_NOSUB) == 0) {
1807 status = regexec(&re,
1808 file + bam_rootlen, 0, NULL, 0);
1809 regfree(&re);
1810 if (status == 0) {
1811 (void) creat(
1812 NEED_UPDATE_SAFE_FILE,
1813 0644);
1814 return (0);
1817 safefilep = safefilep->next;
1821 if (is_flag_on(IS_SPARC_TARGET)) {
1822 set_dir_flag(FILE64, NEED_UPDATE);
1823 } else {
1824 ret = update_dircache(file, flags);
1825 if (ret == BAM_ERROR) {
1826 bam_error(_("directory cache update failed "
1827 "for %s\n"), file);
1828 return (-1);
1832 if (bam_verbose) {
1833 if (bam_smf_check)
1834 bam_print(" %s\n", file);
1835 else
1836 bam_print(_(" changed %s\n"), file);
1840 return (0);
1844 * Remove a directory path recursively
1846 static int
1847 rmdir_r(char *path)
1849 struct dirent *d = NULL;
1850 DIR *dir = NULL;
1851 char tpath[PATH_MAX];
1852 struct stat sb;
1854 if ((dir = opendir(path)) == NULL)
1855 return (-1);
1857 while ((d = readdir(dir)) != NULL) {
1858 if ((strcmp(d->d_name, ".") != 0) &&
1859 (strcmp(d->d_name, "..") != 0)) {
1860 (void) snprintf(tpath, sizeof (tpath), "%s/%s",
1861 path, d->d_name);
1862 if (stat(tpath, &sb) == 0) {
1863 if (sb.st_mode & S_IFDIR)
1864 (void) rmdir_r(tpath);
1865 else
1866 (void) remove(tpath);
1870 return (remove(path));
1874 * Check if cache directory exists and, if not, create it and update flags
1875 * accordingly. If the path exists, but it's not a directory, a best effort
1876 * attempt to remove and recreate it is made.
1877 * If the user requested a 'purge', always recreate the directory from scratch.
1879 static int
1880 set_cache_dir(char *root, int what)
1882 struct stat sb;
1883 int ret = 0;
1885 ret = snprintf(get_cachedir(what), sizeof (get_cachedir(what)),
1886 "%s%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(), what == FILE64 ?
1887 "/amd64" : "", CACHEDIR_SUFFIX);
1889 if (ret >= sizeof (get_cachedir(what))) {
1890 bam_error(_("unable to create path on mountpoint %s, "
1891 "path too long\n"), rootbuf);
1892 return (BAM_ERROR);
1895 if (bam_purge || is_flag_on(INVALIDATE_CACHE))
1896 (void) rmdir_r(get_cachedir(what));
1898 if (stat(get_cachedir(what), &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
1899 /* best effort unlink attempt, mkdir will catch errors */
1900 (void) unlink(get_cachedir(what));
1902 if (bam_verbose)
1903 bam_print(_("archive cache directory not found: %s\n"),
1904 get_cachedir(what));
1905 ret = mkdir(get_cachedir(what), DIR_PERMS);
1906 if (ret < 0) {
1907 bam_error(_("mkdir of %s failed: %s\n"),
1908 get_cachedir(what), strerror(errno));
1909 get_cachedir(what)[0] = '\0';
1910 return (ret);
1912 set_flag(NEED_CACHE_DIR);
1915 return (BAM_SUCCESS);
1918 static int
1919 is_valid_archive(char *root, int what)
1921 char archive_path[PATH_MAX];
1922 char timestamp_path[PATH_MAX];
1923 struct stat sb, timestamp;
1924 int ret;
1926 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
1927 ret = snprintf(archive_path, sizeof (archive_path),
1928 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
1929 ARCHIVE_SUFFIX);
1930 else
1931 ret = snprintf(archive_path, sizeof (archive_path), "%s%s%s%s",
1932 root, ARCHIVE_PREFIX, get_machine(), ARCHIVE_SUFFIX);
1934 if (ret >= sizeof (archive_path)) {
1935 bam_error(_("unable to create path on mountpoint %s, "
1936 "path too long\n"), rootbuf);
1937 return (BAM_ERROR);
1940 if (stat(archive_path, &sb) != 0) {
1941 if (bam_verbose && !bam_check)
1942 bam_print(_("archive not found: %s\n"), archive_path);
1943 set_dir_flag(what, NEED_UPDATE);
1944 return (BAM_SUCCESS);
1948 * The timestamp file is used to prevent stale files in the archive
1949 * cache.
1950 * Stale files can happen if the system is booted back and forth across
1951 * the transition from bootadm-before-the-cache to
1952 * bootadm-after-the-cache, since older versions of bootadm don't know
1953 * about the existence of the archive cache.
1955 * Since only bootadm-after-the-cache versions know about about this
1956 * file, we require that the boot archive be older than this file.
1958 ret = snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
1959 FILE_STAT_TIMESTAMP);
1961 if (ret >= sizeof (timestamp_path)) {
1962 bam_error(_("unable to create path on mountpoint %s, "
1963 "path too long\n"), rootbuf);
1964 return (BAM_ERROR);
1967 if (stat(timestamp_path, &timestamp) != 0 ||
1968 sb.st_mtime > timestamp.st_mtime) {
1969 if (bam_verbose && !bam_check)
1970 bam_print(
1971 _("archive cache is out of sync. Rebuilding.\n"));
1973 * Don't generate a false positive for the boot-archive service
1974 * but trigger an update of the archive cache in
1975 * boot-archive-update.
1977 if (bam_smf_check) {
1978 (void) creat(NEED_UPDATE_FILE, 0644);
1979 return (BAM_SUCCESS);
1982 set_flag(INVALIDATE_CACHE);
1983 set_dir_flag(what, NEED_UPDATE);
1984 return (BAM_SUCCESS);
1987 return (BAM_SUCCESS);
1991 * Check flags and presence of required files and directories.
1992 * The force flag and/or absence of files should
1993 * trigger an update.
1994 * Suppress stdout output if check (-n) option is set
1995 * (as -n should only produce parseable output.)
1997 static int
1998 check_flags_and_files(char *root)
2001 struct stat sb;
2002 int ret;
2005 * If archive is missing, create archive
2007 if (is_flag_on(IS_SPARC_TARGET)) {
2008 ret = is_valid_archive(root, FILE64);
2009 if (ret == BAM_ERROR)
2010 return (BAM_ERROR);
2011 } else {
2012 int what = FILE32;
2013 do {
2014 ret = is_valid_archive(root, what);
2015 if (ret == BAM_ERROR)
2016 return (BAM_ERROR);
2017 what++;
2018 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2021 if (bam_nowrite())
2022 return (BAM_SUCCESS);
2026 * check if cache directories exist on x86.
2027 * check (and always open) the cache file on SPARC.
2029 if (is_sparc()) {
2030 ret = snprintf(get_cachedir(FILE64),
2031 sizeof (get_cachedir(FILE64)), "%s%s%s/%s", root,
2032 ARCHIVE_PREFIX, get_machine(), CACHEDIR_SUFFIX);
2034 if (ret >= sizeof (get_cachedir(FILE64))) {
2035 bam_error(_("unable to create path on mountpoint %s, "
2036 "path too long\n"), rootbuf);
2037 return (BAM_ERROR);
2040 if (stat(get_cachedir(FILE64), &sb) != 0) {
2041 set_flag(NEED_CACHE_DIR);
2042 set_dir_flag(FILE64, NEED_UPDATE);
2045 walk_arg.sparcfile = fopen(get_cachedir(FILE64), "w");
2046 if (walk_arg.sparcfile == NULL) {
2047 bam_error(_("failed to open file: %s: %s\n"),
2048 get_cachedir(FILE64), strerror(errno));
2049 return (BAM_ERROR);
2052 set_dir_present(FILE64);
2053 } else {
2054 int what = FILE32;
2056 do {
2057 if (set_cache_dir(root, what) != 0)
2058 return (BAM_ERROR);
2060 set_dir_present(what);
2062 what++;
2063 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2067 * if force, create archive unconditionally
2069 if (bam_force) {
2070 if (!is_sparc())
2071 set_dir_flag(FILE32, NEED_UPDATE);
2072 set_dir_flag(FILE64, NEED_UPDATE);
2073 if (bam_verbose)
2074 bam_print(_("forced update of archive requested\n"));
2077 return (BAM_SUCCESS);
2080 static error_t
2081 read_one_list(char *root, filelist_t *flistp, char *filelist)
2083 char path[PATH_MAX];
2084 FILE *fp;
2085 char buf[BAM_MAXLINE];
2086 const char *fcn = "read_one_list()";
2088 (void) snprintf(path, sizeof (path), "%s%s", root, filelist);
2090 fp = fopen(path, "r");
2091 if (fp == NULL) {
2092 BAM_DPRINTF(("%s: failed to open archive filelist: %s: %s\n",
2093 fcn, path, strerror(errno)));
2094 return (BAM_ERROR);
2096 while (s_fgets(buf, sizeof (buf), fp) != NULL) {
2097 /* skip blank lines */
2098 if (strspn(buf, " \t") == strlen(buf))
2099 continue;
2100 append_to_flist(flistp, buf);
2102 if (fclose(fp) != 0) {
2103 bam_error(_("failed to close file: %s: %s\n"),
2104 path, strerror(errno));
2105 return (BAM_ERROR);
2107 return (BAM_SUCCESS);
2110 static error_t
2111 read_list(char *root, filelist_t *flistp)
2113 char path[PATH_MAX];
2114 char cmd[PATH_MAX];
2115 struct stat sb;
2116 int n, rval;
2117 const char *fcn = "read_list()";
2119 flistp->head = flistp->tail = NULL;
2122 * build and check path to extract_boot_filelist.ksh
2124 n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
2125 if (n >= sizeof (path)) {
2126 bam_error(_("archive filelist is empty\n"));
2127 return (BAM_ERROR);
2130 if (is_safe_exec(path) == BAM_ERROR)
2131 return (BAM_ERROR);
2134 * If extract_boot_filelist is present, exec it, otherwise read
2135 * the filelists directly, for compatibility with older images.
2137 if (stat(path, &sb) == 0) {
2139 * build arguments to exec extract_boot_filelist.ksh
2141 char *rootarg, *platarg;
2142 int platarglen = 1, rootarglen = 1;
2143 if (strlen(root) > 1)
2144 rootarglen += strlen(root) + strlen("-R ");
2145 if (bam_alt_platform)
2146 platarglen += strlen(bam_platform) + strlen("-p ");
2147 platarg = s_calloc(1, platarglen);
2148 rootarg = s_calloc(1, rootarglen);
2149 *platarg = 0;
2150 *rootarg = 0;
2152 if (strlen(root) > 1) {
2153 (void) snprintf(rootarg, rootarglen,
2154 "-R %s", root);
2156 if (bam_alt_platform) {
2157 (void) snprintf(platarg, platarglen,
2158 "-p %s", bam_platform);
2160 n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s",
2161 path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST);
2162 free(platarg);
2163 free(rootarg);
2164 if (n >= sizeof (cmd)) {
2165 bam_error(_("archive filelist is empty\n"));
2166 return (BAM_ERROR);
2168 if (exec_cmd(cmd, flistp) != 0) {
2169 BAM_DPRINTF(("%s: failed to open archive "
2170 "filelist: %s: %s\n", fcn, path, strerror(errno)));
2171 return (BAM_ERROR);
2173 } else {
2175 * Read current lists of files - only the first is mandatory
2177 rval = read_one_list(root, flistp, BOOT_FILE_LIST);
2178 if (rval != BAM_SUCCESS)
2179 return (rval);
2180 (void) read_one_list(root, flistp, ETC_FILE_LIST);
2183 if (flistp->head == NULL) {
2184 bam_error(_("archive filelist is empty\n"));
2185 return (BAM_ERROR);
2188 return (BAM_SUCCESS);
2191 static void
2192 getoldstat(char *root)
2194 char path[PATH_MAX];
2195 int fd, error;
2196 struct stat sb;
2197 char *ostat;
2199 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
2200 fd = open(path, O_RDONLY);
2201 if (fd == -1) {
2202 if (bam_verbose)
2203 bam_print(_("failed to open file: %s: %s\n"),
2204 path, strerror(errno));
2205 goto out_err;
2208 if (fstat(fd, &sb) != 0) {
2209 bam_error(_("stat of file failed: %s: %s\n"), path,
2210 strerror(errno));
2211 goto out_err;
2214 ostat = s_calloc(1, sb.st_size);
2216 if (read(fd, ostat, sb.st_size) != sb.st_size) {
2217 bam_error(_("read failed for file: %s: %s\n"), path,
2218 strerror(errno));
2219 free(ostat);
2220 goto out_err;
2223 (void) close(fd);
2224 fd = -1;
2226 walk_arg.old_nvlp = NULL;
2227 error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
2229 free(ostat);
2231 if (error) {
2232 bam_error(_("failed to unpack stat data: %s: %s\n"),
2233 path, strerror(error));
2234 walk_arg.old_nvlp = NULL;
2235 goto out_err;
2236 } else {
2237 return;
2240 out_err:
2241 if (fd != -1)
2242 (void) close(fd);
2243 if (!is_flag_on(IS_SPARC_TARGET))
2244 set_dir_flag(FILE32, NEED_UPDATE);
2245 set_dir_flag(FILE64, NEED_UPDATE);
2248 /* Best effort stale entry removal */
2249 static void
2250 delete_stale(char *file, int what)
2252 char path[PATH_MAX];
2253 struct stat sb;
2255 (void) snprintf(path, sizeof (path), "%s/%s", get_cachedir(what), file);
2256 if (!bam_check && stat(path, &sb) == 0) {
2257 if (sb.st_mode & S_IFDIR)
2258 (void) rmdir_r(path);
2259 else
2260 (void) unlink(path);
2262 set_dir_flag(what, NEED_UPDATE);
2267 * Checks if a file in the current (old) archive has
2268 * been deleted from the root filesystem.
2270 static void
2271 check4stale(char *root)
2273 nvpair_t *nvp;
2274 nvlist_t *nvlp;
2275 char *file;
2276 char path[PATH_MAX];
2279 * Skip stale file check during smf check
2281 if (bam_smf_check)
2282 return;
2285 * If we need to (re)create the cache, there's no need to check for
2286 * stale files
2288 if (is_flag_on(NEED_CACHE_DIR))
2289 return;
2291 /* Nothing to do if no old stats */
2292 if ((nvlp = walk_arg.old_nvlp) == NULL)
2293 return;
2295 for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
2296 nvp = nvlist_next_nvpair(nvlp, nvp)) {
2297 file = nvpair_name(nvp);
2298 if (file == NULL)
2299 continue;
2300 (void) snprintf(path, sizeof (path), "%s/%s",
2301 root, file);
2302 if (access(path, F_OK) < 0) {
2303 int what;
2305 if (bam_verbose)
2306 bam_print(_(" stale %s\n"), path);
2308 if (is_flag_on(IS_SPARC_TARGET)) {
2309 set_dir_flag(FILE64, NEED_UPDATE);
2310 } else {
2311 for (what = FILE32; what < CACHEDIR_NUM; what++)
2312 if (has_cachedir(what))
2313 delete_stale(file, what);
2319 static void
2320 create_newstat(void)
2322 int error;
2324 error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
2325 if (error) {
2327 * Not fatal - we can still create archive
2329 walk_arg.new_nvlp = NULL;
2330 bam_error(_("failed to create stat data: %s\n"),
2331 strerror(error));
2335 static int
2336 walk_list(char *root, filelist_t *flistp)
2338 char path[PATH_MAX];
2339 line_t *lp;
2341 for (lp = flistp->head; lp; lp = lp->next) {
2343 * Don't follow symlinks. A symlink must refer to
2344 * a file that would appear in the archive through
2345 * a direct reference. This matches the archive
2346 * construction behavior.
2348 (void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
2349 if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
2350 if (is_flag_on(UPDATE_ERROR))
2351 return (BAM_ERROR);
2353 * Some files may not exist.
2354 * For example: etc/rtc_config on a x86 diskless system
2355 * Emit verbose message only
2357 if (bam_verbose)
2358 bam_print(_("cannot find: %s: %s\n"),
2359 path, strerror(errno));
2363 return (BAM_SUCCESS);
2367 * Update the timestamp file.
2369 static void
2370 update_timestamp(char *root)
2372 char timestamp_path[PATH_MAX];
2374 /* this path length has already been checked in check_flags_and_files */
2375 (void) snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
2376 FILE_STAT_TIMESTAMP);
2379 * recreate the timestamp file. Since an outdated or absent timestamp
2380 * file translates in a complete rebuild of the archive cache, notify
2381 * the user of the performance issue.
2383 if (creat(timestamp_path, FILE_STAT_MODE) < 0) {
2384 bam_error(_("failed to open file: %s: %s\n"), timestamp_path,
2385 strerror(errno));
2386 bam_error(_("failed to update the timestamp file, next"
2387 " archive update may experience reduced performance\n"));
2392 static void
2393 savenew(char *root)
2395 char path[PATH_MAX];
2396 char path2[PATH_MAX];
2397 size_t sz;
2398 char *nstat;
2399 int fd, wrote, error;
2401 nstat = NULL;
2402 sz = 0;
2403 error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
2404 NV_ENCODE_XDR, 0);
2405 if (error) {
2406 bam_error(_("failed to pack stat data: %s\n"),
2407 strerror(error));
2408 return;
2411 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
2412 fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
2413 if (fd == -1) {
2414 bam_error(_("failed to open file: %s: %s\n"), path,
2415 strerror(errno));
2416 free(nstat);
2417 return;
2419 wrote = write(fd, nstat, sz);
2420 if (wrote != sz) {
2421 bam_error(_("write to file failed: %s: %s\n"), path,
2422 strerror(errno));
2423 (void) close(fd);
2424 free(nstat);
2425 return;
2427 (void) close(fd);
2428 free(nstat);
2430 (void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
2431 if (rename(path, path2) != 0) {
2432 bam_error(_("rename to file failed: %s: %s\n"), path2,
2433 strerror(errno));
2437 #define init_walk_args() bzero(&walk_arg, sizeof (walk_arg))
2439 static void
2440 clear_walk_args(void)
2442 nvlist_free(walk_arg.old_nvlp);
2443 nvlist_free(walk_arg.new_nvlp);
2444 if (walk_arg.sparcfile)
2445 (void) fclose(walk_arg.sparcfile);
2446 walk_arg.old_nvlp = NULL;
2447 walk_arg.new_nvlp = NULL;
2448 walk_arg.sparcfile = NULL;
2452 * Returns:
2453 * 0 - no update necessary
2454 * 1 - update required.
2455 * BAM_ERROR (-1) - An error occurred
2457 * Special handling for check (-n):
2458 * ================================
2459 * The check (-n) option produces parseable output.
2460 * To do this, we suppress all stdout messages unrelated
2461 * to out of sync files.
2462 * All stderr messages are still printed though.
2465 static int
2466 update_required(char *root)
2468 struct stat sb;
2469 char path[PATH_MAX];
2470 filelist_t flist;
2471 filelist_t *flistp = &flist;
2472 int ret;
2474 flistp->head = flistp->tail = NULL;
2476 if (is_sparc())
2477 set_flag(IS_SPARC_TARGET);
2480 * Check if cache directories and archives are present
2483 ret = check_flags_and_files(root);
2484 if (ret < 0)
2485 return (BAM_ERROR);
2488 * In certain deployment scenarios, filestat may not
2489 * exist. Do not stop the boot process, but trigger an update
2490 * of the archives (which will recreate filestat.ramdisk).
2492 if (bam_smf_check) {
2493 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
2494 if (stat(path, &sb) != 0) {
2495 (void) creat(NEED_UPDATE_FILE, 0644);
2496 return (0);
2500 getoldstat(root);
2503 * Check if the archive contains files that are no longer
2504 * present on the root filesystem.
2506 check4stale(root);
2509 * read list of files
2511 if (read_list(root, flistp) != BAM_SUCCESS) {
2512 clear_walk_args();
2513 return (BAM_ERROR);
2516 assert(flistp->head && flistp->tail);
2519 * At this point either the update is required
2520 * or the decision is pending. In either case
2521 * we need to create new stat nvlist
2523 create_newstat();
2525 * This walk does 2 things:
2526 * - gets new stat data for every file
2527 * - (optional) compare old and new stat data
2529 ret = walk_list(root, &flist);
2531 /* done with the file list */
2532 filelist_free(flistp);
2534 /* something went wrong */
2536 if (ret == BAM_ERROR) {
2537 bam_error(_("Failed to gather cache files, archives "
2538 "generation aborted\n"));
2539 return (BAM_ERROR);
2542 if (walk_arg.new_nvlp == NULL) {
2543 if (walk_arg.sparcfile != NULL)
2544 (void) fclose(walk_arg.sparcfile);
2545 bam_error(_("cannot create new stat data\n"));
2548 /* If nothing was updated, discard newstat. */
2550 if (!is_dir_flag_on(FILE32, NEED_UPDATE) &&
2551 !is_dir_flag_on(FILE64, NEED_UPDATE)) {
2552 clear_walk_args();
2553 return (0);
2556 if (walk_arg.sparcfile != NULL)
2557 (void) fclose(walk_arg.sparcfile);
2559 return (1);
2562 static boolean_t
2563 is_be(char *root)
2565 zfs_handle_t *zhp;
2566 libzfs_handle_t *hdl;
2567 be_node_list_t *be_nodes = NULL;
2568 be_node_list_t *cur_be;
2569 boolean_t be_exist = B_FALSE;
2570 char ds_path[ZFS_MAX_DATASET_NAME_LEN];
2572 if (!is_zfs(root))
2573 return (B_FALSE);
2575 * Get dataset for mountpoint
2577 if ((hdl = libzfs_init()) == NULL)
2578 return (B_FALSE);
2580 if ((zhp = zfs_path_to_zhandle(hdl, root,
2581 ZFS_TYPE_FILESYSTEM)) == NULL) {
2582 libzfs_fini(hdl);
2583 return (B_FALSE);
2586 (void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
2589 * Check if the current dataset is BE
2591 if (be_list(NULL, &be_nodes) == BE_SUCCESS) {
2592 for (cur_be = be_nodes; cur_be != NULL;
2593 cur_be = cur_be->be_next_node) {
2596 * Because we guarantee that cur_be->be_root_ds
2597 * is null-terminated by internal data structure,
2598 * we can safely use strcmp()
2600 if (strcmp(ds_path, cur_be->be_root_ds) == 0) {
2601 be_exist = B_TRUE;
2602 break;
2605 be_free_list(be_nodes);
2607 zfs_close(zhp);
2608 libzfs_fini(hdl);
2610 return (be_exist);
2613 static error_t
2614 create_ramdisk(char *root)
2616 char *cmdline, path[PATH_MAX];
2617 size_t len;
2618 struct stat sb;
2621 * Setup command args for create_ramdisk.ksh for the cpio archives
2622 * Note: we will not create hash here, CREATE_RAMDISK should create it.
2625 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
2626 if (stat(path, &sb) != 0) {
2627 bam_error(_("archive creation file not found: %s: %s\n"),
2628 path, strerror(errno));
2629 return (BAM_ERROR);
2632 if (is_safe_exec(path) == BAM_ERROR)
2633 return (BAM_ERROR);
2635 len = strlen(path) + strlen(root) + 10; /* room for space + -R */
2636 if (bam_alt_platform)
2637 len += strlen(bam_platform) + strlen("-p ");
2638 cmdline = s_calloc(1, len);
2640 if (bam_alt_platform) {
2641 assert(strlen(root) > 1);
2642 (void) snprintf(cmdline, len, "%s -p %s -R %s",
2643 path, bam_platform, root);
2644 /* chop off / at the end */
2645 cmdline[strlen(cmdline) - 1] = '\0';
2646 } else if (strlen(root) > 1) {
2647 (void) snprintf(cmdline, len, "%s -R %s", path, root);
2648 /* chop off / at the end */
2649 cmdline[strlen(cmdline) - 1] = '\0';
2650 } else
2651 (void) snprintf(cmdline, len, "%s", path);
2653 if (exec_cmd(cmdline, NULL) != 0) {
2654 bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
2655 cmdline);
2656 free(cmdline);
2657 return (BAM_ERROR);
2659 free(cmdline);
2661 * The existence of the expected archives used to be
2662 * verified here. This check is done in create_ramdisk as
2663 * it needs to be in sync with the altroot operated upon.
2665 return (BAM_SUCCESS);
2669 * Checks if target filesystem is on a ramdisk
2670 * 1 - is miniroot
2671 * 0 - is not
2672 * When in doubt assume it is not a ramdisk.
2674 static int
2675 is_ramdisk(char *root)
2677 struct extmnttab mnt;
2678 FILE *fp;
2679 int found;
2680 char mntpt[PATH_MAX];
2681 char *cp;
2684 * There are 3 situations where creating archive is
2685 * of dubious value:
2686 * - create boot_archive on a lofi-mounted boot_archive
2687 * - create it on a ramdisk which is the root filesystem
2688 * - create it on a ramdisk mounted somewhere else
2689 * The first is not easy to detect and checking for it is not
2690 * worth it.
2691 * The other two conditions are handled here
2693 fp = fopen(MNTTAB, "r");
2694 if (fp == NULL) {
2695 bam_error(_("failed to open file: %s: %s\n"),
2696 MNTTAB, strerror(errno));
2697 return (0);
2700 resetmnttab(fp);
2703 * Remove any trailing / from the mount point
2705 (void) strlcpy(mntpt, root, sizeof (mntpt));
2706 if (strcmp(root, "/") != 0) {
2707 cp = mntpt + strlen(mntpt) - 1;
2708 if (*cp == '/')
2709 *cp = '\0';
2711 found = 0;
2712 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
2713 if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
2714 found = 1;
2715 break;
2719 if (!found) {
2720 if (bam_verbose)
2721 bam_error(_("alternate root %s not in mnttab\n"),
2722 mntpt);
2723 (void) fclose(fp);
2724 return (0);
2727 if (strncmp(mnt.mnt_special, RAMDISK_SPECIAL,
2728 strlen(RAMDISK_SPECIAL)) == 0) {
2729 if (bam_verbose)
2730 bam_error(_("%s is on a ramdisk device\n"), bam_root);
2731 (void) fclose(fp);
2732 return (1);
2735 (void) fclose(fp);
2737 return (0);
2740 static int
2741 is_boot_archive(char *root)
2743 char path[PATH_MAX];
2744 struct stat sb;
2745 int error;
2746 const char *fcn = "is_boot_archive()";
2749 * We can't create an archive without the create_ramdisk script
2751 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
2752 error = stat(path, &sb);
2753 INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1);
2754 if (error == -1) {
2755 if (bam_verbose)
2756 bam_print(_("file not found: %s\n"), path);
2757 BAM_DPRINTF(("%s: not a boot archive based Solaris "
2758 "instance: %s\n", fcn, root));
2759 return (0);
2762 BAM_DPRINTF(("%s: *IS* a boot archive based Solaris instance: %s\n",
2763 fcn, root));
2764 return (1);
2768 is_zfs(char *root)
2770 struct statvfs vfs;
2771 int ret;
2772 const char *fcn = "is_zfs()";
2774 ret = statvfs(root, &vfs);
2775 INJECT_ERROR1("STATVFS_ZFS", ret = 1);
2776 if (ret != 0) {
2777 bam_error(_("statvfs failed for %s: %s\n"), root,
2778 strerror(errno));
2779 return (0);
2782 if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
2783 BAM_DPRINTF(("%s: is a ZFS filesystem: %s\n", fcn, root));
2784 return (1);
2785 } else {
2786 BAM_DPRINTF(("%s: is *NOT* a ZFS filesystem: %s\n", fcn, root));
2787 return (0);
2791 static int
2792 is_readonly(char *root)
2794 int fd;
2795 int error;
2796 char testfile[PATH_MAX];
2797 const char *fcn = "is_readonly()";
2800 * Using statvfs() to check for a read-only filesystem is not
2801 * reliable. The only way to reliably test is to attempt to
2802 * create a file
2804 (void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
2805 root, BOOTADM_RDONLY_TEST, getpid());
2807 (void) unlink(testfile);
2809 errno = 0;
2810 fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
2811 error = errno;
2812 INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES);
2813 if (fd == -1 && error == EROFS) {
2814 BAM_DPRINTF(("%s: is a READONLY filesystem: %s\n", fcn, root));
2815 return (1);
2816 } else if (fd == -1) {
2817 bam_error(_("error during read-only test on %s: %s\n"),
2818 root, strerror(error));
2821 (void) close(fd);
2822 (void) unlink(testfile);
2824 BAM_DPRINTF(("%s: is a RDWR filesystem: %s\n", fcn, root));
2825 return (0);
2828 static error_t
2829 update_archive(char *root, char *opt)
2831 error_t ret;
2833 assert(root);
2834 assert(opt == NULL);
2836 init_walk_args();
2837 (void) umask(022);
2840 * Never update non-BE root in update_all
2842 if (!is_be(root) && bam_update_all)
2843 return (BAM_SUCCESS);
2845 * root must belong to a boot archive based OS,
2847 if (!is_boot_archive(root)) {
2849 * Emit message only if not in context of update_all.
2850 * If in update_all, emit only if verbose flag is set.
2852 if (!bam_update_all || bam_verbose)
2853 bam_print(_("%s: not a boot archive based Solaris "
2854 "instance\n"), root);
2855 return (BAM_ERROR);
2859 * If smf check is requested when / is writable (can happen
2860 * on first reboot following an upgrade because service
2861 * dependency is messed up), skip the check.
2863 if (bam_smf_check && !bam_root_readonly && !is_zfs(root))
2864 return (BAM_SUCCESS);
2867 * Don't generate archive on ramdisk.
2869 if (is_ramdisk(root))
2870 return (BAM_SUCCESS);
2873 * root must be writable. This check applies to alternate
2874 * root (-R option); bam_root_readonly applies to '/' only.
2875 * The behaviour translates into being the one of a 'check'.
2877 if (!bam_smf_check && !bam_check && is_readonly(root)) {
2878 set_flag(RDONLY_FSCHK);
2879 bam_check = 1;
2883 * Now check if an update is really needed.
2885 ret = update_required(root);
2888 * The check command (-n) is *not* a dry run.
2889 * It only checks if the archive is in sync.
2890 * A readonly filesystem has to be considered an error only if an update
2891 * is required.
2893 if (bam_nowrite()) {
2894 if (is_flag_on(RDONLY_FSCHK)) {
2895 bam_check = bam_saved_check;
2896 if (ret > 0)
2897 bam_error(_("%s filesystem is read-only, "
2898 "skipping archives update\n"), root);
2899 if (bam_update_all)
2900 return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
2903 bam_exit((ret != 0) ? 1 : 0);
2906 if (ret == 1) {
2907 /* create the ramdisk */
2908 ret = create_ramdisk(root);
2912 * if the archive is updated, save the new stat data and update the
2913 * timestamp file
2915 if (ret == 0 && walk_arg.new_nvlp != NULL) {
2916 savenew(root);
2917 update_timestamp(root);
2920 clear_walk_args();
2922 return (ret);
2925 static error_t
2926 update_all(char *root, char *opt)
2928 struct extmnttab mnt;
2929 struct stat sb;
2930 FILE *fp;
2931 char multibt[PATH_MAX];
2932 char creatram[PATH_MAX];
2933 error_t ret = BAM_SUCCESS;
2935 assert(root);
2936 assert(opt == NULL);
2938 if (bam_rootlen != 1 || *root != '/') {
2939 elide_trailing_slash(root, multibt, sizeof (multibt));
2940 bam_error(_("an alternate root (%s) cannot be used with this "
2941 "sub-command\n"), multibt);
2942 return (BAM_ERROR);
2946 * First update archive for current root
2948 if (update_archive(root, opt) != BAM_SUCCESS)
2949 ret = BAM_ERROR;
2951 if (ret == BAM_ERROR)
2952 goto out;
2955 * Now walk the mount table, performing archive update
2956 * for all mounted Newboot root filesystems
2958 fp = fopen(MNTTAB, "r");
2959 if (fp == NULL) {
2960 bam_error(_("failed to open file: %s: %s\n"),
2961 MNTTAB, strerror(errno));
2962 ret = BAM_ERROR;
2963 goto out;
2966 resetmnttab(fp);
2968 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
2969 if (mnt.mnt_special == NULL)
2970 continue;
2971 if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) &&
2972 (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0))
2973 continue;
2974 if (strcmp(mnt.mnt_mountp, "/") == 0)
2975 continue;
2977 (void) snprintf(creatram, sizeof (creatram), "%s/%s",
2978 mnt.mnt_mountp, CREATE_RAMDISK);
2980 if (stat(creatram, &sb) == -1)
2981 continue;
2984 * We put a trailing slash to be consistent with root = "/"
2985 * case, such that we don't have to print // in some cases.
2987 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
2988 mnt.mnt_mountp);
2989 bam_rootlen = strlen(rootbuf);
2992 * It's possible that other mounts may be an alternate boot
2993 * architecture, so check it again.
2995 if ((get_boot_cap(rootbuf) != BAM_SUCCESS) ||
2996 (update_archive(rootbuf, opt) != BAM_SUCCESS))
2997 ret = BAM_ERROR;
3000 (void) fclose(fp);
3002 out:
3004 * We no longer use biosdev for Live Upgrade. Hence
3005 * there is no need to defer (to shutdown time) any fdisk
3006 * updates
3008 if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) {
3009 bam_error(_("Deferred FDISK update file(s) found: %s, %s. "
3010 "Not supported.\n"), GRUB_fdisk, GRUB_fdisk_target);
3013 return (ret);
3016 static char *
3017 get_mountpoint(char *special, char *fstype)
3019 FILE *mntfp;
3020 struct mnttab mp = {0};
3021 struct mnttab mpref = {0};
3022 int error;
3023 int ret;
3024 const char *fcn = "get_mountpoint()";
3026 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, special, fstype));
3028 mntfp = fopen(MNTTAB, "r");
3029 error = errno;
3030 INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL);
3031 if (mntfp == NULL) {
3032 bam_error(_("failed to open file: %s: %s\n"),
3033 MNTTAB, strerror(error));
3034 return (NULL);
3037 mpref.mnt_special = special;
3038 mpref.mnt_fstype = fstype;
3040 ret = getmntany(mntfp, &mp, &mpref);
3041 INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
3042 if (ret != 0) {
3043 (void) fclose(mntfp);
3044 BAM_DPRINTF(("%s: no mount-point for special=%s and "
3045 "fstype=%s\n", fcn, special, fstype));
3046 return (NULL);
3048 (void) fclose(mntfp);
3050 assert(mp.mnt_mountp);
3052 BAM_DPRINTF(("%s: returning mount-point for special %s: %s\n",
3053 fcn, special, mp.mnt_mountp));
3055 return (s_strdup(mp.mnt_mountp));
3059 * Mounts a "legacy" top dataset (if needed)
3060 * Returns: The mountpoint of the legacy top dataset or NULL on error
3061 * mnted returns one of the above values defined for zfs_mnted_t
3063 static char *
3064 mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
3066 char cmd[PATH_MAX];
3067 char tmpmnt[PATH_MAX];
3068 filelist_t flist = {0};
3069 char *is_mounted;
3070 struct stat sb;
3071 int ret;
3072 const char *fcn = "mount_legacy_dataset()";
3074 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, pool));
3076 *mnted = ZFS_MNT_ERROR;
3078 (void) snprintf(cmd, sizeof (cmd),
3079 "/sbin/zfs get -Ho value mounted %s",
3080 pool);
3082 ret = exec_cmd(cmd, &flist);
3083 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
3084 if (ret != 0) {
3085 bam_error(_("failed to determine mount status of ZFS "
3086 "pool %s\n"), pool);
3087 return (NULL);
3090 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL);
3091 if ((flist.head == NULL) || (flist.head != flist.tail)) {
3092 bam_error(_("ZFS pool %s has bad mount status\n"), pool);
3093 filelist_free(&flist);
3094 return (NULL);
3097 is_mounted = strtok(flist.head->line, " \t\n");
3098 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes");
3099 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no");
3100 if (strcmp(is_mounted, "no") != 0) {
3101 filelist_free(&flist);
3102 *mnted = LEGACY_ALREADY;
3103 /* get_mountpoint returns a strdup'ed string */
3104 BAM_DPRINTF(("%s: legacy pool %s already mounted\n",
3105 fcn, pool));
3106 return (get_mountpoint(pool, "zfs"));
3109 filelist_free(&flist);
3112 * legacy top dataset is not mounted. Mount it now
3113 * First create a mountpoint.
3115 (void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d",
3116 ZFS_LEGACY_MNTPT, getpid());
3118 ret = stat(tmpmnt, &sb);
3119 if (ret == -1) {
3120 BAM_DPRINTF(("%s: legacy pool %s mount-point %s absent\n",
3121 fcn, pool, tmpmnt));
3122 ret = mkdirp(tmpmnt, DIR_PERMS);
3123 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1);
3124 if (ret == -1) {
3125 bam_error(_("mkdir of %s failed: %s\n"), tmpmnt,
3126 strerror(errno));
3127 return (NULL);
3129 } else {
3130 BAM_DPRINTF(("%s: legacy pool %s mount-point %s is already "
3131 "present\n", fcn, pool, tmpmnt));
3134 (void) snprintf(cmd, sizeof (cmd),
3135 "/sbin/mount -F zfs %s %s",
3136 pool, tmpmnt);
3138 ret = exec_cmd(cmd, NULL);
3139 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
3140 if (ret != 0) {
3141 bam_error(_("mount of ZFS pool %s failed\n"), pool);
3142 (void) rmdir(tmpmnt);
3143 return (NULL);
3146 *mnted = LEGACY_MOUNTED;
3147 BAM_DPRINTF(("%s: legacy pool %s successfully mounted at %s\n",
3148 fcn, pool, tmpmnt));
3149 return (s_strdup(tmpmnt));
3153 * Mounts the top dataset (if needed)
3154 * Returns: The mountpoint of the top dataset or NULL on error
3155 * mnted returns one of the above values defined for zfs_mnted_t
3157 char *
3158 mount_top_dataset(char *pool, zfs_mnted_t *mnted)
3160 char cmd[PATH_MAX];
3161 filelist_t flist = {0};
3162 char *is_mounted;
3163 char *mntpt;
3164 char *zmntpt;
3165 int ret;
3166 const char *fcn = "mount_top_dataset()";
3168 *mnted = ZFS_MNT_ERROR;
3170 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, pool));
3173 * First check if the top dataset is a "legacy" dataset
3175 (void) snprintf(cmd, sizeof (cmd),
3176 "/sbin/zfs get -Ho value mountpoint %s",
3177 pool);
3178 ret = exec_cmd(cmd, &flist);
3179 INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
3180 if (ret != 0) {
3181 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
3182 pool);
3183 return (NULL);
3186 if (flist.head && (flist.head == flist.tail)) {
3187 char *legacy = strtok(flist.head->line, " \t\n");
3188 if (legacy && strcmp(legacy, "legacy") == 0) {
3189 filelist_free(&flist);
3190 BAM_DPRINTF(("%s: is legacy, pool=%s\n", fcn, pool));
3191 return (mount_legacy_dataset(pool, mnted));
3195 filelist_free(&flist);
3197 BAM_DPRINTF(("%s: is *NOT* legacy, pool=%s\n", fcn, pool));
3199 (void) snprintf(cmd, sizeof (cmd),
3200 "/sbin/zfs get -Ho value mounted %s",
3201 pool);
3203 ret = exec_cmd(cmd, &flist);
3204 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
3205 if (ret != 0) {
3206 bam_error(_("failed to determine mount status of ZFS "
3207 "pool %s\n"), pool);
3208 return (NULL);
3211 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL);
3212 if ((flist.head == NULL) || (flist.head != flist.tail)) {
3213 bam_error(_("ZFS pool %s has bad mount status\n"), pool);
3214 filelist_free(&flist);
3215 return (NULL);
3218 is_mounted = strtok(flist.head->line, " \t\n");
3219 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes");
3220 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no");
3221 if (strcmp(is_mounted, "no") != 0) {
3222 filelist_free(&flist);
3223 *mnted = ZFS_ALREADY;
3224 BAM_DPRINTF(("%s: non-legacy pool %s mounted already\n",
3225 fcn, pool));
3226 goto mounted;
3229 filelist_free(&flist);
3230 BAM_DPRINTF(("%s: non-legacy pool %s *NOT* already mounted\n",
3231 fcn, pool));
3233 /* top dataset is not mounted. Mount it now */
3234 (void) snprintf(cmd, sizeof (cmd),
3235 "/sbin/zfs mount %s", pool);
3236 ret = exec_cmd(cmd, NULL);
3237 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1);
3238 if (ret != 0) {
3239 bam_error(_("mount of ZFS pool %s failed\n"), pool);
3240 return (NULL);
3242 *mnted = ZFS_MOUNTED;
3243 BAM_DPRINTF(("%s: non-legacy pool %s mounted now\n", fcn, pool));
3244 /*FALLTHRU*/
3245 mounted:
3247 * Now get the mountpoint
3249 (void) snprintf(cmd, sizeof (cmd),
3250 "/sbin/zfs get -Ho value mountpoint %s",
3251 pool);
3253 ret = exec_cmd(cmd, &flist);
3254 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
3255 if (ret != 0) {
3256 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
3257 pool);
3258 goto error;
3261 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL);
3262 if ((flist.head == NULL) || (flist.head != flist.tail)) {
3263 bam_error(_("ZFS pool %s has no mount-point\n"), pool);
3264 goto error;
3267 mntpt = strtok(flist.head->line, " \t\n");
3268 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo");
3269 if (*mntpt != '/') {
3270 bam_error(_("ZFS pool %s has bad mount-point %s\n"),
3271 pool, mntpt);
3272 goto error;
3274 zmntpt = s_strdup(mntpt);
3276 filelist_free(&flist);
3278 BAM_DPRINTF(("%s: non-legacy pool %s is mounted at %s\n",
3279 fcn, pool, zmntpt));
3281 return (zmntpt);
3283 error:
3284 filelist_free(&flist);
3285 (void) umount_top_dataset(pool, *mnted, NULL);
3286 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
3287 return (NULL);
3291 umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
3293 char cmd[PATH_MAX];
3294 int ret;
3295 const char *fcn = "umount_top_dataset()";
3297 INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
3298 switch (mnted) {
3299 case LEGACY_ALREADY:
3300 case ZFS_ALREADY:
3301 /* nothing to do */
3302 BAM_DPRINTF(("%s: pool %s was already mounted at %s, Nothing "
3303 "to umount\n", fcn, pool, mntpt ? mntpt : "NULL"));
3304 free(mntpt);
3305 return (BAM_SUCCESS);
3306 case LEGACY_MOUNTED:
3307 (void) snprintf(cmd, sizeof (cmd),
3308 "/sbin/umount %s", pool);
3309 ret = exec_cmd(cmd, NULL);
3310 INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1);
3311 if (ret != 0) {
3312 bam_error(_("umount of %s failed\n"), pool);
3313 free(mntpt);
3314 return (BAM_ERROR);
3316 if (mntpt)
3317 (void) rmdir(mntpt);
3318 free(mntpt);
3319 BAM_DPRINTF(("%s: legacy pool %s was mounted by us, "
3320 "successfully unmounted\n", fcn, pool));
3321 return (BAM_SUCCESS);
3322 case ZFS_MOUNTED:
3323 free(mntpt);
3324 (void) snprintf(cmd, sizeof (cmd),
3325 "/sbin/zfs unmount %s", pool);
3326 ret = exec_cmd(cmd, NULL);
3327 INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1);
3328 if (ret != 0) {
3329 bam_error(_("umount of %s failed\n"), pool);
3330 return (BAM_ERROR);
3332 BAM_DPRINTF(("%s: nonleg pool %s was mounted by us, "
3333 "successfully unmounted\n", fcn, pool));
3334 return (BAM_SUCCESS);
3335 default:
3336 bam_error(_("Internal error: bad saved mount state for "
3337 "pool %s\n"), pool);
3338 return (BAM_ERROR);
3340 /*NOTREACHED*/
3343 char *
3344 get_special(char *mountp)
3346 FILE *mntfp;
3347 struct mnttab mp = {0};
3348 struct mnttab mpref = {0};
3349 int error;
3350 int ret;
3351 const char *fcn = "get_special()";
3353 INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL);
3354 if (mountp == NULL) {
3355 bam_error(_("cannot get special file: NULL mount-point\n"));
3356 return (NULL);
3359 mntfp = fopen(MNTTAB, "r");
3360 error = errno;
3361 INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL);
3362 if (mntfp == NULL) {
3363 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
3364 strerror(error));
3365 return (NULL);
3368 if (*mountp == '\0')
3369 mpref.mnt_mountp = "/";
3370 else
3371 mpref.mnt_mountp = mountp;
3373 ret = getmntany(mntfp, &mp, &mpref);
3374 INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1);
3375 if (ret != 0) {
3376 (void) fclose(mntfp);
3377 BAM_DPRINTF(("%s: Cannot get special file: mount-point %s "
3378 "not in mnttab\n", fcn, mountp));
3379 return (NULL);
3381 (void) fclose(mntfp);
3383 BAM_DPRINTF(("%s: returning special: %s\n", fcn, mp.mnt_special));
3385 return (s_strdup(mp.mnt_special));
3388 static void
3389 line_free(line_t *lp)
3391 if (lp == NULL)
3392 return;
3394 free(lp->cmd);
3395 free(lp->sep);
3396 free(lp->arg);
3397 free(lp->line);
3398 free(lp);
3401 static void
3402 linelist_free(line_t *start)
3404 line_t *lp;
3406 while (start) {
3407 lp = start;
3408 start = start->next;
3409 line_free(lp);
3413 static void
3414 filelist_free(filelist_t *flistp)
3416 linelist_free(flistp->head);
3417 flistp->head = NULL;
3418 flistp->tail = NULL;
3422 * Utility routines
3427 * Returns 0 on success
3428 * Any other value indicates an error
3430 static int
3431 exec_cmd(char *cmdline, filelist_t *flistp)
3433 char buf[BUFSIZ];
3434 int ret;
3435 FILE *ptr;
3436 sigset_t set;
3437 void (*disp)(int);
3440 * For security
3441 * - only absolute paths are allowed
3442 * - set IFS to space and tab
3444 if (*cmdline != '/') {
3445 bam_error(_("path is not absolute: %s\n"), cmdline);
3446 return (-1);
3448 (void) putenv("IFS= \t");
3451 * We may have been exec'ed with SIGCHLD blocked
3452 * unblock it here
3454 (void) sigemptyset(&set);
3455 (void) sigaddset(&set, SIGCHLD);
3456 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
3457 bam_error(_("cannot unblock SIGCHLD: %s\n"), strerror(errno));
3458 return (-1);
3462 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
3464 disp = sigset(SIGCHLD, SIG_DFL);
3465 if (disp == SIG_ERR) {
3466 bam_error(_("cannot set SIGCHLD disposition: %s\n"),
3467 strerror(errno));
3468 return (-1);
3470 if (disp == SIG_HOLD) {
3471 bam_error(_("SIGCHLD signal blocked. Cannot exec: %s\n"),
3472 cmdline);
3473 return (-1);
3476 ptr = popen(cmdline, "r");
3477 if (ptr == NULL) {
3478 bam_error(_("popen failed: %s: %s\n"), cmdline,
3479 strerror(errno));
3480 return (-1);
3484 * If we simply do a pclose() following a popen(), pclose()
3485 * will close the reader end of the pipe immediately even
3486 * if the child process has not started/exited. pclose()
3487 * does wait for cmd to terminate before returning though.
3488 * When the executed command writes its output to the pipe
3489 * there is no reader process and the command dies with
3490 * SIGPIPE. To avoid this we read repeatedly until read
3491 * terminates with EOF. This indicates that the command
3492 * (writer) has closed the pipe and we can safely do a
3493 * pclose().
3495 * Since pclose() does wait for the command to exit,
3496 * we can safely reap the exit status of the command
3497 * from the value returned by pclose()
3499 while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
3500 if (flistp == NULL) {
3501 /* s_fgets strips newlines, so insert them at the end */
3502 bam_print(_("%s\n"), buf);
3503 } else {
3504 append_to_flist(flistp, buf);
3508 ret = pclose(ptr);
3509 if (ret == -1) {
3510 bam_error(_("pclose failed: %s: %s\n"), cmdline,
3511 strerror(errno));
3512 return (-1);
3515 if (WIFEXITED(ret)) {
3516 return (WEXITSTATUS(ret));
3517 } else {
3518 bam_error(_("command terminated abnormally: %s: %d\n"),
3519 cmdline, ret);
3520 return (-1);
3525 * Since this function returns -1 on error
3526 * it cannot be used to convert -1. However,
3527 * that is sufficient for what we need.
3529 static long
3530 s_strtol(char *str)
3532 long l;
3533 char *res = NULL;
3535 if (str == NULL) {
3536 return (-1);
3539 errno = 0;
3540 l = strtol(str, &res, 10);
3541 if (errno || *res != '\0') {
3542 return (-1);
3545 return (l);
3549 * Wrapper around fgets, that strips newlines returned by fgets
3551 char *
3552 s_fgets(char *buf, int buflen, FILE *fp)
3554 int n;
3556 buf = fgets(buf, buflen, fp);
3557 if (buf) {
3558 n = strlen(buf);
3559 if (n == buflen - 1 && buf[n-1] != '\n')
3560 bam_error(_("the following line is too long "
3561 "(> %d chars)\n\t%s\n"), buflen - 1, buf);
3562 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
3565 return (buf);
3568 void *
3569 s_calloc(size_t nelem, size_t sz)
3571 void *ptr;
3573 ptr = calloc(nelem, sz);
3574 if (ptr == NULL) {
3575 bam_error(_("could not allocate memory: size = %u\n"),
3576 nelem*sz);
3577 bam_exit(1);
3579 return (ptr);
3582 void *
3583 s_realloc(void *ptr, size_t sz)
3585 ptr = realloc(ptr, sz);
3586 if (ptr == NULL) {
3587 bam_error(_("could not allocate memory: size = %u\n"), sz);
3588 bam_exit(1);
3590 return (ptr);
3593 char *
3594 s_strdup(char *str)
3596 char *ptr;
3598 if (str == NULL)
3599 return (NULL);
3601 ptr = strdup(str);
3602 if (ptr == NULL) {
3603 bam_error(_("could not allocate memory: size = %u\n"),
3604 strlen(str) + 1);
3605 bam_exit(1);
3607 return (ptr);
3611 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
3612 * Returns 0 otherwise
3614 static int
3615 is_amd64(void)
3617 static int amd64 = -1;
3618 char isabuf[257]; /* from sysinfo(2) manpage */
3620 if (amd64 != -1)
3621 return (amd64);
3623 if (bam_alt_platform) {
3624 if (strcmp(bam_platform, "i86pc") == 0) {
3625 amd64 = 1; /* diskless server */
3627 } else {
3628 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
3629 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
3630 amd64 = 1;
3631 } else if (strstr(isabuf, "i386") == NULL) {
3632 amd64 = 1; /* diskless server */
3635 if (amd64 == -1)
3636 amd64 = 0;
3638 return (amd64);
3641 static char *
3642 get_machine(void)
3644 static int cached = -1;
3645 static char mbuf[257]; /* from sysinfo(2) manpage */
3647 if (cached == 0)
3648 return (mbuf);
3650 if (bam_alt_platform) {
3651 return (bam_platform);
3652 } else {
3653 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) {
3654 cached = 1;
3657 if (cached == -1) {
3658 mbuf[0] = '\0';
3659 cached = 0;
3662 return (mbuf);
3666 is_sparc(void)
3668 static int issparc = -1;
3669 char mbuf[257]; /* from sysinfo(2) manpage */
3671 if (issparc != -1)
3672 return (issparc);
3674 if (bam_alt_platform) {
3675 if (strncmp(bam_platform, "sun4", 4) == 0) {
3676 issparc = 1;
3678 } else {
3679 if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 &&
3680 strcmp(mbuf, "sparc") == 0) {
3681 issparc = 1;
3684 if (issparc == -1)
3685 issparc = 0;
3687 return (issparc);
3690 static void
3691 append_to_flist(filelist_t *flistp, char *s)
3693 line_t *lp;
3695 lp = s_calloc(1, sizeof (line_t));
3696 lp->line = s_strdup(s);
3697 if (flistp->head == NULL)
3698 flistp->head = lp;
3699 else
3700 flistp->tail->next = lp;
3701 flistp->tail = lp;
3704 #if !defined(_OBP)
3706 UCODE_VENDORS;
3708 /*ARGSUSED*/
3709 static void
3710 ucode_install(char *root)
3712 int i;
3714 for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
3715 int cmd_len = PATH_MAX + 256;
3716 char cmd[PATH_MAX + 256];
3717 char file[PATH_MAX];
3718 char timestamp[PATH_MAX];
3719 struct stat fstatus, tstatus;
3720 struct utimbuf u_times;
3722 (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s",
3723 bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr,
3724 ucode_vendors[i].extstr);
3726 if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
3727 continue;
3729 (void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
3731 if (stat(timestamp, &tstatus) == 0 &&
3732 fstatus.st_mtime <= tstatus.st_mtime)
3733 continue;
3735 (void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
3736 "%s/%s/%s %s > /dev/null 2>&1", bam_root,
3737 UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
3738 if (system(cmd) != 0)
3739 return;
3741 if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
3742 return;
3744 u_times.actime = fstatus.st_atime;
3745 u_times.modtime = fstatus.st_mtime;
3746 (void) utime(timestamp, &u_times);
3749 #endif