Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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*
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*
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
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*
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*
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*
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
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
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
530
quickfree(Quick *q)
531
{
532
	if(q->pat != nil)
533
		free(q->pat);
534
	q->pat = nil;
535
}
536
 
537
char *
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
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*
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*
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
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*
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*
711
fsauth(Fs *, Fcall *)
712
{
713
	return "searchfs: authentication not required";
714
}
715
 
716
char*
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*
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 *
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 *
824
fscreate(Fs *, Fcall *)
825
{
826
	return Eperm;
827
}
828
 
829
char*
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*
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 *
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 *
918
fsremove(Fs *, Fcall *)
919
{
920
	return Eperm;
921
}
922
 
923
char *
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 *
940
fswstat(Fs *, Fcall *)
941
{
942
	return Eperm;
943
}
944
 
945
int
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 *
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
1016
toupper(int c)
1017
{
1018
	if(c >= 'a' && c <= 'z')
1019
		c += 'A' - 'a';
1020
	return c;
1021
}
1022
 
1023
int
1024
tolower(int c)
1025
{
1026
	if(c >= 'A' && c <= 'Z')
1027
		c += 'a' - 'A';
1028
	return c;
1029
}
1030
 
1031
void
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 *
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
1059
usage(void)
1060
{
1061
	fprint(2, "usage: searchfs [-m mountpoint] [-s srvfile] database\n");
1062
	exits("usage");
1063
}