]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/boot/pc/fat.c
remove 9bootfat print
[plan9front.git] / sys / src / boot / pc / fat.c
index 285a0859420c83889fe9d75d0220130102d674d0..d313c6f0f27fadc9b48aab17f16ad0bc182d8375 100644 (file)
@@ -1,20 +1,42 @@
 #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 Extend Extend;
+typedef struct File File;
 typedef struct Dir Dir;
 typedef struct Pbs Pbs;
+typedef struct Fat Fat;
 
-struct Extend
+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;
@@ -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,61 +112,59 @@ unload(void)
 {
 }
 
+static ulong
+readnext(File *fp, ulong clust)
+{
+       Fat *fat = fp->fat;
+       uint b = fat->ver;
+       ulong sect, off;
+       
+       sect = clust * b / Sectsz;
+       off = clust * b % Sectsz;
+       if(readsect(fat->drive, fat->fatlba + sect, fp->buf))
+               memset(fp->buf, 0xff, 4);
+       switch(fat->ver){
+       case Fat16:
+               return GETSHORT(&fp->buf[off]);
+       case Fat32:
+               return GETLONG(&fp->buf[off])& 0x0fffffff;
+       }
+       return 0;
+}
+
 int
 read(void *f, void *data, int len)
 {
-       Extend *ex = f;
+       File *fp = f;
+       Fat *fat = fp->fat;
 
-       if(ex->len > 0 && ex->rp >= ex->ep)
-               if(readsect(ex->drive, ex->lba++, ex->rp = ex->buf))
+       if(fp->len > 0 && fp->rp >= fp->ep){
+               if(fp->clust != ~0U){
+                       if(fp->lbaoff % fat->clustsize == 0){
+                               if((fp->clust >> 4) == 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(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;
+       }
+       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 *f)
+close(void *)
 {
-       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
@@ -155,20 +197,40 @@ dirname(Dir *d, char buf[Maxpath])
        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(Extend *ex, Extend *fat, char *path)
+fatwalk(File *fp, 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){
+               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(ex, &d, Dirsz) != Dirsz)
+               if(readn(fp, &d, Dirsz) != Dirsz)
                        break;
                if((i = dirname(&d, name)) <= 0)
                        continue;
@@ -178,25 +240,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);
-                       ex->len = *((ulong*)d.len);
+                       fileinit(fp, fat, 0);
+                       fp->clust = dirclust(&d);
+                       fp->len = *((ulong*)d.len);
                        if(*end == 0)
                                return 0;
                        else if(d.attr & 0x10){
-                               ex->len = pbs->clustsize * Sectsz;
+                               fp->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 +324,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,7 +351,8 @@ start(void *sp)
 {
        char path[Maxpath], *kern;
        int drive;
-       Extend fat, ex;
+       File fi;
+       Fat fat;
        void *f;
 
        /* drive passed in DL */
@@ -241,17 +362,17 @@ start(void *sp)
                print("no fat\r\n");
                halt();
        }
-       if(fatwalk(f = &ex, &fat, "plan9.ini")){
+       if(fatwalk(f = &fi, &fat, "plan9.ini")){
                print("no config\r\n");
                f = 0;
        }
        for(;;){
                kern = configure(f, path); f = 0;
-               if(fatwalk(&ex, &fat, kern)){
+               if(fatwalk(&fi, &fat, kern)){
                        print("not found\r\n");
                        continue;
                }
-               print(bootkern(&ex));
+               print(bootkern(&fi));
                print(crnl);
        }
 }