xref: /plan9/sys/src/lib9p/srv.c (revision 59cc4ca53493a3c6d2349fe2b7f7c40f7dce7294)
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 void (*endsrv)(void*);
23 static void
24 setfcallerror(Fcall *f, char *err)
25 {
26 	strncpy(f->ename, err, sizeof f->ename);
27 	f->ename[sizeof f->ename - 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 	File *f, *of;
172 
173 	fmtinstall('D', dirconv);
174 	fmtinstall('F', fcallconv);
175 
176 	if(srv->fpool == nil)
177 		srv->fpool = allocfidpool();
178 	if(srv->rpool == nil)
179 		srv->rpool = allocreqpool();
180 
181 	srv->rpool->srv = srv;
182 	srv->fd = fd;
183 
184 	while(r = getreq(srv->rpool, srv->fpool, fd)){
185 		if(r->error){
186 			respond(r, r->error);
187 			continue;
188 		}
189 
190 		switch(r->type){
191 		default:
192 			abort();
193 
194 		case Tnop:
195 			respond(r, nil);
196 			break;
197 
198 		case Tsession:
199 			memset(r->fcall.authid, 0, sizeof r->fcall.authid);
200 			memset(r->fcall.authdom, 0, sizeof r->fcall.authdom);
201 			memset(r->fcall.chal, 0, sizeof r->fcall.chal);
202 			if(srv->session)
203 				srv->session(r, r->fcall.authid,
204 					r->fcall.authdom, r->fcall.chal);
205 			else
206 				respond(r, nil);
207 			break;
208 
209 		case Tflush:
210 			if(r->oldreq && srv->flush)
211 				srv->flush(r, r->oldreq);
212 			else
213 				respond(r, nil);
214 			break;
215 
216 		case Tattach:
217 			strncpy(r->fid->uid, r->ofcall.uname, NAMELEN);
218 			r->fid->uid[NAMELEN-1] = '\0';
219 			if(srv->attach)
220 				srv->attach(r, r->fid, r->ofcall.aname, &r->fcall.qid);
221 			else{		/* assume one tree, no auth */
222 				if(strcmp(r->ofcall.aname, "") != 0)
223 					r->error = Ebadattach;
224 				else{
225 					r->fid->file = srv->tree->root;
226 					r->fcall.qid = r->fid->file->qid;
227 				}
228 				respond(r, nil);
229 			}
230 			break;
231 
232 		case Tclwalk:
233 			r->type = Tclwalk_clone;
234 			/* fall through */
235 		case Tclone:
236 			if(r->fid->file)
237 				incref(&r->fid->file->ref);
238 			r->newfid->file = r->fid->file;
239 
240 			strcpy(r->newfid->uid, r->fid->uid);
241 			r->newfid->qid = r->fid->qid;
242 			r->newfid->aux = r->fid->aux;
243 
244 			if(srv->clone)
245 				srv->clone(r, r->fid, r->newfid);
246 			else
247 				respond(r, nil);
248 			break;
249 
250 		case Twalk:
251 			/* if you change this, change the copy in respond Tclwalk_clone above */
252 
253 			if(r->fid->file){
254 				/*
255 				 * We don't use the walk function if using file
256 				 * trees.  It's too much to expect clients to get
257 				 * the reference counts right on error or when
258 				 * playing other funny games with the tree.
259 				 */
260 				if(f = fwalk(r->fid->file, r->ofcall.name)) {
261 					of = r->fid->file;
262 					r->fid->file = f;
263 					fclose(of);
264 					r->fcall.qid = f->qid;
265 					respond(r, nil);
266 				}else
267 					respond(r, Enotfound);
268 				break;
269 			}
270 			r->fcall.qid = r->fid->qid;
271 			srv->walk(r, r->fid, r->ofcall.name, &r->fcall.qid);
272 			break;
273 
274 		case Topen:
275 			if(r->fid->omode != -1){
276 				respond(r, Ebotch);
277 				break;
278 			}
279 			if(r->fid->file){
280 				switch(r->fcall.mode&3){
281 				case OREAD:	p = AREAD; break;
282 				case OWRITE:	p = AWRITE; break;
283 				case ORDWR:	p = AREAD|AWRITE; break;
284 				case OEXEC:	p = AEXEC; break;
285 				default:	p = ~0; assert(0);
286 				}
287 				if(r->fcall.mode & OTRUNC)
288 					p |= AWRITE;
289 				if(!hasperm(r->fid->file, r->fid->uid, p)){
290 					respond(r, Eperm);
291 					break;
292 				}
293 				if((r->fcall.mode & ORCLOSE)
294 				&& !hasperm(r->fid->file->parent, r->fid->uid, AWRITE)){
295 					respond(r, Eperm);
296 					break;
297 				}
298 				r->fcall.qid = r->fid->file->qid;
299 			}
300 			if(srv->open)
301 				srv->open(r, r->fid, r->fcall.mode, &r->fcall.qid);
302 			else
303 				respond(r, nil);
304 			break;
305 
306 		case Tcreate:
307 			if(r->fid->omode != -1)
308 				r->error = Ebotch;
309 			else if(r->fid->file && !hasperm(r->fid->file, r->fid->uid, AWRITE))
310 				r->error = Eperm;
311 			else if(srv->create == nil)
312 				r->error = Enocreate;
313 
314 			if(r->error)
315 				respond(r, r->error);
316 			else
317 				srv->create(r, r->fid, r->ofcall.name, r->ofcall.mode,
318 					r->ofcall.perm, &r->fcall.qid);
319 			break;
320 
321 		case Tread:
322 			r->rbuf = emalloc(MAXFDATA);
323 			r->fcall.data = r->rbuf;
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, r->rbuf,
336 							&r->fcall.count, r->fcall.offset);
337 			}else{
338 				srv->read(r, r->fid, r->rbuf, &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|ORCLOSE|OCEXEC, 0600);
627 	if(fd == -1)
628 		sysfatal("post %s: %r", name);
629 	fprint(fd, "%d", pfd);
630 }
631 
632 void
633 postmountsrv(Srv *s, char *name, char *mtpt, int flag)
634 {
635 	int fd[2];
636 	if(pipe(fd) < 0)
637 		sysfatal("pipe");
638 	if(name)
639 		_postfd(name, fd[0]);
640 	switch(rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG|RFMEM)){
641 	case -1:
642 		sysfatal("fork");
643 	case 0:
644 		close(fd[0]);
645 		srv(s, fd[1]);
646 		if(endsrv)
647 			endsrv(nil);
648 		_exits(0);
649 	default:
650 		if(mtpt)
651 			if(mount(fd[0], mtpt, flag, "") == -1)
652 				fprint(2, "mount %s: %r\n", mtpt);
653 	}
654 }
655 
656