Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1108 | dev | 1 | /*- |
1117 | dev | 2 | * Copyright 2005, Anatoli Klassen <anatoli@aksoft.net> |
3 | * All rights reserved. |
||
4 | * |
||
5 | * The code is based on code of geom_bsd module by Poul-Henning Kamp. |
||
6 | * |
||
1108 | dev | 7 | * Copyright (c) 2002 Poul-Henning Kamp |
8 | * Copyright (c) 2002 Networks Associates Technology, Inc. |
||
9 | * All rights reserved. |
||
10 | * |
||
11 | * This software was developed for the FreeBSD Project by Poul-Henning Kamp |
||
12 | * and NAI Labs, the Security Research Division of Network Associates, Inc. |
||
13 | * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the |
||
14 | * DARPA CHATS research program. |
||
15 | * |
||
16 | * Redistribution and use in source and binary forms, with or without |
||
17 | * modification, are permitted provided that the following conditions |
||
18 | * are met: |
||
19 | * 1. Redistributions of source code must retain the above copyright |
||
20 | * notice, this list of conditions and the following disclaimer. |
||
21 | * 2. Redistributions in binary form must reproduce the above copyright |
||
22 | * notice, this list of conditions and the following disclaimer in the |
||
23 | * documentation and/or other materials provided with the distribution. |
||
24 | * 3. The names of the authors may not be used to endorse or promote |
||
25 | * products derived from this software without specific prior written |
||
26 | * permission. |
||
27 | * |
||
28 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
||
29 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
30 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
31 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
||
32 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||
33 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||
34 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||
35 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||
36 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||
37 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||
38 | * SUCH DAMAGE. |
||
39 | */ |
||
40 | |||
41 | /* |
||
42 | * This is the method for dealing with BSD disklabels. It has been |
||
43 | * extensively (by my standards at least) commented, in the vain hope that |
||
44 | * it will serve as the source in future copy&paste operations. |
||
45 | */ |
||
46 | |||
47 | #include <sys/param.h> |
||
48 | #include <sys/systm.h> |
||
49 | #include <sys/kernel.h> |
||
50 | #include <sys/bio.h> |
||
51 | #include <sys/malloc.h> |
||
52 | #include <sys/lock.h> |
||
53 | #include <sys/md5.h> |
||
54 | #include <sys/errno.h> |
||
55 | #include <geom/geom.h> |
||
56 | #include <geom/geom_slice.h> |
||
57 | |||
58 | #include "disklabel.h" |
||
59 | |||
60 | #define NBSD_CLASS_NAME "NetBSD" |
||
61 | |||
62 | #define LABELSIZE (148 + 16 * NBSD_MAXPARTITIONS) |
||
63 | |||
64 | /* |
||
65 | * Our private data about one instance. All the rest is handled by the |
||
66 | * slice code and stored in its softc, so this is just the stuff |
||
67 | * specific to BSD disklabels. |
||
68 | */ |
||
69 | struct g_nbsd_softc { |
||
70 | int mbrtype; |
||
71 | off_t labeloffset; |
||
72 | off_t mbroffset; |
||
73 | off_t rawoffset; |
||
74 | struct disklabel ondisk; |
||
75 | u_char label[LABELSIZE]; |
||
76 | u_char labelsum[16]; |
||
77 | }; |
||
78 | |||
79 | /* |
||
80 | * Modify our slicer to match proposed disklabel, if possible. |
||
81 | * This is where we make sure we don't do something stupid. |
||
82 | */ |
||
83 | static int |
||
84 | g_nbsd_modify(struct g_geom *gp, struct g_nbsd_softc *ms) |
||
85 | { |
||
1117 | dev | 86 | char useable[NBSD_MAXPARTITIONS]; |
87 | MD5_CTX md5sum; |
||
1108 | dev | 88 | struct partition *ppp; |
89 | struct g_slicer *gsp; |
||
90 | struct g_consumer *cp; |
||
1117 | dev | 91 | struct disklabel *dl; |
92 | off_t rawoffset, fullsize, o; |
||
93 | int i, error; |
||
1108 | dev | 94 | u_int secsize, u; |
95 | |||
96 | g_topology_assert(); |
||
97 | gsp = gp->softc; |
||
1117 | dev | 98 | dl = &ms->ondisk; |
1108 | dev | 99 | |
100 | /* Get dimensions of our device. */ |
||
101 | cp = LIST_FIRST(&gp->consumer); |
||
102 | secsize = cp->provider->sectorsize; |
||
103 | |||
104 | /* ... or a smaller sector size. */ |
||
1117 | dev | 105 | if (dl->d_secsize < secsize) { |
1108 | dev | 106 | return (EINVAL); |
107 | } |
||
108 | |||
109 | /* ... or a non-multiple sector size. */ |
||
1117 | dev | 110 | if (dl->d_secsize % secsize != 0) { |
1108 | dev | 111 | return (EINVAL); |
112 | } |
||
113 | |||
114 | rawoffset = ms->mbroffset; |
||
115 | fullsize = cp->provider->mediasize; |
||
116 | |||
1117 | dev | 117 | for (i = 0; i < dl->d_npartitions; i++) { |
118 | ppp = &dl->d_partitions[i]; |
||
1108 | dev | 119 | /* skip partitions with no type or outside of slide */ |
120 | if (ppp->p_size == 0 || ppp->p_fstype == 0 || |
||
1117 | dev | 121 | (off_t)ppp->p_offset * dl->d_secsize < rawoffset || |
122 | (off_t)(ppp->p_offset + ppp->p_size) * dl->d_secsize |
||
1108 | dev | 123 | > (rawoffset + fullsize)) |
124 | useable[i] = 0; |
||
125 | else |
||
126 | useable[i] = 1; |
||
127 | } |
||
1117 | dev | 128 | |
1108 | dev | 129 | /* Don't munge open partitions. */ |
1117 | dev | 130 | for (i = 0; i < dl->d_npartitions; i++) { |
1108 | dev | 131 | if(!useable[i]) continue; |
132 | |||
1117 | dev | 133 | ppp = &dl->d_partitions[i]; |
134 | o = (off_t)ppp->p_offset * dl->d_secsize; |
||
1108 | dev | 135 | if (o == 0) |
136 | o = rawoffset; |
||
137 | error = g_slice_config(gp, i, G_SLICE_CONFIG_CHECK, |
||
1117 | dev | 138 | o - rawoffset, (off_t)ppp->p_size * dl->d_secsize, dl->d_secsize, |
1108 | dev | 139 | "%s%c", gp->name, 'a' + i); |
140 | if (error) |
||
141 | return (error); |
||
142 | } |
||
143 | |||
144 | /* Look good, go for it... */ |
||
145 | for (u = 0; u < gsp->nslice; u++) { |
||
146 | if(!useable[u]) continue; |
||
147 | |||
1117 | dev | 148 | ppp = &dl->d_partitions[u]; |
149 | o = (off_t)ppp->p_offset * dl->d_secsize; |
||
1108 | dev | 150 | if (o == 0) |
151 | o = rawoffset; |
||
152 | g_slice_config(gp, u, G_SLICE_CONFIG_SET, |
||
153 | o - rawoffset, |
||
1117 | dev | 154 | (off_t)ppp->p_size * dl->d_secsize, |
155 | dl->d_secsize, |
||
1108 | dev | 156 | "%s%c", gp->name, 'a' + u); |
157 | } |
||
158 | |||
159 | /* Update our softc */ |
||
160 | ms->rawoffset = rawoffset; |
||
161 | |||
162 | /* |
||
163 | * In order to avoid recursively attaching to the same |
||
164 | * on-disk label (it's usually visible through the 'c' |
||
165 | * partition) we calculate an MD5 and ask if other BSD's |
||
166 | * below us love that label. If they do, we don't. |
||
167 | */ |
||
168 | MD5Init(&md5sum); |
||
169 | MD5Update(&md5sum, ms->label, sizeof(ms->label)); |
||
170 | MD5Final(ms->labelsum, &md5sum); |
||
171 | |||
172 | return (0); |
||
173 | } |
||
174 | |||
175 | /* |
||
176 | * This is an internal helper function, called multiple times from the taste |
||
177 | * function to try to locate a disklabel on the disk. More civilized formats |
||
178 | * will not need this, as there is only one possible place on disk to look |
||
179 | * for the magic spot. |
||
180 | */ |
||
181 | |||
182 | static int |
||
1117 | dev | 183 | g_nbsd_try(struct g_geom *gp, struct g_slicer *gsp, struct g_consumer *cp, |
184 | int secsize, struct g_nbsd_softc *ms, off_t offset) |
||
1108 | dev | 185 | { |
186 | int error; |
||
187 | u_char *buf; |
||
188 | struct disklabel *dl; |
||
189 | off_t secoff; |
||
190 | |||
191 | /* |
||
192 | * We need to read entire aligned sectors, and we assume that the |
||
193 | * disklabel does not span sectors, so one sector is enough. |
||
194 | */ |
||
195 | error = 0; |
||
196 | secoff = offset % secsize; |
||
197 | buf = g_read_data(cp, offset - secoff, secsize, &error); |
||
198 | if (buf == NULL || error != 0) |
||
199 | return (ENOENT); |
||
200 | |||
201 | /* Decode into our native format. */ |
||
202 | dl = &ms->ondisk; |
||
203 | error = nbsd_disklabel_le_dec(buf + secoff, dl, NBSD_MAXPARTITIONS); |
||
204 | |||
205 | /* do not recognize slices which will be recognized by geom_bsd module */ |
||
206 | if (!error) |
||
207 | if (ms->mbrtype == MBR_FREEBSD && |
||
208 | dl->d_npartitions <= FREEBSD_MAXPARTITIONS) |
||
209 | error = EINVAL; |
||
210 | |||
211 | if (!error) |
||
212 | bcopy(buf + secoff, ms->label, LABELSIZE); |
||
213 | |||
214 | /* Remember to free the buffer g_read_data() gave us. */ |
||
215 | g_free(buf); |
||
216 | |||
217 | ms->labeloffset = offset; |
||
218 | return (error); |
||
219 | } |
||
220 | |||
221 | /*- |
||
222 | * This start routine is only called for non-trivial requests, all the |
||
223 | * trivial ones are handled autonomously by the slice code. |
||
224 | * For requests we handle here, we must call the g_io_deliver() on the |
||
225 | * bio, and return non-zero to indicate to the slice code that we did so. |
||
226 | * This code executes in the "DOWN" I/O path, this means: |
||
227 | * * No sleeping. |
||
228 | * * Don't grab the topology lock. |
||
229 | * * Don't call biowait, g_getattr(), g_setattr() or g_read_data() |
||
230 | */ |
||
231 | static int |
||
232 | g_nbsd_ioctl(struct g_provider *pp, u_long cmd, void *data, int fflag, struct thread *td) |
||
233 | { |
||
1117 | dev | 234 | return (ENOIOCTL); |
1108 | dev | 235 | } |
236 | |||
237 | static int |
||
238 | g_nbsd_start(struct bio *bp) |
||
239 | { |
||
240 | struct g_geom *gp; |
||
241 | struct g_nbsd_softc *ms; |
||
242 | struct g_slicer *gsp; |
||
243 | |||
244 | gp = bp->bio_to->geom; |
||
245 | gsp = gp->softc; |
||
246 | ms = gsp->softc; |
||
247 | if (bp->bio_cmd == BIO_GETATTR) { |
||
248 | if (g_handleattr(bp, "NBSD::labelsum", ms->labelsum, |
||
249 | sizeof(ms->labelsum))) |
||
250 | return (1); |
||
251 | } |
||
252 | return (0); |
||
253 | } |
||
254 | |||
255 | /* |
||
256 | * Dump configuration information in XML format. |
||
257 | * Notice that the function is called once for the geom and once for each |
||
258 | * consumer and provider. We let g_slice_dumpconf() do most of the work. |
||
259 | */ |
||
260 | static void |
||
1117 | dev | 261 | g_nbsd_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, |
262 | struct g_consumer *cp, struct g_provider *pp) |
||
1108 | dev | 263 | { |
264 | struct g_nbsd_softc *ms; |
||
265 | struct g_slicer *gsp; |
||
266 | |||
267 | gsp = gp->softc; |
||
268 | ms = gsp->softc; |
||
269 | g_slice_dumpconf(sb, indent, gp, cp, pp); |
||
270 | if (indent != NULL && pp == NULL && cp == NULL) { |
||
271 | sbuf_printf(sb, "%s<labeloffset>%jd</labeloffset>\n", |
||
272 | indent, (intmax_t)ms->labeloffset); |
||
273 | sbuf_printf(sb, "%s<rawoffset>%jd</rawoffset>\n", |
||
274 | indent, (intmax_t)ms->rawoffset); |
||
275 | sbuf_printf(sb, "%s<mbroffset>%jd</mbroffset>\n", |
||
276 | indent, (intmax_t)ms->mbroffset); |
||
277 | } else if (pp != NULL) { |
||
278 | if (indent == NULL) |
||
279 | sbuf_printf(sb, " ty %d", |
||
280 | ms->ondisk.d_partitions[pp->index].p_fstype); |
||
281 | else |
||
282 | sbuf_printf(sb, "%s<type>%d</type>\n", indent, |
||
283 | ms->ondisk.d_partitions[pp->index].p_fstype); |
||
284 | } |
||
285 | } |
||
286 | |||
287 | /* |
||
288 | * The taste function is called from the event-handler, with the topology |
||
289 | * lock already held and a provider to examine. The flags are unused. |
||
290 | * |
||
291 | * If flags == G_TF_NORMAL, the idea is to take a bite of the provider and |
||
292 | * if we find valid, consistent magic on it, build a geom on it. |
||
293 | * any magic bits which indicate that we should automatically put a BSD |
||
294 | * geom on it. |
||
295 | * |
||
296 | * There may be cases where the operator would like to put a BSD-geom on |
||
297 | * providers which do not meet all of the requirements. This can be done |
||
298 | * by instead passing the G_TF_INSIST flag, which will override these |
||
299 | * checks. |
||
300 | * |
||
301 | * The final flags value is G_TF_TRANSPARENT, which instructs the method |
||
302 | * to put a geom on top of the provider and configure it to be as transparent |
||
303 | * as possible. This is not really relevant to the BSD method and therefore |
||
304 | * not implemented here. |
||
305 | */ |
||
306 | |||
307 | static struct g_geom * |
||
308 | g_nbsd_taste(struct g_class *mp, struct g_provider *pp, int flags) |
||
309 | { |
||
1117 | dev | 310 | MD5_CTX md5sum; |
311 | u_char hash[16]; |
||
1108 | dev | 312 | struct g_geom *gp; |
313 | struct g_consumer *cp; |
||
1117 | dev | 314 | struct g_nbsd_softc *ms; |
315 | struct g_slicer *gsp; |
||
1108 | dev | 316 | int error; |
317 | u_int secsize; |
||
318 | |||
319 | g_trace(G_T_TOPOLOGY, "nbsd_taste(%s,%s)", mp->name, pp->name); |
||
320 | g_topology_assert(); |
||
321 | |||
322 | /* We don't implement transparent inserts. */ |
||
323 | if (flags == G_TF_TRANSPARENT) |
||
324 | return (NULL); |
||
325 | |||
326 | /* |
||
327 | * BSD labels are a subclass of the general "slicing" topology so |
||
328 | * a lot of the work can be done by the common "slice" code. |
||
329 | * Create a geom with space for NBSD_MAXPARTITIONS providers, one consumer |
||
330 | * and a softc structure for us. Specify the provider to attach |
||
331 | * the consumer to and our "start" routine for special requests. |
||
332 | * The provider is opened with mode (1,0,0) so we can do reads |
||
333 | * from it. |
||
334 | */ |
||
335 | gp = g_slice_new(mp, NBSD_MAXPARTITIONS, pp, &cp, &ms, |
||
336 | sizeof(*ms), g_nbsd_start); |
||
337 | if (gp == NULL) |
||
338 | return (NULL); |
||
339 | |||
340 | /* Get the geom_slicer softc from the geom. */ |
||
341 | gsp = gp->softc; |
||
342 | |||
343 | /* |
||
344 | * The do...while loop here allows us to have multiple escapes |
||
345 | * using a simple "break". This improves code clarity without |
||
346 | * ending up in deep nesting and without using goto or come from. |
||
347 | */ |
||
348 | do { |
||
349 | /* |
||
350 | * If the provider is an MBR we will only auto attach |
||
351 | * to type 165|166|169 slices in the G_TF_NORMAL case. We will |
||
352 | * attach to any other type. |
||
353 | */ |
||
354 | error = g_getattr("MBR::type", cp, &ms->mbrtype); |
||
355 | if (!error) { |
||
356 | if (ms->mbrtype != MBR_OPENBSD && ms->mbrtype != MBR_NETBSD && |
||
357 | ms->mbrtype != MBR_FREEBSD && flags == G_TF_NORMAL) |
||
358 | break; |
||
359 | error = g_getattr("MBR::offset", cp, &ms->mbroffset); |
||
360 | if (error) |
||
361 | break; |
||
362 | } |
||
363 | |||
364 | /* Same thing if we are inside a PC98 */ |
||
365 | /* XXX not implemented */ |
||
366 | |||
367 | /* Get sector size, we need it to read data. */ |
||
368 | secsize = cp->provider->sectorsize; |
||
369 | if (secsize < 512) |
||
370 | break; |
||
371 | |||
372 | /* First look for a label at the start of the second sector. */ |
||
373 | error = g_nbsd_try(gp, gsp, cp, secsize, ms, secsize); |
||
374 | |||
375 | /* If we didn't find a label, punt. */ |
||
376 | if (error) |
||
377 | break; |
||
378 | |||
379 | /* |
||
380 | * In order to avoid recursively attaching to the same |
||
381 | * on-disk label (it's usually visible through the 'c' |
||
382 | * partition) we calculate an MD5 and ask if other BSD's |
||
383 | * below us love that label. If they do, we don't. |
||
384 | */ |
||
385 | MD5Init(&md5sum); |
||
386 | MD5Update(&md5sum, ms->label, sizeof(ms->label)); |
||
387 | MD5Final(ms->labelsum, &md5sum); |
||
388 | |||
389 | error = g_getattr("NBSD::labelsum", cp, &hash); |
||
390 | if (!error && !bcmp(ms->labelsum, hash, sizeof(hash))) |
||
391 | break; |
||
392 | |||
393 | /* |
||
394 | * Process the found disklabel, and modify our "slice" |
||
395 | * instance to match it, if possible. |
||
396 | */ |
||
397 | error = g_nbsd_modify(gp, ms); |
||
398 | } while (0); |
||
399 | |||
400 | /* Success or failure, we can close our provider now. */ |
||
401 | g_access(cp, -1, 0, 0); |
||
402 | |||
403 | /* If we have configured any providers, return the new geom. */ |
||
404 | if (gsp->nprovider > 0) { |
||
405 | g_slice_conf_hot(gp, 0, ms->labeloffset, LABELSIZE, |
||
406 | G_SLICE_HOT_ALLOW, G_SLICE_HOT_DENY, G_SLICE_HOT_CALL); |
||
407 | return (gp); |
||
408 | } |
||
409 | /* |
||
410 | * ...else push the "self-destruct" button, by spoiling our own |
||
411 | * consumer. This triggers a call to g_slice_spoiled which will |
||
412 | * dismantle what was setup. |
||
413 | */ |
||
414 | g_slice_spoiled(cp); |
||
415 | return (NULL); |
||
416 | } |
||
417 | |||
418 | /* |
||
419 | * NB! curthread is user process which GCTL'ed. |
||
420 | */ |
||
421 | static void |
||
422 | g_nbsd_config(struct gctl_req *req, struct g_class *mp, char const *verb) |
||
423 | { |
||
424 | struct g_geom *gp; |
||
425 | struct g_slicer *gsp; |
||
426 | struct g_consumer *cp; |
||
427 | struct g_nbsd_softc *ms; |
||
428 | |||
429 | g_topology_assert(); |
||
430 | gp = gctl_get_geom(req, mp, "geom"); |
||
431 | if (gp == NULL) |
||
432 | return; |
||
433 | cp = LIST_FIRST(&gp->consumer); |
||
434 | gsp = gp->softc; |
||
435 | ms = gsp->softc; |
||
436 | if (!strcmp(verb, "read mbroffset")) { |
||
437 | gctl_set_param(req, "mbroffset", |
||
438 | &ms->mbroffset, sizeof(ms->mbroffset)); |
||
439 | return; |
||
440 | } else { |
||
441 | gctl_error(req, "Unknown verb parameter"); |
||
442 | } |
||
443 | |||
444 | return; |
||
445 | } |
||
446 | |||
447 | /* Finally, register with GEOM infrastructure. */ |
||
448 | static struct g_class g_nbsd_class = { |
||
449 | .name = NBSD_CLASS_NAME, |
||
450 | .version = G_VERSION, |
||
451 | .taste = g_nbsd_taste, |
||
452 | .ctlreq = g_nbsd_config, |
||
453 | .dumpconf = g_nbsd_dumpconf, |
||
454 | .ioctl = g_nbsd_ioctl, |
||
455 | }; |
||
456 | |||
457 | DECLARE_GEOM_CLASS(g_nbsd_class, g_nbsd); |