123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914 |
- /*
- * a pity the code isn't also tiny...
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "../port/error.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- enum{
- Qdir,
- Qmedium,
- Maxfs= 10, /* max file systems */
- Blen= 48, /* block length */
- Nlen= 28, /* name length */
- Dlen= Blen - 4,
- Tagdir= 'd',
- Tagdata= 'D',
- Tagend= 'e',
- Tagfree= 'f',
- Notapin= 0xffff,
- Notabno= 0xffff,
- Fcreating= 1,
- Frmonclose= 2
- };
- /* representation of a Tdir on medium */
- typedef struct Mdir Mdir;
- struct Mdir {
- uchar type;
- uchar bno[2];
- uchar pin[2];
- char name[Nlen];
- char pad[Blen - Nlen - 6];
- uchar sum;
- };
- /* representation of a Tdata/Tend on medium */
- typedef struct Mdata Mdata;
- struct Mdata {
- uchar type;
- uchar bno[2];
- uchar data[Dlen];
- uchar sum;
- };
- typedef struct Tfile Tfile;
- struct Tfile {
- int r;
- char name[NAMELEN];
- ushort bno;
- ushort dbno;
- ushort pin;
- uchar flag;
- ulong length;
- /* hint to avoid egregious reading */
- ushort fbno;
- ulong finger;
- };
- typedef struct Tfs Tfs;
- struct Tfs {
- QLock ql;
- int r;
- Chan *c;
- uchar *map;
- int nblocks;
- Tfile *f;
- int nf;
- int fsize;
- };
- struct {
- Tfs fs[Maxfs];
- } tinyfs;
- #define GETS(x) ((x)[0]|((x)[1]<<8))
- #define PUTS(x, v) {(x)[0] = (v);(x)[1] = ((v)>>8);}
- #define GETL(x) (GETS(x)|(GETS(x+2)<<16))
- #define PUTL(x, v) {PUTS(x, v);PUTS(x+2, (v)>>16)};
- static uchar
- checksum(uchar *p)
- {
- uchar *e;
- uchar s;
- s = 0;
- for(e = p + Blen; p < e; p++)
- s += *p;
- return s;
- }
- static void
- mapclr(Tfs *fs, ulong bno)
- {
- fs->map[bno>>3] &= ~(1<<(bno&7));
- }
- static void
- mapset(Tfs *fs, ulong bno)
- {
- fs->map[bno>>3] |= 1<<(bno&7);
- }
- static int
- isalloced(Tfs *fs, ulong bno)
- {
- return fs->map[bno>>3] & (1<<(bno&7));
- }
- static int
- mapalloc(Tfs *fs)
- {
- int i, j, lim;
- uchar x;
- lim = (fs->nblocks + 8 - 1)/8;
- for(i = 0; i < lim; i++){
- x = fs->map[i];
- if(x == 0xff)
- continue;
- for(j = 0; j < 8; j++)
- if((x & (1<<j)) == 0){
- fs->map[i] = x|(1<<j);
- return i*8 + j;
- }
- }
- return Notabno;
- }
- static Mdir*
- validdir(Tfs *fs, uchar *p)
- {
- Mdir *md;
- ulong x;
- if(checksum(p) != 0)
- return 0;
- if(p[0] != Tagdir)
- return 0;
- md = (Mdir*)p;
- x = GETS(md->bno);
- if(x >= fs->nblocks)
- return 0;
- return md;
- }
- static Mdata*
- validdata(Tfs *fs, uchar *p, int *lenp)
- {
- Mdata *md;
- ulong x;
- if(checksum(p) != 0)
- return 0;
- md = (Mdata*)p;
- switch(md->type){
- case Tagdata:
- x = GETS(md->bno);
- if(x >= fs->nblocks)
- return 0;
- if(lenp)
- *lenp = Dlen;
- break;
- case Tagend:
- x = GETS(md->bno);
- if(x > Dlen)
- return 0;
- if(lenp)
- *lenp = x;
- break;
- default:
- return 0;
- }
- return md;
- }
- static Mdata*
- readdata(Tfs *fs, ulong bno, uchar *buf, int *lenp)
- {
- if(bno >= fs->nblocks)
- return 0;
- if(devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno) != Blen)
- error(Eio);
- return validdata(fs, buf, lenp);
- }
- static void
- writedata(Tfs *fs, ulong bno, ulong next, uchar *buf, int len, int last)
- {
- Mdata md;
- if(bno >= fs->nblocks)
- error(Eio);
- if(len > Dlen)
- len = Dlen;
- if(len < 0)
- error(Eio);
- memset(&md, 0, sizeof(md));
- if(last){
- md.type = Tagend;
- PUTS(md.bno, len);
- } else {
- md.type = Tagdata;
- PUTS(md.bno, next);
- }
- memmove(md.data, buf, len);
- md.sum = 0 - checksum((uchar*)&md);
-
- if(devtab[fs->c->type]->write(fs->c, &md, Blen, Blen*bno) != Blen)
- error(Eio);
- }
- static void
- writedir(Tfs *fs, Tfile *f)
- {
- Mdir *md;
- uchar buf[Blen];
- if(f->bno == Notabno)
- return;
- md = (Mdir*)buf;
- memset(buf, 0, Blen);
- md->type = Tagdir;
- strncpy(md->name, f->name, sizeof(md->name)-1);
- PUTS(md->bno, f->dbno);
- PUTS(md->pin, f->pin);
- md->sum = 0 - checksum(buf);
-
- if(devtab[fs->c->type]->write(fs->c, buf, Blen, Blen*f->bno) != Blen)
- error(Eio);
- }
- static void
- freeblocks(Tfs *fs, ulong bno, ulong bend)
- {
- uchar buf[Blen];
- Mdata *md;
- if(waserror())
- return;
- while(bno != bend && bno != Notabno){
- mapclr(fs, bno);
- if(devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno) != Blen)
- break;
- md = validdata(fs, buf, 0);
- if(md == 0)
- break;
- if(md->type == Tagend)
- break;
- bno = GETS(md->bno);
- }
- poperror();
- }
- static void
- freefile(Tfs *fs, Tfile *f, ulong bend)
- {
- uchar buf[Blen];
- /* remove blocks from map */
- freeblocks(fs, f->dbno, bend);
- /* change file type to free on medium */
- if(f->bno != Notabno){
- memset(buf, 0x55, Blen);
- devtab[fs->c->type]->write(fs->c, buf, Blen, Blen*f->bno);
- mapclr(fs, f->bno);
- }
- /* forget we ever knew about it */
- memset(f, 0, sizeof(*f));
- }
- static void
- expand(Tfs *fs)
- {
- Tfile *f;
- fs->fsize += 8;
- f = malloc(fs->fsize*sizeof(*f));
- if(fs->f){
- memmove(f, fs->f, fs->nf*sizeof(*f));
- free(fs->f);
- }
- fs->f = f;
- }
- static Tfile*
- newfile(Tfs *fs, char *name)
- {
- int i;
- volatile struct {
- Tfile *f;
- Tfs *fs;
- } rock;
- /* find free entry in file table */
- rock.f = 0;
- rock.fs = fs;
- for(;;) {
- for(i = 0; i < rock.fs->fsize; i++){
- rock.f = &rock.fs->f[i];
- if(rock.f->name[0] == 0){
- strncpy(rock.f->name, name, sizeof(rock.f->name)-1);
- break;
- }
- }
- if(i < rock.fs->fsize){
- if(i >= rock.fs->nf)
- rock.fs->nf = i+1;
- break;
- }
- expand(rock.fs);
- }
- rock.f->flag = Fcreating;
- rock.f->dbno = Notabno;
- rock.f->bno = mapalloc(rock.fs);
- rock.f->fbno = Notabno;
- rock.f->r = 1;
- rock.f->pin = Notapin; // what is a pin??
- /* write directory block */
- if(waserror()){
- freefile(rock.fs, rock.f, Notabno);
- nexterror();
- }
- if(rock.f->bno == Notabno)
- error("out of space");
- writedir(rock.fs, rock.f);
- poperror();
-
- return rock.f;
- }
- /*
- * Read the whole medium and build a file table and used
- * block bitmap. Inconsistent files are purged. The medium
- * had better be small or this could take a while.
- */
- static void
- tfsinit(Tfs *fs)
- {
- char dbuf[DIRLEN];
- Dir d;
- uchar buf[Blen];
- ulong x, bno;
- int n, done;
- Tfile *f;
- Mdir *mdir;
- Mdata *mdata;
- devtab[fs->c->type]->stat(fs->c, dbuf);
- convM2D(dbuf, &d);
- fs->nblocks = d.length/Blen;
- if(fs->nblocks < 3)
- error("tinyfs medium too small");
- /* bitmap for block usage */
- x = (fs->nblocks + 8 - 1)/8;
- fs->map = malloc(x);
- memset(fs->map, 0x0, x);
- for(bno = fs->nblocks; bno < x*8; bno++)
- mapset(fs, bno);
- /* find files */
- for(bno = 0; bno < fs->nblocks; bno++){
- n = devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno);
- if(n != Blen)
- break;
- mdir = validdir(fs, buf);
- if(mdir == 0)
- continue;
- if(fs->nf >= fs->fsize)
- expand(fs);
- f = &fs->f[fs->nf++];
- x = GETS(mdir->bno);
- mapset(fs, bno);
- strncpy(f->name, mdir->name, sizeof(f->name));
- f->pin = GETS(mdir->pin);
- f->bno = bno;
- f->dbno = x;
- f->fbno = Notabno;
- }
- /* follow files */
- for(f = fs->f; f < &(fs->f[fs->nf]); f++){
- bno = f->dbno;
- for(done = 0; !done;) {
- if(isalloced(fs, bno)){
- freefile(fs, f, bno);
- break;
- }
- n = devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno);
- if(n != Blen){
- freefile(fs, f, bno);
- break;
- }
- mdata = validdata(fs, buf, 0);
- if(mdata == 0){
- freefile(fs, f, bno);
- break;
- }
- mapset(fs, bno);
- switch(mdata->type){
- case Tagdata:
- bno = GETS(mdata->bno);
- f->length += Dlen;
- break;
- case Tagend:
- f->length += GETS(mdata->bno);
- done = 1;
- break;
- }
- if(done)
- f->flag &= ~Fcreating;
- }
- }
- }
- /*
- * single directory
- */
- static int
- tinyfsgen(Chan *c, Dirtab *tab, int ntab, int i, Dir *dp)
- {
- Tfs *fs;
- Tfile *f;
- Qid qid;
- USED(ntab);
- USED(tab);
- qid.vers = 0;
- fs = &tinyfs.fs[c->dev];
- if(i >= fs->nf)
- return -1;
- if(i == DEVDOTDOT){
- qid.path = CHDIR;
- devdir(c, qid, ".", 0, eve, 0555, dp);
- return 1;
- }
- f = &fs->f[i];
- if(f->name[0] == 0)
- return 0;
- qid.path = i;
- devdir(c, qid, f->name, f->length, eve, 0775, dp);
- return 1;
- }
- static void
- tinyfsinit(void)
- {
- if(Nlen > NAMELEN)
- panic("tinyfsinit");
- }
- /*
- * specifier is an open file descriptor
- */
- static Chan*
- tinyfsattach(char *spec)
- {
- Tfs *fs;
- Chan *c;
- volatile struct { Chan *cc; } rock;
- int i;
- char buf[NAMELEN*2];
- snprint(buf, sizeof(buf), "/dev/%s", spec);
- rock.cc = namec(buf, Aopen, ORDWR, 0);
- if(waserror()){
- cclose(rock.cc);
- nexterror();
- }
- fs = 0;
- for(i = 0; i < Maxfs; i++){
- fs = &tinyfs.fs[i];
- qlock(&fs->ql);
- if(fs->r && eqchan(rock.cc, fs->c, 1))
- break;
- qunlock(&fs->ql);
- }
- if(i < Maxfs){
- fs->r++;
- qunlock(&fs->ql);
- cclose(rock.cc);
- } else {
- for(fs = tinyfs.fs; fs < &tinyfs.fs[Maxfs]; fs++){
- qlock(&fs->ql);
- if(fs->r == 0)
- break;
- qunlock(&fs->ql);
- }
- if(fs == &tinyfs.fs[Maxfs])
- error("too many tinyfs's");
- fs->c = rock.cc;
- fs->r = 1;
- fs->f = 0;
- fs->nf = 0;
- fs->fsize = 0;
- tfsinit(fs);
- qunlock(&fs->ql);
- }
- poperror();
- c = devattach('F', spec);
- c->dev = fs - tinyfs.fs;
- c->qid.path = CHDIR;
- c->qid.vers = 0;
- return c;
- }
- static Chan*
- tinyfsclone(Chan *c, Chan *nc)
- {
- Tfs *fs;
- fs = &tinyfs.fs[c->dev];
- qlock(&fs->ql);
- fs->r++;
- qunlock(&fs->ql);
- return devclone(c, nc);
- }
- static int
- tinyfswalk(Chan *c, char *name)
- {
- int n;
- Tfs *fs;
- fs = &tinyfs.fs[c->dev];
- qlock(&fs->ql);
- n = devwalk(c, name, 0, 0, tinyfsgen);
- if(n != 0 && c->qid.path != CHDIR){
- fs = &tinyfs.fs[c->dev];
- fs->f[c->qid.path].r++;
- }
- qunlock(&fs->ql);
- return n;
- }
- static void
- tinyfsstat(Chan *c, char *db)
- {
- devstat(c, db, 0, 0, tinyfsgen);
- }
- static Chan*
- tinyfsopen(Chan *c, int omode)
- {
- Tfile *f;
- volatile struct { Tfs *fs; } rock;
- rock.fs = &tinyfs.fs[c->dev];
- if(c->qid.path & CHDIR){
- if(omode != OREAD)
- error(Eperm);
- } else {
- qlock(&rock.fs->ql);
- if(waserror()){
- qunlock(&rock.fs->ql);
- nexterror();
- }
- switch(omode){
- case OTRUNC|ORDWR:
- case OTRUNC|OWRITE:
- f = newfile(rock.fs, rock.fs->f[c->qid.path].name);
- rock.fs->f[c->qid.path].r--;
- c->qid.path = f - rock.fs->f;
- break;
- case OREAD:
- case OEXEC:
- break;
- default:
- error(Eperm);
- }
- qunlock(&rock.fs->ql);
- poperror();
- }
- return devopen(c, omode, 0, 0, tinyfsgen);
- }
- static void
- tinyfscreate(Chan *c, char *name, int omode, ulong perm)
- {
- volatile struct { Tfs *fs; } rock;
- Tfile *f;
- USED(perm);
- rock.fs = &tinyfs.fs[c->dev];
- qlock(&rock.fs->ql);
- if(waserror()){
- qunlock(&rock.fs->ql);
- nexterror();
- }
- f = newfile(rock.fs, name);
- qunlock(&rock.fs->ql);
- poperror();
- c->qid.path = f - rock.fs->f;
- c->qid.vers = 0;
- c->mode = openmode(omode);
- }
- static void
- tinyfsremove(Chan *c)
- {
- Tfs *fs;
- Tfile *f;
- if(c->qid.path == CHDIR)
- error(Eperm);
- fs = &tinyfs.fs[c->dev];
- f = &fs->f[c->qid.path];
- qlock(&fs->ql);
- freefile(fs, f, Notabno);
- qunlock(&fs->ql);
- }
- static void
- tinyfsclose(Chan *c)
- {
- volatile struct { Tfs *fs; } rock;
- Tfile *f, *nf;
- int i;
- rock.fs = &tinyfs.fs[c->dev];
- qlock(&rock.fs->ql);
- /* dereference file and remove old versions */
- if(!waserror()){
- if(c->qid.path != CHDIR){
- f = &rock.fs->f[c->qid.path];
- f->r--;
- if(f->r == 0){
- if(f->flag & Frmonclose)
- freefile(rock.fs, f, Notabno);
- else if(f->flag & Fcreating){
- /* remove all other files with this name */
- for(i = 0; i < rock.fs->fsize; i++){
- nf = &rock.fs->f[i];
- if(f == nf)
- continue;
- if(strcmp(nf->name, f->name) == 0){
- if(nf->r)
- nf->flag |= Frmonclose;
- else
- freefile(rock.fs, nf, Notabno);
- }
- }
- f->flag &= ~Fcreating;
- }
- }
- }
- poperror();
- }
- /* dereference rock.fs and remove on zero refs */
- rock.fs->r--;
- if(rock.fs->r == 0){
- if(rock.fs->f)
- free(rock.fs->f);
- rock.fs->f = 0;
- rock.fs->nf = 0;
- rock.fs->fsize = 0;
- if(rock.fs->map)
- free(rock.fs->map);
- rock.fs->map = 0;
- cclose(rock.fs->c);
- rock.fs->c = 0;
- }
- qunlock(&rock.fs->ql);
- }
- static long
- tinyfsread(Chan *c, void *a, long n, vlong offset)
- {
- volatile struct { Tfs *fs; } rock;
- Tfile *f;
- int sofar, i, off;
- ulong bno;
- Mdata *md;
- uchar buf[Blen];
- uchar *p;
- if(c->qid.path & CHDIR)
- return devdirread(c, a, n, 0, 0, tinyfsgen);
- p = a;
- rock.fs = &tinyfs.fs[c->dev];
- f = &rock.fs->f[c->qid.path];
- if(offset >= f->length)
- return 0;
- qlock(&rock.fs->ql);
- if(waserror()){
- qunlock(&rock.fs->ql);
- nexterror();
- }
- if(n + offset >= f->length)
- n = f->length - offset;
- /* walk to starting data block */
- if(0 && f->finger <= offset && f->fbno != Notabno){
- sofar = f->finger;
- bno = f->fbno;
- } else {
- sofar = 0;
- bno = f->dbno;
- }
- for(; sofar + Dlen <= offset; sofar += Dlen){
- md = readdata(rock.fs, bno, buf, 0);
- if(md == 0)
- error(Eio);
- bno = GETS(md->bno);
- }
- /* read data */
- off = offset%Dlen;
- offset -= off;
- for(sofar = 0; sofar < n; sofar += i){
- md = readdata(rock.fs, bno, buf, &i);
- if(md == 0)
- error(Eio);
- /* update finger for successful read */
- f->finger = offset;
- f->fbno = bno;
- offset += Dlen;
- i -= off;
- if(i > n - sofar)
- i = n - sofar;
- memmove(p, md->data+off, i);
- p += i;
- bno = GETS(md->bno);
- off = 0;
- }
- qunlock(&rock.fs->ql);
- poperror();
- return sofar;
- }
- /*
- * if we get a write error in this routine, blocks will
- * be lost. They should be recovered next fsinit.
- */
- static long
- tinyfswrite(Chan *c, void *a, long n, vlong offset)
- {
- Tfile *f;
- int last, next, i, finger, off, used;
- ulong bno, fbno;
- Mdata *md;
- uchar buf[Blen];
- uchar *p;
- volatile struct {
- Tfs *fs;
- ulong dbno;
- } rock;
- if(c->qid.path & CHDIR)
- error(Eperm);
- if(n == 0)
- return 0;
- p = a;
- rock.fs = &tinyfs.fs[c->dev];
- f = &rock.fs->f[c->qid.path];
- qlock(&rock.fs->ql);
- rock.dbno = Notabno;
- if(waserror()){
- freeblocks(rock.fs, rock.dbno, Notabno);
- qunlock(&rock.fs->ql);
- nexterror();
- }
- /* files are append only, anything else is illegal */
- if(offset != f->length)
- error("append only");
- /* write blocks backwards */
- p += n;
- last = offset + n;
- fbno = Notabno;
- finger = 0;
- off = offset; /* so we have something signed to compare against */
- for(next = ((last-1)/Dlen)*Dlen; next >= off; next -= Dlen){
- bno = mapalloc(rock.fs);
- if(bno == Notabno)
- error("out of space");
- i = last - next;
- p -= i;
- if(last == n+offset){
- writedata(rock.fs, bno, rock.dbno, p, i, 1);
- finger = next; /* remember for later */
- fbno = bno;
- } else {
- writedata(rock.fs, bno, rock.dbno, p, i, 0);
- }
- rock.dbno = bno;
- last = next;
- }
- /* walk to last data block */
- md = (Mdata*)buf;
- if(0 && f->finger < offset && f->fbno != Notabno){
- next = f->finger;
- bno = f->fbno;
- } else {
- next = 0;
- bno = f->dbno;
- }
- used = 0;
- while(bno != Notabno){
- md = readdata(rock.fs, bno, buf, &used);
- if(md == 0)
- error(Eio);
- if(md->type == Tagend){
- if(next + Dlen < offset)
- panic("devtinyfs1");
- break;
- }
- next += Dlen;
- if(next > offset)
- panic("devtinyfs1");
- bno = GETS(md->bno);
- }
- /* point to new blocks */
- if(offset == 0){
- /* first block in a file */
- f->dbno = rock.dbno;
- writedir(rock.fs, f);
- } else {
- /* updating a current block */
- i = last - offset;
- if(i > 0){
- p -= i;
- memmove(md->data + used, p, i);
- used += i;
- }
- writedata(rock.fs, bno, rock.dbno, md->data, used, last == n+offset);
- }
- f->length += n;
- /* update finger */
- if(fbno != Notabno){
- f->finger = finger;
- f->fbno = fbno;
- }
- poperror();
- qunlock(&rock.fs->ql);
- return n;
- }
- Dev tinyfsdevtab = {
- 'F',
- "tinyfs",
- devreset,
- tinyfsinit,
- tinyfsattach,
- tinyfsclone,
- tinyfswalk,
- tinyfsstat,
- tinyfsopen,
- tinyfscreate,
- tinyfsclose,
- tinyfsread,
- devbread,
- tinyfswrite,
- devbwrite,
- tinyfsremove,
- devwstat,
- };
|