#include <9pm/u.h> #include <9pm/libc.h> #include <9pm/fcall.h> #include <9pm/ns.h> static void fdclose(Fgrp*, int, int); static long bindmount(int ismount, int fd, int afd, char *new, char *old, int flag, char *spec) { int ret; Chan *c0, *c1, *volatile ac, *bc; struct { Chan* chan; Chan* authchan; char* spec; int flags; } mntparam; if((flag&~MMASK) || (flag&MORDER) == (MBEFORE|MAFTER)) error(Ebadarg); mntparam.flags = flag & MCACHE; if(ismount){ if(up->pgrp->noattach) error(Enoattach); ac = nil; bc = fdtochan(up->fgrp, fd, ORDWR, 0, 1); if(waserror()) { if(ac) cclose(ac); cclose(bc); nexterror(); } if(afd >= 0) ac = fdtochan(up->fgrp, afd, ORDWR, 0, 1); mntparam.chan = bc; mntparam.authchan = ac; mntparam.spec = spec; ret = devno('M', 0); c0 = devtab[ret]->attach((char*)&mntparam); poperror(); if(ac) cclose(ac); cclose(bc); }else{ spec = 0; c0 = namec(new, Amount, 0, 0); } if(waserror()){ cclose(c0); nexterror(); } c1 = namec(old, Amount, 0, 0); if(waserror()){ cclose(c1); nexterror(); } ret = cmount(&c0, c1, flag, spec); poperror(); cclose(c1); poperror(); cclose(c0); if(ismount) fdclose(up->fgrp, fd, 0); return ret; } static 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 */ if(fd>0 && fd%100 == 0) fprint(2, "warning: process exceeds %d file descriptors\n", fd); /* * 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) f->maxfd = fd; return 1; } static void fdclose(Fgrp *f, int fd, int flag) { int i; Chan *c; lock(&f->lk); c = f->fd[fd]; if(c == nil){ /* can happen for users with shared fd tables */ unlock(&f->lk); return; } if(flag){ if(c==0 || !(c->flag&flag)) { unlock(&f->lk); return; } } f->fd[fd] = nil; if(fd == f->maxfd) for(i=fd; --i>=0 && f->fd[i]==0; ) f->maxfd = i; unlock(&f->lk); cclose(c); } Chan* fdtochan(Fgrp *f, int fd, int mode, int chkmnt, int iref) { Chan *c; c = 0; lock(&f->lk); if(fd<0 || f->maxfdfd[fd])==nil) { unlock(&f->lk); error(Ebadfd); } if(iref) incref(&c->ref); unlock(&f->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 */ } int fgrpclose(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, 0); poperror(); return 0; } /* * this assumes that the fgrp is locked */ static 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->lk); fd = findfreefd(f, 0); if(fd < 0){ unlock(&f->lk); return -1; } if(fd > f->maxfd) f->maxfd = fd; f->fd[fd] = c; unlock(&f->lk); return fd; } int newfdx(Chan *c, int fd) { Fgrp *f; if(fd < 0) panic("newfdx fd"); f = up->fgrp; lock(&f->lk); growfd(f, fd); if(fd > f->maxfd) f->maxfd = fd; f->fd[fd] = c; unlock(&f->lk); return fd; } int newfd2(int fd[2], Chan *c[2]) { Fgrp *f; f = up->fgrp; lock(&f->lk); fd[0] = findfreefd(f, 0); if(fd[0] < 0){ unlock(&f->lk); return -1; } fd[1] = findfreefd(f, fd[0]+1); if(fd[1] < 0){ unlock(&f->lk); return -1; } if(fd[1] > f->maxfd) f->maxfd = fd[1]; f->fd[fd[0]] = c[0]; f->fd[fd[1]] = c[1]; unlock(&f->lk); return 0; } int openmode(ulong o) { o &= ~(OTRUNC|OCEXEC|ORCLOSE); if(o > OEXEC) error(Ebadarg); if(o == OEXEC) return OREAD; return o; } long unionread(Chan *c, void *va, long n) { int i; Mhead *m; Mount *mount; long volatile nr; qlock(&c->umqlock); m = c->umh; rlock(&m->lk); 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->lk); qunlock(&c->umqlock); return nr; } 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 bind(char *new, char *old, int flags) { long r; if(waserror()) return -1; r = bindmount(0, -1, -1, new, old, flags, nil); poperror(); return r; } int chdir(char *path) { Chan *c; if(waserror()) return -1; c = namec(path, Atodir, 0, 0); cclose(up->dot); up->dot = c; poperror(); /* BUG: chdir in underlying os; maybe only at exec */ return 0; } int close(int fd) { return fgrpclose(getproc()->fgrp, fd); } int create(char *path, int mode, ulong perm) { int fd; Chan *volatile c = 0; if(waserror()) { if(c) cclose(c); return -1; } openmode(mode); /* error check only */ c = namec(path, Acreate, mode, perm); fd = newfd(c); if(fd < 0) error(Enofd); poperror(); return fd; } int dup(int old, int new) { int fd; Chan *oc; Fgrp *f = up->fgrp; Chan *volatile c = 0; if(waserror()) return -1; /* * Close after dup'ing, so date > #d/1 works */ c = fdtochan(up->fgrp, old, -1, 0, 1); fd = new; if(fd != -1){ lock(&f->lk); if(fd<0 || growfd(f, fd)<0) { unlock(&f->lk); cclose(c); error(Ebadfd); } if(fd > f->maxfd) f->maxfd = fd; oc = f->fd[fd]; f->fd[fd] = c; unlock(&f->lk); if(oc) cclose(oc); }else{ if(waserror()) { cclose(c); nexterror(); } fd = newfd(c); if(fd < 0) error(Enofd); poperror(); } poperror(); return fd; } int fd2path(int fd, char *buf, int nbuf) { Chan *c; char *s; if(waserror()) return -1; c = fdtochan(up->fgrp, fd, -1, 0, 1); poperror(); if(c->name == nil) s = ""; else s = c->name->s; utfecpy(buf, buf+nbuf, s); cclose(c); return 0; } int fstat(int fd, uchar *buf, int n) { Chan *volatile c = 0; if(waserror()) { if(c) cclose(c); return -1; } c = fdtochan(up->fgrp, fd, -1, 0, 1); n = devtab[c->type]->_stat(c, buf, n); poperror(); cclose(c); return n; } int fwstat(int fd, uchar *buf, int n) { Chan *volatile c = 0; if(waserror()) { if(c) cclose(c); return -1; } validstat(buf, n); c = fdtochan(up->fgrp, fd, -1, 1, 1); n = devtab[c->type]->_wstat(c, buf, n); poperror(); cclose(c); return n; } int mount(int fd, int afd, char *old, int flags, char *spec) { long r; if(waserror()) return -1; r = bindmount(1, fd, afd, nil, old, flags, spec); poperror(); return r; } int open(char *path, int mode) { int fd; Chan *volatile c = 0; if(waserror()){ if(c) cclose(c); return -1; } openmode(mode); /* error check only */ c = namec(path, Aopen, mode, 0); fd = newfd((Chan*)c); if(fd < 0) error(Enofd); poperror(); return fd; } int unmount(char *old, char *new) { Chan *volatile cmount = 0, *volatile cmounted = 0; if(waserror()) { if(cmount) cclose(cmount); if(cmounted) cclose(cmounted); return -1; } cmount = namec(new, Aopen, OREAD, 0); if(old != nil){ /* * 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(); } cunmount(cmount, cmounted); poperror(); cclose(cmount); if(cmounted) cclose(cmounted); return 0; } long pread(int fd, void *va, long n, vlong offset) { int dir; vlong off; Chan *volatile c = 0; if(waserror()) { if(c) cclose(c); return -1; } c = fdtochan(up->fgrp, fd, OREAD, 1, 1); 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(offset == (vlong)-1) /* use and maintain channel's offset */ off = c->offset; else off = offset; if(off < 0) error(Enegoff); if(dir && c->umh) n = unionread(c, va, n); else n = devtab[c->type]->_read(c, va, n, off); if(offset == (vlong)-1){ lock(&c->lk); c->offset += n; unlock(&c->lk); } poperror(); cclose(c); return n; } long read(int fd, void *va, long n) { return pread(fd, va, n, (vlong)-1); } int remove(char *path) { Chan *volatile c = 0; if(waserror()) { if(c != nil){ c->type = 0; /* see below */ cclose(c); } return -1; } c = namec(path, Aremove, 0, 0); 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; } vlong seek(int fd, vlong off, int whence) { int n; Dir dir; Chan *volatile c = 0; uchar buf[sizeof(Dir)+100]; if(waserror()) { if(c) cclose(c); return -1; } c = fdtochan(up->fgrp, fd, -1, 1, 1); if(c->qid.type & QTDIR) error(Eisdir); 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); lock(&c->lk); /* vlong assignment is not atomic */ c->offset = off; unlock(&c->lk); break; case 1: if(c->qid.type & QTDIR) error(Eisdir); lock(&c->lk); /* read/write update */ off += c->offset; if(off < 0){ unlock(&c->lk); error(Enegoff); } c->offset = off; unlock(&c->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; if(off < 0) error(Enegoff); lock(&c->lk); /* vlong assignment is not atomic */ c->offset = off; unlock(&c->lk); break; default: error(Ebadarg); } c->uri = 0; c->dri = 0; cclose(c); poperror(); return off; } int stat(char *path, uchar *buf, int n) { Chan *volatile c = 0; if(waserror()){ if(c) cclose(c); return -1; } c = namec(path, Aaccess, 0, 0); n = devtab[c->type]->_stat(c, buf, n); poperror(); cclose(c); return n; } long pwrite(int fd, void *va, long n, vlong offset) { long m; vlong off; Chan *volatile c = 0; if(waserror()){ dprint(PmDebugFd, "pwrite error %s\n", getproc()->err); if(offset == (vlong)-1 && c != nil){ lock(&c->lk); c->offset -= n; unlock(&c->lk); } if(c){ cclose(c); if(devtab[c->type]->dc == (Rune)'|') error("write on closed pipe"); } return -1; } c = fdtochan(up->fgrp, fd, OWRITE, 1, 1); if(c->qid.type & QTDIR) error(Eisdir); if(offset == (vlong)-1){ lock(&c->lk); off = c->offset; c->offset += n; unlock(&c->lk); }else off = offset; if(off < 0) error(Enegoff); dprint(PmDebugFd, "pwrite dev %C\n", devtab[c->type]->dc); m = devtab[c->type]->_write(c, va, n, off); if(offset == (vlong)-1 && m < n){ lock(&c->lk); c->offset -= n - m; unlock(&c->lk); } poperror(); cclose(c); return n; } long write(int fd, void *va, long n) { return pwrite(fd, va, n, (vlong)-1); } int wstat(char *path, uchar *buf, int n) { Chan *volatile c = 0; if(waserror()) { if(c) cclose(c); return -1; } validstat(buf, n); c = namec(path, Aaccess, 0, 0); n = devtab[c->type]->_wstat(c, buf, n); poperror(); cclose(c); return n; } int iounit(int fd) { Chan *c; if(waserror()) return -1; c = fdtochan(up->fgrp, fd, -1, 1, 0); return c->iounit; }