Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include        <u.h>
#include        <libc.h>
#include        "compat.h"
#include        "error.h"

Chan*
newchan(void)
{
        Chan *c;

        c = smalloc(sizeof(Chan));

        /* if you get an error before associating with a dev,
           close calls rootclose, a nop */
        c->type = 0;
        c->flag = 0;
        c->ref = 1;
        c->dev = 0;
        c->offset = 0;
        c->iounit = 0;
        c->aux = 0;
        c->name = 0;
        return c;
}

void
chanfree(Chan *c)
{
        c->flag = CFREE;

        cnameclose(c->name);
        free(c);
}

void
cclose(Chan *c)
{
        if(c->flag&CFREE)
                panic("cclose %#p", getcallerpc(&c));
        if(decref(c))
                return;

        if(!waserror()){
                devtab[c->type]->close(c);
                poperror();
        }

        chanfree(c);
}

Chan*
cclone(Chan *c)
{
        Chan *nc;
        Walkqid *wq;

        wq = devtab[c->type]->walk(c, nil, nil, 0);
        if(wq == nil)
                error("clone failed");
        nc = wq->clone;
        free(wq);
        nc->name = c->name;
        if(c->name)
                incref(c->name);
        return nc;
}

enum
{
        CNAMESLOP       = 20
};

static Ref ncname;

void cleancname(Cname*);

int
isdotdot(char *p)
{
        return p[0]=='.' && p[1]=='.' && p[2]=='\0';
}

int
incref(Ref *r)
{
        int x;

        lock(r);
        x = ++r->ref;
        unlock(r);
        return x;
}

int
decref(Ref *r)
{
        int x;

        lock(r);
        x = --r->ref;
        unlock(r);
        if(x < 0)
                panic("decref");

        return x;
}

Cname*
newcname(char *s)
{
        Cname *n;
        int i;

        n = smalloc(sizeof(Cname));
        i = strlen(s);
        n->len = i;
        n->alen = i+CNAMESLOP;
        n->s = smalloc(n->alen);
        memmove(n->s, s, i+1);
        n->ref = 1;
        incref(&ncname);
        return n;
}

void
cnameclose(Cname *n)
{
        if(n == nil)
                return;
        if(decref(n))
                return;
        decref(&ncname);
        free(n->s);
        free(n);
}

Cname*
addelem(Cname *n, char *s)
{
        int i, a;
        char *t;
        Cname *new;

        if(s[0]=='.' && s[1]=='\0')
                return n;

        if(n->ref > 1){
                /* copy on write */
                new = newcname(n->s);
                cnameclose(n);
                n = new;
        }

        i = strlen(s);
        if(n->len+1+i+1 > n->alen){
                a = n->len+1+i+1 + CNAMESLOP;
                t = smalloc(a);
                memmove(t, n->s, n->len+1);
                free(n->s);
                n->s = t;
                n->alen = a;
        }
        if(n->len>0 && n->s[n->len-1]!='/' && s[0]!='/')        /* don't insert extra slash if one is present */
                n->s[n->len++] = '/';
        memmove(n->s+n->len, s, i+1);
        n->len += i;
        if(isdotdot(s))
                cleancname(n);
        return n;
}

/*
 * In place, rewrite name to compress multiple /, eliminate ., and process ..
 */
void
cleancname(Cname *n)
{
        char *p;

        if(n->s[0] == '#'){
                p = strchr(n->s, '/');
                if(p == nil)
                        return;
                cleanname(p);

                /*
                 * The correct name is #i rather than #i/,
                 * but the correct name of #/ is #/.
                 */
                if(strcmp(p, "/")==0 && n->s[1] != '/')
                        *p = '\0';
        }else
                cleanname(n->s);
        n->len = strlen(n->s);
}

void
isdir(Chan *c)
{
        if(c->qid.type & QTDIR)
                return;
        error(Enotdir);
}