ref: 82b046f36f8084a22bbb5d71edd0edd9179561eb
parent: ee695eff4329234e20abb68ff3100d3b15929418
author: henesy <devnull@localhost>
date: Sat Feb 29 11:16:06 EST 2020
merge 5be4ead0a7930b82adb10355cbac13d84869e0cb
--- a/emu/Linux/emu
+++ b/emu/Linux/emu
@@ -13,6 +13,7 @@
fs
cmd cmd
indir
+ ds
draw win-x11a
pointer
--- a/emu/Linux/mkfile
+++ b/emu/Linux/mkfile
@@ -47,3 +47,4 @@
<../port/portmkfile
devfs.$O: ../port/devfs-posix.c
+devds.$O: ../port/devds.c
--- /dev/null
+++ b/emu/port/devds.c
@@ -1,0 +1,594 @@
+/*
+ * (file system) device subsystems
+ * '#k'.
+ * Follows device config in Ken's file server.
+ * Builds mirrors, device cats, interleaving, and partition of devices out of
+ * other (inner) devices.
+ *
+ * This code is from Plan 9, and subject to the Lucent Public License 1.02.
+ * Only the name changed for Inferno (name clash).
+ */
+
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+enum {
+ Fmirror, // mirror of others
+ Fcat, // catenation of others
+ Finter, // interleaving of others
+ Fpart, // part of others
+
+ Blksize = 8*1024, // for Finter only
+ Maxconf = 1024, // max length for config
+
+ Nfsdevs = 64,
+ Ndevs = 8,
+
+ Qtop = 0, // top dir (contains "ds")
+ Qdir = 1, // actual dir
+ Qctl = 2, // ctl file
+ Qfirst = 3, // first fs file
+};
+
+#define Cfgstr "fsdev:\n"
+
+typedef struct Fsdev Fsdev;
+
+struct Fsdev
+{
+ int type;
+ char *name; // name for this fsdev
+ vlong start; // start address (for Fpart)
+ vlong size; // min(idev sizes)
+ int ndevs; // number of inner devices
+ char *iname[Ndevs]; // inner device names
+ Chan *idev[Ndevs]; // inner devices
+ vlong isize[Ndevs]; // sizes for inneer devices
+};
+
+/*
+ * Once configured, a fsdev is never removed. The name of those
+ * configured is never nil. We have no locks here.
+ */
+static Fsdev fsdev[Nfsdevs];
+
+static Qid tqid = {Qtop, 0, QTDIR};
+static Qid dqid = {Qdir, 0, QTDIR};
+static Qid cqid = {Qctl, 0, 0};
+
+static Cmdtab configs[] = {
+ Fmirror,"mirror", 0,
+ Fcat, "cat", 0,
+ Finter, "inter", 0,
+ Fpart, "part", 5,
+};
+
+static char _confstr[Maxconf];
+static int configed;
+
+
+static Fsdev*
+path2dev(int i, int mustexist)
+{
+ if (i < 0 || i >= nelem(fsdev))
+ error("bug: bad index in devfsdev");
+ if (mustexist && fsdev[i].name == nil)
+ error(Enonexist);
+
+ if (fsdev[i].name == nil)
+ return nil;
+ else
+ return &fsdev[i];
+}
+
+static Fsdev*
+devalloc(void)
+{
+ int i;
+
+ for (i = 0; i < nelem(fsdev); i++)
+ if (fsdev[i].name == nil)
+ break;
+ if (i == nelem(fsdev))
+ error(Enodev);
+
+ return &fsdev[i];
+}
+
+static void
+setdsize(Fsdev* mp)
+{
+ uchar buf[128]; /* old DIRLEN plus a little should be plenty */
+ int i;
+ Chan *mc;
+ Dir d;
+ long l;
+
+ if (mp->type != Fpart){
+ mp->start= 0;
+ mp->size = 0LL;
+ }
+ for (i = 0; i < mp->ndevs; i++){
+ mc = mp->idev[i];
+ l = devtab[mc->type]->stat(mc, buf, sizeof(buf));
+ convM2D(buf, l, &d, nil);
+ mp->isize[i] = d.length;
+ switch(mp->type){
+ case Fmirror:
+ if (mp->size == 0LL || mp->size > d.length)
+ mp->size = d.length;
+ break;
+ case Fcat:
+ mp->size += d.length;
+ break;
+ case Finter:
+ // truncate to multiple of Blksize
+ d.length = (d.length & ~(Blksize-1));
+ mp->isize[i] = d.length;
+ mp->size += d.length;
+ break;
+ case Fpart:
+ // should raise errors here?
+ if (mp->start > d.length)
+ mp->start = d.length;
+ if (d.length < mp->start + mp->size)
+ mp->size = d.length - mp->start;
+ break;
+ }
+ }
+}
+
+static void
+mpshut(Fsdev *mp)
+{
+ int i;
+ char *nm;
+
+ nm = mp->name;
+ mp->name = nil; // prevent others from using this.
+ if (nm)
+ free(nm);
+ for (i = 0; i < mp->ndevs; i++){
+ if (mp->idev[i] != nil)
+ cclose(mp->idev[i]);
+ if (mp->iname[i])
+ free(mp->iname[i]);
+ }
+ memset(mp, 0, sizeof(*mp));
+}
+
+
+static void
+mconfig(char* a, long n) // "name idev0 idev1"
+{
+ static QLock lck;
+ Cmdbuf *cb;
+ Cmdtab *ct;
+ Fsdev *mp;
+ int i;
+ char *oldc;
+ char *c;
+ vlong size, start;
+
+ size = 0;
+ start = 0;
+ if (_confstr[0] == 0)
+ seprint(_confstr, _confstr+sizeof(_confstr), Cfgstr);
+ oldc = _confstr + strlen(_confstr);
+ qlock(&lck);
+ if (waserror()){
+ *oldc = 0;
+ qunlock(&lck);
+ nexterror();
+ }
+ cb = parsecmd(a, n);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+ c = oldc;
+ for (i = 0; i < cb->nf; i++)
+ c = seprint(c, _confstr+sizeof(_confstr), "%s ", cb->f[i]);
+ *(c-1) = '\n';
+ ct = lookupcmd(cb, configs, nelem(configs));
+ cb->f++; // skip command
+ cb->nf--;
+ if (ct->index == Fpart){
+ size = strtoll(cb->f[3], nil, 10);
+ cb->nf--;
+ start = strtoll(cb->f[2], nil, 10);
+ cb->nf--;
+ }
+ for (i = 0; i < nelem(fsdev); i++)
+ if (fsdev[i].name != nil && strcmp(fsdev[i].name, cb->f[0])==0)
+ error(Eexist);
+ if (cb->nf - 1 > Ndevs)
+ error("too many devices; fix me");
+ for (i = 0; i < cb->nf; i++)
+ validname(cb->f[i], (i != 0));
+ mp = devalloc();
+ if(waserror()){
+ mpshut(mp);
+ nexterror();
+ }
+ mp->type = ct->index;
+ if (mp->type == Fpart){
+ mp->size = size;
+ mp->start = start;
+ }
+ kstrdup(&mp->name, cb->f[0]);
+ for (i = 1; i < cb->nf; i++){
+ kstrdup(&mp->iname[i-1], cb->f[i]);
+ mp->idev[i-1] = namec(mp->iname[i-1], Aopen, ORDWR, 0);
+ if (mp->idev[i-1] == nil)
+ error(Egreg);
+ mp->ndevs++;
+ }
+ setdsize(mp);
+ poperror();
+ free(cb);
+ poperror();
+ poperror();
+ configed = 1;
+ qunlock(&lck);
+
+}
+
+static void
+rdconf(void)
+{
+ int mustrd;
+ char *s;
+ char *c;
+ char *p;
+ char *e;
+ Chan *cc;
+
+ mustrd = 0;
+ s = "/dev/sdC0/fscfg";
+ if (waserror()){
+ configed = 1;
+ if (!mustrd)
+ return;
+ nexterror();
+ }
+ cc = namec(s, Aopen, OREAD, 0);
+ if(waserror()){
+ cclose(cc);
+ nexterror();
+ }
+ devtab[cc->type]->read(cc, _confstr, sizeof(_confstr), 0);
+ poperror();
+ cclose(cc);
+ if (strncmp(_confstr, Cfgstr, strlen(Cfgstr)) != 0)
+ error("config string must start with `fsdev:'");
+ kstrdup(&c, _confstr + strlen(Cfgstr));
+ if(waserror()){
+ free(c);
+ nexterror();
+ }
+ memset(_confstr, 0, sizeof(_confstr));
+ for (p = c; p != nil && *p != 0; p = e){
+ e = strchr(p, '\n');
+ if (e == p){
+ e++;
+ continue;
+ }
+ if (e == nil)
+ e = p + strlen(p);
+ mconfig(p, e - p);
+ }
+ poperror();
+ poperror();
+}
+
+
+static int
+mgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
+{
+ Qid qid;
+ Fsdev *mp;
+
+ if (c->qid.path == Qtop){
+ switch(i){
+ case DEVDOTDOT:
+ devdir(c, tqid, "#k", 0, eve, DMDIR|0775, dp);
+ return 1;
+ case 0:
+ devdir(c, dqid, "ds", 0, eve, DMDIR|0775, dp);
+ return 1;
+ default:
+ return -1;
+ }
+ }
+ if (c->qid.path != Qdir){
+ switch(i){
+ case DEVDOTDOT:
+ devdir(c, dqid, "ds", 0, eve, DMDIR|0775, dp);
+ return 1;
+ default:
+ return -1;
+ }
+ }
+ switch(i){
+ case DEVDOTDOT:
+ devdir(c, tqid, "#k", 0, eve, DMDIR|0775, dp);
+ return 1;
+ case 0:
+ devdir(c, cqid, "ctl", 0, eve, 0664, dp);
+ return 1;
+ }
+ i--; // for ctl
+ qid.path = Qfirst + i;
+ qid.vers = 0;
+ qid.type = 0;
+ mp = path2dev(i, 0);
+ if (mp == nil)
+ return -1;
+ kstrcpy(up->genbuf, mp->name, sizeof(up->genbuf));
+ devdir(c, qid, up->genbuf, mp->size, eve, 0664, dp);
+ return 1;
+}
+
+static Chan*
+mattach(char *spec)
+{
+ *_confstr = 0;
+ return devattach(L'k', spec);
+}
+
+static Walkqid*
+mwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ if (!configed)
+ rdconf();
+ return devwalk(c, nc, name, nname, 0, 0, mgen);
+}
+
+static int
+mstat(Chan *c, uchar *db, int n)
+{
+ Dir d;
+ Fsdev *mp;
+ int p;
+
+ p = c->qid.path;
+ memset(&d, 0, sizeof(d));
+ switch(p){
+ case Qtop:
+ devdir(c, tqid, "#k", 0, eve, DMDIR|0775, &d);
+ break;
+ case Qdir:
+ devdir(c, dqid, "ds", 0, eve, DMDIR|0775, &d);
+ break;
+ case Qctl:
+ devdir(c, cqid, "ctl", 0, eve, 0664, &d);
+ break;
+ default:
+ mp = path2dev(p - Qfirst, 1);
+ devdir(c, c->qid, mp->name, mp->size, eve, 0664, &d);
+ }
+ n = convD2M(&d, db, n);
+ if (n == 0)
+ error(Ebadarg);
+ return n;
+}
+
+static Chan*
+mopen(Chan *c, int omode)
+{
+ if((c->qid.type & QTDIR) && omode != OREAD)
+ error(Eperm);
+ if (omode & OTRUNC)
+ omode &= ~OTRUNC;
+ c->mode = openmode(omode);
+ c->flag |= COPEN;
+ c->offset = 0;
+ return c;
+}
+
+static void
+mclose(Chan *c)
+{
+ // that's easy
+}
+
+static long
+catio(Fsdev *mp, int isread, void *a, long n, vlong off)
+{
+ int i;
+ Chan* mc;
+ long l, wl, res;
+ //print("catio %d %p %ld %lld\n", isread, a, n, off);
+ res = n;
+ for (i = 0; n >= 0 && i < mp->ndevs ; i++){
+ mc = mp->idev[i];
+ if (off > mp->isize[i]){
+ off -= mp->isize[i];
+ continue;
+ }
+ if (off + n > mp->isize[i])
+ l = mp->isize[i] - off;
+ else
+ l = n;
+ //print("\tdev %d %p %ld %lld\n", i, a, l, off);
+
+ if (isread)
+ wl = devtab[mc->type]->read(mc, a, l, off);
+ else
+ wl = devtab[mc->type]->write(mc, a, l, off);
+ if (wl != l)
+ error("#k: write failed");
+ a = (char*)a + l;
+ off = 0;
+ n -= l;
+ }
+ //print("\tres %ld\n", res - n);
+ return res - n;
+}
+
+static long
+interio(Fsdev *mp, int isread, void *a, long n, vlong off)
+{
+ int i;
+ Chan* mc;
+ long l, wl, wsz;
+ vlong woff, blk, mblk;
+ long boff, res;
+
+ blk = off / Blksize;
+ boff = off % Blksize;
+ wsz = Blksize - boff;
+ res = n;
+ while(n > 0){
+ i = blk % mp->ndevs;
+ mc = mp->idev[i];
+ mblk = blk / mp->ndevs;
+ woff = mblk * Blksize + boff;
+ if (n > wsz)
+ l = wsz;
+ else
+ l = n;
+ if (isread)
+ wl = devtab[mc->type]->read(mc, a, l, woff);
+ else
+ wl = devtab[mc->type]->write(mc, a, l, woff);
+ if (wl != l || l == 0)
+ error(Eio);
+ a = (char*)a + l;
+ n -= l;
+ blk++;
+ boff = 0;
+ wsz = Blksize;
+ }
+ return res;
+}
+
+static long
+mread(Chan *c, void *a, long n, vlong off)
+{
+ int i;
+ Fsdev *mp;
+ Chan *mc;
+ long l;
+ long res;
+
+ if (c->qid.type & QTDIR)
+ return devdirread(c, a, n, 0, 0, mgen);
+ if (c->qid.path == Qctl)
+ return readstr((long)off, a, n, _confstr + strlen(Cfgstr));
+ i = c->qid.path - Qfirst;
+ mp = path2dev(i, 1);
+
+ if (off >= mp->size)
+ return 0;
+ if (off + n > mp->size)
+ n = mp->size - off;
+ if (n == 0)
+ return 0;
+
+ res = -1;
+ switch(mp->type){
+ case Fmirror:
+ for (i = 0; i < mp->ndevs; i++){
+ mc = mp->idev[i];
+ if (waserror()){
+ // if a read fails we let the user know and try
+ // another device.
+ print("#k: mread: (%llx %d): %s\n",
+ c->qid.path, i, up->env->errstr);
+ continue;
+ }
+ l = devtab[mc->type]->read(mc, a, n, off);
+ poperror();
+ if (l >=0){
+ res = l;
+ break;
+ }
+ }
+ if (i == mp->ndevs)
+ error(Eio);
+ break;
+ case Fcat:
+ res = catio(mp, 1, a, n, off);
+ break;
+ case Finter:
+ res = interio(mp, 1, a, n, off);
+ break;
+ case Fpart:
+ off += mp->start;
+ mc = mp->idev[0];
+ res = devtab[mc->type]->read(mc, a, n, off);
+ break;
+ }
+ return res;
+}
+
+static long
+mwrite(Chan *c, void *a, long n, vlong off)
+{
+ Fsdev *mp;
+ long l, res;
+ int i;
+ Chan *mc;
+
+ if (c->qid.type & QTDIR)
+ error(Eperm);
+ if (c->qid.path == Qctl){
+ mconfig(a, n);
+ return n;
+ }
+ mp = path2dev(c->qid.path - Qfirst, 1);
+
+ if (off >= mp->size)
+ return 0;
+ if (off + n > mp->size)
+ n = mp->size - off;
+ if (n == 0)
+ return 0;
+ res = n;
+ switch(mp->type){
+ case Fmirror:
+ for (i = mp->ndevs-1; i >=0; i--){
+ mc = mp->idev[i];
+ l = devtab[mc->type]->write(mc, a, n, off);
+ if (l < res)
+ res = l;
+ }
+ break;
+ case Fcat:
+ res = catio(mp, 0, a, n, off);
+ break;
+ case Finter:
+ res = interio(mp, 0, a, n, off);
+ break;
+ case Fpart:
+ mc = mp->idev[0];
+ off += mp->start;
+ l = devtab[mc->type]->write(mc, a, n, off);
+ if (l < res)
+ res = l;
+ break;
+ }
+ return res;
+}
+
+Dev dsdevtab = {
+ 'k',
+ "ds",
+
+ devinit,
+ mattach,
+ mwalk,
+ mstat,
+ mopen,
+ devcreate,
+ mclose,
+ mread,
+ devbread,
+ mwrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};