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