#include "stdinc.h"
#include "dat.h"
#include "fns.h"

u32int	maxBlockSize;
int	readonly;

Part*
initPart(char *name, int writable)
{
	Part *part;
	Dir *dir;
	int how;

	part = MK(Part);
	part->name = estrdup(name);
	if(!writable && readonly)
		how = OREAD;
	else
		how = ORDWR;
	part->fd = open(name, how);
	if(part->fd < 0){
		if(how == ORDWR)
			part->fd = open(name, OREAD);
		if(part->fd < 0){
			freePart(part);
			setErr(EOk, "can't open partition='%s': %r", name);
			return nil;
		}
		fprint(2, "warning: %s opened for reading only\n", name);
	}
	dir = dirfstat(part->fd);
	if(dir == nil){
		freePart(part);
		setErr(EOk, "can't stat partition='%s': %r", name);
		return nil;
	}
	part->size = dir->length;
	part->blockSize = 0;
	free(dir);
	return part;
}

void
freePart(Part *part)
{
	if(part == nil)
		return;
	close(part->fd);
	free(part->name);
	free(part);
}

void
partBlockSize(Part *part, u32int blockSize)
{
	if(part->blockSize)
		fatal("resetting partition=%s's block size", part->name);
	part->blockSize = blockSize;
	if(blockSize > maxBlockSize)
		maxBlockSize = blockSize;
}

int
writePart(Part *part, u64int addr, u8int *buf, u32int n)
{
	long m, mm, nn;

	vtLock(stats.lock);
	stats.diskWrites++;
	stats.diskBWrites += n;
	vtUnlock(stats.lock);

	if(addr > part->size || addr + n > part->size){
		setErr(ECorrupt, "out of bounds write to partition='%s'", part->name);
		return 0;
	}
	for(nn = 0; nn < n; nn += m){
		mm = n - nn;
		if(mm > MaxIo)
			mm = MaxIo;
		m = pwrite(part->fd, &buf[nn], mm, addr + nn);
		if(m != mm){
			if(m < 0){
				setErr(EOk, "can't write partition='%s': %r", part->name);
				return 0;
			}
			logErr(EOk, "truncated write to partition='%s' n=%ld wrote=%ld", part->name, mm, m);
		}
	}
	return 1;
}

int
readPart(Part *part, u64int addr, u8int *buf, u32int n)
{
	long m, mm, nn;
	int i;

	vtLock(stats.lock);
	stats.diskReads++;
	stats.diskBReads += n;
	vtUnlock(stats.lock);

	if(addr > part->size || addr + n > part->size){
		setErr(ECorrupt, "out of bounds read from partition='%s': addr=%lld n=%d size=%lld", part->name, addr, n, part->size);
		return 0;
	}
	for(nn = 0; nn < n; nn += m){
		mm = n - nn;
		if(mm > MaxIo)
			mm = MaxIo;
		m = -1;
		for(i=0; i<4; i++) {
			m = pread(part->fd, &buf[nn], mm, addr + nn);
			if(m == mm)
				break;
		}
		if(m != mm){
			if(m < 0){
				setErr(EOk, "can't read partition='%s': %r", part->name);
				return 0;
			}
			logErr(EOk, "warning: truncated read from partition='%s' n=%ld read=%ld", part->name, mm, m);
		}
	}
	return 1;
}
