diff options
Diffstat (limited to 'sys/kern/subr_diskslice.c')
-rw-r--r-- | sys/kern/subr_diskslice.c | 123 |
1 files changed, 106 insertions, 17 deletions
diff --git a/sys/kern/subr_diskslice.c b/sys/kern/subr_diskslice.c index 7dde87c..50a7923 100644 --- a/sys/kern/subr_diskslice.c +++ b/sys/kern/subr_diskslice.c @@ -43,7 +43,7 @@ * from: wd.c,v 1.55 1994/10/22 01:57:12 phk Exp $ * from: @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91 * from: ufs_disksubr.c,v 1.8 1994/06/07 01:21:39 phk Exp $ - * $Id: subr_diskslice.c,v 1.3 1995/01/31 04:33:41 phk Exp $ + * $Id: subr_diskslice.c,v 1.4 1995/02/16 15:19:00 bde Exp $ */ #include <sys/param.h> @@ -62,8 +62,11 @@ #define FALSE 0 #define TRUE 1 + typedef u_char bool_t; +static void adjust_label __P((struct disklabel *lp, u_long offset)); +static void dsiodone __P((struct buf *bp)); static char *fixlabel __P((char *dname, int unit, int slice, struct diskslice *sp, struct disklabel *lp)); static void partition_info __P((char *dname, int unit, int slice, @@ -77,6 +80,20 @@ static void set_ds_label __P((struct diskslices *ssp, int slice, static void set_ds_wlabel __P((struct diskslices *ssp, int slice, int wlabel)); +static void +adjust_label(lp, offset) + struct disklabel *lp; + u_long offset; +{ + int part; + struct partition *pp; + + pp = &lp->d_partitions[0]; + for (part = 0; part < lp->d_npartitions; part++, pp++) + if (pp->p_offset != 0 || pp->p_size != 0) + pp->p_offset += offset; +} + /* * Determine the size of the transfer, and make sure it is * within the boundaries of the partition. Adjust transfer @@ -162,11 +179,48 @@ if (labelsect != 0) Debugger("labelsect != 0 in dscheck()"); /* calculate cylinder for disksort to order transfers with */ bp->b_pblkno = blkno + sp->ds_offset; - if (lp == NULL) bp->b_cylinder = 0; /* XXX always 0 would be better */ else bp->b_cylinder = bp->b_pblkno / lp->d_secpercyl; + + /* + * Snoop on label accesses if the slice offset is nonzero. Fudge + * offsets in the label to keep the in-core label coherent with + * the on-disk one. + */ + if (blkno <= LABELSECTOR + labelsect +#if LABELSECTOR != 0 + && bp->b_blkno + sz > LABELSECTOR + labelsect +#endif + && sp->ds_offset != 0) { + struct iodone_chain *ic; + + ic = malloc(sizeof *ic , M_DEVBUF, M_WAITOK); + ic->ic_prev_flags = bp->b_flags; + ic->ic_prev_iodone = bp->b_iodone; + ic->ic_prev_iodone_chain = bp->b_iodone_chain; + ic->ic_args[0].ia_long = (LABELSECTOR + labelsect - blkno) + << DEV_BSHIFT; + ic->ic_args[1].ia_long = sp->ds_offset; + bp->b_flags |= B_CALL; + bp->b_iodone = dsiodone; + bp->b_iodone_chain = ic; + if (!(bp->b_flags & B_READ)) { + /* + * XXX even disklabel(8) writes directly so we need + * to adjust writes. Perhaps we should drop support + * for DIOCWLABEL (always write protect labels) and + * require the use of DIOCWDINFO. + * + * XXX probably need to copy the data to avoid even + * temporarily corrupting the in-core copy. + */ + adjust_label((struct disklabel *) + (bp->b_data + ic->ic_args[0].ia_long), + sp->ds_offset); + } + } return (1); bad: @@ -287,6 +341,9 @@ dsioctl(dev, cmd, data, flags, ssp, strat, setgeom) error = setdisklabel(lp, (struct disklabel *)data, sp->ds_label != NULL ? sp->ds_openmask : (u_long)0); + /* XXX why doesn't setdisklabel() check this? */ + if (error == 0 && lp->d_partitions[RAW_PART].p_offset != 0) + error = EINVAL; #if 0 /* XXX */ if (error != 0 && setgeom != NULL) error = setgeom(lp); @@ -311,7 +368,17 @@ dsioctl(dev, cmd, data, flags, ssp, strat, setgeom) */ old_wlabel = sp->ds_wlabel; set_ds_wlabel(ssp, slice, TRUE); - error = correct_writedisklabel(dev, strat, sp->ds_label); + /* + * XXX convert on-disk label offsets to absolute sectors for + * backwards compatibility. + */ + lp = malloc(sizeof *lp, M_DEVBUF, M_WAITOK); + *lp = *sp->ds_label; + adjust_label(lp, sp->ds_offset); + + error = correct_writedisklabel(dev, strat, lp); + /* XXX should restore old label if writedisklabel() failed. */ + free(lp, M_DEVBUF); set_ds_wlabel(ssp, slice, old_wlabel); return (error); @@ -328,6 +395,27 @@ dsioctl(dev, cmd, data, flags, ssp, strat, setgeom) } } +static void +dsiodone(bp) + struct buf *bp; +{ + struct iodone_chain *ic; + struct disklabel *lp; + + ic = bp->b_iodone_chain; + bp->b_flags = (ic->ic_prev_flags & B_CALL) + | (bp->b_flags & ~(B_CALL | B_DONE)); + bp->b_iodone = ic->ic_prev_iodone; + bp->b_iodone_chain = ic->ic_prev_iodone_chain; + free(ic, M_DEVBUF); + lp = (struct disklabel *)(bp->b_data + ic->ic_args[0].ia_long); + if (!(bp->b_flags & B_READ)) + adjust_label(lp, ic->ic_args[1].ia_long); + else if (!(bp->b_flags & B_ERROR) && bp->b_error == 0) + adjust_label(lp, -lp->d_partitions[RAW_PART].p_offset); + biodone(bp); +} + int dsisopen(ssp) struct diskslices *ssp; @@ -403,8 +491,11 @@ dsopen(dname, dev, mode, sspp, lp, strat, setgeom) lp1 = malloc(sizeof *lp1, M_DEVBUF, M_WAITOK); *lp1 = *lp; lp = lp1; - if (slice == WHOLE_DISK_SLICE) - goto set; + if (slice == WHOLE_DISK_SLICE) { + sp->ds_label = lp; + sp->ds_wlabel = TRUE; + goto out; + } printf("readdisklabel\n"); msg = correct_readdisklabel(dkmodpart(dev, RAW_PART), strat, lp); #if 0 /* XXX */ @@ -446,9 +537,7 @@ dsopen(dname, dev, mode, sspp, lp, strat, setgeom) return (EINVAL); /* XXX needs translation */ } } -set: set_ds_label(ssp, slice, lp); - set_ds_wlabel(ssp, slice, FALSE); } if (part != RAW_PART && (sp->ds_label == NULL || part >= sp->ds_label->d_npartitions)) @@ -476,7 +565,7 @@ fixlabel(dname, unit, slice, sp, lp) struct disklabel *lp; { bool_t adjust; - int bsdpart; + int part; struct partition *pp; bool_t warned; @@ -511,7 +600,7 @@ fixlabel(dname, unit, slice, sp, lp) warned = TRUE; } pp -= LABEL_PART; - for (bsdpart = 0; bsdpart < lp->d_npartitions; bsdpart++, pp++) { + for (part = 0; part < lp->d_npartitions; part++, pp++) { if (adjust && (pp->p_offset != 0 || pp->p_size != 0)) pp->p_offset -= sp->ds_offset; if (pp->p_offset + pp->p_size > sp->ds_size @@ -525,7 +614,7 @@ fixlabel(dname, unit, slice, sp, lp) } if (adjust) pp->p_offset += sp->ds_offset; - partition_info(dname, unit, slice, bsdpart, pp); + partition_info(dname, unit, slice, part, pp); bzero(pp, sizeof *pp); } } @@ -548,13 +637,6 @@ partition_info(dname, unit, slice, part, pp) pp->p_offset, pp->p_offset + pp->p_size - 1, pp->p_size); } -/* - * Most changes to ds_bad, ds_label and ds_wlabel are made using the - * following functions to ensure coherency of the compatibility slice - * with the first BSD slice. The openmask fields are _not_ shared and - * the other fields (ds_offset and ds_size) aren't changed after they - * are initialized. - */ static void slice_info(dname, unit, slice, sp) char *dname; @@ -567,6 +649,13 @@ slice_info(dname, unit, slice, sp) sp->ds_offset, sp->ds_offset + sp->ds_size - 1, sp->ds_size); } +/* + * Most changes to ds_bad, ds_label and ds_wlabel are made using the + * following functions to ensure coherency of the compatibility slice + * with the first BSD slice. The openmask fields are _not_ shared and + * the other fields (ds_offset and ds_size) aren't changed after they + * are initialized. + */ static void set_ds_bad(ssp, slice, btp) struct diskslices *ssp; |