]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/hgfs/fs.c
hgfs: honor x-bit in manifest
[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->hash){
151                         q->type = 0;
152                 } else {
153                         q->type = QTDIR;
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                 if((rev = hashrev(&changelog, ri->chash)) >= 0)
201                         d->length = changelog.map[rev].flen;
202                 goto Namegen;
203         case Qwho:
204                 ri = aux;
205                 s = ri->who;
206                 goto Strgen;
207         case Qwhy:
208                 ri = aux;
209                 s = ri->why;
210         Strgen:
211                 d->length = s ? strlen(s)+1 : 0;
212         case Qfiles:
213         case Qchanges:
214                 ri = aux;
215                 /* no break */
216         Namegen:
217                 d->name = estrdup9p(nametab[level]);
218                 break;
219         case Qtree:
220                 nd = aux;
221                 d->name = estrdup9p(nd->name);
222                 if(nd->mode == 'x')
223                         d->mode |= 0111;
224                 if(nd->hash){
225                         char path[MAXPATH];
226                         Revlog rl;
227
228                         nodepath(seprint(path, path+MAXPATH, ".hg/store/data"), path+MAXPATH, nd);
229                         if(revlogopen(&rl, path, OREAD) < 0)
230                                 break;
231                         if((rev = hashrev(&rl, nd->hash)) >= 0){
232                                 d->length = rl.map[rev].flen;
233                                 ri = getrevinfo(rl.map[rev].linkrev);
234                         }
235                         revlogclose(&rl);
236                 }
237                 break;
238         }
239
240         if(ri){
241                 d->atime = d->mtime = ri->when;
242                 d->muid = fsmkuid(ri->who);
243                 d->uid = fsmkuid(ri->who);
244         } else
245                 d->atime = d->mtime = time(0);
246
247         if(d->uid == nil)
248                 d->uid = fsmkuid(nil);
249         if(d->gid == nil)
250                 d->gid = fsmkuid(nil);
251         if(d->muid == nil)
252                 d->muid = fsmkuid(nil);
253 }
254
255 static void
256 fsattach(Req *r)
257 {
258         Revfile *rf;
259
260         if(r->ifcall.aname && r->ifcall.aname[0]){
261                 respond(r, "invalid attach specifier");
262                 return;
263         }
264         r->fid->qid.path = Qroot;
265         r->fid->qid.type = QTDIR;
266         r->fid->qid.vers = 0;
267         r->ofcall.qid = r->fid->qid;
268
269         rf = emalloc9p(sizeof(*rf));
270         rf->level = Qroot;
271         rf->info = nil;
272         rf->tree = nil;
273         rf->node = nil;
274
275         rf->fd = -1;
276         rf->buf = nil;
277
278         r->fid->aux = rf;
279
280         respond(r, nil);
281 }
282
283 static void
284 fsstat(Req *r)
285 {
286         Revfile *rf;
287
288         rf = r->fid->aux;
289         if(rf->level < Qtree)
290                 fsmkdir(&r->d, rf->level,  rf->info);
291         else
292                 fsmkdir(&r->d, rf->level,  rf->node);
293         respond(r, nil);
294 }
295
296 static int
297 findrev(Revlog *rl, char *name)
298 {
299         uchar hash[HASHSZ];
300         int n, i, rev;
301         char *s;
302
303         rev = strtol(name, &s, 10);
304         if(s > name && (*s == 0 || ispunct(*s)))
305                 return rev;
306         rev = -1;
307         if(s = strchr(name, '.'))
308                 name = s+1;
309         if((n = strhash(name, hash)) > 0){
310                 for(i=0; i<rl->nmap; i++){
311                         if(memcmp(rl->map[i].hash, hash, n) == 0){
312                                 if(rev < 0)
313                                         rev = i;
314                                 else {
315                                         rev = -1;
316                                         break;
317                                 }
318                         }
319                 }
320         }
321         return rev;
322 }
323
324 static char*
325 fswalk1(Fid *fid, char *name, Qid *qid)
326 {
327         Revtree* (*loadfn)(Revlog *, Revlog *, Revinfo *);
328         Revfile *rf;
329         Revnode *nd;
330         int i;
331
332         if(!(fid->qid.type&QTDIR))
333                 return "walk in non-directory";
334
335         rf = fid->aux;
336         if(strcmp(name, "..") == 0){
337                 switch(rf->level){
338                 case Qroot:
339                         break;
340                 case Qrev:
341                         rf->info = nil;
342                         rf->level = Qroot;
343                         break;
344                 case Qfiles:
345                 case Qchanges:
346                         closerevtree(rf->tree);
347                         rf->tree = nil;
348                         rf->level = Qrev;
349                         break;
350                 case Qtree:
351                         if((rf->node = rf->node->up) == rf->tree->root)
352                                 rf->level = rf->tree->level;
353                         break;
354                 }
355         } else {
356                 switch(rf->level){
357                 case Qroot:
358                         revlogupdate(&changelog);
359                         revlogupdate(&manifest);
360
361                         i = findrev(&changelog, name);
362                         if(rf->info = getrevinfo(i)){
363                                 rf->level = Qrev;
364                                 break;
365                         }
366                 Notfound:
367                         return "directory entry not found";
368                         break;
369                 case Qrev:
370                         for(i = Qrev+1; i < Qtree; i++){
371                                 if(nametab[i] == nil)
372                                         continue;
373                                 if(strcmp(name, nametab[i]) == 0)
374                                         break;
375                         }
376                         loadfn = nil;
377                         switch(i){
378                         case Qtree:
379                                 goto Notfound;
380                         case Qfiles:
381                                 loadfn = loadfilestree;
382                                 break;
383                         case Qchanges:
384                                 loadfn = loadchangestree;
385                                 break;
386                         }
387                         if(loadfn){
388                                 if((rf->tree = getrevtree(loadfn, rf->info)) == nil)
389                                         goto Notfound;
390                                 rf->node = rf->tree->root;
391                                 rf->tree->level = i;
392                         }
393                         rf->level = i;
394                         break;
395                 case Qtree:
396                 case Qfiles:
397                 case Qchanges:
398                         for(nd = rf->node->down; nd; nd = nd->next)
399                                 if(strcmp(nd->name, name) == 0)
400                                         break;
401                         if(nd == nil)
402                                 goto Notfound;
403                         rf->node = nd;
404                         rf->level = Qtree;
405                         break;
406                 }
407         }
408
409         if(rf->level < Qtree)
410                 fsmkqid(qid, rf->level, rf->info);
411         else
412                 fsmkqid(qid, rf->level, rf->node);
413         fid->qid = *qid;
414
415         return nil;
416 }
417
418 static char*
419 fsclone(Fid *oldfid, Fid *newfid)
420 {
421         Revfile *orf, *rf;
422
423         rf = nil;
424         if(orf = oldfid->aux){
425                 rf = emalloc9p(sizeof(*rf));
426                 *rf = *orf;
427                 if(rf->tree)
428                         incref(rf->tree);
429                 if(rf->fd >= 0)
430                         rf->fd = dup(rf->fd, -1);
431                 if(rf->buf)
432                         rf->buf = estrdup9p(rf->buf);
433         }
434         newfid->aux = rf;
435         return nil;
436 }
437
438 static void
439 fsdestroyfid(Fid *fid)
440 {
441         Revfile *rf;
442
443         if(rf = fid->aux){
444                 closerevtree(rf->tree);
445                 if(rf->fd >= 0)
446                         close(rf->fd);
447                 free(rf->buf);
448                 free(rf);
449         }
450 }
451
452 static void
453 fsopen(Req *r)
454 {
455         respond(r, nil);
456 }
457
458 static int
459 rootgen(int i, Dir *d, void *)
460 {
461         Revinfo *ri;
462
463         if((ri = getrevinfo(i)) == nil)
464                 return -1;
465         fsmkdir(d, Qrev, ri);
466         return 0;
467 }
468
469 static int
470 revgen(int i, Dir *d, void *aux)
471 {
472         i += Qrev+1;
473         if(i >= Qtree)
474                 return -1;
475         fsmkdir(d, i, aux);
476         return 0;
477 }
478
479 static int
480 treegen(int i, Dir *d, void *aux)
481 {
482         Revnode *nd = aux;
483
484         while(i > 0 && nd){
485                 nd = nd->next;
486                 i--;
487         }
488         if(i || nd == nil)
489                 return -1;
490         fsmkdir(d, Qtree, nd);
491         return 0;
492 }
493
494 static void
495 fsread(Req *r)
496 {
497         Revfile *rf;
498
499         rf = r->fid->aux;
500         if(r->fid->qid.type == QTDIR){
501                 switch(rf->level){
502                 default:
503                         respond(r, "bug in fsread");
504                         return;
505                 case Qroot:
506                         revlogupdate(&changelog);
507                         revlogupdate(&manifest);
508
509                         dirread9p(r, rootgen, nil);
510                         respond(r, nil);
511                         return;
512                 case Qrev:
513                         dirread9p(r, revgen, rf->info);
514                         respond(r, nil);
515                         return;
516                 case Qtree:
517                 case Qfiles:
518                 case Qchanges:
519                         dirread9p(r, treegen, rf->node->down);
520                         respond(r, nil);
521                         return;
522                 }
523         } else {
524                 char buf[MAXPATH];
525                 Revlog rl;
526                 char *s;
527                 int i, n;
528
529                 switch(rf->level){
530                 default:
531                         respond(r, "bug in fsread");
532                         return;
533                 case Qlog:
534                         if(rf->fd >= 0)
535                                 break;
536                         if((rf->fd = revlogopentemp(&changelog, hashrev(&changelog, rf->info->chash))) < 0){
537                                 responderror(r);
538                                 return;
539                         }
540                         break;
541                 case Qrev1:
542                 case Qrev2:
543                         s = nil;
544                         if((i = hashrev(&changelog, rf->info->chash)) >= 0){
545                                 if(rf->level == Qrev1)
546                                         i = changelog.map[i].p1rev;
547                                 else
548                                         i = changelog.map[i].p2rev;
549                                 if(i >= 0)
550                                         snprint(s = buf, sizeof(buf), "%d.%H", i, changelog.map[i].hash);
551                         }
552                         goto Strgen;
553                 case Qwho:
554                         s = rf->info->who;
555                         goto Strgen;
556                 case Qwhy:
557                         s = rf->info->why;
558                 Strgen:
559                         if(rf->buf == nil)
560                                 rf->buf = s ? smprint("%s\n", s) : estrdup9p("");
561                         readstr(r, rf->buf);
562                         respond(r, nil);
563                         return;
564                 case Qtree:
565                         if(rf->fd >= 0)
566                                 break;
567                         nodepath(seprint(buf, buf+sizeof(buf), ".hg/store/data"), buf+sizeof(buf), rf->node);
568                         if(revlogopen(&rl, buf, OREAD) < 0){
569                                 responderror(r);
570                                 return;
571                         }
572                         if((rf->fd = revlogopentemp(&rl, hashrev(&rl, rf->node->hash))) < 0){
573                                 responderror(r);
574                                 revlogclose(&rl);
575                                 return;
576                         }
577                         revlogclose(&rl);
578                         break;
579                 }
580                 if((n = pread(rf->fd, r->ofcall.data, r->ifcall.count, r->ifcall.offset)) < 0){
581                         responderror(r);
582                         return;
583                 }
584                 r->ofcall.count = n;
585                 respond(r, nil);
586         }
587 }
588
589 Srv fs = 
590 {
591         .attach=fsattach,
592         .stat=fsstat,
593         .walk1=fswalk1,
594         .clone=fsclone,
595         .destroyfid=fsdestroyfid,
596         .open=fsopen,
597         .read=fsread,
598 };
599
600 void
601 usage(void)
602 {
603         fprint(2, "usage: %s [-D] [-m mtpt] [-s srv]\n", argv0);
604         exits("usage");
605 }
606
607 void
608 main(int argc, char *argv[])
609 {
610         char *srv, *mtpt;
611
612         inflateinit();
613         fmtinstall('H', Hfmt);
614
615         srv = nil;
616         mtpt = "/n/hg";
617
618         ARGBEGIN {
619         case 'D':
620                 chatty9p++;
621                 break;
622         case 'm':
623                 mtpt = EARGF(usage());
624                 break;
625         case 's':
626                 srv = EARGF(usage());
627                 break;
628         default:
629                 usage();
630         } ARGEND;
631
632         if(revlogopen(&changelog, ".hg/store/00changelog", OREAD) < 0)
633                 sysfatal("can't open changelog: %r\n");
634         if(revlogopen(&manifest, ".hg/store/00manifest", OREAD) < 0)
635                 sysfatal("can't open menifest: %r\n");
636
637         postmountsrv(&fs, srv, mtpt, MREPL);
638 }