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