]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/sysfile.c
merge
[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 == 0)
44                 goto Exhausted;
45         oldfd = f->fd;
46         memmove(newfd, oldfd, f->nfd*sizeof(Chan*));
47         f->fd = newfd;
48         free(oldfd);
49         f->nfd += DELTAFD;
50         if(fd > f->maxfd){
51                 if(fd/100 > f->maxfd/100)
52                         f->exceed = (fd/100)*100;
53                 f->maxfd = fd;
54         }
55         return 1;
56 }
57
58 /*
59  *  this assumes that the fgrp is locked
60  */
61 int
62 findfreefd(Fgrp *f, int start)
63 {
64         int fd;
65
66         for(fd=start; fd<f->nfd; fd++)
67                 if(f->fd[fd] == 0)
68                         break;
69         if(fd >= f->nfd && growfd(f, fd) < 0)
70                 return -1;
71         return fd;
72 }
73
74 int
75 newfd(Chan *c)
76 {
77         int fd;
78         Fgrp *f;
79
80         f = up->fgrp;
81         lock(f);
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 = 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         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         int fd[2], *ufd;
193         Chan *c[2];
194         Dev *d;
195         static char *datastr[] = {"data", "data1"};
196
197         ufd = va_arg(list, int*);
198         validaddr((uintptr)ufd, sizeof(fd), 1);
199         evenaddr((uintptr)ufd);
200         
201         ufd[0] = ufd[1] = fd[0] = fd[1] = -1;
202         d = devtab[devno('|', 0)];
203         c[0] = namec("#|", Atodir, 0, 0);
204         c[1] = 0;
205         if(waserror()){
206                 cclose(c[0]);
207                 if(c[1])
208                         cclose(c[1]);
209                 nexterror();
210         }
211         c[1] = cclone(c[0]);
212         if(walk(&c[0], datastr+0, 1, 1, nil) < 0)
213                 error(Egreg);
214         if(walk(&c[1], datastr+1, 1, 1, nil) < 0)
215                 error(Egreg);
216         c[0] = d->open(c[0], ORDWR);
217         c[1] = d->open(c[1], ORDWR);
218         if(newfd2(fd, c) < 0)
219                 error(Enofd);
220         ufd[0] = fd[0];
221         ufd[1] = fd[1];
222         poperror();
223         return 0;
224 }
225
226 uintptr
227 sysdup(va_list list)
228 {
229         int fd;
230         Chan *c, *oc;
231         Fgrp *f = up->fgrp;
232
233         fd = va_arg(list, int);
234
235         /*
236          * Close after dup'ing, so date > #d/1 works
237          */
238         c = fdtochan(fd, -1, 0, 1);
239         fd = va_arg(list, int);
240         if(fd != -1){
241                 lock(f);
242                 if(fd<0 || growfd(f, fd)<0) {
243                         unlockfgrp(f);
244                         cclose(c);
245                         error(Ebadfd);
246                 }
247                 if(fd > f->maxfd)
248                         f->maxfd = fd;
249
250                 oc = f->fd[fd];
251                 f->fd[fd] = c;
252                 unlockfgrp(f);
253                 if(oc)
254                         cclose(oc);
255         }else{
256                 if(waserror()) {
257                         cclose(c);
258                         nexterror();
259                 }
260                 fd = newfd(c);
261                 if(fd < 0)
262                         error(Enofd);
263                 poperror();
264         }
265         return (uintptr)fd;
266 }
267
268 uintptr
269 sysopen(va_list list)
270 {
271         int fd;
272         Chan *c;
273         char *name;
274         ulong mode;
275
276         name = va_arg(list, char*);
277         mode = va_arg(list, ulong);
278         openmode(mode); /* error check only */
279         validaddr((uintptr)name, 1, 0);
280         c = namec(name, Aopen, mode, 0);
281         if(waserror()){
282                 cclose(c);
283                 nexterror();
284         }
285         fd = newfd(c);
286         if(fd < 0)
287                 error(Enofd);
288         poperror();
289         return (uintptr)fd;
290 }
291
292 void
293 fdclose(int fd, int flag)
294 {
295         Chan *c;
296         Fgrp *f = up->fgrp;
297
298         lock(f);
299         c = f->fd[fd];
300         if(c == nil || (flag != 0 && (c->flag&flag) == 0)){
301                 unlock(f);
302                 return;
303         }
304         f->fd[fd] = nil;
305         if(fd == f->maxfd){
306                 while(fd > 0 && f->fd[fd] == nil)
307                         f->maxfd = --fd;
308         }
309         unlock(f);
310         cclose(c);
311 }
312
313 uintptr
314 sysclose(va_list list)
315 {
316         int fd;
317
318         fd = va_arg(list, int);
319         fdtochan(fd, -1, 0, 0);
320         fdclose(fd, 0);
321         return 0;
322 }
323
324 long
325 unionread(Chan *c, void *va, long n)
326 {
327         int i;
328         long nr;
329         Mhead *m;
330         Mount *mount;
331
332         eqlock(&c->umqlock);
333         m = c->umh;
334         rlock(&m->lock);
335         mount = m->mount;
336         /* bring mount in sync with c->uri and c->umc */
337         for(i = 0; mount != nil && i < c->uri; i++)
338                 mount = mount->next;
339
340         nr = 0;
341         while(mount != nil){
342                 /* Error causes component of union to be skipped */
343                 if(mount->to && !waserror()){
344                         if(c->umc == nil){
345                                 c->umc = cclone(mount->to);
346                                 c->umc = devtab[c->umc->type]->open(c->umc, OREAD);
347                         }
348         
349                         nr = devtab[c->umc->type]->read(c->umc, va, n, c->umc->offset);
350                         c->umc->offset += nr;
351                         poperror();
352                 }
353                 if(nr > 0)
354                         break;
355
356                 /* Advance to next element */
357                 c->uri++;
358                 if(c->umc){
359                         cclose(c->umc);
360                         c->umc = nil;
361                 }
362                 mount = mount->next;
363         }
364         runlock(&m->lock);
365         qunlock(&c->umqlock);
366         return nr;
367 }
368
369 static void
370 unionrewind(Chan *c)
371 {
372         eqlock(&c->umqlock);
373         c->uri = 0;
374         if(c->umc){
375                 cclose(c->umc);
376                 c->umc = nil;
377         }
378         qunlock(&c->umqlock);
379 }
380
381 static int
382 dirfixed(uchar *p, uchar *e, Dir *d)
383 {
384         int len;
385
386         len = GBIT16(p)+BIT16SZ;
387         if(p + len > e)
388                 return -1;
389
390         p += BIT16SZ;   /* ignore size */
391         d->type = devno(GBIT16(p), 1);
392         p += BIT16SZ;
393         d->dev = GBIT32(p);
394         p += BIT32SZ;
395         d->qid.type = GBIT8(p);
396         p += BIT8SZ;
397         d->qid.vers = GBIT32(p);
398         p += BIT32SZ;
399         d->qid.path = GBIT64(p);
400         p += BIT64SZ;
401         d->mode = GBIT32(p);
402         p += BIT32SZ;
403         d->atime = GBIT32(p);
404         p += BIT32SZ;
405         d->mtime = GBIT32(p);
406         p += BIT32SZ;
407         d->length = GBIT64(p);
408
409         return len;
410 }
411
412 static char*
413 dirname(uchar *p, int *n)
414 {
415         p += BIT16SZ+BIT16SZ+BIT32SZ+BIT8SZ+BIT32SZ+BIT64SZ
416                 + BIT32SZ+BIT32SZ+BIT32SZ+BIT64SZ;
417         *n = GBIT16(p);
418         return (char*)p+BIT16SZ;
419 }
420
421 static long
422 dirsetname(char *name, int len, uchar *p, long n, long maxn)
423 {
424         char *oname;
425         int olen;
426         long nn;
427
428         if(n == BIT16SZ)
429                 return BIT16SZ;
430
431         oname = dirname(p, &olen);
432
433         nn = n+len-olen;
434         PBIT16(p, nn-BIT16SZ);
435         if(nn > maxn)
436                 return BIT16SZ;
437
438         if(len != olen)
439                 memmove(oname+len, oname+olen, p+n-(uchar*)(oname+olen));
440         PBIT16((uchar*)(oname-2), len);
441         memmove(oname, name, len);
442         return nn;
443 }
444
445 /*
446  * Mountfix might have caused the fixed results of the directory read
447  * to overflow the buffer.  Catch the overflow in c->dirrock.
448  */
449 static void
450 mountrock(Chan *c, uchar *p, uchar **pe)
451 {
452         uchar *e, *r;
453         int len, n;
454
455         e = *pe;
456
457         /* find last directory entry */
458         for(;;){
459                 len = BIT16SZ+GBIT16(p);
460                 if(p+len >= e)
461                         break;
462                 p += len;
463         }
464
465         /* save it away */
466         qlock(&c->rockqlock);
467         if(c->nrock+len > c->mrock){
468                 n = ROUND(c->nrock+len, 1024);
469                 r = smalloc(n);
470                 memmove(r, c->dirrock, c->nrock);
471                 free(c->dirrock);
472                 c->dirrock = r;
473                 c->mrock = n;
474         }
475         memmove(c->dirrock+c->nrock, p, len);
476         c->nrock += len;
477         qunlock(&c->rockqlock);
478
479         /* drop it */
480         *pe = p;
481 }
482
483 /*
484  * Satisfy a directory read with the results saved in c->dirrock.
485  */
486 static int
487 mountrockread(Chan *c, uchar *op, long n, long *nn)
488 {
489         long dirlen;
490         uchar *rp, *erp, *ep, *p;
491
492         /* common case */
493         if(c->nrock == 0)
494                 return 0;
495
496         /* copy out what we can */
497         qlock(&c->rockqlock);
498         rp = c->dirrock;
499         erp = rp+c->nrock;
500         p = op;
501         ep = p+n;
502         while(rp+BIT16SZ <= erp){
503                 dirlen = BIT16SZ+GBIT16(rp);
504                 if(p+dirlen > ep)
505                         break;
506                 memmove(p, rp, dirlen);
507                 p += dirlen;
508                 rp += dirlen;
509         }
510
511         if(p == op){
512                 qunlock(&c->rockqlock);
513                 return 0;
514         }
515
516         /* shift the rest */
517         if(rp != erp)
518                 memmove(c->dirrock, rp, erp-rp);
519         c->nrock = erp - rp;
520
521         *nn = p - op;
522         qunlock(&c->rockqlock);
523         return 1;
524 }
525
526 static void
527 mountrewind(Chan *c)
528 {
529         c->nrock = 0;
530 }
531
532 /*
533  * Rewrite the results of a directory read to reflect current 
534  * name space bindings and mounts.  Specifically, replace
535  * directory entries for bind and mount points with the results
536  * of statting what is mounted there.  Except leave the old names.
537  */
538 static long
539 mountfix(Chan *c, uchar *op, long n, long maxn)
540 {
541         char *name;
542         int nbuf, nname;
543         Chan *nc;
544         Mhead *mh;
545         Mount *m;
546         uchar *p;
547         int dirlen, rest;
548         long l;
549         uchar *buf, *e;
550         Dir d;
551
552         p = op;
553         buf = nil;
554         nbuf = 0;
555         for(e=&p[n]; p+BIT16SZ<e; p+=dirlen){
556                 dirlen = dirfixed(p, e, &d);
557                 if(dirlen < 0)
558                         break;
559                 nc = nil;
560                 mh = nil;
561                 if(findmount(&nc, &mh, d.type, d.dev, d.qid)){
562                         /*
563                          * If it's a union directory and the original is
564                          * in the union, don't rewrite anything.
565                          */
566                         for(m=mh->mount; m; m=m->next)
567                                 if(eqchantdqid(m->to, d.type, d.dev, d.qid, 1))
568                                         goto Norewrite;
569
570                         name = dirname(p, &nname);
571                         /*
572                          * Do the stat but fix the name.  If it fails, leave old entry.
573                          * BUG: If it fails because there isn't room for the entry,
574                          * what can we do?  Nothing, really.  Might as well skip it.
575                          */
576                         if(buf == nil){
577                                 buf = smalloc(4096);
578                                 nbuf = 4096;
579                         }
580                         if(waserror())
581                                 goto Norewrite;
582                         l = devtab[nc->type]->stat(nc, buf, nbuf);
583                         l = dirsetname(name, nname, buf, l, nbuf);
584                         if(l == BIT16SZ)
585                                 error("dirsetname");
586                         poperror();
587
588                         /*
589                          * Shift data in buffer to accomodate new entry,
590                          * possibly overflowing into rock.
591                          */
592                         rest = e - (p+dirlen);
593                         if(l > dirlen){
594                                 while(p+l+rest > op+maxn){
595                                         mountrock(c, p, &e);
596                                         if(e == p){
597                                                 dirlen = 0;
598                                                 goto Norewrite;
599                                         }
600                                         rest = e - (p+dirlen);
601                                 }
602                         }
603                         if(l != dirlen){
604                                 memmove(p+l, p+dirlen, rest);
605                                 dirlen = l;
606                                 e = p+dirlen+rest;
607                         }
608
609                         /*
610                          * Rewrite directory entry.
611                          */
612                         memmove(p, buf, l);
613
614                     Norewrite:
615                         cclose(nc);
616                         putmhead(mh);
617                 }
618         }
619         if(buf)
620                 free(buf);
621
622         if(p != e)
623                 error("oops in rockfix");
624
625         return e-op;
626 }
627
628 static long
629 read(int fd, uchar *p, long n, vlong *offp)
630 {
631         long nn, nnn;
632         Chan *c;
633         vlong off;
634
635         validaddr((uintptr)p, n, 1);
636         c = fdtochan(fd, OREAD, 1, 1);
637
638         if(waserror()){
639                 cclose(c);
640                 nexterror();
641         }
642
643         /*
644          * The offset is passed through on directories, normally.
645          * Sysseek complains, but pread is used by servers like exportfs,
646          * that shouldn't need to worry about this issue.
647          *
648          * Notice that c->devoffset is the offset that c's dev is seeing.
649          * The number of bytes read on this fd (c->offset) may be different
650          * due to rewritings in rockfix.
651          */
652         if(offp == nil) /* use and maintain channel's offset */
653                 off = c->offset;
654         else
655                 off = *offp;
656         if(off < 0)
657                 error(Enegoff);
658
659         if(off == 0){   /* rewind to the beginning of the directory */
660                 if(offp == nil){
661                         c->offset = 0;
662                         c->devoffset = 0;
663                 }
664                 mountrewind(c);
665                 unionrewind(c);
666         }
667
668         if(c->qid.type & QTDIR){
669                 if(mountrockread(c, p, n, &nn)){
670                         /* do nothing: mountrockread filled buffer */
671                 }else if(c->umh)
672                         nn = unionread(c, p, n);
673                 else{
674                         if(off != c->offset)
675                                 error(Edirseek);
676                         nn = devtab[c->type]->read(c, p, n, c->devoffset);
677                 }
678                 nnn = mountfix(c, p, nn, n);
679         }else
680                 nnn = nn = devtab[c->type]->read(c, p, n, off);
681
682         lock(c);
683         c->devoffset += nn;
684         c->offset += nnn;
685         unlock(c);
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)
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)
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, ulong flag, char* spec)
1008 {
1009         int ret;
1010         Chan *c0, *c1, *ac, *bc;
1011         struct{
1012                 Chan    *chan;
1013                 Chan    *authchan;
1014                 char    *spec;
1015                 int     flags;
1016         }bogus;
1017
1018         if((flag&~MMASK) || (flag&MORDER)==(MBEFORE|MAFTER))
1019                 error(Ebadarg);
1020
1021         if(ismount){
1022                 validaddr((uintptr)spec, 1, 0);
1023                 spec = validnamedup(spec, 1);
1024                 if(waserror()){
1025                         free(spec);
1026                         nexterror();
1027                 }
1028
1029                 if(up->pgrp->noattach)
1030                         error(Enoattach);
1031
1032                 ac = nil;
1033                 bc = fdtochan(fd, ORDWR, 0, 1);
1034                 if(waserror()) {
1035                         if(ac)
1036                                 cclose(ac);
1037                         cclose(bc);
1038                         nexterror();
1039                 }
1040
1041                 if(afd >= 0)
1042                         ac = fdtochan(afd, ORDWR, 0, 1);
1043
1044                 bogus.flags = flag & MCACHE;
1045                 bogus.chan = bc;
1046                 bogus.authchan = ac;
1047                 bogus.spec = spec;
1048                 ret = devno('M', 0);
1049                 c0 = devtab[ret]->attach((char*)&bogus);
1050                 poperror();     /* ac bc */
1051                 if(ac)
1052                         cclose(ac);
1053                 cclose(bc);
1054         }else{
1055                 spec = 0;
1056                 validaddr((uintptr)arg0, 1, 0);
1057                 c0 = namec(arg0, Abind, 0, 0);
1058         }
1059
1060         if(waserror()){
1061                 cclose(c0);
1062                 nexterror();
1063         }
1064
1065         validaddr((uintptr)arg1, 1, 0);
1066         c1 = namec(arg1, Amount, 0, 0);
1067         if(waserror()){
1068                 cclose(c1);
1069                 nexterror();
1070         }
1071
1072         ret = cmount(&c0, c1, flag, spec);
1073
1074         poperror();
1075         cclose(c1);
1076         poperror();
1077         cclose(c0);
1078         if(ismount){
1079                 fdclose(fd, 0);
1080                 poperror();
1081                 free(spec);
1082         }
1083         return ret;
1084 }
1085
1086 uintptr
1087 sysbind(va_list list)
1088 {
1089         char *arg0, *arg1;
1090         ulong flag;
1091
1092         arg0 = va_arg(list, char*);
1093         arg1 = va_arg(list, char*);
1094         flag = va_arg(list, ulong);
1095         return (uintptr)bindmount(0, -1, -1, arg0, arg1, flag, nil);
1096 }
1097
1098 uintptr
1099 sysmount(va_list list)
1100 {
1101         char *arg1, *spec;
1102         ulong flag;
1103         int fd, afd;
1104
1105         fd = va_arg(list, int);
1106         afd = va_arg(list, int);
1107         arg1 = va_arg(list, char*);
1108         flag = va_arg(list, ulong);
1109         spec = va_arg(list, char*);
1110         return (uintptr)bindmount(1, fd, afd, nil, arg1, flag, spec);
1111 }
1112
1113 uintptr
1114 sys_mount(va_list list)
1115 {
1116         char *arg1, *spec;
1117         ulong flag;
1118         int fd;
1119
1120         fd = va_arg(list, int);
1121         arg1 = va_arg(list, char*);
1122         flag = va_arg(list, ulong);
1123         spec = va_arg(list, char*);
1124         return (uintptr)bindmount(1, fd, -1, nil, arg1, flag, spec);
1125 }
1126
1127 uintptr
1128 sysunmount(va_list list)
1129 {
1130         Chan *cmount, *cmounted;
1131         char *name, *old;
1132
1133         name = va_arg(list, char*);
1134         old = va_arg(list, char*);
1135
1136         cmounted = 0;
1137         validaddr((uintptr)old, 1, 0);
1138         cmount = namec(old, Amount, 0, 0);
1139         if(waserror()) {
1140                 cclose(cmount);
1141                 if(cmounted)
1142                         cclose(cmounted);
1143                 nexterror();
1144         }
1145
1146         if(name) {
1147                 /*
1148                  * This has to be namec(..., Aopen, ...) because
1149                  * if arg[0] is something like /srv/cs or /fd/0,
1150                  * opening it is the only way to get at the real
1151                  * Chan underneath.
1152                  */
1153                 validaddr((uintptr)name, 1, 0);
1154                 cmounted = namec(name, Aopen, OREAD, 0);
1155         }
1156         cunmount(cmount, cmounted);
1157         poperror();
1158         cclose(cmount);
1159         if(cmounted)
1160                 cclose(cmounted);
1161         return 0;
1162 }
1163
1164 uintptr
1165 syscreate(va_list list)
1166 {
1167         int fd, mode, perm;
1168         char *name;
1169         Chan *c;
1170
1171         name = va_arg(list, char*);
1172         mode = va_arg(list, int);
1173         perm = va_arg(list, int);
1174         openmode(mode&~OEXCL);  /* error check only; OEXCL okay here */
1175         validaddr((uintptr)name, 1, 0);
1176         c = namec(name, Acreate, mode, perm);
1177         if(waserror()) {
1178                 cclose(c);
1179                 nexterror();
1180         }
1181         fd = newfd(c);
1182         if(fd < 0)
1183                 error(Enofd);
1184         poperror();
1185         return (uintptr)fd;
1186 }
1187
1188 uintptr
1189 sysremove(va_list list)
1190 {
1191         char *name;
1192         Chan *c;
1193
1194         name = va_arg(list, char*);
1195         validaddr((uintptr)name, 1, 0);
1196         c = namec(name, Aremove, 0, 0);
1197         /*
1198          * Removing mount points is disallowed to avoid surprises
1199          * (which should be removed: the mount point or the mounted Chan?).
1200          */
1201         if(c->ismtpt){
1202                 cclose(c);
1203                 error(Eismtpt);
1204         }
1205         if(waserror()){
1206                 c->type = 0;    /* see below */
1207                 cclose(c);
1208                 nexterror();
1209         }
1210         devtab[c->type]->remove(c);
1211         /*
1212          * Remove clunks the fid, but we need to recover the Chan
1213          * so fake it up.  rootclose() is known to be a nop.
1214          */
1215         c->type = 0;
1216         poperror();
1217         cclose(c);
1218         return 0;
1219 }
1220
1221 static long
1222 wstat(Chan *c, uchar *d, int nd)
1223 {
1224         long l;
1225         int namelen;
1226
1227         if(waserror()){
1228                 cclose(c);
1229                 nexterror();
1230         }
1231         if(c->ismtpt){
1232                 /*
1233                  * Renaming mount points is disallowed to avoid surprises
1234                  * (which should be renamed? the mount point or the mounted Chan?).
1235                  */
1236                 dirname(d, &namelen);
1237                 if(namelen)
1238                         nameerror(chanpath(c), Eismtpt);
1239         }
1240         l = devtab[c->type]->wstat(c, d, nd);
1241         poperror();
1242         cclose(c);
1243         return l;
1244 }
1245
1246 uintptr
1247 syswstat(va_list list)
1248 {
1249         char *name;
1250         uchar *s;
1251         Chan *c;
1252         uint l;
1253
1254         name = va_arg(list, char*);
1255         s = va_arg(list, uchar*);
1256         l = va_arg(list, uint);
1257         validaddr((uintptr)s, l, 0);
1258         validstat(s, l);
1259         validaddr((uintptr)name, 1, 0);
1260         c = namec(name, Aaccess, 0, 0);
1261         return (uintptr)wstat(c, s, l);
1262 }
1263
1264 uintptr
1265 sysfwstat(va_list list)
1266 {
1267         uchar *s;
1268         Chan *c;
1269         uint l;
1270         int fd;
1271
1272         fd = va_arg(list, int);
1273         s = va_arg(list, uchar*);
1274         l = va_arg(list, uint);
1275         validaddr((uintptr)s, l, 0);
1276         validstat(s, l);
1277         c = fdtochan(fd, -1, 1, 1);
1278         return (uintptr)wstat(c, s, l);
1279 }
1280
1281 static void
1282 packoldstat(uchar *buf, Dir *d)
1283 {
1284         uchar *p;
1285         ulong q;
1286
1287         /* lay down old stat buffer - grotty code but it's temporary */
1288         p = buf;
1289         strncpy((char*)p, d->name, 28);
1290         p += 28;
1291         strncpy((char*)p, d->uid, 28);
1292         p += 28;
1293         strncpy((char*)p, d->gid, 28);
1294         p += 28;
1295         q = d->qid.path & ~DMDIR;       /* make sure doesn't accidentally look like directory */
1296         if(d->qid.type & QTDIR) /* this is the real test of a new directory */
1297                 q |= DMDIR;
1298         PBIT32(p, q);
1299         p += BIT32SZ;
1300         PBIT32(p, d->qid.vers);
1301         p += BIT32SZ;
1302         PBIT32(p, d->mode);
1303         p += BIT32SZ;
1304         PBIT32(p, d->atime);
1305         p += BIT32SZ;
1306         PBIT32(p, d->mtime);
1307         p += BIT32SZ;
1308         PBIT64(p, d->length);
1309         p += BIT64SZ;
1310         PBIT16(p, d->type);
1311         p += BIT16SZ;
1312         PBIT16(p, d->dev);
1313 }
1314
1315 uintptr
1316 sys_stat(va_list list)
1317 {
1318         Chan *c;
1319         uint l;
1320         uchar *s, buf[128];     /* old DIRLEN plus a little should be plenty */
1321         char strs[128], *name;
1322         Dir d;
1323         char old[] = "old stat system call - recompile";
1324
1325         name = va_arg(list, char*);
1326         s = va_arg(list, uchar*);
1327         validaddr((uintptr)s, 116, 1);
1328         validaddr((uintptr)name, 1, 0);
1329         c = namec(name, Aaccess, 0, 0);
1330         if(waserror()){
1331                 cclose(c);
1332                 nexterror();
1333         }
1334         l = devtab[c->type]->stat(c, buf, sizeof buf);
1335         /* buf contains a new stat buf; convert to old. yuck. */
1336         if(l <= BIT16SZ)        /* buffer too small; time to face reality */
1337                 error(old);
1338         name = pathlast(c->path);
1339         if(name)
1340                 l = dirsetname(name, strlen(name), buf, l, sizeof buf);
1341         l = convM2D(buf, l, &d, strs);
1342         if(l == 0)
1343                 error(old);
1344         packoldstat(s, &d);
1345         
1346         poperror();
1347         cclose(c);
1348         return 0;
1349 }
1350
1351 uintptr
1352 sys_fstat(va_list list)
1353 {
1354         Chan *c;
1355         char *name;
1356         uint l;
1357         uchar *s, buf[128];     /* old DIRLEN plus a little should be plenty */
1358         char strs[128];
1359         Dir d;
1360         char old[] = "old fstat system call - recompile";
1361         int fd;
1362
1363         fd = va_arg(list, int);
1364         s = va_arg(list, uchar*);
1365         validaddr((uintptr)s, 116, 1);
1366         c = fdtochan(fd, -1, 0, 1);
1367         if(waserror()){
1368                 cclose(c);
1369                 nexterror();
1370         }
1371         l = devtab[c->type]->stat(c, buf, sizeof buf);
1372         /* buf contains a new stat buf; convert to old. yuck. */
1373         if(l <= BIT16SZ)        /* buffer too small; time to face reality */
1374                 error(old);
1375         name = pathlast(c->path);
1376         if(name)
1377                 l = dirsetname(name, strlen(name), buf, l, sizeof buf);
1378         l = convM2D(buf, l, &d, strs);
1379         if(l == 0)
1380                 error(old);
1381         packoldstat(s, &d);
1382         
1383         poperror();
1384         cclose(c);
1385         return 0;
1386 }
1387
1388 uintptr
1389 sys_wstat(va_list)
1390 {
1391         error("old wstat system call - recompile");
1392         return (uintptr)-1;
1393 }
1394
1395 uintptr
1396 sys_fwstat(va_list)
1397 {
1398         error("old fwstat system call - recompile");
1399         return (uintptr)-1;
1400 }