xref: /openbsd/usr.sbin/installboot/softraid.c (revision d415bd75)
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 
34 static int sr_volume(int, char *, int *, int *);
35 
36 static void
37 sr_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 
54 void
55 sr_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 
70 void
71 sr_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 
89 int
90 sr_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 
135 void
136 sr_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 
151 int
152 sr_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