code: 9ferno

ref: 670f4e90ee6028ffee399e5a96fb5e4cd72fc7bc
dir: /appl/lib/disks.b/

View raw version
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
	&& sys->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
		|| sys->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 sys->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);
}