diff options
| author | Charles.Forsyth <devnull@localhost> | 2009-03-25 15:55:14 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2009-03-25 15:55:14 +0000 |
| commit | dfd1934d5e1ddbeb326f77fc0e52307c801a1a3e (patch) | |
| tree | f1e8b23278caae95e01d88b00421d6c3642357ef /emu/port/sysfile.c | |
| parent | 78dfdcbd59dc8f36975e7695933e3f753957474c (diff) | |
x20090325-1554
Diffstat (limited to 'emu/port/sysfile.c')
| -rw-r--r-- | emu/port/sysfile.c | 1089 |
1 files changed, 1089 insertions, 0 deletions
diff --git a/emu/port/sysfile.c b/emu/port/sysfile.c new file mode 100644 index 00000000..8159ae2d --- /dev/null +++ b/emu/port/sysfile.c @@ -0,0 +1,1089 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "kernel.h" + +static int +growfd(Fgrp *f, int fd) +{ + int n; + Chan **nfd, **ofd; + + if(fd < f->nfd) + return 0; + n = f->nfd+DELTAFD; + if(n > MAXNFD) + n = MAXNFD; + if(fd >= n) + return -1; + nfd = malloc(n*sizeof(Chan*)); + if(nfd == nil) + return -1; + ofd = f->fd; + memmove(nfd, ofd, f->nfd*sizeof(Chan *)); + f->fd = nfd; + f->nfd = n; + free(ofd); + return 0; +} + +static int +newfd(Chan *c) +{ + int i; + Fgrp *f = up->env->fgrp; + + lock(&f->l); + for(i=f->minfd; i<f->nfd; i++) + if(f->fd[i] == 0) + break; + if(i >= f->nfd && growfd(f, i) < 0){ + unlock(&f->l); + exhausted("file descriptors"); + return -1; + } + f->minfd = i + 1; + if(i > f->maxfd) + f->maxfd = i; + f->fd[i] = c; + unlock(&f->l); + return i; +} + +Chan* +fdtochan(Fgrp *f, int fd, int mode, int chkmnt, int iref) +{ + Chan *c; + + c = 0; + lock(&f->l); + if(fd<0 || f->maxfd<fd || (c = f->fd[fd])==0) { + unlock(&f->l); + error(Ebadfd); + } + if(iref) + incref(&c->r); + unlock(&f->l); + + if(chkmnt && (c->flag&CMSG)) + goto bad; + if(mode<0 || c->mode==ORDWR) + return c; + if((mode&OTRUNC) && c->mode==OREAD) + goto bad; + if((mode&~OTRUNC) != c->mode) + goto bad; + return c; +bad: + if(iref) + cclose(c); + error(Ebadusefd); + return nil; +} + +long +kchanio(void *vc, void *buf, int n, int mode) +{ + int r; + Chan *c; + + c = vc; + if(waserror()) + return -1; + + if(mode == OREAD) + r = devtab[c->type]->read(c, buf, n, c->offset); + else + r = devtab[c->type]->write(c, buf, n, c->offset); + + lock(&c->l); + c->offset += r; + unlock(&c->l); + poperror(); + return r; +} + +int +openmode(ulong o) +{ + if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC)) + error(Ebadarg); + o &= ~(OTRUNC|OCEXEC|ORCLOSE); + if(o > OEXEC) + error(Ebadarg); + if(o == OEXEC) + return OREAD; + return o; +} + +static void +fdclose(Fgrp *f, int fd) +{ + int i; + Chan *c; + + lock(&f->l); + c = f->fd[fd]; + if(c == 0) { + /* can happen for users with shared fd tables */ + unlock(&f->l); + return; + } + f->fd[fd] = 0; + if(fd == f->maxfd) + for(i=fd; --i>=0 && f->fd[i]==0; ) + f->maxfd = i; + if(fd < f->minfd) + f->minfd = fd; + unlock(&f->l); + cclose(c); +} + +int +kchdir(char *path) +{ + Chan *c; + Pgrp *pg; + + if(waserror()) + return -1; + + c = namec(path, Atodir, 0, 0); + pg = up->env->pgrp; + cclose(pg->dot); + pg->dot = c; + poperror(); + return 0; +} + +int +kfgrpclose(Fgrp *f, int fd) +{ + if(waserror()) + return -1; + + /* + * Take no reference on the chan because we don't really need the + * data structure, and are calling fdtochan only for error checks. + * fdclose takes care of processes racing through here. + */ + fdtochan(f, fd, -1, 0, 0); + fdclose(f, fd); + poperror(); + return 0; +} + +int +kclose(int fd) +{ + return kfgrpclose(up->env->fgrp, fd); +} + +int +kcreate(char *path, int mode, ulong perm) +{ + int fd; + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()) { + cclose(c.c); + return -1; + } + + openmode(mode&~OEXCL); /* error check only; OEXCL okay here */ + c.c = namec(path, Acreate, mode, perm); + fd = newfd(c.c); + if(fd < 0) + error(Enofd); + poperror(); + return fd; +} + +int +kdup(int old, int new) +{ + int fd; + Chan *oc; + Fgrp *f = up->env->fgrp; + volatile struct { Chan *c; } c; + + if(waserror()) + return -1; + + c.c = fdtochan(up->env->fgrp, old, -1, 0, 1); + if(c.c->qid.type & QTAUTH) + error(Eperm); + fd = new; + if(fd != -1) { + lock(&f->l); + if(fd < 0 || growfd(f, fd) < 0) { + unlock(&f->l); + cclose(c.c); + error(Ebadfd); + } + if(fd > f->maxfd) + f->maxfd = fd; + oc = f->fd[fd]; + f->fd[fd] = c.c; + unlock(&f->l); + if(oc != 0) + cclose(oc); + } + else { + if(waserror()) { + cclose(c.c); + nexterror(); + } + fd = newfd(c.c); + if(fd < 0) + error(Enofd); + poperror(); + } + poperror(); + return fd; +} + +int +kfstat(int fd, uchar *buf, int n) +{ + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()) { + cclose(c.c); + return -1; + } + c.c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + devtab[c.c->type]->stat(c.c, buf, n); + poperror(); + cclose(c.c); + return n; +} + +char* +kfd2path(int fd) +{ + Chan *c; + char *s; + + if(waserror()) + return nil; + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + s = nil; + if(c->name != nil){ + s = malloc(c->name->len+1); + if(s == nil){ + cclose(c); + error(Enomem); + } + memmove(s, c->name->s, c->name->len+1); + cclose(c); + } + poperror(); + return s; +} + +int +kfauth(int fd, char *aname) +{ + Chan *c, *ac; + + if(waserror()) + return -1; + + validname(aname, 0); + c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + + ac = mntauth(c, aname); + + /* at this point ac is responsible for keeping c alive */ + poperror(); /* c */ + cclose(c); + + if(waserror()){ + cclose(ac); + nexterror(); + } + + fd = newfd(ac); + if(fd < 0) + error(Enofd); + poperror(); /* ac */ + + poperror(); + + return fd; +} + +int +kfversion(int fd, uint msize, char *vers, uint arglen) +{ + int m; + Chan *c; + + if(waserror()) + return -1; + + /* check there's a NUL in the version string */ + if(arglen==0 || memchr(vers, 0, arglen)==0) + error(Ebadarg); + + c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + + m = mntversion(c, vers, msize, arglen); + + poperror(); + cclose(c); + + poperror(); + return m; +} + +int +kpipe(int fd[2]) +{ + Dev *d; + Fgrp *f; + Chan *c[2]; + static char *names[] = {"data", "data1"}; + + f = up->env->fgrp; + + d = devtab[devno('|', 0)]; + c[0] = namec("#|", Atodir, 0, 0); + c[1] = 0; + fd[0] = -1; + fd[1] = -1; + if(waserror()) { + if(c[0] != 0) + cclose(c[0]); + if(c[1] != 0) + cclose(c[1]); + if(fd[0] >= 0) + f->fd[fd[0]]=0; + if(fd[1] >= 0) + f->fd[fd[1]]=0; + return -1; + } + c[1] = cclone(c[0]); + if(walk(&c[0], &names[0], 1, 1, nil) < 0) + error(Egreg); + if(walk(&c[1], &names[1], 1, 1, nil) < 0) + error(Egreg); + c[0] = d->open(c[0], ORDWR); + c[1] = d->open(c[1], ORDWR); + fd[0] = newfd(c[0]); + if(fd[0] < 0) + error(Enofd); + fd[1] = newfd(c[1]); + if(fd[1] < 0) + error(Enofd); + poperror(); + return 0; +} + +int +kfwstat(int fd, uchar *buf, int n) +{ + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()) { + cclose(c.c); + return -1; + } + validstat(buf, n); + c.c = fdtochan(up->env->fgrp, fd, -1, 1, 1); + n = devtab[c.c->type]->wstat(c.c, buf, n); + poperror(); + cclose(c.c); + return n; +} + +long +bindmount(Chan *c, char *old, int flag, char *spec) +{ + int ret; + volatile struct { Chan *c; } c1; + + if(flag>MMASK || (flag&MORDER) == (MBEFORE|MAFTER)) + error(Ebadarg); + + c1.c = namec(old, Amount, 0, 0); + if(waserror()){ + cclose(c1.c); + nexterror(); + } + ret = cmount(c, c1.c, flag, spec); + + poperror(); + cclose(c1.c); + return ret; +} + +int +kbind(char *new, char *old, int flags) +{ + long r; + volatile struct { Chan *c; } c0; + + c0.c = nil; + if(waserror()) { + cclose(c0.c); + return -1; + } + c0.c = namec(new, Abind, 0, 0); + r = bindmount(c0.c, old, flags, ""); + poperror(); + cclose(c0.c); + return r; +} + +int +kmount(int fd, int afd, char *old, int flags, char *spec) +{ + long r; + volatile struct { Chan *c; } c0; + volatile struct { Chan *c; } bc; + volatile struct { Chan *c; } ac; + Mntparam mntparam; + + ac.c = nil; + bc.c = nil; + c0.c = nil; + if(waserror()) { + cclose(ac.c); + cclose(bc.c); + cclose(c0.c); + return -1; + } + bc.c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1); + if(afd >= 0) + ac.c = fdtochan(up->env->fgrp, afd, ORDWR, 0, 1); + mntparam.chan = bc.c; + mntparam.authchan = ac.c; + mntparam.spec = spec; + mntparam.flags = flags; + c0.c = devtab[devno('M', 0)]->attach((char*)&mntparam); + + r = bindmount(c0.c, old, flags, spec); + poperror(); + cclose(ac.c); + cclose(bc.c); + cclose(c0.c); + + return r; +} + +int +kunmount(char *old, char *new) +{ + volatile struct { Chan *c; } cmount; + volatile struct { Chan *c; } cmounted; + + cmount.c = nil; + cmounted.c = nil; + if(waserror()) { + cclose(cmount.c); + cclose(cmounted.c); + return -1; + } + + cmount.c = namec(new, Amount, 0, 0); + if(old != nil && old[0] != '\0') { + /* + * This has to be namec(..., Aopen, ...) because + * if arg[0] is something like /srv/cs or /fd/0, + * opening it is the only way to get at the real + * Chan underneath. + */ + cmounted.c = namec(old, Aopen, OREAD, 0); + } + + cunmount(cmount.c, cmounted.c); + poperror(); + cclose(cmount.c); + cclose(cmounted.c); + return 0; +} + +int +kopen(char *path, int mode) +{ + int fd; + volatile struct { Chan *c; } c; + + if(waserror()) + return -1; + + openmode(mode); /* error check only */ + c.c = namec(path, Aopen, mode, 0); + if(waserror()){ + cclose(c.c); + nexterror(); + } + fd = newfd(c.c); + if(fd < 0) + error(Enofd); + poperror(); + + poperror(); + return fd; +} + +long +unionread(Chan *c, void *va, long n) +{ + int i; + long nr; + Mhead *m; + Mount *mount; + + qlock(&c->umqlock); + m = c->umh; + rlock(&m->lock); + mount = m->mount; + /* bring mount in sync with c->uri and c->umc */ + for(i = 0; mount != nil && i < c->uri; i++) + mount = mount->next; + + nr = 0; + while(mount != nil) { + /* Error causes component of union to be skipped */ + if(mount->to && !waserror()) { + if(c->umc == nil){ + c->umc = cclone(mount->to); + c->umc = devtab[c->umc->type]->open(c->umc, OREAD); + } + + nr = devtab[c->umc->type]->read(c->umc, va, n, c->umc->offset); + if(nr < 0) + nr = 0; /* dev.c can return -1 */ + c->umc->offset += nr; + poperror(); + } + if(nr > 0) + break; + + /* Advance to next element */ + c->uri++; + if(c->umc) { + cclose(c->umc); + c->umc = nil; + } + mount = mount->next; + } + runlock(&m->lock); + qunlock(&c->umqlock); + return nr; +} + +static void +unionrewind(Chan *c) +{ + qlock(&c->umqlock); + c->uri = 0; + if(c->umc){ + cclose(c->umc); + c->umc = nil; + } + qunlock(&c->umqlock); +} + +static long +rread(int fd, void *va, long n, vlong *offp) +{ + int dir; + Lock *cl; + volatile struct { Chan *c; } c; + vlong off; + + if(waserror()) + return -1; + + c.c = fdtochan(up->env->fgrp, fd, OREAD, 1, 1); + if(waserror()){ + cclose(c.c); + nexterror(); + } + + if(n < 0) + error(Etoosmall); + + dir = c.c->qid.type & QTDIR; + if(dir && c.c->umh) + n = unionread(c.c, va, n); + else{ + cl = &c.c->l; + if(offp == nil){ + lock(cl); /* lock for vlong assignment */ + off = c.c->offset; + unlock(cl); + }else + off = *offp; + if(off < 0) + error(Enegoff); + if(off == 0){ + if(offp == nil){ + lock(cl); + c.c->offset = 0; + c.c->dri = 0; + unlock(cl); + } + unionrewind(c.c); + } + n = devtab[c.c->type]->read(c.c, va, n, off); + lock(cl); + c.c->offset += n; + unlock(cl); + } + + poperror(); + cclose(c.c); + + poperror(); + return n; +} + +long +kread(int fd, void *va, long n) +{ + return rread(fd, va, n, nil); +} + +long +kpread(int fd, void *va, long n, vlong off) +{ + return rread(fd, va, n, &off); +} + +int +kremove(char *path) +{ + volatile struct { Chan *c; } c; + + if(waserror()) + return -1; + + c.c = namec(path, Aremove, 0, 0); + if(waserror()){ + c.c->type = 0; /* see below */ + cclose(c.c); + nexterror(); + } + devtab[c.c->type]->remove(c.c); + /* + * Remove clunks the fid, but we need to recover the Chan + * so fake it up. rootclose() is known to be a nop. + */ + c.c->type = 0; + poperror(); + cclose(c.c); + + poperror(); + return 0; +} + +vlong +kseek(int fd, vlong off, int whence) +{ + Dir *dir; + Chan *c; + + if(waserror()) + return -1; + + c = fdtochan(up->env->fgrp, fd, -1, 1, 1); + if(waserror()) { + cclose(c); + nexterror(); + } + + if(devtab[c->type]->dc == '|') + error(Eisstream); + + switch(whence) { + case 0: + if(c->qid.type & QTDIR){ + if(off != 0) + error(Eisdir); + unionrewind(c); + }else if(off < 0) + error(Enegoff); + lock(&c->l); /* lock for vlong assignment */ + c->offset = off; + unlock(&c->l); + break; + + case 1: + if(c->qid.type & QTDIR) + error(Eisdir); + lock(&c->l); /* lock for read/write update */ + off += c->offset; + if(off < 0){ + unlock(&c->l); + error(Enegoff); + } + c->offset = off; + unlock(&c->l); + break; + + case 2: + if(c->qid.type & QTDIR) + error(Eisdir); + dir = chandirstat(c); + if(dir == nil) + error("internal error: stat error in seek"); + off += dir->length; + free(dir); + if(off < 0) + error(Enegoff); + lock(&c->l); /* lock for read/write update */ + c->offset = off; + unlock(&c->l); + break; + + default: + error(Ebadarg); + break; + } + poperror(); + c->dri = 0; + cclose(c); + poperror(); + return off; +} + +void +validstat(uchar *s, int n) +{ + int m; + char buf[64]; + + if(statcheck(s, n) < 0) + error(Ebadstat); + /* verify that name entry is acceptable */ + s += STATFIXLEN - 4*BIT16SZ; /* location of first string */ + /* + * s now points at count for first string. + * if it's too long, let the server decide; this is + * only for his protection anyway. otherwise + * we'd have to allocate and waserror. + */ + m = GBIT16(s); + s += BIT16SZ; + if(m+1 > sizeof buf) + return; + memmove(buf, s, m); + buf[m] = '\0'; + /* name could be '/' */ + if(strcmp(buf, "/") != 0) + validname(buf, 0); +} + +int +kstat(char *path, uchar *buf, int n) +{ + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()){ + cclose(c.c); + return -1; + } + c.c = namec(path, Aaccess, 0, 0); + devtab[c.c->type]->stat(c.c, buf, n); + poperror(); + cclose(c.c); + return 0; +} + +static long +rwrite(int fd, void *va, long n, vlong *offp) +{ + Lock *cl; + volatile struct { Chan *c; } c; + vlong off; + long m; + + if(waserror()) + return -1; + c.c = fdtochan(up->env->fgrp, fd, OWRITE, 1, 1); + if(waserror()){ + cclose(c.c); + nexterror(); + } + if(c.c->qid.type & QTDIR) + error(Eisdir); + + if(n < 0) + error(Etoosmall); + + cl = &c.c->l; + if(offp == nil){ + lock(cl); + off = c.c->offset; + c.c->offset += n; + unlock(cl); + }else + off = *offp; + + if(waserror()){ + if(offp == nil){ + lock(cl); + c.c->offset -= n; + unlock(cl); + } + nexterror(); + } + if(off < 0) + error(Enegoff); + m = devtab[c.c->type]->write(c.c, va, n, off); + poperror(); + + if(offp == nil && m < n){ + lock(cl); + c.c->offset -= n - m; + unlock(cl); + } + + poperror(); + cclose(c.c); + + poperror(); + return m; +} + +long +kwrite(int fd, void *va, long n) +{ + return rwrite(fd, va, n, nil); +} + +long +kpwrite(int fd, void *va, long n, vlong off) +{ + return rwrite(fd, va, n, &off); +} + +int +kwstat(char *path, uchar *buf, int n) +{ + volatile struct { Chan *c; } c; + + c.c = nil; + if(waserror()){ + cclose(c.c); + return -1; + } + validstat(buf, n); + c.c = namec(path, Aaccess, 0, 0); + n = devtab[c.c->type]->wstat(c.c, buf, n); + poperror(); + cclose(c.c); + return n; +} + +enum +{ + DIRSIZE = STATFIXLEN + 32 * 4, + DIRREADLIM = 2048, /* should handle the largest reasonable directory entry */ +}; + +Dir* +chandirstat(Chan *c) +{ + Dir *d; + uchar *buf; + int n, nd, i; + + nd = DIRSIZE; + for(i=0; i<2; i++){ /* should work by the second try */ + d = smalloc(sizeof(Dir) + nd); + buf = (uchar*)&d[1]; + if(waserror()){ + free(d); + return nil; + } + n = devtab[c->type]->stat(c, buf, nd); + poperror(); + if(n < BIT16SZ){ + free(d); + return nil; + } + nd = GBIT16((uchar*)buf) + BIT16SZ; /* size needed to store whole stat buffer including count */ + if(nd <= n){ + convM2D(buf, n, d, (char*)&d[1]); + return d; + } + /* else sizeof(Dir)+nd is plenty */ + free(d); + } + return nil; + +} + +Dir* +kdirstat(char *name) +{ + volatile struct {Chan *c;} c; + Dir *d; + + c.c = nil; + if(waserror()){ + cclose(c.c); + return nil; + } + c.c = namec(name, Aaccess, 0, 0); + d = chandirstat(c.c); + poperror(); + cclose(c.c); + return d; +} + +Dir* +kdirfstat(int fd) +{ + volatile struct { Chan *c; } c; + Dir *d; + + c.c = nil; + if(waserror()) { + cclose(c.c); + return nil; + } + c.c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + d = chandirstat(c.c); + poperror(); + cclose(c.c); + return d; +} + +int +kdirwstat(char *name, Dir *dir) +{ + uchar *buf; + int r; + + r = sizeD2M(dir); + buf = smalloc(r); + convD2M(dir, buf, r); + r = kwstat(name, buf, r); + free(buf); + return r < 0? r: 0; +} + +int +kdirfwstat(int fd, Dir *dir) +{ + uchar *buf; + int r; + + r = sizeD2M(dir); + buf = smalloc(r); + convD2M(dir, buf, r); + r = kfwstat(fd, buf, r); + free(buf); + return r < 0? r: 0; +} + +static long +dirpackage(uchar *buf, long ts, Dir **d) +{ + char *s; + long ss, i, n, nn, m; + + *d = nil; + if(ts <= 0) + return ts; + + /* + * first find number of all stats, check they look like stats, & size all associated strings + */ + ss = 0; + n = 0; + for(i = 0; i < ts; i += m){ + m = BIT16SZ + GBIT16(&buf[i]); + if(statcheck(&buf[i], m) < 0) + break; + ss += m; + n++; + } + + if(i != ts) + error("bad directory format"); + + *d = malloc(n * sizeof(Dir) + ss); + if(*d == nil) + error(Enomem); + + /* + * then convert all buffers + */ + s = (char*)*d + n * sizeof(Dir); + nn = 0; + for(i = 0; i < ts; i += m){ + m = BIT16SZ + GBIT16((uchar*)&buf[i]); + if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){ + free(*d); + *d = nil; + error("bad directory entry"); + } + nn++; + s += m; + } + + return nn; +} + +long +kdirread(int fd, Dir **d) +{ + uchar *buf; + long ts; + + *d = nil; + if(waserror()) + return -1; + buf = malloc(DIRREADLIM); + if(buf == nil) + error(Enomem); + if(waserror()){ + free(buf); + nexterror(); + } + ts = kread(fd, buf, DIRREADLIM); + if(ts > 0) + ts = dirpackage(buf, ts, d); + + poperror(); + free(buf); + poperror(); + return ts; +} + +int +kiounit(int fd) +{ + Chan *c; + int n; + + c = fdtochan(up->env->fgrp, fd, -1, 0, 1); + if(waserror()){ + cclose(c); + return 0; /* n.b. */ + } + n = c->iounit; + poperror(); + cclose(c); + return n; +} |
