Subversion Repositories general

Rev

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);