diff --git a/sys/src/boot/pc/fat.c b/sys/src/boot/pc/fat.c index 285a08594..a35d4b043 100644 --- a/sys/src/boot/pc/fat.c +++ b/sys/src/boot/pc/fat.c @@ -1,20 +1,42 @@ #include #include "fns.h" +#define GETSHORT(p) (*(ushort *)(p)) +#define GETLONG(p) (*(uint *)(p)) + enum { Sectsz = 0x200, Dirsz = 0x20, Maxpath = 64, + Fat12 = 1, + Fat16 = 2, + Fat32 = 4, }; typedef struct Extend Extend; typedef struct Dir Dir; typedef struct Pbs Pbs; +typedef struct Fat Fat; + +struct Fat +{ + ulong ver; + int drive; + ulong clustsize; + ulong eofmark; + ulong partlba; + ulong fatlba; + ulong dirstart; /* LBA for FAT16, cluster for FAT32 */ + ulong dirents; + ulong datalba; +}; struct Extend { - int drive; + Fat *fat; ulong lba; + ulong clust; + ulong lbaoff; ulong len; uchar *rp; uchar *ep; @@ -53,12 +75,34 @@ struct Pbs uchar nheads[2]; uchar nhidden[4]; uchar bigvolsize[4]; - uchar driveno; - uchar reserved0; - uchar bootsig; - uchar volid[4]; - uchar label[11]; - uchar type[8]; + union + { + struct + { + uchar driveno; + uchar reserved0; + uchar bootsig; + uchar volid[4]; + uchar label[11]; + uchar type[8]; + } fat16; + struct + { + uchar fatsize[4]; + uchar flags[2]; + uchar ver[2]; + uchar rootclust[4]; + uchar fsinfo[2]; + uchar bootbak[2]; + uchar reserved0[12]; + uchar driveno; + uchar reserved1; + uchar bootsig; + uchar volid[4]; + uchar label[11]; + uchar type[8]; + } fat32; + }; }; int readsect(ulong drive, ulong lba, void *buf); @@ -68,14 +112,47 @@ unload(void) { } +static ulong +readnext(Extend *ex, ulong clust) +{ + Fat *fat = ex->fat; + uint b = fat->ver; + ulong sect, off; + + sect = clust * b / Sectsz; + off = clust * b % Sectsz; + if(readsect(fat->drive, fat->fatlba + sect, ex->buf)) + memset(ex->buf, 0xff, 4); + switch(fat->ver){ + case Fat16: + return GETSHORT(&ex->buf[off]); + case Fat32: + return GETLONG(&ex->buf[off])& 0x0fffffff; + } + return 0; +} + int read(void *f, void *data, int len) { Extend *ex = f; + Fat *fat = ex->fat; - if(ex->len > 0 && ex->rp >= ex->ep) - if(readsect(ex->drive, ex->lba++, ex->rp = ex->buf)) + if(ex->len > 0 && ex->rp >= ex->ep){ + if(ex->clust != ~0U){ + if(ex->lbaoff % fat->clustsize == 0){ + if((ex->clust >> 4) == fat->eofmark) + return -1; + ex->lbaoff = (ex->clust - 2) * fat->clustsize; + putc('.'); + ex->clust = readnext(ex, ex->clust); + ex->lba = ex->lbaoff + fat->datalba; + } + ex->lbaoff++; + } + if(readsect(fat->drive, ex->lba++, ex->rp = ex->buf)) return -1; + } if(ex->len < len) len = ex->len; if(len > (ex->ep - ex->rp)) @@ -87,42 +164,21 @@ read(void *f, void *data, int len) } void -close(void *f) +open(Fat *fat, void *f, ulong lba) { Extend *ex = f; - ex->drive = 0; - ex->lba = 0; + ex->fat = fat; + ex->lba = lba; ex->len = 0; + ex->lbaoff = 0; + ex->clust = ~0U; ex->rp = ex->ep = ex->buf + Sectsz; } -static ulong -rootlba(Extend *fat) +void +close(void *) { - 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 @@ -155,18 +211,27 @@ dirname(Dir *d, char buf[Maxpath]) return x - buf; } +static ulong +dirclust(Dir *d) +{ + return *((ushort*)d->starthi)<<16 | *((ushort*)d->startlo); +} + static int -fatwalk(Extend *ex, Extend *fat, char *path) +fatwalk(Extend *ex, Fat *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; + if(fat->ver == Fat32){ + open(fat, ex, 0); + ex->clust = fat->dirstart; + ex->len = ~0U; + }else{ + open(fat, ex, fat->dirstart); + ex->len = fat->dirents * Dirsz; + } for(;;){ if(readn(ex, &d, Dirsz) != Dirsz) break; @@ -178,25 +243,81 @@ fatwalk(Extend *ex, Extend *fat, char *path) end = path + strlen(path); j = end - path; if(i == j && memcmp(name, path, j) == 0){ - ex->rp = ex->ep; - ex->lba = dirlba(fat, &d); + open(fat, ex, 0); + ex->clust = dirclust(&d); ex->len = *((ulong*)d.len); if(*end == 0) return 0; else if(d.attr & 0x10){ - ex->len = pbs->clustsize * Sectsz; + ex->len = fat->clustsize * Sectsz; path = end; continue; } break; } } - close(ex); return -1; } static int -findfat(Extend *fat, int drive) +conffat(Fat *fat, void *buf) +{ + Pbs *p = buf; + uint fatsize, volsize, datasize, reserved; + uint ver, dirsize, dirents, clusters; + + /* sanity check */ + if(GETSHORT(p->sectsize) != Sectsz){ + print("sectsize != 512\r\n"); + halt(); + } + + /* load values from fat */ + fatsize = GETSHORT(p->fatsize); + if(fatsize == 0) + fatsize = GETLONG(p->fat32.fatsize); + volsize = GETSHORT(p->volsize); + if(volsize == 0) + volsize = GETLONG(p->bigvolsize); + reserved = GETSHORT(p->nreserv); + dirents = GETSHORT(p->rootsize); + dirsize = (dirents * Dirsz + Sectsz - 1) / Sectsz; + datasize = volsize - (reserved + fatsize * p->nfats + dirsize); + clusters = datasize / p->clustsize; + + /* determine fat type */ + if(clusters < 4085) + ver = Fat12; + else if(clusters < 65525) + ver = Fat16; + else + ver = Fat32; + + /* another check */ + if(ver == Fat12){ + print("TODO: implement FAT12\r\n"); + halt(); + } + + /* fill FAT descriptor */ + fat->ver = ver; + fat->dirents = dirents; + fat->clustsize = p->clustsize; + fat->fatlba = fat->partlba + reserved; + fat->dirstart = fat->fatlba + fatsize * p->nfats; + if(ver == Fat32){ + fat->datalba = fat->dirstart; + fat->dirstart = GETLONG(p->fat32.rootclust); + fat->eofmark = 0xffffff; + }else{ + fat->datalba = fat->dirstart + dirsize; + fat->eofmark = 0xfff; + } + return 0; +} + +static int +findfat(Fat *fat, int drive) { struct { uchar status; @@ -206,20 +327,22 @@ findfat(Extend *fat, int drive) uchar lba[4]; uchar len[4]; } *p; + uchar buf[Sectsz]; int i; - if(readsect(drive, 0, fat->buf)) + if(readsect(drive, 0, buf)) return -1; - if(fat->buf[0x1fe] != 0x55 || fat->buf[0x1ff] != 0xAA) + if(buf[0x1fe] != 0x55 || buf[0x1ff] != 0xAA) return -1; - p = (void*)&fat->buf[0x1be]; + p = (void*)&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)) + fat->partlba = *((ulong*)p[i].lba); + if(readsect(drive, fat->partlba, buf)) + continue; + if(conffat(fat, buf)) continue; return 0; } @@ -231,12 +354,14 @@ start(void *sp) { char path[Maxpath], *kern; int drive; - Extend fat, ex; + Extend ex; + Fat fat; void *f; /* drive passed in DL */ drive = ((ushort*)sp)[5] & 0xFF; + print("9bootfat\r\n"); if(findfat(&fat, drive)){ print("no fat\r\n"); halt();