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