kill tsol ("Trusted Solaris") aka TX ("Trusted Extensions")
[unleashed.git] / usr / src / cmd / boot / bootadm / bootadm.c
blob08c9946d6a853eb70a4c40237fe31489d559de12
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 "/dev/ramdisk/"
112 #define GRUBSIGN_DIR "/boot/grub/bootsign"
113 #define GRUBSIGN_BACKUP "/etc/bootsign"
114 #define GRUBSIGN_UFS_PREFIX "rootfs"
115 #define GRUBSIGN_ZFS_PREFIX "pool_"
116 #define GRUBSIGN_LU_PREFIX "BE_"
117 #define UFS_SIGNATURE_LIST "/var/run/grub_ufs_signatures"
118 #define ZFS_LEGACY_MNTPT "/tmp/bootadm_mnt_zfs_legacy"
120 /* BE defaults */
121 #define BE_DEFAULTS "/etc/default/be"
122 #define BE_DFLT_BE_HAS_GRUB "BE_HAS_GRUB="
124 #define BOOTADM_RDONLY_TEST "BOOTADM_RDONLY_TEST"
126 /* lock related */
127 #define BAM_LOCK_FILE "/var/run/bootadm.lock"
128 #define LOCK_FILE_PERMS (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
130 #define CREATE_RAMDISK "boot/solaris/bin/create_ramdisk"
131 #define CREATE_DISKMAP "boot/solaris/bin/create_diskmap"
132 #define EXTRACT_BOOT_FILELIST "boot/solaris/bin/extract_boot_filelist"
133 #define GRUBDISK_MAP "/var/run/solaris_grubdisk.map"
135 #define GRUB_slice "/etc/lu/GRUB_slice"
136 #define GRUB_root "/etc/lu/GRUB_root"
137 #define GRUB_fdisk "/etc/lu/GRUB_fdisk"
138 #define GRUB_fdisk_target "/etc/lu/GRUB_fdisk_target"
139 #define FINDROOT_INSTALLGRUB "/etc/lu/installgrub.findroot"
140 #define LULIB "/usr/lib/lu/lulib"
141 #define LULIB_PROPAGATE_FILE "lulib_propagate_file"
142 #define CKSUM "/usr/bin/cksum"
143 #define LU_MENU_CKSUM "/etc/lu/menu.cksum"
144 #define BOOTADM "/sbin/bootadm"
146 #define INSTALLGRUB "/sbin/installgrub"
147 #define STAGE1 "/boot/grub/stage1"
148 #define STAGE2 "/boot/grub/stage2"
151 * Default file attributes
153 #define DEFAULT_DEV_MODE 0644 /* default permissions */
154 #define DEFAULT_DEV_UID 0 /* user root */
155 #define DEFAULT_DEV_GID 3 /* group sys */
158 * exec_cmd related
160 typedef struct {
161 line_t *head;
162 line_t *tail;
163 } filelist_t;
165 #define BOOT_FILE_LIST "boot/solaris/filelist.ramdisk"
166 #define ETC_FILE_LIST "etc/boot/solaris/filelist.ramdisk"
168 #define FILE_STAT "boot/solaris/filestat.ramdisk"
169 #define FILE_STAT_TMP "boot/solaris/filestat.ramdisk.tmp"
170 #define DIR_PERMS (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
171 #define FILE_STAT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
173 #define FILE_STAT_TIMESTAMP "boot/solaris/timestamp.cache"
175 /* Globals */
176 int bam_verbose;
177 int bam_force;
178 int bam_debug;
179 static char *prog;
180 static subcmd_t bam_cmd;
181 char *bam_root;
182 int bam_rootlen;
183 static int bam_root_readonly;
184 int bam_alt_root;
185 static int bam_extend = 0;
186 static int bam_purge = 0;
187 static char *bam_subcmd;
188 static char *bam_opt;
189 static char **bam_argv;
190 static char *bam_pool;
191 static int bam_argc;
192 static int bam_check;
193 static int bam_saved_check;
194 static int bam_smf_check;
195 static int bam_lock_fd = -1;
196 static int bam_zfs;
197 static int bam_mbr;
198 char rootbuf[PATH_MAX] = "/";
199 static int bam_update_all;
200 static int bam_alt_platform;
201 static char *bam_platform;
202 static char *bam_home_env = NULL;
204 /* function prototypes */
205 static void parse_args_internal(int, char *[]);
206 static void parse_args(int, char *argv[]);
207 static error_t bam_install(char *, char *);
208 static error_t bam_archive(char *, char *);
210 static void bam_lock(void);
211 static void bam_unlock(void);
213 static int exec_cmd(char *, filelist_t *);
214 static void linelist_free(line_t *);
215 static void filelist_free(filelist_t *);
217 static error_t install_bootloader(void);
218 static error_t update_archive(char *, char *);
219 static error_t list_archive(char *, char *);
220 static error_t update_all(char *, char *);
221 static error_t read_list(char *, filelist_t *);
223 static long s_strtol(char *);
225 static int is_amd64(void);
226 static char *get_machine(void);
227 static void append_to_flist(filelist_t *, char *);
229 #if !defined(_OBP)
230 static void ucode_install();
231 #endif
233 #if 0
234 /* Menu related sub commands */
235 static subcmd_defn_t menu_subcmds[] = {
236 "set_option", OPT_ABSENT, set_option, 0, /* PUB */
237 "list_entry", OPT_OPTIONAL, list_entry, 1, /* PUB */
238 "delete_all_entries", OPT_ABSENT, delete_all_entries, 0, /* PVT */
239 "update_entry", OPT_REQ, update_entry, 0, /* menu */
240 "update_temp", OPT_OPTIONAL, update_temp, 0, /* reboot */
241 "upgrade", OPT_ABSENT, upgrade_menu, 0, /* menu */
242 "list_setting", OPT_OPTIONAL, list_setting, 1, /* menu */
243 "disable_hypervisor", OPT_ABSENT, cvt_to_metal, 0, /* menu */
244 "enable_hypervisor", OPT_ABSENT, cvt_to_hyper, 0, /* menu */
245 NULL, 0, NULL, 0 /* must be last */
247 #endif
249 /* Archive related sub commands */
250 static subcmd_defn_t arch_subcmds[] = {
251 { "update", OPT_ABSENT, update_archive, 0 }, /* PUB */
252 { "update_all", OPT_ABSENT, update_all, 0 }, /* PVT */
253 { "list", OPT_OPTIONAL, list_archive, 1 }, /* PUB */
254 { NULL, 0, NULL, 0 } /* must be last */
257 /* Install related sub commands */
258 static subcmd_defn_t inst_subcmds[] = {
259 { "install_bootloader", OPT_ABSENT, install_bootloader, 0 }, /* PUB */
260 { NULL, 0, NULL, 0 } /* must be last */
263 enum dircache_copy_opt {
264 FILE32 = 0,
265 FILE64,
266 CACHEDIR_NUM
270 * Directory specific flags:
271 * NEED_UPDATE : the specified archive needs to be updated
272 * NO_MULTI : don't extend the specified archive, but recreate it
274 #define NEED_UPDATE 0x00000001
275 #define NO_MULTI 0x00000002
277 #define set_dir_flag(id, f) (walk_arg.dirinfo[id].flags |= f)
278 #define unset_dir_flag(id, f) (walk_arg.dirinfo[id].flags &= ~f)
279 #define is_dir_flag_on(id, f) (walk_arg.dirinfo[id].flags & f ? 1 : 0)
281 #define get_cachedir(id) (walk_arg.dirinfo[id].cdir_path)
282 #define get_updatedir(id) (walk_arg.dirinfo[id].update_path)
283 #define get_count(id) (walk_arg.dirinfo[id].count)
284 #define has_cachedir(id) (walk_arg.dirinfo[id].has_dir)
285 #define set_dir_present(id) (walk_arg.dirinfo[id].has_dir = 1)
288 * dirinfo_t (specific cache directory information):
289 * cdir_path: path to the archive cache directory
290 * update_path: path to the update directory (contains the files that will be
291 * used to extend the archive)
292 * has_dir: the specified cache directory is active
293 * count: the number of files to update
294 * flags: directory specific flags
296 typedef struct _dirinfo {
297 char cdir_path[PATH_MAX];
298 char update_path[PATH_MAX];
299 int has_dir;
300 int count;
301 int flags;
302 } dirinfo_t;
305 * Update flags:
306 * NEED_CACHE_DIR : cache directory is missing and needs to be created
307 * IS_SPARC_TARGET : the target mountpoint is a SPARC environment
308 * UPDATE_ERROR : an error occourred while traversing the list of files
309 * RDONLY_FSCHK : the target filesystem is read-only
310 * RAMDSK_FSCHK : the target filesystem is on a ramdisk
312 #define NEED_CACHE_DIR 0x00000001
313 #define IS_SPARC_TARGET 0x00000002
314 #define UPDATE_ERROR 0x00000004
315 #define RDONLY_FSCHK 0x00000008
316 #define INVALIDATE_CACHE 0x00000010
318 #define is_flag_on(flag) (walk_arg.update_flags & flag ? 1 : 0)
319 #define set_flag(flag) (walk_arg.update_flags |= flag)
320 #define unset_flag(flag) (walk_arg.update_flags &= ~flag)
323 * struct walk_arg :
324 * update_flags: flags related to the current updating process
325 * new_nvlp/old_nvlp: new and old list of archive-files / attributes pairs
326 * sparcfile: list of file paths for mkisofs -path-list (SPARC only)
328 static struct {
329 int update_flags;
330 nvlist_t *new_nvlp;
331 nvlist_t *old_nvlp;
332 FILE *sparcfile;
333 dirinfo_t dirinfo[CACHEDIR_NUM];
334 } walk_arg;
336 struct safefile {
337 char *name;
338 struct safefile *next;
341 static struct safefile *safefiles = NULL;
344 * svc:/system/filesystem/usr:default service checks for this file and
345 * does a boot archive update and then reboot the system.
347 #define NEED_UPDATE_FILE "/etc/svc/volatile/boot_archive_needs_update"
350 * svc:/system/boot-archive-update:default checks for this file and
351 * updates the boot archive.
353 #define NEED_UPDATE_SAFE_FILE "/etc/svc/volatile/boot_archive_safefile_update"
355 /* Thanks growisofs */
356 #define CD_BLOCK ((off64_t)2048)
357 #define VOLDESC_OFF 16
358 #define DVD_BLOCK (32*1024)
359 #define MAX_IVDs 16
361 struct iso_pdesc {
362 unsigned char type [1];
363 unsigned char id [5];
364 unsigned char void1 [80-5-1];
365 unsigned char volume_space_size [8];
366 unsigned char void2 [2048-80-8];
370 * COUNT_MAX: maximum number of changed files to justify a multisession update
371 * BA_SIZE_MAX: maximum size of the boot_archive to justify a multisession
372 * update
374 #define COUNT_MAX 50
375 #define BA_SIZE_MAX (50 * 1024 * 1024)
377 #define bam_nowrite() (bam_check || bam_smf_check)
379 static void
380 usage(void)
382 (void) fprintf(stderr, "USAGE:\n");
384 /* archive usage */
385 (void) fprintf(stderr,
386 "\t%s update-archive [-vn] [-R altroot [-p platform]]\n", prog);
387 (void) fprintf(stderr,
388 "\t%s list-archive [-R altroot [-p platform]]\n", prog);
389 #if defined(_OBP)
390 (void) fprintf(stderr,
391 "\t%s install-bootloader [-fv] [-R altroot] [-P pool]\n", prog);
392 #else
393 (void) fprintf(stderr,
394 "\t%s install-bootloader [-Mfv] [-R altroot] [-P pool]\n", prog);
395 #endif
396 #if !defined(_OBP)
397 /* x86 only */
398 (void) fprintf(stderr, "\t%s set-menu [-R altroot] key=value\n", prog);
399 (void) fprintf(stderr, "\t%s list-menu [-R altroot]\n", prog);
400 #endif
404 * Best effort attempt to restore the $HOME value.
406 static void
407 restore_env()
409 char home_env[PATH_MAX];
411 if (bam_home_env) {
412 (void) snprintf(home_env, sizeof (home_env), "HOME=%s",
413 bam_home_env);
414 (void) putenv(home_env);
419 #define SLEEP_TIME 5
420 #define MAX_TRIES 4
423 * Sanitize the environment in which bootadm will execute its sub-processes
424 * (ex. mkisofs). This is done to prevent those processes from attempting
425 * to access files (ex. .mkisofsrc) or stat paths that might be on NFS
426 * or, potentially, insecure.
428 static void
429 sanitize_env()
431 int stry = 0;
433 /* don't depend on caller umask */
434 (void) umask(0022);
436 /* move away from a potential unsafe current working directory */
437 while (chdir("/") == -1) {
438 if (errno != EINTR) {
439 bam_print("WARNING: unable to chdir to /");
440 break;
444 bam_home_env = getenv("HOME");
445 while (bam_home_env != NULL && putenv("HOME=/") == -1) {
446 if (errno == ENOMEM) {
447 /* retry no more than MAX_TRIES times */
448 if (++stry > MAX_TRIES) {
449 bam_print("WARNING: unable to recover from "
450 "system memory pressure... aborting \n");
451 bam_exit(EXIT_FAILURE);
453 /* memory is tight, try to sleep */
454 bam_print("Attempting to recover from memory pressure: "
455 "sleeping for %d seconds\n", SLEEP_TIME * stry);
456 (void) sleep(SLEEP_TIME * stry);
457 } else {
458 bam_print("WARNING: unable to sanitize HOME\n");
464 main(int argc, char *argv[])
466 error_t ret = BAM_SUCCESS;
468 (void) setlocale(LC_ALL, "");
469 (void) textdomain(TEXT_DOMAIN);
471 if ((prog = strrchr(argv[0], '/')) == NULL) {
472 prog = argv[0];
473 } else {
474 prog++;
477 INJECT_ERROR1("ASSERT_ON", assert(0))
479 sanitize_env();
481 parse_args(argc, argv);
483 switch (bam_cmd) {
484 case BAM_MENU:
485 ret = bam_loader_menu(bam_subcmd, bam_opt,
486 bam_argc, bam_argv);
487 break;
488 case BAM_ARCHIVE:
489 ret = bam_archive(bam_subcmd, bam_opt);
490 break;
491 case BAM_INSTALL:
492 ret = bam_install(bam_subcmd, bam_opt);
493 break;
494 default:
495 usage();
496 bam_exit(1);
499 if (ret != BAM_SUCCESS)
500 bam_exit((ret == BAM_NOCHANGE) ? 2 : 1);
502 bam_unlock();
503 return (0);
507 * Equivalence of public and internal commands:
508 * update-archive -- -a update
509 * list-archive -- -a list
510 * set-menu -- -m set_option
511 * list-menu -- -m list_entry
512 * update-menu -- -m update_entry
513 * install-bootloader -- -i install_bootloader
515 static struct cmd_map {
516 char *bam_cmdname;
517 int bam_cmd;
518 char *bam_subcmd;
519 } cmd_map[] = {
520 { "update-archive", BAM_ARCHIVE, "update"},
521 { "list-archive", BAM_ARCHIVE, "list"},
522 { "set-menu", BAM_MENU, "set_option"},
523 { "list-menu", BAM_MENU, "list_entry"},
524 { "update-menu", BAM_MENU, "update_entry"},
525 { "install-bootloader", BAM_INSTALL, "install_bootloader"},
526 { NULL, 0, NULL}
530 * Commands syntax published in bootadm(1M) are parsed here
532 static void
533 parse_args(int argc, char *argv[])
535 struct cmd_map *cmp = cmd_map;
537 /* command conforming to the final spec */
538 if (argc > 1 && argv[1][0] != '-') {
540 * Map commands to internal table.
542 while (cmp->bam_cmdname) {
543 if (strcmp(argv[1], cmp->bam_cmdname) == 0) {
544 bam_cmd = cmp->bam_cmd;
545 bam_subcmd = cmp->bam_subcmd;
546 break;
548 cmp++;
550 if (cmp->bam_cmdname == NULL) {
551 usage();
552 bam_exit(1);
554 argc--;
555 argv++;
558 parse_args_internal(argc, argv);
562 * A combination of public and private commands are parsed here.
563 * The internal syntax and the corresponding functionality are:
564 * -a update -- update-archive
565 * -a list -- list-archive
566 * -a update-all -- (reboot to sync all mnted OS archive)
567 * -i install_bootloader -- install-bootloader
568 * -m update_entry -- update-menu
569 * -m list_entry -- list-menu
570 * -m update_temp -- (reboot -- [boot-args])
571 * -m delete_all_entries -- (called from install)
572 * -m enable_hypervisor [args] -- cvt_to_hyper
573 * -m disable_hypervisor -- cvt_to_metal
574 * -m list_setting [entry] [value] -- list_setting
576 * A set of private flags is there too:
577 * -F -- purge the cache directories and rebuild them
578 * -e -- use the (faster) archive update approach (used by
579 * reboot)
581 static void
582 parse_args_internal(int argc, char *argv[])
584 int c, error;
585 extern char *optarg;
586 extern int optind, opterr;
587 #if defined(_OBP)
588 const char *optstring = "a:d:fi:m:no:veFCR:p:P:XZ";
589 #else
590 const char *optstring = "a:d:fi:m:no:veFCMR:p:P:XZ";
591 #endif
593 /* Suppress error message from getopt */
594 opterr = 0;
596 error = 0;
597 while ((c = getopt(argc, argv, optstring)) != -1) {
598 switch (c) {
599 case 'a':
600 if (bam_cmd) {
601 error = 1;
602 bam_error(
603 _("multiple commands specified: -%c\n"), c);
605 bam_cmd = BAM_ARCHIVE;
606 bam_subcmd = optarg;
607 break;
608 case 'd':
609 if (bam_debug) {
610 error = 1;
611 bam_error(
612 _("duplicate options specified: -%c\n"), c);
614 bam_debug = s_strtol(optarg);
615 break;
616 case 'f':
617 bam_force = 1;
618 break;
619 case 'F':
620 bam_purge = 1;
621 break;
622 case 'i':
623 if (bam_cmd) {
624 error = 1;
625 bam_error(
626 _("multiple commands specified: -%c\n"), c);
628 bam_cmd = BAM_INSTALL;
629 bam_subcmd = optarg;
630 break;
631 case 'm':
632 if (bam_cmd) {
633 error = 1;
634 bam_error(
635 _("multiple commands specified: -%c\n"), c);
637 bam_cmd = BAM_MENU;
638 bam_subcmd = optarg;
639 break;
640 #if !defined(_OBP)
641 case 'M':
642 bam_mbr = 1;
643 break;
644 #endif
645 case 'n':
646 bam_check = 1;
648 * We save the original value of bam_check. The new
649 * approach in case of a read-only filesystem is to
650 * behave as a check, so we need a way to restore the
651 * original value after the evaluation of the read-only
652 * filesystem has been done.
653 * Even if we don't allow at the moment a check with
654 * update_all, this approach is more robust than
655 * simply resetting bam_check to zero.
657 bam_saved_check = 1;
658 break;
659 case 'o':
660 if (bam_opt) {
661 error = 1;
662 bam_error(
663 _("duplicate options specified: -%c\n"), c);
665 bam_opt = optarg;
666 break;
667 case 'v':
668 bam_verbose = 1;
669 break;
670 case 'C':
671 bam_smf_check = 1;
672 break;
673 case 'P':
674 if (bam_pool != NULL) {
675 error = 1;
676 bam_error(
677 _("duplicate options specified: -%c\n"), c);
679 bam_pool = optarg;
680 break;
681 case 'R':
682 if (bam_root) {
683 error = 1;
684 bam_error(
685 _("duplicate options specified: -%c\n"), c);
686 break;
687 } else if (realpath(optarg, rootbuf) == NULL) {
688 error = 1;
689 bam_error(_("cannot resolve path %s: %s\n"),
690 optarg, strerror(errno));
691 break;
693 bam_alt_root = 1;
694 bam_root = rootbuf;
695 bam_rootlen = strlen(rootbuf);
696 break;
697 case 'p':
698 bam_alt_platform = 1;
699 bam_platform = optarg;
700 if ((strcmp(bam_platform, "i86pc") != 0) &&
701 (strcmp(bam_platform, "sun4u") != 0) &&
702 (strcmp(bam_platform, "sun4v") != 0)) {
703 error = 1;
704 bam_error(_("invalid platform %s - must be "
705 "one of sun4u, sun4v or i86pc\n"),
706 bam_platform);
708 break;
709 case 'X':
710 bam_is_hv = BAM_HV_PRESENT;
711 break;
712 case 'Z':
713 bam_zfs = 1;
714 break;
715 case 'e':
716 bam_extend = 1;
717 break;
718 case '?':
719 error = 1;
720 bam_error(_("invalid option or missing option "
721 "argument: -%c\n"), optopt);
722 break;
723 default :
724 error = 1;
725 bam_error(_("invalid option or missing option "
726 "argument: -%c\n"), c);
727 break;
732 * An alternate platform requires an alternate root
734 if (bam_alt_platform && bam_alt_root == 0) {
735 usage();
736 bam_exit(0);
740 * A command option must be specfied
742 if (!bam_cmd) {
743 if (bam_opt && strcmp(bam_opt, "all") == 0) {
744 usage();
745 bam_exit(0);
747 bam_error(_("a command option must be specified\n"));
748 error = 1;
751 if (error) {
752 usage();
753 bam_exit(1);
756 if (optind > argc) {
757 bam_error(_("Internal error: %s\n"), "parse_args");
758 bam_exit(1);
759 } else if (optind < argc) {
760 bam_argv = &argv[optind];
761 bam_argc = argc - optind;
765 * mbr and pool are options for install_bootloader
767 if (bam_cmd != BAM_INSTALL && (bam_mbr || bam_pool != NULL)) {
768 usage();
769 bam_exit(0);
773 * -n implies verbose mode
775 if (bam_check)
776 bam_verbose = 1;
779 error_t
780 check_subcmd_and_options(
781 char *subcmd,
782 char *opt,
783 subcmd_defn_t *table,
784 error_t (**fp)())
786 int i;
788 if (subcmd == NULL) {
789 bam_error(_("this command requires a sub-command\n"));
790 return (BAM_ERROR);
793 if (strcmp(subcmd, "set_option") == 0) {
794 if (bam_argc == 0 || bam_argv == NULL || bam_argv[0] == NULL) {
795 bam_error(_("missing argument for sub-command\n"));
796 usage();
797 return (BAM_ERROR);
798 } else if (bam_argc > 1 || bam_argv[1] != NULL) {
799 bam_error(_("invalid trailing arguments\n"));
800 usage();
801 return (BAM_ERROR);
803 } else if (strcmp(subcmd, "update_all") == 0) {
805 * The only option we accept for the "update_all"
806 * subcmd is "fastboot".
808 if (bam_argc > 1 || (bam_argc == 1 &&
809 strcmp(bam_argv[0], "fastboot") != 0)) {
810 bam_error(_("invalid trailing arguments\n"));
811 usage();
812 return (BAM_ERROR);
814 } else if (((strcmp(subcmd, "enable_hypervisor") != 0) &&
815 (strcmp(subcmd, "list_setting") != 0)) && (bam_argc || bam_argv)) {
817 * Of the remaining subcommands, only "enable_hypervisor" and
818 * "list_setting" take trailing arguments.
820 bam_error(_("invalid trailing arguments\n"));
821 usage();
822 return (BAM_ERROR);
825 if (bam_root == NULL) {
826 bam_root = rootbuf;
827 bam_rootlen = 1;
830 /* verify that subcmd is valid */
831 for (i = 0; table[i].subcmd != NULL; i++) {
832 if (strcmp(table[i].subcmd, subcmd) == 0)
833 break;
836 if (table[i].subcmd == NULL) {
837 bam_error(_("invalid sub-command specified: %s\n"), subcmd);
838 return (BAM_ERROR);
841 if (table[i].unpriv == 0 && geteuid() != 0) {
842 bam_error(_("you must be root to run this command\n"));
843 return (BAM_ERROR);
847 * Currently only privileged commands need a lock
849 if (table[i].unpriv == 0)
850 bam_lock();
852 /* subcmd verifies that opt is appropriate */
853 if (table[i].option != OPT_OPTIONAL) {
854 if ((table[i].option == OPT_REQ) ^ (opt != NULL)) {
855 if (opt)
856 bam_error(_("this sub-command (%s) does not "
857 "take options\n"), subcmd);
858 else
859 bam_error(_("an option is required for this "
860 "sub-command: %s\n"), subcmd);
861 return (BAM_ERROR);
865 *fp = table[i].handler;
867 return (BAM_SUCCESS);
871 * NOTE: A single "/" is also considered a trailing slash and will
872 * be deleted.
874 void
875 elide_trailing_slash(const char *src, char *dst, size_t dstsize)
877 size_t dstlen;
879 assert(src);
880 assert(dst);
882 (void) strlcpy(dst, src, dstsize);
884 dstlen = strlen(dst);
885 if (dst[dstlen - 1] == '/') {
886 dst[dstlen - 1] = '\0';
890 static int
891 is_safe_exec(char *path)
893 struct stat sb;
895 if (lstat(path, &sb) != 0) {
896 bam_error(_("stat of file failed: %s: %s\n"), path,
897 strerror(errno));
898 return (BAM_ERROR);
901 if (!S_ISREG(sb.st_mode)) {
902 bam_error(_("%s is not a regular file, skipping\n"), path);
903 return (BAM_ERROR);
906 if (sb.st_uid != getuid()) {
907 bam_error(_("%s is not owned by %d, skipping\n"),
908 path, getuid());
909 return (BAM_ERROR);
912 if (sb.st_mode & S_IWOTH || sb.st_mode & S_IWGRP) {
913 bam_error(_("%s is others or group writable, skipping\n"),
914 path);
915 return (BAM_ERROR);
918 return (BAM_SUCCESS);
921 static error_t
922 install_bootloader(void)
924 nvlist_t *nvl;
925 uint16_t flags = 0;
926 int found = 0;
927 struct extmnttab mnt;
928 struct stat statbuf = {0};
929 be_node_list_t *be_nodes, *node;
930 FILE *fp;
931 char *root_ds = NULL;
932 int ret = BAM_ERROR;
934 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
935 bam_error(_("out of memory\n"));
936 return (ret);
940 * if bam_alt_root is set, the stage files are used from alt root.
941 * if pool is set, the target devices are pool devices, stage files
942 * are read from pool bootfs unless alt root is set.
944 * use arguments as targets, stage files are from alt or current root
945 * if no arguments and no pool, install on current boot pool.
948 if (bam_alt_root) {
949 if (stat(bam_root, &statbuf) != 0) {
950 bam_error(_("stat of file failed: %s: %s\n"), bam_root,
951 strerror(errno));
952 goto done;
954 if ((fp = fopen(MNTTAB, "r")) == NULL) {
955 bam_error(_("failed to open file: %s: %s\n"),
956 MNTTAB, strerror(errno));
957 goto done;
959 resetmnttab(fp);
960 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
961 if (mnt.mnt_major == major(statbuf.st_dev) &&
962 mnt.mnt_minor == minor(statbuf.st_dev)) {
963 found = 1;
964 root_ds = strdup(mnt.mnt_special);
965 break;
968 (void) fclose(fp);
970 if (found == 0) {
971 bam_error(_("alternate root %s not in mnttab\n"),
972 bam_root);
973 goto done;
975 if (root_ds == NULL) {
976 bam_error(_("out of memory\n"));
977 goto done;
980 if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
981 bam_error(_("No BE's found\n"));
982 goto done;
984 for (node = be_nodes; node != NULL; node = node->be_next_node)
985 if (strcmp(root_ds, node->be_root_ds) == 0)
986 break;
988 if (node == NULL)
989 bam_error(_("BE (%s) does not exist\n"), root_ds);
991 free(root_ds);
992 root_ds = NULL;
993 if (node == NULL) {
994 be_free_list(be_nodes);
995 goto done;
997 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
998 node->be_node_name);
999 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1000 node->be_root_ds);
1001 be_free_list(be_nodes);
1002 if (ret != 0) {
1003 ret = BAM_ERROR;
1004 goto done;
1008 if (bam_force)
1009 flags |= BE_INSTALLBOOT_FLAG_FORCE;
1010 if (bam_mbr)
1011 flags |= BE_INSTALLBOOT_FLAG_MBR;
1012 if (bam_verbose)
1013 flags |= BE_INSTALLBOOT_FLAG_VERBOSE;
1015 if (nvlist_add_uint16(nvl, BE_ATTR_INSTALL_FLAGS, flags) != 0) {
1016 bam_error(_("out of memory\n"));
1017 ret = BAM_ERROR;
1018 goto done;
1022 * if altroot was set, we got be name and be root, only need
1023 * to set pool name as target.
1024 * if no altroot, need to find be name and root from pool.
1026 if (bam_pool != NULL) {
1027 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, bam_pool);
1028 if (ret != 0) {
1029 ret = BAM_ERROR;
1030 goto done;
1032 if (found) {
1033 ret = be_installboot(nvl);
1034 if (ret != 0)
1035 ret = BAM_ERROR;
1036 goto done;
1040 if (be_list(NULL, &be_nodes) != BE_SUCCESS) {
1041 bam_error(_("No BE's found\n"));
1042 ret = BAM_ERROR;
1043 goto done;
1046 if (bam_pool != NULL) {
1048 * find active be_node in bam_pool
1050 for (node = be_nodes; node != NULL; node = node->be_next_node) {
1051 if (strcmp(bam_pool, node->be_rpool) != 0)
1052 continue;
1053 if (node->be_active_on_boot)
1054 break;
1056 if (node == NULL) {
1057 bam_error(_("No active BE in %s\n"), bam_pool);
1058 be_free_list(be_nodes);
1059 ret = BAM_ERROR;
1060 goto done;
1062 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME,
1063 node->be_node_name);
1064 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT,
1065 node->be_root_ds);
1066 be_free_list(be_nodes);
1067 if (ret != 0) {
1068 ret = BAM_ERROR;
1069 goto done;
1071 ret = be_installboot(nvl);
1072 if (ret != 0)
1073 ret = BAM_ERROR;
1074 goto done;
1078 * get dataset for "/" and fill up the args.
1080 if ((fp = fopen(MNTTAB, "r")) == NULL) {
1081 bam_error(_("failed to open file: %s: %s\n"),
1082 MNTTAB, strerror(errno));
1083 ret = BAM_ERROR;
1084 be_free_list(be_nodes);
1085 goto done;
1087 resetmnttab(fp);
1088 found = 0;
1089 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
1090 if (strcmp(mnt.mnt_mountp, "/") == 0) {
1091 found = 1;
1092 root_ds = strdup(mnt.mnt_special);
1093 break;
1096 (void) fclose(fp);
1098 if (found == 0) {
1099 bam_error(_("alternate root %s not in mnttab\n"), "/");
1100 ret = BAM_ERROR;
1101 be_free_list(be_nodes);
1102 goto done;
1104 if (root_ds == NULL) {
1105 bam_error(_("out of memory\n"));
1106 ret = BAM_ERROR;
1107 be_free_list(be_nodes);
1108 goto done;
1111 for (node = be_nodes; node != NULL; node = node->be_next_node) {
1112 if (strcmp(root_ds, node->be_root_ds) == 0)
1113 break;
1116 if (node == NULL) {
1117 bam_error(_("No such BE: %s\n"), root_ds);
1118 free(root_ds);
1119 be_free_list(be_nodes);
1120 ret = BAM_ERROR;
1121 goto done;
1123 free(root_ds);
1125 ret = nvlist_add_string(nvl, BE_ATTR_ORIG_BE_NAME, node->be_node_name);
1126 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_ROOT, node->be_root_ds);
1127 ret |= nvlist_add_string(nvl, BE_ATTR_ORIG_BE_POOL, node->be_rpool);
1128 be_free_list(be_nodes);
1130 if (ret != 0)
1131 ret = BAM_ERROR;
1132 else
1133 ret = be_installboot(nvl) ? BAM_ERROR : 0;
1134 done:
1135 nvlist_free(nvl);
1137 return (ret);
1140 static error_t
1141 bam_install(char *subcmd, char *opt)
1143 error_t (*f)(void);
1146 * Check arguments
1148 if (check_subcmd_and_options(subcmd, opt, inst_subcmds, &f) ==
1149 BAM_ERROR)
1150 return (BAM_ERROR);
1152 return (f());
1155 static error_t
1156 bam_archive(
1157 char *subcmd,
1158 char *opt)
1160 error_t ret;
1161 error_t (*f)(char *root, char *opt);
1162 const char *fcn = "bam_archive()";
1165 * Add trailing / for archive subcommands
1167 if (rootbuf[strlen(rootbuf) - 1] != '/')
1168 (void) strcat(rootbuf, "/");
1169 bam_rootlen = strlen(rootbuf);
1172 * Check arguments
1174 ret = check_subcmd_and_options(subcmd, opt, arch_subcmds, &f);
1175 if (ret != BAM_SUCCESS) {
1176 return (BAM_ERROR);
1179 ret = get_boot_cap(rootbuf);
1180 if (ret != BAM_SUCCESS) {
1181 BAM_DPRINTF(("%s: Failed to get boot capability\n", fcn));
1182 return (ret);
1186 * Check archive not supported with update_all
1187 * since it is awkward to display out-of-sync
1188 * information for each BE.
1190 if (bam_check && strcmp(subcmd, "update_all") == 0) {
1191 bam_error(_("the check option is not supported with "
1192 "subcmd: %s\n"), subcmd);
1193 return (BAM_ERROR);
1196 if (strcmp(subcmd, "update_all") == 0)
1197 bam_update_all = 1;
1199 #if !defined(_OBP)
1200 ucode_install(bam_root);
1201 #endif
1203 ret = f(bam_root, opt);
1205 bam_update_all = 0;
1207 return (ret);
1210 /*PRINTFLIKE1*/
1211 void
1212 bam_error(char *format, ...)
1214 va_list ap;
1216 va_start(ap, format);
1217 (void) fprintf(stderr, "%s: ", prog);
1218 (void) vfprintf(stderr, format, ap);
1219 va_end(ap);
1222 /*PRINTFLIKE1*/
1223 void
1224 bam_derror(char *format, ...)
1226 va_list ap;
1228 assert(bam_debug);
1230 va_start(ap, format);
1231 (void) fprintf(stderr, "DEBUG: ");
1232 (void) vfprintf(stderr, format, ap);
1233 va_end(ap);
1236 /*PRINTFLIKE1*/
1237 void
1238 bam_print(char *format, ...)
1240 va_list ap;
1242 va_start(ap, format);
1243 (void) vfprintf(stdout, format, ap);
1244 va_end(ap);
1247 /*PRINTFLIKE1*/
1248 void
1249 bam_print_stderr(char *format, ...)
1251 va_list ap;
1253 va_start(ap, format);
1254 (void) vfprintf(stderr, format, ap);
1255 va_end(ap);
1258 void
1259 bam_exit(int excode)
1261 restore_env();
1262 bam_unlock();
1263 exit(excode);
1266 static void
1267 bam_lock(void)
1269 struct flock lock;
1270 pid_t pid;
1272 bam_lock_fd = open(BAM_LOCK_FILE, O_CREAT|O_RDWR, LOCK_FILE_PERMS);
1273 if (bam_lock_fd < 0) {
1275 * We may be invoked early in boot for archive verification.
1276 * In this case, root is readonly and /var/run may not exist.
1277 * Proceed without the lock
1279 if (errno == EROFS || errno == ENOENT) {
1280 bam_root_readonly = 1;
1281 return;
1284 bam_error(_("failed to open file: %s: %s\n"),
1285 BAM_LOCK_FILE, strerror(errno));
1286 bam_exit(1);
1289 lock.l_type = F_WRLCK;
1290 lock.l_whence = SEEK_SET;
1291 lock.l_start = 0;
1292 lock.l_len = 0;
1294 if (fcntl(bam_lock_fd, F_SETLK, &lock) == -1) {
1295 if (errno != EACCES && errno != EAGAIN) {
1296 bam_error(_("failed to lock file: %s: %s\n"),
1297 BAM_LOCK_FILE, strerror(errno));
1298 (void) close(bam_lock_fd);
1299 bam_lock_fd = -1;
1300 bam_exit(1);
1302 pid = 0;
1303 (void) pread(bam_lock_fd, &pid, sizeof (pid_t), 0);
1304 bam_print(
1305 _("another instance of bootadm (pid %lu) is running\n"),
1306 pid);
1308 lock.l_type = F_WRLCK;
1309 lock.l_whence = SEEK_SET;
1310 lock.l_start = 0;
1311 lock.l_len = 0;
1312 if (fcntl(bam_lock_fd, F_SETLKW, &lock) == -1) {
1313 bam_error(_("failed to lock file: %s: %s\n"),
1314 BAM_LOCK_FILE, strerror(errno));
1315 (void) close(bam_lock_fd);
1316 bam_lock_fd = -1;
1317 bam_exit(1);
1321 /* We own the lock now */
1322 pid = getpid();
1323 (void) write(bam_lock_fd, &pid, sizeof (pid));
1326 static void
1327 bam_unlock(void)
1329 struct flock unlock;
1332 * NOP if we don't hold the lock
1334 if (bam_lock_fd < 0) {
1335 return;
1338 unlock.l_type = F_UNLCK;
1339 unlock.l_whence = SEEK_SET;
1340 unlock.l_start = 0;
1341 unlock.l_len = 0;
1343 if (fcntl(bam_lock_fd, F_SETLK, &unlock) == -1) {
1344 bam_error(_("failed to unlock file: %s: %s\n"),
1345 BAM_LOCK_FILE, strerror(errno));
1348 if (close(bam_lock_fd) == -1) {
1349 bam_error(_("failed to close file: %s: %s\n"),
1350 BAM_LOCK_FILE, strerror(errno));
1352 bam_lock_fd = -1;
1355 static error_t
1356 list_archive(char *root, char *opt)
1358 filelist_t flist;
1359 filelist_t *flistp = &flist;
1360 line_t *lp;
1362 assert(root);
1363 assert(opt == NULL);
1365 flistp->head = flistp->tail = NULL;
1366 if (read_list(root, flistp) != BAM_SUCCESS) {
1367 return (BAM_ERROR);
1369 assert(flistp->head && flistp->tail);
1371 for (lp = flistp->head; lp; lp = lp->next) {
1372 bam_print(_("%s\n"), lp->line);
1375 filelist_free(flistp);
1377 return (BAM_SUCCESS);
1381 * Checks if the path specified (without the file name at the end) exists
1382 * and creates it if not. If the path exists and is not a directory, an attempt
1383 * to unlink is made.
1385 static int
1386 setup_path(char *path)
1388 char *p;
1389 int ret;
1390 struct stat sb;
1392 p = strrchr(path, '/');
1393 if (p != NULL) {
1394 *p = '\0';
1395 if (stat(path, &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
1396 /* best effort attempt, mkdirp will catch the error */
1397 (void) unlink(path);
1398 if (bam_verbose)
1399 bam_print(_("need to create directory "
1400 "path for %s\n"), path);
1401 ret = mkdirp(path, DIR_PERMS);
1402 if (ret == -1) {
1403 bam_error(_("mkdir of %s failed: %s\n"),
1404 path, strerror(errno));
1405 *p = '/';
1406 return (BAM_ERROR);
1409 *p = '/';
1410 return (BAM_SUCCESS);
1412 return (BAM_SUCCESS);
1415 typedef union {
1416 gzFile gzfile;
1417 int fdfile;
1418 } outfile;
1420 typedef struct {
1421 char path[PATH_MAX];
1422 outfile out;
1423 } cachefile;
1425 static int
1426 setup_file(char *base, const char *path, cachefile *cf)
1428 int ret;
1429 char *strip;
1431 /* init gzfile or fdfile in case we fail before opening */
1432 if (bam_direct == BAM_DIRECT_DBOOT)
1433 cf->out.gzfile = NULL;
1434 else
1435 cf->out.fdfile = -1;
1437 /* strip the trailing altroot path */
1438 strip = (char *)path + strlen(rootbuf);
1440 ret = snprintf(cf->path, sizeof (cf->path), "%s/%s", base, strip);
1441 if (ret >= sizeof (cf->path)) {
1442 bam_error(_("unable to create path on mountpoint %s, "
1443 "path too long\n"), rootbuf);
1444 return (BAM_ERROR);
1447 /* Check if path is present in the archive cache directory */
1448 if (setup_path(cf->path) == BAM_ERROR)
1449 return (BAM_ERROR);
1451 if (bam_direct == BAM_DIRECT_DBOOT) {
1452 if ((cf->out.gzfile = gzopen(cf->path, "wb")) == NULL) {
1453 bam_error(_("failed to open file: %s: %s\n"),
1454 cf->path, strerror(errno));
1455 return (BAM_ERROR);
1457 (void) gzsetparams(cf->out.gzfile, Z_BEST_SPEED,
1458 Z_DEFAULT_STRATEGY);
1459 } else {
1460 if ((cf->out.fdfile = open(cf->path, O_WRONLY | O_CREAT, 0644))
1461 == -1) {
1462 bam_error(_("failed to open file: %s: %s\n"),
1463 cf->path, strerror(errno));
1464 return (BAM_ERROR);
1468 return (BAM_SUCCESS);
1471 static int
1472 cache_write(cachefile cf, char *buf, int size)
1474 int err;
1476 if (bam_direct == BAM_DIRECT_DBOOT) {
1477 if (gzwrite(cf.out.gzfile, buf, size) < 1) {
1478 bam_error(_("failed to write to %s\n"),
1479 gzerror(cf.out.gzfile, &err));
1480 if (err == Z_ERRNO && bam_verbose) {
1481 bam_error(_("write to file failed: %s: %s\n"),
1482 cf.path, strerror(errno));
1484 return (BAM_ERROR);
1486 } else {
1487 if (write(cf.out.fdfile, buf, size) < 1) {
1488 bam_error(_("write to file failed: %s: %s\n"),
1489 cf.path, strerror(errno));
1490 return (BAM_ERROR);
1493 return (BAM_SUCCESS);
1496 static int
1497 cache_close(cachefile cf)
1499 int ret;
1501 if (bam_direct == BAM_DIRECT_DBOOT) {
1502 if (cf.out.gzfile) {
1503 ret = gzclose(cf.out.gzfile);
1504 if (ret != Z_OK) {
1505 bam_error(_("failed to close file: %s: %s\n"),
1506 cf.path, strerror(errno));
1507 return (BAM_ERROR);
1510 } else {
1511 if (cf.out.fdfile != -1) {
1512 ret = close(cf.out.fdfile);
1513 if (ret != 0) {
1514 bam_error(_("failed to close file: %s: %s\n"),
1515 cf.path, strerror(errno));
1516 return (BAM_ERROR);
1521 return (BAM_SUCCESS);
1524 static int
1525 dircache_updatefile(const char *path, int what)
1527 int ret, exitcode;
1528 char buf[4096 * 4];
1529 FILE *infile;
1530 cachefile outfile, outupdt;
1532 if (bam_nowrite()) {
1533 set_dir_flag(what, NEED_UPDATE);
1534 return (BAM_SUCCESS);
1537 if (!has_cachedir(what))
1538 return (BAM_SUCCESS);
1540 if ((infile = fopen(path, "rb")) == NULL) {
1541 bam_error(_("failed to open file: %s: %s\n"), path,
1542 strerror(errno));
1543 return (BAM_ERROR);
1546 ret = setup_file(get_cachedir(what), path, &outfile);
1547 if (ret == BAM_ERROR) {
1548 exitcode = BAM_ERROR;
1549 goto out;
1551 if (!is_dir_flag_on(what, NO_MULTI)) {
1552 ret = setup_file(get_updatedir(what), path, &outupdt);
1553 if (ret == BAM_ERROR)
1554 set_dir_flag(what, NO_MULTI);
1557 while ((ret = fread(buf, 1, sizeof (buf), infile)) > 0) {
1558 if (cache_write(outfile, buf, ret) == BAM_ERROR) {
1559 exitcode = BAM_ERROR;
1560 goto out;
1562 if (!is_dir_flag_on(what, NO_MULTI))
1563 if (cache_write(outupdt, buf, ret) == BAM_ERROR)
1564 set_dir_flag(what, NO_MULTI);
1567 set_dir_flag(what, NEED_UPDATE);
1568 get_count(what)++;
1569 if (get_count(what) > COUNT_MAX)
1570 set_dir_flag(what, NO_MULTI);
1571 exitcode = BAM_SUCCESS;
1572 out:
1573 (void) fclose(infile);
1574 if (cache_close(outfile) == BAM_ERROR)
1575 exitcode = BAM_ERROR;
1576 if (!is_dir_flag_on(what, NO_MULTI) &&
1577 cache_close(outupdt) == BAM_ERROR)
1578 exitcode = BAM_ERROR;
1579 if (exitcode == BAM_ERROR)
1580 set_flag(UPDATE_ERROR);
1581 return (exitcode);
1584 static int
1585 dircache_updatedir(const char *path, int what, int updt)
1587 int ret;
1588 char dpath[PATH_MAX];
1589 char *strip;
1590 struct stat sb;
1592 strip = (char *)path + strlen(rootbuf);
1594 ret = snprintf(dpath, sizeof (dpath), "%s/%s", updt ?
1595 get_updatedir(what) : get_cachedir(what), strip);
1597 if (ret >= sizeof (dpath)) {
1598 bam_error(_("unable to create path on mountpoint %s, "
1599 "path too long\n"), rootbuf);
1600 set_flag(UPDATE_ERROR);
1601 return (BAM_ERROR);
1604 if (stat(dpath, &sb) == 0 && S_ISDIR(sb.st_mode))
1605 return (BAM_SUCCESS);
1607 if (updt) {
1608 if (!is_dir_flag_on(what, NO_MULTI))
1609 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1)
1610 set_dir_flag(what, NO_MULTI);
1611 } else {
1612 if (!bam_nowrite() && mkdirp(dpath, DIR_PERMS) == -1) {
1613 set_flag(UPDATE_ERROR);
1614 return (BAM_ERROR);
1618 set_dir_flag(what, NEED_UPDATE);
1619 return (BAM_SUCCESS);
1622 #define DO_CACHE_DIR 0
1623 #define DO_UPDATE_DIR 1
1625 #if defined(_LP64) || defined(_LONGLONG_TYPE)
1626 typedef Elf64_Ehdr _elfhdr;
1627 #else
1628 typedef Elf32_Ehdr _elfhdr;
1629 #endif
1632 * This routine updates the contents of the cache directory
1634 static int
1635 update_dircache(const char *path, int flags)
1637 int rc = BAM_SUCCESS;
1639 switch (flags) {
1640 case FTW_F:
1642 int fd;
1643 _elfhdr elf;
1645 if ((fd = open(path, O_RDONLY)) < 0) {
1646 bam_error(_("failed to open file: %s: %s\n"),
1647 path, strerror(errno));
1648 set_flag(UPDATE_ERROR);
1649 rc = BAM_ERROR;
1650 break;
1654 * libelf and gelf would be a cleaner and easier way to handle
1655 * this, but libelf fails compilation if _ILP32 is defined &&
1656 * _FILE_OFFSET_BITS is != 32 ...
1658 if (read(fd, (void *)&elf, sizeof (_elfhdr)) < 0) {
1659 bam_error(_("read failed for file: %s: %s\n"),
1660 path, strerror(errno));
1661 set_flag(UPDATE_ERROR);
1662 (void) close(fd);
1663 rc = BAM_ERROR;
1664 break;
1666 (void) close(fd);
1669 * If the file is not an executable and is not inside an amd64
1670 * directory, we copy it in both the cache directories,
1671 * otherwise, we only copy it inside the 64-bit one.
1673 if (memcmp(elf.e_ident, ELFMAG, 4) != 0) {
1674 if (strstr(path, "/amd64")) {
1675 rc = dircache_updatefile(path, FILE64);
1676 } else {
1677 rc = dircache_updatefile(path, FILE32);
1678 if (rc == BAM_SUCCESS)
1679 rc = dircache_updatefile(path, FILE64);
1681 } else {
1683 * Based on the ELF class we copy the file in the 32-bit
1684 * or the 64-bit cache directory.
1686 if (elf.e_ident[EI_CLASS] == ELFCLASS32) {
1687 rc = dircache_updatefile(path, FILE32);
1688 } else if (elf.e_ident[EI_CLASS] == ELFCLASS64) {
1689 rc = dircache_updatefile(path, FILE64);
1690 } else {
1691 bam_print(_("WARNING: file %s is neither a "
1692 "32-bit nor a 64-bit ELF\n"), path);
1693 /* paranoid */
1694 rc = dircache_updatefile(path, FILE32);
1695 if (rc == BAM_SUCCESS)
1696 rc = dircache_updatefile(path, FILE64);
1699 break;
1701 case FTW_D:
1702 if (strstr(path, "/amd64") == NULL) {
1703 rc = dircache_updatedir(path, FILE32, DO_UPDATE_DIR);
1704 if (rc == BAM_SUCCESS)
1705 rc = dircache_updatedir(path, FILE32,
1706 DO_CACHE_DIR);
1707 } else {
1708 if (has_cachedir(FILE64)) {
1709 rc = dircache_updatedir(path, FILE64,
1710 DO_UPDATE_DIR);
1711 if (rc == BAM_SUCCESS)
1712 rc = dircache_updatedir(path, FILE64,
1713 DO_CACHE_DIR);
1716 break;
1717 default:
1718 rc = BAM_ERROR;
1719 break;
1722 return (rc);
1725 /*ARGSUSED*/
1726 static int
1727 cmpstat(
1728 const char *file,
1729 const struct stat *st,
1730 int flags,
1731 struct FTW *ftw)
1733 uint_t sz;
1734 uint64_t *value;
1735 uint64_t filestat[2];
1736 int error, ret, status;
1738 struct safefile *safefilep;
1739 FILE *fp;
1740 struct stat sb;
1741 regex_t re;
1744 * On SPARC we create/update links too.
1746 if (flags != FTW_F && flags != FTW_D && (flags == FTW_SL &&
1747 !is_flag_on(IS_SPARC_TARGET)))
1748 return (0);
1751 * Ignore broken links
1753 if (flags == FTW_SL && stat(file, &sb) < 0)
1754 return (0);
1757 * new_nvlp may be NULL if there were errors earlier
1758 * but this is not fatal to update determination.
1760 if (walk_arg.new_nvlp) {
1761 filestat[0] = st->st_size;
1762 filestat[1] = st->st_mtime;
1763 error = nvlist_add_uint64_array(walk_arg.new_nvlp,
1764 file + bam_rootlen, filestat, 2);
1765 if (error)
1766 bam_error(_("failed to update stat data for: %s: %s\n"),
1767 file, strerror(error));
1771 * If we are invoked as part of system/filesystem/boot-archive, then
1772 * there are a number of things we should not worry about
1774 if (bam_smf_check) {
1775 /* ignore amd64 modules unless we are booted amd64. */
1776 if (!is_amd64() && strstr(file, "/amd64/") != 0)
1777 return (0);
1779 /* read in list of safe files */
1780 if (safefiles == NULL) {
1781 fp = fopen("/boot/solaris/filelist.safe", "r");
1782 if (fp != NULL) {
1783 safefiles = s_calloc(1,
1784 sizeof (struct safefile));
1785 safefilep = safefiles;
1786 safefilep->name = s_calloc(1, MAXPATHLEN +
1787 MAXNAMELEN);
1788 safefilep->next = NULL;
1789 while (s_fgets(safefilep->name, MAXPATHLEN +
1790 MAXNAMELEN, fp) != NULL) {
1791 safefilep->next = s_calloc(1,
1792 sizeof (struct safefile));
1793 safefilep = safefilep->next;
1794 safefilep->name = s_calloc(1,
1795 MAXPATHLEN + MAXNAMELEN);
1796 safefilep->next = NULL;
1798 (void) fclose(fp);
1804 * On SPARC we create a -path-list file for mkisofs
1806 if (is_flag_on(IS_SPARC_TARGET) && !bam_nowrite()) {
1807 if (flags != FTW_D) {
1808 char *strip;
1810 strip = (char *)file + strlen(rootbuf);
1811 (void) fprintf(walk_arg.sparcfile, "/%s=%s\n", strip,
1812 file);
1817 * We are transitioning from the old model to the dircache or the cache
1818 * directory was removed: create the entry without further checkings.
1820 if (is_flag_on(NEED_CACHE_DIR)) {
1821 if (bam_verbose)
1822 bam_print(_(" new %s\n"), file);
1824 if (is_flag_on(IS_SPARC_TARGET)) {
1825 set_dir_flag(FILE64, NEED_UPDATE);
1826 return (0);
1829 ret = update_dircache(file, flags);
1830 if (ret == BAM_ERROR) {
1831 bam_error(_("directory cache update failed for %s\n"),
1832 file);
1833 return (-1);
1836 return (0);
1840 * We need an update if file doesn't exist in old archive
1842 if (walk_arg.old_nvlp == NULL ||
1843 nvlist_lookup_uint64_array(walk_arg.old_nvlp,
1844 file + bam_rootlen, &value, &sz) != 0) {
1845 if (bam_smf_check) /* ignore new during smf check */
1846 return (0);
1848 if (is_flag_on(IS_SPARC_TARGET)) {
1849 set_dir_flag(FILE64, NEED_UPDATE);
1850 } else {
1851 ret = update_dircache(file, flags);
1852 if (ret == BAM_ERROR) {
1853 bam_error(_("directory cache update "
1854 "failed for %s\n"), file);
1855 return (-1);
1859 if (bam_verbose)
1860 bam_print(_(" new %s\n"), file);
1861 return (0);
1865 * If we got there, the file is already listed as to be included in the
1866 * iso image. We just need to know if we are going to rebuild it or not
1868 if (is_flag_on(IS_SPARC_TARGET) &&
1869 is_dir_flag_on(FILE64, NEED_UPDATE) && !bam_nowrite())
1870 return (0);
1872 * File exists in old archive. Check if file has changed
1874 assert(sz == 2);
1875 bcopy(value, filestat, sizeof (filestat));
1877 if (flags != FTW_D && (filestat[0] != st->st_size ||
1878 filestat[1] != st->st_mtime)) {
1879 if (bam_smf_check) {
1880 safefilep = safefiles;
1881 while (safefilep != NULL &&
1882 safefilep->name[0] != '\0') {
1883 if (regcomp(&re, safefilep->name,
1884 REG_EXTENDED|REG_NOSUB) == 0) {
1885 status = regexec(&re,
1886 file + bam_rootlen, 0, NULL, 0);
1887 regfree(&re);
1888 if (status == 0) {
1889 (void) creat(
1890 NEED_UPDATE_SAFE_FILE,
1891 0644);
1892 return (0);
1895 safefilep = safefilep->next;
1899 if (is_flag_on(IS_SPARC_TARGET)) {
1900 set_dir_flag(FILE64, NEED_UPDATE);
1901 } else {
1902 ret = update_dircache(file, flags);
1903 if (ret == BAM_ERROR) {
1904 bam_error(_("directory cache update failed "
1905 "for %s\n"), file);
1906 return (-1);
1910 if (bam_verbose) {
1911 if (bam_smf_check)
1912 bam_print(" %s\n", file);
1913 else
1914 bam_print(_(" changed %s\n"), file);
1918 return (0);
1922 * Remove a directory path recursively
1924 static int
1925 rmdir_r(char *path)
1927 struct dirent *d = NULL;
1928 DIR *dir = NULL;
1929 char tpath[PATH_MAX];
1930 struct stat sb;
1932 if ((dir = opendir(path)) == NULL)
1933 return (-1);
1935 while ((d = readdir(dir)) != NULL) {
1936 if ((strcmp(d->d_name, ".") != 0) &&
1937 (strcmp(d->d_name, "..") != 0)) {
1938 (void) snprintf(tpath, sizeof (tpath), "%s/%s",
1939 path, d->d_name);
1940 if (stat(tpath, &sb) == 0) {
1941 if (sb.st_mode & S_IFDIR)
1942 (void) rmdir_r(tpath);
1943 else
1944 (void) remove(tpath);
1948 return (remove(path));
1952 * Check if cache directory exists and, if not, create it and update flags
1953 * accordingly. If the path exists, but it's not a directory, a best effort
1954 * attempt to remove and recreate it is made.
1955 * If the user requested a 'purge', always recreate the directory from scratch.
1957 static int
1958 set_cache_dir(char *root, int what)
1960 struct stat sb;
1961 int ret = 0;
1963 ret = snprintf(get_cachedir(what), sizeof (get_cachedir(what)),
1964 "%s%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(), what == FILE64 ?
1965 "/amd64" : "", CACHEDIR_SUFFIX);
1967 if (ret >= sizeof (get_cachedir(what))) {
1968 bam_error(_("unable to create path on mountpoint %s, "
1969 "path too long\n"), rootbuf);
1970 return (BAM_ERROR);
1973 if (bam_purge || is_flag_on(INVALIDATE_CACHE))
1974 (void) rmdir_r(get_cachedir(what));
1976 if (stat(get_cachedir(what), &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
1977 /* best effort unlink attempt, mkdir will catch errors */
1978 (void) unlink(get_cachedir(what));
1980 if (bam_verbose)
1981 bam_print(_("archive cache directory not found: %s\n"),
1982 get_cachedir(what));
1983 ret = mkdir(get_cachedir(what), DIR_PERMS);
1984 if (ret < 0) {
1985 bam_error(_("mkdir of %s failed: %s\n"),
1986 get_cachedir(what), strerror(errno));
1987 get_cachedir(what)[0] = '\0';
1988 return (ret);
1990 set_flag(NEED_CACHE_DIR);
1991 set_dir_flag(what, NO_MULTI);
1994 return (BAM_SUCCESS);
1997 static int
1998 set_update_dir(char *root, int what)
2000 struct stat sb;
2001 int ret;
2003 if (is_dir_flag_on(what, NO_MULTI))
2004 return (BAM_SUCCESS);
2006 if (!bam_extend) {
2007 set_dir_flag(what, NO_MULTI);
2008 return (BAM_SUCCESS);
2011 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
2012 ret = snprintf(get_updatedir(what),
2013 sizeof (get_updatedir(what)), "%s%s%s/amd64%s", root,
2014 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
2015 else
2016 ret = snprintf(get_updatedir(what),
2017 sizeof (get_updatedir(what)), "%s%s%s%s", root,
2018 ARCHIVE_PREFIX, get_machine(), UPDATEDIR_SUFFIX);
2020 if (ret >= sizeof (get_updatedir(what))) {
2021 bam_error(_("unable to create path on mountpoint %s, "
2022 "path too long\n"), rootbuf);
2023 return (BAM_ERROR);
2026 if (stat(get_updatedir(what), &sb) == 0) {
2027 if (S_ISDIR(sb.st_mode))
2028 ret = rmdir_r(get_updatedir(what));
2029 else
2030 ret = unlink(get_updatedir(what));
2032 if (ret != 0)
2033 set_dir_flag(what, NO_MULTI);
2036 if (mkdir(get_updatedir(what), DIR_PERMS) < 0)
2037 set_dir_flag(what, NO_MULTI);
2039 return (BAM_SUCCESS);
2042 static int
2043 is_valid_archive(char *root, int what)
2045 char archive_path[PATH_MAX];
2046 char timestamp_path[PATH_MAX];
2047 struct stat sb, timestamp;
2048 int ret;
2050 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
2051 ret = snprintf(archive_path, sizeof (archive_path),
2052 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
2053 ARCHIVE_SUFFIX);
2054 else
2055 ret = snprintf(archive_path, sizeof (archive_path), "%s%s%s%s",
2056 root, ARCHIVE_PREFIX, get_machine(), ARCHIVE_SUFFIX);
2058 if (ret >= sizeof (archive_path)) {
2059 bam_error(_("unable to create path on mountpoint %s, "
2060 "path too long\n"), rootbuf);
2061 return (BAM_ERROR);
2064 if (stat(archive_path, &sb) != 0) {
2065 if (bam_verbose && !bam_check)
2066 bam_print(_("archive not found: %s\n"), archive_path);
2067 set_dir_flag(what, NEED_UPDATE);
2068 set_dir_flag(what, NO_MULTI);
2069 return (BAM_SUCCESS);
2073 * The timestamp file is used to prevent stale files in the archive
2074 * cache.
2075 * Stale files can happen if the system is booted back and forth across
2076 * the transition from bootadm-before-the-cache to
2077 * bootadm-after-the-cache, since older versions of bootadm don't know
2078 * about the existence of the archive cache.
2080 * Since only bootadm-after-the-cache versions know about about this
2081 * file, we require that the boot archive be older than this file.
2083 ret = snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
2084 FILE_STAT_TIMESTAMP);
2086 if (ret >= sizeof (timestamp_path)) {
2087 bam_error(_("unable to create path on mountpoint %s, "
2088 "path too long\n"), rootbuf);
2089 return (BAM_ERROR);
2092 if (stat(timestamp_path, &timestamp) != 0 ||
2093 sb.st_mtime > timestamp.st_mtime) {
2094 if (bam_verbose && !bam_check)
2095 bam_print(
2096 _("archive cache is out of sync. Rebuilding.\n"));
2098 * Don't generate a false positive for the boot-archive service
2099 * but trigger an update of the archive cache in
2100 * boot-archive-update.
2102 if (bam_smf_check) {
2103 (void) creat(NEED_UPDATE_FILE, 0644);
2104 return (BAM_SUCCESS);
2107 set_flag(INVALIDATE_CACHE);
2108 set_dir_flag(what, NEED_UPDATE);
2109 set_dir_flag(what, NO_MULTI);
2110 return (BAM_SUCCESS);
2113 if (is_flag_on(IS_SPARC_TARGET))
2114 return (BAM_SUCCESS);
2116 if (bam_extend && sb.st_size > BA_SIZE_MAX) {
2117 if (bam_verbose && !bam_check)
2118 bam_print(_("archive %s is bigger than %d bytes and "
2119 "will be rebuilt\n"), archive_path, BA_SIZE_MAX);
2120 set_dir_flag(what, NO_MULTI);
2123 return (BAM_SUCCESS);
2127 * Check flags and presence of required files and directories.
2128 * The force flag and/or absence of files should
2129 * trigger an update.
2130 * Suppress stdout output if check (-n) option is set
2131 * (as -n should only produce parseable output.)
2133 static int
2134 check_flags_and_files(char *root)
2137 struct stat sb;
2138 int ret;
2141 * If archive is missing, create archive
2143 if (is_flag_on(IS_SPARC_TARGET)) {
2144 ret = is_valid_archive(root, FILE64);
2145 if (ret == BAM_ERROR)
2146 return (BAM_ERROR);
2147 } else {
2148 int what = FILE32;
2149 do {
2150 ret = is_valid_archive(root, what);
2151 if (ret == BAM_ERROR)
2152 return (BAM_ERROR);
2153 what++;
2154 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2157 if (bam_nowrite())
2158 return (BAM_SUCCESS);
2162 * check if cache directories exist on x86.
2163 * check (and always open) the cache file on SPARC.
2165 if (is_sparc()) {
2166 ret = snprintf(get_cachedir(FILE64),
2167 sizeof (get_cachedir(FILE64)), "%s%s%s/%s", root,
2168 ARCHIVE_PREFIX, get_machine(), CACHEDIR_SUFFIX);
2170 if (ret >= sizeof (get_cachedir(FILE64))) {
2171 bam_error(_("unable to create path on mountpoint %s, "
2172 "path too long\n"), rootbuf);
2173 return (BAM_ERROR);
2176 if (stat(get_cachedir(FILE64), &sb) != 0) {
2177 set_flag(NEED_CACHE_DIR);
2178 set_dir_flag(FILE64, NEED_UPDATE);
2181 walk_arg.sparcfile = fopen(get_cachedir(FILE64), "w");
2182 if (walk_arg.sparcfile == NULL) {
2183 bam_error(_("failed to open file: %s: %s\n"),
2184 get_cachedir(FILE64), strerror(errno));
2185 return (BAM_ERROR);
2188 set_dir_present(FILE64);
2189 } else {
2190 int what = FILE32;
2192 do {
2193 if (set_cache_dir(root, what) != 0)
2194 return (BAM_ERROR);
2196 set_dir_present(what);
2198 if (set_update_dir(root, what) != 0)
2199 return (BAM_ERROR);
2200 what++;
2201 } while (bam_direct == BAM_DIRECT_DBOOT && what < CACHEDIR_NUM);
2205 * if force, create archive unconditionally
2207 if (bam_force) {
2208 if (!is_sparc())
2209 set_dir_flag(FILE32, NEED_UPDATE);
2210 set_dir_flag(FILE64, NEED_UPDATE);
2211 if (bam_verbose)
2212 bam_print(_("forced update of archive requested\n"));
2213 return (BAM_SUCCESS);
2216 return (BAM_SUCCESS);
2219 static error_t
2220 read_one_list(char *root, filelist_t *flistp, char *filelist)
2222 char path[PATH_MAX];
2223 FILE *fp;
2224 char buf[BAM_MAXLINE];
2225 const char *fcn = "read_one_list()";
2227 (void) snprintf(path, sizeof (path), "%s%s", root, filelist);
2229 fp = fopen(path, "r");
2230 if (fp == NULL) {
2231 BAM_DPRINTF(("%s: failed to open archive filelist: %s: %s\n",
2232 fcn, path, strerror(errno)));
2233 return (BAM_ERROR);
2235 while (s_fgets(buf, sizeof (buf), fp) != NULL) {
2236 /* skip blank lines */
2237 if (strspn(buf, " \t") == strlen(buf))
2238 continue;
2239 append_to_flist(flistp, buf);
2241 if (fclose(fp) != 0) {
2242 bam_error(_("failed to close file: %s: %s\n"),
2243 path, strerror(errno));
2244 return (BAM_ERROR);
2246 return (BAM_SUCCESS);
2249 static error_t
2250 read_list(char *root, filelist_t *flistp)
2252 char path[PATH_MAX];
2253 char cmd[PATH_MAX];
2254 struct stat sb;
2255 int n, rval;
2256 const char *fcn = "read_list()";
2258 flistp->head = flistp->tail = NULL;
2261 * build and check path to extract_boot_filelist.ksh
2263 n = snprintf(path, sizeof (path), "%s%s", root, EXTRACT_BOOT_FILELIST);
2264 if (n >= sizeof (path)) {
2265 bam_error(_("archive filelist is empty\n"));
2266 return (BAM_ERROR);
2269 if (is_safe_exec(path) == BAM_ERROR)
2270 return (BAM_ERROR);
2273 * If extract_boot_filelist is present, exec it, otherwise read
2274 * the filelists directly, for compatibility with older images.
2276 if (stat(path, &sb) == 0) {
2278 * build arguments to exec extract_boot_filelist.ksh
2280 char *rootarg, *platarg;
2281 int platarglen = 1, rootarglen = 1;
2282 if (strlen(root) > 1)
2283 rootarglen += strlen(root) + strlen("-R ");
2284 if (bam_alt_platform)
2285 platarglen += strlen(bam_platform) + strlen("-p ");
2286 platarg = s_calloc(1, platarglen);
2287 rootarg = s_calloc(1, rootarglen);
2288 *platarg = 0;
2289 *rootarg = 0;
2291 if (strlen(root) > 1) {
2292 (void) snprintf(rootarg, rootarglen,
2293 "-R %s", root);
2295 if (bam_alt_platform) {
2296 (void) snprintf(platarg, platarglen,
2297 "-p %s", bam_platform);
2299 n = snprintf(cmd, sizeof (cmd), "%s %s %s /%s /%s",
2300 path, rootarg, platarg, BOOT_FILE_LIST, ETC_FILE_LIST);
2301 free(platarg);
2302 free(rootarg);
2303 if (n >= sizeof (cmd)) {
2304 bam_error(_("archive filelist is empty\n"));
2305 return (BAM_ERROR);
2307 if (exec_cmd(cmd, flistp) != 0) {
2308 BAM_DPRINTF(("%s: failed to open archive "
2309 "filelist: %s: %s\n", fcn, path, strerror(errno)));
2310 return (BAM_ERROR);
2312 } else {
2314 * Read current lists of files - only the first is mandatory
2316 rval = read_one_list(root, flistp, BOOT_FILE_LIST);
2317 if (rval != BAM_SUCCESS)
2318 return (rval);
2319 (void) read_one_list(root, flistp, ETC_FILE_LIST);
2322 if (flistp->head == NULL) {
2323 bam_error(_("archive filelist is empty\n"));
2324 return (BAM_ERROR);
2327 return (BAM_SUCCESS);
2330 static void
2331 getoldstat(char *root)
2333 char path[PATH_MAX];
2334 int fd, error;
2335 struct stat sb;
2336 char *ostat;
2338 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
2339 fd = open(path, O_RDONLY);
2340 if (fd == -1) {
2341 if (bam_verbose)
2342 bam_print(_("failed to open file: %s: %s\n"),
2343 path, strerror(errno));
2344 goto out_err;
2347 if (fstat(fd, &sb) != 0) {
2348 bam_error(_("stat of file failed: %s: %s\n"), path,
2349 strerror(errno));
2350 goto out_err;
2353 ostat = s_calloc(1, sb.st_size);
2355 if (read(fd, ostat, sb.st_size) != sb.st_size) {
2356 bam_error(_("read failed for file: %s: %s\n"), path,
2357 strerror(errno));
2358 free(ostat);
2359 goto out_err;
2362 (void) close(fd);
2363 fd = -1;
2365 walk_arg.old_nvlp = NULL;
2366 error = nvlist_unpack(ostat, sb.st_size, &walk_arg.old_nvlp, 0);
2368 free(ostat);
2370 if (error) {
2371 bam_error(_("failed to unpack stat data: %s: %s\n"),
2372 path, strerror(error));
2373 walk_arg.old_nvlp = NULL;
2374 goto out_err;
2375 } else {
2376 return;
2379 out_err:
2380 if (fd != -1)
2381 (void) close(fd);
2382 if (!is_flag_on(IS_SPARC_TARGET))
2383 set_dir_flag(FILE32, NEED_UPDATE);
2384 set_dir_flag(FILE64, NEED_UPDATE);
2387 /* Best effort stale entry removal */
2388 static void
2389 delete_stale(char *file, int what)
2391 char path[PATH_MAX];
2392 struct stat sb;
2394 (void) snprintf(path, sizeof (path), "%s/%s", get_cachedir(what), file);
2395 if (!bam_check && stat(path, &sb) == 0) {
2396 if (sb.st_mode & S_IFDIR)
2397 (void) rmdir_r(path);
2398 else
2399 (void) unlink(path);
2401 set_dir_flag(what, (NEED_UPDATE | NO_MULTI));
2406 * Checks if a file in the current (old) archive has
2407 * been deleted from the root filesystem.
2409 static void
2410 check4stale(char *root)
2412 nvpair_t *nvp;
2413 nvlist_t *nvlp;
2414 char *file;
2415 char path[PATH_MAX];
2418 * Skip stale file check during smf check
2420 if (bam_smf_check)
2421 return;
2424 * If we need to (re)create the cache, there's no need to check for
2425 * stale files
2427 if (is_flag_on(NEED_CACHE_DIR))
2428 return;
2430 /* Nothing to do if no old stats */
2431 if ((nvlp = walk_arg.old_nvlp) == NULL)
2432 return;
2434 for (nvp = nvlist_next_nvpair(nvlp, NULL); nvp;
2435 nvp = nvlist_next_nvpair(nvlp, nvp)) {
2436 file = nvpair_name(nvp);
2437 if (file == NULL)
2438 continue;
2439 (void) snprintf(path, sizeof (path), "%s/%s",
2440 root, file);
2441 if (access(path, F_OK) < 0) {
2442 int what;
2444 if (bam_verbose)
2445 bam_print(_(" stale %s\n"), path);
2447 if (is_flag_on(IS_SPARC_TARGET)) {
2448 set_dir_flag(FILE64, NEED_UPDATE);
2449 } else {
2450 for (what = FILE32; what < CACHEDIR_NUM; what++)
2451 if (has_cachedir(what))
2452 delete_stale(file, what);
2458 static void
2459 create_newstat(void)
2461 int error;
2463 error = nvlist_alloc(&walk_arg.new_nvlp, NV_UNIQUE_NAME, 0);
2464 if (error) {
2466 * Not fatal - we can still create archive
2468 walk_arg.new_nvlp = NULL;
2469 bam_error(_("failed to create stat data: %s\n"),
2470 strerror(error));
2474 static int
2475 walk_list(char *root, filelist_t *flistp)
2477 char path[PATH_MAX];
2478 line_t *lp;
2480 for (lp = flistp->head; lp; lp = lp->next) {
2482 * Don't follow symlinks. A symlink must refer to
2483 * a file that would appear in the archive through
2484 * a direct reference. This matches the archive
2485 * construction behavior.
2487 (void) snprintf(path, sizeof (path), "%s%s", root, lp->line);
2488 if (nftw(path, cmpstat, 20, FTW_PHYS) == -1) {
2489 if (is_flag_on(UPDATE_ERROR))
2490 return (BAM_ERROR);
2492 * Some files may not exist.
2493 * For example: etc/rtc_config on a x86 diskless system
2494 * Emit verbose message only
2496 if (bam_verbose)
2497 bam_print(_("cannot find: %s: %s\n"),
2498 path, strerror(errno));
2502 return (BAM_SUCCESS);
2506 * Update the timestamp file.
2508 static void
2509 update_timestamp(char *root)
2511 char timestamp_path[PATH_MAX];
2513 /* this path length has already been checked in check_flags_and_files */
2514 (void) snprintf(timestamp_path, sizeof (timestamp_path), "%s%s", root,
2515 FILE_STAT_TIMESTAMP);
2518 * recreate the timestamp file. Since an outdated or absent timestamp
2519 * file translates in a complete rebuild of the archive cache, notify
2520 * the user of the performance issue.
2522 if (creat(timestamp_path, FILE_STAT_MODE) < 0) {
2523 bam_error(_("failed to open file: %s: %s\n"), timestamp_path,
2524 strerror(errno));
2525 bam_error(_("failed to update the timestamp file, next"
2526 " archive update may experience reduced performance\n"));
2531 static void
2532 savenew(char *root)
2534 char path[PATH_MAX];
2535 char path2[PATH_MAX];
2536 size_t sz;
2537 char *nstat;
2538 int fd, wrote, error;
2540 nstat = NULL;
2541 sz = 0;
2542 error = nvlist_pack(walk_arg.new_nvlp, &nstat, &sz,
2543 NV_ENCODE_XDR, 0);
2544 if (error) {
2545 bam_error(_("failed to pack stat data: %s\n"),
2546 strerror(error));
2547 return;
2550 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT_TMP);
2551 fd = open(path, O_RDWR|O_CREAT|O_TRUNC, FILE_STAT_MODE);
2552 if (fd == -1) {
2553 bam_error(_("failed to open file: %s: %s\n"), path,
2554 strerror(errno));
2555 free(nstat);
2556 return;
2558 wrote = write(fd, nstat, sz);
2559 if (wrote != sz) {
2560 bam_error(_("write to file failed: %s: %s\n"), path,
2561 strerror(errno));
2562 (void) close(fd);
2563 free(nstat);
2564 return;
2566 (void) close(fd);
2567 free(nstat);
2569 (void) snprintf(path2, sizeof (path2), "%s%s", root, FILE_STAT);
2570 if (rename(path, path2) != 0) {
2571 bam_error(_("rename to file failed: %s: %s\n"), path2,
2572 strerror(errno));
2576 #define init_walk_args() bzero(&walk_arg, sizeof (walk_arg))
2578 static void
2579 clear_walk_args(void)
2581 nvlist_free(walk_arg.old_nvlp);
2582 nvlist_free(walk_arg.new_nvlp);
2583 if (walk_arg.sparcfile)
2584 (void) fclose(walk_arg.sparcfile);
2585 walk_arg.old_nvlp = NULL;
2586 walk_arg.new_nvlp = NULL;
2587 walk_arg.sparcfile = NULL;
2591 * Returns:
2592 * 0 - no update necessary
2593 * 1 - update required.
2594 * BAM_ERROR (-1) - An error occurred
2596 * Special handling for check (-n):
2597 * ================================
2598 * The check (-n) option produces parseable output.
2599 * To do this, we suppress all stdout messages unrelated
2600 * to out of sync files.
2601 * All stderr messages are still printed though.
2604 static int
2605 update_required(char *root)
2607 struct stat sb;
2608 char path[PATH_MAX];
2609 filelist_t flist;
2610 filelist_t *flistp = &flist;
2611 int ret;
2613 flistp->head = flistp->tail = NULL;
2615 if (is_sparc())
2616 set_flag(IS_SPARC_TARGET);
2619 * Check if cache directories and archives are present
2622 ret = check_flags_and_files(root);
2623 if (ret < 0)
2624 return (BAM_ERROR);
2627 * In certain deployment scenarios, filestat may not
2628 * exist. Do not stop the boot process, but trigger an update
2629 * of the archives (which will recreate filestat.ramdisk).
2631 if (bam_smf_check) {
2632 (void) snprintf(path, sizeof (path), "%s%s", root, FILE_STAT);
2633 if (stat(path, &sb) != 0) {
2634 (void) creat(NEED_UPDATE_FILE, 0644);
2635 return (0);
2639 getoldstat(root);
2642 * Check if the archive contains files that are no longer
2643 * present on the root filesystem.
2645 check4stale(root);
2648 * read list of files
2650 if (read_list(root, flistp) != BAM_SUCCESS) {
2651 clear_walk_args();
2652 return (BAM_ERROR);
2655 assert(flistp->head && flistp->tail);
2658 * At this point either the update is required
2659 * or the decision is pending. In either case
2660 * we need to create new stat nvlist
2662 create_newstat();
2664 * This walk does 2 things:
2665 * - gets new stat data for every file
2666 * - (optional) compare old and new stat data
2668 ret = walk_list(root, &flist);
2670 /* done with the file list */
2671 filelist_free(flistp);
2673 /* something went wrong */
2675 if (ret == BAM_ERROR) {
2676 bam_error(_("Failed to gather cache files, archives "
2677 "generation aborted\n"));
2678 return (BAM_ERROR);
2681 if (walk_arg.new_nvlp == NULL) {
2682 if (walk_arg.sparcfile != NULL)
2683 (void) fclose(walk_arg.sparcfile);
2684 bam_error(_("cannot create new stat data\n"));
2687 /* If nothing was updated, discard newstat. */
2689 if (!is_dir_flag_on(FILE32, NEED_UPDATE) &&
2690 !is_dir_flag_on(FILE64, NEED_UPDATE)) {
2691 clear_walk_args();
2692 return (0);
2695 if (walk_arg.sparcfile != NULL)
2696 (void) fclose(walk_arg.sparcfile);
2698 return (1);
2701 static int
2702 flushfs(char *root)
2704 char cmd[PATH_MAX + 30];
2706 (void) snprintf(cmd, sizeof (cmd), "%s -f \"%s\" 2>/dev/null",
2707 LOCKFS_PATH, root);
2709 return (exec_cmd(cmd, NULL));
2712 static int
2713 do_archive_copy(char *source, char *dest)
2716 sync();
2718 /* the equivalent of mv archive-new-$pid boot_archive */
2719 if (rename(source, dest) != 0) {
2720 (void) unlink(source);
2721 return (BAM_ERROR);
2724 if (flushfs(bam_root) != 0)
2725 sync();
2727 return (BAM_SUCCESS);
2730 static int
2731 check_cmdline(filelist_t flist)
2733 line_t *lp;
2735 for (lp = flist.head; lp; lp = lp->next) {
2736 if (strstr(lp->line, "Error:") != NULL ||
2737 strstr(lp->line, "Inode number overflow") != NULL) {
2738 (void) fprintf(stderr, "%s\n", lp->line);
2739 return (BAM_ERROR);
2743 return (BAM_SUCCESS);
2746 static void
2747 dump_errormsg(filelist_t flist)
2749 line_t *lp;
2751 for (lp = flist.head; lp; lp = lp->next)
2752 (void) fprintf(stderr, "%s\n", lp->line);
2755 static int
2756 check_archive(char *dest)
2758 struct stat sb;
2760 if (stat(dest, &sb) != 0 || !S_ISREG(sb.st_mode) ||
2761 sb.st_size < 10000) {
2762 bam_error(_("archive file %s not generated correctly\n"), dest);
2763 (void) unlink(dest);
2764 return (BAM_ERROR);
2767 return (BAM_SUCCESS);
2770 static boolean_t
2771 is_be(char *root)
2773 zfs_handle_t *zhp;
2774 libzfs_handle_t *hdl;
2775 be_node_list_t *be_nodes = NULL;
2776 be_node_list_t *cur_be;
2777 boolean_t be_exist = B_FALSE;
2778 char ds_path[ZFS_MAX_DATASET_NAME_LEN];
2780 if (!is_zfs(root))
2781 return (B_FALSE);
2783 * Get dataset for mountpoint
2785 if ((hdl = libzfs_init()) == NULL)
2786 return (B_FALSE);
2788 if ((zhp = zfs_path_to_zhandle(hdl, root,
2789 ZFS_TYPE_FILESYSTEM)) == NULL) {
2790 libzfs_fini(hdl);
2791 return (B_FALSE);
2794 (void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
2797 * Check if the current dataset is BE
2799 if (be_list(NULL, &be_nodes) == BE_SUCCESS) {
2800 for (cur_be = be_nodes; cur_be != NULL;
2801 cur_be = cur_be->be_next_node) {
2804 * Because we guarantee that cur_be->be_root_ds
2805 * is null-terminated by internal data structure,
2806 * we can safely use strcmp()
2808 if (strcmp(ds_path, cur_be->be_root_ds) == 0) {
2809 be_exist = B_TRUE;
2810 break;
2813 be_free_list(be_nodes);
2815 zfs_close(zhp);
2816 libzfs_fini(hdl);
2818 return (be_exist);
2822 * Returns 1 if mkiso is in the expected PATH, 0 otherwise
2824 static int
2825 is_mkisofs()
2827 if (access(MKISOFS_PATH, X_OK) == 0)
2828 return (1);
2829 return (0);
2832 #define MKISO_PARAMS " -quiet -graft-points -dlrDJN -relaxed-filenames "
2834 static int
2835 create_sparc_archive(char *archive, char *tempname, char *bootblk, char *list)
2837 int ret;
2838 char cmdline[3 * PATH_MAX + 64];
2839 filelist_t flist = {0};
2840 const char *func = "create_sparc_archive()";
2842 if (access(bootblk, R_OK) == 1) {
2843 bam_error(_("unable to access bootblk file : %s\n"), bootblk);
2844 return (BAM_ERROR);
2848 * Prepare mkisofs command line and execute it
2850 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -G %s -o \"%s\" "
2851 "-path-list \"%s\" 2>&1", MKISOFS_PATH, MKISO_PARAMS, bootblk,
2852 tempname, list);
2854 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
2856 ret = exec_cmd(cmdline, &flist);
2857 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
2858 dump_errormsg(flist);
2859 goto out_err;
2862 filelist_free(&flist);
2865 * Prepare dd command line to copy the bootblk on the new archive and
2866 * execute it
2868 (void) snprintf(cmdline, sizeof (cmdline), "%s if=\"%s\" of=\"%s\""
2869 " bs=1b oseek=1 count=15 conv=notrunc conv=sync 2>&1", DD_PATH_USR,
2870 bootblk, tempname);
2872 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
2874 ret = exec_cmd(cmdline, &flist);
2875 if (ret != 0 || check_cmdline(flist) == BAM_ERROR)
2876 goto out_err;
2878 filelist_free(&flist);
2880 /* Did we get a valid archive ? */
2881 if (check_archive(tempname) == BAM_ERROR)
2882 return (BAM_ERROR);
2884 return (do_archive_copy(tempname, archive));
2886 out_err:
2887 filelist_free(&flist);
2888 bam_error(_("boot-archive creation FAILED, command: '%s'\n"), cmdline);
2889 (void) unlink(tempname);
2890 return (BAM_ERROR);
2893 static unsigned int
2894 from_733(unsigned char *s)
2896 int i;
2897 unsigned int ret = 0;
2899 for (i = 0; i < 4; i++)
2900 ret |= s[i] << (8 * i);
2902 return (ret);
2905 static void
2906 to_733(unsigned char *s, unsigned int val)
2908 int i;
2910 for (i = 0; i < 4; i++)
2911 s[i] = s[7-i] = (val >> (8 * i)) & 0xFF;
2915 * creates sha1 hash of archive
2917 static int
2918 digest_archive(const char *archive)
2920 char *archive_hash;
2921 char *hash;
2922 int ret;
2923 FILE *fp;
2925 (void) asprintf(&archive_hash, "%s.hash", archive);
2926 if (archive_hash == NULL)
2927 return (BAM_ERROR);
2929 if ((ret = bootadm_digest(archive, &hash)) == BAM_ERROR) {
2930 free(archive_hash);
2931 return (ret);
2934 fp = fopen(archive_hash, "w");
2935 if (fp == NULL) {
2936 free(archive_hash);
2937 free(hash);
2938 return (BAM_ERROR);
2941 (void) fprintf(fp, "%s\n", hash);
2942 (void) fclose(fp);
2943 free(hash);
2944 free(archive_hash);
2945 return (BAM_SUCCESS);
2949 * Extends the current boot archive without recreating it from scratch
2951 static int
2952 extend_iso_archive(char *archive, char *tempname, char *update_dir)
2954 int fd = -1, newfd = -1, ret, i;
2955 int next_session = 0, new_size = 0;
2956 char cmdline[3 * PATH_MAX + 64];
2957 const char *func = "extend_iso_archive()";
2958 filelist_t flist = {0};
2959 struct iso_pdesc saved_desc[MAX_IVDs];
2961 fd = open(archive, O_RDWR);
2962 if (fd == -1) {
2963 if (bam_verbose)
2964 bam_error(_("failed to open file: %s: %s\n"),
2965 archive, strerror(errno));
2966 goto out_err;
2970 * A partial read is likely due to a corrupted file
2972 ret = pread64(fd, saved_desc, sizeof (saved_desc),
2973 VOLDESC_OFF * CD_BLOCK);
2974 if (ret != sizeof (saved_desc)) {
2975 if (bam_verbose)
2976 bam_error(_("read failed for file: %s: %s\n"),
2977 archive, strerror(errno));
2978 goto out_err;
2981 if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
2982 if (bam_verbose)
2983 bam_error(_("iso descriptor signature for %s is "
2984 "invalid\n"), archive);
2985 goto out_err;
2989 * Read primary descriptor and locate next_session offset (it should
2990 * point to the end of the archive)
2992 next_session = P2ROUNDUP(from_733(saved_desc[0].volume_space_size), 16);
2994 (void) snprintf(cmdline, sizeof (cmdline), "%s -C 16,%d -M %s %s -o \""
2995 "%s\" \"%s\" 2>&1", MKISOFS_PATH, next_session, archive,
2996 MKISO_PARAMS, tempname, update_dir);
2998 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3000 ret = exec_cmd(cmdline, &flist);
3001 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3002 if (bam_verbose) {
3003 bam_error(_("Command '%s' failed while generating "
3004 "multisession archive\n"), cmdline);
3005 dump_errormsg(flist);
3007 goto out_flist_err;
3009 filelist_free(&flist);
3011 newfd = open(tempname, O_RDONLY);
3012 if (newfd == -1) {
3013 if (bam_verbose)
3014 bam_error(_("failed to open file: %s: %s\n"),
3015 archive, strerror(errno));
3016 goto out_err;
3019 ret = pread64(newfd, saved_desc, sizeof (saved_desc),
3020 VOLDESC_OFF * CD_BLOCK);
3021 if (ret != sizeof (saved_desc)) {
3022 if (bam_verbose)
3023 bam_error(_("read failed for file: %s: %s\n"),
3024 archive, strerror(errno));
3025 goto out_err;
3028 if (memcmp(saved_desc[0].type, "\1CD001", 6)) {
3029 if (bam_verbose)
3030 bam_error(_("iso descriptor signature for %s is "
3031 "invalid\n"), archive);
3032 goto out_err;
3035 new_size = from_733(saved_desc[0].volume_space_size) + next_session;
3036 to_733(saved_desc[0].volume_space_size, new_size);
3038 for (i = 1; i < MAX_IVDs; i++) {
3039 if (saved_desc[i].type[0] == (unsigned char)255)
3040 break;
3041 if (memcmp(saved_desc[i].id, "CD001", 5))
3042 break;
3044 if (bam_verbose)
3045 bam_print("%s: Updating descriptor entry [%d]\n", func,
3048 to_733(saved_desc[i].volume_space_size, new_size);
3051 ret = pwrite64(fd, saved_desc, DVD_BLOCK, VOLDESC_OFF*CD_BLOCK);
3052 if (ret != DVD_BLOCK) {
3053 if (bam_verbose)
3054 bam_error(_("write to file failed: %s: %s\n"),
3055 archive, strerror(errno));
3056 goto out_err;
3058 (void) close(newfd);
3059 newfd = -1;
3061 ret = fsync(fd);
3062 if (ret != 0)
3063 sync();
3065 ret = close(fd);
3066 if (ret != 0) {
3067 if (bam_verbose)
3068 bam_error(_("failed to close file: %s: %s\n"),
3069 archive, strerror(errno));
3070 return (BAM_ERROR);
3072 fd = -1;
3074 (void) snprintf(cmdline, sizeof (cmdline), "%s if=%s of=%s bs=32k "
3075 "seek=%d conv=sync 2>&1", DD_PATH_USR, tempname, archive,
3076 (next_session/16));
3078 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3080 ret = exec_cmd(cmdline, &flist);
3081 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3082 if (bam_verbose)
3083 bam_error(_("Command '%s' failed while generating "
3084 "multisession archive\n"), cmdline);
3085 goto out_flist_err;
3087 filelist_free(&flist);
3089 (void) unlink(tempname);
3091 if (digest_archive(archive) == BAM_ERROR && bam_verbose)
3092 bam_print("boot archive hashing failed\n");
3094 if (flushfs(bam_root) != 0)
3095 sync();
3097 if (bam_verbose)
3098 bam_print("boot archive updated successfully\n");
3100 return (BAM_SUCCESS);
3102 out_flist_err:
3103 filelist_free(&flist);
3104 out_err:
3105 if (fd != -1)
3106 (void) close(fd);
3107 if (newfd != -1)
3108 (void) close(newfd);
3109 return (BAM_ERROR);
3112 static int
3113 create_x86_archive(char *archive, char *tempname, char *update_dir)
3115 int ret;
3116 char cmdline[3 * PATH_MAX + 64];
3117 filelist_t flist = {0};
3118 const char *func = "create_x86_archive()";
3120 (void) snprintf(cmdline, sizeof (cmdline), "%s %s -o \"%s\" \"%s\" "
3121 "2>&1", MKISOFS_PATH, MKISO_PARAMS, tempname, update_dir);
3123 BAM_DPRINTF(("%s: executing: %s\n", func, cmdline));
3125 ret = exec_cmd(cmdline, &flist);
3126 if (ret != 0 || check_cmdline(flist) == BAM_ERROR) {
3127 bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
3128 cmdline);
3129 dump_errormsg(flist);
3130 filelist_free(&flist);
3131 (void) unlink(tempname);
3132 return (BAM_ERROR);
3135 filelist_free(&flist);
3137 if (check_archive(tempname) == BAM_ERROR)
3138 return (BAM_ERROR);
3140 return (do_archive_copy(tempname, archive));
3143 static int
3144 mkisofs_archive(char *root, int what)
3146 int ret;
3147 char temp[PATH_MAX];
3148 char bootblk[PATH_MAX];
3149 char boot_archive[PATH_MAX];
3151 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
3152 ret = snprintf(temp, sizeof (temp),
3153 "%s%s%s/amd64/archive-new-%d", root, ARCHIVE_PREFIX,
3154 get_machine(), getpid());
3155 else
3156 ret = snprintf(temp, sizeof (temp), "%s%s%s/archive-new-%d",
3157 root, ARCHIVE_PREFIX, get_machine(), getpid());
3159 if (ret >= sizeof (temp))
3160 goto out_path_err;
3162 if (what == FILE64 && !is_flag_on(IS_SPARC_TARGET))
3163 ret = snprintf(boot_archive, sizeof (boot_archive),
3164 "%s%s%s/amd64%s", root, ARCHIVE_PREFIX, get_machine(),
3165 ARCHIVE_SUFFIX);
3166 else
3167 ret = snprintf(boot_archive, sizeof (boot_archive),
3168 "%s%s%s%s", root, ARCHIVE_PREFIX, get_machine(),
3169 ARCHIVE_SUFFIX);
3171 if (ret >= sizeof (boot_archive))
3172 goto out_path_err;
3174 bam_print("updating %s\n", boot_archive);
3176 if (is_flag_on(IS_SPARC_TARGET)) {
3177 ret = snprintf(bootblk, sizeof (bootblk),
3178 "%s/platform/%s/lib/fs/hsfs/bootblk", root, get_machine());
3179 if (ret >= sizeof (bootblk))
3180 goto out_path_err;
3182 ret = create_sparc_archive(boot_archive, temp, bootblk,
3183 get_cachedir(what));
3184 } else {
3185 if (!is_dir_flag_on(what, NO_MULTI)) {
3186 if (bam_verbose)
3187 bam_print("Attempting to extend x86 archive: "
3188 "%s\n", boot_archive);
3190 ret = extend_iso_archive(boot_archive, temp,
3191 get_updatedir(what));
3192 if (ret == BAM_SUCCESS) {
3193 if (bam_verbose)
3194 bam_print("Successfully extended %s\n",
3195 boot_archive);
3197 (void) rmdir_r(get_updatedir(what));
3198 return (BAM_SUCCESS);
3202 * The boot archive will be recreated from scratch. We get here
3203 * if at least one of these conditions is true:
3204 * - bootadm was called without the -e switch
3205 * - the archive (or the archive cache) doesn't exist
3206 * - archive size is bigger than BA_SIZE_MAX
3207 * - more than COUNT_MAX files need to be updated
3208 * - an error occourred either populating the /updates directory
3209 * or extend_iso_archive() failed
3211 if (bam_verbose)
3212 bam_print("Unable to extend %s... rebuilding archive\n",
3213 boot_archive);
3215 if (get_updatedir(what)[0] != '\0')
3216 (void) rmdir_r(get_updatedir(what));
3219 ret = create_x86_archive(boot_archive, temp,
3220 get_cachedir(what));
3223 if (digest_archive(boot_archive) == BAM_ERROR && bam_verbose)
3224 bam_print("boot archive hashing failed\n");
3226 if (ret == BAM_SUCCESS && bam_verbose)
3227 bam_print("Successfully created %s\n", boot_archive);
3229 return (ret);
3231 out_path_err:
3232 bam_error(_("unable to create path on mountpoint %s, path too long\n"),
3233 root);
3234 return (BAM_ERROR);
3237 static error_t
3238 create_ramdisk(char *root)
3240 char *cmdline, path[PATH_MAX];
3241 size_t len;
3242 struct stat sb;
3243 int ret, what, status = BAM_SUCCESS;
3245 /* If there is mkisofs, use it to create the required archives */
3246 if (is_mkisofs()) {
3247 for (what = FILE32; what < CACHEDIR_NUM; what++) {
3248 if (has_cachedir(what) && is_dir_flag_on(what,
3249 NEED_UPDATE)) {
3250 ret = mkisofs_archive(root, what);
3251 if (ret != 0)
3252 status = BAM_ERROR;
3255 return (status);
3259 * Else setup command args for create_ramdisk.ksh for the UFS archives
3260 * Note: we will not create hash here, CREATE_RAMDISK should create it.
3262 if (bam_verbose)
3263 bam_print("mkisofs not found, creating UFS archive\n");
3265 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3266 if (stat(path, &sb) != 0) {
3267 bam_error(_("archive creation file not found: %s: %s\n"),
3268 path, strerror(errno));
3269 return (BAM_ERROR);
3272 if (is_safe_exec(path) == BAM_ERROR)
3273 return (BAM_ERROR);
3275 len = strlen(path) + strlen(root) + 10; /* room for space + -R */
3276 if (bam_alt_platform)
3277 len += strlen(bam_platform) + strlen("-p ");
3278 cmdline = s_calloc(1, len);
3280 if (bam_alt_platform) {
3281 assert(strlen(root) > 1);
3282 (void) snprintf(cmdline, len, "%s -p %s -R %s",
3283 path, bam_platform, root);
3284 /* chop off / at the end */
3285 cmdline[strlen(cmdline) - 1] = '\0';
3286 } else if (strlen(root) > 1) {
3287 (void) snprintf(cmdline, len, "%s -R %s", path, root);
3288 /* chop off / at the end */
3289 cmdline[strlen(cmdline) - 1] = '\0';
3290 } else
3291 (void) snprintf(cmdline, len, "%s", path);
3293 if (exec_cmd(cmdline, NULL) != 0) {
3294 bam_error(_("boot-archive creation FAILED, command: '%s'\n"),
3295 cmdline);
3296 free(cmdline);
3297 return (BAM_ERROR);
3299 free(cmdline);
3301 * The existence of the expected archives used to be
3302 * verified here. This check is done in create_ramdisk as
3303 * it needs to be in sync with the altroot operated upon.
3305 return (BAM_SUCCESS);
3309 * Checks if target filesystem is on a ramdisk
3310 * 1 - is miniroot
3311 * 0 - is not
3312 * When in doubt assume it is not a ramdisk.
3314 static int
3315 is_ramdisk(char *root)
3317 struct extmnttab mnt;
3318 FILE *fp;
3319 int found;
3320 char mntpt[PATH_MAX];
3321 char *cp;
3324 * There are 3 situations where creating archive is
3325 * of dubious value:
3326 * - create boot_archive on a lofi-mounted boot_archive
3327 * - create it on a ramdisk which is the root filesystem
3328 * - create it on a ramdisk mounted somewhere else
3329 * The first is not easy to detect and checking for it is not
3330 * worth it.
3331 * The other two conditions are handled here
3333 fp = fopen(MNTTAB, "r");
3334 if (fp == NULL) {
3335 bam_error(_("failed to open file: %s: %s\n"),
3336 MNTTAB, strerror(errno));
3337 return (0);
3340 resetmnttab(fp);
3343 * Remove any trailing / from the mount point
3345 (void) strlcpy(mntpt, root, sizeof (mntpt));
3346 if (strcmp(root, "/") != 0) {
3347 cp = mntpt + strlen(mntpt) - 1;
3348 if (*cp == '/')
3349 *cp = '\0';
3351 found = 0;
3352 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
3353 if (strcmp(mnt.mnt_mountp, mntpt) == 0) {
3354 found = 1;
3355 break;
3359 if (!found) {
3360 if (bam_verbose)
3361 bam_error(_("alternate root %s not in mnttab\n"),
3362 mntpt);
3363 (void) fclose(fp);
3364 return (0);
3367 if (strncmp(mnt.mnt_special, RAMDISK_SPECIAL,
3368 strlen(RAMDISK_SPECIAL)) == 0) {
3369 if (bam_verbose)
3370 bam_error(_("%s is on a ramdisk device\n"), bam_root);
3371 (void) fclose(fp);
3372 return (1);
3375 (void) fclose(fp);
3377 return (0);
3380 static int
3381 is_boot_archive(char *root)
3383 char path[PATH_MAX];
3384 struct stat sb;
3385 int error;
3386 const char *fcn = "is_boot_archive()";
3389 * We can't create an archive without the create_ramdisk script
3391 (void) snprintf(path, sizeof (path), "%s/%s", root, CREATE_RAMDISK);
3392 error = stat(path, &sb);
3393 INJECT_ERROR1("NOT_ARCHIVE_BASED", error = -1);
3394 if (error == -1) {
3395 if (bam_verbose)
3396 bam_print(_("file not found: %s\n"), path);
3397 BAM_DPRINTF(("%s: not a boot archive based Solaris "
3398 "instance: %s\n", fcn, root));
3399 return (0);
3402 BAM_DPRINTF(("%s: *IS* a boot archive based Solaris instance: %s\n",
3403 fcn, root));
3404 return (1);
3408 is_zfs(char *root)
3410 struct statvfs vfs;
3411 int ret;
3412 const char *fcn = "is_zfs()";
3414 ret = statvfs(root, &vfs);
3415 INJECT_ERROR1("STATVFS_ZFS", ret = 1);
3416 if (ret != 0) {
3417 bam_error(_("statvfs failed for %s: %s\n"), root,
3418 strerror(errno));
3419 return (0);
3422 if (strncmp(vfs.f_basetype, "zfs", strlen("zfs")) == 0) {
3423 BAM_DPRINTF(("%s: is a ZFS filesystem: %s\n", fcn, root));
3424 return (1);
3425 } else {
3426 BAM_DPRINTF(("%s: is *NOT* a ZFS filesystem: %s\n", fcn, root));
3427 return (0);
3431 static int
3432 is_readonly(char *root)
3434 int fd;
3435 int error;
3436 char testfile[PATH_MAX];
3437 const char *fcn = "is_readonly()";
3440 * Using statvfs() to check for a read-only filesystem is not
3441 * reliable. The only way to reliably test is to attempt to
3442 * create a file
3444 (void) snprintf(testfile, sizeof (testfile), "%s/%s.%d",
3445 root, BOOTADM_RDONLY_TEST, getpid());
3447 (void) unlink(testfile);
3449 errno = 0;
3450 fd = open(testfile, O_RDWR|O_CREAT|O_EXCL, 0644);
3451 error = errno;
3452 INJECT_ERROR2("RDONLY_TEST_ERROR", fd = -1, error = EACCES);
3453 if (fd == -1 && error == EROFS) {
3454 BAM_DPRINTF(("%s: is a READONLY filesystem: %s\n", fcn, root));
3455 return (1);
3456 } else if (fd == -1) {
3457 bam_error(_("error during read-only test on %s: %s\n"),
3458 root, strerror(error));
3461 (void) close(fd);
3462 (void) unlink(testfile);
3464 BAM_DPRINTF(("%s: is a RDWR filesystem: %s\n", fcn, root));
3465 return (0);
3468 static error_t
3469 update_archive(char *root, char *opt)
3471 error_t ret;
3473 assert(root);
3474 assert(opt == NULL);
3476 init_walk_args();
3477 (void) umask(022);
3480 * Never update non-BE root in update_all
3482 if (!is_be(root) && bam_update_all)
3483 return (BAM_SUCCESS);
3485 * root must belong to a boot archive based OS,
3487 if (!is_boot_archive(root)) {
3489 * Emit message only if not in context of update_all.
3490 * If in update_all, emit only if verbose flag is set.
3492 if (!bam_update_all || bam_verbose)
3493 bam_print(_("%s: not a boot archive based Solaris "
3494 "instance\n"), root);
3495 return (BAM_ERROR);
3499 * If smf check is requested when / is writable (can happen
3500 * on first reboot following an upgrade because service
3501 * dependency is messed up), skip the check.
3503 if (bam_smf_check && !bam_root_readonly && !is_zfs(root))
3504 return (BAM_SUCCESS);
3507 * Don't generate archive on ramdisk.
3509 if (is_ramdisk(root))
3510 return (BAM_SUCCESS);
3513 * root must be writable. This check applies to alternate
3514 * root (-R option); bam_root_readonly applies to '/' only.
3515 * The behaviour translates into being the one of a 'check'.
3517 if (!bam_smf_check && !bam_check && is_readonly(root)) {
3518 set_flag(RDONLY_FSCHK);
3519 bam_check = 1;
3523 * Now check if an update is really needed.
3525 ret = update_required(root);
3528 * The check command (-n) is *not* a dry run.
3529 * It only checks if the archive is in sync.
3530 * A readonly filesystem has to be considered an error only if an update
3531 * is required.
3533 if (bam_nowrite()) {
3534 if (is_flag_on(RDONLY_FSCHK)) {
3535 bam_check = bam_saved_check;
3536 if (ret > 0)
3537 bam_error(_("%s filesystem is read-only, "
3538 "skipping archives update\n"), root);
3539 if (bam_update_all)
3540 return ((ret != 0) ? BAM_ERROR : BAM_SUCCESS);
3543 bam_exit((ret != 0) ? 1 : 0);
3546 if (ret == 1) {
3547 /* create the ramdisk */
3548 ret = create_ramdisk(root);
3552 * if the archive is updated, save the new stat data and update the
3553 * timestamp file
3555 if (ret == 0 && walk_arg.new_nvlp != NULL) {
3556 savenew(root);
3557 update_timestamp(root);
3560 clear_walk_args();
3562 return (ret);
3565 static error_t
3566 update_all(char *root, char *opt)
3568 struct extmnttab mnt;
3569 struct stat sb;
3570 FILE *fp;
3571 char multibt[PATH_MAX];
3572 char creatram[PATH_MAX];
3573 error_t ret = BAM_SUCCESS;
3575 assert(root);
3576 assert(opt == NULL);
3578 if (bam_rootlen != 1 || *root != '/') {
3579 elide_trailing_slash(root, multibt, sizeof (multibt));
3580 bam_error(_("an alternate root (%s) cannot be used with this "
3581 "sub-command\n"), multibt);
3582 return (BAM_ERROR);
3586 * First update archive for current root
3588 if (update_archive(root, opt) != BAM_SUCCESS)
3589 ret = BAM_ERROR;
3591 if (ret == BAM_ERROR)
3592 goto out;
3595 * Now walk the mount table, performing archive update
3596 * for all mounted Newboot root filesystems
3598 fp = fopen(MNTTAB, "r");
3599 if (fp == NULL) {
3600 bam_error(_("failed to open file: %s: %s\n"),
3601 MNTTAB, strerror(errno));
3602 ret = BAM_ERROR;
3603 goto out;
3606 resetmnttab(fp);
3608 while (getextmntent(fp, &mnt, sizeof (mnt)) == 0) {
3609 if (mnt.mnt_special == NULL)
3610 continue;
3611 if ((strcmp(mnt.mnt_fstype, MNTTYPE_ZFS) != 0) &&
3612 (strncmp(mnt.mnt_special, "/dev/", strlen("/dev/")) != 0))
3613 continue;
3614 if (strcmp(mnt.mnt_mountp, "/") == 0)
3615 continue;
3617 (void) snprintf(creatram, sizeof (creatram), "%s/%s",
3618 mnt.mnt_mountp, CREATE_RAMDISK);
3620 if (stat(creatram, &sb) == -1)
3621 continue;
3624 * We put a trailing slash to be consistent with root = "/"
3625 * case, such that we don't have to print // in some cases.
3627 (void) snprintf(rootbuf, sizeof (rootbuf), "%s/",
3628 mnt.mnt_mountp);
3629 bam_rootlen = strlen(rootbuf);
3632 * It's possible that other mounts may be an alternate boot
3633 * architecture, so check it again.
3635 if ((get_boot_cap(rootbuf) != BAM_SUCCESS) ||
3636 (update_archive(rootbuf, opt) != BAM_SUCCESS))
3637 ret = BAM_ERROR;
3640 (void) fclose(fp);
3642 out:
3644 * We no longer use biosdev for Live Upgrade. Hence
3645 * there is no need to defer (to shutdown time) any fdisk
3646 * updates
3648 if (stat(GRUB_fdisk, &sb) == 0 || stat(GRUB_fdisk_target, &sb) == 0) {
3649 bam_error(_("Deferred FDISK update file(s) found: %s, %s. "
3650 "Not supported.\n"), GRUB_fdisk, GRUB_fdisk_target);
3653 return (ret);
3656 static char *
3657 get_mountpoint(char *special, char *fstype)
3659 FILE *mntfp;
3660 struct mnttab mp = {0};
3661 struct mnttab mpref = {0};
3662 int error;
3663 int ret;
3664 const char *fcn = "get_mountpoint()";
3666 BAM_DPRINTF(("%s: entered. args: %s %s\n", fcn, special, fstype));
3668 mntfp = fopen(MNTTAB, "r");
3669 error = errno;
3670 INJECT_ERROR1("MNTTAB_ERR_GET_MNTPT", mntfp = NULL);
3671 if (mntfp == NULL) {
3672 bam_error(_("failed to open file: %s: %s\n"),
3673 MNTTAB, strerror(error));
3674 return (NULL);
3677 mpref.mnt_special = special;
3678 mpref.mnt_fstype = fstype;
3680 ret = getmntany(mntfp, &mp, &mpref);
3681 INJECT_ERROR1("GET_MOUNTPOINT_MNTANY", ret = 1);
3682 if (ret != 0) {
3683 (void) fclose(mntfp);
3684 BAM_DPRINTF(("%s: no mount-point for special=%s and "
3685 "fstype=%s\n", fcn, special, fstype));
3686 return (NULL);
3688 (void) fclose(mntfp);
3690 assert(mp.mnt_mountp);
3692 BAM_DPRINTF(("%s: returning mount-point for special %s: %s\n",
3693 fcn, special, mp.mnt_mountp));
3695 return (s_strdup(mp.mnt_mountp));
3699 * Mounts a "legacy" top dataset (if needed)
3700 * Returns: The mountpoint of the legacy top dataset or NULL on error
3701 * mnted returns one of the above values defined for zfs_mnted_t
3703 static char *
3704 mount_legacy_dataset(char *pool, zfs_mnted_t *mnted)
3706 char cmd[PATH_MAX];
3707 char tmpmnt[PATH_MAX];
3708 filelist_t flist = {0};
3709 char *is_mounted;
3710 struct stat sb;
3711 int ret;
3712 const char *fcn = "mount_legacy_dataset()";
3714 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, pool));
3716 *mnted = ZFS_MNT_ERROR;
3718 (void) snprintf(cmd, sizeof (cmd),
3719 "/sbin/zfs get -Ho value mounted %s",
3720 pool);
3722 ret = exec_cmd(cmd, &flist);
3723 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_CMD", ret = 1);
3724 if (ret != 0) {
3725 bam_error(_("failed to determine mount status of ZFS "
3726 "pool %s\n"), pool);
3727 return (NULL);
3730 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_OUT", flist.head = NULL);
3731 if ((flist.head == NULL) || (flist.head != flist.tail)) {
3732 bam_error(_("ZFS pool %s has bad mount status\n"), pool);
3733 filelist_free(&flist);
3734 return (NULL);
3737 is_mounted = strtok(flist.head->line, " \t\n");
3738 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_YES", is_mounted = "yes");
3739 INJECT_ERROR1("Z_MOUNT_LEG_GET_MOUNTED_STRTOK_NO", is_mounted = "no");
3740 if (strcmp(is_mounted, "no") != 0) {
3741 filelist_free(&flist);
3742 *mnted = LEGACY_ALREADY;
3743 /* get_mountpoint returns a strdup'ed string */
3744 BAM_DPRINTF(("%s: legacy pool %s already mounted\n",
3745 fcn, pool));
3746 return (get_mountpoint(pool, "zfs"));
3749 filelist_free(&flist);
3752 * legacy top dataset is not mounted. Mount it now
3753 * First create a mountpoint.
3755 (void) snprintf(tmpmnt, sizeof (tmpmnt), "%s.%d",
3756 ZFS_LEGACY_MNTPT, getpid());
3758 ret = stat(tmpmnt, &sb);
3759 if (ret == -1) {
3760 BAM_DPRINTF(("%s: legacy pool %s mount-point %s absent\n",
3761 fcn, pool, tmpmnt));
3762 ret = mkdirp(tmpmnt, DIR_PERMS);
3763 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MNTPT_MKDIRP", ret = -1);
3764 if (ret == -1) {
3765 bam_error(_("mkdir of %s failed: %s\n"), tmpmnt,
3766 strerror(errno));
3767 return (NULL);
3769 } else {
3770 BAM_DPRINTF(("%s: legacy pool %s mount-point %s is already "
3771 "present\n", fcn, pool, tmpmnt));
3774 (void) snprintf(cmd, sizeof (cmd),
3775 "/sbin/mount -F zfs %s %s",
3776 pool, tmpmnt);
3778 ret = exec_cmd(cmd, NULL);
3779 INJECT_ERROR1("Z_MOUNT_TOP_LEG_MOUNT_CMD", ret = 1);
3780 if (ret != 0) {
3781 bam_error(_("mount of ZFS pool %s failed\n"), pool);
3782 (void) rmdir(tmpmnt);
3783 return (NULL);
3786 *mnted = LEGACY_MOUNTED;
3787 BAM_DPRINTF(("%s: legacy pool %s successfully mounted at %s\n",
3788 fcn, pool, tmpmnt));
3789 return (s_strdup(tmpmnt));
3793 * Mounts the top dataset (if needed)
3794 * Returns: The mountpoint of the top dataset or NULL on error
3795 * mnted returns one of the above values defined for zfs_mnted_t
3797 char *
3798 mount_top_dataset(char *pool, zfs_mnted_t *mnted)
3800 char cmd[PATH_MAX];
3801 filelist_t flist = {0};
3802 char *is_mounted;
3803 char *mntpt;
3804 char *zmntpt;
3805 int ret;
3806 const char *fcn = "mount_top_dataset()";
3808 *mnted = ZFS_MNT_ERROR;
3810 BAM_DPRINTF(("%s: entered. arg: %s\n", fcn, pool));
3813 * First check if the top dataset is a "legacy" dataset
3815 (void) snprintf(cmd, sizeof (cmd),
3816 "/sbin/zfs get -Ho value mountpoint %s",
3817 pool);
3818 ret = exec_cmd(cmd, &flist);
3819 INJECT_ERROR1("Z_MOUNT_TOP_GET_MNTPT", ret = 1);
3820 if (ret != 0) {
3821 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
3822 pool);
3823 return (NULL);
3826 if (flist.head && (flist.head == flist.tail)) {
3827 char *legacy = strtok(flist.head->line, " \t\n");
3828 if (legacy && strcmp(legacy, "legacy") == 0) {
3829 filelist_free(&flist);
3830 BAM_DPRINTF(("%s: is legacy, pool=%s\n", fcn, pool));
3831 return (mount_legacy_dataset(pool, mnted));
3835 filelist_free(&flist);
3837 BAM_DPRINTF(("%s: is *NOT* legacy, pool=%s\n", fcn, pool));
3839 (void) snprintf(cmd, sizeof (cmd),
3840 "/sbin/zfs get -Ho value mounted %s",
3841 pool);
3843 ret = exec_cmd(cmd, &flist);
3844 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED", ret = 1);
3845 if (ret != 0) {
3846 bam_error(_("failed to determine mount status of ZFS "
3847 "pool %s\n"), pool);
3848 return (NULL);
3851 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_VAL", flist.head = NULL);
3852 if ((flist.head == NULL) || (flist.head != flist.tail)) {
3853 bam_error(_("ZFS pool %s has bad mount status\n"), pool);
3854 filelist_free(&flist);
3855 return (NULL);
3858 is_mounted = strtok(flist.head->line, " \t\n");
3859 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_YES", is_mounted = "yes");
3860 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MOUNTED_NO", is_mounted = "no");
3861 if (strcmp(is_mounted, "no") != 0) {
3862 filelist_free(&flist);
3863 *mnted = ZFS_ALREADY;
3864 BAM_DPRINTF(("%s: non-legacy pool %s mounted already\n",
3865 fcn, pool));
3866 goto mounted;
3869 filelist_free(&flist);
3870 BAM_DPRINTF(("%s: non-legacy pool %s *NOT* already mounted\n",
3871 fcn, pool));
3873 /* top dataset is not mounted. Mount it now */
3874 (void) snprintf(cmd, sizeof (cmd),
3875 "/sbin/zfs mount %s", pool);
3876 ret = exec_cmd(cmd, NULL);
3877 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_MOUNT_CMD", ret = 1);
3878 if (ret != 0) {
3879 bam_error(_("mount of ZFS pool %s failed\n"), pool);
3880 return (NULL);
3882 *mnted = ZFS_MOUNTED;
3883 BAM_DPRINTF(("%s: non-legacy pool %s mounted now\n", fcn, pool));
3884 /*FALLTHRU*/
3885 mounted:
3887 * Now get the mountpoint
3889 (void) snprintf(cmd, sizeof (cmd),
3890 "/sbin/zfs get -Ho value mountpoint %s",
3891 pool);
3893 ret = exec_cmd(cmd, &flist);
3894 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_CMD", ret = 1);
3895 if (ret != 0) {
3896 bam_error(_("failed to determine mount point of ZFS pool %s\n"),
3897 pool);
3898 goto error;
3901 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_OUT", flist.head = NULL);
3902 if ((flist.head == NULL) || (flist.head != flist.tail)) {
3903 bam_error(_("ZFS pool %s has no mount-point\n"), pool);
3904 goto error;
3907 mntpt = strtok(flist.head->line, " \t\n");
3908 INJECT_ERROR1("Z_MOUNT_TOP_NONLEG_GET_MNTPT_STRTOK", mntpt = "foo");
3909 if (*mntpt != '/') {
3910 bam_error(_("ZFS pool %s has bad mount-point %s\n"),
3911 pool, mntpt);
3912 goto error;
3914 zmntpt = s_strdup(mntpt);
3916 filelist_free(&flist);
3918 BAM_DPRINTF(("%s: non-legacy pool %s is mounted at %s\n",
3919 fcn, pool, zmntpt));
3921 return (zmntpt);
3923 error:
3924 filelist_free(&flist);
3925 (void) umount_top_dataset(pool, *mnted, NULL);
3926 BAM_DPRINTF(("%s: returning FAILURE\n", fcn));
3927 return (NULL);
3931 umount_top_dataset(char *pool, zfs_mnted_t mnted, char *mntpt)
3933 char cmd[PATH_MAX];
3934 int ret;
3935 const char *fcn = "umount_top_dataset()";
3937 INJECT_ERROR1("Z_UMOUNT_TOP_INVALID_STATE", mnted = ZFS_MNT_ERROR);
3938 switch (mnted) {
3939 case LEGACY_ALREADY:
3940 case ZFS_ALREADY:
3941 /* nothing to do */
3942 BAM_DPRINTF(("%s: pool %s was already mounted at %s, Nothing "
3943 "to umount\n", fcn, pool, mntpt ? mntpt : "NULL"));
3944 free(mntpt);
3945 return (BAM_SUCCESS);
3946 case LEGACY_MOUNTED:
3947 (void) snprintf(cmd, sizeof (cmd),
3948 "/sbin/umount %s", pool);
3949 ret = exec_cmd(cmd, NULL);
3950 INJECT_ERROR1("Z_UMOUNT_TOP_LEGACY_UMOUNT_FAIL", ret = 1);
3951 if (ret != 0) {
3952 bam_error(_("umount of %s failed\n"), pool);
3953 free(mntpt);
3954 return (BAM_ERROR);
3956 if (mntpt)
3957 (void) rmdir(mntpt);
3958 free(mntpt);
3959 BAM_DPRINTF(("%s: legacy pool %s was mounted by us, "
3960 "successfully unmounted\n", fcn, pool));
3961 return (BAM_SUCCESS);
3962 case ZFS_MOUNTED:
3963 free(mntpt);
3964 (void) snprintf(cmd, sizeof (cmd),
3965 "/sbin/zfs unmount %s", pool);
3966 ret = exec_cmd(cmd, NULL);
3967 INJECT_ERROR1("Z_UMOUNT_TOP_NONLEG_UMOUNT_FAIL", ret = 1);
3968 if (ret != 0) {
3969 bam_error(_("umount of %s failed\n"), pool);
3970 return (BAM_ERROR);
3972 BAM_DPRINTF(("%s: nonleg pool %s was mounted by us, "
3973 "successfully unmounted\n", fcn, pool));
3974 return (BAM_SUCCESS);
3975 default:
3976 bam_error(_("Internal error: bad saved mount state for "
3977 "pool %s\n"), pool);
3978 return (BAM_ERROR);
3980 /*NOTREACHED*/
3983 char *
3984 get_special(char *mountp)
3986 FILE *mntfp;
3987 struct mnttab mp = {0};
3988 struct mnttab mpref = {0};
3989 int error;
3990 int ret;
3991 const char *fcn = "get_special()";
3993 INJECT_ERROR1("GET_SPECIAL_MNTPT", mountp = NULL);
3994 if (mountp == NULL) {
3995 bam_error(_("cannot get special file: NULL mount-point\n"));
3996 return (NULL);
3999 mntfp = fopen(MNTTAB, "r");
4000 error = errno;
4001 INJECT_ERROR1("GET_SPECIAL_MNTTAB_OPEN", mntfp = NULL);
4002 if (mntfp == NULL) {
4003 bam_error(_("failed to open file: %s: %s\n"), MNTTAB,
4004 strerror(error));
4005 return (NULL);
4008 if (*mountp == '\0')
4009 mpref.mnt_mountp = "/";
4010 else
4011 mpref.mnt_mountp = mountp;
4013 ret = getmntany(mntfp, &mp, &mpref);
4014 INJECT_ERROR1("GET_SPECIAL_MNTTAB_SEARCH", ret = 1);
4015 if (ret != 0) {
4016 (void) fclose(mntfp);
4017 BAM_DPRINTF(("%s: Cannot get special file: mount-point %s "
4018 "not in mnttab\n", fcn, mountp));
4019 return (NULL);
4021 (void) fclose(mntfp);
4023 BAM_DPRINTF(("%s: returning special: %s\n", fcn, mp.mnt_special));
4025 return (s_strdup(mp.mnt_special));
4028 static void
4029 line_free(line_t *lp)
4031 if (lp == NULL)
4032 return;
4034 free(lp->cmd);
4035 free(lp->sep);
4036 free(lp->arg);
4037 free(lp->line);
4038 free(lp);
4041 static void
4042 linelist_free(line_t *start)
4044 line_t *lp;
4046 while (start) {
4047 lp = start;
4048 start = start->next;
4049 line_free(lp);
4053 static void
4054 filelist_free(filelist_t *flistp)
4056 linelist_free(flistp->head);
4057 flistp->head = NULL;
4058 flistp->tail = NULL;
4062 * Utility routines
4067 * Returns 0 on success
4068 * Any other value indicates an error
4070 static int
4071 exec_cmd(char *cmdline, filelist_t *flistp)
4073 char buf[BUFSIZ];
4074 int ret;
4075 FILE *ptr;
4076 sigset_t set;
4077 void (*disp)(int);
4080 * For security
4081 * - only absolute paths are allowed
4082 * - set IFS to space and tab
4084 if (*cmdline != '/') {
4085 bam_error(_("path is not absolute: %s\n"), cmdline);
4086 return (-1);
4088 (void) putenv("IFS= \t");
4091 * We may have been exec'ed with SIGCHLD blocked
4092 * unblock it here
4094 (void) sigemptyset(&set);
4095 (void) sigaddset(&set, SIGCHLD);
4096 if (sigprocmask(SIG_UNBLOCK, &set, NULL) != 0) {
4097 bam_error(_("cannot unblock SIGCHLD: %s\n"), strerror(errno));
4098 return (-1);
4102 * Set SIGCHLD disposition to SIG_DFL for popen/pclose
4104 disp = sigset(SIGCHLD, SIG_DFL);
4105 if (disp == SIG_ERR) {
4106 bam_error(_("cannot set SIGCHLD disposition: %s\n"),
4107 strerror(errno));
4108 return (-1);
4110 if (disp == SIG_HOLD) {
4111 bam_error(_("SIGCHLD signal blocked. Cannot exec: %s\n"),
4112 cmdline);
4113 return (-1);
4116 ptr = popen(cmdline, "r");
4117 if (ptr == NULL) {
4118 bam_error(_("popen failed: %s: %s\n"), cmdline,
4119 strerror(errno));
4120 return (-1);
4124 * If we simply do a pclose() following a popen(), pclose()
4125 * will close the reader end of the pipe immediately even
4126 * if the child process has not started/exited. pclose()
4127 * does wait for cmd to terminate before returning though.
4128 * When the executed command writes its output to the pipe
4129 * there is no reader process and the command dies with
4130 * SIGPIPE. To avoid this we read repeatedly until read
4131 * terminates with EOF. This indicates that the command
4132 * (writer) has closed the pipe and we can safely do a
4133 * pclose().
4135 * Since pclose() does wait for the command to exit,
4136 * we can safely reap the exit status of the command
4137 * from the value returned by pclose()
4139 while (s_fgets(buf, sizeof (buf), ptr) != NULL) {
4140 if (flistp == NULL) {
4141 /* s_fgets strips newlines, so insert them at the end */
4142 bam_print(_("%s\n"), buf);
4143 } else {
4144 append_to_flist(flistp, buf);
4148 ret = pclose(ptr);
4149 if (ret == -1) {
4150 bam_error(_("pclose failed: %s: %s\n"), cmdline,
4151 strerror(errno));
4152 return (-1);
4155 if (WIFEXITED(ret)) {
4156 return (WEXITSTATUS(ret));
4157 } else {
4158 bam_error(_("command terminated abnormally: %s: %d\n"),
4159 cmdline, ret);
4160 return (-1);
4165 * Since this function returns -1 on error
4166 * it cannot be used to convert -1. However,
4167 * that is sufficient for what we need.
4169 static long
4170 s_strtol(char *str)
4172 long l;
4173 char *res = NULL;
4175 if (str == NULL) {
4176 return (-1);
4179 errno = 0;
4180 l = strtol(str, &res, 10);
4181 if (errno || *res != '\0') {
4182 return (-1);
4185 return (l);
4189 * Wrapper around fgets, that strips newlines returned by fgets
4191 char *
4192 s_fgets(char *buf, int buflen, FILE *fp)
4194 int n;
4196 buf = fgets(buf, buflen, fp);
4197 if (buf) {
4198 n = strlen(buf);
4199 if (n == buflen - 1 && buf[n-1] != '\n')
4200 bam_error(_("the following line is too long "
4201 "(> %d chars)\n\t%s\n"), buflen - 1, buf);
4202 buf[n-1] = (buf[n-1] == '\n') ? '\0' : buf[n-1];
4205 return (buf);
4208 void *
4209 s_calloc(size_t nelem, size_t sz)
4211 void *ptr;
4213 ptr = calloc(nelem, sz);
4214 if (ptr == NULL) {
4215 bam_error(_("could not allocate memory: size = %u\n"),
4216 nelem*sz);
4217 bam_exit(1);
4219 return (ptr);
4222 void *
4223 s_realloc(void *ptr, size_t sz)
4225 ptr = realloc(ptr, sz);
4226 if (ptr == NULL) {
4227 bam_error(_("could not allocate memory: size = %u\n"), sz);
4228 bam_exit(1);
4230 return (ptr);
4233 char *
4234 s_strdup(char *str)
4236 char *ptr;
4238 if (str == NULL)
4239 return (NULL);
4241 ptr = strdup(str);
4242 if (ptr == NULL) {
4243 bam_error(_("could not allocate memory: size = %u\n"),
4244 strlen(str) + 1);
4245 bam_exit(1);
4247 return (ptr);
4251 * Returns 1 if amd64 (or sparc, for syncing x86 diskless clients)
4252 * Returns 0 otherwise
4254 static int
4255 is_amd64(void)
4257 static int amd64 = -1;
4258 char isabuf[257]; /* from sysinfo(2) manpage */
4260 if (amd64 != -1)
4261 return (amd64);
4263 if (bam_alt_platform) {
4264 if (strcmp(bam_platform, "i86pc") == 0) {
4265 amd64 = 1; /* diskless server */
4267 } else {
4268 if (sysinfo(SI_ISALIST, isabuf, sizeof (isabuf)) > 0 &&
4269 strncmp(isabuf, "amd64 ", strlen("amd64 ")) == 0) {
4270 amd64 = 1;
4271 } else if (strstr(isabuf, "i386") == NULL) {
4272 amd64 = 1; /* diskless server */
4275 if (amd64 == -1)
4276 amd64 = 0;
4278 return (amd64);
4281 static char *
4282 get_machine(void)
4284 static int cached = -1;
4285 static char mbuf[257]; /* from sysinfo(2) manpage */
4287 if (cached == 0)
4288 return (mbuf);
4290 if (bam_alt_platform) {
4291 return (bam_platform);
4292 } else {
4293 if (sysinfo(SI_MACHINE, mbuf, sizeof (mbuf)) > 0) {
4294 cached = 1;
4297 if (cached == -1) {
4298 mbuf[0] = '\0';
4299 cached = 0;
4302 return (mbuf);
4306 is_sparc(void)
4308 static int issparc = -1;
4309 char mbuf[257]; /* from sysinfo(2) manpage */
4311 if (issparc != -1)
4312 return (issparc);
4314 if (bam_alt_platform) {
4315 if (strncmp(bam_platform, "sun4", 4) == 0) {
4316 issparc = 1;
4318 } else {
4319 if (sysinfo(SI_ARCHITECTURE, mbuf, sizeof (mbuf)) > 0 &&
4320 strcmp(mbuf, "sparc") == 0) {
4321 issparc = 1;
4324 if (issparc == -1)
4325 issparc = 0;
4327 return (issparc);
4330 static void
4331 append_to_flist(filelist_t *flistp, char *s)
4333 line_t *lp;
4335 lp = s_calloc(1, sizeof (line_t));
4336 lp->line = s_strdup(s);
4337 if (flistp->head == NULL)
4338 flistp->head = lp;
4339 else
4340 flistp->tail->next = lp;
4341 flistp->tail = lp;
4344 #if !defined(_OBP)
4346 UCODE_VENDORS;
4348 /*ARGSUSED*/
4349 static void
4350 ucode_install(char *root)
4352 int i;
4354 for (i = 0; ucode_vendors[i].filestr != NULL; i++) {
4355 int cmd_len = PATH_MAX + 256;
4356 char cmd[PATH_MAX + 256];
4357 char file[PATH_MAX];
4358 char timestamp[PATH_MAX];
4359 struct stat fstatus, tstatus;
4360 struct utimbuf u_times;
4362 (void) snprintf(file, PATH_MAX, "%s/%s/%s-ucode.%s",
4363 bam_root, UCODE_INSTALL_PATH, ucode_vendors[i].filestr,
4364 ucode_vendors[i].extstr);
4366 if (stat(file, &fstatus) != 0 || !(S_ISREG(fstatus.st_mode)))
4367 continue;
4369 (void) snprintf(timestamp, PATH_MAX, "%s.ts", file);
4371 if (stat(timestamp, &tstatus) == 0 &&
4372 fstatus.st_mtime <= tstatus.st_mtime)
4373 continue;
4375 (void) snprintf(cmd, cmd_len, "/usr/sbin/ucodeadm -i -R "
4376 "%s/%s/%s %s > /dev/null 2>&1", bam_root,
4377 UCODE_INSTALL_PATH, ucode_vendors[i].vendorstr, file);
4378 if (system(cmd) != 0)
4379 return;
4381 if (creat(timestamp, S_IRUSR | S_IWUSR) == -1)
4382 return;
4384 u_times.actime = fstatus.st_atime;
4385 u_times.modtime = fstatus.st_mtime;
4386 (void) utime(timestamp, &u_times);
4389 #endif