#include #include #include #include #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 vlong l64(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 vlong fakemax(vlong len) { if(len == (1UL << 31) - 1) /* max. 9660 size? */ len = (1ULL << 63) - 1; /* pretend it's vast */ return len; } 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;id, 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, nf); } static void iwalkup(Xfile *f) { vlong 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) { if(isplan9) return strcmp(a, b); return cistrcmp(a, b); } 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, name, perm, 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; vlong size, addr; Isofile *ip = f->ptr; Iobuf *p; size = fakemax(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, buf, offset, 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, 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; vlong addr; uvlong size; Iobuf *p = 0; if(!ip) return -1; size = fakemax(l32(ip->d.size)); while(ip->offset < size){ addr = ((vlong)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= Maxname) j = lj; memmove(d->name, buf, j); }else{ if(n >= Maxname) n = Maxname-1; for(i=0; iname[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 = fakemax(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) { vlong bn, off, len; Iobuf *b; bn = l32(p+4); off = l32(p+12); len = l32(p+20); chat("getcontin %lld...", 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, }; 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= 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 static vlong l64(void *arg) { uchar *p; p = arg; return (vlong)l32(p+4) << 32 | (ulong)l32(p); }