/*
* Copyright 1997 Piermont Information Systems Inc.
* All rights reserved.
*
* Written by Philip A. Nelson for Piermont Information Systems Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of Piermont Information Systems Inc. may not be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* disks.c -- routines to deal with finding disks and labeling disks. */
/* things we could have as /sbin/newfs_* and /sbin/fsck_* */
static const char *extern_fs_with_chk[] = {
"ext2fs", "lfs", "msdos", "udf", "v7fs"
};
/* things we could have as /sbin/newfs_* but not /sbin/fsck_* */
static const char *extern_fs_newfs_only[] = {
"sysvbfs"
};
/* Local prototypes */
static int found_fs(struct data *, size_t, const struct lookfor*);
static int found_fs_nocheck(struct data *, size_t, const struct lookfor*);
static int fsck_preen(const char *, const char *, bool silent);
static void fixsb(const char *, const char *);
static bool tmpfs_on_var_shm(void);
const char *
getfslabelname(uint f, uint f_version)
{
if (f == FS_TMPFS)
return "tmpfs";
else if (f == FS_MFS)
return "mfs";
else if (f == FS_EFI_SP)
return msg_string(MSG_fs_type_efi_sp);
else if (f == FS_BSDFFS) {
switch (f_version) {
default:
case 1: return msg_string(MSG_fs_type_ffs);
case 2: return msg_string(MSG_fs_type_ffsv2);
case 3: return msg_string(MSG_fs_type_ffsv2ea);
}
} else if (f == FS_EX2FS && f_version == 1)
return msg_string(MSG_fs_type_ext2old);
else if (f >= __arraycount(fstypenames) || fstypenames[f] == NULL)
return "invalid";
return fstypenames[f];
}
/*
* Decide whether we want to mount a tmpfs on /var/shm: we do this always
* when the machine has more than 16 MB of user memory. On smaller machines,
* shm_open() and friends will not perform well anyway.
*/
static bool
tmpfs_on_var_shm(void)
{
uint64_t ram;
size_t len;
len = sizeof(ram);
if (sysctlbyname("hw.usermem64", &ram, &len, NULL, 0))
return false;
return ram > 16 * MEG;
}
/*
* Find length of string but ignore trailing whitespace
*/
static int
trimmed_len(const char *s)
{
size_t len = strlen(s);
/* from src/sbin/atactl/atactl.c
* extract_string: copy a block of bytes out of ataparams and make
* a proper string out of it, truncating trailing spaces and preserving
* strict typing. And also, not doing unaligned accesses.
*/
static void
ata_extract_string(char *buf, size_t bufmax,
uint8_t *bytes, unsigned numbytes,
int needswap)
{
unsigned i;
size_t j;
unsigned char ch1, ch2;
for (i = 0, j = 0; i < numbytes; i += 2) {
ch1 = bytes[i];
ch2 = bytes[i+1];
if (needswap && j < bufmax-1) {
buf[j++] = ch2;
}
if (j < bufmax-1) {
buf[j++] = ch1;
}
if (!needswap && j < bufmax-1) {
buf[j++] = ch2;
}
}
while (j > 0 && buf[j-1] == ' ') {
j--;
}
buf[j] = '\0';
}
/* Trim leading and trailing blanks and NULs. */
while (slen > 0 && STRVIS_ISWHITE(src[0]))
++src, --slen;
while (slen > 0 && STRVIS_ISWHITE(src[slen - 1]))
--slen;
if (!disk_ioctl(dd->dd_name, ATAIOCCOMMAND, &req)
|| req.retsts != ATACMD_OK)
return 0;
#if BYTE_ORDER == LITTLE_ENDIAN
/*
* On little endian machines, we need to shuffle the string
* byte order. However, we don't have to do this for NEC or
* Mitsumi ATAPI devices
*/
/* try drvctl first, fallback to direct probing */
if (get_descr_drvctl(dd))
return;
/* try ATA */
if (get_descr_ata(dd))
return;
/* try SCSI */
if (get_descr_scsi(dd))
return;
/* XXX: get description from raid, cgd, vnd... */
/* punt, just give some generic info */
humanize_number(size, sizeof(size),
(uint64_t)dd->dd_secsize * (uint64_t)dd->dd_totsec,
"", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
/*
* State for helper callback for get_default_cdrom
*/
struct default_cdrom_data {
char *device;
size_t max_len;
bool found;
};
/*
* Helper function for get_default_cdrom, gets passed a device
* name and a void pointer to default_cdrom_data.
*/
static bool
get_default_cdrom_helper(void *state, const char *dev)
{
struct default_cdrom_data *data = state;
if (!is_cdrom_device(dev, false))
return true;
strlcpy(data->device, dev, data->max_len);
strlcat(data->device, "a", data->max_len); /* default to partition a */
data->found = true;
return false; /* one is enough, stop iteration */
}
/*
* Set the argument to the name of the first CD devices actually
* available, leave it unmodified otherwise.
* Return true if a device has been found.
*/
bool
get_default_cdrom(char *cd, size_t max_len)
{
struct default_cdrom_data state;
if (state->dd->dd_no_part && !state->with_non_partitionable)
return true;
if (!get_disk_geom(state->dd->dd_name, &geo)) {
if (errno == ENOENT)
return true;
if (errno != ENOTTY || !state->dd->dd_no_part)
/*
* Allow plain partitions,
* like already existing wedges
* (like dk0) if marked as
* non-partitioning device.
* For all other cases, continue
* with the next disk.
*/
return true;
if (!is_ffs_wedge(state->dd->dd_name))
return true;
}
/*
* Exclude a disk mounted as root partition,
* in case of install-image on a USB memstick.
*/
if (is_active_rootpart(state->dd->dd_name,
state->dd->dd_no_part ? -1 : 0))
return true;
if (!state->dd->dd_no_part || !get_wedge_descr(state->dd))
get_descr(state->dd);
state->dd++;
state->numdisks++;
if (state->numdisks == MAX_DISKS)
return false;
return true;
}
/*
* Get all disk devices that are not CDs.
* Optionally leave out those that can not be partitioned further.
*/
static int
get_disks(struct disk_desc *dd, bool with_non_partitionable)
{
struct get_disks_state state;
if (new_scheme == NULL) {
if (err_msg)
*err_msg = INTERNAL_ERROR;
return false;
}
new_parts = new_scheme->create_new_for_disk(p->diskdev,
0, p->dlsize, is_boot_drive, NULL);
if (new_parts == NULL) {
if (err_msg)
*err_msg = MSG_out_of_memory;
return false;
}
if (!convert_copy(old_parts, new_parts)) {
/* need to cleanup */
if (err_msg)
*err_msg = MSG_cvtscheme_error;
new_parts->pscheme->free(new_parts);
return false;
}
/* need a redraw here, kernel messages hose everything */
touchwin(stdscr);
refresh();
/* Kill typeahead, it won't be what the user had in mind */
fpurge(stdin);
/*
* we need space for the menu box and the row label,
* this sums up to 7 characters.
*/
max_desc_len = getmaxx(stdscr) - 8;
if (max_desc_len >= __arraycount(disks[0].dd_descr))
max_desc_len = __arraycount(disks[0].dd_descr) - 1;
/*
* partman_go: <0 - we want to see menu with extended partitioning
* ==0 - we want to see simple select disk menu
* >0 - we do not want to see any menus, just detect
* all disks
*/
if (partman_go <= 0) {
if (numdisks == 0 && !allow_cur_system) {
/* No disks found! */
hit_enter_to_continue(MSG_nodisk, NULL);
/*endwin();*/
return -1;
} else {
/* One or more disks found or current system allowed */
dno = wno = 0;
if (allow_cur_system) {
dsk_menu[dno].opt_name = MSG_running_system;
dsk_menu[dno].opt_flags = OPT_EXIT;
dsk_menu[dno].opt_action = set_menu_select;
disk_no[dno] = -1;
i++; dno++;
}
for (i = 0; i < numdisks; i++) {
if (disks[i].dd_no_part) {
any_wedges = true;
wedge_menu[wno].opt_name =
disks[i].dd_descr;
disks[i].dd_descr[max_desc_len] = 0;
wedge_menu[wno].opt_flags = OPT_EXIT;
wedge_menu[wno].opt_action =
set_menu_select;
wedge_no[wno] = i;
wno++;
} else {
dsk_menu[dno].opt_name =
disks[i].dd_descr;
disks[i].dd_descr[max_desc_len] = 0;
dsk_menu[dno].opt_flags = OPT_EXIT;
dsk_menu[dno].opt_action =
set_menu_select;
disk_no[dno] = i;
dno++;
}
}
if (any_wedges) {
dsk_menu[dno].opt_name = MSG_selectwedge;
dsk_menu[dno].opt_flags = OPT_EXIT;
dsk_menu[dno].opt_action = set_menu_select;
disk_no[dno] = -2;
dno++;
}
if (partman_go < 0) {
dsk_menu[dno].opt_name = MSG_partman;
dsk_menu[dno].opt_flags = OPT_EXIT;
dsk_menu[dno].opt_action = set_menu_select;
disk_no[dno] = -3;
dno++;
}
w_menu_no = -1;
menu_no = new_menu(MSG_Available_disks,
dsk_menu, dno, -1,
7, 0, 0, MC_SCROLL,
NULL, NULL, NULL, NULL, MSG_exit_menu_generic);
if (menu_no == -1)
return -1;
for (;;) {
msg_fmt_display(MSG_ask_disk, "%s", doingwhat);
i = -1;
process_menu(menu_no, &i);
if (i == -1)
return -1;
if (disk_no[i] == -2) {
/* do wedges menu */
if (w_menu_no == -1) {
w_menu_no = new_menu(
MSG_Available_wedges,
wedge_menu, wno, -1,
4, 0, 0, MC_SCROLL,
NULL, NULL, NULL, NULL,
MSG_exit_menu_generic);
if (w_menu_no == -1) {
selected_disk = -1;
break;
}
}
i = -1;
process_menu(w_menu_no, &i);
if (i == -1)
continue;
selected_disk = wedge_no[i];
break;
}
selected_disk = disk_no[i];
break;
}
if (w_menu_no >= 0)
free_menu(w_menu_no);
free_menu(menu_no);
if (allow_cur_system && selected_disk == -1) {
pm = dummy_whole_system_pm();
return 1;
}
}
if (partman_go < 0 && selected_disk == -3) {
partman_go = 1;
return -2;
} else
partman_go = 0;
if (selected_disk < 0 || selected_disk < 0
|| selected_disk >= numdisks)
return -1;
}
/* Fill pm struct with device(s) info */
for (i = 0; i < numdisks; i++) {
if (! partman_go)
disk = disks + selected_disk;
else {
disk = disks + i;
already_found = 0;
SLIST_FOREACH(pm_i, &pm_head, l) {
pm_last = pm_i;
if (strcmp(pm_i->diskdev, disk->dd_name) == 0) {
already_found = 1;
break;
}
}
if (pm_i != NULL && already_found) {
/*
* We already added this device, but
* partitions might have changed
*/
if (!pm_i->found) {
pm_i->found = true;
if (pm_i->parts == NULL) {
pm_i->parts =
partitions_read_disk(
pm_i->diskdev,
disk->dd_totsec,
disk->dd_secsize,
disk->dd_no_mbr);
}
}
continue;
}
}
pm = pm_new;
pm->found = 1;
pm->ptstart = 0;
pm->ptsize = 0;
strlcpy(pm->diskdev, disk->dd_name, sizeof pm->diskdev);
strlcpy(pm->diskdev_descr, disk->dd_descr, sizeof pm->diskdev_descr);
/* Use as a default disk if the user has the sets on a local disk */
strlcpy(localfs_dev, disk->dd_name, sizeof localfs_dev);
if (partman_go) {
pm_getrefdev(pm_new);
if (SLIST_EMPTY(&pm_head) || pm_last == NULL)
SLIST_INSERT_HEAD(&pm_head, pm_new, l);
else
SLIST_INSERT_AFTER(pm_last, pm_new, l);
pm_new = malloc(sizeof (struct pm_devs));
memset(pm_new, 0, sizeof *pm_new);
} else
/* We are not in partman and do not want to process
* all devices, exit */
break;
}
return numdisks-skipped;
}
static int
sort_part_usage_by_mount(const void *a, const void *b)
{
const struct part_usage_info *pa = a, *pb = b;
/* sort all real partitions by mount point */
if ((pa->instflags & PUIINST_MOUNT) &&
(pb->instflags & PUIINST_MOUNT))
return strcmp(pa->mount, pb->mount);
/* real partitions go first */
if (pa->instflags & PUIINST_MOUNT)
return -1;
if (pb->instflags & PUIINST_MOUNT)
return 1;
/* arbitrary order for all other partitions */
if (pa->type == PT_swap)
return -1;
if (pb->type == PT_swap)
return 1;
if (pa->type < pb->type)
return -1;
if (pa->type > pb->type)
return 1;
if (pa->cur_part_id < pb->cur_part_id)
return -1;
if (pa->cur_part_id > pb->cur_part_id)
return 1;
return (uintptr_t)a < (uintptr_t)b ? -1 : 1;
}
/*
* Are we able to newfs this type of file system?
* Keep in sync with switch labels below!
*/
bool
can_newfs_fstype(unsigned int t)
{
switch (t) {
case FS_APPLEUFS:
case FS_BSDFFS:
case FS_BSDLFS:
case FS_MSDOS:
case FS_EFI_SP:
case FS_SYSVBFS:
case FS_V7:
case FS_EX2FS:
return true;
}
return false;
}
/* sort to ensure /usr/local is mounted after /usr (etc) */
qsort(install->infos, install->num, sizeof(*install->infos),
sort_part_usage_by_mount);
for (i = 0; i < install->num; i++) {
/*
* Newfs all file systems marked as needing this.
* Mount the ones that have a mountpoint in the target.
*/
ptn = &install->infos[i];
parts = ptn->parts;
newfs = NULL;
fsname = NULL;
if (ptn->size == 0 || parts == NULL|| ptn->type == PT_swap)
continue;
/* Create the fstab. */
make_target_dir("/etc");
f = target_fopen("/etc/fstab", "w");
scripting_fprintf(NULL, "cat <<EOF >%s/etc/fstab\n", target_prefix());
if (logfp)
(void)fprintf(logfp,
"Making %s/etc/fstab (%s).\n", target_prefix(),
pm->diskdev);
if (f == NULL) {
msg_display(MSG_createfstab);
if (logfp)
(void)fprintf(logfp, "Failed to make /etc/fstab!\n");
hit_enter_to_continue(NULL, NULL);
#ifndef DEBUG
return 1;
#else
f = stdout;
#endif
}
scripting_fprintf(f, "# NetBSD /etc/fstab\n# See /usr/share/examples/"
"fstab/ for more examples.\n");
if (pm->no_part) {
/* single dk? target */
char buf[200], parent[200], swap[200], *prompt;
int res;
if (!find_part_by_name(list[0].u.s_val,
&parts, &pno) || parts == NULL || pno == NO_PART)
return 0;
parts->pscheme->get_part_device(parts, pno,
dev, sizeof(dev), NULL, plain_name, true, true);
parts->pscheme->get_part_device(parts, pno,
rdev, sizeof(rdev), NULL, raw_dev_name, true, true);
} else {
/* this fstab entry uses the plain device name */
if (is_root) {
/*
* PR 54480: we can not use the current device name
* as it might be different from the real environment.
* This is an abuse of the functionality, but it used
* to work before (and still does work if only a single
* target disk is involved).
* Use the device name from the current "pm" instead.
*/
strcpy(rdev, "/dev/r");
strlcat(rdev, pm->diskdev, sizeof(rdev));
strcpy(dev, "/dev/");
strlcat(dev, pm->diskdev, sizeof(dev));
/* copy over the partition letter, if any */
len = strlen(list[0].u.s_val);
if (list[0].u.s_val[len-1] >= 'a' &&
list[0].u.s_val[len-1] <=
('a' + getmaxpartitions())) {
strlcat(rdev, &list[0].u.s_val[len-1],
sizeof(rdev));
strlcat(dev, &list[0].u.s_val[len-1],
sizeof(dev));
}
} else {
strcpy(rdev, "/dev/r");
strlcat(rdev, list[0].u.s_val, sizeof(rdev));
strcpy(dev, "/dev/");
strlcat(dev, list[0].u.s_val, sizeof(dev));
}
}
if (with_fsck) {
/* need the raw device for fsck_preen */
error = fsck_preen(rdev, fsname, false);
if (error != 0)
return error;
}
/* add mount option for fs type */
strcpy(options, "-t ");
strlcat(options, fsname, sizeof(options));
/* extract mount options from fstab */
strlcpy(tmp, list[2].u.s_val, sizeof(tmp));
for (first = true, op = strtok_r(tmp, ",", &last); op != NULL;
op = strtok_r(NULL, ",", &last)) {
if (strcmp(op, FSTAB_RW) == 0 ||
strcmp(op, FSTAB_RQ) == 0 ||
strcmp(op, FSTAB_RO) == 0 ||
strcmp(op, FSTAB_SW) == 0 ||
strcmp(op, FSTAB_DP) == 0 ||
strcmp(op, FSTAB_XX) == 0)
continue;
if (first) {
first = false;
strlcat(options, " -o ", sizeof(options));
} else {
strlcat(options, ",", sizeof(options));
}
strlcat(options, op, sizeof(options));
}
static int
/*ARGSUSED*/
found_fs(struct data *list, size_t num, const struct lookfor *item)
{
return process_found_fs(list, num, item, true);
}
static int
/*ARGSUSED*/
found_fs_nocheck(struct data *list, size_t num, const struct lookfor *item)
{
return process_found_fs(list, num, item, false);
}
/*
* Do an fsck. On failure, inform the user by showing a warning
* message and doing menu_ok() before proceeding.
* The device passed should be the full qualified path to raw disk
* (e.g. /dev/rwd0a).
* Returns 0 on success, or nonzero return code from fsck() on failure.
*/
static int
fsck_preen(const char *disk, const char *fsname, bool silent)
{
char *prog, err[12];
int error;
if (fsname == NULL)
return 0;
/* first, check if fsck program exists, if not, assume ok */
asprintf(&prog, "/sbin/fsck_%s", fsname);
if (prog == NULL)
return 0;
if (access(prog, X_OK) != 0) {
free(prog);
return 0;
}
if (!strcmp(fsname,"ffs"))
fixsb(prog, disk);
error = run_program(silent? RUN_SILENT|RUN_ERROR_OK : 0, "%s -p -q %s", prog, disk);
free(prog);
if (error != 0 && !silent) {
sprintf(err, "%d", error);
msg_display_subst(msg_string(MSG_badfs), 3,
disk, fsname, err);
if (ask_noyes(NULL))
error = 0;
/* XXX at this point maybe we should run a full fsck? */
}
return error;
}
/* This performs the same function as the etc/rc.d/fixsb script
* which attempts to correct problems with ffs1 filesystems
* which may have been introduced by booting a netbsd-current kernel
* from between April of 2003 and January 2004. For more information
* This script was developed as a response to NetBSD pr install/25138
* Additional prs regarding the original issue include:
* bin/17910 kern/21283 kern/21404 port-macppc/23925 port-macppc/23926
*/
static void
fixsb(const char *prog, const char *disk)
{
int fd;
int rval;
union {
struct fs fs;
char buf[SBLOCKSIZE];
} sblk;
struct fs *fs = &sblk.fs;
fd = open(disk, O_RDONLY);
if (fd == -1)
return;
/* Read ffsv1 main superblock */
rval = pread(fd, sblk.buf, sizeof sblk.buf, SBLOCK_UFS1);
close(fd);
if (rval != sizeof sblk.buf)
return;
if (fs->fs_magic != FS_UFS1_MAGIC &&
fs->fs_magic != FS_UFS1_MAGIC_SWAPPED)
/* Not FFSv1 */
return;
if (fs->fs_old_flags & FS_FLAGS_UPDATED)
/* properly updated fslevel 4 */
return;
if (fs->fs_bsize != fs->fs_maxbsize)
/* not messed up */
return;
/*
* OK we have a munged fs, first 'upgrade' to fslevel 4,
* We specify -b16 in order to stop fsck bleating that the
* sb doesn't match the first alternate.
*/
run_program(RUN_DISPLAY | RUN_PROGRESS,
"%s -p -b 16 -c 4 %s", prog, disk);
/* Then downgrade to fslevel 3 */
run_program(RUN_DISPLAY | RUN_PROGRESS,
"%s -p -c 3 %s", prog, disk);
}
/*
* fsck and mount the root partition.
* devdev is the fully qualified block device name.
*/
static int
mount_root(const char *devdev, bool first, bool writeable,
struct install_partition_desc *install)
{
int error;
/* Mount devdev on target's "".
* If we pass "" as mount-on, Prefixing will DTRT.
* for now, use no options.
* XXX consider -o remount in case target root is
* current root, still readonly from single-user?
*/
return target_mount(writeable? "" : "-r", devdev, "");
}
/* Get information on the file systems mounted from the root filesystem.
* Offer to convert them into 4.4BSD inodes if they are not 4.4BSD
* inodes. Fsck them. Mount them.
*/
int
mount_disks(struct install_partition_desc *install)
{
char *fstab;
int fstabsize;
int error;
char devdev[PATH_MAX];
size_t i, num_fs_types, num_entries;
struct lookfor *fstabbuf, *l;
if (install->cur_system)
return 0;
/*
* Check what file system tools are available and create parsers
* for the corresponding fstab(5) entries - all others will be
* ignored.
*/
num_fs_types = 1; /* ffs is implicit */
for (i = 0; i < __arraycount(extern_fs_with_chk); i++) {
sprintf(devdev, "/sbin/newfs_%s", extern_fs_with_chk[i]);
if (file_exists_p(devdev))
num_fs_types++;
}
for (i = 0; i < __arraycount(extern_fs_newfs_only); i++) {
sprintf(devdev, "/sbin/newfs_%s", extern_fs_newfs_only[i]);
if (file_exists_p(devdev))
num_fs_types++;
}
num_entries = 2 * num_fs_types + 1; /* +1 for "ufs" special case */
fstabbuf = calloc(num_entries, sizeof(*fstabbuf));
if (fstabbuf == NULL)
return -1;
l = fstabbuf;
l->head = "/dev/";
l->fmt = strdup("/dev/%s %s ffs %s");
l->todo = "c";
l->var = __UNCONST("ffs");
l->func = found_fs;
l++;
l->head = "/dev/";
l->fmt = strdup("/dev/%s %s ufs %s");
l->todo = "c";
l->var = __UNCONST("ffs");
l->func = found_fs;
l++;
l->head = NAME_PREFIX;
l->fmt = strdup(NAME_PREFIX "%s %s ffs %s");
l->todo = "c";
l->var = __UNCONST("ffs");
l->func = found_fs;
l++;
for (i = 0; i < __arraycount(extern_fs_with_chk); i++) {
sprintf(devdev, "/sbin/newfs_%s", extern_fs_with_chk[i]);
if (!file_exists_p(devdev))
continue;
sprintf(devdev, "/dev/%%s %%s %s %%s", extern_fs_with_chk[i]);
l->head = "/dev/";
l->fmt = strdup(devdev);
l->todo = "c";
l->var = __UNCONST(extern_fs_with_chk[i]);
l->func = found_fs;
l++;
sprintf(devdev, NAME_PREFIX "%%s %%s %s %%s",
extern_fs_with_chk[i]);
l->head = NAME_PREFIX;
l->fmt = strdup(devdev);
l->todo = "c";
l->var = __UNCONST(extern_fs_with_chk[i]);
l->func = found_fs;
l++;
}
for (i = 0; i < __arraycount(extern_fs_newfs_only); i++) {
sprintf(devdev, "/sbin/newfs_%s", extern_fs_newfs_only[i]);
if (!file_exists_p(devdev))
continue;
sprintf(devdev, "/dev/%%s %%s %s %%s", extern_fs_newfs_only[i]);
l->head = "/dev/";
l->fmt = strdup(devdev);
l->todo = "c";
l->var = __UNCONST(extern_fs_newfs_only[i]);
l->func = found_fs_nocheck;
l++;
sprintf(devdev, NAME_PREFIX "%%s %%s %s %%s",
extern_fs_newfs_only[i]);
l->head = NAME_PREFIX;
l->fmt = strdup(devdev);
l->todo = "c";
l->var = __UNCONST(extern_fs_newfs_only[i]);
l->func = found_fs_nocheck;
l++;
}
assert((size_t)(l - fstabbuf) == num_entries);
/* First the root device. */
if (target_already_root()) {
/* avoid needing to call target_already_root() again */
targetroot_mnt[0] = 0;
} else if (pm->no_part) {
snprintf(devdev, sizeof devdev, _PATH_DEV "%s", pm->diskdev);
error = mount_root(devdev, true, false, install);
if (error != 0 && error != EBUSY)
return -1;
} else {
for (i = 0; i < install->num; i++) {
if (is_root_part_mount(install->infos[i].mount))
break;
}
if (i >= install->num) {
hit_enter_to_continue(MSG_noroot, NULL);
return -1;
}
/* Check the target /etc/fstab exists before trying to parse it. */
if (target_dir_exists_p("/etc") == 0 ||
target_file_exists_p("/etc/fstab") == 0) {
msg_fmt_display(MSG_noetcfstab, "%s", pm->diskdev);
hit_enter_to_continue(NULL, NULL);
return -1;
}
/* Get fstab entries from the target-root /etc/fstab. */
fstabsize = target_collect_file(T_FILE, &fstab, "/etc/fstab");
if (fstabsize < 0) {
/* error ! */
msg_fmt_display(MSG_badetcfstab, "%s", pm->diskdev);
hit_enter_to_continue(NULL, NULL);
umount_root();
return -2;
}
/*
* We unmount the read-only root again, so we can mount it
* with proper options from /etc/fstab
*/
umount_root();
/*
* Now do all entries in /etc/fstab and mount them if required
*/
error = walk(fstab, (size_t)fstabsize, fstabbuf, num_entries);
free(fstab);
for (i = 0; i < num_entries; i++)
free(__UNCONST(fstabbuf[i].fmt));
free(fstabbuf);
l = strlen(disk);
while (--nswap >= 0) {
/* Should we check the se_dev or se_path? */
cp = swap[nswap].se_path;
if (memcmp(cp, "/dev/", 5) != 0)
continue;
if (memcmp(cp + 5, disk, l) != 0)
continue;
if (!isalpha(*(unsigned char *)(cp + 5 + l)))
continue;
if (cp[5 + l + 1] != 0)
continue;
/* ok path looks like it is for this device */
if (!remove_swap) {
/* count active swap areas */
rval++;
continue;
}
if (swapctl(SWAP_OFF, cp, 0) == -1)
rval = -1;
}
/* find a partition to be mounted as / */
for (i = 0; i < install->num; i++) {
if ((install->infos[i].instflags & PUIINST_MOUNT)
&& strcmp(install->infos[i].mount, "/") == 0) {
fstype = install->infos[i].fs_type;
break;
}
}
if (fstype < 0) {
/* not found? take first root type partition instead */
for (i = 0; i < install->num; i++) {
if (install->infos[i].type == PT_root) {
fstype = install->infos[i].fs_type;
break;
}
}
}
/* check we have boot code for the root partition type */
switch (fstype) {
#if defined(BOOTXX_FFSV1) || defined(BOOTXX_FFSV2)
case FS_BSDFFS:
if (install->infos[i].fs_version >= 2) {
#ifdef BOOTXX_FFSV2
bootxxname = BOOTXX_FFSV2;
#else
bootxxname = NULL;
#endif
} else {
#ifdef BOOTXX_FFSV1
bootxxname = BOOTXX_FFSV1;
#else
bootxxname = NULL;
#endif
}
break;
#endif
#ifdef BOOTXX_LFSV2
case FS_BSDLFS:
bootxxname = BOOTXX_LFSV2;
break;
#endif
default:
bootxxname = NULL;
break;
}
l = strlen(line);
if (l >= (m->w))
strcpy(line + (m->w-3), "...");
wprintw(m->mw, "%s", line);
}
/*
* is the given "test" partitions set used in the selected set?
*/
static bool
selection_has_parts(struct selected_partitions *sel,
const struct disk_partitions *test)
{
size_t i;
for (i = 0; i < sel->num_sel; i++) {
if (sel->selection[i].parts == test)
return true;
}
return false;
}
/*
* is the given "test" partition in the selected set?
*/
static bool
selection_has_partition(struct selected_partitions *sel,
const struct disk_partitions *test, part_id test_id)
{
size_t i;
for (i = 0; i < sel->num_sel; i++) {
if (sel->selection[i].parts == test &&
sel->selection[i].id == test_id)
return true;
}
return false;
}
/*
* let the user select a partition, optionally skipping all partitions
* on the "ignore" device
*/
static bool
add_select_partition(struct selected_partitions *res,
struct disk_partitions **all_parts, size_t all_cnt)
{
struct disk_partitions *ps;
struct disk_part_info info;
part_id id;
struct single_partition *partitions, *pp;
struct menu_ent *part_menu_opts, *menup;
size_t n, part_cnt;
int sel_menu;
/*
* count how many items our menu will have
*/
part_cnt = 0;
for (n = 0; n < all_cnt; n++) {
ps = all_parts[n];
for (id = 0; id < ps->num_part; id++) {
if (selection_has_partition(res, ps, id))
continue;
if (!ps->pscheme->get_part_info(ps, id, &info))
continue;
if (info.flags & (PTI_SEC_CONTAINER|PTI_WHOLE_DISK|
PTI_PSCHEME_INTERNAL|PTI_RAW_PART))
continue;
part_cnt++;
}
}
/*
* create a menu from this and let the user
* select one partition
*/
part_menu_opts = NULL;
partitions = calloc(part_cnt, sizeof *partitions);
if (partitions == NULL)
goto done;
part_menu_opts = calloc(part_cnt, sizeof *part_menu_opts);
if (part_menu_opts == NULL)
goto done;
pp = partitions;
menup = part_menu_opts;
for (n = 0; n < all_cnt; n++) {
ps = all_parts[n];
for (id = 0; id < ps->num_part; id++) {
if (selection_has_partition(res, ps, id))
continue;
if (!ps->pscheme->get_part_info(ps, id, &info))
continue;
if (info.flags & (PTI_SEC_CONTAINER|PTI_WHOLE_DISK|
PTI_PSCHEME_INTERNAL|PTI_RAW_PART))
continue;
pp->parts = ps;
pp->id = id;
pp++;
menup->opt_action = select_single_part;
menup++;
}
}
sel_menu = new_menu(MSG_select_foreign_part, part_menu_opts, part_cnt,
3, 3, 0, 60,
MC_SUBMENU | MC_SCROLL | MC_NOCLEAR,
NULL, display_single_part, NULL,
NULL, MSG_exit_menu_generic);
if (sel_menu != -1) {
struct selected_partition *newsels;
struct sel_menu_data data;
/*
* collect all available partition sets
*/
data.all_cnt = 0;
if (SLIST_EMPTY(&pm_head)) {
cnt = get_disks(disks, false);
if (cnt <= 0)
return false;
/*
* allocate two slots for each disk (primary/secondary)
*/
data.all_parts = calloc(2*cnt, sizeof *data.all_parts);
if (data.all_parts == NULL)
return false;
for (n = 0; n < cnt; n++) {
if (ignore != NULL &&
strcmp(disks[n].dd_name, ignore->disk) == 0)
continue;
for (i = 0; i < selected->num_sel; i++) {
parts = selected->selection[i].parts;
/* remove from list before testing for other instances */
selected->selection[i].parts = NULL;
/* if this is the secondary partition set, the parent owns it */
if (parts->parent != NULL)
continue;
/* only free once (we use the last one) */
if (selection_has_parts(selected, parts))
continue;
parts->pscheme->free(parts);
}
free(selected->selection);
}
for (i = 0; i < selected->num_sel; i++) {
if (!selected->selection[i].parts->pscheme->get_part_info(
selected->selection[i].parts,
selected->selection[i].id, &info))
continue;
s += info.size;
}