conterm/kern/sysfile.c

1245 lines
18 KiB
C

#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; fd<f->nfd; 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, "<null>");
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;
}