* Copyright (c) 1982, 1986, 1988 Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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
* from: @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91
* Seek sort for disks. We depend on the driver
* which calls us using b_resid as the current cylinder number.
* The argument dp structure holds a b_actf activity chain pointer
* on which we keep two queues, sorted in ascending cylinder order.
* The first queue holds those requests which are positioned after
* the current cylinder (in the first request); the second holds
* requests which came in after their cylinder number was passed.
* Thus we implement a one way scan, retracting after reaching the
* end of the drive to the first request on the second queue,
* at which time it becomes the first queue.
* A one-way scan is natural because of the way UNIX read-ahead
register struct buf
*dp
, *bp
;
* If nothing on the activity queue, then
* we become the only thing.
* If we lie after the first (currently active)
* request, then we must locate the second request list
* and add ourselves to it.
if (bp
->b_cylin
< ap
->b_cylin
) {
* Check for an ``inversion'' in the
* normally ascending cylinder numbers,
* indicating the start of the second request list.
if (ap
->av_forw
->b_cylin
< ap
->b_cylin
) {
* Search the second request list
* for the first request at a larger
* cylinder number. We go before that;
* if there is no such request, we go at end.
if (bp
->b_cylin
< ap
->av_forw
->b_cylin
)
if (bp
->b_cylin
== ap
->av_forw
->b_cylin
&&
bp
->b_blkno
< ap
->av_forw
->b_blkno
)
goto insert
; /* after last */
* No inversions... we will go after the last, and
* be the first request in the second request list.
* Request is at/after the current request...
* sort in the first request list.
* We want to go after the current request
* if there is an inversion after it (i.e. it is
* the end of the first request list), or if
* the next request is a larger cylinder than our request.
if (ap
->av_forw
->b_cylin
< ap
->b_cylin
||
bp
->b_cylin
< ap
->av_forw
->b_cylin
||
(bp
->b_cylin
== ap
->av_forw
->b_cylin
&&
bp
->b_blkno
< ap
->av_forw
->b_blkno
))
* Neither a second list nor a larger
* request... we go at the end of the first list,
* which is the same as the end of the whole schebang.
bp
->av_forw
= ap
->av_forw
;
/* encoding of disk minor numbers, should be elsewhere... */
#define dkunit(dev) (minor(dev) >> 3)
#define dkpart(dev) (minor(dev) & 7)
#define dkminor(unit, part) (((unit) << 3) | (part))
* Attempt to read a disk label from a device
* using the indicated stategy routine.
* The label must be partly set up before this:
* secpercyl, secsize and anything required for a block i/o read
* operation in the driver's strategy/start routines
* must be filled in before calling us.
* If dos partition table requested, attempt to load it and
* find disklabel inside a DOS partition. Also, if bad block
* table needed, attempt to extract it as well. Return buffer
* for use in signalling errors if requested.
* Returns null on success and an error string on failure.
readdisklabel(dev
, strat
, lp
, dp
, bdp
, bpp
)
register struct disklabel
*lp
;
struct dos_partition
*dp
;
/* minimal requirements for archtypal disk label */
if (lp
->d_secperunit
== 0)
lp
->d_secperunit
= 0x1fffffff;
if (lp
->d_partitions
[0].p_size
== 0)
lp
->d_partitions
[0].p_size
= 0x1fffffff;
lp
->d_partitions
[0].p_offset
= 0;
/* obtain buffer to probe drive with */
bp
= geteblk((int)lp
->d_secsize
);
/* request no partition relocation by driver on I/O operations */
bp
->b_dev
= makedev(major(dev
), dkminor((dkunit(dev
)), 3));
/* do dos partitions in the process of getting disklabel? */
cyl
= LABELSECTOR
/ lp
->d_secpercyl
;
struct dos_partition
*ap
;
/* read master boot record */
bp
->b_blkno
= DOSBBSECTOR
;
bp
->b_bcount
= lp
->d_secsize
;
bp
->b_flags
= B_BUSY
| B_READ
;
bp
->b_cylin
= DOSBBSECTOR
/ lp
->d_secpercyl
;
/* if successful, wander through dos partition table */
msg
= "dos partition I/O error";
/* XXX how do we check veracity/bounds of this? */
bcopy(bp
->b_un
.b_addr
+ DOSPARTOFF
, dp
,
for (i
= 0; i
< NDOSPART
; i
++, dp
++)
dp
->dp_typ
== DOSPTYP_386BSD
/* need sector address for SCSI/IDE,
cylinder for ESDI/ST506/RLL */
dospartoff
= dp
->dp_start
;
cyl
= DPCYL(dp
->dp_scyl
, dp
->dp_ssect
);
/* update disklabel with details */
lp
->d_partitions
[0].p_size
=
lp
->d_partitions
[0].p_offset
=
lp
->d_ntracks
= dp
->dp_ehd
+ 1;
lp
->d_nsectors
= DPSECT(dp
->dp_esect
);
lp
->d_subtype
|= (lp
->d_subtype
& 3)
lp
->d_secpercyl
= lp
->d_ntracks
*
/* next, dig out disk label */
bp
->b_blkno
= dospartoff
+ LABELSECTOR
;
bp
->b_bcount
= lp
->d_secsize
;
bp
->b_flags
= B_BUSY
| B_READ
;
/* if successful, locate disk label within block and validate */
msg
= "disk label I/O error";
} else for (dlp
= (struct disklabel
*)bp
->b_un
.b_addr
;
dlp
<= (struct disklabel
*)(bp
->b_un
.b_addr
+DEV_BSIZE
-sizeof(*dlp
));
dlp
= (struct disklabel
*)((char *)dlp
+ sizeof(long))) {
if (dlp
->d_magic
!= DISKMAGIC
|| dlp
->d_magic2
!= DISKMAGIC
) {
} else if (dlp
->d_npartitions
> MAXPARTITIONS
||
msg
= "disk label corrupted";
/* obtain bad sector table if requested and present */
if (bdp
&& (lp
->d_flags
& D_BADSECT
)) {
/* read a bad sector table */
bp
->b_flags
= B_BUSY
| B_READ
;
bp
->b_blkno
= lp
->d_secperunit
- lp
->d_nsectors
+ i
;
if (lp
->d_secsize
> DEV_BSIZE
)
bp
->b_blkno
*= lp
->d_secsize
/ DEV_BSIZE
;
bp
->b_blkno
/= DEV_BSIZE
/ lp
->d_secsize
;
bp
->b_bcount
= lp
->d_secsize
;
bp
->b_cylin
= lp
->d_ncylinders
- 1;
/* if successful, validate, otherwise try another */
msg
= "bad sector table I/O error";
db
= (struct dkbad
*)(bp
->b_un
.b_addr
);
#define DKBAD_MAGIC 0x4321
&& db
->bt_flag
== DKBAD_MAGIC
) {
msg
= "bad sector table corrupted";
} while ((bp
->b_flags
& B_ERROR
) && (i
+= 2) < 10 &&
bp
->b_flags
= B_INVAL
| B_AGE
| B_READ
;
/* if desired, pass back allocated block so caller can use */
* Check new disk label for sensibility
setdisklabel(olp
, nlp
, openmask
, dp
)
register struct disklabel
*olp
, *nlp
;
struct dos_partition
*dp
;
register struct partition
*opp
, *npp
;
if (nlp
->d_secpercyl
== 0 || nlp
->d_secsize
== 0
|| (nlp
->d_secsize
% DEV_BSIZE
) != 0)
/* special case to allow disklabel to be invalidated */
if (nlp
->d_magic
== 0xffffffff) {
if (nlp
->d_magic
!= DISKMAGIC
|| nlp
->d_magic2
!= DISKMAGIC
||
/* XXX missing check if other dos partitions will be overwritten */
while ((i
= ffs((long)openmask
)) != 0) {
if (nlp
->d_npartitions
<= i
)
opp
= &olp
->d_partitions
[i
];
npp
= &nlp
->d_partitions
[i
];
if (npp
->p_offset
!= opp
->p_offset
|| npp
->p_size
< opp
->p_size
)
* Copy internally-set partition information
* if new label doesn't include it. XXX
if (npp
->p_fstype
== FS_UNUSED
&& opp
->p_fstype
!= FS_UNUSED
) {
npp
->p_fstype
= opp
->p_fstype
;
npp
->p_fsize
= opp
->p_fsize
;
npp
->p_frag
= opp
->p_frag
;
nlp
->d_checksum
= dkcksum(nlp
);
* Write disk label back to device after modification.
writedisklabel(dev
, strat
, lp
, dp
)
register struct disklabel
*lp
;
struct dos_partition
*dp
;
int labelpart
, error
= 0, dospartoff
, cyl
, i
;
if (lp
->d_partitions
[labelpart
].p_offset
!= 0) {
if (lp
->d_partitions
[0].p_offset
!= 0)
return (EXDEV
); /* not quite right */
bp
= geteblk((int)lp
->d_secsize
);
/* request no partition relocation by driver on I/O operations */
bp
->b_dev
= makedev(major(dev
), dkminor((dkunit(dev
)), 3));
/* do dos partitions in the process of getting disklabel? */
cyl
= LABELSECTOR
/ lp
->d_secpercyl
;
bp
->b_blkno
= DOSBBSECTOR
;
bp
->b_bcount
= lp
->d_secsize
;
bp
->b_flags
= B_BUSY
| B_READ
;
bp
->b_cylin
= DOSBBSECTOR
/ lp
->d_secpercyl
;
if ((error
= biowait(bp
)) == 0) {
bcopy(bp
->b_un
.b_addr
+ DOSPARTOFF
, dp
,
for (i
= 0; i
< NDOSPART
; i
++, dp
++)
if(dp
->dp_size
&& dp
->dp_typ
== DOSPTYP_386BSD
/* need sector address for SCSI/IDE,
cylinder for ESDI/ST506/RLL */
dospartoff
= dp
->dp_start
;
((dp
->dp_ssect
& 0xc0) << 2);
/* disklabel in appropriate location? */
if (lp
->d_partitions
[0].p_offset
!= 0
&& lp
->d_partitions
[0].p_offset
!= dospartoff
) {
bp
->b_blkno
= dospartoff
+ LABELSECTOR
;
bp
->b_bcount
= lp
->d_secsize
;
for (dlp
= (struct disklabel
*)bp
->b_un
.b_addr
;
dlp
<= (struct disklabel
*)
(bp
->b_un
.b_addr
+ lp
->d_secsize
- sizeof(*dlp
));
dlp
= (struct disklabel
*)((char *)dlp
+ sizeof(long))) {
if (dlp
->d_magic
== DISKMAGIC
&& dlp
->d_magic2
== DISKMAGIC
&&
* Compute checksum for disk label.
register struct disklabel
*lp
;
register u_short
*start
, *end
;
register u_short sum
= 0;
end
= (u_short
*)&lp
->d_partitions
[lp
->d_npartitions
];
* Determine the size of the transfer, and make sure it is
* within the boundaries of the partition. Adjust transfer
* if needed, and signal errors or early completion.
bounds_check_with_label(struct buf
*bp
, struct disklabel
*lp
, int wlabel
)
struct partition
*p
= lp
->d_partitions
+ dkpart(bp
->b_dev
);
int labelsect
= lp
->d_partitions
[0].p_offset
;
sz
= (bp
->b_bcount
+ DEV_BSIZE
- 1) >> DEV_BSHIFT
;
/* overwriting disk label ? */
/* XXX should also protect bootstrap in first 8K */
if (bp
->b_blkno
+ p
->p_offset
<= LABELSECTOR
+ labelsect
&&
bp
->b_blkno
+ p
->p_offset
+ sz
> LABELSECTOR
+ labelsect
&&
(bp
->b_flags
& B_READ
) == 0 && wlabel
== 0) {
#if defined(DOSBBSECTOR) && defined(notyet)
/* overwriting master boot record? */
if (bp
->b_blkno
+ p
->p_offset
<= DOSBBSECTOR
&&
(bp
->b_flags
& B_READ
) == 0 && wlabel
== 0) {
if (bp
->b_blkno
< 0 || bp
->b_blkno
+ sz
> maxsz
) {
/* if exactly at end of disk, return an EOF */
if (bp
->b_blkno
== maxsz
) {
bp
->b_resid
= bp
->b_bcount
;
/* or truncate if part of it fits */
sz
= maxsz
- bp
->b_blkno
;
bp
->b_bcount
= sz
<< DEV_BSHIFT
;
/* calculate cylinder for disksort to order transfers with */
bp
->b_cylin
= (bp
->b_blkno
+ p
->p_offset
) / lp
->d_secpercyl
;
* Disk error is the preface to plaintive error messages
* about failing disk transfers. It prints messages of the form
hp0g: hard error reading fsbn 12345 of 12344-12347 (hp0 bn %d cn %d tn %d sn %d)
* if the offset of the error in the transfer and a disk label
* are both available. blkdone should be -1 if the position of the error
* is unknown; the disklabel pointer may be null from drivers that have not
* been converted to use them. The message is printed with printf
* if pri is LOG_PRINTF, otherwise it uses log at the specified priority.
* The message should be completed (with at least a newline) with printf
* or addlog, respectively. There is no trailing space.
diskerr(bp
, dname
, what
, pri
, blkdone
, lp
)
register struct disklabel
*lp
;
int unit
= dkunit(bp
->b_dev
), part
= dkpart(bp
->b_dev
);
register int (*pr
) __P((const char *, ...));
char partname
= 'a' + part
;
(*pr
)("%s%d%c: %s %sing fsbn ", dname
, unit
, partname
, what
,
bp
->b_flags
& B_READ
? "read" : "writ");
if (bp
->b_bcount
<= DEV_BSIZE
)
(*pr
)("%d-%d", bp
->b_blkno
,
bp
->b_blkno
+ (bp
->b_bcount
- 1) / DEV_BSIZE
);
if (lp
&& (blkdone
>= 0 || bp
->b_bcount
<= lp
->d_secsize
)) {
sn
*= DEV_BSIZE
/ lp
->d_secsize
; /* XXX */
sn
+= lp
->d_partitions
[part
].p_offset
;
(*pr
)(" (%s%d bn %d; cn %d", dname
, unit
, sn
,
(*pr
)(" tn %d sn %d)", sn
/ lp
->d_nsectors
, sn
% lp
->d_nsectors
);