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