Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <authsrv.h>
#include <fcall.h>
#include "tapefs.h"

Fid     *fids;
Ram     *ram;
int     mfd[2];
char    *user;
uchar   mdata[Maxbuf+IOHDRSZ];
int     messagesize = Maxbuf+IOHDRSZ;
Fcall   rhdr;
Fcall   thdr;
ulong   path;
Idmap   *uidmap;
Idmap   *gidmap;
int     replete;
int     blocksize;              /* for 32v */
int     verbose;
int     newtap;         /* tap with time in sec */
int     blocksize;

Fid *   newfid(int);
int     ramstat(Ram*, uchar*, int);
void    io(void);
void    usage(void);
int     perm(int);

char    *rflush(Fid*), *rversion(Fid*), *rauth(Fid*),
        *rattach(Fid*), *rwalk(Fid*),
        *ropen(Fid*), *rcreate(Fid*),
        *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
        *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);

char    *(*fcalls[])(Fid*) = {
        [Tflush]        rflush,
        [Tversion]              rversion,
        [Tauth] rauth,
        [Tattach]       rattach,
        [Twalk]         rwalk,
        [Topen]         ropen,
        [Tcreate]       rcreate,
        [Tread]         rread,
        [Twrite]        rwrite,
        [Tclunk]        rclunk,
        [Tremove]       rremove,
        [Tstat]         rstat,
        [Twstat]        rwstat,
};

char    Eperm[] =       "permission denied";
char    Enotdir[] =     "not a directory";
char    Enoauth[] =     "tapefs: authentication not required";
char    Enotexist[] =   "file does not exist";
char    Einuse[] =      "file in use";
char    Eexist[] =      "file exists";
char    Enotowner[] =   "not owner";
char    Eisopen[] =     "file already open for I/O";
char    Excl[] =        "exclusive use file already open";
char    Ename[] =       "illegal name";

void
notifyf(void *a, char *s)
{
        USED(a);
        if(strncmp(s, "interrupt", 9) == 0)
                noted(NCONT);
        noted(NDFLT);
}

void
main(int argc, char *argv[])
{
        Ram *r;
        char *defmnt;
        int p[2];
        char buf[TICKREQLEN];

        fmtinstall('F', fcallfmt);

        defmnt = "/n/tapefs";
        ARGBEGIN{
        case 'm':
                defmnt = EARGF(usage());
                break;
        case 'p':                       /* password file */
                uidmap = getpass(EARGF(usage()));
                break;
        case 'g':                       /* group file */
                gidmap = getpass(EARGF(usage()));
                break;
        case 'v':
                verbose++;
                break;
        case 'n':
                newtap++;
                break;
        case 'b':
                blocksize = atoi(EARGF(usage()));
                break;
        default:
                usage();
        }ARGEND

        if(argc==0)
                error("no file to mount");
        user = getuser();
        if(user == nil)
                user = "dmr";
        ram = r = (Ram *)emalloc(sizeof(Ram));
        r->busy = 1;
        r->data = 0;
        r->ndata = 0;
        r->perm = DMDIR | 0775;
        r->qid.path = 0;
        r->qid.vers = 0;
        r->qid.type = QTDIR;
        r->parent = 0;
        r->child = 0;
        r->next = 0;
        r->user = user;
        r->group = user;
        r->atime = time(0);
        r->mtime = r->atime;
        r->replete = 0;
        r->name = estrdup(".");
        populate(argv[0]);
        r->replete |= replete;
        if(pipe(p) < 0)
                error("pipe failed");
        mfd[0] = mfd[1] = p[0];
        notify(notifyf);

        switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
        case -1:
                error("fork");
        case 0:
                close(p[1]);
                notify(notifyf);
                io();
                break;
        default:
                close(p[0]);    /* don't deadlock if child fails */
                if(mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0) {
                        sprint(buf, "mount on `%s' failed", defmnt);
                        error(buf);
                }
        }
        exits(0);
}

char*
rversion(Fid *unused)
{
        Fid *f;

        USED(unused);

        if(rhdr.msize < 256)
                return "version: message too small";
        if(rhdr.msize > messagesize)
                rhdr.msize = messagesize;
        else
                messagesize = rhdr.msize;
        thdr.msize = messagesize;
        if(strncmp(rhdr.version, "9P2000", 6) != 0)
                return "unrecognized 9P version";
        thdr.version = "9P2000";

        for(f = fids; f; f = f->next)
                if(f->busy)
                        rclunk(f);
        return 0;
}

char*
rauth(Fid *unused)
{
        USED(unused);

        return Enoauth;
}

char*
rflush(Fid *f)
{
        USED(f);
        return 0;
}

char*
rattach(Fid *f)
{
        /* no authentication! */
        f->busy = 1;
        f->rclose = 0;
        f->ram = ram;
        thdr.qid = f->ram->qid;
        if(rhdr.uname[0])
                f->user = strdup(rhdr.uname);
        else
                f->user = "none";
        return 0;
}

char*
rwalk(Fid *f)
{
        Fid *nf;
        Ram *r;
        char *err;
        char *name;
        Ram *dir;
        int i;

        nf = nil;
        if(f->ram->busy == 0)
                return Enotexist;
        if(f->open)
                return Eisopen;
        if(rhdr.newfid != rhdr.fid){
                nf = newfid(rhdr.newfid);
                nf->busy = 1;
                nf->open = 0;
                nf->rclose = 0;
                nf->ram = f->ram;
                nf->user = f->user;     /* no ref count; the leakage is minor */
                f = nf;
        }

        thdr.nwqid = 0;
        err = nil;
        r = f->ram;

        if(rhdr.nwname > 0){
                for(i=0; i<rhdr.nwname; i++){
                        if((r->qid.type & QTDIR) == 0){
                                err = Enotdir;
                                break;
                        }
                        if(r->busy == 0){
                                err = Enotexist;
                                break;
                        }
                        r->atime = time(0);
                        name = rhdr.wname[i];
                        dir = r;
                        if(!perm(Pexec)){
                                err = Eperm;
                                break;
                        }
                        if(strcmp(name, "..") == 0){
                                r = dir->parent;
   Accept:
                                if(i == MAXWELEM){
                                        err = "name too long";
                                        break;
                                }
                                thdr.wqid[thdr.nwqid++] = r->qid;
                                continue;
                        }
                        if(!dir->replete)
                                popdir(dir);
                        for(r=dir->child; r; r=r->next)
                                if(r->busy && strcmp(name, r->name)==0)
                                        goto Accept;
                        break;  /* file not found */
                }

                if(i==0 && err == nil)
                        err = Enotexist;
        }

        if(err!=nil || thdr.nwqid<rhdr.nwname){
                if(nf){
                        nf->busy = 0;
                        nf->open = 0;
                        nf->ram = 0;
                }
        }else if(thdr.nwqid  == rhdr.nwname)
                f->ram = r;

        return err;

}

char *
ropen(Fid *f)
{
        Ram *r;
        int mode, trunc;

        if(f->open)
                return Eisopen;
        r = f->ram;
        if(r->busy == 0)
                return Enotexist;
        if(r->perm & DMEXCL)
                if(r->open)
                        return Excl;
        mode = rhdr.mode;
        if(r->qid.type & QTDIR){
                if(mode != OREAD)
                        return Eperm;
                thdr.qid = r->qid;
                return 0;
        }
        if(mode & ORCLOSE)
                return Eperm;
        trunc = mode & OTRUNC;
        mode &= OPERM;
        if(mode==OWRITE || mode==ORDWR || trunc)
                if(!perm(Pwrite))
                        return Eperm;
        if(mode==OREAD || mode==ORDWR)
                if(!perm(Pread))
                        return Eperm;
        if(mode==OEXEC)
                if(!perm(Pexec))
                        return Eperm;
        if(trunc && (r->perm&DMAPPEND)==0){
                r->ndata = 0;
                dotrunc(r);
                r->qid.vers++;
        }
        thdr.qid = r->qid;
        thdr.iounit = messagesize-IOHDRSZ;
        f->open = 1;
        r->open++;
        return 0;
}

char *
rcreate(Fid *f)
{
        USED(f);

        return Eperm;
}

char*
rread(Fid *f)
{
        int i, len;
        Ram *r;
        char *buf;
        uvlong off, end;
        int n, cnt;

        if(f->ram->busy == 0)
                return Enotexist;
        n = 0;
        thdr.count = 0;
        off = rhdr.offset;
        end = rhdr.offset + rhdr.count;
        cnt = rhdr.count;
        if(cnt > messagesize-IOHDRSZ)
                cnt = messagesize-IOHDRSZ;
        buf = thdr.data;
        if(f->ram->qid.type & QTDIR){
                if(!f->ram->replete)
                        popdir(f->ram);
                for(i=0,r=f->ram->child; r!=nil && i<end; r=r->next){
                        if(!r->busy)
                                continue;
                        len = ramstat(r, (uchar*)buf+n, cnt-n);
                        if(len <= BIT16SZ)
                                break;
                        if(i >= off)
                                n += len;
                        i += len;
                }
                thdr.count = n;
                return 0;
        }
        r = f->ram;
        if(off >= r->ndata)
                return 0;
        r->atime = time(0);
        n = cnt;
        if(off+n > r->ndata)
                n = r->ndata - off;
        thdr.data = doread(r, off, n);
        thdr.count = n;
        return 0;
}

char*
rwrite(Fid *f)
{
        Ram *r;
        ulong off;
        int cnt;

        r = f->ram;
        if(dopermw(f->ram)==0)
                return Eperm;
        if(r->busy == 0)
                return Enotexist;
        off = rhdr.offset;
        if(r->perm & DMAPPEND)
                off = r->ndata;
        cnt = rhdr.count;
        if(r->qid.type & QTDIR)
                return "file is a directory";
        if(off > 100*1024*1024)         /* sanity check */
                return "write too big";
        dowrite(r, rhdr.data, off, cnt);
        r->qid.vers++;
        r->mtime = time(0);
        thdr.count = cnt;
        return 0;
}

char *
rclunk(Fid *f)
{
        if(f->open)
                f->ram->open--;
        f->busy = 0;
        f->open = 0;
        f->ram = 0;
        return 0;
}

char *
rremove(Fid *f)
{
        USED(f);
        return Eperm;
}

char *
rstat(Fid *f)
{
        if(f->ram->busy == 0)
                return Enotexist;
        thdr.nstat = ramstat(f->ram, thdr.stat, messagesize-IOHDRSZ);
        return 0;
}

char *
rwstat(Fid *f)
{
        if(f->ram->busy == 0)
                return Enotexist;
        return Eperm;
}

int
ramstat(Ram *r, uchar *buf, int nbuf)
{
        Dir dir;

        dir.name = r->name;
        dir.qid = r->qid;
        dir.mode = r->perm;
        dir.length = r->ndata;
        dir.uid = r->user;
        dir.gid = r->group;
        dir.muid = r->user;
        dir.atime = r->atime;
        dir.mtime = r->mtime;
        return convD2M(&dir, buf, nbuf);
}

Fid *
newfid(int fid)
{
        Fid *f, *ff;

        ff = 0;
        for(f = fids; f; f = f->next)
                if(f->fid == fid)
                        return f;
                else if(!ff && !f->busy)
                        ff = f;
        if(ff){
                ff->fid = fid;
                ff->open = 0;
                ff->busy = 1;
        }
        f = emalloc(sizeof *f);
        f->ram = 0;
        f->fid = fid;
        f->busy = 1;
        f->open = 0;
        f->next = fids;
        fids = f;
        return f;
}

void
io(void)
{
        char *err;
        int n, nerr;
        char buf[ERRMAX];

        errstr(buf, sizeof buf);
        for(nerr=0, buf[0]='\0'; nerr<100; nerr++){
                /*
                 * reading from a pipe or a network device
                 * will give an error after a few eof reads
                 * however, we cannot tell the difference
                 * between a zero-length read and an interrupt
                 * on the processes writing to us,
                 * so we wait for the error
                 */
                n = read9pmsg(mfd[0], mdata, sizeof mdata);
                if(n==0)
                        continue;
                if(n < 0){
                        if(buf[0]=='\0')
                                errstr(buf, sizeof buf);
                        continue;
                }
                nerr = 0;
                buf[0] = '\0';
                if(convM2S(mdata, n, &rhdr) != n)
                        error("convert error in convM2S");

                if(verbose)
                        fprint(2, "tapefs: <=%F\n", &rhdr);/**/

                thdr.data = (char*)mdata + IOHDRSZ;
                thdr.stat = mdata + IOHDRSZ;
                if(!fcalls[rhdr.type])
                        err = "bad fcall type";
                else
                        err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
                if(err){
                        thdr.type = Rerror;
                        thdr.ename = err;
                }else{
                        thdr.type = rhdr.type + 1;
                        thdr.fid = rhdr.fid;
                }
                thdr.tag = rhdr.tag;
                n = convS2M(&thdr, mdata, messagesize);
                if(n <= 0)
                        error("convert error in convS2M");
                if(verbose)
                        fprint(2, "tapefs: =>%F\n", &thdr);/**/
                if(write(mfd[1], mdata, n) != n)
                        error("mount write");
        }
        if(buf[0]=='\0' || strstr(buf, "hungup"))
                exits("");
        fprint(2, "%s: mount read: %s\n", argv0, buf);
        exits(buf);
}

int
perm(int p)
{
        if(p==Pwrite)
                return 0;
        return 1;
}

void
error(char *s)
{
        fprint(2, "%s: %s: ", argv0, s);
        perror("");
        exits(s);
}

char*
estrdup(char *s)
{
        char *t;

        t = emalloc(strlen(s)+1);
        strcpy(t, s);
        return t;
}

void *
emalloc(ulong n)
{
        void *p;
        p = mallocz(n, 1);
        if(!p)
                error("out of memory");
        return p;
}

void *
erealloc(void *p, ulong n)
{
        p = realloc(p, n);
        if(!p)
                error("out of memory");
        return p;
}

void
usage(void)
{
        fprint(2, "usage: %s [-s] [-m mountpoint]\n", argv0);
        exits("usage");
}