diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib/disks.b | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/lib/disks.b')
| -rw-r--r-- | appl/lib/disks.b | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/appl/lib/disks.b b/appl/lib/disks.b new file mode 100644 index 00000000..ac718af1 --- /dev/null +++ b/appl/lib/disks.b @@ -0,0 +1,377 @@ +implement Disks; + +# adapted from /sys/src/libdisk on Plan 9: subject to Lucent Public License 1.02 + +include "sys.m"; + sys: Sys; + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "disks.m"; + +scsiverbose := 0; + +Codefile: con "/lib/scsicodes"; + +Code: adt { + v: int; # (asc<<8) | ascq + s: string; +}; +codes: array of Code; + +init() +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; +} + +# +# Discover the disk geometry by various sleazeful means. +# +# First, if there is a partition table in sector 0, +# see if all the partitions have the same end head +# and sector; if so, we'll assume that that's the +# right count. +# +# If that fails, we'll try looking at the geometry that the ATA +# driver supplied, if any, and translate that as a +# BIOS might. +# +# If that too fails, which should only happen on a SCSI +# disk with no currently defined partitions, we'll try +# various common (h, s) pairs used by BIOSes when faking +# the geometries. +# + +# table entry: + Oactive, # active flag + Ostarth, # starting head + Ostarts, # starting sector + Ostartc, # starting cylinder + Otype, # partition type + Oendh, # ending head + Oends, # ending sector + Oendc: con iota; # ending cylinder + Oxlba: con Oendc+1; # starting LBA from start of disc or partition [4] + Oxsize: con Oxlba+4; # size in sectors[4] + +# Table: entry[NTentry][TentrySize] magic[2] +Omagic: con NTentry*TentrySize; + +partitiongeometry(d: ref Disk): int +{ + buf := array[512] of byte; + t := buf[Toffset:]; + + # + # look for an MBR first in the /dev/sdXX/data partition, otherwise + # attempt to fall back on the current partition. + # + rawfd := sys->open(d.prefix+"data", Sys->OREAD); + if(rawfd != nil + && sys->seek(rawfd, big 0, 0) == big 0 + && readn(rawfd, buf, 512) == 512 + && int t[Omagic] == Magic0 + && int t[Omagic+1] == Magic1) { + rawfd = nil; + }else{ + rawfd = nil; + if(sys->seek(d.fd, big 0, 0) < big 0 + || readn(d.fd, buf, 512) != 512 + || int t[Omagic] != Magic0 + || int t[Omagic+1] != Magic1) + return -1; + } + + h := s := -1; + for(i:=0; i<NTentry*TentrySize; i += TentrySize) { + if(t[i+Otype] == byte 0) + continue; + + t[i+Oends] &= byte 63; + if(h == -1) { + h = int t[i+Oendh]; + s = int t[i+Oends]; + } else { + # + # Only accept the partition info if every + # partition is consistent. + # + if(h != int t[i+Oendh] || s != int t[i+Oends]) + return -1; + } + } + + if(h == -1) + return -1; + + d.h = h+1; # heads count from 0 + d.s = s; # sectors count from 1 + d.c = int (d.secs / big (d.h*d.s)); + d.chssrc = "part"; + return 0; +} + +# +# If there is ATA geometry, use it, perhaps massaged +# +drivergeometry(d: ref Disk): int +{ + if(d.c == 0 || d.h == 0 || d.s == 0) + return -1; + + d.chssrc = "disk"; + if(d.c < 1024) + return 0; + + case d.h { + 15 => + d.h = 255; + d.c /= 17; + + * => + for(m := 2; m*d.h < 256; m *= 2) { + if(d.c/m < 1024) { + d.c /= m; + d.h *= m; + return 0; + } + } + + # set to 255, 63 and be done with it + d.h = 255; + d.s = 63; + d.c = int (d.secs / big(d.h * d.s)); + } + return 0; +} + +# +# There's no ATA geometry and no partitions. +# Our guess is as good as anyone's. +# +Guess: adt { + h: int; + s: int; +}; +guess: array of Guess = array[] of { + (64, 32), + (64, 63), + (128, 63), + (255, 63), +}; + +guessgeometry(d: ref Disk) +{ + d.chssrc = "guess"; + c := 1024; + for(i:=0; i<len guess; i++) + if(big(c*guess[i].h*guess[i].s) >= d.secs) { + d.h = guess[i].h; + d.s = guess[i].s; + d.c = int(d.secs / big(d.h * d.s)); + return; + } + + # use maximum values + d.h = 255; + d.s = 63; + d.c = int(d.secs / big(d.h * d.s)); +} + +findgeometry(disk: ref Disk) +{ + disk.h = disk.s = disk.c = 0; + if(partitiongeometry(disk) < 0 && + drivergeometry(disk) < 0) + guessgeometry(disk); +} + +openfile(d: ref Disk): ref Disk +{ + (ok, db) := sys->fstat(d.fd); + if(ok < 0) + return nil; + + d.secsize = 512; + d.size = db.length; + d.secs = d.size / big d.secsize; + d.offset = big 0; + + findgeometry(d); + return mkwidth(d); +} + +opensd(d: ref Disk): ref Disk +{ + b := bufio->fopen(d.ctlfd, Bufio->OREAD); + while((p := b.gets('\n')) != nil){ + p = p[0:len p - 1]; + (nf, f) := sys->tokenize(p, " \t"); # might need str->unquote + if(nf >= 3 && hd f == "geometry") { + d.secsize = int hd tl tl f; + if(nf >= 6) { + d.c = int hd tl tl tl f; + d.h = int hd tl tl tl tl f; + d.s = int hd tl tl tl tl tl f; + } + } + if(nf >= 4 && hd f == "part" && hd tl f == d.part) { + d.offset = big hd tl tl f; + d.secs = big hd tl tl tl f - d.offset; + } + } + + + d.size = d.secs * big d.secsize; + if(d.size <= big 0) { + d.part = ""; + d.dtype = "file"; + return openfile(d); + } + + findgeometry(d); + return mkwidth(d); +} + +Disk.open(name: string, mode: int, noctl: int): ref Disk +{ + d := ref Disk; + d.rdonly = mode == Sys->OREAD; + d.fd = sys->open(name, Sys->OREAD); + if(d.fd == nil) + return nil; + + if(mode != Sys->OREAD){ + d.wfd = sys->open(name, Sys->OWRITE); + if(d.wfd == nil) + d.rdonly = 1; + } + + if(noctl) + return openfile(d); + + # check for floppy(3) disk + if(len name >= 7) { + q := name[len name-7:]; + if(q[0] == 'f' && q[1] == 'd' && isdigit(q[2]) && q[3:] == "disk") { + if((d.ctlfd = sys->open(name[0:len name-4]+"ctl", Sys->ORDWR)) != nil) { + d.prefix = name[0:len name-4]; # fdN (unlike Plan 9) + d.dtype = "floppy"; + return openfile(d); + } + } + } + + # attempt to find sd(3) disk or partition + d.prefix = name; + for(i := len name; --i >= 0;) + if(name[i] == '/'){ + d.prefix = name[0:i+1]; + break; + } + + if((d.ctlfd = sys->open(d.prefix+"ctl", Sys->ORDWR)) != nil) { + d.dtype = "sd"; + d.part = name[len d.prefix:]; + return opensd(d); + } + + # assume we have just a normal file + d.dtype = "file"; + return openfile(d); +} + +mkwidth(d: ref Disk): ref Disk +{ + d.width = len sys->sprint("%bd", d.size); + return d; +} + +isdigit(c: int): int +{ + return c >= '0' && c <= '9'; +} + +putchs(d: ref Disk, p: array of byte, lba: big) +{ + s := int (lba % big d.s); + h := int (lba / big d.s % big d.h); + c := int (lba / (big d.s * big d.h)); + + if(c >= 1024) { + c = 1023; + h = d.h - 1; + s = d.s - 1; + } + + p[0] = byte h; + p[1] = byte (((s+1) & 16r3F) | ((c>>2) & 16rC0)); + p[2] = byte c; +} + +PCpart.bytes(p: self PCpart, d: ref Disk): array of byte +{ + a := array[TentrySize] of byte; + a[Oactive] = byte p.active; + a[Otype] = byte p.ptype; + putchs(d, a[Ostarth:], p.base+p.offset); + putchs(d, a[Oendh:], p.base+p.offset+p.size-big 1); + putle32(a[Oxlba:], p.offset); + putle32(a[Oxsize:], p.size); + return a; +} + +PCpart.extract(a: array of byte, nil: ref Disk): PCpart +{ + p: PCpart; + p.active = int a[Oactive]; + p.ptype = int a[Otype]; + p.base = big 0; + p.offset = getle32(a[Oxlba:]); + p.size = getle32(a[Oxsize:]); + return p; +} + +getle32(p: array of byte): big +{ + return (big p[3]<<24) | (big p[2]<<16) | (big p[1] << 8) | big p[0]; +} + +putle32(p: array of byte, i: big) +{ + p[0] = byte i; + p[1] = byte (i>>8); + p[2] = byte (i>>16); + p[3] = byte (i>>24); +} + +Disk.readn(d: self ref Disk, buf: array of byte, nb: int): int +{ + return readn(d.fd, buf, nb); +} + +chstext(p: array of byte): string +{ + h := int p[0]; + c := int p[2]; + c |= (int p[1]&16rC0)<<2; + s := (int p[1] & 16r3F); + return sys->sprint("%d/%d/%d", c, h, s); +} + +readn(fd: ref Sys->FD, buf: array of byte, nb: int): int +{ + for(nr := 0; nr < nb;){ + n := sys->read(fd, buf[nr:], nb-nr); + if(n <= 0){ + if(nr == 0) + return n; + break; + } + nr += n; + } + return nr; +} |
