File:  [Plan 9 NeXT] / lucent / sys / src / 9 / port / sysfile.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 18:01:03 2018 UTC (8 years, 1 month ago) by root
Branches: lucent, MAIN
CVS tags: plan9, HEAD
Plan 9 NeXT

#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"

/*
 * The sys*() routines needn't poperror() as they return directly to syscall().
 */

int
newfd(Chan *c)
{
	Fgrp *f = u->p->fgrp;
	int i;

	lock(f);
	for(i=0; i<NFD; i++)
		if(f->fd[i] == 0){
			if(i > f->maxfd)
				f->maxfd = i;
			f->fd[i] = c;
			unlock(f);
			return i;
		}
	unlock(f);
	exhausted("file descriptors");
	return 0;
}

Chan*
fdtochan(int fd, int mode, int chkmnt, int iref)
{
	Chan *c;
	Fgrp *f;

	c = 0;
	f = u->p->fgrp;

	lock(f);
	if(fd<0 || NFD<=fd || (c = f->fd[fd])==0) {
		unlock(f);
		error(Ebadfd);
	}
	if(iref)
		incref(c);
	unlock(f);

	if(chkmnt && (c->flag&CMSG)) {
		if(iref)
			close(c);
		error(Ebadusefd);
	}

	if(mode<0 || c->mode==ORDWR)
		return c;

	if((mode&OTRUNC) && c->mode==OREAD) {
		if(iref)
			close(c);
		error(Ebadusefd);
	}

	if((mode&~OTRUNC) != c->mode) {
		if(iref)
			close(c);
		error(Ebadusefd);
	}

	return c;
}

int
openmode(ulong o)
{
	if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC))
		error(Ebadarg);
	o &= ~(OTRUNC|OCEXEC|ORCLOSE);
	if(o > OEXEC)
		error(Ebadarg);
	if(o == OEXEC)
		return OREAD;
	return o;
}

long
syspipe(ulong *arg)
{
	int fd[2];
	Chan *c[2];
	Dev *d;
	Fgrp *f = u->p->fgrp;

	validaddr(arg[0], 2*BY2WD, 1);
	evenaddr(arg[0]);
	d = &devtab[devno('|', 0)];
	c[0] = (*d->attach)(0);
	c[1] = 0;
	fd[0] = -1;
	fd[1] = -1;
	if(waserror()){
		close(c[0]);
		if(c[1])
			close(c[1]);
		if(fd[0] >= 0)
			f->fd[fd[0]]=0;
		if(fd[1] >= 0)
			f->fd[fd[1]]=0;
		nexterror();
	}
	c[1] = (*d->clone)(c[0], 0);
	(*d->walk)(c[0], "data");
	(*d->walk)(c[1], "data1");
	c[0] = (*d->open)(c[0], ORDWR);
	c[1] = (*d->open)(c[1], ORDWR);
	fd[0] = newfd(c[0]);
	fd[1] = newfd(c[1]);
	((long*)arg[0])[0] = fd[0];
	((long*)arg[0])[1] = fd[1];
	poperror();
	return 0;
}

long
sysdup(ulong *arg)
{
	int fd;
	Chan *c, *oc;
	Fgrp *f = u->p->fgrp;

	/*
	 * Close after dup'ing, so date > #d/1 works
	 */
	c = fdtochan(arg[0], -1, 0, 1);
	fd = arg[1];
	if(fd != -1){
		if(fd<0 || NFD<=fd) {
			close(c);
			error(Ebadfd);
		}
		lock(f);
		if(fd > f->maxfd)
			f->maxfd = fd;

		oc = f->fd[fd];
		f->fd[fd] = c;
		unlock(f);
		if(oc)
			close(oc);
	}else{
		if(waserror()) {
			close(c);
			nexterror();
		}
		fd = newfd(c);
		poperror();
	}

	return fd;
}

long
sysopen(ulong *arg)
{
	int fd;
	Chan *c = 0;

	openmode(arg[1]);	/* error check only */
	if(waserror()){
		if(c)
			close(c);
		nexterror();
	}
	validaddr(arg[0], 1, 0);
	c = namec((char*)arg[0], Aopen, arg[1], 0);
	fd = newfd(c);
	poperror();
	return fd;
}

void
fdclose(int fd, int flag)
{
	int i;
	Chan *c;
	Fgrp *f = u->p->fgrp;

	lock(f);
	c = f->fd[fd];
	if(c == 0){
		/* can happen for users with shared fd tables */
		unlock(f);
		return;
	}
	if(flag){
		if(c==0 || !(c->flag&flag)){
			unlock(f);
			return;
		}
	}
	f->fd[fd] = 0;
	if(fd == f->maxfd)
		for(i=fd; --i>=0 && f->fd[i]==0; )
			f->maxfd = i;

	unlock(f);
	close(c);
}

long
sysclose(ulong *arg)
{
	fdtochan(arg[0], -1, 0, 0);
	fdclose(arg[0], 0);

	return 0;
}

long
unionread(Chan *c, void *va, long n)
{
	long nr;
	Chan *nc;
	Pgrp *pg;

	pg = u->p->pgrp;
	rlock(&pg->ns);

	for(;;) {
		/* Error causes component of union to be skipped */
		if(waserror())
			goto next;

		nc = clone(c->mnt->to, 0);
		poperror();

		if(c->mountid != c->mnt->mountid) {
			pprint("unionread: changed underfoot?\n");
			runlock(&pg->ns);
			close(nc);
			return 0;
		}

		if(waserror()) {	
			close(nc);
			goto next;
		}

		nc = (*devtab[nc->type].open)(nc, OREAD);
		nc->offset = c->offset;
		nr = (*devtab[nc->type].read)(nc, va, n, nc->offset);
		/* devdirread e.g. changes it */
		c->offset = nc->offset;	
		poperror();

		close(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(ulong *arg)
{
	int dir;
	long n;
	Chan *c;

	validaddr(arg[1], arg[2], 1);
	c = fdtochan(arg[0], OREAD, 1, 1);
	if(waserror()) {
		close(c);
		nexterror();
	}

	n = arg[2];
	dir = c->qid.path&CHDIR;

	if(dir) {
		n -= n%DIRLEN;
		if(c->offset%DIRLEN || n==0)
			error(Etoosmall);
	}

	if(dir && c->mnt)
		n = unionread(c, (void*)arg[1], n);
	else
		n = (*devtab[c->type].read)(c, (void*)arg[1], n, c->offset);

	lock(c);
	c->offset += n;
	unlock(c);

	poperror();
	close(c);

	return n;
}

long
syswrite(ulong *arg)
{
	Chan *c;
	long n;

	validaddr(arg[1], arg[2], 0);
	c = fdtochan(arg[0], OWRITE, 1, 1);
	if(waserror()) {
		close(c);
		nexterror();
	}

	if(c->qid.path & CHDIR)
		error(Eisdir);

	n = (*devtab[c->type].write)(c, (void*)arg[1], arg[2], c->offset);

	lock(c);
	c->offset += n;
	unlock(c);

	poperror();
	close(c);

	return n;
}

long
sysseek(ulong *arg)
{
	Chan *c;
	char buf[DIRLEN];
	Dir dir;
	long off;

	c = fdtochan(arg[0], -1, 1, 0);
	if(c->qid.path & CHDIR)
		error(Eisdir);

	if(devchar[c->type] == '|')
		error(Eisstream);

	off = 0;
	switch(arg[2]){
	case 0:
		off = c->offset = arg[1];
		break;

	case 1:
		lock(c);	/* lock for read/write update */
		c->offset += (long)arg[1];
		off = c->offset;
		unlock(c);
		break;

	case 2:
		(*devtab[c->type].stat)(c, buf);
		convM2D(buf, &dir);
		c->offset = dir.length + (long)arg[1];
		off = c->offset;
		break;
	}
	return off;
}

long
sysfstat(ulong *arg)
{
	Chan *c;

	validaddr(arg[1], DIRLEN, 1);
	evenaddr(arg[1]);
	c = fdtochan(arg[0], -1, 0, 1);
	if(waserror()) {
		close(c);
		nexterror();
	}
	(*devtab[c->type].stat)(c, (char*)arg[1]);
	poperror();
	close(c);
	return 0;
}

long
sysstat(ulong *arg)
{
	Chan *c;

	validaddr(arg[1], DIRLEN, 1);
	evenaddr(arg[1]);
	validaddr(arg[0], 1, 0);
	c = namec((char*)arg[0], Aaccess, 0, 0);
	if(waserror()){
		close(c);
		nexterror();
	}
	(*devtab[c->type].stat)(c, (char*)arg[1]);
	poperror();
	close(c);
	return 0;
}

long
syschdir(ulong *arg)
{
	Chan *c;

	validaddr(arg[0], 1, 0);
	c = namec((char*)arg[0], Atodir, 0, 0);
	close(u->dot);
	u->dot = c;
	return 0;
}

long
bindmount(ulong *arg, int ismount)
{
	Chan *c0, *c1, *bc;
	ulong flag;
	long ret;
	int fd;
	struct{
		Chan	*chan;
		char	*spec;
	}bogus;

	flag = arg[2];
	fd = arg[0];
	if(flag>MMASK || (flag&MORDER)==(MBEFORE|MAFTER))
		error(Ebadarg);
	if(ismount){
		bc = fdtochan(fd, 2, 0, 1);
		if(waserror()) {
			close(bc);
			nexterror();
		}
		bogus.chan = bc;

		validaddr(arg[3], 1, 0);
		if(vmemchr((char*)arg[3], '\0', NAMELEN) == 0)
			error(Ebadarg);

		bogus.spec = (char*)arg[3];

		ret = devno('M', 0);
		c0 = (*devtab[ret].attach)((char*)&bogus);

		poperror();
		close(bc);
	}
	else {
		validaddr(arg[0], 1, 0);
		c0 = namec((char*)arg[0], Aaccess, 0, 0);
	}
	if(waserror()){
		close(c0);
		nexterror();
	}
	validaddr(arg[1], 1, 0);
	c1 = namec((char*)arg[1], Amount, 0, 0);
	if(waserror()){
		close(c1);
		nexterror();
	}
	if((c0->qid.path^c1->qid.path) & CHDIR)
		error(Emount);
	if(flag && !(c0->qid.path&CHDIR))
		error(Emount);
	ret = mount(c0, c1, flag);
	poperror();
	close(c1);
	poperror();
	close(c0);
	if(ismount)
		fdclose(fd, 0);
	return ret;
}

long
sysbind(ulong *arg)
{
	return bindmount(arg, 0);
}

long
sysmount(ulong *arg)
{
	return bindmount(arg, 1);
}

long
sysunmount(ulong *arg)
{
	Chan *cmount, *cmounted;

	cmounted = 0;

	validaddr(arg[1], 1, 0);
	cmount = namec((char *)arg[1], Amount, 0, 0);

	if(arg[0]) {
		if(waserror()) {
			close(cmount);
			nexterror();
		}
		validaddr(arg[0], 1, 0);
		cmounted = namec((char*)arg[0], Aopen, OREAD, 0);
		poperror();
	}

	if(waserror()) {
		close(cmount);
		if(cmounted)
			close(cmounted);
		nexterror();
	}
	unmount(cmount, cmounted);
	close(cmount);
	if(cmounted)
		close(cmounted);
	poperror();	
	return 0;
}

long
syscreate(ulong *arg)
{
	int fd;
	Chan *c = 0;

	openmode(arg[1]);	/* error check only */
	if(waserror()) {
		if(c)
			close(c);
		nexterror();
	}
	validaddr(arg[0], 1, 0);
	c = namec((char*)arg[0], Acreate, arg[1], arg[2]);
	fd = newfd(c);
	poperror();
	return fd;
}

long
sysremove(ulong *arg)
{
	Chan *c;

	validaddr(arg[0], 1, 0);
	c = namec((char*)arg[0], Aaccess, 0, 0);
	if(waserror()){
		c->type = 0;	/* see below */
		close(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();
	close(c);
	return 0;
}

long
syswstat(ulong *arg)
{
	Chan *c;

	validaddr(arg[1], DIRLEN, 0);
	nameok((char*)arg[1]);
	validaddr(arg[0], 1, 0);
	c = namec((char*)arg[0], Aaccess, 0, 0);
	if(waserror()){
		close(c);
		nexterror();
	}
	(*devtab[c->type].wstat)(c, (char*)arg[1]);
	poperror();
	close(c);
	return 0;
}

long
sysfwstat(ulong *arg)
{
	Chan *c;

	validaddr(arg[1], DIRLEN, 0);
	nameok((char*)arg[1]);
	c = fdtochan(arg[0], -1, 1, 1);
	if(waserror()) {
		close(c);
		nexterror();
	}
	(*devtab[c->type].wstat)(c, (char*)arg[1]);
	poperror();
	close(c);
	return 0;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.