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