ref: 313cf4107eb9ebb2e2b7b6c8f61b93eac0b19a11
dir: /kern/syscall.c/
#include "u.h" #include "lib.h" #include "dat.h" #include "fns.h" #include "error.h" Chan* fdtochan(int fd, int mode, int chkmnt, int iref) { Fgrp *f; Chan *c; c = 0; f = up->fgrp; lock(&f->ref.lk); if(fd<0 || NFD<=fd || (c = f->fd[fd])==0) { unlock(&f->ref.lk); error(Ebadfd); } if(iref) refinc(&c->ref); unlock(&f->ref.lk); 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; /* shut up compiler */ } static void fdclose(int fd, int flag) { int i; Chan *c; Fgrp *f; f = up->fgrp; lock(&f->ref.lk); c = f->fd[fd]; if(c == 0) { 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); } static int newfd(Chan *c) { int i; Fgrp *f; f = up->fgrp; lock(&f->ref.lk); for(i=0; i<NFD; i++) if(f->fd[i] == 0){ if(i > f->maxfd) f->maxfd = i; f->fd[i] = c; unlock(&f->ref.lk); return i; } unlock(&f->ref.lk); error("no file descriptors"); return 0; } int sysclose(int fd) { if(waserror()) return -1; fdtochan(fd, -1, 0, 0); fdclose(fd, 0); poperror(); return 0; } int syscreate(char *path, int mode, ulong perm) { int fd; Chan *c = 0; if(waserror()) { cclose(c); return -1; } openmode(mode); /* error check only */ c = namec(path, Acreate, mode, perm); fd = newfd((Chan*)c); poperror(); return fd; } int sysdup(int old, int new) { Chan *oc; Fgrp *f = up->fgrp; Chan *c = 0; if(waserror()) return -1; c = fdtochan(old, -1, 0, 1); if(new != -1) { if(new < 0 || NFD <= new) { cclose(c); error(Ebadfd); } lock(&f->ref.lk); if(new > f->maxfd) f->maxfd = new; oc = f->fd[new]; f->fd[new] = (Chan*)c; unlock(&f->ref.lk); if(oc != 0) cclose(oc); } else { if(waserror()) { cclose(c); nexterror(); } new = newfd((Chan*)c); poperror(); } poperror(); return new; } int sysfstat(int fd, char *buf) { Chan *c = 0; if(waserror()) { cclose(c); return -1; } c = fdtochan(fd, -1, 0, 1); devtab[c->type]->stat((Chan*)c, buf); poperror(); cclose(c); return 0; } int sysfwstat(int fd, char *buf) { Chan *c = 0; if(waserror()) { cclose(c); return -1; } nameok(buf); c = fdtochan(fd, -1, 1, 1); devtab[c->type]->wstat((Chan*)c, buf); poperror(); cclose(c); return 0; } int syschdir(char *dir) { return 0; } long bindmount(Chan *c0, char *old, int flag, char *spec) { int ret; Chan *c1 = 0; if(flag>MMASK || (flag&MORDER) == (MBEFORE|MAFTER)) error(Ebadarg); c1 = namec(old, Amount, 0, 0); if(waserror()){ cclose(c1); nexterror(); } ret = cmount(c0, c1, flag, spec); poperror(); cclose(c1); return ret; } int sysbind(char *new, char *old, int flags) { long r; Chan *c0 = 0; if(waserror()) { cclose(c0); return -1; } c0 = namec(new, Aaccess, 0, 0); r = bindmount(c0, old, flags, ""); poperror(); cclose(c0); return 0; } int sysmount(int fd, char *old, int flags, char *spec) { long r; Chan *c0 = 0, *bc = 0; struct { Chan* chan; char* spec; int flags; } mntparam; if(waserror()) { cclose(bc); cclose(c0); return -1; } bc = fdtochan(fd, ORDWR, 0, 1); mntparam.chan = (Chan*)bc; mntparam.spec = spec; mntparam.flags = flags; c0 = (*devtab[devno('M', 0)].attach)(&mntparam); cclose(bc); r = bindmount(c0, old, flags, spec); poperror(); cclose(c0); return r; } int sysunmount(char *old, char *new) { Chan *cmount = 0, *cmounted = 0; if(waserror()) { cclose(cmount); cclose(cmounted); return -1; } cmount = namec(new, Amount, OREAD, 0); if(old != 0) cmounted = namec(old, Aopen, OREAD, 0); cunmount(cmount, cmounted); poperror(); cclose(cmount); cclose(cmounted); return 0; } int sysopen(char *path, int mode) { int fd; Chan *c = 0; if(waserror()){ cclose(c); return -1; } openmode(mode); /* error check only */ c = namec(path, Aopen, mode, 0); fd = newfd((Chan*)c); poperror(); return fd; } long unionread(Chan *c, void *va, long n) { long nr; Chan *nc = 0; Pgrp *pg = 0; pg = up->pgrp; rlock(&pg->ns); for(;;) { if(waserror()) { runlock(&pg->ns); nexterror(); } nc = clone(c->mnt->to, 0); poperror(); if(c->mountid != c->mnt->mountid) { runlock(&pg->ns); cclose(nc); return 0; } /* Error causes component of union to be skipped */ if(waserror()) { cclose(nc); goto next; } nc = (*devtab[nc->type].open)((Chan*)nc, OREAD); nc->offset = c->offset; nr = (*devtab[nc->type].read)((Chan*)nc, va, n, nc->offset); /* devdirread e.g. changes it */ c->offset = nc->offset; poperror(); cclose(nc); if(nr > 0) { runlock(&pg->ns); return nr; } /* Advance to next element */ next: c->mnt = c->mnt->next; if(c->mnt == 0) break; c->mountid = c->mnt->mountid; c->offset = 0; } runlock(&pg->ns); return 0; } long sysread(int fd, void *va, long n) { int dir; Lock *cl; Chan *c = 0; if(waserror()) { cclose(c); return -1; } c = fdtochan(fd, OREAD, 1, 1); dir = c->qid.path&CHDIR; if(dir) { n -= n%DIRLEN; if(c->offset%DIRLEN || n==0) error(Etoosmall); } if(dir && c->mnt) n = unionread((Chan*)c, va, n); else n = (*devtab[c->type].read)((Chan*)c, va, n, c->offset); cl = (Lock*)&c->r.l; lock(cl); c->offset += n; unlock(cl); poperror(); cclose(c); return n; } int sysremove(char *path) { Chan *c = 0; if(waserror()) { if(c != 0) c->type = 0; /* see below */ cclose(c); return -1; } c = namec(path, Aaccess, 0, 0); (*devtab[c->type].remove)((Chan*)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 sysseek(int fd, long off, int whence) { Dir dir; Chan *c; char buf[DIRLEN]; if(waserror()) return -1; c = fdtochan(fd, -1, 1, 0); if(c->qid.path & CHDIR) error(Eisdir); switch(whence) { case 0: c->offset = off; break; case 1: lock(&c->r.l); /* lock for read/write update */ c->offset += off; off = c->offset; unlock(&c->r.l); break; case 2: (*devtab[c->type].stat)(c, buf); convM2D(buf, &dir); c->offset = dir.length + off; off = c->offset; break; } poperror(); return off; } int sysstat(char *path, char *buf) { Chan *c = 0; if(waserror()){ cclose(c); return -1; } c = namec(path, Aaccess, 0, 0); (*devtab[c->type].stat)((Chan*)c, buf); poperror(); cclose(c); return 0; } long syswrite(int fd, void *va, long n) { Lock *cl; Chan *c = 0; if(waserror()) { cclose(c); return -1; } c = fdtochan(fd, OWRITE, 1, 1); if(c->qid.path & CHDIR) error(Eisdir); n = (*devtab[c->type].write)((Chan*)c, va, n, c->offset); cl = (Lock*)&c->r.l; lock(cl); c->offset += n; unlock(cl); poperror(); cclose(c); return n; } int syswstat(char *path, char *buf) { Chan *c = 0; if(waserror()) { cclose(c); return -1; } nameok(buf); c = namec(path, Aaccess, 0, 0); (*devtab[c->type].wstat)((Chan*)c, buf); poperror(); cclose(c); return 0; } int sysdirstat(char *name, Dir *dir) { char buf[DIRLEN]; if(sysstat(name, buf) == -1) return -1; convM2D(buf, dir); return 0; } int sysdirfstat(int fd, Dir *dir) { char buf[DIRLEN]; if(sysfstat(fd, buf) == -1) return -1; convM2D(buf, dir); return 0; } int sysdirwstat(char *name, Dir *dir) { char buf[DIRLEN]; convD2M(dir, buf); return syswstat(name, buf); } int sysdirfwstat(int fd, Dir *dir) { char buf[DIRLEN]; convD2M(dir, buf); return sysfwstat(fd, buf); } long sysdirread(int fd, Dir *dbuf, long count) { int c, n, i, r; char buf[DIRLEN*50]; n = 0; count = (count/sizeof(Dir)) * DIRLEN; while(n < count) { c = count - n; if(c > sizeof(buf)) c = sizeof(buf); r = sysread(fd, buf, c); if(r == 0) break; if(r < 0 || r % DIRLEN) return -1; for(i=0; i<r; i+=DIRLEN) { convM2D(buf+i, dbuf); dbuf++; } n += r; if(r != c) break; } return (n/DIRLEN) * sizeof(Dir); } static int call(char *clone, char *dest, int *cfdp, char *dir, char *local) { int fd, cfd, n; char *p, name[3*NAMELEN+5], data[3*NAMELEN+10]; cfd = sysopen(clone, ORDWR); if(cfd < 0){ werrstr("%s: %r", clone); return -1; } /* get directory name */ n = sysread(cfd, name, sizeof(name)-1); if(n < 0) { sysclose(cfd); return -1; } name[n] = 0; sprint(name, "%d", strtoul(name, 0, 0)); p = strrchr(clone, '/'); *p = 0; if(dir) snprint(dir, 2*NAMELEN, "%s/%s", clone, name); snprint(data, sizeof(data), "%s/%s/data", clone, name); /* connect */ /* set local side (port number, for example) if we need to */ if(local) snprint(name, sizeof(name), "connect %s %s", dest, local); else snprint(name, sizeof(name), "connect %s", dest); if(syswrite(cfd, name, strlen(name)) < 0){ werrstr("%s failed: %r", name); sysclose(cfd); return -1; } /* open data connection */ fd = sysopen(data, ORDWR); if(fd < 0){ werrstr("can't open %s: %r", data); sysclose(cfd); return -1; } if(cfdp) *cfdp = cfd; else sysclose(cfd); return fd; } int sysdial(char *dest, char *local, char *dir, int *cfdp) { int n, fd, rv; char *p, net[128], clone[NAMELEN+12]; /* go for a standard form net!... */ p = strchr(dest, '!'); if(p == 0){ snprint(net, sizeof(net), "net!%s", dest); } else { strncpy(net, dest, sizeof(net)-1); net[sizeof(net)-1] = 0; } /* call the connection server */ fd = sysopen("/net/cs", ORDWR); if(fd < 0){ /* no connection server, don't translate */ p = strchr(net, '!'); *p++ = 0; snprint(clone, sizeof(clone), "/net/%s/clone", net); return call(clone, p, cfdp, dir, local); } /* * send dest to connection to translate */ if(syswrite(fd, net, strlen(net)) < 0){ werrstr("%s: %r", net); sysclose(fd); return -1; } /* * loop through each address from the connection server till * we get one that works. */ rv = -1; sysseek(fd, 0, 0); while((n = sysread(fd, net, sizeof(net) - 1)) > 0){ net[n] = 0; p = strchr(net, ' '); if(p == 0) continue; *p++ = 0; rv = call(net, p, cfdp, dir, local); if(rv >= 0) break; } sysclose(fd); return rv; } static int identtrans(char *addr, char *naddr, int na, char *file, int nf) { char *p; char reply[4*NAMELEN]; /* parse the network */ strncpy(reply, addr, sizeof(reply)); reply[sizeof(reply)-1] = 0; p = strchr(reply, '!'); if(p) *p++ = 0; sprint(file, "/net/%.*s/clone", na - sizeof("/net//clone"), reply); strncpy(naddr, p, na); naddr[na-1] = 0; return 1; } static int nettrans(char *addr, char *naddr, int na, char *file, int nf) { long n; int fd; char *cp; char reply[4*NAMELEN]; /* * ask the connection server */ fd = sysopen("/net/cs", ORDWR); if(fd < 0) return identtrans(addr, naddr, na, file, nf); if(syswrite(fd, addr, strlen(addr)) < 0){ sysclose(fd); return -1; } sysseek(fd, 0, 0); n = sysread(fd, reply, sizeof(reply)-1); sysclose(fd); if(n <= 0) return -1; reply[n] = '\0'; /* * parse the reply */ cp = strchr(reply, ' '); if(cp == 0) return -1; *cp++ = 0; strncpy(naddr, cp, na); naddr[na-1] = 0; strncpy(file, reply, nf); file[nf-1] = 0; return 0; } int sysannounce(char *addr, char *dir) { char *cp; int ctl, n, m; char buf[3*NAMELEN]; char buf2[3*NAMELEN]; char netdir[2*NAMELEN]; char naddr[3*NAMELEN]; /* * translate the address */ if(nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0){ werrstr("can't translate address"); return -1; } /* * get a control channel */ ctl = sysopen(netdir, ORDWR); if(ctl<0){ werrstr("can't open control channel"); return -1; } cp = strrchr(netdir, '/'); *cp = 0; /* * find out which line we have */ n = sprint(buf, "%.*s/", 2*NAMELEN+1, netdir); m = sysread(ctl, &buf[n], sizeof(buf)-n-1); if(m <= 0) { sysclose(ctl); werrstr("can't read control file"); return -1; } buf[n+m] = 0; /* * make the call */ n = sprint(buf2, "announce %.*s", 2*NAMELEN, naddr); if(syswrite(ctl, buf2, n) != n) { sysclose(ctl); werrstr("announcement fails"); return -1; } strcpy(dir, buf); return ctl; } int syslisten(char *dir, char *newdir) { char *cp; int ctl, n, m; char buf[3*NAMELEN]; /* * open listen, wait for a call */ sprint(buf, "%.*s/listen", 2*NAMELEN+1, dir); ctl = sysopen(buf, ORDWR); if(ctl < 0) return -1; /* * find out which line we have */ strcpy(buf, dir); cp = strrchr(buf, '/'); *++cp = 0; n = cp-buf; m = sysread(ctl, cp, sizeof(buf) - n - 1); if(n<=0){ sysclose(ctl); return -1; } buf[n+m] = 0; strcpy(newdir, buf); return ctl; }