/*
* allocate 'count' contiguous channels
* of type 'type' and return pointer to base
*/
Chan*
fs_chaninit(int count, int data)
{
uchar *p;
Chan *cp, *icp;
int i;
/*
* returns a locked file structure
*/
File*
filep(Chan *cp, ulong fid, int flag)
{
File *f;
int h;
if(fid == NOFID)
return 0;
h = (long)(uintptr)cp + fid;
if(h < 0)
h = ~h;
h %= nelem(flist);
loop:
lock(&flock);
for(f=flist[h]; f; f=f->next)
if(f->fid == fid && f->cp == cp){
/*
* Already in use is an error
* when called from attach or clone (walk
* in 9P2000). The console uses FID[12] and
* never clunks them so catch that case.
*/
if(flag == 0 || cp == cons.chan)
goto out;
unlock(&flock);
return 0;
}
int
iaccess(File *f, Dentry *d, int m)
{
/* uid none gets only other permissions */
if(f->uid != 0) {
/*
* owner
*/
if(f->uid == d->uid)
if((m<<6) & d->mode)
return 0;
/*
* group membership
*/
if(ingroup(f->uid, d->gid))
if((m<<3) & d->mode)
return 0;
}
/* other */
if(m & d->mode) {
/*
* walk directories regardless.
* otherwise its impossible to get
* from the root to noworld's directories.
*/
if((d->mode & DDIR) && (m == DEXEC))
return 0;
if(!ingroup(f->uid, 9999))
return 0;
}
/* read access for du */
if(duallow != 0 && duallow == f->uid)
if((d->mode & DDIR) && (m == DREAD || m == DEXEC))
return 0;
/* allow god */
return !isallowed(f);
}
int
isallowed(File *f)
{
if(f->cp == cons.chan)
return 1;
switch(allowed){
case 0:
return 0;
case -1:
return 1;
default:
return f->uid == allowed;
}
}
Off
qidpathgen(Device *dev)
{
Iobuf *p;
Superb *sb;
Off path;
p = getbuf(dev, superaddr(dev), Brd|Bmod);
if(!p || checktag(p, Tsuper, QPSUPER))
panic("newqid: super block");
sb = (Superb*)p->iobuf;
do {
path = ++sb->qidgen;
} while(path == QPDIR);
putbuf(p);
return path;
}
/* truncating to length > 0 */
static void
truncfree(Truncstate *ts, Device *dev, int d, Iobuf *p, int i)
{
int pastlast;
Off a;
pastlast = ts->pastlast;
a = ((Off *)p->iobuf)[i];
if (d > 0 || pastlast)
buffree(dev, a, d, ts);
if (pastlast) {
((Off *)p->iobuf)[i] = 0;
p->flags |= Bmod|Bimm;
} else if (d == 0 && ts->relblk == ts->lastblk)
ts->pastlast = 1;
if (d == 0)
ts->relblk++;
}
/*
* free the block at `addr' on dev.
* if it's an indirect block (d [depth] > 0),
* first recursively free all the blocks it names.
*
* ts->relblk is the block number within the file of this
* block (or the first data block eventually pointed to via
* this indirect block).
*/
void
buffree(Device *dev, Off addr, int d, Truncstate *ts)
{
Iobuf *p;
Off a;
int i, pastlast;
if(!addr)
return;
pastlast = (ts == nil? 1: ts->pastlast);
/*
* if this is an indirect block, recurse and free any
* suitable blocks within it (possibly via further indirect blocks).
*/
if(d > 0) {
d--;
p = getbuf(dev, addr, Brd);
if(p) {
if (ts == nil) /* common case: create */
for(i=INDPERBUF-1; i>=0; i--) {
a = ((Off *)p->iobuf)[i];
buffree(dev, a, d, nil);
}
else /* wstat truncation */
for (i = 0; i < INDPERBUF; i++)
truncfree(ts, dev, d, p, i);
putbuf(p);
}
}
if (!pastlast)
return;
/*
* having zeroed the pointer to this block, add it to the free list.
* stop outstanding i/o
*/
p = getbuf(dev, addr, Bprobe);
if(p) {
p->flags &= ~(Bmod|Bimm);
putbuf(p);
}
/*
* dont put written worm
* blocks into free list
*/
if(dev->type == Devcw) {
i = cwfree(dev, addr);
if(i)
return;
}
p = getbuf(dev, superaddr(dev), Brd|Bmod);
if(!p || checktag(p, Tsuper, QPSUPER))
panic("buffree: super block");
addfree(dev, addr, (Superb*)p->iobuf);
putbuf(p);
}
Off
bufalloc(Device *dev, int tag, long qid, int uid)
{
Iobuf *bp, *p;
Superb *sb;
Off a, n;
loop:
n = --sb->fbuf.nfree;
sb->tfree--;
if(n < 0 || n >= FEPERBUF) {
fprint(2, "bufalloc: %Z: bad freelist\n", dev);
n = 0;
sb->fbuf.free[0] = 0;
}
a = sb->fbuf.free[n];
if(n <= 0) {
if(a == 0) {
sb->tfree = 0;
sb->fbuf.nfree = 1;
if(dev->type == Devcw) {
n = uid;
if(n < 0 || n >= nelem(growacct))
n = 0;
growacct[n]++;
if(cwgrow(dev, sb, uid))
goto loop;
}
putbuf(p);
fprint(2, "fs %Z full uid=%d\n", dev, uid);
return 0;
}
bp = getbuf(dev, a, Brd);
if(!bp || checktag(bp, Tfree, QPNONE)) {
if(bp)
putbuf(bp);
putbuf(p);
return 0;
}
sb->fbuf = *(Fbuf*)bp->iobuf;
putbuf(bp);
}
bp = getbuf(dev, a, Bmod);
memset(bp->iobuf, 0, RBUFSIZE);
settag(bp, tag, qid);
if(tag == Tind1 || tag == Tind2 ||
#ifndef COMPAT32
tag == Tind3 || tag == Tind4 || /* add more Tind tags here ... */
#endif
tag == Tdir)
bp->flags |= Bimm;
putbuf(bp);
putbuf(p);
return a;
}
/*
* what are legal characters in a name?
* only disallow control characters.
* utf avoids control characters, so we
* only need to inspect the ascii range.
*/
int
checkname(char *n)
{
int i, c;
/*
* pre-allocate some message buffers at boot time.
* if this supply is exhausted, more will be allocated as needed.
*/
void
mbinit(void)
{
Msgbuf *mb;
Rabuf *rb;
int i;
for (;;)
switch(d->type) {
case Devcw:
return cwread(d, b, c);
case Devjuke:
d = d->j.m;
break;
case Devro:
return roread(d, b, c);
case Devwren:
return wrenread(d, b, c);
case Devworm:
case Devlworm:
return wormread(d, b, c);
case Devfworm:
return fwormread(d, b, c);
case Devmcat:
return mcatread(d, b, c);
case Devmlev:
return mlevread(d, b, c);
case Devmirr:
return mirrread(d, b, c);
case Devpart:
return partread(d, b, c);
case Devswab:
e = devread(d->swab.d, b, c);
if(e == 0)
swab(c, 0);
return e;
case Devnone:
fprint(2, "read from device none(%lld)\n", (Wideoff)b);
return 1;
default:
panic("illegal device in devread: %Z %lld",
d, (Wideoff)b);
return 1;
}
}
int
devwrite(Device *d, Off b, void *c)
{
int e;
/*
* set readonly to non-0 to prevent all writes;
* mainly for trying dangerous experiments.
*/
if (readonly)
return 0;
for (;;)
switch(d->type) {
case Devcw:
return cwwrite(d, b, c);
case Devjuke:
d = d->j.m;
break;
case Devro:
fprint(2, "write to ro device %Z(%lld)\n", d, (Wideoff)b);
return 1;
case Devwren:
return wrenwrite(d, b, c);
case Devworm:
case Devlworm:
return wormwrite(d, b, c);
case Devfworm:
return fwormwrite(d, b, c);
case Devmcat:
return mcatwrite(d, b, c);
case Devmlev:
return mlevwrite(d, b, c);
case Devmirr:
return mirrwrite(d, b, c);
case Devpart:
return partwrite(d, b, c);
case Devswab:
swab(c, 1);
e = devwrite(d->swab.d, b, c);
swab(c, 0);
return e;
case Devnone:
/* checktag() can generate blocks with type devnone */
return 0;
default:
panic("illegal device in devwrite: %Z %lld",
d, (Wideoff)b);
return 1;
}
}
Devsize
devsize(Device *d)
{
for (;;)
switch(d->type) {
case Devcw:
case Devro:
return cwsize(d);
case Devjuke:
d = d->j.m;
break;
case Devwren:
return wrensize(d);
case Devworm:
case Devlworm:
return wormsize(d);
case Devfworm:
return fwormsize(d);
case Devmcat:
return mcatsize(d);
case Devmlev:
return mlevsize(d);
case Devmirr:
return mirrsize(d);
case Devpart:
return partsize(d);
case Devswab:
d = d->swab.d;
break;
default:
panic("illegal device in devsize: %Z", d);
return 0;
}
}
/* result is malloced */
char *
sdof(Device *d)
{
static char name[256];
for (;;)
switch(d->type) {
case Devjuke:
d = d->j.j; /* robotics */
break;
case Devwren:
snprint(name, sizeof name, "/dev/sd%d%d", d->wren.ctrl,
d->wren.targ);
return strdup(name);
case Devswab:
d = d->swab.d;
break;
default:
panic("illegal device in sdof: %Z", d);
return nil;
}
}
Off
superaddr(Device *d)
{
for (;;)
switch(d->type) {
default:
return SUPER_ADDR;
case Devcw:
case Devro:
return cwsaddr(d);
case Devswab:
d = d->swab.d;
break;
}
}
Off
getraddr(Device *d)
{
for (;;)
switch(d->type) {
default:
return ROOT_ADDR;
case Devcw:
case Devro:
return cwraddr(d);
case Devswab:
d = d->swab.d;
break;
}
}
void
devream(Device *d, int top)
{
Device *l;
loop:
if(chatty)
print("\tdevream %Z %d\n", d, top);
switch(d->type) {
default:
fprint(2, "devream: unknown dev type %Z\n", d);
return;
/*
* swab a block
* flag = 0 -- convert from foreign to native
* flag = 1 -- convert from native to foreign
*/
void
swab(void *c, int flag)
{
uchar *p;
Tag *t;
int i, j;
Dentry *d;
Cache *h;
Bucket *b;
Superb *s;
Fbuf *f;
Off *l;
/* swab the tag */
p = (uchar*)c;
t = (Tag*)(p + BUFSIZE);
if(!flag) {
swab2(&t->pad);
swab2(&t->tag);
swaboff(&t->path);
}
/* swab each block type */
switch(t->tag) {
default:
fprint(2, "no swab for tag=%G rw=%d\n", t->tag, flag);
hexdump(p, 256);
panic("swab");
break;
case Tsuper:
s = (Superb*)p;
swaboff(&s->fbuf.nfree);
for(i=0; i<FEPERBUF; i++)
swaboff(&s->fbuf.free[i]);
swaboff(&s->fstart);
swaboff(&s->fsize);
swaboff(&s->tfree);
swaboff(&s->qidgen);
swaboff(&s->cwraddr);
swaboff(&s->roraddr);
swaboff(&s->last);
swaboff(&s->next);
break;
case Tind1:
case Tind2:
#ifndef COMPAT32
case Tind3:
case Tind4:
/* add more Tind tags here ... */
#endif
l = (Off *)p;
for(i=0; i<INDPERBUF; i++) {
swaboff(l);
l++;
}
break;
case Tfree:
f = (Fbuf*)p;
swaboff(&f->nfree);
for(i=0; i<FEPERBUF; i++)
swaboff(&f->free[i]);
break;
case Tcache:
h = (Cache*)p;
swaboff(&h->maddr);
swaboff(&h->msize);
swaboff(&h->caddr);
swaboff(&h->csize);
swaboff(&h->fsize);
swaboff(&h->wsize);
swaboff(&h->wmax);
swaboff(&h->sbaddr);
swaboff(&h->cwraddr);
swaboff(&h->roraddr);
swab4(&h->toytime);
swab4(&h->time);
break;
case Tnone: // unitialized
case Tfile: // someone elses problem
case Tvirgo: // bit map -- all bytes
case Tconfig: // configuration string -- all bytes
break;
}
/* swab the tag */
if(flag) {
swab2(&t->pad);
swab2(&t->tag);
swaboff(&t->path);
}
}