]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/9nfs/nfsserver.c
9nfs: apply patch/nfswarnandflags (from sources, thanks nemo)
[plan9front.git] / sys / src / cmd / 9nfs / nfsserver.c
1 #include "all.h"
2
3 /*
4  *      Cf. /lib/rfc/rfc1094
5  */
6
7 static int      nfsnull(int, Rpccall*, Rpccall*);
8 static int      nfsgetattr(int, Rpccall*, Rpccall*);
9 static int      nfssetattr(int, Rpccall*, Rpccall*);
10 static int      nfsroot(int, Rpccall*, Rpccall*);
11 static int      nfslookup(int, Rpccall*, Rpccall*);
12 static int      nfsreadlink(int, Rpccall*, Rpccall*);
13 static int      nfsread(int, Rpccall*, Rpccall*);
14 static int      nfswritecache(int, Rpccall*, Rpccall*);
15 static int      nfswrite(int, Rpccall*, Rpccall*);
16 static int      nfscreate(int, Rpccall*, Rpccall*);
17 static int      nfsremove(int, Rpccall*, Rpccall*);
18 static int      nfsrename(int, Rpccall*, Rpccall*);
19 static int      nfslink(int, Rpccall*, Rpccall*);
20 static int      nfssymlink(int, Rpccall*, Rpccall*);
21 static int      nfsmkdir(int, Rpccall*, Rpccall*);
22 static int      nfsrmdir(int, Rpccall*, Rpccall*);
23 static int      nfsreaddir(int, Rpccall*, Rpccall*);
24 static int      nfsstatfs(int, Rpccall*, Rpccall*);
25
26 Procmap nfsproc[] = {
27         0, nfsnull,     /* void */
28         1, nfsgetattr,  /* Fhandle */
29         2, nfssetattr,  /* Fhandle, Sattr */
30         3, nfsroot,     /* void */
31         4, nfslookup,   /* Fhandle, String */
32         5, nfsreadlink, /* Fhandle */
33         6, nfsread,     /* Fhandle, long, long, long */
34         7, nfswritecache,/* void */
35         8, nfswrite,    /* Fhandle, long, long, long, String */
36         9, nfscreate,   /* Fhandle, String, Sattr */
37         10, nfsremove,  /* Fhandle, String */
38         11, nfsrename,  /* Fhandle, String, Fhandle, String */
39         12, nfslink,    /* Fhandle, Fhandle, String */
40         13, nfssymlink, /* Fhandle, String, String, Sattr */
41         14, nfsmkdir,   /* Fhandle, String, Sattr */
42         15, nfsrmdir,   /* Fhandle, String */
43         16, nfsreaddir, /* Fhandle, long, long */
44         17, nfsstatfs,  /* Fhandle */
45         0, 0
46 };
47
48 void    nfsinit(int, char**);
49 extern void     mntinit(int, char**);
50 extern Procmap  mntproc[];
51
52 Progmap progmap[] = {
53         100005, 1, mntinit, mntproc,
54         100003, 2, nfsinit, nfsproc,
55         0, 0, 0,
56 };
57
58 int     myport = 2049;
59 long    nfstime;
60 int     conftime;
61
62 void
63 main(int argc, char *argv[])
64 {
65         server(argc, argv, myport, progmap);
66 }
67
68 static void
69 doalarm(void)
70 {
71         nfstime = time(0);
72         mnttimer(nfstime);
73         if(conftime+5*60 < nfstime){
74                 conftime = nfstime;
75                 readunixidmaps(config);
76         }
77 }
78
79 void
80 nfsinit(int argc, char **argv)
81 {
82         /*
83          * mntinit will have already parsed our options.
84          */
85         USED(argc, argv);
86         clog("nfs file server init\n");
87         rpcalarm = doalarm;
88         nfstime = time(0);
89 }
90
91 static int
92 nfsnull(int n, Rpccall *cmd, Rpccall *reply)
93 {
94         USED(n, reply);
95         chat("nfsnull...");
96         showauth(&cmd->cred);
97         chat("OK\n");
98         return 0;
99 }
100
101 static int
102 nfsgetattr(int n, Rpccall *cmd, Rpccall *reply)
103 {
104         Xfid *xf;
105         Dir dir;
106         uchar *dataptr = reply->results;
107
108         chat("getattr...");
109         if(n != FHSIZE)
110                 return garbage(reply, "bad count");
111         xf = rpc2xfid(cmd, &dir);
112         if(xf == 0)
113                 return error(reply, NFSERR_STALE);
114         chat("%s...", xf->xp->name);
115         PLONG(NFS_OK);
116         dataptr += dir2fattr(cmd->up, &dir, dataptr);
117         chat("OK\n");
118         return dataptr - (uchar *)reply->results;
119 }
120
121 static int
122 nfssetattr(int n, Rpccall *cmd, Rpccall *reply)
123 {
124         Xfid *xf;
125         Dir dir, nd;
126         Sattr sattr;
127         int r;
128         uchar *argptr = cmd->args;
129         uchar *dataptr = reply->results;
130
131         chat("setattr...");
132         if(n <= FHSIZE)
133                 return garbage(reply, "count too small");
134         xf = rpc2xfid(cmd, &dir);
135         argptr += FHSIZE;
136         argptr += convM2sattr(argptr, &sattr);
137         if(argptr != &((uchar *)cmd->args)[n])
138                 return garbage(reply, "bad count");
139         chat("mode=0%lo,u=%ld,g=%ld,size=%ld,atime=%ld,mtime=%ld...",
140                 sattr.mode, sattr.uid, sattr.gid, sattr.size,
141                 sattr.atime, sattr.mtime);
142         if(xf == 0)
143                 return error(reply, NFSERR_STALE);
144         if(sattr.uid != NOATTR || sattr.gid != NOATTR)
145                 return error(reply, NFSERR_PERM);
146         if(sattr.size == 0){
147                 if(xf->xp->s != xf->xp->parent->s){
148                         if(xfauthremove(xf, cmd->user) < 0)
149                                 return error(reply, NFSERR_PERM);
150                 }else if(dir.length && xfopen(xf, Trunc|Oread|Owrite) < 0)
151                         return error(reply, NFSERR_PERM);
152         }else if(sattr.size != NOATTR)
153                 return error(reply, NFSERR_PERM);
154         r = 0;
155         nulldir(&nd);
156         if(sattr.mode != NOATTR)
157                 ++r, nd.mode = (dir.mode & ~0777) | (sattr.mode & 0777);
158         if(sattr.atime != NOATTR)
159                 ++r, nd.atime = sattr.atime;
160         if(sattr.mtime != NOATTR)
161                 ++r, nd.mtime = sattr.mtime;
162         chat("sattr.mode=%luo dir.mode=%luo nd.mode=%luo...", sattr.mode, dir.mode, nd.mode);
163         if(r){
164                 r = xfwstat(xf, &nd);
165                 if(r < 0)
166                         return error(reply, NFSERR_PERM);
167         }
168         if(xfstat(xf, &dir) < 0)
169                 return error(reply, NFSERR_STALE);
170         PLONG(NFS_OK);
171         dataptr += dir2fattr(cmd->up, &dir, dataptr);
172         chat("OK\n");
173         return dataptr - (uchar *)reply->results;
174 }
175
176 static int
177 nfsroot(int n, Rpccall *cmd, Rpccall *reply)
178 {
179         USED(n, reply);
180         chat("nfsroot...");
181         showauth(&cmd->cred);
182         chat("OK\n");
183         return 0;
184 }
185
186 static int
187 nfslookup(int n, Rpccall *cmd, Rpccall *reply)
188 {
189         Xfile *xp;
190         Xfid *xf, *newxf;
191         String elem;
192         Dir dir;
193         uchar *argptr = cmd->args;
194         uchar *dataptr = reply->results;
195
196         chat("lookup...");
197         if(n <= FHSIZE)
198                 return garbage(reply, "count too small");
199         xf = rpc2xfid(cmd, 0);
200         argptr += FHSIZE;
201         argptr += string2S(argptr, &elem);
202         if(argptr != &((uchar *)cmd->args)[n])
203                 return garbage(reply, "bad count");
204         if(xf == 0)
205                 return error(reply, NFSERR_STALE);
206         xp = xf->xp;
207         if(!(xp->qid.type & QTDIR))
208                 return error(reply, NFSERR_NOTDIR);
209         chat("%s -> \"%.*s\"...", xp->name, utfnlen(elem.s, elem.n), elem.s);
210         if(xp->s->noauth == 0 && xp->parent == xp && elem.s[0] == '#')
211                 newxf = xfauth(xp, &elem);
212         else
213                 newxf = xfwalkcr(Twalk, xf, &elem, 0);
214         if(newxf == 0)
215                 return error(reply, NFSERR_NOENT);
216         if(xfstat(newxf, &dir) < 0)
217                 return error(reply, NFSERR_IO);
218         PLONG(NFS_OK);
219         dataptr += xp2fhandle(newxf->xp, dataptr);
220         dataptr += dir2fattr(cmd->up, &dir, dataptr);
221         chat("OK\n");
222         return dataptr - (uchar *)reply->results;
223 }
224
225 static int
226 nfsreadlink(int n, Rpccall *cmd, Rpccall *reply)
227 {
228         USED(n, reply);
229         chat("readlink...");
230         showauth(&cmd->cred);
231         return error(reply, NFSERR_NOENT);
232 }
233
234 static int
235 nfsread(int n, Rpccall *cmd, Rpccall *reply)
236 {
237         Session *s;
238         Xfid *xf;
239         Dir dir;
240         int offset, count;
241         uchar *argptr = cmd->args;
242         uchar *dataptr = reply->results;
243         uchar *readptr = dataptr + 4 + 17*4 + 4;
244
245         chat("read...");
246         if(n != FHSIZE+12)
247                 return garbage(reply, "bad count");
248         xf = rpc2xfid(cmd, 0);
249         argptr += FHSIZE;
250         offset = GLONG();
251         count = GLONG();
252         if(xf == 0)
253                 return error(reply, NFSERR_STALE);
254         chat("%s %d %d...", xf->xp->name, offset, count);
255         if(xf->xp->s != xf->xp->parent->s){
256                 count = xfauthread(xf, offset, readptr, count);
257         }else{
258                 if(xfopen(xf, Oread) < 0)
259                         return error(reply, NFSERR_PERM);
260                 if(count > 8192)
261                         count = 8192;
262                 s = xf->xp->s;
263                 setfid(s, xf->opfid);
264                 xf->opfid->tstale = nfstime + 60;
265                 s->f.offset = offset;
266                 s->f.count = count;
267                 if(xmesg(s, Tread) < 0)
268                         return error(reply, NFSERR_IO);
269                 count = s->f.count;
270                 memmove(readptr, s->f.data, count);
271         }
272         if(xfstat(xf, &dir) < 0)
273                 return error(reply, NFSERR_IO);
274         PLONG(NFS_OK);
275         dataptr += dir2fattr(cmd->up, &dir, dataptr);
276         PLONG(count);
277         dataptr += ROUNDUP(count);
278         chat("%d OK\n", count);
279         return dataptr - (uchar *)reply->results;
280 }
281
282 static int
283 nfswritecache(int n, Rpccall *cmd, Rpccall *reply)
284 {
285         USED(n, reply);
286         chat("writecache...");
287         showauth(&cmd->cred);
288         chat("OK\n");
289         return 0;
290 }
291
292 static int
293 nfswrite(int n, Rpccall *cmd, Rpccall *reply)
294 {
295         Session *s;
296         Xfid *xf;
297         Dir dir;
298         int offset, count;
299         uchar *argptr = cmd->args;
300         uchar *dataptr = reply->results;
301
302         chat("write...");
303         if(n < FHSIZE+16)
304                 return garbage(reply, "count too small");
305         xf = rpc2xfid(cmd, 0);
306         argptr += FHSIZE + 4;
307         offset = GLONG();
308         argptr += 4;
309         count = GLONG();
310         if(xf == 0)
311                 return error(reply, NFSERR_STALE);
312         chat("%s %d %d...", xf->xp->name, offset, count);
313         if(xf->xp->s != xf->xp->parent->s){
314                 if(xfauthwrite(xf, offset, argptr, count) < 0)
315                         return error(reply, NFSERR_IO);
316         }else{
317                 if(xfopen(xf, Owrite) < 0)
318                         return error(reply, NFSERR_PERM);
319                 s = xf->xp->s;
320                 setfid(s, xf->opfid);
321                 xf->opfid->tstale = nfstime + 60;
322                 s->f.offset = offset;
323                 s->f.count = count;
324                 s->f.data = (char *)argptr;
325                 if(xmesg(s, Twrite) < 0)
326                         return error(reply, NFSERR_IO);
327         }
328         if(xfstat(xf, &dir) < 0)
329                 return error(reply, NFSERR_IO);
330         PLONG(NFS_OK);
331         dataptr += dir2fattr(cmd->up, &dir, dataptr);
332         chat("OK\n");
333         return dataptr - (uchar *)reply->results;
334 }
335
336 static int
337 creat(int n, Rpccall *cmd, Rpccall *reply, int chdir)
338 {
339         Xfid *xf, *newxf;
340         Xfile *xp;
341         String elem;
342         Dir dir; Sattr sattr;
343         uchar *argptr = cmd->args;
344         uchar *dataptr = reply->results;
345         int trunced;
346
347         if(n <= FHSIZE)
348                 return garbage(reply, "count too small");
349         xf = rpc2xfid(cmd, 0);
350         argptr += FHSIZE;
351         argptr += string2S(argptr, &elem);
352         argptr += convM2sattr(argptr, &sattr);
353         if(argptr != &((uchar *)cmd->args)[n])
354                 return garbage(reply, "bad count");
355         if(xf == 0)
356                 return error(reply, NFSERR_STALE);
357         xp = xf->xp;
358         if(!(xp->qid.type & QTDIR))
359                 return error(reply, NFSERR_NOTDIR);
360         chat("%s/%.*s...", xp->name, utfnlen(elem.s, elem.n), elem.s);
361         trunced = 0;
362         if(xp->parent == xp && elem.s[0] == '#'){
363                 newxf = xfauth(xp, &elem);
364                 if(newxf == 0)
365                         return error(reply, NFSERR_PERM);
366                 if(xfauthremove(newxf, cmd->user) < 0)
367                         return error(reply, NFSERR_PERM);
368                 trunced = 1;
369         }else
370                 newxf = xfwalkcr(Twalk, xf, &elem, 0);
371         if(newxf == 0){
372                 newxf = xfwalkcr(Tcreate, xf, &elem, chdir|(sattr.mode&0777));
373                 if(newxf)
374                         trunced = 1;
375                 else
376                         newxf = xfwalkcr(Twalk, xf, &elem, 0);
377         }
378         if(newxf == 0)
379                 return error(reply, NFSERR_PERM);
380         if(!trunced && chdir)
381                 return error(reply, NFSERR_EXIST);
382         if(!trunced && xfopen(newxf, Trunc|Oread|Owrite) < 0)
383                 return error(reply, NFSERR_PERM);
384         if(xfstat(newxf, &dir) < 0)
385                 return error(reply, NFSERR_IO);
386
387         PLONG(NFS_OK);
388         dataptr += xp2fhandle(newxf->xp, dataptr);
389         dataptr += dir2fattr(cmd->up, &dir, dataptr);
390         chat("OK\n");
391         return dataptr - (uchar *)reply->results;
392 }
393
394 static int
395 nfscreate(int n, Rpccall *cmd, Rpccall *reply)
396 {
397         chat("create...");
398         return creat(n, cmd, reply, 0);
399 }
400
401 static int
402 remov(int n, Rpccall *cmd, Rpccall *reply)
403 {
404         Session *s;
405         Xfile *xp;
406         Xfid *xf, *newxf;
407         String elem;
408         Fid *nfid;
409         uchar *argptr = cmd->args;
410         uchar *dataptr = reply->results;
411
412         if(n <= FHSIZE)
413                 return garbage(reply, "count too small");
414         xf = rpc2xfid(cmd, 0);
415         argptr += FHSIZE;
416         argptr += string2S(argptr, &elem);
417         if(argptr != &((uchar *)cmd->args)[n])
418                 return garbage(reply, "bad count");
419         if(xf == 0)
420                 return error(reply, NFSERR_STALE);
421         xp = xf->xp;
422         if(!(xp->qid.type & QTDIR))
423                 return error(reply, NFSERR_NOTDIR);
424         chat("%s/%.*s...", xp->name, utfnlen(elem.s, elem.n), elem.s);
425         if(xp->s->noauth == 0 && xp->parent == xp && elem.s[0] == '#')
426                 return error(reply, NFSERR_PERM);
427         newxf = xfwalkcr(Twalk, xf, &elem, 0);
428         if(newxf == 0)
429                 return error(reply, NFSERR_NOENT);
430         s = xp->s;
431         nfid = newfid(s);
432         setfid(s, newxf->urfid);
433         s->f.newfid = nfid - s->fids;
434         s->f.nwname = 0;
435         if(xmesg(s, Twalk) < 0){
436                 putfid(s, nfid);
437                 return error(reply, NFSERR_IO);
438         }
439         s->f.fid = nfid - s->fids;
440         if(xmesg(s, Tremove) < 0){
441                 putfid(s, nfid);
442                 return error(reply, NFSERR_PERM);
443         }
444         putfid(s, nfid);
445         xpclear(newxf->xp);
446         PLONG(NFS_OK);
447         chat("OK\n");
448         return dataptr - (uchar *)reply->results;
449 }
450
451 static int
452 nfsremove(int n, Rpccall *cmd, Rpccall *reply)
453 {
454         chat("remove...");
455         return remov(n, cmd, reply);
456 }
457
458 static int
459 nfsrename(int n, Rpccall *cmd, Rpccall *reply)
460 {
461         Xfid *xf, *newxf;
462         Xfile *xp;
463         uchar *fromdir, *todir;
464         String fromelem, toelem;
465         Dir dir;
466         uchar *argptr = cmd->args;
467         uchar *dataptr = reply->results;
468
469         chat("rename...");
470         if(n <= FHSIZE)
471                 return garbage(reply, "count too small");
472         xf = rpc2xfid(cmd, 0);
473         fromdir = argptr;
474         argptr += FHSIZE;
475         argptr += string2S(argptr, &fromelem);
476         todir = argptr;
477         argptr += FHSIZE;
478         argptr += string2S(argptr, &toelem);
479         if(argptr != &((uchar *)cmd->args)[n])
480                 return garbage(reply, "bad count");
481         if(xf == 0)
482                 return error(reply, NFSERR_STALE);
483         xp = xf->xp;
484         if(!(xp->qid.type & QTDIR))
485                 return error(reply, NFSERR_NOTDIR);
486         if(memcmp(fromdir, todir, FHSIZE) != 0)
487                 return error(reply, NFSERR_NXIO);
488         newxf = xfwalkcr(Twalk, xf, &fromelem, 0);
489         if(newxf == 0)
490                 return error(reply, NFSERR_NOENT);
491         if(xfstat(newxf, &dir) < 0)
492                 return error(reply, NFSERR_IO);
493
494         if(xp->parent == xp && toelem.s[0] == '#')
495                 return error(reply, NFSERR_PERM);
496         nulldir(&dir);
497         dir.name = toelem.s;
498         if(xfwstat(newxf, &dir) < 0)
499                 return error(reply, NFSERR_PERM);
500         PLONG(NFS_OK);
501         chat("OK\n");
502         return dataptr - (uchar *)reply->results;
503 }
504
505 static int
506 nfslink(int n, Rpccall *cmd, Rpccall *reply)
507 {
508         USED(n, reply);
509         chat("link...");
510         showauth(&cmd->cred);
511         return error(reply, NFSERR_NOENT);
512 }
513
514 static int
515 nfssymlink(int n, Rpccall *cmd, Rpccall *reply)
516 {
517         USED(n, reply);
518         chat("symlink...");
519         showauth(&cmd->cred);
520         return error(reply, NFSERR_NOENT);
521 }
522
523 static int
524 nfsmkdir(int n, Rpccall *cmd, Rpccall *reply)
525 {
526         chat("mkdir...");
527         return creat(n, cmd, reply, DMDIR);
528 }
529
530 static int
531 nfsrmdir(int n, Rpccall *cmd, Rpccall *reply)
532 {
533         chat("rmdir...");
534         return remov(n, cmd, reply);
535 }
536
537 static int
538 nfsreaddir(int n, Rpccall *cmd, Rpccall *reply)
539 {
540         Session *s;
541         Xfid *xf;
542         Dir dir;
543         char *rdata;
544         int k, offset, count, sfcount, entries, dsize;
545         uchar *argptr = cmd->args;
546         uchar *dataptr = reply->results;
547
548         chat("readdir...");
549         if(n != FHSIZE+8)
550                 return garbage(reply, "bad count");
551         xf = rpc2xfid(cmd, 0);
552         argptr += FHSIZE;
553         offset = GLONG();
554         count = GLONG();
555         if(xf == 0)
556                 return error(reply, NFSERR_STALE);
557         chat("%s (%ld) %d %d...", xf->xp->name, xf->offset, offset, count);
558         s = xf->xp->s;
559         if((xf->mode & Open) && xf->offset > offset)
560                 xfclose(xf);
561         if(xfopen(xf, Oread) < 0)
562                 return error(reply, NFSERR_PERM);
563         while(xf->offset < offset){     /* if we reopened, xf->offset will be zero */
564                 sfcount = offset - xf->offset;
565                 if(sfcount > messagesize-IOHDRSZ)
566                         sfcount = messagesize-IOHDRSZ;
567                 setfid(s, xf->opfid);
568                 s->f.offset = xf->offset;
569                 s->f.count = sfcount;
570                 if(xmesg(s, Tread) < 0){
571                         xfclose(xf);
572                         return error(reply, NFSERR_IO);
573                 }
574                 if(s->f.count <= BIT16SZ)
575                         break;
576                 xf->offset += s->f.count;
577         }
578         if(count > messagesize-IOHDRSZ)
579                 count = messagesize-IOHDRSZ;
580         PLONG(NFS_OK);
581         entries = 0;
582         while(count > 16){      /* at least 16 bytes required; we don't know size of name */
583 chat("top of loop\n");
584                 setfid(s, xf->opfid);
585                 s->f.offset = xf->offset;
586                 s->f.count = count;     /* as good a guess as any */
587                 if(xmesg(s, Tread) < 0){
588                         xfclose(xf);
589                         return error(reply, NFSERR_IO);
590                 }
591                 sfcount = s->f.count;
592                 if(sfcount <= BIT16SZ)
593                         break;
594                 xf->offset += sfcount;
595 chat("count %d data 0x%p\n", s->f.count, s->f.data);
596                 rdata = s->f.data;
597                 /* now have a buffer of Plan 9 directories; unpack into NFS thingies */
598                 while(sfcount >= 0){
599                         dsize = convM2D((uchar*)rdata, sfcount, &dir, (char*)s->statbuf);
600                         if(dsize <= BIT16SZ){
601                                 count = 0;      /* force break from outer loop */
602                                 break;
603                         }
604                         offset += dsize;
605                         k = strlen(dir.name);
606                         if(count < 16+ROUNDUP(k)){
607                                 count = 0;      /* force break from outer loop */
608                                 break;
609                         }
610                         PLONG(TRUE);
611                         PLONG(dir.qid.path);
612                         PLONG(k);
613                         PPTR(dir.name, k);
614                         PLONG(offset);
615                         count -= 16+ROUNDUP(k);
616                         rdata += dsize;
617                         sfcount -= dsize;
618                 }
619         }
620         PLONG(FALSE);
621         if(s->f.count <= 0){
622                 xfclose(xf);
623                 chat("eof...");
624                 PLONG(TRUE);
625         }else
626                 PLONG(FALSE);
627         chat("%d OK\n", entries);
628         return dataptr - (uchar *)reply->results;
629 }
630
631 static int
632 nfsstatfs(int n, Rpccall *cmd, Rpccall *reply)
633 {
634         uchar *dataptr = reply->results;
635         enum {
636                 Xfersize = 2048,
637                 Maxlong = (long)((1ULL<<31) - 1),
638                 Maxfreeblks = Maxlong / Xfersize,
639         };
640
641         chat("statfs...");
642         showauth(&cmd->cred);
643         if(n != FHSIZE)
644                 return garbage(reply, "bad count");
645         PLONG(NFS_OK);
646         PLONG(4096);            /* tsize (fs block size) */
647         PLONG(Xfersize);        /* bsize (optimal transfer size) */
648         PLONG(Maxfreeblks);     /* blocks in fs */
649         PLONG(Maxfreeblks);     /* bfree to root*/
650         PLONG(Maxfreeblks);     /* bavail (free to mortals) */
651         chat("OK\n");
652         /*conftime = 0;
653         readunixidmaps(config);*/
654         return dataptr - (uchar *)reply->results;
655 }