]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ratfs/proto.c
merge
[plan9front.git] / sys / src / cmd / ratfs / proto.c
1 #include "ratfs.h"
2
3 /*
4  *      9P protocol interface
5  */
6
7 enum {
8         RELOAD = 0,             /* commands written to ctl file */
9         RDEBUG,
10         RNODEBUG,
11         RNONE,
12 };
13
14 static void     rflush(Fcall*),
15                 rauth(Fcall*),  rattach(Fcall*),
16                 rclone(Fcall*),         rwalk(Fcall*),
17                 ropen(Fcall*),
18                 rcreate(Fcall*),        rread(Fcall*),
19                 rwrite(Fcall*),         rclunk(Fcall*),
20                 rremove(Fcall*),        rstat(Fcall*),
21                 rwstat(Fcall*), rversion(Fcall*);
22
23 static  Fid*    newfid(int);
24 static  void    reply(Fcall*, char*);
25
26 static  void    (*fcalls[])(Fcall*) = {
27         [Tversion]      rversion,
28         [Tflush]        rflush,
29         [Tauth] rauth,
30         [Tattach]       rattach,
31         [Twalk]         rwalk,
32         [Topen]         ropen,
33         [Tcreate]       rcreate,
34         [Tread]         rread,
35         [Twrite]        rwrite,
36         [Tclunk]        rclunk,
37         [Tremove]       rremove,
38         [Tstat]         rstat,
39         [Twstat]        rwstat,
40 };
41
42
43 static  Keyword cmds[] = {
44         "reload",               RELOAD,
45         "debug",                RDEBUG,
46         "nodebug",              RNODEBUG,
47         0,                      RNONE,
48 };
49
50 /*
51  *      Main protocol loop
52  */
53 void
54 io(void)
55 {
56         Fcall   rhdr;
57         int n;
58         
59         while((n = read9pmsg(srvfd, rbuf, sizeof rbuf-1)) != 0){
60                 if(n < 0)
61                         fatal("mount read: %r");
62                 if(convM2S(rbuf, n, &rhdr) != n){
63                         if(debugfd >= 0)
64                                 fprint(2, "%s: malformed message\n", argv0);
65                         fatal("convM2S format error: %r");
66                 }
67                 if(debugfd >= 0)
68                         fprint(debugfd, "<-%F\n", &rhdr);/**/
69
70                 if(!fcalls[rhdr.type])
71                         reply(&rhdr, "bad fcall type");
72                 else
73                         (*fcalls[rhdr.type])(&rhdr);
74         }
75 }
76
77 /*
78  *      write a protocol reply to the client
79  */
80 static void
81 reply(Fcall *r, char *error)
82 {
83         int n;
84
85         if(error == nil)
86                 r->type++;
87         else {
88                 r->type = Rerror;
89                 r->ename = error;
90         }
91         if(debugfd >= 0)
92                 fprint(debugfd, "->%F\n", r);/**/
93         n = convS2M(r, rbuf, sizeof rbuf);
94         if(n == 0)
95                 fatal("convS2M: %r");
96         if(write(srvfd, rbuf, n) < 0)
97                 fatal("reply: %r");
98 }
99
100
101 /*
102  *  lookup a fid. if not found, create a new one.
103  */
104
105 static Fid*
106 newfid(int fid)
107 {
108         Fid *f, *ff;
109
110         static Fid *fids;
111
112         ff = 0;
113         for(f = fids; f; f = f->next){
114                 if(f->fid == fid){
115                         if(!f->busy)
116                                 f->node = 0;
117                         return f;
118                 } else if(!ff && !f->busy)
119                         ff = f;
120         }
121         if(ff == 0){
122                 ff = mallocz(sizeof(*f), 1);
123                 ff->next = fids;
124                 fids = ff;
125         }
126         ff->node = 0;
127         ff->fid = fid;
128         return ff;
129 }
130
131 static void
132 rversion(Fcall *f)
133 {
134         f->version = "9P2000";
135         if(f->msize > MAXRPC)
136                 f->msize = MAXRPC;
137         reply(f, 0);
138 }
139
140 static void
141 rauth(Fcall *f)
142 {
143         reply(f, "ratfs: authentication not required");
144 }
145
146 static void
147 rflush(Fcall *f)
148 {
149         reply(f, 0);
150 }
151
152 static void
153 rattach(Fcall *f)
154 {
155         Fid *fidp;
156         Dir *d;
157
158         if((d=dirstat(conffile)) != nil && d->mtime > lastconftime)
159                 getconf();
160         free(d);
161         if((d=dirstat(ctlfile)) != nil && d->mtime > lastctltime)
162                 reload();
163         free(d);
164         cleantrusted();
165
166         fidp = newfid(f->fid);
167         fidp->busy = 1;
168         fidp->node = root;
169         fidp->name = root->d.name;
170         fidp->uid = atom(f->uname);
171         f->qid = root->d.qid;
172         reply(f,0);
173 }
174
175 static void
176 rclone(Fcall *f)
177 {
178         Fid *fidp, *nf;
179
180         fidp = newfid(f->fid);
181         if(fidp->node && fidp->node->d.type == Dummynode){
182                 reply(f, "can't clone an address");
183                 return;
184         }
185         nf = newfid(f->newfid);
186         nf->busy = 1;
187         nf->node = fidp->node;
188         nf->uid = fidp->uid;
189         nf->name = fidp->name;
190         if(debugfd >= 0)
191                 printfid(nf);
192         reply(f,0);
193 }
194
195 static void
196 rwalk(Fcall *f)
197 {
198         int i, j;
199         Fcall r;
200         Fid *fidp, *nf;
201         char *err;
202
203         fidp = newfid(f->fid);
204         if(fidp->node && fidp->node->d.type == Dummynode){
205                 reply(f, "can't walk an address node");
206                 return;
207         }
208         if(f->fid == f->newfid)
209                 nf = fidp;
210         else{
211                 nf = newfid(f->newfid);
212                 nf->busy = 1;
213                 nf->node = fidp->node;
214                 nf->uid = fidp->uid;
215                 nf->name = fidp->name;
216                 if(debugfd >= 0)
217                         printfid(nf);
218         }
219
220         err = nil;
221         for(i=0; i<f->nwname; i++){
222                 err = walk(f->wname[i], nf);
223                 if(err)
224                         break;
225                 r.wqid[i] = nf->node->d.qid;
226         }
227         
228
229         if(i < f->nwname && f->fid != f->newfid){
230                 nf->busy = 0;
231                 nf->node = 0;
232                 nf->name = 0;
233                 nf->uid = 0;
234         }
235         if(i > 0 && i < f->nwname && f->fid == f->newfid){
236                 /*
237                  * try to put things back;
238                  * we never get this sort of call from the kernel
239                  */
240                 for(j=0; j<i; j++)
241                         walk("..", nf);
242         }
243         memmove(f->wqid, r.wqid, sizeof f->wqid);
244         f->nwqid = i;
245         if(err && i==0)
246                 reply(f, err);
247         else
248                 reply(f, 0);
249 }
250
251 /*
252  *      We don't have to do full permission checking because most files
253  *      have restricted semantics:
254  *              The ctl file is only writable
255  *              All others, including directories, are only readable
256  */
257 static void
258 ropen(Fcall *f)
259 {
260         Fid *fidp;
261         int mode;
262
263         fidp = newfid(f->fid);
264
265         if(debugfd >= 0)
266                 printfid(fidp);
267
268         mode = f->mode&(OREAD|OWRITE|ORDWR);
269         if(fidp->node->d.type == Ctlfile) {
270                 if(mode != OWRITE) {    
271                         reply(f, "permission denied");
272                         return;
273                 }
274         } else
275         if (mode != OREAD) {
276                 reply(f, "permission denied or operation not supported");
277                 return;
278         }
279
280         f->qid = fidp->node->d.qid;
281         fidp->open = 1;
282         reply(f, 0);
283 }
284
285 static int
286 permitted(Fid *fp, Node *np, int mask)
287 {
288         int mode;
289
290         mode = np->d.mode;
291         return (fp->uid==np->d.uid && (mode&(mask<<6)))
292                 || (fp->uid==np->d.gid && (mode&(mask<<3)))
293                 || (mode&mask);
294 }
295
296 /*
297  *      creates are only allowed in the "trusted" subdirectory
298  *      we also assume that the groupid == the uid
299  */
300 static void
301 rcreate(Fcall *f)
302 {
303         Fid *fidp;
304         Node *np;
305         Cidraddr ip;
306
307         fidp = newfid(f->fid);
308         np = fidp->node;
309         if((np->d.mode&DMDIR) == 0){
310                 reply(f, "not a directory");
311                 return;
312         }
313
314         if(!permitted(fidp, np, AWRITE)) {
315                 reply(f, "permission denied");
316                 return;
317         }
318
319         if(cidrparse(&ip, f->name) == -1){
320                 reply(f, "bad cidr in filename");
321                 return;
322         }
323
324         /* Ignore the supplied mode and force it to be non-writable */
325         np = newnode(np, f->name, Trustedtemp, 0444, trustedqid++);
326         np->ip = ip;
327         if(trustedqid >= Qaddrfile)                     /* wrap QIDs */
328                 trustedqid = Qtrustedfile;
329         f->qid = np->d.qid;
330         np->d.uid = fidp->uid;
331         np->d.gid = np->d.uid;
332         np->d.muid = np->d.muid;
333         fidp->node = np;
334         fidp->open = 1;
335         reply(f, 0);
336         return;
337 }
338
339 /*
340  *      only directories can be read.  everthing else returns EOF.
341  */
342 static void
343 rread(Fcall *f)
344 {
345         long cnt;
346         Fid *fidp;
347
348         cnt = f->count;
349         f->count = 0;
350         fidp = newfid(f->fid);
351         f->data = (char*)rbuf+IOHDRSZ;
352         if(fidp->open == 0) {
353                 reply(f, "file not open");
354                 return;
355         }
356         if ((fidp->node->d.mode&DMDIR) == 0){
357                 reply(f, 0);                            /*EOF*/
358                 return;
359         }
360         if(cnt > MAXRPC)
361                 cnt = MAXRPC;
362
363         if(f->offset == 0)
364                 fidp->dirindex = 0;
365
366         switch(fidp->node->d.type) {
367         case Directory:
368         case Addrdir:
369         case Trusted:
370                 f->count = dread(fidp, cnt);
371                 break;
372         case IPaddr:
373         case Acctaddr:
374                 f->count = hread(fidp, cnt);
375                 break;
376         default:
377                 reply(f, "can't read this type of file");
378                 return;
379         }
380         reply(f, 0);
381 }
382
383
384 /*
385  *      only the 'ctl' file in the top level directory is writable
386  */
387
388 static void
389 rwrite(Fcall *f)
390 {
391         Fid *fidp;
392         int n;
393         char *err, *argv[10];
394
395         fidp = newfid(f->fid);
396         if(fidp->node->d.mode & DMDIR){
397                 reply(f, "directories are not writable");
398                 return;
399         }
400         if(fidp->open == 0) {
401                 reply(f, "file not open");
402                 return;
403         }
404
405         if (!permitted(fidp, fidp->node, AWRITE)) {
406                 reply(f, "permission denied");
407                 return;
408         }
409
410         f->data[f->count] = 0;                  /* the extra byte in rbuf leaves room */
411         n = tokenize(f->data, argv, 10);
412         err = 0;
413         switch(findkey(argv[0], cmds)){
414         case RELOAD:
415                 getconf();
416                 reload();
417                 break;
418         case RDEBUG:
419                 if(n > 1){
420                         debugfd = create(argv[1], OWRITE, 0666);
421                         if(debugfd < 0)
422                                 err = "create failed";
423                 } else
424                         debugfd = 2;
425                 break;
426         case RNODEBUG:
427                 if(debugfd >= 0)
428                         close(debugfd);
429                 debugfd = -1;
430                 break;
431         default:
432                 err = "unknown command";
433                 break;
434         }
435         reply(f, err);
436 }
437
438 static void
439 rclunk(Fcall *f)
440 {
441         Fid *fidp;
442
443         fidp = newfid(f->fid);
444         fidp->open = 0;
445         fidp->busy = 0;
446         fidp->node = 0;
447         fidp->name = 0;
448         fidp->uid = 0;
449         reply(f, 0);
450 }
451
452 /*
453  *  no files or directories are removable; this becomes clunk;
454  */
455 static void
456 rremove(Fcall *f)
457 {
458         Fid *fidp;
459         Node *dir, *np;
460
461         fidp = newfid(f->fid);
462         
463         /*
464          * only trusted temporary files can be removed
465          * and only by their owner.
466          */
467         if(fidp->node->d.type != Trustedtemp){
468                 reply(f, "can't be removed");
469                 return;
470         }
471         if(fidp->uid != fidp->node->d.uid){
472                 reply(f, "permission denied");
473                 return;
474         }
475         dir = fidp->node->parent;
476         for(np = dir->children; np; np = np->sibs)
477                 if(np->sibs == fidp->node)
478                         break;
479         if(np)
480                 np->sibs = fidp->node->sibs;
481         else
482                 dir->children = fidp->node->sibs;
483         dir->count--;
484         free(fidp->node);
485         fidp->node = 0;
486         fidp->open = 0;
487         fidp->busy = 0;
488         fidp->name = 0;
489         fidp->uid = 0;
490         reply(f, 0);
491 }
492
493 static void
494 rstat(Fcall *f)
495 {
496         Fid *fidp;
497
498         fidp = newfid(f->fid);
499         if (fidp->node->d.type == Dummynode)
500                 dummy.d.name = fidp->name;
501         f->stat = (uchar*)rbuf+4+1+2+2; /* knows about stat(5) */
502         f->nstat = convD2M(&fidp->node->d, f->stat, MAXRPC);
503         if(f->nstat <= BIT16SZ)
504                 reply(f, "ratfs: convD2M");
505         else
506                 reply(f, 0);
507         return;
508 }
509
510 static void
511 rwstat(Fcall *f)
512 {
513         reply(f, "wstat not implemented");
514 }
515