xref: /plan9/sys/src/cmd/aux/searchfs.c (revision b85a83648eec38fe82b6f00adfd7828ceec5ee8d)
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 
6 /*
7  * caveat:
8  * this stuff is only meant to work for ascii databases
9  */
10 
11 typedef struct Fid Fid;
12 typedef struct Fs Fs;
13 typedef struct Quick Quick;
14 typedef struct Match Match;
15 typedef struct Search Search;
16 
17 enum
18 {
19 	OPERM	= 0x3,		/* mask of all permission types in open mode */
20 	Nfidhash	= 32,
21 
22 	/*
23 	 * qids
24 	 */
25 	Qroot	= 1,
26 	Qsearch	= 2,
27 	Qstats	= 3,
28 };
29 
30 /*
31  * boyer-moore quick string matching
32  */
33 struct Quick
34 {
35 	char	*pat;
36 	char	*up;		/* match string for upper case of pat */
37 	int	len;		/* of pat (and up) -1; used for fast search */
38 	uchar 	jump[256];	/* jump index table */
39 	int	miss;		/* amount to jump if we falsely match the last char */
40 };
41 extern void	quickmk(Quick*, char*, int);
42 extern void	quickfree(Quick*);
43 extern char*	quicksearch(Quick*, char*, char*);
44 
45 /*
46  * exact matching of a search string
47  */
48 struct Match
49 {
50 	Match	*next;
51 	char	*pat;				/* null-terminated search string */
52 	char	*up;				/* upper case of pat */
53 	int	len;				/* length of both pat and up */
54 	int	(*op)(Match*, char*, char*);		/* method for this partiticular search */
55 };
56 
57 struct Search
58 {
59 	Quick		quick;		/* quick match */
60 	Match		*match;		/* exact matches */
61 	int		skip;		/* number of matches to skip */
62 };
63 
64 extern char*	searchsearch(Search*, char*, char*, int*);
65 extern Search*	searchparse(char*, char*);
66 extern void	searchfree(Search*);
67 
68 struct Fid
69 {
70 	Lock;
71 	Fid	*next;
72 	Fid	**last;
73 	uint	fid;
74 	int	ref;		/* number of fcalls using the fid */
75 	int	attached;		/* fid has beed attached or cloned and not clunked */
76 
77 	int	open;
78 	Qid	qid;
79 	Search	*search;		/* search patterns */
80 	char	*where;		/* current location in the database */
81 	int	n;		/* number of bytes left in found item */
82 };
83 
84 int			dostat(int, uchar*, int);
85 void*			emalloc(uint);
86 void			fatal(char*, ...);
87 Match*			mkmatch(Match*, int(*)(Match*, char*, char*), char*);
88 Match*			mkstrmatch(Match*, char*);
89 char*		nextsearch(char*, char*, char**, char**);
90 int			strlook(Match*, char*, char*);
91 char*			strndup(char*, int);
92 int			tolower(int);
93 int			toupper(int);
94 char*			urlunesc(char*, char*);
95 void			usage(void);
96 
97 struct Fs
98 {
99 	Lock;			/* for fids */
100 
101 	Fid	*hash[Nfidhash];
102 	uchar	statbuf[1024];	/* plenty big enough */
103 };
104 extern	void	fsrun(Fs*, int);
105 extern	Fid*	getfid(Fs*, uint);
106 extern	Fid*	mkfid(Fs*, uint);
107 extern	void	putfid(Fs*, Fid*);
108 extern	char*	fsversion(Fs*, Fcall*);
109 extern	char*	fsauth(Fs*, Fcall*);
110 extern	char*	fsattach(Fs*, Fcall*);
111 extern	char*	fswalk(Fs*, Fcall*);
112 extern	char*	fsopen(Fs*, Fcall*);
113 extern	char*	fscreate(Fs*, Fcall*);
114 extern	char*	fsread(Fs*, Fcall*);
115 extern	char*	fswrite(Fs*, Fcall*);
116 extern	char*	fsclunk(Fs*, Fcall*);
117 extern	char*	fsremove(Fs*, Fcall*);
118 extern	char*	fsstat(Fs*, Fcall*);
119 extern	char*	fswstat(Fs*, Fcall*);
120 
121 char	*(*fcalls[])(Fs*, Fcall*) =
122 {
123 	[Tversion]		fsversion,
124 	[Tattach]	fsattach,
125 	[Tauth]	fsauth,
126 	[Twalk]		fswalk,
127 	[Topen]		fsopen,
128 	[Tcreate]	fscreate,
129 	[Tread]		fsread,
130 	[Twrite]	fswrite,
131 	[Tclunk]	fsclunk,
132 	[Tremove]	fsremove,
133 	[Tstat]		fsstat,
134 	[Twstat]	fswstat
135 };
136 
137 char	Eperm[] =	"permission denied";
138 char	Enotdir[] =	"not a directory";
139 char	Enotexist[] =	"file does not exist";
140 char	Eisopen[] = 	"file already open for I/O";
141 char	Einuse[] =	"fid is already in use";
142 char	Enofid[] = 	"no such fid";
143 char	Enotopen[] =	"file is not open";
144 char	Ebadsearch[] =	"bad search string";
145 
146 Fs	fs;
147 char	*database;
148 char	*edatabase;
149 int	messagesize = 8192+IOHDRSZ;
150 void
main(int argc,char ** argv)151 main(int argc, char **argv)
152 {
153 	Dir *d;
154 	char buf[12], *mnt, *srv;
155 	int fd, p[2], n;
156 
157 	mnt = "/tmp";
158 	srv = nil;
159 	ARGBEGIN{
160 		case 's':
161 			srv = ARGF();
162 			mnt = nil;
163 			break;
164 		case 'm':
165 			mnt = ARGF();
166 			break;
167 	}ARGEND
168 
169 	fmtinstall('F', fcallfmt);
170 
171 	if(argc != 1)
172 		usage();
173 	d = nil;
174 	fd = open(argv[0], OREAD);
175 	if(fd < 0 || (d=dirfstat(fd)) == nil)
176 		fatal("can't open %s: %r", argv[0]);
177 	n = d->length;
178 	free(d);
179 	if(n == 0)
180 		fatal("zero length database %s", argv[0]);
181 	database = emalloc(n);
182 	if(read(fd, database, n) != n)
183 		fatal("can't read %s: %r", argv[0]);
184 	close(fd);
185 	edatabase = database + n;
186 
187 	if(pipe(p) < 0)
188 		fatal("pipe failed");
189 
190 	switch(rfork(RFPROC|RFMEM|RFNOTEG|RFNAMEG)){
191 	case 0:
192 		fsrun(&fs, p[0]);
193 		exits(nil);
194 	case -1:
195 		fatal("fork failed");
196 	}
197 
198 	if(mnt == nil){
199 		if(srv == nil)
200 			usage();
201 		fd = create(srv, OWRITE, 0666);
202 		if(fd < 0){
203 			remove(srv);
204 			fd = create(srv, OWRITE, 0666);
205 			if(fd < 0){
206 				close(p[1]);
207 				fatal("create of %s failed", srv);
208 			}
209 		}
210 		sprint(buf, "%d", p[1]);
211 		if(write(fd, buf, strlen(buf)) < 0){
212 			close(p[1]);
213 			fatal("writing %s", srv);
214 		}
215 		close(p[1]);
216 		exits(nil);
217 	}
218 
219 	if(mount(p[1], -1, mnt, MREPL, "") < 0){
220 		close(p[1]);
221 		fatal("mount failed");
222 	}
223 	close(p[1]);
224 	exits(nil);
225 }
226 
227 /*
228  * execute the search
229  * do a quick match,
230  * isolate the line in which the occured,
231  * and try all of the exact matches
232  */
233 char*
searchsearch(Search * search,char * where,char * end,int * np)234 searchsearch(Search *search, char *where, char *end, int *np)
235 {
236 	Match *m;
237 	char *s, *e;
238 
239 	*np = 0;
240 	if(search == nil || where == nil)
241 		return nil;
242 	for(;;){
243 		s = quicksearch(&search->quick, where, end);
244 		if(s == nil)
245 			return nil;
246 		e = memchr(s, '\n', end - s);
247 		if(e == nil)
248 			e = end;
249 		else
250 			e++;
251 		while(s > where && s[-1] != '\n')
252 			s--;
253 		for(m = search->match; m != nil; m = m->next){
254 			if((*m->op)(m, s, e) == 0)
255 				break;
256 		}
257 
258 		if(m == nil){
259 			if(search->skip > 0)
260 				search->skip--;
261 			else{
262 				*np = e - s;
263 				return s;
264 			}
265 		}
266 
267 		where = e;
268 	}
269 }
270 
271 /*
272  * parse a search string of the form
273  * tag=val&tag1=val1...
274  */
275 Search*
searchparse(char * search,char * esearch)276 searchparse(char *search, char *esearch)
277 {
278 	Search *s;
279 	Match *m, *next, **last;
280 	char *tag, *val, *p;
281 	int ok;
282 
283 	s = emalloc(sizeof *s);
284 	s->match = nil;
285 
286 	/*
287 	 * acording to the http spec,
288 	 * repeated search queries are ingored.
289 	 * the last search given is performed on the original object
290 	 */
291 	while((p = memchr(s, '?', esearch - search)) != nil){
292 		search = p + 1;
293 	}
294 	while(search < esearch){
295 		search = nextsearch(search, esearch, &tag, &val);
296 		if(tag == nil)
297 			continue;
298 
299 		ok = 0;
300 		if(strcmp(tag, "skip") == 0){
301 			s->skip = strtoul(val, &p, 10);
302 			if(*p == 0)
303 				ok = 1;
304 		}else if(strcmp(tag, "search") == 0){
305 			s->match = mkstrmatch(s->match, val);
306 			ok = 1;
307 		}
308 		free(tag);
309 		free(val);
310 		if(!ok){
311 			searchfree(s);
312 			return nil;
313 		}
314 	}
315 
316 	if(s->match == nil){
317 		free(s);
318 		return nil;
319 	}
320 
321 	/*
322 	 * order the matches by probability of occurance
323 	 * first cut is just by length
324 	 */
325 	for(ok = 0; !ok; ){
326 		ok = 1;
327 		last = &s->match;
328 		for(m = *last; m && m->next; m = *last){
329 			if(m->next->len > m->len){
330 				next = m->next;
331 				m->next = next->next;
332 				next->next = m;
333 				*last = next;
334 				ok = 0;
335 			}
336 			last = &m->next;
337 		}
338 	}
339 
340 	/*
341 	 * convert the best search into a fast lookup
342 	 */
343 	m = s->match;
344 	s->match = m->next;
345 	quickmk(&s->quick, m->pat, 1);
346 	free(m->pat);
347 	free(m->up);
348 	free(m);
349 	return s;
350 }
351 
352 void
searchfree(Search * s)353 searchfree(Search *s)
354 {
355 	Match *m, *next;
356 
357 	if(s == nil)
358 		return;
359 	for(m = s->match; m; m = next){
360 		next = m->next;
361 		free(m->pat);
362 		free(m->up);
363 		free(m);
364 	}
365 	quickfree(&s->quick);
366 	free(s);
367 }
368 
369 char*
nextsearch(char * search,char * esearch,char ** tagp,char ** valp)370 nextsearch(char *search, char *esearch, char **tagp, char **valp)
371 {
372 	char *tag, *val;
373 
374 	*tagp = nil;
375 	*valp = nil;
376 	for(tag = search; search < esearch && *search != '='; search++)
377 		;
378 	if(search == esearch)
379 		return search;
380 	tag = urlunesc(tag, search);
381 	search++;
382 	for(val = search; search < esearch && *search != '&'; search++)
383 		;
384 	val = urlunesc(val, search);
385 	if(search != esearch)
386 		search++;
387 	*tagp = tag;
388 	*valp = val;
389 	return search;
390 }
391 
392 Match*
mkstrmatch(Match * m,char * pat)393 mkstrmatch(Match *m, char *pat)
394 {
395 	char *s;
396 
397 	for(s = pat; *s; s++){
398 		if(*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r'){
399 			*s = 0;
400 			m = mkmatch(m, strlook, pat);
401 			pat = s + 1;
402 		}else
403 			*s = tolower(*s);
404 	}
405 	return mkmatch(m, strlook, pat);
406 }
407 
408 Match*
mkmatch(Match * next,int (* op)(Match *,char *,char *),char * pat)409 mkmatch(Match *next, int (*op)(Match*, char*, char*), char *pat)
410 {
411 	Match *m;
412 	char *p;
413 	int n;
414 
415 	n = strlen(pat);
416 	if(n == 0)
417 		return next;
418 	m = emalloc(sizeof *m);
419 	m->op = op;
420 	m->len = n;
421 	m->pat = strdup(pat);
422 	m->up = strdup(pat);
423 	for(p = m->up; *p; p++)
424 		*p = toupper(*p);
425 	for(p = m->pat; *p; p++)
426 		*p = tolower(*p);
427 	m->next = next;
428 	return m;
429 }
430 
431 int
strlook(Match * m,char * str,char * e)432 strlook(Match *m, char *str, char *e)
433 {
434 	char *pat, *up, *s;
435 	int c, pc, fc, fuc, n;
436 
437 	n = m->len;
438 	fc = m->pat[0];
439 	fuc = m->up[0];
440 	for(; str + n <= e; str++){
441 		c = *str;
442 		if(c != fc && c != fuc)
443 			continue;
444 		s = str + 1;
445 		up = m->up + 1;
446 		for(pat = m->pat + 1; pc = *pat; pat++){
447 			c = *s;
448 			if(c != pc && c != *up)
449 				break;
450 			up++;
451 			s++;
452 		}
453 		if(pc == 0)
454 			return 1;
455 	}
456 	return 0;
457 }
458 
459 /*
460  * boyer-moore style pattern matching
461  * implements an exact match for ascii
462  * however, if mulitbyte upper-case and lower-case
463  * characters differ in length or in more than one byte,
464  * it only implements an approximate match
465  */
466 void
quickmk(Quick * q,char * spat,int ignorecase)467 quickmk(Quick *q, char *spat, int ignorecase)
468 {
469 	char *pat, *up;
470 	uchar *j;
471 	int ep, ea, cp, ca, i, c, n;
472 
473 	/*
474 	 * allocate the machine
475 	 */
476 	n = strlen(spat);
477 	if(n == 0){
478 		q->pat = nil;
479 		q->up = nil;
480 		q->len = -1;
481 		return;
482 	}
483 	pat = emalloc(2* n + 2);
484 	q->pat = pat;
485 	up = pat;
486 	if(ignorecase)
487 		up = pat + n + 1;
488 	q->up = up;
489 	while(c = *spat++){
490 		if(ignorecase){
491 			*up++ = toupper(c);
492 			c = tolower(c);
493 		}
494 		*pat++ = c;
495 	}
496 	pat = q->pat;
497 	up = q->up;
498 	pat[n] = up[n] = '\0';
499 
500 	/*
501 	 * make the skipping table
502 	 */
503 	if(n > 255)
504 		n = 255;
505 	j = q->jump;
506 	memset(j, n, 256);
507 	n--;
508 	q->len = n;
509 	for(i = 0; i <= n; i++){
510 		j[(uchar)pat[i]] = n - i;
511 		j[(uchar)up[i]] = n - i;
512 	}
513 
514 	/*
515 	 * find the minimum safe amount to skip
516 	 * if we match the last char but not the whole pat
517 	 */
518 	ep = pat[n];
519 	ea = up[n];
520 	for(i = n - 1; i >= 0; i--){
521 		cp = pat[i];
522 		ca = up[i];
523 		if(cp == ep || cp == ea || ca == ep || ca == ea)
524 			break;
525 	}
526 	q->miss = n - i;
527 }
528 
529 void
quickfree(Quick * q)530 quickfree(Quick *q)
531 {
532 	if(q->pat != nil)
533 		free(q->pat);
534 	q->pat = nil;
535 }
536 
537 char *
quicksearch(Quick * q,char * s,char * e)538 quicksearch(Quick *q, char *s, char *e)
539 {
540 	char *pat, *up, *m, *ee;
541 	uchar *j;
542 	int len, n, c, mc;
543 
544 	len = q->len;
545 	if(len < 0)
546 		return s;
547 	j = q->jump;
548 	pat = q->pat;
549 	up = q->up;
550 	s += len;
551 	ee = e - (len * 4 + 4);
552 	while(s < e){
553 		/*
554 		 * look for a match on the last char
555 		 */
556 		while(s < ee && (n = j[(uchar)*s])){
557 			s += n;
558 			s += j[(uchar)*s];
559 			s += j[(uchar)*s];
560 			s += j[(uchar)*s];
561 		}
562 		if(s >= e)
563 			return nil;
564 		while(n = j[(uchar)*s]){
565 			s += n;
566 			if(s >= e)
567 				return nil;
568 		}
569 
570 		/*
571 		 * does the string match?
572 		 */
573 		m = s - len;
574 		for(n = 0; c = pat[n]; n++){
575 			mc = *m++;
576 			if(c != mc && mc != up[n])
577 				break;
578 		}
579 		if(!c)
580 			return s - len;
581 		s += q->miss;
582 	}
583 	return nil;
584 }
585 
586 void
fsrun(Fs * fs,int fd)587 fsrun(Fs *fs, int fd)
588 {
589 	Fcall rpc;
590 	char *err;
591 	uchar *buf;
592 	int n;
593 
594 	buf = emalloc(messagesize);
595 	for(;;){
596 		/*
597 		 * reading from a pipe or a network device
598 		 * will give an error after a few eof reads
599 		 * however, we cannot tell the difference
600 		 * between a zero-length read and an interrupt
601 		 * on the processes writing to us,
602 		 * so we wait for the error
603 		 */
604 		n = read9pmsg(fd, buf, messagesize);
605 		if(n == 0)
606 			continue;
607 		if(n < 0)
608 			fatal("mount read");
609 
610 		rpc.data = (char*)buf + IOHDRSZ;
611 		if(convM2S(buf, n, &rpc) == 0)
612 			continue;
613 		// fprint(2, "recv: %F\n", &rpc);
614 
615 
616 		/*
617 		 * flushes are way too hard.
618 		 * a reply to the original message seems to work
619 		 */
620 		if(rpc.type == Tflush)
621 			continue;
622 		else if(rpc.type >= Tmax || !fcalls[rpc.type])
623 			err = "bad fcall type";
624 		else
625 			err = (*fcalls[rpc.type])(fs, &rpc);
626 		if(err){
627 			rpc.type = Rerror;
628 			rpc.ename = err;
629 		}else
630 			rpc.type++;
631 		n = convS2M(&rpc, buf, messagesize);
632 		// fprint(2, "send: %F\n", &rpc);
633 		if(write(fd, buf, n) != n)
634 			fatal("mount write");
635 	}
636 }
637 
638 Fid*
mkfid(Fs * fs,uint fid)639 mkfid(Fs *fs, uint fid)
640 {
641 	Fid *f;
642 	int h;
643 
644 	h = fid % Nfidhash;
645 	for(f = fs->hash[h]; f; f = f->next){
646 		if(f->fid == fid)
647 			return nil;
648 	}
649 
650 	f = emalloc(sizeof *f);
651 	f->next = fs->hash[h];
652 	if(f->next != nil)
653 		f->next->last = &f->next;
654 	f->last = &fs->hash[h];
655 	fs->hash[h] = f;
656 
657 	f->fid = fid;
658 	f->ref = 1;
659 	f->attached = 1;
660 	f->open = 0;
661 	return f;
662 }
663 
664 Fid*
getfid(Fs * fs,uint fid)665 getfid(Fs *fs, uint fid)
666 {
667 	Fid *f;
668 	int h;
669 
670 	h = fid % Nfidhash;
671 	for(f = fs->hash[h]; f; f = f->next){
672 		if(f->fid == fid){
673 			if(f->attached == 0)
674 				break;
675 			f->ref++;
676 			return f;
677 		}
678 	}
679 	return nil;
680 }
681 
682 void
putfid(Fs *,Fid * f)683 putfid(Fs *, Fid *f)
684 {
685 	f->ref--;
686 	if(f->ref == 0 && f->attached == 0){
687 		*f->last = f->next;
688 		if(f->next != nil)
689 			f->next->last = f->last;
690 		if(f->search != nil)
691 			searchfree(f->search);
692 		free(f);
693 	}
694 }
695 
696 char*
fsversion(Fs *,Fcall * rpc)697 fsversion(Fs *, Fcall *rpc)
698 {
699 	if(rpc->msize < 256)
700 		return "version: message size too small";
701 	if(rpc->msize > messagesize)
702 		rpc->msize = messagesize;
703 	messagesize = rpc->msize;
704 	if(strncmp(rpc->version, "9P2000", 6) != 0)
705 		return "unrecognized 9P version";
706 	rpc->version = "9P2000";
707 	return nil;
708 }
709 
710 char*
fsauth(Fs *,Fcall *)711 fsauth(Fs *, Fcall *)
712 {
713 	return "searchfs: authentication not required";
714 }
715 
716 char*
fsattach(Fs * fs,Fcall * rpc)717 fsattach(Fs *fs, Fcall *rpc)
718 {
719 	Fid *f;
720 
721 	f = mkfid(fs, rpc->fid);
722 	if(f == nil)
723 		return Einuse;
724 	f->open = 0;
725 	f->qid.type = QTDIR;
726 	f->qid.path = Qroot;
727 	f->qid.vers = 0;
728 	rpc->qid = f->qid;
729 	putfid(fs, f);
730 	return nil;
731 }
732 
733 char*
fswalk(Fs * fs,Fcall * rpc)734 fswalk(Fs *fs, Fcall *rpc)
735 {
736 	Fid *f, *nf;
737 	int nqid, nwname, type;
738 	char *err, *name;
739 	ulong path;
740 
741 	f = getfid(fs, rpc->fid);
742 	if(f == nil)
743 		return Enofid;
744 	nf = nil;
745 	if(rpc->fid != rpc->newfid){
746 		nf = mkfid(fs, rpc->newfid);
747 		if(nf == nil){
748 			putfid(fs, f);
749 			return Einuse;
750 		}
751 		nf->qid = f->qid;
752 		putfid(fs, f);
753 		f = nf;	/* walk f */
754 	}
755 
756 	err = nil;
757 	path = f->qid.path;
758 	nwname = rpc->nwname;
759 	for(nqid=0; nqid<nwname; nqid++){
760 		if(path != Qroot){
761 			err = Enotdir;
762 			break;
763 		}
764 		name = rpc->wname[nqid];
765 		if(strcmp(name, "search") == 0){
766 			type = QTFILE;
767 			path = Qsearch;
768 		}else if(strcmp(name, "stats") == 0){
769 			type = QTFILE;
770 			path = Qstats;
771 		}else if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0){
772 			type = QTDIR;
773 			path = path;
774 		}else{
775 			err = Enotexist;
776 			break;
777 		}
778 		rpc->wqid[nqid] = (Qid){path, 0, type};
779 	}
780 
781 	if(nwname > 0){
782 		if(nf != nil && nqid < nwname)
783 			nf->attached = 0;
784 		if(nqid == nwname)
785 			f->qid = rpc->wqid[nqid-1];
786 	}
787 
788 	putfid(fs, f);
789 	rpc->nwqid = nqid;
790 	f->open = 0;
791 	return err;
792 }
793 
794 char *
fsopen(Fs * fs,Fcall * rpc)795 fsopen(Fs *fs, Fcall *rpc)
796 {
797 	Fid *f;
798 	int mode;
799 
800 	f = getfid(fs, rpc->fid);
801 	if(f == nil)
802 		return Enofid;
803 	if(f->open){
804 		putfid(fs, f);
805 		return Eisopen;
806 	}
807 	mode = rpc->mode & OPERM;
808 	if(mode == OEXEC
809 	|| f->qid.path == Qroot && (mode == OWRITE || mode == ORDWR)){
810 		putfid(fs, f);
811 		return Eperm;
812 	}
813 	f->open = 1;
814 	f->where = nil;
815 	f->n = 0;
816 	f->search = nil;
817 	rpc->qid = f->qid;
818 	rpc->iounit = messagesize-IOHDRSZ;
819 	putfid(fs, f);
820 	return nil;
821 }
822 
823 char *
fscreate(Fs *,Fcall *)824 fscreate(Fs *, Fcall *)
825 {
826 	return Eperm;
827 }
828 
829 char*
fsread(Fs * fs,Fcall * rpc)830 fsread(Fs *fs, Fcall *rpc)
831 {
832 	Fid *f;
833 	int n, off, count, len;
834 
835 	f = getfid(fs, rpc->fid);
836 	if(f == nil)
837 		return Enofid;
838 	if(!f->open){
839 		putfid(fs, f);
840 		return Enotopen;
841 	}
842 	count = rpc->count;
843 	off = rpc->offset;
844 	rpc->count = 0;
845 	if(f->qid.path == Qroot){
846 		if(off > 0)
847 			rpc->count = 0;
848 		else
849 			rpc->count = dostat(Qsearch, (uchar*)rpc->data, count);
850 		putfid(fs, f);
851 		if(off == 0 && rpc->count <= BIT16SZ)
852 			return "directory read count too small";
853 		return nil;
854 	}
855 	if(f->qid.path == Qstats){
856 		len = 0;
857 	}else{
858 		for(len = 0; len < count; len += n){
859 			if(f->where == nil || f->search == nil)
860 				break;
861 			if(f->n == 0)
862 				f->where = searchsearch(f->search, f->where, edatabase, &f->n);
863 			n = f->n;
864 			if(n != 0){
865 				if(n > count-len)
866 					n = count-len;
867 				memmove(rpc->data+len, f->where, n);
868 				f->where += n;
869 				f->n -= n;
870 			}
871 		}
872 	}
873 	putfid(fs, f);
874 	rpc->count = len;
875 	return nil;
876 }
877 
878 char*
fswrite(Fs * fs,Fcall * rpc)879 fswrite(Fs *fs, Fcall *rpc)
880 {
881 	Fid *f;
882 
883 	f = getfid(fs, rpc->fid);
884 	if(f == nil)
885 		return Enofid;
886 	if(!f->open || f->qid.path != Qsearch){
887 		putfid(fs, f);
888 		return Enotopen;
889 	}
890 
891 	if(f->search != nil)
892 		searchfree(f->search);
893 	f->search = searchparse(rpc->data, rpc->data + rpc->count);
894 	if(f->search == nil){
895 		putfid(fs, f);
896 		return Ebadsearch;
897 	}
898 	f->where = database;
899 	f->n = 0;
900 	putfid(fs, f);
901 	return nil;
902 }
903 
904 char *
fsclunk(Fs * fs,Fcall * rpc)905 fsclunk(Fs *fs, Fcall *rpc)
906 {
907 	Fid *f;
908 
909 	f = getfid(fs, rpc->fid);
910 	if(f != nil){
911 		f->attached = 0;
912 		putfid(fs, f);
913 	}
914 	return nil;
915 }
916 
917 char *
fsremove(Fs *,Fcall *)918 fsremove(Fs *, Fcall *)
919 {
920 	return Eperm;
921 }
922 
923 char *
fsstat(Fs * fs,Fcall * rpc)924 fsstat(Fs *fs, Fcall *rpc)
925 {
926 	Fid *f;
927 
928 	f = getfid(fs, rpc->fid);
929 	if(f == nil)
930 		return Enofid;
931 	rpc->stat = fs->statbuf;
932 	rpc->nstat = dostat(f->qid.path, rpc->stat, sizeof fs->statbuf);
933 	putfid(fs, f);
934 	if(rpc->nstat <= BIT16SZ)
935 		return "stat count too small";
936 	return nil;
937 }
938 
939 char *
fswstat(Fs *,Fcall *)940 fswstat(Fs *, Fcall *)
941 {
942 	return Eperm;
943 }
944 
945 int
dostat(int path,uchar * buf,int nbuf)946 dostat(int path, uchar *buf, int nbuf)
947 {
948 	Dir d;
949 
950 	switch(path){
951 	case Qroot:
952 		d.name = ".";
953 		d.mode = DMDIR|0555;
954 		d.qid.type = QTDIR;
955 		break;
956 	case Qsearch:
957 		d.name = "search";
958 		d.mode = 0666;
959 		d.qid.type = QTFILE;
960 		break;
961 	case Qstats:
962 		d.name = "stats";
963 		d.mode = 0666;
964 		d.qid.type = QTFILE;
965 		break;
966 	}
967 	d.qid.path = path;
968 	d.qid.vers = 0;
969 	d.length = 0;
970 	d.uid = d.gid = d.muid = "none";
971 	d.atime = d.mtime = time(nil);
972 	return convD2M(&d, buf, nbuf);
973 }
974 
975 char *
urlunesc(char * s,char * e)976 urlunesc(char *s, char *e)
977 {
978 	char *t, *v;
979 	int c, n;
980 
981 	v = emalloc((e - s) + 1);
982 	for(t = v; s < e; s++){
983 		c = *s;
984 		if(c == '%'){
985 			if(s + 2 >= e)
986 				break;
987 			n = s[1];
988 			if(n >= '0' && n <= '9')
989 				n = n - '0';
990 			else if(n >= 'A' && n <= 'F')
991 				n = n - 'A' + 10;
992 			else if(n >= 'a' && n <= 'f')
993 				n = n - 'a' + 10;
994 			else
995 				break;
996 			c = n;
997 			n = s[2];
998 			if(n >= '0' && n <= '9')
999 				n = n - '0';
1000 			else if(n >= 'A' && n <= 'F')
1001 				n = n - 'A' + 10;
1002 			else if(n >= 'a' && n <= 'f')
1003 				n = n - 'a' + 10;
1004 			else
1005 				break;
1006 			s += 2;
1007 			c = c * 16 + n;
1008 		}
1009 		*t++ = c;
1010 	}
1011 	*t = 0;
1012 	return v;
1013 }
1014 
1015 int
toupper(int c)1016 toupper(int c)
1017 {
1018 	if(c >= 'a' && c <= 'z')
1019 		c += 'A' - 'a';
1020 	return c;
1021 }
1022 
1023 int
tolower(int c)1024 tolower(int c)
1025 {
1026 	if(c >= 'A' && c <= 'Z')
1027 		c += 'a' - 'A';
1028 	return c;
1029 }
1030 
1031 void
fatal(char * fmt,...)1032 fatal(char *fmt, ...)
1033 {
1034 	va_list arg;
1035 	char buf[1024];
1036 
1037 	write(2, "searchfs: ", 8);
1038 	va_start(arg, fmt);
1039 	vseprint(buf, buf+1024, fmt, arg);
1040 	va_end(arg);
1041 	write(2, buf, strlen(buf));
1042 	write(2, "\n", 1);
1043 	exits(fmt);
1044 }
1045 
1046 void *
emalloc(uint n)1047 emalloc(uint n)
1048 {
1049 	void *p;
1050 
1051 	p = malloc(n);
1052 	if(p == nil)
1053 		fatal("out of memory");
1054 	memset(p, 0, n);
1055 	return p;
1056 }
1057 
1058 void
usage(void)1059 usage(void)
1060 {
1061 	fprint(2, "usage: searchfs [-m mountpoint] [-s srvfile] database\n");
1062 	exits("usage");
1063 }
1064