code: purgatorio

ref: f4624471579e0cfb531ffc733cdbe007ecdfed9d
dir: /appl/lib/diskblocks.b/

View raw version
implement Diskblocks;

#
# adapted from Acme's disk.b
#

include "sys.m";
	sys: Sys;

include "diskblocks.m";

init()
{
	sys = load Sys Sys->PATH;
}

tempfile(): ref Sys->FD
{
	user := "inferno";
	fd := sys->open("/dev/user", Sys->OREAD);
	if(fd != nil){
		b := array[Sys->NAMEMAX] of byte;
		n := sys->read(fd, b, len b);
		if(n > 0)
			user = string b[0:n];
	}
	fd = nil;
	buf := sys->sprint("/tmp/X%d.%.4sblks", sys->pctl(0, nil), user);
	for(i:='A'; i<='Z'; i++){
		buf[5] = i;
		if(sys->stat(buf).t0 == 0)
			continue;
		fd = sys->create(buf, Sys->ORDWR|Sys->ORCLOSE|Sys->OEXCL, 8r600);
		if(fd != nil)
			return fd;
	}
	return nil;
}

Disk.init(fd: ref Sys->FD, gran: int, maxblock: int): ref Disk
{
	d := ref Disk;
	if(gran == 0 || maxblock%gran != 0)
		return nil;
	d.maxblock = maxblock;
	d.gran = gran;
	d.free = array[maxblock/gran+1] of list of ref Block;
	d.addr = big 0;
	d.fd = fd;
	d.lock = chan[1] of int;
	return d;
}

ntosize(d: ref Disk, n: int): (int, int)
{
	if (n > d.maxblock)
		return (-1, -1);
	size := n;
	if((size % d.gran) != 0)
		size += d.gran - size%d.gran;
	# last bucket holds blocks of exactly d.maxblock
	return (size, size/d.gran);
}

Disk.new(d: self ref Disk, n: int): ref Block
{
	(size, i) := ntosize(d, n);
	if(i < 0){
		sys->werrstr("illegal Disk allocation");
		return nil;
	}
	b: ref Block;
	d.lock <-= 1;
	if(d.free[i] != nil){
		b = hd d.free[i];
		d.free[i] = tl d.free[i];
	}else{
		b = ref Block(d.addr, 0);
		d.addr += big size;
	}
	<-d.lock;
	b.n = n;
	return b;
}

Disk.release(d: self ref Disk, b: ref Block)
{
	(nil, i) := ntosize(d, b.n);
	d.lock <-= 1;
	d.free[i] = b :: d.free[i];
	<-d.lock;
}

Disk.write(d: self ref Disk, b: ref Block, a: array of byte, n: int): ref Block
{
	if(b != nil){
		(size, nil) := ntosize(d, b.n);
		(nsize, nil) := ntosize(d, n);
		if(size != nsize){
			d.release(b);
			b = d.new(n);
		}
	}else
		b = d.new(n);
	if(b == nil)
		return nil;
	if(sys->pwrite(d.fd, a, n, b.addr) != n){
		sys->werrstr(sys->sprint("Disk write error: %r"));
		return nil;
	}
	b.n = n;
	return b;
}

Disk.read(d: self ref Disk, b: ref Block, a: array of byte, n: int): int
{
	if(b == nil || n > b.n){
		sys->werrstr("read request bigger than block");
		return -1;
	}
	return sys->pread(d.fd, a, n, b.addr);
}