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