#include #include #include #include #include #include #include <9p.h> #include "cram.h" typedef struct Aux { int nparents; vlong *parents; Dentry; /* only for directories */ vlong dirindex; /* what the next read's ifcall.offset must be */ vlong dirnext; /* the offset in bytes within Dentry.data where the next read will begin */ } Aux; Biobuf *disk; Super super; Qid dentryqid(Dentry *d) { return (Qid){d->index, 1, d->mode >> 24}; } void fsattach(Req *r) { Aux *aux; r->fid->aux = aux = emalloc9p(sizeof(Aux)); *aux = (Aux){0, nil, super.root, 0, 0}; r->fid->qid = dentryqid(aux); r->ofcall.qid = r->fid->qid; respond(r, nil); } void filldir(Dir *dir, Dentry *d) { dir->qid = dentryqid(d); dir->mode = d->mode; dir->atime = 0; dir->mtime = 0; dir->length = d->mode & DMDIR ? 0 : d->length; dir->name = estrdup9p(d->name); dir->uid = smprint("%d", d->uid); dir->gid = smprint("%d", d->gid); dir->muid = estrdup9p(""); if(dir->uid == nil || dir->gid == nil) sysfatal("filldir: no memory"); } void fsdirread(Req *r, Aux *dentry) { uchar *p, *ep; int n; Dentry d; Dir dir; uint npacked; if(r->ifcall.offset == 0) { dentry->dirnext = dentry->data; } else if(r->ifcall.offset != 0 && r->ifcall.offset != dentry->dirindex) { respond(r, "out-of-sequence offset"); return; } p = (uchar *)r->ofcall.data; ep = p + r->ifcall.count; while(dentry->dirnext < dentry->data + dentry->length) { if((n = readdentry(disk, &d, dentry->dirnext)) < 0) { fprint(2, "readdentry %#llx: %r\n", dentry->dirnext); respond(r, "bad dentry"); return; } dentry->dirnext += n; filldir(&dir, &d); npacked = convD2M(&dir, p, ep-p); free(dir.name); free(dir.uid); free(dir.gid); free(dir.muid); if(npacked == BIT16SZ) break; p += npacked; } r->ofcall.count = p - (uchar *)r->ofcall.data; dentry->dirindex += r->ofcall.count; respond(r, nil); } int decompressblock(Dentry *dentry, uchar *dblock, int blockno) { uchar cblock[2*Blocksz]; u32int start, end; int n; if(blockno == 0) { start = dentry->data; } else { if(Bpread(disk, cblock, 4, dentry->index + 4*(blockno-1)) < 0) { werrstr("start index: bio failure"); return -1; } start = get32(cblock); } if(Bpread(disk, cblock, 4, dentry->index + 4*blockno) != 4) { werrstr("end index: bio failure"); return -1; } end = get32(cblock); if(end < start || (n = end-start + 2) > sizeof cblock) { /* why +2? */ werrstr("bad index: [%#ux, %#ux)", start, end); return -1; } if(Bpread(disk, cblock, n, start) != n) { werrstr("bio failure"); return -1; } if((n = inflatezlibblock(dblock, Blocksz, cblock, n)) < 0) { werrstr("inflate: %s", flateerr(n)); return -1; } return n; } void fsread(Req *r) { Aux *dentry; vlong o, bo; int blockno, n; uchar block[Blocksz]; dentry = r->fid->aux; if(r->fid->qid.type & QTDIR) { fsdirread(r, dentry); return; } if(r->ifcall.offset > dentry->length) { respond(r, "bad offset"); return; } o = r->ifcall.offset; r->ofcall.count = 0; while(o < dentry->length && r->ofcall.count < r->ifcall.count) { blockno = o / Blocksz; if((n = decompressblock(dentry, block, blockno)) < 0) { fprint(2, "decompressblock %#llx %d %#llx: %r\n", dentry->index, blockno, dentry->data); respond(r, "decompressblock failed"); return; } assert(n <= sizeof block); bo = o - blockno*Blocksz; n = n-bo > r->ifcall.count - r->ofcall.count ? r->ifcall.count - r->ofcall.count : n-bo; memmove(r->ofcall.data + o-r->ifcall.offset, block + bo, n); o += n; r->ofcall.count += n; } assert(r->ofcall.count <= r->ifcall.count); respond(r, nil); } void fsstat(Req *r) { filldir(&r->d, (Aux *)r->fid->aux); respond(r, nil); } void pushaux(Aux **a, vlong up) { Aux *b; int n; n = sizeof(Aux) + ((*a)->nparents+1) * sizeof(vlong); b = emalloc9p(n); memmove(b, *a, n); b->parents = (vlong *)&b[1]; b->parents[b->nparents++] = up; free(*a); *a = b; } char * fswalk1(Fid *fid, char *name, Qid *qid) { Aux **dentry; Dentry d; vlong up; int i, n; dentry = (Aux **)&fid->aux; if(strcmp(name, "..") == 0) { if((*dentry)->nparents == 0) up = super.root.offset; else up = (*dentry)->parents[--(*dentry)->nparents]; if(readdentry(disk, &d, up) < 0) { fprint(2, "readdentry %#llx: %r\n", up); return "bad dentry"; } } else { /* this doesn't take advantage of sorted directories */ for(i = 0; i < (*dentry)->length; ) { if((n = readdentry(disk, &d, (*dentry)->data + i)) < 0) { fprint(2, "readdentry %#llx: %r\n", (*dentry)->data + i); return "bad dentry"; } if(strcmp(d.name, name) == 0) break; i += n; } if(i == (*dentry)->length) return "no such file"; /* warning if a directory points to a dentry whose offset is less than the directory's own offset might be worthwhile */ pushaux(dentry, (*dentry)->offset); } (*dentry)->Dentry = d; fid->qid = dentryqid(*dentry); *qid = fid->qid; return nil; } Aux * dupaux(Aux *a) { Aux *b; int n; n = sizeof(Aux) + a->nparents * sizeof(vlong); b = emalloc9p(n); memmove(b, a, n); b->parents = (vlong *)&b[1]; return b; } char * fsclone(Fid *oldfid, Fid *newfid) { newfid->aux = dupaux(oldfid->aux); return nil; } void fsdestroyfid(Fid *fid) { free(fid->aux); } Srv fs = { .attach = fsattach, .read = fsread, .stat = fsstat, .walk1 = fswalk1, .clone = fsclone, .destroyfid = fsdestroyfid, }; void usage(void) { fprint(2, "usage: %s [-d] fs\n", argv0); exits("usage"); } void main(int argc, char **argv) { ARGBEGIN { case 'd': chatty9p++; break; default: usage(); } ARGEND if(argc < 1) usage(); if(inflateinit() < 0) sysfatal("inflateinit: %r"); if((disk = Bopen(argv[0], OREAD)) == nil) sysfatal("open %s: %r", argv[0]); readsuper(disk, &super); unmount(nil, "/n/cram"); postmountsrv(&fs, nil, "/n/cram", MREPL); exits(nil); } /* linux/Documentaion/filesystems/cramfs.txt */ /* linux/fs/cramfs/README */ /* linux/include/linux/cramfs_fs.h */