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