Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include "all.h"
#include "io.h"

enum {
        Slop = 256,     /* room at the start of a message buf for proto hdrs */
};

Filsys*
fsstr(char *p)
{
        Filsys *fs;

        for(fs=filsys; fs->name; fs++)
                if(strcmp(fs->name, p) == 0)
                        return fs;
        return 0;
}

Filsys*
dev2fs(Device *dev)
{
        Filsys *fs;

        for(fs=filsys; fs->name; fs++)
                if(fs->dev == dev)
                        return fs;
        return 0;
}

/*
 * allocate 'count' contiguous channels
 * of type 'type' and return pointer to base
 */
Chan*
fs_chaninit(int type, int count, int data)
{
        uchar *p;
        Chan *cp, *icp;
        int i;

        p = malloc(count * (sizeof(Chan)+data));
        icp = (Chan*)p;
        for(i = 0; i < count; i++) {
                cp = (Chan*)p;
                cp->next = chans;
                chans = cp;
                cp->type = type;
                cp->chan = cons.chano;
                cons.chano++;
                strncpy(cp->whoname, "<none>", sizeof cp->whoname);
                wlock(&cp->reflock);
                wunlock(&cp->reflock);
                rlock(&cp->reflock);
                runlock(&cp->reflock);

                p += sizeof(Chan);
                if(data){
                        cp->pdata = p;
                        p += data;
                }
        }
        return icp;
}

void
fileinit(Chan *cp)
{
        File *f, *prev;
        Tlock *t;
        int h;

loop:
        lock(&flock);
        for (h=0; h < nelem(flist); h++)
                for (prev=0, f = flist[h]; f; prev=f, f=f->next) {
                        if(f->cp != cp)
                                continue;
                        if(prev) {
                                prev->next = f->next;
                                f->next = flist[h];
                                flist[h] = f;
                        }
                        flist[h] = f->next;
                        unlock(&flock);

                        qlock(f);
                        if(t = f->tlock) {
                                if(t->file == f)
                                        t->time = 0;    /* free the lock */
                                f->tlock = 0;
                        }
                        if(f->open & FREMOV)
                                doremove(f, 0);
                        freewp(f->wpath);
                        f->open = 0;
                        authfree(f->auth);
                        f->auth = 0;
                        f->cp = 0;
                        qunlock(f);
                        goto loop;
                }
        unlock(&flock);
}

enum { NOFID = (ulong)~0 };

/*
 * returns a locked file structure
 */
File*
filep(Chan *cp, ulong fid, int flag)
{
        File *f;
        int h;

        if(fid == NOFID)
                return 0;

        h = (long)(uintptr)cp + fid;
        if(h < 0)
                h = ~h;
        h %= nelem(flist);

loop:
        lock(&flock);
        for(f=flist[h]; f; f=f->next)
                if(f->fid == fid && f->cp == cp){
                        /*
                         * Already in use is an error
                         * when called from attach or clone (walk
                         * in 9P2000). The console uses FID[12] and
                         * never clunks them so catch that case.
                         */
                        if(flag == 0 || cp == cons.chan)
                                goto out;
                        unlock(&flock);
                        return 0;
                }

        if(flag) {
                f = newfp();
                if(f) {
                        f->fid = fid;
                        f->cp = cp;
                        f->wpath = 0;
                        f->tlock = 0;
                        f->doffset = 0;
                        f->dslot = 0;
                        f->auth = 0;
                        f->next = flist[h];
                        flist[h] = f;
                        goto out;
                }
        }
        unlock(&flock);
        return 0;

out:
        unlock(&flock);
        qlock(f);
        if(f->fid == fid && f->cp == cp)
                return f;
        qunlock(f);
        goto loop;
}

/*
 * always called with flock locked
 */
File*
newfp(void)
{
        static int first;
        File *f;
        int start, i;

        i = first;
        start = i;
        do {
                f = &files[i];
                i++;
                if(i >= conf.nfile)
                        i = 0;
                if(f->cp)
                        continue;
                first = i;
                return f;
        } while(i != start);

        print("out of files\n");
        return 0;
}

void
freefp(File *fp)
{
        Chan *cp;
        File *f, *prev;
        int h;

        if(!fp || !(cp = fp->cp))
                return;

        h = (long)(uintptr)cp + fp->fid;
        if(h < 0)
                h = ~h;
        h %= nelem(flist);

        lock(&flock);
        for(prev=0,f=flist[h]; f; prev=f,f=f->next)
                if(f == fp) {
                        if(prev)
                                prev->next = f->next;
                        else
                                flist[h] = f->next;
                        break;
                }
        fp->cp = 0;
        unlock(&flock);
}

int
iaccess(File *f, Dentry *d, int m)
{
        /* uid none gets only other permissions */
        if(f->uid != 0) {
                /*
                 * owner
                 */
                if(f->uid == d->uid)
                        if((m<<6) & d->mode)
                                return 0;
                /*
                 * group membership
                 */
                if(ingroup(f->uid, d->gid))
                        if((m<<3) & d->mode)
                                return 0;
        }

        /*
         * other
         */
        if(m & d->mode) {
                if((d->mode & DDIR) && (m == DEXEC))
                        return 0;
                if(!ingroup(f->uid, 9999))
                        return 0;
        }

        /*
         * various forms of superuser
         */
        if(wstatallow)
                return 0;
        if(duallow != 0 && duallow == f->uid)
                if((d->mode & DDIR) && (m == DREAD || m == DEXEC))
                        return 0;

        return 1;
}

Tlock*
tlocked(Iobuf *p, Dentry *d)
{
        Tlock *t, *t1;
        Off qpath;
        Timet tim;
        Device *dev;

        tim = toytime();
        qpath = d->qid.path;
        dev = p->dev;

again:
        t1 = 0;
        for(t=tlocks+NTLOCK-1; t>=tlocks; t--) {
                if(t->qpath == qpath)
                if(t->time >= tim)
                if(t->dev == dev)
                        return nil;             /* its locked */
                if(t1 != nil && t->time == 0)
                        t1 = t;                 /* remember free lock */
        }
        if(t1 == 0) {
                // reclaim old locks
                lock(&tlocklock);
                for(t=tlocks+NTLOCK-1; t>=tlocks; t--)
                        if(t->time < tim) {
                                t->time = 0;
                                t1 = t;
                        }
                unlock(&tlocklock);
        }
        if(t1) {
                lock(&tlocklock);
                if(t1->time != 0) {
                        unlock(&tlocklock);
                        goto again;
                }
                t1->dev = dev;
                t1->qpath = qpath;
                t1->time = tim + TLOCK;
                unlock(&tlocklock);
        }
        /* botch
         * out of tlock nodes simulates
         * a locked file
         */
        return t1;
}

Wpath*
newwp(void)
{
        static int si = 0;
        int i;
        Wpath *w, *sw, *ew;

        i = si + 1;
        if(i < 0 || i >= conf.nwpath)
                i = 0;
        si = i;
        sw = &wpaths[i];
        ew = &wpaths[conf.nwpath];
        for(w=sw;;) {
                w++;
                if(w >= ew)
                        w = &wpaths[0];
                if(w == sw) {
                        print("out of wpaths\n");
                        return 0;
                }
                if(w->refs)
                        continue;
                lock(&wpathlock);
                if(w->refs) {
                        unlock(&wpathlock);
                        continue;
                }
                w->refs = 1;
                w->up = 0;
                unlock(&wpathlock);
                return w;
        }

}

void
freewp(Wpath *w)
{
        lock(&wpathlock);
        for(; w; w=w->up)
                w->refs--;
        unlock(&wpathlock);
}

Off
qidpathgen(Device *dev)
{
        Iobuf *p;
        Superb *sb;
        Off path;

        p = getbuf(dev, superaddr(dev), Brd|Bmod);
        if(!p || checktag(p, Tsuper, QPSUPER))
                panic("newqid: super block");
        sb = (Superb*)p->iobuf;
        sb->qidgen++;
        path = sb->qidgen;
        putbuf(p);
        return path;
}

/* truncating to length > 0 */
static void
truncfree(Truncstate *ts, Device *dev, int d, Iobuf *p, int i)
{
        int pastlast;
        Off a;

        pastlast = ts->pastlast;
        a = ((Off *)p->iobuf)[i];
        if (d > 0 || pastlast)
                buffree(dev, a, d, ts);
        if (pastlast) {
                ((Off *)p->iobuf)[i] = 0;
                p->flags |= Bmod|Bimm;
        } else if (d == 0 && ts->relblk == ts->lastblk)
                ts->pastlast = 1;
        if (d == 0)
                ts->relblk++;
}

/*
 * free the block at `addr' on dev.
 * if it's an indirect block (d [depth] > 0),
 * first recursively free all the blocks it names.
 *
 * ts->relblk is the block number within the file of this
 * block (or the first data block eventually pointed to via
 * this indirect block).
 */
void
buffree(Device *dev, Off addr, int d, Truncstate *ts)
{
        Iobuf *p;
        Off a;
        int i, pastlast;

        if(!addr)
                return;
        pastlast = (ts == nil? 1: ts->pastlast);
        /*
         * if this is an indirect block, recurse and free any
         * suitable blocks within it (possibly via further indirect blocks).
         */
        if(d > 0) {
                d--;
                p = getbuf(dev, addr, Brd);
                if(p) {
                        if (ts == nil)          /* common case: create */
                                for(i=INDPERBUF-1; i>=0; i--) {
                                        a = ((Off *)p->iobuf)[i];
                                        buffree(dev, a, d, nil);
                                }
                        else                    /* wstat truncation */
                                for (i = 0; i < INDPERBUF; i++)
                                        truncfree(ts, dev, d, p, i);
                        putbuf(p);
                }
        }
        if (!pastlast)
                return;
        /*
         * having zeroed the pointer to this block, add it to the free list.
         * stop outstanding i/o
         */
        p = getbuf(dev, addr, Bprobe);
        if(p) {
                p->flags &= ~(Bmod|Bimm);
                putbuf(p);
        }
        /*
         * dont put written worm
         * blocks into free list
         */
        if(dev->type == Devcw) {
                i = cwfree(dev, addr);
                if(i)
                        return;
        }
        p = getbuf(dev, superaddr(dev), Brd|Bmod);
        if(!p || checktag(p, Tsuper, QPSUPER))
                panic("buffree: super block");
        addfree(dev, addr, (Superb*)p->iobuf);
        putbuf(p);
}

Off
bufalloc(Device *dev, int tag, long qid, int uid)
{
        Iobuf *bp, *p;
        Superb *sb;
        Off a, n;

        p = getbuf(dev, superaddr(dev), Brd|Bmod);
        if(!p || checktag(p, Tsuper, QPSUPER)) {
                print("bufalloc: super block\n");
                if(p)
                        putbuf(p);
                return 0;
        }
        sb = (Superb*)p->iobuf;

loop:
        n = --sb->fbuf.nfree;
        sb->tfree--;
        if(n < 0 || n >= FEPERBUF) {
                print("bufalloc: %Z: bad freelist\n", dev);
                n = 0;
                sb->fbuf.free[0] = 0;
        }
        a = sb->fbuf.free[n];
        if(n <= 0) {
                if(a == 0) {
                        sb->tfree = 0;
                        sb->fbuf.nfree = 1;
                        if(dev->type == Devcw) {
                                n = uid;
                                if(n < 0 || n >= nelem(growacct))
                                        n = 0;
                                growacct[n]++;
                                if(cwgrow(dev, sb, uid))
                                        goto loop;
                        }
                        putbuf(p);
                        print("fs %Z full uid=%d\n", dev, uid);
                        return 0;
                }
                bp = getbuf(dev, a, Brd);
                if(!bp || checktag(bp, Tfree, QPNONE)) {
                        if(bp)
                                putbuf(bp);
                        putbuf(p);
                        return 0;
                }
                sb->fbuf = *(Fbuf*)bp->iobuf;
                putbuf(bp);
        }

        bp = getbuf(dev, a, Bmod);
        memset(bp->iobuf, 0, RBUFSIZE);
        settag(bp, tag, qid);
        if(tag == Tind1 || tag == Tind2 ||
#ifndef COMPAT32
            tag == Tind3 || tag == Tind4 ||  /* add more Tind tags here ... */
#endif
            tag == Tdir)
                bp->flags |= Bimm;
        putbuf(bp);
        putbuf(p);
        return a;
}

/*
 * what are legal characters in a name?
 * only disallow control characters.
 * a) utf avoids control characters.
 * b) '/' may not be the separator
 */
int
checkname(char *n)
{
        int i, c;

        for(i=0; i<NAMELEN; i++) {
                c = *n & 0xff;
                if(c == 0) {
                        if(i == 0)
                                return 1;
                        memset(n, 0, NAMELEN-i);
                        return 0;
                }
                if(c <= 040)
                        return 1;
                n++;
        }
        return 1;       /* too long */
}

void
addfree(Device *dev, Off addr, Superb *sb)
{
        int n;
        Iobuf *p;

        n = sb->fbuf.nfree;
        if(n < 0 || n > FEPERBUF)
                panic("addfree: bad freelist");
        if(n >= FEPERBUF) {
                p = getbuf(dev, addr, Bmod|Bimm);
                if(p == 0)
                        panic("addfree: getbuf");
                *(Fbuf*)p->iobuf = sb->fbuf;
                settag(p, Tfree, QPNONE);
                putbuf(p);
                n = 0;
        }
        sb->fbuf.free[n++] = addr;
        sb->fbuf.nfree = n;
        sb->tfree++;
        if(addr >= sb->fsize)
                sb->fsize = addr+1;
}

/*
static int
Yfmt(Fmt* fmt)
{
        Chan *cp;
        char s[20];

        cp = va_arg(fmt->args, Chan*);
        sprint(s, "C%d.%.3d", cp->type, cp->chan);
        return fmtstrcpy(fmt, s);
}
 */

static int
Zfmt(Fmt* fmt)
{
        Device *d;
        int c, c1;
        char s[100];

        d = va_arg(fmt->args, Device*);
        if(d == nil) {
                sprint(s, "Z***");
                goto out;
        }
        c = c1 = '\0';
        switch(d->type) {
        default:
                sprint(s, "D%d", d->type);
                break;
        case Devwren:
                c = 'w';
                /* fallthrough */
        case Devworm:
                if (c == '\0')
                        c = 'r';
                /* fallthrough */
        case Devlworm:
                if (c == '\0')
                        c = 'l';
                if(d->wren.ctrl == 0 && d->wren.lun == 0)
                        sprint(s, "%c%d", c, d->wren.targ);
                else
                        sprint(s, "%c%d.%d.%d", c, d->wren.ctrl, d->wren.targ,
                                d->wren.lun);
                break;
        case Devmcat:
                c = '(';
                c1 = ')';
                /* fallthrough */
        case Devmlev:
                if (c == '\0') {
                        c = '[';
                        c1 = ']';
                }
                /* fallthrough */
        case Devmirr:
                if (c == '\0') {
                        c = '{';
                        c1 = '}';
                }
                if(d->cat.first == d->cat.last)
                        sprint(s, "%c%Z%c", c, d->cat.first, c1);
                else if(d->cat.first->link == d->cat.last)
                        sprint(s, "%c%Z%Z%c", c, d->cat.first, d->cat.last, c1);
                else
                        sprint(s, "%c%Z-%Z%c", c, d->cat.first, d->cat.last, c1);
                break;
        case Devro:
                sprint(s, "o%Z%Z", d->ro.parent->cw.c, d->ro.parent->cw.w);
                break;
        case Devcw:
                sprint(s, "c%Z%Z", d->cw.c, d->cw.w);
                break;
        case Devjuke:
                sprint(s, "j%Z%Z", d->j.j, d->j.m);
                break;
        case Devfworm:
                sprint(s, "f%Z", d->fw.fw);
                break;
        case Devpart:
                sprint(s, "p(%Z)%ld.%ld", d->part.d, d->part.base, d->part.size);
                break;
        case Devswab:
                sprint(s, "x%Z", d->swab.d);
                break;
        case Devnone:
                sprint(s, "n");
                break;
        }
out:
        return fmtstrcpy(fmt, s);
}

static int
Gfmt(Fmt* fmt)
{
        int t;
        char *s;

        t = va_arg(fmt->args, int);
        s = "<badtag>";
        if(t >= 0 && t < MAXTAG)
                s = tagnames[t];
        return fmtstrcpy(fmt, s);
}

void
formatinit(void)
{
        quotefmtinstall();
//      fmtinstall('Y', Yfmt);  /* print channels */
        fmtinstall('Z', Zfmt);  /* print devices */
        fmtinstall('G', Gfmt);  /* print tags */
        fmtinstall('T', Tfmt);  /* print times */
//      fmtinstall('E', eipfmt);        /* print ether addresses */
        fmtinstall('I', eipfmt);        /* print ip addresses */
}

void
rootream(Device *dev, Off addr)
{
        Iobuf *p;
        Dentry *d;

        p = getbuf(dev, addr, Bmod|Bimm);
        memset(p->iobuf, 0, RBUFSIZE);
        settag(p, Tdir, QPROOT);
        d = getdir(p, 0);
        strcpy(d->name, "/");
        d->uid = -1;
        d->gid = -1;
        d->mode = DALLOC | DDIR |
                ((DREAD|DEXEC) << 6) |
                ((DREAD|DEXEC) << 3) |
                ((DREAD|DEXEC) << 0);
        d->qid = QID9P1(QPROOT|QPDIR,0);
        d->atime = time(nil);
        d->mtime = d->atime;
        d->muid = 0;
        putbuf(p);
}

void
superream(Device *dev, Off addr)
{
        Iobuf *p;
        Superb *s;
        Off i;

        p = getbuf(dev, addr, Bmod|Bimm);
        memset(p->iobuf, 0, RBUFSIZE);
        settag(p, Tsuper, QPSUPER);

        s = (Superb*)p->iobuf;
        s->fstart = 2;
        s->fsize = devsize(dev);
        s->fbuf.nfree = 1;
        s->qidgen = 10;
        for(i = s->fsize-1; i >= addr+2; i--)
                addfree(dev, i, s);
        putbuf(p);
}

struct
{
        Lock;
        Msgbuf  *smsgbuf;
        Msgbuf  *lmsgbuf;
} msgalloc;

/*
 * pre-allocate some message buffers at boot time.
 * if this supply is exhausted, more will be allocated as needed.
 */
void
mbinit(void)
{
        Msgbuf *mb;
        Rabuf *rb;
        int i;

        lock(&msgalloc);
        unlock(&msgalloc);
        msgalloc.lmsgbuf = 0;
        msgalloc.smsgbuf = 0;
        for(i=0; i<conf.nlgmsg; i++) {
                mb = malloc(sizeof(Msgbuf));
                mb->magic = Mbmagic;
                mb->xdata = malloc(LARGEBUF+Slop);
                mb->flags = LARGE;
                mbfree(mb);
                cons.nlarge++;
        }
        for(i=0; i<conf.nsmmsg; i++) {
                mb = malloc(sizeof(Msgbuf));
                mb->magic = Mbmagic;
                mb->xdata = malloc(SMALLBUF+Slop);
                mb->flags = 0;
                mbfree(mb);
                cons.nsmall++;
        }
        memset(mballocs, 0, sizeof(mballocs));

        lock(&rabuflock);
        unlock(&rabuflock);
        rabuffree = 0;
        for(i=0; i<1000; i++) {
                rb = malloc(sizeof(*rb));
                rb->link = rabuffree;
                rabuffree = rb;
        }
}

Msgbuf*
mballoc(int count, Chan *cp, int category)
{
        Msgbuf *mb;

        lock(&msgalloc);
        if(count > SMALLBUF) {
                if(count > LARGEBUF)
                        panic("msgbuf count");
                mb = msgalloc.lmsgbuf;
                if(mb == nil) {
                        mb = malloc(sizeof(Msgbuf));
                        mb->xdata = malloc(LARGEBUF+Slop);
                        cons.nlarge++;
                } else
                        msgalloc.lmsgbuf = mb->next;
                mb->flags = LARGE;
        } else {
                mb = msgalloc.smsgbuf;
                if(mb == nil) {
                        mb = malloc(sizeof(Msgbuf));
                        mb->xdata = malloc(SMALLBUF+Slop);
                        cons.nsmall++;
                } else
                        msgalloc.smsgbuf = mb->next;
                mb->flags = 0;
        }
        mballocs[category]++;
        unlock(&msgalloc);
        mb->magic = Mbmagic;
        mb->count = count;
        mb->chan = cp;
        mb->next = 0;
        mb->param = 0;
        mb->category = category;
        mb->data = mb->xdata+Slop;
        return mb;
}

void
mbfree(Msgbuf *mb)
{
        if(mb == nil)
                return;
        assert(mb->magic == Mbmagic);
        if (mb->magic != Mbmagic)
                panic("mbfree: bad magic 0x%lux", mb->magic);
        if(mb->flags & BTRACE)
                print("mbfree: BTRACE cat=%d flags=%ux, caller %#p\n",
                        mb->category, mb->flags, getcallerpc(&mb));

        if(mb->flags & FREE)
                panic("mbfree already free");

        lock(&msgalloc);
        mballocs[mb->category]--;
        mb->flags |= FREE;
        if(mb->flags & LARGE) {
                mb->next = msgalloc.lmsgbuf;
                msgalloc.lmsgbuf = mb;
        } else {
                mb->next = msgalloc.smsgbuf;
                msgalloc.smsgbuf = mb;
        }
        mb->data = 0;
        mb->magic = 0;
        unlock(&msgalloc);
}

/*
 * returns 1 if n is prime
 * used for adjusting lengths
 * of hashing things.
 * there is no need to be clever
 */
int
prime(vlong n)
{
        long i;

        if((n%2) == 0)
                return 0;
        for(i=3;; i+=2) {
                if((n%i) == 0)
                        return 0;
                if((vlong)i*i >= n)
                        return 1;
        }
}

char*
getwrd(char *word, char *line)
{
        int c, n;

        while(isascii(*line) && isspace(*line) && *line != '\n')
                line++;
        for(n = 0; n < Maxword; n++) {
                c = *line;
                if(c == '\0' || isascii(c) && isspace(c))
                        break;
                line++;
                *word++ = c;
        }
        *word = 0;
        return line;
}

void
hexdump(void *a, int n)
{
        char s1[30], s2[4];
        uchar *p;
        int i;

        p = a;
        s1[0] = 0;
        for(i = 0; i < n; i++) {
                sprint(s2, " %.2ux", p[i]);
                strcat(s1, s2);
                if((i&7) == 7) {
                        print("%s\n", s1);
                        s1[0] = 0;
                }
        }
        if(s1[0])
                print("%s\n", s1);
}

void*
fs_recv(Queue *q, int)
{
        void *a;
        int i, c;

        if(q == nil)
                panic("recv null q");
        qlock(q);
        q->waitedfor = 1;
        while((c = q->count) <= 0)
                rsleep(&q->empty);
        i = q->loc;
        a = q->args[i];
        i++;
        if(i >= q->size)
                i = 0;
        q->loc = i;
        q->count = c-1;
        rwakeup(&q->full);                      /* no longer full */
        qunlock(q);
        return a;
}

void
fs_send(Queue *q, void *a)
{
        int i, c;

        if(q == nil)
                panic("send null q");
        if(!q->waitedfor) {
                for (i = 0; i < 5 && !q->waitedfor; i++)
                        sleep(1000);
                if(!q->waitedfor) {
                        /* likely a bug; don't wait forever */
                        print("no readers yet for %s q\n", q->name);
                        abort();
                }
        }
        qlock(q);
        while((c = q->count) >= q->size)
                rsleep(&q->full);
        i = q->loc + c;
        if(i >= q->size)
                i -= q->size;
        q->args[i] = a;
        q->count = c+1;
        rwakeup(&q->empty);                     /* no longer empty */
        qunlock(q);
}

Queue*
newqueue(int size, char *name)
{
        Queue *q;

        q = malloc(sizeof(Queue) + (size-1)*sizeof(void*));
        q->size = size;
        q->full.l = q->empty.l = &q->QLock;
        q->name = name;
        return q;
}

int
devread(Device *d, Off b, void *c)
{
        int e;

        for (;;)
                switch(d->type) {
                case Devcw:
                        return cwread(d, b, c);

                case Devjuke:
                        d = d->j.m;
                        break;

                case Devro:
                        return roread(d, b, c);

                case Devwren:
                        return wrenread(d, b, c);

                case Devworm:
                case Devlworm:
                        return wormread(d, b, c);

                case Devfworm:
                        return fwormread(d, b, c);

                case Devmcat:
                        return mcatread(d, b, c);

                case Devmlev:
                        return mlevread(d, b, c);

                case Devmirr:
                        return mirrread(d, b, c);

                case Devpart:
                        return partread(d, b, c);

                case Devswab:
                        e = devread(d->swab.d, b, c);
                        if(e == 0)
                                swab(c, 0);
                        return e;

                case Devnone:
                        print("read from device none(%lld)\n", (Wideoff)b);
                        return 1;
                default:
                        panic("illegal device in devread: %Z %lld",
                                d, (Wideoff)b);
                        return 1;
                }
}

int
devwrite(Device *d, Off b, void *c)
{
        int e;

        /*
         * set readonly to non-0 to prevent all writes;
         * mainly for trying dangerous experiments.
         */
        if (readonly)
                return 0;
        for (;;)
                switch(d->type) {
                case Devcw:
                        return cwwrite(d, b, c);

                case Devjuke:
                        d = d->j.m;
                        break;

                case Devro:
                        print("write to ro device %Z(%lld)\n", d, (Wideoff)b);
                        return 1;

                case Devwren:
                        return wrenwrite(d, b, c);

                case Devworm:
                case Devlworm:
                        return wormwrite(d, b, c);

                case Devfworm:
                        return fwormwrite(d, b, c);

                case Devmcat:
                        return mcatwrite(d, b, c);

                case Devmlev:
                        return mlevwrite(d, b, c);

                case Devmirr:
                        return mirrwrite(d, b, c);

                case Devpart:
                        return partwrite(d, b, c);

                case Devswab:
                        swab(c, 1);
                        e = devwrite(d->swab.d, b, c);
                        swab(c, 0);
                        return e;

                case Devnone:
                        /* checktag() can generate blocks with type devnone */
                        return 0;
                default:
                        panic("illegal device in devwrite: %Z %lld",
                                d, (Wideoff)b);
                        return 1;
                }
}

Devsize
devsize(Device *d)
{
        for (;;)
                switch(d->type) {
                case Devcw:
                case Devro:
                        return cwsize(d);

                case Devjuke:
                        d = d->j.m;
                        break;

                case Devwren:
                        return wrensize(d);

                case Devworm:
                case Devlworm:
                        return wormsize(d);

                case Devfworm:
                        return fwormsize(d);

                case Devmcat:
                        return mcatsize(d);

                case Devmlev:
                        return mlevsize(d);

                case Devmirr:
                        return mirrsize(d);

                case Devpart:
                        return partsize(d);

                case Devswab:
                        d = d->swab.d;
                        break;
                default:
                        panic("illegal device in devsize: %Z", d);
                        return 0;
                }
}

/* result is malloced */
char *
sdof(Device *d)
{
        static char name[256];

        for (;;)
                switch(d->type) {
                case Devjuke:
                        d = d->j.j;             /* robotics */
                        break;
                case Devwren:
                        snprint(name, sizeof name, "/dev/sd%d%d", d->wren.ctrl,
                                d->wren.targ);
                        return strdup(name);
                case Devswab:
                        d = d->swab.d;
                        break;
                default:
                        panic("illegal device in sdof: %Z", d);
                        return nil;
                }
}

Off
superaddr(Device *d)
{
        for (;;)
                switch(d->type) {
                default:
                        return SUPER_ADDR;
                case Devcw:
                case Devro:
                        return cwsaddr(d);
                case Devswab:
                        d = d->swab.d;
                        break;
                }
}

Off
getraddr(Device *d)
{
        for (;;)
                switch(d->type) {
                default:
                        return ROOT_ADDR;
                case Devcw:
                case Devro:
                        return cwraddr(d);
                case Devswab:
                        d = d->swab.d;
                        break;
                }
}

void
devream(Device *d, int top)
{
        Device *l;

loop:
        print("\tdevream: %Z %d\n", d, top);
        switch(d->type) {
        default:
                print("ream: unknown dev type %Z\n", d);
                return;

        case Devcw:
                devream(d->cw.w, 0);
                devream(d->cw.c, 0);
                if(top) {
                        wlock(&mainlock);
                        cwream(d);
                        wunlock(&mainlock);
                }
                devinit(d);
                return;

        case Devfworm:
                devream(d->fw.fw, 0);
                fwormream(d);
                break;

        case Devpart:
                devream(d->part.d, 0);
                break;

        case Devmlev:
        case Devmcat:
        case Devmirr:
                for(l=d->cat.first; l; l=l->link)
                        devream(l, 0);
                break;

        case Devjuke:
        case Devworm:
        case Devlworm:
        case Devwren:
                break;

        case Devswab:
                d = d->swab.d;
                goto loop;
        }
        devinit(d);
        if(top) {
                wlock(&mainlock);
                rootream(d, ROOT_ADDR);
                superream(d, SUPER_ADDR);
                wunlock(&mainlock);
        }
}

void
devrecover(Device *d)
{
        for (;;) {
                print("recover: %Z\n", d);
                switch(d->type) {
                default:
                        print("recover: unknown dev type %Z\n", d);
                        return;

                case Devcw:
                        wlock(&mainlock);       /* recover */
                        cwrecover(d);
                        wunlock(&mainlock);
                        return;

                case Devswab:
                        d = d->swab.d;
                        break;
                }
        }
}

void
devinit(Device *d)
{
        for (;;) {
                if(d->init)
                        return;
                d->init = 1;
                print("\tdevinit %Z\n", d);
                switch(d->type) {
                default:
                        print("devinit unknown device %Z\n", d);
                        return;

                case Devro:
                        cwinit(d->ro.parent);
                        return;

                case Devcw:
                        cwinit(d);
                        return;

                case Devjuke:
                        jukeinit(d);
                        return;

                case Devwren:
                        wreninit(d);
                        return;

                case Devworm:
                case Devlworm:
                        return;

                case Devfworm:
                        fworminit(d);
                        return;

                case Devmcat:
                        mcatinit(d);
                        return;

                case Devmlev:
                        mlevinit(d);
                        return;

                case Devmirr:
                        mirrinit(d);
                        return;

                case Devpart:
                        partinit(d);
                        return;

                case Devswab:
                        d = d->swab.d;
                        break;

                case Devnone:
                        print("devinit of Devnone\n");
                        return;
                }
        }
}

void
swab2(void *c)
{
        uchar *p;
        int t;

        p = c;

        t = p[0];
        p[0] = p[1];
        p[1] = t;
}

void
swab4(void *c)
{
        uchar *p;
        int t;

        p = c;

        t = p[0];
        p[0] = p[3];
        p[3] = t;

        t = p[1];
        p[1] = p[2];
        p[2] = t;
}

void
swab8(void *c)
{
        uchar *p;
        int t;

        p = c;

        t = p[0];
        p[0] = p[7];
        p[7] = t;

        t = p[1];
        p[1] = p[6];
        p[6] = t;

        t = p[2];
        p[2] = p[5];
        p[5] = t;

        t = p[3];
        p[3] = p[4];
        p[4] = t;
}

/*
 * swab a block
 *      flag = 0 -- convert from foreign to native
 *      flag = 1 -- convert from native to foreign
 */
void
swab(void *c, int flag)
{
        uchar *p;
        Tag *t;
        int i, j;
        Dentry *d;
        Cache *h;
        Bucket *b;
        Superb *s;
        Fbuf *f;
        Off *l;

        /* swab the tag */
        p = (uchar*)c;
        t = (Tag*)(p + BUFSIZE);
        if(!flag) {
                swab2(&t->pad);
                swab2(&t->tag);
                swaboff(&t->path);
        }

        /* swab each block type */
        switch(t->tag) {

        default:
                print("no swab for tag=%G rw=%d\n", t->tag, flag);
                for(j=0; j<16; j++)
                        print(" %.2x", p[BUFSIZE+j]);
                print("\n");
                for(i=0; i<16; i++) {
                        print("%.4x", i*16);
                        for(j=0; j<16; j++)
                                print(" %.2x", p[i*16+j]);
                        print("\n");
                }
                panic("swab");
                break;

        case Tsuper:
                s = (Superb*)p;
                swaboff(&s->fbuf.nfree);
                for(i=0; i<FEPERBUF; i++)
                        swaboff(&s->fbuf.free[i]);
                swaboff(&s->fstart);
                swaboff(&s->fsize);
                swaboff(&s->tfree);
                swaboff(&s->qidgen);
                swaboff(&s->cwraddr);
                swaboff(&s->roraddr);
                swaboff(&s->last);
                swaboff(&s->next);
                break;

        case Tdir:
                for(i=0; i<DIRPERBUF; i++) {
                        d = (Dentry*)p + i;
                        swab2(&d->uid);
                        swab2(&d->gid);
                        swab2(&d->mode);
                        swab2(&d->muid);
                        swaboff(&d->qid.path);
                        swab4(&d->qid.version);
                        swaboff(&d->size);
                        for(j=0; j<NDBLOCK; j++)
                                swaboff(&d->dblock[j]);
                        for (j = 0; j < NIBLOCK; j++)
                                swaboff(&d->iblocks[j]);
                        swab4(&d->atime);
                        swab4(&d->mtime);
                }
                break;

        case Tind1:
        case Tind2:
#ifndef COMPAT32
        case Tind3:
        case Tind4:
        /* add more Tind tags here ... */
#endif
                l = (Off *)p;
                for(i=0; i<INDPERBUF; i++) {
                        swaboff(l);
                        l++;
                }
                break;

        case Tfree:
                f = (Fbuf*)p;
                swaboff(&f->nfree);
                for(i=0; i<FEPERBUF; i++)
                        swaboff(&f->free[i]);
                break;

        case Tbuck:
                for(i=0; i<BKPERBLK; i++) {
                        b = (Bucket*)p + i;
                        swab4(&b->agegen);
                        for(j=0; j<CEPERBK; j++) {
                                swab2(&b->entry[j].age);
                                swab2(&b->entry[j].state);
                                swaboff(&b->entry[j].waddr);
                        }
                }
                break;

        case Tcache:
                h = (Cache*)p;
                swaboff(&h->maddr);
                swaboff(&h->msize);
                swaboff(&h->caddr);
                swaboff(&h->csize);
                swaboff(&h->fsize);
                swaboff(&h->wsize);
                swaboff(&h->wmax);
                swaboff(&h->sbaddr);
                swaboff(&h->cwraddr);
                swaboff(&h->roraddr);
                swab4(&h->toytime);
                swab4(&h->time);
                break;

        case Tnone:     // unitialized
        case Tfile:     // someone elses problem
        case Tvirgo:    // bit map -- all bytes
        case Tconfig:   // configuration string -- all bytes
                break;
        }

        /* swab the tag */
        if(flag) {
                swab2(&t->pad);
                swab2(&t->tag);
                swaboff(&t->path);
        }
}