diff options
Diffstat (limited to 'src/cmd/9660srv/9660srv.c')
-rw-r--r-- | src/cmd/9660srv/9660srv.c | 896 |
1 files changed, 896 insertions, 0 deletions
diff --git a/src/cmd/9660srv/9660srv.c b/src/cmd/9660srv/9660srv.c new file mode 100644 index 00000000..a64cedfd --- /dev/null +++ b/src/cmd/9660srv/9660srv.c @@ -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 |