#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->maxfd<fd || (c = f->fd[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; 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->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 = "<null>";
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;
}
syntax highlighted by Code2HTML, v. 0.9.1