ref: dc0f1ae1028206a548cc5fad15631d79c212e48a
dir: /sys/src/boot/pc/fat.c/
#include <u.h> #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 File File; typedef struct Dir Dir; typedef struct Pbs Pbs; typedef struct Pbs32 Pbs32; 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 File { Fat *fat; ulong lba; ulong clust; ulong lbaoff; 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]; }; struct Pbs32 { uchar common[36]; 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]; }; int readsect(ulong drive, ulong lba, void *buf); void unload(void) { } static ulong readnext(File *fp, ulong clust) { Fat *fat = fp->fat; uchar tmp[2], *p; ulong idx, lba; if(fat->ver == Fat12) idx = (3*clust)/2; else idx = clust*fat->ver; lba = fat->fatlba + (idx / Sectsz); if(readsect(fat->drive, lba, fp->buf)) memset(fp->buf, 0xff, Sectsz); p = &fp->buf[idx % Sectsz]; if(p == &fp->buf[Sectsz-1]){ tmp[0] = *p; if(readsect(fat->drive, ++lba, fp->buf)) memset(fp->buf, 0xff, Sectsz); tmp[1] = fp->buf[0]; p = tmp; } if(fat->ver == Fat32) return GETLONG(p) & 0xfffffff; idx = GETSHORT(p); if(fat->ver == Fat12){ if(clust & 1) idx >>= 4; idx &= 0xfff; } return idx; } int read(void *f, void *data, int len) { File *fp = f; Fat *fat = fp->fat; if(fp->len > 0 && fp->rp >= fp->ep){ if(fp->clust != ~0U){ if(fp->lbaoff % fat->clustsize == 0){ if(fp->clust < 2 || fp->clust >= fat->eofmark) return -1; fp->lbaoff = (fp->clust - 2) * fat->clustsize; fp->clust = readnext(fp, fp->clust); fp->lba = fp->lbaoff + fat->datalba; } fp->lbaoff++; } if(readsect(fat->drive, fp->lba++, fp->rp = fp->buf)) return -1; } if(fp->len < len) len = fp->len; if(len > (fp->ep - fp->rp)) len = fp->ep - fp->rp; memmove(data, fp->rp, len); fp->rp += len; fp->len -= len; return len; } void close(void *) { } 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 ulong dirclust(Dir *d) { return *((ushort*)d->starthi)<<16 | *((ushort*)d->startlo); } static void fileinit(File *fp, Fat *fat, ulong lba) { fp->fat = fat; fp->lba = lba; fp->len = 0; fp->lbaoff = 0; fp->clust = ~0U; fp->rp = fp->ep = fp->buf + Sectsz; } static int fatwalk(File *fp, Fat *fat, char *path) { char name[Maxpath], *end; int i, j; Dir d; if(fat->ver == Fat32){ fileinit(fp, fat, 0); fp->clust = fat->dirstart; fp->len = ~0U; }else{ fileinit(fp, fat, fat->dirstart); fp->len = fat->dirents * Dirsz; } for(;;){ if(readn(fp, &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){ fileinit(fp, fat, 0); fp->clust = dirclust(&d); fp->len = GETLONG(d.len); if(*end == 0) return 0; else if(d.attr & 0x10){ fp->len = fat->clustsize * Sectsz; path = end; continue; } break; } } return -1; } static int conffat(Fat *fat, void *buf) { Pbs *p = buf; uint fatsize, volsize, datasize, reserved; uint ver, dirsize, dirents, clusters; if(GETSHORT(p->sectsize) != Sectsz) return -1; if(memcmp(p->type, "FAT", 3) && memcmp(((Pbs32*)buf)->type, "FAT", 3)) return -1; /* load values from fat */ ver = 0; fatsize = GETSHORT(p->fatsize); if(fatsize == 0){ fatsize = GETLONG(((Pbs32*)buf)->fatsize); ver = Fat32; } 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; if(ver != Fat32) if(clusters < 0xff7) ver = Fat12; else ver = Fat16; /* 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(((Pbs32*)buf)->rootclust); fat->eofmark = 0xffffff7; }else{ fat->datalba = fat->dirstart + dirsize; if(ver == Fat16) fat->eofmark = 0xfff7; else fat->eofmark = 0xff7; } return 0; } static int findfat(Fat *fat, int drive, ulong xbase, ulong lba) { struct { uchar status; uchar bchs[3]; uchar typ; uchar echs[3]; uchar lba[4]; uchar len[4]; } p[4]; uchar buf[Sectsz]; int i; if(xbase == 0) xbase = lba; if(readsect(drive, lba, buf)) return -1; if(buf[0x1fe] != 0x55 || buf[0x1ff] != 0xAA) return -1; if(lba == 0 && (drive & 0x80) == 0){ /* floppy */ fat->drive = drive; fat->partlba = 0; if(!conffat(fat, buf)) return 0; } memmove(p, &buf[0x1be], sizeof(p)); for(i=0; i<4; i++){ switch(p[i].typ){ case 0x05: case 0x0f: case 0x85: /* extended partitions */ if(!findfat(fat, drive, xbase, xbase + GETLONG(p[i].lba))) return 0; /* no break */ case 0x00: continue; default: if(p[i].status != 0x80) continue; case 0x39: /* always try plan9 partition */ fat->drive = drive; fat->partlba = lba + GETLONG(p[i].lba); if(readsect(drive, fat->partlba, buf)) continue; if(!conffat(fat, buf)) return 0; } } return -1; } void start(void *sp) { char path[Maxpath], *kern; int drive; File fi; Fat fat; void *f; /* drive passed in DL */ drive = ((ushort*)sp)[5] & 0xFF; if(findfat(&fat, drive, 0, 0)){ print("no fat\n"); halt(); } if(fatwalk(f = &fi, &fat, "plan9.ini")){ print("no config\n"); f = 0; } for(;;){ kern = configure(f, path); f = 0; if(fatwalk(&fi, &fat, kern)){ print("not found\n"); continue; } print(bootkern(&fi)); print("\n"); } }