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