diff options
author | phk <phk@FreeBSD.org> | 2003-01-26 21:54:36 +0000 |
---|---|---|
committer | phk <phk@FreeBSD.org> | 2003-01-26 21:54:36 +0000 |
commit | f71e2dab31370e0325052fc73b068e52032e831e (patch) | |
tree | 70afd612269d511d30366eccb8b10b03b5585207 /sys | |
parent | 0d45de23ba9e63ea3cdc543dd8676df4151503cd (diff) | |
download | FreeBSD-src-f71e2dab31370e0325052fc73b068e52032e831e.zip FreeBSD-src-f71e2dab31370e0325052fc73b068e52032e831e.tar.gz |
Implement DIOCBSDBB ioctl which overwrites first BBSIZE bytes of BSD
labeled disk.
This is complicated by the fact that BBSIZE is greater than the
PAGE_SIZE limit ioctl inflicts on arguments which are automatically
copied in.
As long as we don't need access to userland memory (copyin/out) we
can deal with the ioctl using g_callme() which executes it from the
GEOM event thread.
Once we need copyin/out, we need to return the bio with EDIRIOCTL
in order to make geom_dev call us back in the original process context
where copyin will work.
Unfortunately, that results in us getting called with Giant, so
we have to DROP_GIANT/PICKUP_GIANT around the code where we diddle
GEOMs internals.
Sometimes you just can't win...
... But it does make geom_bsd.c an almost complete example of the
GEOM beastiarium.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/geom/geom_bsd.c | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/sys/geom/geom_bsd.c b/sys/geom/geom_bsd.c index 70c0758..2f93f32 100644 --- a/sys/geom/geom_bsd.c +++ b/sys/geom/geom_bsd.c @@ -547,6 +547,63 @@ g_bsd_ioctl(void *arg) } /* + * Rewrite the bootblock, which is BBSIZE bytes from the start of the disk. + * We punch down the disklabel where we expect it to be before writing. + */ +static int +g_bsd_diocbsdbb(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) +{ + struct g_geom *gp; + struct g_slicer *gsp; + struct g_bsd_softc *ms; + struct disklabel *dl; + struct g_consumer *cp; + u_char *buf; + void *p; + u_int secsize; + int error, i; + uint64_t sum; + + /* Get hold of the interesting bits from the bio. */ + gp = (void *)dev; + gsp = gp->softc; + ms = gsp->softc; + + /* The disklabel to set is the ioctl argument. */ + buf = g_malloc(BBSIZE, 0); + p = *(void **)data; + error = copyin(p, buf, BBSIZE); + if (error) { + g_free(buf); + return (error); + } + /* The disklabel to set is the ioctl argument. */ + dl = (void *)(buf + ms->labeloffset); + + DROP_GIANT(); + + /* Validate and modify our slice instance to match. */ + error = g_bsd_modify(gp, dl); /* Picks up topology lock on success. */ + if (!error) { + cp = LIST_FIRST(&gp->consumer); + secsize = cp->provider->sectorsize; + dl = &ms->ondisk; + g_bsd_leenc_disklabel(buf + ms->labeloffset, dl); + if (ms->labeloffset == ALPHA_LABEL_OFFSET) { + sum = 0; + for (i = 0; i < 63; i++) + sum += g_dec_le8(buf + i * 8); + g_enc_le8(buf + 504, sum); + } + error = g_write_data(cp, 0, buf, BBSIZE); + g_topology_unlock(); + } + g_free(buf); + PICKUP_GIANT(); + return (error); +} + +/* * If the user tries to overwrite our disklabel through an open partition * or via a magicwrite config call, we end up here and try to prevent * footshooting as best we can. @@ -655,6 +712,11 @@ g_bsd_start(struct bio *bp) bcopy(&ms->inram, gio->data, sizeof(ms->inram)); g_io_deliver(bp, 0); return (1); + case DIOCBSDBB: + gio->func = g_bsd_diocbsdbb; + gio->dev = (void *)gp; + g_io_deliver(bp, EDIRIOCTL); + return (1); case DIOCSDINFO: case DIOCWDINFO: /* |