]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ratfs/proto.c
ndb/dnsquery, ndb/csquery: handle long lines
[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
306         fidp = newfid(f->fid);
307         np = fidp->node;
308         if((np->d.mode&DMDIR) == 0){
309                 reply(f, "not a directory");
310                 return;
311         }
312
313         if(!permitted(fidp, np, AWRITE)) {
314                 reply(f, "permission denied");
315                 return;
316         }
317
318         /* Ignore the supplied mode and force it to be non-writable */
319
320         np = newnode(np, f->name, Trustedtemp, 0444, trustedqid++);
321         if(trustedqid >= Qaddrfile)                     /* wrap QIDs */
322                 trustedqid = Qtrustedfile;
323         cidrparse(&np->ip, f->name);
324         f->qid = np->d.qid;
325         np->d.uid = fidp->uid;
326         np->d.gid = np->d.uid;
327         np->d.muid = np->d.muid;
328         fidp->node = np;
329         fidp->open = 1;
330         reply(f, 0);
331         return;
332 }
333
334 /*
335  *      only directories can be read.  everthing else returns EOF.
336  */
337 static void
338 rread(Fcall *f)
339 {
340         long cnt;
341         Fid *fidp;
342
343         cnt = f->count;
344         f->count = 0;
345         fidp = newfid(f->fid);
346         f->data = (char*)rbuf+IOHDRSZ;
347         if(fidp->open == 0) {
348                 reply(f, "file not open");
349                 return;
350         }
351         if ((fidp->node->d.mode&DMDIR) == 0){
352                 reply(f, 0);                            /*EOF*/
353                 return;
354         }
355         if(cnt > MAXRPC)
356                 cnt = MAXRPC;
357
358         if(f->offset == 0)
359                 fidp->dirindex = 0;
360
361         switch(fidp->node->d.type) {
362         case Directory:
363         case Addrdir:
364         case Trusted:
365                 f->count = dread(fidp, cnt);
366                 break;
367         case IPaddr:
368         case Acctaddr:
369                 f->count = hread(fidp, cnt);
370                 break;
371         default:
372                 reply(f, "can't read this type of file");
373                 return;
374         }
375         reply(f, 0);
376 }
377
378
379 /*
380  *      only the 'ctl' file in the top level directory is writable
381  */
382
383 static void
384 rwrite(Fcall *f)
385 {
386         Fid *fidp;
387         int n;
388         char *err, *argv[10];
389
390         fidp = newfid(f->fid);
391         if(fidp->node->d.mode & DMDIR){
392                 reply(f, "directories are not writable");
393                 return;
394         }
395         if(fidp->open == 0) {
396                 reply(f, "file not open");
397                 return;
398         }
399
400         if (!permitted(fidp, fidp->node, AWRITE)) {
401                 reply(f, "permission denied");
402                 return;
403         }
404
405         f->data[f->count] = 0;                  /* the extra byte in rbuf leaves room */
406         n = tokenize(f->data, argv, 10);
407         err = 0;
408         switch(findkey(argv[0], cmds)){
409         case RELOAD:
410                 getconf();
411                 reload();
412                 break;
413         case RDEBUG:
414                 if(n > 1){
415                         debugfd = create(argv[1], OWRITE, 0666);
416                         if(debugfd < 0)
417                                 err = "create failed";
418                 } else
419                         debugfd = 2;
420                 break;
421         case RNODEBUG:
422                 if(debugfd >= 0)
423                         close(debugfd);
424                 debugfd = -1;
425                 break;
426         default:
427                 err = "unknown command";
428                 break;
429         }
430         reply(f, err);
431 }
432
433 static void
434 rclunk(Fcall *f)
435 {
436         Fid *fidp;
437
438         fidp = newfid(f->fid);
439         fidp->open = 0;
440         fidp->busy = 0;
441         fidp->node = 0;
442         fidp->name = 0;
443         fidp->uid = 0;
444         reply(f, 0);
445 }
446
447 /*
448  *  no files or directories are removable; this becomes clunk;
449  */
450 static void
451 rremove(Fcall *f)
452 {
453         Fid *fidp;
454         Node *dir, *np;
455
456         fidp = newfid(f->fid);
457         
458         /*
459          * only trusted temporary files can be removed
460          * and only by their owner.
461          */
462         if(fidp->node->d.type != Trustedtemp){
463                 reply(f, "can't be removed");
464                 return;
465         }
466         if(fidp->uid != fidp->node->d.uid){
467                 reply(f, "permission denied");
468                 return;
469         }
470         dir = fidp->node->parent;
471         for(np = dir->children; np; np = np->sibs)
472                 if(np->sibs == fidp->node)
473                         break;
474         if(np)
475                 np->sibs = fidp->node->sibs;
476         else
477                 dir->children = fidp->node->sibs;
478         dir->count--;
479         free(fidp->node);
480         fidp->node = 0;
481         fidp->open = 0;
482         fidp->busy = 0;
483         fidp->name = 0;
484         fidp->uid = 0;
485         reply(f, 0);
486 }
487
488 static void
489 rstat(Fcall *f)
490 {
491         Fid *fidp;
492
493         fidp = newfid(f->fid);
494         if (fidp->node->d.type == Dummynode)
495                 dummy.d.name = fidp->name;
496         f->stat = (uchar*)rbuf+4+1+2+2; /* knows about stat(5) */
497         f->nstat = convD2M(&fidp->node->d, f->stat, MAXRPC);
498         if(f->nstat <= BIT16SZ)
499                 reply(f, "ratfs: convD2M");
500         else
501                 reply(f, 0);
502         return;
503 }
504
505 static void
506 rwstat(Fcall *f)
507 {
508         reply(f, "wstat not implemented");
509 }
510