Plan 9 from Bell Labs’s /usr/web/sources/contrib/quanstro/root/sys/src/fs/dev/part.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include	"all.h"
#include	"mem.h"
#include	"io.h"
#include "../pc/dosfs.h"

#define dprint(...)	if(pdebug)print(__VA_ARGS__)

enum{
	Npart	= 8,
	Ntab	= 8,
	Sblank	= 0,
	Eblank	= 0,
};

typedef struct{
	char	type;
	uchar	rem;			/* unaligned partition */
	Devsize	start;
	Devsize	end;
	char	name[NAMELEN];
}Part;

typedef struct{
	char	devstr[NAMELEN];
	int	n;
	Part	tab[Npart];
}Tab;

static Tab	*tab;
static int		ntab;
static int		pdebug = 0;

static void
initpart(void)
{
	static int done;

	if(done++)
		return;
	tab = ialloc(Ntab*sizeof *tab, 0);
}

/*
 * each Device has one partition table, even if d->dno is different.
 */

Tab*
devtotab(Device *d, int *new)
{
	char *s, buf[NAMELEN];
	int i;
	Tab *t;

	initpart();
	snprint(buf, sizeof buf, "%Z", d);
	for(i = 0; i < Ntab; i++){
		t = tab+i;
		s = t->devstr;
		if(*s == 0){
			memmove(s, buf, sizeof buf);
			*new = 1;
			return t;
		}else if(!strcmp(buf, s))
			return t;
	}
	panic("too many partitioned devices");
	return 0;
}

Devsize
sectooff(Device *d, uvlong sec)
{
	return (sec*devsecsize(d))/RBUFSIZE;
}

uvlong
offtosec(Device *d, Off off)
{
	return ((uvlong)off*RBUFSIZE)/devsecsize(d);
}

Part*
addpart(Device *parent, Device *d, char *s, uvlong a, uvlong b, int sec)
{
	uint sperrb;
	Tab *t;
	Part *p;

	dprint("  %Z %s [%lld, %lld) -> ", d, s, a, b);
	t = parent->private;
	if(t->n+1 == Npart){
		print("too many partitions; part %s %lld %lld dropped\n", s, a, b);
		return t->tab+t->n;
	}
	p = t->tab+t->n++;
	sperrb = offtosec(d, 1);
	if(sperrb > 0)
		p->rem = a%sperrb;
	else
		p->rem = 0;
	if(sec){
		p->type = 's';
		p->start = sectooff(d, a+sperrb-1)+Sblank;		/* round up */
		p->end = sectooff(d, b&~(sperrb-1))-Eblank;	/* round down */
	}else{
		p->type = 'o';
		p->start = a;
		p->end = b;
	}
	if(p->end < p->start)
		print("bad partition %s %lld not < %lld\n", s, p->start, p->end);
	strncpy(p->name, s, NAMELEN);
	dprint("[%lld, %lld)\n", p->start, p->end);
	return p;
}

/*
 * read raw disk; only used for groking partition tables & reading
 * fat during configuration.  very inefficient.
 */
static int
tailio(Device *d, int write, uvlong byte, ulong l, void *buf)
{
	int r;
	ulong rem;
	Devsize off;
	Msgbuf *t;

	t = mballoc(RBUFSIZE, 0, Mxxx);
	rem = byte&(RBUFSIZE-1);
	off = byte/RBUFSIZE;

//	print("tailio(%Z, %c, byte %llud [b %llud], %lud)\n", d, "rw"[write], byte, off, rem);

	r = devread(d, off, t->data);
	if(!r && write){
		memmove(t->data+rem, buf, l);
		r = devwrite(d, off, t->data);
	}else if(!r)
		memmove(buf, t->data+rem, l);
	mbfree(t);
	return r;
}

Off
byteio0(Device *d, int write, uvlong byte, ulong l, void *vbuf)
{
	uchar *buf;
	ulong rem, l0;
	int (*io)(Device*, Off, void*);

	l0 = l;
	buf = vbuf;
	io = write? devwrite: devread;
	rem = RBUFSIZE - (byte%RBUFSIZE);
	if(rem){
		if(rem > l)
			rem = l;
		if(tailio(d, write, byte, rem, buf))
			goto done;
		byte += rem;
		buf += rem;
		l -= rem;
	}
	while(l >= RBUFSIZE){
		if(io(d, byte/RBUFSIZE, buf))
			goto done;
		byte += RBUFSIZE;
		buf += RBUFSIZE;
		l -= RBUFSIZE;
	}
	if(l){
		if(byte%RBUFSIZE != 0)
			panic("byte%%rbufsize");
		if(tailio(d, write, byte, l, buf))
			goto done;
		byte += l;
//		buf += l;
		l -= l;
	}
done:
	return l0-l;
}

int
secio(Device *d, int write, uvlong sec, void *buf)
{
	return byteio0(d, write, sec*512, 512, buf) != 512;
}


/*
 * deal with dos partitions placed on odd-sized boundaries.
 * to further our misfortune, byteio0 can't deal with negative
 * offsets.
 */
static Part *findpart(Device*, char*);
Off
byteio(Device *d, int write, uvlong byte, ulong l, void *vbuf)
{
	uvlong rem;
	Part *p;

	if(d->type == Devpart){
		p = findpart(d, d->part.name);
		rem = p->rem*devsecsize(d);
		if(rem)
			byte -= RBUFSIZE-rem;
		byte -= Sblank*RBUFSIZE;
		byte += d->part.base*RBUFSIZE;
		d = d->part.d;
	}
	return byteio0(d, write, byte, l, vbuf);
}

int
mbrread(Device *d, uvlong sec, void *buf)
{
	uchar *u;

	if(byteio(d, 0, sec*512, 512, buf) != 512)
		return 1;
	u = buf;
	if(u[0x1fe] != 0x55 || u[0x1ff] != 0xaa)
		return 1;
	return 0;
}

static int
p9part(Device *parent, Device *d, uvlong sec, char *buf)
{
	char *field[4], *line[Npart+1];
	uvlong start, end;
	int i, n;

	if(secio(d, 0, sec+1, buf))
		return 1;
	buf[512-1] = '\0';
	if(strncmp(buf, "part ", 5))
		return 1;

	n = getfields(buf, line, Npart+1, 1, "\n");
	dprint("p9part %d lines..", n);
	if(n == 0)
		return -1;
	for(i = 0; i < n; i++){
		if(strncmp(line[i], "part ", 5) != 0)
			break;
		if(getfields(line[i], field, 4, 0, " ") != 4)
			break;
		start = strtoull(field[2], 0, 0);
		end = strtoull(field[3], 0, 0);
		if(start >= end || end > offtosec(d, d->size))
			break;
		addpart(parent, d, field[1], sec+start, sec+end, 1);
	}
	return 0;
}

int
isdos(int t)
{
	return t==FAT12 || t==FAT16 || t==FATHUGE || t==FAT32 || t==FAT32X;
}

int
isextend(int t)
{
	return t==EXTEND || t==EXTHUGE || t==LEXTEND;
}

static int
mbrpart(Device *parent, Device *d, char *mbrbuf, char *partbuf)
{
	char name[10];
	int ndos, i, nplan9;
	ulong sec, start, end;
	ulong firstx, nextx, npart;
	Dospart *dp;
	int (*repart)(Device*, Device*, uvlong, char*);

	sec = 0;
	dp = (Dospart*)&mbrbuf[0x1be];

	/* get the MBR (allowing for DMDDO) */
	if(mbrread(d, sec, mbrbuf))
		return 1;
	for(i=0; i<4; i++)
		if(dp[i].type == DMDDO) {
			dprint("DMDDO %d\n", i);
			sec = 63;
			if(mbrread(d, sec, mbrbuf))
				return 1;
			i = -1;	/* start over */
		}
	/*
	 * Read the partitions, first from the MBR and then
	 * from successive extended partition tables.
	 */
	nplan9 = 0;
	ndos = 0;
	firstx = 0;
	for(npart=0;; npart++) {
		if(mbrread(d, sec, mbrbuf))
			return 1;
		if(firstx)
			print("%Z ext %lud ", d, sec);
		else
			print("%Z mbr ", d);
		nextx = 0;
		for(i=0; i<4; i++) {
			start = sec+GLONG(dp[i].start);
			end = start+GLONG(dp[i].len);
			if(dp[i].type == 0 && start == 0 && end == 0)
				continue;
			dprint("type %x [%ld, %ld)", dp[i].type, start, end);
			repart = 0;
			if(dp[i].type == PLAN9) {
				if(nplan9 == 0)
					strcpy(name, "plan9");
				else
					sprint(name, "plan9.%d", nplan9);
				repart = p9part;
				nplan9++;
			}else if(!ndos && isdos(dp[i].type)){
				ndos = 1;
				strcpy(name, "dos");
			}else
				snprint(name, sizeof name, "%ld", npart);
			if(end != 0){
				dprint(" %s..", name);
				addpart(parent, d, name, start, end, 1);
			}
			if(repart)
				repart(parent, d, start, partbuf);
			
			/* nextx is relative to firstx (or 0), not sec */
			if(isextend(dp[i].type)){
				nextx = start-sec+firstx;
				dprint("link %lud...", nextx);
			}
		}
		dprint("\n");
		if(!nextx)
			break;
		if(!firstx)
			firstx = nextx;
		sec = nextx;
	}	
	return 0;
}

static int
guessparttab(Tab *t)
{
	int i, c;


	for(i = 0; i < t->n; i++){
		c = t->tab[i].type;
		if(c == 's' || c == 'o')
			return 1;
	}
	return 0;
}

static Part*
findpart(Device *d, char *s)
{
	char c;
	int i;
	uvlong l, start, end;
	Part *p;
	Tab *t;

	t = d->private;
	if(s == 0)
		goto mkpart;
	for(i = 0; i < t->n; i++)
		if(!strcmp(t->tab[i].name, s))
			return t->tab+i;
	panic("part %Z not found", d);
mkpart:
	if(guessparttab(t))
		print("warning: ignoring part table on %Z\n", d->part.d);
	if(d->part.base < 101 && d->part.size < 101){
		c = '%';
		l = d->part.d->size / 100;
		start = d->part.base*l;
		end = start + d->part.size*l;
	}else{
		c = 'b';
		start = d->part.base;
		end = d->part.size;
	}
	for(i = 0; i < t->n; i++){
		p = t->tab+i;
		if(start == p->start)
		if(end == p->end)
			return p;
	}
	p = addpart(d, d->part.d, "", start, end, 0);
	if(c)
		p->type = c;
	snprint(p->name, sizeof p->name, "f%ld%ld", t-tab, p-t->tab);	// BOTCH
	return p;
}

void
partition(Device *parent, Device *d)
{
	char *m, *p;
	int new;
	Msgbuf *mbr, *part;
	Part *q;

	new = 0;
	parent->private = devtotab(d, &new);
	if(new){
		mbr = mballoc(RBUFSIZE, 0, Mxxx);
		part = mballoc(RBUFSIZE, 0, Mxxx);
		m = (char*)mbr->data;
		p = (char*)part->data;
		!mbrpart(parent, d, m, p) || p9part(parent, d, 0, p);
		mbfree(mbr);
		mbfree(part);
	}
	q = findpart(parent, parent->part.name);
	parent->part.base = q->start;
	parent->part.size = q->end-q->start;
}

void
cmd_part(int argc, char **argv)
{
	int i, j;
	Part *p;
	Tab *t;

	if(argc == 1 && !strcmp(*argv, "trace")){
		pdebug ^= 1;
		return;
	}
	for(i = 0; i < Ntab; i++){
		t = tab+i;
		if(*t->devstr == 0)
			continue;
		print("%d %s\n", i, t->devstr);
		for(j = 0; j < Npart; j++){
			p = t->tab+j;
			if(*p->name == 0 && p->start == 0 && p->end == 0)
				continue;
			print("  %c\t%s\t%llud\t%llud r %d\n", p->type, p->name, p->start, p->end, p->rem);
		}
	}
}

void
partinit(Device *d)
{
	static int once;

	if(once++ == 0)
		cmd_install("part", "-- partition info", cmd_part);
	devinit(d->part.d);
	d->part.d->size = devsize(d->part.d);
	partition(d, d->part.d);
}

Devsize
partsize(Device *d)
{
	return d->part.size;
}

int
partread(Device *d, Off b, void *c)
{
	if(b < d->part.size)
		return devread(d->part.d, d->part.base+b, c);
	print("partread %llud %llud\n", (Wideoff)b, d->part.size);
	return 1;
}

int
partwrite(Device *d, Off b, void *c)
{
	if(b < d->part.size)
		return devwrite(d->part.d, d->part.base+b, c);
	print("partwrite %llud %llud\n", (Wideoff)b, d->part.size);
	return 1;
}

void
partream(Device *d, int)
{
	devream(d->part.d, 0);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.