1/*	$OpenBSD: softraid.c,v 1.9 2022/11/14 13:39:37 kn Exp $	*/
2/*
3 * Copyright (c) 2012 Joel Sing <jsing@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/types.h>
19#include <sys/disklabel.h>
20#include <sys/dkio.h>
21#include <sys/ioctl.h>
22
23#include <dev/biovar.h>
24
25#include <err.h>
26#include <fcntl.h>
27#include <stdio.h>
28#include <string.h>
29#include <unistd.h>
30#include <util.h>
31
32#include "installboot.h"
33
34static int sr_volume(int, char *, int *, int *);
35
36static void
37sr_prepare_chunk(int devfd, int vol, int disk)
38{
39	struct bioc_disk bd;
40	char *realdev;
41	char part;
42	int diskfd;
43
44	diskfd = sr_open_chunk(devfd, vol, disk, &bd, &realdev, &part);
45	if (diskfd == -1)
46		return;
47
48	/* Prepare file system on device. */
49	md_prepareboot(diskfd, realdev);
50
51	close(diskfd);
52}
53
54void
55sr_prepareboot(int devfd, char *dev)
56{
57	int vol = -1, ndisks = 0, disk;
58
59	/* Use the normal process if this is not a softraid volume. */
60	if (!sr_volume(devfd, dev, &vol, &ndisks)) {
61		md_prepareboot(devfd, dev);
62		return;
63	}
64
65	/* Prepare file system on each disk that is part of this volume. */
66	for (disk = 0; disk < ndisks; disk++)
67		sr_prepare_chunk(devfd, vol, disk);
68}
69
70void
71sr_installboot(int devfd, char *dev)
72{
73	int	vol = -1, ndisks = 0, disk;
74
75	/* Use the normal process if this is not a softraid volume. */
76	if (!sr_volume(devfd, dev, &vol, &ndisks)) {
77		md_installboot(devfd, dev);
78		return;
79	}
80
81	/* Install boot loader in softraid volume. */
82	sr_install_bootldr(devfd, dev);
83
84	/* Install boot block on each disk that is part of this volume. */
85	for (disk = 0; disk < ndisks; disk++)
86		sr_install_bootblk(devfd, vol, disk);
87}
88
89int
90sr_volume(int devfd, char *dev, int *vol, int *disks)
91{
92	struct	bioc_inq bi;
93	struct	bioc_vol bv;
94	int	i;
95
96	/*
97	 * Determine if the given device is a softraid volume.
98	 */
99
100	/* Get volume information. */
101	memset(&bi, 0, sizeof(bi));
102	if (ioctl(devfd, BIOCINQ, &bi) == -1)
103		return 0;
104
105	/* XXX - softraid volumes will always have a "softraid0" controller. */
106	if (strncmp(bi.bi_dev, "softraid0", sizeof("softraid0")))
107		return 0;
108
109	/*
110	 * XXX - this only works with the real disk name (e.g. sd0) - this
111	 * should be extracted from the device name, or better yet, fixed in
112	 * the softraid ioctl.
113	 */
114	/* Locate specific softraid volume. */
115	for (i = 0; i < bi.bi_novol; i++) {
116		memset(&bv, 0, sizeof(bv));
117		bv.bv_volid = i;
118		if (ioctl(devfd, BIOCVOL, &bv) == -1)
119			err(1, "BIOCVOL");
120
121		if (strncmp(dev, bv.bv_dev, sizeof(bv.bv_dev)) == 0) {
122			*vol = i;
123			*disks = bv.bv_nodisk;
124			break;
125		}
126	}
127
128	if (verbose)
129		fprintf(stderr, "%s: softraid volume with %i disk(s)\n",
130		    dev, *disks);
131
132	return 1;
133}
134
135void
136sr_status(struct bio_status *bs)
137{
138	int	i;
139
140	for (i = 0; i < bs->bs_msg_count; i++)
141		warnx("%s", bs->bs_msgs[i].bm_msg);
142
143	if (bs->bs_status == BIO_STATUS_ERROR) {
144		if (bs->bs_msg_count == 0)
145			errx(1, "unknown error");
146		else
147			exit(1);
148	}
149}
150
151int
152sr_open_chunk(int devfd, int vol, int disk, struct bioc_disk *bd,
153    char **realdev, char *part)
154{
155	int diskfd;
156
157	/* Get device name for this disk/chunk. */
158	memset(bd, 0, sizeof(*bd));
159	bd->bd_volid = vol;
160	bd->bd_diskid = disk;
161	if (ioctl(devfd, BIOCDISK, bd) == -1)
162		err(1, "BIOCDISK");
163
164	/* Check disk status. */
165	if (bd->bd_status != BIOC_SDONLINE &&
166	    bd->bd_status != BIOC_SDREBUILD) {
167		fprintf(stderr, "softraid chunk %u not online - skipping...\n",
168		    disk);
169		return -1;
170	}
171
172	/* Keydisks always have a size of zero. */
173	if (bd->bd_size == 0)
174		return -1;
175
176	if (strlen(bd->bd_vendor) < 1)
177		errx(1, "invalid disk name");
178	*part = bd->bd_vendor[strlen(bd->bd_vendor) - 1];
179	if (*part < 'a' || *part >= 'a' + MAXPARTITIONS)
180		errx(1, "invalid partition %c\n", *part);
181	bd->bd_vendor[strlen(bd->bd_vendor) - 1] = '\0';
182
183	/* Open device. */
184	if ((diskfd = opendev(bd->bd_vendor, (nowrite ? O_RDONLY : O_RDWR),
185	    OPENDEV_PART, realdev)) == -1)
186		err(1, "open: %s", *realdev);
187
188	return diskfd;
189}
190