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        "compat.h"
#include        "error.h"

extern ulong    kerndate;

void
mkqid(Qid *q, vlong path, ulong vers, int type)
{
        q->type = type;
        q->vers = vers;
        q->path = path;
}

int
devno(int c, int user)
{
        int i;

        for(i = 0; devtab[i] != nil; i++){
                if(devtab[i]->dc == c)
                        return i;
        }
        if(user == 0)
                panic("devno %C 0x%ux", c, c);

        return -1;
}

void
devdir(Chan *c, Qid qid, char *n, vlong length, char *user, long perm, Dir *db)
{
        db->name = n;
        db->qid = qid;
        db->type = devtab[c->type]->dc;
        db->dev = c->dev;
        db->mode = (qid.type << 24) | perm;
        db->atime = seconds();
        db->mtime = kerndate;
        db->length = length;
        db->uid = user;
        db->gid = eve;
        db->muid = user;
}

/*
 * the zeroth element of the table MUST be the directory itself for ..
*/
int
devgen(Chan *c, Dirtab *tab, int ntab, int i, Dir *dp)
{
        if(tab == 0)
                return -1;
        if(i != DEVDOTDOT){
                i++; /* skip first element for . itself */
                if(i >= ntab)
                        return -1;
                tab += i;
        }
        devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
        return 1;
}

void
devreset(void)
{
}

void
devinit(void)
{
}

Chan*
devattach(int tc, char *spec)
{
        Chan *c;
        char *buf;

        c = newchan();
        mkqid(&c->qid, 0, 0, QTDIR);
        c->type = devno(tc, 0);
        if(spec == nil)
                spec = "";
        buf = smalloc(4+strlen(spec)+1);
        sprint(buf, "#%C%s", tc, spec);
        c->name = newcname(buf);
        free(buf);
        return c;
}


Chan*
devclone(Chan *c)
{
        Chan *nc;

        if(c->flag & COPEN)
                panic("clone of open file type %C\n", devtab[c->type]->dc);

        nc = newchan();

        nc->type = c->type;
        nc->dev = c->dev;
        nc->mode = c->mode;
        nc->qid = c->qid;
        nc->offset = c->offset;
        nc->aux = c->aux;
        return nc;
}

Walkqid*
devwalk(Chan *c, Chan *nc, char **name, int nname, Dirtab *tab, int ntab, Devgen *gen)
{
        int i, j, alloc;
        Walkqid *wq;
        char *n;
        Dir dir;

        isdir(c);

        alloc = 0;
        wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
        if(waserror()){
                if(alloc && wq->clone!=nil)
                        cclose(wq->clone);
                free(wq);
                return nil;
        }
        if(nc == nil){
                nc = devclone(c);
                nc->type = 0;   /* device doesn't know about this channel yet */
                alloc = 1;
        }
        wq->clone = nc;

        for(j=0; j<nname; j++){
                isdir(nc);
                n = name[j];
                if(strcmp(n, ".") == 0){
    Accept:
                        wq->qid[wq->nqid++] = nc->qid;
                        continue;
                }
                if(strcmp(n, "..") == 0){
                        (*gen)(nc, tab, ntab, DEVDOTDOT, &dir);
                        nc->qid = dir.qid;
                        goto Accept;
                }
                for(i=0;; i++){
                        switch((*gen)(nc, tab, ntab, i, &dir)){
                        case -1:
                                if(j == 0)
                                        error(Enonexist);
                                strncpy(up->error, Enonexist, ERRMAX);
                                goto Done;
                        case 0:
                                continue;
                        case 1:
                                if(strcmp(n, dir.name) == 0){
                                        nc->qid = dir.qid;
                                        goto Accept;
                                }
                                continue;
                        }
                }
        }
        /*
         * We processed at least one name, so will return some data.
         * If we didn't process all nname entries succesfully, we drop
         * the cloned channel and return just the Qids of the walks.
         */
Done:
        poperror();
        if(wq->nqid < nname){
                if(alloc)
                        cclose(wq->clone);
                wq->clone = nil;
        }else if(wq->clone){
                /* attach cloned channel to same device */
                wq->clone->type = c->type;
        }
        return wq;
}

int
devstat(Chan *c, uchar *db, int n, Dirtab *tab, int ntab, Devgen *gen)
{
        int i;
        Dir dir;
        char *p, *elem;

        for(i=0;; i++)
                switch((*gen)(c, tab, ntab, i, &dir)){
                case -1:
                        if(c->qid.type & QTDIR){
                                if(c->name == nil)
                                        elem = "???";
                                else if(strcmp(c->name->s, "/") == 0)
                                        elem = "/";
                                else
                                        for(elem=p=c->name->s; *p; p++)
                                                if(*p == '/')
                                                        elem = p+1;
                                devdir(c, c->qid, elem, 0, eve, DMDIR|0555, &dir);
                                return convD2M(&dir, db, n);
                        }

                        error(Enonexist);
                case 0:
                        break;
                case 1:
                        if(c->qid.path == dir.qid.path){
                                return convD2M(&dir, db, n);
                        }
                        break;
                }
}

long
devdirread(Chan *c, char *d, long n, Dirtab *tab, int ntab, Devgen *gen)
{
        long k, m, dsz;
        struct{
                Dir;
                char slop[100];
        }dir;

        k = c->offset;
        for(m=0; m<n; k++){
                switch((*gen)(c, tab, ntab, k, &dir)){
                case -1:
                        return m;

                case 0:
                        c->offset++;    /* BUG??? (was DIRLEN: skip entry) */
                        break;

                case 1:
                        dsz = convD2M(&dir, (uchar*)d, n-m);
                        if(dsz <= BIT16SZ){     /* <= not < because this isn't stat; read is stuck */
                                if(m == 0)
                                        return -1;
                                return m;
                        }
                        m += dsz;
                        d += dsz;
                        break;
                }
        }

        return m;
}

/*
 * error(Eperm) if open permission not granted for up->user.
 */
void
devpermcheck(char *fileuid, ulong perm, int omode)
{
        ulong t;
        static int access[] = { 0400, 0200, 0600, 0100 };

        if(strcmp(up->user, fileuid) == 0)
                perm <<= 0;
        else
        if(strcmp(up->user, eve) == 0)
                perm <<= 3;
        else
                perm <<= 6;

        t = access[omode&3];
        if((t&perm) != t)
                error(Eperm);
}

Chan*
devopen(Chan *c, int omode, Dirtab *tab, int ntab, Devgen *gen)
{
        int i;
        Dir dir;

        for(i=0;; i++){
                switch((*gen)(c, tab, ntab, i, &dir)){
                case -1:
                        goto Return;
                case 0:
                        break;
                case 1:
                        if(c->qid.path == dir.qid.path){
                                devpermcheck(dir.uid, dir.mode, omode);
                                goto Return;
                        }
                        break;
                }
        }
Return:
        c->offset = 0;
        if((c->qid.type&QTDIR) && omode!=OREAD)
                error(Eperm);
        c->mode = openmode(omode);
        c->flag |= COPEN;
        return c;
}

void
devcreate(Chan*, char*, int, ulong)
{
        error(Eperm);
}

Block*
devbread(Chan *, long, ulong)
{
        panic("no block read");
        return nil;
}

long
devbwrite(Chan *, Block *, ulong)
{
        panic("no block write");
        return 0;
}

void
devremove(Chan*)
{
        error(Eperm);
}

int
devwstat(Chan*, uchar*, int)
{
        error(Eperm);
        return 0;
}