plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

fs.c (28255B)


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