0,0 → 1,457 |
/*- |
* Copyright 2005, Anatoli Klassen <anatoli@aksoft.net> |
* All rights reserved. |
* |
* The code is based on code of geom_bsd module by Poul-Henning Kamp. |
* |
* Copyright (c) 2002 Poul-Henning Kamp |
* Copyright (c) 2002 Networks Associates Technology, Inc. |
* All rights reserved. |
* |
* This software was developed for the FreeBSD Project by Poul-Henning Kamp |
* and NAI Labs, the Security Research Division of Network Associates, Inc. |
* under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the |
* DARPA CHATS research program. |
* |
* 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 names of the authors may not be used to endorse or promote |
* products derived from this software without specific prior written |
* permission. |
* |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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. |
*/ |
|
/* |
* This is the method for dealing with BSD disklabels. It has been |
* extensively (by my standards at least) commented, in the vain hope that |
* it will serve as the source in future copy&paste operations. |
*/ |
|
#include <sys/param.h> |
#include <sys/systm.h> |
#include <sys/kernel.h> |
#include <sys/bio.h> |
#include <sys/malloc.h> |
#include <sys/lock.h> |
#include <sys/md5.h> |
#include <sys/errno.h> |
#include <geom/geom.h> |
#include <geom/geom_slice.h> |
|
#include "disklabel.h" |
|
#define NBSD_CLASS_NAME "NetBSD" |
|
#define LABELSIZE (148 + 16 * NBSD_MAXPARTITIONS) |
|
/* |
* Our private data about one instance. All the rest is handled by the |
* slice code and stored in its softc, so this is just the stuff |
* specific to BSD disklabels. |
*/ |
struct g_nbsd_softc { |
int mbrtype; |
off_t labeloffset; |
off_t mbroffset; |
off_t rawoffset; |
struct disklabel ondisk; |
u_char label[LABELSIZE]; |
u_char labelsum[16]; |
}; |
|
/* |
* Modify our slicer to match proposed disklabel, if possible. |
* This is where we make sure we don't do something stupid. |
*/ |
static int |
g_nbsd_modify(struct g_geom *gp, struct g_nbsd_softc *ms) |
{ |
char useable[NBSD_MAXPARTITIONS]; |
MD5_CTX md5sum; |
struct partition *ppp; |
struct g_slicer *gsp; |
struct g_consumer *cp; |
struct disklabel *dl; |
off_t rawoffset, fullsize, o; |
int i, error; |
u_int secsize, u; |
|
g_topology_assert(); |
gsp = gp->softc; |
dl = &ms->ondisk; |
|
/* Get dimensions of our device. */ |
cp = LIST_FIRST(&gp->consumer); |
secsize = cp->provider->sectorsize; |
|
/* ... or a smaller sector size. */ |
if (dl->d_secsize < secsize) { |
return (EINVAL); |
} |
|
/* ... or a non-multiple sector size. */ |
if (dl->d_secsize % secsize != 0) { |
return (EINVAL); |
} |
|
rawoffset = ms->mbroffset; |
fullsize = cp->provider->mediasize; |
|
for (i = 0; i < dl->d_npartitions; i++) { |
ppp = &dl->d_partitions[i]; |
/* skip partitions with no type or outside of slide */ |
if (ppp->p_size == 0 || ppp->p_fstype == 0 || |
(off_t)ppp->p_offset * dl->d_secsize < rawoffset || |
(off_t)(ppp->p_offset + ppp->p_size) * dl->d_secsize |
> (rawoffset + fullsize)) |
useable[i] = 0; |
else |
useable[i] = 1; |
} |
|
/* Don't munge open partitions. */ |
for (i = 0; i < dl->d_npartitions; i++) { |
if (!useable[i]) |
continue; |
|
ppp = &dl->d_partitions[i]; |
o = (off_t)ppp->p_offset * dl->d_secsize; |
if (o == 0) |
o = rawoffset; |
error = g_slice_config(gp, i, G_SLICE_CONFIG_CHECK, |
o - rawoffset, (off_t)ppp->p_size * dl->d_secsize, dl->d_secsize, |
"%s%c", gp->name, 'a' + i); |
if (error) |
return (error); |
} |
|
/* Look good, go for it... */ |
for (u = 0; u < gsp->nslice; u++) { |
if (!useable[u]) |
continue; |
|
ppp = &dl->d_partitions[u]; |
o = (off_t)ppp->p_offset * dl->d_secsize; |
if (o == 0) |
o = rawoffset; |
g_slice_config(gp, u, G_SLICE_CONFIG_SET, |
o - rawoffset, (off_t)ppp->p_size * dl->d_secsize, dl->d_secsize, |
"%s%c", gp->name, 'a' + u); |
} |
|
/* Update our softc */ |
ms->rawoffset = rawoffset; |
|
/* |
* In order to avoid recursively attaching to the same |
* on-disk label (it's usually visible through the 'c' |
* partition) we calculate an MD5 and ask if other BSD's |
* below us love that label. If they do, we don't. |
*/ |
MD5Init(&md5sum); |
MD5Update(&md5sum, ms->label, sizeof(ms->label)); |
MD5Final(ms->labelsum, &md5sum); |
|
return (0); |
} |
|
/* |
* This is an internal helper function, called multiple times from the taste |
* function to try to locate a disklabel on the disk. More civilized formats |
* will not need this, as there is only one possible place on disk to look |
* for the magic spot. |
*/ |
|
static int |
g_nbsd_try(struct g_geom *gp, struct g_slicer *gsp, struct g_consumer *cp, |
int secsize, struct g_nbsd_softc *ms, off_t offset) |
{ |
int error; |
u_char *buf; |
struct disklabel *dl; |
off_t secoff; |
|
/* |
* We need to read entire aligned sectors, and we assume that the |
* disklabel does not span sectors, so one sector is enough. |
*/ |
error = 0; |
secoff = offset % secsize; |
buf = g_read_data(cp, offset - secoff, secsize, &error); |
if (buf == NULL || error != 0) |
return (ENOENT); |
|
/* Decode into our native format. */ |
dl = &ms->ondisk; |
error = nbsd_disklabel_le_dec(buf + secoff, dl, NBSD_MAXPARTITIONS); |
|
/* do not recognize slices which will be recognized by geom_bsd module */ |
if (!error) |
if (ms->mbrtype == MBR_FREEBSD && |
dl->d_npartitions <= FREEBSD_MAXPARTITIONS) |
error = EINVAL; |
|
if (!error) |
bcopy(buf + secoff, ms->label, LABELSIZE); |
|
/* Remember to free the buffer g_read_data() gave us. */ |
g_free(buf); |
|
ms->labeloffset = offset; |
return (error); |
} |
|
/*- |
* This start routine is only called for non-trivial requests, all the |
* trivial ones are handled autonomously by the slice code. |
* For requests we handle here, we must call the g_io_deliver() on the |
* bio, and return non-zero to indicate to the slice code that we did so. |
* This code executes in the "DOWN" I/O path, this means: |
* * No sleeping. |
* * Don't grab the topology lock. |
* * Don't call biowait, g_getattr(), g_setattr() or g_read_data() |
*/ |
static int |
g_nbsd_ioctl(struct g_provider *pp, u_long cmd, void *data, int fflag, struct thread *td) |
{ |
return (ENOIOCTL); |
} |
|
static int |
g_nbsd_start(struct bio *bp) |
{ |
struct g_geom *gp; |
struct g_nbsd_softc *ms; |
struct g_slicer *gsp; |
|
gp = bp->bio_to->geom; |
gsp = gp->softc; |
ms = gsp->softc; |
if (bp->bio_cmd == BIO_GETATTR) { |
if (g_handleattr(bp, "NBSD::labelsum", ms->labelsum, |
sizeof(ms->labelsum))) |
return (1); |
} |
return (0); |
} |
|
/* |
* Dump configuration information in XML format. |
* Notice that the function is called once for the geom and once for each |
* consumer and provider. We let g_slice_dumpconf() do most of the work. |
*/ |
static void |
g_nbsd_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, |
struct g_consumer *cp, struct g_provider *pp) |
{ |
struct g_nbsd_softc *ms; |
struct g_slicer *gsp; |
|
gsp = gp->softc; |
ms = gsp->softc; |
g_slice_dumpconf(sb, indent, gp, cp, pp); |
if (indent != NULL && pp == NULL && cp == NULL) { |
sbuf_printf(sb, "%s<labeloffset>%jd</labeloffset>\n", |
indent, (intmax_t)ms->labeloffset); |
sbuf_printf(sb, "%s<rawoffset>%jd</rawoffset>\n", |
indent, (intmax_t)ms->rawoffset); |
sbuf_printf(sb, "%s<mbroffset>%jd</mbroffset>\n", |
indent, (intmax_t)ms->mbroffset); |
} else if (pp != NULL) { |
if (indent == NULL) |
sbuf_printf(sb, " ty %d", |
ms->ondisk.d_partitions[pp->index].p_fstype); |
else |
sbuf_printf(sb, "%s<type>%d</type>\n", indent, |
ms->ondisk.d_partitions[pp->index].p_fstype); |
} |
} |
|
/* |
* The taste function is called from the event-handler, with the topology |
* lock already held and a provider to examine. The flags are unused. |
* |
* If flags == G_TF_NORMAL, the idea is to take a bite of the provider and |
* if we find valid, consistent magic on it, build a geom on it. |
* any magic bits which indicate that we should automatically put a BSD |
* geom on it. |
* |
* There may be cases where the operator would like to put a BSD-geom on |
* providers which do not meet all of the requirements. This can be done |
* by instead passing the G_TF_INSIST flag, which will override these |
* checks. |
* |
* The final flags value is G_TF_TRANSPARENT, which instructs the method |
* to put a geom on top of the provider and configure it to be as transparent |
* as possible. This is not really relevant to the BSD method and therefore |
* not implemented here. |
*/ |
|
static struct g_geom * |
g_nbsd_taste(struct g_class *mp, struct g_provider *pp, int flags) |
{ |
MD5_CTX md5sum; |
u_char hash[16]; |
struct g_geom *gp; |
struct g_consumer *cp; |
struct g_nbsd_softc *ms; |
struct g_slicer *gsp; |
int error; |
u_int secsize; |
|
g_trace(G_T_TOPOLOGY, "nbsd_taste(%s,%s)", mp->name, pp->name); |
g_topology_assert(); |
|
/* We don't implement transparent inserts. */ |
if (flags == G_TF_TRANSPARENT) |
return (NULL); |
|
/* |
* BSD labels are a subclass of the general "slicing" topology so |
* a lot of the work can be done by the common "slice" code. |
* Create a geom with space for NBSD_MAXPARTITIONS providers, one consumer |
* and a softc structure for us. Specify the provider to attach |
* the consumer to and our "start" routine for special requests. |
* The provider is opened with mode (1,0,0) so we can do reads |
* from it. |
*/ |
gp = g_slice_new(mp, NBSD_MAXPARTITIONS, pp, &cp, &ms, |
sizeof(*ms), g_nbsd_start); |
if (gp == NULL) |
return (NULL); |
|
/* Get the geom_slicer softc from the geom. */ |
gsp = gp->softc; |
|
/* |
* The do...while loop here allows us to have multiple escapes |
* using a simple "break". This improves code clarity without |
* ending up in deep nesting and without using goto or come from. |
*/ |
do { |
/* |
* If the provider is an MBR we will only auto attach |
* to type 165|166|169 slices in the G_TF_NORMAL case. We will |
* attach to any other type. |
*/ |
error = g_getattr("MBR::type", cp, &ms->mbrtype); |
if (!error) { |
if (ms->mbrtype != MBR_OPENBSD && ms->mbrtype != MBR_NETBSD && |
ms->mbrtype != MBR_FREEBSD && flags == G_TF_NORMAL) |
break; |
error = g_getattr("MBR::offset", cp, &ms->mbroffset); |
if (error) |
break; |
} |
|
/* Same thing if we are inside a PC98 */ |
/* XXX not implemented */ |
|
/* Get sector size, we need it to read data. */ |
secsize = cp->provider->sectorsize; |
if (secsize < 512) |
break; |
|
/* First look for a label at the start of the second sector. */ |
error = g_nbsd_try(gp, gsp, cp, secsize, ms, secsize); |
|
/* If we didn't find a label, punt. */ |
if (error) |
break; |
|
/* |
* In order to avoid recursively attaching to the same |
* on-disk label (it's usually visible through the 'c' |
* partition) we calculate an MD5 and ask if other BSD's |
* below us love that label. If they do, we don't. |
*/ |
MD5Init(&md5sum); |
MD5Update(&md5sum, ms->label, sizeof(ms->label)); |
MD5Final(ms->labelsum, &md5sum); |
|
error = g_getattr("NBSD::labelsum", cp, &hash); |
if (!error && !bcmp(ms->labelsum, hash, sizeof(hash))) |
break; |
|
/* |
* Process the found disklabel, and modify our "slice" |
* instance to match it, if possible. |
*/ |
error = g_nbsd_modify(gp, ms); |
} while (0); |
|
/* Success or failure, we can close our provider now. */ |
g_access(cp, -1, 0, 0); |
|
/* If we have configured any providers, return the new geom. */ |
if (gsp->nprovider > 0) { |
g_slice_conf_hot(gp, 0, ms->labeloffset, LABELSIZE, |
G_SLICE_HOT_ALLOW, G_SLICE_HOT_DENY, G_SLICE_HOT_CALL); |
return (gp); |
} |
/* |
* ...else push the "self-destruct" button, by spoiling our own |
* consumer. This triggers a call to g_slice_spoiled which will |
* dismantle what was setup. |
*/ |
g_slice_spoiled(cp); |
return (NULL); |
} |
|
/* |
* NB! curthread is user process which GCTL'ed. |
*/ |
static void |
g_nbsd_config(struct gctl_req *req, struct g_class *mp, char const *verb) |
{ |
struct g_geom *gp; |
struct g_slicer *gsp; |
struct g_consumer *cp; |
struct g_nbsd_softc *ms; |
|
g_topology_assert(); |
gp = gctl_get_geom(req, mp, "geom"); |
if (gp == NULL) |
return; |
cp = LIST_FIRST(&gp->consumer); |
gsp = gp->softc; |
ms = gsp->softc; |
if (!strcmp(verb, "read mbroffset")) { |
gctl_set_param(req, "mbroffset", |
&ms->mbroffset, sizeof(ms->mbroffset)); |
return; |
} else { |
gctl_error(req, "Unknown verb parameter"); |
} |
|
return; |
} |
|
/* Finally, register with GEOM infrastructure. */ |
static struct g_class g_nbsd_class = { |
.name = NBSD_CLASS_NAME, |
.version = G_VERSION, |
.taste = g_nbsd_taste, |
.ctlreq = g_nbsd_config, |
.dumpconf = g_nbsd_dumpconf, |
.ioctl = g_nbsd_ioctl |
}; |
|
DECLARE_GEOM_CLASS(g_nbsd_class, g_nbsd); |