ref: c9b749da88bb2e6b9ef03ffb0bc7a89aafb4089c
dir: /sys/src/cmd/hgfs/fs.c/
#include <u.h>
#include <libc.h>
#include <thread.h>
#include "dat.h"
#include "fns.h"
#include <ctype.h>
#include <flate.h>
#include <auth.h>
#include <fcall.h>
#include <9p.h>
enum {
Qroot,
Qrev,
Qrev1,
Qrev2,
Qlog,
Qwho,
Qwhy,
Qfiles,
Qchanges,
Qtree,
};
static char *nametab[Qtree+1] = {
"/",
nil,
"rev1",
"rev2",
"log",
"who",
"why",
"files",
"changes",
nil,
};
static Revlog changelog;
static Revlog manifest;
static Revinfo*
getrevinfo(int rev)
{
Revinfo *ri;
if(rev < 0 || rev >= changelog.nmap)
return nil;
if(ri = changelog.map[rev].aux)
return ri;
if(ri = loadrevinfo(&changelog, rev))
changelog.map[rev].aux = ri;
return ri;
}
static Revtree*
getrevtree(Revtree *(*fn)(Revlog *, Revlog *, Revinfo *), Revinfo *ri)
{
static ulong gen;
static struct {
ulong g;
void *f;
Revinfo *i;
Revtree *t;
} cache[4];
Revtree *rt;
int i, j;
for(i=j=0; i<nelem(cache); i++){
if(cache[i].t == nil){
j = i;
continue;
}
if(cache[i].f == fn && cache[i].i == ri){
cache[i].g = ++gen;
rt = cache[i].t;
goto found;
}
if(cache[j].t && cache[i].g < cache[j].g)
j = i;
}
if((rt = (*fn)(&changelog, &manifest, ri)) == nil)
return nil;
closerevtree(cache[j].t);
cache[j].g = ++gen;
cache[j].f = fn;
cache[j].i = ri;
cache[j].t = rt;
found:
incref(rt);
return rt;
}
static char*
fsmkuid(char *s)
{
if(s){
char *x;
while(x = strchr(s, '<'))
s = x+1;
s = estrdup9p(s);
if(x = strchr(s, '>'))
*x = 0;
if(x = strchr(s, '@'))
*x = 0;
if(x = strchr(s, '\n'))
*x = 0;
}
if(s == nil || *s == 0){
free(s);
s = estrdup9p("hgfs");
}
return s;
}
static void
fsmkqid(Qid *q, int level, void *aux)
{
Revnode *nd;
Revinfo *ri;
switch(level){
case Qroot:
q->type = QTDIR;
q->path = Qroot;
q->vers = 0;
break;
case Qrev:
case Qfiles:
case Qchanges:
q->type = QTDIR;
if(0){
case Qrev1:
case Qrev2:
case Qlog:
case Qwho:
case Qwhy:
q->type = 0;
}
ri = aux;
q->path = *((uvlong*)ri->chash) + (level - Qrev);
q->vers = 0;
break;
case Qtree:
nd = aux;
if(nd->hash){
q->type = 0;
} else {
q->type = QTDIR;
}
q->path = nd->path;
q->vers = 0;
break;
}
}
static void
fsmkdir(Dir *d, int level, void *aux)
{
char buf[64], *s;
Revnode *nd;
Revinfo *ri;
int rev;
memset(d, 0, sizeof(*d));
fsmkqid(&d->qid, level, aux);
d->mode = 0444;
if(d->qid.type == QTDIR)
d->mode |= DMDIR | 0111;
s = nil;
ri = nil;
switch(level){
case Qroot:
goto Namegen;
case Qrev:
case Qrev1:
case Qrev2:
ri = aux;
rev = hashrev(&changelog, ri->chash);
if(level == Qrev1)
rev = changelog.map[rev].p1rev;
else if(level == Qrev2)
rev = changelog.map[rev].p2rev;
if(rev >= 0)
snprint(s = buf, sizeof(buf), "%d.%H", rev, changelog.map[rev].hash);
if(level == Qrev){
d->name = estrdup9p(buf);
break;
}
goto Strgen;
case Qlog:
ri = aux;
if((rev = hashrev(&changelog, ri->chash)) >= 0)
d->length = changelog.map[rev].flen;
goto Namegen;
case Qwho:
ri = aux;
s = ri->who;
goto Strgen;
case Qwhy:
ri = aux;
s = ri->why;
Strgen:
d->length = s ? strlen(s)+1 : 0;
case Qfiles:
case Qchanges:
ri = aux;
/* no break */
Namegen:
d->name = estrdup9p(nametab[level]);
break;
case Qtree:
nd = aux;
d->name = estrdup9p(nd->name);
if(nd->mode == 'x')
d->mode |= 0111;
if(nd->hash){
char path[MAXPATH];
Revlog rl;
nodepath(seprint(path, path+MAXPATH, ".hg/store/data"), path+MAXPATH, nd);
if(revlogopen(&rl, path, OREAD) < 0)
break;
if((rev = hashrev(&rl, nd->hash)) >= 0){
d->length = rl.map[rev].flen;
ri = getrevinfo(rl.map[rev].linkrev);
}
revlogclose(&rl);
}
break;
}
if(ri){
d->atime = d->mtime = ri->when;
d->muid = fsmkuid(ri->who);
d->uid = fsmkuid(ri->who);
} else
d->atime = d->mtime = time(0);
if(d->uid == nil)
d->uid = fsmkuid(nil);
if(d->gid == nil)
d->gid = fsmkuid(nil);
if(d->muid == nil)
d->muid = fsmkuid(nil);
}
static void
fsattach(Req *r)
{
Revfile *rf;
if(r->ifcall.aname && r->ifcall.aname[0]){
respond(r, "invalid attach specifier");
return;
}
r->fid->qid.path = Qroot;
r->fid->qid.type = QTDIR;
r->fid->qid.vers = 0;
r->ofcall.qid = r->fid->qid;
rf = emalloc9p(sizeof(*rf));
rf->level = Qroot;
rf->info = nil;
rf->tree = nil;
rf->node = nil;
rf->fd = -1;
rf->buf = nil;
r->fid->aux = rf;
respond(r, nil);
}
static void
fsstat(Req *r)
{
Revfile *rf;
rf = r->fid->aux;
if(rf->level < Qtree)
fsmkdir(&r->d, rf->level, rf->info);
else
fsmkdir(&r->d, rf->level, rf->node);
respond(r, nil);
}
static int
findrev(Revlog *rl, char *name)
{
uchar hash[HASHSZ];
int n, i, rev;
char *s;
rev = strtol(name, &s, 10);
if(s > name && (*s == 0 || ispunct(*s)))
return rev;
rev = -1;
if(s = strchr(name, '.'))
name = s+1;
if((n = strhash(name, hash)) > 0){
for(i=0; i<rl->nmap; i++){
if(memcmp(rl->map[i].hash, hash, n) == 0){
if(rev < 0)
rev = i;
else {
rev = -1;
break;
}
}
}
}
return rev;
}
static char*
fswalk1(Fid *fid, char *name, Qid *qid)
{
Revtree* (*loadfn)(Revlog *, Revlog *, Revinfo *);
Revfile *rf;
Revnode *nd;
int i;
if(!(fid->qid.type&QTDIR))
return "walk in non-directory";
rf = fid->aux;
if(strcmp(name, "..") == 0){
switch(rf->level){
case Qroot:
break;
case Qrev:
rf->info = nil;
rf->level = Qroot;
break;
case Qfiles:
case Qchanges:
closerevtree(rf->tree);
rf->tree = nil;
rf->level = Qrev;
break;
case Qtree:
if((rf->node = rf->node->up) == rf->tree->root)
rf->level = rf->tree->level;
break;
}
} else {
switch(rf->level){
case Qroot:
revlogupdate(&changelog);
revlogupdate(&manifest);
i = findrev(&changelog, name);
if(rf->info = getrevinfo(i)){
rf->level = Qrev;
break;
}
Notfound:
return "directory entry not found";
break;
case Qrev:
for(i = Qrev+1; i < Qtree; i++){
if(nametab[i] == nil)
continue;
if(strcmp(name, nametab[i]) == 0)
break;
}
loadfn = nil;
switch(i){
case Qtree:
goto Notfound;
case Qfiles:
loadfn = loadfilestree;
break;
case Qchanges:
loadfn = loadchangestree;
break;
}
if(loadfn){
if((rf->tree = getrevtree(loadfn, rf->info)) == nil)
goto Notfound;
rf->node = rf->tree->root;
rf->tree->level = i;
}
rf->level = i;
break;
case Qtree:
case Qfiles:
case Qchanges:
for(nd = rf->node->down; nd; nd = nd->next)
if(strcmp(nd->name, name) == 0)
break;
if(nd == nil)
goto Notfound;
rf->node = nd;
rf->level = Qtree;
break;
}
}
if(rf->level < Qtree)
fsmkqid(qid, rf->level, rf->info);
else
fsmkqid(qid, rf->level, rf->node);
fid->qid = *qid;
return nil;
}
static char*
fsclone(Fid *oldfid, Fid *newfid)
{
Revfile *orf, *rf;
rf = nil;
if(orf = oldfid->aux){
rf = emalloc9p(sizeof(*rf));
*rf = *orf;
if(rf->tree)
incref(rf->tree);
if(rf->fd >= 0)
rf->fd = dup(rf->fd, -1);
if(rf->buf)
rf->buf = estrdup9p(rf->buf);
}
newfid->aux = rf;
return nil;
}
static void
fsdestroyfid(Fid *fid)
{
Revfile *rf;
if(rf = fid->aux){
closerevtree(rf->tree);
if(rf->fd >= 0)
close(rf->fd);
free(rf->buf);
free(rf);
}
}
static void
fsopen(Req *r)
{
respond(r, nil);
}
static int
rootgen(int i, Dir *d, void *)
{
Revinfo *ri;
if((ri = getrevinfo(i)) == nil)
return -1;
fsmkdir(d, Qrev, ri);
return 0;
}
static int
revgen(int i, Dir *d, void *aux)
{
i += Qrev+1;
if(i >= Qtree)
return -1;
fsmkdir(d, i, aux);
return 0;
}
static int
treegen(int i, Dir *d, void *aux)
{
Revnode *nd = aux;
while(i > 0 && nd){
nd = nd->next;
i--;
}
if(i || nd == nil)
return -1;
fsmkdir(d, Qtree, nd);
return 0;
}
static void
fsread(Req *r)
{
Revfile *rf;
rf = r->fid->aux;
if(r->fid->qid.type == QTDIR){
switch(rf->level){
default:
respond(r, "bug in fsread");
return;
case Qroot:
revlogupdate(&changelog);
revlogupdate(&manifest);
dirread9p(r, rootgen, nil);
respond(r, nil);
return;
case Qrev:
dirread9p(r, revgen, rf->info);
respond(r, nil);
return;
case Qtree:
case Qfiles:
case Qchanges:
dirread9p(r, treegen, rf->node->down);
respond(r, nil);
return;
}
} else {
char buf[MAXPATH];
Revlog rl;
char *s;
int i, n;
switch(rf->level){
default:
respond(r, "bug in fsread");
return;
case Qlog:
if(rf->fd >= 0)
break;
if((rf->fd = revlogopentemp(&changelog, hashrev(&changelog, rf->info->chash))) < 0){
responderror(r);
return;
}
break;
case Qrev1:
case Qrev2:
s = nil;
if((i = hashrev(&changelog, rf->info->chash)) >= 0){
if(rf->level == Qrev1)
i = changelog.map[i].p1rev;
else
i = changelog.map[i].p2rev;
if(i >= 0)
snprint(s = buf, sizeof(buf), "%d.%H", i, changelog.map[i].hash);
}
goto Strgen;
case Qwho:
s = rf->info->who;
goto Strgen;
case Qwhy:
s = rf->info->why;
Strgen:
if(rf->buf == nil)
rf->buf = s ? smprint("%s\n", s) : estrdup9p("");
readstr(r, rf->buf);
respond(r, nil);
return;
case Qtree:
if(rf->fd >= 0)
break;
nodepath(seprint(buf, buf+sizeof(buf), ".hg/store/data"), buf+sizeof(buf), rf->node);
if(revlogopen(&rl, buf, OREAD) < 0){
responderror(r);
return;
}
if((rf->fd = revlogopentemp(&rl, hashrev(&rl, rf->node->hash))) < 0){
responderror(r);
revlogclose(&rl);
return;
}
revlogclose(&rl);
break;
}
if((n = pread(rf->fd, r->ofcall.data, r->ifcall.count, r->ifcall.offset)) < 0){
responderror(r);
return;
}
r->ofcall.count = n;
respond(r, nil);
}
}
Srv fs =
{
.attach=fsattach,
.stat=fsstat,
.walk1=fswalk1,
.clone=fsclone,
.destroyfid=fsdestroyfid,
.open=fsopen,
.read=fsread,
};
void
usage(void)
{
fprint(2, "usage: %s [-D] [-m mtpt] [-s srv]\n", argv0);
exits("usage");
}
void
main(int argc, char *argv[])
{
char *srv, *mtpt;
inflateinit();
fmtinstall('H', Hfmt);
srv = nil;
mtpt = "/n/hg";
ARGBEGIN {
case 'D':
chatty9p++;
break;
case 'm':
mtpt = EARGF(usage());
break;
case 's':
srv = EARGF(usage());
break;
default:
usage();
} ARGEND;
if(revlogopen(&changelog, ".hg/store/00changelog", OREAD) < 0)
sysfatal("can't open changelog: %r\n");
if(revlogopen(&manifest, ".hg/store/00manifest", OREAD) < 0)
sysfatal("can't open menifest: %r\n");
postmountsrv(&fs, srv, mtpt, MREPL);
}