Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <mp.h>
#include <libsec.h>

static void
usage(void)
{
        fprint(2, "mntgen [-s srvname] [mtpt]\n");
        exits("usage");
}

ulong time0;

typedef struct Tab Tab;
struct Tab
{
        char *name;
        vlong qid;
        ulong time;
        int ref;
};

Tab *tab;
int ntab;
int mtab;

static Tab*
findtab(vlong path)
{
        int i;

        for(i=0; i<ntab; i++)
                if(tab[i].qid == path)
                        return &tab[i];
        return nil;
}

static vlong
hash(char *name)
{
        vlong digest[MD5dlen / sizeof(vlong) + 1];
        md5((uchar *)name, strlen(name), (uchar *)digest, nil);
        return digest[0] & ((1ULL<<48)-1);
}

static void
fsopen(Req *r)
{
        if(r->ifcall.mode != OREAD)
                respond(r, "permission denied");
        else
                respond(r, nil);
}

static int
dirgen(int i, Dir *d, void*)
{
        if(i >= ntab)
                return -1;
        memset(d, 0, sizeof *d);
        d->qid.type = QTDIR;
        d->uid = estrdup9p("sys");
        d->gid = estrdup9p("sys");
        d->mode = DMDIR|0555;
        d->length = 0;
        if(i == -1){
                d->name = estrdup9p("/");
                d->atime = d->mtime = time0;
        }else{
                d->qid.path = tab[i].qid;
                d->name = estrdup9p(tab[i].name);
                d->atime = d->mtime = tab[i].time;
        }
        return 0;
}

static void
fsread(Req *r)
{
        if(r->fid->qid.path == 0)
                dirread9p(r, dirgen, nil);
        else
                r->ofcall.count = 0;
        respond(r, nil);
}

static void
fsstat(Req *r)
{
        Tab *t;
        vlong qid;

        qid = r->fid->qid.path;
        if(qid == 0)
                dirgen(-1, &r->d, nil);
        else{
                if((t = findtab(qid)) == nil){
                        respond(r, "path not found (???)");
                        return;
                }
                dirgen(t-tab, &r->d, nil);
        }
        respond(r, nil);
}

static char*
fswalk1(Fid *fid, char *name, void*)
{
        int i;
        Tab *t;
        vlong h;

        if(fid->qid.path != 0){
                /* nothing in child directory */
                if(strcmp(name, "..") == 0){
                        if((t = findtab(fid->qid.path)) != nil)
                                t->ref--;
                        fid->qid.path = 0;
                        return nil;
                }
                return "path not found";
        }
        /* root */
        if(strcmp(name, "..") == 0)
                return nil;
        for(i=0; i<ntab; i++)
                if(strcmp(tab[i].name, name) == 0){
                        tab[i].ref++;
                        fid->qid.path = tab[i].qid;
                        return nil;
                }
        h = hash(name);
        if(findtab(h) != nil)
                return "hash collision";

        /* create it */
        if(ntab == mtab){
                if(mtab == 0)
                        mtab = 16;
                else
                        mtab *= 2;
                tab = erealloc9p(tab, sizeof(tab[0])*mtab);
        }
        tab[ntab].qid = h;
        fid->qid.path = tab[ntab].qid;
        tab[ntab].name = estrdup9p(name);
        tab[ntab].time = time(0);
        tab[ntab].ref = 1;
        ntab++;

        return nil;
}

static char*
fsclone(Fid *fid, Fid*, void*)
{
        Tab *t;

        if((t = findtab(fid->qid.path)) != nil)
                t->ref++;
        return nil;
}

static void
fswalk(Req *r)
{
        walkandclone(r, fswalk1, fsclone, nil);
}

static void
fsclunk(Fid *fid)
{
        Tab *t;
        vlong qid;

        qid = fid->qid.path;
        if(qid == 0)
                return;
        if((t = findtab(qid)) == nil){
                fprint(2, "warning: cannot find %llux\n", qid);
                return;
        }
        if(--t->ref == 0){
                free(t->name);
                tab[t-tab] = tab[--ntab];
        }else if(t->ref < 0)
                fprint(2, "warning: negative ref count for %s\n", t->name);
}

static void
fsattach(Req *r)
{
        char *spec;

        spec = r->ifcall.aname;
        if(spec && spec[0]){
                respond(r, "invalid attach specifier");
                return;
        }
        
        r->ofcall.qid = (Qid){0, 0, QTDIR};
        r->fid->qid = r->ofcall.qid;
        respond(r, nil);
}

Srv fs=
{
.attach=        fsattach,
.open=  fsopen,
.read=  fsread,
.stat=  fsstat,
.walk=  fswalk,
.destroyfid=    fsclunk
};

void
main(int argc, char **argv)
{
        char *service;

        time0 = time(0);
        service = nil;
        ARGBEGIN{
        case 'D':
                chatty9p++;
                break;
        case 's':
                service = EARGF(usage());
                break;
        default:
                usage();
        }ARGEND

        if(argc > 1)
                usage();
        postmountsrv(&fs, service, argc ? argv[0] : "/n", MAFTER);
        exits(nil);
}