]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/nfs.c
devproc: can't wait for ourselfs to stop (thanks Shamar)
[plan9front.git] / sys / src / cmd / nfs.c
1 /* Copyright © 2003 Russ Cox, MIT; see /sys/src/libsunrpc/COPYING */
2 #include <u.h>
3 #include <libc.h>
4 #include <bio.h>
5 #include <fcall.h>
6 #include <thread.h>
7 #include <9p.h>
8 #include <sunrpc.h>
9 #include <nfs3.h>
10
11 SunClient *nfscli;
12 SunClient *mntcli;
13 char *defaultpath = "/";
14 Channel *fschan;
15 char *sys;
16 int verbose;
17 int readplus = 0;
18
19
20 typedef struct Auth Auth;
21 struct Auth
22 {
23         int ref;
24         uchar *data;
25         int ndata;
26 };
27
28 typedef struct FidAux FidAux;
29 struct FidAux
30 {
31         Nfs3Handle handle;
32
33         u64int cookie;  /* for continuing directory reads */
34         char *name;     /* botch: for remove and rename */
35         Nfs3Handle parent;      /* botch: for remove and rename */
36         char err[ERRMAX];       /* for walk1 */
37         Auth *auth;
38 };
39
40 /*
41  * various RPCs.  here is where we'd insert support for NFS v2
42  */
43
44 void
45 portCall(SunCall *c, PortCallType type)
46 {
47         c->rpc.prog = PortProgram;
48         c->rpc.vers = PortVersion;
49         c->rpc.proc = type>>1;
50         c->rpc.iscall = !(type&1);
51         c->type = type;
52 }
53
54 int
55 getport(SunClient *client, uint prog, uint vers, uint prot, uint *port)
56 {
57         PortTGetport tx;
58         PortRGetport rx;
59
60         memset(&tx, 0, sizeof tx);
61         portCall(&tx.call, PortCallTGetport);
62         tx.map.prog = prog;
63         tx.map.vers = vers;
64         tx.map.prot = prot;
65
66         memset(&rx, 0, sizeof rx);
67         portCall(&rx.call, PortCallRGetport);
68
69         if(sunClientRpc(client, 0, &tx.call, &rx.call, nil) < 0)
70                 return -1;
71         *port = rx.port;
72         return 0;
73 }
74
75 void
76 mountCall(Auth *a, SunCall *c, NfsMount3CallType type)
77 {
78         c->rpc.iscall = !(type&1);
79         c->rpc.proc = type>>1;
80         c->rpc.prog = NfsMount3Program;
81         c->rpc.vers = NfsMount3Version;
82         if(c->rpc.iscall && a){
83                 c->rpc.cred.flavor = SunAuthSys;
84                 c->rpc.cred.data = a->data;
85                 c->rpc.cred.ndata = a->ndata;
86         }
87         c->type = type;
88 }
89
90 int
91 mountNull(ulong tag)
92 {
93         NfsMount3TNull tx;
94         NfsMount3RNull rx;
95
96         memset(&tx, 0, sizeof tx);
97         mountCall(nil, &tx.call, NfsMount3CallTNull);
98
99         memset(&rx, 0, sizeof rx);
100         mountCall(nil, &rx.call, NfsMount3CallTNull);
101
102         return sunClientRpc(mntcli, tag, &tx.call, &rx.call, nil);
103 }
104
105 int
106 mountMnt(Auth *a, ulong tag, char *path, Nfs3Handle *h)
107 {
108         uchar *freeme;
109         NfsMount3TMnt tx;
110         NfsMount3RMnt rx;
111
112         memset(&tx, 0, sizeof tx);
113         mountCall(a, &tx.call, NfsMount3CallTMnt);
114         tx.path = path;
115
116         memset(&rx, 0, sizeof rx);
117         mountCall(a, &rx.call, NfsMount3CallRMnt);
118         if(sunClientRpc(mntcli, tag, &tx.call, &rx.call, &freeme) < 0)
119                 return -1;
120         if(rx.status != Nfs3Ok){
121                 nfs3Errstr(rx.status);
122                 return -1;
123         }
124 if(verbose)print("handle %.*H\n", rx.len, rx.handle);
125         if(rx.len >= Nfs3MaxHandleSize){
126                 free(freeme);
127                 werrstr("server-returned handle too long");
128                 return -1;
129         }
130         memmove(h->h, rx.handle, rx.len);
131         h->len = rx.len;
132         free(freeme);
133         return 0;
134 }
135
136 void
137 nfs3Call(Auth *a, SunCall *c, Nfs3CallType type)
138 {
139         c->rpc.iscall = !(type&1);
140         c->rpc.proc = type>>1;
141         c->rpc.prog = Nfs3Program;
142         c->rpc.vers = Nfs3Version;
143         if(c->rpc.iscall && a){
144                 c->rpc.cred.flavor = SunAuthSys;
145                 c->rpc.cred.data = a->data;
146                 c->rpc.cred.ndata = a->ndata;
147         }
148         c->type = type;
149 }
150
151 int
152 nfsNull(ulong tag)
153 {
154         Nfs3TNull tx;
155         Nfs3RNull rx;
156
157         memset(&tx, 0, sizeof tx);
158         nfs3Call(nil, &tx.call, Nfs3CallTNull);
159
160         memset(&rx, 0, sizeof rx);
161         nfs3Call(nil, &rx.call, Nfs3CallTNull);
162
163         return sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil);
164 }
165
166 int
167 nfsGetattr(Auth *a, ulong tag, Nfs3Handle *h, Nfs3Attr *attr)
168 {
169         Nfs3TGetattr tx;
170         Nfs3RGetattr rx;
171
172         memset(&tx, 0, sizeof tx);
173         nfs3Call(a, &tx.call, Nfs3CallTGetattr);
174         tx.handle = *h; 
175
176         memset(&rx, 0, sizeof rx);
177         nfs3Call(a, &rx.call, Nfs3CallRGetattr);
178
179         if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0)
180                 return -1;
181         if(rx.status != Nfs3Ok){
182                 nfs3Errstr(rx.status);
183                 return -1;
184         }
185
186         *attr = rx.attr;
187         return 0;
188 }
189
190 int
191 nfsAccess(Auth *a, ulong tag, Nfs3Handle *h, ulong want, ulong *got, u1int *have, Nfs3Attr *attr)
192 {
193         Nfs3TAccess tx;
194         Nfs3RAccess rx;
195
196         memset(&tx, 0, sizeof tx);
197         nfs3Call(a, &tx.call, Nfs3CallTAccess);
198         tx.handle = *h;
199         tx.access = want;
200
201         memset(&rx, 0, sizeof rx);
202         nfs3Call(a, &rx.call, Nfs3CallRAccess);
203
204         if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0)
205                 return -1;
206         if(rx.status != Nfs3Ok){
207                 nfs3Errstr(rx.status);
208                 return -1;
209         }
210
211         *got = rx.access;
212
213         *have = rx.haveAttr;
214         if(rx.haveAttr)
215                 *attr = rx.attr;
216         return 0;
217 }
218
219 int
220 nfsMkdir(Auth *a, ulong tag, Nfs3Handle *h, char *name, Nfs3Handle *nh, ulong mode, uint gid,
221         u1int *have, Nfs3Attr *attr)
222 {
223         Nfs3TMkdir tx;
224         Nfs3RMkdir rx;
225
226         memset(&tx, 0, sizeof tx);
227         nfs3Call(a, &tx.call, Nfs3CallTMkdir);
228         tx.handle = *h;
229         tx.name = name;
230         tx.attr.setMode = 1;
231         tx.attr.mode = mode;
232         tx.attr.setGid = 1;
233         tx.attr.gid = gid;
234
235         memset(&rx, 0, sizeof rx);
236         nfs3Call(a, &rx.call, Nfs3CallRMkdir);
237
238         if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0)
239                 return -1;
240         if(rx.status != Nfs3Ok){
241                 nfs3Errstr(rx.status);
242                 return -1;
243         }
244
245         if(!rx.haveHandle){
246                 werrstr("nfs mkdir did not return handle");
247                 return -1;
248         }
249         *nh = rx.handle;
250
251         *have = rx.haveAttr;
252         if(rx.haveAttr)
253                 *attr = rx.attr;
254         return 0;
255 }
256
257 int
258 nfsCreate(Auth *a, ulong tag, Nfs3Handle *h, char *name, Nfs3Handle *nh, ulong mode, uint gid,
259         u1int *have, Nfs3Attr *attr)
260 {
261         Nfs3TCreate tx;
262         Nfs3RCreate rx;
263
264         memset(&tx, 0, sizeof tx);
265         nfs3Call(a, &tx.call, Nfs3CallTCreate);
266         tx.handle = *h;
267         tx.name = name;
268         tx.attr.setMode = 1;
269         tx.attr.mode = mode;
270         tx.attr.setGid = 1;
271         tx.attr.gid = gid;
272
273         memset(&rx, 0, sizeof rx);
274         nfs3Call(a, &rx.call, Nfs3CallRCreate);
275
276         if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0)
277                 return -1;
278         if(rx.status != Nfs3Ok){
279                 nfs3Errstr(rx.status);
280                 return -1;
281         }
282
283         if(!rx.haveHandle){
284                 werrstr("nfs create did not return handle");
285                 return -1;
286         }
287         *nh = rx.handle;
288
289         *have = rx.haveAttr;
290         if(rx.haveAttr)
291                 *attr = rx.attr;
292         return 0;
293 }
294
295 int
296 nfsRead(Auth *a, ulong tag, Nfs3Handle *h, u32int count, u64int offset,
297         uchar **pp, u32int *pcount, uchar **pfreeme)
298 {
299         uchar *freeme;
300         Nfs3TRead tx;
301         Nfs3RRead rx;
302
303         memset(&tx, 0, sizeof tx);
304         nfs3Call(a, &tx.call, Nfs3CallTRead);
305         tx.handle = *h;
306         tx.count = count;
307         tx.offset = offset;
308
309         memset(&rx, 0, sizeof rx);
310         nfs3Call(a, &rx.call, Nfs3CallRRead);
311
312         if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, &freeme) < 0)
313                 return -1;
314         if(rx.status != Nfs3Ok){
315                 nfs3Errstr(rx.status);
316                 return -1;
317         }
318         if(rx.count != rx.ndata){
319                 werrstr("nfs read returned count=%ud ndata=%ud", (uint)rx.count, (uint)rx.ndata);
320                 free(freeme);
321                 return -1;
322         }
323         *pfreeme = freeme;
324         *pcount = rx.count;
325         *pp = rx.data;
326         return 0;
327 }
328
329 int
330 nfsWrite(Auth *a, ulong tag, Nfs3Handle *h, uchar *data, u32int count, u64int offset, u32int *pcount)
331 {
332         Nfs3TWrite tx;
333         Nfs3RWrite rx;
334
335         memset(&tx, 0, sizeof tx);
336         nfs3Call(a, &tx.call, Nfs3CallTWrite);
337         tx.handle = *h;
338         tx.count = count;
339         tx.offset = offset;
340         tx.data = data;
341         tx.ndata = count;
342
343         memset(&rx, 0, sizeof rx);
344         nfs3Call(a, &rx.call, Nfs3CallRWrite);
345
346         if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0)
347                 return -1;
348         if(rx.status != Nfs3Ok){
349                 nfs3Errstr(rx.status);
350                 return -1;
351         }
352
353         *pcount = rx.count;
354         return 0;
355 }
356
357 int
358 nfsRmdir(Auth *a, ulong tag, Nfs3Handle *h, char *name)
359 {
360         Nfs3TRmdir tx;
361         Nfs3RRmdir rx;
362
363         memset(&tx, 0, sizeof tx);
364         nfs3Call(a, &tx.call, Nfs3CallTRmdir);
365         tx.handle = *h;
366         tx.name = name;
367
368         memset(&rx, 0, sizeof rx);
369         nfs3Call(a, &rx.call, Nfs3CallRRmdir);
370
371         if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0)
372                 return -1;
373         if(rx.status != Nfs3Ok){
374                 nfs3Errstr(rx.status);
375                 return -1;
376         }
377         return 0;
378 }
379
380 int
381 nfsRemove(Auth *a, ulong tag, Nfs3Handle *h, char *name)
382 {
383         Nfs3TRemove tx;
384         Nfs3RRemove rx;
385
386         memset(&tx, 0, sizeof tx);
387         nfs3Call(a, &tx.call, Nfs3CallTRemove);
388         tx.handle = *h;
389         tx.name = name;
390
391         memset(&rx, 0, sizeof rx);
392         nfs3Call(a, &rx.call, Nfs3CallRRemove);
393
394         if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0)
395                 return -1;
396         if(rx.status != Nfs3Ok){
397                 nfs3Errstr(rx.status);
398                 return -1;
399         }
400         return 0;
401 }
402
403 int
404 nfsRename(Auth *a, ulong tag, Nfs3Handle *h, char *name, Nfs3Handle *th, char *tname)
405 {
406         Nfs3TRename tx;
407         Nfs3RRename rx;
408
409         memset(&tx, 0, sizeof tx);
410         nfs3Call(a, &tx.call, Nfs3CallTRename);
411         tx.from.handle = *h;
412         tx.from.name = name;
413         tx.to.handle = *th;
414         tx.to.name = tname;
415
416         memset(&rx, 0, sizeof rx);
417         nfs3Call(a, &rx.call, Nfs3CallRRename);
418
419         if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0)
420                 return -1;
421         if(rx.status != Nfs3Ok){
422                 nfs3Errstr(rx.status);
423                 return -1;
424         }
425         return 0;
426 }
427
428 int
429 nfsSetattr(Auth *a, ulong tag, Nfs3Handle *h, Nfs3SetAttr *attr)
430 {
431         Nfs3TSetattr tx;
432         Nfs3RSetattr rx;
433
434         memset(&tx, 0, sizeof tx);
435         nfs3Call(a, &tx.call, Nfs3CallTSetattr);
436         tx.handle = *h;
437         tx.attr = *attr;
438
439         memset(&rx, 0, sizeof rx);
440         nfs3Call(a, &rx.call, Nfs3CallRSetattr);
441
442         if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0)
443                 return -1;
444         if(rx.status != Nfs3Ok){
445                 nfs3Errstr(rx.status);
446                 return -1;
447         }
448         return 0;
449 }
450
451 int
452 nfsCommit(Auth *a, ulong tag, Nfs3Handle *h)
453 {
454         Nfs3TCommit tx;
455         Nfs3RCommit rx;
456
457         memset(&tx, 0, sizeof tx);
458         nfs3Call(a, &tx.call, Nfs3CallTCommit);
459         tx.handle = *h;
460
461         memset(&rx, 0, sizeof rx);
462         nfs3Call(a, &rx.call, Nfs3CallRCommit);
463
464         if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0)
465                 return -1;
466
467         if(rx.status != Nfs3Ok){
468                 nfs3Errstr(rx.status);
469                 return -1;
470         }
471         return 0;
472 }
473
474 int
475 nfsLookup(Auth *a, ulong tag, Nfs3Handle *h, char *name, Nfs3Handle *nh, u1int *have, Nfs3Attr *attr)
476 {
477         Nfs3TLookup tx;
478         Nfs3RLookup rx;
479
480         memset(&tx, 0, sizeof tx);
481         nfs3Call(a, &tx.call, Nfs3CallTLookup);
482         tx.handle = *h;
483         tx.name = name;
484
485         memset(&rx, 0, sizeof rx);
486         nfs3Call(a, &rx.call, Nfs3CallRLookup);
487
488         if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, nil) < 0)
489                 return -1;
490
491         if(rx.status != Nfs3Ok){
492                 nfs3Errstr(rx.status);
493                 return -1;
494         }
495         *nh = rx.handle;
496         *have = rx.haveAttr;
497         if(rx.haveAttr)
498                 *attr = rx.attr;
499         return 0;
500 }
501
502 int
503 nfsReadDirPlus(Auth *a, ulong tag, Nfs3Handle *h, u32int count, u64int cookie, uchar **pp,
504         u32int *pcount, int (**unpack)(uchar*, uchar*, uchar**, Nfs3Entry*), uchar **pfreeme)
505 {
506         Nfs3TReadDirPlus tx;
507         Nfs3RReadDirPlus rx;
508
509         memset(&tx, 0, sizeof tx);
510         nfs3Call(a, &tx.call, Nfs3CallTReadDirPlus);
511         tx.handle = *h;
512         tx.maxCount = count;
513         tx.dirCount = 1000;
514         tx.cookie = cookie;
515
516         memset(&rx, 0, sizeof rx);
517         nfs3Call(a, &rx.call, Nfs3CallRReadDirPlus);
518
519         if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, pfreeme) < 0)
520                 return -1;
521         if(rx.status != Nfs3Ok){
522                 free(*pfreeme);
523                 *pfreeme = 0;
524                 nfs3Errstr(rx.status);
525                 return -1;
526         }
527
528         *unpack = nfs3EntryPlusUnpack;
529         *pcount = rx.count;
530         *pp = rx.data;
531         return 0;
532 }
533
534 int
535 nfsReadDir(Auth *a, ulong tag, Nfs3Handle *h, u32int count, u64int cookie, uchar **pp,
536         u32int *pcount, int (**unpack)(uchar*, uchar*, uchar**, Nfs3Entry*), uchar **pfreeme)
537 {
538         /* BUG: try readdirplus */
539         char e[ERRMAX];
540         Nfs3TReadDir tx;
541         Nfs3RReadDir rx;
542
543         if(readplus!=-1){
544                 if(nfsReadDirPlus(a, tag, h, count, cookie, pp, pcount, unpack, pfreeme) == 0){
545                         readplus = 1;
546                         return 0;
547                 }
548                 if(readplus == 0){
549                         rerrstr(e, sizeof e);
550                         if(strstr(e, "procedure unavailable") || strstr(e, "not supported"))
551                                 readplus = -1;
552                 }
553                 if(readplus == 0)
554                         fprint(2, "readdirplus: %r\n");
555         }
556         if(readplus == 1)
557                 return -1;
558
559         memset(&tx, 0, sizeof tx);
560         nfs3Call(a, &tx.call, Nfs3CallTReadDir);
561         tx.handle = *h;
562         tx.count = count;
563         tx.cookie = cookie;
564
565         memset(&rx, 0, sizeof rx);
566         nfs3Call(a, &rx.call, Nfs3CallRReadDir);
567
568         if(sunClientRpc(nfscli, tag, &tx.call, &rx.call, pfreeme) < 0)
569                 return -1;
570         if(rx.status != Nfs3Ok){
571                 free(*pfreeme);
572                 *pfreeme = 0;
573                 nfs3Errstr(rx.status);
574                 return -1;
575         }
576
577         /* readplus failed but read succeeded */
578         readplus = -1;
579
580         *unpack = nfs3EntryUnpack;
581         *pcount = rx.count;
582         *pp = rx.data;
583         return 0;
584 }
585
586 /*
587  * name <-> int translation
588  */
589 typedef struct Map Map;
590 typedef struct User User;
591 typedef struct Group Group;
592
593 Map *map;
594 Map emptymap;
595
596 struct User
597 {
598         char *name;
599         uint uid;
600         uint gid;
601         uint g[16];
602         uint ng;
603         uchar *auth;
604         int nauth;
605 };
606
607 struct Group
608 {
609         char *name;     /* same pos as in User struct */
610         uint gid;       /* same pos as in User struct */
611 };
612
613 struct Map
614 {
615         int nuser;
616         int ngroup;
617         User *user;
618         User **ubyname;
619         User **ubyid;
620         Group *group;
621         Group **gbyname;
622         Group **gbyid;
623 };
624
625 User*
626 finduser(User **u, int nu, char *s)
627 {
628         int lo, hi, mid, n;
629
630         hi = nu;
631         lo = 0;
632         while(hi > lo){
633                 mid = (lo+hi)/2;
634                 n = strcmp(u[mid]->name, s);
635                 if(n == 0)
636                         return u[mid];
637                 if(n < 0)
638                         lo = mid+1;
639                 else
640                         hi = mid;
641         }
642         return nil;
643 }
644
645 int
646 strtoid(User **u, int nu, char *s, u32int *id)
647 {
648         u32int x;
649         char *p;
650         User *uu;
651
652         x = strtoul(s, &p, 10);
653         if(*s != 0 && *p == 0){
654                 *id = x;
655                 return 0;
656         }
657
658         uu = finduser(u, nu, s);
659         if(uu == nil)
660                 return -1;
661         *id = uu->uid;
662         return 0;
663 }
664
665 char*
666 idtostr(User **u, int nu, u32int id)
667 {
668         char buf[32];
669         int lo, hi, mid;
670
671         hi = nu;
672         lo = 0;
673         while(hi > lo){
674                 mid = (lo+hi)/2;
675                 if(u[mid]->uid == id)
676                         return estrdup9p(u[mid]->name);
677                 if(u[mid]->uid < id)
678                         lo = mid+1;
679                 else
680                         hi = mid;
681         }
682         snprint(buf, sizeof buf, "%ud", id);    
683         return estrdup9p(buf);
684 }               
685 char*
686 uidtostr(u32int uid)
687 {
688         return idtostr(map->ubyid, map->nuser, uid);
689 }
690
691 char*
692 gidtostr(u32int gid)
693 {
694         return idtostr((User**)map->gbyid, map->ngroup, gid);
695 }
696
697 int
698 strtouid(char *s, u32int *id)
699 {
700         return strtoid(map->ubyname, map->nuser, s, id);
701 }
702
703 int
704 strtogid(char *s, u32int *id)
705 {
706         return strtoid((User**)map->gbyid, map->ngroup, s, id);
707 }
708
709
710 int
711 idcmp(const void *va, const void *vb)
712 {
713         User **a, **b;
714
715         a = (User**)va;
716         b = (User**)vb;
717         return (*a)->uid - (*b)->uid;
718 }
719
720 int
721 namecmp(const void *va, const void *vb)
722 {
723         User **a, **b;
724
725         a = (User**)va;
726         b = (User**)vb;
727         return strcmp((*a)->name, (*b)->name);
728 }
729
730 void
731 closemap(Map *m)
732 {
733         int i;
734
735         for(i=0; i<m->nuser; i++){
736                 free(m->user[i].name);
737                 free(m->user[i].auth);
738         }
739         for(i=0; i<m->ngroup; i++)
740                 free(m->group[i].name);
741         free(m->user);
742         free(m->group);
743         free(m->ubyid);
744         free(m->ubyname);
745         free(m->gbyid);
746         free(m->gbyname);
747         free(m);
748 }
749
750 Map*
751 readmap(char *passwd, char *group)
752 {
753         char *s, *f[10], *p, *nextp, *name;
754         uchar *q, *eq;
755         int i, n, nf, line, uid, gid;
756         Biobuf *b;
757         Map *m;
758         User *u;
759         Group *g;
760         SunAuthUnix au;
761
762         m = emalloc(sizeof(Map));
763
764         if((b = Bopen(passwd, OREAD)) == nil){
765                 free(m);
766                 return nil;
767         }
768         line = 0;
769         for(; (s = Brdstr(b, '\n', 1)) != nil; free(s)){
770                 line++;
771                 if(s[0] == '#')
772                         continue;
773                 nf = getfields(s, f, nelem(f), 0, ":");
774                 if(nf < 4)
775                         continue;
776                 name = f[0];
777                 uid = strtol(f[2], &p, 10);
778                 if(f[2][0] == 0 || *p != 0){
779                         fprint(2, "%s:%d: non-numeric id in third field\n", passwd, line);
780                         continue;
781                 }
782                 gid = strtol(f[3], &p, 10);
783                 if(f[3][0] == 0 || *p != 0){
784                         fprint(2, "%s:%d: non-numeric id in fourth field\n", passwd, line);
785                         continue;
786                 }
787                 if(m->nuser%32 == 0)
788                         m->user = erealloc(m->user, (m->nuser+32)*sizeof(m->user[0]));
789                 u = &m->user[m->nuser++];
790                 u->name = estrdup9p(name);
791                 u->uid = uid;
792                 u->gid = gid;
793                 u->ng = 0;
794                 u->auth = 0;
795                 u->nauth = 0;
796         }
797         Bterm(b);
798         m->ubyname = emalloc(m->nuser*sizeof(User*));
799         m->ubyid = emalloc(m->nuser*sizeof(User*));
800         for(i=0; i<m->nuser; i++){
801                 m->ubyname[i] = &m->user[i];
802                 m->ubyid[i] = &m->user[i];
803         }
804         qsort(m->ubyname, m->nuser, sizeof(m->ubyname[0]), namecmp);
805         qsort(m->ubyid, m->nuser, sizeof(m->ubyid[0]), idcmp);
806
807         if((b = Bopen(group, OREAD)) == nil){
808                 closemap(m);
809                 return nil;
810         }
811         line = 0;
812         for(; (s = Brdstr(b, '\n', 1)) != nil; free(s)){
813                 line++;
814                 if(s[0] == '#')
815                         continue;
816                 nf = getfields(s, f, nelem(f), 0, ":");
817                 if(nf < 4)
818                         continue;
819                 name = f[0];
820                 gid = strtol(f[2], &p, 10);
821                 if(f[2][0] == 0 || *p != 0){
822                         fprint(2, "%s:%d: non-numeric id in third field\n", group, line);
823                         continue;
824                 }
825                 if(m->ngroup%32 == 0)
826                         m->group = erealloc(m->group, (m->ngroup+32)*sizeof(m->group[0]));
827                 g = &m->group[m->ngroup++];
828                 g->name = estrdup9p(name);
829                 g->gid = gid;
830
831                 for(p=f[3]; *p; p=nextp){
832                         if((nextp = strchr(p, ',')) != nil)
833                                 *nextp++ = 0;
834                         else
835                                 nextp = p+strlen(p);
836                         u = finduser(m->ubyname, m->nuser, p);
837                         if(u == nil){
838                                 if(verbose)
839                                         fprint(2, "%s:%d: unknown user %s\n", group, line, p);
840                                 continue;
841                         }
842                         if(u->ng >= nelem(u->g)){
843                                 fprint(2, "%s:%d: user %s is in too many groups; ignoring %s\n", group, line, p, name);
844                                 continue;
845                         }
846                         u->g[u->ng++] = gid;
847                 }
848         }
849         Bterm(b);
850         m->gbyname = emalloc(m->ngroup*sizeof(Group*));
851         m->gbyid = emalloc(m->ngroup*sizeof(Group*));
852         for(i=0; i<m->ngroup; i++){
853                 m->gbyname[i] = &m->group[i];
854                 m->gbyid[i] = &m->group[i];
855         }
856         qsort(m->gbyname, m->ngroup, sizeof(m->gbyname[0]), namecmp);
857         qsort(m->gbyid, m->ngroup, sizeof(m->gbyid[0]), idcmp);
858
859         for(i=0; i<m->nuser; i++){
860                 au.stamp = 0;
861                 au.sysname = sys;
862                 au.uid = m->user[i].uid;
863                 au.gid = m->user[i].gid;
864                 memmove(au.g, m->user[i].g, sizeof au.g);
865                 au.ng = m->user[i].ng;
866                 n = sunAuthUnixSize(&au);
867                 q = emalloc(n);
868                 eq = q+n;
869                 m->user[i].auth = q;
870                 m->user[i].nauth = n;
871                 if(sunAuthUnixPack(q, eq, &q, &au) < 0 || q != eq){
872                         fprint(2, "sunAuthUnixPack failed for %s\n", m->user[i].name);
873                         free(m->user[i].auth);
874                         m->user[i].auth = 0;
875                         m->user[i].nauth = 0;
876                 }
877         }
878
879         return m;
880 }
881
882 Auth*
883 mkauth(char *user)
884 {
885         Auth *a;
886         uchar *p;
887         int n;
888         SunAuthUnix au;
889         User *u;
890
891         u = finduser(map->ubyname, map->nuser, user);
892         if(u == nil || u->nauth == 0){
893                 /* nobody */
894                 au.stamp = 0;
895                 au.uid = -1;
896                 au.gid = -1;
897                 au.ng = 0;
898                 au.sysname = sys;
899                 n = sunAuthUnixSize(&au);
900                 a = emalloc(sizeof(Auth)+n);
901                 a->data = (uchar*)&a[1];
902                 a->ndata = n;
903                 if(sunAuthUnixPack(a->data, a->data+a->ndata, &p, &au) < 0
904                 || p != a->data+a->ndata){
905                         free(a);
906                         return nil;
907                 }
908                 a->ref = 1;
909 if(verbose)print("creds for %s: %.*H\n", user, a->ndata, a->data);
910                 return a;
911         }
912
913         a = emalloc(sizeof(Auth)+u->nauth);
914         a->data = (uchar*)&a[1];
915         a->ndata = u->nauth;
916         memmove(a->data, u->auth, a->ndata);
917         a->ref = 1;
918 if(verbose)print("creds for %s: %.*H\n", user, a->ndata, a->data);
919         return a;
920 }
921
922 void
923 freeauth(Auth *a)
924 {
925         if(--a->ref > 0)
926                 return;
927         free(a);
928 }
929
930 /*
931  * 9P server
932  */
933 void
934 responderrstr(Req *r)
935 {
936         char e[ERRMAX];
937
938         rerrstr(e, sizeof e);
939         respond(r, e);
940 }
941
942 void
943 fsdestroyfid(Fid *fid)
944 {
945         FidAux *aux;
946
947         aux = fid->aux;
948         if(aux == nil)
949                 return;
950         freeauth(aux->auth);
951         free(aux->name);
952         free(aux);
953 }
954
955 void
956 attrToQid(Nfs3Attr *attr, Qid *qid)
957 {
958         qid->path = attr->fileid;
959         qid->vers = attr->mtime.sec;
960         qid->type = 0;
961         if(attr->type == Nfs3FileDir)
962                 qid->type |= QTDIR;
963 }
964
965 void
966 attrToDir(Nfs3Attr *attr, Dir *d)
967 {
968         d->mode = attr->mode & 0777;
969         if(attr->type == Nfs3FileDir)
970                 d->mode |= DMDIR;
971         d->uid = uidtostr(attr->uid);
972         d->gid = gidtostr(attr->gid);
973         d->length = attr->size;
974         attrToQid(attr, &d->qid);
975         d->mtime = attr->mtime.sec;
976         d->atime = attr->atime.sec;
977         d->muid = nil;
978 }
979
980 void
981 fsattach(Req *r)
982 {
983         char *path;
984         Auth *auth;
985         FidAux *aux;
986         Nfs3Attr attr;
987         Nfs3Handle h;
988
989         path = r->ifcall.aname;
990         if(path==nil || path[0]==0)
991                 path = defaultpath;
992
993         auth = mkauth(r->ifcall.uname);
994
995         if(mountMnt(auth, r->tag, path, &h) < 0
996         || nfsGetattr(auth, r->tag, &h, &attr) < 0){
997                 freeauth(auth);
998                 responderrstr(r);
999                 return;
1000         }
1001
1002         aux = emalloc(sizeof(FidAux));
1003         aux->auth = auth;
1004         aux->handle = h;
1005         aux->cookie = 0;
1006         aux->name = nil;
1007         memset(&aux->parent, 0, sizeof aux->parent);
1008         r->fid->aux = aux;
1009         attrToQid(&attr, &r->fid->qid);
1010         r->ofcall.qid = r->fid->qid;
1011         respond(r, nil);
1012 }
1013
1014 void
1015 fsopen(Req *r)
1016 {
1017         FidAux *aux;
1018         Nfs3Attr attr;
1019         Nfs3SetAttr sa;
1020         u1int have;
1021         ulong a, b;
1022
1023         aux = r->fid->aux;
1024         a = 0;
1025         switch(r->ifcall.mode&OMASK){
1026         case OREAD:
1027                 a = 0x0001;
1028                 break;
1029         case OWRITE:
1030                 a = 0x0004;
1031                 break;
1032         case ORDWR:
1033                 a = 0x0001|0x0004;
1034                 break;
1035         case OEXEC:
1036                 a = 0x20;
1037                 break;
1038         }
1039         if(r->ifcall.mode&OTRUNC)
1040                 a |= 0x0004;
1041
1042         if(nfsAccess(aux->auth, r->tag, &aux->handle, a, &b, &have, &attr) < 0
1043         || (!have && nfsGetattr(aux->auth, r->tag, &aux->handle, &attr) < 0)){
1044     Error:
1045                 responderrstr(r);
1046                 return;
1047         }
1048         if(a != b){
1049                 respond(r, "permission denied");
1050                 return;
1051         }
1052         if(r->ifcall.mode&OTRUNC){
1053                 memset(&sa, 0, sizeof sa);
1054                 sa.setSize = 1;
1055                 if(nfsSetattr(aux->auth, r->tag, &aux->handle, &sa) < 0)
1056                         goto Error;
1057         }
1058         attrToQid(&attr, &r->fid->qid);
1059         r->ofcall.qid = r->fid->qid;
1060         respond(r, nil);
1061 }
1062
1063 void
1064 fscreate(Req *r)
1065 {
1066         FidAux *aux;
1067         u1int have;
1068         Nfs3Attr attr;
1069         Nfs3Handle h;
1070         ulong mode;
1071         uint gid;
1072         int (*mk)(Auth*, ulong, Nfs3Handle*, char*, Nfs3Handle*, ulong, uint, u1int*, Nfs3Attr*);
1073
1074         aux = r->fid->aux;
1075
1076         /*
1077          * Plan 9 has no umask, so let's use the
1078          * parent directory bits like Plan 9 does.
1079          * What the heck, let's inherit the group too.
1080          * (Unix will let us set the group to anything
1081          * since we're the owner!)
1082          */
1083         if(nfsGetattr(aux->auth, r->tag, &aux->handle, &attr) < 0){
1084                 responderrstr(r);
1085                 return;
1086         }
1087         mode = r->ifcall.perm&0777;
1088         if(r->ifcall.perm&DMDIR)
1089                 mode &= (attr.mode&0666) | ~0666;
1090         else
1091                 mode &= (attr.mode&0777) | ~0777;
1092         gid = attr.gid;
1093
1094         if(r->ifcall.perm&DMDIR)
1095                 mk = nfsMkdir;
1096         else
1097                 mk = nfsCreate;
1098
1099         if((*mk)(aux->auth, r->tag, &aux->handle, r->ifcall.name, &h, mode, gid, &have, &attr) < 0
1100         || (!have && nfsGetattr(aux->auth, r->tag, &h, &attr) < 0)){
1101                 responderrstr(r);
1102                 return;
1103         }
1104         attrToQid(&attr, &r->fid->qid);
1105         aux->parent = aux->handle;
1106         aux->handle = h;
1107         free(aux->name);
1108         aux->name = estrdup9p(r->ifcall.name);
1109         r->ofcall.qid = r->fid->qid;
1110         respond(r, nil);
1111 }
1112
1113 void
1114 fsreaddir(Req *r)
1115 {
1116         FidAux *aux;
1117         uchar *p, *freeme, *ep, *p9, *ep9;
1118         char *s;
1119         uint count;
1120         int n, (*unpack)(uchar*, uchar*, uchar**, Nfs3Entry*);
1121         Nfs3Entry e;
1122         u64int cookie;
1123         Dir d;
1124
1125         aux = r->fid->aux;
1126         /*
1127          * r->ifcall.count seems a reasonable estimate to
1128          * how much NFS entry data we want.  is it?
1129          */
1130         if(r->ifcall.offset)
1131                 cookie = aux->cookie;
1132         else
1133                 cookie = 0;
1134         if(nfsReadDir(aux->auth, r->tag, &aux->handle, r->ifcall.count, cookie,
1135                 &p, &count, &unpack, &freeme) < 0){
1136                 responderrstr(r);
1137                 return;
1138         }
1139         ep = p+count;
1140
1141         p9 = (uchar*)r->ofcall.data;
1142         ep9 = p9+r->ifcall.count;
1143
1144         /*
1145          * BUG: Issue all of the stat requests in parallel.
1146          */
1147         while(p < ep && p9 < ep9){
1148                 if((*unpack)(p, ep, &p, &e) < 0)
1149                         break;
1150                 aux->cookie = e.cookie;
1151                 if(strcmp(e.name, ".") == 0 || strcmp(e.name, "..") == 0)
1152                         continue;
1153                 for(s=e.name; (uchar)*s >= ' '; s++)
1154                         ;
1155                 if(*s != 0)     /* bad character in name */
1156                         continue;
1157                 if(!e.haveAttr && !e.haveHandle)
1158                         if(nfsLookup(aux->auth, r->tag, &aux->handle, e.name, &e.handle, &e.haveAttr, &e.attr) < 0)
1159                                 continue;
1160                 if(!e.haveAttr)
1161                         if(nfsGetattr(aux->auth, r->tag, &e.handle, &e.attr) < 0)
1162                                 continue;
1163                 memset(&d, 0, sizeof d);
1164                 attrToDir(&e.attr, &d);
1165                 d.name = e.name;
1166                 if((n = convD2M(&d, p9, ep9-p9)) <= BIT16SZ)
1167                         break;
1168                 p9 += n;
1169         }
1170         free(freeme);
1171         r->ofcall.count = p9 - (uchar*)r->ofcall.data;
1172         respond(r, nil);
1173 }
1174         
1175 void
1176 fsread(Req *r)
1177 {
1178         uchar *p, *freeme;
1179         uint count;
1180         FidAux *aux;
1181
1182         if(r->fid->qid.type&QTDIR){
1183                 fsreaddir(r);
1184                 return;
1185         }
1186
1187         aux = r->fid->aux;
1188         if(nfsRead(aux->auth, r->tag, &aux->handle, r->ifcall.count, r->ifcall.offset, &p, &count, &freeme) < 0){
1189                 responderrstr(r);
1190                 return;
1191         }
1192         r->ofcall.data = (char*)p;
1193         r->ofcall.count = count;
1194         respond(r, nil);
1195         free(freeme);
1196 }
1197
1198 void
1199 fswrite(Req *r)
1200 {
1201         uint count;
1202         FidAux *aux;
1203
1204         aux = r->fid->aux;
1205         if(nfsWrite(aux->auth, r->tag, &aux->handle, (uchar*)r->ifcall.data, r->ifcall.count, r->ifcall.offset, &count) < 0){
1206                 responderrstr(r);
1207                 return;
1208         }
1209         r->ofcall.count = count;
1210         respond(r, nil);
1211 }
1212
1213 void
1214 fsremove(Req *r)
1215 {
1216         int n;
1217         FidAux *aux;
1218
1219         aux = r->fid->aux;
1220         if(aux->name == nil){
1221                 respond(r, "nfs3client botch -- don't know parent handle in remove");
1222                 return;
1223         }
1224         if(r->fid->qid.type&QTDIR)
1225                 n = nfsRmdir(aux->auth, r->tag, &aux->parent, aux->name);
1226         else
1227                 n = nfsRemove(aux->auth, r->tag, &aux->parent, aux->name);
1228         if(n < 0){
1229                 responderrstr(r);
1230                 return;
1231         }
1232         respond(r, nil);
1233 }
1234
1235 void
1236 fsstat(Req *r)
1237 {
1238         FidAux *aux;
1239         Nfs3Attr attr;
1240
1241         aux = r->fid->aux;
1242         if(nfsGetattr(aux->auth, r->tag, &aux->handle, &attr) < 0){
1243                 responderrstr(r);
1244                 return;
1245         }
1246         memset(&r->d, 0, sizeof r->d);
1247         attrToDir(&attr, &r->d);
1248         r->d.name = estrdup9p(aux->name ? aux->name : "???");
1249         respond(r, nil);
1250 }
1251
1252 void
1253 fswstat(Req *r)
1254 {
1255         int op, sync;
1256         FidAux *aux;
1257         Nfs3SetAttr attr;
1258
1259         memset(&attr, 0, sizeof attr);
1260         aux = r->fid->aux;
1261
1262         /* Fill out stat first to catch errors */
1263         op = 0;
1264         sync = 1;
1265         if(~r->d.mode){
1266                 if(r->d.mode&(DMAPPEND|DMEXCL)){
1267                         respond(r, "wstat -- DMAPPEND and DMEXCL bits not supported");
1268                         return;
1269                 }
1270                 op = 1;
1271                 sync = 0;
1272                 attr.setMode = 1;
1273                 attr.mode = r->d.mode & 0777;
1274         }
1275         if(r->d.uid && r->d.uid[0]){
1276                 attr.setUid = 1;
1277                 if(strtouid(r->d.uid, &attr.uid) < 0){
1278                         respond(r, "wstat -- unknown uid");
1279                         return;
1280                 }
1281                 op = 1;
1282                 sync = 0;
1283         }
1284         if(r->d.gid && r->d.gid[0]){
1285                 attr.setGid = 1;
1286                 if(strtogid(r->d.gid, &attr.gid) < 0){
1287                         respond(r, "wstat -- unknown gid");
1288                         return;
1289                 }
1290                 op = 1;
1291                 sync = 0;
1292         }
1293         if(~r->d.length){
1294                 attr.setSize = 1;
1295                 attr.size = r->d.length;
1296                 op = 1;
1297                 sync = 0;
1298         }
1299         if(~r->d.mtime){
1300                 attr.setMtime = Nfs3SetTimeClient;
1301                 attr.mtime.sec = r->d.mtime;
1302                 op = 1;
1303                 sync = 0;
1304         }
1305         if(~r->d.atime){
1306                 attr.setAtime = Nfs3SetTimeClient;
1307                 attr.atime.sec = r->d.atime;
1308                 op = 1;
1309                 sync = 0;
1310         }
1311
1312         /* Try rename first because it's more likely to fail (?) */
1313         if(r->d.name && r->d.name[0]){
1314                 if(aux->name == nil){
1315                         respond(r, "nfsclient botch -- don't know parent handle in rename");
1316                         return;
1317                 }
1318                 if(nfsRename(aux->auth, r->tag, &aux->parent, aux->name, &aux->parent, r->d.name) < 0){
1319                         responderrstr(r);
1320                         return;
1321                 }
1322                 free(aux->name);
1323                 aux->name = estrdup9p(r->d.name);
1324                 sync = 0;
1325         }
1326
1327         /*
1328          * Now we have a problem.  The rename succeeded
1329          * but the setattr could fail.  Sic transit atomicity.
1330          */
1331         if(op){
1332                 if(nfsSetattr(aux->auth, r->tag, &aux->handle, &attr) < 0){
1333                         responderrstr(r);
1334                         return;
1335                 }
1336         }
1337
1338         if(sync){
1339                 /* NFS commit */
1340                 if(nfsCommit(aux->auth, r->tag, &aux->handle) < 0){
1341                         responderrstr(r);
1342                         return;
1343                 }
1344         }
1345
1346         respond(r, nil);
1347 }
1348
1349 char*
1350 fswalk1(Fid *fid, char *name, void *v)
1351 {
1352         u1int have;
1353         ulong tag;
1354         FidAux *aux;
1355         Nfs3Attr attr;
1356         Nfs3Handle h;
1357
1358         tag = *(ulong*)v;
1359         aux = fid->aux;
1360
1361         if(nfsLookup(aux->auth, tag, &aux->handle, name, &h, &have, &attr) < 0
1362         || (!have && nfsGetattr(aux->auth, tag, &h, &attr) < 0)){
1363                 rerrstr(aux->err, sizeof aux->err);
1364                 return aux->err;
1365         }
1366
1367         aux->parent = aux->handle;
1368         aux->handle = h;
1369         free(aux->name);
1370         if(strcmp(name, "..") == 0)
1371                 aux->name = nil;
1372         else
1373                 aux->name = estrdup9p(name);
1374         attrToQid(&attr, &fid->qid);
1375         return nil;
1376 }
1377
1378 char*
1379 fsclone(Fid *fid, Fid *newfid, void*)
1380 {
1381         FidAux *a, *na;
1382
1383         a = fid->aux;
1384         na = emalloc9p(sizeof(FidAux));
1385         *na = *a;
1386         if(na->name)
1387                 na->name = estrdup9p(na->name);
1388         newfid->aux = na;
1389         if(na->auth)
1390                 na->auth->ref++;
1391         return nil;
1392 }
1393
1394 void
1395 fswalk(Req *r)
1396 {
1397         walkandclone(r, fswalk1, fsclone, &r->tag);
1398 }
1399
1400 void
1401 fsflush(Req *r)
1402 {
1403         Req *or;
1404
1405         /*
1406          * Send on the flush channel(s).
1407          * The library will make sure the response
1408          * is delayed as necessary.
1409          */
1410         or = r->oldreq;
1411         if(nfscli)
1412                 sendul(nfscli->flushchan, (ulong)or->tag);
1413         if(mntcli)
1414                 sendul(mntcli->flushchan, (ulong)or->tag);
1415         respond(r, nil);
1416 }
1417
1418 void
1419 fsdispatch(void *v)
1420 {
1421         Req *r;
1422
1423         r = v;
1424         switch(r->ifcall.type){
1425         default:        respond(r, "unknown type");     break;
1426         case Tattach:   fsattach(r);    break;
1427         case Topen:     fsopen(r);      break;
1428         case Tcreate:   fscreate(r);    break;
1429         case Tread:     fsread(r);      break;
1430         case Twrite:    fswrite(r);     break;
1431         case Tremove:   fsremove(r);    break;
1432         case Tflush:    fsflush(r);     break;
1433         case Tstat:     fsstat(r);      break;
1434         case Twstat:    fswstat(r);     break;
1435         case Twalk:     fswalk(r);      break;
1436         }
1437 }
1438
1439 void
1440 fsthread(void*)
1441 {
1442         Req *r;
1443
1444         while((r = recvp(fschan)) != nil)
1445                 threadcreate(fsdispatch, r, SunStackSize);
1446 }
1447
1448 void
1449 fssend(Req *r)
1450 {
1451         sendp(fschan, r);
1452 }
1453
1454 void
1455 fsdie(Srv*)
1456 {
1457         threadexitsall(nil);
1458 }
1459
1460 Srv fs =
1461 {
1462 .destroyfid =   fsdestroyfid,
1463 .attach=                fssend,
1464 .open=          fssend,
1465 .create=                fssend,
1466 .read=          fssend,
1467 .write=         fssend,
1468 .remove=                fssend,
1469 .flush=         fssend,
1470 .stat=          fssend,
1471 .wstat=         fssend,
1472 .walk=          fssend,
1473 .end=           fsdie
1474 };
1475
1476 void
1477 usage(void)
1478 {
1479         fprint(2, "usage: nfs [-DRv] [-p perm] [-s srvname] [-u passwd group] addr [addr]\n");
1480         fprint(2, "\taddr - address of portmapper server\n");
1481         fprint(2, "\taddr addr - addresses of mount server and nfs server\n");
1482         exits("usage");
1483 }
1484
1485 char*
1486 netchangeport(char *addr, uint port, char *buf, uint nbuf)
1487 {
1488         char *r;
1489
1490         strecpy(buf, buf+nbuf, addr);
1491         r = strrchr(buf, '!');
1492         if(r == nil)
1493                 return nil;
1494         r++;
1495         seprint(r, buf+nbuf, "%ud", port);
1496         return buf;
1497 }
1498
1499 char mbuf[256], nbuf[256];
1500 char *mountaddr, *nfsaddr;
1501 Channel *csync;
1502 int chattyrpc;
1503 void dialproc(void*);
1504
1505 void
1506 threadmain(int argc, char **argv)
1507 {
1508         char *srvname, *passwd, *group, *addr, *p;
1509         SunClient *cli;
1510         int proto;
1511         uint mport, nport;
1512         ulong perm;
1513         Dir d;
1514
1515         perm = 0600;
1516         passwd = nil;
1517         group = nil;
1518         srvname = nil;
1519         sys = sysname();
1520         if(sys == nil)
1521                 sys = "plan9";
1522         ARGBEGIN{
1523         default:
1524                 usage();
1525         case 'D':
1526                 chatty9p++;
1527                 break;
1528         case 'R':
1529                 chattyrpc++;
1530                 break;
1531         case 'p':
1532                 perm = strtol(EARGF(usage()), &p, 8);
1533                 if(perm==0 || *p != 0)
1534                         usage();
1535                 break;
1536         case 's':
1537                 srvname = EARGF(usage());
1538                 break;
1539         case 'u':
1540                 passwd = EARGF(usage());
1541                 group = EARGF(usage());
1542                 break;
1543         case 'v':
1544                 verbose++;
1545                 break;
1546         }ARGEND
1547
1548         if(argc != 1 && argc != 2)
1549                 usage();
1550
1551         if(srvname == nil)
1552                 srvname = argv[0];
1553
1554         fmtinstall('B', sunRpcFmt);
1555         fmtinstall('C', sunCallFmt);
1556         fmtinstall('H', encodefmt);
1557         sunFmtInstall(&portProg);
1558         sunFmtInstall(&nfs3Prog);
1559         sunFmtInstall(&nfsMount3Prog);
1560
1561         if(passwd && (map = readmap(passwd, group)) == nil)
1562                 fprint(2, "warning: reading %s and %s: %r\n", passwd, group);
1563
1564         if(map == nil)
1565                 map = &emptymap;
1566
1567         if(argc == 1){
1568                 addr = netmkaddr(argv[0], "udp", "portmap");
1569                 if((cli = sunDial(addr)) == nil)
1570                         sysfatal("dial %s: %r", addr);
1571                 cli->chatty = chattyrpc;
1572                 sunClientProg(cli, &portProg);
1573                 if(strstr(addr, "udp!"))
1574                         proto = PortProtoUdp;
1575                 else
1576                         proto = PortProtoTcp;
1577                 if(getport(cli, NfsMount3Program, NfsMount3Version, proto, &mport) < 0)
1578                         sysfatal("lookup mount program port: %r");
1579                 if(getport(cli, Nfs3Program, Nfs3Version, proto, &nport) < 0)
1580                         sysfatal("lookup nfs program port: %r");
1581                 sunClientClose(cli);
1582                 mountaddr = netchangeport(addr, mport, mbuf, sizeof mbuf);
1583                 nfsaddr = netchangeport(addr, nport, nbuf, sizeof nbuf);
1584                 strcat(mountaddr, "!r");
1585                 strcat(nfsaddr, "!r");
1586                 if(verbose)
1587                         fprint(2, "nfs %s %s\n", mountaddr, nfsaddr);
1588         }else{
1589                 mountaddr = argv[0];
1590                 nfsaddr = argv[1];
1591         }
1592
1593         /* have to dial in another proc because it creates threads */
1594         csync = chancreate(sizeof(void*), 0);
1595         proccreate(dialproc, nil, SunStackSize);
1596         recvp(csync);
1597
1598         threadpostmountsrv(&fs, srvname, nil, 0);
1599         if(perm != 0600){
1600                 p = smprint("/srv/%s", srvname);
1601                 if(p){
1602                         nulldir(&d);
1603                         d.mode = perm;
1604                         dirwstat(p, &d);
1605                 }
1606         }
1607         threadexits(nil);
1608 }
1609
1610 void
1611 dialproc(void*)
1612 {
1613         rfork(RFNAMEG);
1614         rfork(RFNOTEG);
1615         if((mntcli = sunDial(mountaddr)) == nil)
1616                 sysfatal("dial mount program at %s: %r", mountaddr);
1617         mntcli->chatty = chattyrpc;
1618         sunClientProg(mntcli, &nfsMount3Prog);
1619         if(mountNull(0) < 0)
1620                 sysfatal("execute nop with mnt server at %s: %r", mountaddr);
1621         
1622         if((nfscli = sunDial(nfsaddr)) == nil)
1623                 sysfatal("dial nfs program at %s: %r", nfsaddr);
1624         nfscli->chatty = chattyrpc;
1625         sunClientProg(nfscli, &nfs3Prog);
1626         if(nfsNull(0) < 0)
1627                 sysfatal("execute nop with nfs server at %s: %r", nfsaddr);
1628
1629         fschan = chancreate(sizeof(Req*), 0);
1630         threadcreate(fsthread, nil, SunStackSize);
1631         sendp(csync, 0);
1632 }