xref: /plan9-contrib/sys/src/lib9p/srv.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include <thread.h>
6 #include "9p.h"
7 #include "impl.h"
8 
9 enum {
10 	Tclwalk_walk = Tmax,
11 	Tclwalk_clone
12 };
13 
14 static char Enowrite[] = "write prohibited";
15 static char Enowstat[] = "wstat prohibited";
16 static char Enoremove[] = "remove prohibited";
17 static char Enocreate[] = "create prohibited";
18 static char Ebotch[] = "9P protocol botch";
19 static char Ebadattach[] = "unknown specifier in attach";
20 
21 int lib9p_chatty;
22 
23 static void
24 setfcallerror(Fcall *f, char *err)
25 {
26 	strncpy(f->ename, err, sizeof f->ename);
27 	f->ename[sizeof f->name - 1] = 0;
28 	f->type = Rerror;
29 }
30 
31 static void
32 sendreply(Req *r, int fd)
33 {
34 	int n;
35 	char buf[MAXMSG+MAXFDATA];
36 
37 	r->type++;
38 	r->fcall.type = r->type;
39 	if(r->error)
40 		setfcallerror(&r->fcall, r->error);
41 
42 if(lib9p_chatty)
43 	fprint(2, "-> %F\n", &r->fcall);
44 	switch(r->type){
45 	default:
46 		sysfatal("bad type %d in reply", r->fcall.type);
47 		break;
48 
49 	case Rnop:
50 	case Rflush:
51 	case Rattach:
52 	case Rsession:
53 	case Rclone:
54 	case Rwalk:
55 	case Rclwalk:
56 	case Ropen:
57 	case Rcreate:
58 	case Rread:
59 	case Rwrite:
60 	case Rclunk:
61 	case Rremove:
62 	case Rstat:
63 	case Rwstat:
64 	case Rerror:
65 		n = convS2M(&r->fcall, buf);
66 		write(fd, buf, n);
67 
68 	}
69 }
70 
71 static char Eduptag[] = "duplicate tag";
72 static char Edupfid[] = "duplicate fid";
73 static char Eunknownfid[] = "unknown fid";
74 static char Eperm[] = "permission denied";
75 static char Enotfound[] = "file not found";
76 
77 Req*
78 getreq(Reqpool *rpool, Fidpool *fpool, int fd)
79 {
80 	long n;
81 	char *buf;
82 	Fcall f;
83 	Req *r;
84 
85 	n = MAXMSG+MAXFDATA;
86 	buf = mallocz(n, 1);
87 	if(buf == nil)
88 		return nil;
89 
90 	if(getS(fd, buf, &f, &n) != nil){
91 		free(buf);
92 		return nil;
93 	}
94 	if((r=allocreq(rpool, f.tag)) == nil){	/* duplicate tag: cons up a Req */
95 		r = mallocz(sizeof *r, 1);
96 		if(r == nil){
97 			free(buf);
98 			return nil;
99 		}
100 		incref(&r->ref);
101 		r->tag = f.tag;
102 		r->fcall = f;
103 		r->error = Eduptag;
104 		r->buf = buf;
105 		r->responded = 0;
106 		return r;
107 	}
108 
109 	r->responded = 0;
110 	r->buf = buf;
111 	r->fcall = f;
112 	r->ofcall = f;
113 	r->type = r->fcall.type;
114 	switch(r->type){
115 	case Tnop:
116 	case Tsession:
117 		break;
118 
119 	case Tflush:
120 		r->oldreq = lookupreq(rpool, r->fcall.oldtag);
121 		if(r->oldreq == r)
122 			r->error = "you can't flush yourself";
123 		break;
124 
125 	case Tattach:
126 		if((r->fid = allocfid(fpool, r->fcall.fid)) == nil)
127 			r->error = Edupfid;
128 		break;
129 
130 	case Tclone:
131 	case Tclwalk:
132 		if((r->fid = lookupfid(fpool, r->fcall.fid)) == nil)
133 			r->error = Eunknownfid;
134 		else if(r->fid->omode != -1)
135 			r->error = "cannot clone open fid";
136 		else if((r->newfid = allocfid(fpool, r->fcall.newfid)) == nil)
137 			r->error = Edupfid;
138 		break;
139 
140 	case Twalk:
141 	case Topen:
142 	case Tcreate:
143 	case Tread:
144 	case Twrite:
145 	case Tclunk:
146 	case Tremove:
147 	case Tstat:
148 	case Twstat:
149 		if((r->fid = lookupfid(fpool, r->fcall.fid)) == nil)
150 			r->error = Eunknownfid;
151 		break;
152 	}
153 if(lib9p_chatty)
154 	if(r->error)
155 		fprint(2, "<- %F: %s\n", &r->fcall, r->error);
156 	else
157 		fprint(2, "<- %F\n", &r->fcall);
158 
159 	return r;
160 }
161 
162 
163 static char Ebadcount[] = "bad count";
164 static char Ebadoffset[] = "bad offset";
165 
166 void
167 srv(Srv *srv, int fd)
168 {
169 	int o, p;
170 	Req *r;
171 	char buf[MAXFDATA];
172 	File *f, *of;
173 
174 	fmtinstall('D', dirconv);
175 	fmtinstall('F', fcallconv);
176 
177 	if(srv->fpool == nil)
178 		srv->fpool = allocfidpool();
179 	if(srv->rpool == nil)
180 		srv->rpool = allocreqpool();
181 
182 	srv->rpool->srv = srv;
183 	srv->fd = fd;
184 
185 	while(r = getreq(srv->rpool, srv->fpool, fd)){
186 		if(r->error){
187 			respond(r, r->error);
188 			continue;
189 		}
190 
191 		switch(r->type){
192 		default:
193 			abort();
194 
195 		case Tnop:
196 			respond(r, nil);
197 			break;
198 
199 		case Tsession:
200 			memset(r->fcall.authid, 0, sizeof r->fcall.authid);
201 			memset(r->fcall.authdom, 0, sizeof r->fcall.authdom);
202 			memset(r->fcall.chal, 0, sizeof r->fcall.chal);
203 			if(srv->session)
204 				srv->session(r, r->fcall.authid,
205 					r->fcall.authdom, r->fcall.chal);
206 			else
207 				respond(r, nil);
208 			break;
209 
210 		case Tflush:
211 			if(r->oldreq && srv->flush)
212 				srv->flush(r, r->oldreq);
213 			else
214 				respond(r, nil);
215 			break;
216 
217 		case Tattach:
218 			strncpy(r->fid->uid, r->ofcall.uname, NAMELEN);
219 			r->fid->uid[NAMELEN-1] = '\0';
220 			if(srv->attach)
221 				srv->attach(r, r->fid, r->ofcall.aname, &r->fcall.qid);
222 			else{		/* assume one tree, no auth */
223 				if(strcmp(r->ofcall.aname, "") != 0)
224 					r->error = Ebadattach;
225 				else{
226 					r->fid->file = srv->tree->root;
227 					r->fcall.qid = r->fid->file->qid;
228 				}
229 				respond(r, nil);
230 			}
231 			break;
232 
233 		case Tclwalk:
234 			r->type = Tclwalk_clone;
235 			/* fall through */
236 		case Tclone:
237 			if(r->fid->file)
238 				incref(&r->fid->file->ref);
239 			r->newfid->file = r->fid->file;
240 
241 			strcpy(r->newfid->uid, r->fid->uid);
242 			r->newfid->qid = r->fid->qid;
243 			r->newfid->aux = r->fid->aux;
244 
245 			if(srv->clone)
246 				srv->clone(r, r->fid, r->newfid);
247 			else
248 				respond(r, nil);
249 			break;
250 
251 		case Twalk:
252 			/* if you change this, change the copy in respond Tclwalk_clone above */
253 
254 			if(r->fid->file){
255 				/*
256 				 * We don't use the walk function if using file
257 				 * trees.  It's too much to expect clients to get
258 				 * the reference counts right on error or when
259 				 * playing other funny games with the tree.
260 				 */
261 				if(f = fwalk(r->fid->file, r->ofcall.name)) {
262 					of = r->fid->file;
263 					r->fid->file = f;
264 					fclose(of);
265 					r->fcall.qid = f->qid;
266 					respond(r, nil);
267 				}else
268 					respond(r, Enotfound);
269 				break;
270 			}
271 			r->fcall.qid = r->fid->qid;
272 			srv->walk(r, r->fid, r->ofcall.name, &r->fcall.qid);
273 			break;
274 
275 		case Topen:
276 			if(r->fid->omode != -1){
277 				respond(r, Ebotch);
278 				break;
279 			}
280 			if(r->fid->file){
281 				switch(r->fcall.mode&3){
282 				case OREAD:	p = AREAD; break;
283 				case OWRITE:	p = AWRITE; break;
284 				case ORDWR:	p = AREAD|AWRITE; break;
285 				case OEXEC:	p = AEXEC; break;
286 				default:	p = ~0; assert(0);
287 				}
288 				if(r->fcall.mode & OTRUNC)
289 					p |= AWRITE;
290 				if(!hasperm(r->fid->file, r->fid->uid, p)){
291 					respond(r, Eperm);
292 					break;
293 				}
294 				if((r->fcall.mode & ORCLOSE)
295 				&& !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
296 					respond(r, Eperm);
297 					break;
298 				}
299 				r->fcall.qid = r->fid->file->qid;
300 			}
301 			if(srv->open)
302 				srv->open(r, r->fid, r->fcall.mode, &r->fcall.qid);
303 			else
304 				respond(r, nil);
305 			break;
306 
307 		case Tcreate:
308 			if(r->fid->omode != -1)
309 				r->error = Ebotch;
310 			else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE))
311 				r->error = Eperm;
312 			else if(srv->create == nil)
313 				r->error = Enocreate;
314 
315 			if(r->error)
316 				respond(r, r->error);
317 			else
318 				srv->create(r, r->fid, r->ofcall.name, r->ofcall.mode,
319 					r->ofcall.perm, &r->fcall.qid);
320 			break;
321 
322 		case Tread:
323 			r->fcall.data = buf;
324 			o = r->fid->omode & OMASK;
325 			if(r->fcall.count > MAXFDATA)
326 				r->fcall.count = MAXFDATA;
327 			if(o != OREAD && o != ORDWR && o != OEXEC)
328 				r->error = Ebotch;
329 			else if(r->fcall.count < 0)
330 				r->error = Ebadcount;
331 			else if(r->fcall.offset < 0
332 				|| ((r->fid->qid.path&CHDIR) && r->fcall.offset%DIRLEN))
333 				r->error = Ebadoffset;
334 			else if((r->fid->qid.path&CHDIR) && r->fid->file){
335 				r->error = fdirread(r->fid->file, buf,
336 							&r->fcall.count, r->fcall.offset);
337 			}else{
338 				srv->read(r, r->fid, buf, &r->fcall.count, r->fcall.offset);
339 				break;
340 			}
341 			respond(r, r->error);
342 			break;
343 
344 		case Twrite:
345 			o = r->fid->omode & OMASK;
346 			if(o != OWRITE && o != ORDWR)
347 				r->error = Ebotch;
348 			else if(srv->write == nil)
349 				r->error = Enowrite;
350 			else{
351 				srv->write(r, r->fid, r->fcall.data,
352 					&r->fcall.count, r->fcall.offset);
353 				break;
354 			}
355 			respond(r, r->error);
356 			break;
357 
358 		case Tclunk:
359 			if(srv->clunkaux)
360 				srv->clunkaux(r->fid);
361 			respond(r, nil);
362 			break;
363 
364 		case Tremove:
365 			if(r->fid->file
366 			&& !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
367 				respond(r, Eperm);
368 				break;
369 			}
370 			if(srv->remove == nil){
371 				if(r->fid->file)
372 					respond(r, nil);
373 				else
374 					respond(r, Enoremove);
375 				break;
376 			}
377 			srv->remove(r, r->fid);
378 			break;
379 
380 		case Tstat:
381 			if(r->fid->file)
382 				r->d = r->fid->file->Dir;
383 			else if(srv->stat == nil){
384 				respond(r, "no stat");
385 				break;
386 			}
387 
388 			if(srv->stat)
389 				srv->stat(r, r->fid, &r->d);
390 			else
391 				respond(r, nil);
392 			break;
393 
394 		case Twstat:
395 			if(srv->wstat == nil)
396 				respond(r, nil);
397 			else{
398 				convM2D(r->fcall.stat, &r->d);
399 				srv->wstat(r, r->fid, &r->d);
400 			}
401 			break;
402 		}
403 	}
404 }
405 
406 void
407 respond(Req *r, char *error)
408 {
409 	File *f, *of;
410 	Srv *srv;
411 
412 	srv = r->pool->srv;
413 	assert(r->responded == 0);
414 	r->responded = 1;
415 
416 	switch(r->type){
417 	default:
418 		abort();
419 
420 	case Tnop:
421 		break;
422 
423 	case Tflush:
424 		freereq(r->oldreq);
425 		r->oldreq = nil;
426 		break;
427 
428 	case Tattach:
429 		if(error)
430 			freefid(r->fid);
431 		else {
432 			r->fid->qid = r->fcall.qid;
433 			closefid(r->fid);
434 		}
435 		r->fid = nil;
436 		break;
437 
438 	case Tsession:
439 		break;
440 
441 	case Tclone:
442 		closefid(r->fid);
443 		r->fid = nil;
444 		if(error)
445 			freefid(r->newfid);
446 		else
447 			closefid(r->newfid);
448 		r->newfid = nil;
449 		break;
450 
451 	case Tclwalk:	/* can't happen */
452 		abort();
453 
454 	/*
455 	 * In order to use the clone and walk functions rather
456 	 * than require a clwalk, the response here is an error
457 	 * if there was one, but otherwise we call the walk
458 	 * function, who will eventually call us again (next case).
459 	 */
460 	case Tclwalk_clone:
461 		closefid(r->fid);
462 		r->fid = nil;
463 		if(error){
464 			freefid(r->newfid);
465 			r->newfid = nil;
466 			break;
467 		}
468 
469 		r->fid = r->newfid;
470 		r->newfid = nil;
471 		r->type = Tclwalk_walk;
472 
473 		/* copy of case Twalk in srv below */
474 		if(r->fid->file){
475 			/*
476 			 * We don't use the walk function if using file
477 			 * trees.  It's too much to expect clients to get
478 			 * the reference counts right on error or when
479 			 * playing other funny games with the tree.
480 			 */
481 			if(f = fwalk(r->fid->file, r->fcall.name)) {
482 				of = r->fid->file;
483 				r->fid->file = f;
484 				fclose(of);
485 				r->fid->qid = f->qid;
486 				r->fcall.qid = r->fid->qid;
487 				respond(r, nil);
488 			}else
489 				respond(r, Enotfound);
490 			break;
491 		}
492 		r->fcall.qid = r->fid->qid;
493 		srv->walk(r, r->fid, r->ofcall.name, &r->fcall.qid);
494 		break;
495 
496 	case Tclwalk_walk:
497 		r->type = Tclwalk;
498 		if(error)
499 			freefid(r->fid);
500 		else{
501 			r->fid->qid = r->fcall.qid;
502 			closefid(r->fid);
503 		}
504 		r->fid = nil;
505 		break;
506 
507 	case Twalk:
508 		if(error == nil)
509 			r->fid->qid = r->fcall.qid;
510 		closefid(r->fid);
511 		r->fid = nil;
512 		break;
513 
514 	case Topen:
515 		if(error == nil){
516 			r->fid->omode = r->fcall.mode;
517 			r->fid->qid = r->fcall.qid;
518 		}
519 		closefid(r->fid);
520 		r->fid = nil;
521 		break;
522 
523 	case Tcreate:
524 		if(error == nil){
525 			r->fid->omode = r->fcall.mode;
526 			r->fid->qid = r->fcall.qid;
527 		}
528 		closefid(r->fid);
529 		r->fid = nil;
530 		break;
531 
532 	case Tread:
533 		closefid(r->fid);
534 		r->fid = nil;
535 		break;
536 
537 	case Twrite:
538 		if(r->fid->file)
539 			r->fid->file->qid.vers++;
540 		closefid(r->fid);
541 		r->fid = nil;
542 		break;
543 
544 	case Tclunk:
545 		freefid(r->fid);
546 		r->fid = nil;
547 		break;
548 
549 	case Tremove:
550 		if(error == nil) {
551 			if(r->fid->file) {
552 				fremove(r->fid->file);
553 				r->fid->file = nil;
554 			}
555 		}
556 		if(srv->clunkaux)
557 			srv->clunkaux(r->fid);
558 		freefid(r->fid);
559 		r->fid = nil;
560 		break;
561 
562 	case Tstat:
563 		if(error == nil)
564 			convD2M(&r->d, r->fcall.stat);
565 		closefid(r->fid);
566 		r->fid = nil;
567 		break;
568 
569 	case Twstat:
570 		closefid(r->fid);
571 		r->fid = nil;
572 		break;
573 	}
574 
575 	assert(r->type != Tclwalk_clone);
576 
577 	if(r->type == Tclwalk_walk)
578 		return;
579 
580 	r->error = error;
581 	sendreply(r, r->pool->srv->fd);
582 	freereq(r);
583 }
584 
585 
586 /*
587  *  read a message from fd and convert it.
588  *  ignore 0-length messages.
589  */
590 char *
591 getS(int fd, char *buf, Fcall *f, long *lp)
592 {
593 	long m, n;
594 	int i;
595 	char *errstr;
596 
597 	errstr = "EOF";
598 	n = 0;
599 	for(i = 0; i < 3; i++){
600 		n = read(fd, buf, *lp);
601 		if(n == 0){
602 			continue;
603 		}
604 		if(n < 0)
605 			return "read error";
606 		m = convM2S(buf, f, n);
607 		if(m == 0){
608 			errstr = "bad type";
609 			continue;
610 		}
611 		*lp = m;
612 		return 0;
613 	}
614 	*lp = n;
615 	return errstr;
616 }
617 
618 void
619 _postfd(char *name, int pfd)
620 {
621 	char buf[2*NAMELEN];
622 	int fd;
623 
624 	snprint(buf, sizeof buf, "/srv/%s", name);
625 
626 	fd = create(buf, OWRITE, 0666);
627 	if(fd == -1)
628 		sysfatal("postsrv");
629 	fprint(fd, "%d", pfd);
630 	close(fd);
631 }
632 
633 void
634 postmountsrv(Srv *s, char *name, char *mtpt, int flag)
635 {
636 	int fd[2];
637 	if(pipe(fd) < 0)
638 		sysfatal("pipe");
639 	if(name)
640 		_postfd(name, fd[0]);
641 	switch(rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG)){
642 	case -1:
643 		sysfatal("fork");
644 	case 0:
645 		close(fd[0]);
646 		srv(s, fd[1]);
647 		_exits(0);
648 	default:
649 		if(mtpt)
650 			mount(fd[0], mtpt, flag, "");
651 	}
652 }
653 
654