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