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