204 lines
2.6 KiB
C
204 lines
2.6 KiB
C
#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);
|
|
}
|