summaryrefslogtreecommitdiff
path: root/sys/src/cmd/aux/disksim.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/aux/disksim.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/aux/disksim.c')
-rwxr-xr-xsys/src/cmd/aux/disksim.c676
1 files changed, 676 insertions, 0 deletions
diff --git a/sys/src/cmd/aux/disksim.c b/sys/src/cmd/aux/disksim.c
new file mode 100755
index 000000000..1f600a768
--- /dev/null
+++ b/sys/src/cmd/aux/disksim.c
@@ -0,0 +1,676 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+
+typedef struct Part Part;
+typedef struct Trip Trip;
+typedef struct Dbl Dbl;
+typedef struct Ind Ind;
+
+/*
+ * with 8192-byte blocks and 4-byte pointers,
+ * double-indirect gets us 34 GB.
+ * triple-indirect gets us 70,368 GB.
+ */
+
+enum
+{
+ LOGBLKSZ = 13,
+ BLKSZ = 1<<LOGBLKSZ, /* 8192 */
+ LOGNPTR = LOGBLKSZ-2, /* assume sizeof(void*) == 4 */
+ NPTR = 1<<LOGNPTR,
+};
+static uchar zero[BLKSZ];
+
+struct Trip
+{
+ Dbl *dbl[NPTR];
+};
+
+struct Dbl
+{
+ Ind *ind[NPTR];
+};
+
+struct Ind
+{
+ uchar *blk[NPTR];
+};
+
+Trip trip;
+
+struct Part
+{
+ int inuse;
+ int vers;
+ ulong mode;
+ char *name;
+ vlong offset; /* in sectors */
+ vlong length; /* in sectors */
+};
+
+enum
+{
+ Qroot = 0,
+ Qdir,
+ Qctl,
+ Qpart,
+};
+
+Part tab[64];
+int fd = -1;
+char *sdname = "sdXX";
+ulong ctlmode = 0666;
+char *inquiry = "aux/disksim hard drive";
+vlong nsect, sectsize, c, h, s;
+ulong time0;
+int rdonly;
+
+char*
+ctlstring(void)
+{
+ int i;
+ Fmt fmt;
+
+ fmtstrinit(&fmt);
+ fmtprint(&fmt, "inquiry %s\n", inquiry);
+ fmtprint(&fmt, "geometry %lld %lld %lld %lld %lld\n", nsect, sectsize, c, h, s);
+ for(i=0; i<nelem(tab); i++)
+ if(tab[i].inuse)
+ fmtprint(&fmt, "part %s %lld %lld\n", tab[i].name, tab[i].offset, tab[i].length);
+ return fmtstrflush(&fmt);
+}
+
+int
+addpart(char *name, vlong start, vlong end)
+{
+ int i;
+
+ if(start < 0 || start > end || end > nsect){
+ werrstr("bad partition boundaries");
+ return -1;
+ }
+
+ for(i=0; i<nelem(tab); i++)
+ if(tab[i].inuse == 0)
+ break;
+ if(i == nelem(tab)){
+ werrstr("no free partition slots");
+ return -1;
+ }
+
+ free(tab[i].name);
+ tab[i].inuse = 1;
+ tab[i].name = estrdup9p(name);
+ tab[i].offset = start;
+ tab[i].length = end - start;
+ tab[i].mode = ctlmode;
+ tab[i].vers++;
+
+ return 0;
+}
+
+int
+delpart(char *s)
+{
+ int i;
+
+ for(i=0; i<nelem(tab); i++)
+ if(tab[i].inuse && strcmp(tab[i].name, s) == 0)
+ break;
+ if(i==nelem(tab)){
+ werrstr("partition not found");
+ return -1;
+ }
+
+ tab[i].inuse = 0;
+ free(tab[i].name);
+ tab[i].name = 0;
+ return 0;
+}
+
+void
+ctlwrite(Req *r)
+{
+ int i;
+ Cmdbuf *cb;
+ vlong start, end;
+
+ r->ofcall.count = r->ifcall.count;
+ cb = parsecmd(r->ifcall.data, r->ifcall.count);
+ if(cb->nf < 1){
+ respond(r, "empty control message");
+ free(cb);
+ return;
+ }
+
+ if(strcmp(cb->f[0], "part") == 0){
+ if(cb->nf != 4){
+ respondcmderror(r, cb, "part takes 3 args");
+ free(cb);
+ return;
+ }
+ start = strtoll(cb->f[2], 0, 0);
+ end = strtoll(cb->f[3], 0, 0);
+ if(addpart(cb->f[1], start, end) < 0){
+ respondcmderror(r, cb, "%r");
+ free(cb);
+ return;
+ }
+ }
+ else if(strcmp(cb->f[0], "delpart") == 0){
+ if(cb->nf != 2){
+ respondcmderror(r, cb, "delpart takes 1 arg");
+ free(cb);
+ return;
+ }
+ if(delpart(cb->f[1]) < 0){
+ respondcmderror(r, cb, "%r");
+ free(cb);
+ return;
+ }
+ }
+ else if(strcmp(cb->f[0], "inquiry") == 0){
+ if(cb->nf != 2){
+ respondcmderror(r, cb, "inquiry takes 1 arg");
+ free(cb);
+ return;
+ }
+ free(inquiry);
+ inquiry = estrdup9p(cb->f[1]);
+ }
+ else if(strcmp(cb->f[0], "geometry") == 0){
+ if(cb->nf != 6){
+ respondcmderror(r, cb, "geometry takes 5 args");
+ free(cb);
+ return;
+ }
+ nsect = strtoll(cb->f[1], 0, 0);
+ sectsize = strtoll(cb->f[2], 0, 0);
+ c = strtoll(cb->f[3], 0, 0);
+ h = strtoll(cb->f[4], 0, 0);
+ s = strtoll(cb->f[5], 0, 0);
+ if(tab[0].inuse && strcmp(tab[0].name, "data") == 0 && tab[0].vers == 0){
+ tab[0].offset = 0;
+ tab[0].length = nsect;
+ }
+ for(i=0; i<nelem(tab); i++){
+ if(tab[i].inuse && tab[i].offset+tab[i].length > nsect){
+ tab[i].inuse = 0;
+ free(tab[i].name);
+ tab[i].name = 0;
+ }
+ }
+ }
+ else{
+ respondcmderror(r, cb, "unknown control message");
+ free(cb);
+ return;
+ }
+
+ free(cb);
+ respond(r, nil);
+}
+
+void*
+allocblk(vlong addr)
+{
+ uchar *op;
+ static uchar *p;
+ static ulong n;
+
+ if(n == 0){
+ p = malloc(4*1024*1024);
+ if(p == 0)
+ sysfatal("out of memory");
+ n = 4*1024*1024;
+ }
+ op = p;
+ p += BLKSZ;
+ n -= BLKSZ;
+ memset(op, 0, BLKSZ);
+ if(fd != -1 && addr != -1)
+ pread(fd, op, BLKSZ, addr);
+ return op;
+}
+
+uchar*
+getblock(vlong addr, int alloc)
+{
+ Dbl *p2;
+ Ind *p1;
+ uchar *p0;
+ uint i0, i1, i2;
+ vlong oaddr;
+
+ if(fd >= 0)
+ alloc = 1;
+
+ addr >>= LOGBLKSZ;
+ oaddr = addr<<LOGBLKSZ;
+ i0 = addr & (NPTR-1);
+ addr >>= LOGNPTR;
+ i1 = addr & (NPTR-1);
+ addr >>= LOGNPTR;
+ i2 = addr & (NPTR-1);
+ addr >>= LOGNPTR;
+ assert(addr == 0);
+
+ if((p2 = trip.dbl[i2]) == 0){
+ if(!alloc)
+ return zero;
+ trip.dbl[i2] = p2 = allocblk(-1);
+ }
+
+ if((p1 = p2->ind[i1]) == 0){
+ if(!alloc)
+ return zero;
+ p2->ind[i1] = p1 = allocblk(-1);
+ }
+
+ if((p0 = p1->blk[i0]) == 0){
+ if(!alloc)
+ return zero;
+ p1->blk[i0] = p0 = allocblk(oaddr);
+ }
+ return p0;
+}
+
+void
+dirty(vlong addr, uchar *buf)
+{
+ vlong oaddr;
+
+ if(fd == -1 || rdonly)
+ return;
+ oaddr = addr&~((vlong)BLKSZ-1);
+ if(pwrite(fd, buf, BLKSZ, oaddr) != BLKSZ)
+ sysfatal("write: %r");
+}
+
+int
+rootgen(int off, Dir *d, void*)
+{
+ memset(d, 0, sizeof *d);
+ d->atime = time0;
+ d->mtime = time0;
+ if(off == 0){
+ d->name = estrdup9p(sdname);
+ d->mode = DMDIR|0777;
+ d->qid.path = Qdir;
+ d->qid.type = QTDIR;
+ d->uid = estrdup9p("disksim");
+ d->gid = estrdup9p("disksim");
+ d->muid = estrdup9p("");
+ return 0;
+ }
+ return -1;
+}
+
+int
+dirgen(int off, Dir *d, void*)
+{
+ int n, j;
+
+ memset(d, 0, sizeof *d);
+ d->atime = time0;
+ d->mtime = time0;
+ if(off == 0){
+ d->name = estrdup9p("ctl");
+ d->mode = ctlmode;
+ d->qid.path = Qctl;
+ goto Have;
+ }
+
+ off--;
+ n = 0;
+ for(j=0; j<nelem(tab); j++){
+ if(tab[j].inuse==0)
+ continue;
+ if(n == off){
+ d->name = estrdup9p(tab[j].name);
+ d->length = tab[j].length*sectsize;
+ d->mode = tab[j].mode;
+ d->qid.path = Qpart+j;
+ d->qid.vers = tab[j].vers;
+ goto Have;
+ }
+ n++;
+ }
+ return -1;
+
+Have:
+ d->uid = estrdup9p("disksim");
+ d->gid = estrdup9p("disksim");
+ d->muid = estrdup9p("");
+ return 0;
+}
+
+void*
+evommem(void *a, void *b, ulong n)
+{
+ return memmove(b, a, n);
+}
+
+int
+isnonzero(void *v, ulong n)
+{
+ uchar *a, *ea;
+
+ a = v;
+ ea = a+n;
+ for(; a<ea; a++)
+ if(*a)
+ return 1;
+ return 0;
+}
+
+int
+rdwrpart(Req *r)
+{
+ int q, nonzero;
+ Part *p;
+ vlong offset;
+ long count, tot, n, o;
+ uchar *blk, *dat;
+ void *(*move)(void*, void*, ulong);
+
+ q = r->fid->qid.path-Qpart;
+ if(q < 0 || q > nelem(tab) || !tab[q].inuse || tab[q].vers != r->fid->qid.vers){
+ respond(r, "unknown partition");
+ return -1;
+ }
+
+ p = &tab[q];
+ offset = r->ifcall.offset;
+ count = r->ifcall.count;
+ if(offset < 0){
+ respond(r, "negative offset");
+ return -1;
+ }
+ if(count < 0){
+ respond(r, "negative count");
+ return -1;
+ }
+ if(offset > p->length*sectsize){
+ respond(r, "offset past end of partition");
+ return -1;
+ }
+ if(offset+count > p->length*sectsize)
+ count = p->length*sectsize - offset;
+ offset += p->offset*sectsize;
+
+ if(r->ifcall.type == Tread)
+ move = memmove;
+ else
+ move = evommem;
+
+ tot = 0;
+ nonzero = 1;
+ if(r->ifcall.type == Tread)
+ dat = (uchar*)r->ofcall.data;
+ else{
+ dat = (uchar*)r->ifcall.data;
+ nonzero = isnonzero(dat, r->ifcall.count);
+ }
+ o = offset & (BLKSZ-1);
+
+ /* left fringe block */
+ if(o && count){
+ blk = getblock(offset, r->ifcall.type==Twrite && nonzero);
+ n = BLKSZ - o;
+ if(n > count)
+ n = count;
+ if(r->ifcall.type != Twrite || blk != zero)
+ (*move)(dat, blk+o, n);
+ if(r->ifcall.type == Twrite)
+ dirty(offset, blk);
+ tot += n;
+ }
+ /* full and right fringe blocks */
+ while(tot < count){
+ blk = getblock(offset+tot, r->ifcall.type==Twrite && nonzero);
+ n = BLKSZ;
+ if(n > count-tot)
+ n = count-tot;
+ if(r->ifcall.type != Twrite || blk != zero)
+ (*move)(dat+tot, blk, n);
+ if(r->ifcall.type == Twrite)
+ dirty(offset+tot, blk);
+ tot += n;
+ }
+ r->ofcall.count = tot;
+ respond(r, nil);
+ return 0;
+}
+
+void
+fsread(Req *r)
+{
+ char *s;
+
+ switch((int)r->fid->qid.path){
+ case Qroot:
+ dirread9p(r, rootgen, nil);
+ respond(r, nil);
+ break;
+
+ case Qdir:
+ dirread9p(r, dirgen, nil);
+ respond(r, nil);
+ break;
+
+ case Qctl:
+ s = ctlstring();
+ readstr(r, s);
+ free(s);
+ respond(r, nil);
+ break;
+
+ default:
+ rdwrpart(r);
+ break;
+ }
+}
+
+void
+fswrite(Req *r)
+{
+ switch((int)r->fid->qid.path){
+ case Qroot:
+ case Qdir:
+ respond(r, "write to a directory?");
+ break;
+
+ case Qctl:
+ ctlwrite(r);
+ break;
+
+ default:
+ rdwrpart(r);
+ break;
+ }
+}
+
+void
+fsopen(Req *r)
+{
+ if(r->ifcall.mode&ORCLOSE)
+ respond(r, "cannot open ORCLOSE");
+
+ switch((int)r->fid->qid.path){
+ case Qroot:
+ case Qdir:
+ if(r->ifcall.mode != OREAD){
+ respond(r, "bad mode for directory open");
+ return;
+ }
+ }
+
+ respond(r, nil);
+}
+
+void
+fsstat(Req *r)
+{
+ int q;
+ Dir *d;
+ Part *p;
+
+ d = &r->d;
+ memset(d, 0, sizeof *d);
+ d->qid = r->fid->qid;
+ d->atime = d->mtime = time0;
+ q = r->fid->qid.path;
+ switch(q){
+ case Qroot:
+ d->name = estrdup9p("/");
+ d->mode = DMDIR|0777;
+ break;
+
+ case Qdir:
+ d->name = estrdup9p(sdname);
+ d->mode = DMDIR|0777;
+ break;
+
+ default:
+ q -= Qpart;
+ if(q < 0 || q > nelem(tab) || tab[q].inuse==0 || r->fid->qid.vers != tab[q].vers){
+ respond(r, "partition no longer exists");
+ return;
+ }
+ p = &tab[q];
+ d->name = estrdup9p(p->name);
+ d->length = p->length * sectsize;
+ d->mode = p->mode;
+ break;
+ }
+
+ d->uid = estrdup9p("disksim");
+ d->gid = estrdup9p("disksim");
+ d->muid = estrdup9p("");
+ respond(r, nil);
+}
+
+void
+fsattach(Req *r)
+{
+ char *spec;
+
+ spec = r->ifcall.aname;
+ if(spec && spec[0]){
+ respond(r, "invalid attach specifier");
+ return;
+ }
+ r->ofcall.qid = (Qid){Qroot, 0, QTDIR};
+ r->fid->qid = r->ofcall.qid;
+ respond(r, nil);
+}
+
+char*
+fswalk1(Fid *fid, char *name, Qid *qid)
+{
+ int i;
+ switch((int)fid->qid.path){
+ case Qroot:
+ if(strcmp(name, sdname) == 0){
+ fid->qid.path = Qdir;
+ fid->qid.type = QTDIR;
+ *qid = fid->qid;
+ return nil;
+ }
+ break;
+ case Qdir:
+ if(strcmp(name, "ctl") == 0){
+ fid->qid.path = Qctl;
+ fid->qid.vers = 0;
+ fid->qid.type = 0;
+ *qid = fid->qid;
+ return nil;
+ }
+ for(i=0; i<nelem(tab); i++){
+ if(tab[i].inuse && strcmp(tab[i].name, name) == 0){
+ fid->qid.path = i+Qpart;
+ fid->qid.vers = tab[i].vers;
+ fid->qid.type = 0;
+ *qid = fid->qid;
+ return nil;
+ }
+ }
+ break;
+ }
+ return "file not found";
+}
+
+Srv fs = {
+ .attach= fsattach,
+ .open= fsopen,
+ .read= fsread,
+ .write= fswrite,
+ .stat= fsstat,
+ .walk1= fswalk1,
+};
+
+char *mtpt = "/dev";
+char *srvname;
+
+void
+usage(void)
+{
+ fprint(2, "usage: aux/disksim [-D] [-f file] [-s srvname] [-m mtpt] [sdXX]\n");
+ fprint(2, "\tdefault mtpt is /dev\n");
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ char *file;
+
+ file = nil;
+ quotefmtinstall();
+ time0 = time(0);
+ if(NPTR != BLKSZ/sizeof(void*))
+ sysfatal("unexpected pointer size");
+
+ ARGBEGIN{
+ case 'D':
+ chatty9p++;
+ break;
+ case 'f':
+ file = EARGF(usage());
+ break;
+ case 'r':
+ rdonly = 1;
+ break;
+ case 's':
+ srvname = EARGF(usage());
+ break;
+ case 'm':
+ mtpt = EARGF(usage());
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc > 1)
+ usage();
+ if(argc == 1)
+ sdname = argv[0];
+
+ if(file){
+ if((fd = open(file, rdonly ? OREAD : ORDWR)) < 0)
+ sysfatal("open %s: %r", file);
+ }
+
+ inquiry = estrdup9p(inquiry);
+ tab[0].name = estrdup9p("data");
+ tab[0].inuse = 1;
+ tab[0].mode = 0666;
+
+ postmountsrv(&fs, srvname, mtpt, MBEFORE);
+ exits(nil);
+}