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