xref: /plan9/sys/src/cmd/upas/fs/fs.c (revision 901d8e213ab012b55016953db878424fee53d285)
1 #include "common.h"
2 #include <auth.h>
3 #include <fcall.h>
4 #include <libsec.h>
5 #include <ctype.h>
6 #include "dat.h"
7 
8 enum
9 {
10 	OPERM	= 0x3,		// mask of all permission types in open mode
11 };
12 
13 typedef struct Fid Fid;
14 
15 struct Fid
16 {
17 	Qid	qid;
18 	short	busy;
19 	short	open;
20 	int	fid;
21 	Fid	*next;
22 	Mailbox	*mb;
23 	Message	*m;
24 	Message *mtop;		// top level message
25 
26 	//finger pointers to speed up reads of large directories
27 	long	foff;	// offset/DIRLEN of finger
28 	Message	*fptr;	// pointer to message at off
29 	int	fvers;	// mailbox version when finger was saved
30 };
31 
32 ulong	path;		// incremented for each new file
33 Fid	*fids;
34 int	mfd[2];
35 char	user[Elemlen];
36 int	messagesize = 4*1024+IOHDRSZ;
37 uchar	mdata[8*1024+IOHDRSZ];
38 uchar	mbuf[8*1024+IOHDRSZ];
39 Fcall	thdr;
40 Fcall	rhdr;
41 int	fflg;
42 char	*mntpt;
43 int	biffing;
44 int	plumbing = 1;
45 
46 QLock	mbllock;
47 Mailbox	*mbl;
48 
49 Fid		*newfid(int);
50 void		error(char*);
51 void		io(void);
52 void		*erealloc(void*, ulong);
53 void		*emalloc(ulong);
54 void		usage(void);
55 void		reader(void);
56 int		readheader(Message*, char*, int, int);
57 int		cistrncmp(char*, char*, int);
58 int		tokenconvert(String*, char*, int);
59 String*		stringconvert(String*, char*, int);
60 void		post(char*, char*, int);
61 
62 char	*rflush(Fid*), *rauth(Fid*),
63 	*rattach(Fid*), *rwalk(Fid*),
64 	*ropen(Fid*), *rcreate(Fid*),
65 	*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
66 	*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*),
67 	*rversion(Fid*);
68 
69 char 	*(*fcalls[])(Fid*) = {
70 	[Tflush]	rflush,
71 	[Tversion]	rversion,
72 	[Tauth]	rauth,
73 	[Tattach]	rattach,
74 	[Twalk]		rwalk,
75 	[Topen]		ropen,
76 	[Tcreate]	rcreate,
77 	[Tread]		rread,
78 	[Twrite]	rwrite,
79 	[Tclunk]	rclunk,
80 	[Tremove]	rremove,
81 	[Tstat]		rstat,
82 	[Twstat]	rwstat,
83 };
84 
85 char	Eperm[] =	"permission denied";
86 char	Enotdir[] =	"not a directory";
87 char	Enoauth[] =	"upas/fs: authentication not required";
88 char	Enotexist[] =	"file does not exist";
89 char	Einuse[] =	"file in use";
90 char	Eexist[] =	"file exists";
91 char	Enotowner[] =	"not owner";
92 char	Eisopen[] = 	"file already open for I/O";
93 char	Excl[] = 	"exclusive use file already open";
94 char	Ename[] = 	"illegal name";
95 char	Ebadctl[] =	"unknown control message";
96 
97 char *dirtab[] =
98 {
99 [Qdir]		".",
100 [Qbody]		"body",
101 [Qbcc]		"bcc",
102 [Qcc]		"cc",
103 [Qdate]		"date",
104 [Qdigest]	"digest",
105 [Qdisposition]	"disposition",
106 [Qfilename]	"filename",
107 [Qfrom]		"from",
108 [Qheader]	"header",
109 [Qinfo]		"info",
110 [Qinreplyto]	"inreplyto",
111 [Qlines]	"lines",
112 [Qmimeheader]	"mimeheader",
113 [Qmessageid]	"messageid",
114 [Qraw]		"raw",
115 [Qrawunix]	"rawunix",
116 [Qrawbody]	"rawbody",
117 [Qrawheader]	"rawheader",
118 [Qreplyto]	"replyto",
119 [Qsender]	"sender",
120 [Qsubject]	"subject",
121 [Qto]		"to",
122 [Qtype]		"type",
123 [Qunixdate]	"unixdate",
124 [Qunixheader]	"unixheader",
125 [Qctl]		"ctl",
126 [Qmboxctl]	"ctl",
127 };
128 
129 enum
130 {
131 	Hsize=	1277,
132 };
133 
134 Hash	*htab[Hsize];
135 
136 int	debug;
137 int	fflag;
138 int	logging;
139 
140 void
usage(void)141 usage(void)
142 {
143 	fprint(2, "usage: upas/fs [-bdlnps] [-f mboxfile] [-m mountpoint]\n");
144 	exits("usage");
145 }
146 
147 void
notifyf(void * a,char * s)148 notifyf(void *a, char *s)
149 {
150 	USED(a);
151 	if(strncmp(s, "interrupt", 9) == 0)
152 		noted(NCONT);
153 	noted(NDFLT);
154 }
155 
156 void
main(int argc,char * argv[])157 main(int argc, char *argv[])
158 {
159 	int p[2], std, nodflt;
160 	char maildir[128];
161 	char mbox[128];
162 	char *mboxfile, *err;
163 	char srvfile[64];
164 	int srvpost;
165 
166 	rfork(RFNOTEG);
167 	mntpt = nil;
168 	fflag = 0;
169 	mboxfile = nil;
170 	std = 0;
171 	nodflt = 0;
172 	srvpost = 0;
173 
174 	ARGBEGIN{
175 	case 'b':
176 		biffing = 1;
177 		break;
178 	case 'f':
179 		fflag = 1;
180 		mboxfile = EARGF(usage());
181 		break;
182 	case 'm':
183 		mntpt = EARGF(usage());
184 		break;
185 	case 'd':
186 		debug = 1;
187 		break;
188 	case 'p':
189 		plumbing = 0;
190 		break;
191 	case 's':
192 		srvpost = 1;
193 		break;
194 	case 'l':
195 		logging = 1;
196 		break;
197 	case 'n':
198 		nodflt = 1;
199 		break;
200 	default:
201 		usage();
202 	}ARGEND
203 
204 	if(argc)
205 		usage();
206 	if(pipe(p) < 0)
207 		error("pipe failed");
208 	mfd[0] = p[0];
209 	mfd[1] = p[0];
210 
211 	notify(notifyf);
212 	strcpy(user, getuser());
213 	if(mntpt == nil){
214 		snprint(maildir, sizeof(maildir), "/mail/fs");
215 		mntpt = maildir;
216 	}
217 	if(mboxfile == nil && !nodflt){
218 		snprint(mbox, sizeof(mbox), "/mail/box/%s/mbox", user);
219 		mboxfile = mbox;
220 		std = 1;
221 	}
222 
223 	if(debug)
224 		fmtinstall('F', fcallfmt);
225 
226 	if(mboxfile != nil){
227 		err = newmbox(mboxfile, "mbox", std);
228 		if(err != nil)
229 			sysfatal("opening %s: %s", mboxfile, err);
230 	}
231 
232 	switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG|RFREND)){
233 	case -1:
234 		error("fork");
235 	case 0:
236 		henter(PATH(0, Qtop), dirtab[Qctl],
237 			(Qid){PATH(0, Qctl), 0, QTFILE}, nil, nil);
238 		close(p[1]);
239 		io();
240 		postnote(PNGROUP, getpid(), "die yankee pig dog");
241 		break;
242 	default:
243 		close(p[0]);	/* don't deadlock if child fails */
244 		if(srvpost){
245 			sprint(srvfile, "/srv/upasfs.%s", user);
246 			post(srvfile, "upasfs", p[1]);
247 		} else {
248 			if(mount(p[1], -1, mntpt, MREPL, "") < 0)
249 				error("mount failed");
250 		}
251 	}
252 	exits(0);
253 }
254 
255 static int
fileinfo(Message * m,int t,char ** pp)256 fileinfo(Message *m, int t, char **pp)
257 {
258 	char *p;
259 	int len;
260 
261 	p = "";
262 	len = 0;
263 	switch(t){
264 	case Qbody:
265 		p = m->body;
266 		len = m->bend - m->body;
267 		break;
268 	case Qbcc:
269 		if(m->bcc822){
270 			p = s_to_c(m->bcc822);
271 			len = strlen(p);
272 		}
273 		break;
274 	case Qcc:
275 		if(m->cc822){
276 			p = s_to_c(m->cc822);
277 			len = strlen(p);
278 		}
279 		break;
280 	case Qdisposition:
281 		switch(m->disposition){
282 		case Dinline:
283 			p = "inline";
284 			break;
285 		case Dfile:
286 			p = "file";
287 			break;
288 		}
289 		len = strlen(p);
290 		break;
291 	case Qdate:
292 		if(m->date822){
293 			p = s_to_c(m->date822);
294 			len = strlen(p);
295 		} else if(m->unixdate != nil){
296 			p = s_to_c(m->unixdate);
297 			len = strlen(p);
298 		}
299 		break;
300 	case Qfilename:
301 		if(m->filename){
302 			p = s_to_c(m->filename);
303 			len = strlen(p);
304 		}
305 		break;
306 	case Qinreplyto:
307 		if(m->inreplyto822){
308 			p = s_to_c(m->inreplyto822);
309 			len = strlen(p);
310 		}
311 		break;
312 	case Qmessageid:
313 		if(m->messageid822){
314 			p = s_to_c(m->messageid822);
315 			len = strlen(p);
316 		}
317 		break;
318 	case Qfrom:
319 		if(m->from822){
320 			p = s_to_c(m->from822);
321 			len = strlen(p);
322 		} else if(m->unixfrom != nil){
323 			p = s_to_c(m->unixfrom);
324 			len = strlen(p);
325 		}
326 		break;
327 	case Qheader:
328 		p = m->header;
329 		len = headerlen(m);
330 		break;
331 	case Qlines:
332 		p = m->lines;
333 		if(*p == 0)
334 			countlines(m);
335 		len = strlen(m->lines);
336 		break;
337 	case Qraw:
338 		p = m->start;
339 		if(strncmp(m->start, "From ", 5) == 0){
340 			p = strchr(p, '\n');
341 			if(p == nil)
342 				p = m->start;
343 			else
344 				p++;
345 		}
346 		len = m->end - p;
347 		break;
348 	case Qrawunix:
349 		p = m->start;
350 		len = m->end - p;
351 		break;
352 	case Qrawbody:
353 		p = m->rbody;
354 		len = m->rbend - p;
355 		break;
356 	case Qrawheader:
357 		p = m->header;
358 		len = m->hend - p;
359 		break;
360 	case Qmimeheader:
361 		p = m->mheader;
362 		len = m->mhend - p;
363 		break;
364 	case Qreplyto:
365 		p = nil;
366 		if(m->replyto822 != nil){
367 			p = s_to_c(m->replyto822);
368 			len = strlen(p);
369 		} else if(m->from822 != nil){
370 			p = s_to_c(m->from822);
371 			len = strlen(p);
372 		} else if(m->sender822 != nil){
373 			p = s_to_c(m->sender822);
374 			len = strlen(p);
375 		} else if(m->unixfrom != nil){
376 			p = s_to_c(m->unixfrom);
377 			len = strlen(p);
378 		}
379 		break;
380 	case Qsender:
381 		if(m->sender822){
382 			p = s_to_c(m->sender822);
383 			len = strlen(p);
384 		}
385 		break;
386 	case Qsubject:
387 		p = nil;
388 		if(m->subject822){
389 			p = s_to_c(m->subject822);
390 			len = strlen(p);
391 		}
392 		break;
393 	case Qto:
394 		if(m->to822){
395 			p = s_to_c(m->to822);
396 			len = strlen(p);
397 		}
398 		break;
399 	case Qtype:
400 		if(m->type){
401 			p = s_to_c(m->type);
402 			len = strlen(p);
403 		}
404 		break;
405 	case Qunixdate:
406 		if(m->unixdate){
407 			p = s_to_c(m->unixdate);
408 			len = strlen(p);
409 		}
410 		break;
411 	case Qunixheader:
412 		if(m->unixheader){
413 			p = s_to_c(m->unixheader);
414 			len = s_len(m->unixheader);
415 		}
416 		break;
417 	case Qdigest:
418 		if(m->sdigest){
419 			p = s_to_c(m->sdigest);
420 			len = strlen(p);
421 		}
422 		break;
423 	}
424 	*pp = p;
425 	return len;
426 }
427 
428 int infofields[] = {
429 	Qfrom,
430 	Qto,
431 	Qcc,
432 	Qreplyto,
433 	Qunixdate,
434 	Qsubject,
435 	Qtype,
436 	Qdisposition,
437 	Qfilename,
438 	Qdigest,
439 	Qbcc,
440 	Qinreplyto,
441 	Qdate,
442 	Qsender,
443 	Qmessageid,
444 	Qlines,
445 	-1,
446 };
447 
448 static int
readinfo(Message * m,char * buf,long off,int count)449 readinfo(Message *m, char *buf, long off, int count)
450 {
451 	char *p;
452 	int len, i, n;
453 	String *s;
454 
455 	s = s_new();
456 	len = 0;
457 	for(i = 0; len < count && infofields[i] >= 0; i++){
458 		n = fileinfo(m, infofields[i], &p);
459 		s = stringconvert(s, p, n);
460 		s_append(s, "\n");
461 		p = s_to_c(s);
462 		n = strlen(p);
463 		if(off > 0){
464 			if(off >= n){
465 				off -= n;
466 				continue;
467 			}
468 			p += off;
469 			n -= off;
470 			off = 0;
471 		}
472 		if(n > count - len)
473 			n = count - len;
474 		if(buf)
475 			memmove(buf+len, p, n);
476 		len += n;
477 	}
478 	s_free(s);
479 	return len;
480 }
481 
482 static void
mkstat(Dir * d,Mailbox * mb,Message * m,int t)483 mkstat(Dir *d, Mailbox *mb, Message *m, int t)
484 {
485 	char *p;
486 
487 	d->uid = user;
488 	d->gid = user;
489 	d->muid = user;
490 	d->mode = 0444;
491 	d->qid.vers = 0;
492 	d->qid.type = QTFILE;
493 	d->type = 0;
494 	d->dev = 0;
495 	if(mb != nil && mb->d != nil){
496 		d->atime = mb->d->atime;
497 		d->mtime = mb->d->mtime;
498 	} else {
499 		d->atime = time(0);
500 		d->mtime = d->atime;
501 	}
502 
503 	switch(t){
504 	case Qtop:
505 		d->name = ".";
506 		d->mode = DMDIR|0555;
507 		d->atime = d->mtime = time(0);
508 		d->length = 0;
509 		d->qid.path = PATH(0, Qtop);
510 		d->qid.type = QTDIR;
511 		break;
512 	case Qmbox:
513 		d->name = mb->name;
514 		d->mode = DMDIR|0555;
515 		d->length = 0;
516 		d->qid.path = PATH(mb->id, Qmbox);
517 		d->qid.type = QTDIR;
518 		d->qid.vers = mb->vers;
519 		break;
520 	case Qdir:
521 		d->name = m->name;
522 		d->mode = DMDIR|0555;
523 		d->length = 0;
524 		d->qid.path = PATH(m->id, Qdir);
525 		d->qid.type = QTDIR;
526 		break;
527 	case Qctl:
528 		d->name = dirtab[t];
529 		d->mode = 0666;
530 		d->atime = d->mtime = time(0);
531 		d->length = 0;
532 		d->qid.path = PATH(0, Qctl);
533 		break;
534 	case Qmboxctl:
535 		d->name = dirtab[t];
536 		d->mode = 0222;
537 		d->atime = d->mtime = time(0);
538 		d->length = 0;
539 		d->qid.path = PATH(mb->id, Qmboxctl);
540 		break;
541 	case Qinfo:
542 		d->name = dirtab[t];
543 		d->length = readinfo(m, nil, 0, 1<<30);
544 		d->qid.path = PATH(m->id, t);
545 		break;
546 	default:
547 		d->name = dirtab[t];
548 		d->length = fileinfo(m, t, &p);
549 		d->qid.path = PATH(m->id, t);
550 		break;
551 	}
552 }
553 
554 char*
rversion(Fid *)555 rversion(Fid*)
556 {
557 	Fid *f;
558 
559 	if(thdr.msize < 256)
560 		return "max messagesize too small";
561 	if(thdr.msize < messagesize)
562 		messagesize = thdr.msize;
563 	rhdr.msize = messagesize;
564 	if(strncmp(thdr.version, "9P2000", 6) != 0)
565 		return "unknown 9P version";
566 	else
567 		rhdr.version = "9P2000";
568 	for(f = fids; f; f = f->next)
569 		if(f->busy)
570 			rclunk(f);
571 	return nil;
572 }
573 
574 char*
rauth(Fid *)575 rauth(Fid*)
576 {
577 	return Enoauth;
578 }
579 
580 char*
rflush(Fid * f)581 rflush(Fid *f)
582 {
583 	USED(f);
584 	return 0;
585 }
586 
587 char*
rattach(Fid * f)588 rattach(Fid *f)
589 {
590 	f->busy = 1;
591 	f->m = nil;
592 	f->mb = nil;
593 	f->qid.path = PATH(0, Qtop);
594 	f->qid.type = QTDIR;
595 	f->qid.vers = 0;
596 	rhdr.qid = f->qid;
597 	if(strcmp(thdr.uname, user) != 0)
598 		return Eperm;
599 	return 0;
600 }
601 
602 static Fid*
doclone(Fid * f,int nfid)603 doclone(Fid *f, int nfid)
604 {
605 	Fid *nf;
606 
607 	nf = newfid(nfid);
608 	if(nf->busy)
609 		return nil;
610 	nf->busy = 1;
611 	nf->open = 0;
612 	nf->m = f->m;
613 	nf->mtop = f->mtop;
614 	nf->mb = f->mb;
615 	if(f->mb != nil)
616 		mboxincref(f->mb);
617 	if(f->mtop != nil){
618 		qlock(f->mb);
619 		msgincref(f->mtop);
620 		qunlock(f->mb);
621 	}
622 	nf->qid = f->qid;
623 	return nf;
624 }
625 
626 char*
dowalk(Fid * f,char * name)627 dowalk(Fid *f, char *name)
628 {
629 	int t;
630 	Mailbox *omb, *mb;
631 	char *rv, *p;
632 	Hash *h;
633 
634 	t = FILE(f->qid.path);
635 
636 	rv = Enotexist;
637 
638 	omb = f->mb;
639 	if(omb)
640 		qlock(omb);
641 	else
642 		qlock(&mbllock);
643 
644 	// this must catch everything except . and ..
645 retry:
646 	h = hlook(f->qid.path, name);
647 	if(h != nil){
648 		f->mb = h->mb;
649 		f->m = h->m;
650 		switch(t){
651 		case Qtop:
652 			if(f->mb != nil)
653 				mboxincref(f->mb);
654 			break;
655 		case Qmbox:
656 			if(f->m){
657 				msgincref(f->m);
658 				f->mtop = f->m;
659 			}
660 			break;
661 		}
662 		f->qid = h->qid;
663 		rv = nil;
664 	} else if((p = strchr(name, '.')) != nil && *name != '.'){
665 		*p = 0;
666 		goto retry;
667 	}
668 
669 	if(omb)
670 		qunlock(omb);
671 	else
672 		qunlock(&mbllock);
673 	if(rv == nil)
674 		return rv;
675 
676 	if(strcmp(name, ".") == 0)
677 		return nil;
678 
679 	if(f->qid.type != QTDIR)
680 		return Enotdir;
681 
682 	if(strcmp(name, "..") == 0){
683 		switch(t){
684 		case Qtop:
685 			f->qid.path = PATH(0, Qtop);
686 			f->qid.type = QTDIR;
687 			f->qid.vers = 0;
688 			break;
689 		case Qmbox:
690 			f->qid.path = PATH(0, Qtop);
691 			f->qid.type = QTDIR;
692 			f->qid.vers = 0;
693 			qlock(&mbllock);
694 			mb = f->mb;
695 			f->mb = nil;
696 			mboxdecref(mb);
697 			qunlock(&mbllock);
698 			break;
699 		case Qdir:
700 			qlock(f->mb);
701 			if(f->m->whole == f->mb->root){
702 				f->qid.path = PATH(f->mb->id, Qmbox);
703 				f->qid.type = QTDIR;
704 				f->qid.vers = f->mb->d->qid.vers;
705 				msgdecref(f->mb, f->mtop);
706 				f->m = f->mtop = nil;
707 			} else {
708 				f->m = f->m->whole;
709 				f->qid.path = PATH(f->m->id, Qdir);
710 				f->qid.type = QTDIR;
711 			}
712 			qunlock(f->mb);
713 			break;
714 		}
715 		rv = nil;
716 	}
717 	return rv;
718 }
719 
720 char*
rwalk(Fid * f)721 rwalk(Fid *f)
722 {
723 	Fid *nf;
724 	char *rv;
725 	int i;
726 
727 	if(f->open)
728 		return Eisopen;
729 
730 	rhdr.nwqid = 0;
731 	nf = nil;
732 
733 	/* clone if requested */
734 	if(thdr.newfid != thdr.fid){
735 		nf = doclone(f, thdr.newfid);
736 		if(nf == nil)
737 			return "new fid in use";
738 		f = nf;
739 	}
740 
741 	/* if it's just a clone, return */
742 	if(thdr.nwname == 0 && nf != nil)
743 		return nil;
744 
745 	/* walk each element */
746 	rv = nil;
747 	for(i = 0; i < thdr.nwname; i++){
748 		rv = dowalk(f, thdr.wname[i]);
749 		if(rv != nil){
750 			if(nf != nil)
751 				rclunk(nf);
752 			break;
753 		}
754 		rhdr.wqid[i] = f->qid;
755 	}
756 	rhdr.nwqid = i;
757 
758 	/* we only error out if no walk  */
759 	if(i > 0)
760 		rv = nil;
761 
762 	return rv;
763 }
764 
765 char *
ropen(Fid * f)766 ropen(Fid *f)
767 {
768 	int file;
769 
770 	if(f->open)
771 		return Eisopen;
772 
773 	file = FILE(f->qid.path);
774 	if(thdr.mode != OREAD)
775 		if(file != Qctl && file != Qmboxctl)
776 			return Eperm;
777 
778 	// make sure we've decoded
779 	if(file == Qbody){
780 		if(f->m->decoded == 0)
781 			decode(f->m);
782 		if(f->m->converted == 0)
783 			convert(f->m);
784 	}
785 
786 	rhdr.iounit = 0;
787 	rhdr.qid = f->qid;
788 	f->open = 1;
789 	return 0;
790 }
791 
792 char *
rcreate(Fid *)793 rcreate(Fid*)
794 {
795 	return Eperm;
796 }
797 
798 int
readtopdir(Fid *,uchar * buf,long off,int cnt,int blen)799 readtopdir(Fid*, uchar *buf, long off, int cnt, int blen)
800 {
801 	Dir d;
802 	int m, n;
803 	long pos;
804 	Mailbox *mb;
805 
806 	n = 0;
807 	pos = 0;
808 	mkstat(&d, nil, nil, Qctl);
809 	m = convD2M(&d, &buf[n], blen);
810 	if(off <= pos){
811 		if(m <= BIT16SZ || m > cnt)
812 			return 0;
813 		n += m;
814 		cnt -= m;
815 	}
816 	pos += m;
817 
818 	for(mb = mbl; mb != nil; mb = mb->next){
819 		mkstat(&d, mb, nil, Qmbox);
820 		m = convD2M(&d, &buf[n], blen-n);
821 		if(off <= pos){
822 			if(m <= BIT16SZ || m > cnt)
823 				break;
824 			n += m;
825 			cnt -= m;
826 		}
827 		pos += m;
828 	}
829 	return n;
830 }
831 
832 int
readmboxdir(Fid * f,uchar * buf,long off,int cnt,int blen)833 readmboxdir(Fid *f, uchar *buf, long off, int cnt, int blen)
834 {
835 	Dir d;
836 	int n, m;
837 	long pos;
838 	Message *msg;
839 
840 	n = 0;
841 	if(f->mb->ctl){
842 		mkstat(&d, f->mb, nil, Qmboxctl);
843 		m = convD2M(&d, &buf[n], blen);
844 		if(off == 0){
845 			if(m <= BIT16SZ || m > cnt){
846 				f->fptr = nil;
847 				return 0;
848 			}
849 			n += m;
850 			cnt -= m;
851 		} else
852 			off -= m;
853 	}
854 
855 	// to avoid n**2 reads of the directory, use a saved finger pointer
856 	if(f->mb->vers == f->fvers && off >= f->foff && f->fptr != nil){
857 		msg = f->fptr;
858 		pos = f->foff;
859 	} else {
860 		msg = f->mb->root->part;
861 		pos = 0;
862 	}
863 
864 	for(; cnt > 0 && msg != nil; msg = msg->next){
865 		// act like deleted files aren't there
866 		if(msg->deleted)
867 			continue;
868 
869 		mkstat(&d, f->mb, msg, Qdir);
870 		m = convD2M(&d, &buf[n], blen-n);
871 		if(off <= pos){
872 			if(m <= BIT16SZ || m > cnt)
873 				break;
874 			n += m;
875 			cnt -= m;
876 		}
877 		pos += m;
878 	}
879 
880 	// save a finger pointer for next read of the mbox directory
881 	f->foff = pos;
882 	f->fptr = msg;
883 	f->fvers = f->mb->vers;
884 
885 	return n;
886 }
887 
888 int
readmsgdir(Fid * f,uchar * buf,long off,int cnt,int blen)889 readmsgdir(Fid *f, uchar *buf, long off, int cnt, int blen)
890 {
891 	Dir d;
892 	int i, n, m;
893 	long pos;
894 	Message *msg;
895 
896 	n = 0;
897 	pos = 0;
898 	for(i = 0; i < Qmax; i++){
899 		mkstat(&d, f->mb, f->m, i);
900 		m = convD2M(&d, &buf[n], blen-n);
901 		if(off <= pos){
902 			if(m <= BIT16SZ || m > cnt)
903 				return n;
904 			n += m;
905 			cnt -= m;
906 		}
907 		pos += m;
908 	}
909 	for(msg = f->m->part; msg != nil; msg = msg->next){
910 		mkstat(&d, f->mb, msg, Qdir);
911 		m = convD2M(&d, &buf[n], blen-n);
912 		if(off <= pos){
913 			if(m <= BIT16SZ || m > cnt)
914 				break;
915 			n += m;
916 			cnt -= m;
917 		}
918 		pos += m;
919 	}
920 
921 	return n;
922 }
923 
924 char*
rread(Fid * f)925 rread(Fid *f)
926 {
927 	long off;
928 	int t, i, n, cnt;
929 	char *p;
930 
931 	rhdr.count = 0;
932 	off = thdr.offset;
933 	cnt = thdr.count;
934 
935 	if(cnt > messagesize - IOHDRSZ)
936 		cnt = messagesize - IOHDRSZ;
937 
938 	rhdr.data = (char*)mbuf;
939 
940 	t = FILE(f->qid.path);
941 	if(f->qid.type & QTDIR){
942 		if(t == Qtop) {
943 			qlock(&mbllock);
944 			n = readtopdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
945 			qunlock(&mbllock);
946 		} else if(t == Qmbox) {
947 			qlock(f->mb);
948 			if(off == 0)
949 				syncmbox(f->mb, 1);
950 			n = readmboxdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
951 			qunlock(f->mb);
952 		} else if(t == Qmboxctl) {
953 			n = 0;
954 		} else {
955 			n = readmsgdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
956 		}
957 
958 		rhdr.count = n;
959 		return nil;
960 	}
961 
962 	if(FILE(f->qid.path) == Qheader){
963 		rhdr.count = readheader(f->m, (char*)mbuf, off, cnt);
964 		return nil;
965 	}
966 
967 	if(FILE(f->qid.path) == Qinfo){
968 		rhdr.count = readinfo(f->m, (char*)mbuf, off, cnt);
969 		return nil;
970 	}
971 
972 	i = fileinfo(f->m, FILE(f->qid.path), &p);
973 	if(off < i){
974 		if((off + cnt) > i)
975 			cnt = i - off;
976 		memmove(mbuf, p + off, cnt);
977 		rhdr.count = cnt;
978 	}
979 	return nil;
980 }
981 
982 char*
rwrite(Fid * f)983 rwrite(Fid *f)
984 {
985 	char *err;
986 	char *token[1024];
987 	int t, n;
988 	String *file;
989 
990 	t = FILE(f->qid.path);
991 	rhdr.count = thdr.count;
992 	switch(t){
993 	case Qctl:
994 		if(thdr.count == 0)
995 			return Ebadctl;
996 		if(thdr.data[thdr.count-1] == '\n')
997 			thdr.data[thdr.count-1] = 0;
998 		else
999 			thdr.data[thdr.count] = 0;
1000 		n = tokenize(thdr.data, token, nelem(token));
1001 		if(n == 0)
1002 			return Ebadctl;
1003 		if(strcmp(token[0], "open") == 0){
1004 			file = s_new();
1005 			switch(n){
1006 			case 1:
1007 				err = Ebadctl;
1008 				break;
1009 			case 2:
1010 				mboxpath(token[1], getlog(), file, 0);
1011 				err = newmbox(s_to_c(file), nil, 0);
1012 				break;
1013 			default:
1014 				mboxpath(token[1], getlog(), file, 0);
1015 				if(strchr(token[2], '/') != nil)
1016 					err = "/ not allowed in mailbox name";
1017 				else
1018 					err = newmbox(s_to_c(file), token[2], 0);
1019 				break;
1020 			}
1021 			s_free(file);
1022 			return err;
1023 		}
1024 		if(strcmp(token[0], "close") == 0){
1025 			if(n < 2)
1026 				return nil;
1027 			freembox(token[1]);
1028 			return nil;
1029 		}
1030 		if(strcmp(token[0], "delete") == 0){
1031 			if(n < 3)
1032 				return nil;
1033 			delmessages(n-1, &token[1]);
1034 			return nil;
1035 		}
1036 		return Ebadctl;
1037 	case Qmboxctl:
1038 		if(f->mb && f->mb->ctl){
1039 			if(thdr.count == 0)
1040 				return Ebadctl;
1041 			if(thdr.data[thdr.count-1] == '\n')
1042 				thdr.data[thdr.count-1] = 0;
1043 			else
1044 				thdr.data[thdr.count] = 0;
1045 			n = tokenize(thdr.data, token, nelem(token));
1046 			if(n == 0)
1047 				return Ebadctl;
1048 			return (*f->mb->ctl)(f->mb, n, token);
1049 		}
1050 	}
1051 	return Eperm;
1052 }
1053 
1054 char *
rclunk(Fid * f)1055 rclunk(Fid *f)
1056 {
1057 	Mailbox *mb;
1058 
1059 	f->busy = 0;
1060 	f->open = 0;
1061 	if(f->mtop != nil){
1062 		qlock(f->mb);
1063 		msgdecref(f->mb, f->mtop);
1064 		qunlock(f->mb);
1065 	}
1066 	f->m = f->mtop = nil;
1067 	mb = f->mb;
1068 	if(mb != nil){
1069 		f->mb = nil;
1070 		assert(mb->refs > 0);
1071 		qlock(&mbllock);
1072 		mboxdecref(mb);
1073 		qunlock(&mbllock);
1074 	}
1075 	f->fid = -1;
1076 	return 0;
1077 }
1078 
1079 char *
rremove(Fid * f)1080 rremove(Fid *f)
1081 {
1082 	if(f->m != nil){
1083 		if(f->m->deleted == 0)
1084 			mailplumb(f->mb, f->m, 1);
1085 		f->m->deleted = 1;
1086 	}
1087 	return rclunk(f);
1088 }
1089 
1090 char *
rstat(Fid * f)1091 rstat(Fid *f)
1092 {
1093 	Dir d;
1094 
1095 	if(FILE(f->qid.path) == Qmbox){
1096 		qlock(f->mb);
1097 		syncmbox(f->mb, 1);
1098 		qunlock(f->mb);
1099 	}
1100 	mkstat(&d, f->mb, f->m, FILE(f->qid.path));
1101 	rhdr.nstat = convD2M(&d, mbuf, messagesize - IOHDRSZ);
1102 	rhdr.stat = mbuf;
1103 	return 0;
1104 }
1105 
1106 char *
rwstat(Fid *)1107 rwstat(Fid*)
1108 {
1109 	return Eperm;
1110 }
1111 
1112 Fid *
newfid(int fid)1113 newfid(int fid)
1114 {
1115 	Fid *f, *ff;
1116 
1117 	ff = 0;
1118 	for(f = fids; f; f = f->next)
1119 		if(f->fid == fid)
1120 			return f;
1121 		else if(!ff && !f->busy)
1122 			ff = f;
1123 	if(ff){
1124 		ff->fid = fid;
1125 		ff->fptr = nil;
1126 		return ff;
1127 	}
1128 	f = emalloc(sizeof *f);
1129 	f->fid = fid;
1130 	f->fptr = nil;
1131 	f->next = fids;
1132 	fids = f;
1133 	return f;
1134 }
1135 
1136 int
fidmboxrefs(Mailbox * mb)1137 fidmboxrefs(Mailbox *mb)
1138 {
1139 	Fid *f;
1140 	int refs = 0;
1141 
1142 	for(f = fids; f; f = f->next){
1143 		if(f->mb == mb)
1144 			refs++;
1145 	}
1146 	return refs;
1147 }
1148 
1149 void
io(void)1150 io(void)
1151 {
1152 	char *err;
1153 	int n;
1154 
1155 	/* start a process to watch the mailboxes*/
1156 	if(plumbing){
1157 		switch(rfork(RFPROC|RFMEM)){
1158 		case -1:
1159 			/* oh well */
1160 			break;
1161 		case 0:
1162 			reader();
1163 			exits(nil);
1164 		default:
1165 			break;
1166 		}
1167 	}
1168 
1169 	for(;;){
1170 		/*
1171 		 * reading from a pipe or a network device
1172 		 * will give an error after a few eof reads
1173 		 * however, we cannot tell the difference
1174 		 * between a zero-length read and an interrupt
1175 		 * on the processes writing to us,
1176 		 * so we wait for the error
1177 		 */
1178 		checkmboxrefs();
1179 		n = read9pmsg(mfd[0], mdata, messagesize);
1180 		if(n == 0)
1181 			continue;
1182 		if(n < 0)
1183 			return;
1184 		if(convM2S(mdata, n, &thdr) == 0)
1185 			continue;
1186 
1187 		if(debug)
1188 			fprint(2, "%s:<-%F\n", argv0, &thdr);
1189 
1190 		rhdr.data = (char*)mdata + messagesize;
1191 		if(!fcalls[thdr.type])
1192 			err = "bad fcall type";
1193 		else
1194 			err = (*fcalls[thdr.type])(newfid(thdr.fid));
1195 		if(err){
1196 			rhdr.type = Rerror;
1197 			rhdr.ename = err;
1198 		}else{
1199 			rhdr.type = thdr.type + 1;
1200 			rhdr.fid = thdr.fid;
1201 		}
1202 		rhdr.tag = thdr.tag;
1203 		if(debug)
1204 			fprint(2, "%s:->%F\n", argv0, &rhdr);/**/
1205 		n = convS2M(&rhdr, mdata, messagesize);
1206 		if(write(mfd[1], mdata, n) != n)
1207 			error("mount write");
1208 	}
1209 }
1210 
1211 void
reader(void)1212 reader(void)
1213 {
1214 	ulong t;
1215 	Dir *d;
1216 	Mailbox *mb;
1217 
1218 	sleep(15*1000);
1219 	for(;;){
1220 		t = time(0);
1221 		qlock(&mbllock);
1222 		for(mb = mbl; mb != nil; mb = mb->next){
1223 			assert(mb->refs > 0);
1224 			if(mb->waketime != 0 && t > mb->waketime){
1225 				qlock(mb);
1226 				mb->waketime = 0;
1227 				break;
1228 			}
1229 
1230 			d = dirstat(mb->path);
1231 			if(d == nil)
1232 				continue;
1233 
1234 			qlock(mb);
1235 			if(mb->d)
1236 			if(d->qid.path != mb->d->qid.path
1237 			   || d->qid.vers != mb->d->qid.vers){
1238 				free(d);
1239 				break;
1240 			}
1241 			qunlock(mb);
1242 			free(d);
1243 		}
1244 		qunlock(&mbllock);
1245 		if(mb != nil){
1246 			syncmbox(mb, 1);
1247 			qunlock(mb);
1248 		} else
1249 			sleep(15*1000);
1250 	}
1251 }
1252 
1253 int
newid(void)1254 newid(void)
1255 {
1256 	int rv;
1257 	static int id;
1258 	static Lock idlock;
1259 
1260 	lock(&idlock);
1261 	rv = ++id;
1262 	unlock(&idlock);
1263 
1264 	return rv;
1265 }
1266 
1267 void
error(char * s)1268 error(char *s)
1269 {
1270 	postnote(PNGROUP, getpid(), "die yankee pig dog");
1271 	fprint(2, "%s: %s: %r\n", argv0, s);
1272 	exits(s);
1273 }
1274 
1275 
1276 typedef struct Ignorance Ignorance;
1277 struct Ignorance
1278 {
1279 	Ignorance *next;
1280 	char	*str;		/* string */
1281 	int	partial;	/* true if not exact match */
1282 };
1283 Ignorance *ignorance;
1284 
1285 /*
1286  *  read the file of headers to ignore
1287  */
1288 void
readignore(void)1289 readignore(void)
1290 {
1291 	char *p;
1292 	Ignorance *i;
1293 	Biobuf *b;
1294 
1295 	if(ignorance != nil)
1296 		return;
1297 
1298 	b = Bopen("/mail/lib/ignore", OREAD);
1299 	if(b == 0)
1300 		return;
1301 	while(p = Brdline(b, '\n')){
1302 		p[Blinelen(b)-1] = 0;
1303 		while(*p && (*p == ' ' || *p == '\t'))
1304 			p++;
1305 		if(*p == '#')
1306 			continue;
1307 		i = malloc(sizeof(Ignorance));
1308 		if(i == 0)
1309 			break;
1310 		i->partial = strlen(p);
1311 		i->str = strdup(p);
1312 		if(i->str == 0){
1313 			free(i);
1314 			break;
1315 		}
1316 		i->next = ignorance;
1317 		ignorance = i;
1318 	}
1319 	Bterm(b);
1320 }
1321 
1322 int
ignore(char * p)1323 ignore(char *p)
1324 {
1325 	Ignorance *i;
1326 
1327 	readignore();
1328 	for(i = ignorance; i != nil; i = i->next)
1329 		if(cistrncmp(i->str, p, i->partial) == 0)
1330 			return 1;
1331 	return 0;
1332 }
1333 
1334 int
hdrlen(char * p,char * e)1335 hdrlen(char *p, char *e)
1336 {
1337 	char *ep;
1338 
1339 	ep = p;
1340 	do {
1341 		ep = strchr(ep, '\n');
1342 		if(ep == nil){
1343 			ep = e;
1344 			break;
1345 		}
1346 		ep++;
1347 		if(ep >= e){
1348 			ep = e;
1349 			break;
1350 		}
1351 	} while(*ep == ' ' || *ep == '\t');
1352 	return ep - p;
1353 }
1354 
1355 // rfc2047 non-ascii: =?charset?q?encoded-text?=
1356 int
rfc2047convert(String * s,char * token,int len)1357 rfc2047convert(String *s, char *token, int len)
1358 {
1359 	char charset[100], decoded[1024], *e, *x;
1360 	int l;
1361 
1362 	if(len == 0)
1363 		return -1;
1364 
1365 	e = token+len-2;
1366 	token += 2;
1367 
1368 	x = memchr(token, '?', e-token);
1369 	if(x == nil || (l=x-token) >= sizeof charset)
1370 		return -1;
1371 	memmove(charset, token, l);
1372 	charset[l] = 0;
1373 
1374 	token = x+1;
1375 
1376 	// bail if it doesn't fit
1377 	if(e-token > sizeof(decoded)-1)
1378 		return -1;
1379 
1380 	// bail if we don't understand the encoding
1381 	if(cistrncmp(token, "b?", 2) == 0){
1382 		token += 2;
1383 		len = dec64((uchar*)decoded, sizeof(decoded), token, e-token);
1384 		decoded[len] = 0;
1385 	} else if(cistrncmp(token, "q?", 2) == 0){
1386 		token += 2;
1387 		len = decquoted(decoded, token, e, 1);
1388 		if(len > 0 && decoded[len-1] == '\n')
1389 			len--;
1390 		decoded[len] = 0;
1391 	} else
1392 		return -1;
1393 
1394 	if(xtoutf(charset, &x, decoded, decoded+len) <= 0)
1395 		s_append(s, decoded);
1396 	else {
1397 		s_append(s, x);
1398 		free(x);
1399 	}
1400 	return 0;
1401 }
1402 
1403 char*
rfc2047start(char * start,char * end)1404 rfc2047start(char *start, char *end)
1405 {
1406 	int quests;
1407 
1408 	if(*--end != '=')
1409 		return nil;
1410 	if(*--end != '?')
1411 		return nil;
1412 
1413 	quests = 0;
1414 	for(end--; end >= start; end--){
1415 		switch(*end){
1416 		case '=':
1417 			if(quests == 3 && *(end+1) == '?')
1418 				return end;
1419 			break;
1420 		case '?':
1421 			++quests;
1422 			break;
1423 		case ' ':
1424 		case '\t':
1425 		case '\n':
1426 		case '\r':
1427 			/* can't have white space in a token */
1428 			return nil;
1429 		}
1430 	}
1431 	return nil;
1432 }
1433 
1434 // convert a header line
1435 String*
stringconvert(String * s,char * uneaten,int len)1436 stringconvert(String *s, char *uneaten, int len)
1437 {
1438 	char *token, *p, *e;
1439 
1440 	s = s_reset(s);
1441 	p = uneaten;
1442 	for(e = p+len; p < e; ){
1443 		while(*p++ == '=' && (token = rfc2047start(uneaten, p))){
1444 			s_nappend(s, uneaten, token-uneaten);
1445 			if(rfc2047convert(s, token, p - token) < 0)
1446 				s_nappend(s, token, p - token);
1447 			uneaten = p;
1448 			for(; p<e && isspace(*p);)
1449 				p++;
1450 			if(p+2 < e && p[0] == '=' && p[1] == '?')
1451 				uneaten = p;	// paste
1452 		}
1453 	}
1454 	if(p > uneaten)
1455 		s_nappend(s, uneaten, p-uneaten);
1456 	return s;
1457 }
1458 
1459 int
readheader(Message * m,char * buf,int off,int cnt)1460 readheader(Message *m, char *buf, int off, int cnt)
1461 {
1462 	char *p, *e;
1463 	int n, ns;
1464 	char *to = buf;
1465 	String *s;
1466 
1467 	p = m->header;
1468 	e = m->hend;
1469 	s = nil;
1470 
1471 	// copy in good headers
1472 	while(cnt > 0 && p < e){
1473 		n = hdrlen(p, e);
1474 		if(ignore(p)){
1475 			p += n;
1476 			continue;
1477 		}
1478 
1479 		// rfc2047 processing
1480 		s = stringconvert(s, p, n);
1481 		ns = s_len(s);
1482 		if(off > 0){
1483 			if(ns <= off){
1484 				off -= ns;
1485 				p += n;
1486 				continue;
1487 			}
1488 			ns -= off;
1489 		}
1490 		if(ns > cnt)
1491 			ns = cnt;
1492 		memmove(to, s_to_c(s)+off, ns);
1493 		to += ns;
1494 		p += n;
1495 		cnt -= ns;
1496 		off = 0;
1497 	}
1498 
1499 	s_free(s);
1500 	return to - buf;
1501 }
1502 
1503 int
headerlen(Message * m)1504 headerlen(Message *m)
1505 {
1506 	char buf[1024];
1507 	int i, n;
1508 
1509 	if(m->hlen >= 0)
1510 		return m->hlen;
1511 	for(n = 0; ; n += i){
1512 		i = readheader(m, buf, n, sizeof(buf));
1513 		if(i <= 0)
1514 			break;
1515 	}
1516 	m->hlen = n;
1517 	return n;
1518 }
1519 
1520 QLock hashlock;
1521 
1522 uint
hash(ulong ppath,char * name)1523 hash(ulong ppath, char *name)
1524 {
1525 	uchar *p;
1526 	uint h;
1527 
1528 	h = 0;
1529 	for(p = (uchar*)name; *p; p++)
1530 		h = h*7 + *p;
1531 	h += ppath;
1532 
1533 	return h % Hsize;
1534 }
1535 
1536 Hash*
hlook(ulong ppath,char * name)1537 hlook(ulong ppath, char *name)
1538 {
1539 	int h;
1540 	Hash *hp;
1541 
1542 	qlock(&hashlock);
1543 	h = hash(ppath, name);
1544 	for(hp = htab[h]; hp != nil; hp = hp->next)
1545 		if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
1546 			qunlock(&hashlock);
1547 			return hp;
1548 		}
1549 	qunlock(&hashlock);
1550 	return nil;
1551 }
1552 
1553 void
henter(ulong ppath,char * name,Qid qid,Message * m,Mailbox * mb)1554 henter(ulong ppath, char *name, Qid qid, Message *m, Mailbox *mb)
1555 {
1556 	int h;
1557 	Hash *hp, **l;
1558 
1559 	qlock(&hashlock);
1560 	h = hash(ppath, name);
1561 	for(l = &htab[h]; *l != nil; l = &(*l)->next){
1562 		hp = *l;
1563 		if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
1564 			hp->m = m;
1565 			hp->mb = mb;
1566 			hp->qid = qid;
1567 			qunlock(&hashlock);
1568 			return;
1569 		}
1570 	}
1571 
1572 	*l = hp = emalloc(sizeof(*hp));
1573 	hp->m = m;
1574 	hp->mb = mb;
1575 	hp->qid = qid;
1576 	hp->name = name;
1577 	hp->ppath = ppath;
1578 	qunlock(&hashlock);
1579 }
1580 
1581 void
hfree(ulong ppath,char * name)1582 hfree(ulong ppath, char *name)
1583 {
1584 	int h;
1585 	Hash *hp, **l;
1586 
1587 	qlock(&hashlock);
1588 	h = hash(ppath, name);
1589 	for(l = &htab[h]; *l != nil; l = &(*l)->next){
1590 		hp = *l;
1591 		if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
1592 			hp->mb = nil;
1593 			*l = hp->next;
1594 			free(hp);
1595 			break;
1596 		}
1597 	}
1598 	qunlock(&hashlock);
1599 }
1600 
1601 int
hashmboxrefs(Mailbox * mb)1602 hashmboxrefs(Mailbox *mb)
1603 {
1604 	int h;
1605 	Hash *hp;
1606 	int refs = 0;
1607 
1608 	qlock(&hashlock);
1609 	for(h = 0; h < Hsize; h++){
1610 		for(hp = htab[h]; hp != nil; hp = hp->next)
1611 			if(hp->mb == mb)
1612 				refs++;
1613 	}
1614 	qunlock(&hashlock);
1615 	return refs;
1616 }
1617 
1618 void
checkmboxrefs(void)1619 checkmboxrefs(void)
1620 {
1621 	int f, refs;
1622 	Mailbox *mb;
1623 
1624 	qlock(&mbllock);
1625 	for(mb=mbl; mb; mb=mb->next){
1626 		qlock(mb);
1627 		refs = (f=fidmboxrefs(mb))+1;
1628 		if(refs != mb->refs){
1629 			fprint(2, "mbox %s %s ref mismatch actual %d (%d+1) expected %d\n", mb->name, mb->path, refs, f, mb->refs);
1630 			abort();
1631 		}
1632 		qunlock(mb);
1633 	}
1634 	qunlock(&mbllock);
1635 }
1636 
1637 void
post(char * name,char * envname,int srvfd)1638 post(char *name, char *envname, int srvfd)
1639 {
1640 	int fd;
1641 	char buf[32];
1642 
1643 	fd = create(name, OWRITE, 0600);
1644 	if(fd < 0)
1645 		error("post failed");
1646 	sprint(buf, "%d",srvfd);
1647 	if(write(fd, buf, strlen(buf)) != strlen(buf))
1648 		error("srv write");
1649 	close(fd);
1650 	putenv(envname, name);
1651 }
1652