9boot: added support for fragmented files and FAT32

This commit is contained in:
ment 2011-04-27 23:20:54 +02:00
parent 1cd8b4c9b4
commit ca37b188d2

View file

@ -1,20 +1,42 @@
#include <u.h> #include <u.h>
#include "fns.h" #include "fns.h"
#define GETSHORT(p) (*(ushort *)(p))
#define GETLONG(p) (*(uint *)(p))
enum { enum {
Sectsz = 0x200, Sectsz = 0x200,
Dirsz = 0x20, Dirsz = 0x20,
Maxpath = 64, Maxpath = 64,
Fat12 = 1,
Fat16 = 2,
Fat32 = 4,
}; };
typedef struct Extend Extend; typedef struct Extend Extend;
typedef struct Dir Dir; typedef struct Dir Dir;
typedef struct Pbs Pbs; 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 struct Extend
{ {
int drive; Fat *fat;
ulong lba; ulong lba;
ulong clust;
ulong lbaoff;
ulong len; ulong len;
uchar *rp; uchar *rp;
uchar *ep; uchar *ep;
@ -53,12 +75,34 @@ struct Pbs
uchar nheads[2]; uchar nheads[2];
uchar nhidden[4]; uchar nhidden[4];
uchar bigvolsize[4]; uchar bigvolsize[4];
uchar driveno; union
uchar reserved0; {
uchar bootsig; struct
uchar volid[4]; {
uchar label[11]; uchar driveno;
uchar type[8]; 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); 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 int
read(void *f, void *data, int len) read(void *f, void *data, int len)
{ {
Extend *ex = f; Extend *ex = f;
Fat *fat = ex->fat;
if(ex->len > 0 && ex->rp >= ex->ep) if(ex->len > 0 && ex->rp >= ex->ep){
if(readsect(ex->drive, ex->lba++, ex->rp = ex->buf)) 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; return -1;
}
if(ex->len < len) if(ex->len < len)
len = ex->len; len = ex->len;
if(len > (ex->ep - ex->rp)) if(len > (ex->ep - ex->rp))
@ -87,42 +164,21 @@ read(void *f, void *data, int len)
} }
void void
close(void *f) open(Fat *fat, void *f, ulong lba)
{ {
Extend *ex = f; Extend *ex = f;
ex->drive = 0; ex->fat = fat;
ex->lba = 0; ex->lba = lba;
ex->len = 0; ex->len = 0;
ex->lbaoff = 0;
ex->clust = ~0U;
ex->rp = ex->ep = ex->buf + Sectsz; ex->rp = ex->ep = ex->buf + Sectsz;
} }
static ulong void
rootlba(Extend *fat) 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 static int
@ -155,18 +211,27 @@ dirname(Dir *d, char buf[Maxpath])
return x - buf; return x - buf;
} }
static ulong
dirclust(Dir *d)
{
return *((ushort*)d->starthi)<<16 | *((ushort*)d->startlo);
}
static int static int
fatwalk(Extend *ex, Extend *fat, char *path) fatwalk(Extend *ex, Fat *fat, char *path)
{ {
char name[Maxpath], *end; char name[Maxpath], *end;
Pbs *pbs = (Pbs*)fat->buf;
int i, j; int i, j;
Dir d; Dir d;
close(ex); if(fat->ver == Fat32){
ex->drive = fat->drive; open(fat, ex, 0);
ex->lba = rootlba(fat); ex->clust = fat->dirstart;
ex->len = *((ushort*)pbs->rootsize) * Dirsz; ex->len = ~0U;
}else{
open(fat, ex, fat->dirstart);
ex->len = fat->dirents * Dirsz;
}
for(;;){ for(;;){
if(readn(ex, &d, Dirsz) != Dirsz) if(readn(ex, &d, Dirsz) != Dirsz)
break; break;
@ -178,25 +243,81 @@ fatwalk(Extend *ex, Extend *fat, char *path)
end = path + strlen(path); end = path + strlen(path);
j = end - path; j = end - path;
if(i == j && memcmp(name, path, j) == 0){ if(i == j && memcmp(name, path, j) == 0){
ex->rp = ex->ep; open(fat, ex, 0);
ex->lba = dirlba(fat, &d); ex->clust = dirclust(&d);
ex->len = *((ulong*)d.len); ex->len = *((ulong*)d.len);
if(*end == 0) if(*end == 0)
return 0; return 0;
else if(d.attr & 0x10){ else if(d.attr & 0x10){
ex->len = pbs->clustsize * Sectsz; ex->len = fat->clustsize * Sectsz;
path = end; path = end;
continue; continue;
} }
break; break;
} }
} }
close(ex);
return -1; return -1;
} }
static int 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 { struct {
uchar status; uchar status;
@ -206,20 +327,22 @@ findfat(Extend *fat, int drive)
uchar lba[4]; uchar lba[4];
uchar len[4]; uchar len[4];
} *p; } *p;
uchar buf[Sectsz];
int i; int i;
if(readsect(drive, 0, fat->buf)) if(readsect(drive, 0, buf))
return -1; return -1;
if(fat->buf[0x1fe] != 0x55 || fat->buf[0x1ff] != 0xAA) if(buf[0x1fe] != 0x55 || buf[0x1ff] != 0xAA)
return -1; return -1;
p = (void*)&fat->buf[0x1be]; p = (void*)&buf[0x1be];
for(i=0; i<4; i++){ for(i=0; i<4; i++){
if(p[i].status != 0x80) if(p[i].status != 0x80)
continue; continue;
close(fat);
fat->drive = drive; fat->drive = drive;
fat->lba = *((ulong*)p[i].lba); fat->partlba = *((ulong*)p[i].lba);
if(readsect(drive, fat->lba, fat->buf)) if(readsect(drive, fat->partlba, buf))
continue;
if(conffat(fat, buf))
continue; continue;
return 0; return 0;
} }
@ -231,12 +354,14 @@ start(void *sp)
{ {
char path[Maxpath], *kern; char path[Maxpath], *kern;
int drive; int drive;
Extend fat, ex; Extend ex;
Fat fat;
void *f; void *f;
/* drive passed in DL */ /* drive passed in DL */
drive = ((ushort*)sp)[5] & 0xFF; drive = ((ushort*)sp)[5] & 0xFF;
print("9bootfat\r\n");
if(findfat(&fat, drive)){ if(findfat(&fat, drive)){
print("no fat\r\n"); print("no fat\r\n");
halt(); halt();