]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/sysfile.c
pc kernel: fix wrong simd exception mask (fixes go bootstrap)
[plan9front.git] / sys / src / 9 / port / sysfile.c
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
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
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 == nil)
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] == nil)
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);
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);
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         return 0;
117 }
118
119 Chan*
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])==nil) {
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         return c;
158 }
159
160 int
161 openmode(ulong o)
162 {
163         o &= ~(OTRUNC|OCEXEC|ORCLOSE);
164         if(o > OEXEC)
165                 error(Ebadarg);
166         if(o == OEXEC)
167                 return OREAD;
168         return o;
169 }
170
171 uintptr
172 sysfd2path(va_list list)
173 {
174         Chan *c;
175         char *buf;
176         uint len;
177         int fd;
178
179         fd = va_arg(list, int);
180         buf = va_arg(list, char*);
181         len = va_arg(list, uint);
182         validaddr((uintptr)buf, len, 1);
183         c = fdtochan(fd, -1, 0, 1);
184         snprint(buf, len, "%s", chanpath(c));
185         cclose(c);
186         return 0;
187 }
188
189 uintptr
190 syspipe(va_list list)
191 {
192         static char *datastr[] = {"data", "data1"};
193         int fd[2], *ufd;
194         Chan *c[2];
195
196         ufd = va_arg(list, int*);
197         validaddr((uintptr)ufd, sizeof(fd), 1);
198         evenaddr((uintptr)ufd);
199         
200         ufd[0] = ufd[1] = fd[0] = fd[1] = -1;
201         c[0] = namec("#|", Atodir, 0, 0);
202         c[1] = nil;
203         if(waserror()){
204                 cclose(c[0]);
205                 if(c[1] != nil)
206                         cclose(c[1]);
207                 nexterror();
208         }
209         c[1] = cclone(c[0]);
210         if(walk(&c[0], datastr+0, 1, 1, nil) < 0)
211                 error(Egreg);
212         if(walk(&c[1], datastr+1, 1, 1, nil) < 0)
213                 error(Egreg);
214         c[0] = devtab[c[0]->type]->open(c[0], ORDWR);
215         c[1] = devtab[c[1]->type]->open(c[1], ORDWR);
216         if(newfd2(fd, c) < 0)
217                 error(Enofd);
218         ufd[0] = fd[0];
219         ufd[1] = fd[1];
220         poperror();
221         return 0;
222 }
223
224 uintptr
225 sysdup(va_list list)
226 {
227         int fd;
228         Chan *c, *oc;
229         Fgrp *f = up->fgrp;
230
231         fd = va_arg(list, int);
232
233         /*
234          * Close after dup'ing, so date > #d/1 works
235          */
236         c = fdtochan(fd, -1, 0, 1);
237         fd = va_arg(list, int);
238         if(fd != -1){
239                 lock(f);
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 != nil)
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         return (uintptr)fd;
264 }
265
266 uintptr
267 sysopen(va_list list)
268 {
269         int fd;
270         Chan *c;
271         char *name;
272         ulong mode;
273
274         name = va_arg(list, char*);
275         mode = va_arg(list, ulong);
276         openmode(mode); /* error check only */
277         validaddr((uintptr)name, 1, 0);
278         c = namec(name, Aopen, mode, 0);
279         if(waserror()){
280                 cclose(c);
281                 nexterror();
282         }
283         fd = newfd(c);
284         if(fd < 0)
285                 error(Enofd);
286         poperror();
287         return (uintptr)fd;
288 }
289
290 void
291 fdclose(int fd, int flag)
292 {
293         Chan *c;
294         Fgrp *f = up->fgrp;
295
296         lock(f);
297         c = fd <= f->maxfd ? f->fd[fd] : nil;
298         if(c == nil || (flag != 0 && (c->flag&flag) == 0)){
299                 unlock(f);
300                 return;
301         }
302         f->fd[fd] = nil;
303         if(fd == f->maxfd){
304                 while(fd > 0 && f->fd[fd] == nil)
305                         f->maxfd = --fd;
306         }
307         unlock(f);
308         cclose(c);
309 }
310
311 uintptr
312 sysclose(va_list list)
313 {
314         int fd;
315
316         fd = va_arg(list, int);
317         fdtochan(fd, -1, 0, 0);
318         fdclose(fd, 0);
319         return 0;
320 }
321
322 long
323 unionread(Chan *c, void *va, long n)
324 {
325         int i;
326         long nr;
327         Mhead *m;
328         Mount *mount;
329
330         eqlock(&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 != nil && !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 != nil){
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
368 unionrewind(Chan *c)
369 {
370         eqlock(&c->umqlock);
371         c->uri = 0;
372         if(c->umc != nil){
373                 cclose(c->umc);
374                 c->umc = nil;
375         }
376         qunlock(&c->umqlock);
377 }
378
379 static int
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*
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
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
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
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
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
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 != nil; 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 != nil)
618                 free(buf);
619
620         if(p != e)
621                 error("oops in rockfix");
622
623         return e-op;
624 }
625
626 static long
627 read(int fd, uchar *p, long n, vlong *offp)
628 {
629         long nn, nnn;
630         Chan *c;
631         vlong off;
632
633         validaddr((uintptr)p, n, 1);
634         c = fdtochan(fd, OREAD, 1, 1);
635
636         if(waserror()){
637                 cclose(c);
638                 nexterror();
639         }
640
641         /*
642          * The offset is passed through on directories, normally.
643          * Sysseek complains, but pread is used by servers like exportfs,
644          * that shouldn't need to worry about this issue.
645          *
646          * Notice that c->devoffset is the offset that c's dev is seeing.
647          * The number of bytes read on this fd (c->offset) may be different
648          * due to rewritings in rockfix.
649          */
650         if(offp == nil) /* use and maintain channel's offset */
651                 off = c->offset;
652         else
653                 off = *offp;
654         if(off < 0)
655                 error(Enegoff);
656
657         if(off == 0){   /* rewind to the beginning of the directory */
658                 if(offp == nil || (c->qid.type & QTDIR)){
659                         c->offset = 0;
660                         c->devoffset = 0;
661                 }
662                 mountrewind(c);
663                 unionrewind(c);
664         }
665
666         if(c->qid.type & QTDIR){
667                 if(mountrockread(c, p, n, &nn)){
668                         /* do nothing: mountrockread filled buffer */
669                 }else if(c->umh != nil)
670                         nn = unionread(c, p, n);
671                 else{
672                         if(off != c->offset)
673                                 error(Edirseek);
674                         nn = devtab[c->type]->read(c, p, n, c->devoffset);
675                 }
676                 nnn = mountfix(c, p, nn, n);
677         }else
678                 nnn = nn = devtab[c->type]->read(c, p, n, off);
679
680         if(offp == nil || (c->qid.type & QTDIR)){
681                 lock(c);
682                 c->devoffset += nn;
683                 c->offset += nnn;
684                 unlock(c);
685         }
686
687         poperror();
688         cclose(c);
689         return nnn;
690 }
691
692 uintptr
693 sys_read(va_list list)
694 {
695         int fd;
696         void *buf;
697         long len;
698
699         fd = va_arg(list, int);
700         buf = va_arg(list, void*);
701         len = va_arg(list, long);
702         return (uintptr)read(fd, buf, len, nil);
703 }
704
705 uintptr
706 syspread(va_list list)
707 {
708         int fd;
709         void *buf;
710         long len;
711         vlong off, *offp;
712
713         fd = va_arg(list, int);
714         buf = va_arg(list, void*);
715         len = va_arg(list, long);
716         off = va_arg(list, vlong);
717         if(off != ~0ULL)
718                 offp = &off;
719         else
720                 offp = nil;
721         return (uintptr)read(fd, buf, len, offp);
722 }
723
724 static long
725 write(int fd, void *buf, long len, vlong *offp)
726 {
727         Chan *c;
728         long m, n;
729         vlong off;
730
731         validaddr((uintptr)buf, len, 0);
732         n = 0;
733         c = fdtochan(fd, OWRITE, 1, 1);
734         if(waserror()) {
735                 if(offp == nil){
736                         lock(c);
737                         c->offset -= n;
738                         unlock(c);
739                 }
740                 cclose(c);
741                 nexterror();
742         }
743
744         if(c->qid.type & QTDIR)
745                 error(Eisdir);
746
747         n = len;
748
749         if(offp == nil){        /* use and maintain channel's offset */
750                 lock(c);
751                 off = c->offset;
752                 c->offset += n;
753                 unlock(c);
754         }else
755                 off = *offp;
756
757         if(off < 0)
758                 error(Enegoff);
759
760         m = devtab[c->type]->write(c, buf, n, off);
761         if(offp == nil && m < n){
762                 lock(c);
763                 c->offset -= n - m;
764                 unlock(c);
765         }
766
767         poperror();
768         cclose(c);
769         return m;
770 }
771
772 uintptr
773 sys_write(va_list list)
774 {
775         int fd;
776         void *buf;
777         long len;
778
779         fd = va_arg(list, int);
780         buf = va_arg(list, void*);
781         len = va_arg(list, long);
782         return (uintptr)write(fd, buf, len, nil);
783 }
784
785 uintptr
786 syspwrite(va_list list)
787 {
788         int fd;
789         void *buf;
790         long len;
791         vlong off, *offp;
792
793         fd = va_arg(list, int);
794         buf = va_arg(list, void*);
795         len = va_arg(list, long);
796         off = va_arg(list, vlong);
797         if(off != ~0ULL)
798                 offp = &off;
799         else
800                 offp = nil;
801         return (uintptr)write(fd, buf, len, offp);
802 }
803
804 static vlong
805 sseek(int fd, vlong o, int type)
806 {
807         Chan *c;
808         uchar buf[sizeof(Dir)+100];
809         Dir dir;
810         int n;
811         vlong off;
812
813         c = fdtochan(fd, -1, 1, 1);
814         if(waserror()){
815                 cclose(c);
816                 nexterror();
817         }
818         if(devtab[c->type]->dc == L'|')
819                 error(Eisstream);
820
821         off = 0;
822         switch(type){
823         case 0:
824                 off = o;
825                 if((c->qid.type & QTDIR) && off != 0)
826                         error(Eisdir);
827                 if(off < 0)
828                         error(Enegoff);
829                 c->offset = off;
830                 break;
831
832         case 1:
833                 if(c->qid.type & QTDIR)
834                         error(Eisdir);
835                 lock(c);        /* lock for read/write update */
836                 off = o + c->offset;
837                 if(off < 0){
838                         unlock(c);
839                         error(Enegoff);
840                 }
841                 c->offset = off;
842                 unlock(c);
843                 break;
844
845         case 2:
846                 if(c->qid.type & QTDIR)
847                         error(Eisdir);
848                 n = devtab[c->type]->stat(c, buf, sizeof buf);
849                 if(convM2D(buf, n, &dir, nil) == 0)
850                         error("internal error: stat error in seek");
851                 off = dir.length + o;
852                 if(off < 0)
853                         error(Enegoff);
854                 c->offset = off;
855                 break;
856
857         default:
858                 error(Ebadarg);
859         }
860         c->uri = 0;
861         c->dri = 0;
862         cclose(c);
863         poperror();
864         return off;
865 }
866
867 uintptr
868 sysseek(va_list list)
869 {
870         int fd, t;
871         vlong n, *v;
872
873         v = va_arg(list, vlong*);
874         evenaddr((uintptr)v);
875         validaddr((uintptr)v, sizeof(vlong), 1);
876
877         fd = va_arg(list, int);
878         n = va_arg(list, vlong);
879         t = va_arg(list, int);
880
881         *v = sseek(fd, n, t);
882
883         return 0;
884 }
885
886 uintptr
887 sysoseek(va_list list)
888 {
889         int fd, t;
890         long n;
891
892         fd = va_arg(list, int);
893         n = va_arg(list, long);
894         t = va_arg(list, int);
895         return (uintptr)sseek(fd, n, t);
896 }
897
898 void
899 validstat(uchar *s, int n)
900 {
901         int m;
902         char buf[64];
903
904         if(statcheck(s, n) < 0)
905                 error(Ebadstat);
906         /* verify that name entry is acceptable */
907         s += STATFIXLEN - 4*BIT16SZ;    /* location of first string */
908         /*
909          * s now points at count for first string.
910          * if it's too long, let the server decide; this is
911          * only for his protection anyway. otherwise
912          * we'd have to allocate and waserror.
913          */
914         m = GBIT16(s);
915         s += BIT16SZ;
916         if(m+1 > sizeof buf)
917                 return;
918         memmove(buf, s, m);
919         buf[m] = '\0';
920         /* name could be '/' */
921         if(strcmp(buf, "/") != 0)
922                 validname(buf, 0);
923 }
924
925 static char*
926 pathlast(Path *p)
927 {
928         char *s;
929
930         if(p == nil)
931                 return nil;
932         if(p->len == 0)
933                 return nil;
934         s = strrchr(p->s, '/');
935         if(s != nil)
936                 return s+1;
937         return p->s;
938 }
939
940 uintptr
941 sysfstat(va_list list)
942 {
943         Chan *c;
944         int fd;
945         uint l;
946         uchar *s;
947
948         fd = va_arg(list, int);
949         s = va_arg(list, uchar*);
950         l = va_arg(list, uint);
951         validaddr((uintptr)s, l, 1);
952
953         c = fdtochan(fd, -1, 0, 1);
954         if(waserror()) {
955                 cclose(c);
956                 nexterror();
957         }
958         l = devtab[c->type]->stat(c, s, l);
959         poperror();
960         cclose(c);
961         return l;
962 }
963
964 uintptr
965 sysstat(va_list list)
966 {
967         char *name;
968         Chan *c;
969         uint l, r;
970         uchar *s;
971
972         name = va_arg(list, char*);
973         s = va_arg(list, uchar*);
974         l = va_arg(list, uint);
975         validaddr((uintptr)s, l, 1);
976         validaddr((uintptr)name, 1, 0);
977         c = namec(name, Aaccess, 0, 0);
978         if(waserror()){
979                 cclose(c);
980                 nexterror();
981         }
982         r = devtab[c->type]->stat(c, s, l);
983         name = pathlast(c->path);
984         if(name != nil)
985                 r = dirsetname(name, strlen(name), s, r, l);
986
987         poperror();
988         cclose(c);
989         return r;
990 }
991
992 uintptr
993 syschdir(va_list list)
994 {
995         Chan *c;
996         char *name;
997
998         name = va_arg(list, char*);
999         validaddr((uintptr)name, 1, 0);
1000         c = namec(name, Atodir, 0, 0);
1001         cclose(up->dot);
1002         up->dot = c;
1003         return 0;
1004 }
1005
1006 long
1007 bindmount(int ismount, int fd, int afd, char* arg0, char* arg1, int flag, char* spec)
1008 {
1009         int ret;
1010         Chan *c0, *c1, *ac, *bc;
1011
1012         if((flag&~MMASK) || (flag&MORDER)==(MBEFORE|MAFTER))
1013                 error(Ebadarg);
1014
1015         if(ismount){
1016                 validaddr((uintptr)spec, 1, 0);
1017                 spec = validnamedup(spec, 1);
1018                 if(waserror()){
1019                         free(spec);
1020                         nexterror();
1021                 }
1022
1023                 if(up->pgrp->noattach)
1024                         error(Enoattach);
1025
1026                 ac = nil;
1027                 bc = fdtochan(fd, ORDWR, 0, 1);
1028                 if(waserror()) {
1029                         if(ac != nil)
1030                                 cclose(ac);
1031                         cclose(bc);
1032                         nexterror();
1033                 }
1034
1035                 if(afd >= 0)
1036                         ac = fdtochan(afd, ORDWR, 0, 1);
1037
1038                 c0 = mntattach(bc, ac, spec, flag&MCACHE);
1039                 poperror();     /* ac bc */
1040                 if(ac != nil)
1041                         cclose(ac);
1042                 cclose(bc);
1043         }else{
1044                 spec = nil;
1045                 validaddr((uintptr)arg0, 1, 0);
1046                 c0 = namec(arg0, Abind, 0, 0);
1047         }
1048
1049         if(waserror()){
1050                 cclose(c0);
1051                 nexterror();
1052         }
1053
1054         validaddr((uintptr)arg1, 1, 0);
1055         c1 = namec(arg1, Amount, 0, 0);
1056         if(waserror()){
1057                 cclose(c1);
1058                 nexterror();
1059         }
1060
1061         ret = cmount(&c0, c1, flag, spec);
1062
1063         poperror();
1064         cclose(c1);
1065         poperror();
1066         cclose(c0);
1067         if(ismount){
1068                 fdclose(fd, 0);
1069                 poperror();
1070                 free(spec);
1071         }
1072         return ret;
1073 }
1074
1075 uintptr
1076 sysbind(va_list list)
1077 {
1078         char *arg0, *arg1;
1079         int flag;
1080
1081         arg0 = va_arg(list, char*);
1082         arg1 = va_arg(list, char*);
1083         flag = va_arg(list, int);
1084         return (uintptr)bindmount(0, -1, -1, arg0, arg1, flag, nil);
1085 }
1086
1087 uintptr
1088 sysmount(va_list list)
1089 {
1090         char *arg1, *spec;
1091         int flag;
1092         int fd, afd;
1093
1094         fd = va_arg(list, int);
1095         afd = va_arg(list, int);
1096         arg1 = va_arg(list, char*);
1097         flag = va_arg(list, int);
1098         spec = va_arg(list, char*);
1099         return (uintptr)bindmount(1, fd, afd, nil, arg1, flag, spec);
1100 }
1101
1102 uintptr
1103 sys_mount(va_list list)
1104 {
1105         char *arg1, *spec;
1106         int flag;
1107         int fd;
1108
1109         fd = va_arg(list, int);
1110         arg1 = va_arg(list, char*);
1111         flag = va_arg(list, int);
1112         spec = va_arg(list, char*);
1113         return (uintptr)bindmount(1, fd, -1, nil, arg1, flag, spec);
1114 }
1115
1116 uintptr
1117 sysunmount(va_list list)
1118 {
1119         Chan *cmount, *cmounted;
1120         char *name, *old;
1121
1122         name = va_arg(list, char*);
1123         old = va_arg(list, char*);
1124
1125         cmounted = nil;
1126         validaddr((uintptr)old, 1, 0);
1127         cmount = namec(old, Amount, 0, 0);
1128         if(waserror()) {
1129                 cclose(cmount);
1130                 if(cmounted != nil)
1131                         cclose(cmounted);
1132                 nexterror();
1133         }
1134         if(name != nil) {
1135                 /*
1136                  * This has to be namec(..., Aopen, ...) because
1137                  * if arg[0] is something like /srv/cs or /fd/0,
1138                  * opening it is the only way to get at the real
1139                  * Chan underneath.
1140                  */
1141                 validaddr((uintptr)name, 1, 0);
1142                 cmounted = namec(name, Aopen, OREAD, 0);
1143         }
1144         cunmount(cmount, cmounted);
1145         poperror();
1146         cclose(cmount);
1147         if(cmounted != nil)
1148                 cclose(cmounted);
1149         return 0;
1150 }
1151
1152 uintptr
1153 syscreate(va_list list)
1154 {
1155         int fd, mode, perm;
1156         char *name;
1157         Chan *c;
1158
1159         name = va_arg(list, char*);
1160         mode = va_arg(list, int);
1161         perm = va_arg(list, int);
1162         openmode(mode&~OEXCL);  /* error check only; OEXCL okay here */
1163         validaddr((uintptr)name, 1, 0);
1164         c = namec(name, Acreate, mode, perm);
1165         if(waserror()) {
1166                 cclose(c);
1167                 nexterror();
1168         }
1169         fd = newfd(c);
1170         if(fd < 0)
1171                 error(Enofd);
1172         poperror();
1173         return (uintptr)fd;
1174 }
1175
1176 uintptr
1177 sysremove(va_list list)
1178 {
1179         char *name;
1180         Chan *c;
1181
1182         name = va_arg(list, char*);
1183         validaddr((uintptr)name, 1, 0);
1184         c = namec(name, Aremove, 0, 0);
1185         /*
1186          * Removing mount points is disallowed to avoid surprises
1187          * (which should be removed: the mount point or the mounted Chan?).
1188          */
1189         if(c->ismtpt){
1190                 cclose(c);
1191                 error(Eismtpt);
1192         }
1193         if(waserror()){
1194                 c->type = 0;    /* see below */
1195                 cclose(c);
1196                 nexterror();
1197         }
1198         devtab[c->type]->remove(c);
1199         /*
1200          * Remove clunks the fid, but we need to recover the Chan
1201          * so fake it up.  rootclose() is known to be a nop.
1202          */
1203         c->type = 0;
1204         poperror();
1205         cclose(c);
1206         return 0;
1207 }
1208
1209 static long
1210 wstat(Chan *c, uchar *d, int nd)
1211 {
1212         long l;
1213         int namelen;
1214
1215         if(waserror()){
1216                 cclose(c);
1217                 nexterror();
1218         }
1219         if(c->ismtpt){
1220                 /*
1221                  * Renaming mount points is disallowed to avoid surprises
1222                  * (which should be renamed? the mount point or the mounted Chan?).
1223                  */
1224                 dirname(d, &namelen);
1225                 if(namelen)
1226                         nameerror(chanpath(c), Eismtpt);
1227         }
1228         l = devtab[c->type]->wstat(c, d, nd);
1229         poperror();
1230         cclose(c);
1231         return l;
1232 }
1233
1234 uintptr
1235 syswstat(va_list list)
1236 {
1237         char *name;
1238         uchar *s;
1239         Chan *c;
1240         uint l;
1241
1242         name = va_arg(list, char*);
1243         s = va_arg(list, uchar*);
1244         l = va_arg(list, uint);
1245         validaddr((uintptr)s, l, 0);
1246         validstat(s, l);
1247         validaddr((uintptr)name, 1, 0);
1248         c = namec(name, Aaccess, 0, 0);
1249         return (uintptr)wstat(c, s, l);
1250 }
1251
1252 uintptr
1253 sysfwstat(va_list list)
1254 {
1255         uchar *s;
1256         Chan *c;
1257         uint l;
1258         int fd;
1259
1260         fd = va_arg(list, int);
1261         s = va_arg(list, uchar*);
1262         l = va_arg(list, uint);
1263         validaddr((uintptr)s, l, 0);
1264         validstat(s, l);
1265         c = fdtochan(fd, -1, 1, 1);
1266         return (uintptr)wstat(c, s, l);
1267 }
1268
1269 static void
1270 packoldstat(uchar *buf, Dir *d)
1271 {
1272         uchar *p;
1273         ulong q;
1274
1275         /* lay down old stat buffer - grotty code but it's temporary */
1276         p = buf;
1277         strncpy((char*)p, d->name, 28);
1278         p += 28;
1279         strncpy((char*)p, d->uid, 28);
1280         p += 28;
1281         strncpy((char*)p, d->gid, 28);
1282         p += 28;
1283         q = (ulong)d->qid.path & ~DMDIR;        /* make sure doesn't accidentally look like directory */
1284         if(d->qid.type & QTDIR) /* this is the real test of a new directory */
1285                 q |= DMDIR;
1286         PBIT32(p, q);
1287         p += BIT32SZ;
1288         PBIT32(p, d->qid.vers);
1289         p += BIT32SZ;
1290         PBIT32(p, d->mode);
1291         p += BIT32SZ;
1292         PBIT32(p, d->atime);
1293         p += BIT32SZ;
1294         PBIT32(p, d->mtime);
1295         p += BIT32SZ;
1296         PBIT64(p, d->length);
1297         p += BIT64SZ;
1298         PBIT16(p, d->type);
1299         p += BIT16SZ;
1300         PBIT16(p, d->dev);
1301 }
1302
1303 uintptr
1304 sys_stat(va_list list)
1305 {
1306         static char old[] = "old stat system call - recompile";
1307         Chan *c;
1308         uint l;
1309         uchar *s, buf[128];     /* old DIRLEN plus a little should be plenty */
1310         char strs[128], *name;
1311         Dir d;
1312
1313         name = va_arg(list, char*);
1314         s = va_arg(list, uchar*);
1315         validaddr((uintptr)s, 116, 1);
1316         validaddr((uintptr)name, 1, 0);
1317         c = namec(name, Aaccess, 0, 0);
1318         if(waserror()){
1319                 cclose(c);
1320                 nexterror();
1321         }
1322         l = devtab[c->type]->stat(c, buf, sizeof buf);
1323         /* buf contains a new stat buf; convert to old. yuck. */
1324         if(l <= BIT16SZ)        /* buffer too small; time to face reality */
1325                 error(old);
1326         name = pathlast(c->path);
1327         if(name != nil)
1328                 l = dirsetname(name, strlen(name), buf, l, sizeof buf);
1329         l = convM2D(buf, l, &d, strs);
1330         if(l == 0)
1331                 error(old);
1332         packoldstat(s, &d);
1333         
1334         poperror();
1335         cclose(c);
1336         return 0;
1337 }
1338
1339 uintptr
1340 sys_fstat(va_list list)
1341 {
1342         static char old[] = "old fstat system call - recompile";
1343         Chan *c;
1344         char *name;
1345         uint l;
1346         uchar *s, buf[128];     /* old DIRLEN plus a little should be plenty */
1347         char strs[128];
1348         Dir d;
1349         int fd;
1350
1351         fd = va_arg(list, int);
1352         s = va_arg(list, uchar*);
1353         validaddr((uintptr)s, 116, 1);
1354         c = fdtochan(fd, -1, 0, 1);
1355         if(waserror()){
1356                 cclose(c);
1357                 nexterror();
1358         }
1359         l = devtab[c->type]->stat(c, buf, sizeof buf);
1360         /* buf contains a new stat buf; convert to old. yuck. */
1361         if(l <= BIT16SZ)        /* buffer too small; time to face reality */
1362                 error(old);
1363         name = pathlast(c->path);
1364         if(name != nil)
1365                 l = dirsetname(name, strlen(name), buf, l, sizeof buf);
1366         l = convM2D(buf, l, &d, strs);
1367         if(l == 0)
1368                 error(old);
1369         packoldstat(s, &d);
1370         
1371         poperror();
1372         cclose(c);
1373         return 0;
1374 }
1375
1376 uintptr
1377 sys_wstat(va_list)
1378 {
1379         error("old wstat system call - recompile");
1380         return (uintptr)-1;
1381 }
1382
1383 uintptr
1384 sys_fwstat(va_list)
1385 {
1386         error("old fwstat system call - recompile");
1387         return (uintptr)-1;
1388 }