]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/hgfs/fs.c
upas/fs: fix more locking bugs, remove debugging clutter, remove planb mbox code
[plan9front.git] / sys / src / cmd / hgfs / fs.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "dat.h"
5 #include "fns.h"
6
7 #include <ctype.h>
8 #include <flate.h>
9 #include <auth.h>
10 #include <fcall.h>
11 #include <9p.h>
12
13 enum {
14         Qroot,
15                 Qrev,
16                         Qrev0,
17                         Qrev1,
18                         Qrev2,
19                         Qlog,
20                         Qwho,
21                         Qwhy,
22                         Qfiles,
23                         Qchanges,
24                                 Qtree,
25                                 Qtreerev,
26 };
27
28 static char *nametab[] = {
29         "/",
30                 nil,
31                         "rev",
32                         "rev1",
33                         "rev2",
34                         "log",
35                         "who",
36                         "why",
37                         "files",
38                         "changes",
39                                 nil,
40                                 nil,
41 };
42
43 static Revlog changelog;
44 static Revlog manifest;
45 static Revlog *revlogs;
46 static int nfreerevlogs = 0;
47
48 static char workdir[MAXPATH];
49 static int mangle = 0;
50
51 static Revlog*
52 getrevlog(Revnode *nd)
53 {
54         char buf[MAXPATH];
55         Revlog *rl, **link;
56         int mang;
57
58         mang = mangle;
59 Again:
60         nodepath(seprint(buf, buf+sizeof(buf), "%s/.hg/store/data", workdir),
61                 buf+sizeof(buf), nd, mang);
62         link = &revlogs;
63         while(rl = *link){
64                 if(strcmp(buf, rl->path) == 0){
65                         if(rl->ref == 0) nfreerevlogs--;
66                         break;
67                 }
68                 if(nfreerevlogs > 8 && rl->ref == 0){
69                         *link = rl->next;
70                         nfreerevlogs--;
71                         revlogclose(rl);
72                         free(rl);
73                         continue;
74                 }
75                 link = &rl->next;
76         }
77         if(rl == nil){
78                 rl = emalloc9p(sizeof(*rl));
79                 memset(rl, 0, sizeof(*rl));
80                 if(revlogopen(rl, buf, OREAD) < 0){
81                         free(rl);
82                         if(mang++ == 0)
83                                 goto Again;
84                         return nil;
85                 }
86                 rl->next = revlogs;
87                 revlogs = rl;
88                 if(mang)
89                         mangle = 1;
90         } else
91                 revlogupdate(rl);
92         incref(rl);
93         return rl;
94 }
95
96 static void
97 closerevlog(Revlog *rl)
98 {
99         if(rl != nil && decref(rl) == 0)
100                 nfreerevlogs++;
101 }
102
103 static Revinfo*
104 getrevinfo(int rev)
105 {
106         Revinfo *ri;
107
108         if(rev < 0 || rev >= changelog.nmap)
109                 return nil;
110         if(ri = changelog.map[rev].aux)
111                 return ri;
112         if(ri = loadrevinfo(&changelog, rev))
113                 changelog.map[rev].aux = ri;
114         return ri;
115 }
116
117 static Revtree*
118 getrevtree(Revtree *(*fn)(Revlog *, Revlog *, Revinfo *), Revinfo *ri)
119 {
120         static ulong gen;
121         static struct {
122                 ulong g;
123                 void *f;
124                 Revinfo *i;
125                 Revtree *t;
126         } cache[4];
127         Revtree *rt;
128         int i, j;
129
130         for(i=j=0; i<nelem(cache); i++){
131                 if(cache[i].t == nil){
132                         j = i;
133                         continue;
134                 }
135                 if(cache[i].f == fn && cache[i].i == ri){
136                         cache[i].g = ++gen;
137                         rt = cache[i].t;
138                         goto found;
139                 }
140                 if(cache[j].t && cache[i].g < cache[j].g)
141                         j = i;
142         }
143         if((rt = (*fn)(&changelog, &manifest, ri)) == nil)
144                 return nil;
145
146         closerevtree(cache[j].t);
147
148         cache[j].g = ++gen;
149         cache[j].f = fn;
150         cache[j].i = ri;
151         cache[j].t = rt;
152
153 found:
154         incref(rt);
155         return rt;
156 }
157
158 static char*
159 fsmkuid(char *s)
160 {
161         if(s){
162                 char *x;
163
164                 while(x = strchr(s, '<'))
165                         s = x+1;
166                 s = estrdup9p(s);
167                 if(x = strchr(s, '>'))
168                         *x = 0;
169                 if(x = strchr(s, '@'))
170                         *x = 0;
171                 if(x = strchr(s, '\n'))
172                         *x = 0;
173         }
174         if(s == nil || *s == 0){
175                 free(s);
176                 s = estrdup9p("hgfs");
177         }
178         return s;
179 }
180
181 static void
182 fsmkqid(Qid *q, int level, void *aux)
183 {
184         Revnode *nd;
185         Revinfo *ri;
186
187         switch(level){
188         case Qroot:
189                 q->type = QTDIR;
190                 q->path = Qroot;
191                 q->vers = 0;
192                 break;
193         case Qrev:
194         case Qfiles:
195         case Qchanges:
196                 q->type = QTDIR;
197                 if(0){
198         case Qrev0:
199         case Qrev1:
200         case Qrev2:
201         case Qlog:
202         case Qwho:
203         case Qwhy:
204                 q->type = 0;
205                 }
206                 ri = aux;
207                 q->path = hash2qid(ri->chash) + (level - Qrev);
208                 q->vers = 0;
209                 break;
210         case Qtree:
211         case Qtreerev:
212                 nd = aux;
213                 if(level == Qtree && nd->down){
214                         q->type = QTDIR;
215                 } else {
216                         q->type = 0;
217                 }
218                 q->path = nd->path + (level - Qtree);
219                 q->vers = 0;
220                 break;
221         }
222 }
223
224 static char*
225 fsmkrevname(char *buf, int nbuf, int rev)
226 {
227         if(rev < 0 || rev >= changelog.nmap)
228                 return nil;
229         snprint(buf, nbuf, "%d.%H", rev, changelog.map[rev].hash);
230         return buf;
231 }
232
233 static void
234 fsmkdir(Dir *d, int level, void *aux)
235 {
236         char buf[64], *s;
237         Revnode *nd;
238         Revinfo *ri;
239         int rev;
240
241         memset(d, 0, sizeof(*d));
242
243         fsmkqid(&d->qid, level, aux);
244
245         d->mode = 0444;
246         if(d->qid.type & QTDIR)
247                 d->mode |= DMDIR | 0111;
248
249         ri = nil;
250         switch(level){
251         case Qroot:
252                 goto Namegen;
253         case Qrev:
254         case Qrev0:
255         case Qrev1:
256         case Qrev2:
257                 ri = aux;
258         Revgen:
259                 rev = hashrev(&changelog, ri->chash);
260                 if(level == Qrev1)
261                         rev = changelog.map[rev].p1rev;
262                 else if(level == Qrev2)
263                         rev = changelog.map[rev].p2rev;
264                 s = fsmkrevname(buf, sizeof(buf), rev);
265                 if(level == Qrev){
266                         d->name = estrdup9p(s);
267                         break;
268                 }
269                 goto Strgen;
270         case Qlog:
271                 ri = aux;
272                 d->length = ri->loglen;
273                 goto Namegen;
274         case Qwho:
275                 ri = aux;
276                 s = ri->who;
277                 goto Strgen;
278         case Qwhy:
279                 ri = aux;
280                 s = ri->why;
281         Strgen:
282                 d->length = s ? strlen(s)+1 : 0;
283                 if(level == Qtreerev)
284                         break;
285         case Qfiles:
286         case Qchanges:
287                 ri = aux;
288                 /* no break */
289         Namegen:
290                 d->name = estrdup9p(nametab[level]);
291                 break;
292         case Qtree:
293         case Qtreerev:
294                 nd = aux;
295                 if(level == Qtree && nd->mode == 'x')
296                         d->mode |= 0111;
297                 d->name = estrdup9p(nd->name);
298                 if(nd->hash){
299                         Revlog *rl;
300
301                         if((rl = getrevlog(nd)) == nil)
302                                 break;
303                         if((rev = hashrev(rl, nd->hash)) >= 0){
304                                 if(level == Qtree){
305                                         /*
306                                          * BUG: this is not correct. mercurial might
307                                          * prefix the data log with random \1\n escaped
308                                          * metadata strings (see fmetaheader()) and the flen
309                                          * *includes* the metadata part. we try to compensate
310                                          * for this once the revision got extracted and
311                                          * subtract the metadata header in fsstat().
312                                          */
313                                         d->length = rl->map[rev].flen;
314                                 }
315                                 ri = getrevinfo(rl->map[rev].linkrev);
316                         }
317                         closerevlog(rl);
318                         if(level == Qtreerev && ri)
319                                 goto Revgen;
320                 }
321                 break;
322         }
323
324         if(ri){
325                 d->atime = d->mtime = ri->when;
326                 d->muid = fsmkuid(ri->who);
327                 d->uid = fsmkuid(ri->who);
328         } else
329                 d->atime = d->mtime = time(0);
330
331         if(d->uid == nil)
332                 d->uid = fsmkuid(nil);
333         if(d->gid == nil)
334                 d->gid = fsmkuid(nil);
335         if(d->muid == nil)
336                 d->muid = fsmkuid(nil);
337 }
338
339 static void
340 fsattach(Req *r)
341 {
342         Revfile *rf;
343
344         if(r->ifcall.aname && r->ifcall.aname[0]){
345                 respond(r, "invalid attach specifier");
346                 return;
347         }
348         r->fid->qid.path = Qroot;
349         r->fid->qid.type = QTDIR;
350         r->fid->qid.vers = 0;
351         r->ofcall.qid = r->fid->qid;
352
353         rf = emalloc9p(sizeof(*rf));
354         rf->level = Qroot;
355         rf->info = nil;
356         rf->tree = nil;
357         rf->node = nil;
358         rf->rlog = nil;
359
360         rf->fd = -1;
361         rf->doff = 0;
362         rf->buf = nil;
363
364         r->fid->aux = rf;
365
366         respond(r, nil);
367 }
368
369 static void
370 fsstat(Req *r)
371 {
372         Revfile *rf;
373
374         rf = r->fid->aux;
375         if(rf->level < Qtree)
376                 fsmkdir(&r->d, rf->level,  rf->info);
377         else {
378                 fsmkdir(&r->d, rf->level,  rf->node);
379                 if(rf->level == Qtree)
380                         r->d.length -= rf->doff;
381         }
382         respond(r, nil);
383 }
384
385 static int
386 findrev(Revlog *rl, char *name)
387 {
388         uchar hash[HASHSZ];
389         int n, i, rev;
390         char *s;
391
392         if(strcmp(name, "tip") == 0)
393                 return rl->nmap-1;
394         rev = strtol(name, &s, 10);
395         if(s > name && (*s == 0 || ispunct(*s)))
396                 return rev;
397         rev = -1;
398         if(s = strchr(name, '.'))
399                 name = s+1;
400         if((n = hex2hash(name, hash)) > 0){
401                 for(i=0; i<rl->nmap; i++){
402                         if(memcmp(rl->map[i].hash, hash, n) == 0){
403                                 if(rev < 0)
404                                         rev = i;
405                                 else {
406                                         rev = -1;
407                                         break;
408                                 }
409                         }
410                 }
411         }
412         return rev;
413 }
414
415 static char*
416 fswalk1(Fid *fid, char *name, Qid *qid)
417 {
418         Revtree* (*loadfn)(Revlog *, Revlog *, Revinfo *);
419         char buf[MAXPATH], *sname;
420         Revnode *nd;
421         Revfile *rf;
422         int i, level;
423
424         if((fid->qid.type & QTDIR) == 0)
425                 return "walk in non-directory";
426
427         rf = fid->aux;
428         if(strcmp(name, "..") == 0){
429                 switch(rf->level){
430                 case Qroot:
431                         break;
432                 case Qrev:
433                         rf->info = nil;
434                         rf->level = Qroot;
435                         break;
436                 case Qfiles:
437                 case Qchanges:
438                         closerevtree(rf->tree);
439                         rf->tree = nil;
440                         rf->level = Qrev;
441                         break;
442                 case Qtree:
443                         closerevlog(rf->rlog);
444                         rf->rlog = nil;
445                         if((rf->node = rf->node->up) == rf->tree->root)
446                                 rf->level = rf->tree->level;
447                         break;
448                 }
449         } else {
450                 switch(rf->level){
451                 case Qroot:
452                         revlogupdate(&changelog);
453                         revlogupdate(&manifest);
454
455                         i = findrev(&changelog, name);
456                         if(rf->info = getrevinfo(i)){
457                                 rf->level = Qrev;
458                                 break;
459                         }
460                 Notfound:
461                         return "directory entry not found";
462                         break;
463                 case Qrev:
464                         for(i = Qrev+1; i < Qtree; i++){
465                                 if(nametab[i] == nil)
466                                         continue;
467                                 if(strcmp(name, nametab[i]) == 0)
468                                         break;
469                         }
470                         loadfn = nil;
471                         switch(i){
472                         case Qtree:
473                                 goto Notfound;
474                         case Qfiles:
475                                 loadfn = loadfilestree;
476                                 break;
477                         case Qchanges:
478                                 loadfn = loadchangestree;
479                                 break;
480                         }
481                         if(loadfn){
482                                 if((rf->tree = getrevtree(loadfn, rf->info)) == nil)
483                                         goto Notfound;
484                                 rf->node = rf->tree->root;
485                                 rf->tree->level = i;
486                         }
487                         rf->level = i;
488                         break;
489                 case Qtree:
490                 case Qfiles:
491                 case Qchanges:
492                         i = 0;
493                         level = Qtree;
494                         sname = name;
495                 Searchtree:
496                         for(nd = rf->node->down; nd; nd = nd->next)
497                                 if(strcmp(nd->name, sname) == 0)
498                                         break;
499                         if(nd == nil){
500                                 if(sname == name){
501                                         sname = strrchr(name, '.');
502                                         if((i = sname - name) > 0){
503                                                 sname++;
504                                                 if(strncmp(sname, "rev", 3) == 0){
505                                                         level = Qtreerev;
506                                                         sname += 3;
507                                                 }
508                                                 snprint(buf, sizeof(buf), "%.*s", i, name);
509                                                 if(*sname == 0)
510                                                         i = 0;
511                                                 else {
512                                                         i = strtol(sname, &sname, 10);
513                                                         if(i < 0 || *sname != '\0')
514                                                                 goto Notfound;
515                                                 }
516                                                 sname = buf;
517                                                 goto Searchtree;
518                                         }
519                                 }
520                                 goto Notfound;
521                         }
522                         if(nd->hash){
523                                 Revnode *nb;
524                                 int j;
525
526                                 if((rf->rlog = getrevlog(nd)) == nil)
527                                         goto Notfound;
528                                 j = hashrev(rf->rlog, nd->hash) - i;
529                                 if(i < 0 || j < 0 || j >= rf->rlog->nmap){
530                                         closerevlog(rf->rlog);
531                                         rf->rlog = nil;
532                                         goto Notfound;
533                                 }
534                                 for(nb = nd; nb; nb = nb->before)
535                                         if(hashrev(rf->rlog, nb->hash) == j)
536                                                 break;
537                                 if(nb == nil){
538                                         nb = mknode(nd->name, revhash(rf->rlog, j), nd->mode);
539                                         nb->up = nd->up;
540                                         nb->before = nd->before;
541                                         nd->before = nb;
542                                 }
543                                 nd = nb;
544                         } else if(name != sname)
545                                 goto Notfound;
546                         rf->node = nd;
547                         rf->level = level;
548                         break;
549                 }
550         }
551
552         if(rf->level < Qtree)
553                 fsmkqid(qid, rf->level, rf->info);
554         else
555                 fsmkqid(qid, rf->level, rf->node);
556         fid->qid = *qid;
557
558         return nil;
559 }
560
561 static char*
562 fsclone(Fid *oldfid, Fid *newfid)
563 {
564         Revfile *orf, *rf;
565
566         rf = nil;
567         if(orf = oldfid->aux){
568                 rf = emalloc9p(sizeof(*rf));
569                 *rf = *orf;
570                 if(rf->rlog)
571                         incref(rf->rlog);
572                 if(rf->tree)
573                         incref(rf->tree);
574                 if(rf->fd >= 0)
575                         rf->fd = dup(rf->fd, -1);
576                 if(rf->buf)
577                         rf->buf = estrdup9p(rf->buf);
578         }
579         newfid->aux = rf;
580         return nil;
581 }
582
583 static void
584 fsdestroyfid(Fid *fid)
585 {
586         Revfile *rf;
587
588         if(rf = fid->aux){
589                 closerevlog(rf->rlog);
590                 closerevtree(rf->tree);
591                 if(rf->fd >= 0)
592                         close(rf->fd);
593                 free(rf->buf);
594                 free(rf);
595         }
596 }
597
598 static void
599 fsopen(Req *r)
600 {
601         Revfile *rf;
602
603         rf = r->fid->aux;
604         switch(r->ifcall.mode & 3){
605         case OEXEC:
606                 if(rf->node == nil || rf->node->mode != 'x')
607                         break;
608         case OREAD:
609                 if(rf->level == Qlog){
610                         if((rf->fd = revlogopentemp(&changelog, hashrev(&changelog, rf->info->chash))) < 0){
611                                 responderror(r);
612                                 return;
613                         }
614                         rf->doff = rf->info->logoff;
615                 } else if(rf->level == Qtree && rf->node->down == nil){
616                         if((rf->fd = revlogopentemp(rf->rlog, hashrev(rf->rlog, rf->node->hash))) < 0){
617                                 responderror(r);
618                                 return;
619                         }
620                         rf->doff = fmetaheader(rf->fd);
621                 }
622                 respond(r, nil);
623                 return;
624         }
625         respond(r, "permission denied");
626 }
627
628 static int
629 rootgen(int i, Dir *d, void *)
630 {
631         Revinfo *ri;
632
633         if((ri = getrevinfo(i)) == nil)
634                 return -1;
635         fsmkdir(d, Qrev, ri);
636         return 0;
637 }
638
639 static int
640 revgen(int i, Dir *d, void *aux)
641 {
642         i += Qrev+1;
643         if(i >= Qtree)
644                 return -1;
645         fsmkdir(d, i, aux);
646         return 0;
647 }
648
649 static int
650 treegen(int i, Dir *d, void *aux)
651 {
652         Revnode *nd = aux;
653
654         while(i > 0 && nd){
655                 nd = nd->next;
656                 i--;
657         }
658         if(i || nd == nil)
659                 return -1;
660         fsmkdir(d, Qtree, nd);
661         return 0;
662 }
663
664 static void
665 fsread(Req *r)
666 {
667         char buf[MAXPATH], *s;
668         Revfile *rf;
669         int i, n;
670         vlong off;
671         int len;
672
673         off = r->ifcall.offset;
674         len = r->ifcall.count;
675
676         rf = r->fid->aux;
677         switch(rf->level){
678         case Qroot:
679                 if(off == 0){
680                         revlogupdate(&changelog);
681                         revlogupdate(&manifest);
682                 }
683                 dirread9p(r, rootgen, nil);
684                 respond(r, nil);
685                 return;
686         case Qrev:
687                 dirread9p(r, revgen, rf->info);
688                 respond(r, nil);
689                 return;
690         case Qrev0:
691         case Qrev1:
692         case Qrev2:
693                 s = nil;
694                 if(rf->buf)
695                         goto Strgen;
696                 i = hashrev(&changelog, rf->info->chash);
697                 if(rf->level == Qrev1)
698                         i = changelog.map[i].p1rev;
699                 else if(rf->level == Qrev2)
700                         i = changelog.map[i].p2rev;
701         Revgen:
702                 s = fsmkrevname(buf, sizeof(buf), i);
703                 goto Strgen;
704         case Qtreerev:
705                 if((i = hashrev(rf->rlog, rf->node->hash)) >= 0)
706                         i = rf->rlog->map[i].linkrev;
707                 goto Revgen;
708         case Qlog:
709                 if(off >= rf->info->loglen)
710                         len = 0;
711                 else if((off + len) >= rf->info->loglen)
712                         len = rf->info->loglen - off;
713                 goto Fdgen;
714         case Qwho:
715                 s = rf->info->who;
716                 goto Strgen;
717         case Qwhy:
718                 s = rf->info->why;
719         Strgen:
720                 if(rf->buf == nil)
721                         rf->buf = s ? smprint("%s\n", s) : estrdup9p("");
722                 readstr(r, rf->buf);
723                 respond(r, nil);
724                 return;
725         case Qtree:
726                 if(rf->node->down){
727         case Qfiles:
728         case Qchanges:
729                         dirread9p(r, treegen, rf->node->down);
730                         respond(r, nil);
731                         return;
732                 }
733         Fdgen:
734                 if(rf->fd < 0)
735                         break;
736                 if((n = pread(rf->fd, r->ofcall.data, len, off + rf->doff)) < 0){
737                         responderror(r);
738                         return;
739                 }
740                 r->ofcall.count = n;
741                 respond(r, nil);
742                 return;
743         }
744         respond(r, "bug in fsread");
745 }
746
747 Srv fs = 
748 {
749         .attach=fsattach,
750         .stat=fsstat,
751         .walk1=fswalk1,
752         .clone=fsclone,
753         .destroyfid=fsdestroyfid,
754         .open=fsopen,
755         .read=fsread,
756 };
757
758 void
759 usage(void)
760 {
761         fprint(2, "usage: %s [-D] [-m mtpt] [-s srv] [root]\n", argv0);
762         exits("usage");
763 }
764
765 void
766 main(int argc, char *argv[])
767 {
768         char *srv, *mtpt;
769         char buf[MAXPATH];
770
771         inflateinit();
772         fmtinstall('H', Hfmt);
773
774         srv = nil;
775         mtpt = "/mnt/hg";
776
777         ARGBEGIN {
778         case 'D':
779                 chatty9p++;
780                 break;
781         case 'm':
782                 mtpt = EARGF(usage());
783                 break;
784         case 's':
785                 srv = EARGF(usage());
786                 break;
787         default:
788                 usage();
789         } ARGEND;
790
791         if(getworkdir(workdir, *argv) < 0)
792                 sysfatal("can't find workdir: %r");
793
794         snprint(buf, sizeof(buf), "%s/.hg/store/00changelog", workdir);
795         if(revlogopen(&changelog, buf, OREAD) < 0)
796                 sysfatal("can't open changelog: %r\n");
797         snprint(buf, sizeof(buf), "%s/.hg/store/00manifest", workdir);
798         if(revlogopen(&manifest, buf, OREAD) < 0)
799                 sysfatal("can't open menifest: %r\n");
800
801         postmountsrv(&fs, srv, mtpt, MREPL);
802
803         exits(0);
804 }