ref: ca37b188d2da25bf88f28a79c84befb1b3015f5f
parent: 1cd8b4c9b41689f9a74d24bbf5e582b1f1f1a6ce
author: ment <[email protected]>
date: Wed Apr 27 19:20:54 EDT 2011
9boot: added support for fragmented files and FAT32
--- a/sys/src/boot/pc/fat.c
+++ b/sys/src/boot/pc/fat.c
@@ -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 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 Extend
+{
+ Fat *fat;
ulong lba;
+ ulong clust;
+ ulong lbaoff;
ulong len;
uchar *rp;
uchar *ep;
@@ -53,12 +75,34 @@
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 @@
{
}
+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,44 +164,23 @@
}
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
dirname(Dir *d, char buf[Maxpath])
{
@@ -155,18 +211,27 @@
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,13 +243,13 @@
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;
}
@@ -191,13 +256,69 @@
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;
uchar bchs[3];
@@ -206,21 +327,23 @@
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;
}
return -1;
@@ -231,12 +354,14 @@
{
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();