#include "u.h" #include "lib.h" #include "dat.h" #include "fns.h" #include "error.h" #include "user.h" #undef open #undef mount #undef read #undef write #undef seek #undef stat #undef wstat #undef remove #undef close #undef fstat #undef fwstat /* * The sys*() routines needn't poperror() as they return directly to syscall(). */ static void unlockfgrp(Fgrp *f) { int ex; ex = f->exceed; f->exceed = 0; unlock(&f->ref.lk); if(ex) pprint("warning: process exceeds %d file descriptors\n", ex); } int growfd(Fgrp *f, int fd) /* fd is always >= 0 */ { Chan **newfd, **oldfd; if(fd < f->nfd) return 0; if(fd >= f->nfd+DELTAFD) return -1; /* out of range */ /* * Unbounded allocation is unwise; besides, there are only 16 bits * of fid in 9P */ if(f->nfd >= 5000){ Exhausted: print("no free file descriptors\n"); return -1; } newfd = malloc((f->nfd+DELTAFD)*sizeof(Chan*)); if(newfd == 0) goto Exhausted; oldfd = f->fd; memmove(newfd, oldfd, f->nfd*sizeof(Chan*)); f->fd = newfd; free(oldfd); f->nfd += DELTAFD; if(fd > f->maxfd){ if(fd/100 > f->maxfd/100) f->exceed = (fd/100)*100; f->maxfd = fd; } return 1; } /* * this assumes that the fgrp is locked */ int findfreefd(Fgrp *f, int start) { int fd; for(fd=start; fdnfd; fd++) if(f->fd[fd] == 0) break; if(fd >= f->nfd && growfd(f, fd) < 0) return -1; return fd; } int newfd(Chan *c) { int fd; Fgrp *f; f = up->fgrp; lock(&f->ref.lk); fd = findfreefd(f, 0); if(fd < 0){ unlockfgrp(f); return -1; } if(fd > f->maxfd) f->maxfd = fd; f->fd[fd] = c; unlockfgrp(f); return fd; } int newfd2(int fd[2], Chan *c[2]) { Fgrp *f; f = up->fgrp; lock(&f->ref.lk); fd[0] = findfreefd(f, 0); if(fd[0] < 0){ unlockfgrp(f); return -1; } fd[1] = findfreefd(f, fd[0]+1); if(fd[1] < 0){ unlockfgrp(f); return -1; } if(fd[1] > f->maxfd) f->maxfd = fd[1]; f->fd[fd[0]] = c[0]; f->fd[fd[1]] = c[1]; unlockfgrp(f); return 0; } Chan* fdtochan(int fd, int mode, int chkmnt, int iref) { Chan *c; Fgrp *f; c = 0; f = up->fgrp; lock(&f->ref.lk); if(fd<0 || f->nfd<=fd || (c = f->fd[fd])==0) { unlock(&f->ref.lk); error(Ebadfd); } if(iref) incref(&c->ref); unlock(&f->ref.lk); if(chkmnt && (c->flag&CMSG)) { if(iref) cclose(c); error(Ebadusefd); } if(mode<0 || c->mode==ORDWR) return c; if((mode&OTRUNC) && c->mode==OREAD) { if(iref) cclose(c); error(Ebadusefd); } if((mode&~OTRUNC) != c->mode) { if(iref) cclose(c); error(Ebadusefd); } return c; } int openmode(ulong o) { o &= ~(OTRUNC|OCEXEC|ORCLOSE); if(o > OEXEC) error(Ebadarg); if(o == OEXEC) return OREAD; return o; } long _sysfd2path(int fd, char *buf, uint nbuf) { Chan *c; c = fdtochan(fd, -1, 0, 1); if(c->name == nil) snprint(buf, nbuf, ""); else snprint(buf, nbuf, "%s", c->name->s); cclose(c); return 0; } long _syspipe(int fd[2]) { Chan *c[2]; Dev *d; static char *datastr[] = {"data", "data1"}; d = devtab[devno('|', 0)]; c[0] = namec("#|", Atodir, 0, 0); c[1] = 0; fd[0] = -1; fd[1] = -1; if(waserror()){ cclose(c[0]); if(c[1]) cclose(c[1]); nexterror(); } c[1] = cclone(c[0]); if(walk(&c[0], datastr+0, 1, 1, nil) < 0) error(Egreg); if(walk(&c[1], datastr+1, 1, 1, nil) < 0) error(Egreg); c[0] = d->open(c[0], ORDWR); c[1] = d->open(c[1], ORDWR); if(newfd2(fd, c) < 0) error(Enofd); poperror(); return 0; } long _sysdup(int fd0, int fd1) { int fd; Chan *c, *oc; Fgrp *f = up->fgrp; /* * Close after dup'ing, so date > #d/1 works */ c = fdtochan(fd0, -1, 0, 1); fd = fd1; if(fd != -1){ lock(&f->ref.lk); if(fd<0 || growfd(f, fd)<0) { unlockfgrp(f); cclose(c); error(Ebadfd); } if(fd > f->maxfd) f->maxfd = fd; oc = f->fd[fd]; f->fd[fd] = c; unlockfgrp(f); if(oc) cclose(oc); }else{ if(waserror()) { cclose(c); nexterror(); } fd = newfd(c); if(fd < 0) error(Enofd); poperror(); } return fd; } long _sysopen(char *name, int mode) { int fd; Chan *c = 0; openmode(mode); /* error check only */ if(waserror()){ if(c) cclose(c); nexterror(); } c = namec(name, Aopen, mode, 0); fd = newfd(c); if(fd < 0) error(Enofd); poperror(); return fd; } void fdclose(int fd, int flag) { int i; Chan *c; Fgrp *f = up->fgrp; lock(&f->ref.lk); c = f->fd[fd]; if(c == 0){ /* can happen for users with shared fd tables */ unlock(&f->ref.lk); return; } if(flag){ if(c==0 || !(c->flag&flag)){ unlock(&f->ref.lk); return; } } f->fd[fd] = 0; if(fd == f->maxfd) for(i=fd; --i>=0 && f->fd[i]==0; ) f->maxfd = i; unlock(&f->ref.lk); cclose(c); } long _sysclose(int fd) { fdtochan(fd, -1, 0, 0); fdclose(fd, 0); return 0; } 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); 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 long kread(int fd, void *buf, long n, vlong *offp) { int dir; Chan *c; vlong off; c = fdtochan(fd, OREAD, 1, 1); if(waserror()) { cclose(c); nexterror(); } dir = c->qid.type&QTDIR; /* * The offset is passed through on directories, normally. sysseek complains but * pread is used by servers and e.g. exportfs that shouldn't need to worry about this issue. */ if(offp == nil) /* use and maintain channel's offset */ off = c->offset; else off = *offp; if(off < 0) error(Enegoff); if(dir && c->umh) n = unionread(c, buf, n); else n = devtab[c->type]->read(c, buf, n, off); if(offp == nil){ lock(&c->ref.lk); c->offset += n; unlock(&c->ref.lk); } poperror(); cclose(c); return n; } /* name conflicts with netbsd long _sys_read(int fd, void *buf, long n) { return kread(fd, buf, n, nil); } */ long _syspread(int fd, void *buf, long n, vlong off) { if(off == ((uvlong) ~0)) return kread(fd, buf, n, nil); return kread(fd, buf, n, &off); } static long kwrite(int fd, void *buf, long nn, vlong *offp) { Chan *c; long m, n; vlong off; n = 0; c = fdtochan(fd, OWRITE, 1, 1); if(waserror()) { if(offp == nil){ lock(&c->ref.lk); c->offset -= n; unlock(&c->ref.lk); } cclose(c); nexterror(); } if(c->qid.type & QTDIR) error(Eisdir); n = nn; if(offp == nil){ /* use and maintain channel's offset */ lock(&c->ref.lk); off = c->offset; c->offset += n; unlock(&c->ref.lk); }else off = *offp; if(off < 0) error(Enegoff); m = devtab[c->type]->write(c, buf, n, off); if(offp == nil && m < n){ lock(&c->ref.lk); c->offset -= n - m; unlock(&c->ref.lk); } poperror(); cclose(c); return m; } long sys_write(int fd, void *buf, long n) { return kwrite(fd, buf, n, nil); } long _syspwrite(int fd, void *buf, long n, vlong off) { if(off == ((uvlong) ~0)) return kwrite(fd, buf, n, nil); return kwrite(fd, buf, n, &off); } static vlong _sysseek(int fd, vlong off, int whence) { Chan *c; uchar buf[sizeof(Dir)+100]; Dir dir; int n; c = fdtochan(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) && off != 0) error(Eisdir); if(off < 0) error(Enegoff); c->offset = off; break; case 1: if(c->qid.type & QTDIR) error(Eisdir); lock(&c->ref.lk); /* lock for read/write update */ off = off + c->offset; if(off < 0) error(Enegoff); c->offset = off; unlock(&c->ref.lk); break; case 2: if(c->qid.type & QTDIR) error(Eisdir); n = devtab[c->type]->stat(c, buf, sizeof buf); if(convM2D(buf, n, &dir, nil) == 0) error("internal error: stat error in seek"); off = dir.length + off; if(off < 0) error(Enegoff); c->offset = off; break; default: error(Ebadarg); } c->uri = 0; 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); } long _sysfstat(int fd, void *buf, long n) { Chan *c; uint l; l = n; validaddr(buf, l, 1); c = fdtochan(fd, -1, 0, 1); if(waserror()) { cclose(c); nexterror(); } l = devtab[c->type]->stat(c, buf, l); poperror(); cclose(c); return l; } long _sysstat(char *name, void *buf, long n) { Chan *c; uint l; l = n; validaddr(buf, l, 1); validaddr(name, 1, 0); c = namec(name, Aaccess, 0, 0); if(waserror()){ cclose(c); nexterror(); } l = devtab[c->type]->stat(c, buf, l); poperror(); cclose(c); return l; } long _syschdir(char *name) { Chan *c; validaddr(name, 1, 0); c = namec(name, Atodir, 0, 0); cclose(up->dot); up->dot = c; return 0; } long bindmount(int ismount, int fd, int afd, char* arg0, char* arg1, ulong flag, char* spec) { int ret; Chan *c0, *c1, *ac, *bc; struct{ Chan *chan; Chan *authchan; char *spec; int flags; }bogus; if((flag&~MMASK) || (flag&MORDER)==(MBEFORE|MAFTER)) error(Ebadarg); bogus.flags = flag & MCACHE; if(ismount){ if(up->pgrp->noattach) error(Enoattach); ac = nil; bc = fdtochan(fd, ORDWR, 0, 1); if(waserror()) { if(ac) cclose(ac); cclose(bc); nexterror(); } if(afd >= 0) ac = fdtochan(afd, ORDWR, 0, 1); bogus.chan = bc; bogus.authchan = ac; validaddr((ulong)spec, 1, 0); bogus.spec = spec; if(waserror()) error(Ebadspec); validname(spec, 1); poperror(); ret = devno('M', 0); c0 = devtab[ret]->attach((char*)&bogus); poperror(); if(ac) cclose(ac); cclose(bc); }else{ bogus.spec = 0; validaddr((ulong)arg0, 1, 0); c0 = namec(arg0, Abind, 0, 0); } if(waserror()){ cclose(c0); nexterror(); } validaddr((ulong)arg1, 1, 0); c1 = namec(arg1, Amount, 0, 0); if(waserror()){ cclose(c1); nexterror(); } ret = cmount(&c0, c1, flag, bogus.spec); poperror(); cclose(c1); poperror(); cclose(c0); if(ismount) fdclose(fd, 0); return ret; } long _sysbind(char *old, char *new, int flag) { return bindmount(0, -1, -1, old, new, flag, nil); } long _sysmount(int fd, int afd, char *new, int flag, char *spec) { return bindmount(1, fd, afd, nil, new, flag, spec); } long _sysunmount(char *old, char *new) { Chan *cmount, *cmounted; cmounted = 0; cmount = namec(new, Amount, 0, 0); if(old) { if(waserror()) { cclose(cmount); nexterror(); } validaddr(old, 1, 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 = namec(old, Aopen, OREAD, 0); poperror(); } if(waserror()) { cclose(cmount); if(cmounted) cclose(cmounted); nexterror(); } cunmount(cmount, cmounted); cclose(cmount); if(cmounted) cclose(cmounted); poperror(); return 0; } long _syscreate(char *name, int mode, ulong perm) { int fd; Chan *c = 0; openmode(mode&~OEXCL); /* error check only; OEXCL okay here */ if(waserror()) { if(c) cclose(c); nexterror(); } validaddr(name, 1, 0); c = namec(name, Acreate, mode, perm); fd = newfd(c); if(fd < 0) error(Enofd); poperror(); return fd; } long _sysremove(char *name) { Chan *c; c = namec(name, Aremove, 0, 0); if(waserror()){ c->type = 0; /* see below */ cclose(c); nexterror(); } devtab[c->type]->remove(c); /* * Remove clunks the fid, but we need to recover the Chan * so fake it up. rootclose() is known to be a nop. */ c->type = 0; poperror(); cclose(c); return 0; } long _syswstat(char *name, void *buf, long n) { Chan *c; uint l; l = n; validstat(buf, l); validaddr(name, 1, 0); c = namec(name, Aaccess, 0, 0); if(waserror()){ cclose(c); nexterror(); } l = devtab[c->type]->wstat(c, buf, l); poperror(); cclose(c); return l; } long _sysfwstat(int fd, void *buf, long n) { Chan *c; uint l; l = n; validaddr(buf, l, 0); validstat(buf, l); c = fdtochan(fd, -1, 1, 1); if(waserror()) { cclose(c); nexterror(); } l = devtab[c->type]->wstat(c, buf, l); poperror(); cclose(c); return l; } static void starterror(void) { assert(up->nerrlab == 0); } static void _syserror(void) { char *p; p = up->syserrstr; up->syserrstr = up->errstr; up->errstr = p; } static void enderror(void) { assert(up->nerrlab == 1); poperror(); } int sysbind(char *old, char *new, int flag) { int n; starterror(); if(waserror()){ _syserror(); return -1; } n = _sysbind(old, new, flag); enderror(); return n; } int syschdir(char *path) { int n; starterror(); if(waserror()){ _syserror(); return -1; } n = _syschdir(path); enderror(); return n; } int sysclose(int fd) { int n; starterror(); if(waserror()){ _syserror(); return -1; } n = _sysclose(fd); enderror(); return n; } int syscreate(char *name, int mode, ulong perm) { int n; starterror(); if(waserror()){ _syserror(); return -1; } n = _syscreate(name, mode, perm); enderror(); return n; } int sysdup(int fd0, int fd1) { int n; starterror(); if(waserror()){ _syserror(); return -1; } n = _sysdup(fd0, fd1); enderror(); return n; } int sysfstat(int fd, uchar *buf, int n) { starterror(); if(waserror()){ _syserror(); return -1; } n = _sysfstat(fd, buf, n); enderror(); return n; } int sysfwstat(int fd, uchar *buf, int n) { starterror(); if(waserror()){ _syserror(); return -1; } n = _sysfwstat(fd, buf, n); enderror(); return n; } int sysmount(int fd, int afd, char *new, int flag, char *spec) { int n; starterror(); if(waserror()){ _syserror(); return -1; } n = _sysmount(fd, afd, new, flag, spec); enderror(); return n; } int sysunmount(char *old, char *new) { int n; starterror(); if(waserror()){ _syserror(); return -1; } n = _sysunmount(old, new); enderror(); return n; } int sysopen(char *name, int mode) { int n; starterror(); if(waserror()){ _syserror(); return -1; } n = _sysopen(name, mode); enderror(); return n; } int syspipe(int *fd) { int n; starterror(); if(waserror()){ _syserror(); return -1; } n = _syspipe(fd); enderror(); return n; } long syspread(int fd, void *buf, long n, vlong off) { starterror(); if(waserror()){ _syserror(); return -1; } n = _syspread(fd, buf, n, off); enderror(); return n; } long syspwrite(int fd, void *buf, long n, vlong off) { starterror(); if(waserror()){ _syserror(); return -1; } n = _syspwrite(fd, buf, n, off); enderror(); return n; } long sysread(int fd, void *buf, long n) { starterror(); if(waserror()){ _syserror(); return -1; } n = _syspread(fd, buf, n, (uvlong) ~0); enderror(); return n; } int sysremove(char *path) { int n; starterror(); if(waserror()){ _syserror(); return -1; } n = _sysremove(path); enderror(); return n; } vlong sysseek(int fd, vlong off, int whence) { starterror(); if(waserror()){ _syserror(); return -1; } off = _sysseek(fd, off, whence); enderror(); return off; } int sysstat(char *name, uchar *buf, int n) { starterror(); if(waserror()){ _syserror(); return -1; } n = _sysstat(name, buf, n); enderror(); return n; } long syswrite(int fd, void *buf, long n) { starterror(); if(waserror()){ _syserror(); return -1; } n = _syspwrite(fd, buf, n, (uvlong) ~0); enderror(); return n; } int syswstat(char *name, uchar *buf, int n) { starterror(); if(waserror()){ _syserror(); return -1; } n = _syswstat(name, buf, n); enderror(); return n; } void werrstr(char *f, ...) { char buf[ERRMAX]; va_list arg; va_start(arg, f); vsnprint(buf, sizeof buf, f, arg); va_end(arg); if(up->nerrlab) strecpy(up->errstr, up->errstr+ERRMAX, buf); else strecpy(up->syserrstr, up->syserrstr+ERRMAX, buf); } int __errfmt(Fmt *fmt) { if(up->nerrlab) return fmtstrcpy(fmt, up->errstr); else return fmtstrcpy(fmt, up->syserrstr); } int errstr(char *buf, uint n) { char tmp[ERRMAX]; char *p; p = up->nerrlab ? up->errstr : up->syserrstr; memmove(tmp, p, ERRMAX); utfecpy(p, p+ERRMAX, buf); utfecpy(buf, buf+n, tmp); return strlen(buf); } int rerrstr(char *buf, uint n) { char *p; p = up->nerrlab ? up->errstr : up->syserrstr; utfecpy(buf, buf+n, p); return strlen(buf); } void* _sysrendezvous(void* arg0, void* arg1) { void *tag, *val; Proc *p, **l; tag = arg0; l = &REND(up->rgrp, (uintptr)tag); up->rendval = (void*)~0; lock(&up->rgrp->ref.lk); for(p = *l; p; p = p->rendhash) { if(p->rendtag == tag) { *l = p->rendhash; val = p->rendval; p->rendval = arg1; while(p->mach != 0) ; procwakeup(p); unlock(&up->rgrp->ref.lk); return val; } l = &p->rendhash; } /* Going to sleep here */ up->rendtag = tag; up->rendval = arg1; up->rendhash = *l; *l = up; up->state = Rendezvous; unlock(&up->rgrp->ref.lk); procsleep(); return up->rendval; } void* sysrendezvous(void *tag, void *val) { void *n; starterror(); if(waserror()){ _syserror(); return (void*)~0; } n = _sysrendezvous(tag, val); enderror(); return n; }