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