]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/hgfs/fs.c
merge
[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 dothg[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/store/data", dothg),
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                                         d->length = rl->map[rev].flen;
303                                 ri = getrevinfo(rl->map[rev].linkrev);
304                         }
305                         closerevlog(rl);
306                         if(level == Qtreerev && ri)
307                                 goto Revgen;
308                 }
309                 break;
310         }
311
312         if(ri){
313                 d->atime = d->mtime = ri->when;
314                 d->muid = fsmkuid(ri->who);
315                 d->uid = fsmkuid(ri->who);
316         } else
317                 d->atime = d->mtime = time(0);
318
319         if(d->uid == nil)
320                 d->uid = fsmkuid(nil);
321         if(d->gid == nil)
322                 d->gid = fsmkuid(nil);
323         if(d->muid == nil)
324                 d->muid = fsmkuid(nil);
325 }
326
327 static void
328 fsattach(Req *r)
329 {
330         Revfile *rf;
331
332         if(r->ifcall.aname && r->ifcall.aname[0]){
333                 respond(r, "invalid attach specifier");
334                 return;
335         }
336         r->fid->qid.path = Qroot;
337         r->fid->qid.type = QTDIR;
338         r->fid->qid.vers = 0;
339         r->ofcall.qid = r->fid->qid;
340
341         rf = emalloc9p(sizeof(*rf));
342         rf->level = Qroot;
343         rf->info = nil;
344         rf->tree = nil;
345         rf->node = nil;
346         rf->rlog = nil;
347
348         rf->fd = -1;
349         rf->buf = nil;
350
351         r->fid->aux = rf;
352
353         respond(r, nil);
354 }
355
356 static void
357 fsstat(Req *r)
358 {
359         Revfile *rf;
360
361         rf = r->fid->aux;
362         if(rf->level < Qtree)
363                 fsmkdir(&r->d, rf->level,  rf->info);
364         else
365                 fsmkdir(&r->d, rf->level,  rf->node);
366         respond(r, nil);
367 }
368
369 static int
370 findrev(Revlog *rl, char *name)
371 {
372         uchar hash[HASHSZ];
373         int n, i, rev;
374         char *s;
375
376         if(strcmp(name, "tip") == 0)
377                 return rl->nmap-1;
378         rev = strtol(name, &s, 10);
379         if(s > name && (*s == 0 || ispunct(*s)))
380                 return rev;
381         rev = -1;
382         if(s = strchr(name, '.'))
383                 name = s+1;
384         if((n = hex2hash(name, hash)) > 0){
385                 for(i=0; i<rl->nmap; i++){
386                         if(memcmp(rl->map[i].hash, hash, n) == 0){
387                                 if(rev < 0)
388                                         rev = i;
389                                 else {
390                                         rev = -1;
391                                         break;
392                                 }
393                         }
394                 }
395         }
396         return rev;
397 }
398
399 static char*
400 fswalk1(Fid *fid, char *name, Qid *qid)
401 {
402         Revtree* (*loadfn)(Revlog *, Revlog *, Revinfo *);
403         char buf[MAXPATH], *sname;
404         Revnode *nd;
405         Revfile *rf;
406         int i, level;
407
408         if((fid->qid.type & QTDIR) == 0)
409                 return "walk in non-directory";
410
411         rf = fid->aux;
412         if(strcmp(name, "..") == 0){
413                 switch(rf->level){
414                 case Qroot:
415                         break;
416                 case Qrev:
417                         rf->info = nil;
418                         rf->level = Qroot;
419                         break;
420                 case Qfiles:
421                 case Qchanges:
422                         closerevtree(rf->tree);
423                         rf->tree = nil;
424                         rf->level = Qrev;
425                         break;
426                 case Qtree:
427                         closerevlog(rf->rlog);
428                         rf->rlog = nil;
429                         if((rf->node = rf->node->up) == rf->tree->root)
430                                 rf->level = rf->tree->level;
431                         break;
432                 }
433         } else {
434                 switch(rf->level){
435                 case Qroot:
436                         revlogupdate(&changelog);
437                         revlogupdate(&manifest);
438
439                         i = findrev(&changelog, name);
440                         if(rf->info = getrevinfo(i)){
441                                 rf->level = Qrev;
442                                 break;
443                         }
444                 Notfound:
445                         return "directory entry not found";
446                         break;
447                 case Qrev:
448                         for(i = Qrev+1; i < Qtree; i++){
449                                 if(nametab[i] == nil)
450                                         continue;
451                                 if(strcmp(name, nametab[i]) == 0)
452                                         break;
453                         }
454                         loadfn = nil;
455                         switch(i){
456                         case Qtree:
457                                 goto Notfound;
458                         case Qfiles:
459                                 loadfn = loadfilestree;
460                                 break;
461                         case Qchanges:
462                                 loadfn = loadchangestree;
463                                 break;
464                         }
465                         if(loadfn){
466                                 if((rf->tree = getrevtree(loadfn, rf->info)) == nil)
467                                         goto Notfound;
468                                 rf->node = rf->tree->root;
469                                 rf->tree->level = i;
470                         }
471                         rf->level = i;
472                         break;
473                 case Qtree:
474                 case Qfiles:
475                 case Qchanges:
476                         i = 0;
477                         level = Qtree;
478                         sname = name;
479                 Searchtree:
480                         for(nd = rf->node->down; nd; nd = nd->next)
481                                 if(strcmp(nd->name, sname) == 0)
482                                         break;
483                         if(nd == nil){
484                                 if(sname == name){
485                                         sname = strrchr(name, '.');
486                                         if((i = sname - name) > 0){
487                                                 sname++;
488                                                 if(strncmp(sname, "rev", 3) == 0){
489                                                         level = Qtreerev;
490                                                         sname += 3;
491                                                 }
492                                                 snprint(buf, sizeof(buf), "%.*s", i, name);
493                                                 i = atoi(sname);
494                                                 sname = buf;
495                                                 goto Searchtree;
496                                         }
497                                 }
498                                 goto Notfound;
499                         }
500                         if(nd->hash){
501                                 Revnode *nb;
502                                 int j;
503
504                                 if((rf->rlog = getrevlog(nd)) == nil)
505                                         goto Notfound;
506                                 j = hashrev(rf->rlog, nd->hash) - i;
507                                 if(i < 0 || j < 0 || j >= rf->rlog->nmap){
508                                         closerevlog(rf->rlog);
509                                         rf->rlog = nil;
510                                         goto Notfound;
511                                 }
512                                 for(nb = nd; nb; nb = nb->before)
513                                         if(hashrev(rf->rlog, nb->hash) == j)
514                                                 break;
515                                 if(nb == nil){
516                                         nb = mknode(nd->name, revhash(rf->rlog, j), nd->mode);
517                                         nb->up = nd->up;
518                                         nb->before = nd->before;
519                                         nd->before = nb;
520                                 }
521                                 nd = nb;
522                         } else if(name != sname)
523                                 goto Notfound;
524                         rf->node = nd;
525                         rf->level = level;
526                         break;
527                 }
528         }
529
530         if(rf->level < Qtree)
531                 fsmkqid(qid, rf->level, rf->info);
532         else
533                 fsmkqid(qid, rf->level, rf->node);
534         fid->qid = *qid;
535
536         return nil;
537 }
538
539 static char*
540 fsclone(Fid *oldfid, Fid *newfid)
541 {
542         Revfile *orf, *rf;
543
544         rf = nil;
545         if(orf = oldfid->aux){
546                 rf = emalloc9p(sizeof(*rf));
547                 *rf = *orf;
548                 if(rf->rlog)
549                         incref(rf->rlog);
550                 if(rf->tree)
551                         incref(rf->tree);
552                 if(rf->fd >= 0)
553                         rf->fd = dup(rf->fd, -1);
554                 if(rf->buf)
555                         rf->buf = estrdup9p(rf->buf);
556         }
557         newfid->aux = rf;
558         return nil;
559 }
560
561 static void
562 fsdestroyfid(Fid *fid)
563 {
564         Revfile *rf;
565
566         if(rf = fid->aux){
567                 closerevlog(rf->rlog);
568                 closerevtree(rf->tree);
569                 if(rf->fd >= 0)
570                         close(rf->fd);
571                 free(rf->buf);
572                 free(rf);
573         }
574 }
575
576 static void
577 fsopen(Req *r)
578 {
579         Revfile *rf;
580
581         rf = r->fid->aux;
582         switch(r->ifcall.mode & 3){
583         case OEXEC:
584                 if(rf->node == nil || rf->node->mode != 'x')
585                         break;
586         case OREAD:
587                 respond(r, nil);
588                 return;
589         }
590         respond(r, "permission denied");
591 }
592
593 static int
594 rootgen(int i, Dir *d, void *)
595 {
596         Revinfo *ri;
597
598         if((ri = getrevinfo(i)) == nil)
599                 return -1;
600         fsmkdir(d, Qrev, ri);
601         return 0;
602 }
603
604 static int
605 revgen(int i, Dir *d, void *aux)
606 {
607         i += Qrev+1;
608         if(i >= Qtree)
609                 return -1;
610         fsmkdir(d, i, aux);
611         return 0;
612 }
613
614 static int
615 treegen(int i, Dir *d, void *aux)
616 {
617         Revnode *nd = aux;
618
619         while(i > 0 && nd){
620                 nd = nd->next;
621                 i--;
622         }
623         if(i || nd == nil)
624                 return -1;
625         fsmkdir(d, Qtree, nd);
626         return 0;
627 }
628
629 static void
630 fsread(Req *r)
631 {
632         char buf[MAXPATH], *s;
633         Revfile *rf;
634         int i, n;
635         vlong off;
636         int len;
637
638         off = r->ifcall.offset;
639         len = r->ifcall.count;
640
641         rf = r->fid->aux;
642         switch(rf->level){
643         case Qroot:
644                 revlogupdate(&changelog);
645                 revlogupdate(&manifest);
646                 dirread9p(r, rootgen, nil);
647                 respond(r, nil);
648                 return;
649         case Qrev:
650                 dirread9p(r, revgen, rf->info);
651                 respond(r, nil);
652                 return;
653         case Qrev0:
654         case Qrev1:
655         case Qrev2:
656                 s = nil;
657                 if(rf->buf)
658                         goto Strgen;
659                 i = hashrev(&changelog, rf->info->chash);
660                 if(rf->level == Qrev1)
661                         i = changelog.map[i].p1rev;
662                 else if(rf->level == Qrev2)
663                         i = changelog.map[i].p2rev;
664         Revgen:
665                 s = fsmkrevname(buf, sizeof(buf), i);
666                 goto Strgen;
667         case Qtreerev:
668                 if((i = hashrev(rf->rlog, rf->node->hash)) >= 0)
669                         i = rf->rlog->map[i].linkrev;
670                 goto Revgen;
671         case Qlog:
672                 if(off >= rf->info->loglen)
673                         len = 0;
674                 else if((off + len) >= rf->info->loglen)
675                         len = rf->info->loglen - off;
676                 off += rf->info->logoff;
677                 if(rf->fd >= 0)
678                         goto Fdgen;
679                 if((rf->fd = revlogopentemp(&changelog, hashrev(&changelog, rf->info->chash))) < 0){
680                         responderror(r);
681                         return;
682                 }
683                 goto Fdgen;
684         case Qwho:
685                 s = rf->info->who;
686                 goto Strgen;
687         case Qwhy:
688                 s = rf->info->why;
689         Strgen:
690                 if(rf->buf == nil)
691                         rf->buf = s ? smprint("%s\n", s) : estrdup9p("");
692                 readstr(r, rf->buf);
693                 respond(r, nil);
694                 return;
695         case Qtree:
696                 if(rf->node->down){
697         case Qfiles:
698         case Qchanges:
699                         dirread9p(r, treegen, rf->node->down);
700                         respond(r, nil);
701                         return;
702                 }
703                 if(rf->fd >= 0)
704                         goto Fdgen;
705                 if((rf->fd = revlogopentemp(rf->rlog, hashrev(rf->rlog, rf->node->hash))) < 0){
706                         responderror(r);
707                         return;
708                 }
709         Fdgen:
710                 if((n = pread(rf->fd, r->ofcall.data, len, off)) < 0){
711                         responderror(r);
712                         return;
713                 }
714                 r->ofcall.count = n;
715                 respond(r, nil);
716                 return;
717         }
718         respond(r, "bug in fsread");
719 }
720
721 Srv fs = 
722 {
723         .attach=fsattach,
724         .stat=fsstat,
725         .walk1=fswalk1,
726         .clone=fsclone,
727         .destroyfid=fsdestroyfid,
728         .open=fsopen,
729         .read=fsread,
730 };
731
732 void
733 usage(void)
734 {
735         fprint(2, "usage: %s [-D] [-m mtpt] [-s srv] [root]\n", argv0);
736         exits("usage");
737 }
738
739 void
740 main(int argc, char *argv[])
741 {
742         char *srv, *mtpt;
743         char buf[MAXPATH];
744
745         inflateinit();
746         fmtinstall('H', Hfmt);
747
748         srv = nil;
749         mtpt = "/mnt/hg";
750
751         ARGBEGIN {
752         case 'D':
753                 chatty9p++;
754                 break;
755         case 'm':
756                 mtpt = EARGF(usage());
757                 break;
758         case 's':
759                 srv = EARGF(usage());
760                 break;
761         default:
762                 usage();
763         } ARGEND;
764
765         if(getdothg(dothg, *argv) < 0)
766                 sysfatal("can't find .hg: %r");
767
768         snprint(buf, sizeof(buf), "%s/store/00changelog", dothg);
769         if(revlogopen(&changelog, buf, OREAD) < 0)
770                 sysfatal("can't open changelog: %r\n");
771         snprint(buf, sizeof(buf), "%s/store/00manifest", dothg);
772         if(revlogopen(&manifest, buf, OREAD) < 0)
773                 sysfatal("can't open menifest: %r\n");
774
775         postmountsrv(&fs, srv, mtpt, MREPL);
776
777         exits(0);
778 }
779