9660srv: import from Plan 9

This commit is contained in:
Russ Cox 2008-07-20 03:23:19 -04:00
parent 29e9b5683e
commit 78a779a383
9 changed files with 2135 additions and 0 deletions

896
src/cmd/9660srv/9660srv.c Normal file
View file

@ -0,0 +1,896 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include "dat.h"
#include "fns.h"
#include "iso9660.h"
static void ireset(void);
static int iattach(Xfile*);
static void iclone(Xfile*, Xfile*);
static void iwalkup(Xfile*);
static void iwalk(Xfile*, char*);
static void iopen(Xfile*, int);
static void icreate(Xfile*, char*, long, int);
static long ireaddir(Xfile*, uchar*, long, long);
static long iread(Xfile*, char*, vlong, long);
static long iwrite(Xfile*, char*, vlong, long);
static void iclunk(Xfile*);
static void iremove(Xfile*);
static void istat(Xfile*, Dir*);
static void iwstat(Xfile*, Dir*);
static char* nstr(uchar*, int);
static char* rdate(uchar*, int);
static int getcontin(Xdata*, uchar*, uchar**);
static int getdrec(Xfile*, void*);
static void ungetdrec(Xfile*);
static int opendotdot(Xfile*, Xfile*);
static int showdrec(int, int, void*);
static long gtime(uchar*);
static long l16(void*);
static long l32(void*);
static void newdrec(Xfile*, Drec*);
static int rzdir(Xfs*, Dir*, int, Drec*);
Xfsub isosub =
{
ireset, iattach, iclone, iwalkup, iwalk, iopen, icreate,
ireaddir, iread, iwrite, iclunk, iremove, istat, iwstat
};
static void
ireset(void)
{}
static int
iattach(Xfile *root)
{
Xfs *cd = root->xf;
Iobuf *p; Voldesc *v; Isofile *fp; Drec *dp;
int fmt, blksize, i, n, l, haveplan9;
Iobuf *dirp;
uchar dbuf[256];
Drec *rd = (Drec *)dbuf;
uchar *q, *s;
dirp = nil;
blksize = 0;
fmt = 0;
dp = nil;
haveplan9 = 0;
for(i=VOLDESC;i<VOLDESC+100; i++){ /* +100 for sanity */
p = getbuf(cd->d, i);
v = (Voldesc*)(p->iobuf);
if(memcmp(v->byte, "\01CD001\01", 7) == 0){ /* iso */
if(dirp)
putbuf(dirp);
dirp = p;
fmt = 'z';
dp = (Drec*)v->z.desc.rootdir;
blksize = l16(v->z.desc.blksize);
chat("iso, blksize=%d...", blksize);
v = (Voldesc*)(dirp->iobuf);
haveplan9 = (strncmp((char*)v->z.boot.sysid, "PLAN 9", 6)==0);
if(haveplan9){
if(noplan9) {
chat("ignoring plan9");
haveplan9 = 0;
} else {
fmt = '9';
chat("plan9 iso...");
}
}
continue;
}
if(memcmp(&v->byte[8], "\01CDROM\01", 7) == 0){ /* high sierra */
if(dirp)
putbuf(dirp);
dirp = p;
fmt = 'r';
dp = (Drec*)v->r.desc.rootdir;
blksize = l16(v->r.desc.blksize);
chat("high sierra, blksize=%d...", blksize);
continue;
}
if(haveplan9==0 && !nojoliet
&& memcmp(v->byte, "\02CD001\01", 7) == 0){
chat("%d %d\n", haveplan9, nojoliet);
/*
* The right thing to do is walk the escape sequences looking
* for one of 25 2F 4[035], but Microsoft seems to not honor
* the format, which makes it hard to walk over.
*/
q = v->z.desc.escapes;
if(q[0] == 0x25 && q[1] == 0x2F && (q[2] == 0x40 || q[2] == 0x43 || q[2] == 0x45)){ /* Joliet, it appears */
if(dirp)
putbuf(dirp);
dirp = p;
fmt = 'J';
dp = (Drec*)v->z.desc.rootdir;
if(blksize != l16(v->z.desc.blksize))
fprint(2, "warning: suspicious Joliet blocksize\n");
chat("joliet...");
continue;
}
}
putbuf(p);
if(v->byte[0] == 0xFF)
break;
}
if(fmt == 0){
if(dirp)
putbuf(dirp);
return -1;
}
assert(dirp != nil);
if(chatty)
showdrec(2, fmt, dp);
if(blksize > Sectorsize){
chat("blksize too big...");
putbuf(dirp);
return -1;
}
if(waserror()){
putbuf(dirp);
nexterror();
}
root->len = sizeof(Isofile) - sizeof(Drec) + dp->reclen;
root->ptr = fp = ealloc(root->len);
if(haveplan9)
root->xf->isplan9 = 1;
fp->fmt = fmt;
fp->blksize = blksize;
fp->offset = 0;
fp->doffset = 0;
memmove(&fp->d, dp, dp->reclen);
root->qid.path = l32(dp->addr);
root->qid.type = QTDIR;
putbuf(dirp);
poperror();
if(getdrec(root, rd) >= 0){
n = rd->reclen-(34+rd->namelen);
s = (uchar*)rd->name + rd->namelen;
if((uintptr)s & 1){
s++;
n--;
}
if(n >= 7 && s[0] == 'S' && s[1] == 'P' && s[2] == 7 &&
s[3] == 1 && s[4] == 0xBE && s[5] == 0xEF){
root->xf->issusp = 1;
root->xf->suspoff = s[6];
n -= root->xf->suspoff;
s += root->xf->suspoff;
for(; n >= 4; s += l, n -= l){
l = s[2];
if(s[0] == 'E' && s[1] == 'R'){
if(!norock && s[4] == 10 && memcmp(s+8, "RRIP_1991A", 10) == 0)
root->xf->isrock = 1;
break;
} else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){
n = getcontin(root->xf->d, s, &s);
continue;
} else if(s[0] == 'R' && s[1] == 'R'){
if(!norock)
root->xf->isrock = 1;
break;
} else if(s[0] == 'S' && s[1] == 'T')
break;
}
}
}
if(root->xf->isrock)
chat("Rock Ridge...");
fp->offset = 0;
fp->doffset = 0;
return 0;
}
static void
iclone(Xfile *of, Xfile *nf)
{
USED(of);
USED(nf);
}
static void
iwalkup(Xfile *f)
{
long paddr;
uchar dbuf[256];
Drec *d = (Drec *)dbuf;
Xfile pf, ppf;
Isofile piso, ppiso;
memset(&pf, 0, sizeof pf);
memset(&ppf, 0, sizeof ppf);
pf.ptr = &piso;
ppf.ptr = &ppiso;
if(opendotdot(f, &pf) < 0)
error("can't open pf");
paddr = l32(pf.ptr->d.addr);
if(l32(f->ptr->d.addr) == paddr)
return;
if(opendotdot(&pf, &ppf) < 0)
error("can't open ppf");
while(getdrec(&ppf, d) >= 0){
if(l32(d->addr) == paddr){
newdrec(f, d);
f->qid.path = paddr;
f->qid.type = QTDIR;
return;
}
}
error("can't find addr of ..");
}
static int
casestrcmp(int isplan9, char *a, char *b)
{
int ca, cb;
if(isplan9)
return strcmp(a, b);
for(;;) {
ca = *a++;
cb = *b++;
if(ca >= 'A' && ca <= 'Z')
ca += 'a' - 'A';
if(cb >= 'A' && cb <= 'Z')
cb += 'a' - 'A';
if(ca != cb) {
if(ca > cb)
return 1;
return -1;
}
if(ca == 0)
return 0;
}
}
static void
iwalk(Xfile *f, char *name)
{
Isofile *ip = f->ptr;
uchar dbuf[256];
char nbuf[4*Maxname];
Drec *d = (Drec*)dbuf;
Dir dir;
char *p;
int len, vers, dvers;
vers = -1;
if(p = strchr(name, ';')) { /* assign = */
len = p-name;
if(len >= Maxname)
len = Maxname-1;
memmove(nbuf, name, len);
vers = strtoul(p+1, 0, 10);
name = nbuf;
}
/*
len = strlen(name);
if(len >= Maxname){
len = Maxname-1;
if(name != nbuf){
memmove(nbuf, name, len);
name = nbuf;
}
name[len] = 0;
}
*/
chat("%d \"%s\"...", strlen(name), name);
ip->offset = 0;
setnames(&dir, nbuf);
while(getdrec(f, d) >= 0) {
dvers = rzdir(f->xf, &dir, ip->fmt, d);
if(casestrcmp(f->xf->isplan9||f->xf->isrock, name, dir.name) != 0)
continue;
newdrec(f, d);
f->qid.path = dir.qid.path;
f->qid.type = dir.qid.type;
USED(dvers);
return;
}
USED(vers);
error(Enonexist);
}
static void
iopen(Xfile *f, int mode)
{
mode &= ~OCEXEC;
if(mode != OREAD && mode != OEXEC)
error(Eperm);
f->ptr->offset = 0;
f->ptr->doffset = 0;
}
static void
icreate(Xfile *f, char *name, long perm, int mode)
{
USED(f);
USED(name);
USED(perm);
USED(mode);
error(Eperm);
}
static long
ireaddir(Xfile *f, uchar *buf, long offset, long count)
{
Isofile *ip = f->ptr;
Dir d;
char names[4*Maxname];
uchar dbuf[256];
Drec *drec = (Drec *)dbuf;
int n, rcnt;
if(offset==0){
ip->offset = 0;
ip->doffset = 0;
}else if(offset != ip->doffset)
error("seek in directory not allowed");
rcnt = 0;
setnames(&d, names);
while(rcnt < count && getdrec(f, drec) >= 0){
if(drec->namelen == 1){
if(drec->name[0] == 0)
continue;
if(drec->name[0] == 1)
continue;
}
rzdir(f->xf, &d, ip->fmt, drec);
d.qid.vers = f->qid.vers;
if((n = convD2M(&d, buf+rcnt, count-rcnt)) <= BIT16SZ){
ungetdrec(f);
break;
}
rcnt += n;
}
ip->doffset += rcnt;
return rcnt;
}
static long
iread(Xfile *f, char *buf, vlong offset, long count)
{
int n, o, rcnt = 0;
long size;
vlong addr;
Isofile *ip = f->ptr;
Iobuf *p;
size = l32(ip->d.size);
if(offset >= size)
return 0;
if(offset+count > size)
count = size - offset;
addr = ((vlong)l32(ip->d.addr) + ip->d.attrlen)*ip->blksize + offset;
o = addr % Sectorsize;
addr /= Sectorsize;
/*chat("d.addr=%ld, addr=%lld, o=%d...", l32(ip->d.addr), addr, o);*/
n = Sectorsize - o;
while(count > 0){
if(n > count)
n = count;
p = getbuf(f->xf->d, addr);
memmove(&buf[rcnt], &p->iobuf[o], n);
putbuf(p);
count -= n;
rcnt += n;
++addr;
o = 0;
n = Sectorsize;
}
return rcnt;
}
static long
iwrite(Xfile *f, char *buf, vlong offset, long count)
{
USED(f);
USED(buf);
USED(offset);
USED(count);
error(Eperm);
return 0;
}
static void
iclunk(Xfile *f)
{
USED(f);
}
static void
iremove(Xfile *f)
{
USED(f);
error(Eperm);
}
static void
istat(Xfile *f, Dir *d)
{
Isofile *ip = f->ptr;
rzdir(f->xf, d, ip->fmt, &ip->d);
d->qid.vers = f->qid.vers;
if(d->qid.path==f->xf->rootqid.path){
d->qid.path = 0;
d->qid.type = QTDIR;
}
}
static void
iwstat(Xfile *f, Dir *d)
{
USED(f);
USED(d);
error(Eperm);
}
static int
showdrec(int fd, int fmt, void *x)
{
Drec *d = (Drec *)x;
int namelen;
int syslen;
if(d->reclen == 0)
return 0;
fprint(fd, "%d %d %ld %ld ",
d->reclen, d->attrlen, l32(d->addr), l32(d->size));
fprint(fd, "%s 0x%2.2x %d %d %ld ",
rdate(d->date, fmt), (fmt=='z' ? d->flags : d->r_flags),
d->unitsize, d->gapsize, l16(d->vseqno));
fprint(fd, "%d %s", d->namelen, nstr(d->name, d->namelen));
if(fmt != 'J'){
namelen = d->namelen + (1-(d->namelen&1));
syslen = d->reclen - 33 - namelen;
if(syslen != 0)
fprint(fd, " %s", nstr(&d->name[namelen], syslen));
}
fprint(fd, "\n");
return d->reclen + (d->reclen&1);
}
static void
newdrec(Xfile *f, Drec *dp)
{
Isofile *x = f->ptr;
Isofile *n;
int len;
len = sizeof(Isofile) - sizeof(Drec) + dp->reclen;
n = ealloc(len);
n->fmt = x->fmt;
n->blksize = x->blksize;
n->offset = 0;
n->doffset = 0;
memmove(&n->d, dp, dp->reclen);
free(x);
f->ptr = n;
f->len = len;
}
static void
ungetdrec(Xfile *f)
{
Isofile *ip = f->ptr;
if(ip->offset >= ip->odelta){
ip->offset -= ip->odelta;
ip->odelta = 0;
}
}
static int
getdrec(Xfile *f, void *buf)
{
Isofile *ip = f->ptr;
int len = 0, boff = 0;
ulong size;
vlong addr;
Iobuf *p = 0;
if(!ip)
return -1;
size = l32(ip->d.size);
while(ip->offset < size){
addr = (l32(ip->d.addr)+ip->d.attrlen)*ip->blksize + ip->offset;
boff = addr % Sectorsize;
if(boff > Sectorsize-34){
ip->offset += Sectorsize-boff;
continue;
}
p = getbuf(f->xf->d, addr/Sectorsize);
len = p->iobuf[boff];
if(len >= 34)
break;
putbuf(p);
p = 0;
ip->offset += Sectorsize-boff;
}
if(p) {
memmove(buf, &p->iobuf[boff], len);
putbuf(p);
ip->odelta = len + (len&1);
ip->offset += ip->odelta;
return 0;
}
return -1;
}
static int
opendotdot(Xfile *f, Xfile *pf)
{
uchar dbuf[256];
Drec *d = (Drec *)dbuf;
Isofile *ip = f->ptr, *pip = pf->ptr;
ip->offset = 0;
if(getdrec(f, d) < 0){
chat("opendotdot: getdrec(.) failed...");
return -1;
}
if(d->namelen != 1 || d->name[0] != 0){
chat("opendotdot: no . entry...");
return -1;
}
if(l32(d->addr) != l32(ip->d.addr)){
chat("opendotdot: bad . address...");
return -1;
}
if(getdrec(f, d) < 0){
chat("opendotdot: getdrec(..) failed...");
return -1;
}
if(d->namelen != 1 || d->name[0] != 1){
chat("opendotdot: no .. entry...");
return -1;
}
pf->xf = f->xf;
pip->fmt = ip->fmt;
pip->blksize = ip->blksize;
pip->offset = 0;
pip->doffset = 0;
pip->d = *d;
return 0;
}
enum {
Hname = 1,
Hmode = 2,
};
static int
rzdir(Xfs *fs, Dir *d, int fmt, Drec *dp)
{
int n, flags, i, j, lj, nl, vers, sysl, mode, l, have;
uchar *s;
char *p;
char buf[Maxname+UTFmax+1];
uchar *q;
Rune r;
enum { ONAMELEN = 28 }; /* old Plan 9 directory name length */
have = 0;
flags = 0;
vers = -1;
d->qid.path = l32(dp->addr);
d->qid.type = 0;
d->qid.vers = 0;
n = dp->namelen;
memset(d->name, 0, Maxname);
if(n == 1) {
switch(dp->name[0]){
case 1:
d->name[1] = '.';
/* fall through */
case 0:
d->name[0] = '.';
have = Hname;
break;
default:
d->name[0] = tolower(dp->name[0]);
}
} else {
if(fmt == 'J'){ /* Joliet, 16-bit Unicode */
q = (uchar*)dp->name;
for(i=j=lj=0; i<n && j<Maxname; i+=2){
lj = j;
r = (q[i]<<8)|q[i+1];
j += runetochar(buf+j, &r);
}
if(j >= Maxname)
j = lj;
memmove(d->name, buf, j);
}else{
if(n >= Maxname)
n = Maxname-1;
for(i=0; i<n; i++)
d->name[i] = tolower(dp->name[i]);
}
}
sysl = dp->reclen-(34+dp->namelen);
s = (uchar*)dp->name + dp->namelen;
if(((uintptr)s) & 1) {
s++;
sysl--;
}
if(fs->isplan9 && sysl > 0) {
/*
* get gid, uid, mode and possibly name
* from plan9 directory extension
*/
nl = *s;
if(nl >= ONAMELEN)
nl = ONAMELEN-1;
if(nl) {
memset(d->name, 0, ONAMELEN);
memmove(d->name, s+1, nl);
}
s += 1 + *s;
nl = *s;
if(nl >= ONAMELEN)
nl = ONAMELEN-1;
memset(d->uid, 0, ONAMELEN);
memmove(d->uid, s+1, nl);
s += 1 + *s;
nl = *s;
if(nl >= ONAMELEN)
nl = ONAMELEN-1;
memset(d->gid, 0, ONAMELEN);
memmove(d->gid, s+1, nl);
s += 1 + *s;
if(((uintptr)s) & 1)
s++;
d->mode = l32(s);
if(d->mode & DMDIR)
d->qid.type |= QTDIR;
} else {
d->mode = 0444;
switch(fmt) {
case 'z':
if(fs->isrock)
strcpy(d->gid, "ridge");
else
strcpy(d->gid, "iso9660");
flags = dp->flags;
break;
case 'r':
strcpy(d->gid, "sierra");
flags = dp->r_flags;
break;
case 'J':
strcpy(d->gid, "joliet");
flags = dp->flags;
break;
case '9':
strcpy(d->gid, "plan9");
flags = dp->flags;
break;
}
if(flags & 0x02){
d->qid.type |= QTDIR;
d->mode |= DMDIR|0111;
}
strcpy(d->uid, "cdrom");
if(fmt!='9' && !(d->mode&DMDIR)){
/*
* ISO 9660 actually requires that you always have a . and a ;,
* even if there is no version and no extension. Very few writers
* do this. If the version is present, we use it for qid.vers.
* If there is no extension but there is a dot, we strip it off.
* (VMS heads couldn't comprehend the dot as a file name character
* rather than as just a separator between name and extension.)
*
* We don't do this for directory names because directories are
* not allowed to have extensions and versions.
*/
if((p=strchr(d->name, ';')) != nil){
vers = strtoul(p+1, 0, 0);
d->qid.vers = vers;
*p = '\0';
}
if((p=strchr(d->name, '.')) != nil && *(p+1)=='\0')
*p = '\0';
}
if(fs->issusp){
nl = 0;
s += fs->suspoff;
sysl -= fs->suspoff;
for(; sysl >= 4 && have != (Hname|Hmode); sysl -= l, s += l){
if(s[0] == 0 && ((uintptr)s & 1)){
/* MacOS pads individual entries, contrary to spec */
s++;
sysl--;
}
l = s[2];
if(s[0] == 'P' && s[1] == 'X' && s[3] == 1){
/* posix file attributes */
mode = l32(s+4);
d->mode = mode & 0777;
if((mode & 0170000) == 040000){
d->mode |= DMDIR;
d->qid.type |= QTDIR;
}
have |= Hmode;
} else if(s[0] == 'N' && s[1] == 'M' && s[3] == 1){
/* alternative name */
if((s[4] & ~1) == 0){
i = nl+l-5;
if(i >= Maxname)
i = Maxname-1;
if((i -= nl) > 0){
memmove(d->name+nl, s+5, i);
nl += i;
}
if(s[4] == 0)
have |= Hname;
}
} else if(s[0] == 'C' && s[1] == 'E' && s[2] >= 28){
sysl = getcontin(fs->d, s, &s);
continue;
} else if(s[0] == 'S' && s[1] == 'T')
break;
}
}
}
d->length = 0;
if((d->mode & DMDIR) == 0)
d->length = l32(dp->size);
d->type = 0;
d->dev = 0;
d->atime = gtime(dp->date);
d->mtime = d->atime;
return vers;
}
static int
getcontin(Xdata *dev, uchar *p, uchar **s)
{
long bn, off, len;
Iobuf *b;
bn = l32(p+4);
off = l32(p+12);
len = l32(p+20);
chat("getcontin %d...", bn);
b = getbuf(dev, bn);
if(b == 0){
*s = 0;
return 0;
}
*s = b->iobuf+off;
putbuf(b);
return len;
}
static char *
nstr(uchar *p, int n)
{
static char buf[132];
char *q = buf;
while(--n >= 0){
if(*p == '\\')
*q++ = '\\';
if(' ' <= *p && *p <= '~')
*q++ = *p++;
else
q += sprint(q, "\\%2.2ux", *p++);
}
*q = 0;
return buf;
}
static char *
rdate(uchar *p, int fmt)
{
static char buf[64];
int htz, s, n;
n = sprint(buf, "%2.2d.%2.2d.%2.2d %2.2d:%2.2d:%2.2d",
p[0], p[1], p[2], p[3], p[4], p[5]);
if(fmt == 'z'){
htz = p[6];
if(htz >= 128){
htz = 256-htz;
s = '-';
}else
s = '+';
sprint(&buf[n], " (%c%.1f)", s, (float)htz/2);
}
return buf;
}
static char
dmsize[12] =
{
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
};
#define dysize mydysize
static int
dysize(int y)
{
if((y%4) == 0)
return 366;
return 365;
}
static long
gtime(uchar *p) /* yMdhmsz */
{
long t;
int i, y, M, d, h, m, s, tz;
y=p[0]; M=p[1]; d=p[2];
h=p[3]; m=p[4]; s=p[5]; tz=p[6];
USED(tz);
y += 1900;
if (y < 1970)
return 0;
if (M < 1 || M > 12)
return 0;
if (d < 1 || d > dmsize[M-1])
if (!(M == 2 && d == 29 && dysize(y) == 366))
return 0;
if (h > 23)
return 0;
if (m > 59)
return 0;
if (s > 59)
return 0;
t = 0;
for(i=1970; i<y; i++)
t += dysize(i);
if (dysize(y)==366 && M >= 3)
t++;
while(--M)
t += dmsize[M-1];
t += d-1;
t = 24*t + h;
t = 60*t + m;
t = 60*t + s;
return t;
}
#define p ((uchar*)arg)
static long
l16(void *arg)
{
long v;
v = ((long)p[1]<<8)|p[0];
if (v >= 0x8000L)
v -= 0x10000L;
return v;
}
static long
l32(void *arg)
{
return ((((((long)p[3]<<8)|p[2])<<8)|p[1])<<8)|p[0];
}
#undef p

118
src/cmd/9660srv/dat.h Normal file
View file

@ -0,0 +1,118 @@
typedef struct Ioclust Ioclust;
typedef struct Iobuf Iobuf;
typedef struct Isofile Isofile;
typedef struct Xdata Xdata;
typedef struct Xfile Xfile;
typedef struct Xfs Xfs;
typedef struct Xfsub Xfsub;
#pragma incomplete Isofile
enum
{
Sectorsize = 2048,
Maxname = 256,
};
struct Iobuf
{
Ioclust* clust;
long addr;
uchar* iobuf;
};
struct Ioclust
{
long addr; /* in sectors; good to 8TB */
Xdata* dev;
Ioclust* next;
Ioclust* prev;
int busy;
int nbuf;
Iobuf* buf;
uchar* iobuf;
};
struct Xdata
{
Xdata* next;
char* name; /* of underlying file */
Qid qid;
short type;
short fdev;
int ref; /* attach count */
int dev; /* for read/write */
};
struct Xfsub
{
void (*reset)(void);
int (*attach)(Xfile*);
void (*clone)(Xfile*, Xfile*);
void (*walkup)(Xfile*);
void (*walk)(Xfile*, char*);
void (*open)(Xfile*, int);
void (*create)(Xfile*, char*, long, int);
long (*readdir)(Xfile*, uchar*, long, long);
long (*read)(Xfile*, char*, vlong, long);
long (*write)(Xfile*, char*, vlong, long);
void (*clunk)(Xfile*);
void (*remove)(Xfile*);
void (*stat)(Xfile*, Dir*);
void (*wstat)(Xfile*, Dir*);
};
struct Xfs
{
Xdata* d; /* how to get the bits */
Xfsub* s; /* how to use them */
int ref;
int issusp; /* follows system use sharing protocol */
long suspoff; /* if so, offset at which SUSP area begins */
int isrock; /* Rock Ridge format */
int isplan9; /* has Plan 9-specific directory info */
Qid rootqid;
Isofile* ptr; /* private data */
};
struct Xfile
{
Xfile* next; /* in fid hash bucket */
Xfs* xf;
long fid;
ulong flags;
Qid qid;
int len; /* of private data */
Isofile* ptr;
};
enum
{
Asis,
Clean,
Clunk
};
enum
{
Oread = 1,
Owrite = 2,
Orclose = 4,
Omodes = 3,
};
extern char Enonexist[]; /* file does not exist */
extern char Eperm[]; /* permission denied */
extern char Enofile[]; /* no file system specified */
extern char Eauth[]; /* authentication failed */
extern char *srvname;
extern char *deffile;
extern int chatty;
extern jmp_buf err_lab[];
extern int nerr_lab;
extern char err_msg[];
extern int nojoliet;
extern int noplan9;
extern int norock;

22
src/cmd/9660srv/data.c Normal file
View file

@ -0,0 +1,22 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include "dat.h"
#include "fns.h"
char Enonexist[] = "file does not exist";
char Eperm[] = "permission denied";
char Enofile[] = "no file system specified";
char Eauth[] = "authentication failed";
char *srvname = "9660";
char *deffile = 0;
extern Xfsub isosub;
Xfsub* xsublist[] =
{
&isosub,
0
};

17
src/cmd/9660srv/fns.h Normal file
View file

@ -0,0 +1,17 @@
void chat(char*, ...);
void* ealloc(long);
void error(char*);
Iobuf* getbuf(Xdata*, ulong);
Xdata* getxdata(char*);
void iobuf_init(void);
void nexterror(void);
void panic(int, char*, ...);
void purgebuf(Xdata*);
void putbuf(Iobuf*);
void refxfs(Xfs*, int);
void showdir(int, Dir*);
Xfile* xfile(int, int);
void setnames(Dir*, char*);
#define waserror() (++nerr_lab, setjmp(err_lab[nerr_lab-1]))
#define poperror() (--nerr_lab)

177
src/cmd/9660srv/iobuf.c Normal file
View file

@ -0,0 +1,177 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include "dat.h"
#include "fns.h"
/*
* We used to use 100 i/o buffers of size 2kb (Sectorsize).
* Unfortunately, reading 2kb at a time often hopping around
* the disk doesn't let us get near the disk bandwidth.
*
* Based on a trace of iobuf address accesses taken while
* tarring up a Plan 9 distribution CD, we now use 16 128kb
* buffers. This works for ISO9660 because data is required
* to be laid out contiguously; effectively we're doing agressive
* readahead. Because the buffers are so big and the typical
* disk accesses so concentrated, it's okay that we have so few
* of them.
*
* If this is used to access multiple discs at once, it's not clear
* how gracefully the scheme degrades, but I'm not convinced
* it's worth worrying about. -rsc
*/
/* trying a larger value to get greater throughput - geoff */
#define BUFPERCLUST 256 /* sectors/cluster; was 64, 64*Sectorsize = 128kb */
#define NCLUST 16
int nclust = NCLUST;
static Ioclust* iohead;
static Ioclust* iotail;
static Ioclust* getclust(Xdata*, long);
static void putclust(Ioclust*);
static void xread(Ioclust*);
void
iobuf_init(void)
{
int i, j, n;
Ioclust *c;
Iobuf *b;
uchar *mem;
n = nclust*sizeof(Ioclust) +
nclust*BUFPERCLUST*(sizeof(Iobuf)+Sectorsize);
mem = sbrk(n);
if(mem == (void*)-1)
panic(0, "iobuf_init");
memset(mem, 0, n);
for(i=0; i<nclust; i++){
c = (Ioclust*)mem;
mem += sizeof(Ioclust);
c->addr = -1;
c->prev = iotail;
if(iotail)
iotail->next = c;
iotail = c;
if(iohead == nil)
iohead = c;
c->buf = (Iobuf*)mem;
mem += BUFPERCLUST*sizeof(Iobuf);
c->iobuf = mem;
mem += BUFPERCLUST*Sectorsize;
for(j=0; j<BUFPERCLUST; j++){
b = &c->buf[j];
b->clust = c;
b->addr = -1;
b->iobuf = c->iobuf+j*Sectorsize;
}
}
}
void
purgebuf(Xdata *dev)
{
Ioclust *p;
for(p=iohead; p!=nil; p=p->next)
if(p->dev == dev){
p->addr = -1;
p->busy = 0;
}
}
static Ioclust*
getclust(Xdata *dev, long addr)
{
Ioclust *c, *f;
f = nil;
for(c=iohead; c; c=c->next){
if(!c->busy)
f = c;
if(c->addr == addr && c->dev == dev){
c->busy++;
return c;
}
}
if(f == nil)
panic(0, "out of buffers");
f->addr = addr;
f->dev = dev;
f->busy++;
if(waserror()){
f->addr = -1; /* stop caching */
putclust(f);
nexterror();
}
xread(f);
poperror();
return f;
}
static void
putclust(Ioclust *c)
{
if(c->busy <= 0)
panic(0, "putbuf");
c->busy--;
/* Link onto head for LRU */
if(c == iohead)
return;
c->prev->next = c->next;
if(c->next)
c->next->prev = c->prev;
else
iotail = c->prev;
c->prev = nil;
c->next = iohead;
iohead->prev = c;
iohead = c;
}
Iobuf*
getbuf(Xdata *dev, ulong addr)
{
int off;
Ioclust *c;
off = addr%BUFPERCLUST;
c = getclust(dev, addr - off);
if(c->nbuf < off){
c->busy--;
error("I/O read error");
}
return &c->buf[off];
}
void
putbuf(Iobuf *b)
{
putclust(b->clust);
}
static void
xread(Ioclust *c)
{
int n;
Xdata *dev;
dev = c->dev;
seek(dev->dev, (vlong)c->addr * Sectorsize, 0);
n = readn(dev->dev, c->iobuf, BUFPERCLUST*Sectorsize);
if(n < Sectorsize)
error("I/O read error");
c->nbuf = n/Sectorsize;
}

142
src/cmd/9660srv/iso9660.h Normal file
View file

@ -0,0 +1,142 @@
#define VOLDESC 16 /* sector number */
/*
* L means little-endian, M means big-endian, and LM means little-endian
* then again big-endian.
*/
typedef uchar Byte2L[2];
typedef uchar Byte2M[2];
typedef uchar Byte4LM[4];
typedef uchar Byte4L[4];
typedef uchar Byte4M[4];
typedef uchar Byte8LM[8];
typedef union Drec Drec;
typedef union Voldesc Voldesc;
enum
{
Boot = 0,
Primary = 1,
Supplementary = 2,
Partition = 3,
Terminator = 255
};
union Voldesc
{ /* volume descriptor */
uchar byte[Sectorsize];
union { /* for CD001, the ECMA standard */
struct
{
uchar type;
uchar stdid[5];
uchar version;
uchar unused;
uchar sysid[32];
uchar bootid[32];
uchar data[1977];
} boot;
struct
{
uchar type;
uchar stdid[5];
uchar version;
uchar flags;
uchar sysid[32];
uchar volid[32];
Byte8LM partloc;
Byte8LM size;
uchar escapes[32];
Byte4LM vsetsize;
Byte4LM vseqno;
Byte4LM blksize;
Byte8LM ptabsize;
Byte4L lptable;
Byte4L optlptable;
Byte4M mptable;
Byte4M optmptable;
uchar rootdir[34];
uchar volsetid[128];
uchar pubid[128];
uchar prepid[128];
uchar appid[128];
uchar copyright[37];
uchar abstract[37];
uchar bibliography[37];
uchar cdate[17];
uchar mdate[17];
uchar expdate[17];
uchar effdate[17];
uchar fsversion;
uchar unused3[1];
uchar appuse[512];
uchar unused4[653];
} desc;
} z;
union
{ /* for CDROM, the `High Sierra' standard */
struct
{
Byte8LM number;
uchar type;
uchar stdid[5];
uchar version;
uchar flags;
uchar sysid[32];
uchar volid[32];
Byte8LM partloc;
Byte8LM size;
uchar escapes[32];
Byte4LM vsetsize;
Byte4LM vseqno;
Byte4LM blksize;
uchar quux[40];
uchar rootdir[34];
uchar volsetid[128];
uchar pubid[128];
uchar prepid[128];
uchar appid[128];
uchar copyright[32];
uchar abstract[32];
uchar cdate[16];
uchar mdate[16];
uchar expdate[16];
uchar effdate[16];
uchar fsversion;
} desc;
} r;
};
union Drec
{
struct
{
uchar reclen;
uchar attrlen;
Byte8LM addr;
Byte8LM size;
uchar date[6];
uchar tzone; /* flags in high sierra */
uchar flags; /* ? in high sierra */
uchar unitsize; /* ? in high sierra */
uchar gapsize; /* ? in high sierra */
Byte4LM vseqno; /* ? in high sierra */
uchar namelen;
uchar name[1];
};
struct
{
uchar r_pad[24];
uchar r_flags;
};
};
struct Isofile
{
short fmt; /* 'z' if iso, 'r' if high sierra */
short blksize;
long offset; /* true offset when reading directory */
long odelta; /* true size of directory just read */
long doffset; /* plan9 offset when reading directory */
Drec d;
};

576
src/cmd/9660srv/main.c Normal file
View file

@ -0,0 +1,576 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include "dat.h"
#include "fns.h"
enum
{
Maxfdata = 8192,
Maxiosize = IOHDRSZ+Maxfdata,
};
void io(int);
void rversion(void);
void rattach(void);
void rauth(void);
void rclunk(void);
void rcreate(void);
void rflush(void);
void ropen(void);
void rread(void);
void rremove(void);
void rsession(void);
void rstat(void);
void rwalk(void);
void rwrite(void);
void rwstat(void);
static int openflags(int);
static void usage(void);
#define Reqsize (sizeof(Fcall)+Maxfdata)
Fcall *req;
Fcall *rep;
uchar mdata[Maxiosize];
char fdata[Maxfdata];
uchar statbuf[STATMAX];
int errno;
extern Xfsub *xsublist[];
extern int nclust;
jmp_buf err_lab[16];
int nerr_lab;
char err_msg[ERRMAX];
int chatty;
int nojoliet;
int noplan9;
int norock;
void (*fcalls[])(void) = {
[Tversion] rversion,
[Tflush] rflush,
[Tauth] rauth,
[Tattach] rattach,
[Twalk] rwalk,
[Topen] ropen,
[Tcreate] rcreate,
[Tread] rread,
[Twrite] rwrite,
[Tclunk] rclunk,
[Tremove] rremove,
[Tstat] rstat,
[Twstat] rwstat,
};
void
main(int argc, char **argv)
{
int srvfd, pipefd[2], stdio;
Xfsub **xs;
char *mtpt;
stdio = 0;
mtpt = nil;
ARGBEGIN {
case '9':
noplan9 = 1;
break;
case 'c':
nclust = atoi(EARGF(usage()));
if (nclust <= 0)
sysfatal("nclust %d non-positive", nclust);
break;
case 'f':
deffile = EARGF(usage());
break;
case 'r':
norock = 1;
break;
case 's':
stdio = 1;
break;
case 'v':
chatty = 1;
break;
case 'J':
nojoliet = 1;
break;
case 'm':
mtpt = EARGF(usage());
break;
default:
usage();
} ARGEND
switch(argc) {
case 0:
break;
case 1:
srvname = argv[0];
break;
default:
usage();
}
iobuf_init();
for(xs=xsublist; *xs; xs++)
(*(*xs)->reset)();
if(stdio) {
pipefd[0] = 0;
pipefd[1] = 1;
} else {
close(0);
close(1);
open("/dev/null", OREAD);
open("/dev/null", OWRITE);
if(pipe(pipefd) < 0)
panic(1, "pipe");
if(post9pservice(pipefd[0], srvname, mtpt) < 0)
sysfatal("post9pservice: %r");
close(pipefd[0]);
}
srvfd = pipefd[1];
switch(rfork(RFNOWAIT|RFNOTEG|RFFDG|RFPROC)){
case -1:
panic(1, "fork");
default:
_exits(0);
case 0:
break;
}
io(srvfd);
exits(0);
}
void
io(int srvfd)
{
int n, pid;
Fcall xreq, xrep;
req = &xreq;
rep = &xrep;
pid = getpid();
fmtinstall('F', fcallfmt);
for(;;){
/*
* 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(srvfd, mdata, sizeof mdata);
if(n < 0)
break;
if(n == 0)
continue;
if(convM2S(mdata, n, req) == 0)
continue;
if(chatty)
fprint(2, "9660srv %d:<-%F\n", pid, req);
errno = 0;
if(!waserror()){
err_msg[0] = 0;
if(req->type >= nelem(fcalls) || !fcalls[req->type])
error("bad fcall type");
(*fcalls[req->type])();
poperror();
}
if(err_msg[0]){
rep->type = Rerror;
rep->ename = err_msg;
}else{
rep->type = req->type + 1;
rep->fid = req->fid;
}
rep->tag = req->tag;
if(chatty)
fprint(2, "9660srv %d:->%F\n", pid, rep);
n = convS2M(rep, mdata, sizeof mdata);
if(n == 0)
panic(1, "convS2M error on write");
if(write(srvfd, mdata, n) != n)
panic(1, "mount write");
if(nerr_lab != 0)
panic(0, "err stack %d");
}
chat("server shut down");
}
static void
usage(void)
{
fprint(2, "usage: %s [-v] [-9Jr] [-s] [-f devicefile] [srvname]\n", argv0);
exits("usage");
}
void
error(char *p)
{
strecpy(err_msg, err_msg+sizeof err_msg, p);
nexterror();
}
void
nexterror(void)
{
longjmp(err_lab[--nerr_lab], 1);
}
void*
ealloc(long n)
{
void *p;
p = malloc(n);
if(p == 0)
error("no memory");
return p;
}
void
setnames(Dir *d, char *n)
{
d->name = n;
d->uid = n+Maxname;
d->gid = n+Maxname*2;
d->muid = n+Maxname*3;
d->name[0] = '\0';
d->uid[0] = '\0';
d->gid[0] = '\0';
d->muid[0] = '\0';
}
void
rversion(void)
{
if(req->msize > Maxiosize)
rep->msize = Maxiosize;
else
rep->msize = req->msize;
rep->version = "9P2000";
}
void
rauth(void)
{
error("9660srv: authentication not required");
}
void
rflush(void)
{
}
void
rattach(void)
{
Xfs *xf;
Xfile *root;
Xfsub **xs;
chat("attach(fid=%d,uname=\"%s\",aname=\"%s\")...",
req->fid, req->uname, req->aname);
if(waserror()){
xfile(req->fid, Clunk);
nexterror();
}
root = xfile(req->fid, Clean);
root->qid = (Qid){0, 0, QTDIR};
root->xf = xf = ealloc(sizeof(Xfs));
memset(xf, 0, sizeof(Xfs));
xf->ref = 1;
xf->d = getxdata(req->aname);
for(xs=xsublist; *xs; xs++)
if((*(*xs)->attach)(root) >= 0){
poperror();
xf->s = *xs;
xf->rootqid = root->qid;
rep->qid = root->qid;
return;
}
error("unknown format");
}
Xfile*
doclone(Xfile *of, int newfid)
{
Xfile *nf, *next;
nf = xfile(newfid, Clean);
if(waserror()){
xfile(newfid, Clunk);
nexterror();
}
next = nf->next;
*nf = *of;
nf->next = next;
nf->fid = newfid;
refxfs(nf->xf, 1);
if(nf->len){
nf->ptr = ealloc(nf->len);
memmove(nf->ptr, of->ptr, nf->len);
}else
nf->ptr = of->ptr;
(*of->xf->s->clone)(of, nf);
poperror();
return nf;
}
void
rwalk(void)
{
Xfile *f, *nf;
Isofile *oldptr;
int oldlen;
Qid oldqid;
rep->nwqid = 0;
nf = nil;
f = xfile(req->fid, Asis);
if(req->fid != req->newfid)
f = nf = doclone(f, req->newfid);
/* save old state in case of error */
oldqid = f->qid;
oldlen = f->len;
oldptr = f->ptr;
if(oldlen){
oldptr = ealloc(oldlen);
memmove(oldptr, f->ptr, oldlen);
}
if(waserror()){
if(nf != nil)
xfile(req->newfid, Clunk);
if(rep->nwqid == req->nwname){
if(oldlen)
free(oldptr);
}else{
/* restore previous state */
f->qid = oldqid;
if(f->len)
free(f->ptr);
f->ptr = oldptr;
f->len = oldlen;
}
if(rep->nwqid==req->nwname || rep->nwqid > 0){
err_msg[0] = '\0';
return;
}
nexterror();
}
for(rep->nwqid=0; rep->nwqid < req->nwname && rep->nwqid < MAXWELEM; rep->nwqid++){
chat("\twalking %s\n", req->wname[rep->nwqid]);
if(!(f->qid.type & QTDIR)){
chat("\tnot dir: type=%#x\n", f->qid.type);
error("walk in non-directory");
}
if(strcmp(req->wname[rep->nwqid], "..")==0){
if(f->qid.path != f->xf->rootqid.path)
(*f->xf->s->walkup)(f);
}else
(*f->xf->s->walk)(f, req->wname[rep->nwqid]);
rep->wqid[rep->nwqid] = f->qid;
}
poperror();
if(oldlen)
free(oldptr);
}
void
ropen(void)
{
Xfile *f;
f = xfile(req->fid, Asis);
if(f->flags&Omodes)
error("open on open file");
if(req->mode&ORCLOSE)
error("no removes");
(*f->xf->s->open)(f, req->mode);
f->flags = openflags(req->mode);
rep->qid = f->qid;
rep->iounit = 0;
}
void
rcreate(void)
{
error("no creates");
/*
Xfile *f;
if(strcmp(req->name, ".") == 0 || strcmp(req->name, "..") == 0)
error("create . or ..");
f = xfile(req->fid, Asis);
if(f->flags&Omodes)
error("create on open file");
if(!(f->qid.path&CHDIR))
error("create in non-directory");
(*f->xf->s->create)(f, req->name, req->perm, req->mode);
chat("f->qid=0x%8.8lux...", f->qid.path);
f->flags = openflags(req->mode);
rep->qid = f->qid;
*/
}
void
rread(void)
{
Xfile *f;
f=xfile(req->fid, Asis);
if (!(f->flags&Oread))
error("file not opened for reading");
if(f->qid.type & QTDIR)
rep->count = (*f->xf->s->readdir)(f, (uchar*)fdata, req->offset, req->count);
else
rep->count = (*f->xf->s->read)(f, fdata, req->offset, req->count);
rep->data = fdata;
}
void
rwrite(void)
{
Xfile *f;
f=xfile(req->fid, Asis);
if(!(f->flags&Owrite))
error("file not opened for writing");
rep->count = (*f->xf->s->write)(f, req->data, req->offset, req->count);
}
void
rclunk(void)
{
Xfile *f;
if(!waserror()){
f = xfile(req->fid, Asis);
(*f->xf->s->clunk)(f);
poperror();
}
xfile(req->fid, Clunk);
}
void
rremove(void)
{
error("no removes");
}
void
rstat(void)
{
Xfile *f;
Dir dir;
chat("stat(fid=%d)...", req->fid);
f=xfile(req->fid, Asis);
setnames(&dir, fdata);
(*f->xf->s->stat)(f, &dir);
if(chatty)
showdir(2, &dir);
rep->nstat = convD2M(&dir, statbuf, sizeof statbuf);
rep->stat = statbuf;
}
void
rwstat(void)
{
error("no wstat");
}
static int
openflags(int mode)
{
int flags = 0;
switch(mode & ~(OTRUNC|OCEXEC|ORCLOSE)){
case OREAD:
case OEXEC:
flags = Oread; break;
case OWRITE:
flags = Owrite; break;
case ORDWR:
flags = Oread|Owrite; break;
}
if(mode & ORCLOSE)
flags |= Orclose;
return flags;
}
void
showdir(int fd, Dir *s)
{
char a_time[32], m_time[32];
char *p;
strcpy(a_time, ctime(s->atime));
if(p=strchr(a_time, '\n')) /* assign = */
*p = 0;
strcpy(m_time, ctime(s->mtime));
if(p=strchr(m_time, '\n')) /* assign = */
*p = 0;
fprint(fd, "name=\"%s\" qid=(0x%llux,%lud) type=%d dev=%d \
mode=0x%8.8lux=0%luo atime=%s mtime=%s length=%lld uid=\"%s\" gid=\"%s\"...",
s->name, s->qid.path, s->qid.vers, s->type, s->dev,
s->mode, s->mode,
a_time, m_time, s->length, s->uid, s->gid);
}
#define SIZE 1024
void
chat(char *fmt, ...)
{
va_list arg;
if(chatty){
va_start(arg, fmt);
vfprint(2, fmt, arg);
va_end(arg);
}
}
void
panic(int rflag, char *fmt, ...)
{
va_list arg;
char buf[SIZE]; int n;
n = sprint(buf, "%s %d: ", argv0, getpid());
va_start(arg, fmt);
vseprint(buf+n, buf+SIZE, fmt, arg);
va_end(arg);
fprint(2, (rflag ? "%s: %r\n" : "%s\n"), buf);
if(chatty){
fprint(2, "abort\n");
abort();
}
exits("panic");
}

17
src/cmd/9660srv/mkfile Normal file
View file

@ -0,0 +1,17 @@
<$PLAN9/src/mkhdr
TARG=9660srv
OFILES=\
main.$O\
9660srv.$O\
xfile.$O\
iobuf.$O\
data.$O\
HFILES=dat.h fns.h
<$PLAN9/src/mkone
9660srv.$O: iso9660.h

170
src/cmd/9660srv/xfile.c Normal file
View file

@ -0,0 +1,170 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include "dat.h"
#include "fns.h"
static Xfile* clean(Xfile*);
#define FIDMOD 127 /* prime */
static Xdata* xhead;
static Xfile* xfiles[FIDMOD];
static Xfile* freelist;
Xdata*
getxdata(char *name)
{
int fd;
Dir *dir;
Xdata *xf, *fxf;
int flag;
if(name[0] == 0)
name = deffile;
if(name == 0)
error(Enofile);
flag = (access(name, 6) == 0) ? ORDWR : OREAD;
fd = open(name, flag);
if(fd < 0)
error(Enonexist);
dir = nil;
if(waserror()){
close(fd);
free(dir);
nexterror();
}
if((dir = dirfstat(fd)) == nil)
error("I/O error");
if((dir->qid.type & ~QTTMP) != QTFILE)
error("attach name not a plain file");
for(fxf=0,xf=xhead; xf; xf=xf->next){
if(xf->name == 0){
if(fxf == 0)
fxf = xf;
continue;
}
if(xf->qid.path != dir->qid.path || xf->qid.vers != dir->qid.vers)
continue;
if(xf->type != dir->type || xf->fdev != dir->dev)
continue;
xf->ref++;
chat("incref=%d, \"%s\", dev=%d...", xf->ref, xf->name, xf->dev);
close(fd);
poperror();
free(dir);
return xf;
}
if(fxf==0){
fxf = ealloc(sizeof(Xfs));
fxf->next = xhead;
xhead = fxf;
}
chat("alloc \"%s\", dev=%d...", name, fd);
fxf->ref = 1;
fxf->name = strcpy(ealloc(strlen(name)+1), name);
fxf->qid = dir->qid;
fxf->type = dir->type;
fxf->fdev = dir->dev;
fxf->dev = fd;
free(dir);
poperror();
return fxf;
}
static void
putxdata(Xdata *d)
{
if(d->ref <= 0)
panic(0, "putxdata");
d->ref--;
chat("decref=%d, \"%s\", dev=%d...", d->ref, d->name, d->dev);
if(d->ref == 0){
chat("purgebuf...");
purgebuf(d);
close(d->dev);
free(d->name);
d->name = 0;
}
}
void
refxfs(Xfs *xf, int delta)
{
xf->ref += delta;
if(xf->ref == 0){
if(xf->d)
putxdata(xf->d);
if(xf->ptr)
free(xf->ptr);
free(xf);
}
}
Xfile*
xfile(int fid, int flag)
{
int k = fid%FIDMOD;
Xfile **hp=&xfiles[k], *f, *pf;
for(f=*hp,pf=0; f; pf=f,f=f->next)
if(f->fid == fid)
break;
if(f && pf){
pf->next = f->next;
f->next = *hp;
*hp = f;
}
switch(flag){
default:
panic(0, "xfile");
case Asis:
if(f == 0)
error("unassigned fid");
return f;
case Clean:
break;
case Clunk:
if(f){
*hp = f->next;
clean(f);
f->next = freelist;
freelist = f;
}
return 0;
}
if(f)
return clean(f);
if(f = freelist) /* assign = */
freelist = f->next;
else
f = ealloc(sizeof(Xfile));
f->next = *hp;
*hp = f;
f->xf = 0;
f->fid = fid;
f->flags = 0;
f->qid = (Qid){0,0,0};
f->len = 0;
f->ptr = 0;
return f;
}
static Xfile *
clean(Xfile *f)
{
if(f->xf){
refxfs(f->xf, -1);
f->xf = 0;
}
if(f->len){
free(f->ptr);
f->len = 0;
}
f->ptr = 0;
f->flags = 0;
f->qid = (Qid){0,0,0};
return f;
}