xref: /plan9-contrib/sys/src/9/port/sysfile.c (revision 6221fdcc6044408072a88079fb6883c2cd00e432)
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 	if(c->qid.type & QTDIR || offp == nil){
684 		lock(c);
685 		c->devoffset += nn;
686 		c->offset += nnn;
687 		unlock(c);
688 	}
689 
690 	poperror();
691 	cclose(c);
692 
693 	return nnn;
694 }
695 
696 long
sys_read(ulong * arg)697 sys_read(ulong *arg)
698 {
699 	return read(arg, nil);
700 }
701 
702 long
syspread(ulong * arg)703 syspread(ulong *arg)
704 {
705 	vlong v;
706 	va_list list;
707 
708 	/* use varargs to guarantee alignment of vlong */
709 	va_start(list, arg[2]);
710 	v = va_arg(list, vlong);
711 	va_end(list);
712 
713 	if(v == ~0ULL)
714 		return read(arg, nil);
715 
716 	return read(arg, &v);
717 }
718 
719 static long
write(ulong * arg,vlong * offp)720 write(ulong *arg, vlong *offp)
721 {
722 	Chan *c;
723 	long m, n;
724 	vlong off;
725 
726 	validaddr(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);
732 			c->offset -= n;
733 			unlock(c);
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);
746 		off = c->offset;
747 		c->offset += n;
748 		unlock(c);
749 	}else
750 		off = *offp;
751 
752 	if(off < 0)
753 		error(Enegoff);
754 
755 	m = devtab[c->type]->write(c, (void*)arg[1], n, off);
756 
757 	if(offp == nil && m < n){
758 		lock(c);
759 		c->offset -= n - m;
760 		unlock(c);
761 	}
762 
763 	poperror();
764 	cclose(c);
765 
766 	return m;
767 }
768 
769 long
sys_write(ulong * arg)770 sys_write(ulong *arg)
771 {
772 	return write(arg, nil);
773 }
774 
775 long
syspwrite(ulong * arg)776 syspwrite(ulong *arg)
777 {
778 	vlong v;
779 	va_list list;
780 
781 	/* use varargs to guarantee alignment of vlong */
782 	va_start(list, arg[2]);
783 	v = va_arg(list, vlong);
784 	va_end(list);
785 
786 	if(v == ~0ULL)
787 		return write(arg, nil);
788 
789 	return write(arg, &v);
790 }
791 
792 static void
sseek(ulong * arg)793 sseek(ulong *arg)
794 {
795 	Chan *c;
796 	uchar buf[sizeof(Dir)+100];
797 	Dir dir;
798 	int n;
799 	vlong off;
800 	union {
801 		vlong v;
802 		ulong u[2];
803 	} o;
804 
805 	c = fdtochan(arg[1], -1, 1, 1);
806 	if(waserror()){
807 		cclose(c);
808 		nexterror();
809 	}
810 	if(devtab[c->type]->dc == '|')
811 		error(Eisstream);
812 
813 	off = 0;
814 	o.u[0] = arg[2];
815 	o.u[1] = arg[3];
816 	switch(arg[4]){
817 	case 0:
818 		off = o.v;
819 		if((c->qid.type & QTDIR) && off != 0)
820 			error(Eisdir);
821 		if(off < 0)
822 			error(Enegoff);
823 		c->offset = off;
824 		break;
825 
826 	case 1:
827 		if(c->qid.type & QTDIR)
828 			error(Eisdir);
829 		lock(c);	/* lock for read/write update */
830 		off = o.v + c->offset;
831 		if(off < 0){
832 			unlock(c);
833 			error(Enegoff);
834 		}
835 		c->offset = off;
836 		unlock(c);
837 		break;
838 
839 	case 2:
840 		if(c->qid.type & QTDIR)
841 			error(Eisdir);
842 		n = devtab[c->type]->stat(c, buf, sizeof buf);
843 		if(convM2D(buf, n, &dir, nil) == 0)
844 			error("internal error: stat error in seek");
845 		off = dir.length + o.v;
846 		if(off < 0)
847 			error(Enegoff);
848 		c->offset = off;
849 		break;
850 
851 	default:
852 		error(Ebadarg);
853 	}
854 	*(vlong*)arg[0] = off;
855 	c->uri = 0;
856 	c->dri = 0;
857 	cclose(c);
858 	poperror();
859 }
860 
861 long
sysseek(ulong * arg)862 sysseek(ulong *arg)
863 {
864 	validaddr(arg[0], sizeof(vlong), 1);
865 	validalign(arg[0], sizeof(vlong));
866 	sseek(arg);
867 	return 0;
868 }
869 
870 long
sysoseek(ulong * arg)871 sysoseek(ulong *arg)
872 {
873 	union {
874 		vlong v;
875 		ulong u[2];
876 	} o;
877 	ulong a[5];
878 
879 	o.v = (long)arg[1];
880 	a[0] = (ulong)&o.v;
881 	a[1] = arg[0];
882 	a[2] = o.u[0];
883 	a[3] = o.u[1];
884 	a[4] = arg[2];
885 	sseek(a);
886 	return o.v;
887 }
888 
889 void
validstat(uchar * s,int n)890 validstat(uchar *s, int n)
891 {
892 	int m;
893 	char buf[64];
894 
895 	if(statcheck(s, n) < 0)
896 		error(Ebadstat);
897 	/* verify that name entry is acceptable */
898 	s += STATFIXLEN - 4*BIT16SZ;	/* location of first string */
899 	/*
900 	 * s now points at count for first string.
901 	 * if it's too long, let the server decide; this is
902 	 * only for his protection anyway. otherwise
903 	 * we'd have to allocate and waserror.
904 	 */
905 	m = GBIT16(s);
906 	s += BIT16SZ;
907 	if(m+1 > sizeof buf)
908 		return;
909 	memmove(buf, s, m);
910 	buf[m] = '\0';
911 	/* name could be '/' */
912 	if(strcmp(buf, "/") != 0)
913 		validname(buf, 0);
914 }
915 
916 static char*
pathlast(Path * p)917 pathlast(Path *p)
918 {
919 	char *s;
920 
921 	if(p == nil)
922 		return nil;
923 	if(p->len == 0)
924 		return nil;
925 	s = strrchr(p->s, '/');
926 	if(s)
927 		return s+1;
928 	return p->s;
929 }
930 
931 long
sysfstat(ulong * arg)932 sysfstat(ulong *arg)
933 {
934 	Chan *c;
935 	uint l;
936 
937 	l = arg[2];
938 	validaddr(arg[1], l, 1);
939 	c = fdtochan(arg[0], -1, 0, 1);
940 	if(waserror()) {
941 		cclose(c);
942 		nexterror();
943 	}
944 	l = devtab[c->type]->stat(c, (uchar*)arg[1], l);
945 	poperror();
946 	cclose(c);
947 	return l;
948 }
949 
950 long
sysstat(ulong * arg)951 sysstat(ulong *arg)
952 {
953 	char *name;
954 	Chan *c;
955 	uint l;
956 
957 	l = arg[2];
958 	validaddr(arg[1], l, 1);
959 	validaddr(arg[0], 1, 0);
960 	c = namec((char*)arg[0], Aaccess, 0, 0);
961 	if(waserror()){
962 		cclose(c);
963 		nexterror();
964 	}
965 	l = devtab[c->type]->stat(c, (uchar*)arg[1], l);
966 	name = pathlast(c->path);
967 	if(name)
968 		l = dirsetname(name, strlen(name), (uchar*)arg[1], l, arg[2]);
969 
970 	poperror();
971 	cclose(c);
972 	return l;
973 }
974 
975 long
syschdir(ulong * arg)976 syschdir(ulong *arg)
977 {
978 	Chan *c;
979 
980 	validaddr(arg[0], 1, 0);
981 
982 	c = namec((char*)arg[0], Atodir, 0, 0);
983 	cclose(up->dot);
984 	up->dot = c;
985 	return 0;
986 }
987 
988 long
bindmount(int ismount,int fd,int afd,char * arg0,char * arg1,ulong flag,char * spec)989 bindmount(int ismount, int fd, int afd, char* arg0, char* arg1, ulong flag, char* spec)
990 {
991 	int ret;
992 	Chan *c0, *c1, *ac, *bc;
993 	struct{
994 		Chan	*chan;
995 		Chan	*authchan;
996 		char	*spec;
997 		int	flags;
998 	}bogus;
999 
1000 	if((flag&~MMASK) || (flag&MORDER)==(MBEFORE|MAFTER))
1001 		error(Ebadarg);
1002 
1003 	if(ismount){
1004 		validaddr((ulong)spec, 1, 0);
1005 		spec = validnamedup(spec, 1);
1006 		if(waserror()){
1007 			free(spec);
1008 			nexterror();
1009 		}
1010 
1011 		if(up->pgrp->noattach)
1012 			error(Enoattach);
1013 
1014 		ac = nil;
1015 		bc = fdtochan(fd, ORDWR, 0, 1);
1016 		if(waserror()) {
1017 			if(ac)
1018 				cclose(ac);
1019 			cclose(bc);
1020 			nexterror();
1021 		}
1022 
1023 		if(afd >= 0)
1024 			ac = fdtochan(afd, ORDWR, 0, 1);
1025 
1026 		bogus.flags = flag & MCACHE;
1027 		bogus.chan = bc;
1028 		bogus.authchan = ac;
1029 		bogus.spec = spec;
1030 		ret = devno('M', 0);
1031 		c0 = devtab[ret]->attach((char*)&bogus);
1032 		poperror();	/* ac bc */
1033 		if(ac)
1034 			cclose(ac);
1035 		cclose(bc);
1036 	}else{
1037 		spec = 0;
1038 		validaddr((ulong)arg0, 1, 0);
1039 		c0 = namec(arg0, Abind, 0, 0);
1040 	}
1041 
1042 	if(waserror()){
1043 		cclose(c0);
1044 		nexterror();
1045 	}
1046 
1047 	validaddr((ulong)arg1, 1, 0);
1048 	c1 = namec(arg1, Amount, 0, 0);
1049 	if(waserror()){
1050 		cclose(c1);
1051 		nexterror();
1052 	}
1053 
1054 	ret = cmount(&c0, c1, flag, spec);
1055 
1056 	poperror();
1057 	cclose(c1);
1058 	poperror();
1059 	cclose(c0);
1060 	if(ismount){
1061 		fdclose(fd, 0);
1062 		poperror();
1063 		free(spec);
1064 	}
1065 	return ret;
1066 }
1067 
1068 long
sysbind(ulong * arg)1069 sysbind(ulong *arg)
1070 {
1071 	return bindmount(0, -1, -1, (char*)arg[0], (char*)arg[1], arg[2], nil);
1072 }
1073 
1074 long
sysmount(ulong * arg)1075 sysmount(ulong *arg)
1076 {
1077 	return bindmount(1, arg[0], arg[1], nil, (char*)arg[2], arg[3], (char*)arg[4]);
1078 }
1079 
1080 long
sys_mount(ulong * arg)1081 sys_mount(ulong *arg)
1082 {
1083 	return bindmount(1, arg[0], -1, nil, (char*)arg[1], arg[2], (char*)arg[3]);
1084 }
1085 
1086 long
sysunmount(ulong * arg)1087 sysunmount(ulong *arg)
1088 {
1089 	Chan *cmount, *cmounted;
1090 
1091 	cmounted = 0;
1092 
1093 	validaddr(arg[1], 1, 0);
1094 	cmount = namec((char *)arg[1], Amount, 0, 0);
1095 	if(waserror()) {
1096 		cclose(cmount);
1097 		if(cmounted)
1098 			cclose(cmounted);
1099 		nexterror();
1100 	}
1101 
1102 	if(arg[0]) {
1103 		/*
1104 		 * This has to be namec(..., Aopen, ...) because
1105 		 * if arg[0] is something like /srv/cs or /fd/0,
1106 		 * opening it is the only way to get at the real
1107 		 * Chan underneath.
1108 		 */
1109 		validaddr(arg[0], 1, 0);
1110 		cmounted = namec((char*)arg[0], Aopen, OREAD, 0);
1111 	}
1112 	cunmount(cmount, cmounted);
1113 	poperror();
1114 	cclose(cmount);
1115 	if(cmounted)
1116 		cclose(cmounted);
1117 	return 0;
1118 }
1119 
1120 long
syscreate(ulong * arg)1121 syscreate(ulong *arg)
1122 {
1123 	int fd;
1124 	Chan *c;
1125 
1126 	openmode(arg[1]&~OEXCL);	/* error check only; OEXCL okay here */
1127 	validaddr(arg[0], 1, 0);
1128 	c = namec((char*)arg[0], Acreate, arg[1], arg[2]);
1129 	if(waserror()) {
1130 		cclose(c);
1131 		nexterror();
1132 	}
1133 	fd = newfd(c);
1134 	if(fd < 0)
1135 		error(Enofd);
1136 	poperror();
1137 	return fd;
1138 }
1139 
1140 long
sysremove(ulong * arg)1141 sysremove(ulong *arg)
1142 {
1143 	Chan *c;
1144 
1145 	validaddr(arg[0], 1, 0);
1146 	c = namec((char*)arg[0], Aremove, 0, 0);
1147 	/*
1148 	 * Removing mount points is disallowed to avoid surprises
1149 	 * (which should be removed: the mount point or the mounted Chan?).
1150 	 */
1151 	if(c->ismtpt){
1152 		cclose(c);
1153 		error(Eismtpt);
1154 	}
1155 	if(waserror()){
1156 		c->type = 0;	/* see below */
1157 		cclose(c);
1158 		nexterror();
1159 	}
1160 	devtab[c->type]->remove(c);
1161 	/*
1162 	 * Remove clunks the fid, but we need to recover the Chan
1163 	 * so fake it up.  rootclose() is known to be a nop.
1164 	 */
1165 	c->type = 0;
1166 	poperror();
1167 	cclose(c);
1168 	return 0;
1169 }
1170 
1171 static long
wstat(Chan * c,uchar * d,int nd)1172 wstat(Chan *c, uchar *d, int nd)
1173 {
1174 	long l;
1175 	int namelen;
1176 
1177 	if(waserror()){
1178 		cclose(c);
1179 		nexterror();
1180 	}
1181 	if(c->ismtpt){
1182 		/*
1183 		 * Renaming mount points is disallowed to avoid surprises
1184 		 * (which should be renamed? the mount point or the mounted Chan?).
1185 		 */
1186 		dirname(d, &namelen);
1187 		if(namelen)
1188 			nameerror(chanpath(c), Eismtpt);
1189 	}
1190 	l = devtab[c->type]->wstat(c, d, nd);
1191 	poperror();
1192 	cclose(c);
1193 	return l;
1194 }
1195 
1196 long
syswstat(ulong * arg)1197 syswstat(ulong *arg)
1198 {
1199 	Chan *c;
1200 	uint l;
1201 
1202 	l = arg[2];
1203 	validaddr(arg[1], l, 0);
1204 	validstat((uchar*)arg[1], l);
1205 	validaddr(arg[0], 1, 0);
1206 	c = namec((char*)arg[0], Aaccess, 0, 0);
1207 	return wstat(c, (uchar*)arg[1], l);
1208 }
1209 
1210 long
sysfwstat(ulong * arg)1211 sysfwstat(ulong *arg)
1212 {
1213 	Chan *c;
1214 	uint l;
1215 
1216 	l = arg[2];
1217 	validaddr(arg[1], l, 0);
1218 	validstat((uchar*)arg[1], l);
1219 	c = fdtochan(arg[0], -1, 1, 1);
1220 	return wstat(c, (uchar*)arg[1], l);
1221 }
1222 
1223 static void
packoldstat(uchar * buf,Dir * d)1224 packoldstat(uchar *buf, Dir *d)
1225 {
1226 	uchar *p;
1227 	ulong q;
1228 
1229 	/* lay down old stat buffer - grotty code but it's temporary */
1230 	p = buf;
1231 	strncpy((char*)p, d->name, 28);
1232 	p += 28;
1233 	strncpy((char*)p, d->uid, 28);
1234 	p += 28;
1235 	strncpy((char*)p, d->gid, 28);
1236 	p += 28;
1237 	q = d->qid.path & ~(uvlong)DMDIR;	/* make sure doesn't accidentally look like directory */
1238 	if(d->qid.type & QTDIR)	/* this is the real test of a new directory */
1239 		q |= DMDIR;
1240 	PBIT32(p, q);
1241 	p += BIT32SZ;
1242 	PBIT32(p, d->qid.vers);
1243 	p += BIT32SZ;
1244 	PBIT32(p, d->mode);
1245 	p += BIT32SZ;
1246 	PBIT32(p, d->atime);
1247 	p += BIT32SZ;
1248 	PBIT32(p, d->mtime);
1249 	p += BIT32SZ;
1250 	PBIT64(p, d->length);
1251 	p += BIT64SZ;
1252 	PBIT16(p, d->type);
1253 	p += BIT16SZ;
1254 	PBIT16(p, d->dev);
1255 }
1256 
1257 long
sys_stat(ulong * arg)1258 sys_stat(ulong *arg)
1259 {
1260 	Chan *c;
1261 	uint l;
1262 	uchar buf[128];	/* old DIRLEN plus a little should be plenty */
1263 	char strs[128], *name;
1264 	Dir d;
1265 	char old[] = "old stat system call - recompile";
1266 
1267 	validaddr(arg[1], 116, 1);
1268 	validaddr(arg[0], 1, 0);
1269 	c = namec((char*)arg[0], Aaccess, 0, 0);
1270 	if(waserror()){
1271 		cclose(c);
1272 		nexterror();
1273 	}
1274 	l = devtab[c->type]->stat(c, buf, sizeof buf);
1275 	/* buf contains a new stat buf; convert to old. yuck. */
1276 	if(l <= BIT16SZ)	/* buffer too small; time to face reality */
1277 		error(old);
1278 	name = pathlast(c->path);
1279 	if(name)
1280 		l = dirsetname(name, strlen(name), buf, l, sizeof buf);
1281 	l = convM2D(buf, l, &d, strs);
1282 	if(l == 0)
1283 		error(old);
1284 	packoldstat((uchar*)arg[1], &d);
1285 
1286 	poperror();
1287 	cclose(c);
1288 	return 0;
1289 }
1290 
1291 long
sys_fstat(ulong * arg)1292 sys_fstat(ulong *arg)
1293 {
1294 	Chan *c;
1295 	char *name;
1296 	uint l;
1297 	uchar buf[128];	/* old DIRLEN plus a little should be plenty */
1298 	char strs[128];
1299 	Dir d;
1300 	char old[] = "old fstat system call - recompile";
1301 
1302 	validaddr(arg[1], 116, 1);
1303 	c = fdtochan(arg[0], -1, 0, 1);
1304 	if(waserror()){
1305 		cclose(c);
1306 		nexterror();
1307 	}
1308 	l = devtab[c->type]->stat(c, buf, sizeof buf);
1309 	/* buf contains a new stat buf; convert to old. yuck. */
1310 	if(l <= BIT16SZ)	/* buffer too small; time to face reality */
1311 		error(old);
1312 	name = pathlast(c->path);
1313 	if(name)
1314 		l = dirsetname(name, strlen(name), buf, l, sizeof buf);
1315 	l = convM2D(buf, l, &d, strs);
1316 	if(l == 0)
1317 		error(old);
1318 	packoldstat((uchar*)arg[1], &d);
1319 
1320 	poperror();
1321 	cclose(c);
1322 	return 0;
1323 }
1324 
1325 long
sys_wstat(ulong *)1326 sys_wstat(ulong *)
1327 {
1328 	error("old wstat system call - recompile");
1329 	return -1;
1330 }
1331 
1332 long
sys_fwstat(ulong *)1333 sys_fwstat(ulong *)
1334 {
1335 	error("old fwstat system call - recompile");
1336 	return -1;
1337 }
1338