vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

sysfile.c (22482B)


      1 #include	"u.h"
      2 #include	"lib.h"
      3 #include	"mem.h"
      4 #include	"dat.h"
      5 #include	"fns.h"
      6 #include	"error.h"
      7 
      8 /*
      9  * The sys*() routines needn't poperror() as they return directly to syscall().
     10  */
     11 
     12 static void
     13 unlockfgrp(Fgrp *f)
     14 {
     15 	int ex;
     16 
     17 	ex = f->exceed;
     18 	f->exceed = 0;
     19 	unlock(&f->ref.lk);
     20 	if(ex)
     21 		pprint("warning: process exceeds %d file descriptors\n", ex);
     22 }
     23 
     24 int
     25 growfd(Fgrp *f, int fd)	/* fd is always >= 0 */
     26 {
     27 	Chan **newfd, **oldfd;
     28 
     29 	if(fd < f->nfd)
     30 		return 0;
     31 	if(fd >= f->nfd+DELTAFD)
     32 		return -1;	/* out of range */
     33 	/*
     34 	 * Unbounded allocation is unwise; besides, there are only 16 bits
     35 	 * of fid in 9P
     36 	 */
     37 	if(f->nfd >= 5000){
     38     Exhausted:
     39 		print("no free file descriptors\n");
     40 		return -1;
     41 	}
     42 	newfd = malloc((f->nfd+DELTAFD)*sizeof(Chan*));
     43 	if(newfd == 0)
     44 		goto Exhausted;
     45 	oldfd = f->fd;
     46 	memmove(newfd, oldfd, f->nfd*sizeof(Chan*));
     47 	f->fd = newfd;
     48 	free(oldfd);
     49 	f->nfd += DELTAFD;
     50 	if(fd > f->maxfd){
     51 		if(fd/100 > f->maxfd/100)
     52 			f->exceed = (fd/100)*100;
     53 		f->maxfd = fd;
     54 	}
     55 	return 1;
     56 }
     57 
     58 /*
     59  *  this assumes that the fgrp is locked
     60  */
     61 int
     62 findfreefd(Fgrp *f, int start)
     63 {
     64 	int fd;
     65 
     66 	for(fd=start; fd<f->nfd; fd++)
     67 		if(f->fd[fd] == 0)
     68 			break;
     69 	if(fd >= f->nfd && growfd(f, fd) < 0)
     70 		return -1;
     71 	return fd;
     72 }
     73 
     74 int
     75 newfd(Chan *c)
     76 {
     77 	int fd;
     78 	Fgrp *f;
     79 
     80 	f = up->fgrp;
     81 	lock(&f->ref.lk);
     82 	fd = findfreefd(f, 0);
     83 	if(fd < 0){
     84 		unlockfgrp(f);
     85 		return -1;
     86 	}
     87 	if(fd > f->maxfd)
     88 		f->maxfd = fd;
     89 	f->fd[fd] = c;
     90 	unlockfgrp(f);
     91 	return fd;
     92 }
     93 
     94 int
     95 newfd2(int fd[2], Chan *c[2])
     96 {
     97 	Fgrp *f;
     98 
     99 	f = up->fgrp;
    100 	lock(&f->ref.lk);
    101 	fd[0] = findfreefd(f, 0);
    102 	if(fd[0] < 0){
    103 		unlockfgrp(f);
    104 		return -1;
    105 	}
    106 	fd[1] = findfreefd(f, fd[0]+1);
    107 	if(fd[1] < 0){
    108 		unlockfgrp(f);
    109 		return -1;
    110 	}
    111 	if(fd[1] > f->maxfd)
    112 		f->maxfd = fd[1];
    113 	f->fd[fd[0]] = c[0];
    114 	f->fd[fd[1]] = c[1];
    115 	unlockfgrp(f);
    116 
    117 	return 0;
    118 }
    119 
    120 Chan*
    121 fdtochan(int fd, int mode, int chkmnt, int iref)
    122 {
    123 	Chan *c;
    124 	Fgrp *f;
    125 
    126 	c = 0;
    127 	f = up->fgrp;
    128 
    129 	lock(&f->ref.lk);
    130 	if(fd<0 || f->nfd<=fd || (c = f->fd[fd])==0) {
    131 		unlock(&f->ref.lk);
    132 		error(Ebadfd);
    133 	}
    134 	if(iref)
    135 		incref(&c->ref);
    136 	unlock(&f->ref.lk);
    137 
    138 	if(chkmnt && (c->flag&CMSG)) {
    139 		if(iref)
    140 			cclose(c);
    141 		error(Ebadusefd);
    142 	}
    143 
    144 	if(mode<0 || c->mode==ORDWR)
    145 		return c;
    146 
    147 	if((mode&OTRUNC) && c->mode==OREAD) {
    148 		if(iref)
    149 			cclose(c);
    150 		error(Ebadusefd);
    151 	}
    152 
    153 	if((mode&~OTRUNC) != c->mode) {
    154 		if(iref)
    155 			cclose(c);
    156 		error(Ebadusefd);
    157 	}
    158 
    159 	return c;
    160 }
    161 
    162 int
    163 openmode(ulong o)
    164 {
    165 	o &= ~(OTRUNC|OCEXEC|ORCLOSE);
    166 	if(o > OEXEC)
    167 		error(Ebadarg);
    168 	if(o == OEXEC)
    169 		return OREAD;
    170 	return o;
    171 }
    172 
    173 long
    174 sysfd2path(uint32 *arg)
    175 {
    176 	Chan *c;
    177 	char *buf;
    178 
    179 	buf = uvalidaddr(arg[1], arg[2], 1);
    180 
    181 	c = fdtochan(arg[0], -1, 0, 1);
    182 	snprint(buf, arg[2], "%s", chanpath(c));
    183 	cclose(c);
    184 	return 0;
    185 }
    186 
    187 long
    188 syspipe(uint32 *arg)
    189 {
    190 	int fd[2];
    191 	Chan *c[2];
    192 	Dev *d;
    193 	static char *datastr[] = {"data", "data1"};
    194 	int *ufd;
    195 	
    196 	ufd = uvalidaddr(arg[0], 2*BY2WD, 1);
    197 	evenaddr(arg[0]);
    198 	d = devtab[devno('|', 0)];
    199 	c[0] = namec("#|", Atodir, 0, 0);
    200 	c[1] = 0;
    201 	fd[0] = -1;
    202 	fd[1] = -1;
    203 
    204 	if(waserror()){
    205 		cclose(c[0]);
    206 		if(c[1])
    207 			cclose(c[1]);
    208 		nexterror();
    209 	}
    210 	c[1] = cclone(c[0]);
    211 	if(walk(&c[0], datastr+0, 1, 1, nil) < 0)
    212 		error(Egreg);
    213 	if(walk(&c[1], datastr+1, 1, 1, nil) < 0)
    214 		error(Egreg);
    215 	c[0] = d->open(c[0], ORDWR);
    216 	c[1] = d->open(c[1], ORDWR);
    217 	if(newfd2(fd, c) < 0)
    218 		error(Enofd);
    219 	poperror();
    220 
    221 	ufd[0] = fd[0];
    222 	ufd[1] = fd[1];
    223 	return 0;
    224 }
    225 
    226 long
    227 sysdup(uint32 *arg)
    228 {
    229 	int fd;
    230 	Chan *c, *oc;
    231 	Fgrp *f = up->fgrp;
    232 
    233 	/*
    234 	 * Close after dup'ing, so date > #d/1 works
    235 	 */
    236 	c = fdtochan(arg[0], -1, 0, 1);
    237 	fd = arg[1];
    238 	if(fd != -1){
    239 		lock(&f->ref.lk);
    240 		if(fd<0 || growfd(f, fd)<0) {
    241 			unlockfgrp(f);
    242 			cclose(c);
    243 			error(Ebadfd);
    244 		}
    245 		if(fd > f->maxfd)
    246 			f->maxfd = fd;
    247 
    248 		oc = f->fd[fd];
    249 		f->fd[fd] = c;
    250 		unlockfgrp(f);
    251 		if(oc)
    252 			cclose(oc);
    253 	}else{
    254 		if(waserror()) {
    255 			cclose(c);
    256 			nexterror();
    257 		}
    258 		fd = newfd(c);
    259 		if(fd < 0)
    260 			error(Enofd);
    261 		poperror();
    262 	}
    263 
    264 	return fd;
    265 }
    266 
    267 long
    268 sysopen(uint32 *arg)
    269 {
    270 	int fd;
    271 	Chan *c = 0;
    272 	char *name;
    273 
    274 	openmode(arg[1]);	/* error check only */
    275 	name = uvalidaddr(arg[0], 1, 0);
    276 	c = namec(name, Aopen, arg[1], 0);
    277 	if(waserror()){
    278 		cclose(c);
    279 		nexterror();
    280 	}
    281 	fd = newfd(c);
    282 	if(fd < 0)
    283 		error(Enofd);
    284 	poperror();
    285 	return fd;
    286 }
    287 
    288 void
    289 fdclose(int fd, int flag)
    290 {
    291 	int i;
    292 	Chan *c;
    293 	Fgrp *f = up->fgrp;
    294 
    295 	lock(&f->ref.lk);
    296 	c = f->fd[fd];
    297 	if(c == 0){
    298 		/* can happen for users with shared fd tables */
    299 		unlock(&f->ref.lk);
    300 		return;
    301 	}
    302 	if(flag){
    303 		if(c==0 || !(c->flag&flag)){
    304 			unlock(&f->ref.lk);
    305 			return;
    306 		}
    307 	}
    308 	f->fd[fd] = 0;
    309 	if(fd == f->maxfd)
    310 		for(i=fd; --i>=0 && f->fd[i]==0; )
    311 			f->maxfd = i;
    312 
    313 	unlock(&f->ref.lk);
    314 	cclose(c);
    315 }
    316 
    317 long
    318 sysclose(uint32 *arg)
    319 {
    320 	fdtochan(arg[0], -1, 0, 0);
    321 	fdclose(arg[0], 0);
    322 
    323 	return 0;
    324 }
    325 
    326 long
    327 unionread(Chan *c, void *va, long n)
    328 {
    329 	int i;
    330 	long nr;
    331 	Mhead *m;
    332 	Mount *mount;
    333 
    334 	qlock(&c->umqlock);
    335 	m = c->umh;
    336 	rlock(&m->lock);
    337 	mount = m->mount;
    338 	/* bring mount in sync with c->uri and c->umc */
    339 	for(i = 0; mount != nil && i < c->uri; i++)
    340 		mount = mount->next;
    341 
    342 	nr = 0;
    343 	while(mount != nil){
    344 		/* Error causes component of union to be skipped */
    345 		if(mount->to && !waserror()){
    346 			if(c->umc == nil){
    347 				c->umc = cclone(mount->to);
    348 				c->umc = devtab[c->umc->type]->open(c->umc, OREAD);
    349 			}
    350 	
    351 			nr = devtab[c->umc->type]->read(c->umc, va, n, c->umc->offset);
    352 			c->umc->offset += nr;
    353 			poperror();
    354 		}
    355 		if(nr > 0)
    356 			break;
    357 
    358 		/* Advance to next element */
    359 		c->uri++;
    360 		if(c->umc){
    361 			cclose(c->umc);
    362 			c->umc = nil;
    363 		}
    364 		mount = mount->next;
    365 	}
    366 	runlock(&m->lock);
    367 	qunlock(&c->umqlock);
    368 	return nr;
    369 }
    370 
    371 static void
    372 unionrewind(Chan *c)
    373 {
    374 	qlock(&c->umqlock);
    375 	c->uri = 0;
    376 	if(c->umc){
    377 		cclose(c->umc);
    378 		c->umc = nil;
    379 	}
    380 	qunlock(&c->umqlock);
    381 }
    382 
    383 static int
    384 dirfixed(uchar *p, uchar *e, Dir *d)
    385 {
    386 	int len;
    387 
    388 	len = GBIT16(p)+BIT16SZ;
    389 	if(p + len > e)
    390 		return -1;
    391 
    392 	p += BIT16SZ;	/* ignore size */
    393 	d->type = devno(GBIT16(p), 1);
    394 	p += BIT16SZ;
    395 	d->dev = GBIT32(p);
    396 	p += BIT32SZ;
    397 	d->qid.type = GBIT8(p);
    398 	p += BIT8SZ;
    399 	d->qid.vers = GBIT32(p);
    400 	p += BIT32SZ;
    401 	d->qid.path = GBIT64(p);
    402 	p += BIT64SZ;
    403 	d->mode = GBIT32(p);
    404 	p += BIT32SZ;
    405 	d->atime = GBIT32(p);
    406 	p += BIT32SZ;
    407 	d->mtime = GBIT32(p);
    408 	p += BIT32SZ;
    409 	d->length = GBIT64(p);
    410 
    411 	return len;
    412 }
    413 
    414 static char*
    415 dirname(uchar *p, int *n)
    416 {
    417 	p += BIT16SZ+BIT16SZ+BIT32SZ+BIT8SZ+BIT32SZ+BIT64SZ
    418 		+ BIT32SZ+BIT32SZ+BIT32SZ+BIT64SZ;
    419 	*n = GBIT16(p);
    420 	return (char*)p+BIT16SZ;
    421 }
    422 
    423 static long
    424 dirsetname(char *name, int len, uchar *p, long n, long maxn)
    425 {
    426 	char *oname;
    427 	int olen;
    428 	long nn;
    429 
    430 	if(n == BIT16SZ)
    431 		return BIT16SZ;
    432 
    433 	oname = dirname(p, &olen);
    434 
    435 	nn = n+len-olen;
    436 	PBIT16(p, nn-BIT16SZ);
    437 	if(nn > maxn)
    438 		return BIT16SZ;
    439 
    440 	if(len != olen)
    441 		memmove(oname+len, oname+olen, p+n-(uchar*)(oname+olen));
    442 	PBIT16((uchar*)(oname-2), len);
    443 	memmove(oname, name, len);
    444 	return nn;
    445 }
    446 
    447 /*
    448  * Mountfix might have caused the fixed results of the directory read
    449  * to overflow the buffer.  Catch the overflow in c->dirrock.
    450  */
    451 static void
    452 mountrock(Chan *c, uchar *p, uchar **pe)
    453 {
    454 	uchar *e, *r;
    455 	int len, n;
    456 
    457 	e = *pe;
    458 
    459 	/* find last directory entry */
    460 	for(;;){
    461 		len = BIT16SZ+GBIT16(p);
    462 		if(p+len >= e)
    463 			break;
    464 		p += len;
    465 	}
    466 
    467 	/* save it away */
    468 	qlock(&c->rockqlock);
    469 	if(c->nrock+len > c->mrock){
    470 		n = ROUND(c->nrock+len, 1024);
    471 		r = smalloc(n);
    472 		memmove(r, c->dirrock, c->nrock);
    473 		free(c->dirrock);
    474 		c->dirrock = r;
    475 		c->mrock = n;
    476 	}
    477 	memmove(c->dirrock+c->nrock, p, len);
    478 	c->nrock += len;
    479 	qunlock(&c->rockqlock);
    480 
    481 	/* drop it */
    482 	*pe = p;
    483 }
    484 
    485 /*
    486  * Satisfy a directory read with the results saved in c->dirrock.
    487  */
    488 static int
    489 mountrockread(Chan *c, uchar *op, long n, long *nn)
    490 {
    491 	long dirlen;
    492 	uchar *rp, *erp, *ep, *p;
    493 
    494 	/* common case */
    495 	if(c->nrock == 0)
    496 		return 0;
    497 
    498 	/* copy out what we can */
    499 	qlock(&c->rockqlock);
    500 	rp = c->dirrock;
    501 	erp = rp+c->nrock;
    502 	p = op;
    503 	ep = p+n;
    504 	while(rp+BIT16SZ <= erp){
    505 		dirlen = BIT16SZ+GBIT16(rp);
    506 		if(p+dirlen > ep)
    507 			break;
    508 		memmove(p, rp, dirlen);
    509 		p += dirlen;
    510 		rp += dirlen;
    511 	}
    512 
    513 	if(p == op){
    514 		qunlock(&c->rockqlock);
    515 		return 0;
    516 	}
    517 
    518 	/* shift the rest */
    519 	if(rp != erp)
    520 		memmove(c->dirrock, rp, erp-rp);
    521 	c->nrock = erp - rp;
    522 
    523 	*nn = p - op;
    524 	qunlock(&c->rockqlock);
    525 	return 1;
    526 }
    527 
    528 static void
    529 mountrewind(Chan *c)
    530 {
    531 	c->nrock = 0;
    532 }
    533 
    534 /*
    535  * Rewrite the results of a directory read to reflect current 
    536  * name space bindings and mounts.  Specifically, replace
    537  * directory entries for bind and mount points with the results
    538  * of statting what is mounted there.  Except leave the old names.
    539  */
    540 static long
    541 mountfix(Chan *c, uchar *op, long n, long maxn)
    542 {
    543 	char *name;
    544 	int nbuf, nname;
    545 	Chan *nc;
    546 	Mhead *mh;
    547 	Mount *m;
    548 	uchar *p;
    549 	int dirlen, rest;
    550 	long l;
    551 	uchar *buf, *e;
    552 	Dir d;
    553 
    554 	p = op;
    555 	buf = nil;
    556 	nbuf = 0;
    557 	for(e=&p[n]; p+BIT16SZ<e; p+=dirlen){
    558 		dirlen = dirfixed(p, e, &d);
    559 		if(dirlen < 0)
    560 			break;
    561 		nc = nil;
    562 		mh = nil;
    563 		if(findmount(&nc, &mh, d.type, d.dev, d.qid)){
    564 			/*
    565 			 * If it's a union directory and the original is
    566 			 * in the union, don't rewrite anything.
    567 			 */
    568 			for(m=mh->mount; m; m=m->next)
    569 				if(eqchantdqid(m->to, d.type, d.dev, d.qid, 1))
    570 					goto Norewrite;
    571 
    572 			name = dirname(p, &nname);
    573 			/*
    574 			 * Do the stat but fix the name.  If it fails, leave old entry.
    575 			 * BUG: If it fails because there isn't room for the entry,
    576 			 * what can we do?  Nothing, really.  Might as well skip it.
    577 			 */
    578 			if(buf == nil){
    579 				buf = smalloc(4096);
    580 				nbuf = 4096;
    581 			}
    582 			if(waserror())
    583 				goto Norewrite;
    584 			l = devtab[nc->type]->stat(nc, buf, nbuf);
    585 			l = dirsetname(name, nname, buf, l, nbuf);
    586 			if(l == BIT16SZ)
    587 				error("dirsetname");
    588 			poperror();
    589 
    590 			/*
    591 			 * Shift data in buffer to accomodate new entry,
    592 			 * possibly overflowing into rock.
    593 			 */
    594 			rest = e - (p+dirlen);
    595 			if(l > dirlen){
    596 				while(p+l+rest > op+maxn){
    597 					mountrock(c, p, &e);
    598 					if(e == p){
    599 						dirlen = 0;
    600 						goto Norewrite;
    601 					}
    602 					rest = e - (p+dirlen);
    603 				}
    604 			}
    605 			if(l != dirlen){
    606 				memmove(p+l, p+dirlen, rest);
    607 				dirlen = l;
    608 				e = p+dirlen+rest;
    609 			}
    610 
    611 			/*
    612 			 * Rewrite directory entry.
    613 			 */
    614 			memmove(p, buf, l);
    615 
    616 		    Norewrite:
    617 			cclose(nc);
    618 			putmhead(mh);
    619 		}
    620 	}
    621 	if(buf)
    622 		free(buf);
    623 
    624 	if(p != e)
    625 		error("oops in rockfix");
    626 
    627 	return e-op;
    628 }
    629 
    630 static long
    631 doread(uint32 *arg, vlong *offp)
    632 {
    633 	int dir;
    634 	long n, nn, nnn;
    635 	uchar *p;
    636 	Chan *c;
    637 	vlong off;
    638 
    639 	n = arg[2];
    640 	p = uvalidaddr(arg[1], n, 1);
    641 	c = fdtochan(arg[0], OREAD, 1, 1);
    642 
    643 	if(waserror()){
    644 		cclose(c);
    645 		nexterror();
    646 	}
    647 
    648 	/*
    649 	 * The offset is passed through on directories, normally.
    650 	 * Sysseek complains, but pread is used by servers like exportfs,
    651 	 * that shouldn't need to worry about this issue.
    652 	 *
    653 	 * Notice that c->devoffset is the offset that c's dev is seeing.
    654 	 * The number of bytes read on this fd (c->offset) may be different
    655 	 * due to rewritings in rockfix.
    656 	 */
    657 	if(offp == nil)	/* use and maintain channel's offset */
    658 		off = c->offset;
    659 	else
    660 		off = *offp;
    661 	if(off < 0)
    662 		error(Enegoff);
    663 
    664 	if(off == 0){	/* rewind to the beginning of the directory */
    665 		if(offp == nil){
    666 			c->offset = 0;
    667 			c->devoffset = 0;
    668 		}
    669 		mountrewind(c);
    670 		unionrewind(c);
    671 	}
    672 
    673 	dir = c->qid.type&QTDIR;
    674 	if(dir && mountrockread(c, p, n, &nn)){
    675 		/* do nothing: mountrockread filled buffer */
    676 	}else{
    677 		if(dir && c->umh)
    678 			nn = unionread(c, p, n);
    679 		else
    680 			nn = devtab[c->type]->read(c, p, n, off);
    681 	}
    682 	if(dir)
    683 		nnn = mountfix(c, p, nn, n);
    684 	else
    685 		nnn = nn;
    686 
    687 	lock(&c->ref.lk);
    688 	c->devoffset += nn;
    689 	c->offset += nnn;
    690 	unlock(&c->ref.lk);
    691 
    692 	poperror();
    693 	cclose(c);
    694 
    695 	return nnn;
    696 }
    697 
    698 long
    699 sys_read(uint32 *arg)
    700 {
    701 	return doread(arg, nil);
    702 }
    703 
    704 long
    705 syspread(uint32 *arg)
    706 {
    707 	vlong v;
    708 
    709 	// Plan 9 VX replaced dodgy varargs code
    710 	v = *(vlong*)&arg[3];
    711 
    712 	if(v == ~0ULL)
    713 		return doread(arg, nil);
    714 
    715 	return doread(arg, &v);
    716 }
    717 
    718 static long
    719 dowrite(uint32 *arg, vlong *offp)
    720 {
    721 	Chan *c;
    722 	long m, n;
    723 	vlong off;
    724 	uchar *p;
    725 
    726 	p = uvalidaddr(arg[1], arg[2], 0);
    727 	n = 0;
    728 	c = fdtochan(arg[0], OWRITE, 1, 1);
    729 	if(waserror()) {
    730 		if(offp == nil){
    731 			lock(&c->ref.lk);
    732 			c->offset -= n;
    733 			unlock(&c->ref.lk);
    734 		}
    735 		cclose(c);
    736 		nexterror();
    737 	}
    738 
    739 	if(c->qid.type & QTDIR)
    740 		error(Eisdir);
    741 
    742 	n = arg[2];
    743 
    744 	if(offp == nil){	/* use and maintain channel's offset */
    745 		lock(&c->ref.lk);
    746 		off = c->offset;
    747 		c->offset += n;
    748 		unlock(&c->ref.lk);
    749 	}else
    750 		off = *offp;
    751 
    752 	if(off < 0)
    753 		error(Enegoff);
    754 
    755 	m = devtab[c->type]->write(c, p, n, off);
    756 
    757 	if(offp == nil && m < n){
    758 		lock(&c->ref.lk);
    759 		c->offset -= n - m;
    760 		unlock(&c->ref.lk);
    761 	}
    762 
    763 	poperror();
    764 	cclose(c);
    765 
    766 	return m;
    767 }
    768 
    769 long
    770 sys_write(uint32 *arg)
    771 {
    772 	return dowrite(arg, nil);
    773 }
    774 
    775 long
    776 syspwrite(uint32 *arg)
    777 {
    778 	vlong v;
    779 
    780 	// Plan 9 VX replaced dodgy varargs code
    781 	v = *(vlong*)&arg[3];
    782 
    783 	if(v == ~0ULL)
    784 		return dowrite(arg, nil);
    785 
    786 	return dowrite(arg, &v);
    787 }
    788 
    789 static void
    790 sseek(vlong *ret, uint32 *arg)
    791 {
    792 	Chan *c;
    793 	uchar buf[sizeof(Dir)+100];
    794 	Dir dir;
    795 	int n;
    796 	vlong off;
    797 	union {
    798 		vlong v;
    799 		uint32 u[2];
    800 	} o;
    801 
    802 	c = fdtochan(arg[1], -1, 1, 1);
    803 	if(waserror()){
    804 		cclose(c);
    805 		nexterror();
    806 	}
    807 	if(devtab[c->type]->dc == '|')
    808 		error(Eisstream);
    809 
    810 	off = 0;
    811 	o.u[0] = arg[2];
    812 	o.u[1] = arg[3];
    813 	switch(arg[4]){
    814 	case 0:
    815 		off = o.v;
    816 		if((c->qid.type & QTDIR) && off != 0)
    817 			error(Eisdir);
    818 		if(off < 0)
    819 			error(Enegoff);
    820 		c->offset = off;
    821 		break;
    822 
    823 	case 1:
    824 		if(c->qid.type & QTDIR)
    825 			error(Eisdir);
    826 		lock(&c->ref.lk);	/* lock for read/write update */
    827 		off = o.v + c->offset;
    828 		if(off < 0){
    829 			unlock(&c->ref.lk);
    830 			error(Enegoff);
    831 		}
    832 		c->offset = off;
    833 		unlock(&c->ref.lk);
    834 		break;
    835 
    836 	case 2:
    837 		if(c->qid.type & QTDIR)
    838 			error(Eisdir);
    839 		n = devtab[c->type]->stat(c, buf, sizeof buf);
    840 		if(convM2D(buf, n, &dir, nil) == 0)
    841 			error("internal error: stat error in seek");
    842 		off = dir.length + o.v;
    843 		if(off < 0)
    844 			error(Enegoff);
    845 		c->offset = off;
    846 		break;
    847 
    848 	default:
    849 		error(Ebadarg);
    850 	}
    851 	*ret = off;
    852 	c->uri = 0;
    853 	c->dri = 0;
    854 	cclose(c);
    855 	poperror();
    856 }
    857 
    858 long
    859 sysseek(uint32 *arg)
    860 {
    861 	sseek(uvalidaddr(arg[0], BY2V, 1), arg);
    862 	return 0;
    863 }
    864 
    865 long
    866 sysoseek(uint32 *arg)
    867 {
    868 	union {
    869 		vlong v;
    870 		uint32 u[2];
    871 	} o;
    872 	uint32 a[5];
    873 
    874 	o.v = (long)arg[1];
    875 	a[0] = 0;
    876 	a[1] = arg[0];
    877 	a[2] = o.u[0];
    878 	a[3] = o.u[1];
    879 	a[4] = arg[2];
    880 	sseek(&o.v, a);
    881 	return o.v;
    882 }
    883 
    884 void
    885 validstat(uchar *s, int n)
    886 {
    887 	int m;
    888 	char buf[64];
    889 
    890 	if(statcheck(s, n) < 0)
    891 		error(Ebadstat);
    892 	/* verify that name entry is acceptable */
    893 	s += STATFIXLEN - 4*BIT16SZ;	/* location of first string */
    894 	/*
    895 	 * s now points at count for first string.
    896 	 * if it's too long, let the server decide; this is
    897 	 * only for his protection anyway. otherwise
    898 	 * we'd have to allocate and waserror.
    899 	 */
    900 	m = GBIT16(s);
    901 	s += BIT16SZ;
    902 	if(m+1 > sizeof buf)
    903 		return;
    904 	memmove(buf, s, m);
    905 	buf[m] = '\0';
    906 	/* name could be '/' */
    907 	if(strcmp(buf, "/") != 0)
    908 		validname(buf, 0);
    909 }
    910 
    911 static char*
    912 pathlast(Path *p)
    913 {
    914 	char *s;
    915 
    916 	if(p == nil)
    917 		return nil;
    918 	if(p->len == 0)
    919 		return nil;
    920 	s = strrchr(p->s, '/');
    921 	if(s)
    922 		return s+1;
    923 	return p->s;
    924 }
    925 
    926 long
    927 sysfstat(uint32 *arg)
    928 {
    929 	Chan *c;
    930 	uint l;
    931 	uchar *p;
    932 
    933 	l = arg[2];
    934 	p = uvalidaddr(arg[1], l, 1);
    935 	c = fdtochan(arg[0], -1, 0, 1);
    936 	if(waserror()) {
    937 		cclose(c);
    938 		nexterror();
    939 	}
    940 	l = devtab[c->type]->stat(c, p, l);
    941 	poperror();
    942 	cclose(c);
    943 	return l;
    944 }
    945 
    946 long
    947 sysstat(uint32 *arg)
    948 {
    949 	char *name;
    950 	Chan *c;
    951 	uint l;
    952 	uchar *p;
    953 
    954 	l = arg[2];
    955 	p = uvalidaddr(arg[1], l, 1);
    956 	name = uvalidaddr(arg[0], 1, 0);
    957 	c = namec(name, Aaccess, 0, 0);
    958 	if(waserror()){
    959 		cclose(c);
    960 		nexterror();
    961 	}
    962 	l = devtab[c->type]->stat(c, p, l);
    963 	name = pathlast(c->path);
    964 	if(name)
    965 		l = dirsetname(name, strlen(name), p, l, arg[2]);
    966 
    967 	poperror();
    968 	cclose(c);
    969 	return l;
    970 }
    971 
    972 long
    973 syschdir(uint32 *arg)
    974 {
    975 	Chan *c;
    976 	char *name;
    977 
    978 	name = uvalidaddr(arg[0], 1, 0);
    979 
    980 	c = namec(name, Atodir, 0, 0);
    981 	cclose(up->dot);
    982 	up->dot = c;
    983 	return 0;
    984 }
    985 
    986 	// Plan 9 VX added isk parameter.
    987 long
    988 bindmount(int ismount, int fd, int afd, char* arg0, char* arg1, ulong flag, char* spec)
    989 {
    990 	int ret;
    991 	Chan *c0, *c1, *ac, *bc;
    992 	struct{
    993 		Chan	*chan;
    994 		Chan	*authchan;
    995 		char	*spec;
    996 		int	flags;
    997 	}bogus;
    998 
    999 	if((flag&~MMASK) || (flag&MORDER)==(MBEFORE|MAFTER))
   1000 		error(Ebadarg);
   1001 
   1002 	bogus.flags = flag & MCACHE;
   1003 
   1004 	if(ismount){
   1005 		if(up->pgrp->noattach)
   1006 			error(Enoattach);
   1007 
   1008 		ac = nil;
   1009 		bc = fdtochan(fd, ORDWR, 0, 1);
   1010 		if(waserror()) {
   1011 			if(ac)
   1012 				cclose(ac);
   1013 			cclose(bc);
   1014 			nexterror();
   1015 		}
   1016 
   1017 		if(afd >= 0)
   1018 			ac = fdtochan(afd, ORDWR, 0, 1);
   1019 
   1020 		bogus.chan = bc;
   1021 		bogus.authchan = ac;
   1022 
   1023 		bogus.spec = spec;
   1024 		if(waserror())
   1025 			error(Ebadspec);
   1026 		spec = validnamedup(spec, 1);
   1027 		poperror();
   1028 		
   1029 		if(waserror()){
   1030 			free(spec);
   1031 			nexterror();
   1032 		}
   1033 
   1034 		ret = devno('M', 0);
   1035 		c0 = devtab[ret]->attach((char*)&bogus);
   1036 
   1037 		poperror();	/* spec */
   1038 		free(spec);
   1039 		poperror();	/* ac bc */
   1040 		if(ac)
   1041 			cclose(ac);
   1042 		cclose(bc);
   1043 	}else{
   1044 		bogus.spec = 0;
   1045 		c0 = namec(arg0, Abind, 0, 0);
   1046 	}
   1047 
   1048 	if(waserror()){
   1049 		cclose(c0);
   1050 		nexterror();
   1051 	}
   1052 
   1053 	c1 = namec(arg1, Amount, 0, 0);
   1054 	if(waserror()){
   1055 		cclose(c1);
   1056 		nexterror();
   1057 	}
   1058 
   1059 	ret = cmount(&c0, c1, flag, bogus.spec);
   1060 
   1061 	poperror();
   1062 	cclose(c1);
   1063 	poperror();
   1064 	cclose(c0);
   1065 	if(ismount)
   1066 		fdclose(fd, 0);
   1067 
   1068 	return ret;
   1069 }
   1070 
   1071 long
   1072 sysbind(uint32 *arg)
   1073 {
   1074 	return bindmount(0, -1, -1, uvalidaddr(arg[0], 1, 0), uvalidaddr(arg[1], 1, 0), arg[2], nil);
   1075 }
   1076 
   1077 long
   1078 sysmount(uint32 *arg)
   1079 {
   1080 	return bindmount(1, arg[0], arg[1], nil, uvalidaddr(arg[2], 1, 0), arg[3], uvalidaddr(arg[4], 1, 0));
   1081 }
   1082 
   1083 long
   1084 sys_mount(uint32 *arg)
   1085 {
   1086 	return bindmount(1, arg[0], -1, nil, uvalidaddr(arg[1], 1, 0), arg[2], uvalidaddr(arg[3], 1, 0));
   1087 }
   1088 
   1089 long
   1090 sysunmount(uint32 *arg)
   1091 {
   1092 	Chan *cmount, *cmounted;
   1093 	char *mount, *mounted;
   1094 
   1095 	cmounted = 0;
   1096 
   1097 	mount = uvalidaddr(arg[1], 1, 0);
   1098 	cmount = namec(mount, Amount, 0, 0);
   1099 
   1100 	if(arg[0]) {
   1101 		if(waserror()) {
   1102 			cclose(cmount);
   1103 			nexterror();
   1104 		}
   1105 		mounted = uvalidaddr(arg[0], 1, 0);
   1106 		/*
   1107 		 * This has to be namec(..., Aopen, ...) because
   1108 		 * if arg[0] is something like /srv/cs or /fd/0,
   1109 		 * opening it is the only way to get at the real
   1110 		 * Chan underneath.
   1111 		 */
   1112 		cmounted = namec(mounted, Aopen, OREAD, 0);
   1113 		poperror();
   1114 	}
   1115 
   1116 	if(waserror()) {
   1117 		cclose(cmount);
   1118 		if(cmounted)
   1119 			cclose(cmounted);
   1120 		nexterror();
   1121 	}
   1122 
   1123 	cunmount(cmount, cmounted);
   1124 	cclose(cmount);
   1125 	if(cmounted)
   1126 		cclose(cmounted);
   1127 	poperror();
   1128 	return 0;
   1129 }
   1130 
   1131 long
   1132 syscreate(uint32 *arg)
   1133 {
   1134 	int fd;
   1135 	Chan *c = 0;
   1136 	char *name;
   1137 
   1138 	openmode(arg[1]&~OEXCL);	/* error check only; OEXCL okay here */
   1139 	if(waserror()) {
   1140 		if(c)
   1141 			cclose(c);
   1142 		nexterror();
   1143 	}
   1144 	name = uvalidaddr(arg[0], 1, 0);
   1145 	c = namec(name, Acreate, arg[1], arg[2]);
   1146 	fd = newfd(c);
   1147 	if(fd < 0)
   1148 		error(Enofd);
   1149 	poperror();
   1150 	return fd;
   1151 }
   1152 
   1153 long
   1154 sysremove(uint32 *arg)
   1155 {
   1156 	Chan *c;
   1157 	char *name;
   1158 
   1159 	name = uvalidaddr(arg[0], 1, 0);
   1160 	c = namec(name, Aremove, 0, 0);
   1161 	/*
   1162 	 * Removing mount points is disallowed to avoid surprises
   1163 	 * (which should be removed: the mount point or the mounted Chan?).
   1164 	 */
   1165 	if(c->ismtpt){
   1166 		cclose(c);
   1167 		error(Eismtpt);
   1168 	}
   1169 	if(waserror()){
   1170 		c->type = 0;	/* see below */
   1171 		cclose(c);
   1172 		nexterror();
   1173 	}
   1174 	devtab[c->type]->remove(c);
   1175 	/*
   1176 	 * Remove clunks the fid, but we need to recover the Chan
   1177 	 * so fake it up.  rootclose() is known to be a nop.
   1178 	 */
   1179 	c->type = 0;
   1180 	poperror();
   1181 	cclose(c);
   1182 	return 0;
   1183 }
   1184 
   1185 static long
   1186 wstat(Chan *c, uchar *d, int nd)
   1187 {
   1188 	long l;
   1189 	int namelen;
   1190 
   1191 	if(waserror()){
   1192 		cclose(c);
   1193 		nexterror();
   1194 	}
   1195 	if(c->ismtpt){
   1196 		/*
   1197 		 * Renaming mount points is disallowed to avoid surprises
   1198 		 * (which should be renamed? the mount point or the mounted Chan?).
   1199 		 */
   1200 		dirname(d, &namelen);
   1201 		if(namelen)
   1202 			nameerror(chanpath(c), Eismtpt);
   1203 	}
   1204 	l = devtab[c->type]->wstat(c, d, nd);
   1205 	poperror();
   1206 	cclose(c);
   1207 	return l;
   1208 }
   1209 
   1210 long
   1211 syswstat(uint32 *arg)
   1212 {
   1213 	Chan *c;
   1214 	uint l;
   1215 	char *name;
   1216 	uchar *p;
   1217 
   1218 	l = arg[2];
   1219 	p = uvalidaddr(arg[1], l, 0);
   1220 	validstat(p, l);
   1221 	name = uvalidaddr(arg[0], 1, 0);
   1222 	c = namec(name, Aaccess, 0, 0);
   1223 	return wstat(c, p, l);
   1224 }
   1225 
   1226 long
   1227 sysfwstat(uint32 *arg)
   1228 {
   1229 	Chan *c;
   1230 	uint l;
   1231 	uchar *p;
   1232 
   1233 	l = arg[2];
   1234 	p = uvalidaddr(arg[1], l, 0);
   1235 	validstat(p, l);
   1236 	c = fdtochan(arg[0], -1, 1, 1);
   1237 	return wstat(c, p, l);
   1238 }
   1239 
   1240 static void
   1241 packoldstat(uchar *buf, Dir *d)
   1242 {
   1243 	uchar *p;
   1244 	ulong q;
   1245 
   1246 	/* lay down old stat buffer - grotty code but it's temporary */
   1247 	p = buf;
   1248 	strncpy((char*)p, d->name, 28);
   1249 	p += 28;
   1250 	strncpy((char*)p, d->uid, 28);
   1251 	p += 28;
   1252 	strncpy((char*)p, d->gid, 28);
   1253 	p += 28;
   1254 	q = d->qid.path & ~DMDIR;	/* make sure doesn't accidentally look like directory */
   1255 	if(d->qid.type & QTDIR)	/* this is the real test of a new directory */
   1256 		q |= DMDIR;
   1257 	PBIT32(p, q);
   1258 	p += BIT32SZ;
   1259 	PBIT32(p, d->qid.vers);
   1260 	p += BIT32SZ;
   1261 	PBIT32(p, d->mode);
   1262 	p += BIT32SZ;
   1263 	PBIT32(p, d->atime);
   1264 	p += BIT32SZ;
   1265 	PBIT32(p, d->mtime);
   1266 	p += BIT32SZ;
   1267 	PBIT64(p, d->length);
   1268 	p += BIT64SZ;
   1269 	PBIT16(p, d->type);
   1270 	p += BIT16SZ;
   1271 	PBIT16(p, d->dev);
   1272 }
   1273 
   1274 long
   1275 sys_stat(uint32 *arg)
   1276 {
   1277 	Chan *c;
   1278 	uint l;
   1279 	uchar buf[128];	/* old DIRLEN plus a little should be plenty */
   1280 	char strs[128], *name, *elem;
   1281 	Dir d;
   1282 	char old[] = "old stat system call - recompile";
   1283 	uchar *p;
   1284 
   1285 	p = uvalidaddr(arg[1], 116, 1);
   1286 	name = uvalidaddr(arg[0], 1, 0);
   1287 	c = namec(name, Aaccess, 0, 0);
   1288 	if(waserror()){
   1289 		cclose(c);
   1290 		nexterror();
   1291 	}
   1292 	l = devtab[c->type]->stat(c, buf, sizeof buf);
   1293 	/* buf contains a new stat buf; convert to old. yuck. */
   1294 	if(l <= BIT16SZ)	/* buffer too small; time to face reality */
   1295 		error(old);
   1296 	elem = pathlast(c->path);
   1297 	if(elem)
   1298 		l = dirsetname(elem, strlen(elem), buf, l, sizeof buf);
   1299 	l = convM2D(buf, l, &d, strs);
   1300 	if(l == 0)
   1301 		error(old);
   1302 	packoldstat(p, &d);
   1303 	
   1304 	poperror();
   1305 	cclose(c);
   1306 	return 0;
   1307 }
   1308 
   1309 long
   1310 sys_fstat(uint32 *arg)
   1311 {
   1312 	Chan *c;
   1313 	char *name;
   1314 	uint l;
   1315 	uchar buf[128];	/* old DIRLEN plus a little should be plenty */
   1316 	char strs[128];
   1317 	Dir d;
   1318 	char old[] = "old fstat system call - recompile";
   1319 	uchar *p;
   1320 
   1321 	p = uvalidaddr(arg[1], 116, 1);
   1322 	c = fdtochan(arg[0], -1, 0, 1);
   1323 	if(waserror()){
   1324 		cclose(c);
   1325 		nexterror();
   1326 	}
   1327 	l = devtab[c->type]->stat(c, buf, sizeof buf);
   1328 	/* buf contains a new stat buf; convert to old. yuck. */
   1329 	if(l <= BIT16SZ)	/* buffer too small; time to face reality */
   1330 		error(old);
   1331 	name = pathlast(c->path);
   1332 	if(name)
   1333 		l = dirsetname(name, strlen(name), buf, l, sizeof buf);
   1334 	l = convM2D(buf, l, &d, strs);
   1335 	if(l == 0)
   1336 		error(old);
   1337 	packoldstat(p, &d);
   1338 	
   1339 	poperror();
   1340 	cclose(c);
   1341 	return 0;
   1342 }
   1343 
   1344 long
   1345 sys_wstat(uint32 *u)
   1346 {
   1347 	error("old wstat system call - recompile");
   1348 	return -1;
   1349 }
   1350 
   1351 long
   1352 sys_fwstat(uint32 *u)
   1353 {
   1354 	error("old fwstat system call - recompile");
   1355 	return -1;
   1356 }
   1357 
   1358 // Plan 9 VX additions
   1359 long
   1360 kbind(char *new, char *old, int flag)
   1361 {
   1362 	return bindmount(0, -1, -1, new, old, flag, nil);
   1363 }
   1364 
   1365 long
   1366 syspassfd(uint32 *u)
   1367 {
   1368 	error("passfd unimplemented");
   1369 	return -1;
   1370 }
   1371