git: 9front

ref: 2f4e6dd1a8b367044948f75f62b51aae2a944dd2
dir: /sys/src/boot/pc/fat.c/

View raw version
#include <u.h>
#include "fns.h"

enum {
	Sectsz = 0x200,
	Dirsz = 0x20,
	Maxpath = 64,
};

typedef struct Extend Extend;
typedef struct Dir Dir;
typedef struct Pbs Pbs;

struct Extend
{
	int drive;
	ulong lba;
	ulong len;
	uchar *rp;
	uchar *ep;
	uchar buf[Sectsz];
};

struct Dir
{
	char name[11];
	uchar attr;
	uchar reserved;
	uchar ctime;
	uchar ctime[2];
	uchar cdate[2];
	uchar adate[2];
	uchar starthi[2];
	uchar mtime[2];
	uchar mdate[2];
	uchar startlo[2];
	uchar len[4];
};

struct Pbs
{
	uchar magic[3];
	uchar version[8];
	uchar sectsize[2];
	uchar clustsize;
	uchar nreserv[2];
	uchar nfats;
	uchar rootsize[2];
	uchar volsize[2];
	uchar mediadesc;
	uchar fatsize[2];
	uchar trksize[2];
	uchar nheads[2];
	uchar nhidden[4];
	uchar bigvolsize[4];
	uchar driveno;
	uchar reserved0;
	uchar bootsig;
	uchar volid[4];
	uchar label[11];
	uchar type[8];
};

int readsect(ulong drive, ulong lba, void *buf);

int
read(void *f, void *data, int len)
{
	Extend *ex = f;

	if(ex->len > 0 && ex->rp >= ex->ep)
		if(readsect(ex->drive, ex->lba++, ex->rp = ex->buf))
			return -1;
	if(ex->len < len)
		len = ex->len;
	if(len > (ex->ep - ex->rp))
		len = ex->ep - ex->rp;
	memmove(data, ex->rp, len);
	ex->rp += len;
	ex->len -= len;
	return len;
}

void
close(void *f)
{
	Extend *ex = f;

	ex->drive = 0;
	ex->lba = 0;
	ex->len = 0;
	ex->rp = ex->ep = ex->buf + Sectsz;
}

static ulong
rootlba(Extend *fat)
{
	ulong lba;
	Pbs *p = (Pbs*)fat->buf;

	lba = fat->lba;
	lba += *((ushort*)p->nreserv);
	lba += *((ushort*)p->fatsize) * p->nfats;
	return lba;
}

static ulong
dirlba(Extend *fat, Dir *d)
{
	ulong clust;
	ulong dirs;
	ulong lba;
	Pbs *p = (Pbs*)fat->buf;

	lba = rootlba(fat);
	dirs = *((ushort*)p->rootsize);
	lba += (dirs * Dirsz + Sectsz-1) / Sectsz;
	clust = *((ushort*)d->starthi)<<16 | *((ushort*)d->startlo);
	lba += (clust - 2) * p->clustsize;
	return lba;
}

static int
dirname(Dir *d, char buf[Maxpath])
{
	char c, *x;

	if(d->attr == 0x0F || *d->name <= 0)
		return -1;
	memmove(buf, d->name, 8);
	x = buf+8;
	while(x > buf && x[-1] == ' ')
		x--;
	if(d->name[8] != ' '){
		*x++ = '.';
		memmove(x, d->name+8, 3);
		x += 3;
	}
	while(x > buf && x[-1] == ' ')
		x--;
	*x = 0;
	x = buf;
	while(c = *x){
		if(c >= 'A' && c <= 'Z'){
			c -= 'A';
			c += 'a';
		}
		*x++ = c;
	}
	return x - buf;
}

static int
fatwalk(Extend *ex, Extend *fat, char *path)
{
	char name[Maxpath], *end;
	Pbs *pbs = (Pbs*)fat->buf;
	int i, j;
	Dir d;

	close(ex);
	ex->drive = fat->drive;
	ex->lba = rootlba(fat);
	ex->len = *((ushort*)pbs->rootsize) * Dirsz;
	for(;;){
		if(readn(ex, &d, Dirsz) != Dirsz)
			break;
		if((i = dirname(&d, name)) <= 0)
			continue;
		while(*path == '/')
			path++;
		if((end = strchr(path, '/')) == 0)
			end = path + strlen(path);
		j = end - path;
		if(i == j && memcmp(name, path, j) == 0){
			ex->rp = ex->ep;
			ex->lba = dirlba(fat, &d);
			ex->len = *((ulong*)d.len);
			if(*end == 0)
				return 0;
			else if(d.attr & 0x10){
				ex->len = pbs->clustsize * Sectsz;
				path = end;
				continue;
			}
			break;
		}
	}
	close(ex);
	return -1;
}

static int
findfat(Extend *fat, int drive)
{
	struct {
		uchar status;
		uchar bchs[3];
		uchar typ;
		uchar echs[3];
		uchar lba[4];
		uchar len[4];
	} *p;
	int i;

	if(readsect(drive, 0, fat->buf))
		return -1;
	if(fat->buf[0x1fe] != 0x55 || fat->buf[0x1ff] != 0xAA)
		return -1;
	p = (void*)&fat->buf[0x1be];
	for(i=0; i<4; i++){
		if(p[i].status != 0x80)
			continue;
		close(fat);
		fat->drive = drive;
		fat->lba = *((ulong*)p[i].lba);
		if(readsect(drive, fat->lba, fat->buf))
			continue;
		return 0;
	}
	return -1;
}

void
start(void *sp)
{
	char path[Maxpath], *kern;
	int drive;
	Extend fat, ex;
	void *f;

	/* drive passed in DL */
	drive = ((ushort*)sp)[5] & 0xFF;

	if(findfat(&fat, drive)){
		print("no fat\r\n");
		halt();
	}
	if(fatwalk(f = &ex, &fat, "plan9.ini")){
		print("no config\r\n");
		f = 0;
	}
	for(;;){
		kern = configure(f, path); f = 0;
		if(fatwalk(&ex, &fat, kern)){
			print("not found\r\n");
			continue;
		}
		print(bootkern(&ex));
		print(crnl);
	}
}